summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorXavier Ducrohet <xav@android.com>2012-08-17 09:37:18 -0700
committerandroid code review <noreply-gerritcodereview@google.com>2012-08-17 09:37:18 -0700
commitdd37e1328b736a11354a5847ad82c5f2551defa5 (patch)
tree10b2a4be4590c35e0f8027f3773fbcc61e05fd08
parent1fc2254217371e1bae3c9373d2474b1053c869f9 (diff)
parentb613ddc1cf740750ab9dc20f1b051a2f04f5dbef (diff)
downloadmotodev-dd37e1328b736a11354a5847ad82c5f2551defa5.tar.gz
Merge "Initial contribution of Motorola MOTODEV tools code"
-rw-r--r--makefile/common.properties0
-rw-r--r--src/features/basic/.project17
-rw-r--r--src/features/basic/build.properties3
-rw-r--r--src/features/basic/feature.properties14
-rw-r--r--src/features/basic/feature.xml81
-rw-r--r--src/features/feature/.project17
-rw-r--r--src/features/feature/build.properties8
-rw-r--r--src/features/feature/feature.properties15
-rw-r--r--src/features/feature/feature.xml206
-rw-r--r--src/features/feature/p2.inf1
-rw-r--r--src/features/preflighting.sdk/.project17
-rw-r--r--src/features/preflighting.sdk/build.properties3
-rw-r--r--src/features/preflighting.sdk/feature.properties13
-rw-r--r--src/features/preflighting.sdk/feature.xml32
-rw-r--r--src/features/preflighting.ui/.project17
-rw-r--r--src/features/preflighting.ui/build.properties3
-rw-r--r--src/features/preflighting.ui/feature.properties13
-rw-r--r--src/features/preflighting.ui/feature.xml70
-rw-r--r--src/features/preflighting/.project17
-rw-r--r--src/features/preflighting/build.properties16
-rw-r--r--src/features/preflighting/feature.properties14
-rw-r--r--src/features/preflighting/feature.xml56
-rw-r--r--src/help/appvalidator_help/ReadMe.txt13
-rw-r--r--src/help/appvalidator_help/etc/buildfiles/android_appvalidator-help/build.properties6
-rw-r--r--src/help/appvalidator_help/etc/buildfiles/android_appvalidator-help/build.xml66
-rw-r--r--src/help/appvalidator_help/etc/buildfiles/android_appvalidator-help/context_extension_points.xml5
-rw-r--r--src/help/appvalidator_help/etc/buildfiles/android_appvalidator-help/enable-win32.xml6
-rw-r--r--src/help/appvalidator_help/etc/buildfiles/android_appvalidator-help/filter_appvalidator.ditaval8
-rw-r--r--src/help/appvalidator_help/etc/buildfiles/build_lib.xml223
-rw-r--r--src/help/appvalidator_help/etc/resources/commonltr.css217
-rw-r--r--src/help/appvalidator_help/etc/resources/hdr-confidential.xml1
-rw-r--r--src/help/appvalidator_help/etc/resources/hdr-none.xml1
-rw-r--r--src/help/appvalidator_help/src/topics/c_appvalidator-conditions.dita436
-rw-r--r--src/help/appvalidator_help/src/topics/cs_app-validator.dita19
-rw-r--r--src/help/appvalidator_help/src/topics/g_legal.dita25
-rw-r--r--src/help/appvalidator_help/src/topics/g_variables.dita26
-rw-r--r--src/help/appvalidator_help/src/topics/r_appvalidator-cmdline.dita133
-rw-r--r--src/help/appvalidator_help/src/topics/t_app-validating-command-line.dita29
-rw-r--r--src/help/appvalidator_help/src/topics/t_app-validating-gui.dita34
-rw-r--r--src/help/appvalidator_help/src/topics/t_validate-about.dita16
-rw-r--r--src/help/appvalidator_help/src/topics/u_appvalidator-prefs-checkers.dita29
-rw-r--r--src/help/appvalidator_help/src/topics/u_appvalidator-prefs-devices.dita25
-rw-r--r--src/help/appvalidator_help/src/topics/u_appvalidator-prefs-general.dita46
-rw-r--r--src/help/appvalidator_help/src/topics/u_appvalidator-prefs.dita16
-rw-r--r--src/help/appvalidator_help/src/validation_editor.ditamap70
-rw-r--r--src/help/appvalidator_help/tools/ditaot/plugins/com.mot.mdb.deved.xhtml/plugin.xml35
-rw-r--r--src/help/appvalidator_help/tools/ditaot/plugins/com.mot.mdb.deved.xhtml/readme.dita17
-rw-r--r--src/help/appvalidator_help/tools/ditaot/plugins/com.mot.mdb.deved.xhtml/xsl/deved_map2plugin.xsl122
-rw-r--r--src/help/appvalidator_help/tools/ditaot/plugins/com.mot.mdb.deved.xhtml/xsl/deved_xhtml.xsl273
-rw-r--r--src/help/appvalidator_help/tools/ditaot/plugins/cshelp/catalog-dita.xml19
-rw-r--r--src/help/appvalidator_help/tools/ditaot/plugins/cshelp/dtd/cshelp.dtd101
-rw-r--r--src/help/appvalidator_help/tools/ditaot/plugins/cshelp/dtd/cshelp.mod85
-rw-r--r--src/help/appvalidator_help/tools/ditaot/plugins/cshelp/install-plugin.xml20
-rw-r--r--src/help/appvalidator_help/tools/ditaot/plugins/cshelp/plugin.xml16
-rw-r--r--src/help/appvalidator_help/tools/ditaot/plugins/cshelp/samples/test.ditamap17
-rw-r--r--src/help/appvalidator_help/tools/ditaot/plugins/cshelp/samples/test_csh_1.dita47
-rw-r--r--src/help/appvalidator_help/tools/ditaot/plugins/cshelp/samples/test_csh_2.dita33
-rw-r--r--src/help/appvalidator_help/tools/ditaot/plugins/cshelp/xsl/GetCSHMeta.xsl591
-rw-r--r--src/help/appvalidator_help/tools/ditaot/plugins/cshelp/xsl/context2contexttemp.xsl48
-rw-r--r--src/help/appvalidator_help/tools/ditaot/plugins/cshelp/xsl/context2dita.xsl130
-rw-r--r--src/help/appvalidator_help/tools/ditaot/plugins/cshelp/xsl/cshdisplay.xsl71
-rw-r--r--src/help/appvalidator_help/tools/ditaot/plugins/cshelp/xsl/dit2context.xsl1275
-rw-r--r--src/help/studio_help/ReadMe.txt13
-rw-r--r--src/help/studio_help/etc/buildfiles/android_studio-help/build.properties6
-rw-r--r--src/help/studio_help/etc/buildfiles/android_studio-help/build.xml66
-rw-r--r--src/help/studio_help/etc/buildfiles/android_studio-help/context_extension_points.xml22
-rw-r--r--src/help/studio_help/etc/buildfiles/android_studio-help/enable-win32.xml6
-rw-r--r--src/help/studio_help/etc/buildfiles/android_studio-help/filter_android-studio.ditaval8
-rw-r--r--src/help/studio_help/etc/buildfiles/build_lib.xml223
-rw-r--r--src/help/studio_help/etc/resources/commonltr.css217
-rw-r--r--src/help/studio_help/etc/resources/hdr-confidential.xml1
-rw-r--r--src/help/studio_help/etc/resources/hdr-none.xml1
-rw-r--r--src/help/studio_help/src/android_help-studio.ditamap916
-rw-r--r--src/help/studio_help/src/images/KeyCreate.pngbin0 -> 512 bytes
-rw-r--r--src/help/studio_help/src/images/KeyDelete.pngbin0 -> 618 bytes
-rw-r--r--src/help/studio_help/src/images/KeyPasswordChange.pngbin0 -> 696 bytes
-rw-r--r--src/help/studio_help/src/images/KeyProperties.pngbin0 -> 772 bytes
-rw-r--r--src/help/studio_help/src/images/KeystoreCreate.pngbin0 -> 815 bytes
-rw-r--r--src/help/studio_help/src/images/KeystoreDelete.pngbin0 -> 867 bytes
-rw-r--r--src/help/studio_help/src/images/KeystoreEntriesImport.pngbin0 -> 831 bytes
-rw-r--r--src/help/studio_help/src/images/KeystoreImport.pngbin0 -> 848 bytes
-rw-r--r--src/help/studio_help/src/images/KeystorePasswordChange.pngbin0 -> 922 bytes
-rw-r--r--src/help/studio_help/src/images/KeystoreRefresh.pngbin0 -> 411 bytes
-rw-r--r--src/help/studio_help/src/images/KeystoreTypeChange.pngbin0 -> 835 bytes
-rw-r--r--src/help/studio_help/src/images/KeystoresBackup.pngbin0 -> 468 bytes
-rw-r--r--src/help/studio_help/src/images/KeystoresRestore.pngbin0 -> 489 bytes
-rw-r--r--src/help/studio_help/src/images/PackageSign.pngbin0 -> 682 bytes
-rw-r--r--src/help/studio_help/src/images/PackageUnsign.pngbin0 -> 645 bytes
-rw-r--r--src/help/studio_help/src/images/browse-table-contents.pngbin0 -> 4111 bytes
-rw-r--r--src/help/studio_help/src/images/collapse-all.pngbin0 -> 4007 bytes
-rw-r--r--src/help/studio_help/src/images/db-connected-states.pngbin0 -> 46425 bytes
-rw-r--r--src/help/studio_help/src/images/db-create-classes.pngbin0 -> 3576 bytes
-rw-r--r--src/help/studio_help/src/images/db-create-table.pngbin0 -> 6620 bytes
-rw-r--r--src/help/studio_help/src/images/db-icon.pngbin0 -> 3095 bytes
-rw-r--r--src/help/studio_help/src/images/db-tables.pngbin0 -> 26523 bytes
-rw-r--r--src/help/studio_help/src/images/dev-mgr_adb-shell-icon.pngbin0 -> 1929 bytes
-rw-r--r--src/help/studio_help/src/images/dev-mgr_console-icon.pngbin0 -> 1652 bytes
-rw-r--r--src/help/studio_help/src/images/dev-mgr_install-app-icon.pngbin0 -> 1439 bytes
-rw-r--r--src/help/studio_help/src/images/dev-mgr_language-icon.pngbin0 -> 1664 bytes
-rw-r--r--src/help/studio_help/src/images/dev-mgr_mat-analyze-icon.pngbin0 -> 1422 bytes
-rw-r--r--src/help/studio_help/src/images/dev-mgr_monkey-test-icon.pngbin0 -> 1984 bytes
-rw-r--r--src/help/studio_help/src/images/dev-mgr_new-icon.pngbin0 -> 1781 bytes
-rw-r--r--src/help/studio_help/src/images/dev-mgr_repair-icon.pngbin0 -> 1431 bytes
-rw-r--r--src/help/studio_help/src/images/dev-mgr_reset-icon.pngbin0 -> 1527 bytes
-rw-r--r--src/help/studio_help/src/images/dev-mgr_start-icon.pngbin0 -> 1508 bytes
-rw-r--r--src/help/studio_help/src/images/dev-mgr_stop-icon.pngbin0 -> 1534 bytes
-rw-r--r--src/help/studio_help/src/images/dev-mgr_take-screenshot-icon.pngbin0 -> 1987 bytes
-rw-r--r--src/help/studio_help/src/images/dev-mgr_uninstall-app-icon.pngbin0 -> 1423 bytes
-rw-r--r--src/help/studio_help/src/images/display-selected-console-button.pngbin0 -> 485 bytes
-rw-r--r--src/help/studio_help/src/images/display-single-tab-icon.pngbin0 -> 2912 bytes
-rw-r--r--src/help/studio_help/src/images/display-text-mode-icon.pngbin0 -> 3067 bytes
-rw-r--r--src/help/studio_help/src/images/emulator-switch-layout.pngbin0 -> 372 bytes
-rw-r--r--src/help/studio_help/src/images/emulator-zoom-in.pngbin0 -> 668 bytes
-rw-r--r--src/help/studio_help/src/images/emulator-zoom-out.pngbin0 -> 608 bytes
-rw-r--r--src/help/studio_help/src/images/filter-results-icon.pngbin0 -> 3017 bytes
-rw-r--r--src/help/studio_help/src/images/loc_editor-remove-line-button.pngbin0 -> 6485 bytes
-rw-r--r--src/help/studio_help/src/images/menu-button.pngbin0 -> 150 bytes
-rw-r--r--src/help/studio_help/src/images/new-config-button.pngbin0 -> 832 bytes
-rw-r--r--src/help/studio_help/src/images/open-persp-button.jpgbin0 -> 754 bytes
-rw-r--r--src/help/studio_help/src/images/open-scrapbook.pngbin0 -> 4087 bytes
-rw-r--r--src/help/studio_help/src/images/refresh-device-list-button_android.pngbin0 -> 739 bytes
-rw-r--r--src/help/studio_help/src/images/refresh-sdks.pngbin0 -> 458 bytes
-rw-r--r--src/help/studio_help/src/images/remove-result-icon.pngbin0 -> 3015 bytes
-rw-r--r--src/help/studio_help/src/images/remove-visible-results-icon.pngbin0 -> 3125 bytes
-rw-r--r--src/help/studio_help/src/images/start-avd-button.pngbin0 -> 6324 bytes
-rw-r--r--src/help/studio_help/src/images/terminate-result-icon.pngbin0 -> 291 bytes
-rw-r--r--src/help/studio_help/src/images/tml-new-instance.pngbin0 -> 674 bytes
-rw-r--r--src/help/studio_help/src/topics/c_android-studio.dita53
-rw-r--r--src/help/studio_help/src/topics/c_code-generator.dita58
-rw-r--r--src/help/studio_help/src/topics/c_keys-keystores.dita85
-rw-r--r--src/help/studio_help/src/topics/cs_android.dita559
-rw-r--r--src/help/studio_help/src/topics/g_legal.dita25
-rw-r--r--src/help/studio_help/src/topics/g_variables.dita26
-rw-r--r--src/help/studio_help/src/topics/t_activity-creating.dita85
-rw-r--r--src/help/studio_help/src/topics/t_android-perspective-opening.dita45
-rw-r--r--src/help/studio_help/src/topics/t_app-cert-props-changing.dita9
-rw-r--r--src/help/studio_help/src/topics/t_app-cert-removing.dita42
-rw-r--r--src/help/studio_help/src/topics/t_app-cert-signing_android.dita67
-rw-r--r--src/help/studio_help/src/topics/t_app-debugging_android.dita60
-rw-r--r--src/help/studio_help/src/topics/t_app-deploying_android.dita66
-rw-r--r--src/help/studio_help/src/topics/t_app-launching_android.dita60
-rw-r--r--src/help/studio_help/src/topics/t_app-memory-analyzing.dita43
-rw-r--r--src/help/studio_help/src/topics/t_app-packaging_android.dita70
-rw-r--r--src/help/studio_help/src/topics/t_app-signature-removing.dita50
-rw-r--r--src/help/studio_help/src/topics/t_app-testing-monkey.dita71
-rw-r--r--src/help/studio_help/src/topics/t_app-uninstalling-android.dita36
-rw-r--r--src/help/studio_help/src/topics/t_bcast-rcvr-creating.dita55
-rw-r--r--src/help/studio_help/src/topics/t_cert-key-generating.dita57
-rw-r--r--src/help/studio_help/src/topics/t_cert-key-properties.dita42
-rw-r--r--src/help/studio_help/src/topics/t_cert-key-pwd-changing.dita52
-rw-r--r--src/help/studio_help/src/topics/t_code-generator.dita55
-rw-r--r--src/help/studio_help/src/topics/t_config-for-native.dita38
-rw-r--r--src/help/studio_help/src/topics/t_content-provider-creating.dita52
-rw-r--r--src/help/studio_help/src/topics/t_db-about.dita49
-rw-r--r--src/help/studio_help/src/topics/t_db-classes-create.dita64
-rw-r--r--src/help/studio_help/src/topics/t_db-connecting.dita53
-rw-r--r--src/help/studio_help/src/topics/t_db-creating.dita52
-rw-r--r--src/help/studio_help/src/topics/t_db-deleting.dita27
-rw-r--r--src/help/studio_help/src/topics/t_db-table-creating.dita63
-rw-r--r--src/help/studio_help/src/topics/t_db-table-deleting.dita24
-rw-r--r--src/help/studio_help/src/topics/t_db-table-editing.dita62
-rw-r--r--src/help/studio_help/src/topics/t_device-creating_android.dita124
-rw-r--r--src/help/studio_help/src/topics/t_device-editing_android.dita61
-rw-r--r--src/help/studio_help/src/topics/t_device-screen-capturing.dita34
-rw-r--r--src/help/studio_help/src/topics/t_device-starting_android.dita30
-rw-r--r--src/help/studio_help/src/topics/t_device-state-managing.dita32
-rw-r--r--src/help/studio_help/src/topics/t_emulator-controlling.dita33
-rw-r--r--src/help/studio_help/src/topics/t_emulator-external.dita62
-rw-r--r--src/help/studio_help/src/topics/t_emulator-language-changing.dita38
-rw-r--r--src/help/studio_help/src/topics/t_emulator-view-closing.dita27
-rw-r--r--src/help/studio_help/src/topics/t_emulator-view-manipulating.dita76
-rw-r--r--src/help/studio_help/src/topics/t_emulator-view-opening.dita46
-rw-r--r--src/help/studio_help/src/topics/t_keystore-backup-restore.dita53
-rw-r--r--src/help/studio_help/src/topics/t_keystore-backup.dita48
-rw-r--r--src/help/studio_help/src/topics/t_keystore-creating.dita67
-rw-r--r--src/help/studio_help/src/topics/t_keystore-deleting.dita49
-rw-r--r--src/help/studio_help/src/topics/t_keystore-entries-importing.dita58
-rw-r--r--src/help/studio_help/src/topics/t_keystore-importing.dita46
-rw-r--r--src/help/studio_help/src/topics/t_keystore-password-changing.dita55
-rw-r--r--src/help/studio_help/src/topics/t_keystore-type-changing.dita63
-rw-r--r--src/help/studio_help/src/topics/t_log-files-collecting.dita45
-rw-r--r--src/help/studio_help/src/topics/t_obfuscating.dita39
-rw-r--r--src/help/studio_help/src/topics/t_project-creating-widget_android.dita58
-rw-r--r--src/help/studio_help/src/topics/t_project-creating_android.dita80
-rw-r--r--src/help/studio_help/src/topics/t_service-creating.dita47
-rw-r--r--src/help/studio_help/src/topics/u_android-handset-properties.dita14
-rw-r--r--src/help/studio_help/src/topics/u_app-signing-view.dita110
-rw-r--r--src/help/studio_help/src/topics/u_avd-offline-dialog.dita45
-rw-r--r--src/help/studio_help/src/topics/u_avd-properties.dita56
-rw-r--r--src/help/studio_help/src/topics/u_avd-startup-options.dita157
-rw-r--r--src/help/studio_help/src/topics/u_code-generator.dita52
-rw-r--r--src/help/studio_help/src/topics/u_collect-log-files.dita40
-rw-r--r--src/help/studio_help/src/topics/u_db-classes-create.dita66
-rw-r--r--src/help/studio_help/src/topics/u_db-create.dita35
-rw-r--r--src/help/studio_help/src/topics/u_db-database-explorer.dita97
-rw-r--r--src/help/studio_help/src/topics/u_db-database-perspective.dita27
-rw-r--r--src/help/studio_help/src/topics/u_db-sql-results-view.dita102
-rw-r--r--src/help/studio_help/src/topics/u_db-sql-scrapbook.dita56
-rw-r--r--src/help/studio_help/src/topics/u_db-table-create.dita39
-rw-r--r--src/help/studio_help/src/topics/u_device-app-uninstall.dita23
-rw-r--r--src/help/studio_help/src/topics/u_device-manager_android.dita149
-rw-r--r--src/help/studio_help/src/topics/u_device-properties_android.dita19
-rw-r--r--src/help/studio_help/src/topics/u_device-screen-capture.dita50
-rw-r--r--src/help/studio_help/src/topics/u_download-components.dita45
-rw-r--r--src/help/studio_help/src/topics/u_emulator-language.dita39
-rw-r--r--src/help/studio_help/src/topics/u_emulator_android.dita46
-rw-r--r--src/help/studio_help/src/topics/u_keystore-select.dita45
-rw-r--r--src/help/studio_help/src/topics/u_memory-analyze-app-select.dita20
-rw-r--r--src/help/studio_help/src/topics/u_mpkg-sign_android.dita60
-rw-r--r--src/help/studio_help/src/topics/u_mpkg-unsign_android.dita37
-rw-r--r--src/help/studio_help/src/topics/u_new-activity-template.dita91
-rw-r--r--src/help/studio_help/src/topics/u_new-activity.dita81
-rw-r--r--src/help/studio_help/src/topics/u_new-broadcast-receiver.dita62
-rw-r--r--src/help/studio_help/src/topics/u_new-content-provider.dita59
-rw-r--r--src/help/studio_help/src/topics/u_new-device-main_android.dita69
-rw-r--r--src/help/studio_help/src/topics/u_new-device-startup_android.dita165
-rw-r--r--src/help/studio_help/src/topics/u_new-device.dita22
-rw-r--r--src/help/studio_help/src/topics/u_new-proj-widget_android.dita62
-rw-r--r--src/help/studio_help/src/topics/u_new-proj_android.dita89
-rw-r--r--src/help/studio_help/src/topics/u_new-service.dita53
-rw-r--r--src/help/studio_help/src/topics/u_new-widget-provider.dita51
-rw-r--r--src/help/studio_help/src/topics/u_obfuscate-projects.dita24
-rw-r--r--src/help/studio_help/src/topics/u_obfuscation-prop.dita35
-rw-r--r--src/help/studio_help/src/topics/u_options-menu-code-create.dita42
-rw-r--r--src/help/studio_help/src/topics/u_pkg-export.dita63
-rw-r--r--src/help/studio_help/src/topics/u_pkg-install.dita32
-rw-r--r--src/help/studio_help/src/topics/u_run-config-main.dita67
-rw-r--r--src/help/studio_help/src/topics/u_run-config-monkey-main.dita59
-rw-r--r--src/help/studio_help/src/topics/u_save-ui-state.dita44
-rw-r--r--src/help/studio_help/src/topics/u_screen-calculator.dita41
-rw-r--r--src/help/studio_help/src/topics/u_sdk-download-location.dita25
-rw-r--r--src/help/studio_help/src/topics/u_select-activity.dita24
-rw-r--r--src/help/studio_help/src/topics/u_select-columns.dita21
-rw-r--r--src/help/studio_help/src/topics/u_select-instance.dita26
-rw-r--r--src/help/studio_help/src/topics/u_select-project.dita23
-rw-r--r--src/help/studio_help/src/topics/u_select-table.dita21
-rw-r--r--src/help/studio_help/src/topics/u_sign-cert-properties.dita61
-rw-r--r--src/help/studio_help/src/topics/u_sign-create-key-pair.dita52
-rw-r--r--src/help/studio_help/src/topics/u_sign-create-keystore.dita54
-rw-r--r--src/help/studio_help/src/topics/u_sign-create-self-cert.dita74
-rw-r--r--src/help/studio_help/src/topics/u_sign-import-cert.dita29
-rw-r--r--src/help/studio_help/src/topics/u_sign-import-key.dita29
-rw-r--r--src/help/studio_help/src/topics/u_sign-import-keystore-entries.dita42
-rw-r--r--src/help/studio_help/src/topics/u_sign-import-keystore.dita31
-rw-r--r--src/help/studio_help/src/topics/u_sign-keystore-backup-restore.dita34
-rw-r--r--src/help/studio_help/src/topics/u_sign-keystore-backup.dita30
-rw-r--r--src/help/studio_help/src/topics/u_sign-keystore-type-changing.dita53
-rw-r--r--src/help/studio_help/src/topics/u_studio-prefs.dita14
-rw-r--r--src/help/studio_help/src/topics/u_studio-prefs_android.dita41
-rw-r--r--src/help/studio_help/src/topics/u_studio-prefs_appstore.dita32
-rw-r--r--src/help/studio_help/src/topics/u_studio-prefs_db.dita31
-rw-r--r--src/help/studio_help/src/topics/u_studio-prefs_emulator.dita34
-rw-r--r--src/help/studio_help/src/topics/u_video-view.dita28
-rw-r--r--src/help/studio_help/tools/ditaot/plugins/com.mot.mdb.deved.xhtml/plugin.xml35
-rw-r--r--src/help/studio_help/tools/ditaot/plugins/com.mot.mdb.deved.xhtml/readme.dita17
-rw-r--r--src/help/studio_help/tools/ditaot/plugins/com.mot.mdb.deved.xhtml/xsl/deved_map2plugin.xsl122
-rw-r--r--src/help/studio_help/tools/ditaot/plugins/com.mot.mdb.deved.xhtml/xsl/deved_xhtml.xsl273
-rw-r--r--src/help/studio_help/tools/ditaot/plugins/cshelp/catalog-dita.xml19
-rw-r--r--src/help/studio_help/tools/ditaot/plugins/cshelp/dtd/cshelp.dtd101
-rw-r--r--src/help/studio_help/tools/ditaot/plugins/cshelp/dtd/cshelp.mod85
-rw-r--r--src/help/studio_help/tools/ditaot/plugins/cshelp/install-plugin.xml20
-rw-r--r--src/help/studio_help/tools/ditaot/plugins/cshelp/plugin.xml16
-rw-r--r--src/help/studio_help/tools/ditaot/plugins/cshelp/samples/test.ditamap17
-rw-r--r--src/help/studio_help/tools/ditaot/plugins/cshelp/samples/test_csh_1.dita47
-rw-r--r--src/help/studio_help/tools/ditaot/plugins/cshelp/samples/test_csh_2.dita33
-rw-r--r--src/help/studio_help/tools/ditaot/plugins/cshelp/xsl/GetCSHMeta.xsl591
-rw-r--r--src/help/studio_help/tools/ditaot/plugins/cshelp/xsl/context2contexttemp.xsl48
-rw-r--r--src/help/studio_help/tools/ditaot/plugins/cshelp/xsl/context2dita.xsl130
-rw-r--r--src/help/studio_help/tools/ditaot/plugins/cshelp/xsl/cshdisplay.xsl71
-rw-r--r--src/help/studio_help/tools/ditaot/plugins/cshelp/xsl/dit2context.xsl1275
-rw-r--r--src/plugins/android.codeutils/.classpath7
-rw-r--r--src/plugins/android.codeutils/.project28
-rw-r--r--src/plugins/android.codeutils/META-INF/MANIFEST.MF33
-rw-r--r--src/plugins/android.codeutils/build.properties9
-rw-r--r--src/plugins/android.codeutils/icons/device_refresh_on.pngbin0 -> 717 bytes
-rw-r--r--src/plugins/android.codeutils/icons/obj16/androidLogo.pngbin0 -> 3558 bytes
-rw-r--r--src/plugins/android.codeutils/icons/obj16/android_discussion_16x16.pngbin0 -> 1407 bytes
-rw-r--r--src/plugins/android.codeutils/icons/obj16/fill_code_from_layout_16x16.pngbin0 -> 1201 bytes
-rw-r--r--src/plugins/android.codeutils/icons/obj16/new_activity_template_wiz.pngbin0 -> 3246 bytes
-rw-r--r--src/plugins/android.codeutils/icons/obj16/new_activity_wiz.pngbin0 -> 3197 bytes
-rw-r--r--src/plugins/android.codeutils/icons/obj16/plate16.pngbin0 -> 3558 bytes
-rw-r--r--src/plugins/android.codeutils/icons/obj16/provider.pngbin0 -> 530 bytes
-rw-r--r--src/plugins/android.codeutils/icons/obj16/receiver.pngbin0 -> 575 bytes
-rw-r--r--src/plugins/android.codeutils/icons/obj16/service_new.gifbin0 -> 204 bytes
-rw-r--r--src/plugins/android.codeutils/icons/obj16/studio_discussion_16x16.pngbin0 -> 1659 bytes
-rw-r--r--src/plugins/android.codeutils/icons/obj16/widget_provider_block_wiz_toolbar.pngbin0 -> 434 bytes
-rw-r--r--src/plugins/android.codeutils/icons/views/building_block_explorer_plate.gifbin0 -> 1652 bytes
-rw-r--r--src/plugins/android.codeutils/icons/wizban/add_libraries_ban.pngbin0 -> 2560 bytes
-rw-r--r--src/plugins/android.codeutils/icons/wizban/create_management_classes.pngbin0 -> 2342 bytes
-rw-r--r--src/plugins/android.codeutils/icons/wizban/deploy_wizard.pngbin0 -> 3163 bytes
-rw-r--r--src/plugins/android.codeutils/icons/wizban/dump_hprof_wizard.pngbin0 -> 5534 bytes
-rw-r--r--src/plugins/android.codeutils/icons/wizban/fill_activity_ban.pngbin0 -> 2630 bytes
-rw-r--r--src/plugins/android.codeutils/icons/wizban/monkey_wizard.pngbin0 -> 6138 bytes
-rw-r--r--src/plugins/android.codeutils/icons/wizban/new_activity_template_wiz.pngbin0 -> 5392 bytes
-rw-r--r--src/plugins/android.codeutils/icons/wizban/new_activity_wiz.pngbin0 -> 4765 bytes
-rw-r--r--src/plugins/android.codeutils/icons/wizban/new_provider_wiz.pngbin0 -> 3308 bytes
-rw-r--r--src/plugins/android.codeutils/icons/wizban/new_receiver_wiz.pngbin0 -> 1787 bytes
-rw-r--r--src/plugins/android.codeutils/icons/wizban/new_service_wiz.pngbin0 -> 5046 bytes
-rw-r--r--src/plugins/android.codeutils/icons/wizban/newprjwiz.pngbin0 -> 6832 bytes
-rw-r--r--src/plugins/android.codeutils/icons/wizban/obfuscate.gifbin0 -> 2620 bytes
-rw-r--r--src/plugins/android.codeutils/icons/wizban/undeploy_wizard.pngbin0 -> 2052 bytes
-rw-r--r--src/plugins/android.codeutils/icons/wizban/widget_provider_block_wiz.pngbin0 -> 3832 bytes
-rw-r--r--src/plugins/android.codeutils/icons/wizban/widget_provider_prj_wiz.pngbin0 -> 7532 bytes
-rw-r--r--src/plugins/android.codeutils/plugin.properties116
-rw-r--r--src/plugins/android.codeutils/plugin.xml692
-rw-r--r--src/plugins/android.codeutils/resources/databaseDeploy/ContentProviderByTablejava.txt137
-rw-r--r--src/plugins/android.codeutils/resources/databaseDeploy/ContentProviderHelperjava.txt82
-rw-r--r--src/plugins/android.codeutils/resources/databaseDeploy/DAOByTablejava.txt138
-rw-r--r--src/plugins/android.codeutils/resources/databaseDeploy/DatabaseHelperjava.txt197
-rw-r--r--src/plugins/android.codeutils/resources/databaseDeploy/DatabaseListActivity.txt179
-rw-r--r--src/plugins/android.codeutils/schema/com.motorola.studio.android.sampleActivityDatabase.exsd102
-rw-r--r--src/plugins/android.codeutils/src/com/motorola/studio/android/codeutils/CodeUtilsActivator.java85
-rw-r--r--src/plugins/android.codeutils/src/com/motorola/studio/android/codeutils/codegeneration/CreateSampleDatabaseActivityColumnsPage.java382
-rw-r--r--src/plugins/android.codeutils/src/com/motorola/studio/android/codeutils/codegeneration/CreateSampleDatabaseActivityPage.java404
-rw-r--r--src/plugins/android.codeutils/src/com/motorola/studio/android/codeutils/codegeneration/DatabaseListActivityGeneratorByTable.java388
-rw-r--r--src/plugins/android.codeutils/src/com/motorola/studio/android/codeutils/codegeneration/DefineSqlOpenHelperPage.java347
-rw-r--r--src/plugins/android.codeutils/src/com/motorola/studio/android/codeutils/codegeneration/SampleDatabaseActivityColumnsPageLabelProvider.java77
-rw-r--r--src/plugins/android.codeutils/src/com/motorola/studio/android/codeutils/codegeneration/SampleDatabaseActivityPageLabelProvider.java109
-rw-r--r--src/plugins/android.codeutils/src/com/motorola/studio/android/codeutils/db/actions/AbstractCodeGeneratorByTable.java99
-rw-r--r--src/plugins/android.codeutils/src/com/motorola/studio/android/codeutils/db/actions/ContentProviderGeneratorByTable.java662
-rw-r--r--src/plugins/android.codeutils/src/com/motorola/studio/android/codeutils/db/utils/DatabaseUtils.java1103
-rw-r--r--src/plugins/android.codeutils/src/com/motorola/studio/android/codeutils/i18n/CodeUtilsNLS.java539
-rw-r--r--src/plugins/android.codeutils/src/com/motorola/studio/android/codeutils/i18n/codeUtilsNLS.properties298
-rw-r--r--src/plugins/android.codeutils/src/com/motorola/studio/android/codeutils/wizards/DatabaseManagementClassesCreationMainPage.java1187
-rw-r--r--src/plugins/android.codeutils/src/com/motorola/studio/android/codeutils/wizards/DatabaseManagementClassesCreationWizard.java317
-rw-r--r--src/plugins/android.codeutils/src/com/motorola/studio/android/codeutils/wizards/SourcePackageChooserPartWizard.java215
-rw-r--r--src/plugins/android.codeutils/src/com/motorola/studio/android/command/NewActivityBasedOnTemplateHandler.java35
-rw-r--r--src/plugins/android.codeutils/src/com/motorola/studio/android/command/NewActivityWizard.java35
-rw-r--r--src/plugins/android.codeutils/src/com/motorola/studio/android/command/NewBroadcastReceiverWizard.java37
-rw-r--r--src/plugins/android.codeutils/src/com/motorola/studio/android/command/NewContentProviderWizard.java37
-rw-r--r--src/plugins/android.codeutils/src/com/motorola/studio/android/command/NewServiceWizard.java35
-rw-r--r--src/plugins/android.codeutils/src/com/motorola/studio/android/command/NewWidgetProviderWizard.java36
-rw-r--r--src/plugins/android.codeutils/src/com/motorola/studio/android/command/NewWizardHandler.java87
-rw-r--r--src/plugins/android.codeutils/src/com/motorola/studio/android/db/deployment/ContentProviderDeployer.java353
-rw-r--r--src/plugins/android.codeutils/src/com/motorola/studio/android/db/deployment/ContentProviderDeployerByTable.java29
-rw-r--r--src/plugins/android.codeutils/src/com/motorola/studio/android/db/deployment/DatabaseDeployer.java222
-rw-r--r--src/plugins/android.codeutils/src/com/motorola/studio/android/db/wizards/model/Field.java162
-rw-r--r--src/plugins/android.codeutils/src/com/motorola/studio/android/db/wizards/model/Table.java225
-rw-r--r--src/plugins/android.codeutils/src/com/motorola/studio/android/generatecode/AbstractCodeGenerator.java632
-rw-r--r--src/plugins/android.codeutils/src/com/motorola/studio/android/generatecode/AbstractCodeGeneratorData.java67
-rw-r--r--src/plugins/android.codeutils/src/com/motorola/studio/android/generatecode/AndroidXMLFileConstants.java30
-rw-r--r--src/plugins/android.codeutils/src/com/motorola/studio/android/generatecode/BasicCodeVisitor.java58
-rw-r--r--src/plugins/android.codeutils/src/com/motorola/studio/android/generatecode/JDTUtils.java618
-rw-r--r--src/plugins/android.codeutils/src/com/motorola/studio/android/generatecode/JavaCodeModifier.java280
-rw-r--r--src/plugins/android.codeutils/src/com/motorola/studio/android/generatemenucode/model/AbstractMenuNode.java31
-rw-r--r--src/plugins/android.codeutils/src/com/motorola/studio/android/generatemenucode/model/GroupNode.java64
-rw-r--r--src/plugins/android.codeutils/src/com/motorola/studio/android/generatemenucode/model/MenuFile.java261
-rw-r--r--src/plugins/android.codeutils/src/com/motorola/studio/android/generatemenucode/model/MenuItemNode.java79
-rw-r--r--src/plugins/android.codeutils/src/com/motorola/studio/android/generatemenucode/model/MenuNode.java83
-rw-r--r--src/plugins/android.codeutils/src/com/motorola/studio/android/generatemenucode/model/codegenerators/AbstractMenuCodeGenerator.java47
-rw-r--r--src/plugins/android.codeutils/src/com/motorola/studio/android/generatemenucode/model/codegenerators/CodeGeneratorBasedOnMenuConstants.java57
-rw-r--r--src/plugins/android.codeutils/src/com/motorola/studio/android/generatemenucode/model/codegenerators/CodeGeneratorBasedOnMenuVisitor.java153
-rw-r--r--src/plugins/android.codeutils/src/com/motorola/studio/android/generatemenucode/model/codegenerators/CodeGeneratorDataBasedOnMenu.java123
-rw-r--r--src/plugins/android.codeutils/src/com/motorola/studio/android/generatemenucode/model/codegenerators/JavaModifierBasedOnMenu.java59
-rw-r--r--src/plugins/android.codeutils/src/com/motorola/studio/android/generatemenucode/model/codegenerators/MenuHandlerCodeGenerator.java599
-rw-r--r--src/plugins/android.codeutils/src/com/motorola/studio/android/generatemenucode/ui/GenerateMenuCodeDialog.java656
-rw-r--r--src/plugins/android.codeutils/src/com/motorola/studio/android/generatemenucode/ui/GenerateMenuCodeHandler.java333
-rw-r--r--src/plugins/android.codeutils/src/com/motorola/studio/android/generateviewbylayout/GenerateCodeBasedOnLayoutVisitor.java350
-rw-r--r--src/plugins/android.codeutils/src/com/motorola/studio/android/generateviewbylayout/JavaModifierBasedOnLayout.java142
-rw-r--r--src/plugins/android.codeutils/src/com/motorola/studio/android/generateviewbylayout/JavaViewBasedOnLayoutModifierConstants.java126
-rw-r--r--src/plugins/android.codeutils/src/com/motorola/studio/android/generateviewbylayout/MethodBodyVisitor.java226
-rw-r--r--src/plugins/android.codeutils/src/com/motorola/studio/android/generateviewbylayout/SaveStateVisitor.java139
-rw-r--r--src/plugins/android.codeutils/src/com/motorola/studio/android/generateviewbylayout/codegenerators/AbstractLayoutCodeGenerator.java420
-rw-r--r--src/plugins/android.codeutils/src/com/motorola/studio/android/generateviewbylayout/codegenerators/ClassAttributesCodeGenerator.java114
-rw-r--r--src/plugins/android.codeutils/src/com/motorola/studio/android/generateviewbylayout/codegenerators/EditTextCodeGenerator.java144
-rw-r--r--src/plugins/android.codeutils/src/com/motorola/studio/android/generateviewbylayout/codegenerators/FindViewByIdCodeGenerator.java156
-rw-r--r--src/plugins/android.codeutils/src/com/motorola/studio/android/generateviewbylayout/codegenerators/GalleryCodeGenerator.java126
-rw-r--r--src/plugins/android.codeutils/src/com/motorola/studio/android/generateviewbylayout/codegenerators/OnClickGUIsCodeGenerator.java160
-rw-r--r--src/plugins/android.codeutils/src/com/motorola/studio/android/generateviewbylayout/codegenerators/RadioButtonCodeGenerator.java361
-rw-r--r--src/plugins/android.codeutils/src/com/motorola/studio/android/generateviewbylayout/codegenerators/RatingBarCodeGenerator.java123
-rw-r--r--src/plugins/android.codeutils/src/com/motorola/studio/android/generateviewbylayout/codegenerators/SaveStateCodeGenerator.java619
-rw-r--r--src/plugins/android.codeutils/src/com/motorola/studio/android/generateviewbylayout/codegenerators/SeekBarCodeGenerator.java156
-rw-r--r--src/plugins/android.codeutils/src/com/motorola/studio/android/generateviewbylayout/codegenerators/SpinnerCodeGenerator.java146
-rw-r--r--src/plugins/android.codeutils/src/com/motorola/studio/android/generateviewbylayout/model/CheckboxNode.java40
-rw-r--r--src/plugins/android.codeutils/src/com/motorola/studio/android/generateviewbylayout/model/CodeGeneratorDataBasedOnLayout.java260
-rw-r--r--src/plugins/android.codeutils/src/com/motorola/studio/android/generateviewbylayout/model/EditTextNode.java40
-rw-r--r--src/plugins/android.codeutils/src/com/motorola/studio/android/generateviewbylayout/model/JavaLayoutData.java169
-rw-r--r--src/plugins/android.codeutils/src/com/motorola/studio/android/generateviewbylayout/model/LayoutFile.java238
-rw-r--r--src/plugins/android.codeutils/src/com/motorola/studio/android/generateviewbylayout/model/LayoutNode.java276
-rw-r--r--src/plugins/android.codeutils/src/com/motorola/studio/android/generateviewbylayout/model/RadioButtonNode.java40
-rw-r--r--src/plugins/android.codeutils/src/com/motorola/studio/android/generateviewbylayout/model/SeekBarNode.java40
-rw-r--r--src/plugins/android.codeutils/src/com/motorola/studio/android/generateviewbylayout/model/SpinnerNode.java41
-rw-r--r--src/plugins/android.codeutils/src/com/motorola/studio/android/generateviewbylayout/ui/AbstractCodeGeneratorHandler.java295
-rw-r--r--src/plugins/android.codeutils/src/com/motorola/studio/android/generateviewbylayout/ui/AbstractLayoutItemsDialog.java766
-rw-r--r--src/plugins/android.codeutils/src/com/motorola/studio/android/generateviewbylayout/ui/ChooseLayoutItemsDialog.java335
-rw-r--r--src/plugins/android.codeutils/src/com/motorola/studio/android/generateviewbylayout/ui/FillOnSaveInstanceStateDialog.java109
-rw-r--r--src/plugins/android.codeutils/src/com/motorola/studio/android/generateviewbylayout/ui/FillOnSaveInstanceStateHandler.java58
-rw-r--r--src/plugins/android.codeutils/src/com/motorola/studio/android/generateviewbylayout/ui/GenerateViewBasedOnLayoutHandler.java56
-rw-r--r--src/plugins/android.codeutils/src/com/motorola/studio/android/model/Activity.java580
-rw-r--r--src/plugins/android.codeutils/src/com/motorola/studio/android/model/ActivityBasedOnTemplate.java1982
-rw-r--r--src/plugins/android.codeutils/src/com/motorola/studio/android/model/BuildingBlockModel.java562
-rw-r--r--src/plugins/android.codeutils/src/com/motorola/studio/android/model/ContentProvider.java467
-rw-r--r--src/plugins/android.codeutils/src/com/motorola/studio/android/model/IDatabaseSampleActivityParametersWizardCollector.java85
-rw-r--r--src/plugins/android.codeutils/src/com/motorola/studio/android/model/IWizardModel.java58
-rw-r--r--src/plugins/android.codeutils/src/com/motorola/studio/android/model/Launcher.java246
-rw-r--r--src/plugins/android.codeutils/src/com/motorola/studio/android/model/Receiver.java344
-rw-r--r--src/plugins/android.codeutils/src/com/motorola/studio/android/model/Service.java387
-rw-r--r--src/plugins/android.codeutils/src/com/motorola/studio/android/model/TemplateFile.java83
-rw-r--r--src/plugins/android.codeutils/src/com/motorola/studio/android/model/WidgetProvider.java543
-rw-r--r--src/plugins/android.codeutils/src/com/motorola/studio/android/model/java/ActivityClass.java223
-rw-r--r--src/plugins/android.codeutils/src/com/motorola/studio/android/model/java/BroadcastReceiverClass.java190
-rw-r--r--src/plugins/android.codeutils/src/com/motorola/studio/android/model/java/ContentProviderClass.java433
-rw-r--r--src/plugins/android.codeutils/src/com/motorola/studio/android/model/java/JavaClass.java426
-rw-r--r--src/plugins/android.codeutils/src/com/motorola/studio/android/model/java/ServiceClass.java234
-rw-r--r--src/plugins/android.codeutils/src/com/motorola/studio/android/model/java/WidgetProviderClass.java284
-rw-r--r--src/plugins/android.codeutils/src/com/motorola/studio/android/model/resources/ResourceFile.java335
-rw-r--r--src/plugins/android.codeutils/src/com/motorola/studio/android/model/resources/parser/AbstractResourceFileParser.java374
-rw-r--r--src/plugins/android.codeutils/src/com/motorola/studio/android/model/resources/types/AbstractResourceNode.java388
-rw-r--r--src/plugins/android.codeutils/src/com/motorola/studio/android/model/resources/types/AbstractSimpleNameResourceNode.java110
-rw-r--r--src/plugins/android.codeutils/src/com/motorola/studio/android/model/resources/types/ColorNode.java43
-rw-r--r--src/plugins/android.codeutils/src/com/motorola/studio/android/model/resources/types/DimenNode.java43
-rw-r--r--src/plugins/android.codeutils/src/com/motorola/studio/android/model/resources/types/DrawableNode.java43
-rw-r--r--src/plugins/android.codeutils/src/com/motorola/studio/android/model/resources/types/IResourceTypesAttributes.java24
-rw-r--r--src/plugins/android.codeutils/src/com/motorola/studio/android/model/resources/types/ResourcesNode.java62
-rw-r--r--src/plugins/android.codeutils/src/com/motorola/studio/android/model/resources/types/StringNode.java74
-rw-r--r--src/plugins/android.codeutils/src/com/motorola/studio/android/model/resources/types/UnknownNode.java101
-rw-r--r--src/plugins/android.codeutils/src/com/motorola/studio/android/resources/AndroidProjectResources.java164
-rw-r--r--src/plugins/android.codeutils/src/com/motorola/studio/android/wizards/buildingblocks/ActivitySampleSelectionPage.java364
-rw-r--r--src/plugins/android.codeutils/src/com/motorola/studio/android/wizards/buildingblocks/ElementTreeContentProvider.java63
-rw-r--r--src/plugins/android.codeutils/src/com/motorola/studio/android/wizards/buildingblocks/ElementTreeValidator.java82
-rw-r--r--src/plugins/android.codeutils/src/com/motorola/studio/android/wizards/buildingblocks/ElementTreeViewFilter.java72
-rw-r--r--src/plugins/android.codeutils/src/com/motorola/studio/android/wizards/buildingblocks/FilteredActionsSelectionDialog.java153
-rw-r--r--src/plugins/android.codeutils/src/com/motorola/studio/android/wizards/buildingblocks/Method.java53
-rw-r--r--src/plugins/android.codeutils/src/com/motorola/studio/android/wizards/buildingblocks/NewActivityBasedOnTemplatePage.java258
-rw-r--r--src/plugins/android.codeutils/src/com/motorola/studio/android/wizards/buildingblocks/NewActivityBasedOnTemplateWizard.java219
-rw-r--r--src/plugins/android.codeutils/src/com/motorola/studio/android/wizards/buildingblocks/NewActivityMainPage.java277
-rw-r--r--src/plugins/android.codeutils/src/com/motorola/studio/android/wizards/buildingblocks/NewActivityWizard.java235
-rw-r--r--src/plugins/android.codeutils/src/com/motorola/studio/android/wizards/buildingblocks/NewActivityWizardListTemplatesPage.java383
-rw-r--r--src/plugins/android.codeutils/src/com/motorola/studio/android/wizards/buildingblocks/NewBuildingBlocksWizard.java44
-rw-r--r--src/plugins/android.codeutils/src/com/motorola/studio/android/wizards/buildingblocks/NewBuildingBlocksWizardPage.java1078
-rw-r--r--src/plugins/android.codeutils/src/com/motorola/studio/android/wizards/buildingblocks/NewLauncherWizardPage.java344
-rw-r--r--src/plugins/android.codeutils/src/com/motorola/studio/android/wizards/buildingblocks/NewProviderMainPage.java345
-rw-r--r--src/plugins/android.codeutils/src/com/motorola/studio/android/wizards/buildingblocks/NewProviderWizard.java191
-rw-r--r--src/plugins/android.codeutils/src/com/motorola/studio/android/wizards/buildingblocks/NewReceiverMainPage.java119
-rw-r--r--src/plugins/android.codeutils/src/com/motorola/studio/android/wizards/buildingblocks/NewReceiverWizard.java192
-rw-r--r--src/plugins/android.codeutils/src/com/motorola/studio/android/wizards/buildingblocks/NewServiceMainPage.java110
-rw-r--r--src/plugins/android.codeutils/src/com/motorola/studio/android/wizards/buildingblocks/NewServiceWizard.java190
-rw-r--r--src/plugins/android.codeutils/src/com/motorola/studio/android/wizards/buildingblocks/NewWidgetProviderMainPage.java110
-rw-r--r--src/plugins/android.codeutils/src/com/motorola/studio/android/wizards/buildingblocks/NewWidgetProviderWizard.java186
-rw-r--r--src/plugins/android.codeutils/templates/activity_samples/action_bar/ablayout.xml7
-rw-r--r--src/plugins/android.codeutils/templates/activity_samples/action_bar/abmenu.xml30
-rw-r--r--src/plugins/android.codeutils/templates/activity_samples/action_bar/action_bar_activity.java119
-rw-r--r--src/plugins/android.codeutils/templates/activity_samples/action_bar/action_bar_strings.xml9
-rw-r--r--src/plugins/android.codeutils/templates/activity_samples/action_bar/actionbaricon.pngbin0 -> 6458 bytes
-rw-r--r--src/plugins/android.codeutils/templates/activity_samples/action_bar_compatibility/ablayout.xml13
-rw-r--r--src/plugins/android.codeutils/templates/activity_samples/action_bar_compatibility/action_bar_compatibility_activity.java66
-rw-r--r--src/plugins/android.codeutils/templates/activity_samples/action_bar_compatibility/action_bar_compatibility_strings.xml7
-rw-r--r--src/plugins/android.codeutils/templates/activity_samples/action_bar_compatibility/actionbar.xml98
-rw-r--r--src/plugins/android.codeutils/templates/activity_samples/action_bar_compatibility/actionbaricon.pngbin0 -> 6458 bytes
-rw-r--r--src/plugins/android.codeutils/templates/activity_samples/dashboard_pattern/dashboard_activity.java102
-rw-r--r--src/plugins/android.codeutils/templates/activity_samples/dashboard_pattern/dashboard_icon.pngbin0 -> 5260 bytes
-rw-r--r--src/plugins/android.codeutils/templates/activity_samples/dashboard_pattern/dashboard_item_background.xml8
-rw-r--r--src/plugins/android.codeutils/templates/activity_samples/dashboard_pattern/dashboard_layout.xml39
-rw-r--r--src/plugins/android.codeutils/templates/activity_samples/dashboard_pattern/dashboard_layout_land.xml37
-rw-r--r--src/plugins/android.codeutils/templates/activity_samples/dashboard_pattern/dashboard_strings.xml9
-rw-r--r--src/plugins/android.codeutils/templates/activity_samples/database_list/database_list_activity.java161
-rw-r--r--src/plugins/android.codeutils/templates/activity_samples/database_list/databaselayout.xml8
-rw-r--r--src/plugins/android.codeutils/templates/activity_samples/dialog/dialog_activity.java68
-rw-r--r--src/plugins/android.codeutils/templates/activity_samples/dialog/dialog_strings.xml10
-rw-r--r--src/plugins/android.codeutils/templates/activity_samples/dialog/dialoglayout.xml11
-rw-r--r--src/plugins/android.codeutils/templates/activity_samples/dropdown_list/ddlist_strings.xml22
-rw-r--r--src/plugins/android.codeutils/templates/activity_samples/dropdown_list/ddlistlayout.xml17
-rw-r--r--src/plugins/android.codeutils/templates/activity_samples/dropdown_list/dropdown_list_activity.java64
-rw-r--r--src/plugins/android.codeutils/templates/activity_samples/dropdown_list/preview.pngbin0 -> 20262 bytes
-rw-r--r--src/plugins/android.codeutils/templates/activity_samples/endless_list/ListElement.java97
-rw-r--r--src/plugins/android.codeutils/templates/activity_samples/endless_list/endless_list_activity.java334
-rw-r--r--src/plugins/android.codeutils/templates/activity_samples/endless_list/endless_list_strings.xml6
-rw-r--r--src/plugins/android.codeutils/templates/activity_samples/endless_list/listicon.pngbin0 -> 4980 bytes
-rw-r--r--src/plugins/android.codeutils/templates/activity_samples/endless_list/listrow.xml23
-rw-r--r--src/plugins/android.codeutils/templates/activity_samples/endless_list/preview.pngbin0 -> 19443 bytes
-rw-r--r--src/plugins/android.codeutils/templates/activity_samples/endless_list_pull_to_refresh/ListElement.java97
-rw-r--r--src/plugins/android.codeutils/templates/activity_samples/endless_list_pull_to_refresh/endless_list_pull_to_refresh_strings.xml9
-rw-r--r--src/plugins/android.codeutils/templates/activity_samples/endless_list_pull_to_refresh/listheader.xml25
-rw-r--r--src/plugins/android.codeutils/templates/activity_samples/endless_list_pull_to_refresh/listicon.pngbin0 -> 4980 bytes
-rw-r--r--src/plugins/android.codeutils/templates/activity_samples/endless_list_pull_to_refresh/listrow.xml23
-rw-r--r--src/plugins/android.codeutils/templates/activity_samples/endless_list_pull_to_refresh/preview.pngbin0 -> 11680 bytes
-rw-r--r--src/plugins/android.codeutils/templates/activity_samples/endless_list_pull_to_refresh/pull_to_refresh_activity.java404
-rw-r--r--src/plugins/android.codeutils/templates/activity_samples/endless_list_usingfragment/FragmentEndlessList.java358
-rw-r--r--src/plugins/android.codeutils/templates/activity_samples/endless_list_usingfragment/ListElement.java97
-rw-r--r--src/plugins/android.codeutils/templates/activity_samples/endless_list_usingfragment/activitylayout.xml8
-rw-r--r--src/plugins/android.codeutils/templates/activity_samples/endless_list_usingfragment/endless_list_activity.java43
-rw-r--r--src/plugins/android.codeutils/templates/activity_samples/endless_list_usingfragment/endless_list_usingfragment_strings.xml6
-rw-r--r--src/plugins/android.codeutils/templates/activity_samples/endless_list_usingfragment/listicon.pngbin0 -> 4980 bytes
-rw-r--r--src/plugins/android.codeutils/templates/activity_samples/endless_list_usingfragment/listrow.xml23
-rw-r--r--src/plugins/android.codeutils/templates/activity_samples/endless_list_usingfragment/preview.pngbin0 -> 19443 bytes
-rw-r--r--src/plugins/android.codeutils/templates/activity_samples/expandable_list/group_row.xml15
-rw-r--r--src/plugins/android.codeutils/templates/activity_samples/expandable_list/subgroup_row.xml15
-rw-r--r--src/plugins/android.codeutils/templates/activity_samples/img_text_list/img_text_activity.java83
-rw-r--r--src/plugins/android.codeutils/templates/activity_samples/img_text_list/img_text_strings.xml7
-rw-r--r--src/plugins/android.codeutils/templates/activity_samples/img_text_list/listicon.pngbin0 -> 4980 bytes
-rw-r--r--src/plugins/android.codeutils/templates/activity_samples/img_text_list/listrow.xml21
-rw-r--r--src/plugins/android.codeutils/templates/activity_samples/img_text_list/listview.xml14
-rw-r--r--src/plugins/android.codeutils/templates/activity_samples/img_text_list/preview.pngbin0 -> 21886 bytes
-rw-r--r--src/plugins/android.codeutils/templates/activity_samples/list_activities_config.xml61
-rw-r--r--src/plugins/android.codeutils/templates/activity_samples/mult_selection_img/listicon.pngbin0 -> 4980 bytes
-rw-r--r--src/plugins/android.codeutils/templates/activity_samples/mult_selection_img/listrowmult.xml49
-rw-r--r--src/plugins/android.codeutils/templates/activity_samples/mult_selection_img/listviewmult.xml22
-rw-r--r--src/plugins/android.codeutils/templates/activity_samples/mult_selection_img/preview.pngbin0 -> 30059 bytes
-rw-r--r--src/plugins/android.codeutils/templates/activity_samples/mult_selection_img/select_img_list_activity.java242
-rw-r--r--src/plugins/android.codeutils/templates/activity_samples/mult_selection_img/select_img_list_strings.xml7
-rw-r--r--src/plugins/android.codeutils/templates/activity_samples/multitouch_event/multitouch.jpgbin0 -> 7322 bytes
-rw-r--r--src/plugins/android.codeutils/templates/activity_samples/multitouch_event/multitouch_event_activity.java135
-rw-r--r--src/plugins/android.codeutils/templates/activity_samples/multitouch_event/multitouchlayout.xml11
-rw-r--r--src/plugins/android.codeutils/templates/activity_samples/pref_activity/preference_activity.java39
-rw-r--r--src/plugins/android.codeutils/templates/activity_samples/pref_activity/samplelist.xml27
-rw-r--r--src/plugins/android.codeutils/templates/activity_samples/pref_activity/samplepreferences.xml27
-rw-r--r--src/plugins/android.codeutils/templates/activity_samples/quickaction/icon.pngbin0 -> 3125 bytes
-rw-r--r--src/plugins/android.codeutils/templates/activity_samples/quickaction/imgbuttonselector.xml10
-rw-r--r--src/plugins/android.codeutils/templates/activity_samples/quickaction/imgbuttonselector_background.xml8
-rw-r--r--src/plugins/android.codeutils/templates/activity_samples/quickaction/quick_action.java180
-rw-r--r--src/plugins/android.codeutils/templates/activity_samples/quickaction/quick_action_activity.xml67
-rw-r--r--src/plugins/android.codeutils/templates/activity_samples/quickaction/quick_action_grid.xml36
-rw-r--r--src/plugins/android.codeutils/templates/activity_samples/quickaction/quick_action_item.xml25
-rw-r--r--src/plugins/android.codeutils/templates/activity_samples/quickaction/quick_action_strings.xml4
-rw-r--r--src/plugins/android.codeutils/templates/activity_samples/quickaction/quickaction_list_anim.xml5
-rw-r--r--src/plugins/android.codeutils/templates/activity_samples/quickaction/quickaction_list_bottom.9.pngbin0 -> 263 bytes
-rw-r--r--src/plugins/android.codeutils/templates/activity_samples/quickaction/quickaction_list_top.9.pngbin0 -> 260 bytes
-rw-r--r--src/plugins/android.codeutils/templates/activity_samples/radio_button/radio_button_activity.java48
-rw-r--r--src/plugins/android.codeutils/templates/activity_samples/radio_button/radio_strings.xml8
-rw-r--r--src/plugins/android.codeutils/templates/activity_samples/radio_button/radiobuttonlayout.xml27
-rw-r--r--src/plugins/android.codeutils/templates/activity_samples/samples_config.xml69
-rw-r--r--src/plugins/android.codeutils/templates/activity_samples/selection_list/preview.pngbin0 -> 21560 bytes
-rw-r--r--src/plugins/android.codeutils/templates/activity_samples/selection_list/selection_list_activity.java62
-rw-r--r--src/plugins/android.codeutils/templates/activity_samples/selection_list/selection_strings.xml16
-rw-r--r--src/plugins/android.codeutils/templates/activity_samples/simple_list/preview.pngbin0 -> 13794 bytes
-rw-r--r--src/plugins/android.codeutils/templates/activity_samples/simple_list/simple_list_activity.java52
-rw-r--r--src/plugins/android.codeutils/templates/activity_samples/simple_list/simple_list_strings.xml21
-rw-r--r--src/plugins/android.codeutils/templates/activity_samples/single_selection_img/listiconsingle.pngbin0 -> 4980 bytes
-rw-r--r--src/plugins/android.codeutils/templates/activity_samples/single_selection_img/listrowsingle.xml49
-rw-r--r--src/plugins/android.codeutils/templates/activity_samples/single_selection_img/listviewsingle.xml15
-rw-r--r--src/plugins/android.codeutils/templates/activity_samples/single_selection_img/preview.pngbin0 -> 33442 bytes
-rw-r--r--src/plugins/android.codeutils/templates/activity_samples/single_selection_img/single_sel_img_activity.java242
-rw-r--r--src/plugins/android.codeutils/templates/activity_samples/single_selection_img/single_sel_img_strings.xml7
-rw-r--r--src/plugins/android.codeutils/templates/activity_samples/tabs/tabicon.pngbin0 -> 4980 bytes
-rw-r--r--src/plugins/android.codeutils/templates/activity_samples/tabs/tabs_activity.java68
-rw-r--r--src/plugins/android.codeutils/templates/activity_samples/tabs/tabs_strings.xml5
-rw-r--r--src/plugins/android.codeutils/templates/widget_project/WidgetProvider.template30
-rw-r--r--src/plugins/android.codeutils/templates/widget_project/receiver.template6
-rw-r--r--src/plugins/android.codeutils/templates/widget_project/widget_info.xml9
-rw-r--r--src/plugins/android.codeutils/templates/widget_project/widget_initial_layout.xml25
-rw-r--r--src/plugins/android.linux.x86/.classpath7
-rw-r--r--src/plugins/android.linux.x86/.project28
-rw-r--r--src/plugins/android.linux.x86/META-INF/MANIFEST.MF10
-rw-r--r--src/plugins/android.linux.x86/build.properties7
-rw-r--r--src/plugins/android.linux.x86/fragment.properties3
-rw-r--r--src/plugins/android.linux.x86/fragment.xml15
-rw-r--r--src/plugins/android.linux.x86/gtk_bridge_appbin0 -> 70664 bytes
-rw-r--r--src/plugins/android.linux.x86/src/com/motorola/studio/android/nativeos/NativeUI.java175
-rw-r--r--src/plugins/android.linux.x86/src/com/motorola/studio/android/nativeos/linux/gtk/GtkBridge.java275
-rw-r--r--src/plugins/android.linux.x86_64/.classpath7
-rw-r--r--src/plugins/android.linux.x86_64/.project28
-rw-r--r--src/plugins/android.linux.x86_64/META-INF/MANIFEST.MF10
-rw-r--r--src/plugins/android.linux.x86_64/build.properties8
-rw-r--r--src/plugins/android.linux.x86_64/fragment.properties3
-rw-r--r--src/plugins/android.linux.x86_64/fragment.xml15
-rw-r--r--src/plugins/android.linux.x86_64/gtk_bridge_appbin0 -> 77896 bytes
-rw-r--r--src/plugins/android.linux.x86_64/src/com/motorola/studio/android/nativeos/NativeUI.java173
-rw-r--r--src/plugins/android.linux.x86_64/src/com/motorola/studio/android/nativeos/linux/gtk/GtkBridge.java275
-rw-r--r--src/plugins/android.macosx/.classpath7
-rw-r--r--src/plugins/android.macosx/.project28
-rw-r--r--src/plugins/android.macosx/META-INF/MANIFEST.MF10
-rw-r--r--src/plugins/android.macosx/build.properties5
-rw-r--r--src/plugins/android.macosx/fragment.properties2
-rw-r--r--src/plugins/android.macosx/src/com/motorola/studio/android/nativeos/NativeUI.java172
-rw-r--r--src/plugins/android.win32.x86/.classpath7
-rw-r--r--src/plugins/android.win32.x86/.project28
-rw-r--r--src/plugins/android.win32.x86/META-INF/MANIFEST.MF11
-rw-r--r--src/plugins/android.win32.x86/build.properties6
-rw-r--r--src/plugins/android.win32.x86/fragment.properties3
-rw-r--r--src/plugins/android.win32.x86/fragment.xml15
-rw-r--r--src/plugins/android.win32.x86/src/com/motorola/studio/android/nativeos/NativeUI.java200
-rw-r--r--src/plugins/android.win32.x86_64/.classpath7
-rw-r--r--src/plugins/android.win32.x86_64/.project28
-rw-r--r--src/plugins/android.win32.x86_64/META-INF/MANIFEST.MF10
-rw-r--r--src/plugins/android.win32.x86_64/build.properties6
-rw-r--r--src/plugins/android.win32.x86_64/fragment.properties3
-rw-r--r--src/plugins/android.win32.x86_64/fragment.xml15
-rw-r--r--src/plugins/android.win32.x86_64/src/com/motorola/studio/android/nativeos/NativeUI.java200
-rw-r--r--src/plugins/android/.classpath9
-rw-r--r--src/plugins/android/.project34
-rw-r--r--src/plugins/android/META-INF/MANIFEST.MF45
-rw-r--r--src/plugins/android/build.properties15
-rw-r--r--src/plugins/android/commons-net-1.4.1.jarbin0 -> 180792 bytes
-rw-r--r--src/plugins/android/files/proguard.cfg40
-rw-r--r--src/plugins/android/icons/monkey/monkey_16.pngbin0 -> 916 bytes
-rw-r--r--src/plugins/android/icons/monkey/motodevapp.gifbin0 -> 1666 bytes
-rw-r--r--src/plugins/android/icons/obj16/Android_Market.gifbin0 -> 1018 bytes
-rw-r--r--src/plugins/android/icons/obj16/add_libraries_window_icon.pngbin0 -> 1173 bytes
-rw-r--r--src/plugins/android/icons/obj16/android_discussion_16x16.pngbin0 -> 1407 bytes
-rw-r--r--src/plugins/android/icons/obj16/deploy.pngbin0 -> 243 bytes
-rw-r--r--src/plugins/android/icons/obj16/fill_code_from_layout_disabled_16x16.pngbin0 -> 1159 bytes
-rw-r--r--src/plugins/android/icons/obj16/handset_info.pngbin0 -> 735 bytes
-rw-r--r--src/plugins/android/icons/obj16/newaprj_wiz.gifbin0 -> 601 bytes
-rw-r--r--src/plugins/android/icons/obj16/obfuscate.pngbin0 -> 943 bytes
-rw-r--r--src/plugins/android/icons/obj16/plate16.pngbin0 -> 3558 bytes
-rw-r--r--src/plugins/android/icons/obj16/skewed_box_16x16.gifbin0 -> 1030 bytes
-rw-r--r--src/plugins/android/icons/obj16/studio_discussion_16x16.pngbin0 -> 1659 bytes
-rw-r--r--src/plugins/android/icons/obj16/webresources.gifbin0 -> 1666 bytes
-rw-r--r--src/plugins/android/icons/obj16/widget_provider_prj_wiz_toolbar.pngbin0 -> 1106 bytes
-rw-r--r--src/plugins/android/icons/views/building_block_explorer_plate.gifbin0 -> 1652 bytes
-rw-r--r--src/plugins/android/icons/wizban/add_libraries_ban.pngbin0 -> 2560 bytes
-rw-r--r--src/plugins/android/icons/wizban/deploy_wizard.pngbin0 -> 3163 bytes
-rw-r--r--src/plugins/android/icons/wizban/dump_hprof_wizard.pngbin0 -> 5534 bytes
-rw-r--r--src/plugins/android/icons/wizban/fill_activity_ban.pngbin0 -> 2630 bytes
-rw-r--r--src/plugins/android/icons/wizban/monkey_wizard.pngbin0 -> 6138 bytes
-rw-r--r--src/plugins/android/icons/wizban/newprjwiz.pngbin0 -> 6832 bytes
-rw-r--r--src/plugins/android/icons/wizban/obfuscate.gifbin0 -> 2620 bytes
-rw-r--r--src/plugins/android/icons/wizban/undeploy_wizard.pngbin0 -> 2052 bytes
-rw-r--r--src/plugins/android/icons/wizban/widget_provider_prj_wiz.pngbin0 -> 7532 bytes
-rw-r--r--src/plugins/android/jakarta-oro-2.0.8.jarbin0 -> 65261 bytes
-rw-r--r--src/plugins/android/plugin.properties131
-rw-r--r--src/plugins/android/plugin.xml739
-rw-r--r--src/plugins/android/resources/login.html25
-rw-r--r--src/plugins/android/resources/monkey_options.xml91
-rw-r--r--src/plugins/android/src/com/motorola/studio/android/AndroidPlugin.java272
-rw-r--r--src/plugins/android/src/com/motorola/studio/android/AssociateProguardEditor.java72
-rw-r--r--src/plugins/android/src/com/motorola/studio/android/adt/AdtUtils.java57
-rw-r--r--src/plugins/android/src/com/motorola/studio/android/adt/DDMSFacade.java2273
-rw-r--r--src/plugins/android/src/com/motorola/studio/android/adt/DDMSUtils.java1442
-rw-r--r--src/plugins/android/src/com/motorola/studio/android/adt/DdmsRunnable.java41
-rw-r--r--src/plugins/android/src/com/motorola/studio/android/adt/FileListingReceiver.java83
-rw-r--r--src/plugins/android/src/com/motorola/studio/android/adt/IDatabaseListingListener.java29
-rw-r--r--src/plugins/android/src/com/motorola/studio/android/adt/ISerialNumbered.java49
-rw-r--r--src/plugins/android/src/com/motorola/studio/android/adt/InstallPackageBean.java48
-rw-r--r--src/plugins/android/src/com/motorola/studio/android/adt/MotodevHProfDumpHandler.java350
-rw-r--r--src/plugins/android/src/com/motorola/studio/android/adt/ProjectUtils.java73
-rw-r--r--src/plugins/android/src/com/motorola/studio/android/adt/Sample.java100
-rw-r--r--src/plugins/android/src/com/motorola/studio/android/adt/SdkUtils.java1006
-rw-r--r--src/plugins/android/src/com/motorola/studio/android/adt/StudioAndroidEventManager.java205
-rw-r--r--src/plugins/android/src/com/motorola/studio/android/adt/StudioDeviceChangeListener.java94
-rw-r--r--src/plugins/android/src/com/motorola/studio/android/application/store/handlers/OpenMotorolaMobilityDevicePropertiesHandler.java64
-rw-r--r--src/plugins/android/src/com/motorola/studio/android/command/CleanProjects.java147
-rw-r--r--src/plugins/android/src/com/motorola/studio/android/command/NewProjectWizard.java37
-rw-r--r--src/plugins/android/src/com/motorola/studio/android/command/NewWidgetProjectWizard.java35
-rw-r--r--src/plugins/android/src/com/motorola/studio/android/command/NewWizardHandler.java87
-rw-r--r--src/plugins/android/src/com/motorola/studio/android/command/OpenStringEditor.java292
-rw-r--r--src/plugins/android/src/com/motorola/studio/android/devices/AbstractDeviceDropSupportHandler.java185
-rw-r--r--src/plugins/android/src/com/motorola/studio/android/devices/AbstractDevicePropertyPage.java298
-rw-r--r--src/plugins/android/src/com/motorola/studio/android/devices/DevicesManager.java284
-rw-r--r--src/plugins/android/src/com/motorola/studio/android/help/handlers/OpenHelpAndroidHandler.java79
-rw-r--r--src/plugins/android/src/com/motorola/studio/android/help/handlers/OpenHelpStudioHandler.java80
-rw-r--r--src/plugins/android/src/com/motorola/studio/android/help/handlers/OpenOnlineHelpStudioHandler.java51
-rw-r--r--src/plugins/android/src/com/motorola/studio/android/i18n/AndroidNLS.java479
-rw-r--r--src/plugins/android/src/com/motorola/studio/android/i18n/androidNLS.properties258
-rw-r--r--src/plugins/android/src/com/motorola/studio/android/logger/AppValidatorLogCollector.java58
-rw-r--r--src/plugins/android/src/com/motorola/studio/android/logger/DevicePropertyLogger.java154
-rw-r--r--src/plugins/android/src/com/motorola/studio/android/model/AndroidProject.java953
-rw-r--r--src/plugins/android/src/com/motorola/studio/android/model/IWizardModel.java66
-rw-r--r--src/plugins/android/src/com/motorola/studio/android/model/ProjectCreationSupport.java1377
-rw-r--r--src/plugins/android/src/com/motorola/studio/android/monkey/options/IMonkeyOptionsConstants.java93
-rw-r--r--src/plugins/android/src/com/motorola/studio/android/monkey/options/MonkeyOption.java306
-rw-r--r--src/plugins/android/src/com/motorola/studio/android/monkey/options/MonkeyOptionsGroup.java105
-rw-r--r--src/plugins/android/src/com/motorola/studio/android/monkey/options/MonkeyOptionsMgt.java829
-rw-r--r--src/plugins/android/src/com/motorola/studio/android/nativeos/IDevicePropertiesOSConstants.java25
-rw-r--r--src/plugins/android/src/com/motorola/studio/android/nativeos/INativeUI.java52
-rw-r--r--src/plugins/android/src/com/motorola/studio/android/nativeos/NativeUIUtils.java132
-rw-r--r--src/plugins/android/src/com/motorola/studio/android/obfuscate/ObfuscatorManager.java378
-rw-r--r--src/plugins/android/src/com/motorola/studio/android/obfuscate/handlers/ObfuscateProjectsHandler.java128
-rw-r--r--src/plugins/android/src/com/motorola/studio/android/obfuscate/ui/ObfuscateDialog.java227
-rw-r--r--src/plugins/android/src/com/motorola/studio/android/perspective/MotodevStudioAndroidPerspective.java356
-rw-r--r--src/plugins/android/src/com/motorola/studio/android/perspective/OpenWebResources.java78
-rw-r--r--src/plugins/android/src/com/motorola/studio/android/preferences/ui/AndroidPreferencePage.java136
-rw-r--r--src/plugins/android/src/com/motorola/studio/android/preferences/ui/EmulatorPreferencePage.java134
-rw-r--r--src/plugins/android/src/com/motorola/studio/android/preferences/ui/MotodevStudioPreference.java81
-rw-r--r--src/plugins/android/src/com/motorola/studio/android/propertypage/MotodevStudioPropertyPage.java226
-rw-r--r--src/plugins/android/src/com/motorola/studio/android/sdkmanager/IAndroidSDKManagerConstants.java24
-rw-r--r--src/plugins/android/src/com/motorola/studio/android/utilities/AndroidStatus.java46
-rw-r--r--src/plugins/android/src/com/motorola/studio/android/utilities/TelnetFrameworkAndroid.java225
-rw-r--r--src/plugins/android/src/com/motorola/studio/android/wizards/elements/ApplicationGroup.java306
-rw-r--r--src/plugins/android/src/com/motorola/studio/android/wizards/elements/LocationGroup.java239
-rw-r--r--src/plugins/android/src/com/motorola/studio/android/wizards/elements/ProjectNameGroup.java100
-rw-r--r--src/plugins/android/src/com/motorola/studio/android/wizards/elements/SdkTargetSelector.java347
-rw-r--r--src/plugins/android/src/com/motorola/studio/android/wizards/elements/TableWithLoadingInfo.java597
-rw-r--r--src/plugins/android/src/com/motorola/studio/android/wizards/elements/WidgetLocationGroup.java201
-rw-r--r--src/plugins/android/src/com/motorola/studio/android/wizards/elements/sorting/TableItemSortStringSetActionListener.java182
-rw-r--r--src/plugins/android/src/com/motorola/studio/android/wizards/elements/sorting/TableItemStringComparator.java93
-rw-r--r--src/plugins/android/src/com/motorola/studio/android/wizards/installapp/DeployWizard.java135
-rw-r--r--src/plugins/android/src/com/motorola/studio/android/wizards/installapp/DeployWizardPage.java430
-rw-r--r--src/plugins/android/src/com/motorola/studio/android/wizards/installapp/UninstallAppWizard.java73
-rw-r--r--src/plugins/android/src/com/motorola/studio/android/wizards/installapp/UninstallAppWizardPage.java258
-rw-r--r--src/plugins/android/src/com/motorola/studio/android/wizards/mat/DumpHPROFTable.java85
-rw-r--r--src/plugins/android/src/com/motorola/studio/android/wizards/mat/DumpHPROFWizard.java61
-rw-r--r--src/plugins/android/src/com/motorola/studio/android/wizards/mat/DumpHPROFWizardPage.java135
-rw-r--r--src/plugins/android/src/com/motorola/studio/android/wizards/monkey/AbstractPropertiesComposite.java185
-rw-r--r--src/plugins/android/src/com/motorola/studio/android/wizards/monkey/DeviceSelectionDialog.java102
-rw-r--r--src/plugins/android/src/com/motorola/studio/android/wizards/monkey/IMonkeyConfigurationConstants.java52
-rw-r--r--src/plugins/android/src/com/motorola/studio/android/wizards/monkey/MonkeyConfigurationDelegate.java73
-rw-r--r--src/plugins/android/src/com/motorola/studio/android/wizards/monkey/MonkeyConfigurationOtherCmdsTab.java162
-rw-r--r--src/plugins/android/src/com/motorola/studio/android/wizards/monkey/MonkeyConfigurationTab.java666
-rw-r--r--src/plugins/android/src/com/motorola/studio/android/wizards/monkey/MonkeyConfigurationTabGroup.java41
-rw-r--r--src/plugins/android/src/com/motorola/studio/android/wizards/monkey/MonkeyConfigurationTabTable.java133
-rw-r--r--src/plugins/android/src/com/motorola/studio/android/wizards/monkey/MonkeyOptionsComposite.java362
-rw-r--r--src/plugins/android/src/com/motorola/studio/android/wizards/project/NewAndroidProjectMainPage.java307
-rw-r--r--src/plugins/android/src/com/motorola/studio/android/wizards/project/NewAndroidProjectWizard.java441
-rw-r--r--src/plugins/android/src/com/motorola/studio/android/wizards/project/SampleSelectionPage.java150
-rw-r--r--src/plugins/android/src/com/motorola/studio/android/wizards/project/SamplesSelectionAdapter.java65
-rw-r--r--src/plugins/android/src/com/motorola/studio/android/wizards/project/TreeContentProvider.java124
-rw-r--r--src/plugins/android/src/com/motorola/studio/android/wizards/project/TreeLabelProvider.java93
-rw-r--r--src/plugins/android/src/com/motorola/studio/android/wizards/widget/NewAndroidWidgetProjectMainPage.java196
-rw-r--r--src/plugins/android/src/com/motorola/studio/android/wizards/widget/NewAndroidWidgetProjectWizard.java283
-rw-r--r--src/plugins/android/templates/widget_project/AndroidWidgetManifest.template11
-rw-r--r--src/plugins/android/templates/widget_project/WidgetProvider.template30
-rw-r--r--src/plugins/android/templates/widget_project/activity.template2
-rw-r--r--src/plugins/android/templates/widget_project/receiver.template6
-rw-r--r--src/plugins/android/templates/widget_project/uses-sdk.template1
-rw-r--r--src/plugins/android/templates/widget_project/widget_info.xml9
-rw-r--r--src/plugins/android/templates/widget_project/widget_initial_layout.xml25
-rw-r--r--src/plugins/certmanager/.classpath9
-rw-r--r--src/plugins/certmanager/.project28
-rw-r--r--src/plugins/certmanager/META-INF/MANIFEST.MF33
-rw-r--r--src/plugins/certmanager/build.properties9
-rw-r--r--src/plugins/certmanager/icons/backup_keystore.pngbin0 -> 468 bytes
-rw-r--r--src/plugins/certmanager/icons/certificate.pngbin0 -> 502 bytes
-rw-r--r--src/plugins/certmanager/icons/change_keystore_type.pngbin0 -> 835 bytes
-rw-r--r--src/plugins/certmanager/icons/change_keystore_type_disabled.pngbin0 -> 717 bytes
-rw-r--r--src/plugins/certmanager/icons/change_password_key.pngbin0 -> 696 bytes
-rw-r--r--src/plugins/certmanager/icons/change_password_keystore.pngbin0 -> 922 bytes
-rw-r--r--src/plugins/certmanager/icons/create_key.pngbin0 -> 512 bytes
-rw-r--r--src/plugins/certmanager/icons/create_keystore.pngbin0 -> 815 bytes
-rw-r--r--src/plugins/certmanager/icons/delete_key.pngbin0 -> 618 bytes
-rw-r--r--src/plugins/certmanager/icons/delete_keystore.pngbin0 -> 867 bytes
-rw-r--r--src/plugins/certmanager/icons/import_entries.pngbin0 -> 831 bytes
-rw-r--r--src/plugins/certmanager/icons/import_keystore.pngbin0 -> 848 bytes
-rw-r--r--src/plugins/certmanager/icons/key.pngbin0 -> 502 bytes
-rw-r--r--src/plugins/certmanager/icons/key_saved_password.pngbin0 -> 754 bytes
-rw-r--r--src/plugins/certmanager/icons/keystore.pngbin0 -> 851 bytes
-rw-r--r--src/plugins/certmanager/icons/keystore_incorrect_type.pngbin0 -> 783 bytes
-rw-r--r--src/plugins/certmanager/icons/keystore_saved_password.pngbin0 -> 966 bytes
-rw-r--r--src/plugins/certmanager/icons/ovr16/error_ovr.pngbin0 -> 208 bytes
-rw-r--r--src/plugins/certmanager/icons/ovr16/warning_ovr.pngbin0 -> 345 bytes
-rw-r--r--src/plugins/certmanager/icons/properties.pngbin0 -> 772 bytes
-rw-r--r--src/plugins/certmanager/icons/properties_disabled.pngbin0 -> 660 bytes
-rw-r--r--src/plugins/certmanager/icons/refresh.pngbin0 -> 411 bytes
-rw-r--r--src/plugins/certmanager/icons/restore_keystore.pngbin0 -> 489 bytes
-rw-r--r--src/plugins/certmanager/icons/sign_package.pngbin0 -> 682 bytes
-rw-r--r--src/plugins/certmanager/icons/unsign_package.pngbin0 -> 645 bytes
-rw-r--r--src/plugins/certmanager/icons/view_icon.pngbin0 -> 884 bytes
-rw-r--r--src/plugins/certmanager/icons/wizban/backup_keystore_wiz.pngbin0 -> 1998 bytes
-rw-r--r--src/plugins/certmanager/icons/wizban/change_keystore_type_wiz.pngbin0 -> 3109 bytes
-rw-r--r--src/plugins/certmanager/icons/wizban/create_key_wiz.pngbin0 -> 2473 bytes
-rw-r--r--src/plugins/certmanager/icons/wizban/create_keystore_wiz.pngbin0 -> 4207 bytes
-rw-r--r--src/plugins/certmanager/icons/wizban/import_entries_wiz.pngbin0 -> 3676 bytes
-rw-r--r--src/plugins/certmanager/icons/wizban/import_keystore_wiz.pngbin0 -> 3602 bytes
-rw-r--r--src/plugins/certmanager/icons/wizban/restore_keystore_wiz.pngbin0 -> 2062 bytes
-rw-r--r--src/plugins/certmanager/icons/wizban/sign_package_wiz.pngbin0 -> 5146 bytes
-rw-r--r--src/plugins/certmanager/icons/wizban/unsign_package_wiz.pngbin0 -> 4472 bytes
-rw-r--r--src/plugins/certmanager/lib/bcpkix-jdk15on-147.jarbin0 -> 515071 bytes
-rw-r--r--src/plugins/certmanager/lib/bcprov-jdk15on-147.jarbin0 -> 1997327 bytes
-rw-r--r--src/plugins/certmanager/plugin.properties54
-rw-r--r--src/plugins/certmanager/plugin.xml750
-rw-r--r--src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/CertificateManagerActivator.java146
-rw-r--r--src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/command/AbstractHandler2.java63
-rw-r--r--src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/command/BackupHandler.java265
-rw-r--r--src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/command/CertificatePropertiesHandler.java69
-rw-r--r--src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/command/ChangeKeyStoreTypeHandler.java88
-rw-r--r--src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/command/ChangePasswordKeyHandler.java186
-rw-r--r--src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/command/ChangePasswordKeystoreHandler.java157
-rw-r--r--src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/command/CreateKeyHandler.java50
-rw-r--r--src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/command/CreateKeystoreHandler.java51
-rw-r--r--src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/command/DeleteKeyHandler.java137
-rw-r--r--src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/command/DeleteKeystoreHandler.java120
-rw-r--r--src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/command/ImportKeyStoreEntriesHandler.java98
-rw-r--r--src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/command/ImportKeystoreHandler.java52
-rw-r--r--src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/command/RefreshHandler.java39
-rw-r--r--src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/command/RestoreBackupHandler.java306
-rw-r--r--src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/command/SignExternalPackagesHandler.java39
-rw-r--r--src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/command/UnsignExternalPackagesHandler.java40
-rw-r--r--src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/core/BackwardKeystoreManager.java164
-rw-r--r--src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/core/KeyStoreManager.java303
-rw-r--r--src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/core/KeyStoreUtils.java686
-rw-r--r--src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/core/KeyStoreUtilsTest.java243
-rw-r--r--src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/core/PasswordProvider.java484
-rw-r--r--src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/core/SaveStateManager.java328
-rw-r--r--src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/core/SaveStateManagerTest.java156
-rw-r--r--src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/event/IKeyStoreModelListener.java62
-rw-r--r--src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/event/KeyStoreModelEvent.java67
-rw-r--r--src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/event/KeyStoreModelEventManager.java116
-rw-r--r--src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/exception/InvalidPasswordException.java53
-rw-r--r--src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/exception/KeyStoreManagerException.java53
-rw-r--r--src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/i18n/CertificateManagerNLS.java522
-rw-r--r--src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/i18n/certificateManagerNLS.properties285
-rw-r--r--src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/job/CreateKeyJob.java191
-rw-r--r--src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/packaging/PackageFile.java648
-rw-r--r--src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/packaging/sign/ISignConstants.java77
-rw-r--r--src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/packaging/sign/ManifestDigester.java185
-rw-r--r--src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/packaging/sign/ManifestEntry.java243
-rw-r--r--src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/packaging/sign/PackageFileSigner.java263
-rw-r--r--src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/packaging/sign/SignException.java45
-rw-r--r--src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/packaging/sign/SignatureBlockFile.java264
-rw-r--r--src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/packaging/sign/SignatureFile.java226
-rw-r--r--src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/property/tester/TreeNodeTester.java50
-rw-r--r--src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/action/PopupMenuActionDelegate.java224
-rw-r--r--src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/action/RemoveSignatureAction.java69
-rw-r--r--src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/action/SignCreatedPackageAction.java84
-rw-r--r--src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/composite/CertificateBlock.java495
-rw-r--r--src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/composite/KeyPropertiesBlock.java74
-rw-r--r--src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/composite/NewKeyBlock.java303
-rw-r--r--src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/dialogs/BackupContentProvider.java53
-rw-r--r--src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/dialogs/BackupDialog.java400
-rw-r--r--src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/dialogs/BackupLabelProvider.java66
-rw-r--r--src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/dialogs/CertificateInfoDialog.java139
-rw-r--r--src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/dialogs/RestoreBackupDialog.java437
-rw-r--r--src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/dialogs/importks/ConvertKeyStoreTypeDialog.java633
-rw-r--r--src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/dialogs/importks/ImportEntriesDialog.java675
-rw-r--r--src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/model/AbstractTreeNode.java139
-rw-r--r--src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/model/CertificateDetailsInfo.java140
-rw-r--r--src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/model/EntryDummyNode.java71
-rw-r--r--src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/model/EntryNode.java546
-rw-r--r--src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/model/IKeyStore.java132
-rw-r--r--src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/model/IKeyStoreEntry.java87
-rw-r--r--src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/model/ITreeNode.java118
-rw-r--r--src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/model/KeyStoreNode.java786
-rw-r--r--src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/model/KeyStoreRootNode.java126
-rw-r--r--src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/model/SigningAndKeysModelManager.java148
-rw-r--r--src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/tree/ExpiresInColumnLabelProvider.java55
-rw-r--r--src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/tree/KeystoreManagerTreeContentProvider.java132
-rw-r--r--src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/tree/LastBackupDateColumnLabelProvider.java46
-rw-r--r--src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/tree/NameAliasColumnLabelProvider.java135
-rw-r--r--src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/tree/PathColumnLabelProvider.java37
-rw-r--r--src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/tree/TypeColumnLabelProvider.java39
-rw-r--r--src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/wizards/CreateKeyWizard.java107
-rw-r--r--src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/wizards/CreateKeyWizardPage.java284
-rw-r--r--src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/wizards/CreateKeystorePage.java672
-rw-r--r--src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/wizards/CreateKeystoreWizard.java122
-rw-r--r--src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/wizards/ImportKeystorePage.java417
-rw-r--r--src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/wizards/ImportKeystoreWizard.java72
-rw-r--r--src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/wizards/RemoveExternalPackageSignaturePage.java686
-rw-r--r--src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/wizards/RemoveExternalPackageSignatureWizard.java282
-rw-r--r--src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/wizards/SelectExistentKeystorePage.java200
-rw-r--r--src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/wizards/SelectExistentKeystoreWizard.java141
-rw-r--r--src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/wizards/SignExternalPackagePage.java559
-rw-r--r--src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/wizards/SignExternalPackageWizard.java346
-rw-r--r--src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/views/KeystoreManagerView.java378
-rw-r--r--src/plugins/common/.classpath9
-rw-r--r--src/plugins/common/.project28
-rw-r--r--src/plugins/common/META-INF/MANIFEST.MF44
-rw-r--r--src/plugins/common/about.ini24
-rw-r--r--src/plugins/common/about.mappings1
-rw-r--r--src/plugins/common/about.properties7
-rw-r--r--src/plugins/common/build.properties13
-rw-r--r--src/plugins/common/commons-net-1.4.1.jarbin0 -> 180792 bytes
-rw-r--r--src/plugins/common/files/permissions.txt119
-rw-r--r--src/plugins/common/jakarta-oro-2.0.8.jarbin0 -> 65261 bytes
-rw-r--r--src/plugins/common/plugin.properties10
-rw-r--r--src/plugins/common/plugin.xml17
-rw-r--r--src/plugins/common/res/androidjdbc.jarbin0 -> 1591516 bytes
-rw-r--r--src/plugins/common/res/plate32.pngbin0 -> 4980 bytes
-rw-r--r--src/plugins/common/src/com/motorola/studio/android/common/CommonPlugin.java122
-rw-r--r--src/plugins/common/src/com/motorola/studio/android/common/IAndroidConstants.java90
-rw-r--r--src/plugins/common/src/com/motorola/studio/android/common/exception/AndroidException.java62
-rw-r--r--src/plugins/common/src/com/motorola/studio/android/common/log/StudioLogger.java125
-rw-r--r--src/plugins/common/src/com/motorola/studio/android/common/log/UsageDataConstants.java192
-rw-r--r--src/plugins/common/src/com/motorola/studio/android/common/preferences/DialogWithToggleUtils.java346
-rw-r--r--src/plugins/common/src/com/motorola/studio/android/common/proxy/NetworkProxySettingStartup.java42
-rw-r--r--src/plugins/common/src/com/motorola/studio/android/common/proxy/ProxyAuthenticator.java84
-rw-r--r--src/plugins/common/src/com/motorola/studio/android/common/utilities/AndroidStatus.java48
-rw-r--r--src/plugins/common/src/com/motorola/studio/android/common/utilities/AndroidUtils.java612
-rw-r--r--src/plugins/common/src/com/motorola/studio/android/common/utilities/EclipseUtils.java1411
-rw-r--r--src/plugins/common/src/com/motorola/studio/android/common/utilities/FileUtil.java1862
-rw-r--r--src/plugins/common/src/com/motorola/studio/android/common/utilities/HttpUtils.java300
-rw-r--r--src/plugins/common/src/com/motorola/studio/android/common/utilities/PluginUtils.java631
-rw-r--r--src/plugins/common/src/com/motorola/studio/android/common/utilities/TargetDataReader.java124
-rw-r--r--src/plugins/common/src/com/motorola/studio/android/common/utilities/i18n/UtilitiesNLS.java203
-rw-r--r--src/plugins/common/src/com/motorola/studio/android/common/utilities/i18n/utilitiesNLS.properties83
-rw-r--r--src/plugins/common/src/com/motorola/studio/android/common/utilities/ui/Country.java75
-rw-r--r--src/plugins/common/src/com/motorola/studio/android/common/utilities/ui/LoginPasswordDialogCreator.java172
-rw-r--r--src/plugins/common/src/com/motorola/studio/android/common/utilities/ui/PasswordInputDialog.java324
-rw-r--r--src/plugins/common/src/com/motorola/studio/android/common/utilities/ui/ToolsCountries.java92
-rw-r--r--src/plugins/common/src/com/motorola/studio/android/common/utilities/ui/WidgetsFactory.java454
-rw-r--r--src/plugins/common/src/com/motorola/studio/android/common/utilities/ui/WidgetsUtil.java392
-rw-r--r--src/plugins/common/src/com/motorola/studio/android/manifest/AndroidProjectManifestFile.java113
-rw-r--r--src/plugins/common/src/com/motorola/studio/android/model/manifest/AndroidManifestFile.java533
-rw-r--r--src/plugins/common/src/com/motorola/studio/android/model/manifest/dom/AbstractBuildingBlockNode.java167
-rw-r--r--src/plugins/common/src/com/motorola/studio/android/model/manifest/dom/AbstractIconLabelNameNode.java167
-rw-r--r--src/plugins/common/src/com/motorola/studio/android/model/manifest/dom/AbstractSimpleNameNode.java104
-rw-r--r--src/plugins/common/src/com/motorola/studio/android/model/manifest/dom/AbstractUsesNode.java118
-rw-r--r--src/plugins/common/src/com/motorola/studio/android/model/manifest/dom/ActionNode.java54
-rw-r--r--src/plugins/common/src/com/motorola/studio/android/model/manifest/dom/ActivityAliasNode.java304
-rw-r--r--src/plugins/common/src/com/motorola/studio/android/model/manifest/dom/ActivityNode.java882
-rw-r--r--src/plugins/common/src/com/motorola/studio/android/model/manifest/dom/AndroidManifestNode.java513
-rw-r--r--src/plugins/common/src/com/motorola/studio/android/model/manifest/dom/ApplicationNode.java735
-rw-r--r--src/plugins/common/src/com/motorola/studio/android/model/manifest/dom/CategoryNode.java54
-rw-r--r--src/plugins/common/src/com/motorola/studio/android/model/manifest/dom/CommentNode.java139
-rw-r--r--src/plugins/common/src/com/motorola/studio/android/model/manifest/dom/DataNode.java298
-rw-r--r--src/plugins/common/src/com/motorola/studio/android/model/manifest/dom/GrantUriPermissionNode.java176
-rw-r--r--src/plugins/common/src/com/motorola/studio/android/model/manifest/dom/IAndroidManifestProperties.java138
-rw-r--r--src/plugins/common/src/com/motorola/studio/android/model/manifest/dom/InstrumentationNode.java168
-rw-r--r--src/plugins/common/src/com/motorola/studio/android/model/manifest/dom/IntentFilterNode.java403
-rw-r--r--src/plugins/common/src/com/motorola/studio/android/model/manifest/dom/ManifestNode.java636
-rw-r--r--src/plugins/common/src/com/motorola/studio/android/model/manifest/dom/MetadataNode.java185
-rw-r--r--src/plugins/common/src/com/motorola/studio/android/model/manifest/dom/PermissionGroupNode.java106
-rw-r--r--src/plugins/common/src/com/motorola/studio/android/model/manifest/dom/PermissionNode.java243
-rw-r--r--src/plugins/common/src/com/motorola/studio/android/model/manifest/dom/PermissionTreeNode.java73
-rw-r--r--src/plugins/common/src/com/motorola/studio/android/model/manifest/dom/Property.java108
-rw-r--r--src/plugins/common/src/com/motorola/studio/android/model/manifest/dom/ProviderNode.java480
-rw-r--r--src/plugins/common/src/com/motorola/studio/android/model/manifest/dom/ReceiverNode.java41
-rw-r--r--src/plugins/common/src/com/motorola/studio/android/model/manifest/dom/ServiceNode.java152
-rw-r--r--src/plugins/common/src/com/motorola/studio/android/model/manifest/dom/UnknownNode.java123
-rw-r--r--src/plugins/common/src/com/motorola/studio/android/model/manifest/dom/UsesFeatureNode.java55
-rw-r--r--src/plugins/common/src/com/motorola/studio/android/model/manifest/dom/UsesLibraryNode.java54
-rw-r--r--src/plugins/common/src/com/motorola/studio/android/model/manifest/dom/UsesPermissionNode.java69
-rw-r--r--src/plugins/common/src/com/motorola/studio/android/model/manifest/dom/UsesSDKNode.java164
-rw-r--r--src/plugins/common/src/com/motorola/studio/android/model/manifest/parser/AndroidManifestNodeParser.java1099
-rw-r--r--src/plugins/common/src/com/motorola/studio/android/model/manifest/parser/AndroidManifestParser.java243
-rw-r--r--src/plugins/common/src/com/motorola/studio/android/wizards/BaseWizard.java63
-rw-r--r--src/plugins/common/src/com/motorola/studio/android/wizards/BaseWizardPage.java176
-rw-r--r--src/plugins/common/src/com/motorola/studio/android/wizards/elements/AddInputRemoveButtons.java115
-rw-r--r--src/plugins/common/src/com/motorola/studio/android/wizards/elements/AddRemoveButtons.java97
-rw-r--r--src/plugins/common/src/com/motorola/studio/android/wizards/elements/FileChooser.java305
-rw-r--r--src/plugins/common/src/com/motorola/studio/android/wizards/elements/IBaseBlock.java81
-rw-r--r--src/plugins/common/src/com/motorola/studio/android/wizards/elements/InputRemoveButtons.java107
-rw-r--r--src/plugins/common/src/com/motorola/studio/android/wizards/elements/ProjectChooser.java290
-rw-r--r--src/plugins/common/src/countries.properties246
-rw-r--r--src/plugins/db.core/.classpath7
-rw-r--r--src/plugins/db.core/.project34
-rw-r--r--src/plugins/db.core/META-INF/MANIFEST.MF32
-rw-r--r--src/plugins/db.core/build.properties8
-rw-r--r--src/plugins/db.core/icons/action_content_provider.pngbin0 -> 494 bytes
-rw-r--r--src/plugins/db.core/icons/action_create_database.pngbin0 -> 515 bytes
-rw-r--r--src/plugins/db.core/icons/action_new_table.pngbin0 -> 538 bytes
-rw-r--r--src/plugins/db.core/icons/connect.pngbin0 -> 289 bytes
-rw-r--r--src/plugins/db.core/icons/database_explorer_view.pngbin0 -> 1744 bytes
-rw-r--r--src/plugins/db.core/icons/dbplate.gifbin0 -> 1744 bytes
-rw-r--r--src/plugins/db.core/icons/disconnect.pngbin0 -> 296 bytes
-rw-r--r--src/plugins/db.core/icons/filesystem.pngbin0 -> 761 bytes
-rw-r--r--src/plugins/db.core/icons/map.pngbin0 -> 464 bytes
-rw-r--r--src/plugins/db.core/icons/obj16/dbplate.gifbin0 -> 1744 bytes
-rw-r--r--src/plugins/db.core/icons/obj16/plate16.pngbin0 -> 3558 bytes
-rw-r--r--src/plugins/db.core/icons/ovr16/error_ovr.pngbin0 -> 208 bytes
-rw-r--r--src/plugins/db.core/icons/unmap.pngbin0 -> 543 bytes
-rw-r--r--src/plugins/db.core/icons/wizban/create_database_ban.pngbin0 -> 1328 bytes
-rw-r--r--src/plugins/db.core/icons/wizban/create_table.pngbin0 -> 2009 bytes
-rw-r--r--src/plugins/db.core/plugin.properties83
-rw-r--r--src/plugins/db.core/plugin.xml727
-rw-r--r--src/plugins/db.core/res/template.dbbin0 -> 3072 bytes
-rw-r--r--src/plugins/db.core/schema/com.motorolamobility.studio.android.db.core.dbRootNode.exsd134
-rw-r--r--src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/CanRefreshStatus.java129
-rw-r--r--src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/DbCoreActivator.java229
-rw-r--r--src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/DbPerspective.java92
-rw-r--r--src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/DbRootNodeReader.java123
-rw-r--r--src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/command/BrowseTableContentsHandler.java79
-rw-r--r--src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/command/CollapseAllHandler.java33
-rw-r--r--src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/command/CreateDatabaseManagementClassesHandler.java192
-rw-r--r--src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/command/DbConnectHandler.java55
-rw-r--r--src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/command/DbCreateHandler.java226
-rw-r--r--src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/command/DbDisconnectHandler.java51
-rw-r--r--src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/command/DbNodeTester.java37
-rw-r--r--src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/command/DeleteDatabaseHandler.java74
-rw-r--r--src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/command/DeleteTableHandler.java75
-rw-r--r--src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/command/ExtractDataHandler.java44
-rw-r--r--src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/command/LoadDataHandler.java44
-rw-r--r--src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/command/MapDatabaseHandler.java86
-rw-r--r--src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/command/RefreshNodeHandler.java132
-rw-r--r--src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/command/SampleContentsHandler.java43
-rw-r--r--src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/command/TableCreateHandler.java153
-rw-r--r--src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/command/UnmapDatabaseHandler.java107
-rw-r--r--src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/event/DatabaseModelEvent.java68
-rw-r--r--src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/event/DatabaseModelEventManager.java115
-rw-r--r--src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/event/IDatabaseModelListener.java61
-rw-r--r--src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/exception/MotodevDbException.java64
-rw-r--r--src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/filesystem/FilesystemRootNode.java279
-rw-r--r--src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/i18n/DbCoreNLS.java230
-rw-r--r--src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/i18n/dbcoreNLS.properties100
-rw-r--r--src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/junit/DbModelTest.java69
-rw-r--r--src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/junit/TableNodeTest.java64
-rw-r--r--src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/model/DbModel.java728
-rw-r--r--src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/model/Field.java220
-rw-r--r--src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/model/TableModel.java184
-rw-r--r--src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/project/ProjectNode.java408
-rw-r--r--src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/ui/AbstractDbResultManagerAdapter.java106
-rw-r--r--src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/ui/AbstractLoadingNodeJob.java45
-rw-r--r--src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/ui/AbstractTreeNode.java459
-rw-r--r--src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/ui/ColumnNode.java101
-rw-r--r--src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/ui/DbNode.java561
-rw-r--r--src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/ui/IDataSampler.java26
-rw-r--r--src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/ui/IDbMapperNode.java41
-rw-r--r--src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/ui/IDbNode.java94
-rw-r--r--src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/ui/IRootNode.java25
-rw-r--r--src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/ui/ISaveStateTreeNode.java25
-rw-r--r--src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/ui/ITableNode.java27
-rw-r--r--src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/ui/ITreeNode.java182
-rw-r--r--src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/ui/LoadingJobListener.java106
-rw-r--r--src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/ui/LoadingNode.java61
-rw-r--r--src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/ui/RootNode.java42
-rw-r--r--src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/ui/TableNode.java149
-rw-r--r--src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/ui/action/CreateDatabaseManagementClassesAction.java232
-rw-r--r--src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/ui/action/IDbCreatorNode.java35
-rw-r--r--src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/ui/action/ITableCreatorNode.java36
-rw-r--r--src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/ui/action/PopupMenuActionDelegate.java316
-rw-r--r--src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/ui/tree/DatabaseExplorerTreeContentProvider.java111
-rw-r--r--src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/ui/tree/DatabaseExplorerTreeLabelProvider.java179
-rw-r--r--src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/ui/view/MOTODEVDatabaseExplorerView.java409
-rw-r--r--src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/ui/view/SaveStateManager.java113
-rw-r--r--src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/ui/wizards/AddTableFieldDialog.java291
-rw-r--r--src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/ui/wizards/CreateTableWizard.java108
-rw-r--r--src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/ui/wizards/CreateTableWizardPage.java333
-rw-r--r--src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/ui/wizards/TableLabelProvider.java67
-rw-r--r--src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/ui/wizards/TableWizardContentProvider.java48
-rw-r--r--src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/ui/wizards/TableWizardLabelProvider.java84
-rw-r--r--src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/ui/wizards/createdb/CreateDatabaseWizard.java168
-rw-r--r--src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/ui/wizards/createdb/CreateDatabaseWizardPage.java385
-rw-r--r--src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/ui/wizards/createdb/DatabaseCreationFieldValidator.java82
-rw-r--r--src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/workspace/WorkspaceRootNode.java206
-rw-r--r--src/plugins/db.devices/.classpath7
-rw-r--r--src/plugins/db.devices/.project28
-rw-r--r--src/plugins/db.devices/META-INF/MANIFEST.MF21
-rw-r--r--src/plugins/db.devices/build.properties23
-rw-r--r--src/plugins/db.devices/icons/get_from_local_file.pngbin0 -> 439 bytes
-rw-r--r--src/plugins/db.devices/icons/map.pngbin0 -> 464 bytes
-rw-r--r--src/plugins/db.devices/icons/obj16/card.pngbin0 -> 815 bytes
-rw-r--r--src/plugins/db.devices/icons/obj16/device.pngbin0 -> 717 bytes
-rw-r--r--src/plugins/db.devices/icons/obj16/device_root.gifbin0 -> 1429 bytes
-rw-r--r--src/plugins/db.devices/icons/obj16/devices.pngbin0 -> 523 bytes
-rw-r--r--src/plugins/db.devices/icons/save_to_local_file.pngbin0 -> 459 bytes
-rw-r--r--src/plugins/db.devices/plugin.properties38
-rw-r--r--src/plugins/db.devices/plugin.xml136
-rw-r--r--src/plugins/db.devices/src/com/motorolamobility/studio/android/db/devices/DbDevicesPlugin.java78
-rw-r--r--src/plugins/db.devices/src/com/motorolamobility/studio/android/db/devices/i18n/DbDevicesNLS.java109
-rw-r--r--src/plugins/db.devices/src/com/motorolamobility/studio/android/db/devices/i18n/messages.properties54
-rw-r--r--src/plugins/db.devices/src/com/motorolamobility/studio/android/db/devices/model/ApplicationNode.java196
-rw-r--r--src/plugins/db.devices/src/com/motorolamobility/studio/android/db/devices/model/DeviceDbNode.java720
-rw-r--r--src/plugins/db.devices/src/com/motorolamobility/studio/android/db/devices/model/DeviceNode.java381
-rw-r--r--src/plugins/db.devices/src/com/motorolamobility/studio/android/db/devices/model/DevicesRootNode.java176
-rw-r--r--src/plugins/db.devices/src/com/motorolamobility/studio/android/db/devices/model/ExtStorageNode.java323
-rw-r--r--src/plugins/db.devices/src/com/motorolamobility/studio/android/db/devices/model/IDbDeviceMapperNode.java27
-rw-r--r--src/plugins/db.devices/src/com/motorolamobility/studio/android/db/devices/model/IDeviceNode.java39
-rw-r--r--src/plugins/db.devices/src/com/motorolamobility/studio/android/db/devices/ui/action/FilterDbApplicationHandler.java67
-rw-r--r--src/plugins/db.devices/src/com/motorolamobility/studio/android/db/devices/ui/action/MapDeviceDatabaseHandler.java110
-rw-r--r--src/plugins/db.devices/src/com/motorolamobility/studio/android/db/devices/ui/action/PersistentToggleState.java48
-rw-r--r--src/plugins/db.devices/src/com/motorolamobility/studio/android/db/devices/ui/action/PopupMenuActionDelegate.java159
-rw-r--r--src/plugins/db.devices/src/com/motorolamobility/studio/android/db/devices/ui/action/SaveDatabaseToFileHandler.java91
-rw-r--r--src/plugins/db.devices/src/com/motorolamobility/studio/android/db/devices/ui/preferences/DbPreferencePage.java173
-rw-r--r--src/plugins/db.devices/src/com/motorolamobility/studio/android/db/devices/utils/DeviceDbUtils.java122
-rw-r--r--src/plugins/devices.services/.classpath7
-rw-r--r--src/plugins/devices.services/.project28
-rw-r--r--src/plugins/devices.services/META-INF/MANIFEST.MF21
-rw-r--r--src/plugins/devices.services/build.properties8
-rw-r--r--src/plugins/devices.services/icons/adb_shell.pngbin0 -> 815 bytes
-rw-r--r--src/plugins/devices.services/icons/deploy.pngbin0 -> 243 bytes
-rw-r--r--src/plugins/devices.services/icons/emulator_console.pngbin0 -> 1198 bytes
-rw-r--r--src/plugins/devices.services/icons/monkey_16.pngbin0 -> 916 bytes
-rw-r--r--src/plugins/devices.services/icons/screenshot_16.jpgbin0 -> 7642 bytes
-rw-r--r--src/plugins/devices.services/icons/uninstall.pngbin0 -> 250 bytes
-rw-r--r--src/plugins/devices.services/plugin.properties24
-rw-r--r--src/plugins/devices.services/plugin.xml497
-rw-r--r--src/plugins/devices.services/resources/ISO-639-2_utf-8.txt485
-rw-r--r--src/plugins/devices.services/resources/flag.pngbin0 -> 785 bytes
-rw-r--r--src/plugins/devices.services/resources/flag_small.pngbin0 -> 544 bytes
-rw-r--r--src/plugins/devices.services/resources/iso_3166-1_list_en.xml987
-rw-r--r--src/plugins/devices.services/src/com/motorola/studio/android/devices/services/DeviceServicesPlugin.java477
-rw-r--r--src/plugins/devices.services/src/com/motorola/studio/android/devices/services/console/ADBShellCommand.java48
-rw-r--r--src/plugins/devices.services/src/com/motorola/studio/android/devices/services/console/ADBShellHandler.java256
-rw-r--r--src/plugins/devices.services/src/com/motorola/studio/android/devices/services/console/EmulatorConsoleCommand.java48
-rw-r--r--src/plugins/devices.services/src/com/motorola/studio/android/devices/services/console/EmulatorConsoleHandler.java234
-rw-r--r--src/plugins/devices.services/src/com/motorola/studio/android/devices/services/ddms/MonkeyServiceCommand.java52
-rw-r--r--src/plugins/devices.services/src/com/motorola/studio/android/devices/services/ddms/MonkeyServiceHandler.java71
-rw-r--r--src/plugins/devices.services/src/com/motorola/studio/android/devices/services/ddms/ScreenshotServiceCommand.java48
-rw-r--r--src/plugins/devices.services/src/com/motorola/studio/android/devices/services/ddms/ScreenshotServiceHandler.java65
-rw-r--r--src/plugins/devices.services/src/com/motorola/studio/android/devices/services/deploy/DeployServiceCommand.java54
-rw-r--r--src/plugins/devices.services/src/com/motorola/studio/android/devices/services/deploy/DeployServiceHandler.java109
-rw-r--r--src/plugins/devices.services/src/com/motorola/studio/android/devices/services/deploy/EmulatorTester.java30
-rw-r--r--src/plugins/devices.services/src/com/motorola/studio/android/devices/services/deploy/UninstallAppServiceCommand.java48
-rw-r--r--src/plugins/devices.services/src/com/motorola/studio/android/devices/services/deploy/UninstallAppServiceHandler.java96
-rw-r--r--src/plugins/devices.services/src/com/motorola/studio/android/devices/services/i18n/ServicesNLS.java81
-rw-r--r--src/plugins/devices.services/src/com/motorola/studio/android/devices/services/i18n/servicesNLS.properties30
-rw-r--r--src/plugins/devices.services/src/com/motorola/studio/android/devices/services/lang/LangServiceCommand.java59
-rw-r--r--src/plugins/devices.services/src/com/motorola/studio/android/devices/services/lang/LangServiceHandler.java143
-rw-r--r--src/plugins/devices.services/src/com/motorola/studio/android/devices/services/lang/model/Country.java173
-rw-r--r--src/plugins/devices.services/src/com/motorola/studio/android/devices/services/lang/model/LangWizard.java86
-rw-r--r--src/plugins/devices.services/src/com/motorola/studio/android/devices/services/lang/model/LangWizardPage.java135
-rw-r--r--src/plugins/devices.services/src/com/motorola/studio/android/devices/services/lang/model/Language.java170
-rw-r--r--src/plugins/devices.services/src/com/motorola/studio/android/devices/services/lang/model/LocationComposite.java321
-rw-r--r--src/plugins/emulator/.classpath9
-rw-r--r--src/plugins/emulator/.project28
-rw-r--r--src/plugins/emulator/META-INF/MANIFEST.MF46
-rw-r--r--src/plugins/emulator/build.properties27
-rw-r--r--src/plugins/emulator/icons/adv-icon-16x16.pngbin0 -> 465 bytes
-rw-r--r--src/plugins/emulator/icons/device.pngbin0 -> 287 bytes
-rw-r--r--src/plugins/emulator/icons/iniciate-icon-16x16.pngbin0 -> 438 bytes
-rw-r--r--src/plugins/emulator/icons/main-icon-16x16.pngbin0 -> 410 bytes
-rw-r--r--src/plugins/emulator/icons/notavailable-icon-16x16.pngbin0 -> 369 bytes
-rw-r--r--src/plugins/emulator/icons/plate16.gifbin0 -> 1448 bytes
-rw-r--r--src/plugins/emulator/icons/refresh.pngbin0 -> 946 bytes
-rw-r--r--src/plugins/emulator/icons/repair.pngbin0 -> 518 bytes
-rw-r--r--src/plugins/emulator/icons/start.pngbin0 -> 359 bytes
-rw-r--r--src/plugins/emulator/icons/started-icon-16x16.pngbin0 -> 424 bytes
-rw-r--r--src/plugins/emulator/icons/stop.pngbin0 -> 396 bytes
-rw-r--r--src/plugins/emulator/icons/stopped-icon-16x16.pngbin0 -> 417 bytes
-rw-r--r--src/plugins/emulator/icons/wizard-icon-64x64.pngbin0 -> 3693 bytes
-rw-r--r--src/plugins/emulator/lib/commons-net-1.4.1.jarbin0 -> 180792 bytes
-rw-r--r--src/plugins/emulator/lib/jakarta-oro-2.0.8.jarbin0 -> 65261 bytes
-rw-r--r--src/plugins/emulator/plugin.properties139
-rw-r--r--src/plugins/emulator/plugin.xml627
-rw-r--r--src/plugins/emulator/res/AVRCP.properties23
-rw-r--r--src/plugins/emulator/res/fbvncserverbin0 -> 1108351 bytes
-rw-r--r--src/plugins/emulator/res/pseudolayout21
-rw-r--r--src/plugins/emulator/res/qwerty.properties105
-rw-r--r--src/plugins/emulator/resource/emulator.pngbin0 -> 386 bytes
-rw-r--r--src/plugins/emulator/resource/flip.pngbin0 -> 327 bytes
-rw-r--r--src/plugins/emulator/resource/main_display.pngbin0 -> 760 bytes
-rw-r--r--src/plugins/emulator/resource/receivebyemulator.pngbin0 -> 335 bytes
-rw-r--r--src/plugins/emulator/resource/reset.pngbin0 -> 351 bytes
-rw-r--r--src/plugins/emulator/resource/sentbyemulator.pngbin0 -> 338 bytes
-rw-r--r--src/plugins/emulator/resource/startup_options.xml141
-rw-r--r--src/plugins/emulator/resource/zoom-in.pngbin0 -> 763 bytes
-rw-r--r--src/plugins/emulator/resource/zoom-out.pngbin0 -> 725 bytes
-rw-r--r--src/plugins/emulator/schema/androidEmulatorDefinition.exsd152
-rw-r--r--src/plugins/emulator/schema/androidPerspectiveExtension.exsd82
-rw-r--r--src/plugins/emulator/schema/deviceFramework.exsd102
-rw-r--r--src/plugins/emulator/schema/skin.exsd84
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/EmulatorPlugin.java456
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/core/devfrm/DeviceFrameworkManager.java315
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/core/devfrm/IDeviceFrameworkSupport.java46
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/core/emulationui/AbstractEmuCtProvider.java248
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/core/emulationui/AbstractEmuLabelProvider.java286
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/core/emulationui/EmuViewerLeafNode.java71
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/core/emulationui/EmuViewerNode.java152
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/core/emulationui/EmuViewerRootNode.java94
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/core/emulationui/IEmuIconPath.java30
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/core/emulationui/SrcDestComposite.java439
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/core/exception/InstanceNotFoundException.java58
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/core/exception/InstanceStartException.java57
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/core/exception/InstanceStopException.java57
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/core/exception/SkinException.java58
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/core/exception/StartCancelledException.java58
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/core/exception/StartTimeoutException.java58
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/core/model/AbstractInputLogic.java55
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/core/model/IAndroidEmulatorInstance.java228
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/core/model/IEmulatorView.java36
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/core/model/IInputLogic.java56
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/core/skin/AndroidPressKey.java251
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/core/skin/AndroidSkinBean.java123
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/core/skin/IAndroidKey.java57
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/core/skin/IAndroidSkin.java163
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/core/skin/ISkinFrameworkConstants.java33
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/core/skin/ISkinKeyXmlTags.java90
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/core/skin/SkinFramework.java219
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/core/utils/EmulatorCoreUtils.java175
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/core/utils/TelnetAndroidInput.java416
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/core/utils/VncAndroidInput.java96
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/device/AndroidDeviceHandler.java61
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/device/AndroidDeviceUtils.java74
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/device/CreateAVDOnStartupListener.java73
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/device/EmulatorDropSupportHandler.java37
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/device/IAndroidDeviceConstants.java30
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/device/IDevicePropertiesConstants.java132
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/device/SequoyahInstanceBackward.java78
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/device/SequoyahLogRedirector.java237
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/device/TmLDeviceFrameworkSupport.java88
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/device/definition/AndroidEmuDefBean.java102
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/device/definition/AndroidEmuDefMgr.java265
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/device/definition/IAndroidEmuDefConstants.java42
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/device/handlers/OpenNewDeviceWizardHandler.java52
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/device/init/InitServiceHandler.java145
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/device/instance/AndroidDevInstBuilder.java108
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/device/instance/AndroidDevInstListener.java130
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/device/instance/AndroidDeviceInstance.java776
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/device/instance/options/IStartupOptionsConstants.java155
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/device/instance/options/StartupOption.java307
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/device/instance/options/StartupOptionsGroup.java106
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/device/instance/options/StartupOptionsMgt.java785
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/device/refresh/InstancesListRefresh.java184
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/device/refresh/InstancesListRefreshHandler.java39
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/device/sync/DeviceViewsSync.java326
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/device/ui/AbstractPropertiesComposite.java249
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/device/ui/AndroidPropertiesPage.java185
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/device/ui/AndroidPropertiesStartupOptionsPage.java228
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/device/ui/DevicePropertiesPage.java47
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/device/ui/DpiScaleCalculatorDialog.java383
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/device/ui/InfoComposite.java210
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/device/ui/PropertiesMainComposite.java1287
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/device/ui/StartupOptionsComposite.java435
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/device/ui/wizard/WizardMainPage.java445
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/device/ui/wizard/WizardMainPageOperation.java76
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/device/ui/wizard/WizardStartupOptionsPage.java195
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/i18n/EmulatorNLS.java428
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/i18n/emulatorNLS.properties173
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/logic/AbstractStartAndroidEmulatorLogic.java69
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/logic/AndroidExceptionHandler.java392
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/logic/AndroidLogicUtils.java360
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/logic/ConnectVncLogic.java227
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/logic/ForwardVncPortLogic.java55
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/logic/IAndroidLogic.java30
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/logic/IAndroidLogicInstance.java103
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/logic/StartEmulatorProcessLogic.java523
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/logic/StartVncServerLogic.java229
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/logic/TransferFilesLogic.java97
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/logic/reset/AndroidEmulatorReseter.java189
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/logic/start/AndroidEmulatorStarter.java173
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/logic/stop/AndroidEmulatorStopper.java188
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/service/repair/RepairAvdHandler.java107
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/service/reset/ResetServiceHandler.java124
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/service/start/StartEmulatorHandler.java128
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/service/stop/StopEmulatorCommand.java47
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/service/stop/StopEmulatorHandler.java130
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/skin/android/AndroidSkin.java428
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/skin/android/AndroidSkinTranslator.java1370
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/skin/android/parser/ILayoutBean.java27
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/skin/android/parser/ILayoutConstants.java78
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/skin/android/parser/ImagePositionBean.java175
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/skin/android/parser/LayoutBean.java231
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/skin/android/parser/LayoutFileModel.java706
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/skin/android/parser/LayoutFileParser.java552
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/skin/android/parser/PartBean.java154
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/skin/android/parser/PartRefBean.java149
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/skin/android/parser/RectangleBean.java127
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/IAndroidUIConstants.java24
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/IUIHelpConstants.java31
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/controls/IAndroidComposite.java80
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/controls/RemoteCLIDisplay.java277
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/controls/UIHelper.java145
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/controls/maindisplay/MainDisplayComposite.java470
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/controls/nativewindow/NativeWindowComposite.java621
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/controls/skin/AndroidSkinLayout.java507
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/controls/skin/SkinComposite.java1300
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/handlers/AbstractZoomHandler.java146
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/handlers/ChangeEmulatorOrientationHandler.java119
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/handlers/ChangeZoomHandler.java80
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/handlers/IHandlerConstants.java69
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/handlers/ShowViewHandler.java79
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/handlers/ZoomInOutHandler.java90
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/perspective/AndroidEmulatorPerspective.java254
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/perspective/extension/AndroidPerspectiveExtensionBean.java98
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/perspective/extension/AndroidPerspectiveExtensionReader.java90
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/perspective/extension/IAndroidPerspectiveExtensionConstants.java39
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/view/AbstractAndroidView.java1839
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/view/AndroidView.java265
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/view/AndroidViewData.java167
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/view/LayoutContributionItem.java143
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/view/MainDisplayView.java224
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator10/StartAndroidEmulatorLogic.java117
-rw-r--r--src/plugins/handset/.classpath7
-rw-r--r--src/plugins/handset/.project28
-rw-r--r--src/plugins/handset/META-INF/MANIFEST.MF20
-rw-r--r--src/plugins/handset/build.properties7
-rw-r--r--src/plugins/handset/icons/iniciate-icon-16x16.pngbin0 -> 438 bytes
-rw-r--r--src/plugins/handset/icons/plate16.gifbin0 -> 1429 bytes
-rw-r--r--src/plugins/handset/icons/started-icon-16x16.pngbin0 -> 424 bytes
-rw-r--r--src/plugins/handset/plugin.properties14
-rw-r--r--src/plugins/handset/plugin.xml83
-rw-r--r--src/plugins/handset/src/com/motorola/studio/android/handset/AndroidHandsetHandler.java53
-rw-r--r--src/plugins/handset/src/com/motorola/studio/android/handset/AndroidHandsetInstance.java106
-rw-r--r--src/plugins/handset/src/com/motorola/studio/android/handset/DummyServiceHandler.java112
-rw-r--r--src/plugins/handset/src/com/motorola/studio/android/handset/HandsetDropSupportHandler.java36
-rw-r--r--src/plugins/handset/src/com/motorola/studio/android/handset/HandsetInstanceBuilder.java98
-rw-r--r--src/plugins/handset/src/com/motorola/studio/android/handset/HandsetPlugin.java163
-rw-r--r--src/plugins/handset/src/com/motorola/studio/android/handset/i18n/AndroidHandsetNLS.java59
-rw-r--r--src/plugins/handset/src/com/motorola/studio/android/handset/i18n/androidHandsetNLS.properties6
-rw-r--r--src/plugins/handset/src/com/motorola/studio/android/handset/ui/AndroidPropertiesPage.java101
-rw-r--r--src/plugins/handset/src/com/motorola/studio/android/handset/ui/DevicePropertiesPage.java52
-rw-r--r--src/plugins/installer/.classpath7
-rw-r--r--src/plugins/installer/.project28
-rw-r--r--src/plugins/installer/META-INF/MANIFEST.MF40
-rw-r--r--src/plugins/installer/build.properties7
-rw-r--r--src/plugins/installer/icons/item_background.pngbin0 -> 1184 bytes
-rw-r--r--src/plugins/installer/icons/item_background_hover.pngbin0 -> 1550 bytes
-rw-r--r--src/plugins/installer/icons/obj16/MOTODEV_update_icon_16x16.pngbin0 -> 681 bytes
-rw-r--r--src/plugins/installer/icons/obj16/get_new_stuff_box_light.pngbin0 -> 917 bytes
-rw-r--r--src/plugins/installer/icons/obj16/sdk.pngbin0 -> 927 bytes
-rw-r--r--src/plugins/installer/icons/wizban/installer_image_top.pngbin0 -> 20010 bytes
-rw-r--r--src/plugins/installer/plugin.properties24
-rw-r--r--src/plugins/installer/plugin.xml24
-rw-r--r--src/plugins/installer/resources/openInputMac.scpt2
-rw-r--r--src/plugins/installer/resources/openOutputMac.scpt2
-rw-r--r--src/plugins/installer/src/com/motorola/studio/android/installer/InstallerException.java38
-rw-r--r--src/plugins/installer/src/com/motorola/studio/android/installer/InstallerPlugin.java124
-rw-r--r--src/plugins/installer/src/com/motorola/studio/android/installer/handlers/UpdateStudioHandler.java83
-rw-r--r--src/plugins/installer/src/com/motorola/studio/android/installer/i18n/InstallerNLS.java89
-rw-r--r--src/plugins/installer/src/com/motorola/studio/android/installer/i18n/installerNLS.properties36
-rw-r--r--src/plugins/installer/src/com/motorola/studio/android/installer/jobs/UpdateStudioJob.java266
-rw-r--r--src/plugins/installer/src/com/motorola/studio/android/installer/policy/MotodevPolicy.java63
-rw-r--r--src/plugins/installer/src/com/motorola/studio/android/installer/ui/dialogs/AcceptLicensesDialog.java363
-rw-r--r--src/plugins/installer/src/com/motorola/studio/android/installer/utilities/IInstallManager.java72
-rw-r--r--src/plugins/installer/src/com/motorola/studio/android/installer/utilities/InstallManager.java120
-rw-r--r--src/plugins/installer/src/com/motorola/studio/android/installer/utilities/InstallableItem.java231
-rw-r--r--src/plugins/installer/src/com/motorola/studio/android/installer/utilities/P2InstallableItem.java74
-rw-r--r--src/plugins/installer/src/com/motorola/studio/android/installer/utilities/P2Installer.java520
-rw-r--r--src/plugins/installer/src/com/motorola/studio/android/installer/utilities/P2RepositoriesFactory.java123
-rw-r--r--src/plugins/installer/src/com/motorola/studio/android/installer/utilities/P2Utilities.java787
-rw-r--r--src/plugins/launch/.classpath7
-rw-r--r--src/plugins/launch/.project28
-rw-r--r--src/plugins/launch/META-INF/MANIFEST.MF25
-rw-r--r--src/plugins/launch/build.properties23
-rw-r--r--src/plugins/launch/icons/category.gifbin0 -> 1666 bytes
-rw-r--r--src/plugins/launch/icons/choose_compatible_avd_instance.pngbin0 -> 4998 bytes
-rw-r--r--src/plugins/launch/icons/motodevapp.gifbin0 -> 1666 bytes
-rw-r--r--src/plugins/launch/plugin.properties39
-rw-r--r--src/plugins/launch/plugin.xml103
-rw-r--r--src/plugins/launch/src/com/motorola/studio/android/launch/ILaunchConfigurationConstants.java151
-rw-r--r--src/plugins/launch/src/com/motorola/studio/android/launch/LaunchConfigurationShortcut.java408
-rw-r--r--src/plugins/launch/src/com/motorola/studio/android/launch/LaunchPlugin.java78
-rw-r--r--src/plugins/launch/src/com/motorola/studio/android/launch/LaunchUtils.java515
-rw-r--r--src/plugins/launch/src/com/motorola/studio/android/launch/StudioAndroidConfigurationDelegate.java762
-rw-r--r--src/plugins/launch/src/com/motorola/studio/android/launch/i18n/LaunchNLS.java130
-rw-r--r--src/plugins/launch/src/com/motorola/studio/android/launch/i18n/launchNLS.properties57
-rw-r--r--src/plugins/launch/src/com/motorola/studio/android/launch/ui/AndroidProjectsSelectionDialog.java104
-rw-r--r--src/plugins/launch/src/com/motorola/studio/android/launch/ui/DeviceSelectionDialog.java136
-rw-r--r--src/plugins/launch/src/com/motorola/studio/android/launch/ui/LaunchConfigurationTab.java981
-rw-r--r--src/plugins/launch/src/com/motorola/studio/android/launch/ui/LaunchConfigurationTabGroup.java55
-rw-r--r--src/plugins/launch/src/com/motorola/studio/android/launch/ui/StartedInstancesDialog.java386
-rw-r--r--src/plugins/logger.collector/.classpath7
-rw-r--r--src/plugins/logger.collector/.project28
-rw-r--r--src/plugins/logger.collector/META-INF/MANIFEST.MF14
-rw-r--r--src/plugins/logger.collector/build.properties7
-rw-r--r--src/plugins/logger.collector/plugin.properties1
-rw-r--r--src/plugins/logger.collector/plugin.xml42
-rw-r--r--src/plugins/logger.collector/schema/log.exsd102
-rw-r--r--src/plugins/logger.collector/src/com/motorola/studio/android/logger/collector/core/ILogFile.java48
-rw-r--r--src/plugins/logger.collector/src/com/motorola/studio/android/logger/collector/core/internal/CollectLogFile.java112
-rw-r--r--src/plugins/logger.collector/src/com/motorola/studio/android/logger/collector/core/internal/EclipseLogFile.java64
-rw-r--r--src/plugins/logger.collector/src/com/motorola/studio/android/logger/collector/core/internal/EnvironmentLogFile.java65
-rw-r--r--src/plugins/logger.collector/src/com/motorola/studio/android/logger/collector/core/internal/StudioLogFile.java83
-rw-r--r--src/plugins/logger.collector/src/com/motorola/studio/android/logger/collector/ui/LogFileColumn.java217
-rw-r--r--src/plugins/logger.collector/src/com/motorola/studio/android/logger/collector/ui/handler/LoggerCollectorHandler.java42
-rw-r--r--src/plugins/logger.collector/src/com/motorola/studio/android/logger/collector/ui/wizard/LoggerCollectorWizard.java111
-rw-r--r--src/plugins/logger.collector/src/com/motorola/studio/android/logger/collector/ui/wizard/LoggerCollectorWizardPage.java311
-rw-r--r--src/plugins/logger.collector/src/com/motorola/studio/android/logger/collector/util/LogCollectorExtensionLoader.java76
-rw-r--r--src/plugins/logger.collector/src/com/motorola/studio/android/logger/collector/util/LoggerCollectorConstants.java52
-rw-r--r--src/plugins/logger.collector/src/com/motorola/studio/android/logger/collector/util/LoggerCollectorMessages.java123
-rw-r--r--src/plugins/logger.collector/src/com/motorola/studio/android/logger/collector/util/PlatformException.java51
-rw-r--r--src/plugins/logger.collector/src/com/motorola/studio/android/logger/collector/util/PlatformLogger.java201
-rw-r--r--src/plugins/logger.collector/src/com/motorola/studio/android/logger/collector/util/WidgetsFactory.java445
-rw-r--r--src/plugins/logger.collector/src/com/motorola/studio/android/logger/collector/util/WidgetsUtil.java392
-rw-r--r--src/plugins/logger.collector/src/com/motorola/studio/android/logger/collector/util/ZipUtil.java260
-rw-r--r--src/plugins/logger.collector/src/loggerCollector.properties31
-rw-r--r--src/plugins/logger/.classpath8
-rw-r--r--src/plugins/logger/.project28
-rw-r--r--src/plugins/logger/META-INF/MANIFEST.MF13
-rw-r--r--src/plugins/logger/build.properties9
-rw-r--r--src/plugins/logger/lib/log4j-1.2.14.jarbin0 -> 367444 bytes
-rw-r--r--src/plugins/logger/logger.properties22
-rw-r--r--src/plugins/logger/plugin.xml6
-rw-r--r--src/plugins/logger/schema/configuration.exsd102
-rw-r--r--src/plugins/logger/src/com/motorola/studio/android/logger/Level.java77
-rw-r--r--src/plugins/logger/src/com/motorola/studio/android/logger/Logger.java289
-rw-r--r--src/plugins/logger/src/com/motorola/studio/android/logger/internal/Activator.java64
-rw-r--r--src/plugins/logger/src/com/motorola/studio/android/logger/internal/EclipseEnvironmentManager.java328
-rw-r--r--src/plugins/logger/src/com/motorola/studio/android/logger/internal/EnvironmentManager.java28
-rw-r--r--src/plugins/logger/src/com/motorola/studio/android/logger/internal/VMEnvironmentManager.java60
-rw-r--r--src/plugins/mat/.classpath7
-rw-r--r--src/plugins/mat/.project28
-rw-r--r--src/plugins/mat/META-INF/MANIFEST.MF21
-rw-r--r--src/plugins/mat/build.properties7
-rw-r--r--src/plugins/mat/icons/android_oql.pngbin0 -> 347 bytes
-rw-r--r--src/plugins/mat/icons/motodev_pane.pngbin0 -> 1014 bytes
-rw-r--r--src/plugins/mat/plugin.properties13
-rw-r--r--src/plugins/mat/plugin.xml99
-rw-r--r--src/plugins/mat/src/com/motorola/studio/android/mat/Activator.java107
-rw-r--r--src/plugins/mat/src/com/motorola/studio/android/mat/actions/HeapEditorContributions.java50
-rw-r--r--src/plugins/mat/src/com/motorola/studio/android/mat/actions/OpenMotodevPaneAction.java55
-rw-r--r--src/plugins/mat/src/com/motorola/studio/android/mat/commands/DumpHPROFCommand.java50
-rw-r--r--src/plugins/mat/src/com/motorola/studio/android/mat/i18n/MatNLS.java46
-rw-r--r--src/plugins/mat/src/com/motorola/studio/android/mat/i18n/matNLS.properties7
-rw-r--r--src/plugins/mat/src/com/motorola/studio/android/mat/panes/MotodevPane.java322
-rw-r--r--src/plugins/mat/src/com/motorola/studio/android/mat/services/DumpHPROFHandler.java94
-rw-r--r--src/plugins/packaging.ui/.classpath7
-rw-r--r--src/plugins/packaging.ui/.project28
-rw-r--r--src/plugins/packaging.ui/META-INF/MANIFEST.MF22
-rw-r--r--src/plugins/packaging.ui/build.properties7
-rw-r--r--src/plugins/packaging.ui/icons/export_android_package.pngbin0 -> 710 bytes
-rw-r--r--src/plugins/packaging.ui/icons/quick_fix_add.PNGbin0 -> 203 bytes
-rw-r--r--src/plugins/packaging.ui/icons/quick_fix_add_16x16.PNGbin0 -> 134 bytes
-rw-r--r--src/plugins/packaging.ui/icons/sign_package.pngbin0 -> 682 bytes
-rw-r--r--src/plugins/packaging.ui/icons/unsign_package.pngbin0 -> 645 bytes
-rw-r--r--src/plugins/packaging.ui/icons/wizban/export_android_package.pngbin0 -> 4667 bytes
-rw-r--r--src/plugins/packaging.ui/plugin.properties12
-rw-r--r--src/plugins/packaging.ui/plugin.xml105
-rw-r--r--src/plugins/packaging.ui/src/com/motorola/studio/android/packaging/ui/PackagingUIPlugin.java85
-rw-r--r--src/plugins/packaging.ui/src/com/motorola/studio/android/packaging/ui/export/PackageExportWizard.java67
-rw-r--r--src/plugins/packaging.ui/src/com/motorola/studio/android/packaging/ui/export/PackageExportWizardArea.java1897
-rw-r--r--src/plugins/packaging.ui/src/com/motorola/studio/android/packaging/ui/export/PackageExportWizardPage.java121
-rw-r--r--src/plugins/packaging.ui/src/com/motorola/studio/android/packaging/ui/handlers/ExportWizardHandler.java56
-rw-r--r--src/plugins/packaging.ui/src/com/motorola/studio/android/packaging/ui/i18n/Messages.java110
-rw-r--r--src/plugins/packaging.ui/src/com/motorola/studio/android/packaging/ui/i18n/messages.properties47
-rw-r--r--src/plugins/preflighting.checkers.ui/.classpath7
-rw-r--r--src/plugins/preflighting.checkers.ui/.project28
-rw-r--r--src/plugins/preflighting.checkers.ui/META-INF/MANIFEST.MF20
-rw-r--r--src/plugins/preflighting.checkers.ui/build.properties23
-rw-r--r--src/plugins/preflighting.checkers.ui/plugin.properties21
-rw-r--r--src/plugins/preflighting.checkers.ui/plugin.xml107
-rw-r--r--src/plugins/preflighting.checkers.ui/src/com/motorolamobility/preflighting/checkers/ui/DeviceCompatibilityUnsupportedFeaturesQuickFix.java113
-rw-r--r--src/plugins/preflighting.checkers.ui/src/com/motorolamobility/preflighting/checkers/ui/ImpliedFeaturesGenerator.java71
-rw-r--r--src/plugins/preflighting.checkers.ui/src/com/motorolamobility/preflighting/checkers/ui/ImpliedFeaturesMarkerResolution.java100
-rw-r--r--src/plugins/preflighting.checkers.ui/src/com/motorolamobility/preflighting/checkers/ui/MissingMinSdkQuickFix.java110
-rw-r--r--src/plugins/preflighting.checkers.ui/src/com/motorolamobility/preflighting/checkers/ui/MissingPermissionsQuickFix.java104
-rw-r--r--src/plugins/preflighting.checkers.ui/src/com/motorolamobility/preflighting/checkers/ui/QuickFixGenerator.java133
-rw-r--r--src/plugins/preflighting.checkers.ui/src/com/motorolamobility/preflighting/checkers/ui/UneededMaxSdkQuickFix.java96
-rw-r--r--src/plugins/preflighting.checkers.ui/src/com/motorolamobility/preflighting/checkers/ui/UnneededPermissionsQuickFix.java101
-rw-r--r--src/plugins/preflighting.checkers.ui/src/com/motorolamobility/preflighting/checkers/ui/i18n/CheckersUiNLS.java65
-rw-r--r--src/plugins/preflighting.checkers.ui/src/com/motorolamobility/preflighting/checkers/ui/i18n/messages.properties37
-rw-r--r--src/plugins/preflighting.checkers/.classpath7
-rw-r--r--src/plugins/preflighting.checkers/.project28
-rw-r--r--src/plugins/preflighting.checkers/META-INF/MANIFEST.MF13
-rw-r--r--src/plugins/preflighting.checkers/build.properties22
-rw-r--r--src/plugins/preflighting.checkers/plugin.properties134
-rw-r--r--src/plugins/preflighting.checkers/plugin.xml326
-rw-r--r--src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/CheckerPlugin.java58
-rw-r--r--src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/androidmarketfilters/CertificateExpiredCondition.java129
-rw-r--r--src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/androidmarketfilters/DeclaredMaxSdkCondition.java116
-rw-r--r--src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/androidmarketfilters/IsDebuggableCondition.java125
-rw-r--r--src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/androidmarketfilters/LogCallsCondition.java118
-rw-r--r--src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/androidmarketfilters/MinSdkIsPreviewCondition.java102
-rw-r--r--src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/androidmarketfilters/MissingIconLabelCondition.java139
-rw-r--r--src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/androidmarketfilters/MissingMinSdkCondition.java142
-rw-r--r--src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/androidmarketfilters/MissingVersionOrNameCondition.java136
-rw-r--r--src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/androidmarketfilters/PermissionImpliedFeaturesCondition.java159
-rw-r--r--src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/androidmarketfilters/PreviewTargetSdkCondition.java104
-rw-r--r--src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/androidmarketfilters/ZipalignedCondition.java242
-rw-r--r--src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/buildingblocksdeclaration/BuildingBlocksInheritanceCondition.java405
-rw-r--r--src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/deviceCompatibility/SmallScreensSupportCondition.java252
-rw-r--r--src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/deviceCompatibility/UnsupportedFeaturesConditions.java315
-rw-r--r--src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/deviceCompatibility/XLargeScreensSupportCondition.java269
-rw-r--r--src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/i18n/CheckerNLS.java236
-rw-r--r--src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/i18n/CheckerNLS.properties116
-rw-r--r--src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/layout/GlobalLayoutId.java60
-rw-r--r--src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/layout/LayoutFileId.java85
-rw-r--r--src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/layout/MissingIdCondition.java231
-rw-r--r--src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/layout/RepeatedIdCondition.java152
-rw-r--r--src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/layout/ViewTypeIdsCondition.java208
-rw-r--r--src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/layout/XlargeConfigCondition.java235
-rw-r--r--src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/localizationStrings/LocalizationStringsChecker.java333
-rw-r--r--src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/localizationStrings/MissingDefaultLanguageKeyCondition.java219
-rw-r--r--src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/localizationStrings/MissingLanguageKeyCondition.java224
-rw-r--r--src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/localizationStrings/MissingValueCondition.java314
-rw-r--r--src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/logic/OpenedCursorsCondition.java291
-rw-r--r--src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/mainactivity/SingleMainActivityCondition.java270
-rw-r--r--src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/missingdrawable/AllDensitiesSupportCondition.java101
-rw-r--r--src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/missingdrawable/MissingDrawableChecker.java127
-rw-r--r--src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/missingdrawable/MissingDrawableData.java304
-rw-r--r--src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/missingdrawable/MissingDrawableFoldersCondition.java168
-rw-r--r--src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/missingdrawable/MissingDrawableResourcesCondition.java171
-rw-r--r--src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/orphanedstrings/OrphanedStringsCondition.java162
-rw-r--r--src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/permissions/BlockedPermissionCondition.java169
-rw-r--r--src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/permissions/MissingPermissionsCondition.java172
-rw-r--r--src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/permissions/NeededAppPermissions.java91
-rw-r--r--src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/permissions/PermissionsChecker.java73
-rw-r--r--src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/permissions/UnneededPermissionsCondition.java150
-rw-r--r--src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/widgetPreview/MissingWidgetPreviewTagCondition.java386
-rw-r--r--src/plugins/preflighting.core/.classpath9
-rw-r--r--src/plugins/preflighting.core/.project28
-rw-r--r--src/plugins/preflighting.core/META-INF/MANIFEST.MF45
-rw-r--r--src/plugins/preflighting.core/about.ini24
-rw-r--r--src/plugins/preflighting.core/about.properties7
-rw-r--r--src/plugins/preflighting.core/apktool/apktool.jarbin0 -> 3090514 bytes
-rw-r--r--src/plugins/preflighting.core/build.properties16
-rw-r--r--src/plugins/preflighting.core/configuration_files/appvalidator.cfg4
-rw-r--r--src/plugins/preflighting.core/configuration_files/macosx_x86/appvalidator.cfg4
-rw-r--r--src/plugins/preflighting.core/devices/a1260.xml33
-rw-r--r--src/plugins/preflighting.core/devices/a1680.xml33
-rw-r--r--src/plugins/preflighting.core/devices/admiral-xt603.xml56
-rw-r--r--src/plugins/preflighting.core/devices/atrix-2-mb865.xml50
-rw-r--r--src/plugins/preflighting.core/devices/atrix-2-me865.xml50
-rw-r--r--src/plugins/preflighting.core/devices/atrix-mb860.xml56
-rw-r--r--src/plugins/preflighting.core/devices/atrix-me860.xml56
-rw-r--r--src/plugins/preflighting.core/devices/backflip-mb300.xml39
-rw-r--r--src/plugins/preflighting.core/devices/backflip-me600.xml39
-rw-r--r--src/plugins/preflighting.core/devices/bravo.xml53
-rw-r--r--src/plugins/preflighting.core/devices/charm-mb502.xml34
-rw-r--r--src/plugins/preflighting.core/devices/charm-me502.xml34
-rw-r--r--src/plugins/preflighting.core/devices/citrus.xml33
-rw-r--r--src/plugins/preflighting.core/devices/cliq.xml39
-rw-r--r--src/plugins/preflighting.core/devices/cliq2.xml39
-rw-r--r--src/plugins/preflighting.core/devices/cliqxt.xml33
-rw-r--r--src/plugins/preflighting.core/devices/defy.xml33
-rw-r--r--src/plugins/preflighting.core/devices/defyplus-mb526.xml48
-rw-r--r--src/plugins/preflighting.core/devices/devour.xml38
-rw-r--r--src/plugins/preflighting.core/devices/dext-mb200.xml39
-rw-r--r--src/plugins/preflighting.core/devices/droid-3-xt862.xml62
-rw-r--r--src/plugins/preflighting.core/devices/droid-4-xt894.xml65
-rw-r--r--src/plugins/preflighting.core/devices/droid-bionic-xt875.xml52
-rw-r--r--src/plugins/preflighting.core/devices/droid-razr-maxx-xt912.xml49
-rw-r--r--src/plugins/preflighting.core/devices/droid-razr-xt912.xml49
-rw-r--r--src/plugins/preflighting.core/devices/droid-x2-mb870.xml54
-rw-r--r--src/plugins/preflighting.core/devices/droid-xyboard-10-1-mz617.xml60
-rw-r--r--src/plugins/preflighting.core/devices/droid-xyboard-8-2-mz609.xml60
-rw-r--r--src/plugins/preflighting.core/devices/droid.xml39
-rw-r--r--src/plugins/preflighting.core/devices/droid2.xml60
-rw-r--r--src/plugins/preflighting.core/devices/droid2global.xml61
-rw-r--r--src/plugins/preflighting.core/devices/droidpro.xml56
-rw-r--r--src/plugins/preflighting.core/devices/droidx.xml54
-rw-r--r--src/plugins/preflighting.core/devices/electrify.xml50
-rw-r--r--src/plugins/preflighting.core/devices/fire-xt-xt530.xml50
-rw-r--r--src/plugins/preflighting.core/devices/fire-xt311.xml34
-rw-r--r--src/plugins/preflighting.core/devices/fire-xt317.xml34
-rw-r--r--src/plugins/preflighting.core/devices/flipout-mb511.xml39
-rw-r--r--src/plugins/preflighting.core/devices/flipout-me511.xml39
-rw-r--r--src/plugins/preflighting.core/devices/flipside.xml57
-rw-r--r--src/plugins/preflighting.core/devices/i1.xml33
-rw-r--r--src/plugins/preflighting.core/devices/i940.xml45
-rw-r--r--src/plugins/preflighting.core/devices/mb501.xml33
-rw-r--r--src/plugins/preflighting.core/devices/me501.xml33
-rw-r--r--src/plugins/preflighting.core/devices/me525-plus.xml48
-rw-r--r--src/plugins/preflighting.core/devices/me525.xml33
-rw-r--r--src/plugins/preflighting.core/devices/me811.xml54
-rw-r--r--src/plugins/preflighting.core/devices/milestone-3-xt860.xml61
-rw-r--r--src/plugins/preflighting.core/devices/milestone-a853.xml39
-rw-r--r--src/plugins/preflighting.core/devices/milestone-qrty-a853.xml39
-rw-r--r--src/plugins/preflighting.core/devices/milestone-xt720.xml33
-rw-r--r--src/plugins/preflighting.core/devices/milestone2-me722.xml60
-rw-r--r--src/plugins/preflighting.core/devices/milestone2.xml60
-rw-r--r--src/plugins/preflighting.core/devices/motoluxe-xt615.xml51
-rw-r--r--src/plugins/preflighting.core/devices/mt620.xml34
-rw-r--r--src/plugins/preflighting.core/devices/mt710.xml33
-rw-r--r--src/plugins/preflighting.core/devices/mt716.xml39
-rw-r--r--src/plugins/preflighting.core/devices/mt720.xml33
-rw-r--r--src/plugins/preflighting.core/devices/mt810.xml33
-rw-r--r--src/plugins/preflighting.core/devices/mt810lx.xml33
-rw-r--r--src/plugins/preflighting.core/devices/mt870.xml55
-rw-r--r--src/plugins/preflighting.core/devices/mt917.xml50
-rw-r--r--src/plugins/preflighting.core/devices/photon-mb855.xml50
-rw-r--r--src/plugins/preflighting.core/devices/pro-xt610.xml34
-rw-r--r--src/plugins/preflighting.core/devices/quenchxt3.xml33
-rw-r--r--src/plugins/preflighting.core/devices/quenchxt5.xml33
-rw-r--r--src/plugins/preflighting.core/devices/razr-xt910.xml49
-rw-r--r--src/plugins/preflighting.core/devices/spice-key-xt316.xml34
-rw-r--r--src/plugins/preflighting.core/devices/spice-xt-xt531.xml50
-rw-r--r--src/plugins/preflighting.core/devices/spice.xml38
-rw-r--r--src/plugins/preflighting.core/devices/titanium.xml52
-rw-r--r--src/plugins/preflighting.core/devices/triumph-wx435.xml47
-rw-r--r--src/plugins/preflighting.core/devices/xoom-2-3g-mz616.xml56
-rw-r--r--src/plugins/preflighting.core/devices/xoom-2-me-3g-mz608.xml56
-rw-r--r--src/plugins/preflighting.core/devices/xoom-2-me-mz607.xml56
-rw-r--r--src/plugins/preflighting.core/devices/xoom-2-mz615.xml56
-rw-r--r--src/plugins/preflighting.core/devices/xoom-mz505.xml51
-rw-r--r--src/plugins/preflighting.core/devices/xoom-mz601.xml62
-rw-r--r--src/plugins/preflighting.core/devices/xoom-mz604.xml63
-rw-r--r--src/plugins/preflighting.core/devices/xoom-mz605.xml62
-rw-r--r--src/plugins/preflighting.core/devices/xoom.xml65
-rw-r--r--src/plugins/preflighting.core/devices/xprt-mb612.xml56
-rw-r--r--src/plugins/preflighting.core/devices/xt301.xml33
-rw-r--r--src/plugins/preflighting.core/devices/xt316.xml34
-rw-r--r--src/plugins/preflighting.core/devices/xt319.xml48
-rw-r--r--src/plugins/preflighting.core/devices/xt531.xml50
-rw-r--r--src/plugins/preflighting.core/devices/xt532.xml50
-rw-r--r--src/plugins/preflighting.core/devices/xt615.xml51
-rw-r--r--src/plugins/preflighting.core/devices/xt701.xml33
-rw-r--r--src/plugins/preflighting.core/devices/xt702.xml39
-rw-r--r--src/plugins/preflighting.core/devices/xt711.xml33
-rw-r--r--src/plugins/preflighting.core/devices/xt720.xml33
-rw-r--r--src/plugins/preflighting.core/devices/xt800.xml33
-rw-r--r--src/plugins/preflighting.core/devices/xt800plus.xml33
-rw-r--r--src/plugins/preflighting.core/devices/xt800w.xml33
-rw-r--r--src/plugins/preflighting.core/devices/xt806.xml33
-rw-r--r--src/plugins/preflighting.core/devices/xt860-4g.xml61
-rw-r--r--src/plugins/preflighting.core/devices/xt882.xml56
-rw-r--r--src/plugins/preflighting.core/devices/xt883.xml62
-rw-r--r--src/plugins/preflighting.core/devices/xt928.xml54
-rw-r--r--src/plugins/preflighting.core/devices/xyboard-10-1-mz615.xml56
-rw-r--r--src/plugins/preflighting.core/devices/xyboard-8-2-mz607.xml56
-rw-r--r--src/plugins/preflighting.core/etc/log4j.properties11
-rw-r--r--src/plugins/preflighting.core/files/apilevel-permission-map.xml4228
-rw-r--r--src/plugins/preflighting.core/files/blocked_permissions.txt64
-rw-r--r--src/plugins/preflighting.core/files/devices/droidx/hardware.ini73
-rw-r--r--src/plugins/preflighting.core/files/devices/droidx/manifest.ini13
-rw-r--r--src/plugins/preflighting.core/files/feature-apilevel-map.xml83
-rw-r--r--src/plugins/preflighting.core/files/method_permission_list_2.3.csv159
-rw-r--r--src/plugins/preflighting.core/files/method_permission_list_4.0.csv208
-rw-r--r--src/plugins/preflighting.core/files/permission-feature-map.xml111
-rw-r--r--src/plugins/preflighting.core/files/signature-permission-map.xml46
-rw-r--r--src/plugins/preflighting.core/lib/log4j-1.2.14.jarbin0 -> 367444 bytes
-rw-r--r--src/plugins/preflighting.core/plugin.properties7
-rw-r--r--src/plugins/preflighting.core/plugin.xml6
-rw-r--r--src/plugins/preflighting.core/resources/plate32.pngbin0 -> 4980 bytes
-rw-r--r--src/plugins/preflighting.core/schema/checker.exsd304
-rw-r--r--src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/IParameterProcessor.java42
-rw-r--r--src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/PreflightingCorePlugin.java116
-rw-r--r--src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/applicationdata/ApplicationData.java472
-rw-r--r--src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/applicationdata/Element.java188
-rw-r--r--src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/applicationdata/ElementUtils.java84
-rw-r--r--src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/applicationdata/FolderElement.java60
-rw-r--r--src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/applicationdata/ResourcesFolderElement.java377
-rw-r--r--src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/applicationdata/SourceFolderElement.java426
-rw-r--r--src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/applicationdata/StringsElement.java325
-rw-r--r--src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/applicationdata/XMLElement.java93
-rw-r--r--src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/checker/Checker.java339
-rw-r--r--src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/checker/CheckerDescription.java85
-rw-r--r--src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/checker/CheckerExtension.java146
-rw-r--r--src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/checker/IChecker.java135
-rw-r--r--src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/checker/condition/CanExecuteConditionStatus.java167
-rw-r--r--src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/checker/condition/Condition.java254
-rw-r--r--src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/checker/condition/ICondition.java141
-rw-r--r--src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/checker/parameter/CheckerParameter.java213
-rw-r--r--src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/checker/parameter/ICheckerParameter.java170
-rw-r--r--src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/devicelayoutspecification/Device.java228
-rw-r--r--src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/devicelayoutspecification/LayoutDevicesReader.java247
-rw-r--r--src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/devicelayoutspecification/ParametersType.java587
-rw-r--r--src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/devicespecification/DeviceSpecification.java243
-rw-r--r--src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/devicespecification/DevicesSpecsContainer.java403
-rw-r--r--src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/devicespecification/internal/PlatformRules.java808
-rw-r--r--src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/exception/PreflightingCheckerException.java46
-rw-r--r--src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/exception/PreflightingExtensionPointException.java47
-rw-r--r--src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/exception/PreflightingParameterException.java34
-rw-r--r--src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/exception/PreflightingToolException.java47
-rw-r--r--src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/exception/ValidationLimitException.java44
-rw-r--r--src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/i18n/PreflightingCoreNLS.java233
-rw-r--r--src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/i18n/messages.properties101
-rw-r--r--src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/internal/checker/CheckerExtensionReader.java281
-rw-r--r--src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/internal/checkerparameter/CheckerParameterElement.java228
-rw-r--r--src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/internal/cond/utils/ConditionUtils.java50
-rw-r--r--src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/internal/conditions/ConditionElement.java108
-rw-r--r--src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/internal/devicelayoutspecification/ConfigType.java80
-rw-r--r--src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/internal/devicelayoutspecification/LayoutDevicesType.java101
-rw-r--r--src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/internal/devicelayoutspecification/ObjectFactory.java99
-rw-r--r--src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/internal/devicelayoutspecification/ScreenDimension.java87
-rw-r--r--src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/internal/logging/Logger.java295
-rw-r--r--src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/internal/permissionfeature/PermissionToFeatureMapReader.java126
-rw-r--r--src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/internal/permissionfeature/PermissionToFeatureMapping.java73
-rw-r--r--src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/internal/utils/AaptUtils.java1528
-rw-r--r--src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/internal/utils/ApkUtils.java318
-rw-r--r--src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/internal/utils/ApktoolUtils.java982
-rw-r--r--src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/internal/utils/MethodPermissionCSVReader.java208
-rw-r--r--src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/internal/utils/ProjectUtils.java1680
-rw-r--r--src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/internal/utils/StringUsageIdentifier.java120
-rw-r--r--src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/logging/Level.java78
-rw-r--r--src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/logging/PreflightingLogger.java167
-rw-r--r--src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/permissionfeature/Feature.java115
-rw-r--r--src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/permissionfeature/Permission.java123
-rw-r--r--src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/sdk/SdkUtils.java227
-rw-r--r--src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/source/model/Assignment.java27
-rw-r--r--src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/source/model/Constant.java73
-rw-r--r--src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/source/model/Field.java31
-rw-r--r--src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/source/model/Instruction.java66
-rw-r--r--src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/source/model/Invoke.java217
-rw-r--r--src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/source/model/Method.java207
-rw-r--r--src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/source/model/PermissionGroups.java75
-rw-r--r--src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/source/model/SourceFileElement.java283
-rw-r--r--src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/source/model/Variable.java179
-rw-r--r--src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/utils/CheckerUtils.java304
-rw-r--r--src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/utils/LayoutConstants.java29
-rw-r--r--src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/utils/LimitedList.java208
-rw-r--r--src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/utils/ManifestConstants.java117
-rw-r--r--src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/utils/XmlUtils.java209
-rw-r--r--src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/validation/ApplicationValidationResult.java150
-rw-r--r--src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/validation/ComplexParameter.java86
-rw-r--r--src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/validation/GlobalInputParamsValidator.java461
-rw-r--r--src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/validation/Parameter.java140
-rw-r--r--src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/validation/ParameterDescription.java208
-rw-r--r--src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/validation/ParameterType.java36
-rw-r--r--src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/validation/ValidationManager.java1975
-rw-r--r--src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/validation/ValidationManagerConfiguration.java195
-rw-r--r--src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/validation/ValidationResult.java96
-rw-r--r--src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/validation/ValidationResultData.java350
-rw-r--r--src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/validation/Value.java64
-rw-r--r--src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/verbose/DebugVerboseOutputter.java193
-rw-r--r--src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/verbose/WarningLevelFilter.java502
-rw-r--r--src/plugins/preflighting.core/xsd/layout-devices.xsd344
-rw-r--r--src/plugins/preflighting.samplechecker.androidlabel/.classpath7
-rw-r--r--src/plugins/preflighting.samplechecker.androidlabel/.project28
-rw-r--r--src/plugins/preflighting.samplechecker.androidlabel/META-INF/MANIFEST.MF16
-rw-r--r--src/plugins/preflighting.samplechecker.androidlabel/build.properties23
-rw-r--r--src/plugins/preflighting.samplechecker.androidlabel/plugin.properties24
-rw-r--r--src/plugins/preflighting.samplechecker.androidlabel/plugin.xml48
-rw-r--r--src/plugins/preflighting.samplechecker.androidlabel/src/com/motorolamobility/preflighting/samplechecker/androidlabel/AndroidLabelActivator.java73
-rw-r--r--src/plugins/preflighting.samplechecker.androidlabel/src/com/motorolamobility/preflighting/samplechecker/androidlabel/i18n/AndroidLabelCheckerNLS.java52
-rw-r--r--src/plugins/preflighting.samplechecker.androidlabel/src/com/motorolamobility/preflighting/samplechecker/androidlabel/i18n/AndroidLabelCheckerNLS.properties25
-rw-r--r--src/plugins/preflighting.samplechecker.androidlabel/src/com/motorolamobility/preflighting/samplechecker/androidlabel/implementation/AndroidLabelChecker.java59
-rw-r--r--src/plugins/preflighting.samplechecker.androidlabel/src/com/motorolamobility/preflighting/samplechecker/androidlabel/implementation/CorrectTextInLabelCondition.java377
-rw-r--r--src/plugins/preflighting.samplecheckers.findviewbyid.ui/.classpath7
-rw-r--r--src/plugins/preflighting.samplecheckers.findviewbyid.ui/.project28
-rw-r--r--src/plugins/preflighting.samplecheckers.findviewbyid.ui/META-INF/MANIFEST.MF20
-rw-r--r--src/plugins/preflighting.samplecheckers.findviewbyid.ui/build.properties11
-rw-r--r--src/plugins/preflighting.samplecheckers.findviewbyid.ui/plugin.properties3
-rw-r--r--src/plugins/preflighting.samplecheckers.findviewbyid.ui/plugin.xml21
-rw-r--r--src/plugins/preflighting.samplecheckers.findviewbyid.ui/src/com/motorolamobility/preflighting/samplechecker/findviewbyid/quickfix/FindViewByIdMarkerGenerator.java76
-rw-r--r--src/plugins/preflighting.samplecheckers.findviewbyid.ui/src/com/motorolamobility/preflighting/samplechecker/findviewbyid/quickfix/FindViewByIdMarkerResolution.java315
-rw-r--r--src/plugins/preflighting.samplecheckers.findviewbyid.ui/src/com/motorolamobility/preflighting/samplechecker/findviewbyid/quickfix/i18n/MessagesNLS.java45
-rw-r--r--src/plugins/preflighting.samplecheckers.findviewbyid.ui/src/com/motorolamobility/preflighting/samplechecker/findviewbyid/quickfix/i18n/messages.properties6
-rw-r--r--src/plugins/preflighting.samplecheckers.findviewbyid/.classpath7
-rw-r--r--src/plugins/preflighting.samplecheckers.findviewbyid/.project28
-rw-r--r--src/plugins/preflighting.samplecheckers.findviewbyid/META-INF/MANIFEST.MF23
-rw-r--r--src/plugins/preflighting.samplecheckers.findviewbyid/build.properties23
-rw-r--r--src/plugins/preflighting.samplecheckers.findviewbyid/plugin.properties8
-rw-r--r--src/plugins/preflighting.samplecheckers.findviewbyid/plugin.xml36
-rw-r--r--src/plugins/preflighting.samplecheckers.findviewbyid/src/com/motorolamobility/preflighting/samplechecker/findviewbyid/SampleCheckersActivator.java88
-rw-r--r--src/plugins/preflighting.samplecheckers.findviewbyid/src/com/motorolamobility/preflighting/samplechecker/findviewbyid/i18n/Messages.java41
-rw-r--r--src/plugins/preflighting.samplecheckers.findviewbyid/src/com/motorolamobility/preflighting/samplechecker/findviewbyid/i18n/messages.properties18
-rw-r--r--src/plugins/preflighting.samplecheckers.findviewbyid/src/com/motorolamobility/preflighting/samplechecker/findviewbyid/implementation/FindViewByIdInLoop.java107
-rw-r--r--src/plugins/preflighting.samplecheckers.findviewbyid/src/com/motorolamobility/preflighting/samplechecker/findviewbyid/implementation/FindViewByIdVisitor.java203
-rw-r--r--src/plugins/preflighting.sdk/.project22
-rw-r--r--src/plugins/preflighting.sdk/META-INF/MANIFEST.MF8
-rw-r--r--src/plugins/preflighting.sdk/about.ini24
-rw-r--r--src/plugins/preflighting.sdk/about.properties7
-rw-r--r--src/plugins/preflighting.sdk/build.properties3
-rw-r--r--src/plugins/preflighting.sdk/plugin.properties3
-rw-r--r--src/plugins/preflighting.sdk/plugin.xml18
-rw-r--r--src/plugins/preflighting.sdk/resources/plate32.pngbin0 -> 4980 bytes
-rw-r--r--src/plugins/preflighting.ui/.classpath7
-rw-r--r--src/plugins/preflighting.ui/.project28
-rw-r--r--src/plugins/preflighting.ui/META-INF/MANIFEST.MF26
-rw-r--r--src/plugins/preflighting.ui/about.ini40
-rw-r--r--src/plugins/preflighting.ui/about.properties23
-rw-r--r--src/plugins/preflighting.ui/build.properties26
-rw-r--r--src/plugins/preflighting.ui/icons/MOTODEVAppValidator_128x128.pngbin0 -> 4807 bytes
-rw-r--r--src/plugins/preflighting.ui/icons/MOTODEVAppValidator_16x16.pngbin0 -> 563 bytes
-rw-r--r--src/plugins/preflighting.ui/icons/MOTODEVAppValidator_16x16_clear.pngbin0 -> 575 bytes
-rw-r--r--src/plugins/preflighting.ui/icons/MOTODEVAppValidator_24x24.pngbin0 -> 856 bytes
-rw-r--r--src/plugins/preflighting.ui/icons/MOTODEVAppValidator_256x256.pngbin0 -> 10371 bytes
-rw-r--r--src/plugins/preflighting.ui/icons/MOTODEVAppValidator_32x32.pngbin0 -> 1111 bytes
-rw-r--r--src/plugins/preflighting.ui/icons/MOTODEVAppValidator_48x48.pngbin0 -> 1674 bytes
-rw-r--r--src/plugins/preflighting.ui/icons/MOTODEVAppValidator_512x512.pngbin0 -> 22197 bytes
-rw-r--r--src/plugins/preflighting.ui/icons/MOTODEVAppValidator_64x64.pngbin0 -> 2362 bytes
-rw-r--r--src/plugins/preflighting.ui/icons/MOTODEVAppValidator_75x66.pngbin0 -> 2455 bytes
-rw-r--r--src/plugins/preflighting.ui/icons/command_validate_apps_16x16.pngbin0 -> 625 bytes
-rw-r--r--src/plugins/preflighting.ui/icons/command_validate_apps_32x32.pngbin0 -> 1494 bytes
-rw-r--r--src/plugins/preflighting.ui/icons/command_validate_projs_16x16.pngbin0 -> 634 bytes
-rw-r--r--src/plugins/preflighting.ui/icons/command_validate_projs_32x32.pngbin0 -> 1144 bytes
-rw-r--r--src/plugins/preflighting.ui/plugin.properties33
-rw-r--r--src/plugins/preflighting.ui/plugin.xml197
-rw-r--r--src/plugins/preflighting.ui/resources/plate32.pngbin0 -> 4980 bytes
-rw-r--r--src/plugins/preflighting.ui/src/com/motorolamobility/preflighting/ui/AppValidatorClearActionDelegate.java96
-rw-r--r--src/plugins/preflighting.ui/src/com/motorolamobility/preflighting/ui/CommandLinePreferencePage.java216
-rw-r--r--src/plugins/preflighting.ui/src/com/motorolamobility/preflighting/ui/PreflightingUIPlugin.java140
-rw-r--r--src/plugins/preflighting.ui/src/com/motorolamobility/preflighting/ui/handlers/AnalyzeApkHandler.java996
-rw-r--r--src/plugins/preflighting.ui/src/com/motorolamobility/preflighting/ui/handlers/OpenApkDialogHandler.java67
-rw-r--r--src/plugins/preflighting.ui/src/com/motorolamobility/preflighting/ui/handlers/OpenProjectDialogHandler.java197
-rw-r--r--src/plugins/preflighting.ui/src/com/motorolamobility/preflighting/ui/i18n/PreflightingUiNLS.java179
-rw-r--r--src/plugins/preflighting.ui/src/com/motorolamobility/preflighting/ui/i18n/messages.properties92
-rw-r--r--src/plugins/preflighting.ui/src/com/motorolamobility/preflighting/ui/tabs/AbstractAppValidatorTabComposite.java68
-rw-r--r--src/plugins/preflighting.ui/src/com/motorolamobility/preflighting/ui/tabs/CheckersTabComposite.java1755
-rw-r--r--src/plugins/preflighting.ui/src/com/motorolamobility/preflighting/ui/tabs/DevicesTabComposite.java512
-rw-r--r--src/plugins/preflighting.ui/src/com/motorolamobility/preflighting/ui/tabs/GeneralSettingsComposite.java407
-rw-r--r--src/plugins/preflighting.ui/src/com/motorolamobility/preflighting/ui/tabs/UIChangedListener.java24
-rw-r--r--src/plugins/preflighting.ui/src/com/motorolamobility/preflighting/ui/utilities/EclipseUtils.java56
-rw-r--r--src/plugins/preflighting.ui/src/com/motorolamobility/preflighting/ui/wizards/ApkValidationWizard.java96
-rw-r--r--src/plugins/preflighting.ui/src/com/motorolamobility/preflighting/ui/wizards/ApkValidationWizardPage.java435
-rw-r--r--src/plugins/preflighting/.classpath7
-rw-r--r--src/plugins/preflighting/.project28
-rw-r--r--src/plugins/preflighting/META-INF/MANIFEST.MF19
-rw-r--r--src/plugins/preflighting/build.properties7
-rw-r--r--src/plugins/preflighting/icons/AppValidator.icobin0 -> 26694 bytes
-rw-r--r--src/plugins/preflighting/icons/AppValidator.xpm1220
-rw-r--r--src/plugins/preflighting/icons/Appvalidator.icnsbin0 -> 350107 bytes
-rw-r--r--src/plugins/preflighting/plugin.properties3
-rw-r--r--src/plugins/preflighting/plugin.xml28
-rw-r--r--src/plugins/preflighting/schema/com.motorolamobility.preflighting.outputter.exsd109
-rw-r--r--src/plugins/preflighting/src/com/motorolamobility/preflighting/i18n/PreflightingNLS.java155
-rw-r--r--src/plugins/preflighting/src/com/motorolamobility/preflighting/i18n/messages.properties61
-rw-r--r--src/plugins/preflighting/src/com/motorolamobility/preflighting/internal/PreflightingApplication.java239
-rw-r--r--src/plugins/preflighting/src/com/motorolamobility/preflighting/internal/PreflightingPlugin.java125
-rw-r--r--src/plugins/preflighting/src/com/motorolamobility/preflighting/internal/commandinput/ApplicationParameterInterpreter.java416
-rw-r--r--src/plugins/preflighting/src/com/motorolamobility/preflighting/internal/commandinput/CommandLineInputProcessor.java230
-rw-r--r--src/plugins/preflighting/src/com/motorolamobility/preflighting/internal/commandinput/exception/ParameterParseException.java28
-rw-r--r--src/plugins/preflighting/src/com/motorolamobility/preflighting/internal/commandoutput/CSVOutputter.java385
-rw-r--r--src/plugins/preflighting/src/com/motorolamobility/preflighting/internal/commandoutput/DaemonXMLOutputter.java80
-rw-r--r--src/plugins/preflighting/src/com/motorolamobility/preflighting/internal/commandoutput/NullOutputStream.java29
-rw-r--r--src/plugins/preflighting/src/com/motorolamobility/preflighting/internal/commandoutput/OutputterExtensionReader.java85
-rw-r--r--src/plugins/preflighting/src/com/motorolamobility/preflighting/internal/commandoutput/OutputterFactory.java320
-rw-r--r--src/plugins/preflighting/src/com/motorolamobility/preflighting/internal/commandoutput/TextOutputter.java352
-rw-r--r--src/plugins/preflighting/src/com/motorolamobility/preflighting/internal/commandoutput/XmlOutputter.java367
-rw-r--r--src/plugins/preflighting/src/com/motorolamobility/preflighting/internal/daemon/Daemon.java422
-rw-r--r--src/plugins/preflighting/src/com/motorolamobility/preflighting/internal/daemon/JUnitClient.java152
-rw-r--r--src/plugins/preflighting/src/com/motorolamobility/preflighting/internal/help/printer/HelpPrinter.java808
-rw-r--r--src/plugins/preflighting/src/com/motorolamobility/preflighting/output/AbstractOutputter.java160
-rw-r--r--src/plugins/remote.device/.classpath7
-rw-r--r--src/plugins/remote.device/.project28
-rw-r--r--src/plugins/remote.device/META-INF/MANIFEST.MF20
-rw-r--r--src/plugins/remote.device/build.properties7
-rw-r--r--src/plugins/remote.device/icons/plate16.gifbin0 -> 1440 bytes
-rw-r--r--src/plugins/remote.device/icons/remote_device.pngbin0 -> 287 bytes
-rw-r--r--src/plugins/remote.device/icons/start.pngbin0 -> 1220 bytes
-rw-r--r--src/plugins/remote.device/icons/started-icon-16x16.pngbin0 -> 424 bytes
-rw-r--r--src/plugins/remote.device/icons/stop.pngbin0 -> 1220 bytes
-rw-r--r--src/plugins/remote.device/icons/stopped-icon-16x16.pngbin0 -> 417 bytes
-rw-r--r--src/plugins/remote.device/icons/switch2usb_cable.pngbin0 -> 2962 bytes
-rw-r--r--src/plugins/remote.device/icons/switch2usb_cable2.pngbin0 -> 318 bytes
-rw-r--r--src/plugins/remote.device/icons/switch2usb_symbol.pngbin0 -> 286 bytes
-rw-r--r--src/plugins/remote.device/icons/wireless-16x16.pngbin0 -> 497 bytes
-rw-r--r--src/plugins/remote.device/icons/wireless_wizard-icon-64x64.pngbin0 -> 6334 bytes
-rw-r--r--src/plugins/remote.device/plugin.properties42
-rw-r--r--src/plugins/remote.device/plugin.xml238
-rw-r--r--src/plugins/remote.device/src/com/motorola/studio/android/remote/RemoteDeviceConstants.java68
-rw-r--r--src/plugins/remote.device/src/com/motorola/studio/android/remote/RemoteDeviceDropSupportHandler.java36
-rw-r--r--src/plugins/remote.device/src/com/motorola/studio/android/remote/RemoteDeviceInstanceBuilder.java85
-rw-r--r--src/plugins/remote.device/src/com/motorola/studio/android/remote/RemoteDevicePlugin.java280
-rw-r--r--src/plugins/remote.device/src/com/motorola/studio/android/remote/RemoteDeviceUtils.java359
-rw-r--r--src/plugins/remote.device/src/com/motorola/studio/android/remote/RemoteDeviceWorkbenchListener.java85
-rw-r--r--src/plugins/remote.device/src/com/motorola/studio/android/remote/handlers/ConnectToRemoteHandler.java153
-rw-r--r--src/plugins/remote.device/src/com/motorola/studio/android/remote/handlers/DisconnectFromRemoteHandler.java141
-rw-r--r--src/plugins/remote.device/src/com/motorola/studio/android/remote/handlers/InitRemoteHandler.java64
-rw-r--r--src/plugins/remote.device/src/com/motorola/studio/android/remote/handlers/OpenNewRemoteDeviceWizardHandler.java53
-rw-r--r--src/plugins/remote.device/src/com/motorola/studio/android/remote/handlers/RemoteDeviceHandler.java49
-rw-r--r--src/plugins/remote.device/src/com/motorola/studio/android/remote/handlers/USBModeServiceHandler.java189
-rw-r--r--src/plugins/remote.device/src/com/motorola/studio/android/remote/handlers/WirelessServiceHandler.java206
-rw-r--r--src/plugins/remote.device/src/com/motorola/studio/android/remote/i18n/RemoteDeviceNLS.java119
-rw-r--r--src/plugins/remote.device/src/com/motorola/studio/android/remote/i18n/remoteDeviceNLS.properties62
-rw-r--r--src/plugins/remote.device/src/com/motorola/studio/android/remote/instance/RemoteDeviceInstance.java138
-rw-r--r--src/plugins/remote.device/src/com/motorola/studio/android/remote/ui/RemoteDevicePropertiesPage.java126
-rw-r--r--src/plugins/remote.device/src/com/motorola/studio/android/remote/ui/RemoteDeviceWizardPage.java101
-rw-r--r--src/plugins/remote.device/src/com/motorola/studio/android/remote/ui/RemotePropertiesComposite.java340
-rw-r--r--src/plugins/remote.device/src/com/motorola/studio/android/remote/ui/wireless/WirelessDeviceWizardPage.java147
-rw-r--r--src/plugins/remote.device/src/com/motorola/studio/android/remote/ui/wireless/WirelessPropertiesComposite.java502
-rw-r--r--src/plugins/remote.device/src/com/motorola/studio/android/remote/ui/wireless/WirelessWizard.java166
-rw-r--r--src/plugins/remote.device/src/com/motorola/studio/android/remote/ui/wireless/runnables/SwitchFromUSBAndConnectToWirelessRunnable.java230
-rw-r--r--src/plugins/snippets/.classpath7
-rw-r--r--src/plugins/snippets/.project28
-rw-r--r--src/plugins/snippets/META-INF/MANIFEST.MF19
-rw-r--r--src/plugins/snippets/build.properties6
-rw-r--r--src/plugins/snippets/plugin.properties532
-rw-r--r--src/plugins/snippets/plugin.xml2086
-rw-r--r--src/plugins/snippets/src/com/motorola/studio/android/codesnippets/AndroidPermissionInsertSnippet.java274
-rw-r--r--src/plugins/snippets/src/com/motorola/studio/android/codesnippets/AndroidSnippetsStartup.java338
-rw-r--r--src/plugins/snippets/src/com/motorola/studio/android/codesnippets/AndroidSnippetsTooltip.java147
-rw-r--r--src/plugins/snippets/src/com/motorola/studio/android/codesnippets/CodeSnippetsPlugin.java69
-rw-r--r--src/plugins/snippets/src/com/motorola/studio/android/codesnippets/SnippetsViewContributionItem.java227
-rw-r--r--src/plugins/snippets/src/com/motorola/studio/android/codesnippets/TooltipDisplayConfigContriutionItem.java164
-rw-r--r--src/plugins/snippets/src/com/motorola/studio/android/codesnippets/i18n/AndroidSnippetsNLS.java55
-rw-r--r--src/plugins/snippets/src/com/motorola/studio/android/codesnippets/i18n/androidSnippetsNLS.properties7
-rw-r--r--src/plugins/translation/.classpath9
-rw-r--r--src/plugins/translation/.project28
-rw-r--r--src/plugins/translation/META-INF/MANIFEST.MF26
-rw-r--r--src/plugins/translation/build.properties10
-rw-r--r--src/plugins/translation/commons-net-1.4.1.jarbin0 -> 180792 bytes
-rw-r--r--src/plugins/translation/icons/google-branding.pngbin0 -> 2108 bytes
-rw-r--r--src/plugins/translation/jakarta-oro-2.0.8.jarbin0 -> 65261 bytes
-rw-r--r--src/plugins/translation/plugin.properties6
-rw-r--r--src/plugins/translation/plugin.xml25
-rw-r--r--src/plugins/translation/src/com/motorola/studio/android/json/JSONArray.java92
-rw-r--r--src/plugins/translation/src/com/motorola/studio/android/json/JSONBoolean.java77
-rw-r--r--src/plugins/translation/src/com/motorola/studio/android/json/JSONNull.java58
-rw-r--r--src/plugins/translation/src/com/motorola/studio/android/json/JSONNumber.java65
-rw-r--r--src/plugins/translation/src/com/motorola/studio/android/json/JSONObject.java95
-rw-r--r--src/plugins/translation/src/com/motorola/studio/android/json/JSONPair.java109
-rw-r--r--src/plugins/translation/src/com/motorola/studio/android/json/JSONString.java94
-rw-r--r--src/plugins/translation/src/com/motorola/studio/android/json/JSONValue.java28
-rw-r--r--src/plugins/translation/src/com/motorola/studio/android/json/JSONValueParser.java74
-rw-r--r--src/plugins/translation/src/com/motorola/studio/android/json/Jason.java84
-rw-r--r--src/plugins/translation/src/com/motorola/studio/android/localization/translators/GoogleTranslator.java946
-rw-r--r--src/plugins/translation/src/com/motorola/studio/android/localization/translators/GoogleTranslatorConstants.java115
-rw-r--r--src/plugins/translation/src/com/motorola/studio/android/localization/translators/TranslationPlugin.java77
-rw-r--r--src/plugins/translation/src/com/motorola/studio/android/localization/translators/i18n/TranslateNLS.java58
-rw-r--r--src/plugins/translation/src/com/motorola/studio/android/localization/translators/i18n/translateNLS.properties15
-rw-r--r--src/plugins/translation/src/com/motorola/studio/android/localization/translators/preferences/ui/TranslationPreferencePage.java196
-rw-r--r--src/plugins/videos/.classpath16
-rw-r--r--src/plugins/videos/.project28
-rw-r--r--src/plugins/videos/META-INF/MANIFEST.MF26
-rw-r--r--src/plugins/videos/build.properties20
-rw-r--r--src/plugins/videos/contexts.xml12
-rw-r--r--src/plugins/videos/icons/error_icon.pngbin0 -> 5117 bytes
-rw-r--r--src/plugins/videos/icons/loading_icon.gifbin0 -> 6820 bytes
-rw-r--r--src/plugins/videos/icons/play_icon.pngbin0 -> 2112 bytes
-rw-r--r--src/plugins/videos/icons/preview_not_available.pngbin0 -> 1738 bytes
-rw-r--r--src/plugins/videos/icons/thumbnail_loading.pngbin0 -> 2744 bytes
-rw-r--r--src/plugins/videos/icons/video_view16.pngbin0 -> 1482 bytes
-rw-r--r--src/plugins/videos/libs/gdata/java/deps/google-collect-1.0-rc1.jarbin0 -> 548821 bytes
-rw-r--r--src/plugins/videos/libs/gdata/java/deps/jsr305.jarbin0 -> 33017 bytes
-rw-r--r--src/plugins/videos/libs/gdata/java/lib/gdata-client-1.0.jarbin0 -> 127038 bytes
-rw-r--r--src/plugins/videos/libs/gdata/java/lib/gdata-client-meta-1.0.jarbin0 -> 1421 bytes
-rw-r--r--src/plugins/videos/libs/gdata/java/lib/gdata-core-1.0.jarbin0 -> 1036459 bytes
-rw-r--r--src/plugins/videos/libs/gdata/java/lib/gdata-media-1.0.jarbin0 -> 68781 bytes
-rw-r--r--src/plugins/videos/libs/gdata/java/lib/gdata-youtube-2.0.jarbin0 -> 119923 bytes
-rw-r--r--src/plugins/videos/libs/gdata/java/lib/gdata-youtube-meta-2.0.jarbin0 -> 4775 bytes
-rw-r--r--src/plugins/videos/libs/mail.jarbin0 -> 494975 bytes
-rw-r--r--src/plugins/videos/plugin.properties12
-rw-r--r--src/plugins/videos/plugin.xml22
-rw-r--r--src/plugins/videos/resources/motodev_videos.xml14
-rw-r--r--src/plugins/videos/resources/swfobject.js4
-rw-r--r--src/plugins/videos/resources/watch_video.html31
-rw-r--r--src/plugins/videos/src/com/motorola/studio/android/videos/Activator.java83
-rw-r--r--src/plugins/videos/src/com/motorola/studio/android/videos/i18n/VideosNLS.java73
-rw-r--r--src/plugins/videos/src/com/motorola/studio/android/videos/i18n/videosNLS.properties21
-rw-r--r--src/plugins/videos/src/com/motorola/studio/android/videos/implementation/youtube/YoutubeVideoServiceProvider.java181
-rw-r--r--src/plugins/videos/src/com/motorola/studio/android/videos/model/Video.java190
-rw-r--r--src/plugins/videos/src/com/motorola/studio/android/videos/model/VideoChannel.java121
-rw-r--r--src/plugins/videos/src/com/motorola/studio/android/videos/model/VideoManager.java431
-rw-r--r--src/plugins/videos/src/com/motorola/studio/android/videos/model/extension/VideoServiceProvider.java48
-rw-r--r--src/plugins/videos/src/com/motorola/studio/android/videos/ui/utils/UiUtilities.java289
-rw-r--r--src/plugins/videos/src/com/motorola/studio/android/videos/ui/views/MOTODEVVideosView.java1077
-rw-r--r--src/plugins/videos/src/com/motorola/studio/android/videos/ui/views/VideoComposite.java520
-rw-r--r--src/plugins/videos/src/com/motorola/studio/android/videos/ui/views/VideoPlayerComposite.java260
-rw-r--r--src/plugins/videos/src/com/motorola/studio/android/videos/ui/views/VideosListComposite.java393
1870 files changed, 248057 insertions, 0 deletions
diff --git a/makefile/common.properties b/makefile/common.properties
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/makefile/common.properties
diff --git a/src/features/basic/.project b/src/features/basic/.project
new file mode 100644
index 0000000..0f14b7f
--- /dev/null
+++ b/src/features/basic/.project
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>com.motorola.studio.android.basic.feature</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.pde.FeatureBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.pde.FeatureNature</nature>
+ </natures>
+</projectDescription>
diff --git a/src/features/basic/build.properties b/src/features/basic/build.properties
new file mode 100644
index 0000000..705997e
--- /dev/null
+++ b/src/features/basic/build.properties
@@ -0,0 +1,3 @@
+bin.includes = feature.xml,\
+ feature.properties,\
+ eula.html
diff --git a/src/features/basic/feature.properties b/src/features/basic/feature.properties
new file mode 100644
index 0000000..e4e4060
--- /dev/null
+++ b/src/features/basic/feature.properties
@@ -0,0 +1,14 @@
+#################################################################################
+#
+# MOTODEV ANDROID Feature properties
+#
+#################################################################################
+
+
+feature.android.label = MOTODEV Studio for Android Core Development
+feature.android.provider.name = Motorola Mobility, Inc.
+feature.android.update.site.name = MOTODEV Studio for Android Product Updates
+
+feature.android.description = MOTODEV Studio for Android core features. Contains code development tools/utilities and database support for projects.
+feature.android.copyright = Copyright (C) 2012 The Android Open Source Project
+feature.android.license = place your license text here \ No newline at end of file
diff --git a/src/features/basic/feature.xml b/src/features/basic/feature.xml
new file mode 100644
index 0000000..deda110
--- /dev/null
+++ b/src/features/basic/feature.xml
@@ -0,0 +1,81 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<feature
+ id="com.motorola.studio.android.basic.feature"
+ label="%feature.android.label"
+ version="5.0.0.qualifier"
+ provider-name="%feature.android.provider.name"
+ plugin="com.motorola.studio.android.common"
+ os="linux,macosx,win32"
+ ws="carbon,cocoa,gtk,win32"
+ arch="ppc,x86,x86_64">
+
+ <description>
+ %feature.android.description
+ </description>
+
+ <copyright>
+ %feature.android.copyright
+ </copyright>
+
+ <license url="">
+ %feature.android.license
+ </license>
+
+ <url>
+ <update label="%feature.android.update.site.name" url="https://studio-android.motodevupdate.com/android/4.0/basic/"/>
+ </url>
+
+ <requires>
+ <import feature="com.android.ide.eclipse.adt" version="15.0.0" match="greaterOrEqual"/>
+ <import feature="org.eclipse.datatools.enablement.sqlite.feature" version="1.8.0" match="greaterOrEqual"/>
+ </requires>
+
+ <plugin
+ id="com.motorola.studio.android.common"
+ download-size="0"
+ install-size="0"
+ version="0.0.0"
+ unpack="false"/>
+
+ <plugin
+ id="com.motorola.studio.android.codeutils"
+ download-size="0"
+ install-size="0"
+ version="0.0.0"
+ unpack="false"/>
+
+ <plugin
+ id="com.motorola.studio.android.translation"
+ download-size="0"
+ install-size="0"
+ version="0.0.0"
+ unpack="false"/>
+
+ <plugin
+ id="com.motorola.studio.android.codesnippets"
+ download-size="0"
+ install-size="0"
+ version="0.0.0"
+ unpack="false"/>
+
+ <plugin
+ id="com.motorolamobility.studio.android.db.core"
+ download-size="0"
+ install-size="0"
+ version="0.0.0"
+ unpack="false"/>
+
+ <plugin
+ id="com.motorolamobility.studio.android.certmanager"
+ download-size="0"
+ install-size="0"
+ version="0.0.0"
+ unpack="false"/>
+
+ <plugin
+ id="com.motorolamobility.studio.android.logger"
+ download-size="0"
+ install-size="0"
+ version="0.0.0"/>
+
+</feature>
diff --git a/src/features/feature/.project b/src/features/feature/.project
new file mode 100644
index 0000000..2c4e29d
--- /dev/null
+++ b/src/features/feature/.project
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>com.motorola.studio.android.feature</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.pde.FeatureBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.pde.FeatureNature</nature>
+ </natures>
+</projectDescription>
diff --git a/src/features/feature/build.properties b/src/features/feature/build.properties
new file mode 100644
index 0000000..e99172f
--- /dev/null
+++ b/src/features/feature/build.properties
@@ -0,0 +1,8 @@
+bin.includes = feature.xml,\
+ feature.properties,\
+ eula.html
+
+root = absolute:${MAKEFILE}/legal_files/licenses/
+root.folder.MOTODEV = absolute:${MAKEFILE}/legal_files/motodev_site
+root.linux.gtk.x86 = absolute:file:${GIT_REPOSITORY_PATH}/android/src/plugins/product/res/motodev_icon.xpm
+root.linux.gtk.x86_64 = absolute:file:${GIT_REPOSITORY_PATH}/android/src/plugins/product/res/motodev_icon.xpm \ No newline at end of file
diff --git a/src/features/feature/feature.properties b/src/features/feature/feature.properties
new file mode 100644
index 0000000..c109199
--- /dev/null
+++ b/src/features/feature/feature.properties
@@ -0,0 +1,15 @@
+#################################################################################
+#
+# MOTODEV ANDROID Feature properties
+#
+#################################################################################
+
+
+feature.android.label=MOTODEV Studio for Android Feature
+feature.android.provider.name=Motorola Mobility, Inc.
+feature.android.update.site.name=MOTODEV Studio for Android Product Updates
+
+feature.android.description=MOTODEV Studio for Android Product.\
+\ NOTE: We recommend that you install all MOTODEV Studio for Android components by selecting the top-most check box. This allows all features to update properly when new releases are made available.
+feature.android.copyright=Copyright (C) 2012 The Android Open Source Project
+feature.android.license=place your license text here \ No newline at end of file
diff --git a/src/features/feature/feature.xml b/src/features/feature/feature.xml
new file mode 100644
index 0000000..c348f9d
--- /dev/null
+++ b/src/features/feature/feature.xml
@@ -0,0 +1,206 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<feature
+ id="com.motorola.studio.android.feature"
+ label="%feature.android.label"
+ version="5.0.0.qualifier"
+ provider-name="%feature.android.provider.name"
+ plugin="com.motorola.studio.android.product"
+ os="linux,macosx,win32"
+ ws="carbon,cocoa,gtk,win32"
+ arch="ppc,x86,x86_64">
+
+ <description>
+ %feature.android.description
+ </description>
+
+ <copyright>
+ %feature.android.copyright
+ </copyright>
+
+ <license url="">
+ %feature.android.license
+ </license>
+
+ <url>
+ <update label="MOTODEV Studio for Android Updates" url="https://studio-android.motodevupdate.com/android/4.1/"/>
+ </url>
+
+ <includes
+ id="com.motorola.studio.android.basic.feature"
+ version="0.0.0"/>
+
+ <requires>
+ <import feature="biz.junginger.rss.eclipse.RssPlugin.feature" version="1.0.0" match="equivalent"/>
+ <import feature="com.android.ide.eclipse.adt" version="15.0.0" match="greaterOrEqual"/>
+ <import feature="com.android.ide.eclipse.hierarchyviewer" version="15.0.0" match="greaterOrEqual"/>
+ <import feature="com.android.ide.eclipse.ddms" version="15.0.0" match="greaterOrEqual"/>
+ <import feature="com.android.ide.eclipse.traceview" version="15.0.0" match="greaterOrEqual"/>
+ <import feature="com.motorolamobility.preflighting.ui.feature" version="1.0.0" match="greaterOrEqual"/>
+ <import feature="com.motorolamobility.preflighting.feature" version="1.0.0" match="greaterOrEqual"/>
+ <import feature="org.eclipse.datatools.enablement.sqlite.feature" version="1.8.0" match="greaterOrEqual"/>
+ <import feature="org.eclipse.datatools.common.doc.user" version="1.8.0" match="greaterOrEqual"/>
+ <import feature="org.eclipse.datatools.sqltools.doc.user" version="1.8.0" match="greaterOrEqual"/>
+ <import feature="org.eclipse.datatools.connectivity.doc.user" version="1.8.0" match="greaterOrEqual"/>
+ <import feature="org.eclipse.datatools.doc.user" version="1.8.0" match="greaterOrEqual"/>
+ <import feature="org.eclipse.gef" version="3.6.0" match="greaterOrEqual"/>
+ <import feature="org.eclipse.jdt" version="3.6.0" match="greaterOrEqual"/>
+ <import feature="org.eclipse.mat.feature" version="1.0.0" match="greaterOrEqual"/>
+ <import feature="org.eclipse.mat.chart.feature" version="1.0.0" match="greaterOrEqual"/>
+ <import feature="org.eclipse.sequoyah.device.feature" version="2.0.0" match="greaterOrEqual"/>
+ <import feature="org.eclipse.sequoyah.localization.tools.feature" version="2.1.0" match="greaterOrEqual"/>
+ <import feature="org.eclipse.sequoyah.localization.android.feature" version="2.0.0" match="greaterOrEqual"/>
+ <import feature="org.eclipse.wst.common.fproj" version="3.2.0" match="greaterOrEqual"/>
+ <import feature="org.eclipse.wst.jsdt.feature" version="1.2.0" match="greaterOrEqual"/>
+ <import feature="org.eclipse.wst.server_adapters.feature" version="3.2.0" match="greaterOrEqual"/>
+ <import feature="org.eclipse.wst.web_ui.feature" version="3.2.0" match="greaterOrEqual"/>
+ <import feature="org.eclipse.wst.xml_core.feature" version="3.2.0" match="greaterOrEqual"/>
+ <import feature="org.eclipse.wst.xml_ui.feature" version="3.2.0" match="greaterOrEqual"/>
+ <import feature="org.eclipse.wst.xsl.feature" version="1.1.0" match="greaterOrEqual"/>
+ <import feature="org.eclipse.platform" version="3.6.0" match="greaterOrEqual"/>
+ <import feature="net.certiv.proguarddt.feature" version="0.8.0" match="greaterOrEqual"/>
+ <import feature="org.eclipse.epp.mpc" version="1.1.1" match="greaterOrEqual"/>
+ </requires>
+
+ <plugin
+ id="com.motorola.studio.android"
+ download-size="0"
+ install-size="0"
+ version="0.0.0"
+ unpack="false"/>
+
+ <plugin
+ id="com.motorola.studio.android.emulator"
+ download-size="0"
+ install-size="0"
+ version="0.0.0"
+ unpack="false"/>
+
+ <plugin
+ id="com.motorola.studio.android.launch"
+ download-size="0"
+ install-size="0"
+ version="0.0.0"
+ unpack="false"/>
+
+ <plugin
+ id="com.motorola.studio.android.packaging.ui"
+ download-size="0"
+ install-size="0"
+ version="0.0.0"
+ unpack="false"/>
+
+ <plugin
+ id="com.motorola.studio.android.handset"
+ download-size="0"
+ install-size="0"
+ version="0.0.0"
+ unpack="false"/>
+
+ <plugin
+ id="com.motorola.studio.android.tooldocs.studio.helpbase"
+ download-size="0"
+ install-size="0"
+ version="0.0.0"
+ unpack="false"/>
+
+ <plugin
+ id="com.motorola.studio.android.remote"
+ download-size="0"
+ install-size="0"
+ version="0.0.0"
+ unpack="false"/>
+
+ <plugin
+ id="com.motorola.studio.android.macosx"
+ os="macosx"
+ download-size="0"
+ install-size="0"
+ version="0.0.0"
+ fragment="true"
+ unpack="false"/>
+
+ <plugin
+ id="com.motorola.studio.android.linux.x86"
+ os="linux"
+ arch="x86"
+ download-size="0"
+ install-size="0"
+ version="0.0.0"
+ fragment="true"
+ unpack="false"/>
+
+ <plugin
+ id="com.motorola.studio.android.linux.x86_64"
+ os="linux"
+ arch="x86_64"
+ download-size="0"
+ install-size="0"
+ version="0.0.0"
+ fragment="true"
+ unpack="false"/>
+
+ <plugin
+ id="com.motorola.studio.android.win32.x86"
+ os="win32"
+ ws="win32"
+ arch="x86"
+ download-size="0"
+ install-size="0"
+ version="0.0.0"
+ fragment="true"
+ unpack="false"/>
+
+ <plugin
+ id="com.motorola.studio.android.win32.x86_64"
+ os="win32"
+ ws="win32"
+ arch="x86_64"
+ download-size="0"
+ install-size="0"
+ version="0.0.0"
+ fragment="true"
+ unpack="false"/>
+
+ <plugin
+ id="com.motorola.studio.android.mat"
+ download-size="0"
+ install-size="0"
+ version="0.0.0"
+ unpack="false"/>
+
+ <plugin
+ id="com.motorola.studio.android.devices.services"
+ download-size="0"
+ install-size="0"
+ version="0.0.0"
+ unpack="false"/>
+
+ <plugin
+ id="com.motorola.studio.android.videos"
+ download-size="0"
+ install-size="0"
+ version="0.0.0"
+ unpack="false"/>
+
+ <plugin
+ id="com.motorolamobility.studio.android.db.devices"
+ download-size="0"
+ install-size="0"
+ version="0.0.0"
+ unpack="false"/>
+
+ <plugin
+ id="com.motorola.studio.android.installer"
+ download-size="0"
+ install-size="0"
+ version="0.0.0"
+ unpack="false"/>
+
+ <plugin
+ id="com.motorolamobility.studio.android.logger.collector"
+ download-size="0"
+ install-size="0"
+ version="0.0.0"
+ unpack="false"/>
+
+</feature>
diff --git a/src/features/feature/p2.inf b/src/features/feature/p2.inf
new file mode 100644
index 0000000..feb4ba9
--- /dev/null
+++ b/src/features/feature/p2.inf
@@ -0,0 +1 @@
+instructions.configure=org.eclipse.equinox.p2.touchpoint.eclipse.removeProgramArg(programArg:--launcher.XXMaxPermSize);org.eclipse.equinox.p2.touchpoint.eclipse.removeProgramArg(programArg:256m);org.eclipse.equinox.p2.touchpoint.eclipse.addProgramArg(programArg:--launcher.XXMaxPermSize);org.eclipse.equinox.p2.touchpoint.eclipse.addProgramArg(programArg:512m);org.eclipse.equinox.p2.touchpoint.eclipse.removeJvmArg(jvmArg:-Xmx256m);org.eclipse.equinox.p2.touchpoint.eclipse.addJvmArg(jvmArg:-Xmx512m);org.eclipse.equinox.p2.touchpoint.natives.mkdir(path:${installFolder}/LanguagePacks); \ No newline at end of file
diff --git a/src/features/preflighting.sdk/.project b/src/features/preflighting.sdk/.project
new file mode 100644
index 0000000..1683e54
--- /dev/null
+++ b/src/features/preflighting.sdk/.project
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>com.motorolamobility.preflighting.sdk.feature</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.pde.FeatureBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.pde.FeatureNature</nature>
+ </natures>
+</projectDescription>
diff --git a/src/features/preflighting.sdk/build.properties b/src/features/preflighting.sdk/build.properties
new file mode 100644
index 0000000..2a0da75
--- /dev/null
+++ b/src/features/preflighting.sdk/build.properties
@@ -0,0 +1,3 @@
+bin.includes = feature.xml,\
+ feature.properties,\
+ eula.html \ No newline at end of file
diff --git a/src/features/preflighting.sdk/feature.properties b/src/features/preflighting.sdk/feature.properties
new file mode 100644
index 0000000..15b57d5
--- /dev/null
+++ b/src/features/preflighting.sdk/feature.properties
@@ -0,0 +1,13 @@
+#################################################################################
+#
+# MOTODEV Preflighting Feature properties
+#
+#################################################################################
+
+
+feature.preflighting.label= MOTODEV Studio App Validator SDK
+feature.preflighting.provider.name=Motorola Mobility, Inc.
+feature.preflighting.description=MOTODEV Studio App Validator SDK.\
+\ NOTE: We recommend that you install all MOTODEV Studio for Android components by selecting the top-most check box. This allows all features to update properly when new releases are made available.
+feature.preflighting.copyright=Copyright (C) 2012 The Android Open Source Project
+feature.preflighting.license=place your license text here \ No newline at end of file
diff --git a/src/features/preflighting.sdk/feature.xml b/src/features/preflighting.sdk/feature.xml
new file mode 100644
index 0000000..3e37553
--- /dev/null
+++ b/src/features/preflighting.sdk/feature.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<feature
+ id="com.motorolamobility.preflighting.sdk.feature"
+ label="%feature.preflighting.label"
+ version="2.0.0.qualifier"
+ provider-name="%feature.preflighting.provider.name"
+ plugin="com.motorolamobility.preflighting.sdk">
+
+ <description>
+ %feature.preflighting.description
+ </description>
+
+ <copyright>
+ %feature.preflighting.copyright
+ </copyright>
+
+ <license url="">
+ %feature.preflighting.license
+ </license>
+
+ <requires>
+ <import feature="org.eclipse.pde" version="3.6.0" match="greaterOrEqual"/>
+ </requires>
+
+ <plugin
+ id="com.motorolamobility.preflighting.sdk"
+ download-size="0"
+ install-size="0"
+ version="0.0.0"
+ unpack="false"/>
+
+</feature>
diff --git a/src/features/preflighting.ui/.project b/src/features/preflighting.ui/.project
new file mode 100644
index 0000000..2ee5ada
--- /dev/null
+++ b/src/features/preflighting.ui/.project
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>com.motorolamobility.preflighting.ui.feature</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.pde.FeatureBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.pde.FeatureNature</nature>
+ </natures>
+</projectDescription>
diff --git a/src/features/preflighting.ui/build.properties b/src/features/preflighting.ui/build.properties
new file mode 100644
index 0000000..705997e
--- /dev/null
+++ b/src/features/preflighting.ui/build.properties
@@ -0,0 +1,3 @@
+bin.includes = feature.xml,\
+ feature.properties,\
+ eula.html
diff --git a/src/features/preflighting.ui/feature.properties b/src/features/preflighting.ui/feature.properties
new file mode 100644
index 0000000..c854455
--- /dev/null
+++ b/src/features/preflighting.ui/feature.properties
@@ -0,0 +1,13 @@
+#################################################################################
+#
+# MOTODEV Preflighting Feature properties
+#
+#################################################################################
+
+
+feature.preflighting.label= MOTODEV Studio App Validator Tool UI Integration
+feature.preflighting.provider.name=Motorola Mobility, Inc.
+feature.preflighting.description=MOTODEV Studio App Validator Tool UI Integration.\
+\ NOTE: We recommend that you install all MOTODEV Studio for Android components by selecting the top-most check box. This allows all features to update properly when new releases are made available.
+feature.preflighting.copyright=Copyright (C) 2012 The Android Open Source Project
+feature.preflighting.license=Place your license text here \ No newline at end of file
diff --git a/src/features/preflighting.ui/feature.xml b/src/features/preflighting.ui/feature.xml
new file mode 100644
index 0000000..624b966
--- /dev/null
+++ b/src/features/preflighting.ui/feature.xml
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<feature
+ id="com.motorolamobility.preflighting.ui.feature"
+ label="%feature.preflighting.label"
+ version="2.0.0.qualifier"
+ provider-name="%feature.preflighting.provider.name"
+ plugin="com.motorolamobility.preflighting.ui">
+
+ <description>
+ %feature.preflighting.description
+ </description>
+
+ <copyright>
+ %feature.preflighting.copyright
+ </copyright>
+
+ <license url="">
+ %feature.preflighting.license
+ </license>
+
+ <requires>
+ <import feature="com.motorolamobility.preflighting.feature" version="1.0.0" match="greaterOrEqual"/>
+ <import feature="com.motorolamobility.preflighting.sdk.feature" version="1.0.0" match="greaterOrEqual"/>
+ <import feature="com.android.ide.eclipse.adt" version="15.0.0" match="greaterOrEqual"/>
+ <import plugin="com.motorola.studio.android.common" version="4.0.0" match="greaterOrEqual"/>
+ <import plugin="com.motorolamobility.studio.android.logger" version="4.1.0" match="greaterOrEqual"/>
+ </requires>
+
+ <plugin
+ id="com.motorolamobility.preflighting.ui"
+ download-size="0"
+ install-size="0"
+ version="0.0.0"
+ unpack="false"/>
+
+ <plugin
+ id="com.motorolamobility.preflighting.tooldocs.validator.helpbase"
+ download-size="0"
+ install-size="0"
+ version="0.0.0"
+ unpack="false"/>
+
+ <plugin
+ id="com.motorola.studio.android.common"
+ download-size="0"
+ install-size="0"
+ version="0.0.0"
+ unpack="false"/>
+
+ <plugin
+ id="com.motorolamobility.preflighting.checkers.ui"
+ download-size="0"
+ install-size="0"
+ version="0.0.0"
+ unpack="false"/>
+
+ <plugin
+ id="com.motorolamobility.preflighting.samplechecker.findviewbyid.ui"
+ download-size="0"
+ install-size="0"
+ version="0.0.0"
+ unpack="false"/>
+
+ <plugin
+ id="com.motorolamobility.studio.android.logger"
+ download-size="0"
+ install-size="0"
+ version="0.0.0"/>
+
+</feature>
diff --git a/src/features/preflighting/.project b/src/features/preflighting/.project
new file mode 100644
index 0000000..0e4d842
--- /dev/null
+++ b/src/features/preflighting/.project
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>com.motorolamobility.preflighting.feature</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.pde.FeatureBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.pde.FeatureNature</nature>
+ </natures>
+</projectDescription>
diff --git a/src/features/preflighting/build.properties b/src/features/preflighting/build.properties
new file mode 100644
index 0000000..c4e9243
--- /dev/null
+++ b/src/features/preflighting/build.properties
@@ -0,0 +1,16 @@
+bin.includes = feature.xml,\
+ feature.properties,\
+ eula.html
+
+root = absolute:${GIT_REPOSITORY_PATH}/android/makefile/legal_files/appValidator_licenses/
+root.folder.devices = absolute:${GIT_REPOSITORY_PATH}/android/src/plugins/preflighting.core/devices/
+root.win32.win32.x86 = absolute:${GIT_REPOSITORY_PATH}/android/makefile/preflighting/win32,absolute:file:${GIT_REPOSITORY_PATH}/android/src/plugins/preflighting.core/configuration_files/appvalidator.cfg
+root.win32.win32.x86_64 = absolute:${GIT_REPOSITORY_PATH}/android/makefile/preflighting/win32,absolute:file:${GIT_REPOSITORY_PATH}/android/src/plugins/preflighting.core/configuration_files/appvalidator.cfg
+root.linux.gtk.x86 = absolute:${GIT_REPOSITORY_PATH}/android/makefile/preflighting/linux,absolute:file:${GIT_REPOSITORY_PATH}/android/src/plugins/preflighting.core/configuration_files/appvalidator.cfg
+root.linux.gtk.x86_64 = absolute:${GIT_REPOSITORY_PATH}/android/makefile/preflighting/linux,absolute:file:${GIT_REPOSITORY_PATH}/android/src/plugins/preflighting.core/configuration_files/appvalidator.cfg
+root.macosx.cocoa.x86 = absolute:${GIT_REPOSITORY_PATH}/android/makefile/preflighting/macosx,absolute:file:${GIT_REPOSITORY_PATH}/android/src/plugins/preflighting.core/configuration_files/macosx_x86/appvalidator.cfg
+root.macosx.cocoa.x86_64 = absolute:${GIT_REPOSITORY_PATH}/android/makefile/preflighting/macosx,absolute:file:${GIT_REPOSITORY_PATH}/android/src/plugins/preflighting.core/configuration_files/appvalidator.cfg
+root.linux.gtk.x86.permissions.755 = appvalidator.sh
+root.linux.gtk.x86_64.permissions.755 = appvalidator.sh
+root.macosx.cocoa.x86.permissions.755 = appvalidator.sh
+root.macosx.cocoa.x86_64.permissions.755 = appvalidator.sh \ No newline at end of file
diff --git a/src/features/preflighting/feature.properties b/src/features/preflighting/feature.properties
new file mode 100644
index 0000000..a97c6f3
--- /dev/null
+++ b/src/features/preflighting/feature.properties
@@ -0,0 +1,14 @@
+#################################################################################
+#
+# MOTODEV Preflighting Feature properties
+#
+#################################################################################
+
+
+feature.preflighting.label= MOTODEV Studio App Validator Tool
+feature.preflighting.provider.name=Motorola Mobility, Inc.
+
+feature.preflighting.description=MOTODEV Studio App Validator Tool.\
+\ NOTE: We recommend that you install all MOTODEV Studio for Android components by selecting the top-most check box. This allows all features to update properly when new releases are made available.
+feature.preflighting.copyright=Copyright (C) 2012 The Android Open Source Project
+feature.preflighting.license=place your license text here \ No newline at end of file
diff --git a/src/features/preflighting/feature.xml b/src/features/preflighting/feature.xml
new file mode 100644
index 0000000..c247663
--- /dev/null
+++ b/src/features/preflighting/feature.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<feature
+ id="com.motorolamobility.preflighting.feature"
+ label="%feature.preflighting.label"
+ version="2.0.0.qualifier"
+ provider-name="%feature.preflighting.provider.name"
+ plugin="com.motorolamobility.preflighting.core">
+
+ <description>
+ %feature.preflighting.description
+ </description>
+
+ <copyright>
+ %feature.preflighting.copyright
+ </copyright>
+
+ <license url="">
+ %feature.preflighting.license
+ </license>
+
+ <plugin
+ id="com.motorolamobility.preflighting"
+ download-size="0"
+ install-size="0"
+ version="0.0.0"
+ unpack="false"/>
+
+ <plugin
+ id="com.motorolamobility.preflighting.core"
+ download-size="0"
+ install-size="0"
+ version="0.0.0"
+ unpack="false"/>
+
+ <plugin
+ id="com.motorolamobility.preflighting.checkers"
+ download-size="0"
+ install-size="0"
+ version="0.0.0"
+ unpack="false"/>
+
+ <plugin
+ id="com.motorolamobility.preflighting.samplechecker.androidlabel"
+ download-size="0"
+ install-size="0"
+ version="0.0.0"
+ unpack="false"/>
+
+ <plugin
+ id="com.motorolamobility.preflighting.samplechecker.findviewbyid"
+ download-size="0"
+ install-size="0"
+ version="0.0.0"
+ unpack="false"/>
+
+</feature>
diff --git a/src/help/appvalidator_help/ReadMe.txt b/src/help/appvalidator_help/ReadMe.txt
new file mode 100644
index 0000000..d9a854b
--- /dev/null
+++ b/src/help/appvalidator_help/ReadMe.txt
@@ -0,0 +1,13 @@
+Setting up to build:
+1. Obtain the DITA Open Toolkit from http://dita-ot.sourceforge.net. Install it into the tools folder. Note that there is already a ditaot plugin in this folder; that plugin needs to reside in the plugins folder within your ditaot installation.
+2. Obtain DITAReports from http://dita-ot.sourceforge.net/doc/ot-userguide131/ditaotug131-18042007-tools.zip. Install it so that you wind up with a ditareports folder inside tools (parallel to the ditaot folder).
+3. Obtain Linklint from http://www.linklint.org and install it into the tools folder, so that you wind up with a linklint folder inside tools. You should now have three folders inside the tools folder: "ditaot", "ditareports", and "linklint", and within the plugins folder inside ditaot you should see cshelp (part of ditaot) and com.mot.mdb.deved.xhtml. If the cshelp plugin isn't there, you'll need to obtain it from http://dita-ot.sourceforge.net and install it separately.
+
+Instructions for building the online help on Mac OS X:
+
+1. In Finder, double-click docs_dita/studio_help/tools/ditaot/startcmd_mac.command. The terminal window will open and the environment will be set up.
+2. In the terminal window, do:
+ cd ../../etc/buildfiles/android_appvalidator-help
+ ant all -f build.xml
+
+There are Windows batch files (in tools/ditaot) that can be used in place of step 1 on a Windows machine.
diff --git a/src/help/appvalidator_help/etc/buildfiles/android_appvalidator-help/build.properties b/src/help/appvalidator_help/etc/buildfiles/android_appvalidator-help/build.properties
new file mode 100644
index 0000000..dc4e940
--- /dev/null
+++ b/src/help/appvalidator_help/etc/buildfiles/android_appvalidator-help/build.properties
@@ -0,0 +1,6 @@
+# Set version number for all plug-ins
+plugin-version=1.1.0.release
+# Set build number for all plug-in zip files
+build-number=01.01.00i_b001
+# Generate a ditamap debugging report
+report=true
diff --git a/src/help/appvalidator_help/etc/buildfiles/android_appvalidator-help/build.xml b/src/help/appvalidator_help/etc/buildfiles/android_appvalidator-help/build.xml
new file mode 100644
index 0000000..fb9ec59
--- /dev/null
+++ b/src/help/appvalidator_help/etc/buildfiles/android_appvalidator-help/build.xml
@@ -0,0 +1,66 @@
+<?xml version="1.0" encoding="utf-8"?>
+<project name="android_appvalidator-help" default="all" basedir=".">
+
+ <!--Properties unique to this build (others are specified in ../build_lib.xml)-->
+
+ <loadproperties srcfile="build.properties"/>
+
+ <!--Location of output folder where each plug-in's folder goes-->
+ <property name="out-projdir" value="${basedir}/../../../out/android_appvalidator-help"/>
+ <property name="out-basedir" value="${out-projdir}/product"/>
+ <property name="out-jardir" value="${out-basedir}/jars"/>
+ <!--Name of plug-in's TOC file (without extension)-->
+ <property name="toc-filenoext" value="main"/>
+ <!--Path of file containing context extension points for context-sensitive help-->
+ <property name="context-file" value="${basedir}/context_extension_points.xml"/>
+ <property name="report" value="false"/>
+
+ <import file="../build_lib.xml"/>
+
+ <target name="all" depends="plugins"/>
+
+ <target name="plugins" depends="clean" description="Build App Validator help plug-ins.">
+
+ <mkdir dir="${out-jardir}"/>
+
+ <!-- Call build-topics-plugin macro to build help for each ditamap. -->
+ <build-topics-plugin map="validation_editor.ditamap" ditaval="${basedir}/filter_appvalidator.ditaval"
+ hdr="${basedir}/../../resources/hdr-none.xml">
+ <build-cshelp cshelp-topic="${src-dir}/topics/cs_app-validator.dita"
+ context-out-dir="${@{map}-plugin-temp-dir}/contexts" ditaval="${basedir}/filter_appvalidator.ditaval"
+ context-temp-dir="${@{map}-plugin-temp-dir}_contexts"
+ plugin-out-dir="${out-basedir}/${@{map}-plugin-id}_${plugin-version}"/>
+ <zip destfile="${out-jardir}/${@{map}-plugin-id}_${plugin-version}.jar"
+ basedir="${out-basedir}/${@{map}-plugin-id}_${plugin-version}"/>
+ <report map="@{map}"/>
+ <checklinks dir="${out-basedir}/${@{map}-plugin-id}_${plugin-version}" map="@{map}"/>
+ <!-- Create zip file from jar so zip filename includes build number -->
+ <zip destfile="${out-jardir}/android_appvalidator-help_${build-number}.zip">
+ <fileset dir="${out-jardir}" includes="${@{map}-plugin-id}_${plugin-version}.jar"/>
+ </zip>
+ </build-topics-plugin>
+
+ </target>
+
+
+ <target name="releasenotes">
+ <property name="mapfile" value="android_studio-release-notes.ditamap"/>
+ <basename property="mapname" file="${mapfile}" suffix=".ditamap"/>
+ <property name="relnote-out-dir" value="${out-projdir}/web/${mapname}"/>
+ <delete dir="${relnote-out-dir}" quiet="yes"/>
+ <!-- Call DITA-OT build script-->
+ <ant antfile="${dita.dir}/build.xml" target="init" description="Call DITA OT build script">
+ <property name="transtype" value="xhtml"/>
+ <property name="args.input" value="${src-dir}/${mapfile}"/>
+ <property name="dita.input.valfile" value="${basedir}/filter_appvalidator.ditaval"/>
+ <property name="output.dir" value="${relnote-out-dir}"/>
+ <property name="dita.temp.dir" value="${temp-dir}/${mapname}"/>
+ <property name="args.logdir" value="${log-dir}"/>
+ </ant>
+
+ <deleteflagimages output-dir="${relnote-out-dir}"/>
+ <replacecss output-dir="${relnote-out-dir}"/>
+
+ </target>
+
+</project>
diff --git a/src/help/appvalidator_help/etc/buildfiles/android_appvalidator-help/context_extension_points.xml b/src/help/appvalidator_help/etc/buildfiles/android_appvalidator-help/context_extension_points.xml
new file mode 100644
index 0000000..ce1f058
--- /dev/null
+++ b/src/help/appvalidator_help/etc/buildfiles/android_appvalidator-help/context_extension_points.xml
@@ -0,0 +1,5 @@
+
+<!--Context extension point for all Sequoyah context-sensitive help-->
+<extension point="org.eclipse.help.contexts">
+ <contexts file="cs_app-validator.xml" plugin="com.motorolamobility.preflighting.ui"/>
+</extension>
diff --git a/src/help/appvalidator_help/etc/buildfiles/android_appvalidator-help/enable-win32.xml b/src/help/appvalidator_help/etc/buildfiles/android_appvalidator-help/enable-win32.xml
new file mode 100644
index 0000000..980649c
--- /dev/null
+++ b/src/help/appvalidator_help/etc/buildfiles/android_appvalidator-help/enable-win32.xml
@@ -0,0 +1,6 @@
+
+ <enablement>
+ <or>
+ <systemTest property="osgi.os" value="win32"/>
+ </or>
+ </enablement> \ No newline at end of file
diff --git a/src/help/appvalidator_help/etc/buildfiles/android_appvalidator-help/filter_appvalidator.ditaval b/src/help/appvalidator_help/etc/buildfiles/android_appvalidator-help/filter_appvalidator.ditaval
new file mode 100644
index 0000000..01cf654
--- /dev/null
+++ b/src/help/appvalidator_help/etc/buildfiles/android_appvalidator-help/filter_appvalidator.ditaval
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Filter file for App Validator help. -->
+<val>
+ <prop att="product" val="android-studio" action="include"/>
+ <prop att="product" val="javame-studio" action="exclude"/>
+ <prop att="product" val="javame-sdk" action="exclude"/>
+ <prop att="product" val="webui" action="exclude"/>
+</val>
diff --git a/src/help/appvalidator_help/etc/buildfiles/build_lib.xml b/src/help/appvalidator_help/etc/buildfiles/build_lib.xml
new file mode 100644
index 0000000..4c6efbb
--- /dev/null
+++ b/src/help/appvalidator_help/etc/buildfiles/build_lib.xml
@@ -0,0 +1,223 @@
+<?xml version="1.0" encoding="utf-8"?>
+<project name="lib" basedir=".">
+
+ <taskdef resource="net/sf/antcontrib/antlib.xml"/>
+
+ <!--Location of DITA Open Toolkit-->
+ <property name="dita.dir" value="${basedir}/../../../tools/ditaot"/>
+ <!--Location of DITA source files to build-->
+ <property name="src-dir" value="${basedir}/../../../src"/>
+ <!--Location of temp folder-->
+ <property name="temp-dir" value="${basedir}/temp"/>
+ <!--Location of logs folder-->
+ <property name="log-dir" value="${basedir}/logs"/>
+ <!--Path of file containing context extension points for context-sensitive help-->
+ <property name="context-file" value="${basedir}/context_extension_points.xml"/>
+ <!--If present, load file containing Eclipse enablement elements for Windows-only content.-->
+ <available file="enable-win32.xml" property="enablement-present"/>
+ <if>
+ <isset property="enablement-present"/>
+ <then>
+ <loadfile property="enablement-win32" srcfile="enable-win32.xml"/>
+ </then>
+ </if>
+
+ <!-- References ANT extension that provides Perl 5 compatible regex functionality.-->
+ <property name="ant.regexp.regexpimpl" value="org.apache.tools.ant.util.regexp.JakartaOroRegexp"/>
+
+ <!--Import DITA OT build file-->
+ <import file="${dita.dir}/build.xml"/>
+
+ <target name="clean" description="Delete all generated files (out, temp, and logs folders)">
+ <delete dir="${out-projdir}" quiet="yes"/>
+ <delete dir="${log-dir}" quiet="yes"/>
+ <delete dir="${temp-dir}" quiet="yes"/>
+ <delete file="${basedir}/ditaot_batch.log"/>
+ </target>
+
+ <macrodef name="build-topics-plugin" description="Build a DevEd Eclipse help plug-in">
+ <attribute name="map" default="NOT_SET"/>
+ <attribute name="ditaval" default="NOT_SET"/>
+ <attribute name="hdr" default="NOT_SET"/>
+ <attribute name="provider" default="Motorola Mobility, Inc."/>
+ <element name="build-callers-tasks" implicit="yes" optional="yes"/>
+ <sequential>
+ <echo/>
+ <echo>=============================================================================</echo>
+ <echo>START build-topics-plugin for @{map}</echo>
+ <echo>Ditaval file: "@{ditaval}"</echo>
+ <property name="@{map}-path" value="${src-dir}/@{map}"/>
+ <!-- Reads ditamap into ANT XML properties so that build script can use same map ID as plug-in ID everywhere. -->
+ <xmlproperty file="${@{map}-path}" prefix="@{map}"/>
+ <property name="@{map}-plugin-id" value="${@{map}.map(id)}"/>
+ <echo>Plug-in ID: ${@{map}-plugin-id}</echo>
+ <property name="@{map}-plugin-out-dir"
+ value="${out-basedir}/${@{map}-plugin-id}_${plugin-version}"/>
+ <property name="@{map}-plugin-temp-dir"
+ value="${temp-dir}/${@{map}-plugin-id}_${plugin-version}"/>
+ <!-- Get copyright info from map file -->
+ <property name="@{map}-copyryear" value="${@{map}.map.topicmeta.copyright.copyryear(year)}"/>
+ <property name="@{map}-copyrholder" value="${@{map}.map.topicmeta.copyright.copyrholder}"/>
+ <echo>Copyright date: ${@{map}-copyryear}</echo>
+ <echo>Copyright holder: ${@{map}-copyrholder}</echo>
+ <!--Delete output folder before building so no unnecessary files from previous build are left.-->
+ <delete dir="${@{map}-plugin-out-dir}" quiet="yes"/>
+
+ <!-- Call DITA-OT build script-->
+ <ant antfile="${dita.dir}/build.xml" target="init" description="Call DITA OT build script">
+ <property name="transtype" value="eclipsehelp"/>
+ <property name="args.input" value="${@{map}-path}"/>
+ <property name="dita.input.valfile" value="@{ditaval}"/>
+ <property name="args.hdr" value="@{hdr}"/>
+ <property name="output.dir" value="${@{map}-plugin-out-dir}"/>
+ <property name="dita.temp.dir" value="${@{map}-plugin-temp-dir}"/>
+ <property name="args.logdir" value="${log-dir}"/>
+ <!-- Sets "Bundle-Version" in meta-mf/manifest.mf, the version of this help plug-in. -->
+ <property name="args.eclipse.version" value="${plugin-version}"/>
+ <property name="args.eclipse.provider" value="@{provider}"/>
+ <property name="args.eclipsehelp.toc" value="${toc-filenoext}"/>
+ </ant>
+
+ <!--Change filename of TOC file from ditamapname.xml to specified.-->
+ <basename property="@{map}-filenoext" file="@{map}" suffix=".ditamap"/>
+ <move file="${@{map}-plugin-out-dir}/${@{map}-filenoext}.xml"
+ tofile="${@{map}-plugin-out-dir}/${toc-filenoext}.xml"/>
+
+ <deleteflagimages output-dir="${@{map}-plugin-out-dir}"/>
+ <replacecss output-dir="${@{map}-plugin-out-dir}"/>
+
+ <!--Replace special anchors in TOC with Eclipse enablement elements-->
+ <!--Set anchor id="eclipse-enable-win32onlyXXX" for topics that should appear in TOC only in Windows.-->
+ <!--XXX can be anything (required only to make anchor id unique in ditamap file.-->
+ <if>
+ <isset property="enablement-present"/>
+ <then>
+ <replaceregexp file="${@{map}-plugin-out-dir}/${toc-filenoext}.xml"
+ match="\s*?&lt;anchor\s+id\s*?=\s*?&quot;eclipse-enable-win32only.*?&quot;\s*?/&gt;"
+ replace="${enablement-win32}" flags="gi"/>
+ </then>
+ </if>
+
+ <!--Move debugging files to temp folder.-->
+ <!--<move file="${@{map}-plugin-out-dir}/dita.list" todir="${@{map}-plugin-temp-dir}"/>
+ <move file="${@{map}-plugin-out-dir}/dita.xml.properties" todir="${@{map}-plugin-temp-dir}"/>-->
+
+ <!--Perform caller's implicit tasks here-->
+ <build-callers-tasks/>
+
+ <echo>END build-topics-plugin for @{map}</echo>
+ </sequential>
+ </macrodef>
+
+
+ <macrodef name="build-cshelp" description="Build cshelp for a DevEd Eclipse help plug-in">
+ <attribute name="cshelp-topic" default="NOT_SET"/>
+ <attribute name="ditaval" default="NOT_SET"/>
+ <attribute name="context-out-dir" default="NOT_SET"/>
+ <attribute name="context-temp-dir" default="NOT_SET"/>
+ <attribute name="plugin-out-dir" default="NOT_SET"/>
+ <element name="build-callers-tasks" implicit="yes" optional="yes"/>
+ <sequential>
+ <basename property="@{cshelp-topic}-filenoext" file="@{cshelp-topic}" suffix=".dita"/>
+ <echo/>
+ <echo>======================================================</echo>
+ <echo>START build-cshelp for ${@{cshelp-topic}-filenoext}</echo>
+ <!-- Call DITA-OT build script to build cshelp topics using special XSL-->
+ <ant antfile="${dita.dir}/build.xml" target="init">
+ <property name="args.input" value="@{cshelp-topic}"/>
+ <property name="dita.input.valfile" value="@{ditaval}"/>
+ <property name="output.dir" value="@{context-out-dir}"/>
+ <property name="dita.temp.dir" value="@{context-temp-dir}"/>
+ <property name="args.xsl" value="${dita.dir}/plugins/cshelp/xsl/dit2context.xsl"/>
+ <property name="dita.extname" value=".dita"/>
+ <property name="transtype" value="xhtml"/>
+ <property name="args.outext" value="xml"/>
+ </ant>
+ <!-- Add "topics/" to start of each href so that contexts link to topics in "topics" subfolder-->
+ <replaceregexp file="@{context-out-dir}/${@{cshelp-topic}-filenoext}.xml"
+ match="&lt;topic(\s+)href=&#34;" replace="&lt;topic\1href=&#34;topics/"
+ byline="true"/>
+ <!-- Move context XML file to this plug-in's output folder-->
+ <move file="@{context-out-dir}/${@{cshelp-topic}-filenoext}.xml" todir="@{plugin-out-dir}"/>
+ <!--Load context ext points file, then add it to the end of the plugin.xml file-->
+ <loadfile property="context-ext-pts" srcfile="${context-file}"/>
+ <replaceregexp file="@{plugin-out-dir}/plugin.xml" match="&lt;/plugin&gt;"
+ replace="${context-ext-pts}&lt;/plugin&gt;" byline="yes"/>
+
+ <!--Perform caller's implicit tasks here-->
+ <build-callers-tasks/>
+
+ <echo>END build-cshelp for ${@{cshelp-topic}-filenoext}</echo>
+ </sequential>
+ </macrodef>
+
+ <macrodef name="report" description="Generate debugging report for a ditamap">
+ <attribute name="map" default="NOT_SET"/>
+ <sequential>
+ <if>
+ <istrue value="${report}"/>
+ <then>
+ <exec dir="${basedir}" executable="php" output="${log-dir}/@{map}_debug-report.txt"
+ os="Mac OS X" failifexecutionfails="false" logerror="true">
+ <arg file="${dita.dir}/../ditareports/src/ditadebug.php"/>
+ <arg file="${src-dir}/@{map}"/>
+ </exec>
+ </then>
+ </if>
+ </sequential>
+ </macrodef>
+
+ <macrodef name="checklinks" description="Check links in HTML output and generate report">
+ <attribute name="dir" default="NOT_SET"/>
+ <attribute name="map" default="NOT_SET"/>
+ <sequential>
+ <!--Run linklint if report="true" and if OS is Mac or a Unix-like OS, which ship with Perl.-->
+ <if>
+ <and>
+ <istrue value="${report}"/>
+ <or>
+ <os family="mac"/>
+ <os family="unix"/>
+ </or>
+ </and>
+ <then>
+ <echo/>
+ <echo>------------------------------------------------------</echo>
+ <echo message="linklint: checking links in output"/>
+ <exec dir="${basedir}" executable="perl" os="Mac OS X" failifexecutionfails="false"
+ logerror="true">
+ <arg file="${dita.dir}/../linklint/src/linklint"/>
+ <arg value="-root"/>
+ <arg path="@{dir}"/>
+ <arg line="/@ -no_warn_index -htmlonly -quiet"/>
+ <arg value="-doc"/>
+ <arg path="${log-dir}/@{map}_link-report"/>
+ </exec>
+ <echo/>
+ <echo>See index.html in logs directory for full link report: ${log-dir}/@{map}_link-report</echo>
+ <echo>------------------------------------------------------</echo>
+ <echo/>
+ </then>
+ </if>
+ </sequential>
+ </macrodef>
+
+ <macrodef name="deleteflagimages" description="Delete flagging images inserted by DITA-OT">
+ <!--Delete flagging images inserted by DITA-OT because a ditaval file is specified-->
+ <attribute name="output-dir" default="NOT_SET"/>
+ <sequential>
+ <delete file="@{output-dir}/delta.gif"/>
+ <delete file="@{output-dir}/deltaend.gif"/>
+ </sequential>
+ </macrodef>
+
+ <macrodef name="replacecss" description="Replace default CSS">
+ <!--Delete extraneous CSS from output folder and copy Motorola Dev Ed CSS to output folder..-->
+ <attribute name="output-dir" default="NOT_SET"/>
+ <sequential>
+ <delete file="@{output-dir}/commonrtl.css"/>
+ <copy file="${basedir}/../../resources/commonltr.css" todir="@{output-dir}" overwrite="yes"/>
+ </sequential>
+ </macrodef>
+
+</project>
diff --git a/src/help/appvalidator_help/etc/resources/commonltr.css b/src/help/appvalidator_help/etc/resources/commonltr.css
new file mode 100644
index 0000000..ab4917c
--- /dev/null
+++ b/src/help/appvalidator_help/etc/resources/commonltr.css
@@ -0,0 +1,217 @@
+/*
+ | This file is part of the DITA Open Toolkit project hosted on
+ | Sourceforge.net. See the accompanying license.txt file for
+ | applicable licenses.
+*/
+
+/*
+ | (c) Copyright IBM Corp. 2004, 2005 All Rights Reserved.
+ */
+
+
+.hdr-confidential {
+ background-color: #dddddd;
+ float: right;
+ padding: 0.5em;
+}
+
+.help_breadcrumbs {
+ font: message-box;
+ margin-bottom: 10px;
+}
+
+body {
+ color: black;
+ background-color: white;
+ font-family: Arial, sans-serif;
+ font-size: 0.8em;
+ margin-left: 10px;
+ margin-right: 2em;
+ margin-bottom: 15px;
+ margin-top: 4px;
+}
+
+.unresolved { background-color: skyblue; }
+.noTemplate { background-color: yellow; }
+
+.base { background-color: #ffffff; }
+
+/* Add space for top level topics */
+.nested0 { margin-top: 0em;}
+.nested1 { margin-top: 3em; margin-left: 2%; }
+.nested2 { margin-left: 2%;}
+
+div.glossentry { margin-top: 1em; }
+div.glossentry .topictitle2 { border-top: none; }
+
+/* div with class=p is used for paragraphs that contain blocks, to keep the XHTML valid */
+.p {margin-top: 1em}
+
+/* Default of italics to set apart figure captions */
+.figcap { font-style: italic }
+.figdesc { font-style: normal }
+
+/* Use @frame to create frames on figures */
+.figborder { border-style: solid; padding-left : 3px; border-width : 2px; padding-right : 3px; margin-top: 1em; border-color : Silver;}
+.figsides { border-left : 2px solid; padding-left : 3px; border-right : 2px solid; padding-right : 3px; margin-top: 1em; border-color : Silver;}
+.figtop { border-top : 2px solid; margin-top: 1em; border-color : Silver;}
+.figbottom { border-bottom : 2px solid; border-color : Silver;}
+.figtopbot { border-top : 2px solid; border-bottom : 2px solid; margin-top: 1em; border-color : Silver;}
+
+/* Most link groups are created with <div>. Ensure they have space before and after. */
+.familylinks { margin-top: 2em; }
+.ullinks { list-style-type: none; margin-top: 2em; margin-left: 0.5em; padding-left: 0.5em; }
+.ulchildlink { margin-top: 1em; margin-bottom: 1em }
+.olchildlink { margin-top: 1em; margin-bottom: 1em }
+.linklist { margin-bottom: 1em }
+.linklistwithchild { margin-left: 1.5em; margin-bottom: 1em }
+.sublinklist { margin-left: 1.5em; margin-bottom: 1em }
+.relconcepts { margin-top: 2em; margin-bottom: 1em }
+.reltasks { margin-top: 2em; margin-bottom: 1em }
+.relref { margin-top: 2em; margin-bottom: 1em }
+.relinfo { margin-top: 2em; margin-bottom: 1em }
+.breadcrumb { font-size : smaller; margin-bottom: 1em }
+.prereq { margin-left : 20px;}
+
+
+
+/* Set heading sizes, getting smaller for deeper nesting */
+.topictitle1 { margin-top: 0em; margin-bottom: .1em; font-size: 1.8em; }
+.topictitle2 { margin-top: 1em; margin-bottom: .45em; font-size: 1.25em; margin-left: -2%; border-top: thin solid #CCCCCC; padding-top: .5em;}
+.topictitle3 { margin-top: 2em; margin-bottom: 0.2em; font-size: 1.0em; font-weight: bold; margin-left: -2%; border-top: thin solid #CCCCCC; padding-top: 0.5em; }
+.topictitle4 { margin-top: .83em; font-size: 0.9em; font-weight: bold; }
+.topictitle5 { font-size: 0.8em; font-weight: bold; }
+.topictitle6 { font-size: 0.7em; font-style: italic; }
+.sectiontitle { margin-top: 1em; margin-bottom: 0em; color: black; font-size: 1.2em; font-weight: bold;}
+.nested2 .sectiontitle { font-size: 1.0em; }
+.section { margin-top: 2em; margin-bottom: 1em }
+.example { margin-top: 2em; margin-bottom: 1em }
+
+/* All note formats have the same default presentation */
+.note { margin-top: 1em; margin-bottom : 1em;}
+.notetitle { font-weight: bold }
+.notelisttitle { font-weight: bold }
+.tip { margin-top: 1em; margin-bottom : 1em;}
+.tiptitle { font-weight: bold }
+.fastpath { margin-top: 1em; margin-bottom : 1em;}
+.fastpathtitle { font-weight: bold }
+.important { margin-top: 1em; margin-bottom : 1em;}
+.importanttitle { font-weight: bold }
+.remember { margin-top: 1em; margin-bottom : 1em;}
+.remembertitle { font-weight: bold }
+.restriction { margin-top: 1em; margin-bottom : 1em;}
+.restrictiontitle { font-weight: bold }
+.attention { margin-top: 1em; margin-bottom : 1em;}
+.attentiontitle { font-weight: bold }
+.dangertitle { font-weight: bold }
+.danger { margin-top: 1em; margin-bottom : 1em;}
+.cautiontitle { font-weight: bold; margin-top: 0.53em; }
+.caution { font-weight: bold; margin-bottom : 1em; }
+
+/* Simple lists do not get a bullet */
+ul.simple { list-style-type: none }
+
+/* Used on the first column of a table, when rowheader="firstcol" is used */
+.firstcol { font-weight : bold;}
+
+/* Various basic phrase styles */
+.bold { font-weight: bold; }
+.boldItalic { font-weight: bold; font-style: italic; }
+.italic { font-style: italic; }
+.underlined { text-decoration: underline; }
+.uicontrol { font-weight: bold; }
+.parmname { font-weight: bold; }
+.kwd { font-weight: bold; }
+.defkwd { font-weight: bold; text-decoration: underline; }
+.var { font-style : italic;}
+.shortcut { text-decoration: underline; }
+.term { font-style: italic; }
+
+/* Default of bold for definition list terms */
+.dlterm { font-weight: bold; margin-top: 0.5em; }
+
+/* Use CSS to expand lists with @compact="no" */
+.dltermexpand { font-weight: bold; margin-top: 1em; }
+*[compact="yes"]>li { margin-top: 0em;}
+*[compact="no"]>li { margin-top: .53em;}
+.liexpand { margin-top: 1em; margin-bottom: 1em }
+.sliexpand { margin-top: 1em; margin-bottom: 1em }
+.dlexpand { margin-top: 1em; margin-bottom: 1em }
+.ddexpand { margin-top: 1em; margin-bottom: 1em }
+.stepexpand { margin-top: 1em; margin-bottom: 1em }
+.substepexpand { margin-top: 1em; margin-bottom: 1em }
+
+/* Default list styles */
+ul li, ol li, dl { margin-top: .53em; margin-bottom: 0.0em; margin-left: 0.0em;}
+ul, ol { margin-bottom: 1em; margin-top: 0em;}
+
+/* Align images based on @align on topic/image */
+div.imageleft { text-align: left; margin-top: 1em; margin-bottom: 1em; }
+div.imagecenter { text-align: center; margin-top: 1em; }
+div.imageright { text-align: right; margin-top: 1em; }
+div.imagejustify { text-align: justify; margin-top: 1em; }
+
+/* The cell border can be turned on with
+ {border-right:solid}
+ This value creates a very thick border in Firefox (does not match other tables)
+
+ Firefox works with
+ {border-right:solid 1pt}
+ but this causes a barely visible line in IE */
+
+.cellrowborder { border-left:none; border-top:none; border-right:solid 1px; border-bottom:solid 1px }
+.row-nocellborder { border-left:none; border-right:none; border-top:none; border-right: hidden; border-bottom:solid 1px}
+.cell-norowborder { border-top:none; border-bottom:none; border-left:none; border-bottom: hidden; border-right:solid 1px}
+.nocellnorowborder { border:none; border-right: hidden;border-bottom: hidden }
+
+
+table {
+ color: black;
+ border-collapse: collapse;
+ border-color: black;
+ background: white;
+ margin-top: 0.5em;
+ margin-bottom: 0.5em;
+ margin-left: 0em;
+ margin-right: 0em;
+ font-size: 1em;
+}
+
+.tablenoborder { margin-top: 1em; margin-bottom: 1em; }
+table caption { text-align: left; padding-bottom: .5em;}
+.tablecap { font-weight: bold; }
+thead, th { background-color: #dddddd; font-weight: bold; }
+table div.p { margin-top: 0; margin-bottom: 0; }
+th { vertical-align: bottom;}
+td p:first-child, th p:first-child { margin-top: 0em; }
+
+pre.screen { padding: 5px 5px 5px 5px; border: outset; background-color: #CCCCCC; margin-top: 2px; margin-bottom : 2px; white-space: pre; overflow: auto; }
+
+.codeblock {
+ font-family: Monaco, "Courier New", Courier, fixed, Monospace;
+ font-size: 1em;
+ background-color: #dddddd;
+ padding: 6px;
+ margin-top: 1em;
+ margin-bottom: 1em;
+ white-space: pre-wrap; /* css-3 */
+ white-space: -moz-pre-wrap; /* Mozilla, since 1999 */
+ white-space: -pre-wrap; /* Opera 4-6 */
+ white-space: -o-pre-wrap; /* Opera 7 */
+ word-wrap: break-word; /* Internet Explorer 5.5+ */
+}
+
+.msgblock {
+ font-family: Monaco, "Courier New", Courier, fixed, Monospace;
+ font-size: 1em;
+ margin-top: 1em;
+ margin-bottom: 1em;
+ white-space: pre-wrap; /* css-3 */
+ white-space: -moz-pre-wrap; /* Mozilla, since 1999 */
+ white-space: -pre-wrap; /* Opera 4-6 */
+ white-space: -o-pre-wrap; /* Opera 7 */
+ word-wrap: break-word; /* Internet Explorer 5.5+ */
+}
+
+
+.codeph, .apiname { font-family: Monaco, "Courier New", Courier, fixed, Monospace; font-size: 1em; } \ No newline at end of file
diff --git a/src/help/appvalidator_help/etc/resources/hdr-confidential.xml b/src/help/appvalidator_help/etc/resources/hdr-confidential.xml
new file mode 100644
index 0000000..b3f1f73
--- /dev/null
+++ b/src/help/appvalidator_help/etc/resources/hdr-confidential.xml
@@ -0,0 +1 @@
+<div class="hdr-confidential">Motorola Confidential Restricted</div> \ No newline at end of file
diff --git a/src/help/appvalidator_help/etc/resources/hdr-none.xml b/src/help/appvalidator_help/etc/resources/hdr-none.xml
new file mode 100644
index 0000000..953ebac
--- /dev/null
+++ b/src/help/appvalidator_help/etc/resources/hdr-none.xml
@@ -0,0 +1 @@
+<a class="hdr-none"/> \ No newline at end of file
diff --git a/src/help/appvalidator_help/src/topics/c_appvalidator-conditions.dita b/src/help/appvalidator_help/src/topics/c_appvalidator-conditions.dita
new file mode 100644
index 0000000..7b82d17
--- /dev/null
+++ b/src/help/appvalidator_help/src/topics/c_appvalidator-conditions.dita
@@ -0,0 +1,436 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE concept PUBLIC "-//OASIS//DTD DITA Concept//EN" "../dtd/concept.dtd">
+<concept id="c_appvalidator-conditions" xml:lang="en-us">
+ <title>Interpreting App Validator results</title>
+ <shortdesc>The App Validator runs a series of "checkers," each of which has one or more conditions that it checks for.
+ This document provides detailed information about each checker and each condition, detailing the issues that the
+ checkers and conditions are examining your Android apps for and noting the corrective action you may want to take in
+ the event that your app is flagged for a given checker/condition combination. </shortdesc>
+ <prolog>
+ <metadata>
+ <keywords>
+ <!--<indexterm></indexterm>-->
+ </keywords>
+ </metadata>
+ </prolog>
+ <conbody>
+ <p>Note that whether or not you elect to take corrective action is up to you; many conditions simply point out
+ possible problem areas that in some cases may not actually require any correction.</p>
+ <section>
+ <title>Permissions Checker</title>
+ <p>Checker ID: <b>permissions</b></p>
+ <p>Examines the permissions required by the app and the APIs used by the app, looking for possible problems.</p>
+ <dl>
+ <dlentry>
+ <dt>Condition: blockedPermission</dt>
+ <dd>
+ <p>Verifies that the requested permissions are required by the application. Permissions have a "protection
+ level" that characterizes the potential risk implied by the permission and indicates how the system will
+ determine whether to grant the permission to the requesting application (see the Android documentation on
+ AndroidManifestPermission_protectionLevel for more information on these protection levels). The
+ blockedPermission condition looks to see if the application requires a permission that has a <b
+ >signature</b> or <b>signatureOrSystem</b> protection level. Such permissions are not intended to be used
+ by third-party developers; Android will deny their use.</p>
+ <p>If your app fails this test, remove the requirement for the problematic permissions within your app's
+ Android manifest file.</p>
+ </dd>
+ </dlentry>
+ <dlentry>
+ <dt>Condition: missingPermission</dt>
+ <dd>
+ <p>Some APIs require that the app request specific permissions in order to be used. If the necessary
+ permissions are not declared in the Android manifest file, a SecurityException is thrown at runtime. This
+ checker looks at the permissions requested in the Android manifest file to ensure that all needed
+ permissions (based upon an examination of the app's source) have been requested.</p>
+ <p>If your app fails this test, add the noted permissions to your application's Android manifest file.</p>
+ </dd>
+ </dlentry>
+ <dlentry>
+ <dt>Condition: unneededPermission</dt>
+ <dd>
+ <p>Verifies that all of the requested permissions are actually needed by the app</p>
+ <p>If you are notified that your app is requesting unneeded permissions, ensure that they are truly needed.
+ If they are not, remove the superfluous ones from your AndroidManifest.xml file.</p>
+ </dd>
+ </dlentry>
+ </dl>
+ </section>
+ <section>
+ <title>Google Play Filters Checker</title>
+ <p>Checker ID: <b>googlePlayFilters</b></p>
+ <p>The Google Play Filters checker looks for situations where:<ul>
+ <li>your app will be rejected by Google Play</li>
+ <li>your app will not appear in Google Play on specific devices</li>
+ <li>your app will appear in Google Play on devices for which it wasn't intended</li>
+ </ul></p>
+ <dl>
+ <dlentry>
+ <dt>Condition: missingVersionCodeOrName</dt>
+ <dd>
+ <p>Verifies the existence of android:versionCode and android:versionName attributes in the &lt;manifest&gt;
+ element within the app's Android manifest file. Google Play requires the presence of both of these
+ attributes. It uses android:versionCode to identify the application internally and to handle updates, and
+ it displays the android:versionName value to users as the application's version.</p>
+ <p>If your app fails this test, add the required attributes to the &lt;manifest&gt; element in your app's
+ Android manifest file.</p>
+ </dd>
+ </dlentry>
+ <dlentry>
+ <dt>Condition: certificatePeriodExpired</dt>
+ <dd>
+ <p>Verifies that the validity period for the certificate used to sign the app meets the requirements set by
+ Google Play. Google Play requires that your app be signed with a cryptographic private key that is valid
+ beyond 22 October 2033.</p>
+ <p>If your app fails this test, re-sign the app with a certificate that has the required validity
+ period.</p>
+ </dd>
+ </dlentry>
+ <dlentry>
+ <dt>Condition: missingManifestIconOrLabel</dt>
+ <dd>
+ <p>Verifies the existence of android:icon and android:label attributes on the &lt;application&gt; element
+ within the app's Android manifest file. Both of these attributes are required by Google Play.</p>
+ <p>If your app fails this test, edit the app's Android manifest file and add the missing attributes to the
+ &lt;application&gt; element.</p>
+ </dd>
+ </dlentry>
+ <dlentry>
+ <dt>Condition: missingMinSdkVersion</dt>
+ <dd>
+ <p>Verifies that the &lt;uses-sdk&gt; element has the android:minSdkVersion attribute set to an explicit
+ value. Although this attribute is not strictly required, Android assumes a value of "1" if it is not
+ present, indicating that the app is compatible with all versions of Android. Most apps are not compatible
+ with all versions of Android, for various reasons. If, for instance, your app uses an API that was
+ introduced in a later version and you do not set the android:minSdkVersion attribute to that version (or
+ later), your app could potentially be installed on a device running an earlier version of Android, where
+ it will crash when the missing API is called. Because of this, apps should always explicitly declare the
+ minimum SDK version they support.</p>
+ <p>If your app fails this test, add the android:minSdkVersion attribute, with the appropriate API level
+ value, to the &lt;uses-sdk&gt; element in your Android manifest file.</p>
+ </dd>
+ </dlentry>
+ <dlentry>
+ <dt>Condition: permissionToImpliedFeatures</dt>
+ <dd>
+ <p>Checks if an application's manifest file contains &lt;uses-permission> elements that imply certain
+ features without explicitly declaring those features. For example, if an application requests the CAMERA
+ permission but does not declare a &lt;uses-feature> element for android.hardware.camera, Google Play
+ assumes that the application requires a camera and does not show it on devices that do not have a camera. </p>
+ <p>For a list of permissions that imply hardware features, see the <xref
+ href="http://developer.android.com/guide/topics/manifest/uses-feature-element.html" format="html"
+ scope="external">documentation for the &lt;uses-feature> element</xref>. </p>
+ <p>Because Google Play does not filter based on &lt;uses-permission> elements, and because it is able to
+ infer certain features based upon permission requests, you are not required to supply the implied
+ &lt;uses-feature> elements. However, it is good practice to do so: explicitly list all features and
+ permissions required by your app in the app's Android manifest file. Note that if your app is able to use,
+ but does not require, an implied feature, add <b>android:required="false"</b> to the feature declaration,
+ and check for the presence of the feature at run time by calling <b>getSystemAvailableFeatures()</b>. </p>
+ </dd>
+ </dlentry>
+ <dlentry>
+ <dt>Condition: declaredMaxSdkVersion</dt>
+ <dd>
+ <p>Checks if the &lt;uses-sdk> element has the android:maxSdkVersion attribute. This attribute has been
+ deprecated; it is ignored by Android version 2.1 and later. Declaring android:maxSdkVersion is not
+ recommended. </p>
+ <p>If your app fails this test, remove the android:maxSdkVersion attribute from the &lt;uses-sdk> element in
+ your app's Android manifest file. </p>
+ </dd>
+ </dlentry>
+
+ </dl>
+ </section>
+ <section>
+ <title>Device Compatibility Checker</title>
+ <p>Checker ID: <b>deviceCompatibility</b></p>
+ <p>The App Validator maintains a set of specifications for various Android-powered Motorola devices. The Device
+ Compatibility checker compares the requirements listed in the app's Android manifest file against these device
+ specifications, noting those devices with which the app is incompatible.</p>
+ <p>Note that you can get a list of the device specifications by issuing the <b>appValidator</b> command (on the
+ command line) with the <b>-list-devices</b> option. To see the specifications for a given device, use the <b
+ >-describe-device</b> option.</p>
+ <dl>
+ <dlentry>
+ <dt>Condition: smallScreenSupport</dt>
+ <dd>
+ <p>Checks for a lack of support for small screen devices by examining the android:smallScreens attribute
+ within the &lt;supports-screens> element in the app's Android manifest file. Note that if the
+ &lt;uses-sdk> element has both its android:minSdkVersion and android:targetSdkVersion attributes set to 3
+ or less (or are omitted, which implies a value of 1), android:smallScreens is defined to be <b>false</b>.
+ Either or both of these values must be 4 or higher in order for your app to be able to target small screen
+ devices.</p>
+ <p>A small screen is defined as one with a smaller aspect ratio than the "normal" screen. An example of a
+ small screen device is the <xref href="http://developer.motorola.com/products/charm-mb502/" format="html"
+ scope="external">Motorola CHARMâ„¢</xref>.</p>
+ <p>This condition is not checked if android:smallScreens is explictly set to <b>false</b>.</p>
+ <p>If your app fails this test and you want it to be available in Google Play for small screen devices,
+ ensure that the android:minSdkVersion and android:targetSdkVersion attributes in the &lt;uses-sdk> element
+ are set to 4 or higher, and set android:smallScreens (an attribute of the &lt;supports-screens> element)
+ to <b>true</b>.</p>
+ </dd>
+ </dlentry>
+ <dlentry>
+ <dt>Condition: xlargeScreenSupport</dt>
+ <dd>
+ <p>Checks for extra large screen support. Unless the app has explicitly indicated a lack of extra large
+ screen support (with &lt;supports-screens android:xlargeScreens="false"&gt; or extra large screens not
+ listed in the &lt;compatible-screens> element within the manifest file), the checker looks to see if
+ either targetSdk or minSdk are less than 4.</p>
+ <p>If your app fails this test, either change your target API level to 4 or higher (if you intend for your
+ app to support extra large screens) or indicate that your app does not support extra large screens by both
+ adding &lt;supports-screens android:xlargeScreens="false"&gt; to your app's manifest file and ensuring
+ that the &lt;compatible-screens> element, if present, does not mention xlarge screens.</p>
+ </dd>
+ </dlentry>
+ <dlentry>
+ <dt>Condition: unsupportedFeatures</dt>
+ <dd>
+ <p>Compares the permissions and features required by the application (as declared in the application's
+ Android Manifest file) against the device specifications to see if the device has all of the required
+ features. The app will not appear in Google Play on a device that does not have all of the required
+ features (including those that may be implied by required permissions). </p>
+ <p>If your app fails this test, ensure that the noted permission or feature truly is required by your
+ application. Consider adding <b>android:required="false"</b> to the feature declaration if the feature is
+ optional, and not strictly required. Note that if the feature is optional your app can use <b
+ >getSystemAvailableFeatures()</b> to check for the presence of the feature at run time.</p>
+ </dd>
+ </dlentry>
+ </dl>
+ </section>
+ <section>
+ <title>Localization Strings Checker</title>
+ <p>Checker ID: <b>localizationStrings</b></p>
+ <p>The Localization Strings checker attempts to verify that the application is correctly localized, specifically
+ by checking if all values are translated to all languages.</p>
+ <p>This checker only validates applications that have at least one language-specific resource.</p>
+ <dl>
+ <dlentry>
+ <dt>Condition: missingDefaultLanguageKey</dt>
+ <dd>
+ <p>The <b>missingDefaultLanguageKey</b> condition verifies that the default strings file
+ (res/values/strings.xml) is present and that it contains a default string value for each key. If this
+ default strings file is absent, or is missing a string that your application needs, your application will
+ Force Close and present the user with an error message. </p>
+ <p>If your app fails this test, ensure that res/values/strings.xml exists. Within it, supply default text
+ strings for any keys that don't already have them. </p>
+ <p>Note that this situation applies to all types of resources, not just strings: you should create a set of
+ default resource files containing all of the layouts, drawables, animations, and other resources that your
+ application relies upon. For information about localization in Android, see <xref
+ href="http://developer.android.com/guide/topics/resources/localization.html" format="html"
+ scope="external">the Localization topic within the Android Dev Guide</xref>.</p>
+ </dd>
+ </dlentry>
+ <dlentry>
+ <dt>Condition: missingLanguageKey</dt>
+ <dd>
+ <p>Examines non-default text strings files for missing keys: keys that are present in the default strings
+ file (res/values/strings.xml) but not in the locale-specific text strings file. This lack of a translation
+ for a specific key can represent a failure in the translation process. </p>
+ <p>If your app fails this test, ensure that it is OK for your app to fall back to the default string values.
+ If not, add the missing keys to the non-default text strings file and supply localized string values.</p>
+ </dd>
+ </dlentry>
+ <dlentry>
+ <dt>Condition: missingValue</dt>
+ <dd>
+ <p>Checks for keys that do not have a value defined in either the default language or in other languages. If
+ the default string is empty, nothing is displayed on screen, which is probably not what you intend. If a
+ locale-specific key has the empty string for a value, it prevents a fallback to the default value, which
+ is also probably not what you intend. </p>
+ <p>If your app fails this test, and the key with the missing value is in the default text strings file
+ (res/values/strings.xml), supply the missing value. If the missing value is in a locale-specific text
+ strings file, do one of the following:<ul>
+ <li>supply a localized value for the key if there should be one</li>
+ <li>remove the key from the file if the value from the default strings file should be used</li>
+ <li>leave the value empty if the empty string is the correct value for the key in that locale</li>
+ </ul></p>
+ </dd>
+ </dlentry>
+ </dl>
+ </section>
+ <section>
+ <title>Missing Drawable Resources Checker</title>
+ <p>Checker ID: <b>missingDrawableResources</b>
+ </p>
+ <p>Checks your application's drawable resources, looking to see if your app has properly-sized images for every
+ supported screen size and density. </p>
+ <p>This checker only runs if the android:anyDensity attribute of the &lt;supports-screens> element in your app's
+ Android manifest file is set to <b>true</b> and the target SDK is higher than 3. Note that during app packaging
+ AAPT automatically overrides the android:anyDensity value and forces it to <b>false</b> if there are missing
+ drawables. Because of this, this checker does not generate results when you supply an APK to the MOTODEV App
+ Validator.</p>
+ <dl>
+ <dlentry>
+ <dt>Condition: unsupportedDensity</dt>
+ <dd>
+ <p>Checks for the existence of a drawable-xdpi folder when the application's API level is lower than 8. An
+ API level of 7 or lower will cause the drawable-xdpi folder to be ignored, thus the existence of this
+ particular condition. </p>
+ <p>If your app fails this test, consider increasing the app's API level to 8 or above (and then verifying
+ that your app properly supports extra-high-density screens). Alternatively, you can remove the
+ drawable-xdpi folder, since it will be ignored and its contents simply take up unnecessary space in the
+ application package. </p>
+ <p>For more information on screen sizes and supporting the wide variety of Android devices on the market
+ today, see <xref href="http://developer.android.com/guide/practices/screens_support.html" format="html"
+ scope="external">Supporting Multiple Screens in the Android Dev Guide</xref>.</p>
+ </dd>
+ </dlentry>
+ <dlentry>
+ <dt>Condition: missingDrawables</dt>
+ <dd>
+ <p>Examines each of the drawable folders to ensure that they all contain the same set of images. Images that
+ appear in one drawable folder but not in another have not been properly customized for every supported
+ screen size and density. Although Android will fall back on the default in this case, scaling the
+ corresponding default image as necessary, this may result in a less-than-optimal image. For the best
+ results you should supply a full set of images corresponding to each drawable folder. </p>
+ <p>If your app fails this test, consider supplying the missing images, properly sized and scaled as
+ appropriate for the enclosing drawable folder. </p>
+ <p>For more information on screen sizes and supporting the wide variety of Android devices on the market
+ today, see <xref href="http://developer.android.com/guide/practices/screens_support.html" format="html"
+ scope="external">Supporting Multiple Screens in the Android Dev Guide</xref>.</p>
+ </dd>
+ </dlentry>
+ <dlentry>
+ <dt>Condition: missingDrawableFolders</dt>
+ <dd>
+ <p>Verifies that separate drawable folders exist for the four supported densities: low, medium, high, and
+ extra-high. Although Android can automatically scale a default drawable to make up for a missing density,
+ for best results you should supply a full set of images for each supported density. </p>
+ <p>If your app fails this test, consider adding folders for the missing density, complete with a full set of
+ drawables scaled properly for that density. </p>
+ <p>For more information on screen sizes and supporting the wide variety of Android devices on the market
+ today, see <xref href="http://developer.android.com/guide/practices/screens_support.html" format="html"
+ scope="external">Supporting Multiple Screens in the Android Dev Guide</xref>.</p>
+ </dd>
+ </dlentry>
+ </dl>
+ </section>
+ <section>
+ <title>Layout Checker</title>
+ <p>Checker ID: <b>layout</b>
+ </p>
+ <p>Check the application layout files for problems within their definitions and inconsistencies among the layouts
+ for different configurations.</p>
+ <dl>
+ <dlentry>
+ <dt>Condition: missingId</dt>
+ <dd>
+ <p>Compares corresponding layouts to ensure that they each have the same set of IDs. If two corresponding
+ layouts define different IDs for the same UI element, the resulting R.java file will have definitions for
+ both, and any code that references either (or both) will compile. However, the app will force close at run
+ time if the particular layout being shown does not contain the ID being referenced. </p>
+ <p>If your app fails this test, update the named layouts so that they properly declare the noted IDs.</p>
+ </dd>
+ </dlentry>
+ <dlentry>
+ <dt>Condition: repeatedId</dt>
+ <dd>
+ <p>Scans through your layout files, looking for duplicate IDs. Two or more widgets that share an ID can
+ cause problems if your code references one of those widgets by ID: the wrong widget might be referenced,
+ causing unexpected behavior.</p>
+ <p>If your app fails this test, update the widgets containing the listed IDs so that they no longer share a
+ common ID value.</p>
+ </dd>
+ </dlentry>
+ <dlentry>
+ <dt>Condition: viewTypeIds</dt>
+ <dd>
+ <p>Verifies that a given ID is always used by the same kind of view when that ID appears in multiple layout
+ configurations. This condition arises, for example, when you have both portrait and landscape
+ configurations of your layout in which a given ID is used for one widget type (a TextView, say) in one
+ configuration and an entirely different widget type (a Button, say) in the other. A situation such as this
+ can result in a class cast exception: a call to findViewById(), for instance will succeed or fail
+ depending upon the device's current orientation.</p>
+ <p>If your app fails this test, examine those layout configurations that use the listed ID and update them
+ as needed to ensure that different widget types used different IDs.</p>
+ </dd>
+ </dlentry>
+ <dlentry>
+ <dt>Condition: xlargeLayouts</dt>
+ <dd>
+ <p>Examines your project to see if it contains layouts within the res/layout-xlarge folder.</p>
+ <p>If your application fails this test and you intend for it to run on a device with an extra large screen
+ (such as a 10" tablet), be sure that it runs with the look and feel you expect. Most applications will
+ need layouts designed specifically for extra large screens. If your app is not intended to be run on a
+ device with an extra large screen, add &lt;supports-screens android:xlargeScreens="false"&gt; to your
+ AndroidManifest.xml file.</p>
+ </dd>
+ </dlentry>
+ </dl>
+ </section>
+ <section>
+ <title>Single Main Activity Checker</title>
+ <p>Checker ID: <b>singleMainActivity</b>
+ </p>
+ <p>Checks the number of activities set as "main". Although some applications can have more than one (or zero)
+ entry points, most applications have only one.</p>
+ <dl>
+ <dlentry>
+ <dt>Condition: exactlyOne</dt>
+ <dd>
+ <p>Checks for the presence of exactly one activity set as "main", indicating that the app has a single
+ activity that initiates the application. Although having more (or less) than one main activity is not
+ wrong, it is rare enough to warrant a condition check. </p>
+ <p>If your app fails this test, and you intend for it to only have a single main entry point, examine your
+ app's Android manifest file and ensure that only a single activity has an intent filter with the following
+ action: </p>
+ <codeblock>&lt;action android:name="code android.intent.action.MAIN"/></codeblock>
+ </dd>
+ </dlentry>
+ </dl>
+ </section>
+ <section>
+ <title>Code Checker</title>
+ <p>Checker ID: <b>codeChecker</b></p>
+ <p>Looks for possible issues with Java code.</p>
+ <dl>
+ <dlentry>
+ <dt>Condition: openedCursors</dt>
+ <dd>
+ <p>Looks for methods that don't seem to properly close their cursors. Note that this checker can report a
+ problem when none exists, if the cursor is closed in a non-obvious way. Also note that managed cursors
+ don't need to be closed by your app.</p>
+ <p>If your app fails this test, verify that all cursors are properly closed.</p>
+ </dd>
+ </dlentry>
+ </dl>
+ </section>
+ <section>
+ <title>Widget Preview Checker</title>
+ <p>Checker ID: <b>widgetPreview</b></p>
+ <p>Examines widget projects to see if they support the preview feature.</p>
+ <dl>
+ <dlentry>
+ <dt>Condition: missingWidgetPreview</dt>
+ <dd>
+ <p>Warns you if your widget project does not appear to contain a preview of what the AppWidget will look
+ like after it's configured. A widget project that doesn't include a preview image will initially be
+ displayed to the user as the AppWidget's icon. </p>
+ <p>This condition is only valid for Android 3.1 or higher.</p>
+ <p>If you receive this warning, consider whether your widget project should contain a preview image. If so,
+ add the image to your project and then add the the android:previewImage attribute to the &lt;receiver&gt;
+ element in your AndroidManifest.xml file.</p>
+ </dd>
+ </dlentry>
+ </dl>
+ </section>
+ <section>
+ <title>Building Blocks Declaration Checker</title>
+ <p>Checker ID: <b>buildingBlocksDeclaration</b></p>
+ <p>Looks for inconsistencies within the Android components—such as Activities, Services, Content providers, and
+ Broadcast receivers—declared in your AndroidManifest.xml file.</p>
+ <dl>
+ <dlentry>
+ <dt>Condition: buildingBlockMissDeclaration</dt>
+ <dd>
+ <p>Verifies that the Android components declared in your project's AndroidManifest.xml file extend the
+ classes from which they should inherit. If your app fails this test, verify that the indicated building
+ block extends the listed class. If it does not, your app may crash.<note type="note">A class that
+ indirectly inherits from an Android building block will be flagged as a possible problem, even though it
+ is not.</note></p>
+ </dd>
+ </dlentry>
+ </dl>
+ </section>
+ </conbody>
+</concept>
diff --git a/src/help/appvalidator_help/src/topics/cs_app-validator.dita b/src/help/appvalidator_help/src/topics/cs_app-validator.dita
new file mode 100644
index 0000000..28e8ed6
--- /dev/null
+++ b/src/help/appvalidator_help/src/topics/cs_app-validator.dita
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE cshelp
+ PUBLIC "-//IBM//DTD DITA CSHelp//EN" "../dtd/cshelp.dtd">
+<cshelp id="cs_app-validator" xml:lang="en-us">
+ <title>Android Application Validator CS Help</title>
+ <shortdesc/>
+ <csbody/>
+
+ <!-- Plug-in: com.motorolamobility.preflighting.ui -->
+ <cshelp id="preference-appvalidator-commandline">
+ <title>Android App Validator preferences dialog</title>
+ <shortdesc>The Android App Validator preferences dialog enables you to control how the validator operates when invoked
+ from within Eclipse.</shortdesc>
+ <csbody/>
+ <related-links>
+ <link href="u_appvalidator-prefs.dita"/>
+ </related-links>
+ </cshelp>
+</cshelp>
diff --git a/src/help/appvalidator_help/src/topics/g_legal.dita b/src/help/appvalidator_help/src/topics/g_legal.dita
new file mode 100644
index 0000000..bf09c2f
--- /dev/null
+++ b/src/help/appvalidator_help/src/topics/g_legal.dita
@@ -0,0 +1,25 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<!DOCTYPE topic PUBLIC "-//OASIS//DTD DITA Topic//EN" "http://docs.oasis-open.org/dita/v1.1/OS/dtd/topic.dtd">
+<topic id="g_legal">
+ <title>Trademark notices</title>
+ <body>
+ <p>Copyright © <ph conref="g_variables.dita#g_variables/product-copyright-years"/>, Motorola Mobility, Inc. All rights
+ reserved.</p>
+ <p>This documentation is provided to you under the terms of the Motorola End User License Agreement. Motorola Mobility, Inc.
+ reserves the right to revise this documentation and to make changes in content from time to time without
+ obligation on the part of Motorola Mobility, Inc. to provide notification of such revision or changes.</p>
+ <p>If this documentation is provided on compact disc or as part of another software package, the other software and
+ documentation on the compact disc are subject to the license agreement accompanying the compact disc.</p>
+ <p>MOTOROLA and the Stylized M Logo are registered in the U.S. Patent &amp; Trademark Office. <ph
+ product="android-studio">Android is a trademark of Google Inc. Use of this trademark is subject to <xref
+ scope="external" format="html" href="http://www.google.com/permissions/index.html">Google Permissions</xref>.
+ </ph>Java and all other Java-based marks are trademarks or registered trademarks of Sun Microsystems, Inc. in the
+ U.S. and other countries. Eclipse is a trademark of Eclipse Foundation, Inc. <ph product="android-studio"
+ >DeviceAnywhere is a trademark of Mobile Complete, Inc. </ph><ph product="webui">Microsoft, Windows, Windows Me,
+ and Windows XP are registered trademarks of Microsoft Corporation. Linux is the registered trademark of Linus
+ Torvalds in the United States and other countries. VMware is a registered trademark or trademarks (the "Marks")
+ of VMware, Inc. in the United States and/or other jurisdictions. </ph>
+ <ph product="javame-studio javame-sdk">Symbian and UIQ are registered trademarks of Symbian Software Ltd. </ph>
+ All other product and service names are the property of their respective owners.</p>
+ </body>
+</topic>
diff --git a/src/help/appvalidator_help/src/topics/g_variables.dita b/src/help/appvalidator_help/src/topics/g_variables.dita
new file mode 100644
index 0000000..1e8b5ec
--- /dev/null
+++ b/src/help/appvalidator_help/src/topics/g_variables.dita
@@ -0,0 +1,26 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<!DOCTYPE topic PUBLIC "-//OASIS//DTD DITA Topic//EN" "http://docs.oasis-open.org/dita/v1.1/OS/dtd/topic.dtd">
+<topic id="g_variables">
+ <title>Variables</title>
+ <body>
+ <p>Use this file to create variables for elements supported in generic topics. Create a
+ phrase or other element, assign it an ID, then add text to the element. To have a
+ different value based on a filtered attribute (product, platform, audience, otherprops),
+ nest multiple elements within the ID'd element, each with a different attribute value.
+ See the example below:</p>
+ <p>
+ <ph id="studio-sdk-product-name"><ph product="javame-sdk">MOTODEV SDK for Java ME</ph><ph product="javame-studio">MOTODEV Studio for Java ME</ph><ph product="webui">MOTODEV Studio for WebUI</ph><ph product="android-studio">MOTODEV Studio for Android</ph></ph>
+ <ph id="gui-product-name"><ph product="javame-sdk">the Launchpad application</ph><ph product="javame-studio">MOTODEV Studio for Java ME</ph><ph product="webui">MOTODEV Studio for WebUI</ph><ph product="android-studio">MOTODEV Studio for Android</ph></ph>
+ <ph id="tools-and-services-view-name"><ph product="javame-sdk">Utilities</ph><ph product="javame-studio">Java ME Options</ph></ph>
+ <ph id="studio-sdk-prefs-parent-menu"><ph product="javame-sdk"><uicontrol>File</uicontrol></ph><ph product="javame-studio android-studio"><uicontrol>Window</uicontrol> (on Mac OS X, <uicontrol>MOTODEV Studio for Android</uicontrol>)</ph></ph>
+ </p>
+
+ <p>
+ <ph id="product-copyright-years"><ph product="javame-sdk javame-studio">2007-2009</ph><ph product="android-studio">2009-2012</ph></ph>
+ </p>
+
+ <p>Here's an example of how to conref one of the above variables:</p>
+ <codeblock>&lt;ph conref="g_variables.dita#g_variables/studio-sdk-product-name"/></codeblock>
+
+ </body>
+</topic>
diff --git a/src/help/appvalidator_help/src/topics/r_appvalidator-cmdline.dita b/src/help/appvalidator_help/src/topics/r_appvalidator-cmdline.dita
new file mode 100644
index 0000000..51179b7
--- /dev/null
+++ b/src/help/appvalidator_help/src/topics/r_appvalidator-cmdline.dita
@@ -0,0 +1,133 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE reference
+ PUBLIC "-//OASIS//DTD DITA Reference//EN" "../dtd/reference.dtd">
+<reference xml:lang="en-us" id="r_appvalidator-cmdline">
+ <title>appvalidator command-line options</title>
+ <prolog>
+ <metadata>
+ <keywords>
+ <!--<indexterm></indexterm>-->
+ </keywords>
+ </metadata>
+ </prolog>
+ <refbody>
+ <section>
+ <p>The appvalidator script (located in the MOTODEV Studio for Android installation directory) has a number of
+ command-line options that can be used to customize its operation.</p>
+ </section>
+ <section><title>Getting Help</title>Invoke the appvalidator script with the <codeph>-help</codeph> option to see a
+ list of the command-line options.</section>
+ <section>
+ <title>Invoking the App Validator</title>
+ <p><codeblock>appvalidator [FILE] [OPTION [PARAMETER]]...</codeblock>
+ <codeph>FILE</codeph> is the Android APK or project to be validated. </p>
+ </section>
+ <section>
+ <title>Options</title>
+ <p>The <codeph>appvalidator</codeph> command-line options are as follows:</p>
+ <p>
+ <simpletable>
+ <sthead>
+ <stentry>Option</stentry>
+ <stentry>Parameter</stentry>
+ <stentry>Description</stentry>
+ </sthead>
+ <strow>
+ <stentry><codeph>-sdk</codeph></stentry>
+ <stentry>path to the SDK</stentry>
+ <stentry>Allows you to specify the location of the Android SDK to be used when validating the application.
+ If an SDK is not explicitly specified using this option, the Android SDK specified in your system PATH is
+ used instead.</stentry>
+ </strow>
+ <strow>
+ <stentry><codeph>-c</codeph></stentry>
+ <stentry>checker ID, (optional) checker parameters</stentry>
+ <stentry>Specifies the checker to use when validating the application. You specify checkers by their IDs; a
+ list of available checkers and their IDs can be obtained with the <codeph>-list-checkers</codeph>
+ option.</stentry>
+ </strow>
+ <strow>
+ <stentry><codeph>-wx</codeph></stentry>
+ <stentry>(optional) one or more checker IDs or conditions, separated by whitespace</stentry>
+ <stentry>Increases the warning level of the validation results for the specified set of checkers and/or
+ conditions. If no checker ID or condition ID is supplied, increases the warning level for all checkers and
+ conditions. A list of available checkers and their IDs can be obtained with the <codeph
+ >-list-checkers</codeph> option. A list of conditions and their IDs for a given checker can be obtained
+ through the use of the <codeph>-help</codeph> option.</stentry>
+ </strow>
+ <strow>
+ <stentry><codeph>-xw</codeph></stentry>
+ <stentry>(optional) one or more checker IDs, separated by whitespace</stentry>
+ <stentry>Decreases the warning level of the validation results for the specified set of checkers and/or
+ conditions. If no checker ID or condition ID is supplied, decreases the warning level for all checkers and
+ conditions. A list of available checkers and their IDs can be obtained with the <codeph
+ >-list-checkers</codeph> option. A list of conditions and their IDs for a given checker can be obtained
+ through the use of the <codeph>-help</codeph> option.</stentry>
+ </strow>
+ <strow>
+ <stentry><codeph>-output</codeph></stentry>
+ <stentry><codeph>text | csv | xml</codeph></stentry>
+ <stentry>Format the output as text (the default), as comma-separated values suitable for importing into a
+ spreadsheet (<codeph>csv</codeph>) or as XML. When this option is specified, the verbosity level is
+ automatically set to 0.</stentry>
+ </strow>
+ <strow>
+ <stentry><codeph>-limit</codeph></stentry>
+ <stentry>an integer value indicating the number of entries</stentry>
+ <stentry>Limits the output to the specified number of entries.</stentry>
+ </strow>
+ <strow>
+ <stentry><codeph>-list-checkers</codeph></stentry>
+ <stentry/>
+ <stentry>Lists the available checkers. Use the <codeph>-help</codeph> option to get information about an
+ individual checker.</stentry>
+ </strow>
+ <strow>
+ <stentry><codeph>-list-devices</codeph></stentry>
+ <stentry/>
+ <stentry>Lists the devices for which the Android app can be validated.</stentry>
+ </strow>
+ <strow>
+ <stentry><codeph>-describe-device</codeph></stentry>
+ <stentry>device name</stentry>
+ <stentry>For a specified device, enumerates the device parameters that may be used when validating an
+ Android app against that device. Supply the name of the device as listed with the <codeph
+ >-list-devices</codeph> option.</stentry>
+ </strow>
+ <strow>
+ <stentry><codeph>-d</codeph></stentry>
+ <stentry>device name</stentry>
+ <stentry>Validates the app only against the named device (to validate against more than one device, use
+ multiple <codeph>-d</codeph> options). If this option is not supplied, the app is validated against all of
+ the devices listed when you use the <codeph>-list-checkers</codeph> option.</stentry>
+ </strow>
+ <strow>
+ <stentry><codeph>-help</codeph></stentry>
+ <stentry>(optional) checker ID</stentry>
+ <stentry>When used by itself (without a checker ID) this option lists the command-line syntax and options
+ used to invoke the App Validator from the command line. If a checker ID is specified, the <codeph
+ >-help</codeph> option displays information about the specified checker, including a list of conditions
+ and their IDs for use with the <codeph>-wx</codeph> and <codeph>-xw</codeph> options. A list of available
+ checkers and their IDs can be obtained with the <codeph>-list-checkers</codeph> option.</stentry>
+ </strow>
+ <strow>
+ <stentry><codeph>-v</codeph><i>N</i></stentry>
+ <stentry/>
+ <stentry>Indicates the "verbosity level" - the level of detail that is logged (in the Console view, if run
+ from Eclipse) when the validator is run. In place of <i>N</i> supply a digit—either 0, 1, or 2—to indicate
+ the level. <codeph>-v0</codeph> (the default level) results in the most concise output, while <codeph
+ >-v2</codeph> produces a detailed log of every action that the validator takes.</stentry>
+ </strow>
+ <strow>
+ <stentry><codeph>-w</codeph><i>N</i></stentry>
+ <stentry/>
+ <stentry>Indicates the level of warnings that are logged (in the Problems view, if run from Eclipse) when
+ the validator is run. In place of <i>N</i> supply a digit—either 0, 1, 2, 3, or 4—to indicate the warning
+ level. <codeph>-w0</codeph> results in the most concise output, with only fatal errors being logged, while
+ <codeph>-w4</codeph> (the default) produces a detailed log of every warning and error.</stentry>
+ </strow>
+ </simpletable>
+ </p>
+ </section>
+ </refbody>
+</reference>
diff --git a/src/help/appvalidator_help/src/topics/t_app-validating-command-line.dita b/src/help/appvalidator_help/src/topics/t_app-validating-command-line.dita
new file mode 100644
index 0000000..ab9e4c4
--- /dev/null
+++ b/src/help/appvalidator_help/src/topics/t_app-validating-command-line.dita
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE task PUBLIC "-//OASIS//DTD DITA Task//EN" "../dtd/task.dtd">
+<task id="t_app-validating-command-line" xml:lang="en-us">
+ <title>Validating an application from the command line</title>
+ <shortdesc>The App Validator can be run from the command-line as well as from within Eclipse. This enables you
+ to run the validator as part of your automated build process.</shortdesc>
+ <prolog>
+ <metadata>
+ <keywords>
+ <!--<indexterm></indexterm>-->
+ </keywords>
+ </metadata>
+ </prolog>
+ <taskbody><steps>
+ <step>
+ <cmd>From the command-line, or from within your build script, invoke the appvalidator script located in the
+ MOTODEV Studio for Android installation directory. Note that on Microsoft Windows the script is a batch file
+ (appvalidator.bat), while on Mac OS X and Linux it is a shell script (appvalidator.sh). Specify the APK file
+ or the Android project folder for the application to be validated. If your system path does not include the
+ location of the Android SDK's primary tools directory, specify it using the <codeph>-sdk</codeph> option.</cmd>
+ <info>See <xref href="r_appvalidator-cmdline.dita"/> for the set of options you can use when invoking the
+ appvalidator script.</info>
+ </step>
+ </steps><example>To run all available checkers on your project or APK, issue the following command from within the
+ MOTODEV Studio for Android installation directory:
+ <codeblock>appvalidator &lt;path_to_project_or_APK&gt; -sdk &lt;path_to_SDK&gt;></codeblock> To run a specific
+ checker:
+ <codeblock>appvalidator &lt;path_to_project_or_APK&gt; -sdk &lt;path_to_SDK&gt; -c &lt;checker_ID&gt;</codeblock></example></taskbody>
+</task>
diff --git a/src/help/appvalidator_help/src/topics/t_app-validating-gui.dita b/src/help/appvalidator_help/src/topics/t_app-validating-gui.dita
new file mode 100644
index 0000000..90645c7
--- /dev/null
+++ b/src/help/appvalidator_help/src/topics/t_app-validating-gui.dita
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE task PUBLIC "-//OASIS//DTD DITA Task//EN" "../dtd/task.dtd">
+<task id="t_app-validating" xml:lang="en-us">
+ <title>Validating an application from Eclipse</title>
+ <shortdesc>From within Eclipse you can easily run the App Validator against any of your open projects.</shortdesc>
+ <prolog>
+ <metadata>
+ <keywords>
+ <!--<indexterm></indexterm>-->
+ </keywords>
+ </metadata>
+ </prolog>
+ <taskbody>
+ <steps>
+ <step>
+ <cmd>From the Package Explorer view either right-click the open project or right-click an APK file from your
+ workspace and then select <uicontrol>Validate Android Application</uicontrol>. Note that you can validate
+ multiple APKs or multiple Android projects at one time by selecting <menucascade><uicontrol
+ >MOTODEV</uicontrol><uicontrol>App Validator</uicontrol><uicontrol>Validate Android
+ Apps</uicontrol></menucascade> (to validate one or more APKs) or <menucascade><uicontrol
+ >MOTODEV</uicontrol><uicontrol>App Validator</uicontrol><uicontrol>Validate Android
+ Project(s)</uicontrol></menucascade> (to validate one or more Android projects).</cmd>
+ <stepresult>The App Validator runs. The overall results are displayed in a Console view, and the various errors
+ and warnings are listed in the Problems view, often with suggestions for fixing the problem. Double-click an
+ entry in the Problems view to open the appropriate file in an editor view with the problem area highlighted.
+ <p>After running the App Validator, those files with problems are appropriately marked with error or warning
+ symbols in the Package Explorer.</p>Note that by default the output produced by the Android App Validator is
+ equivalent to running it with command-line options for the default verbosity level (-v2) and a detailed log of
+ every warning and error (-w4). These defaults can be changed (and other command-line options added) using the
+ App Validator preferences dialog.</stepresult>
+ </step>
+ </steps>
+ </taskbody>
+</task>
diff --git a/src/help/appvalidator_help/src/topics/t_validate-about.dita b/src/help/appvalidator_help/src/topics/t_validate-about.dita
new file mode 100644
index 0000000..27172fd
--- /dev/null
+++ b/src/help/appvalidator_help/src/topics/t_validate-about.dita
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE task PUBLIC "-//OASIS//DTD DITA Task//EN" "../dtd/task.dtd">
+<task id="t_validate-about" xml:lang="en-us">
+ <title>Validating Android applications</title>
+ <shortdesc>Using the Android Application Validator, you can easily run a set of "checkers" against your application
+ that look for problems that might not be immediately apparent at build or install time. The set of checks that the
+ Android Application Validator runs is extensible; as new checks are developed, they will be made available for
+ download.</shortdesc>
+ <prolog>
+ <metadata>
+ <keywords>
+ <!--<indexterm></indexterm>-->
+ </keywords>
+ </metadata>
+ </prolog>
+</task>
diff --git a/src/help/appvalidator_help/src/topics/u_appvalidator-prefs-checkers.dita b/src/help/appvalidator_help/src/topics/u_appvalidator-prefs-checkers.dita
new file mode 100644
index 0000000..3946efb
--- /dev/null
+++ b/src/help/appvalidator_help/src/topics/u_appvalidator-prefs-checkers.dita
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE reference PUBLIC "-//OASIS//DTD DITA Reference//EN" "../dtd/reference.dtd">
+<reference id="u_appvalidator-prefs-checkers" xml:lang="en-us">
+ <title>Android App Validator preferences - Checkers tab</title>
+ <shortdesc>The Checkers tab of the Android App Validator preferences dialog enables you to specify which checkers and
+ conditions the App Validator should validate your app against, as well as to fine-tune the operation of those
+ checkers.</shortdesc>
+ <prolog>
+ <metadata>
+ <keywords>
+ <!--<indexterm></indexterm>-->
+ </keywords>
+ </metadata>
+ </prolog>
+ <refbody>
+ <section>
+ <p>This tab contains two list boxes, one beneath the other. The upper list box lists all of the supported
+ checkers, while the lower list box lists those conditions that are validated by the currently selected checker
+ (this lower list box is empty if a checker is not selected). To the left of each checker is a checkbox that
+ indicates whether the checker or condition will be run. By default, all checkers are selected; clearing a given
+ checkbox prevents that checker from being run when an app is validated. Note the <uicontrol>Select/deselect
+ all</uicontrol> control beneath the list of checkers; this applies to all listed checkers.</p>
+ <p>In addition to the checkboxes, you can click in the <uicontrol>Parameters</uicontrol> and <uicontrol>Change
+ Warning Level</uicontrol> columns for a given checker row to specify parameters and increase or decrease the
+ warning level for that checker. Similarly, for a given condition row you can click within the <uicontrol>Change
+ Warning Level</uicontrol> column to increase or decrease the warning level for an individual condition.</p>
+ </section>
+ </refbody>
+</reference>
diff --git a/src/help/appvalidator_help/src/topics/u_appvalidator-prefs-devices.dita b/src/help/appvalidator_help/src/topics/u_appvalidator-prefs-devices.dita
new file mode 100644
index 0000000..327953f
--- /dev/null
+++ b/src/help/appvalidator_help/src/topics/u_appvalidator-prefs-devices.dita
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE reference PUBLIC "-//OASIS//DTD DITA Reference//EN" "../dtd/reference.dtd">
+<reference id="u_appvalidator-prefs-devices" xml:lang="en-us">
+ <title>Android App Validator preferences - Devices tab</title>
+ <shortdesc>Allows you to specify which devices, if any, an app should be validated against when the Android App
+ Validator is run.</shortdesc>
+ <prolog>
+ <metadata>
+ <keywords>
+ <!--<indexterm></indexterm>-->
+ </keywords>
+ </metadata>
+ </prolog>
+ <refbody>
+ <section>The list box on this tab lists all devices for which validation criteria is known. By default, all devices
+ are selected, meaning that when the App Validator is run it will validate the app against all of the
+ listed devices. If you don't intend your app to run on certain devices (tablets, for instance, or small-screen
+ devices) you can clear the appropriate checkboxes to prevent the App Validator from generating error or
+ warning messages for those devices. Note that although the device list is initially sorted by device name, you can
+ click the <uicontrol>Screen Size</uicontrol> or <uicontrol>Pixel Density</uicontrol> column headings to sort the
+ list by the selected criteria, making it easier to select or eliminate devices by their screen specifications.
+ Finally, note the <uicontrol>Select/deselect all</uicontrol> control beneath the list, which allows you to quickly
+ select or clear the list of devices for which the app will be validated.</section>
+ </refbody>
+</reference>
diff --git a/src/help/appvalidator_help/src/topics/u_appvalidator-prefs-general.dita b/src/help/appvalidator_help/src/topics/u_appvalidator-prefs-general.dita
new file mode 100644
index 0000000..f0d1b86
--- /dev/null
+++ b/src/help/appvalidator_help/src/topics/u_appvalidator-prefs-general.dita
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE reference PUBLIC "-//OASIS//DTD DITA Reference//EN" "../dtd/reference.dtd">
+<reference id="u_appvalidator-prefs-general" xml:lang="en-us">
+ <title>Android App Validator preferences - General Settings tab</title>
+ <shortdesc>Use the General Settings tab to control those aspects of the App Validator that are independent of
+ any specific checker or target device.</shortdesc>
+ <prolog>
+ <metadata>
+ <keywords>
+ <!--<indexterm></indexterm>-->
+ </keywords>
+ </metadata>
+ </prolog>
+ <refbody>
+ <simpletable>
+ <strow>
+ <stentry><b>Output limit</b></stentry>
+ <stentry>Limits the output to the specified number of entries. A value of zero indicates that there should be no
+ limit on the number of entries output by the App Validator.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Output type</uicontrol></stentry>
+ <stentry>Format the output as text (the default), as XML, or as comma-separated values suitable for importing
+ into a spreadsheet (CSV). When this option is specified, the verbosity level is automatically set to
+ 0.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Warning levels</uicontrol></stentry>
+ <stentry>Controls the level of messages that are logged in the Problems view when the validator is run. Level 0
+ ("No messages") results in the most concise output, with no errors, warnings, or fix suggestions being logged,
+ while level 4 (the default) produces a detailed log of every error, warning, and fix suggestion.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Verbosity levels</uicontrol></stentry>
+ <stentry>Controls the "verbosity level" - the level of detail that is logged in the Console view when the
+ validator is run. Level 0 ("Only validation results") results in the most concise output, while level 2
+ ("Debug and Execution Info"--the default) is the most verbose.</stentry>
+ </strow>
+ <strow>
+ <stentry><b>Show errors as warnings in Eclipse Problems view</b></stentry>
+ <stentry>If selected, causes validation errors to be downgraded to warnings in the Problems view. This ensures
+ that validation errors don't prevent your app from building.</stentry>
+ </strow>
+ </simpletable>
+ </refbody>
+</reference>
diff --git a/src/help/appvalidator_help/src/topics/u_appvalidator-prefs.dita b/src/help/appvalidator_help/src/topics/u_appvalidator-prefs.dita
new file mode 100644
index 0000000..218720e
--- /dev/null
+++ b/src/help/appvalidator_help/src/topics/u_appvalidator-prefs.dita
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE reference PUBLIC "-//OASIS//DTD DITA Reference//EN" "../dtd/reference.dtd">
+<reference id="u_appvalidator-prefs" xml:lang="en-us">
+ <title>Android App Validator preferences dialog</title>
+ <shortdesc>The Android App Validator preferences dialog enables you to control how the validator operates when invoked
+ from within Eclipse. The Android App Validator preferences dialog can be found among the Eclipse preferences, under
+ <menucascade><uicontrol>MOTODEV Studio</uicontrol><uicontrol>Android App Validator</uicontrol></menucascade>.</shortdesc>
+ <prolog>
+ <metadata>
+ <keywords>
+ <!--<indexterm></indexterm>-->
+ </keywords>
+ </metadata>
+ </prolog>
+ <refbody/>
+</reference>
diff --git a/src/help/appvalidator_help/src/validation_editor.ditamap b/src/help/appvalidator_help/src/validation_editor.ditamap
new file mode 100644
index 0000000..9c323ac
--- /dev/null
+++ b/src/help/appvalidator_help/src/validation_editor.ditamap
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE map PUBLIC "-//OASIS//DTD DITA Map//EN" "../dtd/map.dtd">
+<!--<map title="Android App Validator User Guide" id="com.motorola.studio.android.tooldocs.validator.helpbase">-->
+<map title="Android App Validator User Guide" id="com.motorolamobility.preflighting.tooldocs.validator.helpbase">
+ <topicmeta>
+ <copyright>
+ <copyryear year="2010-2012"/>
+ <copyrholder>Motorola Mobility, Inc.</copyrholder>
+ </copyright>
+ </topicmeta>
+ <topichead navtitle="Tasks">
+ <topicref href="topics/t_validate-about.dita">
+ <topicref href="topics/t_app-validating-gui.dita"/>
+ <topicref href="topics/t_app-validating-command-line.dita"/>
+ </topicref>
+ <topicref href="topics/c_appvalidator-conditions.dita"/>
+ </topichead>
+ <topichead navtitle="Reference" type="reference">
+ <topicref href="topics/u_appvalidator-prefs.dita">
+ <topicref href="topics/u_appvalidator-prefs-general.dita"/>
+ <topicref href="topics/u_appvalidator-prefs-checkers.dita"/>
+ <topicref href="topics/u_appvalidator-prefs-devices.dita"/>
+ </topicref>
+ <topicref href="topics/r_appvalidator-cmdline.dita"/>
+ </topichead>
+ <reltable title="Uni-directional links (topics in col 1 show links to topics in col 2)">
+ <relheader>
+ <relcolspec linking="sourceonly"/>
+ <relcolspec/>
+ </relheader>
+ <relrow>
+ <relcell>
+ <topicref href="topics/u_appvalidator-prefs-checkers.dita"/>
+ </relcell>
+ <relcell>
+ <topicref href="topics/c_appvalidator-conditions.dita"/>
+ </relcell>
+ </relrow>
+ </reltable>
+ <reltable title="Bi-directional links (topics in col 1 and 2 show links to each other)">
+ <relheader>
+ <relcolspec/>
+ <relcolspec/>
+ </relheader>
+ <relrow>
+ <relcell>
+ <topicref href="topics/t_app-validating-command-line.dita"/>
+ </relcell>
+ <relcell>
+ <topicref href="topics/t_app-validating-gui.dita"/>
+ </relcell>
+ </relrow>
+ <relrow>
+ <relcell>
+ <topicref href="topics/t_app-validating-command-line.dita"/>
+ </relcell>
+ <relcell>
+ <topicref href="topics/r_appvalidator-cmdline.dita"/>
+ </relcell>
+ </relrow>
+ <relrow>
+ <relcell>
+ <topicref href="topics/t_app-validating-gui.dita"/>
+ </relcell>
+ <relcell>
+ <topicref href="topics/u_appvalidator-prefs.dita"/>
+ </relcell>
+ </relrow>
+ </reltable>
+</map>
diff --git a/src/help/appvalidator_help/tools/ditaot/plugins/com.mot.mdb.deved.xhtml/plugin.xml b/src/help/appvalidator_help/tools/ditaot/plugins/com.mot.mdb.deved.xhtml/plugin.xml
new file mode 100644
index 0000000..31ff52a
--- /dev/null
+++ b/src/help/appvalidator_help/tools/ditaot/plugins/com.mot.mdb.deved.xhtml/plugin.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+ This is the plugin file that controls integration into the toolkit.
+ Each item in the plugin that extends a toolkit function must be listed
+ here. The only critical component is the catalog file, which must
+ extend the Toolkit's catalog in order for your files to be processed.
+
+ If nothing else is overridden, then the toolkit will use fallback processing.
+ This plugin implements overrides for XHTML (including Eclipse help) produced
+ by Motorola Dev Ed.
+
+ NOTE: paths in this file are relative to the current directory
+ (the same directory that includes this file).
+-->
+
+<plugin id="com.mot.mdb.deved.xhtml">
+
+ <!-- Extend the toolkit's XHTML processing to override XHTML output. -->
+ <feature extension="dita.xsl.xhtml" value="xsl/deved_xhtml.xsl" type="file"/>
+
+ <feature extension="dita.xsl.eclipse.plugin" value="xsl/deved_map2plugin.xsl" type="file"/>
+
+ <!-- ************************************************************
+ Any other extensions to standard toolkit files will be listed here.
+ For example, to extend the RTF transform:
+
+ <feature extension="dita.xsl.rtf" value="xsl/music2rtf.xsl" type="file"/>
+
+ To extend the docbook transform:
+ <feature extension="dita.xsl.docbook" value="xsl/music2rtf.xsl" type="file"/>
+
+ -->
+
+</plugin>
diff --git a/src/help/appvalidator_help/tools/ditaot/plugins/com.mot.mdb.deved.xhtml/readme.dita b/src/help/appvalidator_help/tools/ditaot/plugins/com.mot.mdb.deved.xhtml/readme.dita
new file mode 100644
index 0000000..9bfbdb7
--- /dev/null
+++ b/src/help/appvalidator_help/tools/ditaot/plugins/com.mot.mdb.deved.xhtml/readme.dita
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE reference PUBLIC "-//OASIS//DTD DITA Reference//EN"
+ "reference.dtd">
+
+<!-- (C) Copyright IBM Corporation 2006 All Rights Reserved. -->
+<reference id="readme" xml:lang="en-us">
+ <title>Motorola Dev Ed XHTML processing overrides</title>
+ <shortdesc>This DITA Open Toolkit plug-in implements overrides to standard XHTML processing that Motorola Dev Ed
+ uses in all such output.</shortdesc>
+ <refbody>
+
+
+
+
+
+ </refbody>
+</reference>
diff --git a/src/help/appvalidator_help/tools/ditaot/plugins/com.mot.mdb.deved.xhtml/xsl/deved_map2plugin.xsl b/src/help/appvalidator_help/tools/ditaot/plugins/com.mot.mdb.deved.xhtml/xsl/deved_map2plugin.xsl
new file mode 100644
index 0000000..d1b3cfc
--- /dev/null
+++ b/src/help/appvalidator_help/tools/ditaot/plugins/com.mot.mdb.deved.xhtml/xsl/deved_map2plugin.xsl
@@ -0,0 +1,122 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
+
+<xsl:import href="../../../xsl/map2pluginImpl.xsl"/>
+
+ <!-- -->
+ <!-- BEGIN OVERRIDE: USE CRLF RATHER THAN LF IN MANIFEST.MF -->
+ <!-- 2009.06.26 BG: Appears that Eclipse 3.5 requires this, so
+ I added explicit CRLF characters. Be careful not to change
+ the whitespace within this variable.-->
+ <!-- -->
+ <xsl:variable name="newline">
+<xsl:text>&#13;&#10;</xsl:text></xsl:variable>
+ <!-- END OVERRIDE: USE CRLF RATHER THAN LF IN MANIFEST.MF -->
+
+ <!-- -->
+ <!-- BEGIN OVERRIDE: PLUGIN.PROPERTIES -->
+ <!-- 2010.02.16 BG: Engineering requested we change name property to pluginName -->
+ <!-- -->
+ <xsl:template match="*[contains(@class, ' map/map ')]" mode="eclipse.properties">
+
+ <xsl:text># NLS_MESSAGEFORMAT_NONE</xsl:text><xsl:value-of select="$newline"/>
+ <xsl:text># NLS_ENCODING=UTF-8</xsl:text><xsl:value-of select="$newline"/>
+ <!--<xsl:value-of select="$newline"/>-->
+ <xsl:choose>
+ <xsl:when test="@title">
+ <xsl:text>pluginName=</xsl:text><xsl:value-of select="@title"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:text>name=Sample Title</xsl:text>
+ </xsl:otherwise>
+ </xsl:choose>
+ <xsl:value-of select="$newline"/>
+ <xsl:text>providerName=</xsl:text><xsl:value-of select="$provider"/>
+ </xsl:template>
+ <!-- END OVERRIDE: PLUGIN.PROPERTIES -->
+
+
+ <!-- -->
+ <!-- BEGIN OVERRIDE: MANIFEST.MF -->
+ <!-- 2010.02.18 BG: Engineering requested we change "name" property to "pluginName",
+ "Eclipse-LazyStart: true" to "Bundle-ActivationPolicy: lazy", and add
+ "Bundle-RequiredExecutionEnvironment: J2SE-1.5".-->
+ <!-- -->
+<xsl:template match="*[contains(@class, ' map/map ')]" mode="eclipse.manifest">
+
+ <xsl:text>Bundle-Version: </xsl:text><xsl:value-of select="$version"/><xsl:value-of select="$newline"/>
+ <xsl:text>Manifest-Version: 1.0</xsl:text><xsl:value-of select="$newline"/>
+ <xsl:text>Bundle-ManifestVersion: 2</xsl:text><xsl:value-of select="$newline"/>
+ <xsl:text>Bundle-Localization: plugin</xsl:text><xsl:value-of select="$newline"/>
+ <xsl:text>Bundle-Name: %pluginName</xsl:text><xsl:value-of select="$newline"/>
+ <xsl:text>Bundle-Vendor: %providerName</xsl:text><xsl:value-of select="$newline"/>
+
+ <xsl:choose>
+ <xsl:when test="$plugin='true'">
+ <xsl:text>Bundle-ActivationPolicy: lazy</xsl:text><xsl:value-of select="$newline"/>
+ <xsl:choose>
+ <xsl:when test="@id">
+ <xsl:text>Bundle-SymbolicName: </xsl:text><xsl:value-of select="@id"/>;<xsl:text> singleton:=true</xsl:text><xsl:value-of select="$newline"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:text>Bundle-SymbolicName: org.sample.help.doc; singleton:=true</xsl:text><xsl:value-of select="$newline"/>
+ <xsl:call-template name="output-message">
+ <xsl:with-param name="msgnum">050</xsl:with-param>
+ <xsl:with-param name="msgsev">W</xsl:with-param>
+ </xsl:call-template>
+ </xsl:otherwise>
+ </xsl:choose>
+ <xsl:text>Bundle-RequiredExecutionEnvironment: J2SE-1.5</xsl:text><xsl:value-of select="$newline"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:choose>
+ <xsl:when test="@id">
+ <xsl:if test="$fragment.lang!=''">
+ <xsl:text>Fragment-Host: </xsl:text><xsl:value-of select="@id"/>;
+ <xsl:text>Bundle-SymbolicName: </xsl:text>
+ <xsl:choose>
+ <xsl:when test="$fragment.country!=''">
+ <xsl:value-of select="@id"/>.<xsl:value-of select="$fragment.lang"/>.<xsl:value-of select="$fragment.country"/>;<xsl:text/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="@id"/>.<xsl:value-of select="$fragment.lang"/>;<xsl:text/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:if>
+ <xsl:if test="$fragment.lang=''">
+ <xsl:text>Bundle-SymbolicName: </xsl:text><xsl:value-of select="@id"/><xsl:value-of select="$newline"/>
+ </xsl:if>
+ </xsl:when>
+ <xsl:otherwise>
+
+ <xsl:text>Bundle-SymbolicName: org.sample.help.doc.</xsl:text>
+ <xsl:choose>
+ <xsl:when test="$fragment.lang!=''">
+ <xsl:choose>
+ <xsl:when test="$fragment.country!=''">
+ <xsl:value-of select="$fragment.lang"/>.<xsl:value-of select="$fragment.country"/>;
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="$fragment.lang"/>;
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:when>
+ <!-- We shouldn' t be getting here, but just in case -->
+ <xsl:otherwise>
+ <xsl:text>lang; </xsl:text>
+ </xsl:otherwise>
+ </xsl:choose>
+ <xsl:value-of select="$newline"/>
+ <xsl:text>Fragment-Host: org.sample.help.doc;</xsl:text><xsl:value-of select="$newline"/>
+ <xsl:call-template name="output-message">
+ <xsl:with-param name="msgnum">050</xsl:with-param>
+ <xsl:with-param name="msgsev">W</xsl:with-param>
+ </xsl:call-template>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+ <!-- END OVERRIDE: MANIFEST.MF -->
+
+</xsl:stylesheet>
diff --git a/src/help/appvalidator_help/tools/ditaot/plugins/com.mot.mdb.deved.xhtml/xsl/deved_xhtml.xsl b/src/help/appvalidator_help/tools/ditaot/plugins/com.mot.mdb.deved.xhtml/xsl/deved_xhtml.xsl
new file mode 100644
index 0000000..db9530c
--- /dev/null
+++ b/src/help/appvalidator_help/tools/ditaot/plugins/com.mot.mdb.deved.xhtml/xsl/deved_xhtml.xsl
@@ -0,0 +1,273 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
+
+<!--<xsl:import href="../../../xsl/xslhtml/dita2htmlImpl.xsl"/>-->
+
+ <!-- -->
+ <!-- BEGIN OVERRIDE: DON'T REQUIRE @TMCLASS VALUE TO RENDER TRADEMARK -->
+ <!-- Change trademark logic to remove test for specific values of @tmclass, so symbol appears regardless of @tmclass. -->
+ <!-- -->
+ <xsl:template match="*[contains(@class,' topic/tm ')]" name="topic.tm">
+
+ <xsl:apply-templates/>
+ <!-- output the TM content -->
+
+ <xsl:variable name="Ltmclass">
+ <xsl:call-template name="convert-to-lower">
+ <!-- ensure lowercase for comparisons -->
+ <xsl:with-param name="inputval" select="@tmclass"/>
+ </xsl:call-template>
+ </xsl:variable>
+ <!-- If this is a good class, continue... -->
+ <!-- Commented out <xsl:if> test for @tmclass value, so processing continues even if no value is specified. BG 2008.11.25. -->
+ <!-- <xsl:if test="$Ltmclass='ibm' or $Ltmclass='ibmsub' or $Ltmclass='special'">-->
+ <!-- Test for TM area's language -->
+ <xsl:variable name="tmtest">
+ <xsl:call-template name="tm-area"/>
+ </xsl:variable>
+
+ <!-- If this language should get trademark markers, continue... -->
+ <xsl:if test="$tmtest='tm'">
+ <xsl:variable name="tmvalue">
+ <xsl:value-of select="@trademark"/>
+ </xsl:variable>
+
+ <!-- Determine if this is in a title, and should be marked -->
+ <xsl:variable name="usetitle">
+ <xsl:if
+ test="ancestor::*[contains(@class,' topic/title ')]/parent::*[contains(@class,' topic/topic ')]">
+ <xsl:choose>
+ <!-- Not the first one in a title -->
+ <xsl:when test="generate-id(.)!=generate-id(key('tm',.)[1])">skip</xsl:when>
+ <!-- First one in the topic, BUT it appears in a shortdesc or body -->
+ <xsl:when
+ test="//*[contains(@class,' topic/shortdesc ') or contains(@class,' topic/body ')]//*[contains(@class,' topic/tm ')][@trademark=$tmvalue]"
+ >skip</xsl:when>
+ <xsl:otherwise>use</xsl:otherwise>
+ </xsl:choose>
+ </xsl:if>
+ </xsl:variable>
+
+ <!-- Determine if this is in a body, and should be marked -->
+ <xsl:variable name="usebody">
+ <xsl:choose>
+ <!-- If in a title or prolog, skip -->
+ <xsl:when
+ test="ancestor::*[contains(@class,' topic/title ') or contains(@class,' topic/prolog ')]/parent::*[contains(@class,' topic/topic ')]"
+ >skip</xsl:when>
+ <!-- If first in the document, use it -->
+ <xsl:when test="generate-id(.)=generate-id(key('tm',.)[1])">use</xsl:when>
+ <!-- If there is another before this that is in the body or shortdesc, skip -->
+ <xsl:when
+ test="preceding::*[contains(@class,' topic/tm ')][@trademark=$tmvalue][ancestor::*[contains(@class,' topic/body ') or contains(@class,' topic/shortdesc ')]]"
+ >skip</xsl:when>
+ <!-- Otherwise, any before this must be in a title or ignored section -->
+ <xsl:otherwise>use</xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+
+ <!-- If it should be used in a title or used in the body, output your favorite TM marker based on the attributes -->
+ <xsl:if test="$usetitle='use' or $usebody='use'">
+ <xsl:choose>
+ <!-- ignore @tmtype=service or anything else -->
+ <xsl:when test="@tmtype='tm'">&#x2122;</xsl:when>
+ <!-- Removed superscript from TM symbol. 2008.11.25 BG. -->
+ <xsl:when test="@tmtype='reg'">&#xAE;</xsl:when>
+ <xsl:otherwise/>
+ </xsl:choose>
+ </xsl:if>
+ </xsl:if>
+ <!-- </xsl:if>-->
+ </xsl:template>
+ <!-- END OVERRIDE: DON'T REQUIRE @TMCLASS VALUE TO RENDER TRADEMARK -->
+
+
+
+<!-- -->
+<!-- BEGIN OVERRIDE: REMOVE BR TAG BEFORE/AFTER IMAGES -->
+<!--2009.03.17 bg: Removed br tags before and after images when placement="break".
+ Instead, added a div with class="imageleft" around such images.-->
+<!-- -->
+<!-- =========== IMAGE/OBJECT =========== -->
+<xsl:template match="*[contains(@class,' topic/image ')]" name="topic.image">
+ <xsl:variable name="flagrules">
+ <xsl:call-template name="getrules"/>
+ </xsl:variable>
+ <!-- build any pre break indicated by style -->
+ <xsl:choose>
+ <xsl:when test="parent::fig[contains(@frame,'top ')]">
+ <!-- NOP if there is already a break implied by a parent property -->
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:choose>
+ <!-- 2009.03.17 bg: Removed br tag in next line. -->
+ <xsl:when test="(@placement='break')">
+ <xsl:call-template name="start-flagit">
+ <xsl:with-param name="flagrules" select="$flagrules"></xsl:with-param>
+ </xsl:call-template>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:call-template name="flagcheck"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:otherwise>
+ </xsl:choose>
+ <xsl:call-template name="start-revflag">
+ <xsl:with-param name="flagrules" select="$flagrules"/>
+ </xsl:call-template>
+ <xsl:call-template name="setaname"/>
+ <xsl:choose>
+ <xsl:when test="@placement='break'"><!--Align only works for break-->
+ <xsl:choose>
+ <xsl:when test="@align='left'">
+ <div class="imageleft">
+ <xsl:call-template name="topic-image"/>
+ </div>
+ </xsl:when>
+ <xsl:when test="@align='right'">
+ <div class="imageright">
+ <xsl:call-template name="topic-image"/>
+ </div>
+ </xsl:when>
+ <xsl:when test="@align='center'">
+ <div class="imagecenter">
+ <xsl:call-template name="topic-image"/>
+ </div>
+ </xsl:when>
+ <xsl:otherwise>
+ <!--2009.03.17 bg: If @placement=break and @align isn't set, then style the same as when align is set to left.-->
+ <div class="imageleft">
+ <xsl:call-template name="topic-image"/>
+ </div>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:call-template name="topic-image"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ <xsl:call-template name="end-revflag">
+ <xsl:with-param name="flagrules" select="$flagrules"/>
+ </xsl:call-template>
+ <xsl:call-template name="end-flagit">
+ <xsl:with-param name="flagrules" select="$flagrules"></xsl:with-param>
+ </xsl:call-template>
+ <!-- build any post break indicated by style -->
+ <!-- 2009.03.17 bg: Removed br tag in next line. -->
+ <xsl:if test="not(@placement='inline')"></xsl:if>
+ <!-- image name for review -->
+ <xsl:if test="$ARTLBL='yes'">
+ [<xsl:value-of select="@href"/>]
+ </xsl:if>
+</xsl:template>
+<!-- END OVERRIDE: REMOVE BR TAG BEFORE/AFTER IMAGES -->
+
+
+
+<!-- -->
+<!-- BEGIN OVERRIDE: GLOSSENTRY TOPIC CSS STYLE -->
+<!-- 2009.03.17 bg: Added class="glossentry" to div enclosing glossentry-->
+<!-- -->
+<!-- child topics get a div wrapper and fall through -->
+<xsl:template match="*[contains(@class,' glossentry/glossentry ')]" name="child.topic">
+ <xsl:param name="nestlevel">
+ <xsl:choose>
+ <!-- Limit depth for historical reasons, could allow any depth. Previously limit was 5. -->
+ <xsl:when test="count(ancestor::*[contains(@class,' topic/topic ')]) > 9">9</xsl:when>
+ <xsl:otherwise><xsl:value-of select="count(ancestor::*[contains(@class,' topic/topic ')])"/></xsl:otherwise>
+ </xsl:choose>
+ </xsl:param>
+<div class="glossentry nested{$nestlevel}">
+ <xsl:call-template name="gen-topic"/>
+</div><xsl:value-of select="$newline"/>
+</xsl:template>
+<!-- END OVERRIDE: GLOSSENTRY TOPIC CSS STYLE -->
+
+
+<!-- -->
+<!-- BEGIN OVERRIDE: REMOVED BR BEFORE UNORDERED LISTS -->
+<!-- 2009.03.17 bg: Removed br element before ul-->
+<!-- -->
+<xsl:template match="*[contains(@class,' topic/ul ')]" mode="ul-fmt">
+ <xsl:variable name="flagrules">
+ <xsl:call-template name="getrules"/>
+ </xsl:variable>
+ <xsl:variable name="conflictexist">
+ <xsl:call-template name="conflict-check">
+ <xsl:with-param name="flagrules" select="$flagrules"/>
+ </xsl:call-template>
+ </xsl:variable>
+ <xsl:call-template name="start-flagit">
+ <xsl:with-param name="flagrules" select="$flagrules"></xsl:with-param>
+ </xsl:call-template>
+ <xsl:call-template name="start-revflag">
+ <xsl:with-param name="flagrules" select="$flagrules"/>
+ </xsl:call-template>
+ <xsl:call-template name="setaname"/>
+ <ul>
+ <xsl:call-template name="commonattributes"/>
+ <xsl:call-template name="gen-style">
+ <xsl:with-param name="conflictexist" select="$conflictexist"></xsl:with-param>
+ <xsl:with-param name="flagrules" select="$flagrules"></xsl:with-param>
+ </xsl:call-template>
+ <xsl:apply-templates select="@compact"/>
+ <xsl:call-template name="setid"/>
+ <xsl:apply-templates/>
+ </ul>
+ <xsl:call-template name="end-revflag">
+ <xsl:with-param name="flagrules" select="$flagrules"/>
+ </xsl:call-template>
+ <xsl:call-template name="end-flagit">
+ <xsl:with-param name="flagrules" select="$flagrules"></xsl:with-param>
+ </xsl:call-template>
+ <xsl:value-of select="$newline"/>
+</xsl:template>
+ <!-- END OVERRIDE: REMOVED BR BEFORE UNORDERED LISTS -->
+
+<!-- -->
+<!-- BEGIN OVERRIDE: REMOVED BR BEFORE ORDERED LISTS -->
+<!-- 2009.03.17 bg: Removed br element before ol-->
+<!-- -->
+<xsl:template match="*[contains(@class,' topic/ol ')]" name="topic.ol">
+ <xsl:variable name="flagrules">
+ <xsl:call-template name="getrules"/>
+ </xsl:variable>
+ <xsl:variable name="conflictexist">
+ <xsl:call-template name="conflict-check">
+ <xsl:with-param name="flagrules" select="$flagrules"/>
+ </xsl:call-template>
+ </xsl:variable>
+<xsl:variable name="olcount" select="count(ancestor-or-self::*[contains(@class,' topic/ol ')])"/>
+ <xsl:call-template name="start-flagit">
+ <xsl:with-param name="flagrules" select="$flagrules"></xsl:with-param>
+ </xsl:call-template>
+ <xsl:call-template name="start-revflag">
+ <xsl:with-param name="flagrules" select="$flagrules"/>
+ </xsl:call-template>
+<xsl:call-template name="setaname"/>
+<ol>
+ <xsl:call-template name="commonattributes"/>
+ <xsl:call-template name="gen-style">
+ <xsl:with-param name="conflictexist" select="$conflictexist"></xsl:with-param>
+ <xsl:with-param name="flagrules" select="$flagrules"></xsl:with-param>
+ </xsl:call-template>
+ <xsl:apply-templates select="@compact"/>
+ <xsl:choose>
+ <xsl:when test="$olcount mod 3 = 1"/>
+ <xsl:when test="$olcount mod 3 = 2"><xsl:attribute name="type">a</xsl:attribute></xsl:when>
+ <xsl:otherwise><xsl:attribute name="type">i</xsl:attribute></xsl:otherwise>
+ </xsl:choose>
+ <xsl:call-template name="setid"/>
+ <xsl:apply-templates/>
+</ol>
+ <xsl:call-template name="end-revflag">
+ <xsl:with-param name="flagrules" select="$flagrules"/>
+ </xsl:call-template>
+ <xsl:call-template name="end-flagit">
+ <xsl:with-param name="flagrules" select="$flagrules"></xsl:with-param>
+ </xsl:call-template>
+<xsl:value-of select="$newline"/>
+</xsl:template>
+<!-- END OVERRIDE: REMOVED BR BEFORE ORDERED LISTS -->
+
+</xsl:stylesheet>
diff --git a/src/help/appvalidator_help/tools/ditaot/plugins/cshelp/catalog-dita.xml b/src/help/appvalidator_help/tools/ditaot/plugins/cshelp/catalog-dita.xml
new file mode 100644
index 0000000..e12052c
--- /dev/null
+++ b/src/help/appvalidator_help/tools/ditaot/plugins/cshelp/catalog-dita.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" ?>
+<!-- This file is part of the DITA Open Toolkit project hosted on
+ Sourceforge.net. See the accompanying license.txt file in the
+ main toolkit package for applicable licenses.-->
+<!-- (C) Copyright IBM Corporation 2006 All Rights Reserved. -->
+
+<!--
+ This file declares each DTD module that is part of the specialization.
+ The path is relative to the base toolkit directory.
+-->
+<catalog xmlns="urn:oasis:names:tc:entity:xmlns:xml:catalog"
+ prefer="public">
+
+ <public publicId="-//IBM//DTD DITA CSHelp//EN"
+ uri="plugins/cshelp/dtd/cshelp.dtd"/>
+ <public publicId="-//IBM//ELEMENTS DITA CSHelp//EN"
+ uri="plugins/cshelp/dtd/cshelp.mod"/>
+
+</catalog>
diff --git a/src/help/appvalidator_help/tools/ditaot/plugins/cshelp/dtd/cshelp.dtd b/src/help/appvalidator_help/tools/ditaot/plugins/cshelp/dtd/cshelp.dtd
new file mode 100644
index 0000000..331576f
--- /dev/null
+++ b/src/help/appvalidator_help/tools/ditaot/plugins/cshelp/dtd/cshelp.dtd
@@ -0,0 +1,101 @@
+<?xml encoding="utf-8"?>
+<!--
+ | (C) Copyright IBM Corporation 2005, 2006 All Rights Reserved.
+ | This file is a specialization of DITA 3.0. See license.txt
+ | for disclaimers and permissions.
+ |
+ | This file is part of the DITA Open Toolkit project hosted on
+ | Sourceforge.net. See the accompanying license.txt file for
+ | applicable licenses.
+ |
+ | The Darwin Information Typing Architecture (DITA) was orginated by
+ | IBM's XML Workgroup and ID Workbench tools team.
+ |
+ | Refer to this file by the following public identfier or an appropriate
+ | system identifier:
+ |
+ | PUBLIC "-//IBM//DTD DITA CSHelp//EN"
+ |
+ | Release history (vrm):
+ | 1.0.0 Initial release, December 2005
+ *-->
+
+<!--vocabulary declarations-->
+<!ENTITY % ui-d-dec PUBLIC
+"-//OASIS//ENTITIES DITA User Interface Domain//EN"
+"../../../dtd/uiDomain.ent" >
+%ui-d-dec;
+
+<!ENTITY % hi-d-dec PUBLIC
+"-//OASIS//ENTITIES DITA Highlight Domain//EN"
+"../../../dtd/highlightDomain.ent" >
+%hi-d-dec;
+
+<!ENTITY % pr-d-dec PUBLIC
+"-//OASIS//ENTITIES DITA Programming Domain//EN"
+"../../../dtd/programmingDomain.ent" >
+%pr-d-dec;
+
+<!ENTITY % sw-d-dec PUBLIC
+"-//OASIS//ENTITIES DITA Software Domain//EN"
+"../../../dtd/softwareDomain.ent" >
+%sw-d-dec;
+
+<!ENTITY % ut-d-dec PUBLIC
+"-//OASIS//ENTITIES DITA Utilities Domain//EN"
+"../../../dtd/utilitiesDomain.ent" >
+%ut-d-dec;
+
+
+<!--vocabulary substitution (one for each extended base element,
+ with the name of the domain(s) in which the extension was declared)-->
+<!ENTITY % pre "pre | %pr-d-pre; | %sw-d-pre; | %ui-d-pre;">
+<!ENTITY % keyword "keyword | %pr-d-keyword; | %sw-d-keyword; | %ui-d-keyword;">
+<!ENTITY % ph "ph | %pr-d-ph; | %sw-d-ph; | %hi-d-ph; | %ui-d-ph;">
+<!ENTITY % fig "fig | %pr-d-fig; | %ut-d-fig;">
+<!ENTITY % dl "dl | %pr-d-dl;">
+
+<!--Allow nesting of only cshelp infotype-->
+<!ENTITY % cshelp-info-types "cshelp">
+
+<!--vocabulary attributes (must be declared ahead of the dtds, which puts @domains first in order) -->
+<!ENTITY included-domains "&ui-d-att; &hi-d-att; &pr-d-att; &sw-d-att; &ut-d-att;">
+
+<!--Embed topic to get generic elements -->
+<!ENTITY % topic-type PUBLIC
+"-//OASIS//ELEMENTS DITA Topic//EN"
+"../../../dtd/topic.mod">
+ %topic-type;
+
+<!--Embed reference to get specific elements -->
+<!ENTITY % cshelp-typemod PUBLIC
+"-//IBM//ELEMENTS DITA CSHelp//EN"
+"cshelp.mod">
+ %cshelp-typemod;
+
+
+<!--vocabulary definitions-->
+<!ENTITY % ui-d-def PUBLIC
+"-//OASIS//ELEMENTS DITA User Interface Domain//EN"
+"../../../dtd/uiDomain.mod">
+%ui-d-def;
+
+<!ENTITY % hi-d-def PUBLIC
+"-//OASIS//ELEMENTS DITA Highlight Domain//EN"
+"../../../dtd/highlightDomain.mod">
+%hi-d-def;
+
+<!ENTITY % pr-d-def PUBLIC
+"-//OASIS//ELEMENTS DITA Programming Domain//EN"
+"../../../dtd/programmingDomain.mod">
+%pr-d-def;
+
+<!ENTITY % sw-d-def PUBLIC
+"-//OASIS//ELEMENTS DITA Software Domain//EN"
+"../../../dtd/softwareDomain.mod">
+%sw-d-def;
+
+<!ENTITY % ut-d-def PUBLIC
+"-//OASIS//ELEMENTS DITA Utilities Domain//EN"
+"../../../dtd/utilitiesDomain.mod">
+%ut-d-def;
diff --git a/src/help/appvalidator_help/tools/ditaot/plugins/cshelp/dtd/cshelp.mod b/src/help/appvalidator_help/tools/ditaot/plugins/cshelp/dtd/cshelp.mod
new file mode 100644
index 0000000..66942e3
--- /dev/null
+++ b/src/help/appvalidator_help/tools/ditaot/plugins/cshelp/dtd/cshelp.mod
@@ -0,0 +1,85 @@
+<!--
+ | (C) Copyright IBM Corporation 2005, 2006 All Rights Reserved.
+ | This file is a specialization of DITA 3.0. See license.txt
+ | for disclaimers and permissions.
+ |
+ | This file is part of the DITA Open Toolkit project hosted on
+ | Sourceforge.net. See the accompanying license.txt file for
+ | applicable licenses.
+ |
+ | The Darwin Information Typing Architecture (DITA) was orginated by
+ | IBM's XML Workgroup and ID Workbench tools team.
+ |
+ | Refer to this file by the following public identfier or an appropriate
+ | system identifier:
+ |
+ | PUBLIC "-//IBM//ELEMENTS DITA CSHelp//EN"
+ |
+ | Release history (vrm):
+ | 1.0.0 Initial release, December 2005
+ *-->
+
+<!ENTITY DTDVersion 'V1.1.1' >
+
+
+<!-- Specialization of declared elements -->
+
+<!ENTITY % csprolog "csprolog">
+<!ENTITY % csmetadata "csmetadata">
+<!ENTITY % cswindowtitle "cswindowtitle">
+<!ENTITY % cswidgetlabel "cswidgetlabel">
+<!ENTITY % csbody "csbody">
+<!ENTITY % cshelp-info-types "%info-types;">
+
+<!-- declared here, defined later -->
+<!ENTITY included-domains "">
+
+<!--doc:The <cshelp> element is the top-level element for a topic that corresponds to an Eclipse Help context (a brief description and link that appear after the user presses a Help button). To create one Eclipse context.xml file, create one DITA file with a root element of <cshelp> in which you next further <cshelp> topics, one for each context-sensitive help item. Only the content of the nested elements is output; the root element is a required, but empty container.
+Category: CS Help plug-in elements-->
+<!ELEMENT cshelp (%title;, (%titlealts;)?, (%shortdesc;), (%csprolog;)?, %csbody;, (%related-links;)?, (%cshelp-info-types;)* )>
+<!ATTLIST cshelp id ID #REQUIRED
+ conref CDATA #IMPLIED
+ %select-atts;
+ outputclass CDATA #IMPLIED
+ xml:lang NMTOKEN #IMPLIED
+ DTDVersion CDATA #FIXED "&DTDVersion;"
+ domains CDATA "&included-domains;"
+>
+
+<!--doc:The optional <csprolog> element contains <csmetadata> elements to describe the UI related to this context.
+Category: CS Help plug-in elements-->
+<!ELEMENT csprolog ((%author;)*,(%source;)?,(%publisher;)?,(%copyright;)*,(%critdates;)?,(%permissions;)?,(%csmetadata;)*, (%resourceid;)*)>
+
+<!--doc:The optional <csmetadata> element contains <cswindowtitle> and <cswidgetlabel> elements.
+Category: CS Help plug-in elements-->
+<!ELEMENT csmetadata ((%audience;)*,(%cswindowtitle;)?,(%cswidgetlabel;)?,(%category;)*,(%keywords;)*,(%prodinfo;)*,(%othermeta;)*)>
+
+<!-- define a custom block type with predefined topic content types -->
+
+<!-- txt.incl minus footnotes and index entries -->
+
+<!--doc:The <csbody> element contains the body of the CS Help topic. All the elements allowed in <body> are allowed in <csbody>; however, some result in no output because of Eclipse Help limitations. For example, table, simpletable, image, figure, xref, indexterm, indextermref, footnote, object are not output. Many in-line elements display using the <b> tag. Lists are supported, but nested lists are not recommended.
+Category: CS Help plug-in elements-->
+<!ELEMENT csbody (%body.cnt;)*>
+<!ATTLIST csbody %id-atts;
+ translate (yes|no) #IMPLIED
+ xml:lang NMTOKEN #IMPLIED
+ outputclass CDATA #IMPLIED
+>
+
+<!--doc:The optional <cswindowtitle> element identifies the window associated with this context-sensitive help item. This content does not appear in the output context file.
+Category: CS Help plug-in elements-->
+<!ELEMENT cswindowtitle (#PCDATA)>
+
+<!--doc:The optional <cswidgetlabel> element identifies the UI widget within the window associated with this context-sensitive help item. This content does not appear in the output context file.
+Category: CS Help plug-in elements-->
+<!ELEMENT cswidgetlabel (#PCDATA)>
+
+<!--specialization attributes-->
+
+<!ATTLIST cshelp %global-atts; class CDATA "- topic/topic cshelp/cshelp ">
+<!ATTLIST csbody %global-atts; class CDATA "- topic/body cshelp/csbody ">
+<!ATTLIST csprolog %global-atts; class CDATA "- topic/prolog cshelp/csprolog ">
+<!ATTLIST csmetadata %global-atts; class CDATA "- topic/metadata cshelp/csmetadata ">
+<!ATTLIST cswindowtitle %global-atts; class CDATA "- topic/category cshelp/cswindowtitle ">
+<!ATTLIST cswidgetlabel %global-atts; class CDATA "- topic/category cshelp/cswidgetlabel ">
diff --git a/src/help/appvalidator_help/tools/ditaot/plugins/cshelp/install-plugin.xml b/src/help/appvalidator_help/tools/ditaot/plugins/cshelp/install-plugin.xml
new file mode 100644
index 0000000..d3ac98b
--- /dev/null
+++ b/src/help/appvalidator_help/tools/ditaot/plugins/cshelp/install-plugin.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!-- This file is part of the DITA Open Toolkit project hosted on
+ Sourceforge.net. See the accompanying license.txt file in the
+ main toolkit package for applicable licenses.-->
+<!-- (C) Copyright IBM Corporation 2006 All Rights Reserved. -->
+
+<!--
+ This is a shortcut file that will integrate the plugin, if you do not regularly
+ run the integrator as part of a build.
+
+ Alternatively, from the main toolkit directory, you could integrate with:
+ ant -f integrator.xml
+-->
+
+<project name="cshelp" default="all" basedir="../..">
+ <import file="${basedir}${file.separator}integrator.xml"/>
+
+ <target name="all" depends="integrate"/>
+
+</project>
diff --git a/src/help/appvalidator_help/tools/ditaot/plugins/cshelp/plugin.xml b/src/help/appvalidator_help/tools/ditaot/plugins/cshelp/plugin.xml
new file mode 100644
index 0000000..a4a5d07
--- /dev/null
+++ b/src/help/appvalidator_help/tools/ditaot/plugins/cshelp/plugin.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- This file is part of the DITA Open Toolkit project hosted on
+ Sourceforge.net. See the accompanying license.txt file in the
+ main toolkit package for applicable licenses.-->
+<!-- (C) Copyright IBM Corporation 2006 All Rights Reserved. -->
+
+<plugin id="org.dita.specialization.cshelp">
+ <!-- Extend the toolkit catalog to include DTDs. -->
+ <feature extension="dita.specialization.catalog"
+ value="catalog-dita.xml" type="file"/>
+
+ <!-- Extend the toolkit's XHTML processing to override XHTML output. -->
+ <feature extension="dita.xsl.xhtml"
+ value="xsl/cshdisplay.xsl" type="file"/>
+
+</plugin>
diff --git a/src/help/appvalidator_help/tools/ditaot/plugins/cshelp/samples/test.ditamap b/src/help/appvalidator_help/tools/ditaot/plugins/cshelp/samples/test.ditamap
new file mode 100644
index 0000000..55549f5
--- /dev/null
+++ b/src/help/appvalidator_help/tools/ditaot/plugins/cshelp/samples/test.ditamap
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE map PUBLIC "-//OASIS//DTD DITA Map//EN"
+ "ibm-map.dtd">
+<!-- This file is part of the DITA Open Toolkit project hosted on -->
+<!-- Sourceforge.net. See the accompanying license.txt file for -->
+<!-- applicable licenses. -->
+<map title="CS Help test">
+<topicmeta>
+<copyright>
+<copyryear year="2000"/>
+<copyryear year="2006"/>
+<copyrholder></copyrholder>
+</copyright>
+</topicmeta>
+<topicref href="test_csh_1.dita" navtitle=""></topicref>
+<topicref href="test_csh_2.dita" navtitle=""></topicref>
+</map>
diff --git a/src/help/appvalidator_help/tools/ditaot/plugins/cshelp/samples/test_csh_1.dita b/src/help/appvalidator_help/tools/ditaot/plugins/cshelp/samples/test_csh_1.dita
new file mode 100644
index 0000000..f1b729b
--- /dev/null
+++ b/src/help/appvalidator_help/tools/ditaot/plugins/cshelp/samples/test_csh_1.dita
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE cshelp PUBLIC "-//IBM//DTD DITA CSHelp//EN"
+ "..\dtd\cshelp.dtd">
+<!-- This file is part of the DITA Open Toolkit project hosted on -->
+<!-- Sourceforge.net. See the accompanying license.txt file for -->
+<!-- applicable licenses. -->
+<cshelp id="test_csh_1" xml:lang="en-us">
+<title>Test CSH file</title>
+<shortdesc></shortdesc>
+<csbody></csbody>
+<cshelp id="contextId1" xml:lang="en-us">
+<title>My First CSH topic</title>
+<shortdesc>This is the first sentence of my context-sensitive help topic.</shortdesc>
+<csprolog><csmetadata>
+<cswindowtitle>(optional) The name of the window or view discussed by this
+CSH topic.</cswindowtitle>
+<cswidgetlabel>(optional) The name of the control discussed by this CSH topic.</cswidgetlabel>
+</csmetadata></csprolog>
+<csbody>
+<p>Here is the second sentence of my cshelp topic.</p>
+</csbody>
+<related-links>
+<link format="dita" href="../com.mycompany.myplugin/a.dita" scope="peer"><linktext>Link
+text</linktext></link>
+</related-links>
+</cshelp>
+<cshelp id="contextId2" xml:lang="en-us">
+<title>Another CSH topic</title>
+<shortdesc>This is the first sentence of another context-sensitive help topic.</shortdesc>
+<csbody>
+<p>This is the second sentence of my cshelp topic.</p>
+</csbody>
+<related-links>
+<link format="dita" href="../com.mycompany.myplugin/a.dita" scope="peer"><linktext>Link
+text</linktext></link>
+</related-links>
+</cshelp>
+<cshelp id="contextId3" xml:lang="en-us">
+<title>Third CSH topic</title>
+<shortdesc>Here is the third CSH topic.</shortdesc>
+<csbody></csbody>
+<related-links>
+<link format="dita" href="../com.mycompany.myplugin/b.dita" scope="peer"><linktext>Link
+text</linktext></link>
+</related-links>
+</cshelp>
+</cshelp>
diff --git a/src/help/appvalidator_help/tools/ditaot/plugins/cshelp/samples/test_csh_2.dita b/src/help/appvalidator_help/tools/ditaot/plugins/cshelp/samples/test_csh_2.dita
new file mode 100644
index 0000000..e797c89
--- /dev/null
+++ b/src/help/appvalidator_help/tools/ditaot/plugins/cshelp/samples/test_csh_2.dita
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE cshelp PUBLIC "-//IBM//DTD DITA CSHelp//EN"
+ "..\dtd\cshelp.dtd">
+<!-- This file is part of the DITA Open Toolkit project hosted on -->
+<!-- Sourceforge.net. See the accompanying license.txt file for -->
+<!-- applicable licenses. -->
+<cshelp id="test_csh_1" xml:lang="en-us">
+<title>Second CSH file</title>
+<shortdesc></shortdesc>
+<csbody></csbody>
+<cshelp id="contextId4" xml:lang="en-us">
+<title>CSH topic four</title>
+<shortdesc>This is the first sentence of context-sensitive help topic four.</shortdesc>
+<csbody>
+<p>Here is the second sentence.</p>
+</csbody>
+<related-links>
+<link format="dita" href="../com.mycompany.myplugin/a.dita" scope="peer"><linktext>Link
+text</linktext></link>
+</related-links>
+</cshelp>
+<cshelp id="contextId5" xml:lang="en-us">
+<title>CSH topic five</title>
+<shortdesc>This is the first sentence of context-sensitive help topic five.</shortdesc>
+<csbody>
+<p>This is the second sentence.</p>
+</csbody>
+<related-links>
+<link format="dita" href="../com.mycompany.myplugin/a.dita" scope="peer"><linktext>Link
+text</linktext></link>
+</related-links>
+</cshelp>
+</cshelp>
diff --git a/src/help/appvalidator_help/tools/ditaot/plugins/cshelp/xsl/GetCSHMeta.xsl b/src/help/appvalidator_help/tools/ditaot/plugins/cshelp/xsl/GetCSHMeta.xsl
new file mode 100644
index 0000000..d69cc01
--- /dev/null
+++ b/src/help/appvalidator_help/tools/ditaot/plugins/cshelp/xsl/GetCSHMeta.xsl
@@ -0,0 +1,591 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!-- (c) Copyright IBM Corp. 2005, 2006 All Rights Reserved. -->
+<!-- This file is part of the DITA Open Toolkit project hosted on -->
+<!-- Sourceforge.net. See the accompanying license.txt file for -->
+<!-- applicable licenses. -->
+
+<xsl:stylesheet version="1.0"
+ xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+
+<!-- Metadata conversions from DITA to Eclipse context file XML -->
+
+<xsl:key name="meta-keywords" match="*[ancestor::*[contains(@class,' topic/keywords ')]]" use="text()"/>
+
+<xsl:template name="getCSHMeta">
+
+ <!-- = = = = = = = = = = = CONTENT = = = = = = = = = = = -->
+
+ <!-- CONTENT: Type -->
+ <xsl:apply-templates select="." mode="gen-type-metadata"/>
+
+ <!-- CONTENT: Title - title -->
+ <xsl:apply-templates select="*[contains(@class,' topic/title ')] |
+ self::dita/*[1]/*[contains(@class,' topic/title ')]" mode="gen-metadata"/>
+
+ <!-- CONTENT: Description - shortdesc -->
+ <xsl:apply-templates select="*[contains(@class,' topic/shortdesc ')] |
+ self::dita/*[1]/*[contains(@class,' topic/shortdesc ')]" mode="gen-metadata"/>
+
+ <!-- CONTENT: Source - prolog/source/@href -->
+ <xsl:apply-templates select="*[contains(@class,' topic/prolog ')]/*[contains(@class,' topic/source ')]/@href |
+ self::dita/*[1]/*[contains(@class,' topic/prolog ')]/*[contains(@class,' topic/source ')]/@href" mode="gen-metadata"/>
+
+ <!-- CONTENT: Coverage prolog/metadata/category -->
+ <xsl:apply-templates select="*[contains(@class,' topic/prolog ')]/*[contains(@class,' topic/metadata ')]/*[contains(@class,' topic/category ')] |
+ self::dita/*[1]/*[contains(@class,' topic/prolog ')]/*[contains(@class,' topic/metadata ')]/*[contains(@class,' topic/category ')]" mode="gen-metadata"/>
+
+ <!-- CONTENT: Subject - prolog/metadata/keywords -->
+ <xsl:apply-templates select="." mode="gen-keywords-metadata"/>
+
+ <!-- CONTENT: Relation - related-links -->
+ <xsl:apply-templates select="*[contains(@class,' topic/related-links ')]/descendant::*/@href |
+ self::dita/*/*[contains(@class,' topic/related-links ')]/descendant::*/@href" mode="gen-metadata"/>
+
+ <!-- = = = = = = = = = = = Product - Audience = = = = = = = = = = = -->
+ <!-- Audience -->
+ <!-- prolog/metadata/audience/@experiencelevel and other attributes -->
+ <xsl:apply-templates select="*[contains(@class,' topic/prolog ')]/*[contains(@class,' topic/metadata ')]/*[contains(@class,' topic/audience ')]/@experiencelevel |
+ self::dita/*[1]/*[contains(@class,' topic/prolog ')]/*[contains(@class,' topic/metadata ')]/*[contains(@class,' topic/audience ')]/@experiencelevel" mode="gen-metadata"/>
+ <xsl:apply-templates select="*[contains(@class,' topic/prolog ')]/*[contains(@class,' topic/metadata ')]/*[contains(@class,' topic/audience ')]/@importance |
+ self::dita/*[1]/*[contains(@class,' topic/prolog ')]/*[contains(@class,' topic/metadata ')]/*[contains(@class,' topic/audience ')]/@importance" mode="gen-metadata"/>
+ <xsl:apply-templates select="*[contains(@class,' topic/prolog ')]/*[contains(@class,' topic/metadata ')]/*[contains(@class,' topic/audience ')]/@job |
+ self::dita/*[1]/*[contains(@class,' topic/prolog ')]/*[contains(@class,' topic/metadata ')]/*[contains(@class,' topic/audience ')]/@job" mode="gen-metadata"/>
+ <xsl:apply-templates select="*[contains(@class,' topic/prolog ')]/*[contains(@class,' topic/metadata ')]/*[contains(@class,' topic/audience ')]/@name |
+ self::dita/*[1]/*[contains(@class,' topic/prolog ')]/*[contains(@class,' topic/metadata ')]/*[contains(@class,' topic/audience ')]/@name" mode="gen-metadata"/>
+ <xsl:apply-templates select="*[contains(@class,' topic/prolog ')]/*[contains(@class,' topic/metadata ')]/*[contains(@class,' topic/audience ')]/@type |
+ self::dita/*[1]/*[contains(@class,' topic/prolog ')]/*[contains(@class,' topic/metadata ')]/*[contains(@class,' topic/audience ')]/@type" mode="gen-metadata"/>
+
+
+ <!-- <prodname> -->
+ <xsl:apply-templates select="*[contains(@class,' topic/prolog ')]/*[contains(@class,' topic/metadata ')]/*[contains(@class,' topic/prodinfo ')]/*[contains(@class,' topic/prodname ')] |
+ self::dita/*[1]/*[contains(@class,' topic/prolog ')]/*[contains(@class,' topic/metadata ')]/*[contains(@class,' topic/prodinfo ')]/*[contains(@class,' topic/prodname ')]" mode="gen-metadata"/>
+
+ <!-- <vrmlist><vrm modification="3" release="2" version="5"/></vrmlist> -->
+ <xsl:apply-templates select="*[contains(@class,' topic/prolog ')]/*[contains(@class,' topic/metadata ')]/*[contains(@class,' topic/prodinfo ')]/*[contains(@class,' topic/vrmlist ')]/*[contains(@class,' topic/vrm ')]/@version |
+ self::dita/*[1]/*[contains(@class,' topic/prolog ')]/*[contains(@class,' topic/metadata ')]/*[contains(@class,' topic/prodinfo ')]/*[contains(@class,' topic/vrmlist ')]/*[contains(@class,' topic/vrm ')]/@version" mode="gen-metadata"/>
+ <xsl:apply-templates select="*[contains(@class,' topic/prolog ')]/*[contains(@class,' topic/metadata ')]/*[contains(@class,' topic/prodinfo ')]/*[contains(@class,' topic/vrmlist ')]/*[contains(@class,' topic/vrm ')]/@release |
+ self::dita/*[1]/*[contains(@class,' topic/prolog ')]/*[contains(@class,' topic/metadata ')]/*[contains(@class,' topic/prodinfo ')]/*[contains(@class,' topic/vrmlist ')]/*[contains(@class,' topic/vrm ')]/@release" mode="gen-metadata"/>
+ <xsl:apply-templates select="*[contains(@class,' topic/prolog ')]/*[contains(@class,' topic/metadata ')]/*[contains(@class,' topic/prodinfo ')]/*[contains(@class,' topic/vrmlist ')]/*[contains(@class,' topic/vrm ')]/@modification |
+ self::dita/*[1]/*[contains(@class,' topic/prolog ')]/*[contains(@class,' topic/metadata ')]/*[contains(@class,' topic/prodinfo ')]/*[contains(@class,' topic/vrmlist ')]/*[contains(@class,' topic/vrm ')]/@modification" mode="gen-metadata"/>
+
+ <!-- <brand> -->
+ <xsl:apply-templates select="*[contains(@class,' topic/prolog ')]/*[contains(@class,' topic/metadata ')]/*[contains(@class,' topic/prodinfo ')]/*[contains(@class,' topic/brand ')] |
+ self::dita/*[1]/*[contains(@class,' topic/prolog ')]/*[contains(@class,' topic/metadata ')]/*[contains(@class,' topic/prodinfo ')]/*[contains(@class,' topic/brand ')]" mode="gen-metadata"/>
+ <!-- <component> -->
+ <xsl:apply-templates select="*[contains(@class,' topic/prolog ')]/*[contains(@class,' topic/metadata ')]/*[contains(@class,' topic/prodinfo ')]/*[contains(@class,' topic/component ')] |
+ self::dita/*[1]/*[contains(@class,' topic/prolog ')]/*[contains(@class,' topic/metadata ')]/*[contains(@class,' topic/prodinfo ')]/*[contains(@class,' topic/component ')]" mode="gen-metadata"/>
+ <!-- <featnum> -->
+ <xsl:apply-templates select="*[contains(@class,' topic/prolog ')]/*[contains(@class,' topic/metadata ')]/*[contains(@class,' topic/prodinfo ')]/*[contains(@class,' topic/featnum ')] |
+ self::dita/*[1]/*[contains(@class,' topic/prolog ')]/*[contains(@class,' topic/metadata ')]/*[contains(@class,' topic/prodinfo ')]/*[contains(@class,' topic/featnum ')]" mode="gen-metadata"/>
+ <!-- <prognum> -->
+ <xsl:apply-templates select="*[contains(@class,' topic/prolog ')]/*[contains(@class,' topic/metadata ')]/*[contains(@class,' topic/prodinfo ')]/*[contains(@class,' topic/prognum ')] |
+ self::dita/*[1]/*[contains(@class,' topic/prolog ')]/*[contains(@class,' topic/metadata ')]/*[contains(@class,' topic/prodinfo ')]/*[contains(@class,' topic/prognum ')]" mode="gen-metadata"/>
+ <!-- <platform> -->
+ <xsl:apply-templates select="*[contains(@class,' topic/prolog ')]/*[contains(@class,' topic/metadata ')]/*[contains(@class,' topic/prodinfo ')]/*[contains(@class,' topic/platform ')] |
+ self::dita/*[1]/*[contains(@class,' topic/prolog ')]/*[contains(@class,' topic/metadata ')]/*[contains(@class,' topic/prodinfo ')]/*[contains(@class,' topic/platform ')]" mode="gen-metadata"/>
+ <!-- <series> -->
+ <xsl:apply-templates select="*[contains(@class,' topic/prolog ')]/*[contains(@class,' topic/metadata ')]/*[contains(@class,' topic/prodinfo ')]/*[contains(@class,' topic/series ')] |
+ self::dita/*[1]/*[contains(@class,' topic/prolog ')]/*[contains(@class,' topic/metadata ')]/*[contains(@class,' topic/prodinfo ')]/*[contains(@class,' topic/series ')]" mode="gen-metadata"/>
+
+ <!-- = = = = = = = = = = = INTELLECTUAL PROPERTY = = = = = = = = = = = -->
+
+ <!-- INTELLECTUAL PROPERTY: Contributor - prolog/author -->
+ <!-- INTELLECTUAL PROPERTY: Creator -->
+ <xsl:apply-templates select="*[contains(@class,' topic/prolog ')]/*[contains(@class,' topic/author ')] |
+ self::dita/*[1]/*[contains(@class,' topic/prolog ')]/*[contains(@class,' topic/author ')]" mode="gen-metadata"/>
+
+ <!-- INTELLECTUAL PROPERTY: Publisher - prolog/publisher -->
+ <xsl:apply-templates select="*[contains(@class,' topic/prolog ')]/*[contains(@class,' topic/publisher ')] |
+ self::dita/*[1]/*[contains(@class,' topic/prolog ')]/*[contains(@class,' topic/publisher ')]" mode="gen-metadata"/>
+
+ <!-- INTELLECTUAL PROPERTY: Rights - prolog/copyright -->
+ <!-- Put primary first, then secondary, then remainder -->
+ <xsl:apply-templates select="*[contains(@class,' topic/prolog ')]/*[contains(@class,' topic/copyright ')][@type='primary'] |
+ self::dita/*[1]/*[contains(@class,' topic/prolog ')]/*[contains(@class,' topic/copyright ')][@type='primary']" mode="gen-metadata"/>
+ <xsl:apply-templates select="*[contains(@class,' topic/prolog ')]/*[contains(@class,' topic/copyright ')][@type='secondary'] |
+ self::dita/*[1]/*[contains(@class,' topic/prolog ')]/*[contains(@class,' topic/copyright ')][@type='seconday']" mode="gen-metadata"/>
+ <xsl:apply-templates select="*[contains(@class,' topic/prolog ')]/*[contains(@class,' topic/copyright ')][not(@type)] |
+ self::dita/*[1]/*[contains(@class,' topic/prolog ')]/*[contains(@class,' topic/copyright ')][not(@type)]" mode="gen-metadata"/>
+
+ <!-- Usage Rights - prolog/permissions -->
+ <xsl:apply-templates select="*[contains(@class,' topic/prolog ')]/*[contains(@class,' topic/permissions ')] |
+ self::dita/*[1]/*[contains(@class,' topic/prolog ')]/*[contains(@class,' topic/permissions ')]" mode="gen-metadata"/>
+
+ <!-- = = = = = = = = = = = INSTANTIATION = = = = = = = = = = = -->
+
+ <!-- INSTANTIATION: Date - prolog/critdates/created -->
+ <xsl:apply-templates select="*[contains(@class,' topic/prolog ')]/*[contains(@class,' topic/critdates ')]/*[contains(@class,' topic/created ')] |
+ self::dita/*[1]/*[contains(@class,' topic/prolog ')]/*[contains(@class,' topic/critdates ')]/*[contains(@class,' topic/created ')]" mode="gen-metadata"/>
+
+ <!-- prolog/critdates/revised/@modified -->
+ <xsl:apply-templates select="*[contains(@class,' topic/prolog ')]/*[contains(@class,' topic/critdates ')]/*[contains(@class,' topic/revised ')]/@modified |
+ self::dita/*[1]/*[contains(@class,' topic/prolog ')]/*[contains(@class,' topic/critdates ')]/*[contains(@class,' topic/revised ')]/@modified" mode="gen-metadata"/>
+
+ <!-- prolog/critdates/revised/@golive -->
+ <xsl:apply-templates select="*[contains(@class,' topic/prolog ')]/*[contains(@class,' topic/critdates ')]/*[contains(@class,' topic/revised ')]/@golive |
+ self::dita/*[1]/*[contains(@class,' topic/prolog ')]/*[contains(@class,' topic/critdates ')]/*[contains(@class,' topic/revised ')]/@golive" mode="gen-metadata"/>
+
+ <!-- prolog/critdates/revised/@expiry -->
+ <xsl:apply-templates select="*[contains(@class,' topic/prolog ')]/*[contains(@class,' topic/critdates ')]/*[contains(@class,' topic/revised ')]/@expiry |
+ self::dita/*[1]/*[contains(@class,' topic/prolog ')]/*[contains(@class,' topic/critdates ')]/*[contains(@class,' topic/revised ')]/@expiry" mode="gen-metadata"/>
+
+ <!-- prolog/metadata/othermeta -->
+ <xsl:apply-templates select="*[contains(@class,' topic/prolog ')]/*[contains(@class,' topic/metadata ')]/*[contains(@class,' topic/othermeta ')] |
+ self::dita/*[1]/*[contains(@class,' topic/prolog ')]/*[contains(@class,' topic/metadata ')]/*[contains(@class,' topic/othermeta ')]" mode="gen-metadata"/>
+
+ <!-- INSTANTIATION: Format -->
+ <xsl:apply-templates select="." mode="gen-format-metadata"/>
+
+ <!-- INSTANTIATION: Identifier --> <!-- id is an attribute on Topic -->
+ <xsl:apply-templates select="@id | self::dita/*[1]/@id" mode="gen-metadata"/>
+
+ <!-- INSTANTIATION: Language -->
+ <xsl:apply-templates select="@xml:lang | self::dita/*[1]/@xml:lang" mode="gen-metadata"/>
+
+</xsl:template>
+
+
+<!-- CONTENT: Type -->
+<xsl:template match="dita" mode="gen-type-metadata">
+ <xsl:apply-templates select="*[1]" mode="gen-type-metadata"/>
+</xsl:template>
+<xsl:template match="*" mode="gen-type-metadata">
+ <xsl:text disable-output-escaping="yes">&lt;!-- meta name="DC.Type" content="</xsl:text>
+ <xsl:value-of select="name(.)"/>
+ <xsl:text disable-output-escaping="yes"> --&gt;</xsl:text>
+ <xsl:value-of select="$newline"/>
+</xsl:template>
+
+<!-- CONTENT: Title - title -->
+<xsl:template match="*[contains(@class,' topic/title ')]" mode="gen-metadata">
+ <xsl:variable name="titlemeta">
+ <xsl:apply-templates select="*|text()" mode="text-only"/>
+ </xsl:variable>
+ <xsl:text disable-output-escaping="yes">&lt;!-- meta name="DC.Title" content="</xsl:text>
+ <xsl:value-of select="normalize-space($titlemeta)" />
+ <xsl:text disable-output-escaping="yes">" --&gt;</xsl:text>
+ <xsl:value-of select="$newline"/>
+</xsl:template>
+
+<!-- CONTENT: Description - shortdesc -->
+<xsl:template match="*[contains(@class,' topic/shortdesc ')]" mode="gen-metadata">
+ <xsl:variable name="shortmeta">
+ <xsl:apply-templates select="*|text()" mode="text-only"/>
+ </xsl:variable>
+ <xsl:text disable-output-escaping="yes">&lt;!-- meta name="abstract" content="</xsl:text>
+ <xsl:value-of select="normalize-space($shortmeta)"/>
+ <xsl:text disable-output-escaping="yes">" --&gt;</xsl:text>
+ <xsl:value-of select="$newline"/>
+ <xsl:text disable-output-escaping="yes">&lt;!-- meta name="description" content="</xsl:text>
+ <xsl:value-of select="normalize-space($shortmeta)"/>
+ <xsl:text disable-output-escaping="yes">" --&gt;</xsl:text>
+ <xsl:value-of select="$newline"/>
+</xsl:template>
+
+<!-- CONTENT: Source - prolog/source/@href -->
+<xsl:template match="*[contains(@class,' topic/source ')]/@href" mode="gen-metadata">
+ <xsl:text disable-output-escaping="yes">&lt;!-- meta name="DC.Source" content="</xsl:text>
+ <xsl:value-of select="normalize-space(.)"/>
+ <xsl:text disable-output-escaping="yes">" --&gt;</xsl:text>
+ <xsl:value-of select="$newline"/>
+</xsl:template>
+
+<!-- CONTENT: Coverage prolog/metadata/category -->
+<xsl:template match="*[contains(@class,' topic/metadata ')]/*[contains(@class,' topic/category ')]" mode="gen-metadata">
+ <xsl:text disable-output-escaping="yes">&lt;!-- meta name="DC.Coverage" content="</xsl:text>
+ <xsl:value-of select="normalize-space(.)"/>
+ <xsl:text disable-output-escaping="yes">" --&gt;</xsl:text>
+ <xsl:value-of select="$newline"/>
+</xsl:template>
+
+<!-- CONTENT: Subject - prolog/metadata/keywords -->
+<xsl:template match="*" mode="gen-keywords-metadata">
+ <xsl:variable name="keywords-content">
+ <!-- for each item inside keywords (including nested index terms) -->
+ <xsl:for-each select="descendant::*[contains(@class,' topic/prolog ')]/*[contains(@class,' topic/metadata ')]/*[contains(@class,' topic/keywords ')]/descendant-or-self::*">
+ <!-- If this is the first term or keyword with this value -->
+ <xsl:if test="generate-id(key('meta-keywords',text())[1])=generate-id(.)">
+ <xsl:if test="position()>2"><xsl:text>, </xsl:text></xsl:if>
+ <xsl:value-of select="normalize-space(text())"/>
+ </xsl:if>
+ </xsl:for-each>
+ </xsl:variable>
+
+ <xsl:if test="string-length($keywords-content)>0">
+ <!-- <meta name="DC.subject" content="{$keywords-content}"/> -->
+ <xsl:text disable-output-escaping="yes">&lt;!-- meta name="DC.subject" content="</xsl:text>
+ <xsl:value-of select="$keywords-content"/>
+ <xsl:text disable-output-escaping="yes">" --&gt;</xsl:text>
+ <xsl:value-of select="$newline"/>
+ <!-- <meta name="keywords" content="{$keywords-content}"/> -->
+ <xsl:text disable-output-escaping="yes">&lt;!-- meta name="DC.subject" content="</xsl:text>
+ <xsl:value-of select="$keywords-content"/>
+ <xsl:text disable-output-escaping="yes">" --&gt;</xsl:text>
+ <xsl:value-of select="$newline"/>
+ </xsl:if>
+</xsl:template>
+
+<!-- CONTENT: Relation - related-links -->
+<xsl:template match="*[contains(@class,' topic/link ')]/@href" mode="gen-metadata">
+ <xsl:variable name="linkmeta">
+ <xsl:value-of select="normalize-space(.)"/>
+ </xsl:variable>
+ <xsl:choose>
+ <xsl:when test="substring($linkmeta,1,1)='#'" /> <!-- ignore internal file links -->
+ <xsl:otherwise>
+ <xsl:variable name="linkmeta_ext">
+ <xsl:choose>
+ <xsl:when test="contains($linkmeta,'.dita')">
+ <xsl:value-of select="substring-before($linkmeta,'.dita')"/>.<xsl:value-of select="$OUTEXT"/><xsl:value-of select="substring-after($linkmeta,'.dita')"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="$linkmeta"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+ <!-- <meta name="DC.Relation" scheme="URI">
+ <xsl:attribute name="content"><xsl:value-of select="$linkmeta_ext"/></xsl:attribute>
+ </meta> -->
+ <xsl:text disable-output-escaping="yes">&lt;!-- meta name="DC.relation" scheme="URI" content="</xsl:text>
+ <xsl:value-of select="$linkmeta_ext"/>
+ <xsl:text disable-output-escaping="yes">" --&gt;</xsl:text>
+ <xsl:value-of select="$newline"/>
+ </xsl:otherwise>
+ </xsl:choose>
+</xsl:template>
+
+<!-- Do not let any other @href's inside related-links generate metadata -->
+<xsl:template match="*/@href" mode="gen-metadata" priority="0"/>
+
+<!-- INTELLECTUAL PROPERTY: Contributor - prolog/author -->
+<!-- INTELLECTUAL PROPERTY: Creator -->
+<!-- Default is type='creator' -->
+<xsl:template match="*[contains(@class,' topic/author ')]" mode="gen-metadata">
+ <xsl:choose>
+ <xsl:when test="@type= 'contributor'">
+ <!-- <meta name="DC.Contributor" content="{normalize-space(.)}"/> -->
+ <xsl:text disable-output-escaping="yes">&lt;!-- meta name="DC.Contributor" content="</xsl:text>
+ <xsl:value-of select="normalize-space(.)"/>
+ <xsl:text disable-output-escaping="yes">" --&gt;</xsl:text>
+ <xsl:value-of select="$newline"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <!-- <meta name="DC.Creator" content="{normalize-space(.)}"/> -->
+ <xsl:text disable-output-escaping="yes">&lt;!-- meta name="DC.Creator" content="</xsl:text>
+ <xsl:value-of select="normalize-space(.)"/>
+ <xsl:text disable-output-escaping="yes">" --&gt;</xsl:text>
+ <xsl:value-of select="$newline"/>
+ </xsl:otherwise>
+ </xsl:choose>
+</xsl:template>
+
+<!-- INTELLECTUAL PROPERTY: Publisher - prolog/publisher -->
+<xsl:template match="*[contains(@class,' topic/publisher ')]" mode="gen-metadata">
+ <!-- <meta name="DC.Publisher" content="{normalize-space(.)}"/> -->
+ <xsl:text disable-output-escaping="yes">&lt;!-- meta name="DC.Publisher" content="</xsl:text>
+ <xsl:value-of select="normalize-space(.)"/>
+ <xsl:text disable-output-escaping="yes">" --&gt;</xsl:text>
+ <xsl:value-of select="$newline"/>
+</xsl:template>
+
+<!-- Place copyrights in keys for quick lookup. A copyright should only appear once; if it is primary
+ it should appear first with other primaries; if secondary, after primaries; otherwise, after
+ any that had @type. -->
+<xsl:key name="primary-meta-copyright" match="*[contains(@class,' topic/copyright ')][@type='primary']"
+ use="concat(*[contains(@class,' topic/copyryear ')]/@year,
+ *[contains(@class,' topic/copyrholder ')])"/>
+<xsl:key name="secondary-meta-copyright" match="*[contains(@class,' topic/copyright ')][@type='secondary']"
+ use="concat(*[contains(@class,' topic/copyryear ')]/@year,
+ *[contains(@class,' topic/copyrholder ')])"/>
+<xsl:key name="meta-copyright" match="*[contains(@class,' topic/copyright ')][not(@type)]"
+ use="concat(*[contains(@class,' topic/copyryear ')]/@year,
+ *[contains(@class,' topic/copyrholder ')])"/>
+
+<xsl:template name="generate-copyright-attributes">
+ <xsl:choose>
+ <xsl:when test="*[contains(@class,' topic/copyrholder ')]/text() | *[contains(@class,' topic/copyrholder ')]/*">
+ <xsl:value-of select="normalize-space(*[contains(@class,' topic/copyrholder ')])"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:text>(C) </xsl:text>
+ <xsl:call-template name="getString">
+ <xsl:with-param name="stringName" select="'Copyright IBM'"/>
+ </xsl:call-template>
+ </xsl:otherwise>
+ </xsl:choose>
+ <xsl:for-each select="*[contains(@class,' topic/copyryear ')]">
+ <xsl:text> </xsl:text><xsl:value-of select="@year"/>
+ </xsl:for-each>
+</xsl:template>
+
+<xsl:template match="*" mode="valid-copyright">
+ <!--P018721: if year and holder are both empty, do not generate anything -->
+ <xsl:variable name="copyrInfo">
+ <!-- Check for any text in the copyrholder or the year; if both are empty, no
+ copyright will be used -->
+ <xsl:value-of select="*[contains(@class,' topic/copyrholder ')] |
+ *[contains(@class,' topic/copyryear ')]/@year"/>
+ </xsl:variable>
+ <xsl:if test="normalize-space($copyrInfo)!=''">
+ <!-- <meta name="copyright"><xsl:call-template name="generate-copyright-attributes"/></meta> -->
+ <xsl:text disable-output-escaping="yes">&lt;!-- meta name="copyright" content="</xsl:text>
+ <xsl:call-template name="generate-copyright-attributes"/>
+ <xsl:text disable-output-escaping="yes">" --&gt;</xsl:text>
+ <xsl:value-of select="$newline"/>
+ <!-- <meta name="DC.Rights.Owner"><xsl:call-template name="generate-copyright-attributes"/></meta> -->
+ <xsl:text disable-output-escaping="yes">&lt;!-- meta name="DC.Rights.Owner" content="</xsl:text>
+ <xsl:call-template name="generate-copyright-attributes"/>
+ <xsl:text disable-output-escaping="yes">" --&gt;</xsl:text>
+ <xsl:value-of select="$newline"/>
+ </xsl:if>
+</xsl:template>
+
+<!-- INTELLECTUAL PROPERTY: Rights - prolog/copyright -->
+<xsl:template match="*[contains(@class,' topic/copyright ')]" mode="gen-metadata">
+ <xsl:variable name="keylookup"><xsl:value-of select="concat(*[contains(@class,' topic/copyryear ')]/@year,
+ *[contains(@class,' topic/copyrholder ')])"/></xsl:variable>
+ <xsl:choose>
+ <!-- If primary, ensure this is the first time it was used as primary -->
+ <xsl:when test="@type='primary'">
+ <xsl:if test="generate-id(.)=generate-id(key('primary-meta-copyright',$keylookup)[1])">
+ <xsl:apply-templates select="." mode="valid-copyright"/>
+ </xsl:if>
+ </xsl:when>
+ <!-- If secondary, this should be the first time it was used as secondary, AND it should not be primary -->
+ <xsl:when test="@type='secondary'">
+ <xsl:if test="not(key('primary-meta-copyright',$keylookup)) and
+ generate-id(.)=generate-id(key('secondary-meta-copyright',$keylookup)[1])">
+ <xsl:apply-templates select="." mode="valid-copyright"/>
+ </xsl:if>
+ </xsl:when>
+ <!-- No type: should not be used as primary or secondary, and this should be the first time it is used -->
+ <xsl:otherwise>
+ <xsl:if test="not(key('primary-meta-copyright',$keylookup)) and
+ not(key('secondary-meta-copyright',$keylookup)) and
+ generate-id(.)=generate-id(key('meta-copyright',$keylookup)[1])">
+ <xsl:apply-templates select="." mode="valid-copyright"/>
+ </xsl:if>
+ </xsl:otherwise>
+ </xsl:choose>
+</xsl:template>
+
+<!-- Usage Rights - prolog/permissions -->
+<xsl:template match="*[contains(@class,' topic/permissions ')]" mode="gen-metadata">
+ <xsl:text disable-output-escaping="yes">&lt;!-- meta name="DC.Rights.Usage" content="</xsl:text>
+ <xsl:value-of select="@view"/>
+ <xsl:text disable-output-escaping="yes">" --&gt;</xsl:text>
+ <xsl:value-of select="$newline"/>
+</xsl:template>
+
+<!-- = = = = = = = = = = = Product - Audience = = = = = = = = = = = -->
+<!-- Audience -->
+<xsl:template match="*[contains(@class,' topic/audience ')]/@experiencelevel" mode="gen-metadata">
+ <xsl:text disable-output-escaping="yes">&lt;!-- meta name="DC.Audience.Experiencelevel" content="</xsl:text>
+ <xsl:value-of select="."/>
+ <xsl:text disable-output-escaping="yes">" --&gt;</xsl:text>
+ <xsl:value-of select="$newline"/>
+</xsl:template>
+<xsl:template match="*[contains(@class,' topic/audience ')]/@importance" mode="gen-metadata">
+ <xsl:text disable-output-escaping="yes">&lt;!-- meta name="DC.Audience.Importance" content="</xsl:text>
+ <xsl:value-of select="."/>
+ <xsl:text disable-output-escaping="yes">" --&gt;</xsl:text>
+ <xsl:value-of select="$newline"/>
+</xsl:template>
+<xsl:template match="*[contains(@class,' topic/audience ')]/@name" mode="gen-metadata">
+ <xsl:text disable-output-escaping="yes">&lt;!-- meta name="DC.Audience.Name" content="</xsl:text>
+ <xsl:value-of select="."/>
+ <xsl:text disable-output-escaping="yes">" --&gt;</xsl:text>
+ <xsl:value-of select="$newline"/>
+</xsl:template>
+<xsl:template match="*[contains(@class,' topic/audience ')]/@job" mode="gen-metadata">
+ <xsl:choose>
+ <xsl:when test=".='other'">
+ <xsl:text disable-output-escaping="yes">&lt;!-- meta name="DC.Audience.Job" content="</xsl:text>
+ <xsl:value-of select="normalize-space(../@otherjob)"/>
+ <xsl:text disable-output-escaping="yes">" --&gt;</xsl:text>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:text disable-output-escaping="yes">&lt;!-- meta name="DC.Audience.Job" content="</xsl:text>
+ <xsl:value-of select="."/>
+ <xsl:text disable-output-escaping="yes">" --&gt;</xsl:text>
+ </xsl:otherwise>
+ </xsl:choose>
+ <xsl:value-of select="$newline"/>
+</xsl:template>
+<xsl:template match="*[contains(@class,' topic/audience ')]/@type" mode="gen-metadata">
+ <xsl:choose>
+ <xsl:when test=".='other'">
+ <xsl:text disable-output-escaping="yes">&lt;!-- meta name="DC.Audience.Type" content="</xsl:text>
+ <xsl:value-of select="normalize-space(../@othertype)"/>
+ <xsl:text disable-output-escaping="yes">" --&gt;</xsl:text>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:text disable-output-escaping="yes">&lt;!-- meta name="DC.Audience.Type" content="</xsl:text>
+ <xsl:value-of select="."/>
+ <xsl:text disable-output-escaping="yes">" --&gt;</xsl:text>
+ </xsl:otherwise>
+ </xsl:choose>
+ <xsl:value-of select="$newline"/>
+</xsl:template>
+
+<xsl:template match="*[contains(@class,' topic/prodname ')]" mode="gen-metadata">
+ <xsl:variable name="prodnamemeta">
+ <xsl:apply-templates select="*|text()" mode="text-only"/>
+ </xsl:variable>
+ <meta name="prodname">
+ <xsl:attribute name="content"><xsl:value-of select="normalize-space($prodnamemeta)"/></xsl:attribute>
+ </meta>
+ <xsl:value-of select="$newline"/>
+</xsl:template>
+
+<xsl:template match="*[contains(@class,' topic/vrm ')]/@version" mode="gen-metadata">
+ <xsl:text disable-output-escaping="yes">&lt;!-- meta name="version" content="</xsl:text>
+ <xsl:value-of select="."/>
+ <xsl:text disable-output-escaping="yes">" --&gt;</xsl:text>
+ <xsl:value-of select="$newline"/>
+</xsl:template>
+<xsl:template match="*[contains(@class,' topic/vrm ')]/@release" mode="gen-metadata">
+ <xsl:text disable-output-escaping="yes">&lt;!-- meta name="release" content="</xsl:text>
+ <xsl:value-of select="."/>
+ <xsl:text disable-output-escaping="yes">" --&gt;</xsl:text>
+ <xsl:value-of select="$newline"/>
+</xsl:template>
+<xsl:template match="*[contains(@class,' topic/vrm ')]/@modification" mode="gen-metadata">
+ <xsl:text disable-output-escaping="yes">&lt;!-- meta name="modification" content="</xsl:text>
+ <xsl:value-of select="."/>
+ <xsl:text disable-output-escaping="yes">" --&gt;</xsl:text>
+ <xsl:value-of select="$newline"/>
+</xsl:template>
+
+<xsl:template match="*[contains(@class,' topic/brand ')]" mode="gen-metadata">
+ <xsl:variable name="brandmeta">
+ <xsl:apply-templates select="*|text()" mode="text-only"/>
+ </xsl:variable>
+ <xsl:text disable-output-escaping="yes">&lt;!-- meta name="brand" content="</xsl:text>
+ <xsl:value-of select="normalize-space($brandmeta)"/>
+ <xsl:text disable-output-escaping="yes">" --&gt;</xsl:text>
+ <xsl:value-of select="$newline"/>
+</xsl:template>
+
+<xsl:template match="*[contains(@class,' topic/component ')]" mode="gen-metadata">
+ <xsl:variable name="componentmeta">
+ <xsl:apply-templates select="*|text()" mode="text-only"/>
+ </xsl:variable>
+ <xsl:text disable-output-escaping="yes">&lt;!-- meta name="component" content="</xsl:text>
+ <xsl:value-of select="normalize-space($componentmeta)"/>
+ <xsl:text disable-output-escaping="yes">" --&gt;</xsl:text>
+ <xsl:value-of select="$newline"/>
+</xsl:template>
+
+<xsl:template match="*[contains(@class,' topic/featnum ')]" mode="gen-metadata">
+ <xsl:variable name="featnummeta">
+ <xsl:apply-templates select="*|text()" mode="text-only"/>
+ </xsl:variable>
+ <xsl:text disable-output-escaping="yes">&lt;!-- meta name="featnum" content="</xsl:text>
+ <xsl:value-of select="normalize-space($featnummeta)"/>
+ <xsl:text disable-output-escaping="yes">" --&gt;</xsl:text>
+ <xsl:value-of select="$newline"/>
+</xsl:template>
+
+<xsl:template match="*[contains(@class,' topic/prognum ')]" mode="gen-metadata">
+ <xsl:variable name="prognummeta">
+ <xsl:apply-templates select="*|text()" mode="text-only"/>
+ </xsl:variable>
+ <xsl:text disable-output-escaping="yes">&lt;!-- meta name="prognum" content="</xsl:text>
+ <xsl:value-of select="normalize-space($prognummeta)"/>
+ <xsl:text disable-output-escaping="yes">" --&gt;</xsl:text>
+ <xsl:value-of select="$newline"/>
+</xsl:template>
+
+<xsl:template match="*[contains(@class,' topic/platform ')]" mode="gen-metadata">
+ <xsl:variable name="platformmeta">
+ <xsl:apply-templates select="*|text()" mode="text-only"/>
+ </xsl:variable>
+ <xsl:text disable-output-escaping="yes">&lt;!-- meta name="platform" content="</xsl:text>
+ <xsl:value-of select="normalize-space($platformmeta)"/>
+ <xsl:text disable-output-escaping="yes">" --&gt;</xsl:text>
+ <xsl:value-of select="$newline"/>
+</xsl:template>
+
+<xsl:template match="*[contains(@class,' topic/series ')]" mode="gen-metadata">
+ <xsl:variable name="seriesmeta">
+ <xsl:apply-templates select="*|text()" mode="text-only"/>
+ </xsl:variable>
+ <xsl:text disable-output-escaping="yes">&lt;!-- meta name="series" content="</xsl:text>
+ <xsl:value-of select="normalize-space($seriesmeta)"/>
+ <xsl:text disable-output-escaping="yes">" --&gt;</xsl:text>
+ <xsl:value-of select="$newline"/>
+</xsl:template>
+
+<!-- INSTANTIATION: Date - prolog/critdates/created -->
+<xsl:template match="*[contains(@class,' topic/critdates ')]/*[contains(@class,' topic/created ')]" mode="gen-metadata">
+ <xsl:text disable-output-escaping="yes">&lt;!-- meta name="DC.Date.Created" content="</xsl:text>
+ <xsl:value-of select="@date"/>
+ <xsl:text disable-output-escaping="yes">" --&gt;</xsl:text>
+ <xsl:value-of select="$newline"/>
+</xsl:template>
+
+<!-- prolog/critdates/revised/@modified -->
+<xsl:template match="*[contains(@class,' topic/critdates ')]/*[contains(@class,' topic/revised ')]/@modified" mode="gen-metadata">
+ <xsl:text disable-output-escaping="yes">&lt;!-- meta name="DC.Date.Modified" content="</xsl:text>
+ <xsl:value-of select="."/>
+ <xsl:text disable-output-escaping="yes">" --&gt;</xsl:text>
+ <xsl:value-of select="$newline"/>
+</xsl:template>
+
+<!-- prolog/critdates/revised/@golive -->
+<xsl:template match="*[contains(@class,' topic/critdates ')]/*[contains(@class,' topic/revised ')]/@golive" mode="gen-metadata">
+ <xsl:text disable-output-escaping="yes">&lt;!-- meta name="DC.Date.Issued" content="</xsl:text>
+ <xsl:value-of select="."/>
+ <xsl:text disable-output-escaping="yes">" --&gt;</xsl:text>
+ <xsl:value-of select="$newline"/>
+ <xsl:text disable-output-escaping="yes">&lt;!-- meta name="DC.Date.Available" content="</xsl:text>
+ <xsl:value-of select="."/>
+ <xsl:text disable-output-escaping="yes">" --&gt;</xsl:text>
+ <xsl:value-of select="$newline"/>
+</xsl:template>
+
+<!-- prolog/critdates/revised/@expiry -->
+<xsl:template match="*[contains(@class,' topic/critdates ')]/*[contains(@class,' topic/revised ')]/@expiry" mode="gen-metadata">
+ <xsl:text disable-output-escaping="yes">&lt;!-- meta name="DC.Date.Expiry" content="</xsl:text>
+ <xsl:value-of select="."/>
+ <xsl:text disable-output-escaping="yes">" --&gt;</xsl:text>
+ <xsl:value-of select="$newline"/>
+</xsl:template>
+
+<!-- prolog/metadata/othermeta -->
+<xsl:template match="*[contains(@class,' topic/othermeta ')]" mode="gen-metadata">
+ <xsl:text disable-output-escaping="yes">&lt;!-- meta name="</xsl:text>
+ <xsl:value-of select="@name"/>
+ <xsl:text>" content="</xsl:text>
+ <xsl:value-of select="@content"/>
+ <xsl:text disable-output-escaping="yes">" --&gt;</xsl:text>
+ <xsl:value-of select="$newline"/>
+</xsl:template>
+
+<!-- INSTANTIATION: Format -->
+<!-- this value is based on output format used for DC indexing, not source.
+ Put in this odd template for easy overriding, if creating another output format. -->
+<xsl:template match="*" mode="gen-format-metadata">
+ <xsl:text disable-output-escaping="yes">&lt;!-- meta name="DC.Format" content="XML" --&gt;</xsl:text>
+ <xsl:value-of select="$newline"/>
+</xsl:template>
+
+<!-- INSTANTIATION: Identifier --> <!-- id is an attribute on Topic -->
+<xsl:template match="@id" mode="gen-metadata">
+ <xsl:text disable-output-escaping="yes">&lt;!-- meta name="DC.Identifier" content="</xsl:text>
+ <xsl:value-of select="."/>
+ <xsl:text disable-output-escaping="yes">" --&gt;</xsl:text>
+ <xsl:value-of select="$newline"/>
+</xsl:template>
+
+<!-- INSTANTIATION: Language -->
+<!-- ideally, take the first token of the language attribute value -->
+<xsl:template match="@xml:lang" mode="gen-metadata">
+ <xsl:text disable-output-escaping="yes">&lt;!-- meta name="DC.Language" content="</xsl:text>
+ <xsl:value-of select="."/>
+ <xsl:text disable-output-escaping="yes">" --&gt;</xsl:text>
+ <xsl:value-of select="$newline"/>
+</xsl:template>
+
+</xsl:stylesheet>
diff --git a/src/help/appvalidator_help/tools/ditaot/plugins/cshelp/xsl/context2contexttemp.xsl b/src/help/appvalidator_help/tools/ditaot/plugins/cshelp/xsl/context2contexttemp.xsl
new file mode 100644
index 0000000..25fe1a6
--- /dev/null
+++ b/src/help/appvalidator_help/tools/ditaot/plugins/cshelp/xsl/context2contexttemp.xsl
@@ -0,0 +1,48 @@
+<?xml version="1.0"?>
+<!-- (c) Copyright IBM Corp. 2005, 2006 All Rights Reserved. -->
+<!-- This file is part of the DITA Open Toolkit project hosted on -->
+<!-- Sourceforge.net. See the accompanying license.txt file for -->
+<!-- applicable licenses. -->
+
+<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+
+<xsl:variable name="cr"><xsl:text>
+</xsl:text></xsl:variable>
+<xsl:variable name="lt">&#60;</xsl:variable>
+<xsl:variable name="gt">&#62;</xsl:variable>
+
+ <xsl:template match="/">
+ <xsl:processing-instruction name="NLS">type="org.eclipse.help.contexts"</xsl:processing-instruction><xsl:value-of select="$cr"/>
+ <contexts><xsl:value-of select="$cr"/>
+ <xsl:apply-templates select="//context" />
+ </contexts><xsl:value-of select="$cr"/>
+ </xsl:template>
+
+ <xsl:template match="//comment()">
+ <xsl:copy />
+ </xsl:template>
+
+ <xsl:template match="context">
+ <context id="{@id}">
+ <xsl:value-of select="$cr"/>
+ <xsl:apply-templates />
+ </context><xsl:value-of select="$cr"/>
+ </xsl:template>
+
+ <xsl:template match="description">
+ <description>
+ <xsl:apply-templates />
+ </description><xsl:value-of select="$cr"/>
+ </xsl:template>
+
+ <xsl:template match="b">
+ <xsl:text>&lt;b&gt;</xsl:text>
+ <xsl:apply-templates />
+ <xsl:text>&lt;/b&gt;</xsl:text>
+ </xsl:template>
+
+ <xsl:template match="topic">
+ <xsl:copy-of select="." />
+ </xsl:template>
+
+</xsl:stylesheet> \ No newline at end of file
diff --git a/src/help/appvalidator_help/tools/ditaot/plugins/cshelp/xsl/context2dita.xsl b/src/help/appvalidator_help/tools/ditaot/plugins/cshelp/xsl/context2dita.xsl
new file mode 100644
index 0000000..65ac1bc
--- /dev/null
+++ b/src/help/appvalidator_help/tools/ditaot/plugins/cshelp/xsl/context2dita.xsl
@@ -0,0 +1,130 @@
+<?xml version="1.0"?>
+<!-- (c) Copyright IBM Corp. 2005, 2006 All Rights Reserved. -->
+<!-- This file is part of the DITA Open Toolkit project hosted on -->
+<!-- Sourceforge.net. See the accompanying license.txt file for -->
+<!-- applicable licenses. -->
+
+<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+
+<xsl:output method="xml"
+ encoding="utf-8"
+ indent="no"
+ doctype-system="..\dtd\cshelp.dtd"
+ doctype-public="-//IBM//DTD DITA CSHelp//EN"
+/>
+
+<xsl:variable name="cr"><xsl:text>
+</xsl:text></xsl:variable>
+<xsl:variable name="lt">&#60;</xsl:variable>
+<xsl:variable name="gt">&#62;</xsl:variable>
+
+ <xsl:template match="//contexts">
+ <xsl:value-of select="$cr"/>
+ <cshelp id="csh_outer_container" xml:lang="en-us"><xsl:value-of select="$cr"/>
+ <title></title><xsl:value-of select="$cr"/>
+ <shortdesc></shortdesc><xsl:value-of select="$cr"/>
+ <csbody></csbody><xsl:value-of select="$cr"/>
+ <xsl:apply-templates />
+ </cshelp><xsl:value-of select="$cr"/>
+ </xsl:template>
+
+ <xsl:template match="//comment()">
+ <xsl:copy />
+ </xsl:template>
+
+ <xsl:template match="//context">
+
+ <cshelp id="{@id}">
+ <xsl:value-of select="$cr"/>
+
+ <title><xsl:value-of select="@title"/></title><xsl:value-of select="$cr"/>
+
+ <shortdesc>
+ <xsl:choose>
+ <xsl:when test="contains(description,$cr)">
+ <xsl:call-template name="br-replace-1">
+ <xsl:with-param name="brtext" select="substring-before(description,$cr)"/>
+ </xsl:call-template>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="description" disable-output-escaping="yes" />
+ </xsl:otherwise>
+ </xsl:choose>
+ </shortdesc><xsl:value-of select="$cr"/>
+
+ <csbody><xsl:value-of select="$cr"/>
+ <xsl:if test="contains(description,$cr)">
+ <p>
+ <xsl:call-template name="br-replace-2">
+ <xsl:with-param name="brtext2" select="substring-after(description,$cr)"/>
+ </xsl:call-template>
+ </p>
+ </xsl:if>
+ </csbody><xsl:value-of select="$cr" />
+
+ <xsl:if test="topic">
+ <related-links><xsl:value-of select="$cr"/>
+ <xsl:for-each select="topic">
+ <link href="{@href}">
+ <xsl:attribute name="format">
+ <xsl:call-template name="find-file-ext">
+ <xsl:with-param name="path" select="@href"/>
+ </xsl:call-template>
+ </xsl:attribute>
+ <linktext>
+ <xsl:value-of select="@label"/>
+ </linktext>
+ </link><xsl:value-of select="$cr"/>
+ </xsl:for-each>
+ </related-links><xsl:value-of select="$cr"/>
+ </xsl:if>
+ </cshelp><xsl:value-of select="$cr"/>
+ </xsl:template>
+
+ <xsl:template name="br-replace-1">
+ <xsl:param name="brtext"/>
+ <xsl:choose>
+ <xsl:when test="contains($brtext,$cr)"> <!-- is there a CR within the text? -->
+ <xsl:value-of select="$brtext" disable-output-escaping="yes" />
+ <xsl:value-of select="$cr"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="$brtext" disable-output-escaping="yes" /> <!-- No CRs, just output -->
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+
+ <xsl:template name="br-replace-2">
+ <xsl:param name="brtext2"/>
+ <xsl:choose>
+ <xsl:when test="contains($brtext2,$cr)"> <!-- is there a CR within the text? -->
+ <xsl:if test="string-length(substring-before($brtext2,$cr)) &gt; 0">
+ <xsl:value-of select="substring-before($brtext2,$cr)" disable-output-escaping="yes" /> <!-- yes - substring & add the BR & newline -->
+ <xsl:text disable-output-escaping="yes">&#60;</xsl:text>/p<xsl:text disable-output-escaping="yes">&#62;</xsl:text><xsl:value-of select="$cr"/>
+ <xsl:text disable-output-escaping="yes">&#60;</xsl:text>p<xsl:text disable-output-escaping="yes">&#62;</xsl:text>
+ </xsl:if>
+ <xsl:call-template name="br-replace-2">
+ <xsl:with-param name="brtext2" select="substring-after($brtext2,$cr)"/>
+ </xsl:call-template>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="$brtext2" disable-output-escaping="yes" /> <!-- No CRs, just output -->
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+
+ <xsl:template name="find-file-ext">
+ <xsl:param name="path"/>
+ <xsl:choose>
+ <xsl:when test="contains($path,'.')">
+ <xsl:call-template name="find-file-ext">
+ <xsl:with-param name="path" select="substring-after($path,'.')" />
+ </xsl:call-template>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="$path" />
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+
+</xsl:stylesheet> \ No newline at end of file
diff --git a/src/help/appvalidator_help/tools/ditaot/plugins/cshelp/xsl/cshdisplay.xsl b/src/help/appvalidator_help/tools/ditaot/plugins/cshelp/xsl/cshdisplay.xsl
new file mode 100644
index 0000000..0c0c8b5
--- /dev/null
+++ b/src/help/appvalidator_help/tools/ditaot/plugins/cshelp/xsl/cshdisplay.xsl
@@ -0,0 +1,71 @@
+<?xml version="1.0"?>
+<!-- (c) Copyright IBM Corp. 2005, 2006 All Rights Reserved. -->
+<!-- This file is part of the DITA Open Toolkit project hosted on -->
+<!-- Sourceforge.net. See the accompanying license.txt file for -->
+<!-- applicable licenses. -->
+
+<xsl:stylesheet version="1.0"
+ xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+
+<xsl:template match="*[contains(@class,' cshelp/cshelp ')]" name="cshelp">
+ <xsl:choose>
+ <xsl:when test="$DRAFT='yes'">
+ <!-- review output -->
+ <xsl:choose>
+ <xsl:when test="not(parent::*)">
+ <html><xsl:value-of select="$newline"/>
+ <head /><xsl:value-of select="$newline"/>
+ <body><xsl:value-of select="$newline"/>
+ <xsl:call-template name="csreviewoutput"/>
+ <xsl:if test="*[contains(@class,' cshelp/cshelp ')]">
+ <xsl:apply-templates select="*[contains(@class,' cshelp/cshelp ')]"/>
+ </xsl:if>
+ </body><xsl:value-of select="$newline"/>
+ </html><xsl:value-of select="$newline"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:call-template name="csreviewoutput"/>
+ <xsl:if test="*[contains(@class,' cshelp/cshelp ')]">
+ <xsl:apply-templates select="*[contains(@class,' cshelp/cshelp ')]"/>
+ </xsl:if>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:when>
+ <xsl:otherwise>
+ <!-- Otherwise, do standard processing -->
+ <xsl:choose>
+ <xsl:when test="not(parent::*)">
+ <xsl:call-template name="chapter-setup"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:apply-imports/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:otherwise>
+ </xsl:choose>
+</xsl:template>
+
+<xsl:template name="csreviewoutput">
+ <hr/><xsl:value-of select="$newline"/>
+ <hr/><xsl:value-of select="$newline"/>
+<strong><xsl:value-of select="@id"/></strong><br/><xsl:value-of select="$newline"/>
+<xsl:value-of select="*[contains(@class,' cshelp/csprolog ')]/*[contains(@class,' cshelp/csmetadata ')]/*[contains(@class,' cshelp/cswindowtitle ')]"/><br/><xsl:value-of select="$newline"/>
+<xsl:value-of select="*[contains(@class,' cshelp/csprolog ')]/*[contains(@class,' cshelp/csmetadata ')]/*[contains(@class,' cshelp/cswidgetlabel ')]"/><br/><xsl:value-of select="$newline"/>
+<br/><xsl:value-of select="$newline"/>
+ <p><xsl:apply-templates select="*[contains(@class,' topic/shortdesc ')]"/></p><xsl:value-of select="$newline"/>
+ <xsl:apply-templates select="*[contains(@class,' cshelp/csbody ')]"/><xsl:value-of select="$newline"/>
+ <xsl:if test="*[contains(@class,' topic/related-links ')]">
+ <xsl:for-each select="*[contains(@class,' topic/related-links ')]/*[contains(@class,' topic/link ')]">
+ <a>
+ <xsl:attribute name="href">
+ <xsl:call-template name="href"/>
+ </xsl:attribute>
+ <xsl:value-of select="linktext" />
+ </a>
+ <br/><xsl:value-of select="$newline"/>
+ </xsl:for-each>
+ </xsl:if>
+ <br/><xsl:value-of select="$newline"/><xsl:value-of select="$newline"/>
+</xsl:template>
+
+</xsl:stylesheet>
diff --git a/src/help/appvalidator_help/tools/ditaot/plugins/cshelp/xsl/dit2context.xsl b/src/help/appvalidator_help/tools/ditaot/plugins/cshelp/xsl/dit2context.xsl
new file mode 100644
index 0000000..b28b4a7
--- /dev/null
+++ b/src/help/appvalidator_help/tools/ditaot/plugins/cshelp/xsl/dit2context.xsl
@@ -0,0 +1,1275 @@
+<?xml version="1.0"?>
+<!-- (c) Copyright IBM Corp. 2005, 2007 All Rights Reserved. -->
+<!-- This file is part of the DITA Open Toolkit project hosted on -->
+<!-- Sourceforge.net. See the accompanying license.txt file for -->
+<!-- applicable licenses. -->
+
+<xsl:stylesheet version="1.0"
+ xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ xmlns:saxon="http://icl.com/saxon"
+ xmlns:xt="http://www.jclark.com/xt"
+ extension-element-prefixes="saxon xt">
+
+<xsl:import href="../../../xsl/common/dita-utilities.xsl" />
+<xsl:include href="../../../xsl/common/output-message.xsl"/>
+<xsl:include href="GetCSHMeta.xsl"/>
+
+<!-- /OUTEXT = default "output extension" processing parameter ('html')-->
+<!-- Should be overridden by rexx command to be 'xml' -->
+<xsl:param name="OUTEXT" select="'html'"/><!-- "htm" and "html" are valid values -->
+
+<!-- /WORKDIR = the working directory that contains the document being transformed.
+ Needed as a directory prefix for the @conref "document()" function calls.
+ default is '../doc/')-->
+<xsl:param name="WORKDIR" select="'./'"/>
+
+<!-- /FILENAME = the file name (file name and extension only - no path) of the document being transformed.
+ Needed to help with debugging.
+ default is 'myfile.xml')-->
+<xsl:param name="FILENAME"/>
+
+<xsl:variable name="msgprefix">IDXS</xsl:variable>
+<xsl:variable name="newline"><xsl:text>
+</xsl:text></xsl:variable>
+
+<xsl:output indent="no"/>
+
+ <xsl:template match="/">
+ <xsl:value-of select="$newline"/>
+ <xsl:processing-instruction name="NLS">type="org.eclipse.help.contexts"</xsl:processing-instruction>
+ <xsl:value-of select="$newline"/>
+ <xsl:apply-templates select="//*[contains(@class,' cshelp/cshelp ')]"/> <!-- select is formatted to unnest nested cshelp elements -->
+ <xsl:text disable-output-escaping="yes">&lt;/contexts&gt;</xsl:text>
+ <xsl:value-of select="$newline"/>
+ </xsl:template>
+
+ <xsl:template match="//comment()">
+ <xsl:copy />
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' cshelp/cshelp ')]" name="cshelp">
+
+ <xsl:if test="not(parent::*[contains(@class,' cshelp/cshelp ')])">
+ <xsl:call-template name="getCSHMeta"/> <!-- 5/31/2006 -->
+ <!-- <xsl:call-template name="ibmcopyright"/>--> <!-- 5/30/2006 -->
+ <xsl:text disable-output-escaping="yes">&lt;contexts&gt;</xsl:text>
+ <xsl:value-of select="$newline"/>
+ </xsl:if>
+
+ <xsl:if test="parent::*[contains(@class,' cshelp/cshelp ')]">
+ <xsl:element name="context">
+ <xsl:attribute name="id">
+ <xsl:value-of select="@id"/>
+ </xsl:attribute>
+ <xsl:if test="*[contains(@class,' topic/title ')]/text() | *[contains(@class,' topic/title ')]/*">
+ <xsl:attribute name="title">
+ <xsl:value-of select="*[contains(@class,' topic/title ')]" />
+ </xsl:attribute>
+ </xsl:if>
+ <xsl:text>&#xA;</xsl:text>
+
+ <xsl:element name="description">
+ <xsl:apply-templates select="*[contains(@class,' topic/shortdesc ')]" />
+ <xsl:apply-templates select="*[contains(@class,' cshelp/csbody ')]" />
+ </xsl:element>
+ <xsl:text>&#xA;</xsl:text>
+
+ <xsl:if test="*[contains(@class,' topic/related-links ')]">
+ <xsl:for-each select="*[contains(@class,' topic/related-links ')]/*[contains(@class,' topic/link ')]">
+ <xsl:element name="topic">
+ <xsl:attribute name="href">
+ <xsl:call-template name="href"/>
+ </xsl:attribute>
+ <xsl:attribute name="label">
+ <xsl:choose>
+ <xsl:when test="*[contains(@class, ' topic/linktext ')]">
+ <xsl:apply-templates select="*[contains(@class, ' topic/linktext ')]"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <!-- use href -->
+ <xsl:call-template name="href"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:attribute>
+ </xsl:element><xsl:text>&#xA;</xsl:text>
+ </xsl:for-each>
+ </xsl:if>
+
+ </xsl:element><xsl:text>&#xA;</xsl:text>
+ </xsl:if>
+ </xsl:template>
+
+
+ <xsl:template match="*[contains(@class,' topic/shortdesc ')]" name="shortdesc">
+ <xsl:apply-templates />
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' cshelp/csbody ')]" name="csbody">
+ <xsl:if test="node()"><xsl:text>&#xA;&#xA;</xsl:text></xsl:if>
+ <xsl:apply-templates />
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' topic/boolean ')]" name="topic.boolean">
+ <!-- below copied from dit2htm.xsl -->
+ <xsl:value-of select="name()"/><xsl:text>: </xsl:text><xsl:value-of select="@state"/>
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' topic/cite ')]" name="topic.cite">
+ <xsl:call-template name="checkPreceding" />
+ <xsl:apply-templates />
+ <xsl:call-template name="checkFollowing" />
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' topic/dd ')]" name="topic.dd">
+ <!-- jta 11/06/2006 -->
+ <xsl:call-template name="indentDL"/>
+ <xsl:text> </xsl:text>
+ <xsl:apply-templates />
+ <!-- jta 10/16/2006 -->
+ <!-- <xsl:if test="position()!=last()"> -->
+ <xsl:if test="following-sibling::*[contains(@class,' topic/dd ')]">
+ <xsl:text>&#xA;</xsl:text>
+ </xsl:if>
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' topic/ddhd ')]" name="topic.ddhd">
+ <!-- jta 11/06/2006 -->
+ <xsl:call-template name="indentDL"/>
+ <xsl:text> </xsl:text><b><xsl:apply-templates /></b><xsl:text>&#xA;</xsl:text>
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' topic/desc ')]" name="topic.desc">
+ <xsl:call-template name="checkPreceding" />
+ <xsl:text>(</xsl:text><xsl:apply-templates /><xsl:text>)</xsl:text>
+ <xsl:call-template name="checkFollowing" />
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' topic/dl ')]" name="topic.dl">
+ <xsl:if test="not(parent::*[contains(@class,' cshelp/csbody ')])">
+ <xsl:choose>
+ <xsl:when test="parent::*[contains(@class,' topic/p ')]">
+ <xsl:text>&#xA;&#xA;</xsl:text>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:text>&#xA;</xsl:text>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:if>
+ <xsl:apply-templates />
+ <xsl:call-template name="isNestedTextFollows" />
+ <xsl:call-template name="isFirstChildOfCsbody" />
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' topic/dlentry ')]" name="topic.dlentry">
+ <xsl:apply-templates />
+ <!-- jta 11/07/2006 -->
+ <xsl:if test="following-sibling::*[contains(@class,' topic/dlentry ')]">
+ <xsl:text>&#xA;</xsl:text>
+ </xsl:if>
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' topic/dlhead ')]" name="topic.dlhead">
+ <xsl:apply-templates />
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' topic/draft-comment ')]" name="topic.draft-comment">
+ <!-- no output -->
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' topic/dt ')]" name="topic.dt">
+ <!-- jta 11/06/2006 -->
+ <xsl:call-template name="indentDL"/>
+ <b><xsl:apply-templates /></b><xsl:text>&#xA;</xsl:text>
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' topic/dthd ')]" name="topic.dthd">
+ <!-- jta 11/06/2006 -->
+ <xsl:call-template name="indentDL"/>
+ <b><xsl:apply-templates /></b><xsl:text>&#xA;</xsl:text>
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' topic/fig ')]" name="topic.fig">
+ <xsl:call-template name="twoPrecedingCRs" />
+ <xsl:apply-templates />
+ <xsl:call-template name="isFirstChildOfCsbody" />
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' topic/fn ')]" name="topic.fn">
+ <!-- no output -->
+ <xsl:call-template name="output-message">
+ <xsl:with-param name="msg">Eclipse context-sensitive help files do not support footnotes.</xsl:with-param>
+ <xsl:with-param name="msgnum">066</xsl:with-param>
+ <xsl:with-param name="msgsev">I</xsl:with-param>
+ </xsl:call-template>
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' topic/image ')]" name="topic.image">
+ <xsl:variable name="alttext">
+ <xsl:choose>
+ <xsl:when test="*[contains(@class,' topic/alt ')]"><xsl:apply-templates/></xsl:when>
+ <xsl:otherwise><xsl:value-of select="@alt"/></xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+ <!-- no output -->
+ <xsl:if test="$alttext=''">
+ <xsl:call-template name="output-message">
+ <xsl:with-param name="msg">Eclipse context-sensitive help files do not support images.</xsl:with-param>
+ <xsl:with-param name="msgnum">066</xsl:with-param>
+ <xsl:with-param name="msgsev">I</xsl:with-param>
+ </xsl:call-template>
+ </xsl:if>
+ <xsl:choose>
+ <xsl:when test="@placement='break'">
+ <xsl:call-template name="twoPrecedingCRs" />
+ <xsl:value-of select="$alttext"/>
+ <xsl:call-template name="isFirstChildOfCsbody" />
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:call-template name="checkPreceding" />
+ <xsl:value-of select="$alttext"/>
+ <xsl:call-template name="checkFollowing" />
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' topic/indexterm ')]" name="topic.indexterm">
+ <!-- no output -->
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' topic/indextermref ')]" name="topic.indextermref">
+ <!-- no output -->
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' topic/itemgroup ')]" name="topic.itemgroup">
+ <xsl:text>&#xA; </xsl:text><xsl:apply-templates />
+ <!-- jta 10/16/2006 -->
+ <xsl:call-template name="isFirstChildOfCsbody" />
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' topic/keyword ')]" name="topic.keyword">
+ <xsl:call-template name="checkPreceding" />
+ <xsl:apply-templates />
+ <xsl:call-template name="checkFollowing" />
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' topic/li ')]" name="topic.li">
+ <xsl:variable name="olcount" select="count(ancestor-or-self::*[contains(@class,' topic/ol ')])"/>
+ <!-- <xsl:variable name="ulcount" select="count(ancestor-or-self::*[contains(@class,' topic/ul ')])"/> -->
+ <!-- <xsl:variable name="slcount" select="count(ancestor-or-self::*[contains(@class,' topic/sl ')])"/> -->
+ <!-- <xsl:variable name="nestcount" select="number($olcount) + number($ulcount) + number($slcount)" /> -->
+ <!-- <xsl:choose> -->
+ <!-- <xsl:when test="number($nestcount)=1"> -->
+ <!-- <xsl:text> </xsl:text> -->
+ <!-- </xsl:when> -->
+ <!-- <xsl:when test="number($nestcount)=2"> -->
+ <!-- <xsl:text> </xsl:text> -->
+ <!-- </xsl:when> -->
+ <!-- <xsl:when test="number($nestcount)=3"> -->
+ <!-- <xsl:text> </xsl:text> -->
+ <!-- </xsl:when> -->
+ <!-- <xsl:otherwise> -->
+ <!-- <xsl:text> </xsl:text> -->
+ <!-- </xsl:otherwise> -->
+ <!-- </xsl:choose> -->
+ <xsl:call-template name="indentLI"/>
+ <xsl:if test="parent::*[contains(@class,' topic/ul ')]">
+ <xsl:text>&#x2D; </xsl:text>
+ </xsl:if>
+ <xsl:apply-templates />
+ <!-- jta 11/06/2006 -->
+ <xsl:if test="following-sibling::*[contains(@class,' topic/li ')]">
+ <xsl:text>&#xA;</xsl:text>
+ </xsl:if>
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' topic/lines ')]" name="topic.lines">
+ <xsl:call-template name="spec-title-nospace"/>
+ <xsl:call-template name="br-replace">
+ <xsl:with-param name="brtext" select="."/>
+ </xsl:call-template>
+ <xsl:call-template name="isFirstChildOfCsbody" />
+ </xsl:template>
+
+ <xsl:template name="spec-title-nospace">
+ <!-- below adapted from dit2htm.xsl -->
+ <xsl:if test="@spectitle"><b><xsl:value-of select="@spectitle"/></b><xsl:text>&#xA;</xsl:text></xsl:if>
+ </xsl:template>
+
+ <!-- Break replace - used for LINES -->
+ <!-- this replaces newlines with the BR element. Forces breaks. -->
+ <xsl:template name="br-replace">
+ <xsl:param name="brtext"/>
+ <!-- capture an actual newline within the xsl:text element -->
+ <xsl:variable name="cr"><xsl:text>
+</xsl:text></xsl:variable>
+ <xsl:choose>
+ <xsl:when test="contains($brtext,$cr)"> <!-- is there a CR within the text? -->
+ <xsl:value-of select="substring-before($brtext,$cr)"/> <!-- yes - substring & add the BR & newline -->
+ <xsl:value-of select="$cr"/>
+ <xsl:call-template name="br-replace"> <!-- call again to get remaining CRs -->
+ <xsl:with-param name="brtext" select="substring-after($brtext,$cr)"/>
+ </xsl:call-template>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="$brtext"/> <!-- No CRs, just output -->
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' topic/linkinfo ')]" name="topic.linkinfo">
+ <xsl:text>&#xA;</xsl:text><xsl:apply-templates /><xsl:text>&#xA;&#xA;</xsl:text>
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' topic/linklist ')]" name="topic.linklist">
+ <xsl:apply-templates />
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' topic/linkpool ')]" name="topic.linkpool">
+ <xsl:apply-templates />
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' topic/linktext ')]" name="topic.linktext">
+ <xsl:apply-templates />
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' topic/lq ')]" name="topic.lq">
+ <xsl:call-template name="twoPrecedingCRs" />
+ <xsl:call-template name="indentLQ" />
+ <xsl:text>&#34;</xsl:text><xsl:apply-templates /><xsl:text>&#34;</xsl:text>
+ <xsl:call-template name="isNestedTextFollows" />
+ <xsl:call-template name="isFirstChildOfCsbody" />
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' topic/msgblock ')]" name="topic.msgblock">
+ <xsl:call-template name="br-replace">
+ <xsl:with-param name="brtext" select="."/>
+ </xsl:call-template>
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' topic/note ')]" name="topic.note">
+ <!-- jta 10/16/2006 -->
+ <xsl:call-template name="twoPrecedingCRs"/>
+ <xsl:call-template name="indentNote" />
+ <!-- <xsl:text>&#xA;&#xA;</xsl:text> -->
+ <xsl:call-template name="spec-title"/>
+ <xsl:choose>
+ <xsl:when test="@type='note'">
+ <xsl:call-template name="note"/>
+ </xsl:when>
+ <xsl:when test="@type='tip'">
+ <xsl:call-template name="getString">
+ <xsl:with-param name="stringName" select="'Tip'"/>
+ </xsl:call-template>
+ <xsl:call-template name="getString">
+ <xsl:with-param name="stringName" select="'ColonSymbol'"/>
+ </xsl:call-template>
+ <xsl:text> </xsl:text>
+ <xsl:apply-templates /><xsl:text>&#xA;&#xA;</xsl:text>
+ </xsl:when>
+ <xsl:when test="@type='fastpath'">
+ <xsl:call-template name="getString">
+ <xsl:with-param name="stringName" select="'Fastpath'"/>
+ </xsl:call-template>
+ <xsl:call-template name="getString">
+ <xsl:with-param name="stringName" select="'ColonSymbol'"/>
+ </xsl:call-template>
+ <xsl:text> </xsl:text>
+ <xsl:apply-templates /><xsl:text>&#xA;&#xA;</xsl:text>
+ </xsl:when>
+ <xsl:when test="@type='important'">
+ <xsl:call-template name="getString">
+ <xsl:with-param name="stringName" select="'Important'"/>
+ </xsl:call-template>
+ <xsl:call-template name="getString">
+ <xsl:with-param name="stringName" select="'ColonSymbol'"/>
+ </xsl:call-template>
+ <xsl:text> </xsl:text>
+ <xsl:apply-templates /><xsl:text>&#xA;&#xA;</xsl:text>
+ </xsl:when>
+ <xsl:when test="@type='remember'">
+ <xsl:call-template name="getString">
+ <xsl:with-param name="stringName" select="'Remember'"/>
+ </xsl:call-template>
+ <xsl:call-template name="getString">
+ <xsl:with-param name="stringName" select="'ColonSymbol'"/>
+ </xsl:call-template>
+ <xsl:text> </xsl:text>
+ <xsl:apply-templates /><xsl:text>&#xA;&#xA;</xsl:text>
+ </xsl:when>
+ <xsl:when test="@type='restriction'">
+ <xsl:call-template name="getString">
+ <xsl:with-param name="stringName" select="'Restriction'"/>
+ </xsl:call-template>
+ <xsl:call-template name="getString">
+ <xsl:with-param name="stringName" select="'ColonSymbol'"/>
+ </xsl:call-template>
+ <xsl:text> </xsl:text>
+ <xsl:apply-templates />
+ </xsl:when>
+ <xsl:when test="@type='attention'">
+ <xsl:call-template name="getString">
+ <xsl:with-param name="stringName" select="'Attention'"/>
+ </xsl:call-template>
+ <xsl:call-template name="getString">
+ <xsl:with-param name="stringName" select="'ColonSymbol'"/>
+ </xsl:call-template>
+ <xsl:text> </xsl:text>
+ <xsl:apply-templates />
+ </xsl:when>
+ <xsl:when test="@type='caution'">
+ <xsl:call-template name="getString">
+ <xsl:with-param name="stringName" select="'Caution'"/>
+ </xsl:call-template>
+ <xsl:call-template name="getString">
+ <xsl:with-param name="stringName" select="'ColonSymbol'"/>
+ </xsl:call-template>
+ <xsl:text> </xsl:text>
+ <xsl:apply-templates />
+ </xsl:when>
+ <xsl:when test="@type='danger'">
+ <xsl:call-template name="getString">
+ <xsl:with-param name="stringName" select="'Danger'"/>
+ </xsl:call-template>
+ <xsl:call-template name="getString">
+ <xsl:with-param name="stringName" select="'ColonSymbol'"/>
+ </xsl:call-template>
+ <xsl:text> </xsl:text>
+ <xsl:apply-templates />
+ </xsl:when>
+ <xsl:when test="@type='other'">
+ <xsl:choose>
+ <xsl:when test="@othertype"> <!-- is there a type title? Use that -->
+ <!-- TBD: this attr is a key that should look up external, translateable text.
+ For now, just output the othertype attr value. -->
+ <xsl:value-of select="@othertype"/>
+ <xsl:call-template name="getString">
+ <xsl:with-param name="stringName" select="'ColonSymbol'"/>
+ </xsl:call-template>
+ <xsl:text> </xsl:text>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:call-template name="note"/> <!-- otherwise, give them the standard note -->
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:call-template name="note"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ <xsl:call-template name="isNestedTextFollows" />
+ <xsl:call-template name="isFirstChildOfCsbody" />
+ </xsl:template>
+
+ <xsl:template name="note">
+ <xsl:call-template name="getString">
+ <xsl:with-param name="stringName" select="'Note'"/>
+ </xsl:call-template>
+ <xsl:call-template name="getString">
+ <xsl:with-param name="stringName" select="'ColonSymbol'"/>
+ </xsl:call-template>
+ <xsl:text> </xsl:text>
+ <xsl:apply-templates />
+ </xsl:template>
+
+ <xsl:template name="spec-title">
+ <xsl:if test="@spectitle"><b><xsl:value-of select="@spectitle"/></b><xsl:text>&#xA;</xsl:text></xsl:if>
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' topic/object ')]" name="topic.object">
+ <!-- no output -->
+ <xsl:call-template name="output-message">
+ <xsl:with-param name="msg">Eclipse context-sensitive help files do not support objects.</xsl:with-param>
+ <xsl:with-param name="msgnum">066</xsl:with-param>
+ <xsl:with-param name="msgsev">I</xsl:with-param>
+ </xsl:call-template>
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' topic/ol ')]" name="topic.ol">
+ <xsl:variable name="olcount" select="count(ancestor-or-self::*[contains(@class,' topic/ol ')])"/>
+<!-- <xsl:variable name="ulcount" select="count(ancestor-or-self::*[contains(@class,' topic/ul ')])"/> -->
+<!-- <xsl:variable name="slcount" select="count(ancestor-or-self::*[contains(@class,' topic/sl ')])"/> -->
+<!-- <xsl:variable name="ddcount" select="count(ancestor-or-self::*[contains(@class,' topic/dd ')])"/> -->
+<!-- <xsl:variable name="nestcount" select="number($olcount) + number($ulcount) + number($slcount) + number($ddcount)" /> -->
+ <xsl:call-template name="twoPrecedingCRs" />
+
+<!-- <xsl:if test="parent::*[contains(@class,' topic/li ')] | parent::*[contains(@class,' topic/sli ')]"><xsl:text>&#xA;</xsl:text></xsl:if> -->
+ <xsl:for-each select="*[contains(@class,' topic/li ')]">
+<!-- <xsl:choose> -->
+<!-- <xsl:when test="number($nestcount)=1"> -->
+<!-- <xsl:text> </xsl:text> -->
+<!-- </xsl:when> -->
+<!-- <xsl:when test="number($nestcount)=2"> -->
+<!-- <xsl:text> </xsl:text> -->
+<!-- </xsl:when> -->
+<!-- <xsl:when test="number($nestcount)=3"> -->
+<!-- <xsl:text> </xsl:text> -->
+<!-- </xsl:when> -->
+<!-- <xsl:otherwise> -->
+<!-- <xsl:text> </xsl:text> -->
+<!-- </xsl:otherwise> -->
+<!-- </xsl:choose> -->
+ <xsl:call-template name="indentLI"/> <!-- jta 11/07/2006 -->
+ <xsl:choose>
+ <xsl:when test="$olcount mod 3 = 1">
+ <xsl:number value="position()" format="1. "/>
+ </xsl:when>
+ <xsl:when test="$olcount mod 3 = 2">
+ <xsl:number value="position()" format="a. "/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:number value="position()" format="i. "/>
+ </xsl:otherwise>
+ </xsl:choose>
+ <xsl:apply-templates /><!-- jta 11/07/2006 <xsl:text>&#xA;</xsl:text> -->
+ <xsl:if test="following-sibling::*[contains(@class,' topic/li ')]">
+ <xsl:text>&#xA;</xsl:text>
+ </xsl:if>
+ </xsl:for-each>
+ <xsl:call-template name="isNestedTextFollows" />
+ <xsl:call-template name="isFirstChildOfCsbody" />
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' topic/p ')]" name="topic.p">
+ <!-- jta 10/16/2006 -->
+ <xsl:call-template name="twoPrecedingCRs"/>
+ <xsl:call-template name="indentP" />
+ <xsl:apply-templates />
+ <!-- <xsl:text>&#xA;&#xA;</xsl:text> -->
+ <xsl:call-template name="isNestedTextFollows" />
+ <xsl:call-template name="isFirstChildOfCsbody" />
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' topic/ph ')]" name="topic.ph">
+ <xsl:call-template name="checkPreceding" />
+ <xsl:apply-templates />
+ <xsl:call-template name="checkFollowing" />
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' topic/pre ')]" name="topic.pre">
+ <xsl:call-template name="checkPreceding" />
+ <xsl:call-template name="br-replace">
+ <xsl:with-param name="brtext" select="."/>
+ </xsl:call-template>
+ <xsl:call-template name="checkFollowing" />
+ <xsl:call-template name="isFirstChildOfCsbody" />
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' topic/q ')]" name="topic.q">
+ <xsl:call-template name="checkPreceding" />
+ <xsl:text>&#34;</xsl:text><xsl:apply-templates /><xsl:text>&#34;</xsl:text> <!-- places quotation marks -->
+ <xsl:call-template name="checkFollowing" />
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' topic/required-cleanup ')]" name="topic.required-cleanup">
+ <!-- no output -->
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' topic/simpletable ')]" name="topic.simpletable">
+ <!-- no output -->
+ <xsl:call-template name="output-message">
+ <xsl:with-param name="msg">Eclipse context-sensitive help files do not support tables.</xsl:with-param>
+ <xsl:with-param name="msgnum">066</xsl:with-param>
+ <xsl:with-param name="msgsev">I</xsl:with-param>
+ </xsl:call-template>
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' topic/sl ')]" name="topic.sl">
+ <xsl:call-template name="twoPrecedingCRs" />
+<!-- <xsl:if test="parent::*[contains(@class,' topic/li ')] | parent::*[contains(@class,' topic/sli ')]"><xsl:text>&#xA;</xsl:text></xsl:if> -->
+ <xsl:apply-templates />
+ <xsl:call-template name="isNestedTextFollows" />
+ <xsl:call-template name="isFirstChildOfCsbody" />
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' topic/sli ')]" name="topic.sli">
+<!-- <xsl:variable name="olcount" select="count(ancestor-or-self::*[contains(@class,' topic/ol ')])"/> -->
+<!-- <xsl:variable name="ulcount" select="count(ancestor-or-self::*[contains(@class,' topic/ul ')])"/> -->
+<!-- <xsl:variable name="slcount" select="count(ancestor-or-self::*[contains(@class,' topic/sl ')])"/> -->
+<!-- <xsl:variable name="ddcount" select="count(ancestor-or-self::*[contains(@class,' topic/dd ')])"/> -->
+<!-- <xsl:variable name="nestcount" select="number($olcount) + number($ulcount) + number($slcount) + number($ddcount)" /> -->
+<!-- <xsl:choose> -->
+<!-- <xsl:when test="number($nestcount)=1"> -->
+<!-- <xsl:text> </xsl:text> -->
+<!-- </xsl:when> -->
+<!-- <xsl:when test="number($nestcount)=2"> -->
+<!-- <xsl:text> </xsl:text> -->
+<!-- </xsl:when> -->
+<!-- <xsl:when test="number($nestcount)=3"> -->
+<!-- <xsl:text> </xsl:text> -->
+<!-- </xsl:when> -->
+<!-- <xsl:otherwise> -->
+<!-- <xsl:text> </xsl:text> -->
+<!-- </xsl:otherwise> -->
+<!-- </xsl:choose> -->
+ <xsl:call-template name="indentLI"/>
+ <xsl:text>&#x2D; </xsl:text>
+ <xsl:apply-templates />
+ <xsl:if test="following-sibling::*[contains(@class,' topic/sli ')]">
+ <xsl:text>&#xA;</xsl:text>
+ </xsl:if>
+ </xsl:template>
+
+
+ <xsl:template match="*[contains(@class,' topic/state ')]" name="topic.state">
+ <xsl:value-of select="name()"/><xsl:text>: </xsl:text><xsl:value-of select="@name"/><xsl:text>=</xsl:text><xsl:value-of select="@value"/>
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' topic/table ')]" name="topic.table">
+ <!-- no output -->
+ <xsl:call-template name="output-message">
+ <xsl:with-param name="msg">Eclipse context-sensitive help files do not support tables.</xsl:with-param>
+ <xsl:with-param name="msgnum">066</xsl:with-param>
+ <xsl:with-param name="msgsev">I</xsl:with-param>
+ </xsl:call-template>
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' topic/term ')]" name="topic.term">
+ <xsl:call-template name="checkPreceding" />
+ <xsl:apply-templates />
+ <xsl:call-template name="checkFollowing" />
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' topic/title ')]" name="topic.title">
+ <b><xsl:apply-templates /></b><xsl:text>&#xA;</xsl:text>
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' topic/tm ')]" name="topic.tm">
+ <!-- output nothing -->
+ <!--<xsl:call-template name="output-message">
+ <xsl:with-param name="msg">Trademarks are not required in context-sensitive help.</xsl:with-param>
+ <xsl:with-param name="msgnum">069</xsl:with-param>
+ <xsl:with-param name="msgsev">I</xsl:with-param>
+ </xsl:call-template>-->
+ <xsl:call-template name="checkPreceding" />
+ <xsl:apply-templates />
+ <xsl:call-template name="checkFollowing" />
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' topic/ul ')]" name="topic.ul">
+ <xsl:call-template name="twoPrecedingCRs" />
+ <!-- jta commenting out 11/03/2006 -->
+ <!-- <xsl:if test="parent::*[contains(@class,' topic/li ')] | parent::*[contains(@class,' topic/sli ')]"><xsl:text>&#xA;</xsl:text></xsl:if> -->
+ <xsl:apply-templates />
+ <xsl:call-template name="isNestedTextFollows" />
+ <xsl:call-template name="isFirstChildOfCsbody" />
+ </xsl:template>
+
+
+ <xsl:template match="*[contains(@class,' topic/xref ')]" name="topic.xref" priority="100">
+ <xsl:call-template name="checkPreceding" />
+ <xsl:choose>
+ <xsl:when test="*|text()"><xsl:text>&#34;</xsl:text><xsl:apply-templates select="*|text()"/><xsl:text>&#34;</xsl:text></xsl:when>
+ <xsl:otherwise><xsl:call-template name="href"/></xsl:otherwise>
+ </xsl:choose>
+ <xsl:call-template name="checkFollowing" />
+ </xsl:template>
+
+ <xsl:template name="href">
+ <!-- below adapted from rel-links.xsl -->
+ <xsl:choose>
+ <!-- For non-DITA formats - use the href as is -->
+ <xsl:when test="(not(@format) and (@type='external' or @scope='external')) or (@format and not(@format='dita' or @format='DITA'))">
+ <xsl:value-of select="@href"/>
+ </xsl:when>
+ <!-- For DITA - process the internal href -->
+ <xsl:when test="starts-with(@href,'#')">
+ <xsl:call-template name="parsehref">
+ <xsl:with-param name="href" select="@href"/>
+ </xsl:call-template>
+ </xsl:when>
+ <!-- It's to a DITA file - process the file name (adding the html extension)
+ and process the rest of the href -->
+ <xsl:when test="contains(@href,'.dita')">
+ <xsl:value-of select="substring-before(@href,'.dita')"/>.html<xsl:call-template name="parsehref"><xsl:with-param name="href" select="substring-after(@href,'.dita')"/></xsl:call-template>
+ </xsl:when>
+ <xsl:when test="@href=''"/>
+ <xsl:otherwise>
+ <xsl:call-template name="output-message">
+ <xsl:with-param name="msg">Unknown file extension in href: <xsl:value-of select="@href"/>
+If this is a link to a non-DITA resource, set the format attribute to match the resource (for example, 'txt', 'pdf', or 'html').
+If it's a link to a DITA resource, the file extension must be .dita .</xsl:with-param>
+ <xsl:with-param name="msgnum">015</xsl:with-param>
+ <xsl:with-param name="msgsev">E</xsl:with-param>
+ </xsl:call-template>
+ <xsl:value-of select="@href"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+
+ <!-- "/" is not legal in IDs - need to swap it with two underscores -->
+ <xsl:template name="parsehref">
+ <xsl:param name="href"/>
+ <xsl:choose>
+ <xsl:when test="contains($href,'/')">
+ <xsl:value-of select="substring-before($href,'/')"/>__<xsl:value-of select="substring-after($href,'/')"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="$href"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+
+
+
+
+ <!-- Highlight domain overrides -->
+
+ <xsl:template match="*[contains(@class,' hi-d/b ')]" name="topic.hi-d.b" priority="100">
+ <xsl:call-template name="checkPreceding" />
+ <b><xsl:apply-templates /></b>
+ <xsl:call-template name="checkFollowing" />
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' hi-d/i ')]" name="topic.hi-d.i" priority="100">
+ <xsl:call-template name="checkPreceding" />
+ <b><xsl:apply-templates /></b>
+ <xsl:call-template name="checkFollowing" />
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' hi-d/sub ')]" name="topic.hi-d.sub" priority="100">
+ <xsl:apply-templates />
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' hi-d/sup ')]" name="topic.hi-d.sup" priority="100">
+ <xsl:text>&#94;</xsl:text><xsl:apply-templates /> <!-- adds caret -->
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' hi-d/tt ')]" name="topic.hi-d.tt" priority="100">
+ <xsl:call-template name="checkPreceding" />
+ <xsl:apply-templates />
+ <xsl:call-template name="checkFollowing" />
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' hi-d/u ')]" name="topic.hi-d.u" priority="100">
+ <xsl:call-template name="checkPreceding" />
+ <b><xsl:apply-templates /></b>
+ <xsl:call-template name="checkFollowing" />
+ </xsl:template>
+
+ <!-- Programming domain overrides -->
+
+ <xsl:template match="*[contains(@class,' pr-d/codeph ')]" name="topic.pr-d.codeph" priority="100">
+ <xsl:call-template name="checkPreceding" />
+ <b><xsl:apply-templates /></b>
+ <xsl:call-template name="checkFollowing" />
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' pr-d/kwd ')]" name="topic.pr-d.kwd" priority="100">
+ <xsl:apply-templates />
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' pr-d/var ')]" name="topic.pr-d.var" priority="100">
+ <xsl:apply-templates />
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' pr-d/synph ')]" name="topic.pr-d.synph" priority="100">
+ <xsl:apply-templates />
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' pr-d/oper ')]" name="topic.pr-d.oper" priority="100">
+ <xsl:apply-templates />
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' pr-d/delim ')]" name="topic.pr-d.delim" priority="100">
+ <xsl:apply-templates />
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' pr-d/sep ')]" name="topic.pr-d.sep" priority="100">
+ <xsl:apply-templates />
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' pr-d/repsep ')]" name="topic.pr-d.repsep" priority="100">
+ <xsl:apply-templates />
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' pr-d/option ')]" name="topic.pr-d.option" priority="100">
+ <xsl:apply-templates />
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' pr-d/parmname ')]" name="topic.pr-d.parmname" priority="100">
+ <xsl:call-template name="checkPreceding" />
+ <xsl:apply-templates />
+ <xsl:call-template name="checkFollowing" />
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' pr-d/apiname ')]" name="topic.pr-d.apiname" priority="100">
+ <xsl:call-template name="checkPreceding" />
+ <xsl:apply-templates />
+ <xsl:call-template name="checkFollowing" />
+ </xsl:template>
+
+ <!-- Programming overrides - not found in pr-d.xsl? -->
+
+ <xsl:template match="*[contains(@class,' pr-d/codeblock ')]" name="topic.pr-d.codeblock" priority="100">
+ <!-- same as topic.lines -->
+ <xsl:call-template name="br-replace">
+ <xsl:with-param name="brtext" select="."/>
+ </xsl:call-template>
+ <xsl:call-template name="isFirstChildOfCsbody" />
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' pr-d/parml ')]" name="topic.pr-d.parml" priority="100">
+ <xsl:apply-templates />
+ <xsl:call-template name="isFirstChildOfCsbody" />
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' pr-d/plentry ')]" name="topic.pr-d.plentry" priority="100">
+ <!-- same as topic.dlentry -->
+ <xsl:apply-templates />
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' pr-d/pt ')]" name="topic.pr-d.pt" priority="100">
+ <!-- same as topic.dt -->
+ <b><xsl:apply-templates /></b><xsl:text>&#xA;</xsl:text>
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' pr-d/pd ')]" name="topic.pr-d.pd" priority="100">
+ <!-- same as topic.dd -->
+ <xsl:text> </xsl:text>
+ <xsl:apply-templates />
+ <xsl:if test="position()!=last()">
+ <xsl:text>&#xA;</xsl:text>
+ </xsl:if>
+ </xsl:template>
+
+
+
+ <!-- Syntax diagram (Programming domain) overrides -->
+
+ <xsl:template match="*[contains(@class, ' pr-d/syntaxdiagram ')]" name="topic.pr-d.syntaxdiagram" priority="100">
+ <xsl:call-template name="output-message">
+ <xsl:with-param name="msg">Syntax diagrams are not supported in Eclipse context-sensitive help.</xsl:with-param>
+ <xsl:with-param name="msgnum">066</xsl:with-param>
+ <xsl:with-param name="msgsev">I</xsl:with-param>
+ </xsl:call-template>
+ </xsl:template>
+
+<!-- above template modified; several more syntax diagram templates removed -->
+
+
+
+ <!-- Software domain overrides -->
+
+ <xsl:template match="*[contains(@class,' sw-d/cmdname ')]" name="topic.sw-d.cmdname" priority="100">
+ <xsl:call-template name="checkPreceding" />
+ <b><xsl:apply-templates /></b>
+ <xsl:call-template name="checkFollowing" />
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' sw-d/filepath ')]" name="topic.sw-d.filepath" priority="100">
+ <xsl:call-template name="checkPreceding" />
+ <xsl:apply-templates />
+ <xsl:call-template name="checkFollowing" />
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' sw-d/msgnum ')]" name="topic.sw-d.msgnum" priority="100">
+ <b><xsl:apply-templates /></b>
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' sw-d/msgph ')]" name="topic.sw-d.msgph" priority="100">
+ <b><xsl:apply-templates /></b>
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' sw-d/systemoutput ')]" name="topic.sw-d.systemoutput" priority="100">
+ <xsl:call-template name="checkPreceding" />
+ <b><xsl:apply-templates /></b>
+ <xsl:call-template name="checkFollowing" />
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' sw-d/userinput ')]" name="topic.sw-d.userinput" priority="100">
+ <xsl:call-template name="checkPreceding" />
+ <b><xsl:apply-templates /></b>
+ <xsl:call-template name="checkFollowing" />
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' sw-d/varname ')]" name="topic.sw-d.varname" priority="100">
+ <b><xsl:apply-templates /></b>
+ </xsl:template>
+
+
+
+ <!-- UI domain overrides -->
+
+ <xsl:template match="*[contains(@class,' ui-d/menucascade ')]" name="topic.ui-d.menucascade" priority="100">
+ <xsl:apply-templates />
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' ui-d/screen ')]" name="topic.ui-d.screen" priority="100">
+ <!-- no output -->
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' ui-d/shortcut ')]" name="topic.ui-d.shortcut" priority="100">
+ <b><xsl:apply-templates /></b>
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' ui-d/uicontrol ')]" name="topic.ui-d.uicontrol" priority="100">
+ <xsl:call-template name="checkPreceding" />
+ <xsl:if test="parent::*[contains(@class,' ui-d/menucascade ')] and preceding-sibling::*[contains(@class,' ui-d/uicontrol ')]">
+ <xsl:text> > </xsl:text>
+ </xsl:if>
+ <b><xsl:apply-templates /></b>
+ <xsl:call-template name="checkFollowing" />
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' ui-d/wintitle ')]" name="topic.ui-d.wintitle" priority="100">
+ <xsl:call-template name="checkPreceding" />
+ <b><xsl:apply-templates /></b>
+ <xsl:call-template name="checkFollowing" />
+ </xsl:template>
+
+
+
+ <!-- Utilities domain overrides -->
+
+ <!-- imagemap -->
+ <xsl:template match="*[contains(@class,' ut-d/imagemap ')]" name="topic.ut-d.imagemap">
+ <!-- Process the image to get alternate text -->
+ <!--<xsl:call-template name="output-message">
+ <xsl:with-param name="msg">Eclipse context-sensitive help files do not support images.</xsl:with-param>
+ <xsl:with-param name="msgnum">066</xsl:with-param>
+ <xsl:with-param name="msgsev">I</xsl:with-param>
+ </xsl:call-template>-->
+ <xsl:apply-templates select="*[contains(@class,' topic/image ')]"/>
+ </xsl:template>
+
+
+
+ <!-- Miscellaneous templates -->
+
+ <xsl:template match="text()">
+ <xsl:value-of select="normalize-space(.)" />
+ </xsl:template>
+
+ <xsl:template name="commonattributes" />
+ <xsl:template name="setscale" />
+ <xsl:template name="flagit" />
+ <xsl:template name="start-revflag" />
+ <xsl:template name="end-revflag" />
+
+ <xsl:template name="checkPreceding">
+ <xsl:if test="(substring(preceding-sibling::text()[position()=string-length()],1,1)!=' ') and (substring(preceding-sibling::text()[position()=string-length()],1,1)!='(')"><xsl:text> </xsl:text></xsl:if>
+ </xsl:template>
+
+ <xsl:template name="checkFollowing">
+ <xsl:if test="(substring(following-sibling::text()[position()=1],1,1)!='.') and (substring(following-sibling::text()[position()=1],1,1)!=',') and (substring(following-sibling::text()[position()=1],1,1)!=';') and (substring(following-sibling::text()[position()=1],1,1)!=':')"><xsl:text> </xsl:text></xsl:if>
+ </xsl:template>
+
+ <xsl:template name="twoPrecedingCRs">
+ <!-- <xsl:if test="parent::*[contains(@class,' topic/desc ')] | parent::*[contains(@class,' topic/p ')] | parent::*[contains(@class,' topic/note ')] | parent::*[contains(@class,' topic/lq ')] | parent::*[contains(@class,' topic/li ')] | parent::*[contains(@class,' topic/sli ')] | parent::*[contains(@class,' topic/itemgroup ')] | parent::*[contains(@class,' topic/dd ')]"> -->
+ <xsl:choose>
+ <xsl:when test="parent::*[contains(@class,'topic/li ')] | parent::*[contains(@class, 'topic/dd ')] ">
+ <xsl:choose>
+ <xsl:when test="self::*[contains(@class,' topic/ul ')] | self::*[contains(@class,' topic/sl ')] | self::*[contains(@class,' topic/ol ')]">
+ <xsl:text>&#xA;</xsl:text>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:text>&#xA;&#xA;</xsl:text>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:when>
+ <xsl:when test="parent::*[contains(@class,' topic/p ')]">
+ <xsl:if test="preceding-sibling::text()"> <!-- only add 2 cr's if nesting p has text before nested tag -->
+ <xsl:text>&#xA;&#xA;</xsl:text>
+ </xsl:if>
+ </xsl:when>
+ <xsl:when test="not(parent::*[contains(@class,' cshelp/csbody ')])">
+ <xsl:text>&#xA;&#xA;</xsl:text>
+ </xsl:when>
+ <xsl:otherwise>
+ <!-- do nothing -->
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+
+<!-- jta: endOfP no longer called 11/03/2006 -->
+ <xsl:template name="endOfP">
+ <xsl:choose>
+ <xsl:when test="parent::*[contains(@class,' topic/p ')] and position()=last()">
+ <!-- do nothing -->
+ </xsl:when>
+ <xsl:when test="parent::*[contains(@class,' topic/p ')] and position()!=last()">
+ <xsl:text>&#xA;</xsl:text>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:text>&#xA;&#xA;</xsl:text>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+
+<!-- jta: listEndOfP no longer called 11/03/2006 -->
+ <xsl:template name="listEndOfP">
+ <xsl:choose>
+ <!-- jta 10/16/2006 -->
+ <xsl:when test="parent::*[contains(@class,' topic/p ')] | parent::*[contains(@class,' topic/desc ')] | parent::*[contains(@class,' topic/note ')] | parent::*[contains(@class,' topic/lq ')] | parent::*[contains(@class,' topic/li ')] | parent::*[contains(@class,' topic/sli ')] | parent::*[contains(@class,' topic/itemgroup ')] | parent::*[contains(@class,' topic/dd ')]">
+ <!-- do nothing -->
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:text>&#xA;&#xA;</xsl:text>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+
+<!-- jta 10/16/2006 -->
+ <xsl:template name="isFirstChildOfCsbody">
+ <xsl:choose>
+ <xsl:when test="parent::*[contains(@class,' cshelp/csbody ')]">
+ <xsl:if test="following-sibling::*">
+ <xsl:text>&#xA;&#xA;</xsl:text>
+ </xsl:if>
+ </xsl:when>
+ <xsl:when test="parent::*[contains(@class,' topic/dd ')] | parent::*[contains(@class,' topic/li ')]">
+ <xsl:choose>
+ <xsl:when test="self::*[contains(@class,' topic/ol ')] | self::*[contains(@class,' topic/sl ')] | self::*[contains(@class,' topic/ul ')]">
+ <!-- do nothing -->
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:if test="not(following-sibling::*)"><xsl:text>&#xA;</xsl:text></xsl:if>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:when>
+ <xsl:otherwise>
+ <!-- do nothing -->
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+
+<!-- jta 10/19/2006 -->
+ <xsl:template name="indentDL">
+ <xsl:variable name="ddcount" select="count(ancestor::*[contains(@class,' topic/dd ')])"/>
+ <xsl:variable name="licount" select="count(ancestor::*[contains(@class,' topic/li ')])"/>
+ <xsl:variable name="lqcount" select="count(ancestor::*[contains(@class,' topic/lq ')])"/>
+ <xsl:variable name="notecnt" select="count(ancestor::*[contains(@class,' topic/note ')])"/>
+ <xsl:variable name="dd_adj"><xsl:choose><xsl:when test="number($ddcount)>0">1</xsl:when><xsl:otherwise>0</xsl:otherwise></xsl:choose></xsl:variable>
+ <xsl:variable name="li_adj"><xsl:choose><xsl:when test="number($licount)>0">1</xsl:when><xsl:otherwise>0</xsl:otherwise></xsl:choose></xsl:variable>
+ <xsl:variable name="indent" select="number($ddcount) + number($dd_adj) + number($licount) + number($li_adj) + number($lqcount) + number($notecnt)" />
+ <xsl:choose>
+ <xsl:when test="number($indent)=1">
+ <xsl:text> </xsl:text>
+ </xsl:when>
+ <xsl:when test="number($indent)=2">
+ <xsl:text> </xsl:text>
+ </xsl:when>
+ <xsl:when test="number($indent)=3">
+ <xsl:text> </xsl:text>
+ </xsl:when>
+ <xsl:when test="number($indent)=4">
+ <xsl:text> </xsl:text>
+ </xsl:when>
+ <xsl:when test="number($indent)=5">
+ <xsl:text> </xsl:text>
+ </xsl:when>
+ <xsl:when test="number($indent)=6">
+ <xsl:text> </xsl:text>
+ </xsl:when>
+ <xsl:otherwise>
+ <!-- no indent -->
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+
+ <xsl:template name="indentP">
+ <xsl:variable name="ddcount" select="count(ancestor::*[contains(@class,' topic/dd ')])"/>
+ <xsl:variable name="licount" select="count(ancestor::*[contains(@class,' topic/li ')])"/>
+ <xsl:variable name="lqcount" select="count(ancestor::*[contains(@class,' topic/lq ')])"/>
+ <xsl:variable name="notecnt" select="count(ancestor::*[contains(@class,' topic/note ')])"/>
+ <xsl:variable name="p_count" select="count(ancestor::*[contains(@class,' topic/p ')])"/>
+ <!-- <xsl:variable name="dd_adj"><xsl:choose><xsl:when test="number($ddcount)>0">1</xsl:when><xsl:otherwise>0</xsl:otherwise></xsl:choose></xsl:variable> -->
+ <xsl:variable name="li_adj"><xsl:choose><xsl:when test="number($licount)>0">1</xsl:when><xsl:otherwise>0</xsl:otherwise></xsl:choose></xsl:variable>
+ <xsl:variable name="indent" select="number($ddcount) + number($licount) + number($li_adj) + number($lqcount) + number($notecnt) + number($p_count)" />
+ <xsl:choose>
+ <xsl:when test="number($indent)=1">
+ <xsl:text> </xsl:text>
+ </xsl:when>
+ <xsl:when test="number($indent)=2">
+ <xsl:text> </xsl:text>
+ </xsl:when>
+ <xsl:when test="number($indent)=3">
+ <xsl:text> </xsl:text>
+ </xsl:when>
+ <xsl:when test="number($indent)=4">
+ <xsl:text> </xsl:text>
+ </xsl:when>
+ <xsl:when test="number($indent)=5">
+ <xsl:text> </xsl:text>
+ </xsl:when>
+ <xsl:when test="number($indent)=6">
+ <xsl:text> </xsl:text>
+ </xsl:when>
+ <xsl:otherwise>
+ <!-- no indent -->
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+
+ <xsl:template name="indentNote">
+ <xsl:variable name="ddcount" select="count(ancestor::*[contains(@class,' topic/dd ')])"/>
+ <xsl:variable name="licount" select="count(ancestor::*[contains(@class,' topic/li ')])"/>
+ <xsl:variable name="lqcount" select="count(ancestor::*[contains(@class,' topic/lq ')])"/>
+ <xsl:variable name="notecnt" select="count(ancestor::*[contains(@class,' topic/note ')])"/>
+ <xsl:variable name="p_count" select="count(ancestor::*[contains(@class,' topic/p ')])"/>
+ <xsl:variable name="dd_adj"><xsl:choose><xsl:when test="number($ddcount)>0">1</xsl:when><xsl:otherwise>0</xsl:otherwise></xsl:choose></xsl:variable>
+ <xsl:variable name="li_adj"><xsl:choose><xsl:when test="number($licount)>0">1</xsl:when><xsl:otherwise>0</xsl:otherwise></xsl:choose></xsl:variable>
+ <xsl:variable name="indent" select="number($ddcount) + number($dd_adj) + number($licount) + number($li_adj) + number($lqcount) + number($notecnt) + number($p_count)" />
+ <xsl:choose>
+ <xsl:when test="number($indent)=1">
+ <xsl:text> </xsl:text>
+ </xsl:when>
+ <xsl:when test="number($indent)=2">
+ <xsl:text> </xsl:text>
+ </xsl:when>
+ <xsl:when test="number($indent)=3">
+ <xsl:text> </xsl:text>
+ </xsl:when>
+ <xsl:when test="number($indent)=4">
+ <xsl:text> </xsl:text>
+ </xsl:when>
+ <xsl:when test="number($indent)=5">
+ <xsl:text> </xsl:text>
+ </xsl:when>
+ <xsl:when test="number($indent)=6">
+ <xsl:text> </xsl:text>
+ </xsl:when>
+ <xsl:otherwise>
+ <!-- no indent -->
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+
+ <xsl:template name="indentLQ">
+ <xsl:variable name="ddcount" select="count(ancestor::*[contains(@class,' topic/dd ')])"/>
+ <xsl:variable name="licount" select="count(ancestor::*[contains(@class,' topic/li ')])"/>
+ <xsl:variable name="lqcount" select="count(ancestor::*[contains(@class,' topic/lq ')])"/>
+ <xsl:variable name="notecnt" select="count(ancestor::*[contains(@class,' topic/note ')])"/>
+ <xsl:variable name="p_count" select="count(ancestor::*[contains(@class,' topic/p ')])"/>
+ <xsl:variable name="dd_adj"><xsl:choose><xsl:when test="number($ddcount)>0">1</xsl:when><xsl:otherwise>0</xsl:otherwise></xsl:choose></xsl:variable>
+ <xsl:variable name="li_adj"><xsl:choose><xsl:when test="number($licount)>0">1</xsl:when><xsl:otherwise>0</xsl:otherwise></xsl:choose></xsl:variable>
+ <xsl:variable name="indent" select="number($ddcount) + number($dd_adj) + number($licount) + number($li_adj) + number($lqcount) + number($notecnt) + number($p_count)" />
+ <xsl:choose>
+ <xsl:when test="number($indent)=0">
+ <xsl:text> </xsl:text>
+ </xsl:when>
+ <xsl:when test="number($indent)=1">
+ <xsl:text> </xsl:text>
+ </xsl:when>
+ <xsl:when test="number($indent)=2">
+ <xsl:text> </xsl:text>
+ </xsl:when>
+ <xsl:when test="number($indent)=3">
+ <xsl:text> </xsl:text>
+ </xsl:when>
+ <xsl:when test="number($indent)=4">
+ <xsl:text> </xsl:text>
+ </xsl:when>
+ <xsl:when test="number($indent)=5">
+ <xsl:text> </xsl:text>
+ </xsl:when>
+ <xsl:when test="number($indent)=6">
+ <xsl:text> </xsl:text>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:text> </xsl:text>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+
+
+ <xsl:template name="indentLI">
+ <xsl:variable name="ddcount" select="count(ancestor::*[contains(@class,' topic/dd ')])"/>
+ <xsl:variable name="licount" select="count(ancestor::*[contains(@class,' topic/li ')])"/>
+ <xsl:variable name="lqcount" select="count(ancestor::*[contains(@class,' topic/lq ')])"/>
+ <xsl:variable name="notecnt" select="count(ancestor::*[contains(@class,' topic/note ')])"/>
+ <xsl:variable name="p_count" select="count(ancestor::*[contains(@class,' topic/p ')])"/>
+ <xsl:variable name="dd_adj"><xsl:choose><xsl:when test="number($ddcount)>0">1</xsl:when><xsl:otherwise>0</xsl:otherwise></xsl:choose></xsl:variable>
+ <xsl:variable name="li_adj"><xsl:choose><xsl:when test="number($licount)>0">1</xsl:when><xsl:otherwise>0</xsl:otherwise></xsl:choose></xsl:variable>
+ <xsl:variable name="p__adj"><xsl:choose><xsl:when test="number($p_count)>0">-1</xsl:when><xsl:otherwise>0</xsl:otherwise></xsl:choose></xsl:variable>
+ <xsl:variable name="indent" select="number($ddcount) + number($dd_adj) + number($licount) + number($li_adj) + number($lqcount) + number($notecnt) + number($p_count) + number($p__adj)" />
+ <xsl:choose>
+ <xsl:when test="number($indent)=0">
+ <xsl:text> </xsl:text>
+ </xsl:when>
+ <xsl:when test="number($indent)=1">
+ <xsl:text> </xsl:text>
+ </xsl:when>
+ <xsl:when test="number($indent)=2">
+ <xsl:text> </xsl:text>
+ </xsl:when>
+ <xsl:when test="number($indent)=3">
+ <xsl:text> </xsl:text>
+ </xsl:when>
+ <xsl:when test="number($indent)=4">
+ <xsl:text> </xsl:text>
+ </xsl:when>
+ <xsl:when test="number($indent)=5">
+ <xsl:text> </xsl:text>
+ </xsl:when>
+ <xsl:when test="number($indent)=6">
+ <xsl:text> </xsl:text>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:text> </xsl:text>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+
+ <xsl:template name="isNestedTextFollows">
+ <!-- <xsl:if test="parent::*[contains(@class,' topic/dd ')][following-sibling::text()] |
+ parent::*[contains(@class,' topic/lq ')][following-sibling::text()] |
+ parent::*[contains(@class,' topic/li ')][following-sibling::text()] |
+ parent::*[contains(@class,' topic/note ')][following-sibling::text()] |
+ parent::*[contains(@class,' topic/p ')][following-sibling::text()]"> -->
+ <xsl:if test="not(parent::*[contains(@class,' cshelp/csbody ')]) and following-sibling::text()">
+ <xsl:text>&#xA;&#xA;</xsl:text> <!-- jta 11/03/2006: condition when nesting tag has more text -->
+ </xsl:if>
+ </xsl:template>
+
+ <!-- IBM Copyright - English only output, and only when copyright belongs to IBM -->
+
+ <xsl:template name="ibmcopyright">
+ <xsl:variable name="userCopyright">
+ <xsl:value-of select="self::dita/*/*[contains(@class,' topic/prolog ')]/
+ *[contains(@class,' topic/copyright ')]/
+ *[contains(@class,' topic/copyrholder ')] |
+ *[contains(@class,' topic/prolog ')]/
+ *[contains(@class,' topic/copyright ')]/
+ *[contains(@class,' topic/copyrholder ')]"/>
+ </xsl:variable>
+ <xsl:variable name="copyYears">
+ <xsl:value-of select="self::dita/*/*[contains(@class,' topic/prolog ')]/
+ *[contains(@class,' topic/copyright ')]/
+ *[contains(@class,' topic/copyryear ')]/@year |
+ *[contains(@class,' topic/prolog ')]/
+ *[contains(@class,' topic/copyright ')]/
+ *[contains(@class,' topic/copyryear ')]/@year"/>
+ </xsl:variable>
+ <xsl:choose>
+ <xsl:when test="normalize-space($userCopyright)!='' and not(contains($userCopyright,'IBM'))">
+ <!-- P018721: user copyright is specified, and it does not contain IBM; do not put out the comment -->
+ </xsl:when>
+ <xsl:when test="(self::dita/*/*[contains(@class,' topic/prolog ')]/*[contains(@class,' topic/copyright ')] |
+ *[contains(@class,' topic/prolog ')]/*[contains(@class,' topic/copyright ')]) and
+ normalize-space($userCopyright)='' and normalize-space($copyYears)=''">
+ <!-- P018721: if user forces empty copyright, empty year, do not put out the copyright comment -->
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:variable name="childlang">
+ <xsl:choose>
+ <xsl:when test="self::dita">
+ <xsl:for-each select="*[1]"><xsl:call-template name="getLowerCaseLang"/></xsl:for-each>
+ </xsl:when>
+ <xsl:otherwise><xsl:call-template name="getLowerCaseLang"/></xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+ <xsl:choose>
+ <xsl:when test="$childlang='en-us' or $childlang='en'">
+ <xsl:comment> All rights reserved. Licensed Materials Property of IBM </xsl:comment><xsl:value-of select="$newline"/>
+ <xsl:comment> US Government Users Restricted Rights </xsl:comment><xsl:value-of select="$newline"/>
+ <xsl:comment> Use, duplication or disclosure restricted by </xsl:comment><xsl:value-of select="$newline"/>
+ <xsl:comment> GSA ADP Schedule Contract with IBM Corp. </xsl:comment><xsl:value-of select="$newline"/>
+ </xsl:when>
+ <xsl:otherwise/>
+ </xsl:choose>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+
+</xsl:stylesheet>
diff --git a/src/help/studio_help/ReadMe.txt b/src/help/studio_help/ReadMe.txt
new file mode 100644
index 0000000..2099b7c
--- /dev/null
+++ b/src/help/studio_help/ReadMe.txt
@@ -0,0 +1,13 @@
+Setting up to build:
+1. Obtain the DITA Open Toolkit from http://dita-ot.sourceforge.net. Install it into the tools folder. Note that there is already a ditaot plugin in this folder; that plugin needs to reside in the plugins folder within your ditaot installation.
+2. Obtain DITAReports from http://dita-ot.sourceforge.net/doc/ot-userguide131/ditaotug131-18042007-tools.zip. Install it so that you wind up with a ditareports folder inside tools (parallel to the ditaot folder).
+3. Obtain Linklint from http://www.linklint.org and install it into the tools folder, so that you wind up with a linklint folder inside tools. You should now have three folders inside the tools folder: "ditaot", "ditareports", and "linklint", and within the plugins folder inside ditaot you should see cshelp (part of ditaot) and com.mot.mdb.deved.xhtml. If the cshelp plugin isn't there, you'll need to obtain it from http://dita-ot.sourceforge.net and install it separately.
+
+Instructions for building the online help on Mac OS X:
+
+1. In Finder, double-click docs_dita/studio_help/tools/ditaot/startcmd_mac.command. The terminal window will open and the environment will be set up.
+2. In the terminal window, do:
+ cd ../../etc/buildfiles/android_studio-help
+ ant all -f build.xml
+
+There are Windows batch files (in tools/ditaot) that can be used in place of step 1 on a Windows machine.
diff --git a/src/help/studio_help/etc/buildfiles/android_studio-help/build.properties b/src/help/studio_help/etc/buildfiles/android_studio-help/build.properties
new file mode 100644
index 0000000..3a716d6
--- /dev/null
+++ b/src/help/studio_help/etc/buildfiles/android_studio-help/build.properties
@@ -0,0 +1,6 @@
+# Set version number for all plug-ins
+plugin-version=4.1.0.release
+# Set build number for all plug-in zip files
+build-number=04.01.00i_b002
+# Generate a ditamap debugging report
+report=true
diff --git a/src/help/studio_help/etc/buildfiles/android_studio-help/build.xml b/src/help/studio_help/etc/buildfiles/android_studio-help/build.xml
new file mode 100644
index 0000000..1e95743
--- /dev/null
+++ b/src/help/studio_help/etc/buildfiles/android_studio-help/build.xml
@@ -0,0 +1,66 @@
+<?xml version="1.0" encoding="utf-8"?>
+<project name="android_studio-help" default="all" basedir=".">
+
+ <!--Properties unique to this build (others are specified in ../build_lib.xml)-->
+
+ <loadproperties srcfile="build.properties"/>
+
+ <!--Location of output folder where each plug-in's folder goes-->
+ <property name="out-projdir" value="${basedir}/../../../out/android_studio-help"/>
+ <property name="out-basedir" value="${out-projdir}/product"/>
+ <property name="out-jardir" value="${out-basedir}/jars"/>
+ <!--Name of plug-in's TOC file (without extension)-->
+ <property name="toc-filenoext" value="main"/>
+ <!--Path of file containing context extension points for context-sensitive help-->
+ <property name="context-file" value="${basedir}/context_extension_points.xml"/>
+ <property name="report" value="false"/>
+
+ <import file="../build_lib.xml"/>
+
+ <target name="all" depends="plugins"/>
+
+ <target name="plugins" depends="clean" description="Build Studio for Android help plug-ins.">
+
+ <mkdir dir="${out-jardir}"/>
+
+ <!-- Call build-topics-plugin macro to build help for each ditamap. -->
+ <build-topics-plugin map="android_help-studio.ditamap" ditaval="${basedir}/filter_android-studio.ditaval"
+ hdr="${basedir}/../../resources/hdr-none.xml">
+ <build-cshelp cshelp-topic="${src-dir}/topics/cs_android.dita"
+ context-out-dir="${@{map}-plugin-temp-dir}/contexts" ditaval="${basedir}/filter_android-studio.ditaval"
+ context-temp-dir="${@{map}-plugin-temp-dir}_contexts"
+ plugin-out-dir="${out-basedir}/${@{map}-plugin-id}_${plugin-version}"/>
+ <zip destfile="${out-jardir}/${@{map}-plugin-id}_${plugin-version}.jar"
+ basedir="${out-basedir}/${@{map}-plugin-id}_${plugin-version}"/>
+ <report map="@{map}"/>
+ <checklinks dir="${out-basedir}/${@{map}-plugin-id}_${plugin-version}" map="@{map}"/>
+ <!-- Create zip file from jar so zip filename includes build number -->
+ <zip destfile="${out-jardir}/android_studio-help_${build-number}.zip">
+ <fileset dir="${out-jardir}" includes="${@{map}-plugin-id}_${plugin-version}.jar"/>
+ </zip>
+ </build-topics-plugin>
+
+ </target>
+
+
+ <target name="releasenotes">
+ <property name="mapfile" value="android_studio-release-notes.ditamap"/>
+ <basename property="mapname" file="${mapfile}" suffix=".ditamap"/>
+ <property name="relnote-out-dir" value="${out-projdir}/web/${mapname}"/>
+ <delete dir="${relnote-out-dir}" quiet="yes"/>
+ <!-- Call DITA-OT build script-->
+ <ant antfile="${dita.dir}/build.xml" target="init" description="Call DITA OT build script">
+ <property name="transtype" value="xhtml"/>
+ <property name="args.input" value="${src-dir}/${mapfile}"/>
+ <property name="dita.input.valfile" value="${basedir}/filter_android-studio.ditaval"/>
+ <property name="output.dir" value="${relnote-out-dir}"/>
+ <property name="dita.temp.dir" value="${temp-dir}/${mapname}"/>
+ <property name="args.logdir" value="${log-dir}"/>
+ </ant>
+
+ <deleteflagimages output-dir="${relnote-out-dir}"/>
+ <replacecss output-dir="${relnote-out-dir}"/>
+
+ </target>
+
+</project>
diff --git a/src/help/studio_help/etc/buildfiles/android_studio-help/context_extension_points.xml b/src/help/studio_help/etc/buildfiles/android_studio-help/context_extension_points.xml
new file mode 100644
index 0000000..ccae836
--- /dev/null
+++ b/src/help/studio_help/etc/buildfiles/android_studio-help/context_extension_points.xml
@@ -0,0 +1,22 @@
+
+<!--Context extension point for all Studio for Android cshelp-->
+<extension point="org.eclipse.help.contexts">
+
+ <contexts file="cs_android.xml" plugin="com.motorola.studio.android.codeutils"/>
+ <contexts file="cs_android.xml" plugin="com.motorola.studio.android.devices.services"/>
+ <contexts file="cs_android.xml" plugin="com.motorola.studio.android.emulator"/>
+ <contexts file="cs_android.xml" plugin="com.motorola.studio.android.installer"/>
+ <contexts file="cs_android.xml" plugin="com.motorola.studio.android.launch.ui"/>
+ <contexts file="cs_android.xml" plugin="com.motorola.studio.android.launch"/>
+ <contexts file="cs_android.xml" plugin="com.motorola.studio.android.packaging.ui"/>
+ <contexts file="cs_android.xml" plugin="com.motorola.studio.android.videos.views"/>
+ <contexts file="cs_android.xml" plugin="com.motorola.studio.android"/>
+ <contexts file="cs_android.xml" plugin="com.motorola.studio.platform.logger.collector"/>
+ <contexts file="cs_android.xml" plugin="com.motorola.studio.platform.tools.sign.ui"/>
+ <contexts file="cs_android.xml" plugin="com.motorolamobility.studio.android.db.core"/>
+ <contexts file="cs_android.xml" plugin="com.motorolamobility.studio.android.db.devices"/>
+ <contexts file="cs_android.xml" plugin="org.eclipse.datatools.sqltools.sqleditor"/>
+ <contexts file="cs_android.xml" plugin="org.eclipse.sequoyah.device.framework.ui"/>
+ <contexts file="cs_android.xml" plugin="com.motorolamobility.studio.android.certmanager"/>
+
+</extension>
diff --git a/src/help/studio_help/etc/buildfiles/android_studio-help/enable-win32.xml b/src/help/studio_help/etc/buildfiles/android_studio-help/enable-win32.xml
new file mode 100644
index 0000000..980649c
--- /dev/null
+++ b/src/help/studio_help/etc/buildfiles/android_studio-help/enable-win32.xml
@@ -0,0 +1,6 @@
+
+ <enablement>
+ <or>
+ <systemTest property="osgi.os" value="win32"/>
+ </or>
+ </enablement> \ No newline at end of file
diff --git a/src/help/studio_help/etc/buildfiles/android_studio-help/filter_android-studio.ditaval b/src/help/studio_help/etc/buildfiles/android_studio-help/filter_android-studio.ditaval
new file mode 100644
index 0000000..17e5db1
--- /dev/null
+++ b/src/help/studio_help/etc/buildfiles/android_studio-help/filter_android-studio.ditaval
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Filter file for MOTODEV Studio for Android help. -->
+<val>
+ <prop att="product" val="android-studio" action="include"/>
+ <prop att="product" val="javame-studio" action="exclude"/>
+ <prop att="product" val="javame-sdk" action="exclude"/>
+ <prop att="product" val="webui" action="exclude"/>
+</val>
diff --git a/src/help/studio_help/etc/buildfiles/build_lib.xml b/src/help/studio_help/etc/buildfiles/build_lib.xml
new file mode 100644
index 0000000..4c6efbb
--- /dev/null
+++ b/src/help/studio_help/etc/buildfiles/build_lib.xml
@@ -0,0 +1,223 @@
+<?xml version="1.0" encoding="utf-8"?>
+<project name="lib" basedir=".">
+
+ <taskdef resource="net/sf/antcontrib/antlib.xml"/>
+
+ <!--Location of DITA Open Toolkit-->
+ <property name="dita.dir" value="${basedir}/../../../tools/ditaot"/>
+ <!--Location of DITA source files to build-->
+ <property name="src-dir" value="${basedir}/../../../src"/>
+ <!--Location of temp folder-->
+ <property name="temp-dir" value="${basedir}/temp"/>
+ <!--Location of logs folder-->
+ <property name="log-dir" value="${basedir}/logs"/>
+ <!--Path of file containing context extension points for context-sensitive help-->
+ <property name="context-file" value="${basedir}/context_extension_points.xml"/>
+ <!--If present, load file containing Eclipse enablement elements for Windows-only content.-->
+ <available file="enable-win32.xml" property="enablement-present"/>
+ <if>
+ <isset property="enablement-present"/>
+ <then>
+ <loadfile property="enablement-win32" srcfile="enable-win32.xml"/>
+ </then>
+ </if>
+
+ <!-- References ANT extension that provides Perl 5 compatible regex functionality.-->
+ <property name="ant.regexp.regexpimpl" value="org.apache.tools.ant.util.regexp.JakartaOroRegexp"/>
+
+ <!--Import DITA OT build file-->
+ <import file="${dita.dir}/build.xml"/>
+
+ <target name="clean" description="Delete all generated files (out, temp, and logs folders)">
+ <delete dir="${out-projdir}" quiet="yes"/>
+ <delete dir="${log-dir}" quiet="yes"/>
+ <delete dir="${temp-dir}" quiet="yes"/>
+ <delete file="${basedir}/ditaot_batch.log"/>
+ </target>
+
+ <macrodef name="build-topics-plugin" description="Build a DevEd Eclipse help plug-in">
+ <attribute name="map" default="NOT_SET"/>
+ <attribute name="ditaval" default="NOT_SET"/>
+ <attribute name="hdr" default="NOT_SET"/>
+ <attribute name="provider" default="Motorola Mobility, Inc."/>
+ <element name="build-callers-tasks" implicit="yes" optional="yes"/>
+ <sequential>
+ <echo/>
+ <echo>=============================================================================</echo>
+ <echo>START build-topics-plugin for @{map}</echo>
+ <echo>Ditaval file: "@{ditaval}"</echo>
+ <property name="@{map}-path" value="${src-dir}/@{map}"/>
+ <!-- Reads ditamap into ANT XML properties so that build script can use same map ID as plug-in ID everywhere. -->
+ <xmlproperty file="${@{map}-path}" prefix="@{map}"/>
+ <property name="@{map}-plugin-id" value="${@{map}.map(id)}"/>
+ <echo>Plug-in ID: ${@{map}-plugin-id}</echo>
+ <property name="@{map}-plugin-out-dir"
+ value="${out-basedir}/${@{map}-plugin-id}_${plugin-version}"/>
+ <property name="@{map}-plugin-temp-dir"
+ value="${temp-dir}/${@{map}-plugin-id}_${plugin-version}"/>
+ <!-- Get copyright info from map file -->
+ <property name="@{map}-copyryear" value="${@{map}.map.topicmeta.copyright.copyryear(year)}"/>
+ <property name="@{map}-copyrholder" value="${@{map}.map.topicmeta.copyright.copyrholder}"/>
+ <echo>Copyright date: ${@{map}-copyryear}</echo>
+ <echo>Copyright holder: ${@{map}-copyrholder}</echo>
+ <!--Delete output folder before building so no unnecessary files from previous build are left.-->
+ <delete dir="${@{map}-plugin-out-dir}" quiet="yes"/>
+
+ <!-- Call DITA-OT build script-->
+ <ant antfile="${dita.dir}/build.xml" target="init" description="Call DITA OT build script">
+ <property name="transtype" value="eclipsehelp"/>
+ <property name="args.input" value="${@{map}-path}"/>
+ <property name="dita.input.valfile" value="@{ditaval}"/>
+ <property name="args.hdr" value="@{hdr}"/>
+ <property name="output.dir" value="${@{map}-plugin-out-dir}"/>
+ <property name="dita.temp.dir" value="${@{map}-plugin-temp-dir}"/>
+ <property name="args.logdir" value="${log-dir}"/>
+ <!-- Sets "Bundle-Version" in meta-mf/manifest.mf, the version of this help plug-in. -->
+ <property name="args.eclipse.version" value="${plugin-version}"/>
+ <property name="args.eclipse.provider" value="@{provider}"/>
+ <property name="args.eclipsehelp.toc" value="${toc-filenoext}"/>
+ </ant>
+
+ <!--Change filename of TOC file from ditamapname.xml to specified.-->
+ <basename property="@{map}-filenoext" file="@{map}" suffix=".ditamap"/>
+ <move file="${@{map}-plugin-out-dir}/${@{map}-filenoext}.xml"
+ tofile="${@{map}-plugin-out-dir}/${toc-filenoext}.xml"/>
+
+ <deleteflagimages output-dir="${@{map}-plugin-out-dir}"/>
+ <replacecss output-dir="${@{map}-plugin-out-dir}"/>
+
+ <!--Replace special anchors in TOC with Eclipse enablement elements-->
+ <!--Set anchor id="eclipse-enable-win32onlyXXX" for topics that should appear in TOC only in Windows.-->
+ <!--XXX can be anything (required only to make anchor id unique in ditamap file.-->
+ <if>
+ <isset property="enablement-present"/>
+ <then>
+ <replaceregexp file="${@{map}-plugin-out-dir}/${toc-filenoext}.xml"
+ match="\s*?&lt;anchor\s+id\s*?=\s*?&quot;eclipse-enable-win32only.*?&quot;\s*?/&gt;"
+ replace="${enablement-win32}" flags="gi"/>
+ </then>
+ </if>
+
+ <!--Move debugging files to temp folder.-->
+ <!--<move file="${@{map}-plugin-out-dir}/dita.list" todir="${@{map}-plugin-temp-dir}"/>
+ <move file="${@{map}-plugin-out-dir}/dita.xml.properties" todir="${@{map}-plugin-temp-dir}"/>-->
+
+ <!--Perform caller's implicit tasks here-->
+ <build-callers-tasks/>
+
+ <echo>END build-topics-plugin for @{map}</echo>
+ </sequential>
+ </macrodef>
+
+
+ <macrodef name="build-cshelp" description="Build cshelp for a DevEd Eclipse help plug-in">
+ <attribute name="cshelp-topic" default="NOT_SET"/>
+ <attribute name="ditaval" default="NOT_SET"/>
+ <attribute name="context-out-dir" default="NOT_SET"/>
+ <attribute name="context-temp-dir" default="NOT_SET"/>
+ <attribute name="plugin-out-dir" default="NOT_SET"/>
+ <element name="build-callers-tasks" implicit="yes" optional="yes"/>
+ <sequential>
+ <basename property="@{cshelp-topic}-filenoext" file="@{cshelp-topic}" suffix=".dita"/>
+ <echo/>
+ <echo>======================================================</echo>
+ <echo>START build-cshelp for ${@{cshelp-topic}-filenoext}</echo>
+ <!-- Call DITA-OT build script to build cshelp topics using special XSL-->
+ <ant antfile="${dita.dir}/build.xml" target="init">
+ <property name="args.input" value="@{cshelp-topic}"/>
+ <property name="dita.input.valfile" value="@{ditaval}"/>
+ <property name="output.dir" value="@{context-out-dir}"/>
+ <property name="dita.temp.dir" value="@{context-temp-dir}"/>
+ <property name="args.xsl" value="${dita.dir}/plugins/cshelp/xsl/dit2context.xsl"/>
+ <property name="dita.extname" value=".dita"/>
+ <property name="transtype" value="xhtml"/>
+ <property name="args.outext" value="xml"/>
+ </ant>
+ <!-- Add "topics/" to start of each href so that contexts link to topics in "topics" subfolder-->
+ <replaceregexp file="@{context-out-dir}/${@{cshelp-topic}-filenoext}.xml"
+ match="&lt;topic(\s+)href=&#34;" replace="&lt;topic\1href=&#34;topics/"
+ byline="true"/>
+ <!-- Move context XML file to this plug-in's output folder-->
+ <move file="@{context-out-dir}/${@{cshelp-topic}-filenoext}.xml" todir="@{plugin-out-dir}"/>
+ <!--Load context ext points file, then add it to the end of the plugin.xml file-->
+ <loadfile property="context-ext-pts" srcfile="${context-file}"/>
+ <replaceregexp file="@{plugin-out-dir}/plugin.xml" match="&lt;/plugin&gt;"
+ replace="${context-ext-pts}&lt;/plugin&gt;" byline="yes"/>
+
+ <!--Perform caller's implicit tasks here-->
+ <build-callers-tasks/>
+
+ <echo>END build-cshelp for ${@{cshelp-topic}-filenoext}</echo>
+ </sequential>
+ </macrodef>
+
+ <macrodef name="report" description="Generate debugging report for a ditamap">
+ <attribute name="map" default="NOT_SET"/>
+ <sequential>
+ <if>
+ <istrue value="${report}"/>
+ <then>
+ <exec dir="${basedir}" executable="php" output="${log-dir}/@{map}_debug-report.txt"
+ os="Mac OS X" failifexecutionfails="false" logerror="true">
+ <arg file="${dita.dir}/../ditareports/src/ditadebug.php"/>
+ <arg file="${src-dir}/@{map}"/>
+ </exec>
+ </then>
+ </if>
+ </sequential>
+ </macrodef>
+
+ <macrodef name="checklinks" description="Check links in HTML output and generate report">
+ <attribute name="dir" default="NOT_SET"/>
+ <attribute name="map" default="NOT_SET"/>
+ <sequential>
+ <!--Run linklint if report="true" and if OS is Mac or a Unix-like OS, which ship with Perl.-->
+ <if>
+ <and>
+ <istrue value="${report}"/>
+ <or>
+ <os family="mac"/>
+ <os family="unix"/>
+ </or>
+ </and>
+ <then>
+ <echo/>
+ <echo>------------------------------------------------------</echo>
+ <echo message="linklint: checking links in output"/>
+ <exec dir="${basedir}" executable="perl" os="Mac OS X" failifexecutionfails="false"
+ logerror="true">
+ <arg file="${dita.dir}/../linklint/src/linklint"/>
+ <arg value="-root"/>
+ <arg path="@{dir}"/>
+ <arg line="/@ -no_warn_index -htmlonly -quiet"/>
+ <arg value="-doc"/>
+ <arg path="${log-dir}/@{map}_link-report"/>
+ </exec>
+ <echo/>
+ <echo>See index.html in logs directory for full link report: ${log-dir}/@{map}_link-report</echo>
+ <echo>------------------------------------------------------</echo>
+ <echo/>
+ </then>
+ </if>
+ </sequential>
+ </macrodef>
+
+ <macrodef name="deleteflagimages" description="Delete flagging images inserted by DITA-OT">
+ <!--Delete flagging images inserted by DITA-OT because a ditaval file is specified-->
+ <attribute name="output-dir" default="NOT_SET"/>
+ <sequential>
+ <delete file="@{output-dir}/delta.gif"/>
+ <delete file="@{output-dir}/deltaend.gif"/>
+ </sequential>
+ </macrodef>
+
+ <macrodef name="replacecss" description="Replace default CSS">
+ <!--Delete extraneous CSS from output folder and copy Motorola Dev Ed CSS to output folder..-->
+ <attribute name="output-dir" default="NOT_SET"/>
+ <sequential>
+ <delete file="@{output-dir}/commonrtl.css"/>
+ <copy file="${basedir}/../../resources/commonltr.css" todir="@{output-dir}" overwrite="yes"/>
+ </sequential>
+ </macrodef>
+
+</project>
diff --git a/src/help/studio_help/etc/resources/commonltr.css b/src/help/studio_help/etc/resources/commonltr.css
new file mode 100644
index 0000000..ab4917c
--- /dev/null
+++ b/src/help/studio_help/etc/resources/commonltr.css
@@ -0,0 +1,217 @@
+/*
+ | This file is part of the DITA Open Toolkit project hosted on
+ | Sourceforge.net. See the accompanying license.txt file for
+ | applicable licenses.
+*/
+
+/*
+ | (c) Copyright IBM Corp. 2004, 2005 All Rights Reserved.
+ */
+
+
+.hdr-confidential {
+ background-color: #dddddd;
+ float: right;
+ padding: 0.5em;
+}
+
+.help_breadcrumbs {
+ font: message-box;
+ margin-bottom: 10px;
+}
+
+body {
+ color: black;
+ background-color: white;
+ font-family: Arial, sans-serif;
+ font-size: 0.8em;
+ margin-left: 10px;
+ margin-right: 2em;
+ margin-bottom: 15px;
+ margin-top: 4px;
+}
+
+.unresolved { background-color: skyblue; }
+.noTemplate { background-color: yellow; }
+
+.base { background-color: #ffffff; }
+
+/* Add space for top level topics */
+.nested0 { margin-top: 0em;}
+.nested1 { margin-top: 3em; margin-left: 2%; }
+.nested2 { margin-left: 2%;}
+
+div.glossentry { margin-top: 1em; }
+div.glossentry .topictitle2 { border-top: none; }
+
+/* div with class=p is used for paragraphs that contain blocks, to keep the XHTML valid */
+.p {margin-top: 1em}
+
+/* Default of italics to set apart figure captions */
+.figcap { font-style: italic }
+.figdesc { font-style: normal }
+
+/* Use @frame to create frames on figures */
+.figborder { border-style: solid; padding-left : 3px; border-width : 2px; padding-right : 3px; margin-top: 1em; border-color : Silver;}
+.figsides { border-left : 2px solid; padding-left : 3px; border-right : 2px solid; padding-right : 3px; margin-top: 1em; border-color : Silver;}
+.figtop { border-top : 2px solid; margin-top: 1em; border-color : Silver;}
+.figbottom { border-bottom : 2px solid; border-color : Silver;}
+.figtopbot { border-top : 2px solid; border-bottom : 2px solid; margin-top: 1em; border-color : Silver;}
+
+/* Most link groups are created with <div>. Ensure they have space before and after. */
+.familylinks { margin-top: 2em; }
+.ullinks { list-style-type: none; margin-top: 2em; margin-left: 0.5em; padding-left: 0.5em; }
+.ulchildlink { margin-top: 1em; margin-bottom: 1em }
+.olchildlink { margin-top: 1em; margin-bottom: 1em }
+.linklist { margin-bottom: 1em }
+.linklistwithchild { margin-left: 1.5em; margin-bottom: 1em }
+.sublinklist { margin-left: 1.5em; margin-bottom: 1em }
+.relconcepts { margin-top: 2em; margin-bottom: 1em }
+.reltasks { margin-top: 2em; margin-bottom: 1em }
+.relref { margin-top: 2em; margin-bottom: 1em }
+.relinfo { margin-top: 2em; margin-bottom: 1em }
+.breadcrumb { font-size : smaller; margin-bottom: 1em }
+.prereq { margin-left : 20px;}
+
+
+
+/* Set heading sizes, getting smaller for deeper nesting */
+.topictitle1 { margin-top: 0em; margin-bottom: .1em; font-size: 1.8em; }
+.topictitle2 { margin-top: 1em; margin-bottom: .45em; font-size: 1.25em; margin-left: -2%; border-top: thin solid #CCCCCC; padding-top: .5em;}
+.topictitle3 { margin-top: 2em; margin-bottom: 0.2em; font-size: 1.0em; font-weight: bold; margin-left: -2%; border-top: thin solid #CCCCCC; padding-top: 0.5em; }
+.topictitle4 { margin-top: .83em; font-size: 0.9em; font-weight: bold; }
+.topictitle5 { font-size: 0.8em; font-weight: bold; }
+.topictitle6 { font-size: 0.7em; font-style: italic; }
+.sectiontitle { margin-top: 1em; margin-bottom: 0em; color: black; font-size: 1.2em; font-weight: bold;}
+.nested2 .sectiontitle { font-size: 1.0em; }
+.section { margin-top: 2em; margin-bottom: 1em }
+.example { margin-top: 2em; margin-bottom: 1em }
+
+/* All note formats have the same default presentation */
+.note { margin-top: 1em; margin-bottom : 1em;}
+.notetitle { font-weight: bold }
+.notelisttitle { font-weight: bold }
+.tip { margin-top: 1em; margin-bottom : 1em;}
+.tiptitle { font-weight: bold }
+.fastpath { margin-top: 1em; margin-bottom : 1em;}
+.fastpathtitle { font-weight: bold }
+.important { margin-top: 1em; margin-bottom : 1em;}
+.importanttitle { font-weight: bold }
+.remember { margin-top: 1em; margin-bottom : 1em;}
+.remembertitle { font-weight: bold }
+.restriction { margin-top: 1em; margin-bottom : 1em;}
+.restrictiontitle { font-weight: bold }
+.attention { margin-top: 1em; margin-bottom : 1em;}
+.attentiontitle { font-weight: bold }
+.dangertitle { font-weight: bold }
+.danger { margin-top: 1em; margin-bottom : 1em;}
+.cautiontitle { font-weight: bold; margin-top: 0.53em; }
+.caution { font-weight: bold; margin-bottom : 1em; }
+
+/* Simple lists do not get a bullet */
+ul.simple { list-style-type: none }
+
+/* Used on the first column of a table, when rowheader="firstcol" is used */
+.firstcol { font-weight : bold;}
+
+/* Various basic phrase styles */
+.bold { font-weight: bold; }
+.boldItalic { font-weight: bold; font-style: italic; }
+.italic { font-style: italic; }
+.underlined { text-decoration: underline; }
+.uicontrol { font-weight: bold; }
+.parmname { font-weight: bold; }
+.kwd { font-weight: bold; }
+.defkwd { font-weight: bold; text-decoration: underline; }
+.var { font-style : italic;}
+.shortcut { text-decoration: underline; }
+.term { font-style: italic; }
+
+/* Default of bold for definition list terms */
+.dlterm { font-weight: bold; margin-top: 0.5em; }
+
+/* Use CSS to expand lists with @compact="no" */
+.dltermexpand { font-weight: bold; margin-top: 1em; }
+*[compact="yes"]>li { margin-top: 0em;}
+*[compact="no"]>li { margin-top: .53em;}
+.liexpand { margin-top: 1em; margin-bottom: 1em }
+.sliexpand { margin-top: 1em; margin-bottom: 1em }
+.dlexpand { margin-top: 1em; margin-bottom: 1em }
+.ddexpand { margin-top: 1em; margin-bottom: 1em }
+.stepexpand { margin-top: 1em; margin-bottom: 1em }
+.substepexpand { margin-top: 1em; margin-bottom: 1em }
+
+/* Default list styles */
+ul li, ol li, dl { margin-top: .53em; margin-bottom: 0.0em; margin-left: 0.0em;}
+ul, ol { margin-bottom: 1em; margin-top: 0em;}
+
+/* Align images based on @align on topic/image */
+div.imageleft { text-align: left; margin-top: 1em; margin-bottom: 1em; }
+div.imagecenter { text-align: center; margin-top: 1em; }
+div.imageright { text-align: right; margin-top: 1em; }
+div.imagejustify { text-align: justify; margin-top: 1em; }
+
+/* The cell border can be turned on with
+ {border-right:solid}
+ This value creates a very thick border in Firefox (does not match other tables)
+
+ Firefox works with
+ {border-right:solid 1pt}
+ but this causes a barely visible line in IE */
+
+.cellrowborder { border-left:none; border-top:none; border-right:solid 1px; border-bottom:solid 1px }
+.row-nocellborder { border-left:none; border-right:none; border-top:none; border-right: hidden; border-bottom:solid 1px}
+.cell-norowborder { border-top:none; border-bottom:none; border-left:none; border-bottom: hidden; border-right:solid 1px}
+.nocellnorowborder { border:none; border-right: hidden;border-bottom: hidden }
+
+
+table {
+ color: black;
+ border-collapse: collapse;
+ border-color: black;
+ background: white;
+ margin-top: 0.5em;
+ margin-bottom: 0.5em;
+ margin-left: 0em;
+ margin-right: 0em;
+ font-size: 1em;
+}
+
+.tablenoborder { margin-top: 1em; margin-bottom: 1em; }
+table caption { text-align: left; padding-bottom: .5em;}
+.tablecap { font-weight: bold; }
+thead, th { background-color: #dddddd; font-weight: bold; }
+table div.p { margin-top: 0; margin-bottom: 0; }
+th { vertical-align: bottom;}
+td p:first-child, th p:first-child { margin-top: 0em; }
+
+pre.screen { padding: 5px 5px 5px 5px; border: outset; background-color: #CCCCCC; margin-top: 2px; margin-bottom : 2px; white-space: pre; overflow: auto; }
+
+.codeblock {
+ font-family: Monaco, "Courier New", Courier, fixed, Monospace;
+ font-size: 1em;
+ background-color: #dddddd;
+ padding: 6px;
+ margin-top: 1em;
+ margin-bottom: 1em;
+ white-space: pre-wrap; /* css-3 */
+ white-space: -moz-pre-wrap; /* Mozilla, since 1999 */
+ white-space: -pre-wrap; /* Opera 4-6 */
+ white-space: -o-pre-wrap; /* Opera 7 */
+ word-wrap: break-word; /* Internet Explorer 5.5+ */
+}
+
+.msgblock {
+ font-family: Monaco, "Courier New", Courier, fixed, Monospace;
+ font-size: 1em;
+ margin-top: 1em;
+ margin-bottom: 1em;
+ white-space: pre-wrap; /* css-3 */
+ white-space: -moz-pre-wrap; /* Mozilla, since 1999 */
+ white-space: -pre-wrap; /* Opera 4-6 */
+ white-space: -o-pre-wrap; /* Opera 7 */
+ word-wrap: break-word; /* Internet Explorer 5.5+ */
+}
+
+
+.codeph, .apiname { font-family: Monaco, "Courier New", Courier, fixed, Monospace; font-size: 1em; } \ No newline at end of file
diff --git a/src/help/studio_help/etc/resources/hdr-confidential.xml b/src/help/studio_help/etc/resources/hdr-confidential.xml
new file mode 100644
index 0000000..b3f1f73
--- /dev/null
+++ b/src/help/studio_help/etc/resources/hdr-confidential.xml
@@ -0,0 +1 @@
+<div class="hdr-confidential">Motorola Confidential Restricted</div> \ No newline at end of file
diff --git a/src/help/studio_help/etc/resources/hdr-none.xml b/src/help/studio_help/etc/resources/hdr-none.xml
new file mode 100644
index 0000000..953ebac
--- /dev/null
+++ b/src/help/studio_help/etc/resources/hdr-none.xml
@@ -0,0 +1 @@
+<a class="hdr-none"/> \ No newline at end of file
diff --git a/src/help/studio_help/src/android_help-studio.ditamap b/src/help/studio_help/src/android_help-studio.ditamap
new file mode 100644
index 0000000..82fd5b3
--- /dev/null
+++ b/src/help/studio_help/src/android_help-studio.ditamap
@@ -0,0 +1,916 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE map PUBLIC "-//OASIS//DTD DITA Map//EN" "http://docs.oasis-open.org/dita/v1.1/OS/dtd/map.dtd">
+<map title="MOTODEV Studio for Android User's Guide" id="com.motorola.studio.android.tooldocs.studio.helpbase">
+ <topicmeta>
+ <copyright>
+ <copyryear year="2009-2012"/>
+ <copyrholder>Motorola Mobility, Inc.</copyrholder>
+ </copyright>
+ </topicmeta>
+ <!-- <topichead navtitle="Concepts" type="concept" format="dita">
+ </topichead> -->
+ <!-- <topichead navtitle="Tasks" format="dita"> -->
+ <topicref href="topics/c_android-studio.dita"/>
+ <topicref href="topics/t_project-creating_android.dita"/>
+ <topicref href="topics/t_project-creating-widget_android.dita"/>
+ <topicref href="topics/t_activity-creating.dita"/>
+ <topicref href="topics/t_bcast-rcvr-creating.dita"/>
+ <topicref href="topics/t_service-creating.dita"/>
+ <topicref href="topics/t_content-provider-creating.dita"/>
+ <topicref href="topics/c_code-generator.dita">
+ <topicref href="topics/t_code-generator.dita"/>
+ </topicref>
+ <topicref href="topics/t_app-launching_android.dita"/>
+ <topicref href="topics/t_app-debugging_android.dita"/>
+ <topicref href="topics/t_app-testing-monkey.dita"/>
+ <topicref href="topics/t_app-memory-analyzing.dita"/>
+ <topicref href="topics/t_device-screen-capturing.dita"/>
+ <topicref href="topics/t_device-creating_android.dita"/>
+ <topicref href="topics/t_device-editing_android.dita"/>
+ <topicref href="topics/t_device-starting_android.dita"/>
+ <topicref href="topics/t_device-state-managing.dita"/>
+ <topicref href="topics/t_emulator-view-opening.dita"/>
+ <topicref href="topics/t_emulator-view-closing.dita"/>
+ <topicref href="topics/t_emulator-view-manipulating.dita">
+ <topicref href="topics/t_emulator-external.dita"/>
+ <topicref href="topics/t_emulator-language-changing.dita"/>
+ </topicref>
+ <topicref href="topics/t_android-perspective-opening.dita"/>
+ <topicref href="topics/t_app-packaging_android.dita"/>
+ <topicref href="topics/t_app-deploying_android.dita"/>
+ <topicref href="topics/t_app-uninstalling-android.dita"/>
+ <topicref href="topics/t_app-cert-signing_android.dita">
+ <topicref href="topics/t_app-signature-removing.dita"/>
+ </topicref>
+ <topicref href="topics/t_app-cert-props-changing.dita">
+ <topicref href="topics/t_cert-key-generating.dita"/>
+ <topicref href="topics/t_cert-key-properties.dita"/>
+ <topicref href="topics/t_app-cert-removing.dita"/>
+ <topicref href="topics/t_cert-key-pwd-changing.dita"/>
+ <topicref href="topics/t_keystore-creating.dita"/>
+ <topicref href="topics/t_keystore-deleting.dita"/>
+ <topicref href="topics/t_keystore-importing.dita"/>
+ <topicref href="topics/t_keystore-entries-importing.dita"/>
+ <topicref href="topics/t_keystore-backup.dita"/>
+ <topicref href="topics/t_keystore-backup-restore.dita"/>
+ <topicref href="topics/t_keystore-type-changing.dita"/>
+ <topicref href="topics/t_keystore-password-changing.dita"/>
+ </topicref>
+ <topicref href="topics/t_obfuscating.dita"/>
+ <topicref href="topics/t_db-about.dita">
+ <topicref href="topics/t_db-connecting.dita"/>
+ <topicref href="topics/t_db-creating.dita"/>
+ <topicref href="topics/t_db-deleting.dita"/>
+ <topicref href="topics/t_db-table-creating.dita"/>
+ <topicref href="topics/t_db-table-deleting.dita"/>
+ <topicref href="topics/t_db-table-editing.dita"/>
+ <topicref href="topics/t_db-classes-create.dita"/>
+ </topicref>
+ <!--<topicref href="topics/t_vdl-starting.dita"/>-->
+ <topicref href="topics/t_log-files-collecting.dita"/>
+ <anchor id="tasks_end"/>
+ <!-- </topichead> -->
+ <topichead navtitle="Reference">
+ <topichead navtitle="Views and Editors">
+ <topicref href="topics/u_emulator_android.dita" type="reference"/>
+ <!-- <topicref href="topics/u_snippets-view_android.dita" type="reference"/> -->
+ <topicref href="topics/u_new-proj_android.dita" type="reference"/>
+ <topicref href="topics/u_new-proj-widget_android.dita"/>
+ <topicref href="topics/u_new-activity.dita" type="reference"/>
+ <topicref href="topics/u_new-activity-template.dita">
+ <topicref href="topics/u_select-table.dita"/>
+ <topicref href="topics/u_select-columns.dita"/>
+ </topicref>
+ <topicref href="topics/u_new-broadcast-receiver.dita" type="reference"/>
+ <topicref href="topics/u_new-service.dita" type="reference"/>
+ <topicref href="topics/u_new-content-provider.dita" type="reference"/>
+ <topicref href="topics/u_new-widget-provider.dita"/>
+ <topichead navtitle="Manage Database">
+ <topicref href="topics/u_db-classes-create.dita"/>
+ <topicref href="topics/u_db-create.dita"/>
+ </topichead>
+ <topichead navtitle="Auto-Generated Code">
+ <topicref href="topics/u_code-generator.dita"/>
+ <topicref href="topics/u_save-ui-state.dita"/>
+ <topicref href="topics/u_options-menu-code-create.dita"/>
+ <topicref href="topics/u_new-activity-template.dita"/>
+ </topichead>
+ <topicref href="topics/u_collect-log-files.dita" type="reference"/>
+ <topicref href="topics/u_device-manager_android.dita" type="reference">
+ <topicref href="topics/u_new-device.dita" type="reference">
+ <topicref href="topics/u_new-device-main_android.dita" type="reference"/>
+ <topicref href="topics/u_new-device-startup_android.dita" type="reference"/>
+ </topicref>
+ <topicref href="topics/u_pkg-install.dita"/>
+ <topicref href="topics/u_device-app-uninstall.dita"/>
+ <topicref href="topics/u_emulator-language.dita"/>
+ <topicref href="topics/u_memory-analyze-app-select.dita"/>
+ </topicref>
+ <topicref href="topics/u_android-handset-properties.dita">
+ <topicref href="topics/u_device-properties_android.dita"/>
+ </topicref>
+ <topicref href="topics/u_avd-properties.dita">
+ <topicref href="topics/u_device-properties_android.dita"/>
+ <topicref href="topics/u_avd-startup-options.dita"/>
+ </topicref>
+ <topicref href="topics/u_screen-calculator.dita"/>
+ <topicref href="topics/u_device-screen-capture.dita"/>
+ <topicref href="topics/u_run-config-main.dita" type="reference">
+ <topicref href="topics/u_select-project.dita"/>
+ <topicref href="topics/u_select-instance.dita"/>
+ <topicref href="topics/u_select-activity.dita"/>
+ </topicref>
+ <topicref href="topics/u_avd-offline-dialog.dita"/>
+ <topicref href="topics/u_run-config-monkey-main.dita"/>
+ <topicref href="topics/u_pkg-export.dita">
+ <topicref href="topics/u_keystore-select.dita"/>
+ </topicref>
+ <topicref href="topics/u_app-signing-view.dita">
+ <topicref href="topics/u_sign-create-keystore.dita"/>
+ <topicref href="topics/u_sign-keystore-type-changing.dita"/>
+ <topicref href="topics/u_sign-import-keystore.dita"/>
+ <topicref href="topics/u_sign-import-keystore-entries.dita"/>
+ <topicref href="topics/u_sign-keystore-backup.dita"/>
+ <topicref href="topics/u_sign-keystore-backup-restore.dita"/>
+ <topicref href="topics/u_sign-create-self-cert.dita"/>
+ <topicref href="topics/u_sign-cert-properties.dita"/>
+ <topicref href="topics/u_mpkg-sign_android.dita" type="reference"/>
+ <topicref href="topics/u_mpkg-unsign_android.dita"/>
+ </topicref>
+ <topicref href="topics/u_obfuscation-prop.dita"/>
+ <topicref href="topics/u_obfuscate-projects.dita"/>
+ <topicref href="topics/u_db-database-perspective.dita"/>
+ <topicref href="topics/u_db-database-explorer.dita"/>
+ <topicref href="topics/u_db-create.dita"/>
+ <topicref href="topics/u_db-table-create.dita"/>
+ <topicref href="topics/u_db-sql-results-view.dita"/>
+ <topicref href="topics/u_db-sql-scrapbook.dita"/>
+ <topicref href="PLUGINS_ROOT/org.eclipse.datatools.sqltools.doc.user/doc/html/asc1229700427574.html"
+ navtitle="SQL Query Builder" locktitle="yes" format="html" scope="peer"/>
+ <topicref href="PLUGINS_ROOT/org.eclipse.datatools.sqltools.doc.user/doc/html/asc1229700412058.html" format="html"
+ scope="peer" navtitle="SQL Results View Options" locktitle="yes"/>
+ <topicref href="topics/u_studio-prefs.dita">
+ <topicref href="topics/u_studio-prefs_emulator.dita"/>
+ <topicref href="topics/u_studio-prefs_db.dita"/>
+ <topicref href="topics/u_studio-prefs_android.dita"/>
+ </topicref>
+ <topicref href="topics/u_video-view.dita"/>
+ <anchor id="reference_end"/>
+ </topichead>
+ </topichead>
+ <topicref href="topics/g_legal.dita"/>
+ <reltable title="Uni-directional links (topics in col 1 show links to topics in col 2)">
+ <relheader>
+ <relcolspec linking="sourceonly"/>
+ <relcolspec/>
+ </relheader>
+ <relrow>
+ <relcell>
+ <topicref href="topics/t_activity-creating.dita"/>
+ </relcell>
+ <relcell>
+ <topicref href="topics/t_bcast-rcvr-creating.dita"/>
+ <topicref href="topics/t_content-provider-creating.dita"/>
+ <topicref href="topics/t_service-creating.dita"/>
+ </relcell>
+ </relrow>
+ <relrow>
+ <relcell>
+ <topicref href="topics/t_bcast-rcvr-creating.dita"/>
+ </relcell>
+ <relcell>
+ <topicref href="topics/t_activity-creating.dita"/>
+ <topicref href="topics/t_content-provider-creating.dita"/>
+ <topicref href="topics/t_service-creating.dita"/>
+ </relcell>
+ </relrow>
+ <relrow>
+ <relcell>
+ <topicref href="topics/t_content-provider-creating.dita"/>
+ </relcell>
+ <relcell>
+ <topicref href="topics/t_activity-creating.dita"/>
+ <topicref href="topics/t_bcast-rcvr-creating.dita"/>
+ <topicref href="topics/t_service-creating.dita"/>
+ <topicref href="http://developer.android.com/resources/articles/live-folders.html" navtitle="Live Folders"
+ locktitle="yes" format="html" scope="external"/>
+ </relcell>
+ </relrow>
+ <relrow>
+ <relcell>
+ <topicref href="topics/t_service-creating.dita"/>
+ </relcell>
+ <relcell>
+ <topicref href="topics/t_activity-creating.dita"/>
+ <topicref href="topics/t_bcast-rcvr-creating.dita"/>
+ <topicref href="topics/t_content-provider-creating.dita"/>
+ </relcell>
+ </relrow>
+ <relrow>
+ <relcell>
+ <topicref href="topics/t_app-launching_android.dita"/>
+ </relcell>
+ <relcell>
+ <topicref href="topics/t_device-creating_android.dita"/>
+ <topicref href="topics/t_device-editing_android.dita"/>
+ <!--<topicref href="topics/t_vdl-starting.dita"/>-->
+ </relcell>
+ </relrow>
+ <relrow>
+ <relcell>
+ <topicref href="topics/t_app-debugging_android.dita"/>
+ </relcell>
+ <relcell>
+ <topicref href="topics/t_device-creating_android.dita"/>
+ <topicref href="topics/t_device-editing_android.dita"/>
+ <!--<topicref href="topics/t_vdl-starting.dita"/>-->
+ <topicref href="PLUGINS_ROOT/org.eclipse.jdt.doc.user/concepts/clocdbug.htm"
+ navtitle="Local Debugging (Java development user guide)" locktitle="yes" format="html" scope="peer"/>
+ </relcell>
+ </relrow>
+ <relrow>
+ <relcell>
+ <topicref href="topics/t_device-creating_android.dita"/>
+ </relcell>
+ <relcell>
+ <topicref href="topics/t_app-launching_android.dita"/>
+ <topicref href="http://d.android.com/guide/developing/tools/emulator.html#startup-options" format="html"
+ navtitle="Emulator Startup Options" scope="external" locktitle="yes"/>
+ </relcell>
+ </relrow>
+ <relrow>
+ <relcell>
+ <topicref href="topics/t_device-editing_android.dita"/>
+ </relcell>
+ <relcell>
+ <topicref href="topics/t_app-launching_android.dita"/>
+ <topicref href="http://d.android.com/guide/developing/tools/emulator.html#startup-options" format="html"
+ navtitle="Emulator Startup Options" scope="external" locktitle="yes"/>
+ </relcell>
+ </relrow>
+ <relrow>
+ <relcell>
+ <topicref href="topics/t_emulator-view-opening.dita"/>
+ </relcell>
+ <relcell>
+ <topicref href="topics/t_app-launching_android.dita"/>
+ </relcell>
+ </relrow>
+ <relrow>
+ <relcell>
+ <topicref href="topics/t_app-packaging_android.dita"/>
+ </relcell>
+ <relcell>
+ <topicref href="topics/t_app-cert-props-changing.dita"/>
+ </relcell>
+ </relrow>
+ <relrow>
+ <relcell>
+ <topicref href="topics/t_app-deploying_android.dita"/>
+ </relcell>
+ <relcell>
+ <topicref href="topics/t_app-cert-signing_android.dita"/>
+ </relcell>
+ </relrow>
+ <relrow>
+ <relcell>
+ <topicref href="topics/t_cert-key-generating.dita"/>
+ </relcell>
+ <relcell>
+ <topicref href="topics/t_app-cert-signing_android.dita"/>
+ </relcell>
+ </relrow>
+ <relrow>
+ <relcell>
+ <topicref href="topics/t_device-state-managing.dita"/>
+ </relcell>
+ <relcell>
+ <topicref href="http://developer.android.com/guide/developing/tools/adb.html#shellcommands"
+ navtitle="Issuing Shell Commands (Android developers documentation)" locktitle="yes" format="html"
+ scope="external"/>
+ </relcell>
+ </relrow>
+ <relrow>
+ <relcell>
+ <topicref href="topics/t_emulator-controlling.dita"/>
+ </relcell>
+ <relcell>
+ <topicref href="http://developer.android.com/guide/developing/tools/emulator.html#console"
+ navtitle="Using the Emulator Console (Android developers documentation)" locktitle="yes" format="html"
+ scope="external"/>
+ </relcell>
+ </relrow>
+ <relrow>
+ <relcell>
+ <topicref href="topics/t_db-table-creating.dita"/>
+ <topicref href="topics/t_db-table-deleting.dita"/>
+ <topicref href="topics/t_db-table-editing.dita"/>
+ </relcell>
+ <relcell>
+ <topicref href="topics/t_db-connecting.dita"/>
+ </relcell>
+ </relrow>
+ <relrow>
+ <relcell>
+ <topicref href="topics/u_db-database-explorer.dita"/>
+ </relcell>
+ <relcell>
+ <topicref href="topics/u_db-sql-results-view.dita"/>
+ <topicref href="topics/u_studio-prefs_db.dita"/>
+ <topicref href="topics/u_db-classes-create.dita"/>
+ <topicref href="topics/u_db-table-create.dita"/>
+ </relcell>
+ </relrow>
+ <relrow>
+ <relcell>
+ <topicref href="topics/u_run-config-main.dita"/>
+ </relcell>
+ <relcell>
+ <topicref href="topics/t_device-creating_android.dita"/>
+ </relcell>
+ </relrow>
+ <relrow>
+ <relcell>
+ <topicref href="topics/t_emulator-external.dita"/>
+ </relcell>
+ <relcell>
+ <topicref href="topics/t_device-creating_android.dita"/>
+ <topicref href="topics/t_device-editing_android.dita"/>
+ <topicref href="topics/t_emulator-view-opening.dita"/>
+ <topicref href="topics/u_new-device-startup_android.dita"/>
+ </relcell>
+ </relrow>
+ <relrow>
+ <relcell>
+ <topicref href="topics/t_app-testing-monkey.dita"/>
+ <topicref href="topics/u_run-config-monkey-main.dita"/>
+ </relcell>
+ <relcell>
+ <topicref href="http://developer.android.com/guide/developing/tools/monkey.html"
+ navtitle="UI/Application Exerciser Monkey documentation" locktitle="yes" format="html" scope="external"/>
+ </relcell>
+ </relrow>
+ <relrow>
+ <relcell>
+ <topicref href="topics/u_run-config-monkey-main.dita"/>
+ </relcell>
+ <relcell>
+ <topicref href="topics/u_device-manager_android.dita"/>
+ </relcell>
+ </relrow>
+ <relrow>
+ <relcell>
+ <topicref href="topics/t_app-memory-analyzing.dita"/>
+ <topicref href="topics/u_memory-analyze-app-select.dita"/>
+ </relcell>
+ <relcell>
+ <topicref href="http://www.eclipse.org/mat/" navtitle="Memory Analyzer (MAT) documentation" locktitle="yes"
+ format="html" scope="external"/>
+ </relcell>
+ </relrow>
+ <relrow>
+ <relcell>
+ <topicref href="topics/t_emulator-language-changing.dita"/>
+ <topicref href="topics/u_emulator-language.dita"/>
+ </relcell>
+ <relcell>
+ <topicref href="http://developer.android.com/guide/topics/resources/localization.html#emulator"
+ navtitle="Testing on an Emulator" locktitle="yes" format="html" scope="external"/>
+ </relcell>
+ </relrow>
+ <relrow>
+ <relcell>
+ <topicref href="topics/t_project-creating_android.dita"/>
+ </relcell>
+ <relcell>
+ <topicref href="topics/t_config-for-native.dita"/>
+ </relcell>
+ </relrow>
+ <relrow>
+ <relcell>
+ <topicref href="topics/t_obfuscating.dita"/>
+ </relcell>
+ <relcell>
+ <topicref href="http://developer.android.com/guide/developing/tools/proguard.html" format="html"
+ scope="external" locktitle="yes" navtitle="ProGuard (in Android developer documentation)"/>
+ </relcell>
+ </relrow>
+ <relrow>
+ <relcell>
+ <topicref href="topics/u_code-generator.dita"/>
+ </relcell>
+ <relcell>
+ <topicref href="topics/c_code-generator.dita"/>
+ </relcell>
+ </relrow>
+ </reltable>
+ <reltable title="Bi-directional links (topics in col 1 and 2 show links to each other)">
+ <relheader>
+ <relcolspec/>
+ <relcolspec/>
+ </relheader>
+ <relrow>
+ <relcell>
+ <topicref href="topics/t_activity-creating.dita"/>
+ </relcell>
+ <relcell>
+ <topicref href="topics/u_new-activity.dita"/>
+ </relcell>
+ </relrow>
+ <relrow>
+ <relcell>
+ <topicref href="topics/t_activity-creating.dita"/>
+ </relcell>
+ <relcell>
+ <topicref href="topics/u_new-activity-template.dita"/>
+ </relcell>
+ </relrow>
+ <relrow>
+ <relcell>
+ <topicref href="topics/t_bcast-rcvr-creating.dita"/>
+ </relcell>
+ <relcell>
+ <topicref href="topics/u_new-broadcast-receiver.dita"/>
+ </relcell>
+ </relrow>
+ <relrow>
+ <relcell>
+ <topicref href="topics/t_content-provider-creating.dita"/>
+ </relcell>
+ <relcell>
+ <topicref href="topics/u_new-content-provider.dita"/>
+ </relcell>
+ </relrow>
+ <relrow>
+ <relcell>
+ <topicref href="topics/t_service-creating.dita"/>
+ </relcell>
+ <relcell>
+ <topicref href="topics/u_new-service.dita"/>
+ </relcell>
+ </relrow>
+ <relrow>
+ <relcell>
+ <topicref href="topics/t_app-launching_android.dita"/>
+ </relcell>
+ <relcell>
+ <topicref href="topics/u_run-config-main.dita"/>
+ </relcell>
+ </relrow>
+ <relrow>
+ <relcell>
+ <topicref href="topics/t_app-debugging_android.dita"/>
+ </relcell>
+ <relcell>
+ <topicref href="topics/u_run-config-main.dita"/>
+ </relcell>
+ </relrow>
+ <relrow>
+ <relcell>
+ <topicref href="topics/t_device-creating_android.dita"/>
+ </relcell>
+ <relcell>
+ <topicref href="topics/t_device-editing_android.dita"/>
+ </relcell>
+ </relrow>
+ <relrow>
+ <relcell>
+ <topicref href="topics/t_device-creating_android.dita"/>
+ </relcell>
+ <relcell>
+ <topicref href="topics/u_new-device.dita"/>
+ <topicref href="topics/u_new-device-main_android.dita"/>
+ <topicref href="topics/u_new-device-startup_android.dita"/>
+ <topicref href="topics/t_device-starting_android.dita"/>
+ </relcell>
+ </relrow>
+ <relrow>
+ <relcell>
+ <topicref href="topics/t_device-editing_android.dita"/>
+ </relcell>
+ <relcell>
+ <topicref href="topics/u_new-device.dita"/>
+ <topicref href="topics/u_new-device-main_android.dita"/>
+ <topicref href="topics/u_new-device-startup_android.dita"/>
+ </relcell>
+ </relrow>
+ <relrow>
+ <relcell>
+ <topicref href="topics/u_device-manager_android.dita"/>
+ </relcell>
+ <relcell>
+ <topicref href="topics/t_device-starting_android.dita"/>
+ <topicref href="topics/t_device-state-managing.dita"/>
+ <topicref href="topics/t_emulator-controlling.dita"/>
+ <topicref href="topics/t_app-deploying_android.dita"/>
+ <topicref href="topics/t_app-uninstalling-android.dita"/>
+ <topicref href="topics/t_app-testing-monkey.dita"/>
+ <topicref href="topics/t_app-memory-analyzing.dita"/>
+ <topicref href="topics/t_device-screen-capturing.dita"/>
+ <topicref href="topics/t_emulator-language-changing.dita"/>
+ </relcell>
+ </relrow>
+ <relrow>
+ <relcell>
+ <topicref href="topics/t_emulator-view-opening.dita"/>
+ </relcell>
+ <relcell>
+ <topicref href="topics/t_emulator-view-closing.dita"/>
+ <topicref href="topics/t_emulator-view-manipulating.dita"/>
+ </relcell>
+ </relrow>
+ <relrow>
+ <relcell>
+ <topicref href="topics/u_emulator_android.dita"/>
+ </relcell>
+ <relcell>
+ <topicref href="topics/t_emulator-view-opening.dita"/>
+ <topicref href="topics/t_emulator-view-closing.dita"/>
+ <topicref href="topics/t_emulator-view-manipulating.dita"/>
+ </relcell>
+ </relrow>
+ <relrow>
+ <relcell>
+ <topicref href="topics/u_emulator_android.dita"/>
+ </relcell>
+ <relcell>
+ <topicref href="topics/t_emulator-view-closing.dita"/>
+ <topicref href="topics/t_emulator-view-manipulating.dita"/>
+ </relcell>
+ </relrow>
+ <relrow>
+ <relcell>
+ <topicref href="topics/t_app-packaging_android.dita"/>
+ </relcell>
+ <relcell>
+ <topicref href="topics/t_app-cert-signing_android.dita"/>
+ <topicref href="topics/t_app-deploying_android.dita"/>
+ <topicref href="topics/u_pkg-export.dita"/>
+ </relcell>
+ </relrow>
+ <relrow>
+ <relcell>
+ <topicref href="topics/t_app-cert-signing_android.dita"/>
+ </relcell>
+ <relcell>
+ <topicref href="topics/t_app-cert-props-changing.dita"/>
+ <topicref href="topics/u_app-signing-view.dita"/>
+ <topicref href="topics/u_mpkg-sign_android.dita"/>
+ </relcell>
+ </relrow>
+ <relrow>
+ <relcell>
+ <topicref href="topics/t_app-signature-removing.dita"/>
+ </relcell>
+ <relcell>
+ <topicref href="topics/u_app-signing-view.dita"/>
+ <topicref href="topics/u_mpkg-unsign_android.dita"/>
+ <topicref href="topics/t_app-cert-props-changing.dita"/>
+ </relcell>
+ </relrow>
+ <relrow>
+ <relcell>
+ <topicref href="topics/t_app-deploying_android.dita"/>
+ </relcell>
+ <relcell>
+ <topicref href="topics/t_app-launching_android.dita"/>
+ <topicref href="topics/t_app-packaging_android.dita"/>
+ <topicref href="topics/u_device-manager_android.dita"/>
+ <topicref href="topics/u_pkg-install.dita"/>
+ </relcell>
+ </relrow>
+ <relrow>
+ <relcell>
+ <topicref href="topics/t_app-cert-props-changing.dita"/>
+ </relcell>
+ <relcell>
+ <topicref href="topics/u_app-signing-view.dita"/>
+ </relcell>
+ </relrow>
+ <relrow>
+ <relcell>
+ <topicref href="topics/t_cert-key-generating.dita"/>
+ </relcell>
+ <relcell>
+ <topicref href="topics/u_sign-create-self-cert.dita"/>
+ <topicref href="topics/t_app-cert-removing.dita"/>
+ <topicref href="topics/u_sign-cert-properties.dita"/>
+ </relcell>
+ </relrow>
+ <relrow>
+ <relcell>
+ <topicref href="topics/t_emulator-view-manipulating.dita"/>
+ </relcell>
+ <relcell>
+ <topicref href="topics/t_emulator-controlling.dita"/>
+ </relcell>
+ </relrow>
+ <relrow>
+ <relcell>
+ <topicref href="topics/t_log-files-collecting.dita"/>
+ </relcell>
+ <relcell>
+ <topicref href="topics/u_collect-log-files.dita"/>
+ </relcell>
+ </relrow>
+ <relrow>
+ <relcell>
+ <topicref href="topics/u_db-sql-scrapbook.dita"/>
+ </relcell>
+ <relcell>
+ <topicref href="topics/u_db-sql-results-view.dita"/>
+ <topicref href="PLUGINS_ROOT/org.eclipse.datatools.sqltools.doc.user/doc/html/asc1229700427574.html"
+ navtitle="SQL Query Builder" locktitle="yes" format="html" scope="peer"/>
+ </relcell>
+ </relrow>
+ <relrow>
+ <relcell>
+ <topicref href="topics/u_db-sql-results-view.dita"/>
+ </relcell>
+ <relcell>
+ <topicref href="PLUGINS_ROOT/org.eclipse.datatools.sqltools.doc.user/doc/html/asc1229700412058.html"
+ format="html" scope="peer" navtitle="SQL Results View Options" locktitle="yes"/>
+ </relcell>
+ </relrow>
+ <relrow>
+ <relcell>
+ <topicref href="topics/t_db-creating.dita"/>
+ </relcell>
+ <relcell>
+ <topicref href="topics/t_db-table-creating.dita"/>
+ </relcell>
+ </relrow>
+ <relrow>
+ <relcell>
+ <topicref href="topics/t_app-deploying_android.dita"/>
+ </relcell>
+ <relcell>
+ <topicref href="topics/t_app-uninstalling-android.dita"/>
+ </relcell>
+ </relrow>
+ <relrow>
+ <relcell>
+ <topicref href="topics/t_device-screen-capturing.dita"/>
+ </relcell>
+ <relcell>
+ <topicref href="topics/u_device-screen-capture.dita"/>
+ </relcell>
+ </relrow>
+ <relrow>
+ <relcell>
+ <topicref href="topics/u_new-device-startup_android.dita"/>
+ </relcell>
+ <relcell>
+ <topicref href="topics/u_screen-calculator.dita"/>
+ <topicref href="topics/u_avd-startup-options.dita"/>
+ </relcell>
+ </relrow>
+ <relrow>
+ <relcell>
+ <topicref href="topics/u_avd-startup-options.dita"/>
+ </relcell>
+ <relcell>
+ <topicref href="topics/u_screen-calculator.dita"/>
+ </relcell>
+ </relrow>
+ <relrow>
+ <relcell>
+ <topicref href="topics/u_android-handset-properties.dita"/>
+ </relcell>
+ <relcell>
+ <topicref href="topics/u_avd-properties.dita"/>
+ </relcell>
+ </relrow>
+ <relrow>
+ <relcell>
+ <topicref href="topics/t_app-testing-monkey.dita"/>
+ </relcell>
+ <relcell>
+ <topicref href="topics/t_app-debugging_android.dita"/>
+ </relcell>
+ </relrow>
+ <relrow>
+ <relcell>
+ <topicref href="topics/u_run-config-monkey-main.dita"/>
+ </relcell>
+ <relcell>
+ <topicref href="topics/t_app-testing-monkey.dita"/>
+ </relcell>
+ </relrow>
+ <relrow>
+ <relcell>
+ <topicref href="topics/u_db-classes-create.dita"/>
+ </relcell>
+ <relcell>
+ <topicref href="topics/t_db-classes-create.dita"/>
+ </relcell>
+ </relrow>
+ <relrow>
+ <relcell>
+ <topicref href="topics/t_db-classes-create.dita"/>
+ </relcell>
+ <relcell>
+ <topicref href="topics/t_content-provider-creating.dita"/>
+ </relcell>
+ </relrow>
+ <relrow>
+ <relcell>
+ <topicref href="topics/u_emulator-language.dita"/>
+ </relcell>
+ <relcell>
+ <topicref href="topics/t_emulator-language-changing.dita"/>
+ </relcell>
+ </relrow>
+ <relrow>
+ <relcell>
+ <topicref href="topics/u_device-app-uninstall.dita"/>
+ </relcell>
+ <relcell>
+ <topicref href="topics/t_app-uninstalling-android.dita"/>
+ </relcell>
+ </relrow>
+ <relrow>
+ <relcell>
+ <topicref href="topics/u_memory-analyze-app-select.dita"/>
+ </relcell>
+ <relcell>
+ <topicref href="topics/t_app-memory-analyzing.dita"/>
+ </relcell>
+ </relrow>
+ <relrow>
+ <relcell>
+ <topicref href="topics/t_project-creating_android.dita"/>
+ </relcell>
+ <relcell>
+ <topicref href="topics/t_project-creating-widget_android.dita"/>
+ </relcell>
+ </relrow>
+ <relrow>
+ <relcell>
+ <topicref href="topics/t_obfuscating.dita"/>
+ </relcell>
+ <relcell>
+ <topicref href="topics/t_project-creating_android.dita"/>
+ <topicref href="topics/u_obfuscate-projects.dita"/>
+ <topicref href="topics/u_obfuscation-prop.dita"/>
+ <topicref href="topics/t_app-packaging_android.dita"/>
+ </relcell>
+ </relrow>
+ <relrow>
+ <relcell>
+ <topicref href="topics/u_obfuscate-projects.dita"/>
+ </relcell>
+ <relcell>
+ <topicref href="topics/u_obfuscation-prop.dita"/>
+ </relcell>
+ </relrow>
+ <relrow>
+ <relcell>
+ <topicref href="topics/u_new-proj_android.dita"/>
+ </relcell>
+ <relcell>
+ <topicref href="topics/t_project-creating_android.dita"/>
+ </relcell>
+ </relrow>
+ <!--<relrow>
+ <relcell>
+ <topicref href="topics/t_vdl-starting.dita"/>
+ </relcell>
+ <relcell>
+ <topicref href="topics/u_studio-prefs_da.dita"/>
+ </relcell>
+ </relrow>-->
+ <relrow>
+ <relcell>
+ <topicref href="topics/u_db-create.dita"/>
+ </relcell>
+ <relcell>
+ <topicref href="topics/u_db-table-create.dita"/>
+ </relcell>
+ </relrow>
+ <relrow>
+ <relcell>
+ <topicref href="topics/u_db-create.dita"/>
+ </relcell>
+ <relcell>
+ <topicref href="topics/t_db-creating.dita"/>
+ </relcell>
+ </relrow>
+ <relrow>
+ <relcell>
+ <topicref href="topics/u_db-table-create.dita"/>
+ </relcell>
+ <relcell>
+ <topicref href="topics/t_db-table-creating.dita"/>
+ </relcell>
+ </relrow>
+ <relrow>
+ <relcell>
+ <topicref href="topics/u_new-activity.dita"/>
+ </relcell>
+ <relcell>
+ <topicref href="topics/u_new-activity-template.dita"/>
+ </relcell>
+ </relrow>
+ <relrow>
+ <relcell>
+ <topicref href="topics/t_code-generator.dita"/>
+ </relcell>
+ <relcell>
+ <topicref href="topics/u_code-generator.dita"/>
+ </relcell>
+ </relrow>
+ <relrow>
+ <relcell>
+ <topicref href="topics/t_cert-key-properties.dita"/>
+ </relcell>
+ <relcell>
+ <topicref href="topics/u_sign-cert-properties.dita"/>
+ <topicref href="topics/t_cert-key-generating.dita"/>
+ </relcell>
+ </relrow>
+ <relrow>
+ <relcell>
+ <topicref href="topics/t_cert-key-pwd-changing.dita"/>
+ </relcell>
+ <relcell>
+ <topicref href="topics/t_cert-key-generating.dita"/>
+ </relcell>
+ </relrow>
+ <relrow>
+ <relcell>
+ <topicref href="topics/t_keystore-creating.dita"/>
+ </relcell>
+ <relcell>
+ <topicref href="topics/t_keystore-deleting.dita"/>
+ <topicref href="topics/t_keystore-backup-restore.dita"/>
+ <topicref href="topics/t_keystore-importing.dita"/>
+ <topicref href="topics/t_keystore-entries-importing.dita"/>
+ <topicref href="topics/u_sign-create-keystore.dita"/>
+ </relcell>
+ </relrow>
+ <relrow>
+ <relcell>
+ <topicref href="topics/t_keystore-backup.dita"/>
+ </relcell>
+ <relcell>
+ <topicref href="topics/t_keystore-backup-restore.dita"/>
+ <topicref href="topics/u_sign-keystore-backup.dita"/>
+ </relcell>
+ </relrow>
+ <relrow>
+ <relcell>
+ <topicref href="topics/t_keystore-backup-restore.dita"/>
+ </relcell>
+ <relcell>
+ <topicref href="topics/u_sign-keystore-backup-restore.dita"/>
+ </relcell>
+ </relrow>
+ <relrow>
+ <relcell>
+ <topicref href="topics/t_keystore-deleting.dita"/>
+ </relcell>
+ <relcell>
+ <topicref href="topics/t_app-cert-removing.dita"/>
+ <topicref href="topics/t_keystore-backup.dita"/>
+ <topicref href="topics/t_keystore-creating.dita"/>
+ </relcell>
+ </relrow>
+ <relrow>
+ <relcell>
+ <topicref href="topics/t_keystore-importing.dita"/>
+ </relcell>
+ <relcell>
+ <topicref href="topics/t_keystore-entries-importing.dita"/>
+ <topicref href="topics/u_sign-import-keystore.dita"/>
+ </relcell>
+ </relrow>
+ <relrow>
+ <relcell>
+ <topicref href="topics/t_keystore-entries-importing.dita"/>
+ </relcell>
+ <relcell>
+ <topicref href="topics/t_cert-key-generating.dita"/>
+ <topicref href="topics/t_app-cert-removing.dita"/>
+ <topicref href="topics/u_sign-import-keystore-entries.dita"/>
+ </relcell>
+ </relrow>
+ <relrow>
+ <relcell>
+ <topicref href="topics/t_keystore-type-changing.dita"/>
+ </relcell>
+ <relcell>
+ <topicref href="topics/u_sign-keystore-type-changing.dita"/>
+ </relcell>
+ </relrow>
+ <relrow>
+ <relcell>
+ <topicref href="topics/c_keys-keystores.dita"/>
+ </relcell>
+ <relcell>
+ <topicref href="topics/u_app-signing-view.dita"/>
+ <topicref href="topics/t_app-cert-props-changing.dita"/>
+ </relcell>
+ </relrow>
+ </reltable>
+</map>
diff --git a/src/help/studio_help/src/images/KeyCreate.png b/src/help/studio_help/src/images/KeyCreate.png
new file mode 100644
index 0000000..c8131b1
--- /dev/null
+++ b/src/help/studio_help/src/images/KeyCreate.png
Binary files differ
diff --git a/src/help/studio_help/src/images/KeyDelete.png b/src/help/studio_help/src/images/KeyDelete.png
new file mode 100644
index 0000000..85e7de0
--- /dev/null
+++ b/src/help/studio_help/src/images/KeyDelete.png
Binary files differ
diff --git a/src/help/studio_help/src/images/KeyPasswordChange.png b/src/help/studio_help/src/images/KeyPasswordChange.png
new file mode 100644
index 0000000..dc1396b
--- /dev/null
+++ b/src/help/studio_help/src/images/KeyPasswordChange.png
Binary files differ
diff --git a/src/help/studio_help/src/images/KeyProperties.png b/src/help/studio_help/src/images/KeyProperties.png
new file mode 100644
index 0000000..07c9c1c
--- /dev/null
+++ b/src/help/studio_help/src/images/KeyProperties.png
Binary files differ
diff --git a/src/help/studio_help/src/images/KeystoreCreate.png b/src/help/studio_help/src/images/KeystoreCreate.png
new file mode 100644
index 0000000..c2ffade
--- /dev/null
+++ b/src/help/studio_help/src/images/KeystoreCreate.png
Binary files differ
diff --git a/src/help/studio_help/src/images/KeystoreDelete.png b/src/help/studio_help/src/images/KeystoreDelete.png
new file mode 100644
index 0000000..7bdab30
--- /dev/null
+++ b/src/help/studio_help/src/images/KeystoreDelete.png
Binary files differ
diff --git a/src/help/studio_help/src/images/KeystoreEntriesImport.png b/src/help/studio_help/src/images/KeystoreEntriesImport.png
new file mode 100644
index 0000000..ebb6a11
--- /dev/null
+++ b/src/help/studio_help/src/images/KeystoreEntriesImport.png
Binary files differ
diff --git a/src/help/studio_help/src/images/KeystoreImport.png b/src/help/studio_help/src/images/KeystoreImport.png
new file mode 100644
index 0000000..e425ae9
--- /dev/null
+++ b/src/help/studio_help/src/images/KeystoreImport.png
Binary files differ
diff --git a/src/help/studio_help/src/images/KeystorePasswordChange.png b/src/help/studio_help/src/images/KeystorePasswordChange.png
new file mode 100644
index 0000000..8f55c6d
--- /dev/null
+++ b/src/help/studio_help/src/images/KeystorePasswordChange.png
Binary files differ
diff --git a/src/help/studio_help/src/images/KeystoreRefresh.png b/src/help/studio_help/src/images/KeystoreRefresh.png
new file mode 100644
index 0000000..fa0204e
--- /dev/null
+++ b/src/help/studio_help/src/images/KeystoreRefresh.png
Binary files differ
diff --git a/src/help/studio_help/src/images/KeystoreTypeChange.png b/src/help/studio_help/src/images/KeystoreTypeChange.png
new file mode 100644
index 0000000..f82f15b
--- /dev/null
+++ b/src/help/studio_help/src/images/KeystoreTypeChange.png
Binary files differ
diff --git a/src/help/studio_help/src/images/KeystoresBackup.png b/src/help/studio_help/src/images/KeystoresBackup.png
new file mode 100644
index 0000000..6621a9f
--- /dev/null
+++ b/src/help/studio_help/src/images/KeystoresBackup.png
Binary files differ
diff --git a/src/help/studio_help/src/images/KeystoresRestore.png b/src/help/studio_help/src/images/KeystoresRestore.png
new file mode 100644
index 0000000..2feac28
--- /dev/null
+++ b/src/help/studio_help/src/images/KeystoresRestore.png
Binary files differ
diff --git a/src/help/studio_help/src/images/PackageSign.png b/src/help/studio_help/src/images/PackageSign.png
new file mode 100644
index 0000000..84efb17
--- /dev/null
+++ b/src/help/studio_help/src/images/PackageSign.png
Binary files differ
diff --git a/src/help/studio_help/src/images/PackageUnsign.png b/src/help/studio_help/src/images/PackageUnsign.png
new file mode 100644
index 0000000..759118b
--- /dev/null
+++ b/src/help/studio_help/src/images/PackageUnsign.png
Binary files differ
diff --git a/src/help/studio_help/src/images/browse-table-contents.png b/src/help/studio_help/src/images/browse-table-contents.png
new file mode 100644
index 0000000..2fcc207
--- /dev/null
+++ b/src/help/studio_help/src/images/browse-table-contents.png
Binary files differ
diff --git a/src/help/studio_help/src/images/collapse-all.png b/src/help/studio_help/src/images/collapse-all.png
new file mode 100644
index 0000000..30c1c2f
--- /dev/null
+++ b/src/help/studio_help/src/images/collapse-all.png
Binary files differ
diff --git a/src/help/studio_help/src/images/db-connected-states.png b/src/help/studio_help/src/images/db-connected-states.png
new file mode 100644
index 0000000..077ec2a
--- /dev/null
+++ b/src/help/studio_help/src/images/db-connected-states.png
Binary files differ
diff --git a/src/help/studio_help/src/images/db-create-classes.png b/src/help/studio_help/src/images/db-create-classes.png
new file mode 100644
index 0000000..8d27ffd
--- /dev/null
+++ b/src/help/studio_help/src/images/db-create-classes.png
Binary files differ
diff --git a/src/help/studio_help/src/images/db-create-table.png b/src/help/studio_help/src/images/db-create-table.png
new file mode 100644
index 0000000..af8aea3
--- /dev/null
+++ b/src/help/studio_help/src/images/db-create-table.png
Binary files differ
diff --git a/src/help/studio_help/src/images/db-icon.png b/src/help/studio_help/src/images/db-icon.png
new file mode 100644
index 0000000..1c7db35
--- /dev/null
+++ b/src/help/studio_help/src/images/db-icon.png
Binary files differ
diff --git a/src/help/studio_help/src/images/db-tables.png b/src/help/studio_help/src/images/db-tables.png
new file mode 100644
index 0000000..f523d54
--- /dev/null
+++ b/src/help/studio_help/src/images/db-tables.png
Binary files differ
diff --git a/src/help/studio_help/src/images/dev-mgr_adb-shell-icon.png b/src/help/studio_help/src/images/dev-mgr_adb-shell-icon.png
new file mode 100644
index 0000000..75d3f2c
--- /dev/null
+++ b/src/help/studio_help/src/images/dev-mgr_adb-shell-icon.png
Binary files differ
diff --git a/src/help/studio_help/src/images/dev-mgr_console-icon.png b/src/help/studio_help/src/images/dev-mgr_console-icon.png
new file mode 100644
index 0000000..f3c790e
--- /dev/null
+++ b/src/help/studio_help/src/images/dev-mgr_console-icon.png
Binary files differ
diff --git a/src/help/studio_help/src/images/dev-mgr_install-app-icon.png b/src/help/studio_help/src/images/dev-mgr_install-app-icon.png
new file mode 100644
index 0000000..363a6bd
--- /dev/null
+++ b/src/help/studio_help/src/images/dev-mgr_install-app-icon.png
Binary files differ
diff --git a/src/help/studio_help/src/images/dev-mgr_language-icon.png b/src/help/studio_help/src/images/dev-mgr_language-icon.png
new file mode 100644
index 0000000..5d03c1e
--- /dev/null
+++ b/src/help/studio_help/src/images/dev-mgr_language-icon.png
Binary files differ
diff --git a/src/help/studio_help/src/images/dev-mgr_mat-analyze-icon.png b/src/help/studio_help/src/images/dev-mgr_mat-analyze-icon.png
new file mode 100644
index 0000000..429aafd
--- /dev/null
+++ b/src/help/studio_help/src/images/dev-mgr_mat-analyze-icon.png
Binary files differ
diff --git a/src/help/studio_help/src/images/dev-mgr_monkey-test-icon.png b/src/help/studio_help/src/images/dev-mgr_monkey-test-icon.png
new file mode 100644
index 0000000..b74276f
--- /dev/null
+++ b/src/help/studio_help/src/images/dev-mgr_monkey-test-icon.png
Binary files differ
diff --git a/src/help/studio_help/src/images/dev-mgr_new-icon.png b/src/help/studio_help/src/images/dev-mgr_new-icon.png
new file mode 100644
index 0000000..f90fa58
--- /dev/null
+++ b/src/help/studio_help/src/images/dev-mgr_new-icon.png
Binary files differ
diff --git a/src/help/studio_help/src/images/dev-mgr_repair-icon.png b/src/help/studio_help/src/images/dev-mgr_repair-icon.png
new file mode 100644
index 0000000..77856a7
--- /dev/null
+++ b/src/help/studio_help/src/images/dev-mgr_repair-icon.png
Binary files differ
diff --git a/src/help/studio_help/src/images/dev-mgr_reset-icon.png b/src/help/studio_help/src/images/dev-mgr_reset-icon.png
new file mode 100644
index 0000000..c989439
--- /dev/null
+++ b/src/help/studio_help/src/images/dev-mgr_reset-icon.png
Binary files differ
diff --git a/src/help/studio_help/src/images/dev-mgr_start-icon.png b/src/help/studio_help/src/images/dev-mgr_start-icon.png
new file mode 100644
index 0000000..5c18480
--- /dev/null
+++ b/src/help/studio_help/src/images/dev-mgr_start-icon.png
Binary files differ
diff --git a/src/help/studio_help/src/images/dev-mgr_stop-icon.png b/src/help/studio_help/src/images/dev-mgr_stop-icon.png
new file mode 100644
index 0000000..0fc73dd
--- /dev/null
+++ b/src/help/studio_help/src/images/dev-mgr_stop-icon.png
Binary files differ
diff --git a/src/help/studio_help/src/images/dev-mgr_take-screenshot-icon.png b/src/help/studio_help/src/images/dev-mgr_take-screenshot-icon.png
new file mode 100644
index 0000000..b879dba
--- /dev/null
+++ b/src/help/studio_help/src/images/dev-mgr_take-screenshot-icon.png
Binary files differ
diff --git a/src/help/studio_help/src/images/dev-mgr_uninstall-app-icon.png b/src/help/studio_help/src/images/dev-mgr_uninstall-app-icon.png
new file mode 100644
index 0000000..0d09fbc
--- /dev/null
+++ b/src/help/studio_help/src/images/dev-mgr_uninstall-app-icon.png
Binary files differ
diff --git a/src/help/studio_help/src/images/display-selected-console-button.png b/src/help/studio_help/src/images/display-selected-console-button.png
new file mode 100644
index 0000000..8616ccc
--- /dev/null
+++ b/src/help/studio_help/src/images/display-selected-console-button.png
Binary files differ
diff --git a/src/help/studio_help/src/images/display-single-tab-icon.png b/src/help/studio_help/src/images/display-single-tab-icon.png
new file mode 100644
index 0000000..eac6f44
--- /dev/null
+++ b/src/help/studio_help/src/images/display-single-tab-icon.png
Binary files differ
diff --git a/src/help/studio_help/src/images/display-text-mode-icon.png b/src/help/studio_help/src/images/display-text-mode-icon.png
new file mode 100644
index 0000000..f0ad54d
--- /dev/null
+++ b/src/help/studio_help/src/images/display-text-mode-icon.png
Binary files differ
diff --git a/src/help/studio_help/src/images/emulator-switch-layout.png b/src/help/studio_help/src/images/emulator-switch-layout.png
new file mode 100644
index 0000000..e3d71b6
--- /dev/null
+++ b/src/help/studio_help/src/images/emulator-switch-layout.png
Binary files differ
diff --git a/src/help/studio_help/src/images/emulator-zoom-in.png b/src/help/studio_help/src/images/emulator-zoom-in.png
new file mode 100644
index 0000000..71b3422
--- /dev/null
+++ b/src/help/studio_help/src/images/emulator-zoom-in.png
Binary files differ
diff --git a/src/help/studio_help/src/images/emulator-zoom-out.png b/src/help/studio_help/src/images/emulator-zoom-out.png
new file mode 100644
index 0000000..3fc573a
--- /dev/null
+++ b/src/help/studio_help/src/images/emulator-zoom-out.png
Binary files differ
diff --git a/src/help/studio_help/src/images/filter-results-icon.png b/src/help/studio_help/src/images/filter-results-icon.png
new file mode 100644
index 0000000..2dbb696
--- /dev/null
+++ b/src/help/studio_help/src/images/filter-results-icon.png
Binary files differ
diff --git a/src/help/studio_help/src/images/loc_editor-remove-line-button.png b/src/help/studio_help/src/images/loc_editor-remove-line-button.png
new file mode 100644
index 0000000..e32c8d0
--- /dev/null
+++ b/src/help/studio_help/src/images/loc_editor-remove-line-button.png
Binary files differ
diff --git a/src/help/studio_help/src/images/menu-button.png b/src/help/studio_help/src/images/menu-button.png
new file mode 100644
index 0000000..ecf92b6
--- /dev/null
+++ b/src/help/studio_help/src/images/menu-button.png
Binary files differ
diff --git a/src/help/studio_help/src/images/new-config-button.png b/src/help/studio_help/src/images/new-config-button.png
new file mode 100644
index 0000000..dd02df2
--- /dev/null
+++ b/src/help/studio_help/src/images/new-config-button.png
Binary files differ
diff --git a/src/help/studio_help/src/images/open-persp-button.jpg b/src/help/studio_help/src/images/open-persp-button.jpg
new file mode 100644
index 0000000..a55b4d0
--- /dev/null
+++ b/src/help/studio_help/src/images/open-persp-button.jpg
Binary files differ
diff --git a/src/help/studio_help/src/images/open-scrapbook.png b/src/help/studio_help/src/images/open-scrapbook.png
new file mode 100644
index 0000000..312461c
--- /dev/null
+++ b/src/help/studio_help/src/images/open-scrapbook.png
Binary files differ
diff --git a/src/help/studio_help/src/images/refresh-device-list-button_android.png b/src/help/studio_help/src/images/refresh-device-list-button_android.png
new file mode 100644
index 0000000..0b71440
--- /dev/null
+++ b/src/help/studio_help/src/images/refresh-device-list-button_android.png
Binary files differ
diff --git a/src/help/studio_help/src/images/refresh-sdks.png b/src/help/studio_help/src/images/refresh-sdks.png
new file mode 100644
index 0000000..1ef3b3a
--- /dev/null
+++ b/src/help/studio_help/src/images/refresh-sdks.png
Binary files differ
diff --git a/src/help/studio_help/src/images/remove-result-icon.png b/src/help/studio_help/src/images/remove-result-icon.png
new file mode 100644
index 0000000..46c97b4
--- /dev/null
+++ b/src/help/studio_help/src/images/remove-result-icon.png
Binary files differ
diff --git a/src/help/studio_help/src/images/remove-visible-results-icon.png b/src/help/studio_help/src/images/remove-visible-results-icon.png
new file mode 100644
index 0000000..018acd1
--- /dev/null
+++ b/src/help/studio_help/src/images/remove-visible-results-icon.png
Binary files differ
diff --git a/src/help/studio_help/src/images/start-avd-button.png b/src/help/studio_help/src/images/start-avd-button.png
new file mode 100644
index 0000000..f9fbc7b
--- /dev/null
+++ b/src/help/studio_help/src/images/start-avd-button.png
Binary files differ
diff --git a/src/help/studio_help/src/images/terminate-result-icon.png b/src/help/studio_help/src/images/terminate-result-icon.png
new file mode 100644
index 0000000..91f13dc
--- /dev/null
+++ b/src/help/studio_help/src/images/terminate-result-icon.png
Binary files differ
diff --git a/src/help/studio_help/src/images/tml-new-instance.png b/src/help/studio_help/src/images/tml-new-instance.png
new file mode 100644
index 0000000..7f9bc0a
--- /dev/null
+++ b/src/help/studio_help/src/images/tml-new-instance.png
Binary files differ
diff --git a/src/help/studio_help/src/topics/c_android-studio.dita b/src/help/studio_help/src/topics/c_android-studio.dita
new file mode 100644
index 0000000..d9e52f0
--- /dev/null
+++ b/src/help/studio_help/src/topics/c_android-studio.dita
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE concept PUBLIC "-//OASIS//DTD DITA Concept//EN" "../dtd/concept.dtd">
+<concept xml:lang="en-us" id="c_android-studio">
+ <title>About MOTODEV Studio for Android</title>
+ <prolog>
+ <metadata>
+ <keywords><!--<indexterm></indexterm>--></keywords>
+ </metadata>
+ </prolog>
+ <conbody>
+ <p>MOTODEV Studio for Android offers unsurpassed workflow ease and a unique integrated experience for developing
+ Android applications. One installer brings you everything you need: from design to development to deployment on an
+ Android handset to sales, with a focus on Motorola products. MOTODEV Studio for Android saves you many steps so
+ you can focus on creativity while maximizing productivity.</p>
+ <p>Among the many features of MOTODEV Studio for Android are:</p>
+ <ul>
+ <li><b>Complete Development Package</b>: one installer ensures an integrated development environment with Eclipse
+ and Android Development Tools (ADT) plus automatic download of the Android SDKs, add-ons, and samples on 32-bit
+ or 64-bit versions of Microsoft Windows and Linux, and 64-bit versions of Mac OS X. All of the essentials are
+ set up for you. Or, install as plugins into an existing Eclipse installation.</li>
+ <li><uicontrol>Native Development</uicontrol>: Build projects with native (C/C++) components, generate C/C++
+ classes from native function declarations in your Java source files.</li>
+ <li><uicontrol>App Validator</uicontrol>: Detect and repair problems before your app is distributed. The App
+ Validator checks your app for a wide range of improper conditions: missing permissions and translated strings,
+ improper layouts, incompatibilities with various devices, and problems with your Java code such as unclosed
+ database cursors. </li>
+ <li><b>Code Generation</b>: Automatically generate code to access UI widgets defined in your xml-based layout
+ files.</li>
+ <li><b>Device Management View</b>: Easily create and manage AVDs (Android Virtual Devices), and set AVD startup
+ options using the Device Management view. Use this view to quickly launch an ADB shell or Emulator Console.</li>
+ <li><b>Screen Capture</b>: From the Device Management view you can easily capture an image of the screen on a real
+ or emulated device.</li>
+ <li><b>Handset Emulators in a view</b>: test applications on an integrated emulator within a MOTODEV Studio view:
+ no need to switch between IDE and target. Make use of handset-specific emulators.</li>
+ <li><b>Target Motorola Handsets</b>: debug and run applications on connected Android handsets, such as those
+ available from Motorola, Mobility.</li>
+ <li><b>Application Creation Wizards</b>: create essential classes simply and easily, including Broadcast Receiver,
+ Content Provider, Service, and Activity.</li>
+ <li><b>Drag and Drop Snippets</b>: add often-used code from templates for more stable and better performing
+ applications.</li>
+ <li><b>Application Signing</b>: create and import certificates to sign applications.</li>
+ <li><b>Deploy Packages</b>: load your applications onto a target handset or emulator with just a few clicks.
+ Easily export zip-aligned packages (signed or unsigned) compatible with Google Play.</li>
+ <li><b>MOTODEV Studio Perspective</b>: displays the most common development views by default.</li>
+ <li><b>MOTODEV Database Perspective</b>: allows you to view and edit SQLite databases on developer handsets and
+ emulated devices.</li>
+ <li><b>Localization Files Editor</b>: helps you to create, translate, and manage localized strings files.</li>
+ <li><b>RSS Feeds view</b>: includes feeds from Motorola, Inc. related to Android app development and marketing.</li>
+ <li><b>MOTODEV Web Resources</b>: this web page, presented as an Eclipse view, contains links to MOTODEV Studio
+ release notes and other helpful resources on the web.</li>
+ </ul>
+ </conbody>
+</concept>
diff --git a/src/help/studio_help/src/topics/c_code-generator.dita b/src/help/studio_help/src/topics/c_code-generator.dita
new file mode 100644
index 0000000..2744a3f
--- /dev/null
+++ b/src/help/studio_help/src/topics/c_code-generator.dita
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE concept PUBLIC "-//OASIS//DTD DITA Concept//EN" "../dtd/concept.dtd">
+<concept id="c_code-generator" xml:lang="en-us">
+ <title>Generating Java Code for an Activity or Fragment Layout</title>
+ <shortdesc>MOTODEV Studio can generate boilerplate code for selected UI objects defined in an activity's or fragment's
+ layout file. It generates attribute declarations, findViewById() calls, and event handlers.</shortdesc>
+ <prolog>
+ <metadata>
+ <keywords>
+ <!--<indexterm></indexterm>-->
+ </keywords>
+ </metadata>
+ </prolog>
+ <conbody>
+ <p>Initiate the Java code generator by right-clicking the Java source file for your Activity or Fragment and
+ selecting <menucascade><uicontrol>Source</uicontrol><uicontrol>Generate Java Code Based on
+ Layout</uicontrol></menucascade>. The code generator begins by looking through the Java code for your selected
+ Activity or Fragment to identify the xml-based layout file it uses. If it is processing an Activity, it searches
+ for a call to setContentView() in the Activity's onCreate() method. If it is processing a Fragment, it searches
+ for code that looks like this:
+ <codeblock> View v = inflater.inflate(R.layout.<i>myLayoutFileName</i>, container, false);
+ return v;</codeblock>Having
+ identified the layout file corresponding to the selected Java class, the code generator looks through the layout
+ file for UI objects that have Android IDs defined with "@+id". It then presents the list of UI objects to you, and
+ you indicate those UI objects for which it should generate code. Finally, the code generator generates the
+ appropriate code and inserts it into the Activity's or Fragment's top-level class. </p>
+ <p>Note that in order for the above to work, your Activity or Fragment code must not have errors. As well, the xml
+ file that defines your layout must be well-formed. Also note that the Java code generator works with the Android
+ Compatibility package: FragmentActivity subclasses are supported.</p>
+ <section>
+ <title>What it Generates</title>
+ <p>The code generator adds to the Activity or Fragment an attribute for each selected UI object as well as for
+ Fragments inside an Activity. Then, it generates the following code within your Activity's onCreate() method or
+ your Fragment's onCreateView() method: <ol>
+ <li>findViewById() statements for graphical items such as TextView, EditText, Button, etc.</li>
+ <li>OnClickListener handlers for Button, ImageButton, and ToggleButton objects. If the button has an
+ android:onClick attribute defined in the layout file, the named method is created (empty) instead of an
+ inline OnClickListener handler.</li>
+ <li>OnItemSelectedListener handlers for Spinner objects.</li>
+ <li>OnItemClickListener handlers for Gallery objects.</li>
+ <li>OnKeyListener handlers for EditText objects.</li>
+ <li>OnSeekBarChangeListener handlers for SeekBar objects.</li>
+ <li>OnRatingBarChangeListener handlers for RatingBar objects.</li>
+ </ol></p>
+ <p>For fragments inside an Activity, the code generator creates setFragmentManager().findFragmentById() calls.</p>
+ <p>For RadioButtons inside a RadioGroup, the code generator creates an object that implements the
+ View.OnClickListener interface. The object's generated onClick() method contains a switch statement with a case
+ for each RadioButton.</p>
+ <p>Note that the code generator does not simply add all of the above blindly. It is careful not to add duplicate
+ attributes, event handlers, or findViewById() statements if it finds an existing findViewById() statement for a
+ given UI object. So, for instance, if you have an existing layout with supporting code, upon adding a new UI
+ object to that layout and initiating the code-generation process you will only be given the option to generate
+ code for the new UI object. As well, the code is added in such a way as to allow you to use <menucascade
+ ><uicontrol>Edit</uicontrol><uicontrol>Undo</uicontrol></menucascade> to back out the additions, if you
+ decide not to go ahead with them. </p>
+ </section>
+ </conbody>
+</concept>
diff --git a/src/help/studio_help/src/topics/c_keys-keystores.dita b/src/help/studio_help/src/topics/c_keys-keystores.dita
new file mode 100644
index 0000000..9e535ff
--- /dev/null
+++ b/src/help/studio_help/src/topics/c_keys-keystores.dita
@@ -0,0 +1,85 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE concept PUBLIC "-//OASIS//DTD DITA Concept//EN" "../dtd/concept.dtd">
+<concept id="c_keys-keystores" xml:lang="en-us">
+ <title>Keys and Keystores</title>
+ <shortdesc>Keys are used to sign Android packages. Keys live within keystore files.</shortdesc>
+ <prolog>
+ <metadata>
+ <keywords>
+ <!--<indexterm></indexterm>-->
+ </keywords>
+ </metadata>
+ </prolog>
+ <conbody>
+ <section>
+ <p>Android relies upon "public key cryptography" to ensure the integrity of all Android packages (APKs). Public
+ Key Cryptography is a cryptographic system that uses a pair of keys—one public and one private—where one key
+ encrypts a message or signs an app while the other decrypts the encrypted message or verifies the app's
+ signature. The two keys are strings of binary digits; for purposes of Android app signing, Google recommends
+ that your keys be at least 2048 bits in length. Because of the way that the signing algorithm works, the private
+ key must be kept private while the public key can be freely distributed. Note that for message encryption, you
+ encrypt the message with the public key and decrypt it with the private key. For app signing, the reverse is
+ true: you sign the app with the private key, and verify the signature using the public key. </p>
+ </section>
+<section><title>Certificates</title>A certificate is an electronic document that uses a digital signature to connect a
+ public key to an identity (of a person or corporation, typically). The identity is a set of information such as
+ the name of a person or an organization, their address, and so forth. Certificates used to sign Android
+ applications follow the X.509 standard; that standard defines the set of identity fields used within the
+ certificate. Android uses the certificate both to identify the author of an app and to establish a trust
+ relationship between apps that have both been signed using the same certificate. In addition to the public key and
+ identity information, certificates also have a "validity period": a period of time during which the certificate is
+ valid. Beyond this validity period, the certificate is considered invalid. Android developers apply their own
+ certificates; unlike on some platforms, Google doesn't apply a certificate of its own to apps submitted to Google
+ Play. <p>Certificates can be issued by a trusted source—a "Certificate Authority" or CA—or they can be
+ user-generated. This latter form is often referred to as "self-signed". When used to sign an Android app, the
+ certificate need not be issued by a CA. In fact, self-signed certificates are not only allowed but are typically
+ what developers use to sign Android apps. As with your key pairs, you can use keytool to generate, display,
+ import, and export digital certificates. </p><p>X.509 certificates are kept in files; install one by moving the
+ file to the device and pointing the app that needs it to the certificate file. </p><note>The Signing and Keys view presents a streamlined interface for
+ working with keystores and "keys." In reality, you are creating and manipulating certificates, but to minimize the
+ confusion for developers not comfortable with keys, certificates, and signing, the term "key" is used by the tools
+ within this view to represent both certificates and the public/private key pairs associated with them.</note></section> <section>
+ <title>Keystores</title>
+ <p>A keystore is a repository (usually, an encrypted file) containing private keys and
+ security certificates. You can use a command-line tool called keytool to manipulate the keystore, or you can use
+ the tools provided in MOTODEV Studio's Signing and Keys view. These tools employ keytool on your behalf; you don't
+ have to invoke the command-line tools to generate keys and certificates, to manipulate keystores, or to sign your
+ apps.</p>
+ </section>
+ <section>
+ <title>Why Are Apps Signed?</title>
+ <p>Android requires that all apps—development builds and release builds alike—installed on an Android device be
+ signed. There are a few reasons for this: <ul>
+ <li>To provide some degree of assurance that an app is unaltered</li>
+ <li>To securely tie an app to its updates</li>
+ <li>To allow close ties between separate apps signed using the same certificate </li>
+ </ul>For Android apps, there are additional benefits to code signing: <ul>
+ <li>Application signing is the first step to placing an application into its own application sandbox. The
+ signing certificate helps to define which user ID is associated with a given application. Apps signed with
+ different certificates run under different user IDs, and cannot directly access each other's data.
+ Application signing thus ensures that one application can only interact with another through a well-defined
+ IPC mechanism.</li>
+ <li>Two apps signed with the same certificate can be run in the same process. An APK has the option to specify
+ in its manifest that it will share its UID with other similarly signed APKs. If the certificate (or, more
+ accurately, the public key in the certificate) used to sign one app matches the one used to sign another app
+ on the same device, those apps will be run in the same process and will have access to each other's
+ data.</li>
+ <li>Apps can declare security permissions at the Signature protection level, allowing some access by apps
+ signed with the same key while maintaining distinct UIDs and application sandboxes.</li>
+ </ul> The requirement that all apps be signed extends to apps under development, as well as to the release
+ versions of your apps. Both at app installation, and again when the app is run, the device checks the app for a
+ signature. This check cannot be bypassed by "side-loading" apps or downloading apps from other than Google Play.
+ Therefore, all apps must be signed. Fortunately for developers the tools automatically generate and apply a
+ debug certificate to development builds; you need only get involved when you create release versions of your
+ apps. When you are ready to release an app, you sign it with a suitable private key. Note that you cannot
+ publish an app that has been signed with a debug key. </p>
+ <p>You can publish, but users can't install, an app that is signed using a certificate that has expired. Android
+ will, however, allow you to run an app that has already been installed even if the certificate has since
+ expired. Related to this is the fact that app updates must be signed using the same certificate as the app to
+ which they apply. Thus when creating a certificate to use for app signing make sure that it has a sufficiently
+ long validity period—Google recommends a minimum of 25 years—to last for not only the projected lifetime of the
+ app itself, but also any updates you may issue. This is a very important point, because after the certificate
+ expires, users can no longer install the app and can no longer install any updates. </p>
+ </section>
+ </conbody>
+</concept>
diff --git a/src/help/studio_help/src/topics/cs_android.dita b/src/help/studio_help/src/topics/cs_android.dita
new file mode 100644
index 0000000..124a356
--- /dev/null
+++ b/src/help/studio_help/src/topics/cs_android.dita
@@ -0,0 +1,559 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE cshelp
+ PUBLIC "-//IBM//DTD DITA CSHelp//EN" "../dtd/cshelp.dtd">
+<cshelp id="cs_android" xml:lang="en-us">
+ <title>Android CS Help</title>
+ <shortdesc/>
+ <csbody/>
+ <!-- Plug-in: com.motorola.studio.android.emulator -->
+ <cshelp id="emulator">
+ <title>Android Emulator view</title>
+ <shortdesc>Presents a simulated handset to which you can deploy and test your applications.</shortdesc>
+ <csbody/>
+ <related-links>
+ <link href="u_emulator_android.dita"/>
+ </related-links>
+ </cshelp>
+ <!-- Plug-in: com.motorola.studio.android -->
+ <cshelp id="newproj">
+ <title>New Android Project wizard</title>
+ <shortdesc>Creates a new MOTODEV Studio for Android project.</shortdesc>
+ <csbody/>
+ <related-links>
+ <link href="u_new-proj_android.dita"/>
+ </related-links>
+ </cshelp>
+ <!-- Plug-in: com.motorola.studio.android -->
+ <cshelp id="newwdgproj">
+ <title>New Android Widget Project wizard</title>
+ <shortdesc>Creates a new MOTODEV Studio for Android project for an Android widget.</shortdesc>
+ <csbody/>
+ <related-links>
+ <link href="u_new-proj-widget_android.dita"/>
+ </related-links>
+ </cshelp>
+ <!-- Plug-in: com.motorola.studio.android.codeutils -->
+ <cshelp id="newactivity">
+ <title>New Activity wizard</title>
+ <shortdesc>Creates a new activity in an existing project.</shortdesc>
+ <csbody/>
+ <related-links>
+ <link href="u_new-activity.dita"/>
+ </related-links>
+ </cshelp>
+ <!-- Plug-in: com.motorola.studio.android.codeutils -->
+ <cshelp id="new-activity-based-on-template">
+ <title>New Android Activity Based on Template wizard</title>
+ <shortdesc>Creates a new activity, based on a sample activity, in an existing project.</shortdesc>
+ <csbody/>
+ <related-links>
+ <link href="u_new-activity-template.dita"/>
+ </related-links>
+ </cshelp>
+ <!-- Plug-in: com.motorola.studio.android.codeutils -->
+ <cshelp id="newbcastrcvr">
+ <title>New Broadcast Receiver wizard</title>
+ <shortdesc>Creates a new broadcast receiver in an existing project.</shortdesc>
+ <csbody/>
+ <related-links>
+ <link href="u_new-broadcast-receiver.dita"/>
+ </related-links>
+ </cshelp>
+ <!-- Plug-in: com.motorola.studio.android.codeutils -->
+ <cshelp id="newcontprov">
+ <title>New Content Provider wizard</title>
+ <shortdesc>Creates a new content provider in an existing project.</shortdesc>
+ <csbody/>
+ <related-links>
+ <link href="u_new-content-provider.dita"/>
+ </related-links>
+ </cshelp>
+ <!-- Plug-in: com.motorola.studio.android.codeutils -->
+ <cshelp id="newwidgtprvd">
+ <title>New Android Widget Provider wizard</title>
+ <shortdesc>Creates a new Android widget provider in an existing project.</shortdesc>
+ <csbody/>
+ <related-links>
+ <link href="u_new-widget-provider.dita"/>
+ </related-links>
+ </cshelp>
+ <!-- Plug-in: com.motorola.studio.android.codeutils -->
+ <cshelp id="newservice">
+ <title>New Service wizard</title>
+ <shortdesc>Creates a new Android service in an existing project.</shortdesc>
+ <csbody/>
+ <related-links>
+ <link href="u_new-service.dita"/>
+ </related-links>
+ </cshelp>
+ <!-- Plug-in: com.motorola.studio.android -->
+ <cshelp id="started_instances_selection_dialog">
+ <title>Preferred AVD Offline dialog</title>
+ <shortdesc>Shown when the AVD specified by a run configuration is not running, and a compatible AVD is running. This dialog allows you to specify whether the app should be deployed to the compatible AVD or the specified AVD.</shortdesc>
+ <csbody/>
+ <related-links>
+ <link href="u_avd-offline-dialog.dita"/>
+ </related-links>
+ </cshelp>
+ <!-- Plug-in: com.motorola.studio.platform.logger.collector -->
+ <cshelp id="collectlogs">
+ <title>Collect Log Files dialog</title>
+ <shortdesc>Allows you to collect Eclipse and Studio log files and package them into a single ZIP archive.</shortdesc>
+ <csbody/>
+ <related-links>
+ <link href="u_collect-log-files.dita"/>
+ </related-links>
+ </cshelp>
+ <!-- Plug-in: org.eclipse.sequoyah.device.framework.ui -->
+ <cshelp id="devmgr">
+ <title>Device Management view</title>
+ <shortdesc>Displays configuration information for a selected device and can be used to manually start or stop a
+ selected device.</shortdesc>
+ <csbody/>
+ <related-links>
+ <link href="u_device-manager_android.dita"/>
+ </related-links>
+ </cshelp>
+ <!-- Plug-in: org.eclipse.sequoyah.device.framework.ui -->
+ <cshelp id="newdev">
+ <title>Create a New Device dialog</title>
+ <shortdesc>Creates a new Android Virtual Device (AVD).</shortdesc>
+ <csbody/>
+ <related-links>
+ <link href="u_new-device.dita"/>
+ </related-links>
+ </cshelp>
+ <!-- Plug-in: com.motorola.studio.android.emulator -->
+ <cshelp id="newdevmain">
+ <title>New Android Virtual Device Instance dialog (main information)</title>
+ <shortdesc>Supply basic information about the AVD, such as the target operating system and the device skin to use.</shortdesc>
+ <csbody/>
+ <related-links>
+ <link href="u_new-device-main_android.dita"/>
+ </related-links>
+ </cshelp>
+ <!-- Plug-in: com.motorola.studio.android.emulator -->
+ <cshelp id="newdevstartup">
+ <title>New Android Virtual Device Instance dialog (startup options)</title>
+ <shortdesc>Supply options to use when starting the AVD.</shortdesc>
+ <csbody/>
+ <related-links>
+ <link href="u_new-device-startup_android.dita"/>
+ </related-links>
+ </cshelp>
+ <!-- Plug-in: com.motorola.studio.android.launch -->
+ <cshelp id="mainLaunchTab">
+ <title>MOTODEV Studio for Android Application - run/debug configuration dialog</title>
+ <shortdesc>A run configuration brings together all of the information needed to run a specific executable on a
+ specific device instance, making it simple to repeatedly run that executable on that device instance.</shortdesc>
+ <csbody/>
+ <related-links>
+ <link href="u_run-config-main.dita"/>
+ </related-links>
+ </cshelp>
+ <!-- Plug-in: com.motorola.studio.android.launch.ui -->
+ <!-- the above plug-in doesn't exist... -->
+ <cshelp id="DeviceSelectionDialog">
+ <title>Instance Selection dialog</title>
+ <shortdesc>Select a device instance (either real or emulated).</shortdesc>
+ <csbody/>
+ <related-links>
+ <link href="u_select-instance.dita"/>
+ </related-links>
+ </cshelp>
+ <!-- Plug-in: com.motorola.studio.android.launch -->
+ <cshelp id="activitySelectionDialog">
+ <title>Activity Selection dialog</title>
+ <shortdesc>Select an activity.</shortdesc>
+ <csbody/>
+ <related-links>
+ <link href="u_select-activity.dita"/>
+ </related-links>
+ </cshelp>
+ <!-- Plug-in: com.motorola.studio.android.launch -->
+ <cshelp id="projectSelectionDialog">
+ <title>Project Selection dialog</title>
+ <shortdesc>Select a project.</shortdesc>
+ <csbody/>
+ <related-links>
+ <link href="u_select-project.dita"/>
+ </related-links>
+ </cshelp>
+ <!-- Plug-in: com.motorolamobility.studio.android.certmanager -->
+ <cshelp id="sign_external_pkg_wiz">
+ <title>Package Signing dialog</title>
+ <shortdesc>Allows you to sign one or more Android packages with a public/private key pair.</shortdesc>
+ <csbody/>
+ <related-links>
+ <link href="u_mpkg-sign_android.dita"/>
+ </related-links>
+ </cshelp>
+ <!-- Plug-in: com.motorolamobility.studio.android.certmanager -->
+ <cshelp id="unsign_external_pkg_wiz">
+ <title>Package Signature Removal dialog</title>
+ <shortdesc>Allows you to remove the signatures applied to one or more packages.</shortdesc>
+ <csbody/>
+ <related-links>
+ <link href="u_mpkg-unsign_android.dita"/>
+ </related-links>
+ </cshelp>
+ <!-- Plug-in: com.motorola.studio.platform.tools.sign.ui -->
+ <cshelp id="appsigning">
+ <title>Application Signing Tool view</title>
+ <shortdesc>Automates the digital signing of applications for Android devices.</shortdesc>
+ <csbody/>
+ <related-links>
+ <link href="u_app-signing-view.dita"/>
+ </related-links>
+ </cshelp>
+ <!-- Plug-in: com.motorola.studio.android -->
+ <cshelp id="preference-emulator-view">
+ <title>Android Emulator preferences</title>
+ <shortdesc>Contains preferences that control the operation of the Android Emulator view.</shortdesc>
+ <csbody/>
+ <related-links>
+ <link href="u_studio-prefs_emulator.dita"/>
+ </related-links>
+ </cshelp>
+ <!-- Plug-in: com.motorola.studio.android -->
+ <cshelp id="preference-android-emulator">
+ <title>MOTODEV Studio for Android preferences</title>
+ <shortdesc>Contains preferences specific to MOTODEV Studio for Android.</shortdesc>
+ <csbody/>
+ <related-links>
+ <link href="u_studio-prefs_android.dita"/>
+ </related-links>
+ </cshelp>
+ <!-- Plug-in: com.motorola.studio.android -->
+ <!-- Currently this one is not linked from anywhere, but should be Preferences > MOTODEV Studio > MOTODEV Login -->
+ <cshelp id="preference-motoappstore">
+ <title>MOTODEV Login preferences</title>
+ <shortdesc>Contains preferences specific to publishing apps to Motorola.</shortdesc>
+ <csbody/>
+ <related-links>
+ <link href="u_studio-prefs_appstore.dita"/>
+ </related-links>
+ </cshelp>
+ <!-- Plug-in: com.motorolamobility.studio.android.db.devices -->
+ <cshelp id="preference-database">
+ <title>MOTODEV Database preferences</title>
+ <shortdesc>Contains preferences that are used when working with databases within MOTODEV Studio.</shortdesc>
+ <csbody/>
+ <related-links>
+ <link href="u_studio-prefs_db.dita"/>
+ </related-links>
+ </cshelp>
+ <!-- Plug-in: com.motorola.studio.platform.tools.sign.ui -->
+ <cshelp id="create-key-pair">
+ <title>Create Key Pair dialog</title>
+ <shortdesc>Create a public/private key pair.</shortdesc>
+ <csbody/>
+ <related-links>
+ <link href="u_sign-create-key-pair.dita"/>
+ </related-links>
+ </cshelp>
+ <!-- Plug-in: com.motorola.studio.platform.tools.sign.ui -->
+ <cshelp id="import-cert">
+ <title>Import Certificate dialog</title>
+ <shortdesc>Import a certificate.</shortdesc>
+ <csbody/>
+ <related-links>
+ <link href="u_sign-import-cert.dita"/>
+ </related-links>
+ </cshelp>
+ <!-- Plug-in: com.motorola.studio.platform.tools.sign.ui -->
+ <cshelp id="import-key">
+ <title>Import Key dialog</title>
+ <shortdesc>Import a key.</shortdesc>
+ <csbody/>
+ <related-links>
+ <link href="u_sign-import-key.dita"/>
+ </related-links>
+ </cshelp>
+ <!-- Plug-in: com.motorolamobility.studio.android.certmanager -->
+ <cshelp id="create-self-cert">
+ <title>Create Key dialog</title>
+ <shortdesc>Create a new self-signing key for use when signing Android packages (APKs).</shortdesc>
+ <csbody/>
+ <related-links>
+ <link href="u_sign-create-self-cert.dita"/>
+ </related-links>
+ </cshelp>
+ <!-- Plug-in: com.motorolamobility.studio.android.certmanager -->
+ <cshelp id="import_keystore">
+ <title>Import Keystore dialog</title>
+ <shortdesc>Import a keystore created outside of MOTODEV Studio for use within MOTODEV Studio's Signing and Keys view.</shortdesc>
+ <csbody/>
+ <related-links>
+ <link href="u_sign-import-keystore.dita"/>
+ </related-links>
+ </cshelp>
+ <!-- Plug-in: com.motorola.studio.android.packaging.ui -->
+ <cshelp id="packaging_help">
+ <title>Export Android Package Files dialog</title>
+ <shortdesc>Create Android package files for selected projects.</shortdesc>
+ <csbody/>
+ <related-links>
+ <link href="u_pkg-export.dita"/>
+ </related-links>
+ </cshelp>
+ <!-- Plug-in: com.motorola.studio.android -->
+ <cshelp id="install_app">
+ <title>Install application dialog</title>
+ <shortdesc>Specify a package to be installed.</shortdesc>
+ <csbody/>
+ <related-links>
+ <link href="u_pkg-install.dita"/>
+ </related-links>
+ </cshelp>
+ <!-- Plug-in: com.motorola.studio.android -->
+ <cshelp id="uninstall_app">
+ <title>Uninstall Application dialog</title>
+ <shortdesc>Specify one or more packages to be removed from a device.</shortdesc>
+ <csbody/>
+ <related-links>
+ <link href="u_device-app-uninstall.dita"/>
+ </related-links>
+ </cshelp>
+ <!-- Plug-in: com.motorolamobility.studio.android.db.core -->
+ <cshelp id="dbexplorer">
+ <title>MOTODEV Database Explorer</title>
+ <shortdesc>View and edit SQLite databases in Android apps.</shortdesc>
+ <csbody/>
+ <related-links>
+ <link href="u_db-database-explorer.dita"/>
+ </related-links>
+ </cshelp>
+ <!-- Plug-in: org.eclipse.datatools.sqltools.sqleditor -->
+ <cshelp id="sqleditorhelp">
+ <title>SQL Scrapbook</title>
+ <shortdesc>Construct and execute SQL statements.</shortdesc>
+ <csbody/>
+ <related-links>
+ <link href="u_db-sql-scrapbook.dita"/>
+ </related-links>
+ </cshelp>
+ <!-- Plug-in: com.motorola.studio.android.codeutils -->
+ <cshelp id="create_db_classes">
+ <title>Database Management Classes</title>
+ <shortdesc>Generate classes that allow you to manage and access a database.</shortdesc>
+ <csbody/>
+ <related-links>
+ <link href="u_db-classes-create.dita"/>
+ </related-links>
+ </cshelp>
+ <!-- Plug-in: com.motorola.studio.android.installer -->
+ <cshelp id="configuration_dialog">
+ <title>Download Components</title>
+ <shortdesc>Downloads and installs new components, such as SDKs, SDK add-ons, language packs, and code samples.</shortdesc>
+ <csbody/>
+ <related-links>
+ <link href="u_download-components.dita"/>
+ </related-links>
+ </cshelp>
+ <!-- Plug-in: com.motorola.studio.android.installer -->
+ <cshelp id="sdk_download_location">
+ <title>SDK Download Location</title>
+ <shortdesc>Specifies the location to which the Android SDK should be downloaded.</shortdesc>
+ <csbody/>
+ <related-links>
+ <link href="u_sdk-download-location.dita"/>
+ </related-links>
+ </cshelp>
+ <!-- Plug-in: com.motorola.studio.android.installer -->
+ <cshelp id="sdk_download_retry">
+ <title>SDK Not Found</title>
+ <shortdesc>Indicates that the chosen SDK could not be downloaded due to a problem with your Internet connection.</shortdesc>
+ <csbody/>
+ <related-links>
+ <link href="u_download-components.dita"/>
+ </related-links>
+ </cshelp>
+ <!-- Plug-in: com.motorola.studio.android.devices.services -->
+ <cshelp id="langPage">
+ <title>Emulator Language</title>
+ <shortdesc>Changes the device's language configuration.</shortdesc>
+ <csbody/>
+ <related-links>
+ <link href="u_emulator-language.dita"/>
+ </related-links>
+ </cshelp>
+ <!-- Plug-in: com.motorola.studio.android -->
+ <cshelp id="dump_hprof">
+ <title>Analyze Memory Using MAT dialog</title>
+ <shortdesc>Select the running application to be analyzed using MAT, the Eclipse memory analyzer.</shortdesc>
+ <csbody/>
+ <related-links>
+ <link href="u_memory-analyze-app-select.dita"/>
+ </related-links>
+ </cshelp>
+ <!-- Plug-in: com.motorola.studio.android -->
+ <cshelp id="monkey">
+ <title>Run Configurations (Monkey) dialog</title>
+ <shortdesc>The Android UI/Application Exerciser Monkey sends pseudo-random events (within limits you set) to your
+ application. This can prove to be an effective way to exercise little-used paths within your code, thus uncovering
+ bugs you might otherwise not find. Within MOTODEV Studio for Android you create "Test events with Monkey" run
+ configurations that specify the device on which to test, the packages to be exercised, the number of pseudo-random
+ events to send, and other constraints on how the events are generated. These configurations allow you to create
+ repeatable test scenarios for your applications.</shortdesc>
+ <csbody/>
+ <related-links>
+ <link href="u_run-config-monkey-main.dita"/>
+ </related-links>
+ </cshelp>
+ <!-- Plug-in: com.motorola.studio.android -->
+ <cshelp id="obuscation_property">
+ <title>MOTODEV Studio</title>
+ <shortdesc>Use this dialog to control whether Java classes should be obfuscated when the parent Android project is
+ built for release.</shortdesc>
+ <csbody/>
+ <related-links>
+ <link href="u_obfuscation-prop.dita"/>
+ </related-links>
+ </cshelp>
+ <!-- Plug-in: com.motorola.studio.android -->
+ <cshelp id="obfuscation-dialog">
+ <title>Obfuscation</title>
+ <shortdesc>Obfuscate the Java classes that make up one or more Android projects.</shortdesc>
+ <csbody/>
+ <related-links>
+ <link href="u_obfuscate-projects.dita"/>
+ </related-links>
+ </cshelp>
+ <!-- Plug-in: com.motorola.studio.android.codeutils -->
+ <cshelp id="generate-code-from-layout-dialog">
+ <title>Generate Java Code Based on Layout</title>
+ <shortdesc>Generates boilerplate code for selected UI elements defined in an activity's or fragment's layout file.</shortdesc>
+ <csbody/>
+ <related-links>
+ <link href="u_code-generator.dita"/>
+ </related-links>
+ </cshelp>
+ <!-- Plug-in: com.motorola.studio.android.videos.views -->
+ <cshelp id="MOTODEVVideosView">
+ <title>MOTODEV Video Tutorials view</title>
+ <shortdesc>Presents tutorial videos that demonstrate how to use MOTODEV Studio.</shortdesc>
+ <csbody/>
+ <related-links>
+ <link href="u_video-view.dita"/>
+ </related-links>
+ </cshelp>
+ <!-- Plug-in: com.motorolamobility.studio.android.db.core -->
+ <cshelp id="create_database_wizard">
+ <title>Create New Database</title>
+ <shortdesc>Creates a new database, and optionally defines one or more new, empty tables within that database.</shortdesc>
+ <csbody/>
+ <related-links>
+ <link href="u_db-create.dita"/>
+ </related-links>
+ </cshelp>
+ <!-- Plug-in: com.motorolamobility.studio.android.db.core -->
+ <cshelp id="create_table_wizard">
+ <title>Create New Table</title>
+ <shortdesc>Defines a new, empty table within an existing database.</shortdesc>
+ <csbody/>
+ <related-links>
+ <link href="u_db-table-create.dita"/>
+ </related-links>
+ </cshelp>
+ <!-- Plug-in: com.motorola.studio.android.codeutils -->
+ <cshelp id="generate-code-from-context-menu-dialog">
+ <title>Generate Options Menu Code Based on XML file</title>
+ <shortdesc>Generates options menu code from a selected XML-format menu definition file in your project's res/menu
+ folder.</shortdesc>
+ <csbody/>
+ <related-links>
+ <link href="u_options-menu-code-create.dita"/>
+ </related-links>
+ </cshelp>
+ <!-- Plug-in: com.motorola.studio.android.codeutils -->
+ <cshelp id="defineconnectiondatabasepage">
+ <title>Select Database Table dialog</title>
+ <shortdesc>Allows you to select the database table to be displayed when creating a new activity based upon the Database List template.</shortdesc>
+ <csbody/>
+ <related-links>
+ <link href="u_select-table.dita"/>
+ </related-links>
+ </cshelp>
+ <!-- Plug-in: com.motorola.studio.android.codeutils -->
+ <cshelp id="selectcolumnspage">
+ <title>Select Table Columns dialog</title>
+ <shortdesc>Allows you to select the database table's columns to be displayed when creating a new activity based upon the Database List template.</shortdesc>
+ <csbody/>
+ <related-links>
+ <link href="u_select-columns.dita"/>
+ </related-links>
+ </cshelp>
+ <!-- Plug-in: com.motorolamobility.studio.android.certmanager -->
+ <cshelp id="keystore-key-help-id">
+ <title>Keys and Keystores</title>
+ <shortdesc>Keys (public/private key pairs) are used to sign Android packages (APKs); APKs cannot be installed unless they are signed. Keys are contained within specially formatted files known as "keystores."</shortdesc>
+ <csbody/>
+ <related-links>
+ <link href="c_keys-keystores.dita"/>
+ </related-links>
+ </cshelp>
+ <!-- Plug-in: com.motorolamobility.studio.android.certmanager -->
+ <cshelp id="import_entries_dialog">
+ <title>Import Entries dialog</title>
+ <shortdesc>Import into a keystore selected entries (keys) from another keystore.</shortdesc>
+ <csbody/>
+ <related-links>
+ <link href="u_sign-import-keystore-entries.dita"/>
+ </related-links>
+ </cshelp>
+ <!-- Plug-in: com.motorolamobility.studio.android.certmanager -->
+ <cshelp id="new_keystore">
+ <title>Create Keystore dialog</title>
+ <shortdesc>Create a new keystore. A keystore contains keys for use when signing Android packages (APKs); each key must reside within a keystore.</shortdesc>
+ <csbody/>
+ <related-links>
+ <link href="u_sign-create-keystore.dita"/>
+ </related-links>
+ </cshelp>
+ <!-- Plug-in: com.motorolamobility.studio.android.certmanager -->
+ <cshelp id="convert_keystore_type">
+ <title>Change Keystore Type dialog</title>
+ <shortdesc>Change the file format of a keystore.</shortdesc>
+ <csbody/>
+ <related-links>
+ <link href="u_sign-keystore-type-changing.dita"/>
+ </related-links>
+ </cshelp>
+ <!-- Plug-in: com.motorolamobility.studio.android.certmanager -->
+ <cshelp id="backup_keystore">
+ <title>Keystores Backup dialog</title>
+ <shortdesc>Back up one or more keystores to a zip archive.</shortdesc>
+ <csbody/>
+ <related-links>
+ <link href="u_sign-keystore-backup.dita"/>
+ </related-links>
+ </cshelp>
+ <!-- Plug-in: com.motorolamobility.studio.android.certmanager -->
+ <cshelp id="restore_keystore">
+ <title>Restore From Backup dialog</title>
+ <shortdesc>Restore one or more keystores that had been backed up to a zip archive.</shortdesc>
+ <csbody/>
+ <related-links>
+ <link href="u_sign-keystore-backup-restore.dita"/>
+ </related-links>
+ </cshelp>
+ <!-- Plug-in: com.motorolamobility.studio.android.certmanager -->
+ <cshelp id="certificate_info_dialog">
+ <title>Key Properties dialog</title>
+ <shortdesc>Examine the various properties of a key.</shortdesc>
+ <csbody/>
+ <related-links>
+ <link href="u_sign-cert-properties.dita"/>
+ </related-links>
+ </cshelp>
+ <!-- Plug-in: com.motorolamobility.studio.android.certmanager -->
+ <cshelp id="select_keystore">
+ <title>Import Keystore (during package export) dialog</title>
+ <shortdesc>Select a keystore that isn't known to the Signing and Keys view for use during the package export process.</shortdesc>
+ <csbody/>
+ <related-links>
+ <link href="u_keystore-select.dita"/>
+ </related-links>
+ </cshelp>
+</cshelp>
diff --git a/src/help/studio_help/src/topics/g_legal.dita b/src/help/studio_help/src/topics/g_legal.dita
new file mode 100644
index 0000000..bf09c2f
--- /dev/null
+++ b/src/help/studio_help/src/topics/g_legal.dita
@@ -0,0 +1,25 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<!DOCTYPE topic PUBLIC "-//OASIS//DTD DITA Topic//EN" "http://docs.oasis-open.org/dita/v1.1/OS/dtd/topic.dtd">
+<topic id="g_legal">
+ <title>Trademark notices</title>
+ <body>
+ <p>Copyright © <ph conref="g_variables.dita#g_variables/product-copyright-years"/>, Motorola Mobility, Inc. All rights
+ reserved.</p>
+ <p>This documentation is provided to you under the terms of the Motorola End User License Agreement. Motorola Mobility, Inc.
+ reserves the right to revise this documentation and to make changes in content from time to time without
+ obligation on the part of Motorola Mobility, Inc. to provide notification of such revision or changes.</p>
+ <p>If this documentation is provided on compact disc or as part of another software package, the other software and
+ documentation on the compact disc are subject to the license agreement accompanying the compact disc.</p>
+ <p>MOTOROLA and the Stylized M Logo are registered in the U.S. Patent &amp; Trademark Office. <ph
+ product="android-studio">Android is a trademark of Google Inc. Use of this trademark is subject to <xref
+ scope="external" format="html" href="http://www.google.com/permissions/index.html">Google Permissions</xref>.
+ </ph>Java and all other Java-based marks are trademarks or registered trademarks of Sun Microsystems, Inc. in the
+ U.S. and other countries. Eclipse is a trademark of Eclipse Foundation, Inc. <ph product="android-studio"
+ >DeviceAnywhere is a trademark of Mobile Complete, Inc. </ph><ph product="webui">Microsoft, Windows, Windows Me,
+ and Windows XP are registered trademarks of Microsoft Corporation. Linux is the registered trademark of Linus
+ Torvalds in the United States and other countries. VMware is a registered trademark or trademarks (the "Marks")
+ of VMware, Inc. in the United States and/or other jurisdictions. </ph>
+ <ph product="javame-studio javame-sdk">Symbian and UIQ are registered trademarks of Symbian Software Ltd. </ph>
+ All other product and service names are the property of their respective owners.</p>
+ </body>
+</topic>
diff --git a/src/help/studio_help/src/topics/g_variables.dita b/src/help/studio_help/src/topics/g_variables.dita
new file mode 100644
index 0000000..1e8b5ec
--- /dev/null
+++ b/src/help/studio_help/src/topics/g_variables.dita
@@ -0,0 +1,26 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<!DOCTYPE topic PUBLIC "-//OASIS//DTD DITA Topic//EN" "http://docs.oasis-open.org/dita/v1.1/OS/dtd/topic.dtd">
+<topic id="g_variables">
+ <title>Variables</title>
+ <body>
+ <p>Use this file to create variables for elements supported in generic topics. Create a
+ phrase or other element, assign it an ID, then add text to the element. To have a
+ different value based on a filtered attribute (product, platform, audience, otherprops),
+ nest multiple elements within the ID'd element, each with a different attribute value.
+ See the example below:</p>
+ <p>
+ <ph id="studio-sdk-product-name"><ph product="javame-sdk">MOTODEV SDK for Java ME</ph><ph product="javame-studio">MOTODEV Studio for Java ME</ph><ph product="webui">MOTODEV Studio for WebUI</ph><ph product="android-studio">MOTODEV Studio for Android</ph></ph>
+ <ph id="gui-product-name"><ph product="javame-sdk">the Launchpad application</ph><ph product="javame-studio">MOTODEV Studio for Java ME</ph><ph product="webui">MOTODEV Studio for WebUI</ph><ph product="android-studio">MOTODEV Studio for Android</ph></ph>
+ <ph id="tools-and-services-view-name"><ph product="javame-sdk">Utilities</ph><ph product="javame-studio">Java ME Options</ph></ph>
+ <ph id="studio-sdk-prefs-parent-menu"><ph product="javame-sdk"><uicontrol>File</uicontrol></ph><ph product="javame-studio android-studio"><uicontrol>Window</uicontrol> (on Mac OS X, <uicontrol>MOTODEV Studio for Android</uicontrol>)</ph></ph>
+ </p>
+
+ <p>
+ <ph id="product-copyright-years"><ph product="javame-sdk javame-studio">2007-2009</ph><ph product="android-studio">2009-2012</ph></ph>
+ </p>
+
+ <p>Here's an example of how to conref one of the above variables:</p>
+ <codeblock>&lt;ph conref="g_variables.dita#g_variables/studio-sdk-product-name"/></codeblock>
+
+ </body>
+</topic>
diff --git a/src/help/studio_help/src/topics/t_activity-creating.dita b/src/help/studio_help/src/topics/t_activity-creating.dita
new file mode 100644
index 0000000..0e50f7e
--- /dev/null
+++ b/src/help/studio_help/src/topics/t_activity-creating.dita
@@ -0,0 +1,85 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE task
+ PUBLIC "-//OASIS//DTD DITA Task//EN" "../dtd/task.dtd">
+<task xml:lang="en-us" id="t_activity-creating">
+ <title>Adding an Android activity</title>
+ <shortdesc>Allows you to easily add a new activity to an existing Android project. Activities are typically used to
+ implement part of the application's UI.</shortdesc>
+ <prolog>
+ <metadata>
+ <keywords>
+ <!--<indexterm></indexterm>-->
+ </keywords>
+ </metadata>
+ </prolog>
+ <taskbody>
+ <context/>
+ <steps>
+ <step>
+ <cmd>In the Package Explorer, right-click the project to which the activity should be added and select
+ <menucascade><uicontrol>New</uicontrol><uicontrol>Android Activity</uicontrol></menucascade>.</cmd>
+ </step>
+ <step>
+ <cmd>If you would like to base this activity on one of the supplied templates (and thus have some of the code
+ filled in for you), click <uicontrol>Create New Activity Based on Template</uicontrol>.</cmd>
+ <substeps>
+ <substep>
+ <cmd>From the list of template activities, select the one upon which the activity should be based. Note that
+ one of the samples--Database List--lists the contents of selected columns within your application's SQLite
+ database; in order to select this sample your application must already have a database in its <codeph
+ >assets</codeph> folder.</cmd>
+ </substep>
+ <substep>
+ <cmd>Click <uicontrol>Next</uicontrol>.</cmd>
+ </substep>
+ </substeps>
+ </step>
+ <step>
+ <cmd>Verify the contents of the <uicontrol>Source folder</uicontrol> and <uicontrol>Package</uicontrol> fields,
+ ensuring that they are correct for your project.</cmd>
+ </step>
+ <step>
+ <cmd>Specify a name for your new Activity subclass in <uicontrol>Name</uicontrol>.</cmd>
+ </step>
+ <step>
+ <cmd>By default the new activity will not have a label of its own; the application's label will be used if and
+ when the activity needs to be presented to the user. If you want a specific label for this activity, clear the
+ <uicontrol>Default</uicontrol> option (next to the <uicontrol>Label</uicontrol> field) and then enter your
+ preferred user-readable label.</cmd>
+ </step>
+ <step>
+ <cmd>If this activity uses device capabilities for which the user must grant permission, specify them in the
+ <uicontrol>Permission</uicontrol> area. These permissions will be added to the appropriate place in your
+ application's manifest file. To specify a permission, click <uicontrol>Add</uicontrol>, select the needed
+ permission, and click <uicontrol>OK</uicontrol>.</cmd>
+ </step>
+ <step>
+ <cmd>Specify the intents that this new activity responds to: for each, click <uicontrol>Add</uicontrol> (next to
+ <uicontrol>Action</uicontrol>) and select the appropriate intent or click <uicontrol>Input</uicontrol> and
+ enter the full name of the intent in the field provided.</cmd>
+ <info>You can select multiple intents from the dialog that appears when you click <uicontrol>Add</uicontrol>. To
+ select a range, select the first item in the set and then hold down the Shift key while selecting the last
+ item in the set. To select multiple separate intents, select the first item and then hold down the Control key
+ while selecting the remaining items.</info>
+ </step>
+ <step>
+ <cmd>If you need to specify additional information about the intents using one or more standard action
+ categories, for each click <uicontrol>Add</uicontrol> (next to <uicontrol>Category</uicontrol>) and select the
+ appropriate one or click <uicontrol>Input</uicontrol> and enter a category manually.</cmd>
+ </step>
+ <step>
+ <cmd>Unless you are basing your new activity on a template, the New Android Activity wizard will create the
+ activity with an empty onCreate() method. If you will be implementing onStart(), select the option to include
+ an empty onStart() method as well.</cmd>
+ </step>
+ <step>
+ <cmd>If this new activity is the one that should be started when the application is launched, select <uicontrol
+ >Set as the main activity</uicontrol>.</cmd>
+ </step>
+ <step>
+ <cmd>Click <uicontrol>Finish</uicontrol>.</cmd>
+ </step>
+ </steps>
+ <result>The activity is created and added to the selected project. It is also opened in an editor view.</result>
+ </taskbody>
+</task>
diff --git a/src/help/studio_help/src/topics/t_android-perspective-opening.dita b/src/help/studio_help/src/topics/t_android-perspective-opening.dita
new file mode 100644
index 0000000..5eb10ef
--- /dev/null
+++ b/src/help/studio_help/src/topics/t_android-perspective-opening.dita
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE task
+ PUBLIC "-//OASIS//DTD DITA Task//EN" "../dtd/task.dtd">
+<task xml:lang="en-us" id="t_android-perspective-opening">
+ <title>Opening the MOTODEV Studio for Android perspective</title>
+ <prolog>
+ <metadata>
+ <keywords>
+ <!--<indexterm></indexterm>-->
+ </keywords>
+ </metadata>
+ </prolog>
+ <taskbody>
+ <context>The MOTODEV Studio for Android perspective is normally opened automatically, but in the event you need to
+ open it manually you open it like any other <tm tmtype="tm">Eclipse</tm> perspective.</context>
+ <steps>
+ <step>
+ <cmd>Bring up the Open Perspective dialog by doing one of the following:</cmd>
+ <choices>
+ <choice>Click Open Perspective (<image href="../images/open-persp-button.jpg" placement="inline">
+ </image>), located in the top right corner of the MOTODEV Studio window, and select <uicontrol
+ >Other</uicontrol>.</choice>
+ <choice>From the <uicontrol>Window</uicontrol> menu, select <menucascade><uicontrol>Open
+ Perspective</uicontrol>
+ <uicontrol>Other</uicontrol>
+ </menucascade></choice>
+ </choices>
+ </step>
+ <step>
+ <cmd>In the <uicontrol>Open Perspective</uicontrol> dialog, click <uicontrol>MOTODEV Studio for
+ Android</uicontrol> and then click <uicontrol>OK</uicontrol>.</cmd>
+ <stepresult>The MOTODEV Studio for Android perspective is displayed. </stepresult>
+ </step>
+ </steps>
+ <postreq>
+ <note type="note">For further information on how to use, create, customize and configure perspectives, refer to
+ the Eclipse documentation: <ol>
+ <li>Select <uicontrol>Help Contents</uicontrol> from the <uicontrol>Help</uicontrol> menu. </li>
+ <li>Select <menucascade><uicontrol>Workbench User Guide</uicontrol><uicontrol>Getting
+ Started</uicontrol><uicontrol>Basic Tutorial</uicontrol>
+ <uicontrol>Perspectives</uicontrol></menucascade>.</li>
+ </ol></note>
+ </postreq>
+ </taskbody>
+</task>
diff --git a/src/help/studio_help/src/topics/t_app-cert-props-changing.dita b/src/help/studio_help/src/topics/t_app-cert-props-changing.dita
new file mode 100644
index 0000000..0d298c9
--- /dev/null
+++ b/src/help/studio_help/src/topics/t_app-cert-props-changing.dita
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE task
+ PUBLIC "-//OASIS//DTD DITA Task//EN" "../dtd/task.dtd">
+<task xml:lang="en-us" id="t_app-cert-props-changing">
+ <title>Managing keys and keystores</title>
+ <shortdesc>The Signing and Keys view provides you a simple means to generate keys and keystores, delete keys and
+ keystores, import keys from one keystore to another, backup and restore keystores, and of course sign Android
+ Package (APK) files with your keys.</shortdesc>
+</task>
diff --git a/src/help/studio_help/src/topics/t_app-cert-removing.dita b/src/help/studio_help/src/topics/t_app-cert-removing.dita
new file mode 100644
index 0000000..9e800f7
--- /dev/null
+++ b/src/help/studio_help/src/topics/t_app-cert-removing.dita
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE task
+ PUBLIC "-//OASIS//DTD DITA Task//EN" "../dtd/task.dtd">
+<task xml:lang="en-us" id="t_app-cert-removing">
+ <title>Deleting a <ph product="android-studio">key</ph></title>
+ <prolog>
+ <metadata>
+ <keywords>
+ <!--<indexterm></indexterm>-->
+ </keywords>
+ </metadata>
+ </prolog>
+ <taskbody>
+ <context>
+ <p>To delete a key or certificate listed in the Signing and Keys view:</p>
+ </context>
+ <steps>
+ <step>
+ <cmd>If necessary, open the Signing and Keys view by <ph product="webui android-studio">selecting <menucascade>
+ <uicontrol>Window</uicontrol>
+ <uicontrol>Show View</uicontrol>
+ <uicontrol>Other</uicontrol>
+ </menucascade> and then selecting <menucascade>
+ <uicontrol>MOTODEV Studio</uicontrol>
+ <uicontrol>Signing and Keys</uicontrol>
+ </menucascade> from the list of views</ph>. Note that this view is part of the MOTODEV Studio for Android
+ perspective and is thus normally opened when you switch to that perspective.</cmd>
+ </step>
+ <step>
+ <cmd>Within the Signing and Keys view, if the key to be deleted is not already showing, expose the contents of
+ the keystore containing the key by clicking the disclosure triangle to the left of the keystore's name.</cmd>
+ </step>
+ <step>
+ <cmd>Select the key and click <image href="../images/KeyDelete.png"/> (Delete key).</cmd>
+ </step>
+ <step>
+ <cmd>Confirm the deletion of the selected item when prompted.</cmd>
+ </step>
+ </steps>
+ <result> The selected key is deleted from the keystore.</result>
+ </taskbody>
+</task>
diff --git a/src/help/studio_help/src/topics/t_app-cert-signing_android.dita b/src/help/studio_help/src/topics/t_app-cert-signing_android.dita
new file mode 100644
index 0000000..80b9348
--- /dev/null
+++ b/src/help/studio_help/src/topics/t_app-cert-signing_android.dita
@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE task
+ PUBLIC "-//OASIS//DTD DITA Task//EN" "../dtd/task.dtd">
+<task xml:lang="en-us" id="t_app-cert-signing">
+ <title>Signing Android packages</title>
+ <shortdesc>Once you have a keystore with at least one key pair, you can use that key to sign your Android packages
+ (APKs). You generally will sign your applications as you package them, but occasionally you will need to sign an
+ Android Package (APK) file that has already been created (for instance, if you need to change the certificate with
+ which an APK file is signed, you can remove the signature and then apply a new one as described here).</shortdesc>
+ <prolog>
+ <metadata>
+ <keywords>
+ <!--<indexterm></indexterm>-->
+ </keywords>
+ </metadata>
+ </prolog>
+ <taskbody>
+ <prereq>Note that this procedure requires that you have an Android Package (APK) file for each application being
+ signed. As well, the key must already be known to MOTODEV Studio for Android.</prereq>
+ <steps>
+ <step>
+ <cmd>If necessary, open the Signing and Keys view by <ph product="webui android-studio">selecting <menucascade>
+ <uicontrol>Window</uicontrol>
+ <uicontrol>Show View</uicontrol>
+ <uicontrol>Other</uicontrol>
+ </menucascade> and then selecting <menucascade>
+ <uicontrol>MOTODEV Studio</uicontrol>
+ <uicontrol>Signing and Keys</uicontrol>
+ </menucascade> from the list of views</ph>. Note that this view is part of the MOTODEV Studio for Android
+ perspective and is thus normally opened when you switch to that perspective.</cmd>
+ </step>
+ <step>
+ <cmd>In the Signing and Keys view click <image href="../images/PackageSign.png" placement="inline"/> (Sign
+ Android Package).</cmd>
+ <stepresult>The Package Signing dialog appears.</stepresult>
+ </step>
+ <step>
+ <cmd>Using the <uicontrol>Packages folder</uicontrol> field, specify the folder that contains the APK files
+ (there can be more than one) to be signed. This is often the <codeph>dist</codeph> directory within an Android
+ application's project.</cmd>
+ </step>
+ <step>
+ <cmd>Specify the keystore containing the key to use for signing, and enter the keystore password if necessary.
+ Note that if you instructed MOTODEV Studio to save the keystore password when you created the keystore (or
+ during a previous signing operation), the password is filled in for you.</cmd>
+ </step>
+ <step>
+ <cmd>Using the <uicontrol>Key</uicontrol> drop-down list, specify the key to be used when signing the selected
+ package(s).</cmd>
+ </step>
+ <step>
+ <cmd>Under <uicontrol>Select the packages</uicontrol>, select the APK files that are to be signed with the
+ specified key. Note that the listed packages are those found in the folder you specified earlier.</cmd>
+ </step>
+ <step>
+ <cmd>Click <uicontrol>Finish</uicontrol>.</cmd>
+ </step>
+ <step>
+ <cmd>If you did not instruct MOTODEV Studio to save the selected key's password (either when you created the key
+ or during a previous signing operation), you will be prompted to enter the password for the key. Enter it in
+ the field provided and then click <uicontrol>OK</uicontrol>.</cmd>
+ </step>
+ </steps>
+ <result>The selected Android Package files are signed with the chosen key and then aligned on 4-byte boundaries
+ using the zipalign tool.</result>
+ </taskbody>
+</task>
diff --git a/src/help/studio_help/src/topics/t_app-debugging_android.dita b/src/help/studio_help/src/topics/t_app-debugging_android.dita
new file mode 100644
index 0000000..473afe5
--- /dev/null
+++ b/src/help/studio_help/src/topics/t_app-debugging_android.dita
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE task
+ PUBLIC "-//OASIS//DTD DITA Task//EN" "../dtd/task.dtd">
+<task xml:lang="en-us" id="t_app-debugging_android">
+ <title>Debugging an Android application</title>
+ <shortdesc>Use this procedure to debug an Android application, defined by a project listed in the Package Explorer, on
+ an emulated device, or on a physical device connected to your development
+ computer.</shortdesc>
+ <prolog>
+ <metadata>
+ <keywords>
+ <!--<indexterm></indexterm>-->
+ </keywords>
+ </metadata>
+ </prolog>
+ <taskbody>
+ <prereq>In order to debug your application, you need to have a target device defined. To debug on an emulated
+ device, you must have created at least one AVD. To debug on a physical device, you must <!--either -->connect that
+ device to your development computer using a supported method such as a USB
+ cable<!--, or you must have acquired the device
+ and connected to it using the <tm tmtype="tm">DeviceAnywhere</tm> service-->.</prereq>
+ <steps>
+ <step>
+ <cmd>Set a breakpoint in your project by double-clicking in the grey "gutter" to the left of the line before
+ which execution should halt.</cmd>
+ <stepresult>A blue circle appears in the gutter, indicating that a breakpoint has been set. Note that if you do
+ not set a breakpoint before beginning to debug, execution does not automatically halt anywhere within your
+ application.</stepresult>
+ </step>
+ <step>
+ <cmd>Right-click the project in the Package Explorer and select <menucascade><uicontrol>Debug
+ As</uicontrol><uicontrol>Android Application using Studio for Android</uicontrol></menucascade> from the
+ menu that appears.</cmd>
+ <stepresult>The Debug Configurations dialog appears, open to your application's debug configuration (if this is
+ the first time you have debugged this application, a new configuration will have been created for
+ you).</stepresult>
+ </step>
+ <step>
+ <cmd>On the Main tab of the Debug Configurations dialog, click <uicontrol>Browse</uicontrol> next to the
+ <uicontrol>Instance</uicontrol> field and select the target AVD or device.</cmd>
+ <info>The Instance Selection list lists all AVDs and all physical devices connected to your development
+ computer<!--, and all Android devices acquired through the DeviceAnywhere VDL-->. If the Instance Selection
+ list is empty, you will either need to create an AVD or connect an Android device to your development
+ computer<!--, or use DeviceAnywhere Studio to acquire and connect to a target device-->.</info>
+ </step>
+ <!--<step xmlns:ditaarch="http://dita.oasis-open.org/architecture/2005/"><cmd>If you are debugging on a remote device through the DeviceAnywhere VDL, select <uicontrol>Launcher for DeviceAnywhere Studio devices</uicontrol>. Otherwise, leave <uicontrol>Default launcher</uicontrol> selected.</cmd></step>-->
+ <step>
+ <cmd>Click <uicontrol>Debug</uicontrol> to save and run your new configuration.</cmd>
+ </step>
+ <step>
+ <cmd>If you are debugging your application on an emulated device and the Android Emulator view is not open, you
+ will be asked whether the emulator should be presented within an Eclipse view. If you click <uicontrol
+ >No</uicontrol>, the emulator will appear in a separate window.</cmd>
+ <info>At any time you can switch the emulator from a view within MOTODEV Studio to an external window (or from an external window to a view): see <xref href="t_emulator-external.dita"></xref>.
+</info>
+ </step>
+ </steps>
+ <result>The application is transferred to the target device and then launched for debugging.</result>
+ </taskbody>
+</task>
diff --git a/src/help/studio_help/src/topics/t_app-deploying_android.dita b/src/help/studio_help/src/topics/t_app-deploying_android.dita
new file mode 100644
index 0000000..f534afb
--- /dev/null
+++ b/src/help/studio_help/src/topics/t_app-deploying_android.dita
@@ -0,0 +1,66 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE task
+ PUBLIC "-//OASIS//DTD DITA Task//EN" "../dtd/task.dtd">
+<task xml:lang="en-us" id="t_app-deploying_javame">
+ <title>Installing an application on a device</title>
+ <shortdesc>Describes how to deploy a packaged Android application onto a real or emulated Android device.</shortdesc>
+ <prolog>
+ <metadata>
+ <keywords>
+ <!--<indexterm></indexterm>-->
+ </keywords>
+ </metadata>
+ </prolog>
+ <taskbody>
+ <prereq>
+ <note type="important">The target device must be listed in the Device Management view, and it must be online. If
+ you are deploying to an emulated device, that emulator must be running. If you are deploying to an Android
+ handset, it must be connected to your development computer.</note>
+ </prereq>
+ <steps>
+ <step>
+ <cmd>Click the <uicontrol>Device Management</uicontrol> tab, if necessary, to bring forward the Device
+ Management view.</cmd>
+ </step>
+ <step>
+ <cmd>Install the app by dragging and dropping its APK onto a running AVD or connected handset listed in the
+ Device Management view (note that you can use this technique to install multiple APKs by selecting them all
+ and then dragging and dropping the group onto the listed device). Alternatively, you can do the following:</cmd>
+ <substeps>
+ <substep>
+ <cmd>Right-click the device to which the application is to be deployed.</cmd>
+ </substep>
+ <substep>
+ <cmd>From the menu that appears, select <uicontrol>Install App</uicontrol>.</cmd>
+ <stepresult>The Install Application dialog appears.</stepresult>
+ </substep>
+ <substep>
+ <cmd>Using the <uicontrol>Application package</uicontrol> field, select the APK file to be installed.</cmd>
+ <info>If the APK file is not properly signed for the target device, the dialog will indicate that the
+ package is either unsigned or malformed. You cannot proceed if this is the case.</info>
+ </substep>
+ <substep>
+ <cmd>Specify what MOTODEV Studio for Android should do if a version of the application already exists on the
+ target device.</cmd>
+ <info>
+ <ul>
+ <li>If MOTODEV Studio can safely overwrite any existing version of the application, select <uicontrol
+ >Overwrite if application already exists</uicontrol>.</li>
+ <li>To have any previous version of the application uninstalled before installing this new one, select
+ <uicontrol>Uninstall application before install</uicontrol>.</li>
+ <li>If you do not want an existing version of the application overwritten--that is, if you only want the
+ application deployed to the device if it does not already reside on the device--select the <uicontrol
+ >Do nothing if application is already installed</uicontrol> option.</li>
+ </ul>
+ </info>
+ </substep>
+ <substep>
+ <cmd>Click <uicontrol>Finish</uicontrol>.</cmd>
+ </substep>
+ </substeps>
+ </step>
+ </steps>
+ <result>The application is transferred to the chosen device, unless the application already exists on the device and
+ you elected not to overwrite it, in which case an error message will be generated.</result>
+ </taskbody>
+</task>
diff --git a/src/help/studio_help/src/topics/t_app-launching_android.dita b/src/help/studio_help/src/topics/t_app-launching_android.dita
new file mode 100644
index 0000000..c075d23
--- /dev/null
+++ b/src/help/studio_help/src/topics/t_app-launching_android.dita
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE task PUBLIC "-//OASIS//DTD DITA Task//EN" "../dtd/task.dtd">
+<task xml:lang="en-us" id="t_app-launching_android">
+ <title>Running an Android application</title>
+ <shortdesc>Use this procedure to run an Android application, defined by a project listed in the Package Explorer, on
+ an emulated device or on a physical device connected to your development
+ computer.</shortdesc>
+ <prolog>
+ <metadata>
+ <keywords>
+ <!--<indexterm></indexterm>-->
+ </keywords>
+ </metadata>
+ </prolog>
+ <taskbody>
+ <prereq>In order to run your application, you need to have a target device defined. To run on an emulated device,
+ you must have created at least one AVD. To run on a physical device, you must <!--either -->connect that device to
+ your development computer using a supported method such as a USB
+ cable<!--, or you must have acquired the device and connected to it using the <tm tmtype="tm" xmlns:ditaarch="http://dita.oasis-open.org/architecture/2005/">DeviceAnywhere</tm> service-->.</prereq>
+ <steps>
+ <step>
+ <cmd>Right-click the project in the Package Explorer and select <menucascade><uicontrol>Run
+ As</uicontrol><uicontrol>Android Application using Studio for Android</uicontrol></menucascade> from the
+ menu that appears. If MOTODEV Studio is able to determine on which device the app should be run, it will
+ simply run the app. Otherwise, The Run Configurations dialog appears, open to your application's run
+ configuration (if this is the first time you have run this application, a new configuration will have been
+ created for you).</cmd>
+ <substeps>
+ <substep>
+ <cmd>On the Main tab of the Run Configurations dialog, click <uicontrol>Browse</uicontrol> next to the
+ <uicontrol>Device</uicontrol> field and select the target AVD or device.</cmd>
+ <info>The Device Selection list lists all AVDs and all physical devices connected to your development
+ computer<!--, and all Android devices acquired through the DeviceAnywhere VDL-->. If the Device Selection
+ list is empty, you will either need to create an AVD or connect an Android device to your development
+ computer<!--, or use the DeviceAnywhere VDL to acquire a target device-->.</info>
+ </substep>
+ <substep>
+ <cmd>Click <uicontrol>Run</uicontrol> to save and run your new configuration.</cmd>
+ </substep>
+ </substeps>
+ <info>When you use <menucascade><uicontrol>Run As</uicontrol><uicontrol>Android Application using Studio for
+ Android</uicontrol></menucascade> and there isn't an existing Run Configuration for your application,
+ MOTODEV Studio constructs one for you. It determines the appropriate target device by first looking at online
+ devices; the first one (in alphabetical order, by name) that has a compatible API version is used. If there
+ are no compatible devices currently online, it then looks at the offline devices: the first of these (again,
+ in alphabetical order, by name) that has a compatible API version is started and used. If none of the devices,
+ online or not, are compatible, you are presented with the Run Configurations dialog.</info>
+ </step>
+ <!--<step xmlns:ditaarch="http://dita.oasis-open.org/architecture/2005/"><cmd>If you are running on a remote device through the DeviceAnywhere VDL, select <uicontrol>Launcher for DeviceAnywhere Studio devices</uicontrol>. Otherwise, leave <uicontrol>Default launcher</uicontrol> selected.</cmd></step>-->
+ <step>
+ <cmd>If you are running your application on an emulated device and the Android Emulator view is not open, you
+ will be asked whether the emulator should be presented within an Eclipse view. If you click <uicontrol
+ >No</uicontrol>, the emulator will appear in a separate window.</cmd>
+ <info>At any time you can switch the emulator from a view within MOTODEV Studio to an external window (or from
+ an external window to a view): see <xref href="t_emulator-external.dita"/>. </info>
+ </step>
+ </steps>
+ <result>The application is transferred to the target device and then launched.</result>
+ </taskbody>
+</task>
diff --git a/src/help/studio_help/src/topics/t_app-memory-analyzing.dita b/src/help/studio_help/src/topics/t_app-memory-analyzing.dita
new file mode 100644
index 0000000..d11f080
--- /dev/null
+++ b/src/help/studio_help/src/topics/t_app-memory-analyzing.dita
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE task PUBLIC "-//OASIS//DTD DITA Task//EN" "../dtd/task.dtd">
+<task id="t_app-memory-analyzing" xml:lang="en-us">
+ <title>Analyzing Memory with MAT</title>
+ <shortdesc>MOTODEV Studio for Android provides an interface to MAT, the Eclipse memory analyzer. MAT analyzes the Java
+ heap and can be extremely helpful when trying to track down memory leaks. It is also a valuable tool when trying to
+ reduce your app's memory consumption.</shortdesc>
+ <prolog>
+ <metadata>
+ <keywords>
+ <!--<indexterm></indexterm>-->
+ </keywords>
+ </metadata>
+ </prolog>
+ <taskbody>
+ <context>
+ <p>MOTODEV Studio for Android will generate an HPROF binary formatted heap dump for a selected process on a
+ running emulator (or handset, provided that the handset has an SD card installed; on a handset the HPROF file is
+ written to the SD card), and will then launch MAT to analyze the heap dump.</p>
+ </context>
+ <steps>
+ <step>
+ <cmd>From the Device Management view, right-click the running emulator or connected device on which the app or
+ service to be analyzed is running and select <uicontrol>Analyze Memory with MAT</uicontrol>.</cmd>
+ <stepresult>MOTODEV Studio for Android displays a dialog showing all of the running applications and services on
+ the selected device.</stepresult>
+ </step>
+ <step>
+ <cmd>Select the application or service you wish to analyze and click <uicontrol>Finish</uicontrol>.</cmd>
+ <stepresult>MOTODEV Studio for Android generates an HPROF file from the selected application or service and
+ presents a basic analysis in the form of a pie chart in an editor view. It then launches the Getting Started
+ Wizard, from which you can select a report.</stepresult>
+ </step>
+ <step>
+ <cmd>Using the Getting Started Wizard select the desired report to run, or click <uicontrol>Cancel</uicontrol>
+ if you aren't interested in any of the listed reports at this time (you can run these reports at any time from
+ the editor view showing the pie chart; see the list of reports at the bottom of the view.</cmd>
+ </step>
+ </steps>
+ <postreq>Click the various links and pie chart segments to get information about how the selected application or
+ service is using memory. See the Memory Analyzer documentation for instructions on using the tool.</postreq>
+ </taskbody>
+</task>
diff --git a/src/help/studio_help/src/topics/t_app-packaging_android.dita b/src/help/studio_help/src/topics/t_app-packaging_android.dita
new file mode 100644
index 0000000..99be724
--- /dev/null
+++ b/src/help/studio_help/src/topics/t_app-packaging_android.dita
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE task
+ PUBLIC "-//OASIS//DTD DITA Task//EN" "../dtd/task.dtd">
+<task xml:lang="en-us" id="t_app-packaging_android">
+ <title>Packaging an Android application</title>
+ <shortdesc>Describes how to create Android Package (APK) files for one or more MOTODEV Studio for Android projects.
+ APK files are used to distribute and deploy applications for the Android platform. </shortdesc>
+ <prolog>
+ <metadata>
+ <keywords>
+ <!--<indexterm></indexterm>-->
+ </keywords>
+ </metadata>
+ </prolog>
+ <taskbody>
+ <prereq>
+ <p>Note that in order to create an Android package the application being packaged must build without error.</p>
+ </prereq>
+ <steps>
+ <step>
+ <cmd>In the Package Explorer, right-click the project you wish to package into an APK file and select <uicontrol
+ >Export</uicontrol> from the menu that appears.</cmd>
+ <stepresult>The Export dialog appears.</stepresult>
+ </step>
+ <step>
+ <cmd>Select <menucascade><uicontrol>Android</uicontrol><uicontrol>Export Android Application using Studio for
+ Android</uicontrol></menucascade> and click <uicontrol>Next</uicontrol>.</cmd>
+ </step>
+ <step>
+ <cmd>Verify the contents of the Export Android Package Files dialog.</cmd>
+ <substeps>
+ <substep>
+ <cmd>Ensure that the desired packages are selected. An APK file will be generated for each selected
+ package.</cmd>
+ </substep>
+ <substep>
+ <cmd>By default, each APK file is placed in a <codeph>dist</codeph> directory within the corresponding
+ project. Alternatively, you can specify a single directory into which the generated APK files are to be
+ placed: clear the <uicontrol>Use default destination</uicontrol> option, and specify the destination
+ directory using the <uicontrol>Destination</uicontrol> field.</cmd>
+ </substep>
+ <substep>
+ <cmd>If the packages are to be signed, select <uicontrol>Sign the package</uicontrol> and choose the
+ keystore and key to be used. To create an unsigned APK file, make sure that <uicontrol>Sign the
+ package</uicontrol> is not selected.</cmd>
+ <info><ul>
+ <li>If the keystore is already known to (and listed in) the Signing and Keys view, select it from the
+ drop-down list. Otherwise, click <uicontrol>Use existing</uicontrol> to specify an existing keystore
+ that is not yet known to the Signing and Keys view, or <uicontrol>Add new</uicontrol> to create and
+ use a new keystore.</li>
+ <li>Select an existing key from the drop-down list, or create a new key in the keystore by selecting
+ <uicontrol>Add</uicontrol>.</li>
+ </ul></info>
+ </substep>
+ </substeps>
+ </step>
+ <step>
+ <cmd>Click <uicontrol>Finish</uicontrol>.</cmd>
+ <stepresult>
+ <p>The APK files are generated as directed. If you elected to use the default destination, the APK file for
+ each selected project can be found in that project's <codeph>dist</codeph> directory. If you specified your
+ own destination directory, that directory should now contain an APK file for each selected project.</p>
+ <note importance="normal">If you chose to sign the packages, after signing the zipalign tool is used to align
+ the Android Package files on 4-byte boundaries. Also, if obfuscation is enabled for the project, the
+ project's Java classes will be obfuscated.</note>
+ </stepresult>
+ </step>
+ </steps>
+ </taskbody>
+</task>
diff --git a/src/help/studio_help/src/topics/t_app-signature-removing.dita b/src/help/studio_help/src/topics/t_app-signature-removing.dita
new file mode 100644
index 0000000..badbd02
--- /dev/null
+++ b/src/help/studio_help/src/topics/t_app-signature-removing.dita
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE task
+ PUBLIC "-//OASIS//DTD DITA Task//EN" "../dtd/task.dtd">
+<task xml:lang="en-us" id="t_app-signature-removing">
+ <title>Removing package signatures</title>
+ <shortdesc>Describes how to remove all signatures from an Android package (APK).</shortdesc>
+ <prolog>
+ <metadata>
+ <keywords>
+ <!--<indexterm></indexterm>-->
+ </keywords>
+ </metadata>
+ </prolog>
+ <taskbody>
+ <steps>
+ <step>
+ <cmd>If necessary, open the Signing and Keys view by <ph product="webui android-studio">selecting <menucascade>
+ <uicontrol>Window</uicontrol>
+ <uicontrol>Show View</uicontrol>
+ <uicontrol>Other</uicontrol>
+ </menucascade> and then selecting <menucascade>
+ <uicontrol>MOTODEV Studio</uicontrol>
+ <uicontrol>Signing and Keys</uicontrol>
+ </menucascade> from the list of views</ph>. Note that this view is part of the MOTODEV Studio for Android
+ perspective and is thus normally opened when you switch to that perspective.</cmd>
+ </step>
+ <step>
+ <cmd>In the Signing and Keys view click <image href="../images/PackageUnsign.png" placement="inline"/> (Remove
+ Package Signatures).</cmd>
+ <stepresult>The Package Signature Removal dialog appears.</stepresult>
+ </step>
+ <step>
+ <cmd>Using the <uicontrol>Packages folder</uicontrol> field, specify the folder that contains the APK files
+ (there can be more than one) from which the signatures are to be removed. This is often the
+ <codeph>dist</codeph> directory within an Android application's project.</cmd>
+ </step>
+ <step product="android-studio">
+ <cmd>Under <uicontrol>Select the packages</uicontrol>, select the APK files that are to have their signatures
+ removed. </cmd>
+ </step>
+ <step>
+ <cmd>Click <uicontrol>Finish</uicontrol>.</cmd>
+ </step>
+ </steps>
+ <result>
+ <p product="javame-studio javame-sdk">A message confirms that the signature has been removed.</p>
+ <p product="android-studio">The signatures are removed from the selected APK files.</p>
+ </result>
+ </taskbody>
+</task>
diff --git a/src/help/studio_help/src/topics/t_app-testing-monkey.dita b/src/help/studio_help/src/topics/t_app-testing-monkey.dita
new file mode 100644
index 0000000..aaf6a13
--- /dev/null
+++ b/src/help/studio_help/src/topics/t_app-testing-monkey.dita
@@ -0,0 +1,71 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE task PUBLIC "-//OASIS//DTD DITA Task//EN" "../dtd/task.dtd">
+<task id="t_app-testing-monkey" xml:lang="en-us">
+ <title>Testing Your Application with the UI/Application Exerciser Monkey</title>
+ <shortdesc>The Android UI/Application Exerciser Monkey sends pseudo-random events (within limits you set) to your
+ application. This can prove to be an effective way to exercise little-used paths within your code, thus uncovering
+ bugs you might otherwise not find. The Monkey tool itself is provided as part of the Android SDK, but it has a
+ command-line interface. MOTODEV Studio for Android provides an interface for Monkey, so that you can exercise your
+ applications from within the same environment you use to build them.</shortdesc>
+ <prolog>
+ <metadata>
+ <keywords>
+ <!--<indexterm></indexterm>-->
+ </keywords>
+ </metadata>
+ </prolog>
+ <taskbody>
+ <context>
+ <p>Within MOTODEV Studio for Android you use "Test events with Monkey" launch configurations to test your apps
+ with the UI/Application Exerciser Monkey. </p>
+ <note type="important">Unlike other launch configurations, a "Test events with Monkey" configuration does not load
+ your app onto the target device. You must have previously installed the app(s) you wish to test prior to setting
+ up and running a Monkey configuration.</note>
+ </context>
+ <steps>
+ <step>
+ <cmd>Create the launch configuration using one of the following methods:</cmd>
+ <choices>
+ <choice>If the emulator you wish to use is running, or the device you wish to use is connected to your
+ development computer, right-click the emulator or device in the Device Management view and select <uicontrol
+ >Test events with Monkey</uicontrol>.</choice>
+ <choice>Select <uicontrol>Run Configurations</uicontrol> from the <uicontrol>Run</uicontrol> menu. From the
+ left pane of the Run Configurations dialog select <uicontrol>Test events with Monkey</uicontrol> and click
+ 'New' <image href="../images/new-config-button.png"/> to create a new configuration.</choice>
+ </choices>
+ </step>
+ <step>
+ <cmd>Using the <uicontrol>Device</uicontrol> field (on the <uicontrol>Main</uicontrol> tab), select the device
+ instance to be used to run the Monkey test. Note that you can only select from among running emulators and
+ connected devices; if there are no device instances from which to choose, you'll first need to launch an
+ emulator or connect a handset. </cmd>
+ </step>
+ <step>
+ <cmd>Select those packages that Monkey should exercise from the package list. You can select one or several
+ packages; Monkey will limit its scope only to the selected packages. To select a single package, click it. To
+ select a block of adjacent packages, click the first and then Shift-click the last. To select several
+ individual packages, click the first one and then Control-click (Command-click, on Macintosh) the remaining
+ packages.</cmd>
+ </step>
+ <step>
+ <cmd>Using the <uicontrol>Event count</uicontrol> field, specify the number of pseudo-random events to be sent
+ to the selected packages.</cmd>
+ </step>
+ <step>
+ <cmd>To further customize the way that that Monkey tests your applications, select the <uicontrol
+ >Options</uicontrol> tab and fill in the desired fields. See the UI/Application Exerciser Monkey documentation
+ provided with the Android SDK for information on the purpose and allowable values for each of these fields.
+ Note that you can select <uicontrol>General</uicontrol> and supply options as command-line parameters, rather
+ than as form values.</cmd>
+ </step>
+ <step>
+ <cmd>Click <uicontrol>Run</uicontrol> to save your new configuration and run it. Or, click <uicontrol
+ >Apply</uicontrol> and then <uicontrol>Close</uicontrol> to save your new configuration without running it.
+ </cmd>
+ </step>
+ </steps>
+ <result>When Monkey runs it posts its progress to the MOTODEV Studio for Android Console view.</result>
+ <postreq>You can re-run your Monkey configuration at any time by selecting it from your Run configurations and
+ clicking <uicontrol>Run</uicontrol>.</postreq>
+ </taskbody>
+</task>
diff --git a/src/help/studio_help/src/topics/t_app-uninstalling-android.dita b/src/help/studio_help/src/topics/t_app-uninstalling-android.dita
new file mode 100644
index 0000000..650a5ea
--- /dev/null
+++ b/src/help/studio_help/src/topics/t_app-uninstalling-android.dita
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE task PUBLIC "-//OASIS//DTD DITA Task//EN" "../dtd/task.dtd">
+<task id="t_app-uninstalling-android" xml:lang="en-us">
+ <title>Uninstalling Android applications</title>
+ <shortdesc>From within MOTODEV Studio for Android you can easily uninstall applications from a connected device or
+ emulator.</shortdesc>
+ <prolog>
+ <metadata>
+ <keywords>
+ <!--<indexterm></indexterm>-->
+ </keywords>
+ </metadata>
+ </prolog>
+ <taskbody>
+ <steps>
+ <step>
+ <cmd>Click the <uicontrol>Device Management</uicontrol> tab, if necessary, to bring forward the Device
+ Management view.</cmd>
+ </step>
+ <step>
+ <cmd>Right-click the device from which the application is to be uninstalled. </cmd>
+ <info>The target device must be listed in the Device Management view, and it must be online. If you are
+ uninstalling an application from an emulated device, that emulator must be running. If you are uninstalling
+ from an Android handset, it must be connected to your development computer.</info>
+ </step>
+ <step>
+ <cmd>From the menu that appears, select <uicontrol>Uninstall App</uicontrol>.</cmd>
+ <stepresult>The Uninstall Application dialog appears, listing all of the packages on the target device that can
+ be uninstalled.</stepresult>
+ </step>
+ <step>
+ <cmd>Select the package that you want to uninstall and click <uicontrol>Finish</uicontrol></cmd>
+ </step>
+ </steps>
+ </taskbody>
+</task>
diff --git a/src/help/studio_help/src/topics/t_bcast-rcvr-creating.dita b/src/help/studio_help/src/topics/t_bcast-rcvr-creating.dita
new file mode 100644
index 0000000..b419bc6
--- /dev/null
+++ b/src/help/studio_help/src/topics/t_bcast-rcvr-creating.dita
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE task
+ PUBLIC "-//OASIS//DTD DITA Task//EN" "../dtd/task.dtd">
+<task xml:lang="en-us" id="t_bcast-rcvr-creating">
+ <title>Adding a broadcast receiver</title>
+ <shortdesc>Allows you to easily add a new broadcast receiver to an existing Android project. A broadcast receiver
+ enables you to receive intents sent either from the system or from other applications, even when your application
+ isn't running.</shortdesc>
+ <prolog>
+ <metadata>
+ <keywords>
+ <!--<indexterm></indexterm>-->
+ </keywords>
+ </metadata>
+ </prolog>
+ <taskbody>
+ <context/>
+ <steps>
+ <step>
+ <cmd>In the Package Explorer, right-click the project to which the activity should be added and select
+ <menucascade><uicontrol>New</uicontrol><uicontrol>Android Broadcast
+ Receiver</uicontrol></menucascade>.</cmd>
+ </step>
+ <step>
+ <cmd>Specify a name for your new BroadcastReceiver subclass in <uicontrol>Name</uicontrol>.</cmd>
+ </step>
+ <step>
+ <cmd>By default the new broadcast receiver will not have a label of its own; the application's label will be
+ used wherever necessary. If you want a specific label for this broadcast receiver, clear the <uicontrol
+ >Default</uicontrol> option (next to the <uicontrol>Label</uicontrol> field) and then enter your preferred
+ user-readable label.</cmd>
+ </step>
+ <step>
+ <cmd>If this broadcast receiver uses device capabilities for which the user must grant permission, specify them
+ in the <uicontrol>Permission</uicontrol> area. These permissions will be added to the appropriate place in
+ your application's manifest file. To specify a permission, click <uicontrol>Add</uicontrol>, select the needed
+ permission, and click <uicontrol>OK</uicontrol>.</cmd>
+ </step>
+ <step>
+ <cmd>Specify the types of broadcasts that this new receiver can handle: for each, click <uicontrol
+ >Add</uicontrol> (next to <uicontrol>Action</uicontrol>) and select the appropriate intent.</cmd>
+ </step>
+ <step>
+ <cmd>If you need to specify additional information about the intents using one or more standard categories, for
+ each click <uicontrol>Add</uicontrol> (next to <uicontrol>Category</uicontrol>) and select the appropriate
+ one.</cmd>
+ </step>
+ <step>
+ <cmd>Click <uicontrol>Finish</uicontrol>.</cmd>
+ </step>
+ </steps>
+ <result>The broadcast receiver is created and added to the selected project. It is also opened in an editor
+ view.</result>
+ </taskbody>
+</task>
diff --git a/src/help/studio_help/src/topics/t_cert-key-generating.dita b/src/help/studio_help/src/topics/t_cert-key-generating.dita
new file mode 100644
index 0000000..4c0587a
--- /dev/null
+++ b/src/help/studio_help/src/topics/t_cert-key-generating.dita
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE task
+ PUBLIC "-//OASIS//DTD DITA Task//EN" "../dtd/task.dtd">
+<task xml:lang="en-us" id="t_cert-key-generating">
+ <title>Creating a key</title>
+ <shortdesc>Use the Signing and Keys view within MOTODEV Studio for Android to generate public/private key pairs for
+ use when signing Android packages (APKs).</shortdesc>
+ <prolog>
+ <metadata>
+ <keywords>
+ <!--<indexterm></indexterm>-->
+ </keywords>
+ </metadata>
+ </prolog>
+ <taskbody>
+ <steps>
+ <step>
+ <cmd>If necessary, open the Signing and Keys view by <ph product="webui android-studio">selecting <menucascade>
+ <uicontrol>Window</uicontrol>
+ <uicontrol>Show View</uicontrol>
+ <uicontrol>Other</uicontrol>
+ </menucascade> and then selecting <menucascade>
+ <uicontrol>MOTODEV Studio</uicontrol>
+ <uicontrol>Signing and Keys</uicontrol>
+ </menucascade> from the list of views</ph>. Note that this view is part of the MOTODEV Studio for Android
+ perspective and is thus normally opened when you switch to that perspective.</cmd>
+ </step>
+ <step>
+ <cmd>Within the Signing and Keys view, select the keystore into which the newly created key is to be stored.</cmd>
+ </step>
+ <step>
+ <cmd>Click <image href="../images/KeyCreate.png"/> (Create Key).</cmd>
+ <info>A dialog appears that contains fields for the key name (its "alias"), for each of the components of a
+ distinguished name (DN; used to identify the creator of a key pair or certificate) and for a password used to
+ protect the key.</info>
+ </step>
+ <step>
+ <cmd>Enter an identifier for your key using the <uicontrol>Alias name</uicontrol> field.</cmd>
+ </step>
+ <step>
+ <cmd>Fill out the DN fields. Note that most of these are optional, although the <uicontrol>Validity</uicontrol>
+ field is required.</cmd>
+ <info>See <xref href="u_sign-cert-properties.dita"/> if you have
+ questions about what to supply for a particular field.</info>
+ </step>
+ <step>
+ <cmd>Enter a password that will be used to protect the key (you'll need to supply this password when you sign an
+ Android package using this key). If you want the password to be saved (and automatically used when you sign an
+ APK with this key), select <uicontrol>Save this password</uicontrol>.</cmd>
+ </step>
+ <step>
+ <cmd>Click <uicontrol>Finish</uicontrol>.</cmd>
+ </step>
+ </steps>
+ <result>The key pair is generated and added to the list shown in the Signing and Keys view.</result>
+ </taskbody>
+</task>
diff --git a/src/help/studio_help/src/topics/t_cert-key-properties.dita b/src/help/studio_help/src/topics/t_cert-key-properties.dita
new file mode 100644
index 0000000..dc1367f
--- /dev/null
+++ b/src/help/studio_help/src/topics/t_cert-key-properties.dita
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE task PUBLIC "-//OASIS//DTD DITA Task//EN" "../dtd/task.dtd">
+<task id="t_cert-key-properties" xml:lang="en-us">
+ <title>Examining a key's properties</title>
+ <shortdesc>Use this procedure to look at the properties that were specified when a key was created.</shortdesc>
+ <prolog>
+ <metadata>
+ <keywords>
+ <!--<indexterm></indexterm>-->
+ </keywords>
+ </metadata>
+ </prolog>
+ <taskbody>
+ <steps>
+ <step>
+ <cmd>If necessary, open the Signing and Keys view by <ph product="webui android-studio">selecting <menucascade>
+ <uicontrol>Window</uicontrol>
+ <uicontrol>Show View</uicontrol>
+ <uicontrol>Other</uicontrol>
+ </menucascade> and then selecting <menucascade>
+ <uicontrol>MOTODEV Studio</uicontrol>
+ <uicontrol>Signing and Keys</uicontrol>
+ </menucascade> from the list of views</ph>. Note that this view is part of the MOTODEV Studio for Android
+ perspective and is thus normally opened when you switch to that perspective.</cmd>
+ </step>
+ <step>
+ <cmd>In the Signing and Keys view select the key whose properties you want to see. Note that you may need to
+ click the disclosure triangle next to the keystore containing the key to expose that keystore's
+ contents.</cmd>
+ </step>
+ <step>
+ <cmd>Click <image href="../images/KeyProperties.png"/> (Key Properties).</cmd>
+ <stepresult>The Key Properties dialog is displayed, which shows all of the properties that were specified when
+ the key was created. The Validity period, specified as a duration (in years) when the key was created, is
+ shown as two dates: the date the key was created, and the date that the key expires.</stepresult>
+ </step>
+ <step>
+ <cmd>Click <uicontrol>OK</uicontrol> when you are done reviewing the key's properties.</cmd>
+ </step>
+ </steps>
+ </taskbody>
+</task>
diff --git a/src/help/studio_help/src/topics/t_cert-key-pwd-changing.dita b/src/help/studio_help/src/topics/t_cert-key-pwd-changing.dita
new file mode 100644
index 0000000..a9a98c9
--- /dev/null
+++ b/src/help/studio_help/src/topics/t_cert-key-pwd-changing.dita
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE task PUBLIC "-//OASIS//DTD DITA Task//EN" "../dtd/task.dtd">
+<task id="t_cert-key-pwd-changing" xml:lang="en-us">
+ <title>Changing a key's password</title>
+ <shortdesc>Keys used to sign an Android package are protected using a password: you need to supply the key's password whenever you sign an Android package (APK). You specify the password when you create the key, but you can change that password at any time.</shortdesc>
+ <prolog>
+ <metadata>
+ <keywords>
+ <!--<indexterm></indexterm>-->
+ </keywords>
+ </metadata>
+ </prolog>
+ <taskbody>
+ <steps>
+ <step>
+ <cmd>If necessary, open the Signing and Keys view by <ph product="webui android-studio">selecting <menucascade>
+ <uicontrol>Window</uicontrol>
+ <uicontrol>Show View</uicontrol>
+ <uicontrol>Other</uicontrol>
+ </menucascade> and then selecting <menucascade>
+ <uicontrol>MOTODEV Studio</uicontrol>
+ <uicontrol>Signing and Keys</uicontrol>
+ </menucascade> from the list of views</ph>. Note that this view is part of the MOTODEV Studio for Android
+ perspective and is thus normally opened when you switch to that perspective.</cmd>
+ </step>
+ <step>
+ <cmd>In the Signing and Keys view select the key whose password is to be changed. Note that you may need to
+ click the disclosure triangle next to the keystore containing the key to expose that keystore's
+ contents.</cmd>
+ </step>
+ <step>
+ <cmd>Click <image href="../images/KeyPasswordChange.png"/> (Change Key Password).</cmd>
+ <stepresult>The Enter Password dialog is displayed.</stepresult>
+ </step>
+ <step>
+ <cmd>Enter the key's existing password into the <uicontrol>Old Password</uicontrol> field.</cmd>
+ </step>
+ <step>
+ <cmd>Enter the new key password into the <uicontrol>New password</uicontrol> field, and again in the
+ <uicontrol>Confirm new password</uicontrol> field.</cmd>
+ </step>
+ <step>
+ <cmd>If you want MOTODEV Studio to remember this key's password and automatically fill it in when you use the
+ key to sign an Android package (APK), select <uicontrol>Save this password</uicontrol>.</cmd>
+ </step>
+ <step>
+ <cmd>Click <uicontrol>OK</uicontrol>.</cmd>
+ </step>
+ </steps>
+ <result>The key's password is changed.</result>
+ </taskbody>
+</task>
diff --git a/src/help/studio_help/src/topics/t_code-generator.dita b/src/help/studio_help/src/topics/t_code-generator.dita
new file mode 100644
index 0000000..1d273f5
--- /dev/null
+++ b/src/help/studio_help/src/topics/t_code-generator.dita
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE task PUBLIC "-//OASIS//DTD DITA Task//EN" "../dtd/task.dtd">
+<task id="t_code-generator" xml:lang="en-us">
+ <title>Generating Java Code</title>
+ <shortdesc>The Java code generator can quickly generate boilerplate code for selected UI elements defined in an
+ activity's or fragment's layout file. It generates attribute declarations, findViewById() calls, and event handlers.</shortdesc>
+ <prolog>
+ <metadata>
+ <keywords>
+ <!--<indexterm></indexterm>-->
+ </keywords>
+ </metadata>
+ </prolog>
+ <taskbody>
+ <steps>
+ <step>
+ <cmd>Construct your layout file, ensuring that it contains all of the UI elements that you want to
+ programmatically access from your Java Activity or Fragment subclass. Save your work.</cmd>
+ </step>
+ <step>
+ <cmd>Right-click the Activity or Fragment subclass that presents and controls the layout (right-click either the
+ editor window showing this subclass, or the subclass' filename in the Package Explorer) and select
+ <menucascade><uicontrol>Source</uicontrol><uicontrol>Generate Java Code Based on
+ Layout</uicontrol></menucascade>.</cmd>
+ <info>
+ <note>In order for this operation to work, your Activity or Fragment code must not have errors. As well, the
+ xml file that defines your layout must be well-formed.</note>
+ </info>
+ <stepresult>The Generate Java Code Based on Layout dialog appears.</stepresult>
+ </step>
+ <step>
+ <cmd>Ensure that the <uicontrol>Project</uicontrol> and <uicontrol>Target Class</uicontrol> selections are
+ correct.</cmd>
+ </step>
+ <step>
+ <cmd>From the <uicontrol>UI objects</uicontrol> list, select those items for which code is to be generated.</cmd>
+ <info>To avoid adding duplicate code, if the code generator finds an existing findViewById() statement for a
+ given UI object, it does not include that UI object in the list.</info>
+ </step>
+ <step>
+ <cmd>If at least one of the UI objects is normally used with a listener object, and you do <i>not</i> want
+ listeners to be created for you, clear the <uicontrol>Generate default listeners when possible</uicontrol>
+ option. Otherwise, leave this option selected so that listeners are generated for the appropriate UI
+ objects.</cmd>
+ </step>
+ <step>
+ <cmd>Click <uicontrol>OK</uicontrol>.</cmd>
+ <info>Note that the <uicontrol>OK</uicontrol> button isn't enabled until you select at least one UI object from
+ the list.</info>
+ </step>
+ </steps>
+ <result>Attribute declarations, findViewById() calls, and event handlers are generated, as appropriate, and added to
+ the selected Activity or Fragment subclass.</result>
+ </taskbody>
+</task>
diff --git a/src/help/studio_help/src/topics/t_config-for-native.dita b/src/help/studio_help/src/topics/t_config-for-native.dita
new file mode 100644
index 0000000..237c4ff
--- /dev/null
+++ b/src/help/studio_help/src/topics/t_config-for-native.dita
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE task
+ PUBLIC "-//OASIS//DTD DITA Task//EN" "../dtd/task.dtd">
+<task xml:lang="en-us" id="t_config-for-native">
+ <title>Configuring your development environment for native development</title>
+ <shortdesc>After installing the Android NDK and a few other needed components, Eclipse can be configured to let you
+ develop Android applications that include native (C/C++) code.</shortdesc>
+ <prolog>
+ <metadata>
+ <keywords>
+ <!--<indexterm></indexterm>-->
+ </keywords>
+ </metadata>
+ </prolog>
+ <taskbody>
+ <prereq>Native support requires that the following be installed:<ul>
+ <li>The Eclipse CDT plugin (version 7 or later)</li>
+ <li>The Sequoyah Android feature</li>
+ <li>Android NDK</li>
+ <li>Cygwin (only on systems running Microsoft Windows)</li>
+ <li>The Java JDK (the JRE is not sufficient; you need the full JDK)</li>
+ </ul><p>Your development computer's system path (not the Eclipse path) should include the directory containing the
+ Android NDK, and, on Microsoft Windows, the directory containing the Cygwin binaries.</p><p>For instructions on
+ locating and installing the above components, and on setting your computer's system path, see the <i>MOTODEV
+ Studio for Android Installation Guide</i>.</p></prereq>
+ <context/>
+ <steps>
+ <step>
+ <cmd>Specify the location of the NDK from within your development environment: navigate to the <menucascade
+ ><uicontrol>Android</uicontrol><uicontrol>Android NDK</uicontrol></menucascade> preferences page and
+ specify the path to the directory containing the NDK using the <uicontrol>NDK Location</uicontrol>
+ field.</cmd>
+ </step>
+ </steps>
+ <postreq>Now that you have configured your development environment to enable native development, enable it in those
+ Android projects that have (or will have) native code.</postreq>
+ </taskbody>
+</task>
diff --git a/src/help/studio_help/src/topics/t_content-provider-creating.dita b/src/help/studio_help/src/topics/t_content-provider-creating.dita
new file mode 100644
index 0000000..4d45430
--- /dev/null
+++ b/src/help/studio_help/src/topics/t_content-provider-creating.dita
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE task
+ PUBLIC "-//OASIS//DTD DITA Task//EN" "../dtd/task.dtd">
+<task xml:lang="en-us" id="t_content-provider-creating">
+ <title>Adding a content provider</title>
+ <shortdesc>Allows you to easily add a new content provider, with empty implementations of many commonly-used methods,
+ to an existing Android project. A content provider allows other applications and services to access your
+ application's data, and can be used to expose your application's tables as live folders.</shortdesc>
+ <prolog>
+ <metadata>
+ <keywords>
+ <!--<indexterm></indexterm>-->
+ </keywords>
+ </metadata>
+ </prolog>
+ <taskbody>
+ <context/>
+ <steps>
+ <step>
+ <cmd>In the Package Explorer, right-click the project to which the activity should be added and select
+ <menucascade><uicontrol>New</uicontrol><uicontrol>Android Content Provider</uicontrol></menucascade>.</cmd>
+ </step>
+ <step>
+ <cmd>Specify a name for your new ContentProvider subclass in <uicontrol>Name</uicontrol>.</cmd>
+ </step>
+ <step>
+ <cmd>By default the new provider will not have a label of its own; the application's label will be used to
+ represent the content being exposed. If you want a specific label for this content provider, clear the
+ <uicontrol>Default</uicontrol> option (next to the <uicontrol>Label</uicontrol> field) and then enter your
+ preferred user-readable label.</cmd>
+ </step>
+ <step>
+ <cmd>If this content provider uses device capabilities for which the user must grant permission, specify them in
+ the <uicontrol>Permission</uicontrol> area. These permissions will be added to the appropriate place in your
+ application's manifest file. To specify a permission, click <uicontrol>Add</uicontrol>, select the needed
+ permission, and click <uicontrol>OK</uicontrol>.</cmd>
+ </step>
+ <step>
+ <cmd>Content providers typically use a URI containing the name of their ContentProvider subclass to identify the
+ data being exposed by the provider. The <uicontrol>Use default authority</uicontrol> option, which is selected
+ by default, causes the New Android Content Provider wizard to do this for you. If you wish to specify a
+ different URI, or if you wish to specify additional URIs for this content provider, for each click <uicontrol
+ >Add</uicontrol> (next to <uicontrol>Authorities</uicontrol>) and enter the desired URI.</cmd>
+ </step>
+ <step>
+ <cmd>Click <uicontrol>Finish</uicontrol>.</cmd>
+ </step>
+ </steps>
+ <result>The content provider is created and added to the selected project. It is also opened in an editor
+ view.</result>
+ </taskbody>
+</task>
diff --git a/src/help/studio_help/src/topics/t_db-about.dita b/src/help/studio_help/src/topics/t_db-about.dita
new file mode 100644
index 0000000..64b38ac
--- /dev/null
+++ b/src/help/studio_help/src/topics/t_db-about.dita
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE task PUBLIC "-//OASIS//DTD DITA Task//EN" "../dtd/task.dtd">
+<task id="t_db-about" xml:lang="en-us">
+ <title>Exploring and manipulating Android databases</title>
+ <shortdesc>The MOTODEV Database Explorer perspective allows you to create and delete SQLite databases and database
+ tables, view and edit database table content, and import and export database table contents, all on a connected
+ developer handset or emulated device, within an Android project, or anywhere else accessible to your development
+ computer (such as on an SD card within a connected handset that is currently mounted for USB access). Note that for
+ security reasons you can only work with emulated devices or Android devices intended for development; you cannot
+ view or modify database contents on a production Android handset.</shortdesc>
+ <prolog>
+ <metadata>
+ <keywords>
+ <!--<indexterm></indexterm>-->
+ </keywords>
+ </metadata>
+ </prolog>
+ <taskbody>
+ <context>
+ <p>The MOTODEV Database Explorer view lists the following:</p>
+ <dl>
+ <dlentry>
+ <dt>Connected devices and running emulators</dt>
+ <dd>These are listed first in the MOTODEV Database Explorer view. Note that all Android AVDs are listed, not
+ just those that are running (AVDs listed with a disclosure triangle to the left of them are running; those
+ without a disclosure triangle are not runing). Connected devices are listed, but once they are disconnected
+ they are removed from the list. If the connected device or running emulator contains an SD card, disclosing
+ the device's applications also lists "External Storage"; you can map the databases on the device's SD card
+ for browsing or editing without having to mount the SD card for USB access. Note that non-development
+ handsets do not provide access to their databases. </dd>
+ </dlentry>
+ <dlentry>
+ <dt>Projects in your current workspace</dt>
+ <dd>Within the entry labeled "Workspace" are all of the Android projects found within your current workspace.
+ Those listed with a disclosure triangle to the left contain a database. Those without a disclosure triangle
+ do not currently contain a database (although you can add one).</dd>
+ </dlentry>
+ <dlentry>
+ <dt>Databases outside your current workspace that you have mapped</dt>
+ <dd>The "Filesystem" grouping in the MOTODEV Database Explorer view lists additional SQLite databases that you
+ have "mapped" into the view. This allows you to work with databases that are outside of your current
+ workspace. In particular, you can connect an Android handset to your development computer, mount its SD card
+ for USB access, and then map any databases on that SD card. You can then browse and edit those databases as
+ you would any other.</dd>
+ </dlentry>
+ </dl>
+ </context>
+ </taskbody>
+</task>
diff --git a/src/help/studio_help/src/topics/t_db-classes-create.dita b/src/help/studio_help/src/topics/t_db-classes-create.dita
new file mode 100644
index 0000000..d9c1447
--- /dev/null
+++ b/src/help/studio_help/src/topics/t_db-classes-create.dita
@@ -0,0 +1,64 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE task PUBLIC "-//OASIS//DTD DITA Task//EN" "../dtd/task.dtd">
+<task id="t_db-classes-create" xml:lang="en-us">
+ <title>Creating database management classes</title>
+ <shortdesc>Creates classes you can use to manage and access your application's database: content providers for each of
+ the database's tables, and a SQL open helper that can create and if desired initialize your database at runtime.</shortdesc>
+ <prolog>
+ <metadata>
+ <keywords>
+ <!--<indexterm></indexterm>-->
+ </keywords>
+ </metadata>
+ </prolog>
+ <taskbody>
+ <prereq>This feature creates classes based upon the schema of an existing database. Accordingly, the database for
+ which you want to create classes must already exist. Note that the tables need to have the desired structure, but
+ need not have any content.</prereq>
+ <steps>
+ <step>
+ <cmd>In the Package Explorer right-click the project into which the database management classes are to be added
+ and select <menucascade>
+ <uicontrol>MOTODEV Studio</uicontrol><uicontrol>Create Database Management Classes</uicontrol>
+ </menucascade>.</cmd>
+ <stepresult>The Database Management Classes dialog appears, with various fields specific to your project filled
+ in.</stepresult>
+ </step>
+ <step>
+ <cmd>Using the <uicontrol>Database File</uicontrol> field, specify the location of the database from which the
+ classes are to be generated.</cmd>
+ <choices>
+ <choice>If the database is already part of your project click <uicontrol>Workspace</uicontrol> and select your
+ project's database.</choice>
+ <choice>If the database is not part of your project, click <uicontrol>Filesystem</uicontrol> and specify the
+ database to be used. Note that if you select this option the database will be copied into the appropriate
+ location within your project.</choice>
+ </choices>
+ </step>
+ <step>
+ <cmd>An SQL Open Helper class ensures at runtime that there is a copy of your database in your application's
+ <filepath>/data/data/</filepath> directory, from where it can be accessed by your application; it is
+ particularly useful if you want your app to launch with a preinitialized database. If you would like to have a
+ SQL Open Helper class generated for your database, ensure that <uicontrol>Generate SQL Open Helper</uicontrol>
+ is selected and specify a name for the helper class in the <uicontrol>Name</uicontrol> field. Also indicate
+ the source folder into which the generated class is to be written using the <uicontrol>Source
+ folder</uicontrol> field, and the package of which the class is to be a member using the <uicontrol
+ >Package</uicontrol> field. If you do not want an SQL Open Helper, clear the <uicontrol>Generate SQL Open
+ Helper</uicontrol> option.</cmd>
+ </step>
+ <step>
+ <cmd>To generate content providers for each of your database's tables, ensure that <uicontrol>Generate content
+ providers for each table</uicontrol> is selected. Specify the source folder into which the generated classes
+ are to be written using the <uicontrol>Source Folder</uicontrol> field, and the package of which the classes
+ are to be a member using the <uicontrol>Package</uicontrol> field. Each generated content provider class will
+ be named for the table from which it was generated; the class name will have the form <i>table_name</i><codeph
+ >ContentProvider</codeph>. If the generated source file names conflict with existing files within the
+ specified source folder, the existing files will be overwritten unless you clear the <uicontrol>Overwrite if
+ it already exists</uicontrol> option.</cmd>
+ </step>
+ <step>
+ <cmd>Click <uicontrol>Finish</uicontrol>.</cmd>
+ </step>
+ </steps>
+ </taskbody>
+</task>
diff --git a/src/help/studio_help/src/topics/t_db-connecting.dita b/src/help/studio_help/src/topics/t_db-connecting.dita
new file mode 100644
index 0000000..cde8eb7
--- /dev/null
+++ b/src/help/studio_help/src/topics/t_db-connecting.dita
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE task PUBLIC "-//OASIS//DTD DITA Task//EN" "../dtd/task.dtd">
+<task id="t_db-connecting" xml:lang="en-us">
+ <title>Connecting to a database</title>
+ <shortdesc>Before you can work with the contents of a database, you must connect to it.</shortdesc>
+ <prolog>
+ <metadata>
+ <keywords>
+ <!--<indexterm></indexterm>-->
+ </keywords>
+ </metadata>
+ </prolog>
+ <taskbody><prereq>You perform this task from within the MOTODEV Database Explorer view. This view is part of the
+ MOTODEV Database perspective; switch to this perspective when working with Android databases.</prereq><steps>
+ <step>
+ <cmd>Locate the database within the MOTODEV Database Explorer view.</cmd>
+ <choices>
+ <choice>If the database is within an app on a connected handset or a running emulator, click the disclosure
+ triangle to the left of the device or emulator to see all of the applications with databases. Click the
+ disclosure triangle next to the application that controls the database you wish to work with.</choice>
+ <choice>If the database is on an SD card on a connected handset or a running emulator, and the SD card is not
+ mounted for USB access, click the disclosure triangle to the left of the device or emulator. At the top of
+ the list of applications is an entry for <uicontrol>External Storage</uicontrol>; right-click it and select
+ <uicontrol>Map Database</uicontrol>. In the dialog that appears, enter the path to the database. Note that
+ although you can browse and edit the contents of a mapped database, you cannot delete it.</choice>
+ <choice>If the database is part of a project in your current workspace, click the disclosure triangle next to
+ the word "Workspace" to expose all of the projects in your workspace. Then, click the disclosure triangle
+ next to the project that contains the database you wish to work with.</choice>
+ <choice>If the database is somewhere else accessible to your development computer (for instance, if it is on
+ an SD card within a connected device that is mounted for USB access), right-click the "Filesystem" entry in
+ the MOTODEV Database Explorer view and select <uicontrol>Map Database</uicontrol>. Using the dialog that
+ appears, browse to the desired SQLite database and click <uicontrol>Open</uicontrol>. Note that although you
+ can browse and edit the contents of a mapped database, you cannot delete it.</choice>
+ </choices>
+ </step>
+ <step>
+ <cmd>Double-click the grey database icon (<image href="../images/db-icon.png"/>) to connect it. When it is
+ connected, a copy of the database is transferred from the target device to your computer's Temp directory and
+ displayed as a node in the MOTODEV Database Explorer view.</cmd>
+ <info>
+ <note type="important">Changes made to a database with the MOTODEV Studio Table Editor are made to the local
+ copy in your Temp directory. MOTODEV Studio normally will keep the local database and the version on the
+ connected device or emulator in sync. If necessary you can manually synchronize the two either by
+ right-clicking the database file and selecting <uicontrol>Refresh</uicontrol> or selecting the database file
+ and pressing F5. Note that MOTODEV Studio does not perform a true synchronization operation; if the database
+ has changed on the device since you connected to it, you cannot merge your changes with the device database.
+ Instead, you either must override the device database with your Temp copy or abandon your changes.</note>
+ </info>
+ </step>
+ </steps><example>The following shows how the "alarms" database within the MOTODEV Database Explorer view looks when
+ it is disconnected and when it is connected. <image href="../images/db-connected-states.png" placement="break"
+ /></example></taskbody>
+</task>
diff --git a/src/help/studio_help/src/topics/t_db-creating.dita b/src/help/studio_help/src/topics/t_db-creating.dita
new file mode 100644
index 0000000..8c214ad
--- /dev/null
+++ b/src/help/studio_help/src/topics/t_db-creating.dita
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE task PUBLIC "-//OASIS//DTD DITA Task//EN" "../dtd/task.dtd">
+<task id="t_db-creating" xml:lang="en-us">
+ <title>Creating a database</title>
+ <shortdesc>From the MOTODEV Database Explorer view you can create new databases on a device or within an Android
+ project in your workspace.</shortdesc>
+ <prolog>
+ <metadata>
+ <keywords>
+ <!--<indexterm></indexterm>-->
+ </keywords>
+ </metadata>
+ </prolog>
+ <taskbody>
+ <prereq>You perform this task from within the MOTODEV Database Explorer view. This view is part of the MOTODEV
+ Database perspective; switch to this perspective when working with Android databases.</prereq>
+ <steps>
+ <step>
+ <cmd>In the MOTODEV Database Explorer view, right-click the app (under Workspace) or the APK on a connected
+ device or running emulator in which the database is to be created and select <uicontrol>Create
+ Database</uicontrol>.</cmd>
+ <info>
+ <note>The <uicontrol>Duplicate</uicontrol> command is not supported by MOTODEV Studio for Android, and cannot
+ be used to "clone" an existing database.</note>
+ </info>
+ <stepresult>You are prompted for a name for the new database.</stepresult>
+ </step>
+ <step>
+ <cmd>In the Database Name dialog, enter the name for your new database and click <uicontrol>OK</uicontrol>.</cmd>
+ <stepresult>The SQLite database is created. If you created it for a project in your current workspace, the
+ database file is created in the project's <filepath>assets</filepath> folder.</stepresult>
+ </step>
+ <step>
+ <cmd>In the MOTODEV Explorer view double-click the app to expose its contents, then double-click the grey
+ database icon (<image href="../images/db-icon.png"/>) to connect the database. When it is connected, a copy of
+ the database is transferred from the target app or device to your computer's Temp directory and displayed as a
+ node in the MOTODEV Database Explorer view.</cmd>
+ <info>
+ <note type="important">Changes made to a database using the MOTODEV Studio Table Editor are made to the local
+ copy in your Temp directory. MOTODEV Studio normally will keep the local database and the version on the
+ connected device or emulator in sync. If necessary you can manually synchronize the two either by
+ right-clicking the database file and selecting <uicontrol>Refresh</uicontrol> or selecting the database file
+ and pressing F5. Note that MOTODEV Studio does not perform a true synchronization operation; if the database
+ has changed on the device since you connected to it, you cannot merge your changes with the device database.
+ Instead, you either must override the device database with your Temp copy or abandon your changes.</note>
+ </info>
+ </step>
+ </steps>
+ <postreq>After creating a new database, you will likely want to create one or more tables within that
+ database.</postreq>
+ </taskbody>
+</task>
diff --git a/src/help/studio_help/src/topics/t_db-deleting.dita b/src/help/studio_help/src/topics/t_db-deleting.dita
new file mode 100644
index 0000000..5cca984
--- /dev/null
+++ b/src/help/studio_help/src/topics/t_db-deleting.dita
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE task PUBLIC "-//OASIS//DTD DITA Task//EN" "../dtd/task.dtd">
+<task id="t_db-deleting" xml:lang="en-us">
+ <title>Deleting a database</title>
+ <shortdesc>From the MOTODEV Database Explorer view you can delete device databases.</shortdesc>
+ <prolog>
+ <metadata>
+ <keywords>
+ <!--<indexterm></indexterm>-->
+ </keywords>
+ </metadata>
+ </prolog>
+ <taskbody>
+ <prereq>You perform this task from within the MOTODEV Database Explorer view. This view is part of the MOTODEV
+ Database perspective; switch to this perspective when working with Android databases.</prereq>
+ <steps>
+ <step>
+ <cmd>In the MOTODEV Database Explorer view, right-click the database to be deleted and select <uicontrol
+ >Delete</uicontrol>.</cmd>
+ <stepresult>You are asked to confirm that you want to delete the database.</stepresult>
+ </step>
+ <step>
+ <cmd>In the Drop Database(s) dialog, click <uicontrol>Yes</uicontrol>.</cmd>
+ </step>
+ </steps>
+ </taskbody>
+</task>
diff --git a/src/help/studio_help/src/topics/t_db-table-creating.dita b/src/help/studio_help/src/topics/t_db-table-creating.dita
new file mode 100644
index 0000000..757d7af
--- /dev/null
+++ b/src/help/studio_help/src/topics/t_db-table-creating.dita
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE task PUBLIC "-//OASIS//DTD DITA Task//EN" "../dtd/task.dtd">
+<task id="t_db-table-creating" xml:lang="en-us">
+ <title>Creating a database table</title>
+ <shortdesc>From the MOTODEV Database Explorer view you can create new tables within SQLite databases.</shortdesc>
+ <prolog>
+ <metadata>
+ <keywords>
+ <!--<indexterm></indexterm>-->
+ </keywords>
+ </metadata>
+ </prolog>
+ <taskbody>
+ <prereq>You perform this task from within the MOTODEV Database Explorer view. This view is part of the MOTODEV
+ Database perspective; switch to this perspective when working with Android databases.</prereq>
+ <steps>
+ <step>
+ <cmd>Ensure that you are connected to the database.</cmd>
+ </step>
+ <step>
+ <cmd>Right-click the connected database file and select <uicontrol>Create Table</uicontrol> from the menu that
+ appears.</cmd>
+ <stepresult>The Create New Table dialog appears.</stepresult>
+ </step>
+ <step>
+ <cmd>Supply a name for the new table using the <uicontrol>Table Name</uicontrol> field.</cmd>
+ </step>
+ <step>
+ <cmd>Add columns to your table (you must create at least one). For each:</cmd>
+ <substeps>
+ <substep>
+ <cmd>Click <uicontrol>Add</uicontrol>.</cmd>
+ <stepresult>The Add/Edit Field dialog appears</stepresult>
+ </substep>
+ <substep>
+ <cmd>Supply a name for the table column using the <uicontrol>Name</uicontrol> field.</cmd>
+ </substep>
+ <substep>
+ <cmd>If the column will contain a primary key, select <uicontrol>Primary key</uicontrol> and specify any
+ automatic key behavior. </cmd>
+ </substep>
+ <substep>
+ <cmd>Ensure that the column type is correct. If necessary, select a different type.</cmd>
+ </substep>
+ <substep>
+ <cmd>If the table column should have a default value, specify it using the <uicontrol>Default
+ Value</uicontrol> field.</cmd>
+ </substep>
+ </substeps>
+ </step>
+ <step>
+ <cmd>Once you have specified all of the needed table columns, click <uicontrol>Finish</uicontrol> in the Create
+ New Table dialog.</cmd>
+ </step>
+ </steps>
+ <result> The table is created and added to the database. </result>
+ <postreq>
+ <note type="important">If you create the new table within a database on a connected device or running emulator,
+ you may need to refresh the database (press F5, or right-click the database and select <uicontrol
+ >Refresh</uicontrol>) in order for the change to be reflected on the device.</note>
+ </postreq>
+ </taskbody>
+</task>
diff --git a/src/help/studio_help/src/topics/t_db-table-deleting.dita b/src/help/studio_help/src/topics/t_db-table-deleting.dita
new file mode 100644
index 0000000..2564e06
--- /dev/null
+++ b/src/help/studio_help/src/topics/t_db-table-deleting.dita
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE task PUBLIC "-//OASIS//DTD DITA Task//EN" "../dtd/task.dtd">
+<task id="t_db-table-deleting" xml:lang="en-us">
+ <title>Deleting a database table</title>
+ <shortdesc>From the MOTODEV Database Explorer view you can delete device database tables.</shortdesc>
+ <prolog>
+ <metadata>
+ <keywords>
+ <!--<indexterm></indexterm>-->
+ </keywords>
+ </metadata>
+ </prolog>
+ <taskbody>
+ <prereq>You perform this task from within the MOTODEV Database Explorer view. This view is part of the MOTODEV
+ Database perspective; switch to this perspective when working with Android databases.</prereq>
+ <steps>
+ <step>
+ <cmd>In the MOTODEV Database Explorer view, right-click the database table to be deleted and select <uicontrol
+ >Delete Table</uicontrol>.</cmd>
+ <stepresult>The selected database table is immediately deleted.</stepresult>
+ </step>
+ </steps>
+ </taskbody>
+</task>
diff --git a/src/help/studio_help/src/topics/t_db-table-editing.dita b/src/help/studio_help/src/topics/t_db-table-editing.dita
new file mode 100644
index 0000000..a90e8c5
--- /dev/null
+++ b/src/help/studio_help/src/topics/t_db-table-editing.dita
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE task PUBLIC "-//OASIS//DTD DITA Task//EN" "../dtd/task.dtd">
+<task id="t_db-table-editing" xml:lang="en-us">
+ <title>Exploring or editing a database table</title>
+ <shortdesc>MOTODEV Studio for Android lets you easily view and manipulate the contents of an SQLite database table in
+ spreadsheet form.</shortdesc>
+ <prolog>
+ <metadata>
+ <keywords>
+ <!--<indexterm></indexterm>-->
+ </keywords>
+ </metadata>
+ </prolog>
+ <taskbody>
+ <prereq>You perform this task from within the MOTODEV Database Explorer view. This view is part of the MOTODEV
+ Database perspective; switch to this perspective when working with Android databases.</prereq>
+ <steps>
+ <step>
+ <cmd>Ensure that you are connected to the database.</cmd>
+ </step>
+ <step>
+ <cmd>Within the MOTODEV Database Explorer view, use the dislosure triangles to expose the tables within your
+ database.</cmd>
+ <stepxmp>The following image shows the tables that make up the "alarms" database:<image
+ href="../images/db-tables.png" placement="break"/></stepxmp>
+ </step>
+ <step>
+ <cmd>Right-click the table you want to edit or explore, and select <uicontrol>Browse table contents</uicontrol>.</cmd>
+ <stepresult>A view appears showing the table contents, with columns representing the fields in a record, and
+ rows representing the table records.</stepresult>
+ </step>
+ <step>
+ <cmd>To change the contents of a record, simply click within the cell to be changed and edit its contents. To
+ add a new row, simply enter the new row's data in the row marked <uicontrol>&lt;new row></uicontrol>. To
+ perform other operations, right-click to get a context-sensitive menu that will allow you to:</cmd>
+ <choices>
+ <choice>Set the current cell's value to NULL.</choice>
+ <choice>Insert a new row at the bottom of the table.</choice>
+ <choice>Delete the current row.</choice>
+ <choice>Refresh the table. This will load the table's contents from the local filesystem, potentially
+ overwriting any changes you have made. You will be given an opportunity to first save any changes you have
+ made. To reload data from the device you must disconnect and reconnect.</choice>
+ <choice>Revert to the last saved version of the table. This will also re-load the table's contents from the
+ local filesystem, overwriting any changes you have made. You are not given an opportunity to save any
+ changes first. To reload data from the device you must disconnect and reconnect.</choice>
+ </choices>
+ </step>
+ <step>
+ <cmd>If you have made any changes, save them by selecting <menucascade><uicontrol>File</uicontrol><uicontrol
+ >Save</uicontrol></menucascade>.</cmd>
+ <info>
+ <note type="important">Changes made to a database on a connected device or running emulator with the MOTODEV
+ Studio Table Editor are made to the local copy in your Temp directory. To have those changes reflected on
+ the device, you must save your changes (<menucascade><uicontrol>File</uicontrol><uicontrol
+ >Save</uicontrol></menucascade>). Note that if the database has changed on the device since you connected to
+ it, you cannot merge your changes with the device database; you either must override the device database
+ with your Temp copy or abandon your changes.</note>
+ </info>
+ </step>
+ </steps>
+ </taskbody>
+</task>
diff --git a/src/help/studio_help/src/topics/t_device-creating_android.dita b/src/help/studio_help/src/topics/t_device-creating_android.dita
new file mode 100644
index 0000000..cfa739a
--- /dev/null
+++ b/src/help/studio_help/src/topics/t_device-creating_android.dita
@@ -0,0 +1,124 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE task
+ PUBLIC "-//OASIS//DTD DITA Task//EN" "../dtd/task.dtd">
+<task xml:lang="en-us" id="t_device-creating_android">
+ <title>Creating an Android Virtual Device (AVD)</title>
+ <shortdesc>Although MOTODEV Studio for Android recognizes Android Virtual Devices (AVDs) created from the command
+ line, it is much simpler to create them from within MOTODEV Studio for Android.</shortdesc>
+ <prolog>
+ <metadata>
+ <keywords>
+ <!--<indexterm></indexterm>-->
+ </keywords>
+ </metadata>
+ </prolog>
+ <taskbody>
+ <steps>
+ <step>
+ <cmd>Click the <uicontrol>Device Management</uicontrol> tab, if necessary, to bring forward the Device
+ Management view.</cmd>
+ </step>
+ <step>
+ <cmd>Click the New Device button (<image href="../images/tml-new-instance.png"/>) and select <uicontrol>Android
+ Virtual Device</uicontrol>.</cmd>
+ <stepresult>The Create a New Device dialog appears.</stepresult>
+ </step>
+ <step>
+ <cmd>Supply a name for your device instance and click <uicontrol>Next</uicontrol>. </cmd>
+ <info>Note that this name must not be in use by an existing AVD listed in the Device Management view.</info>
+ </step>
+ <step>
+ <cmd>Verify or supply the information found on the Main tab: </cmd>
+ <substeps>
+ <substep>
+ <cmd><uicontrol>AVD Target</uicontrol>: the system image the AVD is to use.</cmd>
+ </substep>
+ <substep>
+ <cmd><uicontrol>AVD Skin</uicontrol>: the screen resolution and orientation. For instance, "HVGA-L" causes
+ the AVD to use an HVGA display (480x320 pixels) in landscape orientation. "QVGA-P" indicates a QVGA
+ display (320x240 pixels) in portrait orientation. </cmd>
+ </substep>
+ <substep>
+ <cmd><uicontrol>ABI Type</uicontrol>: Application Binary Interface (ABI) type. For all current
+ Android-powered devices, this should be "ARM (armeabi)".</cmd>
+ </substep>
+ <substep>
+ <cmd><uicontrol>AVD Path</uicontrol>: Use this field if you want to specify a non-default location for the
+ AVD directory that will be constructed for this virtual device. Leave <uicontrol>Use default</uicontrol>
+ selected if the default location is acceptable. Otherwise, clear the <uicontrol>Use default</uicontrol>
+ option and specify your desired directory location. </cmd>
+ <info>
+ <note>Storing your AVD on a remote location on a network file system will result in decreased performance.
+ For best results, store your AVDs on the local file system. Note that on Linux hosts your user home
+ folder may be on a remote file server; in such cases storing the AVD in your home folder will cause
+ performance to suffer.</note>
+ </info>
+ </substep>
+ <substep>
+ <cmd><uicontrol>SD Card</uicontrol>: Allows you to specify whether the emulated device has an emulated SD
+ card. Select <uicontrol>None</uicontrol> if you don't need or want the emulated device to have an SD card.
+ To use an existing SD card image (a .img file), select <uicontrol>Existing</uicontrol> and then specify
+ the path to the card image file. To create a new SD card, select <uicontrol>New</uicontrol>, enter a
+ numeric value in the field that follows, and specify the units for that value (KB or MB).</cmd>
+ </substep>
+ <substep>
+ <cmd><uicontrol>Proxy Settings</uicontrol>: Select this option if you want the emulated device to use the
+ same network settings that Eclipse is configured to use. If your network requires the use of a proxy, for
+ instance, and Eclipse is configured to use that proxy, selecting this option causes the emulated device to
+ use that same proxy for its network communications.</cmd>
+ </substep>
+ <substep>
+ <cmd><uicontrol>Snapshot Settings</uicontrol>: Enables the capture and use of a "snapshot"--a copy of the
+ emulator's memory--to speed the emulator startup process (subsequent to the first startup; the initial
+ startup will proceed at the normal speed, but after a snapshot has been taken subsequent startups can be
+ based on that snapshot, bypassing much of the startup process). <uicontrol>Enable Snapshot</uicontrol>
+ enables snapshots, and activates the other snapshot options. <uicontrol>Launch emulator from
+ snapshot</uicontrol> indicates that the most recent snapshot should be used whenever the emulator is
+ launched. <uicontrol>Save to snapshot on exit</uicontrol> takes a snapshot--and thus records the current
+ state of the emulator--whenever the emulator is shut down.</cmd>
+ </substep>
+ <substep>
+ <cmd><uicontrol>Internal Emulator Window</uicontrol> (not available on Mac OS X): Leave <uicontrol>Show the
+ Native Emulator Window within an Eclipse View (Recommended)</uicontrol> selected unless you know that
+ the native option does not work correctly on your development machine (in which case you should select
+ <uicontrol>Use VNC to show the Emulator within an Eclipse View</uicontrol>). Note that if necessary you
+ can later change this setting for an existing AVD by editing its properties.</cmd>
+ </substep>
+ <substep>
+ <cmd><uicontrol>Timeout (sec)</uicontrol>: Use this field to specify a timeout period after which, if the
+ emulator has not finished starting, the emulator should be shut down. Typically this is set to two minutes
+ (120 seconds).</cmd>
+ </substep>
+ </substeps>
+ <info>Note that when creating an AVD, the name, target, skin, and path values cannot later be changed.</info>
+ </step>
+ <step>
+ <cmd>Click <uicontrol>Finish</uicontrol> if you don't need to further customize the AVD. Otherwise, click
+ <uicontrol>Next</uicontrol>.</cmd>
+ </step>
+ <step>
+ <cmd>Specify any additional options using the <uicontrol>User Interface</uicontrol>, <uicontrol>Disk
+ Images</uicontrol>, <uicontrol>Network</uicontrol>, <uicontrol>System</uicontrol>, and <uicontrol
+ >Others</uicontrol> tabs. Note that only the more commonly-used AVD options are presented on these tabs; if
+ you need to specify additional options, enter them into the field on the <uicontrol>Others</uicontrol> tab,
+ command-line style.</cmd>
+ <info>
+ <p>For a description of each of the startup options displayed on the various tabs, see <xref
+ href="u_new-device-startup_android.dita"/>. For all of the possible command-line arguments, see <xref
+ href="http://d.android.com/guide/developing/tools/emulator.html#startup-options" format="html"
+ scope="external">http://d.android.com/guide/developing/tools/emulator.html#startup-options</xref>.</p>
+ <p>Except on Mac OS X, to work with the emulator in a separate window simply close the Android Emulator view
+ and click <uicontrol>No</uicontrol> when you are asked if running emulator instances should be stopped.
+ Opening the Android Emulator view while the emulator is running externally causes the emulator to be shown
+ in the view. On Mac OS X, things work somewhat differently: see <xref href="t_emulator-external.dita"/> for
+ instructions.</p>
+ </info>
+ </step>
+ <step>
+ <cmd>Click <uicontrol>Finish</uicontrol>. </cmd>
+ </step>
+ </steps>
+ <result>The AVD is created and listed under <uicontrol>Android Virtual Device</uicontrol> in the Device Management
+ view.</result>
+ </taskbody>
+</task>
diff --git a/src/help/studio_help/src/topics/t_device-editing_android.dita b/src/help/studio_help/src/topics/t_device-editing_android.dita
new file mode 100644
index 0000000..3990c27
--- /dev/null
+++ b/src/help/studio_help/src/topics/t_device-editing_android.dita
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE task
+ PUBLIC "-//OASIS//DTD DITA Task//EN" "../dtd/task.dtd">
+<task xml:lang="en-us" id="t_device-editing_android">
+ <title>Editing or deleting an Android Virtual Device (AVD)</title>
+ <prolog>
+ <metadata>
+ <keywords>
+ <!--<indexterm></indexterm>-->
+ </keywords>
+ </metadata>
+ </prolog>
+ <taskbody>
+ <context>
+ <p>To delete an existing AVD, ensure that it is not running (select it in the Device Management view and click
+ <uicontrol>Stop</uicontrol> if necessary), then right-click it in the Device Management view and select
+ <uicontrol>Delete</uicontrol>.</p>
+ <p>To edit an existing AVD:</p>
+ </context>
+ <steps>
+ <step>
+ <cmd>Click the <uicontrol>Device Management</uicontrol> tab, if necessary, to bring forward the Device
+ Management view.</cmd>
+ </step>
+ <step>
+ <cmd>Right-click the AVD and select <uicontrol>Properties</uicontrol>.</cmd>
+ <stepresult>The Android Virtual Device properties dialog appears.</stepresult>
+ </step>
+ <step>
+ <cmd>To alter the timeout, click Android Virtual Device in the left part of the dialog, and then change the
+ timeout value in the field on the right.</cmd>
+ <info>Note that you cannot change the name, target, skin, or path for an existing AVD.</info>
+ </step>
+ <step>
+ <cmd>Click <uicontrol>Startup Options</uicontrol> on the left side of the dialog.</cmd>
+ </step>
+ <step>
+ <cmd>Alter the AVD's startup options using the <uicontrol>User Interface</uicontrol>, <uicontrol>Disk
+ Images</uicontrol>, <uicontrol>Network</uicontrol>, <uicontrol>System</uicontrol>, and <uicontrol
+ >Others</uicontrol> tabs. Note that only the more commonly-used AVD options are presented on these tabs; if
+ you need to specify additional options, enter them into the field on the <uicontrol>Others</uicontrol> tab,
+ command-line style.</cmd>
+ <info>
+ <p>For a description of each of the startup options displayed on the various tabs, see <xref
+ href="u_new-device-startup_android.dita"/>. For all of the possible command-line arguments, see <xref
+ href="http://d.android.com/guide/developing/tools/emulator.html#startup-options" format="html"
+ scope="external">http://d.android.com/guide/developing/tools/emulator.html#startup-options</xref>.</p>
+ <p>Except on Mac OS X, to work with the emulator in a separate window simply close the Android Emulator view
+ and click <uicontrol>No</uicontrol> when you are asked if running emulator instances should be stopped.
+ Opening the Android Emulator view while the emulator is running externally causes the emulator to be shown
+ in the view. On Mac OS X, things work somewhat differently: see <xref href="t_emulator-external.dita"/> for
+ instructions.</p>
+ </info>
+ </step>
+ <step>
+ <cmd>Click <uicontrol>OK</uicontrol>. </cmd>
+ </step>
+ </steps>
+ <result>Your changes are saved and the dialog is closed.</result>
+ </taskbody>
+</task>
diff --git a/src/help/studio_help/src/topics/t_device-screen-capturing.dita b/src/help/studio_help/src/topics/t_device-screen-capturing.dita
new file mode 100644
index 0000000..95356de
--- /dev/null
+++ b/src/help/studio_help/src/topics/t_device-screen-capturing.dita
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE task PUBLIC "-//OASIS//DTD DITA Task//EN" "../dtd/task.dtd">
+<task id="t_device-screen-capturing" xml:lang="en-us">
+ <title>Capturing the device screen</title>
+ <shortdesc>MOTODEV Studio allows you to easily capture an image of the screen on a real or emulated device.</shortdesc>
+ <prolog>
+ <metadata>
+ <keywords>
+ <!--<indexterm></indexterm>-->
+ </keywords>
+ </metadata>
+ </prolog>
+ <taskbody>
+ <prereq>If capturing the screen on a physical device, that device must be connected to your development computer and
+ listed in MOTODEV Studio's Device Management view (under <uicontrol>Android Handset</uicontrol>). If capturing the
+ screen of an emulated device, the AVD must be running and listed in the Device Management view under <uicontrol
+ >Android Virtual Device</uicontrol>. </prereq>
+ <steps>
+ <step>
+ <cmd>Right-click the device in the Device Management view and select <uicontrol>Take Screenshot</uicontrol>.</cmd>
+ <stepresult>The Device Screen Capture dialog opens, showing the snapshot.</stepresult>
+ </step>
+ <step>
+ <cmd>To save the captured image as a PNG file, click <uicontrol>Save</uicontrol>. To copy the captured image to
+ your computer's clipboard, click <uicontrol>Copy</uicontrol>. </cmd>
+ </step>
+ <step>
+ <cmd>Click <uicontrol>Done</uicontrol> or simply close the Device Screen Capture dialog when done.</cmd>
+ </step>
+ </steps>
+ <postreq>To capture a different screen while the Device Screen Capture dialog is open, manipulate the device to
+ display the screen you want to capture, and then click <uicontrol>Refresh</uicontrol>. </postreq>
+ </taskbody>
+</task>
diff --git a/src/help/studio_help/src/topics/t_device-starting_android.dita b/src/help/studio_help/src/topics/t_device-starting_android.dita
new file mode 100644
index 0000000..9824f24
--- /dev/null
+++ b/src/help/studio_help/src/topics/t_device-starting_android.dita
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE task
+ PUBLIC "-//OASIS//DTD DITA Task//EN" "../dtd/task.dtd">
+<task xml:lang="en-us" id="t_device-starting_android">
+ <title>Starting, stopping, and resetting an Android Virtual Device (AVD)</title>
+ <shortdesc/>
+ <prolog>
+ <metadata>
+ <keywords>
+ <!--<indexterm></indexterm>-->
+ </keywords>
+ </metadata>
+ </prolog>
+ <taskbody>
+ <prereq>You must have an existing AVD, listed in the Device Management view. </prereq>
+ <steps>
+ <step>
+ <cmd>Click the <uicontrol>Device Management</uicontrol> tab, if necessary, to bring forward the Device
+ Management view.</cmd>
+ </step>
+ <step>
+ <cmd>Double-click an offline AVD to start it. Similarly, double-click an online AVD to stop it. Or, in the
+ Device Management view, right-click the AVD you wish to start, stop, or reset and select the desired operation
+ from the menu that appears. </cmd>
+ <info>Note that when starting or resetting an AVD, it must not already be running. Also note that if you launch one of your Android projects using a run configuration that targets an AVD, and
+ that AVD is not yet running, it will be started automatically for you.</info>
+ </step>
+ </steps><postreq>You can also start, stop, or reset multiple AVDs at one time by selecting the AVDs and clicking the appropriate command icon at the top of the Device Management View.</postreq>
+ </taskbody>
+</task>
diff --git a/src/help/studio_help/src/topics/t_device-state-managing.dita b/src/help/studio_help/src/topics/t_device-state-managing.dita
new file mode 100644
index 0000000..0820928
--- /dev/null
+++ b/src/help/studio_help/src/topics/t_device-state-managing.dita
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE task
+ PUBLIC "-//OASIS//DTD DITA Task//EN" "../dtd/task.dtd">
+<task xml:lang="en-us" id="t_device-state-managing">
+ <title>Managing device state</title>
+ <shortdesc>Issue Android Debug Bridge (adb) commands to manage the state of real or emulated devices.</shortdesc>
+ <prolog>
+ <metadata>
+ <keywords>
+ <!--<indexterm></indexterm>-->
+ </keywords>
+ </metadata>
+ </prolog>
+ <taskbody>
+ <steps>
+ <step>
+ <cmd>Switch to the Device Management view.</cmd>
+ </step>
+ <step>
+ <cmd>Right-click an Android handset or running AVD.</cmd>
+ <info>The AVD must be running; start if, if necessary.</info>
+ </step>
+ <step>
+ <cmd>From the menu that appears, select <uicontrol>ADB Shell</uicontrol>.</cmd>
+ <stepresult>The Console view is opened or brought forward, and an adb shell console for the selected device is
+ displayed. Use it to issue adb shell commands. </stepresult>
+ </step>
+ </steps>
+ <postreq>Note that you can switch the Console view between various consoles by clicking <image
+ href="../images/display-selected-console-button.png"/> (Display Selected Console).</postreq>
+ </taskbody>
+</task>
diff --git a/src/help/studio_help/src/topics/t_emulator-controlling.dita b/src/help/studio_help/src/topics/t_emulator-controlling.dita
new file mode 100644
index 0000000..e8342be
--- /dev/null
+++ b/src/help/studio_help/src/topics/t_emulator-controlling.dita
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE task
+ PUBLIC "-//OASIS//DTD DITA Task//EN" "../dtd/task.dtd">
+<task xml:lang="en-us" id="t_emulator-controlling">
+ <title>Controlling an emulated device</title>
+ <shortdesc>Opens a new Emulator Console connected to the selected AVD instance. You can use the Emulator Console to
+ simulate hardware events, simulate telephony events, and control various aspects of the emulated device.</shortdesc>
+ <prolog>
+ <metadata>
+ <keywords>
+ <!--<indexterm></indexterm>-->
+ </keywords>
+ </metadata>
+ </prolog>
+ <taskbody>
+ <steps>
+ <step>
+ <cmd>Switch to the Device Management view.</cmd>
+ </step>
+ <step>
+ <cmd>Right-click a running AVD.</cmd>
+ <info>The AVD must be running; start if, if necessary.</info>
+ </step>
+ <step>
+ <cmd>From the menu that appears, select <uicontrol>Console</uicontrol>.</cmd>
+ <stepresult>The Console view is opened or brought forward, and an Emulator Console connected to the selected
+ device is displayed. Use it to issue Emulator Console commands. </stepresult>
+ </step>
+ </steps>
+ <postreq>Note that you can switch the Console view between various consoles by clicking <image
+ href="../images/display-selected-console-button.png"/> (Display Selected Console).</postreq>
+ </taskbody>
+</task>
diff --git a/src/help/studio_help/src/topics/t_emulator-external.dita b/src/help/studio_help/src/topics/t_emulator-external.dita
new file mode 100644
index 0000000..a4d837d
--- /dev/null
+++ b/src/help/studio_help/src/topics/t_emulator-external.dita
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE task PUBLIC "-//OASIS//DTD DITA Task//EN" "../dtd/task.dtd">
+<task id="t_emulator_external" xml:lang="en-us">
+ <title>Opening the emulator in an external window</title>
+ <shortdesc>MOTODEV Studio normally presents emulated devices in the Android Emulator view. If you prefer, though,
+ emulated devices can be displayed in a window external to MOTODEV Studio.</shortdesc>
+ <prolog>
+ <metadata>
+ <keywords>
+ <!--<indexterm></indexterm>-->
+ </keywords>
+ </metadata>
+ </prolog>
+ <taskbody>
+ <context>
+ <p>By default, MOTODEV Studio for Android opens the device emulator in an Eclipse view. Except on Mac OS X, to
+ work with the emulator in a separate window simply close the Android Emulator view and click <uicontrol
+ >No</uicontrol> when you are asked if running emulator instances should be stopped. The reverse works as you
+ might expect: while the emulator is running externally if you open the Android Emulator view the emulator will
+ be shown in that view (rather than externally).</p>
+ <p>On Mac OS X, things work a bit differently. Here, each AVD has an associated property that controls whether or
+ not the emulator is shown externally. In addition, if the Android Emulator view is open, the emulator is shown
+ there. This means that you can show the emulator in a view, or externally, or in both places at once, or in
+ neither place. </p>
+ <p><b>The remainder of this document assumes that you are running Mac OS X.</b>
+ </p>
+ <p>AVDs are presented within the Android Emulator view by default. To specify that an AVD be opened externally at
+ the time you create it: click <uicontrol>Next</uicontrol> after filling out the dialog asking for the AVD target
+ and skin. In the dialog that follows (the one in which you set startup options for the AVD), on the <uicontrol
+ >Others</uicontrol> tab, remove the <uicontrol>-no-window</uicontrol> option.</p>
+ <p>To alter an existing AVD so that it opens externally:</p>
+ </context>
+ <steps>
+ <step>
+ <cmd>Right-click the AVD and select <uicontrol>Properties</uicontrol>.</cmd>
+ <stepresult>The Properties dialog appears.</stepresult>
+ </step>
+ <step>
+ <cmd>In the Properties dialog, select <menucascade><uicontrol>Android Virtual Device</uicontrol><uicontrol
+ >Startup Options</uicontrol></menucascade>.</cmd>
+ </step>
+ <step>
+ <cmd>In the Startup Options dialog, on the <uicontrol>Others</uicontrol> tab, clear the <uicontrol
+ >-no-window</uicontrol> option.</cmd>
+ </step>
+ <step>
+ <cmd>Click <uicontrol>OK</uicontrol> to save your changes and close the dialog</cmd>
+ <info>
+ <note type="important">If the AVD is running at the time you make this change, you will need to stop and
+ restart the AVD.</note>
+ </info>
+ </step>
+ <step>
+ <cmd>If you do not wish to also see the emulator presented in the Android Emulator view, close that view. </cmd>
+ </step>
+ </steps>
+ <postreq>Note that if the AVD has the <uicontrol>-no-window</uicontrol> option set and you elect not to have the
+ emulator presented within an Eclipse view, the AVD will not be displayed. Conversely, if you leave the <uicontrol
+ >-no-window</uicontrol> option set and you also specify that the emulator is to be displayed within an Eclipse
+ view, it will appear both within Eclipse and in a separate window. </postreq>
+ </taskbody>
+</task>
diff --git a/src/help/studio_help/src/topics/t_emulator-language-changing.dita b/src/help/studio_help/src/topics/t_emulator-language-changing.dita
new file mode 100644
index 0000000..e288ae9
--- /dev/null
+++ b/src/help/studio_help/src/topics/t_emulator-language-changing.dita
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE task PUBLIC "-//OASIS//DTD DITA Task//EN" "../dtd/task.dtd">
+<task id="t_emulator-language-changing" xml:lang="en-us">
+ <title>Changing the emulator's operating language</title>
+ <shortdesc>From the Device Management view you can change the locale of a running emulator, and thus its operating
+ language.</shortdesc>
+ <prolog>
+ <metadata>
+ <keywords>
+ <!--<indexterm></indexterm>-->
+ </keywords>
+ </metadata>
+ </prolog>
+ <taskbody>
+ <context>
+ <p>You have two options for changing the operating language of a running emulator: either use the <uicontrol
+ >Custom Locale</uicontrol> application (found on the Application tab of the emulated device), or change the
+ emulator settings using the appropriate adb commands. MOTODEV Studio for Android simplifies this latter
+ operation, allowing you to easily change the emulator's locale from the Device Management view. Note that the
+ emulator will be restarted if you choose this option.</p>
+ </context>
+ <steps>
+ <step>
+ <cmd>In the Device Management view, right-click the running emulator and select <uicontrol>Language</uicontrol>.</cmd>
+ <stepresult>The Emulator Language dialog appears.</stepresult>
+ </step>
+ <step>
+ <cmd>Specify the desired locale as a combination of language and country. For example, to configure the emulator
+ to appear as it would in England, select "English" as the language and "United Kingdom" as the country.</cmd>
+ </step>
+ <step>
+ <cmd>Click <uicontrol>Finish</uicontrol>.</cmd>
+ </step>
+ </steps>
+ <result>MOTODEV Studio for Android issues the appropriate <codeph>setprop persist</codeph> adb shell commands, and
+ then restarts the emulator.</result>
+ </taskbody>
+</task>
diff --git a/src/help/studio_help/src/topics/t_emulator-view-closing.dita b/src/help/studio_help/src/topics/t_emulator-view-closing.dita
new file mode 100644
index 0000000..a5515b9
--- /dev/null
+++ b/src/help/studio_help/src/topics/t_emulator-view-closing.dita
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE task
+ PUBLIC "-//OASIS//DTD DITA Task//EN" "../dtd/task.dtd">
+<task xml:lang="en-us" id="t_emulator-view-closing">
+ <title>Closing the Android Emulator view</title>
+ <prolog>
+ <metadata>
+ <keywords>
+ <!--<indexterm></indexterm>-->
+ </keywords>
+ </metadata>
+ </prolog>
+ <taskbody>
+ <context>
+ <p>To close the Android Emulator view, click the white "X" in the view's tab. If the view was displaying at least
+ one running emulator, and no other view is still displaying that emulator, you will be asked if the running
+ Android emulator instances should be stopped as well. Click <uicontrol>Yes</uicontrol> if you want to stop all
+ of them. Click <uicontrol>No</uicontrol> to leave them all running. Note that emulators can continue to run even
+ though they aren't displayed in an Eclipse view or in an external window; simply re-open the Android Emulator
+ view to once again view and interact with the running emulators.</p>
+ <p>The Device Management view shows which emulators are currently running (the Status column displays "Online" for
+ each running emulator). You can stop individual emulators from this view, even if they are not displayed in the
+ Android Emulator view. Note that stopping all emulator instances does not cause the Android Emulator view to
+ close automatically.</p>
+ </context>
+ </taskbody>
+</task>
diff --git a/src/help/studio_help/src/topics/t_emulator-view-manipulating.dita b/src/help/studio_help/src/topics/t_emulator-view-manipulating.dita
new file mode 100644
index 0000000..4b7ce86
--- /dev/null
+++ b/src/help/studio_help/src/topics/t_emulator-view-manipulating.dita
@@ -0,0 +1,76 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE task
+ PUBLIC "-//OASIS//DTD DITA Task//EN" "../dtd/task.dtd">
+<task xml:lang="en-us" id="t_emulator-view-manipulating">
+ <title>Interacting with the Android emulator</title>
+ <prolog>
+ <metadata>
+ <keywords>
+ <!--<indexterm></indexterm>-->
+ </keywords>
+ </metadata>
+ </prolog>
+ <task id="t_select_running_emulator">
+ <title>Selecting a running emulator</title>
+ <shortdesc>All running emulators are displayed within the Android Emulator view. Tabs are used to identify the
+ emulators and select between them.</shortdesc>
+ </task>
+ <task id="t_emulator_image_size">
+ <title>Changing the image size</title>
+ <shortdesc>Within the Android Emulator view you can enlarge or shrink the size of the emulated handset or emulated
+ display currently being displayed.</shortdesc>
+ <taskbody>
+ <steps-unordered>
+ <step>
+ <cmd>To "zoom in" on the current image, click <image href="../images/emulator-zoom-in.png"/></cmd>
+ </step>
+ <step>
+ <cmd>To "zoom out" and show more of the current image, click <image href="../images/emulator-zoom-out.png"
+ /></cmd>
+ </step>
+ <step>
+ <cmd>To change the image to a specific size, click menu (<image href="../images/menu-button.png"/>), and then
+ select <uicontrol>Zoom</uicontrol>. From the menu that appears, select one of the relative sizes (25%, 50%,
+ 75%, 100%), or <uicontrol>Fit to Window</uicontrol>, which sizes the image so that it fits completely within
+ the view.</cmd>
+ </step>
+ </steps-unordered>
+ </taskbody>
+ </task>
+ <task id="t_emulator_change_orientation">
+ <title>Changing the emulator orientation</title>
+ <shortdesc>Click the handset image (<image href="../images/emulator-switch-layout.png"/>) at the top of the view to
+ toggle the orientation of the emulated device that is currently visible between portrait and landscape. You can
+ also click the small black triangle next to the handset image and then select <uicontrol>portrait</uicontrol> or
+ <uicontrol>landscape</uicontrol>. Note that other running emulators are not affected.</shortdesc>
+ </task>
+ <task id="t_device_interact">
+ <title>Interacting with the emulated device</title>
+ <shortdesc>From within the Android Emulator view you can enter data, click device keys, and use your mouse to
+ simulate tapping on the screen. Many of the keys on your development computer's keyboard map to the emulated
+ device's keyboard, making it easy to enter textual data.</shortdesc>
+ </task>
+ <!--
+ <task id="t_emulator_change_locale">
+ <title>Changing the emulated device's locale</title>
+ <shortdesc>From the Device Management view, you can change the locale of an emulated device. This feature is
+ particularly useful when testing localized versions of your applications.</shortdesc>
+ <taskbody>
+ <steps>
+ <step>
+ <cmd>In the Device Management view, right-click the emulated device (it must be running) and select <uicontrol
+ >Language</uicontrol>.</cmd>
+ <stepresult>You will be prompted for a language and a country.</stepresult>
+ </step>
+ <step>
+ <cmd>Specify the language and country that constitute the desired locale.</cmd>
+ <stepresult>The emulated device restarts using the specified locale.</stepresult>
+ </step>
+ </steps>
+ <postreq>Note that you can change the emulated device's locale to any language/country combination, whether or not
+ the device has the specified locale installed. If the chosen locale is not installed the device defaults to
+ English, but within your application your localized strings.xml file is used.</postreq>
+ </taskbody>
+ </task>
+-->
+</task>
diff --git a/src/help/studio_help/src/topics/t_emulator-view-opening.dita b/src/help/studio_help/src/topics/t_emulator-view-opening.dita
new file mode 100644
index 0000000..b805178
--- /dev/null
+++ b/src/help/studio_help/src/topics/t_emulator-view-opening.dita
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE task
+ PUBLIC "-//OASIS//DTD DITA Task//EN" "../dtd/task.dtd">
+<task xml:lang="en-us" id="t_emulator-view-opening">
+ <title>Opening the Android Emulator view</title>
+ <prolog>
+ <metadata>
+ <keywords>
+ <!--<indexterm></indexterm>-->
+ </keywords>
+ </metadata>
+ </prolog>
+ <taskbody>
+ <context>
+ <p>MOTODEV Studio for Android can open the Android Emulator view for you whenever you start an AVD from the Device
+ Management view or whenever you launch one of the run configurations that targets an AVD (which automatically
+ starts the AVD, which can open the Android Emulator view). At the point where MOTODEV Studio for Android
+ determines that you might want the Android Emulator view opened, it prompts you. If you say "no", the view is
+ not opened: instead the emulator launches and runs in an external window.</p>
+ <p>To explicitly open the Android Emulator view:</p>
+ </context>
+ <steps>
+ <step>
+ <cmd>Select <menucascade><uicontrol>Show View</uicontrol><uicontrol>Other</uicontrol></menucascade> from the
+ <uicontrol>Window</uicontrol> menu. (If <uicontrol>Android Emulator</uicontrol> is listed under <uicontrol
+ >Show View</uicontrol>, select that; the Android Emulator view will open directly.)</cmd>
+ <stepresult>The Show View dialog appears.</stepresult>
+ </step>
+ <step>
+ <cmd>Select <menucascade>
+ <uicontrol>MOTODEV Studio for Android</uicontrol><uicontrol>Android Emulator</uicontrol>
+ </menucascade>.</cmd>
+ </step>
+ </steps>
+ <result>The Android Emulator view appears. Any AVDs running in the background will be displayed in this view. If no
+ AVDs are running, it will be empty.</result>
+ <postreq>
+ <p>Using this view you can interact with the emulated device. You can enlarge or shrink the handset image or
+ switch between portrait and landscape orientation using the controls at the top of the view.</p>
+ <p>Except on Mac OS X, to work with the emulator in a separate window simply close the Android Emulator view and
+ click <uicontrol>No</uicontrol> when you are asked if running emulator instances should be stopped. Opening the
+ Android Emulator view while the emulator is running externally causes the emulator to be shown in the view. On
+ Mac OS X, things work somewhat differently: see <xref href="t_emulator-external.dita"/> for instructions.</p>
+ </postreq>
+ </taskbody>
+</task>
diff --git a/src/help/studio_help/src/topics/t_keystore-backup-restore.dita b/src/help/studio_help/src/topics/t_keystore-backup-restore.dita
new file mode 100644
index 0000000..8da0449
--- /dev/null
+++ b/src/help/studio_help/src/topics/t_keystore-backup-restore.dita
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE task PUBLIC "-//OASIS//DTD DITA Task//EN" "../dtd/task.dtd">
+<task id="t_keystore-backup-restore" xml:lang="en-us">
+ <title>Restoring keystores from a backup</title>
+ <shortdesc>Because updates to an Android package (APK) must be signed with the same key as the original APK was signed
+ with, it is extremely important that you keep a copy of all of your signing keys. The Signing and Keys view provides
+ a simple mechanism to back up and then restore one or more keystores to a zip archive file.</shortdesc>
+ <prolog>
+ <metadata>
+ <keywords>
+ <!--<indexterm></indexterm>-->
+ </keywords>
+ </metadata>
+ </prolog>
+ <taskbody>
+ <steps>
+ <step>
+ <cmd>If necessary, open the Signing and Keys view by <ph product="webui android-studio">selecting <menucascade>
+ <uicontrol>Window</uicontrol>
+ <uicontrol>Show View</uicontrol>
+ <uicontrol>Other</uicontrol>
+ </menucascade> and then selecting <menucascade>
+ <uicontrol>MOTODEV Studio</uicontrol>
+ <uicontrol>Signing and Keys</uicontrol>
+ </menucascade> from the list of views</ph>. Note that this view is part of the MOTODEV Studio for Android
+ perspective and is thus normally opened when you switch to that perspective.</cmd>
+ </step>
+ <step>
+ <cmd>In the Signing and Keys view toolbar, click <image href="../images/KeystoresRestore.png"/> (Restore From
+ Backup).</cmd>
+ <stepresult>The Restore From Backup dialog is displayed.</stepresult>
+ </step>
+ <step>
+ <cmd>Specify the archive containing the backed-up keystore files using the <uicontrol>Backup File</uicontrol>
+ field.</cmd>
+ </step>
+ <step>
+ <cmd>In the <uicontrol>Destination</uicontrol> field, indicate the directory to which the backed-up keystores
+ should be restored.</cmd>
+ </step>
+ <step>
+ <cmd>The keystores contained in the specified archive are listed under <uicontrol>Keystores</uicontrol>. Select
+ the keystores to be restored.</cmd>
+ </step>
+ <step>
+ <cmd>Click <uicontrol>OK</uicontrol>.</cmd>
+ </step>
+ </steps>
+ <result>The specified keystores are restored from the archive file and copied to the indicated destination
+ directory. They are also added to the Signing and Keys view so that you can use their entries (keys) to sign your
+ Android packages.</result>
+ </taskbody>
+</task>
diff --git a/src/help/studio_help/src/topics/t_keystore-backup.dita b/src/help/studio_help/src/topics/t_keystore-backup.dita
new file mode 100644
index 0000000..8acb175
--- /dev/null
+++ b/src/help/studio_help/src/topics/t_keystore-backup.dita
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE task PUBLIC "-//OASIS//DTD DITA Task//EN" "../dtd/task.dtd">
+<task id="t_keystore-backup" xml:lang="en-us">
+ <title>Backing up keystores</title>
+ <shortdesc>Because updates to an Android package (APK) must be signed with the same key as the original APK was signed
+ with, it is extremely important that you keep a copy of all of your signing keys. The Signing and Keys view provides
+ a simple mechanism to back up and then restore one or more keystores to a zip archive file.</shortdesc>
+ <prolog>
+ <metadata>
+ <keywords>
+ <!--<indexterm></indexterm>-->
+ </keywords>
+ </metadata>
+ </prolog>
+ <taskbody>
+ <steps>
+ <step>
+ <cmd>If necessary, open the Signing and Keys view by <ph product="webui android-studio">selecting <menucascade>
+ <uicontrol>Window</uicontrol>
+ <uicontrol>Show View</uicontrol>
+ <uicontrol>Other</uicontrol>
+ </menucascade> and then selecting <menucascade>
+ <uicontrol>MOTODEV Studio</uicontrol>
+ <uicontrol>Signing and Keys</uicontrol>
+ </menucascade> from the list of views</ph>. Note that this view is part of the MOTODEV Studio for Android
+ perspective and is thus normally opened when you switch to that perspective.</cmd>
+ </step>
+ <step>
+ <cmd>In the Signing and Keys view toolbar, click <image href="../images/KeystoresBackup.png"/> (Back Up
+ Keystores).</cmd>
+ <stepresult>The Keystores Backup dialog is displayed.</stepresult>
+ </step>
+ <step>
+ <cmd>Using the Path field, specify the path to and name of the archive (zip) file to be created. Note that if
+ you select an existing file, that file will be overwritten. Otherwise, the specified file will be created for
+ you.</cmd>
+ </step>
+ <step>
+ <cmd>From the list of keystores, select those keystores to be backed up.</cmd>
+ </step>
+ <step>
+ <cmd>Click <uicontrol>OK</uicontrol>.</cmd>
+ </step>
+ </steps>
+ <result>The archive file will be created (if the specified file already exists, you will first be asked whether it
+ is OK to overwrite the existing file), containing the specified keystores.</result>
+ </taskbody>
+</task>
diff --git a/src/help/studio_help/src/topics/t_keystore-creating.dita b/src/help/studio_help/src/topics/t_keystore-creating.dita
new file mode 100644
index 0000000..467202c
--- /dev/null
+++ b/src/help/studio_help/src/topics/t_keystore-creating.dita
@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE task PUBLIC "-//OASIS//DTD DITA Task//EN" "../dtd/task.dtd">
+<task id="t_keystore-creating" xml:lang="en-us">
+ <title>Creating a keystore</title>
+ <shortdesc>Keys must be contained within a keystore. Keystores are easy to create from within the Signing and Keys
+ view.</shortdesc>
+ <prolog>
+ <metadata>
+ <keywords>
+ <!--<indexterm></indexterm>-->
+ </keywords>
+ </metadata>
+ </prolog>
+ <taskbody>
+ <steps>
+ <step>
+ <cmd>If necessary, open the Signing and Keys view by <ph product="webui android-studio">selecting <menucascade>
+ <uicontrol>Window</uicontrol>
+ <uicontrol>Show View</uicontrol>
+ <uicontrol>Other</uicontrol>
+ </menucascade> and then selecting <menucascade>
+ <uicontrol>MOTODEV Studio</uicontrol>
+ <uicontrol>Signing and Keys</uicontrol>
+ </menucascade> from the list of views</ph>. Note that this view is part of the MOTODEV Studio for Android
+ perspective and is thus normally opened when you switch to that perspective.</cmd>
+ </step>
+
+ <step>
+ <cmd>In the Signing and Keys toolbar, click <image href="../images/KeystoreCreate.png"/> (Create
+ Keystore).</cmd>
+ <stepresult>The Create Keystore dialog is displayed.</stepresult>
+ </step>
+ <step>
+ <cmd>Specify the name (including the path to the file) to be given to the keystore file using the
+ <uicontrol>Keystore Filename</uicontrol> field.</cmd>
+ </step>
+ <step>
+ <cmd>Choose a keystore type from the Keystore Type pull-down list. The available choices are: JKS (the standard
+ Java keystore), JCEKS (a Java keystore that provides much stronger protection of private keys), or PKCS12 (a
+ file format defined as part of the Public-Key Cryptography Standards [PKCS] that can be created and
+ interpreted using the OpenSSL pkcs12 command).</cmd>
+ </step>
+ <step>
+ <cmd>By default, the chosen keystore type is added to the keystore filename you specified as an extension. Clear
+ this option if you don't want this extension to be automatically added.</cmd>
+ </step>
+ <step>
+ <cmd>Provide a password to be used to protect the keystore using the <uicontrol>Keystore Password</uicontrol>
+ and <uicontrol>Confirm Password</uicontrol> fields. You will be prompted to reenter this password whenever you
+ add keys to or alter keys within the keystore.</cmd>
+ </step>
+ <step>
+ <cmd>If you would like MOTODEV Studio to remember this keystore's password and automatically supply it whenever
+ it is needed, select <uicontrol>Save this password</uicontrol>.</cmd>
+ </step>
+ <step>
+ <cmd>Create the keystore. </cmd>
+ <choices>
+ <choice>Click <uicontrol>Finish</uicontrol> to create a new, empty keystore.</choice>
+ <choice>Click <uicontrol>Next</uicontrol> if you would like to create the keystore and create a new key within
+ that keystore. You will then be presented with a dialog asking for the characteristics of that new key;
+ follow the instructions under <xref href="t_cert-key-generating.dita">Creating a key</xref>.</choice>
+ </choices>
+ </step>
+ </steps>
+ </taskbody>
+</task>
diff --git a/src/help/studio_help/src/topics/t_keystore-deleting.dita b/src/help/studio_help/src/topics/t_keystore-deleting.dita
new file mode 100644
index 0000000..bcf4fd1
--- /dev/null
+++ b/src/help/studio_help/src/topics/t_keystore-deleting.dita
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE task PUBLIC "-//OASIS//DTD DITA Task//EN" "../dtd/task.dtd">
+<task id="t_keystore-deleting" xml:lang="en-us">
+ <title>Deleting a keystore</title>
+ <shortdesc>Easily delete keystores that you no longer need from within the Signing and Keys view.</shortdesc>
+ <prolog>
+ <metadata>
+ <keywords>
+ <!--<indexterm></indexterm>-->
+ </keywords>
+ </metadata>
+ </prolog>
+ <taskbody>
+ <prereq>Deleting a keystore deletes all of the keys within that keystore as well. Before deleting any keystore,
+ examine the keystore's contents to make sure that the keys are no longer needed. To be safe, you may want to back
+ up the keystore before deleting it.<note>When submitting an Android application update to Google Play, the update
+ <i>must</i> be signed using the exact same key that was used to sign the original application. Be sure to keep
+ copies of all keys you have used to sign apps submitted to Google Play.</note></prereq>
+ <steps>
+ <step>
+ <cmd>If necessary, open the Signing and Keys view by <ph product="webui android-studio">selecting <menucascade>
+ <uicontrol>Window</uicontrol>
+ <uicontrol>Show View</uicontrol>
+ <uicontrol>Other</uicontrol>
+ </menucascade> and then selecting <menucascade>
+ <uicontrol>MOTODEV Studio</uicontrol>
+ <uicontrol>Signing and Keys</uicontrol>
+ </menucascade> from the list of views</ph>. Note that this view is part of the MOTODEV Studio for Android
+ perspective and is thus normally opened when you switch to that perspective.</cmd>
+ </step>
+
+ <step>
+ <cmd>In the Signing and Keys toolbar, click <image href="../images/KeystoreDelete.png"/> (Delete
+ Keystore).</cmd>
+ <stepresult>The Delete Keystore dialog is displayed.</stepresult>
+ </step>
+ <step>
+ <cmd>If you want to delete the keystore file itself, select <uicontrol>Delete content on disk?</uicontrol>. If
+ you don't select this option, the keystore will be removed from the Signing and Keys view, but the keystore
+ file itself will not actually be deleted.</cmd>
+ </step>
+ <step>
+ <cmd>Click <uicontrol>Yes</uicontrol>.</cmd>
+ </step>
+ </steps>
+ <result>The keystore is removed from the Signing and Keys view, and, if you selected the option, the keystore file
+ is deleted.</result>
+ </taskbody>
+</task>
diff --git a/src/help/studio_help/src/topics/t_keystore-entries-importing.dita b/src/help/studio_help/src/topics/t_keystore-entries-importing.dita
new file mode 100644
index 0000000..7ed3916
--- /dev/null
+++ b/src/help/studio_help/src/topics/t_keystore-entries-importing.dita
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE task PUBLIC "-//OASIS//DTD DITA Task//EN" "../dtd/task.dtd">
+<task id="t_keystore-entries-importing" xml:lang="en-us">
+ <title>Importing keystore entries</title>
+ <shortdesc>The Signing and Keys view makes it easy to copy keys from one keystore to another.</shortdesc>
+ <prolog>
+ <metadata>
+ <keywords>
+ <!--<indexterm></indexterm>-->
+ </keywords>
+ </metadata>
+ </prolog>
+ <taskbody>
+ <steps>
+
+ <step>
+ <cmd>If necessary, open the Signing and Keys view by <ph product="webui android-studio">selecting <menucascade>
+ <uicontrol>Window</uicontrol>
+ <uicontrol>Show View</uicontrol>
+ <uicontrol>Other</uicontrol>
+ </menucascade> and then selecting <menucascade>
+ <uicontrol>MOTODEV Studio</uicontrol>
+ <uicontrol>Signing and Keys</uicontrol>
+ </menucascade> from the list of views</ph>. Note that this view is part of the MOTODEV Studio for Android
+ perspective and is thus normally opened when you switch to that perspective.</cmd>
+ </step>
+ <step>
+ <cmd>From the list of keystores displayed in the Signing and Keys view, select the one into which the keys are
+ to be copied.</cmd>
+ </step>
+ <step>
+ <cmd>In the Signing and Keys toolbar, click <image href="../images/KeystoreEntriesImport.png"/> (Import
+ Entries).</cmd>
+ <stepresult>The Import Entries dialog is displayed.</stepresult>
+ </step>
+ <step>
+ <cmd>In the <uicontrol>Source Keystore</uicontrol> section of the dialog, select the keystore containing the
+ keys to be imported, and supply the keystore's password (if necessary; the password is filled in automatically
+ if you previously instructed MOTODEV Studio to save this keystore's password).</cmd>
+ </step>
+ <step>
+ <cmd>The keystore's entries should now be listed (if not, click <uicontrol>Load</uicontrol>). Select the entries
+ to be copied. You must select at least one key or you will see the message "There are no entries to
+ import".</cmd>
+ </step>
+ <step>
+ <cmd>For any selected keys that show "Not verified" in the <uicontrol>Verified</uicontrol> column, supply their
+ passwords in the <uicontrol>Password</uicontrol> column. "Not verified" indicates that the password is either
+ missing or incorrect. Note that if you previously instructed MOTODEV Studio to save the password for a given
+ key, that key's password is automatically filled in for you.</cmd>
+ </step>
+ <step>
+ <cmd>Click <uicontrol>OK</uicontrol>.</cmd>
+ </step>
+ </steps>
+ <result>The selected keys are copied into the target keystore.</result>
+ </taskbody>
+</task>
diff --git a/src/help/studio_help/src/topics/t_keystore-importing.dita b/src/help/studio_help/src/topics/t_keystore-importing.dita
new file mode 100644
index 0000000..8e4f895
--- /dev/null
+++ b/src/help/studio_help/src/topics/t_keystore-importing.dita
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE task PUBLIC "-//OASIS//DTD DITA Task//EN" "../dtd/task.dtd">
+<task id="t_keystore-importing" xml:lang="en-us">
+ <title>Importing a keystore</title>
+ <shortdesc>A keystore created outside of MOTODEV Studio can be imported into the Signing and Keys view for use within that view.</shortdesc>
+ <prolog>
+ <metadata>
+ <keywords>
+ <!--<indexterm></indexterm>-->
+ </keywords>
+ </metadata>
+ </prolog>
+ <taskbody>
+ <steps>
+ <step>
+ <cmd>If necessary, open the Signing and Keys view by <ph product="webui android-studio">selecting <menucascade>
+ <uicontrol>Window</uicontrol>
+ <uicontrol>Show View</uicontrol>
+ <uicontrol>Other</uicontrol>
+ </menucascade> and then selecting <menucascade>
+ <uicontrol>MOTODEV Studio</uicontrol>
+ <uicontrol>Signing and Keys</uicontrol>
+ </menucascade> from the list of views</ph>. Note that this view is part of the MOTODEV Studio for Android
+ perspective and is thus normally opened when you switch to that perspective.</cmd>
+ </step>
+ <step>
+ <cmd>Click <image href="../images/KeystoreImport.png"/> (Import Keystore).</cmd>
+ <stepresult>The Import Keystore dialog is displayed.</stepresult>
+ </step>
+ <step>
+ <cmd>Using the <uicontrol>Keystore Filename</uicontrol> field, specify the keystore to be imported.</cmd>
+ </step>
+ <step>
+ <cmd>Specify the keystore file format using the <uicontrol>Keystore Type</uicontrol> pull-down list. Note that
+ the keystore being imported must be of one of the listed types.</cmd>
+ </step>
+ <step>
+ <cmd>Click <uicontrol>Finish</uicontrol>.</cmd>
+ </step>
+ </steps>
+ <result>The specified keystore is imported and shown in the Signing and Keys view. You can now sign Android packages
+ using the keystore's keys. <note>Although you don't need to supply the keystore password as part of the import
+ process, you will need to do so when you attempt to display its contents in the Signing and Keys
+ view.</note></result>
+ </taskbody>
+</task>
diff --git a/src/help/studio_help/src/topics/t_keystore-password-changing.dita b/src/help/studio_help/src/topics/t_keystore-password-changing.dita
new file mode 100644
index 0000000..caca698
--- /dev/null
+++ b/src/help/studio_help/src/topics/t_keystore-password-changing.dita
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE task PUBLIC "-//OASIS//DTD DITA Task//EN" "../dtd/task.dtd">
+<task id="t_keystore-password-changing" xml:lang="en-us">
+ <title>Changing a keystore's password</title>
+ <shortdesc>The Signing and Keys view enables you to change the password used to protect a keystore.</shortdesc>
+ <prolog>
+ <metadata>
+ <keywords>
+ <!--<indexterm></indexterm>-->
+ </keywords>
+ </metadata>
+ </prolog>
+ <taskbody>
+ <steps>
+ <step>
+ <cmd>If necessary, open the Signing and Keys view by <ph product="webui android-studio">selecting <menucascade>
+ <uicontrol>Window</uicontrol>
+ <uicontrol>Show View</uicontrol>
+ <uicontrol>Other</uicontrol>
+ </menucascade> and then selecting <menucascade>
+ <uicontrol>MOTODEV Studio</uicontrol>
+ <uicontrol>Signing and Keys</uicontrol>
+ </menucascade> from the list of views</ph>. Note that this view is part of the MOTODEV Studio for Android
+ perspective and is thus normally opened when you switch to that perspective.</cmd>
+ </step>
+ <step>
+ <cmd>From the list of keystores displayed in the Signing and Keys view, select the keystore for which you want
+ to change the password.</cmd>
+ </step>
+
+ <step>
+ <cmd>Click <image href="../images/KeystorePasswordChange.png"/> (Change Keystore Password) in the Signing and
+ Keys toolbar.</cmd>
+ <stepresult>The Enter Password dialog is displayed.</stepresult>
+ </step>
+ <step>
+ <cmd>If necessary, supply the keystore's password in the <uicontrol>Old password</uicontrol> field provided.
+ Note that if you previously indicated that MOTODEV Studio should save this keystore's password, that password
+ is automatically filled in for you.</cmd>
+ </step>
+ <step>
+ <cmd>Enter the new password to be used to protect the keystore into the <uicontrol>New password</uicontrol> and
+ <uicontrol>Confirm new password fields</uicontrol>.</cmd>
+ </step>
+ <step>
+ <cmd>If you would like MOTODEV Studio to save this keystore's password and then automatically fill it in when it
+ is called for in the various Signing and Keys dialogs, select <uicontrol>Save this password</uicontrol>.</cmd>
+ </step>
+ <step>
+ <cmd>Click <uicontrol>OK</uicontrol>.</cmd>
+ </step>
+ </steps>
+ <result>The keystore password is changed.</result>
+ </taskbody>
+</task>
diff --git a/src/help/studio_help/src/topics/t_keystore-type-changing.dita b/src/help/studio_help/src/topics/t_keystore-type-changing.dita
new file mode 100644
index 0000000..dda7f44
--- /dev/null
+++ b/src/help/studio_help/src/topics/t_keystore-type-changing.dita
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE task PUBLIC "-//OASIS//DTD DITA Task//EN" "../dtd/task.dtd">
+<task id="t_keystore-type-changing" xml:lang="en-us">
+ <title>Changing a keystore's type</title>
+ <shortdesc>The Signing and Keys view enables you to change the file format of a keystore file.</shortdesc>
+ <prolog>
+ <metadata>
+ <keywords>
+ <!--<indexterm></indexterm>-->
+ </keywords>
+ </metadata>
+ </prolog>
+ <taskbody>
+ <steps>
+ <step>
+ <cmd>If necessary, open the Signing and Keys view by <ph product="webui android-studio">selecting <menucascade>
+ <uicontrol>Window</uicontrol>
+ <uicontrol>Show View</uicontrol>
+ <uicontrol>Other</uicontrol>
+ </menucascade> and then selecting <menucascade>
+ <uicontrol>MOTODEV Studio</uicontrol>
+ <uicontrol>Signing and Keys</uicontrol>
+ </menucascade> from the list of views</ph>. Note that this view is part of the MOTODEV Studio for Android
+ perspective and is thus normally opened when you switch to that perspective.</cmd>
+ </step>
+ <step>
+ <cmd>From the list of keystores displayed in the Signing and Keys view, select the keystore for which you want
+ to change the type.</cmd>
+ </step>
+
+ <step>
+ <cmd>Click <image href="../images/KeystoreTypeChange.png"/> (Change Keystore Type) in the Signing and Keys
+ toolbar.</cmd>
+ <stepresult>The Change Keystore Type dialog is displayed.</stepresult>
+ </step>
+ <step>
+ <cmd>If necessary, supply the keystore's password in the field provided and then click
+ <uicontrol>Load</uicontrol> to display the keys contained in the keystore. Note that if you previously
+ indicated that MOTODEV Studio should save this keystore's password, that password is automatically filled in
+ for you.</cmd>
+ </step>
+ <step>
+ <cmd>Choose a new keystore type from the Keystore Type pull-down list. The available choices are: JKS (the
+ standard Java keystore), JCEKS (a Java keystore that provides much stronger protection of private keys), or
+ PKCS12 (a file format defined as part of the Public-Key Cryptography Standards [PKCS] that can be created and
+ interpreted using the OpenSSL pkcs12 command). Note that the keystore's current type is not included in the
+ pull-down list.</cmd>
+ </step>
+ <step>
+ <cmd>If any of the keys within the keystore do not have saved passwords (indicated by "Not verified" in the
+ <uicontrol>Entries</uicontrol> table), enter those passwords in the table's <uicontrol>Password</uicontrol>
+ column.</cmd>
+ <info>Note that you won't be able to change the keystore's type unless the keystore password and the password
+ for each of the keystore's entries are provided, either manually by you or automatically by MOTODEV
+ Studio.</info>
+ </step>
+ <step>
+ <cmd>Click <uicontrol>OK</uicontrol>.</cmd>
+ </step>
+ </steps>
+ <result>The keystore file is reformatted as directed.</result>
+ </taskbody>
+</task>
diff --git a/src/help/studio_help/src/topics/t_log-files-collecting.dita b/src/help/studio_help/src/topics/t_log-files-collecting.dita
new file mode 100644
index 0000000..a9cb94b
--- /dev/null
+++ b/src/help/studio_help/src/topics/t_log-files-collecting.dita
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE task
+ PUBLIC "-//OASIS//DTD DITA Task//EN" "../dtd/task.dtd">
+<task xml:lang="en-us" id="t_log-files-collecting">
+ <title>Collecting log files</title>
+ <prolog>
+ <metadata>
+ <keywords>
+ <!--<indexterm></indexterm>-->
+ </keywords>
+ </metadata>
+ </prolog>
+ <taskbody>
+ <context>
+ <p>MOTODEV Studio allows you to collect Eclipse and MOTODEV Studio log files and package them into a single ZIP
+ archive.</p>
+ </context>
+ <steps>
+ <step>
+ <cmd>Select <uicontrol>Collect Log Files</uicontrol> from the <ph product="javame-studio javame-sdk webui"
+ ><uicontrol>MOTODEV</uicontrol></ph><ph product="android-studio"><uicontrol>Help</uicontrol></ph> menu. </cmd>
+ <stepresult>The Collect Log Files dialog appears.</stepresult>
+ </step>
+ <step product="javame-studio javame-sdk webui">
+ <cmd>Using the <uicontrol>Directory</uicontrol> field, specify the directory into which the archive containing
+ the log files should be written.</cmd>
+ <info>By default, the log file is written to your home directory.</info>
+ </step>
+ <step product="javame-studio javame-sdk webui">
+ <cmd>In the <uicontrol>File Name</uicontrol> field, enter the name to be used for the archive file.</cmd>
+ </step>
+ <step product="android-studio">
+ <cmd>Using the <uicontrol>Output file</uicontrol> field, specify the path and filename for the archive file.
+ Because this is a ZIP archive, you may want to give the filename a <codeph>.zip</codeph> extension.</cmd>
+ </step>
+ <step>
+ <cmd>Select the log files that should be archived from those listed under <uicontrol>Log
+ Files</uicontrol>.</cmd>
+ </step>
+ <step>
+ <cmd>Click <uicontrol>Finish</uicontrol>.</cmd>
+ </step>
+ </steps>
+ </taskbody>
+</task>
diff --git a/src/help/studio_help/src/topics/t_obfuscating.dita b/src/help/studio_help/src/topics/t_obfuscating.dita
new file mode 100644
index 0000000..f2c7295
--- /dev/null
+++ b/src/help/studio_help/src/topics/t_obfuscating.dita
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE task
+ PUBLIC "-//OASIS//DTD DITA Task//EN" "../dtd/task.dtd">
+<task xml:lang="en-us" id="t_obfuscating">
+ <title>Obfuscating Android projects</title>
+ <prolog>
+ <metadata>
+ <keywords>
+ <!--<indexterm></indexterm>-->
+ </keywords>
+ </metadata>
+ </prolog>
+ <taskbody>
+ <context>
+ <p>ADT uses ProGuard to shrink, optimize, and obfuscate your Java code. Obfuscation is intended to make your Java
+ code harder to reverse-engineer, and thus is a useful tool when writing code that needs to be secure. Android
+ projects are not obfuscated by default, but you can enable it at project creation time or after the fact. Note
+ that obfuscation is only performed when building a release version of your project: obfuscation gets in the way
+ of the debugging process and thus is not performed for debug builds.</p>
+ <ul>
+ <li>To specify at project creation time that a project should be obfuscated, simply select <uicontrol>Obfuscate
+ Java classes</uicontrol> in the New Android Project using Studio for Android dialog.</li>
+ <li>To enable obfuscation for a single project, right-click the project in the Package Explorer, select
+ <uicontrol>Properties</uicontrol>, select <uicontrol>MOTODEV Studio</uicontrol> from the list of properties,
+ and then select <uicontrol>Obfuscate Java classes</uicontrol>. Clear this option if you want to prevent
+ obfuscation of this project in future project builds.</li>
+ <li>To enable or disable obfuscation for multiple projects, select <uicontrol>Enable/Disable Obfuscation</uicontrol> from the MOTODEV menu. In the dialog that appears select those projects that should be
+ obfuscated (ensuring that any projects that should not be obfuscated are not selected), then click <uicontrol
+ >OK</uicontrol>. </li>
+ </ul>
+ <p>Whichever of the above you use to enable obfuscation, MOTODEV Studio for Android enables and configures
+ ProGuard for you. If needed you can then further customize it as outlined on the ProGuard page within the
+ Android developer documentation.</p>
+ <note>ProGuard requires that your development computer have a full JDK (Java Development Kit) installed; you
+ cannot obfuscate your Android projects with only a JRE (Java Runtime Environment). Also, ProGuard will not work
+ if the project's path contains spaces or parenthesis.</note>
+ </context>
+ </taskbody>
+</task>
diff --git a/src/help/studio_help/src/topics/t_project-creating-widget_android.dita b/src/help/studio_help/src/topics/t_project-creating-widget_android.dita
new file mode 100644
index 0000000..3e75207
--- /dev/null
+++ b/src/help/studio_help/src/topics/t_project-creating-widget_android.dita
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE task
+ PUBLIC "-//OASIS//DTD DITA Task//EN" "../dtd/task.dtd">
+<task xml:lang="en-us" id="t_project-creating-widget_android">
+ <title>Creating an Android widget project</title>
+ <shortdesc>The New Android Widget Project wizard allows you to quickly and easily create a new widget application for
+ an Android device.</shortdesc>
+ <prolog>
+ <metadata>
+ <keywords>
+ <!--<indexterm></indexterm>-->
+ </keywords>
+ </metadata>
+ </prolog>
+ <taskbody>
+ <context/>
+ <steps>
+ <step>
+ <cmd>Select <menucascade><uicontrol>File</uicontrol><uicontrol>New</uicontrol><uicontrol>Android Widget Project
+ using Studio for Android</uicontrol></menucascade>. </cmd>
+ <stepresult>The New Android Widget Project dialog appears with a number of default values filled
+ in.</stepresult>
+ </step>
+ <step>
+ <cmd>Specify a name for your new project in <uicontrol>Project name</uicontrol>.</cmd>
+ </step>
+ <step>
+ <cmd>If you want your project stored in a subdirectory (named with the project name) within default location
+ (usually the workspace root) leave <uicontrol>Use default location</uicontrol> selected. Otherwise: </cmd>
+ <substeps>
+ <substep>
+ <cmd>Clear <uicontrol>Use default location</uicontrol>.</cmd>
+ </substep>
+ <substep>
+ <cmd>Use the <uicontrol>Location</uicontrol> field to specify the directory in which the project
+ subdirectory should be created.</cmd>
+ </substep>
+ </substeps>
+ </step>
+ <step>
+ <cmd>Verify that the SDK target is correct for your widget application.</cmd>
+ </step>
+ <step>
+ <cmd>Using the <uicontrol>Application name</uicontrol> field, supply the name that should identify your widget
+ in the widgets list accessible from the Android home screen.</cmd>
+ </step>
+ <step>
+ <cmd>Verify the <uicontrol>Package name</uicontrol>, <uicontrol>Activity name</uicontrol>, and <uicontrol>Min
+ SDK version</uicontrol> fields. All are set to reasonable defaults by the new Android Widget Project wizard,
+ but all can be changed if needed for your particular application.</cmd>
+ </step>
+ <step>
+ <cmd>Click <uicontrol>Finish</uicontrol>.</cmd>
+ </step>
+ </steps>
+ <result>You project is now created and added to those listed in the Project Explorer view. </result>
+ </taskbody>
+</task>
diff --git a/src/help/studio_help/src/topics/t_project-creating_android.dita b/src/help/studio_help/src/topics/t_project-creating_android.dita
new file mode 100644
index 0000000..e55df60
--- /dev/null
+++ b/src/help/studio_help/src/topics/t_project-creating_android.dita
@@ -0,0 +1,80 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE task
+ PUBLIC "-//OASIS//DTD DITA Task//EN" "../dtd/task.dtd">
+<task xml:lang="en-us" id="t_project-creating_android">
+ <title>Creating a MOTODEV Studio for Android project</title>
+ <shortdesc>The New Android project wizard allows you to quickly and easily create a new application for an Android
+ device.</shortdesc>
+ <prolog>
+ <metadata>
+ <keywords>
+ <!--<indexterm></indexterm>-->
+ </keywords>
+ </metadata>
+ </prolog>
+ <taskbody>
+ <context/>
+ <steps>
+ <step>
+ <cmd>Select <menucascade><uicontrol>File</uicontrol><uicontrol>New</uicontrol><uicontrol>Android Project using
+ Studio for Android</uicontrol></menucascade>. </cmd>
+ <stepresult>The New Android Project dialog appears with a number of default values filled in.</stepresult>
+ </step>
+ <step>
+ <cmd>Specify a name for your new project in <uicontrol>Project name</uicontrol>.</cmd>
+ </step>
+ <step>
+ <cmd>By default the New Android Project wizard will create a very basic Android project with a single activity
+ that will serve as a starting point. If you would prefer to start from one of the sample applications included
+ with the Android SDK, select <uicontrol>Create new project using sample</uicontrol> (on the following dialog
+ you will be asked to select the sample application). Or, if you have an existing Android project outside of
+ your workspace that you want to open, select <uicontrol>Create project from existing source</uicontrol> and
+ then specify the path to the existing project using the <uicontrol>Location</uicontrol> field (note that in
+ this case the project already exists so there is no need to specify a location in which the project is to be
+ created; skip the next step).</cmd>
+ </step>
+ <step>
+ <cmd>If you want your project stored in a subdirectory (named with the project name) within default location
+ (usually the workspace root) leave <uicontrol>Use default location</uicontrol> selected. Otherwise: </cmd>
+ <substeps>
+ <substep>
+ <cmd>Clear <uicontrol>Use default location</uicontrol>.</cmd>
+ </substep>
+ <substep>
+ <cmd>Use the <uicontrol>Location</uicontrol> field to specify the directory in which the project
+ subdirectory should be created.</cmd>
+ </substep>
+ </substeps>
+ <info>Some features, such as obfuscation, will not work if the project's path contains spaces or
+ parenthesis.</info>
+ </step>
+ <step>
+ <cmd>Verify that the SDK target is correct for your application.</cmd>
+ </step>
+ <step>
+ <cmd>Using the <uicontrol>Application name</uicontrol> field, supply the name that your application should be
+ labeled with in the Android home screen.</cmd>
+ </step>
+ <step>
+ <cmd>Verify the <uicontrol>Package name</uicontrol>, <uicontrol>Activity name</uicontrol>, and <uicontrol>Min
+ SDK version</uicontrol> fields. All are set to reasonable defaults by the new Android Project wizard, but
+ all can be changed if needed for your particular application.</cmd>
+ </step>
+ <step>
+ <cmd>If the new Android project will contain native (C/C++) code in addition to Java, select <uicontrol>Add
+ native support</uicontrol>. Note that you will not be able to select this option unless you have properly
+ installed the Android NDK and configured MOTODEV Studio for Android for native development.</cmd>
+ </step>
+ <step>
+ <cmd>If your Java classes should be obfuscated when you create a release version of your app, select <uicontrol
+ >Obfuscate Java classes</uicontrol>.</cmd>
+ </step>
+ <step>
+ <cmd>If you elected to create a new project based upon an SDK sample, click <uicontrol>Next</uicontrol>, select
+ the sample, and then click <uicontrol>Finish</uicontrol>. Otherwise, just click <uicontrol
+ >Finish</uicontrol>.</cmd>
+ </step>
+ </steps>
+ <result>You project is now created and added to those listed in the Project Explorer view. </result>
+ </taskbody>
+</task>
diff --git a/src/help/studio_help/src/topics/t_service-creating.dita b/src/help/studio_help/src/topics/t_service-creating.dita
new file mode 100644
index 0000000..16ac572
--- /dev/null
+++ b/src/help/studio_help/src/topics/t_service-creating.dita
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE task
+ PUBLIC "-//OASIS//DTD DITA Task//EN" "../dtd/task.dtd">
+<task xml:lang="en-us" id="t_service-creating">
+ <title>Adding an Android service</title>
+ <shortdesc>Allows you to easily add a new service to an existing Android project. A service allows you to perform
+ lengthy background operations or an API that can be called by other applications.</shortdesc>
+ <prolog>
+ <metadata>
+ <keywords>
+ <!--<indexterm></indexterm>-->
+ </keywords>
+ </metadata>
+ </prolog>
+ <taskbody>
+ <context/>
+ <steps>
+ <step>
+ <cmd>In the Package Explorer, right-click the project to which the service should be added and select
+ <menucascade><uicontrol>New</uicontrol><uicontrol>Android Service</uicontrol></menucascade>.</cmd>
+ </step>
+ <step>
+ <cmd>Specify a name for your new Service subclass in <uicontrol>Name</uicontrol>.</cmd>
+ </step>
+ <step>
+ <cmd>By default the new service will not have a label of its own; the application's label will be used when
+ necessary. If you want a specific label for this service, clear the <uicontrol>Default</uicontrol> option
+ (next to the <uicontrol>Label</uicontrol> field) and then enter your preferred user-readable label.</cmd>
+ </step>
+ <step>
+ <cmd>If this service uses device capabilities for which the user must grant permission, specify them in the
+ <uicontrol>Permission</uicontrol> area. These permissions will be added to the appropriate place in your
+ application's manifest file. To specify a permission, click <uicontrol>Add</uicontrol>, select the needed
+ permission, and click <uicontrol>OK</uicontrol>.</cmd>
+ </step>
+ <step>
+ <cmd>The New Android Service wizard will create the service with an empty onBind() method. If you will be
+ implementing onCreate() and/or onStart(), select the appropriate options to include empty versions of those
+ method as well.</cmd>
+ </step>
+ <step>
+ <cmd>Click <uicontrol>Finish</uicontrol>.</cmd>
+ </step>
+ </steps>
+ <result>The service is created and added to the selected project. It is also opened in an editor view.</result>
+ </taskbody>
+</task>
diff --git a/src/help/studio_help/src/topics/u_android-handset-properties.dita b/src/help/studio_help/src/topics/u_android-handset-properties.dita
new file mode 100644
index 0000000..6024381
--- /dev/null
+++ b/src/help/studio_help/src/topics/u_android-handset-properties.dita
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE reference PUBLIC "-//OASIS//DTD DITA Reference//EN" "../dtd/reference.dtd">
+<reference id="u_android-handset-properties" xml:lang="en-us">
+ <title>Android Handset Properties</title>
+ <shortdesc>Presents the basic properties of an Android handset, such as the device serial number, Android OS version, and API version.</shortdesc>
+ <prolog>
+ <metadata>
+ <keywords>
+ <!--<indexterm></indexterm>-->
+ </keywords>
+ </metadata>
+ </prolog>
+ <refbody><section>To view the properties of an Android handset connected to your development computer, right-click the entry for the handset in the Device Management view and select <uicontrol>Properties</uicontrol> from the menu that appears. For more detailed properties, select <menucascade><uicontrol>Android Handset Properties</uicontrol><uicontrol>Device Properties</uicontrol></menucascade> in the left pane of the Properties dialog.</section> </refbody>
+</reference>
diff --git a/src/help/studio_help/src/topics/u_app-signing-view.dita b/src/help/studio_help/src/topics/u_app-signing-view.dita
new file mode 100644
index 0000000..b6b95d4
--- /dev/null
+++ b/src/help/studio_help/src/topics/u_app-signing-view.dita
@@ -0,0 +1,110 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE reference PUBLIC "-//OASIS//DTD DITA Reference//EN" "../dtd/reference.dtd">
+<reference id="u_app-signing-view" xml:lang="en-us">
+ <title>Signing And Keys view</title>
+ <shortdesc>The Signing and Keys view allows you to create and manage keys and keystores. From this view you can sign
+ or remove signatures from your application packages.</shortdesc>
+ <prolog>
+ <metadata>
+ <keywords>
+ <!--<indexterm></indexterm>-->
+ </keywords>
+ </metadata>
+ </prolog>
+ <refbody>
+ <section><p>The Signing and Keys view is part of the MOTODEV Studio for Android perspective. You can open it manually by <ph
+ product="webui android-studio">selecting <menucascade>
+ <uicontrol>Window</uicontrol>
+ <uicontrol>Show View</uicontrol>
+ <uicontrol>Other</uicontrol>
+ </menucascade> and then selecting <menucascade>
+ <uicontrol product="webui">Studio for WebUI</uicontrol>
+ <uicontrol product="android-studio">MOTODEV Studio</uicontrol>
+ <uicontrol>Signing and Keys</uicontrol>
+ </menucascade> from the list of views.</ph></p></section>
+ <section>
+ <p>The Signing and Keys view lists the keys (within keystores) that you can use to sign your application packages.
+ In the upper-right corner of the view are a set of buttons that allow you to manipulate and use those keys and
+ keystores:</p>
+ <simpletable>
+ <strow product="android-studio">
+ <stentry><image href="../images/KeystoreRefresh.png"/></stentry>
+ <stentry>Refresh Keystore</stentry>
+ <stentry>Rereads the contents of the selected keystore and updates the Signing and Keys view
+ accordingly.</stentry>
+ </strow>
+ <strow>
+ <stentry><image href="../images/KeystoreCreate.png"/></stentry>
+ <stentry>Create Keystore</stentry>
+ <stentry>Creates a new keystore.</stentry>
+ </strow>
+ <strow product="android-studio">
+ <stentry><image href="../images/KeystoreDelete.png"/></stentry>
+ <stentry>Delete Keystore</stentry>
+ <stentry>Deletes the selected keystore.</stentry>
+ </strow>
+ <strow>
+ <stentry><image href="../images/KeystorePasswordChange.png"/></stentry>
+ <stentry>Change Keystore Password</stentry>
+ <stentry>Changes the password used to protect the selected keystore.</stentry>
+ </strow>
+ <strow>
+ <stentry><image href="../images/KeystoreTypeChange.png"/></stentry>
+ <stentry>Change Keystore Type</stentry>
+ <stentry>Changes the keystore type (to JKS, JCEKS, or PKCS12).</stentry>
+ </strow>
+ <strow>
+ <stentry><image href="../images/KeystoreImport.png"/></stentry>
+ <stentry>Import Keystore</stentry>
+ <stentry>Imports an existing keystore into the Signing and Keys view.</stentry>
+ </strow>
+ <strow product="android-studio">
+ <stentry><image href="../images/KeystoreEntriesImport.png"/></stentry>
+ <stentry>Import Entries</stentry>
+ <stentry>Imports one or more entries (keys) from one keystore into another.</stentry>
+ </strow>
+ <strow product="android-studio">
+ <stentry><image href="../images/KeystoresBackup.png"/></stentry>
+ <stentry>Backup Keystores</stentry>
+ <stentry>Backs up one or more keystores to a zip archive.</stentry>
+ </strow>
+ <strow product="android-studio">
+ <stentry><image href="../images/KeystoresRestore.png"/></stentry>
+ <stentry>Restore Backup</stentry>
+ <stentry>Restores one or more keystores that had been backed up to a zip archive.</stentry>
+ </strow>
+ <strow product="webui">
+ <stentry><image href="../images/KeyCreate.png"/></stentry>
+ <stentry>Create Key</stentry>
+ <stentry>Creates a new key within a selected keystore.</stentry>
+ </strow>
+ <strow>
+ <stentry><image href="../images/KeyPasswordChange.png"/></stentry>
+ <stentry>Change Key Password</stentry>
+ <stentry>Changes the password used to protect a selected key.</stentry>
+ </strow>
+ <strow>
+ <stentry><image href="../images/KeyDelete.png"/></stentry>
+ <stentry>Delete Key</stentry>
+ <stentry>Deletes a key.</stentry>
+ </strow>
+ <strow>
+ <stentry><image href="../images/KeyProperties.png"/></stentry>
+ <stentry>View Key Properties</stentry>
+ <stentry>Displays the properties of a selected key.</stentry>
+ </strow>
+ <strow>
+ <stentry><image href="../images/PackageSign.png"/></stentry>
+ <stentry>Sign Android Package</stentry>
+ <stentry>Signs one or more Android packages (APKs) with a selected key.</stentry>
+ </strow>
+ <strow>
+ <stentry><image href="../images/PackageUnsign.png"/></stentry>
+ <stentry>Remove Android Package Signature</stentry>
+ <stentry>Removes any signatures from one or more selected Android packages (APKs).</stentry>
+ </strow>
+ </simpletable>
+ <p>Note that some of the buttons are inactive; only those buttons that apply to the currently selected key or keystore are usable.</p>
+ </section>
+ </refbody>
+</reference>
diff --git a/src/help/studio_help/src/topics/u_avd-offline-dialog.dita b/src/help/studio_help/src/topics/u_avd-offline-dialog.dita
new file mode 100644
index 0000000..ce988d5
--- /dev/null
+++ b/src/help/studio_help/src/topics/u_avd-offline-dialog.dita
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE reference PUBLIC "-//OASIS//DTD DITA Reference//EN" "../dtd/reference.dtd">
+<reference id="u_avd-offline-dialog" xml:lang="en-us">
+ <title>Preferred AVD Offline dialog</title>
+ <shortdesc>The AVD specified by your run configuration is offline, but a compatible AVD is running. This dialog gives
+ you the choice of either launching the app in the compatible AVD or starting the specified AVD and launching it
+ there.</shortdesc>
+ <prolog>
+ <metadata>
+ <keywords>
+ <!--<indexterm></indexterm>-->
+ </keywords>
+ </metadata>
+ </prolog>
+ <refbody>
+ <section>This dialog is only displayed if you attempt to launch an app on an emulated device (AVD) that isn't
+ running while there is a compatible emulated device already running. The three buttons in this dialog allow you to
+ do the following:</section>
+ <simpletable>
+ <strow>
+ <stentry>
+ <uicontrol>Ignore</uicontrol>
+ </stentry>
+ <stentry>Ignore the suggested AVD(s). Launch the AVD specified in the run configuration, and then install and
+ run the app on that AVD.</stentry>
+ </strow>
+ <strow>
+ <stentry>
+ <uicontrol>Abort</uicontrol>
+ </stentry>
+ <stentry>Cancel the app launch.</stentry>
+ </strow>
+ <strow>
+ <stentry>
+ <uicontrol>OK</uicontrol>
+ </stentry>
+ <stentry>Deploy the app to the chosen AVD and launch it, rather than using the AVD specified in the run
+ configuration. Note that the OK button is disabled if you have not yet selected a running AVD.</stentry>
+ </strow>
+ </simpletable>
+ <section>There is also an option to "Update configuration to use selected AVD". If you select this option, the
+ Device field in the run configuration will be changed to the selected AVD, ensuring that future runs of that
+ configuration will deploy to the selected AVD automatically.</section>
+ </refbody>
+</reference>
diff --git a/src/help/studio_help/src/topics/u_avd-properties.dita b/src/help/studio_help/src/topics/u_avd-properties.dita
new file mode 100644
index 0000000..11dd14b
--- /dev/null
+++ b/src/help/studio_help/src/topics/u_avd-properties.dita
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE reference PUBLIC "-//OASIS//DTD DITA Reference//EN" "../dtd/reference.dtd">
+<reference id="u_avd-properties" xml:lang="en-us">
+ <title>Android Virtual Device Properties</title>
+ <shortdesc>Presents the basic properties of an Android Virtual Device (AVD), such as the name, target device, skin,
+ and the path to the AVD file. This properties dialog also allows you to specify proxy settings for connecting the
+ AVD to the Internet, emulator snapshot settings, the technology used when presenting the emulator in the Android
+ Emulator view (Windows and Linux only), and a timeout value used when launching the emulator.</shortdesc>
+ <prolog>
+ <metadata>
+ <keywords>
+ <!--<indexterm></indexterm>-->
+ </keywords>
+ </metadata>
+ </prolog>
+ <refbody>
+ <section>To view the properties of an AVD, right-click the entry for the AVD in the Device Management view and
+ select <uicontrol>Properties</uicontrol> from the menu that appears. For more detailed properties, select
+ <menucascade><uicontrol>Android Virtual Device</uicontrol><uicontrol>Device Properties</uicontrol></menucascade>
+ in the left pane of the Properties dialog. Options that control how the emulator is launched can be found under
+ <menucascade><uicontrol>Android Virtual Device</uicontrol><uicontrol>Startup Options</uicontrol></menucascade>.</section>
+ <section>
+ <simpletable>
+ <strow>
+ <stentry><uicontrol>Proxy Settings</uicontrol></stentry>
+ <stentry>Select this option if you want the emulated device to use the same network settings that Eclipse is
+ configured to use. If your network requires the use of a proxy, for instance, and Eclipse is configured to
+ use that proxy, selecting this option causes the emulated device to use that same proxy for its network
+ communications.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Snapshot Settings</uicontrol></stentry>
+ <stentry>Enables the capture and use of a "snapshot"--a copy of the emulator's memory--to speed the emulator
+ startup process (subsequent to the first startup; the initial startup will proceed at the normal speed, but
+ after a snapshot has been taken subsequent startups can be based on that snapshot, bypassing much of the
+ startup process)</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Internal Emulator Window</uicontrol></stentry>
+ <stentry>(not available on Mac OS X) Specifies whether, when displaying the emulator image in the Android
+ Emulator view, the emulator image is presented natively or using VNC. Leave <uicontrol>Show the Native
+ Emulator Window within an Eclipse View (Recommended)</uicontrol> selected unless you know that the native
+ option does not work correctly on your development machine (in which case you should select <uicontrol>Use
+ VNC to show the Emulator within an Eclipse View</uicontrol>). Note that you cannot change the <uicontrol
+ >Internal Emulator Window</uicontrol> properties while the emulator is running.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Timeout (sec)</uicontrol></stentry>
+ <stentry>Specifies the timeout value used when starting the emulator; if the emulator does not launch during
+ the specified interval, the launch is aborted. Note that you cannot change the timeout value while the
+ emulator is running.</stentry>
+ </strow>
+ </simpletable>
+ </section>
+ </refbody>
+</reference>
diff --git a/src/help/studio_help/src/topics/u_avd-startup-options.dita b/src/help/studio_help/src/topics/u_avd-startup-options.dita
new file mode 100644
index 0000000..812697a
--- /dev/null
+++ b/src/help/studio_help/src/topics/u_avd-startup-options.dita
@@ -0,0 +1,157 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE reference PUBLIC "-//OASIS//DTD DITA Reference//EN" "../dtd/reference.dtd">
+<reference id="u_avd-startup-options" xml:lang="en-us">
+ <title>Startup Options</title>
+ <shortdesc>This dialog lets you specify options that are applied when you start the emulator.</shortdesc>
+ <prolog>
+ <metadata>
+ <keywords>
+ <!--<indexterm></indexterm>-->
+ </keywords>
+ </metadata>
+ </prolog>
+ <refbody>
+ <section>
+ <p>This dialog contains a number of tabs, each with a number of fields. Note that beneath the tabbed sections is a
+ non-editable field that presents your selected options, command-line style, as they will be passed to the
+ emulator when it is started. See <xref
+ href="http://d.android.com/guide/developing/tools/emulator.html#startup-options" format="html"
+ scope="external">http://d.android.com/guide/developing/tools/emulator.html#startup-options</xref> for all of
+ the possible command-line arguments.</p>
+ </section>
+ <section>
+ <title>User Interface tab</title>
+ <simpletable>
+ <strow>
+ <stentry><uicontrol>Screen Resolution</uicontrol></stentry>
+ <stentry>A DPI value that represents a scaling factor to be applied to the emulator window. This allows you to
+ alter the emulator's screen resolution to match the screen size of an actual device. By default, this value
+ is 165. Click the <uicontrol>Calculate</uicontrol> button to calculate screen resolution and scale values
+ that result in a life-sized image given the screen size of the device being emulated (in inches) and your
+ development computer's display size (in inches) or density (DPI).</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Scale</uicontrol></stentry>
+ <stentry>A value ranging from 0.1 to 3.0 that represents the scaling factor to be applied to the emulator
+ window. To specify the scale as a DPI value, include a suffix of "dpi". Click the <uicontrol
+ >Calculate</uicontrol> button to calculate screen resolution and scale values that result in a life-sized
+ image given the screen size of the device being emulated (in inches) and your development computer's display
+ size (in inches) or density (DPI).</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Disable Boot Animation</uicontrol></stentry>
+ <stentry>Speeds up the emulator startup by disabling the boot animation sequence.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Don't show Emulator Skin</uicontrol></stentry>
+ <stentry>Hides the "skin" that makes the emulated device look more like a specific handheld. Note that the
+ skin includes the device keyboard (if the emulated device has a physical keyboard); hiding the skin makes
+ this keyboard, along with any other physical controls presented by the emulated device, inaccessible. Also
+ note that if you change this option on a running emulator, you will need to stop and restart the emulator
+ for the option to take effect.</stentry>
+ </strow>
+ </simpletable>
+ </section>
+ <section>
+ <title>Disk Images tab</title>
+ <simpletable>
+ <strow>
+ <stentry><uicontrol>Cache</uicontrol></stentry>
+ <stentry>The working cache partition image.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>User Data</uicontrol></stentry>
+ <stentry>The working user-data disk image.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Init User Data</uicontrol></stentry>
+ <stentry>A file containing a user-data disk image that is copied to the working user-data disk image if the
+ <uicontrol>Wipe User Data</uicontrol> option is selected. </stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>RAM</uicontrol></stentry>
+ <stentry>A file that to be used as a RAM-disk image.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>SD Card</uicontrol></stentry>
+ <stentry>An SD card image that will be used by the new AVD instance.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Disable Cache Partition</uicontrol></stentry>
+ <stentry>Controls whether or not the AVD uses a cache partition.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Wipe User Data</uicontrol></stentry>
+ <stentry>Select this option to reset the user-data disk image when the AVD starts up. If a disk image is
+ provided in the <uicontrol>Init User Data</uicontrol> field, the contents of that file are copied to the
+ working user-data disk image, rather than the emulator's default image.</stentry>
+ </strow>
+ </simpletable>
+ </section>
+ <section>
+ <title>Network tab</title>
+ <p>The fields on this tab control how the AVD accesses the Internet.</p>
+ <simpletable>
+ <strow>
+ <stentry><uicontrol>DNS Server</uicontrol></stentry>
+ <stentry>A comma-separated list of up to four DNS server names or IP addresses to be used by the
+ AVD.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Network Latency</uicontrol></stentry>
+ <stentry>Emulates the network latency of various cellular network types, such as GPRS, EDGE, or UMTS.
+ </stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Network Speed</uicontrol></stentry>
+ <stentry>Throttles the networking speed to simulate that of a cellular network. Use this field to specify the
+ desired network type.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Port Number</uicontrol></stentry>
+ <stentry>The console port number for this emulator instance. This should be an even integer value that ranges
+ from 5554 to 5584.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Max Network Speed/Min Network Latency</uicontrol></stentry>
+ <stentry>Indicates that network operations should proceed at full speed, with no latency.</stentry>
+ </strow>
+ </simpletable>
+ </section>
+ <section>
+ <title>System tab</title>
+ <simpletable>
+ <strow>
+ <stentry><uicontrol>CPU Delay</uicontrol></stentry>
+ <stentry>Allows you to emulate a slow CPU. Supply a value from 0 (no delay) to 1000 (maximum delay). Note that
+ these integer values simply indicate a relative degree of delay and do not represent any actual amount of
+ time. </stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Redirect GPS</uicontrol></stentry>
+ <stentry>Emulates an NMEA-compatible GPS device that is connected to an external character device or socket.
+ For information on the format of this field, see the description of the -serial option in the QEMU user
+ documentation, which can be found at <xref href="http://www.nongnu.org/qemu/qemu-doc.html" format="html"
+ scope="external">http://www.nongnu.org/qemu/qemu-doc.html</xref>.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Disable JNI checks</uicontrol></stentry>
+ <stentry>Disables JNI checks in the Dalvik runtime.</stentry>
+ </strow>
+ </simpletable>
+ </section>
+ <section>
+ <title>Others tab</title>
+ <simpletable>
+ <strow>
+ <stentry><uicontrol>Other Startup Options</uicontrol></stentry>
+ <stentry>Use this field to enter any other startup options not available from the other tabs. Enter the
+ startup options as documented in the Android developer documentation at <xref
+ href="http://d.android.com/guide/developing/tools/emulator.html#startup-options" format="html"
+ scope="external"
+ >http://d.android.com/guide/developing/tools/emulator.html#startup-options</xref>.</stentry>
+ </strow>
+ </simpletable>
+ </section>
+ </refbody>
+</reference>
diff --git a/src/help/studio_help/src/topics/u_code-generator.dita b/src/help/studio_help/src/topics/u_code-generator.dita
new file mode 100644
index 0000000..0d21031
--- /dev/null
+++ b/src/help/studio_help/src/topics/u_code-generator.dita
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE reference PUBLIC "-//OASIS//DTD DITA Reference//EN" "../dtd/reference.dtd">
+<reference id="u_code-generator" xml:lang="en-us">
+ <title>Generate Java Code Based on Layout dialog</title>
+ <shortdesc>Use this dialog to specify those UI objects for which attribute declarations, findViewById() calls, and
+ event handlers should be generated. This dialog appears when you right-click an Activity or Fragment subclass
+ (right-click either the editor window showing this subclass, or the subclass' filename in the Package Explorer) and
+ select <menucascade><uicontrol>Source</uicontrol><uicontrol>Generate Java Code Based on
+ Layout</uicontrol></menucascade>.</shortdesc>
+ <prolog>
+ <metadata>
+ <keywords>
+ <!--<indexterm></indexterm>-->
+ </keywords>
+ </metadata>
+ </prolog>
+ <refbody>
+ <section>
+ <simpletable>
+ <strow>
+ <stentry>
+ <uicontrol>Project</uicontrol>
+ </stentry>
+ <stentry>The project containing the Activity or Fragment for which code is to be generated.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Target Class</uicontrol></stentry>
+ <stentry>The Activity or Fragment subclass to which the generated code should be added. </stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Source Layout File</uicontrol></stentry>
+ <stentry>(read only) The name of the layout file (minus the ".xml" extension) controlled by the selected
+ target class file. The Java code generator determines the name of this file by examining the target class
+ file for a call to setContentView() in the Activity's onCreate() method (if the target class is an Activity)
+ or a call to inflater.inflate() (if the target class is a Fragment). </stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>UI Objects</uicontrol></stentry>
+ <stentry>The list of objects eligible for code generation. Code will be generated for those items that have
+ been selected using the checkbox in the <uicontrol>Id</uicontrol> column. For a UI object to be listed here,
+ it must have an Android ID defined with "@+id" in the layout file and there must not already be a
+ findViewById() statement for it in the selected target class file.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Generate default listeners when possible</uicontrol></stentry>
+ <stentry>If selected (this is the default), listeners will be generated for those selected UI elements that
+ can register them. </stentry>
+ </strow>
+ </simpletable>
+ </section>
+ </refbody>
+</reference>
diff --git a/src/help/studio_help/src/topics/u_collect-log-files.dita b/src/help/studio_help/src/topics/u_collect-log-files.dita
new file mode 100644
index 0000000..ac853d0
--- /dev/null
+++ b/src/help/studio_help/src/topics/u_collect-log-files.dita
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE reference PUBLIC "-//OASIS//DTD DITA Reference//EN" "../dtd/reference.dtd">
+<reference id="u_collect-log-files" xml:lang="en-us">
+ <title>Collect Log Files dialog</title>
+ <shortdesc>Using this dialog you can collect Eclipse and MOTODEV Studio log files and package them into a single ZIP
+ archive.</shortdesc>
+ <prolog>
+ <metadata>
+ <keywords>
+ <!--<indexterm></indexterm>-->
+ </keywords>
+ </metadata>
+ </prolog>
+ <refbody>
+ <section>
+ <p>This dialog appears when you select Collect Log Files from the Help menu.</p>
+ <simpletable>
+ <strow product="javame-studio javame-sdk webui">
+ <stentry><uicontrol>Directory</uicontrol></stentry>
+ <stentry>The directory into which the ZIP archive containing the log files should be written.</stentry>
+ </strow>
+ <strow product="javame-studio javame-sdk webui">
+ <stentry><uicontrol>File name</uicontrol></stentry>
+ <stentry>The name to be used for the archive file, minus the extension. An extension of <codeph>.zip</codeph>
+ will be added when the archive is created.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Output file</uicontrol></stentry>
+ <stentry>The path and filename for the archive file. Because this is a ZIP archive, you may want to give the
+ filename a <codeph>.zip</codeph> extension.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Log Files</uicontrol></stentry>
+ <stentry>A list of files that are eligible to be archived. Only those files that are selected will be
+ archived.</stentry>
+ </strow>
+ </simpletable>
+ </section>
+ </refbody>
+</reference>
diff --git a/src/help/studio_help/src/topics/u_db-classes-create.dita b/src/help/studio_help/src/topics/u_db-classes-create.dita
new file mode 100644
index 0000000..9d6f568
--- /dev/null
+++ b/src/help/studio_help/src/topics/u_db-classes-create.dita
@@ -0,0 +1,66 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE reference PUBLIC "-//OASIS//DTD DITA Reference//EN" "../dtd/reference.dtd">
+<reference id="u_db-classes-create" xml:lang="en-us">
+ <title>Create Database Management Classes</title>
+ <shortdesc>Creates classes you can use to manage and access your application's database: content providers for each of
+ the database's tables, and a SQL open helper that can create and if desired initialize your database at runtime. To
+ access this dialog you right-click your project in the Package Explorer and select <menucascade><uicontrol>Studio
+ for Android</uicontrol><uicontrol>Create Database Management Classes</uicontrol></menucascade>. Note that these
+ classes are generated based upon the schema from an existing database; thus, you must have a version of your
+ application's database on your development computer.</shortdesc>
+ <prolog>
+ <metadata>
+ <keywords>
+ <!--<indexterm></indexterm>-->
+ </keywords>
+ </metadata>
+ </prolog>
+ <refbody>
+ <section>
+ <simpletable>
+ <strow>
+ <stentry><uicontrol>Project</uicontrol></stentry>
+ <stentry>The project to which the database management classes are to be added.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Database File</uicontrol></stentry>
+ <stentry>The SQLite database file from which the classes are to be generated. Note that this database file
+ need not be in your current project; click <uicontrol>Filesystem</uicontrol> to select a database from
+ anywhere that is accessible to your development computer, or <uicontrol>Workspace</uicontrol> to select a
+ database from within the current project. If the database is not part of your current project, it will be
+ copied to the project's <codeph>assets</codeph> folder.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Generate SQL Open Helper</uicontrol></stentry>
+ <stentry>Adds a SQL Open Helper class to your project. The SQL Open Helper can automatically check for an
+ accessible database at runtime, and if necessary copy an initial database from the <codeph>assets</codeph>
+ folder within your APK to the proper location in <codeph>/data/data/</codeph>. This allows your application
+ to start with a pre-populated database on first launch.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>SQL Open Helper</uicontrol></stentry>
+ <stentry>If <uicontrol>Generate SQL Open Helper</uicontrol> is selected, specify the desired name of the
+ generated helper class using the <uicontrol>Name</uicontrol> field. Also indicate the source folder into
+ which the generated class is to be written using the <uicontrol>Source folder</uicontrol> field, and the
+ package of which the class is to be a member using the <uicontrol>Package</uicontrol> field.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Generate Content Providers for each table</uicontrol></stentry>
+ <stentry>Generates a content provider class for each table in the specified database. Content providers allow
+ you to create Live Folders that can show the contents of your database tables, and can make your database
+ tables accessible to other applications.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Content Provider</uicontrol></stentry>
+ <stentry>If <uicontrol>Generate Content Providers for each table</uicontrol> is selected, specify the source
+ folder into which the generated classes are to be written using the <uicontrol>Source Folder</uicontrol>
+ field, and the package of which the classes are to be a member using the <uicontrol>Package</uicontrol>
+ field. Each generated content provider class will be named for the table from which it was generated; the
+ class name will have the form <i>table_name</i><codeph>ContentProvider</codeph>. If the generated source
+ file names conflict with existing files within the specified source folder, the existing files will be
+ overwritten unless you clear the <uicontrol>Overwrite if it already exists</uicontrol> option.</stentry>
+ </strow>
+ </simpletable>
+ </section>
+ </refbody>
+</reference>
diff --git a/src/help/studio_help/src/topics/u_db-create.dita b/src/help/studio_help/src/topics/u_db-create.dita
new file mode 100644
index 0000000..3b5d7e0
--- /dev/null
+++ b/src/help/studio_help/src/topics/u_db-create.dita
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE reference PUBLIC "-//OASIS//DTD DITA Reference//EN" "../dtd/reference.dtd">
+<reference id="u_db-create" xml:lang="en-us">
+ <title>Create Database</title>
+ <shortdesc>Creates a new database, and optionally defines one or more new, empty tables within that database. To
+ access this dialog, select the project in which the database is to be created using the Package Explorer. Then,
+ either right-click the project and select <menucascade><uicontrol>MOTODEV Studio</uicontrol><uicontrol>Create
+ Database</uicontrol></menucascade> or select <menucascade><uicontrol>Manage Database</uicontrol><uicontrol
+ >Create Database</uicontrol></menucascade> from the MOTODEV menu.</shortdesc>
+ <prolog>
+ <metadata>
+ <keywords>
+ <!--<indexterm></indexterm>-->
+ </keywords>
+ </metadata>
+ </prolog>
+ <refbody>
+ <section>
+ <simpletable>
+ <strow>
+ <stentry><uicontrol>Database Name</uicontrol></stentry>
+ <stentry>The name of the SQLite database to be created.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Tables</uicontrol></stentry>
+ <stentry>Use this area to specify tables to be created within the new database. Clicking <uicontrol
+ >Add</uicontrol> brings up the Create New Table dialog, with which you specify the particulars of a given
+ database table. Note that you can have zero, one, or more database tables created within the new database.
+ As well, at any time you can manually use the Create New Table dialog to add, edit, or remove tables within
+ this database.</stentry>
+ </strow>
+ </simpletable>
+ </section>
+ </refbody>
+</reference>
diff --git a/src/help/studio_help/src/topics/u_db-database-explorer.dita b/src/help/studio_help/src/topics/u_db-database-explorer.dita
new file mode 100644
index 0000000..6a61014
--- /dev/null
+++ b/src/help/studio_help/src/topics/u_db-database-explorer.dita
@@ -0,0 +1,97 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE reference PUBLIC "-//OASIS//DTD DITA Reference//EN" "../dtd/reference.dtd">
+<reference id="u_db-database-explorer" xml:lang="en-us">
+ <title>MOTODEV Database Explorer</title>
+ <shortdesc>The MOTODEV Database Explorer lists all AVDs (Android Virtual Devices), connected devices, and your
+ workspace and allows you to see and explore their SQLite databases. You can also use it to create and edit databases
+ within projects in your current workspace.</shortdesc>
+ <prolog>
+ <metadata>
+ <keywords>
+ <!--<indexterm></indexterm>-->
+ </keywords>
+ </metadata>
+ </prolog>
+ <refbody>
+ <section>
+ <p>The MOTODEV Database Explorer lists all AVDs and connected devices. Note, however, that due to security
+ constraints you can view or modify SQLite databases only on handsets intended for development use.
+ Non-development handsets do not provide access to their databases.</p>
+ <p>The MOTODEV Database Explorer consists of a tree view that lists the following:</p>
+ <dl>
+ <dlentry>
+ <dt>Connected devices and running emulators</dt>
+ <dd>These are listed first in the MOTODEV Database Explorer view. Note that all Android AVDs are listed, not
+ just those that are running (AVDs listed with a disclosure triangle to the left of them are running; those
+ without a disclosure triangle are not runing). Connected devices are listed, but once they are disconnected
+ they are removed from the list. If the connected device or running emulator contains an SD card, disclosing
+ the device's applications also lists "External Storage"; you can map the databases on the device's SD card
+ for browsing or editing without having to mount the SD card for USB access.</dd>
+ </dlentry>
+ <dlentry>
+ <dt>Projects in your current workspace</dt>
+ <dd>Within the entry labeled "Workspace" are all of the Android projects found within your current workspace.
+ Those listed with a disclosure triangle to the left contain a database. Those without a disclosure triangle
+ do not currently contain a database (although you can add one).</dd>
+ </dlentry>
+ <dlentry>
+ <dt>Databases outside your current workspace that you have mapped</dt>
+ <dd>The "Filesystem" grouping in the MOTODEV Database Explorer view lists additional SQLite databases that you
+ have "mapped" into the view. This allows you to work with databases that are outside of your current
+ workspace. In particular, you can connect an Android handset to your development computer, mount its SD card
+ for USB access, and then map any databases on that SD card. You can then browse and edit those databases as
+ you would any other.</dd>
+ </dlentry>
+ </dl>
+ <p>You must connect to a database in order to view or modify its contents. When you connect to a database, a copy
+ of that database is made in your computer's Temp directory (if desired, you can change where this temporary copy
+ is made; see the MOTODEV Studio preferences). It is that copy that you view and edit. It is important to note
+ that changes you make to a database in MOTODEV Studio for Android are made to this local copy of the database;
+ only when you save your changes is the database copied back to the device or AVD.</p>
+ <p>The MOTODEV Database Explorer also provides the following controls:</p>
+ <simpletable>
+ <strow>
+ <stentry><image href="../images/collapse-all.png"/></stentry>
+ <stentry>Collapse All</stentry>
+ <stentry>Collapses the tree so that only the root nodes are showing.</stentry>
+ </strow>
+ <strow>
+ <stentry><image href="../images/refresh-sdks.png"/></stentry>
+ <stentry>Refresh</stentry>
+ <stentry>Re-scans the filesystem, workspace, and any running or connected devices looking for accessible
+ databases.</stentry>
+ </strow>
+ <strow>
+ <stentry><image href="../images/db-create-table.png"/></stentry>
+ <stentry>Create table</stentry>
+ <stentry>Defines a new table within a database</stentry>
+ </strow>
+ <strow>
+ <stentry><image href="../images/db-create-classes.png"/></stentry>
+ <stentry>Create Database Management Classes </stentry>
+ <stentry>Creates classes you can use to manage and access your application's database: content providers for
+ each of the database's tables, and a SQL open helper that can create and if desired initialize your database
+ at runtime.</stentry>
+ </strow>
+ <strow>
+ <stentry><image href="../images/browse-table-contents.png"/></stentry>
+ <stentry>Browse or edit table contents</stentry>
+ <stentry>Only active when a table is selected, this control presents the contents of the table in an editor
+ view, formatted much like a spreadsheet, with rows representing records and columns representing the fields
+ within each record. </stentry>
+ </strow>
+ <strow>
+ <stentry><image href="../images/open-scrapbook.png"/></stentry>
+ <stentry>Open scrapbook to edit SQL statements</stentry>
+ <stentry>Opens the SQL Scrapbook editor, from which you can execute arbitrary SQL statements.</stentry>
+ </strow>
+ </simpletable>
+ </section>
+ <section>
+ <title>Filtering the MOTODEV Database Explorer view</title>
+ <p>The Devices list initially shows all apps on each connected device (if accessible) or running emulator.
+ Right-click the device name and select <b>Filter applications without db</b> to eliminate those apps that don't
+ have SQLite databases. The resulting list will then only list applications that have SQLite databases.</p>
+ </section>
+ </refbody>
+</reference>
diff --git a/src/help/studio_help/src/topics/u_db-database-perspective.dita b/src/help/studio_help/src/topics/u_db-database-perspective.dita
new file mode 100644
index 0000000..e273603
--- /dev/null
+++ b/src/help/studio_help/src/topics/u_db-database-perspective.dita
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE reference PUBLIC "-//OASIS//DTD DITA Reference//EN" "../dtd/reference.dtd">
+<reference id="u_db-database-perspective" xml:lang="en-us">
+ <title>MOTODEV Database perspective</title>
+ <shortdesc>Using the MOTODEV Database perspective you can explore and edit local and on-device SQL databases.</shortdesc>
+ <prolog>
+ <metadata>
+ <keywords>
+ <!--<indexterm></indexterm>-->
+ </keywords>
+ </metadata>
+ </prolog>
+ <refbody>
+ <section>
+ <p>The MOTODEV Database perspective consists of the following views:</p>
+ <ul>
+ <li><xref href="u_db-database-explorer.dita">MOTODEV Database Explorer</xref></li>
+ <li><xref href="u_db-sql-results-view.dita">SQL Results view</xref></li>
+ <li><xref href="u_device-manager_android.dita">Device Management view</xref></li>
+ <li>Console view</li>
+ </ul>
+ <p>Open the MOTODEV Database perspective by clicking the <uicontrol>Window</uicontrol> menu and selecting
+ <menucascade><uicontrol>Open Perspective</uicontrol><uicontrol>Other</uicontrol></menucascade>. Then, from the
+ list of perspectives, select <uicontrol>MOTODEV Database</uicontrol>. </p>
+ </section>
+ </refbody>
+</reference>
diff --git a/src/help/studio_help/src/topics/u_db-sql-results-view.dita b/src/help/studio_help/src/topics/u_db-sql-results-view.dita
new file mode 100644
index 0000000..434e43d
--- /dev/null
+++ b/src/help/studio_help/src/topics/u_db-sql-results-view.dita
@@ -0,0 +1,102 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE reference PUBLIC "-//OASIS//DTD DITA Reference//EN" "../dtd/reference.dtd">
+<reference id="u_db-sql-results-view" xml:lang="en-us">
+ <title>SQL Results view</title>
+ <shortdesc>Displays the status and results of SQL statements entered using the SQL Scrapbook.</shortdesc>
+ <prolog>
+ <metadata>
+ <keywords>
+ <!--<indexterm></indexterm>-->
+ </keywords>
+ </metadata>
+ </prolog>
+ <refbody>
+ <section>
+ <p>The SQL Results view is divided into two halves. The left half consists of the following:</p>
+ <simpletable>
+ <strow>
+ <stentry>Filter field (unlabeled)</stentry>
+ <stentry>(this field is located above the table, and spans the entire left half of the view) Filters the rows
+ in the following table. Only those rows that have an operation containing the words listed here are
+ displayed. Clear this field to see all of the SQL operations that have been performed.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Status</uicontrol></stentry>
+ <stentry>Result of the corresponding SQL operation, such as <codeph>Failed</codeph> or <codeph
+ >Succeeded</codeph>.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Operation</uicontrol></stentry>
+ <stentry>SQL operation that was performed.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Date</uicontrol></stentry>
+ <stentry>When the SQL operation was performed.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Connection Profile</uicontrol></stentry>
+ <stentry>The connection profile that was used when performing the SQL operation.</stentry>
+ </strow>
+ </simpletable>
+ <p>Select a row in the table on the left side of the SQL Results view to see the actual results of the operation.
+ Depending upon the operation that was performed, the right half is usually organized into one or more of the
+ following tabs (if the "Display result in a single tab" control is selected, the right half will show the
+ results of the operation and the elapsed time in a single tab):</p>
+ <simpletable>
+ <strow>
+ <stentry><uicontrol>Status</uicontrol></stentry>
+ <stentry>Displays the SQL statement(s) executed, the status of the operation, and the elapsed time.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Parameters</uicontrol></stentry>
+ <stentry>Displays parameter names, data types, values, and parameter types for a routine procedural
+ object.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Result</uicontrol></stentry>
+ <stentry>Displays any output from the operation. For instance, if you execute a SELECT statement the table
+ rows are displayed here.</stentry>
+ </strow>
+ </simpletable>
+ <p>The SQL Results view contains the following controls:</p>
+ <simpletable>
+ <strow>
+ <stentry><image href="../images/terminate-result-icon.png"/></stentry>
+ <stentry>Terminate Result</stentry>
+ <stentry>Terminates a long-running SQL operation. Note that this control only active when a SQL operation is
+ in progress.</stentry>
+ </strow>
+ <strow>
+ <stentry><image href="../images/remove-result-icon.png"/></stentry>
+ <stentry>Remove Result</stentry>
+ <stentry>Deletes the selected result. This control is not active if you haven't selected a results
+ row.</stentry>
+ </strow>
+ <strow>
+ <stentry><image href="../images/remove-visible-results-icon.png"/></stentry>
+ <stentry>Remove all Visible Results</stentry>
+ <stentry>Deletes all of the displayed results. Note that you can use the Filter Results control (<image
+ href="../images/filter-results-icon.png"/>) and the filter field to select the set of results that is to
+ be deleted; this control does not delete results that aren't currently displayed.</stentry>
+ </strow>
+ <strow>
+ <stentry><image href="../images/display-single-tab-icon.png"/></stentry>
+ <stentry>Display result in a single tab</stentry>
+ <stentry>Presents the results of the selected SQL operation in a single tab.</stentry>
+ </strow>
+ <strow>
+ <stentry><image href="../images/display-text-mode-icon.png"/></stentry>
+ <stentry>Display result in text mode</stentry>
+ <stentry>Presents the results of the selected SQL operation in a set of tabs as described above.</stentry>
+ </strow>
+ <strow>
+ <stentry><image href="../images/filter-results-icon.png"/></stentry>
+ <stentry>Filter Results</stentry>
+ <stentry>Displays the SQL Results Filters dialog, from which you can filter the results in various
+ ways.</stentry>
+ </strow>
+ </simpletable>
+ <p>Right-click in the results to copy, save, export (as xml, text, or other), or print the displayed results.</p>
+ </section>
+ </refbody>
+</reference>
diff --git a/src/help/studio_help/src/topics/u_db-sql-scrapbook.dita b/src/help/studio_help/src/topics/u_db-sql-scrapbook.dita
new file mode 100644
index 0000000..d54f2ca
--- /dev/null
+++ b/src/help/studio_help/src/topics/u_db-sql-scrapbook.dita
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE reference PUBLIC "-//OASIS//DTD DITA Reference//EN" "../dtd/reference.dtd">
+<reference id="u_db-sql-scrapbook" xml:lang="en-us">
+ <title>SQL Scrapbook</title>
+ <shortdesc>From the SQL Scrapbook views (you can have multiple SQL Scrapbook views) you can enter and execute
+ arbitrary SQL. This allows you to perform more complex SQL operations than you can with the UI controls provided by
+ the MOTODEV Database perspective.</shortdesc>
+ <prolog>
+ <metadata>
+ <keywords>
+ <!--<indexterm></indexterm>-->
+ </keywords>
+ </metadata>
+ </prolog>
+ <refbody>
+ <section>
+ <p>The SQL Scrapbook view is divided into two parts. The upper portion contains the Connection profile and
+ identifies the database to which the view is connected (if any). The lower part is a single large text field
+ into which you enter your SQL statements. </p>
+ <p>The Connection profile consists of the following fields:</p>
+ <simpletable>
+ <strow>
+ <stentry><uicontrol>Type</uicontrol></stentry>
+ <stentry>Database type. For instance, <codeph>SQLITE_3.5.9</codeph>. The type is automatically filled in when
+ you connect to a database from the SQL Scrapbook view.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Name</uicontrol></stentry>
+ <stentry>Full name of the connection profile. For instance, <codeph
+ >myAVD.com.motorola.example.database.books</codeph>. Select a profile from this list to connect to
+ it.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Database</uicontrol></stentry>
+ <stentry>Database name. For instance, <codeph>books</codeph></stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Status</uicontrol></stentry>
+ <stentry>Database status. For instance, <codeph>Connected, Auto Commit</codeph></stentry>
+ </strow>
+ </simpletable>
+ <p>You must be connected to a database from within the Connection profile to be able to execute SQL statements.</p>
+ <p>To execute a single SQL statement, enter the statement in the large text area, or right-click in this area and
+ select <uicontrol>Edit in SQL Query Builder</uicontrol> to construct your SQL query statement interactively.
+ Then, right-click the statement and select <uicontrol>Execute Current Text</uicontrol>. Note that you can
+ execute all of the SQL statements in the text are with <uicontrol>Execute All</uicontrol>, or a set of
+ statements by selecting them and then selecting one of the <uicontrol>Execute Selected Text</uicontrol> commands
+ from the context-sensitive menu.</p>
+ <p>The execution results appear in the SQL Results view. </p>
+ <note>The SQL Scrapbook is not connected to the MOTODEV Database Explorer; if the MOTODEV Database Explorer is
+ opened when you run a command in the SQL Scrapbook view, any database changes that result from that command are
+ not automatically reflected in the MOTODEV Database Explorer. You will need to manually refresh the MOTODEV
+ Database Explorer view to see the changes.</note>
+ </section>
+ </refbody>
+</reference>
diff --git a/src/help/studio_help/src/topics/u_db-table-create.dita b/src/help/studio_help/src/topics/u_db-table-create.dita
new file mode 100644
index 0000000..c43487f
--- /dev/null
+++ b/src/help/studio_help/src/topics/u_db-table-create.dita
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE reference PUBLIC "-//OASIS//DTD DITA Reference//EN" "../dtd/reference.dtd">
+<reference id="u_db-table-create" xml:lang="en-us">
+ <title>Create New Table</title>
+ <shortdesc>Defines a new, empty table within an existing database. To access this dialog, switch to the MOTODEV
+ Database perspective. Then, using the MOTODEV Database Explorer, right-click a connected database and select
+ <uicontrol>Create Table</uicontrol>.</shortdesc>
+ <prolog>
+ <metadata>
+ <keywords>
+ <!--<indexterm></indexterm>-->
+ </keywords>
+ </metadata>
+ </prolog>
+ <refbody>
+ <section>
+ <simpletable>
+ <strow>
+ <stentry><uicontrol>Table Name</uicontrol></stentry>
+ <stentry>The name of the SQLite database table to be created.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Add</uicontrol></stentry>
+ <stentry>Adds a new column to the database table. You will be prompted for the column name, type, and an
+ optional default value. You can also indicate whether the column represents a primary key, and, if so,
+ whether the key should be generated automatically.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Edit</uicontrol></stentry>
+ <stentry>Edits a selected table column. </stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Remove</uicontrol></stentry>
+ <stentry>Deletes a selected table column.</stentry>
+ </strow>
+ </simpletable>
+ </section>
+ </refbody>
+</reference>
diff --git a/src/help/studio_help/src/topics/u_device-app-uninstall.dita b/src/help/studio_help/src/topics/u_device-app-uninstall.dita
new file mode 100644
index 0000000..9ce7257
--- /dev/null
+++ b/src/help/studio_help/src/topics/u_device-app-uninstall.dita
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE reference PUBLIC "-//OASIS//DTD DITA Reference//EN" "../dtd/reference.dtd">
+<reference id="u_device-app-uninstall" xml:lang="en-us">
+ <title>Uninstall Application dialog</title>
+ <shortdesc>Allows you to remove applications from a running emulator or connected device. To bring up this dialog,
+ from the Device Management view right-click the running emulator or connected device and select <uicontrol>Uninstall
+ App</uicontrol>. </shortdesc>
+ <prolog>
+ <metadata>
+ <keywords>
+ <!--<indexterm></indexterm>-->
+ </keywords>
+ </metadata>
+ </prolog>
+ <refbody>
+ <section>
+ <p>This dialog presents a list of applications (identified by package name) that can be deleted from the selected
+ device. Note that not all applications are listed: only those that can be uninstalled. Click an application to
+ select it. To select multiple applications, Control-click them (on Mac OS X, Command-click the applications).
+ Selected applications will not be uninstalled until you click <uicontrol>Finish</uicontrol>.</p>
+ </section>
+ </refbody>
+</reference>
diff --git a/src/help/studio_help/src/topics/u_device-manager_android.dita b/src/help/studio_help/src/topics/u_device-manager_android.dita
new file mode 100644
index 0000000..6d18357
--- /dev/null
+++ b/src/help/studio_help/src/topics/u_device-manager_android.dita
@@ -0,0 +1,149 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE reference PUBLIC "-//OASIS//DTD DITA Reference//EN" "../dtd/reference.dtd">
+<reference id="u_device-manager_android" xml:lang="en-us">
+ <title>Device Management view</title>
+ <shortdesc>Lists available devices (both physical and virtual) and allows you to install applications to them.</shortdesc>
+ <prolog>
+ <metadata>
+ <keywords>
+ <!--<indexterm></indexterm>-->
+ </keywords>
+ </metadata>
+ </prolog>
+ <refbody>
+ <section>
+ <p>The Device Management view displays any connected Android devices and any available Android Virtual Devices
+ (AVDs), identified by name and categorized by type. For each device, the Devices column also includes the AVD
+ name and operating system version (for running emulated devices) or device ID and operating system version (for
+ real devices). The Status column indicates whether the device is online (an AVD that is online is one that is
+ running) or offline. If an AVD shows "no user data", that AVD has no associated user data: either it has never
+ been run, or it has been reset since it was last run.</p>
+ <p>Right-click a device to display a menu that varies depend upon the type of device you have selected. When a
+ physical device is selected you can open an ADB shell window or install an application to the device. When an
+ AVD is selected you can also start, stop, or reset the virtual device, and you can open an Android console
+ window.</p>
+ <simpletable>
+ <strow>
+ <stentry><image href="../images/tml-new-instance.png"/></stentry>
+ <stentry>Creates a new device. Use this to create new AVD instances.</stentry>
+ </strow>
+ <strow>
+ <stentry><image href="../images/refresh-device-list-button_android.png"/></stentry>
+ <stentry>Refreshes the list of devices and their status indications.</stentry>
+ </strow>
+ <strow>
+ <stentry><image href="../images/menu-button.png"/></stentry>
+ <stentry>Displays a menu that allows you to create a new device.</stentry>
+ </strow>
+ </simpletable>
+ <p>The context-sensitive menu that appears when you right-click a device has some or all of the following commands
+ (depending upon whether the device is real or emulated).</p>
+ <simpletable>
+ <sthead>
+ <stentry>Command</stentry>
+ <stentry>Icon</stentry>
+ <stentry>Function</stentry>
+ </sthead>
+ <strow>
+ <stentry><uicontrol>New</uicontrol></stentry>
+ <stentry><image href="../images/dev-mgr_new-icon.png"/></stentry>
+ <stentry>Creates a new AVD.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Delete</uicontrol></stentry>
+ <stentry><image href="../images/loc_editor-remove-line-button.png"/></stentry>
+ <stentry>Deletes an existing AVD.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Properties</uicontrol></stentry>
+ <stentry/>
+ <stentry>Opens a dialog from which you can view and edit some of the device's properties.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>ADB Shell</uicontrol></stentry>
+ <stentry><image href="../images/dev-mgr_adb-shell-icon.png"/></stentry>
+ <stentry>Opens a new ADB (Android Debug Bridge) shell console from which you can issue adb commands. These
+ commands allow you to manage the state of connected Android devices and AVDs. See the Android SDK
+ documentation at <xref href="http://developer.android.com/guide/developing/tools/adb.html#shellcommands"
+ scope="external" format="html">http://developer.android.com/guide/developing/tools/adb.html</xref> for
+ information on the commands you can issue.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Console</uicontrol></stentry>
+ <stentry><image href="../images/dev-mgr_console-icon.png"/></stentry>
+ <stentry>Opens a new Emulator Console connected to the selected emulator instance. You can use the Emulator
+ Console to simulate hardware events, simulate telephony events, and control various aspects of the emulated
+ device. For more information see the Android SDK documentation for the Emulator Console at <xref
+ href="http://developer.android.com/guide/developing/tools/emulator.html#console" scope="external"
+ format="html">http://developer.android.com/guide/developing/tools/emulator.html#console</xref>.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Install app</uicontrol></stentry>
+ <stentry><image href="../images/dev-mgr_install-app-icon.png"/></stentry>
+ <stentry>Prompts you for an application package, and then installs the package to the selected device. Note
+ that the target device must be running. Also note that you can install apps simply by dragging and dropping
+ one or more APKs onto a running AVD or connected handset listed in the Device Management view.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Uninstall app</uicontrol></stentry>
+ <stentry><image href="../images/dev-mgr_uninstall-app-icon.png"/></stentry>
+ <stentry>Lists applications that can be uninstalled, and uninstalls those that you have selected.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Take Screenshot</uicontrol></stentry>
+ <stentry><image href="../images/dev-mgr_take-screenshot-icon.png"/></stentry>
+ <stentry>Captures an image of the device's screen. This function works for both real and emulated
+ devices.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Test events with Monkey</uicontrol></stentry>
+ <stentry><image href="../images/dev-mgr_monkey-test-icon.png"/></stentry>
+ <stentry>Creates a launch configuration for the Android UI/Application Exerciser Monkey, which tests your apps
+ by sending pseudo-random events to them.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Language</uicontrol></stentry>
+ <stentry><image href="../images/dev-mgr_language-icon.png"/></stentry>
+ <stentry>Changes the locale of the selected emulator instance. The emulator must be running. This feature is
+ particularly useful when testing localized versions of your applications. Note that it lets you change the
+ emulated device's locale to any language/country combination, whether or not the device has the specified
+ locale installed. If the locale is not installed the device defaults to English, but within your application
+ your localized strings.xml file is used.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Reset</uicontrol></stentry>
+ <stentry><image href="../images/dev-mgr_reset-icon.png"/></stentry>
+ <stentry>Deletes all of the selected emulator's data. This has the same effect as deleting and recreating the
+ AVD.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Stop</uicontrol></stentry>
+ <stentry><image href="../images/dev-mgr_stop-icon.png"/></stentry>
+ <stentry>Stops the selected emulator instance.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Repair</uicontrol></stentry>
+ <stentry><image href="../images/dev-mgr_repair-icon.png"/></stentry>
+ <stentry>Tries to repair a corrupt AVD (depending upon the problem) so that it can once again be
+ used.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Start</uicontrol></stentry>
+ <stentry><image href="../images/dev-mgr_start-icon.png"/></stentry>
+ <stentry>Starts the selected emulator instance.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Analyze Memory with MAT</uicontrol></stentry>
+ <stentry><image href="../images/dev-mgr_mat-analyze-icon.png"/></stentry>
+ <stentry>Generates an HPROF file and then runs the Memory Analyzer (MAT). MAT analyzes the Java heap and can
+ be extremely helpful when trying to track down memory leaks. It is also a valuable tool when trying to
+ reduce your app's memory consumption.</stentry>
+ </strow>
+ </simpletable>
+ <p>The toolbar icons across the top of the Device Management view correspond to many of the above commands. These
+ icons are enabled and disabled as appropriate for the selected devices. Note that they enable you to issue
+ commands to more than one device at a time. For instance, you can start multiple AVDs by selecting them and
+ clicking <image href="../images/start-avd-button.png"/>.</p>
+ </section>
+ </refbody>
+</reference>
diff --git a/src/help/studio_help/src/topics/u_device-properties_android.dita b/src/help/studio_help/src/topics/u_device-properties_android.dita
new file mode 100644
index 0000000..13ff567
--- /dev/null
+++ b/src/help/studio_help/src/topics/u_device-properties_android.dita
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE reference PUBLIC "-//OASIS//DTD DITA Reference//EN" "../dtd/reference.dtd">
+<reference id="u_device-properties_android" xml:lang="en-us">
+ <title>Device Properties</title>
+ <shortdesc>Presents a detailed list of device properties for the chosen connected device or running AVD (if the AVD is
+ not running, this dialog will be empty).</shortdesc>
+ <prolog>
+ <metadata>
+ <keywords>
+ <!--<indexterm></indexterm>-->
+ </keywords>
+ </metadata>
+ </prolog>
+ <refbody>
+ <section>This dialog consists of a scrolling two-column table in which the first column is the property name and the
+ second is the value of that property. To export these properties to a text file in comma-separated-value (CSV)
+ format, click <uicontrol>CSV Export</uicontrol> and specify the destination.</section>
+ </refbody>
+</reference>
diff --git a/src/help/studio_help/src/topics/u_device-screen-capture.dita b/src/help/studio_help/src/topics/u_device-screen-capture.dita
new file mode 100644
index 0000000..05a34bb
--- /dev/null
+++ b/src/help/studio_help/src/topics/u_device-screen-capture.dita
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE reference PUBLIC "-//OASIS//DTD DITA Reference//EN" "../dtd/reference.dtd">
+<reference id="u_device-screen-capture" xml:lang="en-us">
+ <title>Device Screen Capture view</title>
+ <shortdesc>The Device Screen Capture view presents a snapshot of a real or emulated device screen. From this dialog
+ you can save the image as a PNG file or copy it to the clipboard. From this dialog you can also capture additional
+ screen images from the selected device.</shortdesc>
+ <prolog>
+ <metadata>
+ <keywords>
+ <!--<indexterm></indexterm>-->
+ </keywords>
+ </metadata>
+ </prolog>
+ <refbody>
+ <section>
+ <p>The Device Screen Capture view appears when you right-click a device in the Device Management view (either a
+ connected device listed under <uicontrol>Android Handset</uicontrol> or an emulated device listed under
+ <uicontrol>Android Virtual Device</uicontrol>) and select <uicontrol>Take Screenshot</uicontrol>. Note that
+ the captured screen isn't automatically saved or copied to the clipboard; you need to use the <uicontrol
+ >Save</uicontrol> or <uicontrol>Copy</uicontrol> buttons to preserve the image.</p>
+ </section>
+ <simpletable>
+ <strow>
+ <stentry><uicontrol>Refresh</uicontrol></stentry>
+ <stentry>Recaptures the device screen. By manipulating the device and clicking <uicontrol>Refresh</uicontrol>,
+ you can easily capture a series of screen images.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Rotate</uicontrol></stentry>
+ <stentry>Rotates the captured image 90 degrees counterclockwise. Successive clicks of the <uicontrol
+ >Rotate</uicontrol> button continue to rotate the image; two clicks turn the image upside down, for
+ instance.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Save</uicontrol></stentry>
+ <stentry>Saves the image as a PNG file in a location you specify.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Copy</uicontrol></stentry>
+ <stentry>Copies the image to the computer's clipboard.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Done</uicontrol></stentry>
+ <stentry>Closes the Device Screen Capture dialog. Note that if you haven't saved the image or copied it to the
+ clipboard, the image won't be preserved.</stentry>
+ </strow>
+ </simpletable>
+ </refbody>
+</reference>
diff --git a/src/help/studio_help/src/topics/u_download-components.dita b/src/help/studio_help/src/topics/u_download-components.dita
new file mode 100644
index 0000000..6cf74e0
--- /dev/null
+++ b/src/help/studio_help/src/topics/u_download-components.dita
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE reference PUBLIC "-//OASIS//DTD DITA Reference//EN" "../dtd/reference.dtd">
+<reference id="u_download-components" xml:lang="en-us">
+ <title>Download Components dialog</title>
+ <shortdesc>The Download Components dialog allows you to specify or download an SDK, and install support for native
+ (C/C++) code development. To open this dialog, select <uicontrol>Download components</uicontrol> from the
+ <uicontrol>MOTODEV</uicontrol> menu.</shortdesc>
+ <prolog>
+ <metadata>
+ <keywords>
+ <!--<indexterm></indexterm>-->
+ </keywords>
+ </metadata>
+ </prolog>
+ <refbody>
+ <section>
+ <p>The left side of the Download Components dialog lists the types of components you can download and install. (If
+ you don't see this list in the Download Components dialog, click the <uicontrol>More</uicontrol> button near the
+ bottom of the dialog.) Selecting a component type alters the right side of the dialog; on this side are listed
+ those components of the selected type that you have not yet installed. From here you can select individual
+ components to be installed.</p>
+ <p>The supported component types are (not all may be listed in the dialog, depending upon component availability):</p>
+ <simpletable>
+ <strow>
+ <stentry><uicontrol>SDK</uicontrol></stentry>
+ <stentry>Software development kit (SDK). Use this option to specify an SDK (if you have already downloaded
+ one) or download one (by clicking <uicontrol>Download</uicontrol>) if you have not. Note that the Android
+ SDK by itself is not enough to develop with: you will also need one or more platforms. After downloading or
+ selecting the SDK, use the Android SDK and AVD Manager (which you can access using the link provided below
+ the SDK Location field) to download platforms. You also use the Android SDK and AVD Manager to update SDKs
+ and platforms; this dialog is primarily just for new installations to link MOTODEV Studio to the Android
+ SDK.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Native Support</uicontrol></stentry>
+ <stentry>Supports native (C/C++) code development, in addition to Java. In order to use this option you must
+ have separately downloaded and installed the Android NDK. Additionally, the JDK (not just the JRE) must be
+ installed, and if your development computer is running Microsoft Windows you must have Cygwin installed.
+ Verify the correct installation and configuration of each before enabling native support in MOTODEV
+ Studio.</stentry>
+ </strow>
+ </simpletable>
+ </section>
+ </refbody>
+</reference>
diff --git a/src/help/studio_help/src/topics/u_emulator-language.dita b/src/help/studio_help/src/topics/u_emulator-language.dita
new file mode 100644
index 0000000..8ca2b3c
--- /dev/null
+++ b/src/help/studio_help/src/topics/u_emulator-language.dita
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE reference PUBLIC "-//OASIS//DTD DITA Reference//EN" "../dtd/reference.dtd">
+<reference id="u_emulator-language" xml:lang="en-us">
+ <title>Emulator Language dialog</title>
+ <shortdesc>Changes the emulated device's configuration to a locale that you specify as a combination of language and
+ region (or country). Note that this operation causes the emulator to restart.</shortdesc>
+ <prolog>
+ <metadata>
+ <keywords>
+ <!--<indexterm></indexterm>-->
+ </keywords>
+ </metadata>
+ </prolog>
+ <refbody>
+ <section>
+ <p>This function (accessible by right-clicking a running emulator from the Device Management view and selecting
+ <uicontrol>Language</uicontrol>) changes the emulator settings to the selected locale using <codeph>setprop
+ persist</codeph> adb shell commands. The emulator is then restarted so that the settings can take effect. Note
+ that not all emulators support all of the listed languages and countries; if you select an unsupported locale
+ the system displays using its default language. However, your application should localize based upon the
+ selected locale.</p>
+ <p>A locale is specified as a combination of language and region (or country).</p>
+ </section>
+ <section>
+ <simpletable>
+ <strow>
+ <stentry><uicontrol>Language</uicontrol></stentry>
+ <stentry>The language the device should use. Select from the provided list of languages.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Country</uicontrol></stentry>
+ <stentry>The region that uses the desired language. Select from the provided list.</stentry>
+ </strow>
+ </simpletable>
+ </section>
+ <section>Note that you can instead change the emulator's locale using the <uicontrol>Custom Locale</uicontrol>
+ application that is accessible from the Application tab on the emulated device.</section>
+ </refbody>
+</reference>
diff --git a/src/help/studio_help/src/topics/u_emulator_android.dita b/src/help/studio_help/src/topics/u_emulator_android.dita
new file mode 100644
index 0000000..cf7652d
--- /dev/null
+++ b/src/help/studio_help/src/topics/u_emulator_android.dita
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE reference PUBLIC "-//OASIS//DTD DITA Reference//EN" "../dtd/reference.dtd">
+<reference id="u_emulator_android" xml:lang="en-us">
+ <title>Android Emulator view</title>
+ <shortdesc>The Android Emulator view allows you to see and interact with a running AVD. This allows you to test your
+ applications without needing an Android device, all from within MOTODEV Studio for Android. Note that this view
+ displays all running AVDs; if there are none, the view is empty. </shortdesc>
+ <prolog>
+ <metadata>
+ <keywords>
+ <!--<indexterm></indexterm>-->
+ </keywords>
+ </metadata>
+ </prolog>
+ <refbody>
+ <section>This view presents the complete AVD image, including the virtual keyboard if the image includes one. Use
+ the scroll bars to move around the image, and the zoom controls to resize the image within the view. Select
+ between multiple running AVDs by clicking the tabs that identify each by name. Use the <xref
+ href="u_device-manager_android.dita">Device Management view</xref> to start and stop an AVD within this view or
+ to install applications to the running AVD.</section>
+ <section>To open the Android Emulator view, either launch an AVD (which will open the Android Emulator View
+ automatically) or click the <uicontrol>Window</uicontrol> menu and select <menucascade><uicontrol>Show
+ View</uicontrol><uicontrol>Android Emulator</uicontrol></menucascade>. <simpletable>
+ <strow>
+ <stentry><image href="../images/emulator-zoom-in.png"/></stentry>
+ <stentry>Zooms in, enlarging the image of the emulated Android device.</stentry>
+ </strow>
+ <strow>
+ <stentry><image href="../images/emulator-zoom-out.png"/></stentry>
+ <stentry>Zooms out, shrinking the image of the emulated Android device.</stentry>
+ </strow>
+ <strow>
+ <stentry><image href="../images/emulator-switch-layout.png"/></stentry>
+ <stentry>Click the handset icon to toggle between portrait and landscape orientations. Click the small black
+ triangle to select a specific orientation.</stentry>
+ </strow>
+ <strow>
+ <stentry><image href="../images/menu-button.png"/></stentry>
+ <stentry>Opens a menu with the following entries:<ul>
+ <li><b>Zoom</b>: allows you to scale the AVD image to various sizes.</li>
+ <li><b>Switch Layout</b>: lets you select between portrait and landscape orientations.</li>
+ </ul></stentry>
+ </strow>
+ </simpletable></section>
+ </refbody>
+</reference>
diff --git a/src/help/studio_help/src/topics/u_keystore-select.dita b/src/help/studio_help/src/topics/u_keystore-select.dita
new file mode 100644
index 0000000..32e819c
--- /dev/null
+++ b/src/help/studio_help/src/topics/u_keystore-select.dita
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE reference PUBLIC "-//OASIS//DTD DITA Reference//EN" "../dtd/reference.dtd">
+<reference id="u_keystore-select" xml:lang="en-us">
+ <title>Import Keystore (during package export) dialog</title>
+ <shortdesc>Allows you to select a keystore that isn't known to the Signing and Keys view for use during the package export process. </shortdesc>
+ <prolog>
+ <metadata>
+ <keywords>
+ <!--<indexterm></indexterm>-->
+ </keywords>
+ </metadata>
+ </prolog>
+ <refbody>
+ <section>
+ <p>This dialog is displayed when you click <uicontrol>Use existing</uicontrol> in the Export Android Package Files dialog while exporting an Android package using MOTODEV Studio's export wizard.</p>
+ <simpletable>
+ <strow>
+ <stentry><uicontrol>Keystore Filename</uicontrol></stentry>
+ <stentry>Path to the keystore file to be used.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Keystore Type</uicontrol></stentry>
+ <stentry>List of keystore types (JKS, JCEKS, or PCKS12). The keystore being used must be one of the listed
+ types, and you must identify the keystore's type using this list.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Keystore Password</uicontrol></stentry>
+ <stentry>The password, specified when the keystore was created, that protects this keystore. You must supply
+ this password.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Save this password</uicontrol></stentry>
+ <stentry>Select this option if you want MOTODEV Studio to automatically supply the password when you add keys
+ to this keystore or delete keys within this keystore. Leave this option unselected if you want MOTODEV
+ Studio to prompt for the password for each operation.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Also import this to Signing and Keys view</uicontrol></stentry>
+ <stentry>Indicates that this keystore should also be added to the Signing and Keys view, for subsequent easy
+ access.</stentry>
+ </strow>
+ </simpletable>
+ </section>
+ </refbody>
+</reference>
diff --git a/src/help/studio_help/src/topics/u_memory-analyze-app-select.dita b/src/help/studio_help/src/topics/u_memory-analyze-app-select.dita
new file mode 100644
index 0000000..957cf70
--- /dev/null
+++ b/src/help/studio_help/src/topics/u_memory-analyze-app-select.dita
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE reference PUBLIC "-//OASIS//DTD DITA Reference//EN" "../dtd/reference.dtd">
+<reference id="u_memory-analyze-app-select" xml:lang="en-us">
+ <title>Analyze Memory Using MAT dialog</title>
+ <shortdesc>MOTODEV Studio for Android can generate an HPROF file for a selected Android application and then pass it
+ to MAT, the Eclipse memory analyzer. Using MAT you can look for memory leaks and locate areas where your app might
+ be using an excessive amount of memory.</shortdesc>
+ <prolog>
+ <metadata>
+ <keywords>
+ <!--<indexterm></indexterm>-->
+ </keywords>
+ </metadata>
+ </prolog>
+ <refbody>
+ <section>The first step in the memory analysis process is to select the running application to be analyzed. The
+ Analyze Memory Using MAT dialog allows you to do this: select the application's package from the list and click
+ <uicontrol>Finish</uicontrol>.</section>
+ </refbody>
+</reference>
diff --git a/src/help/studio_help/src/topics/u_mpkg-sign_android.dita b/src/help/studio_help/src/topics/u_mpkg-sign_android.dita
new file mode 100644
index 0000000..b1f5c2b
--- /dev/null
+++ b/src/help/studio_help/src/topics/u_mpkg-sign_android.dita
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE reference PUBLIC "-//OASIS//DTD DITA Reference//EN" "../dtd/reference.dtd">
+<reference id="u_mpkg-sign_android" xml:lang="en-us">
+ <title>Package Signing dialog</title>
+ <shortdesc>Signs one or more Android packages with a public/private key pair. Note that in order to sign multiple
+ packages at once, they must all reside in the same directory. </shortdesc>
+ <prolog>
+ <metadata>
+ <keywords>
+ <!--<indexterm></indexterm>-->
+ </keywords>
+ </metadata>
+ </prolog>
+ <refbody>
+ <section>This dialog is displayed when you click <image href="../images/PackageSign.png"/> (Sign Android Package) in
+ the Signing and Keys view, when you right click in the Package Explorer and select <menucascade>
+ <uicontrol>MOTODEV Studio</uicontrol>
+ <uicontrol>Sign Android Package</uicontrol>
+ </menucascade>, or when you select <menucascade>
+ <uicontrol>Signing</uicontrol>
+ <uicontrol>Sign Android Package</uicontrol>
+ </menucascade> from the MOTODEV menu. <simpletable>
+ <strow>
+ <stentry><uicontrol>Packages folder</uicontrol></stentry>
+ <stentry>Path to the folder in which the Android package (.apk) files reside.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Keystore</uicontrol></stentry>
+ <stentry>Drop-down list that enumerates the keystores that are known to the Signing and Keys view. Select the
+ keystore that contains the key to be used for signing.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Keystore Password</uicontrol></stentry>
+ <stentry>Supply the password for the selected keystore. If you previously indicated that the keystore password
+ should be saved (either when you created the keystore or during a previous signing operation), the keystore
+ password will be filled in for you.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Save this password</uicontrol></stentry>
+ <stentry>Select this option if you want the keystore password to be saved and then automatically filled in
+ when you use this keystore during future signing operations.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Key</uicontrol></stentry>
+ <stentry>Public/private key pair to be used to sign the Android packages. The drop-down list allows you to
+ select from among the key pairs within the selected keystore. </stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Select the packages</uicontrol></stentry>
+ <stentry>Lists the Android package files found in the directory specified by <uicontrol>Packages
+ folder</uicontrol>. Select at least one of them to be signed.</stentry>
+ </strow>
+ </simpletable>
+ <p>After you click <uicontrol>Finish</uicontrol> to begin the signing process, you may be prompted for the
+ selected key's password. If so, supply it in the field provided. If you had previously indicated that the key's
+ password should be saved (either when you created the key or during a previous signing operation), the saved
+ password will be used and this secondary dialog will not be displayed.</p>
+ </section>
+ </refbody>
+</reference>
diff --git a/src/help/studio_help/src/topics/u_mpkg-unsign_android.dita b/src/help/studio_help/src/topics/u_mpkg-unsign_android.dita
new file mode 100644
index 0000000..796e744
--- /dev/null
+++ b/src/help/studio_help/src/topics/u_mpkg-unsign_android.dita
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE reference PUBLIC "-//OASIS//DTD DITA Reference//EN" "../dtd/reference.dtd">
+<reference id="u_mpkg-unsign_android" xml:lang="en-us">
+ <title>Package Signature Removal dialog</title>
+ <shortdesc>Removes all signatures from one or more signed Android packages (APKs). Note that in order to remove
+ signatures from multiple packages at once, they must all reside in the same directory. </shortdesc>
+ <prolog>
+ <metadata>
+ <keywords>
+ <!--<indexterm></indexterm>-->
+ </keywords>
+ </metadata>
+ </prolog>
+ <refbody>
+ <section>
+ <p>This dialog is displayed when you click <image href="../images/PackageUnsign.png"/> (Remove Package Signatures)
+ in the Signing and Keys view, when you right click in the Package Explorer and select <menucascade>
+ <uicontrol>MOTODEV Studio</uicontrol>
+ <uicontrol>Remove Package Signatures</uicontrol>
+ </menucascade>, or when you select <menucascade>
+ <uicontrol>Signing</uicontrol>
+ <uicontrol>Remove Package Signatures</uicontrol>
+ </menucascade> from the MOTODEV menu.</p>
+ <simpletable>
+ <strow>
+ <stentry><uicontrol>Packages folder</uicontrol></stentry>
+ <stentry>Path to the folder in which the signed Android package (.apk) files reside.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Select the packages</uicontrol></stentry>
+ <stentry>Lists the Android package files found in the directory specified by <uicontrol>Packages
+ folder</uicontrol>. Select at least one of them.</stentry>
+ </strow>
+ </simpletable>
+ </section>
+ </refbody>
+</reference>
diff --git a/src/help/studio_help/src/topics/u_new-activity-template.dita b/src/help/studio_help/src/topics/u_new-activity-template.dita
new file mode 100644
index 0000000..780ea4f
--- /dev/null
+++ b/src/help/studio_help/src/topics/u_new-activity-template.dita
@@ -0,0 +1,91 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE reference PUBLIC "-//OASIS//DTD DITA Reference//EN" "../dtd/reference.dtd">
+<reference id="u_new-activity-template" xml:lang="en-us">
+ <title>New Activity Based on Template wizard</title>
+ <shortdesc>Creates a new Android activity based upon a sample you have chosen.</shortdesc>
+ <prolog>
+ <metadata>
+ <keywords>
+ <!--<indexterm></indexterm>-->
+ </keywords>
+ </metadata>
+ </prolog>
+ <refbody>
+ <section>
+ <p>When adding a new activity to your Android project, MOTODEV Studio enables you to quickly and easily add either
+ a basic (mostly empty) activity, or one based upon a sample. MOTODEV Studio includes a number of sample
+ activities that, with minor modifications, can be incorporated into your applications and will perform various
+ functions commonly needed by many Android applications.</p>
+ <p>To create a basic, empty activity you select <menucascade><uicontrol>New</uicontrol><uicontrol>Android
+ Activity</uicontrol></menucascade> from the <uicontrol>File</uicontrol> menu. If you want to base your
+ activity on one of the supplied sample activities, either select <menucascade><uicontrol
+ >New</uicontrol><uicontrol>Android Activity</uicontrol></menucascade> from the <uicontrol>File</uicontrol>
+ menu and then click <uicontrol>Create New Activity Based On Template</uicontrol> from the dialog that appears,
+ or select <menucascade><uicontrol>Auto-generated code</uicontrol><uicontrol>Create Activity based on
+ template</uicontrol></menucascade> from the <uicontrol>MOTODEV</uicontrol> menu. Either method invokes the New
+ Android Activity Based on Template wizard.</p>
+ <p>The New Android Activity Based on Template wizard begins by having you select the sample activity on which your
+ project's new activity is to be based. (Note that one of the samples--Database List--lists the contents of
+ selected columns within your application's SQLite database; in order to select this sample your application must
+ already have a database in its <codeph>assets</codeph> folder.) Select the template activity, and click
+ <uicontrol>Next</uicontrol>. You will then be presented with a dialog containing a number of fields with which
+ you specify various aspects of the activity to be created.</p>
+ <simpletable>
+ <strow>
+ <stentry><uicontrol>Source folder</uicontrol></stentry>
+ <stentry>The folder into which the source code for the new activity is to be stored. By default this is the
+ current project's src folder.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Package</uicontrol></stentry>
+ <stentry>The package that is to contain the new activity. By default this is the current project's
+ package.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Name</uicontrol></stentry>
+ <stentry>The name to be used for the class that implements the activity. This should be a simple,
+ non-qualified name.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Label</uicontrol></stentry>
+ <stentry>(optional) The label for the activity. This label is displayed to the user, often along with the
+ activity's icon, when the activity needs to be identified to the user. Note that if you specify a label, the
+ wizard creates a string resource to hold this value. If the <uicontrol>Default</uicontrol> option is
+ selected, a label is not set for this activity (the Label field is not editable); the user sees the
+ application's label instead.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Superclass</uicontrol></stentry>
+ <stentry>The class from which the activity inherits. For activities, this is <codeph
+ >android.app.Activity</codeph>.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Permission</uicontrol></stentry>
+ <stentry>(optional) Permissions that should be added to the project's manifest file due to this activity.
+ Click <uicontrol>Add</uicontrol> to display a list of possible permissions from which you can
+ select.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Action</uicontrol></stentry>
+ <stentry>(optional) The actions to be performed by an intent filter associated with this activity. Click
+ <uicontrol>Add</uicontrol> to select from the available actions, or <uicontrol>Input</uicontrol> to simply
+ enter the full name of the intent filter. Note that you can select multiple intents from the dialog that
+ appears when you click <uicontrol>Add</uicontrol>. To select a range, select the first item in the set and
+ then hold down the Shift key while selecting the last item in the set. To select multiple separate intents,
+ select the first item and then hold down the Control key while selecting the remaining items.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Category</uicontrol></stentry>
+ <stentry>(optional) The associated intent filter categories. Click <uicontrol>Add</uicontrol> to select from
+ the available categories or click <uicontrol>Input</uicontrol> to enter a category manually.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Set as the main activity</uicontrol></stentry>
+ <stentry>Select this option if this new activity is to be the app's main activity. Doing so updates the
+ application's manifest file to specify that this is the activity that should be started when the application
+ is first launched.</stentry>
+ </strow>
+ </simpletable>
+ </section>
+ </refbody>
+</reference>
diff --git a/src/help/studio_help/src/topics/u_new-activity.dita b/src/help/studio_help/src/topics/u_new-activity.dita
new file mode 100644
index 0000000..242e00d
--- /dev/null
+++ b/src/help/studio_help/src/topics/u_new-activity.dita
@@ -0,0 +1,81 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE reference PUBLIC "-//OASIS//DTD DITA Reference//EN" "../dtd/reference.dtd">
+<reference id="u_new-activity" xml:lang="en-us">
+ <title>New Activity wizard</title>
+ <shortdesc>Creates a new Android activity.</shortdesc>
+ <prolog>
+ <metadata>
+ <keywords>
+ <!--<indexterm></indexterm>-->
+ </keywords>
+ </metadata>
+ </prolog>
+ <refbody>
+ <section>
+ <p>Use this dialog if you just want a basic activity as your starting point (this basic activity consists of
+ little more than an empty onCreate() method; you can also have an empty onStart() method added by selecting it
+ from the list at the bottom of the dialog). If you would like the activity to be created based upon a sample,
+ select <uicontrol>Create New Activity Based On Template</uicontrol>. </p>
+ <simpletable>
+ <strow>
+ <stentry><uicontrol>Source folder</uicontrol></stentry>
+ <stentry>The folder into which the source code for the new activity is to be stored. By default this is the
+ current project's src folder.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Package</uicontrol></stentry>
+ <stentry>The package that is to contain the new activity. By default this is the current project's
+ package.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Name</uicontrol></stentry>
+ <stentry>The name to be used for the class that implements the activity. This should be a simple,
+ non-qualified name.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Label</uicontrol></stentry>
+ <stentry>(optional) The label for the activity. This label is displayed to the user, often along with the
+ activity's icon, when the activity needs to be identified to the user. Note that if you specify a label, the
+ New Activity wizard creates a string resource to hold this value. If the <uicontrol>Default</uicontrol>
+ option is selected, a label is not set for this activity (the Label field is not editable); the user sees
+ the application's label instead.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Superclass</uicontrol></stentry>
+ <stentry>The class from which the activity inherits. For activities, this is <codeph
+ >android.app.Activity</codeph>.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Permission</uicontrol></stentry>
+ <stentry>(optional) Permissions that should be added to the project's manifest file due to this activity.
+ Click <uicontrol>Add</uicontrol> to display a list of possible permissions from which you can
+ select.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Action</uicontrol></stentry>
+ <stentry>(optional) The actions to be performed by an intent filter associated with this activity. Click
+ <uicontrol>Add</uicontrol> to select from the available actions. Note that you can select multiple intents
+ from the dialog that appears when you click <uicontrol>Add</uicontrol>. To select a range, select the first
+ item in the set and then hold down the Shift key while selecting the last item in the set. To select
+ multiple separate intents, select the first item and then hold down the Control key while selecting the
+ remaining items.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Category</uicontrol></stentry>
+ <stentry>(optional) The associated intent filter categories. Click <uicontrol>Add</uicontrol> to select from
+ the available categories.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Set as the main activity</uicontrol></stentry>
+ <stentry>Select this option if this new activity is to be the app's main activity. Doing so updates the
+ application's manifest file to specify that this is the activity that should be started when the application
+ is first launched.</stentry>
+ </strow>
+ <strow>
+ <stentry>Create an onStart() method</stentry>
+ <stentry>Select this option if you would like to have an empty onStart() method included in your activity.</stentry>
+ </strow>
+ </simpletable>
+ </section>
+ </refbody>
+</reference>
diff --git a/src/help/studio_help/src/topics/u_new-broadcast-receiver.dita b/src/help/studio_help/src/topics/u_new-broadcast-receiver.dita
new file mode 100644
index 0000000..43bc907
--- /dev/null
+++ b/src/help/studio_help/src/topics/u_new-broadcast-receiver.dita
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE reference PUBLIC "-//OASIS//DTD DITA Reference//EN" "../dtd/reference.dtd">
+<reference id="u_new-broadcast-receiver" xml:lang="en-us">
+ <title>New Broadcast Receiver wizard</title>
+ <shortdesc> Creates a new Android broadcast receiver.</shortdesc>
+ <prolog>
+ <metadata>
+ <keywords>
+ <!--<indexterm></indexterm>-->
+ </keywords>
+ </metadata>
+ </prolog>
+ <refbody>
+ <section>
+ <simpletable>
+ <strow>
+ <stentry><uicontrol>Source folder</uicontrol></stentry>
+ <stentry>The folder into which the source code for the new broadcast receiver is to be stored. By default this
+ is the current project's src folder.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Package</uicontrol></stentry>
+ <stentry>The package that is to contain the new broadcast receiver. By default this is the current project's
+ package.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Name</uicontrol></stentry>
+ <stentry>The name to be used for the class that implements the broadcast receiver. This should be a simple,
+ non-qualified name.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Label</uicontrol></stentry>
+ <stentry>(optional) The label for the broadcast receiver. Note that if you specify a label, the New Broadcast
+ Receiver wizard creates a string resource to hold this value. If the <uicontrol>Default</uicontrol> option
+ is selected, a label is not set for this broadcast receiver (the Label field is not editable); the
+ application's label is used instead.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Superclass</uicontrol></stentry>
+ <stentry>The class from which the broadcast receiver inherits. For broadcast receivers, this is <codeph
+ >android.app.BroadcastReceiver</codeph>.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Permission</uicontrol></stentry>
+ <stentry>(optional) Permissions that should be added to the project's manifest file due to this broadcast
+ receiver. Click <uicontrol>Add</uicontrol> to display a list of possible permissions from which you can
+ select.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Action</uicontrol></stentry>
+ <stentry>(optional) The actions to be performed by an intent filter associated with this broadcast receiver.
+ Click <uicontrol>Add</uicontrol> to select from the available actions.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Category</uicontrol></stentry>
+ <stentry>(optional) The associated intent filter's categories. Click <uicontrol>Add</uicontrol> to select from
+ the available categories.</stentry>
+ </strow>
+ </simpletable>
+ </section>
+ </refbody>
+</reference>
diff --git a/src/help/studio_help/src/topics/u_new-content-provider.dita b/src/help/studio_help/src/topics/u_new-content-provider.dita
new file mode 100644
index 0000000..a821fac
--- /dev/null
+++ b/src/help/studio_help/src/topics/u_new-content-provider.dita
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE reference PUBLIC "-//OASIS//DTD DITA Reference//EN" "../dtd/reference.dtd">
+<reference id="u_new-content-provider" xml:lang="en-us">
+ <title>New Content Provider wizard</title>
+ <shortdesc>Creates a new Android content provider.</shortdesc>
+ <prolog>
+ <metadata>
+ <keywords>
+ <!--<indexterm></indexterm>-->
+ </keywords>
+ </metadata>
+ </prolog>
+ <refbody>
+ <section>
+ <simpletable>
+ <strow>
+ <stentry><uicontrol>Source folder</uicontrol></stentry>
+ <stentry>The folder into which the source code for the new content provider is to be stored. By default this
+ is the current project's src folder.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Package</uicontrol></stentry>
+ <stentry>The package that is to contain the new content provider. By default this is the current project's
+ package.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Name</uicontrol></stentry>
+ <stentry>The name to be used for the class that implements the content provider. This should be a simple,
+ non-qualified name.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Label</uicontrol></stentry>
+ <stentry>(optional) The label for the content provider. Note that if you specify a label, the New Content
+ Provider wizard creates a string resource to hold this value. If the <uicontrol>Default</uicontrol> option
+ is selected, a label is not set for this content provider (the Label field is not editable); the
+ application's label is used instead.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Superclass</uicontrol></stentry>
+ <stentry>The class from which the content provider inherits. For content providers, this is <codeph
+ >android.app.ContentProvider</codeph>.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Permission</uicontrol></stentry>
+ <stentry>(optional) Permissions that should be added to the project's manifest file due to this content
+ provider. Click <uicontrol>Add</uicontrol> to display a list of possible permissions from which you can
+ select.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Authorities</uicontrol></stentry>
+ <stentry>One or more URIs that are used to associate data with this content provider. Typically, the
+ fully-qualified name of the content provider's class is used; this is automatically filled in for you by the
+ wizard when the <uicontrol>Use default authority</uicontrol> option is selected. Add additional URIs by
+ clicking <uicontrol>Add</uicontrol>. </stentry>
+ </strow>
+ </simpletable>
+ </section>
+ </refbody>
+</reference>
diff --git a/src/help/studio_help/src/topics/u_new-device-main_android.dita b/src/help/studio_help/src/topics/u_new-device-main_android.dita
new file mode 100644
index 0000000..4b9996c
--- /dev/null
+++ b/src/help/studio_help/src/topics/u_new-device-main_android.dita
@@ -0,0 +1,69 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE reference PUBLIC "-//OASIS//DTD DITA Reference//EN" "../dtd/reference.dtd">
+<reference id="u_new-device-main_android" xml:lang="en-us">
+ <title>New Android Virtual Device Instance dialog (main information)</title>
+ <shortdesc>The second in a sequence of dialogs that are presented when you create a new AVD from the Device Management
+ view, this dialog lets you specify various aspects of the AVD itself, such as which version of the operating system
+ it runs and what the screen dimensions are.</shortdesc>
+ <prolog>
+ <metadata>
+ <keywords>
+ <!--<indexterm></indexterm>-->
+ </keywords>
+ </metadata>
+ </prolog>
+ <refbody>
+ <section>
+ <simpletable>
+ <strow>
+ <stentry>
+ <uicontrol>AVD Target</uicontrol>
+ </stentry>
+ <stentry>The system image the AVD is to use.</stentry>
+ </strow>
+ <strow>
+ <stentry>
+ <uicontrol>AVD Skin</uicontrol>
+ </stentry>
+ <stentry>The screen resolution and orientation. For instance, "HVGA-L" causes the AVD to use an HVGA display
+ (480x320 pixels) in landscape orientation. "QVGA-P" indicates a QVGA display (320x240 pixels) in portrait
+ orientation.</stentry>
+ </strow>
+ <strow>
+ <stentry>
+ <uicontrol>AVD Path</uicontrol>
+ </stentry>
+ <stentry>Use this field if you want to specify a non-default location for the AVD directory that will be
+ constructed for this virtual device. Leave <uicontrol>Use default</uicontrol> selected if the default
+ location is acceptable. Otherwise, clear the <uicontrol>Use default</uicontrol> option and specify your
+ desired directory location.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>SD Card</uicontrol></stentry>
+ <stentry>Allows you to specify whether the emulated device has an emulated SD card. To use an existing SD card
+ image (a .img file), select <uicontrol>Existing</uicontrol> and then specify the path to the card image
+ file. To create a new SD card, select <uicontrol>New</uicontrol>, enter a numeric value in the field that
+ follows, and specify the units for that value (KB or MB).</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Internal Emulator Window</uicontrol> (not available on Mac OS X)</stentry>
+ <stentry>Controls whether the AVD is displayed within the Android Emulator view directly or using VNC.
+ Normally you would leave <uicontrol>Show the Native Emulator Window within an Eclipse View
+ (Recommended)</uicontrol> selected; only if the native option does not work correctly on your development
+ machine should you select <uicontrol>Use VNC to show the Emulator within an Eclipse View</uicontrol>. Note
+ that these options only control how the emulator image is displayed within the Android Emulator view, and
+ not whether the image is displayed in a view or in an external window.</stentry>
+ </strow>
+ <strow>
+ <stentry>
+ <uicontrol>Timeout (sec)</uicontrol>
+ </stentry>
+ <stentry>AVD timeout value. By default this is set to 60 seconds.</stentry>
+ </strow>
+ </simpletable>
+ <p>Click <uicontrol>Finish</uicontrol> to create the AVD with the default startup options, or click <uicontrol
+ >Next</uicontrol> if you want to view and possibly alter the options that will be used when this AVD is
+ started.</p>
+ </section>
+ </refbody>
+</reference>
diff --git a/src/help/studio_help/src/topics/u_new-device-startup_android.dita b/src/help/studio_help/src/topics/u_new-device-startup_android.dita
new file mode 100644
index 0000000..2c71052
--- /dev/null
+++ b/src/help/studio_help/src/topics/u_new-device-startup_android.dita
@@ -0,0 +1,165 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE reference PUBLIC "-//OASIS//DTD DITA Reference//EN" "../dtd/reference.dtd">
+<reference id="u_new-device-startup_android" xml:lang="en-us">
+ <title>New Android Virtual Device Instance dialog (startup options)</title>
+ <shortdesc>The third in a sequence of dialogs that are presented when you create a new AVD from the Device Management
+ view, this dialog lets you specify options that are applied when you start the emulator.</shortdesc>
+ <prolog>
+ <metadata>
+ <keywords>
+ <!--<indexterm></indexterm>-->
+ </keywords>
+ </metadata>
+ </prolog>
+ <refbody>
+ <section>
+ <p>This dialog contains a number of tabs, each with a number of fields. Note that beneath the tabbed sections is a
+ non-editable <uicontrol>Startup Arguments</uicontrol> field that presents your selected options, command-line
+ style, as they will be passed to the emulator when it is started. See <xref
+ href="http://d.android.com/guide/developing/tools/emulator.html#startup-options" format="html"
+ scope="external">http://d.android.com/guide/developing/tools/emulator.html#startup-options</xref> for all of
+ the possible command-line arguments.</p>
+ </section>
+ <section>
+ <title>User Interface tab</title>
+ <simpletable>
+ <strow>
+ <stentry><uicontrol>Screen Resolution</uicontrol></stentry>
+ <stentry>A DPI value that represents a scaling factor to be applied to the emulator window. This allows you to
+ alter the emulator's screen resolution to match the screen size of an actual device. By default, this value
+ is 165. Click the <uicontrol>Calculate</uicontrol> button to calculate screen resolution and scale values
+ that result in a life-sized image given the screen size of the device being emulated (in inches) and your
+ development computer's display size (in inches) or density (DPI).</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Scale</uicontrol></stentry>
+ <stentry>A value ranging from 0.1 to 3.0 that represents the scaling factor to be applied to the emulator
+ window. To specify the scale as a DPI value, include a suffix of "dpi". Click the <uicontrol
+ >Calculate</uicontrol> button to calculate screen resolution and scale values that result in a life-sized
+ image given the screen size of the device being emulated (in inches) and your development computer's display
+ size (in inches) or density (DPI).</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Disable Boot Animation</uicontrol></stentry>
+ <stentry>Speeds up the emulator startup by disabling the boot animation sequence.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Don't show Emulator Skin</uicontrol></stentry>
+ <stentry>Hides the "skin" that makes the emulated device look more like a specific handheld. Note that the
+ skin includes the device keyboard (if the emulated device has a physical keyboard); hiding the skin makes
+ this keyboard, along with any other physical controls presented by the emulated device, inaccessible. Also
+ note that if you change this option on a running emulator, you will need to stop and restart the emulator
+ for the option to take effect.</stentry>
+ </strow>
+ </simpletable>
+ </section>
+ <section>
+ <title>Disk Images tab</title>
+ <simpletable>
+ <strow>
+ <stentry><uicontrol>Cache</uicontrol></stentry>
+ <stentry>The working cache partition image.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>User Data</uicontrol></stentry>
+ <stentry>The working user-data disk image.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Init User Data</uicontrol></stentry>
+ <stentry>A file containing a user-data disk image that is copied to the working user-data disk image if the
+ <uicontrol>Wipe User Data</uicontrol> option is selected. </stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>RAM</uicontrol></stentry>
+ <stentry>A file that to be used as a RAM-disk image.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>SD Card</uicontrol></stentry>
+ <stentry>An SD card image that will be used by the new AVD instance.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Disable Cache Partition</uicontrol></stentry>
+ <stentry>Controls whether or not the AVD uses a cache partition.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Wipe User Data</uicontrol></stentry>
+ <stentry>Select this option to reset the user-data disk image when the AVD starts up. If a disk image is
+ provided in the <uicontrol>Init User Data</uicontrol> field, the contents of that file are copied to the
+ working user-data disk image, rather than the emulator's default image.</stentry>
+ </strow>
+ </simpletable>
+ </section>
+ <section>
+ <title>Network tab</title>
+ <p>The fields on this tab control how the AVD accesses the Internet.</p>
+ <simpletable>
+ <strow>
+ <stentry><uicontrol>DNS Server</uicontrol></stentry>
+ <stentry>A comma-separated list of up to four DNS server names or IP addresses to be used by the
+ AVD.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>HTTP Proxy</uicontrol></stentry>
+ <stentry>An HTTP/HTTPS proxy through which all TCP connections are routed. See <xref
+ href="http://d.android.com/guide/developing/tools/emulator.html#startup-options" format="html"
+ scope="external">http://d.android.com/guide/developing/tools/emulator.html#startup-options</xref> for the
+ format of this field.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Network Latency</uicontrol></stentry>
+ <stentry>Emulates the network latency of various cellular network types, such as GPRS, EDGE, or UMTS.
+ </stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Network Speed</uicontrol></stentry>
+ <stentry>Throttles the networking speed to simulate that of a cellular network. Use this field to specify the
+ desired network type.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Port Number</uicontrol></stentry>
+ <stentry>The console port number for this emulator instance. This should be an even integer value that ranges
+ from 5554 to 5584.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Max Network Speed/Min Network Latency</uicontrol></stentry>
+ <stentry>Indicates that network operations should proceed at full speed, with no latency.</stentry>
+ </strow>
+ </simpletable>
+ </section>
+ <section>
+ <title>System tab</title>
+ <simpletable>
+ <strow>
+ <stentry><uicontrol>CPU Delay</uicontrol></stentry>
+ <stentry>Allows you to emulate a slow CPU. Supply a value from 0 (no delay) to 1000 (maximum delay). Note that
+ these integer values simply indicate a relative degree of delay and do not represent any actual amount of
+ time. </stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Redirect GPS</uicontrol></stentry>
+ <stentry>Emulates an NMEA-compatible GPS device that is connected to an external character device or socket.
+ For information on the format of this field, see the description of the -serial option in the QEMU user
+ documentation, which can be found at <xref href="http://www.nongnu.org/qemu/qemu-doc.html" format="html"
+ scope="external">http://www.nongnu.org/qemu/qemu-doc.html</xref>.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Disable JNI checks</uicontrol></stentry>
+ <stentry>Disables JNI checks in the Dalvik runtime.</stentry>
+ </strow>
+ </simpletable>
+ </section>
+ <section>
+ <title>Others tab</title>
+ <simpletable>
+ <strow>
+ <stentry><uicontrol>Other Startup Options</uicontrol></stentry>
+ <stentry>Use this field to enter any other startup options not available from the other tabs. Enter the
+ startup options as documented in the Android developer documentation at <xref
+ href="http://d.android.com/guide/developing/tools/emulator.html#startup-options" format="html"
+ scope="external"
+ >http://d.android.com/guide/developing/tools/emulator.html#startup-options</xref>.</stentry>
+ </strow>
+ </simpletable>
+ </section>
+ </refbody>
+</reference>
diff --git a/src/help/studio_help/src/topics/u_new-device.dita b/src/help/studio_help/src/topics/u_new-device.dita
new file mode 100644
index 0000000..5b53c1b
--- /dev/null
+++ b/src/help/studio_help/src/topics/u_new-device.dita
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE reference PUBLIC "-//OASIS//DTD DITA Reference//EN" "../dtd/reference.dtd">
+<reference id="u_new-device" xml:lang="en-us">
+ <title>Create a New Device dialog</title>
+ <shortdesc>Creates a new Android Virtual Device (AVD).</shortdesc>
+ <prolog>
+ <metadata>
+ <keywords>
+ <!--<indexterm></indexterm>-->
+ </keywords>
+ </metadata>
+ </prolog>
+ <refbody>
+ <section>
+ <p>When creating a new device you are presented with a series of dialogs in sequence. The first simply asks for a
+ name for the new AVD; supply a name and click <uicontrol>Next</uicontrol>. The second dialog contains basic
+ information about the AVD; see <xref href="u_new-device-main_android.dita" format="dita"/> for details. The
+ third dialog, which is optional, allows you to specify startup options to be applied each time the emulator is
+ started. <xref href="u_new-device-startup_android.dita"/> describes the available startup options.</p>
+ </section>
+ </refbody>
+</reference>
diff --git a/src/help/studio_help/src/topics/u_new-proj-widget_android.dita b/src/help/studio_help/src/topics/u_new-proj-widget_android.dita
new file mode 100644
index 0000000..988b7f0
--- /dev/null
+++ b/src/help/studio_help/src/topics/u_new-proj-widget_android.dita
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE reference PUBLIC "-//OASIS//DTD DITA Reference//EN" "../dtd/reference.dtd">
+<reference id="u_new-proj-widget_android" xml:lang="en-us">
+ <title>New Android Widget Project</title>
+ <shortdesc>Creates a project for an Android widget.</shortdesc>
+ <prolog>
+ <metadata>
+ <keywords>
+ <!--<indexterm></indexterm>-->
+ </keywords>
+ </metadata>
+ </prolog>
+ <refbody>
+ <section>
+ <simpletable>
+ <strow>
+ <stentry><uicontrol>Project name</uicontrol></stentry>
+ <stentry>A name for the newly created Eclipse project. Note that MOTODEV Studio will create a folder for your
+ project using this name; therefore, the name you supply must follow the rules for filenames on your host
+ operating system.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Use default location</uicontrol></stentry>
+ <stentry>Indicates whether projects should be created in the current workspace. To specify a different
+ location, clear this option and then use the <uicontrol>Location</uicontrol> field to specify the folder in
+ which the project is to be created.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Location</uicontrol></stentry>
+ <stentry>Specifies the folder in which projects are created. By default projects are created in the current
+ workspace. Clear the <uicontrol>Use default location</uicontrol> option to specify a different
+ location.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Target</uicontrol></stentry>
+ <stentry>Specifies the Android platform against which the application is to be built.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Application name</uicontrol></stentry>
+ <stentry>The name of your widget application as it will appear to the user on the Android device.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Package name</uicontrol></stentry>
+ <stentry>The full Java package name for your application. Note that this needs to be a unique identifier; use
+ standard domain-style naming.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Activity name</uicontrol></stentry>
+ <stentry>The name for the application's main activity. This is the name for the class stub that will be
+ generated by the plugin.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Min SDK version</uicontrol></stentry>
+ <stentry>A value that corresponds to the minimum required API level. This value determines whether or not your
+ application will run on a given Android device: if the <uicontrol>Min SDK version</uicontrol> is greater
+ than the level supported by the device, the application will not run on that device. This field is
+ automatically set when you specify a <uicontrol>Target</uicontrol>, but can be overridden.</stentry>
+ </strow>
+ </simpletable>
+ </section>
+ </refbody>
+</reference>
diff --git a/src/help/studio_help/src/topics/u_new-proj_android.dita b/src/help/studio_help/src/topics/u_new-proj_android.dita
new file mode 100644
index 0000000..8ad7df8
--- /dev/null
+++ b/src/help/studio_help/src/topics/u_new-proj_android.dita
@@ -0,0 +1,89 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE reference PUBLIC "-//OASIS//DTD DITA Reference//EN" "../dtd/reference.dtd">
+<reference id="u_new-proj_android" xml:lang="en-us">
+ <title>New Android Project wizard</title>
+ <shortdesc>Creates a MOTODEV Studio for Android project.</shortdesc>
+ <prolog>
+ <metadata>
+ <keywords>
+ <!--<indexterm></indexterm>-->
+ </keywords>
+ </metadata>
+ </prolog>
+ <refbody>
+ <section>The newly created project can be a basic one, or can be pre-populated from an SDK sample or from an
+ existing Android project.<simpletable>
+ <strow>
+ <stentry><uicontrol>Project name</uicontrol></stentry>
+ <stentry>A name for the newly created Eclipse project. Note that MOTODEV Studio will create a folder for your
+ project using this name; therefore, the name you supply must follow the rules for filenames on your host
+ operating system.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Create new project</uicontrol></stentry>
+ <stentry>Creates a basic "Hello World"-style project.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Create new project using sample</uicontrol></stentry>
+ <stentry>Creates a project based upon one of the samples included with the Android SDK. You will be asked to
+ specify the base sample in the wizard's next dialog.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Create project from existing source</uicontrol></stentry>
+ <stentry>Opens an existing project in place, without copying it to your workspace.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Use default location</uicontrol></stentry>
+ <stentry>Indicates whether projects should be created in the current workspace. To specify a different
+ location, clear this option and then use the <uicontrol>Location</uicontrol> field to specify the folder in
+ which the project is to be created.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Location</uicontrol></stentry>
+ <stentry>Specifies the folder in which projects are created. By default projects are created in the current
+ workspace. Clear the <uicontrol>Use default location</uicontrol> option to specify a different
+ location.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Target</uicontrol></stentry>
+ <stentry>Specifies the Android platform against which the application is to be built. Note that you cannot
+ specify a target when creating a project from existing source.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Application name</uicontrol></stentry>
+ <stentry>The name of your application as it will appear to the user on the Android device. Note that if you
+ are creating a project based on a sample or on existing source, you cannot change this field. </stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Package name</uicontrol></stentry>
+ <stentry>The full Java package name for your application. Note that this needs to be a unique identifier; use
+ standard domain-style naming.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Activity name</uicontrol></stentry>
+ <stentry>The name for the application's main activity. This is the name for the class stub that will be
+ generated by the plugin.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Min SDK version</uicontrol></stentry>
+ <stentry>A value that corresponds to the minimum required API level. This value determines whether or not your
+ application will run on a given Android device: if the <uicontrol>Min SDK version</uicontrol> is greater
+ than the level supported by the device, the application will not run on that device. This field is
+ automatically set when you specify a <uicontrol>Target</uicontrol>, but can be overridden.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Add native support</uicontrol></stentry>
+ <stentry>If this project will have native components and you will be using MOTODEV Studio for Android to
+ manage and build those components (along with the non-native components, such as Java source files and xml
+ layout files), select this option. Note that in order to work with native code within MOTODEV Studio for
+ Android you must have the Android NDK properly installed and configured. Also note that you can add native
+ support to an existing Android project; you needn't add it when you first create the project.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Obfuscate Java classes</uicontrol></stentry>
+ <stentry>Enables (if selected) or disables obfuscation of the Java classes in the new Android project whenever
+ you build it for release.</stentry>
+ </strow>
+ </simpletable></section>
+ </refbody>
+</reference>
diff --git a/src/help/studio_help/src/topics/u_new-service.dita b/src/help/studio_help/src/topics/u_new-service.dita
new file mode 100644
index 0000000..d178e87
--- /dev/null
+++ b/src/help/studio_help/src/topics/u_new-service.dita
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE reference PUBLIC "-//OASIS//DTD DITA Reference//EN" "../dtd/reference.dtd">
+<reference id="u_new-service" xml:lang="en-us">
+ <title>New Service wizard</title>
+ <shortdesc>Creates a new Android service.</shortdesc>
+ <prolog>
+ <metadata>
+ <keywords>
+ <!--<indexterm></indexterm>-->
+ </keywords>
+ </metadata>
+ </prolog>
+ <refbody>
+ <section>
+ <simpletable>
+ <strow>
+ <stentry><uicontrol>Source folder</uicontrol></stentry>
+ <stentry>The folder into which the source code for the new service is to be stored. By default this is the
+ current project's src folder.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Package</uicontrol></stentry>
+ <stentry>The package that is to contain the new service. By default this is the current project's
+ package.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Name</uicontrol></stentry>
+ <stentry>The name to be used for the class that implements the service. This should be a simple, non-qualified
+ name.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Label</uicontrol></stentry>
+ <stentry>(optional) The label for the service. This label is displayed to the user, often along with the
+ service's icon, when the service needs to be identified to the user. Note that if you specify a label, the
+ New Service wizard creates a string resource to hold this value. If the <uicontrol>Default</uicontrol>
+ option is selected, a label is not set for this service (the Label field is not editable); the user sees the
+ application's label instead.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Superclass</uicontrol></stentry>
+ <stentry>The class from which the service inherits. For services, this is <codeph
+ >android.app.Service</codeph>.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Permission</uicontrol></stentry>
+ <stentry>(optional) Permissions that should be added to the project's manifest file due to this service. Click
+ <uicontrol>Add</uicontrol> to display a list of possible permissions from which you can select.</stentry>
+ </strow>
+ </simpletable>
+ <p>The New Service wizard can optionally include empty onCreate() and onStart() methods.</p>
+ </section>
+ </refbody>
+</reference>
diff --git a/src/help/studio_help/src/topics/u_new-widget-provider.dita b/src/help/studio_help/src/topics/u_new-widget-provider.dita
new file mode 100644
index 0000000..bd1c495
--- /dev/null
+++ b/src/help/studio_help/src/topics/u_new-widget-provider.dita
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE reference PUBLIC "-//OASIS//DTD DITA Reference//EN" "../dtd/reference.dtd">
+<reference id="u_new-widget-provider" xml:lang="en-us">
+ <title>New Android Widget Provider wizard</title>
+ <shortdesc>Creates a new Android widget provider.</shortdesc>
+ <prolog>
+ <metadata>
+ <keywords>
+ <!--<indexterm></indexterm>-->
+ </keywords>
+ </metadata>
+ </prolog>
+ <refbody>
+ <section>
+ <simpletable>
+ <strow>
+ <stentry><uicontrol>Source folder</uicontrol></stentry>
+ <stentry>The folder into which the source code for the new widget provider is to be stored. By default this is
+ the current project's src folder.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Package</uicontrol></stentry>
+ <stentry>The package that is to contain the new widget provider. By default this is the current project's
+ package.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Name</uicontrol></stentry>
+ <stentry>The name to be used for the class that implements the widget provider. This should be a simple,
+ non-qualified name.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Label</uicontrol></stentry>
+ <stentry>(optional) The label for the widget provider. Note that if you specify a label, the New Android
+ Widget Provider wizard creates a string resource to hold this value. If the <uicontrol>Default</uicontrol>
+ option is selected, a label is not set for this widget provider (the Label field is not editable); the
+ application's label is used instead.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Superclass</uicontrol></stentry>
+ <stentry>This field is not editable. Widget providers subclass <codeph
+ >android.appwidget.AppWidgetProvider</codeph>.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Permission</uicontrol></stentry>
+ <stentry>Permissions that should be added to the application's manifest file. Click <uicontrol>Add</uicontrol>
+ to display a list of permissions from which you can choose.</stentry>
+ </strow>
+ </simpletable>
+ </section>
+ </refbody>
+</reference>
diff --git a/src/help/studio_help/src/topics/u_obfuscate-projects.dita b/src/help/studio_help/src/topics/u_obfuscate-projects.dita
new file mode 100644
index 0000000..1f5001b
--- /dev/null
+++ b/src/help/studio_help/src/topics/u_obfuscate-projects.dita
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE reference PUBLIC "-//OASIS//DTD DITA Reference//EN" "../dtd/reference.dtd">
+<reference id="u_obfuscate-projects" xml:lang="en-us">
+ <title>Obfuscation dialog</title>
+ <shortdesc>Use this dialog to enable or disable obfuscation of the Java classes in one or more Android projects. You
+ access this dialog by selecting <uicontrol>Enable/Disable Obfuscation</uicontrol> from the MOTODEV menu.</shortdesc>
+ <prolog>
+ <metadata>
+ <keywords>
+ <!--<indexterm></indexterm>-->
+ </keywords>
+ </metadata>
+ </prolog>
+ <refbody>
+ <section>
+ <p>This dialog allows you to enable or disable obfuscation of the selected Android projects. Select the projects
+ to be obfuscated, clear any that should not be obfuscated, and click <uicontrol>OK</uicontrol>. If MOTODEV
+ Studio is configured so that projects build automatically, changing the obfuscation setting for one or more
+ projects will cause those projects to immediately be rebuilt.</p>
+ <p>Note that this is not simply a one-time change; this dialog alters the obfuscation setting in each project's
+ properties. </p>
+ </section>
+ </refbody>
+</reference>
diff --git a/src/help/studio_help/src/topics/u_obfuscation-prop.dita b/src/help/studio_help/src/topics/u_obfuscation-prop.dita
new file mode 100644
index 0000000..4a2fecc
--- /dev/null
+++ b/src/help/studio_help/src/topics/u_obfuscation-prop.dita
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE reference PUBLIC "-//OASIS//DTD DITA Reference//EN" "../dtd/reference.dtd">
+<reference id="u_obfuscation-prop" xml:lang="en-us">
+ <title>MOTODEV Studio project properties dialog</title>
+ <shortdesc>Use this dialog to control whether Java classes should be obfuscated when the parent Android project is
+ built for release. You access this dialog by right-clicking a project from the Package Explorer and selecting
+ <uicontrol>Properties</uicontrol>, and then selecting MOTODEV Studio from the list of properties.</shortdesc>
+ <prolog>
+ <metadata>
+ <keywords>
+ <!--<indexterm></indexterm>-->
+ </keywords>
+ </metadata>
+ </prolog>
+ <refbody>
+ <section>
+ <p>This dialog has a single option that allows you to enable or disable obfuscation of Java classes for the
+ selected project. Obfuscation is performed at build time, and only for release builds. Obfuscation is not
+ performed when building a debug version of a project, whether or not this option is enabled. Note that this
+ option may have been specified when the project was created; it is available for selection in the New Android
+ Project using Studio for Android dialog.</p>
+ </section>
+ <section>
+ <simpletable>
+ <strow>
+ <stentry>
+ <uicontrol>Obfuscation</uicontrol>
+ </stentry>
+ <stentry>If selected, Java classes will be obfuscation at build time when a release version of the project is
+ built.</stentry>
+ </strow>
+ </simpletable>
+ </section>
+ </refbody>
+</reference>
diff --git a/src/help/studio_help/src/topics/u_options-menu-code-create.dita b/src/help/studio_help/src/topics/u_options-menu-code-create.dita
new file mode 100644
index 0000000..ee69e31
--- /dev/null
+++ b/src/help/studio_help/src/topics/u_options-menu-code-create.dita
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE reference PUBLIC "-//OASIS//DTD DITA Reference//EN" "../dtd/reference.dtd">
+<reference id="u_options-menu-code-create" xml:lang="en-us">
+ <title>Generate Options Menu Code Based on xml file</title>
+ <shortdesc>Generates options menu code from a selected xml-format menu definition file in your project's res/menu
+ folder. </shortdesc>
+ <prolog>
+ <metadata>
+ <keywords>
+ <!--<indexterm></indexterm>-->
+ </keywords>
+ </metadata>
+ </prolog>
+ <refbody>
+ <section>
+ <p>This dialog is presented when you select a project in the Package Explorer that has one or more menus in the
+ project's res/menu folder and then select <menucascade>
+ <uicontrol>Auto-Generated Code</uicontrol>
+ <uicontrol>Generate Java Code Based on Menu xml Files</uicontrol>
+ </menucascade> from the <uicontrol>MOTODEV</uicontrol> menu.</p>
+ <p>This command can be re-invoked as many times as necessary to accomodate changes to your xml file.</p>
+ <simpletable>
+ <strow>
+ <stentry><uicontrol>Project</uicontrol></stentry>
+ <stentry>The project into which the menu-handling code is to be added.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Target Class</uicontrol></stentry>
+ <stentry>The Java class (an Activity or Fragement) to which the menu-handling code is to be added. The Generate Options Menu... feature
+ adds an onCreateOptionsMenu() method that inflates the specified menu. It also adds an
+ onOptionsItemSelected() method containing an if/else block that provides you with a place to put your menu
+ item handler code for each item in the menu.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Menu xml File</uicontrol></stentry>
+ <stentry>The xml file in the selected project's res/menu folder that defines the menu for which code is to be
+ generated.</stentry>
+ </strow>
+ </simpletable>
+ </section>
+ </refbody>
+</reference>
diff --git a/src/help/studio_help/src/topics/u_pkg-export.dita b/src/help/studio_help/src/topics/u_pkg-export.dita
new file mode 100644
index 0000000..7527eb3
--- /dev/null
+++ b/src/help/studio_help/src/topics/u_pkg-export.dita
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE reference PUBLIC "-//OASIS//DTD DITA Reference//EN" "../dtd/reference.dtd">
+<reference id="u_pkg-export" xml:lang="en-us">
+ <title>Export Android Package Files dialog</title>
+ <shortdesc>Creates an Android Package file (.apk) for each APK configuration of each selected project. This dialog
+ appears when you select <uicontrol>Export</uicontrol> from the <uicontrol>File</uicontrol> menu, and then select <menucascade>
+ <uicontrol>Android</uicontrol>
+ <uicontrol>Export Android Application using MOTODEV Studio for Android</uicontrol>
+ </menucascade> from the list of export destinations.</shortdesc>
+ <prolog>
+ <metadata>
+ <keywords>
+ <!--<indexterm></indexterm>-->
+ </keywords>
+ </metadata>
+ </prolog>
+ <refbody>
+ <section>
+ <simpletable>
+ <strow>
+ <stentry>Package list</stentry>
+ <stentry>Lists all of the projects in the current workspace. Specify at least one project to be
+ exported.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Use default destination</uicontrol></stentry>
+ <stentry>If selected, the Android Package file for each project is placed within that project's <codeph
+ >dist</codeph> directory. If this option is not selected, the Android Package files are all placed in the
+ directory indicated by the <uicontrol>Destination</uicontrol> field. This option is selected by
+ default.</stentry>
+ </strow>
+ <strow>
+ <stentry>
+ <uicontrol>Destination</uicontrol>
+ </stentry>
+ <stentry>Specifies the directory into which the Android Package files are stored. This field is only valid if
+ <uicontrol>Use default destination</uicontrol> is not selected.</stentry>
+ </strow>
+ <strow>
+ <stentry>
+ <uicontrol>Sign the package</uicontrol>
+ </stentry>
+ <stentry>If selected, the exported Android Packages are signed according to the remaining fields in this
+ section. Note that if you do not sign the Android Package when you export it, you can later sign it by
+ right-clicking it in the Package Explorer view and selecting <uicontrol>Sign Android Package</uicontrol>
+ from the <uicontrol>MOTODEV Studio for Android</uicontrol> menu.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Keystore</uicontrol></stentry>
+ <stentry>Specify the keystore that contains the key to be used during the signing process. If the keystore is
+ already known to (and listed in) the Signing and Keys view, select it from the drop-down list. Otherwise,
+ click <uicontrol>Use existing</uicontrol> to specify an existing keystore that is not yet known to the
+ Signing and Keys view, or <uicontrol>Add new</uicontrol> to create and use a new keystore.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Key</uicontrol></stentry>
+ <stentry>Specify the key within the chosen keystore to be used for signing. Select an existing key from the
+ drop-down list, or create a new key in the keystore by selecting <uicontrol>Add</uicontrol>.</stentry>
+ </strow>
+ </simpletable>
+ </section>
+ </refbody>
+</reference>
diff --git a/src/help/studio_help/src/topics/u_pkg-install.dita b/src/help/studio_help/src/topics/u_pkg-install.dita
new file mode 100644
index 0000000..e9d6335
--- /dev/null
+++ b/src/help/studio_help/src/topics/u_pkg-install.dita
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE reference PUBLIC "-//OASIS//DTD DITA Reference//EN" "../dtd/reference.dtd">
+<reference id="u_pkg-install" xml:lang="en-us">
+ <title>Install Application dialog</title>
+ <shortdesc>Installs an application to an Android device or a running emulator instance. This dialog is presented when
+ you right-click a device in the Device Management view and select <uicontrol>Install App</uicontrol> from the menu
+ that appears.</shortdesc>
+ <prolog>
+ <metadata>
+ <keywords>
+ <!--<indexterm></indexterm>-->
+ </keywords>
+ </metadata>
+ </prolog>
+ <refbody>
+ <section>
+ <simpletable>
+ <strow>
+ <stentry><uicontrol>Application package</uicontrol></stentry>
+ <stentry>Path to the application package (.apk file) to be installed. This file can usually be found inside
+ the project's <codeph>bin</codeph> directory.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Replace if application already exists</uicontrol></stentry>
+ <stentry>This option, which is selected by default, allows an existing version of the application to be
+ overwritten on the target device. Clear this option if you don't want to overwrite any existing version of
+ the application.</stentry>
+ </strow>
+ </simpletable>
+ </section>
+ </refbody>
+</reference>
diff --git a/src/help/studio_help/src/topics/u_run-config-main.dita b/src/help/studio_help/src/topics/u_run-config-main.dita
new file mode 100644
index 0000000..c6c452d
--- /dev/null
+++ b/src/help/studio_help/src/topics/u_run-config-main.dita
@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE reference
+ PUBLIC "-//OASIS//DTD DITA Reference//EN" "../dtd/reference.dtd">
+<reference xml:lang="en-us" id="u_run-config-main">
+ <title>Run Configurations dialog</title>
+ <shortdesc>A run configuration brings together all of the information needed to run a specific executable on a
+ specific device instance, making it simple to repeatedly run that executable on that device instance.</shortdesc>
+ <prolog>
+ <metadata>
+ <keywords>
+ <!--<indexterm></indexterm>-->
+ </keywords>
+ </metadata>
+ </prolog>
+ <refbody>
+ <section>
+ <p>You can create different <term>run configurations</term> in order to run or debug <ph product="webui"
+ >WebUI</ph><ph product="android-studio">Android</ph> applications<ph product="webui"> or widgets</ph> on real
+ or emulated devices. A run configuration encapsulates all of the information relating to a single execution
+ scenario into a single entity. It identifies not only the specific <ph product="webui">package within a
+ particular </ph>project to be run, but the device on which it is to be run. By creating multiple run
+ configurations, you can quickly and easily switch between them to try out different scenarios.</p>
+ <p>The Run Configurations dialog appears when you select <uicontrol>Run Configurations</uicontrol> from the
+ <uicontrol>Run</uicontrol> menu and then either create or select an existing <uicontrol>Android Application
+ using Studio for Android</uicontrol> configuration.</p>
+ <simpletable>
+ <strow>
+ <stentry>
+ <uicontrol>Project</uicontrol>
+ </stentry>
+ <stentry>Specifies the project that contains the application to be run. </stentry>
+ </strow>
+ <strow product="webui">
+ <stentry>
+ <uicontrol>Package/Descriptor</uicontrol>
+ </stentry>
+ <stentry>Specifies the package that corresponds to the application to be run. The package can either be a
+ <codeph>.descriptor</codeph> or a <codeph>.mpkg</codeph> file. </stentry>
+ </strow>
+ <strow>
+ <stentry>
+ <uicontrol>Device</uicontrol>
+ </stentry>
+ <stentry>Specifies the device instance on which the application should run. This can be either a real or an
+ emulated device.</stentry>
+ </strow>
+ <strow product="webui">
+ <stentry>
+ <uicontrol>Signing</uicontrol>
+ </stentry>
+ <stentry>Specifies the certificate to be used to sign the application before transferring it to the device. If
+ the chosen instance is a MOTOMAGX Emulator, this field is disabled; you can only specify a certificate if
+ the run configuration is for a physical device.</stentry>
+ </strow>
+ <strow product="android-studio">
+ <stentry>
+ <uicontrol>Activity</uicontrol>
+ </stentry>
+ <stentry>Specifies the activity that should be launched, if any. Often you will just want the default activity
+ to be launched, but you can specify a specific activity by selecting <uicontrol>Launch</uicontrol> and
+ indicating the activity to be launched. If the project should be installed but not run (as might be in the
+ case of a widget), select <uicontrol>Do Nothing</uicontrol>.</stentry>
+ </strow>
+ </simpletable>
+ </section>
+ </refbody>
+</reference>
diff --git a/src/help/studio_help/src/topics/u_run-config-monkey-main.dita b/src/help/studio_help/src/topics/u_run-config-monkey-main.dita
new file mode 100644
index 0000000..b945d3a
--- /dev/null
+++ b/src/help/studio_help/src/topics/u_run-config-monkey-main.dita
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE reference
+ PUBLIC "-//OASIS//DTD DITA Reference//EN" "../dtd/reference.dtd">
+<reference xml:lang="en-us" id="u_run-config-monkey-main">
+ <title>Run Configurations (Monkey) dialog</title>
+ <shortdesc>A "Test events with Monkey" run configuration brings together all of the information needed to execute the
+ UI/Application Exerciser Monkey against a specific set of executables on a specific device instance, making it
+ simple to repeatedly test those executables on that device instance.</shortdesc>
+ <prolog>
+ <metadata>
+ <keywords>
+ <!--<indexterm></indexterm>-->
+ </keywords>
+ </metadata>
+ </prolog>
+ <refbody>
+ <section>
+ <p>You can create different <term>run configurations</term> in order to test Android applications on real or
+ emulated devices. A run configuration encapsulates all of the information relating to a single test scenario
+ into a single entity. It identifies not only the specific packages to be exercised, but the device on which the
+ test is to be run. By creating multiple run configurations, you can quickly and easily switch between them to
+ try out different test scenarios.</p>
+ <p>The Run Configurations dialog appears when you right-click a running emulator or connected device in the Device
+ Management view and select <uicontrol>Test events with Monkey</uicontrol>.</p>
+ <p>The Run Configurations dialog is organized into two tabs: <uicontrol>Main</uicontrol> is where you specify the
+ device on which to run, the packages to be exercised, and the number of events to generate and send. <uicontrol
+ >Options</uicontrol> allows you to fine-tune the events that are sent.</p>
+ </section>
+ <section>
+ <title>Main tab</title>
+ <simpletable>
+ <strow>
+ <stentry><uicontrol>Device</uicontrol>
+ </stentry>
+ <stentry>Specifies the running emulator or connected device on which the test is to take place.</stentry>
+ </strow>
+ <strow>
+ <stentry>Package list</stentry>
+ <stentry>Lists the packages to which events can be sent. Select those packages that should receive the events
+ generated by the UI/Application Exerciser Monkey. You can select one or several packages; Monkey will limit
+ its scope only to the selected packages. To select a single package, click it. To select a block of adjacent
+ packages, click the first and then Shift-click the last. To select several individual packages, click the
+ first one and then Control-click (Command-click, on Macintosh) the remaining packages.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Event count</uicontrol></stentry>
+ <stentry>Specifies the number of events to be generated and sent.</stentry>
+ </strow>
+ </simpletable>
+ </section>
+ <section>
+ <title>Options tab</title>
+ <p>Contains fields that allow you to further customize the way that that Monkey tests your applications. See the
+ UI/Application Exerciser Monkey documentation provided with the Android SDK for information on the purpose and
+ allowable values for each of these fields. Note that you can select <uicontrol>General</uicontrol> and supply
+ options as command-line parameters, rather than as form values.</p>
+ </section>
+ </refbody>
+</reference>
diff --git a/src/help/studio_help/src/topics/u_save-ui-state.dita b/src/help/studio_help/src/topics/u_save-ui-state.dita
new file mode 100644
index 0000000..6b52af0
--- /dev/null
+++ b/src/help/studio_help/src/topics/u_save-ui-state.dita
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE reference PUBLIC "-//OASIS//DTD DITA Reference//EN" "../dtd/reference.dtd">
+<reference id="u_save-ui-state" xml:lang="en-us">
+ <title>Generate Save UI State code</title>
+ <shortdesc>Generates code to save and restore UI state for many of the views in a selected xml layout.</shortdesc>
+ <prolog>
+ <metadata>
+ <keywords>
+ <!--<indexterm></indexterm>-->
+ </keywords>
+ </metadata>
+ </prolog>
+ <refbody>
+ <section>
+ <p>This dialog is presented when you select a project in the Package Explorer that has views that can be saved and
+ then select <menucascade>
+ <uicontrol>Auto-Generated Code</uicontrol>
+ <uicontrol>Generate Save UI State code</uicontrol>
+ </menucascade> from the <uicontrol>MOTODEV</uicontrol> menu.</p>
+ <simpletable>
+ <strow>
+ <stentry><uicontrol>Project</uicontrol></stentry>
+ <stentry>The project into which the state-saving code is to be added.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Target Class</uicontrol></stentry>
+ <stentry>The Java class into which the state-saving code is to be added. This feature adds code to the
+ selected class's onPause() method that saves the state of the selected views. It also adds code to the
+ selected class's onResume() method to restore the saved state.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Source Layout File</uicontrol></stentry>
+ <stentry>A read-only text field that indicates which layout file's state will be saved and restored by the
+ generated code.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>UI Objects</uicontrol></stentry>
+ <stentry>A list of views for which state can be saved and restored. Select those views for which you want code
+ to be generated.</stentry>
+ </strow>
+ </simpletable>
+ </section>
+ </refbody>
+</reference>
diff --git a/src/help/studio_help/src/topics/u_screen-calculator.dita b/src/help/studio_help/src/topics/u_screen-calculator.dita
new file mode 100644
index 0000000..de7fdad
--- /dev/null
+++ b/src/help/studio_help/src/topics/u_screen-calculator.dita
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE reference PUBLIC "-//OASIS//DTD DITA Reference//EN" "../dtd/reference.dtd">
+<reference id="u_screen-calculator" xml:lang="en-us">
+ <title>Screen Resolution/Scale Calculator</title>
+ <shortdesc>Calculates pixel density (or "screen resolution") in DPI and a scale value that cause an emulated device to
+ be displayed "life-size" on your development computer's screen..</shortdesc>
+ <prolog>
+ <metadata>
+ <keywords>
+ <!--<indexterm></indexterm>-->
+ </keywords>
+ </metadata>
+ </prolog>
+ <refbody>
+ <section>This calculator can be invoked either from the New Android Virtual Device Instance dialog (the final dialog
+ you see when creating a new AVD from the Device Management view) or from the AVD properties (right-click an AVD in
+ the Device Management view, select <uicontrol>Properties</uicontrol>, and then in the Properties dialog navigate
+ to <menucascade><uicontrol>Android Virtual Device</uicontrol><uicontrol>Startup
+ Options</uicontrol></menucascade>). Simply click <uicontrol>Calculate</uicontrol> next to either the <uicontrol
+ >Screen Resolution (dpi)</uicontrol> or <uicontrol>Scale</uicontrol> options to display the calculator. Once you
+ have entered the necessary values into the calculator, click <uicontrol>OK</uicontrol> to close the calculator and
+ copy the calculated values to the appropriate startup options.</section>
+ <section>
+ <simpletable>
+ <strow>
+ <stentry><uicontrol>Device Screen Size (in)</uicontrol></stentry>
+ <stentry>The screen size (diagonal), in inches, of the device being emulated. This information is generally
+ included on the device specification sheet.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Monitor Screen Resolution</uicontrol></stentry>
+ <stentry>The pixel density or screen size (diagonal) of your development computer's display. Either click
+ <uicontrol>Value (dpi)</uicontrol> and supply your monitor's density (in pixels per inch), or click
+ <uicontrol>Based on Monitor Size (in)</uicontrol> and specify your monitor's size (the diagonal
+ measurement) in inches. It is important to note that when supplying a monitor size you only specify the
+ actual viewable display size; do not include the bezel.</stentry>
+ </strow>
+ </simpletable>
+ </section>
+ </refbody>
+</reference>
diff --git a/src/help/studio_help/src/topics/u_sdk-download-location.dita b/src/help/studio_help/src/topics/u_sdk-download-location.dita
new file mode 100644
index 0000000..11c877c
--- /dev/null
+++ b/src/help/studio_help/src/topics/u_sdk-download-location.dita
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE reference PUBLIC "-//OASIS//DTD DITA Reference//EN" "../dtd/reference.dtd">
+<reference id="u_sdk-download-location" xml:lang="en-us">
+ <title>SDK Download Location dialog</title>
+ <shortdesc>Specify the location to which the Android SDK should be downloaded.</shortdesc>
+ <prolog>
+ <metadata>
+ <keywords>
+ <!--<indexterm></indexterm>-->
+ </keywords>
+ </metadata>
+ </prolog>
+ <refbody>
+ <simpletable>
+ <strow>
+ <stentry><uicontrol>Download Location</uicontrol></stentry>
+ <stentry>Use this field to specify where the downloaded SDK should be stored on your development computer.
+ Either enter the path into this field, or click <uicontrol>Browse</uicontrol> and navigate to the directory in
+ which the SDK should be placed. Note that the path to the chosen directory should not contain any whitespace
+ characters, since whitespace characters cause problems with the Android project obfuscation
+ mechanism.</stentry>
+ </strow>
+ </simpletable>
+ </refbody>
+</reference>
diff --git a/src/help/studio_help/src/topics/u_select-activity.dita b/src/help/studio_help/src/topics/u_select-activity.dita
new file mode 100644
index 0000000..7de04fe
--- /dev/null
+++ b/src/help/studio_help/src/topics/u_select-activity.dita
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE reference PUBLIC "-//OASIS//DTD DITA Reference//EN" "../dtd/reference.dtd">
+<!-- get to this dialog by clicking Browse next to Activity in the Run Configurations dialog (Main tab) -->
+<reference id="u_select-activity" xml:lang="en-us">
+ <title>Activity Selection dialog</title>
+ <shortdesc>Allows you to select the activity within the specified Android project that is to be launched. This dialog
+ appears when you click <uicontrol>Browse</uicontrol> next to the <uicontrol>Activity</uicontrol> field on the Main
+ tab of an <uicontrol>Android Application using Studio for Android</uicontrol> run configuration.</shortdesc>
+ <prolog>
+ <metadata>
+ <keywords>
+ <!--<indexterm></indexterm>-->
+ </keywords>
+ </metadata>
+ </prolog>
+ <refbody>
+ <section>
+ <p>This dialog displays the set of activities encapsulated within the current project. Select the desired activity
+ and click <uicontrol>OK</uicontrol>. Note that if the <uicontrol>Default activity</uicontrol> option is selected
+ on the <uicontrol>Main</uicontrol> tab of the Run Configurations dialog, the <uicontrol>Activity</uicontrol>
+ field is ignored and thus you cannot reach this dialog.</p>
+ </section>
+ </refbody>
+</reference>
diff --git a/src/help/studio_help/src/topics/u_select-columns.dita b/src/help/studio_help/src/topics/u_select-columns.dita
new file mode 100644
index 0000000..31023b8
--- /dev/null
+++ b/src/help/studio_help/src/topics/u_select-columns.dita
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE reference PUBLIC "-//OASIS//DTD DITA Reference//EN" "../dtd/reference.dtd">
+<reference id="u_select-columns" xml:lang="en-us">
+ <title>Select Table Columns dialog</title>
+ <shortdesc>Allows you to select the database columns that are to be displayed within a new Database List
+ activity.</shortdesc>
+ <prolog>
+ <metadata>
+ <keywords>
+ <!--<indexterm></indexterm>-->
+ </keywords>
+ </metadata>
+ </prolog>
+ <refbody>
+ <section>
+ <p>When creating a new activity based upon the Database List template, after selecting the database table you are
+ then asked to select the table columns to be displayed in the list. Use this dialog to select the columns. Note
+ that by default all of the table's columns are selected.</p>
+ </section>
+ </refbody>
+</reference>
diff --git a/src/help/studio_help/src/topics/u_select-instance.dita b/src/help/studio_help/src/topics/u_select-instance.dita
new file mode 100644
index 0000000..e93bad5
--- /dev/null
+++ b/src/help/studio_help/src/topics/u_select-instance.dita
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE reference PUBLIC "-//OASIS//DTD DITA Reference//EN" "../dtd/reference.dtd">
+<!-- get to this dialog by clicking Browse next to Instance in the Run Configurations dialog (Main tab) -->
+<reference id="u_select-instance" xml:lang="en-us">
+ <title>Instance Selection dialog</title>
+ <shortdesc>Allows you to select the device instance that will serve as the target for the run configuration. This
+ dialog appears when you click <uicontrol>Browse</uicontrol> next to the <uicontrol>Instance</uicontrol> field on the
+ Main tab of an <uicontrol>Android Application using Studio for Android</uicontrol> run configuration.</shortdesc>
+ <prolog>
+ <metadata>
+ <keywords>
+ <!--<indexterm></indexterm>-->
+ </keywords>
+ </metadata>
+ </prolog>
+ <refbody>
+ <section>
+ <p>This dialog displays the set of device instances currently known to MOTODEV Studio for Android. This includes
+ physical devices that are currently connected to your development computer as well as any AVDs you have created
+ (whether or not they are running). Note that if you create a run configuration that targets a physical Android
+ device and then unplug that device, the configuration becomes invalid. You can only run such a configuration if
+ you either select a different device instance or once again plug in the device used to create the
+ configuration.</p>
+ </section>
+ </refbody>
+</reference>
diff --git a/src/help/studio_help/src/topics/u_select-project.dita b/src/help/studio_help/src/topics/u_select-project.dita
new file mode 100644
index 0000000..fcd81da
--- /dev/null
+++ b/src/help/studio_help/src/topics/u_select-project.dita
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE reference PUBLIC "-//OASIS//DTD DITA Reference//EN" "../dtd/reference.dtd">
+<!-- get to this dialog by clicking Browse next to Project in the Run Configurations dialog (Main tab) -->
+<reference id="u_select-project" xml:lang="en-us">
+ <title>Project Selection dialog</title>
+ <shortdesc>Allows you to select the Android project containing the application to be run by a run configuration. This
+ dialog appears when you click <uicontrol>Browse</uicontrol> next to the <uicontrol>Project</uicontrol> field on the
+ Main tab of an <uicontrol>Android Application using Studio for Android</uicontrol> run configuration.</shortdesc>
+ <prolog>
+ <metadata>
+ <keywords>
+ <!--<indexterm></indexterm>-->
+ </keywords>
+ </metadata>
+ </prolog>
+ <refbody>
+ <section>
+ <p>This dialog displays the set of projects associated with the current workspace. Select the appropriate project
+ and click <uicontrol>OK</uicontrol>. Note that you cannot create a run configuration that references a project
+ outside of the current workspace.</p>
+ </section>
+ </refbody>
+</reference>
diff --git a/src/help/studio_help/src/topics/u_select-table.dita b/src/help/studio_help/src/topics/u_select-table.dita
new file mode 100644
index 0000000..09c66fd
--- /dev/null
+++ b/src/help/studio_help/src/topics/u_select-table.dita
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE reference PUBLIC "-//OASIS//DTD DITA Reference//EN" "../dtd/reference.dtd">
+<reference id="u_select-table" xml:lang="en-us">
+ <title>Select Database Table dialog</title>
+ <shortdesc>Allows you to select the database table that is to be displayed within a new Database List
+ activity.</shortdesc>
+ <prolog>
+ <metadata>
+ <keywords>
+ <!--<indexterm></indexterm>-->
+ </keywords>
+ </metadata>
+ </prolog>
+ <refbody>
+ <section>
+ <p>When creating a new activity based upon the Database List template, you are first asked to select the database
+ table that should be displayed. Use this dialog to select the table. After this dialog you will be given a
+ chance to select the individual columns within the table that are to be displayed.</p>
+ </section>
+ </refbody>
+</reference>
diff --git a/src/help/studio_help/src/topics/u_sign-cert-properties.dita b/src/help/studio_help/src/topics/u_sign-cert-properties.dita
new file mode 100644
index 0000000..84de26e
--- /dev/null
+++ b/src/help/studio_help/src/topics/u_sign-cert-properties.dita
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE reference PUBLIC "-//OASIS//DTD DITA Reference//EN" "../dtd/reference.dtd">
+<reference id="u_sign-cert-properties" xml:lang="en-us">
+ <title>Key Properties dialog</title>
+ <shortdesc>Allows you to see the various properties of a key.</shortdesc>
+ <prolog>
+ <metadata>
+ <keywords>
+ <!--<indexterm></indexterm>-->
+ </keywords>
+ </metadata>
+ </prolog>
+ <refbody>
+ <section>
+ <p>This dialog is displayed when you select a key and then click <image href="../images/KeyProperties.png"/> (View
+ Key Properties) in the Signing and Keys view (or when you right-click the key and select <uicontrol>View Key
+ Properties</uicontrol>).</p>
+ <simpletable>
+ <strow>
+ <stentry><uicontrol>Alias name</uicontrol></stentry>
+ <stentry>A unique name to be given to this key pair. Key pairs are identified by alias within the
+ keystore.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>First and Last Name</uicontrol></stentry>
+ <stentry>Your name.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Organization</uicontrol></stentry>
+ <stentry>The name of the company you are working for.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Organization unit</uicontrol></stentry>
+ <stentry>The company branch or division you are working for.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>City or Locality</uicontrol></stentry>
+ <stentry>The full name of your city or locality. For instance, "New York".</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>State or Province</uicontrol></stentry>
+ <stentry>Your state or province. This should be a full name. For instance, "California".</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Country Code (XX)</uicontrol></stentry>
+ <stentry>Select your country from the list.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Creation Date</uicontrol></stentry>
+ <stentry>The date (and time) that the key was created.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Expiration Date</uicontrol></stentry>
+ <stentry>The date (and time) when the key will expire (and is thus no longer valid). Although an app signed
+ with a key that has since expired will continue to launch, users won't be able to download or update an app
+ signed with an expired key.</stentry>
+ </strow>
+ </simpletable>
+ </section>
+ </refbody>
+</reference>
diff --git a/src/help/studio_help/src/topics/u_sign-create-key-pair.dita b/src/help/studio_help/src/topics/u_sign-create-key-pair.dita
new file mode 100644
index 0000000..49dc18c
--- /dev/null
+++ b/src/help/studio_help/src/topics/u_sign-create-key-pair.dita
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE reference PUBLIC "-//OASIS//DTD DITA Reference//EN" "../dtd/reference.dtd">
+<reference id="u_sign-create-key-pair" xml:lang="en-us">
+ <title>Create Key Pair dialog</title>
+ <shortdesc>Contains fields for each of the components of a distinguished name (DN) that identifies you as the person
+ generating the key pair. </shortdesc>
+ <prolog>
+ <metadata>
+ <keywords>
+ <!--<indexterm></indexterm>-->
+ </keywords>
+ </metadata>
+ </prolog>
+ <refbody>
+ <section>
+ <p>This dialog is displayed when you click <image href="../images/create-key-pair-button.png"/> (Create Key Pair)
+ in the Application Signing Tool view.</p>
+ <p>You must supply a value for each of the form's fields in order to generate the public-private key pair.</p>
+ <simpletable>
+ <strow>
+ <stentry><uicontrol>Alias name</uicontrol></stentry>
+ <stentry>A unique name to be given to this key pair. Certificates and key pairs are identified by alias within
+ the Application Signing Tool.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>First and Last Name</uicontrol></stentry>
+ <stentry>Your name.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Organization</uicontrol></stentry>
+ <stentry>The name of the company you are working for.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Organization unit</uicontrol></stentry>
+ <stentry>The company branch or division you are working for.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>City or Locality</uicontrol></stentry>
+ <stentry>The full name of your city or locality. For instance, "New York".</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>State or Province</uicontrol></stentry>
+ <stentry>Your state or province. This should be a full name. For instance, "California".</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Country Code (XX)</uicontrol></stentry>
+ <stentry>Select your country from the list.</stentry>
+ </strow>
+ </simpletable>
+ </section>
+ </refbody>
+</reference>
diff --git a/src/help/studio_help/src/topics/u_sign-create-keystore.dita b/src/help/studio_help/src/topics/u_sign-create-keystore.dita
new file mode 100644
index 0000000..29b1e3f
--- /dev/null
+++ b/src/help/studio_help/src/topics/u_sign-create-keystore.dita
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE reference PUBLIC "-//OASIS//DTD DITA Reference//EN" "../dtd/reference.dtd">
+<reference id="u_sign-create-keystore" xml:lang="en-us">
+ <title>Create Keystore dialog</title>
+ <shortdesc>Allows you to create a new keystore. A keystore contains keys for use when signing Android packages (APKs);
+ each key must reside within a keystore.</shortdesc>
+ <prolog>
+ <metadata>
+ <keywords>
+ <!--<indexterm></indexterm>-->
+ </keywords>
+ </metadata>
+ </prolog>
+ <refbody>
+ <section>
+ <p>This dialog is displayed when you click <image href="../images/KeystoreCreate.png"/> (Create Keystore) in the
+ Signing and Keys view.</p>
+ <simpletable>
+ <strow>
+ <stentry><uicontrol>Keystore Filename</uicontrol></stentry>
+ <stentry>The filename (including its path) of the keystore file to be created.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Keystore Type</uicontrol></stentry>
+ <stentry>The type of the file-based keystore to be created. Either JKS (the standard Java keystore), JCEKS (a
+ Java keystore that provides much stronger protection of private keys), or PKCS12 (a file format defined as
+ part of the Public-Key Cryptography Standards [PKCS] that can be created and interpreted using the OpenSSL
+ pkcs12 command).</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Use keystore type as keystore filename extension</uicontrol></stentry>
+ <stentry>If selected (it is, by default), the keystore file is given an extension that reflects the keystore's
+ type.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Keystore Password</uicontrol></stentry>
+ <stentry>A password that protects this keystore. You must supply this password when you add to or alter the
+ keys within the keystore. Note that if you select <uicontrol>Save this password</uicontrol>, MOTODEV Studio
+ will remember the password and supply it for you when you add or delete keys within this keystore.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Confirm Password</uicontrol></stentry>
+ <stentry>Enter the keystore password again, to confirm that what you entered is what you intended.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Save this password</uicontrol></stentry>
+ <stentry>Select this option if you want MOTODEV Studio to automatically supply the password when you add keys
+ to this keystore or delete keys within this keystore. Leave this option unselected if you want MOTODEV
+ Studio to prompt for the password for each operation.</stentry>
+ </strow>
+ </simpletable>
+ </section>
+ </refbody>
+</reference>
diff --git a/src/help/studio_help/src/topics/u_sign-create-self-cert.dita b/src/help/studio_help/src/topics/u_sign-create-self-cert.dita
new file mode 100644
index 0000000..7d46a2d
--- /dev/null
+++ b/src/help/studio_help/src/topics/u_sign-create-self-cert.dita
@@ -0,0 +1,74 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE reference PUBLIC "-//OASIS//DTD DITA Reference//EN" "../dtd/reference.dtd">
+<reference id="u_sign-create-self-cert" xml:lang="en-us">
+ <title>Create Key dialog</title>
+ <shortdesc>Allows you to create a new self-signing key for use when signing Android packages (APKs). Contains fields
+ for the key name (its "alias"), for each of the components of a distinguished name (DN; used to identify the creator
+ of a key pair or certificate) and for a password used to protect the key.</shortdesc>
+ <prolog>
+ <metadata>
+ <keywords>
+ <!--<indexterm></indexterm>-->
+ </keywords>
+ </metadata>
+ </prolog>
+ <refbody>
+ <section>
+ <p>This dialog is displayed when you select a keystore and then click <image href="../images/KeyCreate.png"/>
+ (Create Key) in the Signing and Keys view. The new key will be placed in the selected keystore.</p>
+ <p>You must supply a value for each of the starred (*) form fields in order to generate the public-private key
+ pair.</p>
+ <simpletable>
+ <strow>
+ <stentry><uicontrol>Alias name</uicontrol></stentry>
+ <stentry>A unique name to be given to this key pair. Key pairs are identified by alias within the
+ keystore.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>First and Last Name</uicontrol></stentry>
+ <stentry>Your name.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Organization</uicontrol></stentry>
+ <stentry>The name of the company you are working for.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Organization unit</uicontrol></stentry>
+ <stentry>The company branch or division you are working for.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>City or Locality</uicontrol></stentry>
+ <stentry>The full name of your city or locality. For instance, "New York".</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>State or Province</uicontrol></stentry>
+ <stentry>Your state or province. This should be a full name. For instance, "California".</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Country Code (XX)</uicontrol></stentry>
+ <stentry>Select your country from the list.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Validity (years)</uicontrol></stentry>
+ <stentry>How long (in years) the certificate should be considered valid. </stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Password</uicontrol></stentry>
+ <stentry>A password that protects this key. You must supply this password when you use this key to sign an
+ Android package. Note that if you select <uicontrol>Save this password</uicontrol>, MOTODEV Studio will
+ remember the password and supply it for you when you use this key to sign a package.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Confirm Password</uicontrol></stentry>
+ <stentry>Enter the key password again, to confirm that what you entered is what you intended.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Save this password</uicontrol></stentry>
+ <stentry>Select this option if you want MOTODEV Studio to automatically supply the password when you sign
+ packages with this key. Leave this option unselected if you want MOTODEV Studio to prompt for the password
+ each time this key is used.</stentry>
+ </strow>
+ </simpletable>
+ </section>
+ </refbody>
+</reference>
diff --git a/src/help/studio_help/src/topics/u_sign-import-cert.dita b/src/help/studio_help/src/topics/u_sign-import-cert.dita
new file mode 100644
index 0000000..a4d06fd
--- /dev/null
+++ b/src/help/studio_help/src/topics/u_sign-import-cert.dita
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE reference PUBLIC "-//OASIS//DTD DITA Reference//EN" "../dtd/reference.dtd">
+<reference id="u_sign-import-cert" xml:lang="en-us">
+ <title>Import Certificate dialog</title>
+ <shortdesc>Allows you to specify a certificate to be imported. </shortdesc>
+ <prolog>
+ <metadata>
+ <keywords>
+ <!--<indexterm></indexterm>-->
+ </keywords>
+ </metadata>
+ </prolog>
+ <refbody>
+ <section>
+ <p>This dialog is displayed when you click (Import Certificate) in the Application Signing Tool view.</p>
+ <simpletable>
+ <strow>
+ <stentry><uicontrol>Alias name</uicontrol></stentry>
+ <stentry>A unique name to be given to this certificate. Certificates and key pairs are identified by alias
+ within the Application Signing Tool.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Certificate</uicontrol></stentry>
+ <stentry>Path to the certificate to be imported.</stentry>
+ </strow>
+ </simpletable>
+ </section>
+ </refbody>
+</reference>
diff --git a/src/help/studio_help/src/topics/u_sign-import-key.dita b/src/help/studio_help/src/topics/u_sign-import-key.dita
new file mode 100644
index 0000000..27a453f
--- /dev/null
+++ b/src/help/studio_help/src/topics/u_sign-import-key.dita
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE reference PUBLIC "-//OASIS//DTD DITA Reference//EN" "../dtd/reference.dtd">
+<reference id="u_sign-import-key" xml:lang="en-us">
+ <title>Import Key dialog</title>
+ <shortdesc>Allows you to specify a public-private key pair to be imported. </shortdesc>
+ <prolog>
+ <metadata>
+ <keywords>
+ <!--<indexterm></indexterm>-->
+ </keywords>
+ </metadata>
+ </prolog>
+ <refbody>
+ <section>
+ <p>This dialog is displayed when you click (Import Key) in the Application Signing Tool view.</p>
+ <simpletable>
+ <strow>
+ <stentry><uicontrol>Alias name</uicontrol></stentry>
+ <stentry>A unique name to be given to this key pair. Certificates and key pairs are identified by alias within
+ the Application Signing Tool.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Certificate</uicontrol></stentry>
+ <stentry>Path to the private key file to be imported.</stentry>
+ </strow>
+ </simpletable>
+ </section>
+ </refbody>
+</reference>
diff --git a/src/help/studio_help/src/topics/u_sign-import-keystore-entries.dita b/src/help/studio_help/src/topics/u_sign-import-keystore-entries.dita
new file mode 100644
index 0000000..89ebb4d
--- /dev/null
+++ b/src/help/studio_help/src/topics/u_sign-import-keystore-entries.dita
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE reference PUBLIC "-//OASIS//DTD DITA Reference//EN" "../dtd/reference.dtd">
+<reference id="u_sign-import-keystore-entries" xml:lang="en-us">
+ <title>Import Entries dialog</title>
+ <shortdesc>Allows you to import into a keystore selected entries (keys) from another keystore. </shortdesc>
+ <prolog>
+ <metadata>
+ <keywords>
+ <!--<indexterm></indexterm>-->
+ </keywords>
+ </metadata>
+ </prolog>
+ <refbody>
+ <section>
+ <p>This dialog is displayed when you click <image href="../images/KeystoreEntriesImport.png"/> (Import Entries) in
+ the Signing and Keys view.</p>
+ <simpletable>
+ <strow>
+ <stentry>(Source) <uicontrol>Keystore</uicontrol></stentry>
+ <stentry>Path to the keystore file containing one or more keys to be imported.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Password</uicontrol></stentry>
+ <stentry>The password controls provides access to the contents of the source keystore.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Load</uicontrol></stentry>
+ <stentry>Loads the contents of the source keystore and displays its contents in the list box in the center of
+ the dialog.</stentry>
+ </strow>
+ <strow>
+ <stentry>Source keystore entries</stentry>
+ <stentry>List of possible keystore entries for importation. Select those that should be imported.</stentry>
+ </strow>
+ <strow>
+ <stentry>(Target) <uicontrol>Keystore</uicontrol></stentry>
+ <stentry>The keystore to which the imported entries are to be added.</stentry>
+ </strow>
+ </simpletable>
+ </section>
+ </refbody>
+</reference>
diff --git a/src/help/studio_help/src/topics/u_sign-import-keystore.dita b/src/help/studio_help/src/topics/u_sign-import-keystore.dita
new file mode 100644
index 0000000..4ceb318
--- /dev/null
+++ b/src/help/studio_help/src/topics/u_sign-import-keystore.dita
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE reference PUBLIC "-//OASIS//DTD DITA Reference//EN" "../dtd/reference.dtd">
+<reference id="u_sign-import-keystore" xml:lang="en-us">
+ <title>Import Keystore dialog</title>
+ <shortdesc>Allows you to import a keystore created outside of MOTODEV Studio for use within MOTODEV Studio's Signing
+ and Keys view. </shortdesc>
+ <prolog>
+ <metadata>
+ <keywords>
+ <!--<indexterm></indexterm>-->
+ </keywords>
+ </metadata>
+ </prolog>
+ <refbody>
+ <section>
+ <p>This dialog is displayed when you click <image href="../images/KeystoreImport.png"/> (Import Keystore) in the
+ Signing and Keys view.</p>
+ <simpletable>
+ <strow>
+ <stentry><uicontrol>Keystore Filename</uicontrol></stentry>
+ <stentry>Path to the keystore file to be imported.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Keystore Type</uicontrol></stentry>
+ <stentry>List of keystore types (JKS, JCEKS, or PCKS12). The keystore being imported must be one of the listed
+ types, and you must identify the keystore's type using this list.</stentry>
+ </strow>
+ </simpletable>
+ </section>
+ </refbody>
+</reference>
diff --git a/src/help/studio_help/src/topics/u_sign-keystore-backup-restore.dita b/src/help/studio_help/src/topics/u_sign-keystore-backup-restore.dita
new file mode 100644
index 0000000..ebb4b72
--- /dev/null
+++ b/src/help/studio_help/src/topics/u_sign-keystore-backup-restore.dita
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE reference PUBLIC "-//OASIS//DTD DITA Reference//EN" "../dtd/reference.dtd">
+<reference id="u_sign-keystore-backup-restore" xml:lang="en-us">
+ <title>Restore From Backup dialog</title>
+ <shortdesc>Allows you to restore one or more keystores that had been backed up to a zip archive.</shortdesc>
+ <prolog>
+ <metadata>
+ <keywords>
+ <!--<indexterm></indexterm>-->
+ </keywords>
+ </metadata>
+ </prolog>
+ <refbody>
+ <section>
+ <p>This dialog is displayed when you click <image href="../images/KeystoresRestore.png"/> (Restore From Backup) in
+ the Signing and Keys view.</p>
+ <simpletable>
+ <strow>
+ <stentry><uicontrol>Backup File</uicontrol></stentry>
+ <stentry>The archive (zip) file containing the backed-up keystore file(s).</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Destination</uicontrol></stentry>
+ <stentry>The directory into which the keystores are to be restored.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Keystores</uicontrol></stentry>
+ <stentry>The keystores to be restored. Select at least one. If any of the keystores being restored already
+ exist in the destination directory, you will be prompted before each keystore file is overwritten.</stentry>
+ </strow>
+ </simpletable>
+ </section>
+ </refbody>
+</reference>
diff --git a/src/help/studio_help/src/topics/u_sign-keystore-backup.dita b/src/help/studio_help/src/topics/u_sign-keystore-backup.dita
new file mode 100644
index 0000000..109b8de
--- /dev/null
+++ b/src/help/studio_help/src/topics/u_sign-keystore-backup.dita
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE reference PUBLIC "-//OASIS//DTD DITA Reference//EN" "../dtd/reference.dtd">
+<reference id="u_sign-keystore-backup" xml:lang="en-us">
+ <title>Keystores Backup dialog</title>
+ <shortdesc>Allows you to easily back up one or more keystores to a zip archive.</shortdesc>
+ <prolog>
+ <metadata>
+ <keywords>
+ <!--<indexterm></indexterm>-->
+ </keywords>
+ </metadata>
+ </prolog>
+ <refbody>
+ <section>
+ <p>This dialog is displayed when you click <image href="../images/KeystoresBackup.png"/> (Back Up Keystores) in
+ the Signing and Keys view.</p>
+ <simpletable>
+ <strow>
+ <stentry><uicontrol>Backup File</uicontrol></stentry>
+ <stentry>The archive (zip) file to which you want to back up your keystore file(s). If you select an existing
+ file, that file will be overwritten.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Keystores</uicontrol></stentry>
+ <stentry>The keystores to be backed up. Select at least one to copy to the archive file. </stentry>
+ </strow>
+ </simpletable>
+ </section>
+ </refbody>
+</reference>
diff --git a/src/help/studio_help/src/topics/u_sign-keystore-type-changing.dita b/src/help/studio_help/src/topics/u_sign-keystore-type-changing.dita
new file mode 100644
index 0000000..64e7273
--- /dev/null
+++ b/src/help/studio_help/src/topics/u_sign-keystore-type-changing.dita
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE reference PUBLIC "-//OASIS//DTD DITA Reference//EN" "../dtd/reference.dtd">
+<reference id="u_sign-keystore-type-changing" xml:lang="en-us">
+ <title>Change Keystore Type dialog</title>
+ <shortdesc>Allows you to change the file format of a keystore.</shortdesc>
+ <prolog>
+ <metadata>
+ <keywords>
+ <!--<indexterm></indexterm>-->
+ </keywords>
+ </metadata>
+ </prolog>
+ <refbody>
+ <section>
+ <p>This dialog is displayed when you click <image href="../images/KeystoreTypeChange.png"/> (Change Keystore Type)
+ in the Signing and Keys view.</p>
+ <simpletable>
+ <strow>
+ <stentry><uicontrol>Keystore</uicontrol></stentry>
+ <stentry>The keystore file for which the format is to be changed. Note that the keystore file must already be
+ known to the Signing and Keys view. If it isn't in the drop-down list, you'll need to import it
+ first.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Password</uicontrol></stentry>
+ <stentry>The password that protects this keystore. If you selected <uicontrol>Save this password</uicontrol>
+ either when you created the keystore or during a previous keystore operation, MOTODEV Studio supplies it for
+ you.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Load</uicontrol></stentry>
+ <stentry>Reads the contents of the keystore and displays them in the <uicontrol>Entries</uicontrol> table,
+ below.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>New Type</uicontrol></stentry>
+ <stentry>The format you wish the file-based keystore to have. Either JKS (the standard Java keystore), JCEKS
+ (a Java keystore that provides much stronger protection of private keys), or PKCS12 (a file format defined
+ as part of the Public-Key Cryptography Standards [PKCS] that can be created and interpreted using the
+ OpenSSL pkcs12 command). Note that the keystore's current format is not included in the list of new
+ types.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>Entries</uicontrol></stentry>
+ <stentry>The keys and certificates contained within the keystore. MOTODEV Studio automatically fills in the
+ passwords for those keys for which you previously indicated that the password should be saved. You need to
+ supply the password for any keys where the <uicontrol>Verified</uicontrol> column indicates "Not verified";
+ this indicates that the password has yet to be supplied or is incorrect.</stentry>
+ </strow>
+ </simpletable>
+ </section>
+ </refbody>
+</reference>
diff --git a/src/help/studio_help/src/topics/u_studio-prefs.dita b/src/help/studio_help/src/topics/u_studio-prefs.dita
new file mode 100644
index 0000000..8c6ad10
--- /dev/null
+++ b/src/help/studio_help/src/topics/u_studio-prefs.dita
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE reference PUBLIC "-//OASIS//DTD DITA Reference//EN" "../dtd/reference.dtd">
+<reference id="u_studio-prefs" xml:lang="en-us">
+ <title>MOTODEV Studio preferences</title>
+ <shortdesc>Preferences that are specific to MOTODEV Studio.</shortdesc>
+ <prolog>
+ <metadata>
+ <keywords>
+ <!--<indexterm></indexterm>-->
+ </keywords>
+ </metadata>
+ </prolog>
+ <refbody> </refbody>
+</reference>
diff --git a/src/help/studio_help/src/topics/u_studio-prefs_android.dita b/src/help/studio_help/src/topics/u_studio-prefs_android.dita
new file mode 100644
index 0000000..d1ff9fa
--- /dev/null
+++ b/src/help/studio_help/src/topics/u_studio-prefs_android.dita
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE reference
+ PUBLIC "-//OASIS//DTD DITA Reference//EN" "../dtd/reference.dtd">
+<reference xml:lang="en-us" id="u_studio-prefs_android">
+ <title>MOTODEV Studio for Android preferences</title>
+ <shortdesc>Contains preferences specific to MOTODEV Studio for Android. </shortdesc>
+ <prolog>
+ <metadata>
+ <keywords>
+ <!--<indexterm></indexterm>-->
+ </keywords>
+ </metadata>
+ </prolog>
+ <refbody>
+ <section>To access these preferences, select <uicontrol>Preferences</uicontrol> from the <ph
+ conref="g_variables.dita#g_variables/studio-sdk-prefs-parent-menu"/> menu. Then from the list of preferences,
+ select <menucascade><uicontrol>MOTODEV Studio</uicontrol><uicontrol>MOTODEV Studio for
+ Android</uicontrol></menucascade>.</section>
+ <section>
+ <title>MOTODEV Studio for Android Dialogs</title>
+ <simpletable>
+ <strow>
+ <stentry>
+ <uicontrol>Clear</uicontrol>
+ </stentry>
+ <stentry>Click <uicontrol>Clear</uicontrol> to once again be presented with all dialogs that you may have
+ permanently dismissed by selecting the "do not show again" and "always proceed without asking"
+ options.</stentry>
+ </strow>
+ <strow>
+ <stentry><uicontrol>API Key</uicontrol></stentry>
+ <stentry>In order to use Google's translation service to automatically translate text strings within the
+ Localization Files Editor you must have obtained a key from Google and entered it into this field. Click the
+ link above this field to be taken to the Google APIs Console, from which you can obtain the needed key. See
+ the "Configuring the Localization Files Editor" help topic (under "Localizing Text Strings") for
+ step-by-step instructions on how to obtain this key.</stentry>
+ </strow>
+ </simpletable>
+ </section>
+ </refbody>
+</reference>
diff --git a/src/help/studio_help/src/topics/u_studio-prefs_appstore.dita b/src/help/studio_help/src/topics/u_studio-prefs_appstore.dita
new file mode 100644
index 0000000..1388c09
--- /dev/null
+++ b/src/help/studio_help/src/topics/u_studio-prefs_appstore.dita
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE reference
+ PUBLIC "-//OASIS//DTD DITA Reference//EN" "../dtd/reference.dtd">
+<reference xml:lang="en-us" id="u_studio-prefs_appstore">
+ <title>MOTODEV Login preferences</title>
+ <shortdesc>Contains preferences that are used when publishing applications to Motorola, Inc. </shortdesc>
+ <prolog>
+ <metadata>
+ <keywords>
+ <!--<indexterm></indexterm>-->
+ </keywords>
+ </metadata>
+ </prolog>
+ <refbody>
+ <section>
+ <p>To access these preferences, select <uicontrol>Preferences</uicontrol> from the <ph
+ conref="g_variables.dita#g_variables/studio-sdk-prefs-parent-menu"/> menu. Then from the list of preferences,
+ select <menucascade><uicontrol>MOTODEV Studio</uicontrol><uicontrol>MOTODEV Login</uicontrol></menucascade>.</p>
+ <p>When connecting to the web site used to publish applications to Motorola, Inc., if you save your credentials the
+ Login and Password fields will be pre-populated in future sessions. Use this preferences dialog to edit or
+ remove those credentials.</p>
+ <simpletable>
+ <strow>
+ <stentry>
+ <uicontrol>Login</uicontrol> and <uicontrol>Password</uicontrol>
+ </stentry>
+ <stentry>Your credentials. Normally these are your MOTODEV login and password.</stentry>
+ </strow>
+ </simpletable>
+ </section>
+ </refbody>
+</reference>
diff --git a/src/help/studio_help/src/topics/u_studio-prefs_db.dita b/src/help/studio_help/src/topics/u_studio-prefs_db.dita
new file mode 100644
index 0000000..d934e1b
--- /dev/null
+++ b/src/help/studio_help/src/topics/u_studio-prefs_db.dita
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE reference
+ PUBLIC "-//OASIS//DTD DITA Reference//EN" "../dtd/reference.dtd">
+<reference xml:lang="en-us" id="u_studio-prefs_db">
+ <title>MOTODEV Database for Devices preferences</title>
+ <shortdesc>Contains preferences that are used when working with databases within MOTODEV Studio.</shortdesc>
+ <prolog>
+ <metadata>
+ <keywords>
+ <!--<indexterm></indexterm>-->
+ </keywords>
+ </metadata>
+ </prolog>
+ <refbody><section>To access these
+ preferences, select <uicontrol>Preferences</uicontrol> from the <ph
+ conref="g_variables.dita#g_variables/studio-sdk-prefs-parent-menu"/> menu. Then from the list of preferences,
+ select <menucascade><uicontrol>MOTODEV Studio</uicontrol><uicontrol>MOTODEV Database</uicontrol></menucascade>.</section>
+ <section>
+ <simpletable>
+ <strow>
+ <stentry>
+ <uicontrol>Database Temporary Location</uicontrol>
+ </stentry>
+ <stentry>Allows you to specify the directory into which temporary databases are written. Temporary databases
+ are created when you connect to an on-device database from the MOTODEV Database perspective's MOTODEV
+ Database Explorer view.</stentry>
+ </strow>
+ </simpletable>
+ </section>
+ </refbody>
+</reference>
diff --git a/src/help/studio_help/src/topics/u_studio-prefs_emulator.dita b/src/help/studio_help/src/topics/u_studio-prefs_emulator.dita
new file mode 100644
index 0000000..0991586
--- /dev/null
+++ b/src/help/studio_help/src/topics/u_studio-prefs_emulator.dita
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE reference
+ PUBLIC "-//OASIS//DTD DITA Reference//EN" "../dtd/reference.dtd">
+<reference xml:lang="en-us" id="u_studio-prefs_emulator">
+ <title>Android Emulator preferences</title>
+ <shortdesc>Contains preferences that control the operation of the Android Emulator view.</shortdesc>
+ <prolog>
+ <metadata>
+ <keywords>
+ <!--<indexterm></indexterm>-->
+ </keywords>
+ </metadata>
+ </prolog>
+ <refbody><section>To access these preferences,
+ select <uicontrol>Preferences</uicontrol> from the <menucascade><uicontrol>File</uicontrol><uicontrol
+ >Window</uicontrol></menucascade> menu. Then from the list of preferences, select <menucascade><uicontrol>MOTODEV
+ Studio</uicontrol><uicontrol>Android Emulator</uicontrol></menucascade>.</section>
+ <section>
+ <title>MOTODEV Studio for Android Dialogs</title>
+ <simpletable>
+ <strow>
+ <stentry>
+ <uicontrol>Show emulators externally upon closing the Emulator View</uicontrol>
+ </stentry>
+ <stentry>This option should normally be selected. Clear this option if the emulator unexpectedly stops when
+ you close the Android Emulator view. If this option is cleared, when you close the Android Emulator view the
+ emulator will continue to run although it will not be displayed, either in an Eclipse view or in an external
+ window. Re-opening the Android Emulator view will cause the running emulator to once again be
+ displayed.<note>This option is not applicable on systems running Mac OS X.</note></stentry>
+ </strow>
+ </simpletable>
+ </section>
+ </refbody>
+</reference>
diff --git a/src/help/studio_help/src/topics/u_video-view.dita b/src/help/studio_help/src/topics/u_video-view.dita
new file mode 100644
index 0000000..fba11d5
--- /dev/null
+++ b/src/help/studio_help/src/topics/u_video-view.dita
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE reference PUBLIC "-//OASIS//DTD DITA Reference//EN" "../dtd/reference.dtd">
+<reference id="u_video-view" xml:lang="en-us">
+ <title>MOTODEV Video Tutorials view</title>
+ <shortdesc>Presents tutorial videos that demonstrate how to use MOTODEV Studio. This view is part of the MOTODEV
+ Studio for Android perspective. It can also be opened by selecting <menucascade>
+ <uicontrol>Window</uicontrol><uicontrol>Show View</uicontrol><uicontrol>Other</uicontrol>
+ </menucascade> and then selecting <menucascade>
+ <uicontrol>MOTODEV Studio</uicontrol><uicontrol>MOTODEV Video Tutorials</uicontrol>
+ </menucascade>. </shortdesc>
+ <prolog>
+ <metadata>
+ <keywords>
+ <!--<indexterm></indexterm>-->
+ </keywords>
+ </metadata>
+ </prolog>
+ <refbody>
+ <section>
+ <p>The MOTODEV Video Tutorials view presents instructional videos from a dedicated YouTube channel that is
+ maintained by Motorola Mobility. You can either watch these videos in the MOTODEV Video Tutorials view or in
+ your browser. Select <uicontrol>Open in External Browser</uicontrol> to show the selected video in your default
+ browser rather than in the view.</p>
+ <p>Use the view's <uicontrol>Search</uicontrol> box to search for terms within the video titles and
+ descriptions.</p>
+ </section>
+ </refbody>
+</reference>
diff --git a/src/help/studio_help/tools/ditaot/plugins/com.mot.mdb.deved.xhtml/plugin.xml b/src/help/studio_help/tools/ditaot/plugins/com.mot.mdb.deved.xhtml/plugin.xml
new file mode 100644
index 0000000..31ff52a
--- /dev/null
+++ b/src/help/studio_help/tools/ditaot/plugins/com.mot.mdb.deved.xhtml/plugin.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+ This is the plugin file that controls integration into the toolkit.
+ Each item in the plugin that extends a toolkit function must be listed
+ here. The only critical component is the catalog file, which must
+ extend the Toolkit's catalog in order for your files to be processed.
+
+ If nothing else is overridden, then the toolkit will use fallback processing.
+ This plugin implements overrides for XHTML (including Eclipse help) produced
+ by Motorola Dev Ed.
+
+ NOTE: paths in this file are relative to the current directory
+ (the same directory that includes this file).
+-->
+
+<plugin id="com.mot.mdb.deved.xhtml">
+
+ <!-- Extend the toolkit's XHTML processing to override XHTML output. -->
+ <feature extension="dita.xsl.xhtml" value="xsl/deved_xhtml.xsl" type="file"/>
+
+ <feature extension="dita.xsl.eclipse.plugin" value="xsl/deved_map2plugin.xsl" type="file"/>
+
+ <!-- ************************************************************
+ Any other extensions to standard toolkit files will be listed here.
+ For example, to extend the RTF transform:
+
+ <feature extension="dita.xsl.rtf" value="xsl/music2rtf.xsl" type="file"/>
+
+ To extend the docbook transform:
+ <feature extension="dita.xsl.docbook" value="xsl/music2rtf.xsl" type="file"/>
+
+ -->
+
+</plugin>
diff --git a/src/help/studio_help/tools/ditaot/plugins/com.mot.mdb.deved.xhtml/readme.dita b/src/help/studio_help/tools/ditaot/plugins/com.mot.mdb.deved.xhtml/readme.dita
new file mode 100644
index 0000000..9bfbdb7
--- /dev/null
+++ b/src/help/studio_help/tools/ditaot/plugins/com.mot.mdb.deved.xhtml/readme.dita
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE reference PUBLIC "-//OASIS//DTD DITA Reference//EN"
+ "reference.dtd">
+
+<!-- (C) Copyright IBM Corporation 2006 All Rights Reserved. -->
+<reference id="readme" xml:lang="en-us">
+ <title>Motorola Dev Ed XHTML processing overrides</title>
+ <shortdesc>This DITA Open Toolkit plug-in implements overrides to standard XHTML processing that Motorola Dev Ed
+ uses in all such output.</shortdesc>
+ <refbody>
+
+
+
+
+
+ </refbody>
+</reference>
diff --git a/src/help/studio_help/tools/ditaot/plugins/com.mot.mdb.deved.xhtml/xsl/deved_map2plugin.xsl b/src/help/studio_help/tools/ditaot/plugins/com.mot.mdb.deved.xhtml/xsl/deved_map2plugin.xsl
new file mode 100644
index 0000000..d1b3cfc
--- /dev/null
+++ b/src/help/studio_help/tools/ditaot/plugins/com.mot.mdb.deved.xhtml/xsl/deved_map2plugin.xsl
@@ -0,0 +1,122 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
+
+<xsl:import href="../../../xsl/map2pluginImpl.xsl"/>
+
+ <!-- -->
+ <!-- BEGIN OVERRIDE: USE CRLF RATHER THAN LF IN MANIFEST.MF -->
+ <!-- 2009.06.26 BG: Appears that Eclipse 3.5 requires this, so
+ I added explicit CRLF characters. Be careful not to change
+ the whitespace within this variable.-->
+ <!-- -->
+ <xsl:variable name="newline">
+<xsl:text>&#13;&#10;</xsl:text></xsl:variable>
+ <!-- END OVERRIDE: USE CRLF RATHER THAN LF IN MANIFEST.MF -->
+
+ <!-- -->
+ <!-- BEGIN OVERRIDE: PLUGIN.PROPERTIES -->
+ <!-- 2010.02.16 BG: Engineering requested we change name property to pluginName -->
+ <!-- -->
+ <xsl:template match="*[contains(@class, ' map/map ')]" mode="eclipse.properties">
+
+ <xsl:text># NLS_MESSAGEFORMAT_NONE</xsl:text><xsl:value-of select="$newline"/>
+ <xsl:text># NLS_ENCODING=UTF-8</xsl:text><xsl:value-of select="$newline"/>
+ <!--<xsl:value-of select="$newline"/>-->
+ <xsl:choose>
+ <xsl:when test="@title">
+ <xsl:text>pluginName=</xsl:text><xsl:value-of select="@title"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:text>name=Sample Title</xsl:text>
+ </xsl:otherwise>
+ </xsl:choose>
+ <xsl:value-of select="$newline"/>
+ <xsl:text>providerName=</xsl:text><xsl:value-of select="$provider"/>
+ </xsl:template>
+ <!-- END OVERRIDE: PLUGIN.PROPERTIES -->
+
+
+ <!-- -->
+ <!-- BEGIN OVERRIDE: MANIFEST.MF -->
+ <!-- 2010.02.18 BG: Engineering requested we change "name" property to "pluginName",
+ "Eclipse-LazyStart: true" to "Bundle-ActivationPolicy: lazy", and add
+ "Bundle-RequiredExecutionEnvironment: J2SE-1.5".-->
+ <!-- -->
+<xsl:template match="*[contains(@class, ' map/map ')]" mode="eclipse.manifest">
+
+ <xsl:text>Bundle-Version: </xsl:text><xsl:value-of select="$version"/><xsl:value-of select="$newline"/>
+ <xsl:text>Manifest-Version: 1.0</xsl:text><xsl:value-of select="$newline"/>
+ <xsl:text>Bundle-ManifestVersion: 2</xsl:text><xsl:value-of select="$newline"/>
+ <xsl:text>Bundle-Localization: plugin</xsl:text><xsl:value-of select="$newline"/>
+ <xsl:text>Bundle-Name: %pluginName</xsl:text><xsl:value-of select="$newline"/>
+ <xsl:text>Bundle-Vendor: %providerName</xsl:text><xsl:value-of select="$newline"/>
+
+ <xsl:choose>
+ <xsl:when test="$plugin='true'">
+ <xsl:text>Bundle-ActivationPolicy: lazy</xsl:text><xsl:value-of select="$newline"/>
+ <xsl:choose>
+ <xsl:when test="@id">
+ <xsl:text>Bundle-SymbolicName: </xsl:text><xsl:value-of select="@id"/>;<xsl:text> singleton:=true</xsl:text><xsl:value-of select="$newline"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:text>Bundle-SymbolicName: org.sample.help.doc; singleton:=true</xsl:text><xsl:value-of select="$newline"/>
+ <xsl:call-template name="output-message">
+ <xsl:with-param name="msgnum">050</xsl:with-param>
+ <xsl:with-param name="msgsev">W</xsl:with-param>
+ </xsl:call-template>
+ </xsl:otherwise>
+ </xsl:choose>
+ <xsl:text>Bundle-RequiredExecutionEnvironment: J2SE-1.5</xsl:text><xsl:value-of select="$newline"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:choose>
+ <xsl:when test="@id">
+ <xsl:if test="$fragment.lang!=''">
+ <xsl:text>Fragment-Host: </xsl:text><xsl:value-of select="@id"/>;
+ <xsl:text>Bundle-SymbolicName: </xsl:text>
+ <xsl:choose>
+ <xsl:when test="$fragment.country!=''">
+ <xsl:value-of select="@id"/>.<xsl:value-of select="$fragment.lang"/>.<xsl:value-of select="$fragment.country"/>;<xsl:text/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="@id"/>.<xsl:value-of select="$fragment.lang"/>;<xsl:text/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:if>
+ <xsl:if test="$fragment.lang=''">
+ <xsl:text>Bundle-SymbolicName: </xsl:text><xsl:value-of select="@id"/><xsl:value-of select="$newline"/>
+ </xsl:if>
+ </xsl:when>
+ <xsl:otherwise>
+
+ <xsl:text>Bundle-SymbolicName: org.sample.help.doc.</xsl:text>
+ <xsl:choose>
+ <xsl:when test="$fragment.lang!=''">
+ <xsl:choose>
+ <xsl:when test="$fragment.country!=''">
+ <xsl:value-of select="$fragment.lang"/>.<xsl:value-of select="$fragment.country"/>;
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="$fragment.lang"/>;
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:when>
+ <!-- We shouldn' t be getting here, but just in case -->
+ <xsl:otherwise>
+ <xsl:text>lang; </xsl:text>
+ </xsl:otherwise>
+ </xsl:choose>
+ <xsl:value-of select="$newline"/>
+ <xsl:text>Fragment-Host: org.sample.help.doc;</xsl:text><xsl:value-of select="$newline"/>
+ <xsl:call-template name="output-message">
+ <xsl:with-param name="msgnum">050</xsl:with-param>
+ <xsl:with-param name="msgsev">W</xsl:with-param>
+ </xsl:call-template>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+ <!-- END OVERRIDE: MANIFEST.MF -->
+
+</xsl:stylesheet>
diff --git a/src/help/studio_help/tools/ditaot/plugins/com.mot.mdb.deved.xhtml/xsl/deved_xhtml.xsl b/src/help/studio_help/tools/ditaot/plugins/com.mot.mdb.deved.xhtml/xsl/deved_xhtml.xsl
new file mode 100644
index 0000000..db9530c
--- /dev/null
+++ b/src/help/studio_help/tools/ditaot/plugins/com.mot.mdb.deved.xhtml/xsl/deved_xhtml.xsl
@@ -0,0 +1,273 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
+
+<!--<xsl:import href="../../../xsl/xslhtml/dita2htmlImpl.xsl"/>-->
+
+ <!-- -->
+ <!-- BEGIN OVERRIDE: DON'T REQUIRE @TMCLASS VALUE TO RENDER TRADEMARK -->
+ <!-- Change trademark logic to remove test for specific values of @tmclass, so symbol appears regardless of @tmclass. -->
+ <!-- -->
+ <xsl:template match="*[contains(@class,' topic/tm ')]" name="topic.tm">
+
+ <xsl:apply-templates/>
+ <!-- output the TM content -->
+
+ <xsl:variable name="Ltmclass">
+ <xsl:call-template name="convert-to-lower">
+ <!-- ensure lowercase for comparisons -->
+ <xsl:with-param name="inputval" select="@tmclass"/>
+ </xsl:call-template>
+ </xsl:variable>
+ <!-- If this is a good class, continue... -->
+ <!-- Commented out <xsl:if> test for @tmclass value, so processing continues even if no value is specified. BG 2008.11.25. -->
+ <!-- <xsl:if test="$Ltmclass='ibm' or $Ltmclass='ibmsub' or $Ltmclass='special'">-->
+ <!-- Test for TM area's language -->
+ <xsl:variable name="tmtest">
+ <xsl:call-template name="tm-area"/>
+ </xsl:variable>
+
+ <!-- If this language should get trademark markers, continue... -->
+ <xsl:if test="$tmtest='tm'">
+ <xsl:variable name="tmvalue">
+ <xsl:value-of select="@trademark"/>
+ </xsl:variable>
+
+ <!-- Determine if this is in a title, and should be marked -->
+ <xsl:variable name="usetitle">
+ <xsl:if
+ test="ancestor::*[contains(@class,' topic/title ')]/parent::*[contains(@class,' topic/topic ')]">
+ <xsl:choose>
+ <!-- Not the first one in a title -->
+ <xsl:when test="generate-id(.)!=generate-id(key('tm',.)[1])">skip</xsl:when>
+ <!-- First one in the topic, BUT it appears in a shortdesc or body -->
+ <xsl:when
+ test="//*[contains(@class,' topic/shortdesc ') or contains(@class,' topic/body ')]//*[contains(@class,' topic/tm ')][@trademark=$tmvalue]"
+ >skip</xsl:when>
+ <xsl:otherwise>use</xsl:otherwise>
+ </xsl:choose>
+ </xsl:if>
+ </xsl:variable>
+
+ <!-- Determine if this is in a body, and should be marked -->
+ <xsl:variable name="usebody">
+ <xsl:choose>
+ <!-- If in a title or prolog, skip -->
+ <xsl:when
+ test="ancestor::*[contains(@class,' topic/title ') or contains(@class,' topic/prolog ')]/parent::*[contains(@class,' topic/topic ')]"
+ >skip</xsl:when>
+ <!-- If first in the document, use it -->
+ <xsl:when test="generate-id(.)=generate-id(key('tm',.)[1])">use</xsl:when>
+ <!-- If there is another before this that is in the body or shortdesc, skip -->
+ <xsl:when
+ test="preceding::*[contains(@class,' topic/tm ')][@trademark=$tmvalue][ancestor::*[contains(@class,' topic/body ') or contains(@class,' topic/shortdesc ')]]"
+ >skip</xsl:when>
+ <!-- Otherwise, any before this must be in a title or ignored section -->
+ <xsl:otherwise>use</xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+
+ <!-- If it should be used in a title or used in the body, output your favorite TM marker based on the attributes -->
+ <xsl:if test="$usetitle='use' or $usebody='use'">
+ <xsl:choose>
+ <!-- ignore @tmtype=service or anything else -->
+ <xsl:when test="@tmtype='tm'">&#x2122;</xsl:when>
+ <!-- Removed superscript from TM symbol. 2008.11.25 BG. -->
+ <xsl:when test="@tmtype='reg'">&#xAE;</xsl:when>
+ <xsl:otherwise/>
+ </xsl:choose>
+ </xsl:if>
+ </xsl:if>
+ <!-- </xsl:if>-->
+ </xsl:template>
+ <!-- END OVERRIDE: DON'T REQUIRE @TMCLASS VALUE TO RENDER TRADEMARK -->
+
+
+
+<!-- -->
+<!-- BEGIN OVERRIDE: REMOVE BR TAG BEFORE/AFTER IMAGES -->
+<!--2009.03.17 bg: Removed br tags before and after images when placement="break".
+ Instead, added a div with class="imageleft" around such images.-->
+<!-- -->
+<!-- =========== IMAGE/OBJECT =========== -->
+<xsl:template match="*[contains(@class,' topic/image ')]" name="topic.image">
+ <xsl:variable name="flagrules">
+ <xsl:call-template name="getrules"/>
+ </xsl:variable>
+ <!-- build any pre break indicated by style -->
+ <xsl:choose>
+ <xsl:when test="parent::fig[contains(@frame,'top ')]">
+ <!-- NOP if there is already a break implied by a parent property -->
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:choose>
+ <!-- 2009.03.17 bg: Removed br tag in next line. -->
+ <xsl:when test="(@placement='break')">
+ <xsl:call-template name="start-flagit">
+ <xsl:with-param name="flagrules" select="$flagrules"></xsl:with-param>
+ </xsl:call-template>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:call-template name="flagcheck"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:otherwise>
+ </xsl:choose>
+ <xsl:call-template name="start-revflag">
+ <xsl:with-param name="flagrules" select="$flagrules"/>
+ </xsl:call-template>
+ <xsl:call-template name="setaname"/>
+ <xsl:choose>
+ <xsl:when test="@placement='break'"><!--Align only works for break-->
+ <xsl:choose>
+ <xsl:when test="@align='left'">
+ <div class="imageleft">
+ <xsl:call-template name="topic-image"/>
+ </div>
+ </xsl:when>
+ <xsl:when test="@align='right'">
+ <div class="imageright">
+ <xsl:call-template name="topic-image"/>
+ </div>
+ </xsl:when>
+ <xsl:when test="@align='center'">
+ <div class="imagecenter">
+ <xsl:call-template name="topic-image"/>
+ </div>
+ </xsl:when>
+ <xsl:otherwise>
+ <!--2009.03.17 bg: If @placement=break and @align isn't set, then style the same as when align is set to left.-->
+ <div class="imageleft">
+ <xsl:call-template name="topic-image"/>
+ </div>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:call-template name="topic-image"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ <xsl:call-template name="end-revflag">
+ <xsl:with-param name="flagrules" select="$flagrules"/>
+ </xsl:call-template>
+ <xsl:call-template name="end-flagit">
+ <xsl:with-param name="flagrules" select="$flagrules"></xsl:with-param>
+ </xsl:call-template>
+ <!-- build any post break indicated by style -->
+ <!-- 2009.03.17 bg: Removed br tag in next line. -->
+ <xsl:if test="not(@placement='inline')"></xsl:if>
+ <!-- image name for review -->
+ <xsl:if test="$ARTLBL='yes'">
+ [<xsl:value-of select="@href"/>]
+ </xsl:if>
+</xsl:template>
+<!-- END OVERRIDE: REMOVE BR TAG BEFORE/AFTER IMAGES -->
+
+
+
+<!-- -->
+<!-- BEGIN OVERRIDE: GLOSSENTRY TOPIC CSS STYLE -->
+<!-- 2009.03.17 bg: Added class="glossentry" to div enclosing glossentry-->
+<!-- -->
+<!-- child topics get a div wrapper and fall through -->
+<xsl:template match="*[contains(@class,' glossentry/glossentry ')]" name="child.topic">
+ <xsl:param name="nestlevel">
+ <xsl:choose>
+ <!-- Limit depth for historical reasons, could allow any depth. Previously limit was 5. -->
+ <xsl:when test="count(ancestor::*[contains(@class,' topic/topic ')]) > 9">9</xsl:when>
+ <xsl:otherwise><xsl:value-of select="count(ancestor::*[contains(@class,' topic/topic ')])"/></xsl:otherwise>
+ </xsl:choose>
+ </xsl:param>
+<div class="glossentry nested{$nestlevel}">
+ <xsl:call-template name="gen-topic"/>
+</div><xsl:value-of select="$newline"/>
+</xsl:template>
+<!-- END OVERRIDE: GLOSSENTRY TOPIC CSS STYLE -->
+
+
+<!-- -->
+<!-- BEGIN OVERRIDE: REMOVED BR BEFORE UNORDERED LISTS -->
+<!-- 2009.03.17 bg: Removed br element before ul-->
+<!-- -->
+<xsl:template match="*[contains(@class,' topic/ul ')]" mode="ul-fmt">
+ <xsl:variable name="flagrules">
+ <xsl:call-template name="getrules"/>
+ </xsl:variable>
+ <xsl:variable name="conflictexist">
+ <xsl:call-template name="conflict-check">
+ <xsl:with-param name="flagrules" select="$flagrules"/>
+ </xsl:call-template>
+ </xsl:variable>
+ <xsl:call-template name="start-flagit">
+ <xsl:with-param name="flagrules" select="$flagrules"></xsl:with-param>
+ </xsl:call-template>
+ <xsl:call-template name="start-revflag">
+ <xsl:with-param name="flagrules" select="$flagrules"/>
+ </xsl:call-template>
+ <xsl:call-template name="setaname"/>
+ <ul>
+ <xsl:call-template name="commonattributes"/>
+ <xsl:call-template name="gen-style">
+ <xsl:with-param name="conflictexist" select="$conflictexist"></xsl:with-param>
+ <xsl:with-param name="flagrules" select="$flagrules"></xsl:with-param>
+ </xsl:call-template>
+ <xsl:apply-templates select="@compact"/>
+ <xsl:call-template name="setid"/>
+ <xsl:apply-templates/>
+ </ul>
+ <xsl:call-template name="end-revflag">
+ <xsl:with-param name="flagrules" select="$flagrules"/>
+ </xsl:call-template>
+ <xsl:call-template name="end-flagit">
+ <xsl:with-param name="flagrules" select="$flagrules"></xsl:with-param>
+ </xsl:call-template>
+ <xsl:value-of select="$newline"/>
+</xsl:template>
+ <!-- END OVERRIDE: REMOVED BR BEFORE UNORDERED LISTS -->
+
+<!-- -->
+<!-- BEGIN OVERRIDE: REMOVED BR BEFORE ORDERED LISTS -->
+<!-- 2009.03.17 bg: Removed br element before ol-->
+<!-- -->
+<xsl:template match="*[contains(@class,' topic/ol ')]" name="topic.ol">
+ <xsl:variable name="flagrules">
+ <xsl:call-template name="getrules"/>
+ </xsl:variable>
+ <xsl:variable name="conflictexist">
+ <xsl:call-template name="conflict-check">
+ <xsl:with-param name="flagrules" select="$flagrules"/>
+ </xsl:call-template>
+ </xsl:variable>
+<xsl:variable name="olcount" select="count(ancestor-or-self::*[contains(@class,' topic/ol ')])"/>
+ <xsl:call-template name="start-flagit">
+ <xsl:with-param name="flagrules" select="$flagrules"></xsl:with-param>
+ </xsl:call-template>
+ <xsl:call-template name="start-revflag">
+ <xsl:with-param name="flagrules" select="$flagrules"/>
+ </xsl:call-template>
+<xsl:call-template name="setaname"/>
+<ol>
+ <xsl:call-template name="commonattributes"/>
+ <xsl:call-template name="gen-style">
+ <xsl:with-param name="conflictexist" select="$conflictexist"></xsl:with-param>
+ <xsl:with-param name="flagrules" select="$flagrules"></xsl:with-param>
+ </xsl:call-template>
+ <xsl:apply-templates select="@compact"/>
+ <xsl:choose>
+ <xsl:when test="$olcount mod 3 = 1"/>
+ <xsl:when test="$olcount mod 3 = 2"><xsl:attribute name="type">a</xsl:attribute></xsl:when>
+ <xsl:otherwise><xsl:attribute name="type">i</xsl:attribute></xsl:otherwise>
+ </xsl:choose>
+ <xsl:call-template name="setid"/>
+ <xsl:apply-templates/>
+</ol>
+ <xsl:call-template name="end-revflag">
+ <xsl:with-param name="flagrules" select="$flagrules"/>
+ </xsl:call-template>
+ <xsl:call-template name="end-flagit">
+ <xsl:with-param name="flagrules" select="$flagrules"></xsl:with-param>
+ </xsl:call-template>
+<xsl:value-of select="$newline"/>
+</xsl:template>
+<!-- END OVERRIDE: REMOVED BR BEFORE ORDERED LISTS -->
+
+</xsl:stylesheet>
diff --git a/src/help/studio_help/tools/ditaot/plugins/cshelp/catalog-dita.xml b/src/help/studio_help/tools/ditaot/plugins/cshelp/catalog-dita.xml
new file mode 100644
index 0000000..e12052c
--- /dev/null
+++ b/src/help/studio_help/tools/ditaot/plugins/cshelp/catalog-dita.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" ?>
+<!-- This file is part of the DITA Open Toolkit project hosted on
+ Sourceforge.net. See the accompanying license.txt file in the
+ main toolkit package for applicable licenses.-->
+<!-- (C) Copyright IBM Corporation 2006 All Rights Reserved. -->
+
+<!--
+ This file declares each DTD module that is part of the specialization.
+ The path is relative to the base toolkit directory.
+-->
+<catalog xmlns="urn:oasis:names:tc:entity:xmlns:xml:catalog"
+ prefer="public">
+
+ <public publicId="-//IBM//DTD DITA CSHelp//EN"
+ uri="plugins/cshelp/dtd/cshelp.dtd"/>
+ <public publicId="-//IBM//ELEMENTS DITA CSHelp//EN"
+ uri="plugins/cshelp/dtd/cshelp.mod"/>
+
+</catalog>
diff --git a/src/help/studio_help/tools/ditaot/plugins/cshelp/dtd/cshelp.dtd b/src/help/studio_help/tools/ditaot/plugins/cshelp/dtd/cshelp.dtd
new file mode 100644
index 0000000..331576f
--- /dev/null
+++ b/src/help/studio_help/tools/ditaot/plugins/cshelp/dtd/cshelp.dtd
@@ -0,0 +1,101 @@
+<?xml encoding="utf-8"?>
+<!--
+ | (C) Copyright IBM Corporation 2005, 2006 All Rights Reserved.
+ | This file is a specialization of DITA 3.0. See license.txt
+ | for disclaimers and permissions.
+ |
+ | This file is part of the DITA Open Toolkit project hosted on
+ | Sourceforge.net. See the accompanying license.txt file for
+ | applicable licenses.
+ |
+ | The Darwin Information Typing Architecture (DITA) was orginated by
+ | IBM's XML Workgroup and ID Workbench tools team.
+ |
+ | Refer to this file by the following public identfier or an appropriate
+ | system identifier:
+ |
+ | PUBLIC "-//IBM//DTD DITA CSHelp//EN"
+ |
+ | Release history (vrm):
+ | 1.0.0 Initial release, December 2005
+ *-->
+
+<!--vocabulary declarations-->
+<!ENTITY % ui-d-dec PUBLIC
+"-//OASIS//ENTITIES DITA User Interface Domain//EN"
+"../../../dtd/uiDomain.ent" >
+%ui-d-dec;
+
+<!ENTITY % hi-d-dec PUBLIC
+"-//OASIS//ENTITIES DITA Highlight Domain//EN"
+"../../../dtd/highlightDomain.ent" >
+%hi-d-dec;
+
+<!ENTITY % pr-d-dec PUBLIC
+"-//OASIS//ENTITIES DITA Programming Domain//EN"
+"../../../dtd/programmingDomain.ent" >
+%pr-d-dec;
+
+<!ENTITY % sw-d-dec PUBLIC
+"-//OASIS//ENTITIES DITA Software Domain//EN"
+"../../../dtd/softwareDomain.ent" >
+%sw-d-dec;
+
+<!ENTITY % ut-d-dec PUBLIC
+"-//OASIS//ENTITIES DITA Utilities Domain//EN"
+"../../../dtd/utilitiesDomain.ent" >
+%ut-d-dec;
+
+
+<!--vocabulary substitution (one for each extended base element,
+ with the name of the domain(s) in which the extension was declared)-->
+<!ENTITY % pre "pre | %pr-d-pre; | %sw-d-pre; | %ui-d-pre;">
+<!ENTITY % keyword "keyword | %pr-d-keyword; | %sw-d-keyword; | %ui-d-keyword;">
+<!ENTITY % ph "ph | %pr-d-ph; | %sw-d-ph; | %hi-d-ph; | %ui-d-ph;">
+<!ENTITY % fig "fig | %pr-d-fig; | %ut-d-fig;">
+<!ENTITY % dl "dl | %pr-d-dl;">
+
+<!--Allow nesting of only cshelp infotype-->
+<!ENTITY % cshelp-info-types "cshelp">
+
+<!--vocabulary attributes (must be declared ahead of the dtds, which puts @domains first in order) -->
+<!ENTITY included-domains "&ui-d-att; &hi-d-att; &pr-d-att; &sw-d-att; &ut-d-att;">
+
+<!--Embed topic to get generic elements -->
+<!ENTITY % topic-type PUBLIC
+"-//OASIS//ELEMENTS DITA Topic//EN"
+"../../../dtd/topic.mod">
+ %topic-type;
+
+<!--Embed reference to get specific elements -->
+<!ENTITY % cshelp-typemod PUBLIC
+"-//IBM//ELEMENTS DITA CSHelp//EN"
+"cshelp.mod">
+ %cshelp-typemod;
+
+
+<!--vocabulary definitions-->
+<!ENTITY % ui-d-def PUBLIC
+"-//OASIS//ELEMENTS DITA User Interface Domain//EN"
+"../../../dtd/uiDomain.mod">
+%ui-d-def;
+
+<!ENTITY % hi-d-def PUBLIC
+"-//OASIS//ELEMENTS DITA Highlight Domain//EN"
+"../../../dtd/highlightDomain.mod">
+%hi-d-def;
+
+<!ENTITY % pr-d-def PUBLIC
+"-//OASIS//ELEMENTS DITA Programming Domain//EN"
+"../../../dtd/programmingDomain.mod">
+%pr-d-def;
+
+<!ENTITY % sw-d-def PUBLIC
+"-//OASIS//ELEMENTS DITA Software Domain//EN"
+"../../../dtd/softwareDomain.mod">
+%sw-d-def;
+
+<!ENTITY % ut-d-def PUBLIC
+"-//OASIS//ELEMENTS DITA Utilities Domain//EN"
+"../../../dtd/utilitiesDomain.mod">
+%ut-d-def;
diff --git a/src/help/studio_help/tools/ditaot/plugins/cshelp/dtd/cshelp.mod b/src/help/studio_help/tools/ditaot/plugins/cshelp/dtd/cshelp.mod
new file mode 100644
index 0000000..66942e3
--- /dev/null
+++ b/src/help/studio_help/tools/ditaot/plugins/cshelp/dtd/cshelp.mod
@@ -0,0 +1,85 @@
+<!--
+ | (C) Copyright IBM Corporation 2005, 2006 All Rights Reserved.
+ | This file is a specialization of DITA 3.0. See license.txt
+ | for disclaimers and permissions.
+ |
+ | This file is part of the DITA Open Toolkit project hosted on
+ | Sourceforge.net. See the accompanying license.txt file for
+ | applicable licenses.
+ |
+ | The Darwin Information Typing Architecture (DITA) was orginated by
+ | IBM's XML Workgroup and ID Workbench tools team.
+ |
+ | Refer to this file by the following public identfier or an appropriate
+ | system identifier:
+ |
+ | PUBLIC "-//IBM//ELEMENTS DITA CSHelp//EN"
+ |
+ | Release history (vrm):
+ | 1.0.0 Initial release, December 2005
+ *-->
+
+<!ENTITY DTDVersion 'V1.1.1' >
+
+
+<!-- Specialization of declared elements -->
+
+<!ENTITY % csprolog "csprolog">
+<!ENTITY % csmetadata "csmetadata">
+<!ENTITY % cswindowtitle "cswindowtitle">
+<!ENTITY % cswidgetlabel "cswidgetlabel">
+<!ENTITY % csbody "csbody">
+<!ENTITY % cshelp-info-types "%info-types;">
+
+<!-- declared here, defined later -->
+<!ENTITY included-domains "">
+
+<!--doc:The <cshelp> element is the top-level element for a topic that corresponds to an Eclipse Help context (a brief description and link that appear after the user presses a Help button). To create one Eclipse context.xml file, create one DITA file with a root element of <cshelp> in which you next further <cshelp> topics, one for each context-sensitive help item. Only the content of the nested elements is output; the root element is a required, but empty container.
+Category: CS Help plug-in elements-->
+<!ELEMENT cshelp (%title;, (%titlealts;)?, (%shortdesc;), (%csprolog;)?, %csbody;, (%related-links;)?, (%cshelp-info-types;)* )>
+<!ATTLIST cshelp id ID #REQUIRED
+ conref CDATA #IMPLIED
+ %select-atts;
+ outputclass CDATA #IMPLIED
+ xml:lang NMTOKEN #IMPLIED
+ DTDVersion CDATA #FIXED "&DTDVersion;"
+ domains CDATA "&included-domains;"
+>
+
+<!--doc:The optional <csprolog> element contains <csmetadata> elements to describe the UI related to this context.
+Category: CS Help plug-in elements-->
+<!ELEMENT csprolog ((%author;)*,(%source;)?,(%publisher;)?,(%copyright;)*,(%critdates;)?,(%permissions;)?,(%csmetadata;)*, (%resourceid;)*)>
+
+<!--doc:The optional <csmetadata> element contains <cswindowtitle> and <cswidgetlabel> elements.
+Category: CS Help plug-in elements-->
+<!ELEMENT csmetadata ((%audience;)*,(%cswindowtitle;)?,(%cswidgetlabel;)?,(%category;)*,(%keywords;)*,(%prodinfo;)*,(%othermeta;)*)>
+
+<!-- define a custom block type with predefined topic content types -->
+
+<!-- txt.incl minus footnotes and index entries -->
+
+<!--doc:The <csbody> element contains the body of the CS Help topic. All the elements allowed in <body> are allowed in <csbody>; however, some result in no output because of Eclipse Help limitations. For example, table, simpletable, image, figure, xref, indexterm, indextermref, footnote, object are not output. Many in-line elements display using the <b> tag. Lists are supported, but nested lists are not recommended.
+Category: CS Help plug-in elements-->
+<!ELEMENT csbody (%body.cnt;)*>
+<!ATTLIST csbody %id-atts;
+ translate (yes|no) #IMPLIED
+ xml:lang NMTOKEN #IMPLIED
+ outputclass CDATA #IMPLIED
+>
+
+<!--doc:The optional <cswindowtitle> element identifies the window associated with this context-sensitive help item. This content does not appear in the output context file.
+Category: CS Help plug-in elements-->
+<!ELEMENT cswindowtitle (#PCDATA)>
+
+<!--doc:The optional <cswidgetlabel> element identifies the UI widget within the window associated with this context-sensitive help item. This content does not appear in the output context file.
+Category: CS Help plug-in elements-->
+<!ELEMENT cswidgetlabel (#PCDATA)>
+
+<!--specialization attributes-->
+
+<!ATTLIST cshelp %global-atts; class CDATA "- topic/topic cshelp/cshelp ">
+<!ATTLIST csbody %global-atts; class CDATA "- topic/body cshelp/csbody ">
+<!ATTLIST csprolog %global-atts; class CDATA "- topic/prolog cshelp/csprolog ">
+<!ATTLIST csmetadata %global-atts; class CDATA "- topic/metadata cshelp/csmetadata ">
+<!ATTLIST cswindowtitle %global-atts; class CDATA "- topic/category cshelp/cswindowtitle ">
+<!ATTLIST cswidgetlabel %global-atts; class CDATA "- topic/category cshelp/cswidgetlabel ">
diff --git a/src/help/studio_help/tools/ditaot/plugins/cshelp/install-plugin.xml b/src/help/studio_help/tools/ditaot/plugins/cshelp/install-plugin.xml
new file mode 100644
index 0000000..d3ac98b
--- /dev/null
+++ b/src/help/studio_help/tools/ditaot/plugins/cshelp/install-plugin.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!-- This file is part of the DITA Open Toolkit project hosted on
+ Sourceforge.net. See the accompanying license.txt file in the
+ main toolkit package for applicable licenses.-->
+<!-- (C) Copyright IBM Corporation 2006 All Rights Reserved. -->
+
+<!--
+ This is a shortcut file that will integrate the plugin, if you do not regularly
+ run the integrator as part of a build.
+
+ Alternatively, from the main toolkit directory, you could integrate with:
+ ant -f integrator.xml
+-->
+
+<project name="cshelp" default="all" basedir="../..">
+ <import file="${basedir}${file.separator}integrator.xml"/>
+
+ <target name="all" depends="integrate"/>
+
+</project>
diff --git a/src/help/studio_help/tools/ditaot/plugins/cshelp/plugin.xml b/src/help/studio_help/tools/ditaot/plugins/cshelp/plugin.xml
new file mode 100644
index 0000000..a4a5d07
--- /dev/null
+++ b/src/help/studio_help/tools/ditaot/plugins/cshelp/plugin.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- This file is part of the DITA Open Toolkit project hosted on
+ Sourceforge.net. See the accompanying license.txt file in the
+ main toolkit package for applicable licenses.-->
+<!-- (C) Copyright IBM Corporation 2006 All Rights Reserved. -->
+
+<plugin id="org.dita.specialization.cshelp">
+ <!-- Extend the toolkit catalog to include DTDs. -->
+ <feature extension="dita.specialization.catalog"
+ value="catalog-dita.xml" type="file"/>
+
+ <!-- Extend the toolkit's XHTML processing to override XHTML output. -->
+ <feature extension="dita.xsl.xhtml"
+ value="xsl/cshdisplay.xsl" type="file"/>
+
+</plugin>
diff --git a/src/help/studio_help/tools/ditaot/plugins/cshelp/samples/test.ditamap b/src/help/studio_help/tools/ditaot/plugins/cshelp/samples/test.ditamap
new file mode 100644
index 0000000..55549f5
--- /dev/null
+++ b/src/help/studio_help/tools/ditaot/plugins/cshelp/samples/test.ditamap
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE map PUBLIC "-//OASIS//DTD DITA Map//EN"
+ "ibm-map.dtd">
+<!-- This file is part of the DITA Open Toolkit project hosted on -->
+<!-- Sourceforge.net. See the accompanying license.txt file for -->
+<!-- applicable licenses. -->
+<map title="CS Help test">
+<topicmeta>
+<copyright>
+<copyryear year="2000"/>
+<copyryear year="2006"/>
+<copyrholder></copyrholder>
+</copyright>
+</topicmeta>
+<topicref href="test_csh_1.dita" navtitle=""></topicref>
+<topicref href="test_csh_2.dita" navtitle=""></topicref>
+</map>
diff --git a/src/help/studio_help/tools/ditaot/plugins/cshelp/samples/test_csh_1.dita b/src/help/studio_help/tools/ditaot/plugins/cshelp/samples/test_csh_1.dita
new file mode 100644
index 0000000..f1b729b
--- /dev/null
+++ b/src/help/studio_help/tools/ditaot/plugins/cshelp/samples/test_csh_1.dita
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE cshelp PUBLIC "-//IBM//DTD DITA CSHelp//EN"
+ "..\dtd\cshelp.dtd">
+<!-- This file is part of the DITA Open Toolkit project hosted on -->
+<!-- Sourceforge.net. See the accompanying license.txt file for -->
+<!-- applicable licenses. -->
+<cshelp id="test_csh_1" xml:lang="en-us">
+<title>Test CSH file</title>
+<shortdesc></shortdesc>
+<csbody></csbody>
+<cshelp id="contextId1" xml:lang="en-us">
+<title>My First CSH topic</title>
+<shortdesc>This is the first sentence of my context-sensitive help topic.</shortdesc>
+<csprolog><csmetadata>
+<cswindowtitle>(optional) The name of the window or view discussed by this
+CSH topic.</cswindowtitle>
+<cswidgetlabel>(optional) The name of the control discussed by this CSH topic.</cswidgetlabel>
+</csmetadata></csprolog>
+<csbody>
+<p>Here is the second sentence of my cshelp topic.</p>
+</csbody>
+<related-links>
+<link format="dita" href="../com.mycompany.myplugin/a.dita" scope="peer"><linktext>Link
+text</linktext></link>
+</related-links>
+</cshelp>
+<cshelp id="contextId2" xml:lang="en-us">
+<title>Another CSH topic</title>
+<shortdesc>This is the first sentence of another context-sensitive help topic.</shortdesc>
+<csbody>
+<p>This is the second sentence of my cshelp topic.</p>
+</csbody>
+<related-links>
+<link format="dita" href="../com.mycompany.myplugin/a.dita" scope="peer"><linktext>Link
+text</linktext></link>
+</related-links>
+</cshelp>
+<cshelp id="contextId3" xml:lang="en-us">
+<title>Third CSH topic</title>
+<shortdesc>Here is the third CSH topic.</shortdesc>
+<csbody></csbody>
+<related-links>
+<link format="dita" href="../com.mycompany.myplugin/b.dita" scope="peer"><linktext>Link
+text</linktext></link>
+</related-links>
+</cshelp>
+</cshelp>
diff --git a/src/help/studio_help/tools/ditaot/plugins/cshelp/samples/test_csh_2.dita b/src/help/studio_help/tools/ditaot/plugins/cshelp/samples/test_csh_2.dita
new file mode 100644
index 0000000..e797c89
--- /dev/null
+++ b/src/help/studio_help/tools/ditaot/plugins/cshelp/samples/test_csh_2.dita
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE cshelp PUBLIC "-//IBM//DTD DITA CSHelp//EN"
+ "..\dtd\cshelp.dtd">
+<!-- This file is part of the DITA Open Toolkit project hosted on -->
+<!-- Sourceforge.net. See the accompanying license.txt file for -->
+<!-- applicable licenses. -->
+<cshelp id="test_csh_1" xml:lang="en-us">
+<title>Second CSH file</title>
+<shortdesc></shortdesc>
+<csbody></csbody>
+<cshelp id="contextId4" xml:lang="en-us">
+<title>CSH topic four</title>
+<shortdesc>This is the first sentence of context-sensitive help topic four.</shortdesc>
+<csbody>
+<p>Here is the second sentence.</p>
+</csbody>
+<related-links>
+<link format="dita" href="../com.mycompany.myplugin/a.dita" scope="peer"><linktext>Link
+text</linktext></link>
+</related-links>
+</cshelp>
+<cshelp id="contextId5" xml:lang="en-us">
+<title>CSH topic five</title>
+<shortdesc>This is the first sentence of context-sensitive help topic five.</shortdesc>
+<csbody>
+<p>This is the second sentence.</p>
+</csbody>
+<related-links>
+<link format="dita" href="../com.mycompany.myplugin/a.dita" scope="peer"><linktext>Link
+text</linktext></link>
+</related-links>
+</cshelp>
+</cshelp>
diff --git a/src/help/studio_help/tools/ditaot/plugins/cshelp/xsl/GetCSHMeta.xsl b/src/help/studio_help/tools/ditaot/plugins/cshelp/xsl/GetCSHMeta.xsl
new file mode 100644
index 0000000..d69cc01
--- /dev/null
+++ b/src/help/studio_help/tools/ditaot/plugins/cshelp/xsl/GetCSHMeta.xsl
@@ -0,0 +1,591 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!-- (c) Copyright IBM Corp. 2005, 2006 All Rights Reserved. -->
+<!-- This file is part of the DITA Open Toolkit project hosted on -->
+<!-- Sourceforge.net. See the accompanying license.txt file for -->
+<!-- applicable licenses. -->
+
+<xsl:stylesheet version="1.0"
+ xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+
+<!-- Metadata conversions from DITA to Eclipse context file XML -->
+
+<xsl:key name="meta-keywords" match="*[ancestor::*[contains(@class,' topic/keywords ')]]" use="text()"/>
+
+<xsl:template name="getCSHMeta">
+
+ <!-- = = = = = = = = = = = CONTENT = = = = = = = = = = = -->
+
+ <!-- CONTENT: Type -->
+ <xsl:apply-templates select="." mode="gen-type-metadata"/>
+
+ <!-- CONTENT: Title - title -->
+ <xsl:apply-templates select="*[contains(@class,' topic/title ')] |
+ self::dita/*[1]/*[contains(@class,' topic/title ')]" mode="gen-metadata"/>
+
+ <!-- CONTENT: Description - shortdesc -->
+ <xsl:apply-templates select="*[contains(@class,' topic/shortdesc ')] |
+ self::dita/*[1]/*[contains(@class,' topic/shortdesc ')]" mode="gen-metadata"/>
+
+ <!-- CONTENT: Source - prolog/source/@href -->
+ <xsl:apply-templates select="*[contains(@class,' topic/prolog ')]/*[contains(@class,' topic/source ')]/@href |
+ self::dita/*[1]/*[contains(@class,' topic/prolog ')]/*[contains(@class,' topic/source ')]/@href" mode="gen-metadata"/>
+
+ <!-- CONTENT: Coverage prolog/metadata/category -->
+ <xsl:apply-templates select="*[contains(@class,' topic/prolog ')]/*[contains(@class,' topic/metadata ')]/*[contains(@class,' topic/category ')] |
+ self::dita/*[1]/*[contains(@class,' topic/prolog ')]/*[contains(@class,' topic/metadata ')]/*[contains(@class,' topic/category ')]" mode="gen-metadata"/>
+
+ <!-- CONTENT: Subject - prolog/metadata/keywords -->
+ <xsl:apply-templates select="." mode="gen-keywords-metadata"/>
+
+ <!-- CONTENT: Relation - related-links -->
+ <xsl:apply-templates select="*[contains(@class,' topic/related-links ')]/descendant::*/@href |
+ self::dita/*/*[contains(@class,' topic/related-links ')]/descendant::*/@href" mode="gen-metadata"/>
+
+ <!-- = = = = = = = = = = = Product - Audience = = = = = = = = = = = -->
+ <!-- Audience -->
+ <!-- prolog/metadata/audience/@experiencelevel and other attributes -->
+ <xsl:apply-templates select="*[contains(@class,' topic/prolog ')]/*[contains(@class,' topic/metadata ')]/*[contains(@class,' topic/audience ')]/@experiencelevel |
+ self::dita/*[1]/*[contains(@class,' topic/prolog ')]/*[contains(@class,' topic/metadata ')]/*[contains(@class,' topic/audience ')]/@experiencelevel" mode="gen-metadata"/>
+ <xsl:apply-templates select="*[contains(@class,' topic/prolog ')]/*[contains(@class,' topic/metadata ')]/*[contains(@class,' topic/audience ')]/@importance |
+ self::dita/*[1]/*[contains(@class,' topic/prolog ')]/*[contains(@class,' topic/metadata ')]/*[contains(@class,' topic/audience ')]/@importance" mode="gen-metadata"/>
+ <xsl:apply-templates select="*[contains(@class,' topic/prolog ')]/*[contains(@class,' topic/metadata ')]/*[contains(@class,' topic/audience ')]/@job |
+ self::dita/*[1]/*[contains(@class,' topic/prolog ')]/*[contains(@class,' topic/metadata ')]/*[contains(@class,' topic/audience ')]/@job" mode="gen-metadata"/>
+ <xsl:apply-templates select="*[contains(@class,' topic/prolog ')]/*[contains(@class,' topic/metadata ')]/*[contains(@class,' topic/audience ')]/@name |
+ self::dita/*[1]/*[contains(@class,' topic/prolog ')]/*[contains(@class,' topic/metadata ')]/*[contains(@class,' topic/audience ')]/@name" mode="gen-metadata"/>
+ <xsl:apply-templates select="*[contains(@class,' topic/prolog ')]/*[contains(@class,' topic/metadata ')]/*[contains(@class,' topic/audience ')]/@type |
+ self::dita/*[1]/*[contains(@class,' topic/prolog ')]/*[contains(@class,' topic/metadata ')]/*[contains(@class,' topic/audience ')]/@type" mode="gen-metadata"/>
+
+
+ <!-- <prodname> -->
+ <xsl:apply-templates select="*[contains(@class,' topic/prolog ')]/*[contains(@class,' topic/metadata ')]/*[contains(@class,' topic/prodinfo ')]/*[contains(@class,' topic/prodname ')] |
+ self::dita/*[1]/*[contains(@class,' topic/prolog ')]/*[contains(@class,' topic/metadata ')]/*[contains(@class,' topic/prodinfo ')]/*[contains(@class,' topic/prodname ')]" mode="gen-metadata"/>
+
+ <!-- <vrmlist><vrm modification="3" release="2" version="5"/></vrmlist> -->
+ <xsl:apply-templates select="*[contains(@class,' topic/prolog ')]/*[contains(@class,' topic/metadata ')]/*[contains(@class,' topic/prodinfo ')]/*[contains(@class,' topic/vrmlist ')]/*[contains(@class,' topic/vrm ')]/@version |
+ self::dita/*[1]/*[contains(@class,' topic/prolog ')]/*[contains(@class,' topic/metadata ')]/*[contains(@class,' topic/prodinfo ')]/*[contains(@class,' topic/vrmlist ')]/*[contains(@class,' topic/vrm ')]/@version" mode="gen-metadata"/>
+ <xsl:apply-templates select="*[contains(@class,' topic/prolog ')]/*[contains(@class,' topic/metadata ')]/*[contains(@class,' topic/prodinfo ')]/*[contains(@class,' topic/vrmlist ')]/*[contains(@class,' topic/vrm ')]/@release |
+ self::dita/*[1]/*[contains(@class,' topic/prolog ')]/*[contains(@class,' topic/metadata ')]/*[contains(@class,' topic/prodinfo ')]/*[contains(@class,' topic/vrmlist ')]/*[contains(@class,' topic/vrm ')]/@release" mode="gen-metadata"/>
+ <xsl:apply-templates select="*[contains(@class,' topic/prolog ')]/*[contains(@class,' topic/metadata ')]/*[contains(@class,' topic/prodinfo ')]/*[contains(@class,' topic/vrmlist ')]/*[contains(@class,' topic/vrm ')]/@modification |
+ self::dita/*[1]/*[contains(@class,' topic/prolog ')]/*[contains(@class,' topic/metadata ')]/*[contains(@class,' topic/prodinfo ')]/*[contains(@class,' topic/vrmlist ')]/*[contains(@class,' topic/vrm ')]/@modification" mode="gen-metadata"/>
+
+ <!-- <brand> -->
+ <xsl:apply-templates select="*[contains(@class,' topic/prolog ')]/*[contains(@class,' topic/metadata ')]/*[contains(@class,' topic/prodinfo ')]/*[contains(@class,' topic/brand ')] |
+ self::dita/*[1]/*[contains(@class,' topic/prolog ')]/*[contains(@class,' topic/metadata ')]/*[contains(@class,' topic/prodinfo ')]/*[contains(@class,' topic/brand ')]" mode="gen-metadata"/>
+ <!-- <component> -->
+ <xsl:apply-templates select="*[contains(@class,' topic/prolog ')]/*[contains(@class,' topic/metadata ')]/*[contains(@class,' topic/prodinfo ')]/*[contains(@class,' topic/component ')] |
+ self::dita/*[1]/*[contains(@class,' topic/prolog ')]/*[contains(@class,' topic/metadata ')]/*[contains(@class,' topic/prodinfo ')]/*[contains(@class,' topic/component ')]" mode="gen-metadata"/>
+ <!-- <featnum> -->
+ <xsl:apply-templates select="*[contains(@class,' topic/prolog ')]/*[contains(@class,' topic/metadata ')]/*[contains(@class,' topic/prodinfo ')]/*[contains(@class,' topic/featnum ')] |
+ self::dita/*[1]/*[contains(@class,' topic/prolog ')]/*[contains(@class,' topic/metadata ')]/*[contains(@class,' topic/prodinfo ')]/*[contains(@class,' topic/featnum ')]" mode="gen-metadata"/>
+ <!-- <prognum> -->
+ <xsl:apply-templates select="*[contains(@class,' topic/prolog ')]/*[contains(@class,' topic/metadata ')]/*[contains(@class,' topic/prodinfo ')]/*[contains(@class,' topic/prognum ')] |
+ self::dita/*[1]/*[contains(@class,' topic/prolog ')]/*[contains(@class,' topic/metadata ')]/*[contains(@class,' topic/prodinfo ')]/*[contains(@class,' topic/prognum ')]" mode="gen-metadata"/>
+ <!-- <platform> -->
+ <xsl:apply-templates select="*[contains(@class,' topic/prolog ')]/*[contains(@class,' topic/metadata ')]/*[contains(@class,' topic/prodinfo ')]/*[contains(@class,' topic/platform ')] |
+ self::dita/*[1]/*[contains(@class,' topic/prolog ')]/*[contains(@class,' topic/metadata ')]/*[contains(@class,' topic/prodinfo ')]/*[contains(@class,' topic/platform ')]" mode="gen-metadata"/>
+ <!-- <series> -->
+ <xsl:apply-templates select="*[contains(@class,' topic/prolog ')]/*[contains(@class,' topic/metadata ')]/*[contains(@class,' topic/prodinfo ')]/*[contains(@class,' topic/series ')] |
+ self::dita/*[1]/*[contains(@class,' topic/prolog ')]/*[contains(@class,' topic/metadata ')]/*[contains(@class,' topic/prodinfo ')]/*[contains(@class,' topic/series ')]" mode="gen-metadata"/>
+
+ <!-- = = = = = = = = = = = INTELLECTUAL PROPERTY = = = = = = = = = = = -->
+
+ <!-- INTELLECTUAL PROPERTY: Contributor - prolog/author -->
+ <!-- INTELLECTUAL PROPERTY: Creator -->
+ <xsl:apply-templates select="*[contains(@class,' topic/prolog ')]/*[contains(@class,' topic/author ')] |
+ self::dita/*[1]/*[contains(@class,' topic/prolog ')]/*[contains(@class,' topic/author ')]" mode="gen-metadata"/>
+
+ <!-- INTELLECTUAL PROPERTY: Publisher - prolog/publisher -->
+ <xsl:apply-templates select="*[contains(@class,' topic/prolog ')]/*[contains(@class,' topic/publisher ')] |
+ self::dita/*[1]/*[contains(@class,' topic/prolog ')]/*[contains(@class,' topic/publisher ')]" mode="gen-metadata"/>
+
+ <!-- INTELLECTUAL PROPERTY: Rights - prolog/copyright -->
+ <!-- Put primary first, then secondary, then remainder -->
+ <xsl:apply-templates select="*[contains(@class,' topic/prolog ')]/*[contains(@class,' topic/copyright ')][@type='primary'] |
+ self::dita/*[1]/*[contains(@class,' topic/prolog ')]/*[contains(@class,' topic/copyright ')][@type='primary']" mode="gen-metadata"/>
+ <xsl:apply-templates select="*[contains(@class,' topic/prolog ')]/*[contains(@class,' topic/copyright ')][@type='secondary'] |
+ self::dita/*[1]/*[contains(@class,' topic/prolog ')]/*[contains(@class,' topic/copyright ')][@type='seconday']" mode="gen-metadata"/>
+ <xsl:apply-templates select="*[contains(@class,' topic/prolog ')]/*[contains(@class,' topic/copyright ')][not(@type)] |
+ self::dita/*[1]/*[contains(@class,' topic/prolog ')]/*[contains(@class,' topic/copyright ')][not(@type)]" mode="gen-metadata"/>
+
+ <!-- Usage Rights - prolog/permissions -->
+ <xsl:apply-templates select="*[contains(@class,' topic/prolog ')]/*[contains(@class,' topic/permissions ')] |
+ self::dita/*[1]/*[contains(@class,' topic/prolog ')]/*[contains(@class,' topic/permissions ')]" mode="gen-metadata"/>
+
+ <!-- = = = = = = = = = = = INSTANTIATION = = = = = = = = = = = -->
+
+ <!-- INSTANTIATION: Date - prolog/critdates/created -->
+ <xsl:apply-templates select="*[contains(@class,' topic/prolog ')]/*[contains(@class,' topic/critdates ')]/*[contains(@class,' topic/created ')] |
+ self::dita/*[1]/*[contains(@class,' topic/prolog ')]/*[contains(@class,' topic/critdates ')]/*[contains(@class,' topic/created ')]" mode="gen-metadata"/>
+
+ <!-- prolog/critdates/revised/@modified -->
+ <xsl:apply-templates select="*[contains(@class,' topic/prolog ')]/*[contains(@class,' topic/critdates ')]/*[contains(@class,' topic/revised ')]/@modified |
+ self::dita/*[1]/*[contains(@class,' topic/prolog ')]/*[contains(@class,' topic/critdates ')]/*[contains(@class,' topic/revised ')]/@modified" mode="gen-metadata"/>
+
+ <!-- prolog/critdates/revised/@golive -->
+ <xsl:apply-templates select="*[contains(@class,' topic/prolog ')]/*[contains(@class,' topic/critdates ')]/*[contains(@class,' topic/revised ')]/@golive |
+ self::dita/*[1]/*[contains(@class,' topic/prolog ')]/*[contains(@class,' topic/critdates ')]/*[contains(@class,' topic/revised ')]/@golive" mode="gen-metadata"/>
+
+ <!-- prolog/critdates/revised/@expiry -->
+ <xsl:apply-templates select="*[contains(@class,' topic/prolog ')]/*[contains(@class,' topic/critdates ')]/*[contains(@class,' topic/revised ')]/@expiry |
+ self::dita/*[1]/*[contains(@class,' topic/prolog ')]/*[contains(@class,' topic/critdates ')]/*[contains(@class,' topic/revised ')]/@expiry" mode="gen-metadata"/>
+
+ <!-- prolog/metadata/othermeta -->
+ <xsl:apply-templates select="*[contains(@class,' topic/prolog ')]/*[contains(@class,' topic/metadata ')]/*[contains(@class,' topic/othermeta ')] |
+ self::dita/*[1]/*[contains(@class,' topic/prolog ')]/*[contains(@class,' topic/metadata ')]/*[contains(@class,' topic/othermeta ')]" mode="gen-metadata"/>
+
+ <!-- INSTANTIATION: Format -->
+ <xsl:apply-templates select="." mode="gen-format-metadata"/>
+
+ <!-- INSTANTIATION: Identifier --> <!-- id is an attribute on Topic -->
+ <xsl:apply-templates select="@id | self::dita/*[1]/@id" mode="gen-metadata"/>
+
+ <!-- INSTANTIATION: Language -->
+ <xsl:apply-templates select="@xml:lang | self::dita/*[1]/@xml:lang" mode="gen-metadata"/>
+
+</xsl:template>
+
+
+<!-- CONTENT: Type -->
+<xsl:template match="dita" mode="gen-type-metadata">
+ <xsl:apply-templates select="*[1]" mode="gen-type-metadata"/>
+</xsl:template>
+<xsl:template match="*" mode="gen-type-metadata">
+ <xsl:text disable-output-escaping="yes">&lt;!-- meta name="DC.Type" content="</xsl:text>
+ <xsl:value-of select="name(.)"/>
+ <xsl:text disable-output-escaping="yes"> --&gt;</xsl:text>
+ <xsl:value-of select="$newline"/>
+</xsl:template>
+
+<!-- CONTENT: Title - title -->
+<xsl:template match="*[contains(@class,' topic/title ')]" mode="gen-metadata">
+ <xsl:variable name="titlemeta">
+ <xsl:apply-templates select="*|text()" mode="text-only"/>
+ </xsl:variable>
+ <xsl:text disable-output-escaping="yes">&lt;!-- meta name="DC.Title" content="</xsl:text>
+ <xsl:value-of select="normalize-space($titlemeta)" />
+ <xsl:text disable-output-escaping="yes">" --&gt;</xsl:text>
+ <xsl:value-of select="$newline"/>
+</xsl:template>
+
+<!-- CONTENT: Description - shortdesc -->
+<xsl:template match="*[contains(@class,' topic/shortdesc ')]" mode="gen-metadata">
+ <xsl:variable name="shortmeta">
+ <xsl:apply-templates select="*|text()" mode="text-only"/>
+ </xsl:variable>
+ <xsl:text disable-output-escaping="yes">&lt;!-- meta name="abstract" content="</xsl:text>
+ <xsl:value-of select="normalize-space($shortmeta)"/>
+ <xsl:text disable-output-escaping="yes">" --&gt;</xsl:text>
+ <xsl:value-of select="$newline"/>
+ <xsl:text disable-output-escaping="yes">&lt;!-- meta name="description" content="</xsl:text>
+ <xsl:value-of select="normalize-space($shortmeta)"/>
+ <xsl:text disable-output-escaping="yes">" --&gt;</xsl:text>
+ <xsl:value-of select="$newline"/>
+</xsl:template>
+
+<!-- CONTENT: Source - prolog/source/@href -->
+<xsl:template match="*[contains(@class,' topic/source ')]/@href" mode="gen-metadata">
+ <xsl:text disable-output-escaping="yes">&lt;!-- meta name="DC.Source" content="</xsl:text>
+ <xsl:value-of select="normalize-space(.)"/>
+ <xsl:text disable-output-escaping="yes">" --&gt;</xsl:text>
+ <xsl:value-of select="$newline"/>
+</xsl:template>
+
+<!-- CONTENT: Coverage prolog/metadata/category -->
+<xsl:template match="*[contains(@class,' topic/metadata ')]/*[contains(@class,' topic/category ')]" mode="gen-metadata">
+ <xsl:text disable-output-escaping="yes">&lt;!-- meta name="DC.Coverage" content="</xsl:text>
+ <xsl:value-of select="normalize-space(.)"/>
+ <xsl:text disable-output-escaping="yes">" --&gt;</xsl:text>
+ <xsl:value-of select="$newline"/>
+</xsl:template>
+
+<!-- CONTENT: Subject - prolog/metadata/keywords -->
+<xsl:template match="*" mode="gen-keywords-metadata">
+ <xsl:variable name="keywords-content">
+ <!-- for each item inside keywords (including nested index terms) -->
+ <xsl:for-each select="descendant::*[contains(@class,' topic/prolog ')]/*[contains(@class,' topic/metadata ')]/*[contains(@class,' topic/keywords ')]/descendant-or-self::*">
+ <!-- If this is the first term or keyword with this value -->
+ <xsl:if test="generate-id(key('meta-keywords',text())[1])=generate-id(.)">
+ <xsl:if test="position()>2"><xsl:text>, </xsl:text></xsl:if>
+ <xsl:value-of select="normalize-space(text())"/>
+ </xsl:if>
+ </xsl:for-each>
+ </xsl:variable>
+
+ <xsl:if test="string-length($keywords-content)>0">
+ <!-- <meta name="DC.subject" content="{$keywords-content}"/> -->
+ <xsl:text disable-output-escaping="yes">&lt;!-- meta name="DC.subject" content="</xsl:text>
+ <xsl:value-of select="$keywords-content"/>
+ <xsl:text disable-output-escaping="yes">" --&gt;</xsl:text>
+ <xsl:value-of select="$newline"/>
+ <!-- <meta name="keywords" content="{$keywords-content}"/> -->
+ <xsl:text disable-output-escaping="yes">&lt;!-- meta name="DC.subject" content="</xsl:text>
+ <xsl:value-of select="$keywords-content"/>
+ <xsl:text disable-output-escaping="yes">" --&gt;</xsl:text>
+ <xsl:value-of select="$newline"/>
+ </xsl:if>
+</xsl:template>
+
+<!-- CONTENT: Relation - related-links -->
+<xsl:template match="*[contains(@class,' topic/link ')]/@href" mode="gen-metadata">
+ <xsl:variable name="linkmeta">
+ <xsl:value-of select="normalize-space(.)"/>
+ </xsl:variable>
+ <xsl:choose>
+ <xsl:when test="substring($linkmeta,1,1)='#'" /> <!-- ignore internal file links -->
+ <xsl:otherwise>
+ <xsl:variable name="linkmeta_ext">
+ <xsl:choose>
+ <xsl:when test="contains($linkmeta,'.dita')">
+ <xsl:value-of select="substring-before($linkmeta,'.dita')"/>.<xsl:value-of select="$OUTEXT"/><xsl:value-of select="substring-after($linkmeta,'.dita')"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="$linkmeta"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+ <!-- <meta name="DC.Relation" scheme="URI">
+ <xsl:attribute name="content"><xsl:value-of select="$linkmeta_ext"/></xsl:attribute>
+ </meta> -->
+ <xsl:text disable-output-escaping="yes">&lt;!-- meta name="DC.relation" scheme="URI" content="</xsl:text>
+ <xsl:value-of select="$linkmeta_ext"/>
+ <xsl:text disable-output-escaping="yes">" --&gt;</xsl:text>
+ <xsl:value-of select="$newline"/>
+ </xsl:otherwise>
+ </xsl:choose>
+</xsl:template>
+
+<!-- Do not let any other @href's inside related-links generate metadata -->
+<xsl:template match="*/@href" mode="gen-metadata" priority="0"/>
+
+<!-- INTELLECTUAL PROPERTY: Contributor - prolog/author -->
+<!-- INTELLECTUAL PROPERTY: Creator -->
+<!-- Default is type='creator' -->
+<xsl:template match="*[contains(@class,' topic/author ')]" mode="gen-metadata">
+ <xsl:choose>
+ <xsl:when test="@type= 'contributor'">
+ <!-- <meta name="DC.Contributor" content="{normalize-space(.)}"/> -->
+ <xsl:text disable-output-escaping="yes">&lt;!-- meta name="DC.Contributor" content="</xsl:text>
+ <xsl:value-of select="normalize-space(.)"/>
+ <xsl:text disable-output-escaping="yes">" --&gt;</xsl:text>
+ <xsl:value-of select="$newline"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <!-- <meta name="DC.Creator" content="{normalize-space(.)}"/> -->
+ <xsl:text disable-output-escaping="yes">&lt;!-- meta name="DC.Creator" content="</xsl:text>
+ <xsl:value-of select="normalize-space(.)"/>
+ <xsl:text disable-output-escaping="yes">" --&gt;</xsl:text>
+ <xsl:value-of select="$newline"/>
+ </xsl:otherwise>
+ </xsl:choose>
+</xsl:template>
+
+<!-- INTELLECTUAL PROPERTY: Publisher - prolog/publisher -->
+<xsl:template match="*[contains(@class,' topic/publisher ')]" mode="gen-metadata">
+ <!-- <meta name="DC.Publisher" content="{normalize-space(.)}"/> -->
+ <xsl:text disable-output-escaping="yes">&lt;!-- meta name="DC.Publisher" content="</xsl:text>
+ <xsl:value-of select="normalize-space(.)"/>
+ <xsl:text disable-output-escaping="yes">" --&gt;</xsl:text>
+ <xsl:value-of select="$newline"/>
+</xsl:template>
+
+<!-- Place copyrights in keys for quick lookup. A copyright should only appear once; if it is primary
+ it should appear first with other primaries; if secondary, after primaries; otherwise, after
+ any that had @type. -->
+<xsl:key name="primary-meta-copyright" match="*[contains(@class,' topic/copyright ')][@type='primary']"
+ use="concat(*[contains(@class,' topic/copyryear ')]/@year,
+ *[contains(@class,' topic/copyrholder ')])"/>
+<xsl:key name="secondary-meta-copyright" match="*[contains(@class,' topic/copyright ')][@type='secondary']"
+ use="concat(*[contains(@class,' topic/copyryear ')]/@year,
+ *[contains(@class,' topic/copyrholder ')])"/>
+<xsl:key name="meta-copyright" match="*[contains(@class,' topic/copyright ')][not(@type)]"
+ use="concat(*[contains(@class,' topic/copyryear ')]/@year,
+ *[contains(@class,' topic/copyrholder ')])"/>
+
+<xsl:template name="generate-copyright-attributes">
+ <xsl:choose>
+ <xsl:when test="*[contains(@class,' topic/copyrholder ')]/text() | *[contains(@class,' topic/copyrholder ')]/*">
+ <xsl:value-of select="normalize-space(*[contains(@class,' topic/copyrholder ')])"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:text>(C) </xsl:text>
+ <xsl:call-template name="getString">
+ <xsl:with-param name="stringName" select="'Copyright IBM'"/>
+ </xsl:call-template>
+ </xsl:otherwise>
+ </xsl:choose>
+ <xsl:for-each select="*[contains(@class,' topic/copyryear ')]">
+ <xsl:text> </xsl:text><xsl:value-of select="@year"/>
+ </xsl:for-each>
+</xsl:template>
+
+<xsl:template match="*" mode="valid-copyright">
+ <!--P018721: if year and holder are both empty, do not generate anything -->
+ <xsl:variable name="copyrInfo">
+ <!-- Check for any text in the copyrholder or the year; if both are empty, no
+ copyright will be used -->
+ <xsl:value-of select="*[contains(@class,' topic/copyrholder ')] |
+ *[contains(@class,' topic/copyryear ')]/@year"/>
+ </xsl:variable>
+ <xsl:if test="normalize-space($copyrInfo)!=''">
+ <!-- <meta name="copyright"><xsl:call-template name="generate-copyright-attributes"/></meta> -->
+ <xsl:text disable-output-escaping="yes">&lt;!-- meta name="copyright" content="</xsl:text>
+ <xsl:call-template name="generate-copyright-attributes"/>
+ <xsl:text disable-output-escaping="yes">" --&gt;</xsl:text>
+ <xsl:value-of select="$newline"/>
+ <!-- <meta name="DC.Rights.Owner"><xsl:call-template name="generate-copyright-attributes"/></meta> -->
+ <xsl:text disable-output-escaping="yes">&lt;!-- meta name="DC.Rights.Owner" content="</xsl:text>
+ <xsl:call-template name="generate-copyright-attributes"/>
+ <xsl:text disable-output-escaping="yes">" --&gt;</xsl:text>
+ <xsl:value-of select="$newline"/>
+ </xsl:if>
+</xsl:template>
+
+<!-- INTELLECTUAL PROPERTY: Rights - prolog/copyright -->
+<xsl:template match="*[contains(@class,' topic/copyright ')]" mode="gen-metadata">
+ <xsl:variable name="keylookup"><xsl:value-of select="concat(*[contains(@class,' topic/copyryear ')]/@year,
+ *[contains(@class,' topic/copyrholder ')])"/></xsl:variable>
+ <xsl:choose>
+ <!-- If primary, ensure this is the first time it was used as primary -->
+ <xsl:when test="@type='primary'">
+ <xsl:if test="generate-id(.)=generate-id(key('primary-meta-copyright',$keylookup)[1])">
+ <xsl:apply-templates select="." mode="valid-copyright"/>
+ </xsl:if>
+ </xsl:when>
+ <!-- If secondary, this should be the first time it was used as secondary, AND it should not be primary -->
+ <xsl:when test="@type='secondary'">
+ <xsl:if test="not(key('primary-meta-copyright',$keylookup)) and
+ generate-id(.)=generate-id(key('secondary-meta-copyright',$keylookup)[1])">
+ <xsl:apply-templates select="." mode="valid-copyright"/>
+ </xsl:if>
+ </xsl:when>
+ <!-- No type: should not be used as primary or secondary, and this should be the first time it is used -->
+ <xsl:otherwise>
+ <xsl:if test="not(key('primary-meta-copyright',$keylookup)) and
+ not(key('secondary-meta-copyright',$keylookup)) and
+ generate-id(.)=generate-id(key('meta-copyright',$keylookup)[1])">
+ <xsl:apply-templates select="." mode="valid-copyright"/>
+ </xsl:if>
+ </xsl:otherwise>
+ </xsl:choose>
+</xsl:template>
+
+<!-- Usage Rights - prolog/permissions -->
+<xsl:template match="*[contains(@class,' topic/permissions ')]" mode="gen-metadata">
+ <xsl:text disable-output-escaping="yes">&lt;!-- meta name="DC.Rights.Usage" content="</xsl:text>
+ <xsl:value-of select="@view"/>
+ <xsl:text disable-output-escaping="yes">" --&gt;</xsl:text>
+ <xsl:value-of select="$newline"/>
+</xsl:template>
+
+<!-- = = = = = = = = = = = Product - Audience = = = = = = = = = = = -->
+<!-- Audience -->
+<xsl:template match="*[contains(@class,' topic/audience ')]/@experiencelevel" mode="gen-metadata">
+ <xsl:text disable-output-escaping="yes">&lt;!-- meta name="DC.Audience.Experiencelevel" content="</xsl:text>
+ <xsl:value-of select="."/>
+ <xsl:text disable-output-escaping="yes">" --&gt;</xsl:text>
+ <xsl:value-of select="$newline"/>
+</xsl:template>
+<xsl:template match="*[contains(@class,' topic/audience ')]/@importance" mode="gen-metadata">
+ <xsl:text disable-output-escaping="yes">&lt;!-- meta name="DC.Audience.Importance" content="</xsl:text>
+ <xsl:value-of select="."/>
+ <xsl:text disable-output-escaping="yes">" --&gt;</xsl:text>
+ <xsl:value-of select="$newline"/>
+</xsl:template>
+<xsl:template match="*[contains(@class,' topic/audience ')]/@name" mode="gen-metadata">
+ <xsl:text disable-output-escaping="yes">&lt;!-- meta name="DC.Audience.Name" content="</xsl:text>
+ <xsl:value-of select="."/>
+ <xsl:text disable-output-escaping="yes">" --&gt;</xsl:text>
+ <xsl:value-of select="$newline"/>
+</xsl:template>
+<xsl:template match="*[contains(@class,' topic/audience ')]/@job" mode="gen-metadata">
+ <xsl:choose>
+ <xsl:when test=".='other'">
+ <xsl:text disable-output-escaping="yes">&lt;!-- meta name="DC.Audience.Job" content="</xsl:text>
+ <xsl:value-of select="normalize-space(../@otherjob)"/>
+ <xsl:text disable-output-escaping="yes">" --&gt;</xsl:text>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:text disable-output-escaping="yes">&lt;!-- meta name="DC.Audience.Job" content="</xsl:text>
+ <xsl:value-of select="."/>
+ <xsl:text disable-output-escaping="yes">" --&gt;</xsl:text>
+ </xsl:otherwise>
+ </xsl:choose>
+ <xsl:value-of select="$newline"/>
+</xsl:template>
+<xsl:template match="*[contains(@class,' topic/audience ')]/@type" mode="gen-metadata">
+ <xsl:choose>
+ <xsl:when test=".='other'">
+ <xsl:text disable-output-escaping="yes">&lt;!-- meta name="DC.Audience.Type" content="</xsl:text>
+ <xsl:value-of select="normalize-space(../@othertype)"/>
+ <xsl:text disable-output-escaping="yes">" --&gt;</xsl:text>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:text disable-output-escaping="yes">&lt;!-- meta name="DC.Audience.Type" content="</xsl:text>
+ <xsl:value-of select="."/>
+ <xsl:text disable-output-escaping="yes">" --&gt;</xsl:text>
+ </xsl:otherwise>
+ </xsl:choose>
+ <xsl:value-of select="$newline"/>
+</xsl:template>
+
+<xsl:template match="*[contains(@class,' topic/prodname ')]" mode="gen-metadata">
+ <xsl:variable name="prodnamemeta">
+ <xsl:apply-templates select="*|text()" mode="text-only"/>
+ </xsl:variable>
+ <meta name="prodname">
+ <xsl:attribute name="content"><xsl:value-of select="normalize-space($prodnamemeta)"/></xsl:attribute>
+ </meta>
+ <xsl:value-of select="$newline"/>
+</xsl:template>
+
+<xsl:template match="*[contains(@class,' topic/vrm ')]/@version" mode="gen-metadata">
+ <xsl:text disable-output-escaping="yes">&lt;!-- meta name="version" content="</xsl:text>
+ <xsl:value-of select="."/>
+ <xsl:text disable-output-escaping="yes">" --&gt;</xsl:text>
+ <xsl:value-of select="$newline"/>
+</xsl:template>
+<xsl:template match="*[contains(@class,' topic/vrm ')]/@release" mode="gen-metadata">
+ <xsl:text disable-output-escaping="yes">&lt;!-- meta name="release" content="</xsl:text>
+ <xsl:value-of select="."/>
+ <xsl:text disable-output-escaping="yes">" --&gt;</xsl:text>
+ <xsl:value-of select="$newline"/>
+</xsl:template>
+<xsl:template match="*[contains(@class,' topic/vrm ')]/@modification" mode="gen-metadata">
+ <xsl:text disable-output-escaping="yes">&lt;!-- meta name="modification" content="</xsl:text>
+ <xsl:value-of select="."/>
+ <xsl:text disable-output-escaping="yes">" --&gt;</xsl:text>
+ <xsl:value-of select="$newline"/>
+</xsl:template>
+
+<xsl:template match="*[contains(@class,' topic/brand ')]" mode="gen-metadata">
+ <xsl:variable name="brandmeta">
+ <xsl:apply-templates select="*|text()" mode="text-only"/>
+ </xsl:variable>
+ <xsl:text disable-output-escaping="yes">&lt;!-- meta name="brand" content="</xsl:text>
+ <xsl:value-of select="normalize-space($brandmeta)"/>
+ <xsl:text disable-output-escaping="yes">" --&gt;</xsl:text>
+ <xsl:value-of select="$newline"/>
+</xsl:template>
+
+<xsl:template match="*[contains(@class,' topic/component ')]" mode="gen-metadata">
+ <xsl:variable name="componentmeta">
+ <xsl:apply-templates select="*|text()" mode="text-only"/>
+ </xsl:variable>
+ <xsl:text disable-output-escaping="yes">&lt;!-- meta name="component" content="</xsl:text>
+ <xsl:value-of select="normalize-space($componentmeta)"/>
+ <xsl:text disable-output-escaping="yes">" --&gt;</xsl:text>
+ <xsl:value-of select="$newline"/>
+</xsl:template>
+
+<xsl:template match="*[contains(@class,' topic/featnum ')]" mode="gen-metadata">
+ <xsl:variable name="featnummeta">
+ <xsl:apply-templates select="*|text()" mode="text-only"/>
+ </xsl:variable>
+ <xsl:text disable-output-escaping="yes">&lt;!-- meta name="featnum" content="</xsl:text>
+ <xsl:value-of select="normalize-space($featnummeta)"/>
+ <xsl:text disable-output-escaping="yes">" --&gt;</xsl:text>
+ <xsl:value-of select="$newline"/>
+</xsl:template>
+
+<xsl:template match="*[contains(@class,' topic/prognum ')]" mode="gen-metadata">
+ <xsl:variable name="prognummeta">
+ <xsl:apply-templates select="*|text()" mode="text-only"/>
+ </xsl:variable>
+ <xsl:text disable-output-escaping="yes">&lt;!-- meta name="prognum" content="</xsl:text>
+ <xsl:value-of select="normalize-space($prognummeta)"/>
+ <xsl:text disable-output-escaping="yes">" --&gt;</xsl:text>
+ <xsl:value-of select="$newline"/>
+</xsl:template>
+
+<xsl:template match="*[contains(@class,' topic/platform ')]" mode="gen-metadata">
+ <xsl:variable name="platformmeta">
+ <xsl:apply-templates select="*|text()" mode="text-only"/>
+ </xsl:variable>
+ <xsl:text disable-output-escaping="yes">&lt;!-- meta name="platform" content="</xsl:text>
+ <xsl:value-of select="normalize-space($platformmeta)"/>
+ <xsl:text disable-output-escaping="yes">" --&gt;</xsl:text>
+ <xsl:value-of select="$newline"/>
+</xsl:template>
+
+<xsl:template match="*[contains(@class,' topic/series ')]" mode="gen-metadata">
+ <xsl:variable name="seriesmeta">
+ <xsl:apply-templates select="*|text()" mode="text-only"/>
+ </xsl:variable>
+ <xsl:text disable-output-escaping="yes">&lt;!-- meta name="series" content="</xsl:text>
+ <xsl:value-of select="normalize-space($seriesmeta)"/>
+ <xsl:text disable-output-escaping="yes">" --&gt;</xsl:text>
+ <xsl:value-of select="$newline"/>
+</xsl:template>
+
+<!-- INSTANTIATION: Date - prolog/critdates/created -->
+<xsl:template match="*[contains(@class,' topic/critdates ')]/*[contains(@class,' topic/created ')]" mode="gen-metadata">
+ <xsl:text disable-output-escaping="yes">&lt;!-- meta name="DC.Date.Created" content="</xsl:text>
+ <xsl:value-of select="@date"/>
+ <xsl:text disable-output-escaping="yes">" --&gt;</xsl:text>
+ <xsl:value-of select="$newline"/>
+</xsl:template>
+
+<!-- prolog/critdates/revised/@modified -->
+<xsl:template match="*[contains(@class,' topic/critdates ')]/*[contains(@class,' topic/revised ')]/@modified" mode="gen-metadata">
+ <xsl:text disable-output-escaping="yes">&lt;!-- meta name="DC.Date.Modified" content="</xsl:text>
+ <xsl:value-of select="."/>
+ <xsl:text disable-output-escaping="yes">" --&gt;</xsl:text>
+ <xsl:value-of select="$newline"/>
+</xsl:template>
+
+<!-- prolog/critdates/revised/@golive -->
+<xsl:template match="*[contains(@class,' topic/critdates ')]/*[contains(@class,' topic/revised ')]/@golive" mode="gen-metadata">
+ <xsl:text disable-output-escaping="yes">&lt;!-- meta name="DC.Date.Issued" content="</xsl:text>
+ <xsl:value-of select="."/>
+ <xsl:text disable-output-escaping="yes">" --&gt;</xsl:text>
+ <xsl:value-of select="$newline"/>
+ <xsl:text disable-output-escaping="yes">&lt;!-- meta name="DC.Date.Available" content="</xsl:text>
+ <xsl:value-of select="."/>
+ <xsl:text disable-output-escaping="yes">" --&gt;</xsl:text>
+ <xsl:value-of select="$newline"/>
+</xsl:template>
+
+<!-- prolog/critdates/revised/@expiry -->
+<xsl:template match="*[contains(@class,' topic/critdates ')]/*[contains(@class,' topic/revised ')]/@expiry" mode="gen-metadata">
+ <xsl:text disable-output-escaping="yes">&lt;!-- meta name="DC.Date.Expiry" content="</xsl:text>
+ <xsl:value-of select="."/>
+ <xsl:text disable-output-escaping="yes">" --&gt;</xsl:text>
+ <xsl:value-of select="$newline"/>
+</xsl:template>
+
+<!-- prolog/metadata/othermeta -->
+<xsl:template match="*[contains(@class,' topic/othermeta ')]" mode="gen-metadata">
+ <xsl:text disable-output-escaping="yes">&lt;!-- meta name="</xsl:text>
+ <xsl:value-of select="@name"/>
+ <xsl:text>" content="</xsl:text>
+ <xsl:value-of select="@content"/>
+ <xsl:text disable-output-escaping="yes">" --&gt;</xsl:text>
+ <xsl:value-of select="$newline"/>
+</xsl:template>
+
+<!-- INSTANTIATION: Format -->
+<!-- this value is based on output format used for DC indexing, not source.
+ Put in this odd template for easy overriding, if creating another output format. -->
+<xsl:template match="*" mode="gen-format-metadata">
+ <xsl:text disable-output-escaping="yes">&lt;!-- meta name="DC.Format" content="XML" --&gt;</xsl:text>
+ <xsl:value-of select="$newline"/>
+</xsl:template>
+
+<!-- INSTANTIATION: Identifier --> <!-- id is an attribute on Topic -->
+<xsl:template match="@id" mode="gen-metadata">
+ <xsl:text disable-output-escaping="yes">&lt;!-- meta name="DC.Identifier" content="</xsl:text>
+ <xsl:value-of select="."/>
+ <xsl:text disable-output-escaping="yes">" --&gt;</xsl:text>
+ <xsl:value-of select="$newline"/>
+</xsl:template>
+
+<!-- INSTANTIATION: Language -->
+<!-- ideally, take the first token of the language attribute value -->
+<xsl:template match="@xml:lang" mode="gen-metadata">
+ <xsl:text disable-output-escaping="yes">&lt;!-- meta name="DC.Language" content="</xsl:text>
+ <xsl:value-of select="."/>
+ <xsl:text disable-output-escaping="yes">" --&gt;</xsl:text>
+ <xsl:value-of select="$newline"/>
+</xsl:template>
+
+</xsl:stylesheet>
diff --git a/src/help/studio_help/tools/ditaot/plugins/cshelp/xsl/context2contexttemp.xsl b/src/help/studio_help/tools/ditaot/plugins/cshelp/xsl/context2contexttemp.xsl
new file mode 100644
index 0000000..25fe1a6
--- /dev/null
+++ b/src/help/studio_help/tools/ditaot/plugins/cshelp/xsl/context2contexttemp.xsl
@@ -0,0 +1,48 @@
+<?xml version="1.0"?>
+<!-- (c) Copyright IBM Corp. 2005, 2006 All Rights Reserved. -->
+<!-- This file is part of the DITA Open Toolkit project hosted on -->
+<!-- Sourceforge.net. See the accompanying license.txt file for -->
+<!-- applicable licenses. -->
+
+<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+
+<xsl:variable name="cr"><xsl:text>
+</xsl:text></xsl:variable>
+<xsl:variable name="lt">&#60;</xsl:variable>
+<xsl:variable name="gt">&#62;</xsl:variable>
+
+ <xsl:template match="/">
+ <xsl:processing-instruction name="NLS">type="org.eclipse.help.contexts"</xsl:processing-instruction><xsl:value-of select="$cr"/>
+ <contexts><xsl:value-of select="$cr"/>
+ <xsl:apply-templates select="//context" />
+ </contexts><xsl:value-of select="$cr"/>
+ </xsl:template>
+
+ <xsl:template match="//comment()">
+ <xsl:copy />
+ </xsl:template>
+
+ <xsl:template match="context">
+ <context id="{@id}">
+ <xsl:value-of select="$cr"/>
+ <xsl:apply-templates />
+ </context><xsl:value-of select="$cr"/>
+ </xsl:template>
+
+ <xsl:template match="description">
+ <description>
+ <xsl:apply-templates />
+ </description><xsl:value-of select="$cr"/>
+ </xsl:template>
+
+ <xsl:template match="b">
+ <xsl:text>&lt;b&gt;</xsl:text>
+ <xsl:apply-templates />
+ <xsl:text>&lt;/b&gt;</xsl:text>
+ </xsl:template>
+
+ <xsl:template match="topic">
+ <xsl:copy-of select="." />
+ </xsl:template>
+
+</xsl:stylesheet> \ No newline at end of file
diff --git a/src/help/studio_help/tools/ditaot/plugins/cshelp/xsl/context2dita.xsl b/src/help/studio_help/tools/ditaot/plugins/cshelp/xsl/context2dita.xsl
new file mode 100644
index 0000000..65ac1bc
--- /dev/null
+++ b/src/help/studio_help/tools/ditaot/plugins/cshelp/xsl/context2dita.xsl
@@ -0,0 +1,130 @@
+<?xml version="1.0"?>
+<!-- (c) Copyright IBM Corp. 2005, 2006 All Rights Reserved. -->
+<!-- This file is part of the DITA Open Toolkit project hosted on -->
+<!-- Sourceforge.net. See the accompanying license.txt file for -->
+<!-- applicable licenses. -->
+
+<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+
+<xsl:output method="xml"
+ encoding="utf-8"
+ indent="no"
+ doctype-system="..\dtd\cshelp.dtd"
+ doctype-public="-//IBM//DTD DITA CSHelp//EN"
+/>
+
+<xsl:variable name="cr"><xsl:text>
+</xsl:text></xsl:variable>
+<xsl:variable name="lt">&#60;</xsl:variable>
+<xsl:variable name="gt">&#62;</xsl:variable>
+
+ <xsl:template match="//contexts">
+ <xsl:value-of select="$cr"/>
+ <cshelp id="csh_outer_container" xml:lang="en-us"><xsl:value-of select="$cr"/>
+ <title></title><xsl:value-of select="$cr"/>
+ <shortdesc></shortdesc><xsl:value-of select="$cr"/>
+ <csbody></csbody><xsl:value-of select="$cr"/>
+ <xsl:apply-templates />
+ </cshelp><xsl:value-of select="$cr"/>
+ </xsl:template>
+
+ <xsl:template match="//comment()">
+ <xsl:copy />
+ </xsl:template>
+
+ <xsl:template match="//context">
+
+ <cshelp id="{@id}">
+ <xsl:value-of select="$cr"/>
+
+ <title><xsl:value-of select="@title"/></title><xsl:value-of select="$cr"/>
+
+ <shortdesc>
+ <xsl:choose>
+ <xsl:when test="contains(description,$cr)">
+ <xsl:call-template name="br-replace-1">
+ <xsl:with-param name="brtext" select="substring-before(description,$cr)"/>
+ </xsl:call-template>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="description" disable-output-escaping="yes" />
+ </xsl:otherwise>
+ </xsl:choose>
+ </shortdesc><xsl:value-of select="$cr"/>
+
+ <csbody><xsl:value-of select="$cr"/>
+ <xsl:if test="contains(description,$cr)">
+ <p>
+ <xsl:call-template name="br-replace-2">
+ <xsl:with-param name="brtext2" select="substring-after(description,$cr)"/>
+ </xsl:call-template>
+ </p>
+ </xsl:if>
+ </csbody><xsl:value-of select="$cr" />
+
+ <xsl:if test="topic">
+ <related-links><xsl:value-of select="$cr"/>
+ <xsl:for-each select="topic">
+ <link href="{@href}">
+ <xsl:attribute name="format">
+ <xsl:call-template name="find-file-ext">
+ <xsl:with-param name="path" select="@href"/>
+ </xsl:call-template>
+ </xsl:attribute>
+ <linktext>
+ <xsl:value-of select="@label"/>
+ </linktext>
+ </link><xsl:value-of select="$cr"/>
+ </xsl:for-each>
+ </related-links><xsl:value-of select="$cr"/>
+ </xsl:if>
+ </cshelp><xsl:value-of select="$cr"/>
+ </xsl:template>
+
+ <xsl:template name="br-replace-1">
+ <xsl:param name="brtext"/>
+ <xsl:choose>
+ <xsl:when test="contains($brtext,$cr)"> <!-- is there a CR within the text? -->
+ <xsl:value-of select="$brtext" disable-output-escaping="yes" />
+ <xsl:value-of select="$cr"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="$brtext" disable-output-escaping="yes" /> <!-- No CRs, just output -->
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+
+ <xsl:template name="br-replace-2">
+ <xsl:param name="brtext2"/>
+ <xsl:choose>
+ <xsl:when test="contains($brtext2,$cr)"> <!-- is there a CR within the text? -->
+ <xsl:if test="string-length(substring-before($brtext2,$cr)) &gt; 0">
+ <xsl:value-of select="substring-before($brtext2,$cr)" disable-output-escaping="yes" /> <!-- yes - substring & add the BR & newline -->
+ <xsl:text disable-output-escaping="yes">&#60;</xsl:text>/p<xsl:text disable-output-escaping="yes">&#62;</xsl:text><xsl:value-of select="$cr"/>
+ <xsl:text disable-output-escaping="yes">&#60;</xsl:text>p<xsl:text disable-output-escaping="yes">&#62;</xsl:text>
+ </xsl:if>
+ <xsl:call-template name="br-replace-2">
+ <xsl:with-param name="brtext2" select="substring-after($brtext2,$cr)"/>
+ </xsl:call-template>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="$brtext2" disable-output-escaping="yes" /> <!-- No CRs, just output -->
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+
+ <xsl:template name="find-file-ext">
+ <xsl:param name="path"/>
+ <xsl:choose>
+ <xsl:when test="contains($path,'.')">
+ <xsl:call-template name="find-file-ext">
+ <xsl:with-param name="path" select="substring-after($path,'.')" />
+ </xsl:call-template>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="$path" />
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+
+</xsl:stylesheet> \ No newline at end of file
diff --git a/src/help/studio_help/tools/ditaot/plugins/cshelp/xsl/cshdisplay.xsl b/src/help/studio_help/tools/ditaot/plugins/cshelp/xsl/cshdisplay.xsl
new file mode 100644
index 0000000..0c0c8b5
--- /dev/null
+++ b/src/help/studio_help/tools/ditaot/plugins/cshelp/xsl/cshdisplay.xsl
@@ -0,0 +1,71 @@
+<?xml version="1.0"?>
+<!-- (c) Copyright IBM Corp. 2005, 2006 All Rights Reserved. -->
+<!-- This file is part of the DITA Open Toolkit project hosted on -->
+<!-- Sourceforge.net. See the accompanying license.txt file for -->
+<!-- applicable licenses. -->
+
+<xsl:stylesheet version="1.0"
+ xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+
+<xsl:template match="*[contains(@class,' cshelp/cshelp ')]" name="cshelp">
+ <xsl:choose>
+ <xsl:when test="$DRAFT='yes'">
+ <!-- review output -->
+ <xsl:choose>
+ <xsl:when test="not(parent::*)">
+ <html><xsl:value-of select="$newline"/>
+ <head /><xsl:value-of select="$newline"/>
+ <body><xsl:value-of select="$newline"/>
+ <xsl:call-template name="csreviewoutput"/>
+ <xsl:if test="*[contains(@class,' cshelp/cshelp ')]">
+ <xsl:apply-templates select="*[contains(@class,' cshelp/cshelp ')]"/>
+ </xsl:if>
+ </body><xsl:value-of select="$newline"/>
+ </html><xsl:value-of select="$newline"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:call-template name="csreviewoutput"/>
+ <xsl:if test="*[contains(@class,' cshelp/cshelp ')]">
+ <xsl:apply-templates select="*[contains(@class,' cshelp/cshelp ')]"/>
+ </xsl:if>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:when>
+ <xsl:otherwise>
+ <!-- Otherwise, do standard processing -->
+ <xsl:choose>
+ <xsl:when test="not(parent::*)">
+ <xsl:call-template name="chapter-setup"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:apply-imports/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:otherwise>
+ </xsl:choose>
+</xsl:template>
+
+<xsl:template name="csreviewoutput">
+ <hr/><xsl:value-of select="$newline"/>
+ <hr/><xsl:value-of select="$newline"/>
+<strong><xsl:value-of select="@id"/></strong><br/><xsl:value-of select="$newline"/>
+<xsl:value-of select="*[contains(@class,' cshelp/csprolog ')]/*[contains(@class,' cshelp/csmetadata ')]/*[contains(@class,' cshelp/cswindowtitle ')]"/><br/><xsl:value-of select="$newline"/>
+<xsl:value-of select="*[contains(@class,' cshelp/csprolog ')]/*[contains(@class,' cshelp/csmetadata ')]/*[contains(@class,' cshelp/cswidgetlabel ')]"/><br/><xsl:value-of select="$newline"/>
+<br/><xsl:value-of select="$newline"/>
+ <p><xsl:apply-templates select="*[contains(@class,' topic/shortdesc ')]"/></p><xsl:value-of select="$newline"/>
+ <xsl:apply-templates select="*[contains(@class,' cshelp/csbody ')]"/><xsl:value-of select="$newline"/>
+ <xsl:if test="*[contains(@class,' topic/related-links ')]">
+ <xsl:for-each select="*[contains(@class,' topic/related-links ')]/*[contains(@class,' topic/link ')]">
+ <a>
+ <xsl:attribute name="href">
+ <xsl:call-template name="href"/>
+ </xsl:attribute>
+ <xsl:value-of select="linktext" />
+ </a>
+ <br/><xsl:value-of select="$newline"/>
+ </xsl:for-each>
+ </xsl:if>
+ <br/><xsl:value-of select="$newline"/><xsl:value-of select="$newline"/>
+</xsl:template>
+
+</xsl:stylesheet>
diff --git a/src/help/studio_help/tools/ditaot/plugins/cshelp/xsl/dit2context.xsl b/src/help/studio_help/tools/ditaot/plugins/cshelp/xsl/dit2context.xsl
new file mode 100644
index 0000000..b28b4a7
--- /dev/null
+++ b/src/help/studio_help/tools/ditaot/plugins/cshelp/xsl/dit2context.xsl
@@ -0,0 +1,1275 @@
+<?xml version="1.0"?>
+<!-- (c) Copyright IBM Corp. 2005, 2007 All Rights Reserved. -->
+<!-- This file is part of the DITA Open Toolkit project hosted on -->
+<!-- Sourceforge.net. See the accompanying license.txt file for -->
+<!-- applicable licenses. -->
+
+<xsl:stylesheet version="1.0"
+ xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ xmlns:saxon="http://icl.com/saxon"
+ xmlns:xt="http://www.jclark.com/xt"
+ extension-element-prefixes="saxon xt">
+
+<xsl:import href="../../../xsl/common/dita-utilities.xsl" />
+<xsl:include href="../../../xsl/common/output-message.xsl"/>
+<xsl:include href="GetCSHMeta.xsl"/>
+
+<!-- /OUTEXT = default "output extension" processing parameter ('html')-->
+<!-- Should be overridden by rexx command to be 'xml' -->
+<xsl:param name="OUTEXT" select="'html'"/><!-- "htm" and "html" are valid values -->
+
+<!-- /WORKDIR = the working directory that contains the document being transformed.
+ Needed as a directory prefix for the @conref "document()" function calls.
+ default is '../doc/')-->
+<xsl:param name="WORKDIR" select="'./'"/>
+
+<!-- /FILENAME = the file name (file name and extension only - no path) of the document being transformed.
+ Needed to help with debugging.
+ default is 'myfile.xml')-->
+<xsl:param name="FILENAME"/>
+
+<xsl:variable name="msgprefix">IDXS</xsl:variable>
+<xsl:variable name="newline"><xsl:text>
+</xsl:text></xsl:variable>
+
+<xsl:output indent="no"/>
+
+ <xsl:template match="/">
+ <xsl:value-of select="$newline"/>
+ <xsl:processing-instruction name="NLS">type="org.eclipse.help.contexts"</xsl:processing-instruction>
+ <xsl:value-of select="$newline"/>
+ <xsl:apply-templates select="//*[contains(@class,' cshelp/cshelp ')]"/> <!-- select is formatted to unnest nested cshelp elements -->
+ <xsl:text disable-output-escaping="yes">&lt;/contexts&gt;</xsl:text>
+ <xsl:value-of select="$newline"/>
+ </xsl:template>
+
+ <xsl:template match="//comment()">
+ <xsl:copy />
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' cshelp/cshelp ')]" name="cshelp">
+
+ <xsl:if test="not(parent::*[contains(@class,' cshelp/cshelp ')])">
+ <xsl:call-template name="getCSHMeta"/> <!-- 5/31/2006 -->
+ <!-- <xsl:call-template name="ibmcopyright"/>--> <!-- 5/30/2006 -->
+ <xsl:text disable-output-escaping="yes">&lt;contexts&gt;</xsl:text>
+ <xsl:value-of select="$newline"/>
+ </xsl:if>
+
+ <xsl:if test="parent::*[contains(@class,' cshelp/cshelp ')]">
+ <xsl:element name="context">
+ <xsl:attribute name="id">
+ <xsl:value-of select="@id"/>
+ </xsl:attribute>
+ <xsl:if test="*[contains(@class,' topic/title ')]/text() | *[contains(@class,' topic/title ')]/*">
+ <xsl:attribute name="title">
+ <xsl:value-of select="*[contains(@class,' topic/title ')]" />
+ </xsl:attribute>
+ </xsl:if>
+ <xsl:text>&#xA;</xsl:text>
+
+ <xsl:element name="description">
+ <xsl:apply-templates select="*[contains(@class,' topic/shortdesc ')]" />
+ <xsl:apply-templates select="*[contains(@class,' cshelp/csbody ')]" />
+ </xsl:element>
+ <xsl:text>&#xA;</xsl:text>
+
+ <xsl:if test="*[contains(@class,' topic/related-links ')]">
+ <xsl:for-each select="*[contains(@class,' topic/related-links ')]/*[contains(@class,' topic/link ')]">
+ <xsl:element name="topic">
+ <xsl:attribute name="href">
+ <xsl:call-template name="href"/>
+ </xsl:attribute>
+ <xsl:attribute name="label">
+ <xsl:choose>
+ <xsl:when test="*[contains(@class, ' topic/linktext ')]">
+ <xsl:apply-templates select="*[contains(@class, ' topic/linktext ')]"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <!-- use href -->
+ <xsl:call-template name="href"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:attribute>
+ </xsl:element><xsl:text>&#xA;</xsl:text>
+ </xsl:for-each>
+ </xsl:if>
+
+ </xsl:element><xsl:text>&#xA;</xsl:text>
+ </xsl:if>
+ </xsl:template>
+
+
+ <xsl:template match="*[contains(@class,' topic/shortdesc ')]" name="shortdesc">
+ <xsl:apply-templates />
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' cshelp/csbody ')]" name="csbody">
+ <xsl:if test="node()"><xsl:text>&#xA;&#xA;</xsl:text></xsl:if>
+ <xsl:apply-templates />
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' topic/boolean ')]" name="topic.boolean">
+ <!-- below copied from dit2htm.xsl -->
+ <xsl:value-of select="name()"/><xsl:text>: </xsl:text><xsl:value-of select="@state"/>
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' topic/cite ')]" name="topic.cite">
+ <xsl:call-template name="checkPreceding" />
+ <xsl:apply-templates />
+ <xsl:call-template name="checkFollowing" />
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' topic/dd ')]" name="topic.dd">
+ <!-- jta 11/06/2006 -->
+ <xsl:call-template name="indentDL"/>
+ <xsl:text> </xsl:text>
+ <xsl:apply-templates />
+ <!-- jta 10/16/2006 -->
+ <!-- <xsl:if test="position()!=last()"> -->
+ <xsl:if test="following-sibling::*[contains(@class,' topic/dd ')]">
+ <xsl:text>&#xA;</xsl:text>
+ </xsl:if>
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' topic/ddhd ')]" name="topic.ddhd">
+ <!-- jta 11/06/2006 -->
+ <xsl:call-template name="indentDL"/>
+ <xsl:text> </xsl:text><b><xsl:apply-templates /></b><xsl:text>&#xA;</xsl:text>
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' topic/desc ')]" name="topic.desc">
+ <xsl:call-template name="checkPreceding" />
+ <xsl:text>(</xsl:text><xsl:apply-templates /><xsl:text>)</xsl:text>
+ <xsl:call-template name="checkFollowing" />
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' topic/dl ')]" name="topic.dl">
+ <xsl:if test="not(parent::*[contains(@class,' cshelp/csbody ')])">
+ <xsl:choose>
+ <xsl:when test="parent::*[contains(@class,' topic/p ')]">
+ <xsl:text>&#xA;&#xA;</xsl:text>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:text>&#xA;</xsl:text>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:if>
+ <xsl:apply-templates />
+ <xsl:call-template name="isNestedTextFollows" />
+ <xsl:call-template name="isFirstChildOfCsbody" />
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' topic/dlentry ')]" name="topic.dlentry">
+ <xsl:apply-templates />
+ <!-- jta 11/07/2006 -->
+ <xsl:if test="following-sibling::*[contains(@class,' topic/dlentry ')]">
+ <xsl:text>&#xA;</xsl:text>
+ </xsl:if>
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' topic/dlhead ')]" name="topic.dlhead">
+ <xsl:apply-templates />
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' topic/draft-comment ')]" name="topic.draft-comment">
+ <!-- no output -->
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' topic/dt ')]" name="topic.dt">
+ <!-- jta 11/06/2006 -->
+ <xsl:call-template name="indentDL"/>
+ <b><xsl:apply-templates /></b><xsl:text>&#xA;</xsl:text>
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' topic/dthd ')]" name="topic.dthd">
+ <!-- jta 11/06/2006 -->
+ <xsl:call-template name="indentDL"/>
+ <b><xsl:apply-templates /></b><xsl:text>&#xA;</xsl:text>
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' topic/fig ')]" name="topic.fig">
+ <xsl:call-template name="twoPrecedingCRs" />
+ <xsl:apply-templates />
+ <xsl:call-template name="isFirstChildOfCsbody" />
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' topic/fn ')]" name="topic.fn">
+ <!-- no output -->
+ <xsl:call-template name="output-message">
+ <xsl:with-param name="msg">Eclipse context-sensitive help files do not support footnotes.</xsl:with-param>
+ <xsl:with-param name="msgnum">066</xsl:with-param>
+ <xsl:with-param name="msgsev">I</xsl:with-param>
+ </xsl:call-template>
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' topic/image ')]" name="topic.image">
+ <xsl:variable name="alttext">
+ <xsl:choose>
+ <xsl:when test="*[contains(@class,' topic/alt ')]"><xsl:apply-templates/></xsl:when>
+ <xsl:otherwise><xsl:value-of select="@alt"/></xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+ <!-- no output -->
+ <xsl:if test="$alttext=''">
+ <xsl:call-template name="output-message">
+ <xsl:with-param name="msg">Eclipse context-sensitive help files do not support images.</xsl:with-param>
+ <xsl:with-param name="msgnum">066</xsl:with-param>
+ <xsl:with-param name="msgsev">I</xsl:with-param>
+ </xsl:call-template>
+ </xsl:if>
+ <xsl:choose>
+ <xsl:when test="@placement='break'">
+ <xsl:call-template name="twoPrecedingCRs" />
+ <xsl:value-of select="$alttext"/>
+ <xsl:call-template name="isFirstChildOfCsbody" />
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:call-template name="checkPreceding" />
+ <xsl:value-of select="$alttext"/>
+ <xsl:call-template name="checkFollowing" />
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' topic/indexterm ')]" name="topic.indexterm">
+ <!-- no output -->
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' topic/indextermref ')]" name="topic.indextermref">
+ <!-- no output -->
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' topic/itemgroup ')]" name="topic.itemgroup">
+ <xsl:text>&#xA; </xsl:text><xsl:apply-templates />
+ <!-- jta 10/16/2006 -->
+ <xsl:call-template name="isFirstChildOfCsbody" />
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' topic/keyword ')]" name="topic.keyword">
+ <xsl:call-template name="checkPreceding" />
+ <xsl:apply-templates />
+ <xsl:call-template name="checkFollowing" />
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' topic/li ')]" name="topic.li">
+ <xsl:variable name="olcount" select="count(ancestor-or-self::*[contains(@class,' topic/ol ')])"/>
+ <!-- <xsl:variable name="ulcount" select="count(ancestor-or-self::*[contains(@class,' topic/ul ')])"/> -->
+ <!-- <xsl:variable name="slcount" select="count(ancestor-or-self::*[contains(@class,' topic/sl ')])"/> -->
+ <!-- <xsl:variable name="nestcount" select="number($olcount) + number($ulcount) + number($slcount)" /> -->
+ <!-- <xsl:choose> -->
+ <!-- <xsl:when test="number($nestcount)=1"> -->
+ <!-- <xsl:text> </xsl:text> -->
+ <!-- </xsl:when> -->
+ <!-- <xsl:when test="number($nestcount)=2"> -->
+ <!-- <xsl:text> </xsl:text> -->
+ <!-- </xsl:when> -->
+ <!-- <xsl:when test="number($nestcount)=3"> -->
+ <!-- <xsl:text> </xsl:text> -->
+ <!-- </xsl:when> -->
+ <!-- <xsl:otherwise> -->
+ <!-- <xsl:text> </xsl:text> -->
+ <!-- </xsl:otherwise> -->
+ <!-- </xsl:choose> -->
+ <xsl:call-template name="indentLI"/>
+ <xsl:if test="parent::*[contains(@class,' topic/ul ')]">
+ <xsl:text>&#x2D; </xsl:text>
+ </xsl:if>
+ <xsl:apply-templates />
+ <!-- jta 11/06/2006 -->
+ <xsl:if test="following-sibling::*[contains(@class,' topic/li ')]">
+ <xsl:text>&#xA;</xsl:text>
+ </xsl:if>
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' topic/lines ')]" name="topic.lines">
+ <xsl:call-template name="spec-title-nospace"/>
+ <xsl:call-template name="br-replace">
+ <xsl:with-param name="brtext" select="."/>
+ </xsl:call-template>
+ <xsl:call-template name="isFirstChildOfCsbody" />
+ </xsl:template>
+
+ <xsl:template name="spec-title-nospace">
+ <!-- below adapted from dit2htm.xsl -->
+ <xsl:if test="@spectitle"><b><xsl:value-of select="@spectitle"/></b><xsl:text>&#xA;</xsl:text></xsl:if>
+ </xsl:template>
+
+ <!-- Break replace - used for LINES -->
+ <!-- this replaces newlines with the BR element. Forces breaks. -->
+ <xsl:template name="br-replace">
+ <xsl:param name="brtext"/>
+ <!-- capture an actual newline within the xsl:text element -->
+ <xsl:variable name="cr"><xsl:text>
+</xsl:text></xsl:variable>
+ <xsl:choose>
+ <xsl:when test="contains($brtext,$cr)"> <!-- is there a CR within the text? -->
+ <xsl:value-of select="substring-before($brtext,$cr)"/> <!-- yes - substring & add the BR & newline -->
+ <xsl:value-of select="$cr"/>
+ <xsl:call-template name="br-replace"> <!-- call again to get remaining CRs -->
+ <xsl:with-param name="brtext" select="substring-after($brtext,$cr)"/>
+ </xsl:call-template>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="$brtext"/> <!-- No CRs, just output -->
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' topic/linkinfo ')]" name="topic.linkinfo">
+ <xsl:text>&#xA;</xsl:text><xsl:apply-templates /><xsl:text>&#xA;&#xA;</xsl:text>
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' topic/linklist ')]" name="topic.linklist">
+ <xsl:apply-templates />
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' topic/linkpool ')]" name="topic.linkpool">
+ <xsl:apply-templates />
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' topic/linktext ')]" name="topic.linktext">
+ <xsl:apply-templates />
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' topic/lq ')]" name="topic.lq">
+ <xsl:call-template name="twoPrecedingCRs" />
+ <xsl:call-template name="indentLQ" />
+ <xsl:text>&#34;</xsl:text><xsl:apply-templates /><xsl:text>&#34;</xsl:text>
+ <xsl:call-template name="isNestedTextFollows" />
+ <xsl:call-template name="isFirstChildOfCsbody" />
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' topic/msgblock ')]" name="topic.msgblock">
+ <xsl:call-template name="br-replace">
+ <xsl:with-param name="brtext" select="."/>
+ </xsl:call-template>
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' topic/note ')]" name="topic.note">
+ <!-- jta 10/16/2006 -->
+ <xsl:call-template name="twoPrecedingCRs"/>
+ <xsl:call-template name="indentNote" />
+ <!-- <xsl:text>&#xA;&#xA;</xsl:text> -->
+ <xsl:call-template name="spec-title"/>
+ <xsl:choose>
+ <xsl:when test="@type='note'">
+ <xsl:call-template name="note"/>
+ </xsl:when>
+ <xsl:when test="@type='tip'">
+ <xsl:call-template name="getString">
+ <xsl:with-param name="stringName" select="'Tip'"/>
+ </xsl:call-template>
+ <xsl:call-template name="getString">
+ <xsl:with-param name="stringName" select="'ColonSymbol'"/>
+ </xsl:call-template>
+ <xsl:text> </xsl:text>
+ <xsl:apply-templates /><xsl:text>&#xA;&#xA;</xsl:text>
+ </xsl:when>
+ <xsl:when test="@type='fastpath'">
+ <xsl:call-template name="getString">
+ <xsl:with-param name="stringName" select="'Fastpath'"/>
+ </xsl:call-template>
+ <xsl:call-template name="getString">
+ <xsl:with-param name="stringName" select="'ColonSymbol'"/>
+ </xsl:call-template>
+ <xsl:text> </xsl:text>
+ <xsl:apply-templates /><xsl:text>&#xA;&#xA;</xsl:text>
+ </xsl:when>
+ <xsl:when test="@type='important'">
+ <xsl:call-template name="getString">
+ <xsl:with-param name="stringName" select="'Important'"/>
+ </xsl:call-template>
+ <xsl:call-template name="getString">
+ <xsl:with-param name="stringName" select="'ColonSymbol'"/>
+ </xsl:call-template>
+ <xsl:text> </xsl:text>
+ <xsl:apply-templates /><xsl:text>&#xA;&#xA;</xsl:text>
+ </xsl:when>
+ <xsl:when test="@type='remember'">
+ <xsl:call-template name="getString">
+ <xsl:with-param name="stringName" select="'Remember'"/>
+ </xsl:call-template>
+ <xsl:call-template name="getString">
+ <xsl:with-param name="stringName" select="'ColonSymbol'"/>
+ </xsl:call-template>
+ <xsl:text> </xsl:text>
+ <xsl:apply-templates /><xsl:text>&#xA;&#xA;</xsl:text>
+ </xsl:when>
+ <xsl:when test="@type='restriction'">
+ <xsl:call-template name="getString">
+ <xsl:with-param name="stringName" select="'Restriction'"/>
+ </xsl:call-template>
+ <xsl:call-template name="getString">
+ <xsl:with-param name="stringName" select="'ColonSymbol'"/>
+ </xsl:call-template>
+ <xsl:text> </xsl:text>
+ <xsl:apply-templates />
+ </xsl:when>
+ <xsl:when test="@type='attention'">
+ <xsl:call-template name="getString">
+ <xsl:with-param name="stringName" select="'Attention'"/>
+ </xsl:call-template>
+ <xsl:call-template name="getString">
+ <xsl:with-param name="stringName" select="'ColonSymbol'"/>
+ </xsl:call-template>
+ <xsl:text> </xsl:text>
+ <xsl:apply-templates />
+ </xsl:when>
+ <xsl:when test="@type='caution'">
+ <xsl:call-template name="getString">
+ <xsl:with-param name="stringName" select="'Caution'"/>
+ </xsl:call-template>
+ <xsl:call-template name="getString">
+ <xsl:with-param name="stringName" select="'ColonSymbol'"/>
+ </xsl:call-template>
+ <xsl:text> </xsl:text>
+ <xsl:apply-templates />
+ </xsl:when>
+ <xsl:when test="@type='danger'">
+ <xsl:call-template name="getString">
+ <xsl:with-param name="stringName" select="'Danger'"/>
+ </xsl:call-template>
+ <xsl:call-template name="getString">
+ <xsl:with-param name="stringName" select="'ColonSymbol'"/>
+ </xsl:call-template>
+ <xsl:text> </xsl:text>
+ <xsl:apply-templates />
+ </xsl:when>
+ <xsl:when test="@type='other'">
+ <xsl:choose>
+ <xsl:when test="@othertype"> <!-- is there a type title? Use that -->
+ <!-- TBD: this attr is a key that should look up external, translateable text.
+ For now, just output the othertype attr value. -->
+ <xsl:value-of select="@othertype"/>
+ <xsl:call-template name="getString">
+ <xsl:with-param name="stringName" select="'ColonSymbol'"/>
+ </xsl:call-template>
+ <xsl:text> </xsl:text>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:call-template name="note"/> <!-- otherwise, give them the standard note -->
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:call-template name="note"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ <xsl:call-template name="isNestedTextFollows" />
+ <xsl:call-template name="isFirstChildOfCsbody" />
+ </xsl:template>
+
+ <xsl:template name="note">
+ <xsl:call-template name="getString">
+ <xsl:with-param name="stringName" select="'Note'"/>
+ </xsl:call-template>
+ <xsl:call-template name="getString">
+ <xsl:with-param name="stringName" select="'ColonSymbol'"/>
+ </xsl:call-template>
+ <xsl:text> </xsl:text>
+ <xsl:apply-templates />
+ </xsl:template>
+
+ <xsl:template name="spec-title">
+ <xsl:if test="@spectitle"><b><xsl:value-of select="@spectitle"/></b><xsl:text>&#xA;</xsl:text></xsl:if>
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' topic/object ')]" name="topic.object">
+ <!-- no output -->
+ <xsl:call-template name="output-message">
+ <xsl:with-param name="msg">Eclipse context-sensitive help files do not support objects.</xsl:with-param>
+ <xsl:with-param name="msgnum">066</xsl:with-param>
+ <xsl:with-param name="msgsev">I</xsl:with-param>
+ </xsl:call-template>
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' topic/ol ')]" name="topic.ol">
+ <xsl:variable name="olcount" select="count(ancestor-or-self::*[contains(@class,' topic/ol ')])"/>
+<!-- <xsl:variable name="ulcount" select="count(ancestor-or-self::*[contains(@class,' topic/ul ')])"/> -->
+<!-- <xsl:variable name="slcount" select="count(ancestor-or-self::*[contains(@class,' topic/sl ')])"/> -->
+<!-- <xsl:variable name="ddcount" select="count(ancestor-or-self::*[contains(@class,' topic/dd ')])"/> -->
+<!-- <xsl:variable name="nestcount" select="number($olcount) + number($ulcount) + number($slcount) + number($ddcount)" /> -->
+ <xsl:call-template name="twoPrecedingCRs" />
+
+<!-- <xsl:if test="parent::*[contains(@class,' topic/li ')] | parent::*[contains(@class,' topic/sli ')]"><xsl:text>&#xA;</xsl:text></xsl:if> -->
+ <xsl:for-each select="*[contains(@class,' topic/li ')]">
+<!-- <xsl:choose> -->
+<!-- <xsl:when test="number($nestcount)=1"> -->
+<!-- <xsl:text> </xsl:text> -->
+<!-- </xsl:when> -->
+<!-- <xsl:when test="number($nestcount)=2"> -->
+<!-- <xsl:text> </xsl:text> -->
+<!-- </xsl:when> -->
+<!-- <xsl:when test="number($nestcount)=3"> -->
+<!-- <xsl:text> </xsl:text> -->
+<!-- </xsl:when> -->
+<!-- <xsl:otherwise> -->
+<!-- <xsl:text> </xsl:text> -->
+<!-- </xsl:otherwise> -->
+<!-- </xsl:choose> -->
+ <xsl:call-template name="indentLI"/> <!-- jta 11/07/2006 -->
+ <xsl:choose>
+ <xsl:when test="$olcount mod 3 = 1">
+ <xsl:number value="position()" format="1. "/>
+ </xsl:when>
+ <xsl:when test="$olcount mod 3 = 2">
+ <xsl:number value="position()" format="a. "/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:number value="position()" format="i. "/>
+ </xsl:otherwise>
+ </xsl:choose>
+ <xsl:apply-templates /><!-- jta 11/07/2006 <xsl:text>&#xA;</xsl:text> -->
+ <xsl:if test="following-sibling::*[contains(@class,' topic/li ')]">
+ <xsl:text>&#xA;</xsl:text>
+ </xsl:if>
+ </xsl:for-each>
+ <xsl:call-template name="isNestedTextFollows" />
+ <xsl:call-template name="isFirstChildOfCsbody" />
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' topic/p ')]" name="topic.p">
+ <!-- jta 10/16/2006 -->
+ <xsl:call-template name="twoPrecedingCRs"/>
+ <xsl:call-template name="indentP" />
+ <xsl:apply-templates />
+ <!-- <xsl:text>&#xA;&#xA;</xsl:text> -->
+ <xsl:call-template name="isNestedTextFollows" />
+ <xsl:call-template name="isFirstChildOfCsbody" />
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' topic/ph ')]" name="topic.ph">
+ <xsl:call-template name="checkPreceding" />
+ <xsl:apply-templates />
+ <xsl:call-template name="checkFollowing" />
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' topic/pre ')]" name="topic.pre">
+ <xsl:call-template name="checkPreceding" />
+ <xsl:call-template name="br-replace">
+ <xsl:with-param name="brtext" select="."/>
+ </xsl:call-template>
+ <xsl:call-template name="checkFollowing" />
+ <xsl:call-template name="isFirstChildOfCsbody" />
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' topic/q ')]" name="topic.q">
+ <xsl:call-template name="checkPreceding" />
+ <xsl:text>&#34;</xsl:text><xsl:apply-templates /><xsl:text>&#34;</xsl:text> <!-- places quotation marks -->
+ <xsl:call-template name="checkFollowing" />
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' topic/required-cleanup ')]" name="topic.required-cleanup">
+ <!-- no output -->
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' topic/simpletable ')]" name="topic.simpletable">
+ <!-- no output -->
+ <xsl:call-template name="output-message">
+ <xsl:with-param name="msg">Eclipse context-sensitive help files do not support tables.</xsl:with-param>
+ <xsl:with-param name="msgnum">066</xsl:with-param>
+ <xsl:with-param name="msgsev">I</xsl:with-param>
+ </xsl:call-template>
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' topic/sl ')]" name="topic.sl">
+ <xsl:call-template name="twoPrecedingCRs" />
+<!-- <xsl:if test="parent::*[contains(@class,' topic/li ')] | parent::*[contains(@class,' topic/sli ')]"><xsl:text>&#xA;</xsl:text></xsl:if> -->
+ <xsl:apply-templates />
+ <xsl:call-template name="isNestedTextFollows" />
+ <xsl:call-template name="isFirstChildOfCsbody" />
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' topic/sli ')]" name="topic.sli">
+<!-- <xsl:variable name="olcount" select="count(ancestor-or-self::*[contains(@class,' topic/ol ')])"/> -->
+<!-- <xsl:variable name="ulcount" select="count(ancestor-or-self::*[contains(@class,' topic/ul ')])"/> -->
+<!-- <xsl:variable name="slcount" select="count(ancestor-or-self::*[contains(@class,' topic/sl ')])"/> -->
+<!-- <xsl:variable name="ddcount" select="count(ancestor-or-self::*[contains(@class,' topic/dd ')])"/> -->
+<!-- <xsl:variable name="nestcount" select="number($olcount) + number($ulcount) + number($slcount) + number($ddcount)" /> -->
+<!-- <xsl:choose> -->
+<!-- <xsl:when test="number($nestcount)=1"> -->
+<!-- <xsl:text> </xsl:text> -->
+<!-- </xsl:when> -->
+<!-- <xsl:when test="number($nestcount)=2"> -->
+<!-- <xsl:text> </xsl:text> -->
+<!-- </xsl:when> -->
+<!-- <xsl:when test="number($nestcount)=3"> -->
+<!-- <xsl:text> </xsl:text> -->
+<!-- </xsl:when> -->
+<!-- <xsl:otherwise> -->
+<!-- <xsl:text> </xsl:text> -->
+<!-- </xsl:otherwise> -->
+<!-- </xsl:choose> -->
+ <xsl:call-template name="indentLI"/>
+ <xsl:text>&#x2D; </xsl:text>
+ <xsl:apply-templates />
+ <xsl:if test="following-sibling::*[contains(@class,' topic/sli ')]">
+ <xsl:text>&#xA;</xsl:text>
+ </xsl:if>
+ </xsl:template>
+
+
+ <xsl:template match="*[contains(@class,' topic/state ')]" name="topic.state">
+ <xsl:value-of select="name()"/><xsl:text>: </xsl:text><xsl:value-of select="@name"/><xsl:text>=</xsl:text><xsl:value-of select="@value"/>
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' topic/table ')]" name="topic.table">
+ <!-- no output -->
+ <xsl:call-template name="output-message">
+ <xsl:with-param name="msg">Eclipse context-sensitive help files do not support tables.</xsl:with-param>
+ <xsl:with-param name="msgnum">066</xsl:with-param>
+ <xsl:with-param name="msgsev">I</xsl:with-param>
+ </xsl:call-template>
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' topic/term ')]" name="topic.term">
+ <xsl:call-template name="checkPreceding" />
+ <xsl:apply-templates />
+ <xsl:call-template name="checkFollowing" />
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' topic/title ')]" name="topic.title">
+ <b><xsl:apply-templates /></b><xsl:text>&#xA;</xsl:text>
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' topic/tm ')]" name="topic.tm">
+ <!-- output nothing -->
+ <!--<xsl:call-template name="output-message">
+ <xsl:with-param name="msg">Trademarks are not required in context-sensitive help.</xsl:with-param>
+ <xsl:with-param name="msgnum">069</xsl:with-param>
+ <xsl:with-param name="msgsev">I</xsl:with-param>
+ </xsl:call-template>-->
+ <xsl:call-template name="checkPreceding" />
+ <xsl:apply-templates />
+ <xsl:call-template name="checkFollowing" />
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' topic/ul ')]" name="topic.ul">
+ <xsl:call-template name="twoPrecedingCRs" />
+ <!-- jta commenting out 11/03/2006 -->
+ <!-- <xsl:if test="parent::*[contains(@class,' topic/li ')] | parent::*[contains(@class,' topic/sli ')]"><xsl:text>&#xA;</xsl:text></xsl:if> -->
+ <xsl:apply-templates />
+ <xsl:call-template name="isNestedTextFollows" />
+ <xsl:call-template name="isFirstChildOfCsbody" />
+ </xsl:template>
+
+
+ <xsl:template match="*[contains(@class,' topic/xref ')]" name="topic.xref" priority="100">
+ <xsl:call-template name="checkPreceding" />
+ <xsl:choose>
+ <xsl:when test="*|text()"><xsl:text>&#34;</xsl:text><xsl:apply-templates select="*|text()"/><xsl:text>&#34;</xsl:text></xsl:when>
+ <xsl:otherwise><xsl:call-template name="href"/></xsl:otherwise>
+ </xsl:choose>
+ <xsl:call-template name="checkFollowing" />
+ </xsl:template>
+
+ <xsl:template name="href">
+ <!-- below adapted from rel-links.xsl -->
+ <xsl:choose>
+ <!-- For non-DITA formats - use the href as is -->
+ <xsl:when test="(not(@format) and (@type='external' or @scope='external')) or (@format and not(@format='dita' or @format='DITA'))">
+ <xsl:value-of select="@href"/>
+ </xsl:when>
+ <!-- For DITA - process the internal href -->
+ <xsl:when test="starts-with(@href,'#')">
+ <xsl:call-template name="parsehref">
+ <xsl:with-param name="href" select="@href"/>
+ </xsl:call-template>
+ </xsl:when>
+ <!-- It's to a DITA file - process the file name (adding the html extension)
+ and process the rest of the href -->
+ <xsl:when test="contains(@href,'.dita')">
+ <xsl:value-of select="substring-before(@href,'.dita')"/>.html<xsl:call-template name="parsehref"><xsl:with-param name="href" select="substring-after(@href,'.dita')"/></xsl:call-template>
+ </xsl:when>
+ <xsl:when test="@href=''"/>
+ <xsl:otherwise>
+ <xsl:call-template name="output-message">
+ <xsl:with-param name="msg">Unknown file extension in href: <xsl:value-of select="@href"/>
+If this is a link to a non-DITA resource, set the format attribute to match the resource (for example, 'txt', 'pdf', or 'html').
+If it's a link to a DITA resource, the file extension must be .dita .</xsl:with-param>
+ <xsl:with-param name="msgnum">015</xsl:with-param>
+ <xsl:with-param name="msgsev">E</xsl:with-param>
+ </xsl:call-template>
+ <xsl:value-of select="@href"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+
+ <!-- "/" is not legal in IDs - need to swap it with two underscores -->
+ <xsl:template name="parsehref">
+ <xsl:param name="href"/>
+ <xsl:choose>
+ <xsl:when test="contains($href,'/')">
+ <xsl:value-of select="substring-before($href,'/')"/>__<xsl:value-of select="substring-after($href,'/')"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="$href"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+
+
+
+
+ <!-- Highlight domain overrides -->
+
+ <xsl:template match="*[contains(@class,' hi-d/b ')]" name="topic.hi-d.b" priority="100">
+ <xsl:call-template name="checkPreceding" />
+ <b><xsl:apply-templates /></b>
+ <xsl:call-template name="checkFollowing" />
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' hi-d/i ')]" name="topic.hi-d.i" priority="100">
+ <xsl:call-template name="checkPreceding" />
+ <b><xsl:apply-templates /></b>
+ <xsl:call-template name="checkFollowing" />
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' hi-d/sub ')]" name="topic.hi-d.sub" priority="100">
+ <xsl:apply-templates />
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' hi-d/sup ')]" name="topic.hi-d.sup" priority="100">
+ <xsl:text>&#94;</xsl:text><xsl:apply-templates /> <!-- adds caret -->
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' hi-d/tt ')]" name="topic.hi-d.tt" priority="100">
+ <xsl:call-template name="checkPreceding" />
+ <xsl:apply-templates />
+ <xsl:call-template name="checkFollowing" />
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' hi-d/u ')]" name="topic.hi-d.u" priority="100">
+ <xsl:call-template name="checkPreceding" />
+ <b><xsl:apply-templates /></b>
+ <xsl:call-template name="checkFollowing" />
+ </xsl:template>
+
+ <!-- Programming domain overrides -->
+
+ <xsl:template match="*[contains(@class,' pr-d/codeph ')]" name="topic.pr-d.codeph" priority="100">
+ <xsl:call-template name="checkPreceding" />
+ <b><xsl:apply-templates /></b>
+ <xsl:call-template name="checkFollowing" />
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' pr-d/kwd ')]" name="topic.pr-d.kwd" priority="100">
+ <xsl:apply-templates />
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' pr-d/var ')]" name="topic.pr-d.var" priority="100">
+ <xsl:apply-templates />
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' pr-d/synph ')]" name="topic.pr-d.synph" priority="100">
+ <xsl:apply-templates />
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' pr-d/oper ')]" name="topic.pr-d.oper" priority="100">
+ <xsl:apply-templates />
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' pr-d/delim ')]" name="topic.pr-d.delim" priority="100">
+ <xsl:apply-templates />
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' pr-d/sep ')]" name="topic.pr-d.sep" priority="100">
+ <xsl:apply-templates />
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' pr-d/repsep ')]" name="topic.pr-d.repsep" priority="100">
+ <xsl:apply-templates />
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' pr-d/option ')]" name="topic.pr-d.option" priority="100">
+ <xsl:apply-templates />
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' pr-d/parmname ')]" name="topic.pr-d.parmname" priority="100">
+ <xsl:call-template name="checkPreceding" />
+ <xsl:apply-templates />
+ <xsl:call-template name="checkFollowing" />
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' pr-d/apiname ')]" name="topic.pr-d.apiname" priority="100">
+ <xsl:call-template name="checkPreceding" />
+ <xsl:apply-templates />
+ <xsl:call-template name="checkFollowing" />
+ </xsl:template>
+
+ <!-- Programming overrides - not found in pr-d.xsl? -->
+
+ <xsl:template match="*[contains(@class,' pr-d/codeblock ')]" name="topic.pr-d.codeblock" priority="100">
+ <!-- same as topic.lines -->
+ <xsl:call-template name="br-replace">
+ <xsl:with-param name="brtext" select="."/>
+ </xsl:call-template>
+ <xsl:call-template name="isFirstChildOfCsbody" />
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' pr-d/parml ')]" name="topic.pr-d.parml" priority="100">
+ <xsl:apply-templates />
+ <xsl:call-template name="isFirstChildOfCsbody" />
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' pr-d/plentry ')]" name="topic.pr-d.plentry" priority="100">
+ <!-- same as topic.dlentry -->
+ <xsl:apply-templates />
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' pr-d/pt ')]" name="topic.pr-d.pt" priority="100">
+ <!-- same as topic.dt -->
+ <b><xsl:apply-templates /></b><xsl:text>&#xA;</xsl:text>
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' pr-d/pd ')]" name="topic.pr-d.pd" priority="100">
+ <!-- same as topic.dd -->
+ <xsl:text> </xsl:text>
+ <xsl:apply-templates />
+ <xsl:if test="position()!=last()">
+ <xsl:text>&#xA;</xsl:text>
+ </xsl:if>
+ </xsl:template>
+
+
+
+ <!-- Syntax diagram (Programming domain) overrides -->
+
+ <xsl:template match="*[contains(@class, ' pr-d/syntaxdiagram ')]" name="topic.pr-d.syntaxdiagram" priority="100">
+ <xsl:call-template name="output-message">
+ <xsl:with-param name="msg">Syntax diagrams are not supported in Eclipse context-sensitive help.</xsl:with-param>
+ <xsl:with-param name="msgnum">066</xsl:with-param>
+ <xsl:with-param name="msgsev">I</xsl:with-param>
+ </xsl:call-template>
+ </xsl:template>
+
+<!-- above template modified; several more syntax diagram templates removed -->
+
+
+
+ <!-- Software domain overrides -->
+
+ <xsl:template match="*[contains(@class,' sw-d/cmdname ')]" name="topic.sw-d.cmdname" priority="100">
+ <xsl:call-template name="checkPreceding" />
+ <b><xsl:apply-templates /></b>
+ <xsl:call-template name="checkFollowing" />
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' sw-d/filepath ')]" name="topic.sw-d.filepath" priority="100">
+ <xsl:call-template name="checkPreceding" />
+ <xsl:apply-templates />
+ <xsl:call-template name="checkFollowing" />
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' sw-d/msgnum ')]" name="topic.sw-d.msgnum" priority="100">
+ <b><xsl:apply-templates /></b>
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' sw-d/msgph ')]" name="topic.sw-d.msgph" priority="100">
+ <b><xsl:apply-templates /></b>
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' sw-d/systemoutput ')]" name="topic.sw-d.systemoutput" priority="100">
+ <xsl:call-template name="checkPreceding" />
+ <b><xsl:apply-templates /></b>
+ <xsl:call-template name="checkFollowing" />
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' sw-d/userinput ')]" name="topic.sw-d.userinput" priority="100">
+ <xsl:call-template name="checkPreceding" />
+ <b><xsl:apply-templates /></b>
+ <xsl:call-template name="checkFollowing" />
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' sw-d/varname ')]" name="topic.sw-d.varname" priority="100">
+ <b><xsl:apply-templates /></b>
+ </xsl:template>
+
+
+
+ <!-- UI domain overrides -->
+
+ <xsl:template match="*[contains(@class,' ui-d/menucascade ')]" name="topic.ui-d.menucascade" priority="100">
+ <xsl:apply-templates />
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' ui-d/screen ')]" name="topic.ui-d.screen" priority="100">
+ <!-- no output -->
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' ui-d/shortcut ')]" name="topic.ui-d.shortcut" priority="100">
+ <b><xsl:apply-templates /></b>
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' ui-d/uicontrol ')]" name="topic.ui-d.uicontrol" priority="100">
+ <xsl:call-template name="checkPreceding" />
+ <xsl:if test="parent::*[contains(@class,' ui-d/menucascade ')] and preceding-sibling::*[contains(@class,' ui-d/uicontrol ')]">
+ <xsl:text> > </xsl:text>
+ </xsl:if>
+ <b><xsl:apply-templates /></b>
+ <xsl:call-template name="checkFollowing" />
+ </xsl:template>
+
+ <xsl:template match="*[contains(@class,' ui-d/wintitle ')]" name="topic.ui-d.wintitle" priority="100">
+ <xsl:call-template name="checkPreceding" />
+ <b><xsl:apply-templates /></b>
+ <xsl:call-template name="checkFollowing" />
+ </xsl:template>
+
+
+
+ <!-- Utilities domain overrides -->
+
+ <!-- imagemap -->
+ <xsl:template match="*[contains(@class,' ut-d/imagemap ')]" name="topic.ut-d.imagemap">
+ <!-- Process the image to get alternate text -->
+ <!--<xsl:call-template name="output-message">
+ <xsl:with-param name="msg">Eclipse context-sensitive help files do not support images.</xsl:with-param>
+ <xsl:with-param name="msgnum">066</xsl:with-param>
+ <xsl:with-param name="msgsev">I</xsl:with-param>
+ </xsl:call-template>-->
+ <xsl:apply-templates select="*[contains(@class,' topic/image ')]"/>
+ </xsl:template>
+
+
+
+ <!-- Miscellaneous templates -->
+
+ <xsl:template match="text()">
+ <xsl:value-of select="normalize-space(.)" />
+ </xsl:template>
+
+ <xsl:template name="commonattributes" />
+ <xsl:template name="setscale" />
+ <xsl:template name="flagit" />
+ <xsl:template name="start-revflag" />
+ <xsl:template name="end-revflag" />
+
+ <xsl:template name="checkPreceding">
+ <xsl:if test="(substring(preceding-sibling::text()[position()=string-length()],1,1)!=' ') and (substring(preceding-sibling::text()[position()=string-length()],1,1)!='(')"><xsl:text> </xsl:text></xsl:if>
+ </xsl:template>
+
+ <xsl:template name="checkFollowing">
+ <xsl:if test="(substring(following-sibling::text()[position()=1],1,1)!='.') and (substring(following-sibling::text()[position()=1],1,1)!=',') and (substring(following-sibling::text()[position()=1],1,1)!=';') and (substring(following-sibling::text()[position()=1],1,1)!=':')"><xsl:text> </xsl:text></xsl:if>
+ </xsl:template>
+
+ <xsl:template name="twoPrecedingCRs">
+ <!-- <xsl:if test="parent::*[contains(@class,' topic/desc ')] | parent::*[contains(@class,' topic/p ')] | parent::*[contains(@class,' topic/note ')] | parent::*[contains(@class,' topic/lq ')] | parent::*[contains(@class,' topic/li ')] | parent::*[contains(@class,' topic/sli ')] | parent::*[contains(@class,' topic/itemgroup ')] | parent::*[contains(@class,' topic/dd ')]"> -->
+ <xsl:choose>
+ <xsl:when test="parent::*[contains(@class,'topic/li ')] | parent::*[contains(@class, 'topic/dd ')] ">
+ <xsl:choose>
+ <xsl:when test="self::*[contains(@class,' topic/ul ')] | self::*[contains(@class,' topic/sl ')] | self::*[contains(@class,' topic/ol ')]">
+ <xsl:text>&#xA;</xsl:text>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:text>&#xA;&#xA;</xsl:text>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:when>
+ <xsl:when test="parent::*[contains(@class,' topic/p ')]">
+ <xsl:if test="preceding-sibling::text()"> <!-- only add 2 cr's if nesting p has text before nested tag -->
+ <xsl:text>&#xA;&#xA;</xsl:text>
+ </xsl:if>
+ </xsl:when>
+ <xsl:when test="not(parent::*[contains(@class,' cshelp/csbody ')])">
+ <xsl:text>&#xA;&#xA;</xsl:text>
+ </xsl:when>
+ <xsl:otherwise>
+ <!-- do nothing -->
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+
+<!-- jta: endOfP no longer called 11/03/2006 -->
+ <xsl:template name="endOfP">
+ <xsl:choose>
+ <xsl:when test="parent::*[contains(@class,' topic/p ')] and position()=last()">
+ <!-- do nothing -->
+ </xsl:when>
+ <xsl:when test="parent::*[contains(@class,' topic/p ')] and position()!=last()">
+ <xsl:text>&#xA;</xsl:text>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:text>&#xA;&#xA;</xsl:text>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+
+<!-- jta: listEndOfP no longer called 11/03/2006 -->
+ <xsl:template name="listEndOfP">
+ <xsl:choose>
+ <!-- jta 10/16/2006 -->
+ <xsl:when test="parent::*[contains(@class,' topic/p ')] | parent::*[contains(@class,' topic/desc ')] | parent::*[contains(@class,' topic/note ')] | parent::*[contains(@class,' topic/lq ')] | parent::*[contains(@class,' topic/li ')] | parent::*[contains(@class,' topic/sli ')] | parent::*[contains(@class,' topic/itemgroup ')] | parent::*[contains(@class,' topic/dd ')]">
+ <!-- do nothing -->
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:text>&#xA;&#xA;</xsl:text>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+
+<!-- jta 10/16/2006 -->
+ <xsl:template name="isFirstChildOfCsbody">
+ <xsl:choose>
+ <xsl:when test="parent::*[contains(@class,' cshelp/csbody ')]">
+ <xsl:if test="following-sibling::*">
+ <xsl:text>&#xA;&#xA;</xsl:text>
+ </xsl:if>
+ </xsl:when>
+ <xsl:when test="parent::*[contains(@class,' topic/dd ')] | parent::*[contains(@class,' topic/li ')]">
+ <xsl:choose>
+ <xsl:when test="self::*[contains(@class,' topic/ol ')] | self::*[contains(@class,' topic/sl ')] | self::*[contains(@class,' topic/ul ')]">
+ <!-- do nothing -->
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:if test="not(following-sibling::*)"><xsl:text>&#xA;</xsl:text></xsl:if>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:when>
+ <xsl:otherwise>
+ <!-- do nothing -->
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+
+<!-- jta 10/19/2006 -->
+ <xsl:template name="indentDL">
+ <xsl:variable name="ddcount" select="count(ancestor::*[contains(@class,' topic/dd ')])"/>
+ <xsl:variable name="licount" select="count(ancestor::*[contains(@class,' topic/li ')])"/>
+ <xsl:variable name="lqcount" select="count(ancestor::*[contains(@class,' topic/lq ')])"/>
+ <xsl:variable name="notecnt" select="count(ancestor::*[contains(@class,' topic/note ')])"/>
+ <xsl:variable name="dd_adj"><xsl:choose><xsl:when test="number($ddcount)>0">1</xsl:when><xsl:otherwise>0</xsl:otherwise></xsl:choose></xsl:variable>
+ <xsl:variable name="li_adj"><xsl:choose><xsl:when test="number($licount)>0">1</xsl:when><xsl:otherwise>0</xsl:otherwise></xsl:choose></xsl:variable>
+ <xsl:variable name="indent" select="number($ddcount) + number($dd_adj) + number($licount) + number($li_adj) + number($lqcount) + number($notecnt)" />
+ <xsl:choose>
+ <xsl:when test="number($indent)=1">
+ <xsl:text> </xsl:text>
+ </xsl:when>
+ <xsl:when test="number($indent)=2">
+ <xsl:text> </xsl:text>
+ </xsl:when>
+ <xsl:when test="number($indent)=3">
+ <xsl:text> </xsl:text>
+ </xsl:when>
+ <xsl:when test="number($indent)=4">
+ <xsl:text> </xsl:text>
+ </xsl:when>
+ <xsl:when test="number($indent)=5">
+ <xsl:text> </xsl:text>
+ </xsl:when>
+ <xsl:when test="number($indent)=6">
+ <xsl:text> </xsl:text>
+ </xsl:when>
+ <xsl:otherwise>
+ <!-- no indent -->
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+
+ <xsl:template name="indentP">
+ <xsl:variable name="ddcount" select="count(ancestor::*[contains(@class,' topic/dd ')])"/>
+ <xsl:variable name="licount" select="count(ancestor::*[contains(@class,' topic/li ')])"/>
+ <xsl:variable name="lqcount" select="count(ancestor::*[contains(@class,' topic/lq ')])"/>
+ <xsl:variable name="notecnt" select="count(ancestor::*[contains(@class,' topic/note ')])"/>
+ <xsl:variable name="p_count" select="count(ancestor::*[contains(@class,' topic/p ')])"/>
+ <!-- <xsl:variable name="dd_adj"><xsl:choose><xsl:when test="number($ddcount)>0">1</xsl:when><xsl:otherwise>0</xsl:otherwise></xsl:choose></xsl:variable> -->
+ <xsl:variable name="li_adj"><xsl:choose><xsl:when test="number($licount)>0">1</xsl:when><xsl:otherwise>0</xsl:otherwise></xsl:choose></xsl:variable>
+ <xsl:variable name="indent" select="number($ddcount) + number($licount) + number($li_adj) + number($lqcount) + number($notecnt) + number($p_count)" />
+ <xsl:choose>
+ <xsl:when test="number($indent)=1">
+ <xsl:text> </xsl:text>
+ </xsl:when>
+ <xsl:when test="number($indent)=2">
+ <xsl:text> </xsl:text>
+ </xsl:when>
+ <xsl:when test="number($indent)=3">
+ <xsl:text> </xsl:text>
+ </xsl:when>
+ <xsl:when test="number($indent)=4">
+ <xsl:text> </xsl:text>
+ </xsl:when>
+ <xsl:when test="number($indent)=5">
+ <xsl:text> </xsl:text>
+ </xsl:when>
+ <xsl:when test="number($indent)=6">
+ <xsl:text> </xsl:text>
+ </xsl:when>
+ <xsl:otherwise>
+ <!-- no indent -->
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+
+ <xsl:template name="indentNote">
+ <xsl:variable name="ddcount" select="count(ancestor::*[contains(@class,' topic/dd ')])"/>
+ <xsl:variable name="licount" select="count(ancestor::*[contains(@class,' topic/li ')])"/>
+ <xsl:variable name="lqcount" select="count(ancestor::*[contains(@class,' topic/lq ')])"/>
+ <xsl:variable name="notecnt" select="count(ancestor::*[contains(@class,' topic/note ')])"/>
+ <xsl:variable name="p_count" select="count(ancestor::*[contains(@class,' topic/p ')])"/>
+ <xsl:variable name="dd_adj"><xsl:choose><xsl:when test="number($ddcount)>0">1</xsl:when><xsl:otherwise>0</xsl:otherwise></xsl:choose></xsl:variable>
+ <xsl:variable name="li_adj"><xsl:choose><xsl:when test="number($licount)>0">1</xsl:when><xsl:otherwise>0</xsl:otherwise></xsl:choose></xsl:variable>
+ <xsl:variable name="indent" select="number($ddcount) + number($dd_adj) + number($licount) + number($li_adj) + number($lqcount) + number($notecnt) + number($p_count)" />
+ <xsl:choose>
+ <xsl:when test="number($indent)=1">
+ <xsl:text> </xsl:text>
+ </xsl:when>
+ <xsl:when test="number($indent)=2">
+ <xsl:text> </xsl:text>
+ </xsl:when>
+ <xsl:when test="number($indent)=3">
+ <xsl:text> </xsl:text>
+ </xsl:when>
+ <xsl:when test="number($indent)=4">
+ <xsl:text> </xsl:text>
+ </xsl:when>
+ <xsl:when test="number($indent)=5">
+ <xsl:text> </xsl:text>
+ </xsl:when>
+ <xsl:when test="number($indent)=6">
+ <xsl:text> </xsl:text>
+ </xsl:when>
+ <xsl:otherwise>
+ <!-- no indent -->
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+
+ <xsl:template name="indentLQ">
+ <xsl:variable name="ddcount" select="count(ancestor::*[contains(@class,' topic/dd ')])"/>
+ <xsl:variable name="licount" select="count(ancestor::*[contains(@class,' topic/li ')])"/>
+ <xsl:variable name="lqcount" select="count(ancestor::*[contains(@class,' topic/lq ')])"/>
+ <xsl:variable name="notecnt" select="count(ancestor::*[contains(@class,' topic/note ')])"/>
+ <xsl:variable name="p_count" select="count(ancestor::*[contains(@class,' topic/p ')])"/>
+ <xsl:variable name="dd_adj"><xsl:choose><xsl:when test="number($ddcount)>0">1</xsl:when><xsl:otherwise>0</xsl:otherwise></xsl:choose></xsl:variable>
+ <xsl:variable name="li_adj"><xsl:choose><xsl:when test="number($licount)>0">1</xsl:when><xsl:otherwise>0</xsl:otherwise></xsl:choose></xsl:variable>
+ <xsl:variable name="indent" select="number($ddcount) + number($dd_adj) + number($licount) + number($li_adj) + number($lqcount) + number($notecnt) + number($p_count)" />
+ <xsl:choose>
+ <xsl:when test="number($indent)=0">
+ <xsl:text> </xsl:text>
+ </xsl:when>
+ <xsl:when test="number($indent)=1">
+ <xsl:text> </xsl:text>
+ </xsl:when>
+ <xsl:when test="number($indent)=2">
+ <xsl:text> </xsl:text>
+ </xsl:when>
+ <xsl:when test="number($indent)=3">
+ <xsl:text> </xsl:text>
+ </xsl:when>
+ <xsl:when test="number($indent)=4">
+ <xsl:text> </xsl:text>
+ </xsl:when>
+ <xsl:when test="number($indent)=5">
+ <xsl:text> </xsl:text>
+ </xsl:when>
+ <xsl:when test="number($indent)=6">
+ <xsl:text> </xsl:text>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:text> </xsl:text>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+
+
+ <xsl:template name="indentLI">
+ <xsl:variable name="ddcount" select="count(ancestor::*[contains(@class,' topic/dd ')])"/>
+ <xsl:variable name="licount" select="count(ancestor::*[contains(@class,' topic/li ')])"/>
+ <xsl:variable name="lqcount" select="count(ancestor::*[contains(@class,' topic/lq ')])"/>
+ <xsl:variable name="notecnt" select="count(ancestor::*[contains(@class,' topic/note ')])"/>
+ <xsl:variable name="p_count" select="count(ancestor::*[contains(@class,' topic/p ')])"/>
+ <xsl:variable name="dd_adj"><xsl:choose><xsl:when test="number($ddcount)>0">1</xsl:when><xsl:otherwise>0</xsl:otherwise></xsl:choose></xsl:variable>
+ <xsl:variable name="li_adj"><xsl:choose><xsl:when test="number($licount)>0">1</xsl:when><xsl:otherwise>0</xsl:otherwise></xsl:choose></xsl:variable>
+ <xsl:variable name="p__adj"><xsl:choose><xsl:when test="number($p_count)>0">-1</xsl:when><xsl:otherwise>0</xsl:otherwise></xsl:choose></xsl:variable>
+ <xsl:variable name="indent" select="number($ddcount) + number($dd_adj) + number($licount) + number($li_adj) + number($lqcount) + number($notecnt) + number($p_count) + number($p__adj)" />
+ <xsl:choose>
+ <xsl:when test="number($indent)=0">
+ <xsl:text> </xsl:text>
+ </xsl:when>
+ <xsl:when test="number($indent)=1">
+ <xsl:text> </xsl:text>
+ </xsl:when>
+ <xsl:when test="number($indent)=2">
+ <xsl:text> </xsl:text>
+ </xsl:when>
+ <xsl:when test="number($indent)=3">
+ <xsl:text> </xsl:text>
+ </xsl:when>
+ <xsl:when test="number($indent)=4">
+ <xsl:text> </xsl:text>
+ </xsl:when>
+ <xsl:when test="number($indent)=5">
+ <xsl:text> </xsl:text>
+ </xsl:when>
+ <xsl:when test="number($indent)=6">
+ <xsl:text> </xsl:text>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:text> </xsl:text>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+
+ <xsl:template name="isNestedTextFollows">
+ <!-- <xsl:if test="parent::*[contains(@class,' topic/dd ')][following-sibling::text()] |
+ parent::*[contains(@class,' topic/lq ')][following-sibling::text()] |
+ parent::*[contains(@class,' topic/li ')][following-sibling::text()] |
+ parent::*[contains(@class,' topic/note ')][following-sibling::text()] |
+ parent::*[contains(@class,' topic/p ')][following-sibling::text()]"> -->
+ <xsl:if test="not(parent::*[contains(@class,' cshelp/csbody ')]) and following-sibling::text()">
+ <xsl:text>&#xA;&#xA;</xsl:text> <!-- jta 11/03/2006: condition when nesting tag has more text -->
+ </xsl:if>
+ </xsl:template>
+
+ <!-- IBM Copyright - English only output, and only when copyright belongs to IBM -->
+
+ <xsl:template name="ibmcopyright">
+ <xsl:variable name="userCopyright">
+ <xsl:value-of select="self::dita/*/*[contains(@class,' topic/prolog ')]/
+ *[contains(@class,' topic/copyright ')]/
+ *[contains(@class,' topic/copyrholder ')] |
+ *[contains(@class,' topic/prolog ')]/
+ *[contains(@class,' topic/copyright ')]/
+ *[contains(@class,' topic/copyrholder ')]"/>
+ </xsl:variable>
+ <xsl:variable name="copyYears">
+ <xsl:value-of select="self::dita/*/*[contains(@class,' topic/prolog ')]/
+ *[contains(@class,' topic/copyright ')]/
+ *[contains(@class,' topic/copyryear ')]/@year |
+ *[contains(@class,' topic/prolog ')]/
+ *[contains(@class,' topic/copyright ')]/
+ *[contains(@class,' topic/copyryear ')]/@year"/>
+ </xsl:variable>
+ <xsl:choose>
+ <xsl:when test="normalize-space($userCopyright)!='' and not(contains($userCopyright,'IBM'))">
+ <!-- P018721: user copyright is specified, and it does not contain IBM; do not put out the comment -->
+ </xsl:when>
+ <xsl:when test="(self::dita/*/*[contains(@class,' topic/prolog ')]/*[contains(@class,' topic/copyright ')] |
+ *[contains(@class,' topic/prolog ')]/*[contains(@class,' topic/copyright ')]) and
+ normalize-space($userCopyright)='' and normalize-space($copyYears)=''">
+ <!-- P018721: if user forces empty copyright, empty year, do not put out the copyright comment -->
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:variable name="childlang">
+ <xsl:choose>
+ <xsl:when test="self::dita">
+ <xsl:for-each select="*[1]"><xsl:call-template name="getLowerCaseLang"/></xsl:for-each>
+ </xsl:when>
+ <xsl:otherwise><xsl:call-template name="getLowerCaseLang"/></xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+ <xsl:choose>
+ <xsl:when test="$childlang='en-us' or $childlang='en'">
+ <xsl:comment> All rights reserved. Licensed Materials Property of IBM </xsl:comment><xsl:value-of select="$newline"/>
+ <xsl:comment> US Government Users Restricted Rights </xsl:comment><xsl:value-of select="$newline"/>
+ <xsl:comment> Use, duplication or disclosure restricted by </xsl:comment><xsl:value-of select="$newline"/>
+ <xsl:comment> GSA ADP Schedule Contract with IBM Corp. </xsl:comment><xsl:value-of select="$newline"/>
+ </xsl:when>
+ <xsl:otherwise/>
+ </xsl:choose>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+
+</xsl:stylesheet>
diff --git a/src/plugins/android.codeutils/.classpath b/src/plugins/android.codeutils/.classpath
new file mode 100644
index 0000000..8a8f166
--- /dev/null
+++ b/src/plugins/android.codeutils/.classpath
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>
+ <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/src/plugins/android.codeutils/.project b/src/plugins/android.codeutils/.project
new file mode 100644
index 0000000..39137c3
--- /dev/null
+++ b/src/plugins/android.codeutils/.project
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>com.motorola.studio.android.codeutils</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.ManifestBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.SchemaBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.pde.PluginNature</nature>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+</projectDescription>
diff --git a/src/plugins/android.codeutils/META-INF/MANIFEST.MF b/src/plugins/android.codeutils/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..9ed6b32
--- /dev/null
+++ b/src/plugins/android.codeutils/META-INF/MANIFEST.MF
@@ -0,0 +1,33 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: %Plugin_Name
+Bundle-SymbolicName: com.motorola.studio.android.codeutils;singleton:=true
+Bundle-Version: 5.0.0.qualifier
+Bundle-Activator: com.motorola.studio.android.codeutils.CodeUtilsActivator
+Bundle-Vendor: %Plugin_Vendor
+Bundle-ActivationPolicy: lazy
+Bundle-RequiredExecutionEnvironment: JavaSE-1.6
+Bundle-Localization: plugin
+Export-Package: com.motorola.studio.android.codeutils.codegeneration,
+ com.motorola.studio.android.codeutils.db.actions,
+ com.motorola.studio.android.codeutils.db.utils,
+ com.motorola.studio.android.codeutils.wizards,
+ com.motorola.studio.android.db.deployment,
+ com.motorola.studio.android.db.wizards.model,
+ com.motorola.studio.android.wizards.buildingblocks
+Bundle-ClassPath: .
+Require-Bundle: org.eclipse.core.runtime,
+ org.eclipse.core.resources,
+ org.eclipse.datatools.connectivity,
+ org.eclipse.datatools.modelbase.sql,
+ org.eclipse.jdt.ui,
+ org.eclipse.ui,
+ org.eclipse.ui.ide,
+ com.motorola.studio.android.common,
+ org.eclipse.jface.text,
+ org.eclipse.datatools.connectivity.sqm.core,
+ org.eclipse.jdt.core,
+ org.apache.xerces,
+ org.eclipse.datatools.sqltools.data.ui,
+ org.eclipse.sequoyah.localization.tools
+Import-Package: org.eclipse.ui.texteditor
diff --git a/src/plugins/android.codeutils/build.properties b/src/plugins/android.codeutils/build.properties
new file mode 100644
index 0000000..a74ce91
--- /dev/null
+++ b/src/plugins/android.codeutils/build.properties
@@ -0,0 +1,9 @@
+source.. = src/
+output.. = bin/
+bin.includes = META-INF/,\
+ .,\
+ plugin.xml,\
+ plugin.properties,\
+ templates/,\
+ icons/,\
+ resources/
diff --git a/src/plugins/android.codeutils/icons/device_refresh_on.png b/src/plugins/android.codeutils/icons/device_refresh_on.png
new file mode 100644
index 0000000..3303876
--- /dev/null
+++ b/src/plugins/android.codeutils/icons/device_refresh_on.png
Binary files differ
diff --git a/src/plugins/android.codeutils/icons/obj16/androidLogo.png b/src/plugins/android.codeutils/icons/obj16/androidLogo.png
new file mode 100644
index 0000000..8336aef
--- /dev/null
+++ b/src/plugins/android.codeutils/icons/obj16/androidLogo.png
Binary files differ
diff --git a/src/plugins/android.codeutils/icons/obj16/android_discussion_16x16.png b/src/plugins/android.codeutils/icons/obj16/android_discussion_16x16.png
new file mode 100644
index 0000000..cc46fb3
--- /dev/null
+++ b/src/plugins/android.codeutils/icons/obj16/android_discussion_16x16.png
Binary files differ
diff --git a/src/plugins/android.codeutils/icons/obj16/fill_code_from_layout_16x16.png b/src/plugins/android.codeutils/icons/obj16/fill_code_from_layout_16x16.png
new file mode 100644
index 0000000..498ce04
--- /dev/null
+++ b/src/plugins/android.codeutils/icons/obj16/fill_code_from_layout_16x16.png
Binary files differ
diff --git a/src/plugins/android.codeutils/icons/obj16/new_activity_template_wiz.png b/src/plugins/android.codeutils/icons/obj16/new_activity_template_wiz.png
new file mode 100644
index 0000000..770bc23
--- /dev/null
+++ b/src/plugins/android.codeutils/icons/obj16/new_activity_template_wiz.png
Binary files differ
diff --git a/src/plugins/android.codeutils/icons/obj16/new_activity_wiz.png b/src/plugins/android.codeutils/icons/obj16/new_activity_wiz.png
new file mode 100644
index 0000000..d6d1e80
--- /dev/null
+++ b/src/plugins/android.codeutils/icons/obj16/new_activity_wiz.png
Binary files differ
diff --git a/src/plugins/android.codeutils/icons/obj16/plate16.png b/src/plugins/android.codeutils/icons/obj16/plate16.png
new file mode 100644
index 0000000..8336aef
--- /dev/null
+++ b/src/plugins/android.codeutils/icons/obj16/plate16.png
Binary files differ
diff --git a/src/plugins/android.codeutils/icons/obj16/provider.png b/src/plugins/android.codeutils/icons/obj16/provider.png
new file mode 100644
index 0000000..a832fad
--- /dev/null
+++ b/src/plugins/android.codeutils/icons/obj16/provider.png
Binary files differ
diff --git a/src/plugins/android.codeutils/icons/obj16/receiver.png b/src/plugins/android.codeutils/icons/obj16/receiver.png
new file mode 100644
index 0000000..f6d2f7f
--- /dev/null
+++ b/src/plugins/android.codeutils/icons/obj16/receiver.png
Binary files differ
diff --git a/src/plugins/android.codeutils/icons/obj16/service_new.gif b/src/plugins/android.codeutils/icons/obj16/service_new.gif
new file mode 100644
index 0000000..2e5ba64
--- /dev/null
+++ b/src/plugins/android.codeutils/icons/obj16/service_new.gif
Binary files differ
diff --git a/src/plugins/android.codeutils/icons/obj16/studio_discussion_16x16.png b/src/plugins/android.codeutils/icons/obj16/studio_discussion_16x16.png
new file mode 100644
index 0000000..4ad83eb
--- /dev/null
+++ b/src/plugins/android.codeutils/icons/obj16/studio_discussion_16x16.png
Binary files differ
diff --git a/src/plugins/android.codeutils/icons/obj16/widget_provider_block_wiz_toolbar.png b/src/plugins/android.codeutils/icons/obj16/widget_provider_block_wiz_toolbar.png
new file mode 100644
index 0000000..f61ba37
--- /dev/null
+++ b/src/plugins/android.codeutils/icons/obj16/widget_provider_block_wiz_toolbar.png
Binary files differ
diff --git a/src/plugins/android.codeutils/icons/views/building_block_explorer_plate.gif b/src/plugins/android.codeutils/icons/views/building_block_explorer_plate.gif
new file mode 100644
index 0000000..e4e6c0a
--- /dev/null
+++ b/src/plugins/android.codeutils/icons/views/building_block_explorer_plate.gif
Binary files differ
diff --git a/src/plugins/android.codeutils/icons/wizban/add_libraries_ban.png b/src/plugins/android.codeutils/icons/wizban/add_libraries_ban.png
new file mode 100644
index 0000000..d9553d4
--- /dev/null
+++ b/src/plugins/android.codeutils/icons/wizban/add_libraries_ban.png
Binary files differ
diff --git a/src/plugins/android.codeutils/icons/wizban/create_management_classes.png b/src/plugins/android.codeutils/icons/wizban/create_management_classes.png
new file mode 100644
index 0000000..f348424
--- /dev/null
+++ b/src/plugins/android.codeutils/icons/wizban/create_management_classes.png
Binary files differ
diff --git a/src/plugins/android.codeutils/icons/wizban/deploy_wizard.png b/src/plugins/android.codeutils/icons/wizban/deploy_wizard.png
new file mode 100644
index 0000000..35877fb
--- /dev/null
+++ b/src/plugins/android.codeutils/icons/wizban/deploy_wizard.png
Binary files differ
diff --git a/src/plugins/android.codeutils/icons/wizban/dump_hprof_wizard.png b/src/plugins/android.codeutils/icons/wizban/dump_hprof_wizard.png
new file mode 100644
index 0000000..139d6be
--- /dev/null
+++ b/src/plugins/android.codeutils/icons/wizban/dump_hprof_wizard.png
Binary files differ
diff --git a/src/plugins/android.codeutils/icons/wizban/fill_activity_ban.png b/src/plugins/android.codeutils/icons/wizban/fill_activity_ban.png
new file mode 100644
index 0000000..f0f80c2
--- /dev/null
+++ b/src/plugins/android.codeutils/icons/wizban/fill_activity_ban.png
Binary files differ
diff --git a/src/plugins/android.codeutils/icons/wizban/monkey_wizard.png b/src/plugins/android.codeutils/icons/wizban/monkey_wizard.png
new file mode 100644
index 0000000..7965073
--- /dev/null
+++ b/src/plugins/android.codeutils/icons/wizban/monkey_wizard.png
Binary files differ
diff --git a/src/plugins/android.codeutils/icons/wizban/new_activity_template_wiz.png b/src/plugins/android.codeutils/icons/wizban/new_activity_template_wiz.png
new file mode 100644
index 0000000..945e6c4
--- /dev/null
+++ b/src/plugins/android.codeutils/icons/wizban/new_activity_template_wiz.png
Binary files differ
diff --git a/src/plugins/android.codeutils/icons/wizban/new_activity_wiz.png b/src/plugins/android.codeutils/icons/wizban/new_activity_wiz.png
new file mode 100644
index 0000000..a0f18a7
--- /dev/null
+++ b/src/plugins/android.codeutils/icons/wizban/new_activity_wiz.png
Binary files differ
diff --git a/src/plugins/android.codeutils/icons/wizban/new_provider_wiz.png b/src/plugins/android.codeutils/icons/wizban/new_provider_wiz.png
new file mode 100644
index 0000000..094c781
--- /dev/null
+++ b/src/plugins/android.codeutils/icons/wizban/new_provider_wiz.png
Binary files differ
diff --git a/src/plugins/android.codeutils/icons/wizban/new_receiver_wiz.png b/src/plugins/android.codeutils/icons/wizban/new_receiver_wiz.png
new file mode 100644
index 0000000..3bafd98
--- /dev/null
+++ b/src/plugins/android.codeutils/icons/wizban/new_receiver_wiz.png
Binary files differ
diff --git a/src/plugins/android.codeutils/icons/wizban/new_service_wiz.png b/src/plugins/android.codeutils/icons/wizban/new_service_wiz.png
new file mode 100644
index 0000000..4634b73
--- /dev/null
+++ b/src/plugins/android.codeutils/icons/wizban/new_service_wiz.png
Binary files differ
diff --git a/src/plugins/android.codeutils/icons/wizban/newprjwiz.png b/src/plugins/android.codeutils/icons/wizban/newprjwiz.png
new file mode 100644
index 0000000..81c1bb1
--- /dev/null
+++ b/src/plugins/android.codeutils/icons/wizban/newprjwiz.png
Binary files differ
diff --git a/src/plugins/android.codeutils/icons/wizban/obfuscate.gif b/src/plugins/android.codeutils/icons/wizban/obfuscate.gif
new file mode 100644
index 0000000..a274919
--- /dev/null
+++ b/src/plugins/android.codeutils/icons/wizban/obfuscate.gif
Binary files differ
diff --git a/src/plugins/android.codeutils/icons/wizban/undeploy_wizard.png b/src/plugins/android.codeutils/icons/wizban/undeploy_wizard.png
new file mode 100644
index 0000000..daa00e0
--- /dev/null
+++ b/src/plugins/android.codeutils/icons/wizban/undeploy_wizard.png
Binary files differ
diff --git a/src/plugins/android.codeutils/icons/wizban/widget_provider_block_wiz.png b/src/plugins/android.codeutils/icons/wizban/widget_provider_block_wiz.png
new file mode 100644
index 0000000..c5501f5
--- /dev/null
+++ b/src/plugins/android.codeutils/icons/wizban/widget_provider_block_wiz.png
Binary files differ
diff --git a/src/plugins/android.codeutils/icons/wizban/widget_provider_prj_wiz.png b/src/plugins/android.codeutils/icons/wizban/widget_provider_prj_wiz.png
new file mode 100644
index 0000000..b4d8a50
--- /dev/null
+++ b/src/plugins/android.codeutils/icons/wizban/widget_provider_prj_wiz.png
Binary files differ
diff --git a/src/plugins/android.codeutils/plugin.properties b/src/plugins/android.codeutils/plugin.properties
new file mode 100644
index 0000000..a37d6e9
--- /dev/null
+++ b/src/plugins/android.codeutils/plugin.properties
@@ -0,0 +1,116 @@
+#
+# Copyright (C) 2012 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+Plugin_Vendor = Motorola Mobility, Inc.
+Plugin_Name = MOTODEV Studio for Android Code Utils Plug-in
+
+android.wizard.activity = Android Activity
+android.wizard.activity.description = Creates a new Activity building block
+android.wizard.activity.template = Android Activity Based on Template
+android.wizard.activity.template.description = Creates a new Activity building block based on a template
+
+android.wizard.activity.list = Android List Activities
+
+android.wizard.activity.imageoptionlist.name = Image option list
+android.wizard.activity.imageoptionlist.description = List with items that may contain an image and text
+
+android.wizard.activity.expandablelist.name = Expandable list
+android.wizard.activity.expandablelist.description = List grouped by sections which can be expanded and collapsed
+
+android.wizard.activity.singlechoicelist.name = Single choice list
+android.wizard.activity.singlechoicelist.description = List containing image and single-choice items (radio buttons)
+
+android.wizard.activity.multiplechoicelist.name = Multiple choice list
+android.wizard.activity.multiplechoicelist.description = List containing image and multiple-choice items (checkboxes)
+
+android.wizard.activity.autoloadlist.name = Auto-load list
+android.wizard.activity.autoloadlist.description = List that loads additional items as needed
+
+android.wizard.activity.treelist.name = Tree list
+android.wizard.activity.treelist.description = List that presents items in a collapsible tree
+
+android.wizard.activity.endlesslist.name = Endless List
+android.wizard.activity.endlesslist.description = List which has no end. Every time its end is about to be reached, more data is retrieved and added to the list.
+
+android.wizard.activity.pulltorefresh.name = Endless List (Pull to Refresh)
+android.wizard.activity.pulltorefresh.description = List which has no end. Pulling down on the list while it is scrolled to the top causes more data to be retrieved and added to the beginning of the list.
+
+android.wizard.receiver = Android Broadcast Receiver
+android.wizard.receiver.description = Creates a new Broadcast Receiver building block
+android.wizard.service = Android Service
+android.wizard.service.description = Creates a new Service building block
+android.wizard.provider = Android Content Provider
+android.wizard.provider.description = Creates a new Content Provider building block
+android.wizard.project.name = Android Project Using Studio for Android
+android.wizard.project.description = Creates a new MOTODEV Studio for Android project
+android.wizard.widget.project.name = Android Widget Project Using Studio for Android
+
+android.wizard.widget.project.description = Creates a new MOTODEV Studio for Android widget project
+
+android.wizard.widget.provider = Android Widget Provider
+
+android.wizard.widget.provider.description = Creates a new Widget Provider building block
+
+android.wizard.category.description = Creates a new MOTODEV Studio for Android project
+
+motodevmenu.autogeneratedcode=Auto-Generated Code
+motodevmenu.autogeneratedcode.activity=Create Activity Based on Template
+
+motodevmenu.new.activity=New Android Activity
+motodevmenu.new.broadcastreceiver=New Android Broadcast Receiver
+motodevmenu.new.contentprovider=New Android Content Provider
+motodevmenu.new.service=New Android Service
+motodevmenu.new.widget.provider=New Android Widget Provider
+
+motodevmenu.localization.openEditor=Open Android Localization Files Editor
+
+Motodev_Studio_Fill_Activity_Based_On_Layout=Generate Java Code Based on Android Layout...
+fill_save_instance_state_command_name=Generate Save UI State Code...
+fill_save_instance_state_command_description=Generates the onSaveInstanceState() method based on those layout items that are handled by the activity
+
+Motodev_Studio_Generate_Menu_Code=Generate Java Code Based on Menu XML Files...
+
+Activity_Samples_DB_name=Database List
+Activity_Samples_DB_description=Displays the contents of a specified database table.
+Activity_Samples_Dialog_name=Dialog
+Activity_Samples_Dialog_description=A dialog containing a list of choices.
+Activity_Samples_DDList_name=Drop Down List
+Activity_Samples_DDList_description=A drop down list.
+Activity_Samples_Multitouch_name=Multitouch Event
+Activity_Samples_Multitouch_description=Enables the user to move or resize an image using multitouch.
+Activity_Samples_Preference_name=Preference Activity
+Activity_Samples_Preference_description=An XML-based preferences activity.
+Activity_Samples_RadioButton_name=Radio Button
+Activity_Samples_RadioButton_description=Radio buttons.
+Activity_Samples_SelectionList_name=Selection List
+Activity_Samples_SelectionList_description=A selection list that can be configured to allow for one or multiple choices.
+Activity_Samples_SimpleList_name=Simple List
+Activity_Samples_SimpleList_description=A simple list of strings.
+Activity_Samples_Tabs_name=Tabs for versions of Android versions prior to 3.2
+Activity_Samples_Tabs_description=An activity containing tabs that are created as they are selected.\n\nNotes:\n\n - The API used in this template is deprecated as of Android 3.2.\n - New applications should use fragments.
+Activity_Samples_ListActivities_name=List Activities
+Activity_Samples_ListActivities_description=Set of templates involving many list layouts.
+Activity_Samples_DashboardPattern_name=Dashboard Pattern
+Activity_Samples_DashboardPattern_description=A landing page for your application with large icons representing its most important features.
+Activity_Samples_ActionBar_name=Action Bar for Android 3.0 and later
+Activity_Samples_ActionBar_description=An activity that uses the Action Bar UI design pattern.
+Activity_Samples_ActionBarCompatibility_name=Action Bar for versions of Android prior to 3.0
+Activity_Samples_ActionBarCompatibility_description=An activity that uses the Action Bar UI design pattern.
+Activity_Samples_QuickAction_name=Quick Action Menu
+Activity_Samples_QuickAction_description=An activity that uses the Quick Action UI design pattern.
+
+android.wizard.activity.endlesslist.usingfragment.name = Endless List (using fragment - requires API level 11 or higher)
+android.wizard.activity.endlesslist.usingfragment.description = ListFragment which has no end. Every time the end of the list is about to be reached, more data is retrieved and added to the list. \ No newline at end of file
diff --git a/src/plugins/android.codeutils/plugin.xml b/src/plugins/android.codeutils/plugin.xml
new file mode 100644
index 0000000..4edeeff
--- /dev/null
+++ b/src/plugins/android.codeutils/plugin.xml
@@ -0,0 +1,692 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?eclipse version="3.2"?>
+
+<!--
+ Copyright (C) 2012 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<plugin>
+ <extension-point id="com.motorola.studio.android.codeutils.sampleActivityDatabase" name="Add Android Sample Activity Page Based on Database" schema="schema/com.motorola.studio.android.sampleActivityDatabase.exsd"/>
+ <extension
+ point="org.eclipse.ui.newWizards">
+ <wizard
+ canFinishEarly="false"
+ category="com.android.ide.eclipse.wizards.category"
+ class="com.motorola.studio.android.wizards.buildingblocks.NewActivityWizard"
+ finalPerspective="com.motorola.studio.android.perspective"
+ hasPages="true"
+ icon="icons/obj16/new_activity_wiz.png"
+ id="com.motorola.studio.android.wizards.newActivityWizard"
+ name="%android.wizard.activity"
+ preferredPerspectives="com.motorola.studio.android.perspective, org.eclipse.jdt.ui.JavaPerspective"
+ project="false">
+ <description>
+ %android.wizard.activity.description
+ </description>
+ </wizard>
+ <wizard
+ canFinishEarly="false"
+ category="com.android.ide.eclipse.wizards.category"
+ class="com.motorola.studio.android.wizards.buildingblocks.NewActivityBasedOnTemplateWizard"
+ finalPerspective="com.motorola.studio.android.perspective"
+ hasPages="true"
+ icon="icons/obj16/new_activity_template_wiz.png"
+ id="com.motorola.studio.android.wizards.newActivityBasedOnTemplateWizard"
+ name="%android.wizard.activity.template"
+ preferredPerspectives="com.motorola.studio.android.perspective, org.eclipse.jdt.ui.JavaPerspective"
+ project="false">
+ <description>
+ %android.wizard.activity.template.description
+ </description>
+ </wizard>
+ <wizard
+ canFinishEarly="false"
+ category="com.android.ide.eclipse.wizards.category"
+ class="com.motorola.studio.android.wizards.buildingblocks.NewReceiverWizard"
+ finalPerspective="com.motorola.studio.android.perspective"
+ hasPages="true"
+ icon="icons/obj16/receiver.png"
+ id="com.motorola.studio.android.wizards.newReceiverWizard"
+ name="%android.wizard.receiver"
+ preferredPerspectives="com.motorola.studio.android.perspective, org.eclipse.jdt.ui.JavaPerspective"
+ project="false">
+ <description>
+ %android.wizard.receiver.description
+ </description>
+ </wizard>
+ <wizard
+ canFinishEarly="false"
+ category="com.android.ide.eclipse.wizards.category"
+ class="com.motorola.studio.android.wizards.buildingblocks.NewServiceWizard"
+ finalPerspective="com.motorola.studio.android.perspective"
+ hasPages="true"
+ icon="icons/obj16/service_new.gif"
+ id="com.motorola.studio.android.wizards.newServiceWizard"
+ name="%android.wizard.service"
+ preferredPerspectives="com.motorola.studio.android.perspective, org.eclipse.jdt.ui.JavaPerspective"
+ project="false">
+ <description>
+ %android.wizard.service.description
+ </description>
+ </wizard>
+ <wizard
+ canFinishEarly="false"
+ category="com.android.ide.eclipse.wizards.category"
+ class="com.motorola.studio.android.wizards.buildingblocks.NewProviderWizard"
+ finalPerspective="com.motorola.studio.android.perspective"
+ hasPages="true"
+ icon="icons/obj16/provider.png"
+ id="com.motorola.studio.android.wizards.newProviderWizard"
+ name="%android.wizard.provider"
+ preferredPerspectives="com.motorola.studio.android.perspective, org.eclipse.jdt.ui.JavaPerspective"
+ project="false">
+ <description>
+ %android.wizard.provider.description
+ </description>
+ </wizard>
+ <wizard
+ canFinishEarly="false"
+ category="com.android.ide.eclipse.wizards.category"
+ class="com.motorola.studio.android.wizards.buildingblocks.NewWidgetProviderWizard"
+ finalPerspective="com.motorola.studio.android.perspective"
+ hasPages="true"
+ icon="icons/obj16/widget_provider_block_wiz_toolbar.png"
+ id="com.motorola.studio.android.wizard.newWidgetProviderWizard"
+ name="%android.wizard.widget.provider"
+ preferredPerspectives="com.motorola.studio.android.perspective, org.eclipse.jdt.ui.JavaPerspective"
+ project="false">
+ <description>
+ %android.wizard.widget.provider.description
+ </description>
+ </wizard>
+ </extension>
+ <extension
+ point="org.eclipse.ui.commands">
+ <command
+ defaultHandler="com.motorola.studio.android.command.NewActivityWizard"
+ id="com.motorola.studio.android.new.activity"
+ name="%motodevmenu.new.activity">
+ </command>
+ <command
+ defaultHandler="com.motorola.studio.android.command.NewActivityBasedOnTemplateHandler"
+ id="com.motorola.studio.android.new.activity.template"
+ name="%motodevmenu.autogeneratedcode.activity">
+ </command>
+ <command
+ defaultHandler="com.motorola.studio.android.command.NewBroadcastReceiverWizard"
+ id="com.motorola.studio.android.new.broadcast.receiver"
+ name="%motodevmenu.new.broadcastreceiver">
+ </command>
+ <command
+ defaultHandler="com.motorola.studio.android.command.NewContentProviderWizard"
+ id="com.motorola.studio.android.new.content.provider"
+ name="%motodevmenu.new.contentprovider">
+ </command>
+ <command
+ defaultHandler="com.motorola.studio.android.command.NewServiceWizard"
+ id="com.motorola.studio.android.new.service"
+ name="%motodevmenu.new.service">
+ </command>
+ <command
+ defaultHandler="com.motorola.studio.android.command.NewWidgetProviderWizard"
+ id="com.motorola.studio.android.new.widget.provider"
+ name="%motodevmenu.new.widget.provider">
+ </command>
+ </extension>
+ <extension
+ point="org.eclipse.ui.menus">
+ <menuContribution
+ allPopups="true"
+ locationURI="popup:org.eclipse.jdt.ui.source.menu?endof=externalizeGroup">
+ <separator
+ name="com.motorola.studio.android.fillcodeseparator1"
+ visible="true">
+ </separator>
+ <command
+ commandId="com.motorola.studio.android.fillActivityBasedOnLayout"
+ icon="icons/obj16/fill_code_from_layout_16x16.png"
+ label="%Motodev_Studio_Fill_Activity_Based_On_Layout"
+ style="push">
+ <visibleWhen
+ checkEnabled="false">
+ <and>
+ <count
+ value="1">
+ </count>
+ <or>
+ <iterate
+ ifEmpty="false"
+ operator="and">
+ <adapt
+ type="org.eclipse.core.resources.IResource">
+ <test
+ forcePluginActivation="true"
+ property="org.eclipse.core.resources.extension"
+ value="java">
+ </test>
+ </adapt>
+ </iterate>
+ <iterate
+ ifEmpty="false"
+ operator="and">
+ <adapt
+ type="org.eclipse.core.resources.IProject">
+ <test
+ property="org.eclipse.core.resources.open">
+ </test>
+ <test
+ forcePluginActivation="true"
+ property="org.eclipse.core.resources.projectNature"
+ value="com.android.ide.eclipse.adt.AndroidNature">
+ </test>
+ </adapt>
+ </iterate>
+ <iterate>
+ <adapt
+ type="org.eclipse.jface.text.TextSelection">
+ </adapt>
+ </iterate>
+ </or>
+ </and>
+ </visibleWhen>
+ </command>
+ <command
+ commandId="com.motorola.studio.android.fillSaveInstanceState"
+ label="%fill_save_instance_state_command_name"
+ style="push"
+ tooltip="%fill_save_instance_state_command_description">
+ <visibleWhen
+ checkEnabled="false">
+ <and>
+ <count
+ value="1">
+ </count>
+ <or>
+ <iterate
+ ifEmpty="false"
+ operator="and">
+ <adapt
+ type="org.eclipse.core.resources.IResource">
+ <test
+ forcePluginActivation="true"
+ property="org.eclipse.core.resources.extension"
+ value="java">
+ </test>
+ </adapt>
+ </iterate>
+ <iterate
+ ifEmpty="false"
+ operator="and">
+ <adapt
+ type="org.eclipse.core.resources.IProject">
+ <test
+ property="org.eclipse.core.resources.open">
+ </test>
+ <test
+ forcePluginActivation="true"
+ property="org.eclipse.core.resources.projectNature"
+ value="com.android.ide.eclipse.adt.AndroidNature">
+ </test>
+ </adapt>
+ </iterate>
+ <iterate>
+ <adapt
+ type="org.eclipse.jface.text.TextSelection">
+ </adapt>
+ </iterate>
+ </or>
+ </and>
+ </visibleWhen>
+ </command>
+ <command
+ commandId="com.motorola.studio.android.GenerateMenuCode"
+ label="%Motodev_Studio_Generate_Menu_Code"
+ style="push">
+ <visibleWhen
+ checkEnabled="false">
+ <and>
+ <count
+ value="1">
+ </count>
+ <or>
+ <iterate
+ ifEmpty="false"
+ operator="and">
+ <adapt
+ type="org.eclipse.core.resources.IResource">
+ <test
+ forcePluginActivation="true"
+ property="org.eclipse.core.resources.extension"
+ value="java">
+ </test>
+ </adapt>
+ </iterate>
+ <iterate
+ ifEmpty="false"
+ operator="and">
+ <adapt
+ type="org.eclipse.core.resources.IProject">
+ <test
+ property="org.eclipse.core.resources.open">
+ </test>
+ <test
+ forcePluginActivation="true"
+ property="org.eclipse.core.resources.projectNature"
+ value="com.android.ide.eclipse.adt.AndroidNature">
+ </test>
+ </adapt>
+ </iterate>
+ <iterate>
+ <adapt
+ type="org.eclipse.jface.text.TextSelection">
+ </adapt>
+ </iterate>
+ </or>
+ </and>
+ </visibleWhen>
+ </command>
+ </menuContribution>
+ <menuContribution
+ allPopups="false"
+ locationURI="menu:studioAndroidAutoGenerateCode">
+ <command
+ commandId="com.motorola.studio.android.fillActivityBasedOnLayout"
+ icon="icons/obj16/fill_code_from_layout_16x16.png"
+ label="%Motodev_Studio_Fill_Activity_Based_On_Layout"
+ style="push">
+ </command>
+ <command
+ commandId="com.motorola.studio.android.fillSaveInstanceState"
+ label="%fill_save_instance_state_command_name"
+ style="push">
+ </command>
+ <command
+ commandId="com.motorola.studio.android.GenerateMenuCode"
+ label="%Motodev_Studio_Generate_Menu_Code"
+ style="push">
+ </command>
+ </menuContribution>
+ <menuContribution
+ locationURI="menu:studioAndroidNewWizardsMenu">
+ <separator
+ name="com.motorola.studio.android.afterNewProjectSeparator"
+ visible="true">
+ </separator>
+ <command
+ commandId="com.motorola.studio.android.new.activity"
+ icon="icons/obj16/new_activity_wiz.png"
+ label="%motodevmenu.new.activity"
+ style="push">
+ </command>
+ <command
+ commandId="com.motorola.studio.android.new.broadcast.receiver"
+ icon="icons/obj16/receiver.png"
+ label="%motodevmenu.new.broadcastreceiver"
+ style="push">
+ </command>
+ <command
+ commandId="com.motorola.studio.android.new.content.provider"
+ icon="icons/obj16/provider.png"
+ label="%motodevmenu.new.contentprovider"
+ style="push">
+ </command>
+ <command
+ commandId="com.motorola.studio.android.new.service"
+ icon="icons/obj16/service_new.gif"
+ label="%motodevmenu.new.service"
+ style="push">
+ </command>
+ <command
+ commandId="com.motorola.studio.android.new.widget.provider"
+ icon="icons/obj16/widget_provider_block_wiz_toolbar.png"
+ label="%motodevmenu.new.widget.provider"
+ style="push">
+ </command>
+ </menuContribution>
+ </extension>
+ <extension
+ point="org.eclipse.ui.commands">
+ <command
+ defaultHandler="com.motorola.studio.android.generateviewbylayout.ui.GenerateViewBasedOnLayoutHandler"
+ id="com.motorola.studio.android.fillActivityBasedOnLayout"
+ name="%Motodev_Studio_Fill_Activity_Based_On_Layout">
+ </command>
+ <command
+ defaultHandler="com.motorola.studio.android.generateviewbylayout.ui.FillOnSaveInstanceStateHandler"
+ description="%fill_save_instance_state_command_description"
+ id="com.motorola.studio.android.fillSaveInstanceState"
+ name="%fill_save_instance_state_command_name">
+ </command>
+ <command
+ defaultHandler="com.motorola.studio.android.generatemenucode.ui.GenerateMenuCodeHandler"
+ id="com.motorola.studio.android.GenerateMenuCode"
+ name="%Motodev_Studio_Generate_Menu_Code">
+ </command>
+ </extension>
+ <extension
+ point="org.eclipse.ui.handlers">
+ <handler
+ class="com.motorola.studio.android.generateviewbylayout.ui.GenerateViewBasedOnLayoutHandler"
+ commandId="com.motorola.studio.android.fillActivityBasedOnLayout">
+ <enabledWhen>
+ <or>
+ <and>
+ <count
+ value="1">
+ </count>
+ <or>
+ <iterate
+ ifEmpty="false"
+ operator="and">
+ <adapt
+ type="org.eclipse.core.resources.IResource">
+ <test
+ forcePluginActivation="true"
+ property="org.eclipse.core.resources.extension"
+ value="java">
+ </test>
+ </adapt>
+ </iterate>
+ <iterate
+ ifEmpty="false"
+ operator="and">
+ <adapt
+ type="org.eclipse.core.resources.IProject">
+ <test
+ property="org.eclipse.core.resources.open">
+ </test>
+ <test
+ forcePluginActivation="true"
+ property="org.eclipse.core.resources.projectNature"
+ value="com.android.ide.eclipse.adt.AndroidNature">
+ </test>
+ </adapt>
+ </iterate>
+ </or>
+ </and>
+ <and>
+ <with
+ variable="activeEditorId">
+ <equals
+ value="org.eclipse.jdt.ui.CompilationUnitEditor">
+ </equals>
+ </with>
+ </and>
+ </or></enabledWhen>
+ <activeWhen>
+ <with
+ variable="activeWorkbenchWindow">
+ <instanceof
+ value="org.eclipse.ui.IWorkbenchWindow">
+ </instanceof>
+ </with></activeWhen>
+ </handler>
+ <handler
+ class="com.motorola.studio.android.generateviewbylayout.ui.FillOnSaveInstanceStateHandler"
+ commandId="com.motorola.studio.android.fillSaveInstanceState">
+ <activeWhen>
+ <with
+ variable="activeWorkbenchWindow">
+ <instanceof
+ value="org.eclipse.ui.IWorkbenchWindow">
+ </instanceof>
+ </with>
+ </activeWhen>
+ <enabledWhen>
+ <or>
+ <and>
+ <count
+ value="1">
+ </count>
+ <or>
+ <iterate
+ ifEmpty="false"
+ operator="and">
+ <adapt
+ type="org.eclipse.core.resources.IResource">
+ <test
+ forcePluginActivation="true"
+ property="org.eclipse.core.resources.extension"
+ value="java">
+ </test>
+ </adapt>
+ </iterate>
+ <iterate
+ ifEmpty="false"
+ operator="and">
+ <adapt
+ type="org.eclipse.core.resources.IProject">
+ <test
+ property="org.eclipse.core.resources.open">
+ </test>
+ <test
+ forcePluginActivation="true"
+ property="org.eclipse.core.resources.projectNature"
+ value="com.android.ide.eclipse.adt.AndroidNature">
+ </test>
+ </adapt>
+ </iterate>
+ </or>
+ </and>
+ <and>
+ <with
+ variable="activeEditorId">
+ <equals
+ value="org.eclipse.jdt.ui.CompilationUnitEditor">
+ </equals>
+ </with>
+ </and>
+ </or>
+ </enabledWhen>
+ </handler>
+ <handler
+ class="com.motorola.studio.android.generatemenucode.ui.GenerateMenuCodeHandler"
+ commandId="com.motorola.studio.android.GenerateMenuCode">
+ <enabledWhen>
+ <or>
+ <and>
+ <count
+ value="1">
+ </count>
+ <or>
+ <iterate
+ ifEmpty="false"
+ operator="and">
+ <adapt
+ type="org.eclipse.core.resources.IResource">
+ <test
+ forcePluginActivation="true"
+ property="org.eclipse.core.resources.extension"
+ value="java">
+ </test>
+ </adapt>
+ </iterate>
+ <iterate
+ ifEmpty="false"
+ operator="and">
+ <adapt
+ type="org.eclipse.core.resources.IProject">
+ <test
+ property="org.eclipse.core.resources.open">
+ </test>
+ <test
+ forcePluginActivation="true"
+ property="org.eclipse.core.resources.projectNature"
+ value="com.android.ide.eclipse.adt.AndroidNature">
+ </test>
+ </adapt>
+ </iterate>
+ </or>
+ </and>
+ <and>
+ <with
+ variable="activeEditorId">
+ <equals
+ value="org.eclipse.jdt.ui.CompilationUnitEditor">
+ </equals>
+ </with>
+ </and>
+ </or></enabledWhen>
+ <activeWhen>
+ <with
+ variable="activeWorkbenchWindow">
+ <instanceof
+ value="org.eclipse.ui.IWorkbenchWindow">
+ </instanceof>
+ </with></activeWhen>
+ </handler>
+ </extension>
+ <extension
+ point="com.motorola.studio.android.codeutils.sampleActivityDatabase">
+ <parameterCollector
+ class="com.motorola.studio.android.codeutils.codegeneration.DatabaseListActivityGeneratorByTable">
+ </parameterCollector>
+ </extension>
+ <extension
+ point="org.eclipse.ui.menus">
+ <menuContribution
+ allPopups="false"
+ locationURI="menu:motorolaMenu?after=autoGenerateCodeSeparator">
+ <menu
+ id="studioAndroidAutoGenerateCode"
+ label="%motodevmenu.autogeneratedcode">
+ <command
+ commandId="com.motorola.studio.android.new.activity.template"
+ icon="icons/obj16/new_activity_template_wiz.png"
+ label="%motodevmenu.autogeneratedcode.activity"
+ style="push">
+ </command>
+ </menu>
+ </menuContribution>
+ </extension>
+ <extension
+ point="org.eclipse.ui.perspectiveExtensions">
+ <perspectiveExtension
+ targetID="org.eclipse.jdt.ui.JavaPerspective">
+ <newWizardShortcut
+ id="com.motorola.studio.android.wizards.newActivityWizard">
+ </newWizardShortcut>
+ <newWizardShortcut
+ id="com.motorola.studio.android.wizards.newActivityBasedOnTemplateWizard">
+ </newWizardShortcut>
+ <newWizardShortcut
+ id="com.motorola.studio.android.wizards.newReceiverWizard">
+ </newWizardShortcut>
+ <newWizardShortcut
+ id="com.motorola.studio.android.wizards.newServiceWizard">
+ </newWizardShortcut>
+ <newWizardShortcut
+ id="com.motorola.studio.android.wizards.newProviderWizard">
+ </newWizardShortcut>
+ <newWizardShortcut
+ id="com.motorola.studio.android.wizard.newWidgetProviderWizard">
+ </newWizardShortcut>
+ </perspectiveExtension>
+ <perspectiveExtension
+ targetID="org.eclipse.sequoyah.android.cdt.build.ui.perspective">
+ <newWizardShortcut
+ id="com.motorola.studio.android.wizards.newActivityWizard">
+ </newWizardShortcut>
+ <newWizardShortcut
+ id="com.motorola.studio.android.wizards.newActivityBasedOnTemplateWizard">
+ </newWizardShortcut>
+ <newWizardShortcut
+ id="com.motorola.studio.android.wizards.newReceiverWizard">
+ </newWizardShortcut>
+ <newWizardShortcut
+ id="com.motorola.studio.android.wizards.newServiceWizard">
+ </newWizardShortcut>
+ <newWizardShortcut
+ id="com.motorola.studio.android.wizards.newProviderWizard">
+ </newWizardShortcut>
+ <newWizardShortcut
+ id="com.motorola.studio.android.wizard.newWidgetProviderWizard">
+ </newWizardShortcut>
+ </perspectiveExtension>
+ </extension>
+ <extension
+ point="org.eclipse.ui.navigator.navigatorContent">
+ <commonWizard
+ menuGroupId="2newAndroidBuildingBlock"
+ type="new"
+ wizardId="com.motorola.studio.android.wizards.newActivityWizard">
+ <enablement>
+ <adapt
+ type="org.eclipse.core.resources.IProject">
+ <test
+ property="org.eclipse.core.resources.projectNature"
+ value="com.android.ide.eclipse.adt.AndroidNature">
+ </test>
+ </adapt>
+ </enablement>
+ </commonWizard>
+ <commonWizard
+ menuGroupId="2newAndroidBuildingBlock"
+ type="new"
+ wizardId="com.motorola.studio.android.wizards.newActivityBasedOnTemplateWizard">
+ <enablement>
+ <adapt
+ type="org.eclipse.core.resources.IProject">
+ <test
+ property="org.eclipse.core.resources.projectNature"
+ value="com.android.ide.eclipse.adt.AndroidNature">
+ </test>
+ </adapt>
+ </enablement>
+ </commonWizard>
+ <commonWizard
+ menuGroupId="2newAndroidBuildingBlock"
+ type="new"
+ wizardId="com.motorola.studio.android.wizards.newReceiverWizard">
+ <enablement>
+ <adapt
+ type="org.eclipse.core.resources.IProject">
+ <test
+ property="org.eclipse.core.resources.projectNature"
+ value="com.android.ide.eclipse.adt.AndroidNature">
+ </test>
+ </adapt>
+ </enablement>
+ </commonWizard>
+ <commonWizard
+ menuGroupId="2newAndroidBuildingBlock"
+ type="new"
+ wizardId="com.motorola.studio.android.wizards.newServiceWizard">
+ <enablement>
+ <adapt
+ type="org.eclipse.core.resources.IProject">
+ <test
+ property="org.eclipse.core.resources.projectNature"
+ value="com.android.ide.eclipse.adt.AndroidNature">
+ </test>
+ </adapt>
+ </enablement>
+ </commonWizard>
+ <commonWizard
+ menuGroupId="2newAndroidBuildingBlock"
+ type="new"
+ wizardId="com.motorola.studio.android.wizards.newProviderWizard">
+ <enablement>
+ <adapt
+ type="org.eclipse.core.resources.IProject">
+ <test
+ property="org.eclipse.core.resources.projectNature"
+ value="com.android.ide.eclipse.adt.AndroidNature">
+ </test>
+ </adapt>
+ </enablement>
+ </commonWizard>
+ </extension>
+</plugin>
diff --git a/src/plugins/android.codeutils/resources/databaseDeploy/ContentProviderByTablejava.txt b/src/plugins/android.codeutils/resources/databaseDeploy/ContentProviderByTablejava.txt
new file mode 100644
index 0000000..ce5a3e2
--- /dev/null
+++ b/src/plugins/android.codeutils/resources/databaseDeploy/ContentProviderByTablejava.txt
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package #packageName#;
+
+import java.util.*;
+
+import android.content.*;
+import android.database.*;
+import android.database.sqlite.*;
+import android.net.*;
+import android.text.*;
+
+import #databaseOpenHelperPackageName#.*;
+
+public class #className# extends ContentProvider {
+
+ private #databaseOpenHelperClassName# dbHelper;
+ private static HashMap<String, String> #tableNameUpperCase#_PROJECTION_MAP;
+ private static final String TABLE_NAME = "#tableNameLowerCase#";
+ private static final String AUTHORITY = "#authority#";
+
+ public static final Uri CONTENT_URI = Uri.parse("content://"+AUTHORITY+"/"+TABLE_NAME);
+ #uriConstants#
+
+ #defaultSortOrder#
+
+ private static final UriMatcher URL_MATCHER;
+
+ #constIndexesProjectMap#
+
+ //Content values keys (using column names)
+ #constContentValuesKeys#
+
+ public boolean onCreate() {
+ dbHelper = new #databaseOpenHelperClassName#(getContext(),true);
+ return (dbHelper == null) ? false : true;
+ }
+
+
+ public Cursor query(Uri url, String[] projection, String selection, String[] selectionArgs, String sort) {
+ SQLiteDatabase mDB = dbHelper.getReadableDatabase();
+ SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
+ switch (URL_MATCHER.match(url)) {
+ #queryUrlCases#
+ default:
+ throw new IllegalArgumentException("Unknown URL " + url);
+ }
+ String orderBy = "";
+ if (TextUtils.isEmpty(sort)) {
+ orderBy = DEFAULT_SORT_ORDER;
+ } else {
+ orderBy = sort;
+ }
+ Cursor c = qb.query(mDB, projection, selection, selectionArgs, null, null, orderBy);
+ c.setNotificationUri(getContext().getContentResolver(), url);
+ return c;
+ }
+
+ public String getType(Uri url) {
+ switch (URL_MATCHER.match(url)) {
+ #typeRecordCases#
+ default:
+ throw new IllegalArgumentException("Unknown URL " + url);
+ }
+ }
+
+ public Uri insert(Uri url, ContentValues initialValues) {
+ SQLiteDatabase mDB = dbHelper.getWritableDatabase();
+ long rowID;
+ ContentValues values;
+ if (initialValues != null) {
+ values = new ContentValues(initialValues);
+ } else {
+ values = new ContentValues();
+ }
+ if (URL_MATCHER.match(url) != #tableNameUpperCase#) {
+ throw new IllegalArgumentException("Unknown URL " + url);
+ }
+
+ #insertStatementCases#
+
+ rowID = mDB.insert("#tableNameLowerCase#", "#tableNameLowerCase#", values);
+ if (rowID > 0) {
+ Uri uri = ContentUris.withAppendedId(CONTENT_URI, rowID);
+ getContext().getContentResolver().notifyChange(uri, null);
+ return uri;
+ }
+ throw new SQLException("Failed to insert row into " + url);
+ }
+
+ public int delete(Uri url, String where, String[] whereArgs) {
+ SQLiteDatabase mDB = dbHelper.getWritableDatabase();
+ int count;
+ String segment = "";
+ switch (URL_MATCHER.match(url)) {
+ #deleteStatementCases#
+ default:
+ throw new IllegalArgumentException("Unknown URL " + url);
+ }
+ getContext().getContentResolver().notifyChange(url, null);
+ return count;
+ }
+
+ public int update(Uri url, ContentValues values, String where, String[] whereArgs) {
+ SQLiteDatabase mDB = dbHelper.getWritableDatabase();
+ int count;
+ String segment = "";
+ switch (URL_MATCHER.match(url)) {
+ #updateStatementCases#
+ default:
+ throw new IllegalArgumentException("Unknown URL " + url);
+ }
+ getContext().getContentResolver().notifyChange(url, null);
+ return count;
+ }
+
+ static {
+ URL_MATCHER = new UriMatcher(UriMatcher.NO_MATCH);
+ #UrlMatcherStatementCases#
+
+ #tableNameUpperCase#_PROJECTION_MAP = new HashMap<String, String>();
+ #ProjectionMapStatementCases#
+ }
+} \ No newline at end of file
diff --git a/src/plugins/android.codeutils/resources/databaseDeploy/ContentProviderHelperjava.txt b/src/plugins/android.codeutils/resources/databaseDeploy/ContentProviderHelperjava.txt
new file mode 100644
index 0000000..ef24976
--- /dev/null
+++ b/src/plugins/android.codeutils/resources/databaseDeploy/ContentProviderHelperjava.txt
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package #packageName#;
+
+import java.io.IOException;
+
+import android.content.ContentProvider;
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.database.SQLException;
+import android.net.Uri;
+
+public class #className# extends ContentProvider {
+ public static final Uri CONTENT_URI = Uri.parse("content://#authority#");
+
+ /**
+ * @see android.content.ContentProvider#delete(Uri,String,String[])
+ */
+ public int delete(Uri uri, String selection, String[] selectionArgs) {
+ // TODO Put your code here
+ return 0;
+ }
+
+ /**
+ * @see android.content.ContentProvider#getType(Uri)
+ */
+ public String getType(Uri uri) {
+ // TODO Put your code here
+ return null;
+ }
+
+ /**
+ * @see android.content.ContentProvider#insert(Uri,ContentValues)
+ */
+ public Uri insert(Uri uri, ContentValues values) {
+ // TODO Put your code here
+ return null;
+ }
+
+ public Cursor query(Uri uri, String[] projection, String selection,
+ String[] selectionArgs, String sortOrder) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ public int update(Uri uri, ContentValues values, String selection,
+ String[] selectionArgs) {
+ // TODO Auto-generated method stub
+ return 0;
+ }
+
+ /**
+ * @see android.content.ContentProvider#onCreate()
+ */
+ public boolean onCreate() {
+ #dbName#DbDatabaseHelper myDbHelper = new #dbName#DbDatabaseHelper(getContext());
+ try {
+ myDbHelper.createDataBase();
+ } catch (IOException ioe) {
+ throw new Error("Unable to create database");
+ }
+ try {
+ myDbHelper.openDataBase();
+ } catch (SQLException sqle) {
+ throw sqle;
+ }
+ return false;
+ }
+} \ No newline at end of file
diff --git a/src/plugins/android.codeutils/resources/databaseDeploy/DAOByTablejava.txt b/src/plugins/android.codeutils/resources/databaseDeploy/DAOByTablejava.txt
new file mode 100644
index 0000000..48c6413
--- /dev/null
+++ b/src/plugins/android.codeutils/resources/databaseDeploy/DAOByTablejava.txt
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package #packageName#;
+
+import java.util.*;
+
+import android.content.*;
+import android.database.*;
+import android.database.sqlite.*;
+import android.util.*;
+
+
+public class #className# {
+
+ private DatabaseHelper dbHelper;
+ private static final String TAG = "#tableName#DAO";
+ private static final String DATABASE_NAME = "#dbName#";
+ private static final int DATABASE_VERSION = 1;
+ private static HashMap<String, String> #tableNameUpperCase#_PROJECTION_MAP;
+ private static final String TABLE_NAME = "#tableNameLowerCase#";
+
+ /**
+ * Singleton constructor
+ */
+ private #className# ()
+ {
+ }
+
+ private static final #className# instance = new #className# ();
+
+ /**
+ * Get singleton
+ */
+ public static #className# getInstance() {
+ return instance;
+ }
+
+ private static class DatabaseHelper extends SQLiteOpenHelper {
+ /**
+ * Constructor
+ * Takes and keeps a reference of the passed context in order to access to the application assets and resources.
+ * @param context
+ */
+ public DatabaseHelper(Context context) {
+ super(context, DATABASE_NAME, null, DATABASE_VERSION);
+ }
+
+ public void onCreate(SQLiteDatabase db) {
+ #createTableStatement#
+ }
+
+ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+ Log.w(TAG, "Upgrading database from version " + oldVersion + "to " + newVersion + ", which will destroy all old data");
+ #dropTableStatement#
+ }
+ }
+
+ /**
+ * Need to be called before using query, insert, delete or update methods
+ */
+ public boolean openDatabase(Context context) {
+ dbHelper = new DatabaseHelper(context);
+ return (dbHelper == null) ? false : true;
+ }
+
+ /**
+ * Need to be called to release SQLite db connection
+ */
+ public void closeDatabase(Context context) {
+ dbHelper.close();
+ }
+
+ /**
+ * Selects all items from table using all the columns
+ */
+ public Cursor selectAll(String sort) {
+ return query(getProjectionMapAllItems(), null, null, null, null, sort);
+ }
+
+ public Cursor query(String[] projection, String selection, String[] selectionArgs, String groupBy, String having, String sort) {
+ SQLiteDatabase mDB = dbHelper.getReadableDatabase();
+ Cursor c = mDB.query(TABLE_NAME, projection, selection, selectionArgs, groupBy, having, sort);
+ return c;
+ }
+
+ public long insert(ContentValues initialValues) {
+ SQLiteDatabase mDB = dbHelper.getWritableDatabase();
+ long rowID;
+ ContentValues values;
+ if (initialValues != null) {
+ values = new ContentValues(initialValues);
+ } else {
+ values = new ContentValues();
+ }
+
+ #insertStatementCases#
+
+ rowID = mDB.insert("#tableNameLowerCase#", "#tableNameLowerCase#", values);
+ return rowID;
+ }
+
+ public int delete(String where, String[] whereArgs) {
+ SQLiteDatabase mDB = dbHelper.getWritableDatabase();
+ int count;
+ count = mDB.delete(TABLE_NAME, where, whereArgs);
+ return count;
+ }
+
+ public int update(ContentValues values, String where, String[] whereArgs) {
+ SQLiteDatabase mDB = dbHelper.getWritableDatabase();
+ int count;
+ count = mDB.update(TABLE_NAME, values, where, whereArgs);
+ return count;
+ }
+
+ public static String[] getProjectionMapAllItems(){
+ return #tableNameUpperCase#_PROJECTION_MAP.keySet().toArray(new String[]{});
+ }
+
+
+ static {
+ #tableNameUpperCase#_PROJECTION_MAP = new HashMap<String, String>();
+#ProjectionMapStatementCases#
+ }
+}
diff --git a/src/plugins/android.codeutils/resources/databaseDeploy/DatabaseHelperjava.txt b/src/plugins/android.codeutils/resources/databaseDeploy/DatabaseHelperjava.txt
new file mode 100644
index 0000000..d24e695
--- /dev/null
+++ b/src/plugins/android.codeutils/resources/databaseDeploy/DatabaseHelperjava.txt
@@ -0,0 +1,197 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package #packageName#;
+
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import android.content.Context;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteException;
+import android.database.sqlite.SQLiteOpenHelper;
+
+/**
+ * <p>
+ * This class copies a SQLite database from your application's assets directory to
+ * /data/data/<your_application_package>/databases/ so you can access it using the SQLite APIs
+ * provided by the Android SDK. Note that {@link #className##copyDatabaseFile()} checks
+ * for the existence of the database and only copies it if needed.
+ * </p>
+ * <p>
+ * {@link #className##copyDatabaseFile()} calls {@link SQLiteOpenHelper#getReadableDatabase()},
+ * which in turn calls {@link SQLiteOpenHelper#onCreate(SQLiteDatabase)}. Be aware that the
+ * implementation of the overridden {@link SQLiteOpenHelper#onCreate(SQLiteDatabase)} must remain
+ * empty in order for the copy operation to work correctly.
+ * </p>
+ * <p>
+ * This class includes a constructor {@link #className###className#(Context, boolean)} which
+ * allows you to control whether the database file should be copied when the class is instantiated.
+ * </p>
+ * @see SQLiteOpenHelper
+ */
+public class #className# extends SQLiteOpenHelper {
+
+ // Android's default system path for your application's database.
+ private static String DB_PATH = "/data/data/#applicationPackageNamespace#/databases/";
+
+ private static String DB_NAME = "#dbName#";
+
+ private final Context myContext;
+
+ /**
+ * Constructor
+ * Keeps a reference to the passed context in order to access the application's assets.
+ * @param context Context to be used
+ */
+ public #className#(Context context) {
+
+ super(context, DB_NAME, null, 1);
+ this.myContext = context;
+ }
+
+ /**
+ * This constructor copies the database file if the copyDatabase argument is <code>true</code>.
+ * It keeps a reference to the passed context in order to access the application's assets.
+ *
+ * @param context Context to be used
+ * @param copyDatabase If <code>true</code>, the database file is copied (if it does not already exist)
+ */
+ public #className#(Context context, boolean copyDatabase) {
+ // call overloaded constructor
+ this(context);
+ // copy database file in case desired
+ if (copyDatabase) {
+ copyDatabaseFile();
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see android.database.sqlite.SQLiteOpenHelper#onCreate(android.database.sqlite.SQLiteDatabase)
+ */
+ @Override
+ public void onCreate(SQLiteDatabase db) {
+ // Leave this method empty
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * android.database.sqlite.SQLiteOpenHelper#onUpgrade(android.database.sqlite
+ * .SQLiteDatabase, int, int)
+ */
+ @Override
+ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+ // fill in your code here
+ }
+
+ /**
+ * <p>
+ * Copy the database file from the assets directory to the proper location where the
+ * application can access it. The database location and name is given by the
+ * constants {@link #DB_PATH} and {@link #DB_NAME} respectively.
+ * </p>
+ * <p>
+ * If the database file already exists, it will not be overwritten.
+ *</p>
+ */
+ public void copyDatabaseFile() {
+
+ // variables
+ InputStream myInput = null;
+ OutputStream myOutput = null;
+ SQLiteDatabase database = null;
+
+ // only proceed in case the database does not exist
+ if (!checkDataBaseExistence()) {
+ // get the database
+ database = this.getReadableDatabase();
+ try {
+ // Open your local db as the input stream
+ myInput = myContext.getAssets().open(DB_NAME);
+
+ // Path to the just created empty db
+ String outFileName = DB_PATH + DB_NAME;
+
+ // Open the empty db as the output stream
+ myOutput = new FileOutputStream(outFileName);
+
+ // transfer bytes from the input file to the output file
+ byte[] buffer = new byte[1024];
+ int length;
+ while ((length = myInput.read(buffer)) > 0) {
+ myOutput.write(buffer, 0, length);
+ }
+ } catch (FileNotFoundException e) {
+ // handle your exception here
+ } catch (IOException e) {
+ // handle your exception here
+ } finally {
+ try {
+ // Close the streams
+ myOutput.flush();
+ myOutput.close();
+ myInput.close();
+ // close the database in case it is opened
+ if (database != null && database.isOpen())
+ {
+ database.close();
+ }
+
+ } catch (Exception e) {
+ // handle your exception here
+ }
+ }
+ }
+ }
+
+ /**
+ * Returns whether the database already exists.
+ *
+ * @return <code>true</code> if the database exists, <code>false</code>
+ * otherwise.
+ */
+ private boolean checkDataBaseExistence() {
+
+ // database to be verified
+ SQLiteDatabase dbToBeVerified = null;
+
+ try {
+ // get database path
+ String dbPath = DB_PATH + DB_NAME;
+ // try to open the database
+ dbToBeVerified = SQLiteDatabase.openDatabase(dbPath, null,
+ SQLiteDatabase.OPEN_READONLY);
+
+ } catch (SQLiteException e) {
+ // do nothing since the database does not exist
+ }
+
+ // in case it exists, close it
+ if (dbToBeVerified != null) {
+ // close DB
+ dbToBeVerified.close();
+
+ }
+
+ // in case there is a DB entity, the DB exists
+ return dbToBeVerified != null ? true : false;
+ }
+}
diff --git a/src/plugins/android.codeutils/resources/databaseDeploy/DatabaseListActivity.txt b/src/plugins/android.codeutils/resources/databaseDeploy/DatabaseListActivity.txt
new file mode 100644
index 0000000..17a017a
--- /dev/null
+++ b/src/plugins/android.codeutils/resources/databaseDeploy/DatabaseListActivity.txt
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package #package_name#;
+
+import android.app.Activity;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.graphics.Color;
+import android.graphics.Typeface;
+import android.os.Bundle;
+import android.view.Gravity;
+import android.widget.TableLayout;
+import android.widget.TableRow;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import #ManifestPackageName#.R;
+
+public class #class_name# extends Activity {
+ /* Called when the activity is first created. */
+
+ private final int ONE = 1;
+ private TableLayout tbl = null;
+ private TableRow row = null;
+
+ private android.widget.TableRow.LayoutParams rowParams =
+ new android.widget.TableRow.LayoutParams(
+ android.widget.TableRow.LayoutParams.FILL_PARENT,
+ android.widget.TableRow.LayoutParams.WRAP_CONTENT);
+
+ private android.widget.TableRow.LayoutParams tableParams =
+ new android.widget.TableRow.LayoutParams(
+ android.widget.TableRow.LayoutParams.FILL_PARENT,
+ android.widget.TableRow.LayoutParams.WRAP_CONTENT);
+
+ //set database location
+ private String DB_PATH = "/data/data/#package_name#/databases/";
+ private String DB_NAME = "#dbName#";
+
+ //column names
+ #constColumnsNames#
+
+ //table name
+ private final String SQL_TABLE_NAME = "#tableNameLowerCase#";
+ private boolean DISTINCT_ROWS = true;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ setContentView(R.layout.#layout_name#database_list/database_layout.xml#);
+
+ tbl = (TableLayout) findViewById(R.id.myTableLayout);
+ tbl.setBackgroundColor(Color.BLACK);
+
+
+
+ SQLiteDatabase checkDB = null;
+
+ try
+ {
+ String myPath = DB_PATH + DB_NAME;
+ checkDB = SQLiteDatabase.openDatabase(myPath, null, SQLiteDatabase.OPEN_READONLY);
+ loadTable(checkDB);
+ }
+ catch(Exception e)
+ {
+ Toast.makeText(getApplicationContext(), e.getMessage(), Toast.LENGTH_LONG).show();
+ }
+
+ if(checkDB != null)
+ {
+ checkDB.close();
+ }
+ }
+
+ public void loadTable(SQLiteDatabase sqliteDatabase) {
+ Cursor cursor = sqliteDatabase.query(DISTINCT_ROWS, SQL_TABLE_NAME,
+ new String[] { #columsNames# } /*columns*/, COL_1 + "=" + COL_1 /*selection*/,
+ null /*selection args*/, null /*group by*/,
+ null /*having*/, null /*order by*/, null /*limit*/);
+
+ if (cursor != null) {
+ this.startManagingCursor(cursor);
+
+ String[] columns = cursor.getColumnNames();
+ createHeader(columns);
+
+ while(cursor.move(ONE))
+ {
+ #columnGetValues#
+
+ TableRow row = new TableRow(getApplicationContext());
+ row.setLayoutParams(rowParams);
+
+ if(cursor.getPosition() % 2 == 1) {
+ row.setBackgroundColor(Color.LTGRAY);
+ }
+ else {
+ row.setBackgroundColor(Color.WHITE);
+ }
+
+ #columnAddRows#
+
+ tbl.addView(row, tableParams);
+ }
+ }
+ }
+
+ //adds new value to row
+ private void addToRow(Object obj, TableRow row)
+ {
+ TextView txt = new TextView(this);
+ if(obj != null)
+ txt.setText(obj.toString());
+ else
+ txt.setText("");
+ txt.setLayoutParams(rowParams);
+ txt.setTextColor(Color.BLACK);
+ txt.setGravity(Gravity.RIGHT);
+ txt.setPadding(5, 1, 1, 1);
+ row.addView(txt);
+ }
+
+ //creates table header
+ private void createHeader(String[] names)
+ {
+ row = new TableRow(getApplicationContext());
+ row.setLayoutParams(rowParams);
+ row.setBackgroundColor(Color.GRAY);
+
+ for(String column : names)
+ {
+ TextView txt = new TextView(this);
+ txt.setText(column.toUpperCase());
+ txt.setLayoutParams(rowParams);
+ txt.setWidth(80);
+ txt.setTextColor(Color.BLACK);
+ txt.setTypeface(Typeface.DEFAULT_BOLD);
+ txt.setGravity(Gravity.CENTER);
+ //adds new field to row
+ row.addView(txt);
+ }
+
+ //adds row to table
+ tbl.addView(row, tableParams);
+ }
+
+ /**
+ *
+ */
+ private boolean createDatabase() {
+ #sqlOpenHelperName# helper = new #sqlOpenHelperName#(getApplicationContext());
+ try {
+ helper.createDataBase();
+ } catch (IOException ioe) {
+ throw new Error("Unable to create database");
+ }
+ try {
+ helper.openDataBase();
+ } catch (SQLException sqle) {
+ throw sqle;
+ }
+ return false;
+ }
+} \ No newline at end of file
diff --git a/src/plugins/android.codeutils/schema/com.motorola.studio.android.sampleActivityDatabase.exsd b/src/plugins/android.codeutils/schema/com.motorola.studio.android.sampleActivityDatabase.exsd
new file mode 100644
index 0000000..bc0662e
--- /dev/null
+++ b/src/plugins/android.codeutils/schema/com.motorola.studio.android.sampleActivityDatabase.exsd
@@ -0,0 +1,102 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<!-- Schema file written by PDE -->
+<schema targetNamespace="com.motorola.studio.android" xmlns="http://www.w3.org/2001/XMLSchema">
+<annotation>
+ <appInfo>
+ <meta.schema plugin="com.motorola.studio.android" id="com.motorola.studio.android.sampleActivityDatabase" name="Add Android Sample Activity Page Based on Database"/>
+ </appInfo>
+ <documentation>
+ [Enter description of this extension point.]
+ </documentation>
+ </annotation>
+
+ <element name="extension">
+ <annotation>
+ <appInfo>
+ <meta.element />
+ </appInfo>
+ </annotation>
+ <complexType>
+ <sequence>
+ <element ref="parameterCollector"/>
+ </sequence>
+ <attribute name="point" type="string" use="required">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ </annotation>
+ </attribute>
+ <attribute name="id" type="string">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ </annotation>
+ </attribute>
+ <attribute name="name" type="string">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ <appInfo>
+ <meta.attribute translatable="true"/>
+ </appInfo>
+ </annotation>
+ </attribute>
+ </complexType>
+ </element>
+
+ <element name="parameterCollector">
+ <complexType>
+ <attribute name="class" type="string" use="required">
+ <annotation>
+ <documentation>
+ Class that contributes a new page to be added as the last step on new activity wizard. It must provide database and table selection and should return the parameters to fill on class to be created.
+ </documentation>
+ <appInfo>
+ <meta.attribute kind="java" basedOn=":com.motorola.studio.android.model.IDatabaseSampleActivityParametersWizardCollector"/>
+ </appInfo>
+ </annotation>
+ </attribute>
+ </complexType>
+ </element>
+
+ <annotation>
+ <appInfo>
+ <meta.section type="since"/>
+ </appInfo>
+ <documentation>
+ [Enter the first release in which this extension point appears.]
+ </documentation>
+ </annotation>
+
+ <annotation>
+ <appInfo>
+ <meta.section type="examples"/>
+ </appInfo>
+ <documentation>
+ [Enter extension point usage example here.]
+ </documentation>
+ </annotation>
+
+ <annotation>
+ <appInfo>
+ <meta.section type="apiinfo"/>
+ </appInfo>
+ <documentation>
+ [Enter API information here.]
+ </documentation>
+ </annotation>
+
+ <annotation>
+ <appInfo>
+ <meta.section type="implementation"/>
+ </appInfo>
+ <documentation>
+ [Enter information about supplied implementation of this extension point.]
+ </documentation>
+ </annotation>
+
+
+</schema>
diff --git a/src/plugins/android.codeutils/src/com/motorola/studio/android/codeutils/CodeUtilsActivator.java b/src/plugins/android.codeutils/src/com/motorola/studio/android/codeutils/CodeUtilsActivator.java
new file mode 100644
index 0000000..6207a15
--- /dev/null
+++ b/src/plugins/android.codeutils/src/com/motorola/studio/android/codeutils/CodeUtilsActivator.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.codeutils;
+
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.ui.plugin.AbstractUIPlugin;
+import org.osgi.framework.BundleContext;
+
+import com.motorola.studio.android.common.log.StudioLogger;
+
+/**
+ * The activator class controls the plug-in life cycle
+ */
+public class CodeUtilsActivator extends AbstractUIPlugin
+{
+
+ // The plug-in ID
+ public static final String PLUGIN_ID = "com.motorola.studio.android.codeutils"; //$NON-NLS-1$
+
+ // Studio for Android Perspective ID
+ public static final String PERSPECTIVE_ID = "com.motorola.studio.android.perspective";
+
+ // The shared instance
+ private static CodeUtilsActivator plugin;
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.ui.plugin.AbstractUIPlugin#start(org.osgi.framework.BundleContext)
+ */
+ @Override
+ public void start(BundleContext context) throws Exception
+ {
+ StudioLogger.debug(CodeUtilsActivator.class,
+ "Starting MOTODEV Android Code Utils Plugin...");
+
+ super.start(context);
+ plugin = this;
+
+ StudioLogger.debug(CodeUtilsActivator.class, "MOTODEV Android Code Utils Plugin started.");
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.ui.plugin.AbstractUIPlugin#stop(org.osgi.framework.BundleContext)
+ */
+ @Override
+ public void stop(BundleContext context) throws Exception
+ {
+ plugin = null;
+ super.stop(context);
+ }
+
+ /**
+ * Returns the shared instance
+ *
+ * @return the shared instance
+ */
+ public static CodeUtilsActivator getDefault()
+ {
+ return plugin;
+ }
+
+ /**
+ * Creates and returns a new image descriptor for an image file in this plug-in.
+ * @param path the relative path of the image file, relative to the root of the plug-in; the path must be legal
+ * @return an image descriptor, or null if no image could be found
+ */
+ public static ImageDescriptor getImageDescriptor(String path)
+ {
+ return imageDescriptorFromPlugin(PLUGIN_ID, path);
+ }
+}
diff --git a/src/plugins/android.codeutils/src/com/motorola/studio/android/codeutils/codegeneration/CreateSampleDatabaseActivityColumnsPage.java b/src/plugins/android.codeutils/src/com/motorola/studio/android/codeutils/codegeneration/CreateSampleDatabaseActivityColumnsPage.java
new file mode 100644
index 0000000..97520c5
--- /dev/null
+++ b/src/plugins/android.codeutils/src/com/motorola/studio/android/codeutils/codegeneration/CreateSampleDatabaseActivityColumnsPage.java
@@ -0,0 +1,382 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.codeutils.codegeneration;
+
+import java.util.Arrays;
+import java.util.Collection;
+
+import org.eclipse.datatools.modelbase.sql.tables.Column;
+import org.eclipse.emf.common.util.BasicEList;
+import org.eclipse.emf.common.util.EList;
+import org.eclipse.jface.dialogs.IPageChangeProvider;
+import org.eclipse.jface.dialogs.IPageChangedListener;
+import org.eclipse.jface.dialogs.PageChangedEvent;
+import org.eclipse.jface.viewers.ArrayContentProvider;
+import org.eclipse.jface.viewers.CheckStateChangedEvent;
+import org.eclipse.jface.viewers.CheckboxTableViewer;
+import org.eclipse.jface.viewers.ICheckStateListener;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.layout.RowLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.ui.PlatformUI;
+
+import com.motorola.studio.android.codeutils.CodeUtilsActivator;
+import com.motorola.studio.android.codeutils.i18n.CodeUtilsNLS;
+import com.motorola.studio.android.common.exception.AndroidException;
+import com.motorola.studio.android.common.utilities.AndroidUtils;
+import com.motorola.studio.android.model.ActivityBasedOnTemplate;
+import com.motorola.studio.android.wizards.buildingblocks.Method;
+import com.motorola.studio.android.wizards.buildingblocks.NewLauncherWizardPage;
+
+/**
+ * Wizard page to create the columns of databases under create activities based on database samples wizard page context.
+ * */
+public class CreateSampleDatabaseActivityColumnsPage extends NewLauncherWizardPage
+{
+ /**
+ * Help id of the page.
+ * */
+ public static final String PAGE_HELP_ID = CodeUtilsActivator.PLUGIN_ID + ".selectcolumnspage";
+
+ private boolean firstLoad = true;
+
+ private String previousSelectedTableName = "";
+
+ private CheckboxTableViewer checkboxTableViewer;
+
+ /**
+ * Default constructor.
+ * </br></br>
+ * Creates a new instance using {@link com.motorola.studio.android.wizards.buildingblocks.NewLauncherWizardPage#NewLauncherWizardPage(com.motorola.studio.android.model.BuildingBlockModel,java.lang.String) NewLauncherWizardPage(BuildingBlockModel, String)}
+ * and passing {@code null} and {@link CodeUtilsNLS#UI_CreateSampleDatabaseActivityColumnsPageName Page name} as arguments.
+ */
+ public CreateSampleDatabaseActivityColumnsPage()
+ {
+ super(null, CodeUtilsNLS.UI_CreateSampleDatabaseActivityColumnsPageName);
+ }
+
+ /**
+ * Creates a new instance using {@link com.motorola.studio.android.wizards.buildingblocks.NewLauncherWizardPage#NewLauncherWizardPage(com.motorola.studio.android.model.BuildingBlockModel,java.lang.String) NewLauncherWizardPage(BuildingBlockModel, String)}
+ * and passing {@code activity} and {@link CodeUtilsNLS#UI_CreateSampleDatabaseActivityColumnsPageName Page name} as arguments.
+ *
+ * @param activity an {@code com.motorola.studio.android.model.ActivityBasedOnTemplate} to be used as the building block model.
+ */
+ public CreateSampleDatabaseActivityColumnsPage(ActivityBasedOnTemplate activity)
+ {
+ super(activity, CodeUtilsNLS.UI_CreateSampleDatabaseActivityColumnsPageName);
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.wizards.buildingblocks.NewBuildingBlocksWizardPage#createIntermediateControls(org.eclipse.swt.widgets.Composite)
+ */
+ @Override
+ protected void createExtendedControls(Composite parent)
+ {
+ Composite mainComposite = new Composite(parent, SWT.NONE);
+ mainComposite.setLayout(new GridLayout());
+ mainComposite.setLayoutData(new GridData(GridData.FILL_BOTH));
+
+ Composite composite = new Composite(mainComposite, SWT.NONE);
+ composite.setLayout(new GridLayout());
+ composite.setLayoutData(new GridData(GridData.FILL_BOTH));
+
+ // Create a checkbox table viewer
+ checkboxTableViewer =
+ CheckboxTableViewer.newCheckList(composite, SWT.MULTI | SWT.BORDER | SWT.V_SCROLL);
+ checkboxTableViewer.getControl()
+ .setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+
+ // Set a content and label provider
+ checkboxTableViewer.setLabelProvider(new SampleDatabaseActivityColumnsPageLabelProvider());
+ checkboxTableViewer.setContentProvider(new ArrayContentProvider());
+
+ // Add a listener to the table viewer
+ checkboxTableViewer.addCheckStateListener(new CheckboxTableViewerListener());
+
+ // Add a listener to the wizard to listen for page changes
+ if (getContainer() instanceof IPageChangeProvider)
+ {
+ ((IPageChangeProvider) getContainer()).addPageChangedListener(new PageChangeListener());
+ }
+
+ Composite buttonsComposite = new Composite(mainComposite, SWT.NONE);
+ buttonsComposite.setLayout(new RowLayout(SWT.HORIZONTAL));
+
+ // Create buttons to select and deselect all items
+ Button selectAllButton = new Button(buttonsComposite, SWT.PUSH | SWT.CENTER);
+ selectAllButton
+ .setText(CodeUtilsNLS.UI_CreateSampleDatabaseActivityColumnsPage_SelectAllButton);
+ selectAllButton.addSelectionListener(new SelectAllButtonListener());
+
+ Button unselectAllButton = new Button(buttonsComposite, SWT.PUSH | SWT.CENTER);
+ unselectAllButton
+ .setText(CodeUtilsNLS.UI_CreateSampleDatabaseActivityColumnsPage_DeselectAllButton);
+ unselectAllButton.addSelectionListener(new DeselectAllButtonLister());
+
+ PlatformUI.getWorkbench().getHelpSystem().setHelp(mainComposite, PAGE_HELP_ID);
+
+ setControl(mainComposite);
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.wizards.buildingblocks.NewLauncherWizardPage#getIntentFiltersActions()
+ */
+ @Override
+ protected String[] getIntentFiltersActions()
+ {
+ String[] intentFiltersActions = new String[0];
+ try
+ {
+ intentFiltersActions = AndroidUtils.getActivityActions(getBuildBlock().getProject());
+ }
+ catch (AndroidException e)
+ {
+ setErrorMessage(e.getMessage());
+ }
+ return intentFiltersActions;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.wizards.buildingblocks.NewBuildingBlocksWizardPage#getDefaultMessage()
+ */
+ @Override
+ public String getDefaultMessage()
+ {
+ return CodeUtilsNLS.UI_CreateSampleDatabaseActivityColumnsPage_Default_Message;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.wizards.buildingblocks.NewBuildingBlocksWizardPage#getHelpId()
+ */
+ @Override
+ protected String getHelpId()
+ {
+ return PAGE_HELP_ID;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.wizards.buildingblocks.NewBuildingBlocksWizardPage#getMethods()
+ */
+ @Override
+ protected Method[] getMethods()
+ {
+ return null;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.wizards.buildingblocks.NewBuildingBlocksWizardPage#getWizardTitle()
+ */
+ @Override
+ public String getWizardTitle()
+ {
+ return CodeUtilsNLS.UI_ActivityWizard_Title;
+ }
+
+ /*(non-Javadoc)
+ * @see com.motorola.studio.android.wizards.buildingblocks.NewBuildingBlocksWizardPage#canFlipToNextPage()
+ * */
+ @Override
+ public boolean canFlipToNextPage()
+ {
+ return (getErrorMessage() == null) && (checkboxTableViewer.getCheckedElements().length > 0);
+ }
+
+ /**
+ * @return True if page has header. Otherwise, returns false.
+ */
+ @Override
+ public boolean hasHeader()
+ {
+ return false;
+ }
+
+ private class CheckboxTableViewerListener implements ICheckStateListener
+ {
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.viewers.ICheckStateListener#checkStateChanged(org.eclipse.jface.viewers.CheckStateChangedEvent)
+ */
+ public void checkStateChanged(CheckStateChangedEvent event)
+ {
+ // Check if there are checked elements or not
+ if (checkboxTableViewer.getCheckedElements().length > 0)
+ {
+ // Update list of columns in the Activity according to event
+
+ // Get the element changed
+ if (event.getElement() instanceof Column)
+ {
+ Column changedElement = (Column) event.getElement();
+
+ if (event.getChecked())
+ {
+ // Add element to the list
+ ((ActivityBasedOnTemplate) getBuildBlock()).getCollectorColumnList().add(
+ changedElement);
+ }
+ else
+ {
+ // Remove element from the list
+ ((ActivityBasedOnTemplate) getBuildBlock()).getCollectorColumnList()
+ .remove(changedElement);
+ }
+
+ // Wizard can finish
+ ((ActivityBasedOnTemplate) getBuildBlock())
+ .setUseSampleDatabaseTableSelected(true);
+
+ }
+
+ }
+ else
+ {
+ // Wizard cannot finish
+ ((ActivityBasedOnTemplate) getBuildBlock())
+ .setUseSampleDatabaseTableSelected(false);
+
+ // Remove all columns from the collection
+ ((ActivityBasedOnTemplate) getBuildBlock()).getCollectorColumnList().clear();
+ }
+
+ getWizard().getContainer().updateButtons();
+ }
+ }
+
+ /**
+ * Listener to verify when this page is visible.
+ */
+ private class PageChangeListener implements IPageChangedListener
+ {
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * org.eclipse.jface.dialogs.IPageChangedListener#pageChanged(org.eclipse
+ * .jface.dialogs.PageChangedEvent)
+ */
+ @SuppressWarnings("unchecked")
+ public void pageChanged(PageChangedEvent event)
+ {
+ if ((event.getSelectedPage() == CreateSampleDatabaseActivityColumnsPage.this))
+ {
+ // Retrieve the collection of columns from the selected table
+ EList<Column> columnList = new BasicEList<Column>();
+ String currentTableName = "";
+
+ if (((ActivityBasedOnTemplate) getBuildBlock()).getCollectorTable() != null)
+ {
+ currentTableName =
+ ((ActivityBasedOnTemplate) getBuildBlock()).getCollectorTable()
+ .getName();
+ columnList =
+ ((ActivityBasedOnTemplate) getBuildBlock()).getCollectorTable()
+ .getColumns();
+ }
+
+ checkboxTableViewer.setInput(columnList.toArray(new Column[0]));
+
+ if (firstLoad || (!currentTableName.equals(previousSelectedTableName)))
+ {
+ selectAllItems();
+
+ }
+ previousSelectedTableName = currentTableName;
+ firstLoad = false;
+ }
+
+ }
+
+ }
+
+ /**
+ * Listener for the select all button.
+ */
+ private class SelectAllButtonListener implements SelectionListener
+ {
+
+ /* (non-Javadoc)
+ * @see org.eclipse.swt.events.SelectionListener#widgetDefaultSelected(org.eclipse.swt.events.SelectionEvent)
+ */
+ public void widgetDefaultSelected(SelectionEvent e)
+ {
+ // do nothing
+
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.swt.events.SelectionListener#widgetSelected(org.eclipse.swt.events.SelectionEvent)
+ */
+ public void widgetSelected(SelectionEvent e)
+ {
+ selectAllItems();
+ }
+ }
+
+ /**
+ * Select all items in {@link CreateSampleDatabaseActivityColumnsPage#checkboxTableViewer checkboxTableViewer}.
+ */
+ @SuppressWarnings("unchecked")
+ private void selectAllItems()
+ {
+ // Make all the items in the list selected
+ checkboxTableViewer.setAllChecked(true);
+ // Add elements to the list
+ ((ActivityBasedOnTemplate) getBuildBlock()).getCollectorColumnList().addAll(
+ (Collection<? extends Column>) Arrays.asList(checkboxTableViewer
+ .getCheckedElements()));
+
+ // Wizard can finish
+ ((ActivityBasedOnTemplate) getBuildBlock()).setUseSampleDatabaseTableSelected(true);
+ getWizard().getContainer().updateButtons();
+ }
+
+ /**
+ * Listener for the deselect all button.
+ */
+ private class DeselectAllButtonLister implements SelectionListener
+ {
+
+ /* (non-Javadoc)
+ * @see org.eclipse.swt.events.SelectionListener#widgetDefaultSelected(org.eclipse.swt.events.SelectionEvent)
+ */
+ public void widgetDefaultSelected(SelectionEvent e)
+ {
+ // do nothing
+
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.swt.events.SelectionListener#widgetSelected(org.eclipse.swt.events.SelectionEvent)
+ */
+ public void widgetSelected(SelectionEvent e)
+ {
+ // Make all the items in the list deselected
+ checkboxTableViewer.setAllChecked(false);
+ // Clear collection of columns
+ ((ActivityBasedOnTemplate) getBuildBlock()).getCollectorColumnList().clear();
+ // Wizard can finish
+ ((ActivityBasedOnTemplate) getBuildBlock()).setUseSampleDatabaseTableSelected(false);
+ getWizard().getContainer().updateButtons();
+
+ }
+
+ }
+}
diff --git a/src/plugins/android.codeutils/src/com/motorola/studio/android/codeutils/codegeneration/CreateSampleDatabaseActivityPage.java b/src/plugins/android.codeutils/src/com/motorola/studio/android/codeutils/codegeneration/CreateSampleDatabaseActivityPage.java
new file mode 100644
index 0000000..e9f0af3
--- /dev/null
+++ b/src/plugins/android.codeutils/src/com/motorola/studio/android/codeutils/codegeneration/CreateSampleDatabaseActivityPage.java
@@ -0,0 +1,404 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.motorola.studio.android.codeutils.codegeneration;
+
+import java.io.IOException;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.datatools.connectivity.ConnectionProfileException;
+import org.eclipse.datatools.modelbase.sql.schema.Database;
+import org.eclipse.datatools.modelbase.sql.tables.Table;
+import org.eclipse.jface.dialogs.IPageChangeProvider;
+import org.eclipse.jface.dialogs.IPageChangedListener;
+import org.eclipse.jface.dialogs.PageChangedEvent;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.ITreeSelection;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+import org.eclipse.jface.viewers.TreeNode;
+import org.eclipse.jface.viewers.TreeNodeContentProvider;
+import org.eclipse.jface.viewers.TreeSelection;
+import org.eclipse.jface.viewers.TreeViewer;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+
+import com.motorola.studio.android.codeutils.CodeUtilsActivator;
+import com.motorola.studio.android.codeutils.db.utils.DatabaseUtils;
+import com.motorola.studio.android.codeutils.i18n.CodeUtilsNLS;
+import com.motorola.studio.android.common.exception.AndroidException;
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.common.utilities.AndroidUtils;
+import com.motorola.studio.android.model.ActivityBasedOnTemplate;
+import com.motorola.studio.android.wizards.buildingblocks.Method;
+import com.motorola.studio.android.wizards.buildingblocks.NewLauncherWizardPage;
+
+/**
+ * Wizard page to create an activity based on database samples.
+ * */
+public class CreateSampleDatabaseActivityPage extends NewLauncherWizardPage
+{
+
+ private TreeViewer treeViewer;
+
+ //Tree viewer input
+ private TreeNode[] treeNodeArray;
+
+ // Page help ID
+ public static final String PAGE_HELP_ID = CodeUtilsActivator.PLUGIN_ID + ".selecttablepage";
+
+ // ANDROID_METADATA Table name
+ private static final String ANDROID_METADATA_TABLE_NAME = "ANDROID_METADATA";
+
+ // Can flip to next page flag
+ private boolean canFlip = false;
+
+ /**
+ * Default constructor.
+ * </br></br>
+ * Creates a new instance using {@link com.motorola.studio.android.wizards.buildingblocks.NewLauncherWizardPage#NewLauncherWizardPage(com.motorola.studio.android.model.BuildingBlockModel,java.lang.String) NewLauncherWizardPage(BuildingBlockModel, String)}
+ * and passing {@code null} and {@link CodeUtilsNLS#UI_CreateSampleDatabaseActivityPageName Page name} as arguments.
+ */
+ public CreateSampleDatabaseActivityPage()
+ {
+ super(null, CodeUtilsNLS.UI_CreateSampleDatabaseActivityPageName);
+ }
+
+ /**
+ * Creates a new instance using {@link com.motorola.studio.android.wizards.buildingblocks.NewLauncherWizardPage#NewLauncherWizardPage(com.motorola.studio.android.model.BuildingBlockModel,java.lang.String) NewLauncherWizardPage(BuildingBlockModel, String)}
+ * and passing {@code activity} and {@link CodeUtilsNLS#UI_CreateSampleDatabaseActivityPageName Page name} as arguments.
+ *
+ * @param activity an {@code com.motorola.studio.android.model.ActivityBasedOnTemplate} to be used as the building block model.
+ */
+ public CreateSampleDatabaseActivityPage(ActivityBasedOnTemplate activity)
+ {
+ super(activity, CodeUtilsNLS.UI_CreateSampleDatabaseActivityPageName);
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.wizards.buildingblocks.NewBuildingBlocksWizardPage#createIntermediateControls(org.eclipse.swt.widgets.Composite)
+ */
+ @Override
+ protected void createExtendedControls(Composite parent)
+ {
+ Composite mainComposite = new Composite(parent, SWT.NONE);
+ mainComposite.setLayout(new GridLayout());
+ mainComposite.setLayoutData(new GridData(GridData.FILL_BOTH));
+ mainComposite.setLayout(new GridLayout());
+
+ Composite composite = new Composite(mainComposite, SWT.NONE);
+ composite.setLayout(new GridLayout());
+ composite.setLayoutData(new GridData(GridData.FILL_BOTH));
+
+ // Databases and Tables Tree Viewer
+ treeViewer = new TreeViewer(composite, SWT.BORDER | SWT.SINGLE | SWT.V_SCROLL);
+ treeViewer.getControl().setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+
+ // Set content and label provider
+ treeViewer.setLabelProvider(new SampleDatabaseActivityPageLabelProvider());
+ treeViewer.setContentProvider(new TreeNodeContentProvider());
+
+ // Create an array of type TreeNode[] to serve as the input for the tree
+ if ((treeNodeArray == null) || (treeNodeArray.length < 1))
+ {
+ treeNodeArray = generateTreeViewerInput();
+ }
+
+ treeViewer.setInput(treeNodeArray);
+ treeViewer.addSelectionChangedListener(new DatabaseTreeListener());
+
+ // Check if there were any databases found and update status if not.
+ if (treeNodeArray.length < 1)
+ {
+ // Create a warning status
+ IStatus status =
+ new Status(
+ IStatus.WARNING,
+ CodeUtilsActivator.PLUGIN_ID,
+ CodeUtilsNLS.UI_CreateSampleDatabaseActivityPage_No_Database_Found_Information);
+ updateStatus(status);
+ }
+
+ // Expand all elements
+ treeViewer.expandAll();
+
+ // Add a listener to the wizard to listen for page changes
+ if (getContainer() instanceof IPageChangeProvider)
+ {
+ ((IPageChangeProvider) getContainer()).addPageChangedListener(new PageChangeListener());
+ }
+
+ setControl(mainComposite);
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.wizards.buildingblocks.NewLauncherWizardPage#getIntentFiltersActions()
+ */
+ @Override
+ protected String[] getIntentFiltersActions()
+ {
+ String[] intentFiltersActions = new String[0];
+ try
+ {
+ intentFiltersActions = AndroidUtils.getActivityActions(getBuildBlock().getProject());
+ }
+ catch (AndroidException e)
+ {
+ setErrorMessage(e.getMessage());
+ }
+ return intentFiltersActions;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.wizards.buildingblocks.NewBuildingBlocksWizardPage#getDefaultMessage()
+ */
+ @Override
+ public String getDefaultMessage()
+ {
+ return CodeUtilsNLS.UI_CreateSampleDatabaseActivityPage_Default_Message;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.wizards.buildingblocks.NewBuildingBlocksWizardPage#getHelpId()
+ */
+ @Override
+ protected String getHelpId()
+ {
+ return PAGE_HELP_ID;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.wizards.buildingblocks.NewBuildingBlocksWizardPage#getWizardTitle()
+ */
+ @Override
+ public String getWizardTitle()
+ {
+ return CodeUtilsNLS.UI_ActivityWizard_Title;
+
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.wizards.buildingblocks.NewBuildingBlocksWizardPage#getMethods()
+ */
+ @Override
+ protected Method[] getMethods()
+ {
+ return null;
+ }
+
+ /*
+ * (non-Javadoc)
+ * */
+ @Override
+ public boolean canFlipToNextPage()
+ {
+
+ return canFlip;
+ }
+
+ /*
+ * Method to retrieve the databases and tables from the target project and make a TreeNodeArray
+ */
+ private TreeNode[] generateTreeViewerInput()
+ {
+ // Collection of TreeNodes
+ HashSet<TreeNode> treeNodeColletion = new HashSet<TreeNode>();
+
+ // The selected project for which the activity will be created
+ IProject project = getBuildBlock().getProject();
+
+ if (project != null)
+ {
+ // Get a collection of existing .db files inside the project
+ Set<IFile> dbFilesSet = DatabaseUtils.getDbFilesFromProject(project);
+
+ // Retrieve the database instances
+ for (IFile dbFile : dbFilesSet)
+ {
+ try
+ {
+ // For each database retrieved, construct a TreeNode object containing itself and it's tables
+ Database database =
+ DatabaseUtils.getDatabase(project.getName(), dbFile.getName());
+ TreeNode treeNodeDatabase = new TreeNode(database);
+
+ // Collection to store the table treeNodes from this database. Will be used later to set the children nodes of the database.
+ HashSet<TreeNode> databaseChildren = new HashSet<TreeNode>();
+
+ // Construct another TreeTable object for each table and set the database tree node as the parent.
+ // Tables don't have children
+ for (Table table : DatabaseUtils.getTables(database))
+ {
+ // Do not add ANDROID_METADATA table
+ if (!table.getName().equalsIgnoreCase(ANDROID_METADATA_TABLE_NAME))
+ {
+ TreeNode treeNodeTable = new TreeNode(table);
+ treeNodeTable.setParent(treeNodeDatabase);
+
+ // Add this node as a children of the database tree node.
+ databaseChildren.add(treeNodeTable);
+ }
+
+ }
+
+ // Add the table nodes as the children of the database node
+ treeNodeDatabase.setChildren(databaseChildren.toArray(new TreeNode[0]));
+
+ // Add the database tree node to the resulting TreeNode collection that will serve as input
+ treeNodeColletion.add(treeNodeDatabase);
+ }
+ catch (ConnectionProfileException e)
+ {
+ // Log error
+ StudioLogger.error(DatabaseUtils.class,
+ "A error ocurred while retrieving the connection profile.", e);
+ }
+ catch (IOException e)
+ {
+ // Log error
+ StudioLogger.error(DatabaseUtils.class, "An I/O error ocurred.", e);
+ }
+ }
+ }
+
+ // Return a TreeNode array
+ return treeNodeColletion.toArray(new TreeNode[0]);
+
+ }
+
+ /**
+ * @return Returns true if page has header. Otherwise, returns false.
+ */
+ @Override
+ public boolean hasHeader()
+ {
+ return false;
+ }
+
+ /**
+ * Selection listener for the tree viewer.
+ */
+ class DatabaseTreeListener implements ISelectionChangedListener
+ {
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.viewers.ISelectionChangedListener#selectionChanged(org.eclipse.jface.viewers.SelectionChangedEvent)
+ */
+ public void selectionChanged(SelectionChangedEvent event)
+ {
+ ActivityBasedOnTemplate activity = ((ActivityBasedOnTemplate) getBuildBlock());
+
+ if (event.getSelection() instanceof ITreeSelection)
+ {
+ ITreeSelection treeSelection = (ITreeSelection) event.getSelection();
+
+ if (treeSelection.getFirstElement() instanceof TreeNode)
+ {
+ TreeNode selectedNode = (TreeNode) treeSelection.getFirstElement();
+ // Check if it's a database or table that was selected and set verification flags
+ if (selectedNode.getValue() instanceof Table)
+ {
+ canFlip = true;
+ // Set the collector table
+ activity.setCollectorTable((Table) selectedNode.getValue());
+ // Set the collector database
+ TreeNode parentNode = selectedNode.getParent();
+ activity.setCollectorDatabaseName(((Database) parentNode.getValue())
+ .getName());
+ activity.setDatabaseTableSelected(true);
+
+ }
+ else
+ {
+ canFlip = false;
+ // Set the collector info to null
+ activity.setCollectorTable(null);
+ activity.setCollectorDatabaseName(null);
+ activity.setDatabaseTableSelected(false);
+
+ }
+
+ getWizard().getContainer().updateButtons();
+ }
+ }
+
+ }
+ }
+
+ /**
+ * Listener to verify when this page is visible
+ */
+ private class PageChangeListener implements IPageChangedListener
+ {
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * org.eclipse.jface.dialogs.IPageChangedListener#pageChanged(org.eclipse
+ * .jface.dialogs.PageChangedEvent)
+ */
+ public void pageChanged(PageChangedEvent event)
+ {
+ if ((event.getSelectedPage() == CreateSampleDatabaseActivityPage.this))
+ {
+ ActivityBasedOnTemplate activity = (ActivityBasedOnTemplate) getBuildBlock();
+
+ treeNodeArray = generateTreeViewerInput();
+ treeViewer.setInput(treeNodeArray);
+
+ // Check if there were any databases found and update status if not.
+ if (treeNodeArray.length < 1)
+ {
+ // Create a warning status
+ IStatus status =
+ new Status(
+ IStatus.WARNING,
+ CodeUtilsActivator.PLUGIN_ID,
+ CodeUtilsNLS.UI_CreateSampleDatabaseActivityPage_No_Database_Found_Information);
+ updateStatus(status);
+ }
+ else
+ {
+ updateStatus(new Status(IStatus.OK, CodeUtilsActivator.PLUGIN_ID, null));
+ }
+
+ if ((activity.getCollectorTable() == null)
+ && (!treeViewer.getSelection().isEmpty()))
+ {
+ treeViewer.setSelection(TreeSelection.EMPTY);
+ }
+
+ if (treeViewer.getSelection().isEmpty())
+ {
+ canFlip = false;
+ }
+
+ //update buttons
+ getWizard().getContainer().updateButtons();
+
+ // Expand all elements
+ treeViewer.expandAll();
+ }
+
+ }
+ }
+}
diff --git a/src/plugins/android.codeutils/src/com/motorola/studio/android/codeutils/codegeneration/DatabaseListActivityGeneratorByTable.java b/src/plugins/android.codeutils/src/com/motorola/studio/android/codeutils/codegeneration/DatabaseListActivityGeneratorByTable.java
new file mode 100644
index 0000000..1c4d9f2
--- /dev/null
+++ b/src/plugins/android.codeutils/src/com/motorola/studio/android/codeutils/codegeneration/DatabaseListActivityGeneratorByTable.java
@@ -0,0 +1,388 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.motorola.studio.android.codeutils.codegeneration;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.Map;
+
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.SubMonitor;
+import org.eclipse.datatools.modelbase.sql.datatypes.SQLDataType;
+import org.eclipse.datatools.modelbase.sql.tables.Column;
+import org.eclipse.datatools.modelbase.sql.tables.Table;
+import org.eclipse.jface.wizard.IWizardPage;
+
+import com.motorola.studio.android.codeutils.CodeUtilsActivator;
+import com.motorola.studio.android.codeutils.db.utils.DatabaseUtils;
+import com.motorola.studio.android.codeutils.i18n.CodeUtilsNLS;
+import com.motorola.studio.android.common.exception.AndroidException;
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.common.utilities.EclipseUtils;
+import com.motorola.studio.android.model.IDatabaseSampleActivityParametersWizardCollector;
+
+/**
+ * Helper class to create Activity based on Sqlite tables.
+ * It uses one file as template and substitutes parameters enclosed by hash-sign (#)
+ * Warning: The tables named "ANDROID_METADATA" and "sqlite_sequence" will be ignored as it is an special table in Android apps context.
+ */
+public class DatabaseListActivityGeneratorByTable implements
+ IDatabaseSampleActivityParametersWizardCollector
+{
+
+ private static final String CLASS_EXTENTION_TYPE = "Activity";
+
+ private String databaseName = null;
+
+ private String sqlOpenHelperClassName = null;
+
+ private String sqlOpenHelperPackageName = null;
+
+ private boolean createOpenHelper = false;
+
+ private Table table = null;
+
+ private final List<Column> selectedColumns = new ArrayList<Column>();
+
+ //includes only items that are not default, that is with the java cursor type varies from the return type
+ private static Map<String, String> SQLTYPE_TO_CURSOR_TYPE_METHOD =
+ new HashMap<String, String>();
+
+ private static Map<String, String> SQLTYPE_TO_RETURN_TYPE = new HashMap<String, String>();
+
+ static
+ {
+ SQLTYPE_TO_CURSOR_TYPE_METHOD.put("BLOB", "Byte");
+ SQLTYPE_TO_CURSOR_TYPE_METHOD.put("INTEGER", "Int");
+
+ SQLTYPE_TO_RETURN_TYPE.put("CHAR", "String");
+ SQLTYPE_TO_RETURN_TYPE.put("VARCHAR", "String");
+ SQLTYPE_TO_RETURN_TYPE.put("TEXT", "String");
+ SQLTYPE_TO_RETURN_TYPE.put("SMALLINT", "Short");
+ SQLTYPE_TO_RETURN_TYPE.put("INTEGER", "Integer");
+ SQLTYPE_TO_RETURN_TYPE.put("BIGINT", "Long");
+ SQLTYPE_TO_RETURN_TYPE.put("REAL", "Float");
+ SQLTYPE_TO_RETURN_TYPE.put("FLOAT", "Double");
+ SQLTYPE_TO_RETURN_TYPE.put("DOUBLE", "Double");
+ SQLTYPE_TO_RETURN_TYPE.put("BINARY", "byte[]");
+ SQLTYPE_TO_RETURN_TYPE.put("VARBINARY", "byte[]");
+ SQLTYPE_TO_RETURN_TYPE.put("LONG VARBINARY", "byte[]");
+ SQLTYPE_TO_RETURN_TYPE.put("IMAGE", "byte[]");
+ SQLTYPE_TO_RETURN_TYPE.put("BLOB", "byte[]");
+ }
+
+ /**
+ * @return the table created by this class.
+ * */
+ @Override
+ public Table getTable()
+ {
+ return this.table;
+ }
+
+ /**
+ * Sets the initial table to be used.
+ * */
+ @Override
+ public void setTable(Table table)
+ {
+ this.table = table;
+ }
+
+ @Override
+ public String getTableName()
+ {
+ return getTable().getName();
+ }
+
+ /**
+ * Authority to access content URI (it is based on package name and content provider name)
+ * @param packageName the package name of the authority.
+ * @param activityName the activity name of the authority.
+ * @return the authority using package and activity name.
+ */
+ protected String getAuthority(String packageName, String activityName)
+ {
+ return packageName + "." + activityName.toLowerCase();
+ }
+
+ public String getClassName()
+ {
+ String className = getTableName() + CLASS_EXTENTION_TYPE;
+ return className;
+ }
+
+ @Override
+ public String getDatabaseName()
+ {
+ return this.databaseName;
+ }
+
+ @Override
+ public void setDatabaseName(String databaseName)
+ {
+ this.databaseName = databaseName;
+ }
+
+ /**
+ * @return a string representing a list of the column names.
+ */
+ @Override
+ public String getColumnsNames()
+ {
+ StringBuilder buf = new StringBuilder();
+ String columnsResult = "";
+ for (int i = 0; i < selectedColumns.size(); i++)
+ {
+ int colIndex = i + 1;
+ String colSt = "COL_" + colIndex;
+ if ((colIndex > 1) && (colIndex <= selectedColumns.size()))
+ {
+ colSt = ", " + colSt;
+ }
+ columnsResult += colSt;
+ }
+ buf.append(columnsResult);
+ return buf.toString();
+ }
+
+ /**
+ * @return constants to get on table list
+ */
+ @Override
+ public String getConstColumnsNames()
+ {
+ StringBuilder buf = new StringBuilder();
+
+ ListIterator<Column> columnsIter = selectedColumns.listIterator();
+ int index = 1;
+ String constSt = "private final String ";
+ while (columnsIter.hasNext())
+ {
+ Column column = columnsIter.next();
+ String colSt = constSt + "COL_" + index + " = " + "\"" + column.getName() + "\";";
+ buf.append("\t" + colSt + "\n");
+ index++;
+ }
+ return buf.toString();
+ }
+
+ /**
+ * Creates add columns
+ * @throws AndroidException when the type of a column is not valid.
+ */
+ @Override
+ public String getCursorValues() throws AndroidException
+ {
+ StringBuilder buf = new StringBuilder();
+
+ ListIterator<Column> columnsIter = selectedColumns.listIterator();
+ Integer index = 1;
+ String templateSt =
+ "#returnType# col#index# = cursor.get#CursorType#(cursor.getColumnIndex(COL_#index#));";
+ while (columnsIter.hasNext())
+ {
+ String cursorSt = templateSt;
+ Column column = columnsIter.next();
+ String columnName = column.getName().toLowerCase();
+ String sqltype = null;
+ SQLDataType type = column.getContainedType();
+ if (type != null)
+ {
+ sqltype = type.getName();
+ }
+ else
+ {
+ throw new AndroidException("Column " + columnName
+ + " does not have a recognized type");
+ }
+
+ String javaReturnType = SQLTYPE_TO_RETURN_TYPE.get(sqltype);
+ cursorSt = cursorSt.replace("#returnType#", javaReturnType);
+ cursorSt = cursorSt.replaceAll("#index#", index.toString());
+ String getType = "";
+ if (SQLTYPE_TO_CURSOR_TYPE_METHOD.containsKey(sqltype))
+ {
+ //non-default rule
+ getType = SQLTYPE_TO_CURSOR_TYPE_METHOD.get(sqltype);
+ }
+ else
+ {
+ //default rule - getX where X is the java type
+ getType = SQLTYPE_TO_RETURN_TYPE.get(sqltype);
+ getType = getType.replace("[]", "");
+ //change first letter to upper case
+ String firstElem = "" + getType.charAt(0);
+ getType = getType.replace(firstElem, firstElem.toUpperCase());
+ }
+ cursorSt = cursorSt.replaceAll("#CursorType#", getType);
+ index++;
+ buf.append("\t" + cursorSt + "\n");
+ }
+ return buf.toString();
+ }
+
+ /**
+ * Creates add columns to row statements
+ */
+ @Override
+ public String getAddColumnsToRow()
+ {
+ StringBuilder buf = new StringBuilder();
+
+ String constSt = "addToRow(col#index# , row);";
+ for (int i = 0; i < selectedColumns.size(); i++)
+ {
+ String replacedText = constSt.replace("#index#", Integer.toString(i + 1));
+ buf.append("\t" + replacedText + "\n");
+ }
+ return buf.toString();
+ }
+
+ /**
+ * @return a list of wizard pages required to create activities based on database list templates.
+ * */
+ @Override
+ public List<IWizardPage> getWizardPages()
+ {
+ List<IWizardPage> contributedPages = new ArrayList<IWizardPage>();
+ contributedPages.add(new CreateSampleDatabaseActivityPage());
+ contributedPages.add(new CreateSampleDatabaseActivityColumnsPage());
+ contributedPages.add(new DefineSqlOpenHelperPage());
+ return contributedPages;
+ }
+
+ @Override
+ public void setSelectedColumns(List<Column> selectedColumns)
+ {
+ this.selectedColumns.addAll(selectedColumns);
+ }
+
+ /**
+ * Creates Sql Open Helper required to transfer db file and make the activity work correctly
+ */
+ @Override
+ public void createSqlOpenHelper(IProject project, IProgressMonitor monitor)
+ {
+ boolean createOpenHelper = true;
+ boolean createContentProvider = false;
+ boolean isOverrideContentProviders = false;
+ boolean generateDao = false;
+ String contentProvidersPackageName = null; //not used
+
+ //create deployer
+ try
+ {
+ // sub monitor
+ SubMonitor subMonitor = SubMonitor.convert(monitor, 10);
+
+ DatabaseUtils.createDatabaseManagementClasses(project, databaseName, createOpenHelper,
+ createContentProvider, sqlOpenHelperPackageName, contentProvidersPackageName,
+ getSqlOpenHelperClassName(), isOverrideContentProviders, generateDao,
+ subMonitor.newChild(10), false);
+ }
+ catch (Exception e)
+ {
+ StudioLogger.error(DatabaseListActivityGeneratorByTable.class,
+ CodeUtilsNLS.DATABASE_DEPLOY_ERROR_DEPLOYING_DATABASE, e);
+ IStatus status =
+ new Status(IStatus.ERROR, CodeUtilsActivator.PLUGIN_ID, e.getLocalizedMessage());
+ EclipseUtils.showErrorDialog(CodeUtilsNLS.DATABASE_DEPLOY_ERROR_DEPLOYING_DATABASE,
+ CodeUtilsNLS.DATABASE_DEPLOY_ERROR_DEPLOYING_DATABASE, status);
+ }
+ }
+
+ @Override
+ public String getSqlOpenHelperClassName()
+ {
+ return this.sqlOpenHelperClassName;
+ }
+
+ @Override
+ public void setSqlOpenHelperClassName(String sqlOpenHelperClassName)
+ {
+ this.sqlOpenHelperClassName = sqlOpenHelperClassName;
+ }
+
+ @Override
+ public void setSqlOpenHelperPackageName(String sqlOpenHelperPackageName)
+ {
+ this.sqlOpenHelperPackageName = sqlOpenHelperPackageName;
+ }
+
+ /**
+ * @return The fully qualified name of the generated class to be used as {@code import} statement.
+ * */
+ @Override
+ public String getImports()
+ {
+ String imports = "";
+ if (createOpenHelper)
+ {
+ imports =
+ "import " + this.sqlOpenHelperPackageName + "." + this.sqlOpenHelperClassName
+ + ";";
+ }
+ return imports;
+ }
+
+ /**
+ * @return True if the open helper classes will be created. Otherwise, returns false.
+ * */
+ @Override
+ public boolean createOpenHelper()
+ {
+ return this.createOpenHelper;
+ }
+
+ /**
+ * Set whether the open helper classes should be created.
+ * */
+ @Override
+ public void setCreateOpenHelper(boolean createOpenHelper)
+ {
+ this.createOpenHelper = createOpenHelper;
+ }
+
+ /**
+ * @return A string representing the necessary code to retrieve an android readable database of the database created in this class.
+ * */
+ @Override
+ public String getReadableDatabase()
+ {
+ StringBuilder buf = new StringBuilder();
+
+ if (createOpenHelper)
+ {
+ buf.append(sqlOpenHelperClassName + " helper = new " + sqlOpenHelperClassName
+ + "(getApplicationContext(),true);" + "\n");
+ buf.append("checkDB = helper.getReadableDatabase();" + "\n");
+ }
+ else
+ {
+ buf.append("String myPath = DB_PATH + DB_NAME;" + "\n");
+ buf.append("checkDB = SQLiteDatabase.openDatabase(myPath, null, SQLiteDatabase.OPEN_READONLY);"
+ + "\n");
+ }
+ return buf.toString();
+ }
+}
diff --git a/src/plugins/android.codeutils/src/com/motorola/studio/android/codeutils/codegeneration/DefineSqlOpenHelperPage.java b/src/plugins/android.codeutils/src/com/motorola/studio/android/codeutils/codegeneration/DefineSqlOpenHelperPage.java
new file mode 100644
index 0000000..78e6148
--- /dev/null
+++ b/src/plugins/android.codeutils/src/com/motorola/studio/android/codeutils/codegeneration/DefineSqlOpenHelperPage.java
@@ -0,0 +1,347 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.codeutils.codegeneration;
+
+import org.eclipse.jdt.ui.wizards.NewTypeWizardPage;
+import org.eclipse.jface.dialogs.DialogPage;
+import org.eclipse.jface.dialogs.IPageChangeProvider;
+import org.eclipse.jface.dialogs.IPageChangedListener;
+import org.eclipse.jface.dialogs.PageChangedEvent;
+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.Control;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Group;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.ui.PlatformUI;
+
+import com.motorola.studio.android.codeutils.CodeUtilsActivator;
+import com.motorola.studio.android.codeutils.i18n.CodeUtilsNLS;
+import com.motorola.studio.android.common.exception.AndroidException;
+import com.motorola.studio.android.common.utilities.AndroidUtils;
+import com.motorola.studio.android.model.ActivityBasedOnTemplate;
+import com.motorola.studio.android.model.Launcher;
+import com.motorola.studio.android.wizards.buildingblocks.Method;
+import com.motorola.studio.android.wizards.buildingblocks.NewLauncherWizardPage;
+
+/**
+ * Wizard to define (select existing or create new) SQL Open Helper file
+ * to enable connection and copy of database for the sample
+ **/
+public class DefineSqlOpenHelperPage extends NewLauncherWizardPage
+{
+ private static final String SQL_OPEN_HELPER = "SqlOpenHelper";
+
+ private Button ckbGenerateSQLOpenHelper;
+
+ private Group sqlOpenHelperGroup;
+
+ private boolean firstLoad = true;
+
+ public static final String PAGE_HELP_ID = CodeUtilsActivator.PLUGIN_ID
+ + ".defineconnectiondatabasepage";
+
+ /**
+ * Default constructor.
+ * </br></br>
+ * Creates a new instance using {@link com.motorola.studio.android.wizards.buildingblocks.NewLauncherWizardPage#NewLauncherWizardPage(com.motorola.studio.android.model.BuildingBlockModel,java.lang.String) NewLauncherWizardPage(BuildingBlockModel, String)}
+ * and passing {@link com.motorola.studio.android.model.ActivityBasedOnTemplate#ActivityBasedOnTemplate()} and {@link CodeUtilsNLS#UI_DefineSqlOpenHelperPage_Title Page name} as arguments.
+ */
+ public DefineSqlOpenHelperPage()
+ {
+ //need to instantiate new activity because it will define a new sql open helper class to create.
+ super(new ActivityBasedOnTemplate(), CodeUtilsNLS.UI_DefineSqlOpenHelperPage_Title);
+ }
+
+ /**
+ * Creates a new instance using {@link com.motorola.studio.android.wizards.buildingblocks.NewLauncherWizardPage#NewLauncherWizardPage(com.motorola.studio.android.model.BuildingBlockModel,java.lang.String) NewLauncherWizardPage(BuildingBlockModel, String)}
+ * and passing {@code activity} and {@link CodeUtilsNLS#UI_CreateSampleDatabaseActivityPageName Page name} as arguments.
+ *
+ * @param activity an {@code com.motorola.studio.android.model.ActivityBasedOnTemplate} to be used as the building block model.
+ */
+ public DefineSqlOpenHelperPage(ActivityBasedOnTemplate activity)
+ {
+ super(activity, CodeUtilsNLS.UI_DefineSqlOpenHelperPage_Title);
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.wizards.buildingblocks.NewBuildingBlocksWizardPage#createIntermediateControls(org.eclipse.swt.widgets.Composite)
+ */
+ @Override
+ protected void createExtendedControls(Composite parent)
+ {
+ Composite mainComposite = new Composite(parent, SWT.NONE);
+ mainComposite.setLayout(new GridLayout());
+ mainComposite.setLayoutData(new GridData(GridData.FILL_BOTH));
+
+ // Add a listener to the wizard to listen for page changes
+ if (getContainer() instanceof IPageChangeProvider)
+ {
+ ((IPageChangeProvider) getContainer()).addPageChangedListener(new PageChangeListener());
+ }
+
+ PlatformUI.getWorkbench().getHelpSystem().setHelp(mainComposite, PAGE_HELP_ID);
+
+ createOpenHelperSection(mainComposite);
+
+ setControl(mainComposite);
+ }
+
+ /**
+ * Listener to verify when this page is visible.
+ */
+ private class PageChangeListener implements IPageChangedListener
+ {
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * org.eclipse.jface.dialogs.IPageChangedListener#pageChanged(org.eclipse
+ * .jface.dialogs.PageChangedEvent)
+ */
+ public void pageChanged(PageChangedEvent event)
+ {
+ if ((event.getSelectedPage() == DefineSqlOpenHelperPage.this))
+ {
+ Launcher launcher = getBuildBlock();
+ if (launcher instanceof ActivityBasedOnTemplate)
+ {
+ ActivityBasedOnTemplate activity = (ActivityBasedOnTemplate) getBuildBlock();
+ if (activity != null)
+ {
+
+ setPackageFragmentRoot(activity.getPackageFragmentRoot(), false);
+
+ if (firstLoad)
+ {
+ setPackageFragment(activity.getPackageFragment(), true);
+ setTypeName(activity.getName() + SQL_OPEN_HELPER, true);
+ }
+ firstLoad = false;
+ }
+ }
+ handleFieldChanged(NewTypeWizardPage.TYPENAME);
+ handleFieldChanged(NewTypeWizardPage.PACKAGE);
+ }
+ }
+ }
+
+ /**
+ * Create composite group to display SQL Open Helper parameters.
+ * @param mainComposite parent composite.
+ */
+ private void createOpenHelperSection(Composite mainComposite)
+ {
+ // check box for generating SQL Open Helper
+ ckbGenerateSQLOpenHelper = new Button(mainComposite, SWT.CHECK);
+ ckbGenerateSQLOpenHelper
+ .setText(CodeUtilsNLS.UI_PersistenceWizardPageCreateNewSQLOpenHelper);
+ ckbGenerateSQLOpenHelper.setSelection(true);
+
+ sqlOpenHelperGroup = new Group(mainComposite, SWT.NONE);
+ sqlOpenHelperGroup.setText(CodeUtilsNLS.UI_PersistenceWizardPageSQLOpenHelperGroupTitle);
+ int numColumns = 5;
+ sqlOpenHelperGroup.setLayout(new GridLayout(numColumns, false));
+ sqlOpenHelperGroup.setLayoutData(new GridData(GridData.FILL, GridData.FILL, true, false));
+
+ /* Class selection for SQLOpenHelper */
+ createTypeNameControls(sqlOpenHelperGroup, numColumns);
+
+ /* Package selection for SQLOpenHelper */
+ createContainerControls(sqlOpenHelperGroup, numColumns);
+
+ createPackageControls(sqlOpenHelperGroup, numColumns);
+
+ ckbGenerateSQLOpenHelper.setEnabled(true);
+ ((ActivityBasedOnTemplate) getBuildBlock()).setCreateOpenHelper(true);
+
+ // add Listener for the check box of the open helper enablement
+ ckbGenerateSQLOpenHelper.addListener(SWT.Selection, new Listener()
+ {
+ public void handleEvent(Event event)
+ {
+ boolean selected = ckbGenerateSQLOpenHelper.getSelection();
+ ((ActivityBasedOnTemplate) getBuildBlock()).setCreateOpenHelper(selected);
+
+ if (!selected)
+ {
+ setMessage(CodeUtilsNLS.UI_DefineSqlOpenHelperPage_WarningNoOpenHelperSelected,
+ DialogPage.WARNING);
+ }
+ else
+ {
+ setMessage(null);
+ }
+
+ // get the check box which dispatched the event
+ Button checkBox = event.widget != null ? (Button) event.widget : null;
+ // proceed in case there is a check box
+ if (checkBox != null)
+ {
+ // flag indicating whether to enable/disable the controls
+ boolean enabled = checkBox.getSelection();
+ // enable/disable the children of panelEnablementGroup field
+ setCompositeChildremEnabled(sqlOpenHelperGroup, enabled);
+ }
+ setOpenHelperDefined();
+ getWizard().getContainer().updateButtons();
+ }
+ });
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.wizards.buildingblocks.NewLauncherWizardPage#getIntentFiltersActions()
+ */
+ @Override
+ protected String[] getIntentFiltersActions()
+ {
+ String[] intentFiltersActions = new String[0];
+ try
+ {
+ intentFiltersActions = AndroidUtils.getActivityActions(getBuildBlock().getProject());
+ }
+ catch (AndroidException e)
+ {
+ setErrorMessage(e.getMessage());
+ }
+ return intentFiltersActions;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.wizards.buildingblocks.NewBuildingBlocksWizardPage#getDefaultMessage()
+ */
+ @Override
+ public String getDefaultMessage()
+ {
+ return CodeUtilsNLS.UI_DefineSqlOpenHelperPage_Default_Message;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.wizards.buildingblocks.NewBuildingBlocksWizardPage#getHelpId()
+ */
+ @Override
+ protected String getHelpId()
+ {
+ return PAGE_HELP_ID;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.wizards.buildingblocks.NewBuildingBlocksWizardPage#getMethods()
+ */
+ @Override
+ protected Method[] getMethods()
+ {
+ return null;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.wizards.buildingblocks.NewBuildingBlocksWizardPage#getWizardTitle()
+ */
+ @Override
+ public String getWizardTitle()
+ {
+ return CodeUtilsNLS.UI_ActivityWizard_Title;
+ }
+
+ /*
+ * (non-Javadoc)
+ * */
+ @Override
+ public boolean canFlipToNextPage()
+ {
+ return false;
+ }
+
+ /**
+ * @return Returns true if page has header. Otherwise, returns false.
+ */
+ @Override
+ public boolean hasHeader()
+ {
+ return false;
+ }
+
+ /*
+ * (non-Javadoc)
+ * */
+ @Override
+ protected void handleFieldChanged(String fieldName)
+ {
+ if (NewTypeWizardPage.TYPENAME.equals(fieldName))
+ {
+ String typeNameWithParameters = getTypeName();
+ ((ActivityBasedOnTemplate) getBuildBlock())
+ .setSqlOpenHelperClassName(typeNameWithParameters);
+ getBuildBlock().setNameStatus(typeNameChanged());
+ getBuildBlock().setPackageStatus(packageChanged());
+ }
+ else if (NewTypeWizardPage.PACKAGE.equals(fieldName))
+ {
+ String packName = getPackageText();
+ ((ActivityBasedOnTemplate) getBuildBlock()).setSqlOpenHelperPackageName(packName);
+ getBuildBlock().setPackageStatus(packageChanged());
+ }
+ updateStatus(getBuildBlock().getStatus());
+ setOpenHelperDefined();
+ getWizard().getContainer().updateButtons();
+ }
+
+ /**
+ * True if user selects to create open helper class and there is no error message, false otherwise.
+ */
+ private void setOpenHelperDefined()
+ {
+ //update if no error detected on name and package
+ ((ActivityBasedOnTemplate) getBuildBlock())
+ .setSqlOpenHelperDefined(!isCreateSQLOpenHelperClass()
+ || (getErrorMessage() == null));
+ }
+
+ /**
+ * Returns <code>true</code> in case it is necessary to create
+ * the Open SQL Helper classes, <code>false</code> otherwise.
+ *
+ * @return <code>true</code> in case it is necessary to create SQL
+ * Open Helper class, <code>false</code> otherwise.
+ */
+ public boolean isCreateSQLOpenHelperClass()
+ {
+ return ckbGenerateSQLOpenHelper != null ? ckbGenerateSQLOpenHelper.getSelection() : false;
+ }
+
+ /**
+ * Enable/disable children of the entered {@link Composite}.
+ *
+ * @param composite Composite to have its children enabled/disabled
+ * @param enabled <code>true</code> for enabling the elements, <code>false</code>
+ * for disabling the elements.
+ */
+ private void setCompositeChildremEnabled(Composite composite, boolean enabled)
+ {
+ Control[] controls = composite.getChildren();
+ if ((controls != null) && (controls.length > 0))
+ {
+ for (Control control : controls)
+ {
+ control.setEnabled(enabled);
+ }
+ }
+ }
+
+}
diff --git a/src/plugins/android.codeutils/src/com/motorola/studio/android/codeutils/codegeneration/SampleDatabaseActivityColumnsPageLabelProvider.java b/src/plugins/android.codeutils/src/com/motorola/studio/android/codeutils/codegeneration/SampleDatabaseActivityColumnsPageLabelProvider.java
new file mode 100644
index 0000000..03f9d5f
--- /dev/null
+++ b/src/plugins/android.codeutils/src/com/motorola/studio/android/codeutils/codegeneration/SampleDatabaseActivityColumnsPageLabelProvider.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.codeutils.codegeneration;
+
+import org.eclipse.datatools.modelbase.sql.tables.Column;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.jface.viewers.LabelProvider;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.ui.plugin.AbstractUIPlugin;
+
+/**
+ * Label provider for the tree viewer in the {@link CreateSampleDatabaseActivityColumnsPage}.
+ */
+public class SampleDatabaseActivityColumnsPageLabelProvider extends LabelProvider
+{
+
+ public static final String DATATOOLS_UI_PLUGIN_ID =
+ "org.eclipse.datatools.connectivity.sqm.core.ui";
+
+ private static final String COLUMN_ICON = "icons/columns.gif";
+
+ private ImageDescriptor desc = null;
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.viewers.LabelProvider#getImage(java.lang.Object)
+ */
+ @Override
+ public Image getImage(Object element)
+ {
+ Image resultImage = null;
+
+ if (element instanceof Column)
+ {
+ if (desc == null)
+ {
+ desc =
+ AbstractUIPlugin.imageDescriptorFromPlugin(DATATOOLS_UI_PLUGIN_ID,
+ COLUMN_ICON);
+
+ }
+
+ resultImage = desc.createImage();
+ }
+
+ return resultImage;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.viewers.LabelProvider#getText(java.lang.Object)
+ */
+ @Override
+ public String getText(Object element)
+ {
+ String resultLabel = null;
+
+ if (element instanceof Column)
+ {
+ resultLabel = ((Column) element).getName();
+ }
+
+ return resultLabel;
+ }
+
+}
diff --git a/src/plugins/android.codeutils/src/com/motorola/studio/android/codeutils/codegeneration/SampleDatabaseActivityPageLabelProvider.java b/src/plugins/android.codeutils/src/com/motorola/studio/android/codeutils/codegeneration/SampleDatabaseActivityPageLabelProvider.java
new file mode 100644
index 0000000..8d8797d
--- /dev/null
+++ b/src/plugins/android.codeutils/src/com/motorola/studio/android/codeutils/codegeneration/SampleDatabaseActivityPageLabelProvider.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.motorola.studio.android.codeutils.codegeneration;
+
+import org.eclipse.datatools.modelbase.sql.schema.Database;
+import org.eclipse.datatools.modelbase.sql.tables.Table;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.jface.viewers.LabelProvider;
+import org.eclipse.jface.viewers.TreeNode;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.ui.plugin.AbstractUIPlugin;
+
+/**
+ * Label provider for the tree viewer in the {@link CreateSampleDatabaseActivityPage}.
+ */
+public class SampleDatabaseActivityPageLabelProvider extends LabelProvider
+{
+
+ public static final String DATATOOLS_UI_PLUGIN_ID =
+ "org.eclipse.datatools.connectivity.sqm.core.ui";
+
+ private static final String DATABASE_ICON = "icons/database.gif";
+
+ private static final String TABLE_ICON = "icons/table.gif";
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.viewers.LabelProvider#getImage(java.lang.Object)
+ */
+ @Override
+ public Image getImage(Object element)
+ {
+ Image resultImage = null;
+
+ // Element type should be TreeObject
+ if (element instanceof TreeNode)
+ {
+ // Get the value and check if it's a database or table
+ Object value = ((TreeNode) element).getValue();
+ if (value instanceof Database)
+ {
+ ImageDescriptor desc =
+ AbstractUIPlugin.imageDescriptorFromPlugin(DATATOOLS_UI_PLUGIN_ID,
+ DATABASE_ICON);
+ resultImage = desc.createImage();
+ }
+ else if (value instanceof Table)
+ {
+ ImageDescriptor desc =
+ AbstractUIPlugin.imageDescriptorFromPlugin(DATATOOLS_UI_PLUGIN_ID,
+ TABLE_ICON);
+ resultImage = desc.createImage();
+ }
+ else if (value instanceof com.motorola.studio.android.db.wizards.model.Table)
+ {
+ ImageDescriptor desc =
+ AbstractUIPlugin.imageDescriptorFromPlugin(DATATOOLS_UI_PLUGIN_ID,
+ TABLE_ICON);
+ resultImage = desc.createImage();
+ }
+ }
+
+ return resultImage;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.viewers.LabelProvider#getText(java.lang.Object)
+ */
+ @Override
+ public String getText(Object element)
+ {
+ String result = "";
+
+ // Element type should be TreeObject
+ if (element instanceof TreeNode)
+ {
+ // Get the value and check if it's a database or table
+ Object value = ((TreeNode) element).getValue();
+ if (value instanceof Database)
+ {
+ result = ((Database) value).getName();
+ }
+ else if (value instanceof Table)
+ {
+ result = ((Table) value).getName();
+ }
+ else if (value instanceof com.motorola.studio.android.db.wizards.model.Table)
+ {
+ result =
+ ((com.motorola.studio.android.db.wizards.model.Table) value).getTableName();
+ }
+ }
+
+ return result;
+ }
+}
diff --git a/src/plugins/android.codeutils/src/com/motorola/studio/android/codeutils/db/actions/AbstractCodeGeneratorByTable.java b/src/plugins/android.codeutils/src/com/motorola/studio/android/codeutils/db/actions/AbstractCodeGeneratorByTable.java
new file mode 100644
index 0000000..df32ca2
--- /dev/null
+++ b/src/plugins/android.codeutils/src/com/motorola/studio/android/codeutils/db/actions/AbstractCodeGeneratorByTable.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.motorola.studio.android.codeutils.db.actions;
+
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.datatools.modelbase.sql.tables.Table;
+
+import com.motorola.studio.android.common.exception.AndroidException;
+import com.motorola.studio.android.manifest.AndroidProjectManifestFile;
+import com.motorola.studio.android.model.manifest.AndroidManifestFile;
+import com.motorola.studio.android.model.manifest.dom.ManifestNode;
+
+/**
+ *
+ * Base class with common methods to generate persistence classes.
+ *
+ */
+public abstract class AbstractCodeGeneratorByTable
+{
+ /**
+ * Table name that will not generate a class as it is android specific table.
+ */
+ public String ANDROID_METADATA = "ANDROID_METADATA";
+
+ /**
+ * Table name that will not generate a class as it is android specific table.
+ */
+ public String SQLITE_SEQUENCES = "sqlite_sequence";
+
+ private Table table;
+
+ /**
+ * Constructor that initializes the {@link org.eclipse.datatools.modelbase.sql.tables.Table Table} that will have its code generated.
+ * */
+ public AbstractCodeGeneratorByTable(Table table)
+ {
+ this.table = table;
+ }
+
+ public Table getTable()
+ {
+ return table;
+ }
+
+ public void setTable(Table table)
+ {
+ this.table = table;
+ }
+
+ /**
+ * Content provider package name (based on project name).
+ * @param project The project in which the table is contained.
+ * @return The package name that will hold the generated classes.
+ * @throws CoreException Exception thrown in case there are problems handling the android project.
+ * @throws AndroidException Exception thrown in case there are problems handling the android project.
+ */
+ protected String getPackageName(IProject project) throws AndroidException, CoreException
+ {
+ // get android application name
+ AndroidManifestFile androidManifestFile =
+ AndroidProjectManifestFile.getFromProject(project);
+ ManifestNode manifestNode =
+ androidManifestFile != null ? androidManifestFile.getManifestNode() : null;
+ String appNamespace = manifestNode.getPackageName().toLowerCase();
+ // return the android application name along with a persistence constant
+ return appNamespace + ".persistence";
+ }
+
+ protected String getTableName()
+ {
+ return table.getName();
+ }
+
+ /**
+ * Authority to access content URI (it is based on package name and content provider name).
+ * @param packageName The package name that compounds the authority.
+ * @param contentProviderName The content provider that compounds the authority.
+ * @return The authority URI.
+ */
+ protected String getAuthority(String packageName, String contentProviderName)
+ {
+ return packageName + "." + contentProviderName.toLowerCase();
+ }
+}
diff --git a/src/plugins/android.codeutils/src/com/motorola/studio/android/codeutils/db/actions/ContentProviderGeneratorByTable.java b/src/plugins/android.codeutils/src/com/motorola/studio/android/codeutils/db/actions/ContentProviderGeneratorByTable.java
new file mode 100644
index 0000000..8dbf052
--- /dev/null
+++ b/src/plugins/android.codeutils/src/com/motorola/studio/android/codeutils/db/actions/ContentProviderGeneratorByTable.java
@@ -0,0 +1,662 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.motorola.studio.android.codeutils.db.actions;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.Map;
+
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.datatools.modelbase.sql.tables.Column;
+import org.eclipse.datatools.modelbase.sql.tables.Table;
+import org.eclipse.emf.common.util.EList;
+
+import com.motorola.studio.android.common.exception.AndroidException;
+import com.motorola.studio.android.db.deployment.ContentProviderDeployer;
+import com.motorola.studio.android.db.deployment.ContentProviderDeployerByTable;
+import com.motorola.studio.android.db.deployment.DatabaseDeployer;
+
+/**
+ * Helper class to create ContentProviders based on Sqlite tables.
+ * It uses one file as template and substitutes parameters enclosed by hash-sign (#).
+ * Warning: The tables named "ANDROID_METADATA" and "sqlite_sequence" will be ignored as it is an special table in Android apps context.
+ */
+public class ContentProviderGeneratorByTable extends AbstractCodeGeneratorByTable
+{
+ private String dbName = null;
+
+ /**
+ * Creates a new content provider given a {@link org.eclipse.datatools.modelbase.sql.tables.Table Table} and the name of the database which the table is attached to.
+ * @param table The table used in the content provider.
+ * @param dbName The database name in which the table is attached to.
+ * */
+ public ContentProviderGeneratorByTable(Table table, String dbName)
+ {
+ super(table);
+ this.dbName = dbName;
+ }
+
+ /**
+ * Creates content provider based on table from Sqlite
+ * @param project
+ * @param addCreateTableStatement NOT USED ON 1.3.0 (for future implementation)
+ * @param addDropTableStatementOnUpdate NOT USED ON 1.3.0 (for future implementation)
+ * @param overrideContentProviderIfExists If <code>true</code> overrides content provider in case it exists
+ * @param contentProviderPackageName null to use default (.persistence inside project main package), otherwise the file path to place
+ * @param databaseOpenHelperPackageName Database Open Helper package name
+ * @param databaseOpenHelperClassName Database Open Helper class name
+ * @throws IOException
+ * @throws CoreException
+ * @throws AndroidException
+ */
+ public void createContentProvider(IProject project, boolean addCreateTableStatement,
+ boolean addDropTableStatementOnUpdate, boolean overrideContentProviderIfExists,
+ String contentProviderPackageName, String databaseOpenHelperPackageName,
+ String databaseOpenHelperClassName, List<String> tableNameForClasses)
+ throws IOException, CoreException, AndroidException
+ {
+ if (tableNameForClasses == null)
+ {
+ tableNameForClasses = new ArrayList<String>();
+ }
+
+ if ((getTableName() != null) && !getTableName().equalsIgnoreCase(ANDROID_METADATA)
+ && !getTableName().equalsIgnoreCase(SQLITE_SEQUENCES))
+ {
+ String packageName = "";
+ if (contentProviderPackageName != null)
+ {
+ //use package defined by user
+ packageName = contentProviderPackageName;
+ }
+ else
+ {
+ //use default package
+ packageName = getPackageName(project);
+ }
+
+ String openHelperPackageName = "";
+ if (databaseOpenHelperPackageName != null)
+ {
+ // use package defined by user
+ openHelperPackageName = databaseOpenHelperPackageName;
+ }
+ else
+ {
+ // use default package
+ openHelperPackageName = getPackageName(project);
+ }
+
+ String contentProviderName =
+ getJavaStyleTableName(tableNameForClasses) + "ContentProvider";
+ String authority = getAuthority(packageName, contentProviderName);
+
+ //create parameters and copy content provider class to the android project
+ Map<String, String> contentProviderParameters = new HashMap<String, String>();
+
+ contentProviderParameters.put(ContentProviderDeployer.ANDROID_PROJECT_PACKAGE_NAME,
+ packageName);
+ contentProviderParameters.put(ContentProviderDeployer.CONTENT_PROVIDER_CLASS_NAME,
+ contentProviderName);
+ contentProviderParameters.put(ContentProviderDeployer.CONTENT_PROVIDER_AUTHORITY,
+ authority);
+ contentProviderParameters.put("#databaseOpenHelperPackageName#", openHelperPackageName);
+ contentProviderParameters.put("#databaseOpenHelperClassName#",
+ databaseOpenHelperClassName);
+ contentProviderParameters.put(DatabaseDeployer.DATABASE_NAME, getDbName());
+ contentProviderParameters.put("#tableName#", getTableName());
+ contentProviderParameters.put("#tableNameUpperCase#", getTableName().toUpperCase());
+ contentProviderParameters.put("#tableNameLowerCase#", getTableName().toLowerCase());
+ contentProviderParameters.put("#uriConstants#", getUriConstants());
+ contentProviderParameters.put("#constIndexesProjectMap#", getConstIndexesProjectMap());
+
+ contentProviderParameters.put("#constContentValuesKeys#", getConstContentValuesKeys());
+ contentProviderParameters.put("#defaultSortOrder#", getDefaultSortOrder());
+ contentProviderParameters.put("#queryUrlCases#", getQueryUrlCases());
+ contentProviderParameters.put("#typeRecordCases#", getTypeRecordCases(packageName));
+ contentProviderParameters.put("#insertStatementCases#", getInsertStatementCases());
+ contentProviderParameters.put("#deleteStatementCases#", getDeleteStatementCases());
+ contentProviderParameters.put("#updateStatementCases#", getUpdateStatementCases());
+ contentProviderParameters.put("#UrlMatcherStatementCases#",
+ getUrlMatcherStatementCases());
+ contentProviderParameters.put("#ProjectionMapStatementCases#",
+ getProjectionMapStatementCases());
+
+ ContentProviderDeployerByTable.copyContentProviderHelperClassToProject(project,
+ contentProviderParameters,
+ ContentProviderDeployerByTable.CONTENT_PROVIDER_BY_TABLE_CLASS_LOCATION, true,
+ overrideContentProviderIfExists, new NullProgressMonitor());
+ }
+ }
+
+ /**
+ * @param tableNameForClasses This list contains names that were used before and should not be used anymore
+ * @return The table name in java style.
+ */
+ private String getJavaStyleTableName(List<String> tableNameForClasses)
+ {
+ String originalTableName = getTableName();
+
+ //this loop will guarantee that the table name do not starts with _
+ while (originalTableName.charAt(0) == '_')
+ {
+ originalTableName = originalTableName.substring(1);
+ }
+
+ char[] charList = originalTableName.toLowerCase().toCharArray();
+ charList[0] = Character.toUpperCase(charList[0]);
+
+ //will all character after _ in upper case, than remove all _
+ if (originalTableName.contains("_"))
+ {
+ for (int i = 1; i < charList.length; i++)
+ {
+ if (charList[i] == '_')
+ {
+ if ((i + 1) < charList.length)
+ {
+ charList[i + 1] = Character.toUpperCase(charList[i + 1]);
+ }
+ }
+ }
+ originalTableName = new String(charList);
+ originalTableName = originalTableName.replace("_", "");
+ }
+ else
+ {
+ originalTableName = new String(charList);
+ }
+
+ //will search tableNameForClasses to verify if there was a table with the same name
+ //created before. If it was, we will put a counter in the end of the name.
+ String newName = originalTableName;
+ int count = 1;
+ for (String name : tableNameForClasses)
+ {
+ if (name.equals(newName))
+ {
+ count++;
+ newName = originalTableName + count;
+ }
+ }
+
+ tableNameForClasses.add(newName);
+
+ return newName;
+ }
+
+ public String getDbName()
+ {
+ return dbName;
+ }
+
+ public void setDbName(String dbName)
+ {
+ this.dbName = dbName;
+ }
+
+ /**
+ * @return The list of Uri constants to access items on query from Content Provider.
+ */
+ @SuppressWarnings(
+ {
+ "unchecked", "rawtypes"
+ })
+ private String getUriConstants()
+ {
+ StringBuilder buf = new StringBuilder();
+ String declaration = "public static final Uri ";
+ Table table = getTable();
+ if (table != null)
+ {
+ EList columns = getTable().getColumns();
+ if ((columns != null) && (columns.size() > 0))
+ {
+ ListIterator<Column> columnsIter = columns.listIterator();
+ while (columnsIter.hasNext())
+ {
+ Column column = columnsIter.next();
+ String uriVarName = column.getName().toUpperCase() + "_FIELD_CONTENT_URI";
+ String parseStatement = "";
+ if (column.getName().equalsIgnoreCase("_ID"))
+ {
+ parseStatement =
+ "Uri.parse(\"content://\"+AUTHORITY+\"/\"+TABLE_NAME.toLowerCase());";
+ }
+ else
+ {
+ parseStatement =
+ "Uri.parse(\"content://\"+AUTHORITY+\"/\"+TABLE_NAME.toLowerCase()+\"/"
+ + column.getName().toLowerCase() + "\");";
+ }
+ String uriDeclarationStatement =
+ declaration + uriVarName + " = " + parseStatement;
+ buf.append(uriDeclarationStatement + "\n");
+ }
+ }
+ }
+ return buf.toString();
+ }
+
+ /**
+ * @return The list of indexes to access items on query from Content Provider.
+ */
+ @SuppressWarnings(
+ {
+ "unchecked", "rawtypes"
+ })
+ private String getConstIndexesProjectMap()
+ {
+ int initialIndex = 1;
+ StringBuilder buf = new StringBuilder();
+ String declaration = "private static final int ";
+ String tableUpperCase = getTableName().toUpperCase();
+ String constSt = declaration + tableUpperCase + " = " + initialIndex + ";";
+ buf.append(constSt + "\n");
+ initialIndex++;
+ Table table = getTable();
+ if (table != null)
+ {
+ EList columns = getTable().getColumns();
+ if ((columns != null) && (columns.size() > 0))
+ {
+ ListIterator<Column> columnsIter = columns.listIterator();
+ while (columnsIter.hasNext())
+ {
+ Column column = columnsIter.next();
+ String constStByTable =
+ declaration + tableUpperCase + "_" + column.getName().toUpperCase()
+ + " = " + initialIndex + ";";
+ buf.append(constStByTable + "\n");
+ initialIndex++;
+ }
+ }
+ }
+ return buf.toString();
+ }
+
+ /**
+ * Default sort order will be set to the first primary key column.
+ * If there is not, it will set as the first column name found.
+ * @return java code to declare a sql string to order select results using the default column to sort the rows.
+ */
+ @SuppressWarnings(
+ {
+ "unchecked", "rawtypes"
+ })
+ private String getDefaultSortOrder()
+ {
+ Column firstColumn = null;
+ Column chosenDefaultColumn = null;
+ StringBuilder buf = new StringBuilder();
+ String declaration = "public static final String DEFAULT_SORT_ORDER = ";
+ Table table = this.getTable();
+ if (table != null)
+ {
+ EList columns = table.getColumns();
+ if ((columns != null) && (columns.size() > 0))
+ {
+ ListIterator<Column> columnsIter = columns.listIterator();
+ while (columnsIter.hasNext())
+ {
+ int index = columnsIter.nextIndex();
+ Column column = columnsIter.next();
+ if (index == 0)
+ {
+ //if no column is primary key, set it first column as default to sort
+ firstColumn = column;
+ }
+ if (column.isPartOfPrimaryKey())
+ {
+ chosenDefaultColumn = column;
+ break;
+ }
+ }
+ }
+ }
+ if (chosenDefaultColumn == null)
+ {
+ chosenDefaultColumn = firstColumn;
+ }
+ String defaultSortSt = declaration + "\"" + chosenDefaultColumn.getName() + " ASC\";";
+ buf.append(defaultSortSt + "\n");
+ return buf.toString();
+ }
+
+ /**
+ * @return The URLs to be used on queries.
+ */
+ @SuppressWarnings(
+ {
+ "unchecked", "rawtypes"
+ })
+ private String getQueryUrlCases()
+ {
+ StringBuilder buf = new StringBuilder();
+ String caseSt = "case " + getTableName().toUpperCase() + ":";
+ buf.append(caseSt + "\n");
+ String setTablesSt = "qb.setTables(TABLE_NAME);";
+ String setProjectionSt =
+ "qb.setProjectionMap(" + getTableName().toUpperCase() + "_PROJECTION_MAP);";
+ String breakSt = "break;";
+ buf.append("\t" + setTablesSt + "\n");
+ buf.append("\t" + setProjectionSt + "\n");
+ buf.append("\t" + breakSt + "\n");
+
+ Table table = getTable();
+ if (table != null)
+ {
+ EList columns = getTable().getColumns();
+ if ((columns != null) && (columns.size() > 0))
+ {
+ ListIterator<Column> columnsIter = columns.listIterator();
+ while (columnsIter.hasNext())
+ {
+ Column column = columnsIter.next();
+ String caseColumnSt =
+ "case " + getTableName().toUpperCase() + "_"
+ + column.getName().toUpperCase() + ":";
+ buf.append(caseColumnSt + "\n");
+ String setTablesColumnsSt = "qb.setTables(TABLE_NAME);";
+ buf.append("\t" + setTablesColumnsSt + "\n");
+ String setAppendWhereSt = "";
+ if (column.getName().equalsIgnoreCase("_ID"))
+ {
+ //obrigatory primary key on android
+ setAppendWhereSt =
+ "qb.appendWhere(\"" + column.getName().toLowerCase()
+ + "=\" + url.getPathSegments().get(1));";
+ }
+ else
+ {
+ setAppendWhereSt =
+ "qb.appendWhere(\"" + column.getName().toLowerCase()
+ + "='\" + url.getPathSegments().get(2)+\"'\");";
+ }
+ buf.append("\t" + setAppendWhereSt + "\n");
+ buf.append("\t" + breakSt + "\n");
+ }
+ }
+ }
+ return buf.toString();
+ }
+
+ /**
+ * Get types for multiple (records for the entire Bean - use dir cursor)
+ * or single items (simple columns into the Bean - use item cursor)
+ */
+ @SuppressWarnings(
+ {
+ "unchecked", "rawtypes"
+ })
+ private String getTypeRecordCases(String packageName)
+ {
+ StringBuilder buf = new StringBuilder();
+ String caseSt = "case " + getTableName().toUpperCase() + ":";
+ buf.append(caseSt + "\n");
+ String returnMultipleSt =
+ "return " + "\"vnd.android.cursor.dir/vnd." + packageName + "."
+ + getTableName().toLowerCase() + "\"" + ";";
+ buf.append("\t" + returnMultipleSt + "\n");
+
+ Table table = getTable();
+ if (table != null)
+ {
+ EList columns = getTable().getColumns();
+ if ((columns != null) && (columns.size() > 0))
+ {
+ ListIterator<Column> columnsIter = columns.listIterator();
+ while (columnsIter.hasNext())
+ {
+ Column column = columnsIter.next();
+ String caseColumnSt =
+ "case " + getTableName().toUpperCase() + "_"
+ + column.getName().toUpperCase() + ":";
+ buf.append(caseColumnSt + "\n");
+ String returnSingleSt =
+ "return " + "\"vnd.android.cursor.item/vnd." + packageName + "."
+ + getTableName().toLowerCase() + "\"" + ";";
+ buf.append("\t" + returnSingleSt + "\n");
+ }
+ }
+ }
+ return buf.toString();
+ }
+
+ /**
+ * @return The insert statements generated based on table columns.
+ */
+ private String getInsertStatementCases()
+ {
+ //Empty for now - Let method because users can ask something in the future
+ return "";
+ }
+
+ /**
+ *
+ * @return The delete statements generated based on table columns.
+ */
+ @SuppressWarnings(
+ {
+ "unchecked", "rawtypes"
+ })
+ private String getDeleteStatementCases()
+ {
+ StringBuilder buf = new StringBuilder();
+ String caseSt = "case " + getTableName().toUpperCase() + ":";
+ buf.append(caseSt + "\n");
+ String countSt = "count = mDB.delete(TABLE_NAME, where, whereArgs);";
+ buf.append("\t" + countSt + "\n");
+ buf.append("\t" + "break;" + "\n");
+
+ Table table = getTable();
+ if (table != null)
+ {
+ EList columns = getTable().getColumns();
+ if ((columns != null) && (columns.size() > 0))
+ {
+ ListIterator<Column> columnsIter = columns.listIterator();
+ while (columnsIter.hasNext())
+ {
+ Column column = columnsIter.next();
+ String caseColumnSt =
+ "case " + getTableName().toUpperCase() + "_"
+ + column.getName().toUpperCase() + ":";
+ buf.append(caseColumnSt + "\n");
+ String segmentSt = "";
+ if (column.getName().equalsIgnoreCase("_ID"))
+ {
+ segmentSt = "segment = url.getPathSegments().get(1);";
+ }
+ else
+ {
+ //non-id items
+ segmentSt = "segment = \"'\" + url.getPathSegments().get(2) + \"'\";";
+ }
+ buf.append("\t" + segmentSt + "\n");
+ String countStByColumn =
+ "count = mDB.delete(TABLE_NAME, \""
+ + column.getName().toLowerCase()
+ + "=\" + segment + (!TextUtils.isEmpty(where) ? \" AND (\" + where + ')' : \"\"), whereArgs);";
+ buf.append("\t" + countStByColumn + "\n");
+ buf.append("\t" + "break;" + "\n");
+ }
+ }
+ }
+ return buf.toString();
+ }
+
+ /**
+ * @return Update statements generated based on table columns.
+ */
+ @SuppressWarnings(
+ {
+ "unchecked", "rawtypes"
+ })
+ private String getUpdateStatementCases()
+ {
+ StringBuilder buf = new StringBuilder();
+ String caseSt = "case " + getTableName().toUpperCase() + ":";
+ buf.append(caseSt + "\n");
+ String countSt = "count = mDB.update(TABLE_NAME, values, where, whereArgs);";
+ buf.append("\t" + countSt + "\n");
+ buf.append("\t" + "break;" + "\n");
+
+ Table table = getTable();
+ if (table != null)
+ {
+ EList columns = getTable().getColumns();
+ if ((columns != null) && (columns.size() > 0))
+ {
+ ListIterator<Column> columnsIter = columns.listIterator();
+ while (columnsIter.hasNext())
+ {
+ Column column = columnsIter.next();
+ String caseColumnSt =
+ "case " + getTableName().toUpperCase() + "_"
+ + column.getName().toUpperCase() + ":";
+ buf.append(caseColumnSt + "\n");
+ String segmentSt = "";
+ if (column.getName().equalsIgnoreCase("_ID"))
+ {
+ segmentSt = "segment = url.getPathSegments().get(1);";
+ }
+ else
+ {
+ //non-id items
+ segmentSt = "segment = \"'\" + url.getPathSegments().get(2) + \"'\";";
+ }
+ buf.append("\t" + segmentSt + "\n");
+ String countStByColumn =
+ "count = mDB.update(TABLE_NAME, values, \""
+ + column.getName().toLowerCase()
+ + "=\" + segment + (!TextUtils.isEmpty(where) ? \" AND (\" + where + ')' : \"\"), whereArgs);";
+ buf.append("\t" + countStByColumn + "\n");
+ buf.append("\t" + "break;" + "\n");
+ }
+ }
+ }
+ return buf.toString();
+ }
+
+ /**
+ * @return The matcher to recognize the pattern developer is trying to query the data from ContentProvider.
+ */
+ @SuppressWarnings(
+ {
+ "unchecked", "rawtypes"
+ })
+ private String getUrlMatcherStatementCases()
+ {
+ StringBuilder buf = new StringBuilder();
+ buf.append("URL_MATCHER.addURI(AUTHORITY, TABLE_NAME.toLowerCase(), "
+ + getTableName().toUpperCase() + ");" + "\n");
+
+ Table table = getTable();
+ if (table != null)
+ {
+ EList columns = getTable().getColumns();
+ if ((columns != null) && (columns.size() > 0))
+ {
+ ListIterator<Column> columnsIter = columns.listIterator();
+
+ while (columnsIter.hasNext())
+ {
+ Column column = columnsIter.next();
+ if (column.getName().equalsIgnoreCase("_ID"))
+ {
+ buf.append("URL_MATCHER.addURI(AUTHORITY, TABLE_NAME.toLowerCase()"
+ + "+\"/#\", " + getTableName().toUpperCase() + "_"
+ + column.getName().toUpperCase() + ");" + "\n");
+ }
+ else
+ {
+ //non-id items
+ buf.append("URL_MATCHER.addURI(AUTHORITY, TABLE_NAME.toLowerCase()"
+ + "+\"/" + column.getName().toLowerCase() + "\"+\"/*\", "
+ + getTableName().toUpperCase() + "_"
+ + column.getName().toUpperCase() + ");" + "\n");
+ }
+ }
+ }
+ }
+ return buf.toString();
+ }
+
+ @SuppressWarnings(
+ {
+ "rawtypes", "unchecked"
+ })
+ private String getConstContentValuesKeys()
+ {
+ StringBuilder buf = new StringBuilder();
+ String declaration = "\tpublic static final String ";
+ Table table = this.getTable();
+ if (table != null)
+ {
+ EList columns = table.getColumns();
+ if ((columns != null) && (columns.size() > 0))
+ {
+ ListIterator<Column> columnsIter = columns.listIterator();
+ while (columnsIter.hasNext())
+ {
+ Column column = columnsIter.next();
+ String contValuesKeysSt =
+ declaration + column.getName().toUpperCase() + " = \""
+ + column.getName() + "\";";
+ buf.append(contValuesKeysSt + "\n");
+ }
+ }
+ }
+ return buf.toString();
+ }
+
+ /**
+ * Initialize maps to get table items into the static constructor from the Provider.
+ */
+ @SuppressWarnings(
+ {
+ "unchecked", "rawtypes"
+ })
+ private String getProjectionMapStatementCases()
+ {
+ StringBuilder buf = new StringBuilder();
+
+ Table table = getTable();
+ if (table != null)
+ {
+ EList columns = getTable().getColumns();
+ if ((columns != null) && (columns.size() > 0))
+ {
+ ListIterator<Column> columnsIter = columns.listIterator();
+ while (columnsIter.hasNext())
+ {
+ Column column = columnsIter.next();
+ buf.append(getTableName().toUpperCase() + "_PROJECTION_MAP.put("
+ + column.getName().toUpperCase() + "," + "\""
+ + column.getName().toLowerCase() + "\");" + "\n");
+ }
+ }
+ }
+ return buf.toString();
+ }
+}
diff --git a/src/plugins/android.codeutils/src/com/motorola/studio/android/codeutils/db/utils/DatabaseUtils.java b/src/plugins/android.codeutils/src/com/motorola/studio/android/codeutils/db/utils/DatabaseUtils.java
new file mode 100644
index 0000000..644999a
--- /dev/null
+++ b/src/plugins/android.codeutils/src/com/motorola/studio/android/codeutils/db/utils/DatabaseUtils.java
@@ -0,0 +1,1103 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.motorola.studio.android.codeutils.db.utils;
+
+import static com.motorola.studio.android.common.log.StudioLogger.info;
+
+import java.io.BufferedWriter;
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IFolder;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.SubMonitor;
+import org.eclipse.datatools.connectivity.ConnectionProfileException;
+import org.eclipse.datatools.connectivity.IConnection;
+import org.eclipse.datatools.connectivity.IConnectionProfile;
+import org.eclipse.datatools.connectivity.IManagedConnection;
+import org.eclipse.datatools.connectivity.ProfileManager;
+import org.eclipse.datatools.connectivity.drivers.DriverManager;
+import org.eclipse.datatools.connectivity.sqm.core.connection.ConnectionInfo;
+import org.eclipse.datatools.connectivity.sqm.core.connection.DatabaseConnectionRegistry;
+import org.eclipse.datatools.connectivity.sqm.internal.core.connection.ConnectionInfoImpl;
+import org.eclipse.datatools.modelbase.sql.schema.Catalog;
+import org.eclipse.datatools.modelbase.sql.schema.Database;
+import org.eclipse.datatools.modelbase.sql.schema.Schema;
+import org.eclipse.datatools.modelbase.sql.tables.Table;
+import org.eclipse.datatools.sqltools.data.internal.ui.editor.TableDataEditor;
+import org.eclipse.emf.common.util.EList;
+import org.eclipse.jdt.core.IJavaProject;
+import org.eclipse.jdt.core.JavaCore;
+import org.eclipse.jdt.core.formatter.CodeFormatter;
+import org.eclipse.jdt.internal.corext.util.CodeFormatterUtil;
+import org.eclipse.jface.text.Document;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.text.edits.TextEdit;
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.IWorkbenchPage;
+
+import com.motorola.studio.android.codeutils.CodeUtilsActivator;
+import com.motorola.studio.android.codeutils.db.actions.ContentProviderGeneratorByTable;
+import com.motorola.studio.android.codeutils.i18n.CodeUtilsNLS;
+import com.motorola.studio.android.common.CommonPlugin;
+import com.motorola.studio.android.common.exception.AndroidException;
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.common.log.UsageDataConstants;
+import com.motorola.studio.android.common.utilities.EclipseUtils;
+import com.motorola.studio.android.common.utilities.FileUtil;
+import com.motorola.studio.android.db.deployment.DatabaseDeployer;
+import com.motorola.studio.android.manifest.AndroidProjectManifestFile;
+import com.motorola.studio.android.model.manifest.AndroidManifestFile;
+import com.motorola.studio.android.model.manifest.dom.ManifestNode;
+
+@SuppressWarnings("restriction")
+public class DatabaseUtils
+{
+
+ // Candidate folders that may contain .db resources
+ public static final String ASSESTS_FOLDER = "assets"; //$NON-NLS-1$
+
+ public static final String RAW_FOLDER = "raw"; //$NON-NLS-1$
+
+ public static final String JDBC_SQLITE_PREFIX = "jdbc:sqlite:";
+
+ public static final String URL_PROPERTY = "org.eclipse.datatools.connectivity.db.URL"; //$NON-NLS-1$
+
+ public static final String DBNAME_PROPERTY =
+ "org.eclipse.datatools.connectivity.db.databaseName"; //$NON-NLS-1$
+
+ // private static final String DEVICENAME_PROPERTY =
+ // "org.eclipse.datatools.connectivity.db.deviceName"; //$NON-NLS-1$
+
+ // private static final String SERIAL_PROPERTY = "com.motorola.studio.db.serialProperty"; //$NON-NLS-1$
+
+ // private static final String APPNAME_PROPERTY = "com.motorola.studio.db.appNameProperty"; //$NON-NLS-1$
+
+ public static final String REMOTEPATH_PROPERTY = "com.motorola.studio.db.remotePathProperty"; //$NON-NLS-1$
+
+ public static final String LOCALPATH_PROPERTY = "com.motorola.studio.db.localPathProperty"; //$NON-NLS-1$
+
+ public static final String TYPE_PROPERTY = "org.eclipse.datatools.connectivity.db.TYPE";
+
+ public static final String PROVIDER_ID =
+ "org.eclipse.datatools.enablement.sqlite.connectionProfile"; //$NON-NLS-1$
+
+ private static final String TEMPLATE_ID =
+ "org.eclipse.datatools.enablement.sqlite.3_5_9.driver"; //$NON-NLS-1
+
+ public static final String DB_FOLDER = "assets";
+
+ public static Set<IConnectionProfile> profilesBeingDisconnected =
+ new HashSet<IConnectionProfile>();
+
+ /**
+ * Check if a file is a valid SQLite Database by analyzing the first 16 bytes block,
+ * since every SQLite database begins with the byte sequence:
+ *
+ * 0x53 0x51 0x4c 0x69 0x74 0x65 0x20 0x66 0x6f 0x72 0x6d 0x61 0x74 0x20 0x33 0x00
+ *
+ * Source: http://www.sqlite.org/fileformat.html#database_header
+ *
+ * @param databaseFile The file to be checked.
+ * @return True if {@code databaseFile} is a valid SQLite database file. Otherwise, returns false.
+ * @throws IOException Thrown if there were errors reading the file.
+ */
+ public static boolean isValidSQLiteDatabase(File databaseFile) throws IOException
+ {
+ boolean result = true;
+
+ final int BYTE_ARRAY_SIZE = 16;
+
+ byte[] headerByteArray = new byte[16];
+ headerByteArray[0] = 0x53;
+ headerByteArray[1] = 0x51;
+ headerByteArray[2] = 0x4c;
+ headerByteArray[3] = 0x69;
+ headerByteArray[4] = 0x74;
+ headerByteArray[5] = 0x65;
+ headerByteArray[6] = 0x20;
+ headerByteArray[7] = 0x66;
+ headerByteArray[8] = 0x6f;
+ headerByteArray[9] = 0x72;
+ headerByteArray[10] = 0x6d;
+ headerByteArray[11] = 0x61;
+ headerByteArray[12] = 0x74;
+ headerByteArray[13] = 0x20;
+ headerByteArray[14] = 0x33;
+ headerByteArray[15] = 0x00;
+
+ byte[] fileByteArray = new byte[BYTE_ARRAY_SIZE];
+ FileInputStream fis = null;
+
+ try
+ {
+ fis = new FileInputStream(databaseFile);
+ fis.read(fileByteArray);
+ }
+ finally
+ {
+ if (fis != null)
+ {
+ try
+ {
+ fis.close();
+ }
+ catch (IOException e)
+ {
+ StudioLogger
+ .error("Could not close stream while checking if a file is a sqlite valid database"
+ + e.getMessage());
+ }
+ }
+ }
+
+ ByteArrayInputStream bais = null;
+ try
+ {
+ bais = new ByteArrayInputStream(fileByteArray);
+ for (int aux = 0; aux < BYTE_ARRAY_SIZE; aux++)
+ {
+ int myByte = bais.read();
+ if (myByte != headerByteArray[aux])
+ {
+ result = false;
+ }
+ }
+ }
+ finally
+ {
+ if (bais != null)
+ {
+ try
+ {
+ bais.close();
+ }
+ catch (IOException e)
+ {
+ StudioLogger
+ .error("Could not close stream while checking if a file is a sqlite valid database"
+ + e.getMessage());
+ }
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Retrieve a collections of .db resources declared inside an IProject in the workspace.
+ * @param project The project to be considered.
+ */
+ public static Set<IFile> getDbFilesFromProject(IProject project)
+ {
+ // Result containing the .db resources
+ HashSet<IFile> dbCollection = new HashSet<IFile>();
+
+ // List of candidate folder to be inspected
+ HashSet<IFolder> folderCollection = new HashSet<IFolder>();
+
+ // First, retrieve and check if the folders likely to contain the .db files exist
+ folderCollection.add(project.getFolder(ASSESTS_FOLDER));
+ folderCollection.add(project.getFolder(RAW_FOLDER));
+
+ // Iterate through the folders and retrieve the .db IFiles
+ for (IFolder folder : folderCollection)
+ {
+ if (folder.exists())
+ {
+ // Get a list of files in the folder and try to find the .db files
+ try
+ {
+ for (IResource resource : folder.members())
+ {
+ // Check if it's a file
+ if (resource.getType() == IResource.FILE)
+ {
+ IFile file = (IFile) resource;
+
+ // Check if the file is a valid database
+ try
+ {
+ if (file.exists()
+ & isValidSQLiteDatabase(file.getLocation().toFile()))
+ {
+ dbCollection.add(file);
+ }
+ }
+ catch (IOException e)
+ {
+ StudioLogger
+ .warn(DatabaseUtils.class,
+ "It was not possible verify if the file is a valid SQLite database",
+ e);
+ }
+ }
+ }
+ }
+ catch (CoreException e)
+ {
+ // Log error
+ StudioLogger.error(DatabaseUtils.class,
+ "An error ocurred while looking for .db files.", e); //$NON-NLS-1$
+ }
+ }
+ }
+ return dbCollection;
+
+ }
+
+ /**
+ * @param database The database that will have its table retrieved.
+ * @return All tables in the {@code database}.
+ * */
+ @SuppressWarnings(
+ {
+ "unchecked", "rawtypes"
+ })
+ public static Set<Table> getTables(Database database)
+ {
+ HashSet<Table> tableSet = new HashSet<Table>();
+ ListIterator<Catalog> catalogIter = database.getCatalogs().listIterator();
+ while (catalogIter.hasNext())
+ {
+ Catalog catalog = catalogIter.next();
+ EList schemas = catalog.getSchemas();
+ if ((schemas != null) && (schemas.size() > 0))
+ {
+ ListIterator<Schema> schemasIter = schemas.listIterator();
+ while (schemasIter.hasNext())
+ {
+ Schema schema = schemasIter.next();
+ EList tables = schema.getTables();
+ if ((tables != null) && (tables.size() > 0))
+ {
+ ListIterator<Table> tablesIter = tables.listIterator();
+ while (tablesIter.hasNext())
+ {
+ tableSet.add(tablesIter.next());
+ }
+ }
+ }
+ }
+ }
+ return tableSet;
+ }
+
+ /**
+ * Creates Database management classes
+ *
+ * @param project Target Project
+ * @param databaseName Database name
+ * @param generateSQLOpenHelperClases <code>true</code> for generating SQL Open Helper classes
+ * @param generateContentProviderClasses <code>true</code> in case
+ * it is desired to create Content Provider classes, <code>false</code> otherwise
+ * @param openHelperPackageName Open Helper Package Name
+ * @param contentProvidersPackageName Content provider Package Name
+ * @param sqlOpenHelperClassName SQL Open Helper Class name
+ * @param overrideContentProviders <code>true</code> in order to override the Content Providers
+ * @param generateDAO <code>true</code> in case one wishes to create DAO classes, <code>false</code>
+ * otherwise
+ * @param monitor Monitor of the process
+ *
+ * @throws AndroidException Exception thrown when there are problems handling Android files
+ * @throws CoreException Exception thrown when there are problems handling Eclipse
+ * @throws IOException Exception thrown when there are I/O problems with the Database
+ * @throws ConnectionProfileException Exception thrown when there are problems handling the database connection
+ * @throws SQLException Exception thrown when there are errors dealing with SQL
+ */
+ public static void createDatabaseManagementClasses(IProject project, String databaseName,
+ boolean generateSQLOpenHelperClases, boolean generateContentProviderClasses,
+ String openHelperPackageName, String contentProvidersPackageName,
+ String sqlOpenHelperClassName, boolean overrideContentProviders, boolean generateDAO,
+ IProgressMonitor monitor, boolean showSuccessDialog) throws IOException, CoreException,
+ AndroidException, ConnectionProfileException, SQLException
+ {
+ // get sub monitor
+ SubMonitor subMonitor = SubMonitor.convert(monitor, 10);
+
+ // begin
+ subMonitor.beginTask(null, 10);
+
+ // create parameters and copy database deployer class to the android project
+ Map<String, String> dbParameters = new HashMap<String, String>();
+
+ AndroidManifestFile androidManifestFile =
+ AndroidProjectManifestFile.getFromProject(project);
+
+ ManifestNode manifestNode =
+ androidManifestFile != null ? androidManifestFile.getManifestNode() : null;
+
+ String appNamespace = manifestNode.getPackageName().toLowerCase();
+ String packageName = ""; //$NON-NLS-1$
+ if (openHelperPackageName != null)
+ {
+ //user-defined package for deployer
+ packageName = openHelperPackageName;
+ }
+ else
+ {
+ //use default package for deployer
+ packageName = appNamespace + ".deployer"; //$NON-NLS-1$
+ }
+
+ dbParameters.put(DatabaseDeployer.DATABASE_NAME, databaseName);
+ dbParameters.put(DatabaseDeployer.APPLICATION_DATABASE_NAMESPACE, appNamespace);
+ dbParameters.put(DatabaseDeployer.ANDROID_PROJECT_PACKAGE_NAME, packageName);
+ dbParameters.put(DatabaseDeployer.DATABASE_HELPER_CLASS_NAME, sqlOpenHelperClassName);
+
+ subMonitor.worked(1);
+
+ if (generateSQLOpenHelperClases)
+ {
+ DatabaseDeployer.copyDataBaseDeployerClassToProject(project, dbParameters,
+ subMonitor.newChild(2));
+ StudioLogger.info("Finished creating Deployer classes"); //$NON-NLS-1$
+
+ // Creates UDC log reporting that an OpenHelper class was created
+ StudioLogger.collectUsageData(UsageDataConstants.WHAT_OPENHELPER, //$NON-NLS-1$
+ UsageDataConstants.KIND_OPENHELPER, "generated SQLOpenHelper class", //$NON-NLS-1$
+ CodeUtilsActivator.PLUGIN_ID, CodeUtilsActivator.getDefault().getBundle()
+ .getVersion().toString());
+
+ }
+
+ // variables for connecting to the database
+ IConnectionProfile profile = null;
+ boolean isConnectionOK = false;
+ boolean isConnectionStartedHere = false;
+ IStatus status = null;
+ try
+ {
+ // get local database profile
+ profile = DatabaseUtils.getLocalDbProfile(project.getName(), databaseName);
+ // continue in case there is a profile
+ if (profile != null)
+ {
+ // assert driver
+ DatabaseUtils.assertDriverExistsAtModel();
+ // if the connection is established, set the flag of the connection to true
+ if (profile.getConnectionState() == IConnectionProfile.CONNECTED_STATE)
+ {
+ // set the flag
+ isConnectionOK = true;
+ }
+ // the connection is not established, therefore connect
+ else if (profile.getConnectionState() == IConnectionProfile.DISCONNECTED_STATE)
+ {
+ // execute connection and get the status
+ status = profile.connectWithoutJob();
+ // state that the connection was established here, thus the disconnection is required
+ isConnectionStartedHere = true;
+ // set the connection flag to OK
+ if ((status != null) && (status.getCode() == IStatus.OK))
+ {
+ isConnectionOK = true;
+ }
+ }
+
+ subMonitor.worked(4);
+
+ // proceed in case the connection is established and OK
+ if (isConnectionOK)
+ {
+ String appendMsg = CodeUtilsNLS.DATABASE_DEPLOY_SUCCESS_MESSAGE;
+ boolean createMetadata = true;
+ String query = "SELECT * FROM \"android_metadata\""; //$NON-NLS-1$
+ ResultSet rs = null;
+
+ try
+ {
+ rs = DatabaseUtils.executeSqliteQuery(profile, query);
+
+ if (rs != null)
+ {
+ if (rs.next())
+ {
+ createMetadata = false;
+ }
+ }
+ }
+ finally
+ {
+ if (rs != null)
+ {
+ rs.close();
+ }
+ }
+ if (createMetadata)
+ {
+ // create the tables and insert data
+ DatabaseUtils
+ .executeSqliteStatement(profile,
+ "CREATE TABLE IF NOT EXISTS \"android_metadata\" (\"locale\" TEXT DEFAULT 'en_US');"); //$NON-NLS-1$
+ DatabaseUtils
+ .executeSqliteStatement(profile,
+ "insert into DEFAULT.ANDROID_METADATA (\"locale\") values('en_US');"); //$NON-NLS-1$
+ }
+
+ subMonitor.worked(2);
+
+ if (generateContentProviderClasses)
+ {
+ Database database = DatabaseUtils.getDatabaseForProfile(profile);
+ DatabaseUtils.createPersistenceClassesForDatabase(project, database, false,
+ false, contentProvidersPackageName, packageName,
+ sqlOpenHelperClassName, overrideContentProviders, generateDAO);
+
+ subMonitor.worked(2);
+ }
+
+ subMonitor.worked(1);
+
+ if (showSuccessDialog)
+ {
+ // show success message
+ EclipseUtils.showInformationDialog(
+ CodeUtilsNLS.DATABASE_DEPLOY_SUCCESS_MESSAGE_TITLE, appendMsg);
+ }
+ }
+ else
+ {
+ // retrieve status error message
+ String errorMessage = ""; //$NON-NLS-1$
+ Throwable exception = null;
+ if (status != null)
+ {
+ exception = status.getException();
+ if (exception != null)
+ {
+ // get error message
+ errorMessage = exception.getLocalizedMessage();
+ }
+ }
+ // print and log database connection error message
+ StudioLogger.error(DatabaseUtils.class,
+ CodeUtilsNLS.DATABASE_DEPLOY_ERROR_CONNECTING_DATABASE, exception);
+ EclipseUtils.showErrorDialog(
+ CodeUtilsNLS.DATABASE_DEPLOY_CREATING_ANDROID_METADATA_TABLE,
+ CodeUtilsNLS.DATABASE_DEPLOY_ERROR_CONNECTING_DATABASE
+ + ((errorMessage != null) && (errorMessage.length() > 0)
+ ? "\r\n" + errorMessage : ""), status); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+ }
+ }
+ finally
+ {
+ // in case the connection was established, here, close it
+ if (isConnectionStartedHere && (profile != null // there is a profile
+ ) && (profile.getConnectionState() == IConnectionProfile.CONNECTED_STATE // the connection is up and running
+ ) && DatabaseUtils.prepareConnectionToClose(profile, false)) // prepare the connection to close
+ {
+ // close the connection
+ profile.disconnect(null);
+ }
+ }
+ }
+
+ /**
+ * @return The connection profile to the given database. Create the connection profile if it does not exist yet.
+ * */
+ public static IConnectionProfile getLocalDbProfile(String projectName, String databaseName)
+ throws ConnectionProfileException
+ {
+ ProfileManager pm = ProfileManager.getInstance();
+ String profileName = projectName + "." + databaseName; //$NON-NLS-1$
+ IConnectionProfile profile = pm.getProfileByName(profileName);
+ if (profile == null)
+ {
+ String driverPath = CommonPlugin.getDefault().getDriverPath();
+ Properties prop = DatabaseUtils.getBaseConnProperties(driverPath, databaseName);
+
+ String providerId = DatabaseUtils.PROVIDER_ID;
+ profile = pm.createProfile(profileName, "", providerId, prop); //$NON-NLS-1$
+
+ String dbPath =
+ ResourcesPlugin.getWorkspace().getRoot().getProject(projectName)
+ .getFolder(DB_FOLDER).getFile(databaseName).getLocation().toString();
+ prop.put(DatabaseUtils.URL_PROPERTY, DatabaseUtils.JDBC_SQLITE_PREFIX + dbPath); //$NON-NLS-1$
+ prop.put(DatabaseUtils.LOCALPATH_PROPERTY, dbPath);
+ profile.setBaseProperties(prop);
+ }
+ return profile;
+ }
+
+ /**
+ * Return the following set of properties to be used in a connection profile:
+ * <ul>
+ * <li>org.eclipse.datatools.connectivity.db.vendor = "SQLITE"</li>
+ * <li>org.eclipse.datatools.connectivity.db.password = ""</li>
+ * <li>org.eclipse.datatools.connectivity.driverDefinitionID = "DriverDefn.org.eclipse.datatools.enablement.sqlite.3_5_9.driver." + {@link CommonPlugin#JDBC_DRIVER_INSTANCE_NAME}</li>
+ * <li>org.eclipse.datatools.connectivity.drivers.defnType = "org.eclipse.datatools.enablement.sqlite.3_5_9.driver"</li>
+ * <li>org.eclipse.datatools.connectivity.db.savePWD = "false"</li>
+ * <li>org.eclipse.datatools.connectivity.db.connectionProperties = ""</li>
+ * <li>org.eclipse.datatools.connectivity.db.version = "3.5.9"</li>
+ * <li>org.eclipse.datatools.connectivity.db.databaseName = {@code dbName}</li>
+ * <li>jarList = {@code driverPath}</li>
+ * <li>org.eclipse.datatools.connectivity.db.username = ""</li>
+ * <li>org.eclipse.datatools.connectivity.db.driverClass = "org.sqlite.JDBC"</li>
+ * </ul>
+ * */
+ public static Properties getBaseConnProperties(String driverPath, String dbName)
+ {
+ Properties prop = new Properties();
+ prop.put("org.eclipse.datatools.connectivity.db.vendor", "SQLITE"); //$NON-NLS-1$ //$NON-NLS-2$
+ prop.put("org.eclipse.datatools.connectivity.db.password", ""); //$NON-NLS-1$ //$NON-NLS-2$
+ prop.put("org.eclipse.datatools.connectivity.driverDefinitionID", //$NON-NLS-1$
+ "DriverDefn.org.eclipse.datatools.enablement.sqlite.3_5_9.driver." //$NON-NLS-1$
+ + CommonPlugin.JDBC_DRIVER_INSTANCE_NAME);
+ prop.put("org.eclipse.datatools.connectivity.drivers.defnType", //$NON-NLS-1$
+ "org.eclipse.datatools.enablement.sqlite.3_5_9.driver"); //$NON-NLS-1$
+ prop.put("org.eclipse.datatools.connectivity.db.savePWD", "false"); //$NON-NLS-1$ //$NON-NLS-2$
+ prop.put("org.eclipse.datatools.connectivity.db.connectionProperties", ""); //$NON-NLS-1$ //$NON-NLS-2$
+ prop.put("org.eclipse.datatools.connectivity.db.version", "3.5.9"); //$NON-NLS-1$ //$NON-NLS-2$
+ prop.put(DatabaseUtils.DBNAME_PROPERTY, dbName);
+ prop.put("jarList", driverPath); //$NON-NLS-1$
+ prop.put("org.eclipse.datatools.connectivity.db.username", ""); //$NON-NLS-1$ //$NON-NLS-2$
+ prop.put("org.eclipse.datatools.connectivity.db.driverClass", "org.sqlite.JDBC"); //$NON-NLS-1$ //$NON-NLS-2$
+
+ return prop;
+ }
+
+ /**
+ * Checks if a compatible JDBC driver is registered. If not, registers one.
+ */
+ public static void assertDriverExistsAtModel()
+ {
+ DriverManager driverMan = DriverManager.getInstance();
+ String allDrivers = driverMan.getFullJarList();
+ String driverPath = CommonPlugin.getDefault().getDriverPath();
+ if ((allDrivers == null) || (!allDrivers.contains(driverPath)))
+ {
+ String templateId = DatabaseUtils.TEMPLATE_ID;
+ driverMan.createNewDriverInstance(templateId, CommonPlugin.JDBC_DRIVER_INSTANCE_NAME,
+ driverPath);
+ info("Created a MOTODEV Studio JDBC driver instance at Data Tools."); //$NON-NLS-1$
+ }
+ }
+
+ /**
+ * Executes Sqlite query and return a {@link ResultSet}.
+ * @param profile The profile representing the database to be queried.
+ * @param query SQL statement (select).
+ * @return SQL Results (columns and values).
+ */
+ public static ResultSet executeSqliteQuery(IConnectionProfile profile, String query)
+ {
+ ResultSet resultSet = null;
+ java.sql.Connection conn = DatabaseUtils.getJavaConnectionForProfile(profile);
+ if (conn != null)
+ {
+ try
+ {
+ java.sql.Statement stmt = conn.createStatement();
+ resultSet = stmt.executeQuery(query);
+ }
+ catch (java.sql.SQLException sqle)
+ {
+ StudioLogger.error(DatabaseUtils.class, "Problems executing query", sqle); //$NON-NLS-1$
+ }
+ }
+ return resultSet;
+ }
+
+ /**
+ * @param profile A datatools connection profile.
+ * @return A java sql connection to make create, insert, delete, update calls to database.
+ */
+ public static java.sql.Connection getJavaConnectionForProfile(IConnectionProfile profile)
+ {
+ IManagedConnection managedConnection =
+ (profile).getManagedConnection("java.sql.Connection"); //$NON-NLS-1$
+ if (managedConnection != null)
+ {
+ return (java.sql.Connection) managedConnection.getConnection().getRawConnection();
+ }
+ return null;
+ }
+
+ /**
+ * Executes Sqlite statements that does not return items (create, update, delete).
+ * @param profile A datatools connection profile.
+ * @param query SQL statement (create, update, delete)
+ * @return Same as {@link Statement#executeUpdate(String)}.
+ */
+ public static int executeSqliteStatement(IConnectionProfile profile, String query)
+ {
+ int count = 0;
+ java.sql.Connection conn = getJavaConnectionForProfile(profile);
+ if (conn != null)
+ {
+ try
+ {
+ java.sql.Statement stmt = conn.createStatement();
+ count = stmt.executeUpdate(query);
+ }
+ catch (java.sql.SQLException sqle)
+ {
+ StudioLogger.error(DatabaseUtils.class,
+ CodeUtilsNLS.DATABASE_ERROR_EXECUTING_STATEMENT, sqle);
+ }
+ }
+ return count;
+ }
+
+ /**
+ * Search for database to get model (tables and colums definitions).
+ * WARNING: check return after proceeding, because if the database is not connected, it will be null.
+ * @param profile A datatools connection profile.
+ * @return A datatools database abstraction.
+ */
+ public static Database getDatabaseForProfile(IConnectionProfile profile)
+ {
+ IManagedConnection managedConnection =
+ (profile)
+ .getManagedConnection("org.eclipse.datatools.connectivity.sqm.core.connection.ConnectionInfo"); //$NON-NLS-1$
+ if (managedConnection != null)
+ {
+ try
+ {
+ IConnection conn = managedConnection.getConnection();
+ if (conn != null)
+ {
+ ConnectionInfo connectionInfo = (ConnectionInfo) conn.getRawConnection();
+ if (connectionInfo != null)
+ {
+ return connectionInfo.getSharedDatabase();
+ }
+ }
+ }
+ catch (Exception e)
+ {
+ StudioLogger.error(DatabaseUtils.class, "Problems executing query", e); //$NON-NLS-1$
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Generates Persistence classes for the tables on db
+ * @param project
+ * @param database
+ * @param addCreateTableStatement add create table statement on helper
+ * @param addDropTableStatementOnUpdate add drop statement on helper
+ * @param persistencePackageName package where to place the persistence classes on project
+ * @param databaseOpenHelperPackageName Database Open Helper Package Name
+ * @param databaseOpenHelperClassName Database open Helper Class Name
+ * @param overrideContentProviders <code>true</code> in case one whishes to override the Content Providers
+ * in case they exist
+ * @param generateDAO false create Content Provider, true create DAO (DAO should NOT be used now)
+ * @throws IOException
+ * @throws CoreException
+ * @throws AndroidException
+ */
+ @SuppressWarnings(
+ {
+ "unchecked", "rawtypes"
+ })
+ public static void createPersistenceClassesForDatabase(IProject project, Database database,
+ boolean addCreateTableStatement, boolean addDropTableStatementOnUpdate,
+ String persistencePackageName, String databaseOpenHelperPackageName,
+ String databaseOpenHelperClassName, boolean overrideContentProviders,
+ boolean generateDAO) throws IOException, CoreException, AndroidException
+ {
+ ListIterator<Catalog> catalogIter = database.getCatalogs().listIterator();
+ while (catalogIter.hasNext())
+ {
+ Catalog catalog = catalogIter.next();
+ EList schemas = catalog.getSchemas();
+ if ((schemas != null) && (schemas.size() > 0))
+ {
+ ListIterator<Schema> schemasIter = schemas.listIterator();
+ while (schemasIter.hasNext())
+ {
+ Schema schema = schemasIter.next();
+ EList tables = schema.getTables();
+
+ //this list will be created to control the classes names. The name for each table will be put in CamelCase and
+ //all underscores "_" will be removed. If two name are equals, we will have to put a counter in the end o the name.
+ //This list will hold all the names that were created.
+ List<String> tableNameForClasses = new ArrayList<String>();
+ if ((tables != null) && (tables.size() > 0))
+ {
+ ListIterator<Table> tablesIter = tables.listIterator();
+ while (tablesIter.hasNext())
+ {
+ Table table = tablesIter.next();
+ StudioLogger.info("Start creating persistence classes for table " //$NON-NLS-1$
+ + table.getName());
+
+ //generate Content Provider
+ DatabaseUtils.generateContentProvider(project, table, database,
+ addCreateTableStatement, addDropTableStatementOnUpdate,
+ overrideContentProviders, persistencePackageName,
+ databaseOpenHelperPackageName, databaseOpenHelperClassName,
+ tableNameForClasses);
+ StudioLogger.collectUsageData("generateContentProviderClasses", //$NON-NLS-1$
+ "database", UsageDataConstants.DESCRIPTION_DEFAULT, //$NON-NLS-1$
+ CodeUtilsActivator.PLUGIN_ID, CodeUtilsActivator.getDefault()
+ .getBundle().getVersion().toString());
+
+ StudioLogger.info("Finished creating persistence classes for table " //$NON-NLS-1$
+ + table.getName());
+ }
+ }
+ }
+ }
+
+ }
+ }
+
+ /**
+ * Generates Content Provider class for the table
+ * @param project
+ * @param table
+ * @param database
+ * @param addCreateTableStatement
+ * @param addDropTableStatementOnUpdate
+ * @param overrideContentProviders <code>true</code> in case one wishes to override the Content Providers
+ * @param persistencePackageName
+ * @param databaseOpenHelperPackageName Database Open Helper package name
+ * @param databaseOpenHelperClassName Database Open Helper class name
+ * @param beanName
+ * @throws IOException
+ * @throws CoreException
+ * @throws AndroidException
+ */
+ public static void generateContentProvider(IProject project, Table table, Database database,
+ boolean addCreateTableStatement, boolean addDropTableStatementOnUpdate,
+ boolean overrideContentProviders, String persistencePackageName,
+ String databaseOpenHelperPackageName, String databaseOpenHelperClassName,
+ List<String> tableNameForClasses) throws IOException, CoreException, AndroidException
+ {
+ String dbName = database.getName();
+ ContentProviderGeneratorByTable contentProviderGeneratorByTable =
+ new ContentProviderGeneratorByTable(table, dbName);
+ contentProviderGeneratorByTable.createContentProvider(project, addCreateTableStatement,
+ addDropTableStatementOnUpdate, overrideContentProviders, persistencePackageName,
+ databaseOpenHelperPackageName, databaseOpenHelperClassName, tableNameForClasses);
+ }
+
+ /**
+ * Prepare a connection profile to close means closing all its opened editor.
+ * If the device related to the connection profile will be disconnected, set {@code willDiscDevice} to true.
+ * @return True if the connection profile can be safely closed. Otherwise, returns false.
+ * */
+ public static boolean prepareConnectionToClose(IConnectionProfile profile,
+ boolean willDiscDevice)
+ {
+ final boolean[] success = new boolean[]
+ {
+ true
+ };
+
+ DatabaseUtils.profilesBeingDisconnected.add(profile);
+ Set<IEditorPart> editorsSet = DatabaseUtils.getEditorsForProfile(profile);
+
+ for (final IEditorPart editor : editorsSet)
+ {
+ final IWorkbenchPage page = EclipseUtils.getPageForEditor(editor);
+ Display.getDefault().syncExec(new Runnable()
+ {
+ @Override
+ public void run()
+ {
+ page.bringToTop(editor);
+ if (!page.closeEditor(editor, true))
+ {
+ success[0] = false;
+ }
+ }
+ });
+
+ if (!success[0])
+ {
+ break;
+ }
+ }
+
+ if (!willDiscDevice)
+ {
+ DatabaseUtils.profilesBeingDisconnected.remove(profile);
+ }
+
+ return success[0];
+ }
+
+ /**
+ * Retrieves the open editors that is used to edit the given profile, if any.
+ *
+ * @param profile
+ * The profile that owns the requested editor.
+ * @return The open editors for the given profile, or <code>null</code>
+ * if there is no opened editor for this profile.
+ */
+ public static Set<IEditorPart> getEditorsForProfile(IConnectionProfile profile)
+ {
+ Collection<IEditorPart> allEditors = EclipseUtils.getAllOpenedEditors();
+ Set<IEditorPart> selectedEditors = new HashSet<IEditorPart>();
+
+ for (IEditorPart e : allEditors)
+ {
+ if (e instanceof TableDataEditor)
+ {
+ TableDataEditor tde = (TableDataEditor) e;
+ Table table = tde.getSqlTable();
+ Catalog cat = table.getSchema().getCatalog();
+ Database database =
+ cat != null ? cat.getDatabase() : table.getSchema().getDatabase();
+ ConnectionInfo connInfo =
+ DatabaseConnectionRegistry.getConnectionForDatabase(database);
+ if (connInfo != null)
+ {
+ IConnectionProfile editorProfile =
+ ((ConnectionInfoImpl) connInfo).getConnectionProfile();
+ if (editorProfile == profile)
+ {
+ selectedEditors.add(e);
+ }
+ }
+ }
+ }
+
+ return selectedEditors;
+ }
+
+ /**
+ * @param projectName The project which contains the database.
+ * @param dbNameWithExtension The complete database name, including its extension.
+ * @return A datatools abstraction for the database of the given project.
+ * */
+ public static Database getDatabase(String projectName, String dbNameWithExtension)
+ throws ConnectionProfileException, IOException
+ {
+ // variables for connecting to the database
+ Database database = null;
+ IConnectionProfile profile = null;
+ boolean isConnectionOK = false;
+ IStatus status = null;
+
+ // get local database profile
+ profile = getLocalDbProfile(projectName, dbNameWithExtension);
+ // continue in case there is a profile
+ if (profile != null)
+ {
+ // assert driver
+ assertDriverExistsAtModel();
+ // if the connection is established, set the flag of the connection to true
+ if (profile.getConnectionState() == IConnectionProfile.CONNECTED_STATE)
+ {
+ // set the flag
+ isConnectionOK = true;
+ }
+ // the connection is not established, therefore connect
+ else if (profile.getConnectionState() == IConnectionProfile.DISCONNECTED_STATE)
+ {
+ // execute connection and get the status
+ status = profile.connectWithoutJob();
+ // set the connection flag to OK
+ if ((status != null) && (status.getCode() == IStatus.OK))
+ {
+ isConnectionOK = true;
+ }
+ }
+
+ // proceed in case the connection is established and OK
+ if (isConnectionOK)
+ {
+ database = getDatabaseForProfile(profile);
+
+ }
+ }
+
+ return database;
+ }
+
+ /**
+ * Given a Database file, provided by an {@link IPath}, in case
+ * it does not exist in the project´s asset´s directory, the file
+ * is to be copied to the mentioned directory. The project is represented
+ * by an {@link IProject}.
+ *
+ * @param databaseFilePath Database file path.
+ * @param targetProject Project in which the database is to be copied, in
+ * @param monitor Monitor for measuring the progress of the operation.
+ * case it does not exist.
+ *
+ * @throws FileNotFoundException Exception thrown in case the entered path points to an invalid path our file.
+ * @throws IllegalArgumentException Exception thrown in case the targetProject is null.
+ * @throws IOException Exception thrown when there are problems handling files in the copying process.
+ * @throws CoreException Exception thrown when there are problems creating the assets folder.
+ */
+ public static void copyDatabaseFileToAssetsFolder(IPath databaseFilePath,
+ IProject targetProject, IProgressMonitor monitor) throws IOException, CoreException
+ {
+ // get sub monitor
+ SubMonitor subMonitor = SubMonitor.convert(monitor, 10);
+
+ // begin
+ subMonitor.beginTask(null, 10);
+
+ // validate the path
+ if ((databaseFilePath == null) || !databaseFilePath.toFile().exists())
+ {
+ throw new FileNotFoundException(
+ "The file entered by the databaseFilePath does not exists."); //$NON-NLS-1$
+ }
+
+ // validate the project
+ if (targetProject == null)
+ {
+ throw new IllegalArgumentException("The argument targetProject cannot be null."); //$NON-NLS-1$
+ }
+
+ // get database file
+ File databaseFile = databaseFilePath.toFile();
+
+ // get assets folder
+ IFolder assetsFolder = targetProject.getFolder(DatabaseUtils.ASSESTS_FOLDER);
+
+ subMonitor.worked(3);
+
+ if (assetsFolder.exists())
+ {
+ // get the file matching the one entered on the Path
+ IResource foundDatabaseFile = assetsFolder.findMember(databaseFile.getName());
+ subMonitor.worked(5);
+ // in case there is no file, or the resource is not a FILE, or the found file does not actually exists, copy it
+ if ((foundDatabaseFile == null) || (foundDatabaseFile.getType() != IResource.FILE)
+ || !foundDatabaseFile.exists())
+ {
+ // copy the file
+ FileUtil.copyFile(databaseFile, assetsFolder.getFile(databaseFile.getName())
+ .getLocation().toFile());
+ // refresh assets
+ assetsFolder.refreshLocal(IResource.DEPTH_INFINITE, monitor);
+ }
+ }
+ else
+ {
+ // create the assets folder
+ assetsFolder.create(true, true, monitor);
+ subMonitor.worked(5);
+ // copy the file
+ FileUtil.copyFile(databaseFile, assetsFolder.getFile(databaseFile.getName())
+ .getLocation().toFile());
+ // refresh assets
+ assetsFolder.refreshLocal(IResource.DEPTH_INFINITE, monitor);
+ }
+ subMonitor.worked(10);
+ }
+
+ /**
+ * Formats the code using the Eclipse Java settings if possible,
+ * otherwise returns original document not indented.
+ * @param destinationFile Destination file.
+ * @param databaseHelperText Text to generate.
+ * @param monitor A progress monitor to be used to show operation status.
+ * @return Created document.
+ */
+ @SuppressWarnings(
+ {
+ "rawtypes", "unchecked"
+ })
+ public static IDocument formatCode(IFile destinationFile, String databaseHelperText,
+ IProgressMonitor monitor)
+ {
+ IDocument document = new Document();
+ File file = new File(destinationFile.getLocation().toOSString());
+
+ try
+ {
+ document.set(databaseHelperText);
+
+ try
+ {
+ IJavaProject p = JavaCore.create(destinationFile.getProject());
+ Map mapOptions = p.getOptions(true);
+
+ TextEdit textEdit =
+ CodeFormatterUtil.format2(CodeFormatter.K_COMPILATION_UNIT
+ | CodeFormatter.F_INCLUDE_COMMENTS, document.get(), 0,
+ System.getProperty("line.separator"), mapOptions);
+
+ if (textEdit != null)
+ {
+ textEdit.apply(document);
+ }
+ }
+ catch (Exception ex)
+ {
+ //do nothing
+ }
+
+ BufferedWriter out = new BufferedWriter(new FileWriter(file));
+ try
+ {
+ out.write(document.get());
+ out.flush();
+ }
+ finally
+ {
+ try
+ {
+ out.close();
+ }
+ catch (IOException e)
+ {
+ /* ignore */
+ }
+ }
+ // the refresh is needed in order to avoid the user to have to press F5
+ destinationFile.getParent().refreshLocal(IResource.DEPTH_INFINITE, monitor);
+ }
+ catch (Exception e)
+ {
+ String errMsg =
+ NLS.bind(CodeUtilsNLS.EXC_JavaClass_ErrorFormattingSourceCode,
+ destinationFile.getName());
+ StudioLogger.error(DatabaseUtils.class, errMsg, e);
+ }
+ return document;
+
+ }
+
+}
diff --git a/src/plugins/android.codeutils/src/com/motorola/studio/android/codeutils/i18n/CodeUtilsNLS.java b/src/plugins/android.codeutils/src/com/motorola/studio/android/codeutils/i18n/CodeUtilsNLS.java
new file mode 100644
index 0000000..631960c
--- /dev/null
+++ b/src/plugins/android.codeutils/src/com/motorola/studio/android/codeutils/i18n/CodeUtilsNLS.java
@@ -0,0 +1,539 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.motorola.studio.android.codeutils.i18n;
+
+import org.eclipse.osgi.util.NLS;
+
+/**
+ * Class that contains the localized messages to be used through the
+ * Android SDK support and project creation
+ */
+public class CodeUtilsNLS extends NLS
+{
+ static
+ {
+ NLS.initializeMessages("com.motorola.studio.android.codeutils.i18n.codeUtilsNLS",
+ CodeUtilsNLS.class);
+ }
+
+ /*
+ * Generic strings
+ */
+
+ public static String GenerateMenuCodeDialog_DefaultMessage;
+
+ public static String GenerateMenuCodeDialog_InflatedMessage;
+
+ public static String GenerateMenuCodeDialog_Error_MenuFolderDoesNotExist;
+
+ public static String GenerateMenuCodeDialog_MenuFileLabel;
+
+ public static String GenerateMenuCodeDialog_NoSuitableClasses;
+
+ public static String GenerateMenuCodeDialog_NoSuitableMenus;
+
+ public static String GenerateMenuCodeDialog_NoSuitableProjects;
+
+ public static String GenerateMenuCodeDialog_ProjectLabel;
+
+ public static String GenerateMenuCodeDialog_ShellTitle;
+
+ public static String GenerateMenuCodeDialog_TargetClassLabel;
+
+ public static String GenerateMenuCodeDialog_Title;
+
+ public static String GenerateMenuCodeHandler_Error_CannotRetrieveClassInformation;
+
+ public static String GenerateMenuCodeHandler_SelectedClassNeitherActivityFragment;
+
+ public static String GenerateViewBasedOnLayoutHandler_FillJavaActivityBasedOnLayout;
+
+ public static String GenerateViewBasedOnLayoutHandler_SelectedClassNeitherActivityFragment;
+
+ public static String MenuHandlerCodeGenerator_AddingOnCreateAndSetHasOptionMenu;
+
+ public static String GenerateMenuCodeDialog_Class_Error;
+
+ public static String MenuHandlerCodeGenerator_AddingOnCreateOptionsMenu;
+
+ public static String MenuHandlerCodeGenerator_AddingOnOptionsItemSelected;
+
+ public static String MenuHandlerCodeGenerator_InvalidJavaCharacterInAndroidOnClickAttribute;
+
+ public static String MethodVisitor_InvalidFormatForFragmentOnCreateView;
+
+ public static String NewActivityWizard_MessageSomeProblemsOccurredWhileBuildingProject;
+
+ public static String NewBuildingBlocksWizardPage_PermissionLabel;
+
+ public static String NewLauncherWizardPage_ActionTypeDialogMessage;
+
+ public static String NewLauncherWizardPage_ActionTypeDialogTitle;
+
+ public static String NewLauncherWizardPage_CategoryTypeDialogMessage;
+
+ public static String NewLauncherWizardPage_CategoryTypeDialogTitle;
+
+ public static String NewLauncherWizardPage_InputDialogValidationMessage;
+
+ public static String JavaViewBasedOnLayoutModifier_InsertingCode;
+
+ public static String JavaViewBasedOnLayoutModifier_CreatingImports;
+
+ public static String JavaViewBasedOnLayoutModifier_AddingAttributes;
+
+ public static String JavaViewBasedOnLayoutModifier_AddingOnClickMethodFromXML;
+
+ public static String JavaViewBasedOnLayoutModifier_AddingMethodToHandleButton;
+
+ public static String JavaViewBasedOnLayoutModifier_AddingFindingViewById;
+
+ public static String JavaViewBasedOnLayoutModifier_AddingGalleryHandler;
+
+ public static String JavaViewBasedOnLayoutModifier_AddingSeekbarHandler;
+
+ public static String JavaViewBasedOnLayoutModifier_AddingSetOnClickListener;
+
+ public static String JavaViewBasedOnLayoutModifier_AddingSpinnerHandler;
+
+ public static String JavaViewBasedOnLayoutModifier_AddingOnKeyHandler;
+
+ public static String JavaViewBasedOnLayoutModifier_AddingOnClickHandler;
+
+ public static String JavaViewBasedOnLayoutModifier_AddingRatingBarHandler;
+
+ public static String JDTUtils_FragmentOnCreateViewWithProblemsOrWithWrongFormat;
+
+ public static String JDTUtils_MalformedXMLWhenFilenameAvailable_Error;
+
+ public static String JDTUtils_MalformedMenuXMLWhenFilenameAvailable_Error;
+
+ public static String JDTUtils_GenerateCodeForMenuVisitingCode_Error;
+
+ public static String JDTUtils_MalformedXMLWhenFilenameNotAvailable_Error;
+
+ public static String UI_ChooseLayoutItemsDialog_Dialog_Title;
+
+ public static String UI_ChooseLayoutItemsDialog_Error_onCreate_Not_Declared;
+
+ public static String UI_ChooseLayoutItemsDialog_No_Gui_Items_Available;
+
+ /*
+ * Error strings
+ */
+ public static String ERR_BuildingBlockCreation_ErrorMessage;
+
+ public static String ERR_ContentProvider_InvalidAuthoritySelection;
+
+ public static String ERR_NewProviderMainPage_ErrorMessageAlreadyExists;
+
+ public static String ERR_NewProviderMainPage_ErrorMessageDefaultName;
+
+ public static String ERR_NewProviderMainPage_InvalidCharactersInAuthority;
+
+ public static String ERR_NewBuildingBlocksWizardPage_PackageMustHaveAtLeastTwoIdentifiers;
+
+ public static String ERR_NewBuildingBlocksWizardPage_SelectAnAndroidProject;
+
+ public static String ERR_NewBuildingBlocksWizardPage_SelectAValidSourceFolder;
+
+ public static String ERR_NewBuildingBlocksWizardPage_CannotUseTheGenFolderAsSourceFolder;
+
+ public static String ERR_NewBuildingBlocksWizardPage_PackageAndClassAlreadyExist;
+
+ public static String ERR_NewBuildingBlocksWizardPage_InvalidTypeName;
+
+ public static String ERR_NewBuildingBlocksWizardPage_InvalidPackageName;
+
+ public static String ERR_NewBuildingBlocksWizardPage_FileNameTooLong;
+
+ public static String ERR_NewBuildingBlocksWizardPage_OneOrMoreErrorsWhenParsingManifest;
+
+ public static String ERR_NewBuildingBlocksWizardPage_CannotProceedWithTheBuildingBlockCreation;
+
+ /*
+ * Exception strings
+ */
+ public static String EXC_Service_CannotCreateTheServiceClass;
+
+ public static String EXC_Service_CannotUpdateTheManifestFile;
+
+ public static String EXC_Service_CannotCreateTheServiceLabel;
+
+ public static String EXC_Activity_CannotCreateTheActivityClass;
+
+ public static String EXC_Activity_CannotUpdateTheManifestFile;
+
+ public static String EXC_Activity_CannotCreateTheActivityLabel;
+
+ public static String EXC_Receiver_CannotCreateTheReceiverClass;
+
+ public static String EXC_Receiver_CannotUpdateTheManifestFile;
+
+ public static String EXC_WidgetProvider_CannotCopyTemplateFiles;
+
+ public static String EXC_ContentProvider_CannotCreateTheContentProviderClass;
+
+ public static String EXC_ContentProvider_CannotUpdateTheManifestFile;
+
+ public static String EXC_ContentProvider_CannotCreateTheContentProviderLabel;
+
+ public static String EXC_JavaClass_ErrorFormattingSourceCode;
+
+ public static String EXC_JavaClass_ErrorApplyingCommentsToCode;
+
+ /*
+ * UI strings
+ */
+ public static String UI_GenericErrorDialogTitle;
+
+ public static String UI_Common_UpdatingTheStringsResourceFile;
+
+ public static String UI_Common_SavingTheAndroidManifestXMLFile;
+
+ public static String UI_Common_UpdatingTheAndroidManifestXMLFile;
+
+ public static String UI_Service_CreatingTheServiceJavaClass;
+
+ public static String UI_Receiver_CreatingTheReceiverJavaClass;
+
+ public static String UI_ContentProvider_CreatingTheContentProviderJavaClass;
+
+ public static String UI_Activity_CreatingTheActivityJavaClass;
+
+ public static String UI_WidgetProvider_CreatingTheWidgetProviderJavaClass;
+
+ public static String UI_GenerateSampleListError;
+
+ public static String UI_NewWidgetProviderWizard_WizardTitle;
+
+ public static String UI_NewWidgetProviderMainPage_PageTitle;
+
+ public static String UI_NewWidgetProviderMainPage_DefaultWizardDescription;
+
+ public static String UI_NewWidgetProviderMainPage_WizardTitle;
+
+ public static String UI_NewActivityMainPage_DescriptionCreateActivity;
+
+ public static String UI_NewActivityMainPage_DescriptionCreateActivityBasedOnTemplate;
+
+ public static String UI_NewActivityMainPage_TitleActivity;
+
+ public static String UI_NewActivityMainPage_TitleActivityBasedOnTemplate;
+
+ public static String UI_NewActivityMainPage_PageTitle;
+
+ public static String UI_NewActivityMainPage_CheckMainButton;
+
+ public static String UI_DefineSqlOpenHelperPage_Title;
+
+ public static String UI_CreateNewActivityBasedOnTemplateLink;
+
+ public static String UI_NewActivityWizard_TitleNewActivityWizard;
+
+ public static String UI_NewActivityWizard_TitleNewActivityBasedOnTemplateWizard;
+
+ public static String UI_NewBuildingBlocksWizardPage_WizardTitle;
+
+ public static String UI_NewBuildingBlocksWizardPage_MessageChooseFolder;
+
+ public static String UI_NewBuildingBlocksWizardPage_TextLabel;
+
+ public static String UI_NewBuildingBlocksWizardPage_ButtonNameDefault;
+
+ public static String UI_NewBuildingBlocksWizardPage_QuestionWhichMethodCreate;
+
+ public static String UI_NewProviderMainPage_WizardTitle;
+
+ public static String UI_NewProviderMainPage_LabelAuthorities;
+
+ public static String UI_NewProviderMainPage_TitleNewAuthority;
+
+ public static String UI_NewProviderMainPage_MessageAvoidConflicts;
+
+ public static String UI_NewProviderMainPage_OptionUseDefault;
+
+ public static String UI_NewProviderMainPage_SubtitleCreateContentProvider;
+
+ public static String UI_NewProviderMainPage_TitleContentProvider;
+
+ public static String UI_NewProviderWizard_WizardTitle;
+
+ public static String UI_NewServiceMainPage_WizardTitle;
+
+ public static String UI_NewServiceMainPage_SubtitleCreateService;
+
+ public static String UI_NewServiceMainPage_TitleService;
+
+ public static String UI_NewServiceWizard_WizardTitle;
+
+ public static String UI_SampleSelectionPage_SamplesTreeLabel;
+
+ public static String UI_SampleSelectionPage_SamplesDescriptionPane;
+
+ public static String UI_SampleSelectionPage_Description;
+
+ public static String UI_SampleSelectionPage_Description_JavaFile;
+
+ public static String UI_SampleSelectionPage_Description_LayoutFile;
+
+ public static String UI_SampleSelectionPage_Description_StringFile;
+
+ public static String UI_SampleSelectionPage_Description_XMLFile;
+
+ public static String UI_SampleSelectionPage_Description_AnimFile;
+
+ public static String UI_SampleSelectionPage_Description_DrawableFile;
+
+ public static String UI_SampleSelectionPage_Description_MenuFile;
+
+ public static String UI_SampleSelectionPage_ErrorParsingStringXml;
+
+ public static String UI_NewReceiverMainPage_DefaultWizardDescription;
+
+ public static String UI_NewReceiverMainPage_WizardTitle;
+
+ public static String UI_NewReceiverMainPage_PageTitle;
+
+ public static String UI_NewLauncherWizardPage_ActionLabel;
+
+ public static String UI_NewLauncherWizardPage_ActionSelectionDialogTitle;
+
+ public static String UI_NewLauncherWizardPage_ActionSelectionDialogMessage;
+
+ public static String UI_NewLauncherWizardPage_CategoryLabel;
+
+ public static String UI_NewLauncherWizardPage_CategorySelectionDialogTitle;
+
+ public static String UI_NewLauncherWizardPage_CategorySelectionDialogMessage;
+
+ public static String UI_NewLauncherWizardPage_IntentFilterGroupName;
+
+ public static String UI_NewReceiverWizard_WizardTitle;
+
+ /*
+ * Model strings area
+ */
+ public static String MODEL_Common_ToDoPutYourCodeHere;
+
+ public static String MODEL_ServiceClass_ServiceDescription;
+
+ public static String MODEL_ServiceClass_OnBindMethodDescription;
+
+ public static String MODEL_ServiceClass_OnCreateMethodDescription;
+
+ public static String MODEL_ServiceClass_OnStartMethodDescription;
+
+ public static String MODEL_ContentProviderClass_ContentProviderDescription;
+
+ public static String MODEL_ContentProviderClass_DeleteMethodDescription;
+
+ public static String MODEL_ContentProviderClass_GetTypeMethodDescription;
+
+ public static String MODEL_ContentProviderClass_InsertMethodDescription;
+
+ public static String MODEL_ContentProviderClass_OnCreateMethodDescription;
+
+ public static String MODEL_ContentProviderClass_QueryMethodDescription;
+
+ public static String MODEL_ContentProviderClass_UpdateMethodDescription;
+
+ public static String MODEL_ContentProviderClass_ContentUriDescription;
+
+ public static String MODEL_BroadcastReceiverClass_BroadcastReceiverDescription;
+
+ public static String MODEL_BroadcastReceiverClass_onReceiveMethodDescription;
+
+ public static String MODEL_ActivityClass_ActivityDescription;
+
+ public static String MODEL_ActivityClass_OnCreateMethodDescription;
+
+ public static String MODEL_ActivityClass_OnStartMethodDescription;
+
+ public static String UI_ListActivityPage_Preview;
+
+ public static String UI_ListActivityPage_TitleWizard;
+
+ public static String EXC_ResourceFile_ErrorFormattingTheXMLOutput;
+
+ /*
+ * Resource file
+ */
+ public static String EXC_ResourceFile_ErrorCreatingTheDocumentBuilder;
+
+ public static String EXC_AbstractResourceFileParser_ErrorParsingTheXMLFile;
+
+ public static String EXC_AbstractResourceFileParser_ErrorReadingTheXMLContent;
+
+ /*
+ * Activity based on template
+ */
+ public static String UI_ActivityBasedOnTemplateSupport_Configuring_Sample_Source_Task;
+
+ /*
+ * DB
+ */
+ public static String UI_CreateSampleDatabaseActivityColumnsPageName;
+
+ public static String UI_ActivityWizard_Title;
+
+ public static String UI_CreateSampleDatabaseActivityColumnsPage_SelectAllButton;
+
+ public static String UI_CreateSampleDatabaseActivityColumnsPage_DeselectAllButton;
+
+ public static String UI_CreateSampleDatabaseActivityColumnsPage_Default_Message;
+
+ public static String UI_CreateSampleDatabaseActivityPageName;
+
+ public static String UI_CreateSampleDatabaseActivityPage_No_Database_Found_Information;
+
+ public static String UI_CreateSampleDatabaseActivityPage_Default_Message;
+
+ public static String DATABASE_DEPLOY_ERROR_DEPLOYING_DATABASE;
+
+ public static String UI_PersistenceWizardPageCreateNewSQLOpenHelper;
+
+ public static String UI_PersistenceWizardPageSQLOpenHelperGroupTitle;
+
+ public static String UI_DefineSqlOpenHelperPage_WarningNoOpenHelperSelected;
+
+ public static String UI_DefineSqlOpenHelperPage_Default_Message;
+
+ /*
+ * DB Management classes wizard
+ */
+ public static String Db_GenerateManagementClassesError;
+
+ public static String UI_PersistenceWizardPageDescriptionDeploy;
+
+ public static String UI_PersistenceWizardPageTitleDeploy;
+
+ public static String UI_PersistenceWizardPageSelectProjectTitle;
+
+ public static String UI_PersistenceWizardPageDatabaseFileGroupTitle;
+
+ public static String UI_PersistenceWizardGenerateContentProvidersForEachTable;
+
+ public static String UI_PersistenceWizardPageContentProviderGroupTitle;
+
+ public static String UI_PersistenceWizardOverrideContentProvidersIfAlreadyExists;
+
+ public static String DatabaseManagementClassesCreationMainPage_UI_OpenHelperPackageNameMustNotBeEmpty;
+
+ public static String DatabaseManagementClassesCreationMainPage_UI_ContentProvidersPackageNameMustNotBeEmpty;
+
+ public static String UI_PersistenceWizardPageThereMustBeASelectedProject;
+
+ public static String UI_PersistenceWizardPageTheEnteredProjectIsInvalid;
+
+ public static String UI_PersistenceWizardPageThereMustBeASelectedDatabaseFile;
+
+ public static String UI_PersistenceWizardPageTheEnteredPathIsInvalid;
+
+ public static String UI_PersistenceWizardPageFileDoesNotExist;
+
+ public static String UI_PersistenceWizardPageFileNotValid;
+
+ public static String UI_PersistenceWizardPageFileNotEvaluated;
+
+ public static String UI_PersistenceWizardPageFileTooLarge;
+
+ public static String UI_PersistenceWizardPageTheDatabaseFileWillBeCopiedToProjectsAssetsFolder;
+
+ public static String UI_PersistenceWizard_ChangePerspectiveToJava_DialogMessage;
+
+ public static String UI_PersistenceWizard_ChangePerspectiveToJava_DialogTitle;
+
+ public static String UI_PersistenceWizard_ChangePerspectiveToMOTODEVStudioAndroid_DialogTitle;
+
+ public static String UI_PersistenceWizard_ChangePerspectiveToMOTODEVStudioAndroid_DialogMessage;
+
+ public static String Field_ErrorAutoIncrementNotAllowed;
+
+ public static String AddTableFieldDialog_InvalidName;
+
+ public static String Table_ErrorUnamedColumns;
+
+ public static String Table_ErrorConflictingNames;
+
+ public static String Table_ErrorMoreThanOnePrimaryKey;
+
+ public static String FillOnSaveInstanceStateDialog_DialogDescription;
+
+ public static String FillOnSaveInstanceStateDialog_DialogTitle;
+
+ public static String FillOnSaveInstanceStateDialog_ShellTitle;
+
+ public static String FindViewByIdCodeGenerator_CompatibilityModeClassNeedToExtendFragmentActivityError;
+
+ public static String ChooseLayoutItemsDialog_DefaultMessage;
+
+ public static String ChooseLayoutItemsDialog_FillActivityBasedOnLayout;
+
+ public static String ChooseLayoutItemsDialog_GenerateDefaultListeners;
+
+ public static String ChooseLayoutItemsDialog_Gui_Items_Available_No_Id;
+
+ public static String ChooseLayoutItemsDialog_GUIItems;
+
+ public static String ChooseLayoutItemsDialog_Id;
+
+ public static String ChooseLayoutItemsDialog_Project;
+
+ public static String ChooseLayoutItemsDialog_SaveState;
+
+ public static String ChooseLayoutItemsDialog_SaveStateTooltip;
+
+ public static String ChooseLayoutItemsDialog_SourceLayoutFile;
+
+ public static String ChooseLayoutItemsDialog_TargetClass;
+
+ public static String ChooseLayoutItemsDialog_TryToGenerateCodeWhenThereIsAnError;
+
+ public static String ChooseLayoutItemsDialog_Type;
+
+ public static String ChooseLayoutItemsDialog_VariableName;
+
+ public static String ChooseLayoutItemsDialog_VariableNameInUse_Error;
+
+ public static String UI_UnselectAll;
+
+ public static String UI_SelectAll;
+
+ public static String Info_ChooseLayoutItemsDialog_Project_Nature;
+
+ public static String Info_ChooseLayoutItemsDialog_Available_Classes;
+
+ public static String AbstractLayoutItemsDialog_Error_No_Class_Found;
+
+ public static String AbstractLayoutItemsDialog_Error_No_Layout_Found;
+
+ public static String AbstractLayoutItemsDialog_Error_No_Projects_Found;
+
+ public static String SaveStateCodeGenerator_AddingCodeSaveRestoreUIState;
+
+ public static String DATABASE_DEPLOY_SUCCESS_MESSAGE;
+
+ public static String DATABASE_DEPLOY_SUCCESS_MESSAGE_TITLE;
+
+ public static String DATABASE_DEPLOY_ERROR_CONNECTING_DATABASE;
+
+ public static String DATABASE_DEPLOY_CREATING_ANDROID_METADATA_TABLE;
+
+ public static String DATABASE_ERROR_EXECUTING_STATEMENT;
+
+}
diff --git a/src/plugins/android.codeutils/src/com/motorola/studio/android/codeutils/i18n/codeUtilsNLS.properties b/src/plugins/android.codeutils/src/com/motorola/studio/android/codeutils/i18n/codeUtilsNLS.properties
new file mode 100644
index 0000000..0ec110f
--- /dev/null
+++ b/src/plugins/android.codeutils/src/com/motorola/studio/android/codeutils/i18n/codeUtilsNLS.properties
@@ -0,0 +1,298 @@
+#
+# Copyright (C) 2012 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+UI_NewBuildingBlocksWizardPage_WizardTitle=Source Folder Selection
+UI_NewBuildingBlocksWizardPage_MessageChooseFolder=&Choose a source folder:
+UI_NewBuildingBlocksWizardPage_TextLabel=Label:
+UI_NewBuildingBlocksWizardPage_ButtonNameDefault=Default
+UI_NewBuildingBlocksWizardPage_QuestionWhichMethodCreate=Which method would you like to create?
+UI_NewProviderMainPage_WizardTitle=New Android Content Provider
+UI_NewProviderMainPage_LabelAuthorities=Authorities:
+
+NewActivityWizard_MessageSomeProblemsOccurredWhileBuildingProject=Some problems occurred while building. Rebuild the project before deploying it.
+
+NewBuildingBlocksWizardPage_PermissionLabel=Permission:
+NewLauncherWizardPage_ActionTypeDialogMessage=Enter the name of an action intent:
+NewLauncherWizardPage_ActionTypeDialogTitle=Enter an action
+NewLauncherWizardPage_CategoryTypeDialogMessage=Enter the name of a category intent:
+NewLauncherWizardPage_CategoryTypeDialogTitle=Enter a category
+NewLauncherWizardPage_InputDialogValidationMessage=Supplied value must not contain whitespace characters
+
+EXC_ResourceFile_ErrorFormattingTheXMLOutput=An error occurred while formatting the XML output. It will not be possible to get the XML content.
+
+ERR_BuildingBlockCreation_ErrorMessage=An error has occurred while creating the selected building block.
+ERR_ContentProvider_InvalidAuthoritySelection=At least one authority must be specified.
+ERR_NewBuildingBlocksWizardPage_PackageMustHaveAtLeastTwoIdentifiers=The package name must have at least two identifiers.
+ERR_NewBuildingBlocksWizardPage_SelectAnAndroidProject=Select an Android project.
+ERR_NewBuildingBlocksWizardPage_SelectAValidSourceFolder=Select a valid source folder.
+ERR_NewBuildingBlocksWizardPage_CannotUseTheGenFolderAsSourceFolder=Cannot use the 'gen' folder as the source folder.
+ERR_NewBuildingBlocksWizardPage_PackageAndClassAlreadyExist=The given package and class already exist in another source folder.
+ERR_NewBuildingBlocksWizardPage_InvalidTypeName=Type name ''{0}'' is not a valid identifier.
+ERR_NewBuildingBlocksWizardPage_InvalidPackageName=Package name ''{0}'' is not valid.
+ERR_NewBuildingBlocksWizardPage_FileNameTooLong=The combination of source folder name, package name and type name will result in a file name that is too long.
+ERR_NewBuildingBlocksWizardPage_OneOrMoreErrorsWhenParsingManifest=One or more errors have occurred while parsing AndroidManifest.xml:
+ERR_NewBuildingBlocksWizardPage_CannotProceedWithTheBuildingBlockCreation=Cannot proceed with building block creation.
+ERR_NewProviderMainPage_ErrorMessageAlreadyExists=Authority already exists.
+ERR_NewProviderMainPage_ErrorMessageDefaultName=This is the default authority name.
+ERR_NewProviderMainPage_InvalidCharactersInAuthority=The authority name contains invalid characters.
+
+EXC_ResourceFile_ErrorCreatingTheDocumentBuilder=Cannot create the XML document builder. It will not be possible to get the XML content.
+
+EXC_Service_CannotCreateTheServiceClass=Cannot create the service class {0} because an error has occurred: {1}
+EXC_Service_CannotUpdateTheManifestFile=Cannot update AndroidManifest.xml while creating the service {0}: {1}
+EXC_Service_CannotCreateTheServiceLabel=Cannot create the service label in the strings resource file: {0}
+EXC_Activity_CannotCreateTheActivityClass=Cannot create the activity class {0} because an error has occurred: {1}
+EXC_Activity_CannotUpdateTheManifestFile=Cannot update AndroidManifest.xml while creating the activity {0}: {1}
+EXC_Activity_CannotCreateTheActivityLabel=Cannot create the activity label in the strings resource file: {0}
+EXC_Receiver_CannotCreateTheReceiverClass=Cannot create the broadcast receiver class {0} because an error has occurred: {1}
+EXC_Receiver_CannotUpdateTheManifestFile=Cannot update AndroidManifest.xml while creating the broadcast receiver {0}: {1}
+EXC_ContentProvider_CannotCreateTheContentProviderClass=Cannot create the content provider class {0} because an error has occurred: {1}
+EXC_ContentProvider_CannotUpdateTheManifestFile=Cannot update AndroidManifest.xml while creating the content provider {0}: {1}
+EXC_ContentProvider_CannotCreateTheContentProviderLabel=Cannot create the content provider label in the strings resource file: {0}
+EXC_JavaClass_ErrorFormattingSourceCode=Error formatting source code comments for the class {0}.
+EXC_JavaClass_ErrorApplyingCommentsToCode=Error applying comments to the class {0}.
+EXC_WidgetProvider_CannotCopyTemplateFiles=Cannot copy template files: {0}
+UI_NewProviderMainPage_TitleNewAuthority=New authority name
+UI_NewProviderMainPage_MessageAvoidConflicts=To avoid conflicts, authority names should use a Java-style naming convention (such as com.example.provider.cartoonprovider)
+UI_NewActivityMainPage_DescriptionCreateActivity=Creates a new Android Activity.
+UI_NewActivityMainPage_DescriptionCreateActivityBasedOnTemplate=Creates a new Android Activity based on a template.
+UI_NewActivityMainPage_TitleActivity=Android Activity
+UI_NewActivityMainPage_TitleActivityBasedOnTemplate=Android Activity Based on Template
+UI_NewActivityMainPage_PageTitle=New Android Activity
+UI_NewActivityMainPage_CheckMainButton=Set as the main activity
+UI_DefineSqlOpenHelperPage_Title=Define Connection to Database
+UI_CreateNewActivityBasedOnTemplateLink=Create new activity based on template
+UI_NewActivityWizard_TitleNewActivityWizard=New Android Activity
+UI_NewActivityWizard_TitleNewActivityBasedOnTemplateWizard=New Android Activity Based on Template
+UI_NewProviderMainPage_OptionUseDefault=Use default authority
+UI_NewProviderMainPage_SubtitleCreateContentProvider=Create a new Android content provider.
+UI_NewProviderMainPage_TitleContentProvider=Android Content Provider
+UI_NewProviderWizard_WizardTitle=New Android Content Provider
+UI_NewServiceMainPage_WizardTitle=New Android Service
+UI_NewServiceMainPage_SubtitleCreateService=Create a new Android service.
+UI_NewServiceMainPage_TitleService=Android Service
+UI_NewServiceWizard_WizardTitle=New Android Service
+
+UI_SampleSelectionPage_SamplesTreeLabel=Available templates:
+UI_SampleSelectionPage_SamplesDescriptionPane=Description
+UI_SampleSelectionPage_Description=The following will be added to or modified in your project:
+UI_SampleSelectionPage_Description_JavaFile=Java source file
+UI_SampleSelectionPage_Description_LayoutFile=Layout file
+UI_SampleSelectionPage_Description_StringFile=String values file
+UI_SampleSelectionPage_Description_XMLFile=XML file
+UI_SampleSelectionPage_Description_AnimFile=Animation file
+UI_SampleSelectionPage_Description_DrawableFile=Drawable file
+UI_SampleSelectionPage_Description_MenuFile=Menu file
+UI_SampleSelectionPage_ErrorParsingStringXml=Malformed sample string values file
+
+UI_GenericErrorDialogTitle=An error occurred during project creation
+
+UI_Common_UpdatingTheAndroidManifestXMLFile=Updating AndroidManifest.xml...
+UI_Common_SavingTheAndroidManifestXMLFile=Saving AndroidManifest.xml...
+UI_Common_UpdatingTheStringsResourceFile=Updating the strings resource file...
+
+UI_Service_CreatingTheServiceJavaClass=Creating the Service Java class...
+UI_Receiver_CreatingTheReceiverJavaClass=Creating the Broadcast Receiver Java class...
+UI_ContentProvider_CreatingTheContentProviderJavaClass=Creating the Content Provider Java class...
+UI_Activity_CreatingTheActivityJavaClass=Creating the Activity Java class...
+UI_WidgetProvider_CreatingTheWidgetProviderJavaClass=Creating the WidgetProvider Java class...
+
+UI_GenerateSampleListError=Failed to generate template list
+
+UI_NewReceiverMainPage_DefaultWizardDescription=Creates a new Android broadcast receiver.
+UI_NewReceiverMainPage_WizardTitle=Android Broadcast Receiver
+UI_NewReceiverMainPage_PageTitle=New Android Broadcast Receiver
+UI_NewLauncherWizardPage_ActionLabel=Action:
+UI_NewLauncherWizardPage_ActionSelectionDialogTitle=Select an action
+UI_NewLauncherWizardPage_ActionSelectionDialogMessage=Select an item to open (? = any character, ** = any string):
+UI_NewLauncherWizardPage_CategoryLabel=Category:
+UI_NewLauncherWizardPage_CategorySelectionDialogTitle=Select a category
+UI_NewLauncherWizardPage_CategorySelectionDialogMessage=Select an item to open (? = any character, ** = any string):
+UI_NewLauncherWizardPage_IntentFilterGroupName=Intent Filter
+UI_NewReceiverWizard_WizardTitle=New Android Broadcast Receiver Wizard
+UI_NewWidgetProviderWizard_WizardTitle=New Android Widget Provider Wizard
+UI_NewWidgetProviderMainPage_PageTitle=New Android Widget Provider
+UI_NewWidgetProviderMainPage_DefaultWizardDescription=Creates a new Android widget provider
+UI_NewWidgetProviderMainPage_WizardTitle=Android Widget Provider
+
+MODEL_Common_ToDoPutYourCodeHere=// TODO Put your code here
+MODEL_ServiceClass_ServiceDescription=
+MODEL_ServiceClass_OnBindMethodDescription=
+MODEL_ServiceClass_OnCreateMethodDescription=
+MODEL_ServiceClass_OnStartMethodDescription=
+MODEL_ContentProviderClass_ContentProviderDescription=
+MODEL_ContentProviderClass_DeleteMethodDescription=
+MODEL_ContentProviderClass_GetTypeMethodDescription=
+MODEL_ContentProviderClass_InsertMethodDescription=
+MODEL_ContentProviderClass_OnCreateMethodDescription=
+MODEL_ContentProviderClass_QueryMethodDescription=
+MODEL_ContentProviderClass_UpdateMethodDescription=
+MODEL_ContentProviderClass_ContentUriDescription=
+MODEL_BroadcastReceiverClass_BroadcastReceiverDescription=
+MODEL_BroadcastReceiverClass_onReceiveMethodDescription=
+MODEL_ActivityClass_ActivityDescription=
+MODEL_ActivityClass_OnCreateMethodDescription=
+MODEL_ActivityClass_OnStartMethodDescription=
+
+UI_ListActivityPage_TitleWizard=List Activities Templates
+UI_ListActivityPage_Preview=Preview
+
+JDTUtils_GenerateCodeForMenuVisitingCode_Error=Error visiting code.
+
+JavaViewBasedOnLayoutModifier_InsertingCode=Inserting code
+JavaViewBasedOnLayoutModifier_CreatingImports=Creating imports
+JavaViewBasedOnLayoutModifier_AddingAttributes=Adding attributes
+JavaViewBasedOnLayoutModifier_AddingOnClickMethodFromXML=Adding onClick methods based on layout
+JavaViewBasedOnLayoutModifier_AddingMethodToHandleButton=Adding method to handle buttons
+JavaViewBasedOnLayoutModifier_AddingFindingViewById=Adding findViewById()
+JavaViewBasedOnLayoutModifier_AddingGalleryHandler=Adding Gallery handler
+JavaViewBasedOnLayoutModifier_AddingSeekbarHandler=Adding SeekBar handler
+JavaViewBasedOnLayoutModifier_AddingSetOnClickListener=Adding setOnClickListener()
+JavaViewBasedOnLayoutModifier_AddingSpinnerHandler=Adding Spinner handler
+JavaViewBasedOnLayoutModifier_AddingOnKeyHandler=Adding onKey() handler
+JavaViewBasedOnLayoutModifier_AddingOnClickHandler=Adding onClick() handler
+JavaViewBasedOnLayoutModifier_AddingRatingBarHandler=Adding RatingBar handler
+JDTUtils_FragmentOnCreateViewWithProblemsOrWithWrongFormat=For this operation to work the Fragment code must compile, onCreateView() must call inflater.inflate(), and its result must be stored as a local variable.
+JDTUtils_MalformedXMLWhenFilenameAvailable_Error=Malformed layout file: {0}. Fix it before generating code based on layout.
+JDTUtils_MalformedXMLWhenFilenameNotAvailable_Error=Malformed layout file associated with Activity/Fragment. Fix it before generating code based on layout.
+
+UI_ChooseLayoutItemsDialog_Dialog_Title=Retrieve UI object references from the layout file
+UI_ChooseLayoutItemsDialog_Error_onCreate_Not_Declared=The onCreate/onCreateView method is not declared or contains errors, or the Activity/Fragment has no layout set.
+UI_ChooseLayoutItemsDialog_No_Gui_Items_Available=The selected layout file has no additional UI objects that are supported for automatic code retrieval.
+
+EXC_AbstractResourceFileParser_ErrorParsingTheXMLFile=An error occurred while parsing the resource file {0}: {1}
+EXC_AbstractResourceFileParser_ErrorReadingTheXMLContent=An error occurred while reading the resource file {0}: {1}
+
+UI_ActivityBasedOnTemplateSupport_Configuring_Sample_Source_Task=Configuring Sample Source Code
+
+UI_CreateSampleDatabaseActivityColumnsPageName=Select Columns Page
+
+UI_ActivityWizard_Title=Android Activity Based on Template
+
+UI_CreateSampleDatabaseActivityColumnsPage_SelectAllButton=Select all items
+
+UI_CreateSampleDatabaseActivityColumnsPage_DeselectAllButton=Deselect all items
+
+UI_CreateSampleDatabaseActivityColumnsPage_Default_Message=Select the columns to be displayed.
+
+UI_CreateSampleDatabaseActivityPageName=Select Table Page
+
+UI_CreateSampleDatabaseActivityPage_No_Database_Found_Information=No valid databases were found in the target project. Create a database and table before creating an activity based on this template.
+
+Field_ErrorAutoIncrementNotAllowed=Auto increment/decrement can only be used with Integer or Real types.
+AddTableFieldDialog_InvalidName=Invalid column name:
+Table_ErrorUnamedColumns=There is one or more unnamed column
+Table_ErrorConflictingNames=There are conflicting field names:
+Table_ErrorMoreThanOnePrimaryKey=Two or more fields are set as primary keys:
+
+GenerateMenuCodeDialog_DefaultMessage=Select a project, a class and a menu file to generate its options menu Java code
+GenerateMenuCodeDialog_InflatedMessage=Select a project or a class to add java code to generate another options menu
+GenerateMenuCodeDialog_Error_MenuFolderDoesNotExist=Menu folder does not exist on this project.
+GenerateMenuCodeDialog_Class_Error=The selected target class contains compilation errors.
+GenerateMenuCodeDialog_MenuFileLabel=Menu XML File:
+GenerateMenuCodeDialog_NoSuitableClasses=No suitable activities or fragments were found
+GenerateMenuCodeDialog_NoSuitableMenus=No suitable menu XML files were found. Menu XML files must be in "res/menu" folder
+GenerateMenuCodeDialog_NoSuitableProjects=No suitable projects were found
+GenerateMenuCodeDialog_ProjectLabel=Project:
+GenerateMenuCodeDialog_ShellTitle=Generate Options Menu Code Based on XML Files
+GenerateMenuCodeDialog_TargetClassLabel=Target Class:
+GenerateMenuCodeDialog_Title=Generate options menu Java code based on an XML file
+GenerateMenuCodeHandler_Error_CannotRetrieveClassInformation=Error retrieving class information
+GenerateMenuCodeHandler_SelectedClassNeitherActivityFragment=The selected class extends neither Activity nor Fragment.
+GenerateViewBasedOnLayoutHandler_FillJavaActivityBasedOnLayout=Add Java Code Based on Layout
+GenerateViewBasedOnLayoutHandler_SelectedClassNeitherActivityFragment=The selected class extends neither Activity nor Fragment.
+
+MenuHandlerCodeGenerator_AddingOnCreateAndSetHasOptionMenu=Adding onCreate(Bundle savedInstanceState)
+MenuHandlerCodeGenerator_AddingOnCreateOptionsMenu=Adding boolean onCreateOptionsMenu(Menu)
+MenuHandlerCodeGenerator_AddingOnOptionsItemSelected=Adding boolean onOptionsItemSelected(MenuItem)
+MenuHandlerCodeGenerator_InvalidJavaCharacterInAndroidOnClickAttribute=Method name : {0} declared in attribute android:onClick inside xml: {1} contains an invalid Java character: {2} to create the menu handler method.
+MethodVisitor_InvalidFormatForFragmentOnCreateView=Your fragment's onCreateView() method must call inflater.inflate() and its result must be stored as a local variable.
+
+FillOnSaveInstanceStateDialog_DialogDescription=Choose which layout items should have their state saved when the application pauses
+FillOnSaveInstanceStateDialog_DialogTitle=Save UI State
+FillOnSaveInstanceStateDialog_ShellTitle=Save UI State
+FindViewByIdCodeGenerator_CompatibilityModeClassNeedToExtendFragmentActivityError=To use compatibility mode your class must extend FragmentActivity instead of Activity.
+
+
+ChooseLayoutItemsDialog_DefaultMessage=Add UI object references from the layout file to an Activity or Fragment.
+ChooseLayoutItemsDialog_FillActivityBasedOnLayout=Generate Java Code Based on Layout
+ChooseLayoutItemsDialog_GenerateDefaultListeners=Generate default listeners when possible
+ChooseLayoutItemsDialog_Gui_Items_Available_No_Id=Some available UI objects cannot be retrieved: their android:id property is not set in the selected layout file.
+ChooseLayoutItemsDialog_GUIItems=UI Objects:
+ChooseLayoutItemsDialog_Id=Id
+ChooseLayoutItemsDialog_Project=Project:
+ChooseLayoutItemsDialog_SaveState=Save State?
+ChooseLayoutItemsDialog_SaveStateTooltip=Generate code to save this item in the onSaveInstanceState() method
+ChooseLayoutItemsDialog_SourceLayoutFile=Source Layout File:
+ChooseLayoutItemsDialog_TargetClass=Target Class:
+ChooseLayoutItemsDialog_TryToGenerateCodeWhenThereIsAnError=The selected Activity/Fragment has compilation errors. Fix them before generating layout-based code.
+ChooseLayoutItemsDialog_Type=Type
+ChooseLayoutItemsDialog_VariableName=Variable name
+ChooseLayoutItemsDialog_VariableNameInUse_Error=Variable name {0} is already in use. Change class code (field declaration) or layout file (id declaration).
+
+UI_UnselectAll=Deselect All
+UI_SelectAll=Select All
+
+Info_ChooseLayoutItemsDialog_Project_Nature=Project nature could not be checked.
+Info_ChooseLayoutItemsDialog_Available_Classes=Could not get available classes for the selected project
+
+AbstractLayoutItemsDialog_Error_No_Class_Found=No suitable activities or fragments were found
+AbstractLayoutItemsDialog_Error_No_Layout_Found=No suitable layouts were found
+AbstractLayoutItemsDialog_Error_No_Projects_Found=No suitable projects were found
+
+SaveStateCodeGenerator_AddingCodeSaveRestoreUIState=Adding code to save/restore UI state
+
+JDTUtils_MalformedMenuXMLWhenFilenameAvailable_Error=Malformed menu file: {0}. Fix it before generating code based on menu.
+
+DATABASE_DEPLOY_ERROR_DEPLOYING_DATABASE=Error generating Database deployment classes
+UI_PersistenceWizardPageCreateNewSQLOpenHelper = Generate SQL Open Helper
+UI_PersistenceWizardPageSQLOpenHelperGroupTitle = SQL Open Helper
+UI_DefineSqlOpenHelperPage_WarningNoOpenHelperSelected = For the template to work correctly, make sure the database is deployed to the device
+UI_DefineSqlOpenHelperPage_Default_Message = Add to your project a SQLOpenHelper class that copies a database from your application's assets directory to the appropriate location in /data/data/, so you can access the database using the SQLite APIs provided by the Android SDK.
+
+
+UI_PersistenceWizardPageDescriptionDeploy=Create classes to deploy and access your database.
+UI_PersistenceWizardPageTitleDeploy=Database Management Classes
+UI_PersistenceWizardPageSelectProjectTitle=Select a Project
+UI_PersistenceWizardPageDatabaseFileGroupTitle=Database File
+UI_PersistenceWizardGenerateContentProvidersForEachTable =Generate Content Providers for each table
+UI_PersistenceWizardPageContentProviderGroupTitle=Content Provider
+UI_PersistenceWizardOverrideContentProvidersIfAlreadyExists=Overwrite if it already exists
+DatabaseManagementClassesCreationMainPage_UI_OpenHelperPackageNameMustNotBeEmpty=The SQL Open Helper package name must not be empty
+DatabaseManagementClassesCreationMainPage_UI_ContentProvidersPackageNameMustNotBeEmpty=The Content Providers package name must not be empty
+UI_PersistenceWizardPageThereMustBeASelectedProject=There must be a selected Project
+UI_PersistenceWizardPageTheEnteredProjectIsInvalid=The entered project is invalid
+UI_PersistenceWizardPageThereMustBeASelectedDatabaseFile=There must be a selected database file
+UI_PersistenceWizardPageTheEnteredPathIsInvalid=The entered path is invalid
+UI_PersistenceWizardPageFileDoesNotExist=The File does not exist
+UI_PersistenceWizardPageFileNotValid=The selected file is not a valid SQLite database
+UI_PersistenceWizardPageFileNotEvaluated=It was not possible to determine whether the selected file is a valid SQLite database. Make sure it is.
+UI_PersistenceWizardPageFileTooLarge=The database file is larger than {0}. It could not be successfully loaded by your application.
+UI_PersistenceWizardPageTheDatabaseFileWillBeCopiedToProjectsAssetsFolder=The selected database file does not exist within the project's assets folder. It will be copied there.
+
+UI_PersistenceWizard_ChangePerspectiveToJava_DialogMessage=Would you like to switch to the Java perspective to check the newly created files?
+UI_PersistenceWizard_ChangePerspectiveToJava_DialogTitle=Switch to Java perspective
+UI_PersistenceWizard_ChangePerspectiveToMOTODEVStudioAndroid_DialogTitle = Switch to MOTODEV Studio Android Perspective
+UI_PersistenceWizard_ChangePerspectiveToMOTODEVStudioAndroid_DialogMessage = Would you like to switch to the MOTODEV Studio Android perspective to check the newly created files?
+
+DATABASE_DEPLOY_SUCCESS_MESSAGE = Database management classes have been created successfully.
+DATABASE_DEPLOY_SUCCESS_MESSAGE_TITLE = Database files deployment
+DATABASE_DEPLOY_ERROR_CONNECTING_DATABASE = Error connecting to Database
+DATABASE_DEPLOY_CREATING_ANDROID_METADATA_TABLE = Error creating or inserting into Android Metadata table
+DATABASE_ERROR_EXECUTING_STATEMENT = Error executing statement
+Db_GenerateManagementClassesError=Database Management classes generation error
+
+UI_CreateSampleDatabaseActivityPage_Default_Message = Select a table from one of the available databases. \ No newline at end of file
diff --git a/src/plugins/android.codeutils/src/com/motorola/studio/android/codeutils/wizards/DatabaseManagementClassesCreationMainPage.java b/src/plugins/android.codeutils/src/com/motorola/studio/android/codeutils/wizards/DatabaseManagementClassesCreationMainPage.java
new file mode 100644
index 0000000..ac826f6
--- /dev/null
+++ b/src/plugins/android.codeutils/src/com/motorola/studio/android/codeutils/wizards/DatabaseManagementClassesCreationMainPage.java
@@ -0,0 +1,1187 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.codeutils.wizards;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.IWorkspaceRoot;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.jdt.core.IJavaProject;
+import org.eclipse.jdt.core.IPackageFragmentRoot;
+import org.eclipse.jdt.core.JavaCore;
+import org.eclipse.jdt.core.JavaModelException;
+import org.eclipse.jdt.ui.wizards.NewTypeWizardPage;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.ScrolledComposite;
+import org.eclipse.swt.events.ControlAdapter;
+import org.eclipse.swt.events.ControlEvent;
+import org.eclipse.swt.events.ModifyEvent;
+import org.eclipse.swt.events.ModifyListener;
+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.Event;
+import org.eclipse.swt.widgets.Group;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.ui.PlatformUI;
+
+import com.motorola.studio.android.codeutils.CodeUtilsActivator;
+import com.motorola.studio.android.codeutils.db.utils.DatabaseUtils;
+import com.motorola.studio.android.codeutils.i18n.CodeUtilsNLS;
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.common.utilities.EclipseUtils;
+import com.motorola.studio.android.manifest.AndroidProjectManifestFile;
+import com.motorola.studio.android.model.manifest.AndroidManifestFile;
+import com.motorola.studio.android.model.manifest.dom.ManifestNode;
+import com.motorola.studio.android.wizards.elements.FileChooser;
+import com.motorola.studio.android.wizards.elements.ProjectChooser;
+
+/**
+ *
+ * Wizard page to create classes which aids the database management.
+ *
+ */
+public class DatabaseManagementClassesCreationMainPage extends NewTypeWizardPage
+{
+ private static final String CONTEXT_HELP_ID = CodeUtilsActivator.PLUGIN_ID
+ + ".create_db_classes";
+
+ /**
+ * <p>
+ * This listener is called when the Project´s name is modified. Here
+ * is checked whether the project´s name represents any available project
+ * in the current workspace. In case it does, the project of the wizard is updated and
+ * the status is also checked. Otherwise, the status is checked and an error
+ * message regarding this validation is displayed.
+ * </p>
+ * <p>
+ * This listener also enables/disables components depending on whether the
+ * chosen project is valid. Moreover, it populates the fields accordingly if
+ * the selected project is valid.
+ * </p>
+ */
+ private final class ProjectChooserModifyListener implements ModifyListener
+ {
+ public void modifyText(ModifyEvent e)
+ {
+ boolean isValidationOK = getProjectStatus(projectChooser.getText()) == null;
+ // in case there is no status, the project´s name is OK therefore update the project
+ try
+ {
+ if (isValidationOK)
+ {
+ //update fileChooser container
+ fileChooser.setContainer(projectChooser.getProject());
+ // update project
+ updateProject(projectChooser.getProject());
+ }
+ else
+ {
+ fileChooser.setContainer(ResourcesPlugin.getWorkspace().getRoot());
+ // null the project
+ updateProject(null);
+ }
+ }
+ catch (JavaModelException jme)
+ {
+ StudioLogger.error(this.getClass(), CodeUtilsNLS.Db_GenerateManagementClassesError,
+ jme);
+ IStatus status = new Status(IStatus.ERROR, PLUGIN_ID, jme.getLocalizedMessage());
+ EclipseUtils.showErrorDialog(CodeUtilsNLS.Db_GenerateManagementClassesError,
+ CodeUtilsNLS.Db_GenerateManagementClassesError, status);
+ }
+
+ // enable/disable this wizard´s group
+ //setCompositeChildremEnabled(databaseFileGroup, isValidationOK);
+ //ckbGenerateSQLOpenHelper.setEnabled(isValidationOK);
+ //setCompositeChildremEnabled(sqlOpenHelperGroup, isValidationOK);
+ //ckbCreateContentProviders.setEnabled(isValidationOK);
+ //setCompositeChildremEnabled(contentProviderGroup, isValidationOK);
+
+ // update status and page completion
+ doStatusAndPageCompletionUpdate();
+ }
+ }
+
+ /**
+ * <p>
+ * This class represents a listener which is called every time the
+ * text field in the file chooser is modified. This listener intends
+ * to validate whether the entered path is a valid path or not. To do so,
+ * the {@link DatabaseManagementClassesCreationMainPage#doStatusAndPageCompletionUpdate()} is called.
+ * </p>
+ * <p>
+ * This listener also enable/disables components depending on whether the
+ * database file is validated correctly.
+ * </p>
+ */
+ private final class DBFileChooserModifyListener implements ModifyListener
+ {
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.swt.events.ModifyListener#modifyText(org.eclipse.swt.events.ModifyEvent)
+ */
+ public void modifyText(ModifyEvent e)
+ {
+ // validation flag
+ boolean isValidationOK =
+ ((getFileStatus(fileChooser.getText()) == null) || (!getFileStatus(
+ fileChooser.getText()).equals(IStatus.ERROR)));
+
+ if (isValidationOK)
+ {
+ // set the database file
+ selectedDatabasePath = getDatabaseFilePath();
+ }
+
+ // enable/disable this wizard´s group
+ setCompositeChildremEnabled(sqlOpenHelperGroup, isValidationOK);
+ ckbCreateContentProviders.setEnabled(isValidationOK);
+ setCompositeChildremEnabled(contentProviderGroup, isValidationOK);
+
+ // update status and page completion
+ doStatusAndPageCompletionUpdate();
+ }
+ }
+
+ /**
+ * This listener is dispatched, from the inner part of Source/Package component - {@link SourcePackageChooserPartWizard}.
+ * when a message inside it is thrown. This listener calls the {@link DatabaseManagementClassesCreationMainPage#doStatusAndPageCompletionUpdate()}
+ * which gets the error message from the Source?Package element and displays it.
+ */
+ private final class ContentProviderPackageElementMessageActionListener implements
+ ActionListener
+ {
+ /*
+ * (non-Javadoc)
+ * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
+ */
+ public void actionPerformed(ActionEvent e)
+ {
+ // update status and page completion
+ doStatusAndPageCompletionUpdate();
+ }
+ }
+
+ /**
+ * Listener which handles when a check box button is pressed.
+ * Basically, it enables or disables all components stored in
+ * the constructor parameter {@link Group}. It also
+ * starts the event for validating this screen.
+ */
+ private final class CkbPanelEnablementListener implements Listener
+ {
+ private final Group panelEnablementGroup;
+
+ /*
+ * Constructor
+ *
+ * @param panelEnablementGroup SQL Helper group
+ */
+ private CkbPanelEnablementListener(Group panelEnablementGroup)
+ {
+ this.panelEnablementGroup = panelEnablementGroup;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.swt.widgets.Listener#handleEvent(org.eclipse.swt.widgets.Event)
+ */
+ public void handleEvent(Event event)
+ {
+ // get the check box which dispatched the event
+ Button checkBox = event.widget != null ? (Button) event.widget : null;
+ // proceed in case there is a check box
+ if (checkBox != null)
+ {
+ // flag indicating whether to enable/disable the controls
+ boolean enabled = checkBox.getSelection();
+ // enable/disable the children of panelEnablementGroup field
+ setCompositeChildremEnabled(panelEnablementGroup, enabled);
+ }
+ // update status and page completion
+ doStatusAndPageCompletionUpdate();
+ }
+ }
+
+ /* Constants */
+
+ // max size of the database file in bytes
+ private static final long MAX_FILE_SIZE = 1048576;
+
+ private static final String MAX_FILE_CHAR_SIZE = "1MB"; //$NON-NLS-1$
+
+ private static final String ASSESTS_FOLDER = "assets";
+
+ private final String PLUGIN_ID = "com.motorola.studio.android.db"; //$NON-NLS-1$
+
+ /* Fields */
+
+ private IProject selectedProject;
+
+ private Button ckbCreateContentProviders;
+
+ private Button ckbOverrideContentProviders;
+
+ private SourcePackageChooserPartWizard contentProviderPackageComposite;
+
+ private Group sqlOpenHelperGroup;
+
+ private Group contentProviderGroup;
+
+ private Group databaseFileGroup;
+
+ private FileChooser fileChooser;
+
+ private Group projectGroup;
+
+ private ProjectChooser projectChooser;
+
+ private ScrolledComposite scroll;
+
+ private Composite innerSchollComposite;
+
+ IPath selectedDatabasePath;
+
+ /**
+ * Get the SQL Open Helper class name.
+ *
+ * @return SQl Open Helper class name
+ */
+ public String getSQLOpenHelperClassName()
+ {
+ return getTypeName();
+ }
+
+ /**
+ * Get the destination package of the Content Providers
+ *
+ * @return Destination package of the Content Providers
+ */
+ public String getContentProviderPackage()
+ {
+ return contentProviderPackageComposite != null ? contentProviderPackageComposite
+ .getPackageText() : ""; //$NON-NLS-1$
+ }
+
+ /**
+ * Get the chosen project.
+ * @return Selected {@link IProject}.
+ */
+ public IProject getSelectedProject()
+ {
+ return selectedProject;
+ }
+
+ /**
+ * @return The {@link IPath} representing the selected Database file.
+ */
+ public IPath getDatabaseFilePath()
+ {
+ // file to be returned
+ IPath dbFile = null;
+ // text representing the file
+ String fileText = fileChooser.getText();
+ // get the database file in case there is a selected project
+ if (selectedProject != null)
+ {
+ dbFile = (fileText != null) && (fileText.length() > 0) ? new Path(fileText) : null; //$NON-NLS-1$
+ }
+ return dbFile;
+ }
+
+ /**
+ * Get the Database Open Helper Package.
+ *
+ * @return Database Open Helper Package.
+ */
+ public String getDatabaseOpenHelperPackage()
+ {
+ return getPackageText();
+ }
+
+ /**
+ * Returns <code>true</code> in case is is necessary to create
+ * the Content Provider classes, <code>false</code> otherwise.
+ *
+ * @return <code>true</code> in order to create Content Provider
+ * classes, <code>false</code> otherwise.
+ */
+ public boolean isCreateContentProviders()
+ {
+ return ckbCreateContentProviders != null ? ckbCreateContentProviders.getSelection() : false;
+ }
+
+ /**
+ * Returns <code>true</code> in case it is necessary to create
+ * the Open SQL Helper classes, <code>false</code> otherwise.
+ *
+ * @return <code>true</code> in case it is necessary to create SQL
+ * Open Helper class, <code>false</code> otherwise.
+ */
+ public boolean isCreateSQLOpenHelperClass()
+ {
+ return true;
+ }
+
+ /**
+ * Returns <code>true</code> in case one wishes to override existing
+ * Content Provider classes, <code>false</code> otherwise.
+ *
+ * @return <code>true</code> in case one wishes to override existing
+ * Content Provider classes, <code>false</code> otherwise.
+ */
+ public boolean isOverrideExistingContentProviderClasses()
+ {
+ return ckbOverrideContentProviders != null ? ckbOverrideContentProviders.getSelection()
+ : false;
+ }
+
+ /**
+ * Create a new wizard page based on selection
+ *
+ * @param pageName
+ * the page name
+ * @param isDeployWaizard
+ * show deployment fields
+ */
+ public DatabaseManagementClassesCreationMainPage(String pageName, IProject project,
+ IResource database)
+ {
+ // call super
+ super(true, pageName);
+ // set description and title
+ setDescription(CodeUtilsNLS.UI_PersistenceWizardPageDescriptionDeploy);
+ setTitle(CodeUtilsNLS.UI_PersistenceWizardPageTitleDeploy);
+
+ // update project
+ try
+ {
+ updateProject(project);
+ }
+ catch (JavaModelException e)
+ {
+ StudioLogger.error(this.getClass(), CodeUtilsNLS.Db_GenerateManagementClassesError, e);
+ IStatus status = new Status(IStatus.ERROR, PLUGIN_ID, e.getLocalizedMessage());
+ EclipseUtils.showErrorDialog(CodeUtilsNLS.Db_GenerateManagementClassesError,
+ CodeUtilsNLS.Db_GenerateManagementClassesError, status);
+ }
+ // update database
+ if (database != null)
+ {
+ selectedDatabasePath = database.getLocation();
+ }
+ // update status and page completion
+ doStatusAndPageCompletionUpdate();
+ }
+
+ /*
+ * @see NewContainerWizardPage#handleFieldChanged
+ */
+ @Override
+ protected void handleFieldChanged(String fieldName)
+ {
+ super.handleFieldChanged(fieldName);
+ // update status and page completion
+ doStatusAndPageCompletionUpdate();
+ }
+
+ /**
+ * Creates page content.
+ *
+ * @param parent wizard composite.
+ */
+ public void createControl(Composite parent)
+ {
+ try
+ {
+ // main control
+ Composite mainComposite = new Composite(parent, SWT.FILL);
+ PlatformUI.getWorkbench().getHelpSystem().setHelp(mainComposite, CONTEXT_HELP_ID);
+ mainComposite.setLayout(new FillLayout(SWT.FILL));
+
+ scroll = new ScrolledComposite(mainComposite, SWT.H_SCROLL | SWT.V_SCROLL);
+ innerSchollComposite = new Composite(scroll, SWT.NONE);
+
+ // set a grid layout with 1 column
+ GridLayout layout = new GridLayout();
+ layout.numColumns = 1;
+ innerSchollComposite.setLayout(layout);
+ innerSchollComposite.setLayoutData(new GridData(GridData.FILL, GridData.FILL, true,
+ true));
+
+ // create Project selection stuff
+ createProjectSelectionSession(innerSchollComposite);
+
+ // create Database File stuff
+ createDatabaseFileSession(innerSchollComposite);
+
+ // retrieve package from manifest file
+ String manifestPackage = null;
+ try
+ {
+ AndroidManifestFile androidManifestFile =
+ AndroidProjectManifestFile.getFromProject(getSelectedProject());
+ ManifestNode manifestNode = androidManifestFile.getManifestNode();
+ manifestPackage =
+ manifestNode.getNodeProperties().get(AndroidManifestFile.PROP_PACKAGE);
+ }
+ catch (Exception e)
+ {
+ // do nothing; default package value will be passed as null
+ }
+
+ // add SQL helper stuff
+ createOpenHelperSession(innerSchollComposite, manifestPackage);
+
+ // add content provider stuff
+ createContentProviderSession(innerSchollComposite, manifestPackage);
+
+ // add listeners
+ addListeners();
+
+ // set up scroll
+ scroll.setContent(innerSchollComposite);
+
+ scroll.setExpandHorizontal(true);
+ scroll.setExpandVertical(true);
+
+ scroll.addControlListener(new ControlAdapter()
+ {
+ @Override
+ public void controlResized(ControlEvent e)
+ {
+ scroll.setMinSize(innerSchollComposite.computeSize(SWT.DEFAULT, SWT.DEFAULT));
+ }
+ });
+
+ // set control
+ setControl(mainComposite);
+
+ // update project text field
+ if (selectedProject != null)
+ {
+ projectChooser.setText(selectedProject.getName());
+ }
+
+ // update database text field
+ if (selectedDatabasePath != null)
+ {
+ fileChooser.setText(selectedDatabasePath.toOSString());
+ }
+
+ // set focus on the name text field
+ setFocus();
+ }
+ catch (Exception e)
+ {
+ StudioLogger.error(this.getClass(), CodeUtilsNLS.Db_GenerateManagementClassesError, e);
+ IStatus status = new Status(IStatus.ERROR, PLUGIN_ID, e.getLocalizedMessage());
+ EclipseUtils.showErrorDialog(CodeUtilsNLS.Db_GenerateManagementClassesError,
+ CodeUtilsNLS.Db_GenerateManagementClassesError, status);
+ }
+ }
+
+ /**
+ * Add Project Selection Section.
+ *
+ * @param mainComposite Main Composite.
+ */
+ private void createProjectSelectionSession(Composite mainComposite)
+ {
+ // create layout for Content Provider class creation
+ projectGroup = new Group(mainComposite, SWT.NONE);
+ projectGroup.setText(CodeUtilsNLS.UI_PersistenceWizardPageSelectProjectTitle);
+
+ projectGroup.setLayout(new GridLayout(5, false));
+ projectGroup.setLayoutData(new GridData(GridData.FILL, GridData.CENTER, true, false));
+
+ // add project chooser
+ projectChooser = new ProjectChooser(projectGroup, SWT.FILL);
+ }
+
+ /**
+ * Create the Database File Session
+ *
+ * @param mainComposite Main composite
+ */
+ private void createDatabaseFileSession(Composite mainComposite)
+ {
+ // create group for Database file
+ databaseFileGroup = new Group(mainComposite, SWT.NONE);
+ databaseFileGroup.setText(CodeUtilsNLS.UI_PersistenceWizardPageDatabaseFileGroupTitle);
+ databaseFileGroup.setLayout(new GridLayout(3, false));
+ databaseFileGroup.setLayoutData(new GridData(GridData.FILL, GridData.FILL, true, false));
+
+ // add file chooser
+ fileChooser = new FileChooser(selectedProject, databaseFileGroup, SWT.NONE, null);
+ fileChooser.setFilterExtensions(new String[]
+ {
+
+ });
+ fileChooser.setLayoutData(new GridData(SWT.FILL, SWT.NONE, true, false));
+ }
+
+ /**
+ * Create the "Generate SQLOpenHelper" session
+ *
+ * @param mainComposite The Composite used as the main screen of
+ * the page.
+ * @param defaultPackageName The name of the default package to use
+ * on the package field.
+ */
+ private void createOpenHelperSession(Composite mainComposite, String defaultPackageName)
+ {
+ // check box for generating SQL Open Helper
+ sqlOpenHelperGroup = new Group(mainComposite, SWT.NONE);
+ sqlOpenHelperGroup.setText(CodeUtilsNLS.UI_PersistenceWizardPageSQLOpenHelperGroupTitle);
+ int numColumns = 5;
+ sqlOpenHelperGroup.setLayout(new GridLayout(numColumns, false));
+ sqlOpenHelperGroup.setLayoutData(new GridData(GridData.FILL, GridData.FILL, true, false));
+
+ /* Class selection for SQLOpenHelper */
+
+ createTypeNameControls(sqlOpenHelperGroup, numColumns);
+
+ /* Package selection for SQLOpenHelper */
+ setPackageFragmentRoot(getPackageFragmentRoot(), true);
+ createContainerControls(sqlOpenHelperGroup, numColumns);
+ boolean defaultPackageUsed = false;
+ if (defaultPackageName != null)
+ {
+ // try to use the manifest package, but if this fails, use the default getPackageFragment() logic
+ try
+ {
+ setPackageFragment(getPackageFragmentRoot().getPackageFragment(defaultPackageName),
+ true);
+ defaultPackageUsed = true;
+ }
+ catch (Exception e)
+ {
+ // do nothing
+ }
+ }
+ if (!defaultPackageUsed)
+ {
+ setPackageFragment(getPackageFragment(), true);
+ }
+ // create the controls for the package
+ createPackageControls(sqlOpenHelperGroup, numColumns);
+ }
+
+ /**
+ * Create the "Content Provider" session
+ *
+ * @param mainComposite The Composite used as the main screen of
+ * the page.
+ * @param defaultPackageName The name of the default package to use
+ * on the package field.
+ */
+ private void createContentProviderSession(Composite mainComposite, String defaultPackageName)
+ {
+ // create the check for generating content providers
+ ckbCreateContentProviders = new Button(mainComposite, SWT.CHECK);
+ ckbCreateContentProviders
+ .setText(CodeUtilsNLS.UI_PersistenceWizardGenerateContentProvidersForEachTable);
+ ckbCreateContentProviders.setSelection(true);
+
+ contentProviderGroup = new Group(mainComposite, SWT.NONE);
+ contentProviderGroup
+ .setText(CodeUtilsNLS.UI_PersistenceWizardPageContentProviderGroupTitle);
+ int numColumns = 5;
+ contentProviderGroup.setLayout(new GridLayout(numColumns, false));
+ contentProviderGroup.setLayoutData(new GridData(GridData.FILL, GridData.FILL, true, false));
+
+ // add the Source/Package part
+ contentProviderPackageComposite =
+ new SourcePackageChooserPartWizard(getName(), selectedProject, defaultPackageName,
+ contentProviderGroup, numColumns);
+
+ // create the check for overriding content providers
+ ckbOverrideContentProviders = new Button(contentProviderGroup, SWT.CHECK);
+ ckbOverrideContentProviders
+ .setText(CodeUtilsNLS.UI_PersistenceWizardOverrideContentProvidersIfAlreadyExists);
+ ckbOverrideContentProviders.setSelection(true);
+ ckbOverrideContentProviders.setLayoutData(new GridData(GridData.FILL, GridData.FILL, true,
+ false, numColumns, 1));
+ }
+
+ /**
+ * Add listeners to the components
+ */
+ private void addListeners()
+ {
+ // add message listener
+ contentProviderPackageComposite
+ .addMessageNotificationActionListener(new ContentProviderPackageElementMessageActionListener());
+
+ // add listener for content provider panel enablement
+ ckbCreateContentProviders.addListener(SWT.Selection, new CkbPanelEnablementListener(
+ contentProviderGroup));
+
+ // add listener for DB file chooser
+ fileChooser.addModifyListener(new DBFileChooserModifyListener());
+
+ // add listener for the Project chooser
+ projectChooser.addModifyListener(new ProjectChooserModifyListener());
+ }
+
+ /**
+ * Update the status, related to this page´s components,
+ * which interferes with this wizard. Moreover, validate the page
+ * and launch the {@link NewTypeWizardPage}{@link #setPageComplete(boolean)} method.
+ */
+ private void doStatusAndPageCompletionUpdate()
+ {
+ // variable which indicates whether there is any status with an error level
+ boolean isAnyStatusWithError = false;
+ // variable which indicates whether the wizard validation is OK
+ boolean isPageValidated = false;
+ // list to hold all status to validate
+ List<IStatus> statusList = new ArrayList<IStatus>();
+ // array list to be validated
+ IStatus[] statusArray = new IStatus[0];
+
+ // get the status from the project chooser
+ if (projectChooser != null)
+ {
+ // get the project Name
+ String projectName = projectChooser.getText();
+
+ // validate project´s name
+ IStatus status = getProjectStatus(projectName);
+ // add status to the list and check for error status, in case there is any
+ if (status != null)
+ {
+ // add status to the list
+ statusList.add(status);
+ // verify status error
+ isAnyStatusWithError = status.getSeverity() == IStatus.ERROR;
+ }
+ }
+
+ // get the status from the database file chooser path, in case there is no error status
+ if (!isAnyStatusWithError && (fileChooser != null))
+ {
+ // get path
+ String path = fileChooser.getText();
+
+ // get the status
+ IStatus status = getFileStatus(path);
+ // in case it is not null, add it to the list and check error status
+ if (status != null)
+ {
+ statusList.add(status);
+ // check error status
+ isAnyStatusWithError = status.getSeverity() == IStatus.ERROR;
+ }
+ }
+
+ // get the status from Database helper name and package selection, in case it is required and there is no error status
+ if (!isAnyStatusWithError)
+ {
+ for (IStatus status : getThisPageStatusList())
+ {
+ // add the status to its list
+ statusList.add(status);
+ // verify error status
+ isAnyStatusWithError = status.getSeverity() == IStatus.ERROR;
+ // in case there is error status, quit the loop
+ if (isAnyStatusWithError)
+ {
+ break;
+ }
+ }
+ // in case there are still no errors, check for the package name status
+ if (!isAnyStatusWithError)
+ {
+ IStatus status = getDatabasePackageStatus();
+ if (status != null)
+ {
+ // add status to its list
+ statusList.add(status);
+ // verify whether there is no error status
+ isAnyStatusWithError = status.getSeverity() == IStatus.ERROR;
+ }
+ }
+ }
+
+ // get the status of the content provider part, in case it is required and there is no error status
+ if (!isAnyStatusWithError && (ckbCreateContentProviders != null)
+ && ckbCreateContentProviders.getSelection())
+ {
+ if (this.contentProviderPackageComposite != null)
+ {
+ IStatus status = this.contentProviderPackageComposite.getMostSevereStatus();
+ if (status != null)
+ {
+ // add status to its list
+ statusList.add(status);
+ // verify whether there is no error status
+ isAnyStatusWithError = status.getSeverity() == IStatus.ERROR;
+ }
+ }
+ // in case there are still no errors, check for the package name status
+ if (!isAnyStatusWithError)
+ {
+ IStatus status = getContentProvidersPackageStatus();
+ if (status != null)
+ {
+ // add status to its list
+ statusList.add(status);
+ // verify whether there is no error status
+ isAnyStatusWithError = status.getSeverity() == IStatus.ERROR;
+ }
+ }
+ }
+
+ // get the status for minimum requirements of classes generation, if there is no error status
+ if (!isAnyStatusWithError)
+ {
+ IStatus status = getMinimumClassesGenerationRequirementsStatus();
+ if (status != null)
+ {
+ // add status to its list
+ statusList.add(status);
+ // verify whether there is no error status
+ isAnyStatusWithError = status.getSeverity() == IStatus.ERROR;
+ }
+ }
+
+ // convert to an array
+ if (statusList.size() > 0)
+ {
+ statusArray = new IStatus[statusList.size()];
+ for (int index = 0; index < statusList.size(); index++)
+ {
+ statusArray[index] = statusList.get(index);
+ }
+ }
+ else
+ {
+ // in case there is no error, status, add an OK status with the default message
+ statusArray =
+ new IStatus[]
+ {
+ new Status(IStatus.OK, PLUGIN_ID,
+ CodeUtilsNLS.UI_PersistenceWizardPageDescriptionDeploy)
+ };
+ }
+
+ // the most severe status will be displayed and the OK button enabled/disabled.
+ updateStatus(statusArray);
+
+ /*
+ * The page is validated if:
+ * 1 - There are no status errors
+ * 2 - The SQL Helper creation and Content Provider creation check boxes exists
+ */
+ isPageValidated = (!isAnyStatusWithError) && ((ckbCreateContentProviders != null));
+
+ // set page completion
+ setPageComplete(isPageValidated);
+ }
+
+ /**
+ * Get the SQL Open Helper package name status. Basically, it points an error
+ * in case there is no package.
+ *
+ * @return SQL Open Helper package name status
+ */
+ private IStatus getDatabasePackageStatus()
+ {
+ IStatus status = null;
+
+ // get the package name
+ String packageName = getPackageText();
+ // it must have a value
+ if ((packageName == null) || (packageName.length() == 0))
+ {
+ status =
+ new Status(
+ IStatus.ERROR,
+ PLUGIN_ID,
+ CodeUtilsNLS.DatabaseManagementClassesCreationMainPage_UI_OpenHelperPackageNameMustNotBeEmpty);
+ }
+ else
+ {
+ status = new Status(IStatus.OK, PLUGIN_ID, ""); //$NON-NLS-1$
+ }
+
+ return status;
+ }
+
+ /**
+ * Get the Content Providers package name status. Basically, it points an error
+ * in case there is no package.
+ *
+ * @return SQL Open Helper package name status
+ */
+ private IStatus getContentProvidersPackageStatus()
+ {
+ IStatus status = null;
+
+ // get the package name
+ String packageName =
+ contentProviderPackageComposite != null ? contentProviderPackageComposite
+ .getPackageText() : ""; //$NON-NLS-1$
+ // it must have a value
+ if ((packageName == null) || (packageName.length() == 0))
+ {
+ status =
+ new Status(
+ IStatus.ERROR,
+ PLUGIN_ID,
+ CodeUtilsNLS.DatabaseManagementClassesCreationMainPage_UI_ContentProvidersPackageNameMustNotBeEmpty);
+ }
+ else
+ {
+ status = new Status(IStatus.OK, PLUGIN_ID, ""); //$NON-NLS-1$
+ }
+
+ return status;
+ }
+
+ /**
+ * Return the status regarding the mininum requirements
+ * for the generation of classes.
+ *
+ * These requirements are simple:
+ * there must be either the creation of SQL Open Helper classes.
+ *
+ * @return Status regarding minimum requirements for the classes
+ * generation.
+ */
+ private IStatus getMinimumClassesGenerationRequirementsStatus()
+ {
+ IStatus status = null;
+ status = new Status(IStatus.OK, PLUGIN_ID, ""); //$NON-NLS-1$
+ return status;
+ }
+
+ /**
+ * Get this page Status list considering only
+ * the {@link NewTypeWizardPage} inheritance. It means
+ * that this method return status only related to elements
+ * from {@link NewTypeWizardPage}. Other things associated, for instance,
+ * with {@link FileChooser} or {@link ProjectChooser} are treated
+ * some place else.
+ *
+ * @return {@link NewTypeWizardPage} related status list
+ */
+ private IStatus[] getThisPageStatusList()
+ {
+ return new IStatus[]
+ {
+ fContainerStatus,
+ isEnclosingTypeSelected() ? fEnclosingTypeStatus : fPackageStatus, fTypeNameStatus,
+ };
+ }
+
+ /**
+ * <p>
+ * Check whether a Project Name refers to a Project. In case it does,
+ * <code>null</code> is returned, otherwise, an {@link IStatus} is returned
+ * indicating the error.
+ * </p>
+ * <p>
+ * Note the the Project must belong to the current workspace.
+ * </p>
+ *
+ * @param projectName Project Name to be validated.
+ * @return
+ */
+ private IStatus getProjectStatus(String projectName)
+ {
+ IStatus status = null;
+
+ if ((projectName == null) || (projectName.length() == 0))
+ {
+ // there must be a selected project
+ status =
+ new Status(IStatus.ERROR, PLUGIN_ID,
+ CodeUtilsNLS.UI_PersistenceWizardPageThereMustBeASelectedProject);
+ }
+ else
+ {
+
+ // get root workspace
+ IWorkspaceRoot workspaceRoot = ResourcesPlugin.getWorkspace().getRoot();
+
+ IProject[] projects = workspaceRoot.getProjects();
+
+ // iterate through the projects
+ if ((projects != null) && (projects.length > 0))
+ {
+ // flag indicating whether the project was found
+ boolean isProjectFound = false;
+
+ for (IProject project : projects)
+ {
+ if (project.getName().equals(projectName))
+ {
+ // the validation is OK, set the flag and quit the loop
+ isProjectFound = true;
+ break;
+ }
+ }
+ // in case the project was not found, set the flag
+ if (!isProjectFound)
+ {
+ status =
+ new Status(IStatus.ERROR, PLUGIN_ID,
+ CodeUtilsNLS.UI_PersistenceWizardPageTheEnteredProjectIsInvalid);
+ }
+ }
+ else
+ {
+ // there must be a selected project
+ status =
+ new Status(IStatus.ERROR, PLUGIN_ID,
+ CodeUtilsNLS.UI_PersistenceWizardPageThereMustBeASelectedProject);
+ }
+ }
+
+ return status;
+ }
+
+ /**
+ * Get a file validation status. In case the file is OK, <code>null</code>
+ * is returned otherwise an error status is returned.
+ *
+ * @param filePath The file path (including the file name) to be
+ * validated
+ *
+ * @return <code>null</code> in case the validation is OK, an error
+ * status otherwise.
+ */
+ private IStatus getFileStatus(String filePath)
+ {
+ // get status
+ IStatus status = null;
+ // there must be a file path and it must not be empty
+ if ((filePath == null) || filePath.equals("")) //$NON-NLS-1$
+ {
+ status =
+ new Status(IStatus.ERROR, PLUGIN_ID,
+ CodeUtilsNLS.UI_PersistenceWizardPageThereMustBeASelectedDatabaseFile);
+ }
+ else
+ {
+ // validation result
+ boolean isFileOK = false;
+ // get path object
+ Path path = new Path(filePath);
+
+ // testing if the entered path is a folder
+ isFileOK = path.toFile().isFile();
+ if (!isFileOK)
+ {
+ status =
+ new Status(IStatus.ERROR, PLUGIN_ID,
+ CodeUtilsNLS.UI_PersistenceWizardPageTheEnteredPathIsInvalid);
+ }
+ else
+ {
+ isFileOK = path.isValidPath(path.toString());
+
+ if (!isFileOK)
+ {
+ status =
+ new Status(IStatus.ERROR, PLUGIN_ID,
+ CodeUtilsNLS.UI_PersistenceWizardPageTheEnteredPathIsInvalid);
+ }
+ else
+ {
+ // Test if file exists
+ isFileOK = path.toFile().exists();
+ if (!isFileOK)
+ {
+
+ status =
+ new Status(IStatus.ERROR, PLUGIN_ID,
+ CodeUtilsNLS.UI_PersistenceWizardPageFileDoesNotExist);
+ }
+ else
+ {
+ try
+ {
+ if (!DatabaseUtils.isValidSQLiteDatabase(path.toFile()))
+ {
+ status =
+ new Status(IStatus.ERROR, PLUGIN_ID,
+ CodeUtilsNLS.UI_PersistenceWizardPageFileNotValid);
+
+ }
+ }
+ catch (IOException e)
+ {
+ status =
+ new Status(IStatus.WARNING, PLUGIN_ID,
+ CodeUtilsNLS.UI_PersistenceWizardPageFileNotEvaluated);
+ }
+
+ if (path.toFile().length() > MAX_FILE_SIZE)
+ {
+ status =
+ new Status(IStatus.WARNING, PLUGIN_ID, NLS.bind(
+ CodeUtilsNLS.UI_PersistenceWizardPageFileTooLarge,
+ MAX_FILE_CHAR_SIZE));
+ }
+ }
+
+ }
+ }
+ }
+
+ // in case there is no status, the validation went OK, or if the status is not an ERROR
+ // verify whether the file belongs to the project assets
+ if ((status == null) || (status.getSeverity() != IStatus.ERROR))
+ {
+ // verify whether the file is within asset´s folder
+ if (!isFileWithinProjectAssets(new Path(filePath)))
+ {
+ // set the status
+ status =
+ new Status(
+ IStatus.WARNING,
+ PLUGIN_ID,
+ CodeUtilsNLS.UI_PersistenceWizardPageTheDatabaseFileWillBeCopiedToProjectsAssetsFolder);
+ }
+ }
+
+ return status;
+ }
+
+ /**
+ * Verifies whether a given file path belongs to the Project´s assets
+ * folder. <code>true</code> is returned in case it does, <code>false</code> otherwise.
+ *
+ * @param path File path which will be verified whether it is in the Project´s assets directory.
+ *
+ * @return <code>true</code> in case the file path belongs to the Project´s assets, <code>false</code>
+ * otherwise.
+ */
+ private boolean isFileWithinProjectAssets(IPath path)
+ {
+ boolean isPathWithin = false;
+
+ // proceed in case there is a project and a path
+ if ((selectedProject != null) && (path != null))
+ {
+ // make the assets path = project path + assets + file name
+ IPath databaseInAssetsPath =
+ selectedProject.getLocation().addTrailingSeparator().append(ASSESTS_FOLDER)
+ .addTrailingSeparator().append(path.toFile().getName());
+
+ // the database path and the entered path must match
+ isPathWithin = databaseInAssetsPath.toOSString().equals(path.toOSString());
+ }
+
+ return isPathWithin;
+ }
+
+ /**
+ * Enable/disable children of the entered {@link Composite}.
+ *
+ * @param composite Composite to have its children enabled/disabled
+ * @param enabled <code>true</code> for enabling the elements, <code>false</code>
+ * for disabling the elements.
+ */
+ private void setCompositeChildremEnabled(Composite composite, boolean enabled)
+ {
+ Control[] controls = composite.getChildren();
+ if ((controls != null) && (controls.length > 0))
+ {
+ for (Control control : controls)
+ {
+ control.setEnabled(enabled);
+ }
+ }
+ }
+
+ /**
+ * Update project information within this wizard. Besides
+ * the basic project field input update, the package fragment
+ * root is determined and correctly updated in this wizard. Therefore,
+ * use this method preferably to update the project so everything in the
+ * wizard is updated accordingly.
+ *
+ * @param project {@link IProject} information to update
+ * @throws JavaModelException Exception thrown when there are problems retrieving
+ * the project´s fragment root.
+ */
+ private void updateProject(IProject project) throws JavaModelException
+ {
+ if (project != null)
+ {
+ // set selected project
+ this.selectedProject = project;
+
+ // update project text in case it is not already set correctly
+ if ((projectChooser != null) && !projectChooser.getText().equals(project.getName()))
+ {
+ projectChooser.setText(project.getName());
+ }
+
+ // get the java project
+ IJavaProject javaProject = JavaCore.create(project);
+ IPackageFragmentRoot[] possibleRoots = null;
+ // continue in case it does exist
+ if (javaProject != null)
+ {
+ // get all possible roots
+ possibleRoots = javaProject.getPackageFragmentRoots();
+ // select the first one, in case it does exist
+ if ((possibleRoots != null) && (possibleRoots.length > 0))
+ {
+ // set the first one
+ setPackageFragmentRoot(possibleRoots[0], true);
+ if (contentProviderPackageComposite != null)
+ {
+ contentProviderPackageComposite.setPackageFragmentRoot(possibleRoots[0],
+ true);
+ }
+ }
+ }
+ }
+ else
+ {
+ // update null information
+ selectedProject = null;
+ setPackageFragmentRoot(null, true);
+ if (contentProviderPackageComposite != null)
+ {
+ contentProviderPackageComposite.setPackageFragmentRoot(null, true);
+ }
+ }
+ }
+}
diff --git a/src/plugins/android.codeutils/src/com/motorola/studio/android/codeutils/wizards/DatabaseManagementClassesCreationWizard.java b/src/plugins/android.codeutils/src/com/motorola/studio/android/codeutils/wizards/DatabaseManagementClassesCreationWizard.java
new file mode 100644
index 0000000..3baf292
--- /dev/null
+++ b/src/plugins/android.codeutils/src/com/motorola/studio/android/codeutils/wizards/DatabaseManagementClassesCreationWizard.java
@@ -0,0 +1,317 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.motorola.studio.android.codeutils.wizards;
+
+import java.lang.reflect.InvocationTargetException;
+
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.SubMonitor;
+import org.eclipse.jdt.ui.JavaUI;
+import org.eclipse.jface.operation.IRunnableWithProgress;
+import org.eclipse.jface.wizard.Wizard;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.WorkbenchException;
+
+import com.motorola.studio.android.codeutils.CodeUtilsActivator;
+import com.motorola.studio.android.codeutils.db.utils.DatabaseUtils;
+import com.motorola.studio.android.codeutils.i18n.CodeUtilsNLS;
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.common.log.UsageDataConstants;
+import com.motorola.studio.android.common.preferences.DialogWithToggleUtils;
+import com.motorola.studio.android.common.utilities.EclipseUtils;
+
+/**
+ *
+ * Wizard to create classes which aids the database management.
+ *
+ */
+public class DatabaseManagementClassesCreationWizard extends Wizard
+{
+
+ private static final String SDK_SWITCH_PERSPECTIVE_TO_MOTODEV_STUDIO_ANDROID_KEY =
+ "switch.perspective.to.motodevstudioandroid"; //$NON-NLS-1$
+
+ private static final String SDK_SWITCH_PERSPECTIVE_TO_JAVA_KEY = "switch.perspective.to.java"; //$NON-NLS-1$
+
+ /**
+ * This listener generates Database Management classes. It has the
+ * characteristic of displaying a progress bar.
+ */
+ private final class GenerateDatabaseManagementClassesRunnableWithProgress implements
+ IRunnableWithProgress
+ {
+ private final IProject project;
+
+ private final IPath databaseFilePath;
+
+ private final boolean isCreateSQLOpenHelper;
+
+ private final boolean isCreateContentProviders;
+
+ private final String sqlOpenHelperPackageName;
+
+ private final String contentProvidersPackageName;
+
+ private final String sqlOpenHelperClassName;
+
+ private final boolean isOverrideContentProviders;
+
+ /**
+ * Constructor for assigning values
+ *
+ * @param project Target Project
+ * @param databaseFilePath Database file path
+ * @param isCreateSQLOpenHelper Creates or not SQL Open Helper class
+ * @param isCreateContentProviders Creates or not Content Provider classes
+ * @param sqlOpenHelperPackageName SQL Open Helper Package Name
+ * @param contentProvidersPackageName Content Provider classes package name
+ * @param sqlOpenHelperClassName SQL Open Helper class Name
+ * @param isOverrideContentProviders Overrides or not existing Content Provider classes
+ */
+ public GenerateDatabaseManagementClassesRunnableWithProgress(IProject project,
+ IPath databaseFilePath, boolean isCreateSQLOpenHelper,
+ boolean isCreateContentProviders, String sqlOpenHelperPackageName,
+ String contentProvidersPackageName, String sqlOpenHelperClassName,
+ boolean isOverrideContentProviders)
+ {
+ this.project = project;
+ this.databaseFilePath = databaseFilePath;
+ this.isCreateSQLOpenHelper = isCreateSQLOpenHelper;
+ this.isCreateContentProviders = isCreateContentProviders;
+ this.sqlOpenHelperPackageName = sqlOpenHelperPackageName;
+ this.contentProvidersPackageName = contentProvidersPackageName;
+ this.sqlOpenHelperClassName = sqlOpenHelperClassName;
+ this.isOverrideContentProviders = isOverrideContentProviders;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.jface.operation.IRunnableWithProgress#run(org.eclipse.core.runtime.IProgressMonitor)
+ */
+ @Override
+ public void run(IProgressMonitor monitor) throws InvocationTargetException,
+ InterruptedException
+ {
+ // create Database Management classes
+ try
+ {
+ // sub monitor
+ SubMonitor subMonitor = SubMonitor.convert(monitor, 10);
+
+ // copy Database file to assets folder, in case it is not already there
+ DatabaseUtils.copyDatabaseFileToAssetsFolder(databaseFilePath, project,
+ subMonitor.newChild(4));
+
+ DatabaseUtils.createDatabaseManagementClasses(project, databaseFilePath.toFile()
+ .getName(), isCreateSQLOpenHelper, isCreateContentProviders,
+ sqlOpenHelperPackageName, contentProvidersPackageName,
+ sqlOpenHelperClassName, isOverrideContentProviders, false, subMonitor
+ .newChild(6), true);
+ }
+ catch (Exception e)
+ {
+ StudioLogger.error(DatabaseManagementClassesCreationWizard.class,
+ CodeUtilsNLS.DATABASE_DEPLOY_ERROR_DEPLOYING_DATABASE, e);
+ IStatus status =
+ new Status(IStatus.ERROR, CodeUtilsActivator.PLUGIN_ID,
+ e.getLocalizedMessage());
+ EclipseUtils.showErrorDialog(CodeUtilsNLS.DATABASE_DEPLOY_ERROR_DEPLOYING_DATABASE,
+ CodeUtilsNLS.DATABASE_DEPLOY_ERROR_DEPLOYING_DATABASE, status);
+ }
+
+ }
+ }
+
+ // deployment page
+ private DatabaseManagementClassesCreationMainPage page = null;
+
+ public DatabaseManagementClassesCreationWizard(IProject selectedProject,
+ IResource selectedDatabase)
+ {
+ // set title
+ setWindowTitle(CodeUtilsNLS.UI_PersistenceWizardPageTitleDeploy);
+ // set properties
+ setNeedsProgressMonitor(true);
+ setDefaultPageImageDescriptor(CodeUtilsActivator.imageDescriptorFromPlugin(
+ CodeUtilsActivator.PLUGIN_ID, "icons/wizban/create_management_classes.png")); //$NON-NLS-1$
+ // set page
+ this.page = new DatabaseManagementClassesCreationMainPage("Main Page", selectedProject, //$NON-NLS-1$
+ selectedDatabase);
+
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.jface.wizard.Wizard#addPages()
+ */
+ @Override
+ public void addPages()
+ {
+ addPage(this.page);
+ }
+
+ /**
+ * Generate the requested classes and finishes this wizard.
+ */
+ @Override
+ public boolean performFinish()
+ {
+ // get project
+ IProject project = page.getSelectedProject();
+ // get the file name
+ IPath databaseFilePath = page.getDatabaseFilePath();
+ // get package of content providers classes
+ String contentProvidersPackageName = page.getContentProviderPackage();
+ // get the package of SQL Open Helper
+ String sqlOpenHelperPackageName = page.getDatabaseOpenHelperPackage();
+ // get the SQL Open Helper class name
+ String sqlOpenHelperClassName = page.getSQLOpenHelperClassName();
+ // create or no open helper
+ boolean isCreateSQLOpenHelper = page.isCreateSQLOpenHelperClass();
+ // create or not content providers
+ boolean isCreateContentProviders = page.isCreateContentProviders();
+ // override or not content providers
+ boolean isOverrideContentProviders = page.isOverrideExistingContentProviderClasses();
+
+ try
+ {
+ // create database management classes
+ getContainer().run(
+ true,
+ true,
+ new GenerateDatabaseManagementClassesRunnableWithProgress(project,
+ databaseFilePath, isCreateSQLOpenHelper, isCreateContentProviders,
+ sqlOpenHelperPackageName, contentProvidersPackageName,
+ sqlOpenHelperClassName, isOverrideContentProviders));
+ }
+ catch (Exception e)
+ {
+ StudioLogger.error(DatabaseManagementClassesCreationWizard.class,
+ CodeUtilsNLS.DATABASE_DEPLOY_ERROR_DEPLOYING_DATABASE, e);
+ IStatus status =
+ new Status(IStatus.ERROR, CodeUtilsActivator.PLUGIN_ID, e.getLocalizedMessage());
+ EclipseUtils.showErrorDialog(CodeUtilsNLS.DATABASE_DEPLOY_ERROR_DEPLOYING_DATABASE,
+ CodeUtilsNLS.DATABASE_DEPLOY_ERROR_DEPLOYING_DATABASE, status);
+ }
+
+ try
+ {
+
+ if ((PlatformUI.getWorkbench().getActiveWorkbenchWindow() != null)
+ && (PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage() != null))
+ {
+ if (PlatformUI.getWorkbench().getPerspectiveRegistry()
+ .findPerspectiveWithId(CodeUtilsActivator.PERSPECTIVE_ID) != null)
+ {
+ if (!PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage()
+ .getPerspective().getId().equals(CodeUtilsActivator.PERSPECTIVE_ID))
+ {
+
+ //ask users if they want to change perspective to check files created
+ if (DialogWithToggleUtils
+ .showQuestion(
+ SDK_SWITCH_PERSPECTIVE_TO_MOTODEV_STUDIO_ANDROID_KEY,
+ CodeUtilsNLS.UI_PersistenceWizard_ChangePerspectiveToMOTODEVStudioAndroid_DialogTitle,
+ CodeUtilsNLS.UI_PersistenceWizard_ChangePerspectiveToMOTODEVStudioAndroid_DialogMessage))
+ {
+ PlatformUI.getWorkbench().showPerspective(
+ CodeUtilsActivator.PERSPECTIVE_ID,
+ PlatformUI.getWorkbench().getActiveWorkbenchWindow());
+ }
+ }
+ }
+ else
+ {
+ if (DialogWithToggleUtils
+ .showQuestion(
+ SDK_SWITCH_PERSPECTIVE_TO_JAVA_KEY,
+ CodeUtilsNLS.UI_PersistenceWizard_ChangePerspectiveToJava_DialogTitle,
+ CodeUtilsNLS.UI_PersistenceWizard_ChangePerspectiveToJava_DialogMessage))
+ {
+ PlatformUI.getWorkbench().showPerspective(JavaUI.ID_PERSPECTIVE,
+ PlatformUI.getWorkbench().getActiveWorkbenchWindow());
+ }
+ }
+ }
+ }
+ catch (WorkbenchException e)
+ {
+ StudioLogger.warn("It was not possible to change perspective to " //$NON-NLS-1$
+ + CodeUtilsActivator.PERSPECTIVE_ID);
+ }
+
+ // create UDC log for db file size
+ StudioLogger
+ .collectUsageData(
+ UsageDataConstants.WHAT_DATABASE_MANAGMT_CLASSES, //$NON-NLS-1$
+ UsageDataConstants.KIND_DATABASE_MANAGMT_CLASSES,
+ "Database classes created. Database filesize " + getDatabaseFileSize(databaseFilePath) + ".", //$NON-NLS-1$ //$NON-NLS-2$
+ CodeUtilsActivator.PLUGIN_ID, CodeUtilsActivator.getDefault().getBundle()
+ .getVersion().toString());
+
+ return true;
+ }
+
+ /**
+ * Returns a database file size string in Kb, Mb or Gb. Example: '10 Kb'.
+ * @param databaseFilePath The database file path.
+ * @return String containing the database file size.
+ */
+ private String getDatabaseFileSize(IPath databaseFilePath)
+ {
+
+ String fileSizeStr = null;
+
+ long fileSize = databaseFilePath.toFile().length();
+
+ long KBsize = fileSize / 1024;
+
+ if (KBsize > 1024)
+ {
+
+ long MBSize = KBsize / 1024;
+
+ if (MBSize > 1024)
+ {
+
+ long GBSize = MBSize / 1024;
+
+ fileSizeStr = GBSize + " Gb"; //$NON-NLS-1$
+
+ }
+ else
+ {
+ fileSizeStr = MBSize + " Mb"; //$NON-NLS-1$
+
+ }
+ }
+ else
+ {
+ fileSizeStr = KBsize + " Kb"; //$NON-NLS-1$
+
+ }
+
+ return fileSizeStr;
+ }
+
+}
diff --git a/src/plugins/android.codeutils/src/com/motorola/studio/android/codeutils/wizards/SourcePackageChooserPartWizard.java b/src/plugins/android.codeutils/src/com/motorola/studio/android/codeutils/wizards/SourcePackageChooserPartWizard.java
new file mode 100644
index 0000000..b04c564
--- /dev/null
+++ b/src/plugins/android.codeutils/src/com/motorola/studio/android/codeutils/wizards/SourcePackageChooserPartWizard.java
@@ -0,0 +1,215 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.codeutils.wizards;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.jdt.core.IJavaProject;
+import org.eclipse.jdt.core.IPackageFragmentRoot;
+import org.eclipse.jdt.core.JavaCore;
+import org.eclipse.jdt.core.JavaModelException;
+import org.eclipse.jdt.internal.ui.dialogs.StatusUtil;
+import org.eclipse.jdt.ui.wizards.NewTypeWizardPage;
+import org.eclipse.swt.widgets.Composite;
+
+import com.motorola.studio.android.codeutils.CodeUtilsActivator;
+import com.motorola.studio.android.codeutils.i18n.CodeUtilsNLS;
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.common.utilities.EclipseUtils;
+
+/**
+ * <p>
+ * Class which holds a Source/Package selection part. It is a workaround
+ * in order to use Source/Package selection twice in the same page of
+ * a certain wizard.
+ * </p>
+ * <p>
+ * Despite the fact the this class implements <code>NewTypeWizardPage</code>, it
+ * should be be used as a wizard. It is to be used as a part of it to add a
+ * Source/Package functionality.
+ * </p>
+ */
+@SuppressWarnings("restriction")
+public class SourcePackageChooserPartWizard extends NewTypeWizardPage
+{
+ /**
+ * Command representing the event of a message dispatched.
+ */
+ public static final String MESSAGE_DISPATCHED_ACTION = "MESSAGE_DISPATCHED";
+
+ /**
+ * Id representing the action of a message dispatched.
+ */
+ public static final int MESSAGE_DISPATCHED_ID_ACTION = 0;
+
+ private int numColumnsGridLayout = 0;
+
+ private final List<ActionListener> messageActionListenerList = new ArrayList<ActionListener>();
+
+ private IStatus mostSevereStatus;
+
+ /**
+ * Get the most severe status of this Part Page.
+ *
+ * @return The most severe status.
+ */
+ public IStatus getMostSevereStatus()
+ {
+ return mostSevereStatus;
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param pageName Page Name
+ * @param project Project related to the wizard
+ * @param defaultPackageName The name of the default package to use
+ * on the package field
+ * @param parent The parent composite
+ * @param numColumnsGridLayout The number of columns on the grid layout
+ */
+ public SourcePackageChooserPartWizard(String pageName, IProject project,
+ String defaultPackageName, Composite parent, int numColumnsGridLayout)
+ {
+ super(true, pageName);
+ // set description and title
+ setDescription(CodeUtilsNLS.UI_PersistenceWizardPageDescriptionDeploy);
+ setTitle(CodeUtilsNLS.UI_PersistenceWizardPageTitleDeploy);
+
+ // set attributes
+ this.numColumnsGridLayout = numColumnsGridLayout;
+ if (project != null)
+ {
+ // get the java project
+ IJavaProject javaProject = JavaCore.create(project);
+ IPackageFragmentRoot[] possibleRoots = null;
+ // continue in case it does exist
+ if (javaProject != null)
+ {
+ try
+ {
+ // get all possible roots
+ possibleRoots = javaProject.getPackageFragmentRoots();
+ // select the first one, in case it does exist
+ if ((possibleRoots != null) && (possibleRoots.length > 0))
+ {
+ // set the first one
+ setPackageFragmentRoot(possibleRoots[0], true);
+ }
+ }
+ catch (JavaModelException e)
+ {
+ StudioLogger.error(this.getClass(),
+ CodeUtilsNLS.Db_GenerateManagementClassesError, e);
+ IStatus status =
+ new Status(IStatus.ERROR, CodeUtilsActivator.PLUGIN_ID,
+ e.getLocalizedMessage());
+ EclipseUtils.showErrorDialog(CodeUtilsNLS.Db_GenerateManagementClassesError,
+ CodeUtilsNLS.Db_GenerateManagementClassesError, status);
+ }
+ }
+ }
+
+ doStatusUpdate();
+
+ // create GUI here because since this GUI is an auxiliary one, the interface does not work when created in the org.eclipse.jface.dialogs.IDialogPage#createControl(org.eclipse.swt.widgets.Composite method
+ setPackageFragmentRoot(getPackageFragmentRoot(), true);
+ createContainerControls(parent, this.numColumnsGridLayout);
+
+ boolean defaultPackageUsed = false;
+ if (defaultPackageName != null)
+ {
+ // try to use the manifest package, but if this fails, use the default getPackageFragment() logic
+ try
+ {
+ setPackageFragment(getPackageFragmentRoot().getPackageFragment(defaultPackageName),
+ true);
+ defaultPackageUsed = true;
+ }
+ catch (Exception e)
+ {
+ // do nothing
+ }
+ }
+ if (!defaultPackageUsed)
+ {
+ setPackageFragment(getPackageFragment(), true);
+ }
+ createPackageControls(parent, this.numColumnsGridLayout);
+ }
+
+ /**
+ * Add an action listener to which notificates the caller
+ * when a message is dispatched.
+ *
+ * @param actionListener Listener to be notified when some field changed.
+ */
+ public void addMessageNotificationActionListener(ActionListener actionListener)
+ {
+ messageActionListenerList.add(actionListener);
+ }
+
+ /*
+ * @see NewContainerWizardPage#handleFieldChanged
+ */
+ @Override
+ protected void handleFieldChanged(String fieldName)
+ {
+ super.handleFieldChanged(fieldName);
+
+ doStatusUpdate();
+
+ ActionEvent actionEvent =
+ new ActionEvent(this, MESSAGE_DISPATCHED_ID_ACTION, MESSAGE_DISPATCHED_ACTION);
+
+ // execute listeners
+ for (ActionListener listener : messageActionListenerList)
+ {
+ listener.actionPerformed(actionEvent);
+ }
+ }
+
+ private void doStatusUpdate()
+ {
+ // status of all used components
+ IStatus[] status =
+ new IStatus[]
+ {
+ fContainerStatus,
+ isEnclosingTypeSelected() ? fEnclosingTypeStatus : fPackageStatus,
+ };
+
+ this.mostSevereStatus = StatusUtil.getMostSevere(status);
+ if (this.mostSevereStatus != null)
+ {
+ StatusUtil.applyToStatusLine(this, this.mostSevereStatus);
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.dialogs.IDialogPage#createControl(org.eclipse.swt.widgets.Composite)
+ */
+ public void createControl(Composite parent)
+ {
+ // do nothing
+ }
+}
diff --git a/src/plugins/android.codeutils/src/com/motorola/studio/android/command/NewActivityBasedOnTemplateHandler.java b/src/plugins/android.codeutils/src/com/motorola/studio/android/command/NewActivityBasedOnTemplateHandler.java
new file mode 100644
index 0000000..43fc492
--- /dev/null
+++ b/src/plugins/android.codeutils/src/com/motorola/studio/android/command/NewActivityBasedOnTemplateHandler.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.command;
+
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+
+/**
+ * Command to open the New Activity Based On Template wizard through the MOTODEV menu.
+ */
+public class NewActivityBasedOnTemplateHandler extends NewWizardHandler
+{
+ /* (non-Javadoc)
+ * @see org.eclipse.core.commands.IHandler#execute(org.eclipse.core.commands.ExecutionEvent)
+ */
+ public Object execute(ExecutionEvent event) throws ExecutionException
+ {
+ openWizard(new com.motorola.studio.android.wizards.buildingblocks.NewActivityBasedOnTemplateWizard());
+
+ return null;
+ }
+}
diff --git a/src/plugins/android.codeutils/src/com/motorola/studio/android/command/NewActivityWizard.java b/src/plugins/android.codeutils/src/com/motorola/studio/android/command/NewActivityWizard.java
new file mode 100644
index 0000000..d2ef672
--- /dev/null
+++ b/src/plugins/android.codeutils/src/com/motorola/studio/android/command/NewActivityWizard.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.command;
+
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+
+/**
+ * Command to open the New Activity wizard through the MOTODEV menu
+ */
+public class NewActivityWizard extends NewWizardHandler
+{
+ /* (non-Javadoc)
+ * @see org.eclipse.core.commands.IHandler#execute(org.eclipse.core.commands.ExecutionEvent)
+ */
+ public Object execute(ExecutionEvent event) throws ExecutionException
+ {
+ openWizard(new com.motorola.studio.android.wizards.buildingblocks.NewActivityWizard());
+
+ return null;
+ }
+}
diff --git a/src/plugins/android.codeutils/src/com/motorola/studio/android/command/NewBroadcastReceiverWizard.java b/src/plugins/android.codeutils/src/com/motorola/studio/android/command/NewBroadcastReceiverWizard.java
new file mode 100644
index 0000000..731676c
--- /dev/null
+++ b/src/plugins/android.codeutils/src/com/motorola/studio/android/command/NewBroadcastReceiverWizard.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.command;
+
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+
+import com.motorola.studio.android.wizards.buildingblocks.NewReceiverWizard;
+
+/**
+ * Command to open the New Broadcast Receiver wizard through the MOTODEV menu
+ */
+public class NewBroadcastReceiverWizard extends NewWizardHandler
+{
+ /* (non-Javadoc)
+ * @see org.eclipse.core.commands.IHandler#execute(org.eclipse.core.commands.ExecutionEvent)
+ */
+ public Object execute(ExecutionEvent event) throws ExecutionException
+ {
+ openWizard(new NewReceiverWizard());
+
+ return null;
+ }
+}
diff --git a/src/plugins/android.codeutils/src/com/motorola/studio/android/command/NewContentProviderWizard.java b/src/plugins/android.codeutils/src/com/motorola/studio/android/command/NewContentProviderWizard.java
new file mode 100644
index 0000000..e8dd147
--- /dev/null
+++ b/src/plugins/android.codeutils/src/com/motorola/studio/android/command/NewContentProviderWizard.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.command;
+
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+
+import com.motorola.studio.android.wizards.buildingblocks.NewProviderWizard;
+
+/**
+ * Command to open the New Content Provider wizard through the MOTODEV menu
+ */
+public class NewContentProviderWizard extends NewWizardHandler
+{
+ /* (non-Javadoc)
+ * @see org.eclipse.core.commands.IHandler#execute(org.eclipse.core.commands.ExecutionEvent)
+ */
+ public Object execute(ExecutionEvent event) throws ExecutionException
+ {
+ openWizard(new NewProviderWizard());
+
+ return null;
+ }
+}
diff --git a/src/plugins/android.codeutils/src/com/motorola/studio/android/command/NewServiceWizard.java b/src/plugins/android.codeutils/src/com/motorola/studio/android/command/NewServiceWizard.java
new file mode 100644
index 0000000..12cd415
--- /dev/null
+++ b/src/plugins/android.codeutils/src/com/motorola/studio/android/command/NewServiceWizard.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.command;
+
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+
+/**
+ * Command to open the New Service wizard through the MOTODEV menu.
+ */
+public class NewServiceWizard extends NewWizardHandler
+{
+ /* (non-Javadoc)
+ * @see org.eclipse.core.commands.IHandler#execute(org.eclipse.core.commands.ExecutionEvent)
+ */
+ public Object execute(ExecutionEvent event) throws ExecutionException
+ {
+ openWizard(new com.motorola.studio.android.wizards.buildingblocks.NewServiceWizard());
+
+ return null;
+ }
+}
diff --git a/src/plugins/android.codeutils/src/com/motorola/studio/android/command/NewWidgetProviderWizard.java b/src/plugins/android.codeutils/src/com/motorola/studio/android/command/NewWidgetProviderWizard.java
new file mode 100644
index 0000000..f15f655
--- /dev/null
+++ b/src/plugins/android.codeutils/src/com/motorola/studio/android/command/NewWidgetProviderWizard.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.command;
+
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+
+/**
+ * Command to open the New Widget Provider wizard through the MOTODEV menu.
+ */
+public class NewWidgetProviderWizard extends NewWizardHandler
+{
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.commands.IHandler#execute(org.eclipse.core.commands.ExecutionEvent)
+ */
+ public Object execute(ExecutionEvent event) throws ExecutionException
+ {
+ openWizard(new com.motorola.studio.android.wizards.buildingblocks.NewWidgetProviderWizard());
+
+ return null;
+ }
+}
diff --git a/src/plugins/android.codeutils/src/com/motorola/studio/android/command/NewWizardHandler.java b/src/plugins/android.codeutils/src/com/motorola/studio/android/command/NewWizardHandler.java
new file mode 100644
index 0000000..64d83b1
--- /dev/null
+++ b/src/plugins/android.codeutils/src/com/motorola/studio/android/command/NewWizardHandler.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.command;
+
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.StructuredSelection;
+import org.eclipse.jface.wizard.WizardDialog;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.ui.INewWizard;
+import org.eclipse.ui.PlatformUI;
+
+/**
+ * Abstract class intended to be used to create menu commands
+ * for items in the MOTODEV Menu.
+ */
+abstract class NewWizardHandler extends AbstractHandler
+{
+ private static final int WIZARD_WIDTH = 500;
+
+ /**
+ * Opens a wizard.
+ *
+ * @param wizard the wizard to be opened.
+ */
+ protected void openWizard(final INewWizard wizard)
+ {
+ if (!PlatformUI.getWorkbench().isClosing())
+ {
+ Shell shell = new Shell();
+
+ ISelection selection =
+ PlatformUI.getWorkbench().getActiveWorkbenchWindow().getSelectionService()
+ .getSelection();
+ IStructuredSelection structuredSelection;
+
+ if (selection instanceof IStructuredSelection)
+ {
+ structuredSelection = (IStructuredSelection) selection;
+ }
+ else
+ {
+ structuredSelection = new StructuredSelection();
+ }
+
+ wizard.init(PlatformUI.getWorkbench(), structuredSelection);
+ WizardDialog dialog = new WizardDialog(shell, wizard);
+
+ dialog.setPageSize(WIZARD_WIDTH, SWT.DEFAULT);
+ shell.pack();
+ centralizeShell(shell);
+
+ dialog.open();
+ }
+ }
+
+ /*
+ * Centralizes a shell on the display.
+ *
+ * @param shell The shell to be centralized.
+ */
+ private void centralizeShell(Shell shell)
+ {
+ int displayWidth = shell.getDisplay().getClientArea().width;
+ int displayHeight = shell.getDisplay().getClientArea().height;
+
+ int x = (displayWidth - shell.getSize().x) / 2;
+ int y = (displayHeight - shell.getSize().y) / 2;
+
+ shell.setLocation(x, y);
+ }
+}
diff --git a/src/plugins/android.codeutils/src/com/motorola/studio/android/db/deployment/ContentProviderDeployer.java b/src/plugins/android.codeutils/src/com/motorola/studio/android/db/deployment/ContentProviderDeployer.java
new file mode 100644
index 0000000..0d5b8ff
--- /dev/null
+++ b/src/plugins/android.codeutils/src/com/motorola/studio/android/db/deployment/ContentProviderDeployer.java
@@ -0,0 +1,353 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.motorola.studio.android.db.deployment;
+
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.List;
+import java.util.Map;
+
+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.FileLocator;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.osgi.util.NLS;
+
+import com.motorola.studio.android.codeutils.db.utils.DatabaseUtils;
+import com.motorola.studio.android.codeutils.i18n.CodeUtilsNLS;
+import com.motorola.studio.android.common.IAndroidConstants;
+import com.motorola.studio.android.common.exception.AndroidException;
+import com.motorola.studio.android.common.utilities.FileUtil;
+import com.motorola.studio.android.manifest.AndroidProjectManifestFile;
+import com.motorola.studio.android.model.manifest.AndroidManifestFile;
+import com.motorola.studio.android.model.manifest.dom.ApplicationNode;
+import com.motorola.studio.android.model.manifest.dom.ManifestNode;
+import com.motorola.studio.android.model.manifest.dom.ProviderNode;
+
+/**
+ * Creates Content Provider that have methods to copy .db file.
+ */
+public class ContentProviderDeployer
+{
+
+ /*
+ * Plug-in Identifier
+ */
+ private static final String PLUGIN_IDENTIFIER = "com.motorola.studio.android.codeutils";
+
+ /**
+ * Constant representing the location of the DataaseHelper class.
+ */
+ public static final String CONTENT_PROVIDER_HELPER_CLASS_LOCATION =
+ "resources/databaseDeploy/ContentProviderHelperjava.txt"; //$NON-NLS-1$
+
+ /**
+ * This constant represents the value of newly-create Application Package Name for the Content Provider.
+ */
+ public static final String APPLICATION_PACKAGE_NAMESPACE = "#applicationPackageNamespace#";
+
+ /**
+ * Directory of the ContentProvider Helper in the android project.
+ */
+ public static final String ANDROID_PROJECT_PACKAGE_NAME = "#packageName#"; //$NON-NLS-1$
+
+ /**
+ * File name of the ContentProvider Helper in the android project.
+ */
+ public static final String CONTENT_PROVIDER_CLASS_NAME = "#className#"; //$NON-NLS-1$
+
+ public static final String CONTENT_PROVIDER_AUTHORITY = "#authority#"; //$NON-NLS-1$
+
+ /**
+ * Copy the ContentProvider Helper deploy file to the newly-created-android project.
+ *
+ * @param project Project where the files will be copied to
+ * @param parameters Copy parameters
+ * @param templateLocation The template (origin) location
+ * @param needToAddOnManifest <code>true</code> in case the manifest is to be updated, false otherwise
+ * @param overrideConentProviderIfExists <code>true</code> in case the Content Providers must be overridden
+ * in case they exist
+ * @param monitor UI monitor
+ *
+ * @throws IOException Exception thrown in case there are problems accessing data of a file.
+ * @throws CoreException Exception thrown in case there are problems accessing a file itself.
+ */
+ public static void copyContentProviderHelperClassToProject(IProject project,
+ Map<String, String> parameters, String templateLocation, boolean needToAddOnManifest,
+ boolean overrideConentProviderIfExists, IProgressMonitor monitor) throws IOException,
+ CoreException
+ {
+ // get the destination folder
+ IFolder destinationFolder =
+ project.getFolder(IAndroidConstants.WS_ROOT + IAndroidConstants.FD_SOURCES);
+ // split the new sequence of folders - they will be created, one by one
+ String[] folders =
+ parameters.get(ANDROID_PROJECT_PACKAGE_NAME).split(IAndroidConstants.RE_DOT);
+ // iterate through the folders and create them
+ if ((folders != null) && (folders.length > 0))
+ {
+ // iterate
+ for (String folder : folders)
+ {
+ // get destination folder with the next one to be created
+ destinationFolder = destinationFolder.getFolder(folder);
+
+ // proceed in case the destination folder does not exists and can be created
+ if (!destinationFolder.exists())
+ {
+ // create the folder
+ destinationFolder.create(true, true, monitor);
+ }
+ }
+ }
+
+ // get the destination file
+ IFile destinationFile =
+ destinationFolder.getFile(parameters.get(CONTENT_PROVIDER_CLASS_NAME)
+ + IAndroidConstants.DOT_JAVA);
+ //refresh to avoid inconsistency
+ destinationFile.getParent().refreshLocal(IResource.DEPTH_INFINITE, monitor);
+ // proceed in case it does not exist and can be created
+ if ((!destinationFile.exists() || overrideConentProviderIfExists)
+ && FileUtil.canWrite(destinationFile))
+ {
+ // get the Database Helper class as a text
+ String databaseHelperText =
+ readContentProviderHelperClassFile(project, parameters, templateLocation);
+
+ // transform it in a stream
+ InputStream stream = null;
+
+ try
+ {
+ stream = new ByteArrayInputStream(databaseHelperText.getBytes("UTF-8")); //$NON-NLS-1$
+ // if the file exists, delete it
+ if (destinationFile.exists())
+ {
+ destinationFile.delete(true, monitor);
+ }
+ // create the destination file
+ destinationFile.create(stream, true, monitor);
+
+ DatabaseUtils.formatCode(destinationFile, databaseHelperText, monitor);
+ }
+ finally
+ {
+ if (stream != null)
+ {
+ stream.close();
+ }
+ }
+ }
+ if (needToAddOnManifest)
+ {
+ try
+ {
+ String className = parameters.get(CONTENT_PROVIDER_CLASS_NAME);
+ String packageName = parameters.get(ANDROID_PROJECT_PACKAGE_NAME);
+ createProviderOnManifest(project, className, packageName, monitor);
+ }
+ catch (AndroidException e)
+ {
+ throw new IOException(e.getMessage());
+ }
+ }
+ }
+
+ /**
+ * Read the ContentProvider Helper class file, replace the parameter maps
+ * and return it as a String.
+ *
+ * @param project Project where the ContentProviderHelper is retrieved.
+ * @param parameters Parameters for replacement in the ContentProviderHelper class file.
+ *
+ * @return a String representing the ContentProviderHelper class file.
+ *
+ * @throws IOException Exception thrown when there are problems reading elements from the
+ * ContentProviderHelper class file.
+ */
+ private static String readContentProviderHelperClassFile(IProject project,
+ Map<String, String> parameters, String templateLocation) throws IOException
+ {
+ URL url = Platform.getBundle(PLUGIN_IDENTIFIER).getEntry(templateLocation);
+ url = FileLocator.toFileURL(url);
+
+ // string buffer which holds the file as a text
+ StringBuffer contentProviderHelperBuffer = new StringBuffer("");
+ // reader
+ BufferedReader contentProviderHelperReader = null;
+
+ // get the file to be read
+ File f = null;
+ try
+ {
+ f = new File(url.getFile());
+ // create the reader to manipulate the file
+ contentProviderHelperReader = new BufferedReader(new FileReader(f));
+
+ // read the Database Helper class file in steps (using buffer)
+ String buffer = null;
+
+ // iterate while there is stuff to read
+ while ((buffer = contentProviderHelperReader.readLine()) != null)
+ {
+ // read and put the content in the string buffer element
+ contentProviderHelperBuffer.append(buffer);
+ contentProviderHelperBuffer.append(System.getProperty("line.separator"));
+ }
+ }
+ finally
+ {
+ // close reader
+ if (contentProviderHelperReader != null)
+ {
+ contentProviderHelperReader.close();
+ }
+ }
+
+ // string holding the "text" of the ContentProvider class
+ String contentProviderHelperText = "";
+ // proceed in case there is stuff in the buffer to read
+ if (contentProviderHelperBuffer != null)
+ {
+ contentProviderHelperText = contentProviderHelperBuffer.toString();
+
+ // iterate through the parameters and replace the parameters in the map
+ if (parameters != null)
+ {
+ for (String key : parameters.keySet())
+ {
+ // replace all the keys
+ contentProviderHelperText =
+ contentProviderHelperText.replaceAll(key, parameters.get(key));
+ }
+ }
+ }
+ // return the file as a text
+ return contentProviderHelperText;
+ }
+
+ /**
+ * Creates the Content Provider class entry on AndroidManifest.xml file
+ *
+ * @param monitor the progress monitor
+ *
+ * @return true if the entry has been added or false otherwise
+ * @throws AndroidException
+ */
+ private static boolean createProviderOnManifest(IProject project, String className,
+ String packageName, IProgressMonitor monitor) throws AndroidException
+ {
+ boolean created = false;
+
+ try
+ {
+ int manifestUpdatingSteps = 4;
+ int totalWork = manifestUpdatingSteps;
+
+ monitor.beginTask("", totalWork);
+
+ monitor.subTask(CodeUtilsNLS.UI_Common_UpdatingTheAndroidManifestXMLFile);
+
+ AndroidManifestFile androidManifestFile =
+ AndroidProjectManifestFile.getFromProject(project);
+
+ monitor.worked(1);
+
+ ManifestNode manifestNode =
+ androidManifestFile != null ? androidManifestFile.getManifestNode() : null;
+ ApplicationNode applicationNode =
+ manifestNode != null ? manifestNode.getApplicationNode() : null;
+
+ monitor.worked(1);
+
+ if (applicationNode != null)
+ {
+ ProviderNode providerNode =
+ new ProviderNode(className, packageName + "." + className.toLowerCase());
+
+ String authority = packageName + "." + className.toLowerCase();
+ providerNode.addAuthority(authority);
+ String name = packageName + "." + className;
+ providerNode.setName(name);
+
+ // get all provider nodes
+ List<ProviderNode> providerNodes = applicationNode.getProviderNodes();
+ // see whether the node to be inserted is already in the list - in case it is not, add it, otherwise do nothing
+ boolean hasProviderNode = false;
+ // verify in case the list of nodes is not empty
+ if ((providerNodes != null) && (providerNodes.size() > 0))
+ {
+ // iterate through the list
+ for (ProviderNode retrievedProvidedNode : providerNodes)
+ {
+ // match the nodes
+ if (retrievedProvidedNode.getName().equals(providerNode.getName()))
+ {
+ // in case a match is found, set the flag and leave the loop
+ hasProviderNode = true;
+ break;
+ }
+ }
+ }
+ // in case there is not this provider node, add it
+ if (!hasProviderNode)
+ {
+ applicationNode.addProviderNode(providerNode);
+ }
+
+ monitor.worked(1);
+
+ monitor.subTask(CodeUtilsNLS.UI_Common_SavingTheAndroidManifestXMLFile);
+
+ AndroidProjectManifestFile.saveToProject(project, androidManifestFile, true);
+ created = true;
+
+ monitor.worked(1);
+ }
+ }
+ catch (AndroidException e)
+ {
+ String errMsg =
+ NLS.bind(CodeUtilsNLS.EXC_ContentProvider_CannotUpdateTheManifestFile,
+ className, e.getLocalizedMessage());
+ throw new AndroidException(errMsg);
+ }
+ catch (CoreException e)
+ {
+ String errMsg =
+ NLS.bind(CodeUtilsNLS.EXC_ContentProvider_CannotUpdateTheManifestFile,
+ className, e.getLocalizedMessage());
+ throw new AndroidException(errMsg);
+ }
+ finally
+ {
+ monitor.done();
+ }
+
+ return created;
+ }
+}
diff --git a/src/plugins/android.codeutils/src/com/motorola/studio/android/db/deployment/ContentProviderDeployerByTable.java b/src/plugins/android.codeutils/src/com/motorola/studio/android/db/deployment/ContentProviderDeployerByTable.java
new file mode 100644
index 0000000..6eb3328
--- /dev/null
+++ b/src/plugins/android.codeutils/src/com/motorola/studio/android/db/deployment/ContentProviderDeployerByTable.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.db.deployment;
+
+/**
+ * Creates Content Provider that have methods to query, insert, update, delete items.
+ */
+public class ContentProviderDeployerByTable extends ContentProviderDeployer
+{
+
+ /**
+ * Constant representing the location of the DataaseHelper class.
+ */
+ public static final String CONTENT_PROVIDER_BY_TABLE_CLASS_LOCATION =
+ "resources/databaseDeploy/ContentProviderByTablejava.txt"; //$NON-NLS-1$
+}
diff --git a/src/plugins/android.codeutils/src/com/motorola/studio/android/db/deployment/DatabaseDeployer.java b/src/plugins/android.codeutils/src/com/motorola/studio/android/db/deployment/DatabaseDeployer.java
new file mode 100644
index 0000000..0c95ca8
--- /dev/null
+++ b/src/plugins/android.codeutils/src/com/motorola/studio/android/db/deployment/DatabaseDeployer.java
@@ -0,0 +1,222 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.motorola.studio.android.db.deployment;
+
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.Map;
+
+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.FileLocator;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.Platform;
+
+import com.motorola.studio.android.codeutils.db.utils.DatabaseUtils;
+import com.motorola.studio.android.common.IAndroidConstants;
+import com.motorola.studio.android.common.utilities.FileUtil;
+
+/**
+ * Handles database deployment in devices and plug-in projects.
+ */
+public class DatabaseDeployer
+{
+
+ /**
+ * Plug-in Identifier
+ */
+ private static final String PLUGIN_IDENTIFIER = "com.motorola.studio.android.codeutils";
+
+ /**
+ * Constant representing the location of the DatabaseHelper class.
+ */
+ private static final String DATABASE_HELPER_CLASS_LOCATION =
+ "resources/databaseDeploy/DatabaseHelperjava.txt"; //$NON-NLS-1$
+
+ /**
+ * This constant represents the value of the to-be-copied Database Name
+ */
+ public static final String DATABASE_NAME = "#dbName#";
+
+ /**
+ * This constant represents the value of newly-created Android Application Package Name for the Database
+ */
+ public static final String APPLICATION_DATABASE_NAMESPACE = "#applicationPackageNamespace#";
+
+ /**
+ * Directory of the Database Helper in the android project
+ */
+ public static final String ANDROID_PROJECT_PACKAGE_NAME = "#packageName#"; //$NON-NLS-1$
+
+ /**
+ * Open Helper Package Name
+ */
+ public static final String OPEN_HELPER_PACKAGE_NAME = "#databaseOpenHelperPackageName#"; //$NON-NLS-1$
+
+ /**
+ * File name of the Database Helper in the android project
+ */
+ public static final String DATABASE_HELPER_CLASS_NAME = "#className#"; //$NON-NLS-1$
+
+ /**
+ * Copy the DatabaseHelper deploy file to the newly-created-android-plu-in.
+ *
+ * @param project Newly-created-android Project
+ * @param parameters Parameters to replace in the DatabaseHelper class file
+ * @param monitor Monitor
+ *
+ * @throws IOException Exception thrown in case there are problems accessing data of a file.
+ * @throws CoreException Exception thrown in case there are problems accessing a file itself.
+ */
+ public static void copyDataBaseDeployerClassToProject(IProject project,
+ Map<String, String> parameters, IProgressMonitor monitor) throws IOException,
+ CoreException
+ {
+ // get the destination folder
+ IFolder destinationFolder =
+ project.getFolder(IAndroidConstants.WS_ROOT + IAndroidConstants.FD_SOURCES);
+ // split the new sequence of folders - they will be created, one by one
+ String[] folders =
+ parameters.get(ANDROID_PROJECT_PACKAGE_NAME).split(IAndroidConstants.RE_DOT);
+ // iterate through the folders and create them
+ if ((folders != null) && (folders.length > 0))
+ {
+ // iterate
+ for (String folder : folders)
+ {
+ // get destination folder with the next one to be created
+ destinationFolder = destinationFolder.getFolder(folder);
+
+ // proceed in case the destination folder does not exists and can be created
+ if (!destinationFolder.exists())
+ {
+ // create the folder
+ destinationFolder.create(true, true, monitor);
+ }
+ }
+ }
+
+ // get the destination file
+ IFile destinationFile =
+ destinationFolder.getFile(parameters.get(DATABASE_HELPER_CLASS_NAME)
+ + IAndroidConstants.DOT_JAVA);
+ // proceed in case it does not exist and can be created
+ if (!destinationFile.exists() && FileUtil.canWrite(destinationFile))
+ {
+ // get the Database Helper class as a text
+ String databaseHelperText = readDatabaseHelperClassFile(project, parameters);
+
+ // transform it in a stream
+ InputStream stream = null;
+
+ try
+ {
+ stream = new ByteArrayInputStream(databaseHelperText.getBytes("UTF-8")); //$NON-NLS-1$
+ // create the destination folder
+ destinationFile.create(stream, true, monitor);
+
+ DatabaseUtils.formatCode(destinationFile, databaseHelperText, monitor);
+ }
+ finally
+ {
+ if (stream != null)
+ {
+ stream.close();
+ }
+ }
+ }
+ }
+
+ /**
+ * Read the DatabaseHelper class file, replace the parameter maps
+ * and return it as a String.
+ *
+ * @param project Project where the DatabaseHelper is retrieved.
+ * @param parameters Parameters for replacement in the DatabaseHelper class file.
+ *
+ * @return a String representing the DatabaseHelper class file.
+ *
+ * @throws IOException Exception thrown when there are problems reading elements from the
+ * DatabaseHelper class file.
+ */
+ private static String readDatabaseHelperClassFile(IProject project,
+ Map<String, String> parameters) throws IOException
+ {
+ URL url = Platform.getBundle(PLUGIN_IDENTIFIER).getEntry(DATABASE_HELPER_CLASS_LOCATION);
+ url = FileLocator.toFileURL(url);
+
+ // string buffer which holds the file as a text
+ StringBuffer databaseHelperBuffer = new StringBuffer("");
+ // reader
+ BufferedReader databaseHelperReader = null;
+
+ // get the file to be read
+ File f = null;
+ try
+ {
+ f = new File(url.getFile());
+ // create the reader to manipulate the file
+ databaseHelperReader = new BufferedReader(new FileReader(f));
+
+ // read the Database Helper class file in steps (using buffer)
+ String buffer = null;
+
+ // iterate while there is stuff to read
+ while ((buffer = databaseHelperReader.readLine()) != null)
+ {
+ // read and put the content in the string buffer element
+ databaseHelperBuffer.append(buffer);
+ databaseHelperBuffer.append(System.getProperty("line.separator"));
+ }
+
+ }
+ finally
+ {
+ // close reader
+ if (databaseHelperReader != null)
+ {
+ databaseHelperReader.close();
+ }
+ }
+
+ // string holding the "text" of the DatabaseHelper class
+ String databaseHelperText = "";
+ // proceed in case there is stuff in the buffer to read
+ if (databaseHelperBuffer != null)
+ {
+ databaseHelperText = databaseHelperBuffer.toString();
+
+ // iterate through the parameters and replace the parameters in the map
+ if (parameters != null)
+ {
+ for (String key : parameters.keySet())
+ {
+ databaseHelperText = databaseHelperText.replaceAll(key, parameters.get(key));
+ }
+ }
+ }
+ // return the file as a text
+ return databaseHelperText;
+ }
+}
diff --git a/src/plugins/android.codeutils/src/com/motorola/studio/android/db/wizards/model/Field.java b/src/plugins/android.codeutils/src/com/motorola/studio/android/db/wizards/model/Field.java
new file mode 100644
index 0000000..b70ca26
--- /dev/null
+++ b/src/plugins/android.codeutils/src/com/motorola/studio/android/db/wizards/model/Field.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.motorola.studio.android.db.wizards.model;
+
+import com.motorola.studio.android.codeutils.i18n.CodeUtilsNLS;
+
+/**
+ * This class abstracts a field in a database table.
+ * */
+public class Field
+{
+ /**
+ * A primary key may be set to be updated incrementally, decrementally or none.
+ * */
+ public enum PrimaryKeyBehaviour
+ {
+ NONE, INCREMENTAL, DECREMENTAL
+ };
+
+ private String type;
+
+ private String name;
+
+ private String defaultValue;
+
+ private boolean isPrimary;
+
+ private PrimaryKeyBehaviour primaryKeyBehaviour;
+
+ /**
+ * @return The behavior of the primary key as described in {@link PrimaryKeyBehaviour}.
+ * */
+ public PrimaryKeyBehaviour getPrimaryKeyBehaviour()
+ {
+ return primaryKeyBehaviour;
+ }
+
+ /**
+ * Set the primary key behavior as described in {@link PrimaryKeyBehaviour}.
+ * */
+ public void setPrimaryKeyBehaviour(PrimaryKeyBehaviour primaryKeyBehaviour)
+ {
+ this.primaryKeyBehaviour = primaryKeyBehaviour;
+ }
+
+ /**
+ * Constructor that initializes the field name.
+ * */
+ public Field(String name)
+ {
+ this.name = name;
+ }
+
+ /**
+ * @return field type.
+ * */
+ public String getType()
+ {
+ return type;
+ }
+
+ public void setType(String type)
+ {
+ this.type = type;
+ }
+
+ /**
+ * @return field name.
+ * */
+ public String getName()
+ {
+ return name;
+ }
+
+ public void setName(String name)
+ {
+ this.name = name;
+ }
+
+ /**
+ * @return The default value used to fill in the field when it has no value in insert statements.
+ * */
+ public String getDefaultValue()
+ {
+ return defaultValue;
+ }
+
+ public void setDefaultValue(String defaultValue)
+ {
+ this.defaultValue = defaultValue;
+ }
+
+ /**
+ * @return True if this field belongs to the primary key set. Otherwise, returns false.
+ * */
+ public boolean isPrimary()
+ {
+ return isPrimary;
+ }
+
+ public void setPrimary(boolean isPrimary)
+ {
+ this.isPrimary = isPrimary;
+ }
+
+ /**
+ * @return
+ * <ul>
+ * <li>empty string, if {@link PrimaryKeyBehaviour} is set to {@link PrimaryKeyBehaviour#NONE}.</li>
+ * <li>ASC, if {@link PrimaryKeyBehaviour} is set to {@link PrimaryKeyBehaviour#INCREMENTAL}.</li>
+ * <li>DESC, if {@link PrimaryKeyBehaviour} is set to {@link PrimaryKeyBehaviour#DECREMENTAL}.</li>
+ * </ul>
+ * */
+ public String getKeyBehaviourQuery()
+ {
+ String[] query =
+ {
+ "", " ASC", " DESC" //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ };
+
+ return query[getPrimaryKeyBehaviour().ordinal()];
+
+ }
+
+ /**
+ * @return An string describing field property error and {@code null} if there are no errors.
+ * */
+ public String getErrorMessage()
+ {
+ String message = null;
+
+ if (!type.equals("INTEGER") && !type.equals("REAL") //$NON-NLS-1$ //$NON-NLS-2$
+ && !primaryKeyBehaviour.equals(PrimaryKeyBehaviour.NONE))
+ {
+ message = CodeUtilsNLS.Field_ErrorAutoIncrementNotAllowed;
+ }
+
+ // Validate name to don't use sqlite keywords
+ if ((message == null) && !Table.validateName(getName()))
+ {
+
+ message = CodeUtilsNLS.AddTableFieldDialog_InvalidName + getName();
+ }
+
+ return message;
+ }
+
+}
diff --git a/src/plugins/android.codeutils/src/com/motorola/studio/android/db/wizards/model/Table.java b/src/plugins/android.codeutils/src/com/motorola/studio/android/db/wizards/model/Table.java
new file mode 100644
index 0000000..9dcb3e4
--- /dev/null
+++ b/src/plugins/android.codeutils/src/com/motorola/studio/android/db/wizards/model/Table.java
@@ -0,0 +1,225 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.motorola.studio.android.db.wizards.model;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import com.motorola.studio.android.codeutils.i18n.CodeUtilsNLS;
+
+/**
+ * An abstraction of a database table.
+ * */
+public class Table
+{
+ private String tableName = null;
+
+ private final List<Field> fields = new ArrayList<Field>();
+
+ @Override
+ public String toString()
+ {
+ String query = "("; //$NON-NLS-1$
+ String primaryKey = " , PRIMARY KEY ("; //$NON-NLS-1$
+
+ boolean hasPrimary = false;
+
+ Iterator<Field> it = fields.iterator();
+ Field fie;
+ while (it.hasNext())
+ {
+
+ fie = it.next();
+ query +=
+ fie.getName()
+ + " " //$NON-NLS-1$
+ + fie.getType()
+ + " " //$NON-NLS-1$
+ + ((!(fie.getDefaultValue().equals("") || fie.isPrimary())) //$NON-NLS-1$
+ ? " default \'" : "") //$NON-NLS-1$ //$NON-NLS-2$
+ + fie.getDefaultValue()
+ + ((!(fie.getDefaultValue().equals("") || fie.isPrimary())) ? "\' " //$NON-NLS-1$ //$NON-NLS-2$
+ : ""); //$NON-NLS-1$
+ if (fie.isPrimary())
+ {
+ hasPrimary = true;
+ primaryKey += fie.getName() + fie.getKeyBehaviourQuery() + " )"; //$NON-NLS-1$
+
+ }
+ if (it.hasNext())
+
+ {
+ query += " , "; //$NON-NLS-1$
+ }
+
+ }
+
+ query += (hasPrimary ? primaryKey : "") + " )"; //$NON-NLS-1$ //$NON-NLS-2$
+
+ return query;
+
+ }
+
+ public List<Field> getFields()
+ {
+ return fields;
+ }
+
+ public void setFields(List<Field> fields)
+ {
+ this.fields.clear();
+ if (fields != null)
+ {
+ for (Field field : fields)
+ {
+ this.fields.add(field);
+ }
+ }
+ }
+
+ /**
+ * Add {@code field} to the table.
+ * */
+ public void addField(Field field)
+ {
+
+ fields.add(field);
+
+ }
+
+ /**
+ * Remove {@code field} from the table.
+ * */
+ public void removeField(Field obj)
+ {
+ fields.remove(obj);
+
+ }
+
+ /**
+ * @return If this table has some error in its fields, like unnamed columns, a string describing the error is returned.
+ * */
+ public String getErrorMessage()
+ {
+
+ Iterator<Field> it = fields.iterator();
+ String msg = null;
+
+ while (it.hasNext() && (msg == null))
+ {
+ Field field = it.next();
+
+ msg = field.getErrorMessage();
+
+ if ((msg == null) && field.getName().trim().contains(" ")) //$NON-NLS-1$
+ {
+ msg = CodeUtilsNLS.Table_ErrorUnamedColumns;
+ }
+ else if (msg == null)
+ {
+
+ Iterator<Field> iterator = fields.iterator();
+
+ while (iterator.hasNext() && (msg == null))
+ {
+ Field testField = iterator.next();
+ if (field != testField)
+ {
+ if (field.getName().equalsIgnoreCase(testField.getName()))
+ {
+ msg = CodeUtilsNLS.Table_ErrorConflictingNames + field.getName() + ", " //$NON-NLS-2$
+ + testField.getName();
+ }
+ else if (field.isPrimary() && testField.isPrimary())
+ {
+ msg =
+ CodeUtilsNLS.Table_ErrorMoreThanOnePrimaryKey + field.getName()
+ + ", " + testField.getName(); //$NON-NLS-1$
+ }
+
+ }
+
+ }
+ }
+ }
+ return msg;
+ }
+
+ /**
+ * Validates the name to be different from SQLite keywords.
+ * Reference: http://www.sqlite.org/lang_keywords.html
+ *
+ * @param name
+ * The table name
+ * @return True is a valid table name, false otherwise
+ */
+ public static boolean validateName(String name)
+ {
+
+ boolean isValid = true;
+ String[] keywords =
+ {
+ "ABORT", "ACTION", "ADD", "AFTER", "ALL", "ALTER", "ANALYZE", "AND", "AS",
+ "ASC", "ATTACH", "AUTOINCREMENT", "BEFORE", "BEGIN", "BETWEEN", "BY",
+ "CASCADE", "CASE", "CAST", "CHECK", "COLLATE", "COLUMN", "COMMIT",
+ "CONFLICT", "CONSTRAINT", "CREATE", "CROSS", "CURRENT_DATE",
+ "CURRENT_TIME", "CURRENT_TIMESTAMP", "DATABASE", "DEFAULT", "DEFERRABLE",
+ "DEFERRED", "DELETE", "DESC", "DETACH", "DISTINCT", "DROP", "EACH", "ELSE",
+ "END", "ESCAPE", "EXCEPT", "EXCLUSIVE", "EXISTS", "EXPLAIN", "FAIL", "FOR",
+ "FOREIGN", "FROM", "FULL", "GLOB", "GROUP", "HAVING", "IF", "IGNORE",
+ "IMMEDIATE", "IN", "INDEX", "INDEXED", "INITIALLY", "INNER", "INSERT",
+ "INSTEAD", "INTERSECT", "INTO", "IS", "ISNULL", "JOIN", "KEY", "LEFT",
+ "LIKE", "LIMIT", "MATCH", "NATURAL", "NO", "NOT", "NOTNULL", "NULL", "OF",
+ "OFFSET", "ON", "OR", "ORDER", "OUTER", "PLAN", "PRAGMA", "PRIMARY",
+ "QUERY", "RAISE", "REFERENCES", "REGEXP", "REINDEX", "RELEASE", "RENAME",
+ "REPLACE", "RESTRICT", "RIGHT", "ROLLBACK", "ROW", "SAVEPOINT", "SELECT",
+ "SET", "TABLE", "TEMP", "TEMPORARY", "THEN", "TO", "TRANSACTION",
+ "TRIGGER", "UNION", "UNIQUE", "UPDATE", "USING", "VACUUM", "VALUES",
+ "VIEW", "VIRTUAL", "WHEN", "WHERE"
+ };
+
+ for (String keyword : keywords)
+ {
+
+ // Found. It is a keyword
+ if (keyword.toLowerCase().compareTo(name.toLowerCase()) == 0)
+ {
+
+ isValid = false;
+ }
+ }
+
+ return isValid;
+ }
+
+ /**
+ * @return the tableName
+ */
+ public String getTableName()
+ {
+ return tableName;
+ }
+
+ /**
+ * @param tableName the tableName to set
+ */
+ public void setTableName(String tableName)
+ {
+ this.tableName = tableName == null ? null : tableName.trim();
+ }
+}
diff --git a/src/plugins/android.codeutils/src/com/motorola/studio/android/generatecode/AbstractCodeGenerator.java b/src/plugins/android.codeutils/src/com/motorola/studio/android/generatecode/AbstractCodeGenerator.java
new file mode 100644
index 0000000..2407bfe
--- /dev/null
+++ b/src/plugins/android.codeutils/src/com/motorola/studio/android/generatecode/AbstractCodeGenerator.java
@@ -0,0 +1,632 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.generatecode;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.jdt.core.JavaModelException;
+import org.eclipse.jdt.core.dom.ASTNode;
+import org.eclipse.jdt.core.dom.Block;
+import org.eclipse.jdt.core.dom.Expression;
+import org.eclipse.jdt.core.dom.ExpressionStatement;
+import org.eclipse.jdt.core.dom.IMethodBinding;
+import org.eclipse.jdt.core.dom.IfStatement;
+import org.eclipse.jdt.core.dom.InfixExpression;
+import org.eclipse.jdt.core.dom.MethodDeclaration;
+import org.eclipse.jdt.core.dom.MethodInvocation;
+import org.eclipse.jdt.core.dom.Modifier;
+import org.eclipse.jdt.core.dom.Modifier.ModifierKeyword;
+import org.eclipse.jdt.core.dom.ParameterizedType;
+import org.eclipse.jdt.core.dom.PrimitiveType;
+import org.eclipse.jdt.core.dom.PrimitiveType.Code;
+import org.eclipse.jdt.core.dom.QualifiedName;
+import org.eclipse.jdt.core.dom.ReturnStatement;
+import org.eclipse.jdt.core.dom.SimpleName;
+import org.eclipse.jdt.core.dom.SimpleType;
+import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
+import org.eclipse.jdt.core.dom.Statement;
+import org.eclipse.jdt.core.dom.SuperMethodInvocation;
+import org.eclipse.jdt.core.dom.TypeDeclaration;
+import org.eclipse.jdt.core.dom.WildcardType;
+
+import com.motorola.studio.android.generateviewbylayout.JavaViewBasedOnLayoutModifierConstants;
+import com.motorola.studio.android.generateviewbylayout.model.LayoutNode;
+
+/**
+ * Abstract code generator class that has common methods to generate code (for Menu, GUI handlers, findViewById, attributes).
+ */
+public abstract class AbstractCodeGenerator
+{
+ protected static final String THIZ = "this.";
+
+ protected TypeDeclaration typeDeclaration;
+
+ /**
+ * Default constructor
+ * @param typeDeclaration AST for the type to modify
+ */
+ public AbstractCodeGenerator(TypeDeclaration typeDeclaration)
+ {
+ this.typeDeclaration = typeDeclaration;
+ }
+
+ /**
+ * Generates code by changing AST (Abstract Syntax tree) for a given Android class
+ * @param monitor concrete implementation should use <code>SubMonitor.convert(monitor)</code> to display status messages during code generation
+ * @throws JavaModelException if any error occurs to create new/modified AST to be written
+ */
+ public abstract void generateCode(IProgressMonitor monitor) throws JavaModelException;
+
+ /**
+ * Constructs a new method declaration on type declaration (for a single parameter).
+ * Warning: Calling methods need to fill the body and add the item into typeDeclaration
+ * @param node
+ */
+ protected MethodDeclaration addMethodDeclaration(ModifierKeyword modifierKeyword,
+ String methodNameStr, Code returnType, String parameterClazzType,
+ String parameterVariableName)
+ {
+ SingleVariableDeclaration singleVarDecl =
+ createVariableDeclarationFromStrings(parameterClazzType, parameterVariableName);
+ List<SingleVariableDeclaration> parameters = new ArrayList<SingleVariableDeclaration>();
+ parameters.add(singleVarDecl);
+ MethodDeclaration methodDeclaration =
+ addMethodDeclaration(modifierKeyword, methodNameStr, returnType, parameters);
+ return methodDeclaration;
+ }
+
+ /**
+ * Constructs a new method declaration on type declaration (for multiple parameters).
+ * Warning: Calling methods need to fill the body and add the item into typeDeclaration
+ * @param node
+ */
+ @SuppressWarnings("unchecked")
+ protected MethodDeclaration addMethodDeclaration(ModifierKeyword modifierKeyword,
+ String methodNameStr, Code returnType, List<SingleVariableDeclaration> parameters)
+ {
+ MethodDeclaration methodDeclaration = typeDeclaration.getAST().newMethodDeclaration();
+ Modifier mod = typeDeclaration.getAST().newModifier(modifierKeyword);
+ methodDeclaration.modifiers().add(mod);
+ SimpleName methodName = typeDeclaration.getAST().newSimpleName(methodNameStr);
+ methodDeclaration.setName(methodName);
+ PrimitiveType voidType = typeDeclaration.getAST().newPrimitiveType(returnType);
+ methodDeclaration.setReturnType2(voidType);
+
+ if (parameters != null)
+ {
+ for (SingleVariableDeclaration param : parameters)
+ {
+ methodDeclaration.parameters().add(param);
+ }
+ }
+ return methodDeclaration;
+ }
+
+ /**
+ *
+ * @param parameterClazzType
+ * @param parameterVariableName
+ * @return
+ */
+ protected SingleVariableDeclaration createVariableDeclarationFromStrings(
+ String parameterClazzType, String parameterVariableName)
+ {
+ SingleVariableDeclaration singleVarDecl =
+ typeDeclaration.getAST().newSingleVariableDeclaration();
+ singleVarDecl.setType(typeDeclaration.getAST().newSimpleType(
+ getViewName(parameterClazzType)));
+ SimpleName variableName = getVariableName(parameterVariableName);
+ singleVarDecl.setName(variableName);
+ return singleVarDecl;
+ }
+
+ /**
+ * Creates a variable declaration when it required a List with a parameterized type
+ * @param parameterClazzType
+ * @param typeArgument used to create variables such as List<T>
+ * @param parameterVariableName
+ * @return
+ */
+ @SuppressWarnings("unchecked")
+ protected SingleVariableDeclaration createWildcardTypeVariableDeclarationFromStrings(
+ String parameterClazzType, String parameterVariableName)
+ {
+ SingleVariableDeclaration singleVarDecl =
+ typeDeclaration.getAST().newSingleVariableDeclaration();
+ SimpleType type = typeDeclaration.getAST().newSimpleType(getViewName(parameterClazzType));
+ ParameterizedType paramType = typeDeclaration.getAST().newParameterizedType(type);
+ WildcardType wildcardType = typeDeclaration.getAST().newWildcardType();
+ paramType.typeArguments().add(wildcardType);
+ singleVarDecl.setType(paramType);
+ SimpleName variableName = getVariableName(parameterVariableName);
+ singleVarDecl.setName(variableName);
+ return singleVarDecl;
+ }
+
+ /**
+ * @param parameterClazzType
+ * @param parameterVariableName
+ * @return AST
+ */
+ protected SingleVariableDeclaration createVariableDeclarationPrimitiveCode(Code code,
+ String parameterVariableName)
+ {
+ SingleVariableDeclaration singleVarDecl =
+ typeDeclaration.getAST().newSingleVariableDeclaration();
+ singleVarDecl.setType(typeDeclaration.getAST().newPrimitiveType(code));
+ SimpleName variableName = getVariableName(parameterVariableName);
+ singleVarDecl.setName(variableName);
+ return singleVarDecl;
+ }
+
+ /**
+ * @return AST representation for the name
+ */
+ protected SimpleName getVariableName(String name)
+ {
+ SimpleName variableName = typeDeclaration.getAST().newSimpleName(name);
+ return variableName;
+ }
+
+ /**
+ * @return AST for simple type
+ */
+ protected SimpleType getListenerSimpleType(String clazzType, String methodListenerMethodName)
+ {
+ SimpleName listenerName = typeDeclaration.getAST().newSimpleName(methodListenerMethodName);
+ SimpleName viewName = getViewName(clazzType);
+ QualifiedName listenerQualifiedName =
+ typeDeclaration.getAST().newQualifiedName(viewName, listenerName);
+ SimpleType listenerType = typeDeclaration.getAST().newSimpleType(listenerQualifiedName);
+ return listenerType;
+ }
+
+ /**
+ * @return AST Simple Name for the clazz type
+ */
+ protected SimpleName getViewName(String clazzType)
+ {
+ SimpleName viewName = typeDeclaration.getAST().newSimpleName(clazzType);
+ return viewName;
+ }
+
+ /**
+ * Returns the index of the inflate invocation
+ * @param index
+ * @param expression
+ */
+ protected int findInflateIndexAtStatement(int index, Expression expression)
+ {
+ int foundIndex = -1;
+ if (expression instanceof MethodInvocation)
+ {
+ MethodInvocation inflateInvocation = (MethodInvocation) expression;
+ if ((inflateInvocation.getName() != null)
+ && inflateInvocation.getName().getIdentifier().equals("inflate"))
+ {
+ foundIndex = index;
+ }
+ }
+ return foundIndex;
+ }
+
+ /**
+ * Checks if onClick is already declared in Java Activity (based on layout XML declaration)
+ * @param node
+ * @return true if declared, false otherwise
+ */
+ protected boolean onClickFromXmlAlreadyDeclared(LayoutNode node)
+ {
+ boolean containMethodDeclared = false;
+ if (typeDeclaration.bodyDeclarations() != null)
+ {
+ //check if method already declared
+ for (Object bd : typeDeclaration.bodyDeclarations())
+ {
+ if (bd instanceof MethodDeclaration)
+ {
+ MethodDeclaration md = (MethodDeclaration) bd;
+ if ((md.getName() != null) && md.getName().toString().equals(node.getOnClick()))
+ {
+ containMethodDeclared = true;
+ break;
+ }
+ }
+ }
+ }
+ return containMethodDeclared;
+ }
+
+ /**
+ * Checks if a method is already declared
+ * @param methodToCheck method to verify if already existent in the code
+ * @param bindingString
+ * @return null if there method not declared yet, or the the method found (if already declared)
+ */
+ protected MethodDeclaration isMethodAlreadyDeclared(MethodDeclaration methodToCheck,
+ String bindingString)
+ {
+ MethodDeclaration result = null;
+ if (typeDeclaration.bodyDeclarations() != null)
+ {
+ //check if method already declared
+ for (Object bd : typeDeclaration.bodyDeclarations())
+ {
+ if (bd instanceof MethodDeclaration)
+ {
+ MethodDeclaration md = (MethodDeclaration) bd;
+ IMethodBinding binding = md.resolveBinding();
+ if ((binding != null) && (bindingString != null)
+ && binding.toString().trim().equals(bindingString.trim()))
+ {
+ result = md;
+ break;
+ }
+ }
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Adds a statement into methodDeclaration if the statement is not already declared.
+ * If the last statement is a {@link ReturnStatement}, it inserts before it, otherwise it inserts as the last statement in the block
+ * @param methodDeclaration
+ * @param declarationStatement
+ * @param sameClass true, it will compare if there is the same statement class in the body of the methodDeclaration (but the content may be different), false it will ignore the class and it will compare if the content is the same (given by toString.equals())
+ */
+ protected void addStatementIfNotFound(MethodDeclaration methodDeclaration,
+ Statement declarationStatement, boolean sameClass)
+ {
+ addStatementIfNotFound(methodDeclaration.getBody(), declarationStatement, sameClass);
+ }
+
+ /**
+ * Adds a statement into block if the statement is not already declared.
+ * If the last statement is a {@link ReturnStatement}, it inserts before it, otherwise it inserts as the last statement in the block
+ * @param block
+ * @param declarationStatement
+ * @param sameClass true, it will compare if there is the same statement class in the body of the methodDeclaration (but the content may be different), false it will ignore the class and it will compare if the content is the same (given by toString.equals())
+ */
+ @SuppressWarnings("unchecked")
+ protected void addStatementIfNotFound(Block block, Statement declarationStatement,
+ boolean sameClass)
+ {
+ boolean alreadyDeclared = false;
+ List<Statement> statements = block.statements();
+ alreadyDeclared = isStatementAlreadyDeclared(declarationStatement, sameClass, statements);
+ if (!alreadyDeclared)
+ {
+ Statement statement =
+ block.statements().size() > 0 ? (Statement) block.statements().get(
+ block.statements().size() - 1) : null;
+ if ((statement instanceof ReturnStatement)
+ && !(declarationStatement instanceof ReturnStatement))
+ {
+ //need to insert before return
+ block.statements().add(block.statements().size() - 1, declarationStatement);
+ }
+ else
+ {
+ block.statements().add(declarationStatement);
+ }
+ }
+ }
+
+ /**
+ * Checks if the given declarationg statement is already available in the list of statements
+ * @param declarationStatement
+ * @param sameClass true, it will compare if there is the same statement class in the body of the methodDeclaration (but the content may be different), false it will ignore the class and it will compare if the content is the same (given by toString.equals())
+ * @param alreadyDeclared
+ * @param statements
+ * @return true if statement found, false otherwise
+ */
+ protected boolean isStatementAlreadyDeclared(Statement declarationStatement, boolean sameClass,
+ List<Statement> statements)
+ {
+ boolean alreadyDeclared = false;
+ if (statements != null)
+ {
+ for (Statement statement : statements)
+ {
+ if ((!sameClass && declarationStatement.toString().equals(statement.toString()))
+ || (sameClass && statement.getClass().equals(
+ declarationStatement.getClass())))
+ {
+ alreadyDeclared = true;
+ break;
+ }
+ }
+ }
+ return alreadyDeclared;
+ }
+
+ /**
+ * Finds a statement if already declared
+ * @param declarationStatement
+ * @param sameClass true, it will compare if there is the same statement class in the body of the methodDeclaration (but the content may be different), false it will ignore the class and it will compare if the content is the same (given by toString.equals())
+ * @param alreadyDeclared
+ * @param statements
+ * @return null if not found, the reference to the statement if it is found
+ */
+ protected Statement findIfStatementAlreadyDeclared(Statement declarationStatement,
+ boolean sameClass, List<Statement> statements)
+ {
+ Statement foundStatement = null;
+ if (statements != null)
+ {
+ for (Statement statement : statements)
+ {
+ if ((!sameClass && declarationStatement.toString().equals(statement.toString()))
+ || (sameClass && statement.getClass().equals(
+ declarationStatement.getClass())))
+ {
+ foundStatement = statement;
+ break;
+ }
+ }
+ }
+ return foundStatement;
+ }
+
+ /**
+ * Inserts method in the format super.$superMethodName($list_params); and inserts it into the methodDeclaration (if not already available)
+ * @param methodDeclaration
+ * @param superMethodName
+ * @param arguments null if not necessary or a list of arguments to pass for method
+ */
+ @SuppressWarnings("unchecked")
+ public void insertSuperInvocation(MethodDeclaration methodDeclaration, String superMethodName,
+ List<String> arguments)
+ {
+ boolean alreadyHaveMethod = false;
+ if (methodDeclaration.getBody() != null)
+ {
+ //check if method already declared
+ for (Object bd : methodDeclaration.getBody().statements())
+ {
+ if (bd instanceof ExpressionStatement)
+ {
+ ExpressionStatement es = (ExpressionStatement) bd;
+ Expression ex = es.getExpression();
+ if (ex instanceof SuperMethodInvocation)
+ {
+ SuperMethodInvocation smi = (SuperMethodInvocation) ex;
+ if (smi.getName().toString().equals(superMethodName))
+ {
+ alreadyHaveMethod = true;
+ break;
+ }
+ }
+ }
+ }
+ }
+ if (!alreadyHaveMethod)
+ {
+ SuperMethodInvocation superInvoke =
+ createSuperMethodInvocation(superMethodName, arguments);
+ ExpressionStatement exprSt =
+ methodDeclaration.getAST().newExpressionStatement(superInvoke);
+ methodDeclaration.getBody().statements().add(exprSt);
+ }
+ }
+
+ /**
+ * Creates a method in the format super.$superMethodName($list_params);
+ * @param superMethodName
+ * @param arguments null if not necessary or a list of arguments to pass for method
+ * @return
+ */
+ @SuppressWarnings("unchecked")
+ protected SuperMethodInvocation createSuperMethodInvocation(String superMethodName,
+ List<String> arguments)
+ {
+ SuperMethodInvocation superInvoke = typeDeclaration.getAST().newSuperMethodInvocation();
+ SimpleName onSaveStateName = typeDeclaration.getAST().newSimpleName(superMethodName);
+ superInvoke.setName(onSaveStateName);
+ if (arguments != null)
+ {
+ for (String a : arguments)
+ {
+ SimpleName arg = typeDeclaration.getAST().newSimpleName(a);
+ superInvoke.arguments().add(arg);
+ }
+ }
+ return superInvoke;
+ }
+
+ @SuppressWarnings("unchecked")
+ /**
+ * Generates AST to invoke a method with the given structure <code>prefix.methodName(){}</code>.
+ * This code avoids method invocation be duplicated in the {@link MethodDeclaration}.
+ * @param method declared method to insert the invocation
+ * @param prefix
+ * @param methodName
+ */
+ protected void invokeMethod(MethodDeclaration method, String prefix, String methodName)
+ {
+ boolean alreadyHaveMethod = false;
+ if (method.getBody() != null)
+ {
+ //check if method already declared
+ for (Object bd : method.getBody().statements())
+ {
+ if (bd instanceof ExpressionStatement)
+ {
+ ExpressionStatement es = (ExpressionStatement) bd;
+ Expression ex = es.getExpression();
+ if (ex instanceof MethodInvocation)
+ {
+ MethodInvocation mi = (MethodInvocation) ex;
+ if (mi.getName().toString().equals(methodName)
+ && mi.getExpression().toString().equals(prefix))
+ {
+ alreadyHaveMethod = true;
+ break;
+ }
+ }
+ }
+ }
+ }
+ if (!alreadyHaveMethod)
+ {
+ MethodInvocation invoke = createMethodInvocation(prefix, methodName);
+ ExpressionStatement commitExpr = method.getAST().newExpressionStatement(invoke);
+ method.getBody().statements().add(commitExpr);
+ }
+ }
+
+ /**
+ * Create a method invocation in the format
+ * <code>prefix.methodName()</code>
+ * @param prefix null if does not have
+ * @param methodName
+ * @return {@link MethodInvocation}
+ */
+ protected MethodInvocation createMethodInvocation(String prefix, String methodName)
+ {
+ MethodInvocation invoke = typeDeclaration.getAST().newMethodInvocation();
+ SimpleName methodInvokeName = typeDeclaration.getAST().newSimpleName(methodName);
+ invoke.setName(methodInvokeName);
+ if (prefix != null)
+ {
+ SimpleName prefixName = typeDeclaration.getAST().newSimpleName(prefix);
+ invoke.setExpression(prefixName);
+ }
+ return invoke;
+ }
+
+ /**
+ * Recursive private method to verify if an radio button id is in a "else if" chain
+ *
+ * @param ifSt
+ * @param expression
+ * @return
+ */
+ protected boolean ifChainContainsExpression(IfStatement ifSt, Expression expression)
+ {
+
+ boolean containsExpression = false;
+ Statement elseStatement = ifSt.getElseStatement();
+
+ //verifies if the first if's expression already verifies the current radio button.
+ //the characters "(" and ")" are added to avoid that an substring is considered true
+ if (ifSt.getExpression().toString().equals(expression.toString()))
+ {
+ containsExpression = true;
+ }
+ else if ((elseStatement != null) && (elseStatement instanceof IfStatement))
+ {
+ containsExpression = ifChainContainsExpression((IfStatement) elseStatement, expression);
+ }
+
+ return containsExpression;
+ }
+
+ /**
+ * Recursive private method to retrieve the last if in a "else if" chain
+ *
+ * @param ifSt
+ * @return
+ */
+ protected IfStatement getLastIfStatementInChain(IfStatement ifSt)
+ {
+
+ IfStatement lastStatement = null;
+ Statement elseStatement = ifSt.getElseStatement();
+
+ // looks for the if statement which is in a else statement. Will stop when find and if withoud else statement.
+ if ((elseStatement != null) && (elseStatement instanceof IfStatement))
+ {
+ lastStatement = getLastIfStatementInChain((IfStatement) elseStatement);
+ }
+ //lastStatement will receive ifSt because it does not have the else or have an else but it is not and
+ else
+ {
+ lastStatement = ifSt;
+ }
+
+ return lastStatement;
+ }
+
+ /**
+ * Creates a chain og else if and else statement for the given if statement
+ * @param ifSt
+ * @param invocation
+ * @param guiQN
+ */
+ protected void createElseIfAndElseStatements(IfStatement ifSt, MethodInvocation invocation,
+ QualifiedName guiQN)
+ {
+ InfixExpression infixExp = typeDeclaration.getAST().newInfixExpression();
+ infixExp.setOperator(InfixExpression.Operator.EQUALS);
+
+ infixExp.setLeftOperand(invocation);
+ infixExp.setRightOperand(guiQN);
+
+ //first verifies if the expression of the if statement is missing, it means we created it, just need to add the expression.
+ //Otherwise, the "else if" chain must be verified before add the new if statement
+ if (ifSt.getExpression().toString()
+ .equals(JavaViewBasedOnLayoutModifierConstants.EXPRESSION_MISSING))
+ {
+ ifSt.setExpression(infixExp);
+ }
+ else
+ {
+ boolean expressionAlreadyExists = false;
+ //verifies if the first if's expression already verifies the current menu item or radio button
+ if (ifChainContainsExpression(ifSt, infixExp))
+ {
+ expressionAlreadyExists = true;
+ }
+
+ if (!expressionAlreadyExists)
+ {
+ IfStatement lastIfStatement = getLastIfStatementInChain(ifSt);
+ if (lastIfStatement != null)
+ {
+ IfStatement elseSt = typeDeclaration.getAST().newIfStatement();
+ elseSt.setExpression(infixExp);
+ if (lastIfStatement.getElseStatement() != null)
+ {
+ Statement oldElseStatement = lastIfStatement.getElseStatement();
+ elseSt.setElseStatement((Statement) ASTNode.copySubtree(elseSt.getAST(),
+ oldElseStatement));
+ lastIfStatement.setElseStatement(elseSt);
+ }
+ else
+ {
+ lastIfStatement.setElseStatement(elseSt);
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Creates a return statemtn into the method declaration (it only adds the return if it does not exist yet)
+ * @param methodDeclaration to add the return statement
+ */
+ protected void createReturnStatement(MethodDeclaration methodDeclaration)
+ {
+ ReturnStatement returnStatement = typeDeclaration.getAST().newReturnStatement();
+ returnStatement.setExpression(typeDeclaration.getAST().newBooleanLiteral(true));
+ //try to find a ReturnStatement (may be a different return, but the content may differ)
+ addStatementIfNotFound(methodDeclaration, returnStatement, true);
+ }
+}
diff --git a/src/plugins/android.codeutils/src/com/motorola/studio/android/generatecode/AbstractCodeGeneratorData.java b/src/plugins/android.codeutils/src/com/motorola/studio/android/generatecode/AbstractCodeGeneratorData.java
new file mode 100644
index 0000000..9f0861c
--- /dev/null
+++ b/src/plugins/android.codeutils/src/com/motorola/studio/android/generatecode/AbstractCodeGeneratorData.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.generatecode;
+
+import org.eclipse.core.resources.IResource;
+import org.eclipse.jdt.core.ICompilationUnit;
+import org.eclipse.jdt.core.dom.CompilationUnit;
+
+/**
+ * Model representing the code generator data needed to generate code (for layout, for menu, or other new future purpose).
+ */
+public abstract class AbstractCodeGeneratorData
+{
+
+ public enum TYPE
+ {
+ ACTIVITY, FRAGMENT;
+ }
+
+ private TYPE associatedType;
+
+ public TYPE getAssociatedType()
+ {
+ return associatedType;
+ }
+
+ public void setAssociatedType(TYPE associatedType)
+ {
+ this.associatedType = associatedType;
+ }
+
+ /**
+ * @return the java file that will be modified
+ */
+ public abstract IResource getResource();
+
+ /**
+ * Necessary to apply the text modification
+ * @return {@link ICompilationUnit} to be modified
+ */
+ public abstract ICompilationUnit getICompilationUnit();
+
+ /**
+ * Necessary to record AST changes
+ * @return {@link CompilationUnit} to be modified
+ */
+ public abstract CompilationUnit getCompilationUnit();
+
+ /**
+ * Visitor to get data from the java file (e.g.: inflate or setContentView)
+ * return {@link BasicCodeVisitor} or a subclass that can collect data about the java file being visited
+ */
+ public abstract BasicCodeVisitor getAbstractCodeVisitor();
+}
diff --git a/src/plugins/android.codeutils/src/com/motorola/studio/android/generatecode/AndroidXMLFileConstants.java b/src/plugins/android.codeutils/src/com/motorola/studio/android/generatecode/AndroidXMLFileConstants.java
new file mode 100644
index 0000000..fbaa6f8
--- /dev/null
+++ b/src/plugins/android.codeutils/src/com/motorola/studio/android/generatecode/AndroidXMLFileConstants.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.generatecode;
+
+/**
+ * Constants that are common to XML files (menu, layout, etc)
+ */
+public interface AndroidXMLFileConstants
+{
+ public static final String ANDROID_ON_CLICK = "android:onClick";
+
+ public static final String IDENTIFIER = "@+id/";
+
+ public static final String ANDROID_ID = "android:id";
+
+ public static final String ANDROID_NAME = "android:name";
+}
diff --git a/src/plugins/android.codeutils/src/com/motorola/studio/android/generatecode/BasicCodeVisitor.java b/src/plugins/android.codeutils/src/com/motorola/studio/android/generatecode/BasicCodeVisitor.java
new file mode 100644
index 0000000..19a1217
--- /dev/null
+++ b/src/plugins/android.codeutils/src/com/motorola/studio/android/generatecode/BasicCodeVisitor.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.generatecode;
+
+import org.eclipse.jdt.core.dom.ASTVisitor;
+import org.eclipse.jdt.core.dom.TypeDeclaration;
+
+/**
+ * AST visitor base class for activity / fragment classes.
+ */
+public class BasicCodeVisitor extends ASTVisitor
+{
+ protected TypeDeclaration typeDeclaration;
+
+ /**
+ * @return the typeDeclaration
+ */
+ public TypeDeclaration getTypeDeclaration()
+ {
+ return typeDeclaration;
+ }
+
+ /**
+ * @param typeDeclaration the typeDeclaration to set
+ */
+ public void setTypeDeclaration(TypeDeclaration typeDeclaration)
+ {
+ this.typeDeclaration = typeDeclaration;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.TypeDeclarationStatement)
+ */
+ @Override
+ public boolean visit(TypeDeclaration node)
+ {
+ boolean result = super.visit(node);
+ if (node.isPackageMemberTypeDeclaration())
+ {
+ //only keep if it is the top level class
+ typeDeclaration = node;
+ }
+ return result;
+ }
+}
diff --git a/src/plugins/android.codeutils/src/com/motorola/studio/android/generatecode/JDTUtils.java b/src/plugins/android.codeutils/src/com/motorola/studio/android/generatecode/JDTUtils.java
new file mode 100644
index 0000000..b1b4cf5
--- /dev/null
+++ b/src/plugins/android.codeutils/src/com/motorola/studio/android/generatecode/JDTUtils.java
@@ -0,0 +1,618 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.motorola.studio.android.generatecode;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.core.runtime.SubMonitor;
+import org.eclipse.jdt.core.ICompilationUnit;
+import org.eclipse.jdt.core.IJavaElement;
+import org.eclipse.jdt.core.IJavaProject;
+import org.eclipse.jdt.core.IPackageFragment;
+import org.eclipse.jdt.core.IPackageFragmentRoot;
+import org.eclipse.jdt.core.IType;
+import org.eclipse.jdt.core.ITypeHierarchy;
+import org.eclipse.jdt.core.JavaCore;
+import org.eclipse.jdt.core.JavaModelException;
+import org.eclipse.jdt.core.compiler.IProblem;
+import org.eclipse.jdt.core.dom.AST;
+import org.eclipse.jdt.core.dom.ASTParser;
+import org.eclipse.jdt.core.dom.CompilationUnit;
+import org.eclipse.osgi.util.NLS;
+
+import com.motorola.studio.android.codeutils.i18n.CodeUtilsNLS;
+import com.motorola.studio.android.common.IAndroidConstants;
+import com.motorola.studio.android.common.exception.AndroidException;
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.generatemenucode.model.codegenerators.CodeGeneratorBasedOnMenuVisitor;
+import com.motorola.studio.android.generatemenucode.model.codegenerators.CodeGeneratorDataBasedOnMenu;
+import com.motorola.studio.android.generateviewbylayout.GenerateCodeBasedOnLayoutVisitor;
+import com.motorola.studio.android.generateviewbylayout.model.CodeGeneratorDataBasedOnLayout;
+import com.motorola.studio.android.generateviewbylayout.model.JavaLayoutData;
+
+/**
+ * Class that implements convenient methods to abstract and handle JDT related operations.
+ * This class is not meant to be instantiated.
+ * */
+public class JDTUtils
+{
+
+ private JDTUtils()
+ {
+ //does nothing.
+ //prevents other objects to instantiate this class.
+ }
+
+ /**
+ * Retrieves the name of the inflated menu inside Activity or Fragment {@code type}.
+ * @param project The android project that contains the activity or fragment.
+ * @param compUnit The compilation unit of the activity or fragment.
+ * @return The name of the inflated menu inside {@code compUnit}.
+ * */
+ public static String getInflatedMenuFileName(IProject project, ICompilationUnit compUnit)
+ {
+ //check if type is either activity or fragment
+ //check if type inflates a menu on OnCreateOptionsMenu
+ //return the name of the inflated menu with ".xml" appended
+
+ CodeGeneratorBasedOnMenuVisitor visitor = new CodeGeneratorBasedOnMenuVisitor();
+ CompilationUnit cpAstNode = parse(compUnit);
+ StudioLogger.info("Trying to visit code for class: " + compUnit.getResource().getName()); //$NON-NLS-1$
+ try
+ {
+ cpAstNode.accept(visitor);
+ }
+ catch (IllegalArgumentException illegalArgumentException)
+ {
+ StudioLogger.error("Error while trying to visit code to get an inflated menu:"
+ + compUnit.getResource().getName());
+ }
+ return visitor.getInflatedMenuName();
+ }
+
+ /**
+ * Retrieves a list with the available android activities inside {@code project}.
+ * @param project The android project to retrieve the activities.
+ * @param monitor A progress monitor to be used to show operation status.
+ * */
+ public static List<IType> getAvailableActivities(IProject project, IProgressMonitor monitor)
+ throws JavaModelException
+ {
+ return getAvailableSubclasses(project, "android.app.Activity", monitor); //$NON-NLS-1$
+ }
+
+ /**
+ * Retrieves the list of subclasses of a given {@code superclass} inside an {@code androidProject}.
+ * @throws JavaModelException If there are problems parsing java files.
+ * */
+ public static List<IType> getAvailableSubclasses(IProject androidProject, String superclass,
+ IProgressMonitor monitor) throws JavaModelException
+ {
+ List<IType> availableActivities = new ArrayList<IType>();
+
+ SubMonitor subMonitor = SubMonitor.convert(monitor);
+ subMonitor.beginTask("Resolving available types", 1000); //$NON-NLS-1$
+
+ IJavaProject javaProject = JavaCore.create(androidProject);
+ IPackageFragmentRoot root =
+ javaProject.getPackageFragmentRoot(androidProject.findMember("src")); //$NON-NLS-1$
+ ArrayList<IPackageFragment> fragments = new ArrayList<IPackageFragment>();
+ for (IJavaElement element : root.getChildren())
+ {
+ fragments.add((IPackageFragment) element);
+ }
+
+ subMonitor.worked(100);
+
+ if (fragments.size() == 0)
+ {
+ subMonitor.worked(900);
+ }
+
+ for (IPackageFragment fragment : fragments)
+ {
+ ICompilationUnit[] units = fragment.getCompilationUnits();
+ if (units.length == 0)
+ {
+ subMonitor.worked(900 / fragments.size());
+ }
+ for (int j = 0; j < units.length; j++)
+ {
+ ICompilationUnit unit = units[j];
+ IType[] availableTypes = unit.getTypes();
+ if (availableTypes.length == 0)
+ {
+ subMonitor.worked(900 / fragments.size() / units.length);
+ }
+
+ for (int k = 0; k < availableTypes.length; k++)
+ {
+ ITypeHierarchy hierarchy =
+ availableTypes[k].newSupertypeHierarchy(subMonitor.newChild(900
+ / fragments.size() / units.length / availableTypes.length));
+
+ if (isSubclass(hierarchy, availableTypes[k], superclass))
+ {
+ availableActivities.add(availableTypes[k]);
+ }
+ }
+
+ }
+ }
+
+ return availableActivities;
+ }
+
+ /**
+ * Retrieves the list of fragments of a given {@code androidProject}.
+ * @throws JavaModelException If there are problems parsing java files.
+ * */
+ public static List<IType> getAvailableFragmentsSubclasses(IProject androidProject,
+ IProgressMonitor monitor) throws JavaModelException
+ {
+ List<IType> availableFragments = new ArrayList<IType>();
+
+ SubMonitor subMonitor = SubMonitor.convert(monitor);
+ subMonitor.beginTask("Resolving available types", 1000); //$NON-NLS-1$
+
+ IJavaProject javaProject = JavaCore.create(androidProject);
+ IPackageFragmentRoot root =
+ javaProject.getPackageFragmentRoot(androidProject.findMember("src")); //$NON-NLS-1$
+ ArrayList<IPackageFragment> fragments = new ArrayList<IPackageFragment>();
+ for (IJavaElement element : root.getChildren())
+ {
+ fragments.add((IPackageFragment) element);
+ }
+
+ subMonitor.worked(100);
+
+ if (fragments.size() == 0)
+ {
+ subMonitor.worked(900);
+ }
+
+ for (IPackageFragment fragment : fragments)
+ {
+ ICompilationUnit[] units = fragment.getCompilationUnits();
+ if (units.length == 0)
+ {
+ subMonitor.worked(900 / fragments.size());
+ }
+ for (int j = 0; j < units.length; j++)
+ {
+ ICompilationUnit unit = units[j];
+ IType[] availableTypes = unit.getTypes();
+ if (availableTypes.length == 0)
+ {
+ subMonitor.worked(900 / fragments.size() / units.length);
+ }
+
+ for (int k = 0; k < availableTypes.length; k++)
+ {
+ ITypeHierarchy hierarchy =
+ availableTypes[k].newSupertypeHierarchy(subMonitor.newChild(900
+ / fragments.size() / units.length / availableTypes.length));
+
+ if (isFragmentSubclass(hierarchy, availableTypes[k]))
+ {
+ availableFragments.add(availableTypes[k]);
+ }
+ }
+
+ }
+ }
+
+ return availableFragments;
+ }
+
+ /*
+ * Returns true if the {@code type} belongs to {@code superclass} hierarchy. Otherwise, returns false.
+ * */
+ private static boolean isSubclass(ITypeHierarchy hierarchy, IType type, String superclass)
+ {
+ boolean contains = false;
+ IType superclasstype = hierarchy.getSuperclass(type);
+ if (superclasstype != null)
+ {
+ if (hierarchy.getType().getFullyQualifiedName().equals(superclass)
+ || superclasstype.getFullyQualifiedName().equals(superclass))
+ {
+ contains = true;
+ }
+ else
+ {
+ contains = isSubclass(hierarchy, superclasstype, superclass);
+ }
+ }
+
+ return contains;
+ }
+
+ /**
+ * @return
+ * True if the {@code type} belongs to {@code superclass} hierarchy. Otherwise, returns false.
+ * */
+ public static boolean isSubclass(IType type, String superclass) throws JavaModelException
+ {
+ ITypeHierarchy typeHierarchy = type.newSupertypeHierarchy(new NullProgressMonitor());
+ return isSubclass(typeHierarchy, type, superclass);
+ }
+
+ /*
+ * Verifies if a given class extends a Fragment class (from sdk after 3.0 or from compatibility pack)
+ *
+ * @param hierarchy the hierarchy abstraction to the {@code type}
+ * @param type the type to be checked
+ * @return
+ * True if {@code type} has a Android Fragment in its type hierarchy.
+ */
+ private static boolean isFragmentSubclass(ITypeHierarchy hierarchy, IType type)
+ {
+ boolean contains = false;
+ IType superclasstype = hierarchy.getSuperclass(type);
+ if (superclasstype != null)
+ {
+ if (isAndroidFragment(superclasstype.getFullyQualifiedName()))
+ {
+ contains = true;
+ }
+ else
+ {
+ contains = isFragmentSubclass(hierarchy, superclasstype);
+ }
+ }
+
+ return contains;
+ }
+
+ /*
+ * Verifies if a given class extends a Fragment class (Fragment from compatibility pack)
+ *
+ * @param hierarchy
+ * @param type
+ * @return
+ */
+ private static boolean isCompatibilityPackFragmentsSubclass(ITypeHierarchy hierarchy, IType type)
+ {
+ boolean contains = false;
+ IType superclasstype = hierarchy.getSuperclass(type);
+ if (superclasstype != null)
+ {
+ if (isAndroidCompatibilityPackFragment(superclasstype.getFullyQualifiedName()))
+ {
+ contains = true;
+ }
+ else
+ {
+ contains = isCompatibilityPackFragmentsSubclass(hierarchy, superclasstype);
+ }
+ }
+
+ return contains;
+ }
+
+ /*
+ * Verifies if a given class extends a Fragment class (FragmentActivity from compatibility pack)
+ *
+ * @param hierarchy
+ * @param type
+ * @return
+ */
+ private static boolean isCompatibilityPackFragmentActivitySubclass(ITypeHierarchy hierarchy,
+ IType type)
+ {
+ boolean contains = false;
+ IType superclasstype = hierarchy.getSuperclass(type);
+ if (superclasstype != null)
+ {
+ if (isAndroidCompatibilityPackFragmentActivity(superclasstype.getFullyQualifiedName()))
+ {
+ contains = true;
+ }
+ else
+ {
+ contains = isCompatibilityPackFragmentActivitySubclass(hierarchy, superclasstype);
+ }
+ }
+
+ return contains;
+ }
+
+ private static boolean isAndroidFragment(String className)
+ {
+ boolean result = false;
+
+ if ((className != null) && (className.startsWith("android."))
+ && (className.endsWith(".Fragment")))
+ {
+ result = true;
+ }
+
+ return result;
+ }
+
+ private static boolean isAndroidCompatibilityPackFragment(String className)
+ {
+ boolean result = false;
+
+ if ((className != null) && (className.startsWith("android.support."))
+ && (className.endsWith(".Fragment")))
+ {
+ result = true;
+ }
+
+ return result;
+ }
+
+ private static boolean isAndroidCompatibilityPackFragmentActivity(String className)
+ {
+ boolean result = false;
+
+ if ((className != null) && (className.startsWith("android.support."))
+ && (className.endsWith(".FragmentActivity")))
+ {
+ result = true;
+ }
+
+ return result;
+ }
+
+ /**
+ * Parses source code.
+ *
+ * @param lwUnit
+ * the Java Model handle for the compilation unit
+ * @return the root AST node of the parsed source
+ */
+ public static CompilationUnit parse(ICompilationUnit lwUnit)
+ {
+ ASTParser parser = ASTParser.newParser(AST.JLS3);
+ parser.setKind(ASTParser.K_COMPILATION_UNIT);
+ parser.setSource(lwUnit); // set source
+ parser.setResolveBindings(true); // we need bindings later on
+ return (CompilationUnit) parser.createAST(null /* IProgressMonitor */); // parse
+ }
+
+ /**
+ * Creates the representation of a layout file based on the compilation unit
+ * @param visitor
+ * @param layout
+ * @return layout file
+ * @throws AndroidException if layout xml is malformed
+ */
+ public static CodeGeneratorDataBasedOnLayout createLayoutFile(IProject project,
+ ICompilationUnit compUnit) throws AndroidException
+ {
+ GenerateCodeBasedOnLayoutVisitor visitor = new GenerateCodeBasedOnLayoutVisitor();
+ CompilationUnit cpAstNode = parse(compUnit);
+ StudioLogger.info("Trying to visit code for class: " + compUnit.getResource().getName()); //$NON-NLS-1$
+ try
+ {
+ cpAstNode.accept(visitor);
+ }
+ catch (IllegalArgumentException illegalArgumentException)
+ {
+ String msg = CodeUtilsNLS.JDTUtils_FragmentOnCreateViewWithProblemsOrWithWrongFormat;
+ throw new AndroidException(msg, illegalArgumentException);
+ }
+ IFile layout =
+ project.getFile(File.separator + IAndroidConstants.FD_RES + File.separator
+ + IAndroidConstants.FD_LAYOUT + File.separator + visitor.getLayoutName()
+ + ".xml"); //$NON-NLS-1$
+ CodeGeneratorDataBasedOnLayout codeGeneratorData = null;
+ if (visitor.getLayoutName() == null)
+ {
+ //layout set or inflate not declared
+ throw new AndroidException(
+ CodeUtilsNLS.UI_ChooseLayoutItemsDialog_Error_onCreate_Not_Declared);
+ }
+ else
+ {
+ StudioLogger.info("Trying to read layout: " + layout); //$NON-NLS-1$
+ try
+ {
+ codeGeneratorData = new CodeGeneratorDataBasedOnLayout();
+ codeGeneratorData.init(visitor.getLayoutName(), layout.getLocation().toFile());
+ codeGeneratorData.setAssociatedType(visitor.getTypeAssociatedToLayout());
+
+ JavaLayoutData javaLayoutData = new JavaLayoutData();
+ javaLayoutData.setInflatedViewName(visitor.getInflatedViewName());
+ javaLayoutData.setDeclaredViewIdsOnCode(visitor.getDeclaredViewIds());
+ javaLayoutData.setSavedViewIds(visitor.getSavedViewIds());
+ javaLayoutData.setRestoredViewIds(visitor.getRestoredViewIds());
+ javaLayoutData.setVisitor(visitor);
+ javaLayoutData.setCompUnit(compUnit);
+ javaLayoutData.setCompUnitAstNode(cpAstNode);
+
+ codeGeneratorData.setJavaLayoutData(javaLayoutData);
+ }
+ catch (AndroidException ae)
+ {
+ String errorsMsg =
+ visitor.getLayoutName() != null ? NLS.bind(
+ CodeUtilsNLS.JDTUtils_MalformedXMLWhenFilenameAvailable_Error,
+ layout.getFullPath().toFile())
+ : CodeUtilsNLS.JDTUtils_MalformedXMLWhenFilenameNotAvailable_Error;
+ throw new AndroidException(errorsMsg, ae);
+ }
+ }
+ return codeGeneratorData;
+ }
+
+ /**
+ * Creates the representation of a menu file based on the compilation unit
+ * @param project
+ * @param compUnit
+ * @param menuFileName
+ * @param typeAssociated
+ * @return
+ * @throws AndroidException if menu xml is malformed
+ */
+ public static CodeGeneratorDataBasedOnMenu createMenuFile(IProject project,
+ ICompilationUnit compUnit, String menuFileName,
+ CodeGeneratorDataBasedOnMenu.TYPE typeAssociated) throws AndroidException
+ {
+ CodeGeneratorBasedOnMenuVisitor visitor = new CodeGeneratorBasedOnMenuVisitor();
+ CompilationUnit cpAstNode = parse(compUnit);
+ StudioLogger.info("Trying to visit code for class: " + compUnit.getResource().getName()); //$NON-NLS-1$
+ try
+ {
+ cpAstNode.accept(visitor);
+ }
+ catch (IllegalArgumentException illegalArgumentException)
+ {
+ String msg = CodeUtilsNLS.JDTUtils_GenerateCodeForMenuVisitingCode_Error;
+ throw new AndroidException(msg, illegalArgumentException);
+ }
+ IFile menu =
+ project.getFile(File.separator + IAndroidConstants.FD_RES + File.separator
+ + IAndroidConstants.FD_MENU + File.separator + menuFileName); //$NON-NLS-1$
+ CodeGeneratorDataBasedOnMenu codeGeneratorData = null;
+ StudioLogger.info("Trying to read menu: " + menu); //$NON-NLS-1$
+ try
+ {
+ codeGeneratorData = new CodeGeneratorDataBasedOnMenu();
+ codeGeneratorData.init(menuFileName, menu.getLocation().toFile());
+ codeGeneratorData.setAssociatedType(typeAssociated);
+ codeGeneratorData.setAbstractCodeVisitor(visitor);
+ codeGeneratorData.setICompilationUnit(compUnit);
+ codeGeneratorData.setCompilationUnit(cpAstNode);
+ }
+ catch (AndroidException ae)
+ {
+ String errorsMsg =
+ NLS.bind(CodeUtilsNLS.JDTUtils_MalformedMenuXMLWhenFilenameAvailable_Error,
+ menu.getLocation().toFile().toString());
+ throw new AndroidException(errorsMsg, ae);
+ }
+
+ return codeGeneratorData;
+ }
+
+ /**
+ * Given a Java {@link IFile}, this method returns <code>true</code> in case
+ * the qualified name entered as parameter represents its superclass.
+ *
+ * @param javaFile Java {@link IFile}.
+ * @param superClassFullyQualifiedName Super class fully qualified naem.
+ *
+ * @return Returns <code>true</code> in case the Java {@link IFile} class
+ * inherits from the super class represented by its full qualified name.
+ *
+ * @throws JavaModelException Exception thrown in case there are problems retrieving
+ * classes hierarchy.
+ */
+ public static boolean isSubclass(IFile javaFile, String superClassFullyQualifiedName)
+ throws JavaModelException
+ {
+ ICompilationUnit compUnit = JavaCore.createCompilationUnitFrom(javaFile);
+
+ IType type =
+ compUnit.getType(javaFile.getName().split("." + javaFile.getFileExtension())[0]); //$NON-NLS-1$
+
+ ITypeHierarchy typeHierarchy = type.newSupertypeHierarchy(new NullProgressMonitor());
+
+ return isSubclass(typeHierarchy, type, superClassFullyQualifiedName);
+
+ }
+
+ /**
+ * Verifies if a java class extends a Fragment class from a compatibility pack
+ *
+ * @param javaFile
+ * @return
+ * @throws JavaModelException
+ */
+ public static boolean isFragmentSubclass(IFile javaFile) throws JavaModelException
+ {
+ ICompilationUnit compUnit = JavaCore.createCompilationUnitFrom(javaFile);
+
+ IType type =
+ compUnit.getType(javaFile.getName().split("." + javaFile.getFileExtension())[0]); //$NON-NLS-1$
+
+ ITypeHierarchy typeHierarchy = type.newSupertypeHierarchy(new NullProgressMonitor());
+
+ return isFragmentSubclass(typeHierarchy, type);
+ }
+
+ /**
+ * Verifies if a java class extends a Fragment class from a compatibility pack
+ *
+ * @param javaFile
+ * @return
+ * @throws JavaModelException
+ */
+ public static boolean isCompatibilityFragmentSubclass(IFile javaFile) throws JavaModelException
+ {
+ ICompilationUnit compUnit = JavaCore.createCompilationUnitFrom(javaFile);
+
+ IType type =
+ compUnit.getType(javaFile.getName().split("." + javaFile.getFileExtension())[0]); //$NON-NLS-1$
+
+ ITypeHierarchy typeHierarchy = type.newSupertypeHierarchy(new NullProgressMonitor());
+
+ return isCompatibilityPackFragmentsSubclass(typeHierarchy, type);
+ }
+
+ /**
+ * Verifies if a java class extends a FragmentActivity class from a compatibility pack
+ *
+ * @param javaFile
+ * @return
+ * @throws JavaModelException
+ */
+ public static boolean isCompatibilityFragmentActivitySubclass(IFile javaFile)
+ throws JavaModelException
+ {
+ ICompilationUnit compUnit = JavaCore.createCompilationUnitFrom(javaFile);
+
+ IType type =
+ compUnit.getType(javaFile.getName().split("." + javaFile.getFileExtension())[0]); //$NON-NLS-1$
+
+ ITypeHierarchy typeHierarchy = type.newSupertypeHierarchy(new NullProgressMonitor());
+
+ return isCompatibilityPackFragmentActivitySubclass(typeHierarchy, type);
+ }
+
+ /**
+ * @return true if AST have at least one error (warnings are not considered), false otherwise.
+ */
+ public static boolean hasErrorInCompilationUnitAstUtils(CompilationUnit cpUnit)
+ {
+ boolean hasError = false;
+ if (cpUnit != null)
+ {
+ IProblem[] problems = cpUnit.getProblems();
+ for (IProblem probl : problems)
+ {
+ if (probl.isError())
+ {
+ hasError = true;
+ break;
+ }
+ }
+ }
+ return hasError;
+ }
+
+}
diff --git a/src/plugins/android.codeutils/src/com/motorola/studio/android/generatecode/JavaCodeModifier.java b/src/plugins/android.codeutils/src/com/motorola/studio/android/generatecode/JavaCodeModifier.java
new file mode 100644
index 0000000..4daa46d
--- /dev/null
+++ b/src/plugins/android.codeutils/src/com/motorola/studio/android/generatecode/JavaCodeModifier.java
@@ -0,0 +1,280 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.generatecode;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.SubMonitor;
+import org.eclipse.jdt.core.ICompilationUnit;
+import org.eclipse.jdt.core.IJavaModelStatusConstants;
+import org.eclipse.jdt.core.JavaCore;
+import org.eclipse.jdt.core.JavaModelException;
+import org.eclipse.jdt.core.dom.AST;
+import org.eclipse.jdt.core.dom.CompilationUnit;
+import org.eclipse.jdt.core.dom.ImportDeclaration;
+import org.eclipse.jdt.core.dom.TypeDeclaration;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.text.edits.TextEdit;
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.texteditor.AbstractTextEditor;
+
+import com.motorola.studio.android.codeutils.i18n.CodeUtilsNLS;
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.generatemenucode.model.codegenerators.CodeGeneratorDataBasedOnMenu;
+import com.motorola.studio.android.generateviewbylayout.model.CodeGeneratorDataBasedOnLayout;
+
+/**
+ * Manager responsible to modify activity / fragment.
+ */
+public abstract class JavaCodeModifier
+{
+ protected List<AbstractCodeGenerator> codeGenerators = new ArrayList<AbstractCodeGenerator>();
+
+ protected AbstractCodeGeneratorData codeGeneratorData;
+
+ public static final List<String> IMPORT_LIST = new ArrayList<String>();
+
+ protected TypeDeclaration typeDeclaration;
+
+ /**
+ * Insert code into the class (activity / fragment) and adds imports if necessary.
+ * @throws JavaModelException Thrown if there were problems parsing the java file.
+ */
+ public void insertCode(IProgressMonitor monitor, IEditorPart editor) throws JavaModelException
+ {
+ final SubMonitor theMonitor = SubMonitor.convert(monitor);
+ IResource resource = codeGeneratorData.getResource();
+ if (resource instanceof IFile)
+ {
+ IFile java = (IFile) resource;
+ StudioLogger
+ .info("Trying to insert code for class: " + java.getFullPath() + " based on resource " + getDataResource()); //$NON-NLS-1$
+ IDocument document = null;
+ try
+ {
+ document =
+ ((AbstractTextEditor) editor).getDocumentProvider().getDocument(
+ editor.getEditorInput());
+ final ICompilationUnit compUnit = getCodeGeneratorData().getICompilationUnit();
+ CompilationUnit cpU = getCodeGeneratorData().getCompilationUnit();
+
+ try
+ {
+ cpU.recordModifications();
+
+ initVariables();
+
+ codeGenerators.clear();
+ codeGenerators = populateListOfCodeGenerators(getCodeGeneratorData());
+
+ theMonitor.beginTask(CodeUtilsNLS.JavaViewBasedOnLayoutModifier_InsertingCode,
+ 1000 * getNumberOfTasks());
+
+ callCodeGenerators(theMonitor, java);
+
+ addImportsIfRequired(theMonitor, cpU);
+ Map<?, ?> mapOptions = JavaCore.create(java.getProject()).getOptions(true);
+ final TextEdit edit = cpU.rewrite(document, mapOptions);
+ PlatformUI.getWorkbench().getDisplay().syncExec(new Runnable()
+ {
+
+ public void run()
+ {
+ try
+ {
+ compUnit.applyTextEdit(edit, theMonitor);
+ }
+ catch (JavaModelException e)
+ {
+ StudioLogger.error(this.getClass(),
+ "Error applying changes: " + e.getMessage(), e); //$NON-NLS-1$
+ }
+
+ }
+ });
+ }
+ catch (CoreException e)
+ {
+ StudioLogger.error(this.getClass(),
+ "Error changing AST activity/fragment: " + e.getMessage()); //$NON-NLS-1$
+ throw new JavaModelException(e);
+ }
+ catch (RuntimeException rte)
+ {
+ StudioLogger.error(this.getClass(),
+ "Error changing AST activity/fragment: " + rte.getMessage()); //$NON-NLS-1$
+ throw new JavaModelException(rte, IJavaModelStatusConstants.CORE_EXCEPTION);
+ }
+ }
+ catch (CoreException e)
+ {
+ StudioLogger
+ .error(this.getClass(),
+ "Error creating IDocument from java file: " + java + " message: " + e.getMessage()); //$NON-NLS-1$ //$NON-NLS-2$
+ throw new JavaModelException(e, IJavaModelStatusConstants.CORE_EXCEPTION);
+ }
+ finally
+ {
+ theMonitor.done();
+ }
+ }
+ }
+
+ /**
+ * Init variables required by the code modifier (default behaviour init only typeDeclaration variable, override if necessary to add other variables)
+ */
+ protected void initVariables()
+ {
+ typeDeclaration = getCodeGeneratorData().getAbstractCodeVisitor().getTypeDeclaration();
+ }
+
+ /**
+ * @return file representing the path to data resource (e.g.: layout or menu)
+ */
+ protected abstract File getDataResource();
+
+ /**
+ * Calls code generators (override it if you have a special condition flag to generate code).
+ * <br>
+ * It iterates over {@link JavaCodeModifier#codeGenerators} list and calls {@link AbstractCodeGenerator#generateCode(IProgressMonitor)}
+ * @param theMonitor
+ * @param java file being modified
+ * @throws JavaModelException
+ */
+ protected void callCodeGenerators(final SubMonitor theMonitor, IFile java)
+ throws JavaModelException
+ {
+ for (AbstractCodeGenerator codeGenerator : codeGenerators)
+ {
+ codeGenerator.generateCode(theMonitor);
+ }
+ }
+
+ /**
+ * Adds imports if they were not added yet
+ * @param theMonitor
+ * @param compUnit
+ * @throws JavaModelException
+ */
+ public void addImportsIfRequired(SubMonitor theMonitor, CompilationUnit compUnit)
+ throws JavaModelException
+ {
+
+ for (String importString : IMPORT_LIST)
+ {
+ String importName = "";
+ boolean onDemand = false;
+ if (importString.endsWith(".*"))
+ {
+ importName = importString.substring(0, importString.length() - 2);
+ onDemand = true;
+ }
+ else
+ {
+ importName = importString;
+ }
+ boolean exists = false;
+ for (Object importDecl : compUnit.imports())
+ {
+ ImportDeclaration declaration = (ImportDeclaration) importDecl;
+ String name = declaration.getName().getFullyQualifiedName();
+ if (importName.equals(name))
+ {
+ exists = true;
+ break;
+ }
+ }
+ if (!exists)
+ {
+ createImport(importName, compUnit, onDemand);
+ }
+ }
+
+ }
+
+ @SuppressWarnings("unchecked")
+ private void createImport(String name, CompilationUnit compUnit, boolean onDemand)
+ {
+ AST ast = compUnit.getAST();
+ ImportDeclaration importDeclaration = ast.newImportDeclaration();
+ importDeclaration.setName(ast.newName(name));
+ importDeclaration.setOnDemand(onDemand);
+ compUnit.imports().add(importDeclaration);
+ }
+
+ /**
+ * Creates the necessary imports listed in {@link JavaCodeModifier#IMPORT_LIST}.
+ * @throws JavaModelException Thrown if there were problems during the insertion of imports in the java file.
+ */
+ protected void createImports(IProgressMonitor monitor, ICompilationUnit compilationUnit)
+ throws JavaModelException
+ {
+ SubMonitor subMonitor = SubMonitor.convert(monitor);
+ //need to look at each GUI item and them create 1 method
+ subMonitor.beginTask(CodeUtilsNLS.JavaViewBasedOnLayoutModifier_CreatingImports,
+ IMPORT_LIST.size());
+ if (IMPORT_LIST != null)
+ {
+ for (String importItem : IMPORT_LIST)
+ {
+ compilationUnit.createImport(importItem, null, subMonitor);
+ }
+ }
+ subMonitor.worked(IMPORT_LIST.size());
+ }
+
+ /**
+ * Sets the code generator input data (for example: {@link CodeGeneratorDataBasedOnLayout} or {@link CodeGeneratorDataBasedOnMenu}) to be used for the java code modifier
+ * @param codeGeneratorData the codeGeneratorData to set.
+ */
+ public void setCodeGeneratorData(AbstractCodeGeneratorData codeGeneratorData)
+ {
+ this.codeGeneratorData = codeGeneratorData;
+ }
+
+ /**
+ * @return the codeGeneratorData
+ */
+ public AbstractCodeGeneratorData getCodeGeneratorData()
+ {
+ return codeGeneratorData;
+ }
+
+ /**
+ * Populates the list of code generators that the modifier will use to change the code.
+ * @param codeGeneratorDataBasedOnLayout the data source to use into the modification
+ * @return list of code generators.
+ */
+ public abstract List<AbstractCodeGenerator> populateListOfCodeGenerators(
+ AbstractCodeGeneratorData abstractCodeGeneratorData);
+
+ /**
+ * @return The number of tasks based on the number of code generators.
+ */
+ protected int getNumberOfTasks()
+ {
+ return codeGenerators.size();
+ }
+
+}
diff --git a/src/plugins/android.codeutils/src/com/motorola/studio/android/generatemenucode/model/AbstractMenuNode.java b/src/plugins/android.codeutils/src/com/motorola/studio/android/generatemenucode/model/AbstractMenuNode.java
new file mode 100644
index 0000000..4193b4d
--- /dev/null
+++ b/src/plugins/android.codeutils/src/com/motorola/studio/android/generatemenucode/model/AbstractMenuNode.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.generatemenucode.model;
+
+/**
+ * Represents an abstraction for the nodes from menu.xml (to treat them uniformly in some return methods)
+ */
+public abstract class AbstractMenuNode
+{
+ /**
+ * XML elements available inside menu.xml
+ */
+ public static enum MenuNodeType
+ {
+ menu, item, group
+ }
+
+}
diff --git a/src/plugins/android.codeutils/src/com/motorola/studio/android/generatemenucode/model/GroupNode.java b/src/plugins/android.codeutils/src/com/motorola/studio/android/generatemenucode/model/GroupNode.java
new file mode 100644
index 0000000..75ab699
--- /dev/null
+++ b/src/plugins/android.codeutils/src/com/motorola/studio/android/generatemenucode/model/GroupNode.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.generatemenucode.model;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Represents the &lt;group&gt; element from menu.xml
+ */
+public class GroupNode extends AbstractMenuNode
+{
+ private String id;
+
+ private final List<MenuItemNode> menuItems = new ArrayList<MenuItemNode>();
+
+ /**
+ * Adds the menu item into menuItems list
+ * @param item
+ * @return
+ * @see java.util.List#add(java.lang.Object)
+ */
+ public boolean add(MenuItemNode item)
+ {
+ return menuItems.add(item);
+ }
+
+ /**
+ * @return the menuItems
+ */
+ protected final List<MenuItemNode> getMenuItems()
+ {
+ return menuItems;
+ }
+
+ /**
+ * @return the id
+ */
+ protected String getId()
+ {
+ return id;
+ }
+
+ /**
+ * @param id the id to set
+ */
+ protected void setId(String id)
+ {
+ this.id = id;
+ }
+}
diff --git a/src/plugins/android.codeutils/src/com/motorola/studio/android/generatemenucode/model/MenuFile.java b/src/plugins/android.codeutils/src/com/motorola/studio/android/generatemenucode/model/MenuFile.java
new file mode 100644
index 0000000..c2e0c40
--- /dev/null
+++ b/src/plugins/android.codeutils/src/com/motorola/studio/android/generatemenucode/model/MenuFile.java
@@ -0,0 +1,261 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.generatemenucode.model;
+
+import java.io.File;
+import java.io.IOException;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.xml.sax.SAXException;
+
+import com.motorola.studio.android.common.exception.AndroidException;
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.generatecode.AndroidXMLFileConstants;
+
+/**
+ * Represents a single menu.xml file.
+ * Based on Android documentation: {@link http://developer.android.com/guide/topics/resources/menu-resource.html}.
+ */
+public class MenuFile
+{
+ private final String name;
+
+ private final File file;
+
+ private MenuNode rootMenuNode;
+
+ /**
+ * @param menuFileName name that may appear into the dialog to create code based on menu
+ * @param menuFilePath absolute file path to menu.xml
+ * @throws AndroidException fail to parse menu.xml
+ */
+ public MenuFile(String menuFileName, File menuFilePath) throws AndroidException
+ {
+ this.name = menuFileName;
+ this.file = menuFilePath;
+ rootMenuNode = parseDocument(file);
+ }
+
+ /**
+ * @return the rootMenuNode the in-memory representation from menu.xml
+ */
+ public MenuNode getRootMenuNode()
+ {
+ return rootMenuNode;
+ }
+
+ /**
+ * The path to file (relative to project)
+ */
+ public String getName()
+ {
+ return name;
+ }
+
+ /**
+ * The path to file (relative to project)
+ */
+ public String getNameWithoutExtension()
+ {
+ String result;
+ if ((name != null) && name.contains("."))
+ {
+ result = name.substring(0, name.lastIndexOf('.'));
+ }
+ else
+ {
+ result = name;
+ }
+ return result;
+ }
+
+ public File getFile()
+ {
+ return file;
+ }
+
+ /**
+ * Parses an IDocument object containing the menu.xml into a DOM
+ *
+ * @param document the IDocument object
+ * @throws SAXException When a parsing error occurs
+ * @throws IOException When a reading error occurs
+ */
+ private static final MenuNode parseDocument(File f) throws AndroidException
+ {
+ MenuNode mainMenuNode = null;
+ DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
+ Document doc = null;
+ try
+ {
+ DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
+ doc = dBuilder.parse(f);
+ doc.getDocumentElement().normalize();
+ }
+ catch (Exception e)
+ {
+ StudioLogger.error(MenuFile.class, "Error parsing menu: " + e.getMessage());
+ throw new AndroidException(e);
+ }
+
+ Node node = doc.getFirstChild();
+ mainMenuNode = (MenuNode) readAttributes(node, null);
+
+ populateNodeForRootMenuNode(mainMenuNode, node);
+
+ return mainMenuNode;
+ }
+
+ /**
+ * Populates information about menus initiating recursion (start on menu node that is in the root of the file)
+ * @param mainMenuNode
+ * @param node
+ */
+ public static void populateNodeForRootMenuNode(MenuNode mainMenuNode, Node node)
+ {
+ NodeList children = node.getChildNodes();
+ for (int i = 0; i < children.getLength(); i++)
+ {
+ node = children.item(i);
+ if ((node != null) && (node.getNodeType() == Node.ELEMENT_NODE))
+ {
+ AbstractMenuNode menuNode = readAttributes(node, mainMenuNode);
+ if (node.hasChildNodes())
+ {
+ //navigate in deep in the tree, using current menuNode as parent node
+ populateNodes(node.getChildNodes(), menuNode);
+ }
+ }
+ }
+ }
+
+ /**
+ * Populates information about menus on non-root nodes
+ * @param children
+ * @param parentNode
+ */
+ private static final void populateNodes(NodeList children, AbstractMenuNode parentNode)
+ {
+ Node node;
+ for (int i = 0; i < children.getLength(); i++)
+ {
+ node = children.item(i);
+ if ((node != null) && (node.getNodeType() == Node.ELEMENT_NODE))
+ {
+ AbstractMenuNode menuNode = readAttributes(node, parentNode);
+ if (node.hasChildNodes())
+ {
+ //navigate in deep in the tree, using current menuNode as parent node
+ populateNodes(node.getChildNodes(), menuNode);
+ }
+ }
+ }
+ }
+
+ /**
+ * Reads attributes that are relevant to generate code based on menu
+ * @param node
+ * @param parentNode null if the root node, non-null if internal node
+ * @return current node being navigated
+ */
+ private static AbstractMenuNode readAttributes(Node node, AbstractMenuNode parentNode)
+ {
+ AbstractMenuNode currentMenuNode = getMenuNode(node.getNodeName());
+ Node id = node.getAttributes().getNamedItem(AndroidXMLFileConstants.ANDROID_ID);
+ if ((id != null))
+ {
+ String idText = id.getNodeValue();
+ idText = idText.replace(AndroidXMLFileConstants.IDENTIFIER, "");
+ if (currentMenuNode instanceof MenuItemNode)
+ {
+ MenuItemNode menuItemNode = (MenuItemNode) currentMenuNode;
+ menuItemNode.setId(idText);
+
+ Node onClick =
+ node.getAttributes().getNamedItem(AndroidXMLFileConstants.ANDROID_ON_CLICK);
+ if (onClick != null)
+ {
+ menuItemNode.setOnClickMethod(onClick.getNodeValue());
+ }
+ }
+ else if (currentMenuNode instanceof GroupNode)
+ {
+ GroupNode groupNode = (GroupNode) currentMenuNode;
+ groupNode.setId(idText);
+ }
+ }
+
+ if (parentNode != null)
+ {
+ //if internal node => set its parent
+ appendItemNodeStructure(parentNode, currentMenuNode);
+ }
+ return currentMenuNode;
+ }
+
+ /**
+ * Appends current node into tree representation
+ * @param parentNode
+ * @param currentMenuNode
+ */
+ public static void appendItemNodeStructure(AbstractMenuNode parentNode,
+ AbstractMenuNode currentMenuNode)
+ {
+ if (parentNode instanceof MenuNode)
+ {
+ MenuNode menuNode = (MenuNode) parentNode;
+ menuNode.add(currentMenuNode);
+ }
+ else if ((parentNode instanceof GroupNode) && (currentMenuNode instanceof MenuItemNode))
+ {
+ GroupNode groupNode = (GroupNode) parentNode;
+ groupNode.add((MenuItemNode) currentMenuNode);
+ }
+ else if ((parentNode instanceof MenuItemNode) && (currentMenuNode instanceof MenuNode))
+ {
+ MenuItemNode menuItemNode = (MenuItemNode) parentNode;
+ menuItemNode.setSubMenu((MenuNode) currentMenuNode);
+ }
+ }
+
+ /**
+ * Gets the type of the node, which can be menu, item or group
+ * @param nodeName
+ * @return
+ */
+ private static AbstractMenuNode getMenuNode(String nodeName)
+ {
+ AbstractMenuNode node = null;
+ if (nodeName.equals(AbstractMenuNode.MenuNodeType.menu.name()))
+ {
+ node = new MenuNode();
+ }
+ else if (nodeName.equals(AbstractMenuNode.MenuNodeType.item.name()))
+ {
+ node = new MenuItemNode();
+ }
+ else if (nodeName.equals(AbstractMenuNode.MenuNodeType.group.name()))
+ {
+ node = new GroupNode();
+ }
+ return node;
+ }
+}
diff --git a/src/plugins/android.codeutils/src/com/motorola/studio/android/generatemenucode/model/MenuItemNode.java b/src/plugins/android.codeutils/src/com/motorola/studio/android/generatemenucode/model/MenuItemNode.java
new file mode 100644
index 0000000..06ca953
--- /dev/null
+++ b/src/plugins/android.codeutils/src/com/motorola/studio/android/generatemenucode/model/MenuItemNode.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.generatemenucode.model;
+
+/**
+ * Represents the &lt;item&gt; element from menu.xml
+ */
+public class MenuItemNode extends AbstractMenuNode
+{
+ private MenuNode subMenu = null;
+
+ private String id;
+
+ private String onClickMethod;
+
+ /**
+ * @return the subMenu
+ */
+ protected MenuNode getSubMenu()
+ {
+ return subMenu;
+ }
+
+ /**
+ * @param subMenu the subMenu to set
+ */
+ protected void setSubMenu(MenuNode subMenu)
+ {
+ this.subMenu = subMenu;
+ }
+
+ /**
+ * @return the id
+ */
+ public String getId()
+ {
+ return id;
+ }
+
+ /**
+ * @param id the id to set
+ */
+ protected void setId(String id)
+ {
+ this.id = id;
+ }
+
+ public String getOnClickMethod()
+ {
+ return onClickMethod;
+ }
+
+ public void setOnClickMethod(String onClick)
+ {
+ this.onClickMethod = onClick;
+ }
+
+ /* (non-Javadoc)
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString()
+ {
+ return "MenuItemNode [id=" + id + ", onClickMethod=" + onClickMethod + "]";
+ }
+}
diff --git a/src/plugins/android.codeutils/src/com/motorola/studio/android/generatemenucode/model/MenuNode.java b/src/plugins/android.codeutils/src/com/motorola/studio/android/generatemenucode/model/MenuNode.java
new file mode 100644
index 0000000..3fd6b11
--- /dev/null
+++ b/src/plugins/android.codeutils/src/com/motorola/studio/android/generatemenucode/model/MenuNode.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.generatemenucode.model;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Represents the &lt;menu&gt; element from menu.xml
+ */
+public class MenuNode extends AbstractMenuNode
+{
+ private final List<MenuItemNode> menuItems = new ArrayList<MenuItemNode>();
+
+ private final List<GroupNode> groups = new ArrayList<GroupNode>();
+
+ /**
+ * Adds {@link MenuItemNode} into menuItems list
+ * or adds {@link GroupNode} into groups list.
+ * @param node
+ */
+ public void add(AbstractMenuNode node)
+ {
+ if (node instanceof MenuItemNode)
+ {
+ MenuItemNode menuItemNode = (MenuItemNode) node;
+ menuItems.add(menuItemNode);
+ }
+ else if (node instanceof GroupNode)
+ {
+ GroupNode groupNode = (GroupNode) node;
+ groups.add(groupNode);
+ }
+ }
+
+ /**
+ * Navigates into menu items and groups to deeply collect all menu items available in this root menu node
+ * @return list of {@link MenuItemNode}
+ */
+ public List<MenuItemNode> getAllMenuItems()
+ {
+ //adding direct menu items
+ List<MenuItemNode> menuItemNodes = new ArrayList<MenuItemNode>();
+ menuItemNodes.addAll(menuItems);
+
+ //adding inner menu items from internal menu items
+ for (MenuItemNode node : menuItems)
+ {
+ if (node.getSubMenu() != null)
+ {
+ //has submenu => add all items inside submenu
+ menuItemNodes.addAll(node.getSubMenu().getAllMenuItems());
+ }
+ }
+ //adding inner menu items from groups
+ for (GroupNode group : groups)
+ {
+ for (MenuItemNode node : group.getMenuItems())
+ {
+ menuItemNodes.add(node);
+ if (node.getSubMenu() != null)
+ {
+ //has submenu => add all items inside submenu
+ menuItemNodes.addAll(node.getSubMenu().getAllMenuItems());
+ }
+ }
+ }
+ return menuItemNodes;
+ }
+}
diff --git a/src/plugins/android.codeutils/src/com/motorola/studio/android/generatemenucode/model/codegenerators/AbstractMenuCodeGenerator.java b/src/plugins/android.codeutils/src/com/motorola/studio/android/generatemenucode/model/codegenerators/AbstractMenuCodeGenerator.java
new file mode 100644
index 0000000..7e3f06b
--- /dev/null
+++ b/src/plugins/android.codeutils/src/com/motorola/studio/android/generatemenucode/model/codegenerators/AbstractMenuCodeGenerator.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.generatemenucode.model.codegenerators;
+
+import org.eclipse.jdt.core.dom.TypeDeclaration;
+
+import com.motorola.studio.android.generatecode.AbstractCodeGenerator;
+
+/**
+ * Class that have common methods to generate code based on menu
+ */
+public abstract class AbstractMenuCodeGenerator extends AbstractCodeGenerator
+{
+ protected CodeGeneratorDataBasedOnMenu codeGeneratorData;
+
+ /**
+ * @param codeGeneratorData input data (representing menu.xml file) to use for creating automatic code
+ * @param typeDeclaration AST type where to insert the code
+ */
+ public AbstractMenuCodeGenerator(CodeGeneratorDataBasedOnMenu codeGeneratorData,
+ TypeDeclaration typeDeclaration)
+ {
+ super(typeDeclaration);
+ this.codeGeneratorData = codeGeneratorData;
+ }
+
+ /**
+ * @return the codeGeneratorData
+ */
+ protected CodeGeneratorDataBasedOnMenu getCodeGeneratorData()
+ {
+ return codeGeneratorData;
+ }
+}
diff --git a/src/plugins/android.codeutils/src/com/motorola/studio/android/generatemenucode/model/codegenerators/CodeGeneratorBasedOnMenuConstants.java b/src/plugins/android.codeutils/src/com/motorola/studio/android/generatemenucode/model/codegenerators/CodeGeneratorBasedOnMenuConstants.java
new file mode 100644
index 0000000..b5a744f
--- /dev/null
+++ b/src/plugins/android.codeutils/src/com/motorola/studio/android/generatemenucode/model/codegenerators/CodeGeneratorBasedOnMenuConstants.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.generatemenucode.model.codegenerators;
+
+/**
+ * Constants for creating code based on menu (method names, variables, types)
+ */
+public interface CodeGeneratorBasedOnMenuConstants
+{
+ /*
+ * Constants
+ */
+ public static final String GET_ITEM_ID = "getItemId"; //$NON-NLS-1$
+
+ public static final String ITEM = "item"; //$NON-NLS-1$
+
+ public static final String MENU_ITEM = "MenuItem"; //$NON-NLS-1$
+
+ public static final String ON_OPTIONS_ITEM_SELECTED = "onOptionsItemSelected"; //$NON-NLS-1$
+
+ public static final String MENU_INFLATER_VARIABLE = "MenuInflater"; //$NON-NLS-1$
+
+ public static final String GET_MENU_INFLATER = "getMenuInflater"; //$NON-NLS-1$
+
+ public static final String INFLATE_METHOD = "inflate"; //$NON-NLS-1$
+
+ public static final String INFLATER_VARIABLE = "inflater"; //$NON-NLS-1$
+
+ public static final String R = "R"; //$NON-NLS-1$
+
+ public static final String ON_CREATE_OPTIONS_MENU = "onCreateOptionsMenu"; //$NON-NLS-1$
+
+ public static final String MENU_VARIABLE = "menu"; //$NON-NLS-1$
+
+ public static final String MENU_TYPE = "Menu"; //$NON-NLS-1$
+
+ public static final String BUNDLE = "Bundle"; //$NON-NLS-1$
+
+ public static final String ON_CREATE = "onCreate"; //$NON-NLS-1$
+
+ public static final String SAVED_INSTANCE_STATE = "savedInstanceState"; //$NON-NLS-1$
+
+ public static final String SET_HAS_OPTIONS_MENU = "setHasOptionsMenu"; //$NON-NLS-1$
+}
diff --git a/src/plugins/android.codeutils/src/com/motorola/studio/android/generatemenucode/model/codegenerators/CodeGeneratorBasedOnMenuVisitor.java b/src/plugins/android.codeutils/src/com/motorola/studio/android/generatemenucode/model/codegenerators/CodeGeneratorBasedOnMenuVisitor.java
new file mode 100644
index 0000000..909e780
--- /dev/null
+++ b/src/plugins/android.codeutils/src/com/motorola/studio/android/generatemenucode/model/codegenerators/CodeGeneratorBasedOnMenuVisitor.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.generatemenucode.model.codegenerators;
+
+import java.util.List;
+
+import org.eclipse.jdt.core.dom.Block;
+import org.eclipse.jdt.core.dom.Expression;
+import org.eclipse.jdt.core.dom.ExpressionStatement;
+import org.eclipse.jdt.core.dom.IMethodBinding;
+import org.eclipse.jdt.core.dom.MethodDeclaration;
+import org.eclipse.jdt.core.dom.MethodInvocation;
+import org.eclipse.jdt.core.dom.SimpleName;
+
+import com.motorola.studio.android.generatecode.BasicCodeVisitor;
+
+/**
+ * Visitor to collect information on activity/fragment class (about menu creation methods or variables).
+ * It serves to avoid code duplication and fill the menu declared for this activity/fragment.
+ */
+public class CodeGeneratorBasedOnMenuVisitor extends BasicCodeVisitor
+{
+ /*
+ * Constants
+ */
+ private static final String ACTIVITY_ON_CREATE_MENU_DECLARATION =
+ "public boolean onCreateOptionsMenu(android.view.Menu)"; //$NON-NLS-1$
+
+ private static final String FRAGMENT_ON_CREATE_MENU_DECLARATION =
+ "public void onCreateOptionsMenu(android.view.Menu, android.view.MenuInflater)"; //$NON-NLS-1$
+
+ private static final String ACTIVITY_ON_CREATE_MENU = "onCreateOptionsMenu"; //$NON-NLS-1$
+
+ private static final String FRAGMENT_ON_CREATE_MENU = "onCreateOptionsMenu"; //$NON-NLS-1$
+
+ private static final String INFLATE_METHOD = "inflate";
+
+ /**
+ * If type is fragment, there may be an inflated menu name.
+ */
+ private String inflatedMenuName;
+
+ /**
+ * Visit method declaration, searching for instructions
+ * onCreate for activity or fragment
+ */
+ @Override
+ public boolean visit(MethodDeclaration node)
+ {
+ //Fill Method information
+ SimpleName name = node.getName();
+ if (name.getIdentifier().equals(ACTIVITY_ON_CREATE_MENU)
+ || name.getIdentifier().equals(FRAGMENT_ON_CREATE_MENU))
+ {
+ IMethodBinding binding = node.resolveBinding();
+ if (binding != null)
+ {
+ if (binding.toString().trim().contains(ACTIVITY_ON_CREATE_MENU_DECLARATION)
+ || binding.toString().trim().contains(FRAGMENT_ON_CREATE_MENU_DECLARATION))
+ {
+ visitMethodBodyToIdentifyMenu(node);
+ }
+ }
+ }
+
+ return super.visit(node);
+ }
+
+ /**
+ * Visit method body from onCreateOptionsMenu declaration to the inflated menu
+ * @param node
+ */
+ protected void visitMethodBodyToIdentifyMenu(MethodDeclaration node)
+ {
+ //Navigate through statements...
+
+ Block body = node.getBody();
+
+ if (body != null)
+ {
+
+ List<?> statements = body.statements();
+ if (statements != null)
+ {
+ for (Object statement : statements)
+ {
+
+ if ((statement != null) && (statement instanceof ExpressionStatement))
+ {
+ Expression argumentExpression =
+ ((ExpressionStatement) statement).getExpression();
+ if ((argumentExpression != null)
+ && (argumentExpression instanceof MethodInvocation))
+ {
+ String methodSimpleName =
+ ((MethodInvocation) argumentExpression).getName().toString();
+ if ((methodSimpleName != null)
+ && (methodSimpleName.equals(INFLATE_METHOD)))
+ {
+ if ((((MethodInvocation) argumentExpression).arguments() != null)
+ && (((MethodInvocation) argumentExpression).arguments()
+ .size() > 0))
+ {
+ String menuBeingInflated =
+ ((MethodInvocation) argumentExpression).arguments()
+ .get(0).toString();
+ if ((menuBeingInflated != null)
+ && (menuBeingInflated.indexOf('.') > 0))
+ {
+ setInflatedMenuName(menuBeingInflated.substring(
+ menuBeingInflated.lastIndexOf('.') + 1,
+ menuBeingInflated.length()));
+ }
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * @return the inflatedMenuName
+ */
+ public String getInflatedMenuName()
+ {
+ return inflatedMenuName;
+ }
+
+ /**
+ * @param inflatedMenuName the inflatedMenuName to set
+ */
+ public void setInflatedMenuName(String inflatedMenuName)
+ {
+ this.inflatedMenuName = inflatedMenuName;
+ }
+
+}
diff --git a/src/plugins/android.codeutils/src/com/motorola/studio/android/generatemenucode/model/codegenerators/CodeGeneratorDataBasedOnMenu.java b/src/plugins/android.codeutils/src/com/motorola/studio/android/generatemenucode/model/codegenerators/CodeGeneratorDataBasedOnMenu.java
new file mode 100644
index 0000000..3afab72
--- /dev/null
+++ b/src/plugins/android.codeutils/src/com/motorola/studio/android/generatemenucode/model/codegenerators/CodeGeneratorDataBasedOnMenu.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.generatemenucode.model.codegenerators;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.core.resources.IResource;
+import org.eclipse.jdt.core.ICompilationUnit;
+import org.eclipse.jdt.core.dom.CompilationUnit;
+
+import com.motorola.studio.android.common.exception.AndroidException;
+import com.motorola.studio.android.generatecode.AbstractCodeGeneratorData;
+import com.motorola.studio.android.generatecode.BasicCodeVisitor;
+import com.motorola.studio.android.generatemenucode.model.MenuFile;
+import com.motorola.studio.android.generatemenucode.model.MenuItemNode;
+
+/**
+ * Model representing the code generator data needed to generate code for menu.
+ * You MUST call init before using the object.
+ */
+public class CodeGeneratorDataBasedOnMenu extends AbstractCodeGeneratorData
+{
+ private MenuFile menuFile;
+
+ private BasicCodeVisitor codeVisitor;
+
+ private ICompilationUnit iCompilationUnit;
+
+ private CompilationUnit compilationUnit;
+
+ /**
+ * Creates {@link MenuFile} representation for the menu.xml
+ * @param menuName name of the menu
+ * @param menu file (full path) to menu.xml
+ * @throws AndroidException if an error occurs parsing menu.xml
+ */
+ public void init(String menuName, File menu) throws AndroidException
+ {
+ menuFile = new MenuFile(menuName, menu);
+ }
+
+ /**
+ * Get Menu Items that are not declared in the code yet
+ * @return list of Menu items from menu file (only ones with id set).
+ */
+ public List<MenuItemNode> getMenuItemsNodes()
+ {
+ return menuFile.getRootMenuNode() != null ? menuFile.getRootMenuNode().getAllMenuItems()
+ : new ArrayList<MenuItemNode>(0);
+ }
+
+ /**
+ * @return the representation from {@link MenuFile}
+ */
+ public MenuFile getMenuFile()
+ {
+ return menuFile;
+ }
+
+ @Override
+ public IResource getResource()
+ {
+ return compilationUnit.getJavaElement().getResource();
+ }
+
+ @Override
+ public ICompilationUnit getICompilationUnit()
+ {
+ return iCompilationUnit;
+ }
+
+ @Override
+ public CompilationUnit getCompilationUnit()
+ {
+ return compilationUnit;
+ }
+
+ @Override
+ public BasicCodeVisitor getAbstractCodeVisitor()
+ {
+ return codeVisitor;
+ }
+
+ /**
+ * Sets {@link BasicCodeVisitor} responsible to avoid code duplication or identify menu already inflated
+ * @param visitor
+ */
+ public void setAbstractCodeVisitor(BasicCodeVisitor visitor)
+ {
+ this.codeVisitor = visitor;
+ }
+
+ /**
+ * @param iCompilationUnit the iCompilationUnit to set
+ */
+ public void setICompilationUnit(ICompilationUnit iCompilationUnit)
+ {
+ this.iCompilationUnit = iCompilationUnit;
+ }
+
+ /**
+ * @param compilationUnit the compilationUnit to set
+ */
+ public void setCompilationUnit(CompilationUnit compilationUnit)
+ {
+ this.compilationUnit = compilationUnit;
+ }
+} \ No newline at end of file
diff --git a/src/plugins/android.codeutils/src/com/motorola/studio/android/generatemenucode/model/codegenerators/JavaModifierBasedOnMenu.java b/src/plugins/android.codeutils/src/com/motorola/studio/android/generatemenucode/model/codegenerators/JavaModifierBasedOnMenu.java
new file mode 100644
index 0000000..8c77741
--- /dev/null
+++ b/src/plugins/android.codeutils/src/com/motorola/studio/android/generatemenucode/model/codegenerators/JavaModifierBasedOnMenu.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.generatemenucode.model.codegenerators;
+
+import java.io.File;
+import java.util.List;
+
+import com.motorola.studio.android.generatecode.AbstractCodeGenerator;
+import com.motorola.studio.android.generatecode.AbstractCodeGeneratorData;
+import com.motorola.studio.android.generatecode.JavaCodeModifier;
+import com.motorola.studio.android.generateviewbylayout.JavaViewBasedOnLayoutModifierConstants;
+
+/**
+ * Manager responsible to modify activity / fragment based on menu
+ */
+public class JavaModifierBasedOnMenu extends JavaCodeModifier
+{
+ static
+ {
+ IMPORT_LIST.add(JavaViewBasedOnLayoutModifierConstants.IMPORT_ANDROID_VIEW_VIEW);
+ IMPORT_LIST.add(JavaViewBasedOnLayoutModifierConstants.IMPORT_ANDROID_OS);
+ }
+
+ /**
+ * @see com.motorola.studio.android.generatecode.JavaCodeModifier#populateListOfCodeGenerators(com.motorola.studio.android.generatecode.AbstractCodeGeneratorData)
+ */
+ @Override
+ public List<AbstractCodeGenerator> populateListOfCodeGenerators(
+ AbstractCodeGeneratorData abstractCodeGeneratorData)
+ {
+ CodeGeneratorDataBasedOnMenu codeGeneratorDataBasedOnMenu =
+ (CodeGeneratorDataBasedOnMenu) abstractCodeGeneratorData;
+ codeGenerators.add(new MenuHandlerCodeGenerator(codeGeneratorDataBasedOnMenu,
+ codeGeneratorDataBasedOnMenu.getAbstractCodeVisitor().getTypeDeclaration()));
+ return codeGenerators;
+ }
+
+ /**
+ * @see com.motorola.studio.android.generatecode.JavaCodeModifier#getDataResource()
+ */
+ @Override
+ protected File getDataResource()
+ {
+ return ((CodeGeneratorDataBasedOnMenu) codeGeneratorData).getMenuFile().getFile();
+ }
+}
diff --git a/src/plugins/android.codeutils/src/com/motorola/studio/android/generatemenucode/model/codegenerators/MenuHandlerCodeGenerator.java b/src/plugins/android.codeutils/src/com/motorola/studio/android/generatemenucode/model/codegenerators/MenuHandlerCodeGenerator.java
new file mode 100644
index 0000000..a05c963
--- /dev/null
+++ b/src/plugins/android.codeutils/src/com/motorola/studio/android/generatemenucode/model/codegenerators/MenuHandlerCodeGenerator.java
@@ -0,0 +1,599 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.generatemenucode.model.codegenerators;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.SubMonitor;
+import org.eclipse.jdt.core.IJavaModelStatus;
+import org.eclipse.jdt.core.JavaModelException;
+import org.eclipse.jdt.core.dom.Block;
+import org.eclipse.jdt.core.dom.BooleanLiteral;
+import org.eclipse.jdt.core.dom.ExpressionStatement;
+import org.eclipse.jdt.core.dom.IfStatement;
+import org.eclipse.jdt.core.dom.MethodDeclaration;
+import org.eclipse.jdt.core.dom.MethodInvocation;
+import org.eclipse.jdt.core.dom.Modifier.ModifierKeyword;
+import org.eclipse.jdt.core.dom.PrimitiveType;
+import org.eclipse.jdt.core.dom.QualifiedName;
+import org.eclipse.jdt.core.dom.ReturnStatement;
+import org.eclipse.jdt.core.dom.SimpleName;
+import org.eclipse.jdt.core.dom.SimpleType;
+import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
+import org.eclipse.jdt.core.dom.SuperMethodInvocation;
+import org.eclipse.jdt.core.dom.TypeDeclaration;
+import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
+import org.eclipse.jdt.core.dom.VariableDeclarationStatement;
+
+import com.motorola.studio.android.codeutils.i18n.CodeUtilsNLS;
+import com.motorola.studio.android.generatemenucode.model.MenuItemNode;
+import com.motorola.studio.android.generateviewbylayout.JavaViewBasedOnLayoutModifierConstants;
+
+/**
+ * Responsible to create menu handlers (Android code) for activities / fragments
+ */
+public class MenuHandlerCodeGenerator extends AbstractMenuCodeGenerator
+{
+ /*
+ * Constants (method bindings to avoid repetitive code)
+ */
+ private static final String ON_CREATE_OPTIONS_MENU_MENU_METHODBINDING =
+ "public boolean onCreateOptionsMenu(android.view.Menu)"; //$NON-NLS-1$
+
+ private static final String ON_CREATE_OPTIONS_MENU_MENU_METHODBINDING_FRAG =
+ "public void onCreateOptionsMenu(android.view.Menu, android.view.MenuInflater)"; //$NON-NLS-1$
+
+ private static final String ON_OPTIONS_ITEM_SELECTED_MENU_ITEM_METHODBINDING =
+ "public boolean onOptionsItemSelected(android.view.MenuItem)"; //$NON-NLS-1$
+
+ private static final String ON_CREATE_METHODBINDING = "public void onCreate(android.os.Bundle)"; //$NON-NLS-1$
+
+ /**
+ * @param codeGeneratorData
+ * @param typeDeclaration
+ */
+ public MenuHandlerCodeGenerator(CodeGeneratorDataBasedOnMenu codeGeneratorData,
+ TypeDeclaration typeDeclaration)
+ {
+ super(codeGeneratorData, typeDeclaration);
+ }
+
+ @Override
+ public void generateCode(IProgressMonitor monitor) throws JavaModelException
+ {
+ if (getCodeGeneratorData().getAssociatedType().equals(
+ CodeGeneratorDataBasedOnMenu.TYPE.FRAGMENT))
+ {
+ //for fragments, it is required to change onCreate to add setHasOptionMenu invocation
+ createOnCreateAndSetHasOptionMenu(monitor);
+ }
+ insertMethodToInflateMenu(monitor);
+ addMethodToHandleMenu(monitor);
+ }
+
+ /**
+ * Calls method that enables menu contribution for fragments
+ * <br>
+ * GENERATED_CODE_FORMAT:
+ * <br>
+ * <br>
+ * <code>
+ * public void onCreate (Bundle savedInstanceState) {
+ * <br>
+ * setHasOptionMenu(true);
+ * <br>
+ * super.onCreate(savedInstanceState);
+ * <br>
+ * }
+ * </code>
+ */
+ @SuppressWarnings("unchecked")
+ private void createOnCreateAndSetHasOptionMenu(IProgressMonitor monitor)
+ {
+ MethodDeclaration onCreateMethodDeclaration = createOnCreateMethod(monitor);
+ MethodDeclaration foundMethod =
+ isMethodAlreadyDeclared(onCreateMethodDeclaration, ON_CREATE_METHODBINDING);
+ if (foundMethod != null)
+ {
+ //method onCreateOptionsMenu is already created => use the found method instead of the new created one
+ onCreateMethodDeclaration = foundMethod;
+ }
+ MethodInvocation setHasOptionMenuInvoke =
+ createMethodInvocation(null, CodeGeneratorBasedOnMenuConstants.SET_HAS_OPTIONS_MENU);
+ BooleanLiteral defaultValue = typeDeclaration.getAST().newBooleanLiteral(true);
+ setHasOptionMenuInvoke.arguments().add(defaultValue);
+ ExpressionStatement statement =
+ typeDeclaration.getAST().newExpressionStatement(setHasOptionMenuInvoke);
+ addStatementIfNotFound(onCreateMethodDeclaration, statement, false);
+
+ List<String> arguments = new ArrayList<String>();
+ arguments.add(CodeGeneratorBasedOnMenuConstants.SAVED_INSTANCE_STATE); //$NON-NLS-1$
+ //super.onCreate(savedInstanceState);
+ insertSuperInvocation(onCreateMethodDeclaration,
+ CodeGeneratorBasedOnMenuConstants.ON_CREATE, arguments);
+ if (foundMethod == null)
+ {
+ //method onCreateOptionsMenu was not yet declared
+ typeDeclaration.bodyDeclarations().add(onCreateMethodDeclaration);
+ }
+ }
+
+ private MethodDeclaration createOnCreateMethod(IProgressMonitor monitor)
+ {
+ SubMonitor subMonitor = SubMonitor.convert(monitor);
+ subMonitor.beginTask(
+ CodeUtilsNLS.MenuHandlerCodeGenerator_AddingOnCreateAndSetHasOptionMenu, 1);
+
+ List<SingleVariableDeclaration> parameters = new ArrayList<SingleVariableDeclaration>();
+ SingleVariableDeclaration param1 =
+ createVariableDeclarationFromStrings(CodeGeneratorBasedOnMenuConstants.BUNDLE,
+ CodeGeneratorBasedOnMenuConstants.SAVED_INSTANCE_STATE);
+ parameters.add(param1);
+ //public void onCreate (Bundle savedInstanceState)
+ MethodDeclaration methodDeclaration =
+ addMethodDeclaration(ModifierKeyword.PUBLIC_KEYWORD,
+ CodeGeneratorBasedOnMenuConstants.ON_CREATE, PrimitiveType.VOID, parameters);
+ Block block = typeDeclaration.getAST().newBlock();
+ methodDeclaration.setBody(block);
+
+ subMonitor.worked(1);
+ return methodDeclaration;
+ }
+
+ /**
+ * Adds method to inflate menu
+ * <br>
+ * GENERATED_CODE_FORMAT (for activity):
+ * <br>
+ * <br>
+ * <code>
+ * public boolean onCreateOptionsMenu(Menu menu) {
+ * <br>
+ * MenuInflater inflater = getMenuInflater();
+ * <br>
+ * inflater.inflate(R.menu.<menu_id>, menu);
+ * <br>
+ * return true;
+ * <br>
+ * }
+ *
+ * <br>
+ * GENERATED_CODE_FORMAT (for fragment):
+ * <br>
+ * <br>
+ * <code>
+ * public boolean onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+ * <br>
+ * inflater.inflate(R.menu.<menu_id>, menu);
+ * <br>
+ * return true;
+ * <br>
+ * }
+ * </code>
+ *
+ * @param monitor to indicate progress when adding method declaration
+ */
+ @SuppressWarnings("unchecked")
+ private void insertMethodToInflateMenu(IProgressMonitor monitor)
+ {
+ SubMonitor subMonitor = SubMonitor.convert(monitor);
+ //need to look at each GUI item and them create 1 method
+ subMonitor.beginTask(CodeUtilsNLS.MenuHandlerCodeGenerator_AddingOnCreateOptionsMenu, 1);
+
+ MethodDeclaration methodDeclaration = null;
+ MethodDeclaration foundMethod = null;
+ if (getCodeGeneratorData().getAssociatedType().equals(
+ CodeGeneratorDataBasedOnMenu.TYPE.ACTIVITY))
+ {
+ //declare method
+ methodDeclaration = declareOnCreateOptionsMenuMethod();
+ foundMethod =
+ isMethodAlreadyDeclared(methodDeclaration,
+ ON_CREATE_OPTIONS_MENU_MENU_METHODBINDING);
+ if (foundMethod != null)
+ {
+ //method onCreateOptionsMenu is already created => use the found method instead of the new created one
+ methodDeclaration = foundMethod;
+ }
+ //declare inflater variable
+ declareInflaterVariable(methodDeclaration);
+ //call inflate method
+ callsInflateMethod(methodDeclaration);
+ //add return statement
+ createReturnStatement(methodDeclaration);
+ }
+ else if (getCodeGeneratorData().getAssociatedType().equals(
+ CodeGeneratorDataBasedOnMenu.TYPE.FRAGMENT))
+ {
+ //declare method
+ methodDeclaration = declareOnCreateOptionsMenuMethodFragment();
+ foundMethod =
+ isMethodAlreadyDeclared(methodDeclaration,
+ ON_CREATE_OPTIONS_MENU_MENU_METHODBINDING_FRAG);
+ if (foundMethod != null)
+ {
+ //method onCreateOptionsMenu is already created => use the found method instead of the new created one
+ methodDeclaration = foundMethod;
+ }
+ //call inflate method
+ callsInflateMethod(methodDeclaration);
+ //add return statement
+ createReturnStatementFragment(methodDeclaration);
+ }
+
+ if (foundMethod == null)
+ {
+ //method onCreateOptionsMenu was not yet declared
+ typeDeclaration.bodyDeclarations().add(methodDeclaration);
+ }
+ subMonitor.worked(1);
+ }
+
+ /**
+ * @param methodDeclaration
+ */
+ protected void createReturnStatementFragment(MethodDeclaration methodDeclaration)
+ {
+
+ List<String> arguments = new ArrayList<String>();
+ arguments.add(CodeGeneratorBasedOnMenuConstants.MENU_VARIABLE); //$NON-NLS-1$
+ arguments.add(CodeGeneratorBasedOnMenuConstants.INFLATER_VARIABLE); //$NON-NLS-1$
+ insertSuperInvocation(methodDeclaration,
+ CodeGeneratorBasedOnMenuConstants.ON_CREATE_OPTIONS_MENU, arguments);
+ }
+
+ /**
+ * Generates the following code:
+ * <code>public boolean onCreateOptionsMenu(Menu menu){}</code>
+ * @return
+ */
+ protected MethodDeclaration declareOnCreateOptionsMenuMethod()
+ {
+ List<SingleVariableDeclaration> parameters = new ArrayList<SingleVariableDeclaration>();
+ SingleVariableDeclaration param1 =
+ createVariableDeclarationFromStrings(CodeGeneratorBasedOnMenuConstants.MENU_TYPE,
+ CodeGeneratorBasedOnMenuConstants.MENU_VARIABLE);
+ parameters.add(param1);
+ MethodDeclaration methodDeclaration =
+ addMethodDeclaration(ModifierKeyword.PUBLIC_KEYWORD,
+ CodeGeneratorBasedOnMenuConstants.ON_CREATE_OPTIONS_MENU,
+ PrimitiveType.BOOLEAN, parameters);
+ Block block = typeDeclaration.getAST().newBlock();
+ methodDeclaration.setBody(block);
+ return methodDeclaration;
+ }
+
+ /**
+ * Generates the following code:
+ * <code>public void onCreateOptionsMenu(Menu menu, Inflater inflate){}</code>
+ * @return
+ */
+ protected MethodDeclaration declareOnCreateOptionsMenuMethodFragment()
+ {
+ List<SingleVariableDeclaration> parameters = new ArrayList<SingleVariableDeclaration>();
+ SingleVariableDeclaration param1 =
+ createVariableDeclarationFromStrings(CodeGeneratorBasedOnMenuConstants.MENU_TYPE,
+ CodeGeneratorBasedOnMenuConstants.MENU_VARIABLE);
+ parameters.add(param1);
+ SingleVariableDeclaration param2 =
+ createVariableDeclarationFromStrings(
+ CodeGeneratorBasedOnMenuConstants.MENU_INFLATER_VARIABLE,
+ CodeGeneratorBasedOnMenuConstants.INFLATER_VARIABLE);
+ parameters.add(param2);
+ //public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+ MethodDeclaration methodDeclaration =
+ addMethodDeclaration(ModifierKeyword.PUBLIC_KEYWORD,
+ CodeGeneratorBasedOnMenuConstants.ON_CREATE_OPTIONS_MENU,
+ PrimitiveType.VOID, parameters);
+ Block block = typeDeclaration.getAST().newBlock();
+ methodDeclaration.setBody(block);
+ return methodDeclaration;
+ }
+
+ /**
+ * Generates the following code:
+ * <code>inflater.inflate(R.menu.<menu_id>, menu);</code>
+ * @param methodDeclaration
+ */
+ @SuppressWarnings("unchecked")
+ protected void callsInflateMethod(MethodDeclaration methodDeclaration)
+ {
+ MethodInvocation inflateInvoke =
+ createMethodInvocation(CodeGeneratorBasedOnMenuConstants.INFLATER_VARIABLE,
+ CodeGeneratorBasedOnMenuConstants.INFLATE_METHOD);
+ SimpleName r = typeDeclaration.getAST().newSimpleName(CodeGeneratorBasedOnMenuConstants.R);
+ SimpleName menu =
+ typeDeclaration.getAST().newSimpleName(
+ CodeGeneratorBasedOnMenuConstants.MENU_VARIABLE);
+ SimpleName menuIdName =
+ typeDeclaration.getAST().newSimpleName(
+ getCodeGeneratorData().getMenuFile().getNameWithoutExtension());
+ QualifiedName rMenu = typeDeclaration.getAST().newQualifiedName(r, menu);
+ QualifiedName menuId = typeDeclaration.getAST().newQualifiedName(rMenu, menuIdName);
+ inflateInvoke.arguments().add(menuId);
+ SimpleName menuArg =
+ typeDeclaration.getAST().newSimpleName(
+ CodeGeneratorBasedOnMenuConstants.MENU_VARIABLE);
+ inflateInvoke.arguments().add(menuArg);
+ ExpressionStatement inflateExprStatement =
+ typeDeclaration.getAST().newExpressionStatement(inflateInvoke);
+
+ addStatementIfNotFound(methodDeclaration, inflateExprStatement, false);
+ }
+
+ /**
+ * Generates the following code:
+ * <code>MenuInflater inflater = getMenuInflater();</code>
+ * @param methodDeclaration
+ */
+ protected void declareInflaterVariable(MethodDeclaration methodDeclaration)
+ {
+ MethodInvocation getMenuInflaterInvoke =
+ createMethodInvocation(null, CodeGeneratorBasedOnMenuConstants.GET_MENU_INFLATER);
+ VariableDeclarationFragment declarationFragment =
+ typeDeclaration.getAST().newVariableDeclarationFragment();
+ SimpleName inflater =
+ typeDeclaration.getAST().newSimpleName(
+ CodeGeneratorBasedOnMenuConstants.INFLATER_VARIABLE);
+ declarationFragment.setName(inflater);
+ declarationFragment.setInitializer(getMenuInflaterInvoke);
+ VariableDeclarationStatement declarationStatement =
+ typeDeclaration.getAST().newVariableDeclarationStatement(declarationFragment);
+ SimpleName menuInflaterName =
+ typeDeclaration.getAST().newSimpleName(
+ CodeGeneratorBasedOnMenuConstants.MENU_INFLATER_VARIABLE);
+ SimpleType menuInflaterType = typeDeclaration.getAST().newSimpleType(menuInflaterName);
+ declarationStatement.setType(menuInflaterType);
+
+ addStatementIfNotFound(methodDeclaration, declarationStatement, false);
+ }
+
+ /**
+ * Adds method to declare menu events handler
+ * @param monitor
+ * @throws JavaModelException
+ *
+ * <br>
+ * GENERATED_CODE_FORMAT:
+ * <br>
+ * <br>
+ * <code>
+ * public boolean onOptionsItemSelected(MenuItem item) {
+ * <br>
+ * if (item.getItemId() == $MENUITEM_ID1) {
+ * <br>
+ * } else if (item.getItemId() == $MENUITEM_ID2) {
+ * <br>
+ * } else {
+ * <br>
+ * return super.onOptionsItemSelected(item);
+ * <br>
+ * }
+ * <br>
+ * return true;
+ * <br>
+ * }
+ * <br>
+ * </code>
+ */
+ private void addMethodToHandleMenu(IProgressMonitor monitor) throws JavaModelException
+ {
+ SubMonitor subMonitor = SubMonitor.convert(monitor);
+ //need to look at each GUI item and them create 1 method
+ subMonitor.beginTask(CodeUtilsNLS.MenuHandlerCodeGenerator_AddingOnOptionsItemSelected,
+ codeGeneratorData.getMenuItemsNodes().size() + 1);
+ IfStatement ifStatement = null;
+ for (MenuItemNode node : codeGeneratorData.getMenuItemsNodes())
+ {
+ if ((node.getOnClickMethod() == null) || node.getOnClickMethod().equals("")) //$NON-NLS-1$
+ {
+ //does not have on click declared
+ if (ifStatement == null)
+ {
+ //create method in the first time, after that, start to add new items in the same method
+ ifStatement = createOnOptionsItemSelectedForMenuItems(node);
+ }
+ else
+ {
+ //already have onOptionsItemSelected method declared => append new item into the "else if" chain
+ addElseIfForEachMenuItemId(ifStatement, node);
+ }
+ }
+ else
+ {
+ //has on click declared
+ createMenuItemHandlerForOnClick(node);
+ }
+ subMonitor.worked(1);
+ }
+
+ }
+
+ /**
+ * Creates method to handle menu item if android:onClick is defined on menu.xml
+ * <code>public void $myMethodName(MenuItem item)</code>
+ * @param node
+ */
+ @SuppressWarnings("unchecked")
+ private void createMenuItemHandlerForOnClick(MenuItemNode node) throws JavaModelException
+ {
+ if (node.getOnClickMethod() != null)
+ {
+ int i = 0;
+ //check if the onClick is valid
+ String invalidChar = null;
+ boolean validMethodName = true;
+ for (char ch : node.getOnClickMethod().toCharArray())
+ {
+ if ((i <= 0) && !Character.isJavaIdentifierStart(ch))
+ {
+ invalidChar = "" + ch; //$NON-NLS-1$
+ validMethodName = false;
+ break;
+ }
+ else if ((i > 0) && !Character.isJavaIdentifierPart(ch))
+ {
+ //i>0
+ invalidChar = "" + ch; //$NON-NLS-1$
+ validMethodName = false;
+ break;
+ }
+ i++;
+ }
+ if (!validMethodName)
+ {
+ Object[] bindings =
+ new Object[]
+ {
+ node.getOnClickMethod(),
+ getCodeGeneratorData().getMenuFile().getFile().getName(),
+ invalidChar
+ };
+ String msg =
+ CodeUtilsNLS
+ .bind(CodeUtilsNLS.MenuHandlerCodeGenerator_InvalidJavaCharacterInAndroidOnClickAttribute,
+ bindings);
+ throw new JavaModelException(new IllegalArgumentException(msg),
+ IJavaModelStatus.ERROR);
+ }
+
+ MethodDeclaration methodDeclaration =
+ addMethodDeclaration(ModifierKeyword.PUBLIC_KEYWORD, node.getOnClickMethod(),
+ PrimitiveType.VOID, CodeGeneratorBasedOnMenuConstants.MENU_ITEM,
+ CodeGeneratorBasedOnMenuConstants.ITEM);
+ String methodBinding =
+ "public void " + node.getOnClickMethod() + "(android.view.MenuItem)"; //$NON-NLS-1$ //$NON-NLS-2$
+ MethodDeclaration foundMethod =
+ isMethodAlreadyDeclared(methodDeclaration, methodBinding);
+ if (foundMethod == null)
+ {
+ //method public void $myMethodName(MenuItem item) was not yet declared
+ Block block = typeDeclaration.getAST().newBlock();
+ methodDeclaration.setBody(block);
+ typeDeclaration.bodyDeclarations().add(methodDeclaration);
+ }
+ }
+ }
+
+ /**
+ * Creates method with handle for menu items (if android:onClick) is not defined on menu.xml
+ * <code>public boolean onOptionsItemSelected(MenuItem item)</code>
+ */
+ @SuppressWarnings("unchecked")
+ private IfStatement createOnOptionsItemSelectedForMenuItems(MenuItemNode node)
+ {
+ IfStatement ifSt;
+ //declare method
+ MethodDeclaration methodDeclaration =
+ addMethodDeclaration(ModifierKeyword.PUBLIC_KEYWORD,
+ CodeGeneratorBasedOnMenuConstants.ON_OPTIONS_ITEM_SELECTED,
+ PrimitiveType.BOOLEAN, CodeGeneratorBasedOnMenuConstants.MENU_ITEM,
+ CodeGeneratorBasedOnMenuConstants.ITEM);
+ MethodDeclaration foundMethod =
+ isMethodAlreadyDeclared(methodDeclaration,
+ ON_OPTIONS_ITEM_SELECTED_MENU_ITEM_METHODBINDING);
+ Block block = null;
+ if (foundMethod != null)
+ {
+ //method onOptionsItemSelected is already created => use the found method instead of the new created one
+ methodDeclaration = foundMethod;
+ block = methodDeclaration.getBody();
+ }
+ else
+ {
+ //method onOptionsItemSelected not found => create block to insert statements
+ block = typeDeclaration.getAST().newBlock();
+ methodDeclaration.setBody(block);
+ }
+ //create if and else-if's
+ ifSt = createElseIfForEachMenuItemId(node);
+ IfStatement foundIfStatement =
+ (IfStatement) findIfStatementAlreadyDeclared(ifSt, true, block.statements());
+ if (foundIfStatement != null)
+ {
+ ifSt = foundIfStatement;
+ }
+ else
+ {
+ //if not existent yet then:
+ //1-add else
+ addingElseExpression(ifSt, methodDeclaration);
+ //2-add if statement only if there is not another one
+ addStatementIfNotFound(block, ifSt, true);
+ }
+ createReturnStatement(methodDeclaration);
+ if (foundMethod == null)
+ {
+ //method onOptionsItemSelected was not yet declared
+ typeDeclaration.bodyDeclarations().add(methodDeclaration);
+ }
+ return ifSt;
+ }
+
+ /**
+ * @param ifSt
+ * @param methodDeclaration
+ */
+ private void addingElseExpression(IfStatement ifSt, MethodDeclaration methodDeclaration)
+ {
+ Block block = typeDeclaration.getAST().newBlock();
+ ReturnStatement returnStatement = typeDeclaration.getAST().newReturnStatement();
+ List<String> arguments = new ArrayList<String>();
+ arguments.add(CodeGeneratorBasedOnMenuConstants.ITEM); //$NON-NLS-1$
+ SuperMethodInvocation superMethodInvocation =
+ createSuperMethodInvocation(
+ CodeGeneratorBasedOnMenuConstants.ON_OPTIONS_ITEM_SELECTED, arguments); //$NON-NLS-1$
+ returnStatement.setExpression(superMethodInvocation);
+ addStatementIfNotFound(block, returnStatement, false);
+ ifSt.setElseStatement(block);
+ }
+
+ /**
+ * @return "else if" chain for each menu item id
+ */
+ private IfStatement createElseIfForEachMenuItemId(MenuItemNode node)
+ {
+ IfStatement ifStatement = typeDeclaration.getAST().newIfStatement();
+ addElseIfForEachMenuItemId(ifStatement, node);
+ return ifStatement;
+ }
+
+ /**
+ * Creates else if and else statements for each menu item node
+ * @param ifSt If statement where the next "else if" will be appended
+ * @param node Menu node
+ */
+ private void addElseIfForEachMenuItemId(IfStatement ifSt, MenuItemNode node)
+ {
+ MethodInvocation invocation = typeDeclaration.getAST().newMethodInvocation();
+ invocation.setExpression(getVariableName(CodeGeneratorBasedOnMenuConstants.ITEM));
+ SimpleName getIdName =
+ typeDeclaration.getAST().newSimpleName(
+ CodeGeneratorBasedOnMenuConstants.GET_ITEM_ID);
+ invocation.setName(getIdName);
+
+ SimpleName r =
+ typeDeclaration.getAST().newSimpleName(JavaViewBasedOnLayoutModifierConstants.R);
+ SimpleName id =
+ typeDeclaration.getAST().newSimpleName(JavaViewBasedOnLayoutModifierConstants.ID);
+ QualifiedName rid = typeDeclaration.getAST().newQualifiedName(r, id);
+ SimpleName guiId = typeDeclaration.getAST().newSimpleName(node.getId());
+ QualifiedName guiQN = typeDeclaration.getAST().newQualifiedName(rid, guiId);
+ createElseIfAndElseStatements(ifSt, invocation, guiQN);
+ }
+} \ No newline at end of file
diff --git a/src/plugins/android.codeutils/src/com/motorola/studio/android/generatemenucode/ui/GenerateMenuCodeDialog.java b/src/plugins/android.codeutils/src/com/motorola/studio/android/generatemenucode/ui/GenerateMenuCodeDialog.java
new file mode 100644
index 0000000..72bb4e5
--- /dev/null
+++ b/src/plugins/android.codeutils/src/com/motorola/studio/android/generatemenucode/ui/GenerateMenuCodeDialog.java
@@ -0,0 +1,656 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.motorola.studio.android.generatemenucode.ui;
+
+import java.util.List;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IFolder;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.jdt.core.ICompilationUnit;
+import org.eclipse.jdt.core.IType;
+import org.eclipse.jdt.core.JavaCore;
+import org.eclipse.jdt.core.JavaModelException;
+import org.eclipse.jdt.core.dom.CompilationUnit;
+import org.eclipse.jface.dialogs.IMessageProvider;
+import org.eclipse.jface.dialogs.TitleAreaDialog;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Combo;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.ui.PlatformUI;
+
+import com.motorola.studio.android.codeutils.CodeUtilsActivator;
+import com.motorola.studio.android.codeutils.i18n.CodeUtilsNLS;
+import com.motorola.studio.android.common.IAndroidConstants;
+import com.motorola.studio.android.common.exception.AndroidException;
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.generatecode.JDTUtils;
+import com.motorola.studio.android.generatemenucode.model.codegenerators.CodeGeneratorDataBasedOnMenu;
+import com.motorola.studio.android.generatemenucode.model.codegenerators.JavaModifierBasedOnMenu;
+
+/**
+ * Dialog to generate code to deal with Android menus base on a selected menu.xml file
+ */
+public class GenerateMenuCodeDialog extends TitleAreaDialog
+{
+
+ private ICompilationUnit javaFile;
+
+ private IProject javaProject = null;
+
+ private JavaModifierBasedOnMenu modifier;
+
+ private Combo projectNameComboBox;
+
+ private Combo classNameComboBox;
+
+ private Combo menuFileNameComboBox;
+
+ private String menuFileErrorMessage;
+
+ private final String helpID = CodeUtilsActivator.PLUGIN_ID
+ + ".generate-code-from-context-menu-dialog"; //$NON-NLS-1$
+
+ private Image image = null;
+
+ private List<IType> availableFragmentClasses = null;
+
+ private final String defaultMessage;
+
+ private final String title;
+
+ private final String shellTitle;
+
+ /**
+ * Default constructor for the dialog
+ * @param parentShell shell to open the dialog
+ * @param description text to show dialog
+ * @param title text
+ * @param shellTitle window text
+ * @param image icon to show in the dialog
+ */
+ public GenerateMenuCodeDialog(Shell parentShell, String description, String title,
+ String shellTitle, Image image)
+ {
+ super(parentShell);
+ this.defaultMessage = description != null ? description : ""; //$NON-NLS-1$
+ this.title = title != null ? title : ""; //$NON-NLS-1$
+ this.shellTitle = shellTitle != null ? shellTitle : ""; //$NON-NLS-1$
+ this.image = image;
+ }
+
+ /**
+ * Set the initial values for project name and class name, if there is some selected.
+ * Also, set the modifier that will be used to generate code - the dialog set its codeGeneratorData
+ * according to the selected menu.
+ * @param modifier modifier that will have its codeGeneratorData set
+ * @param javaProject the project that will be selected when the dialog appears
+ * @param javaFile the class that will be selected when the dialog appears
+ * */
+ public void init(JavaModifierBasedOnMenu modifier, IProject javaProject, IFile javaFile)
+ {
+ setJavaModifier(modifier);
+ setJavaProject(javaProject);
+ setJavaFile(javaFile);
+ }
+
+ @Override
+ protected Control createContents(Composite parent)
+ {
+ Control c = super.createContents(parent);
+ setTitle(title);
+ if (image != null)
+ {
+ setTitleImage(image);
+ }
+ validate();
+ return c;
+ }
+
+ @Override
+ protected final Control createDialogArea(Composite parent)
+ {
+ if (helpID != null)
+ {
+ PlatformUI.getWorkbench().getHelpSystem().setHelp(parent, helpID);
+ }
+ Composite parentComposite = (Composite) super.createDialogArea(parent);
+ Composite mainComposite = new Composite(parentComposite, SWT.NULL);
+ mainComposite.setLayoutData(new GridData(GridData.FILL_BOTH));
+ mainComposite.setLayout(new GridLayout(2, false));
+ createProjectNameArea(mainComposite);
+ createClassNameArea(mainComposite);
+ createMenuFileNameArea(mainComposite);
+
+ Label separator = new Label(mainComposite, SWT.SEPARATOR | SWT.HORIZONTAL);
+ separator.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false, 2, 1));
+
+ return parentComposite;
+
+ }
+
+ /**
+ * Create GUI items for project name selection.
+ * @param optionsComposite
+ */
+ private void createProjectNameArea(Composite parent)
+ {
+ Label projectLabel = new Label(parent, SWT.NONE);
+ projectLabel.setText(CodeUtilsNLS.GenerateMenuCodeDialog_ProjectLabel);
+ projectLabel.setLayoutData(new GridData(SWT.LEFT, SWT.TOP, false, false, 1, 1));
+
+ projectNameComboBox = new Combo(parent, SWT.READ_ONLY | SWT.DROP_DOWN);
+ projectNameComboBox.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false, 1, 1));
+ projectNameComboBox.addSelectionListener(new SelectionAdapter()
+ {
+ @Override
+ public void widgetSelected(SelectionEvent e)
+ {
+ setJavaProject((IProject) projectNameComboBox.getData(projectNameComboBox.getText()));
+ populateClasses();
+ populateMenuFileNames();
+ }
+ });
+ populateProjects();
+ }
+
+ /**
+ * Create GUI items for class name selection.
+ * @param parent
+ */
+ private void createClassNameArea(Composite parent)
+ {
+ Label classLabel = new Label(parent, SWT.NONE);
+ classLabel.setText(CodeUtilsNLS.GenerateMenuCodeDialog_TargetClassLabel);
+ classLabel.setLayoutData(new GridData(SWT.LEFT, SWT.TOP, false, false, 1, 1));
+
+ classNameComboBox = new Combo(parent, SWT.READ_ONLY | SWT.DROP_DOWN);
+ classNameComboBox.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false, 1, 1));
+ classNameComboBox.addSelectionListener(new SelectionAdapter()
+ {
+ @Override
+ public void widgetSelected(SelectionEvent e)
+ {
+ setJavaFile(((IType) classNameComboBox.getData(classNameComboBox.getText()))
+ .getCompilationUnit());
+ populateMenuFileNames();
+ }
+ });
+ populateClasses();
+ }
+
+ /**
+ * Create GUI items for layout file name selection.
+ * @param parent
+ */
+ private void createMenuFileNameArea(Composite parent)
+ {
+ Label menuFileLabel = new Label(parent, SWT.NONE);
+ menuFileLabel.setText(CodeUtilsNLS.GenerateMenuCodeDialog_MenuFileLabel);
+ menuFileLabel.setLayoutData(new GridData(SWT.LEFT, SWT.TOP, false, false, 1, 1));
+
+ menuFileNameComboBox = new Combo(parent, SWT.READ_ONLY | SWT.DROP_DOWN);
+ menuFileNameComboBox.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false, 1, 1));
+
+ menuFileNameComboBox.addSelectionListener(new SelectionAdapter()
+ {
+ @Override
+ public void widgetSelected(SelectionEvent e)
+ {
+ validateMenuFileNames(menuFileNameComboBox.getText());
+ }
+ });
+ populateMenuFileNames();
+
+ }
+
+ /**
+ * Populate the combobox that holds projects, with information gathered from the ResourcesPlugin.
+ * also selects the project set in the init method
+ */
+ private void populateProjects()
+ {
+ if (projectNameComboBox != null)
+ {
+ IProject[] projects = ResourcesPlugin.getWorkspace().getRoot().getProjects();
+ int i = 0, selectedProjectIndex = -1;
+
+ for (IProject prj : projects)
+ {
+ try
+ {
+ if (prj.hasNature(IAndroidConstants.ANDROID_NATURE))
+ {
+ projectNameComboBox.add(prj.getName());
+ projectNameComboBox.setData(prj.getName(), prj);
+ if ((javaProject != null) && prj.equals(javaProject))
+ {
+ selectedProjectIndex = i;
+ }
+ i++;
+ }
+ }
+ catch (CoreException e)
+ {
+ StudioLogger.info("Project nature could not be checked."); //$NON-NLS-1$
+ }
+ }
+
+ if (projectNameComboBox.getItemCount() > 0)
+ {
+ if (selectedProjectIndex == -1)
+ {
+ projectNameComboBox.select(0);
+ setJavaProject((IProject) projectNameComboBox.getData(projectNameComboBox
+ .getText()));
+ }
+ else
+ {
+ projectNameComboBox.select(selectedProjectIndex);
+ }
+ }
+
+ }
+ validate();
+ }
+
+ /**
+ * Populate the combobox that holds class names.
+ */
+ private void populateClasses()
+ {
+ if (classNameComboBox != null)
+ {
+ classNameComboBox.removeAll();
+ if (javaProject != null)
+ {
+ int i = 0, selectedTypeIndex = -1;
+ try
+ {
+ List<IType> availableClasses =
+ JDTUtils.getAvailableActivities(javaProject, new NullProgressMonitor());
+
+ //Fragment classes can have menu too, so add all fragment classes to menu combo box
+
+ if (availableFragmentClasses != null)
+ {
+ availableFragmentClasses.clear();
+ }
+ availableFragmentClasses =
+ JDTUtils.getAvailableFragmentsSubclasses((IProject) projectNameComboBox
+ .getData(projectNameComboBox.getText()),
+ new NullProgressMonitor());
+
+ availableClasses.addAll(availableFragmentClasses);
+
+ for (IType availableClass : availableClasses)
+ {
+ classNameComboBox.add(availableClass.getFullyQualifiedName());
+ classNameComboBox.setData(availableClass.getFullyQualifiedName(),
+ availableClass);
+ if ((getJavaFile() != null)
+ && availableClass.getCompilationUnit().equals(getJavaFile()))
+ {
+ selectedTypeIndex = i;
+ }
+ i++;
+ }
+ if (classNameComboBox.getItemCount() > 0)
+ {
+ if (selectedTypeIndex == -1)
+ {
+ classNameComboBox.select(0);
+ setJavaFile(((IType) classNameComboBox.getData(classNameComboBox
+ .getText())).getCompilationUnit());
+ }
+ else
+ {
+ classNameComboBox.select(selectedTypeIndex);
+ }
+ }
+ }
+ catch (JavaModelException e)
+ {
+ StudioLogger.info("Could not get available classes for the selected project"); //$NON-NLS-1$
+ }
+
+ classNameComboBox.setEnabled(classNameComboBox.getItemCount() > 0);
+ }
+ }
+ validate();
+ }
+
+ /**
+ * Validate the selected menu file of the combobox that holds menu file names.
+ */
+ protected void validateMenuFileNames(String menuFileName)
+ {
+ menuFileErrorMessage = null;
+
+ if ((menuFileNameComboBox != null)
+ && (menuFileNameComboBox.getData(menuFileName) instanceof String))
+ {
+ // If the data type is a String, it represents an error message.
+ menuFileErrorMessage = (String) menuFileNameComboBox.getData(menuFileName);
+ }
+
+ validate();
+ }
+
+ /**
+ * Populate the combobox that holds menu file names.
+ */
+ private void populateMenuFileNames()
+ {
+ if ((menuFileNameComboBox != null) && (getJavaFile() != null))
+ {
+ menuFileNameComboBox.removeAll();
+ menuFileErrorMessage = null;
+ CompilationUnit cpAstNode = JDTUtils.parse(getJavaFile());
+ if (!JDTUtils.hasErrorInCompilationUnitAstUtils(cpAstNode))
+ {
+ IProject prj =
+ (IProject) this.projectNameComboBox.getData(this.projectNameComboBox
+ .getText());
+
+ IFolder menuFolder =
+ prj.getFolder(IAndroidConstants.FD_RESOURCES).getFolder(
+ IAndroidConstants.FD_MENU);
+
+ String inflatedMenu =
+ JDTUtils.getInflatedMenuFileName(getJavaProject(), getJavaFile());
+
+ if ((inflatedMenu != null) && (inflatedMenu.length() > 0))
+ {
+ //the activity/fragment already inflates a menu
+ //select this menu on the combo box
+ try
+ {
+ inflatedMenu = inflatedMenu + '.' + IAndroidConstants.MENU_FILE_EXTENSION;
+ menuFileNameComboBox.add(inflatedMenu);
+ CodeGeneratorDataBasedOnMenu codeGeneratorData;
+ codeGeneratorData =
+ JDTUtils.createMenuFile(getJavaProject(), getJavaFile(),
+ inflatedMenu, getTypeAssociatedToJavaFile());
+
+ menuFileNameComboBox.setData(inflatedMenu, codeGeneratorData);
+ menuFileNameComboBox.select(0);
+
+ setMessage(CodeUtilsNLS.GenerateMenuCodeDialog_InflatedMessage,
+ IMessageProvider.NONE);
+
+ }
+ catch (AndroidException e)
+ {
+ menuFileErrorMessage = e.getMessage();
+ menuFileNameComboBox.setData(inflatedMenu, menuFileErrorMessage);
+ }
+
+ //Disable the menu combo box so the user cannot change the menu file
+ menuFileNameComboBox.select(0);
+ menuFileNameComboBox.setEnabled(false);
+ }
+ else
+ {
+ //there is no inflated menu
+
+ setMessage(defaultMessage, IMessageProvider.NONE);
+ //iterate over all files inside res/menu
+ try
+ {
+ for (IResource menuFile : menuFolder.members())
+ {
+ //only consider xml files
+ if ((menuFile.getType() == IResource.FILE)
+ && menuFile.getFileExtension().equals(
+ IAndroidConstants.MENU_FILE_EXTENSION))
+ {
+
+ menuFileNameComboBox.add(menuFile.getName());
+
+ try
+ {
+ CodeGeneratorDataBasedOnMenu codeGeneratorData =
+ JDTUtils.createMenuFile(getJavaProject(),
+ getJavaFile(), menuFile.getName(),
+ getTypeAssociatedToJavaFile());
+
+ menuFileNameComboBox.setData(menuFile.getName(),
+ codeGeneratorData);
+ }
+
+ catch (AndroidException e)
+ {
+ // The malformed xml files
+ menuFileErrorMessage = e.getMessage();
+ menuFileNameComboBox.setData(menuFile.getName(),
+ menuFileErrorMessage);
+ }
+
+ }
+ }
+ }
+ catch (CoreException e)
+ {
+ menuFileErrorMessage =
+ CodeUtilsNLS.GenerateMenuCodeDialog_Error_MenuFolderDoesNotExist;
+ }
+
+ menuFileNameComboBox.select(0); //if the combo box is empty, the selection is ignored
+ menuFileNameComboBox.setEnabled(menuFileNameComboBox.getItemCount() > 0);
+ }
+ }
+ else
+ {
+ menuFileErrorMessage = CodeUtilsNLS.GenerateMenuCodeDialog_Class_Error;
+ this.javaFile = null;
+ menuFileNameComboBox.setEnabled(false);
+ }
+ }
+ else
+ {
+ menuFileNameComboBox.setEnabled(false);
+ }
+ validate();
+ }
+
+ /**
+ * Retrieve the type ACTIVITY/FRAGMENT from the android class
+ * @return
+ */
+ private CodeGeneratorDataBasedOnMenu.TYPE getTypeAssociatedToJavaFile()
+ {
+ CodeGeneratorDataBasedOnMenu.TYPE type = CodeGeneratorDataBasedOnMenu.TYPE.ACTIVITY;
+ for (IType availableClass : availableFragmentClasses)
+ {
+ if (availableClass.getCompilationUnit().equals(getJavaFile()))
+ {
+ type = CodeGeneratorDataBasedOnMenu.TYPE.FRAGMENT;
+ break;
+ }
+ }
+ return type;
+ }
+
+ @Override
+ protected void configureShell(Shell newShell)
+ {
+ newShell.setSize(640, 280);
+ newShell.setText(shellTitle);
+ super.configureShell(newShell);
+ }
+
+ /**
+ * Validate the UI
+ * @return
+ */
+ protected void validate()
+ {
+
+ String errorMessage = null;
+
+ if ((projectNameComboBox != null) && (projectNameComboBox.getItemCount() == 0))
+ {
+ errorMessage = CodeUtilsNLS.GenerateMenuCodeDialog_NoSuitableProjects;
+ }
+ if ((errorMessage == null) && (classNameComboBox != null)
+ && (classNameComboBox.getItemCount() == 0))
+ {
+ errorMessage = CodeUtilsNLS.GenerateMenuCodeDialog_NoSuitableClasses;
+ }
+ if ((errorMessage == null) && (menuFileNameComboBox != null))
+ {
+ if ((menuFileErrorMessage != null)
+ && (menuFileNameComboBox.getSelectionIndex() >= 0)
+ && (menuFileNameComboBox.getData(menuFileNameComboBox
+ .getItem(menuFileNameComboBox.getSelectionIndex())) instanceof String))
+ {
+ errorMessage =
+ (String) menuFileNameComboBox.getData(menuFileNameComboBox
+ .getItem(menuFileNameComboBox.getSelectionIndex()));
+ }
+ else
+ {
+ if ((menuFileNameComboBox.getItemCount() == 0) && (getJavaFile() != null))
+ {
+ errorMessage = CodeUtilsNLS.GenerateMenuCodeDialog_NoSuitableMenus;
+ }
+ else if (getJavaFile() == null)
+ {
+ errorMessage = menuFileErrorMessage;
+ }
+ }
+ }
+
+ this.setMessage(errorMessage, IMessageProvider.ERROR);
+
+ if (errorMessage == null)
+ {
+ if ((this.getMessage() == null) || (this.getMessage().length() == 0))
+ {
+ this.setMessage(defaultMessage, IMessageProvider.NONE);
+ }
+ }
+
+ if (getButton(OK) != null)
+ {
+ getButton(OK)
+ .setEnabled(
+ (getErrorMessage() == null)
+ || ((getErrorMessage() != null) && (getErrorMessage().trim()
+ .isEmpty())));
+ }
+ }
+
+ private void setJavaFile(IFile javaFile)
+ {
+ if (javaFile != null)
+ {
+ setJavaFile(JavaCore.createCompilationUnitFrom(javaFile));
+ }
+ }
+
+ private void setJavaFile(ICompilationUnit javaFile)
+ {
+ this.javaFile = javaFile;
+ setJavaProject(javaFile.getJavaProject().getProject());
+ }
+
+ /**
+ * @return Compilation unit for the selected file (activity or fragment)
+ */
+ public ICompilationUnit getJavaFile()
+ {
+ return this.javaFile;
+ }
+
+ private void setJavaProject(IProject javaProject)
+ {
+ this.javaProject = javaProject;
+ }
+
+ private IProject getJavaProject()
+ {
+ return this.javaProject;
+ }
+
+ /**
+ * Returns CodeGeneratorDataBasedOnMenu according to the selected menu file, or null if no menu is selected.
+ * */
+ private CodeGeneratorDataBasedOnMenu getCodeGeneratorData()
+ {
+ CodeGeneratorDataBasedOnMenu result = null;
+ if (menuFileNameComboBox.getSelectionIndex() >= 0)
+ {
+ result =
+ (CodeGeneratorDataBasedOnMenu) this.menuFileNameComboBox
+ .getData(menuFileNameComboBox.getText());
+ }
+
+ return result;
+ }
+
+ /**
+ * @return the modifier responsible to modify the source code
+ */
+ public JavaModifierBasedOnMenu getJavaModifier()
+ {
+ return modifier;
+ }
+
+ /**
+ * @param modifier the modifier to set
+ */
+ public void setJavaModifier(JavaModifierBasedOnMenu modifier)
+ {
+ this.modifier = modifier;
+ }
+
+ @Override
+ protected void okPressed()
+ {
+ modifier.setCodeGeneratorData(getCodeGeneratorData());
+
+ super.okPressed();
+ }
+
+ /**
+ * Sets the focus of the content area of the dialog
+ */
+ public void setFocus()
+ {
+ this.getContents().setFocus();
+ }
+
+ @Override
+ protected boolean isResizable()
+ {
+ return true;
+ }
+} \ No newline at end of file
diff --git a/src/plugins/android.codeutils/src/com/motorola/studio/android/generatemenucode/ui/GenerateMenuCodeHandler.java b/src/plugins/android.codeutils/src/com/motorola/studio/android/generatemenucode/ui/GenerateMenuCodeHandler.java
new file mode 100644
index 0000000..6b8c49e
--- /dev/null
+++ b/src/plugins/android.codeutils/src/com/motorola/studio/android/generatemenucode/ui/GenerateMenuCodeHandler.java
@@ -0,0 +1,333 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.motorola.studio.android.generatemenucode.ui;
+
+import java.lang.reflect.InvocationTargetException;
+import java.util.Iterator;
+
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.core.commands.IHandler;
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.IAdaptable;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.MultiStatus;
+import org.eclipse.jdt.core.ICompilationUnit;
+import org.eclipse.jdt.core.JavaModelException;
+import org.eclipse.jdt.ui.JavaUI;
+import org.eclipse.jface.dialogs.ErrorDialog;
+import org.eclipse.jface.dialogs.ProgressMonitorDialog;
+import org.eclipse.jface.operation.IRunnableWithProgress;
+import org.eclipse.jface.text.TextSelection;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.window.Window;
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.IFileEditorInput;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.handlers.HandlerUtil;
+import org.eclipse.ui.texteditor.ITextEditor;
+
+import com.motorola.studio.android.codeutils.CodeUtilsActivator;
+import com.motorola.studio.android.codeutils.i18n.CodeUtilsNLS;
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.common.log.UsageDataConstants;
+import com.motorola.studio.android.common.utilities.EclipseUtils;
+import com.motorola.studio.android.generatecode.JDTUtils;
+import com.motorola.studio.android.generatemenucode.model.codegenerators.JavaModifierBasedOnMenu;
+import com.motorola.studio.android.generateviewbylayout.ui.AbstractCodeGeneratorHandler;
+
+/**
+ * Command handler to start code generation based on menu
+ */
+public class GenerateMenuCodeHandler extends AbstractHandler implements IHandler
+{
+
+ private static final String WIZARD_IMAGE_PATH = "icons/wizban/fill_activity_ban.png"; //$NON-NLS-1$
+
+ /**
+ * Open {@link GenerateMenuCodeDialog} and use {@link JavaModifierBasedOnMenu} to insert code into Android source code (Activity/Fragment).
+ */
+ public Object execute(ExecutionEvent event) throws ExecutionException
+ {
+ SelectionBean selectionBean = resolveSelection(event);
+
+ if (selectionBean.isProject() || selectionBean.isAllowedClassInstance())
+ {
+
+ final IWorkbenchWindow window = HandlerUtil.getActiveWorkbenchWindow(event);
+
+ GenerateMenuCodeDialog menuDialog =
+ new GenerateMenuCodeDialog(window.getShell(),
+ CodeUtilsNLS.GenerateMenuCodeDialog_DefaultMessage,
+ CodeUtilsNLS.GenerateMenuCodeDialog_Title,
+ CodeUtilsNLS.GenerateMenuCodeDialog_ShellTitle, CodeUtilsActivator
+ .getImageDescriptor(WIZARD_IMAGE_PATH).createImage());
+
+ final JavaModifierBasedOnMenu modifier = new JavaModifierBasedOnMenu();
+ menuDialog.init(modifier, selectionBean.getJavaProject(), selectionBean.getJavaFile());
+
+ int status = menuDialog.open();
+ if (status == Window.OK)
+ {
+ ICompilationUnit compilationUnit = menuDialog.getJavaFile();
+ IEditorPart editor = null;
+ try
+ {
+ editor = JavaUI.openInEditor(compilationUnit);
+ }
+ catch (Exception e)
+ {
+ StudioLogger
+ .warn(GenerateMenuCodeHandler.class,
+ "Unable to open editor or bring it to front for Java file while trying to generate menu code based on xml file", //$NON-NLS-1$
+ e);
+ }
+ final ProgressMonitorDialog dialog =
+ new ProgressMonitorDialog(menuDialog.getShell());
+ final IEditorPart editorPart = editor;
+
+ PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable()
+ {
+
+ public void run()
+ {
+ try
+ {
+
+ dialog.run(true, false, new IRunnableWithProgress()
+ {
+
+ public void run(IProgressMonitor monitor)
+ throws InvocationTargetException, InterruptedException
+ {
+ try
+ {
+ // collect usage data - UDC
+ StudioLogger.collectUsageData(
+ UsageDataConstants.WHAT_VIEW_BY_MENU_EXEC,
+ UsageDataConstants.KIND_VIEW_BY_MENU_EXEC,
+ "View by menu feature executed.", //$NON-NLS-1$
+ CodeUtilsActivator.PLUGIN_ID, CodeUtilsActivator
+ .getDefault().getBundle().getVersion()
+ .toString());
+ modifier.insertCode(monitor, editorPart);
+ }
+ catch (final JavaModelException e)
+ {
+ final MultiStatus errorStatus =
+ new MultiStatus(
+ CodeUtilsActivator.PLUGIN_ID,
+ IStatus.ERROR,
+ "Error inserting code on activity/fragment based on menu", //$NON-NLS-1$
+ null);
+ errorStatus.merge(e.getStatus());
+
+ PlatformUI.getWorkbench().getDisplay()
+ .asyncExec(new Runnable()
+ {
+
+ public void run()
+ {
+ IStatus mostSevere =
+ EclipseUtils
+ .findMostSevereError(errorStatus);
+ ErrorDialog
+ .openError(
+ PlatformUI.getWorkbench()
+ .getDisplay()
+ .getActiveShell(),
+ "Error inserting code on activity/fragment based on menu", //$NON-NLS-1$
+ e.getMessage(), mostSevere);
+ }
+ });
+ StudioLogger.error(this.getClass(),
+ "Error inserting code on activity/fragment based on menu" //$NON-NLS-1$
+ + ": " + e.getMessage()); //$NON-NLS-1$
+ }
+ }
+ });
+ }
+ catch (Exception e)
+ {
+ StudioLogger.error(this.getClass(),
+ "Error inserting code on activity/fragment based on menu" //$NON-NLS-1$
+ + ": " + e.getMessage()); //$NON-NLS-1$
+ }
+ }
+ });
+ }
+ }
+ else
+ {
+ EclipseUtils.showErrorDialog(CodeUtilsNLS.GenerateMenuCodeDialog_Title,
+ CodeUtilsNLS.GenerateMenuCodeHandler_SelectedClassNeitherActivityFragment);//GenerateMenuCodeHandler_SelectedClassNeitherActivityFragment
+ }
+
+ return null;
+ }
+
+ protected static SelectionBean resolveSelection(ExecutionEvent event) throws ExecutionException
+ {
+ SelectionBean selectionBean = new SelectionBean();
+ ITextEditor editor = null;
+ IFileEditorInput fileEditorInput = null;
+
+ ISelection selection = HandlerUtil.getCurrentSelection(event);
+
+ // case where the selection comes from the Text Editor
+ if (selection instanceof TextSelection)
+ {
+ editor = (ITextEditor) HandlerUtil.getActiveEditorChecked(event);
+ if (editor.getEditorInput() instanceof IFileEditorInput)
+ {
+ fileEditorInput = (IFileEditorInput) editor.getEditorInput();
+ selectionBean.setJavaFile(fileEditorInput.getFile());
+ }
+ }
+ else if (selection instanceof IStructuredSelection)
+ {
+ Iterator<?> selectionIterator = ((IStructuredSelection) selection).iterator();
+ Object selectedObject = selectionIterator.next();
+
+ // case where the selection comes from the package explorer
+ if (selectedObject instanceof IFile)
+ {
+ selectionBean.setJavaFile((IFile) selectedObject);
+ }
+
+ // again, case where the selection comes from the package explorer
+ else if (selectedObject instanceof ICompilationUnit)
+ {
+ ICompilationUnit compilationUnit = (ICompilationUnit) selectedObject;
+ selectionBean.setJavaFile((IFile) compilationUnit.getResource());
+ }
+
+ // case where the selection comes from a project
+ else if (selectedObject instanceof IAdaptable)
+ {
+ try
+ {
+ IResource resource =
+ (IResource) ((IAdaptable) selectedObject).getAdapter(IResource.class);
+ selectionBean.setJavaProject(resource.getProject());
+ selectionBean.setProject(true);
+ }
+ catch (Exception ex)
+ {
+ StudioLogger.error(AbstractCodeGeneratorHandler.class,
+ "Error retrieving class information", ex); //$NON-NLS-1$
+ throw new RuntimeException(
+ CodeUtilsNLS.GenerateMenuCodeHandler_Error_CannotRetrieveClassInformation,
+ ex);
+ }
+ }
+ }
+
+ // just check classes in case classes were selected, not project
+ if (!selectionBean.isProject())
+ {
+ try
+ {
+ // the selected class must be either an Activity or a Fragment
+ selectionBean.setAllowedClassInstance(JDTUtils.isSubclass(
+ selectionBean.getJavaFile(), "android.app.Activity") //$NON-NLS-1$
+ || JDTUtils.isFragmentSubclass(selectionBean.getJavaFile())
+ || JDTUtils.isCompatibilityFragmentSubclass(selectionBean.getJavaFile()));
+ }
+ catch (JavaModelException jme)
+ {
+ StudioLogger.error(AbstractCodeGeneratorHandler.class,
+ "Error retrieving class information", jme); //$NON-NLS-1$
+ throw new RuntimeException(
+ CodeUtilsNLS.GenerateMenuCodeHandler_Error_CannotRetrieveClassInformation,
+ jme);
+ }
+ }
+ return selectionBean;
+ }
+}
+
+class SelectionBean
+{
+ private boolean isProject = false;
+
+ private boolean isAllowedClassInstance = false;
+
+ private IFile javaFile = null;
+
+ private IProject javaProject = null;
+
+ public boolean isProject()
+ {
+ return isProject;
+ }
+
+ public void setProject(boolean isProject)
+ {
+ this.isProject = isProject;
+ }
+
+ /**
+ * @return true if activity or fragment, false otherwise
+ */
+ public boolean isAllowedClassInstance()
+ {
+ return isAllowedClassInstance;
+ }
+
+ public void setAllowedClassInstance(boolean isAllowedClassInstance)
+ {
+ this.isAllowedClassInstance = isAllowedClassInstance;
+ }
+
+ /**
+ * @return selected Android file (Activity or Fragment)
+ */
+ public IFile getJavaFile()
+ {
+ return javaFile;
+ }
+
+ /**
+ * Sets the Android file selected (Activity or Fragment)
+ * @param javaFile
+ */
+ public void setJavaFile(IFile javaFile)
+ {
+ this.javaFile = javaFile;
+ javaProject = javaFile.getProject();
+ }
+
+ /**
+ * @return the project where the Android file (Activity or Fragment) is located
+ */
+ public IProject getJavaProject()
+ {
+ return javaProject;
+ }
+
+ public void setJavaProject(IProject javaProject)
+ {
+ this.javaProject = javaProject;
+ }
+}
diff --git a/src/plugins/android.codeutils/src/com/motorola/studio/android/generateviewbylayout/GenerateCodeBasedOnLayoutVisitor.java b/src/plugins/android.codeutils/src/com/motorola/studio/android/generateviewbylayout/GenerateCodeBasedOnLayoutVisitor.java
new file mode 100644
index 0000000..e4b0dd0
--- /dev/null
+++ b/src/plugins/android.codeutils/src/com/motorola/studio/android/generateviewbylayout/GenerateCodeBasedOnLayoutVisitor.java
@@ -0,0 +1,350 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.generateviewbylayout;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import org.eclipse.jdt.core.JavaModelException;
+import org.eclipse.jdt.core.dom.Block;
+import org.eclipse.jdt.core.dom.FieldDeclaration;
+import org.eclipse.jdt.core.dom.IMethodBinding;
+import org.eclipse.jdt.core.dom.MethodDeclaration;
+import org.eclipse.jdt.core.dom.SimpleName;
+import org.eclipse.jdt.core.dom.TypeDeclaration;
+import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
+
+import com.motorola.studio.android.codeutils.i18n.CodeUtilsNLS;
+import com.motorola.studio.android.generatecode.BasicCodeVisitor;
+import com.motorola.studio.android.generateviewbylayout.model.CodeGeneratorDataBasedOnLayout;
+import com.motorola.studio.android.generateviewbylayout.model.LayoutNode;
+
+/**
+ * Visitor for class method declarations to find onCreate methods inside activity / fragment.
+ * It calls BodyVisitor to continue extracting information about the Android code.
+ */
+public class GenerateCodeBasedOnLayoutVisitor extends BasicCodeVisitor
+{
+ /*
+ * Constants
+ */
+ private static final String ACTIVITY_ON_CREATE_DECLARATION = "void onCreate(android.os.Bundle)"; //$NON-NLS-1$
+
+ private static final String FRAGMENT_ON_CREATE_DECLARATION =
+ "android.view.View onCreateView(android.view.LayoutInflater, android.view.ViewGroup, android.os.Bundle)"; //$NON-NLS-1$
+
+ private static final String ACTIVITY_ON_CREATE = "onCreate"; //$NON-NLS-1$
+
+ private static final String FRAGMENT_ON_CREATE = "onCreateView"; //$NON-NLS-1$
+
+ private static final String ACTIVITY_ON_PAUSE_DECLARATION = "void onPause()";
+
+ private static final String ACTIVITY_ON_RESUME_DECLARATION = "void onResume()";
+
+ private static final String ACTIVITY_ON_PAUSE = "onPause";
+
+ private static final String ACTIVITY_ON_RESUME = "onResume";
+
+ private MethodDeclaration onCreateDeclaration;
+
+ private CodeGeneratorDataBasedOnLayout.TYPE typeAssociatedToLayout;
+
+ /**
+ * If type is fragment, there may be an inflated view name.
+ * This will be used to call findViewById inside fragments
+ */
+ private String inflatedViewName;
+
+ private final Set<String> declaredViewIds = new HashSet<String>();
+
+ private final Set<String> savedViewIds = new HashSet<String>();
+
+ private final Set<String> restoredViewIds = new HashSet<String>();
+
+ private String layoutName;
+
+ /**
+ * @return method declaration reference if there an onCreate method declared, null if not found
+ */
+ public MethodDeclaration getOnCreateDeclaration()
+ {
+ return onCreateDeclaration;
+ }
+
+ public void setOnCreateDeclaration(MethodDeclaration onCreateDeclaration)
+ {
+
+ this.onCreateDeclaration = onCreateDeclaration;
+ }
+
+ /**
+ * @return name of the layout being visited
+ */
+ public String getLayoutName()
+ {
+ return layoutName;
+ }
+
+ public void setLayoutName(String layoutName)
+ {
+ this.layoutName = layoutName;
+ }
+
+ /**
+ * Visit method declaration, searching for instructions
+ * onCreate for activity or fragment
+ */
+ @Override
+ public boolean visit(MethodDeclaration node)
+ {
+ //Fill Method information
+ SimpleName name = node.getName();
+ if (name.getIdentifier().equals(ACTIVITY_ON_CREATE)
+ || name.getIdentifier().equals(FRAGMENT_ON_CREATE))
+ {
+ IMethodBinding binding = node.resolveBinding();
+ if (binding != null)
+ {
+ if (binding.toString().trim().contains(ACTIVITY_ON_CREATE_DECLARATION))
+ {
+ visitMethodBodyToIdentifyLayout(node);
+ }
+ else if (binding.toString().trim().contains(FRAGMENT_ON_CREATE_DECLARATION))
+ {
+ if (node.getBody().statements().size() <= 1)
+ {
+ throw new IllegalArgumentException(
+ CodeUtilsNLS.MethodVisitor_InvalidFormatForFragmentOnCreateView);
+ }
+ else
+ {
+ visitMethodBodyToIdentifyLayout(node);
+ }
+ }
+ else
+ {
+ //for each method visit to identify views already declared
+ visitToIdentifyViewsAlreadyDeclared(node);
+ }
+ }
+ }
+ else if (name.getIdentifier().equals(ACTIVITY_ON_PAUSE)
+ || name.getIdentifier().equals(ACTIVITY_ON_RESUME))
+ {
+ IMethodBinding binding = node.resolveBinding();
+ if (binding != null)
+ {
+ //find declared save state
+ if (binding.toString().trim().contains(ACTIVITY_ON_PAUSE_DECLARATION))
+ {
+ findSavedViews(node);
+ }
+ //find declared restore state
+ else if (binding.toString().trim().contains(ACTIVITY_ON_RESUME_DECLARATION))
+ {
+ findRestoredViews(node);
+ }
+
+ }
+ }
+ else
+ {
+ //for each method visit to identify views already declared
+ visitToIdentifyViewsAlreadyDeclared(node);
+ }
+ return super.visit(node);
+ }
+
+ private void findRestoredViews(MethodDeclaration node)
+ {
+ SaveStateVisitor visitor = new SaveStateVisitor();
+ node.accept(visitor);
+ restoredViewIds.addAll(visitor.getViewIds());
+ }
+
+ private void findSavedViews(MethodDeclaration node)
+ {
+ SaveStateVisitor visitor = new SaveStateVisitor();
+ node.accept(visitor);
+ savedViewIds.addAll(visitor.getViewIds());
+ }
+
+ /**
+ * @param node
+ */
+ protected synchronized void visitToIdentifyViewsAlreadyDeclared(MethodDeclaration node)
+ {
+ Block body = node.getBody();
+ if (body != null)
+ {
+ MethodBodyVisitor visitor = new MethodBodyVisitor();
+ visitAndUpdateDeclaredViewsBasedOnFindViewById(body, visitor);
+ }
+ }
+
+ /**
+ * Visit method body from onCreate declaration to identify layout used
+ * (it also verifies views already declared)
+ * @param node
+ * @throws JavaModelException
+ */
+ protected void visitMethodBodyToIdentifyLayout(MethodDeclaration node)
+ {
+ //Navigate through statements...
+ setOnCreateDeclaration(node);
+ Block body = node.getBody();
+ if (body != null)
+ {
+ identifyLayout(body);
+ }
+ }
+
+ /**
+ * Navigates in a Block and extract layout name, if class associated to layout is activity or fragment,
+ * and, in case of fragment only, the name of the view inflated.
+ */
+ private void identifyLayout(Block body)
+ {
+ MethodBodyVisitor visitor = new MethodBodyVisitor();
+ visitAndUpdateDeclaredViewsBasedOnFindViewById(body, visitor);
+ setLayoutName(visitor.getLayoutName());
+ typeAssociatedToLayout = visitor.getTypeAssociatedToLayout();
+ setInflatedViewName(visitor.getInflatedViewName());
+ }
+
+ /**
+ * Visit method body to identify view ids already declared
+ * @param body
+ * @param visitor
+ */
+ public void visitAndUpdateDeclaredViewsBasedOnFindViewById(Block body, MethodBodyVisitor visitor)
+ {
+ body.accept(visitor);
+ synchronized (declaredViewIds)
+ {
+ declaredViewIds.addAll(visitor.getDeclaredViewIds());
+ }
+
+ }
+
+ /**
+ * Check if there is an attribute already declared with the name given.
+ * @param node
+ * @param considerType false, if must not consider the type in the analysis
+ * @return true if there a variable declared with the node.getNodeId() independent on variable type,
+ * false otherwise
+ */
+ public boolean checkIfAttributeAlreadyDeclared(LayoutNode node, boolean considerType)
+ {
+ boolean containFieldDeclared = false;
+ if (typeDeclaration.bodyDeclarations() != null)
+ {
+ //check if attribute already declared
+ for (Object bd : typeDeclaration.bodyDeclarations())
+ {
+ if (bd instanceof FieldDeclaration)
+ {
+ FieldDeclaration fd = (FieldDeclaration) bd;
+ if (fd.getParent() instanceof TypeDeclaration)
+ {
+ TypeDeclaration type = (TypeDeclaration) fd.getParent();
+ if (typeDeclaration.equals(type))
+ {
+ //only considers attributes from main class inside the file
+ for (Object fragment : fd.fragments())
+ {
+ if (fragment instanceof VariableDeclarationFragment)
+ {
+ VariableDeclarationFragment frag =
+ (VariableDeclarationFragment) fragment;
+ if ((frag.getName() != null)
+ && frag.getName().toString().equals(node.getNodeId()))
+ {
+ if (considerType)
+ {
+ if ((fd.getType() != null)
+ && !fd.getType().toString()
+ .equals(node.getNodeType()))
+ {
+ containFieldDeclared = true;
+ break;
+ }
+ }
+ else
+ {
+ containFieldDeclared = true;
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ return containFieldDeclared;
+ }
+
+ /**
+ * @return the typeAssociatedToLayout
+ */
+ public CodeGeneratorDataBasedOnLayout.TYPE getTypeAssociatedToLayout()
+ {
+ return typeAssociatedToLayout;
+ }
+
+ /**
+ * @return the inflatedViewName
+ */
+ public String getInflatedViewName()
+ {
+ return inflatedViewName;
+ }
+
+ /**
+ * @param inflatedViewName the inflatedViewName to set
+ */
+ public void setInflatedViewName(String inflatedViewName)
+ {
+ this.inflatedViewName = inflatedViewName;
+ }
+
+ /**
+ * @return the list of declared layout ids (as specified in layout.xml under android:id attribute)
+ */
+ public synchronized Set<String> getDeclaredViewIds()
+ {
+ return declaredViewIds;
+ }
+
+ /**
+ * @return the list of view ids that have code to restore state (using SharedPreferences)
+ */
+ public Set<String> getRestoredViewIds()
+ {
+ return restoredViewIds;
+ }
+
+ /**
+ * @return the list of view ids that have code to save state (using SharedPreferences)
+ */
+ public Set<String> getSavedViewIds()
+ {
+ return savedViewIds;
+ }
+
+}
diff --git a/src/plugins/android.codeutils/src/com/motorola/studio/android/generateviewbylayout/JavaModifierBasedOnLayout.java b/src/plugins/android.codeutils/src/com/motorola/studio/android/generateviewbylayout/JavaModifierBasedOnLayout.java
new file mode 100644
index 0000000..55ca8df
--- /dev/null
+++ b/src/plugins/android.codeutils/src/com/motorola/studio/android/generateviewbylayout/JavaModifierBasedOnLayout.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.generateviewbylayout;
+
+import java.io.File;
+import java.util.List;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.runtime.SubMonitor;
+import org.eclipse.jdt.core.JavaModelException;
+import org.eclipse.jdt.core.dom.MethodDeclaration;
+
+import com.motorola.studio.android.generatecode.AbstractCodeGenerator;
+import com.motorola.studio.android.generatecode.AbstractCodeGeneratorData;
+import com.motorola.studio.android.generatecode.JavaCodeModifier;
+import com.motorola.studio.android.generateviewbylayout.codegenerators.ClassAttributesCodeGenerator;
+import com.motorola.studio.android.generateviewbylayout.codegenerators.EditTextCodeGenerator;
+import com.motorola.studio.android.generateviewbylayout.codegenerators.FindViewByIdCodeGenerator;
+import com.motorola.studio.android.generateviewbylayout.codegenerators.GalleryCodeGenerator;
+import com.motorola.studio.android.generateviewbylayout.codegenerators.OnClickGUIsCodeGenerator;
+import com.motorola.studio.android.generateviewbylayout.codegenerators.RadioButtonCodeGenerator;
+import com.motorola.studio.android.generateviewbylayout.codegenerators.RatingBarCodeGenerator;
+import com.motorola.studio.android.generateviewbylayout.codegenerators.SaveStateCodeGenerator;
+import com.motorola.studio.android.generateviewbylayout.codegenerators.SeekBarCodeGenerator;
+import com.motorola.studio.android.generateviewbylayout.codegenerators.SpinnerCodeGenerator;
+import com.motorola.studio.android.generateviewbylayout.model.CodeGeneratorDataBasedOnLayout;
+
+/**
+ * Manager responsible to modify activity / fragment based on layout xml
+ */
+public class JavaModifierBasedOnLayout extends JavaCodeModifier
+{
+ private boolean generateDefaultListeners = false;
+
+ private MethodDeclaration onCreateDeclaration;
+
+ static
+ {
+ IMPORT_LIST.add(JavaViewBasedOnLayoutModifierConstants.IMPORT_ANDROID_APP);
+ IMPORT_LIST.add(JavaViewBasedOnLayoutModifierConstants.IMPORT_ANDROID_WIDGET);
+ IMPORT_LIST.add(JavaViewBasedOnLayoutModifierConstants.IMPORT_ANDROID_VIEW_VIEW);
+ }
+
+ @Override
+ protected void initVariables()
+ {
+ CodeGeneratorDataBasedOnLayout codeGeneratorDataBasedOnLayout =
+ (CodeGeneratorDataBasedOnLayout) getCodeGeneratorData();
+ onCreateDeclaration =
+ codeGeneratorDataBasedOnLayout.getJavaLayoutData().getVisitor()
+ .getOnCreateDeclaration();
+ //get typeDeclaration
+ super.initVariables();
+ }
+
+ @Override
+ protected void callCodeGenerators(final SubMonitor theMonitor, IFile java)
+ throws JavaModelException
+ {
+ CodeGeneratorDataBasedOnLayout codeGeneratorDataBasedOnLayout =
+ (CodeGeneratorDataBasedOnLayout) getCodeGeneratorData();
+ new ClassAttributesCodeGenerator(codeGeneratorDataBasedOnLayout, onCreateDeclaration,
+ typeDeclaration).generateCode(theMonitor);
+ FindViewByIdCodeGenerator f =
+ new FindViewByIdCodeGenerator(codeGeneratorDataBasedOnLayout, onCreateDeclaration,
+ typeDeclaration, java);
+ f.generateCode(theMonitor);
+ if (shouldGenerateDefaultListeners())
+ {
+ //call the generators declared into the codeGenerators list
+ super.callCodeGenerators(theMonitor, java);
+ }
+
+ //generate save state code
+ new SaveStateCodeGenerator(codeGeneratorDataBasedOnLayout, onCreateDeclaration,
+ typeDeclaration).generateCode(theMonitor);
+ }
+
+ /**
+ * Factory method to include all the code generators for the given java modifier
+ * @param codeGeneratorDataBasedOnLayout
+ * @return list of code generators
+ */
+ @Override
+ public List<AbstractCodeGenerator> populateListOfCodeGenerators(
+ AbstractCodeGeneratorData abstractCodeGeneratorData)
+ {
+ CodeGeneratorDataBasedOnLayout codeGeneratorDataBasedOnLayout =
+ (CodeGeneratorDataBasedOnLayout) abstractCodeGeneratorData;
+ codeGenerators.add(new OnClickGUIsCodeGenerator(codeGeneratorDataBasedOnLayout,
+ onCreateDeclaration, typeDeclaration));
+ codeGenerators.add(new RadioButtonCodeGenerator(codeGeneratorDataBasedOnLayout,
+ onCreateDeclaration, typeDeclaration));
+ codeGenerators.add(new EditTextCodeGenerator(codeGeneratorDataBasedOnLayout,
+ onCreateDeclaration, typeDeclaration));
+ codeGenerators.add(new SpinnerCodeGenerator(codeGeneratorDataBasedOnLayout,
+ onCreateDeclaration, typeDeclaration));
+ codeGenerators.add(new GalleryCodeGenerator(codeGeneratorDataBasedOnLayout,
+ onCreateDeclaration, typeDeclaration));
+ codeGenerators.add(new RatingBarCodeGenerator(codeGeneratorDataBasedOnLayout,
+ onCreateDeclaration, typeDeclaration));
+ codeGenerators.add(new SeekBarCodeGenerator(codeGeneratorDataBasedOnLayout,
+ onCreateDeclaration, typeDeclaration));
+ return codeGenerators;
+ }
+
+ /**
+ * @return true if need to generate code for listeners (buttons, edit fields), false if do not need
+ */
+ public boolean shouldGenerateDefaultListeners()
+ {
+ return generateDefaultListeners;
+ }
+
+ /**
+ * Defines if the class have to generate listeners (e.g.: onclick for buttons, onKey for edittext, and so on)
+ * @param generateDefaultListeners the generateDefaultListeners to set
+ */
+ public void setGenerateDefaultListeners(boolean generateDefaultListeners)
+ {
+ this.generateDefaultListeners = generateDefaultListeners;
+ }
+
+ @Override
+ protected File getDataResource()
+ {
+ return ((CodeGeneratorDataBasedOnLayout) codeGeneratorData).getLayoutFile().getFile();
+ }
+}
diff --git a/src/plugins/android.codeutils/src/com/motorola/studio/android/generateviewbylayout/JavaViewBasedOnLayoutModifierConstants.java b/src/plugins/android.codeutils/src/com/motorola/studio/android/generateviewbylayout/JavaViewBasedOnLayoutModifierConstants.java
new file mode 100644
index 0000000..1f0d28b
--- /dev/null
+++ b/src/plugins/android.codeutils/src/com/motorola/studio/android/generateviewbylayout/JavaViewBasedOnLayoutModifierConstants.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.generateviewbylayout;
+
+/**
+ * Constants for creating code based on layout (method names, variables, types)
+ */
+public interface JavaViewBasedOnLayoutModifierConstants
+{
+
+ /*
+ * Constants
+ */
+ String ON_KEY_LISTENER = "OnKeyListener"; //$NON-NLS-1$
+
+ String SET_ON_KEY_LISTENER = "setOnKeyListener"; //$NON-NLS-1$
+
+ String ON_KEY = "onKey"; //$NON-NLS-1$
+
+ String EVENT = "event"; //$NON-NLS-1$
+
+ String KEY_EVENT = "KeyEvent"; //$NON-NLS-1$
+
+ String KEY_CODE = "keyCode"; //$NON-NLS-1$
+
+ String METHOD_ON_CLICK_LISTENER = "OnClickListener"; //$NON-NLS-1$
+
+ String VIEW_VARIABLE_NAME = "target"; //$NON-NLS-1$
+
+ String METHOD_NAME_GET_ID = "getId"; //$NON-NLS-1$
+
+ String METHOD_NAME_ON_CLICK = "onClick"; //$NON-NLS-1$
+
+ String IMPORT_ANDROID_VIEW_VIEW = "android.view.*"; //$NON-NLS-1$
+
+ String IMPORT_ANDROID_WIDGET = "android.widget.*"; //$NON-NLS-1$
+
+ String IMPORT_ANDROID_APP = "android.app.*"; //$NON-NLS-1$
+
+ String IMPORT_ANDROID_OS = "android.os.Bundle"; //$NON-NLS-1$
+
+ String HANDLER_ONCLICK_LISTENER = "onClickHandler"; //$NON-NLS-1$
+
+ String SET_ON_CLICK_LISTENER = "setOnClickListener"; //$NON-NLS-1$
+
+ String ID = "id"; //$NON-NLS-1$
+
+ String R = "R"; //$NON-NLS-1$
+
+ String FIND_VIEW_BY_ID = "findViewById"; //$NON-NLS-1$
+
+ String FIND_FRAGMENT_BY_ID = "findFragmentById"; //$NON-NLS-1$
+
+ String ON_ITEM_SELECTED_LISTENER = "OnItemSelectedListener"; //$NON-NLS-1$
+
+ String SET_ON_ITEM_SELECTED_LISTENER = "setOnItemSelectedListener"; //$NON-NLS-1$
+
+ String ON_NOTHING_SELECTED = "onNothingSelected"; //$NON-NLS-1$
+
+ String ON_ITEM_SELECTED = "onItemSelected"; //$NON-NLS-1$
+
+ String ROW = "row"; //$NON-NLS-1$
+
+ String POSITION = "position"; //$NON-NLS-1$
+
+ String SELECTED_ITEM_VIEW = "selectedItemView"; //$NON-NLS-1$
+
+ String VIEW_CLASS = "View"; //$NON-NLS-1$
+
+ String ADAPTER_VIEW = "AdapterView"; //$NON-NLS-1$
+
+ String PARENT_VIEW = "parentView"; //$NON-NLS-1$
+
+ String SET_ON_ITEM_CLICK_LISTENER = "setOnItemClickListener"; //$NON-NLS-1$
+
+ String ON_ITEM_CLICK_LISTENER = "OnItemClickListener"; //$NON-NLS-1$
+
+ String SET_ON_RATING_BAR_CHANGE_LISTENER = "setOnRatingBarChangeListener"; //$NON-NLS-1$
+
+ String ON_ITEM_CLICK = "onItemClick"; //$NON-NLS-1$
+
+ String ON_SEEK_BAR_CHANGE_LISTENER = "OnSeekBarChangeListener"; //$NON-NLS-1$
+
+ String ON_STOP_TRACKING_TOUCH = "onStopTrackingTouch"; //$NON-NLS-1$
+
+ String ON_START_TRACKING_TOUCH = "onStartTrackingTouch"; //$NON-NLS-1$
+
+ String ON_PROGRESS_CHANGED = "onProgressChanged"; //$NON-NLS-1$
+
+ String ON_RATING_BAR_CHANGE_LISTENER = "OnRatingBarChangeListener"; //$NON-NLS-1$
+
+ String SEEK_BAR = "SeekBar"; //$NON-NLS-1$
+
+ String SET_ON_SEEK_BAR_CHANGE_LISTENER = "setOnSeekBarChangeListener"; //$NON-NLS-1$
+
+ String ON_RATING_CHANGED = "onRatingChanged"; //$NON-NLS-1$
+
+ String RATING_VARIABLE = "rating"; //$NON-NLS-1$
+
+ String RATING_BAR_VARIABLE = "ratingBar"; //$NON-NLS-1$
+
+ String FROM_USER_VARIABLE = "fromUser"; //$NON-NLS-1$
+
+ String PROGRESS_VARIABLE = "progress"; //$NON-NLS-1$
+
+ String SEEK_BAR_VARIABLE = "seekBar"; //$NON-NLS-1$
+
+ String GET_FRAGMENT_MANAGER = "getFragmentManager"; //$NON-NLS-1$
+
+ String GET_SUPPORT_FRAGMENT_MANAGER = "getSupportFragmentManager"; //$NON-NLS-1$
+
+ String EXPRESSION_MISSING = "MISSING";
+}
diff --git a/src/plugins/android.codeutils/src/com/motorola/studio/android/generateviewbylayout/MethodBodyVisitor.java b/src/plugins/android.codeutils/src/com/motorola/studio/android/generateviewbylayout/MethodBodyVisitor.java
new file mode 100644
index 0000000..953693b
--- /dev/null
+++ b/src/plugins/android.codeutils/src/com/motorola/studio/android/generateviewbylayout/MethodBodyVisitor.java
@@ -0,0 +1,226 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.generateviewbylayout;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.eclipse.jdt.core.IJavaElement;
+import org.eclipse.jdt.core.IType;
+import org.eclipse.jdt.core.JavaModelException;
+import org.eclipse.jdt.core.dom.ASTNode;
+import org.eclipse.jdt.core.dom.ASTVisitor;
+import org.eclipse.jdt.core.dom.Assignment;
+import org.eclipse.jdt.core.dom.Expression;
+import org.eclipse.jdt.core.dom.IMethodBinding;
+import org.eclipse.jdt.core.dom.ITypeBinding;
+import org.eclipse.jdt.core.dom.MethodInvocation;
+import org.eclipse.jdt.core.dom.QualifiedName;
+import org.eclipse.jdt.core.dom.SimpleName;
+import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
+import org.eclipse.jdt.core.dom.VariableDeclarationStatement;
+
+import com.motorola.studio.android.generatecode.JDTUtils;
+import com.motorola.studio.android.generateviewbylayout.model.CodeGeneratorDataBasedOnLayout;
+
+/**
+ * Visitor responsible to search method invocations inside a method body
+ * and set the layout name associated with the activity / fragment
+ */
+public class MethodBodyVisitor extends ASTVisitor
+{
+ /*
+ * Constants
+ */
+ private static final String INFLATE = "inflate"; //$NON-NLS-1$
+
+ private static final String VIEW = "View"; //$NON-NLS-1$
+
+ private static final String ACTIVITY_METHOD_TO_SET_LAYOUT = "setContentView"; //$NON-NLS-1$
+
+ private static final String FRAGMENT_METHOD_TO_SET_LAYOUT = INFLATE;
+
+ private String layoutName;
+
+ /**
+ * If type is fragment, there may be an inflated view name.
+ * This will be used to call findViewById inside fragments
+ */
+ private String inflatedViewName;
+
+ private CodeGeneratorDataBasedOnLayout.TYPE typeAssociatedToLayout;
+
+ private Set<String> declaredViewIds = new HashSet<String>();
+
+ public String getLayoutName()
+ {
+ return layoutName;
+ }
+
+ public void setLayoutName(String layoutName)
+ {
+ this.layoutName = layoutName;
+ }
+
+ /**
+ * Visits statements to find inflate method called on fragments
+ * to get the name of the view declared (this info will be used on findViewById,
+ * for fragments only)
+ */
+ @Override
+ public boolean visit(VariableDeclarationStatement node)
+ {
+ if (node.getType().toString().equals(VIEW))
+ {
+ VariableDeclarationFragment frag =
+ (VariableDeclarationFragment) node.fragments().get(0);
+ if (frag.getInitializer() instanceof MethodInvocation)
+ {
+ MethodInvocation invoke = (MethodInvocation) frag.getInitializer();
+ String invokeName = invoke.getName().toString();
+ if (invoke.getExpression() instanceof SimpleName)
+ {
+ if ((invokeName != null) && invokeName.equals(INFLATE))
+ {
+ inflatedViewName = frag.getName().getFullyQualifiedName();
+ }
+ }
+ }
+ }
+ return super.visit(node);
+ }
+
+ /**
+ * Visit method invocations to find layout name set on activity/fragment
+ */
+ @Override
+ public boolean visit(MethodInvocation node)
+ {
+ //Fill invoked method model.
+ MethodInvocation invoked = node;
+ IMethodBinding methodBinding = invoked.resolveMethodBinding();
+ if (methodBinding != null)
+ {
+ String methodSimpleName = methodBinding.getName();
+ //Retrieve parameter types and look for R constants used within method arguments
+ List<?> arguments = invoked.arguments();
+ for (Object argument : arguments)
+ {
+ Expression argumentExpression = (Expression) argument;
+ if (argumentExpression instanceof QualifiedName) /*Can be a constant access*/
+ {
+ QualifiedName qualifiedName = (QualifiedName) argumentExpression;
+ String layoutName = qualifiedName.getName().getIdentifier();
+ if (methodSimpleName != null)
+ {
+ if (methodSimpleName.equals(ACTIVITY_METHOD_TO_SET_LAYOUT))
+ {
+ typeAssociatedToLayout = CodeGeneratorDataBasedOnLayout.TYPE.ACTIVITY;
+ setLayoutName(layoutName);
+ }
+ else if (methodSimpleName.equals(FRAGMENT_METHOD_TO_SET_LAYOUT))
+ {
+ typeAssociatedToLayout = CodeGeneratorDataBasedOnLayout.TYPE.FRAGMENT;
+ setLayoutName(layoutName);
+ checkInflatedViewNameOnFields(node);
+ }
+ else if (methodSimpleName
+ .equals(JavaViewBasedOnLayoutModifierConstants.FIND_VIEW_BY_ID)
+ || methodSimpleName
+ .equals(JavaViewBasedOnLayoutModifierConstants.FIND_FRAGMENT_BY_ID)) //$NON-NLS-1$
+ {
+ //findViewById
+ String viewId = qualifiedName.getName().toString();
+ getDeclaredViewIds().add(viewId);
+ }
+
+ }
+ }
+ }
+ }
+ return super.visit(node);
+ }
+
+ private void checkInflatedViewNameOnFields(MethodInvocation node)
+ {
+ //check if this method invocation is binded to an assignment
+ ASTNode nodeParent = node.getParent();
+ while ((nodeParent != null) && (inflatedViewName == null))
+ {
+ if (nodeParent instanceof Assignment)
+ {
+ Assignment assignment = (Assignment) nodeParent;
+ Expression lhs = assignment.getLeftHandSide();
+ ITypeBinding binding = lhs.resolveTypeBinding();
+ IJavaElement javaElement = binding.getJavaElement();
+ if ((javaElement != null) && (lhs instanceof SimpleName))
+ {
+ IType type = (IType) javaElement.getAdapter(IType.class);
+ if (type != null)
+ {
+ try
+ {
+ if (JDTUtils.isSubclass(type, "android.view.View"))
+ {
+ inflatedViewName = ((SimpleName) lhs).getFullyQualifiedName();
+ }
+ }
+ catch (JavaModelException e)
+ {
+ // do nothing
+ }
+ }
+
+ }
+ }
+ nodeParent = nodeParent.getParent();
+ }
+
+ }
+
+ /**
+ * @return the typeAssociatedToLayout
+ */
+ public CodeGeneratorDataBasedOnLayout.TYPE getTypeAssociatedToLayout()
+ {
+ return typeAssociatedToLayout;
+ }
+
+ /**
+ * @return the inflatedViewName
+ */
+ public String getInflatedViewName()
+ {
+ return inflatedViewName;
+ }
+
+ /**
+ * @param inflatedViewName the inflatedViewName to set
+ */
+ public void setInflatedViewName(String inflatedViewName)
+ {
+ this.inflatedViewName = inflatedViewName;
+ }
+
+ /**
+ * @return the declaredLayoutIds
+ */
+ public synchronized Set<String> getDeclaredViewIds()
+ {
+ return declaredViewIds;
+ }
+}
diff --git a/src/plugins/android.codeutils/src/com/motorola/studio/android/generateviewbylayout/SaveStateVisitor.java b/src/plugins/android.codeutils/src/com/motorola/studio/android/generateviewbylayout/SaveStateVisitor.java
new file mode 100644
index 0000000..ae876cd
--- /dev/null
+++ b/src/plugins/android.codeutils/src/com/motorola/studio/android/generateviewbylayout/SaveStateVisitor.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.generateviewbylayout;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import org.eclipse.jdt.core.IJavaElement;
+import org.eclipse.jdt.core.IType;
+import org.eclipse.jdt.core.JavaModelException;
+import org.eclipse.jdt.core.dom.ASTVisitor;
+import org.eclipse.jdt.core.dom.Expression;
+import org.eclipse.jdt.core.dom.ITypeBinding;
+import org.eclipse.jdt.core.dom.MethodInvocation;
+import org.eclipse.jdt.core.dom.SimpleName;
+
+import com.motorola.studio.android.codeutils.CodeUtilsActivator;
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.generatecode.JDTUtils;
+import com.motorola.studio.android.generateviewbylayout.codegenerators.SaveStateCodeGenerator;
+import com.motorola.studio.android.generateviewbylayout.model.LayoutNode.ViewProperties;
+
+/**
+ * Visitor responsible to identify already saved or restored views inside a View (generally Activity / Fragment)
+ */
+public class SaveStateVisitor extends ASTVisitor
+{
+ private final Set<String> viewIds = new HashSet<String>();
+
+ public Set<String> getViewIds()
+ {
+ return viewIds;
+ }
+
+ @Override
+ public boolean visit(MethodInvocation node)
+ {
+ boolean handled = false;
+ int i = 0;
+ while (!handled && (i < SaveStateCodeGenerator.saveStateNodeTypes.length))
+ {
+ if ((node.getName().toString().equals(SaveStateCodeGenerator.saveStateNodeTypes[i]
+ .getProperty(ViewProperties.ViewStateGetMethod))))
+ {
+ identifySavedView(node);
+ handled = true;
+ }
+ else if (node
+ .getName()
+ .toString()
+ .equals(SaveStateCodeGenerator.saveStateNodeTypes[i]
+ .getProperty(ViewProperties.ViewStateSetMethod)))
+ {
+ identifyRestoredView(node);
+ handled = true;
+ }
+ i++;
+ }
+
+ return super.visit(node);
+ }
+
+ /**
+ * Restored views are in form
+ * <variable>.<viewSetMethod>(preferences.<preferenceGetMethod>("property", <defaultValue>));
+ * @param node
+ */
+ private void identifyRestoredView(MethodInvocation node)
+ {
+ Expression expression = node.getExpression();
+
+ if (expression instanceof SimpleName)
+ {
+ ITypeBinding binding = ((SimpleName) expression).resolveTypeBinding();
+ IJavaElement javaElement = binding.getJavaElement();
+ if (javaElement != null)
+ {
+ try
+ {
+ IType type = (IType) javaElement.getAdapter(IType.class);
+ if (JDTUtils.isSubclass(type, "android.view.View"))
+ {
+ viewIds.add(((SimpleName) expression).getFullyQualifiedName());
+ }
+ }
+ catch (JavaModelException e)
+ {
+ StudioLogger.warn(CodeUtilsActivator.PLUGIN_ID, "Unable to identify if "
+ + binding.getName() + " is a subclass of android.view.View", e);
+ }
+ }
+ }
+ }
+
+ /**
+ * Saved views are in form
+ * editor.<propertySetMethod>("property", <variable>.<viewGetMethod>);
+ * @param node
+ */
+ private void identifySavedView(MethodInvocation node)
+ {
+ Expression expression = node.getExpression();
+
+ if (expression instanceof SimpleName)
+ {
+ ITypeBinding binding = ((SimpleName) expression).resolveTypeBinding();
+ IJavaElement javaElement = binding.getJavaElement();
+ if (javaElement != null)
+ {
+ try
+ {
+ IType type = (IType) javaElement.getAdapter(IType.class);
+ if (JDTUtils.isSubclass(type, "android.view.View"))
+ {
+ viewIds.add(((SimpleName) expression).getFullyQualifiedName());
+ }
+ }
+ catch (JavaModelException e)
+ {
+ StudioLogger.warn(CodeUtilsActivator.PLUGIN_ID, "Unable to identify if "
+ + binding.getName() + " is a subclass of android.view.View", e);
+ }
+ }
+ }
+ }
+}
diff --git a/src/plugins/android.codeutils/src/com/motorola/studio/android/generateviewbylayout/codegenerators/AbstractLayoutCodeGenerator.java b/src/plugins/android.codeutils/src/com/motorola/studio/android/generateviewbylayout/codegenerators/AbstractLayoutCodeGenerator.java
new file mode 100644
index 0000000..03273d9
--- /dev/null
+++ b/src/plugins/android.codeutils/src/com/motorola/studio/android/generateviewbylayout/codegenerators/AbstractLayoutCodeGenerator.java
@@ -0,0 +1,420 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.generateviewbylayout.codegenerators;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.jdt.core.JavaConventions;
+import org.eclipse.jdt.core.JavaModelException;
+import org.eclipse.jdt.core.dom.AnonymousClassDeclaration;
+import org.eclipse.jdt.core.dom.Assignment;
+import org.eclipse.jdt.core.dom.CastExpression;
+import org.eclipse.jdt.core.dom.ClassInstanceCreation;
+import org.eclipse.jdt.core.dom.Expression;
+import org.eclipse.jdt.core.dom.ExpressionStatement;
+import org.eclipse.jdt.core.dom.FieldAccess;
+import org.eclipse.jdt.core.dom.MethodDeclaration;
+import org.eclipse.jdt.core.dom.MethodInvocation;
+import org.eclipse.jdt.core.dom.QualifiedName;
+import org.eclipse.jdt.core.dom.SimpleName;
+import org.eclipse.jdt.core.dom.SimpleType;
+import org.eclipse.jdt.core.dom.Statement;
+import org.eclipse.jdt.core.dom.TypeDeclaration;
+import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
+import org.eclipse.jdt.core.dom.VariableDeclarationStatement;
+
+import com.motorola.studio.android.codeutils.i18n.CodeUtilsNLS;
+import com.motorola.studio.android.generatecode.AbstractCodeGenerator;
+import com.motorola.studio.android.generateviewbylayout.JavaViewBasedOnLayoutModifierConstants;
+import com.motorola.studio.android.generateviewbylayout.model.CodeGeneratorDataBasedOnLayout;
+import com.motorola.studio.android.generateviewbylayout.model.LayoutNode;
+
+/**
+ * Class that have common methods to generate code based on layout (for GUI handlers, findViewById, attributes)
+ */
+public abstract class AbstractLayoutCodeGenerator extends AbstractCodeGenerator
+{
+ protected CodeGeneratorDataBasedOnLayout codeGeneratorData;
+
+ protected MethodDeclaration onCreateDeclaration;
+
+ /**
+ * @param codeGeneratorData input where to get the information (generally a xml, for example, it could be menu or layout)
+ * @param onCreateDeclaration method onCreate (for Activity) or onCreateView (for Fragment)
+ * @param typeDeclaration AST type representing Activity or Fragment Android class
+ */
+ public AbstractLayoutCodeGenerator(CodeGeneratorDataBasedOnLayout codeGeneratorData,
+ MethodDeclaration onCreateDeclaration, TypeDeclaration typeDeclaration)
+ {
+ super(typeDeclaration);
+ this.codeGeneratorData = codeGeneratorData;
+ this.onCreateDeclaration = onCreateDeclaration;
+ }
+
+ /**
+ * @return the codeGeneratorData
+ */
+ protected CodeGeneratorDataBasedOnLayout getCodeGeneratorData()
+ {
+ return codeGeneratorData;
+ }
+
+ /**
+ * Adds method invocation that instantiates an anonymous class to deal with the event
+ */
+ protected void addMethodInvocationToListenerHandler(String callerId, String invocationMethod,
+ String classType, String listenerClazzName, MethodDeclaration methodDeclaration)
+ throws JavaModelException
+ {
+ List<MethodDeclaration> declarations = new ArrayList<MethodDeclaration>();
+ declarations.add(methodDeclaration);
+ addMethodInvocationToListenerHandler(callerId, invocationMethod, classType,
+ listenerClazzName, declarations);
+ }
+
+ /**
+ * Adds method invocation that instantiates an anonymous class to deal with the event
+ */
+ @SuppressWarnings("unchecked")
+ protected void addMethodInvocationToListenerHandler(String callerId, String invocationMethod,
+ String classType, String listenerClazzName, List<MethodDeclaration> methodDeclarations)
+ throws JavaModelException
+ {
+ MethodInvocation methodInvocation = onCreateDeclaration.getAST().newMethodInvocation();
+ SimpleName listenerInvocationName =
+ onCreateDeclaration.getAST().newSimpleName(invocationMethod);
+ SimpleName listenerOptionalName = onCreateDeclaration.getAST().newSimpleName(callerId);
+ FieldAccess fieldAccess = onCreateDeclaration.getAST().newFieldAccess();
+ fieldAccess.setExpression(onCreateDeclaration.getAST().newThisExpression());
+ fieldAccess.setName(listenerOptionalName);
+
+ methodInvocation.setName(listenerInvocationName);
+ methodInvocation.setExpression(fieldAccess);
+
+ ClassInstanceCreation classInstanceCreation =
+ onCreateDeclaration.getAST().newClassInstanceCreation();
+ SimpleType listenerType = getListenerSimpleType(classType, listenerClazzName);
+ classInstanceCreation.setType(listenerType);
+
+ AnonymousClassDeclaration classDeclaration =
+ onCreateDeclaration.getAST().newAnonymousClassDeclaration();
+
+ for (MethodDeclaration methodDeclaration : methodDeclarations)
+ {
+ classDeclaration.bodyDeclarations().add(methodDeclaration);
+ }
+ classInstanceCreation.setAnonymousClassDeclaration(classDeclaration);
+
+ methodInvocation.arguments().add(classInstanceCreation);
+
+ ExpressionStatement expressionStatement =
+ onCreateDeclaration.getAST().newExpressionStatement(methodInvocation);
+ insertStatementsAtOnCreateDeclaration(expressionStatement, false);
+ }
+
+ /**
+ * Insert statements on create declaration depending if it is activity / fragment
+ * @param expr
+ * @param insertInTheBeginningOfMethod true, if must insert after setContentView (activity) or after inflate (fragment);
+ * false if must insert in the of the method (activity), last statement before return (fragment)
+ * @throws JavaModelException if fragment can not be modified because it is not in the format appropriate
+ */
+ @SuppressWarnings("unchecked")
+ protected void insertStatementsAtOnCreateDeclaration(Statement expr,
+ boolean insertInTheBeginningOfMethod) throws JavaModelException
+ {
+ int size = onCreateDeclaration.getBody().statements().size();
+ if (getCodeGeneratorData().getAssociatedType().equals(
+ CodeGeneratorDataBasedOnLayout.TYPE.ACTIVITY))
+ {
+ if (insertInTheBeginningOfMethod)
+ {
+ int foundIndex = findSetContentViewIndexInsideStatement();
+ //if activity => add after second statement (after setContentView)
+ if (foundIndex >= 0)
+ {
+ //it should have super.onCreate and setContentView => add after them
+ onCreateDeclaration.getBody().statements().add(foundIndex + 1, expr);
+ }
+ }
+ else
+ {
+ //last statement
+ onCreateDeclaration.getBody().statements().add(size, expr);
+ }
+ }
+ else if (getCodeGeneratorData().getAssociatedType().equals(
+ CodeGeneratorDataBasedOnLayout.TYPE.FRAGMENT))
+ {
+ if (size <= 1)
+ {
+ throw new JavaModelException(new IllegalArgumentException(
+ CodeUtilsNLS.MethodVisitor_InvalidFormatForFragmentOnCreateView),
+ IStatus.ERROR);
+ }
+ if (insertInTheBeginningOfMethod)
+ {
+ if (size >= 2)
+ {
+ int foundIndex = findInflateIndexAtStatement();
+ if (foundIndex >= 0)
+ {
+ //if fragment => add after first statement (after inflater.inflate)
+ //it should have inflater.inflate and return statement => add between them
+ onCreateDeclaration.getBody().statements().add(foundIndex + 1, expr);
+ }
+ }
+ }
+ else
+ {
+ //last statement before return
+ onCreateDeclaration.getBody().statements().add(size - 1, expr);
+ }
+ }
+ }
+
+ /**
+ * Returns the last statement of inflate inside onCreate from Fragment
+ * @return -1 if inflate not found, value >=0 if statement found
+ */
+ private int findInflateIndexAtStatement()
+ {
+ int foundIndex = -1;
+ int index = 0;
+ while (index < onCreateDeclaration.getBody().statements().size())
+ {
+ Object st = onCreateDeclaration.getBody().statements().get(index);
+ Expression expression = null;
+ if (st instanceof VariableDeclarationStatement)
+ {
+ VariableDeclarationStatement variableDeclarationStatement =
+ (VariableDeclarationStatement) st;
+ for (Object f : variableDeclarationStatement.fragments())
+ {
+ VariableDeclarationFragment frag = (VariableDeclarationFragment) f;
+ expression = frag.getInitializer();
+ }
+ }
+ else if (st instanceof ExpressionStatement)
+ {
+ ExpressionStatement expressionStatement = (ExpressionStatement) st;
+ if (expressionStatement.getExpression() instanceof Assignment)
+ {
+ Assignment assignment = (Assignment) expressionStatement.getExpression();
+ expression = assignment.getRightHandSide();
+ }
+ }
+ if (expression != null)
+ {
+ int aux = findInflateIndexAtStatement(index, expression);
+ if (aux >= 0)
+ {
+ foundIndex = aux;
+ }
+ }
+ index++;
+ }
+ return foundIndex;
+ }
+
+ /**
+ * Returns the last statement of setContentView inside onCreate from Activity
+ * @return -1 if setContentView not found, value >=0 if statement found
+ */
+ private int findSetContentViewIndexInsideStatement()
+ {
+ int foundIndex = -1;
+ int index = 0;
+ while (index < onCreateDeclaration.getBody().statements().size())
+ {
+ Object st = onCreateDeclaration.getBody().statements().get(index);
+ if (st instanceof ExpressionStatement)
+ {
+ ExpressionStatement expressionStatement = (ExpressionStatement) st;
+ if (expressionStatement.getExpression() instanceof MethodInvocation)
+ {
+ MethodInvocation setContentInvocation =
+ (MethodInvocation) expressionStatement.getExpression();
+ if ((setContentInvocation.getName() != null)
+ && setContentInvocation.getName().getIdentifier()
+ .equals("setContentView"))
+ {
+ foundIndex = index;
+ }
+ }
+ }
+ index++;
+ }
+ return foundIndex;
+ }
+
+ /**
+ * Checks if the method is already invoked in the body of onCreate method
+ * @param node
+ * @return
+ */
+ public boolean checkIfInvokeMethodIsDeclared(LayoutNode node, String method)
+ {
+ boolean containMethodDeclared = false;
+ if (onCreateDeclaration.getBody() != null)
+ {
+ //check if method invocation already declared
+ for (Object bodystat : onCreateDeclaration.getBody().statements())
+ {
+ if (bodystat instanceof ExpressionStatement)
+ {
+ ExpressionStatement exprStatement = (ExpressionStatement) bodystat;
+ if (exprStatement.getExpression() instanceof MethodInvocation)
+ {
+ MethodInvocation invoke = (MethodInvocation) exprStatement.getExpression();
+ if ((invoke.getName() != null)
+ && invoke.getName().toString().equals(method))
+ {
+ if ((invoke.getExpression() != null)
+ && invoke.getExpression().toString()
+ .equals(THIZ + node.getNodeId()))
+ {
+ containMethodDeclared = true;
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ return containMethodDeclared;
+ }
+
+ /**
+ * @param node
+ * @return true if there is one findViewById with the give node.getNodeId(),
+ * false otherwise
+ */
+ public boolean checkIfFindViewByIdAlreadyDeclared(LayoutNode node)
+ {
+ return codeGeneratorData.getJavaLayoutData().getDeclaredViewIdsOnCode()
+ .contains(node.getNodeId());
+ }
+
+ /**
+ * Creates an assigment statement. The format follows the example:
+ *
+ * <br><br>
+ * <code>Button b = (Button) v.findViewById($nodeId);</code>
+ *
+ *
+ * @param node
+ * @param optionalExpression if invocation was nested (e.g.: getFragmentManager())
+ * @param methodToBeCalled
+ * @throws JavaModelException
+ */
+ @SuppressWarnings("unchecked")
+ public void addAssignmentStatement(LayoutNode node, Expression optionalExpression,
+ String methodToBeCalled) throws JavaModelException
+ {
+ SimpleName guiName;
+ try
+ {
+ guiName = getNodeVariableTypeBasedOnLayoutNode(node);
+ }
+ catch (CoreException e)
+ {
+ throw new JavaModelException(e);
+ }
+ SimpleType guiType = onCreateDeclaration.getAST().newSimpleType(guiName);
+
+ SimpleName method = onCreateDeclaration.getAST().newSimpleName(methodToBeCalled);
+ SimpleName rId1 =
+ onCreateDeclaration.getAST()
+ .newSimpleName(JavaViewBasedOnLayoutModifierConstants.R);
+ SimpleName rId2 =
+ onCreateDeclaration.getAST().newSimpleName(
+ JavaViewBasedOnLayoutModifierConstants.ID);
+ QualifiedName rQualified1 = onCreateDeclaration.getAST().newQualifiedName(rId1, rId2);
+
+ SimpleName guiId = onCreateDeclaration.getAST().newSimpleName(node.getNodeId());
+ QualifiedName rQualified2 =
+ onCreateDeclaration.getAST().newQualifiedName(rQualified1, guiId);
+
+ MethodInvocation invocation = onCreateDeclaration.getAST().newMethodInvocation();
+ invocation.setName(method);
+ if (optionalExpression != null)
+ {
+ invocation.setExpression(optionalExpression);
+ }
+ if (getCodeGeneratorData().getAssociatedType().equals(
+ CodeGeneratorDataBasedOnLayout.TYPE.FRAGMENT))
+ {
+ if (!node.isFragmentPlaceholder())
+ {
+ invocation.setExpression(onCreateDeclaration.getAST().newSimpleName(
+ getCodeGeneratorData().getJavaLayoutData().getInflatedViewName()));
+ }
+ }
+ invocation.arguments().add(rQualified2);
+
+ CastExpression castExpr = onCreateDeclaration.getAST().newCastExpression();
+ castExpr.setExpression(invocation);
+ castExpr.setType(guiType);
+
+ Assignment assign = onCreateDeclaration.getAST().newAssignment();
+ SimpleName variableId = onCreateDeclaration.getAST().newSimpleName(node.getNodeId());
+
+ FieldAccess fieldAccess = onCreateDeclaration.getAST().newFieldAccess();
+ fieldAccess.setExpression(onCreateDeclaration.getAST().newThisExpression());
+ fieldAccess.setName(variableId);
+
+ assign.setLeftHandSide(fieldAccess);
+ assign.setOperator(Assignment.Operator.ASSIGN);
+ assign.setRightHandSide(castExpr);
+
+ ExpressionStatement expr = onCreateDeclaration.getAST().newExpressionStatement(assign);
+ insertStatementsAtOnCreateDeclaration(expr, true);
+ }
+
+ /**
+ * Gets the name of the variable based on the type declared in the layout xml
+ * @param node
+ * @return
+ * @throws CoreException
+ */
+ public SimpleName getNodeVariableTypeBasedOnLayoutNode(LayoutNode node) throws CoreException
+ {
+ SimpleName guiName = null;
+ String clazzName = node.getClazzName();
+ if (node.isFragmentPlaceholder() && (clazzName != null))
+ {
+ //use type defined in the xml
+ IStatus nameStatus = JavaConventions.validateIdentifier(clazzName, "5", "5");
+ if (nameStatus.isOK())
+ {
+ guiName = onCreateDeclaration.getAST().newSimpleName(clazzName);
+ }
+ else
+ {
+ throw new CoreException(nameStatus);
+ }
+ }
+ else
+ {
+ guiName = onCreateDeclaration.getAST().newSimpleName(node.getNodeType());
+ }
+ return guiName;
+ }
+}
diff --git a/src/plugins/android.codeutils/src/com/motorola/studio/android/generateviewbylayout/codegenerators/ClassAttributesCodeGenerator.java b/src/plugins/android.codeutils/src/com/motorola/studio/android/generateviewbylayout/codegenerators/ClassAttributesCodeGenerator.java
new file mode 100644
index 0000000..9765308
--- /dev/null
+++ b/src/plugins/android.codeutils/src/com/motorola/studio/android/generateviewbylayout/codegenerators/ClassAttributesCodeGenerator.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.generateviewbylayout.codegenerators;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.SubMonitor;
+import org.eclipse.jdt.core.JavaModelException;
+import org.eclipse.jdt.core.dom.FieldDeclaration;
+import org.eclipse.jdt.core.dom.MethodDeclaration;
+import org.eclipse.jdt.core.dom.Modifier;
+import org.eclipse.jdt.core.dom.Modifier.ModifierKeyword;
+import org.eclipse.jdt.core.dom.SimpleName;
+import org.eclipse.jdt.core.dom.SimpleType;
+import org.eclipse.jdt.core.dom.TypeDeclaration;
+import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
+
+import com.motorola.studio.android.codeutils.i18n.CodeUtilsNLS;
+import com.motorola.studio.android.generateviewbylayout.model.CodeGeneratorDataBasedOnLayout;
+import com.motorola.studio.android.generateviewbylayout.model.LayoutNode;
+
+/**
+ * Responsible to generate class attributes based on layout type
+ */
+public class ClassAttributesCodeGenerator extends AbstractLayoutCodeGenerator
+{
+ /**
+ * @param codeGeneratorData
+ * @param onCreateDeclaration
+ * @param typeDeclaration
+ */
+ public ClassAttributesCodeGenerator(CodeGeneratorDataBasedOnLayout codeGeneratorData,
+ MethodDeclaration onCreateDeclaration, TypeDeclaration typeDeclaration)
+ {
+ super(codeGeneratorData, onCreateDeclaration, typeDeclaration);
+ }
+
+ @Override
+ public void generateCode(IProgressMonitor monitor) throws JavaModelException
+ {
+ addAttributes(monitor);
+ }
+
+ /**
+ * Add attributes based on the GUI items declared on layout XML
+ * <br>
+ * GENERATED_CODE_FORMAT:
+ * <br>
+ * private $GUI_TYPE $GUI_ID;
+ * @throws JavaModelException
+ */
+ @SuppressWarnings("unchecked")
+ private void addAttributes(IProgressMonitor monitor) throws JavaModelException
+ {
+ SubMonitor subMonitor = SubMonitor.convert(monitor);
+ subMonitor.beginTask(CodeUtilsNLS.JavaViewBasedOnLayoutModifier_AddingAttributes,
+ codeGeneratorData.getGuiItems().size());
+ /*
+ * AST to be written
+ * FieldDeclaration:
+ * [Javadoc] { ExtendedModifier } Type VariableDeclarationFragment
+ * { , VariableDeclarationFragment } ;
+ */
+ for (LayoutNode node : codeGeneratorData.getGuiItems())
+ {
+ if ((node.getNodeId() != null) && node.shouldInsertCode())
+ {
+ boolean containFieldDeclared =
+ getCodeGeneratorData().getJavaLayoutData().getVisitor()
+ .checkIfAttributeAlreadyDeclared(node, false);
+ if (!containFieldDeclared)
+ {
+ //avoid to declare attribute twice
+ Modifier privateMod =
+ typeDeclaration.getAST().newModifier(ModifierKeyword.PRIVATE_KEYWORD);
+ SimpleName guiName;
+ try
+ {
+ guiName = getNodeVariableTypeBasedOnLayoutNode(node);
+ }
+ catch (CoreException e)
+ {
+ throw new JavaModelException(e);
+ }
+ SimpleType guiType = typeDeclaration.getAST().newSimpleType(guiName);
+ VariableDeclarationFragment variableFragment =
+ typeDeclaration.getAST().newVariableDeclarationFragment();
+ SimpleName varName = variableFragment.getAST().newSimpleName(node.getNodeId());
+ variableFragment.setName(varName);
+ FieldDeclaration declaration =
+ typeDeclaration.getAST().newFieldDeclaration(variableFragment);
+ declaration.modifiers().add(privateMod);
+ declaration.setType(guiType);
+ typeDeclaration.bodyDeclarations().add(0, declaration); //add as the first item
+ }
+ }
+ subMonitor.worked(1);
+ }
+ }
+
+}
diff --git a/src/plugins/android.codeutils/src/com/motorola/studio/android/generateviewbylayout/codegenerators/EditTextCodeGenerator.java b/src/plugins/android.codeutils/src/com/motorola/studio/android/generateviewbylayout/codegenerators/EditTextCodeGenerator.java
new file mode 100644
index 0000000..e3ebfec
--- /dev/null
+++ b/src/plugins/android.codeutils/src/com/motorola/studio/android/generateviewbylayout/codegenerators/EditTextCodeGenerator.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.generateviewbylayout.codegenerators;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.SubMonitor;
+import org.eclipse.jdt.core.JavaModelException;
+import org.eclipse.jdt.core.dom.Block;
+import org.eclipse.jdt.core.dom.MethodDeclaration;
+import org.eclipse.jdt.core.dom.Modifier.ModifierKeyword;
+import org.eclipse.jdt.core.dom.PrimitiveType;
+import org.eclipse.jdt.core.dom.ReturnStatement;
+import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
+import org.eclipse.jdt.core.dom.TypeDeclaration;
+
+import com.motorola.studio.android.codeutils.i18n.CodeUtilsNLS;
+import com.motorola.studio.android.generateviewbylayout.JavaViewBasedOnLayoutModifierConstants;
+import com.motorola.studio.android.generateviewbylayout.model.CodeGeneratorDataBasedOnLayout;
+import com.motorola.studio.android.generateviewbylayout.model.LayoutNode;
+
+/**
+ * Responsible to add methods to deal with EditText events
+ */
+public class EditTextCodeGenerator extends AbstractLayoutCodeGenerator
+{
+ /**
+ * @param codeGeneratorData
+ * @param onCreateDeclaration
+ * @param typeDeclaration
+ */
+ public EditTextCodeGenerator(CodeGeneratorDataBasedOnLayout codeGeneratorData,
+ MethodDeclaration onCreateDeclaration, TypeDeclaration typeDeclaration)
+ {
+ super(codeGeneratorData, onCreateDeclaration, typeDeclaration);
+ }
+
+ @Override
+ public void generateCode(IProgressMonitor monitor) throws JavaModelException
+ {
+ addOnKeyHandler(monitor);
+ }
+
+ /**
+ * Add methods to deal with key handler (EditText's event)
+ *
+ * <br>
+ * GENERATED_CODE_FORMAT:
+ * <br>
+ * $GUI_ID.setOnKeyListener(new View.OnKeyListener() {
+ * <br>
+ * public boolean onKey(View target, int keyCode, KeyEvent event) {
+ * <br>
+ * return true;
+ * <br>
+ * }
+ * <br>
+ * });
+ */
+ @SuppressWarnings("unchecked")
+ private void addOnKeyHandler(IProgressMonitor monitor) throws JavaModelException
+ {
+ SubMonitor subMonitor = SubMonitor.convert(monitor);
+ subMonitor.beginTask(CodeUtilsNLS.JavaViewBasedOnLayoutModifier_AddingOnKeyHandler,
+ codeGeneratorData.getGuiItems().size());
+ /*
+ * AST to be written:
+ *
+ * MethodInvocations
+ String methodName = SimpleName("setOnKeyListener")
+ String optionalExpression = SimpleName(guiId);
+ arguments=ClassInstanceCreation
+ AnonymousClassDeclaration
+ var body=MethodDeclaration();
+ modifier=public
+ methodName = SimpleName("onKey");
+ List<SingleVariableDeclaration>
+ List<String> types converter p/ SimpleType (pode ser PrimitiveType)
+ List<String> names converter p/ SimpleName
+ ReturnStatement optionalExpression BooleanLiteral
+ type=SimpleType("listenerName")
+ */
+ for (LayoutNode node : codeGeneratorData.getGuiItems())
+ {
+ if (node.shouldInsertCode()
+ && node.getNodeType().equals(LayoutNode.LayoutNodeViewType.EditText.name()))
+ {
+ boolean containMethodDeclared =
+ checkIfInvokeMethodIsDeclared(node,
+ JavaViewBasedOnLayoutModifierConstants.SET_ON_KEY_LISTENER);
+ if (!containMethodDeclared)
+ {
+ List<SingleVariableDeclaration> parameters =
+ new ArrayList<SingleVariableDeclaration>();
+ SingleVariableDeclaration param1 =
+ createVariableDeclarationFromStrings(
+ JavaViewBasedOnLayoutModifierConstants.VIEW_CLASS,
+ JavaViewBasedOnLayoutModifierConstants.VIEW_VARIABLE_NAME);
+ parameters.add(param1);
+ SingleVariableDeclaration param2 =
+ createVariableDeclarationPrimitiveCode(PrimitiveType.INT,
+ JavaViewBasedOnLayoutModifierConstants.KEY_CODE); //$NON-NLS-1$
+ parameters.add(param2);
+ SingleVariableDeclaration param3 =
+ createVariableDeclarationFromStrings(
+ JavaViewBasedOnLayoutModifierConstants.KEY_EVENT,
+ JavaViewBasedOnLayoutModifierConstants.EVENT); //$NON-NLS-1$ //$NON-NLS-2$
+ parameters.add(param3);
+ MethodDeclaration methodDeclaration =
+ addMethodDeclaration(ModifierKeyword.PUBLIC_KEYWORD,
+ JavaViewBasedOnLayoutModifierConstants.ON_KEY, //$NON-NLS-1$
+ PrimitiveType.BOOLEAN, parameters);
+ Block block = onCreateDeclaration.getAST().newBlock();
+ methodDeclaration.setBody(block);
+ ReturnStatement statement = onCreateDeclaration.getAST().newReturnStatement();
+ statement.setExpression(onCreateDeclaration.getAST().newBooleanLiteral(true));
+ methodDeclaration.getBody().statements().add(statement);
+ addMethodInvocationToListenerHandler(
+ node.getNodeId(),
+ JavaViewBasedOnLayoutModifierConstants.SET_ON_KEY_LISTENER,
+ JavaViewBasedOnLayoutModifierConstants.VIEW_CLASS, //$NON-NLS-1$
+ JavaViewBasedOnLayoutModifierConstants.ON_KEY_LISTENER,
+ methodDeclaration); //$NON-NLS-1$
+ }
+ }
+ subMonitor.worked(1);
+ }
+ }
+}
diff --git a/src/plugins/android.codeutils/src/com/motorola/studio/android/generateviewbylayout/codegenerators/FindViewByIdCodeGenerator.java b/src/plugins/android.codeutils/src/com/motorola/studio/android/generateviewbylayout/codegenerators/FindViewByIdCodeGenerator.java
new file mode 100644
index 0000000..f48ebf2
--- /dev/null
+++ b/src/plugins/android.codeutils/src/com/motorola/studio/android/generateviewbylayout/codegenerators/FindViewByIdCodeGenerator.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.generateviewbylayout.codegenerators;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.SubMonitor;
+import org.eclipse.jdt.core.JavaModelException;
+import org.eclipse.jdt.core.dom.MethodDeclaration;
+import org.eclipse.jdt.core.dom.MethodInvocation;
+import org.eclipse.jdt.core.dom.SimpleName;
+import org.eclipse.jdt.core.dom.TypeDeclaration;
+
+import com.motorola.studio.android.codeutils.i18n.CodeUtilsNLS;
+import com.motorola.studio.android.common.IAndroidConstants;
+import com.motorola.studio.android.common.utilities.AndroidUtils;
+import com.motorola.studio.android.generatecode.JDTUtils;
+import com.motorola.studio.android.generateviewbylayout.JavaViewBasedOnLayoutModifierConstants;
+import com.motorola.studio.android.generateviewbylayout.model.CodeGeneratorDataBasedOnLayout;
+import com.motorola.studio.android.generateviewbylayout.model.LayoutNode;
+
+/**
+ * Responsible to create findViewById statements
+ */
+public class FindViewByIdCodeGenerator extends AbstractLayoutCodeGenerator
+{
+
+ protected IFile javaFile;
+
+ /**
+ * @param codeGeneratorData
+ * @param onCreateDeclaration
+ * @param typeDeclaration
+ * @param javaFile file used to get API version number (it is necessary to differentiate between Activity or Fragment in the findViewById method invocation)
+ */
+ public FindViewByIdCodeGenerator(CodeGeneratorDataBasedOnLayout codeGeneratorData,
+ MethodDeclaration onCreateDeclaration, TypeDeclaration typeDeclaration, IFile javaFile)
+ {
+ super(codeGeneratorData, onCreateDeclaration, typeDeclaration);
+ this.javaFile = javaFile;
+ }
+
+ @Override
+ public void generateCode(IProgressMonitor monitor) throws JavaModelException
+ {
+ addFindByIdStatement(monitor);
+ }
+
+ /**
+ * Field Assigment to find item inside layout xml
+ *
+ * <br>
+ * GENERATED_CODE_FORMAT:
+ * <br>
+ * $GUI_ID = $GUI_TYPE findViewById(R.id.$GUI_ID);
+ */
+ private void addFindByIdStatement(IProgressMonitor monitor) throws JavaModelException
+ {
+ SubMonitor subMonitor = SubMonitor.convert(monitor);
+ subMonitor.beginTask(CodeUtilsNLS.JavaViewBasedOnLayoutModifier_AddingFindingViewById,
+ codeGeneratorData.getGuiItems().size());
+ /* AST To be written
+ * ExpressionStatement
+ Assignment
+ leftHand SimpleName(button1)
+ Assignment.Operator =
+ rightHand CastExpression
+ expression MethodInvocation
+ arguments = QualifiedName(R.id.button1)
+ SimpleName = findViewById
+ type
+ SimpleType (Button)
+ */
+
+ for (LayoutNode node : codeGeneratorData.getGuiItems())
+ {
+ if ((node.getNodeId() != null) && node.shouldInsertCode())
+ {
+ boolean containsFindViewByIdDeclaration = checkIfFindViewByIdAlreadyDeclared(node);
+ if (!containsFindViewByIdDeclaration)
+ {
+ //avoid to call method twice
+ if (!node.isFragmentPlaceholder())
+ {
+ //not fragment
+ addAssignmentStatement(node, null,
+ JavaViewBasedOnLayoutModifierConstants.FIND_VIEW_BY_ID);
+ }
+ else
+ {
+ //is fragment placeholder
+ MethodInvocation optionalInvocation =
+ onCreateDeclaration.getAST().newMethodInvocation();
+ String methodName = null;
+ try
+ {
+ if ((AndroidUtils.getApiVersionNumberForProject(javaFile.getProject()) < 11)
+ && JDTUtils.isCompatibilityFragmentActivitySubclass(javaFile))
+ {
+ //if compatibility package (before 3.0)
+ methodName =
+ JavaViewBasedOnLayoutModifierConstants.GET_SUPPORT_FRAGMENT_MANAGER;
+ }
+ else if ((AndroidUtils.getApiVersionNumberForProject(javaFile
+ .getProject()) < IAndroidConstants.API_LEVEL_FOR_PLATFORM_VERSION_3_0_0)
+ && !JDTUtils.isCompatibilityFragmentActivitySubclass(javaFile))
+ {
+ //if compatibility package (before 3.0)
+ //but user is trying to use compatibility package with Activity (instead of FragmentActivity)
+ throw new JavaModelException(
+ new IllegalArgumentException(
+ CodeUtilsNLS.FindViewByIdCodeGenerator_CompatibilityModeClassNeedToExtendFragmentActivityError),
+ IStatus.ERROR);
+ }
+ else if ((AndroidUtils.getApiVersionNumberForProject(javaFile
+ .getProject()) >= IAndroidConstants.API_LEVEL_FOR_PLATFORM_VERSION_3_0_0))
+ {
+ //if after 3.0 (API level >=11)
+ methodName =
+ JavaViewBasedOnLayoutModifierConstants.GET_FRAGMENT_MANAGER;
+ }
+ if (methodName != null)
+ {
+ SimpleName managerMethod =
+ onCreateDeclaration.getAST().newSimpleName(methodName);
+ optionalInvocation.setName(managerMethod);
+ addAssignmentStatement(node, optionalInvocation,
+ JavaViewBasedOnLayoutModifierConstants.FIND_FRAGMENT_BY_ID);
+ }
+ }
+ catch (Exception e)
+ {
+ throw new JavaModelException(e, IStatus.ERROR);
+ }
+
+ }
+ }
+ }
+ subMonitor.worked(1);
+ }
+ }
+}
diff --git a/src/plugins/android.codeutils/src/com/motorola/studio/android/generateviewbylayout/codegenerators/GalleryCodeGenerator.java b/src/plugins/android.codeutils/src/com/motorola/studio/android/generateviewbylayout/codegenerators/GalleryCodeGenerator.java
new file mode 100644
index 0000000..5c2b4b5
--- /dev/null
+++ b/src/plugins/android.codeutils/src/com/motorola/studio/android/generateviewbylayout/codegenerators/GalleryCodeGenerator.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.generateviewbylayout.codegenerators;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.SubMonitor;
+import org.eclipse.jdt.core.JavaModelException;
+import org.eclipse.jdt.core.dom.Block;
+import org.eclipse.jdt.core.dom.MethodDeclaration;
+import org.eclipse.jdt.core.dom.Modifier.ModifierKeyword;
+import org.eclipse.jdt.core.dom.PrimitiveType;
+import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
+import org.eclipse.jdt.core.dom.TypeDeclaration;
+
+import com.motorola.studio.android.codeutils.i18n.CodeUtilsNLS;
+import com.motorola.studio.android.generateviewbylayout.JavaViewBasedOnLayoutModifierConstants;
+import com.motorola.studio.android.generateviewbylayout.model.CodeGeneratorDataBasedOnLayout;
+import com.motorola.studio.android.generateviewbylayout.model.LayoutNode;
+
+/**
+ * Responsible to add methods to deal with Gallery events
+ */
+public class GalleryCodeGenerator extends AbstractLayoutCodeGenerator
+{
+
+ /**
+ * @param codeGeneratorData
+ * @param onCreateDeclaration
+ * @param typeDeclaration
+ */
+ public GalleryCodeGenerator(CodeGeneratorDataBasedOnLayout codeGeneratorData,
+ MethodDeclaration onCreateDeclaration, TypeDeclaration typeDeclaration)
+ {
+ super(codeGeneratorData, onCreateDeclaration, typeDeclaration);
+ }
+
+ @Override
+ public void generateCode(IProgressMonitor monitor) throws JavaModelException
+ {
+ addOnItemClickListenerGalleryHandler(monitor);
+ }
+
+ /**
+ * Adds methods to deal with Gallery events
+ *
+ * <br>
+ * GENERATED_CODE_FORMAT:
+ * <br>
+ * $GUI_ID.setOnItemClickListener(new OnItemClickListener() {
+ * <br>
+ * public void onItemClick(AdapterView parent, View v, int position, long id) {
+ * <br>
+ * }
+ * <br>
+ * });
+ */
+ private void addOnItemClickListenerGalleryHandler(IProgressMonitor monitor)
+ throws JavaModelException
+ {
+ SubMonitor subMonitor = SubMonitor.convert(monitor);
+ subMonitor.beginTask(CodeUtilsNLS.JavaViewBasedOnLayoutModifier_AddingGalleryHandler,
+ codeGeneratorData.getGuiItems().size());
+ for (LayoutNode node : codeGeneratorData.getGuiItems())
+ {
+ if (node.shouldInsertCode()
+ && node.getNodeType().equals(LayoutNode.LayoutNodeViewType.Gallery.name()))
+ {
+ boolean containMethodDeclared =
+ checkIfInvokeMethodIsDeclared(node,
+ JavaViewBasedOnLayoutModifierConstants.SET_ON_ITEM_CLICK_LISTENER);
+ if (!containMethodDeclared)
+ {
+ List<SingleVariableDeclaration> parameters1 =
+ new ArrayList<SingleVariableDeclaration>();
+ SingleVariableDeclaration param1 =
+ createWildcardTypeVariableDeclarationFromStrings(
+ JavaViewBasedOnLayoutModifierConstants.ADAPTER_VIEW,
+ JavaViewBasedOnLayoutModifierConstants.PARENT_VIEW);
+ parameters1.add(param1);
+ SingleVariableDeclaration param2 =
+ createVariableDeclarationFromStrings(
+ JavaViewBasedOnLayoutModifierConstants.VIEW_CLASS,
+ JavaViewBasedOnLayoutModifierConstants.SELECTED_ITEM_VIEW);
+ parameters1.add(param2);
+ SingleVariableDeclaration param3 =
+ createVariableDeclarationPrimitiveCode(PrimitiveType.INT,
+ JavaViewBasedOnLayoutModifierConstants.POSITION); //$NON-NLS-1$
+ parameters1.add(param3);
+ SingleVariableDeclaration param4 =
+ createVariableDeclarationPrimitiveCode(PrimitiveType.LONG,
+ JavaViewBasedOnLayoutModifierConstants.ROW); //$NON-NLS-1$ //$NON-NLS-2$
+ parameters1.add(param4);
+ MethodDeclaration methodDeclaration1 =
+ addMethodDeclaration(ModifierKeyword.PUBLIC_KEYWORD,
+ JavaViewBasedOnLayoutModifierConstants.ON_ITEM_CLICK,
+ PrimitiveType.VOID, parameters1);
+ Block block1 = onCreateDeclaration.getAST().newBlock();
+ methodDeclaration1.setBody(block1);
+
+ addMethodInvocationToListenerHandler(node.getNodeId(),
+ JavaViewBasedOnLayoutModifierConstants.SET_ON_ITEM_CLICK_LISTENER,
+ JavaViewBasedOnLayoutModifierConstants.ADAPTER_VIEW,
+ JavaViewBasedOnLayoutModifierConstants.ON_ITEM_CLICK_LISTENER,
+ methodDeclaration1);
+ }
+ }
+ subMonitor.worked(1);
+ }
+ }
+}
diff --git a/src/plugins/android.codeutils/src/com/motorola/studio/android/generateviewbylayout/codegenerators/OnClickGUIsCodeGenerator.java b/src/plugins/android.codeutils/src/com/motorola/studio/android/generateviewbylayout/codegenerators/OnClickGUIsCodeGenerator.java
new file mode 100644
index 0000000..00136b3
--- /dev/null
+++ b/src/plugins/android.codeutils/src/com/motorola/studio/android/generateviewbylayout/codegenerators/OnClickGUIsCodeGenerator.java
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.generateviewbylayout.codegenerators;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.SubMonitor;
+import org.eclipse.jdt.core.JavaModelException;
+import org.eclipse.jdt.core.dom.Block;
+import org.eclipse.jdt.core.dom.MethodDeclaration;
+import org.eclipse.jdt.core.dom.Modifier.ModifierKeyword;
+import org.eclipse.jdt.core.dom.PrimitiveType;
+import org.eclipse.jdt.core.dom.TypeDeclaration;
+
+import com.motorola.studio.android.codeutils.i18n.CodeUtilsNLS;
+import com.motorola.studio.android.generateviewbylayout.JavaViewBasedOnLayoutModifierConstants;
+import com.motorola.studio.android.generateviewbylayout.model.CodeGeneratorDataBasedOnLayout;
+import com.motorola.studio.android.generateviewbylayout.model.LayoutNode;
+
+/**
+ * Class that deals with GUI that use on click as listener
+ * (Button, ImageButton, ToggleButton, Checkbox)
+ */
+public class OnClickGUIsCodeGenerator extends AbstractLayoutCodeGenerator
+{
+
+ /**
+ * @param codeGeneratorData
+ * @param onCreateDeclaration
+ * @param typeDeclaration
+ */
+ public OnClickGUIsCodeGenerator(CodeGeneratorDataBasedOnLayout codeGeneratorData,
+ MethodDeclaration onCreateDeclaration, TypeDeclaration typeDeclaration)
+ {
+ super(codeGeneratorData, onCreateDeclaration, typeDeclaration);
+ }
+
+ @Override
+ public void generateCode(IProgressMonitor monitor) throws JavaModelException
+ {
+ onClickMethodsForButtonsFromLayoutXML(monitor);
+ addOnClickHandler(monitor);
+ }
+
+ /**
+ * Adds methods declared to deal with Buttons' events (declared on layout XML)
+ *
+ * NOTE: we exclude fragments because there is an exception that do not let them to be handled this way
+ *
+ * <br>
+ * GENERATED_CODE_FORMAT:
+ * <br>
+ * public void $ONCLICK_ATTRIBUTE_FROMXML(View target) { }
+ *
+ */
+ @SuppressWarnings("unchecked")
+ private void onClickMethodsForButtonsFromLayoutXML(IProgressMonitor monitor)
+ {
+ SubMonitor subMonitor = SubMonitor.convert(monitor);
+ subMonitor.beginTask(CodeUtilsNLS.JavaViewBasedOnLayoutModifier_AddingOnClickMethodFromXML,
+ codeGeneratorData.getGuiItems().size());
+ for (LayoutNode node : codeGeneratorData.getGuiItems())
+ {
+ if (node.getNodeType().equals(LayoutNode.LayoutNodeViewType.Button.name())
+ && (node.getOnClick() != null)
+ && node.shouldInsertCode()
+ && codeGeneratorData.getAssociatedType().equals(
+ CodeGeneratorDataBasedOnLayout.TYPE.ACTIVITY))
+ {
+ boolean containMethodDeclared = onClickFromXmlAlreadyDeclared(node);
+ if (!containMethodDeclared)
+ {
+ //avoid to declare method twice
+
+ //generate for buttons which have onClick declaration
+ MethodDeclaration methodDeclaration =
+ addMethodDeclaration(ModifierKeyword.PUBLIC_KEYWORD, node.getOnClick(),
+ PrimitiveType.VOID,
+ JavaViewBasedOnLayoutModifierConstants.VIEW_CLASS,
+ JavaViewBasedOnLayoutModifierConstants.VIEW_VARIABLE_NAME);
+ Block block = typeDeclaration.getAST().newBlock();
+ //empty block
+ methodDeclaration.setBody(block);
+ typeDeclaration.bodyDeclarations().add(methodDeclaration);
+ }
+ }
+
+ subMonitor.worked(1);
+ }
+ }
+
+ /**
+ * Add methods to on click handler
+ *
+ * NOTE: We include fragments that have onClick declared on XML (because it is an exception in the framework)
+ *
+ * <br>
+ * GENERATED_CODE_FORMAT:
+ * <br>
+ * $GUI_ID.setOnClickListener(new View.OnClickListener() {
+ * <br>
+ * public void onClick(View target) {
+ * <br>
+ * }
+ * <br>
+ * });
+ */
+ private void addOnClickHandler(IProgressMonitor monitor) throws JavaModelException
+ {
+ SubMonitor subMonitor = SubMonitor.convert(monitor);
+ subMonitor.beginTask(CodeUtilsNLS.JavaViewBasedOnLayoutModifier_AddingOnClickHandler,
+ codeGeneratorData.getGuiItems().size());
+ for (LayoutNode node : codeGeneratorData.getGuiItems())
+ {
+ boolean containMethodDeclared =
+ checkIfInvokeMethodIsDeclared(node,
+ JavaViewBasedOnLayoutModifierConstants.SET_ON_CLICK_LISTENER);
+ if (!containMethodDeclared && node.shouldInsertCode())
+ {
+ if (node.getNodeType().equals(LayoutNode.LayoutNodeViewType.CheckBox.name())
+ || node.getNodeType().equals(
+ LayoutNode.LayoutNodeViewType.ImageButton.name())
+ || node.getNodeType().equals(
+ LayoutNode.LayoutNodeViewType.ToggleButton.name())
+ || (node.getNodeType().equals(LayoutNode.LayoutNodeViewType.Button.name()) && (codeGeneratorData
+ .getAssociatedType().equals(
+ CodeGeneratorDataBasedOnLayout.TYPE.FRAGMENT) || (node
+ .getOnClick() == null))))
+ {
+ MethodDeclaration methodDeclaration =
+ addMethodDeclaration(ModifierKeyword.PUBLIC_KEYWORD,
+ JavaViewBasedOnLayoutModifierConstants.METHOD_NAME_ON_CLICK,
+ PrimitiveType.VOID,
+ JavaViewBasedOnLayoutModifierConstants.VIEW_CLASS,
+ JavaViewBasedOnLayoutModifierConstants.VIEW_VARIABLE_NAME);
+ Block block = onCreateDeclaration.getAST().newBlock();
+ methodDeclaration.setBody(block);
+ addMethodInvocationToListenerHandler(node.getNodeId(),
+ JavaViewBasedOnLayoutModifierConstants.SET_ON_CLICK_LISTENER,
+ JavaViewBasedOnLayoutModifierConstants.VIEW_CLASS,
+ JavaViewBasedOnLayoutModifierConstants.METHOD_ON_CLICK_LISTENER,
+ methodDeclaration);
+ }
+ }
+ subMonitor.worked(1);
+ }
+ }
+}
diff --git a/src/plugins/android.codeutils/src/com/motorola/studio/android/generateviewbylayout/codegenerators/RadioButtonCodeGenerator.java b/src/plugins/android.codeutils/src/com/motorola/studio/android/generateviewbylayout/codegenerators/RadioButtonCodeGenerator.java
new file mode 100644
index 0000000..90c5cd2
--- /dev/null
+++ b/src/plugins/android.codeutils/src/com/motorola/studio/android/generateviewbylayout/codegenerators/RadioButtonCodeGenerator.java
@@ -0,0 +1,361 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.generateviewbylayout.codegenerators;
+
+import java.util.List;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.SubMonitor;
+import org.eclipse.jdt.core.JavaModelException;
+import org.eclipse.jdt.core.dom.AnonymousClassDeclaration;
+import org.eclipse.jdt.core.dom.Block;
+import org.eclipse.jdt.core.dom.ClassInstanceCreation;
+import org.eclipse.jdt.core.dom.Expression;
+import org.eclipse.jdt.core.dom.ExpressionStatement;
+import org.eclipse.jdt.core.dom.FieldAccess;
+import org.eclipse.jdt.core.dom.FieldDeclaration;
+import org.eclipse.jdt.core.dom.IfStatement;
+import org.eclipse.jdt.core.dom.MethodDeclaration;
+import org.eclipse.jdt.core.dom.MethodInvocation;
+import org.eclipse.jdt.core.dom.Modifier;
+import org.eclipse.jdt.core.dom.Modifier.ModifierKeyword;
+import org.eclipse.jdt.core.dom.PrimitiveType;
+import org.eclipse.jdt.core.dom.QualifiedName;
+import org.eclipse.jdt.core.dom.SimpleName;
+import org.eclipse.jdt.core.dom.SimpleType;
+import org.eclipse.jdt.core.dom.TypeDeclaration;
+import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
+
+import com.motorola.studio.android.codeutils.i18n.CodeUtilsNLS;
+import com.motorola.studio.android.generateviewbylayout.JavaViewBasedOnLayoutModifierConstants;
+import com.motorola.studio.android.generateviewbylayout.model.CodeGeneratorDataBasedOnLayout;
+import com.motorola.studio.android.generateviewbylayout.model.LayoutNode;
+
+/**
+ * Responsible to add methods to deal with RadioButtons events
+ */
+public class RadioButtonCodeGenerator extends AbstractLayoutCodeGenerator
+{
+ /**
+ * @param codeGeneratorData
+ * @param onCreateDeclaration
+ * @param typeDeclaration
+ */
+ public RadioButtonCodeGenerator(CodeGeneratorDataBasedOnLayout codeGeneratorData,
+ MethodDeclaration onCreateDeclaration, TypeDeclaration typeDeclaration)
+ {
+ super(codeGeneratorData, onCreateDeclaration, typeDeclaration);
+ }
+
+ @Override
+ public void generateCode(IProgressMonitor monitor) throws JavaModelException
+ {
+ addSetOnClickListener(monitor);
+ addMethodToDeclareRadioButtonHandlerNotDeclaredOnLayoutXML(monitor);
+ }
+
+ /**
+ * Add method handlers for RadioButtons
+ *
+ * <br>
+ * GENERATED_CODE_FORMAT:
+ * <br>
+ * this.$GUI_ID.setOnClickListener(onClickHandler);
+ */
+ @SuppressWarnings("unchecked")
+ private void addSetOnClickListener(IProgressMonitor monitor) throws JavaModelException
+ {
+ SubMonitor subMonitor = SubMonitor.convert(monitor);
+ subMonitor.beginTask(CodeUtilsNLS.JavaViewBasedOnLayoutModifier_AddingSetOnClickListener,
+ codeGeneratorData.getGuiItems().size());
+ /* ExpressionStatement
+ expression
+ MethodInvocation
+ arguments handler
+ methodName SimpleName(setOnClickListener)
+ optionalExpression SimpleName(button1)
+ */
+ for (LayoutNode node : codeGeneratorData.getGuiItems())
+ {
+ if (node.shouldInsertCode()
+ && node.getNodeType().equals(LayoutNode.LayoutNodeViewType.RadioButton.name()))
+ {
+ boolean containMethodDeclared =
+ checkIfInvokeMethodIsDeclared(node,
+ JavaViewBasedOnLayoutModifierConstants.SET_ON_CLICK_LISTENER);
+ if (!containMethodDeclared)
+ {
+ SimpleName method =
+ onCreateDeclaration.getAST().newSimpleName(
+ JavaViewBasedOnLayoutModifierConstants.SET_ON_CLICK_LISTENER);
+ SimpleName bId = onCreateDeclaration.getAST().newSimpleName(node.getNodeId());
+ FieldAccess fieldAccess = onCreateDeclaration.getAST().newFieldAccess();
+ fieldAccess.setExpression(onCreateDeclaration.getAST().newThisExpression());
+ fieldAccess.setName(bId);
+
+ SimpleName handler =
+ onCreateDeclaration
+ .getAST()
+ .newSimpleName(
+ JavaViewBasedOnLayoutModifierConstants.HANDLER_ONCLICK_LISTENER);
+
+ MethodInvocation mI = onCreateDeclaration.getAST().newMethodInvocation();
+ mI.arguments().add(handler);
+ mI.setName(method);
+ mI.setExpression(fieldAccess);
+
+ ExpressionStatement expr =
+ onCreateDeclaration.getAST().newExpressionStatement(mI);
+ if (!onCreateDeclaration.toString().contains(expr.toString()))
+ {
+ //avoid to duplicate statement
+ insertStatementsAtOnCreateDeclaration(expr, false);
+ }
+ }
+ }
+ subMonitor.worked(1);
+ }
+ }
+
+ /**
+ * Add method to declare button events handler (not declared on layout XML)
+ * @throws JavaModelException
+ *
+ * <br>
+ * GENERATED_CODE_FORMAT:
+ * <br>
+ * private View.OnClickListener onClickHandler = new View.OnClickListener() {
+ * <br>
+ * public void onClick(View target) {
+ * <br>
+ * if (target.getId() == $GUI_ID1) {
+ * <br>
+ * } else if (target.getId() == $GUI_ID2) {
+ * <br>
+ * }
+ * <br>
+ * }
+ * <br>
+ * };
+ *
+ */
+ @SuppressWarnings(
+ {
+ "rawtypes", "unchecked"
+ })
+ private void addMethodToDeclareRadioButtonHandlerNotDeclaredOnLayoutXML(IProgressMonitor monitor)
+ throws JavaModelException
+ {
+ SubMonitor subMonitor = SubMonitor.convert(monitor);
+ //need to look at each GUI item and them create 1 method
+ subMonitor.beginTask(CodeUtilsNLS.JavaViewBasedOnLayoutModifier_AddingMethodToHandleButton,
+ codeGeneratorData.getGuiItems().size() + 1);
+ IfStatement ifStatement = null;
+ for (LayoutNode node : codeGeneratorData.getGuiItems())
+ {
+ if (node.getNodeType().equals(LayoutNode.LayoutNodeViewType.RadioButton.name())
+ && node.shouldInsertCode())
+ {
+
+ if (typeDeclaration.bodyDeclarations() != null)
+ {
+ //check if field already declared
+ outer: for (Object bd : typeDeclaration.bodyDeclarations())
+ {
+ if (bd instanceof FieldDeclaration)
+ {
+ FieldDeclaration fd = (FieldDeclaration) bd;
+ if ((fd.fragments() != null))
+ {
+ String listenerType =
+ getListenerSimpleType(
+ JavaViewBasedOnLayoutModifierConstants.VIEW_CLASS,
+ JavaViewBasedOnLayoutModifierConstants.METHOD_ON_CLICK_LISTENER)
+ .toString();
+ for (Object fragment : fd.fragments())
+ {
+ if (fragment instanceof VariableDeclarationFragment)
+ {
+ VariableDeclarationFragment variableFragment =
+ (VariableDeclarationFragment) fragment;
+ if ((variableFragment.getName() != null)
+ && variableFragment
+ .getName()
+ .toString()
+ .equals(JavaViewBasedOnLayoutModifierConstants.HANDLER_ONCLICK_LISTENER)
+ && fd.getType().toString().equals(listenerType))
+ {
+ Expression expr = variableFragment.getInitializer();
+ if (expr instanceof ClassInstanceCreation)
+ {
+ ClassInstanceCreation classInstanceCreation =
+ (ClassInstanceCreation) expr;
+ AnonymousClassDeclaration anonymousClassDeclaration =
+ classInstanceCreation
+ .getAnonymousClassDeclaration();
+ for (Object bodyDeclaration : anonymousClassDeclaration
+ .bodyDeclarations())
+ {
+ if (bodyDeclaration instanceof MethodDeclaration)
+ {
+ MethodDeclaration methodDeclaration =
+ (MethodDeclaration) bodyDeclaration;
+ List statementsList =
+ methodDeclaration.getBody()
+ .statements();
+ for (Object statement : statementsList)
+ {
+ if (statement instanceof IfStatement)
+ {
+ if (statement
+ .toString()
+ .contains(
+ JavaViewBasedOnLayoutModifierConstants.VIEW_VARIABLE_NAME
+ + "." //$NON-NLS-1$
+ + JavaViewBasedOnLayoutModifierConstants.METHOD_NAME_GET_ID))
+ {
+ //found if statement
+ ifStatement =
+ (IfStatement) statement;
+ break outer;
+ }
+ }
+ }
+ if (ifStatement == null)
+ {
+ ifStatement =
+ typeDeclaration.getAST()
+ .newIfStatement();
+ statementsList.add(ifStatement);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ }
+ }
+ }
+ }
+ if (ifStatement == null)
+ {
+ //create field with onClickListener class instance creation
+ //create method in the first time, after that, start to add new items in the same method
+ ifStatement = createOnClickListenerForRadioButtons(node);
+ }
+ else
+ {
+ //already have onClickListener field declared => append new item into the "else if" chain
+ addElseIfForEachRadioButtonId(ifStatement, node);
+ }
+ }
+ subMonitor.worked(1);
+ }
+
+ }
+
+ /**
+ * Creates field with an anonymous class declaration for radio buttons
+ */
+ @SuppressWarnings("unchecked")
+ private IfStatement createOnClickListenerForRadioButtons(LayoutNode node)
+ {
+ IfStatement ifSt;
+ Modifier privateMod = typeDeclaration.getAST().newModifier(ModifierKeyword.PRIVATE_KEYWORD);
+ SimpleType listenerType =
+ getListenerSimpleType(JavaViewBasedOnLayoutModifierConstants.VIEW_CLASS,
+ JavaViewBasedOnLayoutModifierConstants.METHOD_ON_CLICK_LISTENER);
+
+ VariableDeclarationFragment variableFragment =
+ typeDeclaration.getAST().newVariableDeclarationFragment();
+ SimpleName varName =
+ variableFragment.getAST().newSimpleName(
+ JavaViewBasedOnLayoutModifierConstants.HANDLER_ONCLICK_LISTENER);
+ variableFragment.setName(varName);
+ FieldDeclaration declaration =
+ typeDeclaration.getAST().newFieldDeclaration(variableFragment);
+ declaration.modifiers().add(privateMod);
+ declaration.setType(listenerType);
+
+ ClassInstanceCreation classInstanceCreation =
+ typeDeclaration.getAST().newClassInstanceCreation();
+
+ SimpleType listenerType2 =
+ getListenerSimpleType(JavaViewBasedOnLayoutModifierConstants.VIEW_CLASS,
+ JavaViewBasedOnLayoutModifierConstants.METHOD_ON_CLICK_LISTENER);
+
+ classInstanceCreation.setType(listenerType2);
+ AnonymousClassDeclaration classDeclaration =
+ typeDeclaration.getAST().newAnonymousClassDeclaration();
+ MethodDeclaration methodDeclaration =
+ addMethodDeclaration(ModifierKeyword.PUBLIC_KEYWORD,
+ JavaViewBasedOnLayoutModifierConstants.METHOD_NAME_ON_CLICK,
+ PrimitiveType.VOID, JavaViewBasedOnLayoutModifierConstants.VIEW_CLASS,
+ JavaViewBasedOnLayoutModifierConstants.VIEW_VARIABLE_NAME);
+ Block block = typeDeclaration.getAST().newBlock();
+ ifSt = createElseIfForEachRadioButtonId(node);
+ block.statements().add(ifSt);
+ methodDeclaration.setBody(block);
+ classDeclaration.bodyDeclarations().add(methodDeclaration);
+ classInstanceCreation.setAnonymousClassDeclaration(classDeclaration);
+ variableFragment.setInitializer(classInstanceCreation);
+ typeDeclaration.bodyDeclarations().add(declaration);
+ return ifSt;
+ }
+
+ /**
+ * @return "else if" chain for each radio button id
+ */
+ private IfStatement createElseIfForEachRadioButtonId(LayoutNode node)
+ {
+ IfStatement ifStatement = typeDeclaration.getAST().newIfStatement();
+ addElseIfForEachRadioButtonId(ifStatement, node);
+ return ifStatement;
+ }
+
+ /**
+ * Creates else if and else statements for each radio button
+ * @param ifSt If statement where the next "else if" will be appended
+ * @param node Layout node
+ */
+ private void addElseIfForEachRadioButtonId(IfStatement ifSt, LayoutNode node)
+ {
+ if (node.getNodeType().equals(LayoutNode.LayoutNodeViewType.RadioButton.name())
+ && node.shouldInsertCode())
+ {
+ MethodInvocation invocation = typeDeclaration.getAST().newMethodInvocation();
+ invocation
+ .setExpression(getVariableName(JavaViewBasedOnLayoutModifierConstants.VIEW_VARIABLE_NAME));
+ SimpleName getIdName =
+ typeDeclaration.getAST().newSimpleName(
+ JavaViewBasedOnLayoutModifierConstants.METHOD_NAME_GET_ID);
+ invocation.setName(getIdName);
+
+ SimpleName r =
+ typeDeclaration.getAST()
+ .newSimpleName(JavaViewBasedOnLayoutModifierConstants.R);
+ SimpleName id =
+ typeDeclaration.getAST().newSimpleName(
+ JavaViewBasedOnLayoutModifierConstants.ID);
+ QualifiedName rid = typeDeclaration.getAST().newQualifiedName(r, id);
+ SimpleName guiId = typeDeclaration.getAST().newSimpleName(node.getNodeId());
+ QualifiedName guiQN = typeDeclaration.getAST().newQualifiedName(rid, guiId);
+ createElseIfAndElseStatements(ifSt, invocation, guiQN);
+ }
+ }
+} \ No newline at end of file
diff --git a/src/plugins/android.codeutils/src/com/motorola/studio/android/generateviewbylayout/codegenerators/RatingBarCodeGenerator.java b/src/plugins/android.codeutils/src/com/motorola/studio/android/generateviewbylayout/codegenerators/RatingBarCodeGenerator.java
new file mode 100644
index 0000000..e471934
--- /dev/null
+++ b/src/plugins/android.codeutils/src/com/motorola/studio/android/generateviewbylayout/codegenerators/RatingBarCodeGenerator.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.generateviewbylayout.codegenerators;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.SubMonitor;
+import org.eclipse.jdt.core.JavaModelException;
+import org.eclipse.jdt.core.dom.Block;
+import org.eclipse.jdt.core.dom.MethodDeclaration;
+import org.eclipse.jdt.core.dom.Modifier.ModifierKeyword;
+import org.eclipse.jdt.core.dom.PrimitiveType;
+import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
+import org.eclipse.jdt.core.dom.TypeDeclaration;
+
+import com.motorola.studio.android.codeutils.i18n.CodeUtilsNLS;
+import com.motorola.studio.android.generateviewbylayout.JavaViewBasedOnLayoutModifierConstants;
+import com.motorola.studio.android.generateviewbylayout.model.CodeGeneratorDataBasedOnLayout;
+import com.motorola.studio.android.generateviewbylayout.model.LayoutNode;
+
+/**
+ * Responsible to add methods to deal with RatingBars events
+ */
+public class RatingBarCodeGenerator extends AbstractLayoutCodeGenerator
+{
+ /**
+ * @param codeGeneratorData
+ * @param onCreateDeclaration
+ * @param typeDeclaration
+ */
+ public RatingBarCodeGenerator(CodeGeneratorDataBasedOnLayout codeGeneratorData,
+ MethodDeclaration onCreateDeclaration, TypeDeclaration typeDeclaration)
+ {
+ super(codeGeneratorData, onCreateDeclaration, typeDeclaration);
+ }
+
+ @Override
+ public void generateCode(IProgressMonitor monitor) throws JavaModelException
+ {
+ addOnRatingBarChangeListener(monitor);
+ }
+
+ /**
+ * Adds methods (event handler) for RatingBar
+ *
+ * <br>
+ * GENERATED_CODE_FORMAT:
+ * <br>
+ * $GUI_ID.setOnRatingBarChangeListener(new OnRatingBarChangeListener() {
+ * <br>
+ public void onRatingChanged(RatingBar ratingBar, float rating, boolean fromUser) {
+ <br>
+ }
+ <br>
+ });
+ *
+ * @param monitor
+ */
+ private void addOnRatingBarChangeListener(IProgressMonitor monitor) throws JavaModelException
+ {
+ SubMonitor subMonitor = SubMonitor.convert(monitor);
+ subMonitor.beginTask(CodeUtilsNLS.JavaViewBasedOnLayoutModifier_AddingRatingBarHandler,
+ codeGeneratorData.getGuiItems().size());
+ for (LayoutNode node : codeGeneratorData.getGuiItems())
+ {
+ if (node.shouldInsertCode()
+ && node.getNodeType().equals(LayoutNode.LayoutNodeViewType.RatingBar.name()))
+ {
+ boolean containMethodDeclared =
+ checkIfInvokeMethodIsDeclared(
+ node,
+ JavaViewBasedOnLayoutModifierConstants.SET_ON_RATING_BAR_CHANGE_LISTENER);
+ if (!containMethodDeclared)
+ {
+ List<SingleVariableDeclaration> parameters1 =
+ new ArrayList<SingleVariableDeclaration>();
+ SingleVariableDeclaration param1 =
+ createVariableDeclarationFromStrings(
+ LayoutNode.LayoutNodeViewType.RatingBar.name(),
+ JavaViewBasedOnLayoutModifierConstants.RATING_BAR_VARIABLE); //$NON-NLS-1$
+ parameters1.add(param1);
+ SingleVariableDeclaration param2 =
+ createVariableDeclarationPrimitiveCode(PrimitiveType.FLOAT,
+ JavaViewBasedOnLayoutModifierConstants.RATING_VARIABLE); //$NON-NLS-1$
+ parameters1.add(param2);
+ SingleVariableDeclaration param3 =
+ createVariableDeclarationPrimitiveCode(PrimitiveType.BOOLEAN,
+ JavaViewBasedOnLayoutModifierConstants.FROM_USER_VARIABLE); //$NON-NLS-1$
+ parameters1.add(param3);
+ MethodDeclaration methodDeclaration1 =
+ addMethodDeclaration(ModifierKeyword.PUBLIC_KEYWORD,
+ JavaViewBasedOnLayoutModifierConstants.ON_RATING_CHANGED,
+ PrimitiveType.VOID, parameters1);
+ Block block1 = onCreateDeclaration.getAST().newBlock();
+ methodDeclaration1.setBody(block1);
+
+ addMethodInvocationToListenerHandler(
+ node.getNodeId(),
+ JavaViewBasedOnLayoutModifierConstants.SET_ON_RATING_BAR_CHANGE_LISTENER,
+ LayoutNode.LayoutNodeViewType.RatingBar.name(),
+ JavaViewBasedOnLayoutModifierConstants.ON_RATING_BAR_CHANGE_LISTENER,
+ methodDeclaration1);
+ }
+ }
+ subMonitor.worked(1);
+ }
+ }
+}
diff --git a/src/plugins/android.codeutils/src/com/motorola/studio/android/generateviewbylayout/codegenerators/SaveStateCodeGenerator.java b/src/plugins/android.codeutils/src/com/motorola/studio/android/generateviewbylayout/codegenerators/SaveStateCodeGenerator.java
new file mode 100644
index 0000000..cdf05c4
--- /dev/null
+++ b/src/plugins/android.codeutils/src/com/motorola/studio/android/generateviewbylayout/codegenerators/SaveStateCodeGenerator.java
@@ -0,0 +1,619 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.generateviewbylayout.codegenerators;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.SubMonitor;
+import org.eclipse.jdt.core.JavaModelException;
+import org.eclipse.jdt.core.dom.Block;
+import org.eclipse.jdt.core.dom.BooleanLiteral;
+import org.eclipse.jdt.core.dom.Expression;
+import org.eclipse.jdt.core.dom.ExpressionStatement;
+import org.eclipse.jdt.core.dom.MethodDeclaration;
+import org.eclipse.jdt.core.dom.MethodInvocation;
+import org.eclipse.jdt.core.dom.Modifier.ModifierKeyword;
+import org.eclipse.jdt.core.dom.NumberLiteral;
+import org.eclipse.jdt.core.dom.PrimitiveType;
+import org.eclipse.jdt.core.dom.QualifiedName;
+import org.eclipse.jdt.core.dom.QualifiedType;
+import org.eclipse.jdt.core.dom.SimpleName;
+import org.eclipse.jdt.core.dom.SimpleType;
+import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
+import org.eclipse.jdt.core.dom.Statement;
+import org.eclipse.jdt.core.dom.StringLiteral;
+import org.eclipse.jdt.core.dom.TypeDeclaration;
+import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
+import org.eclipse.jdt.core.dom.VariableDeclarationStatement;
+
+import com.motorola.studio.android.codeutils.i18n.CodeUtilsNLS;
+import com.motorola.studio.android.generateviewbylayout.JavaModifierBasedOnLayout;
+import com.motorola.studio.android.generateviewbylayout.model.CheckboxNode;
+import com.motorola.studio.android.generateviewbylayout.model.CodeGeneratorDataBasedOnLayout;
+import com.motorola.studio.android.generateviewbylayout.model.EditTextNode;
+import com.motorola.studio.android.generateviewbylayout.model.LayoutNode;
+import com.motorola.studio.android.generateviewbylayout.model.LayoutNode.ViewProperties;
+import com.motorola.studio.android.generateviewbylayout.model.RadioButtonNode;
+import com.motorola.studio.android.generateviewbylayout.model.SeekBarNode;
+import com.motorola.studio.android.generateviewbylayout.model.SpinnerNode;
+
+/**
+ * Generate code to save/restore GUI state (e.g.: EditText, ComboBox, etc)
+ */
+public class SaveStateCodeGenerator extends AbstractLayoutCodeGenerator
+
+{
+
+ public static LayoutNode[] saveStateNodeTypes = new LayoutNode[]
+ {
+ new CheckboxNode(), new RadioButtonNode(), new EditTextNode(), new SpinnerNode(),
+ new SeekBarNode()
+ };
+
+ /*
+ * Constants (types, methods and variable names)
+ */
+ private static final String COMMIT = "commit";
+
+ private static final String ANDROID_CONTENT_IMPORT = "android.content.*";
+
+ private static final String MODE_PRIVATE = "MODE_PRIVATE";
+
+ private static final String GET_PREFERENCES = "getPreferences";
+
+ private static final String PREFERENCES = "preferences";
+
+ private static final String EDITOR_CAPITAL_LETTER = "Editor";
+
+ private static final String SHARED_PREFERENCES = "SharedPreferences";
+
+ private static final String EDITOR = "editor";
+
+ private static final String EDIT = "edit";
+
+ private static final String TO_STRING = "toString";
+
+ private static final String PROTECTED_VOID_ON_PAUSE = "protected void onPause()"; //$NON-NLS-1$
+
+ private static final String PROTECTED_VOID_ON_RESUME = "protected void onResume()"; //$NON-NLS-1$
+
+ private static final String ON_PAUSE = "onPause"; //$NON-NLS-1$
+
+ private static final String ON_RESUME = "onResume"; //$NON-NLS-1$
+
+ /**
+ * @param codeGeneratorData
+ * @param onCreateDeclaration
+ * @param typeDeclaration
+ */
+ public SaveStateCodeGenerator(CodeGeneratorDataBasedOnLayout codeGeneratorData,
+ MethodDeclaration onCreateDeclaration, TypeDeclaration typeDeclaration)
+ {
+ super(codeGeneratorData, onCreateDeclaration, typeDeclaration);
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public void generateCode(IProgressMonitor monitor) throws JavaModelException
+ {
+ SubMonitor subMonitor = SubMonitor.convert(monitor);
+ subMonitor.beginTask(CodeUtilsNLS.SaveStateCodeGenerator_AddingCodeSaveRestoreUIState,
+ codeGeneratorData.getGuiItems().size());
+
+ boolean alreadyFoundItemToSaveState = false;
+ MethodDeclaration onPauseFoundMethod = null;
+ MethodDeclaration onResumeFoundMethod = null;
+ SimpleName editorVarName = null;
+ SimpleName preferencesVarName = null;
+ for (LayoutNode node : codeGeneratorData.getGUIItems(false))
+ {
+ if (canGenerateSaveStateCode(node) && node.getSaveState()
+ && (node.isAlreadyDeclaredInCode() || node.shouldInsertCode()))
+ {
+ if (!alreadyFoundItemToSaveState)
+ {
+ onPauseFoundMethod = insertOnPause();
+
+ onResumeFoundMethod = insertOnResume();
+
+ String superMethodNameOnPause = ON_PAUSE;
+ insertSuperInvocation(onPauseFoundMethod, superMethodNameOnPause, null);
+
+ String superMethodNameOnResume = ON_RESUME;
+ insertSuperInvocation(onResumeFoundMethod, superMethodNameOnResume, null);
+
+ JavaModifierBasedOnLayout.IMPORT_LIST.add(ANDROID_CONTENT_IMPORT);
+
+ preferencesVarName = getPreferenceVariable(onPauseFoundMethod);
+
+ editorVarName = insertPreferencesEditor(onPauseFoundMethod, preferencesVarName);
+
+ getPreferenceVariable(onResumeFoundMethod);
+
+ //need to add method declaration and variables only once
+ alreadyFoundItemToSaveState = true;
+ }
+ String putMethodName = node.getProperty(ViewProperties.PreferenceSetMethod);
+ String getUIStateMethodName = node.getProperty(ViewProperties.ViewStateGetMethod);
+ String getter = node.getProperty(ViewProperties.PreferenceGetMethod);
+ String setMethodName = node.getProperty(ViewProperties.ViewStateSetMethod);
+
+ MethodInvocation getGUIStateInvocation = null;
+ if (node.getNodeType().equals(LayoutNode.LayoutNodeViewType.EditText.name()))
+ {
+ //Add code for save state: savedInstanceState.putString("MyEditTextId", edittext.getText());
+ getGUIStateInvocation = onPauseFoundMethod.getAST().newMethodInvocation();
+ String getGUIStateToStringName = TO_STRING; //$NON-NLS-1$
+
+ MethodInvocation editTextMethod =
+ addMethodToRetrieveUIState(onPauseFoundMethod, node,
+ getUIStateMethodName);
+
+ SimpleName toStringName =
+ onPauseFoundMethod.getAST().newSimpleName(getGUIStateToStringName);
+ getGUIStateInvocation.setName(toStringName);
+ getGUIStateInvocation.setExpression(editTextMethod);
+
+ insertPutMethod(onPauseFoundMethod, node, putMethodName,
+ editorVarName.getIdentifier(), getGUIStateInvocation);
+
+ //Add code for restore state: edittext.setText(savedInstanceState.getString("MyEditTextId"));
+ MethodInvocation getBundleStateInvocation =
+ onResumeFoundMethod.getAST().newMethodInvocation();
+
+ SimpleName getName = onPauseFoundMethod.getAST().newSimpleName(getter);
+ getBundleStateInvocation.setName(getName);
+ SimpleName getExpr =
+ onPauseFoundMethod.getAST().newSimpleName(
+ preferencesVarName.getIdentifier());
+ getBundleStateInvocation.setExpression(getExpr);
+ StringLiteral id = onPauseFoundMethod.getAST().newStringLiteral();
+ id.setLiteralValue(node.getNodeId());
+ getBundleStateInvocation.arguments().add(id);
+
+ StringLiteral defaultValue = onPauseFoundMethod.getAST().newStringLiteral();
+ defaultValue.setLiteralValue("");
+ getBundleStateInvocation.arguments().add(defaultValue);
+
+ insertSetMethod(onResumeFoundMethod, node, setMethodName,
+ getBundleStateInvocation);
+ }
+ else
+ {
+ //Add code to save state: savedInstanceState.putBoolean("MyCheckboxId", checkbox.getEnabled());
+ //Add code to restore state: checkbox.setEnabled(savedInstanceState.getBoolean("MyCheckbox"));
+
+ insertSaveRestoreCode(onPauseFoundMethod, onResumeFoundMethod,
+ editorVarName.getIdentifier(), preferencesVarName.getIdentifier(),
+ node, putMethodName, setMethodName, getter, getUIStateMethodName);
+ }
+
+ }
+ subMonitor.worked(1);
+ }
+
+ if (alreadyFoundItemToSaveState)
+ {
+ //Add code: editor.commit();
+ invokeMethod(onPauseFoundMethod, EDITOR, COMMIT);
+ }
+ }
+
+ /**
+ * @param node layout node being analyzed
+ * @return true if it is in the array of saveStateNodeTypes (the current supported view items to save/restore state), false otherwise
+ */
+ public static boolean canGenerateSaveStateCode(LayoutNode node)
+ {
+ boolean canSaveCode = false;
+
+ if ((node != null) && (node.getNodeType() != null))
+ {
+ int i = 0;
+ while (!canSaveCode && (i < saveStateNodeTypes.length))
+ {
+ if (node.getNodeType().equals(saveStateNodeTypes[i].getNodeType()))
+ {
+ canSaveCode = true;
+ }
+ i++;
+ }
+ }
+
+ return canSaveCode;
+ }
+
+ /**
+ * Inserts SharedPreferences.Editor statement into onPause method
+ * @param onPauseFoundMethod
+ * @param preferencesVarName
+ * @return
+ */
+ @SuppressWarnings("unchecked")
+ public SimpleName insertPreferencesEditor(MethodDeclaration onPauseFoundMethod,
+ SimpleName preferencesVarName)
+ {
+ SimpleName editorVarName = onPauseFoundMethod.getAST().newSimpleName(EDITOR);
+ boolean alreadyAddedVariable = false;
+ if (onPauseFoundMethod.getBody() != null)
+ {
+ outer: for (Object s : onPauseFoundMethod.getBody().statements())
+ {
+ if (s instanceof VariableDeclarationStatement)
+ {
+ VariableDeclarationStatement variableDeclarationStatement =
+ (VariableDeclarationStatement) s;
+ if (variableDeclarationStatement.getType().toString()
+ .equals(SHARED_PREFERENCES + "." + EDITOR_CAPITAL_LETTER))
+ {
+ for (Object f : variableDeclarationStatement.fragments())
+ {
+ VariableDeclarationFragment frag = (VariableDeclarationFragment) f;
+ if (frag.getName().toString().equals(EDITOR))
+ {
+ alreadyAddedVariable = true;
+ break outer;
+ }
+ }
+ }
+ }
+ }
+ }
+ if (!alreadyAddedVariable)
+ {
+ VariableDeclarationFragment getEditorPreference =
+ onPauseFoundMethod.getAST().newVariableDeclarationFragment();
+ MethodInvocation getEditorInvoke = onPauseFoundMethod.getAST().newMethodInvocation();
+ SimpleName editInvokeName = onPauseFoundMethod.getAST().newSimpleName(EDIT);
+ getEditorInvoke.setName(editInvokeName);
+ SimpleName editInvokeVariable =
+ onPauseFoundMethod.getAST().newSimpleName(preferencesVarName.getIdentifier());
+ getEditorInvoke.setExpression(editInvokeVariable);
+ getEditorPreference.setInitializer(getEditorInvoke);
+
+ getEditorPreference.setName(editorVarName);
+ VariableDeclarationStatement getEditorVariableDeclarationStatement =
+ onPauseFoundMethod.getAST()
+ .newVariableDeclarationStatement(getEditorPreference);
+ SimpleName sharedPreferencesName1 =
+ onPauseFoundMethod.getAST().newSimpleName(SHARED_PREFERENCES);
+ SimpleType type = onPauseFoundMethod.getAST().newSimpleType(sharedPreferencesName1);
+ SimpleName sharedPreferencesName2 =
+ onPauseFoundMethod.getAST().newSimpleName(EDITOR_CAPITAL_LETTER);
+ QualifiedType editorPreferencesType =
+ onPauseFoundMethod.getAST().newQualifiedType(type, sharedPreferencesName2);
+ getEditorVariableDeclarationStatement.setType(editorPreferencesType);
+ onPauseFoundMethod.getBody().statements().add(getEditorVariableDeclarationStatement);
+ }
+ return editorVarName;
+ }
+
+ /**
+ * Creates onResume method if it does not exist yet
+ * @return {@link MethodDeclaration} for void onResume() method
+ */
+ @SuppressWarnings("unchecked")
+ public MethodDeclaration insertOnResume()
+ {
+ //Add protected void onResume()
+ ModifierKeyword keyword = ModifierKeyword.PUBLIC_KEYWORD;
+ if (getCodeGeneratorData().getAssociatedType().equals(
+ CodeGeneratorDataBasedOnLayout.TYPE.FRAGMENT))
+ {
+ keyword = ModifierKeyword.PUBLIC_KEYWORD;
+ }
+ else if (getCodeGeneratorData().getAssociatedType().equals(
+ CodeGeneratorDataBasedOnLayout.TYPE.ACTIVITY))
+ {
+ keyword = ModifierKeyword.PROTECTED_KEYWORD;
+ }
+ MethodDeclaration onResumeMethodDeclaration =
+ addMethodDeclaration(keyword, ON_RESUME, PrimitiveType.VOID,
+ new ArrayList<SingleVariableDeclaration>());
+ Block onResumeMethodBlock = onCreateDeclaration.getAST().newBlock();
+ MethodDeclaration onResumeFoundMethod =
+ isMethodAlreadyDeclared(onResumeMethodDeclaration, PROTECTED_VOID_ON_RESUME);
+ if (onResumeFoundMethod == null)
+ {
+ //add method onRestore if it does not exist yet
+ onResumeMethodDeclaration.setBody(onResumeMethodBlock);
+ typeDeclaration.bodyDeclarations().add(onResumeMethodDeclaration);
+ onResumeFoundMethod = onResumeMethodDeclaration;
+ }
+ return onResumeFoundMethod;
+ }
+
+ /**
+ * Creates onPause method if it does not exist yet
+ * @return {@link MethodDeclaration} for void onPause() method
+ */
+ @SuppressWarnings("unchecked")
+ public MethodDeclaration insertOnPause()
+ {
+ //Add protected void onPause()
+ ModifierKeyword keyword = ModifierKeyword.PUBLIC_KEYWORD;
+ if (getCodeGeneratorData().getAssociatedType().equals(
+ CodeGeneratorDataBasedOnLayout.TYPE.FRAGMENT))
+ {
+ keyword = ModifierKeyword.PUBLIC_KEYWORD;
+ }
+ else if (getCodeGeneratorData().getAssociatedType().equals(
+ CodeGeneratorDataBasedOnLayout.TYPE.ACTIVITY))
+ {
+ keyword = ModifierKeyword.PROTECTED_KEYWORD;
+ }
+ MethodDeclaration onPauseMethodDeclaration =
+ addMethodDeclaration(keyword, ON_PAUSE, PrimitiveType.VOID,
+ new ArrayList<SingleVariableDeclaration>());
+ Block onPauseMethodBlock = onCreateDeclaration.getAST().newBlock();
+ MethodDeclaration onPauseFoundMethod =
+ isMethodAlreadyDeclared(onPauseMethodDeclaration, PROTECTED_VOID_ON_PAUSE);
+ if (onPauseFoundMethod == null)
+ {
+ //add method onPause if it does not exist yet
+ onPauseMethodDeclaration.setBody(onPauseMethodBlock);
+ typeDeclaration.bodyDeclarations().add(onPauseMethodDeclaration);
+ onPauseFoundMethod = onPauseMethodDeclaration;
+ }
+ return onPauseFoundMethod;
+ }
+
+ /**
+ * @param method
+ * @return {@link SimpleName} for the variable with SharedPreferences type inside the method body
+ */
+ @SuppressWarnings("unchecked")
+ public SimpleName getPreferenceVariable(MethodDeclaration method)
+ {
+ SimpleName preferencesVarName = method.getAST().newSimpleName(PREFERENCES);
+ boolean alreadyAddedVariable = false;
+ if (method.getBody() != null)
+ {
+ outer: for (Object s : method.getBody().statements())
+ {
+ if (s instanceof VariableDeclarationStatement)
+ {
+ VariableDeclarationStatement variableDeclarationStatement =
+ (VariableDeclarationStatement) s;
+ if (variableDeclarationStatement.getType().toString()
+ .equals(SHARED_PREFERENCES))
+ {
+ for (Object f : variableDeclarationStatement.fragments())
+ {
+ VariableDeclarationFragment frag = (VariableDeclarationFragment) f;
+ if (frag.getName().toString().equals(PREFERENCES))
+ {
+ alreadyAddedVariable = true;
+ break outer;
+ }
+ }
+ }
+ }
+ }
+ }
+ if (!alreadyAddedVariable)
+ {
+ VariableDeclarationFragment getPreferencefragment =
+ method.getAST().newVariableDeclarationFragment();
+ MethodInvocation invoke = method.getAST().newMethodInvocation();
+ SimpleName invokeName = method.getAST().newSimpleName(GET_PREFERENCES);
+ invoke.setName(invokeName);
+ if (getCodeGeneratorData().getAssociatedType().equals(
+ CodeGeneratorDataBasedOnLayout.TYPE.ACTIVITY))
+ {
+ SimpleName invokeMode = method.getAST().newSimpleName(MODE_PRIVATE);
+ invoke.arguments().add(invokeMode);
+ }
+ else if (getCodeGeneratorData().getAssociatedType().equals(
+ CodeGeneratorDataBasedOnLayout.TYPE.FRAGMENT))
+ {
+ SimpleName invokeMode = method.getAST().newSimpleName(MODE_PRIVATE);
+ SimpleName activityRef = method.getAST().newSimpleName("Activity");
+ QualifiedName qName = method.getAST().newQualifiedName(activityRef, invokeMode);
+
+ MethodInvocation activityInvoke = method.getAST().newMethodInvocation();
+ SimpleName activityMethodName = method.getAST().newSimpleName("getActivity");
+ activityInvoke.setName(activityMethodName);
+ invoke.setExpression(activityInvoke);
+
+ invoke.arguments().add(qName);
+ }
+
+ getPreferencefragment.setInitializer(invoke);
+ getPreferencefragment.setName(preferencesVarName);
+ VariableDeclarationStatement variableDeclarationStatement =
+ method.getAST().newVariableDeclarationStatement(getPreferencefragment);
+ SimpleName sharedPreferencesName = method.getAST().newSimpleName(SHARED_PREFERENCES);
+ SimpleType sharedPreferencesType = method.getAST().newSimpleType(sharedPreferencesName);
+ variableDeclarationStatement.setType(sharedPreferencesType);
+ method.getBody().statements().add(variableDeclarationStatement);
+ }
+ return preferencesVarName;
+ }
+
+ /**
+ * Add code to save state: $bundleNameOnSaveMethod.$putMethodName("$nodeId", $nodeId.$getUIStateMethodName());
+ * Add code to restore state: $nodeId.$setMethodName($bundleNameOnRestoreMethod.$getBundleState("$nodeId"));
+ */
+ @SuppressWarnings("unchecked")
+ public void insertSaveRestoreCode(MethodDeclaration onSaveInstanceStateFoundMethod,
+ MethodDeclaration onRestoreInstanceFoundMethod, String bundleNameOnSaveMethod,
+ String bundleNameOnRestoreMethod, LayoutNode node, String putMethodName,
+ String setMethodName, String getBundleState, String getUIStateMethodName)
+ {
+ MethodInvocation getGUIStateInvocation;
+ getGUIStateInvocation =
+ addMethodToRetrieveUIState(onSaveInstanceStateFoundMethod, node,
+ getUIStateMethodName);
+
+ insertPutMethod(onSaveInstanceStateFoundMethod, node, putMethodName,
+ bundleNameOnSaveMethod, getGUIStateInvocation);
+
+ MethodInvocation getBundleStateInvocation =
+ onRestoreInstanceFoundMethod.getAST().newMethodInvocation();
+
+ SimpleName getName = onSaveInstanceStateFoundMethod.getAST().newSimpleName(getBundleState);
+ getBundleStateInvocation.setName(getName);
+ SimpleName getExpr =
+ onSaveInstanceStateFoundMethod.getAST().newSimpleName(bundleNameOnRestoreMethod);
+ getBundleStateInvocation.setExpression(getExpr);
+ StringLiteral id = onSaveInstanceStateFoundMethod.getAST().newStringLiteral();
+ id.setLiteralValue(node.getNodeId());
+ getBundleStateInvocation.arguments().add(id);
+
+ String stateType = node.getProperty(ViewProperties.ViewStateValueType);
+
+ if (stateType != null)
+ {
+ if (stateType.equals(Integer.class.toString()))
+ {
+ NumberLiteral defaultValue =
+ onSaveInstanceStateFoundMethod.getAST().newNumberLiteral();
+ getBundleStateInvocation.arguments().add(defaultValue);
+ }
+ else if (stateType.equals(Boolean.class.toString()))
+ {
+ BooleanLiteral defaultValue =
+ onSaveInstanceStateFoundMethod.getAST().newBooleanLiteral(false);
+ getBundleStateInvocation.arguments().add(defaultValue);
+ }
+ }
+
+ insertSetMethod(onRestoreInstanceFoundMethod, node, setMethodName, getBundleStateInvocation);
+ }
+
+ /**
+ * Insert method in the format $nodeId.$getUIStateMethodName()
+ * @param onSaveInstanceStateFoundMethod
+ * @param node
+ * @param getUIStateMethodName
+ * @return
+ */
+ public MethodInvocation addMethodToRetrieveUIState(
+ MethodDeclaration onSaveInstanceStateFoundMethod, LayoutNode node,
+ String getUIStateMethodName)
+ {
+ MethodInvocation retrieveUIStateMethod =
+ onSaveInstanceStateFoundMethod.getAST().newMethodInvocation();
+ SimpleName guiId = onSaveInstanceStateFoundMethod.getAST().newSimpleName(node.getNodeId());
+ retrieveUIStateMethod.setExpression(guiId);
+ SimpleName getText =
+ onSaveInstanceStateFoundMethod.getAST().newSimpleName(getUIStateMethodName);
+ retrieveUIStateMethod.setName(getText);
+ return retrieveUIStateMethod;
+ }
+
+ /**
+ * Add method in the format savedInstanceState.putXXXX($nodeid, $getGUIStateInvocation);
+ * @param onSaveInstanceStateFoundMethod
+ * @param node
+ * @param methodName
+ * @param bundleName
+ * @param getGUIStateInvocation
+ */
+ @SuppressWarnings("unchecked")
+ public void insertPutMethod(MethodDeclaration onSaveInstanceStateFoundMethod, LayoutNode node,
+ String methodName, String bundleName, MethodInvocation getGUIStateInvocation)
+ {
+ MethodInvocation putMethod = onSaveInstanceStateFoundMethod.getAST().newMethodInvocation();
+ SimpleName putMethodName =
+ onSaveInstanceStateFoundMethod.getAST().newSimpleName(methodName);
+ putMethod.setName(putMethodName);
+ SimpleName bundle = onSaveInstanceStateFoundMethod.getAST().newSimpleName(bundleName);
+ putMethod.setExpression(bundle);
+ StringLiteral id = onSaveInstanceStateFoundMethod.getAST().newStringLiteral();
+ id.setLiteralValue(node.getNodeId());
+ putMethod.arguments().add(id);
+
+ putMethod.arguments().add(getGUIStateInvocation);
+
+ ExpressionStatement exprSt =
+ onSaveInstanceStateFoundMethod.getAST().newExpressionStatement(putMethod);
+ int commitPosition =
+ findMethodInvocation(onSaveInstanceStateFoundMethod.getBody().statements(), bundle,
+ "commit");
+ onSaveInstanceStateFoundMethod.getBody().statements().add(commitPosition, exprSt);
+ }
+
+ private int findMethodInvocation(List<?> statements, final SimpleName bundle,
+ final String methodName)
+ {
+ int position = -1;
+ int i = 0;
+ while ((i < statements.size()) && (position == -1))
+ {
+ Statement statement = (Statement) statements.get(i);
+ if (statement instanceof ExpressionStatement)
+ {
+ ExpressionStatement expressionSt = (ExpressionStatement) statement;
+ Expression expression = expressionSt.getExpression();
+ if (expression instanceof MethodInvocation)
+ {
+ MethodInvocation method = (MethodInvocation) expression;
+ if (method.getName().getIdentifier().equals(methodName)
+ && (method.getExpression() instanceof SimpleName))
+ {
+ SimpleName name = (SimpleName) method.getExpression();
+ if (name.getIdentifier().equals(bundle.getIdentifier()))
+ {
+ position = i;
+ }
+
+ }
+
+ }
+
+ }
+ i++;
+ }
+ if (position == -1)
+ {
+ position = i;
+ }
+
+ return position;
+ }
+
+ /**
+ * Add method in the format $nodeId.setXXXXX($getBundleStateInvocation);
+ * @param onRestoreInstanceStateFoundMethod
+ * @param node
+ * @param methodName
+ * @param bundleName
+ * @param getBundleStateInvocation
+ */
+ @SuppressWarnings("unchecked")
+ public void insertSetMethod(MethodDeclaration onRestoreInstanceStateFoundMethod,
+ LayoutNode node, String methodName, MethodInvocation getBundleStateInvocation)
+ {
+ MethodInvocation setMethod =
+ onRestoreInstanceStateFoundMethod.getAST().newMethodInvocation();
+ SimpleName setMethodName =
+ onRestoreInstanceStateFoundMethod.getAST().newSimpleName(methodName);
+ setMethod.setName(setMethodName);
+ SimpleName id = onRestoreInstanceStateFoundMethod.getAST().newSimpleName(node.getNodeId());
+ setMethod.setExpression(id);
+
+ //add in the end of the method
+ setMethod.arguments().add(getBundleStateInvocation);
+
+ ExpressionStatement exprSt =
+ onRestoreInstanceStateFoundMethod.getAST().newExpressionStatement(setMethod);
+ onRestoreInstanceStateFoundMethod.getBody().statements().add(exprSt);
+ }
+}
diff --git a/src/plugins/android.codeutils/src/com/motorola/studio/android/generateviewbylayout/codegenerators/SeekBarCodeGenerator.java b/src/plugins/android.codeutils/src/com/motorola/studio/android/generateviewbylayout/codegenerators/SeekBarCodeGenerator.java
new file mode 100644
index 0000000..dc8d0ff
--- /dev/null
+++ b/src/plugins/android.codeutils/src/com/motorola/studio/android/generateviewbylayout/codegenerators/SeekBarCodeGenerator.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.generateviewbylayout.codegenerators;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.SubMonitor;
+import org.eclipse.jdt.core.JavaModelException;
+import org.eclipse.jdt.core.dom.Block;
+import org.eclipse.jdt.core.dom.MethodDeclaration;
+import org.eclipse.jdt.core.dom.Modifier.ModifierKeyword;
+import org.eclipse.jdt.core.dom.PrimitiveType;
+import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
+import org.eclipse.jdt.core.dom.TypeDeclaration;
+
+import com.motorola.studio.android.codeutils.i18n.CodeUtilsNLS;
+import com.motorola.studio.android.generateviewbylayout.JavaViewBasedOnLayoutModifierConstants;
+import com.motorola.studio.android.generateviewbylayout.model.CodeGeneratorDataBasedOnLayout;
+import com.motorola.studio.android.generateviewbylayout.model.LayoutNode;
+
+/**
+ * Responsible to add methods to deal with SeekBar events
+ */
+public class SeekBarCodeGenerator extends AbstractLayoutCodeGenerator
+{
+ /**
+ * @param codeGeneratorData
+ * @param onCreateDeclaration
+ * @param typeDeclaration
+ */
+ public SeekBarCodeGenerator(CodeGeneratorDataBasedOnLayout codeGeneratorData,
+ MethodDeclaration onCreateDeclaration, TypeDeclaration typeDeclaration)
+ {
+ super(codeGeneratorData, onCreateDeclaration, typeDeclaration);
+ }
+
+ @Override
+ public void generateCode(IProgressMonitor monitor) throws JavaModelException
+ {
+ addOnSeekBarChangeListener(monitor);
+ }
+
+ /**
+ * Adds methods for SeekBar's events
+ *
+ * <br>
+ * GENERATED_CODE_FORMAT:
+ * <br>
+ * $GUI_ID.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
+ * <br>
+ * void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {}
+ * <br>
+ * void onStartTrackingTouch(SeekBar seekBar) {}
+ * <br>
+ * void onStopTrackingTouch(SeekBar seekBar) {}
+ * <br>
+ * });
+ */
+ private void addOnSeekBarChangeListener(IProgressMonitor monitor) throws JavaModelException
+ {
+ SubMonitor subMonitor = SubMonitor.convert(monitor);
+ subMonitor.beginTask(CodeUtilsNLS.JavaViewBasedOnLayoutModifier_AddingSeekbarHandler,
+ codeGeneratorData.getGuiItems().size());
+ for (LayoutNode node : codeGeneratorData.getGuiItems())
+ {
+ if (node.shouldInsertCode()
+ && node.getNodeType().equals(JavaViewBasedOnLayoutModifierConstants.SEEK_BAR))
+ {
+ boolean containMethodDeclared =
+ checkIfInvokeMethodIsDeclared(
+ node,
+ JavaViewBasedOnLayoutModifierConstants.SET_ON_SEEK_BAR_CHANGE_LISTENER);
+ if (!containMethodDeclared)
+ {
+ List<MethodDeclaration> declarations = new ArrayList<MethodDeclaration>();
+
+ List<SingleVariableDeclaration> parameters1 =
+ new ArrayList<SingleVariableDeclaration>();
+ SingleVariableDeclaration param1 =
+ createVariableDeclarationFromStrings(
+ JavaViewBasedOnLayoutModifierConstants.SEEK_BAR,
+ JavaViewBasedOnLayoutModifierConstants.SEEK_BAR_VARIABLE); //$NON-NLS-1$
+ parameters1.add(param1);
+ SingleVariableDeclaration param2 =
+ createVariableDeclarationPrimitiveCode(PrimitiveType.INT,
+ JavaViewBasedOnLayoutModifierConstants.PROGRESS_VARIABLE); //$NON-NLS-1$
+ parameters1.add(param2);
+ SingleVariableDeclaration param3 =
+ createVariableDeclarationPrimitiveCode(PrimitiveType.BOOLEAN,
+ JavaViewBasedOnLayoutModifierConstants.FROM_USER_VARIABLE); //$NON-NLS-1$
+ parameters1.add(param3);
+ MethodDeclaration methodDeclaration1 =
+ addMethodDeclaration(ModifierKeyword.PUBLIC_KEYWORD,
+ JavaViewBasedOnLayoutModifierConstants.ON_PROGRESS_CHANGED,
+ PrimitiveType.VOID, parameters1);
+ Block block1 = onCreateDeclaration.getAST().newBlock();
+ methodDeclaration1.setBody(block1);
+ declarations.add(methodDeclaration1);
+
+ List<SingleVariableDeclaration> parameters2 =
+ new ArrayList<SingleVariableDeclaration>();
+ SingleVariableDeclaration param1Method2 =
+ createVariableDeclarationFromStrings(
+ JavaViewBasedOnLayoutModifierConstants.SEEK_BAR,
+ JavaViewBasedOnLayoutModifierConstants.SEEK_BAR_VARIABLE); //$NON-NLS-1$
+ parameters2.add(param1Method2);
+ MethodDeclaration methodDeclaration2 =
+ addMethodDeclaration(ModifierKeyword.PUBLIC_KEYWORD,
+ JavaViewBasedOnLayoutModifierConstants.ON_START_TRACKING_TOUCH,
+ PrimitiveType.VOID, parameters2);
+ Block block2 = onCreateDeclaration.getAST().newBlock();
+ methodDeclaration2.setBody(block2);
+ declarations.add(methodDeclaration2);
+
+ List<SingleVariableDeclaration> parameters3 =
+ new ArrayList<SingleVariableDeclaration>();
+ SingleVariableDeclaration param1Method3 =
+ createVariableDeclarationFromStrings(
+ JavaViewBasedOnLayoutModifierConstants.SEEK_BAR,
+ JavaViewBasedOnLayoutModifierConstants.SEEK_BAR_VARIABLE); //$NON-NLS-1$
+ parameters3.add(param1Method3);
+ MethodDeclaration methodDeclaration3 =
+ addMethodDeclaration(ModifierKeyword.PUBLIC_KEYWORD,
+ JavaViewBasedOnLayoutModifierConstants.ON_STOP_TRACKING_TOUCH,
+ PrimitiveType.VOID, parameters3);
+ Block block3 = onCreateDeclaration.getAST().newBlock();
+ methodDeclaration3.setBody(block3);
+ declarations.add(methodDeclaration3);
+
+ addMethodInvocationToListenerHandler(node.getNodeId(),
+ JavaViewBasedOnLayoutModifierConstants.SET_ON_SEEK_BAR_CHANGE_LISTENER,
+ JavaViewBasedOnLayoutModifierConstants.SEEK_BAR,
+ JavaViewBasedOnLayoutModifierConstants.ON_SEEK_BAR_CHANGE_LISTENER,
+ declarations);
+ }
+ }
+ subMonitor.worked(1);
+ }
+ }
+
+}
diff --git a/src/plugins/android.codeutils/src/com/motorola/studio/android/generateviewbylayout/codegenerators/SpinnerCodeGenerator.java b/src/plugins/android.codeutils/src/com/motorola/studio/android/generateviewbylayout/codegenerators/SpinnerCodeGenerator.java
new file mode 100644
index 0000000..e28911e
--- /dev/null
+++ b/src/plugins/android.codeutils/src/com/motorola/studio/android/generateviewbylayout/codegenerators/SpinnerCodeGenerator.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.generateviewbylayout.codegenerators;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.SubMonitor;
+import org.eclipse.jdt.core.JavaModelException;
+import org.eclipse.jdt.core.dom.Block;
+import org.eclipse.jdt.core.dom.MethodDeclaration;
+import org.eclipse.jdt.core.dom.Modifier.ModifierKeyword;
+import org.eclipse.jdt.core.dom.PrimitiveType;
+import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
+import org.eclipse.jdt.core.dom.TypeDeclaration;
+
+import com.motorola.studio.android.codeutils.i18n.CodeUtilsNLS;
+import com.motorola.studio.android.generateviewbylayout.JavaViewBasedOnLayoutModifierConstants;
+import com.motorola.studio.android.generateviewbylayout.model.CodeGeneratorDataBasedOnLayout;
+import com.motorola.studio.android.generateviewbylayout.model.LayoutNode;
+
+/**
+ * Responsible to add methods to deal with Spinner events
+ */
+public class SpinnerCodeGenerator extends AbstractLayoutCodeGenerator
+{
+ /**
+ * @param codeGeneratorData
+ * @param onCreateDeclaration
+ * @param typeDeclaration
+ */
+ public SpinnerCodeGenerator(CodeGeneratorDataBasedOnLayout codeGeneratorData,
+ MethodDeclaration onCreateDeclaration, TypeDeclaration typeDeclaration)
+ {
+ super(codeGeneratorData, onCreateDeclaration, typeDeclaration);
+ }
+
+ @Override
+ public void generateCode(IProgressMonitor monitor) throws JavaModelException
+ {
+ addOnItemSpinnerHandler(monitor);
+ }
+
+ /**
+ * Adds methods to handle with Spinner (combo box) events
+ *
+ * <br>
+ * GENERATED_CODE_FORMAT:
+ * <br>
+ * $GUI_ID.setOnItemSelectedListener(new OnItemSelectedListener() {
+ * <br>
+ * public void onItemSelected(AdapterView<?> parentView, View selectedItemView, int position, long id) {
+ * <br>
+ * }
+ * <br>
+ * public void onNothingSelected(AdapterView<?> parentView) {
+ * <br>
+ * }
+ * <br>
+ * });
+ */
+ private void addOnItemSpinnerHandler(IProgressMonitor monitor) throws JavaModelException
+ {
+ SubMonitor subMonitor = SubMonitor.convert(monitor);
+ subMonitor.beginTask(CodeUtilsNLS.JavaViewBasedOnLayoutModifier_AddingSpinnerHandler,
+ codeGeneratorData.getGuiItems().size());
+ for (LayoutNode node : codeGeneratorData.getGuiItems())
+ {
+ if (node.shouldInsertCode()
+ && node.getNodeType().equals(LayoutNode.LayoutNodeViewType.Spinner.name()))
+ {
+ boolean containMethodDeclared =
+ checkIfInvokeMethodIsDeclared(
+ node,
+ JavaViewBasedOnLayoutModifierConstants.SET_ON_ITEM_SELECTED_LISTENER);
+ if (!containMethodDeclared)
+ {
+ List<MethodDeclaration> declarations = new ArrayList<MethodDeclaration>();
+ List<SingleVariableDeclaration> parameters1 =
+ new ArrayList<SingleVariableDeclaration>();
+ SingleVariableDeclaration param1 =
+ createWildcardTypeVariableDeclarationFromStrings(
+ JavaViewBasedOnLayoutModifierConstants.ADAPTER_VIEW,
+ JavaViewBasedOnLayoutModifierConstants.PARENT_VIEW);
+ parameters1.add(param1);
+ SingleVariableDeclaration param2 =
+ createVariableDeclarationFromStrings(
+ JavaViewBasedOnLayoutModifierConstants.VIEW_CLASS,
+ JavaViewBasedOnLayoutModifierConstants.SELECTED_ITEM_VIEW);
+ parameters1.add(param2);
+ SingleVariableDeclaration param3 =
+ createVariableDeclarationPrimitiveCode(PrimitiveType.INT,
+ JavaViewBasedOnLayoutModifierConstants.POSITION); //$NON-NLS-1$
+ parameters1.add(param3);
+ SingleVariableDeclaration param4 =
+ createVariableDeclarationPrimitiveCode(PrimitiveType.LONG,
+ JavaViewBasedOnLayoutModifierConstants.ROW); //$NON-NLS-1$ //$NON-NLS-2$
+ parameters1.add(param4);
+ MethodDeclaration methodDeclaration1 =
+ addMethodDeclaration(ModifierKeyword.PUBLIC_KEYWORD,
+ JavaViewBasedOnLayoutModifierConstants.ON_ITEM_SELECTED,
+ PrimitiveType.VOID, parameters1);
+ Block block1 = onCreateDeclaration.getAST().newBlock();
+ methodDeclaration1.setBody(block1);
+ declarations.add(methodDeclaration1);
+
+ List<SingleVariableDeclaration> parameters2 =
+ new ArrayList<SingleVariableDeclaration>();
+ SingleVariableDeclaration param1Method2 =
+ createWildcardTypeVariableDeclarationFromStrings(
+ JavaViewBasedOnLayoutModifierConstants.ADAPTER_VIEW,
+ JavaViewBasedOnLayoutModifierConstants.PARENT_VIEW);
+ parameters2.add(param1Method2);
+ MethodDeclaration methodDeclaration2 =
+ addMethodDeclaration(ModifierKeyword.PUBLIC_KEYWORD,
+ JavaViewBasedOnLayoutModifierConstants.ON_NOTHING_SELECTED,
+ PrimitiveType.VOID, parameters2);
+ Block block2 = onCreateDeclaration.getAST().newBlock();
+ methodDeclaration2.setBody(block2);
+ declarations.add(methodDeclaration2);
+
+ addMethodInvocationToListenerHandler(node.getNodeId(),
+ JavaViewBasedOnLayoutModifierConstants.SET_ON_ITEM_SELECTED_LISTENER,
+ JavaViewBasedOnLayoutModifierConstants.ADAPTER_VIEW,
+ JavaViewBasedOnLayoutModifierConstants.ON_ITEM_SELECTED_LISTENER,
+ declarations);
+ }
+ }
+ subMonitor.worked(1);
+ }
+ }
+}
diff --git a/src/plugins/android.codeutils/src/com/motorola/studio/android/generateviewbylayout/model/CheckboxNode.java b/src/plugins/android.codeutils/src/com/motorola/studio/android/generateviewbylayout/model/CheckboxNode.java
new file mode 100644
index 0000000..d60c5f3
--- /dev/null
+++ b/src/plugins/android.codeutils/src/com/motorola/studio/android/generateviewbylayout/model/CheckboxNode.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.generateviewbylayout.model;
+
+/**
+ * Extends {@link LayoutNode} to describe save/restore method names and the name of the node type
+ * (specific from CheckBox)
+ */
+public class CheckboxNode extends LayoutNode
+{
+
+ public CheckboxNode()
+ {
+ properties.put(ViewProperties.ViewStateSetMethod, ViewSetMethods.setChecked.name());
+ properties.put(ViewProperties.ViewStateGetMethod, ViewGetMethods.isChecked.name());
+ properties.put(ViewProperties.PreferenceSetMethod, PreferenceSetMethods.putBoolean.name());
+ properties.put(ViewProperties.PreferenceGetMethod, PreferenceGetMethods.getBoolean.name());
+ properties.put(ViewProperties.ViewStateValueType, Boolean.class.toString());
+ }
+
+ @Override
+ public String getNodeType()
+ {
+ return LayoutNodeViewType.CheckBox.name();
+ }
+
+}
diff --git a/src/plugins/android.codeutils/src/com/motorola/studio/android/generateviewbylayout/model/CodeGeneratorDataBasedOnLayout.java b/src/plugins/android.codeutils/src/com/motorola/studio/android/generateviewbylayout/model/CodeGeneratorDataBasedOnLayout.java
new file mode 100644
index 0000000..481ecb9
--- /dev/null
+++ b/src/plugins/android.codeutils/src/com/motorola/studio/android/generateviewbylayout/model/CodeGeneratorDataBasedOnLayout.java
@@ -0,0 +1,260 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.generateviewbylayout.model;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.core.resources.IResource;
+import org.eclipse.jdt.core.ICompilationUnit;
+import org.eclipse.jdt.core.dom.CompilationUnit;
+
+import com.motorola.studio.android.common.exception.AndroidException;
+import com.motorola.studio.android.generatecode.AbstractCodeGeneratorData;
+import com.motorola.studio.android.generatecode.BasicCodeVisitor;
+
+/**
+ * Model representing the code generator data needed to generate code for layout
+ * You MUST call init before using the object.
+ */
+public class CodeGeneratorDataBasedOnLayout extends AbstractCodeGeneratorData
+{
+
+ private LayoutFile layoutFile;
+
+ private JavaLayoutData javaLayoutData;
+
+ /**
+ * Creates {@link LayoutFile} representation for the layout xml
+ * @param layoutName name of the layout (to appear on dialog to generate code)
+ * @param layout full file path to layout xml
+ * @throws AndroidException if an error occurs parsing layout xml
+ */
+ public void init(String layoutName, File layout) throws AndroidException
+ {
+ layoutFile = new LayoutFile(layoutName, layout);
+ }
+
+ private void refreshDeclared()
+ {
+ for (LayoutNode node : layoutFile.getNodes())
+ {
+ node.setAlreadyDeclaredInCode(getJavaLayoutData().getDeclaredViewIdsOnCode().contains(
+ node.getNodeId()));
+ node.setAlreadySaved(getJavaLayoutData().getSavedViewIds().contains(node.getNodeId()));
+ node.setAlreadyRestored(getJavaLayoutData().getRestoredViewIds().contains(
+ node.getNodeId()));
+ }
+ }
+
+ /**
+ * Get GUI items (not layouts or fragment placeholders)
+ * that are not declared in the code yet
+ * @return list of GUI items from layout file (only ones with id set).
+ */
+ public List<LayoutNode> getGuiItems()
+ {
+ List<LayoutNode> guiItems = getGUIItems(true);
+ return guiItems;
+ }
+
+ /**
+ * Get the list of layout nodes available into layout xml
+ * @param doNotshowAlreadyDeclared if true, remove the items already declared, if false include them in the result list
+ * @return list of layout nodes
+ */
+ public List<LayoutNode> getGUIItems(boolean doNotshowAlreadyDeclared)
+ {
+ List<LayoutNode> guiItems = new ArrayList<LayoutNode>();
+
+ for (LayoutNode node : layoutFile.getNodes())
+ {
+ if (node.isGUIItem() && (node.getNodeId() != null))
+ {
+ if (doNotshowAlreadyDeclared)
+ {
+ if (!node.isAlreadyDeclaredInCode())
+ {
+ guiItems.add(node);
+ }
+ else
+ {
+ //do not inserted already declared item
+ node.setInsertCode(false);
+ }
+ }
+ else
+ {
+ guiItems.add(node);
+ }
+ }
+ }
+ return guiItems;
+ }
+
+ /**
+ * Get GUI items (not layouts)
+ * that are not declared in the code yet for the dialog UI.
+ *
+ * @return list of GUI items from layout file (with or without id set).
+ */
+ public List<LayoutNode> getGUIItemsForUI()
+ {
+ List<LayoutNode> guiItems = new ArrayList<LayoutNode>();
+
+ for (LayoutNode node : layoutFile.getNodes())
+ {
+ if (node.isGUIItem())
+ {
+ if (!node.isAlreadyDeclaredInCode())
+ {
+ guiItems.add(node);
+ }
+ else
+ {
+ //do not inserted already declared item
+ node.setInsertCode(false);
+ }
+ }
+ }
+ return guiItems;
+ }
+
+ /**
+ * Get fragments (not layouts or GUI items)
+ * that are not declared in the code yet
+ * @return
+ */
+ public List<LayoutNode> getFragments()
+ {
+ List<LayoutNode> fragmentItems = getFragments(true);
+ return fragmentItems;
+ }
+
+ private List<LayoutNode> getFragments(boolean doNotshowAlreadyDeclared)
+ {
+ List<LayoutNode> fragmentItems = new ArrayList<LayoutNode>();
+ for (LayoutNode node : layoutFile.getNodes())
+ {
+ if (node.isFragmentPlaceholder() && (node.getNodeId() != null))
+ {
+ if (doNotshowAlreadyDeclared)
+ {
+ if (!node.isAlreadyDeclaredInCode())
+ {
+ fragmentItems.add(node);
+ }
+ else
+ {
+ //do not inserted already declared item
+ node.setInsertCode(false);
+ }
+ }
+ else
+ {
+ fragmentItems.add(node);
+ }
+ }
+ }
+ return fragmentItems;
+ }
+
+ /**
+ * Get layout items (not GUI items or fragment placeholders)
+ * that are not declared in the code yet
+ * @return
+ */
+ public List<LayoutNode> getLayoutItems()
+ {
+ List<LayoutNode> layoutItems = getLayoutItems(true);
+ return layoutItems;
+ }
+
+ private List<LayoutNode> getLayoutItems(boolean doNotshowAlreadyDeclared)
+ {
+ List<LayoutNode> layoutItems = new ArrayList<LayoutNode>();
+ for (LayoutNode node : layoutFile.getNodes())
+ {
+ if (node.isLayout() && (node.getNodeId() != null))
+ {
+ if (doNotshowAlreadyDeclared)
+ {
+ if (!node.isAlreadyDeclaredInCode())
+ {
+ layoutItems.add(node);
+ }
+ else
+ {
+ //do not inserted already declared item
+ node.setInsertCode(false);
+ }
+ }
+ else
+ {
+ layoutItems.add(node);
+ }
+ }
+ }
+ return layoutItems;
+ }
+
+ /**
+ * @return the javaLayoutData
+ */
+ public JavaLayoutData getJavaLayoutData()
+ {
+ return javaLayoutData;
+ }
+
+ /**
+ * @param javaLayoutData the javaLayoutData to set
+ */
+ public void setJavaLayoutData(JavaLayoutData javaLayoutData)
+ {
+ this.javaLayoutData = javaLayoutData;
+ refreshDeclared();
+ }
+
+ public LayoutFile getLayoutFile()
+ {
+ return layoutFile;
+ }
+
+ @Override
+ public IResource getResource()
+ {
+ return getJavaLayoutData().getCompUnitAstNode().getJavaElement().getResource();
+ }
+
+ @Override
+ public ICompilationUnit getICompilationUnit()
+ {
+ return getJavaLayoutData().getCompUnit();
+ }
+
+ @Override
+ public CompilationUnit getCompilationUnit()
+ {
+ return getJavaLayoutData().getCompUnitAstNode();
+ }
+
+ @Override
+ public BasicCodeVisitor getAbstractCodeVisitor()
+ {
+ return getJavaLayoutData().getVisitor();
+ }
+}
diff --git a/src/plugins/android.codeutils/src/com/motorola/studio/android/generateviewbylayout/model/EditTextNode.java b/src/plugins/android.codeutils/src/com/motorola/studio/android/generateviewbylayout/model/EditTextNode.java
new file mode 100644
index 0000000..b4e77b4
--- /dev/null
+++ b/src/plugins/android.codeutils/src/com/motorola/studio/android/generateviewbylayout/model/EditTextNode.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.generateviewbylayout.model;
+
+/**
+ * Extends {@link LayoutNode} to describe save/restore method names and the name of the node type
+ * (specific from EditText)
+ */
+public class EditTextNode extends LayoutNode
+{
+
+ public EditTextNode()
+ {
+ properties.put(ViewProperties.ViewStateSetMethod, ViewSetMethods.setText.name());
+ properties.put(ViewProperties.ViewStateGetMethod, ViewGetMethods.getText.name());
+ properties.put(ViewProperties.PreferenceSetMethod, PreferenceSetMethods.putString.name());
+ properties.put(ViewProperties.PreferenceGetMethod, PreferenceGetMethods.getString.name());
+ properties.put(ViewProperties.ViewStateValueType, String.class.toString());
+ }
+
+ @Override
+ public String getNodeType()
+ {
+ return LayoutNodeViewType.EditText.name();
+ }
+
+}
diff --git a/src/plugins/android.codeutils/src/com/motorola/studio/android/generateviewbylayout/model/JavaLayoutData.java b/src/plugins/android.codeutils/src/com/motorola/studio/android/generateviewbylayout/model/JavaLayoutData.java
new file mode 100644
index 0000000..e8e8c8b
--- /dev/null
+++ b/src/plugins/android.codeutils/src/com/motorola/studio/android/generateviewbylayout/model/JavaLayoutData.java
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.generateviewbylayout.model;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import org.eclipse.jdt.core.ICompilationUnit;
+import org.eclipse.jdt.core.dom.CompilationUnit;
+
+import com.motorola.studio.android.generatecode.JDTUtils;
+import com.motorola.studio.android.generateviewbylayout.GenerateCodeBasedOnLayoutVisitor;
+
+/**
+ * Class encapsulating data from java code such as already declared IDs and the compilation unit.
+ * It serves as a bridge to generate code inside activity/fragment based on layout
+ */
+public class JavaLayoutData
+{
+ private final Set<String> declaredViewIdsOnCode = new HashSet<String>();
+
+ private final Set<String> savedViewIds = new HashSet<String>();
+
+ private final Set<String> restoredViewIds = new HashSet<String>();
+
+ private ICompilationUnit compUnit;
+
+ private CompilationUnit compUnitAstNode;
+
+ /**
+ * If type is fragment, there may be an inflated view name.
+ * This will be used to call findViewById inside fragments.
+ *
+ * Null in these cases:
+ * 1) if fragment does not use it (nofify it is not possible to fill fragment by layout), or
+ * 2) if it is an activity.
+ */
+ private String inflatedViewName = null;
+
+ private GenerateCodeBasedOnLayoutVisitor visitor;
+
+ /**
+ * @return the inflatedViewName
+ */
+ public String getInflatedViewName()
+ {
+ return inflatedViewName;
+ }
+
+ /**
+ * @param inflatedViewName the inflatedViewName to set
+ */
+ public void setInflatedViewName(String inflatedViewName)
+ {
+ this.inflatedViewName = inflatedViewName;
+ }
+
+ /**
+ * @param declaredViewIdsOnCode the declaredViewIdsOnCode to set
+ */
+ public void setDeclaredViewIdsOnCode(Set<String> declaredViewIdsOnCode)
+ {
+ this.declaredViewIdsOnCode.clear();
+ this.declaredViewIdsOnCode.addAll(declaredViewIdsOnCode);
+ }
+
+ /**
+ * @return the declaredViewIdsOnCode
+ */
+ public Set<String> getDeclaredViewIdsOnCode()
+ {
+ return declaredViewIdsOnCode;
+ }
+
+ /**
+ * @return the visitor
+ */
+ public GenerateCodeBasedOnLayoutVisitor getVisitor()
+ {
+ return visitor;
+ }
+
+ /**
+ * @param visitor the visitor to set
+ */
+ public void setVisitor(GenerateCodeBasedOnLayoutVisitor visitor)
+ {
+ this.visitor = visitor;
+ }
+
+ /**
+ * @return the compUnit
+ */
+ public ICompilationUnit getCompUnit()
+ {
+ return compUnit;
+ }
+
+ /**
+ * Item required to write AST
+ * @param compUnit the compUnit to set
+ */
+ public void setCompUnit(ICompilationUnit compUnit)
+ {
+ this.compUnit = compUnit;
+ }
+
+ /**
+ * @return the compUnitAstNode
+ */
+ public CompilationUnit getCompUnitAstNode()
+ {
+ return compUnitAstNode;
+ }
+
+ /**
+ * @param compUnitAstNode the compUnitAstNode to set
+ */
+ public void setCompUnitAstNode(CompilationUnit compUnitAstNode)
+ {
+ this.compUnitAstNode = compUnitAstNode;
+ }
+
+ /**
+ *
+ * @return true if AST have at least one error (warnings are not considered), false otherwise
+ */
+ public boolean hasErrorInCompilationUnitAst()
+ {
+ CompilationUnit cpUnit = getCompUnitAstNode();
+ return JDTUtils.hasErrorInCompilationUnitAstUtils(cpUnit);
+ }
+
+ public void setSavedViewIds(Set<String> savedViewIds)
+ {
+ this.savedViewIds.clear();
+ this.savedViewIds.addAll(savedViewIds);
+
+ }
+
+ public void setRestoredViewIds(Set<String> restoredViewIds)
+ {
+ this.restoredViewIds.clear();
+ this.restoredViewIds.addAll(restoredViewIds);
+ }
+
+ public Set<String> getSavedViewIds()
+ {
+ return savedViewIds;
+ }
+
+ public Set<String> getRestoredViewIds()
+ {
+ return restoredViewIds;
+ }
+}
diff --git a/src/plugins/android.codeutils/src/com/motorola/studio/android/generateviewbylayout/model/LayoutFile.java b/src/plugins/android.codeutils/src/com/motorola/studio/android/generateviewbylayout/model/LayoutFile.java
new file mode 100644
index 0000000..c87677c
--- /dev/null
+++ b/src/plugins/android.codeutils/src/com/motorola/studio/android/generateviewbylayout/model/LayoutFile.java
@@ -0,0 +1,238 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.generateviewbylayout.model;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.xml.sax.SAXException;
+
+import com.motorola.studio.android.common.exception.AndroidException;
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.generatecode.AndroidXMLFileConstants;
+
+/**
+ * Represents a layout xml data under a Android project
+ */
+public class LayoutFile
+{
+
+ /*
+ * Constants
+ */
+ private static final String FRAGMENT = "fragment";
+
+ private static final String LAYOUT = "Layout";
+
+ /*
+ * Layout variables
+ */
+
+ private final List<LayoutNode> nodes;
+
+ private final String name;
+
+ private final File file;
+
+ public LayoutFile(String layoutName, File layout) throws AndroidException
+ {
+ this.name = layoutName;
+ this.file = layout;
+ nodes = parseDocument(file);
+ }
+
+ /**
+ * @return list of GUI items inside layout XML
+ */
+ public List<LayoutNode> getNodes()
+ {
+ return nodes;
+ }
+
+ /**
+ * @return name of the layout xml (to appear in dialogs/wizards)
+ */
+ public String getName()
+ {
+ return name;
+ }
+
+ /**
+ * @return path to layout xml file
+ */
+ public File getFile()
+ {
+ return file;
+ }
+
+ /**
+ * Parses an IDocument object containing the layout.xml into a DOM
+ *
+ * @param document the IDocument object
+ * @throws SAXException When a parsing error occurs
+ * @throws IOException When a reading error occurs
+ */
+ private static final List<LayoutNode> parseDocument(File f) throws AndroidException
+ {
+ List<LayoutNode> lNodes = new ArrayList<LayoutNode>();
+ DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
+ Document doc = null;
+ try
+ {
+ DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
+ doc = dBuilder.parse(f);
+ doc.getDocumentElement().normalize();
+ }
+ catch (Exception e)
+ {
+ StudioLogger.error(LayoutFile.class, "Error parsing layout: " + e.getMessage());
+ throw new AndroidException(e);
+ }
+
+ NodeList children = doc.getChildNodes();
+
+ visitNodes(children, lNodes);
+ return lNodes;
+ }
+
+ private static final void visitNodes(NodeList children, List<LayoutNode> lNodes)
+ {
+ Node node;
+ for (int i = 0; i < children.getLength(); i++)
+ {
+ node = children.item(i);
+ if ((node != null) && (node.getNodeType() == Node.ELEMENT_NODE))
+ {
+ LayoutNode layNode = readAttributes(node);
+ if (layNode != null)
+ {
+ lNodes.add(layNode);
+ }
+ if (node.hasChildNodes())
+ {
+ visitNodes(node.getChildNodes(), lNodes);
+ }
+ }
+ }
+ }
+
+ private static final LayoutNode readAttributes(Node node)
+ {
+ LayoutNode layoutNode = null;
+ Node id = node.getAttributes().getNamedItem(AndroidXMLFileConstants.ANDROID_ID);
+ if ((id != null) && id.getNodeValue().contains(AndroidXMLFileConstants.IDENTIFIER))
+ {
+ //only treats @+id , i.e, if the id is defined in the user-application.
+ //Using @id is intended for calling ids defined in android (or another application)
+ layoutNode = getLayoutNode(node.getNodeName());
+ layoutNode.setNodeType(node.getNodeName());
+
+ String idText = id.getNodeValue();
+ idText = idText.replace(AndroidXMLFileConstants.IDENTIFIER, "");
+ layoutNode.setNodeId(idText);
+
+ Node onClick =
+ node.getAttributes().getNamedItem(AndroidXMLFileConstants.ANDROID_ON_CLICK);
+ if (onClick != null)
+ {
+ layoutNode.setOnClick(onClick.getNodeValue());
+ }
+
+ if (node.getNodeName().contains(LAYOUT))
+ {
+ layoutNode.setLayout(true);
+ }
+ else
+ {
+ layoutNode.setGUIItem(true);
+ }
+ if (node.getNodeName().toLowerCase().contains(FRAGMENT))
+ {
+ layoutNode.setNodeType("Fragment"); //to avoid problems with mixed case
+ layoutNode.setFragmentPlaceholder(true);
+ Node clazzNameNode =
+ node.getAttributes().getNamedItem(AndroidXMLFileConstants.ANDROID_NAME);
+ if (clazzNameNode != null)
+ {
+ String clazzName = clazzNameNode.getNodeValue();
+ int lastNameInd = clazzName.lastIndexOf(".");
+ if (lastNameInd >= 0)
+ {
+ clazzName = clazzName.substring(lastNameInd + 1);
+ }
+ layoutNode.setClazzName(clazzName);
+ }
+ }
+ }
+ else if (id == null)
+ {
+ // no android:id set
+ layoutNode = new LayoutNode();
+ layoutNode.setNodeType(node.getNodeName());
+
+ if (node.getNodeName().contains(LAYOUT))
+ {
+ layoutNode.setLayout(true);
+ }
+ else
+ {
+ layoutNode.setGUIItem(true);
+ }
+ }
+ return layoutNode;
+ }
+
+ private static LayoutNode getLayoutNode(String nodeName)
+ {
+ LayoutNode node = null;
+
+ if (nodeName.equals(LayoutNode.LayoutNodeViewType.EditText.name()))
+ {
+ node = new EditTextNode();
+ }
+ else if (nodeName.equals(LayoutNode.LayoutNodeViewType.CheckBox.name()))
+ {
+ node = new CheckboxNode();
+ }
+ else if (nodeName.equals(LayoutNode.LayoutNodeViewType.RadioButton.name()))
+ {
+ node = new RadioButtonNode();
+ }
+ else if (nodeName.equals(LayoutNode.LayoutNodeViewType.Spinner.name()))
+ {
+ node = new SpinnerNode();
+ }
+ else if (nodeName.equals(LayoutNode.LayoutNodeViewType.SeekBar.name()))
+ {
+ node = new SeekBarNode();
+ }
+ else
+ {
+ node = new LayoutNode();
+ }
+
+ return node;
+ }
+
+}
diff --git a/src/plugins/android.codeutils/src/com/motorola/studio/android/generateviewbylayout/model/LayoutNode.java b/src/plugins/android.codeutils/src/com/motorola/studio/android/generateviewbylayout/model/LayoutNode.java
new file mode 100644
index 0000000..7f406f2
--- /dev/null
+++ b/src/plugins/android.codeutils/src/com/motorola/studio/android/generateviewbylayout/model/LayoutNode.java
@@ -0,0 +1,276 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.generateviewbylayout.model;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Model representing an Android GUI item that can be inserted into activity / fragment
+ */
+public class LayoutNode
+{
+ /**
+ * Methods available for setting View state (eg.: RadioButton, Spinner, EditText)
+ */
+ protected enum ViewSetMethods
+ {
+ setChecked, setSelected, setText, setSelection, setProgress
+ }
+
+ /**
+ * Methods available for getting View state (eg.: RadioButton, Spinner, EditText)
+ */
+ protected enum ViewGetMethods
+ {
+ isChecked, isSelected, getText, getSelectedItemPosition, getProgress
+ }
+
+ /**
+ * Methods available for Preference putters
+ */
+ protected enum PreferenceSetMethods
+ {
+ putInt, putBoolean, putString
+ }
+
+ /**
+ * Methods available for Preference getters
+ */
+ protected enum PreferenceGetMethods
+ {
+ getInt, getBoolean, getString
+ }
+
+ /**
+ * Available options for save/restore operations
+ */
+ public enum ViewProperties
+ {
+ ViewStateSetMethod, ViewStateGetMethod, PreferenceSetMethod, PreferenceGetMethod,
+ ViewStateValueType
+ }
+
+ /**
+ * Available items to generate code for
+ */
+ public static enum LayoutNodeViewType
+ {
+ Button, ToggleButton, ImageButton, CheckBox, RadioButton, EditText, Spinner, Gallery,
+ RatingBar, SeekBar
+ }
+
+ protected final Map<ViewProperties, String> properties =
+ new HashMap<LayoutNode.ViewProperties, String>();
+
+ private boolean isFragmentPlaceholder = false;
+
+ private boolean isLayout = false;
+
+ private boolean isGUIItem = false;
+
+ private String nodeType;
+
+ private String nodeId;
+
+ private String onClick;
+
+ private boolean insertCode = false;
+
+ private boolean saveState = false;
+
+ private boolean alreadySaved = false;
+
+ private boolean alreadyDeclaredInCode = false;
+
+ private String clazzName; //used for fragments only
+
+ private boolean alreadyRestored;
+
+ /**
+ * @return the clazzName (given by android:name attribute)
+ */
+ public String getClazzName()
+ {
+ return clazzName;
+ }
+
+ /**
+ * @param clazzName the clazzName to set
+ */
+ public void setClazzName(String clazzName)
+ {
+ this.clazzName = clazzName;
+ }
+
+ /**
+ * @return true if it is a &lt;fragment&gt; element, false otherwise
+ */
+ public boolean isFragmentPlaceholder()
+ {
+ return isFragmentPlaceholder;
+ }
+
+ /**
+ * @param isFragmentPlaceholder the isFragmentPlaceholder to set
+ */
+ public void setFragmentPlaceholder(boolean isFragmentPlaceholder)
+ {
+ this.isFragmentPlaceholder = isFragmentPlaceholder;
+ }
+
+ /**
+ * @return true if element is a layout, false otherwise (if it is a GUI item or fragment)
+ */
+ public boolean isLayout()
+ {
+ return isLayout;
+ }
+
+ public void setLayout(boolean isLayout)
+ {
+ this.isLayout = isLayout;
+ }
+
+ /**
+ * @return true if it is a GUI Item (Button, EditText, etc) element
+ */
+ public boolean isGUIItem()
+ {
+ return isGUIItem;
+ }
+
+ public void setGUIItem(boolean isGUIItem)
+ {
+ this.isGUIItem = isGUIItem;
+ }
+
+ /**
+ * Type depends on XML element (eg.: &lt;button&gt;)
+ */
+ public String getNodeType()
+ {
+ return nodeType;
+ }
+
+ public void setNodeType(String nodeType)
+ {
+ this.nodeType = nodeType;
+ }
+
+ /**
+ * @return @return value available on android:id attribute
+ */
+ public String getNodeId()
+ {
+ return nodeId;
+ }
+
+ public void setNodeId(String nodeId)
+ {
+ this.nodeId = nodeId;
+ }
+
+ /**
+ * @return value available on android:onClick attribute
+ */
+ public String getOnClick()
+ {
+ return onClick;
+ }
+
+ public void setOnClick(String onClick)
+ {
+ this.onClick = onClick;
+ }
+
+ /**
+ * @return true if user marked in the dialog/wizard to insert the code for this layout node
+ */
+ public boolean shouldInsertCode()
+ {
+ return insertCode;
+ }
+
+ public void setInsertCode(boolean insertCode)
+ {
+ this.insertCode = insertCode;
+ }
+
+ /**
+ * @return true if the element is already declared on code (according to code visitors)
+ */
+ public boolean isAlreadyDeclaredInCode()
+ {
+ return alreadyDeclaredInCode;
+ }
+
+ /**
+ * @param alreadyDeclaredInCode the alreadyDeclaredInCode to set
+ */
+ public void setAlreadyDeclaredInCode(boolean alreadyDeclaredInCode)
+ {
+ this.alreadyDeclaredInCode = alreadyDeclaredInCode;
+ }
+
+ /**
+ * @return true if need to save/restore state, false otherwise
+ */
+ public boolean getSaveState()
+ {
+ return saveState;
+ }
+
+ /**
+ * @param saveState the saveState to set
+ */
+ public void setSaveState(boolean saveState)
+ {
+ this.saveState = saveState;
+ }
+
+ /**
+ * @return true if already has code to save state
+ */
+ public boolean isAlreadySaved()
+ {
+ return alreadySaved;
+ }
+
+ public void setAlreadySaved(boolean alreadySaved)
+ {
+ this.alreadySaved = alreadySaved;
+ }
+
+ /**
+ * @return true if already has code to restore state
+ */
+ public boolean isAlreadyRestored()
+ {
+ return alreadyRestored;
+ }
+
+ public void setAlreadyRestored(boolean alreadyRestored)
+ {
+ this.alreadyRestored = alreadyRestored;
+ }
+
+ public String getProperty(ViewProperties property)
+ {
+ return properties.get(property);
+ }
+
+}
diff --git a/src/plugins/android.codeutils/src/com/motorola/studio/android/generateviewbylayout/model/RadioButtonNode.java b/src/plugins/android.codeutils/src/com/motorola/studio/android/generateviewbylayout/model/RadioButtonNode.java
new file mode 100644
index 0000000..3496a54
--- /dev/null
+++ b/src/plugins/android.codeutils/src/com/motorola/studio/android/generateviewbylayout/model/RadioButtonNode.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.generateviewbylayout.model;
+
+/**
+ * Extends {@link LayoutNode} to describe save/restore method names and the name of the node type
+ * (specific from RadioButton)
+ */
+public class RadioButtonNode extends LayoutNode
+{
+
+ public RadioButtonNode()
+ {
+ properties.put(ViewProperties.ViewStateSetMethod, ViewSetMethods.setChecked.name());
+ properties.put(ViewProperties.ViewStateGetMethod, ViewGetMethods.isChecked.name());
+ properties.put(ViewProperties.PreferenceSetMethod, PreferenceSetMethods.putBoolean.name());
+ properties.put(ViewProperties.PreferenceGetMethod, PreferenceGetMethods.getBoolean.name());
+ properties.put(ViewProperties.ViewStateValueType, Boolean.class.toString());
+ }
+
+ @Override
+ public String getNodeType()
+ {
+ return LayoutNodeViewType.RadioButton.name();
+ }
+
+}
diff --git a/src/plugins/android.codeutils/src/com/motorola/studio/android/generateviewbylayout/model/SeekBarNode.java b/src/plugins/android.codeutils/src/com/motorola/studio/android/generateviewbylayout/model/SeekBarNode.java
new file mode 100644
index 0000000..cd2caeb
--- /dev/null
+++ b/src/plugins/android.codeutils/src/com/motorola/studio/android/generateviewbylayout/model/SeekBarNode.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.generateviewbylayout.model;
+
+/**
+ * Extends {@link LayoutNode} to describe save/restore method names and the name of the node type
+ * (specific from SeekBar)
+ */
+public class SeekBarNode extends LayoutNode
+{
+
+ public SeekBarNode()
+ {
+ properties.put(ViewProperties.ViewStateSetMethod, ViewSetMethods.setProgress.name());
+ properties.put(ViewProperties.ViewStateGetMethod, ViewGetMethods.getProgress.name());
+ properties.put(ViewProperties.PreferenceSetMethod, PreferenceSetMethods.putInt.name());
+ properties.put(ViewProperties.PreferenceGetMethod, PreferenceGetMethods.getInt.name());
+ properties.put(ViewProperties.ViewStateValueType, Integer.class.toString());
+ }
+
+ @Override
+ public String getNodeType()
+ {
+ return LayoutNodeViewType.SeekBar.name();
+ }
+
+}
diff --git a/src/plugins/android.codeutils/src/com/motorola/studio/android/generateviewbylayout/model/SpinnerNode.java b/src/plugins/android.codeutils/src/com/motorola/studio/android/generateviewbylayout/model/SpinnerNode.java
new file mode 100644
index 0000000..c6c5b38
--- /dev/null
+++ b/src/plugins/android.codeutils/src/com/motorola/studio/android/generateviewbylayout/model/SpinnerNode.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.generateviewbylayout.model;
+
+/**
+ * Extends {@link LayoutNode} to describe save/restore method names and the name of the node type
+ * (specific from Spinner)
+ */
+public class SpinnerNode extends LayoutNode
+{
+
+ public SpinnerNode()
+ {
+ properties.put(ViewProperties.ViewStateSetMethod, ViewSetMethods.setSelection.name());
+ properties.put(ViewProperties.ViewStateGetMethod,
+ ViewGetMethods.getSelectedItemPosition.name());
+ properties.put(ViewProperties.PreferenceSetMethod, PreferenceSetMethods.putInt.name());
+ properties.put(ViewProperties.PreferenceGetMethod, PreferenceGetMethods.getInt.name());
+ properties.put(ViewProperties.ViewStateValueType, Integer.class.toString());
+ }
+
+ @Override
+ public String getNodeType()
+ {
+ return LayoutNodeViewType.Spinner.name();
+ }
+
+}
diff --git a/src/plugins/android.codeutils/src/com/motorola/studio/android/generateviewbylayout/ui/AbstractCodeGeneratorHandler.java b/src/plugins/android.codeutils/src/com/motorola/studio/android/generateviewbylayout/ui/AbstractCodeGeneratorHandler.java
new file mode 100644
index 0000000..9cfdc69
--- /dev/null
+++ b/src/plugins/android.codeutils/src/com/motorola/studio/android/generateviewbylayout/ui/AbstractCodeGeneratorHandler.java
@@ -0,0 +1,295 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.generateviewbylayout.ui;
+
+import java.lang.reflect.InvocationTargetException;
+import java.util.Iterator;
+
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.IAdaptable;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.MultiStatus;
+import org.eclipse.jdt.core.ICompilationUnit;
+import org.eclipse.jdt.core.JavaModelException;
+import org.eclipse.jdt.ui.JavaUI;
+import org.eclipse.jface.dialogs.ErrorDialog;
+import org.eclipse.jface.dialogs.ProgressMonitorDialog;
+import org.eclipse.jface.operation.IRunnableWithProgress;
+import org.eclipse.jface.text.TextSelection;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.window.Window;
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.IFileEditorInput;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.handlers.HandlerUtil;
+import org.eclipse.ui.texteditor.ITextEditor;
+
+import com.motorola.studio.android.codeutils.CodeUtilsActivator;
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.common.log.UsageDataConstants;
+import com.motorola.studio.android.common.utilities.EclipseUtils;
+import com.motorola.studio.android.generatecode.JDTUtils;
+import com.motorola.studio.android.generateviewbylayout.JavaModifierBasedOnLayout;
+
+/**
+ * Abstract command handler to deal with selection of items in the Package Explorer view or Text Editors
+ */
+public abstract class AbstractCodeGeneratorHandler extends AbstractHandler
+{
+
+ protected static SelectionBean resolveSelection(ExecutionEvent event) throws ExecutionException
+ {
+ SelectionBean selectionBean = new SelectionBean();
+ ITextEditor editor = null;
+ IFileEditorInput fileEditorInput = null;
+
+ ISelection selection = HandlerUtil.getCurrentSelection(event);
+
+ // case where the selection comes from the Text Editor
+ if (selection instanceof TextSelection)
+ {
+ editor = (ITextEditor) HandlerUtil.getActiveEditorChecked(event);
+ if (editor.getEditorInput() instanceof IFileEditorInput)
+ {
+ fileEditorInput = (IFileEditorInput) editor.getEditorInput();
+ selectionBean.setJavaFile(fileEditorInput.getFile());
+ }
+ }
+ else if (selection instanceof IStructuredSelection)
+ {
+ Iterator<?> selectionIterator = ((IStructuredSelection) selection).iterator();
+ Object selectedObject = selectionIterator.next();
+
+ // case where the selection comes from the package explorer
+ if (selectedObject instanceof IFile)
+ {
+ selectionBean.setJavaFile((IFile) selectedObject);
+ }
+
+ // again, case where the selection comes from the package explorer
+ else if (selectedObject instanceof ICompilationUnit)
+ {
+ ICompilationUnit compilationUnit = (ICompilationUnit) selectedObject;
+ selectionBean.setJavaFile((IFile) compilationUnit.getResource());
+ }
+
+ // case where the selection comes from a project
+ else if (selectedObject instanceof IAdaptable)
+ {
+ try
+ {
+ IResource resource =
+ (IResource) ((IAdaptable) selectedObject).getAdapter(IResource.class);
+ selectionBean.setJavaProject(resource.getProject());
+ selectionBean.setProject(true);
+ }
+ catch (Exception ex)
+ {
+ StudioLogger.error(AbstractCodeGeneratorHandler.class,
+ "Error retrieving class information", ex);
+ throw new RuntimeException("Error retrieving class information", ex);
+ }
+ }
+ }
+
+ // just check classes in case classes were selected, not project
+ if (!selectionBean.isProject())
+ {
+ try
+ {
+ // the selected class must be either an Activity or a Fragment
+ selectionBean.setAllowedClassInstance(JDTUtils.isSubclass(
+ selectionBean.getJavaFile(), "android.app.Activity") //$NON-NLS-1$
+ || JDTUtils.isFragmentSubclass(selectionBean.getJavaFile())
+ || JDTUtils.isCompatibilityFragmentSubclass(selectionBean.getJavaFile()));
+ }
+ catch (JavaModelException jme)
+ {
+ StudioLogger.error(AbstractCodeGeneratorHandler.class,
+ "Error retrieving class information", jme);
+ throw new RuntimeException("Error retrieving class information", jme);
+ }
+ }
+ return selectionBean;
+ }
+
+ protected static void executeCodeGenerationWizard(ExecutionEvent event, IFile javaFile,
+ final IProject javaProject, final AbstractLayoutItemsDialog layoutDialog)
+ {
+ final JavaModifierBasedOnLayout modifier = new JavaModifierBasedOnLayout();
+
+ layoutDialog.init(modifier, javaProject, javaFile);
+
+ int status = layoutDialog.open();
+ if (status == Window.OK)
+ {
+ ICompilationUnit compilationUnit = layoutDialog.getJavaFile();
+ IEditorPart editor = null;
+ try
+ {
+ editor = JavaUI.openInEditor(compilationUnit);
+ }
+ catch (Exception e)
+ {
+ StudioLogger
+ .warn(AbstractCodeGeneratorHandler.class,
+ "Unable to open editor or bring it to front for Java file while trying to generate code from layout xml file", //$NON-NLS-1$
+ e);
+ }
+ final ProgressMonitorDialog dialog = new ProgressMonitorDialog(layoutDialog.getShell());
+ final IEditorPart editorPart = editor;
+
+ PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable()
+ {
+
+ public void run()
+ {
+ try
+ {
+
+ dialog.run(true, false, new IRunnableWithProgress()
+ {
+
+ public void run(IProgressMonitor monitor)
+ throws InvocationTargetException, InterruptedException
+ {
+ try
+ {
+
+ // collect usage data - UDC
+ StudioLogger.collectUsageData(
+ UsageDataConstants.WHAT_VIEW_BY_LAYOUT_EXEC,
+ UsageDataConstants.KIND_VIEW_BY_LAYOUT_EXEC,
+ "View by layout feature executed.", //$NON-NLS-1$
+ CodeUtilsActivator.PLUGIN_ID, CodeUtilsActivator
+ .getDefault().getBundle().getVersion()
+ .toString());
+ modifier.insertCode(monitor, editorPart);
+ }
+
+ catch (final JavaModelException e)
+ {
+ final MultiStatus errorStatus =
+ new MultiStatus(
+ CodeUtilsActivator.PLUGIN_ID,
+ IStatus.ERROR,
+ "Error inserting code on activity/fragment based on layout",
+ null);
+ errorStatus.merge(e.getStatus());
+
+ PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable()
+ {
+
+ public void run()
+ {
+ IStatus mostSevere =
+ EclipseUtils.findMostSevereError(errorStatus);
+ ErrorDialog
+ .openError(
+ PlatformUI.getWorkbench().getDisplay()
+ .getActiveShell(),
+ "Error inserting code on activity/fragment based on layout",
+ e.getMessage(), mostSevere);
+ }
+ });
+ StudioLogger.error(this.getClass(),
+ "Error inserting code on activity/fragment based on layout"
+ + ": " + e.getMessage()); //$NON-NLS-1$
+ }
+ }
+ });
+ }
+ catch (Exception e)
+ {
+ StudioLogger.error(this.getClass(),
+ "Error inserting code on activity/fragment based on layout"
+ + ": " + e.getMessage()); //$NON-NLS-1$
+ }
+ }
+ });
+ }
+ }
+
+}
+
+class SelectionBean
+{
+ private boolean isProject = false;
+
+ private boolean isAllowedClassInstance = false;
+
+ private IFile javaFile = null;
+
+ private IProject javaProject = null;
+
+ public boolean isProject()
+ {
+ return isProject;
+ }
+
+ public void setProject(boolean isProject)
+ {
+ this.isProject = isProject;
+ }
+
+ /**
+ * @return true if activity or fragment, false otherwise
+ */
+ public boolean isAllowedClassInstance()
+ {
+ return isAllowedClassInstance;
+ }
+
+ public void setAllowedClassInstance(boolean isAllowedClassInstance)
+ {
+ this.isAllowedClassInstance = isAllowedClassInstance;
+ }
+
+ /**
+ * @return selected Android file (Activity or Fragment)
+ */
+ public IFile getJavaFile()
+ {
+ return javaFile;
+ }
+
+ public void setJavaFile(IFile javaFile)
+ {
+ this.javaFile = javaFile;
+ javaProject = javaFile.getProject();
+ }
+
+ /**
+ * @return the project where the Android file (Activity or Fragment) is located
+ */
+ public IProject getJavaProject()
+ {
+ return javaProject;
+ }
+
+ public void setJavaProject(IProject javaProject)
+ {
+ this.javaProject = javaProject;
+ }
+
+}
diff --git a/src/plugins/android.codeutils/src/com/motorola/studio/android/generateviewbylayout/ui/AbstractLayoutItemsDialog.java b/src/plugins/android.codeutils/src/com/motorola/studio/android/generateviewbylayout/ui/AbstractLayoutItemsDialog.java
new file mode 100644
index 0000000..1a8ee07
--- /dev/null
+++ b/src/plugins/android.codeutils/src/com/motorola/studio/android/generateviewbylayout/ui/AbstractLayoutItemsDialog.java
@@ -0,0 +1,766 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.generateviewbylayout.ui;
+
+import java.util.List;
+
+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.NullProgressMonitor;
+import org.eclipse.jdt.core.ICompilationUnit;
+import org.eclipse.jdt.core.IType;
+import org.eclipse.jdt.core.JavaCore;
+import org.eclipse.jdt.core.JavaModelException;
+import org.eclipse.jface.dialogs.TitleAreaDialog;
+import org.eclipse.jface.viewers.ArrayContentProvider;
+import org.eclipse.jface.viewers.ColumnLabelProvider;
+import org.eclipse.jface.viewers.DoubleClickEvent;
+import org.eclipse.jface.viewers.IDoubleClickListener;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.jface.viewers.TableViewerColumn;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.layout.RowData;
+import org.eclipse.swt.layout.RowLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Combo;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.swt.widgets.TableColumn;
+import org.eclipse.swt.widgets.TableItem;
+import org.eclipse.ui.PlatformUI;
+
+import com.motorola.studio.android.codeutils.i18n.CodeUtilsNLS;
+import com.motorola.studio.android.common.IAndroidConstants;
+import com.motorola.studio.android.common.exception.AndroidException;
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.generatecode.JDTUtils;
+import com.motorola.studio.android.generateviewbylayout.JavaModifierBasedOnLayout;
+import com.motorola.studio.android.generateviewbylayout.model.CodeGeneratorDataBasedOnLayout;
+import com.motorola.studio.android.generateviewbylayout.model.JavaLayoutData;
+import com.motorola.studio.android.generateviewbylayout.model.LayoutNode;
+
+/**
+ * Abstract dialog to deal with common methods for the UI dialog involved into the code generation
+ */
+public abstract class AbstractLayoutItemsDialog extends TitleAreaDialog
+{
+
+ private JavaModifierBasedOnLayout modifier;
+
+ private ICompilationUnit javaFile;
+
+ private IProject javaProject;
+
+ private CodeGeneratorDataBasedOnLayout codeGeneratorData;
+
+ private Combo projectNameComboBox;
+
+ private Combo classNameComboBox;
+
+ private Label layoutFileNameLabel;
+
+ private TableViewer viewer;
+
+ private Button unselectAllButton;
+
+ private Button selectAllButton;
+
+ private String layoutFileErrorMessage;
+
+ private String helpID = null;
+
+ protected static final String DEFAULT_LAYOUT_FILE_NAME_LABEL_VALUE = "--"; //$NON-NLS-1$
+
+ private final String defaultMessage;
+
+ private final String title;
+
+ private final String shellTitle;
+
+ private final Image image;
+
+ public AbstractLayoutItemsDialog(Shell parentShell, String description, String title,
+ String shellTitle, Image image)
+ {
+ super(parentShell);
+ this.defaultMessage = description != null ? description : ""; //$NON-NLS-1$
+ this.title = title != null ? title : ""; //$NON-NLS-1$
+ this.shellTitle = shellTitle != null ? shellTitle : ""; //$NON-NLS-1$
+ this.image = image;
+ }
+
+ /**
+ * Initializes the modifier to enable code generation
+ * @param modifier the responsible to modify code
+ * @param javaProject the project of the Android file to modify
+ * @param javaFile the Android file to change
+ */
+ public void init(JavaModifierBasedOnLayout modifier, IProject javaProject, IFile javaFile)
+ {
+ setModifier(modifier);
+ setJavaProject(javaProject);
+ setJavaFile(javaFile);
+ }
+
+ @Override
+ protected Control createContents(Composite parent)
+ {
+ Control c = super.createContents(parent);
+ setTitle(title);
+ if (image != null)
+ {
+ setTitleImage(image);
+ }
+ validate();
+ return c;
+ }
+
+ @Override
+ protected final Control createDialogArea(Composite parent)
+ {
+ if (helpID != null)
+ {
+ PlatformUI.getWorkbench().getHelpSystem().setHelp(parent, helpID);
+ }
+ Composite parentComposite = (Composite) super.createDialogArea(parent);
+ Composite mainComposite = new Composite(parentComposite, SWT.NONE);
+ mainComposite.setLayoutData(new GridData(GridData.FILL_BOTH));
+ mainComposite.setLayout(new GridLayout(2, false));
+ createProjectNameArea(mainComposite);
+ createClassNameArea(mainComposite);
+ createLayoutFileNameArea(mainComposite);
+
+ Label separator = new Label(mainComposite, SWT.SEPARATOR | SWT.HORIZONTAL);
+ separator.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false, 2, 1));
+
+ createTableArea(mainComposite);
+ createCustomContentArea(mainComposite);
+
+ separator = new Label(parent, SWT.SEPARATOR | SWT.HORIZONTAL);
+ separator.setLayoutData(new GridData(SWT.FILL, SWT.BOTTOM, true, false, 2, 1));
+
+ return parentComposite;
+
+ }
+
+ /**
+ * Create a custom area in the end of the dialog
+ * The parent composite has a grid layout with two columns
+ * @param mainComposite
+ */
+ protected abstract void createCustomContentArea(Composite mainComposite);
+
+ /**
+ * Create GUI items for project name selection.
+ * @param optionsComposite
+ */
+ private void createProjectNameArea(Composite parent)
+ {
+ Label projectLabel = new Label(parent, SWT.NONE);
+ projectLabel.setText(CodeUtilsNLS.ChooseLayoutItemsDialog_Project);
+ projectLabel.setLayoutData(new GridData(SWT.LEFT, SWT.TOP, false, false, 1, 1));
+
+ projectNameComboBox = new Combo(parent, SWT.READ_ONLY | SWT.DROP_DOWN);
+ projectNameComboBox.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false, 1, 1));
+ projectNameComboBox.addSelectionListener(new SelectionAdapter()
+ {
+ @Override
+ public void widgetSelected(SelectionEvent e)
+ {
+ setJavaProject((IProject) projectNameComboBox.getData(projectNameComboBox.getText()));
+ setErrorMessage(null);
+ javaFile = null;
+ layoutFileErrorMessage = null;
+ codeGeneratorData = null;
+ populateClasses();
+ populateLayouts();
+ }
+ });
+ populateProjects();
+ }
+
+ /**
+ * Create GUI items for class name selection.
+ * @param parent
+ */
+ private void createClassNameArea(Composite parent)
+ {
+ Label classLabel = new Label(parent, SWT.NONE);
+ classLabel.setText(CodeUtilsNLS.ChooseLayoutItemsDialog_TargetClass);
+ classLabel.setLayoutData(new GridData(SWT.LEFT, SWT.TOP, false, false, 1, 1));
+
+ classNameComboBox = new Combo(parent, SWT.READ_ONLY | SWT.DROP_DOWN);
+ classNameComboBox.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false, 1, 1));
+ classNameComboBox.addSelectionListener(new SelectionAdapter()
+ {
+ @Override
+ public void widgetSelected(SelectionEvent e)
+ {
+ setJavaFile(((IType) classNameComboBox.getData(classNameComboBox.getText()))
+ .getCompilationUnit());
+ codeGeneratorData = null;
+ populateLayouts();
+ }
+ });
+ populateClasses();
+ }
+
+ /**
+ * Create GUI items for layout file name selection.
+ * @param parent
+ */
+ private void createLayoutFileNameArea(Composite parent)
+ {
+ Label layoutFileLabel = new Label(parent, SWT.NONE);
+ layoutFileLabel.setText(CodeUtilsNLS.ChooseLayoutItemsDialog_SourceLayoutFile);
+ layoutFileLabel.setLayoutData(new GridData(SWT.LEFT, SWT.TOP, false, false, 1, 1));
+
+ layoutFileNameLabel = new Label(parent, SWT.NONE);
+ layoutFileNameLabel.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false, 1, 1));
+ layoutFileNameLabel.setText(DEFAULT_LAYOUT_FILE_NAME_LABEL_VALUE);
+ populateLayouts();
+
+ }
+
+ /**
+ * Create GUI items for GUI Items selection.
+ * @param parent
+ */
+ private void createTableArea(Composite parent)
+ {
+ Composite tableArea = new Composite(parent, SWT.NONE);
+ tableArea.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 2, 1));
+ tableArea.setLayout(new GridLayout(2, false));
+
+ Label tableLabel = new Label(tableArea, SWT.NONE);
+ tableLabel.setLayoutData(new GridData(SWT.LEFT, SWT.TOP, false, false, 2, 1));
+ tableLabel.setText(CodeUtilsNLS.ChooseLayoutItemsDialog_GUIItems);
+
+ viewer =
+ new TableViewer(tableArea, SWT.SINGLE | SWT.H_SCROLL | SWT.V_SCROLL
+ | SWT.FULL_SELECTION | SWT.BORDER | SWT.CHECK);
+ viewer.getControl().setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1));
+
+ Composite buttonsComposite = new Composite(tableArea, SWT.NONE);
+ buttonsComposite.setLayoutData(new GridData(SWT.RIGHT, SWT.TOP, false, true, 1, 1));
+ RowLayout buttonsLayout = new RowLayout(SWT.VERTICAL);
+ buttonsLayout.pack = false;
+ buttonsComposite.setLayout(buttonsLayout);
+
+ selectAllButton = new Button(buttonsComposite, SWT.PUSH);
+ selectAllButton.setText(CodeUtilsNLS.UI_SelectAll);
+ selectAllButton.setLayoutData(new RowData());
+ selectAllButton.addSelectionListener(new SelectionAdapter()
+ {
+ @Override
+ public void widgetSelected(SelectionEvent e)
+ {
+ checkAllItems(true);
+ validate();
+ }
+
+ });
+
+ unselectAllButton = new Button(buttonsComposite, SWT.PUSH);
+ unselectAllButton.setText(CodeUtilsNLS.UI_UnselectAll);
+ unselectAllButton.setLayoutData(new RowData());
+ unselectAllButton.addSelectionListener(new SelectionAdapter()
+ {
+ @Override
+ public void widgetSelected(SelectionEvent e)
+ {
+ checkAllItems(false);
+ validate();
+ }
+ });
+
+ createColumns(viewer);
+
+ final Table table = viewer.getTable();
+ table.setHeaderVisible(true);
+ table.setLinesVisible(true);
+
+ ArrayContentProvider provider = new ArrayContentProvider();
+ viewer.setContentProvider(provider);
+
+ viewer.addDoubleClickListener(new IDoubleClickListener()
+ {
+
+ @Override
+ public void doubleClick(DoubleClickEvent event)
+ {
+ if (event.getSource() instanceof TableViewer)
+ {
+ Table tb = ((TableViewer) event.getSource()).getTable();
+ TableItem[] items = tb.getSelection();
+ items[0].setChecked(!items[0].getChecked());
+ itemCheckStateChanged(items[0]);
+ validate();
+ }
+ }
+ });
+
+ viewer.addSelectionChangedListener(new ISelectionChangedListener()
+ {
+
+ @Override
+ public void selectionChanged(SelectionChangedEvent event)
+ {
+ validate();
+ }
+ });
+
+ populateViewer();
+
+ }
+
+ /**
+ * Notify when the check state changed. This is a workaround for SWT not notifying when we change widget state programatically
+ *
+ * @param item
+ */
+ protected void itemCheckStateChanged(TableItem item)
+ {
+ //default implementation does nothing
+ };
+
+ private void checkAllItems(boolean checked)
+ {
+ for (TableItem item : viewer.getTable().getItems())
+ {
+ item.setChecked(checked);
+ itemCheckStateChanged(item);
+ }
+
+ }
+
+ /**
+ * Creates the columns for the GUI Items table.
+ * @param viewer the TableViewer whose columns will be created.
+ */
+ protected void createColumns(final TableViewer viewer)
+ {
+ String[] titles =
+ {
+ CodeUtilsNLS.ChooseLayoutItemsDialog_Id,
+ CodeUtilsNLS.ChooseLayoutItemsDialog_Type,
+ CodeUtilsNLS.ChooseLayoutItemsDialog_VariableName,
+ };
+ int[] bounds =
+ {
+ 150, 100, 170
+ };
+
+ TableViewerColumn col = createTableViewerColumn(viewer, titles[0], bounds[0], 0);
+ col.setLabelProvider(new ColumnLabelProvider()
+ {
+ @Override
+ public String getText(Object element)
+ {
+ LayoutNode node = (LayoutNode) element;
+ return node.getNodeId();
+ }
+ });
+
+ col = createTableViewerColumn(viewer, titles[1], bounds[1], 1);
+ col.setLabelProvider(new ColumnLabelProvider()
+ {
+ @Override
+ public String getText(Object element)
+ {
+ LayoutNode node = (LayoutNode) element;
+ return node.getNodeType();
+ }
+ });
+
+ col = createTableViewerColumn(viewer, titles[2], bounds[2], 2);
+ col.setLabelProvider(new ColumnLabelProvider()
+ {
+ @Override
+ public String getText(Object element)
+ {
+ LayoutNode node = (LayoutNode) element;
+ return node.getNodeId();
+ }
+ });
+
+ }
+
+ /**
+ * Creates a column for the GUI Items table.
+ * @param parent
+ */
+ protected final TableViewerColumn createTableViewerColumn(TableViewer viewer, String title,
+ int bound, final int colNumber)
+ {
+ final TableViewerColumn viewerColumn = new TableViewerColumn(viewer, SWT.NONE);
+ final TableColumn column = viewerColumn.getColumn();
+ column.setText(title);
+ column.setWidth(bound);
+ column.setResizable(true);
+ column.setMoveable(true);
+ return viewerColumn;
+
+ }
+
+ /**
+ * Populate the combobox that holds projects, with information gathered from the ResourcesPlugin.
+ * also selects the project set in the init method
+ */
+ private void populateProjects()
+ {
+ if (projectNameComboBox != null)
+ {
+ IProject[] projects = ResourcesPlugin.getWorkspace().getRoot().getProjects();
+ int i = 0, selectedProjectIndex = -1;
+
+ for (IProject prj : projects)
+ {
+ try
+ {
+ if (prj.hasNature(IAndroidConstants.ANDROID_NATURE))
+ {
+ projectNameComboBox.add(prj.getName());
+ projectNameComboBox.setData(prj.getName(), prj);
+ if ((javaProject != null) && prj.equals(javaProject))
+ {
+ selectedProjectIndex = i;
+ }
+ i++;
+ }
+ }
+ catch (CoreException e)
+ {
+ StudioLogger.info(CodeUtilsNLS.Info_ChooseLayoutItemsDialog_Project_Nature);
+ }
+ }
+
+ if (projectNameComboBox.getItemCount() > 0)
+ {
+ if (selectedProjectIndex == -1)
+ {
+ projectNameComboBox.select(0);
+ setJavaProject((IProject) projectNameComboBox.getData(projectNameComboBox
+ .getText()));
+ }
+ else
+ {
+ projectNameComboBox.select(selectedProjectIndex);
+ }
+ }
+
+ validate();
+
+ }
+ }
+
+ /**
+ * Populate the combobox that holds class names.
+ */
+ private void populateClasses()
+ {
+ if (classNameComboBox != null)
+ {
+ classNameComboBox.removeAll();
+
+ if (javaProject != null)
+ {
+ int i = 0, selectedTypeIndex = -1;
+ try
+ {
+ List<IType> availableClasses =
+ JDTUtils.getAvailableActivities(javaProject, new NullProgressMonitor());
+
+ availableClasses.addAll(JDTUtils.getAvailableFragmentsSubclasses(
+ (IProject) projectNameComboBox.getData(projectNameComboBox.getText()),
+ new NullProgressMonitor()));
+
+ //add the fragments JDTUtils.getAvailableFragments to activityClasses
+
+ for (IType availableClass : availableClasses)
+ {
+ classNameComboBox.add(availableClass.getFullyQualifiedName());
+ classNameComboBox.setData(availableClass.getFullyQualifiedName(),
+ availableClass);
+ if ((javaFile != null)
+ && availableClass.getCompilationUnit().equals(javaFile))
+ {
+ selectedTypeIndex = i;
+ }
+ i++;
+ }
+ if (classNameComboBox.getItemCount() > 0)
+ {
+ if (selectedTypeIndex == -1)
+ {
+ classNameComboBox.select(0);
+ setJavaFile(((IType) classNameComboBox.getData(classNameComboBox
+ .getText())).getCompilationUnit());
+ }
+ else
+ {
+ classNameComboBox.select(selectedTypeIndex);
+ }
+ }
+ }
+ catch (JavaModelException e)
+ {
+ StudioLogger.info(CodeUtilsNLS.Info_ChooseLayoutItemsDialog_Available_Classes);
+ }
+
+ classNameComboBox.setEnabled(classNameComboBox.getItemCount() > 0);
+ }
+ }
+ validate();
+ }
+
+ /**
+ * Populate the combobox that holds layout file names.
+ */
+ private void populateLayouts()
+ {
+ if (layoutFileNameLabel != null)
+ {
+ layoutFileNameLabel.setText(DEFAULT_LAYOUT_FILE_NAME_LABEL_VALUE);
+ layoutFileErrorMessage = null;
+ IType activity = (IType) classNameComboBox.getData(classNameComboBox.getText());
+ if (activity != null)
+ {
+ try
+ {
+ codeGeneratorData = JDTUtils.createLayoutFile(getJavaProject(), getJavaFile());
+ JavaLayoutData viewLayoutData = getCodeGeneratorData().getJavaLayoutData();
+ if ((viewLayoutData == null) || (viewLayoutData.hasErrorInCompilationUnitAst()))
+ {
+ //there are errors in the compilation unit
+ layoutFileErrorMessage =
+ CodeUtilsNLS.ChooseLayoutItemsDialog_TryToGenerateCodeWhenThereIsAnError;
+ codeGeneratorData = null;
+ }
+ else
+ {
+ if (getCodeGeneratorData().getLayoutFile().getName() != null)
+ {
+ layoutFileNameLabel.setText(getCodeGeneratorData().getLayoutFile()
+ .getName());
+ }
+ else
+ {
+ layoutFileErrorMessage =
+ CodeUtilsNLS.UI_ChooseLayoutItemsDialog_Error_onCreate_Not_Declared;
+ }
+ }
+ }
+ catch (AndroidException e)
+ {
+ //if layout xml is malformed indicate it on screen
+ layoutFileErrorMessage = e.getMessage();
+ StudioLogger.error(this.getClass(), "Error parsing layout: " + e.getMessage()); //$NON-NLS-1$
+ }
+
+ populateViewer();
+ }
+ validate();
+ }
+ }
+
+ protected void populateViewer()
+ {
+ if (viewer != null)
+ {
+ viewer.getTable().removeAll();
+
+ // Get the content for the viewer, setInput will call getElements in the
+ // contentProvider
+ if (getCodeGeneratorData() != null)
+ {
+ viewer.setInput(getGuiItemsList());
+ viewer.refresh();
+ }
+
+ if (viewer.getTable().getItemCount() == 0)
+ {
+ viewer.getTable().setEnabled(false);
+ selectAllButton.setEnabled(false);
+ unselectAllButton.setEnabled(false);
+ }
+ else
+ {
+ selectAllButton.setEnabled(true);
+ unselectAllButton.setEnabled(true);
+ viewer.getTable().setEnabled(true);
+ }
+ validate();
+ }
+ }
+
+ /**
+ * Get the list of items to be displayed
+ * @return
+ */
+ protected List<LayoutNode> getGuiItemsList()
+ {
+ return getCodeGeneratorData().getGUIItems(false);
+ }
+
+ @Override
+ protected void configureShell(Shell newShell)
+ {
+ super.configureShell(newShell);
+ newShell.setText(shellTitle);
+ }
+
+ /**
+ * Validate the UI
+ * @return
+ */
+ protected void validate()
+ {
+ String errorMessage = null;
+
+ if ((projectNameComboBox != null) && (projectNameComboBox.getItemCount() == 0))
+ {
+ errorMessage = CodeUtilsNLS.AbstractLayoutItemsDialog_Error_No_Projects_Found;
+ }
+ if ((errorMessage == null) && (classNameComboBox != null)
+ && (classNameComboBox.getItemCount() == 0))
+ {
+ errorMessage = CodeUtilsNLS.AbstractLayoutItemsDialog_Error_No_Class_Found;
+ }
+ if ((errorMessage == null) && (layoutFileNameLabel != null))
+ {
+ if (layoutFileErrorMessage != null)
+ {
+ errorMessage = layoutFileErrorMessage;
+ }
+ else if (getJavaFile() == null)
+ {
+ errorMessage = CodeUtilsNLS.AbstractLayoutItemsDialog_Error_No_Layout_Found;
+ }
+ }
+
+ setErrorMessage(errorMessage);
+
+ if (errorMessage == null)
+ {
+ setMessage(defaultMessage);
+ }
+
+ if (getButton(OK) != null)
+ {
+ getButton(OK).setEnabled((getErrorMessage() == null) && hasAtLeastOneItemChecked());
+ }
+ }
+
+ private boolean hasAtLeastOneItemChecked()
+ {
+ boolean hasItemsChecked = false;
+
+ TableItem[] items = viewer.getTable().getItems();
+ int i = 0;
+ while (!hasItemsChecked && (i < items.length))
+ {
+ if (items[i++].getChecked())
+ {
+ hasItemsChecked = true;
+ }
+ }
+
+ return hasItemsChecked;
+ }
+
+ /**
+ * @return {@link ICompilationUnit} selected Android file to generate the code
+ */
+ public ICompilationUnit getJavaFile()
+ {
+ return javaFile;
+ }
+
+ /**
+ * @return {@link IProject} of the selected Android file
+ */
+ public IProject getJavaProject()
+ {
+ return javaProject;
+ }
+
+ /**
+ * @return {@link JavaModifierBasedOnLayout} responsible to change the source code of Android file
+ */
+ public JavaModifierBasedOnLayout getModifier()
+ {
+ return modifier;
+ }
+
+ private void setModifier(JavaModifierBasedOnLayout modifier)
+ {
+ this.modifier = modifier;
+ }
+
+ private void setJavaFile(IFile javaFile)
+ {
+ if (javaFile != null)
+ {
+ setJavaFile(JavaCore.createCompilationUnitFrom(javaFile));
+ }
+ }
+
+ private void setJavaFile(ICompilationUnit javaFile)
+ {
+ this.javaFile = javaFile;
+ setJavaProject(javaFile.getJavaProject().getProject());
+ }
+
+ private void setJavaProject(IProject javaProject)
+ {
+ this.javaProject = javaProject;
+ }
+
+ /**
+ * @return object representing the input data (from layout XML) to use as basis during code generation
+ */
+ public CodeGeneratorDataBasedOnLayout getCodeGeneratorData()
+ {
+ return codeGeneratorData;
+ }
+
+ public void setHelpID(String helpID)
+ {
+ this.helpID = helpID;
+ }
+
+ /**
+ * @return table to select the Android GUI items to generate code for
+ */
+ public TableViewer getViewer()
+ {
+ return viewer;
+ }
+
+}
diff --git a/src/plugins/android.codeutils/src/com/motorola/studio/android/generateviewbylayout/ui/ChooseLayoutItemsDialog.java b/src/plugins/android.codeutils/src/com/motorola/studio/android/generateviewbylayout/ui/ChooseLayoutItemsDialog.java
new file mode 100644
index 0000000..d3cec39
--- /dev/null
+++ b/src/plugins/android.codeutils/src/com/motorola/studio/android/generateviewbylayout/ui/ChooseLayoutItemsDialog.java
@@ -0,0 +1,335 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.generateviewbylayout.ui;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.jface.dialogs.IMessageProvider;
+import org.eclipse.jface.viewers.ColumnLabelProvider;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.jface.viewers.TableViewerColumn;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.TableEditor;
+import org.eclipse.swt.events.ControlAdapter;
+import org.eclipse.swt.events.ControlEvent;
+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.events.SelectionListener;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.TableItem;
+
+import com.motorola.studio.android.codeutils.CodeUtilsActivator;
+import com.motorola.studio.android.codeutils.i18n.CodeUtilsNLS;
+import com.motorola.studio.android.generateviewbylayout.codegenerators.SaveStateCodeGenerator;
+import com.motorola.studio.android.generateviewbylayout.model.LayoutNode;
+
+/**
+ * GUI to enable selection of which Android items
+ * will be inserted into activity or fragment
+ */
+public class ChooseLayoutItemsDialog extends AbstractLayoutItemsDialog
+{
+ private final String DIALOG_HELP = CodeUtilsActivator.PLUGIN_ID
+ + ".generate-code-from-layout-dialog"; //$NON-NLS-1$
+
+ private Button generateListeners;
+
+ private boolean hasGuiItemsWithoutId = false;
+
+ private final Map<TableItem, TableEditor> itemToEditorMap;
+
+ private static final String WIZARD_IMAGE_PATH = "icons/wizban/fill_activity_ban.png"; //$NON-NLS-1$
+
+ public ChooseLayoutItemsDialog(Shell parentShell)
+ {
+ super(parentShell, CodeUtilsNLS.ChooseLayoutItemsDialog_DefaultMessage,
+ CodeUtilsNLS.UI_ChooseLayoutItemsDialog_Dialog_Title,
+ CodeUtilsNLS.ChooseLayoutItemsDialog_FillActivityBasedOnLayout, CodeUtilsActivator
+ .getImageDescriptor(WIZARD_IMAGE_PATH).createImage());
+ setHelpID(DIALOG_HELP);
+ itemToEditorMap = new HashMap<TableItem, TableEditor>();
+ }
+
+ @Override
+ protected void createCustomContentArea(Composite parent)
+ {
+
+ Label separator = new Label(parent, SWT.SEPARATOR | SWT.HORIZONTAL);
+ separator.setLayoutData(new GridData(SWT.FILL, SWT.BOTTOM, true, false, 2, 1));
+
+ createCheckboxArea(parent);
+
+ }
+
+ @Override
+ protected void createColumns(TableViewer viewer)
+ {
+ super.createColumns(viewer);
+ TableViewerColumn column =
+ createTableViewerColumn(viewer, CodeUtilsNLS.ChooseLayoutItemsDialog_SaveState, 80,
+ 3);
+ column.setLabelProvider(new ColumnLabelProvider()
+ {
+ @Override
+ public String getText(Object element)
+ {
+ return null;
+ }
+ });
+ column.getColumn().setToolTipText(CodeUtilsNLS.ChooseLayoutItemsDialog_SaveStateTooltip);
+
+ }
+
+ @Override
+ protected void populateViewer()
+ {
+ super.populateViewer();
+ populateSaveStateColumn();
+ }
+
+ private void populateSaveStateColumn()
+ {
+ if (getViewer() != null)
+ {
+ itemToEditorMap.clear();
+ for (final TableItem item : getViewer().getTable().getItems())
+ {
+ LayoutNode node = (LayoutNode) item.getData();
+
+ if (SaveStateCodeGenerator.canGenerateSaveStateCode(node))
+ {
+
+ final TableEditor editor = new TableEditor(getViewer().getTable());
+ editor.setColumn(3);
+ editor.horizontalAlignment = SWT.CENTER;
+ editor.grabHorizontal = false;
+ editor.minimumWidth =
+ getViewer().getTable().getColumn(3).getWidth() < 20 ? getViewer()
+ .getTable().getColumn(3).getWidth() : 20;
+ getViewer().getTable().getColumn(3).addControlListener(new ControlAdapter()
+ {
+
+ @Override
+ public void controlResized(ControlEvent e)
+ {
+ editor.minimumWidth =
+ getViewer().getTable().getColumn(3).getWidth() < 20
+ ? getViewer().getTable().getColumn(3).getWidth() : 20;
+ }
+ });
+ final Button checkbox = new Button(getViewer().getTable(), SWT.CHECK);
+ checkbox.setEnabled(false);
+ checkbox.pack();
+ final SelectionListener listener = new SelectionAdapter()
+ {
+ @Override
+ public void widgetSelected(SelectionEvent e)
+ {
+ if ((e.detail & SWT.CHECK) != 0)
+ {
+ if ((e.item instanceof TableItem) && (editor.getItem() == e.item))
+ {
+ checkbox.setEnabled(((TableItem) e.item).getChecked());
+ }
+ }
+ }
+ };
+ checkbox.setSelection(node.getSaveState());
+ getViewer().getTable().addSelectionListener(listener);
+ checkbox.addSelectionListener(new SelectionAdapter()
+ {
+ @Override
+ public void widgetSelected(SelectionEvent e)
+ {
+ LayoutNode node = (LayoutNode) editor.getItem().getData();
+ node.setSaveState(checkbox.getSelection());
+ }
+ });
+ editor.setEditor(checkbox, item, 3);
+
+ item.addDisposeListener(new DisposeListener()
+ {
+
+ public void widgetDisposed(DisposeEvent e)
+ {
+ getViewer().getTable().removeSelectionListener(listener);
+ editor.getEditor().dispose();
+ editor.dispose();
+ }
+ });
+ itemToEditorMap.put(item, editor);
+ }
+ }
+ }
+ }
+
+ @Override
+ protected void itemCheckStateChanged(TableItem item)
+ {
+ super.itemCheckStateChanged(item);
+ if (itemToEditorMap.get(item) != null)
+ {
+ ((Button) itemToEditorMap.get(item).getEditor()).setEnabled(item.getChecked());
+ }
+ }
+
+ @Override
+ protected List<LayoutNode> getGuiItemsList()
+ {
+ List<LayoutNode> completeGuiItemsList = getCodeGeneratorData().getGUIItemsForUI();
+ List<LayoutNode> processedGuiItemsList = new ArrayList<LayoutNode>();
+ hasGuiItemsWithoutId = false;
+
+ for (LayoutNode guiItem : completeGuiItemsList)
+ {
+ if ((guiItem.getNodeId() != null) && (guiItem.getNodeId().length() > 0))
+ {
+ processedGuiItemsList.add(guiItem);
+ }
+ else
+ {
+ hasGuiItemsWithoutId = true;
+ }
+ }
+
+ return processedGuiItemsList;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.window.Window#configureShell(org.eclipse.swt.widgets.Shell)
+ */
+ @Override
+ protected void configureShell(Shell newShell)
+ {
+ newShell.setSize(640, 480);
+ super.configureShell(newShell);
+ }
+
+ /**
+ * Creates GUI items for choosing whether code for listeners should be auto-generated.
+ * @param optionsComposite
+ */
+ private void createCheckboxArea(Composite parent)
+ {
+ generateListeners = new Button(parent, SWT.CHECK);
+ generateListeners.setText(CodeUtilsNLS.ChooseLayoutItemsDialog_GenerateDefaultListeners);
+ generateListeners.setSelection(true);
+ generateListeners.setLayoutData(new GridData(SWT.LEFT, SWT.BOTTOM, false, false, 2, 1));
+ }
+
+ /**
+ * Handles the enablement of the Ok button.
+ * It will only be enabled when at least one table item is checked.
+ */
+ @Override
+ protected void validate()
+ {
+ super.validate();
+ if ((getViewer() != null) && (getErrorMessage() == null))
+ {
+
+ // set the appropriate message
+ String message = ""; //$NON-NLS-1$
+ int messageType = IMessageProvider.NONE;
+
+ //check if at least one table item was selected
+ for (TableItem item : getViewer().getTable().getItems())
+ {
+ LayoutNode node = (LayoutNode) item.getData();
+ if (item.getChecked()
+ && (getCodeGeneratorData() != null)
+ && getCodeGeneratorData().getJavaLayoutData().getVisitor()
+ .checkIfAttributeAlreadyDeclared(node, true))
+ {
+ message =
+ NLS.bind(CodeUtilsNLS.ChooseLayoutItemsDialog_VariableNameInUse_Error,
+ node.getNodeId());
+ messageType = IMessageProvider.ERROR;
+ break;
+ }
+ }
+
+ if (messageType == IMessageProvider.NONE)
+ {
+
+ if (getViewer().getTable().getItemCount() == 0)
+ {
+ message = CodeUtilsNLS.UI_ChooseLayoutItemsDialog_No_Gui_Items_Available;
+ messageType = IMessageProvider.INFORMATION;
+ }
+ else if (hasGuiItemsWithoutId)
+ {
+ message = CodeUtilsNLS.ChooseLayoutItemsDialog_Gui_Items_Available_No_Id;
+ messageType = IMessageProvider.INFORMATION;
+ }
+ else
+ {
+ message = CodeUtilsNLS.ChooseLayoutItemsDialog_DefaultMessage;
+ }
+ }
+ this.setMessage(message, messageType);
+
+ }
+ }
+
+ /**
+ * Each table item refers to a LayoutNode object.
+ * When user press Ok, the insertCode status of these objects are set accordingly to table selections.
+ */
+ @Override
+ protected void okPressed()
+ {
+ //set the insertCode for each layoutNode accordingly
+ for (TableItem item : getViewer().getTable().getItems())
+ {
+ if (item.getData() instanceof LayoutNode)
+ {
+ LayoutNode node = (LayoutNode) item.getData();
+ node.setInsertCode(item.getChecked());
+ }
+ }
+ getModifier().setGenerateDefaultListeners(generateListeners.getSelection());
+ getModifier().setCodeGeneratorData(getCodeGeneratorData());
+ super.okPressed();
+ }
+
+ /**
+ * Passing the focus request to the viewer's control.
+ */
+ public void setFocus()
+ {
+ getViewer().getControl().setFocus();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.dialogs.Dialog#isResizable()
+ */
+ @Override
+ protected boolean isResizable()
+ {
+ return true;
+ }
+}
diff --git a/src/plugins/android.codeutils/src/com/motorola/studio/android/generateviewbylayout/ui/FillOnSaveInstanceStateDialog.java b/src/plugins/android.codeutils/src/com/motorola/studio/android/generateviewbylayout/ui/FillOnSaveInstanceStateDialog.java
new file mode 100644
index 0000000..617f941
--- /dev/null
+++ b/src/plugins/android.codeutils/src/com/motorola/studio/android/generateviewbylayout/ui/FillOnSaveInstanceStateDialog.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.generateviewbylayout.ui;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.TableItem;
+
+import com.motorola.studio.android.codeutils.i18n.CodeUtilsNLS;
+import com.motorola.studio.android.generateviewbylayout.codegenerators.SaveStateCodeGenerator;
+import com.motorola.studio.android.generateviewbylayout.model.LayoutNode;
+
+/**
+ * Dialog to Save UI state for:
+ * <ul>
+ * <li>a selected Activity/Fragment
+ * <li>a selected layout xml
+ * </ul>
+ */
+public class FillOnSaveInstanceStateDialog extends AbstractLayoutItemsDialog
+{
+
+ public FillOnSaveInstanceStateDialog(Shell parentShell)
+ {
+ super(parentShell, CodeUtilsNLS.FillOnSaveInstanceStateDialog_DialogDescription,
+ CodeUtilsNLS.FillOnSaveInstanceStateDialog_DialogTitle,
+ CodeUtilsNLS.FillOnSaveInstanceStateDialog_ShellTitle, null);
+ }
+
+ @Override
+ protected void createCustomContentArea(Composite mainComposite)
+ {
+ //default implementation does nothing
+ }
+
+ @Override
+ protected boolean isResizable()
+ {
+ return true;
+ }
+
+ @Override
+ protected List<LayoutNode> getGuiItemsList()
+ {
+ List<LayoutNode> alreadyDeclared = new ArrayList<LayoutNode>();
+ if (getCodeGeneratorData() != null)
+ {
+ List<LayoutNode> allNodes = getCodeGeneratorData().getGUIItems(false);
+
+ for (LayoutNode node : allNodes)
+ {
+ if (node.isAlreadyDeclaredInCode() && !node.isAlreadySaved() && canSaveState(node))
+ {
+ alreadyDeclared.add(node);
+ }
+ }
+ }
+ return alreadyDeclared;
+ }
+
+ private boolean canSaveState(LayoutNode node)
+ {
+ int i = 0;
+ boolean canSaveState = false;
+ while (!canSaveState && (i < SaveStateCodeGenerator.saveStateNodeTypes.length))
+ {
+ if (SaveStateCodeGenerator.saveStateNodeTypes[i].getNodeType().equals(
+ node.getNodeType()))
+ {
+ canSaveState = true;
+ }
+ i++;
+ }
+
+ return canSaveState;
+ }
+
+ @Override
+ protected void okPressed()
+ {
+ for (TableItem item : getViewer().getTable().getItems())
+ {
+ if (item.getData() instanceof LayoutNode)
+ {
+ LayoutNode node = (LayoutNode) item.getData();
+ node.setSaveState(item.getChecked());
+ }
+ }
+ getModifier().setCodeGeneratorData(getCodeGeneratorData());
+ super.okPressed();
+ }
+
+}
diff --git a/src/plugins/android.codeutils/src/com/motorola/studio/android/generateviewbylayout/ui/FillOnSaveInstanceStateHandler.java b/src/plugins/android.codeutils/src/com/motorola/studio/android/generateviewbylayout/ui/FillOnSaveInstanceStateHandler.java
new file mode 100644
index 0000000..0cbae35
--- /dev/null
+++ b/src/plugins/android.codeutils/src/com/motorola/studio/android/generateviewbylayout/ui/FillOnSaveInstanceStateHandler.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.generateviewbylayout.ui;
+
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.handlers.HandlerUtil;
+
+import com.motorola.studio.android.codeutils.i18n.CodeUtilsNLS;
+import com.motorola.studio.android.common.utilities.EclipseUtils;
+
+/**
+ * Command handler to Save UI state
+ */
+public class FillOnSaveInstanceStateHandler extends AbstractCodeGeneratorHandler
+{
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.commands.IHandler#execute(org.eclipse.core.commands.ExecutionEvent)
+ */
+
+ public Object execute(ExecutionEvent event) throws ExecutionException
+ {
+ SelectionBean selectionBean = resolveSelection(event);
+
+ if (selectionBean.isProject() || selectionBean.isAllowedClassInstance())
+ {
+ final IWorkbenchWindow window = HandlerUtil.getActiveWorkbenchWindow(event);
+ executeCodeGenerationWizard(event, selectionBean.getJavaFile(),
+ selectionBean.getJavaProject(),
+ new FillOnSaveInstanceStateDialog(window.getShell()));
+ }
+ else
+ {
+ EclipseUtils
+ .showErrorDialog(
+ CodeUtilsNLS.GenerateViewBasedOnLayoutHandler_FillJavaActivityBasedOnLayout,
+ CodeUtilsNLS.GenerateViewBasedOnLayoutHandler_SelectedClassNeitherActivityFragment);
+ }
+
+ return null;
+ }
+
+}
diff --git a/src/plugins/android.codeutils/src/com/motorola/studio/android/generateviewbylayout/ui/GenerateViewBasedOnLayoutHandler.java b/src/plugins/android.codeutils/src/com/motorola/studio/android/generateviewbylayout/ui/GenerateViewBasedOnLayoutHandler.java
new file mode 100644
index 0000000..a46607d
--- /dev/null
+++ b/src/plugins/android.codeutils/src/com/motorola/studio/android/generateviewbylayout/ui/GenerateViewBasedOnLayoutHandler.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.generateviewbylayout.ui;
+
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.handlers.HandlerUtil;
+
+import com.motorola.studio.android.codeutils.i18n.CodeUtilsNLS;
+import com.motorola.studio.android.common.utilities.EclipseUtils;
+
+/**
+ * Handler to modify source code (activity / fragment) based on layout xml
+ */
+public class GenerateViewBasedOnLayoutHandler extends AbstractCodeGeneratorHandler
+{
+ /**
+ * Open {@link ChooseLayoutItemsDialog} dialog to select the items to generate code for (depending on the selected layout on combo box).
+ * After finish button click, it modifies Android source code (depending on Activity or Fragment selected).
+ */
+ public Object execute(ExecutionEvent event) throws ExecutionException
+ {
+ SelectionBean selectionBean = resolveSelection(event);
+
+ if (selectionBean.isProject() || selectionBean.isAllowedClassInstance())
+ {
+ final IWorkbenchWindow window = HandlerUtil.getActiveWorkbenchWindow(event);
+ executeCodeGenerationWizard(event, selectionBean.getJavaFile(),
+ selectionBean.getJavaProject(), new ChooseLayoutItemsDialog(window.getShell()));
+ }
+ else
+ {
+ EclipseUtils
+ .showErrorDialog(
+ CodeUtilsNLS.GenerateViewBasedOnLayoutHandler_FillJavaActivityBasedOnLayout,
+ CodeUtilsNLS.GenerateViewBasedOnLayoutHandler_SelectedClassNeitherActivityFragment);
+ }
+
+ return null;
+ }
+
+}
diff --git a/src/plugins/android.codeutils/src/com/motorola/studio/android/model/Activity.java b/src/plugins/android.codeutils/src/com/motorola/studio/android/model/Activity.java
new file mode 100644
index 0000000..dc457b8
--- /dev/null
+++ b/src/plugins/android.codeutils/src/com/motorola/studio/android/model/Activity.java
@@ -0,0 +1,580 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.model;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.jdt.core.JavaModelException;
+import org.eclipse.jface.wizard.IWizardContainer;
+import org.eclipse.osgi.util.NLS;
+
+import com.motorola.studio.android.codeutils.i18n.CodeUtilsNLS;
+import com.motorola.studio.android.common.IAndroidConstants;
+import com.motorola.studio.android.common.exception.AndroidException;
+import com.motorola.studio.android.manifest.AndroidProjectManifestFile;
+import com.motorola.studio.android.model.java.ActivityClass;
+import com.motorola.studio.android.model.manifest.AndroidManifestFile;
+import com.motorola.studio.android.model.manifest.dom.ActionNode;
+import com.motorola.studio.android.model.manifest.dom.ActivityNode;
+import com.motorola.studio.android.model.manifest.dom.ApplicationNode;
+import com.motorola.studio.android.model.manifest.dom.CategoryNode;
+import com.motorola.studio.android.model.manifest.dom.IntentFilterNode;
+import com.motorola.studio.android.model.manifest.dom.ManifestNode;
+import com.motorola.studio.android.model.manifest.dom.UsesPermissionNode;
+import com.motorola.studio.android.model.resources.ResourceFile;
+import com.motorola.studio.android.model.resources.types.AbstractResourceNode.NodeType;
+import com.motorola.studio.android.model.resources.types.ResourcesNode;
+import com.motorola.studio.android.resources.AndroidProjectResources;
+
+/**
+ * Activity Controller Model.
+ * As part of a MVC architecture, this class should communicate with the Wizard UI
+ * to provide all needed information to create a functional Activity.
+ */
+public class Activity extends Launcher
+{
+ private static final String INTENT_ACTION_MAIN_NAME = "android.intent.action.MAIN";
+
+ private static final String INTENT_CATEGORY_LAUNCHER_NAME = "android.intent.category.LAUNCHER";
+
+ private static final String ACTIVITY_RESOURCE_LABEL_SUFFIX = "ActivityLabel"; //$NON-NLS-1$
+
+ private static final int MANIFEST_UPDATING_STEPS = 6;
+
+ private static final int RESOURCES_UPDATING_STEPS = 3;
+
+ private boolean onStart = false;
+
+ HashMap<String, String> workspaceResourceNames = null;
+
+ /**
+ * Boolean flag to tell if the Activity will be set as MAIN or not in the AndroidManifest.
+ */
+ private boolean isMainActivity = false;
+
+ /**
+ * Check if the onStart Method should be created
+ * @return
+ */
+ public boolean isOnStart()
+ {
+ return onStart;
+ }
+
+ /**
+ * Change the onStart create property
+ * @param onStart
+ */
+ public void setOnStart(boolean onStart)
+ {
+ this.onStart = onStart;
+ }
+
+ /**
+ * Constructor for the Activity.
+ */
+ public Activity()
+ {
+ super(IAndroidConstants.CLASS_ACTIVITY);
+ }
+
+ /*
+ * Enables finish button when page is filled.
+ */
+ public boolean needMoreInformation()
+ {
+ boolean needInfo = false;
+ IStatus status = getStatus();
+
+ if (status.getSeverity() > IStatus.WARNING)
+ {
+ needInfo = true;
+ }
+
+ return needInfo;
+ }
+
+ /**
+ * Package name (based on project name declared on manifest)
+ * @param project
+ * @return
+ * @throws CoreException Exception thrown in case there are problems handling the android project
+ * @throws AndroidException Exception thrown in case there are problems handling the android project
+ */
+ protected String getManifestPackageName(IProject project) throws AndroidException,
+ CoreException
+ {
+ // get android application name
+ AndroidManifestFile androidManifestFile =
+ AndroidProjectManifestFile.getFromProject(project);
+ String appNamespace = "";
+ if (androidManifestFile != null)
+ {
+ ManifestNode manifestNode = androidManifestFile.getManifestNode();
+ appNamespace = manifestNode.getPackageName();
+ }
+ // return the android application name along with a persistence constant
+ return appNamespace;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.model.IWizardModel#save(org.eclipse.jface.wizard.IWizardContainer, org.eclipse.core.runtime.IProgressMonitor)
+ */
+ @Override
+ public boolean save(IWizardContainer container, IProgressMonitor monitor)
+ throws AndroidException
+ {
+ workspaceResourceNames = new HashMap<String, String>();
+
+ boolean classCreated = createActivityClass(monitor);
+
+ boolean addedOnManifest = false;
+
+ if (classCreated)
+ {
+ addedOnManifest = createActivityOnManifest(monitor);
+ }
+
+ // Logs all permissions used in UDC log
+ super.save(container, monitor);
+
+ try
+ {
+ ResourcesPlugin.getWorkspace().getRoot()
+ .refreshLocal(IResource.DEPTH_INFINITE, monitor);
+ }
+ catch (CoreException e)
+ {
+ // do nothing
+ }
+ return classCreated && addedOnManifest;
+
+ }
+
+ /**
+ * Creates the Activity java class
+ *
+ * @param monitor the progress monitor
+ *
+ * @return true if the class has been created or false otherwise
+ * @throws AndroidException
+ */
+ private boolean createActivityClass(IProgressMonitor monitor) throws AndroidException
+ {
+ boolean created = false;
+
+ monitor.subTask(CodeUtilsNLS.UI_Activity_CreatingTheActivityJavaClass);
+
+ ActivityClass activityClass = null;
+
+ try
+ {
+ activityClass =
+ new ActivityClass(getName(), getPackageFragment().getElementName(), onStart);
+ createJavaClassFile(activityClass, monitor);
+
+ created = true;
+ }
+ catch (JavaModelException e)
+ {
+ String errMsg =
+ NLS.bind(CodeUtilsNLS.EXC_Activity_CannotCreateTheActivityClass, getName(),
+ e.getLocalizedMessage());
+
+ throw new AndroidException(errMsg);
+ }
+ catch (AndroidException e)
+ {
+ String errMsg =
+ NLS.bind(CodeUtilsNLS.EXC_Activity_CannotCreateTheActivityClass, getName(),
+ e.getLocalizedMessage());
+ throw new AndroidException(errMsg);
+ }
+
+ return created;
+ }
+
+ /**
+ * Creates the Activity class entry on AndroidManifest.xml file
+ *
+ * @param monitor the progress monitor
+ *
+ * @return true if the entry has been added or false otherwise
+ * @throws AndroidException
+ */
+ private boolean createActivityOnManifest(IProgressMonitor monitor) throws AndroidException
+ {
+ boolean created = false;
+
+ try
+ {
+ int totalWork = MANIFEST_UPDATING_STEPS + RESOURCES_UPDATING_STEPS;
+
+ monitor.beginTask("", totalWork);
+
+ monitor.subTask(CodeUtilsNLS.UI_Common_UpdatingTheAndroidManifestXMLFile);
+
+ AndroidManifestFile androidManifestFile =
+ AndroidProjectManifestFile.getFromProject(getProject());
+
+ monitor.worked(1);
+
+ ManifestNode manifestNode =
+ androidManifestFile != null ? androidManifestFile.getManifestNode() : null;
+
+ ApplicationNode applicationNode =
+ manifestNode != null ? manifestNode.getApplicationNode() : null;
+
+ monitor.worked(1);
+
+ if (applicationNode != null)
+ {
+ // Adds the added permission nodes to manifest file
+ List<String> permissionsNames = new ArrayList<String>();
+ for (UsesPermissionNode i : manifestNode.getUsesPermissionNodes())
+ {
+ if (!permissionsNames.contains(i.getName()))
+ {
+ permissionsNames.add(i.getName());
+ }
+ }
+
+ for (String intentFilterPermission : getIntentFilterPermissionsAsArray())
+ {
+ if (!permissionsNames.contains(intentFilterPermission))
+ {
+ manifestNode.addChild(new UsesPermissionNode(intentFilterPermission));
+ }
+ }
+
+ boolean activityExists = false;
+
+ // Existing activity, if exists
+ ActivityNode existingActivity = null;
+
+ String classQualifier =
+ (getPackageFragment().getElementName()
+ .equals(manifestNode.getPackageName()) ? "" : getPackageFragment() //$NON-NLS-1$
+ .getElementName()) + "."; //$NON-NLS-1$
+
+ for (ActivityNode activityNode : applicationNode.getActivityNodes())
+ {
+ if (activityNode.getName()
+ .substring(activityNode.getName().lastIndexOf('.') + 1)
+ .equals(getName()))
+ {
+ activityExists = true;
+ existingActivity = activityNode;
+ break;
+ }
+ }
+
+ if (isMainActivity)
+ {
+ boolean actionRemoved = false;
+
+ //check if there is a main activity. If so, removes actions and intent filter
+ for (ActivityNode activityNode : applicationNode.getActivityNodes())
+ {
+ if ((existingActivity != null) && existingActivity.equals(activityNode))
+ {
+ continue;
+ }
+
+ List<IntentFilterNode> intentList = activityNode.getIntentFilterNodes();
+ for (IntentFilterNode currentIntent : intentList)
+ {
+ actionRemoved = false;
+ List<ActionNode> actionList = currentIntent.getActionNodes();
+ for (ActionNode currentAction : actionList)
+ {
+ if (currentAction.getName().equals(INTENT_ACTION_MAIN_NAME))
+ {
+ currentIntent.removeActionNode(currentAction);
+ actionRemoved = true;
+ }
+ }
+ //if INTENT_ACTION_MAIN_NAME is found remove INTENT_CATEGORY_LAUNCHER_NAME too
+ if (actionRemoved)
+ {
+ List<CategoryNode> categoryList = currentIntent.getCategoryNodes();
+ for (CategoryNode currentCategory : categoryList)
+ {
+ if (currentCategory.getName().equals(
+ INTENT_CATEGORY_LAUNCHER_NAME))
+ {
+ currentIntent.removeCategoryNode(currentCategory);
+ }
+ }
+ }
+ //remove intent filter if empty
+ if (actionRemoved && (currentIntent.getChildren().length == 0))
+ {
+ activityNode.removeIntentFilterNode(currentIntent);
+ }
+ }
+ }
+ }
+
+ monitor.worked(1);
+
+ if (!activityExists)
+ {
+ ActivityNode activityNode = new ActivityNode(classQualifier + getName());
+
+ String activityLabel = createActivityLabel(monitor);
+
+ if (activityLabel != null)
+ {
+ activityNode.setLabel(AndroidProjectResources.STRING_CALL_PREFIX
+ + activityLabel);
+ }
+
+ IntentFilterNode intentFilterNode = new IntentFilterNode();
+
+ for (String intentFilterAction : getIntentFilterActionsAsArray())
+ {
+ intentFilterNode.addActionNode(new ActionNode(intentFilterAction));
+ }
+
+ for (String intentFilterCategory : getIntentFilterCategoriesAsArray())
+ {
+ intentFilterNode.addCategoryNode(new CategoryNode(intentFilterCategory));
+ }
+
+ // Check if we need to insert a filter action and filter category setting this activity as MAIN
+ if (isMainActivity)
+ {
+ intentFilterNode.addActionNode(new ActionNode(INTENT_ACTION_MAIN_NAME));
+ intentFilterNode.addCategoryNode(new CategoryNode(
+ INTENT_CATEGORY_LAUNCHER_NAME));
+
+ }
+
+ if (intentFilterNode.getChildren().length > 0)
+ {
+ activityNode.addIntentFilterNode(intentFilterNode);
+ }
+
+ applicationNode.addActivityNode(activityNode);
+
+ monitor.worked(1);
+
+ monitor.subTask(CodeUtilsNLS.UI_Common_SavingTheAndroidManifestXMLFile);
+
+ AndroidProjectManifestFile.saveToProject(getProject(), androidManifestFile,
+ true);
+ created = true;
+
+ monitor.worked(1);
+ }
+ else
+ {
+ if (isMainActivity)
+ {
+ boolean hasMainAction = false;
+ boolean hasLauncherCategory = false;
+
+ // Check if the existing activity already has the MAIN and LAUNCHER intents
+ if (existingActivity != null)
+ {
+ // Retrieve list of intent nodes
+ List<IntentFilterNode> intentFilterNodeList =
+ existingActivity.getIntentFilterNodes();
+
+ // Create a intent filter in case it does not exist
+ if (intentFilterNodeList.size() < 1)
+ {
+ IntentFilterNode intentNode = new IntentFilterNode();
+ intentFilterNodeList.add(intentNode);
+ existingActivity.addIntentFilterNode(intentNode);
+ }
+
+ for (IntentFilterNode intentFilterNode : intentFilterNodeList)
+ {
+ // Retrieve a list of intent actions
+ List<ActionNode> actionNodes = intentFilterNode.getActionNodes();
+
+ for (ActionNode actionNode : actionNodes)
+ {
+ if (actionNode.getName().equals(INTENT_ACTION_MAIN_NAME))
+ {
+ hasMainAction = true;
+ }
+ break;
+
+ }
+
+ // Retrieve a list of intent categories
+ List<CategoryNode> categoryNodes =
+ intentFilterNode.getCategoryNodes();
+
+ for (CategoryNode categoryNode : categoryNodes)
+ {
+ if (categoryNode.getName()
+ .equals(INTENT_CATEGORY_LAUNCHER_NAME))
+ {
+ hasLauncherCategory = true;
+ }
+ break;
+ }
+
+ // If both the action and launcher are missing, insert them and break the loop to avoid duplicates
+ if (!hasMainAction && !hasLauncherCategory)
+ {
+ intentFilterNode.addActionNode(new ActionNode(
+ INTENT_ACTION_MAIN_NAME));
+ intentFilterNode.addCategoryNode(new CategoryNode(
+ INTENT_CATEGORY_LAUNCHER_NAME));
+ break;
+ }
+ }
+ }
+
+ monitor.subTask(CodeUtilsNLS.UI_Common_SavingTheAndroidManifestXMLFile);
+
+ AndroidProjectManifestFile.saveToProject(getProject(), androidManifestFile,
+ true);
+ created = true;
+
+ monitor.worked(1);
+
+ }
+ else
+ {
+ created = true;
+ }
+ }
+
+ }
+ }
+ catch (AndroidException e)
+ {
+ String errMsg =
+ NLS.bind(CodeUtilsNLS.EXC_Activity_CannotUpdateTheManifestFile, getName(),
+ e.getLocalizedMessage());
+ throw new AndroidException(errMsg);
+ }
+ catch (CoreException e)
+ {
+ String errMsg =
+ NLS.bind(CodeUtilsNLS.EXC_Activity_CannotUpdateTheManifestFile, getName(),
+ e.getLocalizedMessage());
+ throw new AndroidException(errMsg);
+ }
+ finally
+ {
+ monitor.done();
+ }
+
+ return created;
+ }
+
+ /**
+ * Adds the Activity label value on the strings resource file
+ *
+ * @param monitor the progress monitor
+ *
+ * @return The label value if it has been added to the strings resource file or null otherwise
+ * @throws AndroidException
+ */
+ private String createActivityLabel(IProgressMonitor monitor) throws AndroidException
+ {
+ String resLabel = null;
+
+ if ((getLabel() != null) && (getLabel().trim().length() > 0))
+ {
+ try
+ {
+ monitor.subTask(CodeUtilsNLS.UI_Common_UpdatingTheStringsResourceFile);
+
+ ResourceFile stringsFile =
+ AndroidProjectResources.getResourceFile(getProject(), NodeType.String);
+
+ monitor.worked(1);
+
+ if (stringsFile.getResourcesNode() == null)
+ {
+ stringsFile.addResourceEntry(new ResourcesNode());
+ }
+
+ resLabel =
+ stringsFile.getNewResourceName(getName() + ACTIVITY_RESOURCE_LABEL_SUFFIX);
+
+ com.motorola.studio.android.model.resources.types.StringNode strNode =
+ new com.motorola.studio.android.model.resources.types.StringNode(resLabel);
+ strNode.setNodeValue(getLabel());
+
+ stringsFile.getResourcesNode().addChildNode(strNode);
+
+ monitor.worked(1);
+
+ AndroidProjectResources
+ .saveResourceFile(getProject(), stringsFile, NodeType.String);
+
+ monitor.worked(1);
+ }
+ catch (CoreException e)
+ {
+ String errMsg =
+ NLS.bind(CodeUtilsNLS.EXC_Activity_CannotCreateTheActivityLabel,
+ e.getLocalizedMessage());
+ throw new AndroidException(errMsg);
+ }
+ catch (AndroidException e)
+ {
+ String errMsg =
+ NLS.bind(CodeUtilsNLS.EXC_Activity_CannotCreateTheActivityLabel,
+ e.getLocalizedMessage());
+ throw new AndroidException(errMsg);
+ }
+ }
+
+ return resLabel;
+ }
+
+ /**
+ * @return The default onStart() method signature including return value and visibility level.
+ */
+ public String getOnStartMessage()
+ {
+ return "protected void onStart()"; //$NON-NLS-1$
+ }
+
+ /**
+ * @return True if this activity is to be set as main activity. Otherwise, returns false.
+ */
+ public boolean isMainActivity()
+ {
+ return isMainActivity;
+ }
+
+ /**
+ * @param isMainActivity Set to true if this activity is to be set as main activity.
+ */
+ public void setMainActivity(boolean isMainActivity)
+ {
+ this.isMainActivity = isMainActivity;
+ }
+} \ No newline at end of file
diff --git a/src/plugins/android.codeutils/src/com/motorola/studio/android/model/ActivityBasedOnTemplate.java b/src/plugins/android.codeutils/src/com/motorola/studio/android/model/ActivityBasedOnTemplate.java
new file mode 100644
index 0000000..3e8c8f6
--- /dev/null
+++ b/src/plugins/android.codeutils/src/com/motorola/studio/android/model/ActivityBasedOnTemplate.java
@@ -0,0 +1,1982 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.model;
+
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.ResourceBundle;
+import java.util.regex.Matcher;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IFolder;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.FileLocator;
+import org.eclipse.core.runtime.IConfigurationElement;
+import org.eclipse.core.runtime.IExtension;
+import org.eclipse.core.runtime.IExtensionPoint;
+import org.eclipse.core.runtime.IExtensionRegistry;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.core.runtime.SubProgressMonitor;
+import org.eclipse.datatools.modelbase.sql.tables.Column;
+import org.eclipse.datatools.modelbase.sql.tables.Table;
+import org.eclipse.jdt.core.ICompilationUnit;
+import org.eclipse.jdt.core.IPackageFragment;
+import org.eclipse.jdt.core.JavaCore;
+import org.eclipse.jdt.core.JavaModelException;
+import org.eclipse.jdt.core.formatter.CodeFormatter;
+import org.eclipse.jdt.internal.core.DocumentAdapter;
+import org.eclipse.jdt.internal.corext.util.CodeFormatterUtil;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.wizard.IWizardContainer;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.sequoyah.localization.tools.datamodel.node.StringArrayNode;
+import org.eclipse.sequoyah.localization.tools.datamodel.node.StringNode;
+import org.eclipse.text.edits.TextEdit;
+import org.osgi.framework.Bundle;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import com.motorola.studio.android.codeutils.CodeUtilsActivator;
+import com.motorola.studio.android.codeutils.i18n.CodeUtilsNLS;
+import com.motorola.studio.android.common.IAndroidConstants;
+import com.motorola.studio.android.common.exception.AndroidException;
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.common.log.UsageDataConstants;
+import com.motorola.studio.android.common.utilities.EclipseUtils;
+import com.motorola.studio.android.manifest.AndroidProjectManifestFile;
+import com.motorola.studio.android.model.manifest.AndroidManifestFile;
+import com.motorola.studio.android.model.manifest.dom.ActionNode;
+import com.motorola.studio.android.model.manifest.dom.ActivityNode;
+import com.motorola.studio.android.model.manifest.dom.ApplicationNode;
+import com.motorola.studio.android.model.manifest.dom.CategoryNode;
+import com.motorola.studio.android.model.manifest.dom.IntentFilterNode;
+import com.motorola.studio.android.model.manifest.dom.ManifestNode;
+import com.motorola.studio.android.model.manifest.dom.UsesPermissionNode;
+import com.motorola.studio.android.model.resources.ResourceFile;
+import com.motorola.studio.android.model.resources.types.AbstractResourceNode.NodeType;
+import com.motorola.studio.android.model.resources.types.ResourcesNode;
+import com.motorola.studio.android.resources.AndroidProjectResources;
+
+/**
+ * Activity Controller Model.
+ * As part of a MVC architecture, this class should communicate with the Wizard UI
+ * to provide all needed information to create a functional Activity.
+ */
+@SuppressWarnings("restriction")
+public class ActivityBasedOnTemplate extends Launcher
+{
+ private static final String NEW_LINE = "\n";
+
+ public static String DATABASE_LIST_SAMPLE_LOCALIZED = ""; //$NON-NLS-1$
+
+ public static String LIST_ACTIVITIES_SAMPLE_LOCALIZED = ""; //$NON-NLS-1$
+
+ private static final String INTENT_ACTION_MAIN_NAME = "android.intent.action.MAIN"; //$NON-NLS-1$
+
+ private static final String INTENT_CATEGORY_LAUNCHER_NAME = "android.intent.category.LAUNCHER"; //$NON-NLS-1$
+
+ private static final String ACTIVITY_RESOURCE_LABEL_SUFFIX = "ActivityLabel"; //$NON-NLS-1$
+
+ private static final int MANIFEST_UPDATING_STEPS = 6;
+
+ private static final int RESOURCES_UPDATING_STEPS = 3;
+
+ public static final String ACTIVITY_SAMPLES_FOLDER = "templates/activity_samples"; //$NON-NLS-1$
+
+ private static final String SAMPLES_CONFIG_FILE_NAME = "samples_config.xml"; //$NON-NLS-1$
+
+ private static final String LIST_ACTIVITIES_CONFIG_FILE_NAME = "list_activities_config.xml"; //$NON-NLS-1$
+
+ private static final String STRINGS_FILE = "strings.xml"; //$NON-NLS-1$
+
+ public static final String PREVIEW_FILE_NAME = "preview.png"; //$NON-NLS-1$
+
+ public static final String NINE_PATCH_QUALIFIER = ".9";
+
+ private static final String RES_TYPE_LAYOUT = "layout"; //$NON-NLS-1$
+
+ private static final String RES_TYPE_DRAWABLE = "drawable"; //$NON-NLS-1$
+
+ private static final String RES_TYPE_ANIM = "anim"; //$NON-NLS-1$
+
+ private static final String RES_TYPE_SOURCE = "src"; //$NON-NLS-1$
+
+ private static final String RES_TYPE_VALUES = "values"; //$NON-NLS-1$
+
+ private static final String RES_TYPE_MENU = "menu"; //$NON-NLS-1$
+
+ private static final String RES_TYPE_XML = "xml"; //$NON-NLS-1$
+
+ private static final String DESCRIPTION_TAG = "description"; //$NON-NLS-1$
+
+ private static final String PREVIEW_TAG = "preview"; //$NON-NLS-1$
+
+ private static final String RESOURCE_TYPE_TAG = "resourceType"; //$NON-NLS-1$
+
+ private static final String MODIFIER_TAG = "modifier"; //$NON-NLS-1$
+
+ private static final String FILE_TAG = "file"; //$NON-NLS-1$
+
+ private static final String NAME_TAG = "name"; //$NON-NLS-1$
+
+ private static final String FINAL_NAME_TAG = "finalName"; //$NON-NLS-1$
+
+ private static final String SAMPLE_TAG = "sample"; //$NON-NLS-1$
+
+ private static final String DRAWABLE_REPLACE_TAG = "#drawable_name#"; //$NON-NLS-1$
+
+ private static final String ANIM_REPLACE_TAG = "#anim_name#"; //$NON-NLS-1$
+
+ private static final String LAYOUT_REPLACE_TAG = "#layout_name#"; //$NON-NLS-1$
+
+ private static final String PACKAGE_REPLACE_TAG = "#package_name#"; //$NON-NLS-1$
+
+ private static final String CLASS_REPLACE_TAG = "#class_name#"; //$NON-NLS-1$
+
+ private static final String MAIN_ACTIVITY_REPLACE_TAG = "#main_activity#"; //$NON-NLS-1$
+
+ private static final String XML_REPLACE_TAG = "#xml_name#"; //$NON-NLS-1$
+
+ private static final String MENU_REPLACE_TAG = "#menu_name#"; //$NON-NLS-1$
+
+ private static final String JAVA_EXTENSION = ".java"; //$NON-NLS-1$
+
+ public static final String DATABASE_LIST_SAMPLE = "Database List"; //$NON-NLS-1$
+
+ public static final String LIST_ACTIVITIES_SAMPLE = "List Activities"; //$NON-NLS-1$
+
+ public static final String TABS_SAMPLE = "Tabs"; //$NON-NLS-1$
+
+ public static enum SAMPLE_CATEGORY
+ {
+ SAMPLE_ACTIVITIES_CATEGORY, LIST_ACTIVITIES_CATEGORY
+ };
+
+ private boolean onStart = false;
+
+ private String sample = null;
+
+ private SAMPLE_CATEGORY sampleCategory = SAMPLE_CATEGORY.SAMPLE_ACTIVITIES_CATEGORY;
+
+ private boolean createOpenHelper = false;
+
+ private HashMap<String, HashMap<String, List<TemplateFile>>> availableSamples = null;
+
+ private HashMap<String, HashMap<String, List<TemplateFile>>> availableListSamples = null;
+
+ private final HashMap<String, String> samplesDescription = new HashMap<String, String>();
+
+ private final HashMap<String, String> samplesPreview = new HashMap<String, String>();
+
+ HashMap<String, String> workspaceResourceNames = null;
+
+ private List<StringNode> sampleStringNodes = null;
+
+ private List<StringArrayNode> sampleArrayNodes = null;
+
+ /*
+ * Boolean flag to tell if the Activity is based on the DatabaseList sample or not
+ */
+ private boolean useSampleDatabaseColumnsSelected = false;
+
+ /*
+ * Boolean flag to tell if a table is selected if user has choose Database template
+ */
+ private boolean isDatabaseTableSelected = false;
+
+ /*
+ * Boolean flag to tell if the Activity is based on the DatabaseList
+ * defines the SQL Open Helper (class name and package name)
+ */
+ private boolean sqlOpenHelperDefined = false;
+
+ /*
+ * Boolean flag to tell if a list activity template is selected.
+ */
+ private boolean isListActivitySelected = false;
+
+ private boolean isDatabaseTemplateSelected = false;
+
+ /*
+ * Collector interface specific to the DatabaseList sample
+ */
+ private IDatabaseSampleActivityParametersWizardCollector collector = null;
+
+ /*
+ * Database name used by the collector
+ */
+ private String collectorDatabaseName = null;
+
+ /*
+ * Database table used by the collector
+ */
+ private Table collectorTable = null;
+
+ /*
+ * Database sqlOpenHelper class name used by the collector
+ */
+ private String sqlOpenHelperClassName = null;
+
+ /*
+ * Database sqlOpenHelper package used by the collector
+ */
+ private String sqlOpenHelperPackageName = null;
+
+ /*
+ * List of columns used by the collector
+ */
+ private ArrayList<Column> collectorColumnList = new ArrayList<Column>();
+
+ /*
+ * Boolean flag to tell if the Activity will be set as MAIN or not in the AndroidManifest.
+ */
+ private boolean isMainActivity = false;
+
+ /**
+ * Check if the onStart Method should be created
+ * @return
+ */
+ public boolean isOnStart()
+ {
+ return onStart;
+ }
+
+ /**
+ * Change the onStart create property
+ * @param onStart
+ */
+ public void setOnStart(boolean onStart)
+ {
+ this.onStart = onStart;
+ }
+
+ /**
+ * Constructor for the Activity.
+ */
+ public ActivityBasedOnTemplate()
+ {
+ super(IAndroidConstants.CLASS_ACTIVITY);
+ DATABASE_LIST_SAMPLE_LOCALIZED =
+ ResourceBundle.getBundle("plugin").getString("Activity_Samples_DB_name"); //$NON-NLS-1$ //$NON-NLS-2$
+ LIST_ACTIVITIES_SAMPLE_LOCALIZED =
+ ResourceBundle
+ .getBundle("plugin").getString("Activity_Samples_ListActivities_name"); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+
+ public void setSample(String sample)
+ {
+ this.sample = sample;
+ }
+
+ public void setSampleCategoty(SAMPLE_CATEGORY sampleCategory)
+ {
+ this.sampleCategory = sampleCategory;
+ }
+
+ public SAMPLE_CATEGORY getSampleCategoty()
+ {
+ return this.sampleCategory;
+ }
+
+ public String getSample()
+ {
+ return sample;
+ }
+
+ public boolean isBasicInformationFilledIn()
+ {
+ boolean needInfo = false;
+ IStatus status = getStatus();
+
+ if (status.getSeverity() > IStatus.WARNING)
+ {
+ needInfo = true;
+ }
+
+ return needInfo;
+ }
+
+ /**
+ * Enables finish button when page is filled.
+ */
+ @Override
+ public boolean needMoreInformation()
+ {
+ boolean needInfo = isBasicInformationFilledIn();
+
+ if ((sample == null))
+ {
+ needInfo = true;
+ }
+ else if (sample.equals(ActivityBasedOnTemplate.DATABASE_LIST_SAMPLE_LOCALIZED))
+ {
+ if (useSampleDatabaseColumnsSelected && sqlOpenHelperDefined && isDatabaseTableSelected)
+ {
+ needInfo = false || needInfo;
+ }
+ else
+ {
+ needInfo = true;
+ }
+ }
+ else if (sample.equals(ActivityBasedOnTemplate.LIST_ACTIVITIES_SAMPLE_LOCALIZED))
+ {
+ if (isListActivitySelected)
+ {
+ needInfo = false || needInfo;
+ }
+ else
+ {
+ needInfo = true;
+ }
+ }
+
+ return needInfo;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.model.IWizardModel#save(org.eclipse.jface.wizard.IWizardContainer, org.eclipse.core.runtime.IProgressMonitor)
+ */
+ @Override
+ public boolean save(IWizardContainer container, IProgressMonitor monitor)
+ throws AndroidException
+ {
+ boolean templateFiles = true;
+ String sourceFolder = null;
+ workspaceResourceNames = new HashMap<String, String>();
+
+ try
+ {
+ HashMap<String, List<TemplateFile>> selectedSample = null;
+
+ if (sampleCategory.equals(SAMPLE_CATEGORY.SAMPLE_ACTIVITIES_CATEGORY))
+ {
+ selectedSample = availableSamples.get(getSample());
+ }
+ else
+ {
+ selectedSample = availableListSamples.get(getSample());
+ }
+
+ Iterator<String> iterator = selectedSample.keySet().iterator();
+
+ // UDC log for activity samples use
+ StudioLogger.collectUsageData(
+ UsageDataConstants.WHAT_SAMPLE_ACTIVITY_CREATED, //$NON-NLS-1$
+ UsageDataConstants.KIND_SAMPLE_ACTIVITY_CREATED,
+ "Activity using sample " + getSample(), //$NON-NLS-1$
+ CodeUtilsActivator.PLUGIN_ID, CodeUtilsActivator.getDefault().getBundle()
+ .getVersion().toString());
+
+ /*
+ * Copy resources that have no references to other types
+ */
+ while (iterator.hasNext() && templateFiles)
+ {
+ String resourceType = iterator.next();
+
+ /*
+ * First of all copy animation files, since they have no references so far
+ */
+ if (resourceType.equals(RES_TYPE_ANIM))
+ {
+ //handle animation files
+ List<TemplateFile> filesList = selectedSample.get(resourceType);
+
+ sourceFolder = getAnimFolder(null);
+ for (int i = 0; i < filesList.size(); i++)
+ {
+ TemplateFile templateFile = filesList.get(i);
+
+ templateFiles =
+ templateFiles
+ & copyTemplateFile(templateFile.getModelName(),
+ templateFile.getFinalName(), sourceFolder,
+ ANIM_REPLACE_TAG, false, monitor);
+ }
+
+ }
+ else if (resourceType.equals(RES_TYPE_DRAWABLE))
+ {
+ List<TemplateFile> filesList = selectedSample.get(resourceType);
+
+ for (int i = 0; i < filesList.size(); i++)
+ {
+ TemplateFile templateFile = filesList.get(i);
+
+ sourceFolder = getDrawableFolder(templateFile.getModifier());
+
+ templateFiles =
+ templateFiles
+ & copyTemplateFile(templateFile.getModelName(),
+ templateFile.getFinalName(), sourceFolder,
+ DRAWABLE_REPLACE_TAG, templateFile.getFinalName()
+ .endsWith(".xml"), monitor);
+
+ }
+ }
+ else if (resourceType.equals(RES_TYPE_XML))
+ {
+ List<TemplateFile> filesList = selectedSample.get(resourceType);
+
+ for (int i = 0; i < filesList.size(); i++)
+ {
+ TemplateFile templateFile = filesList.get(i);
+
+ sourceFolder = getXmlFolder(templateFile.getModifier());
+
+ templateFiles =
+ templateFiles
+ & copyTemplateFile(templateFile.getModelName(),
+ templateFile.getFinalName(), sourceFolder,
+ XML_REPLACE_TAG, false, monitor);
+ }
+ }
+ else if (resourceType.equals(RES_TYPE_VALUES))
+ {
+ //treat sample string.xml
+ List<TemplateFile> filesList = selectedSample.get(resourceType);
+
+ for (int i = 0; i < filesList.size(); i++)
+ {
+ TemplateFile templateFile = filesList.get(i);
+
+ sourceFolder = getValuesFolder(templateFile.getModifier());
+
+ parseStringXmlNodes(templateFile.getModelName());
+
+ //add to string.xml
+ EclipseUtils.createOrUpdateDictionaryFile(getProject(),
+ getSampleStringNodes(), getSampleArrayNodes(), monitor);
+ }
+ }
+ else if (resourceType.equals(RES_TYPE_MENU))
+ {
+ //treat menu xml files
+ List<TemplateFile> filesList = selectedSample.get(resourceType);
+
+ sourceFolder = getMenuFolder(null);
+
+ for (int i = 0; i < filesList.size(); i++)
+ {
+ TemplateFile templateFile = filesList.get(i);
+
+ templateFiles =
+ templateFiles
+ & copyTemplateFile(templateFile.getModelName(),
+ templateFile.getFinalName(), sourceFolder,
+ MENU_REPLACE_TAG, false, monitor);
+ }
+ }
+
+ }
+
+ /*
+ * Copy layout files and replace references
+ */
+ List<TemplateFile> filesList = selectedSample.get(RES_TYPE_LAYOUT);
+
+ if (filesList != null)
+ {
+ for (int i = 0; i < filesList.size(); i++)
+ {
+ TemplateFile templateFile = filesList.get(i);
+
+ sourceFolder = getLayoutFolder(templateFile.getModifier());
+
+ templateFiles =
+ templateFiles
+ & copyTemplateFile(templateFile.getModelName(),
+ templateFile.getFinalName(), sourceFolder,
+ LAYOUT_REPLACE_TAG, true, monitor);
+
+ }
+ }
+ }
+ catch (Exception e)
+ {
+ throw new AndroidException(e);
+ }
+
+ boolean classCreated = false;
+ if (templateFiles)
+ {
+ classCreated = createActivityClass(monitor);
+ }
+ boolean addedOnManifest = false;
+
+ if (classCreated)
+ {
+ addedOnManifest = createActivityOnManifest(monitor);
+ }
+
+ // Logs all permissions used in UDC log
+ super.save(container, monitor);
+
+ try
+ {
+ ResourcesPlugin.getWorkspace().getRoot()
+ .refreshLocal(IResource.DEPTH_INFINITE, monitor);
+ }
+ catch (CoreException e)
+ {
+ // do nothing
+ }
+ return classCreated && addedOnManifest && templateFiles;
+
+ }
+
+ private String getValuesFolder(String modifier)
+ {
+ return IAndroidConstants.FD_RES + IPath.SEPARATOR + IAndroidConstants.FD_VALUES
+ + ((modifier != null) ? "-" + modifier : "");
+ }
+
+ private String getXmlFolder(String modifier)
+ {
+ return IAndroidConstants.FD_RES + IPath.SEPARATOR + IAndroidConstants.FD_XML
+ + ((modifier != null) ? "-" + modifier : "");
+ }
+
+ private String getDrawableFolder(String modifier)
+ {
+ return IAndroidConstants.FD_RES + IPath.SEPARATOR + IAndroidConstants.FD_DRAWABLE
+ + ((modifier != null) ? "-" + modifier : "");
+ }
+
+ private String getAnimFolder(String modifier)
+ {
+ return IAndroidConstants.FD_RES + IPath.SEPARATOR + IAndroidConstants.FD_ANIM;
+ }
+
+ private String getLayoutFolder(String modifier)
+ {
+ return IAndroidConstants.FD_RES + IPath.SEPARATOR + IAndroidConstants.FD_LAYOUT
+ + ((modifier != null) ? "-" + modifier : "");
+ }
+
+ private String getMenuFolder(String modifier)
+ {
+ return IAndroidConstants.FD_RES + IPath.SEPARATOR + IAndroidConstants.FD_MENU
+ + ((modifier != null) ? "-" + modifier : "");
+ }
+
+ public List<StringNode> getSampleStringNodes()
+ {
+ if (sampleStringNodes == null)
+ {
+ sampleStringNodes = new ArrayList<StringNode>();
+ }
+ return sampleStringNodes;
+ }
+
+ private void setSampleStringNodes(List<StringNode> sampleStringNodes)
+ {
+ this.sampleStringNodes = sampleStringNodes;
+ }
+
+ public List<StringArrayNode> getSampleArrayNodes()
+ {
+ if (sampleArrayNodes == null)
+ {
+ sampleArrayNodes = new ArrayList<StringArrayNode>();
+ }
+ return sampleArrayNodes;
+ }
+
+ private void setSampleArrayNodes(List<StringArrayNode> sampleArrayNodes)
+ {
+ this.sampleArrayNodes = sampleArrayNodes;
+ }
+
+ private void parseStringXmlNodes(String sampleStringFileName)
+ {
+ List<StringArrayNode> newArrayList = new ArrayList<StringArrayNode>();
+ List<StringNode> newStrList = new ArrayList<StringNode>();
+
+ try
+ {
+ File sampleStringXmlFile =
+ new File(FileLocator.toFileURL(
+ CodeUtilsActivator
+ .getDefault()
+ .getBundle()
+ .getEntry(
+ ACTIVITY_SAMPLES_FOLDER + IPath.SEPARATOR
+ + sampleStringFileName)).getFile());
+
+ DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
+ DocumentBuilder db = dbf.newDocumentBuilder();
+ Document doc = db.parse(sampleStringXmlFile);
+ doc.getDocumentElement().normalize();
+
+ //string nodes elements
+ NodeList nodeLst = doc.getElementsByTagName("string"); //$NON-NLS-1$
+ for (int s = 0; s < nodeLst.getLength(); s++)
+ {
+ Node fstNode = nodeLst.item(s);
+
+ if (fstNode.getNodeType() == Node.ELEMENT_NODE)
+ {
+ Element fstElmnt = (Element) fstNode;
+ String nameAtr = fstElmnt.getAttribute("name"); //$NON-NLS-1$
+ String stringValue = fstElmnt.getTextContent();
+ if ((nameAtr != null) && (nameAtr.length() > 0))
+ {
+ newStrList.add(new StringNode(nameAtr, stringValue));
+ }
+ }
+ }
+
+ //array nodes elements
+ nodeLst = doc.getElementsByTagName("string-array"); //$NON-NLS-1$
+ for (int s = 0; s < nodeLst.getLength(); s++)
+ {
+ Node fstNode = nodeLst.item(s);
+
+ if (fstNode.getNodeType() == Node.ELEMENT_NODE)
+ {
+ Element fstElmnt = (Element) fstNode;
+ String nameAtr = fstElmnt.getAttribute("name"); //$NON-NLS-1$
+ StringArrayNode strArray = new StringArrayNode(nameAtr);
+
+ NodeList arrayItems = fstElmnt.getChildNodes();
+ for (int i = 0; i < arrayItems.getLength(); i++)
+ {
+ Node itemNode = arrayItems.item(i);
+ if (itemNode.getNodeType() == Node.ELEMENT_NODE)
+ {
+ Element itemElmnt = (Element) itemNode;
+ String itemValue = itemElmnt.getTextContent();
+ strArray.addValue(itemValue);
+ }
+ }
+ newArrayList.add(strArray);
+ }
+ }
+
+ setSampleStringNodes(newStrList);
+ setSampleArrayNodes(newArrayList);
+ }
+ catch (Exception e)
+ {
+ StudioLogger.error(ActivityBasedOnTemplate.class, e.getMessage() + " " //$NON-NLS-1$
+ + CodeUtilsNLS.UI_SampleSelectionPage_ErrorParsingStringXml, e);
+ }
+
+ return;
+ }
+
+ /*
+ * @param modelFilename Where file is going to be copied from
+ * @param finalFilename Final name for resource, if any
+ * @param sourceFolder Where file is going to be copied to
+ * @param monitor Progress monitor
+ * @param replacementTag tag do be replaced in the class file
+ * @return
+ * @throws Exception
+ */
+ private boolean copyTemplateFile(String modelFilename, String finalFilename,
+ String sourceFolder, String replacementTag, boolean replaceReferences,
+ IProgressMonitor monitor) throws Exception
+ {
+
+ IFolder pkgFolder = getProject().getFolder(sourceFolder);
+ if (!pkgFolder.exists())
+ {
+ pkgFolder.create(true, true, monitor);
+ }
+ IFile destFile = null;
+
+ int i = 0;
+
+ String[] resourceNameAndExtension = getResourceNameAndExtension(finalFilename);
+ String resourceName = resourceNameAndExtension[0];
+ String extension = resourceNameAndExtension[1];
+
+ resourceName = filterResourceName(resourceName);
+
+ //ensures file to be copied still does not exist
+ do
+ {
+ if (i == 0)
+ {
+ destFile = pkgFolder.getFile(resourceName + extension);
+ }
+ else
+ {
+ destFile = pkgFolder.getFile(resourceName + i + extension);
+ }
+ i++;
+ }
+ while (destFile.exists());
+
+ if (i != 1)
+ {
+ resourceName = resourceName + --i;
+ }
+
+ //updates tags to be replaced at class file
+ String literalFileName = Matcher.quoteReplacement(modelFilename);
+ workspaceResourceNames.put(replacementTag + literalFileName + "#", resourceName); //$NON-NLS-1$
+
+ monitor.beginTask(
+ CodeUtilsNLS.UI_ActivityBasedOnTemplateSupport_Configuring_Sample_Source_Task, 150);
+ InputStream is = null;
+
+ try
+ {
+
+ //gets template stream and creates a copy at user workspace
+
+ if (!replaceReferences)
+ {
+
+ Bundle bundle = CodeUtilsActivator.getDefault().getBundle();
+
+ URL url = bundle.getEntry((new StringBuilder("/")).append( //$NON-NLS-1$
+ ACTIVITY_SAMPLES_FOLDER + IPath.SEPARATOR + modelFilename).toString());
+
+ is = url.openStream();
+
+ }
+ else
+ {
+ // Get the original file as a string
+ String fileString =
+ EclipseUtils.readEmbeddedResource(CodeUtilsActivator.getDefault()
+ .getBundle(), ACTIVITY_SAMPLES_FOLDER + IPath.SEPARATOR
+ + modelFilename);
+
+ fileString = replaceResourceNames(fileString);
+
+ is = new ByteArrayInputStream(fileString.getBytes("UTF-8"));
+
+ }
+
+ destFile.create(is, IResource.NONE, new SubProgressMonitor(monitor, 100));
+
+ }
+ finally
+ {
+ if (is != null)
+ {
+ is.close();
+ }
+ monitor.done();
+ }
+ return true;
+ }
+
+ private String[] getResourceNameAndExtension(String fullName)
+ {
+
+ String[] result = new String[2];
+
+ String resourceName = fullName.substring(fullName.lastIndexOf("/") + 1, //$NON-NLS-1$
+ fullName.lastIndexOf(".")); //$NON-NLS-1$
+ String extension = fullName.substring(fullName.lastIndexOf(".")); //$NON-NLS-1$
+
+ //detect 9-patch files
+ if (extension.equals(".png") && resourceName.endsWith(NINE_PATCH_QUALIFIER))
+ {
+ resourceName =
+ resourceName
+ .substring(0, resourceName.length() - NINE_PATCH_QUALIFIER.length());
+ extension = NINE_PATCH_QUALIFIER + extension;
+ }
+ result[0] = resourceName;
+ result[1] = extension;
+
+ return result;
+
+ }
+
+ /*
+ * Creates the Activity java class
+ *
+ * @param monitor the progress monitor
+ *
+ * @return true if the class has been created or false otherwise
+ * @throws AndroidException
+ */
+ private boolean createActivityClass(IProgressMonitor monitor) throws AndroidException
+ {
+ boolean created = false;
+
+ monitor.subTask(CodeUtilsNLS.UI_Activity_CreatingTheActivityJavaClass);
+
+ String[] samplePath = null;
+
+ try
+ {
+
+ samplePath = getSamplePath(RES_TYPE_SOURCE);
+ createJavaClassFileFromTemplate(samplePath, monitor);
+ created = true;
+ }
+ catch (JavaModelException e)
+ {
+ String errMsg =
+ NLS.bind(CodeUtilsNLS.EXC_Activity_CannotCreateTheActivityClass, getName(),
+ e.getLocalizedMessage());
+
+ throw new AndroidException(errMsg);
+ }
+ catch (AndroidException e)
+ {
+ String errMsg =
+ NLS.bind(CodeUtilsNLS.EXC_Activity_CannotCreateTheActivityClass, getName(),
+ e.getLocalizedMessage());
+ throw new AndroidException(errMsg);
+ }
+
+ return created;
+ }
+
+ /**
+ * Package name (based on project name declared on manifest)
+ * @param project
+ * @return
+ * @throws CoreException Exception thrown in case there are problems handling the android project
+ * @throws AndroidException Exception thrown in case there are problems handling the android project
+ */
+ protected String getManifestPackageName(IProject project) throws AndroidException,
+ CoreException
+ {
+ // get android application name
+ AndroidManifestFile androidManifestFile =
+ AndroidProjectManifestFile.getFromProject(project);
+ String appNamespace = ""; //$NON-NLS-1$
+ if (androidManifestFile != null)
+ {
+ ManifestNode manifestNode = androidManifestFile.getManifestNode();
+ appNamespace = manifestNode.getPackageName();
+ }
+ // return the android application name along with a persistence constant
+ return appNamespace;
+ }
+
+ /**
+ * Creates the Java Class file based on text template file
+ *
+ * @param sourcePath The path to the file
+ * @param monitor The progress monitor
+ * @throws JavaModelException
+ * @throws AndroidException
+ */
+ @SuppressWarnings(
+ {
+ "rawtypes", "unchecked"
+ })
+ protected void createJavaClassFileFromTemplate(String[] sourcePath, IProgressMonitor monitor)
+ throws JavaModelException, AndroidException
+ {
+
+ //only one class supported
+ for (int i = 0; i < sourcePath.length; i++)
+ {
+ String loadedTemplate =
+ EclipseUtils.readEmbeddedResource(CodeUtilsActivator.getDefault().getBundle(),
+ sourcePath[i]);
+ String packageName = getPackageFragment().getElementName();
+
+ loadedTemplate = loadedTemplate.replaceAll(CLASS_REPLACE_TAG, getName());
+ loadedTemplate = loadedTemplate.replaceAll(PACKAGE_REPLACE_TAG, packageName);
+ loadedTemplate =
+ loadedTemplate.replaceAll("#FILL_PARENT_LPARAM#", getApiVersion() > 7
+ ? "MATCH_PARENT" : "FILL_PARENT");
+ try
+ {
+ loadedTemplate = loadedTemplate.replaceAll("#ManifestPackageName#", //$NON-NLS-1$
+ getManifestPackageName(getProject()));
+ }
+ catch (CoreException e)
+ {
+ throw new AndroidException("Failed to get manifest file to add import from R", e); //$NON-NLS-1$
+ }
+
+ if ((sample != null)
+ && sample.equals(ActivityBasedOnTemplate.DATABASE_LIST_SAMPLE_LOCALIZED))
+ {
+ collector = getDatabaseSampleActivityParametersWizardCollector();
+ if (collector != null)
+ {
+
+ collector.setDatabaseName(this.collectorDatabaseName);
+ collector.setTable(this.collectorTable);
+ collector.setSelectedColumns(collectorColumnList);
+ collector.setSqlOpenHelperClassName(getSqlOpenHelperClassName());
+ collector.setSqlOpenHelperPackageName(getSqlOpenHelperPackageName());
+ collector.setCreateOpenHelper(isCreateOpenHelper());
+
+ //using Database list sample - it is an special case because it get data from an specific .db table
+ loadedTemplate =
+ loadedTemplate.replaceAll("#dbName#", collector.getDatabaseName()); //$NON-NLS-1$
+ loadedTemplate =
+ loadedTemplate.replaceAll("#tableName#", collector.getTableName()); //$NON-NLS-1$
+ loadedTemplate =
+ loadedTemplate.replaceAll(
+ "#tableNameUpperCase#", collector.getTableName() //$NON-NLS-1$
+ .toUpperCase());
+ loadedTemplate =
+ loadedTemplate.replaceAll(
+ "#tableNameLowerCase#", collector.getTableName() //$NON-NLS-1$
+ .toLowerCase());
+ loadedTemplate =
+ loadedTemplate.replaceAll("#columsNames#", collector.getColumnsNames()); //$NON-NLS-1$
+ loadedTemplate = loadedTemplate.replaceAll("#constColumnsNames#", //$NON-NLS-1$
+ collector.getConstColumnsNames());
+ loadedTemplate =
+ loadedTemplate.replaceAll(
+ "#columnGetValues#", collector.getCursorValues()); //$NON-NLS-1$
+ loadedTemplate =
+ loadedTemplate.replaceAll(
+ "#columnAddRows#", collector.getAddColumnsToRow()); //$NON-NLS-1$
+ loadedTemplate = loadedTemplate.replaceAll("#sqlOpenHelperName#", //$NON-NLS-1$
+ getSqlOpenHelperClassName());
+ loadedTemplate = loadedTemplate.replaceAll("#imports#", collector.getImports()); //$NON-NLS-1$
+ loadedTemplate = loadedTemplate.replaceAll("#getReadableDatabase#", //$NON-NLS-1$
+ collector.getReadableDatabase());
+
+ if (isCreateOpenHelper())
+ {
+ collector.createSqlOpenHelper(getProject(), monitor);
+ }
+ }
+ }
+
+ //replace the main activity of the project, first used by action_bar template
+ try
+ {
+ //assume mainActivity be the activity we are creating (if the project has no main activity). Try to find the real main activity if the isMainActivity flag is false.
+ String mainActivityName = getName();
+
+ if (!isMainActivity)
+ {
+ ActivityNode mainActivityNode =
+ AndroidProjectManifestFile.getFromProject(getProject())
+ .getMainActivity();
+ if (mainActivityNode != null)
+ {
+ mainActivityName = mainActivityNode.getNodeProperties().get("android:name");
+ //remove a possible '.' that activities may contain before the name
+ if ((mainActivityName.length() > 0) && (mainActivityName.charAt(0) == '.'))
+ {
+ mainActivityName =
+ mainActivityName.substring(1, mainActivityName.length());
+ }
+ }
+ }
+
+ loadedTemplate =
+ loadedTemplate.replaceAll(MAIN_ACTIVITY_REPLACE_TAG, mainActivityName);
+ }
+ catch (CoreException e)
+ {
+ StudioLogger.error("Could not get Android Manifest File from project.");
+ }
+
+ loadedTemplate = replaceResourceNames(loadedTemplate);
+
+ IPackageFragment targetPackage =
+ getPackageFragmentRoot().getPackageFragment(
+ getPackageFragment().getElementName());
+
+ if (!targetPackage.exists())
+ {
+ getPackageFragmentRoot().createPackageFragment(targetPackage.getElementName(),
+ true, monitor);
+ }
+
+ /*
+ * Create activity class. Only the first src resource will become the Activity subclass.
+ * The other classes will be copied AS IS
+ */
+
+ String resourceName =
+ i == 0 ? getName() + JAVA_EXTENSION : Path.fromPortableString(sourcePath[i])
+ .lastSegment();
+ ICompilationUnit cu =
+ targetPackage
+ .createCompilationUnit(resourceName, loadedTemplate, true, monitor);
+
+ //indent activity class
+ try
+ {
+ ICompilationUnit workingCopy = cu.getWorkingCopy(monitor);
+ IDocument document = new DocumentAdapter(workingCopy.getBuffer());
+
+ //get project indentation configuration
+ Map mapOptions = JavaCore.create(getProject()).getOptions(true);
+
+ //changes to be applyed to the document
+ TextEdit textEdit =
+ CodeFormatterUtil.format2(CodeFormatter.K_COMPILATION_UNIT
+ | CodeFormatter.F_INCLUDE_COMMENTS, document.get(), 0,
+ System.getProperty("line.separator"), mapOptions); //$NON-NLS-1$
+
+ workingCopy.applyTextEdit(textEdit, monitor);
+ workingCopy.reconcile(ICompilationUnit.NO_AST, false, null, null);
+ workingCopy.commitWorkingCopy(true, monitor);
+ workingCopy.discardWorkingCopy();
+ }
+ catch (Exception ex)
+ {
+ //do nothing - indentation fails
+ }
+ }
+
+ }
+
+ private String replaceResourceNames(String textFile)
+ {
+ for (String curTag : workspaceResourceNames.keySet())
+ {
+ textFile = textFile.replaceAll(curTag, workspaceResourceNames.get(curTag));
+ }
+
+ return textFile;
+ }
+
+ public boolean isCreateOpenHelper()
+ {
+ return createOpenHelper;
+ }
+
+ public void setCreateOpenHelper(boolean createOpenHelper)
+ {
+ this.createOpenHelper = createOpenHelper;
+ }
+
+ /*
+ * Creates the Activity class entry on AndroidManifest.xml file
+ *
+ * @param monitor the progress monitor
+ *
+ * @return true if the entry has been added or false otherwise
+ * @throws AndroidException
+ */
+ private boolean createActivityOnManifest(IProgressMonitor monitor) throws AndroidException
+ {
+ boolean created = false;
+
+ try
+ {
+ int totalWork = MANIFEST_UPDATING_STEPS + RESOURCES_UPDATING_STEPS;
+
+ monitor.beginTask("", totalWork); //$NON-NLS-1$
+
+ monitor.subTask(CodeUtilsNLS.UI_Common_UpdatingTheAndroidManifestXMLFile);
+
+ AndroidManifestFile androidManifestFile =
+ AndroidProjectManifestFile.getFromProject(getProject());
+
+ monitor.worked(1);
+
+ ManifestNode manifestNode =
+ androidManifestFile != null ? androidManifestFile.getManifestNode() : null;
+
+ ApplicationNode applicationNode =
+ manifestNode != null ? manifestNode.getApplicationNode() : null;
+
+ monitor.worked(1);
+
+ if (applicationNode != null)
+ {
+ // Adds the added permission nodes to manifest file
+ List<String> permissionsNames = new ArrayList<String>();
+ for (UsesPermissionNode i : manifestNode.getUsesPermissionNodes())
+ {
+ if (!permissionsNames.contains(i.getName()))
+ {
+ permissionsNames.add(i.getName());
+ }
+ }
+
+ for (String intentFilterPermission : getIntentFilterPermissionsAsArray())
+ {
+ if (!permissionsNames.contains(intentFilterPermission))
+ {
+ manifestNode.addChild(new UsesPermissionNode(intentFilterPermission));
+ }
+ }
+
+ boolean activityExists = false;
+
+ // Existing activity, if exists
+ ActivityNode existingActivity = null;
+
+ String classQualifier =
+ (getPackageFragment().getElementName()
+ .equals(manifestNode.getPackageName()) ? "" : getPackageFragment() //$NON-NLS-1$
+ .getElementName()) + "."; //$NON-NLS-1$
+
+ for (ActivityNode activityNode : applicationNode.getActivityNodes())
+ {
+ if (activityNode.getName()
+ .substring(activityNode.getName().lastIndexOf('.') + 1)
+ .equals(getName()))
+ {
+ activityExists = true;
+ existingActivity = activityNode;
+ break;
+ }
+ }
+
+ if (isMainActivity)
+ {
+ boolean actionRemoved = false;
+
+ //check if there is a main activity. If so, removes actions and intent filter
+ for (ActivityNode activityNode : applicationNode.getActivityNodes())
+ {
+ if ((existingActivity != null) && existingActivity.equals(activityNode))
+ {
+ continue;
+ }
+
+ List<IntentFilterNode> intentList = activityNode.getIntentFilterNodes();
+ for (IntentFilterNode currentIntent : intentList)
+ {
+ actionRemoved = false;
+ List<ActionNode> actionList = currentIntent.getActionNodes();
+ for (ActionNode currentAction : actionList)
+ {
+ if (currentAction.getName().equals(INTENT_ACTION_MAIN_NAME))
+ {
+ currentIntent.removeActionNode(currentAction);
+ actionRemoved = true;
+ }
+ }
+ //if INTENT_ACTION_MAIN_NAME is found remove INTENT_CATEGORY_LAUNCHER_NAME too
+ if (actionRemoved)
+ {
+ List<CategoryNode> categoryList = currentIntent.getCategoryNodes();
+ for (CategoryNode currentCategory : categoryList)
+ {
+ if (currentCategory.getName().equals(
+ INTENT_CATEGORY_LAUNCHER_NAME))
+ {
+ currentIntent.removeCategoryNode(currentCategory);
+ }
+ }
+ }
+ //remove intent filter if empty
+ if (actionRemoved && (currentIntent.getChildren().length == 0))
+ {
+ activityNode.removeIntentFilterNode(currentIntent);
+ }
+ }
+ }
+ }
+
+ monitor.worked(1);
+
+ if (!activityExists)
+ {
+ ActivityNode activityNode = new ActivityNode(classQualifier + getName());
+
+ String activityLabel = createActivityLabel(monitor);
+
+ if (activityLabel != null)
+ {
+ activityNode.setLabel(AndroidProjectResources.STRING_CALL_PREFIX
+ + activityLabel);
+ }
+
+ IntentFilterNode intentFilterNode = new IntentFilterNode();
+
+ for (String intentFilterAction : getIntentFilterActionsAsArray())
+ {
+ intentFilterNode.addActionNode(new ActionNode(intentFilterAction));
+ }
+
+ for (String intentFilterCategory : getIntentFilterCategoriesAsArray())
+ {
+ intentFilterNode.addCategoryNode(new CategoryNode(intentFilterCategory));
+ }
+
+ // Check if we need to insert a filter action and filter category setting this activity as MAIN
+ if (isMainActivity)
+ {
+ intentFilterNode.addActionNode(new ActionNode(INTENT_ACTION_MAIN_NAME));
+ intentFilterNode.addCategoryNode(new CategoryNode(
+ INTENT_CATEGORY_LAUNCHER_NAME));
+
+ }
+
+ if (intentFilterNode.getChildren().length > 0)
+ {
+ activityNode.addIntentFilterNode(intentFilterNode);
+ }
+
+ applicationNode.addActivityNode(activityNode);
+
+ monitor.worked(1);
+
+ monitor.subTask(CodeUtilsNLS.UI_Common_SavingTheAndroidManifestXMLFile);
+
+ AndroidProjectManifestFile.saveToProject(getProject(), androidManifestFile,
+ true);
+ created = true;
+
+ monitor.worked(1);
+ }
+ else
+ {
+ if (isMainActivity)
+ {
+ boolean hasMainAction = false;
+ boolean hasLauncherCategory = false;
+
+ // Check if the existing activity already has the MAIN and LAUNCHER intents
+ if (existingActivity != null)
+ {
+ // Retrieve list of intent nodes
+ List<IntentFilterNode> intentFilterNodeList =
+ existingActivity.getIntentFilterNodes();
+
+ // Create a intent filter in case it does not exist
+ if (intentFilterNodeList.size() < 1)
+ {
+ IntentFilterNode intentNode = new IntentFilterNode();
+ intentFilterNodeList.add(intentNode);
+ existingActivity.addIntentFilterNode(intentNode);
+ }
+
+ for (IntentFilterNode intentFilterNode : intentFilterNodeList)
+ {
+ // Retrieve a list of intent actions
+ List<ActionNode> actionNodes = intentFilterNode.getActionNodes();
+
+ for (ActionNode actionNode : actionNodes)
+ {
+ if (actionNode.getName().equals(INTENT_ACTION_MAIN_NAME))
+ {
+ hasMainAction = true;
+ }
+ break;
+
+ }
+
+ // Retrieve a list of intent categories
+ List<CategoryNode> categoryNodes =
+ intentFilterNode.getCategoryNodes();
+
+ for (CategoryNode categoryNode : categoryNodes)
+ {
+ if (categoryNode.getName()
+ .equals(INTENT_CATEGORY_LAUNCHER_NAME))
+ {
+ hasLauncherCategory = true;
+ }
+ break;
+ }
+
+ // If both the action and launcher are missing, insert them and break the loop to avoid duplicates
+ if (!hasMainAction && !hasLauncherCategory)
+ {
+ intentFilterNode.addActionNode(new ActionNode(
+ INTENT_ACTION_MAIN_NAME));
+ intentFilterNode.addCategoryNode(new CategoryNode(
+ INTENT_CATEGORY_LAUNCHER_NAME));
+ break;
+ }
+ }
+ }
+
+ monitor.subTask(CodeUtilsNLS.UI_Common_SavingTheAndroidManifestXMLFile);
+
+ AndroidProjectManifestFile.saveToProject(getProject(), androidManifestFile,
+ true);
+ created = true;
+
+ monitor.worked(1);
+
+ }
+ else
+ {
+ created = true;
+ }
+ }
+
+ }
+ }
+ catch (AndroidException e)
+ {
+ String errMsg =
+ NLS.bind(CodeUtilsNLS.EXC_Activity_CannotUpdateTheManifestFile, getName(),
+ e.getLocalizedMessage());
+ throw new AndroidException(errMsg);
+ }
+ catch (CoreException e)
+ {
+ String errMsg =
+ NLS.bind(CodeUtilsNLS.EXC_Activity_CannotUpdateTheManifestFile, getName(),
+ e.getLocalizedMessage());
+ throw new AndroidException(errMsg);
+ }
+ finally
+ {
+ monitor.done();
+ }
+
+ return created;
+ }
+
+ /**
+ * Adds the Activity label value on the strings resource file
+ *
+ * @param monitor the progress monitor
+ *
+ * @return The label value if it has been added to the strings resource file or null otherwise
+ * @throws AndroidException
+ */
+ protected String createActivityLabel(IProgressMonitor monitor) throws AndroidException
+ {
+ String resLabel = null;
+
+ if ((getLabel() != null) && (getLabel().trim().length() > 0))
+ {
+ try
+ {
+ monitor.subTask(CodeUtilsNLS.UI_Common_UpdatingTheStringsResourceFile);
+
+ ResourceFile stringsFile =
+ AndroidProjectResources.getResourceFile(getProject(), NodeType.String);
+
+ monitor.worked(1);
+
+ if (stringsFile.getResourcesNode() == null)
+ {
+ stringsFile.addResourceEntry(new ResourcesNode());
+ }
+
+ resLabel =
+ stringsFile.getNewResourceName(getName() + ACTIVITY_RESOURCE_LABEL_SUFFIX);
+
+ com.motorola.studio.android.model.resources.types.StringNode strNode =
+ new com.motorola.studio.android.model.resources.types.StringNode(resLabel);
+ strNode.setNodeValue(getLabel());
+
+ stringsFile.getResourcesNode().addChildNode(strNode);
+
+ monitor.worked(1);
+
+ AndroidProjectResources
+ .saveResourceFile(getProject(), stringsFile, NodeType.String);
+
+ monitor.worked(1);
+ }
+ catch (CoreException e)
+ {
+ String errMsg =
+ NLS.bind(CodeUtilsNLS.EXC_Activity_CannotCreateTheActivityLabel,
+ e.getLocalizedMessage());
+ throw new AndroidException(errMsg);
+ }
+ catch (AndroidException e)
+ {
+ String errMsg =
+ NLS.bind(CodeUtilsNLS.EXC_Activity_CannotCreateTheActivityLabel,
+ e.getLocalizedMessage());
+ throw new AndroidException(errMsg);
+ }
+ }
+
+ return resLabel;
+ }
+
+ /**
+ * Return the default onStart() method signature including return value and visibility level.
+ * @return
+ */
+ public String getOnStartMessage()
+ {
+ return "protected void onStart()"; //$NON-NLS-1$
+ }
+
+ /**
+ * @return The selected sample's file.
+ */
+ public String[] getSamplePath(String resourceType)
+ {
+ List<TemplateFile> resTypeFiles = null;
+
+ if (sampleCategory.equals(SAMPLE_CATEGORY.SAMPLE_ACTIVITIES_CATEGORY))
+ {
+ resTypeFiles = availableSamples.get(getSample()).get(resourceType);
+ }
+ else
+ {
+ resTypeFiles = availableListSamples.get(getSample()).get(resourceType);
+ }
+
+ String[] resTypePaths = new String[resTypeFiles.size()];
+
+ for (int i = 0; i < resTypeFiles.size(); i++)
+ {
+ resTypePaths[i] =
+ ACTIVITY_SAMPLES_FOLDER + IPath.SEPARATOR + resTypeFiles.get(i).getModelName();
+ }
+ return resTypePaths;
+ }
+
+ /**
+ * Evaluate available samples parsing samples_config.xml
+ * @return
+ */
+ public void evaluateSamplesList(SAMPLE_CATEGORY category)
+ {
+
+ HashMap<String, HashMap<String, List<TemplateFile>>> currentMap = null;
+ try
+ {
+ String configFileName;
+
+ if (category.equals(SAMPLE_CATEGORY.SAMPLE_ACTIVITIES_CATEGORY))
+ {
+ configFileName = SAMPLES_CONFIG_FILE_NAME;
+ availableSamples = new HashMap<String, HashMap<String, List<TemplateFile>>>();
+ currentMap = availableSamples;
+ }
+ else if (category.equals(SAMPLE_CATEGORY.LIST_ACTIVITIES_CATEGORY))
+ {
+ configFileName = LIST_ACTIVITIES_CONFIG_FILE_NAME;
+ availableListSamples = new HashMap<String, HashMap<String, List<TemplateFile>>>();
+ currentMap = availableListSamples;
+ }
+ else
+ {
+ throw new Exception();
+ }
+
+ File configFile =
+ new File(FileLocator.toFileURL(
+ CodeUtilsActivator
+ .getDefault()
+ .getBundle()
+ .getEntry(
+ ACTIVITY_SAMPLES_FOLDER + IPath.SEPARATOR
+ + configFileName)).getFile());
+
+ DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
+ DocumentBuilder db = dbf.newDocumentBuilder();
+ Document doc = db.parse(configFile);
+ doc.getDocumentElement().normalize();
+ NodeList nodeLst = doc.getElementsByTagName(SAMPLE_TAG);
+
+ for (int s = 0; s < nodeLst.getLength(); s++)
+ {
+
+ HashMap<String, List<TemplateFile>> currentSample = null;
+ Node fstNode = nodeLst.item(s);
+
+ if (fstNode.getNodeType() == Node.ELEMENT_NODE)
+ {
+
+ Element fstElmnt = (Element) fstNode;
+ NodeList fstNmElmntLst = fstElmnt.getElementsByTagName(FILE_TAG);
+ String nameAtr = fstElmnt.getAttribute(NAME_TAG);
+ nameAtr =
+ Platform.getResourceString(CodeUtilsActivator.getDefault().getBundle(),
+ nameAtr.trim());
+
+ String descAtr = fstElmnt.getAttribute(DESCRIPTION_TAG);
+ descAtr =
+ Platform.getResourceString(CodeUtilsActivator.getDefault().getBundle(),
+ descAtr.trim());
+
+ String folderAtr = fstElmnt.getAttribute(PREVIEW_TAG);
+ if (folderAtr != null)
+ {
+ samplesPreview.put(nameAtr, folderAtr);
+ }
+
+ samplesDescription.put(nameAtr, descAtr);
+ currentSample = new HashMap<String, List<TemplateFile>>();
+
+ for (int n = 0; n < fstNmElmntLst.getLength(); n++)
+ {
+ Node fileNode = fstNmElmntLst.item(n);
+ Element fileElmnt = (Element) fileNode;
+
+ String resType = fileElmnt.getAttribute(RESOURCE_TYPE_TAG);
+ String resName = fileElmnt.getAttribute(NAME_TAG);
+ String finalName = fileElmnt.getAttribute(FINAL_NAME_TAG);
+
+ String modifier = fileElmnt.getAttribute(MODIFIER_TAG);
+
+ TemplateFile templateFile =
+ new TemplateFile(resType, resName, ((finalName.equals(""))
+ ? resName : finalName), (((modifier.equals("")) ? null
+ : modifier)));
+
+ if ((resName != null) && (resName.lastIndexOf(".") > -1)) //$NON-NLS-1$
+ {
+ if (currentSample.get(resType) == null)
+ {
+ List<TemplateFile> curRes = new ArrayList<TemplateFile>();
+ curRes.add(templateFile);
+ currentSample.put(resType, curRes);
+ }
+ else
+ {
+ List<TemplateFile> curRes = currentSample.get(resType);
+ curRes.add(templateFile);
+ }
+ }
+ }
+
+ currentMap.put(nameAtr, currentSample);
+ }
+ }
+ }
+ catch (Exception e)
+ {
+ currentMap.clear();
+ StudioLogger.error(ActivityBasedOnTemplate.class,
+ CodeUtilsNLS.UI_GenerateSampleListError, e);
+ }
+ }
+
+ /**
+ * @return available samples
+ */
+ public HashMap<String, HashMap<String, List<TemplateFile>>> getAvailableSamples()
+ {
+ return availableSamples;
+ }
+
+ /**
+ * Return available list activities samples.
+ * @return available list activities samples
+ */
+ public HashMap<String, HashMap<String, List<TemplateFile>>> getListActivitiesSamples()
+ {
+ return availableListSamples;
+ }
+
+ public String getSamplePreview()
+ {
+ return samplesPreview.get(getSample());
+ }
+
+ /**
+ * Returns selected sample description
+ * @return sample description
+ */
+ public String getSampleDescription()
+ {
+ StringBuffer result = new StringBuffer();
+ HashMap<String, List<TemplateFile>> selectedSample = null;
+
+ if (sampleCategory.equals(SAMPLE_CATEGORY.SAMPLE_ACTIVITIES_CATEGORY))
+ {
+ selectedSample = availableSamples.get(getSample());
+ }
+ else
+ {
+ selectedSample = availableListSamples.get(getSample());
+ }
+
+ if (selectedSample != null)
+ {
+ result.append(samplesDescription.get(getSample()));
+ result.append(NEW_LINE); //$NON-NLS-1$
+ if (selectedSample.size() > 0)
+ {
+ result.append(NEW_LINE); //$NON-NLS-1$
+ result.append(CodeUtilsNLS.UI_SampleSelectionPage_Description);
+ result.append(NEW_LINE); //$NON-NLS-1$
+
+ for (String resourceType : selectedSample.keySet())
+ {
+ result.append("\n - "); //$NON-NLS-1$
+ List<TemplateFile> filesList = selectedSample.get(resourceType);
+ if (resourceType.equals(RES_TYPE_SOURCE))
+ {
+ for (int i = 0; i < filesList.size(); i++)
+ {
+ if (i > 0)
+ {
+ result.append("\n - "); //$NON-NLS-1$
+ }
+
+ if (getPackageFragment() != null)
+ {
+ IResource packageResource = getPackageFragment().getResource();
+ IPath projectRelativePath =
+ packageResource.getProjectRelativePath();
+ String tmpName =
+ filesList
+ .get(i)
+ .getFinalName()
+ .substring(
+ filesList.get(i).getFinalName()
+ .indexOf("/") + 1); //$NON-NLS-1$
+
+ /*
+ * Resolve class name.
+ * Keeping backward compatibility, the first declared resource of type RES_TYPE_SOURCE will be considered as the class that will become the Activity
+ * So the other RES_TYPE_SOURCE resources names are kept unchanged
+ */
+
+ if (i == 0)
+ {
+ String className = getName();
+ if ((className == null)
+ || ((className != null) && (className.length() == 0)))
+ {
+ className = "<Activity>";
+ }
+ tmpName =
+ tmpName.replace(
+ tmpName.substring(0, tmpName.indexOf(".")), //$NON-NLS-1$
+ className);
+ }
+ result.append(projectRelativePath.toString() + IPath.SEPARATOR
+ + tmpName);
+ result.append(" (" + getSampleResourceTypeFriendlyName(resourceType) //$NON-NLS-1$
+ + ")"); //$NON-NLS-1$
+ }
+ }
+ }
+ else
+ {
+ for (int i = 0; i < filesList.size(); i++)
+ {
+ if (i > 0)
+ {
+ result.append("\n - "); //$NON-NLS-1$
+ }
+
+ // append folder
+ if (resourceType.equals(RES_TYPE_DRAWABLE))
+ {
+ result.append(getDrawableFolder(filesList.get(i).getModifier())
+ + IPath.SEPARATOR);
+ }
+ else if (resourceType.equals(RES_TYPE_LAYOUT))
+ {
+ result.append(getLayoutFolder(filesList.get(i).getModifier())
+ + IPath.SEPARATOR);
+ }
+ else if (resourceType.equals(RES_TYPE_VALUES))
+ {
+ result.append(getValuesFolder(filesList.get(i).getModifier())
+ + IPath.SEPARATOR);
+ }
+ else if (resourceType.equals(RES_TYPE_ANIM))
+ {
+ result.append(getAnimFolder(filesList.get(i).getModifier())
+ + IPath.SEPARATOR);
+ }
+ else if (resourceType.equals(RES_TYPE_XML))
+ {
+ result.append(getXmlFolder(filesList.get(i).getModifier())
+ + IPath.SEPARATOR);
+ }
+ else if (resourceType.equals(RES_TYPE_MENU))
+ {
+ result.append(getMenuFolder(filesList.get(i).getModifier())
+ + IPath.SEPARATOR);
+ }
+
+ // append file name
+ if (resourceType.equals(RES_TYPE_VALUES))
+ {
+ result.append(STRINGS_FILE);
+ }
+ else
+ {
+ String[] resourceNameAndExtension =
+ getResourceNameAndExtension(filesList.get(i).getFinalName());
+ String resourceName = resourceNameAndExtension[0];
+ String extension = resourceNameAndExtension[1];
+
+ result.append(filterResourceName(resourceName) + extension); //$NON-NLS-1$
+ }
+ result.append(" (" + getSampleResourceTypeFriendlyName(resourceType) //$NON-NLS-1$
+ + ")"); //$NON-NLS-1$
+ }
+ }
+ }
+ }
+ }
+
+ return result.toString();
+ }
+
+ private String getSampleResourceTypeFriendlyName(String resourceType)
+ {
+ String result = null;
+ if (resourceType.equals(RES_TYPE_LAYOUT))
+ {
+ result = CodeUtilsNLS.UI_SampleSelectionPage_Description_LayoutFile;
+ }
+ else if (resourceType.equals(RES_TYPE_DRAWABLE))
+ {
+ result = CodeUtilsNLS.UI_SampleSelectionPage_Description_DrawableFile;
+ }
+ else if (resourceType.equals(RES_TYPE_SOURCE))
+ {
+ result = CodeUtilsNLS.UI_SampleSelectionPage_Description_JavaFile;
+ }
+ else if (resourceType.equals(RES_TYPE_VALUES))
+ {
+ result = CodeUtilsNLS.UI_SampleSelectionPage_Description_StringFile;
+ }
+ else if (resourceType.equals(RES_TYPE_XML))
+ {
+ result = CodeUtilsNLS.UI_SampleSelectionPage_Description_XMLFile;
+ }
+ else if (resourceType.equals(RES_TYPE_ANIM))
+ {
+ result = CodeUtilsNLS.UI_SampleSelectionPage_Description_AnimFile;
+ }
+ else if (resourceType.equals(RES_TYPE_MENU))
+ {
+ result = CodeUtilsNLS.UI_SampleSelectionPage_Description_MenuFile;
+ }
+ return result;
+
+ }
+
+ /*
+ * Filter file names to [a-z][1-9]
+ * @param name: string typed by the user
+ * @return filtered string
+ */
+ private String filterResourceName(String name)
+ {
+ String resourceName = name.toLowerCase();
+ StringBuilder bld = new StringBuilder();
+
+ for (int i = 0; i < resourceName.length(); i++)
+ {
+ char temp = resourceName.charAt(i);
+ if (Character.isLetter(temp) || Character.isDigit(temp))
+ {
+ bld.append(temp);
+ }
+ }
+
+ return bld.toString();
+ }
+
+ /*
+ * Extension point related ids section
+ */
+ private static final String PARAMETER_COLLECTOR_EXTENSION_POINT_ID =
+ CodeUtilsActivator.PLUGIN_ID + ".sampleActivityDatabase"; //$NON-NLS-1$
+
+ private static final String PARAMETER_COLLECTOR_ELEM = "parameterCollector"; //$NON-NLS-1$
+
+ private static final String IMPL_CLASS_ATTR = "class"; //$NON-NLS-1$
+
+ public IDatabaseSampleActivityParametersWizardCollector getDatabaseSampleActivityParametersWizardCollector()
+ {
+ IExtensionRegistry extReg = Platform.getExtensionRegistry();
+ IExtensionPoint extPoint = extReg.getExtensionPoint(PARAMETER_COLLECTOR_EXTENSION_POINT_ID);
+ IExtension[] extensions = extPoint.getExtensions();
+ IDatabaseSampleActivityParametersWizardCollector collector = null;
+
+ for (IExtension aExtension : extensions)
+ {
+ IConfigurationElement[] configElements = aExtension.getConfigurationElements();
+ for (IConfigurationElement aConfig : configElements)
+ {
+ if (aConfig.getName().equals(PARAMETER_COLLECTOR_ELEM))
+ {
+ try
+ {
+ collector =
+ (IDatabaseSampleActivityParametersWizardCollector) aConfig
+ .createExecutableExtension(IMPL_CLASS_ATTR);
+ break;
+ }
+ catch (CoreException e)
+ {
+ // Do nothing.
+ // If a device framework cannot be instantiated, it will
+ // not be plugged to emulator core plugin.
+ }
+ }
+ }
+ }
+ return collector;
+ }
+
+ public boolean isUseSampleDatabaseTableSelected()
+ {
+ return useSampleDatabaseColumnsSelected;
+ }
+
+ public void setUseSampleDatabaseTableSelected(boolean useSampleDatabaseTableSelected)
+ {
+ this.useSampleDatabaseColumnsSelected = useSampleDatabaseTableSelected;
+ }
+
+ public boolean isListActivitySelected()
+ {
+ return isListActivitySelected;
+ }
+
+ public void setIsListActivitySelected(boolean isListActivitySelected)
+ {
+ this.isListActivitySelected = isListActivitySelected;
+ }
+
+ /**
+ * @return the isDatabaseTemplateSelected
+ */
+ public boolean isDatabaseTemplateSelected()
+ {
+ return isDatabaseTemplateSelected;
+ }
+
+ /**
+ * @param isDatabaseTemplateSelected the isDatabaseTemplateSelected to set
+ */
+ public void setDatabaseTemplateSelected(boolean isDatabaseTemplateSelected)
+ {
+ this.isDatabaseTemplateSelected = isDatabaseTemplateSelected;
+ }
+
+ /**
+ * @return the isDatabaseTableSelected
+ */
+ public boolean isDatabaseTableSelected()
+ {
+ return isDatabaseTableSelected;
+ }
+
+ /**
+ * @param isDatabaseTableSelected the isDatabaseTableSelected to set
+ */
+ public void setDatabaseTableSelected(boolean isDatabaseTableSelected)
+ {
+ this.isDatabaseTableSelected = isDatabaseTableSelected;
+ }
+
+ /**
+ * @param collectorDatabaseName the collectorDatabaseName to set
+ */
+ public void setCollectorDatabaseName(String collectorDatabaseName)
+ {
+ this.collectorDatabaseName = collectorDatabaseName;
+ }
+
+ /**
+ * @param collectorTable the collectorTable to set
+ */
+ public void setCollectorTable(Table collectorTable)
+ {
+ this.collectorTable = collectorTable;
+ }
+
+ /**
+ * @return the collectorTable
+ */
+ public Table getCollectorTable()
+ {
+ return collectorTable;
+ }
+
+ /**
+ * @param collectorColumnList the collectorColumnList to set
+ */
+ public void setCollectorColumnList(ArrayList<Column> collectorColumnList)
+ {
+ this.collectorColumnList = collectorColumnList;
+ }
+
+ /**
+ * @return the collectorColumnList
+ */
+ public ArrayList<Column> getCollectorColumnList()
+ {
+ return collectorColumnList;
+ }
+
+ /**
+ * @return the isMainActivity
+ */
+ public boolean isMainActivity()
+ {
+ return isMainActivity;
+ }
+
+ /**
+ * @param isMainActivity the isMainActivity to set
+ */
+ public void setMainActivity(boolean isMainActivity)
+ {
+ this.isMainActivity = isMainActivity;
+ }
+
+ public String getSqlOpenHelperClassName()
+ {
+ return sqlOpenHelperClassName;
+ }
+
+ public void setSqlOpenHelperClassName(String sqlOpenHelperClassName)
+ {
+ this.sqlOpenHelperClassName = sqlOpenHelperClassName;
+ }
+
+ public String getSqlOpenHelperPackageName()
+ {
+ return sqlOpenHelperPackageName;
+ }
+
+ public void setSqlOpenHelperPackageName(String sqlOpenHelperPackageName)
+ {
+ this.sqlOpenHelperPackageName = sqlOpenHelperPackageName;
+ }
+
+ public boolean isSqlOpenHelperDefined()
+ {
+ return sqlOpenHelperDefined;
+ }
+
+ public void setSqlOpenHelperDefined(boolean sqlOpenHelperDefined)
+ {
+ this.sqlOpenHelperDefined = sqlOpenHelperDefined;
+ }
+
+} \ No newline at end of file
diff --git a/src/plugins/android.codeutils/src/com/motorola/studio/android/model/BuildingBlockModel.java b/src/plugins/android.codeutils/src/com/motorola/studio/android/model/BuildingBlockModel.java
new file mode 100644
index 0000000..fc5c289
--- /dev/null
+++ b/src/plugins/android.codeutils/src/com/motorola/studio/android/model/BuildingBlockModel.java
@@ -0,0 +1,562 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.model;
+
+import java.security.InvalidParameterException;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.jdt.core.IJavaElement;
+import org.eclipse.jdt.core.IJavaProject;
+import org.eclipse.jdt.core.IPackageFragment;
+import org.eclipse.jdt.core.IPackageFragmentRoot;
+import org.eclipse.jdt.core.JavaCore;
+import org.eclipse.jdt.core.JavaModelException;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.TreeSelection;
+
+import com.motorola.studio.android.codeutils.CodeUtilsActivator;
+import com.motorola.studio.android.common.IAndroidConstants;
+import com.motorola.studio.android.common.exception.AndroidException;
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.common.utilities.AndroidUtils;
+import com.motorola.studio.android.common.utilities.EclipseUtils;
+import com.motorola.studio.android.model.java.JavaClass;
+
+/**
+ * Controller for Building Blocks.
+ */
+public abstract class BuildingBlockModel implements IWizardModel
+{
+ private String name = "";
+
+ private final Set<String> intentFilterPermissions = new HashSet<String>();
+
+ /**
+ * Constructor for Building class
+ * @param superClass the Building block superclass
+ */
+ public BuildingBlockModel(String superClass)
+ {
+ if ((superClass == null) || (superClass.length() == 0))
+ {
+ throw new InvalidParameterException();
+ }
+ this.superClass = superClass;
+ }
+
+ private String superClass;
+
+ private IPackageFragmentRoot packageFragmentRoot;
+
+ private IPackageFragment packageFragment;
+
+ private IStatus nameStatus = new Status(IStatus.OK, CodeUtilsActivator.PLUGIN_ID, null);
+
+ private IStatus packageStatus = new Status(IStatus.OK, CodeUtilsActivator.PLUGIN_ID, null);
+
+ private IStatus packageFragmentRootStatus = new Status(IStatus.OK,
+ CodeUtilsActivator.PLUGIN_ID, null);
+
+ private String label;
+
+ private IStatus labelStatus = new Status(IStatus.OK, CodeUtilsActivator.PLUGIN_ID, null);
+
+ private int apiVersion;
+
+ /**
+ * Return building block name
+ * @return
+ */
+ public String getName()
+ {
+ return name;
+ }
+
+ /**
+ * Return superclass
+ * @return
+ */
+ public String getSuperClass()
+ {
+ return superClass;
+ }
+
+ /**
+ * Set building block name
+ * @param typeName
+ */
+ public void setName(String typeName)
+ {
+ this.name = typeName;
+ }
+
+ /**
+ * Set superclass
+ * @param superClass
+ */
+ public void setSuperClass(String superClass)
+ {
+ this.superClass = superClass;
+ }
+
+ /**
+ * Change source folder
+ * @return
+ */
+ public IPackageFragmentRoot getPackageFragmentRoot()
+ {
+ return packageFragmentRoot;
+ }
+
+ /**
+ * Check if using extended Class
+ * @return
+ */
+ public boolean useExtendedClass()
+ {
+ return false;
+ }
+
+ /**
+ * Change source folder
+ * @param pack
+ */
+ public void setPackageFragmentRoot(IPackageFragmentRoot pack)
+ {
+ this.packageFragmentRoot = pack;
+ }
+
+ /**
+ * Change package
+ * @param packageFragment
+ */
+ public void setPackageFragment(IPackageFragment packageFragment)
+ {
+ if ((packageFragmentRoot != null) && (packageFragment != null)
+ && !packageFragmentRoot.getJavaProject().equals(packageFragment.getJavaProject()))
+ {
+ throw new InvalidParameterException();
+ }
+ else
+ {
+ this.packageFragment = packageFragment;
+ }
+ }
+
+ /**
+ * Return package
+ * @return
+ */
+ public IPackageFragment getPackageFragment()
+ {
+ return packageFragment;
+ }
+
+ /**
+ * Configure source folder and package from workbench selection
+ * @param selection
+ */
+ public void configure(IStructuredSelection selection)
+ {
+ try
+ {
+ IPackageFragmentRoot srcFolder = extractPackageFragmentRoot(selection);
+ setPackageFragmentRoot(srcFolder);
+ IJavaProject javaProject = srcFolder == null ? null : srcFolder.getJavaProject();
+ setPackageFragment(extractPackageFragment(selection, javaProject));
+ if (javaProject != null)
+ {
+ apiVersion = AndroidUtils.getApiVersionNumberForProject(javaProject.getProject());
+ }
+
+ }
+ catch (Exception e)
+ {
+ StudioLogger.error(BuildingBlockModel.class,
+ "Error configuring building block from selection.", e);
+ }
+ }
+
+ /**
+ * Extract Package from selection
+ * @param selection
+ * @param javaProject
+ * @return
+ * @throws CoreException
+ */
+ private IPackageFragment extractPackageFragment(IStructuredSelection selection,
+ IJavaProject javaProject) throws CoreException
+ {
+ IPackageFragment pack = null;
+ Object object = selection.getFirstElement();
+ if ((object instanceof IPackageFragment)
+ && ((IPackageFragment) object).getJavaProject().equals(javaProject))
+ {
+ pack = (IPackageFragment) object;
+ }
+ else if ((object instanceof IJavaElement)
+ && ((IJavaElement) object).getJavaProject().equals(javaProject))
+ {
+ pack =
+ (IPackageFragment) ((IJavaElement) object)
+ .getAncestor(IJavaElement.PACKAGE_FRAGMENT);
+ if (pack == null)
+ {
+ pack =
+ EclipseUtils.getDefaultPackageFragment(((IJavaElement) object)
+ .getJavaProject());
+ }
+ }
+ else if (object instanceof IResource)
+ {
+ pack =
+ EclipseUtils.getDefaultPackageFragment(javaProject == null ? JavaCore
+ .create(((IResource) object).getProject()) : javaProject);
+ }
+ else
+ {
+ if (javaProject != null)
+ {
+ pack = EclipseUtils.getDefaultPackageFragment(javaProject);
+ }
+ else
+ {
+ IJavaProject[] prjs =
+ JavaCore.create(ResourcesPlugin.getWorkspace().getRoot()).getJavaProjects();
+ if (prjs.length > 0)
+ {
+ pack = EclipseUtils.getDefaultPackageFragment(prjs[0]);
+ }
+ }
+
+ }
+
+ if (pack != null)
+ {
+ if (!pack.getJavaProject().getProject().hasNature(IAndroidConstants.ANDROID_NATURE))
+ {
+ pack = extractPackageFragment(new TreeSelection(), javaProject);
+ }
+ }
+ return pack;
+ }
+
+ /**
+ * Extract source folder from selection.
+ * @param selection
+ * @return
+ * @throws CoreException
+ */
+ private static IPackageFragmentRoot extractPackageFragmentRoot(IStructuredSelection selection)
+ throws CoreException
+ {
+ IPackageFragmentRoot pack = null;
+ Object selectionElement = selection.getFirstElement();
+
+ if (selectionElement instanceof IPackageFragmentRoot)
+ {
+ pack = (IPackageFragmentRoot) selectionElement;
+ }
+ else if (selectionElement instanceof IJavaElement)
+ {
+ pack =
+ (IPackageFragmentRoot) ((IJavaElement) selectionElement)
+ .getAncestor(IJavaElement.PACKAGE_FRAGMENT_ROOT);
+ if (pack == null)
+ {
+ IJavaProject element = ((IJavaElement) selectionElement).getJavaProject();
+
+ for (IPackageFragmentRoot root : element.getPackageFragmentRoots())
+ {
+ if (root.getResource() != null)
+ {
+ boolean isSrc =
+ (root.getElementType() & IPackageFragmentRoot.K_SOURCE) == IPackageFragmentRoot.K_SOURCE;
+ boolean isGen =
+ root.getElementName().equals(IAndroidConstants.GEN_SRC_FOLDER)
+ && (root.getParent() instanceof IJavaProject);
+ if (isSrc && !isGen)
+ {
+ pack = root;
+ break;
+ }
+ }
+ }
+ }
+ }
+ else if (selectionElement instanceof IResource)
+ {
+ IJavaProject element = JavaCore.create(((IResource) selectionElement).getProject());
+
+ if (element.isOpen())
+ {
+ for (IPackageFragmentRoot root : element.getPackageFragmentRoots())
+ {
+ if (root.getResource() != null)
+ {
+ boolean isSrc =
+ (root.getElementType() & IPackageFragmentRoot.K_SOURCE) == IPackageFragmentRoot.K_SOURCE;
+ boolean isGen =
+ root.getElementName().equals(IAndroidConstants.GEN_SRC_FOLDER)
+ && (root.getParent() instanceof IJavaProject);
+ if (isSrc && !isGen)
+ {
+ pack = root;
+ break;
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ IJavaProject[] allProjects =
+ JavaCore.create(ResourcesPlugin.getWorkspace().getRoot()).getJavaProjects();
+ if ((allProjects != null) && (allProjects.length > 0))
+ {
+ for (IJavaProject project : allProjects)
+ {
+ if (project.getResource().getProject()
+ .hasNature(IAndroidConstants.ANDROID_NATURE))
+ {
+ IPackageFragmentRoot[] roots = project.getPackageFragmentRoots();
+
+ if ((roots != null) && (roots.length > 0))
+ {
+ boolean found = false;
+
+ for (IPackageFragmentRoot root : roots)
+ {
+ boolean isSrc =
+ (root.getElementType() & IPackageFragmentRoot.K_SOURCE) == IPackageFragmentRoot.K_SOURCE;
+ boolean isGen =
+ root.getElementName().equals(
+ IAndroidConstants.GEN_SRC_FOLDER)
+ && (root.getParent() instanceof IJavaProject);
+ if (isSrc && !isGen)
+ {
+ found = true;
+ pack = root;
+ break;
+ }
+ }
+
+ if (found)
+ {
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (pack != null)
+ {
+ if (!pack.getJavaProject().getProject().hasNature(IAndroidConstants.ANDROID_NATURE))
+ {
+ pack = extractPackageFragmentRoot(new TreeSelection());
+ }
+ }
+ return pack;
+ }
+
+ /**
+ * Return selected project.
+ * @return
+ */
+ public IProject getProject()
+ {
+ IProject project = null;
+ if (packageFragmentRoot != null)
+ {
+ project = packageFragmentRoot.getJavaProject().getProject();
+ }
+ else if (packageFragment != null)
+ {
+ project = packageFragment.getJavaProject().getProject();
+ }
+ return project;
+ }
+
+ /**
+ * Change status from name. Use JDT validation.
+ * @param nameStatus
+ */
+ public void setNameStatus(IStatus nameStatus)
+ {
+ this.nameStatus = nameStatus;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.model.IWizardModel#getStatus()
+ */
+ public IStatus getStatus()
+ {
+ return getMostSevere(new IStatus[]
+ {
+ packageFragmentRootStatus, nameStatus, packageStatus, labelStatus
+ });
+ }
+
+ /**
+ * Find most severe status.
+ * @param status
+ * @return
+ */
+ protected static IStatus getMostSevere(IStatus[] status)
+ {
+ IStatus max = null;
+ for (int i = 0; i < status.length; i++)
+ {
+ IStatus curr = status[i];
+ if (curr.matches(IStatus.ERROR))
+ {
+ return curr;
+ }
+ if ((max == null) || (curr.getSeverity() > max.getSeverity()))
+ {
+ max = curr;
+ }
+ }
+
+ return max;
+ }
+
+ /**
+ * Change package status. Use JDT Validation
+ * @param packageStatus
+ */
+ public void setPackageStatus(IStatus packageStatus)
+ {
+ this.packageStatus = packageStatus;
+ }
+
+ /**
+ * Change source folder status. Use JDT Validation
+ * @param packageFragmentRootStatus
+ */
+ public void setPackageFragmentRootStatus(IStatus packageFragmentRootStatus)
+ {
+ this.packageFragmentRootStatus = packageFragmentRootStatus;
+ }
+
+ /**
+ * Change label.
+ * @param label
+ */
+ public void setLabel(String label)
+ {
+ this.label = label;
+ }
+
+ /**
+ * Return Label.
+ * @return
+ */
+ public String getLabel()
+ {
+ return label;
+ }
+
+ /**
+ * Change Label Status. Don't uses JDT validation.
+ * @param labelStatus
+ */
+ public void setLabelStatus(IStatus labelStatus)
+ {
+ this.labelStatus = labelStatus;
+ }
+
+ /**
+ * Creates the Java Class file
+ *
+ * @param javaClass The Java Class model
+ * @param monitor The progress monitor
+ * @throws JavaModelException
+ * @throws AndroidException
+ */
+ protected void createJavaClassFile(JavaClass javaClass, IProgressMonitor monitor)
+ throws JavaModelException, AndroidException
+ {
+ final String JAVA_EXTENSION = ".java";
+
+ IPackageFragment targetPackage =
+ getPackageFragmentRoot().getPackageFragment(getPackageFragment().getElementName());
+
+ if (!targetPackage.exists())
+ {
+ getPackageFragmentRoot().createPackageFragment(targetPackage.getElementName(), true,
+ monitor);
+ }
+
+ targetPackage.createCompilationUnit(getName() + JAVA_EXTENSION, //$NON-NLS-1$
+ javaClass.getClassContent().get(), true, monitor);
+ }
+
+ /**
+ * Return all Filter Permissions as an Array.
+ * @return
+ */
+ public String[] getIntentFilterPermissionsAsArray()
+ {
+ return intentFilterPermissions.toArray(new String[intentFilterPermissions.size()]);
+ }
+
+ /**
+ * Returns all intent filter permissions.
+ * @return
+ */
+ public Set<String> getIntentFilterPermissions()
+ {
+ return intentFilterPermissions;
+ }
+
+ /**
+ * Adds an intent filter permission to this launcher.
+ * @param category
+ */
+ public void addIntentFilterPermissions(String permission)
+ {
+ intentFilterPermissions.add(permission);
+ }
+
+ /**
+ * Remove intent filter permission.
+ * @param category
+ */
+ public void removeIntentFilterPermissions(String permission)
+ {
+ intentFilterPermissions.remove(permission);
+ }
+
+ /**
+ * @return the apiVersion
+ */
+ public int getApiVersion()
+ {
+ return apiVersion;
+ }
+}
diff --git a/src/plugins/android.codeutils/src/com/motorola/studio/android/model/ContentProvider.java b/src/plugins/android.codeutils/src/com/motorola/studio/android/model/ContentProvider.java
new file mode 100644
index 0000000..06a0bb0
--- /dev/null
+++ b/src/plugins/android.codeutils/src/com/motorola/studio/android/model/ContentProvider.java
@@ -0,0 +1,467 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.model;
+
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.jdt.core.IPackageFragment;
+import org.eclipse.jdt.core.JavaModelException;
+import org.eclipse.jface.wizard.IWizardContainer;
+import org.eclipse.osgi.util.NLS;
+
+import com.motorola.studio.android.codeutils.CodeUtilsActivator;
+import com.motorola.studio.android.codeutils.i18n.CodeUtilsNLS;
+import com.motorola.studio.android.common.IAndroidConstants;
+import com.motorola.studio.android.common.exception.AndroidException;
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.common.log.UsageDataConstants;
+import com.motorola.studio.android.manifest.AndroidProjectManifestFile;
+import com.motorola.studio.android.model.java.ContentProviderClass;
+import com.motorola.studio.android.model.manifest.AndroidManifestFile;
+import com.motorola.studio.android.model.manifest.dom.ApplicationNode;
+import com.motorola.studio.android.model.manifest.dom.IntentFilterNode;
+import com.motorola.studio.android.model.manifest.dom.ManifestNode;
+import com.motorola.studio.android.model.manifest.dom.ProviderNode;
+import com.motorola.studio.android.model.manifest.dom.UsesPermissionNode;
+import com.motorola.studio.android.model.resources.ResourceFile;
+import com.motorola.studio.android.model.resources.types.AbstractResourceNode.NodeType;
+import com.motorola.studio.android.model.resources.types.ResourcesNode;
+import com.motorola.studio.android.model.resources.types.StringNode;
+import com.motorola.studio.android.resources.AndroidProjectResources;
+
+/**
+ * ContentProvider Model
+ */
+public class ContentProvider extends BuildingBlockModel
+{
+ private static final String PROVIDER_RESOURCE_LABEL_SUFFIX = "ContentProviderLabel";
+
+ private final List<String> authoritiesList = new LinkedList<String>();
+
+ private static final int MANIFEST_UPDATING_STEPS = 6;
+
+ private static final int RESOURCES_UPDATING_STEPS = 3;
+
+ /**
+ * Constructor for Content Provider.
+ */
+ public ContentProvider()
+ {
+ super(IAndroidConstants.CLASS_CONTENTPROVIDER);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.model.BuildingBlockModel#getStatus()
+ */
+ @Override
+ public IStatus getStatus()
+ {
+ return getMostSevere(new IStatus[]
+ {
+ super.getStatus(), getAuthorityStatus()
+ });
+ }
+
+ /**
+ * Return Authority Status
+ * @see #getStatus()
+ * @return
+ */
+ private IStatus getAuthorityStatus()
+ {
+ IStatus status = new Status(IStatus.OK, CodeUtilsActivator.PLUGIN_ID, null);
+ if (authoritiesList.isEmpty())
+ {
+ status =
+ new Status(IStatus.ERROR, CodeUtilsActivator.PLUGIN_ID,
+ CodeUtilsNLS.ERR_ContentProvider_InvalidAuthoritySelection);
+ }
+ return status;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.model.IWizardModel#needMoreInformation()
+ */
+ public boolean needMoreInformation()
+ {
+ return false;
+ }
+
+ /**
+ * Create the content provider class and add it to the manifest file.
+ * @return True if the content provider class was successfully create and added to the manifest file. Otherwise, return false.
+ * */
+ public boolean save(IWizardContainer container, IProgressMonitor monitor)
+ throws AndroidException
+ {
+ boolean classCreated = createProviderClass(monitor);
+ boolean addedOnManifest = false;
+
+ if (classCreated)
+ {
+ addedOnManifest = createProviderOnManifest(monitor);
+ }
+
+ // Logs all permissions used in UDC log
+ saveUsedPermissions(container, monitor);
+ return classCreated && addedOnManifest;
+
+ }
+
+ /*
+ * Logs to UDC all permissions used
+ */
+ private boolean saveUsedPermissions(IWizardContainer container, IProgressMonitor monitor)
+ throws AndroidException
+ {
+
+ StringBuffer permissionList = new StringBuffer("Added building block permissions: ");
+ int selectedPermissionsSize = getIntentFilterPermissionsAsArray().length;
+
+ for (int i = 0; i < selectedPermissionsSize; i++)
+ {
+
+ String permission = getIntentFilterPermissionsAsArray()[i];
+ permissionList.append(permission);
+
+ if (i < (selectedPermissionsSize - 1))
+ {
+ permissionList.append(", ");
+ }
+ }
+
+ if (selectedPermissionsSize > 0)
+ {
+
+ // Logs to UDC the permissions selected
+ StudioLogger.collectUsageData(
+ UsageDataConstants.WHAT_BUILDINGBLOCK_PERMISSION, //$NON-NLS-1$
+ UsageDataConstants.KIND_BUILDINGBLOCK_PERMISSION,
+ "permissions: " + permissionList.toString(), //$NON-NLS-1$
+ CodeUtilsActivator.PLUGIN_ID, CodeUtilsActivator.getDefault().getBundle()
+ .getVersion().toString());
+ }
+ return true;
+ }
+
+ /**
+ * Return default authority.
+ * @return
+ */
+ public String getDefaultAuthority()
+ {
+ IPackageFragment packageFragment = getPackageFragment();
+ String name = getName();
+ String defaultAuthority = "";
+ if ((name != null) && (packageFragment != null))
+ {
+ defaultAuthority = packageFragment.getElementName() + "." + name.toLowerCase();
+ }
+
+ return defaultAuthority;
+ }
+
+ /**
+ * Remove Authority
+ * @param authority
+ */
+ public void removeAuthority(String authority)
+ {
+ authoritiesList.remove(authority);
+ }
+
+ /**
+ * Add Authority
+ * @param authority
+ */
+ public void addAuthority(String authority)
+ {
+ authoritiesList.add(authority);
+ }
+
+ /**
+ * Return Authorities
+ * @return
+ */
+ public List<String> getAuthoritiesList()
+ {
+ return authoritiesList;
+ }
+
+ /*
+ * Creates the Content Provider java class
+ *
+ * @param monitor the progress monitor
+ *
+ * @return true if the class has been created or false otherwise
+ * @throws AndroidException
+ */
+ private boolean createProviderClass(IProgressMonitor monitor) throws AndroidException
+ {
+ boolean created = false;
+ String firstAuthority = "";
+
+ monitor.subTask(CodeUtilsNLS.UI_ContentProvider_CreatingTheContentProviderJavaClass);
+
+ for (String auth : getAuthoritiesList())
+ {
+ firstAuthority = auth;
+ break;
+ }
+
+ ContentProviderClass providerClass =
+ new ContentProviderClass(getName(), getPackageFragment().getElementName(),
+ firstAuthority);
+
+ try
+ {
+ createJavaClassFile(providerClass, monitor);
+ created = true;
+ }
+ catch (JavaModelException e)
+ {
+ String errMsg =
+ NLS.bind(CodeUtilsNLS.EXC_ContentProvider_CannotCreateTheContentProviderClass,
+ getName(), e.getLocalizedMessage());
+
+ throw new AndroidException(errMsg);
+ }
+ catch (AndroidException e)
+ {
+ String errMsg =
+ NLS.bind(CodeUtilsNLS.EXC_ContentProvider_CannotCreateTheContentProviderClass,
+ getName(), e.getLocalizedMessage());
+ throw new AndroidException(errMsg);
+ }
+
+ return created;
+ }
+
+ /*
+ * Creates the Content Provider class entry on AndroidManifest.xml file
+ *
+ * @param monitor the progress monitor
+ *
+ * @return true if the entry has been added or false otherwise
+ * @throws AndroidException
+ */
+ private boolean createProviderOnManifest(IProgressMonitor monitor) throws AndroidException
+ {
+ boolean created = false;
+
+ try
+ {
+ int totalWork = MANIFEST_UPDATING_STEPS + RESOURCES_UPDATING_STEPS;
+
+ monitor.beginTask("", totalWork);
+
+ monitor.subTask(CodeUtilsNLS.UI_Common_UpdatingTheAndroidManifestXMLFile);
+
+ AndroidManifestFile androidManifestFile =
+ AndroidProjectManifestFile.getFromProject(getProject());
+
+ monitor.worked(1);
+
+ ManifestNode manifestNode =
+ androidManifestFile != null ? androidManifestFile.getManifestNode() : null;
+ ApplicationNode applicationNode =
+ manifestNode != null ? manifestNode.getApplicationNode() : null;
+
+ monitor.worked(1);
+
+ if (applicationNode != null)
+ {
+
+ // Adds the added permission nodes to manifest file
+ List<String> permissionsNames = new ArrayList<String>();
+ for (UsesPermissionNode i : manifestNode.getUsesPermissionNodes())
+ {
+ permissionsNames.add(i.getName());
+ }
+
+ for (String intentFilterPermission : getIntentFilterPermissionsAsArray())
+ {
+ if (!permissionsNames.contains(intentFilterPermission))
+ {
+ manifestNode.addChild(new UsesPermissionNode(intentFilterPermission));
+ }
+ }
+
+ boolean providerExists = false;
+
+ String classQualifier =
+ (getPackageFragment().getElementName()
+ .equals(manifestNode.getPackageName()) ? "" : getPackageFragment()
+ .getElementName())
+ + ".";
+
+ for (ProviderNode providerNode : applicationNode.getProviderNodes())
+ {
+ if (providerNode.getName().equals(getName()))
+ {
+ providerExists = true;
+ break;
+ }
+ }
+
+ monitor.worked(1);
+
+ String[] authorities = new String[getAuthoritiesList().size()];
+ authorities = getAuthoritiesList().toArray(authorities);
+
+ if (!providerExists)
+ {
+ ProviderNode providerNode =
+ new ProviderNode(classQualifier + getName(), authorities[0]);
+
+ String providerLabel = createProviderLabel(monitor);
+
+ if (providerLabel != null)
+ {
+ providerNode.setLabel(AndroidProjectResources.STRING_CALL_PREFIX
+ + providerLabel);
+ }
+
+ IntentFilterNode intentFilterNode = new IntentFilterNode();
+
+ for (int i = 1; i < authorities.length; i++)
+ {
+ providerNode.addAuthority(authorities[i]);
+ }
+
+ if (intentFilterNode.getChildren().length > 0)
+ {
+ providerNode.addIntentFilterNode(intentFilterNode);
+ }
+
+ applicationNode.addProviderNode(providerNode);
+
+ monitor.worked(1);
+
+ monitor.subTask(CodeUtilsNLS.UI_Common_SavingTheAndroidManifestXMLFile);
+
+ AndroidProjectManifestFile.saveToProject(getProject(), androidManifestFile,
+ true);
+ created = true;
+
+ monitor.worked(1);
+ }
+ }
+ }
+ catch (AndroidException e)
+ {
+ String errMsg =
+ NLS.bind(CodeUtilsNLS.EXC_ContentProvider_CannotUpdateTheManifestFile,
+ getName(), e.getLocalizedMessage());
+ throw new AndroidException(errMsg);
+ }
+ catch (CoreException e)
+ {
+ String errMsg =
+ NLS.bind(CodeUtilsNLS.EXC_ContentProvider_CannotUpdateTheManifestFile,
+ getName(), e.getLocalizedMessage());
+ throw new AndroidException(errMsg);
+ }
+ finally
+ {
+ monitor.done();
+ }
+
+ return created;
+ }
+
+ /*
+ * Adds the Content Provider label value on the strings resource file
+ *
+ * @param monitor the progress monitor
+ *
+ * @return The label value if it has been added to the strings resource file or null otherwise
+ * @throws AndroidException
+ */
+ private String createProviderLabel(IProgressMonitor monitor) throws AndroidException
+ {
+ String resLabel = null;
+
+ if ((getLabel() != null) && (getLabel().trim().length() > 0))
+ {
+ try
+ {
+ monitor.subTask(CodeUtilsNLS.UI_Common_UpdatingTheStringsResourceFile);
+
+ ResourceFile stringsFile =
+ AndroidProjectResources.getResourceFile(getProject(), NodeType.String);
+
+ monitor.worked(1);
+
+ if (stringsFile.getResourcesNode() == null)
+ {
+ stringsFile.addResourceEntry(new ResourcesNode());
+ }
+
+ resLabel =
+ stringsFile.getNewResourceName(getName() + PROVIDER_RESOURCE_LABEL_SUFFIX);
+
+ StringNode strNode = new StringNode(resLabel);
+ strNode.setNodeValue(getLabel());
+
+ stringsFile.getResourcesNode().addChildNode(strNode);
+
+ monitor.worked(1);
+
+ AndroidProjectResources
+ .saveResourceFile(getProject(), stringsFile, NodeType.String);
+
+ monitor.worked(1);
+ }
+ catch (CoreException e)
+ {
+ String errMsg =
+ NLS.bind(
+ CodeUtilsNLS.EXC_ContentProvider_CannotCreateTheContentProviderLabel,
+ e.getLocalizedMessage());
+ throw new AndroidException(errMsg);
+ }
+ catch (AndroidException e)
+ {
+ String errMsg =
+ NLS.bind(
+ CodeUtilsNLS.EXC_ContentProvider_CannotCreateTheContentProviderLabel,
+ e.getLocalizedMessage());
+ throw new AndroidException(errMsg);
+ }
+ }
+
+ return resLabel;
+ }
+
+ /**
+ * Check if contains authorities.
+ * @param authority
+ * @return
+ */
+ public boolean containsAuthority(String authority)
+ {
+ return authoritiesList.contains(authority);
+ }
+}
diff --git a/src/plugins/android.codeutils/src/com/motorola/studio/android/model/IDatabaseSampleActivityParametersWizardCollector.java b/src/plugins/android.codeutils/src/com/motorola/studio/android/model/IDatabaseSampleActivityParametersWizardCollector.java
new file mode 100644
index 0000000..1a7790f
--- /dev/null
+++ b/src/plugins/android.codeutils/src/com/motorola/studio/android/model/IDatabaseSampleActivityParametersWizardCollector.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.motorola.studio.android.model;
+
+import java.util.List;
+
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.datatools.modelbase.sql.tables.Column;
+import org.eclipse.datatools.modelbase.sql.tables.Table;
+import org.eclipse.jface.wizard.IWizardPage;
+
+import com.motorola.studio.android.common.exception.AndroidException;
+
+/**
+ * Helper class to create Activity based on Sqlite tables.
+ */
+public interface IDatabaseSampleActivityParametersWizardCollector
+{
+
+ public void setDatabaseName(String databaseName);
+
+ public void setTable(Table table);
+
+ public void setSelectedColumns(List<Column> selectedColumns);
+
+ public String getDatabaseName();
+
+ public String getTableName();
+
+ public Table getTable();
+
+ public String getColumnsNames();
+
+ public String getConstColumnsNames();
+
+ public String getCursorValues() throws AndroidException;
+
+ public String getAddColumnsToRow();
+
+ /**
+ * Get import to the package and class name for Sql Open Helper
+ * @return import statement
+ */
+ public String getImports();
+
+ public void setSqlOpenHelperClassName(String sqlOpenHelperClassName);
+
+ public void setSqlOpenHelperPackageName(String sqlOpenHelperPackageName);
+
+ public String getSqlOpenHelperClassName();
+
+ public boolean createOpenHelper();
+
+ public void setCreateOpenHelper(boolean createOpenHelper);
+
+ public String getReadableDatabase();
+
+ /**
+ * Add pages that contributes to fill parameters to create activity sample
+ * @return
+ */
+ public List<IWizardPage> getWizardPages();
+
+ /**
+ * Creates Sql Open Helper required to transfer db file and make the activity work correctly
+ * @param project
+ * @param monitor
+ */
+ public void createSqlOpenHelper(IProject project, IProgressMonitor monitor);
+}
diff --git a/src/plugins/android.codeutils/src/com/motorola/studio/android/model/IWizardModel.java b/src/plugins/android.codeutils/src/com/motorola/studio/android/model/IWizardModel.java
new file mode 100644
index 0000000..7584077
--- /dev/null
+++ b/src/plugins/android.codeutils/src/com/motorola/studio/android/model/IWizardModel.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.model;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.jface.wizard.IWizard;
+import org.eclipse.jface.wizard.IWizardContainer;
+
+import com.motorola.studio.android.common.exception.AndroidException;
+
+/**
+ * Controller interface. It should communicate with the Wizard UI
+ * to create on workspace the wizard output.
+ */
+public interface IWizardModel
+{
+ public static final int MODIFIED = 1546;
+
+ /**
+ * Return the Model Status.
+ * This Status must contains the
+ * {@link IStatus#getSeverity()}
+ * according with the needed
+ * values, and must contain the
+ * message for not OK Status.
+ *
+ * @return The Model Status
+ */
+ public IStatus getStatus();
+
+ /**
+ * Save Contents in Workspace;
+ * @return True if it succeeds, false otherwise.
+ * @throws AndroidException
+ */
+ boolean save(IWizardContainer container, IProgressMonitor monitor) throws AndroidException;
+
+ /**
+ * Check if need more information to finish.
+ * @see IWizard#canFinish()
+ * @return True if more information is needed to finish the page.
+ */
+ boolean needMoreInformation();
+}
diff --git a/src/plugins/android.codeutils/src/com/motorola/studio/android/model/Launcher.java b/src/plugins/android.codeutils/src/com/motorola/studio/android/model/Launcher.java
new file mode 100644
index 0000000..98d5bff
--- /dev/null
+++ b/src/plugins/android.codeutils/src/com/motorola/studio/android/model/Launcher.java
@@ -0,0 +1,246 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.model;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.jface.wizard.IWizardContainer;
+
+import com.motorola.studio.android.codeutils.CodeUtilsActivator;
+import com.motorola.studio.android.common.exception.AndroidException;
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.common.log.UsageDataConstants;
+
+/**
+ * Launcher Controller Model. As part of a MVC architecture, this class should
+ * communicate with the Wizard UI to provide all needed information to create a
+ * functional Launcher Building Block (Activity, Service or Broadcast Receiver).
+ */
+public abstract class Launcher extends BuildingBlockModel
+{
+ private final Set<String> intentFilterCategories = new HashSet<String>();
+
+ private final Set<String> intentFilterActions = new HashSet<String>();
+
+ private final Set<String> intentFilterPermissions = new HashSet<String>();
+
+ /**
+ * Constructor for the Launcher.
+ *
+ * @param superClass
+ * The super class for this Launcher
+ */
+ public Launcher(String superClass)
+ {
+ super(superClass);
+ }
+
+ /**
+ * Creates a Launcher with a Default Intent Filter
+ *
+ * @param superClass
+ * the launcher superclass
+ * @param category
+ * the intent filter category
+ * @param action
+ * the intent filter action
+ */
+ public Launcher(String superClass, String category, String action)
+ {
+ super(superClass);
+ intentFilterCategories.add(category);
+ intentFilterActions.add(action);
+ }
+
+ /**
+ * Adds an Intent Filter Action to this Launcher.
+ *
+ * @param action
+ */
+ public void addIntentFilterAction(String action)
+ {
+ this.intentFilterActions.add(action);
+ }
+
+ /**
+ * Adds an intent filter category to this launcher.
+ *
+ * @param category
+ */
+ public void addIntentFilterCategories(String category)
+ {
+ intentFilterCategories.add(category);
+ }
+
+ /**
+ * Adds an intent filter category to this launcher.
+ *
+ * @param category
+ */
+ @Override
+ public void addIntentFilterPermissions(String permission)
+ {
+ intentFilterPermissions.add(permission);
+ }
+
+ /**
+ * Return all intent filter actions as Array.
+ *
+ * @return
+ */
+ public String[] getIntentFilterActionsAsArray()
+ {
+ return intentFilterActions.toArray(new String[intentFilterActions.size()]);
+ }
+
+ /**
+ * Returns all intent filter actions.
+ *
+ * @return
+ */
+ public Set<String> getIntentFilterActions()
+ {
+ return intentFilterActions;
+ }
+
+ /**
+ * Returns all intent filter categories.
+ *
+ * @return
+ */
+ public Set<String> getIntentFilterCategories()
+ {
+ return intentFilterCategories;
+ }
+
+ /**
+ * Returns all intent filter categories.
+ *
+ * @return
+ */
+ @Override
+ public Set<String> getIntentFilterPermissions()
+ {
+ return intentFilterPermissions;
+ }
+
+ /**
+ * Return all Filter Categories as an Array.
+ *
+ * @return
+ */
+ public String[] getIntentFilterCategoriesAsArray()
+ {
+ return intentFilterCategories.toArray(new String[intentFilterCategories.size()]);
+ }
+
+ /**
+ * Return all Filter Permissions as an Array.
+ *
+ * @return
+ */
+ @Override
+ public String[] getIntentFilterPermissionsAsArray()
+ {
+ return intentFilterPermissions.toArray(new String[intentFilterPermissions.size()]);
+ }
+
+ /**
+ * Remove the Intent filter action.
+ *
+ * @param action
+ */
+ public void removeIntentFilterAction(String action)
+ {
+ intentFilterActions.remove(action);
+ }
+
+ /**
+ * Remove intent filter category.
+ *
+ * @param category
+ */
+ public void removeIntentFilterCategories(String category)
+ {
+ intentFilterCategories.remove(category);
+ }
+
+ /**
+ * Remove intent filter category.
+ *
+ * @param category
+ */
+ @Override
+ public void removeIntentFilterPermissions(String permission)
+ {
+ intentFilterPermissions.remove(permission);
+ }
+
+ /**
+ * Remove all Intent Filters action.
+ */
+ public void removeAllIntentFilterActions()
+ {
+ intentFilterActions.clear();
+ }
+
+ /**
+ * Remove all intent filters categories.
+ */
+ public void removeAllIntentFilterCategories()
+ {
+ intentFilterCategories.clear();
+ }
+
+ /**
+ * Logs to UDC all permissions used
+ */
+ public boolean save(IWizardContainer container, IProgressMonitor monitor)
+ throws AndroidException
+ {
+
+ StringBuffer permissionList = new StringBuffer("Added building block permissions: ");
+ int selectedPermissionsSize = getIntentFilterPermissionsAsArray().length;
+
+ for (int i = 0; i < selectedPermissionsSize; i++)
+ {
+
+ String permission = getIntentFilterPermissionsAsArray()[i];
+ permissionList.append(permission);
+
+ if (i < (selectedPermissionsSize - 1))
+ {
+ permissionList.append(", ");
+ }
+ }
+
+ if (selectedPermissionsSize > 0)
+ {
+
+ // Logs to UDC the permissions selected
+ StudioLogger.collectUsageData(
+ UsageDataConstants.WHAT_BUILDINGBLOCK_PERMISSION, //$NON-NLS-1$
+ UsageDataConstants.KIND_BUILDINGBLOCK_PERMISSION,
+ "permissions: " + permissionList.toString(), //$NON-NLS-1$
+ CodeUtilsActivator.PLUGIN_ID, CodeUtilsActivator.getDefault().getBundle()
+ .getVersion().toString());
+ }
+ return true;
+ }
+
+}
diff --git a/src/plugins/android.codeutils/src/com/motorola/studio/android/model/Receiver.java b/src/plugins/android.codeutils/src/com/motorola/studio/android/model/Receiver.java
new file mode 100644
index 0000000..aa55762
--- /dev/null
+++ b/src/plugins/android.codeutils/src/com/motorola/studio/android/model/Receiver.java
@@ -0,0 +1,344 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.model;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.jdt.core.JavaModelException;
+import org.eclipse.jface.wizard.IWizardContainer;
+import org.eclipse.osgi.util.NLS;
+
+import com.motorola.studio.android.codeutils.i18n.CodeUtilsNLS;
+import com.motorola.studio.android.common.IAndroidConstants;
+import com.motorola.studio.android.common.exception.AndroidException;
+import com.motorola.studio.android.manifest.AndroidProjectManifestFile;
+import com.motorola.studio.android.model.java.BroadcastReceiverClass;
+import com.motorola.studio.android.model.manifest.AndroidManifestFile;
+import com.motorola.studio.android.model.manifest.dom.ActionNode;
+import com.motorola.studio.android.model.manifest.dom.ApplicationNode;
+import com.motorola.studio.android.model.manifest.dom.CategoryNode;
+import com.motorola.studio.android.model.manifest.dom.IntentFilterNode;
+import com.motorola.studio.android.model.manifest.dom.ManifestNode;
+import com.motorola.studio.android.model.manifest.dom.ReceiverNode;
+import com.motorola.studio.android.model.manifest.dom.UsesPermissionNode;
+import com.motorola.studio.android.model.resources.ResourceFile;
+import com.motorola.studio.android.model.resources.types.AbstractResourceNode.NodeType;
+import com.motorola.studio.android.model.resources.types.ResourcesNode;
+import com.motorola.studio.android.model.resources.types.StringNode;
+import com.motorola.studio.android.resources.AndroidProjectResources;
+
+/**
+ * Receiver Class.
+ */
+public class Receiver extends Launcher
+{
+ private static final String RECEIVER_RESOURCE_LABEL_SUFFIX = "BroadcastReceiverLabel";
+
+ private static final int MANIFEST_UPDATING_STEPS = 6;
+
+ private static final int RESOURCES_UPDATING_STEPS = 3;
+
+ /**
+ * Default constructor.
+ */
+ public Receiver()
+ {
+ super(IAndroidConstants.CLASS_BROADCASTRECEIVER);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.model.BuildingBlockModel#getStatus()
+ */
+ @Override
+ public IStatus getStatus()
+ {
+ return super.getStatus();
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.model.IWizardModel#needMoreInformation()
+ */
+ public boolean needMoreInformation()
+ {
+ return false;
+ }
+
+ /**
+ * Create the broadcast receiver class and add it to the manifest file.
+ * @return True if the broadcast receiver class was successfully created and added to the manifest file.
+ * */
+ @Override
+ public boolean save(IWizardContainer container, IProgressMonitor monitor)
+ throws AndroidException
+ {
+ boolean classCreated = createReceiverClass(monitor);
+ boolean addedOnManifest = false;
+
+ if (classCreated)
+ {
+ addedOnManifest = createReceiverOnManifest(monitor);
+ }
+
+ // Logs all permissions used in UDC log
+ super.save(container, monitor);
+
+ return classCreated && addedOnManifest;
+ }
+
+ /*
+ * Creates the Broadcast Receiver java class
+ *
+ * @param monitor the progress monitor
+ *
+ * @return true if the class has been created or false otherwise
+ * @throws AndroidException
+ */
+ private boolean createReceiverClass(IProgressMonitor monitor) throws AndroidException
+ {
+ boolean created = false;
+
+ monitor.subTask(CodeUtilsNLS.UI_Receiver_CreatingTheReceiverJavaClass);
+
+ BroadcastReceiverClass receiverClass =
+ new BroadcastReceiverClass(getName(), getPackageFragment().getElementName());
+
+ try
+ {
+ createJavaClassFile(receiverClass, monitor);
+ created = true;
+ }
+ catch (JavaModelException e)
+ {
+ String errMsg =
+ NLS.bind(CodeUtilsNLS.EXC_Receiver_CannotCreateTheReceiverClass, getName(),
+ e.getLocalizedMessage());
+
+ throw new AndroidException(errMsg);
+ }
+ catch (AndroidException e)
+ {
+ String errMsg =
+ NLS.bind(CodeUtilsNLS.EXC_Receiver_CannotCreateTheReceiverClass, getName(),
+ e.getLocalizedMessage());
+ throw new AndroidException(errMsg);
+ }
+
+ return created;
+ }
+
+ /*
+ * Creates the Broadcast Receiver class entry on AndroidManifest.xml file
+ *
+ * @param monitor the progress monitor
+ *
+ * @return true if the entry has been added or false otherwise
+ * @throws AndroidException
+ */
+ private boolean createReceiverOnManifest(IProgressMonitor monitor) throws AndroidException
+ {
+ boolean created = false;
+
+ try
+ {
+ int totalWork = MANIFEST_UPDATING_STEPS + RESOURCES_UPDATING_STEPS;
+
+ monitor.beginTask("", totalWork);
+
+ monitor.subTask(CodeUtilsNLS.UI_Common_UpdatingTheAndroidManifestXMLFile);
+
+ AndroidManifestFile androidManifestFile =
+ AndroidProjectManifestFile.getFromProject(getProject());
+
+ monitor.worked(1);
+
+ ManifestNode manifestNode =
+ androidManifestFile != null ? androidManifestFile.getManifestNode() : null;
+ ApplicationNode applicationNode =
+ manifestNode != null ? manifestNode.getApplicationNode() : null;
+
+ monitor.worked(1);
+
+ if (applicationNode != null)
+ {
+
+ // Adds the added permission nodes to manifest file
+ List<String> permissionsNames = new ArrayList<String>();
+ for (UsesPermissionNode i : manifestNode.getUsesPermissionNodes())
+ {
+ permissionsNames.add(i.getName());
+ }
+
+ for (String intentFilterPermission : getIntentFilterPermissionsAsArray())
+ {
+ if (!permissionsNames.contains(intentFilterPermission))
+ {
+ manifestNode.addChild(new UsesPermissionNode(intentFilterPermission));
+ }
+ }
+
+ boolean receiverExists = false;
+
+ String classQualifier =
+ (getPackageFragment().getElementName()
+ .equals(manifestNode.getPackageName()) ? "" : getPackageFragment()
+ .getElementName())
+ + ".";
+
+ for (ReceiverNode receiverNode : applicationNode.getReceiverNodes())
+ {
+ if (receiverNode.getName().equals(getName()))
+ {
+ receiverExists = true;
+ break;
+ }
+ }
+
+ monitor.worked(1);
+
+ if (!receiverExists)
+ {
+ ReceiverNode receiverNode = new ReceiverNode(classQualifier + getName());
+
+ String receiverLabel = createReceiverLabel(monitor);
+
+ if (receiverLabel != null)
+ {
+ receiverNode.setLabel(AndroidProjectResources.STRING_CALL_PREFIX
+ + receiverLabel);
+ }
+
+ IntentFilterNode intentFilterNode = new IntentFilterNode();
+
+ for (String intentFilterAction : getIntentFilterActionsAsArray())
+ {
+ intentFilterNode.addActionNode(new ActionNode(intentFilterAction));
+ }
+
+ for (String intentFilterCategory : getIntentFilterCategoriesAsArray())
+ {
+ intentFilterNode.addCategoryNode(new CategoryNode(intentFilterCategory));
+ }
+
+ if (intentFilterNode.getChildren().length > 0)
+ {
+ receiverNode.addIntentFilterNode(intentFilterNode);
+ }
+
+ applicationNode.addReceiverNode(receiverNode);
+
+ monitor.worked(1);
+
+ monitor.subTask(CodeUtilsNLS.UI_Common_SavingTheAndroidManifestXMLFile);
+
+ AndroidProjectManifestFile.saveToProject(getProject(), androidManifestFile,
+ true);
+
+ created = true;
+
+ monitor.worked(1);
+ }
+ }
+ }
+ catch (AndroidException e)
+ {
+ String errMsg =
+ NLS.bind(CodeUtilsNLS.EXC_Receiver_CannotUpdateTheManifestFile, getName(),
+ e.getLocalizedMessage());
+ throw new AndroidException(errMsg);
+ }
+ catch (CoreException e)
+ {
+ String errMsg =
+ NLS.bind(CodeUtilsNLS.EXC_Receiver_CannotUpdateTheManifestFile, getName(),
+ e.getLocalizedMessage());
+ throw new AndroidException(errMsg);
+ }
+ finally
+ {
+ monitor.done();
+ }
+
+ return created;
+ }
+
+ /*
+ * Adds the Receiver label value on the strings resource file
+ *
+ * @param monitor the progress monitor
+ *
+ * @return The label value if it has been added to the strings resource file or null otherwise
+ * @throws AndroidException
+ */
+ private String createReceiverLabel(IProgressMonitor monitor) throws AndroidException
+ {
+ String resLabel = null;
+
+ if ((getLabel() != null) && (getLabel().trim().length() > 0))
+ {
+ try
+ {
+ monitor.subTask(CodeUtilsNLS.UI_Common_UpdatingTheStringsResourceFile);
+
+ ResourceFile stringsFile =
+ AndroidProjectResources.getResourceFile(getProject(), NodeType.String);
+
+ monitor.worked(1);
+
+ if (stringsFile.getResourcesNode() == null)
+ {
+ stringsFile.addResourceEntry(new ResourcesNode());
+ }
+
+ resLabel =
+ stringsFile.getNewResourceName(getName() + RECEIVER_RESOURCE_LABEL_SUFFIX);
+
+ StringNode strNode = new StringNode(resLabel);
+ strNode.setNodeValue(getLabel());
+
+ stringsFile.getResourcesNode().addChildNode(strNode);
+
+ monitor.worked(1);
+
+ AndroidProjectResources
+ .saveResourceFile(getProject(), stringsFile, NodeType.String);
+
+ monitor.worked(1);
+ }
+ catch (CoreException e)
+ {
+ String errMsg =
+ NLS.bind(CodeUtilsNLS.EXC_Activity_CannotCreateTheActivityLabel,
+ e.getLocalizedMessage());
+ throw new AndroidException(errMsg);
+ }
+ catch (AndroidException e)
+ {
+ String errMsg =
+ NLS.bind(CodeUtilsNLS.EXC_Activity_CannotCreateTheActivityLabel,
+ e.getLocalizedMessage());
+ throw new AndroidException(errMsg);
+ }
+ }
+
+ return resLabel;
+ }
+}
diff --git a/src/plugins/android.codeutils/src/com/motorola/studio/android/model/Service.java b/src/plugins/android.codeutils/src/com/motorola/studio/android/model/Service.java
new file mode 100644
index 0000000..eeb6197
--- /dev/null
+++ b/src/plugins/android.codeutils/src/com/motorola/studio/android/model/Service.java
@@ -0,0 +1,387 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.model;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.jdt.core.JavaModelException;
+import org.eclipse.jface.wizard.IWizardContainer;
+import org.eclipse.osgi.util.NLS;
+
+import com.motorola.studio.android.codeutils.i18n.CodeUtilsNLS;
+import com.motorola.studio.android.common.IAndroidConstants;
+import com.motorola.studio.android.common.exception.AndroidException;
+import com.motorola.studio.android.manifest.AndroidProjectManifestFile;
+import com.motorola.studio.android.model.java.ServiceClass;
+import com.motorola.studio.android.model.manifest.AndroidManifestFile;
+import com.motorola.studio.android.model.manifest.dom.ApplicationNode;
+import com.motorola.studio.android.model.manifest.dom.IntentFilterNode;
+import com.motorola.studio.android.model.manifest.dom.ManifestNode;
+import com.motorola.studio.android.model.manifest.dom.ServiceNode;
+import com.motorola.studio.android.model.manifest.dom.UsesPermissionNode;
+import com.motorola.studio.android.model.resources.ResourceFile;
+import com.motorola.studio.android.model.resources.types.AbstractResourceNode.NodeType;
+import com.motorola.studio.android.model.resources.types.ResourcesNode;
+import com.motorola.studio.android.model.resources.types.StringNode;
+import com.motorola.studio.android.resources.AndroidProjectResources;
+
+/**
+ * Class that provides a service model to be used for the service wizard.
+ */
+public class Service extends Launcher
+{
+ private static final String SERVICE_RESOURCE_LABEL_SUFFIX = "ServiceLabel"; //$NON-NLS-1$
+
+ private static final int MANIFEST_UPDATING_STEPS = 6;
+
+ private static final int RESOURCES_UPDATING_STEPS = 3;
+
+ private boolean onStartMethod;
+
+ private boolean onCreateMethod;
+
+ /**
+ * Default constructor.
+ * */
+ public Service()
+ {
+ super(IAndroidConstants.CLASS_SERVICE);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.model.BuildingBlockModel#getStatus()
+ */
+ @Override
+ public IStatus getStatus()
+ {
+ return super.getStatus();
+ }
+
+ /**
+ * Create a new service class and add it to the manifest file.
+ * @return True if the service class was successfully created and added to the manifest file. Otherwise, returns false.
+ * */
+ @Override
+ public boolean save(IWizardContainer container, IProgressMonitor monitor)
+ throws AndroidException
+ {
+ boolean classCreated = createServiceClass(monitor);
+ boolean addedOnManifest = false;
+
+ if (classCreated)
+ {
+ addedOnManifest = createServiceOnManifest(monitor);
+ }
+
+ // Logs all permissions used in UDC log
+ super.save(container, monitor);
+
+ return classCreated && addedOnManifest;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.model.IWizardModel#needMoreInformation()
+ */
+ public boolean needMoreInformation()
+ {
+ return false;
+ }
+
+ /*
+ * Creates the Service java class
+ *
+ * @param monitor the progress monitor
+ *
+ * @return true if the class has been created or false otherwise
+ * @throws AndroidException
+ */
+ private boolean createServiceClass(IProgressMonitor monitor) throws AndroidException
+ {
+ boolean created = false;
+
+ monitor.subTask(CodeUtilsNLS.UI_Service_CreatingTheServiceJavaClass);
+
+ ServiceClass serviceClass =
+ new ServiceClass(getName(), getPackageFragment().getElementName(), onCreateMethod,
+ onStartMethod);
+
+ try
+ {
+ createJavaClassFile(serviceClass, monitor);
+ created = true;
+ }
+ catch (JavaModelException e)
+ {
+ String errMsg =
+ NLS.bind(CodeUtilsNLS.EXC_Service_CannotCreateTheServiceClass, getName(),
+ e.getLocalizedMessage());
+
+ throw new AndroidException(errMsg);
+ }
+ catch (AndroidException e)
+ {
+ String errMsg =
+ NLS.bind(CodeUtilsNLS.EXC_Service_CannotCreateTheServiceClass, getName(),
+ e.getLocalizedMessage());
+ throw new AndroidException(errMsg);
+ }
+
+ return created;
+ }
+
+ /*
+ * Creates the Service class entry on AndroidManifest.xml file
+ *
+ * @param monitor the progress monitor
+ *
+ * @return true if the entry has been added or false otherwise
+ * @throws AndroidException
+ */
+ private boolean createServiceOnManifest(IProgressMonitor monitor) throws AndroidException
+ {
+ boolean created = false;
+
+ try
+ {
+ int totalWork = MANIFEST_UPDATING_STEPS + RESOURCES_UPDATING_STEPS;
+
+ monitor.beginTask("", totalWork);
+
+ monitor.subTask(CodeUtilsNLS.UI_Common_UpdatingTheAndroidManifestXMLFile);
+
+ AndroidManifestFile androidManifestFile =
+ AndroidProjectManifestFile.getFromProject(getProject());
+
+ monitor.worked(1);
+
+ ManifestNode manifestNode =
+ androidManifestFile != null ? androidManifestFile.getManifestNode() : null;
+ ApplicationNode applicationNode =
+ manifestNode != null ? manifestNode.getApplicationNode() : null;
+
+ monitor.worked(1);
+
+ if (applicationNode != null)
+ {
+
+ // Adds the added permission nodes to manifest file
+ List<String> permissionsNames = new ArrayList<String>();
+ for (UsesPermissionNode i : manifestNode.getUsesPermissionNodes())
+ {
+ permissionsNames.add(i.getName());
+ }
+
+ for (String intentFilterPermission : getIntentFilterPermissionsAsArray())
+ {
+ if (!permissionsNames.contains(intentFilterPermission))
+ {
+ manifestNode.addChild(new UsesPermissionNode(intentFilterPermission));
+ }
+ }
+
+ boolean serviceExists = false;
+
+ String classQualifier =
+ (getPackageFragment().getElementName()
+ .equals(manifestNode.getPackageName()) ? "" : getPackageFragment() //$NON-NLS-1$
+ .getElementName()) + "."; //$NON-NLS-1$
+
+ for (ServiceNode serviceNode : applicationNode.getServiceNodes())
+ {
+ if (serviceNode.getName().equals(getName()))
+ {
+ serviceExists = true;
+ break;
+ }
+ }
+
+ monitor.worked(1);
+
+ if (!serviceExists)
+ {
+ ServiceNode serviceNode = new ServiceNode(classQualifier + getName());
+
+ String serviceLabel = createServiceLabel(monitor);
+
+ if (serviceLabel != null)
+ {
+ serviceNode.setLabel(AndroidProjectResources.STRING_CALL_PREFIX
+ + serviceLabel);
+ }
+
+ IntentFilterNode intentFilterNode = new IntentFilterNode();
+
+ if (intentFilterNode.getChildren().length > 0)
+ {
+ serviceNode.addIntentFilterNode(intentFilterNode);
+ }
+
+ applicationNode.addServiceNode(serviceNode);
+
+ monitor.worked(1);
+
+ monitor.subTask(CodeUtilsNLS.UI_Common_SavingTheAndroidManifestXMLFile);
+
+ AndroidProjectManifestFile.saveToProject(getProject(), androidManifestFile,
+ true);
+ created = true;
+
+ monitor.worked(1);
+ }
+ }
+ }
+ catch (AndroidException e)
+ {
+ String errMsg =
+ NLS.bind(CodeUtilsNLS.EXC_Service_CannotUpdateTheManifestFile, getName(),
+ e.getLocalizedMessage());
+ throw new AndroidException(errMsg);
+ }
+ catch (CoreException e)
+ {
+ String errMsg =
+ NLS.bind(CodeUtilsNLS.EXC_Service_CannotUpdateTheManifestFile, getName(),
+ e.getLocalizedMessage());
+ throw new AndroidException(errMsg);
+ }
+ finally
+ {
+ monitor.done();
+ }
+
+ return created;
+ }
+
+ /*
+ * Adds the Service label value on the strings resource file
+ *
+ * @param monitor The progress monitor
+ *
+ * @return The label value if it has been added to the strings resource file or null otherwise
+ * @throws AndroidException
+ */
+ private String createServiceLabel(IProgressMonitor monitor) throws AndroidException
+ {
+ String resLabel = null;
+
+ if ((getLabel() != null) && (getLabel().trim().length() > 0))
+ {
+ try
+ {
+ monitor.subTask(CodeUtilsNLS.UI_Common_UpdatingTheStringsResourceFile);
+
+ ResourceFile stringsFile =
+ AndroidProjectResources.getResourceFile(getProject(), NodeType.String);
+
+ monitor.worked(1);
+
+ if (stringsFile.getResourcesNode() == null)
+ {
+ stringsFile.addResourceEntry(new ResourcesNode());
+ }
+
+ resLabel =
+ stringsFile.getNewResourceName(getName() + SERVICE_RESOURCE_LABEL_SUFFIX);
+
+ StringNode strNode = new StringNode(resLabel);
+ strNode.setNodeValue(getLabel());
+
+ stringsFile.getResourcesNode().addChildNode(strNode);
+
+ monitor.worked(1);
+
+ AndroidProjectResources
+ .saveResourceFile(getProject(), stringsFile, NodeType.String);
+
+ monitor.worked(1);
+ }
+ catch (CoreException e)
+ {
+ String errMsg =
+ NLS.bind(CodeUtilsNLS.EXC_Service_CannotCreateTheServiceLabel,
+ e.getLocalizedMessage());
+ throw new AndroidException(errMsg);
+ }
+ catch (AndroidException e)
+ {
+ String errMsg =
+ NLS.bind(CodeUtilsNLS.EXC_Service_CannotCreateTheServiceLabel,
+ e.getLocalizedMessage());
+ throw new AndroidException(errMsg);
+ }
+ }
+
+ return resLabel;
+ }
+
+ /**
+ * @return True if the default onStart() method of the service class should be created. Otherwise, returns false.
+ */
+ public boolean isOnStartMethod()
+ {
+ return onStartMethod;
+ }
+
+ /**
+ * @param onStartMethod Set to true to have the default onStart() method of the service class automatically created.
+ */
+ public void setOnStartMethod(boolean onStartMethod)
+ {
+ this.onStartMethod = onStartMethod;
+ }
+
+ /**
+ * @return True if the default onCreate() method of the service class should be created. Otherwise, returns false.
+ */
+ public boolean isOnCreateMethod()
+ {
+ return onCreateMethod;
+ }
+
+ /**
+ * @param onStartMethod Set to true to have the default onCreate() method of the service class automatically created.
+ */
+ public void setOnCreateMethod(boolean onCreateMethod)
+ {
+ this.onCreateMethod = onCreateMethod;
+ }
+
+ /**
+ * @return The default onStart() method signature including return value and visibility level.
+ */
+ public String getOnStartMessage()
+ {
+ final String ON_START_METHOD = "public void onStart(Intent intent, int startId)";
+
+ return ON_START_METHOD; //$NON-NLS-1$
+ }
+
+ /**
+ * @return The default onCreate() method signature including return value and visibility level.
+ */
+ public String getOnCreateMessage()
+ {
+ final String ON_CREATE_METHOD = "public void onCreate()";
+
+ return ON_CREATE_METHOD; //$NON-NLS-1$
+ }
+}
diff --git a/src/plugins/android.codeutils/src/com/motorola/studio/android/model/TemplateFile.java b/src/plugins/android.codeutils/src/com/motorola/studio/android/model/TemplateFile.java
new file mode 100644
index 0000000..cd23240
--- /dev/null
+++ b/src/plugins/android.codeutils/src/com/motorola/studio/android/model/TemplateFile.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.model;
+
+/**
+ * Represents a file which is part of a template.
+ */
+public class TemplateFile
+{
+
+ private String type;
+
+ private String modelName;
+
+ private String finalName;
+
+ private String modifier;
+
+ /**
+ * Constructs a new template file setting its initial values.
+ * */
+ public TemplateFile(String type, String modelName, String finalName, String modifier)
+ {
+ this.type = type;
+ this.modelName = modelName;
+ this.finalName = finalName;
+ this.modifier = modifier;
+ }
+
+ public String getType()
+ {
+ return type;
+ }
+
+ public void setType(String type)
+ {
+ this.type = type;
+ }
+
+ public String getModelName()
+ {
+ return modelName;
+ }
+
+ public void setModelName(String modelName)
+ {
+ this.modelName = modelName;
+ }
+
+ public String getFinalName()
+ {
+ return finalName;
+ }
+
+ public void setFinalName(String finalName)
+ {
+ this.finalName = finalName;
+ }
+
+ public String getModifier()
+ {
+ return modifier;
+ }
+
+ public void setModifier(String modifier)
+ {
+ this.modifier = modifier;
+ }
+
+}
diff --git a/src/plugins/android.codeutils/src/com/motorola/studio/android/model/WidgetProvider.java b/src/plugins/android.codeutils/src/com/motorola/studio/android/model/WidgetProvider.java
new file mode 100644
index 0000000..9939522
--- /dev/null
+++ b/src/plugins/android.codeutils/src/com/motorola/studio/android/model/WidgetProvider.java
@@ -0,0 +1,543 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.model;
+
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IFolder;
+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.IStatus;
+import org.eclipse.core.runtime.SubProgressMonitor;
+import org.eclipse.jdt.core.JavaModelException;
+import org.eclipse.jface.wizard.IWizardContainer;
+import org.eclipse.osgi.util.NLS;
+
+import com.motorola.studio.android.codeutils.CodeUtilsActivator;
+import com.motorola.studio.android.codeutils.i18n.CodeUtilsNLS;
+import com.motorola.studio.android.common.exception.AndroidException;
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.common.log.UsageDataConstants;
+import com.motorola.studio.android.common.utilities.FileUtil;
+import com.motorola.studio.android.manifest.AndroidProjectManifestFile;
+import com.motorola.studio.android.model.java.WidgetProviderClass;
+import com.motorola.studio.android.model.manifest.AndroidManifestFile;
+import com.motorola.studio.android.model.manifest.dom.ActionNode;
+import com.motorola.studio.android.model.manifest.dom.ApplicationNode;
+import com.motorola.studio.android.model.manifest.dom.IntentFilterNode;
+import com.motorola.studio.android.model.manifest.dom.ManifestNode;
+import com.motorola.studio.android.model.manifest.dom.MetadataNode;
+import com.motorola.studio.android.model.manifest.dom.ReceiverNode;
+import com.motorola.studio.android.model.manifest.dom.UsesPermissionNode;
+import com.motorola.studio.android.model.resources.ResourceFile;
+import com.motorola.studio.android.model.resources.types.AbstractResourceNode.NodeType;
+import com.motorola.studio.android.model.resources.types.ResourcesNode;
+import com.motorola.studio.android.model.resources.types.StringNode;
+import com.motorola.studio.android.resources.AndroidProjectResources;
+
+/**
+ * Android WidgetProvider abstraction.
+ */
+public class WidgetProvider extends Launcher
+{
+
+ /**
+ * Constant used to define the name of the super class of the widget provider building block.
+ * We can't use the AndroidConstansts class since there's no definition there for this yet.
+ */
+ public static final String WIDGET_PROVIDER_SUPER_CLASS = "android.appwidget.AppWidgetProvider";
+
+ // Constants for monitor progress
+ private static final int MANIFEST_UPDATING_STEPS = 6;
+
+ private static final int RESOURCES_UPDATING_STEPS = 3;
+
+ private static final String COPY_WIDGET_TEMPLATES_TASK_NAME = "Copying widget template files.";
+
+ // Metadata node name
+ private static final String METADATA_NODE_NAME = "android.appwidget.provider";
+
+ // Action node name
+ private static final String ACTION_NODE_NAME = "android.appwidget.action.APPWIDGET_UPDATE";
+
+ // Widget info xml file
+ private static final String WIDGET_INFO_FILE_NAME = "widget_info";
+
+ private static final String WIDGET_PROVIDER_RESOURCE_LABEL_SUFFIX = "WidgetProviderLabel";
+
+ // Directory constants
+ private static final String RES_DIR = "res/";
+
+ private static final String XML_DIR = "xml/";
+
+ private static final String LAYOUT_DIR = "layout/";
+
+ private static final String DRAWABLE = "drawable";
+
+ private static final String WIDGET_TEMPLATE_FOLDER = "templates/widget_project/";
+
+ private static final String WIDGET_INITIAL_LAYOUT_XML = "widget_initial_layout.xml";
+
+ private static final String WIDGET_INFO_XML = "widget_info.xml";
+
+ private static final String ICON = "icon.png";
+
+ private static final String WIDGET_ICON_PATH = "icons/obj16/plate16.png";
+
+ /**
+ * Constructor
+ */
+ public WidgetProvider()
+ {
+ super(WIDGET_PROVIDER_SUPER_CLASS);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.model.BuildingBlockModel#getStatus()
+ */
+ @Override
+ public IStatus getStatus()
+ {
+ return super.getStatus();
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.model.IWizardModel#needMoreInformation()
+ */
+ @Override
+ public boolean needMoreInformation()
+ {
+ return false;
+ }
+
+ /**
+ * Create the wigdet class and add it to the manifest file.
+ * @return True if the widget class was successfully create and added to the manifest file. Otherwise, returns false.
+ * */
+ @Override
+ public boolean save(IWizardContainer container, IProgressMonitor monitor)
+ throws AndroidException
+ {
+ boolean classCreated = createWidgetProviderClass(monitor);
+ boolean addedOnManifest = false;
+ boolean createdWidgetInfoFiles = false;
+
+ if (classCreated)
+ {
+ addedOnManifest = createWidgetProviderOnManifest(monitor);
+
+ // Logs to UDC the widget provider creation
+ StudioLogger.collectUsageData(UsageDataConstants.WHAT_WIDGETPROVIDER_CREATED, //$NON-NLS-1$
+ UsageDataConstants.KIND_WIDGETPROVIDER, "", //$NON-NLS-1$
+ CodeUtilsActivator.PLUGIN_ID, CodeUtilsActivator.getDefault().getBundle()
+ .getVersion().toString());
+
+ }
+
+ if (addedOnManifest)
+ {
+ createdWidgetInfoFiles = createWidgetInfoFiles(monitor);
+ }
+
+ // Logs all permissions used in UDC log
+ super.save(container, monitor);
+
+ return classCreated && addedOnManifest && createdWidgetInfoFiles;
+ }
+
+ /*
+ * Creates the WidgetProvider java class
+ *
+ * @param monitor the progress monitor
+ *
+ * @return true if the class has been created or false otherwise
+ * @throws AndroidException
+ */
+ private boolean createWidgetProviderClass(IProgressMonitor monitor) throws AndroidException
+ {
+ boolean created = false;
+
+ monitor.subTask(CodeUtilsNLS.UI_WidgetProvider_CreatingTheWidgetProviderJavaClass);
+
+ WidgetProviderClass widgetProviderClass =
+ new WidgetProviderClass(getName(), getPackageFragment().getElementName());
+
+ try
+ {
+ createJavaClassFile(widgetProviderClass, monitor);
+ created = true;
+ }
+ catch (JavaModelException e)
+ {
+ String errMsg =
+ NLS.bind(CodeUtilsNLS.EXC_Receiver_CannotCreateTheReceiverClass, getName(),
+ e.getLocalizedMessage());
+
+ throw new AndroidException(errMsg);
+ }
+ catch (AndroidException e)
+ {
+ String errMsg =
+ NLS.bind(CodeUtilsNLS.EXC_Receiver_CannotCreateTheReceiverClass, getName(),
+ e.getLocalizedMessage());
+ throw new AndroidException(errMsg);
+ }
+
+ return created;
+ }
+
+ /*
+ * Creates the Widget Provider class entry on AndroidManifest.xml file
+ *
+ * @param monitor the progress monitor
+ *
+ * @return true if the entry has been added or false otherwise
+ * @throws AndroidException
+ */
+ private boolean createWidgetProviderOnManifest(IProgressMonitor monitor)
+ throws AndroidException
+ {
+ boolean created = false;
+
+ try
+ {
+ int totalWork = MANIFEST_UPDATING_STEPS + RESOURCES_UPDATING_STEPS;
+
+ monitor.beginTask("", totalWork);
+
+ monitor.subTask(CodeUtilsNLS.UI_Common_UpdatingTheAndroidManifestXMLFile);
+
+ AndroidManifestFile androidManifestFile =
+ AndroidProjectManifestFile.getFromProject(getProject());
+
+ monitor.worked(1);
+
+ ManifestNode manifestNode =
+ androidManifestFile != null ? androidManifestFile.getManifestNode() : null;
+ ApplicationNode applicationNode =
+ manifestNode != null ? manifestNode.getApplicationNode() : null;
+
+ monitor.worked(1);
+
+ if (applicationNode != null)
+ {
+
+ // Adds the added permission nodes to manifest file
+ List<String> permissionsNames = new ArrayList<String>();
+ for (UsesPermissionNode i : manifestNode.getUsesPermissionNodes())
+ {
+ permissionsNames.add(i.getName());
+ }
+
+ for (String intentFilterPermission : getIntentFilterPermissionsAsArray())
+ {
+ if (!permissionsNames.contains(intentFilterPermission))
+ {
+ manifestNode.addChild(new UsesPermissionNode(intentFilterPermission));
+ }
+ }
+
+ boolean widgetProviderExists = false;
+
+ String classQualifier =
+ (getPackageFragment().getElementName()
+ .equals(manifestNode.getPackageName()) ? "" : getPackageFragment()
+ .getElementName())
+ + ".";
+
+ // Check if the building block already exists in the manifest file
+ for (ReceiverNode receiverNode : applicationNode.getReceiverNodes())
+ {
+ if (receiverNode.getName().equals(getName()))
+ {
+ widgetProviderExists = true;
+ break;
+ }
+ }
+
+ monitor.worked(1);
+
+ // Create the receiver node that declares the widget provider
+ if (!widgetProviderExists)
+ {
+ ReceiverNode widgetProviderNode = new ReceiverNode(classQualifier + getName());
+
+ String widgetProviderLabel = createWidgetProviderLabel(monitor);
+
+ if (widgetProviderLabel != null)
+ {
+ widgetProviderNode.setLabel(AndroidProjectResources.STRING_CALL_PREFIX
+ + widgetProviderLabel);
+ }
+
+ // Add a intent filter node with the correct action to the receiver node
+ IntentFilterNode intentFilterNode = new IntentFilterNode();
+ ActionNode actionNode = new ActionNode(ACTION_NODE_NAME);
+ intentFilterNode.addActionNode(actionNode);
+ widgetProviderNode.addIntentFilterNode(intentFilterNode);
+
+ // Add a metadada node to the receiver node
+ MetadataNode metadataNode = new MetadataNode(METADATA_NODE_NAME);
+ metadataNode.setResource(AndroidProjectResources.XML_CALL_PREFIX
+ + WIDGET_INFO_FILE_NAME);
+ widgetProviderNode.addMetadataNode(metadataNode);
+
+ applicationNode.addReceiverNode(widgetProviderNode);
+
+ monitor.worked(1);
+
+ monitor.subTask(CodeUtilsNLS.UI_Common_SavingTheAndroidManifestXMLFile);
+
+ AndroidProjectManifestFile.saveToProject(getProject(), androidManifestFile,
+ true);
+
+ created = true;
+
+ monitor.worked(1);
+ }
+ }
+ }
+ catch (AndroidException e)
+ {
+ String errMsg =
+ NLS.bind(CodeUtilsNLS.EXC_Receiver_CannotUpdateTheManifestFile, getName(),
+ e.getLocalizedMessage());
+ throw new AndroidException(errMsg);
+ }
+ catch (CoreException e)
+ {
+ String errMsg =
+ NLS.bind(CodeUtilsNLS.EXC_Receiver_CannotUpdateTheManifestFile, getName(),
+ e.getLocalizedMessage());
+ throw new AndroidException(errMsg);
+ }
+ finally
+ {
+ monitor.done();
+ }
+
+ return created;
+ }
+
+ /*
+ * Adds the Widget label value on the strings resource file
+ *
+ * @param monitor the progress monitor
+ *
+ * @return The label value if it has been added to the strings resource file or null otherwise
+ * @throws AndroidException
+ */
+ private String createWidgetProviderLabel(IProgressMonitor monitor) throws AndroidException
+ {
+ String resLabel = null;
+
+ if ((getLabel() != null) && (getLabel().trim().length() > 0))
+ {
+ try
+ {
+ monitor.subTask(CodeUtilsNLS.UI_Common_UpdatingTheStringsResourceFile);
+
+ ResourceFile stringsFile =
+ AndroidProjectResources.getResourceFile(getProject(), NodeType.String);
+
+ monitor.worked(1);
+
+ if (stringsFile.getResourcesNode() == null)
+ {
+ stringsFile.addResourceEntry(new ResourcesNode());
+ }
+
+ resLabel =
+ stringsFile.getNewResourceName(getName()
+ + WIDGET_PROVIDER_RESOURCE_LABEL_SUFFIX);
+
+ StringNode strNode = new StringNode(resLabel);
+ strNode.setNodeValue(getLabel());
+
+ stringsFile.getResourcesNode().addChildNode(strNode);
+
+ monitor.worked(1);
+
+ AndroidProjectResources
+ .saveResourceFile(getProject(), stringsFile, NodeType.String);
+
+ monitor.worked(1);
+ }
+ catch (CoreException e)
+ {
+ String errMsg =
+ NLS.bind(CodeUtilsNLS.EXC_Activity_CannotCreateTheActivityLabel,
+ e.getLocalizedMessage());
+ throw new AndroidException(errMsg);
+ }
+ catch (AndroidException e)
+ {
+ String errMsg =
+ NLS.bind(CodeUtilsNLS.EXC_Activity_CannotCreateTheActivityLabel,
+ e.getLocalizedMessage());
+ throw new AndroidException(errMsg);
+ }
+ }
+
+ return resLabel;
+ }
+
+ private boolean createWidgetInfoFiles(IProgressMonitor monitor) throws AndroidException
+ {
+ boolean created = false;
+
+ try
+ {
+ CodeUtilsActivator.getDefault();
+
+ monitor.beginTask("", 100);
+
+ monitor.subTask(COPY_WIDGET_TEMPLATES_TASK_NAME);
+
+ monitor.worked(10);
+
+ IFolder resFolder = getProject().getFolder(RES_DIR);
+
+ // get project folders
+ IResource[] resList = resFolder.members(IResource.FOLDER);
+
+ // looks forward icon.png resource
+ boolean iconExist = false;
+ for (int i = 0; i < resList.length; i++)
+ {
+ // inside drawable folders
+ if (resList[i].getName().indexOf(DRAWABLE) >= 0)
+ {
+ IFile iconFile = ((IFolder) resList[i]).getFile(ICON);
+
+ if (iconFile.exists())
+ {
+ iconExist = true;
+ break;
+ }
+ }
+ }
+ // creates the icon if it does not exist
+ if (!iconExist)
+ {
+ IFile imageFile =
+ getProject().getFile(
+ RES_DIR + IPath.SEPARATOR + DRAWABLE + IPath.SEPARATOR + ICON);
+
+ URL imgUrl = CodeUtilsActivator.getDefault().getBundle().getEntry(WIDGET_ICON_PATH);
+
+ if (imgUrl != null)
+ {
+ IFolder drawablefolder =
+ getProject().getFolder(RES_DIR + DRAWABLE + IPath.SEPARATOR);
+
+ // creates drawable folder if it does not exist
+ if (!drawablefolder.exists())
+ {
+ FileUtil.createProjectFolder(getProject(), RES_DIR, DRAWABLE
+ + IPath.SEPARATOR, monitor);
+ }
+ imageFile.create(imgUrl.openStream(), IResource.NONE, new SubProgressMonitor(
+ monitor, 1000));
+ }
+ }
+
+ // Create an "xml" folder inside the "res" folder of the project
+ FileUtil.createProjectFolder(getProject(), RES_DIR, XML_DIR, monitor);
+
+ // Copy the "widget_info" file to the xml folder and the "widget_initial_layout" file to the layout folder
+ IFolder layoutfolder = getProject().getFolder(RES_DIR + LAYOUT_DIR);
+ IFolder xmlFolder = getProject().getFolder(RES_DIR + XML_DIR);
+
+ if (!layoutfolder.exists())
+ {
+ FileUtil.createProjectFolder(getProject(), RES_DIR, LAYOUT_DIR + IPath.SEPARATOR,
+ monitor);
+ }
+
+ if (!xmlFolder.exists())
+ {
+ FileUtil.createProjectFolder(getProject(), RES_DIR, XML_DIR + IPath.SEPARATOR,
+ monitor);
+ }
+
+ IFile initialLayoutFile = layoutfolder.getFile(WIDGET_INITIAL_LAYOUT_XML);
+ if (!initialLayoutFile.exists())
+ {
+
+ // Retrieve template file and create it at destination
+ URL templateURL =
+ CodeUtilsActivator.getDefault().getBundle()
+ .getEntry(WIDGET_TEMPLATE_FOLDER + WIDGET_INITIAL_LAYOUT_XML);
+ if (templateURL != null)
+ {
+ initialLayoutFile.create(templateURL.openStream(), false, monitor);
+ }
+ else
+ {
+ throw new AndroidException();
+ }
+
+ monitor.worked(20);
+
+ }
+
+ IFile widgetInfoFile = xmlFolder.getFile(WIDGET_INFO_XML);
+ if (!widgetInfoFile.exists())
+ {
+ // Retrieve template file and create it at destination
+ URL templateURL =
+ CodeUtilsActivator.getDefault().getBundle()
+ .getEntry(WIDGET_TEMPLATE_FOLDER + WIDGET_INFO_XML);
+
+ if (templateURL != null)
+ {
+ widgetInfoFile.create(templateURL.openStream(), false, monitor);
+ }
+ else
+ {
+ throw new AndroidException();
+ }
+
+ monitor.worked(20);
+ }
+
+ created = true;
+
+ }
+ catch (Exception e)
+ {
+ String errMsg =
+ NLS.bind(CodeUtilsNLS.EXC_WidgetProvider_CannotCopyTemplateFiles, getName(),
+ e.getLocalizedMessage());
+ throw new AndroidException(errMsg);
+ }
+ finally
+ {
+ monitor.done();
+ }
+
+ return created;
+
+ }
+}
diff --git a/src/plugins/android.codeutils/src/com/motorola/studio/android/model/java/ActivityClass.java b/src/plugins/android.codeutils/src/com/motorola/studio/android/model/java/ActivityClass.java
new file mode 100644
index 0000000..fd7a5ed
--- /dev/null
+++ b/src/plugins/android.codeutils/src/com/motorola/studio/android/model/java/ActivityClass.java
@@ -0,0 +1,223 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.model.java;
+
+import org.eclipse.jdt.core.dom.AST;
+import org.eclipse.jdt.core.dom.ASTNode;
+import org.eclipse.jdt.core.dom.ASTParser;
+import org.eclipse.jdt.core.dom.Block;
+import org.eclipse.jdt.core.dom.CompilationUnit;
+import org.eclipse.jdt.core.dom.ImportDeclaration;
+import org.eclipse.jdt.core.dom.MethodDeclaration;
+import org.eclipse.jdt.core.dom.Modifier.ModifierKeyword;
+import org.eclipse.jdt.core.dom.PrimitiveType;
+import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
+import org.eclipse.jdt.core.dom.SuperMethodInvocation;
+import org.eclipse.jdt.core.dom.Type;
+import org.eclipse.jdt.core.dom.TypeDeclaration;
+import org.eclipse.jdt.core.dom.rewrite.ASTRewrite;
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.text.edits.MalformedTreeException;
+import org.eclipse.text.edits.TextEdit;
+
+import com.motorola.studio.android.codeutils.i18n.CodeUtilsNLS;
+import com.motorola.studio.android.common.exception.AndroidException;
+import com.motorola.studio.android.common.log.StudioLogger;
+
+/**
+ * Class used to create an Android Activity building block class
+ */
+public class ActivityClass extends JavaClass
+{
+ public static final String ACTIVITY_SUPERCLASS = "android.app.Activity";
+
+ private static final String[] BUNDLE_CLASS = getFQNAsArray("android.os.Bundle");
+
+ protected static final String ONCREATE_METHOD_NAME = "onCreate";
+
+ protected static final String ONSTART_METHOD_NAME = "onStart";
+
+ private ASTRewrite rewrite = null;
+
+ /**
+ * The constructor
+ *
+ * @param className The simple class name
+ * @param packageName The full-qualified class package name
+ * @param addOnStart If true, adds the OnStart method to the activity class
+ */
+ public ActivityClass(String className, String packageName, boolean addOnStart)
+ {
+ super(className, packageName, ACTIVITY_SUPERCLASS);
+ addBasicActivityInfo();
+
+ if (addOnStart)
+ {
+ addOnStartMethod();
+ }
+ }
+
+ /**
+ * Adds basic information to the activity class
+ */
+ private void addBasicActivityInfo()
+ {
+ addOnCreateMethod();
+
+ // Adds JavaDoc to elements
+ addComment(classDecl, CodeUtilsNLS.MODEL_ActivityClass_ActivityDescription);
+ }
+
+ /**
+ * Adds the onCreate method to the activity class
+ */
+ @SuppressWarnings("unchecked")
+ private void addOnCreateMethod()
+ {
+ // Adds import for Bundle
+ ImportDeclaration intentImport = ast.newImportDeclaration();
+ intentImport.setName(ast.newName(BUNDLE_CLASS));
+ compUnit.imports().add(intentImport);
+
+ MethodDeclaration onCreateMethod = ast.newMethodDeclaration();
+ onCreateMethod.modifiers().add(ast.newModifier(ModifierKeyword.PROTECTED_KEYWORD));
+ onCreateMethod.setReturnType2(ast.newPrimitiveType(PrimitiveType.VOID));
+ onCreateMethod.setName(ast.newSimpleName(ONCREATE_METHOD_NAME));
+ addMethodParameter(onCreateMethod, "savedInstanceState",
+ ast.newSimpleType(ast.newSimpleName(getName(BUNDLE_CLASS))));
+ addEmptyBlock(onCreateMethod);
+ addSuperInvocation(onCreateMethod);
+ classDecl.bodyDeclarations().add(onCreateMethod);
+
+ // Adds JavaDoc to the method
+ addComment(onCreateMethod, CodeUtilsNLS.MODEL_ActivityClass_OnCreateMethodDescription);
+ addMethodReference(onCreateMethod, ACTIVITY_SUPERCLASS, ONCREATE_METHOD_NAME, new Type[]
+ {
+ ast.newSimpleType(ast.newSimpleName(getName(BUNDLE_CLASS)))
+ });
+ }
+
+ /**
+ * Adds the onStart method to the activity class
+ */
+ @SuppressWarnings("unchecked")
+ private void addOnStartMethod()
+ {
+ MethodDeclaration onStartMethod = ast.newMethodDeclaration();
+ onStartMethod.modifiers().add(ast.newModifier(ModifierKeyword.PROTECTED_KEYWORD));
+ onStartMethod.setReturnType2(ast.newPrimitiveType(PrimitiveType.VOID));
+ onStartMethod.setName(ast.newSimpleName(ONSTART_METHOD_NAME));
+ addEmptyBlock(onStartMethod);
+ addSuperInvocation(onStartMethod);
+ classDecl.bodyDeclarations().add(onStartMethod);
+
+ // Adds JavaDoc to the method
+ addComment(onStartMethod, CodeUtilsNLS.MODEL_ActivityClass_OnStartMethodDescription);
+ addMethodReference(onStartMethod, ACTIVITY_SUPERCLASS, ONSTART_METHOD_NAME, null);
+ }
+
+ /**
+ * Adds a "super.method(arguments...)" statement inside the given method body
+ *
+ * @param method The method declaration
+ */
+ @SuppressWarnings("unchecked")
+ private void addSuperInvocation(MethodDeclaration method)
+ {
+ SuperMethodInvocation superInv = ast.newSuperMethodInvocation();
+ superInv.setName(ast.newSimpleName(method.getName().toString()));
+ for (Object param : method.parameters())
+ {
+ if (param instanceof SingleVariableDeclaration)
+ {
+ SingleVariableDeclaration vd = (SingleVariableDeclaration) param;
+ String varName = vd.getName().toString();
+ superInv.arguments().add(ast.newSimpleName(varName));
+ }
+ }
+ method.getBody().statements().add(ast.newExpressionStatement(superInv));
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.model.java.JavaClass#addComments()
+ */
+ @Override
+ protected void addComments() throws AndroidException
+ {
+ ASTNode todoComment;
+
+ ASTParser parser = ASTParser.newParser(AST.JLS3);
+ parser.setSource(document.get().toCharArray());
+
+ compUnit = (CompilationUnit) parser.createAST(null);
+ ast = compUnit.getAST();
+ rewrite = ASTRewrite.create(ast);
+
+ todoComment =
+ rewrite.createStringPlaceholder(CodeUtilsNLS.MODEL_Common_ToDoPutYourCodeHere,
+ ASTNode.EMPTY_STATEMENT);
+
+ TypeDeclaration activityClass = (TypeDeclaration) compUnit.types().get(0);
+ MethodDeclaration method;
+ Block block;
+
+ // Adds the Override annotation and ToDo comment to all overridden methods
+ for (int i = 0; i < activityClass.bodyDeclarations().size(); i++)
+ {
+ method = (MethodDeclaration) activityClass.bodyDeclarations().get(i);
+
+ // Adds the Override annotation
+ rewrite.getListRewrite(method, method.getModifiersProperty()).insertFirst(
+ OVERRIDE_ANNOTATION, null);
+
+ // Adds the ToDo comment
+ block = method.getBody();
+ rewrite.getListRewrite(block, Block.STATEMENTS_PROPERTY).insertLast(todoComment, null);
+ }
+
+ try
+ {
+ // Writes the modifications
+ TextEdit modifications = rewrite.rewriteAST(document, null);
+ modifications.apply(document);
+ }
+ catch (IllegalArgumentException e)
+ {
+ String errMsg =
+ NLS.bind(CodeUtilsNLS.EXC_JavaClass_ErrorApplyingCommentsToCode, className);
+
+ StudioLogger.error(ActivityClass.class, errMsg, e);
+ throw new AndroidException(errMsg);
+ }
+ catch (MalformedTreeException e)
+ {
+ String errMsg =
+ NLS.bind(CodeUtilsNLS.EXC_JavaClass_ErrorApplyingCommentsToCode, className);
+
+ StudioLogger.error(ActivityClass.class, errMsg, e);
+ throw new AndroidException(errMsg);
+ }
+ catch (BadLocationException e)
+ {
+ String errMsg =
+ NLS.bind(CodeUtilsNLS.EXC_JavaClass_ErrorApplyingCommentsToCode, className);
+
+ StudioLogger.error(ActivityClass.class, errMsg, e);
+ throw new AndroidException(errMsg);
+ }
+ }
+}
diff --git a/src/plugins/android.codeutils/src/com/motorola/studio/android/model/java/BroadcastReceiverClass.java b/src/plugins/android.codeutils/src/com/motorola/studio/android/model/java/BroadcastReceiverClass.java
new file mode 100644
index 0000000..ab0fb50
--- /dev/null
+++ b/src/plugins/android.codeutils/src/com/motorola/studio/android/model/java/BroadcastReceiverClass.java
@@ -0,0 +1,190 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.motorola.studio.android.model.java;
+
+import org.eclipse.jdt.core.dom.AST;
+import org.eclipse.jdt.core.dom.ASTNode;
+import org.eclipse.jdt.core.dom.ASTParser;
+import org.eclipse.jdt.core.dom.Block;
+import org.eclipse.jdt.core.dom.CompilationUnit;
+import org.eclipse.jdt.core.dom.ImportDeclaration;
+import org.eclipse.jdt.core.dom.MethodDeclaration;
+import org.eclipse.jdt.core.dom.Modifier.ModifierKeyword;
+import org.eclipse.jdt.core.dom.PrimitiveType;
+import org.eclipse.jdt.core.dom.Type;
+import org.eclipse.jdt.core.dom.TypeDeclaration;
+import org.eclipse.jdt.core.dom.rewrite.ASTRewrite;
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.text.edits.MalformedTreeException;
+import org.eclipse.text.edits.TextEdit;
+
+import com.motorola.studio.android.codeutils.i18n.CodeUtilsNLS;
+import com.motorola.studio.android.common.exception.AndroidException;
+import com.motorola.studio.android.common.log.StudioLogger;
+
+/**
+ * Class used to create an Android Broadcast Receiver building block class
+ */
+public class BroadcastReceiverClass extends JavaClass
+{
+ private static final String BROADCAST_RECEIVER_SUPERCLASS = "android.content.BroadcastReceiver";
+
+ private static final String[] INTENT_CLASS = getFQNAsArray("android.content.Intent");
+
+ private static final String[] CONTEXT_CLASS = getFQNAsArray("android.content.Context");
+
+ private static final String ONRECEIVE_METHOD_NAME = "onReceive";
+
+ private ASTRewrite rewrite = null;
+
+ /**
+ * The constructor
+ *
+ * @param className The simple class name
+ * @param packageName The full-qualified class package name
+ */
+ public BroadcastReceiverClass(String className, String packageName)
+ {
+ super(className, packageName, BROADCAST_RECEIVER_SUPERCLASS);
+
+ addBasicBroadcastReceiverInfo();
+ }
+
+ /**
+ * Adds basic information to the broadcast receiver class
+ */
+ @SuppressWarnings("unchecked")
+ private void addBasicBroadcastReceiverInfo()
+ {
+ // Adds import for Intent
+ ImportDeclaration intentImport = ast.newImportDeclaration();
+ intentImport.setName(ast.newName(INTENT_CLASS));
+ compUnit.imports().add(intentImport);
+
+ ImportDeclaration contextImport = ast.newImportDeclaration();
+ contextImport.setName(ast.newName(CONTEXT_CLASS));
+ compUnit.imports().add(contextImport);
+
+ addOnReceiveMethod();
+
+ // Adds JavaDoc to elements
+ addComment(classDecl,
+ CodeUtilsNLS.MODEL_BroadcastReceiverClass_BroadcastReceiverDescription);
+
+ }
+
+ /**
+ * Adds the onReceive method to the broadcast receiver class
+ */
+ @SuppressWarnings("unchecked")
+ private void addOnReceiveMethod()
+ {
+ MethodDeclaration onReceiveMethod = ast.newMethodDeclaration();
+ onReceiveMethod.modifiers().add(ast.newModifier(ModifierKeyword.PUBLIC_KEYWORD));
+ onReceiveMethod.setReturnType2(ast.newPrimitiveType(PrimitiveType.VOID));
+ onReceiveMethod.setName(ast.newSimpleName(ONRECEIVE_METHOD_NAME));
+ addMethodParameter(onReceiveMethod, getName(CONTEXT_CLASS).toLowerCase(),
+ ast.newSimpleType(ast.newSimpleName(getName(CONTEXT_CLASS))));
+ addMethodParameter(onReceiveMethod, getName(INTENT_CLASS).toLowerCase(),
+ ast.newSimpleType(ast.newSimpleName(getName(INTENT_CLASS))));
+ addEmptyBlock(onReceiveMethod);
+ classDecl.bodyDeclarations().add(onReceiveMethod);
+
+ // Adds JavaDoc to the method
+ addComment(onReceiveMethod,
+ CodeUtilsNLS.MODEL_BroadcastReceiverClass_onReceiveMethodDescription);
+ addMethodReference(
+ onReceiveMethod,
+ BROADCAST_RECEIVER_SUPERCLASS,
+ ONRECEIVE_METHOD_NAME,
+ new Type[]
+ {
+ ast.newSimpleType(ast.newSimpleName(getName(CONTEXT_CLASS))),
+ ast.newSimpleType(ast.newSimpleName(getName(INTENT_CLASS)))
+ });
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.model.java.JavaClass#addComments()
+ */
+ @Override
+ protected void addComments() throws AndroidException
+ {
+ ASTNode todoComment;
+
+ ASTParser parser = ASTParser.newParser(AST.JLS3);
+ parser.setSource(document.get().toCharArray());
+
+ compUnit = (CompilationUnit) parser.createAST(null);
+ ast = compUnit.getAST();
+ rewrite = ASTRewrite.create(ast);
+
+ todoComment =
+ rewrite.createStringPlaceholder(CodeUtilsNLS.MODEL_Common_ToDoPutYourCodeHere,
+ ASTNode.EMPTY_STATEMENT);
+
+ TypeDeclaration receiverClass = (TypeDeclaration) compUnit.types().get(0);
+ MethodDeclaration method;
+ Block block;
+
+ // Adds the Override annotation and ToDo comment to all overridden methods
+ for (int i = 0; i < receiverClass.bodyDeclarations().size(); i++)
+ {
+ method = (MethodDeclaration) receiverClass.bodyDeclarations().get(i);
+
+ // Adds the Override annotation
+ rewrite.getListRewrite(method, method.getModifiersProperty()).insertFirst(
+ OVERRIDE_ANNOTATION, null);
+
+ // Adds the ToDo comment
+ block = method.getBody();
+ rewrite.getListRewrite(block, Block.STATEMENTS_PROPERTY).insertFirst(todoComment, null);
+ }
+
+ try
+ {
+ // Writes the modifications
+ TextEdit modifications = rewrite.rewriteAST(document, null);
+ modifications.apply(document);
+ }
+ catch (IllegalArgumentException e)
+ {
+ String errMsg =
+ NLS.bind(CodeUtilsNLS.EXC_JavaClass_ErrorApplyingCommentsToCode, className);
+
+ StudioLogger.error(BroadcastReceiverClass.class, errMsg, e);
+ throw new AndroidException(errMsg);
+ }
+ catch (MalformedTreeException e)
+ {
+ String errMsg =
+ NLS.bind(CodeUtilsNLS.EXC_JavaClass_ErrorApplyingCommentsToCode, className);
+
+ StudioLogger.error(BroadcastReceiverClass.class, errMsg, e);
+ throw new AndroidException(errMsg);
+ }
+ catch (BadLocationException e)
+ {
+ String errMsg =
+ NLS.bind(CodeUtilsNLS.EXC_JavaClass_ErrorApplyingCommentsToCode, className);
+
+ StudioLogger.error(BroadcastReceiverClass.class, errMsg, e);
+ throw new AndroidException(errMsg);
+ }
+ }
+} \ No newline at end of file
diff --git a/src/plugins/android.codeutils/src/com/motorola/studio/android/model/java/ContentProviderClass.java b/src/plugins/android.codeutils/src/com/motorola/studio/android/model/java/ContentProviderClass.java
new file mode 100644
index 0000000..2a81305
--- /dev/null
+++ b/src/plugins/android.codeutils/src/com/motorola/studio/android/model/java/ContentProviderClass.java
@@ -0,0 +1,433 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.model.java;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.jdt.core.dom.AST;
+import org.eclipse.jdt.core.dom.ASTNode;
+import org.eclipse.jdt.core.dom.ASTParser;
+import org.eclipse.jdt.core.dom.Block;
+import org.eclipse.jdt.core.dom.BodyDeclaration;
+import org.eclipse.jdt.core.dom.CompilationUnit;
+import org.eclipse.jdt.core.dom.FieldDeclaration;
+import org.eclipse.jdt.core.dom.ImportDeclaration;
+import org.eclipse.jdt.core.dom.MethodDeclaration;
+import org.eclipse.jdt.core.dom.MethodInvocation;
+import org.eclipse.jdt.core.dom.Modifier.ModifierKeyword;
+import org.eclipse.jdt.core.dom.Name;
+import org.eclipse.jdt.core.dom.PrimitiveType;
+import org.eclipse.jdt.core.dom.StringLiteral;
+import org.eclipse.jdt.core.dom.Type;
+import org.eclipse.jdt.core.dom.TypeDeclaration;
+import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
+import org.eclipse.jdt.core.dom.rewrite.ASTRewrite;
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.text.edits.MalformedTreeException;
+import org.eclipse.text.edits.TextEdit;
+
+import com.motorola.studio.android.codeutils.i18n.CodeUtilsNLS;
+import com.motorola.studio.android.common.exception.AndroidException;
+import com.motorola.studio.android.common.log.StudioLogger;
+
+/**
+ * Class used to create an Android Content Provider building block class
+ */
+public class ContentProviderClass extends JavaClass
+{
+ private static final String CP_SUPERCLASS = "android.content.ContentProvider";
+
+ private static final String[] URI_CLASS = getFQNAsArray("android.net.Uri");
+
+ private static final String[] CONTENTVALUES_CLASS =
+ getFQNAsArray("android.content.ContentValues");
+
+ private static final String[] CURSOR_CLASS = getFQNAsArray("android.database.Cursor");
+
+ private static final String DELETE_METHOD_NAME = "delete";
+
+ private static final String GETTYPE_METHOD_NAME = "getType";
+
+ private static final String INSERT_METHOD_NAME = "insert";
+
+ private static final String ONCREATE_METHOD_NAME = "onCreate";
+
+ private static final String QUERY_METHOD_NAME = "query";
+
+ private static final String UPDATE_METHOD_NAME = "update";
+
+ private static final String CONTENT_SCHEME = "content://";
+
+ private static final String CONTENT_URI_NAME = "CONTENT_URI";
+
+ private ASTRewrite rewrite;
+
+ private String authority;
+
+ /**
+ * Default constructor
+ *
+ * @param className The simple class name
+ * @param packageName The full-qualified class package name
+ */
+ public ContentProviderClass(String className, String packageName, String authority)
+ {
+ super(className, packageName, CP_SUPERCLASS);
+
+ Assert.isNotNull(authority);
+ this.authority = authority;
+
+ addBasicCPInfo();
+ }
+
+ /**
+ * Adds basic information to the content provider class
+ */
+ @SuppressWarnings("unchecked")
+ private void addBasicCPInfo()
+ {
+ ImportDeclaration importDecl;
+
+ // Adds import for Uri
+ importDecl = ast.newImportDeclaration();
+ importDecl.setName(ast.newName(URI_CLASS));
+ compUnit.imports().add(importDecl);
+
+ // Adds import for ContentValues
+ importDecl = ast.newImportDeclaration();
+ importDecl.setName(ast.newName(CONTENTVALUES_CLASS));
+ compUnit.imports().add(importDecl);
+
+ // Adds import for Cursor
+ importDecl = ast.newImportDeclaration();
+ importDecl.setName(ast.newName(CURSOR_CLASS));
+ compUnit.imports().add(importDecl);
+
+ // Adds the authorities constants
+ addAuthority();
+
+ // Adds the delete method
+ addDeleteMethod();
+
+ // Adds the getType method
+ addGetTypeMethod();
+
+ // Adds the insert method
+ addInsertMethod();
+
+ // Adds the onCreate method
+ addOnCreateMethod();
+
+ // Adds the query method
+ addQueryMethod();
+
+ // Adds the update method
+ addUpdateMethod();
+
+ // Adds JavaDoc to elements
+ addComment(classDecl, CodeUtilsNLS.MODEL_ContentProviderClass_ContentProviderDescription);
+ }
+
+ /**
+ * Adds the default content provider Uri
+ */
+ @SuppressWarnings("unchecked")
+ private void addAuthority()
+ {
+ final String URI_PARSE_METHOD = "parse";
+
+ String contentUriValue = CONTENT_SCHEME + authority;
+ StringLiteral stringLiteral = ast.newStringLiteral();
+ stringLiteral.setLiteralValue(contentUriValue);
+
+ Name uri = ast.newSimpleName(getName(URI_CLASS));
+
+ MethodInvocation parse = ast.newMethodInvocation();
+ parse.setExpression(uri);
+ parse.setName(ast.newSimpleName(URI_PARSE_METHOD));
+ parse.arguments().add(stringLiteral);
+
+ VariableDeclarationFragment contentUri = ast.newVariableDeclarationFragment();
+
+ contentUri.setName(ast.newSimpleName(CONTENT_URI_NAME));
+ contentUri.setInitializer(parse);
+
+ FieldDeclaration fieldDeclaration = ast.newFieldDeclaration(contentUri);
+ fieldDeclaration.modifiers().add(ast.newModifier(ModifierKeyword.PUBLIC_KEYWORD));
+ fieldDeclaration.modifiers().add(ast.newModifier(ModifierKeyword.STATIC_KEYWORD));
+ fieldDeclaration.modifiers().add(ast.newModifier(ModifierKeyword.FINAL_KEYWORD));
+ fieldDeclaration.setType(uriType());
+
+ classDecl.bodyDeclarations().add(fieldDeclaration);
+
+ addComment(fieldDeclaration, CodeUtilsNLS.MODEL_ContentProviderClass_ContentUriDescription);
+ }
+
+ /**
+ * Adds the delete method to the content provider class
+ */
+ @SuppressWarnings("unchecked")
+ private void addDeleteMethod()
+ {
+ final String SELECTION_PARAM = "selection";
+ final String SELECTION_ARGS_PARAM = "selectionArgs";
+
+ MethodDeclaration method = ast.newMethodDeclaration();
+ method.modifiers().add(ast.newModifier(ModifierKeyword.PUBLIC_KEYWORD));
+ method.setReturnType2(ast.newPrimitiveType(PrimitiveType.INT));
+ method.setName(ast.newSimpleName(DELETE_METHOD_NAME));
+ addMethodParameter(method, getName(URI_CLASS).toLowerCase(), uriType());
+ addMethodParameter(method, SELECTION_PARAM, stringType());
+ addMethodParameter(method, SELECTION_ARGS_PARAM, stringArrayType());
+ addEmptyBlock(method);
+ classDecl.bodyDeclarations().add(method);
+
+ addComment(method, CodeUtilsNLS.MODEL_ContentProviderClass_DeleteMethodDescription);
+ addMethodReference(method, CP_SUPERCLASS, DELETE_METHOD_NAME, new Type[]
+ {
+ uriType(), stringType(), stringArrayType()
+ });
+ }
+
+ /**
+ * Adds the getType method to the content provider class
+ */
+ @SuppressWarnings("unchecked")
+ private void addGetTypeMethod()
+ {
+ MethodDeclaration method = ast.newMethodDeclaration();
+ method.modifiers().add(ast.newModifier(ModifierKeyword.PUBLIC_KEYWORD));
+ method.setReturnType2(stringType());
+ method.setName(ast.newSimpleName(GETTYPE_METHOD_NAME));
+ addMethodParameter(method, getName(URI_CLASS).toLowerCase(), uriType());
+
+ addEmptyBlock(method);
+ classDecl.bodyDeclarations().add(method);
+
+ addComment(method, CodeUtilsNLS.MODEL_ContentProviderClass_GetTypeMethodDescription);
+ addMethodReference(method, CP_SUPERCLASS, GETTYPE_METHOD_NAME, new Type[]
+ {
+ uriType()
+ });
+ }
+
+ /**
+ * Adds the insert method to the content provider class
+ */
+ @SuppressWarnings("unchecked")
+ private void addInsertMethod()
+ {
+ final String VALUES_PARAM = "values";
+
+ MethodDeclaration method = ast.newMethodDeclaration();
+ method.modifiers().add(ast.newModifier(ModifierKeyword.PUBLIC_KEYWORD));
+ method.setReturnType2(uriType());
+ method.setName(ast.newSimpleName(INSERT_METHOD_NAME));
+ addMethodParameter(method, getName(URI_CLASS).toLowerCase(), uriType());
+ addMethodParameter(method, VALUES_PARAM, contentValuesType());
+ addEmptyBlock(method);
+ classDecl.bodyDeclarations().add(method);
+
+ addComment(method, CodeUtilsNLS.MODEL_ContentProviderClass_InsertMethodDescription);
+ addMethodReference(method, CP_SUPERCLASS, INSERT_METHOD_NAME, new Type[]
+ {
+ uriType(), contentValuesType()
+ });
+ }
+
+ /**
+ * Adds the onCreate method to the content provider class
+ */
+ @SuppressWarnings("unchecked")
+ private void addOnCreateMethod()
+ {
+ MethodDeclaration method = ast.newMethodDeclaration();
+ method.modifiers().add(ast.newModifier(ModifierKeyword.PUBLIC_KEYWORD));
+ method.setReturnType2(ast.newPrimitiveType(PrimitiveType.BOOLEAN));
+ method.setName(ast.newSimpleName(ONCREATE_METHOD_NAME));
+ addEmptyBlock(method);
+ classDecl.bodyDeclarations().add(method);
+
+ addComment(method, CodeUtilsNLS.MODEL_ContentProviderClass_OnCreateMethodDescription);
+ addMethodReference(method, CP_SUPERCLASS, ONCREATE_METHOD_NAME, null);
+ }
+
+ /**
+ * Adds the query method to the content provider class
+ */
+ @SuppressWarnings("unchecked")
+ private void addQueryMethod()
+ {
+ final String PROJECTION_PARAM = "projection";
+ final String SELECTION_PARAM = "selection";
+ final String SELECTION_ARGS_PARAM = "selectionArgs";
+ final String SORT_ORDER_PARAM = "sortOrder";
+
+ MethodDeclaration method = ast.newMethodDeclaration();
+ method.modifiers().add(ast.newModifier(ModifierKeyword.PUBLIC_KEYWORD));
+ method.setReturnType2(cursorType());
+ method.setName(ast.newSimpleName(QUERY_METHOD_NAME));
+ addMethodParameter(method, getName(URI_CLASS).toLowerCase(), uriType());
+ addMethodParameter(method, PROJECTION_PARAM, stringArrayType());
+ addMethodParameter(method, SELECTION_PARAM, stringType());
+ addMethodParameter(method, SELECTION_ARGS_PARAM, stringArrayType());
+ addMethodParameter(method, SORT_ORDER_PARAM, stringType());
+
+ addEmptyBlock(method);
+ classDecl.bodyDeclarations().add(method);
+
+ addComment(method, CodeUtilsNLS.MODEL_ContentProviderClass_QueryMethodDescription);
+ addMethodReference(method, CP_SUPERCLASS, QUERY_METHOD_NAME, new Type[]
+ {
+ uriType(), stringArrayType(), stringType(), stringArrayType(), stringType()
+ });
+ }
+
+ /**
+ * Adds the update method to the content provider class
+ */
+ @SuppressWarnings("unchecked")
+ private void addUpdateMethod()
+ {
+ final String VALUES_PARAM = "values";
+ final String SELECTION_PARAM = "selection";
+ final String SELECTION_ARGS_PARAM = "selectionArgs";
+
+ MethodDeclaration method = ast.newMethodDeclaration();
+ method.modifiers().add(ast.newModifier(ModifierKeyword.PUBLIC_KEYWORD));
+ method.setReturnType2(ast.newPrimitiveType(PrimitiveType.INT));
+ method.setName(ast.newSimpleName(UPDATE_METHOD_NAME));
+ addMethodParameter(method, getName(URI_CLASS).toLowerCase(), uriType());
+ addMethodParameter(method, VALUES_PARAM, contentValuesType());
+ addMethodParameter(method, SELECTION_PARAM, stringType());
+ addMethodParameter(method, SELECTION_ARGS_PARAM, stringArrayType());
+
+ addEmptyBlock(method);
+ classDecl.bodyDeclarations().add(method);
+
+ addComment(method, CodeUtilsNLS.MODEL_ContentProviderClass_UpdateMethodDescription);
+ addMethodReference(method, CP_SUPERCLASS, UPDATE_METHOD_NAME, new Type[]
+ {
+ uriType(), contentValuesType(), stringType(), stringArrayType()
+ });
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.model.java.JavaClass#addComments()
+ */
+ @Override
+ protected void addComments() throws AndroidException
+ {
+ ASTNode todoComment;
+
+ ASTParser parser = ASTParser.newParser(AST.JLS3);
+ parser.setSource(document.get().toCharArray());
+
+ compUnit = (CompilationUnit) parser.createAST(null);
+ ast = compUnit.getAST();
+ rewrite = ASTRewrite.create(ast);
+
+ todoComment =
+ rewrite.createStringPlaceholder(CodeUtilsNLS.MODEL_Common_ToDoPutYourCodeHere,
+ ASTNode.EMPTY_STATEMENT);
+
+ TypeDeclaration cpClass = (TypeDeclaration) compUnit.types().get(0);
+ MethodDeclaration method;
+ Block block;
+
+ // Adds the Override annotation and ToDo comment to all abstract methods
+ for (int i = 0; i < cpClass.bodyDeclarations().size(); i++)
+ {
+ BodyDeclaration bodyDecl = (BodyDeclaration) cpClass.bodyDeclarations().get(i);
+
+ if (bodyDecl instanceof MethodDeclaration)
+ {
+ method = (MethodDeclaration) bodyDecl;
+
+ // Adds the Override annotation
+ rewrite.getListRewrite(method, method.getModifiersProperty()).insertFirst(
+ OVERRIDE_ANNOTATION, null);
+
+ // Adds the ToDo comment
+ block = method.getBody();
+ rewrite.getListRewrite(block, Block.STATEMENTS_PROPERTY).insertFirst(todoComment,
+ null);
+ }
+ }
+
+ try
+ {
+ // Writes the modifications
+ TextEdit modifications = rewrite.rewriteAST(document, null);
+ modifications.apply(document);
+ }
+ catch (IllegalArgumentException e)
+ {
+ String errMsg =
+ NLS.bind(CodeUtilsNLS.EXC_JavaClass_ErrorApplyingCommentsToCode, className);
+
+ StudioLogger.error(ContentProviderClass.class, errMsg, e);
+ throw new AndroidException(errMsg);
+ }
+ catch (MalformedTreeException e)
+ {
+ String errMsg =
+ NLS.bind(CodeUtilsNLS.EXC_JavaClass_ErrorApplyingCommentsToCode, className);
+
+ StudioLogger.error(ContentProviderClass.class, errMsg, e);
+ throw new AndroidException(errMsg);
+ }
+ catch (BadLocationException e)
+ {
+ String errMsg =
+ NLS.bind(CodeUtilsNLS.EXC_JavaClass_ErrorApplyingCommentsToCode, className);
+
+ StudioLogger.error(ContentProviderClass.class, errMsg, e);
+ throw new AndroidException(errMsg);
+ }
+ }
+
+ /**
+ * Returns a new Uri type
+ *
+ * @return a new Uri type
+ */
+ private Type uriType()
+ {
+ return ast.newSimpleType(ast.newSimpleName(getName(URI_CLASS)));
+ }
+
+ /**
+ * Returns a new ContentValues type
+ *
+ * @return a new ContentValues type
+ */
+ private Type contentValuesType()
+ {
+ return ast.newSimpleType(ast.newSimpleName(getName(CONTENTVALUES_CLASS)));
+ }
+
+ /**
+ * Returns a new Cursor type
+ *
+ * @return a new Cursor type
+ */
+ private Type cursorType()
+ {
+ return ast.newSimpleType(ast.newSimpleName(getName(CURSOR_CLASS)));
+ }
+
+}
diff --git a/src/plugins/android.codeutils/src/com/motorola/studio/android/model/java/JavaClass.java b/src/plugins/android.codeutils/src/com/motorola/studio/android/model/java/JavaClass.java
new file mode 100644
index 0000000..523a38e
--- /dev/null
+++ b/src/plugins/android.codeutils/src/com/motorola/studio/android/model/java/JavaClass.java
@@ -0,0 +1,426 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.motorola.studio.android.model.java;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.jdt.core.ToolFactory;
+import org.eclipse.jdt.core.dom.AST;
+import org.eclipse.jdt.core.dom.Annotation;
+import org.eclipse.jdt.core.dom.Block;
+import org.eclipse.jdt.core.dom.BodyDeclaration;
+import org.eclipse.jdt.core.dom.CompilationUnit;
+import org.eclipse.jdt.core.dom.Expression;
+import org.eclipse.jdt.core.dom.ImportDeclaration;
+import org.eclipse.jdt.core.dom.Javadoc;
+import org.eclipse.jdt.core.dom.MethodDeclaration;
+import org.eclipse.jdt.core.dom.MethodRef;
+import org.eclipse.jdt.core.dom.MethodRefParameter;
+import org.eclipse.jdt.core.dom.Modifier.ModifierKeyword;
+import org.eclipse.jdt.core.dom.PackageDeclaration;
+import org.eclipse.jdt.core.dom.PrimitiveType;
+import org.eclipse.jdt.core.dom.QualifiedName;
+import org.eclipse.jdt.core.dom.ReturnStatement;
+import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
+import org.eclipse.jdt.core.dom.TagElement;
+import org.eclipse.jdt.core.dom.Type;
+import org.eclipse.jdt.core.dom.TypeDeclaration;
+import org.eclipse.jdt.core.formatter.CodeFormatter;
+import org.eclipse.jdt.core.formatter.DefaultCodeFormatterConstants;
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.Document;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.text.edits.MalformedTreeException;
+import org.eclipse.text.edits.TextEdit;
+
+import com.motorola.studio.android.codeutils.i18n.CodeUtilsNLS;
+import com.motorola.studio.android.common.exception.AndroidException;
+import com.motorola.studio.android.common.log.StudioLogger;
+
+/**
+ * Abstract class that contains the basic structure to create java classes programatically
+ */
+public abstract class JavaClass
+{
+ protected static Annotation OVERRIDE_ANNOTATION;
+
+ static
+ {
+ AST tempAST = AST.newAST(AST.JLS3);
+
+ OVERRIDE_ANNOTATION = tempAST.newMarkerAnnotation();
+ OVERRIDE_ANNOTATION.setTypeName(tempAST.newSimpleName("Override"));
+
+ tempAST = null;
+ }
+
+ protected CompilationUnit compUnit;
+
+ protected AST ast;
+
+ protected TypeDeclaration classDecl = null;
+
+ protected IDocument document;
+
+ protected String className;
+
+ protected String[] packageName;
+
+ protected String[] superClass;
+
+ /**
+ * Class constructor. Creates the basic elements for the class:
+ * package name and class declaration based on a super class.
+ *
+ * @param className The simple class name
+ * @param packageName The full-qualified class package name
+ * @param superClass The full-qualified super class name
+ */
+ @SuppressWarnings("unchecked")
+ protected JavaClass(String className, String packageName, String superClass)
+ {
+ // It is expected that the parameters have been validated
+ // by the UI
+ Assert.isNotNull(className);
+ Assert.isNotNull(packageName);
+ Assert.isNotNull(superClass);
+
+ this.className = className;
+ this.packageName = getFQNAsArray(packageName);
+ this.superClass = getFQNAsArray(superClass);
+
+ // The package name must have two identifiers at least, according to
+ // the Android specifications
+ Assert.isLegal(packageName.length() > 1);
+ // So, the superclass must have at least two identifiers plus the name
+ Assert.isLegal(superClass.length() > 2);
+
+ ast = AST.newAST(AST.JLS3);
+ compUnit = ast.newCompilationUnit();
+
+ Type superClassType = null;
+
+ // Sets the package name to the class
+ PackageDeclaration pd = ast.newPackageDeclaration();
+ QualifiedName qPackageName =
+ ast.newQualifiedName(ast.newName(getQualifier(this.packageName)),
+ ast.newSimpleName(getName(this.packageName)));
+ pd.setName(qPackageName);
+ compUnit.setPackage(pd);
+
+ // Imports the super class
+ ImportDeclaration id = ast.newImportDeclaration();
+ id.setName(ast.newName(superClass));
+ compUnit.imports().add(id);
+ superClassType = ast.newSimpleType(ast.newName(getName(this.superClass)));
+
+ // Creates the class
+ classDecl = ast.newTypeDeclaration();
+ classDecl.modifiers().add(ast.newModifier(ModifierKeyword.PUBLIC_KEYWORD));
+ if (superClassType != null)
+ {
+ classDecl.setSuperclassType(superClassType);
+ }
+
+ classDecl.setName(ast.newSimpleName(className));
+ compUnit.types().add(classDecl);
+
+ document = new Document(compUnit.toString());
+ }
+
+ /**
+ * Gets the class content
+ *
+ * @return an IDocument object containing the class content
+ */
+ public IDocument getClassContent() throws AndroidException
+ {
+ String content = compUnit.toString();
+ document = new Document(content);
+
+ // Formats the code using the Eclipse settings
+ CodeFormatter codeFormatter =
+ ToolFactory.createCodeFormatter(DefaultCodeFormatterConstants
+ .getEclipseDefaultSettings());
+
+ TextEdit textEdit =
+ codeFormatter.format(CodeFormatter.K_COMPILATION_UNIT
+ | CodeFormatter.F_INCLUDE_COMMENTS, content, 0, content.length(), 0, null);
+
+ try
+ {
+ textEdit.apply(document);
+ }
+ catch (MalformedTreeException e)
+ {
+ String errMsg =
+ NLS.bind(CodeUtilsNLS.EXC_JavaClass_ErrorFormattingSourceCode, className);
+
+ StudioLogger.error(JavaClass.class, errMsg, e);
+ throw new AndroidException(errMsg);
+ }
+ catch (BadLocationException e)
+ {
+ String errMsg =
+ NLS.bind(CodeUtilsNLS.EXC_JavaClass_ErrorFormattingSourceCode, className);
+
+ StudioLogger.error(JavaClass.class, errMsg, e);
+ throw new AndroidException(errMsg);
+ }
+
+ addComments();
+
+ return document;
+ }
+
+ /**
+ * Adds comments to the code. As we cannot add comments when
+ * we are making the AST, comments need to be insert after the
+ * class creation
+ */
+ protected abstract void addComments() throws AndroidException;
+
+ /**
+ * Adds a parameter to a method
+ *
+ * @param method The method object
+ * @param parameterName The parameter name
+ * @param parameterType The parameter type (only the single name, not the qualified)
+ */
+ @SuppressWarnings("unchecked")
+ protected void addMethodParameter(MethodDeclaration method, String parameterName,
+ Type parameterType)
+ {
+ SingleVariableDeclaration vd = ast.newSingleVariableDeclaration();
+ vd.setName(ast.newSimpleName(parameterName));
+ vd.setType(parameterType);
+ method.parameters().add(vd);
+ }
+
+ /**
+ * Adds a comment to a BodyDeclaration object.
+ * For now, this method does nothing.
+ *
+ * @param element The element to add the comment
+ * @param comment The comment
+ */
+ //@SuppressWarnings("unchecked")
+ protected void addComment(BodyDeclaration element, String comment)
+ {
+ // TODO These comments will be reviewed for the Phase B
+ /*Javadoc javadoc = element.getJavadoc();
+ TextElement textElement = ast.newTextElement();
+ TagElement tagElement = ast.newTagElement();
+
+ if (javadoc == null)
+ {
+ javadoc = ast.newJavadoc();
+ element.setJavadoc(javadoc);
+ }
+
+ textElement.setText(comment);
+ tagElement.fragments().add(textElement);
+ javadoc.tags().add(tagElement);*/
+ }
+
+ /**
+ * Adds documentation reference to a method (the see tag to the javadoc)
+ *
+ * @param element The method declaration object
+ * @param qualifiedClassName The full qualified class name to refer
+ * @param methodName The method to refer
+ * @param parameters The method parameters
+ */
+ @SuppressWarnings("unchecked")
+ protected void addMethodReference(MethodDeclaration element, String qualifiedClassName,
+ String methodName, Type[] parameters)
+ {
+ String[] fqnArray = getFQNAsArray(qualifiedClassName);
+
+ MethodRef methodRef = ast.newMethodRef();
+ methodRef.setQualifier(ast.newQualifiedName(ast.newName(getQualifier(fqnArray)),
+ ast.newSimpleName(getName(fqnArray))));
+
+ methodRef.setName(ast.newSimpleName(methodName));
+
+ if ((parameters != null) && (parameters.length > 0))
+ {
+ for (Type param : parameters)
+ {
+ MethodRefParameter methodParam = ast.newMethodRefParameter();
+ methodParam.setType(param);
+ methodRef.parameters().add(methodParam);
+ }
+ }
+
+ Javadoc javadoc = element.getJavadoc();
+ TagElement tagElement = ast.newTagElement();
+ tagElement.setTagName(TagElement.TAG_SEE);
+
+ if (javadoc == null)
+ {
+ javadoc = ast.newJavadoc();
+ element.setJavadoc(javadoc);
+ }
+
+ tagElement.fragments().add(methodRef);
+ javadoc.tags().add(tagElement);
+ }
+
+ /**
+ * Adds an empty block to a method declaration
+ *
+ * @param method The method declaration
+ * @param returnType The method return type. If the method does not have one, use null
+ */
+ @SuppressWarnings("unchecked")
+ protected void addEmptyBlock(MethodDeclaration method)
+ {
+ Expression expression = null;
+ Block emptyBlock = ast.newBlock();
+ ReturnStatement returnStatement = ast.newReturnStatement();
+ Type returnType = method.getReturnType2();
+
+ if (returnType instanceof PrimitiveType)
+ {
+ PrimitiveType pType = (PrimitiveType) returnType;
+ if (pType.getPrimitiveTypeCode() == PrimitiveType.BOOLEAN)
+ {
+ expression = ast.newBooleanLiteral(false);
+ }
+ else if (pType.getPrimitiveTypeCode() != PrimitiveType.VOID)
+ {
+ expression = ast.newNumberLiteral("0");
+ }
+ }
+ else
+ {
+ expression = ast.newNullLiteral();
+ }
+
+ if (expression != null)
+ {
+ returnStatement.setExpression(expression);
+ emptyBlock.statements().add(returnStatement);
+ }
+
+ method.setBody(emptyBlock);
+ }
+
+ /**
+ * Creates a new string Type object
+ *
+ * @return a new string Type object
+ */
+ protected Type stringType()
+ {
+ return ast.newSimpleType(ast.newSimpleName(String.class.getSimpleName()));
+ }
+
+ /**
+ * Creates a new string array Type object
+ *
+ * @return a new string array Type object
+ */
+ protected Type stringArrayType()
+ {
+ return ast.newArrayType(ast.newSimpleType(ast.newName(String.class.getSimpleName())));
+ }
+
+ /**
+ * Creates a new int array Type object
+ *
+ * @return a new int array Type object
+ */
+ protected Type intArrayType()
+ {
+ return ast.newArrayType(ast.newPrimitiveType(PrimitiveType.INT));
+ }
+
+ /**
+ * Returns a full qualified class name as a array
+ *
+ * @param fqn The full qualified class name
+ * @return the full qualified class name as a array
+ */
+ protected static String[] getFQNAsArray(String fqn)
+ {
+ String[] parts;
+
+ if (fqn.contains("."))
+ {
+ parts = fqn.split("\\.");
+ }
+ else
+ {
+ parts = new String[]
+ {
+ fqn
+ };
+ }
+
+ return parts;
+ }
+
+ /**
+ * Retrieves the qualifier for a full qualified name.
+ * Example:
+ * com.motorola.studio.android.MyClass
+ *
+ * The qualifier for the class is com.motorola.studio.android
+ *
+ * @param qualifiedName The full qualified name
+ * @return The qualifier
+ */
+ protected static String[] getQualifier(String[] qualifiedName)
+ {
+ String[] qualifier;
+
+ if (qualifiedName.length > 1)
+ {
+ qualifier = new String[qualifiedName.length - 1];
+
+ System.arraycopy(qualifiedName, 0, qualifier, 0, qualifiedName.length - 1);
+ }
+ else
+ {
+ qualifier = qualifiedName;
+ }
+
+ return qualifier;
+ }
+
+ /**
+ * Gets the name part from a full qualified name
+ *
+ * @param qualifiedName The full qualified name
+ *
+ * @return The name part from a full qualified name
+ */
+ protected static String getName(String[] qualifiedName)
+ {
+ String name = null;
+
+ if ((qualifiedName != null) && (qualifiedName.length > 0))
+ {
+ name = qualifiedName[qualifiedName.length - 1];
+ }
+
+ return name;
+ }
+
+}
diff --git a/src/plugins/android.codeutils/src/com/motorola/studio/android/model/java/ServiceClass.java b/src/plugins/android.codeutils/src/com/motorola/studio/android/model/java/ServiceClass.java
new file mode 100644
index 0000000..75e6e60
--- /dev/null
+++ b/src/plugins/android.codeutils/src/com/motorola/studio/android/model/java/ServiceClass.java
@@ -0,0 +1,234 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.motorola.studio.android.model.java;
+
+import org.eclipse.jdt.core.dom.AST;
+import org.eclipse.jdt.core.dom.ASTNode;
+import org.eclipse.jdt.core.dom.ASTParser;
+import org.eclipse.jdt.core.dom.Block;
+import org.eclipse.jdt.core.dom.CompilationUnit;
+import org.eclipse.jdt.core.dom.ImportDeclaration;
+import org.eclipse.jdt.core.dom.MethodDeclaration;
+import org.eclipse.jdt.core.dom.Modifier.ModifierKeyword;
+import org.eclipse.jdt.core.dom.PrimitiveType;
+import org.eclipse.jdt.core.dom.Type;
+import org.eclipse.jdt.core.dom.TypeDeclaration;
+import org.eclipse.jdt.core.dom.rewrite.ASTRewrite;
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.text.edits.MalformedTreeException;
+import org.eclipse.text.edits.TextEdit;
+
+import com.motorola.studio.android.codeutils.i18n.CodeUtilsNLS;
+import com.motorola.studio.android.common.exception.AndroidException;
+import com.motorola.studio.android.common.log.StudioLogger;
+
+/**
+ * Class used to create an Android Service building block class
+ */
+public class ServiceClass extends JavaClass
+{
+ private static final String SERVICE_SUPERCLASS = "android.app.Service";
+
+ private static final String[] INTENT_CLASS = getFQNAsArray("android.content.Intent");
+
+ private static final String[] IBINDER_CLASS = getFQNAsArray("android.os.IBinder");
+
+ private static final String ONBIND_METHOD_NAME = "onBind";
+
+ private static final String ONCREATE_METHOD_NAME = "onCreate";
+
+ private static final String ONSTART_METHOD_NAME = "onStart";
+
+ private ASTRewrite rewrite = null;
+
+ /**
+ * The constructor
+ *
+ * @param className The simple class name
+ * @param packageName The full-qualified class package name
+ * @param addOnCreate If true, adds the OnCreate method to the service class
+ * @param addOnStart If true, adds the OnStart method to the service class
+ */
+ public ServiceClass(String className, String packageName, boolean addOnCreate,
+ boolean addOnStart)
+ {
+ super(className, packageName, SERVICE_SUPERCLASS);
+ addBasicServiceInfo();
+
+ if (addOnCreate)
+ {
+ addOnCreateMethod();
+ }
+
+ if (addOnStart)
+ {
+ addOnStartMethod();
+ }
+ }
+
+ /**
+ * Adds basic information to the service class
+ */
+ @SuppressWarnings("unchecked")
+ private void addBasicServiceInfo()
+ {
+ // Adds import for Intent
+ ImportDeclaration intentImport = ast.newImportDeclaration();
+ intentImport.setName(ast.newName(INTENT_CLASS));
+ compUnit.imports().add(intentImport);
+
+ // Adds import for IBinder
+ ImportDeclaration ibinderImport = ast.newImportDeclaration();
+ ibinderImport.setName(ast.newName(IBINDER_CLASS));
+ compUnit.imports().add(ibinderImport);
+
+ // Adds onBind method
+ MethodDeclaration onBindMethod = ast.newMethodDeclaration();
+ onBindMethod.modifiers().add(ast.newModifier(ModifierKeyword.PUBLIC_KEYWORD));
+ onBindMethod.setReturnType2(ast.newSimpleType(ast.newSimpleName(IBINDER_CLASS[2])));
+ onBindMethod.setName(ast.newSimpleName(ONBIND_METHOD_NAME));
+ addMethodParameter(onBindMethod, getName(INTENT_CLASS).toLowerCase(),
+ ast.newSimpleType(ast.newSimpleName(getName(INTENT_CLASS))));
+ addEmptyBlock(onBindMethod);
+ classDecl.bodyDeclarations().add(onBindMethod);
+
+ // Adds JavaDoc to elements
+ addComment(classDecl, CodeUtilsNLS.MODEL_ServiceClass_ServiceDescription);
+ addComment(onBindMethod, CodeUtilsNLS.MODEL_ServiceClass_OnBindMethodDescription);
+ addMethodReference(onBindMethod, SERVICE_SUPERCLASS, ONBIND_METHOD_NAME, new Type[]
+ {
+ ast.newSimpleType(ast.newSimpleName(getName(INTENT_CLASS)))
+ });
+ }
+
+ /**
+ * Adds the onCreate method to the service class
+ */
+ @SuppressWarnings("unchecked")
+ private void addOnCreateMethod()
+ {
+ MethodDeclaration onCreateMethod = ast.newMethodDeclaration();
+ onCreateMethod.modifiers().add(ast.newModifier(ModifierKeyword.PUBLIC_KEYWORD));
+ onCreateMethod.setReturnType2(ast.newPrimitiveType(PrimitiveType.VOID));
+ onCreateMethod.setName(ast.newSimpleName(ONCREATE_METHOD_NAME));
+ addEmptyBlock(onCreateMethod);
+ classDecl.bodyDeclarations().add(onCreateMethod);
+
+ // Adds JavaDoc to the method
+ addComment(onCreateMethod, CodeUtilsNLS.MODEL_ServiceClass_OnCreateMethodDescription);
+ addMethodReference(onCreateMethod, SERVICE_SUPERCLASS, ONCREATE_METHOD_NAME, null);
+ }
+
+ /**
+ * Adds the onStart method to the service class
+ */
+ @SuppressWarnings("unchecked")
+ private void addOnStartMethod()
+ {
+ MethodDeclaration onStartMethod = ast.newMethodDeclaration();
+ onStartMethod.modifiers().add(ast.newModifier(ModifierKeyword.PUBLIC_KEYWORD));
+ onStartMethod.setReturnType2(ast.newPrimitiveType(PrimitiveType.VOID));
+ onStartMethod.setName(ast.newSimpleName(ONSTART_METHOD_NAME));
+ addMethodParameter(onStartMethod, getName(INTENT_CLASS).toLowerCase(),
+ ast.newSimpleType(ast.newSimpleName(getName(INTENT_CLASS))));
+ addMethodParameter(onStartMethod, "startId", ast.newPrimitiveType(PrimitiveType.INT));
+ addEmptyBlock(onStartMethod);
+ classDecl.bodyDeclarations().add(onStartMethod);
+
+ // Adds JavaDoc to the method
+ addComment(onStartMethod, CodeUtilsNLS.MODEL_ServiceClass_OnStartMethodDescription);
+ addMethodReference(
+ onStartMethod,
+ SERVICE_SUPERCLASS,
+ ONSTART_METHOD_NAME,
+ new Type[]
+ {
+ ast.newSimpleType(ast.newSimpleName(getName(INTENT_CLASS))),
+ ast.newPrimitiveType(PrimitiveType.INT)
+ });
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.model.java.JavaClass#addComments()
+ */
+ @Override
+ protected void addComments() throws AndroidException
+ {
+ ASTNode todoComment;
+
+ ASTParser parser = ASTParser.newParser(AST.JLS3);
+ parser.setSource(document.get().toCharArray());
+
+ compUnit = (CompilationUnit) parser.createAST(null);
+ ast = compUnit.getAST();
+ rewrite = ASTRewrite.create(ast);
+
+ todoComment =
+ rewrite.createStringPlaceholder(CodeUtilsNLS.MODEL_Common_ToDoPutYourCodeHere,
+ ASTNode.EMPTY_STATEMENT);
+
+ TypeDeclaration serviceClass = (TypeDeclaration) compUnit.types().get(0);
+ MethodDeclaration method;
+ Block block;
+
+ // Adds the Override annotation and ToDo comment to all abstract methods
+ for (int i = 0; i < serviceClass.bodyDeclarations().size(); i++)
+ {
+ method = (MethodDeclaration) serviceClass.bodyDeclarations().get(i);
+
+ // Adds the Override annotation
+ rewrite.getListRewrite(method, method.getModifiersProperty()).insertFirst(
+ OVERRIDE_ANNOTATION, null);
+
+ // Adds the ToDo comment
+ block = method.getBody();
+ rewrite.getListRewrite(block, Block.STATEMENTS_PROPERTY).insertFirst(todoComment, null);
+ }
+
+ try
+ {
+ // Writes the modifications
+ TextEdit modifications = rewrite.rewriteAST(document, null);
+ modifications.apply(document);
+ }
+ catch (IllegalArgumentException e)
+ {
+ String errMsg =
+ NLS.bind(CodeUtilsNLS.EXC_JavaClass_ErrorApplyingCommentsToCode, className);
+
+ StudioLogger.error(ServiceClass.class, errMsg, e);
+ throw new AndroidException(errMsg);
+ }
+ catch (MalformedTreeException e)
+ {
+ String errMsg =
+ NLS.bind(CodeUtilsNLS.EXC_JavaClass_ErrorApplyingCommentsToCode, className);
+
+ StudioLogger.error(ServiceClass.class, errMsg, e);
+ throw new AndroidException(errMsg);
+ }
+ catch (BadLocationException e)
+ {
+ String errMsg =
+ NLS.bind(CodeUtilsNLS.EXC_JavaClass_ErrorApplyingCommentsToCode, className);
+
+ StudioLogger.error(ServiceClass.class, errMsg, e);
+ throw new AndroidException(errMsg);
+ }
+ }
+}
diff --git a/src/plugins/android.codeutils/src/com/motorola/studio/android/model/java/WidgetProviderClass.java b/src/plugins/android.codeutils/src/com/motorola/studio/android/model/java/WidgetProviderClass.java
new file mode 100644
index 0000000..11e36bb
--- /dev/null
+++ b/src/plugins/android.codeutils/src/com/motorola/studio/android/model/java/WidgetProviderClass.java
@@ -0,0 +1,284 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.motorola.studio.android.model.java;
+
+import org.eclipse.jdt.core.dom.AST;
+import org.eclipse.jdt.core.dom.ASTNode;
+import org.eclipse.jdt.core.dom.ASTParser;
+import org.eclipse.jdt.core.dom.Block;
+import org.eclipse.jdt.core.dom.CompilationUnit;
+import org.eclipse.jdt.core.dom.ImportDeclaration;
+import org.eclipse.jdt.core.dom.MethodDeclaration;
+import org.eclipse.jdt.core.dom.Modifier.ModifierKeyword;
+import org.eclipse.jdt.core.dom.PrimitiveType;
+import org.eclipse.jdt.core.dom.TypeDeclaration;
+import org.eclipse.jdt.core.dom.rewrite.ASTRewrite;
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.text.edits.MalformedTreeException;
+import org.eclipse.text.edits.TextEdit;
+
+import com.motorola.studio.android.codeutils.i18n.CodeUtilsNLS;
+import com.motorola.studio.android.common.exception.AndroidException;
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.model.WidgetProvider;
+
+/**
+ * Class used to create an Android Widget Provider building block class
+ */
+public class WidgetProviderClass extends JavaClass
+{
+
+ private static final String[] INTENT_CLASS = getFQNAsArray("android.content.Intent");
+
+ private static final String[] CONTEXT_CLASS = getFQNAsArray("android.content.Context");
+
+ private static final String[] APP_WIDGET_MANAGER_CLASS =
+ getFQNAsArray("android.appwidget.AppWidgetManager");
+
+ private static final String ON_UPDATE_METHOD_NAME = "onUpdate";
+
+ private static final String ON_DELETED_METHOD_NAME = "onDeleted";
+
+ private static final String ON_ENABLED_METHOD_NAME = "onEnabled";
+
+ private static final String ON_DISABLED_METHOD_NAME = "onDisabled";
+
+ private static final String ON_RECEIVE_METHOD_NAME = "onReceive";
+
+ private ASTRewrite rewrite = null;
+
+ /**
+ * The constructor
+ *
+ * @param className The simple class name
+ * @param packageName The full-qualified class package name
+ */
+ public WidgetProviderClass(String className, String packageName)
+ {
+ super(className, packageName, WidgetProvider.WIDGET_PROVIDER_SUPER_CLASS);
+
+ addBasicWidgetProviderInfo();
+ }
+
+ /**
+ * Adds basic information to the Widget Provider class
+ */
+ @SuppressWarnings("unchecked")
+ private void addBasicWidgetProviderInfo()
+ {
+ // Adds import declarations
+ ImportDeclaration intentImport = ast.newImportDeclaration();
+ intentImport.setName(ast.newName(INTENT_CLASS));
+ compUnit.imports().add(intentImport);
+
+ ImportDeclaration contextImport = ast.newImportDeclaration();
+ contextImport.setName(ast.newName(CONTEXT_CLASS));
+ compUnit.imports().add(contextImport);
+
+ ImportDeclaration widgetManagerImport = ast.newImportDeclaration();
+ widgetManagerImport.setName(ast.newName(APP_WIDGET_MANAGER_CLASS));
+ compUnit.imports().add(widgetManagerImport);
+
+ // Add override methods
+ addOnUpdateMethod();
+ addOnDeletedMethod();
+ addOnEnabledMethod();
+ addOnDisabledMethod();
+ addOnReceiveMethod();
+
+ }
+
+ /**
+ * Adds the onUpdate method to the widget provider class
+ */
+ @SuppressWarnings("unchecked")
+ private void addOnUpdateMethod()
+ {
+ final String WIDGET_IDS_PARAM = "appWidgetIds";
+
+ // Method declaration
+ MethodDeclaration onUpdateMethod = ast.newMethodDeclaration();
+ onUpdateMethod.modifiers().add(ast.newModifier(ModifierKeyword.PUBLIC_KEYWORD));
+ onUpdateMethod.setReturnType2(ast.newPrimitiveType(PrimitiveType.VOID));
+ onUpdateMethod.setName(ast.newSimpleName(ON_UPDATE_METHOD_NAME));
+
+ // Parameters
+ addMethodParameter(onUpdateMethod, getName(CONTEXT_CLASS).toLowerCase(),
+ ast.newSimpleType(ast.newSimpleName(getName(CONTEXT_CLASS))));
+ addMethodParameter(onUpdateMethod, getName(APP_WIDGET_MANAGER_CLASS).toLowerCase(),
+ ast.newSimpleType(ast.newSimpleName(getName(APP_WIDGET_MANAGER_CLASS))));
+ addMethodParameter(onUpdateMethod, WIDGET_IDS_PARAM, intArrayType());
+ addEmptyBlock(onUpdateMethod);
+ classDecl.bodyDeclarations().add(onUpdateMethod);
+
+ }
+
+ /**
+ * Adds the onDelete method to the widget provider class
+ */
+ @SuppressWarnings("unchecked")
+ private void addOnDeletedMethod()
+ {
+ final String WIDGET_IDS_PARAM = "appWidgetIds";
+
+ // Method declaration
+ MethodDeclaration onDeletedMethod = ast.newMethodDeclaration();
+ onDeletedMethod.modifiers().add(ast.newModifier(ModifierKeyword.PUBLIC_KEYWORD));
+ onDeletedMethod.setReturnType2(ast.newPrimitiveType(PrimitiveType.VOID));
+ onDeletedMethod.setName(ast.newSimpleName(ON_DELETED_METHOD_NAME));
+
+ // Parameters
+ addMethodParameter(onDeletedMethod, getName(CONTEXT_CLASS).toLowerCase(),
+ ast.newSimpleType(ast.newSimpleName(getName(CONTEXT_CLASS))));
+ addMethodParameter(onDeletedMethod, WIDGET_IDS_PARAM, intArrayType());
+ addEmptyBlock(onDeletedMethod);
+ classDecl.bodyDeclarations().add(onDeletedMethod);
+ }
+
+ /**
+ * Adds the onEnabled method to the widget provider class
+ */
+ @SuppressWarnings("unchecked")
+ private void addOnEnabledMethod()
+ {
+ // Method declaration
+ MethodDeclaration onEnabledMethod = ast.newMethodDeclaration();
+ onEnabledMethod.modifiers().add(ast.newModifier(ModifierKeyword.PUBLIC_KEYWORD));
+ onEnabledMethod.setReturnType2(ast.newPrimitiveType(PrimitiveType.VOID));
+ onEnabledMethod.setName(ast.newSimpleName(ON_ENABLED_METHOD_NAME));
+
+ // Parameters
+ addMethodParameter(onEnabledMethod, getName(CONTEXT_CLASS).toLowerCase(),
+ ast.newSimpleType(ast.newSimpleName(getName(CONTEXT_CLASS))));
+
+ addEmptyBlock(onEnabledMethod);
+ classDecl.bodyDeclarations().add(onEnabledMethod);
+ }
+
+ /**
+ * Adds the onDisabled method to the widget provider class
+ */
+ @SuppressWarnings("unchecked")
+ private void addOnDisabledMethod()
+ {
+ // Method declaration
+ MethodDeclaration onDisabledMethod = ast.newMethodDeclaration();
+ onDisabledMethod.modifiers().add(ast.newModifier(ModifierKeyword.PUBLIC_KEYWORD));
+ onDisabledMethod.setReturnType2(ast.newPrimitiveType(PrimitiveType.VOID));
+ onDisabledMethod.setName(ast.newSimpleName(ON_DISABLED_METHOD_NAME));
+
+ // Parameters
+ addMethodParameter(onDisabledMethod, getName(CONTEXT_CLASS).toLowerCase(),
+ ast.newSimpleType(ast.newSimpleName(getName(CONTEXT_CLASS))));
+
+ addEmptyBlock(onDisabledMethod);
+ classDecl.bodyDeclarations().add(onDisabledMethod);
+ }
+
+ /**
+ * Adds the onReceive method to the widget provider class
+ */
+ @SuppressWarnings("unchecked")
+ private void addOnReceiveMethod()
+ {
+ // Method declaration
+ MethodDeclaration onReceiveMethod = ast.newMethodDeclaration();
+ onReceiveMethod.modifiers().add(ast.newModifier(ModifierKeyword.PUBLIC_KEYWORD));
+ onReceiveMethod.setReturnType2(ast.newPrimitiveType(PrimitiveType.VOID));
+ onReceiveMethod.setName(ast.newSimpleName(ON_RECEIVE_METHOD_NAME));
+
+ // Parameters
+ addMethodParameter(onReceiveMethod, getName(CONTEXT_CLASS).toLowerCase(),
+ ast.newSimpleType(ast.newSimpleName(getName(CONTEXT_CLASS))));
+ addMethodParameter(onReceiveMethod, getName(INTENT_CLASS).toLowerCase(),
+ ast.newSimpleType(ast.newSimpleName(getName(INTENT_CLASS))));
+
+ addEmptyBlock(onReceiveMethod);
+ classDecl.bodyDeclarations().add(onReceiveMethod);
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.model.java.JavaClass#addComments()
+ */
+ @Override
+ protected void addComments() throws AndroidException
+ {
+ ASTNode todoComment;
+
+ ASTParser parser = ASTParser.newParser(AST.JLS3);
+ parser.setSource(document.get().toCharArray());
+
+ compUnit = (CompilationUnit) parser.createAST(null);
+ ast = compUnit.getAST();
+ rewrite = ASTRewrite.create(ast);
+
+ todoComment =
+ rewrite.createStringPlaceholder(CodeUtilsNLS.MODEL_Common_ToDoPutYourCodeHere,
+ ASTNode.EMPTY_STATEMENT);
+
+ TypeDeclaration widgetProviderClass = (TypeDeclaration) compUnit.types().get(0);
+ MethodDeclaration method;
+ Block block;
+
+ // Adds the Override annotation and ToDo comment to all overridden methods
+ for (int i = 0; i < widgetProviderClass.bodyDeclarations().size(); i++)
+ {
+ method = (MethodDeclaration) widgetProviderClass.bodyDeclarations().get(i);
+
+ // Adds the Override annotation
+ rewrite.getListRewrite(method, method.getModifiersProperty()).insertFirst(
+ OVERRIDE_ANNOTATION, null);
+
+ // Adds the ToDo comment
+ block = method.getBody();
+ rewrite.getListRewrite(block, Block.STATEMENTS_PROPERTY).insertFirst(todoComment, null);
+ }
+
+ try
+ {
+ // Writes the modifications
+ TextEdit modifications = rewrite.rewriteAST(document, null);
+ modifications.apply(document);
+ }
+ catch (IllegalArgumentException e)
+ {
+ String errMsg =
+ NLS.bind(CodeUtilsNLS.EXC_JavaClass_ErrorApplyingCommentsToCode, className);
+
+ StudioLogger.error(BroadcastReceiverClass.class, errMsg, e);
+ throw new AndroidException(errMsg);
+ }
+ catch (MalformedTreeException e)
+ {
+ String errMsg =
+ NLS.bind(CodeUtilsNLS.EXC_JavaClass_ErrorApplyingCommentsToCode, className);
+
+ StudioLogger.error(BroadcastReceiverClass.class, errMsg, e);
+ throw new AndroidException(errMsg);
+ }
+ catch (BadLocationException e)
+ {
+ String errMsg =
+ NLS.bind(CodeUtilsNLS.EXC_JavaClass_ErrorApplyingCommentsToCode, className);
+
+ StudioLogger.error(BroadcastReceiverClass.class, errMsg, e);
+ throw new AndroidException(errMsg);
+ }
+ }
+
+}
diff --git a/src/plugins/android.codeutils/src/com/motorola/studio/android/model/resources/ResourceFile.java b/src/plugins/android.codeutils/src/com/motorola/studio/android/model/resources/ResourceFile.java
new file mode 100644
index 0000000..e459476
--- /dev/null
+++ b/src/plugins/android.codeutils/src/com/motorola/studio/android/model/resources/ResourceFile.java
@@ -0,0 +1,335 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.motorola.studio.android.model.resources;
+
+import java.io.IOException;
+import java.io.StringWriter;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+
+import org.apache.xml.serialize.OutputFormat;
+import org.apache.xml.serialize.XMLSerializer;
+import org.eclipse.jface.text.IDocument;
+import org.w3c.dom.Attr;
+import org.w3c.dom.Document;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+
+import com.motorola.studio.android.codeutils.i18n.CodeUtilsNLS;
+import com.motorola.studio.android.common.exception.AndroidException;
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.model.resources.parser.AbstractResourceFileParser;
+import com.motorola.studio.android.model.resources.types.AbstractResourceNode;
+import com.motorola.studio.android.model.resources.types.AbstractResourceNode.NodeType;
+import com.motorola.studio.android.model.resources.types.AbstractSimpleNameResourceNode;
+import com.motorola.studio.android.model.resources.types.ResourcesNode;
+import com.motorola.studio.android.model.resources.types.UnknownNode;
+
+/**
+ * Class that represents a resource file
+ */
+@SuppressWarnings("deprecation")
+public class ResourceFile extends AbstractResourceFileParser
+{
+ /**
+ * Adds a resource entry to the resources file
+ *
+ * @param node The entry to be added
+ * @return true if the entry has been added or false otherwise
+ */
+ public boolean addResourceEntry(AbstractResourceNode node)
+ {
+ boolean added = false;
+
+ if (!rootNodes.contains(node))
+ {
+ rootNodes.add(node);
+ added = true;
+ }
+
+ return added;
+ }
+
+ /**
+ * Removes a resource entry from the resources file
+ *
+ * @param node the entry to be removed
+ * @return true if the entry has been removed or false otherwise
+ */
+ public boolean removeResourceEntry(AbstractResourceNode node)
+ {
+ boolean removed = false;
+
+ if (rootNodes.contains(node))
+ {
+ rootNodes.remove(node);
+ removed = true;
+ }
+
+ return removed;
+ }
+
+ /**
+ * Retrieves an array containing all root nodes of the resources file.
+ * If the file is well-formed, only the <resources> node must be present
+ * in the array.
+ *
+ * @return an array containing all root nodes of the resources file.
+ */
+ public AbstractResourceNode[] getResourceEntries()
+ {
+ AbstractResourceNode[] nodes = new AbstractResourceNode[rootNodes.size()];
+
+ nodes = rootNodes.toArray(nodes);
+
+ return nodes;
+ }
+
+ /**
+ * Retrieves the <resources> main node
+ *
+ * @return the <resources> main node or null if it does not exist.
+ */
+ public ResourcesNode getResourcesNode()
+ {
+ ResourcesNode resourcesNode = null;
+
+ for (AbstractResourceNode node : rootNodes)
+ {
+ if (node.getNodeType() == NodeType.Resources)
+ {
+ resourcesNode = (ResourcesNode) node;
+ break;
+ }
+ }
+
+ return resourcesNode;
+ }
+
+ /**
+ * Retrieves an IDocument object containing the xml content for the file
+ *
+ * @return an IDocument object containing the xml content for the file
+ */
+ public IDocument getContent() throws AndroidException
+ {
+ IDocument document = null;
+ DocumentBuilder documentBuilder = null;
+
+ try
+ {
+ documentBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
+ }
+ catch (ParserConfigurationException e)
+ {
+ StudioLogger.error(ResourceFile.class,
+ CodeUtilsNLS.EXC_ResourceFile_ErrorCreatingTheDocumentBuilder, e);
+ throw new AndroidException(
+ CodeUtilsNLS.EXC_ResourceFile_ErrorCreatingTheDocumentBuilder);
+ }
+
+ Document xmlDocument = documentBuilder.newDocument();
+
+ for (AbstractResourceNode node : rootNodes)
+ {
+ addNode(xmlDocument, null, node);
+ }
+
+ document = new org.eclipse.jface.text.Document(getXmlContent(xmlDocument));
+
+ return document;
+ }
+
+ /**
+ * Recursive function to build a XML file from AbstractResourceNode objects
+ *
+ * @param xmlDocument The XML Document
+ * @param xmlParentNode The XML parent node
+ * @param nodeToAdd The AndroidManifestNode to be added
+ */
+ private void addNode(Document xmlDocument, Node xmlParentNode, AbstractResourceNode nodeToAdd)
+ {
+ Node xmlNode = xmlDocument.createElement(nodeToAdd.getNodeName());
+ String[] attributes = nodeToAdd.getAttributes();
+ String[] unknownAttributes = nodeToAdd.getUnknownAttributes();
+ AbstractResourceNode[] children = nodeToAdd.getChildNodes();
+ AbstractResourceNode[] unknownChildren = nodeToAdd.getUnknownChildNodes();
+
+ // Sets the node value
+ if (nodeToAdd instanceof AbstractSimpleNameResourceNode)
+ {
+ AbstractSimpleNameResourceNode asnrNode = (AbstractSimpleNameResourceNode) nodeToAdd;
+ if (asnrNode.getNodeValue() != null)
+ {
+ xmlNode.appendChild(xmlDocument.createTextNode(asnrNode.getNodeValue()));
+ }
+ }
+ else if (nodeToAdd.getNodeType() == NodeType.Unknown)
+ {
+ UnknownNode unknownNode = (UnknownNode) nodeToAdd;
+
+ if (unknownNode.getNodeValue() != null)
+ {
+ xmlNode.appendChild(xmlDocument.createTextNode(unknownNode.getNodeValue()));
+ }
+ }
+
+ // Adds valid attributes
+ if (attributes.length > 0)
+ {
+ NamedNodeMap xmlAttributes = xmlNode.getAttributes();
+
+ for (String attrName : attributes)
+ {
+ Attr attr = xmlDocument.createAttribute(attrName);
+ attr.setValue(nodeToAdd.getAttributeValue(attrName));
+ xmlAttributes.setNamedItem(attr);
+ }
+ }
+
+ // Adds invalid attributes
+ if (unknownAttributes.length > 0)
+ {
+ NamedNodeMap xmlAttributes = xmlNode.getAttributes();
+
+ for (String attrName : unknownAttributes)
+ {
+ Attr attr = xmlDocument.createAttribute(attrName);
+ attr.setValue(nodeToAdd.getUnknownAttributeValue(attrName));
+ xmlAttributes.setNamedItem(attr);
+ }
+ }
+
+ // Adds known child nodes
+ for (AbstractResourceNode child : children)
+ {
+ addNode(xmlDocument, xmlNode, child);
+ }
+
+ // Adds unknown child nodes
+ for (AbstractResourceNode child : unknownChildren)
+ {
+ addNode(xmlDocument, xmlNode, child);
+ }
+
+ if (xmlParentNode == null)
+ {
+ xmlDocument.appendChild(xmlNode);
+ }
+ else
+ {
+ xmlParentNode.appendChild(xmlNode);
+ }
+ }
+
+ /**
+ * Creates the XML content from a XML Document
+ *
+ * @param xmlDocument The XML Document
+ * @return a String object containing the XML content
+ */
+ private String getXmlContent(Document xmlDocument) throws AndroidException
+ {
+ // Despite Xerces is deprecated, its formatted xml source output works
+ // better than W3C xml output classes
+ OutputFormat outputFormat = new OutputFormat();
+ XMLSerializer xmlSerializer = new XMLSerializer();
+ StringWriter writer = null;
+ String content = null;
+ try
+ {
+ writer = new StringWriter();
+
+ outputFormat.setEncoding("UTF-8");
+ outputFormat.setLineSeparator(System.getProperty("line.separator"));
+ outputFormat.setIndenting(true);
+
+ xmlSerializer.setOutputCharStream(writer);
+ xmlSerializer.setOutputFormat(outputFormat);
+
+ xmlSerializer.serialize(xmlDocument);
+ content = writer.toString();
+ }
+ catch (IOException e)
+ {
+ StudioLogger.error(ResourceFile.class,
+ CodeUtilsNLS.EXC_ResourceFile_ErrorFormattingTheXMLOutput, e);
+ throw new AndroidException(CodeUtilsNLS.EXC_ResourceFile_ErrorFormattingTheXMLOutput);
+ }
+ finally
+ {
+ if (writer != null)
+ {
+ try
+ {
+ writer.close();
+ }
+ catch (IOException e)
+ {
+ StudioLogger
+ .error("Could not close stream while retrieving resource xml content. "
+ + e.getMessage());
+ }
+ }
+ }
+
+ return content;
+ }
+
+ /**
+ * Returns a new resource name to create a new resource entry. This method grants that you are not
+ * creating a resource with a duplicated name.
+ *
+ * @param baseName The initial resource name
+ * @return The baseName value if a resource with this name does not exist or a new suggested name otherwise
+ */
+ public String getNewResourceName(String baseName)
+ {
+ int count = 0;
+ String newName = baseName;
+ boolean found = true;
+
+ if (getResourcesNode() != null)
+ {
+ while (found)
+ {
+ found = false;
+ for (AbstractResourceNode resNode : getResourcesNode().getChildNodes())
+ {
+ newName = baseName + (count == 0 ? "" : "_" + Integer.toString(count));
+
+ if (resNode instanceof AbstractSimpleNameResourceNode)
+ {
+ AbstractSimpleNameResourceNode validResNode =
+ (AbstractSimpleNameResourceNode) resNode;
+ if (validResNode.getName().equals(newName))
+ {
+ found = true;
+ count++;
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ return newName;
+ }
+
+}
diff --git a/src/plugins/android.codeutils/src/com/motorola/studio/android/model/resources/parser/AbstractResourceFileParser.java b/src/plugins/android.codeutils/src/com/motorola/studio/android/model/resources/parser/AbstractResourceFileParser.java
new file mode 100644
index 0000000..e2c7884
--- /dev/null
+++ b/src/plugins/android.codeutils/src/com/motorola/studio/android/model/resources/parser/AbstractResourceFileParser.java
@@ -0,0 +1,374 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.motorola.studio.android.model.resources.parser;
+
+import java.io.IOException;
+import java.io.StringReader;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.apache.xerces.parsers.DOMParser;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.osgi.util.NLS;
+import org.w3c.dom.Element;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.w3c.dom.Text;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+
+import com.motorola.studio.android.codeutils.i18n.CodeUtilsNLS;
+import com.motorola.studio.android.common.exception.AndroidException;
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.model.resources.types.AbstractResourceNode;
+import com.motorola.studio.android.model.resources.types.AbstractResourceNode.NodeType;
+import com.motorola.studio.android.model.resources.types.AbstractSimpleNameResourceNode;
+import com.motorola.studio.android.model.resources.types.ColorNode;
+import com.motorola.studio.android.model.resources.types.DimenNode;
+import com.motorola.studio.android.model.resources.types.DrawableNode;
+import com.motorola.studio.android.model.resources.types.IResourceTypesAttributes;
+import com.motorola.studio.android.model.resources.types.ResourcesNode;
+import com.motorola.studio.android.model.resources.types.StringNode;
+import com.motorola.studio.android.model.resources.types.UnknownNode;
+
+/**
+ * Abstract class that implements methods to parse a resource file
+ */
+public class AbstractResourceFileParser implements IResourceTypesAttributes
+{
+ /**
+ * The root nodes of the resource file
+ */
+ protected final List<AbstractResourceNode> rootNodes;
+
+ /**
+ * Default constructor
+ */
+ public AbstractResourceFileParser()
+ {
+ rootNodes = new LinkedList<AbstractResourceNode>();
+ }
+
+ /**
+ * Parses an IDocument object containing a resource file content
+ *
+ * @param document the IDocument object
+ * @param sourceFileName The resource file that is being parsed
+ *
+ * @throws SAXException When a parsing error occurs
+ * @throws IOException When a reading error occurs
+ */
+ public void parseDocument(IDocument document, String sourceFileName) throws AndroidException
+ {
+ Element element;
+ DOMParser domParser = new DOMParser();
+
+ rootNodes.clear();
+
+ StringReader stringReader = null;
+ try
+ {
+ stringReader = new StringReader(document.get());
+ domParser.parse(new InputSource(stringReader));
+ }
+ catch (SAXException e)
+ {
+ String errMsg =
+ NLS.bind(CodeUtilsNLS.EXC_AbstractResourceFileParser_ErrorParsingTheXMLFile,
+ sourceFileName, e.getLocalizedMessage());
+ StudioLogger.error(AbstractResourceFileParser.class, errMsg, e);
+ throw new AndroidException(errMsg);
+ }
+ catch (IOException e)
+ {
+ String errMsg =
+ NLS.bind(CodeUtilsNLS.EXC_AbstractResourceFileParser_ErrorReadingTheXMLContent,
+ sourceFileName, e.getLocalizedMessage());
+ StudioLogger.error(AbstractResourceFileParser.class, errMsg, e);
+ throw new AndroidException(errMsg);
+ }
+ finally
+ {
+ if (stringReader != null)
+ {
+ stringReader.close();
+ }
+ }
+
+ element = domParser.getDocument().getDocumentElement();
+ parseNode(element, null);
+ }
+
+ /**
+ * Parses a XML Node
+ *
+ * @param element The XML Node
+ * @param rootNode The XML Node parent (An AbstractResourceNode that has been parsed)
+ */
+ private void parseNode(Node node, AbstractResourceNode rootNode)
+ {
+ if (node instanceof Element)
+ {
+ Element element = (Element) node;
+ AbstractResourceNode arNode;
+ Node xmlNode;
+ NodeList xmlChildNodes;
+ NamedNodeMap attributes = element.getAttributes();
+ NodeType nodeType = identifyNode(element);
+
+ switch (nodeType)
+ {
+ case Resources:
+ arNode = parseResourcesNode();
+ break;
+ case String:
+ arNode = parseStringNode(attributes);
+ break;
+ case Color:
+ arNode = parseColorNode(attributes);
+ break;
+ case Dimen:
+ arNode = parseDimenNode(attributes);
+ break;
+ case Drawable:
+ arNode = parseDrawableNode(attributes);
+ break;
+ default:
+ arNode = parseUnknownNode(node);
+ }
+
+ // Adds the child nodes
+ xmlChildNodes = element.getChildNodes();
+
+ for (int i = 0; i < xmlChildNodes.getLength(); i++)
+ {
+ xmlNode = xmlChildNodes.item(i);
+ parseNode(xmlNode, arNode);
+ }
+
+ if (rootNode == null)
+ {
+ rootNodes.add(arNode);
+ }
+ else
+ {
+ rootNode.addChildNode(arNode);
+ }
+ }
+ else if ((node instanceof Text) && (rootNode != null))
+ {
+ if ((node.getNodeValue() != null) && (node.getNodeValue().trim().length() > 0))
+ {
+ if (rootNode instanceof AbstractSimpleNameResourceNode)
+ {
+ AbstractSimpleNameResourceNode asnrNode =
+ (AbstractSimpleNameResourceNode) rootNode;
+ if (asnrNode.getNodeValue() == null)
+ {
+ asnrNode.setNodeValue(node.getNodeValue());
+ }
+ }
+ else
+ {
+ UnknownNode unknownNode = (UnknownNode) rootNode;
+ if (unknownNode.getNodeValue() == null)
+ {
+ unknownNode.setNodeValue(node.getNodeValue());
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Identifies an XML Node type as an AbstractResourceNode type.
+ *
+ * @param xmlNode The XML Node
+ * @return The corresponding AndroidManifestNode type to the XML Node
+ */
+ private NodeType identifyNode(Element xmlNode)
+ {
+ String nodeName = xmlNode.getNodeName();
+ NodeType identifiedType = AbstractResourceNode.getNodeType(nodeName);
+ return identifiedType;
+ }
+
+ /**
+ * Parses a <resources> node
+ *
+ * @return An AbstractResourceNode object that represents the <resources> node
+ */
+ private AbstractResourceNode parseResourcesNode()
+ {
+ return new ResourcesNode();
+ }
+
+ /**
+ * Parses a <string> node
+ *
+ * @param attributes the node attributes list
+ * @return An AbstractResourceNode object that represents the <string> node
+ */
+ private AbstractResourceNode parseStringNode(NamedNodeMap attributes)
+ {
+ StringNode arNode = new StringNode("");
+ Node attribute;
+ String attrName, attrValue;
+
+ for (int i = 0; i < attributes.getLength(); i++)
+ {
+ attribute = attributes.item(i);
+
+ attrName = attribute.getNodeName();
+ attrValue = attribute.getNodeValue();
+
+ if (attrName.equalsIgnoreCase(ATTR_NAME))
+ {
+ arNode.setName(attrValue);
+ }
+ else
+ {
+ arNode.addUnknownAttribute(attrName, attrValue);
+ }
+ }
+
+ return arNode;
+ }
+
+ /**
+ * Parses a <color> node
+ *
+ * @param attributes the node attributes list
+ * @return An AbstractResourceNode object that represents the <color> node
+ */
+ private AbstractResourceNode parseColorNode(NamedNodeMap attributes)
+ {
+ ColorNode arNode = new ColorNode("");
+ Node attribute;
+ String attrName, attrValue;
+
+ for (int i = 0; i < attributes.getLength(); i++)
+ {
+ attribute = attributes.item(i);
+
+ attrName = attribute.getNodeName();
+ attrValue = attribute.getNodeValue();
+
+ if (attrName.equalsIgnoreCase(ATTR_NAME))
+ {
+ arNode.setName(attrValue);
+ }
+ else
+ {
+ arNode.addUnknownAttribute(attrName, attrValue);
+ }
+ }
+
+ return arNode;
+ }
+
+ /**
+ * Parses a <dimen> node
+ *
+ * @param attributes the node attributes list
+ * @return An AbstractResourceNode object that represents the <diment> node
+ */
+ private AbstractResourceNode parseDimenNode(NamedNodeMap attributes)
+ {
+ DimenNode arNode = new DimenNode("");
+ Node attribute;
+ String attrName, attrValue;
+
+ for (int i = 0; i < attributes.getLength(); i++)
+ {
+ attribute = attributes.item(i);
+
+ attrName = attribute.getNodeName();
+ attrValue = attribute.getNodeValue();
+
+ if (attrName.equalsIgnoreCase(ATTR_NAME))
+ {
+ arNode.setName(attrValue);
+ }
+ else
+ {
+ arNode.addUnknownAttribute(attrName, attrValue);
+ }
+ }
+
+ return arNode;
+ }
+
+ /**
+ * Parses a <drawable> node
+ *
+ * @param attributes the node attributes list
+ * @return An AbstractResourceNode object that represents the <drawable> node
+ */
+ private AbstractResourceNode parseDrawableNode(NamedNodeMap attributes)
+ {
+ DrawableNode arNode = new DrawableNode("");
+ Node attribute;
+ String attrName, attrValue;
+
+ for (int i = 0; i < attributes.getLength(); i++)
+ {
+ attribute = attributes.item(i);
+
+ attrName = attribute.getNodeName();
+ attrValue = attribute.getNodeValue();
+
+ if (attrName.equalsIgnoreCase(ATTR_NAME))
+ {
+ arNode.setName(attrValue);
+ }
+ else
+ {
+ arNode.addUnknownAttribute(attrName, attrValue);
+ }
+ }
+
+ return arNode;
+ }
+
+ /**
+ * Parses an unknown node
+ *
+ * @param node The xml node
+ * @return An AbstractResourceNode object that represents the unknown node
+ */
+ private AbstractResourceNode parseUnknownNode(Node node)
+ {
+ UnknownNode arNode = new UnknownNode(node.getNodeName());
+ NamedNodeMap attributes = node.getAttributes();
+ Node attribute;
+ String attrName, attrValue;
+
+ for (int i = 0; i < attributes.getLength(); i++)
+ {
+ attribute = attributes.item(i);
+
+ attrName = attribute.getNodeName();
+ attrValue = attribute.getNodeValue();
+
+ arNode.addUnknownAttribute(attrName, attrValue);
+ }
+
+ return arNode;
+ }
+}
diff --git a/src/plugins/android.codeutils/src/com/motorola/studio/android/model/resources/types/AbstractResourceNode.java b/src/plugins/android.codeutils/src/com/motorola/studio/android/model/resources/types/AbstractResourceNode.java
new file mode 100644
index 0000000..f6af472
--- /dev/null
+++ b/src/plugins/android.codeutils/src/com/motorola/studio/android/model/resources/types/AbstractResourceNode.java
@@ -0,0 +1,388 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.model.resources.types;
+
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Abstract class used to describe a node of a resource file
+ */
+public abstract class AbstractResourceNode
+{
+ /**
+ * Enumeration used to describe the resource node types
+ */
+ public static enum NodeType
+ {
+ Resources, String, Color, Dimen, Drawable, Unknown
+ }
+
+ /**
+ * The known attributes of a resource node
+ */
+ protected final Map<String, String> attributes;
+
+ /**
+ * The unknown attributes of a resource node
+ */
+ protected final Map<String, String> unknownAttributes;
+
+ /**
+ * The known child nodes of a resource
+ */
+ protected final List<AbstractResourceNode> children;
+
+ /**
+ * The unknown child nodes of a resource
+ */
+ protected final List<AbstractResourceNode> unknownChildren;
+
+ /**
+ * Checks if an attribute name is valid for a resource node
+ *
+ * @param attributeName The attribute name
+ *
+ * @return true if the attribute is acceptable for the node and false otherwise
+ */
+ protected abstract boolean isAttributeValid(String attributeName);
+
+ /**
+ * Retrieves the resource node type
+ *
+ * @return the resource node type
+ */
+ public abstract NodeType getNodeType();
+
+ /**
+ * Checks if a node can be accepted as child node
+ *
+ * @param node The node to be checked
+ *
+ * @return true if the node can be a child node or false otherwise
+ */
+ protected abstract boolean canAddChildNode(AbstractResourceNode node);
+
+ /**
+ * Retrieves the node type based on a node name
+ *
+ * @param nodeName the node name
+ *
+ * @return the node type related to the node name. If the name cannot be associated
+ * to any node type, the unknown type will be returned.
+ */
+ public static NodeType getNodeType(String nodeName)
+ {
+ NodeType nodeType = NodeType.Unknown;
+
+ for (NodeType type : NodeType.values())
+ {
+ if (nodeName.trim().equalsIgnoreCase(getNodeTypeName(type)))
+ {
+ nodeType = type;
+ break;
+ }
+ }
+
+ return nodeType;
+ }
+
+ /**
+ * Retrieves the node name based on the node type
+ *
+ * @param nodeType The node type
+ *
+ * @return The node name
+ */
+ public static String getNodeTypeName(NodeType nodeType)
+ {
+ String nodeName;
+
+ switch (nodeType)
+ {
+ case Resources:
+ nodeName = "resources";
+ break;
+ case String:
+ nodeName = "string";
+ break;
+ case Color:
+ nodeName = "color";
+ break;
+ case Dimen:
+ nodeName = "dimen";
+ break;
+ case Drawable:
+ nodeName = "drawable";
+ break;
+ default:
+ nodeName = "unknown";
+ }
+
+ return nodeName;
+ }
+
+ /**
+ * Default constructor
+ */
+ public AbstractResourceNode()
+ {
+ attributes = new HashMap<String, String>();
+ unknownAttributes = new HashMap<String, String>();
+ children = new LinkedList<AbstractResourceNode>();
+ unknownChildren = new LinkedList<AbstractResourceNode>();
+ }
+
+ /**
+ * Retrieves an attribute value from a resource node. The attribute must be valid.
+ *
+ * @param attributeName The attribute name
+ * @return The attribute value
+ */
+ public String getAttributeValue(String attributeName)
+ {
+ String attrValue = null;
+
+ if (attributeName != null)
+ {
+ attrValue = attributes.get(attributeName);
+ }
+
+ return attrValue;
+ }
+
+ /**
+ * Retrieves an array containing all known attributes of a resource node
+ *
+ * @return an array containing all known attributes of a resource node
+ */
+ public String[] getAttributes()
+ {
+ String[] attrs = new String[attributes.size()];
+
+ attrs = attributes.keySet().toArray(attrs);
+
+ return attrs;
+ }
+
+ /**
+ * Adds an unknown attribute to a resource node
+ *
+ * @param attributeName The attribute name
+ * @param attributeValue The attribute value
+ * @return true if the attribute has been added or false otherwise. An attribute is not
+ * added if it exists.
+ */
+ public boolean addUnknownAttribute(String attributeName, String attributeValue)
+ {
+ boolean added = false;
+
+ if (attributeName != null)
+ {
+ if (!isAttributeValid(attributeName))
+ {
+ if (!unknownAttributes.containsKey(attributeName))
+ {
+ unknownAttributes.put(attributeName, attributeValue);
+ added = true;
+ }
+ }
+ }
+
+ return added;
+ }
+
+ /**
+ * Removes an unknown attribute value from a resource node
+ *
+ * @param attributeName The attribute name
+ * @return true if the attribute has been removed or false otherwise.
+ */
+ public boolean removeUnknownAttribute(String attributeName)
+ {
+ boolean removed = false;
+
+ if (attributeName != null)
+ {
+ if (unknownAttributes.containsKey(attributeName))
+ {
+ unknownAttributes.remove(attributeName);
+ removed = true;
+ }
+ }
+
+ return removed;
+ }
+
+ /**
+ * Retrieves the value of an unknown attribute of a resource node
+ *
+ * @param attributeName The attribute name
+ * @return The attribute value
+ */
+ public String getUnknownAttributeValue(String attributeName)
+ {
+ String value = null;
+
+ if (attributeName != null)
+ {
+ if (unknownAttributes.containsKey(attributeName))
+ {
+ value = unknownAttributes.get(attributeName);
+ }
+ }
+
+ return value;
+ }
+
+ /**
+ * Retrieves an array containing all unknown attribute names
+ *
+ * @return an array containing all unknown attribute names
+ */
+ public String[] getUnknownAttributes()
+ {
+ String attributes[] = new String[this.unknownAttributes.size()];
+
+ attributes = this.unknownAttributes.keySet().toArray(attributes);
+
+ return attributes;
+ }
+
+ /**
+ * Removes all unknown attributes
+ */
+ public void clearUnknownAttributes()
+ {
+ unknownAttributes.clear();
+ }
+
+ /**
+ * Adds a child resource node to this node
+ *
+ * @param node The node to be added
+ * @return true if the node has been added or false otherwise
+ */
+ public boolean addChildNode(AbstractResourceNode node)
+ {
+ boolean added = false;
+
+ if (node != null)
+ {
+ if (canAddChildNode(node))
+ {
+ if (!children.contains(node))
+ {
+ children.add(node);
+ added = true;
+ }
+ }
+ else
+ {
+ if (!unknownChildren.contains(node))
+ {
+ unknownChildren.add(node);
+ added = true;
+ }
+ }
+ }
+
+ return added;
+ }
+
+ /**
+ * Removes a child node from this node
+ *
+ * @param node the node to be removed
+ * @return true if the node has been removed or false otherwise
+ */
+ public boolean removeChildNode(AbstractResourceNode node)
+ {
+ boolean removed = false;
+
+ if (node != null)
+ {
+ if (children.contains(node))
+ {
+ children.remove(node);
+ removed = true;
+ }
+ else if (unknownChildren.contains(node))
+ {
+ unknownChildren.remove(node);
+ removed = true;
+ }
+ }
+
+ return removed;
+ }
+
+ /**
+ * Retrieves an array containing all child nodes of this node
+ *
+ * @return an array containing all child nodes of this node
+ */
+ public AbstractResourceNode[] getChildNodes()
+ {
+ AbstractResourceNode[] childNodes = new AbstractResourceNode[children.size()];
+
+ childNodes = children.toArray(childNodes);
+
+ return childNodes;
+ }
+
+ /**
+ * Retrieves an array containing all unknown child nodes of this node
+ *
+ * @return an array containing all unknown child nodes of this node
+ */
+ public AbstractResourceNode[] getUnknownChildNodes()
+ {
+ AbstractResourceNode[] childNodes = new AbstractResourceNode[unknownChildren.size()];
+
+ childNodes = unknownChildren.toArray(childNodes);
+
+ return childNodes;
+ }
+
+ /**
+ * Removes all child nodes of this node
+ */
+ public void clearChildNodes()
+ {
+ children.clear();
+ }
+
+ /**
+ * Removes all unknown child nodes of this node
+ */
+ public void clearUnknownChildNodes()
+ {
+ unknownChildren.clear();
+ }
+
+ /**
+ * Retrieves the name of this node
+ *
+ * @return the name of this node
+ */
+ public String getNodeName()
+ {
+ return getNodeTypeName(getNodeType());
+ }
+}
diff --git a/src/plugins/android.codeutils/src/com/motorola/studio/android/model/resources/types/AbstractSimpleNameResourceNode.java b/src/plugins/android.codeutils/src/com/motorola/studio/android/model/resources/types/AbstractSimpleNameResourceNode.java
new file mode 100644
index 0000000..baa36b6
--- /dev/null
+++ b/src/plugins/android.codeutils/src/com/motorola/studio/android/model/resources/types/AbstractSimpleNameResourceNode.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.model.resources.types;
+
+import org.eclipse.core.runtime.Assert;
+
+/**
+ * Abstract class used to describe the behavior of resource entries on a resource file.
+ * Every resource entry (string, drawable, dimen and color) have the same format and
+ * an abstract class can describe their behavior:
+ *
+ * <resourcenode name="resource name">resource value</resourcenode>
+ */
+public abstract class AbstractSimpleNameResourceNode extends AbstractResourceNode implements
+ IResourceTypesAttributes
+{
+ /**
+ * The node value
+ */
+ protected String nodeValue;
+
+ /**
+ * Default constructor
+ *
+ * @param name The node name. It must not be null.
+ */
+ public AbstractSimpleNameResourceNode(String name)
+ {
+ Assert.isLegal(name != null);
+ attributes.put(ATTR_NAME, name);
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.model.resources.types.AbstractResourceNode#canAddChildNode(com.motorola.studio.android.model.resources.types.AbstractResourceNode)
+ */
+ @Override
+ protected boolean canAddChildNode(AbstractResourceNode node)
+ {
+ // No child nodes are allowed
+ return false;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.model.resources.types.AbstractResourceNode#isAttributeValid(java.lang.String)
+ */
+ @Override
+ protected boolean isAttributeValid(String attributeName)
+ {
+ return attributeName.equalsIgnoreCase(ATTR_NAME);
+ }
+
+ /**
+ * Retrieves the value of name property.
+ * <resourcenode name="resourceName">resource value</resourcenode>
+ *
+ * @return the value of name property
+ */
+ public String getName()
+ {
+ return attributes.get(ATTR_NAME);
+ }
+
+ /**
+ * Sets the value of name property.
+ * <resourcenode name="resourceName">resource value</resourcenode>
+ *
+ * @param name the value of name property
+ */
+ public void setName(String name)
+ {
+ Assert.isLegal(name != null);
+
+ attributes.put(ATTR_NAME, name);
+ }
+
+ /**
+ * Retrieves the resource value.
+ * <resourcenode name="resourceName">resource value</resourcenode>
+ *
+ * @return the resource value
+ */
+ public String getNodeValue()
+ {
+ return nodeValue;
+ }
+
+ /**
+ * Sets the resource value.
+ * <resourcenode name="resourceName">resource value</resourcenode>
+ *
+ * @param nodeValue the resource value
+ */
+ public void setNodeValue(String nodeValue)
+ {
+ this.nodeValue = nodeValue;
+ }
+}
diff --git a/src/plugins/android.codeutils/src/com/motorola/studio/android/model/resources/types/ColorNode.java b/src/plugins/android.codeutils/src/com/motorola/studio/android/model/resources/types/ColorNode.java
new file mode 100644
index 0000000..a9a502b
--- /dev/null
+++ b/src/plugins/android.codeutils/src/com/motorola/studio/android/model/resources/types/ColorNode.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.model.resources.types;
+
+/**
+ * Class that represents a <color> node on a resource file
+ *
+ * Format: <color name="ColorName">Color Value</color>
+ */
+public class ColorNode extends AbstractSimpleNameResourceNode
+{
+ /**
+ * Default constructor
+ *
+ * @param name The color name. It must not be null.
+ */
+ public ColorNode(String name)
+ {
+ super(name);
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.model.resources.types.AbstractResourceNode#getNodeType()
+ */
+ @Override
+ public NodeType getNodeType()
+ {
+ return NodeType.Color;
+ }
+}
diff --git a/src/plugins/android.codeutils/src/com/motorola/studio/android/model/resources/types/DimenNode.java b/src/plugins/android.codeutils/src/com/motorola/studio/android/model/resources/types/DimenNode.java
new file mode 100644
index 0000000..2ddf54a
--- /dev/null
+++ b/src/plugins/android.codeutils/src/com/motorola/studio/android/model/resources/types/DimenNode.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.model.resources.types;
+
+/**
+ * Class that represents a <dimen> node on a resource file
+ *
+ * Format: <dimen name="DimenName">Dimen Value</dimen>
+ */
+public class DimenNode extends AbstractSimpleNameResourceNode
+{
+ /**
+ * Default constructor
+ *
+ * @param name The dimen name. It must not be null.
+ */
+ public DimenNode(String name)
+ {
+ super(name);
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.model.resources.types.AbstractResourceNode#getNodeType()
+ */
+ @Override
+ public NodeType getNodeType()
+ {
+ return NodeType.Dimen;
+ }
+}
diff --git a/src/plugins/android.codeutils/src/com/motorola/studio/android/model/resources/types/DrawableNode.java b/src/plugins/android.codeutils/src/com/motorola/studio/android/model/resources/types/DrawableNode.java
new file mode 100644
index 0000000..c5fa561
--- /dev/null
+++ b/src/plugins/android.codeutils/src/com/motorola/studio/android/model/resources/types/DrawableNode.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.model.resources.types;
+
+/**
+ * Class that represents a <drawable> node on a resource file
+ *
+ * Format: <drawable name="DrawableName">Drawable value</drawable>
+ */
+public class DrawableNode extends AbstractSimpleNameResourceNode
+{
+ /**
+ * Default constructor
+ *
+ * @param name The drawable name. It must not be null.
+ */
+ public DrawableNode(String name)
+ {
+ super(name);
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.model.resources.types.AbstractResourceNode#getNodeType()
+ */
+ @Override
+ public NodeType getNodeType()
+ {
+ return NodeType.Drawable;
+ }
+}
diff --git a/src/plugins/android.codeutils/src/com/motorola/studio/android/model/resources/types/IResourceTypesAttributes.java b/src/plugins/android.codeutils/src/com/motorola/studio/android/model/resources/types/IResourceTypesAttributes.java
new file mode 100644
index 0000000..e0b2985
--- /dev/null
+++ b/src/plugins/android.codeutils/src/com/motorola/studio/android/model/resources/types/IResourceTypesAttributes.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.model.resources.types;
+
+/**
+ * Interface that contains the attribute names for resource nodes
+ */
+public interface IResourceTypesAttributes
+{
+ String ATTR_NAME = "name";
+}
diff --git a/src/plugins/android.codeutils/src/com/motorola/studio/android/model/resources/types/ResourcesNode.java b/src/plugins/android.codeutils/src/com/motorola/studio/android/model/resources/types/ResourcesNode.java
new file mode 100644
index 0000000..9a93387
--- /dev/null
+++ b/src/plugins/android.codeutils/src/com/motorola/studio/android/model/resources/types/ResourcesNode.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.model.resources.types;
+
+/**
+ * Class that represents a <resources> node on a resource file
+ *
+ * Format: <resources>
+ * <string ...>
+ * <color ...>
+ * <drawable ...>
+ * <dimen ...>
+ * </resources>
+ */
+public class ResourcesNode extends AbstractResourceNode
+{
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.model.resources.AbstractResourceNode#canAddChildNode(com.motorola.studio.android.model.resources.AbstractResourceNode)
+ */
+ @Override
+ protected boolean canAddChildNode(AbstractResourceNode node)
+ {
+ NodeType nodeType = node.getNodeType();
+ boolean canAdd =
+ (nodeType == NodeType.String) || (nodeType == NodeType.Color)
+ || (nodeType == NodeType.Dimen) || (nodeType == NodeType.Drawable);
+
+ return canAdd;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.model.resources.AbstractResourceNode#getNodeType()
+ */
+ @Override
+ public NodeType getNodeType()
+ {
+ return NodeType.Resources;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.model.resources.AbstractResourceNode#isAttributeValid(java.lang.String)
+ */
+ @Override
+ protected boolean isAttributeValid(String attributeName)
+ {
+ return false;
+ }
+
+}
diff --git a/src/plugins/android.codeutils/src/com/motorola/studio/android/model/resources/types/StringNode.java b/src/plugins/android.codeutils/src/com/motorola/studio/android/model/resources/types/StringNode.java
new file mode 100644
index 0000000..25c6b4c
--- /dev/null
+++ b/src/plugins/android.codeutils/src/com/motorola/studio/android/model/resources/types/StringNode.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.model.resources.types;
+
+/**
+ * Class that represents a <string> node on a resource file
+ *
+ * Format: <string name="StringName">String Value</string>
+ */
+public class StringNode extends AbstractSimpleNameResourceNode
+{
+ /**
+ * Default constructor
+ *
+ * @param name The string name. It must not be null.
+ */
+ public StringNode(String name)
+ {
+ super(name);
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.model.resources.AbstractResourceNode#getNodeType()
+ */
+ @Override
+ public NodeType getNodeType()
+ {
+ return NodeType.String;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.model.resources.types.AbstractSimpleNameResourceNode#setNodeValue(java.lang.String)
+ */
+ @Override
+ public void setNodeValue(String nodeValue)
+ {
+ super.setNodeValue(escapeString(nodeValue));
+ }
+
+ /**
+ * Escape invalid characters for the strings resource file
+ *
+ * @param str The string to be escaped
+ * @return The escaped string
+ */
+ private String escapeString(String str)
+ {
+ String newStr = str;
+
+ if (newStr != null)
+ {
+ newStr = newStr.replace("\\'", "'");
+ newStr = newStr.replace("\\@", "@");
+
+ newStr = newStr.replace("'", "\\'");
+ newStr = newStr.replace("@", "\\@");
+ }
+
+ return newStr;
+ }
+}
diff --git a/src/plugins/android.codeutils/src/com/motorola/studio/android/model/resources/types/UnknownNode.java b/src/plugins/android.codeutils/src/com/motorola/studio/android/model/resources/types/UnknownNode.java
new file mode 100644
index 0000000..c49d237
--- /dev/null
+++ b/src/plugins/android.codeutils/src/com/motorola/studio/android/model/resources/types/UnknownNode.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.model.resources.types;
+
+import org.eclipse.core.runtime.Assert;
+
+/**
+ * Class that represents an unknown node on a resource file
+ */
+public class UnknownNode extends AbstractResourceNode
+{
+ /**
+ * The node name: <nodename attrName="attrValue">nodeValue</nodename>
+ */
+ private String nodeName;
+
+ /**
+ * The node value: <nodename attrName="attrValue">nodeValue</nodename>
+ */
+ private String nodeValue;
+
+ /**
+ * Default constructor
+ *
+ * @param nodeName The node name. It must not be null.
+ */
+ public UnknownNode(String nodeName)
+ {
+ Assert.isLegal(nodeName != null);
+ this.nodeName = nodeName;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.model.resources.AbstractResourceNode#canAddChildNode(com.motorola.studio.android.model.resources.AbstractResourceNode)
+ */
+ @Override
+ protected boolean canAddChildNode(AbstractResourceNode node)
+ {
+ return true;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.model.resources.AbstractResourceNode#getNodeName()
+ */
+ @Override
+ public String getNodeName()
+ {
+ return nodeName;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.model.resources.AbstractResourceNode#getNodeType()
+ */
+ @Override
+ public NodeType getNodeType()
+ {
+ return NodeType.Unknown;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.model.resources.AbstractResourceNode#isAttributeValid(java.lang.String)
+ */
+ @Override
+ protected boolean isAttributeValid(String attributeName)
+ {
+ return false;
+ }
+
+ /**
+ * Sets the node value: <nodename attrName="attrValue">nodeValue</nodename>
+ *
+ * @param value The node value
+ */
+ public void setNodeValue(String value)
+ {
+ this.nodeValue = value;
+ }
+
+ /**
+ * Retrieves the node value: <nodename attrName="attrValue">nodeValue</nodename>
+ *
+ * @return The node value
+ */
+ public String getNodeValue()
+ {
+ return nodeValue;
+ }
+}
diff --git a/src/plugins/android.codeutils/src/com/motorola/studio/android/resources/AndroidProjectResources.java b/src/plugins/android.codeutils/src/com/motorola/studio/android/resources/AndroidProjectResources.java
new file mode 100644
index 0000000..6f46d67
--- /dev/null
+++ b/src/plugins/android.codeutils/src/com/motorola/studio/android/resources/AndroidProjectResources.java
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.resources;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.jface.text.IDocument;
+
+import com.motorola.studio.android.common.exception.AndroidException;
+import com.motorola.studio.android.common.utilities.FileUtil;
+import com.motorola.studio.android.model.resources.ResourceFile;
+import com.motorola.studio.android.model.resources.types.AbstractResourceNode.NodeType;
+
+/**
+ * Class used to deal with project resource files
+ */
+public class AndroidProjectResources
+{
+ /**
+ * The base path of resource files
+ */
+ private static final String BASE_DIR = "/res/values/";
+
+ /**
+ * The strings file
+ */
+ private static final String STRINGS_FILE = BASE_DIR + "strings.xml";
+
+ /**
+ * The colors file
+ */
+ private static final String COLORS_FILE = BASE_DIR + "colors.xml";
+
+ /**
+ * The drawables file
+ */
+ private static final String DRAWABLES_FILE = COLORS_FILE;
+
+ /**
+ * The dimensions file
+ */
+ private static final String DIMENS_FILE = BASE_DIR + "dimens.xml";
+
+ /**
+ * Prefix to refer a string on the AndroidManifest.xml file
+ */
+ public static final String STRING_CALL_PREFIX = "@string/";
+
+ /**
+ * Prefix to refer a drawable on the AndroidManifest.xml file
+ */
+ public static final String DRAWABLE_CALL_PREFIX = "@drawable/";
+
+ /**
+ * Prefix to refer a color on the AndroidManifest.xml file
+ */
+ public static final String COLOR_CALL_PREFIX = "@color/";
+
+ /**
+ * Prefix to refer a dimension on the AndroidManifest.xml file
+ */
+ public static final String DIMEN_CALL_PREFIX = "@dimen/";
+
+ /**
+ * Prefix to refer a file in the xml folder on the AndroidManifest.xml file
+ */
+ public static final String XML_CALL_PREFIX = "@xml/";
+
+ /**
+ * Gets the default file path for a resource type
+ *
+ * @param resourceType The resource type
+ * @return the default file path for a resource type
+ */
+ private static String getDefaultResourceFileLocation(NodeType resourceType)
+ {
+ String resLocation;
+
+ switch (resourceType)
+ {
+ case Color:
+ resLocation = COLORS_FILE;
+ break;
+ case Dimen:
+ resLocation = DIMENS_FILE;
+ break;
+ case Drawable:
+ resLocation = DRAWABLES_FILE;
+ break;
+ case String:
+ resLocation = STRINGS_FILE;
+ break;
+ default:
+ resLocation = null;
+ }
+
+ return resLocation;
+ }
+
+ /**
+ * Retrieves the ResourceFile object related to a resource file.
+ *
+ * @param project The project that contains the resource file
+ * @param resourceType The resource file type to be retrieved
+ * @return the ResourceFile object related to the selected resource file type.
+ * @throws CoreException
+ * @throws AndroidException
+ */
+ public static ResourceFile getResourceFile(IProject project, NodeType resourceType)
+ throws CoreException, AndroidException
+ {
+ Assert.isLegal(project != null);
+ Assert.isLegal(resourceType != null);
+
+ ResourceFile resourceFile = new ResourceFile();
+ IFile resFile = (IFile) project.findMember(getDefaultResourceFileLocation(resourceType));
+
+ if (resFile.exists())
+ {
+ IDocument document = FileUtil.readFile(resFile);
+ resourceFile.parseDocument(document, getDefaultResourceFileLocation(resourceType));
+ }
+
+ return resourceFile;
+ }
+
+ /**
+ * Saves a ResourceFile object to a resource file in a project
+ *
+ * @param project The resource file project
+ * @param resourceFile The resource file content (a ResourceFile object)
+ * @param resourceType The resource type
+ * @throws CoreException
+ * @throws AndroidException
+ */
+ public static void saveResourceFile(IProject project, ResourceFile resourceFile,
+ NodeType resourceType) throws CoreException, AndroidException
+ {
+ Assert.isLegal(project != null);
+ Assert.isLegal(resourceFile != null);
+ Assert.isLegal(resourceType != null);
+
+ final String UTF8_ENCODING = "UTF-8";
+
+ IFile resFile = (IFile) project.findMember(getDefaultResourceFileLocation(resourceType));
+
+ FileUtil.saveFile(resFile, resourceFile.getContent(), UTF8_ENCODING, true);
+ }
+}
diff --git a/src/plugins/android.codeutils/src/com/motorola/studio/android/wizards/buildingblocks/ActivitySampleSelectionPage.java b/src/plugins/android.codeutils/src/com/motorola/studio/android/wizards/buildingblocks/ActivitySampleSelectionPage.java
new file mode 100644
index 0000000..fd53350
--- /dev/null
+++ b/src/plugins/android.codeutils/src/com/motorola/studio/android/wizards/buildingblocks/ActivitySampleSelectionPage.java
@@ -0,0 +1,364 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.motorola.studio.android.wizards.buildingblocks;
+
+import org.eclipse.jface.dialogs.IPageChangeProvider;
+import org.eclipse.jface.dialogs.IPageChangedListener;
+import org.eclipse.jface.dialogs.PageChangedEvent;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.jface.viewers.ArrayContentProvider;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.ITreeContentProvider;
+import org.eclipse.jface.viewers.LabelProvider;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+import org.eclipse.jface.viewers.TreeViewer;
+import org.eclipse.jface.viewers.ViewerComparator;
+import org.eclipse.jface.wizard.IWizardPage;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.ScrolledComposite;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Group;
+import org.eclipse.swt.widgets.Label;
+
+import com.motorola.studio.android.codeutils.CodeUtilsActivator;
+import com.motorola.studio.android.codeutils.i18n.CodeUtilsNLS;
+import com.motorola.studio.android.common.exception.AndroidException;
+import com.motorola.studio.android.common.utilities.AndroidUtils;
+import com.motorola.studio.android.model.ActivityBasedOnTemplate;
+
+/**
+ * New activity wizard sample page.
+ */
+public class ActivitySampleSelectionPage extends NewLauncherWizardPage
+{
+ private static final String ANDROID_LOGO_ICON_PATH = "icons/obj16/androidLogo.png";
+
+ private TreeViewer treeViewer;
+
+ private Label descriptionLabel;
+
+ private String content[];
+
+ private boolean canFlip = false;
+
+ private static final String NEW_ACTIVITY_BASED_ON_TEMPLATE_HELP = CodeUtilsActivator.PLUGIN_ID
+ + ".new-activity-based-on-template"; //$NON-NLS-1$
+
+ private static Image androidImg = null;
+
+ public static final String PAGE_NAME = "Samples Page";
+
+ /*
+ * Listener to update description pane whenever this page is open
+ */
+ private class PageChangeListener implements IPageChangedListener
+ {
+ public void pageChanged(PageChangedEvent event)
+ {
+ if ((event.getSelectedPage() == ActivitySampleSelectionPage.this))
+ {
+ ActivitySampleSelectionPage.this.getControl().update();
+ ((ActivitySampleSelectionPage) event.getSelectedPage()).updateDescriptionPane();
+ }
+ }
+ }
+
+ /**
+ * Create a new wizard page based on activity samples.
+ * @param activity The building block model to be used in the wizard page.
+ * */
+ protected ActivitySampleSelectionPage(ActivityBasedOnTemplate activity)
+ {
+ super(activity, PAGE_NAME);
+
+ activity.evaluateSamplesList(ActivityBasedOnTemplate.SAMPLE_CATEGORY.SAMPLE_ACTIVITIES_CATEGORY);
+
+ ImageDescriptor imgDescr =
+ CodeUtilsActivator.imageDescriptorFromPlugin(CodeUtilsActivator.PLUGIN_ID,
+ ANDROID_LOGO_ICON_PATH);
+ if (imgDescr != null)
+ {
+ androidImg = imgDescr.createImage();
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ * */
+ @Override
+ public boolean canFlipToNextPage()
+ {
+
+ return canFlip;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.wizards.buildingblocks.NewBuildingBlocksWizard#getBuildBlock()
+ */
+ @Override
+ public ActivityBasedOnTemplate getBuildBlock()
+ {
+ return (ActivityBasedOnTemplate) super.getBuildBlock();
+ }
+
+ @Override
+ public IWizardPage getNextPage()
+ {
+ String selection =
+ treeViewer.getSelection() != null ? treeViewer.getSelection().toString() : null;
+ selection = selection != null ? selection.substring(1, selection.length() - 1) : null;
+
+ if ((selection != null)
+ && selection
+ .equalsIgnoreCase(ActivityBasedOnTemplate.LIST_ACTIVITIES_SAMPLE_LOCALIZED))
+ {
+ return this.getWizard().getPage(NewActivityWizardListTemplatesPage.PAGE_NAME);
+ }
+ return this.getWizard().getPage(CodeUtilsNLS.UI_NewActivityMainPage_PageTitle);
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.wizards.buildingblocks.NewBuildingBlocksWizardPage#getMethods()
+ */
+ @Override
+ protected Method[] getMethods()
+ {
+ Method onCreateMethod = new Method(getBuildBlock().getOnStartMessage())
+ {
+ @Override
+ public void handle(boolean selection)
+ {
+ getBuildBlock().setOnStart(selection);
+ }
+ };
+ return new Method[]
+ {
+ onCreateMethod
+ };
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.wizards.buildingblocks.NewBuildingBlocksWizardPage#createIntermediateControls(org.eclipse.swt.widgets.Composite)
+ */
+ @Override
+ protected void createExtendedControls(Composite parent)
+ {
+ Composite mainComposite = new Composite(parent, SWT.FILL);
+ mainComposite.setLayout(new GridLayout(1, false));
+ mainComposite.setLayoutData(new GridData(GridData.FILL_BOTH));
+
+ // Samples Tree Label
+ Label itemsTableLabel = new Label(mainComposite, SWT.NONE);
+ itemsTableLabel.setText(CodeUtilsNLS.UI_SampleSelectionPage_SamplesTreeLabel);
+
+ // Samples Tree Viewer
+ treeViewer = new TreeViewer(mainComposite, SWT.BORDER | SWT.SINGLE | SWT.V_SCROLL);
+ treeViewer.getControl().setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+ treeViewer.setLabelProvider(new LabelProvider()
+ {
+ @Override
+ public Image getImage(Object obj)
+ {
+ return androidImg;
+ }
+
+ @Override
+ public String getText(Object element)
+ {
+ return element.toString();
+ }
+ });
+
+ content = new String[getBuildBlock().getAvailableSamples().size()];
+
+ int i = 0;
+ for (String currentSample : getBuildBlock().getAvailableSamples().keySet())
+ {
+ content[i] = currentSample;
+ i++;
+ }
+
+ treeViewer.setContentProvider(new SampleTreeContentProvider(content));
+ treeViewer.setInput(content);
+
+ final Group intentFilterGroup = new Group(mainComposite, SWT.NONE);
+
+ treeViewer.addSelectionChangedListener(new ISelectionChangedListener()
+ {
+ public void selectionChanged(SelectionChangedEvent e)
+ {
+ String selection = e.getSelection().toString();
+ getBuildBlock().setSample(selection.substring(1, selection.length() - 1));
+
+ getBuildBlock().setSampleCategoty(
+ ActivityBasedOnTemplate.SAMPLE_CATEGORY.SAMPLE_ACTIVITIES_CATEGORY);
+
+ if (selection.substring(1, selection.length() - 1).equals(
+ ActivityBasedOnTemplate.DATABASE_LIST_SAMPLE_LOCALIZED))
+ {
+ getBuildBlock().setDatabaseTemplateSelected(true);
+ }
+ else
+ {
+ getBuildBlock().setDatabaseTemplateSelected(false);
+ }
+
+ canFlip = true;
+
+ updateDescriptionPane();
+ getWizard().getContainer().updateButtons();
+ }
+ });
+
+ treeViewer.setComparator(new ViewerComparator());
+
+ treeViewer.expandAll();
+
+ intentFilterGroup.setText(CodeUtilsNLS.UI_SampleSelectionPage_SamplesDescriptionPane);
+ intentFilterGroup.setLayout(new GridLayout(1, false));
+ intentFilterGroup
+ .setLayoutData(new GridData(GridData.FILL, GridData.BEGINNING, true, false));
+
+ ScrolledComposite scrolledArea = new ScrolledComposite(intentFilterGroup, SWT.V_SCROLL);
+ GridData descriptionLabelData = new GridData(GridData.FILL, GridData.FILL, true, true);
+ descriptionLabelData.heightHint = 140;
+ scrolledArea.setLayoutData(descriptionLabelData);
+
+ descriptionLabel = new Label(scrolledArea, SWT.FILL | SWT.WRAP);
+ descriptionLabel.setText("");
+ scrolledArea.setContent(descriptionLabel);
+ descriptionLabel.setSize(descriptionLabel.computeSize(SWT.DEFAULT, SWT.DEFAULT));
+
+ // Add a listener to the wizard to listen for page changes
+ if (getContainer() instanceof IPageChangeProvider)
+ {
+ ((IPageChangeProvider) getContainer()).addPageChangedListener(new PageChangeListener());
+ }
+ setControl(mainComposite);
+ }
+
+ /*
+ * Updates selected label description.
+ */
+ private void updateDescriptionPane()
+ {
+ descriptionLabel.setText(getBuildBlock().getSampleDescription());
+ // descriptionLabel.setVisible(true);
+ descriptionLabel.setSize(descriptionLabel.computeSize(SWT.DEFAULT, SWT.DEFAULT));
+ descriptionLabel.update();
+ descriptionLabel.getParent().update();
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.wizards.buildingblocks.NewBuildingBlocksWizardPage#getDefaultMessage()
+ */
+ @Override
+ public String getDefaultMessage()
+ {
+ return CodeUtilsNLS.UI_NewActivityMainPage_DescriptionCreateActivityBasedOnTemplate;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.wizards.buildingblocks.NewBuildingBlocksWizardPage#getWizardTitle()
+ */
+ @Override
+ public String getWizardTitle()
+ {
+ return CodeUtilsNLS.UI_NewActivityMainPage_TitleActivityBasedOnTemplate;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.wizards.buildingblocks.NewLauncherWizardPage#getIntentFiltersActions()
+ */
+ @Override
+ protected String[] getIntentFiltersActions()
+ {
+ String[] intentFiltersActions = new String[0];
+ try
+ {
+ intentFiltersActions = AndroidUtils.getActivityActions(getBuildBlock().getProject());
+ }
+ catch (AndroidException e)
+ {
+ setErrorMessage(e.getMessage());
+ }
+ return intentFiltersActions;
+ }
+
+ /**
+ * Gets the help ID to be used for attaching
+ * context sensitive help.
+ *
+ * Classes that extends this class and want to set
+ * their on help should override this method
+ */
+ @Override
+ protected String getHelpId()
+ {
+ return NEW_ACTIVITY_BASED_ON_TEMPLATE_HELP;
+ }
+
+ /**
+ * Returns true if page has header false otherwise
+ *
+ * @return true if page has header false otherwise
+ */
+ @Override
+ public boolean hasHeader()
+ {
+ return false;
+ }
+}
+
+/**
+ * Fills tree viewer with sample options
+ */
+class SampleTreeContentProvider extends ArrayContentProvider implements ITreeContentProvider
+{
+
+ Object[] elements;
+
+ public SampleTreeContentProvider(Object[] elements)
+ {
+ this.elements = elements;
+ }
+
+ @Override
+ public Object[] getElements(Object inputElement)
+ {
+ return elements;
+ }
+
+ public Object[] getChildren(Object parentElement)
+ {
+ return new Object[0];
+ }
+
+ public Object getParent(Object element)
+ {
+ return new Object[0];
+ }
+
+ public boolean hasChildren(Object element)
+ {
+ return false;
+ }
+} \ No newline at end of file
diff --git a/src/plugins/android.codeutils/src/com/motorola/studio/android/wizards/buildingblocks/ElementTreeContentProvider.java b/src/plugins/android.codeutils/src/com/motorola/studio/android/wizards/buildingblocks/ElementTreeContentProvider.java
new file mode 100644
index 0000000..b3d00a4
--- /dev/null
+++ b/src/plugins/android.codeutils/src/com/motorola/studio/android/wizards/buildingblocks/ElementTreeContentProvider.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.wizards.buildingblocks;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.jdt.core.IJavaModel;
+import org.eclipse.jdt.core.IJavaProject;
+import org.eclipse.jdt.core.JavaModelException;
+import org.eclipse.jdt.ui.StandardJavaElementContentProvider;
+
+import com.motorola.studio.android.common.IAndroidConstants;
+import com.motorola.studio.android.common.log.StudioLogger;
+
+/**
+ * Class that implements a Content Provider for the project selection
+ * on the New Project Wizard
+ */
+class ElementTreeContentProvider extends StandardJavaElementContentProvider
+{
+ /* (non-Javadoc)
+ * @see org.eclipse.jdt.ui.StandardJavaElementContentProvider#getJavaProjects(org.eclipse.jdt.core.IJavaModel)
+ */
+ @Override
+ protected Object[] getJavaProjects(IJavaModel jm) throws JavaModelException
+ {
+ Object[] javaProjects = super.getJavaProjects(jm);
+ List<Object> androidProjects = new ArrayList<Object>();
+ for (Object obj : javaProjects)
+ {
+ try
+ {
+ if ((obj instanceof IJavaProject)
+ && ((IJavaProject) obj).getProject().hasNature(
+ IAndroidConstants.ANDROID_NATURE))
+ {
+ androidProjects.add(obj);
+ }
+ }
+ catch (CoreException ce)
+ {
+ StudioLogger.error(ElementTreeContentProvider.class, ce.getLocalizedMessage(), ce);
+ }
+
+ }
+ return androidProjects.toArray();
+ }
+}
diff --git a/src/plugins/android.codeutils/src/com/motorola/studio/android/wizards/buildingblocks/ElementTreeValidator.java b/src/plugins/android.codeutils/src/com/motorola/studio/android/wizards/buildingblocks/ElementTreeValidator.java
new file mode 100644
index 0000000..29b6c1a
--- /dev/null
+++ b/src/plugins/android.codeutils/src/com/motorola/studio/android/wizards/buildingblocks/ElementTreeValidator.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.wizards.buildingblocks;
+
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.jdt.core.IJavaProject;
+import org.eclipse.jdt.core.IPackageFragmentRoot;
+import org.eclipse.jdt.core.JavaModelException;
+import org.eclipse.jdt.internal.ui.wizards.TypedElementSelectionValidator;
+
+import com.motorola.studio.android.common.IAndroidConstants;
+import com.motorola.studio.android.common.log.StudioLogger;
+
+/**
+ * Class that implements a Validator for the project/package selection
+ * on the New Project Wizard
+ */
+@SuppressWarnings("restriction")
+class ElementTreeValidator extends TypedElementSelectionValidator
+{
+ private static Class<?>[] acceptedClasses = new Class[]
+ {
+ IPackageFragmentRoot.class, IJavaProject.class
+ };
+
+ public ElementTreeValidator()
+ {
+ super(acceptedClasses, false);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jdt.internal.ui.wizards.TypedElementSelectionValidator#isSelectedValid(java.lang.Object)
+ */
+ @Override
+ public boolean isSelectedValid(Object element)
+ {
+ boolean isValid = false;
+ try
+ {
+ if (element instanceof IJavaProject)
+ {
+ IJavaProject jproject = (IJavaProject) element;
+ IPath path = jproject.getProject().getFullPath();
+ isValid = (jproject.findPackageFragmentRoot(path) != null);
+ }
+ else if (element instanceof IPackageFragmentRoot)
+ {
+ IPackageFragmentRoot packageFragmentRoot = (IPackageFragmentRoot) element;
+
+ boolean isSrc = (packageFragmentRoot.getKind() == IPackageFragmentRoot.K_SOURCE);
+ boolean isGen =
+ packageFragmentRoot.getElementName().equals(
+ IAndroidConstants.GEN_SRC_FOLDER)
+ && (packageFragmentRoot.getParent() instanceof IJavaProject);
+
+ isValid = isSrc && !isGen;
+ }
+ else
+ {
+ isValid = true;
+ }
+ }
+ catch (JavaModelException e)
+ {
+ StudioLogger.error(ElementTreeValidator.class, e.getLocalizedMessage(), e);
+ }
+ return isValid;
+ }
+}
diff --git a/src/plugins/android.codeutils/src/com/motorola/studio/android/wizards/buildingblocks/ElementTreeViewFilter.java b/src/plugins/android.codeutils/src/com/motorola/studio/android/wizards/buildingblocks/ElementTreeViewFilter.java
new file mode 100644
index 0000000..cc9f9e6
--- /dev/null
+++ b/src/plugins/android.codeutils/src/com/motorola/studio/android/wizards/buildingblocks/ElementTreeViewFilter.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.wizards.buildingblocks;
+
+import org.eclipse.jdt.core.IJavaModel;
+import org.eclipse.jdt.core.IJavaProject;
+import org.eclipse.jdt.core.IPackageFragmentRoot;
+import org.eclipse.jdt.core.JavaModelException;
+import org.eclipse.jdt.internal.ui.wizards.TypedViewerFilter;
+import org.eclipse.jface.viewers.Viewer;
+
+import com.motorola.studio.android.common.log.StudioLogger;
+
+/**
+ * Class that implements a View Filter for the package selection
+ * on the New Project Wizard
+ */
+@SuppressWarnings("restriction")
+class ElementTreeViewFilter extends TypedViewerFilter
+{
+ private static Class<?>[] acceptedClasses = new Class[]
+ {
+ IJavaModel.class, IPackageFragmentRoot.class, IJavaProject.class
+ };
+
+ /**
+ * Default constructor
+ */
+ public ElementTreeViewFilter()
+ {
+ super(acceptedClasses);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jdt.internal.ui.wizards.TypedViewerFilter#select(org.eclipse.jface.viewers.Viewer, java.lang.Object, java.lang.Object)
+ */
+ @Override
+ public boolean select(Viewer viewer, Object parent, Object element)
+ {
+ boolean select = false;
+ if (element instanceof IPackageFragmentRoot)
+ {
+ try
+ {
+ select =
+ (((IPackageFragmentRoot) element).getKind() == IPackageFragmentRoot.K_SOURCE);
+ }
+ catch (JavaModelException e)
+ {
+ StudioLogger.error(ElementTreeViewFilter.class, e.getLocalizedMessage(), e);
+ }
+ }
+ else
+ {
+ select = super.select(viewer, parent, element);
+ }
+ return select;
+ }
+}
diff --git a/src/plugins/android.codeutils/src/com/motorola/studio/android/wizards/buildingblocks/FilteredActionsSelectionDialog.java b/src/plugins/android.codeutils/src/com/motorola/studio/android/wizards/buildingblocks/FilteredActionsSelectionDialog.java
new file mode 100644
index 0000000..5399ab3
--- /dev/null
+++ b/src/plugins/android.codeutils/src/com/motorola/studio/android/wizards/buildingblocks/FilteredActionsSelectionDialog.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.wizards.buildingblocks;
+
+import java.util.Comparator;
+import java.util.Set;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.jface.dialogs.IDialogSettings;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.ui.dialogs.FilteredItemsSelectionDialog;
+
+import com.motorola.studio.android.codeutils.CodeUtilsActivator;
+
+/**
+ * Class that implements a filter dialog to be used by NewLauncherWizardPage class
+ */
+class FilteredActionsSelectionDialog extends FilteredItemsSelectionDialog
+{
+ private final Set<String> categorySet;
+
+ public FilteredActionsSelectionDialog(Shell shell, Set<String> categorySet)
+ {
+ super(shell, true);
+ this.categorySet = categorySet;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.dialogs.FilteredItemsSelectionDialog#createExtendedContentArea(org.eclipse.swt.widgets.Composite)
+ */
+ @Override
+ protected Control createExtendedContentArea(Composite parent)
+ {
+ return null;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.dialogs.FilteredItemsSelectionDialog#createFilter()
+ */
+ @Override
+ protected ItemsFilter createFilter()
+ {
+ return new ItemsFilter()
+ {
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.dialogs.FilteredItemsSelectionDialog.ItemsFilter#isConsistentItem(java.lang.Object)
+ */
+ @Override
+ public boolean isConsistentItem(Object item)
+ {
+ return true;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.dialogs.FilteredItemsSelectionDialog.ItemsFilter#matchItem(java.lang.Object)
+ */
+ @Override
+ public boolean matchItem(Object item)
+ {
+ if (!(item instanceof String))
+ {
+ return false;
+ }
+ return matches((String) item);
+ }
+
+ };
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.dialogs.FilteredItemsSelectionDialog#fillContentProvider(org.eclipse.ui.dialogs.FilteredItemsSelectionDialog.AbstractContentProvider, org.eclipse.ui.dialogs.FilteredItemsSelectionDialog.ItemsFilter, org.eclipse.core.runtime.IProgressMonitor)
+ */
+ @Override
+ protected void fillContentProvider(AbstractContentProvider contentProvider,
+ ItemsFilter itemsFilter, IProgressMonitor progressMonitor) throws CoreException
+ {
+ for (String action : categorySet)
+ {
+ contentProvider.add(action, itemsFilter);
+ }
+
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.dialogs.FilteredItemsSelectionDialog#getDialogSettings()
+ */
+ @Override
+ protected IDialogSettings getDialogSettings()
+ {
+ return CodeUtilsActivator.getDefault().getDialogSettings();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.dialogs.FilteredItemsSelectionDialog#getElementName(java.lang.Object)
+ */
+ @Override
+ public String getElementName(Object item)
+ {
+ return (String) item;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.dialogs.FilteredItemsSelectionDialog#getItemsComparator()
+ */
+ @Override
+ protected Comparator<String> getItemsComparator()
+ {
+ return new Comparator<String>()
+ {
+
+ public int compare(String o1, String o2)
+ {
+ return o1.compareToIgnoreCase(o2);
+ }
+ };
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.dialogs.FilteredItemsSelectionDialog#validateItem(java.lang.Object)
+ */
+ @Override
+ protected IStatus validateItem(Object item)
+ {
+ IStatus status;
+ if (categorySet.contains(item))
+ {
+ status = new Status(IStatus.OK, CodeUtilsActivator.PLUGIN_ID, "");
+ }
+ else
+ {
+ status = new Status(IStatus.ERROR, CodeUtilsActivator.PLUGIN_ID, "Select an item.");
+ }
+ return status;
+ }
+}
diff --git a/src/plugins/android.codeutils/src/com/motorola/studio/android/wizards/buildingblocks/Method.java b/src/plugins/android.codeutils/src/com/motorola/studio/android/wizards/buildingblocks/Method.java
new file mode 100644
index 0000000..4779e2d
--- /dev/null
+++ b/src/plugins/android.codeutils/src/com/motorola/studio/android/wizards/buildingblocks/Method.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.wizards.buildingblocks;
+
+/**
+ * Abstract bean to define method descriptions to be used on the
+ * building block wizards
+ */
+public abstract class Method
+{
+ private final String message;
+
+ /**
+ * Default constructor
+ *
+ * @param message The method description
+ */
+ public Method(String message)
+ {
+ this.message = message;
+ }
+
+ /**
+ * Retrieves the method description
+ *
+ * @return the method description
+ */
+ public String getMessage()
+ {
+ return message;
+ }
+
+ /**
+ * Handles the method selection, i.e., when the user
+ * selects the method on the wizard
+ *
+ * @param selection if the method has been selected or not
+ */
+ public abstract void handle(boolean selection);
+}
diff --git a/src/plugins/android.codeutils/src/com/motorola/studio/android/wizards/buildingblocks/NewActivityBasedOnTemplatePage.java b/src/plugins/android.codeutils/src/com/motorola/studio/android/wizards/buildingblocks/NewActivityBasedOnTemplatePage.java
new file mode 100644
index 0000000..6efe847
--- /dev/null
+++ b/src/plugins/android.codeutils/src/com/motorola/studio/android/wizards/buildingblocks/NewActivityBasedOnTemplatePage.java
@@ -0,0 +1,258 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.wizards.buildingblocks;
+
+import org.eclipse.jdt.core.IPackageFragmentRoot;
+import org.eclipse.jdt.ui.wizards.NewTypeWizardPage;
+import org.eclipse.jface.dialogs.IPageChangeProvider;
+import org.eclipse.jface.dialogs.IPageChangedListener;
+import org.eclipse.jface.dialogs.PageChangedEvent;
+import org.eclipse.jface.wizard.IWizardPage;
+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.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+
+import com.motorola.studio.android.codeutils.CodeUtilsActivator;
+import com.motorola.studio.android.codeutils.i18n.CodeUtilsNLS;
+import com.motorola.studio.android.common.exception.AndroidException;
+import com.motorola.studio.android.common.utilities.AndroidUtils;
+import com.motorola.studio.android.model.ActivityBasedOnTemplate;
+
+/**
+ * Class that implements the Activity Wizard Main Page
+ */
+public class NewActivityBasedOnTemplatePage extends NewLauncherWizardPage
+{
+
+ private IPackageFragmentRoot currentPackageFragmentRoot;
+
+ private IPackageFragmentRoot previousPackageFragmentRoot;
+
+ private boolean firstLoad = true;
+
+ /**
+ * Listener to verify when this page is visible
+ */
+ private class PageChangeListener implements IPageChangedListener
+ {
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * org.eclipse.jface.dialogs.IPageChangedListener#pageChanged(org.eclipse
+ * .jface.dialogs.PageChangedEvent)
+ */
+ public void pageChanged(PageChangedEvent event)
+ {
+ if ((event.getSelectedPage() == NewActivityBasedOnTemplatePage.this))
+ {
+ if (!firstLoad)
+ {
+ handleFieldChanged(NewTypeWizardPage.TYPENAME);
+ handleFieldChanged(NewTypeWizardPage.PACKAGE);
+ }
+ firstLoad = false;
+ }
+ }
+ }
+
+ @Override
+ public boolean canFlipToNextPage()
+ {
+ if (getBuildBlock().isDatabaseTemplateSelected())
+ {
+ return !getBuildBlock().isBasicInformationFilledIn();
+ }
+ else
+ {
+ return false;
+ }
+
+ }
+
+ private static final String NEW_ACTIVITY_BASED_ON_TEMPLATE_HELP = CodeUtilsActivator.PLUGIN_ID
+ + ".new-activity-based-on-template"; //$NON-NLS-1$
+
+ /**
+ * Default constructor
+ *
+ * @param activity The activity model
+ */
+ public NewActivityBasedOnTemplatePage(ActivityBasedOnTemplate activity)
+ {
+ super(activity, CodeUtilsNLS.UI_NewActivityMainPage_PageTitle);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.wizards.buildingblocks.NewBuildingBlocksWizard#getBuildBlock()
+ */
+ @Override
+ public ActivityBasedOnTemplate getBuildBlock()
+ {
+ return (ActivityBasedOnTemplate) super.getBuildBlock();
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.wizards.buildingblocks.NewBuildingBlocksWizardPage#getMethods()
+ */
+ @Override
+ protected Method[] getMethods()
+ {
+ return null;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.wizards.buildingblocks.NewBuildingBlocksWizardPage#createIntermediateControls(org.eclipse.swt.widgets.Composite)
+ */
+ @Override
+ protected void createIntermediateControls(Composite parent)
+ {
+ createIntentFilterControls(parent);
+ createMainActivityControl(parent, 4);
+ }
+
+ private void createMainActivityControl(Composite parent, int nColumns)
+ {
+ // Create a checkbox to allow the user to set the created activity as the MAIN activity
+ Button checkButtonMainActivity = new Button(parent, SWT.CHECK | SWT.LEFT);
+ GridData gridData = new GridData(SWT.FILL, SWT.CENTER, true, false, nColumns, 1);
+ checkButtonMainActivity.setLayoutData(gridData);
+ checkButtonMainActivity.setText(CodeUtilsNLS.UI_NewActivityMainPage_CheckMainButton);
+ checkButtonMainActivity.addSelectionListener(new SelectionAdapter()
+ {
+
+ @Override
+ public void widgetSelected(SelectionEvent e)
+ {
+ if (e.widget instanceof Button)
+ {
+ // Set Activity flag accordingly
+ getBuildBlock().setMainActivity(((Button) e.widget).getSelection());
+
+ }
+ }
+
+ });
+
+ // Add a listener to the wizard to listen for page changes
+ if (getContainer() instanceof IPageChangeProvider)
+ {
+ ((IPageChangeProvider) getContainer()).addPageChangedListener(new PageChangeListener());
+ }
+
+ currentPackageFragmentRoot = getBuildBlock().getPackageFragmentRoot();
+ previousPackageFragmentRoot = getBuildBlock().getPackageFragmentRoot();
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.wizards.buildingblocks.NewBuildingBlocksWizardPage#getDefaultMessage()
+ */
+ @Override
+ public String getDefaultMessage()
+ {
+ return CodeUtilsNLS.UI_NewActivityMainPage_DescriptionCreateActivityBasedOnTemplate;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.wizards.buildingblocks.NewBuildingBlocksWizardPage#getWizardTitle()
+ */
+ @Override
+ public String getWizardTitle()
+ {
+ return CodeUtilsNLS.UI_NewActivityMainPage_TitleActivityBasedOnTemplate;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.wizards.buildingblocks.NewLauncherWizardPage#getIntentFiltersActions()
+ */
+ @Override
+ protected String[] getIntentFiltersActions()
+ {
+ String[] intentFiltersActions = new String[0];
+ try
+ {
+ intentFiltersActions = AndroidUtils.getActivityActions(getBuildBlock().getProject());
+ }
+ catch (AndroidException e)
+ {
+ setErrorMessage(e.getMessage());
+ }
+ return intentFiltersActions;
+ }
+
+ /**
+ * Gets the help ID to be used for attaching
+ * context sensitive help.
+ *
+ * Classes that extends this class and want to set
+ * their on help should override this method
+ */
+ @Override
+ protected String getHelpId()
+ {
+ return NEW_ACTIVITY_BASED_ON_TEMPLATE_HELP;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * org.eclipse.jdt.ui.wizards.NewTypeWizardPage#handleFieldChanged(java.
+ * lang.String)
+ */
+ @Override
+ protected void handleFieldChanged(String fieldName)
+ {
+ super.handleFieldChanged(fieldName);
+
+ if (NewTypeWizardPage.CONTAINER.equals(fieldName))
+ {
+ currentPackageFragmentRoot = getBuildBlock().getPackageFragmentRoot();
+
+ if ((currentPackageFragmentRoot != null)
+ && (!currentPackageFragmentRoot.equals(previousPackageFragmentRoot)))
+ {
+ previousPackageFragmentRoot = currentPackageFragmentRoot;
+ // Set the collector info to null
+ getBuildBlock().setCollectorTable(null);
+ getBuildBlock().setCollectorDatabaseName(null);
+ getBuildBlock().setDatabaseTableSelected(false);
+ getBuildBlock().setSqlOpenHelperDefined(false);
+ getBuildBlock().setUseSampleDatabaseTableSelected(false);
+
+ IWizardPage page =
+ getWizard().getPage(CodeUtilsNLS.UI_DefineSqlOpenHelperPage_Title);
+
+ if (!firstLoad)
+ {
+ if (page instanceof NewTypeWizardPage)
+ {
+ ((NewTypeWizardPage) page).setPackageFragment(getPackageFragment(), true);
+ }
+ }
+ }
+ }
+ if (getWizard().getContainer().getCurrentPage() != null)
+ {
+ getWizard().getContainer().updateButtons();
+ }
+
+ }
+} \ No newline at end of file
diff --git a/src/plugins/android.codeutils/src/com/motorola/studio/android/wizards/buildingblocks/NewActivityBasedOnTemplateWizard.java b/src/plugins/android.codeutils/src/com/motorola/studio/android/wizards/buildingblocks/NewActivityBasedOnTemplateWizard.java
new file mode 100644
index 0000000..feb75a7
--- /dev/null
+++ b/src/plugins/android.codeutils/src/com/motorola/studio/android/wizards/buildingblocks/NewActivityBasedOnTemplateWizard.java
@@ -0,0 +1,219 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.wizards.buildingblocks;
+
+import java.lang.reflect.InvocationTargetException;
+import java.util.List;
+
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.IncrementalProjectBuilder;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.jdt.core.ICompilationUnit;
+import org.eclipse.jdt.ui.JavaUI;
+import org.eclipse.jface.operation.IRunnableWithProgress;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.wizard.IWizardPage;
+import org.eclipse.ui.IWorkbench;
+
+import com.motorola.studio.android.codeutils.CodeUtilsActivator;
+import com.motorola.studio.android.codeutils.i18n.CodeUtilsNLS;
+import com.motorola.studio.android.common.exception.AndroidException;
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.common.utilities.EclipseUtils;
+import com.motorola.studio.android.model.ActivityBasedOnTemplate;
+import com.motorola.studio.android.model.BuildingBlockModel;
+import com.motorola.studio.android.model.IDatabaseSampleActivityParametersWizardCollector;
+
+/**
+ * Class that implements the Activity Wizard
+ */
+public class NewActivityBasedOnTemplateWizard extends NewBuildingBlocksWizard
+{
+ private static final String WIZBAN_ICON = "icons/wizban/new_activity_template_wiz.png"; //$NON-NLS-1$
+
+ private final ActivityBasedOnTemplate activity = new ActivityBasedOnTemplate();
+
+ /*
+ * IRunnableWithProgress object to create the activity
+ */
+ private class DoSave implements IRunnableWithProgress
+ {
+ AndroidException exception = null;
+
+ boolean saved = false;
+
+ public void run(IProgressMonitor monitor) throws InvocationTargetException,
+ InterruptedException
+ {
+ try
+ {
+ saved = getBuildingBlock().save(getContainer(), monitor);
+ getBuildingBlock().getProject().refreshLocal(IResource.DEPTH_INFINITE, monitor);
+ getBuildingBlock().getProject().build(IncrementalProjectBuilder.INCREMENTAL_BUILD,
+ monitor);
+ }
+ catch (CoreException ce)
+ {
+ //build failed - show a warning message
+ StudioLogger.error(this.getClass(), ce.getMessage(), ce);
+ EclipseUtils
+ .showWarningDialog(
+ CodeUtilsNLS.UI_NewActivityWizard_TitleNewActivityWizard,
+ CodeUtilsNLS.NewActivityWizard_MessageSomeProblemsOccurredWhileBuildingProject);
+ }
+ catch (AndroidException e)
+ {
+ exception = e;
+ }
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.wizard.Wizard#canFinish()
+ */
+ @Override
+ public boolean canFinish()
+ {
+ return (!activity.needMoreInformation())
+ && !(getContainer().getCurrentPage() instanceof ActivitySampleSelectionPage);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.wizard.Wizard#performFinish()
+ */
+ @Override
+ public boolean performFinish()
+ {
+ boolean saved = false;
+
+ try
+ {
+ DoSave doSave = new DoSave();
+
+ getContainer().run(false, false, doSave);
+
+ if (doSave.exception != null)
+ {
+ throw doSave.exception;
+ }
+ else
+ {
+ saved = doSave.saved;
+ }
+ }
+ catch (Exception e)
+ {
+ IStatus status =
+ new Status(IStatus.ERROR, CodeUtilsActivator.PLUGIN_ID, e.getLocalizedMessage());
+ EclipseUtils.showErrorDialog(CodeUtilsNLS.UI_GenericErrorDialogTitle,
+ CodeUtilsNLS.ERR_BuildingBlockCreation_ErrorMessage, status);
+ }
+
+ if (saved)
+ {
+ ICompilationUnit javaFile =
+ getBuildingBlock().getPackageFragment().getCompilationUnit(
+ getBuildingBlock().getName() + ".java"); //$NON-NLS-1$
+
+ if ((javaFile != null) && javaFile.exists())
+ {
+ try
+ {
+ JavaUI.openInEditor(javaFile);
+ }
+ catch (Exception e)
+ {
+ // Do nothing
+ StudioLogger.error(NewActivityBasedOnTemplateWizard.class,
+ "Could not open the activity " //$NON-NLS-1$
+ + getBuildingBlock().getName() + " on an editor.", e); //$NON-NLS-1$
+ }
+ }
+ }
+
+ if (saved)
+ {
+ // Collecting usage data for statistical purposes
+ try
+ {
+ StudioLogger.collectUsageData(StudioLogger.WHAT_BUILDINGBLOCK_ACTIVITY,
+ StudioLogger.KIND_BUILDINGBLOCK, StudioLogger.DESCRIPTION_DEFAULT,
+ CodeUtilsActivator.PLUGIN_ID, CodeUtilsActivator.getDefault().getBundle()
+ .getVersion().toString());
+ }
+ catch (Throwable e)
+ {
+ //Do nothing, but error on the log should never prevent app from working
+ }
+ }
+ return saved;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.IWorkbenchWizard#init(org.eclipse.ui.IWorkbench, org.eclipse.jface.viewers.IStructuredSelection)
+ */
+ public void init(IWorkbench workbench, IStructuredSelection selection)
+ {
+ setWindowTitle(CodeUtilsNLS.UI_NewActivityWizard_TitleNewActivityBasedOnTemplateWizard);
+ setNeedsProgressMonitor(true);
+ setDefaultPageImageDescriptor(CodeUtilsActivator.getImageDescriptor(WIZBAN_ICON));
+ activity.configure(selection);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.wizard.Wizard#addPages()
+ */
+ @Override
+ public void addPages()
+ {
+ addPage(new ActivitySampleSelectionPage(activity));
+ addPage(new NewActivityBasedOnTemplatePage(activity));
+ IDatabaseSampleActivityParametersWizardCollector collector =
+ activity.getDatabaseSampleActivityParametersWizardCollector();
+ if (collector != null)
+ {
+ List<IWizardPage> contributedPageList = collector.getWizardPages();
+ if (contributedPageList != null)
+ {
+ for (IWizardPage page : contributedPageList)
+ {
+ if (page instanceof NewBuildingBlocksWizardPage)
+ {
+ //there is page to select parameters for activity creation
+ NewBuildingBlocksWizardPage buildBlockPage =
+ (NewBuildingBlocksWizardPage) page;
+ buildBlockPage.setBuildBlock(activity);
+ addPage(buildBlockPage);
+ }
+ }
+ }
+ }
+ addPage(new NewActivityWizardListTemplatesPage(activity));
+
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.wizards.buildingblocks.NewBuildingBlocksWizard#getBuildingBlock()
+ */
+ @Override
+ protected BuildingBlockModel getBuildingBlock()
+ {
+ return activity;
+ }
+}
diff --git a/src/plugins/android.codeutils/src/com/motorola/studio/android/wizards/buildingblocks/NewActivityMainPage.java b/src/plugins/android.codeutils/src/com/motorola/studio/android/wizards/buildingblocks/NewActivityMainPage.java
new file mode 100644
index 0000000..b91a5b1
--- /dev/null
+++ b/src/plugins/android.codeutils/src/com/motorola/studio/android/wizards/buildingblocks/NewActivityMainPage.java
@@ -0,0 +1,277 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.wizards.buildingblocks;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.jface.wizard.IWizardContainer;
+import org.eclipse.jface.wizard.WizardDialog;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.CLabel;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Link;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.wizards.IWizardDescriptor;
+
+import com.motorola.studio.android.codeutils.CodeUtilsActivator;
+import com.motorola.studio.android.codeutils.i18n.CodeUtilsNLS;
+import com.motorola.studio.android.common.exception.AndroidException;
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.common.utilities.AndroidUtils;
+import com.motorola.studio.android.model.Activity;
+
+/**
+ * Class that implements the Activity Wizard Main Page
+ */
+public class NewActivityMainPage extends NewLauncherWizardPage
+{
+ @Override
+ public boolean canFlipToNextPage()
+ {
+
+ return false;
+ }
+
+ private static final String NEW_ACTIVITY_HELP = CodeUtilsActivator.PLUGIN_ID + ".newactivity";
+
+ /**
+ * Default constructor
+ *
+ * @param activity The activity model
+ */
+ public NewActivityMainPage(Activity activity)
+ {
+ super(activity, CodeUtilsNLS.UI_NewActivityMainPage_PageTitle);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.wizards.buildingblocks.NewBuildingBlocksWizard#getBuildBlock()
+ */
+ @Override
+ public Activity getBuildBlock()
+ {
+ return (Activity) super.getBuildBlock();
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.wizards.buildingblocks.NewBuildingBlocksWizardPage#getMethods()
+ */
+ @Override
+ protected Method[] getMethods()
+ {
+ Method onCreateMethod = new Method(getBuildBlock().getOnStartMessage())
+ {
+ @Override
+ public void handle(boolean selection)
+ {
+ getBuildBlock().setOnStart(selection);
+ }
+ };
+ return new Method[]
+ {
+ onCreateMethod
+ };
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.wizards.buildingblocks.NewBuildingBlocksWizardPage#createIntermediateControls(org.eclipse.swt.widgets.Composite)
+ */
+ @Override
+ protected void createIntermediateControls(Composite parent)
+ {
+ createIntentFilterControls(parent);
+ createMainActivityControl(parent, 4);
+ createSeparator(parent, 4);
+ }
+
+ /**
+ * add samples control.
+ *
+ * @param composite
+ * The wizard page composite
+ */
+ @Override
+ protected void createSampleControls(Composite composite, int nColumns)
+ {
+ GridData data = null;
+
+ Composite linkCompositecomposite = new Composite(composite, SWT.FILL);
+ linkCompositecomposite.setFont(composite.getFont());
+ GridLayout layout = new GridLayout(nColumns, false);
+ linkCompositecomposite.setLayout(layout);
+ data = new GridData(SWT.FILL, SWT.FILL, true, true, nColumns, 2);
+ linkCompositecomposite.setLayoutData(data);
+
+ Image image = null;
+ try
+ {
+ ImageDescriptor imageDesc =
+ ImageDescriptor.createFromURL(CodeUtilsActivator.getDefault().getBundle()
+ .getEntry("icons/obj16/new_activity_template_wiz.png")); //$NON-NLS-1$
+ image = imageDesc.createImage();
+ }
+ catch (Exception ex)
+ {
+ // do nothing;
+ }
+
+ CLabel imgLabelLeft = new CLabel(linkCompositecomposite, SWT.CENTER);
+ data = new GridData(SWT.LEFT, SWT.CENTER, false, false);
+ imgLabelLeft.setLayoutData(data);
+ imgLabelLeft.setImage(image);
+
+ final Link templateLink = new Link(linkCompositecomposite, SWT.NONE);
+ data = new GridData(SWT.LEFT, SWT.CENTER, true, true);
+ templateLink.setLayoutData(data);
+ templateLink.setText("<a>" + CodeUtilsNLS.UI_CreateNewActivityBasedOnTemplateLink //$NON-NLS-1$
+ + "</a>"); //$NON-NLS-1$
+
+ templateLink.addSelectionListener(new SelectionAdapter()
+ {
+ /* (non-Javadoc)
+ * @see org.eclipse.swt.events.SelectionAdapter#widgetSelected(org.eclipse.swt.events.SelectionEvent)
+ */
+ @Override
+ public void widgetSelected(SelectionEvent event)
+ {
+
+ NewActivityWizard newActivityWizard = (NewActivityWizard) getWizard();
+ IWizardContainer container = newActivityWizard.getContainer();
+
+ container.getShell().setVisible(false);
+
+ if (container instanceof WizardDialog)
+ {
+
+ IWizardDescriptor descriptor =
+ PlatformUI
+ .getWorkbench()
+ .getNewWizardRegistry()
+ .findWizard(
+ "com.motorola.studio.android.wizards.newActivityBasedOnTemplateWizard");
+ if (descriptor != null)
+ {
+ NewActivityBasedOnTemplateWizard wizard = null;
+ try
+ {
+ wizard = (NewActivityBasedOnTemplateWizard) descriptor.createWizard();
+ NewActivityWizard activityWizard = (NewActivityWizard) getWizard();
+
+ wizard.init(PlatformUI.getWorkbench(), activityWizard.getSelection());
+ WizardDialog nextWd = new WizardDialog(getShell(), wizard);
+ nextWd.setTitle(wizard.getWindowTitle());
+ nextWd.open();
+ }
+ catch (CoreException e)
+ {
+ StudioLogger
+ .error(NewActivityMainPage.class,
+ "could not open new activity based on template wizard from inside new activity wizard");
+ }
+ }
+
+ WizardDialog nextWd = (WizardDialog) container;
+ nextWd.close();
+ }
+
+ }
+
+ });
+
+ templateLink.setEnabled(true);
+
+ }
+
+ private void createMainActivityControl(Composite parent, int nColumns)
+ {
+ // Create a checkbox to allow the user to set the created activity as the MAIN activity
+ Button checkButtonMainActivity = new Button(parent, SWT.CHECK | SWT.LEFT);
+ GridData gridData = new GridData(SWT.FILL, SWT.CENTER, true, false, nColumns, 1);
+ checkButtonMainActivity.setLayoutData(gridData);
+ checkButtonMainActivity.setText(CodeUtilsNLS.UI_NewActivityMainPage_CheckMainButton);
+ checkButtonMainActivity.addSelectionListener(new SelectionAdapter()
+ {
+
+ @Override
+ public void widgetSelected(SelectionEvent e)
+ {
+ if (e.widget instanceof Button)
+ {
+ // Set Activity flag accordingly
+ getBuildBlock().setMainActivity(((Button) e.widget).getSelection());
+
+ }
+ }
+
+ });
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.wizards.buildingblocks.NewBuildingBlocksWizardPage#getDefaultMessage()
+ */
+ @Override
+ public String getDefaultMessage()
+ {
+ return CodeUtilsNLS.UI_NewActivityMainPage_DescriptionCreateActivity;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.wizards.buildingblocks.NewBuildingBlocksWizardPage#getWizardTitle()
+ */
+ @Override
+ public String getWizardTitle()
+ {
+ return CodeUtilsNLS.UI_NewActivityMainPage_TitleActivity;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.wizards.buildingblocks.NewLauncherWizardPage#getIntentFiltersActions()
+ */
+ @Override
+ protected String[] getIntentFiltersActions()
+ {
+ String[] intentFiltersActions = new String[0];
+ try
+ {
+ intentFiltersActions = AndroidUtils.getActivityActions(getBuildBlock().getProject());
+ }
+ catch (AndroidException e)
+ {
+ setErrorMessage(e.getMessage());
+ }
+ return intentFiltersActions;
+ }
+
+ /**
+ * Gets the help ID to be used for attaching
+ * context sensitive help.
+ *
+ * Classes that extends this class and want to set
+ * their on help should override this method
+ */
+ @Override
+ protected String getHelpId()
+ {
+ return NEW_ACTIVITY_HELP;
+ }
+} \ No newline at end of file
diff --git a/src/plugins/android.codeutils/src/com/motorola/studio/android/wizards/buildingblocks/NewActivityWizard.java b/src/plugins/android.codeutils/src/com/motorola/studio/android/wizards/buildingblocks/NewActivityWizard.java
new file mode 100644
index 0000000..0d57fb0
--- /dev/null
+++ b/src/plugins/android.codeutils/src/com/motorola/studio/android/wizards/buildingblocks/NewActivityWizard.java
@@ -0,0 +1,235 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.wizards.buildingblocks;
+
+import java.lang.reflect.InvocationTargetException;
+
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.IncrementalProjectBuilder;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.jdt.core.ICompilationUnit;
+import org.eclipse.jdt.core.JavaModelException;
+import org.eclipse.jdt.ui.JavaUI;
+import org.eclipse.jface.operation.IRunnableWithProgress;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.PartInitException;
+
+import com.motorola.studio.android.codeutils.CodeUtilsActivator;
+import com.motorola.studio.android.codeutils.i18n.CodeUtilsNLS;
+import com.motorola.studio.android.common.exception.AndroidException;
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.common.utilities.EclipseUtils;
+import com.motorola.studio.android.model.Activity;
+import com.motorola.studio.android.model.BuildingBlockModel;
+
+/**
+ * Class that implements the Activity Wizard.
+ */
+public class NewActivityWizard extends NewBuildingBlocksWizard
+{
+ private static final String WIZBAN_ICON = "icons/wizban/new_activity_wiz.png"; //$NON-NLS-1$
+
+ private final Activity activity = new Activity();
+
+ private IStructuredSelection selection = null;
+
+ private IWorkbench workbench = null;
+
+ /**
+ * IRunnableWithProgress object to create the activity.
+ */
+ private class DoSave implements IRunnableWithProgress
+ {
+ AndroidException exception = null;
+
+ boolean saved = false;
+
+ public void run(IProgressMonitor monitor) throws InvocationTargetException,
+ InterruptedException
+ {
+ try
+ {
+ saved = getBuildingBlock().save(getContainer(), monitor);
+ getBuildingBlock().getProject().refreshLocal(IResource.DEPTH_INFINITE, monitor);
+ getBuildingBlock().getProject().build(IncrementalProjectBuilder.INCREMENTAL_BUILD,
+ monitor);
+ }
+ catch (CoreException ce)
+ {
+ //build failed - show a warning message
+ StudioLogger.error(this.getClass(), ce.getMessage(), ce);
+ EclipseUtils
+ .showWarningDialog(
+ CodeUtilsNLS.UI_NewActivityWizard_TitleNewActivityWizard,
+ CodeUtilsNLS.NewActivityWizard_MessageSomeProblemsOccurredWhileBuildingProject);
+ }
+ catch (AndroidException e)
+ {
+ exception = e;
+ }
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.wizard.Wizard#canFinish()
+ */
+ @Override
+ public boolean canFinish()
+ {
+ return !activity.needMoreInformation();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.wizard.Wizard#performFinish()
+ */
+ @Override
+ public boolean performFinish()
+ {
+ boolean saved = false;
+
+ try
+ {
+ DoSave doSave = new DoSave();
+
+ getContainer().run(false, false, doSave);
+
+ if (doSave.exception != null)
+ {
+ throw doSave.exception;
+ }
+ else
+ {
+ saved = doSave.saved;
+ }
+ }
+ catch (AndroidException e)
+ {
+ IStatus status =
+ new Status(IStatus.ERROR, CodeUtilsActivator.PLUGIN_ID, e.getLocalizedMessage());
+ EclipseUtils.showErrorDialog(CodeUtilsNLS.UI_GenericErrorDialogTitle,
+ CodeUtilsNLS.ERR_BuildingBlockCreation_ErrorMessage, status);
+ }
+ catch (InvocationTargetException e)
+ {
+ IStatus status =
+ new Status(IStatus.ERROR, CodeUtilsActivator.PLUGIN_ID, e.getLocalizedMessage());
+ EclipseUtils.showErrorDialog(CodeUtilsNLS.UI_GenericErrorDialogTitle,
+ CodeUtilsNLS.ERR_BuildingBlockCreation_ErrorMessage, status);
+ }
+ catch (InterruptedException e)
+ {
+ IStatus status =
+ new Status(IStatus.ERROR, CodeUtilsActivator.PLUGIN_ID, e.getLocalizedMessage());
+ EclipseUtils.showErrorDialog(CodeUtilsNLS.UI_GenericErrorDialogTitle,
+ CodeUtilsNLS.ERR_BuildingBlockCreation_ErrorMessage, status);
+ }
+
+ if (saved)
+ {
+ ICompilationUnit javaFile =
+ getBuildingBlock().getPackageFragment().getCompilationUnit(
+ getBuildingBlock().getName() + ".java"); //$NON-NLS-1$
+
+ if ((javaFile != null) && javaFile.exists())
+ {
+ try
+ {
+ JavaUI.openInEditor(javaFile);
+ }
+ catch (PartInitException e)
+ {
+ // Do nothing
+ StudioLogger.error(NewActivityWizard.class, "Could not open the activity " //$NON-NLS-1$
+ + getBuildingBlock().getName() + " on an editor.", e); //$NON-NLS-1$
+ }
+ catch (JavaModelException e)
+ {
+ // Do nothing
+ StudioLogger.error(NewActivityWizard.class, "Could not open the activity " //$NON-NLS-1$
+ + getBuildingBlock().getName() + " on an editor.", e); //$NON-NLS-1$
+ }
+ }
+ }
+
+ if (saved)
+ {
+ // Collecting usage data for statistical purposes
+ try
+ {
+ StudioLogger.collectUsageData(StudioLogger.WHAT_BUILDINGBLOCK_ACTIVITY,
+ StudioLogger.KIND_BUILDINGBLOCK, StudioLogger.DESCRIPTION_DEFAULT,
+ CodeUtilsActivator.PLUGIN_ID, CodeUtilsActivator.getDefault().getBundle()
+ .getVersion().toString());
+ }
+ catch (Throwable e)
+ {
+ //Do nothing, but error on the log should never prevent app from working
+ }
+ }
+ return saved;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.IWorkbenchWizard#init(org.eclipse.ui.IWorkbench, org.eclipse.jface.viewers.IStructuredSelection)
+ */
+ public void init(IWorkbench workbench, IStructuredSelection selection)
+ {
+ this.selection = selection;
+ this.workbench = workbench;
+ setWindowTitle(CodeUtilsNLS.UI_NewActivityWizard_TitleNewActivityWizard);
+ setNeedsProgressMonitor(true);
+ setDefaultPageImageDescriptor(CodeUtilsActivator.getImageDescriptor(WIZBAN_ICON));
+ activity.configure(selection);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.wizard.Wizard#addPages()
+ */
+ @Override
+ public void addPages()
+ {
+ addPage(new NewActivityMainPage(activity));
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.wizards.buildingblocks.NewBuildingBlocksWizard#getBuildingBlock()
+ */
+ @Override
+ protected BuildingBlockModel getBuildingBlock()
+ {
+ return activity;
+ }
+
+ /**
+ * @return the selection
+ */
+ protected IStructuredSelection getSelection()
+ {
+ return selection;
+ }
+
+ /**
+ * @return the workbench
+ */
+ protected IWorkbench getWorkbench()
+ {
+ return workbench;
+ }
+}
diff --git a/src/plugins/android.codeutils/src/com/motorola/studio/android/wizards/buildingblocks/NewActivityWizardListTemplatesPage.java b/src/plugins/android.codeutils/src/com/motorola/studio/android/wizards/buildingblocks/NewActivityWizardListTemplatesPage.java
new file mode 100644
index 0000000..0b53800
--- /dev/null
+++ b/src/plugins/android.codeutils/src/com/motorola/studio/android/wizards/buildingblocks/NewActivityWizardListTemplatesPage.java
@@ -0,0 +1,383 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.motorola.studio.android.wizards.buildingblocks;
+
+import java.net.URL;
+
+import org.eclipse.core.runtime.FileLocator;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.jface.dialogs.IPageChangeProvider;
+import org.eclipse.jface.dialogs.IPageChangedListener;
+import org.eclipse.jface.dialogs.PageChangedEvent;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.LabelProvider;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+import org.eclipse.jface.viewers.TreeViewer;
+import org.eclipse.jface.viewers.ViewerComparator;
+import org.eclipse.jface.wizard.IWizardPage;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.ScrolledComposite;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Group;
+import org.eclipse.swt.widgets.Label;
+
+import com.motorola.studio.android.codeutils.CodeUtilsActivator;
+import com.motorola.studio.android.codeutils.i18n.CodeUtilsNLS;
+import com.motorola.studio.android.common.exception.AndroidException;
+import com.motorola.studio.android.common.utilities.AndroidUtils;
+import com.motorola.studio.android.model.ActivityBasedOnTemplate;
+
+/**
+ * New activity wizard sample page.
+ */
+public class NewActivityWizardListTemplatesPage extends NewLauncherWizardPage
+{
+
+ private TreeViewer treeViewer;
+
+ private Label imgLabel;
+
+ private Label descriptionLabel;
+
+ private String content[];
+
+ private static final String NEW_ACTIVITY_HELP = CodeUtilsActivator.PLUGIN_ID + ".newactivity";
+
+ private static Image androidImg = null;
+
+ public static final String PAGE_NAME = "List Activities Page";
+
+ //private boolean canFlip = false;
+
+ /*
+ * Listener to update description pane whenever this page is open
+ */
+ private class PageChangeListener implements IPageChangedListener
+ {
+ public void pageChanged(PageChangedEvent event)
+ {
+ if ((event.getSelectedPage() == NewActivityWizardListTemplatesPage.this))
+ {
+ if (!treeViewer.getSelection().isEmpty())
+ {
+ updateTreeViewAfterSelection(treeViewer.getSelection());
+ }
+
+ NewActivityWizardListTemplatesPage.this.getControl().update();
+ ((NewActivityWizardListTemplatesPage) event.getSelectedPage())
+ .updateDescriptionPane();
+ }
+ }
+ }
+
+ protected NewActivityWizardListTemplatesPage(ActivityBasedOnTemplate activity)
+ {
+ super(activity, PAGE_NAME);
+
+ activity.evaluateSamplesList(ActivityBasedOnTemplate.SAMPLE_CATEGORY.LIST_ACTIVITIES_CATEGORY);
+
+ ImageDescriptor descr =
+ CodeUtilsActivator.imageDescriptorFromPlugin(CodeUtilsActivator.PLUGIN_ID,
+ "icons/device_refresh_on.png");
+ androidImg = descr.createImage();
+ }
+
+ /*
+ * (non-Javadoc)
+ * */
+ @Override
+ public boolean canFlipToNextPage()
+ {
+ return getBuildBlock().isListActivitySelected();
+ }
+
+ @Override
+ public IWizardPage getNextPage()
+ {
+ return this.getWizard().getPage(CodeUtilsNLS.UI_NewActivityMainPage_PageTitle);
+ }
+
+ @Override
+ public IWizardPage getPreviousPage()
+ {
+ return this.getWizard().getPage(ActivitySampleSelectionPage.PAGE_NAME);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.wizards.buildingblocks.NewBuildingBlocksWizard#getBuildBlock()
+ */
+ @Override
+ public ActivityBasedOnTemplate getBuildBlock()
+ {
+ return (ActivityBasedOnTemplate) super.getBuildBlock();
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.wizards.buildingblocks.NewBuildingBlocksWizardPage#getMethods()
+ */
+ @Override
+ protected Method[] getMethods()
+ {
+ Method onCreateMethod = new Method(getBuildBlock().getOnStartMessage())
+ {
+ @Override
+ public void handle(boolean selection)
+ {
+ getBuildBlock().setOnStart(selection);
+ }
+ };
+ return new Method[]
+ {
+ onCreateMethod
+ };
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.wizards.buildingblocks.NewBuildingBlocksWizardPage#createIntermediateControls(org.eclipse.swt.widgets.Composite)
+ */
+ @Override
+ protected void createExtendedControls(Composite parent)
+ {
+ Composite mainComposite = new Composite(parent, SWT.FILL);
+ mainComposite.setLayout(new GridLayout(2, false));
+ mainComposite.setLayoutData(new GridData(GridData.FILL_BOTH));
+
+ // Samples Tree Label
+ Label itemsTableLabel = new Label(mainComposite, SWT.NONE);
+ itemsTableLabel
+ .setLayoutData(new GridData(GridData.FILL, GridData.FILL, true, false, 2, 1));
+ itemsTableLabel.setText(CodeUtilsNLS.UI_SampleSelectionPage_SamplesTreeLabel);
+
+ // Samples Tree Viewer
+ treeViewer = new TreeViewer(mainComposite, SWT.BORDER | SWT.SINGLE | SWT.V_SCROLL);
+ treeViewer.getControl().setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1));
+ treeViewer.setLabelProvider(new LabelProvider()
+ {
+ @Override
+ public Image getImage(Object obj)
+ {
+ return androidImg;
+ }
+
+ @Override
+ public String getText(Object element)
+ {
+ return element.toString();
+ }
+ });
+
+ content = new String[getBuildBlock().getListActivitiesSamples().size()];
+
+ int i = 0;
+ for (String currentSample : getBuildBlock().getListActivitiesSamples().keySet())
+ {
+ content[i] = currentSample;
+ i++;
+ }
+
+ //sets tree content and icon
+ treeViewer.setContentProvider(new SampleTreeContentProvider(content));
+ treeViewer.setInput(content);
+
+ final Group previewGroup = new Group(mainComposite, SWT.NONE);
+ previewGroup.setText(CodeUtilsNLS.UI_ListActivityPage_Preview);
+ previewGroup.setLayout(new GridLayout(1, false));
+ previewGroup.setLayoutData(new GridData(GridData.FILL, GridData.FILL, false, true, 1, 1));
+
+ imgLabel = new Label(previewGroup, SWT.NONE);
+ imgLabel.setImage(null);
+
+ GridData imageLabelData = new GridData(GridData.FILL, GridData.FILL, true, true);
+ imageLabelData.widthHint = 200;
+ imgLabel.setLayoutData(imageLabelData);
+
+ final Group descriptionGroup = new Group(mainComposite, SWT.NONE);
+
+ treeViewer.addSelectionChangedListener(new ISelectionChangedListener()
+ {
+ public void selectionChanged(SelectionChangedEvent e)
+ {
+ updateTreeViewAfterSelection(e.getSelection());
+
+ updateDescriptionPane();
+ getWizard().getContainer().updateButtons();
+ }
+ });
+
+ //sort tree
+ treeViewer.setComparator(new ViewerComparator());
+ treeViewer.expandAll();
+
+ //description pane
+ descriptionGroup.setText(CodeUtilsNLS.UI_SampleSelectionPage_SamplesDescriptionPane);
+ descriptionGroup.setLayout(new GridLayout(1, false));
+ descriptionGroup.setLayoutData(new GridData(GridData.FILL, GridData.CENTER, true, false, 2,
+ 1));
+
+ ScrolledComposite scroll = new ScrolledComposite(descriptionGroup, SWT.V_SCROLL);
+ GridData scrollData = new GridData(GridData.FILL, GridData.FILL, true, true);
+ scroll.setLayoutData(scrollData);
+ scroll.setMinSize(100, 140);
+
+ descriptionLabel = new Label(scroll, SWT.FILL | SWT.WRAP);
+ descriptionLabel.setText("");
+
+ scroll.setContent(descriptionLabel);
+
+ // Add a listener to the wizard to listen for page changes
+ if (getContainer() instanceof IPageChangeProvider)
+ {
+ ((IPageChangeProvider) getContainer()).addPageChangedListener(new PageChangeListener());
+ }
+
+ setControl(mainComposite);
+ }
+
+ private void updateTreeViewAfterSelection(ISelection selection)
+ {
+ String template = selection.toString();
+
+ getBuildBlock().setSample(template.substring(1, template.length() - 1));
+
+ //condition to enable finish button
+ getBuildBlock().setIsListActivitySelected(true);
+ //category of sample, used to load the correct files
+ getBuildBlock().setSampleCategoty(
+ ActivityBasedOnTemplate.SAMPLE_CATEGORY.LIST_ACTIVITIES_CATEGORY);
+
+ String strPreview = getBuildBlock().getSamplePreview();
+ if (strPreview != null)
+ {
+ URL url = null;
+ try
+ {
+ //loads the selected sample preview image
+ url =
+ FileLocator.toFileURL(CodeUtilsActivator
+ .getDefault()
+ .getBundle()
+ .getEntry(
+ ActivityBasedOnTemplate.ACTIVITY_SAMPLES_FOLDER
+ + IPath.SEPARATOR + strPreview));
+
+ ImageDescriptor imageDesc = ImageDescriptor.createFromURL(url);
+ Image image = imageDesc.createImage();
+
+ Image previousImage = imgLabel.getImage();
+ if (previousImage != null)
+ {
+ previousImage.dispose();
+ }
+
+ imgLabel.setImage(image);
+ }
+ catch (Exception ex)
+ {
+ imgLabel.setImage(null);
+ }
+ }
+ else
+ {
+ imgLabel.setImage(null);
+ }
+ }
+
+ /*
+ * Updates selected label description
+ */
+ private void updateDescriptionPane()
+ {
+ if (getBuildBlock().isListActivitySelected())
+ {
+ descriptionLabel.setText(getBuildBlock().getSampleDescription());
+ }
+ else
+ {
+ descriptionLabel.setText("");
+ imgLabel.setImage(null);
+ }
+ descriptionLabel.update();
+ descriptionLabel.setSize(descriptionLabel.computeSize(SWT.DEFAULT, SWT.DEFAULT));
+ descriptionLabel.getParent().layout();
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.wizards.buildingblocks.NewBuildingBlocksWizardPage#getDefaultMessage()
+ */
+ @Override
+ public String getDefaultMessage()
+ {
+ return CodeUtilsNLS.UI_NewActivityMainPage_DescriptionCreateActivityBasedOnTemplate;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.wizards.buildingblocks.NewBuildingBlocksWizardPage#getWizardTitle()
+ */
+ @Override
+ public String getWizardTitle()
+ {
+ return CodeUtilsNLS.UI_ListActivityPage_TitleWizard;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.wizards.buildingblocks.NewLauncherWizardPage#getIntentFiltersActions()
+ */
+ @Override
+ protected String[] getIntentFiltersActions()
+ {
+ String[] intentFiltersActions = new String[0];
+ try
+ {
+ intentFiltersActions = AndroidUtils.getActivityActions(getBuildBlock().getProject());
+ }
+ catch (AndroidException e)
+ {
+ setErrorMessage(e.getMessage());
+ }
+ return intentFiltersActions;
+ }
+
+ /**
+ * Gets the help ID to be used for attaching
+ * context sensitive help.
+ *
+ * Classes that extends this class and want to set
+ * their on help should override this method
+ */
+ @Override
+ protected String getHelpId()
+ {
+ return NEW_ACTIVITY_HELP;
+ }
+
+ /**
+ * Returns true if page has header false otherwise
+ *
+ * @return true if page has header false otherwise
+ */
+ @Override
+ public boolean hasHeader()
+ {
+ return false;
+ }
+}
diff --git a/src/plugins/android.codeutils/src/com/motorola/studio/android/wizards/buildingblocks/NewBuildingBlocksWizard.java b/src/plugins/android.codeutils/src/com/motorola/studio/android/wizards/buildingblocks/NewBuildingBlocksWizard.java
new file mode 100644
index 0000000..a705ee0
--- /dev/null
+++ b/src/plugins/android.codeutils/src/com/motorola/studio/android/wizards/buildingblocks/NewBuildingBlocksWizard.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.wizards.buildingblocks;
+
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.jface.wizard.Wizard;
+import org.eclipse.ui.INewWizard;
+
+import com.motorola.studio.android.model.BuildingBlockModel;
+
+/**
+ * Base abstract class to create the building block wizards UI.
+ * A building block represents an Android abstraction, which can be an Activity, Service, Broadcast Receiver, Content Provider or Wigdet Provider.
+ */
+public abstract class NewBuildingBlocksWizard extends Wizard implements INewWizard
+{
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.wizard.Wizard#canFinish()
+ */
+ @Override
+ public boolean canFinish()
+ {
+ return (!getBuildingBlock().needMoreInformation())
+ && (getBuildingBlock().getStatus().getSeverity() != IStatus.ERROR);
+ }
+
+ /**
+ * @return The building block model used by the wizard.
+ */
+ protected abstract BuildingBlockModel getBuildingBlock();
+}
diff --git a/src/plugins/android.codeutils/src/com/motorola/studio/android/wizards/buildingblocks/NewBuildingBlocksWizardPage.java b/src/plugins/android.codeutils/src/com/motorola/studio/android/wizards/buildingblocks/NewBuildingBlocksWizardPage.java
new file mode 100644
index 0000000..c10e668
--- /dev/null
+++ b/src/plugins/android.codeutils/src/com/motorola/studio/android/wizards/buildingblocks/NewBuildingBlocksWizardPage.java
@@ -0,0 +1,1078 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.wizards.buildingblocks;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.IWorkspaceRoot;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.MultiStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.jdt.core.IJavaElement;
+import org.eclipse.jdt.core.IJavaProject;
+import org.eclipse.jdt.core.IPackageFragment;
+import org.eclipse.jdt.core.IPackageFragmentRoot;
+import org.eclipse.jdt.core.JavaCore;
+import org.eclipse.jdt.core.JavaModelException;
+import org.eclipse.jdt.ui.JavaElementComparator;
+import org.eclipse.jdt.ui.JavaElementLabelProvider;
+import org.eclipse.jdt.ui.StandardJavaElementContentProvider;
+import org.eclipse.jdt.ui.wizards.NewTypeWizardPage;
+import org.eclipse.jface.dialogs.Dialog;
+import org.eclipse.jface.viewers.ILabelProvider;
+import org.eclipse.jface.viewers.ViewerFilter;
+import org.eclipse.jface.window.Window;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.ScrolledComposite;
+import org.eclipse.swt.events.ControlAdapter;
+import org.eclipse.swt.events.ControlEvent;
+import org.eclipse.swt.events.ShellEvent;
+import org.eclipse.swt.events.ShellListener;
+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.Event;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.List;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.dialogs.ElementTreeSelectionDialog;
+import org.eclipse.ui.dialogs.ISelectionStatusValidator;
+
+import com.motorola.studio.android.codeutils.CodeUtilsActivator;
+import com.motorola.studio.android.codeutils.i18n.CodeUtilsNLS;
+import com.motorola.studio.android.common.IAndroidConstants;
+import com.motorola.studio.android.common.exception.AndroidException;
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.common.utilities.AndroidUtils;
+import com.motorola.studio.android.common.utilities.EclipseUtils;
+import com.motorola.studio.android.manifest.AndroidProjectManifestFile;
+import com.motorola.studio.android.model.BuildingBlockModel;
+import com.motorola.studio.android.model.manifest.AndroidManifestFile;
+import com.motorola.studio.android.wizards.elements.AddRemoveButtons;
+
+/**
+ * Abstract class used to create the building block wizard main pages.
+ */
+public abstract class NewBuildingBlocksWizardPage extends NewTypeWizardPage
+{
+ private static final String JAVA_EXTENSION = ".java"; //$NON-NLS-1$
+
+ private static final int MAX_PATH_SIZE = 255;
+
+ protected static String LABEL = TYPENAME + ".LABEL"; //$NON-NLS-1$
+
+ protected IWorkspaceRoot fWorkspaceRoot = ResourcesPlugin.getWorkspace().getRoot();
+
+ private BuildingBlockModel buildBlock;
+
+ private Text labelText;
+
+ private Button defaultLabelButton;
+
+ private AddRemoveButtons addRemovePermissionsButtons;
+
+ private List activityPermissions;
+
+ private final Set<String> intentFilterPermissions = new HashSet<String>();
+
+ private MethodCreationControl methodCreationControl;
+
+ /**
+ * Listener to check if the wizard can be opened.
+ */
+ private class WizardShellListener implements ShellListener
+ {
+ private boolean wasChecked = false;
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * org.eclipse.swt.events.ShellListener#shellActivated(org.eclipse.swt
+ * .events.ShellEvent)
+ */
+ @Override
+ public void shellActivated(ShellEvent e)
+ {
+ if (!wasChecked)
+ {
+ wasChecked = true;
+
+ if (!canOpen())
+ {
+ ((Shell) e.widget).close();
+ }
+ }
+
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * org.eclipse.swt.events.ShellListener#shellClosed(org.eclipse.swt.
+ * events.ShellEvent)
+ */
+ @Override
+ public void shellClosed(ShellEvent e)
+ {
+ // Do nothing
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * org.eclipse.swt.events.ShellListener#shellDeactivated(org.eclipse
+ * .swt.events.ShellEvent)
+ */
+ @Override
+ public void shellDeactivated(ShellEvent e)
+ {
+ // Do nothing
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * org.eclipse.swt.events.ShellListener#shellDeiconified(org.eclipse
+ * .swt.events.ShellEvent)
+ */
+ @Override
+ public void shellDeiconified(ShellEvent e)
+ {
+ // Do nothing
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * org.eclipse.swt.events.ShellListener#shellIconified(org.eclipse.swt
+ * .events.ShellEvent)
+ */
+ @Override
+ public void shellIconified(ShellEvent e)
+ {
+ // Do nothing
+ }
+
+ }
+
+ /* Each building block is represent by a class (e.g., an Activity or Service).
+ * Each of these classes contain some methods that must be overridden by subclasses
+ * in order to be called by android framework (e.g., onCreate(...) methods).
+ * This class is responsible to create check boxes that let users choose which of these methods
+ * should be automatically created by the wizard.
+ * */
+ private class MethodCreationControl
+ {
+ private Label stubMessage;
+
+ private Button[] stubButtonArray;
+
+ MethodCreationControl(Composite parent, Method[] methods)
+ {
+ if (methods != null)
+ {
+ if (methods.length > 0)
+ {
+ stubMessage = new Label(parent, SWT.NONE);
+ stubMessage.setLayoutData(new GridData(GridData.FILL, GridData.FILL, false,
+ false, 4, 1));
+ stubMessage
+ .setText(CodeUtilsNLS.UI_NewBuildingBlocksWizardPage_QuestionWhichMethodCreate);
+ }
+
+ createStubsComponent(parent, methods);
+ }
+ }
+
+ /*
+ * Creates a single method declaration to the wizard page
+ *
+ * @param parent
+ * The wizard page composite
+ * @param method
+ * The method to add
+ */
+ private void createStubsComponent(Composite parent, Method[] methods)
+ {
+ stubButtonArray = new Button[methods.length];
+ int i = 0;
+ for (final Method method : methods)
+ {
+ new Label(parent, SWT.NONE);
+ final Button stubsButton = new Button(parent, SWT.CHECK);
+ stubsButton.setLayoutData(new GridData(GridData.FILL, GridData.FILL, false, false,
+ 3, 1));
+ stubsButton.setText(method.getMessage());
+ stubsButton.addListener(SWT.Selection, new Listener()
+ {
+ @Override
+ public void handleEvent(Event event)
+ {
+ method.handle(stubsButton.getSelection());
+ }
+ });
+ stubButtonArray[i++] = stubsButton;
+ }
+ }
+
+ public void setMethodCreationControlEnabled(boolean enabled)
+ {
+ stubMessage.setEnabled(enabled);
+ for (Button stubButton : stubButtonArray)
+ {
+ stubButton.setEnabled(enabled);
+ }
+ }
+ }
+
+ /**
+ * Default constructor.
+ *
+ * @param buildBlock
+ * The building block model that the wizard will create.
+ * @param pageName
+ * The page name.
+ */
+ protected NewBuildingBlocksWizardPage(BuildingBlockModel buildBlock, String pageName)
+ {
+ super(true, pageName);
+ this.buildBlock = buildBlock;
+ setTitle(getWizardTitle());
+ setDescription(getDefaultMessage());
+ setPageComplete(false);
+ }
+
+ /**
+ * Gets the help ID to be used for attaching context sensitive help.
+ *
+ * Classes that extends this class and want to set their own help should
+ * override this method.
+ */
+ protected abstract String getHelpId();
+
+ /**
+ * Returns the wizard title.
+ *
+ * @return the wizard title.
+ */
+ public abstract String getWizardTitle();
+
+ /**
+ * Returns the wizard default status message.
+ *
+ * @return the wizard default status message.
+ */
+ public abstract String getDefaultMessage();
+
+ /**
+ * Returns all methods that the building block can override.
+ *
+ * @return all methods that the building block can override.
+ */
+ protected abstract Method[] getMethods();
+
+ /**
+ * @param enabled If true, all available methods in the building block will be checked
+ * for automatic creation.
+ * */
+ public void setMethodCreationControlEnabled(boolean enabled)
+ {
+ methodCreationControl.setMethodCreationControlEnabled(enabled);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * org.eclipse.jface.dialogs.IDialogPage#createControl(org.eclipse.swt.widgets
+ * .Composite)
+ */
+ @Override
+ public void createControl(Composite parent)
+ {
+ initializeDialogUnits(parent);
+
+ // main control
+ Composite mainComposite = new Composite(parent, SWT.FILL);
+ mainComposite.setLayout(new FillLayout(SWT.FILL));
+ final ScrolledComposite scroll =
+ new ScrolledComposite(mainComposite, SWT.H_SCROLL | SWT.V_SCROLL);
+
+ final Composite composite = new Composite(scroll, SWT.NONE);
+ composite.setFont(parent.getFont());
+
+ int nColumns = 4;
+
+ createSampleControls(composite, nColumns);
+
+ GridLayout layout = new GridLayout(nColumns, false);
+ composite.setLayout(layout);
+ composite.setLayoutData(new GridData(GridData.FILL, GridData.FILL, true, true));
+
+ if (hasHeader())
+ {
+ setPackageFragmentRoot(getBuildBlock().getPackageFragmentRoot(), true);
+ createContainerControls(composite, nColumns);
+ setPackageFragment(getBuildBlock().getPackageFragment(), true);
+ createPackageControls(composite, nColumns);
+
+ createSeparator(composite, nColumns);
+
+ createTypeNameControls(composite, nColumns);
+
+ createLabelControls(composite);
+
+ setSuperClass(getBuildBlock().getSuperClass(), getBuildBlock().useExtendedClass());
+ createSuperClassControls(composite, nColumns);
+
+ createPermissionControls(composite);
+
+ createIntermediateControls(composite);
+
+ createMethodCreationControl(composite, getMethods());
+ }
+ createExtendedControls(composite);
+
+ // set up scroll
+ scroll.setContent(composite);
+
+ scroll.setExpandHorizontal(true);
+ scroll.setExpandVertical(true);
+
+ scroll.addControlListener(new ControlAdapter()
+ {
+ @Override
+ public void controlResized(ControlEvent e)
+ {
+ scroll.setMinSize(composite.computeSize(SWT.DEFAULT, SWT.DEFAULT));
+ }
+ });
+
+ setControl(mainComposite);
+ Dialog.applyDialogFont(mainComposite);
+
+ mainComposite.getShell().addShellListener(new WizardShellListener());
+ PlatformUI.getWorkbench().getHelpSystem().setHelp(parent, getHelpId());
+ PlatformUI.getWorkbench().getHelpSystem().setHelp(mainComposite, getHelpId());
+ }
+
+ /**
+ * Override this class to create the label controls.
+ *
+ * @param parent
+ * The wizard page composite
+ */
+ protected void createLabelControls(Composite parent)
+ {
+ Label label = new Label(parent, SWT.NONE);
+ label.setText(CodeUtilsNLS.UI_NewBuildingBlocksWizardPage_TextLabel);
+
+ labelText = new Text(parent, SWT.BORDER);
+ GridData gridData = new GridData(GridData.FILL_HORIZONTAL);
+ gridData.horizontalSpan = 2;
+ labelText.setLayoutData(gridData);
+ labelText.setEnabled(false);
+
+ defaultLabelButton = new Button(parent, SWT.CHECK);
+ defaultLabelButton.setText(CodeUtilsNLS.UI_NewBuildingBlocksWizardPage_ButtonNameDefault);
+ defaultLabelButton.setSelection(true);
+
+ Listener listener = new Listener()
+ {
+ @Override
+ public void handleEvent(Event event)
+ {
+ if (!defaultLabelButton.getSelection() || !event.widget.equals(labelText))
+ {
+ handleFieldChanged(LABEL);
+ }
+ if (event.widget.equals(defaultLabelButton) && !defaultLabelButton.getSelection()
+ && labelText.isEnabled())
+ {
+ labelText.forceFocus();
+ labelText.selectAll();
+ }
+ }
+ };
+
+ labelText.addListener(SWT.Modify, listener);
+ defaultLabelButton.addListener(SWT.Selection, listener);
+ }
+
+ /**
+ * Override this class to add samples control.
+ *
+ * @param composite
+ * The wizard page composite
+ */
+ protected void createSampleControls(Composite composite, int nColumns)
+ {
+ //default implementation does nothing
+ }
+
+ /**
+ * Override this class to add components after superclass control.
+ *
+ * @param composite
+ * The wizard page composite
+ */
+ protected void createIntermediateControls(Composite composite)
+ {
+ //default implementation does nothing
+ }
+
+ /**
+ * Return all Filter Permissions as an Array.
+ *
+ * @return
+ */
+ public String[] getIntentFilterPermissionsAsArray()
+ {
+ return intentFilterPermissions.toArray(new String[intentFilterPermissions.size()]);
+ }
+
+ /**
+ * Creates the "Permissions" section on the wizard.
+ *
+ * @param composite
+ * the wizard composite
+ */
+ protected void createPermissionControls(Composite composite)
+ {
+ GridData gridData;
+ Label activityPermissionsLabel = new Label(composite, SWT.NONE);
+ activityPermissionsLabel.setText(CodeUtilsNLS.NewBuildingBlocksWizardPage_PermissionLabel);
+ gridData = new GridData(SWT.LEFT, SWT.CENTER, false, false);
+ gridData.verticalAlignment = GridData.BEGINNING;
+ activityPermissionsLabel.setLayoutData(gridData);
+
+ activityPermissions = new List(composite, SWT.BORDER | SWT.V_SCROLL | SWT.H_SCROLL);
+ activityPermissions.setItems(getBuildBlock().getIntentFilterPermissionsAsArray());
+ gridData = new GridData(SWT.FILL, SWT.CENTER, true, false, 2, 1);
+ gridData.heightHint = convertHeightInCharsToPixels(3);
+ activityPermissions.setLayoutData(gridData);
+
+ addRemovePermissionsButtons = new AddRemoveButtons(composite);
+ setButtonLayoutData(addRemovePermissionsButtons.getAddButton());
+ setButtonLayoutData(addRemovePermissionsButtons.getRemoveButton());
+ addRemovePermissionsButtons.getAddButton().addListener(SWT.Selection, new Listener()
+ {
+ @Override
+ public void handleEvent(Event arg0)
+ {
+ Set<String> permissionSet =
+ new HashSet<String>(Arrays.asList(AndroidUtils
+ .getIntentFilterPermissions(getBuildBlock().getProject())));
+
+ permissionSet.removeAll(getBuildBlock().getIntentFilterPermissions());
+
+ FilteredActionsSelectionDialog dialog =
+ new FilteredActionsSelectionDialog(getShell(), permissionSet);
+ dialog.setInitialPattern("**"); //$NON-NLS-1$
+ dialog.setTitle("Select an action permission"); //$NON-NLS-1$
+ dialog.setMessage(CodeUtilsNLS.UI_NewLauncherWizardPage_CategorySelectionDialogMessage);
+
+ if (Dialog.OK == dialog.open())
+ {
+ for (Object result : dialog.getResult())
+ {
+ getBuildBlock().addIntentFilterPermissions((String) result);
+ }
+ activityPermissions.setItems(getBuildBlock()
+ .getIntentFilterPermissionsAsArray());
+ addRemovePermissionsButtons.getRemoveButton().setEnabled(
+ activityPermissions.getSelectionCount() > 0);
+ updateStatus(getBuildBlock().getStatus());
+ }
+ }
+ });
+ addRemovePermissionsButtons.getRemoveButton().addListener(SWT.Selection, new Listener()
+ {
+ @Override
+ public void handleEvent(Event arg0)
+ {
+ for (int selection : activityPermissions.getSelectionIndices())
+ {
+ getBuildBlock().removeIntentFilterPermissions(
+ activityPermissions.getItem(selection));
+ }
+ activityPermissions.setItems(getBuildBlock().getIntentFilterPermissionsAsArray());
+ addRemovePermissionsButtons.getRemoveButton().setEnabled(
+ activityPermissions.getSelectionCount() > 0);
+ updateStatus(getBuildBlock().getStatus());
+ }
+ });
+ addRemovePermissionsButtons.getRemoveButton().setEnabled(
+ activityPermissions.getSelectionCount() > 0);
+ activityPermissions.addListener(SWT.Selection, new Listener()
+ {
+ @Override
+ public void handleEvent(Event arg0)
+ {
+ addRemovePermissionsButtons.getRemoveButton().setEnabled(
+ activityPermissions.getSelectionCount() > 0);
+ }
+ });
+ }
+
+ /**
+ * Override this class to add components at the end of the Page.
+ *
+ * @param parent
+ * The wizard page composite
+ */
+ protected void createExtendedControls(Composite parent)
+ {
+ //default implementation does nothing
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.jdt.ui.wizards.NewElementWizardPage#setVisible(boolean)
+ */
+ @Override
+ public void setVisible(boolean visible)
+ {
+ super.setVisible(visible);
+ if (visible)
+ {
+ setFocus();
+ }
+ }
+
+ /**
+ * Returns true if page has header false otherwise.
+ *
+ * @return true if page has header false otherwise.
+ */
+ public boolean hasHeader()
+ {
+ return true;
+ }
+
+ /**
+ * Adds the methods that a building block can override to the wizard page.
+ *
+ * @param parent
+ * The wizard page composite.
+ * @param methods
+ * The methods to add to the wizard.
+ */
+ protected void createMethodCreationControl(Composite parent, Method[] methods)
+ {
+ methodCreationControl = new MethodCreationControl(parent, methods);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.jdt.ui.wizards.NewContainerWizardPage#chooseContainer()
+ */
+ @Override
+ protected IPackageFragmentRoot chooseContainer()
+ {
+ IJavaElement initElement = getPackageFragmentRoot();
+
+ ISelectionStatusValidator validator = new ElementTreeValidator();
+ ViewerFilter filter = new ElementTreeViewFilter();
+ StandardJavaElementContentProvider provider = new ElementTreeContentProvider();
+
+ ILabelProvider labelProvider =
+ new JavaElementLabelProvider(JavaElementLabelProvider.SHOW_DEFAULT);
+ ElementTreeSelectionDialog dialog =
+ new ElementTreeSelectionDialog(getShell(), labelProvider, provider);
+ dialog.setComparator(new JavaElementComparator());
+ dialog.setValidator(validator);
+ dialog.setTitle(CodeUtilsNLS.UI_NewBuildingBlocksWizardPage_WizardTitle);
+ dialog.setMessage(CodeUtilsNLS.UI_NewBuildingBlocksWizardPage_MessageChooseFolder);
+ dialog.setInput(JavaCore.create(fWorkspaceRoot));
+ dialog.setInitialSelection(initElement);
+ dialog.addFilter(filter);
+ dialog.setHelpAvailable(false);
+
+ IPackageFragmentRoot rootSelection = null;
+ if (dialog.open() == Window.OK)
+ {
+ Object element = dialog.getFirstResult();
+ if (element instanceof IJavaProject)
+ {
+ IJavaProject jproject = (IJavaProject) element;
+ rootSelection = jproject.getPackageFragmentRoot(jproject.getProject());
+ }
+ else if (element instanceof IPackageFragmentRoot)
+ {
+ rootSelection = (IPackageFragmentRoot) element;
+ }
+ }
+ return rootSelection;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * org.eclipse.jdt.ui.wizards.NewTypeWizardPage#handleFieldChanged(java.
+ * lang.String)
+ */
+ @Override
+ protected void handleFieldChanged(String fieldName)
+ {
+ if (NewTypeWizardPage.TYPENAME.equals(fieldName))
+ {
+ getBuildBlock().setName(getTypeName());
+ getBuildBlock().setNameStatus(typeNameChanged());
+ getBuildBlock().setPackageStatus(packageChanged());
+ }
+ else if (NewTypeWizardPage.CONTAINER.equals(fieldName))
+ {
+ // source folder
+ getBuildBlock().setPackageFragmentRoot(getPackageFragmentRoot());
+ getBuildBlock().setPackageFragmentRootStatus(containerChanged());
+ getBuildBlock().setPackageStatus(packageChanged());
+ getBuildBlock().setNameStatus(typeNameChanged());
+
+ updatePackage(getPackageFragmentRoot());
+ }
+ else if (NewTypeWizardPage.PACKAGE.equals(fieldName))
+ {
+ if (getPackageFragmentRoot() != null)
+ {
+ getBuildBlock().setPackageFragment(
+ getPackageFragmentRoot().getPackageFragment(getPackageText()));
+ }
+ getBuildBlock().setPackageStatus(packageChanged());
+ getBuildBlock().setNameStatus(typeNameChanged());
+ }
+ else if (LABEL.equals(fieldName))
+ {
+ getBuildBlock().setLabelStatus(labelChanged());
+ }
+ updateStatus(getBuildBlock().getStatus());
+ }
+
+ private void updatePackage(IPackageFragmentRoot packageFragmentRoot)
+ {
+ if (packageFragmentRoot != null)
+ {
+ IJavaProject project = null;
+ IPackageFragment pack = null;
+
+ project = packageFragmentRoot.getJavaProject();
+ try
+ {
+ pack = EclipseUtils.getDefaultPackageFragment(project);
+ getBuildBlock().setPackageFragment(pack);
+ }
+ catch (JavaModelException e)
+ {
+ StudioLogger.error(NewBuildingBlocksWizardPage.class,
+ "Error getting default package fragment.", e); //$NON-NLS-1$
+ // do nothing
+ }
+ setPackageFragment(pack, true);
+ handleFieldChanged(NewTypeWizardPage.PACKAGE);
+ }
+ }
+
+ /**
+ * @return A status indicating if the building block label property has been change.
+ */
+ protected IStatus labelChanged()
+ {
+ IStatus status = new Status(IStatus.OK, CodeUtilsActivator.PLUGIN_ID, null);
+ if ((defaultLabelButton != null) && (labelText != null))
+ {
+ if (defaultLabelButton.getSelection())
+ {
+ labelText.setText(""); //$NON-NLS-1$
+
+ }
+ labelText.setEnabled(!defaultLabelButton.getSelection());
+ getBuildBlock().setLabel(getLabel());
+ }
+ return status;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.jdt.ui.wizards.NewTypeWizardPage#typeNameChanged()
+ */
+ @Override
+ protected IStatus typeNameChanged()
+ {
+ IStatus superStatus = super.typeNameChanged();
+ IStatus status =
+ new Status(superStatus.getSeverity(), CodeUtilsActivator.PLUGIN_ID,
+ superStatus.getMessage());
+
+ Pattern pattern = Pattern.compile("([A-Za-z0-9_]+)"); //$NON-NLS-1$
+
+ if (superStatus.getSeverity() != IStatus.ERROR)
+ {
+ Matcher matcher = pattern.matcher(getTypeName());
+
+ if (!matcher.matches() || !matcher.group().equals(getTypeName()))
+ {
+ String errMsg =
+ NLS.bind(CodeUtilsNLS.ERR_NewBuildingBlocksWizardPage_InvalidTypeName,
+ getTypeName());
+
+ status = new Status(IStatus.ERROR, CodeUtilsActivator.PLUGIN_ID, errMsg);
+ }
+ else if (packageAndClassExist())
+ {
+ status =
+ new Status(
+ IStatus.ERROR,
+ CodeUtilsActivator.PLUGIN_ID,
+ CodeUtilsNLS.ERR_NewBuildingBlocksWizardPage_PackageAndClassAlreadyExist);
+ }
+ else if (isTooLongOnFileSystem())
+ {
+ status =
+ new Status(IStatus.ERROR, CodeUtilsActivator.PLUGIN_ID,
+ CodeUtilsNLS.ERR_NewBuildingBlocksWizardPage_FileNameTooLong);
+ }
+ }
+
+ labelChanged();
+ return status;
+ }
+
+ /**
+ * Returns the building block label property value.
+ *
+ * @return the building block label property value.
+ */
+ protected String getLabel()
+ {
+ String label;
+ if (defaultLabelButton.getSelection())
+ {
+ label = ""; //$NON-NLS-1$
+ }
+ else
+ {
+ label = labelText.getText();
+ }
+ return label;
+ }
+
+ /**
+ * Returns the building block model
+ *
+ * @return the building block model
+ */
+ public BuildingBlockModel getBuildBlock()
+ {
+ return buildBlock;
+ }
+
+ public void setBuildBlock(BuildingBlockModel buildBlock)
+ {
+ this.buildBlock = buildBlock;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.jdt.ui.wizards.NewTypeWizardPage#packageChanged()
+ */
+ @Override
+ protected IStatus packageChanged()
+ {
+ IStatus superStatus = super.packageChanged();
+ IStatus status =
+ new Status(superStatus.getSeverity(), CodeUtilsActivator.PLUGIN_ID,
+ superStatus.getMessage());
+
+ // The package name is being get by getPackageText because the method
+ // getPackageFragment
+ // (from super class) is not returning the right value in some cases
+ String packageName = getPackageText();
+
+ if (status.getCode() != IStatus.ERROR)
+ {
+ if (packageName != null)
+ {
+ Pattern pattern = Pattern.compile("[A-Za-z0-9_\\.]+"); //$NON-NLS-1$
+ Matcher matcher = pattern.matcher(packageName);
+
+ if (packageName.indexOf('.') == -1)
+ {
+ status =
+ new Status(
+ IStatus.ERROR,
+ CodeUtilsActivator.PLUGIN_ID,
+ CodeUtilsNLS.ERR_NewBuildingBlocksWizardPage_PackageMustHaveAtLeastTwoIdentifiers);
+ }
+ else if (!matcher.matches())
+ {
+ String errMsg =
+ NLS.bind(
+ CodeUtilsNLS.ERR_NewBuildingBlocksWizardPage_InvalidPackageName,
+ packageName);
+ status = new Status(IStatus.ERROR, CodeUtilsActivator.PLUGIN_ID, errMsg);
+ }
+ else if (packageAndClassExist())
+ {
+ status =
+ new Status(
+ IStatus.ERROR,
+ CodeUtilsActivator.PLUGIN_ID,
+ CodeUtilsNLS.ERR_NewBuildingBlocksWizardPage_PackageAndClassAlreadyExist);
+ }
+ else if (isTooLongOnFileSystem())
+ {
+ status =
+ new Status(IStatus.ERROR, CodeUtilsActivator.PLUGIN_ID,
+ CodeUtilsNLS.ERR_NewBuildingBlocksWizardPage_FileNameTooLong);
+ }
+ }
+
+ }
+
+ return status;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.jdt.ui.wizards.NewTypeWizardPage#containerChanged()
+ */
+ @Override
+ protected IStatus containerChanged()
+ {
+ IStatus superStatus = super.containerChanged();
+ IStatus status =
+ new Status(superStatus.getSeverity(), CodeUtilsActivator.PLUGIN_ID,
+ superStatus.getMessage());
+
+ boolean hasNature = false;
+
+ if (status.getCode() != IStatus.ERROR)
+ {
+ try
+ {
+ if ((getPackageFragmentRoot() != null)
+ && (getPackageFragmentRoot().getJavaProject() != null))
+ {
+ hasNature =
+ getPackageFragmentRoot().getJavaProject().getProject()
+ .hasNature(IAndroidConstants.ANDROID_NATURE);
+ }
+ }
+ catch (CoreException ce)
+ {
+ StudioLogger.error(NewBuildingBlocksWizardPage.class,
+ "Error getting the project nature.", ce); //$NON-NLS-1$
+ hasNature = false;
+ }
+
+ if ((getPackageFragmentRoot() != null) && !hasNature)
+ {
+ status =
+ new Status(IStatus.ERROR, CodeUtilsActivator.PLUGIN_ID,
+ CodeUtilsNLS.ERR_NewBuildingBlocksWizardPage_SelectAnAndroidProject);
+ }
+ else if ((getPackageFragmentRoot() == null)
+ || (getPackageFragmentRoot().getResource().getType() == IResource.PROJECT)
+ || ((getPackageFragmentRoot().getElementType() & IPackageFragmentRoot.K_SOURCE) != IPackageFragmentRoot.K_SOURCE))
+ {
+ status =
+ new Status(
+ IStatus.ERROR,
+ CodeUtilsActivator.PLUGIN_ID,
+ CodeUtilsNLS.ERR_NewBuildingBlocksWizardPage_SelectAValidSourceFolder);
+ }
+ else if (getPackageFragmentRoot().getElementName().equals(
+ IAndroidConstants.GEN_SRC_FOLDER)
+ && (getPackageFragmentRoot().getParent() instanceof IJavaProject))
+ {
+ status =
+ new Status(
+ IStatus.ERROR,
+ CodeUtilsActivator.PLUGIN_ID,
+ CodeUtilsNLS.ERR_NewBuildingBlocksWizardPage_CannotUseTheGenFolderAsSourceFolder);
+ }
+ else if (isTooLongOnFileSystem())
+ {
+ status =
+ new Status(IStatus.ERROR, CodeUtilsActivator.PLUGIN_ID,
+ CodeUtilsNLS.ERR_NewBuildingBlocksWizardPage_FileNameTooLong);
+ }
+ }
+
+ return status;
+ }
+
+ /**
+ * Checks for cross package/class collision among source folders
+ *
+ * @return true if there is any collision or false otherwise
+ */
+ private boolean packageAndClassExist()
+ {
+ boolean exists = false;
+
+ try
+ {
+ if ((getJavaProject() != null) && getJavaProject().isOpen())
+ {
+ IPackageFragmentRoot[] roots = getJavaProject().getPackageFragmentRoots();
+
+ if (roots != null)
+ {
+ for (IPackageFragmentRoot root : roots)
+ {
+ if ((root.getKind() & IPackageFragmentRoot.K_SOURCE) == IPackageFragmentRoot.K_SOURCE)
+ {
+ IPackageFragment pack = root.getPackageFragment(getPackageText());
+
+ if ((pack != null) && pack.exists())
+ {
+ IJavaElement classes[] = pack.getChildren();
+
+ if (classes != null)
+ {
+ for (IJavaElement clazz : classes)
+ {
+ if (clazz.getElementName().equals(
+ getTypeName() + JAVA_EXTENSION))
+ {
+ exists = true;
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ if (exists)
+ {
+ break;
+ }
+ }
+ }
+ }
+ }
+ catch (JavaModelException e)
+ {
+ // Do nothing
+ StudioLogger.error(NewBuildingBlocksWizardPage.class, e.getLocalizedMessage(), e);
+ }
+
+ return exists;
+ }
+
+ /**
+ * Checks if the current building block that is being created can be written
+ * to the file system without throw a "file name too long" error
+ *
+ * @return true if the building block can be written or false otherwise
+ */
+ private boolean isTooLongOnFileSystem()
+ {
+ boolean isTooLong = false;
+
+ if (getPackageFragment() != null)
+ {
+ String javaFileName =
+ getPackageFragment().getCompilationUnit(getTypeName() + JAVA_EXTENSION)
+ .getResource().getLocation().toFile().getPath();
+
+ isTooLong = javaFileName.length() > MAX_PATH_SIZE;
+ }
+
+ return isTooLong;
+ }
+
+ /**
+ * Checks if the wizard can be opened. If the wizard cannot be opened, an
+ * error message is displayed.
+ *
+ * @return true if the wizard can be opened or false otherwise.
+ */
+ private boolean canOpen()
+ {
+ boolean canOpen = true;
+
+ if (getBuildBlock().getProject() != null)
+ {
+ IStatus status = null;
+
+ try
+ {
+ AndroidManifestFile manifestFile =
+ AndroidProjectManifestFile.getFromProject(getBuildBlock().getProject());
+
+ if (manifestFile.hasErrors())
+ {
+ status =
+ new MultiStatus(
+ CodeUtilsActivator.PLUGIN_ID,
+ IStatus.ERROR,
+ manifestFile.getErrors(),
+ CodeUtilsNLS.ERR_NewBuildingBlocksWizardPage_OneOrMoreErrorsWhenParsingManifest,
+ null);
+ }
+ }
+ catch (AndroidException e)
+ {
+ status =
+ new Status(IStatus.ERROR, CodeUtilsActivator.PLUGIN_ID,
+ e.getLocalizedMessage());
+ }
+ catch (CoreException e)
+ {
+ status = e.getStatus();
+ }
+
+ if (status != null)
+ {
+ canOpen = false;
+
+ EclipseUtils
+ .showErrorDialog(
+ CodeUtilsNLS.UI_GenericErrorDialogTitle,
+ CodeUtilsNLS.ERR_NewBuildingBlocksWizardPage_CannotProceedWithTheBuildingBlockCreation,
+ status);
+ }
+ }
+
+ return canOpen;
+ }
+}
diff --git a/src/plugins/android.codeutils/src/com/motorola/studio/android/wizards/buildingblocks/NewLauncherWizardPage.java b/src/plugins/android.codeutils/src/com/motorola/studio/android/wizards/buildingblocks/NewLauncherWizardPage.java
new file mode 100644
index 0000000..709b063
--- /dev/null
+++ b/src/plugins/android.codeutils/src/com/motorola/studio/android/wizards/buildingblocks/NewLauncherWizardPage.java
@@ -0,0 +1,344 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.wizards.buildingblocks;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.eclipse.jface.dialogs.Dialog;
+import org.eclipse.jface.dialogs.IInputValidator;
+import org.eclipse.jface.dialogs.InputDialog;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Group;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.List;
+import org.eclipse.swt.widgets.Listener;
+
+import com.motorola.studio.android.codeutils.i18n.CodeUtilsNLS;
+import com.motorola.studio.android.common.exception.AndroidException;
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.common.utilities.AndroidUtils;
+import com.motorola.studio.android.model.BuildingBlockModel;
+import com.motorola.studio.android.model.Launcher;
+import com.motorola.studio.android.wizards.elements.AddInputRemoveButtons;
+
+/**
+ * Abstract class to contribute with the Building Blocks Wizards. This class
+ * creates the controls to add Intent Filters and Categories to the
+ * Building Blocks
+ */
+public abstract class NewLauncherWizardPage extends NewBuildingBlocksWizardPage
+{
+ private List activityActions;
+
+ private AddInputRemoveButtons addRemoveActionButtons;
+
+ private AddInputRemoveButtons addRemoveCategoryButtons;
+
+ private List activityCategories;
+
+ private class InputDialogValidator implements IInputValidator
+ {
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.dialogs.IInputValidator#isValid(java.lang.String)
+ */
+ public String isValid(String newText)
+ {
+ String message = null;
+ if (((newText == null) || (newText.length() == 0) || (newText.contains(" ")))) //$NON-NLS-1$
+ {
+ message = CodeUtilsNLS.NewLauncherWizardPage_InputDialogValidationMessage;
+ }
+ return message;
+ }
+ }
+
+ /**
+ * Default constructor
+ *
+ * @param buildBlock The building block model
+ * @param pageName The page name
+ */
+ protected NewLauncherWizardPage(BuildingBlockModel buildBlock, String pageName)
+ {
+ super(buildBlock, pageName);
+ }
+
+ /**
+ * Creates the "Action" section on the wizard
+ *
+ * @param composite the wizard composite
+ */
+ protected void createActionsControls(Composite composite)
+ {
+ Label activityActionsLabel = new Label(composite, SWT.NONE);
+ activityActionsLabel.setText(CodeUtilsNLS.UI_NewLauncherWizardPage_ActionLabel);
+ GridData gridData = new GridData();
+ gridData.widthHint = convertWidthInCharsToPixels(12);
+ gridData.verticalAlignment = GridData.BEGINNING;
+ activityActionsLabel.setLayoutData(gridData);
+
+ activityActions = new List(composite, SWT.BORDER | SWT.V_SCROLL | SWT.H_SCROLL);
+ activityActions.setItems(getBuildBlock().getIntentFilterActionsAsArray());
+ gridData = new GridData(SWT.FILL, SWT.FILL, true, true, 2, 1);
+ gridData.heightHint = convertHeightInCharsToPixels(3);
+ activityActions.setLayoutData(gridData);
+ addRemoveActionButtons = new AddInputRemoveButtons(composite);
+ setButtonLayoutData(addRemoveActionButtons.getAddButton());
+ setButtonLayoutData(addRemoveActionButtons.getInputButton());
+ setButtonLayoutData(addRemoveActionButtons.getRemoveButton());
+
+ addRemoveActionButtons.getAddButton().addListener(SWT.Selection, new Listener()
+ {
+ public void handleEvent(Event arg0)
+ {
+ Set<String> categorySet =
+ new HashSet<String>(Arrays.asList(getIntentFiltersActions()));
+
+ try
+ {
+ categorySet.removeAll(getBuildBlock().getIntentFilterActions());
+ }
+ catch (Exception e)
+ {
+ StudioLogger.error(NewLauncherWizardPage.class, e.getLocalizedMessage(), e);
+ }
+ FilteredActionsSelectionDialog dialog =
+ new FilteredActionsSelectionDialog(getShell(), categorySet);
+ dialog.setInitialPattern("**");
+ dialog.setTitle(CodeUtilsNLS.UI_NewLauncherWizardPage_ActionSelectionDialogTitle);
+ dialog.setMessage(CodeUtilsNLS.UI_NewLauncherWizardPage_ActionSelectionDialogMessage);
+
+ if (Dialog.OK == dialog.open())
+ {
+ for (Object result : dialog.getResult())
+ {
+ getBuildBlock().addIntentFilterAction((String) result);
+ }
+ activityActions.setItems(getBuildBlock().getIntentFilterActionsAsArray());
+ addRemoveActionButtons.getRemoveButton().setEnabled(
+ activityActions.getSelectionCount() > 0);
+ updateStatus(getBuildBlock().getStatus());
+ }
+ }
+
+ });
+ addRemoveActionButtons.getInputButton().addListener(SWT.Selection, new Listener()
+ {
+ public void handleEvent(Event event)
+ {
+ InputDialog dialog =
+ new InputDialog(getShell(),
+ CodeUtilsNLS.NewLauncherWizardPage_ActionTypeDialogTitle,
+ CodeUtilsNLS.NewLauncherWizardPage_ActionTypeDialogMessage, null,
+ new InputDialogValidator());
+ int result = dialog.open();
+ if (result == InputDialog.OK)
+ {
+ String action = dialog.getValue();
+ if (action != null)
+ {
+ getBuildBlock().addIntentFilterAction(action.trim());
+ activityActions.setItems(getBuildBlock().getIntentFilterActionsAsArray());
+ addRemoveActionButtons.getRemoveButton().setEnabled(
+ activityActions.getSelectionCount() > 0);
+ updateStatus(getBuildBlock().getStatus());
+ }
+ }
+ }
+ });
+ addRemoveActionButtons.getRemoveButton()
+ .setEnabled(activityActions.getSelectionCount() > 0);
+ addRemoveActionButtons.getRemoveButton().addListener(SWT.Selection, new Listener()
+ {
+
+ public void handleEvent(Event arg0)
+ {
+ for (int selection : activityActions.getSelectionIndices())
+ {
+ getBuildBlock().removeIntentFilterAction(activityActions.getItem(selection));
+ }
+ activityActions.setItems(getBuildBlock().getIntentFilterActionsAsArray());
+ addRemoveActionButtons.getRemoveButton().setEnabled(
+ activityActions.getSelectionCount() > 0);
+ updateStatus(getBuildBlock().getStatus());
+ }
+
+ });
+ activityActions.addListener(SWT.Selection, new Listener()
+ {
+ public void handleEvent(Event arg0)
+ {
+ addRemoveActionButtons.getRemoveButton().setEnabled(
+ activityActions.getSelectionCount() > 0);
+ }
+ });
+ }
+
+ /**
+ * Creates the "Categories" section on the wizard
+ *
+ * @param composite the wizard composite
+ */
+ protected void createCategoriesControls(Composite composite)
+ {
+ GridData gridData;
+ Label activitycategoriesLabel = new Label(composite, SWT.NONE);
+ activitycategoriesLabel.setText(CodeUtilsNLS.UI_NewLauncherWizardPage_CategoryLabel);
+ gridData = new GridData();
+ gridData.verticalAlignment = GridData.BEGINNING;
+ activitycategoriesLabel.setLayoutData(gridData);
+
+ activityCategories = new List(composite, SWT.BORDER | SWT.V_SCROLL | SWT.H_SCROLL);
+ activityCategories.setItems(getBuildBlock().getIntentFilterCategoriesAsArray());
+ gridData = new GridData(SWT.FILL, SWT.FILL, true, true, 2, 1);
+ gridData.heightHint = convertHeightInCharsToPixels(3);
+ activityCategories.setLayoutData(gridData);
+
+ addRemoveCategoryButtons = new AddInputRemoveButtons(composite);
+ setButtonLayoutData(addRemoveCategoryButtons.getAddButton());
+ setButtonLayoutData(addRemoveCategoryButtons.getInputButton());
+ setButtonLayoutData(addRemoveCategoryButtons.getRemoveButton());
+
+ addRemoveCategoryButtons.getAddButton().addListener(SWT.Selection, new Listener()
+ {
+ public void handleEvent(Event arg0)
+ {
+ Set<String> categorySet = new HashSet<String>(0);
+ try
+ {
+ categorySet =
+ new HashSet<String>(Arrays.asList(AndroidUtils
+ .getIntentFilterCategories(getBuildBlock().getProject())));
+ }
+ catch (AndroidException e)
+ {
+ setErrorMessage(e.getMessage());
+ }
+
+ categorySet.removeAll(getBuildBlock().getIntentFilterCategories());
+
+ FilteredActionsSelectionDialog dialog =
+ new FilteredActionsSelectionDialog(getShell(), categorySet);
+ dialog.setInitialPattern("**");
+ dialog.setTitle(CodeUtilsNLS.UI_NewLauncherWizardPage_CategorySelectionDialogTitle);
+ dialog.setMessage(CodeUtilsNLS.UI_NewLauncherWizardPage_CategorySelectionDialogMessage);
+
+ if (Dialog.OK == dialog.open())
+ {
+ for (Object result : dialog.getResult())
+ {
+ getBuildBlock().addIntentFilterCategories((String) result);
+ }
+ activityCategories.setItems(getBuildBlock().getIntentFilterCategoriesAsArray());
+ addRemoveCategoryButtons.getRemoveButton().setEnabled(
+ activityCategories.getSelectionCount() > 0);
+ updateStatus(getBuildBlock().getStatus());
+ }
+ }
+ });
+ addRemoveCategoryButtons.getInputButton().addListener(SWT.Selection, new Listener()
+ {
+ public void handleEvent(Event event)
+ {
+ InputDialog dialog =
+ new InputDialog(getShell(),
+ CodeUtilsNLS.NewLauncherWizardPage_CategoryTypeDialogTitle,
+ CodeUtilsNLS.NewLauncherWizardPage_CategoryTypeDialogMessage, null,
+ new InputDialogValidator());
+ int result = dialog.open();
+ if (result == InputDialog.OK)
+ {
+ String action = dialog.getValue();
+ if (action != null)
+ {
+ getBuildBlock().addIntentFilterCategories(action.trim());
+ activityCategories.setItems(getBuildBlock()
+ .getIntentFilterCategoriesAsArray());
+ addRemoveCategoryButtons.getRemoveButton().setEnabled(
+ activityCategories.getSelectionCount() > 0);
+ updateStatus(getBuildBlock().getStatus());
+ }
+ }
+ }
+ });
+ addRemoveCategoryButtons.getRemoveButton().addListener(SWT.Selection, new Listener()
+ {
+ public void handleEvent(Event arg0)
+ {
+ for (int selection : activityCategories.getSelectionIndices())
+ {
+ getBuildBlock().removeIntentFilterCategories(
+ activityCategories.getItem(selection));
+ }
+ activityCategories.setItems(getBuildBlock().getIntentFilterCategoriesAsArray());
+ addRemoveCategoryButtons.getRemoveButton().setEnabled(
+ activityCategories.getSelectionCount() > 0);
+ updateStatus(getBuildBlock().getStatus());
+ }
+ });
+ addRemoveCategoryButtons.getRemoveButton().setEnabled(
+ activityCategories.getSelectionCount() > 0);
+ activityCategories.addListener(SWT.Selection, new Listener()
+ {
+ public void handleEvent(Event arg0)
+ {
+ addRemoveCategoryButtons.getRemoveButton().setEnabled(
+ activityCategories.getSelectionCount() > 0);
+ }
+ });
+ }
+
+ /**
+ * Creates the Intent Filter group on the wizard page
+ *
+ * @param parent The wizard composite
+ */
+ protected void createIntentFilterControls(Composite parent)
+ {
+ Group intentFilterGroup = new Group(parent, SWT.NONE);
+ intentFilterGroup.setText(CodeUtilsNLS.UI_NewLauncherWizardPage_IntentFilterGroupName);
+ intentFilterGroup.setLayout(new GridLayout(4, false));
+ intentFilterGroup.setLayoutData(new GridData(GridData.FILL, GridData.FILL, false, false, 4,
+ 1));
+
+ createActionsControls(intentFilterGroup);
+ createCategoriesControls(intentFilterGroup);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.wizards.buildingblocks.NewBuildingBlocksWizardPage#getBuildBlock()
+ */
+ @Override
+ public Launcher getBuildBlock()
+ {
+ return (Launcher) super.getBuildBlock();
+ }
+
+ /**
+ * Return all intent filters actions according with the Building Block
+ * @return
+ */
+ protected abstract String[] getIntentFiltersActions();
+
+}
diff --git a/src/plugins/android.codeutils/src/com/motorola/studio/android/wizards/buildingblocks/NewProviderMainPage.java b/src/plugins/android.codeutils/src/com/motorola/studio/android/wizards/buildingblocks/NewProviderMainPage.java
new file mode 100644
index 0000000..2eb8782
--- /dev/null
+++ b/src/plugins/android.codeutils/src/com/motorola/studio/android/wizards/buildingblocks/NewProviderMainPage.java
@@ -0,0 +1,345 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.wizards.buildingblocks;
+
+import java.util.Arrays;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.jdt.core.JavaConventions;
+import org.eclipse.jdt.core.JavaCore;
+import org.eclipse.jface.dialogs.Dialog;
+import org.eclipse.jface.dialogs.IInputValidator;
+import org.eclipse.jface.dialogs.InputDialog;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.List;
+import org.eclipse.swt.widgets.Listener;
+
+import com.motorola.studio.android.codeutils.CodeUtilsActivator;
+import com.motorola.studio.android.codeutils.i18n.CodeUtilsNLS;
+import com.motorola.studio.android.model.ContentProvider;
+import com.motorola.studio.android.wizards.elements.InputRemoveButtons;
+
+/**
+ * Class that implements the Content Provider Wizard Main Page.
+ */
+public class NewProviderMainPage extends NewBuildingBlocksWizardPage
+{
+ private List authoritiesList;
+
+ private Button useDefault;
+
+ private static final String NEW_PROVIDER_HELP = CodeUtilsActivator.PLUGIN_ID + ".newcontprov";
+
+ protected int defaultIndex = -1;
+
+ /*
+ * Private class that implements an Input Validator for the Input Button.
+ */
+ private class InputButtonValidator implements IInputValidator
+ {
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.dialogs.IInputValidator#isValid(java.lang.String)
+ */
+ public String isValid(String newText)
+ {
+ IStatus status =
+ JavaConventions.validatePackageName(newText, JavaCore.VERSION_1_7,
+ JavaCore.VERSION_1_7);
+ if (status.isOK() && getBuildBlock().getAuthoritiesList().contains(newText))
+ {
+ status =
+ new Status(IStatus.ERROR, CodeUtilsActivator.PLUGIN_ID,
+ CodeUtilsNLS.ERR_NewProviderMainPage_ErrorMessageAlreadyExists);
+ }
+
+ if (status.isOK())
+ {
+ Pattern pattern = Pattern.compile("[a-z0-9\\._]+");
+ Matcher matcher = pattern.matcher(newText);
+
+ if (!matcher.matches())
+ {
+ status =
+ new Status(
+ IStatus.ERROR,
+ CodeUtilsActivator.PLUGIN_ID,
+ CodeUtilsNLS.ERR_NewProviderMainPage_InvalidCharactersInAuthority);
+ }
+ }
+
+ if (status.isOK() && newText.equalsIgnoreCase(getBuildBlock().getDefaultAuthority()))
+ {
+ status =
+ new Status(IStatus.ERROR, CodeUtilsActivator.PLUGIN_ID,
+ CodeUtilsNLS.ERR_NewProviderMainPage_ErrorMessageDefaultName);
+ }
+ return status.isOK() ? null : status.getMessage();
+ }
+ }
+
+ /*
+ * Private class that implements a Listener for the Add Button.
+ */
+ private class InputButtonListener implements Listener
+ {
+ /* (non-Javadoc)
+ * @see org.eclipse.swt.widgets.Listener#handleEvent(org.eclipse.swt.widgets.Event)
+ */
+ public void handleEvent(Event arg0)
+ {
+ InputDialog dialog =
+ new InputDialog(getShell(),
+ CodeUtilsNLS.UI_NewProviderMainPage_TitleNewAuthority,
+ CodeUtilsNLS.UI_NewProviderMainPage_MessageAvoidConflicts,
+ "", new InputButtonValidator()); //$NON-NLS-1$
+ if (dialog.open() == Dialog.OK)
+ {
+ String authority = dialog.getValue();
+ authoritiesList.add(authority);
+ getBuildBlock().addAuthority(authority);
+ }
+ updateStatus(getBuildBlock().getStatus());
+ }
+ }
+
+ /*
+ * Private class that implements a Listener for the Remove Button.
+ */
+ private class RemoveButtonListener implements Listener
+ {
+ /* (non-Javadoc)
+ * @see org.eclipse.swt.widgets.Listener#handleEvent(org.eclipse.swt.widgets.Event)
+ */
+ public void handleEvent(Event arg0)
+ {
+ int selectionIndex = authoritiesList.getSelectionIndex();
+ if (selectionIndex != defaultIndex)
+ {
+ String authoritySelected = authoritiesList.getItem(selectionIndex);
+ authoritiesList.remove(selectionIndex);
+ getBuildBlock().removeAuthority(authoritySelected);
+ updateStatus(getBuildBlock().getStatus());
+ }
+ }
+ }
+
+ /*
+ * Private class that implements a Listener for the Use Default Button.
+ */
+ private class UseDefaultButtonListener implements Listener
+ {
+ /* (non-Javadoc)
+ * @see org.eclipse.swt.widgets.Listener#handleEvent(org.eclipse.swt.widgets.Event)
+ */
+ public void handleEvent(Event arg0)
+ {
+ java.util.List<String> asList = Arrays.asList(authoritiesList.getItems());
+ if (useDefault.getSelection()
+ && !asList.contains(getBuildBlock().getDefaultAuthority()))
+ {
+ getBuildBlock().addAuthority(getBuildBlock().getDefaultAuthority());
+ authoritiesList.add(getBuildBlock().getDefaultAuthority(), 0);
+ defaultIndex = authoritiesList.indexOf(getBuildBlock().getDefaultAuthority());
+ }
+ else if (!useDefault.getSelection())
+ {
+ getBuildBlock().removeAuthority(getBuildBlock().getDefaultAuthority());
+ authoritiesList.remove(getBuildBlock().getDefaultAuthority());
+ defaultIndex = -1;
+ }
+ updateStatus(getBuildBlock().getStatus());
+ }
+ }
+
+ /*
+ * Private class that implements a Listener for the Authorities Listener
+ */
+ private class AuthoritiesListListener implements Listener
+ {
+ InputRemoveButtons inputRemoveButtons;
+
+ /**
+ * Default constructor
+ *
+ * @param addRemoveButtons The AddRemoveButtons composite
+ */
+ public AuthoritiesListListener(InputRemoveButtons addRemoveButtons)
+ {
+ this.inputRemoveButtons = addRemoveButtons;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.swt.widgets.Listener#handleEvent(org.eclipse.swt.widgets.Event)
+ */
+ public void handleEvent(Event arg0)
+ {
+ int selectionCount = authoritiesList.getSelectionCount();
+ boolean enabled = selectionCount > 0;
+ if (selectionCount == 1)
+ {
+ enabled =
+ !getBuildBlock().getDefaultAuthority().equals(
+ authoritiesList.getItem(authoritiesList.getSelectionIndex()));
+ }
+ inputRemoveButtons.getRemoveButton().setEnabled(enabled);
+ updateStatus(getBuildBlock().getStatus());
+ }
+ }
+
+ /**
+ * Default constructor.
+ *
+ * @param buildBlock The content provider building block model.
+ */
+ protected NewProviderMainPage(ContentProvider buildBlock)
+ {
+ super(buildBlock, CodeUtilsNLS.UI_NewProviderMainPage_WizardTitle);
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.wizards.buildingblocks.NewBuildingBlocksWizardPage#getBuildBlock()
+ */
+ @Override
+ public ContentProvider getBuildBlock()
+ {
+ return (ContentProvider) super.getBuildBlock();
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.wizards.buildingblocks.NewBuildingBlocksWizardPage#createIntermediateControls(org.eclipse.swt.widgets.Composite)
+ */
+ @Override
+ protected void createIntermediateControls(Composite parent)
+ {
+
+ Label authoritiesLabel = new Label(parent, SWT.NONE);
+ GridData gridData = new GridData();
+ gridData.verticalAlignment = GridData.BEGINNING;
+ authoritiesLabel.setLayoutData(gridData);
+ authoritiesLabel.setText(CodeUtilsNLS.UI_NewProviderMainPage_LabelAuthorities);
+ authoritiesList = new List(parent, SWT.BORDER | SWT.V_SCROLL | SWT.H_SCROLL);
+ gridData = new GridData(SWT.FILL, SWT.FILL, false, false, 2, 1);
+ gridData.heightHint = convertHeightInCharsToPixels(3);
+ authoritiesList.setLayoutData(gridData);
+
+ final InputRemoveButtons inputRemoveButtons = new InputRemoveButtons(parent);
+ setButtonLayoutData(inputRemoveButtons.getInputButton());
+ setButtonLayoutData(inputRemoveButtons.getRemoveButton());
+ inputRemoveButtons.getRemoveButton().setEnabled(false);
+ inputRemoveButtons.getInputButton().addListener(SWT.Selection, new InputButtonListener());
+ inputRemoveButtons.getRemoveButton().addListener(SWT.Selection, new RemoveButtonListener());
+ new Label(parent, SWT.NONE);
+ useDefault = new Button(parent, SWT.CHECK);
+ useDefault.setText(CodeUtilsNLS.UI_NewProviderMainPage_OptionUseDefault);
+ gridData = new GridData();
+ gridData.horizontalSpan = 3;
+ useDefault.setLayoutData(gridData);
+ useDefault.setSelection(true);
+
+ String defaultAuthority = getBuildBlock().getDefaultAuthority();
+ authoritiesList.add(defaultAuthority);
+ defaultIndex = 0;
+ getBuildBlock().addAuthority(defaultAuthority);
+
+ useDefault.addListener(SWT.Selection, new UseDefaultButtonListener());
+ authoritiesList.addListener(SWT.Selection, new AuthoritiesListListener(inputRemoveButtons));
+
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.wizards.buildingblocks.NewBuildingBlocksWizardPage#typeNameChanged()
+ */
+ @Override
+ protected IStatus typeNameChanged()
+ {
+ if (authoritiesList != null)
+ {
+ String defaultAuthority = getBuildBlock().getDefaultAuthority();
+ if (defaultIndex != -1)
+ {
+ getBuildBlock().removeAuthority(authoritiesList.getItem(defaultIndex));
+ if ((defaultAuthority != null) && (defaultAuthority.length() > 0))
+ {
+
+ if (!getBuildBlock().containsAuthority(defaultAuthority))
+ {
+ authoritiesList.setItem(defaultIndex, defaultAuthority);
+ getBuildBlock().addAuthority(defaultAuthority);
+ }
+ else
+ {
+ authoritiesList.remove(defaultIndex);
+ defaultIndex = -1;
+ }
+ }
+ else
+ {
+ authoritiesList.remove(defaultIndex);
+ defaultIndex = -1;
+ }
+ }
+ }
+ return super.typeNameChanged();
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.wizards.buildingblocks.NewBuildingBlocksWizardPage#getMethods()
+ */
+ @Override
+ protected Method[] getMethods()
+ {
+ return new Method[0];
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.wizards.buildingblocks.NewBuildingBlocksWizardPage#getDefaultMessage()
+ */
+ @Override
+ public String getDefaultMessage()
+ {
+ return CodeUtilsNLS.UI_NewProviderMainPage_SubtitleCreateContentProvider;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.wizards.buildingblocks.NewBuildingBlocksWizardPage#getWizardTitle()
+ */
+ @Override
+ public String getWizardTitle()
+ {
+ return CodeUtilsNLS.UI_NewProviderMainPage_TitleContentProvider;
+ }
+
+ /**
+ * Gets the help ID to be used for attaching
+ * context sensitive help.
+ *
+ * Classes that extends this class and want to set
+ * their on help should override this method
+ */
+ @Override
+ protected String getHelpId()
+ {
+ return NEW_PROVIDER_HELP;
+ }
+}
diff --git a/src/plugins/android.codeutils/src/com/motorola/studio/android/wizards/buildingblocks/NewProviderWizard.java b/src/plugins/android.codeutils/src/com/motorola/studio/android/wizards/buildingblocks/NewProviderWizard.java
new file mode 100644
index 0000000..7f83b81
--- /dev/null
+++ b/src/plugins/android.codeutils/src/com/motorola/studio/android/wizards/buildingblocks/NewProviderWizard.java
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.wizards.buildingblocks;
+
+import java.lang.reflect.InvocationTargetException;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.jdt.core.ICompilationUnit;
+import org.eclipse.jdt.core.JavaModelException;
+import org.eclipse.jdt.ui.JavaUI;
+import org.eclipse.jface.operation.IRunnableWithProgress;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.PartInitException;
+
+import com.motorola.studio.android.codeutils.CodeUtilsActivator;
+import com.motorola.studio.android.codeutils.i18n.CodeUtilsNLS;
+import com.motorola.studio.android.common.exception.AndroidException;
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.common.utilities.EclipseUtils;
+import com.motorola.studio.android.model.BuildingBlockModel;
+import com.motorola.studio.android.model.ContentProvider;
+
+/**
+ * Class that implements the Content Provider Wizard.
+ */
+public class NewProviderWizard extends NewBuildingBlocksWizard
+{
+ private static final String WIZBAN_ICON = "icons/wizban/new_provider_wiz.png";
+
+ private ContentProvider contentProvider = new ContentProvider();
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.wizard.Wizard#performFinish()
+ */
+ @Override
+ public boolean performFinish()
+ {
+ boolean saved = false;
+
+ try
+ {
+ DoSave doSave = new DoSave();
+
+ getContainer().run(false, false, doSave);
+
+ if (doSave.exception != null)
+ {
+ throw doSave.exception;
+ }
+ else
+ {
+ saved = doSave.saved;
+ }
+ }
+ catch (AndroidException e)
+ {
+ IStatus status =
+ new Status(IStatus.ERROR, CodeUtilsActivator.PLUGIN_ID, e.getLocalizedMessage());
+ EclipseUtils.showErrorDialog(CodeUtilsNLS.UI_GenericErrorDialogTitle,
+ CodeUtilsNLS.ERR_BuildingBlockCreation_ErrorMessage, status);
+ }
+ catch (InvocationTargetException e)
+ {
+ IStatus status =
+ new Status(IStatus.ERROR, CodeUtilsActivator.PLUGIN_ID, e.getLocalizedMessage());
+ EclipseUtils.showErrorDialog(CodeUtilsNLS.UI_GenericErrorDialogTitle,
+ CodeUtilsNLS.ERR_BuildingBlockCreation_ErrorMessage, status);
+ }
+ catch (InterruptedException e)
+ {
+ IStatus status =
+ new Status(IStatus.ERROR, CodeUtilsActivator.PLUGIN_ID, e.getLocalizedMessage());
+ EclipseUtils.showErrorDialog(CodeUtilsNLS.UI_GenericErrorDialogTitle,
+ CodeUtilsNLS.ERR_BuildingBlockCreation_ErrorMessage, status);
+ }
+
+ if (saved)
+ {
+ ICompilationUnit javaFile =
+ getBuildingBlock().getPackageFragment().getCompilationUnit(
+ getBuildingBlock().getName() + ".java");
+
+ if ((javaFile != null) && javaFile.exists())
+ {
+ try
+ {
+ JavaUI.openInEditor(javaFile);
+ }
+ catch (PartInitException e)
+ {
+ // Do nothing
+ StudioLogger.error(NewProviderWizard.class,
+ "Could not open the content provider " + getBuildingBlock().getName()
+ + " on an editor.", e);
+ }
+ catch (JavaModelException e)
+ {
+ // Do nothing
+ StudioLogger.error(NewProviderWizard.class,
+ "Could not open the content provider " + getBuildingBlock().getName()
+ + " on an editor.", e);
+ }
+ }
+ }
+
+ if (saved)
+ {
+ // Collecting usage data for statistical purposes
+ try
+ {
+ StudioLogger.collectUsageData(StudioLogger.WHAT_BUILDINGBLOCK_PROVIDER,
+ StudioLogger.KIND_BUILDINGBLOCK, StudioLogger.DESCRIPTION_DEFAULT,
+ CodeUtilsActivator.PLUGIN_ID, CodeUtilsActivator.getDefault().getBundle()
+ .getVersion().toString());
+ }
+ catch (Throwable e)
+ {
+ //Do nothing, but error on the log should never prevent app from working
+ }
+ }
+ return saved;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.IWorkbenchWizard#init(org.eclipse.ui.IWorkbench, org.eclipse.jface.viewers.IStructuredSelection)
+ */
+ public void init(IWorkbench arg0, IStructuredSelection selection)
+ {
+ setWindowTitle(CodeUtilsNLS.UI_NewProviderWizard_WizardTitle);
+ setNeedsProgressMonitor(true);
+ setDefaultPageImageDescriptor(CodeUtilsActivator.getImageDescriptor(WIZBAN_ICON));
+ contentProvider.configure(selection);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.wizard.Wizard#addPages()
+ */
+ @Override
+ public void addPages()
+ {
+ addPage(new NewProviderMainPage(contentProvider));
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.wizards.buildingblocks.NewBuildingBlocksWizard#getBuildingBlock()
+ */
+ @Override
+ protected BuildingBlockModel getBuildingBlock()
+ {
+ return contentProvider;
+ }
+
+ /*
+ * IRunnableWithProgress object to create the content provider
+ */
+ private class DoSave implements IRunnableWithProgress
+ {
+ AndroidException exception = null;
+
+ boolean saved = false;
+
+ public void run(IProgressMonitor monitor) throws InvocationTargetException,
+ InterruptedException
+ {
+ try
+ {
+ saved = getBuildingBlock().save(getContainer(), monitor);
+ }
+ catch (AndroidException e)
+ {
+ exception = e;
+ }
+ }
+ }
+}
diff --git a/src/plugins/android.codeutils/src/com/motorola/studio/android/wizards/buildingblocks/NewReceiverMainPage.java b/src/plugins/android.codeutils/src/com/motorola/studio/android/wizards/buildingblocks/NewReceiverMainPage.java
new file mode 100644
index 0000000..3fae001
--- /dev/null
+++ b/src/plugins/android.codeutils/src/com/motorola/studio/android/wizards/buildingblocks/NewReceiverMainPage.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.wizards.buildingblocks;
+
+import org.eclipse.swt.widgets.Composite;
+
+import com.motorola.studio.android.codeutils.CodeUtilsActivator;
+import com.motorola.studio.android.codeutils.i18n.CodeUtilsNLS;
+import com.motorola.studio.android.common.exception.AndroidException;
+import com.motorola.studio.android.common.utilities.AndroidUtils;
+import com.motorola.studio.android.model.Receiver;
+
+/**
+ * Class that implements the Broadcast Receiver Wizard Main Page
+ */
+public class NewReceiverMainPage extends NewLauncherWizardPage
+{
+
+ private static final String NEW_RECEIVER_HELP = CodeUtilsActivator.PLUGIN_ID + ".newbcastrcvr";
+
+ /**
+ * Default constructor
+ *
+ * @param buildBlock The broadcast receiver model
+ */
+ protected NewReceiverMainPage(Receiver buildBlock)
+ {
+ super(buildBlock, CodeUtilsNLS.UI_NewReceiverMainPage_PageTitle);
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.wizards.buildingblocks.NewBuildingBlocksWizardPage#createIntermediateControls(org.eclipse.swt.widgets.Composite)
+ */
+ @Override
+ protected void createIntermediateControls(Composite parent)
+ {
+ createIntentFilterControls(parent);
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.wizards.buildingblocks.NewLauncherWizardPage#getBuildBlock()
+ */
+ @Override
+ public Receiver getBuildBlock()
+ {
+ return (Receiver) super.getBuildBlock();
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.wizards.buildingblocks.NewBuildingBlocksWizardPage#getMethods()
+ */
+ @Override
+ protected Method[] getMethods()
+ {
+ return new Method[0];
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.wizards.buildingblocks.NewBuildingBlocksWizardPage#getDefaultMessage()
+ */
+ @Override
+ public String getDefaultMessage()
+ {
+ return CodeUtilsNLS.UI_NewReceiverMainPage_DefaultWizardDescription;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.wizards.buildingblocks.NewBuildingBlocksWizardPage#getWizardTitle()
+ */
+ @Override
+ public String getWizardTitle()
+ {
+ return CodeUtilsNLS.UI_NewReceiverMainPage_WizardTitle;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.wizards.buildingblocks.NewLauncherWizardPage#getIntentFiltersActions()
+ */
+ @Override
+ protected String[] getIntentFiltersActions()
+ {
+ String[] receiverActions = new String[0];
+ try
+ {
+ receiverActions = AndroidUtils.getReceiverActions(getBuildBlock().getProject());
+ }
+ catch (AndroidException e)
+ {
+ setErrorMessage(e.getMessage());
+ }
+ return receiverActions;
+ }
+
+ /**
+ * Gets the help ID to be used for attaching
+ * context sensitive help.
+ *
+ * Classes that extends this class and want to set
+ * their on help should override this method
+ */
+ @Override
+ protected String getHelpId()
+ {
+ return NEW_RECEIVER_HELP;
+ }
+}
diff --git a/src/plugins/android.codeutils/src/com/motorola/studio/android/wizards/buildingblocks/NewReceiverWizard.java b/src/plugins/android.codeutils/src/com/motorola/studio/android/wizards/buildingblocks/NewReceiverWizard.java
new file mode 100644
index 0000000..64fd47c
--- /dev/null
+++ b/src/plugins/android.codeutils/src/com/motorola/studio/android/wizards/buildingblocks/NewReceiverWizard.java
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.wizards.buildingblocks;
+
+import java.lang.reflect.InvocationTargetException;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.jdt.core.ICompilationUnit;
+import org.eclipse.jdt.core.JavaModelException;
+import org.eclipse.jdt.ui.JavaUI;
+import org.eclipse.jface.operation.IRunnableWithProgress;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.PartInitException;
+
+import com.motorola.studio.android.codeutils.CodeUtilsActivator;
+import com.motorola.studio.android.codeutils.i18n.CodeUtilsNLS;
+import com.motorola.studio.android.common.exception.AndroidException;
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.common.utilities.EclipseUtils;
+import com.motorola.studio.android.model.BuildingBlockModel;
+import com.motorola.studio.android.model.Receiver;
+
+/**
+ * Class that implements the Broadcast Receiver Wizard.
+ */
+public class NewReceiverWizard extends NewBuildingBlocksWizard
+{
+ private static final String WIZ_BANNER_ICON = "icons/wizban/new_receiver_wiz.png";
+
+ private Receiver receiver = new Receiver();
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.wizard.Wizard#performFinish()
+ */
+ @Override
+ public boolean performFinish()
+ {
+ boolean saved = false;
+
+ try
+ {
+ DoSave doSave = new DoSave();
+
+ getContainer().run(false, false, doSave);
+
+ if (doSave.exception != null)
+ {
+ throw doSave.exception;
+ }
+ else
+ {
+ saved = doSave.saved;
+ }
+ }
+ catch (AndroidException e)
+ {
+ IStatus status =
+ new Status(IStatus.ERROR, CodeUtilsActivator.PLUGIN_ID, e.getLocalizedMessage());
+ EclipseUtils.showErrorDialog(CodeUtilsNLS.UI_GenericErrorDialogTitle,
+ CodeUtilsNLS.ERR_BuildingBlockCreation_ErrorMessage, status);
+ }
+ catch (InvocationTargetException e)
+ {
+ IStatus status =
+ new Status(IStatus.ERROR, CodeUtilsActivator.PLUGIN_ID, e.getLocalizedMessage());
+ EclipseUtils.showErrorDialog(CodeUtilsNLS.UI_GenericErrorDialogTitle,
+ CodeUtilsNLS.ERR_BuildingBlockCreation_ErrorMessage, status);
+ }
+ catch (InterruptedException e)
+ {
+ IStatus status =
+ new Status(IStatus.ERROR, CodeUtilsActivator.PLUGIN_ID, e.getLocalizedMessage());
+ EclipseUtils.showErrorDialog(CodeUtilsNLS.UI_GenericErrorDialogTitle,
+ CodeUtilsNLS.ERR_BuildingBlockCreation_ErrorMessage, status);
+ }
+
+ if (saved)
+ {
+ ICompilationUnit javaFile =
+ getBuildingBlock().getPackageFragment().getCompilationUnit(
+ getBuildingBlock().getName() + ".java");
+
+ if ((javaFile != null) && javaFile.exists())
+ {
+ try
+ {
+ JavaUI.openInEditor(javaFile);
+ }
+ catch (PartInitException e)
+ {
+ // Do nothing
+ StudioLogger.error(NewReceiverWizard.class,
+ "Could not open the broadcast receiver " + getBuildingBlock().getName()
+ + " on an editor.", e);
+ }
+ catch (JavaModelException e)
+ {
+ // Do nothing
+ StudioLogger.error(NewReceiverWizard.class,
+ "Could not open the broadcast receiver " + getBuildingBlock().getName()
+ + " on an editor.", e);
+ }
+ }
+ }
+
+ if (saved)
+ {
+ // Collecting usage data for statistical purposes
+ try
+ {
+ StudioLogger.collectUsageData(StudioLogger.WHAT_BUILDINGBLOCK_RECEIVER,
+ StudioLogger.KIND_BUILDINGBLOCK, StudioLogger.DESCRIPTION_DEFAULT,
+ CodeUtilsActivator.PLUGIN_ID, CodeUtilsActivator.getDefault().getBundle()
+ .getVersion().toString());
+ }
+ catch (Throwable e)
+ {
+ //Do nothing, but error on the log should never prevent app from working
+ }
+ }
+
+ return saved;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.IWorkbenchWizard#init(org.eclipse.ui.IWorkbench, org.eclipse.jface.viewers.IStructuredSelection)
+ */
+ public void init(IWorkbench arg0, IStructuredSelection selection)
+ {
+ setWindowTitle(CodeUtilsNLS.UI_NewReceiverWizard_WizardTitle);
+ setNeedsProgressMonitor(true);
+ setDefaultPageImageDescriptor(CodeUtilsActivator.getImageDescriptor(WIZ_BANNER_ICON));
+ receiver.configure(selection);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.wizard.Wizard#addPages()
+ */
+ @Override
+ public void addPages()
+ {
+ addPage(new NewReceiverMainPage(receiver));
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.wizards.buildingblocks.NewBuildingBlocksWizard#getBuildingBlock()
+ */
+ @Override
+ protected BuildingBlockModel getBuildingBlock()
+ {
+ return receiver;
+ }
+
+ /*
+ * IRunnableWithProgress object to create the broadcast receiver
+ */
+ private class DoSave implements IRunnableWithProgress
+ {
+ AndroidException exception = null;
+
+ boolean saved = false;
+
+ public void run(IProgressMonitor monitor) throws InvocationTargetException,
+ InterruptedException
+ {
+ try
+ {
+ saved = getBuildingBlock().save(getContainer(), monitor);
+ }
+ catch (AndroidException e)
+ {
+ exception = e;
+ }
+ }
+ }
+}
diff --git a/src/plugins/android.codeutils/src/com/motorola/studio/android/wizards/buildingblocks/NewServiceMainPage.java b/src/plugins/android.codeutils/src/com/motorola/studio/android/wizards/buildingblocks/NewServiceMainPage.java
new file mode 100644
index 0000000..7a63913
--- /dev/null
+++ b/src/plugins/android.codeutils/src/com/motorola/studio/android/wizards/buildingblocks/NewServiceMainPage.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.wizards.buildingblocks;
+
+import com.motorola.studio.android.codeutils.CodeUtilsActivator;
+import com.motorola.studio.android.codeutils.i18n.CodeUtilsNLS;
+import com.motorola.studio.android.model.Service;
+
+/**
+ * Class that implements the Service Wizard Main Page.
+ */
+public class NewServiceMainPage extends NewBuildingBlocksWizardPage
+{
+
+ private static final String NEW_SERVICE_HELP = CodeUtilsActivator.PLUGIN_ID + ".newservice";
+
+ /**
+ * Default constructor
+ *
+ * @param service The service wizard model
+ */
+ public NewServiceMainPage(Service service)
+ {
+ super(service, CodeUtilsNLS.UI_NewServiceMainPage_WizardTitle);
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.wizards.buildingblocks.NewBuildingBlocksWizardPage#getBuildBlock()
+ */
+ @Override
+ public Service getBuildBlock()
+ {
+ return (Service) super.getBuildBlock();
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.wizards.buildingblocks.NewBuildingBlocksWizardPage#getMethods()
+ */
+ @Override
+ protected Method[] getMethods()
+ {
+ Method onCreateMethod = new Method(getBuildBlock().getOnCreateMessage())
+ {
+ @Override
+ public void handle(boolean selection)
+ {
+ getBuildBlock().setOnCreateMethod(selection);
+ }
+ };
+
+ Method onStartMethod = new Method(getBuildBlock().getOnStartMessage())
+ {
+ @Override
+ public void handle(boolean selection)
+ {
+ getBuildBlock().setOnStartMethod(selection);
+ }
+ };
+
+ Method[] methods = new Method[]
+ {
+ onCreateMethod, onStartMethod
+ };
+ return methods;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.wizards.buildingblocks.NewBuildingBlocksWizardPage#getDefaultMessage()
+ */
+ @Override
+ public String getDefaultMessage()
+ {
+ return CodeUtilsNLS.UI_NewServiceMainPage_SubtitleCreateService;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.wizards.buildingblocks.NewBuildingBlocksWizardPage#getWizardTitle()
+ */
+ @Override
+ public String getWizardTitle()
+ {
+ return CodeUtilsNLS.UI_NewServiceMainPage_TitleService;
+ }
+
+ /**
+ * Gets the help ID to be used for attaching
+ * context sensitive help.
+ *
+ * Classes that extends this class and want to set
+ * their on help should override this method
+ */
+ @Override
+ protected String getHelpId()
+ {
+ return NEW_SERVICE_HELP;
+ }
+}
diff --git a/src/plugins/android.codeutils/src/com/motorola/studio/android/wizards/buildingblocks/NewServiceWizard.java b/src/plugins/android.codeutils/src/com/motorola/studio/android/wizards/buildingblocks/NewServiceWizard.java
new file mode 100644
index 0000000..c626633
--- /dev/null
+++ b/src/plugins/android.codeutils/src/com/motorola/studio/android/wizards/buildingblocks/NewServiceWizard.java
@@ -0,0 +1,190 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.wizards.buildingblocks;
+
+import java.lang.reflect.InvocationTargetException;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.jdt.core.ICompilationUnit;
+import org.eclipse.jdt.core.JavaModelException;
+import org.eclipse.jdt.ui.JavaUI;
+import org.eclipse.jface.operation.IRunnableWithProgress;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.PartInitException;
+
+import com.motorola.studio.android.codeutils.CodeUtilsActivator;
+import com.motorola.studio.android.codeutils.i18n.CodeUtilsNLS;
+import com.motorola.studio.android.common.exception.AndroidException;
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.common.utilities.EclipseUtils;
+import com.motorola.studio.android.model.BuildingBlockModel;
+import com.motorola.studio.android.model.Service;
+
+/**
+ * Class that implements the Service Wizard.
+ */
+public class NewServiceWizard extends NewBuildingBlocksWizard
+{
+ private static final String WIZBAN_ICON = "icons/wizban/new_service_wiz.png";
+
+ private Service service = new Service();
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.wizard.Wizard#performFinish()
+ */
+ @Override
+ public boolean performFinish()
+ {
+ boolean saved = false;
+
+ try
+ {
+ DoSave doSave = new DoSave();
+
+ getContainer().run(false, false, doSave);
+
+ if (doSave.exception != null)
+ {
+ throw doSave.exception;
+ }
+ else
+ {
+ saved = doSave.saved;
+ }
+ }
+ catch (AndroidException e)
+ {
+ IStatus status =
+ new Status(IStatus.ERROR, CodeUtilsActivator.PLUGIN_ID, e.getLocalizedMessage());
+ EclipseUtils.showErrorDialog(CodeUtilsNLS.UI_GenericErrorDialogTitle,
+ CodeUtilsNLS.ERR_BuildingBlockCreation_ErrorMessage, status);
+ }
+ catch (InvocationTargetException e)
+ {
+ IStatus status =
+ new Status(IStatus.ERROR, CodeUtilsActivator.PLUGIN_ID, e.getLocalizedMessage());
+ EclipseUtils.showErrorDialog(CodeUtilsNLS.UI_GenericErrorDialogTitle,
+ CodeUtilsNLS.ERR_BuildingBlockCreation_ErrorMessage, status);
+ }
+ catch (InterruptedException e)
+ {
+ IStatus status =
+ new Status(IStatus.ERROR, CodeUtilsActivator.PLUGIN_ID, e.getLocalizedMessage());
+ EclipseUtils.showErrorDialog(CodeUtilsNLS.UI_GenericErrorDialogTitle,
+ CodeUtilsNLS.ERR_BuildingBlockCreation_ErrorMessage, status);
+ }
+
+ if (saved)
+ {
+ ICompilationUnit javaFile =
+ getBuildingBlock().getPackageFragment().getCompilationUnit(
+ getBuildingBlock().getName() + ".java");
+
+ if ((javaFile != null) && javaFile.exists())
+ {
+ try
+ {
+ JavaUI.openInEditor(javaFile);
+ }
+ catch (PartInitException e)
+ {
+ // Do nothing
+ StudioLogger.error(NewServiceWizard.class, "Could not open the service "
+ + getBuildingBlock().getName() + " on an editor.", e);
+ }
+ catch (JavaModelException e)
+ {
+ // Do nothing
+ StudioLogger.error(NewServiceWizard.class, "Could not open the service "
+ + getBuildingBlock().getName() + " on an editor.", e);
+ }
+ }
+ }
+
+ if (saved)
+ {
+ // Collecting usage data for statistical purposes
+ try
+ {
+ StudioLogger.collectUsageData(StudioLogger.WHAT_BUILDINGBLOCK_SERVICE,
+ StudioLogger.KIND_BUILDINGBLOCK, StudioLogger.DESCRIPTION_DEFAULT,
+ CodeUtilsActivator.PLUGIN_ID, CodeUtilsActivator.getDefault().getBundle()
+ .getVersion().toString());
+ }
+ catch (Throwable e)
+ {
+ //Do nothing, but error on the log should never prevent app from working
+ }
+ }
+
+ return saved;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.IWorkbenchWizard#init(org.eclipse.ui.IWorkbench, org.eclipse.jface.viewers.IStructuredSelection)
+ */
+ public void init(IWorkbench arg0, IStructuredSelection selection)
+ {
+ setWindowTitle(CodeUtilsNLS.UI_NewServiceWizard_WizardTitle);
+ setNeedsProgressMonitor(true);
+ setDefaultPageImageDescriptor(CodeUtilsActivator.getImageDescriptor(WIZBAN_ICON));
+ service.configure(selection);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.wizard.Wizard#addPages()
+ */
+ @Override
+ public void addPages()
+ {
+ addPage(new NewServiceMainPage(service));
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.wizards.buildingblocks.NewBuildingBlocksWizard#getBuildingBlock()
+ */
+ @Override
+ protected BuildingBlockModel getBuildingBlock()
+ {
+ return service;
+ }
+
+ /*
+ * IRunnableWithProgress object to create the service
+ */
+ private class DoSave implements IRunnableWithProgress
+ {
+ AndroidException exception = null;
+
+ boolean saved = false;
+
+ public void run(IProgressMonitor monitor) throws InvocationTargetException,
+ InterruptedException
+ {
+ try
+ {
+ saved = getBuildingBlock().save(getContainer(), monitor);
+ }
+ catch (AndroidException e)
+ {
+ exception = e;
+ }
+ }
+ }
+}
diff --git a/src/plugins/android.codeutils/src/com/motorola/studio/android/wizards/buildingblocks/NewWidgetProviderMainPage.java b/src/plugins/android.codeutils/src/com/motorola/studio/android/wizards/buildingblocks/NewWidgetProviderMainPage.java
new file mode 100644
index 0000000..d30eb14
--- /dev/null
+++ b/src/plugins/android.codeutils/src/com/motorola/studio/android/wizards/buildingblocks/NewWidgetProviderMainPage.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.motorola.studio.android.wizards.buildingblocks;
+
+import com.motorola.studio.android.codeutils.CodeUtilsActivator;
+import com.motorola.studio.android.codeutils.i18n.CodeUtilsNLS;
+import com.motorola.studio.android.common.exception.AndroidException;
+import com.motorola.studio.android.common.utilities.AndroidUtils;
+import com.motorola.studio.android.model.WidgetProvider;
+
+/**
+ * Class that implements the Widget Provider Wizard Main Page.
+ */
+public class NewWidgetProviderMainPage extends NewLauncherWizardPage
+{
+
+ private static final String NEW_WIDGET_PROVIDER_HELP = CodeUtilsActivator.PLUGIN_ID
+ + ".newwidgtprvd";
+
+ /**
+ * Default constructor
+ *
+ * @param buildBlock The broadcast receiver model
+ */
+ protected NewWidgetProviderMainPage(WidgetProvider buildBlock)
+ {
+ super(buildBlock, CodeUtilsNLS.UI_NewWidgetProviderMainPage_PageTitle);
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.wizards.buildingblocks.NewLauncherWizardPage#getBuildBlock()
+ */
+ @Override
+ public WidgetProvider getBuildBlock()
+ {
+ return (WidgetProvider) super.getBuildBlock();
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.wizards.buildingblocks.NewBuildingBlocksWizardPage#getMethods()
+ */
+ @Override
+ protected Method[] getMethods()
+ {
+ return new Method[0];
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.wizards.buildingblocks.NewBuildingBlocksWizardPage#getDefaultMessage()
+ */
+ @Override
+ public String getDefaultMessage()
+ {
+ return CodeUtilsNLS.UI_NewWidgetProviderMainPage_DefaultWizardDescription;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.wizards.buildingblocks.NewBuildingBlocksWizardPage#getWizardTitle()
+ */
+ @Override
+ public String getWizardTitle()
+ {
+ return CodeUtilsNLS.UI_NewWidgetProviderMainPage_WizardTitle;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.wizards.buildingblocks.NewLauncherWizardPage#getIntentFiltersActions()
+ */
+ @Override
+ protected String[] getIntentFiltersActions()
+ {
+ String[] receiverActions = new String[0];
+ try
+ {
+ receiverActions = AndroidUtils.getReceiverActions(getBuildBlock().getProject());
+ }
+ catch (AndroidException e)
+ {
+ setErrorMessage(e.getMessage());
+ }
+ return receiverActions;
+ }
+
+ /**
+ * Gets the help ID to be used for attaching
+ * context sensitive help.
+ *
+ * Classes that extends this class and want to set
+ * their on help should override this method
+ */
+ @Override
+ protected String getHelpId()
+ {
+ return NEW_WIDGET_PROVIDER_HELP;
+ }
+}
diff --git a/src/plugins/android.codeutils/src/com/motorola/studio/android/wizards/buildingblocks/NewWidgetProviderWizard.java b/src/plugins/android.codeutils/src/com/motorola/studio/android/wizards/buildingblocks/NewWidgetProviderWizard.java
new file mode 100644
index 0000000..be83a7e
--- /dev/null
+++ b/src/plugins/android.codeutils/src/com/motorola/studio/android/wizards/buildingblocks/NewWidgetProviderWizard.java
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.wizards.buildingblocks;
+
+import java.lang.reflect.InvocationTargetException;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.jdt.core.ICompilationUnit;
+import org.eclipse.jdt.core.JavaModelException;
+import org.eclipse.jdt.ui.JavaUI;
+import org.eclipse.jface.operation.IRunnableWithProgress;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.PartInitException;
+
+import com.motorola.studio.android.codeutils.CodeUtilsActivator;
+import com.motorola.studio.android.codeutils.i18n.CodeUtilsNLS;
+import com.motorola.studio.android.common.exception.AndroidException;
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.common.utilities.EclipseUtils;
+import com.motorola.studio.android.model.BuildingBlockModel;
+import com.motorola.studio.android.model.WidgetProvider;
+
+/**
+ * Class that implements the Widget Provider building block wizard.
+ */
+public class NewWidgetProviderWizard extends NewBuildingBlocksWizard
+{
+
+ private static final String WIZ_BANNER_ICON = "icons/wizban/widget_provider_block_wiz.png";
+
+ private WidgetProvider widgetProvider = new WidgetProvider();
+
+ @Override
+ protected BuildingBlockModel getBuildingBlock()
+ {
+ return widgetProvider;
+ }
+
+ @Override
+ public boolean performFinish()
+ {
+ boolean saved = false;
+
+ try
+ {
+ DoSave doSave = new DoSave();
+
+ getContainer().run(false, false, doSave);
+
+ if (doSave.exception != null)
+ {
+ throw doSave.exception;
+ }
+ else
+ {
+ saved = doSave.saved;
+ }
+ }
+ catch (AndroidException e)
+ {
+ IStatus status =
+ new Status(IStatus.ERROR, CodeUtilsActivator.PLUGIN_ID, e.getLocalizedMessage());
+ EclipseUtils.showErrorDialog(CodeUtilsNLS.UI_GenericErrorDialogTitle,
+ CodeUtilsNLS.ERR_BuildingBlockCreation_ErrorMessage, status);
+ }
+ catch (InvocationTargetException e)
+ {
+ IStatus status =
+ new Status(IStatus.ERROR, CodeUtilsActivator.PLUGIN_ID, e.getLocalizedMessage());
+ EclipseUtils.showErrorDialog(CodeUtilsNLS.UI_GenericErrorDialogTitle,
+ CodeUtilsNLS.ERR_BuildingBlockCreation_ErrorMessage, status);
+ }
+ catch (InterruptedException e)
+ {
+ IStatus status =
+ new Status(IStatus.ERROR, CodeUtilsActivator.PLUGIN_ID, e.getLocalizedMessage());
+ EclipseUtils.showErrorDialog(CodeUtilsNLS.UI_GenericErrorDialogTitle,
+ CodeUtilsNLS.ERR_BuildingBlockCreation_ErrorMessage, status);
+ }
+
+ if (saved)
+ {
+ ICompilationUnit javaFile =
+ getBuildingBlock().getPackageFragment().getCompilationUnit(
+ getBuildingBlock().getName() + ".java");
+
+ if ((javaFile != null) && javaFile.exists())
+ {
+ try
+ {
+ JavaUI.openInEditor(javaFile);
+ }
+ catch (PartInitException e)
+ {
+ // Do nothing
+ StudioLogger.error(NewWidgetProviderWizard.class,
+ "Could not open the widget provider " + getBuildingBlock().getName()
+ + " on an editor.", e);
+ }
+ catch (JavaModelException e)
+ {
+ // Do nothing
+ StudioLogger.error(NewWidgetProviderWizard.class,
+ "Could not open the widget provider " + getBuildingBlock().getName()
+ + " on an editor.", e);
+ }
+ }
+ }
+
+ if (saved)
+ {
+ // Collecting usage data for statistical purposes
+ try
+ {
+ StudioLogger.collectUsageData(StudioLogger.WHAT_BUILDINGBLOCK_WIDGET_PROVIDER,
+ StudioLogger.KIND_BUILDINGBLOCK, StudioLogger.DESCRIPTION_DEFAULT,
+ CodeUtilsActivator.PLUGIN_ID, CodeUtilsActivator.getDefault().getBundle()
+ .getVersion().toString());
+ }
+ catch (Throwable e)
+ {
+ //Do nothing, but error on the log should never prevent app from working
+ }
+ }
+
+ return saved;
+ }
+
+ public void init(IWorkbench workbench, IStructuredSelection selection)
+ {
+ setWindowTitle(CodeUtilsNLS.UI_NewWidgetProviderWizard_WizardTitle);
+ setNeedsProgressMonitor(true);
+ setDefaultPageImageDescriptor(CodeUtilsActivator.getImageDescriptor(WIZ_BANNER_ICON));
+ widgetProvider.configure(selection);
+
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.wizard.Wizard#addPages()
+ */
+ @Override
+ public void addPages()
+ {
+ addPage(new NewWidgetProviderMainPage(widgetProvider));
+ }
+
+ /*
+ * IRunnableWithProgress object to create the broadcast receiver
+ */
+ private class DoSave implements IRunnableWithProgress
+ {
+ AndroidException exception = null;
+
+ boolean saved = false;
+
+ public void run(IProgressMonitor monitor) throws InvocationTargetException,
+ InterruptedException
+ {
+ try
+ {
+ saved = getBuildingBlock().save(getContainer(), monitor);
+ }
+ catch (AndroidException e)
+ {
+ exception = e;
+ }
+ }
+ }
+
+}
diff --git a/src/plugins/android.codeutils/templates/activity_samples/action_bar/ablayout.xml b/src/plugins/android.codeutils/templates/activity_samples/action_bar/ablayout.xml
new file mode 100644
index 0000000..a439efb
--- /dev/null
+++ b/src/plugins/android.codeutils/templates/activity_samples/action_bar/ablayout.xml
@@ -0,0 +1,7 @@
+<?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/src/plugins/android.codeutils/templates/activity_samples/action_bar/abmenu.xml b/src/plugins/android.codeutils/templates/activity_samples/action_bar/abmenu.xml
new file mode 100644
index 0000000..55297cf
--- /dev/null
+++ b/src/plugins/android.codeutils/templates/activity_samples/action_bar/abmenu.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+ <!-- Menu items that will appear if there is enough UI space -->
+
+ <!-- Item represented by an icon -->
+ <item android:id="@+id/menuItem1"
+ android:title="@string/menuItem1"
+ android:icon="@drawable/actionbaricon"
+ android:showAsAction="ifRoom"/>
+
+ <!-- Item represented by a text description (title)-->
+ <item android:id="@+id/menuItem2"
+ android:title="@string/menuItem2"
+ android:showAsAction="ifRoom"/>
+
+ <!-- Item represented both by an icon and a text description (title)-->
+ <item android:id="@+id/menuItem3"
+ android:title="@string/menuItem3"
+ android:icon="@drawable/actionbaricon"
+ android:showAsAction="ifRoom|withText"/>
+
+ <!-- Menu items that will appear on the overflow menu by default -->
+
+ <item android:id="@+id/menuItem4"
+ android:icon="@drawable/actionbaricon"
+ android:title="@string/menuItem4"/>
+
+ <item android:id="@+id/menuItem5"
+ android:title="@string/menuItem5"/>
+</menu> \ No newline at end of file
diff --git a/src/plugins/android.codeutils/templates/activity_samples/action_bar/action_bar_activity.java b/src/plugins/android.codeutils/templates/activity_samples/action_bar/action_bar_activity.java
new file mode 100644
index 0000000..94b5732
--- /dev/null
+++ b/src/plugins/android.codeutils/templates/activity_samples/action_bar/action_bar_activity.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package #package_name#;
+
+import android.app.ActionBar;
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.widget.Toast;
+
+import #ManifestPackageName#.R;
+
+public class #class_name# extends Activity {
+
+ //reference to manipulate the action bar
+ ActionBar actionBar = null;
+
+ /** Called when the activity is first created. */
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.#layout_name#action_bar/ablayout.xml#);
+
+ //get reference to the action bar
+ actionBar = getActionBar();
+
+ //set title and subtitle of the action bar
+ actionBar.setTitle(getString(R.string.actionBarTitle));
+ actionBar.setSubtitle(getString(R.string.actionBarSubtitle));
+ }
+
+ /**
+ * Inflate the menu into the action bar.
+ * */
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ //inflate the xml menu into java code
+ MenuInflater inflater = getMenuInflater();
+ inflater.inflate(R.menu.#menu_name#action_bar/abmenu.xml#, menu);
+
+ return true;
+ }
+
+ /**
+ * Handle menu/action bar item selection.
+ * */
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+
+ //Variable to check whether the menu item was handled.
+ //If this method return false, then the system proceeds with normal menu processing.
+ boolean eventHandled = false;
+
+ //Example of how to handle menu/action bar item selections.
+ //Adjust, insert and/or remove case statements for your menu items accordingly.
+ switch (item.getItemId()){
+ case android.R.id.home:
+ //when app icon in action bar is clicked, show home/main activity
+ Intent intent = new Intent(this, #ManifestPackageName#.#main_activity#.class);
+ intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ startActivity(intent);
+ eventHandled = true;
+ break;
+
+ case R.id.menuItem1:
+ Toast.makeText(this, getString(R.string.menuItem1),
+ Toast.LENGTH_SHORT).show();
+ eventHandled = true;
+ break;
+
+ case R.id.menuItem2:
+ Toast.makeText(this, getString(R.string.menuItem2),
+ Toast.LENGTH_SHORT).show();
+ eventHandled = true;
+ break;
+
+ case R.id.menuItem3:
+ Toast.makeText(this, getString(R.string.menuItem3),
+ Toast.LENGTH_SHORT).show();
+ eventHandled = true;
+ break;
+
+ case R.id.menuItem4:
+ Toast.makeText(this, getString(R.string.menuItem4),
+ Toast.LENGTH_SHORT).show();
+ eventHandled = true;
+ break;
+
+ case R.id.menuItem5:
+ Toast.makeText(this, getString(R.string.menuItem5),
+ Toast.LENGTH_SHORT).show();
+ eventHandled = true;
+ break;
+
+ default:
+ eventHandled = super.onOptionsItemSelected(item);
+ }
+
+ return eventHandled;
+ }
+
+} \ No newline at end of file
diff --git a/src/plugins/android.codeutils/templates/activity_samples/action_bar/action_bar_strings.xml b/src/plugins/android.codeutils/templates/activity_samples/action_bar/action_bar_strings.xml
new file mode 100644
index 0000000..bef8770
--- /dev/null
+++ b/src/plugins/android.codeutils/templates/activity_samples/action_bar/action_bar_strings.xml
@@ -0,0 +1,9 @@
+<resources>
+ <string name="menuItem1">Item 1</string>
+ <string name="menuItem2">Item 2</string>
+ <string name="menuItem3">Item 3</string>
+ <string name="menuItem4">Item 4</string>
+ <string name="menuItem5">Item 5</string>
+ <string name="actionBarTitle">Action Bar Title</string>
+ <string name="actionBarSubtitle">Action Bar Subtitle</string>
+</resources> \ No newline at end of file
diff --git a/src/plugins/android.codeutils/templates/activity_samples/action_bar/actionbaricon.png b/src/plugins/android.codeutils/templates/activity_samples/action_bar/actionbaricon.png
new file mode 100644
index 0000000..ce9d505
--- /dev/null
+++ b/src/plugins/android.codeutils/templates/activity_samples/action_bar/actionbaricon.png
Binary files differ
diff --git a/src/plugins/android.codeutils/templates/activity_samples/action_bar_compatibility/ablayout.xml b/src/plugins/android.codeutils/templates/activity_samples/action_bar_compatibility/ablayout.xml
new file mode 100644
index 0000000..7b10254
--- /dev/null
+++ b/src/plugins/android.codeutils/templates/activity_samples/action_bar_compatibility/ablayout.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ >
+
+ <!-- Include the ActionBar layout -->
+ <include layout="@layout/actionbar"/>
+
+ <!-- Insert your layout items here -->
+
+</LinearLayout>
diff --git a/src/plugins/android.codeutils/templates/activity_samples/action_bar_compatibility/action_bar_compatibility_activity.java b/src/plugins/android.codeutils/templates/activity_samples/action_bar_compatibility/action_bar_compatibility_activity.java
new file mode 100644
index 0000000..d160577
--- /dev/null
+++ b/src/plugins/android.codeutils/templates/activity_samples/action_bar_compatibility/action_bar_compatibility_activity.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package #package_name#;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.View;
+import android.view.Window;
+import android.widget.Toast;
+
+import #ManifestPackageName#.R;
+
+public class #class_name# extends Activity {
+ /** Called when the activity is first created. */
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ //action bar substitutes the title bar
+ requestWindowFeature(Window.FEATURE_NO_TITLE);
+
+ setContentView(R.layout.#layout_name#action_bar_compatibility/ablayout.xml#);
+ }
+
+ public void onActionBarItemSelected(View v){
+
+ //Example of how to handle action bar item selections.
+ //Adjust, insert and/or remove case statements for your menu items accordingly.
+ switch (v.getId()){
+ //when app icon in action bar or action bar title is clicked, show home/main activity
+ case R.id.applicationIcon:
+ case R.id.actionBarTitle:
+ Intent intent = new Intent(this, #ManifestPackageName#.#main_activity#.class);
+ intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ startActivity(intent);
+ break;
+ case R.id.menuItem1:
+ Toast.makeText(this, getString(R.string.actionItem1), Toast.LENGTH_SHORT).show();
+ break;
+ case R.id.menuItem2:
+ Toast.makeText(this, getString(R.string.actionItem2), Toast.LENGTH_SHORT).show();
+ break;
+ case R.id.menuItem3:
+ Toast.makeText(this, getString(R.string.actionItem3), Toast.LENGTH_SHORT).show();
+ break;
+ default:
+ }
+
+ }
+
+} \ No newline at end of file
diff --git a/src/plugins/android.codeutils/templates/activity_samples/action_bar_compatibility/action_bar_compatibility_strings.xml b/src/plugins/android.codeutils/templates/activity_samples/action_bar_compatibility/action_bar_compatibility_strings.xml
new file mode 100644
index 0000000..666b821
--- /dev/null
+++ b/src/plugins/android.codeutils/templates/activity_samples/action_bar_compatibility/action_bar_compatibility_strings.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<resources>
+ <string name="actionBarTitle">Action Bar Title</string>
+ <string name="actionItem1">Action item 1</string>
+ <string name="actionItem2">Action item 2</string>
+ <string name="actionItem3">Action item 3</string>
+</resources> \ No newline at end of file
diff --git a/src/plugins/android.codeutils/templates/activity_samples/action_bar_compatibility/actionbar.xml b/src/plugins/android.codeutils/templates/activity_samples/action_bar_compatibility/actionbar.xml
new file mode 100644
index 0000000..80cb1ea
--- /dev/null
+++ b/src/plugins/android.codeutils/templates/activity_samples/action_bar_compatibility/actionbar.xml
@@ -0,0 +1,98 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Action bar layout file.
+ Adjust it to your application.
+-->
+
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:background="@android:drawable/screen_background_light"
+ android:orientation="vertical" >
+
+ <ImageButton
+ android:id="@+id/applicationIcon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentLeft="true"
+ android:background="@android:drawable/screen_background_light"
+ android:onClick="onActionBarItemSelected"
+ android:paddingBottom="2dp"
+ android:paddingTop="2dp"
+ android:src="@drawable/actionbaricon" />
+
+ <TextView
+ android:id="@+id/actionBarTitle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_centerVertical="true"
+ android:layout_marginLeft="2dp"
+ android:layout_toRightOf="@id/applicationIcon"
+ android:clickable="true"
+ android:onClick="onActionBarItemSelected"
+ android:text="@string/actionBarTitle"
+ android:textColor="@android:color/primary_text_light"
+ android:textSize="18dp"
+ android:textStyle="bold" />
+
+ <LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/linearLayout"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:layout_alignBottom="@id/applicationIcon"
+ android:layout_alignParentRight="true"
+ android:layout_alignTop="@id/applicationIcon"
+ android:background="@android:drawable/screen_background_light"
+ android:orientation="horizontal"
+ android:baselineAligned="false" >
+
+ <ImageButton
+ android:id="@+id/menuItem3"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:layout_marginLeft="2dp"
+ android:background="@android:drawable/menuitem_background"
+ android:onClick="onActionBarItemSelected"
+ android:src="@drawable/actionbaricon" />
+
+ <ImageView
+ android:id="@+id/separator2"
+ android:layout_width="1dp"
+ android:layout_height="match_parent"
+ android:layout_gravity="center_vertical"
+ android:layout_marginLeft="2dp"
+ android:scaleType="fitXY"
+ android:src="@android:drawable/divider_horizontal_dark" />
+
+ <ImageButton
+ android:id="@+id/menuItem2"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:layout_marginLeft="2dp"
+ android:background="@android:drawable/menuitem_background"
+ android:onClick="onActionBarItemSelected"
+ android:src="@drawable/actionbaricon" />
+
+ <ImageView
+ android:id="@+id/separator1"
+ android:layout_width="1dp"
+ android:layout_height="match_parent"
+ android:layout_gravity="center_vertical"
+ android:layout_marginLeft="2dp"
+ android:scaleType="fitXY"
+ android:src="@android:drawable/divider_horizontal_dark" />
+
+ <ImageButton
+ android:id="@+id/menuItem1"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:background="@android:drawable/menuitem_background"
+ android:onClick="onActionBarItemSelected"
+ android:src="@drawable/actionbaricon" />
+ </LinearLayout>
+
+</RelativeLayout> \ No newline at end of file
diff --git a/src/plugins/android.codeutils/templates/activity_samples/action_bar_compatibility/actionbaricon.png b/src/plugins/android.codeutils/templates/activity_samples/action_bar_compatibility/actionbaricon.png
new file mode 100644
index 0000000..ce9d505
--- /dev/null
+++ b/src/plugins/android.codeutils/templates/activity_samples/action_bar_compatibility/actionbaricon.png
Binary files differ
diff --git a/src/plugins/android.codeutils/templates/activity_samples/dashboard_pattern/dashboard_activity.java b/src/plugins/android.codeutils/templates/activity_samples/dashboard_pattern/dashboard_activity.java
new file mode 100644
index 0000000..b22e29d
--- /dev/null
+++ b/src/plugins/android.codeutils/templates/activity_samples/dashboard_pattern/dashboard_activity.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package #package_name#;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.widget.*;
+import android.view.*;
+
+import #ManifestPackageName#.R;
+
+public class #class_name# extends Activity {
+
+ private ViewGroup feature_1;
+ private ViewGroup feature_2;
+ private ViewGroup feature_3;
+ private ViewGroup feature_4;
+ private ViewGroup feature_5;
+ private ViewGroup feature_6;
+
+ /**
+ * @see android.app.Activity#onCreate(Bundle)
+ */
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.#layout_name#dashboard_pattern/dashboard_layout.xml#);
+
+ this.feature_1 = (ViewGroup) findViewById(R.id.feature_1);
+ this.feature_2 = (ViewGroup) findViewById(R.id.feature_2);
+ this.feature_3 = (ViewGroup) findViewById(R.id.feature_3);
+ this.feature_4 = (ViewGroup) findViewById(R.id.feature_4);
+ this.feature_5 = (ViewGroup) findViewById(R.id.feature_5);
+ this.feature_6 = (ViewGroup) findViewById(R.id.feature_6);
+
+ View.OnClickListener onClickListener = new DashboardClickListener();
+
+ this.feature_1.setOnClickListener(onClickListener);
+ this.feature_2.setOnClickListener(onClickListener);
+ this.feature_3.setOnClickListener(onClickListener);
+ this.feature_4.setOnClickListener(onClickListener);
+ this.feature_5.setOnClickListener(onClickListener);
+ this.feature_6.setOnClickListener(onClickListener);
+
+ }
+
+ private class DashboardClickListener implements View.OnClickListener{
+
+ public void onClick(View v) {
+
+ /*
+ * TODO: Replace the code below with your business logic
+ *
+ * You will probably open an activity, which can be done using a code like below:
+ * startActivity(new Intent(v.getContext(), YourActivity.class));
+ */
+
+ String msg = "You selected Feature ";
+
+ switch(v.getId()){
+ case R.id.feature_1:
+ msg += "1";
+ break;
+ case R.id.feature_2:
+ msg += "2";
+ break;
+ case R.id.feature_3:
+ msg += "3";
+ break;
+ case R.id.feature_4:
+ msg += "4";
+ break;
+ case R.id.feature_5:
+ msg += "5";
+ break;
+ case R.id.feature_6:
+ msg += "6";
+ break;
+ default:
+ // none
+ }
+
+ Toast.makeText(v.getContext(), msg, Toast.LENGTH_SHORT).show();
+ }
+
+ }
+
+}
diff --git a/src/plugins/android.codeutils/templates/activity_samples/dashboard_pattern/dashboard_icon.png b/src/plugins/android.codeutils/templates/activity_samples/dashboard_pattern/dashboard_icon.png
new file mode 100644
index 0000000..4ea0b75
--- /dev/null
+++ b/src/plugins/android.codeutils/templates/activity_samples/dashboard_pattern/dashboard_icon.png
Binary files differ
diff --git a/src/plugins/android.codeutils/templates/activity_samples/dashboard_pattern/dashboard_item_background.xml b/src/plugins/android.codeutils/templates/activity_samples/dashboard_pattern/dashboard_item_background.xml
new file mode 100644
index 0000000..cad5ef8
--- /dev/null
+++ b/src/plugins/android.codeutils/templates/activity_samples/dashboard_pattern/dashboard_item_background.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android" >
+ <item
+ android:state_pressed="true"
+ android:drawable="@android:drawable/btn_default"/>
+ <item
+ android:drawable="@android:color/transparent"/>
+</selector> \ No newline at end of file
diff --git a/src/plugins/android.codeutils/templates/activity_samples/dashboard_pattern/dashboard_layout.xml b/src/plugins/android.codeutils/templates/activity_samples/dashboard_pattern/dashboard_layout.xml
new file mode 100644
index 0000000..a827d64
--- /dev/null
+++ b/src/plugins/android.codeutils/templates/activity_samples/dashboard_pattern/dashboard_layout.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:weightSum="1" android:layout_width="match_parent" android:layout_height="match_parent">
+ <TableLayout android:layout_width="match_parent" android:layout_height="match_parent">
+ <TableRow android:layout_width="wrap_content" android:layout_height="match_parent" android:layout_weight="1">
+ <LinearLayout android:layout_width="wrap_content" android:layout_weight="1" android:orientation="vertical" android:layout_height="wrap_content" android:layout_gravity="center" android:clickable="true" android:background="@drawable/#drawable_name#dashboard_pattern/dashboard_item_background.xml#" android:id="@+id/feature_1">
+ <ImageView android:src="@drawable/#drawable_name#dashboard_pattern/dashboard_icon.png#" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:id="@+id/feature_icon_1" android:background="@android:color/transparent"></ImageView>
+ <TextView android:layout_gravity="center" android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/feature_name_1" android:textAppearance="?android:attr/textAppearanceMedium" android:textStyle="bold" android:text="@string/feature_1"></TextView>
+ </LinearLayout>
+ <LinearLayout android:layout_weight="1" android:layout_width="wrap_content" android:orientation="vertical" android:layout_height="wrap_content" android:layout_gravity="center" android:clickable="true" android:background="@drawable/#drawable_name#dashboard_pattern/dashboard_item_background.xml#" android:id="@+id/feature_2">
+ <ImageView android:src="@drawable/#drawable_name#dashboard_pattern/dashboard_icon.png#" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:id="@+id/feature_icon_2" android:background="@android:color/transparent"></ImageView>
+ <TextView android:textAppearance="?android:attr/textAppearanceMedium" android:layout_gravity="center" android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/feature_name_2" android:textStyle="bold" android:text="@string/feature_2"></TextView>
+ </LinearLayout>
+ </TableRow>
+ <TableRow android:layout_width="wrap_content" android:layout_height="match_parent" android:layout_weight="1">
+ <LinearLayout android:layout_width="wrap_content" android:layout_weight="1" android:orientation="vertical" android:layout_height="wrap_content" android:layout_gravity="center" android:clickable="true" android:background="@drawable/#drawable_name#dashboard_pattern/dashboard_item_background.xml#" android:id="@+id/feature_3">
+ <ImageView android:src="@drawable/#drawable_name#dashboard_pattern/dashboard_icon.png#" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:id="@+id/feature_icon_3" android:background="@android:color/transparent"></ImageView>
+ <TextView android:textAppearance="?android:attr/textAppearanceMedium" android:layout_gravity="center" android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/feature_name_3" android:textStyle="bold" android:text="@string/feature_3"></TextView>
+ </LinearLayout>
+ <LinearLayout android:layout_width="wrap_content" android:layout_weight="1" android:orientation="vertical" android:layout_height="wrap_content" android:layout_gravity="center" android:clickable="true" android:background="@drawable/#drawable_name#dashboard_pattern/dashboard_item_background.xml#" android:id="@+id/feature_4">
+ <ImageView android:src="@drawable/#drawable_name#dashboard_pattern/dashboard_icon.png#" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:id="@+id/feature_icon_4" android:background="@android:color/transparent"></ImageView>
+ <TextView android:textAppearance="?android:attr/textAppearanceMedium" android:layout_gravity="center" android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/feature_name_4" android:textStyle="bold" android:text="@string/feature_4"></TextView>
+ </LinearLayout>
+ </TableRow>
+ <TableRow android:layout_width="wrap_content" android:layout_height="match_parent" android:layout_weight="1">
+ <LinearLayout android:layout_width="wrap_content" android:layout_weight="1" android:orientation="vertical" android:layout_height="wrap_content" android:layout_gravity="center" android:clickable="true" android:background="@drawable/#drawable_name#dashboard_pattern/dashboard_item_background.xml#" android:id="@+id/feature_5">
+ <ImageView android:src="@drawable/#drawable_name#dashboard_pattern/dashboard_icon.png#" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:id="@+id/feature_icon_5" android:background="@android:color/transparent"></ImageView>
+ <TextView android:textAppearance="?android:attr/textAppearanceMedium" android:layout_gravity="center" android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/feature_name_5" android:textStyle="bold" android:text="@string/feature_5"></TextView>
+ </LinearLayout>
+ <LinearLayout android:layout_width="wrap_content" android:layout_weight="1" android:orientation="vertical" android:layout_height="wrap_content" android:layout_gravity="center" android:clickable="true" android:background="@drawable/#drawable_name#dashboard_pattern/dashboard_item_background.xml#" android:id="@+id/feature_6">
+ <ImageView android:src="@drawable/#drawable_name#dashboard_pattern/dashboard_icon.png#" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:id="@+id/feature_icon_6" android:background="@android:color/transparent"></ImageView>
+ <TextView android:textAppearance="?android:attr/textAppearanceMedium" android:layout_gravity="center" android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/feature_name_6" android:textStyle="bold" android:text="@string/feature_6"></TextView>
+ </LinearLayout>
+ </TableRow>
+ </TableLayout>
+
+</LinearLayout>
diff --git a/src/plugins/android.codeutils/templates/activity_samples/dashboard_pattern/dashboard_layout_land.xml b/src/plugins/android.codeutils/templates/activity_samples/dashboard_pattern/dashboard_layout_land.xml
new file mode 100644
index 0000000..e01acfa
--- /dev/null
+++ b/src/plugins/android.codeutils/templates/activity_samples/dashboard_pattern/dashboard_layout_land.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:weightSum="1" android:layout_width="match_parent" android:layout_height="match_parent">
+ <TableLayout android:layout_width="match_parent" android:layout_height="match_parent">
+ <TableRow android:layout_width="wrap_content" android:layout_height="match_parent" android:layout_weight="1">
+ <LinearLayout android:layout_width="wrap_content" android:layout_weight="1" android:orientation="vertical" android:layout_height="wrap_content" android:layout_gravity="center" android:clickable="true" android:background="@drawable/#drawable_name#dashboard_pattern/dashboard_item_background.xml#" android:id="@+id/feature_1">
+ <ImageView android:src="@drawable/#drawable_name#dashboard_pattern/dashboard_icon.png#" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_vertical|center_horizontal" android:id="@+id/feature_icon_1" android:background="@android:color/transparent"></ImageView>
+ <TextView android:textAppearance="?android:attr/textAppearanceMedium" android:layout_gravity="center" android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/feature_name_1" android:textStyle="bold" android:text="@string/feature_1"></TextView>
+ </LinearLayout>
+ <LinearLayout android:layout_weight="1" android:layout_width="wrap_content" android:orientation="vertical" android:layout_height="wrap_content" android:layout_gravity="center" android:clickable="true" android:background="@drawable/#drawable_name#dashboard_pattern/dashboard_item_background.xml#" android:id="@+id/feature_2">
+ <ImageView android:src="@drawable/#drawable_name#dashboard_pattern/dashboard_icon.png#" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:id="@+id/feature_icon_2" android:background="@android:color/transparent"></ImageView>
+ <TextView android:textAppearance="?android:attr/textAppearanceMedium" android:layout_gravity="center" android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/feature_name_2" android:textStyle="bold" android:text="@string/feature_2"></TextView>
+ </LinearLayout>
+ <LinearLayout android:layout_weight="1" android:layout_width="wrap_content" android:orientation="vertical" android:layout_height="wrap_content" android:layout_gravity="center" android:clickable="true" android:background="@drawable/#drawable_name#dashboard_pattern/dashboard_item_background.xml#" android:id="@+id/feature_3">
+ <ImageView android:src="@drawable/#drawable_name#dashboard_pattern/dashboard_icon.png#" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:id="@+id/feature_icon_3" android:background="@android:color/transparent"></ImageView>
+ <TextView android:textAppearance="?android:attr/textAppearanceMedium" android:layout_gravity="center" android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/feature_name_3" android:textStyle="bold" android:text="@string/feature_3"></TextView>
+ </LinearLayout>
+ </TableRow>
+ <TableRow android:layout_width="wrap_content" android:layout_height="match_parent" android:layout_weight="1">
+ <LinearLayout android:layout_width="wrap_content" android:layout_weight="1" android:orientation="vertical" android:layout_height="wrap_content" android:layout_gravity="center" android:clickable="true" android:background="@drawable/#drawable_name#dashboard_pattern/dashboard_item_background.xml#" android:id="@+id/feature_4">
+ <ImageView android:src="@drawable/#drawable_name#dashboard_pattern/dashboard_icon.png#" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:id="@+id/feature_icon_4" android:background="@android:color/transparent"></ImageView>
+ <TextView android:textAppearance="?android:attr/textAppearanceMedium" android:layout_gravity="center" android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/feature_name_4" android:textStyle="bold" android:text="@string/feature_4"></TextView>
+ </LinearLayout>
+ <LinearLayout android:layout_width="wrap_content" android:layout_weight="1" android:layout_gravity="center" android:orientation="vertical" android:layout_height="wrap_content" android:clickable="true" android:background="@drawable/#drawable_name#dashboard_pattern/dashboard_item_background.xml#" android:id="@+id/feature_5">
+ <ImageView android:src="@drawable/#drawable_name#dashboard_pattern/dashboard_icon.png#" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:id="@+id/feature_icon_5" android:background="@android:color/transparent"></ImageView>
+ <TextView android:textAppearance="?android:attr/textAppearanceMedium" android:layout_gravity="center" android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/feature_name_5" android:textStyle="bold" android:text="@string/feature_5"></TextView>
+ </LinearLayout>
+ <LinearLayout android:layout_width="wrap_content" android:layout_weight="1" android:layout_gravity="center" android:orientation="vertical" android:layout_height="wrap_content" android:clickable="true" android:background="@drawable/#drawable_name#dashboard_pattern/dashboard_item_background.xml#" android:id="@+id/feature_6">
+ <ImageView android:src="@drawable/#drawable_name#dashboard_pattern/dashboard_icon.png#" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:id="@+id/feature_icon_6" android:background="@android:color/transparent"></ImageView>
+ <TextView android:textAppearance="?android:attr/textAppearanceMedium" android:layout_gravity="center" android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/feature_name_6" android:textStyle="bold" android:text="@string/feature_6"></TextView>
+ </LinearLayout>
+ </TableRow>
+ </TableLayout>
+
+</LinearLayout>
diff --git a/src/plugins/android.codeutils/templates/activity_samples/dashboard_pattern/dashboard_strings.xml b/src/plugins/android.codeutils/templates/activity_samples/dashboard_pattern/dashboard_strings.xml
new file mode 100644
index 0000000..858b96a
--- /dev/null
+++ b/src/plugins/android.codeutils/templates/activity_samples/dashboard_pattern/dashboard_strings.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <string name="feature_1">Feature 1</string>
+ <string name="feature_2">Feature 2</string>
+ <string name="feature_3">Feature 3</string>
+ <string name="feature_4">Feature 4</string>
+ <string name="feature_5">Feature 5</string>
+ <string name="feature_6">Feature 6</string>
+</resources> \ No newline at end of file
diff --git a/src/plugins/android.codeutils/templates/activity_samples/database_list/database_list_activity.java b/src/plugins/android.codeutils/templates/activity_samples/database_list/database_list_activity.java
new file mode 100644
index 0000000..ad083ac
--- /dev/null
+++ b/src/plugins/android.codeutils/templates/activity_samples/database_list/database_list_activity.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package #package_name#;
+
+import android.app.Activity;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.graphics.Color;
+import android.graphics.Typeface;
+import android.os.Bundle;
+import android.view.Gravity;
+import android.widget.TableLayout;
+import android.widget.TableRow;
+import android.widget.TextView;
+import android.widget.Toast;
+
+#imports#
+
+import #ManifestPackageName#.R;
+
+public class #class_name# extends Activity {
+ /* Called when the activity is first created. */
+
+ private final int ONE = 1;
+ private TableLayout tbl = null;
+ private TableRow row = null;
+
+ private android.widget.TableRow.LayoutParams rowParams =
+ new android.widget.TableRow.LayoutParams(
+ android.widget.TableRow.LayoutParams.#FILL_PARENT_LPARAM#,
+ android.widget.TableRow.LayoutParams.WRAP_CONTENT);
+
+ private android.widget.TableRow.LayoutParams tableParams =
+ new android.widget.TableRow.LayoutParams(
+ android.widget.TableRow.LayoutParams.#FILL_PARENT_LPARAM#,
+ android.widget.TableRow.LayoutParams.WRAP_CONTENT);
+
+ //set database location
+ public String DB_PATH = "/data/data/#package_name#/databases/";
+ public String DB_NAME = "#dbName#";
+
+ //column names
+ #constColumnsNames#
+
+ //table name
+ private final String SQL_TABLE_NAME = "#tableNameLowerCase#";
+ private boolean DISTINCT_ROWS = true;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ setContentView(R.layout.#layout_name#database_list/databaselayout.xml#);
+
+ tbl = (TableLayout) findViewById(R.id.myTableLayout);
+ tbl.setBackgroundColor(Color.BLACK);
+
+ SQLiteDatabase checkDB = null;
+ #getReadableDatabase#
+
+ try
+ {
+ loadTable(checkDB);
+ }
+ catch(Exception e)
+ {
+ Toast.makeText(getApplicationContext(), e.getMessage(), Toast.LENGTH_LONG).show();
+ }
+
+ if(checkDB != null)
+ {
+ checkDB.close();
+ }
+ }
+
+ public void loadTable(SQLiteDatabase sqliteDatabase) {
+ Cursor cursor = sqliteDatabase.query(DISTINCT_ROWS, SQL_TABLE_NAME,
+ new String[] { #columsNames# } /*columns*/, COL_1 + "=" + COL_1 /*selection*/,
+ null /*selection args*/, null /*group by*/,
+ null /*having*/, null /*order by*/, null /*limit*/);
+
+ if (cursor != null) {
+ this.startManagingCursor(cursor);
+
+ String[] columns = cursor.getColumnNames();
+ createHeader(columns);
+
+ while(cursor.move(ONE))
+ {
+ #columnGetValues#
+
+ TableRow row = new TableRow(getApplicationContext());
+ row.setLayoutParams(rowParams);
+
+ if(cursor.getPosition() % 2 == 1) {
+ row.setBackgroundColor(Color.LTGRAY);
+ }
+ else {
+ row.setBackgroundColor(Color.WHITE);
+ }
+
+ #columnAddRows#
+
+ tbl.addView(row, tableParams);
+ }
+ }
+ }
+
+ //adds new value to row
+ private void addToRow(Object obj, TableRow row)
+ {
+ TextView txt = new TextView(this);
+ if(obj != null)
+ txt.setText(obj.toString());
+ else
+ txt.setText("");
+ txt.setLayoutParams(rowParams);
+ txt.setTextColor(Color.BLACK);
+ txt.setGravity(Gravity.LEFT);
+ txt.setPadding(5, 1, 1, 1);
+ row.addView(txt);
+ }
+
+ //creates table header
+ private void createHeader(String[] names)
+ {
+ row = new TableRow(getApplicationContext());
+ row.setLayoutParams(rowParams);
+ row.setBackgroundColor(Color.GRAY);
+
+ for(String column : names)
+ {
+ TextView txt = new TextView(this);
+ txt.setText(column.toUpperCase());
+ txt.setLayoutParams(rowParams);
+ txt.setWidth(80);
+ txt.setTextColor(Color.BLACK);
+ txt.setTypeface(Typeface.DEFAULT_BOLD);
+ txt.setGravity(Gravity.LEFT);
+ //adds new field to row
+ row.addView(txt);
+ }
+
+ //adds row to table
+ tbl.addView(row, tableParams);
+ }
+} \ No newline at end of file
diff --git a/src/plugins/android.codeutils/templates/activity_samples/database_list/databaselayout.xml b/src/plugins/android.codeutils/templates/activity_samples/database_list/databaselayout.xml
new file mode 100644
index 0000000..2a53379
--- /dev/null
+++ b/src/plugins/android.codeutils/templates/activity_samples/database_list/databaselayout.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<TableLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/myTableLayout"
+ android:layout_height="match_parent"
+ android:layout_width="match_parent"
+>
+</TableLayout> \ No newline at end of file
diff --git a/src/plugins/android.codeutils/templates/activity_samples/dialog/dialog_activity.java b/src/plugins/android.codeutils/templates/activity_samples/dialog/dialog_activity.java
new file mode 100644
index 0000000..a2b39bf
--- /dev/null
+++ b/src/plugins/android.codeutils/templates/activity_samples/dialog/dialog_activity.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package #package_name#;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.content.DialogInterface;
+import android.content.res.Resources;
+import android.os.Bundle;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.Button;
+import android.widget.Toast;
+
+import #ManifestPackageName#.R;
+
+public class #class_name# extends Activity implements OnClickListener{
+
+ private int dialogId = 1;
+
+ /** Called when the activity is first created. */
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.#layout_name#dialog/dialoglayout.xml#);
+
+ Button btn = (Button) findViewById(R.id.dialog_btn);
+ btn.setOnClickListener(this);
+ showDialog(dialogId);
+ }
+
+ @Override
+ protected Dialog onCreateDialog(int id) {
+ // Items to be displayed
+ Resources res = getResources();
+ final CharSequence[] items = res.getStringArray(R.array.sample_dialog_items);
+
+ AlertDialog.Builder builder = new AlertDialog.Builder(this);
+ builder.setTitle(getString(R.string.sample_pick_item));
+ builder.setItems(items, new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int item) {
+ // Add your action here
+ Toast.makeText(getApplicationContext(), items[item], Toast.LENGTH_LONG).show();
+ }
+ });
+ AlertDialog alert = builder.create();
+ return alert;
+ }
+
+ public void onClick(View arg0) {
+ showDialog(dialogId);
+ }
+} \ No newline at end of file
diff --git a/src/plugins/android.codeutils/templates/activity_samples/dialog/dialog_strings.xml b/src/plugins/android.codeutils/templates/activity_samples/dialog/dialog_strings.xml
new file mode 100644
index 0000000..30b82c3
--- /dev/null
+++ b/src/plugins/android.codeutils/templates/activity_samples/dialog/dialog_strings.xml
@@ -0,0 +1,10 @@
+<resources>
+ <string name="sample_show_dialog">Show Dialog</string>
+ <string name="sample_pick_item">Pick an Item</string>
+ <string-array name="sample_dialog_items">
+ <item>List Item 1</item>
+ <item>List Item 2</item>
+ <item>List Item 3</item>
+ <item>List Item 4</item>
+ </string-array>
+</resources> \ No newline at end of file
diff --git a/src/plugins/android.codeutils/templates/activity_samples/dialog/dialoglayout.xml b/src/plugins/android.codeutils/templates/activity_samples/dialog/dialoglayout.xml
new file mode 100644
index 0000000..3bf63fa
--- /dev/null
+++ b/src/plugins/android.codeutils/templates/activity_samples/dialog/dialoglayout.xml
@@ -0,0 +1,11 @@
+<?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">
+ <Button
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:id="@+id/dialog_btn"
+ android:text="@string/sample_show_dialog" />
+</LinearLayout> \ No newline at end of file
diff --git a/src/plugins/android.codeutils/templates/activity_samples/dropdown_list/ddlist_strings.xml b/src/plugins/android.codeutils/templates/activity_samples/dropdown_list/ddlist_strings.xml
new file mode 100644
index 0000000..c2e9015
--- /dev/null
+++ b/src/plugins/android.codeutils/templates/activity_samples/dropdown_list/ddlist_strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <string name="sample_ddlist_select">Selected: </string>
+ <string name="sample_ddlist_pick_item">Pick an Item</string>
+ <string-array name="sample_ddlist_items">
+ <item>List Item 1</item>
+ <item>List Item 2</item>
+ <item>List Item 3</item>
+ <item>List Item 4</item>
+ <item>List Item 5</item>
+ <item>List Item 6</item>
+ <item>List Item 7</item>
+ <item>List Item 8</item>
+ <item>List Item 9</item>
+ <item>List Item 10</item>
+ <item>List Item 11</item>
+ <item>List Item 12</item>
+ <item>List Item 13</item>
+ <item>List Item 14</item>
+ <item>List Item 15</item>
+ </string-array>
+</resources>
diff --git a/src/plugins/android.codeutils/templates/activity_samples/dropdown_list/ddlistlayout.xml b/src/plugins/android.codeutils/templates/activity_samples/dropdown_list/ddlistlayout.xml
new file mode 100644
index 0000000..e79703a
--- /dev/null
+++ b/src/plugins/android.codeutils/templates/activity_samples/dropdown_list/ddlistlayout.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:padding="10dip"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="10dip"
+ android:id="@+id/prompt" />
+ <Spinner
+ android:id="@+id/spinner"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+</LinearLayout>
+
diff --git a/src/plugins/android.codeutils/templates/activity_samples/dropdown_list/dropdown_list_activity.java b/src/plugins/android.codeutils/templates/activity_samples/dropdown_list/dropdown_list_activity.java
new file mode 100644
index 0000000..b73722d
--- /dev/null
+++ b/src/plugins/android.codeutils/templates/activity_samples/dropdown_list/dropdown_list_activity.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package #package_name#;
+
+import android.app.Activity;
+import android.content.res.Resources;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.Spinner;
+import android.widget.Toast;
+import android.widget.AdapterView.OnItemSelectedListener;
+
+import #ManifestPackageName#.R;
+
+public class #class_name# extends Activity implements OnItemSelectedListener{
+
+ private String[] listItems;
+
+ @Override protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ setContentView(R.layout.#layout_name#dropdown_list/ddlistlayout.xml#);
+
+ Resources res = getResources();
+ listItems = res.getStringArray(R.array.sample_ddlist_items);
+
+ ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
+ android.R.layout.simple_spinner_item, listItems);
+
+ Spinner spinner = (Spinner) findViewById(R.id.spinner);
+
+ adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+ spinner.setAdapter(adapter);
+ spinner.setPrompt(getString(R.string.sample_ddlist_pick_item));
+ spinner.setOnItemSelectedListener(this);
+ }
+
+ public void onItemSelected(AdapterView<?> arg0, View arg1, int arg2,
+ long arg3) {
+
+ Toast.makeText(arg0.getContext(), getString(R.string.sample_ddlist_select) +
+ arg0.getItemAtPosition(arg2).toString(), Toast.LENGTH_SHORT).show();
+ }
+
+ public void onNothingSelected(AdapterView<?> arg0) {
+ //do nothing
+ }
+}
diff --git a/src/plugins/android.codeutils/templates/activity_samples/dropdown_list/preview.png b/src/plugins/android.codeutils/templates/activity_samples/dropdown_list/preview.png
new file mode 100644
index 0000000..ceb752d
--- /dev/null
+++ b/src/plugins/android.codeutils/templates/activity_samples/dropdown_list/preview.png
Binary files differ
diff --git a/src/plugins/android.codeutils/templates/activity_samples/endless_list/ListElement.java b/src/plugins/android.codeutils/templates/activity_samples/endless_list/ListElement.java
new file mode 100644
index 0000000..8030fb2
--- /dev/null
+++ b/src/plugins/android.codeutils/templates/activity_samples/endless_list/ListElement.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package #package_name#;
+
+import java.io.Serializable;
+
+/**
+ * Element which belongs to each row of the List of this view.
+ */
+class ListElement implements Serializable {
+
+ private static final long serialVersionUID = -1494059051539426231L;
+ String text;
+ Integer imageId;
+
+ /**
+ * Always create the object with a text and an image.
+ *
+ * @param text
+ * Text of the {@link ListElement}.
+ * @param imageId
+ * Image identifier of the {@link ListElement}.
+ */
+ public ListElement(String text, Integer imageId) {
+ this.text = text;
+ this.imageId = imageId;
+ }
+
+ /**
+ * Get the Text.
+ *
+ * @return Return the Text.
+ */
+ public String getText() {
+ return text;
+ }
+
+ /**
+ * Set the Text.
+ *
+ * @param text
+ * Text to be set.
+ */
+ public void setText(String text) {
+ this.text = text;
+ }
+
+ /**
+ * Get the Image identifier.
+ *
+ * @return Return the Image identifier.
+ */
+ public Integer getImageId() {
+ return imageId;
+ }
+
+ /**
+ * Set the Image identifier.
+ *
+ * @param imageId
+ * Image Identifier to be set.
+ */
+ public void setImageId(Integer imageId) {
+ this.imageId = imageId;
+ }
+
+ /**
+ * Here we consider two {@link ListElement}s to be equal when they have
+ * the same text field.
+ */
+ @Override
+ public boolean equals(Object element) {
+ return element != null && element instanceof ListElement
+ && this.text != null
+ && ((ListElement) element).text != null
+ && ((ListElement) element).text.equals(text);
+ }
+
+ @Override
+ public int hashCode() {
+ return text != null ? text.length() * 3 : 4;
+ }
+} \ No newline at end of file
diff --git a/src/plugins/android.codeutils/templates/activity_samples/endless_list/endless_list_activity.java b/src/plugins/android.codeutils/templates/activity_samples/endless_list/endless_list_activity.java
new file mode 100644
index 0000000..2ea453e
--- /dev/null
+++ b/src/plugins/android.codeutils/templates/activity_samples/endless_list/endless_list_activity.java
@@ -0,0 +1,334 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package #package_name#;
+
+import java.util.ArrayList;
+import java.util.List;
+import android.app.Activity;
+import android.app.LauncherActivity.ListItem;
+import android.app.ListActivity;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AbsListView;
+import android.widget.AbsListView.OnScrollListener;
+import android.widget.ArrayAdapter;
+import android.widget.ImageView;
+import android.widget.LinearLayout.LayoutParams;
+import android.widget.LinearLayout;
+import android.widget.ListView;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import #ManifestPackageName#.R;
+
+/**
+ * Class which holds an example of a endless list. It means a list will be
+ * displayed and it will always have items to be displayed. <br>
+ * New data is loaded asynchronously in order to provide a good user experience.
+ */
+public class #class_name# extends ListActivity {
+
+ /**
+ * Adapter for endless list.
+ */
+ private EndlessListAdapter arrayAdapter = null;
+
+ /**
+ * Variable which controls when new items start being fetched. For instance
+ * you may want to start get element when the list have 5 elements left to
+ * be displayed.
+ */
+ private int totalSizeToBe = 0;
+
+ /**
+ * Variable which controls when new items are being loaded. If this variable
+ * is true, it means items are being loaded, otherwise it is set to false.
+ */
+ private boolean isLoading = false;
+
+ /**
+ * The number of elements which are retrieved every time the service is
+ * called for retrieving elements.
+ */
+ private static final int BLOCK_SIZE = 15;
+
+ /**
+ * The number of elements left in the list when the asynchronous service
+ * will be called.
+ */
+ private static final int LOAD_AHEAD_SIZE = 5;
+
+ /**
+ * The number of items added to the <i>totalSizeToBe</i> field.
+ */
+ private static final int INCREMENT_TOTAL_MINIMUM_SIZE = 15;
+
+ /**
+ * Property to save the number of items already loaded
+ */
+ private static final String PROP_ITEM_COUNT = "item_count";
+
+ /**
+ * Property to save the top most index of the list
+ */
+ private static final String PROP_TOP_ITEM = "top_list_item";
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ arrayAdapter = new EndlessListAdapter(this, R.layout.#layout_name#endless_list/listrow.xml#, new ArrayList<ListElement>());
+
+ // download asynchronously initial list
+ Downloaditems downloadAction = new Downloaditems();
+ downloadAction.execute(new Integer[] {
+ 0, BLOCK_SIZE });
+
+ setListAdapter(arrayAdapter);
+ getListView().setOnScrollListener(new EndlessListScrollListener());
+ }
+
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ outState.putInt(PROP_ITEM_COUNT, arrayAdapter.getCount());
+ for (int i = 0; i < arrayAdapter.getCount(); i++) {
+ outState.putSerializable(Integer.toString(i),
+ arrayAdapter.getItemAt(i));
+ }
+ outState.putInt(PROP_TOP_ITEM, getListView().getFirstVisiblePosition());
+ super.onSaveInstanceState(outState);
+ }
+
+ @Override
+ protected void onRestoreInstanceState(Bundle state) {
+ super.onRestoreInstanceState(state);
+ int count = state.getInt(PROP_ITEM_COUNT);
+ for (int i = 0; i < count; i++) {
+ arrayAdapter.add((ListElement) state.get(Integer.toString(i)));
+ }
+ getListView().setSelection(state.getInt(PROP_TOP_ITEM));
+ }
+
+ @Override
+ protected void onListItemClick(ListView lv, View v, int position, long id) {
+ // your action here
+ Toast.makeText(
+ lv.getContext(),
+ getString(R.string.selected_element_message,
+ arrayAdapter.getItemAt(position).text),
+ Toast.LENGTH_SHORT).show();
+ }
+
+ /**
+ * This method represents a service which takes a long time to be executed.
+ * To simulate it, it is inserted a lag of 1 second. <br>
+ * This method basically creates a <i>cache</i> number of
+ * {@link ListElement} and returns it. It creates {@link ListElement}s with
+ * text higher than <i>itemNumber</i>.
+ *
+ * @param itemNumber
+ * Basic number to create other elements.
+ * @param numberOfItemsToBeCreated
+ * Number of items to be created.
+ *
+ * @return Returns the created list of {@link ListElement}s.
+ */
+ private List<ListElement> retrieveItems(Integer itemNumber, int numberOfItemsToBeCreated) {
+ List<ListElement> results = new ArrayList<ListElement>();
+ try {
+ // wait for 1 second in order to simulate the long service
+ Thread.sleep(1000);
+ // create items
+ for (int i = 0; i <= numberOfItemsToBeCreated; i++) {
+ String itemToBeAdded = getString(R.string.list_item_number,
+ (itemNumber + i));
+ results.add(new ListElement(itemToBeAdded, R.drawable.listicon));
+ }
+ } catch (InterruptedException e) {
+ // treat exception here
+ }
+ return results;
+ }
+
+ /**
+ * Listener which handles the endless list. It is responsible for
+ * determining when the long service will be called asynchronously.
+ */
+ class EndlessListScrollListener implements OnScrollListener {
+
+ @Override
+ public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,
+ int totalItemCount) {
+
+ // load more elements if there are LOAD_AHEAD_SIZE left in the list to be displayed
+ boolean loadMore = firstVisibleItem + visibleItemCount >= totalItemCount
+ - LOAD_AHEAD_SIZE;
+
+ /*
+ * Add one more condition: only get more results in case the list achieves
+ * a minimum size. This is necessary in order to avoid that this method is called
+ * each time the condition above is reached and the scroll is pressed.
+ */
+ if (loadMore && totalSizeToBe <= totalItemCount) {
+ totalSizeToBe += INCREMENT_TOTAL_MINIMUM_SIZE;
+ // call service
+ Downloaditems downloadAction = new Downloaditems();
+ downloadAction.execute(new Integer[] {
+ totalItemCount, BLOCK_SIZE });
+ }
+ }
+
+ @Override
+ public void onScrollStateChanged(AbsListView view, int scrollState) {
+ // do nothing
+ }
+ }
+
+ /**
+ * Asynchronous job call. This class is responsible for calling
+ * the long service and managing the <i>isLoading</i> flag.
+ */
+ class Downloaditems extends AsyncTask<Integer, Void, List<ListElement>> {
+
+ // indexes constants
+ private static final int ITEM_NUMBER_INDES = 0;
+ private static final int NUMBER_OF_ITEMS_TO_BE_CREATED_INDEX = 1;
+
+ @Override
+ protected void onPreExecute() {
+ // flag loading is being executed
+ isLoading = true;
+ }
+
+ @Override
+ protected List<ListElement> doInBackground(Integer... params) {
+
+ // execute the long service
+ return retrieveItems(params[ITEM_NUMBER_INDES], params[NUMBER_OF_ITEMS_TO_BE_CREATED_INDEX]);
+ }
+
+ @Override
+ protected void onPostExecute(List<ListElement> result) {
+ arrayAdapter.setNotifyOnChange(true);
+ for (ListElement item : result) {
+ // it is necessary to verify whether the item was already added
+ // because this job is called many times asynchronously
+ synchronized (arrayAdapter) {
+ if (!arrayAdapter.contains(item)) {
+ arrayAdapter.add(item);
+ }
+ }
+ }
+ // flag the loading is finished
+ isLoading = false;
+ }
+ }
+
+ /**
+ * Adapter which handles the list be be displayed.
+ */
+ class EndlessListAdapter extends ArrayAdapter<ListElement> {
+
+ private final Activity context;
+ private final List<ListElement> items;
+ private final int rowViewId;
+
+ /**
+ * Instantiate the Adapter for an Endless List Activity.
+ *
+ * @param context {@link Activity} which holds the endless list.
+ * @param rowviewId Identifier of the View which holds each row of
+ * the List.
+ * @param items Initial set of items which are added to list being displayed.
+ */
+ public EndlessListAdapter(Activity context, int rowviewId, List<ListElement> items) {
+ super(context, rowviewId, items);
+ this.context = context;
+ this.items = items;
+ this.rowViewId = rowviewId;
+ }
+
+ /**
+ * Check whether a {@link ListItem} is already in this adapter.
+ *
+ * @param item Item to be verified whether it is in the adapter.
+ *
+ * @return Returns <code>true</code> in case the {@link ListElement} is
+ * in the adapter, <code>false</code> otherwise.
+ */
+ public boolean contains(ListElement item) {
+ return items.contains(item);
+ }
+
+ /**
+ * Get a {@link ListElement} at a certain position.
+ *
+ * @param index Position where the {@link ListElement} is retrieved.
+ *
+ * @return Returns the {@link ListElement} give a certain position.
+ */
+ public ListElement getItemAt(int index) {
+ return items.get(index);
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+
+ ImageView imageView;
+ TextView textView;
+
+ View rowView = convertView;
+
+ /*
+ * We inflate the row using the determined layout. Also,
+ * we fill the necessary data in the text and image views.
+ */
+ LayoutInflater inflater = context.getLayoutInflater();
+ rowView = inflater.inflate(rowViewId, null, true);
+ textView = (TextView) rowView.findViewById(R.id.text01);
+ imageView = (ImageView) rowView.findViewById(R.id.img01);
+ textView.setText(items.get(position).text);
+ imageView.setImageResource(items.get(position).imageId);
+
+ /*
+ * If we reached the last position of the list and the loading
+ * operation is still being performed, set the loading message
+ * instead the normal value.
+ *
+ * Moreover, we modify the layout in order to center the loading message.
+ */
+ if (isLoading && position == items.size() - 1) {
+ textView.setText(R.string.loading_message);
+
+ // wrap content of the text view in order to center it
+ LayoutParams layoutParameters = (LayoutParams) textView.getLayoutParams();
+ layoutParameters.width = LayoutParams.WRAP_CONTENT;
+
+ // set image to the center, the text field will go along
+ imageView.setImageResource(android.R.drawable.progress_indeterminate_horizontal);
+ LinearLayout linearLayout = (LinearLayout) rowView.findViewById(R.id.vw01);
+ linearLayout.setGravity(Gravity.CENTER);
+ }
+
+ return rowView;
+ }
+ }
+} \ No newline at end of file
diff --git a/src/plugins/android.codeutils/templates/activity_samples/endless_list/endless_list_strings.xml b/src/plugins/android.codeutils/templates/activity_samples/endless_list/endless_list_strings.xml
new file mode 100644
index 0000000..6f3e21c
--- /dev/null
+++ b/src/plugins/android.codeutils/templates/activity_samples/endless_list/endless_list_strings.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<resources>
+ <string name="selected_element_message">Selected element is: %s</string>
+ <string name="list_item_number">List Item %s</string>
+ <string name="loading_message">Loading...</string>
+</resources>
diff --git a/src/plugins/android.codeutils/templates/activity_samples/endless_list/listicon.png b/src/plugins/android.codeutils/templates/activity_samples/endless_list/listicon.png
new file mode 100644
index 0000000..4bff905
--- /dev/null
+++ b/src/plugins/android.codeutils/templates/activity_samples/endless_list/listicon.png
Binary files differ
diff --git a/src/plugins/android.codeutils/templates/activity_samples/endless_list/listrow.xml b/src/plugins/android.codeutils/templates/activity_samples/endless_list/listrow.xml
new file mode 100644
index 0000000..0009dc9
--- /dev/null
+++ b/src/plugins/android.codeutils/templates/activity_samples/endless_list/listrow.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/vw01"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+
+ <ImageView android:id="@+id/img01"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:padding="5dip"/>
+
+ <TextView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/text01"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceLarge"
+ android:gravity="center_vertical"
+ android:paddingLeft="6dip"
+ android:minHeight="?android:attr/listPreferredItemHeight"
+/>
+</LinearLayout>
diff --git a/src/plugins/android.codeutils/templates/activity_samples/endless_list/preview.png b/src/plugins/android.codeutils/templates/activity_samples/endless_list/preview.png
new file mode 100644
index 0000000..d5051ee
--- /dev/null
+++ b/src/plugins/android.codeutils/templates/activity_samples/endless_list/preview.png
Binary files differ
diff --git a/src/plugins/android.codeutils/templates/activity_samples/endless_list_pull_to_refresh/ListElement.java b/src/plugins/android.codeutils/templates/activity_samples/endless_list_pull_to_refresh/ListElement.java
new file mode 100644
index 0000000..8030fb2
--- /dev/null
+++ b/src/plugins/android.codeutils/templates/activity_samples/endless_list_pull_to_refresh/ListElement.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package #package_name#;
+
+import java.io.Serializable;
+
+/**
+ * Element which belongs to each row of the List of this view.
+ */
+class ListElement implements Serializable {
+
+ private static final long serialVersionUID = -1494059051539426231L;
+ String text;
+ Integer imageId;
+
+ /**
+ * Always create the object with a text and an image.
+ *
+ * @param text
+ * Text of the {@link ListElement}.
+ * @param imageId
+ * Image identifier of the {@link ListElement}.
+ */
+ public ListElement(String text, Integer imageId) {
+ this.text = text;
+ this.imageId = imageId;
+ }
+
+ /**
+ * Get the Text.
+ *
+ * @return Return the Text.
+ */
+ public String getText() {
+ return text;
+ }
+
+ /**
+ * Set the Text.
+ *
+ * @param text
+ * Text to be set.
+ */
+ public void setText(String text) {
+ this.text = text;
+ }
+
+ /**
+ * Get the Image identifier.
+ *
+ * @return Return the Image identifier.
+ */
+ public Integer getImageId() {
+ return imageId;
+ }
+
+ /**
+ * Set the Image identifier.
+ *
+ * @param imageId
+ * Image Identifier to be set.
+ */
+ public void setImageId(Integer imageId) {
+ this.imageId = imageId;
+ }
+
+ /**
+ * Here we consider two {@link ListElement}s to be equal when they have
+ * the same text field.
+ */
+ @Override
+ public boolean equals(Object element) {
+ return element != null && element instanceof ListElement
+ && this.text != null
+ && ((ListElement) element).text != null
+ && ((ListElement) element).text.equals(text);
+ }
+
+ @Override
+ public int hashCode() {
+ return text != null ? text.length() * 3 : 4;
+ }
+} \ No newline at end of file
diff --git a/src/plugins/android.codeutils/templates/activity_samples/endless_list_pull_to_refresh/endless_list_pull_to_refresh_strings.xml b/src/plugins/android.codeutils/templates/activity_samples/endless_list_pull_to_refresh/endless_list_pull_to_refresh_strings.xml
new file mode 100644
index 0000000..3f11b19
--- /dev/null
+++ b/src/plugins/android.codeutils/templates/activity_samples/endless_list_pull_to_refresh/endless_list_pull_to_refresh_strings.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<resources>
+ <string name="last_updated">Last updated on %s</string>
+ <string name="pull_to_refresh">Pull to refresh</string>
+ <string name="release_to_refresh">Release to refresh</string>
+ <string name="selected_element_message">Selected element is: %s</string>
+ <string name="list_item_number">List Item %s</string>
+ <string name="loading_message">Loading...</string>
+</resources>
diff --git a/src/plugins/android.codeutils/templates/activity_samples/endless_list_pull_to_refresh/listheader.xml b/src/plugins/android.codeutils/templates/activity_samples/endless_list_pull_to_refresh/listheader.xml
new file mode 100644
index 0000000..3f3605b
--- /dev/null
+++ b/src/plugins/android.codeutils/templates/activity_samples/endless_list_pull_to_refresh/listheader.xml
@@ -0,0 +1,25 @@
+<?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:gravity="center_horizontal"
+ android:orientation="horizontal"
+ android:minHeight="44dp"
+ android:background="@android:drawable/title_bar">
+
+ <ProgressBar
+ android:id="@+id/progressBar"
+ android:layout_width="40dp"
+ android:layout_height="40dp"
+ android:layout_gravity="center_vertical" />
+
+ <TextView
+ android:id="@+id/lastUpdated"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:maxLines="2"
+ android:textColor="@android:color/primary_text_dark"
+ android:textSize="16dp" />
+
+</LinearLayout> \ No newline at end of file
diff --git a/src/plugins/android.codeutils/templates/activity_samples/endless_list_pull_to_refresh/listicon.png b/src/plugins/android.codeutils/templates/activity_samples/endless_list_pull_to_refresh/listicon.png
new file mode 100644
index 0000000..4bff905
--- /dev/null
+++ b/src/plugins/android.codeutils/templates/activity_samples/endless_list_pull_to_refresh/listicon.png
Binary files differ
diff --git a/src/plugins/android.codeutils/templates/activity_samples/endless_list_pull_to_refresh/listrow.xml b/src/plugins/android.codeutils/templates/activity_samples/endless_list_pull_to_refresh/listrow.xml
new file mode 100644
index 0000000..0009dc9
--- /dev/null
+++ b/src/plugins/android.codeutils/templates/activity_samples/endless_list_pull_to_refresh/listrow.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/vw01"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+
+ <ImageView android:id="@+id/img01"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:padding="5dip"/>
+
+ <TextView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/text01"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceLarge"
+ android:gravity="center_vertical"
+ android:paddingLeft="6dip"
+ android:minHeight="?android:attr/listPreferredItemHeight"
+/>
+</LinearLayout>
diff --git a/src/plugins/android.codeutils/templates/activity_samples/endless_list_pull_to_refresh/preview.png b/src/plugins/android.codeutils/templates/activity_samples/endless_list_pull_to_refresh/preview.png
new file mode 100644
index 0000000..6cce524
--- /dev/null
+++ b/src/plugins/android.codeutils/templates/activity_samples/endless_list_pull_to_refresh/preview.png
Binary files differ
diff --git a/src/plugins/android.codeutils/templates/activity_samples/endless_list_pull_to_refresh/pull_to_refresh_activity.java b/src/plugins/android.codeutils/templates/activity_samples/endless_list_pull_to_refresh/pull_to_refresh_activity.java
new file mode 100644
index 0000000..47d2a17
--- /dev/null
+++ b/src/plugins/android.codeutils/templates/activity_samples/endless_list_pull_to_refresh/pull_to_refresh_activity.java
@@ -0,0 +1,404 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package #package_name#;
+
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.List;
+
+import android.app.Activity;
+import android.app.LauncherActivity.ListItem;
+import android.app.ListActivity;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.text.format.DateFormat;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.View.OnTouchListener;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.ListView;
+import android.widget.ProgressBar;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import #ManifestPackageName#.R;
+
+/**
+ * Class which holds an example of a endless list. It means a list will be
+ * displayed and it will always have items to be displayed. <br>
+ * New data is loaded asynchronously in order to provide a good user experience.
+ */
+public class #class_name# extends ListActivity {
+
+ /**
+ * Adapter for endless list.
+ */
+ private EndlessListAdapter arrayAdapter = null;
+
+ /**
+ * The list header (Where is the loading and last updated labels)
+ */
+ private LinearLayout listHeader = null;
+
+ /**
+ * Last loaded item
+ */
+ private TextView lastUpdated = null;
+
+ /**
+ * Loading progress
+ */
+ private ProgressBar loadingProgress = null;
+
+ /**
+ * Just an integer to add items sequentially
+ */
+ private int lastAdded = 0;
+
+ /**
+ * Variable which controls when new items are being loaded. If this variable
+ * is true, it means items are being loaded, otherwise it is set to false.
+ */
+ private boolean isLoading = false;
+
+ /**
+ * The number of elements which are retrieved every time the service is
+ * called for retrieving elements.
+ */
+ private static final int BLOCK_SIZE = 2;
+
+ /**
+ * Property to save the number of items already loaded
+ */
+ private static final String PROP_ITEM_COUNT = "item_count";
+
+ /**
+ * Property to save the top most index of the list
+ */
+ private static final String PROP_TOP_ITEM = "top_list_item";
+
+ /**
+ * Property to save the time of the last update
+ */
+ private static final String PROP_LAST_UPDATED = "last_updated";
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ arrayAdapter = new EndlessListAdapter(this, R.layout.#layout_name#endless_list_pull_to_refresh/listrow.xml#,
+ new ArrayList<ListElement>());
+
+ listHeader = (LinearLayout) getLayoutInflater().inflate(
+ R.layout.#layout_name#endless_list_pull_to_refresh/listheader.xml#, null);
+ getListView().addHeaderView(listHeader);
+ lastUpdated = (TextView) listHeader.findViewById(R.id.lastUpdated);
+ loadingProgress = (ProgressBar) listHeader
+ .findViewById(R.id.progressBar);
+ loadingProgress.setVisibility(View.GONE);
+ lastUpdated.setText(R.string.last_updated);
+ lastUpdated.setText(lastUpdated.getText().toString() + DateFormat.format("EEEE, MMMM dd, yyyy",
+ Calendar.getInstance()));
+ if (savedInstanceState == null
+ || (savedInstanceState != null && savedInstanceState.getInt(
+ PROP_ITEM_COUNT, 0) == 0)) {
+ // download asynchronously initial list
+ Downloaditems downloadAction = new Downloaditems();
+ downloadAction.execute(new Integer[] { BLOCK_SIZE });
+ }
+
+ setListAdapter(arrayAdapter);
+ getListView().setOnTouchListener(new PullEventListener());
+ }
+
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ /*
+ * Save instance loaded items to be restored after rotate the device OR
+ * after you have pressed home button
+ */
+ outState.putInt(PROP_ITEM_COUNT, arrayAdapter.getCount());
+ for (int i = 0; i < arrayAdapter.getCount(); i++) {
+ outState.putSerializable(Integer.toString(i),
+ arrayAdapter.getItemAt(i));
+ }
+ outState.putInt(PROP_TOP_ITEM, getListView().getFirstVisiblePosition());
+ outState.putString(PROP_LAST_UPDATED, lastUpdated.getText().toString());
+ super.onSaveInstanceState(outState);
+ }
+
+ @Override
+ protected void onRestoreInstanceState(Bundle state) {
+ /*
+ * Restore state. Also restore lastAdded value since this class is a new
+ * instance on restore
+ */
+ super.onRestoreInstanceState(state);
+ int count = state.getInt(PROP_ITEM_COUNT);
+ for (int i = 0; i < count; i++) {
+ arrayAdapter.add((ListElement) state.get(Integer.toString(i)));
+ }
+ lastAdded = count;
+ getListView().setSelection(state.getInt(PROP_TOP_ITEM));
+ lastUpdated.setText(state.getString(PROP_LAST_UPDATED));
+ }
+
+ @Override
+ protected void onListItemClick(ListView lv, View v, int position, long id) {
+ int listIndex = position - 1;
+ if (arrayAdapter.getItemAt(listIndex) != null) {
+ //your action here
+ Toast.makeText(
+ lv.getContext(),
+ getString(R.string.selected_element_message,
+ arrayAdapter.getItemAt(listIndex).text),
+ Toast.LENGTH_SHORT).show();
+ }
+ }
+
+ /**
+ * This method represents a service which takes a long time to be executed.
+ * To simulate it, it is inserted a lag of 1 second. <br>
+ * This method basically creates a <i>cache</i> number of
+ * {@link ListElement} and returns it. It creates {@link ListElement}s with
+ * text higher than <i>itemNumber</i>.
+ *
+ * @param itemNumber
+ * Basic number to create other elements.
+ * @param numberOfItemsToBeCreated
+ * Number of items to be created.
+ *
+ * @return Returns the created list of {@link ListElement}s.
+ */
+ private List<ListElement> retrieveItems(int numberOfItemsToBeCreated) {
+ List<ListElement> results = new ArrayList<ListElement>();
+ try {
+ // wait for 2 seconds in order to simulate the long service
+ Thread.sleep(2000);
+ // create items
+ for (int i = 0; i <= numberOfItemsToBeCreated; i++) {
+ String itemToBeAdded = getString(R.string.list_item_number,
+ (lastAdded++));
+ results.add(new ListElement(itemToBeAdded, R.drawable.#drawable_name#endless_list_pull_to_refresh/listicon.png#));
+ }
+ } catch (InterruptedException e) {
+ // treat exception here
+ }
+ return results;
+ }
+
+ /**
+ * Listener which handles the endless list. It is responsible for
+ * determining when the long service will be called asynchronously.
+ */
+
+ /**
+ * Asynchronous job call. This class is responsible for calling the long
+ * service and managing the <i>isLoading</i> flag.
+ */
+ class Downloaditems extends AsyncTask<Integer, Void, List<ListElement>> {
+
+ // indexes constants
+ private static final int NUMBER_OF_ITEMS_TO_BE_CREATED_INDEX = 0;
+
+ @Override
+ protected void onPreExecute() {
+ // flag loading is being executed
+ isLoading = true;
+ loadingProgress.setVisibility(View.VISIBLE);
+ lastUpdated.setText(R.string.loading_message);
+ }
+
+ @Override
+ protected List<ListElement> doInBackground(Integer... params) {
+
+ // execute the long service
+ return retrieveItems(params[NUMBER_OF_ITEMS_TO_BE_CREATED_INDEX]);
+ }
+
+ @Override
+ protected void onPostExecute(List<ListElement> result) {
+ arrayAdapter.setNotifyOnChange(true);
+ for (ListElement item : result) {
+ // it is necessary to verify whether the item was already added
+ // because this job is called many times asynchronously
+ synchronized (arrayAdapter) {
+ if (!arrayAdapter.contains(item)) {
+ // Add items always in the beginning
+ arrayAdapter.insert(item, 0);
+ }
+ }
+ }
+
+ loadingProgress.setVisibility(View.GONE);
+ lastUpdated.setText(getString(R.string.last_updated,
+ DateFormat.format("EEEE, MMMM dd, yyyy",
+ Calendar.getInstance())));
+ // flag the loading is finished
+ isLoading = false;
+ }
+ }
+
+ /**
+ * Adapter which handles the list be be displayed.
+ */
+ class EndlessListAdapter extends ArrayAdapter<ListElement> {
+
+ private final Activity context;
+ private final List<ListElement> items;
+ private final int rowViewId;
+
+ /**
+ * Instantiate the Adapter for an Endless List Activity.
+ *
+ * @param context
+ * {@link Activity} which holds the endless list.
+ * @param rowviewId
+ * Identifier of the View which holds each row of the List.
+ * @param items
+ * Initial set of items which are added to list being
+ * displayed.
+ */
+ public EndlessListAdapter(Activity context, int rowviewId,
+ List<ListElement> items) {
+ super(context, rowviewId, items);
+ this.context = context;
+ this.items = items;
+ this.rowViewId = rowviewId;
+ }
+
+ /**
+ * Check whether a {@link ListItem} is already in this adapter.
+ *
+ * @param item
+ * Item to be verified whether it is in the adapter.
+ *
+ * @return Returns <code>true</code> in case the {@link ListElement} is
+ * in the adapter, <code>false</code> otherwise.
+ */
+ public boolean contains(ListElement item) {
+ return items.contains(item);
+ }
+
+ /**
+ * Get a {@link ListElement} at a certain position.
+ *
+ * @param index
+ * Position where the {@link ListElement} is retrieved.
+ *
+ * @return Returns the {@link ListElement} give a certain position.
+ */
+ public ListElement getItemAt(int index) {
+ return index < items.size() ? items.get(index) : null;
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+
+ ImageView imageView;
+ TextView textView;
+
+ View rowView = convertView;
+
+ /*
+ * We inflate the row using the determined layout. Also, we fill the
+ * necessary data in the text and image views.
+ */
+ LayoutInflater inflater = context.getLayoutInflater();
+ rowView = inflater.inflate(rowViewId, null, true);
+ textView = (TextView) rowView.findViewById(R.id.text01);
+ imageView = (ImageView) rowView.findViewById(R.id.img01);
+ textView.setText(items.get(position).text);
+ imageView.setImageResource(items.get(position).imageId);
+
+ return rowView;
+ }
+ }
+
+ class PullEventListener implements OnTouchListener {
+
+ private float firstEventY;
+ private CharSequence previousLastUpdatedText;
+ private boolean shouldConsiderRefresh = false;
+
+ @Override
+ public boolean onTouch(View v, MotionEvent event) {
+ switch (event.getAction()) {
+ // Save the first touch position
+ case MotionEvent.ACTION_DOWN:
+ shouldConsiderRefresh = getListView().getFirstVisiblePosition() == 0
+ && listHeader.getTop() == 0;
+ previousLastUpdatedText = lastUpdated.getText();
+ if (shouldConsiderRefresh) {
+ firstEventY = event.getY();
+ }
+ break;
+
+ // Check if we can refresh with certain delta to update texts
+ case MotionEvent.ACTION_MOVE:
+ if (shouldConsiderRefresh) {
+ float currentEventY = event.getY();
+ if (currentEventY != firstEventY) {
+ lastUpdated.setText(R.string.pull_to_refresh);
+ }
+ if (shouldRefresh(firstEventY, currentEventY) && !isLoading) {
+ lastUpdated.setText(R.string.release_to_refresh);
+ }
+ }
+ break;
+
+ // Check if we can refresh with certain delta and go back to the
+ // original text because the touch event is finished
+ case MotionEvent.ACTION_UP:
+ lastUpdated.setText(previousLastUpdatedText);
+ if (shouldConsiderRefresh) {
+ float currentEventY = event.getY();
+ if (shouldRefresh(firstEventY, currentEventY) && !isLoading) {
+ lastUpdated.setText(previousLastUpdatedText);
+ Downloaditems downloadAction = new Downloaditems();
+ downloadAction.execute(new Integer[] { BLOCK_SIZE });
+ event.setAction(MotionEvent.ACTION_CANCEL);
+ dispatchTouchEvent(event);
+ shouldConsiderRefresh = false;
+ return true;
+ }
+ }
+ break;
+ }
+ return false;
+ }
+
+ /**
+ * Check if the difference between first and current touch positions are
+ * enough to dispatch a refresh
+ *
+ * @param firstTapPosition
+ * @param currentPosition
+ * @return true if the difference is big enough to refresh, false otherwise
+ */
+ private boolean shouldRefresh(float firstTapPosition,
+ float currentPosition) {
+ int threshold = getListView().getHeight() / 4;
+ return ((currentPosition - firstTapPosition) / 2) > threshold;
+ }
+
+ }
+} \ No newline at end of file
diff --git a/src/plugins/android.codeutils/templates/activity_samples/endless_list_usingfragment/FragmentEndlessList.java b/src/plugins/android.codeutils/templates/activity_samples/endless_list_usingfragment/FragmentEndlessList.java
new file mode 100644
index 0000000..5e95df7
--- /dev/null
+++ b/src/plugins/android.codeutils/templates/activity_samples/endless_list_usingfragment/FragmentEndlessList.java
@@ -0,0 +1,358 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package #package_name#;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import android.app.Activity;
+import android.app.LauncherActivity.ListItem;
+import android.app.ListFragment;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AbsListView;
+import android.widget.AbsListView.OnScrollListener;
+import android.widget.ArrayAdapter;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.LinearLayout.LayoutParams;
+import android.widget.ListView;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import #ManifestPackageName#.R;
+
+/**
+ * Class which holds an example of a endless list. It means a list will be
+ * displayed and it will always have items to be displayed. <br>
+ * New data is loaded asynchronously in order to provide a good user experience.
+ */
+public class FragmentEndlessList extends ListFragment {
+
+ /**
+ * Adapter for endless list.
+ */
+ private EndlessListAdapter arrayAdapter = null;
+
+ /**
+ * Variable which controls when new items start being fetched. For instance
+ * you may want to start get element when the list have 5 elements left to
+ * be displayed.
+ */
+ private int totalSizeToBe = 0;
+
+ /**
+ * Variable which controls when new items are being loaded. If this variable
+ * is true, it means items are being loaded, otherwise it is set to false.
+ */
+ boolean isLoading = false;
+
+ /**
+ * The number of elements which are retrieved every time the service is
+ * called for retrieving elements.
+ */
+ private static final int BLOCK_SIZE = 15;
+
+ /**
+ * The number of elements left in the list when the asynchronous service
+ * will be called.
+ */
+ private static final int LOAD_AHEAD_SIZE = 5;
+
+ /**
+ * The number of items added to the <i>totalSizeToBe</i> field.
+ */
+ private static final int INCREMENT_TOTAL_MINIMUM_SIZE = 15;
+
+ /**
+ * Property to save the top most index of the list
+ */
+ private static final String PROP_TOP_ITEM = "top_list_item";
+
+ @Override
+ public void onActivityCreated(Bundle savedInstanceState) {
+ super.onActivityCreated(savedInstanceState);
+
+ // Populate list
+ arrayAdapter = new EndlessListAdapter(getActivity(), R.layout.#layout_name#endless_list_usingfragment/listrow.xml#,
+ new ArrayList<ListElement>());
+
+ // download asynchronously initial list
+ Downloaditems downloadAction = new Downloaditems();
+ downloadAction.execute(new Integer[] { 0, BLOCK_SIZE });
+
+ setListAdapter(arrayAdapter);
+ getListView().setOnScrollListener(new EndlessListScrollListener());
+
+ if (savedInstanceState != null) {
+ // Restore last state for top list position
+ int listTopPosition = savedInstanceState.getInt(PROP_TOP_ITEM, 0);
+
+ // load elements enough to get to the top of the list
+ downloadAction = new Downloaditems();
+ if (listTopPosition > BLOCK_SIZE) {
+ // download asynchronously initial list
+ downloadAction.execute(new Integer[] { BLOCK_SIZE,
+ listTopPosition + BLOCK_SIZE, listTopPosition });
+ }
+ }
+ }
+
+ @Override
+ public void onSaveInstanceState(Bundle state) {
+ super.onSaveInstanceState(state);
+ int listPosition = getListView().getFirstVisiblePosition();
+ if (listPosition > 0) {
+ state.putInt(PROP_TOP_ITEM, listPosition);
+ }
+ }
+
+ @Override
+ public void onListItemClick(ListView lv, View v, int position, long id) {
+ // your action here
+ Toast.makeText(
+ lv.getContext(),
+ getString(R.string.selected_element_message,
+ arrayAdapter.getItemAt(position).text),
+ Toast.LENGTH_SHORT).show();
+ }
+
+ /**
+ * This method represents a service which takes a long time to be executed.
+ * To simulate it, it is inserted a lag of 1 second. <br>
+ * This method basically creates a <i>cache</i> number of
+ * {@link ListElement} and returns it. It creates {@link ListElement}s with
+ * text higher than <i>itemNumber</i>.
+ *
+ * @param itemNumber
+ * Basic number to create other elements.
+ * @param numberOfItemsToBeCreated
+ * Number of items to be created.
+ *
+ * @return Returns the created list of {@link ListElement}s.
+ */
+ private List<ListElement> retrieveItems(Integer itemNumber,
+ int numberOfItemsToBeCreated) {
+ List<ListElement> results = new ArrayList<ListElement>();
+ try {
+ // wait for 1 second in order to simulate the long service
+ Thread.sleep(1000);
+ // create items
+ for (int i = 0; i <= numberOfItemsToBeCreated; i++) {
+ String itemToBeAdded = getString(R.string.list_item_number,
+ (itemNumber + i));
+ results.add(new ListElement(itemToBeAdded, R.drawable.listicon));
+ }
+ } catch (InterruptedException e) {
+ // treat exception here
+ }
+ return results;
+ }
+
+ /**
+ * Listener which handles the endless list. It is responsible for
+ * determining when the long service will be called asynchronously.
+ */
+ class EndlessListScrollListener implements OnScrollListener {
+
+ @Override
+ public void onScroll(AbsListView view, int firstVisibleItem,
+ int visibleItemCount, int totalItemCount) {
+
+ // load more elements if there are LOAD_AHEAD_SIZE left in the list
+ // to be displayed
+ boolean loadMore = firstVisibleItem + visibleItemCount >= totalItemCount
+ - LOAD_AHEAD_SIZE;
+
+ /*
+ * Add one more condition: only get more results in case the list
+ * achieves a minimum size. This is necessary in order to avoid that
+ * this method is called each time the condition above is reached
+ * and the scroll is pressed.
+ */
+ if (loadMore && totalSizeToBe <= totalItemCount) {
+ totalSizeToBe += INCREMENT_TOTAL_MINIMUM_SIZE;
+ // call service
+ Downloaditems downloadAction = new Downloaditems();
+ downloadAction.execute(new Integer[] { totalItemCount,
+ BLOCK_SIZE });
+ }
+ }
+
+ @Override
+ public void onScrollStateChanged(AbsListView view, int scrollState) {
+ // do nothing
+ }
+ }
+
+ /**
+ * Asynchronous job call. This class is responsible for calling the long
+ * service and managing the <i>isLoading</i> flag.
+ */
+ class Downloaditems extends AsyncTask<Integer, Void, List<ListElement>> {
+
+ // indexes constants
+ private static final int ITEM_NUMBER_INDEX = 0;
+ private static final int NUMBER_OF_ITEMS_TO_BE_CREATED_INDEX = 1;
+ private static final int TOP_ITEM_INDEX = 2;
+
+ // position to scroll the list to
+ private int listTopPosition = 0;
+
+ @Override
+ protected void onPreExecute() {
+ // flag loading is being executed
+ isLoading = true;
+ }
+
+ @Override
+ protected List<ListElement> doInBackground(Integer... params) {
+ if (params.length > TOP_ITEM_INDEX) {
+ listTopPosition = params[TOP_ITEM_INDEX];
+ }
+
+ // execute the long service
+ return retrieveItems(params[ITEM_NUMBER_INDEX],
+ params[NUMBER_OF_ITEMS_TO_BE_CREATED_INDEX]);
+ }
+
+ @Override
+ protected void onPostExecute(List<ListElement> result) {
+ arrayAdapter.setNotifyOnChange(true);
+ for (ListElement item : result) {
+ // it is necessary to verify whether the item was already added
+ // because this job is called many times asynchronously
+ synchronized (arrayAdapter) {
+ if (!arrayAdapter.contains(item)) {
+ arrayAdapter.add(item);
+ }
+ }
+ }
+ // flag the loading is finished
+ isLoading = false;
+
+ // update top list item after the list is loaded, if necessary
+ if (listTopPosition > 0) {
+ getListView().setSelection(listTopPosition);
+ }
+ }
+ }
+
+ /**
+ * Adapter which handles the list be be displayed.
+ */
+ class EndlessListAdapter extends ArrayAdapter<ListElement> {
+
+ private final Activity context;
+ private final List<ListElement> items;
+ private final int rowViewId;
+
+ /**
+ * Instantiate the Adapter for an Endless List Activity.
+ *
+ * @param context
+ * {@link Activity} which holds the endless list.
+ * @param rowviewId
+ * Identifier of the View which holds each row of the List.
+ * @param items
+ * Initial set of items which are added to list being
+ * displayed.
+ */
+ public EndlessListAdapter(Activity context, int rowviewId,
+ List<ListElement> items) {
+ super(context, rowviewId, items);
+ this.context = context;
+ this.items = items;
+ this.rowViewId = rowviewId;
+ }
+
+ /**
+ * Check whether a {@link ListItem} is already in this adapter.
+ *
+ * @param item
+ * Item to be verified whether it is in the adapter.
+ *
+ * @return Returns <code>true</code> in case the {@link ListElement} is
+ * in the adapter, <code>false</code> otherwise.
+ */
+ public boolean contains(ListElement item) {
+ return items.contains(item);
+ }
+
+ /**
+ * Get a {@link ListElement} at a certain position.
+ *
+ * @param index
+ * Position where the {@link ListElement} is retrieved.
+ *
+ * @return Returns the {@link ListElement} give a certain position.
+ */
+ public ListElement getItemAt(int index) {
+ return items.get(index);
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+
+ ImageView imageView;
+ TextView textView;
+
+ View rowView = convertView;
+
+ /*
+ * We inflate the row using the determined layout. Also, we fill the
+ * necessary data in the text and image views.
+ */
+ LayoutInflater inflater = context.getLayoutInflater();
+ rowView = inflater.inflate(rowViewId, null, true);
+ textView = (TextView) rowView.findViewById(R.id.text01);
+ imageView = (ImageView) rowView.findViewById(R.id.img01);
+ textView.setText(items.get(position).text);
+ imageView.setImageResource(items.get(position).imageId);
+
+ /*
+ * If we reached the last position of the list and the loading
+ * operation is still being performed, set the loading message
+ * instead the normal value.
+ *
+ * Moreover, we modify the layout in order to center the loading
+ * message.
+ */
+ if (isLoading && position == items.size() - 1) {
+ textView.setText(R.string.loading_message);
+
+ // wrap content of the text view in order to center it
+ LayoutParams layoutParameters = (LayoutParams) textView
+ .getLayoutParams();
+ layoutParameters.width = LayoutParams.WRAP_CONTENT;
+
+ // set image to the center, the text field will go along
+ imageView
+ .setImageResource(android.R.drawable.progress_indeterminate_horizontal);
+ LinearLayout linearLayout = (LinearLayout) rowView
+ .findViewById(R.id.vw01);
+ linearLayout.setGravity(Gravity.CENTER);
+ }
+
+ return rowView;
+ }
+ }
+} \ No newline at end of file
diff --git a/src/plugins/android.codeutils/templates/activity_samples/endless_list_usingfragment/ListElement.java b/src/plugins/android.codeutils/templates/activity_samples/endless_list_usingfragment/ListElement.java
new file mode 100644
index 0000000..8030fb2
--- /dev/null
+++ b/src/plugins/android.codeutils/templates/activity_samples/endless_list_usingfragment/ListElement.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package #package_name#;
+
+import java.io.Serializable;
+
+/**
+ * Element which belongs to each row of the List of this view.
+ */
+class ListElement implements Serializable {
+
+ private static final long serialVersionUID = -1494059051539426231L;
+ String text;
+ Integer imageId;
+
+ /**
+ * Always create the object with a text and an image.
+ *
+ * @param text
+ * Text of the {@link ListElement}.
+ * @param imageId
+ * Image identifier of the {@link ListElement}.
+ */
+ public ListElement(String text, Integer imageId) {
+ this.text = text;
+ this.imageId = imageId;
+ }
+
+ /**
+ * Get the Text.
+ *
+ * @return Return the Text.
+ */
+ public String getText() {
+ return text;
+ }
+
+ /**
+ * Set the Text.
+ *
+ * @param text
+ * Text to be set.
+ */
+ public void setText(String text) {
+ this.text = text;
+ }
+
+ /**
+ * Get the Image identifier.
+ *
+ * @return Return the Image identifier.
+ */
+ public Integer getImageId() {
+ return imageId;
+ }
+
+ /**
+ * Set the Image identifier.
+ *
+ * @param imageId
+ * Image Identifier to be set.
+ */
+ public void setImageId(Integer imageId) {
+ this.imageId = imageId;
+ }
+
+ /**
+ * Here we consider two {@link ListElement}s to be equal when they have
+ * the same text field.
+ */
+ @Override
+ public boolean equals(Object element) {
+ return element != null && element instanceof ListElement
+ && this.text != null
+ && ((ListElement) element).text != null
+ && ((ListElement) element).text.equals(text);
+ }
+
+ @Override
+ public int hashCode() {
+ return text != null ? text.length() * 3 : 4;
+ }
+} \ No newline at end of file
diff --git a/src/plugins/android.codeutils/templates/activity_samples/endless_list_usingfragment/activitylayout.xml b/src/plugins/android.codeutils/templates/activity_samples/endless_list_usingfragment/activitylayout.xml
new file mode 100644
index 0000000..1dd363f
--- /dev/null
+++ b/src/plugins/android.codeutils/templates/activity_samples/endless_list_usingfragment/activitylayout.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"
+ android:id="@+id/layout1"
+ >
+</LinearLayout>
diff --git a/src/plugins/android.codeutils/templates/activity_samples/endless_list_usingfragment/endless_list_activity.java b/src/plugins/android.codeutils/templates/activity_samples/endless_list_usingfragment/endless_list_activity.java
new file mode 100644
index 0000000..ea342e8
--- /dev/null
+++ b/src/plugins/android.codeutils/templates/activity_samples/endless_list_usingfragment/endless_list_activity.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package #package_name#;
+
+import android.app.Activity;
+import android.app.FragmentTransaction;
+import android.os.Bundle;
+
+import #ManifestPackageName#.R;
+
+/**
+ * Activity that calls an endless list using fragment
+ */
+public class #class_name# extends Activity {
+
+ /** Called when the activity is first created. */
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.#layout_name#endless_list_usingfragment/activitylayout.xml#);
+
+ //use fragment
+ //add fragment as an endless list (as a FragmentList)
+ FragmentTransaction ft = getFragmentManager().beginTransaction();
+ FragmentEndlessList newFrag = new FragmentEndlessList();
+ ft.add(R.id.layout1, newFrag);
+ ft.commit();
+ }
+} \ No newline at end of file
diff --git a/src/plugins/android.codeutils/templates/activity_samples/endless_list_usingfragment/endless_list_usingfragment_strings.xml b/src/plugins/android.codeutils/templates/activity_samples/endless_list_usingfragment/endless_list_usingfragment_strings.xml
new file mode 100644
index 0000000..210cede
--- /dev/null
+++ b/src/plugins/android.codeutils/templates/activity_samples/endless_list_usingfragment/endless_list_usingfragment_strings.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<resources>
+ <string name="selected_element_message">Selected element is: %s</string>
+ <string name="list_item_number">List Item %s</string>
+ <string name="loading_message">Loading...</string>
+</resources>
diff --git a/src/plugins/android.codeutils/templates/activity_samples/endless_list_usingfragment/listicon.png b/src/plugins/android.codeutils/templates/activity_samples/endless_list_usingfragment/listicon.png
new file mode 100644
index 0000000..4bff905
--- /dev/null
+++ b/src/plugins/android.codeutils/templates/activity_samples/endless_list_usingfragment/listicon.png
Binary files differ
diff --git a/src/plugins/android.codeutils/templates/activity_samples/endless_list_usingfragment/listrow.xml b/src/plugins/android.codeutils/templates/activity_samples/endless_list_usingfragment/listrow.xml
new file mode 100644
index 0000000..0009dc9
--- /dev/null
+++ b/src/plugins/android.codeutils/templates/activity_samples/endless_list_usingfragment/listrow.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/vw01"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+
+ <ImageView android:id="@+id/img01"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:padding="5dip"/>
+
+ <TextView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/text01"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceLarge"
+ android:gravity="center_vertical"
+ android:paddingLeft="6dip"
+ android:minHeight="?android:attr/listPreferredItemHeight"
+/>
+</LinearLayout>
diff --git a/src/plugins/android.codeutils/templates/activity_samples/endless_list_usingfragment/preview.png b/src/plugins/android.codeutils/templates/activity_samples/endless_list_usingfragment/preview.png
new file mode 100644
index 0000000..d5051ee
--- /dev/null
+++ b/src/plugins/android.codeutils/templates/activity_samples/endless_list_usingfragment/preview.png
Binary files differ
diff --git a/src/plugins/android.codeutils/templates/activity_samples/expandable_list/group_row.xml b/src/plugins/android.codeutils/templates/activity_samples/expandable_list/group_row.xml
new file mode 100644
index 0000000..288bdb4
--- /dev/null
+++ b/src/plugins/android.codeutils/templates/activity_samples/expandable_list/group_row.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="horizontal"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+
+ <TextView android:id="@+id/name"
+ android:paddingLeft="60px"
+ android:focusable="false"
+ android:textSize="30px"
+ android:textStyle="normal"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"/>
+
+</LinearLayout>
diff --git a/src/plugins/android.codeutils/templates/activity_samples/expandable_list/subgroup_row.xml b/src/plugins/android.codeutils/templates/activity_samples/expandable_list/subgroup_row.xml
new file mode 100644
index 0000000..acc2f07
--- /dev/null
+++ b/src/plugins/android.codeutils/templates/activity_samples/expandable_list/subgroup_row.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="horizontal"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+
+ <TextView android:id="@+id/name"
+ android:paddingLeft="50px"
+ android:textSize="14px"
+ android:textStyle="normal"
+ android:layout_width="150px"
+ android:layout_height="wrap_content"/>
+
+</LinearLayout>
+
diff --git a/src/plugins/android.codeutils/templates/activity_samples/img_text_list/img_text_activity.java b/src/plugins/android.codeutils/templates/activity_samples/img_text_list/img_text_activity.java
new file mode 100644
index 0000000..27591ef
--- /dev/null
+++ b/src/plugins/android.codeutils/templates/activity_samples/img_text_list/img_text_activity.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package #package_name#;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import android.app.ListActivity;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemClickListener;
+import android.widget.ListView;
+import android.widget.SimpleAdapter;
+import android.widget.Toast;
+
+import #ManifestPackageName#.R;
+
+public class #class_name# extends ListActivity
+{
+ private final String TEXT_KEY = "text";
+ private final String IMG_KEY = "img";
+
+ public void onCreate(Bundle savedInstanceState)
+ {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.#layout_name#img_text_list/listview.xml#);
+
+ //adds listener to list view
+ ListView listView = getListView();
+ listView.setOnItemClickListener(new OnItemClickListener(){
+ public void onItemClick(AdapterView<?> arg0, View arg1, int arg2, long arg3) {
+ Toast.makeText(arg1.getContext(), getString(R.string.selected) + " " + arg2 , Toast.LENGTH_SHORT).show();
+ }
+ });
+
+ // list data
+ List<Map<String, Object>> resourceNames =
+ new ArrayList<Map<String, Object>>();
+
+ generateData(resourceNames);
+
+ //adapter that will build the list items
+ SimpleAdapter adapter = new SimpleAdapter(
+ this,
+ resourceNames,
+ R.layout.#layout_name#img_text_list/listrow.xml#,
+ new String[] { TEXT_KEY, IMG_KEY },
+ new int[] { R.id.text01, R.id.img01 });
+
+ setListAdapter(adapter);
+ }
+
+ private void generateData(List<Map<String, Object>> resourceNames)
+ {
+ // number of list items
+ int NUM_ITEMS = 50;
+ Map<String, Object> data;
+
+ for ( int i = 0; i <= NUM_ITEMS; i++ )
+ {
+ data = new HashMap<String, Object>();
+ data.put(TEXT_KEY, getString(R.string.list_item) + " " + Integer.toString(i));
+ data.put(IMG_KEY, R.drawable.#drawable_name#img_text_list/listicon.png# );
+ resourceNames.add(data);
+ }
+ }
+} \ No newline at end of file
diff --git a/src/plugins/android.codeutils/templates/activity_samples/img_text_list/img_text_strings.xml b/src/plugins/android.codeutils/templates/activity_samples/img_text_list/img_text_strings.xml
new file mode 100644
index 0000000..28870c1
--- /dev/null
+++ b/src/plugins/android.codeutils/templates/activity_samples/img_text_list/img_text_strings.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<resources>
+ <string name="selected">You selected: </string>
+ <string name="list_item">List Item</string>
+ <string name="description">description</string>
+ <string name="noselection">No item selected</string>
+</resources>
diff --git a/src/plugins/android.codeutils/templates/activity_samples/img_text_list/listicon.png b/src/plugins/android.codeutils/templates/activity_samples/img_text_list/listicon.png
new file mode 100644
index 0000000..4bff905
--- /dev/null
+++ b/src/plugins/android.codeutils/templates/activity_samples/img_text_list/listicon.png
Binary files differ
diff --git a/src/plugins/android.codeutils/templates/activity_samples/img_text_list/listrow.xml b/src/plugins/android.codeutils/templates/activity_samples/img_text_list/listrow.xml
new file mode 100644
index 0000000..7bb9825
--- /dev/null
+++ b/src/plugins/android.codeutils/templates/activity_samples/img_text_list/listrow.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/vw01"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+
+ <ImageView android:id="@+id/img01"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:padding="5dip"/>
+
+ <TextView android:id="@+id/text01"
+ android:textStyle="bold"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:gravity="center_vertical"
+ android:paddingLeft="10dip"/>
+
+</LinearLayout>
diff --git a/src/plugins/android.codeutils/templates/activity_samples/img_text_list/listview.xml b/src/plugins/android.codeutils/templates/activity_samples/img_text_list/listview.xml
new file mode 100644
index 0000000..651573e
--- /dev/null
+++ b/src/plugins/android.codeutils/templates/activity_samples/img_text_list/listview.xml
@@ -0,0 +1,14 @@
+<?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">
+
+ <ListView
+ android:id="@id/android:list"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:drawSelectorOnTop="false"
+ />
+
+</LinearLayout> \ No newline at end of file
diff --git a/src/plugins/android.codeutils/templates/activity_samples/img_text_list/preview.png b/src/plugins/android.codeutils/templates/activity_samples/img_text_list/preview.png
new file mode 100644
index 0000000..ca13023
--- /dev/null
+++ b/src/plugins/android.codeutils/templates/activity_samples/img_text_list/preview.png
Binary files differ
diff --git a/src/plugins/android.codeutils/templates/activity_samples/list_activities_config.xml b/src/plugins/android.codeutils/templates/activity_samples/list_activities_config.xml
new file mode 100644
index 0000000..24039a0
--- /dev/null
+++ b/src/plugins/android.codeutils/templates/activity_samples/list_activities_config.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <sample name="%Activity_Samples_DDList_name" description="%Activity_Samples_DDList_description" preview="dropdown_list/preview.png">
+ <file name="dropdown_list/dropdown_list_activity.java" resourceType="src" />
+ <file name="dropdown_list/ddlistlayout.xml" resourceType="layout" />
+ <file name="dropdown_list/ddlist_strings.xml" resourceType="values" />
+ </sample>
+ <sample name="%Activity_Samples_SelectionList_name" description="%Activity_Samples_SelectionList_description" preview="selection_list/preview.png">
+ <file name="selection_list/selection_list_activity.java" resourceType="src" />
+ <file name="selection_list/selection_strings.xml" resourceType="values" />
+ </sample>
+ <sample name="%Activity_Samples_SimpleList_name" description="%Activity_Samples_SimpleList_description" preview="simple_list/preview.png">
+ <file name="simple_list/simple_list_activity.java" resourceType="src" />
+ <file name="simple_list/simple_list_strings.xml" resourceType="values" />
+ </sample>
+ <sample name="%android.wizard.activity.multiplechoicelist.name" description="%android.wizard.activity.multiplechoicelist.description" preview="mult_selection_img/preview.png">
+ <file name="mult_selection_img/select_img_list_activity.java" resourceType="src" />
+ <file name="mult_selection_img/select_img_list_strings.xml" resourceType="values" />
+ <file name="mult_selection_img/listicon.png" resourceType="drawable" />
+ <file name="mult_selection_img/listviewmult.xml" resourceType="layout" />
+ <file name="mult_selection_img/listrowmult.xml" resourceType="layout" />
+ </sample>
+ <sample name="%android.wizard.activity.singlechoicelist.name" description="%android.wizard.activity.singlechoicelist.description" preview="single_selection_img/preview.png">
+ <file name="single_selection_img/single_sel_img_activity.java" resourceType="src" />
+ <file name="single_selection_img/single_sel_img_strings.xml" resourceType="values" />
+ <file name="single_selection_img/listrowsingle.xml" resourceType="layout" />
+ <file name="single_selection_img/listviewsingle.xml" resourceType="layout" />
+ <file name="single_selection_img/listiconsingle.png" resourceType="drawable" />
+ </sample>
+ <sample name="%android.wizard.activity.imageoptionlist.name" description="%android.wizard.activity.imageoptionlist.description" preview="img_text_list/preview.png">
+ <file name="img_text_list/img_text_activity.java" resourceType="src" />
+ <file name="img_text_list/img_text_strings.xml" resourceType="values" />
+ <file name="img_text_list/listview.xml" resourceType="layout" />
+ <file name="img_text_list/listrow.xml" resourceType="layout" />
+ <file name="img_text_list/listicon.png" resourceType="drawable" />
+ </sample>
+ <sample name="%android.wizard.activity.endlesslist.name" description="%android.wizard.activity.endlesslist.description" preview="endless_list/preview.png">
+ <file name="endless_list/endless_list_activity.java" resourceType="src" />
+ <file name="endless_list/ListElement.java" resourceType="src" />
+ <file name="endless_list/listrow.xml" resourceType="layout" />
+ <file name="endless_list/listicon.png" resourceType="drawable" />
+ <file name="endless_list/endless_list_strings.xml" resourceType="values" />
+ </sample>
+ <sample name="%android.wizard.activity.endlesslist.usingfragment.name" description="%android.wizard.activity.endlesslist.usingfragment.description" preview="endless_list_usingfragment/preview.png">
+ <file name="endless_list_usingfragment/endless_list_activity.java" resourceType="src" />
+ <file name="endless_list_usingfragment/FragmentEndlessList.java" resourceType="src" />
+ <file name="endless_list_usingfragment/ListElement.java" resourceType="src" />
+ <file name="endless_list_usingfragment/activitylayout.xml" resourceType="layout" />
+ <file name="endless_list_usingfragment/listrow.xml" resourceType="layout" />
+ <file name="endless_list_usingfragment/listicon.png" resourceType="drawable" />
+ <file name="endless_list_usingfragment/endless_list_usingfragment_strings.xml" resourceType="values" />
+ </sample>
+ <sample name="%android.wizard.activity.pulltorefresh.name" description="%android.wizard.activity.pulltorefresh.description" preview="endless_list_pull_to_refresh/preview.png">
+ <file name="endless_list_pull_to_refresh/pull_to_refresh_activity.java" resourceType="src" />
+ <file name="endless_list_pull_to_refresh/ListElement.java" resourceType="src" />
+ <file name="endless_list_pull_to_refresh/listrow.xml" resourceType="layout" />
+ <file name="endless_list_pull_to_refresh/listheader.xml" resourceType="layout" />
+ <file name="endless_list_pull_to_refresh/listicon.png" resourceType="drawable" />
+ <file name="endless_list_pull_to_refresh/endless_list_pull_to_refresh_strings.xml" resourceType="values" />
+ </sample>
+</resources> \ No newline at end of file
diff --git a/src/plugins/android.codeutils/templates/activity_samples/mult_selection_img/listicon.png b/src/plugins/android.codeutils/templates/activity_samples/mult_selection_img/listicon.png
new file mode 100644
index 0000000..4bff905
--- /dev/null
+++ b/src/plugins/android.codeutils/templates/activity_samples/mult_selection_img/listicon.png
Binary files differ
diff --git a/src/plugins/android.codeutils/templates/activity_samples/mult_selection_img/listrowmult.xml b/src/plugins/android.codeutils/templates/activity_samples/mult_selection_img/listrowmult.xml
new file mode 100644
index 0000000..509ebda
--- /dev/null
+++ b/src/plugins/android.codeutils/templates/activity_samples/mult_selection_img/listrowmult.xml
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/vw1"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+
+ <ImageView android:id="@+id/img"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:padding="6dip"/>
+
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <TextView android:id="@+id/text1"
+ android:textSize="12sp"
+ android:textStyle="bold"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingLeft="10dip"
+ android:paddingTop="6dip"/>
+
+ <TextView android:id="@+id/text2"
+ android:textSize="12sp"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingLeft="10dip"/>
+ </LinearLayout>
+
+ <RelativeLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="right"
+ android:layout_gravity="right"
+ >
+
+ <CheckBox android:id="@+id/ckb"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="right"
+ android:checked="false"
+ android:clickable="false"
+ />
+ </RelativeLayout>
+</LinearLayout> \ No newline at end of file
diff --git a/src/plugins/android.codeutils/templates/activity_samples/mult_selection_img/listviewmult.xml b/src/plugins/android.codeutils/templates/activity_samples/mult_selection_img/listviewmult.xml
new file mode 100644
index 0000000..1a78f1f
--- /dev/null
+++ b/src/plugins/android.codeutils/templates/activity_samples/mult_selection_img/listviewmult.xml
@@ -0,0 +1,22 @@
+<?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">
+
+ <ListView
+ android:id="@id/android:list"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:drawSelectorOnTop="false"
+ />
+ <Button
+ android:id="@+id/button"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/button_label"
+ android:gravity="center_horizontal"
+ />
+
+</LinearLayout> \ No newline at end of file
diff --git a/src/plugins/android.codeutils/templates/activity_samples/mult_selection_img/preview.png b/src/plugins/android.codeutils/templates/activity_samples/mult_selection_img/preview.png
new file mode 100644
index 0000000..2dae1b1
--- /dev/null
+++ b/src/plugins/android.codeutils/templates/activity_samples/mult_selection_img/preview.png
Binary files differ
diff --git a/src/plugins/android.codeutils/templates/activity_samples/mult_selection_img/select_img_list_activity.java b/src/plugins/android.codeutils/templates/activity_samples/mult_selection_img/select_img_list_activity.java
new file mode 100644
index 0000000..a85336d
--- /dev/null
+++ b/src/plugins/android.codeutils/templates/activity_samples/mult_selection_img/select_img_list_activity.java
@@ -0,0 +1,242 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package #package_name#;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import android.app.ListActivity;
+import android.content.Context;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.widget.CheckBox;
+import android.widget.ImageView;
+import android.widget.SimpleAdapter;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import #ManifestPackageName#.R;
+
+public class #class_name# extends ListActivity implements OnClickListener
+{
+ private ArrayList<Integer> selectedItems = new ArrayList<Integer>();
+ private final String SELECTED_ITEM_KEY = "selected_items";
+ public final String TEXT_KEY_1 = "title";
+ public final String TEXT_KEY_2 = "description";
+ public final String ITEM_ID = "id";
+ public final String IMG_KEY = "img";
+
+ public void onCreate(Bundle savedInstanceState)
+ {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.#layout_name#mult_selection_img/listviewmult.xml#);
+
+ findViewById(R.id.button).setOnClickListener(this);
+
+ // list data
+ List<Map<String, Object>> resourceNames = new ArrayList<Map<String, Object>>();
+ generateData(resourceNames);
+
+ MyAdapter notes = new MyAdapter(
+ this,
+ resourceNames,
+ R.layout.#layout_name#mult_selection_img/listrowmult.xml#,
+ new String[] { TEXT_KEY_1,TEXT_KEY_2, IMG_KEY, ITEM_ID },
+ new int[] { R.id.text1, R.id.text2, R.id.img},
+ selectedItems);
+
+
+ setListAdapter(notes);
+ }
+
+ private void generateData(List<Map<String, Object>> resourceNames)
+ {
+ //TODO here you will fill resourceNames with your own data
+
+ Map<String, Object> data;
+ int NUM_ITEMS = 50;
+
+ for ( int i = 0; i <= NUM_ITEMS; i++ )
+ {
+ data = new HashMap<String, Object>();
+ data.put(ITEM_ID, i);
+ data.put(TEXT_KEY_1, getString(R.string.list_item) + " " + Integer.toString(i) );
+ data.put(TEXT_KEY_2, getString(R.string.description));
+ data.put(IMG_KEY, R.drawable.#drawable_name#mult_selection_img/listicon.png# );
+ resourceNames.add(data);
+ }
+ }
+
+ /*
+ * Restores list selection
+ */
+ @Override
+ protected void onRestoreInstanceState(Bundle state) {
+ super.onRestoreInstanceState(state);
+ selectedItems.addAll(state.getIntegerArrayList(SELECTED_ITEM_KEY));
+ }
+
+ /*
+ * When the device is rotated, this activity is killed
+ * This method is called when activity is about to be killed and saves
+ * the current list selection
+ */
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ outState.putIntegerArrayList(SELECTED_ITEM_KEY, selectedItems);
+ }
+
+ /*
+ * Prints on screen the currently selected items
+ */
+ public void onClick(View target)
+ {
+ //TODO execute your action here
+
+ StringBuilder strText = new StringBuilder();
+ strText.append(getString(R.string.selected));
+
+ Collections.sort(selectedItems);
+
+ boolean first = true;
+ for(Integer cur : selectedItems)
+ {
+ if(first)
+ {
+ strText.append(cur);
+ first = false;
+ }
+ else
+ {
+ strText.append(", " + cur);
+ }
+ }
+
+ Toast.makeText(getApplicationContext(), strText.toString(), Toast.LENGTH_LONG).show();
+ }
+
+ class MyAdapter extends SimpleAdapter
+ {
+ List<? extends Map<String, ?>> resourceNames;
+ OnItemClickListener listener = null;
+ ArrayList<Integer> selectedItems;
+ String[] strKeys;
+ int[] ids;
+
+ public MyAdapter(Context context, List<? extends Map<String, ?>> data,
+ int resource, String[] from, int[] to, ArrayList<Integer> selectedItems) {
+ super(context, data, resource, from, to);
+ this.selectedItems = selectedItems;
+ resourceNames = data;
+ strKeys = from;
+ ids = to;
+ }
+
+ /* Returns a view to be added on the list
+ * When we scroll the list, some items leave the screen becoming invisible to the user.
+ * Since creating views is an expensive task, we'd rather recycle these not visible views,
+ * that are referenced by convertView, updating its fields values.*/
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+
+ // used to improve performance, since we call findViewById
+ // only once for each created, but not recycled, view
+ ViewHolder holder;
+
+ if(listener == null)
+ listener = new OnItemClickListener(selectedItems);
+
+ //view to be recycled
+ if(convertView == null)
+ {
+ holder = new ViewHolder();
+ convertView = LayoutInflater.from(parent.getContext()).inflate(R.layout.#layout_name#mult_selection_img/listrowmult.xml#, null);
+
+ holder.tv1 = (TextView) convertView.findViewById(R.id.text1);
+ holder.tv2 = (TextView) convertView.findViewById(R.id.text2);
+ holder.img = (ImageView) convertView.findViewById(R.id.img);
+ holder.ckb = (CheckBox) convertView.findViewById(R.id.ckb);
+
+ convertView.setTag(holder);
+ }
+ else
+ {
+ holder = (ViewHolder) convertView.getTag();
+ }
+ Map<String, ?> currentData = resourceNames.get(position);
+
+ //updates list items values
+ holder.tv1.setText(currentData.get(strKeys[0]).toString());
+ holder.tv2.setText(currentData.get(strKeys[1]).toString());
+ holder.img.setImageResource((Integer) currentData.get(strKeys[2]));
+ holder.ckb.setChecked(selectedItems.contains((Integer) currentData.get(strKeys[3])));
+
+ convertView.setId((Integer) currentData.get(strKeys[3]));
+ convertView.setOnClickListener(listener);
+
+ return convertView;
+ }
+ }
+
+ /*
+ * Holds references to list items
+ */
+ class ViewHolder
+ {
+ TextView tv1, tv2;
+ ImageView img;
+ CheckBox ckb;
+ }
+
+ /*
+ * Called when a list item is clicked
+ */
+ class OnItemClickListener implements OnClickListener
+ {
+ ArrayList<Integer> selectedItems;
+
+ public OnItemClickListener(ArrayList<Integer> selectedItems)
+ {
+ this.selectedItems = selectedItems;
+ }
+
+ public void onClick(View v) {
+ //handles list item click
+ CheckBox ckb = (CheckBox) v.findViewById(R.id.ckb);
+ boolean checked = ckb.isChecked();
+ //updates selected list
+ if(checked)
+ {
+ selectedItems.remove(new Integer(v.getId()));
+ }
+ else
+ {
+ selectedItems.add(v.getId());
+ }
+
+ //update check box value
+ ckb.setChecked(!checked);
+ }
+ }
+}
+
diff --git a/src/plugins/android.codeutils/templates/activity_samples/mult_selection_img/select_img_list_strings.xml b/src/plugins/android.codeutils/templates/activity_samples/mult_selection_img/select_img_list_strings.xml
new file mode 100644
index 0000000..29eb46e
--- /dev/null
+++ b/src/plugins/android.codeutils/templates/activity_samples/mult_selection_img/select_img_list_strings.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<resources>
+ <string name="selected">You selected: </string>
+ <string name="list_item">List Item </string>
+ <string name="description">description</string>
+ <string name="button_label">Done</string>
+</resources>
diff --git a/src/plugins/android.codeutils/templates/activity_samples/multitouch_event/multitouch.jpg b/src/plugins/android.codeutils/templates/activity_samples/multitouch_event/multitouch.jpg
new file mode 100644
index 0000000..0bd41b8
--- /dev/null
+++ b/src/plugins/android.codeutils/templates/activity_samples/multitouch_event/multitouch.jpg
Binary files differ
diff --git a/src/plugins/android.codeutils/templates/activity_samples/multitouch_event/multitouch_event_activity.java b/src/plugins/android.codeutils/templates/activity_samples/multitouch_event/multitouch_event_activity.java
new file mode 100644
index 0000000..bba418c
--- /dev/null
+++ b/src/plugins/android.codeutils/templates/activity_samples/multitouch_event/multitouch_event_activity.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package #package_name#;
+
+import android.app.Activity;
+import android.graphics.Matrix;
+import android.graphics.PointF;
+import android.os.Bundle;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.View.OnTouchListener;
+import android.widget.ImageView;
+
+import #ManifestPackageName#.R;
+
+public class #class_name# extends Activity {
+ private ImageView imgView;
+
+ //available operations
+ private final int MOVE = 1;
+ private final int ZOOM = 2;
+ private int action = 0;
+
+ PointF startPoint = new PointF();
+ PointF centerPoint = new PointF();
+
+ //holds previous state information
+ private double prevDist = 0;
+ private Matrix curMatrix = new Matrix();
+ private Matrix auxMatrix = new Matrix();
+
+ //Minimum distance between fingers
+ private Double MIN_DISTANCE;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.#layout_name#multitouch_event/multitouchlayout.xml#);
+
+ // convert dip measurements to pixels. Screen independent value.
+ final float scale = getResources().getDisplayMetrics().density;
+ MIN_DISTANCE = (double) ( 15.0f * scale + 0.5f );
+
+ //loads image view
+ imgView = (ImageView) findViewById(R.id.img01);
+ imgView.setImageMatrix(curMatrix);
+ imgView.setImageResource(R.drawable.#drawable_name#multitouch_event/multitouch.jpg#);
+ imgView.setVisibility(View.VISIBLE);
+ imgView.setOnTouchListener(new OnTouchListener(){
+
+ public boolean onTouch(View v, MotionEvent event) {
+ ImageView view = (ImageView)v;
+
+ //get the 8 bits that represents the action itself
+ int eventType = event.getAction() & MotionEvent.ACTION_MASK;
+
+ switch (eventType) {
+ //first touch
+ case MotionEvent.ACTION_DOWN:
+ action = MOVE;
+ auxMatrix.set(curMatrix);
+ startPoint.set(event.getX(), event.getY());
+ break;
+ //second touch
+ case MotionEvent.ACTION_POINTER_DOWN:
+ prevDist = distance(event);
+ if (prevDist > MIN_DISTANCE) {
+ action = ZOOM;
+ auxMatrix.set(curMatrix);
+ //used to center the image
+ centerPoint = mean(event);
+ }
+ break;
+ //movement event
+ case MotionEvent.ACTION_MOVE:
+ //using one finger we drag the image
+ if (action == MOVE) {
+ curMatrix.set(auxMatrix);
+ //moves the image
+ curMatrix.postTranslate(event.getX() - startPoint.x,
+ event.getY() - startPoint.y);
+ }
+ //using two fingers, zoom in or out and rotate the image
+ else if (action == ZOOM)
+ {
+ double curDist = distance(event);
+ if (curDist > MIN_DISTANCE) {
+ curMatrix.set(auxMatrix);
+ //relation between fingers distance
+ Double scale = curDist / prevDist;
+ //resize image keeping its center position
+ curMatrix.postScale(scale.floatValue(), scale.floatValue(),
+ centerPoint.x, centerPoint.y);
+ }
+ }
+ break;
+ }
+ //apply changes
+ view.setImageMatrix(curMatrix);
+ return true;
+ }});
+ }
+
+ /*distance between the two fingers of dual touch event*/
+ private double distance(MotionEvent event) {
+ float dy = event.getY(1) - event.getY(0);
+ float dx = event.getX(1) - event.getX(0);
+ dx *= dx;
+ dy *= dy;
+ return Math.sqrt(dx + dy);
+ }
+
+ /*evaluates the center point*/
+ private PointF mean(MotionEvent event) {
+ PointF point = new PointF();
+ float dy = event.getY(1) + event.getY(0);
+ float dx = event.getX(1) + event.getX(0);
+ point.set(dx / 2, dy / 2);
+ return point;
+ }
+} \ No newline at end of file
diff --git a/src/plugins/android.codeutils/templates/activity_samples/multitouch_event/multitouchlayout.xml b/src/plugins/android.codeutils/templates/activity_samples/multitouch_event/multitouchlayout.xml
new file mode 100644
index 0000000..c921155
--- /dev/null
+++ b/src/plugins/android.codeutils/templates/activity_samples/multitouch_event/multitouchlayout.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <ImageView
+ android:id="@+id/img01"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:scaleType="matrix">
+ </ImageView>
+</FrameLayout>
diff --git a/src/plugins/android.codeutils/templates/activity_samples/pref_activity/preference_activity.java b/src/plugins/android.codeutils/templates/activity_samples/pref_activity/preference_activity.java
new file mode 100644
index 0000000..f85a082
--- /dev/null
+++ b/src/plugins/android.codeutils/templates/activity_samples/pref_activity/preference_activity.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package #package_name#;
+
+import android.os.Bundle;
+import android.preference.PreferenceActivity;
+
+import #ManifestPackageName#.R;
+
+/***
+ * PreferenceActivity is a built-in Activity for preferences management
+ *
+ * To retrieve the values stored by this activity in other activities use the following snippet:
+ *
+ * SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
+ * <Preference Type> preferenceValue = sharedPreferences.get<Preference Type>("<Preference Key>",<default value>);
+ */
+public class #class_name# extends PreferenceActivity {
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ addPreferencesFromResource(R.xml.#xml_name#pref_activity/samplepreferences.xml#);
+ }
+}
diff --git a/src/plugins/android.codeutils/templates/activity_samples/pref_activity/samplelist.xml b/src/plugins/android.codeutils/templates/activity_samples/pref_activity/samplelist.xml
new file mode 100644
index 0000000..66d1cc7
--- /dev/null
+++ b/src/plugins/android.codeutils/templates/activity_samples/pref_activity/samplelist.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <string name="sample_pref_categ_1">Preferences category 1</string>
+ <string name="sample_pref_categ_2">Preferences category 2</string>
+ <string name="sample_pref_categ_3">Preferences category 3</string>
+ <string name="sample_pref_ckb">Checkbox Preference</string>
+ <string name="sample_pref_check_it">Check It</string>
+ <string name="sample_pref_other_screen">Another Preference Screen</string>
+ <string name="sample_pref_add_prefs">Additional preferences</string>
+ <string name="sample_pref_other_ckb">Another Checkbox</string>
+ <string name="sample_pref_txt_dialog">Text Entry Dialog</string>
+ <string name="sample_pref_enter_txt">Enter something</string>
+ <string name="sample_pref_summary">Preference Summary</string>
+ <string name="sample_pref_sel_dialog">Selection Dialog</string>
+ <string name="sample_pref_click_pop_up">Click to pop up a list to choose from</string>
+ <string name="sample_pref_choose_number">Choose a number</string>
+ <string-array name="numbers">
+ <item>1</item>
+ <item>2</item>
+ <item>3</item>
+ </string-array>
+ <string-array name="numbernames">
+ <item>One</item>
+ <item>Two</item>
+ <item>Three</item>
+ </string-array>
+</resources> \ No newline at end of file
diff --git a/src/plugins/android.codeutils/templates/activity_samples/pref_activity/samplepreferences.xml b/src/plugins/android.codeutils/templates/activity_samples/pref_activity/samplepreferences.xml
new file mode 100644
index 0000000..aade5f0
--- /dev/null
+++ b/src/plugins/android.codeutils/templates/activity_samples/pref_activity/samplepreferences.xml
@@ -0,0 +1,27 @@
+<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
+ <PreferenceCategory android:title="@string/sample_pref_categ_1">
+ <CheckBoxPreference android:key="checkbox"
+ android:title="@string/sample_pref_ckb"
+ android:summary="@string/sample_pref_check_it" />
+ </PreferenceCategory>
+ <PreferenceCategory android:title="@string/sample_pref_categ_2">
+ <PreferenceScreen android:key="detail"
+ android:title="@string/sample_pref_other_screen"
+ android:summary="@string/sample_pref_add_prefs">
+ <CheckBoxPreference android:key="checkbox2"
+ android:title="@string/sample_pref_other_ckb" />
+ </PreferenceScreen>
+ </PreferenceCategory>
+ <PreferenceCategory android:title="@string/sample_pref_categ_3">
+ <EditTextPreference android:key="text"
+ android:title="@string/sample_pref_txt_dialog"
+ android:dialogTitle="@string/sample_pref_enter_txt" />
+ android:summary="Preference Summary"/>
+ <ListPreference android:key="list"
+ android:title="@string/sample_pref_sel_dialog"
+ android:summary="@string/sample_pref_click_pop_up"
+ android:entries="@array/numbernames"
+ android:entryValues="@array/numbers"
+ android:dialogTitle="@string/sample_pref_choose_number" />
+ </PreferenceCategory>
+</PreferenceScreen> \ No newline at end of file
diff --git a/src/plugins/android.codeutils/templates/activity_samples/quickaction/icon.png b/src/plugins/android.codeutils/templates/activity_samples/quickaction/icon.png
new file mode 100644
index 0000000..bd5a4f3
--- /dev/null
+++ b/src/plugins/android.codeutils/templates/activity_samples/quickaction/icon.png
Binary files differ
diff --git a/src/plugins/android.codeutils/templates/activity_samples/quickaction/imgbuttonselector.xml b/src/plugins/android.codeutils/templates/activity_samples/quickaction/imgbuttonselector.xml
new file mode 100644
index 0000000..d09d526
--- /dev/null
+++ b/src/plugins/android.codeutils/templates/activity_samples/quickaction/imgbuttonselector.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item
+ android:state_pressed="true"
+ android:drawable="@drawable/#drawable_name#quickaction/icon.png#"
+ android:background="@android:color/white"/>
+ <item
+ android:drawable="@drawable/#drawable_name#quickaction/icon.png#"
+ android:background="@android:color/transparent"/>
+</selector> \ No newline at end of file
diff --git a/src/plugins/android.codeutils/templates/activity_samples/quickaction/imgbuttonselector_background.xml b/src/plugins/android.codeutils/templates/activity_samples/quickaction/imgbuttonselector_background.xml
new file mode 100644
index 0000000..055582a
--- /dev/null
+++ b/src/plugins/android.codeutils/templates/activity_samples/quickaction/imgbuttonselector_background.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item
+ android:state_pressed="true"
+ android:drawable="@android:drawable/btn_default"/>
+ <item
+ android:drawable="@android:color/transparent"/>
+</selector> \ No newline at end of file
diff --git a/src/plugins/android.codeutils/templates/activity_samples/quickaction/quick_action.java b/src/plugins/android.codeutils/templates/activity_samples/quickaction/quick_action.java
new file mode 100644
index 0000000..921c805
--- /dev/null
+++ b/src/plugins/android.codeutils/templates/activity_samples/quickaction/quick_action.java
@@ -0,0 +1,180 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package #package_name#;
+
+import android.app.Activity;
+import android.graphics.drawable.BitmapDrawable;
+import android.os.Bundle;
+import android.util.TypedValue;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.View.OnTouchListener;
+import android.view.ViewGroup;
+import android.view.ViewGroup.LayoutParams;
+import android.view.animation.Animation;
+import android.view.animation.AnimationUtils;
+import android.widget.ImageView;
+import android.widget.PopupWindow;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import #ManifestPackageName#.R;
+
+public class #class_name# extends Activity {
+
+ /*
+ * Pixels added to QuickAction window growing window area to avoid fading edges
+ */
+ private static final int MARGIN_ADJUSTMENT = 8;
+
+ /*
+ * Pixels added to QuickAction window growing window area to avoid fading edges
+ */
+ private static final int ACTION_ITEM_SIZE = 64;
+
+ /*
+ * The current quick action window (to dismiss on application pause/rotation)
+ */
+ private PopupWindow currentQuickActionWindow = null;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.#layout_name#quickaction/quick_action_activity.xml#);
+ }
+
+ /**
+ * Create an action item
+ * @param name the action name
+ * @param imageResId the image ID to be set in the action item
+ * @return
+ */
+ private View createAction (final String name, int imageResId) {
+ View action = getLayoutInflater().inflate(R.layout.#layout_name#quickaction/quick_action_item.xml#, null);
+ ImageView image = (ImageView) action.findViewById(R.id.action_image);
+ TextView text = (TextView) action.findViewById(R.id.action_name);
+ text.setText(name);
+
+ /*
+ * Set image dimensions (Density Independent Pixels)
+ */
+ action.setMinimumHeight((int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, ACTION_ITEM_SIZE, getResources().getDisplayMetrics()));
+ action.setMinimumWidth((int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, ACTION_ITEM_SIZE, getResources().getDisplayMetrics()));
+ image.setImageResource(imageResId);
+
+ /*
+ * Set action being performed when item action is clicked
+ */
+ action.setOnClickListener(new OnClickListener() {
+ public void onClick(View v) {
+ /*
+ * TODO: Place your action code here
+ */
+ Toast.makeText(#class_name#.this, "You have selected " + name , Toast.LENGTH_SHORT).show();
+ }
+ });
+ return action;
+ }
+
+ public void openQuickActionWindow(View target) {
+ /*
+ * Get the layout of quick action window
+ */
+ ViewGroup quickActionLayout = (ViewGroup) getLayoutInflater()
+ .inflate(R.layout.#layout_name#quickaction/quick_action_grid.xml#, null);
+
+ /*
+ * Get the quick action bar
+ */
+ ViewGroup quickActionBar = (ViewGroup)quickActionLayout.findViewById(R.id.quick_action_bar);
+
+ /*
+ * Add the actions (change to your own needs)
+ */
+ quickActionBar.addView(createAction("Action 1", R.drawable.#drawable_name#quickaction/icon.png#));
+ quickActionBar.addView(createAction("Action 2", R.drawable.#drawable_name#quickaction/icon.png#));
+ quickActionBar.addView(createAction("Action 3", R.drawable.#drawable_name#quickaction/icon.png#));
+ quickActionBar.addView(createAction("Action n", R.drawable.#drawable_name#quickaction/icon.png#));
+
+ /*
+ * Create the window and set the content
+ * It is important to set the right context due to touch events
+ */
+ currentQuickActionWindow = new PopupWindow(getApplicationContext());
+ currentQuickActionWindow.setContentView(quickActionLayout);
+
+ /*
+ * The popup must be touchable (to touch actions),
+ * focusable (if you have more actions than space) and,
+ * outside touchable (to be able to close the window)
+ */
+ currentQuickActionWindow.setTouchable(true);
+ currentQuickActionWindow.setFocusable(true);
+ currentQuickActionWindow.setOutsideTouchable(true);
+
+ /*
+ * Make background transparent
+ */
+ currentQuickActionWindow.setBackgroundDrawable(new BitmapDrawable());
+
+ /*
+ * Close Quick Action window when clicked outside
+ */
+ currentQuickActionWindow.setTouchInterceptor(new OnTouchListener() {
+ public boolean onTouch(View v, MotionEvent event) {
+ if (event.getAction() == MotionEvent.ACTION_OUTSIDE) {
+ currentQuickActionWindow.dismiss();
+ return true;
+ }
+ return false;
+ }
+ });
+
+ /*
+ * Measure the Quick Action window.
+ * Add MARGIN_ADJUSTMENT density independent pixels to the size in order to not show scrolls and left some edge spaces
+ * Check layout files to check
+ */
+ quickActionLayout.measure(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT);
+ currentQuickActionWindow.setWidth(quickActionLayout.getMeasuredWidth() + (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, MARGIN_ADJUSTMENT, getResources().getDisplayMetrics()));
+ currentQuickActionWindow.setHeight(quickActionLayout.getMeasuredHeight() + (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, MARGIN_ADJUSTMENT, getResources().getDisplayMetrics()));
+
+ /*
+ * Open the Quick Action window
+ */
+ currentQuickActionWindow.showAsDropDown(target);
+
+ /*
+ * Animate action list
+ * This must be manually called
+ */
+ Animation animation = AnimationUtils.loadAnimation(getApplicationContext(), R.anim.#anim_name#quickaction/quickaction_list_anim.xml#);
+ quickActionBar.startAnimation(animation);
+ }
+
+ @Override
+ protected void onPause() {
+ /*
+ * Dismiss quick action if still opened
+ */
+ if (currentQuickActionWindow != null) {
+ currentQuickActionWindow.dismiss();
+ }
+ super.onPause();
+ }
+} \ No newline at end of file
diff --git a/src/plugins/android.codeutils/templates/activity_samples/quickaction/quick_action_activity.xml b/src/plugins/android.codeutils/templates/activity_samples/quickaction/quick_action_activity.xml
new file mode 100644
index 0000000..232b0db
--- /dev/null
+++ b/src/plugins/android.codeutils/templates/activity_samples/quickaction/quick_action_activity.xml
@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/main"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:gravity="fill"
+ android:orientation="vertical" >
+
+ <RelativeLayout
+ android:id="@+id/buttons_top"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_alignParentTop="true" >
+
+ <Button
+ android:id="@+id/buttonTopLeft"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentLeft="true"
+ android:onClick="openQuickActionWindow"
+ android:text="@string/press_here" />
+
+ <Button
+ android:id="@+id/buttonTopRight"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentRight="true"
+ android:onClick="openQuickActionWindow"
+ android:text="@string/press_here" />
+ </RelativeLayout>
+
+ <ImageButton
+ android:id="@+id/imageView"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_centerInParent="true"
+ android:background="@drawable/#drawable_name#quickaction/imgbuttonselector_background.xml#"
+ android:minHeight="96dp"
+ android:minWidth="96dp"
+ android:onClick="openQuickActionWindow"
+ android:src="@drawable/#drawable_name#quickaction/imgbuttonselector.xml#"
+ android:scaleType="fitXY"/>
+
+ <RelativeLayout
+ android:id="@+id/buttons_bottom"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_alignParentBottom="true" >
+
+ <Button
+ android:id="@+id/buttonBottomLeft"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentLeft="true"
+ android:onClick="openQuickActionWindow"
+ android:text="@string/press_here" />
+
+ <Button
+ android:id="@+id/buttonBottomRight"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentRight="true"
+ android:onClick="openQuickActionWindow"
+ android:text="@string/press_here" />
+ </RelativeLayout>
+
+</RelativeLayout> \ No newline at end of file
diff --git a/src/plugins/android.codeutils/templates/activity_samples/quickaction/quick_action_grid.xml b/src/plugins/android.codeutils/templates/activity_samples/quickaction/quick_action_grid.xml
new file mode 100644
index 0000000..375c418
--- /dev/null
+++ b/src/plugins/android.codeutils/templates/activity_samples/quickaction/quick_action_grid.xml
@@ -0,0 +1,36 @@
+<?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="wrap_content"
+ android:orientation="vertical"
+ android:background="@android:color/transparent">
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:background="@drawable/#drawable_name#quickaction/quickaction_list_top.9.png#" />
+
+ <HorizontalScrollView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:background="@android:color/background_light"
+ android:fadingEdgeLength="0dp"
+ android:scrollbars="none" >
+
+ <LinearLayout
+ android:id="@+id/quick_action_bar"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingLeft="4dp"
+ android:paddingTop="4dp"
+ android:paddingBottom="4dp" >
+ </LinearLayout>
+ </HorizontalScrollView>
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:background="@drawable/#drawable_name#quickaction/quickaction_list_bottom.9.png#"/>
+
+</LinearLayout> \ No newline at end of file
diff --git a/src/plugins/android.codeutils/templates/activity_samples/quickaction/quick_action_item.xml b/src/plugins/android.codeutils/templates/activity_samples/quickaction/quick_action_item.xml
new file mode 100644
index 0000000..a311bd8
--- /dev/null
+++ b/src/plugins/android.codeutils/templates/activity_samples/quickaction/quick_action_item.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:background="@android:drawable/btn_default"
+ android:layout_marginLeft="2dp"
+ android:clickable="true"
+ >
+
+ <TextView
+ android:id="@+id/action_name"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ />
+
+ <ImageView
+ android:id="@+id/action_image"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_gravity="center_horizontal"
+ />
+
+</LinearLayout> \ No newline at end of file
diff --git a/src/plugins/android.codeutils/templates/activity_samples/quickaction/quick_action_strings.xml b/src/plugins/android.codeutils/templates/activity_samples/quickaction/quick_action_strings.xml
new file mode 100644
index 0000000..2fdfaca
--- /dev/null
+++ b/src/plugins/android.codeutils/templates/activity_samples/quickaction/quick_action_strings.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<resources>
+ <string name="press_here">Touch Me!</string>
+</resources>
diff --git a/src/plugins/android.codeutils/templates/activity_samples/quickaction/quickaction_list_anim.xml b/src/plugins/android.codeutils/templates/activity_samples/quickaction/quickaction_list_anim.xml
new file mode 100644
index 0000000..52e8be8
--- /dev/null
+++ b/src/plugins/android.codeutils/templates/activity_samples/quickaction/quickaction_list_anim.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<translate xmlns:android="http://schemas.android.com/apk/res/android"
+ android:fromXDelta="100%p"
+ android:toXDelta="0"
+ android:duration="400"/> \ No newline at end of file
diff --git a/src/plugins/android.codeutils/templates/activity_samples/quickaction/quickaction_list_bottom.9.png b/src/plugins/android.codeutils/templates/activity_samples/quickaction/quickaction_list_bottom.9.png
new file mode 100644
index 0000000..eb8566d
--- /dev/null
+++ b/src/plugins/android.codeutils/templates/activity_samples/quickaction/quickaction_list_bottom.9.png
Binary files differ
diff --git a/src/plugins/android.codeutils/templates/activity_samples/quickaction/quickaction_list_top.9.png b/src/plugins/android.codeutils/templates/activity_samples/quickaction/quickaction_list_top.9.png
new file mode 100644
index 0000000..4f002f2
--- /dev/null
+++ b/src/plugins/android.codeutils/templates/activity_samples/quickaction/quickaction_list_top.9.png
Binary files differ
diff --git a/src/plugins/android.codeutils/templates/activity_samples/radio_button/radio_button_activity.java b/src/plugins/android.codeutils/templates/activity_samples/radio_button/radio_button_activity.java
new file mode 100644
index 0000000..b365b70
--- /dev/null
+++ b/src/plugins/android.codeutils/templates/activity_samples/radio_button/radio_button_activity.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package #package_name#;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.widget.RadioButton;
+import android.widget.RadioGroup;
+import android.widget.TextView;
+import android.widget.RadioGroup.OnCheckedChangeListener;
+
+import #ManifestPackageName#.R;
+
+public class #class_name# extends Activity implements OnCheckedChangeListener {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.#layout_name#radio_button/radiobuttonlayout.xml#);
+ RadioGroup rg = (RadioGroup) findViewById(R.id.myradiogroup);
+ rg.setOnCheckedChangeListener(this);
+ }
+
+ public void onCheckedChanged(RadioGroup group, int checkedId) {
+ for (int i = 0; i < group.getChildCount(); i++) {
+ RadioButton curBut = (RadioButton) group.getChildAt(i);
+ if (curBut.isChecked()) {
+ ((TextView) findViewById(R.id.mytext1)).setText(getString(R.string.sample_radio_btn_selected) +
+ curBut.getText());
+ break;
+ }
+ }
+ }
+}
diff --git a/src/plugins/android.codeutils/templates/activity_samples/radio_button/radio_strings.xml b/src/plugins/android.codeutils/templates/activity_samples/radio_button/radio_strings.xml
new file mode 100644
index 0000000..a00915b
--- /dev/null
+++ b/src/plugins/android.codeutils/templates/activity_samples/radio_button/radio_strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <string name="sample_radio_1">Option 1</string>
+ <string name="sample_radio_2">Option 2</string>
+ <string name="sample_radio_3">Option 3</string>
+ <string name="sample_radio_btn_selected">You selected: </string>
+ <string name="sample_radio_btn_none">none</string>
+</resources> \ No newline at end of file
diff --git a/src/plugins/android.codeutils/templates/activity_samples/radio_button/radiobuttonlayout.xml b/src/plugins/android.codeutils/templates/activity_samples/radio_button/radiobuttonlayout.xml
new file mode 100644
index 0000000..356b34d
--- /dev/null
+++ b/src/plugins/android.codeutils/templates/activity_samples/radio_button/radiobuttonlayout.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="vertical" >
+ <RadioGroup
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:id="@+id/myradiogroup">
+ <RadioButton
+ android:text="@string/sample_radio_1"
+ android:id="@+id/myradio1" />
+ <RadioButton
+ android:text="@string/sample_radio_2"
+ android:id="@+id/myradio2" />
+ <RadioButton
+ android:text="@string/sample_radio_3"
+ android:id="@+id/myradio3" />
+ </RadioGroup>
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/sample_radio_btn_selected"
+ android:id="@+id/mytext1" />
+</LinearLayout> \ No newline at end of file
diff --git a/src/plugins/android.codeutils/templates/activity_samples/samples_config.xml b/src/plugins/android.codeutils/templates/activity_samples/samples_config.xml
new file mode 100644
index 0000000..eb17666
--- /dev/null
+++ b/src/plugins/android.codeutils/templates/activity_samples/samples_config.xml
@@ -0,0 +1,69 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <sample name="%Activity_Samples_DB_name" description="%Activity_Samples_DB_description">
+ <file name="database_list/database_list_activity.java" resourceType="src" />
+ <file name="database_list/databaselayout.xml" resourceType="layout" />
+ </sample>
+ <sample name="%Activity_Samples_ListActivities_name" description="%Activity_Samples_ListActivities_description">
+ </sample>
+ <sample name="%Activity_Samples_Dialog_name" description="%Activity_Samples_Dialog_description">
+ <file name="dialog/dialog_activity.java" resourceType="src" />
+ <file name="dialog/dialoglayout.xml" resourceType="layout" />
+ <file name="dialog/dialog_strings.xml" resourceType="values" />
+ </sample>
+ <sample name="%Activity_Samples_Multitouch_name" description="%Activity_Samples_Multitouch_description">
+ <file name="multitouch_event/multitouch_event_activity.java" resourceType="src" />
+ <file name="multitouch_event/multitouchlayout.xml" resourceType="layout" />
+ <file name="multitouch_event/multitouch.jpg" resourceType="drawable" />
+ </sample>
+ <sample name="%Activity_Samples_Preference_name" description="%Activity_Samples_Preference_description">
+ <file name="pref_activity/preference_activity.java" resourceType="src" />
+ <file name="pref_activity/samplepreferences.xml" resourceType="xml" />
+ <file name="pref_activity/samplelist.xml" resourceType="values" />
+ </sample>
+ <sample name="%Activity_Samples_RadioButton_name" description="%Activity_Samples_RadioButton_description">
+ <file name="radio_button/radio_button_activity.java" resourceType="src" />
+ <file name="radio_button/radiobuttonlayout.xml" resourceType="layout" />
+ <file name="radio_button/radio_strings.xml" resourceType="values" />
+ </sample>
+ <sample name="%Activity_Samples_Tabs_name" description="%Activity_Samples_Tabs_description">
+ <file name="tabs/tabs_activity.java" resourceType="src" />
+ <file name="tabs/tabicon.png" resourceType="drawable" />
+ <file name="tabs/tabs_strings.xml" resourceType="values" />
+ </sample>
+ <sample name="%Activity_Samples_DashboardPattern_name" description="%Activity_Samples_DashboardPattern_description">
+ <file name="dashboard_pattern/dashboard_activity.java" resourceType="src" />
+ <file name="dashboard_pattern/dashboard_icon.png" resourceType="drawable" />
+ <file name="dashboard_pattern/dashboard_item_background.xml" resourceType="drawable" />
+ <file name="dashboard_pattern/dashboard_strings.xml" resourceType="values" />
+ <file name="dashboard_pattern/dashboard_layout.xml" finalName="dashboard.xml" resourceType="layout" />
+ <file name="dashboard_pattern/dashboard_layout_land.xml" finalName="dashboard.xml" resourceType="layout" modifier="land" />
+ </sample>
+ <sample name="%Activity_Samples_ActionBar_name" description="%Activity_Samples_ActionBar_description">
+ <file name="action_bar/action_bar_activity.java" resourceType="src"/>
+ <file name="action_bar/ablayout.xml" resourceType="layout" />
+ <file name="action_bar/abmenu.xml" resourceType="menu" />
+ <file name="action_bar/action_bar_strings.xml" resourceType="values" />
+ <file name="action_bar/actionbaricon.png" resourceType="drawable" />
+ </sample>
+ <sample name="%Activity_Samples_ActionBarCompatibility_name" description="%Activity_Samples_ActionBarCompatibility_description">
+ <file name="action_bar_compatibility/action_bar_compatibility_activity.java" resourceType="src"/>
+ <file name="action_bar_compatibility/ablayout.xml" resourceType="layout" />
+ <file name="action_bar_compatibility/actionbar.xml" resourceType="layout" />
+ <file name="action_bar_compatibility/action_bar_compatibility_strings.xml" resourceType="values" />
+ <file name="action_bar/actionbaricon.png" resourceType="drawable" />
+ </sample>
+ <sample name="%Activity_Samples_QuickAction_name" description="%Activity_Samples_QuickAction_description">
+ <file name="quickaction/quick_action.java" resourceType="src"/>
+ <file name="quickaction/quick_action_item.xml" resourceType="layout" />
+ <file name="quickaction/quick_action_grid.xml" resourceType="layout" />
+ <file name="quickaction/quick_action_activity.xml" resourceType="layout" />
+ <file name="quickaction/quickaction_list_bottom.9.png" resourceType="drawable" />
+ <file name="quickaction/quickaction_list_top.9.png" resourceType="drawable" />
+ <file name="quickaction/icon.png" resourceType="drawable" />
+ <file name="quickaction/imgbuttonselector_background.xml" resourceType="drawable" />
+ <file name="quickaction/imgbuttonselector.xml" resourceType="drawable" />
+ <file name="quickaction/quickaction_list_anim.xml" resourceType="anim" />
+ <file name="quickaction/quick_action_strings.xml" resourceType="values" />
+ </sample>
+</resources> \ No newline at end of file
diff --git a/src/plugins/android.codeutils/templates/activity_samples/selection_list/preview.png b/src/plugins/android.codeutils/templates/activity_samples/selection_list/preview.png
new file mode 100644
index 0000000..3153ff4
--- /dev/null
+++ b/src/plugins/android.codeutils/templates/activity_samples/selection_list/preview.png
Binary files differ
diff --git a/src/plugins/android.codeutils/templates/activity_samples/selection_list/selection_list_activity.java b/src/plugins/android.codeutils/templates/activity_samples/selection_list/selection_list_activity.java
new file mode 100644
index 0000000..daa88df
--- /dev/null
+++ b/src/plugins/android.codeutils/templates/activity_samples/selection_list/selection_list_activity.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package #package_name#;
+
+import android.app.ListActivity;
+import android.content.res.Resources;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.ArrayAdapter;
+import android.widget.ListView;
+import android.widget.Toast;
+
+import #ManifestPackageName#.R;
+
+public class #class_name# extends ListActivity {
+
+ private static final boolean allowMultipleSelection = false;
+
+ private static String[] listItems;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ Resources res = getResources();
+ listItems = res.getStringArray(R.array.sample_selection_items);
+
+ setListAdapter(new ArrayAdapter<String>(this,
+ android.R.layout.simple_list_item_single_choice, listItems));
+
+ final ListView listView = getListView();
+
+ if(allowMultipleSelection)
+ {
+ listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
+ }
+ else
+ {
+ listView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
+ }
+ }
+
+ @Override
+ protected void onListItemClick(ListView l, View v, int position, long id) {
+ Toast.makeText(l.getContext(), getString(R.string.sample_selection_selected) +
+ l.getItemAtPosition(position).toString(), Toast.LENGTH_SHORT).show();
+ }
+
+}
diff --git a/src/plugins/android.codeutils/templates/activity_samples/selection_list/selection_strings.xml b/src/plugins/android.codeutils/templates/activity_samples/selection_list/selection_strings.xml
new file mode 100644
index 0000000..05eece0
--- /dev/null
+++ b/src/plugins/android.codeutils/templates/activity_samples/selection_list/selection_strings.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <string name="sample_selection_selected">Selected: </string>
+ <string-array name="sample_selection_items">
+ <item>List Item 1</item>
+ <item>List Item 2</item>
+ <item>List Item 3</item>
+ <item>List Item 4</item>
+ <item>List Item 5</item>
+ <item>List Item 6</item>
+ <item>List Item 7</item>
+ <item>List Item 8</item>
+ <item>List Item 9</item>
+ <item>List Item 10</item>
+ </string-array>
+</resources>
diff --git a/src/plugins/android.codeutils/templates/activity_samples/simple_list/preview.png b/src/plugins/android.codeutils/templates/activity_samples/simple_list/preview.png
new file mode 100644
index 0000000..99207c1
--- /dev/null
+++ b/src/plugins/android.codeutils/templates/activity_samples/simple_list/preview.png
Binary files differ
diff --git a/src/plugins/android.codeutils/templates/activity_samples/simple_list/simple_list_activity.java b/src/plugins/android.codeutils/templates/activity_samples/simple_list/simple_list_activity.java
new file mode 100644
index 0000000..bf3876b
--- /dev/null
+++ b/src/plugins/android.codeutils/templates/activity_samples/simple_list/simple_list_activity.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package #package_name#;
+
+import android.app.ListActivity;
+import android.content.res.Resources;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.ArrayAdapter;
+import android.widget.ListView;
+import android.widget.Toast;
+
+import #ManifestPackageName#.R;
+
+public class #class_name# extends ListActivity {
+
+ private String[] listItems = null;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ //load list
+ Resources res = getResources();
+ listItems = res.getStringArray(R.array.sample_simple_list_items);
+
+ // maps an array to TextViews
+ setListAdapter(new ArrayAdapter<String>(this,
+ android.R.layout.simple_list_item_1, listItems));
+ }
+
+ @Override
+ protected void onListItemClick(ListView lv, View v, int position, long id) {
+ // your action here
+ Toast.makeText(lv.getContext(), getString(R.string.sample_simple_list_selected) +
+ lv.getItemAtPosition(position).toString(), Toast.LENGTH_SHORT).show();
+ }
+}
diff --git a/src/plugins/android.codeutils/templates/activity_samples/simple_list/simple_list_strings.xml b/src/plugins/android.codeutils/templates/activity_samples/simple_list/simple_list_strings.xml
new file mode 100644
index 0000000..509e05c
--- /dev/null
+++ b/src/plugins/android.codeutils/templates/activity_samples/simple_list/simple_list_strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <string name="sample_simple_list_selected">Selected: </string>
+ <string-array name="sample_simple_list_items">
+ <item>List Item 1</item>
+ <item>List Item 2</item>
+ <item>List Item 3</item>
+ <item>List Item 4</item>
+ <item>List Item 5</item>
+ <item>List Item 6</item>
+ <item>List Item 7</item>
+ <item>List Item 8</item>
+ <item>List Item 9</item>
+ <item>List Item 10</item>
+ <item>List Item 11</item>
+ <item>List Item 12</item>
+ <item>List Item 13</item>
+ <item>List Item 14</item>
+ <item>List Item 15</item>
+ </string-array>
+</resources>
diff --git a/src/plugins/android.codeutils/templates/activity_samples/single_selection_img/listiconsingle.png b/src/plugins/android.codeutils/templates/activity_samples/single_selection_img/listiconsingle.png
new file mode 100644
index 0000000..4bff905
--- /dev/null
+++ b/src/plugins/android.codeutils/templates/activity_samples/single_selection_img/listiconsingle.png
Binary files differ
diff --git a/src/plugins/android.codeutils/templates/activity_samples/single_selection_img/listrowsingle.xml b/src/plugins/android.codeutils/templates/activity_samples/single_selection_img/listrowsingle.xml
new file mode 100644
index 0000000..0bbe9fc
--- /dev/null
+++ b/src/plugins/android.codeutils/templates/activity_samples/single_selection_img/listrowsingle.xml
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/ll01"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+
+ <ImageView android:id="@+id/img1"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:padding="6dip"/>
+
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <TextView android:id="@+id/text1"
+ android:textSize="12sp"
+ android:textStyle="bold"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingLeft="10dip"
+ android:paddingTop="6dip"/>
+
+ <TextView android:id="@+id/text2"
+ android:textSize="12sp"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingLeft="10dip"/>
+ </LinearLayout>
+
+ <RelativeLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="right"
+ android:layout_gravity="right"
+ >
+
+ <RadioButton
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:checked="false"
+ android:clickable="false"
+ android:id="@+id/radio"/>
+
+</RelativeLayout>
+</LinearLayout> \ No newline at end of file
diff --git a/src/plugins/android.codeutils/templates/activity_samples/single_selection_img/listviewsingle.xml b/src/plugins/android.codeutils/templates/activity_samples/single_selection_img/listviewsingle.xml
new file mode 100644
index 0000000..9999ae9
--- /dev/null
+++ b/src/plugins/android.codeutils/templates/activity_samples/single_selection_img/listviewsingle.xml
@@ -0,0 +1,15 @@
+<?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">
+
+ <ListView
+ android:id="@id/android:list"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:drawSelectorOnTop="false"
+ />
+
+</LinearLayout> \ No newline at end of file
diff --git a/src/plugins/android.codeutils/templates/activity_samples/single_selection_img/preview.png b/src/plugins/android.codeutils/templates/activity_samples/single_selection_img/preview.png
new file mode 100644
index 0000000..11d9818
--- /dev/null
+++ b/src/plugins/android.codeutils/templates/activity_samples/single_selection_img/preview.png
Binary files differ
diff --git a/src/plugins/android.codeutils/templates/activity_samples/single_selection_img/single_sel_img_activity.java b/src/plugins/android.codeutils/templates/activity_samples/single_selection_img/single_sel_img_activity.java
new file mode 100644
index 0000000..1c271f9
--- /dev/null
+++ b/src/plugins/android.codeutils/templates/activity_samples/single_selection_img/single_sel_img_activity.java
@@ -0,0 +1,242 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package #package_name#;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import android.app.ListActivity;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.RadioButton;
+import android.widget.SimpleAdapter;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import #ManifestPackageName#.R;
+
+public class #class_name# extends ListActivity implements OnClickListener
+{
+ private final String SELECTED_ITEM_KEY = "selected_items";
+ public final String TEXT_KEY_1 = "title";
+ public final String TEXT_KEY_2 = "description";
+ public final String ITEM_ID = "id";
+ public final String IMG_KEY = "img";
+ public final String RADIO_KEY = "radio";
+ private Integer selectedItem = -1;
+ private RadioButton selectedRadio;
+
+ public void onCreate(Bundle savedInstanceState)
+ {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.#layout_name#single_selection_img/listviewsingle.xml#);
+
+ // list data
+ List<Map<String, Object>> resourceNames =
+ new ArrayList<Map<String, Object>>();
+
+ generateData(resourceNames);
+
+ MySingleAdapter notes = new MySingleAdapter(
+ this,
+ resourceNames,
+ R.layout.#layout_name#single_selection_img/listrowsingle.xml#,
+ new String[] { TEXT_KEY_1,TEXT_KEY_2, IMG_KEY, RADIO_KEY, ITEM_ID },
+ new int[] { R.id.text1, R.id.text2, R.id.img1, R.id.radio});
+
+ setListAdapter(notes);
+ }
+
+ /*
+ * Populate list
+ */
+ private void generateData(List<Map<String, Object>> resourceNames)
+ {
+ //TODO here you will fill resourceNames with your own data
+ HashMap<String, Object> data;
+ int NUM_ITEMS = 50;
+
+ for (int i = 0; i <= NUM_ITEMS; i++)
+ {
+ data = new HashMap<String, Object>();
+ data.put(ITEM_ID, i);
+ data.put(TEXT_KEY_1, getString(R.string.list_item) + " " + Integer.toString(i));
+ data.put(TEXT_KEY_2, getString(R.string.description));
+ data.put(IMG_KEY, R.drawable.#drawable_name#single_selection_img/listiconsingle.png#);
+ data.put(RADIO_KEY, false);
+ resourceNames.add(data);
+ }
+ }
+
+ /*
+ * Prints on screen the currently selected item
+ */
+ public void printMessage()
+ {
+ //TODO execute your action here
+
+ StringBuilder strText = new StringBuilder();
+ if(!selectedItem.equals(-1))
+ {
+ strText.append(getString(R.string.selected) + " ");
+ strText.append(selectedItem.toString());
+ }
+ else
+ {
+ strText.append(getString(R.string.noselection));
+ }
+
+ Toast.makeText(getApplicationContext(), strText.toString(),
+ Toast.LENGTH_SHORT).show();
+ }
+
+ public void onClick(View v) {
+
+ //handles list item click
+ RadioButton radio = (RadioButton) v.findViewById(R.id.radio);
+ boolean checked = radio.isChecked();
+ //updates selected list
+ if(checked)
+ {
+ selectedRadio = null;
+ selectedItem = -1;
+ }
+ else
+ {
+ if(!selectedItem.equals(-1))
+ {
+ selectedRadio.setChecked(false);
+ }
+ selectedItem = v.getId();
+ selectedRadio = radio;
+ }
+
+ //update check box value
+ radio.setChecked(!checked);
+ printMessage();
+ }
+
+ public void setSelectedRadio(RadioButton selectedRadio)
+ {
+ this.selectedRadio = selectedRadio;
+ }
+
+ /*
+ * Restores list selection
+ */
+ @Override
+ protected void onRestoreInstanceState(Bundle state) {
+ super.onRestoreInstanceState(state);
+ selectedItem = state.getInt(SELECTED_ITEM_KEY, -1);
+ }
+
+ /* when the device is rotated, this activity is killed
+ * this method is called when activity is about to be shut down and saves
+ * the current list selection
+ */
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ outState.putInt(SELECTED_ITEM_KEY, selectedItem);
+ }
+
+ public Integer getSelectedItem()
+ {
+ return selectedItem;
+ }
+
+ class MySingleAdapter extends SimpleAdapter
+ {
+ List<? extends Map<String, ?>> resourceNames;
+ #class_name# context;
+ String[] strKeys;
+
+ public MySingleAdapter(#class_name# context, List<? extends Map<String, ?>> data,
+ int resource, String[] from, int[] to) {
+ super(context, data, resource, from, to);
+ this.context = context;
+ resourceNames = data;
+ strKeys = from;
+ }
+
+ /* Return a view to be added on the list
+ * When we scroll the list, some items leave the screen becoming invisible to the user.
+ * Since creating views is an expensive task, we'd rather recycle these not visible views,
+ * that are referenced by convertView, updating its fields values.*/
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+
+ // used to improve performance, since we call findViewById
+ // only once for each created, but not recycled, view
+ ViewHolder holder;
+
+ //view to be recycled
+ if(convertView == null)
+ {
+ holder = new ViewHolder();
+
+ convertView = LayoutInflater.from(parent.getContext()).inflate(
+ R.layout.#layout_name#single_selection_img/listrowsingle.xml#, null);
+
+ holder.textView1 = (TextView) convertView.findViewById(R.id.text1);
+ holder.textView2 = (TextView) convertView.findViewById(R.id.text2);
+ holder.imgView = (ImageView) convertView.findViewById(R.id.img1);
+ holder.radioButton = (RadioButton) convertView.findViewById(R.id.radio);
+
+ convertView.setTag(holder);
+ }
+ else
+ {
+ holder = (ViewHolder) convertView.getTag();
+ }
+ Map<String, ?> currentData = resourceNames.get(position);
+
+ //updates list items values
+ holder.textView1.setText(currentData.get(context.TEXT_KEY_1).toString());
+ holder.textView2.setText(currentData.get(context.TEXT_KEY_2).toString());
+ holder.imgView.setImageResource((Integer) currentData.get(context.IMG_KEY));
+ holder.radioButton.setChecked(context.getSelectedItem().equals(
+ (Integer) currentData.get(context.ITEM_ID)));
+
+ if(holder.radioButton.isChecked())
+ {
+ context.setSelectedRadio(holder.radioButton);
+ }
+
+ convertView.setId((Integer) currentData.get(context.ITEM_ID));
+ convertView.setOnClickListener(context);
+
+ return convertView;
+ }
+ }
+
+ /*
+ * Holds references to list items
+ */
+ class ViewHolder
+ {
+ TextView textView1, textView2;
+ ImageView imgView;
+ RadioButton radioButton;
+ }
+}
+
diff --git a/src/plugins/android.codeutils/templates/activity_samples/single_selection_img/single_sel_img_strings.xml b/src/plugins/android.codeutils/templates/activity_samples/single_selection_img/single_sel_img_strings.xml
new file mode 100644
index 0000000..28870c1
--- /dev/null
+++ b/src/plugins/android.codeutils/templates/activity_samples/single_selection_img/single_sel_img_strings.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<resources>
+ <string name="selected">You selected: </string>
+ <string name="list_item">List Item</string>
+ <string name="description">description</string>
+ <string name="noselection">No item selected</string>
+</resources>
diff --git a/src/plugins/android.codeutils/templates/activity_samples/tabs/tabicon.png b/src/plugins/android.codeutils/templates/activity_samples/tabs/tabicon.png
new file mode 100644
index 0000000..4bff905
--- /dev/null
+++ b/src/plugins/android.codeutils/templates/activity_samples/tabs/tabicon.png
Binary files differ
diff --git a/src/plugins/android.codeutils/templates/activity_samples/tabs/tabs_activity.java b/src/plugins/android.codeutils/templates/activity_samples/tabs/tabs_activity.java
new file mode 100644
index 0000000..0a477a3
--- /dev/null
+++ b/src/plugins/android.codeutils/templates/activity_samples/tabs/tabs_activity.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package #package_name#;
+
+import android.app.TabActivity;
+import android.content.res.Resources;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.TabHost;
+import android.widget.TextView;
+import android.widget.TabHost.TabContentFactory;
+import android.widget.TabHost.TabSpec;
+
+import #ManifestPackageName#.R;
+
+/*
+ * NOTES:
+ * - TabActivity class is deprecated since API level 13.
+ * - New applications are recommended to use fragments.
+ *
+ * */
+
+public class #class_name# extends TabActivity implements TabContentFactory{
+
+ private static int numTabs = 4;
+
+ private static final String TAB_TITLE = "Tab";
+ private static final String TAB_CONTENT = "content.";
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ TabHost host = getTabHost();
+
+ for(int i = 0; i < numTabs; i++)
+ {
+ TabSpec spec = host.newTabSpec(TAB_TITLE + i);
+ Resources res = getResources();
+ Drawable icon = res.getDrawable(R.drawable.tabicon);
+ //set tab text and icon
+ spec.setIndicator(TAB_TITLE + " " + i, icon);
+ spec.setContent(this);
+ host.addTab(spec);
+ }
+ }
+
+ public View createTabContent(String tag) {
+ TextView tv = new TextView(this);
+ tv.setText(tag + " " + TAB_CONTENT);
+ return tv;
+ }
+}
diff --git a/src/plugins/android.codeutils/templates/activity_samples/tabs/tabs_strings.xml b/src/plugins/android.codeutils/templates/activity_samples/tabs/tabs_strings.xml
new file mode 100644
index 0000000..cf83f1e
--- /dev/null
+++ b/src/plugins/android.codeutils/templates/activity_samples/tabs/tabs_strings.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <string name="sample_tab_tab">Tab</string>
+ <string name="sample_tab_content">content.</string>
+</resources>
diff --git a/src/plugins/android.codeutils/templates/widget_project/WidgetProvider.template b/src/plugins/android.codeutils/templates/widget_project/WidgetProvider.template
new file mode 100644
index 0000000..f8ced0e
--- /dev/null
+++ b/src/plugins/android.codeutils/templates/widget_project/WidgetProvider.template
@@ -0,0 +1,30 @@
+package PACKAGE;
+
+import android.app.PendingIntent;
+import android.appwidget.AppWidgetManager;
+import android.appwidget.AppWidgetProvider;
+import android.content.Context;
+import android.content.Intent;
+import android.widget.RemoteViews;
+
+public class WidgetProvider extends AppWidgetProvider {
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ super.onReceive(context, intent);
+ }
+
+ @Override
+ public void onUpdate(Context context, AppWidgetManager appWidgetManager,
+ int[] appWidgetIds) {
+ RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.widget_initial_layout);
+ Intent intent = new Intent(context, ACTIVITY_NAME.class);
+ PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0);
+ views.setOnClickPendingIntent(R.id.widget_root, pendingIntent);
+
+ appWidgetManager.updateAppWidget(appWidgetIds[0], views);
+ }
+
+
+
+}
diff --git a/src/plugins/android.codeutils/templates/widget_project/receiver.template b/src/plugins/android.codeutils/templates/widget_project/receiver.template
new file mode 100644
index 0000000..6df02ca
--- /dev/null
+++ b/src/plugins/android.codeutils/templates/widget_project/receiver.template
@@ -0,0 +1,6 @@
+ <receiver android:name="WidgetProvider">
+ <intent-filter>
+ <action android:name="android.appwidget.action.APPWIDGET_UPDATE"/>
+ </intent-filter>
+ <meta-data android:name="android.appwidget.provider" android:resource="@xml/widget_info"/>
+ </receiver> \ No newline at end of file
diff --git a/src/plugins/android.codeutils/templates/widget_project/widget_info.xml b/src/plugins/android.codeutils/templates/widget_project/widget_info.xml
new file mode 100644
index 0000000..853c627
--- /dev/null
+++ b/src/plugins/android.codeutils/templates/widget_project/widget_info.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<appwidget-provider
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:minWidth="142dp"
+ android:minHeight="72dp"
+ android:updatePeriodMillis="86400000"
+ android:initialLayout="@layout/widget_initial_layout"
+ >
+</appwidget-provider>
diff --git a/src/plugins/android.codeutils/templates/widget_project/widget_initial_layout.xml b/src/plugins/android.codeutils/templates/widget_project/widget_initial_layout.xml
new file mode 100644
index 0000000..d6f650d
--- /dev/null
+++ b/src/plugins/android.codeutils/templates/widget_project/widget_initial_layout.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/widget_root"
+ android:layout_width="142dp"
+ android:orientation="vertical"
+ android:layout_height="72dp"
+ >
+ <ImageView
+ android:id="@+id/ImageView01"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:src="@drawable/icon"
+ android:layout_gravity="center_vertical|center_horizontal"/>
+ <TextView
+ android:text="MotoWidget"
+ android:id="@+id/TextView01"
+ android:layout_gravity="center_vertical|center_horizontal"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textColor="#FFFFFF"
+ android:background="#000000"/>
+
+</LinearLayout>
+
diff --git a/src/plugins/android.linux.x86/.classpath b/src/plugins/android.linux.x86/.classpath
new file mode 100644
index 0000000..64c5e31
--- /dev/null
+++ b/src/plugins/android.linux.x86/.classpath
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.5"/>
+ <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/src/plugins/android.linux.x86/.project b/src/plugins/android.linux.x86/.project
new file mode 100644
index 0000000..bb37391
--- /dev/null
+++ b/src/plugins/android.linux.x86/.project
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>com.motorola.studio.android.linux.x86</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.ManifestBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.SchemaBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.pde.PluginNature</nature>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+</projectDescription>
diff --git a/src/plugins/android.linux.x86/META-INF/MANIFEST.MF b/src/plugins/android.linux.x86/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..99a11f5
--- /dev/null
+++ b/src/plugins/android.linux.x86/META-INF/MANIFEST.MF
@@ -0,0 +1,10 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: %Bundle-Name
+Bundle-SymbolicName: com.motorola.studio.android.linux.x86;singleton:=true
+Bundle-Version: 2.0.0.qualifier
+Bundle-Vendor: %Bundle-Vendor
+Fragment-Host: com.motorola.studio.android;bundle-version="2.0.0"
+Bundle-RequiredExecutionEnvironment: J2SE-1.5
+Bundle-Localization: fragment
+Eclipse-PlatformFilter: (& (osgi.os=linux) (osgi.arch=x86))
diff --git a/src/plugins/android.linux.x86/build.properties b/src/plugins/android.linux.x86/build.properties
new file mode 100644
index 0000000..53e94a7
--- /dev/null
+++ b/src/plugins/android.linux.x86/build.properties
@@ -0,0 +1,7 @@
+source.. = src/
+output.. = bin/
+bin.includes = META-INF/,\
+ .,\
+ gtk_bridge_app,\
+ fragment.properties,\
+ fragment.xml
diff --git a/src/plugins/android.linux.x86/fragment.properties b/src/plugins/android.linux.x86/fragment.properties
new file mode 100644
index 0000000..9bb6606
--- /dev/null
+++ b/src/plugins/android.linux.x86/fragment.properties
@@ -0,0 +1,3 @@
+Bundle-Name=MOTODEV Studio for Android Linux x86 Fragment
+Bundle-Vendor=Motorola Mobility, Inc.
+preferencePageName=Android Emulator \ No newline at end of file
diff --git a/src/plugins/android.linux.x86/fragment.xml b/src/plugins/android.linux.x86/fragment.xml
new file mode 100644
index 0000000..043c4f2
--- /dev/null
+++ b/src/plugins/android.linux.x86/fragment.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?eclipse version="3.4"?>
+<fragment>
+
+ <extension
+ point="org.eclipse.ui.preferencePages">
+ <page
+ category="com.motorola.studio.platform.ui.preference"
+ class="com.motorola.studio.android.preferences.ui.EmulatorPreferencePage"
+ id="com.motorola.studio.android.emulator.preferencepage"
+ name="%preferencePageName">
+ </page>
+ </extension>
+
+</fragment>
diff --git a/src/plugins/android.linux.x86/gtk_bridge_app b/src/plugins/android.linux.x86/gtk_bridge_app
new file mode 100644
index 0000000..85a42ff
--- /dev/null
+++ b/src/plugins/android.linux.x86/gtk_bridge_app
Binary files differ
diff --git a/src/plugins/android.linux.x86/src/com/motorola/studio/android/nativeos/NativeUI.java b/src/plugins/android.linux.x86/src/com/motorola/studio/android/nativeos/NativeUI.java
new file mode 100644
index 0000000..17d8e5a
--- /dev/null
+++ b/src/plugins/android.linux.x86/src/com/motorola/studio/android/nativeos/NativeUI.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.nativeos;
+
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.internal.gtk.OS;
+import org.eclipse.swt.widgets.Composite;
+
+import com.motorola.studio.android.nativeos.linux.gtk.GtkBridge;
+
+/***
+ * This class is responsible for provide LINUX X86 specific constants values
+ * and implementation of INativeUI interface
+ */
+@SuppressWarnings("restriction")
+public class NativeUI implements INativeUI
+{
+ String DEFAULT_COMMANDLINE = "";
+
+ String DEFAULT_USEVNC = "false";
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.nativeos.INativeUI#getDefaultCommandLine()
+ */
+ public String getDefaultCommandLine()
+ {
+ return DEFAULT_COMMANDLINE;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.nativeos.INativeUI#getDefaultUseVnc()
+ */
+ public String getDefaultUseVnc()
+ {
+ return DEFAULT_USEVNC;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.nativeos.INativeUI#getWindowHandle(java.lang.String)
+ */
+ public long getWindowHandle(String windowName)
+ {
+ return GtkBridge.getWindowHandle(windowName);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.nativeos.INativeUI#getWindowProperties(long)
+ */
+ public long getWindowProperties(long windowHandle)
+ {
+ return 0;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.nativeos.INativeUI#setWindowProperties(long, long)
+ */
+ public void setWindowProperties(long windowHandle, long originalProperties)
+ {
+ //Do nothing
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.nativeos.INativeUI#embedWindow(long, org.eclipse.swt.widgets.Composite)
+ */
+ public long embedWindow(long windowHandle, Composite composite)
+ {
+ long hnd = 0;
+ hnd = composite.embeddedHandle;
+ return GtkBridge.embedNativeWindow(hnd, windowHandle);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.nativeos.INativeUI#unembedWindow(long, long)
+ */
+ public void unembedWindow(long windowHandle, long originalParent)
+ {
+ GtkBridge.unembedNativeWindow(windowHandle, originalParent);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.nativeos.INativeUI#getWindowSize(long, long)
+ */
+ public Point getWindowSize(long originalWindowHandle, long windowHandle)
+ {
+ return GtkBridge.getWindowSize(originalWindowHandle);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.nativeos.INativeUI#setWindowStyle(long)
+ */
+ public void setWindowStyle(long windowHandle)
+ {
+ //Not needed on Linux
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.nativeos.INativeUI#hideWindow(long)
+ */
+ public void hideWindow(long windowHandle)
+ {
+ GtkBridge.hideWindow(windowHandle);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.nativeos.INativeUI#showWindow(long)
+ */
+ public void showWindow(long windowHandle)
+ {
+ GtkBridge.showWindow(windowHandle);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.nativeos.INativeUI#restoreWindow(long)
+ */
+ public void restoreWindow(long windowHandle)
+ {
+ GtkBridge.restoreWindow(windowHandle);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.nativeos.INativeUI#sendNextLayoutCommand(long, long)
+ */
+ public void sendNextLayoutCommand(long originalParent, long windowHandle)
+ {
+ GtkBridge.pressKey(originalParent, OS.GDK_Control_L);
+ GtkBridge.pressKey(originalParent, OS.GDK_F11);
+ GtkBridge.releaseKey(originalParent, OS.GDK_F11);
+ GtkBridge.releaseKey(originalParent, OS.GDK_Control_L);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.nativeos.INativeUI#isWindowEnabled(long)
+ */
+ public boolean isWindowEnabled(long windowHandle)
+ {
+ // Current not needed on Linux
+ return false;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.nativeos.INativeUI#setWindowFocus(long)
+ */
+ public void setWindowFocus(long windowHandle)
+ {
+ // Current not needed on Linux
+ }
+}
diff --git a/src/plugins/android.linux.x86/src/com/motorola/studio/android/nativeos/linux/gtk/GtkBridge.java b/src/plugins/android.linux.x86/src/com/motorola/studio/android/nativeos/linux/gtk/GtkBridge.java
new file mode 100644
index 0000000..aed2597
--- /dev/null
+++ b/src/plugins/android.linux.x86/src/com/motorola/studio/android/nativeos/linux/gtk/GtkBridge.java
@@ -0,0 +1,275 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.nativeos.linux.gtk;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.net.URL;
+import java.util.Arrays;
+
+import org.eclipse.core.runtime.FileLocator;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.swt.graphics.Point;
+import org.osgi.framework.Bundle;
+
+import com.motorola.studio.android.AndroidPlugin;
+import com.motorola.studio.android.common.log.StudioLogger;
+
+public class GtkBridge
+{
+ private static final String EXEC = getExecCommad();
+
+ private static final String DISPLAY = getDisplayNum();
+
+ public static String getDisplayNum()
+ {
+ String display = System.getenv("DISPLAY");
+ return display;
+ }
+
+ public static String getExecCommad()
+ {
+ String absoluteFile = "";
+ try
+ {
+ Bundle bundle = AndroidPlugin.getDefault().getBundle();
+ URL locationUrl = FileLocator.find(bundle, new Path("gtk_bridge_app"), null);
+ URL fileUrl = FileLocator.toFileURL(locationUrl);
+ File file = new File(fileUrl.getFile());
+ absoluteFile = file.getAbsolutePath();
+ StudioLogger.info(GtkBridge.class, "Using gtk bridge app at: " + absoluteFile);
+ checkExecutionPermission(absoluteFile);
+ }
+ catch (Exception e)
+ {
+ StudioLogger.error(GtkBridge.class, "Failed to retrieve gtk bridge app", e);
+ }
+ return absoluteFile;
+ }
+
+ private static void checkExecutionPermission(String executable) throws IOException
+ {
+ String[] command = new String[]
+ {
+ "chmod", "+x", executable
+ };
+ Runtime.getRuntime().exec(command);
+ }
+
+ public static synchronized String exec(String[] command)
+ {
+ String lastline = "";
+ Process p = null;
+ BufferedReader reader = null;
+ try
+ {
+ p = Runtime.getRuntime().exec(command);
+ reader = new BufferedReader(new InputStreamReader(p.getInputStream()));
+ String returnedValue = "";
+ do
+ {
+ returnedValue = reader.readLine();
+ if (returnedValue != null)
+ {
+ lastline = returnedValue;
+ }
+ }
+ while (returnedValue != null);
+ int exitCode = p.waitFor();
+ if (exitCode != 0)
+ {
+ StudioLogger.debug(GtkBridge.class, "Command " + Arrays.toString(command)
+ + " finished with error code:" + exitCode);
+ }
+ }
+ catch (Exception e)
+ {
+ StudioLogger.error(GtkBridge.class,
+ "Failed to execute command :" + Arrays.toString(command), e);
+ }
+ finally
+ {
+ if (p != null)
+ {
+ try
+ {
+ p.getInputStream().close();
+ }
+ catch (IOException e)
+ {
+ //Do nothing.
+ }
+ try
+ {
+ p.getOutputStream().close();
+ }
+ catch (IOException e)
+ {
+ //Do nothing.
+ }
+ try
+ {
+ p.getErrorStream().close();
+ }
+ catch (IOException e)
+ {
+ //Do nothing.
+ }
+ }
+ if (reader != null)
+ {
+ try
+ {
+ reader.close();
+ }
+ catch (IOException e)
+ {
+ //Do nothing.
+ }
+ }
+ }
+
+ return lastline;
+ }
+
+ private static long exec_toLong(String[] command)
+ {
+ long response = 0;
+ try
+ {
+ String returnedValue = exec(command);
+ if (!"".equals(command))
+ {
+ response = Long.parseLong(returnedValue);
+ }
+ }
+ catch (Exception e)
+ {
+ StudioLogger.error(GtkBridge.class, "Failed to execute command", e);
+ }
+ return response;
+ }
+
+ public static long getWindowHandle(String windowName)
+ {
+ StudioLogger.debug(GtkBridge.class, "getWindowHandle " + windowName);
+
+ String[] command =
+ {
+ EXEC, "getWindowHandler", DISPLAY, windowName
+ };
+ long handle = exec_toLong(command);
+ StudioLogger.debug(GtkBridge.class, "getWindowHandle handle=" + handle);
+ return handle;
+ }
+
+ public static long embedNativeWindow(long parentHandle, long windowHandle)
+ {
+ StudioLogger.debug(GtkBridge.class, "embedNativeWindow parent=" + parentHandle + " window="
+ + windowHandle);
+ String[] arguments =
+ {
+ EXEC, "embedWindow", DISPLAY, Long.toString(parentHandle),
+ Long.toString(windowHandle)
+ };
+ long handle = exec_toLong(arguments);
+ return handle;
+ }
+
+ public static void unembedNativeWindow(long parentHandle, long formerParentHandle)
+ {
+ StudioLogger.debug(GtkBridge.class, "unembedNativeWindow " + parentHandle);
+ String[] arguments =
+ {
+ EXEC, "unembedWindow", DISPLAY, Long.toString(parentHandle),
+ Long.toString(formerParentHandle)
+ };
+ exec(arguments);
+ }
+
+ public static Point getWindowSize(long handle)
+ {
+ String[] arguments =
+ {
+ EXEC, "getWindowSize", DISPLAY, Long.toString(handle)
+ };
+ String size = exec(arguments);
+ Point point;
+ try
+ {
+ String[] pointStr = size.split(",");
+ int x = Integer.parseInt(pointStr[0]);
+ int y = Integer.parseInt(pointStr[1]);
+ point = new Point(x, y);
+ }
+ catch (Exception e)
+ {
+ point = new Point(0, 0);
+ }
+ return point;
+ }
+
+ public static void pressKey(long windowHandle, int key)
+ {
+ String[] arguments =
+ {
+ EXEC, "pressKey", DISPLAY, Long.toString(windowHandle), Integer.toString(key)
+ };
+ exec(arguments);
+ }
+
+ public static void releaseKey(long windowHandle, int key)
+ {
+ String[] arguments =
+ {
+ EXEC, "releaseKey", DISPLAY, Long.toString(windowHandle), Integer.toString(key)
+ };
+ exec(arguments);
+ }
+
+ public static void showWindow(long windowHandle)
+ {
+ StudioLogger.debug(GtkBridge.class, "showWindow " + windowHandle);
+ String[] arguments =
+ {
+ EXEC, "showWindow", DISPLAY, Long.toString(windowHandle)
+ };
+ exec(arguments);
+ }
+
+ public static void hideWindow(long windowHandle)
+ {
+ StudioLogger.debug(GtkBridge.class, "hideWindow " + windowHandle);
+ String[] arguments =
+ {
+ EXEC, "hideWindow", DISPLAY, Long.toString(windowHandle)
+ };
+ exec(arguments);
+ }
+
+ public static void restoreWindow(long windowHandle)
+ {
+ StudioLogger.debug(GtkBridge.class, "restoreWindow " + windowHandle);
+ String[] arguments =
+ {
+ EXEC, "restoreWindow", DISPLAY, Long.toString(windowHandle)
+ };
+ exec(arguments);
+ }
+
+}
diff --git a/src/plugins/android.linux.x86_64/.classpath b/src/plugins/android.linux.x86_64/.classpath
new file mode 100644
index 0000000..64c5e31
--- /dev/null
+++ b/src/plugins/android.linux.x86_64/.classpath
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.5"/>
+ <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/src/plugins/android.linux.x86_64/.project b/src/plugins/android.linux.x86_64/.project
new file mode 100644
index 0000000..caaa293
--- /dev/null
+++ b/src/plugins/android.linux.x86_64/.project
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>com.motorola.studio.android.linux.x86_64</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.ManifestBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.SchemaBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.pde.PluginNature</nature>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+</projectDescription>
diff --git a/src/plugins/android.linux.x86_64/META-INF/MANIFEST.MF b/src/plugins/android.linux.x86_64/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..5d4b1ea
--- /dev/null
+++ b/src/plugins/android.linux.x86_64/META-INF/MANIFEST.MF
@@ -0,0 +1,10 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: %Bundle-Name
+Bundle-SymbolicName: com.motorola.studio.android.linux.x86_64;singleton:=true
+Bundle-Version: 2.0.0.qualifier
+Bundle-Vendor: %Bundle-Vendor
+Fragment-Host: com.motorola.studio.android;bundle-version="2.0.0"
+Bundle-RequiredExecutionEnvironment: J2SE-1.5
+Bundle-Localization: fragment
+Eclipse-PlatformFilter: (& (osgi.os=linux) (osgi.arch=x86_64))
diff --git a/src/plugins/android.linux.x86_64/build.properties b/src/plugins/android.linux.x86_64/build.properties
new file mode 100644
index 0000000..2c52d60
--- /dev/null
+++ b/src/plugins/android.linux.x86_64/build.properties
@@ -0,0 +1,8 @@
+source.. = src/
+output.. = bin/
+bin.includes = META-INF/,\
+ .,\
+ libgtk_bridge.so,\
+ gtk_bridge_app,\
+ fragment.properties,\
+ fragment.xml
diff --git a/src/plugins/android.linux.x86_64/fragment.properties b/src/plugins/android.linux.x86_64/fragment.properties
new file mode 100644
index 0000000..967186e
--- /dev/null
+++ b/src/plugins/android.linux.x86_64/fragment.properties
@@ -0,0 +1,3 @@
+Bundle-Name=MOTODEV Studio for Android Linux x86_64 Fragment
+Bundle-Vendor=Motorola Mobility, Inc.
+preferencePageName=Android Emulator \ No newline at end of file
diff --git a/src/plugins/android.linux.x86_64/fragment.xml b/src/plugins/android.linux.x86_64/fragment.xml
new file mode 100644
index 0000000..043c4f2
--- /dev/null
+++ b/src/plugins/android.linux.x86_64/fragment.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?eclipse version="3.4"?>
+<fragment>
+
+ <extension
+ point="org.eclipse.ui.preferencePages">
+ <page
+ category="com.motorola.studio.platform.ui.preference"
+ class="com.motorola.studio.android.preferences.ui.EmulatorPreferencePage"
+ id="com.motorola.studio.android.emulator.preferencepage"
+ name="%preferencePageName">
+ </page>
+ </extension>
+
+</fragment>
diff --git a/src/plugins/android.linux.x86_64/gtk_bridge_app b/src/plugins/android.linux.x86_64/gtk_bridge_app
new file mode 100644
index 0000000..c552798
--- /dev/null
+++ b/src/plugins/android.linux.x86_64/gtk_bridge_app
Binary files differ
diff --git a/src/plugins/android.linux.x86_64/src/com/motorola/studio/android/nativeos/NativeUI.java b/src/plugins/android.linux.x86_64/src/com/motorola/studio/android/nativeos/NativeUI.java
new file mode 100644
index 0000000..1ba9e01
--- /dev/null
+++ b/src/plugins/android.linux.x86_64/src/com/motorola/studio/android/nativeos/NativeUI.java
@@ -0,0 +1,173 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.nativeos;
+
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.internal.gtk.OS;
+import com.motorola.studio.android.nativeos.linux.gtk.GtkBridge;
+
+/***
+ * This class is responsible for provide LINUX X86_64 specific constants values
+ * and implementation of INativeUI interface
+ */
+public class NativeUI implements INativeUI
+{
+ String DEFAULT_COMMANDLINE = "";
+
+ String DEFAULT_USEVNC = "false";
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.nativeos.INativeUI#getDefaultCommandLine()
+ */
+ public String getDefaultCommandLine()
+ {
+ return DEFAULT_COMMANDLINE;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.nativeos.INativeUI#getDefaultUseVnc()
+ */
+ public String getDefaultUseVnc()
+ {
+ return DEFAULT_USEVNC;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.nativeos.INativeUI#getWindowHandle(java.lang.String)
+ */
+ public long getWindowHandle(String windowName)
+ {
+ return GtkBridge.getWindowHandle(windowName);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.nativeos.INativeUI#getWindowProperties(long)
+ */
+ public long getWindowProperties(long windowHandle)
+ {
+ return 0;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.nativeos.INativeUI#setWindowProperties(long, long)
+ */
+ public void setWindowProperties(long windowHandle, long originalProperties)
+ {
+ //Do Nothing.
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.nativeos.INativeUI#embedWindow(long, org.eclipse.swt.widgets.Composite)
+ */
+ public long embedWindow(long windowHandle, Composite composite)
+ {
+ long hnd = 0;
+ hnd = composite.embeddedHandle;
+ return GtkBridge.embedNativeWindow(hnd, windowHandle);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.nativeos.INativeUI#unembedWindow(long, long)
+ */
+ public void unembedWindow(long windowHandle, long originalParent)
+ {
+ GtkBridge.unembedNativeWindow(windowHandle, originalParent);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.nativeos.INativeUI#getWindowSize(long, long)
+ */
+ public Point getWindowSize(long originalWindowHandle, long windowHandle)
+ {
+ return GtkBridge.getWindowSize(originalWindowHandle);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.nativeos.INativeUI#setWindowStyle(long)
+ */
+ public void setWindowStyle(long windowHandle)
+ {
+ //Not needed on Linux
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.nativeos.INativeUI#hideWindow(long)
+ */
+ public void hideWindow(long windowHandle)
+ {
+ GtkBridge.hideWindow(windowHandle);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.nativeos.INativeUI#showWindow(long)
+ */
+ public void showWindow(long windowHandle)
+ {
+ GtkBridge.showWindow(windowHandle);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.nativeos.INativeUI#restoreWindow(long)
+ */
+ public void restoreWindow(long windowHandle)
+ {
+ GtkBridge.restoreWindow(windowHandle);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.nativeos.INativeUI#sendNextLayoutCommand(long, long)
+ */
+ public void sendNextLayoutCommand(long originalParent, long windowHandle)
+ {
+ GtkBridge.pressKey(originalParent, OS.GDK_Control_L);
+ GtkBridge.pressKey(originalParent, OS.GDK_F11);
+ GtkBridge.releaseKey(originalParent, OS.GDK_F11);
+ GtkBridge.releaseKey(originalParent, OS.GDK_Control_L);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.nativeos.INativeUI#isWindowEnabled(long)
+ */
+ public boolean isWindowEnabled(long windowHandle)
+ {
+ // Current not needed on Linux
+ return false;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.nativeos.INativeUI#setWindowFocus(long)
+ */
+ public void setWindowFocus(long windowHandle)
+ {
+ // Current not needed on Linux
+ }
+}
diff --git a/src/plugins/android.linux.x86_64/src/com/motorola/studio/android/nativeos/linux/gtk/GtkBridge.java b/src/plugins/android.linux.x86_64/src/com/motorola/studio/android/nativeos/linux/gtk/GtkBridge.java
new file mode 100644
index 0000000..d3655a8
--- /dev/null
+++ b/src/plugins/android.linux.x86_64/src/com/motorola/studio/android/nativeos/linux/gtk/GtkBridge.java
@@ -0,0 +1,275 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.nativeos.linux.gtk;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.net.URL;
+import java.util.Arrays;
+
+import org.eclipse.core.runtime.FileLocator;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.swt.graphics.Point;
+import org.osgi.framework.Bundle;
+
+import com.motorola.studio.android.AndroidPlugin;
+import com.motorola.studio.android.common.log.StudioLogger;
+
+public class GtkBridge
+{
+ private static final String EXEC = getExecCommad();
+
+ private static final String DISPLAY = getDisplayNum();
+
+ public static String getDisplayNum()
+ {
+ String display = System.getenv("DISPLAY");
+ return display;
+ }
+
+ public static String getExecCommad()
+ {
+ String absoluteFile = "";
+ try
+ {
+ Bundle bundle = AndroidPlugin.getDefault().getBundle();
+ URL locationUrl = FileLocator.find(bundle, new Path("gtk_bridge_app"), null);
+ URL fileUrl = FileLocator.toFileURL(locationUrl);
+ File file = new File(fileUrl.getFile());
+ absoluteFile = file.getAbsolutePath();
+ StudioLogger.info(GtkBridge.class, "Using gtk bridge app at: " + absoluteFile);
+ checkExecutionPermission(absoluteFile);
+ }
+ catch (Exception e)
+ {
+ StudioLogger.error(GtkBridge.class, "Failed to retrieve gtk bridge app", e);
+ }
+ return absoluteFile;
+ }
+
+ private static void checkExecutionPermission(String executable) throws IOException
+ {
+ String[] command = new String[]
+ {
+ "chmod", "+x", executable
+ };
+ Runtime.getRuntime().exec(command);
+ }
+
+ public static synchronized String exec(String[] command)
+ {
+ String lastline = "";
+ Process p = null;
+ BufferedReader reader = null;
+ try
+ {
+ p = Runtime.getRuntime().exec(command);
+ reader = new BufferedReader(new InputStreamReader(p.getInputStream()));
+ String returnedValue = "";
+ do
+ {
+ returnedValue = reader.readLine();
+ if (returnedValue != null)
+ {
+ lastline = returnedValue;
+ }
+ }
+ while (returnedValue != null);
+ int exitCode = p.waitFor();
+ if (exitCode != 0)
+ {
+ StudioLogger.debug(GtkBridge.class, "Command " + Arrays.toString(command)
+ + " finished with error code:" + exitCode);
+ }
+ }
+ catch (Exception e)
+ {
+ StudioLogger.error(GtkBridge.class,
+ "Failed to execute command :" + Arrays.toString(command), e);
+ }
+ finally
+ {
+ if (p != null)
+ {
+ try
+ {
+ p.getInputStream().close();
+ }
+ catch (IOException e)
+ {
+ //Do Nothing.
+ }
+ try
+ {
+ p.getOutputStream().close();
+ }
+ catch (IOException e)
+ {
+ //Do Nothing.
+ }
+ try
+ {
+ p.getErrorStream().close();
+ }
+ catch (IOException e)
+ {
+ //Do Nothing.
+ }
+ }
+ if (reader != null)
+ {
+ try
+ {
+ reader.close();
+ }
+ catch (IOException e)
+ {
+ //Do Nothing.
+ }
+ }
+ }
+
+ return lastline;
+ }
+
+ private static long exec_toLong(String[] command)
+ {
+ long response = 0;
+ try
+ {
+ String returnedValue = exec(command);
+ if (!"".equals(command))
+ {
+ response = Long.parseLong(returnedValue);
+ }
+ }
+ catch (Exception e)
+ {
+ StudioLogger.error(GtkBridge.class, "Failed to execute command", e);
+ }
+ return response;
+ }
+
+ public static long getWindowHandle(String windowName)
+ {
+ StudioLogger.debug(GtkBridge.class, "getWindowHandle " + windowName);
+
+ String[] command =
+ {
+ EXEC, "getWindowHandler", DISPLAY, windowName
+ };
+ long handle = exec_toLong(command);
+ StudioLogger.debug(GtkBridge.class, "getWindowHandle handle=" + handle);
+ return handle;
+ }
+
+ public static long embedNativeWindow(long parentHandle, long windowHandle)
+ {
+ StudioLogger.debug(GtkBridge.class, "embedNativeWindow parent=" + parentHandle + " window="
+ + windowHandle);
+ String[] arguments =
+ {
+ EXEC, "embedWindow", DISPLAY, Long.toString(parentHandle),
+ Long.toString(windowHandle)
+ };
+ long handle = exec_toLong(arguments);
+ return handle;
+ }
+
+ public static void unembedNativeWindow(long parentHandle, long formerParentHandle)
+ {
+ StudioLogger.debug(GtkBridge.class, "unembedNativeWindow " + parentHandle);
+ String[] arguments =
+ {
+ EXEC, "unembedWindow", DISPLAY, Long.toString(parentHandle),
+ Long.toString(formerParentHandle)
+ };
+ exec(arguments);
+ }
+
+ public static Point getWindowSize(long handle)
+ {
+ String[] arguments =
+ {
+ EXEC, "getWindowSize", DISPLAY, Long.toString(handle)
+ };
+ String size = exec(arguments);
+ Point point;
+ try
+ {
+ String[] pointStr = size.split(",");
+ int x = Integer.parseInt(pointStr[0]);
+ int y = Integer.parseInt(pointStr[1]);
+ point = new Point(x, y);
+ }
+ catch (Exception e)
+ {
+ point = new Point(0, 0);
+ }
+ return point;
+ }
+
+ public static void pressKey(long windowHandle, int key)
+ {
+ String[] arguments =
+ {
+ EXEC, "pressKey", DISPLAY, Long.toString(windowHandle), Integer.toString(key)
+ };
+ exec(arguments);
+ }
+
+ public static void releaseKey(long windowHandle, int key)
+ {
+ String[] arguments =
+ {
+ EXEC, "releaseKey", DISPLAY, Long.toString(windowHandle), Integer.toString(key)
+ };
+ exec(arguments);
+ }
+
+ public static void showWindow(long windowHandle)
+ {
+ StudioLogger.debug(GtkBridge.class, "showWindow " + windowHandle);
+ String[] arguments =
+ {
+ EXEC, "showWindow", DISPLAY, Long.toString(windowHandle)
+ };
+ exec(arguments);
+ }
+
+ public static void hideWindow(long windowHandle)
+ {
+ StudioLogger.debug(GtkBridge.class, "hideWindow " + windowHandle);
+ String[] arguments =
+ {
+ EXEC, "hideWindow", DISPLAY, Long.toString(windowHandle)
+ };
+ exec(arguments);
+ }
+
+ public static void restoreWindow(long windowHandle)
+ {
+ StudioLogger.debug(GtkBridge.class, "restoreWindow " + windowHandle);
+ String[] arguments =
+ {
+ EXEC, "restoreWindow", DISPLAY, Long.toString(windowHandle)
+ };
+ exec(arguments);
+ }
+
+}
diff --git a/src/plugins/android.macosx/.classpath b/src/plugins/android.macosx/.classpath
new file mode 100644
index 0000000..64c5e31
--- /dev/null
+++ b/src/plugins/android.macosx/.classpath
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.5"/>
+ <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/src/plugins/android.macosx/.project b/src/plugins/android.macosx/.project
new file mode 100644
index 0000000..5affbee
--- /dev/null
+++ b/src/plugins/android.macosx/.project
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>com.motorola.studio.android.macosx</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.ManifestBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.SchemaBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.pde.PluginNature</nature>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+</projectDescription>
diff --git a/src/plugins/android.macosx/META-INF/MANIFEST.MF b/src/plugins/android.macosx/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..5fa6e95
--- /dev/null
+++ b/src/plugins/android.macosx/META-INF/MANIFEST.MF
@@ -0,0 +1,10 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: %Bundle-Name
+Bundle-SymbolicName: com.motorola.studio.android.macosx;singleton:=true
+Bundle-Version: 2.0.0.qualifier
+Bundle-Vendor: %Bundle-Vendor
+Fragment-Host: com.motorola.studio.android;bundle-version="2.0.0"
+Bundle-RequiredExecutionEnvironment: J2SE-1.5
+Bundle-Localization: fragment
+Eclipse-PlatformFilter: (& (osgi.os=macosx))
diff --git a/src/plugins/android.macosx/build.properties b/src/plugins/android.macosx/build.properties
new file mode 100644
index 0000000..a835c40
--- /dev/null
+++ b/src/plugins/android.macosx/build.properties
@@ -0,0 +1,5 @@
+source.. = src/
+output.. = bin/
+bin.includes = META-INF/,\
+ .,\
+ fragment.properties
diff --git a/src/plugins/android.macosx/fragment.properties b/src/plugins/android.macosx/fragment.properties
new file mode 100644
index 0000000..39bb98b
--- /dev/null
+++ b/src/plugins/android.macosx/fragment.properties
@@ -0,0 +1,2 @@
+Bundle-Name=MOTODEV Studio for Android MacOSX Fragment
+Bundle-Vendor=Motorola Mobility, Inc. \ No newline at end of file
diff --git a/src/plugins/android.macosx/src/com/motorola/studio/android/nativeos/NativeUI.java b/src/plugins/android.macosx/src/com/motorola/studio/android/nativeos/NativeUI.java
new file mode 100644
index 0000000..295bbd4
--- /dev/null
+++ b/src/plugins/android.macosx/src/com/motorola/studio/android/nativeos/NativeUI.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.nativeos;
+
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.widgets.Composite;
+
+/***
+ * This class is responsible for provide MAC OS specific constants values since the option of
+ * use native emulator window to show emulator within the MOTODEV Studio is not available on MAC OS.
+ * This is why the methods are not filled.
+ */
+public class NativeUI implements INativeUI
+{
+ String DEFAULT_COMMANDLINE = "-no-window";
+
+ String DEFAULT_USEVNC = "true";
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.nativeos.INativeUI#getDefaultCommandLine()
+ */
+ public String getDefaultCommandLine()
+ {
+ return DEFAULT_COMMANDLINE;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.nativeos.INativeUI#getDefaultUseVnc()
+ */
+ public String getDefaultUseVnc()
+ {
+ return DEFAULT_USEVNC;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.nativeos.INativeUI#getWindowHandle(java.lang.String)
+ */
+ public long getWindowHandle(String windowName)
+ {
+ //Not needed
+ return 0;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.nativeos.INativeUI#getWindowProperties(long)
+ */
+ public long getWindowProperties(long windowHandle)
+ {
+ //Not needed
+ return 0;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.nativeos.INativeUI#getWindowSize(long, long)
+ */
+ public Point getWindowSize(long originalHandle, long windowHandle)
+ {
+ //Not needed
+ return null;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.nativeos.INativeUI#hideWindow(long)
+ */
+ public void hideWindow(long windowHandle)
+ {
+ //Not needed
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.nativeos.INativeUI#isWindowEnabled(long)
+ */
+ public boolean isWindowEnabled(long windowHandle)
+ {
+ //Not needed
+ return false;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.nativeos.INativeUI#restoreWindow(long)
+ */
+ public void restoreWindow(long windowHandle)
+ {
+ //Not needed
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.nativeos.INativeUI#sendNextLayoutCommand(long, long)
+ */
+ public void sendNextLayoutCommand(long originalHandle, long windowHandle)
+ {
+ //Not needed
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.nativeos.INativeUI#setWindowFocus(long)
+ */
+ public void setWindowFocus(long windowHandle)
+ {
+ //Not needed
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.nativeos.INativeUI#embedWindow(long, org.eclipse.swt.widgets.Composite)
+ */
+ public long embedWindow(long windowHandle, Composite composite)
+ {
+ //Not needed
+ return 0;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.nativeos.INativeUI#unembedWindow(long, long)
+ */
+ public void unembedWindow(long windowHandle, long originalParent)
+ {
+ //Not needed
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.nativeos.INativeUI#setWindowProperties(long, long)
+ */
+ public void setWindowProperties(long windowHandle, long originalProperties)
+ {
+ //Not needed
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.nativeos.INativeUI#setWindowStyle(long)
+ */
+ public void setWindowStyle(long windowHandle)
+ {
+ //Not needed
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.nativeos.INativeUI#showWindow(long)
+ */
+ public void showWindow(long windowHandle)
+ {
+ //Not needed
+ }
+
+}
diff --git a/src/plugins/android.win32.x86/.classpath b/src/plugins/android.win32.x86/.classpath
new file mode 100644
index 0000000..64c5e31
--- /dev/null
+++ b/src/plugins/android.win32.x86/.classpath
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.5"/>
+ <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/src/plugins/android.win32.x86/.project b/src/plugins/android.win32.x86/.project
new file mode 100644
index 0000000..95816ef
--- /dev/null
+++ b/src/plugins/android.win32.x86/.project
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>com.motorola.studio.android.win32.x86</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.ManifestBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.SchemaBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.pde.PluginNature</nature>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+</projectDescription>
diff --git a/src/plugins/android.win32.x86/META-INF/MANIFEST.MF b/src/plugins/android.win32.x86/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..6f67c4a
--- /dev/null
+++ b/src/plugins/android.win32.x86/META-INF/MANIFEST.MF
@@ -0,0 +1,11 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: %Bundle-Name
+Bundle-SymbolicName: com.motorola.studio.android.win32.x86;singleton:=true
+Bundle-Version: 2.1.0.qualifier
+Bundle-Vendor: %Bundle-Vendor
+Fragment-Host: com.motorola.studio.android;bundle-version="2.0.0"
+Bundle-RequiredExecutionEnvironment: J2SE-1.5
+Export-Package: com.motorola.studio.android.nativeos
+Bundle-Localization: fragment
+Eclipse-PlatformFilter: (& (osgi.os=win32) (osgi.arch=x86))
diff --git a/src/plugins/android.win32.x86/build.properties b/src/plugins/android.win32.x86/build.properties
new file mode 100644
index 0000000..9223f2c
--- /dev/null
+++ b/src/plugins/android.win32.x86/build.properties
@@ -0,0 +1,6 @@
+source.. = src/
+output.. = bin/
+bin.includes = META-INF/,\
+ .,\
+ fragment.properties,\
+ fragment.xml
diff --git a/src/plugins/android.win32.x86/fragment.properties b/src/plugins/android.win32.x86/fragment.properties
new file mode 100644
index 0000000..52c2158
--- /dev/null
+++ b/src/plugins/android.win32.x86/fragment.properties
@@ -0,0 +1,3 @@
+Bundle-Name=MOTODEV Studio for Android Windows x86 Fragment
+Bundle-Vendor=Motorola Mobility, Inc.
+preferencePageName=Android Emulator \ No newline at end of file
diff --git a/src/plugins/android.win32.x86/fragment.xml b/src/plugins/android.win32.x86/fragment.xml
new file mode 100644
index 0000000..043c4f2
--- /dev/null
+++ b/src/plugins/android.win32.x86/fragment.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?eclipse version="3.4"?>
+<fragment>
+
+ <extension
+ point="org.eclipse.ui.preferencePages">
+ <page
+ category="com.motorola.studio.platform.ui.preference"
+ class="com.motorola.studio.android.preferences.ui.EmulatorPreferencePage"
+ id="com.motorola.studio.android.emulator.preferencepage"
+ name="%preferencePageName">
+ </page>
+ </extension>
+
+</fragment>
diff --git a/src/plugins/android.win32.x86/src/com/motorola/studio/android/nativeos/NativeUI.java b/src/plugins/android.win32.x86/src/com/motorola/studio/android/nativeos/NativeUI.java
new file mode 100644
index 0000000..c70f3a5
--- /dev/null
+++ b/src/plugins/android.win32.x86/src/com/motorola/studio/android/nativeos/NativeUI.java
@@ -0,0 +1,200 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.nativeos;
+
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.internal.win32.OS;
+import org.eclipse.swt.internal.win32.RECT;
+import org.eclipse.swt.internal.win32.TCHAR;
+import org.eclipse.swt.widgets.Composite;
+
+import com.motorola.studio.android.common.log.StudioLogger;
+
+/***
+ * This class is responsible for provide WIN32 X86 specific constants values
+ * and implementation of INativeUI interface
+ */
+@SuppressWarnings("restriction")
+public class NativeUI implements INativeUI
+{
+ private static final int SWP_SHOWWINDOW = 0x0040;
+
+ String DEFAULT_COMMANDLINE = "";
+
+ String DEFAULT_USEVNC = "false";
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.nativeos.INativeUI#getDefaultCommandLine()
+ */
+ public String getDefaultCommandLine()
+ {
+ return DEFAULT_COMMANDLINE;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.nativeos.INativeUI#getDefaultUseVnc()
+ */
+ public String getDefaultUseVnc()
+ {
+ return DEFAULT_USEVNC;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.nativeos.INativeUI#getWindowHandle(java.lang.String)
+ */
+ public long getWindowHandle(String windowName)
+ {
+ StudioLogger.debug(this, "Get native window handler for: " + windowName);
+ int windowHandle = 0;
+ try
+ {
+ TCHAR className = null;
+ TCHAR tChrTitle = new TCHAR(0, windowName, true);
+ windowHandle = OS.FindWindow(className, tChrTitle);
+ }
+ catch (Throwable t)
+ {
+ StudioLogger.error(this.getClass(), "Failed to retrieve window handler for window "
+ + windowName, t);
+ }
+
+ return windowHandle;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.nativeos.INativeUI#getWindowProperties(long)
+ */
+ public long getWindowProperties(long windowHandle)
+ {
+ long windowLong = OS.GetWindowLong((int) windowHandle, OS.GWL_STYLE);
+ return windowLong;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.nativeos.INativeUI#setWindowProperties(long, long)
+ */
+ public void setWindowProperties(long windowHandle, long originalProperties)
+ {
+ OS.SetWindowLong((int) windowHandle, OS.GWL_STYLE, (int) originalProperties);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.nativeos.INativeUI#embedWindow(long, org.eclipse.swt.widgets.Composite)
+ */
+ public long embedWindow(long windowHandle, Composite composite)
+ {
+ // Set the position to fix an odd behavior in the Windows Classic Theme - the window goes off-screen and Emulator View stops working
+ OS.SetWindowPos((int) windowHandle, OS.HWND_TOP, 0, 0, 0, 0, SWP_SHOWWINDOW);
+
+ long originalParent = OS.SetParent((int) windowHandle, composite.handle);
+ return originalParent;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.nativeos.INativeUI#unembedWindow(long, long)
+ */
+ public void unembedWindow(long windowHandle, long originalParent)
+ {
+ OS.SetParent((int) windowHandle, (int) originalParent);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.nativeos.INativeUI#getWindowSize(long, long)
+ */
+ public Point getWindowSize(long originalParentHandle, long windowHandle)
+ {
+ RECT rect = new RECT();
+ OS.GetClientRect((int) windowHandle, rect);
+ return new Point(rect.right, rect.bottom);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.nativeos.INativeUI#setWindowStyle(long)
+ */
+ public void setWindowStyle(long windowHandle)
+ {
+ OS.SetWindowLong((int) windowHandle, OS.GWL_STYLE, OS.WS_VISIBLE | OS.WS_CLIPCHILDREN
+ | OS.WS_CLIPSIBLINGS);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.nativeos.INativeUI#hideWindow(long)
+ */
+ public void hideWindow(long windowHandle)
+ {
+ OS.ShowWindow((int) windowHandle, OS.SW_HIDE);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.nativeos.INativeUI#showWindow(long)
+ */
+ public void showWindow(long windowHandle)
+ {
+ OS.ShowWindow((int) windowHandle, OS.SW_SHOW);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.nativeos.INativeUI#restoreWindow(long)
+ */
+ public void restoreWindow(long windowHandle)
+ {
+ OS.ShowWindow((int) windowHandle, OS.SW_SHOWMINIMIZED);
+ OS.ShowWindow((int) windowHandle, OS.SW_RESTORE);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.nativeos.INativeUI#sendNextLayoutCommand(long, long)
+ */
+ public void sendNextLayoutCommand(long originalParent, long windowHandle)
+ {
+ OS.SendMessage((int) windowHandle, OS.WM_KEYDOWN, OS.VK_CONTROL, 0);
+ OS.SendMessage((int) windowHandle, OS.WM_KEYDOWN, OS.VK_F12, 0);
+ OS.SendMessage((int) windowHandle, OS.WM_KEYUP, OS.VK_CONTROL, 0);
+ OS.SendMessage((int) windowHandle, OS.WM_KEYUP, OS.VK_F12, 0);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.nativeos.INativeUI#isWindowEnabled(long)
+ */
+ public boolean isWindowEnabled(long windowHandle)
+ {
+ long getFocus = OS.GetForegroundWindow();
+ return windowHandle == getFocus;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.nativeos.INativeUI#setWindowFocus(long)
+ */
+ public void setWindowFocus(long windowHandle)
+ {
+ OS.SetForegroundWindow((int) windowHandle);
+ }
+}
diff --git a/src/plugins/android.win32.x86_64/.classpath b/src/plugins/android.win32.x86_64/.classpath
new file mode 100644
index 0000000..64c5e31
--- /dev/null
+++ b/src/plugins/android.win32.x86_64/.classpath
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.5"/>
+ <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/src/plugins/android.win32.x86_64/.project b/src/plugins/android.win32.x86_64/.project
new file mode 100644
index 0000000..c8f31b1
--- /dev/null
+++ b/src/plugins/android.win32.x86_64/.project
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>com.motorola.studio.android.win32.x86_64</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.ManifestBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.SchemaBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.pde.PluginNature</nature>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+</projectDescription>
diff --git a/src/plugins/android.win32.x86_64/META-INF/MANIFEST.MF b/src/plugins/android.win32.x86_64/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..86685b4
--- /dev/null
+++ b/src/plugins/android.win32.x86_64/META-INF/MANIFEST.MF
@@ -0,0 +1,10 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: %Bundle-Name
+Bundle-SymbolicName: com.motorola.studio.android.win32.x86_64;singleton:=true
+Bundle-Version: 5.0.0.qualifier
+Bundle-Vendor: %Bundle-Vendor
+Fragment-Host: com.motorola.studio.android;bundle-version="2.0.0"
+Bundle-RequiredExecutionEnvironment: JavaSE-1.6
+Bundle-Localization: fragment
+Eclipse-PlatformFilter: (& (osgi.os=win32) (osgi.arch=x86_64))
diff --git a/src/plugins/android.win32.x86_64/build.properties b/src/plugins/android.win32.x86_64/build.properties
new file mode 100644
index 0000000..9223f2c
--- /dev/null
+++ b/src/plugins/android.win32.x86_64/build.properties
@@ -0,0 +1,6 @@
+source.. = src/
+output.. = bin/
+bin.includes = META-INF/,\
+ .,\
+ fragment.properties,\
+ fragment.xml
diff --git a/src/plugins/android.win32.x86_64/fragment.properties b/src/plugins/android.win32.x86_64/fragment.properties
new file mode 100644
index 0000000..583492f
--- /dev/null
+++ b/src/plugins/android.win32.x86_64/fragment.properties
@@ -0,0 +1,3 @@
+Bundle-Name=MOTODEV Studio for Android Windows x86_64 Fragment
+Bundle-Vendor=Motorola Mobility, Inc.
+preferencePageName=Android Emulator \ No newline at end of file
diff --git a/src/plugins/android.win32.x86_64/fragment.xml b/src/plugins/android.win32.x86_64/fragment.xml
new file mode 100644
index 0000000..043c4f2
--- /dev/null
+++ b/src/plugins/android.win32.x86_64/fragment.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?eclipse version="3.4"?>
+<fragment>
+
+ <extension
+ point="org.eclipse.ui.preferencePages">
+ <page
+ category="com.motorola.studio.platform.ui.preference"
+ class="com.motorola.studio.android.preferences.ui.EmulatorPreferencePage"
+ id="com.motorola.studio.android.emulator.preferencepage"
+ name="%preferencePageName">
+ </page>
+ </extension>
+
+</fragment>
diff --git a/src/plugins/android.win32.x86_64/src/com/motorola/studio/android/nativeos/NativeUI.java b/src/plugins/android.win32.x86_64/src/com/motorola/studio/android/nativeos/NativeUI.java
new file mode 100644
index 0000000..36d1fc5
--- /dev/null
+++ b/src/plugins/android.win32.x86_64/src/com/motorola/studio/android/nativeos/NativeUI.java
@@ -0,0 +1,200 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.nativeos;
+
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.internal.win32.OS;
+import org.eclipse.swt.internal.win32.RECT;
+import org.eclipse.swt.internal.win32.TCHAR;
+import org.eclipse.swt.widgets.Composite;
+
+import com.motorola.studio.android.common.log.StudioLogger;
+
+/***
+ * This class is responsible for provide WIN32 X86_64 specific constants values
+ * and implementation of INativeUI interface
+ */
+@SuppressWarnings("restriction")
+public class NativeUI implements INativeUI
+{
+ private static final int SWP_SHOWWINDOW = 0x0040;
+
+ String DEFAULT_COMMANDLINE = "";
+
+ String DEFAULT_USEVNC = "false";
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.nativeos.INativeUI#getDefaultCommandLine()
+ */
+ public String getDefaultCommandLine()
+ {
+ return DEFAULT_COMMANDLINE;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.nativeos.INativeUI#getDefaultUseVnc()
+ */
+ public String getDefaultUseVnc()
+ {
+ return DEFAULT_USEVNC;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.nativeos.INativeUI#getWindowHandle(java.lang.String)
+ */
+ public long getWindowHandle(String windowName)
+ {
+ StudioLogger.debug(this, "Get native window handler for: " + windowName);
+ long windowHandle = 0;
+ try
+ {
+ TCHAR className = null;
+ TCHAR tChrTitle = new TCHAR(0, windowName, true);
+ windowHandle = OS.FindWindow(className, tChrTitle);
+ }
+ catch (Throwable t)
+ {
+ StudioLogger.error(this.getClass(), "Failed to retrieve window handler for window "
+ + windowName, t);
+ }
+
+ return windowHandle;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.nativeos.INativeUI#getWindowProperties(long)
+ */
+ public long getWindowProperties(long windowHandle)
+ {
+ long windowLong = OS.GetWindowLong(windowHandle, OS.GWL_STYLE);
+ return windowLong;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.nativeos.INativeUI#setWindowProperties(long, long)
+ */
+ public void setWindowProperties(long windowHandle, long originalProperties)
+ {
+ OS.SetWindowLong(windowHandle, OS.GWL_STYLE, (int) originalProperties);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.nativeos.INativeUI#embedWindow(long, org.eclipse.swt.widgets.Composite)
+ */
+ public long embedWindow(long windowHandle, Composite composite)
+ {
+ // Set the position to fix an odd behavior in the Windows Classic Theme - the window goes off-screen and Emulator View stops working
+ OS.SetWindowPos(windowHandle, OS.HWND_TOP, 0, 0, 0, 0, SWP_SHOWWINDOW);
+
+ long originalParent = OS.SetParent(windowHandle, composite.handle);
+ return originalParent;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.nativeos.INativeUI#unembedWindow(long, long)
+ */
+ public void unembedWindow(long windowHandle, long originalParent)
+ {
+ OS.SetParent(windowHandle, originalParent);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.nativeos.INativeUI#getWindowSize(long, long)
+ */
+ public Point getWindowSize(long originalParentHandle, long windowHandle)
+ {
+ RECT rect = new RECT();
+ OS.GetClientRect(windowHandle, rect);
+ return new Point(rect.right, rect.bottom);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.nativeos.INativeUI#setWindowStyle(long)
+ */
+ public void setWindowStyle(long windowHandle)
+ {
+ OS.SetWindowLong(windowHandle, OS.GWL_STYLE, OS.WS_VISIBLE | OS.WS_CLIPCHILDREN
+ | OS.WS_CLIPSIBLINGS);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.nativeos.INativeUI#hideWindow(long)
+ */
+ public void hideWindow(long windowHandle)
+ {
+ OS.ShowWindow(windowHandle, OS.SW_HIDE);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.nativeos.INativeUI#showWindow(long)
+ */
+ public void showWindow(long windowHandle)
+ {
+ OS.ShowWindow(windowHandle, OS.SW_SHOW);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.nativeos.INativeUI#restoreWindow(long)
+ */
+ public void restoreWindow(long windowHandle)
+ {
+ OS.ShowWindow(windowHandle, OS.SW_SHOWMINIMIZED);
+ OS.ShowWindow(windowHandle, OS.SW_RESTORE);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.nativeos.INativeUI#sendNextLayoutCommand(long, long)
+ */
+ public void sendNextLayoutCommand(long originalParent, long windowHandle)
+ {
+ OS.SendMessage(windowHandle, OS.WM_KEYDOWN, OS.VK_CONTROL, 0);
+ OS.SendMessage(windowHandle, OS.WM_KEYDOWN, OS.VK_F12, 0);
+ OS.SendMessage(windowHandle, OS.WM_KEYUP, OS.VK_CONTROL, 0);
+ OS.SendMessage(windowHandle, OS.WM_KEYUP, OS.VK_F12, 0);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.nativeos.INativeUI#isWindowEnabled(long)
+ */
+ public boolean isWindowEnabled(long windowHandle)
+ {
+ long getFocus = OS.GetForegroundWindow();
+ return windowHandle == getFocus;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.nativeos.INativeUI#setWindowFocus(long)
+ */
+ public void setWindowFocus(long windowHandle)
+ {
+ OS.SetForegroundWindow(windowHandle);
+ }
+}
diff --git a/src/plugins/android/.classpath b/src/plugins/android/.classpath
new file mode 100644
index 0000000..4db0e26
--- /dev/null
+++ b/src/plugins/android/.classpath
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>
+ <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry exported="true" kind="lib" path="commons-net-1.4.1.jar"/>
+ <classpathentry exported="true" kind="lib" path="jakarta-oro-2.0.8.jar"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/src/plugins/android/.project b/src/plugins/android/.project
new file mode 100644
index 0000000..7dc0721
--- /dev/null
+++ b/src/plugins/android/.project
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>com.motorola.studio.android</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.ManifestBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.SchemaBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.api.tools.apiAnalysisBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.pde.PluginNature</nature>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ <nature>org.eclipse.pde.api.tools.apiAnalysisNature</nature>
+ </natures>
+</projectDescription>
diff --git a/src/plugins/android/META-INF/MANIFEST.MF b/src/plugins/android/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..0c67e74
--- /dev/null
+++ b/src/plugins/android/META-INF/MANIFEST.MF
@@ -0,0 +1,45 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: %Plugin_Name
+Bundle-SymbolicName: com.motorola.studio.android;singleton:=true
+Bundle-Version: 5.0.0.qualifier
+Bundle-Activator: com.motorola.studio.android.AndroidPlugin
+Bundle-Vendor: %Plugin_Vendor
+Require-Bundle: org.eclipse.ui,
+ org.eclipse.ui.ide,
+ com.motorola.studio.android.common,
+ com.motorolamobility.studio.android.logger.collector,
+ org.eclipse.core.runtime,
+ org.eclipse.core.filesystem,
+ com.android.ide.eclipse.adt,
+ com.android.ide.eclipse.ddms,
+ org.eclipse.jdt.core,
+ org.eclipse.jface.text,
+ org.apache.xerces,
+ org.eclipse.ui.console,
+ org.eclipse.sequoyah.localization.tools,
+ org.eclipse.sequoyah.device.framework,
+ org.eclipse.sequoyah.device.common.utilities,
+ org.eclipse.debug.ui,
+ org.eclipse.core.expressions,
+ com.android.ide.eclipse.base,
+ org.eclipse.jdt.ui
+Bundle-RequiredExecutionEnvironment: JavaSE-1.6
+Bundle-Localization: plugin
+Export-Package: com.motorola.studio.android,
+ com.motorola.studio.android.adt,
+ com.motorola.studio.android.devices,
+ com.motorola.studio.android.i18n,
+ com.motorola.studio.android.model,
+ com.motorola.studio.android.nativeos,
+ com.motorola.studio.android.sdkmanager,
+ com.motorola.studio.android.utilities,
+ com.motorola.studio.android.wizards.elements,
+ com.motorola.studio.android.wizards.installapp,
+ com.motorola.studio.android.wizards.monkey
+Bundle-ActivationPolicy: lazy
+Import-Package: org.eclipse.equinox.security.storage,
+ org.eclipse.ui.console
+Bundle-ClassPath: .,
+ commons-net-1.4.1.jar,
+ jakarta-oro-2.0.8.jar
diff --git a/src/plugins/android/build.properties b/src/plugins/android/build.properties
new file mode 100644
index 0000000..386907f
--- /dev/null
+++ b/src/plugins/android/build.properties
@@ -0,0 +1,15 @@
+source.. = src/
+output.. = bin/
+
+bin.includes = META-INF/,\
+ .,\
+ plugin.properties,\
+ plugin.xml,\
+ icons/,\
+ resources/,\
+ templates/,\
+ files/,\
+ commons-net-1.4.1.jar,\
+ jakarta-oro-2.0.8.jar
+jars.compile.order = .
+
diff --git a/src/plugins/android/commons-net-1.4.1.jar b/src/plugins/android/commons-net-1.4.1.jar
new file mode 100644
index 0000000..9666a92
--- /dev/null
+++ b/src/plugins/android/commons-net-1.4.1.jar
Binary files differ
diff --git a/src/plugins/android/files/proguard.cfg b/src/plugins/android/files/proguard.cfg
new file mode 100644
index 0000000..b1cdf17
--- /dev/null
+++ b/src/plugins/android/files/proguard.cfg
@@ -0,0 +1,40 @@
+-optimizationpasses 5
+-dontusemixedcaseclassnames
+-dontskipnonpubliclibraryclasses
+-dontpreverify
+-verbose
+-optimizations !code/simplification/arithmetic,!field/*,!class/merging/*
+
+-keep public class * extends android.app.Activity
+-keep public class * extends android.app.Application
+-keep public class * extends android.app.Service
+-keep public class * extends android.content.BroadcastReceiver
+-keep public class * extends android.content.ContentProvider
+-keep public class * extends android.app.backup.BackupAgentHelper
+-keep public class * extends android.preference.Preference
+-keep public class com.android.vending.licensing.ILicensingService
+
+-keepclasseswithmembernames class * {
+ native <methods>;
+}
+
+-keepclasseswithmembers class * {
+ public <init>(android.content.Context, android.util.AttributeSet);
+}
+
+-keepclasseswithmembers class * {
+ public <init>(android.content.Context, android.util.AttributeSet, int);
+}
+
+-keepclassmembers class * extends android.app.Activity {
+ public void *(android.view.View);
+}
+
+-keepclassmembers enum * {
+ public static **[] values();
+ public static ** valueOf(java.lang.String);
+}
+
+-keep class * implements android.os.Parcelable {
+ public static final android.os.Parcelable$Creator *;
+}
diff --git a/src/plugins/android/icons/monkey/monkey_16.png b/src/plugins/android/icons/monkey/monkey_16.png
new file mode 100644
index 0000000..9afbe36
--- /dev/null
+++ b/src/plugins/android/icons/monkey/monkey_16.png
Binary files differ
diff --git a/src/plugins/android/icons/monkey/motodevapp.gif b/src/plugins/android/icons/monkey/motodevapp.gif
new file mode 100644
index 0000000..add6a8b
--- /dev/null
+++ b/src/plugins/android/icons/monkey/motodevapp.gif
Binary files differ
diff --git a/src/plugins/android/icons/obj16/Android_Market.gif b/src/plugins/android/icons/obj16/Android_Market.gif
new file mode 100644
index 0000000..b269615
--- /dev/null
+++ b/src/plugins/android/icons/obj16/Android_Market.gif
Binary files differ
diff --git a/src/plugins/android/icons/obj16/add_libraries_window_icon.png b/src/plugins/android/icons/obj16/add_libraries_window_icon.png
new file mode 100644
index 0000000..36da816
--- /dev/null
+++ b/src/plugins/android/icons/obj16/add_libraries_window_icon.png
Binary files differ
diff --git a/src/plugins/android/icons/obj16/android_discussion_16x16.png b/src/plugins/android/icons/obj16/android_discussion_16x16.png
new file mode 100644
index 0000000..cc46fb3
--- /dev/null
+++ b/src/plugins/android/icons/obj16/android_discussion_16x16.png
Binary files differ
diff --git a/src/plugins/android/icons/obj16/deploy.png b/src/plugins/android/icons/obj16/deploy.png
new file mode 100644
index 0000000..c2d878d
--- /dev/null
+++ b/src/plugins/android/icons/obj16/deploy.png
Binary files differ
diff --git a/src/plugins/android/icons/obj16/fill_code_from_layout_disabled_16x16.png b/src/plugins/android/icons/obj16/fill_code_from_layout_disabled_16x16.png
new file mode 100644
index 0000000..e52d9de
--- /dev/null
+++ b/src/plugins/android/icons/obj16/fill_code_from_layout_disabled_16x16.png
Binary files differ
diff --git a/src/plugins/android/icons/obj16/handset_info.png b/src/plugins/android/icons/obj16/handset_info.png
new file mode 100644
index 0000000..b96498f
--- /dev/null
+++ b/src/plugins/android/icons/obj16/handset_info.png
Binary files differ
diff --git a/src/plugins/android/icons/obj16/newaprj_wiz.gif b/src/plugins/android/icons/obj16/newaprj_wiz.gif
new file mode 100644
index 0000000..294470c
--- /dev/null
+++ b/src/plugins/android/icons/obj16/newaprj_wiz.gif
Binary files differ
diff --git a/src/plugins/android/icons/obj16/obfuscate.png b/src/plugins/android/icons/obj16/obfuscate.png
new file mode 100644
index 0000000..ee0a6e7
--- /dev/null
+++ b/src/plugins/android/icons/obj16/obfuscate.png
Binary files differ
diff --git a/src/plugins/android/icons/obj16/plate16.png b/src/plugins/android/icons/obj16/plate16.png
new file mode 100644
index 0000000..8336aef
--- /dev/null
+++ b/src/plugins/android/icons/obj16/plate16.png
Binary files differ
diff --git a/src/plugins/android/icons/obj16/skewed_box_16x16.gif b/src/plugins/android/icons/obj16/skewed_box_16x16.gif
new file mode 100644
index 0000000..755dbb1
--- /dev/null
+++ b/src/plugins/android/icons/obj16/skewed_box_16x16.gif
Binary files differ
diff --git a/src/plugins/android/icons/obj16/studio_discussion_16x16.png b/src/plugins/android/icons/obj16/studio_discussion_16x16.png
new file mode 100644
index 0000000..4ad83eb
--- /dev/null
+++ b/src/plugins/android/icons/obj16/studio_discussion_16x16.png
Binary files differ
diff --git a/src/plugins/android/icons/obj16/webresources.gif b/src/plugins/android/icons/obj16/webresources.gif
new file mode 100644
index 0000000..add6a8b
--- /dev/null
+++ b/src/plugins/android/icons/obj16/webresources.gif
Binary files differ
diff --git a/src/plugins/android/icons/obj16/widget_provider_prj_wiz_toolbar.png b/src/plugins/android/icons/obj16/widget_provider_prj_wiz_toolbar.png
new file mode 100644
index 0000000..159cc72
--- /dev/null
+++ b/src/plugins/android/icons/obj16/widget_provider_prj_wiz_toolbar.png
Binary files differ
diff --git a/src/plugins/android/icons/views/building_block_explorer_plate.gif b/src/plugins/android/icons/views/building_block_explorer_plate.gif
new file mode 100644
index 0000000..e4e6c0a
--- /dev/null
+++ b/src/plugins/android/icons/views/building_block_explorer_plate.gif
Binary files differ
diff --git a/src/plugins/android/icons/wizban/add_libraries_ban.png b/src/plugins/android/icons/wizban/add_libraries_ban.png
new file mode 100644
index 0000000..d9553d4
--- /dev/null
+++ b/src/plugins/android/icons/wizban/add_libraries_ban.png
Binary files differ
diff --git a/src/plugins/android/icons/wizban/deploy_wizard.png b/src/plugins/android/icons/wizban/deploy_wizard.png
new file mode 100644
index 0000000..35877fb
--- /dev/null
+++ b/src/plugins/android/icons/wizban/deploy_wizard.png
Binary files differ
diff --git a/src/plugins/android/icons/wizban/dump_hprof_wizard.png b/src/plugins/android/icons/wizban/dump_hprof_wizard.png
new file mode 100644
index 0000000..139d6be
--- /dev/null
+++ b/src/plugins/android/icons/wizban/dump_hprof_wizard.png
Binary files differ
diff --git a/src/plugins/android/icons/wizban/fill_activity_ban.png b/src/plugins/android/icons/wizban/fill_activity_ban.png
new file mode 100644
index 0000000..f0f80c2
--- /dev/null
+++ b/src/plugins/android/icons/wizban/fill_activity_ban.png
Binary files differ
diff --git a/src/plugins/android/icons/wizban/monkey_wizard.png b/src/plugins/android/icons/wizban/monkey_wizard.png
new file mode 100644
index 0000000..7965073
--- /dev/null
+++ b/src/plugins/android/icons/wizban/monkey_wizard.png
Binary files differ
diff --git a/src/plugins/android/icons/wizban/newprjwiz.png b/src/plugins/android/icons/wizban/newprjwiz.png
new file mode 100644
index 0000000..81c1bb1
--- /dev/null
+++ b/src/plugins/android/icons/wizban/newprjwiz.png
Binary files differ
diff --git a/src/plugins/android/icons/wizban/obfuscate.gif b/src/plugins/android/icons/wizban/obfuscate.gif
new file mode 100644
index 0000000..a274919
--- /dev/null
+++ b/src/plugins/android/icons/wizban/obfuscate.gif
Binary files differ
diff --git a/src/plugins/android/icons/wizban/undeploy_wizard.png b/src/plugins/android/icons/wizban/undeploy_wizard.png
new file mode 100644
index 0000000..daa00e0
--- /dev/null
+++ b/src/plugins/android/icons/wizban/undeploy_wizard.png
Binary files differ
diff --git a/src/plugins/android/icons/wizban/widget_provider_prj_wiz.png b/src/plugins/android/icons/wizban/widget_provider_prj_wiz.png
new file mode 100644
index 0000000..b4d8a50
--- /dev/null
+++ b/src/plugins/android/icons/wizban/widget_provider_prj_wiz.png
Binary files differ
diff --git a/src/plugins/android/jakarta-oro-2.0.8.jar b/src/plugins/android/jakarta-oro-2.0.8.jar
new file mode 100644
index 0000000..23488d2
--- /dev/null
+++ b/src/plugins/android/jakarta-oro-2.0.8.jar
Binary files differ
diff --git a/src/plugins/android/plugin.properties b/src/plugins/android/plugin.properties
new file mode 100644
index 0000000..5277caf
--- /dev/null
+++ b/src/plugins/android/plugin.properties
@@ -0,0 +1,131 @@
+#
+# Copyright (C) 2012 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+Plugin_Vendor = Motorola Mobility, Inc.
+Plugin_Name = MOTODEV Studio for Android
+motodev.category.name = MOTODEV Studio
+android.wizard.project.name = Android Project Using Studio for Android
+android.wizard.project.description = Creates a new MOTODEV Studio for Android project
+android.wizard.widget.project.name = Android Widget Project Using Studio for Android
+
+
+android.wizard.widget.project.description = Creates a new MOTODEV Studio for Android widget project
+
+popupmenu.clean.projects.command.name=Clean selected project(s)
+
+preferencePageName = MOTODEV Studio for Android
+ndkPreferencePageName = Android NDK
+
+wizardName=Install Application
+wizardDescription=Transfers an application package to a device and installs it
+
+command.localization.name=Open Localization Files Editor
+command.localization.description=Open the string editor for localization
+command.name.2 = Open Motorola Mobility Products page
+command.name.obfuscate = Enable/Disable Obfuscation
+command.tooltip.2 = View Android-powered devices from Motorola Mobility
+command.tooltip.obfuscate = Enables or disables automatic obfuscation when packaging Android projects
+command.mnemonic.0 = O
+command.mnemonic.1 = M
+command.mnemonic.2 = P
+
+command.name.helponline = Help Contents
+
+command.name.helpandroid = MOTODEV Discussion Boards
+command.mnemonic.helpandroid = A
+
+command.name.helpstudio = MOTODEV Studio for Android Discussion Boards
+command.mnemonic.helpstudio = S
+
+label.help = Help
+
+
+building.block.explorer.name = Building Block Explorer
+
+monkeyLaunch.description = Create a configuration to launch the UI/Application Exerciser Monkey
+
+monkeyLaunch.name=Test events with Monkey
+
+Monkey_Option_Seed_Description=Seed value for pseudo-random number generator. If you re-run the Monkey with the same seed value, it will generate the same sequence of events.
+Monkey_Option_Throttle_Description=Inserts a fixed delay between events. You can use this option to slow down the Monkey. If not specified, there is no delay and the events are generated as rapidly as possible.
+Monkey_Option_PctTouch_Description=Adjust percentage of touch events. (Touch events are a down-up event in a single place on the screen.)
+Monkey_Option_PctMotion_Description=Adjust percentage of motion events. (Motion events consist of a down event somewhere on the screen, a series of pseudo-random movements, and an up event.)
+Monkey_Option_PctTrackball_Description=Adjust percentage of trackball events. (Trackball events consist of one or more random movements, sometimes followed by a click.)
+Monkey_Option_PctNav_Description=Adjust percentage of "basic" navigation events. (Navigation events consist of up/down/left/right, as input from a directional input device.)
+Monkey_Option_PctMajornav_Description=Adjust percentage of "major" navigation events. (These are navigation events that will typically cause actions within your UI, such as the center button in a 5-way pad, the back key, or the menu key.)
+Monkey_Option_PctSyskeys_Description=Adjust percentage of "system" key events. (These are keys that are generally reserved for use by the system, such as Home, Back, Start Call, End Call, or Volume controls.)
+Monkey_Option_PctAppswitch_Description=Adjust percentage of activity launches. At random intervals, the Monkey will issue a startActivity() call, as a way of maximizing coverage of all activities within your package.
+Monkey_Option_PctAnyevent_Description=Adjust percentage of other types of events. This is a catch-all for all other types of events such as keypresses, other less-used buttons on the device, and so forth.
+Monkey_Option_Categories_Description=If you specify one or more categories this way, the Monkey will only allow the system to visit activities that are listed with one of the specified categories. If you don't specify any categories, the Monkey will select activities listed with the category Intent.CATEGORY_LAUNCHER or Intent.CATEGORY_MONKEY. To specify multiple categories, use the -c option multiple times one -c option per category.
+Monkey_Option_DbgNoEvents_Description=When specified, the Monkey will perform the initial launch into a test activity, but will not generate any further events. For best results, combine with -v, one or more package constraints, and a non-zero throttle to keep the Monkey running for 30 seconds or more. This provides an environment in which you can monitor package transitions invoked by your application.
+Monkey_Option_Hprof_Description=If set, this option will generate profiling reports immediately before and after the Monkey event sequence. This will generate large (~5Mb) files in data/misc, so use with care. See Traceview for more information on trace files.
+Monkey_Option_IgnoreCrashs_Description=Normally, the Monkey will stop when the application crashes or experiences any type of unhandled exception. If you specify this option, the Monkey will continue to send events to the system, until the count is completed.
+Monkey_Option_IgnoreTimeouts_Description=Normally, the Monkey will stop when the application experiences any type of timeout error such as a "Application Not Responding" dialog. If you specify this option, the Monkey will continue to send events to the system, until the count is completed.
+Monkey_Option_IgnoreSecurity_Description=Normally, the Monkey will stop when the application experiences any type of permissions error, for example if it attempts to launch an activity that requires certain permissions. If you specify this option, the Monkey will continue to send events to the system, until the count is completed.
+Monkey_Option_KillProcessAfterError_Description=Normally, when the Monkey stops due to an error, the application that failed will be left running. When this option is set, it will signal the system to stop the process in which the error occurred. Note, under a normal (successful) completion, the launched process(es) are not stopped, and the device is simply left in the last state after the final event.
+Monkey_Option_MonitorNativeCrashes_Description=Watches for and reports crashes occurring in the Android system native code. If --kill-process-after-error is set, the system will stop.
+Monkey_Option_WaitDBG_Description=Stops the Monkey from executing until a debugger is attached to it.
+Monkey_Option_Verbose_Label=Verbose
+Monkey_Option_Verbose_Description=Provides more details about the test as it runs.
+Monkey_Option_OtherMonkeyOptions_Description=Other Monkey options
+Monkey_Option_Categories_Label=Categories
+Monkey_Option_OtherMonkeyOptions_Label=Other Monkey Options
+Monkey_Option_Seed_Label=seed
+Monkey_Option_Throttle_Label=throttle
+Monkey_Option_PctTouch_Label=pct-touch
+Monkey_Option_PctMotion_Label=pct-motion
+Monkey_Option_PctTrackball_Label=pct-trackball
+Monkey_Option_PctNav_Label=pct-nav
+Monkey_Option_PctMajornav_Label=pct-majornav
+Monkey_Option_PctSyskeys_Label=pct-syskeys
+Monkey_Option_PctAppswitch_Label=pct-appswitch
+Monkey_Option_PctAnyevent_Label=pct-anyevent
+Monkey_Option_DbgNoEvents_Label=dbg-no-events
+Monkey_Option_Hprof_Label=hprof
+Monkey_Option_IgnoreCrashs_Label=ignore-crashes
+Monkey_Option_IgnoreTimeouts_Label=ignore-timeouts
+Monkey_Option_IgnoreSecurity_Label=ignore-security-exceptions
+Monkey_Option_KillProcessAfterError_Label=kill-process-after-error
+Monkey_Option_MonitorNativeCrashes_Label=monitor-native-crashes
+Monkey_Option_WaitDBG_Label=wait-dbg
+
+Monkey_Tab_Label_Events=Events
+Monkey_Tab_Label_Constraints=Constraints
+Monkey_Tab_Label_Debugging=Debugging
+Monkey_Tab_Label_General=General
+
+motodevmenu.new.project=New Android Project
+motodevmenu.new.project.tooltip = Creates a new Android project using MOTODEV Studio for Android
+motodevmenu.new.widget.project=New Android Widget Project
+motodevmenu.new.widget.project.tooltip=Creates a new Android widget project using MOTODEV Studio for Android
+motodevmenu.native.androidNDKAddNativeSupport=Adds native support to a project
+motodevmenu.localization.openEditor=Open Android Localization Files Editor
+
+Motodev_Studio_PropertyPage_Name=MOTODEV Studio
+
+popupmenu.add.libraries.command.name=Add Libraries
+popupmenu.add.libraries.command.label=Add Libraries...
+
+android.wizard.activity.endlesslist.usingfragment.name = Endless List (using fragment - requires API level 11 or higher)
+
+android.wizard.activity.endlesslist.usingfragment.description = ListFragment which has no end. Every time the end of the list is about to be reached, more data is retrieved and added to the list.
+
+motodevmenu.autogeneratedcode=Auto-Generated Code
+motodevmenu.autogeneratedcode.activity=Create Activity Based on Template
+
+newWizardsLabel=New
+motodevmenu.web.resources=MOTODEV Web Resources
+studioLabel=MOTODEV Studio \ No newline at end of file
diff --git a/src/plugins/android/plugin.xml b/src/plugins/android/plugin.xml
new file mode 100644
index 0000000..17abdc0
--- /dev/null
+++ b/src/plugins/android/plugin.xml
@@ -0,0 +1,739 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?eclipse version="3.2"?>
+
+<!--
+ Copyright (C) 2012 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<plugin>
+<extension
+ point="org.eclipse.debug.core.launchConfigurationTypes">
+ <launchConfigurationType
+ id="monkeyLaunchConfigurationType"
+ modes="run"
+ name="%monkeyLaunch.name"
+ public="true">
+ </launchConfigurationType>
+ </extension>
+ <extension
+ point="org.eclipse.debug.core.launchDelegates">
+ <launchDelegate
+ delegate="com.motorola.studio.android.wizards.monkey.MonkeyConfigurationDelegate"
+ delegateDescription="monkeyDelegateDescription"
+ id="monkeyLaunchDelegate"
+ modes="run"
+ name="monkeyDelegateName"
+ sourceLocatorId="org.eclipse.jdt.launching.sourceLocator.JavaSourceLookupDirector"
+ sourcePathComputerId="org.eclipse.jdt.launching.sourceLookup.javaSourcePathComputer"
+ type="monkeyLaunchConfigurationType">
+ </launchDelegate>
+ </extension>
+ <extension
+ point="org.eclipse.debug.ui.launchConfigurationTabGroups">
+ <launchConfigurationTabGroup
+ class="com.motorola.studio.android.wizards.monkey.MonkeyConfigurationTabGroup"
+ description="%monkeyLaunch.description"
+ id="monkeyLaunchConfigurationTabGroup"
+ type="monkeyLaunchConfigurationType">
+ </launchConfigurationTabGroup>
+ </extension>
+ <extension
+ point="org.eclipse.debug.ui.launchConfigurationTypeImages">
+ <launchConfigurationTypeImage
+ configTypeID="monkeyLaunchConfigurationType"
+ icon="icons/monkey/monkey_16.png"
+ id="monkeyLaunchConfigurationTypeImage">
+ </launchConfigurationTypeImage>
+ </extension>
+<extension
+ id="com.motorola.studio.android.studioAndroidWizard"
+ point="org.eclipse.ui.newWizards">
+ <wizard
+ canFinishEarly="false"
+ category="com.android.ide.eclipse.wizards.category"
+ class="com.motorola.studio.android.wizards.project.NewAndroidProjectWizard"
+ finalPerspective="com.motorola.studio.android.perspective"
+ hasPages="true"
+ icon="icons/obj16/newaprj_wiz.gif"
+ id="com.motorola.studio.android.wizards.newProjectWizard"
+ name="%android.wizard.project.name"
+ project="true">
+ <description>
+ %android.wizard.project.description
+ </description>
+ </wizard>
+ <wizard
+ canFinishEarly="false"
+ category="com.android.ide.eclipse.wizards.category"
+ class="com.motorola.studio.android.wizards.widget.NewAndroidWidgetProjectWizard"
+ finalPerspective="com.motorola.studio.android.perspective"
+ hasPages="true"
+ icon="icons/obj16/widget_provider_prj_wiz_toolbar.png"
+ id="com.motorola.studio.android.wizards.newWidgetProjectWizard"
+ name="%android.wizard.widget.project.name"
+ project="true">
+ <description>
+ %android.wizard.widget.project.description
+ </description>
+ </wizard>
+ </extension>
+
+ <extension
+ point="org.eclipse.ui.startup">
+ <startup
+ class="com.motorola.studio.android.adt.StudioDeviceChangeListener">
+ </startup>
+ <startup
+ class="com.motorola.studio.android.AssociateProguardEditor">
+ </startup>
+ </extension>
+ <extension
+ point="org.eclipse.ui.preferencePages">
+ <page
+ category="com.motorola.studio.platform.ui.preference"
+ class="com.motorola.studio.android.preferences.ui.AndroidPreferencePage"
+ id="com.motorola.studio.android.preferencepage"
+ name="%Plugin_Name">
+ </page>
+ </extension>
+ <extension
+ point="org.eclipse.ui.commands">
+ <command
+ defaultHandler="com.motorola.studio.android.command.NewProjectWizard"
+ id="com.motorola.studio.android.new.project"
+ name="%motodevmenu.new.project">
+ </command>
+ <command
+ defaultHandler="com.motorola.studio.android.command.NewWidgetProjectWizard"
+ id="com.motorola.studio.android.new.widget.project"
+ name="%motodevmenu.new.widget.project">
+ </command>
+ <command
+ defaultHandler="com.motorola.studio.android.command.OpenStringEditor"
+ description="%command.localization.description"
+ id="com.motorola.studio.android.open.localization.editor"
+ name="%command.localization.name">
+ </command>
+ <command
+ defaultHandler="com.motorola.studio.android.command.CleanProjects"
+ description="%popupmenu.clean.projects.command.name"
+ id="com.motorola.studio.android.clean.projects"
+ name="%popupmenu.clean.projects.command.name">
+ </command>
+ </extension>
+ <extension
+ point="org.eclipse.ui.menus">
+ <menuContribution
+ locationURI="menu:studioAndroidNewWizardsMenu">
+ <command
+ commandId="com.motorola.studio.android.new.project"
+ icon="icons/obj16/newaprj_wiz.gif"
+ label="%motodevmenu.new.project"
+ style="push"
+ tooltip="%motodevmenu.new.project.tooltip">
+ </command>
+ <command
+ commandId="com.motorola.studio.android.new.widget.project"
+ icon="icons/obj16/widget_provider_prj_wiz_toolbar.png"
+ label="%motodevmenu.new.widget.project"
+ style="push"
+ tooltip="%motodevmenu.new.widget.project.tooltip">
+ </command>
+ <separator
+ name="com.motorola.studio.android.afterNewProjectSeparator"
+ visible="true">
+ </separator>
+ </menuContribution>
+ <menuContribution
+ locationURI="toolbar:studioAndroidToolbar">
+ <command
+ commandId="com.motorola.studio.android.new.project"
+ icon="icons/obj16/newaprj_wiz.gif"
+ label="%motodevmenu.new.project"
+ style="push">
+ <visibleWhen
+ checkEnabled="false">
+ <with
+ variable="activeWorkbenchWindow.activePerspective">
+ <or>
+ <equals
+ value="com.motorola.studio.android.perspective">
+ </equals>
+ <equals
+ value="com.motorola.studio.android.db.perspective">
+ </equals>
+ <equals
+ value="org.eclipse.sequoyah.android.cdt.build.ui.perspective">
+ </equals>
+ </or>
+ </with>
+ </visibleWhen>
+ </command>
+ <command
+ commandId="com.motorola.studio.android.new.widget.project"
+ icon="icons/obj16/widget_provider_prj_wiz_toolbar.png"
+ label="%motodevmenu.new.widget.project"
+ style="push">
+ <visibleWhen
+ checkEnabled="false">
+ <with
+ variable="activeWorkbenchWindow.activePerspective">
+ <or>
+ <equals
+ value="com.motorola.studio.android.perspective">
+ </equals>
+ <equals
+ value="com.motorola.studio.android.db.perspective">
+ </equals>
+ <equals
+ value="org.eclipse.sequoyah.android.cdt.build.ui.perspective">
+ </equals>
+ </or>
+ </with>
+ </visibleWhen>
+ </command>
+ </menuContribution>
+ <menuContribution locationURI="menu:motorolaMenu?after=localizationToolsSeparator">
+ <command
+ commandId="com.motorola.studio.android.open.localization.editor"
+ icon="platform:/plugin/org.eclipse.sequoyah.localization.tools/icons/loc_icon.png"
+ label="%motodevmenu.localization.openEditor"
+ style="push">
+ </command>
+ </menuContribution>
+ <menuContribution
+ allPopups="true"
+ locationURI="popup:org.eclipse.ui.popup.any">
+ <command
+ commandId="com.motorola.studio.android.clean.projects"
+ label="%popupmenu.clean.projects.command.name"
+ style="push"
+ tooltip="%popupmenu.clean.projects.command.name">
+ <visibleWhen
+ checkEnabled="false">
+ <iterate
+ ifEmpty="false"
+ operator="and">
+ <adapt
+ type="org.eclipse.core.resources.IProject">
+ <test
+ property="org.eclipse.core.resources.open">
+ </test>
+ <test
+ forcePluginActivation="true"
+ property="org.eclipse.core.resources.projectNature"
+ value="com.android.ide.eclipse.adt.AndroidNature">
+ </test>
+ </adapt>
+ </iterate>
+ </visibleWhen>
+ </command>
+ </menuContribution>
+ <menuContribution
+ locationURI="menu:motorolaMenu?after=externalToolsSeparator">
+ <command
+ commandId="com.motorola.studio.android.obfuscate.projects"
+ icon="icons/obj16/obfuscate.png"
+ label="%command.name.obfuscate"
+ style="push">
+ </command>
+ </menuContribution>
+ </extension>
+ <extension
+ point="com.motorola.studio.android.logger.collector.log">
+ <logContribution
+ logFileImpl="com.motorola.studio.android.logger.DevicePropertyLogger">
+ </logContribution>
+ <logContribution
+ logFileImpl="com.motorola.studio.android.logger.AppValidatorLogCollector">
+ </logContribution>
+ </extension>
+ <extension
+ point="biz.junginger.rss.eclipse.RssPlugin.channel">
+ <channel
+ name="MOTODEV Blog"
+ enablement="true"
+ url="http://feeds2.feedburner.com/MOTODEVBlog">
+ </channel>
+ <channel
+ name="MOTODEV Studio Blog"
+ enablement="true"
+ url="http://feeds2.feedburner.com/motodevstudioblog">
+ </channel>
+ <channel
+ name="MOTODEV Studio for Android discussion boards"
+ enablement="false"
+ url="http://community.developer.motorola.com/mtrl/rss/board?board.id=Studio_Android">
+ </channel>
+ </extension>
+ <extension
+ point="org.eclipse.ui.commands">
+ <command
+ defaultHandler="com.motorola.studio.android.application.store.handlers.OpenMotorolaMobilityDevicePropertiesHandler"
+ id="com.motorola.studio.android.open.motorola.products"
+ name="%command.name.2">
+ </command>
+ <command
+ defaultHandler="com.motorola.studio.android.obfuscate.handlers.ObfuscateProjectsHandler"
+ id="com.motorola.studio.android.obfuscate.projects"
+ name="%command.name.obfuscate">
+ </command>
+ <command
+ defaultHandler="com.motorola.studio.android.help.handlers.OpenHelpAndroidHandler"
+ id="com.motorola.studio.android.HelpAndroid"
+ name="%command.name.helpandroid">
+ </command>
+ <command
+ defaultHandler="com.motorola.studio.android.help.handlers.OpenHelpStudioHandler"
+ id="com.motorola.studio.android.HelpStudio"
+ name="%command.name.helpstudio">
+ </command>
+ <command
+ defaultHandler="com.motorola.studio.android.help.handlers.OpenOnlineHelpStudioHandler"
+ id="com.motorola.studio.android.OnlineHelpStudio"
+ name="%command.name.helponline">
+ </command>
+ </extension>
+ <extension
+ point="org.eclipse.ui.menus">
+ <menuContribution
+ allPopups="false"
+ locationURI="menu:motorolaMenu?after=com.motorola.studio.android.open.motodev.web.resources">
+ <command
+ commandId="com.motorola.studio.android.open.motorola.products"
+ disabledIcon="icons/obj16/handset_info.png"
+ hoverIcon="icons/obj16/handset_info.png"
+ icon="icons/obj16/handset_info.png"
+ id="open.motorola.products.ui.command"
+ label="%command.name.2"
+ mnemonic="%command.mnemonic.2"
+ style="push"
+ tooltip="%command.tooltip.2">
+ </command>
+ </menuContribution>
+ </extension>
+ <extension
+ point="org.eclipse.ui.perspectiveExtensions">
+ <perspectiveExtension
+ targetID="org.eclipse.sequoyah.android.cdt.build.ui.perspective">
+ <newWizardShortcut
+ id="com.motorola.studio.android.wizards.newProjectWizard">
+ </newWizardShortcut>
+ <newWizardShortcut
+ id="com.motorola.studio.android.wizards.newWidgetProjectWizard">
+ </newWizardShortcut>
+ </perspectiveExtension>
+ </extension>
+ <extension
+ point="org.eclipse.ui.navigator.navigatorContent">
+ <commonWizard
+ menuGroupId="1newAndroidProject"
+ type="new"
+ wizardId="com.motorola.studio.android.wizards.newProjectWizard">
+ <enablement></enablement>
+ </commonWizard>
+ <commonWizard
+ menuGroupId="1newAndroidProject"
+ type="new"
+ wizardId="com.motorola.studio.android.wizards.newWidgetProjectWizard">
+ <enablement></enablement>
+ </commonWizard>
+ <commonWizard
+ menuGroupId="2newAndroidBuildingBlock"
+ type="new"
+ wizardId="com.motorola.studio.android.wizards.newActivityWizard">
+ <enablement>
+ <adapt
+ type="org.eclipse.core.resources.IProject">
+ <test
+ property="org.eclipse.core.resources.projectNature"
+ value="com.android.ide.eclipse.adt.AndroidNature">
+ </test>
+ </adapt>
+ </enablement>
+ </commonWizard>
+ <commonWizard
+ menuGroupId="2newAndroidBuildingBlock"
+ type="new"
+ wizardId="com.motorola.studio.android.wizards.newActivityBasedOnTemplateWizard">
+ <enablement>
+ <adapt
+ type="org.eclipse.core.resources.IProject">
+ <test
+ property="org.eclipse.core.resources.projectNature"
+ value="com.android.ide.eclipse.adt.AndroidNature">
+ </test>
+ </adapt>
+ </enablement>
+ </commonWizard>
+ <commonWizard
+ menuGroupId="2newAndroidBuildingBlock"
+ type="new"
+ wizardId="com.motorola.studio.android.wizards.newReceiverWizard">
+ <enablement>
+ <adapt
+ type="org.eclipse.core.resources.IProject">
+ <test
+ property="org.eclipse.core.resources.projectNature"
+ value="com.android.ide.eclipse.adt.AndroidNature">
+ </test>
+ </adapt>
+ </enablement>
+ </commonWizard>
+ <commonWizard
+ menuGroupId="2newAndroidBuildingBlock"
+ type="new"
+ wizardId="com.motorola.studio.android.wizards.newServiceWizard">
+ <enablement>
+ <adapt
+ type="org.eclipse.core.resources.IProject">
+ <test
+ property="org.eclipse.core.resources.projectNature"
+ value="com.android.ide.eclipse.adt.AndroidNature">
+ </test>
+ </adapt>
+ </enablement>
+ </commonWizard>
+ <commonWizard
+ menuGroupId="2newAndroidBuildingBlock"
+ type="new"
+ wizardId="com.motorola.studio.android.wizards.newProviderWizard">
+ <enablement>
+ <adapt
+ type="org.eclipse.core.resources.IProject">
+ <test
+ property="org.eclipse.core.resources.projectNature"
+ value="com.android.ide.eclipse.adt.AndroidNature">
+ </test>
+ </adapt>
+ </enablement>
+ </commonWizard>
+ </extension>
+ <extension
+ point = "org.eclipse.ui.propertyPages">
+ <page
+ id="com.motorola.studio.android.MotodevStudioPropertyPage"
+ name="%motodev.category.name"
+ class="com.motorola.studio.android.propertypage.MotodevStudioPropertyPage">
+ <enabledWhen>
+ <adapt type="org.eclipse.core.resources.IProject">
+ <test property="org.eclipse.core.resources.projectNature" value="com.android.ide.eclipse.adt.AndroidNature"/>
+ </adapt>
+ </enabledWhen>
+ </page>
+
+ </extension>
+ <!-- Help -->
+ <extension
+ point="org.eclipse.ui.menus">
+ <menuContribution
+ allPopups="false"
+ locationURI="menu:motorolaMenu?after=helpSeparator">
+ <menu
+ id="studioHelpMenu"
+ label="%label.help">
+ <command
+ commandId="com.motorola.studio.android.OnlineHelpStudio"
+ icon="IMG_LCL_LINKTO_HELP"
+ label="%command.name.helponline"
+ style="push"
+ tooltip="%command.name.helponline">
+ </command>
+ <command
+ commandId="com.motorola.studio.android.HelpAndroid"
+ icon="icons/obj16/android_discussion_16x16.png"
+ label="%command.name.helpandroid"
+ style="push"
+ tooltip="%command.name.helpandroid">
+ </command>
+ <command
+ commandId="com.motorola.studio.android.HelpStudio"
+ icon="icons/obj16/studio_discussion_16x16.png"
+ label="%command.name.helpstudio"
+ style="push"
+ tooltip="%command.name.helpstudio">
+ </command>
+ </menu>
+ </menuContribution>
+ </extension>
+ <!-- PLATFORM -->
+ <extension
+ point="org.eclipse.ui.preferencePages">
+ <page
+ class="com.motorola.studio.android.preferences.ui.MotodevStudioPreference"
+ id="com.motorola.studio.platform.ui.preference"
+ name="MOTODEV Studio">
+ </page>
+ </extension>
+ <extension
+ point="org.eclipse.ui.cheatsheets.cheatSheetContent">
+ <category
+ id="com.motorola.studio.platform.ui.cheatsheet"
+ name="MOTODEV Studio">
+ </category>
+ </extension>
+ <extension
+ point="org.eclipse.ui.importWizards">
+ <category
+ id="com.motorola.studio.platform.ui.import"
+ name="MOTODEV Studio">
+ </category>
+ </extension>
+ <extension
+ point="org.eclipse.ui.exportWizards">
+ <category
+ id="com.motorola.studio.platform.ui.export"
+ name="MOTODEV Studio">
+ </category>
+ </extension>
+ <extension
+ point="org.eclipse.ui.newWizards">
+ <category
+ id="com.motorola.studio.platform.ui.newproject"
+ name="MOTODEV Studio">
+ </category>
+ </extension>
+ <extension
+ point="org.eclipse.ui.perspectives">
+ <perspective
+ class="com.motorola.studio.android.perspective.MotodevStudioAndroidPerspective"
+ fixed="false"
+ icon="icons/obj16/plate16.png"
+ id="com.motorola.studio.android.perspective"
+ name="MOTODEV Studio for Android">
+ </perspective>
+ </extension>
+ <extension
+ point="org.eclipse.ui.menus">
+ <menuContribution
+ locationURI="menu:motorolaMenu">
+ <separator
+ name="newSeparator"
+ visible="true">
+ </separator>
+ <menu
+ id="studioAndroidNewWizardsMenu"
+ label="%newWizardsLabel">
+ </menu>
+ <separator
+ name="appValidatorSeparator"
+ visible="false">
+ </separator>
+ <separator
+ name="manageDatabaseSeparator"
+ visible="false">
+ </separator>
+ <separator
+ name="autoGenerateCodeSeparator"
+ visible="false">
+ </separator>
+ <separator
+ name="signingSeparator"
+ visible="false">
+ </separator>
+ <separator
+ name="otherNewWizardsSeparator"
+ visible="true">
+ </separator>
+ <separator
+ name="localizationToolsSeparator"
+ visible="true">
+ </separator>
+ <separator
+ name="externalToolsSeparator"
+ visible="true">
+ </separator>
+ <separator
+ name="webResourceSeparator"
+ visible="true">
+ </separator>
+ <command
+ commandId="com.motorola.studio.android.open.motodev.web.resources"
+ icon="icons/obj16/webresources.gif"
+ label="%motodevmenu.web.resources"
+ style="push">
+ </command>
+ <separator
+ name="updateSeparator"
+ visible="true">
+ </separator>
+ <separator
+ name="helpSeparator"
+ visible="true">
+ </separator>
+ </menuContribution>
+ <menuContribution
+ locationURI="toolbar:org.eclipse.ui.main.toolbar">
+ <toolbar
+ id="studioAndroidToolbar">
+ <!-- This visibleWhen block is created for when the bug of the visibleWhen behavior on
+ toolbar is fixed -->
+ <visibleWhen
+ checkEnabled="false">
+ <with
+ variable="activeWorkbenchWindow.activePerspective">
+ <or>
+ <equals
+ value="com.motorola.studio.android.perspective">
+ </equals>
+ <equals
+ value="com.motorola.studio.android.db.perspective">
+ </equals>
+ <equals
+ value="org.eclipse.sequoyah.android.cdt.build.ui.perspective">
+ </equals>
+ </or>
+ </with>
+ </visibleWhen>
+ </toolbar>
+ </menuContribution>
+ <menuContribution
+ locationURI="popup:org.eclipse.jdt.ui.PackageExplorer">
+ <menu
+ id="studioAndroidPopupMenu"
+ label="%studioLabel">
+ <visibleWhen
+ checkEnabled="false">
+ <with
+ variable="activeWorkbenchWindow.activePerspective">
+ <or>
+ <equals
+ value="com.motorola.studio.android.perspective">
+ </equals>
+ <equals
+ value="org.eclipse.sequoyah.android.cdt.build.ui.perspective">
+ </equals>
+ </or>
+ </with>
+ </visibleWhen>
+ </menu>
+ </menuContribution>
+ <menuContribution
+ locationURI="popup:org.eclipse.ui.views.ResourceNavigator">
+ <menu
+ id="studioAndroidPopupMenu"
+ label="%studioLabel">
+ <visibleWhen
+ checkEnabled="false">
+ <with
+ variable="activeWorkbenchWindow.activePerspective">
+ <or>
+ <equals
+ value="com.motorola.studio.android.perspective">
+ </equals>
+ <equals
+ value="org.eclipse.sequoyah.android.cdt.build.ui.perspective">
+ </equals>
+ </or>
+ </with>
+ </visibleWhen>
+ </menu>
+ </menuContribution>
+ <menuContribution
+ locationURI="popup:org.eclipse.ui.navigator.ProjectExplorer#PopupMenu">
+ <menu
+ id="studioAndroidPopupMenu"
+ label="%studioLabel">
+ <visibleWhen
+ checkEnabled="false">
+ <with
+ variable="activeWorkbenchWindow.activePerspective">
+ <or>
+ <equals
+ value="com.motorola.studio.android.perspective">
+ </equals>
+ <equals
+ value="org.eclipse.sequoyah.android.cdt.build.ui.perspective">
+ </equals>
+ </or>
+ </with>
+ </visibleWhen>
+ </menu>
+ </menuContribution>
+ <menuContribution
+ locationURI="menu:help?before=com.motorola.studio.platform.logger.collector.separator">
+ <command
+ commandId="com.motorola.studio.android.open.motodev.web.resources"
+ icon="icons/obj16/webresources.gif"
+ label="%motodevmenu.web.resources"
+ style="push">
+ </command>
+ </menuContribution>
+ </extension>
+ <extension
+ point="org.eclipse.ui.exportWizards">
+ <category
+ id="studioAndroidExportCategory"
+ name="%studioLabel"
+ parentCategory="com.motorola.studio.platform.ui.export">
+ </category>
+ </extension>
+ <extension
+ point="org.eclipse.ui.newWizards">
+ <category
+ id="studioAndroidNewWizardsCategory"
+ name="%studioLabel"
+ parentCategory="com.motorola.studio.platform.ui.newproject">
+ </category>
+ </extension>
+ <extension
+ point="org.eclipse.ui.perspectiveExtensions">
+ <perspectiveExtension
+ targetID="org.eclipse.debug.ui.DebugPerspective">
+ <view
+ id="com.android.ide.eclipse.ddms.views.ThreadView"
+ minimized="false"
+ relationship="stack"
+ relative="org.eclipse.debug.ui.BreakpointView">
+ </view>
+ <view
+ id="com.android.ide.eclipse.ddms.views.HeapView"
+ minimized="false"
+ relationship="stack"
+ relative="org.eclipse.debug.ui.BreakpointView">
+ </view>
+ </perspectiveExtension>
+ <perspectiveExtension
+ targetID="com.motorola.studio.android.perspective">
+ <view
+ id="org.eclipse.ui.navigator.ProjectExplorer"
+ minimized="false"
+ relationship="stack"
+ relative="org.eclipse.jdt.ui.PackageExplorer"
+ visible="false">
+ </view>
+ </perspectiveExtension>
+ </extension>
+ <extension
+ point="org.eclipse.ui.commands">
+ <command
+ defaultHandler="com.motorola.studio.android.perspective.OpenWebResources"
+ id="com.motorola.studio.android.open.motodev.web.resources"
+ name="%motodevmenu.web.resources">
+ </command>
+ </extension>
+ <extension
+ point="org.eclipse.ui.menus">
+ <menuContribution
+ locationURI="menu:org.eclipse.ui.main.menu?after=project">
+ <menu
+ id="motorolaMenu"
+ label="MOTODEV">
+
+ </menu>
+ </menuContribution>
+ </extension>
+
+</plugin>
diff --git a/src/plugins/android/resources/login.html b/src/plugins/android/resources/login.html
new file mode 100644
index 0000000..c0a28f1
--- /dev/null
+++ b/src/plugins/android/resources/login.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <script>
+ function sendForm() {
+ var form = document.getElementById("login_form");
+ form.submit();
+ }
+ </script>
+ </head>
+
+ <body>
+ <div style="display:none">
+ <form id="login_form" action="https://developer.motorola.com/?login=1" method="post">
+ <input class="text" id="login-email" type="text" name="login_username" value="$$login" />
+ <input class="text" id="login-password" type="password" name="login_password" value="$$password" />
+ <input type='hidden' name='login_return_to' value='/studio-flow/v1/motoappstore' />
+ </form>
+ </div>
+ <p>Loading...</p>
+ <script>
+ sendForm();
+ </script>
+ </body>
+</html> \ No newline at end of file
diff --git a/src/plugins/android/resources/monkey_options.xml b/src/plugins/android/resources/monkey_options.xml
new file mode 100644
index 0000000..c1f5492
--- /dev/null
+++ b/src/plugins/android/resources/monkey_options.xml
@@ -0,0 +1,91 @@
+<monkeyOptions>
+
+ <group id="%Monkey_Tab_Label_Events">
+
+ <monkeyOption name="-s" fName="%Monkey_Option_Seed_Label" type="int">
+ <description>%Monkey_Option_Seed_Description</description>
+ </monkeyOption>
+
+ <monkeyOption name="--throttle" fName="%Monkey_Option_Throttle_Label" type="int">
+ <description>%Monkey_Option_Throttle_Description</description>
+ </monkeyOption>
+
+ <monkeyOption name="--pct-touch" fName="%Monkey_Option_PctTouch_Label" type="int">
+ <description>%Monkey_Option_PctTouch_Description</description>
+ </monkeyOption>
+
+ <monkeyOption name="--pct-motion" fName="%Monkey_Option_PctMotion_Label" type="int">
+ <description>%Monkey_Option_PctMotion_Description</description>
+ </monkeyOption>
+
+ <monkeyOption name="--pct-trackball" fName="%Monkey_Option_PctTrackball_Label" type="int">
+ <description>%Monkey_Option_PctTrackball_Description</description>
+ </monkeyOption>
+
+ <monkeyOption name="--pct-nav" fName="%Monkey_Option_PctNav_Label" type="int">
+ <description>%Monkey_Option_PctNav_Description</description>
+ </monkeyOption>
+
+ <monkeyOption name="--pct-majornav" fName="%Monkey_Option_PctMajornav_Label" type="int">
+ <description>%Monkey_Option_PctMajornav_Description</description>
+ </monkeyOption>
+
+ <monkeyOption name="--pct-syskeys" fName="%Monkey_Option_PctSyskeys_Label" type="int">
+ <description>%Monkey_Option_PctSyskeys_Description</description>
+ </monkeyOption>
+
+ <monkeyOption name="--pct-appswitch" fName="%Monkey_Option_PctAppswitch_Label" type="int">
+ <description>%Monkey_Option_PctAppswitch_Description</description>
+ </monkeyOption>
+
+ <monkeyOption name="--pct-anyevent" fName="%Monkey_Option_PctAnyevent_Label" type="int">
+ <description>%Monkey_Option_PctAnyevent_Description</description>
+ </monkeyOption>
+ </group>
+
+
+ <group id="%Monkey_Tab_Label_Constraints">
+ <monkeyOption name="-c" fName="%Monkey_Option_Categories_Label" type="text">
+ <description>%Monkey_Option_Categories_Description</description>
+ </monkeyOption>
+ </group>
+
+ <group id="%Monkey_Tab_Label_Debugging">
+ <monkeyOption name="--dbg-no-events" fName="%Monkey_Option_DbgNoEvents_Label" type="none">
+ <description>%Monkey_Option_DbgNoEvents_Description</description>
+ </monkeyOption>
+ <monkeyOption name="--hprof" fName="%Monkey_Option_Hprof_Label" type="none">
+ <description>%Monkey_Option_Hprof_Description</description>
+ </monkeyOption>
+ <monkeyOption name="--ignore-crashes" fName="%Monkey_Option_IgnoreCrashs_Label" type="none">
+ <description>%Monkey_Option_IgnoreCrashs_Description</description>
+ </monkeyOption>
+ <monkeyOption name="--ignore-timeouts" fName="%Monkey_Option_IgnoreTimeouts_Label" type="none">
+ <description>%Monkey_Option_IgnoreTimeouts_Description</description>
+ </monkeyOption>
+ <monkeyOption name="--ignore-security-exceptions" fName="%Monkey_Option_IgnoreSecurity_Label" type="none">
+ <description>%Monkey_Option_IgnoreSecurity_Description</description>
+ </monkeyOption>
+ <monkeyOption name="--kill-process-after-error" fName="%Monkey_Option_KillProcessAfterError_Label" type="none">
+ <description>%Monkey_Option_KillProcessAfterError_Description</description>
+ </monkeyOption>
+ <monkeyOption name="--monitor-native-crashes" fName="%Monkey_Option_MonitorNativeCrashes_Label" type="none">
+ <description>%Monkey_Option_MonitorNativeCrashes_Description</description>
+ </monkeyOption>
+ <monkeyOption name="--wait-dbg" fName="%Monkey_Option_WaitDBG_Label" type="none">
+ <description>%Monkey_Option_WaitDBG_Description</description>
+ </monkeyOption>
+ </group>
+
+ <group id="%Monkey_Tab_Label_General">
+ <monkeyOption name="-v" fName="%Monkey_Option_Verbose_Label" type="none">
+ <description>%Monkey_Option_Verbose_Description</description>
+ </monkeyOption>
+ <monkeyOption name="other" fName="%Monkey_Option_OtherMonkeyOptions_Label" type="text">
+ <description>%Monkey_Option_OtherMonkeyOptions_Description</description>
+ </monkeyOption>
+
+ </group>
+
+
+</monkeyOptions> \ No newline at end of file
diff --git a/src/plugins/android/src/com/motorola/studio/android/AndroidPlugin.java b/src/plugins/android/src/com/motorola/studio/android/AndroidPlugin.java
new file mode 100644
index 0000000..21a5faa
--- /dev/null
+++ b/src/plugins/android/src/com/motorola/studio/android/AndroidPlugin.java
@@ -0,0 +1,272 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.motorola.studio.android;
+
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.Set;
+
+import org.eclipse.core.runtime.jobs.IJobChangeEvent;
+import org.eclipse.core.runtime.jobs.IJobManager;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.core.runtime.jobs.JobChangeAdapter;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.ui.IWindowListener;
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.plugin.AbstractUIPlugin;
+import org.osgi.framework.BundleContext;
+
+import com.motorola.studio.android.adt.DDMSFacade;
+import com.motorola.studio.android.common.IAndroidConstants;
+import com.motorola.studio.android.common.log.StudioLogger;
+
+/**
+ * The activator class controls the plug-in life cycle
+ */
+public class AndroidPlugin extends AbstractUIPlugin
+{
+ //Listening to this job, instead of loading sdk job, which seems to don't exist anymore.
+ private static final String ANDROID_SDK_CONTENT_LOADER_JOB = "Android SDK Content Loader";
+
+ private final LinkedList<Runnable> listeners = new LinkedList<Runnable>();
+
+ protected boolean sdkLoaded = false;
+
+ /**
+ * The plug-in ID
+ */
+ public static final String PLUGIN_ID = "com.motorola.studio.android";
+
+ /**
+ * Studio for Android Perspective ID
+ */
+ public static final String PERSPECTIVE_ID = "com.motorola.studio.android.perspective";
+
+ /**
+ * Nature of Android projects
+ */
+ public static final String Android_Nature = IAndroidConstants.ANDROID_NATURE;
+
+ /**
+ * The Motorola Android Branding icon
+ */
+ public static final String ANDROID_MOTOROLA_BRAND_ICON_PATH = "icons/obj16/plate16.png";
+
+ public static final String SHALL_UNEMBED_EMULATORS_PREF_KEY = "shallUnembedEmulators";
+
+ // The shared instance
+ private static AndroidPlugin plugin;
+
+ public static final String NDK_LOCATION_PREFERENCE = PLUGIN_ID + ".ndkpath";
+
+ public static final String CYGWIN_LOCATION_PREFERENCE = PLUGIN_ID + ".cigwinpath";
+
+ public static final String WARN_ABOUT_HPROF_PREFERENCE = PLUGIN_ID
+ + ".warnAboutHprofSaveAction";
+
+ public static final String GCC_VERSION_PROPERTY = "gccversion";
+
+ public static final String PLATFORM_PROPERTY = "platform";
+
+ public static final String SRC_LOCATION_PROPERTY = "srclocation";
+
+ public static final String OBJ_LOCATION_PROPERTY = "objlocation";
+
+ public static final String LIB_LOCATION_PROPERTY = "liblocation";
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.ui.plugin.AbstractUIPlugin#start(org.osgi.framework.BundleContext)
+ */
+ @Override
+ public void start(BundleContext context) throws Exception
+ {
+ StudioLogger.debug(AndroidPlugin.class, "Starting MOTODEV Android Plugin...");
+
+ super.start(context);
+ plugin = this;
+
+ Thread t = new Thread("DDMS Setup")
+ {
+ @Override
+ public void run()
+ {
+ IWorkbench workbench = PlatformUI.getWorkbench();
+ IWorkbenchWindow window = workbench.getActiveWorkbenchWindow();
+
+ /* Linux problem with e4.
+ * Check if workbench window is done before calling DDMSFacade#setup().
+ * If the workbench window is not done yet, then add a listener to the workbench window
+ * that will be responsible to call DDMSFacade#setup().
+ * It seems that e4 changed its behavior when loading plugins, which was causing deadlocks
+ * on linux startup.
+ * This workaround works in any OS.
+ */
+ if (window != null)
+ {
+ StudioLogger.debug(AndroidPlugin.class,
+ "Starting DDMS facade WITHOUT using listener...");
+
+ DDMSFacade.setup();
+
+ StudioLogger.debug(AndroidPlugin.class,
+ "DDMS facade started WITHOUT using listener.");
+ }
+ else
+ {
+ workbench.addWindowListener(new IWindowListener()
+ {
+
+ public void windowActivated(IWorkbenchWindow window)
+ {
+ //do nothing
+ }
+
+ public void windowDeactivated(IWorkbenchWindow window)
+ {
+ //do nothing
+ }
+
+ public void windowClosed(IWorkbenchWindow window)
+ {
+ //do nothing
+ }
+
+ public void windowOpened(IWorkbenchWindow window)
+ {
+ StudioLogger.debug(AndroidPlugin.class,
+ "Starting DDMS facade using listener...");
+
+ DDMSFacade.setup();
+
+ StudioLogger.debug(AndroidPlugin.class,
+ "DDMS facade started using listener.");
+ }
+ });
+ }
+ };
+ };
+
+ getPreferenceStore().setDefault(AndroidPlugin.SHALL_UNEMBED_EMULATORS_PREF_KEY, true);
+
+ // every time the Android SDK Job finishes its execution
+ IJobManager manager = Job.getJobManager();
+ manager.addJobChangeListener(new JobChangeAdapter()
+ {
+ @Override
+ public void done(IJobChangeEvent event)
+ {
+ Job job = event.getJob();
+ if (job != null)
+ {
+ String jobName = job.getName();
+ if (jobName != null)
+ {
+ if (jobName.equals(ANDROID_SDK_CONTENT_LOADER_JOB))
+ {
+
+ sdkLoaded = true;
+
+ /*
+ * Workaround
+ * The Listener should be copied in this set, to avoid exceptions in the loop.
+ * The exception occurs when a listener remove itself.
+ */
+ StudioLogger.debug(AndroidPlugin.this, "Notify SDK loader listeners");
+ Set<Runnable> setListeners = new HashSet<Runnable>(listeners);
+ for (Runnable listener : setListeners)
+ {
+ try
+ {
+ listener.run();
+ }
+ catch (Throwable e)
+ {
+ // Log error of this listener and keep handling the next listener...
+ StudioLogger.error(AndroidPlugin.class,
+ "Error while handling SDK loader procedure.", e);
+ }
+ }
+ }
+ }
+ }
+ }
+ });
+ t.start();
+
+ StudioLogger.debug(AndroidPlugin.class, "MOTODEV Android Plugin started.");
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.ui.plugin.AbstractUIPlugin#stop(org.osgi.framework.BundleContext)
+ */
+ @Override
+ public void stop(BundleContext context) throws Exception
+ {
+ plugin = null;
+ super.stop(context);
+ }
+
+ /**
+ * Add a Listener that will be executed after any SDK loader action.
+ * @param listener
+ */
+ public void addSDKLoaderListener(Runnable listener)
+ {
+ listeners.addLast(listener);
+
+ if (sdkLoaded)
+ {
+ listener.run();
+ }
+ }
+
+ /**
+ * Remove the given Listener.
+ * @param listener
+ */
+ public void removeSDKLoaderListener(Runnable listener)
+ {
+ if (listeners.contains(listener))
+ {
+ listeners.remove(listener);
+ }
+ }
+
+ /**
+ * Returns the shared instance
+ *
+ * @return the shared instance
+ */
+ public static AndroidPlugin getDefault()
+ {
+ return plugin;
+ }
+
+ /**
+ * Creates and returns a new image descriptor for an image file in this plug-in.
+ * @param path the relative path of the image file, relative to the root of the plug-in; the path must be legal
+ * @return an image descriptor, or null if no image could be found
+ */
+ public static ImageDescriptor getImageDescriptor(String path)
+ {
+ return imageDescriptorFromPlugin(PLUGIN_ID, path);
+ }
+}
diff --git a/src/plugins/android/src/com/motorola/studio/android/AssociateProguardEditor.java b/src/plugins/android/src/com/motorola/studio/android/AssociateProguardEditor.java
new file mode 100644
index 0000000..ac7c597
--- /dev/null
+++ b/src/plugins/android/src/com/motorola/studio/android/AssociateProguardEditor.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.motorola.studio.android;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import org.eclipse.ui.IStartup;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.internal.WorkbenchPlugin;
+import org.eclipse.ui.internal.registry.EditorRegistry;
+import org.eclipse.ui.internal.registry.FileEditorMapping;
+
+/**
+ * Associates Proguard editor to proguard.cfg file on studio start-up.
+ */
+@SuppressWarnings("restriction")
+public class AssociateProguardEditor implements IStartup
+{
+ // The plug-in ID
+ public static final String EDITOR_ID = "net.certiv.proguarddt.editor.ProGuardDTEditor"; //$NON-NLS-1$
+
+ public void earlyStartup()
+ {
+ PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable()
+ {
+
+ public void run()
+ {
+ try
+ {
+ if (!System.getProperty("java.version").startsWith("1.5"))
+ {
+ //code valid for Java 1.6 or higher
+ EditorRegistry registry =
+ (EditorRegistry) WorkbenchPlugin.getDefault().getEditorRegistry(); // cast to allow save to be called
+ FileEditorMapping[] mappings =
+ (FileEditorMapping[]) registry.getFileEditorMappings();
+ List<FileEditorMapping> listMappings = new ArrayList<FileEditorMapping>();
+ listMappings.addAll(Arrays.asList(mappings));
+ listMappings.add(new FileEditorMapping("proguard", "cfg"));
+ FileEditorMapping[] newMappings =
+ listMappings.toArray(new FileEditorMapping[0]);
+ registry.setFileEditorMappings(newMappings);
+
+ registry.setDefaultEditor("proguard.cfg", EDITOR_ID);
+ registry.saveAssociations();
+ }
+ }
+ catch (Throwable t)
+ {
+ //do nothing - let association for proguard.cfg file to be with text editor
+ }
+ }
+ });
+ }
+}
diff --git a/src/plugins/android/src/com/motorola/studio/android/adt/AdtUtils.java b/src/plugins/android/src/com/motorola/studio/android/adt/AdtUtils.java
new file mode 100644
index 0000000..13521b9
--- /dev/null
+++ b/src/plugins/android/src/com/motorola/studio/android/adt/AdtUtils.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.motorola.studio.android.adt;
+
+import java.io.File;
+import java.security.PrivateKey;
+import java.security.cert.X509Certificate;
+
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+
+import com.android.ide.eclipse.adt.internal.project.ExportHelper;
+
+/**
+ * Methods to interface with ADT plug-in.
+ */
+public class AdtUtils
+{
+
+ /**
+ * Exports APK in release mode, signing the package with the given certificate/key
+ * @throws CoreException
+ */
+ public static void exportReleaseApk(IProject project, File outputFile, PrivateKey key,
+ X509Certificate certificate, IProgressMonitor monitor) throws CoreException
+ {
+ ExportHelper exportHelper = new ExportHelper();
+ ExportHelper.exportReleaseApk(project, outputFile, key, certificate, monitor);
+ }
+
+ /**
+ * Exports APK in release mode, without signing
+ * @throws CoreException
+ */
+ public static void exportUnsignedReleaseApk(IProject project, File outputFile,
+ IProgressMonitor monitor) throws CoreException
+ {
+ ExportHelper exportHelper = new ExportHelper();
+ ExportHelper.exportReleaseApk(project, outputFile, null, null, monitor);
+ }
+
+}
diff --git a/src/plugins/android/src/com/motorola/studio/android/adt/DDMSFacade.java b/src/plugins/android/src/com/motorola/studio/android/adt/DDMSFacade.java
new file mode 100644
index 0000000..31d6f14
--- /dev/null
+++ b/src/plugins/android/src/com/motorola/studio/android/adt/DDMSFacade.java
@@ -0,0 +1,2273 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.motorola.studio.android.adt;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+import java.util.concurrent.TimeoutException;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.SubMonitor;
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.ui.PlatformUI;
+
+import com.android.ddmlib.AdbCommandRejectedException;
+import com.android.ddmlib.AndroidDebugBridge;
+import com.android.ddmlib.AndroidDebugBridge.IClientChangeListener;
+import com.android.ddmlib.Client;
+import com.android.ddmlib.ClientData;
+import com.android.ddmlib.EmulatorConsole;
+import com.android.ddmlib.FileListingService;
+import com.android.ddmlib.FileListingService.FileEntry;
+import com.android.ddmlib.IDevice;
+import com.android.ddmlib.IDevice.DeviceState;
+import com.android.ddmlib.MultiLineReceiver;
+import com.android.ddmlib.SyncException;
+import com.android.ddmlib.SyncService;
+import com.android.ddmlib.SyncService.ISyncProgressMonitor;
+import com.android.ide.eclipse.adt.AdtPlugin;
+import com.android.ide.eclipse.adt.internal.preferences.AdtPrefs;
+import com.android.ide.eclipse.ddms.DdmsPlugin;
+import com.motorola.studio.android.AndroidPlugin;
+import com.motorola.studio.android.adt.StudioAndroidEventManager.EventType;
+import com.motorola.studio.android.common.IAndroidConstants;
+import com.motorola.studio.android.common.exception.AndroidException;
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.i18n.AndroidNLS;
+import com.motorola.studio.android.utilities.TelnetFrameworkAndroid;
+
+/**
+ * DESCRIPTION:
+ * This class is the common interface to functionalities from DDMS
+ *
+ * RESPONSIBILITY:
+ * Centralizes the access to DDMS.
+ *
+ * COLABORATORS:
+ * None.
+ *
+ * USAGE:
+ * Use the public methods to use DDMS
+ */
+public class DDMSFacade
+{
+ /**
+ * Command for switching back to USB connection mode.
+ */
+ private static final String USB_SWITCH_BACK_COMMAND = "usb"; //$NON-NLS-1$
+
+ /**
+ * Argument which indicates the device to apply a certain command.
+ */
+ private static final String DEVICE_ID_INDICATOR = "-s"; //$NON-NLS-1$
+
+ private static final String DEFAULT_WIRELESS_DEVICE_PROPERTY = "tiwlan0"; //$NON-NLS-1$
+
+ /**
+ * Map containing all connected devices.
+ * It is being kept for us not to depend on ADT every time we need one, preventing
+ * deadlocks.
+ */
+ private static final Map<String, IDevice> connectedDevices = new HashMap<String, IDevice>();
+
+ /**
+ * Set containing the serial numbers of the devices completely loaded.
+ * A device is considered completely loaded if it has already loaded the HOME application.
+ */
+ private static final Set<String> completelyUpDevices = new HashSet<String>();
+
+ /**
+ * Folder located inside the SDK folder containing some sdk tools.
+ */
+ static final String TOOLS_FOLDER = IAndroidConstants.FD_TOOLS;
+
+ /**
+ * Folder located inside the SDK folder and containing the ADB.
+ */
+ static final String PLATFORM_TOOLS_FOLDER = IAndroidConstants.FD_PLATFORM_TOOLS;
+
+ /**
+ * adb (android debug bridge) command.
+ */
+ static final String ADB_COMMAND = "adb"; //$NON-NLS-1$
+
+ /**
+ * Command to concatenate with "adb" to have the device shell.
+ */
+ static final String SHELL_CMD = "shell"; //$NON-NLS-1$
+
+ /**
+ * Options to be used with adb to indicate run operation.
+ */
+ private static final String AM_CMD = "am"; //$NON-NLS-1$
+
+ /**
+ * Command to concatenate with "am" to have an activity executed at the device.
+ */
+ private static final String START_CMD = "start"; //$NON-NLS-1$
+
+ /**
+ * Parameter for running in debug mode.
+ */
+ private static final String ADB_AM_DEBUG = "-D"; //$NON-NLS-1$
+
+ /**
+ * Parameter provided before the application package/name.
+ */
+ private static final String ADB_AM_NAME = "-n"; //$NON-NLS-1$
+
+ /**
+ * Parameter for selecting emulator instance.
+ */
+ static final String ADB_INSTANCE_PARAMETER = DEVICE_ID_INDICATOR; //$NON-NLS-1$
+
+ /**
+ * Folder for the SDK.
+ */
+ private static final String SDCARD_FOLDER = "sdcard"; //$NON-NLS-1$
+
+ /**
+ * Folder for the SDK.
+ */
+ private static final String MNT_SDCARD_FOLDER = "mnt/sdcard"; //$NON-NLS-1$
+
+ /*
+ * TCP/IP
+ */
+ private static final String CONNECT_TCPIP_CMD = "connect"; //$NON-NLS-1$
+
+ private static final String DISCONNECT_TCPIP_CMD = "disconnect"; //$NON-NLS-1$
+
+ private static final String TCPIP_CMD = "tcpip"; //$NON-NLS-1$
+
+ private static final String IFCONFIG_CMD = "ifconfig"; //$NON-NLS-1$
+
+ static Object consoleLock = new Object();
+
+ private static Map<String, String> avdNameMap = new HashMap<String, String>();
+
+ /**
+ * Property from device which represents the wi-fi value to use ipconfig command.
+ */
+ private static final String WIFI_INTERFACE_DEVICE_PROPERTY = "wifi.interface"; //$NON-NLS-1$
+
+ // IP validation
+ private static final String ZERO_TO_255_PATTERN =
+ "((\\d)|(\\d\\d)|([0-1]\\d\\d)|(2[0-4]\\d)|(25[0-5]))"; //$NON-NLS-1$
+
+ private static final String IP_PATTERN = "(" + ZERO_TO_255_PATTERN + "\\." //$NON-NLS-1$ //$NON-NLS-2$
+ + ZERO_TO_255_PATTERN + "\\." + ZERO_TO_255_PATTERN + "\\." + ZERO_TO_255_PATTERN //$NON-NLS-1$ //$NON-NLS-2$
+ + ")+"; //$NON-NLS-1$
+
+ /**
+ * Must be called only once, during AndroidPlugin start-up.
+ * This method configures all necessary device listeners.
+ */
+ public static void setup()
+ {
+ AndroidPlugin.getDefault().addSDKLoaderListener(new Runnable()
+ {
+
+ public void run()
+ {
+ AndroidDebugBridge adb = AndroidDebugBridge.getBridge();
+ if (adb == null)
+ {
+ AndroidDebugBridge.disconnectBridge();
+ DdmsPlugin.setToolsLocation(AdtPlugin.getOsAbsoluteAdb(), true,
+ AdtPlugin.getOsAbsoluteHprofConv(), AdtPlugin.getOsAbsoluteTraceview());
+ }
+
+ if (adb != null)
+ {
+ IDevice[] x = adb.getDevices();
+ IDevice[] newDevices = x;
+ List<IDevice> oldDevList = new ArrayList<IDevice>(connectedDevices.values());
+
+ for (IDevice newDev : newDevices)
+ {
+ String serialNum = newDev.getSerialNumber();
+ if (connectedDevices.containsKey(serialNum))
+ {
+ IDevice oldDev = connectedDevices.get(serialNum);
+ oldDevList.remove(oldDev);
+ if (oldDev.getState().compareTo((newDev).getState()) != 0)
+ {
+ if ((newDev).getState() == DeviceState.OFFLINE)
+ {
+ deviceDisconnected(newDev);
+ }
+ else if ((newDev).getState() == DeviceState.ONLINE)
+ {
+ deviceConnected(newDev);
+ }
+ }
+ }
+ else
+ {
+ deviceConnected(newDev);
+ }
+ }
+
+ for (IDevice oldDev : oldDevList)
+ {
+ deviceDisconnected(oldDev);
+ }
+ }
+
+ }
+ });
+
+ // Adds listener for the HOME application. It adds the serial number of the
+ // device to a collection when it identifies that the HOME application has
+ // loaded
+ AndroidDebugBridge.addClientChangeListener(new IClientChangeListener()
+ {
+
+ public void clientChanged(Client client, int changeMask)
+ {
+ if ((changeMask & Client.CHANGE_NAME) == Client.CHANGE_NAME)
+ {
+ final Client finalClient = client;
+ Thread t = new Thread()
+ {
+
+ @Override
+ public void run()
+ {
+ String applicationName =
+ finalClient.getClientData().getClientDescription();
+ if (applicationName != null)
+ {
+ IPreferenceStore store =
+ AdtPlugin.getDefault().getPreferenceStore();
+ String home = store.getString(AdtPrefs.PREFS_HOME_PACKAGE);
+ if (home.equals(applicationName))
+ {
+ String serialNum = finalClient.getDevice().getSerialNumber();
+ synchronized (completelyUpDevices)
+ {
+ StudioLogger.debug("Completely Up Device: " + serialNum); //$NON-NLS-1$
+ completelyUpDevices.add(serialNum);
+ }
+ }
+ }
+ }
+ };
+ t.start();
+ }
+ }
+ });
+ }
+
+ static void deviceStatusChanged(IDevice device)
+ {
+ StudioLogger.debug("Device changed: " + device.getSerialNumber()); //$NON-NLS-1$
+ synchronized (connectedDevices)
+ {
+ connectedDevices.put(device.getSerialNumber(), device);
+ }
+ if ((device).getState() == DeviceState.ONLINE)
+ {
+ IPreferenceStore store = AdtPlugin.getDefault().getPreferenceStore();
+ String home = store.getString(AdtPrefs.PREFS_HOME_PACKAGE);
+ if (device.getClient(home) != null)
+ {
+ synchronized (completelyUpDevices)
+ {
+ StudioLogger.debug("Completely Up Device: " + device.getSerialNumber()); //$NON-NLS-1$
+ if (!completelyUpDevices.contains(device.getSerialNumber()))
+ {
+ completelyUpDevices.add(device.getSerialNumber());
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Registers a device as connected
+ *
+ * @param device
+ */
+ static void deviceConnected(IDevice device)
+ {
+ final String serialNumber = device.getSerialNumber();
+ StudioLogger.debug("Device connected: " + serialNumber); //$NON-NLS-1$
+ synchronized (connectedDevices)
+ {
+ connectedDevices.put(serialNumber, device);
+ }
+
+ if (!device.isEmulator() && !device.hasClients())
+ {
+ boolean timeout = false;
+ long startTime = System.currentTimeMillis();
+ int maxInterval = 10000;
+ do
+ {
+ try
+ {
+ Thread.sleep(250);
+ }
+ catch (InterruptedException e)
+ {
+ //do nothing
+ }
+ long currentTime = System.currentTimeMillis();
+ timeout = ((startTime + maxInterval) < currentTime);
+
+ }
+ while (!device.hasClients() && !timeout);
+ if (timeout)
+ {
+ synchronized (completelyUpDevices)
+ {
+ //put the device up anyway.
+ completelyUpDevices.add(serialNumber);
+ }
+ }
+ }
+
+ if (device.hasClients())
+ {
+ // When a device is connected, look for the HOME application and add
+ // the device serial number to a collection if it is already running.
+ IPreferenceStore store = AdtPlugin.getDefault().getPreferenceStore();
+ String home = store.getString(AdtPrefs.PREFS_HOME_PACKAGE);
+ if (device.getClient(home) != null)
+ {
+ StudioLogger.debug("Completely Up Device: " + serialNumber); //$NON-NLS-1$
+ synchronized (completelyUpDevices)
+ {
+ completelyUpDevices.add(serialNumber);
+ }
+ }
+ }
+
+ StudioAndroidEventManager.fireEvent(EventType.DEVICE_CONNECTED, serialNumber);
+ }
+
+ /**
+ * Unregisters a device as connected
+ *
+ * @param device
+ */
+ static void deviceDisconnected(IDevice device)
+ {
+ final String serialNumber = device.getSerialNumber();
+ StudioLogger.debug("Device disconnected: " + serialNumber); //$NON-NLS-1$
+ synchronized (completelyUpDevices)
+ {
+ completelyUpDevices.remove(serialNumber);
+ }
+ synchronized (connectedDevices)
+ {
+ connectedDevices.remove(serialNumber);
+ }
+ StudioAndroidEventManager.fireEvent(EventType.DEVICE_DISCONNECTED, serialNumber);
+ avdNameMap.remove(device.getSerialNumber());
+
+ }
+
+ /**
+ * Get all connected device serial numbers
+ *
+ * @return
+ */
+ public static Collection<String> getConnectedSerialNumbers()
+ {
+ return connectedDevices.keySet();
+ }
+
+ /**
+ * Get the Device associated with the given serial number
+ *
+ * @param serialNumber Serial number of the device to retrieve
+ * @return Device associated with the given serial number
+ */
+ public static IDevice getDeviceBySerialNumber(String serialNumber)
+ {
+ return connectedDevices.get(serialNumber);
+ }
+
+ /**
+ * Runs an activity at the given device
+ *
+ * @param serialNumber The serial number of the device to have the activity executed
+ * @param activityName The activity to execute
+ * @param debugMode Whether the activity shall be run in debug mode or not
+ * @param processOut The output stream of the process running "adb"
+ *
+ * @return An IStatus object with the result of the operation
+ */
+ public static IStatus runActivity(String serialNumber, String activityName, boolean debugMode,
+ OutputStream processOut)
+ {
+ IStatus status = Status.OK_STATUS;
+
+ // Return if no instance is selected
+ if (serialNumber == null)
+ {
+ StudioLogger.error("Abort run operation. Serial number is null."); //$NON-NLS-1$
+ return new Status(IStatus.ERROR, AndroidPlugin.PLUGIN_ID,
+ AndroidNLS.ERR_DDMSFacade_SerialNumberNullPointer);
+ }
+
+ // Return if instance is not started
+ if (!isDeviceOnline(serialNumber))
+ {
+ StudioLogger.error("Abort run operation. Device is not online."); //$NON-NLS-1$
+ return new Status(IStatus.ERROR, AndroidPlugin.PLUGIN_ID, ""); //$NON-NLS-1$
+ }
+
+ try
+ {
+ String[] cmd = createRunCommand(serialNumber, activityName, debugMode);
+ executeCommand(cmd, processOut);
+ }
+ catch (IOException e)
+ {
+ StudioLogger.error("Deploy: Could not execute adb install command."); //$NON-NLS-1$
+ status = new Status(IStatus.ERROR, AndroidPlugin.PLUGIN_ID, e.getMessage());
+ }
+
+ return status;
+ }
+
+ static String executeCommand(String[] cmd, OutputStream out) throws IOException
+ {
+ return executeCommand(cmd, out, null);
+ }
+
+ /**
+ * DOCUMENT ME!!
+ * @param cmd
+ * @param out
+ * @param serialNumber
+ * @return
+ * @throws IOException
+ */
+ static String executeCommand(String[] cmd, OutputStream out, String serialNumber)
+ throws IOException
+ {
+ String fullCmd = ""; //$NON-NLS-1$
+ if (out != null)
+ {
+ for (String cmdArg : cmd)
+ {
+ fullCmd += cmdArg + " "; //$NON-NLS-1$
+ }
+ out.write(fullCmd.getBytes());
+ out.write("\n".getBytes()); //$NON-NLS-1$
+ }
+
+ Runtime r = Runtime.getRuntime();
+ Process p = r.exec(cmd);
+
+ String command_results = ""; //$NON-NLS-1$
+ InputStream processIn = p.getInputStream();
+ final BufferedReader br = new BufferedReader(new InputStreamReader(processIn));
+ String line;
+ try
+ {
+ while ((line = br.readLine()) != null)
+ {
+ command_results += line;
+ command_results += "\n"; //$NON-NLS-1$
+ if (out != null)
+ {
+ if (serialNumber != null)
+ {
+ out.write((serialNumber + ": ").getBytes()); //$NON-NLS-1$
+ }
+ out.write(line.getBytes());
+ out.write("\n".getBytes()); //$NON-NLS-1$
+ }
+ }
+ }
+ finally
+ {
+ br.close();
+ }
+
+ return command_results;
+ }
+
+ /**
+ * See http://www.javaworld.com/javaworld/jw-12-2000/jw-1229-traps.html
+ * to understand how Process.exec works and its problems
+ *
+ * @param cmd Command to be executed.
+ * @param out Output Stream.
+ * @param timeout Timeout (secs.)
+ * @param monitor {@link IProgressMonitor}
+ *
+ * @return the {@link IStatus} of this process execution.
+ *
+ * @throws IOException Exception thrown in case there is any problem
+ * executing the command.
+ */
+ private static IStatus executeRemoteDevicesCommand(String[] cmd, OutputStream out, int timeout,
+ String timeoutMsg, IStopCondition stopCondition, IProgressMonitor monitor)
+ throws IOException
+ {
+
+ IStatus status = Status.OK_STATUS;
+
+ long timeoutLimit = -1;
+ if (timeout != 0)
+ {
+ timeoutLimit = System.currentTimeMillis() + (timeout * 1000);
+ }
+
+ String fullCmd = ""; //$NON-NLS-1$
+ for (String cmdArg : cmd)
+ {
+ fullCmd += cmdArg + " "; //$NON-NLS-1$
+ }
+ if (out != null)
+ {
+ out.write(fullCmd.getBytes());
+ out.write("\n".getBytes()); //$NON-NLS-1$
+ }
+
+ Runtime r = Runtime.getRuntime();
+ Process p = r.exec(cmd);
+
+ int errorCode = 0;
+
+ // inputStream / errorStream;
+ String[] commandResults = new String[]
+ {
+ "", "" //$NON-NLS-1$ //$NON-NLS-2$
+ };
+
+ commandResults =
+ readCmdOutputFromStreams(commandResults[0], commandResults[1], p.getInputStream(),
+ p.getErrorStream(), out);
+
+ while (!stopCondition.canStop())
+ {
+ if ((monitor != null) && (monitor.isCanceled()))
+ {
+ p.destroy();
+ return Status.CANCEL_STATUS;
+ }
+
+ try
+ {
+ errorCode = p.exitValue();
+ if (errorCode != 0)
+ {
+ break;
+ }
+
+ }
+ catch (IllegalThreadStateException e)
+ {
+ // Process is still running... Proceed with loop
+ }
+
+ try
+ {
+ Thread.sleep(1000);
+ }
+ catch (InterruptedException e)
+ {
+ StudioLogger.error("Execute command: thread has been interrupted"); //$NON-NLS-1$
+ }
+
+ if (timeout > 0)
+ {
+ try
+ {
+ testTimeout(timeoutLimit, ((timeoutMsg != null) ? timeoutMsg
+ : AndroidNLS.ERR_GenericTimeout));
+ }
+ catch (TimeoutException e)
+ {
+ p.destroy();
+ StudioLogger.debug("The timeout " + timeout //$NON-NLS-1$
+ + " has been reached when executing the command " + fullCmd); //$NON-NLS-1$
+ return new Status(IStatus.ERROR, AndroidPlugin.PLUGIN_ID, e.getMessage(), e);
+ }
+ }
+
+ }
+
+ commandResults =
+ readCmdOutputFromStreams(commandResults[0], commandResults[1], p.getInputStream(),
+ p.getErrorStream(), out);
+
+ if (errorCode != 0)
+ {
+ StudioLogger.debug("Command " + cmd + " returned an error code: " + errorCode); //$NON-NLS-1$ //$NON-NLS-2$
+ status =
+ new Status(IStatus.ERROR, AndroidPlugin.PLUGIN_ID, NLS.bind(
+ AndroidNLS.ERR_CommandError, errorCode) + "\n" //$NON-NLS-1$
+ + ((!commandResults[1].equals("")) ? commandResults[1] //$NON-NLS-1$
+ : commandResults[0]));
+ }
+ else
+ {
+ status = new Status(IStatus.OK, AndroidPlugin.PLUGIN_ID, commandResults[0]);
+ }
+
+ return status;
+ }
+
+ /**
+ * Defines a stop condition
+ */
+ interface IStopCondition
+ {
+ public boolean canStop();
+ }
+
+ /**
+ * @param commandResults
+ * @param errorResults
+ * @param inputStream
+ * @param errorStream
+ * @param out
+ */
+ private static String[] readCmdOutputFromStreams(String commandResults, String errorResults,
+ InputStream inputStream, InputStream errorStream, OutputStream out)
+ {
+ String[] results = new String[2];
+ String line = ""; //$NON-NLS-1$
+
+ BufferedReader brInput = new BufferedReader(new InputStreamReader(inputStream));
+ BufferedReader brError = new BufferedReader(new InputStreamReader(errorStream));
+
+ try
+ {
+
+ // input stream
+ if (brInput.ready())
+ {
+ while ((line = brInput.readLine()) != null)
+ {
+ commandResults += line;
+ commandResults += "\n"; //$NON-NLS-1$
+ if (out != null)
+ {
+ out.write(line.getBytes());
+ out.write("\n".getBytes()); //$NON-NLS-1$
+ }
+ }
+ }
+
+ // error stream
+ if (brError.ready())
+ {
+ while ((line = brError.readLine()) != null)
+ {
+ errorResults += "\n"; //$NON-NLS-1$
+ if (out != null)
+ {
+ out.write(line.getBytes());
+ out.write("\n".getBytes()); //$NON-NLS-1$
+ }
+ }
+ }
+ }
+ catch (IOException e)
+ {
+ StudioLogger.error("Cannot read command outputs"); //$NON-NLS-1$
+ }
+ finally
+ {
+ try
+ {
+ brInput.close();
+ brError.close();
+ }
+ catch (IOException e)
+ {
+ StudioLogger.error("Could not close console stream: " + e.getMessage());
+ }
+ }
+
+ results[0] = commandResults;
+ results[1] = errorResults;
+
+ return results;
+
+ }
+
+ /**
+ * Checks if the timeout limit has reached.
+ *
+ * @param timeoutLimit The system time limit that cannot be overtaken, in milliseconds.
+ * @throws StartTimeoutException When the system time limit is overtaken.
+ */
+ private static void testTimeout(long timeoutLimit, String timeoutErrorMessage)
+ throws TimeoutException
+ {
+ if (System.currentTimeMillis() > timeoutLimit)
+ {
+ throw new TimeoutException(timeoutErrorMessage);
+ }
+ }
+
+ /**
+ * Creates a string with the command that should
+ * be called in order to run the application.
+ */
+ private static String[] createRunCommand(String serialNumber, String activityName,
+ boolean debugMode)
+ {
+ String cmd[];
+ String sdkPath = SdkUtils.getSdkPath();
+
+ // The tools folder should exist and be here, but double-cheking
+ // once more wont kill
+ File f = new File(sdkPath + PLATFORM_TOOLS_FOLDER + File.separator);
+ if (!f.exists())
+ {
+ StudioLogger
+ .error("Run: Could not find tools folder on " + sdkPath + PLATFORM_TOOLS_FOLDER //$NON-NLS-1$
+ + File.separator);
+ }
+ else
+ {
+ if (!f.isDirectory())
+ {
+ StudioLogger.error("Run: Invalid tools folder " + sdkPath + PLATFORM_TOOLS_FOLDER //$NON-NLS-1$
+ + File.separator);
+ }
+ }
+
+ String completeAppPath =
+ activityName.substring(0, activityName.lastIndexOf(".")) + "/" + activityName; //$NON-NLS-1$ //$NON-NLS-2$
+ if (debugMode)
+ {
+ // If debugMode option is checked, create command with the -D paramater
+ String cmdTemp[] =
+ {
+ sdkPath + PLATFORM_TOOLS_FOLDER + File.separator + ADB_COMMAND,
+ ADB_INSTANCE_PARAMETER, serialNumber, SHELL_CMD, AM_CMD, START_CMD,
+ ADB_AM_DEBUG, ADB_AM_NAME, completeAppPath
+ };
+ cmd = cmdTemp;
+ }
+ else
+ {
+ // If debugMode option is unchecked, create command without the -D paramater
+ String cmdTemp[] =
+ {
+ sdkPath + PLATFORM_TOOLS_FOLDER + File.separator + ADB_COMMAND,
+ ADB_INSTANCE_PARAMETER, serialNumber, SHELL_CMD, AM_CMD, START_CMD,
+ ADB_AM_NAME, completeAppPath
+ };
+ cmd = cmdTemp;
+ }
+
+ return cmd;
+ }
+
+ /**
+ * Check if the device is Online (i.e. if it's possible to communicate with it)
+ * Notice it is a verification of the status of the Device which may be different than the status of the Tml Instance.
+ *
+ * @param serialNumber
+ * @return true if the Device is online, false otherwise
+ */
+ public static boolean isDeviceOnline(String serialNumber)
+ {
+ IDevice device = getDeviceBySerialNumber(serialNumber);
+ if ((device == null) || !device.isOnline())
+ {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Return true if the Device is being shown on the OFFLINE state.
+ *
+ * @param serialNumber Device´s serial number.
+ *
+ * @return <code>true</code> in case the Device if offline,
+ * <code>false</code> otherwise.
+ */
+ public static boolean isDeviceOffline(String serialNumber)
+ {
+
+ IDevice device = getDeviceBySerialNumber(serialNumber);
+ return ((device == null) || ((device != null) && device.isOffline()));
+ }
+
+ /**
+ * Check if the device is completely loaded
+ * A device is completely loaded when it loads the HOME application
+ *
+ * @param serialNumber
+ * @return true if the Device has completely loaded; false otherwise
+ */
+ public static boolean isDeviceCompletelyLoaded(String serialNumber)
+ {
+ return completelyUpDevices.contains(serialNumber);
+ }
+
+ /**
+ * Tests if the device represented by the serial number (if it exists) is an emulator
+ *
+ * @param serialNumber
+ * @return true if it is an emulator, false if not or non existent
+ */
+ public static boolean isEmulator(String serialNumber)
+ {
+ IDevice device = getDeviceBySerialNumber(serialNumber);
+ if ((device != null) && device.isEmulator())
+ {
+ return true;
+ }
+ return false;
+ }
+
+ public static boolean isRemote(String serialNumber)
+ {
+ // firstly, test if the serial number has the format "anything:digits"
+ Pattern p = Pattern.compile("(.)+:(\\d)+"); //$NON-NLS-1$
+ Matcher m = p.matcher(serialNumber);
+ if (m.matches())
+ {
+ IDevice device = getDeviceBySerialNumber(serialNumber);
+ if ((device != null) && !device.isEmulator())
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Execute an app in the Device
+ *
+ * @param serialNumber Serial number of the device where to execute the command
+ * @param remoteCommand command to be executed remotely on the Device
+ * @param monitor monitor associated with the operation
+ *
+ * @return The lines read from the command output
+ *
+ * @throws IOException
+ */
+ public static Collection<String> execRemoteApp(String serialNumber, String remoteCommand,
+ final IProgressMonitor monitor) throws IOException
+ {
+ return executeShellCmd(serialNumber, remoteCommand, monitor);
+ }
+
+ /**
+ * Execute an app in the Device
+ *
+ * @param serialNumber Serial number of the device where to execute the command
+ * @param remoteCommands commands to be executed remotely on the Device
+ * @param monitor monitor associated with the operation
+ *
+ * @throws IOException
+ */
+ public static Map<String, Collection<String>> execRemoteApp(String serialNumber,
+ Collection<String> remoteCommands, final IProgressMonitor monitor) throws IOException
+ {
+ Map<String, Collection<String>> cmdAnswers =
+ new LinkedHashMap<String, Collection<String>>();
+ for (String remoteCommand : remoteCommands)
+ {
+ StudioLogger.debug(remoteCommand);
+ Collection<String> answers = executeShellCmd(serialNumber, remoteCommand, monitor);
+ cmdAnswers.put(remoteCommand, answers);
+ }
+
+ return cmdAnswers;
+ }
+
+ private static Collection<String> executeShellCmd(String serialNumber, final String cmd,
+ final IProgressMonitor monitor)
+ {
+ final Collection<String> results = new ArrayList<String>();
+ IDevice d = getDeviceBySerialNumber(serialNumber);
+ if (d != null)
+ {
+ try
+ {
+ d.executeShellCommand(cmd, new MultiLineReceiver()
+ {
+ public boolean isCancelled()
+ {
+ return monitor.isCanceled();
+ }
+
+ @Override
+ public void processNewLines(String[] lines)
+ {
+ for (String line : lines)
+ {
+ if ((!line.equals("")) && (!line.equals(cmd))) //$NON-NLS-1$
+ {
+ results.add(line);
+ }
+ }
+ }
+ }, 0);
+ }
+ catch (Exception e)
+ {
+ StudioLogger.error(DDMSFacade.class, "Error executing shell command " + cmd //$NON-NLS-1$
+ + " at device " + serialNumber, e); //$NON-NLS-1$
+ }
+ }
+ return results;
+ }
+
+ /**
+ * Retrieves all properties from the device with provided serial number.
+ * @param serialNumber
+ * @return
+ */
+ public static Properties getDeviceProperties(String serialNumber)
+ {
+ Properties instanceProperties = new Properties();
+ if (serialNumber != null)
+ {
+ String key = ""; //$NON-NLS-1$
+ String value = ""; //$NON-NLS-1$
+ Collection<String> lines;
+ try
+ {
+ lines = execRemoteApp(serialNumber, "getprop", new NullProgressMonitor()); //$NON-NLS-1$
+
+ for (String line : lines)
+ {
+ String[] split = line.split("]:"); //$NON-NLS-1$
+ if (split.length >= 2)
+ {
+ if (!split[0].equals("")) //$NON-NLS-1$
+ {
+ key = split[0].substring(1, split[0].length());
+ if (!split[1].equals("")) //$NON-NLS-1$
+ {
+ value = split[1].substring(2, split[1].length() - 1);
+ instanceProperties.setProperty(key, value);
+ }
+ }
+
+ }
+ }
+ }
+ catch (IOException e)
+ {
+ StudioLogger.error("IOException while executing an app on device. "
+ + e.getMessage());
+ }
+ }
+ return instanceProperties;
+ }
+
+ /**
+ * Retrieves a given property from the device with provided serial number.
+ *
+ * @param serialNumber
+ * @param propertyName
+ *
+ * @return
+ */
+ public static String getDeviceProperty(String serialNumber, String propertyName)
+ {
+ String result = null;
+ IDevice device = DDMSFacade.getDeviceBySerialNumber(serialNumber);
+ if (device != null)
+ {
+ result = device.getProperty(propertyName);
+ }
+ return result;
+ }
+
+ /**
+ * Get the name of the VM associated to the emulator running in the given deviceSerial identification.
+ *
+ * @param deviceSerial identification of the emulator whose vm name must be retrieved.
+ * @return the name of the VM used by the emulator running with the given id,
+ * or null if the vmname could be retrieved.
+ */
+ public static String getVmName(final IDevice d)
+ {
+ String vmName = null;
+ String serialNumber = d.getSerialNumber();
+ int MAX_TRIES = 120;
+ int tries = 0;
+
+ while ((vmName == null) && (tries < MAX_TRIES))
+ {
+ synchronized (avdNameMap)
+ {
+ vmName = avdNameMap.get(serialNumber);
+ }
+
+ if (vmName == null)
+ {
+ vmName = d.getAvdName();
+ }
+
+ if (vmName == null)
+ {
+ try
+ {
+ Thread.sleep(1000);
+ }
+ catch (InterruptedException e)
+ {
+ //do nothing
+ }
+ finally
+ {
+ tries++;
+ }
+ }
+
+ //try to get vmname by telnet as last option
+ if (vmName == null)
+ {
+ vmName = getVmNameByTelnet(serialNumber);
+ }
+
+ }
+
+ if (vmName != null)
+ {
+ synchronized (avdNameMap)
+ {
+ if (avdNameMap.get(serialNumber) == null)
+ {
+ avdNameMap.put(serialNumber, vmName);
+ }
+ }
+
+ }
+
+ return vmName;
+ }
+
+ private static String getVmNameByTelnet(String serialNumber)
+ {
+ Pattern pattern = Pattern.compile("emulator-([0-9]+)"); //$NON-NLS-1$
+ TelnetFrameworkAndroid telnet = new TelnetFrameworkAndroid();
+ Matcher matcher = pattern.matcher(serialNumber);
+ matcher.matches();
+ String avdName = null;
+
+ try
+ {
+ Integer telnetPort = Integer.valueOf(matcher.group(1));
+ telnet.connect("localhost", telnetPort); //$NON-NLS-1$
+ String avdNameRaw = telnet.write("avd name\r\n", new String[] //$NON-NLS-1$
+ {
+ "KO" //$NON-NLS-1$
+ });
+
+ String split = avdNameRaw.contains("\r\n") ? "\r\n" : "\n";
+
+ String[] outputArray = avdNameRaw.split(split); //$NON-NLS-1$
+ if (outputArray.length > 2)
+ {
+ avdName = outputArray[2];
+ }
+
+ if (avdName != null)
+ {
+ avdNameMap.put(serialNumber, avdName);
+ }
+ }
+ catch (NumberFormatException e)
+ {
+ avdName = serialNumber;
+ }
+ catch (IOException e)
+ {
+ avdName = serialNumber;
+ }
+ finally
+ {
+ try
+ {
+ telnet.disconnect();
+ }
+ catch (IOException e)
+ {
+ //Do nothing.
+ }
+ }
+ return avdName;
+ }
+
+ /**
+ * Get the Device associated with the Android VM
+ *
+ * @param vmName Android VM name
+ * @return Device associated with the given Android VM
+ */
+ public static IDevice getDeviceWithVmName(String vmName)
+ {
+ IDevice toReturn = null;
+ if (vmName != null)
+ {
+ Collection<IDevice> devices = connectedDevices.values();
+ for (IDevice d : devices)
+ {
+ if (d.isEmulator())
+ {
+ String deviceVmName = DDMSFacade.getVmName(d);
+ if (vmName.equals(deviceVmName))
+ {
+ toReturn = d;
+ break;
+ }
+ }
+ }
+ }
+ return toReturn;
+ }
+
+ /**
+ * Securely get the name of the AVD associated to the given device.
+ *
+ * @param serialNumber The serialNumber of the device that we want the AVD name for
+ * @return the name of the AVD used by the emulator running with the given device,
+ * or null if the vmname could be retrieved.
+ */
+ public static String getNameBySerialNumber(String serialNumber)
+ {
+ String avdName = null;
+ IDevice d = getDeviceBySerialNumber(serialNumber);
+ avdName = getNameByDevice(d);
+
+ return avdName;
+ }
+
+ /**
+ * Securely get the serial number of the given instance.
+ *
+ * @param instanceName The name of the instance we want the serial number of
+ * @return the serial number of the given instance, or <code>null</code> if no instance with the
+ * given name is online
+ */
+ public static String getSerialNumberByName(String instanceName)
+ {
+ String serialNumber = null;
+ if (instanceName != null)
+ {
+ List<IDevice> devices = null;
+ synchronized (connectedDevices)
+ {
+ devices = new ArrayList<IDevice>(connectedDevices.size());
+ devices.addAll(connectedDevices.values());
+ }
+ if (devices != null)
+ {
+ for (IDevice dev : devices)
+ {
+ if (instanceName.equals(getNameByDevice(dev)))
+ {
+ serialNumber = dev.getSerialNumber();
+ break;
+ }
+ }
+ }
+ }
+
+ return serialNumber;
+ }
+
+ public static Collection<String> getRunningApplications(String serialNumber)
+ {
+ Collection<String> apps = new ArrayList<String>();
+ if (serialNumber != null)
+ {
+ IDevice dev = getDeviceBySerialNumber(serialNumber);
+ if (dev != null)
+ {
+ Client[] clients = dev.getClients();
+ if ((clients != null) && (clients.length > 0))
+ {
+ for (Client c : clients)
+ {
+ apps.add(c.getClientData().getClientDescription());
+ }
+ }
+ }
+ }
+
+ return apps;
+ }
+
+ /**
+ * Dumps a HPROF file based on a client description and a device serial number
+ * @param clientDescription A client description of a running application
+ */
+ public static IStatus dumpHprofFile(String clientDescription, String serialNumber,
+ IProgressMonitor monitor)
+ {
+ IStatus status = Status.OK_STATUS;
+ monitor.beginTask(AndroidNLS.DumpHprofFile_GeneratingMemoryAnalysisOutput, 100);
+
+ // Retrive running apps
+ monitor.setTaskName(AndroidNLS.DumpHprofFile_GettingRunningApplications);
+ IDevice dev = getDeviceBySerialNumber(serialNumber);
+ Client[] clients = dev.getClients();
+ monitor.worked(25);
+
+ // Store the shell
+ final Shell[] shell = new Shell[1];
+
+ PlatformUI.getWorkbench().getDisplay().syncExec(new Runnable()
+ {
+ public void run()
+ {
+
+ shell[0] = new Shell(PlatformUI.getWorkbench().getDisplay());
+
+ }
+ });
+
+ MotodevHProfDumpHandler hprofHandler = new MotodevHProfDumpHandler(shell[0], monitor);
+ monitor.setTaskName(AndroidNLS.DumpHprofFile_SettingApplicationToAnalyse);
+ hprofHandler.setSelectedApp(clientDescription);
+ monitor.worked(25);
+
+ try
+ {
+ // Find a client with matching description and dum the HPROF file
+ for (Client c : clients)
+ {
+ if (c.getClientData().getClientDescription().equals(clientDescription))
+ {
+ // Set our handler as the HprofDumpHandler
+ ClientData.setHprofDumpHandler(hprofHandler);
+ monitor.setTaskName(AndroidNLS.DumpHprofFile_DumpingHprofFile);
+ c.dumpHprof();
+ synchronized (DDMSFacade.class)
+ {
+ DDMSFacade.class.wait();
+ }
+
+ monitor.worked(50);
+ }
+ }
+ }
+ catch (Exception e)
+ {
+ // Status not ok
+ status = Status.CANCEL_STATUS;
+ }
+ finally
+ {
+ monitor.done();
+ }
+
+ return status;
+
+ }
+
+ /**
+ * Gets the AVD name of the device
+ *
+ * @param d The device to be searched for the AVD name
+ *
+ * @return The AVD name
+ */
+ private static String getNameByDevice(final IDevice d)
+ {
+ String name = null;
+ if (d != null)
+ {
+ if (d.isEmulator())
+ {
+
+ name = getVmName(d);
+ }
+ else
+ {
+ name = d.getSerialNumber();
+ }
+ }
+
+ return name;
+ }
+
+ /**
+ * Create port forward for a given VM
+ *
+ * @param serialNumber Android serial number
+ * @param from port number from
+ * @param to port number to
+ * @return true is the port forward was successful, false otherwise
+ */
+ public static boolean createForward(String serialNumber, int from, int to)
+ {
+ boolean ok = true;
+ IDevice device = getDeviceBySerialNumber(serialNumber);
+ try
+ {
+ device.createForward(from, to);
+ }
+ catch (Exception e)
+ {
+ StudioLogger.error(DDMSFacade.class, "Error creating forward of device: " //$NON-NLS-1$
+ + serialNumber + " from " + from + " to " + to, e); //$NON-NLS-1$ //$NON-NLS-2$
+ ok = false;
+ }
+ return ok;
+ }
+
+ /**
+ * Kill the communication channel
+ *
+ * @param serialNumber The serial number of the device to kill
+ */
+ public static void kill(String serialNumber)
+ {
+ if (isDeviceOnline(serialNumber))
+ {
+ IDevice deviceToKill = getDeviceBySerialNumber(serialNumber);
+ if (deviceToKill != null)
+ {
+ synchronized (consoleLock)
+ {
+ EmulatorConsole console = EmulatorConsole.getConsole(deviceToKill);
+ if (console != null)
+ {
+ console.kill();
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Push files to device
+ *
+ * @param serialNumber Android device serial number
+ * @param localDir local folder path
+ * @param fileNames files to transfer
+ * @param remoteDir destination folder path
+ * @param timeout timeout for the operation
+ * @param monitor monitor associated with the operation
+ */
+ public static IStatus pushFiles(String serialNumber, String localDir,
+ Collection<String> fileNames, String remoteDir, int timeout,
+ final IProgressMonitor monitor, OutputStream outputStream)
+ {
+ return transferFiles(true, serialNumber, localDir, fileNames, remoteDir, timeout, monitor,
+ outputStream);
+ }
+
+ /**
+ * Push files to device
+ *
+ * @param serialNumber Android device serial number
+ * @param localFiles destination local files
+ * @param remoteFiles remote files to transfer as localFiles to desktop
+ * @param timeout timeout for the operation
+ * @param monitor monitor associated with the operation
+ */
+ public static IStatus pushFiles(String serialNumber, List<File> localFiles,
+ List<String> remoteFiles, int timeout, final IProgressMonitor monitor,
+ OutputStream outputStream)
+ {
+ return transferFiles(true, serialNumber, localFiles, remoteFiles, timeout, monitor,
+ outputStream);
+ }
+
+ /**
+ * Pull files from device
+ *
+ * @param serialNumber Android device serial number
+ * @param localDir local folder path
+ * @param fileNames files to transfer
+ * @param remoteDir destination folder path
+ * @param timeout timeout for the operation
+ * @param monitor monitor associated with the operation
+ */
+ public static IStatus pullFiles(String serialNumber, String localDir,
+ Collection<String> fileNames, String remoteDir, int timeout,
+ final IProgressMonitor monitor, OutputStream outputStream)
+ {
+ return transferFiles(false, serialNumber, localDir, fileNames, remoteDir, timeout, monitor,
+ outputStream);
+ }
+
+ /**
+ * Pull files from device
+ *
+ * @param serialNumber Android device serial number
+ * @param localFiles local files to transfer as remoteFiles to device
+ * @param remoteFiles destination remote files
+ * @param timeout timeout for the operation
+ * @param monitor monitor associated with the operation
+ */
+ public static IStatus pullFiles(String serialNumber, List<File> localFiles,
+ List<String> remoteFiles, int timeout, final IProgressMonitor monitor,
+ OutputStream outputStream)
+ {
+ return transferFiles(false, serialNumber, localFiles, remoteFiles, timeout, monitor,
+ outputStream);
+ }
+
+ /**
+ * Get the service used to transfer files to the Device
+ *
+ * @param device Device
+ * @param timeout timeout for the operation
+ * @param monitor monitor associated with the operation
+ * @return The service used to transfer files to the Device
+ *
+ * @throws AndroidException
+ */
+ private static SyncService getSyncService(IDevice device, int timeout,
+ final IProgressMonitor monitor) throws AndroidException
+ {
+
+ SyncService service = null;
+ long timeoutLimit = System.currentTimeMillis() + timeout;
+ do
+ {
+ if ((device != null) && (device.isOnline()))
+ {
+ try
+ {
+ service = device.getSyncService();
+ }
+ catch (IOException e)
+ {
+ StudioLogger.debug("Couldn't get sync service; cause: " + e.getMessage()); //$NON-NLS-1$
+ }
+ catch (com.android.ddmlib.TimeoutException e)
+ {
+ StudioLogger.debug("Couldn't get sync service; cause: " + e.getMessage()); //$NON-NLS-1$
+ }
+ catch (AdbCommandRejectedException e)
+ {
+ StudioLogger.debug("Couldn't get sync service; cause: " + e.getMessage()); //$NON-NLS-1$
+ }
+ }
+
+ try
+ {
+ Thread.sleep(100);
+ }
+ catch (InterruptedException e)
+ {
+ //do nothing
+ }
+
+ if (monitor.isCanceled())
+ {
+ StudioLogger.info("Operation canceled by the user"); //$NON-NLS-1$
+ return null;
+ }
+
+ if (System.currentTimeMillis() > timeoutLimit)
+ {
+ StudioLogger.error("The emulator was not up within the set timeout"); //$NON-NLS-1$
+ throw new AndroidException(
+ "Timeout while preparing to transfer files to the Device. " + device); //$NON-NLS-1$
+ }
+ }
+ while (service == null);
+
+ return service;
+ }
+
+ private static IStatus transferFiles(boolean isPush, String serialNumber, String localDir,
+ Collection<String> fileNames, String remoteDir, int timeout,
+ final IProgressMonitor monitor, OutputStream outputStream)
+ {
+ List<File> localList = new ArrayList<File>();
+ List<String> remoteList = new ArrayList<String>();
+ for (String name : fileNames)
+ {
+ localList.add(new File(localDir, name));
+ remoteList.add(remoteDir + "/" + name); //$NON-NLS-1$
+ }
+ return transferFiles(isPush, serialNumber, localList, remoteList, timeout, monitor,
+ outputStream);
+ }
+
+ private static IStatus transferFiles(boolean isPush, String serialNumber,
+ List<File> localFiles, List<String> remoteFiles, int timeout,
+ final IProgressMonitor monitor, OutputStream outputStream)
+ {
+ if (localFiles.size() != remoteFiles.size())
+ {
+ return new Status(IStatus.ERROR, AndroidPlugin.PLUGIN_ID,
+ AndroidNLS.ERR_DDMSFacade_IncompatibleFileLists);
+ }
+
+ IStatus status = Status.OK_STATUS;
+ IDevice device = DDMSFacade.getDeviceBySerialNumber(serialNumber);
+
+ SyncService service = null;
+ try
+ {
+ service = getSyncService(device, timeout, monitor);
+ if (service == null)
+ {
+ status = Status.CANCEL_STATUS;
+ }
+ else
+ {
+ final ISyncProgressMonitor syncMonitor = new ISyncProgressMonitor()
+ {
+ public void start(int i)
+ {
+ //do nothing
+ }
+
+ public void stop()
+ {
+ //do nothing
+ }
+
+ public void advance(int i)
+ {
+ //do nothing
+ }
+
+ public boolean isCanceled()
+ {
+ return monitor.isCanceled();
+ }
+
+ public void startSubTask(String s)
+ {
+ //do nothing
+ }
+ };
+
+ FileListingService flService = device.getFileListingService();
+
+ for (int i = 0; i < localFiles.size(); i++)
+ {
+ File localFile = localFiles.get(i);
+ String remotePath = remoteFiles.get(i);
+ String absLocalFile = localFile.getAbsolutePath();
+
+ String resultMessage = null;
+ if (isPush)
+ {
+ StudioLogger.debug("Push " + absLocalFile + " to " + remotePath); //$NON-NLS-1$ //$NON-NLS-2$
+ try
+ {
+ service.pushFile(absLocalFile, remotePath, syncMonitor);
+ }
+ catch (SyncException e1)
+ {
+ StudioLogger
+ .debug("Push result: " + "SyncException occured " + e1.getMessage()); //$NON-NLS-1$ //$NON-NLS-2$
+ resultMessage =
+ NLS.bind(AndroidNLS.CON_ConsolePush, absLocalFile, remotePath)
+ + ": " + e1.getLocalizedMessage(); //$NON-NLS-1$
+ }
+ catch (FileNotFoundException e1)
+ {
+ StudioLogger.debug("Push result: " + "FileNotFoundException occured " //$NON-NLS-1$ //$NON-NLS-2$
+ + e1.getMessage());
+ resultMessage =
+ NLS.bind(AndroidNLS.CON_ConsolePush, absLocalFile, remotePath)
+ + ": " + e1.getLocalizedMessage(); //$NON-NLS-1$
+ }
+ catch (IOException e1)
+ {
+ StudioLogger
+ .debug("Push result: " + "IOException occured " + e1.getMessage()); //$NON-NLS-1$ //$NON-NLS-2$
+ resultMessage =
+ NLS.bind(AndroidNLS.CON_ConsolePush, absLocalFile, remotePath)
+ + ": " + e1.getLocalizedMessage(); //$NON-NLS-1$
+ }
+ catch (com.android.ddmlib.TimeoutException e1)
+ {
+ StudioLogger
+ .debug("Push result: " + "TimeoutException occured " + e1.getMessage()); //$NON-NLS-1$ //$NON-NLS-2$
+ resultMessage =
+ NLS.bind(AndroidNLS.CON_ConsolePush, absLocalFile, remotePath)
+ + ": " + e1.getLocalizedMessage(); //$NON-NLS-1$
+ }
+
+ if ((outputStream != null) && (resultMessage != null))
+ {
+ try
+ {
+ outputStream.write(resultMessage.getBytes());
+ outputStream.write('\n');
+ outputStream.flush();
+ }
+ catch (Exception e)
+ {
+ // Do nothing
+ }
+ }
+
+ }
+ else
+ {
+ FileEntry f1 = null;
+ FileEntry f2 = null;
+
+ f2 = flService.getRoot();
+ flService.getChildren(f2, false, null);
+ String[] dirs = remotePath.split("/"); //$NON-NLS-1$
+
+ for (int j = 1; j < (dirs.length - 1); j++)
+ {
+ f1 = f2.findChild(dirs[j]);
+ flService.getChildren(f1, false, null);
+ f2 = f1;
+ }
+
+ final FileEntry fileToPull = f2.findChild(dirs[dirs.length - 1]);
+
+ if (fileToPull != null)
+ {
+ try
+ {
+ service.pullFile(fileToPull, absLocalFile, syncMonitor);
+ }
+ catch (FileNotFoundException e)
+ {
+ resultMessage = e.getLocalizedMessage();
+ StudioLogger.debug("Pull result: " + e.getMessage()); //$NON-NLS-1$
+ }
+ catch (SyncException e)
+ {
+ resultMessage = e.getLocalizedMessage();
+ StudioLogger.debug("Pull result: " + e.getMessage()); //$NON-NLS-1$
+ }
+ catch (IOException e)
+ {
+ resultMessage = e.getLocalizedMessage();
+ StudioLogger.debug("Pull result: " + e.getMessage()); //$NON-NLS-1$
+ }
+ catch (com.android.ddmlib.TimeoutException e)
+ {
+ resultMessage = e.getLocalizedMessage();
+ StudioLogger.debug("Pull result: " + e.getMessage()); //$NON-NLS-1$
+ }
+
+ if ((outputStream != null) && (resultMessage != null))
+ {
+ String message =
+ NLS.bind(AndroidNLS.CON_ConsolePull,
+ fileToPull.getFullPath(), absLocalFile)
+ + ": " + resultMessage; //$NON-NLS-1$
+ try
+ {
+ outputStream.write(message.getBytes());
+ outputStream.write('\n');
+ outputStream.flush();
+ }
+ catch (IOException e)
+ {
+ //do nothing
+ }
+ }
+ }
+ else
+ {
+ resultMessage =
+ NLS.bind(AndroidNLS.DDMSFacade_Remote_File_Not_Found,
+ remotePath);
+ StudioLogger.debug("Pull result: File not found " + remotePath); //$NON-NLS-1$
+ }
+ }
+
+ if (resultMessage != null)
+ {
+ status = new Status(IStatus.ERROR, AndroidPlugin.PLUGIN_ID, resultMessage);
+ }
+ if (syncMonitor.isCanceled())
+ {
+ status = Status.CANCEL_STATUS;
+ break;
+ }
+ }
+ }
+ }
+ catch (AndroidException e)
+ {
+ status = new Status(IStatus.ERROR, AndroidPlugin.PLUGIN_ID, e.getMessage());
+ }
+ catch (NullPointerException e1)
+ {
+ status =
+ new Status(IStatus.ERROR, AndroidPlugin.PLUGIN_ID,
+ AndroidNLS.ERR_DDMSFacade_FileNotFound);
+ }
+ finally
+ {
+ if (service != null)
+ {
+ service.close();
+ }
+ }
+
+ return status;
+ }
+
+ /**
+ * Check if the application is running in the device with specified serial number
+ * @param serialNumber
+ * @param applicationName
+ * @return true if it is running, false otherwise
+ */
+ public static boolean isApplicationRunning(String serialNumber, String applicationName)
+ {
+ IDevice dev = null;
+ boolean running = false;
+ dev = connectedDevices.get(serialNumber);
+ if (dev != null)
+ {
+ running = dev.getClient(applicationName) != null;
+ }
+ return running;
+ }
+
+ /**
+ * Connect to a Remote Device given its IP/Port
+ *
+ * @param device the Remote Device Instance
+ * @param host device host (IP)
+ * @param port device port
+ * @param timeout the maximum time allowed to successfully connect to the device
+ * @param monitor the monitor of the operation
+ * @return the status of the operation
+ * @throws IOException
+ */
+ public static IStatus connectTcpIp(final ISerialNumbered device, String host, String port,
+ int timeout, IProgressMonitor monitor) throws IOException
+ {
+ SubMonitor subMonitor = SubMonitor.convert(monitor, 1000);
+
+ subMonitor.beginTask(AndroidNLS.DDMSFacade_MsgConnectingToDeviceViaTCPIP, 10);
+
+ IStatus status = Status.OK_STATUS;
+
+ String serialNumber = device.getSerialNumber();
+ if (!isDeviceOnline(serialNumber)) // check if it's already connected
+ {
+ String[] cmd = createConnectTcpIpCommand(host, port);
+
+ status =
+ executeRemoteDevicesCommand(
+ cmd,
+ null,
+ timeout,
+ NLS.bind(AndroidNLS.ERR_RemoteDevice_TimeoutWhileConnecting,
+ device.getDeviceName()), new IStopCondition()
+ {
+
+ public boolean canStop()
+ {
+ String serialNumber = device.getSerialNumber();
+ if (serialNumber != null)
+ {
+ return isDeviceOnline(serialNumber);
+ }
+ else
+ {
+ return false;
+ }
+ }
+ }, subMonitor.newChild(1000));
+
+ }
+
+ subMonitor.worked(1000);
+
+ return status;
+ }
+
+ /**
+ * Method which switches the device connection mode from TCP/IP
+ * to USB.
+ *
+ * @param device {@link ISerialNumbered} device to have its connection mode changed.
+ * @param host The IP of the device.
+ * @param port The port in which the TCP/IP connection is established.
+ * @param timeout The maximum time which the switching operation is attempted.
+ * @param monitor The {@link IProgressMonitor} which this operation is being computed.
+ *
+ * @return Returns the {@link IStatus} of this operation.
+ *
+ * @throws IOException Exception thrown in case something goes wrong while trying
+ * to switch the device connection mode from TCP/IP to USB.
+ */
+ public static IStatus switchFromTCPConnectionModeToUSBConnectionMode(
+ final ISerialNumbered device, String host, String port, int timeout,
+ IProgressMonitor monitor) throws IOException
+ {
+ SubMonitor subMonitor = SubMonitor.convert(monitor, 1000);
+
+ subMonitor.beginTask(AndroidNLS.DDMSFacade_MsgSwitchingDeviceFromTCPIPToUSB, 10);
+
+ IStatus status = Status.OK_STATUS;
+
+ String serialNumber = device.getSerialNumber();
+ if (isDeviceOnline(serialNumber)) // check if it's already connected
+ {
+ String[] cmd = createSwitchToUSBConnectionModeCommand(host, port);
+
+ status =
+ executeRemoteDevicesCommand(cmd, null, timeout, NLS.bind(
+ AndroidNLS.DDMSFacade_MsgTimeoutReachedSwitchingFromTCPToUSB,
+ device.getDeviceName()), new IStopCondition()
+ {
+
+ public boolean canStop()
+ {
+ String serialNumber = device.getSerialNumber();
+ if (serialNumber != null)
+ {
+ return isDeviceOnline(serialNumber);
+ }
+ else
+ {
+ return false;
+ }
+ }
+ }, subMonitor.newChild(1000));
+
+ }
+
+ subMonitor.worked(1000);
+
+ if (status.isOK())
+ {
+ StudioLogger.collectUsageData(StudioLogger.WHAT_REMOTE_USB,
+ StudioLogger.KIND_REMOTE_DEVICE, StudioLogger.DESCRIPTION_DEFAULT,
+ AndroidPlugin.PLUGIN_ID, AndroidPlugin.getDefault().getBundle().getVersion()
+ .toString());
+ }
+
+ return status;
+ }
+
+ /**
+ * Get the wireless ip from the connected handset
+ * @param serialNumber
+ * @param monitor
+ * @return the ip or null if not possible to retrieve it
+ */
+ public static String getWirelessIPfromHandset(String serialNumber, IProgressMonitor monitor)
+ {
+ String handset_wireless_ip = null;
+ IDevice device = null;
+
+ device = connectedDevices.get(serialNumber);
+ if (device != null)
+ {
+ // get the wi-fi name for executing the ipconfig command
+ String wifiProperty = device.getProperty(WIFI_INTERFACE_DEVICE_PROPERTY);
+ if (wifiProperty == null)
+ {
+ wifiProperty = DEFAULT_WIRELESS_DEVICE_PROPERTY;
+ }
+ // execute ipconfig command
+ Collection<String> answers =
+ executeShellCmd(serialNumber, IFCONFIG_CMD + " " + wifiProperty, monitor); //$NON-NLS-1$
+
+ // Success message - for example
+ // [tiwlan0: ip 192.168.0.174 mask 255.255.255.0 flags [up broadcast running multicast]]
+
+ if (answers != null)
+ {
+ String result = answers.toString();
+ if (result != null)
+ {
+ // splits the result of the shell command and gets the third position
+ // that should be the IP number
+ String[] result_splited = result.split(" "); //$NON-NLS-1$
+ if (result_splited.length >= 3)
+ {
+ // check whether there is an IP
+ Pattern pattern = Pattern.compile(IP_PATTERN);
+ Matcher matcher = pattern.matcher(result);
+ if (matcher.find())
+ {
+ handset_wireless_ip = result_splited[2];
+ }
+ }
+ }
+ }
+ }
+ return handset_wireless_ip;
+ }
+
+ /**
+ * Switch adb connection mode of an specific device to TCPIP
+ *
+ * @param deviceName name of the handset instance
+ * @param host wireless ip of the handset instance
+ * @param port number of the port to be using during the connection
+ * @param timeout the maximum time allowed to successfully connect to the device
+ * @param monitor the monitor of the operation
+ * @return the status of the operation
+ *
+ * @throws IOException Exception thrown in case there are problems switching the device.
+ */
+ public static IStatus switchUSBtoTcpIp(String deviceName, final String serialNumber,
+ String port, int timeout, IProgressMonitor monitor) throws IOException
+ {
+ SubMonitor subMonitor = SubMonitor.convert(monitor, 1000);
+
+ subMonitor.beginTask(AndroidNLS.DDMSFacade_MsgSwitchingFromUSBConnection, 10);
+
+ IStatus status = Status.OK_STATUS;
+
+ if (isDeviceOnline(serialNumber))
+ {
+ String[] cmd = createSwitchToTcpIpCommand(serialNumber, port);
+
+ status =
+ executeRemoteDevicesCommand(cmd, null, timeout,
+ NLS.bind(AndroidNLS.ERR_WirelessRemoteDevice_TimeoutWhileConnecting,
+ deviceName), new IStopCondition()
+ {
+
+ public boolean canStop()
+ {
+ if (serialNumber != null)
+ {
+ return isDeviceOffline(serialNumber);
+ }
+ else
+ {
+ return false;
+ }
+ }
+ }, subMonitor.newChild(1000));
+
+ }
+
+ monitor.worked(1000);
+
+ return status;
+ }
+
+ /**
+ * Disconnect from a Remote Device given its IP/Port
+ *
+ * @param device the Remote Device Instance
+ * @param host device host (IP)
+ * @param port device port
+ * @param timeout the maximum time allowed to successfully disconnect from the device
+ * @param monitor the monitor of the operation
+ * @return the status of the operation
+ * @throws IOException
+ */
+ public static IStatus disconnectTcpIp(final ISerialNumbered device, String host, String port,
+ int timeout, IProgressMonitor monitor) throws IOException
+ {
+ IStatus status = Status.OK_STATUS;
+
+ String serialNumber = device.getSerialNumber();
+ if (isDeviceOnline(serialNumber)) // check if it's already disconnected
+ {
+ String[] cmd = createDisconnectTcpIpCommand(host, port);
+
+ status =
+ executeRemoteDevicesCommand(
+ cmd,
+ null,
+ timeout,
+ NLS.bind(AndroidNLS.ERR_RemoteDevice_TimeoutWhileDisconnecting,
+ device.getDeviceName()), new IStopCondition()
+ {
+
+ public boolean canStop()
+ {
+ String serialNumber = device.getSerialNumber();
+ return !isDeviceOnline(serialNumber);
+ }
+ }, monitor);
+
+ }
+
+ return status;
+ }
+
+ /**
+ * Creates a string with the command that should
+ * be called in order to connect to an IP/Port
+ *
+ * @param host device host (IP)
+ * @param port device port
+ * @return the command to be used to connect to an IP/Port
+ */
+ private static String[] createConnectTcpIpCommand(String host, String port)
+ {
+
+ String hostPort = host + ":" + port; //$NON-NLS-1$
+
+ String sdkPath = SdkUtils.getSdkPath();
+
+ String cmd[] =
+ {
+ sdkPath + PLATFORM_TOOLS_FOLDER + File.separator + ADB_COMMAND,
+ CONNECT_TCPIP_CMD, hostPort
+ };
+
+ return cmd;
+ }
+
+ /**
+ * Creates a string with the command switches
+ * a device from the TCP/IP connection mode
+ * to the USB connection mode.
+ *
+ * @param host Device host (IP).
+ * @param port Device port.
+ *
+ * @return The command to be used to switch back to USB connection mode.
+ */
+ private static String[] createSwitchToUSBConnectionModeCommand(String host, String port)
+ {
+
+ String hostPort = host + ":" + port; //$NON-NLS-1$
+
+ String sdkPath = SdkUtils.getSdkPath();
+
+ String cmd[] =
+ {
+ sdkPath + PLATFORM_TOOLS_FOLDER + File.separator + ADB_COMMAND,
+ DEVICE_ID_INDICATOR, hostPort, USB_SWITCH_BACK_COMMAND
+ };
+
+ return cmd;
+ }
+
+ /**
+ * Creates a string with the command that should
+ * be called in order to switch adb connection from USB to TPCIP mode
+ *
+ * @param serialNumber device serial number
+ * @param port device port
+ * @return the command to be used to switch adb connection to TCPIP mode
+ */
+ private static String[] createSwitchToTcpIpCommand(String serialNumber, String port)
+ {
+ String sdkPath = SdkUtils.getSdkPath();
+
+ String cmd[] =
+ {
+ sdkPath + PLATFORM_TOOLS_FOLDER + File.separator + ADB_COMMAND,
+ ADB_INSTANCE_PARAMETER, serialNumber, TCPIP_CMD, port
+ };
+
+ return cmd;
+ }
+
+ /**
+ * Creates a string with the command that should
+ * be called to delete a file from device
+ *
+ * @param serialNumber
+ * @param file
+ * @param folder
+ * @return
+ */
+ private static String[] createDeleteFileFromDeviceCommand(String serialNumber, String file,
+ String folder)
+ {
+ String sdkPath = SdkUtils.getSdkPath();
+
+ // The tools folder should exist and be here, but double-cheking
+ // once more wont kill
+ File f = new File(sdkPath + PLATFORM_TOOLS_FOLDER + File.separator);
+ if (!f.exists())
+ {
+ StudioLogger
+ .error("Run: Could not find tools folder on " + sdkPath + PLATFORM_TOOLS_FOLDER //$NON-NLS-1$
+ + File.separator);
+ }
+ else
+ {
+ if (!f.isDirectory())
+ {
+ StudioLogger.error("Run: Invalid tools folder " + sdkPath + PLATFORM_TOOLS_FOLDER //$NON-NLS-1$
+ + File.separator);
+ }
+ }
+
+ String cmd[] =
+ {
+ sdkPath + PLATFORM_TOOLS_FOLDER + File.separator + ADB_COMMAND,
+ ADB_INSTANCE_PARAMETER, serialNumber, SHELL_CMD,
+ "rm /" + folder + "/" + file //$NON-NLS-1$ //$NON-NLS-2$
+ };
+
+ return cmd;
+ }
+
+ /**
+ * Uses the ADB shell command to remove a file from the device
+ *
+ * @param serialNumber
+ * @param fileName
+ * @param sdCardFolder
+ * @return
+ * @throws IOException
+ */
+ private static boolean deleteFileFromDevice(String serialNumber, String fileName, String folder)
+ throws IOException
+ {
+
+ String command[] = createDeleteFileFromDeviceCommand(serialNumber, fileName, folder);
+ IStatus status = executeRemoteDevicesCommand(command, null, 1000, "", new IStopCondition() //$NON-NLS-1$
+ {
+
+ public boolean canStop()
+ {
+ return true;
+ }
+ }, null);
+
+ return status.isOK();
+ }
+
+ /**
+ * Check if a device identified by the serial number has a mounted SDCard
+ * @param serialNumber the serial number
+ * @return true if the device has a SDCard
+ * @throws IOException
+ */
+ public static boolean hasSDCard(String serialNumber) throws IOException
+ {
+ boolean hasSdCard = false;
+ File tempSdCardFile = File.createTempFile("SDcheck", ".tmp"); //$NON-NLS-1$ //$NON-NLS-2$
+ boolean tempCopiedOnSdCardFile =
+ pushFileToDevice(serialNumber, SDCARD_FOLDER, tempSdCardFile);
+
+ if (tempCopiedOnSdCardFile)
+ {
+ //trying to write on /sdcard folder (it works for phones previous from Platform 2.2)
+ if (!deleteFileFromDevice(serialNumber, tempSdCardFile.getName(), SDCARD_FOLDER))
+ {
+ StudioLogger
+ .error("DDMSFacade: Could not delete tempfile from /sdcard when checking if card is enabled"); //$NON-NLS-1$
+ }
+ hasSdCard = true;
+ tempSdCardFile.delete();
+ }
+ else
+ {
+
+ File tempMntFile = File.createTempFile("SDcheck", ".tmp"); //$NON-NLS-1$ //$NON-NLS-2$
+ boolean tempCopiedOnMntFile =
+ pushFileToDevice(serialNumber, MNT_SDCARD_FOLDER, tempSdCardFile);
+
+ if (tempCopiedOnMntFile)
+ {
+ //trying to write on /mnt/sdcard folder (it works for phones since Platform 2.2)
+ if (!deleteFileFromDevice(serialNumber, tempMntFile.getName(), MNT_SDCARD_FOLDER))
+ {
+ StudioLogger
+ .error("DDMSFacade: Could not delete tempfile from /mnt/sdcard when checking if card is enabled"); //$NON-NLS-1$
+ }
+ hasSdCard = true;
+ tempMntFile.delete();
+ }
+
+ }
+
+ return hasSdCard;
+ }
+
+ /**
+ *
+ * @param serialNumber
+ * @param sdCardFolder
+ * @param tempFile
+ * @return true if manages to push file into the folder specified on device
+ */
+ private static boolean pushFileToDevice(String serialNumber, String folder, File file)
+ {
+ Collection<String> files = new ArrayList<String>();
+ files.add(file.getName());
+ Path path = new Path(file.getAbsolutePath());
+
+ IStatus status =
+ pushFiles(serialNumber, path.removeLastSegments(1).toString(), files, folder, 2000,
+ new NullProgressMonitor(), null);
+
+ return status.isOK();
+ }
+
+ /**
+ * Creates a string with the command that should
+ * be called in order to disconnect from an IP/Port
+ *
+ * @param host device host (IP)
+ * @param port device port
+ * @return the command to be used to disconnect from an IP/Port
+ */
+ private static String[] createDisconnectTcpIpCommand(String host, String port)
+ {
+
+ String hostPort = host + ":" + port; //$NON-NLS-1$
+
+ String sdkPath = SdkUtils.getSdkPath();
+ String cmd[] =
+ {
+ sdkPath + PLATFORM_TOOLS_FOLDER + File.separator + ADB_COMMAND,
+ DISCONNECT_TCPIP_CMD, hostPort
+ };
+
+ return cmd;
+ }
+
+ public static void deleteFile(String serialNumber, String path) throws IOException
+ {
+ DDMSFacade.execRemoteApp(serialNumber, "rm " + path, //$NON-NLS-1$
+ new NullProgressMonitor());
+ }
+}
diff --git a/src/plugins/android/src/com/motorola/studio/android/adt/DDMSUtils.java b/src/plugins/android/src/com/motorola/studio/android/adt/DDMSUtils.java
new file mode 100644
index 0000000..d98b079
--- /dev/null
+++ b/src/plugins/android/src/com/motorola/studio/android/adt/DDMSUtils.java
@@ -0,0 +1,1442 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.motorola.studio.android.adt;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.MultiStatus;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.debug.core.DebugPlugin;
+import org.eclipse.debug.core.ILaunchConfiguration;
+import org.eclipse.debug.core.ILaunchConfigurationType;
+import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
+import org.eclipse.debug.core.ILaunchManager;
+import org.eclipse.debug.ui.DebugUITools;
+import org.eclipse.debug.ui.ILaunchGroup;
+import org.eclipse.jface.dialogs.IDialogConstants;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.StructuredSelection;
+import org.eclipse.jface.wizard.WizardDialog;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.console.IOConsoleOutputStream;
+
+import com.android.ddmlib.FileListingService;
+import com.android.ddmlib.FileListingService.FileEntry;
+import com.android.ddmlib.IDevice;
+import com.android.ddmuilib.ScreenShotDialog;
+import com.android.sdklib.IAndroidTarget;
+import com.motorola.studio.android.AndroidPlugin;
+import com.motorola.studio.android.adt.StudioAndroidEventManager.EventType;
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.common.log.UsageDataConstants;
+import com.motorola.studio.android.common.utilities.EclipseUtils;
+import com.motorola.studio.android.common.utilities.FileUtil;
+import com.motorola.studio.android.i18n.AndroidNLS;
+import com.motorola.studio.android.wizards.installapp.DeployWizard;
+import com.motorola.studio.android.wizards.installapp.DeployWizard.INSTALL_TYPE;
+import com.motorola.studio.android.wizards.installapp.UninstallAppWizard;
+import com.motorola.studio.android.wizards.mat.DumpHPROFWizard;
+import com.motorola.studio.android.wizards.monkey.IMonkeyConfigurationConstants;
+
+public class DDMSUtils
+{
+ private static final Map<String, FileListingService> deviceFileListingServiceMap =
+ new HashMap<String, FileListingService>();
+
+ /**
+ * The APK extension
+ */
+ private static final String APK_FILE_EXTENSION = "apk";
+
+ /**
+ * Parameter for overwriting existing applications, if any
+ */
+ private static final String ADB_INSTALL_OVERWRITE = "-r";
+
+ /**
+ * Options to be used with adb to indicate package manager application
+ */
+ private static final String PM_CMD = "pm";
+
+ /**
+ * Options to be used with adb to run monkey application
+ */
+ private static final String MONKEY_CMD = "monkey";
+
+ /**
+ * Uninstall directive to the package manager
+ */
+ private static final String PM_UNINSTALL_DIRECTIVE = "uninstall";
+
+ /**
+ * List packages directive
+ */
+ private static final String PM_LIST_DIRECTIVE = "list";
+
+ /**
+ * List packages directive
+ */
+ private static final String PM_PACKAGES_DIRECTIVE = "packages";
+
+ /**
+ * List packages force directive
+ */
+ private static final String PM_PACKAGES_DIRECTIVE_FORCE = "-f";
+
+ /**
+ * Monkey packages directive
+ */
+ private static final String MONKEY_PACKAGES_DIRECTIVE = " -p ";
+
+ /**
+ * Options to be used with adb to indicate install operation
+ */
+ private static final String INSTALL_CMD = "install";
+
+ /**
+ * This word must exist in the adb install commmand answer to indicate
+ * succefull installation
+ */
+ private static final String SUCCESS_CONSTANT = "Success";
+
+ private static final DdmsRunnable disconnectedListener = new DdmsRunnable()
+ {
+ public void run(String serialNumber)
+ {
+ synchronized (deviceFileListingServiceMap)
+ {
+ deviceFileListingServiceMap.remove(serialNumber);
+ }
+ }
+ };
+
+ static
+ {
+ StudioAndroidEventManager.asyncAddDeviceChangeListeners(null, disconnectedListener);
+ }
+
+ public static void takeScreenshot(final String serialNumber)
+ {
+ Display.getDefault().asyncExec(new Runnable()
+ {
+
+ public void run()
+ {
+ Shell shell = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell();
+ ScreenShotDialog sshot = new ScreenShotDialog(new Shell(shell));
+ sshot.open(DDMSFacade.getDeviceBySerialNumber(serialNumber));
+ }
+ });
+ }
+
+ public static void getApplicationDatabases(String serialNumber, String applicationName,
+ IDatabaseListingListener listener)
+ {
+ LinkedList<String> pathSegments = new LinkedList<String>();
+ pathSegments.add("data");
+ pathSegments.add("data");
+ pathSegments.add(applicationName);
+ pathSegments.add("databases");
+
+ FileListingService fileListing = getFileListingService(serialNumber);
+
+ if (fileListing != null)
+ {
+ FileEntry root = fileListing.getRoot();
+ FileEntry[] children = null;
+
+ children =
+ fileListing.getChildren(root, true, new FileListingReceiver(pathSegments,
+ fileListing, listener));
+ /*
+ * If children isn't null means that file listing service found the
+ * files in it cache to speed up the operation
+ */
+ if (children != null)
+ {
+ List<String> databases = new ArrayList<String>();
+ FileEntry temp1 = null, temp2 = root;
+ // start from root
+ while ((children != null))
+ {
+ // if we have something to search for
+ if (pathSegments.size() > 0)
+ {
+ String theSegment = pathSegments.remove(0);
+ temp1 = temp2.findChild(theSegment);
+
+ if (temp1 != null)
+ {
+ temp2 = temp1;
+ children =
+ fileListing.getChildren(temp2, true, new FileListingReceiver(
+ pathSegments, fileListing, listener));
+ }
+ else
+ {
+ children = null;
+ listener.databasesFound(databases);
+ }
+ }
+ // else just notify the listener
+ else
+ {
+ if (children != null)
+ {
+ for (FileEntry child : children)
+ {
+ if (child.getName().endsWith(".db"))
+ {
+ databases.add(child.getName());
+ }
+ }
+ children = null;
+ }
+ listener.databasesFound(databases);
+ }
+ }
+ }
+ }
+ }
+
+ public static List<String> getApplicationDatabases(String serialNumber, String applicationName)
+ throws IOException
+ {
+ List<String> dbs = new ArrayList<String>();
+
+ String appDbPath = "/data/data/" + applicationName + "/databases/";
+
+ Collection<String> commandOutput =
+ DDMSFacade
+ .execRemoteApp(serialNumber, "ls " + appDbPath, new NullProgressMonitor());
+ List<String> dbPathCandidates = new ArrayList(commandOutput.size() + 10);
+
+ for (String commandOutline : commandOutput)
+ {
+ String[] strings = commandOutline.split(" ");
+ for (String string : strings)
+ {
+ if (string.trim().endsWith(".db"))
+ {
+ dbPathCandidates.add(string);
+ }
+ }
+ }
+
+ return dbPathCandidates;
+ }
+
+ /**
+ * @param serialNumber
+ * @return
+ */
+ private static FileListingService getFileListingService(String serialNumber)
+ {
+ FileListingService fileListing = null;
+ IDevice dev = DDMSFacade.getDeviceBySerialNumber(serialNumber);
+ if (dev != null)
+ {
+ synchronized (dev)
+ {
+ fileListing = deviceFileListingServiceMap.get(serialNumber);
+ }
+ if (fileListing == null)
+ {
+ fileListing = dev.getFileListingService();
+ synchronized (deviceFileListingServiceMap)
+ {
+ deviceFileListingServiceMap.put(serialNumber, fileListing);
+ }
+
+ }
+ }
+ return fileListing;
+ }
+
+ /**
+ * This method returns the current language and country in use by given
+ * emulation instance.
+ *
+ * @param serialNumber The serial number of emulation instance
+ * @return An array of Strings containing the command results.
+ */
+ public static String[] getCurrentEmulatorLanguageAndCountry(final String serialNumber)
+ {
+ ArrayList<String[]> commands = createCurrentEmulatorLanguageAndCountryCommand(serialNumber);
+ String[] responses = new String[2];
+ String[] languageCommand = commands.get(0);
+ String[] countryCommand = commands.get(1);
+ String languageCommandResult = null;
+ String countryCommandResult = null;
+
+ try
+ {
+ // Do not write the command output to the console
+ languageCommandResult = DDMSFacade.executeCommand(languageCommand, null);
+ countryCommandResult = DDMSFacade.executeCommand(countryCommand, null);
+ responses[0] = languageCommandResult.replaceAll("\\n$", "");
+ responses[1] = countryCommandResult.replaceAll("\\n$", "");
+ }
+ catch (IOException e)
+ {
+ StudioLogger.error("Deploy: Could not execute adb current language command.");
+ }
+ return responses;
+ }
+
+ public static InstallPackageBean installPackageWizard()
+ {
+
+ final InstallPackageBean bean = new InstallPackageBean();
+
+ final Display display = PlatformUI.getWorkbench().getDisplay();
+ display.syncExec(new Runnable()
+ {
+ public void run()
+ {
+ try
+ {
+ String defaultPath = null;
+ DeployWizard wizard;
+ IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
+ if (window != null)
+ {
+ ISelection selection = window.getSelectionService().getSelection();
+ if (selection instanceof IStructuredSelection)
+ {
+ IStructuredSelection workbenchSSelection =
+ (IStructuredSelection) selection;
+ for (Object o : workbenchSSelection.toList())
+ {
+ if (o instanceof IFile)
+ {
+ IFile file = (IFile) o;
+ if (file.getFileExtension()
+ .equalsIgnoreCase(APK_FILE_EXTENSION))
+ {
+ defaultPath = file.getLocation().toOSString();
+ }
+ }
+ }
+ }
+ }
+ wizard = new DeployWizard(defaultPath);
+ wizard.init(PlatformUI.getWorkbench(), new StructuredSelection());
+ WizardDialog dialog =
+ new WizardDialog(PlatformUI.getWorkbench().getActiveWorkbenchWindow()
+ .getShell(), wizard);
+ dialog.setPageSize(500, 200);
+ if (dialog.open() == IDialogConstants.OK_ID)
+ {
+ bean.setPackagePath(wizard.getPackagePath());
+ bean.setCanOverwrite(wizard.canOverwrite());
+ }
+ }
+ catch (Throwable e)
+ {
+ StudioLogger.error(DDMSFacade.class, "Error executing deploy wizard", e);
+ }
+ }
+ });
+
+ return bean;
+ }
+
+ public static IStatus installPackage(final String serialNumber, InstallPackageBean bean)
+ {
+ IStatus status = Status.CANCEL_STATUS;
+
+ if ((bean.getPackagePath() != null) && (bean.getCanOverwrite() != null))
+ {
+ OutputStream consoleOut = null;
+ try
+ {
+ consoleOut = EclipseUtils.getStudioConsoleOutputStream(true);
+ status =
+ installPackage(serialNumber, bean.getPackagePath(), bean.getCanOverwrite(),
+ consoleOut);
+ }
+ finally
+ {
+ try
+ {
+ if (consoleOut != null)
+ {
+ consoleOut.close();
+ }
+ }
+ catch (IOException e)
+ {
+ StudioLogger.error("Install App: could not close console stream"
+ + e.getMessage());
+ }
+ }
+ }
+
+ if (status.isOK())
+ {
+ StudioAndroidEventManager.fireEvent(EventType.PACKAGE_INSTALLED, serialNumber);
+ }
+
+ return status;
+ }
+
+ /**
+ * Install an application on an emulator instance
+ *
+ * @param serialNumber
+ * The serial number of the device where the application will be
+ * installed
+ * @param path
+ * Path of the package containing the application to be installed
+ * @param installationType
+ * one of the {@link INSTALL_TYPE} install types available
+ * @param force
+ * Perform the operation without asking for user intervention
+ *
+ * @return the status of the operation (OK, Cancel or Error+ErrorMessage)
+ */
+ public static IStatus installPackage(String serialNumber, String path,
+ INSTALL_TYPE installationType, OutputStream processOut)
+ {
+ IStatus status = null;
+
+ if (installationType.equals(INSTALL_TYPE.UNINSTALL))
+ {
+ status = uninstallPackage(new File(path), serialNumber, processOut);
+ }
+
+ boolean overwrite = installationType.equals(INSTALL_TYPE.OVERWRITE);
+ status = installPackage(serialNumber, path, overwrite, processOut);
+
+ return status;
+ }
+
+ /**
+ * Uninstall the given package (if installed) in the given serialNumber
+ * device
+ *
+ * @param path
+ * the package path
+ * @param serialNumber
+ * the device serial number
+ */
+ public static IStatus uninstallPackage(File path, String serialNumber, OutputStream processOut)
+ {
+ IStatus returnStatus = null;
+ if ((path != null) && path.exists() && path.isFile())
+ {
+ IDevice dev = DDMSFacade.getDeviceBySerialNumber(serialNumber);
+ String apiLevel = dev.getProperty("ro.build.version.sdk");
+ IAndroidTarget target = SdkUtils.getTargetByAPILevel(Integer.parseInt(apiLevel));
+ String aaptPath = SdkUtils.getTargetAAPTPath(target);
+ if (aaptPath != null)
+ {
+
+ // resolve package name
+ String[] aaptComm = new String[]
+ {
+ aaptPath, "d", "badging", path.toString()
+ };
+
+ InputStreamReader reader = null;
+ BufferedReader bufferedReader = null;
+
+ try
+ {
+ Process aapt = Runtime.getRuntime().exec(aaptComm);
+
+ reader = new InputStreamReader(aapt.getInputStream());
+ bufferedReader = new BufferedReader(reader);
+ String infoLine = bufferedReader.readLine();
+
+ infoLine = infoLine.split(" ")[1].split("=")[1].replaceAll("'", "");
+ returnStatus = uninstallPackage(serialNumber, infoLine, processOut);
+
+ }
+ catch (Exception e)
+ {
+ returnStatus =
+ new Status(IStatus.ERROR, AndroidPlugin.PLUGIN_ID,
+ AndroidNLS.ERR_DDMSFacade_UninstallPackageException, e);
+ }
+ finally
+ {
+ try
+ {
+ if (reader != null)
+ {
+ reader.close();
+ }
+ if (bufferedReader != null)
+ {
+ bufferedReader.close();
+ }
+ }
+ catch (IOException e)
+ {
+ StudioLogger.error("Uninstall app could not close stream. "
+ + e.getMessage());
+ }
+
+ }
+ }
+ else
+ {
+ StudioLogger
+ .error(DDMSFacade.class,
+ "Impossible to check APK package name. No android targets found inside SDK");
+ }
+
+ }
+ else
+ {
+ returnStatus =
+ new Status(IStatus.ERROR, AndroidPlugin.PLUGIN_ID,
+ AndroidNLS.ERR_DDMSFacade_UninstallPackage);
+ }
+ return returnStatus;
+ }
+
+ /**
+ * Uninstall the given package within device with given serial number
+ *
+ * @param serialNumber
+ * @param packageName
+ * @param processOutput
+ * outputStream to write output of the process
+ */
+ public static IStatus uninstallPackage(String serialNumber, String packageName,
+ OutputStream processOutput)
+ {
+ IStatus status = Status.OK_STATUS;
+ String command[] = createUninstallCommand(serialNumber, packageName);
+
+ try
+ {
+ String commandResult = DDMSFacade.executeCommand(command, processOutput);
+ if (!commandResult.toLowerCase().contains(SUCCESS_CONSTANT.toLowerCase()))
+ {
+ status =
+ new Status(IStatus.ERROR, AndroidPlugin.PLUGIN_ID,
+ AndroidNLS.ERR_DDMSFacade_UninstallPackageError + ": "
+ + packageName);
+ }
+
+ }
+ catch (Exception e)
+ {
+ status =
+ new Status(IStatus.ERROR, AndroidPlugin.PLUGIN_ID,
+ AndroidNLS.ERR_DDMSFacade_UninstallPackageException, e);
+ StudioLogger.error(DDMSFacade.class, "Failed to remove package: " + packageName + ". "
+ + e.getMessage());
+ }
+ return status;
+ }
+
+ /**
+ * Run the Monkey command for the given package within device with given serial number
+ *
+ * @param serialNumber
+ * @param packageName
+ * @param processOutput
+ * @param otherCmd
+ * @return the status of the monkey process
+ */
+ public static IStatus runMonkey(String serialNumber, String allPackages,
+ OutputStream processOutput, String otherCmd)
+ {
+ IStatus status = Status.OK_STATUS;
+ String command[] = createMonkeyCommand(serialNumber, allPackages, otherCmd);
+
+ try
+ {
+ DDMSFacade.executeCommand(command, processOutput);
+
+ }
+ catch (Exception e)
+ {
+ EclipseUtils.showErrorDialog(AndroidNLS.UI_MonkeyError_Title,
+ AndroidNLS.UI_MonkeyError_Msg);
+ StudioLogger.error(DDMSFacade.class, "Failed to run monkey command: " + command + " "
+ + e.getMessage());
+ }
+ return status;
+ }
+
+ /**
+ * Uninstall packages from the given serialNumber device
+ *
+ * @param serialNumber
+ * @param packagesToUninstall
+ * @param outputStream
+ * @return the status of the uninstall process or null if no packages were
+ * uninstalled
+ */
+ private static IStatus uninstallPackages(String serialNumber,
+ ArrayList<String> packagesToUninstall, OutputStream outputStream)
+ {
+
+ IStatus returnStatus = null;
+ for (String packageToUninstall : packagesToUninstall)
+ {
+ StudioLogger.info(DDMSUtils.class, "Removing package: " + packageToUninstall);
+ IStatus temp = uninstallPackage(serialNumber, packageToUninstall, outputStream);
+ if (!temp.isOK())
+ {
+ if (returnStatus == null)
+ {
+ returnStatus =
+ new MultiStatus(AndroidPlugin.PLUGIN_ID, 0,
+ AndroidNLS.ERR_DDMSFacade_UninstallPackageError, null);
+ }
+
+ ((MultiStatus) returnStatus).add(temp);
+ }
+ }
+ return returnStatus == null ? Status.OK_STATUS : returnStatus;
+ }
+
+ /**
+ * Run monkey command on the given packages with the specified commands.
+ *
+ * @param serialNumber
+ * @param packagesToRunMonkey
+ * @param outputStream
+ * @param otherCmds
+ * @return the status of the monkey process or null if the process was not successful
+ */
+ private static IStatus runMonkey(String serialNumber, ArrayList<String> packagesToRunMonkey,
+ OutputStream outputStream, String otherCmds)
+ {
+
+ IStatus returnStatus = null;
+ String allPackages = "";
+ for (String packageToRunMonkey : packagesToRunMonkey)
+ {
+ allPackages += MONKEY_PACKAGES_DIRECTIVE + packageToRunMonkey;
+ }
+ StudioLogger.info(DDMSUtils.class, "Running monkey for: " + allPackages);
+ IStatus temp = runMonkey(serialNumber, allPackages, outputStream, otherCmds);
+ if (!temp.isOK())
+ {
+ if (returnStatus == null)
+ {
+ returnStatus =
+ new MultiStatus(AndroidPlugin.PLUGIN_ID, 0,
+ AndroidNLS.ERR_DDMSFacade_MonkeyError, null);
+ }
+
+ ((MultiStatus) returnStatus).add(temp);
+ }
+ return returnStatus == null ? Status.OK_STATUS : returnStatus;
+ }
+
+ /**
+ * Uninstall packages from the given device. A Wizard will be opened asking
+ * the user which packages to uninstall
+ *
+ * @param serialNumber
+ * @return the status of the operation
+ */
+ public static IStatus uninstallPackage(final String serialNumber)
+ {
+ final ArrayList<String> packagesToUninstall = new ArrayList<String>();
+ IStatus status = null;
+ //wrap the dialog within a final variable
+ final UninstallAppWizard[] wizard = new UninstallAppWizard[1];
+
+ // do package listing async
+ Thread listingThread = new Thread("listPackages")
+ {
+
+ @Override
+ public void run()
+ {
+ Map<String, String> installedPackages = null;
+ try
+ {
+ installedPackages = listInstalledPackages(serialNumber);
+ }
+ catch (IOException e1)
+ {
+ installedPackages = new HashMap<String, String>(0);
+ }
+
+ while (wizard[0] == null)
+ {
+ try
+ {
+ sleep(100);
+ }
+ catch (InterruptedException e)
+ {
+ //do nothing
+ }
+ }
+ wizard[0].setAvailablePackages(installedPackages);
+ };
+ };
+
+ listingThread.start();
+
+ PlatformUI.getWorkbench().getDisplay().syncExec(new Runnable()
+ {
+ public void run()
+ {
+ UninstallAppWizard unAppWiz = new UninstallAppWizard();
+ WizardDialog dialog =
+ new WizardDialog(new Shell(PlatformUI.getWorkbench()
+ .getActiveWorkbenchWindow().getShell()), unAppWiz);
+ wizard[0] = unAppWiz;
+ dialog.open();
+ List<String> selectedPackages = wizard[0].getSelectedPackages();
+ if (selectedPackages != null)
+ {
+ for (String aPackage : selectedPackages)
+ {
+ packagesToUninstall.add(aPackage);
+ }
+ }
+ }
+ });
+
+ if (packagesToUninstall.size() > 0)
+ {
+ OutputStream consoleOut = null;
+
+ try
+ {
+ consoleOut = EclipseUtils.getStudioConsoleOutputStream(true);
+ status = uninstallPackages(serialNumber, packagesToUninstall, consoleOut);
+ }
+ finally
+ {
+ try
+ {
+ if (consoleOut != null)
+ {
+ consoleOut.close();
+ }
+ }
+ catch (IOException e)
+ {
+ StudioLogger.error("Uninstall App: could not close console stream"
+ + e.getMessage());
+ }
+ }
+ }
+ if (status != null)
+ {
+ if (status.isOK())
+ {
+ Display.getDefault().asyncExec(new Runnable()
+ {
+ public void run()
+ {
+ MessageDialog.openInformation(PlatformUI.getWorkbench()
+ .getActiveWorkbenchWindow().getShell(),
+ AndroidNLS.UI_UninstallApp_SucessDialogTitle,
+ AndroidNLS.UI_UninstallApp_Message);
+ }
+ });
+ StudioAndroidEventManager.fireEvent(EventType.PACKAGE_UNINSTALLED, serialNumber);
+ }
+ else
+ {
+ EclipseUtils.showErrorDialog(AndroidNLS.UI_UninstallApp_ERRDialogTitle,
+ AndroidNLS.UI_UninstallApp_ERRUninstallApp, status);
+ }
+ }
+ // all return is successful since operations are async
+ return Status.OK_STATUS;
+
+ }
+
+ /**
+ * If there is no Monkey Launch configuration for the given deviceName, it is created.
+ * @param deviceName
+ */
+ private static ILaunchConfiguration createLaunchConfiguration(String deviceName)
+ {
+ ILaunchConfiguration config = null;
+ ILaunchManager launchManager = DebugPlugin.getDefault().getLaunchManager();
+ ILaunchConfigurationType motodevLaunchType =
+ launchManager
+ .getLaunchConfigurationType(IMonkeyConfigurationConstants.LAUNCH_CONFIGURATION_TYPE_EXTENSION_ID);
+ String launchConfigurationName =
+ launchManager
+ .generateUniqueLaunchConfigurationNameFrom(IMonkeyConfigurationConstants.NEW_CONFIGURATION_NAME);
+ try
+ {
+ ILaunchConfigurationWorkingCopy workingCopy =
+ motodevLaunchType.newInstance(null, launchConfigurationName);
+ workingCopy.setAttribute(IMonkeyConfigurationConstants.ATTR_DEVICE_INSTANCE_NAME,
+ deviceName);
+ config = workingCopy.doSave();
+ }
+ catch (CoreException e)
+ {
+ EclipseUtils.showErrorDialog(AndroidNLS.UI_MonkeyError_Title, e.getMessage());
+ }
+ return config;
+
+ }
+
+ /**
+ * Run adb monkey.
+ *
+ * @param serialNumber
+ * @param deviceName
+ * @return the status of the operation
+ */
+ public static IStatus runMonkey(final String serialNumber, final String deviceName)
+ {
+
+ ILaunchConfigurationType type =
+ DebugPlugin
+ .getDefault()
+ .getLaunchManager()
+ .getLaunchConfigurationType(
+ IMonkeyConfigurationConstants.LAUNCH_CONFIGURATION_TYPE_EXTENSION_ID);
+ ILaunchConfiguration[] launchs;
+ try
+ {
+ launchs = DebugPlugin.getDefault().getLaunchManager().getLaunchConfigurations(type);
+
+ ILaunchConfiguration launchConfig = null;
+ for (int i = 0; i < launchs.length; i++)
+ {
+ launchConfig = launchs[i];
+ String configDeviceName =
+ launchConfig.getAttribute(
+ IMonkeyConfigurationConstants.ATTR_DEVICE_INSTANCE_NAME, "");
+ if (configDeviceName.equals(deviceName))
+ {
+ break;
+ }
+ else
+ {
+ launchConfig = null;
+ }
+
+ }
+ if (launchConfig == null)
+ {
+ launchConfig = createLaunchConfiguration(deviceName);
+
+ }
+
+ final ILaunchGroup lc =
+ DebugUITools.getLaunchGroup(launchConfig, ILaunchManager.RUN_MODE);
+
+ final IStructuredSelection selection = new StructuredSelection(launchConfig);
+
+ PlatformUI.getWorkbench().getDisplay().syncExec(new Runnable()
+ {
+ public void run()
+ {
+
+ DebugUITools.openLaunchConfigurationDialogOnGroup(new Shell(PlatformUI
+ .getWorkbench().getActiveWorkbenchWindow().getShell()), selection,
+ lc.getIdentifier(), null);
+
+ }
+ });
+
+ }
+ catch (CoreException e)
+ {
+ StudioLogger.error("Monkey: could not open the launch configuration dialog "
+ + e.getMessage());
+
+ }
+
+ // UDC log for monkey execution
+ StudioLogger.collectUsageData(UsageDataConstants.WHAT_MONKEY_EXEC, //$NON-NLS-1$
+ UsageDataConstants.KIND_MONKEY_EXEC, "Monkey executed", //$NON-NLS-1$
+ AndroidPlugin.PLUGIN_ID, AndroidPlugin.getDefault().getBundle().getBundleContext()
+ .getBundle().getVersion().toString());
+
+ // all return is successful since operations are async
+ return Status.OK_STATUS;
+
+ }
+
+ /**
+ * Run adb monkey.
+ *
+ * @param serialNumber
+ * @param packagesToRunMonkey
+ * @param otherCmds
+ * @return the status of the operation
+ */
+ public static IStatus runMonkey(final String serialNumber,
+ ArrayList<String> packagesToRunMonkey, String otherCmds)
+ {
+
+ if (packagesToRunMonkey.size() > 0)
+ {
+ OutputStream consoleOut = null;
+
+ try
+ {
+ consoleOut = EclipseUtils.getStudioConsoleOutputStream(true);
+ runMonkey(serialNumber, packagesToRunMonkey, consoleOut, otherCmds);
+ }
+ finally
+ {
+ try
+ {
+ if (consoleOut != null)
+ {
+ consoleOut.close();
+ }
+ }
+ catch (IOException e)
+ {
+ StudioLogger.error("Monkey: could not close console stream" + e.getMessage());
+ }
+ }
+ }
+
+ // all return is successful since operations are async
+ return Status.OK_STATUS;
+
+ }
+
+ /**
+ * List the installed packages in the device with the serial number Each
+ * package entry carries their package location
+ *
+ * @param serialNumber
+ * @return a HashMap where keys are the package names and values are package
+ * location
+ * @throws IOException
+ */
+ public static Map<String, String> listInstalledPackages(String serialNumber) throws IOException
+ {
+ Map<String, String> packages = new LinkedHashMap<String, String>();
+ String sdkPath = SdkUtils.getSdkPath();
+ String command[] =
+ new String[]
+ {
+ sdkPath + DDMSFacade.PLATFORM_TOOLS_FOLDER + File.separator
+ + DDMSFacade.ADB_COMMAND, DDMSFacade.ADB_INSTANCE_PARAMETER,
+ serialNumber, DDMSFacade.SHELL_CMD, PM_CMD, PM_LIST_DIRECTIVE,
+ PM_PACKAGES_DIRECTIVE, PM_PACKAGES_DIRECTIVE_FORCE
+ };
+
+ String commResult = DDMSFacade.executeCommand(command, null);
+ String[] packageList = null;
+ if ((commResult != null) && (commResult.length() > 0)
+ && !commResult.contains("system running?"))
+ {
+ packageList = commResult.trim().replaceAll("\n\n", "\n").split("\n");
+ int i = 0;
+ String aPackage = null;
+ String[] packageUnit = null;
+ while (i < packageList.length)
+ {
+ if (packageList[i].toLowerCase().contains("package:"))
+ {
+ String[] splittedPackage = packageList[i].split(":");
+ if (splittedPackage.length >= 2)
+ {
+ aPackage = splittedPackage[1].trim();
+ packageUnit = aPackage.split("=");
+ if (packageUnit.length > 1)
+ {
+ packages.put(packageUnit[1], packageUnit[0]);
+ }
+ }
+ }
+ i++;
+ }
+ }
+ return packages;
+ }
+
+ /**
+ * Install an application on an emulator instance
+ *
+ * @param serialNumber
+ * The serial number of the device where the application will be
+ * installed
+ * @param path
+ * Path of the package containing the application to be installed
+ * @param canOverwrite
+ * If the application will be installed even if there is a
+ * previous installation
+ * @param force
+ * Perform the operation without asking for user intervention
+ *
+ * @return the status of the operation (OK, Cancel or Error+ErrorMessage)
+ */
+ public static IStatus installPackage(String serialNumber, String path, boolean canOverwrite,
+ OutputStream processOut)
+ {
+ IStatus status = Status.OK_STATUS;
+
+ // Return if no instance is selected
+ if (serialNumber == null)
+ {
+ StudioLogger.error("Abort deploy operation. Serial number is null.");
+ status =
+ new Status(IStatus.ERROR, AndroidPlugin.PLUGIN_ID,
+ AndroidNLS.ERR_DDMSFacade_SerialNumberNullPointer);
+ }
+
+ // Return if instance is not started
+ if (status.isOK() && !DDMSFacade.isDeviceOnline(serialNumber))
+ {
+ StudioLogger.error("Abort deploy operation. Device is not online.");
+ status = new Status(IStatus.ERROR, AndroidPlugin.PLUGIN_ID, "");
+ }
+
+ String command_results = "";
+ if (status.isOK())
+ {
+ try
+ {
+ String[] cmd = createInstallCommand(canOverwrite, path, serialNumber);
+ command_results = DDMSFacade.executeCommand(cmd, processOut, serialNumber);
+
+ // Check if the result has a success message
+ if (!command_results.contains(SUCCESS_CONSTANT))
+ {
+ status =
+ new Status(IStatus.ERROR, AndroidPlugin.PLUGIN_ID,
+ "Error executing the operation. Execution results: "
+ + command_results);
+ }
+ }
+ catch (IOException e)
+ {
+ StudioLogger.error("Deploy: Could not execute adb install command.");
+ status = new Status(IStatus.ERROR, AndroidPlugin.PLUGIN_ID, e.getMessage());
+ }
+ }
+
+ return status;
+ }
+
+ /**
+ * Change the emulator language
+ *
+ * @param serialNumber
+ * The serial number of the device
+ * @param language
+ * the language id
+ * @param country
+ * the country id
+ * @return the status of the operation (OK, Error+ErrorMessage)
+ */
+ public static void changeLanguage(final String serialNumber, final String language,
+ final String country)
+ {
+
+ if ((language != null) || (country != null))
+ {
+ /*
+ * A new thread is used since this command takes too long to be executed
+ */
+ Thread thead = new Thread(new Runnable()
+ {
+
+ public void run()
+ {
+
+ String[] cmd = createChangeLanguageCommand(serialNumber, language, country);
+ try
+ {
+
+ IOConsoleOutputStream consoleOut =
+ EclipseUtils.getStudioConsoleOutputStream(true);
+ if (language != null)
+ {
+ consoleOut.write(AndroidNLS.UI_ChangeLang_Language + " " + language
+ + "\n");
+ }
+ if (country != null)
+ {
+ consoleOut.write(AndroidNLS.UI_ChangeLang_Country + " " + country
+ + "\n");
+ }
+
+ DDMSFacade.executeCommand(cmd, consoleOut);
+ consoleOut.write("\n " + serialNumber + ":"
+ + AndroidNLS.UI_ChangeLang_Restart_Device_Manually + "\n\n");
+ StudioAndroidEventManager.fireEvent(EventType.LANGUAGE_CHANGED,
+ serialNumber);
+ }
+ catch (IOException e)
+ {
+ StudioLogger
+ .error("Language: Could not execute adb change language command.");
+ }
+
+ }
+ });
+ thead.start();
+ }
+
+ }
+
+ /**
+ * Creates a string with the command that should be called in order to
+ * change the device language
+ *
+ * @param serialNumber
+ * The serial number of the device
+ * @param language
+ * the language id
+ * @param country
+ * the country id
+ * @return the command to be used to change the device language
+ */
+ private static String[] createChangeLanguageCommand(String serialNumber, String language,
+ String country)
+ {
+ String cmd[];
+ String sdkPath = SdkUtils.getSdkPath();
+
+ String CHANGE_LANGUAGE_CMD = "";
+ if (language != null)
+ {
+ CHANGE_LANGUAGE_CMD += "setprop persist.sys.language " + language;
+ }
+ if (country != null)
+ {
+ CHANGE_LANGUAGE_CMD +=
+ ((CHANGE_LANGUAGE_CMD.length() > 0) ? ";" : "")
+ + "setprop persist.sys.country " + country;
+ }
+
+ // The tools folder should exist and be here, but double-checking
+ // once more wont kill
+ File f = new File(sdkPath + DDMSFacade.PLATFORM_TOOLS_FOLDER + File.separator);
+ if (!f.exists())
+ {
+ StudioLogger.error("Language: Could not find tools folder on " + sdkPath
+ + DDMSFacade.PLATFORM_TOOLS_FOLDER + File.separator);
+ }
+ else
+ {
+ if (!f.isDirectory())
+ {
+ StudioLogger.error("Language: Invalid tools folder " + sdkPath
+ + DDMSFacade.PLATFORM_TOOLS_FOLDER + File.separator);
+ }
+ }
+
+ String cmdTemp[] =
+ {
+ sdkPath + DDMSFacade.PLATFORM_TOOLS_FOLDER + File.separator
+ + DDMSFacade.ADB_COMMAND, DDMSFacade.ADB_INSTANCE_PARAMETER,
+ serialNumber, "shell", CHANGE_LANGUAGE_CMD
+ };
+ cmd = cmdTemp;
+
+ return cmd;
+ }
+
+ /**
+ * Creates strings with the command that should be called in order to
+ * retrieve the current language and country in use by given emulator instance.
+ *
+ * @param serialNumber The serial number of the device
+ * @return An ArrayList with command strings to be used to get the
+ * current emulator language and country.
+ */
+ private static ArrayList<String[]> createCurrentEmulatorLanguageAndCountryCommand(
+ String serialNumber)
+ {
+ String languageCommand[];
+ String countryCommand[];
+ String sdkPath = SdkUtils.getSdkPath();
+ String GET_LANGUAGE_CMD = "getprop persist.sys.language";
+ String GET_COUNTRY_CMD = "getprop persist.sys.country";
+ // The tools folder should exist and be here, but double-cheking
+ // once more wont kill
+ File f = new File(sdkPath + DDMSFacade.PLATFORM_TOOLS_FOLDER + File.separator);
+ if (!f.exists())
+ {
+ StudioLogger.error("Language: Could not find tools folder on " + sdkPath
+ + DDMSFacade.PLATFORM_TOOLS_FOLDER + File.separator);
+ }
+ else
+ {
+ if (!f.isDirectory())
+ {
+ StudioLogger.error("Language: Invalid tools folder " + sdkPath
+ + DDMSFacade.PLATFORM_TOOLS_FOLDER + File.separator);
+ }
+ }
+ String langCmdTemp[] =
+ {
+ sdkPath + DDMSFacade.PLATFORM_TOOLS_FOLDER + File.separator
+ + DDMSFacade.ADB_COMMAND, DDMSFacade.ADB_INSTANCE_PARAMETER,
+ serialNumber, "shell", GET_LANGUAGE_CMD
+ };
+ String countryCmdTemp[] =
+ {
+ sdkPath + DDMSFacade.PLATFORM_TOOLS_FOLDER + File.separator
+ + DDMSFacade.ADB_COMMAND, DDMSFacade.ADB_INSTANCE_PARAMETER,
+ serialNumber, "shell", GET_COUNTRY_CMD
+ };
+ languageCommand = langCmdTemp;
+ countryCommand = countryCmdTemp;
+
+ ArrayList<String[]> commands = new ArrayList<String[]>();
+ commands.add(0, languageCommand);
+ commands.add(1, countryCommand);
+ return commands;
+ }
+
+ /**
+ * Creates a string with the command that should be called in order to
+ * install the application
+ *
+ * @param canOverwrite
+ * If true, than existent application will be overwritten
+ * @param path
+ * Location of the package containing the application
+ * @param serialNumber
+ * The serial number of the device to be targeted
+ *
+ * @return
+ */
+ private static String[] createInstallCommand(boolean canOverwrite, String path,
+ String serialNumber)
+ {
+ String cmd[];
+ String sdkPath = SdkUtils.getSdkPath();
+
+ // The tools folder should exist and be here, but double-checking
+ // once more wont kill
+ File f = new File(sdkPath + DDMSFacade.PLATFORM_TOOLS_FOLDER + File.separator);
+ if (!f.exists())
+ {
+ StudioLogger.error("Deploy: Could not find tools folder on " + sdkPath
+ + DDMSFacade.PLATFORM_TOOLS_FOLDER + File.separator);
+ }
+ else
+ {
+ if (!f.isDirectory())
+ {
+ StudioLogger.error("Deploy: Invalid tools folder " + sdkPath
+ + DDMSFacade.PLATFORM_TOOLS_FOLDER + File.separator);
+ }
+ }
+
+ if (canOverwrite)
+ {
+ // If overwrite option is checked, create command with the -r
+ // paramater
+ String cmdTemp[] =
+ {
+ sdkPath + DDMSFacade.PLATFORM_TOOLS_FOLDER + File.separator
+ + DDMSFacade.ADB_COMMAND, DDMSFacade.ADB_INSTANCE_PARAMETER,
+ serialNumber, INSTALL_CMD, ADB_INSTALL_OVERWRITE, path
+ };
+ cmd = cmdTemp;
+ }
+ else
+ {
+ // If overwrite option is unchecked, create command without the -r
+ // paramater
+ String cmdTemp[] =
+ {
+ sdkPath + DDMSFacade.PLATFORM_TOOLS_FOLDER + File.separator
+ + DDMSFacade.ADB_COMMAND, DDMSFacade.ADB_INSTANCE_PARAMETER,
+ serialNumber, INSTALL_CMD, path
+ };
+ cmd = cmdTemp;
+ }
+
+ return cmd;
+ }
+
+ private static String[] createUninstallCommand(String serialNumber, String packageName)
+ {
+ String sdkPath = SdkUtils.getSdkPath();
+ // The tools folder should exist and be here, but double-checking
+ // once more wont kill
+ File f = new File(sdkPath + DDMSFacade.PLATFORM_TOOLS_FOLDER + File.separator);
+ if (!f.exists())
+ {
+ StudioLogger.error("Run: Could not find tools folder on " + sdkPath
+ + DDMSFacade.PLATFORM_TOOLS_FOLDER + File.separator);
+ }
+ else
+ {
+ if (!f.isDirectory())
+ {
+ StudioLogger.error("Run: Invalid tools folder " + sdkPath
+ + DDMSFacade.PLATFORM_TOOLS_FOLDER + File.separator);
+ }
+ }
+
+ String cmd[] =
+ {
+ sdkPath + DDMSFacade.PLATFORM_TOOLS_FOLDER + File.separator
+ + DDMSFacade.ADB_COMMAND, DDMSFacade.ADB_INSTANCE_PARAMETER,
+ serialNumber, DDMSFacade.SHELL_CMD, PM_CMD, PM_UNINSTALL_DIRECTIVE,
+ packageName
+ };
+
+ return cmd;
+
+ }
+
+ /**
+ * Mount the adb monkey command based on the given parameters.
+ * @param serialNumber
+ * @param packagesName
+ * @param otherCmd
+ * @return the array of strings containing the monkey command to be executed.
+ */
+ private static String[] createMonkeyCommand(String serialNumber, String packagesName,
+ String otherCmd)
+ {
+ String sdkPath = SdkUtils.getSdkPath();
+ // The tools folder should exist and be here, but double-checking
+ // once more wont kill
+ File f = new File(sdkPath + DDMSFacade.PLATFORM_TOOLS_FOLDER + File.separator);
+ if (!f.exists())
+ {
+ StudioLogger.error("Run: Could not find tools folder on " + sdkPath
+ + DDMSFacade.PLATFORM_TOOLS_FOLDER + File.separator);
+ }
+ else
+ {
+ if (!f.isDirectory())
+ {
+ StudioLogger.error("Run: Invalid tools folder " + sdkPath
+ + DDMSFacade.PLATFORM_TOOLS_FOLDER + File.separator);
+ }
+ }
+
+ String cmd[] =
+ {
+ sdkPath + DDMSFacade.PLATFORM_TOOLS_FOLDER + File.separator
+ + DDMSFacade.ADB_COMMAND, DDMSFacade.ADB_INSTANCE_PARAMETER,
+ serialNumber, DDMSFacade.SHELL_CMD, MONKEY_CMD, packagesName, otherCmd
+ };
+
+ return cmd;
+
+ }
+
+ /**
+ * Dump HPROF service implementation
+ * @param serialNumber The device serial number
+ * @param monitor
+ * @return A IStatus describing if the service was successful or not
+ */
+ public static IStatus dumpHPROF(final String serialNumber, IProgressMonitor monitor)
+ {
+ IStatus status;
+
+ // Selected app. The array should have only 1 element
+ final String[] selectedAppSet = new String[1];
+
+ // Instantiate the wizard and populate it with the retrieved process list
+ PlatformUI.getWorkbench().getDisplay().syncExec(new Runnable()
+ {
+ public void run()
+ {
+ DumpHPROFWizard dumpHPROFWizard = new DumpHPROFWizard(serialNumber);
+ WizardDialog dialog =
+ new WizardDialog(new Shell(PlatformUI.getWorkbench()
+ .getActiveWorkbenchWindow().getShell()), dumpHPROFWizard);
+ dialog.open();
+
+ // Get the selected application
+ selectedAppSet[0] = dumpHPROFWizard.getSelectedApp();
+
+ }
+ });
+
+ if (selectedAppSet[0] != null)
+ {
+ // Dump HPROF file based on the selected application
+ status = DDMSFacade.dumpHprofFile(selectedAppSet[0], serialNumber, monitor);
+ }
+ else
+ {
+ status = Status.CANCEL_STATUS;
+ }
+
+ return status;
+ }
+
+ public static int getDeviceApiVersion(String serialNumber)
+ {
+ int deviceSdkVersion = -1;
+ String deviceProperty = DDMSFacade.getDeviceProperty(serialNumber, "ro.build.version.sdk");
+ if (deviceProperty != null)
+ {
+ deviceSdkVersion = Integer.parseInt(deviceProperty);
+ }
+
+ return deviceSdkVersion;
+ }
+
+ public static boolean remoteFileExists(String serialNumber, String remotePath)
+ throws IOException
+ {
+ boolean found = false;
+ Collection<String> results =
+ DDMSFacade.execRemoteApp(serialNumber,
+ "ls " + FileUtil.getEscapedPath(remotePath, Platform.OS_LINUX),
+ new NullProgressMonitor());
+ for (String result : results)
+ {
+ if (result.equals(remotePath))
+ {
+ found = true;
+ break;
+ }
+ }
+ return found;
+ }
+}
diff --git a/src/plugins/android/src/com/motorola/studio/android/adt/DdmsRunnable.java b/src/plugins/android/src/com/motorola/studio/android/adt/DdmsRunnable.java
new file mode 100644
index 0000000..34582c4
--- /dev/null
+++ b/src/plugins/android/src/com/motorola/studio/android/adt/DdmsRunnable.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.adt;
+
+/**
+ * DESCRIPTION:
+ * Interface to be implemented when some serial number dependent piece of code
+ * needs to be run
+ *
+ * RESPONSIBILITY:
+ * Behaves as Runnable, but receives a serial number in the parameters list
+ *
+ * COLABORATORS:
+ * None.
+ *
+ * USAGE:
+ * Provide a runnable like this when DDMSFacade needs one
+ */
+public interface DdmsRunnable
+{
+ /**
+ * @see Runnable#run()
+ *
+ * @param serialNumber The serial number of the device that triggered
+ * this operation
+ */
+ void run(String serialNumber);
+}
diff --git a/src/plugins/android/src/com/motorola/studio/android/adt/FileListingReceiver.java b/src/plugins/android/src/com/motorola/studio/android/adt/FileListingReceiver.java
new file mode 100644
index 0000000..0bc6342
--- /dev/null
+++ b/src/plugins/android/src/com/motorola/studio/android/adt/FileListingReceiver.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.adt;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.android.ddmlib.FileListingService;
+import com.android.ddmlib.FileListingService.FileEntry;
+import com.android.ddmlib.FileListingService.IListingReceiver;
+
+/**
+ * Receive ddms notifications about file listing and notify the listener
+ */
+class FileListingReceiver implements IListingReceiver
+{
+ private final List<String> availableSegments;
+
+ private final FileListingService service;
+
+ private final IDatabaseListingListener listener;
+
+ private final List<String> databases;
+
+ public FileListingReceiver(List<String> segments, FileListingService fileListingService,
+ IDatabaseListingListener listener)
+ {
+ this.availableSegments = segments;
+ this.service = fileListingService;
+ this.listener = listener;
+ this.databases = new ArrayList<String>();
+ }
+
+ public void refreshEntry(FileEntry fileentry)
+ {
+ //do nothing
+ }
+
+ public void setChildren(FileEntry fileentry, FileEntry[] afileentry)
+ {
+ if (availableSegments.size() > 0)
+ {
+ String theSegment = availableSegments.remove(0);
+ FileEntry entry = fileentry.findChild(theSegment);
+ if (entry != null)
+ {
+ service.getChildren(entry, false, new FileListingReceiver(availableSegments,
+ service, listener));
+ }
+ else
+ {
+ notifyListeners();
+ }
+ }
+ else
+ {
+ for (FileEntry entry : afileentry)
+ {
+ databases.add(entry.getName());
+ }
+ notifyListeners();
+ }
+ }
+
+ private void notifyListeners()
+ {
+ this.listener.databasesFound(databases);
+ }
+
+} \ No newline at end of file
diff --git a/src/plugins/android/src/com/motorola/studio/android/adt/IDatabaseListingListener.java b/src/plugins/android/src/com/motorola/studio/android/adt/IDatabaseListingListener.java
new file mode 100644
index 0000000..ac6c705
--- /dev/null
+++ b/src/plugins/android/src/com/motorola/studio/android/adt/IDatabaseListingListener.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.motorola.studio.android.adt;
+
+import java.util.List;
+
+/**
+ * Listener to be notified about database found
+ */
+public interface IDatabaseListingListener
+{
+
+ public void databasesFound(List<String> databases);
+
+}
diff --git a/src/plugins/android/src/com/motorola/studio/android/adt/ISerialNumbered.java b/src/plugins/android/src/com/motorola/studio/android/adt/ISerialNumbered.java
new file mode 100644
index 0000000..756cbfc
--- /dev/null
+++ b/src/plugins/android/src/com/motorola/studio/android/adt/ISerialNumbered.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.adt;
+
+/**
+ * DESCRIPTION:
+ * Interface to be implemented by every entity that have serial numbers
+ *
+ * RESPONSIBILITY:
+ * Allows the serial number of the entity to be retrieved
+ *
+ * COLABORATORS:
+ * None.
+ *
+ * USAGE:
+ * Call getSerialNumber to retrieve the entity serial number
+ */
+public interface ISerialNumbered
+{
+ /**
+ * Retrieves the serial number
+ *
+ * @return serial number
+ */
+ String getSerialNumber();
+
+ /**
+ * Retrieves the device name that is displayed in the UI
+ *
+ * @return
+ */
+ String getDeviceName();
+
+ String getFullName();
+
+}
diff --git a/src/plugins/android/src/com/motorola/studio/android/adt/InstallPackageBean.java b/src/plugins/android/src/com/motorola/studio/android/adt/InstallPackageBean.java
new file mode 100644
index 0000000..1c1083a
--- /dev/null
+++ b/src/plugins/android/src/com/motorola/studio/android/adt/InstallPackageBean.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.motorola.studio.android.adt;
+
+import com.motorola.studio.android.wizards.installapp.DeployWizard.INSTALL_TYPE;
+
+public class InstallPackageBean
+{
+
+ private String packagePath = null;
+
+ private INSTALL_TYPE canOverwrite = null;
+
+ public String getPackagePath()
+ {
+ return packagePath;
+ }
+
+ public void setPackagePath(String packagePath)
+ {
+ this.packagePath = packagePath;
+ }
+
+ public INSTALL_TYPE getCanOverwrite()
+ {
+ return canOverwrite;
+ }
+
+ public void setCanOverwrite(INSTALL_TYPE canOverwrite)
+ {
+ this.canOverwrite = canOverwrite;
+ }
+
+}
diff --git a/src/plugins/android/src/com/motorola/studio/android/adt/MotodevHProfDumpHandler.java b/src/plugins/android/src/com/motorola/studio/android/adt/MotodevHProfDumpHandler.java
new file mode 100644
index 0000000..e74662e
--- /dev/null
+++ b/src/plugins/android/src/com/motorola/studio/android/adt/MotodevHProfDumpHandler.java
@@ -0,0 +1,350 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.motorola.studio.android.adt;
+
+import java.io.File;
+import java.io.IOException;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+import org.eclipse.core.filesystem.EFS;
+import org.eclipse.core.filesystem.IFileStore;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
+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.ide.IDE;
+
+import com.android.ddmlib.Client;
+import com.android.ddmlib.ClientData.IHprofDumpHandler;
+import com.android.ddmlib.IDevice;
+import com.android.ddmlib.SyncService;
+import com.android.ddmuilib.SyncProgressMonitor;
+import com.android.ddmuilib.handler.BaseFileHandler;
+import com.android.ide.eclipse.ddms.DdmsPlugin;
+import com.android.ide.eclipse.ddms.preferences.PreferenceInitializer;
+import com.motorola.studio.android.AndroidPlugin;
+import com.motorola.studio.android.common.preferences.DialogWithToggleUtils;
+import com.motorola.studio.android.common.utilities.EclipseUtils;
+import com.motorola.studio.android.i18n.AndroidNLS;
+
+/**
+ *
+ * Class to handle post HPROF dumping things. Based on the existing HprofHandler from ADT.
+ */
+public class MotodevHProfDumpHandler extends BaseFileHandler implements IHprofDumpHandler
+{
+ public final static String SAVE_ACTION = "hprof.save"; //$NON-NLS-1$
+
+ public final static String OPEN_ACTION = "hprof.open"; //$NON-NLS-1$
+
+ public final static String HPROF_FILE_EXTENSION = ".hprof"; //$NON-NLS-1$
+
+ private final static String FORMATTED_ERROR_STRING = " '%1$s'.\n\n%2$s"; //$NON-NLS-1$
+
+ private final static String FORMATTED_ERROR_STRING_2 = " '%1$s'."; //$NON-NLS-1$
+
+ private final static String DATE_FORMAT = "yyyy-MM-dd HH-mm-ss"; //$NON-NLS-1$
+
+ private String selectedApp;
+
+ private final IProgressMonitor progressMonitor;
+
+ /**
+ * @return the selectedApp
+ */
+ public String getSelectedApp()
+ {
+ return selectedApp;
+ }
+
+ /**
+ * @param selectedApp the selectedApp to set
+ */
+ public void setSelectedApp(String selectedApp)
+ {
+ this.selectedApp = selectedApp;
+ }
+
+ public MotodevHProfDumpHandler(Shell parentShell, IProgressMonitor monitor)
+ {
+ super(parentShell);
+ this.progressMonitor = monitor;
+ }
+
+ @Override
+ protected String getDialogTitle()
+ {
+ return AndroidNLS.UI_Hprof_Handler_Dialog_Error_Title;
+ }
+
+ public void onEndFailure(final Client client, final String message)
+ {
+ mParentShell.getDisplay().asyncExec(new Runnable()
+ {
+ public void run()
+ {
+ displayErrorFromUiThread(AndroidNLS.UI_Hprof_Handler_Dialog_Unable_to_create_Hprof
+ + FORMATTED_ERROR_STRING
+ + AndroidNLS.UI_Hprof_Handler_Dialog_Error_Check_Log_Cat, client
+ .getClientData().getClientDescription(), message != null
+ ? message + "\n\n" : ""); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+ });
+ synchronized (DDMSFacade.class)
+ {
+ DDMSFacade.class.notify();
+ }
+ }
+
+ public void onSuccess(final String remoteFilePath, final Client client)
+ {
+ mParentShell.getDisplay().asyncExec(new Runnable()
+ {
+ public void run()
+ {
+ extractRemoteHprof(remoteFilePath, client);
+ }
+ });
+ synchronized (DDMSFacade.class)
+ {
+ DDMSFacade.class.notify();
+ }
+ }
+
+ private void extractRemoteHprof(final String remoteFilePath, final Client client)
+ {
+ progressMonitor.beginTask(AndroidNLS.DumpHprofFile_GeneratingMemoryAnalysisOutput, 100);
+ final IDevice targetDevice = client.getDevice();
+ try
+ {
+ // get the sync service to pull the HPROF file
+ final SyncService syncService = client.getDevice().getSyncService();
+ if (syncService != null)
+ {
+ // get from the preference what action to take
+ IPreferenceStore preferenceStore = DdmsPlugin.getDefault().getPreferenceStore();
+ String actionValue =
+ preferenceStore.getString(PreferenceInitializer.ATTR_HPROF_ACTION);
+
+ if (SAVE_ACTION.equals(actionValue))
+ {
+ warnAboutSaveHprofPreference();
+ }
+
+ actionValue = preferenceStore.getString(PreferenceInitializer.ATTR_HPROF_ACTION);
+
+ if (OPEN_ACTION.equals(actionValue))
+ {
+ progressMonitor.setTaskName(AndroidNLS.DumpHprofFile_CreatingTempFile);
+ File hprofTempFile = File.createTempFile(selectedApp, HPROF_FILE_EXTENSION);
+ progressMonitor.worked(25);
+
+ String tempHprofFilePath = hprofTempFile.getAbsolutePath();
+
+ progressMonitor
+ .setTaskName(AndroidNLS.DumpHprofFile_GettingFileFromRemoteDevice);
+ progressMonitor.worked(50);
+
+ syncService.pullFile(remoteFilePath, tempHprofFilePath,
+ new SyncProgressMonitor(progressMonitor, "")); //$NON-NLS-1$
+
+ openHprofFileInEditor(tempHprofFilePath);
+
+ }
+ else
+ {
+ progressMonitor.setTaskName(AndroidNLS.DumpHprofFile_SavingFile);
+ try
+ {
+ promptAndPull(syncService, client.getClientData().getClientDescription()
+ + HPROF_FILE_EXTENSION, remoteFilePath,
+ AndroidNLS.MotodevHProfDumpHandler_saveHProfFile);
+ }
+ catch (Exception e)
+ {
+ displayErrorFromUiThread(
+ AndroidNLS.UI_Hprof_Handler_Dialog_Unable_to_download_Hprof
+ + FORMATTED_ERROR_STRING, targetDevice.getSerialNumber(),
+ e.getLocalizedMessage());
+ }
+ progressMonitor.worked(100);
+ }
+
+ }
+ else
+ {
+ displayErrorFromUiThread(
+ AndroidNLS.UI_Hprof_Handler_Dialog_Unable_to_download_Hprof
+ + FORMATTED_ERROR_STRING_2, targetDevice.getSerialNumber());
+ }
+ }
+ catch (Exception e)
+ {
+ displayErrorFromUiThread(AndroidNLS.UI_Hprof_Handler_Dialog_Unable_to_download_Hprof
+ + FORMATTED_ERROR_STRING_2, targetDevice.getSerialNumber());
+
+ }
+ finally
+ {
+ progressMonitor.done();
+ }
+ }
+
+ public void onSuccess(final byte[] data, final Client client)
+ {
+ mParentShell.getDisplay().asyncExec(new Runnable()
+ {
+ public void run()
+ {
+ extractLocalHprof(data, client, progressMonitor);
+ }
+ });
+ synchronized (DDMSFacade.class)
+ {
+ DDMSFacade.class.notify();
+ }
+ }
+
+ private void extractLocalHprof(final byte[] data, final Client client, IProgressMonitor monitor)
+ {
+ monitor.beginTask(AndroidNLS.DumpHprofFile_GeneratingMemoryAnalysisOutput, 100);
+ IPreferenceStore preferenceStore = DdmsPlugin.getDefault().getPreferenceStore();
+ String value = preferenceStore.getString(PreferenceInitializer.ATTR_HPROF_ACTION);
+
+ if (SAVE_ACTION.equals(value))
+ {
+ warnAboutSaveHprofPreference();
+ }
+
+ value = preferenceStore.getString(PreferenceInitializer.ATTR_HPROF_ACTION);
+
+ if (OPEN_ACTION.equals(value))
+ {
+ try
+ {
+ monitor.setTaskName(AndroidNLS.DumpHprofFile_SavingTempFile);
+ File tempHprofFile = saveTempFile(data, HPROF_FILE_EXTENSION);
+ monitor.worked(50);
+ monitor.setTaskName(AndroidNLS.DumpHprofFile_OpeningMemoryAnalysisFile);
+ openHprofFileInEditor(tempHprofFile.getAbsolutePath());
+ monitor.worked(50);
+ }
+ catch (Exception e)
+ {
+ String errorMsg = e.getMessage();
+ displayErrorFromUiThread(
+ AndroidNLS.UI_Hprof_Handler_Dialog_Unable_to_Save_Hprof_Data
+ + FORMATTED_ERROR_STRING_2, errorMsg != null ? ":\n" + errorMsg //$NON-NLS-1$
+ : "."); //$NON-NLS-1$
+ }
+ }
+ else
+ {
+ monitor.setTaskName(AndroidNLS.DumpHprofFile_SavingFile);
+ promptAndSave(client.getClientData().getClientDescription() + HPROF_FILE_EXTENSION,
+ data, AndroidNLS.UI_Hprof_Handler_Save_Prompt);
+ monitor.worked(100);
+ }
+ monitor.done();
+ }
+
+ private void warnAboutSaveHprofPreference()
+ {
+ Display.getCurrent().syncExec(new Runnable()
+ {
+
+ public void run()
+ {
+ boolean openPrefPage =
+ DialogWithToggleUtils.showQuestion(
+ AndroidPlugin.WARN_ABOUT_HPROF_PREFERENCE,
+ AndroidNLS.MotodevHProfDumpHandler_warnAboutHprofSavePrefTitle,
+ AndroidNLS.MotodevHProfDumpHandler_warnAboutHprofSavePrefMsg);
+ if (openPrefPage)
+ {
+ IWorkbench workbench = PlatformUI.getWorkbench();
+ IWorkbenchWindow ww = workbench.getActiveWorkbenchWindow();
+ EclipseUtils.openPreference(ww.getShell(),
+ "com.android.ide.eclipse.ddms.preferences.PreferencePage"); //$NON-NLS-1$
+ }
+ }
+ });
+ }
+
+ /**
+ * Opens the HProf file into MAT editor
+ * @param path
+ * @throws IOException
+ * @throws InterruptedException
+ * @throws PartInitException
+ */
+ private void openHprofFileInEditor(String path) throws IOException, InterruptedException,
+ PartInitException
+ {
+ // make a file to convert the hprof into something
+ // readable by normal tools
+ String hprofPath = getHProfLocalFileName(path);
+
+ String[] commands = new String[3];
+ commands[0] = DdmsPlugin.getHprofConverter();
+ commands[1] = path;
+ commands[2] = hprofPath;
+
+ Process p = Runtime.getRuntime().exec(commands);
+ p.waitFor();
+
+ IFileStore fileSystemStore = EFS.getLocalFileSystem().getStore(new Path(hprofPath));
+ if (!fileSystemStore.fetchInfo().isDirectory() && fileSystemStore.fetchInfo().exists())
+ {
+ IWorkbenchPage workbenchPage =
+ AndroidPlugin.getDefault().getWorkbench().getActiveWorkbenchWindow()
+ .getActivePage();
+ IEditorPart editorPart = IDE.openEditorOnFileStore(workbenchPage, fileSystemStore);
+ // Store information about the opened file and the selected app
+ AndroidPlugin.getDefault().getPreferenceStore()
+ .setValue(editorPart.getEditorInput().getName(), selectedApp);
+
+ }
+ }
+
+ /**
+ * Gets local (desktop) file name based on selected app and the current date
+ * @param path
+ * @return
+ */
+ private String getHProfLocalFileName(String path)
+ {
+ Date date = new Date();
+ DateFormat dateFormat = new SimpleDateFormat(DATE_FORMAT);
+ File ddmsPath = new File(path);
+
+ File hprofFileHandler =
+ new File(ddmsPath.getParent(), selectedApp + " " + dateFormat.format(date) //$NON-NLS-1$
+ + HPROF_FILE_EXTENSION);
+ String hprofPath = hprofFileHandler.getAbsolutePath();
+ return hprofPath;
+ }
+}
diff --git a/src/plugins/android/src/com/motorola/studio/android/adt/ProjectUtils.java b/src/plugins/android/src/com/motorola/studio/android/adt/ProjectUtils.java
new file mode 100644
index 0000000..03a01ff
--- /dev/null
+++ b/src/plugins/android/src/com/motorola/studio/android/adt/ProjectUtils.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.adt;
+
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.jdt.core.IClasspathEntry;
+import org.eclipse.jdt.core.JavaModelException;
+
+import com.android.ide.eclipse.adt.internal.project.AndroidNature;
+import com.android.ide.eclipse.adt.internal.project.ProjectHelper;
+
+/**
+ * Class that contains useful methods to handle with Android Projects
+ */
+public class ProjectUtils
+{
+ /**
+ * Fixes an Android project (a project with errors)
+ *
+ * @param project the project to be fixed
+ *
+ * @throws JavaModelException
+ */
+ public static void fixProject(IProject project) throws JavaModelException
+ {
+ ProjectHelper.fixProject(project);
+ }
+
+ /**
+ * Sets the Android Project natures to a project
+ *
+ * @param project The project
+ * @param monitor The progress monitor
+ *
+ * @throws CoreException
+ */
+ public static void setupAndroidNatures(IProject project, IProgressMonitor monitor)
+ throws CoreException
+ {
+ // Add the Java and android nature to the project
+ AndroidNature.setupProjectNatures(project, monitor);
+ }
+
+ /**
+ * Adds a new entry to a set of classpath entries
+ *
+ * @param entries the classpath entries
+ * @param newSourceEntry the new entry to add
+ *
+ * @return the new set of classpath
+ */
+ public static IClasspathEntry[] addEntryToClasspath(IClasspathEntry[] entries,
+ IClasspathEntry newSourceEntry)
+ {
+ return ProjectHelper.addEntryToClasspath(entries, newSourceEntry);
+ }
+
+}
diff --git a/src/plugins/android/src/com/motorola/studio/android/adt/Sample.java b/src/plugins/android/src/com/motorola/studio/android/adt/Sample.java
new file mode 100644
index 0000000..857aa34
--- /dev/null
+++ b/src/plugins/android/src/com/motorola/studio/android/adt/Sample.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.adt;
+
+import java.io.File;
+import java.io.FilenameFilter;
+
+import com.android.sdklib.IAndroidTarget;
+import com.motorola.studio.android.common.IAndroidConstants;
+
+/**
+ * Bean that represents a sample application
+ */
+public class Sample
+{
+ private final File folder;
+
+ private final String name;
+
+ private final IAndroidTarget target;
+
+ public Sample(File sampleFolder, IAndroidTarget parentTarget)
+ {
+ folder = sampleFolder;
+ name = sampleFolder.getName();
+ target = parentTarget;
+ }
+
+ /**
+ * Retrieves the sample application folder
+ *
+ * @return the sample application folder
+ */
+ public File getFolder()
+ {
+ return folder;
+ }
+
+ /**
+ * Retrieves the sample application name
+ *
+ * @return the sample application name
+ */
+ public String getName()
+ {
+ return name;
+ }
+
+ /**
+ * Retrieves the sample application target SDK
+ *
+ * @return the sample application target SDK
+ */
+ public IAndroidTarget getTarget()
+ {
+ return target;
+ }
+
+ /**
+ * Checks if a folder is a sample folder
+ *
+ * @param sampleFolder The folder to be tested
+ * @return true if a folder is a sample folder or false otherwise
+ */
+ public static boolean isSample(File sampleFolder)
+ {
+ boolean result = false;
+
+ // check if the folder contains a manifest file
+ FilenameFilter androidManifest = new FilenameFilter()
+ {
+ public boolean accept(File arg0, String arg1)
+ {
+ return arg1.equals(IAndroidConstants.FN_ANDROID_MANIFEST);
+ }
+ };
+
+ if ((sampleFolder != null) && (sampleFolder.isDirectory()))
+ {
+ if (sampleFolder.list(androidManifest).length > 0)
+ {
+ result = true;
+ }
+ }
+ return result;
+ }
+}
diff --git a/src/plugins/android/src/com/motorola/studio/android/adt/SdkUtils.java b/src/plugins/android/src/com/motorola/studio/android/adt/SdkUtils.java
new file mode 100644
index 0000000..14836f3
--- /dev/null
+++ b/src/plugins/android/src/com/motorola/studio/android/adt/SdkUtils.java
@@ -0,0 +1,1006 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.adt;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FilenameFilter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.ui.PlatformUI;
+
+import com.android.ide.eclipse.adt.AdtPlugin;
+import com.android.ide.eclipse.adt.internal.sdk.AndroidTargetData;
+import com.android.ide.eclipse.adt.internal.sdk.Sdk;
+import com.android.sdklib.AndroidVersion;
+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.sdkuilib.internal.widgets.MessageBoxLog;
+import com.motorola.studio.android.AndroidPlugin;
+import com.motorola.studio.android.common.IAndroidConstants;
+import com.motorola.studio.android.common.exception.AndroidException;
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.common.utilities.FileUtil;
+import com.motorola.studio.android.i18n.AndroidNLS;
+import com.motorola.studio.android.manifest.AndroidProjectManifestFile;
+import com.motorola.studio.android.model.manifest.AndroidManifestFile;
+import com.motorola.studio.android.model.manifest.dom.AndroidManifestNode;
+import com.motorola.studio.android.model.manifest.dom.UsesSDKNode;
+
+/**
+ * DESCRIPTION: This class provides utility methods related to
+ * the Android SDK.
+ * <br>
+ * USAGE: See public methods
+ */
+
+public class SdkUtils
+{
+ public static final int API_LEVEL_FOR_PLATFORM_VERSION_3_0_0 = 11;
+
+ public static final String VM_CONFIG_FILENAME = "config.ini"; //$NON-NLS-1$
+
+ public static final String USERIMAGE_FILENAME = "userdata-qemu.img"; //$NON-NLS-1$
+
+ public static final String[] STATEDATA_FILENAMES =
+ {
+ "cache.img", "userdata.img", "emulator-user.ini" //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ };
+
+ public static final String EMU_CONFIG_SKIN_NAME_PROPERTY = "skin.name"; //$NON-NLS-1$
+
+ /**
+ * Gets the current SDK object
+ */
+ public static Sdk getCurrentSdk()
+ {
+ return Sdk.getCurrent();
+ }
+
+ /**
+ * Gets the directory where the configured SDK is located
+ */
+ public static String getSdkPath()
+ {
+ String sdkDir = null;
+ Sdk sdk = getCurrentSdk();
+ if (sdk != null)
+ {
+ sdkDir = sdk.getSdkLocation();
+ }
+ return sdkDir;
+ }
+
+ /**
+ * Gets the path to the "tools" folder of the SDK
+ * @return
+ */
+ public static String getSdkToolsPath()
+ {
+ return AdtPlugin.getOsSdkToolsFolder();
+ }
+
+ public static IAndroidTarget getTargetByAPILevel(Integer apiLevel)
+ {
+ IAndroidTarget returnTarget = null;
+
+ for (IAndroidTarget target : getAllTargets())
+ {
+ if (target.getVersion().getApiLevel() == apiLevel)
+ {
+ returnTarget = target;
+ }
+ }
+ return returnTarget;
+ }
+
+ /**
+ * Get the AAPT application path from an android target
+ * if null is passed, try to get some aapt
+ * @param target
+ * @return
+ */
+ public static String getTargetAAPTPath(IAndroidTarget target)
+ {
+ IAndroidTarget realTarget = null;
+ if (target == null)
+ {
+ StudioLogger.warn(SdkUtils.class, "Trying to find a suitable aapt application to use"); //$NON-NLS-1$
+ IAndroidTarget[] allTargets = Sdk.getCurrent().getTargets();
+ if (allTargets.length > 0)
+ {
+ realTarget = allTargets[0];
+ }
+ }
+ else
+ {
+ realTarget = target;
+ }
+
+ while ((realTarget != null) && !realTarget.isPlatform())
+ {
+ realTarget = realTarget.getParent();
+ }
+
+ if (realTarget == null)
+ {
+ StudioLogger.warn(SdkUtils.class,
+ "No aapt executable found: do you have an Android platform installed?"); //$NON-NLS-1$
+ }
+
+ return realTarget != null ? realTarget.getPath(IAndroidTarget.AAPT) : null;
+ }
+
+ /**
+ * Gets the path to the "adb" executable of the SDK
+ * @return
+ */
+ public static String getAdbPath()
+ {
+ return AdtPlugin.getOsAbsoluteAdb();
+ }
+
+ /**
+ * Reloads the recognized AVD list
+ */
+ public static void reloadAvds()
+ {
+
+ try
+ {
+ getVmManager().reloadAvds(NullSdkLog.getLogger());
+ }
+ catch (Exception e)
+ {
+ StudioLogger.error(SdkUtils.class, "Error while reloading AVDs"); //$NON-NLS-1$
+ }
+
+ }
+
+ /**
+ * Gets the VmManager object
+ */
+ public static AvdManager getVmManager()
+ {
+ AvdManager vmManager = null;
+ Sdk sdk = getCurrentSdk();
+ if (sdk != null)
+ {
+ vmManager = sdk.getAvdManager();
+ }
+
+ return vmManager;
+ }
+
+ /**
+ * Gets all available Targets
+ */
+ public static IAndroidTarget[] getAllTargets()
+ {
+ IAndroidTarget[] allTargets = null;
+ Sdk sdk = getCurrentSdk();
+ if (sdk != null)
+ {
+ allTargets = sdk.getTargets();
+ }
+ return allTargets;
+ }
+
+ /**
+ * Gets a Target by name
+ *
+ * @param name the target name
+ */
+ public static IAndroidTarget getTargetByName(String name)
+ {
+ IAndroidTarget ret = null;
+
+ if ((name != null) && (name.length() > 0))
+ {
+ IAndroidTarget[] allTargets = getAllTargets();
+
+ for (int i = 0; i < allTargets.length; i++)
+ {
+ if (name.equals(allTargets[i].getName()))
+ {
+ ret = allTargets[i];
+ break;
+ }
+ }
+ }
+
+ return ret;
+ }
+
+ /**
+ * Gets all VMs
+ */
+ public static AvdInfo[] getAllVms()
+ {
+ AvdInfo[] allVmInfo = null;
+ AvdManager vmManager = getVmManager();
+ if (vmManager != null)
+ {
+ allVmInfo = vmManager.getAllAvds();
+ }
+ return allVmInfo;
+ }
+
+ /**
+ * Gets all valid VMs
+ */
+ public static AvdInfo[] getAllValidVms()
+ {
+ AvdInfo[] validVmInfo = null;
+ AvdManager vmManager = getVmManager();
+ if (vmManager != null)
+ {
+ validVmInfo = vmManager.getValidAvds();
+ }
+ return validVmInfo;
+ }
+
+ /**
+ * Gets the name of all VMs that are recognized by the configured SDK.
+ */
+ public static Collection<String> getAllVmNames()
+ {
+ Collection<String> vmNames = new LinkedList<String>();
+ for (AvdInfo vm : getAllVms())
+ {
+ vmNames.add(vm.getName());
+ }
+ return vmNames;
+ }
+
+ /**
+ * Gets the name of all VMs that are recognized by the configured SDK.
+ */
+ public static Collection<String> getAllValidVmNames()
+ {
+ Collection<String> vmNames = new LinkedList<String>();
+ AvdInfo[] allAvds = getAllValidVms();
+ if (allAvds != null)
+ {
+ for (AvdInfo vm : allAvds)
+ {
+ vmNames.add(vm.getName());
+ }
+ }
+
+ return vmNames;
+ }
+
+ /**
+ * Gets a skin name
+ *
+ * @param vmInfo the VM to get the skin
+ */
+ public static String getSkin(AvdInfo vmInfo)
+ {
+ String skin = ""; //$NON-NLS-1$
+ File configFile = vmInfo.getConfigFile();
+ Properties p = new Properties();
+ InputStream configFileStream = null;
+ try
+ {
+ configFileStream = new FileInputStream(configFile);
+ p.load(configFileStream);
+ skin = p.getProperty(EMU_CONFIG_SKIN_NAME_PROPERTY);
+ }
+ catch (FileNotFoundException e)
+ {
+ StudioLogger
+ .error(SdkUtils.class,
+ "Error getting VM skin definition. Could not find file " + configFile.getAbsolutePath(), e); //$NON-NLS-1$
+ }
+ catch (IOException e)
+ {
+ StudioLogger
+ .error(SdkUtils.class,
+ "Error getting VM skin definition. Could not access file " + configFile.getAbsolutePath(), e); //$NON-NLS-1$
+ }
+ finally
+ {
+ if (configFileStream != null)
+ {
+ try
+ {
+ configFileStream.close();
+ }
+ catch (IOException e)
+ {
+ //nothing to do
+ }
+ }
+ }
+
+ return skin;
+ }
+
+ /**
+ * Gets a VM by name.
+ *
+ * @param vmName The VM name
+ */
+ public static AvdInfo getValidVm(String vmName)
+ {
+ AvdInfo vmInfo = null;
+ AvdManager vmManager = getVmManager();
+ if (vmManager != null)
+ {
+ vmInfo = vmManager.getAvd(vmName, true);
+ }
+ return vmInfo;
+ }
+
+ /**
+ * Gets a VM by name.
+ *
+ * @param vmName The VM name
+ */
+ public static AvdInfo getVm(String vmName)
+ {
+ AvdInfo vmInfo = null;
+ AvdManager vmManager = getVmManager();
+ if (vmManager != null)
+ {
+ vmInfo = vmManager.getAvd(vmName, false);
+ }
+ return vmInfo;
+ }
+
+ /**
+ * Creates a new VM instance.
+ *
+ * @param folder Folder where the VM files will be stored
+ * @param name VM Name
+ * @param target VM Target represented by the IAndroidTarget object
+ * @param skinName VM Skin name from the VM Target
+ *
+ * @throws CoreException
+ */
+ public static AvdInfo createVm(String folder, String name, IAndroidTarget target,
+ String abiType, String skinName, String useSnapshot, String sdCard)
+ throws CoreException
+
+ {
+ AvdInfo vmInfo;
+ AvdManager vmManager = SdkUtils.getVmManager();
+
+ // get the abi type
+ if (abiType == null)
+ {
+ abiType = SdkConstants.ABI_ARMEABI;
+ }
+
+ /*
+ * public VmInfo createVm(String parentFolder, String name, IAndroidTarget target,
+ * String skinName, String sdcard, Map hardwareConfig)
+ */
+ vmInfo =
+ vmManager.createAvd(new File(folder), name, target, abiType, skinName, sdCard,
+ null, Boolean.parseBoolean(useSnapshot), true, false,
+ NullSdkLog.getLogger());
+
+ if (vmInfo == null)
+ {
+ String errMsg = NLS.bind(AndroidNLS.EXC_SdkUtils_CannotCreateTheVMInstance, name);
+
+ IStatus status = new Status(IStatus.ERROR, AdtPlugin.PLUGIN_ID, errMsg);
+ throw new CoreException(status);
+ }
+
+ return vmInfo;
+ }
+
+ /**
+ * Deletes a VM instance.
+ *
+ * @param name VM Name
+ */
+ public static void deleteVm(String name)
+ {
+
+ AvdManager vmManager = SdkUtils.getVmManager();
+ AvdInfo avdToDelete = vmManager != null ? vmManager.getAvd(name, false) : null;
+ if (avdToDelete != null)
+ {
+ try
+ {
+ if ((avdToDelete.getIniFile() != null) && avdToDelete.getIniFile().exists())
+ {
+ avdToDelete.getIniFile().delete();
+ }
+ String path = avdToDelete.getDataFolderPath();
+ if (path != null)
+ {
+ File avdDir = new File(path);
+ if (avdDir.exists())
+ {
+ FileUtil.deleteDirRecursively(avdDir);
+ }
+ }
+ vmManager.removeAvd(avdToDelete);
+
+ }
+ catch (Exception e)
+ {
+ StudioLogger.error("Could not delete AVD: " + e.getMessage()); //$NON-NLS-1$
+ }
+ }
+
+ }
+
+ /**
+ * Get the reference to the File that points to the filesystem location of the directory
+ * where the user data files of the VM with the given name are stored.
+ * @param vmName name of the VM whose userdata directory is to be retrieved.
+ * @return the File object that references the filesystem location of the directory where
+ * the userdata files of the given VM will be stored. Returns a null reference if SDK is not configured
+ * or if there is no VM with the given name.
+ */
+ public static File getUserdataDir(String vmName)
+ {
+ AvdInfo vminfo = SdkUtils.getValidVm(vmName);
+ File userdataDir = null;
+
+ if (vminfo != null)
+ {
+ String vmpath = vminfo.getDataFolderPath();
+ userdataDir = new File(vmpath);
+ }
+
+ return userdataDir;
+ }
+
+ /**
+ * Get the reference to the File that points to the filesystem location where the
+ * user data file of the VM with the given name is.
+ *
+ * @param vmName name of the VM whose userdata file is to be retrieved.
+ * @return the File object that references the filesystem location where the userdata
+ * of the given VM should be. Returns a null reference if SDK is not configured
+ * or if there is no VM with the given name.
+ */
+ public static File getUserdataFile(String vmName)
+ {
+ File userdataDir = getUserdataDir(vmName);
+ File userdataFile = null;
+
+ if (userdataDir != null)
+ {
+ userdataFile = new File(userdataDir, USERIMAGE_FILENAME);
+ }
+
+ return userdataFile;
+ }
+
+ /**
+ * Get the reference to the Files that point to the filesystem location where the
+ * state data files of the VM with the given name are.
+ *
+ * @param vmName name of the VM whose state data files is to be retrieved.
+ * @return the File objects that reference the filesystem location where the state data
+ * files of the given VM should be. Returns a null reference if SDK is not configured
+ * or if there is no VM with the given name.
+ */
+ public static List<File> getStateDataFiles(String vmName)
+ {
+ File userdataDir = getUserdataDir(vmName);
+ List<File> stateDataFiles = null;
+
+ if (userdataDir != null)
+ {
+ stateDataFiles = new ArrayList<File>();
+
+ for (int i = 0; i < STATEDATA_FILENAMES.length; i++)
+ {
+ stateDataFiles.add(new File(userdataDir, STATEDATA_FILENAMES[i]));
+ }
+ }
+
+ return stateDataFiles;
+ }
+
+ /**
+ * Retrieves all sample applications from a target
+ *
+ * @param target The target
+ * @return all sample applications from a target
+ */
+ public static Object[] getSamples(IAndroidTarget target)
+ {
+ List<Sample> samples = new ArrayList<Sample>();
+ File samplesFolder = new File(target.getPath(IAndroidTarget.SAMPLES));
+ samples = findSamples(samplesFolder, target);
+ return samples.toArray();
+ }
+
+ /**
+ * Find the samples inside an specific directory (recursively)
+ *
+ * @param folder the folder that can contain samples
+ * @param target the target of the samples in the folder
+ * @return a list of samples
+ */
+ private static List<Sample> findSamples(File folder, IAndroidTarget target)
+ {
+
+ List<Sample> samples = new ArrayList<Sample>();
+
+ if (folder.exists() && folder.isDirectory())
+ {
+ for (File sampleFolder : folder.listFiles())
+ {
+ if (sampleFolder.isDirectory())
+ {
+ if (Sample.isSample(sampleFolder))
+ {
+ samples.add(new Sample(sampleFolder, target));
+ }
+ else
+ {
+ samples.addAll(findSamples(sampleFolder, target));
+ }
+ }
+ }
+ }
+
+ return samples;
+ }
+
+ /**
+ * Retrieves all targets for a given SDK
+ *
+ * @param sdk The sdk
+ *
+ * @return all targets for the given SDK
+ */
+ public static Object[] getTargets(Sdk sdk)
+ {
+ Object[] targets = null;
+ if (sdk != null)
+ {
+ targets = sdk.getTargets();
+ }
+ return targets;
+ }
+
+ /**
+ * Associates a project to a target
+ *
+ * @param project The project
+ * @param target The target
+ */
+ public static void associate(IProject project, IAndroidTarget target)
+ {
+ try
+ {
+ Sdk.getCurrent().initProject(project, target);
+ }
+ catch (Exception e)
+ {
+ StudioLogger.error(SdkUtils.class, "Error associating project " + project.getName() //$NON-NLS-1$
+ + " with target " + target.getName()); //$NON-NLS-1$
+ }
+ }
+
+ /**
+ * Retrieves the target for a project
+ *
+ * @param project the project
+ *
+ * @return the target for the project
+ */
+ public static IAndroidTarget getTarget(IProject project)
+ {
+ IAndroidTarget target = null;
+ if (project != null)
+ {
+ target = Sdk.getCurrent().getTarget(project);
+ }
+ return target;
+ }
+
+ /**
+ * Retrieves the target for a project
+ *
+ * @param project the project
+ *
+ * @return the target for the project
+ */
+ public static String getMinSdkVersion(IProject project)
+ {
+ String minSdkVersion = "";
+ try
+ {
+ AndroidManifestFile androidManifestFile =
+ AndroidProjectManifestFile.getFromProject(project);
+ UsesSDKNode usesSdkNode =
+ (UsesSDKNode) androidManifestFile.getNode(AndroidManifestNode.NodeType.UsesSdk);
+ if (usesSdkNode != null)
+ {
+ minSdkVersion = usesSdkNode.getMinSdkVersion();
+ }
+
+ }
+ catch (AndroidException e)
+ {
+ StudioLogger.error("Error getting min sdk version. " + e.getMessage());
+ }
+ catch (CoreException e)
+ {
+ StudioLogger.error("Error getting min sdk version. " + e.getMessage());
+ }
+ return minSdkVersion;
+ }
+
+ /**
+ * Retrieves all Activity Actions for a project.
+ *
+ * @param project The project
+ *
+ * @return all Activity Actions for the project.
+ */
+ public static String[] getActivityActions(IProject project)
+ {
+ String[] attributeValues = new String[0];
+
+ if ((project != null) && project.isOpen())
+ {
+ IAndroidTarget target = SdkUtils.getTarget(project);
+ AndroidTargetData targetData = Sdk.getCurrent().getTargetData(target);
+
+ if (targetData != null)
+ {
+ attributeValues =
+ targetData.getAttributeValues("action", "android:name", "activity"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+ }
+
+ return attributeValues;
+ }
+
+ /**
+ * Retrieves all Service Actions for a project.
+ *
+ * @param project The project
+ *
+ * @return all Service Actions for the project.
+ */
+ public static String[] getServiceActions(IProject project)
+ {
+ String[] attributeValues = new String[0];
+
+ if ((project != null) && project.isOpen())
+ {
+ IAndroidTarget target = SdkUtils.getTarget(project);
+ AndroidTargetData targetData = Sdk.getCurrent().getTargetData(target);
+
+ if (targetData != null)
+ {
+ attributeValues =
+ targetData.getAttributeValues("action", "android:name", "service"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+ }
+
+ return attributeValues;
+ }
+
+ /**
+ * Retrieves all Broadcast Receiver Actions for a project.
+ *
+ * @param project The project
+ *
+ * @return all Broadcast Receiver Actions for the project.
+ */
+ public static String[] getReceiverActions(IProject project)
+ {
+ String[] attributeValues = new String[0];
+
+ if ((project != null) && project.isOpen())
+ {
+ IAndroidTarget target = SdkUtils.getTarget(project);
+ AndroidTargetData targetData = Sdk.getCurrent().getTargetData(target);
+
+ if (targetData != null)
+ {
+ attributeValues =
+ targetData.getAttributeValues("action", "android:name", "receiver"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+ }
+
+ return attributeValues;
+ }
+
+ /**
+ * Retrieves all Intent Filter Actions for a project.
+ *
+ * @param project The project
+ *
+ * @return all Intent Filter Actions for the project.
+ */
+ public static String[] getIntentFilterCategories(IProject project)
+ {
+ String[] attributeValues = new String[0];
+
+ if ((project != null) && project.isOpen())
+ {
+ IAndroidTarget target = SdkUtils.getTarget(project);
+ AndroidTargetData targetData = Sdk.getCurrent().getTargetData(target);
+
+ if (targetData != null)
+ {
+ attributeValues = targetData.getAttributeValues("category", "android:name"); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+ }
+
+ return attributeValues;
+ }
+
+ /**
+ * Get the api version number for a given project
+ * @param project: the project
+ * @return the api version number or 0 if some error occurs
+ */
+ public static int getApiVersionNumberForProject(IProject project)
+ {
+ int api = 0;
+ IAndroidTarget target = SdkUtils.getTarget(project);
+ if (target != null)
+ {
+ AndroidVersion version = target.getVersion();
+ if (version != null)
+ {
+ api = version.getApiLevel();
+ }
+ }
+ return api;
+ }
+
+ public static String getTargetNameForProject(IProject project)
+ {
+ IAndroidTarget target = getTarget(project);
+ return target != null ? target.getName() : ""; //$NON-NLS-1$
+ }
+
+ public static boolean isPlatformTarget(String avdName)
+ {
+ IAndroidTarget target = getValidVm(avdName).getTarget();
+ return target != null ? target.isPlatform() : false;
+ }
+
+ public static boolean isProjectTargetAPlatform(IProject project)
+ {
+ IAndroidTarget target = getTarget(project);
+ return target != null ? target.isPlatform() : false;
+ }
+
+ public static boolean isPlatformTarget(IAndroidTarget target)
+ {
+ return target != null ? target.isPlatform() : false;
+ }
+
+ /**
+ * Retrieves the APK configurations of a project
+ *
+ * @param project the project
+ * @return the APK configurations
+ */
+ public static Map<String, String> getAPKConfigurationsForProject(IProject project)
+ {
+
+ Map<String, String> apkConfigurations = null;
+
+ if ((project != null) && project.isOpen())
+ {
+ Sdk.getCurrent();
+ //This is not supported on ADT 14 preview so let's comment it for now.
+ // apkConfigurations = Sdk.getProjectState(project).getApkSettings().getLocaleFilters();
+ apkConfigurations = new HashMap<String, String>(0);
+ }
+
+ return apkConfigurations;
+
+ }
+
+ public static String getBaseTarget(String name)
+ {
+ IAndroidTarget target = getValidVm(name).getTarget();
+ while (!target.isPlatform())
+ {
+ target = target.getParent();
+ }
+ return target.getName();
+ }
+
+ /**
+ * Check if an SDK is an OPhone Sdk
+ * @return
+ */
+ public static boolean isOphoneSDK()
+ {
+ boolean result = false;
+
+ // check if the folder contains the oms jar
+ FilenameFilter omsFilenameFilter = new FilenameFilter()
+ {
+ public boolean accept(File arg0, String arg1)
+ {
+ return arg1.equals(IAndroidConstants.OPHONE_JAR);
+ }
+ };
+
+ Sdk sdk = getCurrentSdk();
+ IAndroidTarget[] targets = sdk.getTargets();
+ for (IAndroidTarget target : targets)
+ {
+ File folder = new File(target.getLocation());
+ if (folder.list(omsFilenameFilter).length > 0)
+ {
+ result = true;
+ break;
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Check if an SDK is an JIL sdk
+ * @return
+ */
+ public static boolean isJILSdk()
+ {
+ boolean result = false;
+
+ // check if the folder contains the oms jar
+ FilenameFilter jilFilenameFilter = new FilenameFilter()
+ {
+
+ public boolean accept(File arg0, String arg1)
+ {
+ return arg1.equals(IAndroidConstants.JIL_JAR);
+ }
+ };
+
+ Sdk sdk = getCurrentSdk();
+ if (sdk != null)
+ {
+ IAndroidTarget[] targets = sdk.getTargets();
+ for (IAndroidTarget target : targets)
+ {
+ File folder = new File(target.getLocation());
+ if (folder.list(jilFilenameFilter).length > 0)
+ {
+ result = true;
+ break;
+ }
+ }
+ }
+
+ return result;
+ }
+
+ public static String getEmulatorWindowName(String avdName, int port)
+ {
+ String windowName = ""; //$NON-NLS-1$
+ if (isJILSdk())
+ {
+ windowName = "JIL Emulator (" + avdName + ":" + port + ")"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+ else if (isOphoneSDK())
+ {
+ windowName = "OPhone Emulator (" + avdName + ":" + port + ")"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+ else
+ {
+ windowName = port + ":" + avdName; //$NON-NLS-1$
+ }
+ return windowName;
+ }
+
+ public static boolean isLibraryProject(IProject project)
+ {
+ return Sdk.getProjectState(project) != null ? Sdk.getProjectState(project).isLibrary()
+ : false;
+ }
+
+ /**
+ * Returns all available permissions
+ *
+ * @return String array containing the available permissions
+ */
+ public static String[] getIntentFilterPermissions(IProject project)
+ {
+ String[] attributeValues = new String[0];
+
+ if ((project != null) && project.isOpen())
+ {
+ IAndroidTarget target = SdkUtils.getTarget(project);
+ AndroidTargetData targetData = Sdk.getCurrent().getTargetData(target);
+
+ if (targetData != null)
+ {
+ attributeValues = targetData.getAttributeValues("uses-permission", "android:name"); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+ }
+
+ return attributeValues;
+ }
+
+ /**
+ * Try to repair an AVD. Currently only avds with wrong image path are repariable.
+ * Display a message with the changes to the config.ini
+ * @param avdInfo
+ * @return Status ERROR if an IO exception occured.
+ */
+ public static IStatus repairAvd(AvdInfo avdInfo)
+ {
+ IStatus status = Status.OK_STATUS;
+
+ AvdManager avdManager = Sdk.getCurrent().getAvdManager();
+ Display display = PlatformUI.getWorkbench().getDisplay();
+ ISdkLog log =
+ new MessageBoxLog(String.format("Result of updating AVD '%s':", avdInfo.getName()), //$NON-NLS-1$
+ display, false);
+ try
+ {
+ avdManager.updateAvd(avdInfo, log);
+ // display the result
+ if (log instanceof MessageBoxLog)
+ {
+ ((MessageBoxLog) log).displayResult(true);
+ }
+ SdkUtils.reloadAvds();
+
+ }
+ catch (IOException e)
+ {
+ status =
+ new Status(Status.ERROR, AndroidPlugin.PLUGIN_ID,
+ AndroidNLS.SdkUtils_COULD_NOT_REPAIR_AVD, e);
+ }
+
+ return status;
+ }
+
+ public static String getDefaultSkin(String targetName)
+ {
+ IAndroidTarget target = getTargetByName(targetName);
+ return target != null ? target.getDefaultSkin() : "HVGA";
+ }
+}
diff --git a/src/plugins/android/src/com/motorola/studio/android/adt/StudioAndroidEventManager.java b/src/plugins/android/src/com/motorola/studio/android/adt/StudioAndroidEventManager.java
new file mode 100644
index 0000000..e7fb6b1
--- /dev/null
+++ b/src/plugins/android/src/com/motorola/studio/android/adt/StudioAndroidEventManager.java
@@ -0,0 +1,205 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.adt;
+
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.eclipse.core.runtime.SafeRunner;
+import org.eclipse.jface.util.SafeRunnable;
+
+import com.android.ddmlib.AndroidDebugBridge;
+import com.android.ddmlib.AndroidDebugBridge.IClientChangeListener;
+import com.android.ddmlib.AndroidDebugBridge.IDebugBridgeChangeListener;
+
+public class StudioAndroidEventManager
+{
+ private static final HashMap<EventType, List<DdmsRunnable>> listeners =
+ new HashMap<EventType, List<DdmsRunnable>>();
+
+ public enum EventType
+ {
+ DEVICE_CONNECTED, DEVICE_DISCONNECTED, PACKAGE_INSTALLED, PACKAGE_UNINSTALLED,
+ LANGUAGE_CHANGED
+ };
+
+ public static void addEventListener(EventType type, DdmsRunnable listener)
+ {
+ synchronized (listeners)
+ {
+ List<DdmsRunnable> typeListeners = listeners.get(type);
+ if (typeListeners == null)
+ {
+ typeListeners = new LinkedList<DdmsRunnable>();
+ listeners.put(type, typeListeners);
+ }
+ typeListeners.add(listener);
+ }
+ }
+
+ public static void removeEventListener(EventType type, DdmsRunnable listener)
+ {
+ synchronized (listeners)
+ {
+ List<DdmsRunnable> typeListeners = listeners.get(type);
+ if (typeListeners == null)
+ {
+ typeListeners = new LinkedList<DdmsRunnable>();
+ listeners.put(type, typeListeners);
+ }
+ typeListeners.remove(listener);
+ }
+ }
+
+ /**
+ * Adds a IClientChangeListener asynchronously
+ *
+ * @param listener The listener to add
+ */
+ public static void asyncAddClientChangeListener(final IClientChangeListener listener)
+ {
+ new Thread(new Runnable()
+ {
+ public void run()
+ {
+ AndroidDebugBridge.addClientChangeListener(listener);
+ }
+ }).start();
+ }
+
+ /**
+ * Removes a IClientChangeListener asynchronously
+ *
+ * @param listener The listener to remove
+ */
+ public static void asyncRemoveClientChangeListener(final IClientChangeListener listener)
+ {
+ new Thread(new Runnable()
+ {
+
+ public void run()
+ {
+ AndroidDebugBridge.removeClientChangeListener(listener);
+
+ }
+ }).start();
+ }
+
+ /**
+ * Adds a IDebugBridgeChangeListener asynchronously
+ *
+ * @param listener The listener to add
+ */
+ public static void asyncAddDebugBridgeChangeListener(final IDebugBridgeChangeListener listener)
+ {
+ new Thread(new Runnable()
+ {
+
+ public void run()
+ {
+ AndroidDebugBridge.addDebugBridgeChangeListener(listener);
+
+ }
+ }).start();
+ }
+
+ /**
+ * Removes a IDebugBridgeChangeListener asynchronously
+ *
+ * @param listener The listener to remove
+ */
+ public static void asyncRemoveDebugBridgeChangeListener(
+ final IDebugBridgeChangeListener listener)
+ {
+ new Thread(new Runnable()
+ {
+ public void run()
+ {
+ AndroidDebugBridge.removeDebugBridgeChangeListener(listener);
+
+ }
+ }).start();
+ }
+
+ /**
+ * Adds a IDeviceChangeListener asynchronously
+ *
+ * @param listener The listener to add
+ */
+ public static void asyncAddDeviceChangeListeners(final DdmsRunnable connectedListener,
+ final DdmsRunnable disconnectedListener)
+ {
+ new Thread(new Runnable()
+ {
+ public void run()
+ {
+ if (connectedListener != null)
+ {
+ addEventListener(EventType.DEVICE_CONNECTED, connectedListener);
+ }
+ if (disconnectedListener != null)
+ {
+ addEventListener(EventType.DEVICE_DISCONNECTED, disconnectedListener);
+ }
+ }
+ }).start();
+ }
+
+ /**
+ * Removes a IDeviceChangeListener asynchronously
+ *
+ * @param listener The listener to remove
+ */
+ public static void asyncRemoveDeviceChangeListeners(final DdmsRunnable connectedListener,
+ final DdmsRunnable disconnectedListener)
+ {
+ new Thread(new Runnable()
+ {
+ public void run()
+ {
+ if (connectedListener != null)
+ {
+ removeEventListener(EventType.DEVICE_CONNECTED, connectedListener);
+ }
+ if (disconnectedListener != null)
+ {
+ removeEventListener(EventType.DEVICE_DISCONNECTED, disconnectedListener);
+ }
+ }
+ }).start();
+ }
+
+ public static void fireEvent(EventType type, final String serialNumber)
+ {
+ synchronized (listeners)
+ {
+ if (listeners.get(type) != null)
+ {
+ for (final DdmsRunnable runnable : listeners.get(type))
+ {
+ SafeRunner.run(new SafeRunnable()
+ {
+ public void run() throws Exception
+ {
+ runnable.run(serialNumber);
+ }
+ });
+ }
+ }
+ }
+ }
+}
diff --git a/src/plugins/android/src/com/motorola/studio/android/adt/StudioDeviceChangeListener.java b/src/plugins/android/src/com/motorola/studio/android/adt/StudioDeviceChangeListener.java
new file mode 100644
index 0000000..27cb19f
--- /dev/null
+++ b/src/plugins/android/src/com/motorola/studio/android/adt/StudioDeviceChangeListener.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.adt;
+
+import org.eclipse.ui.IStartup;
+
+import com.android.ddmlib.AndroidDebugBridge;
+import com.android.ddmlib.AndroidDebugBridge.IDeviceChangeListener;
+import com.android.ddmlib.IDevice;
+import com.android.ddmlib.IDevice.DeviceState;
+
+/**
+ * DESCRIPTION:
+ * The device change listener to be used by the whole MOTODEV Studio for Android
+ * tool. Other plugins should not register listeners in DDMS. Instead, use
+ * DDMSFacade
+ *
+ * RESPONSIBILITY:
+ * Delegate the deviceConnected and deviceDisconnected events to the registered
+ * runnables
+ *
+ * COLABORATORS:
+ * None.
+ *
+ * USAGE:
+ * This class shall be used by DDMS and DDMSFacade only
+ */
+public class StudioDeviceChangeListener implements IDeviceChangeListener, IStartup
+{
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.ui.IStartup#earlyStartup()
+ */
+ public void earlyStartup()
+ {
+ // Adding the listener in the early startup to guarantee that we will receive
+ // events for the devices that were already online before starting the workbench
+ AndroidDebugBridge.addDeviceChangeListener(this);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.android.ddmlib.AndroidDebugBridge.IDeviceChangeListener#deviceChanged(com.android.ddmlib.Device, int)
+ */
+ public void deviceChanged(IDevice device, int i)
+ {
+ if (i == IDevice.CHANGE_STATE)
+ {
+ // a handset should only be instantiated when its state change from OFFLINE to ONLINE
+ // to avoid the problem of a remote device on the OFFLINE state be presented as an ONLINE handset
+ if ((device.getState() == DeviceState.ONLINE) && (!device.isEmulator()))
+ {
+ DDMSFacade.deviceConnected(device);
+ }
+ DDMSFacade.deviceStatusChanged(device);
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.android.ddmlib.AndroidDebugBridge.IDeviceChangeListener#deviceConnected(com.android.ddmlib.Device)
+ */
+ public void deviceConnected(IDevice device)
+ {
+ // handsets should not be instantiated right after connection because at that time
+ // they appear on the OFFLINE state
+ if (device.isEmulator())
+ {
+ DDMSFacade.deviceConnected(device);
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.android.ddmlib.AndroidDebugBridge.IDeviceChangeListener#deviceDisconnected(com.android.ddmlib.Device)
+ */
+ public void deviceDisconnected(IDevice device)
+ {
+ DDMSFacade.deviceDisconnected(device);
+ }
+}
diff --git a/src/plugins/android/src/com/motorola/studio/android/application/store/handlers/OpenMotorolaMobilityDevicePropertiesHandler.java b/src/plugins/android/src/com/motorola/studio/android/application/store/handlers/OpenMotorolaMobilityDevicePropertiesHandler.java
new file mode 100644
index 0000000..b544a48
--- /dev/null
+++ b/src/plugins/android/src/com/motorola/studio/android/application/store/handlers/OpenMotorolaMobilityDevicePropertiesHandler.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.application.store.handlers;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.ui.PartInitException;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.browser.IWebBrowser;
+import org.eclipse.ui.browser.IWorkbenchBrowserSupport;
+
+import com.motorola.studio.android.common.log.StudioLogger;
+
+public class OpenMotorolaMobilityDevicePropertiesHandler extends AbstractHandler
+{
+ private static final String URL_STRING = "http://developer.motorola.com/products/";
+
+ public Object execute(ExecutionEvent event) throws ExecutionException
+ {
+ IWorkbenchBrowserSupport browserSupport = PlatformUI.getWorkbench().getBrowserSupport();
+
+ /*
+ * open the browser
+ */
+ IWebBrowser browser;
+ try
+ {
+ browser =
+ browserSupport.createBrowser(IWorkbenchBrowserSupport.LOCATION_BAR
+ | IWorkbenchBrowserSupport.NAVIGATION_BAR
+ | IWorkbenchBrowserSupport.AS_EXTERNAL, "MOTODEV", null, null);
+
+ browser.openURL(new URL(URL_STRING));
+
+ }
+ catch (PartInitException e)
+ {
+ StudioLogger.error("Error opening the Motorola products page: " + e.getMessage());
+ }
+ catch (MalformedURLException e)
+ {
+ StudioLogger.error("Error opening the Motorola products page: " + e.getMessage());
+ }
+ return null;
+ }
+
+}
diff --git a/src/plugins/android/src/com/motorola/studio/android/command/CleanProjects.java b/src/plugins/android/src/com/motorola/studio/android/command/CleanProjects.java
new file mode 100644
index 0000000..cc362a4
--- /dev/null
+++ b/src/plugins/android/src/com/motorola/studio/android/command/CleanProjects.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.motorola.studio.android.command;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IncrementalProjectBuilder;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IAdaptable;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.PlatformUI;
+
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.i18n.AndroidNLS;
+
+/**
+ * Handler responsible for cleaning selected projects by the user.
+ */
+public class CleanProjects extends AbstractHandler
+{
+
+ /**
+ * Job responsible for cleaning a list of projects
+ */
+ private final class CleanProjectsJob extends Job
+ {
+
+ private List<IProject> projectList = null;
+
+ /**
+ * @param name
+ * @param stream
+ * @param sdkPath
+ */
+ private CleanProjectsJob(List<IProject> projects)
+ {
+ super(AndroidNLS.UI_CleanProjectsJob_Name);
+ projectList = projects;
+ }
+
+ @Override
+ protected IStatus run(IProgressMonitor monitor)
+ {
+ monitor.beginTask(AndroidNLS.UI_CleanProjectsJob_Description, projectList.size());
+
+ for (IProject p : projectList)
+ {
+ // Try to clean it and update progress
+ try
+ {
+ p.build(IncrementalProjectBuilder.CLEAN_BUILD, monitor);
+ monitor.worked(1);
+ }
+ catch (CoreException e)
+ {
+ // Just log the error. Not much we can do about it.
+ StudioLogger.error(CleanProjectsJob.class,
+ "Error cleaning project " + p.getName() + ". ", e);
+ }
+ }
+
+ monitor.done();
+ return Status.OK_STATUS;
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.commands.IHandler#execute(org.eclipse.core.commands.ExecutionEvent)
+ */
+ public Object execute(ExecutionEvent event) throws ExecutionException
+ {
+
+ // Retrieve the selection used in the command
+ IWorkbench workbench = PlatformUI.getWorkbench();
+ if ((workbench != null) && !workbench.isClosing())
+ {
+ IWorkbenchWindow window = workbench.getActiveWorkbenchWindow();
+ if (window != null)
+ {
+ ISelection selection = window.getSelectionService().getSelection();
+ if (selection instanceof IStructuredSelection)
+ {
+ IStructuredSelection sselection = (IStructuredSelection) selection;
+ Iterator<?> it = sselection.iterator();
+
+ // Construct a list of valid projects to be cleaned
+ List<IProject> projectList = new ArrayList<IProject>(sselection.size());
+
+ while (it.hasNext())
+ {
+ Object resource = it.next();
+
+ // Check if the selected item is a project
+ if (resource instanceof IProject)
+ {
+ projectList.add(((IProject) resource));
+ }
+ else if (resource instanceof IAdaptable)
+ {
+ IAdaptable adaptable = (IAdaptable) resource;
+ projectList.add((IProject) adaptable.getAdapter(IProject.class));
+
+ }
+
+ }
+
+ // Instantiate the clean job and schedule it.
+ if (projectList.size() > 0)
+ {
+ CleanProjectsJob job = new CleanProjectsJob(projectList);
+ job.schedule();
+ }
+
+ }
+ }
+ }
+ return null;
+ }
+
+}
diff --git a/src/plugins/android/src/com/motorola/studio/android/command/NewProjectWizard.java b/src/plugins/android/src/com/motorola/studio/android/command/NewProjectWizard.java
new file mode 100644
index 0000000..d13fb77
--- /dev/null
+++ b/src/plugins/android/src/com/motorola/studio/android/command/NewProjectWizard.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.command;
+
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+
+import com.motorola.studio.android.wizards.project.NewAndroidProjectWizard;
+
+/**
+ * Command to open the New Project wizard through the MOTODEV menu
+ */
+public class NewProjectWizard extends NewWizardHandler
+{
+ /* (non-Javadoc)
+ * @see org.eclipse.core.commands.IHandler#execute(org.eclipse.core.commands.ExecutionEvent)
+ */
+ public Object execute(ExecutionEvent event) throws ExecutionException
+ {
+ openWizard(new NewAndroidProjectWizard());
+
+ return null;
+ }
+}
diff --git a/src/plugins/android/src/com/motorola/studio/android/command/NewWidgetProjectWizard.java b/src/plugins/android/src/com/motorola/studio/android/command/NewWidgetProjectWizard.java
new file mode 100644
index 0000000..8bdda17
--- /dev/null
+++ b/src/plugins/android/src/com/motorola/studio/android/command/NewWidgetProjectWizard.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.command;
+
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+
+import com.motorola.studio.android.wizards.widget.NewAndroidWidgetProjectWizard;
+
+public class NewWidgetProjectWizard extends NewWizardHandler
+{
+ /* (non-Javadoc)
+ * @see org.eclipse.core.commands.IHandler#execute(org.eclipse.core.commands.ExecutionEvent)
+ */
+ public Object execute(ExecutionEvent event) throws ExecutionException
+ {
+ openWizard(new NewAndroidWidgetProjectWizard());
+
+ return null;
+ }
+
+}
diff --git a/src/plugins/android/src/com/motorola/studio/android/command/NewWizardHandler.java b/src/plugins/android/src/com/motorola/studio/android/command/NewWizardHandler.java
new file mode 100644
index 0000000..880f745
--- /dev/null
+++ b/src/plugins/android/src/com/motorola/studio/android/command/NewWizardHandler.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.command;
+
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.StructuredSelection;
+import org.eclipse.jface.wizard.WizardDialog;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.ui.INewWizard;
+import org.eclipse.ui.PlatformUI;
+
+/**
+ * Abstract class intended to be used to create menu commands
+ * for items in the MOTODEV Menu
+ */
+abstract class NewWizardHandler extends AbstractHandler
+{
+ private static final int WIZARD_WIDTH = 500;
+
+ /**
+ * Opens a wizard
+ *
+ * @param wizard the wizard to be opened
+ */
+ protected void openWizard(final INewWizard wizard)
+ {
+ if (!PlatformUI.getWorkbench().isClosing())
+ {
+ Shell shell = new Shell();
+
+ ISelection selection =
+ PlatformUI.getWorkbench().getActiveWorkbenchWindow().getSelectionService()
+ .getSelection();
+ IStructuredSelection structuredSelection;
+
+ if (selection instanceof IStructuredSelection)
+ {
+ structuredSelection = (IStructuredSelection) selection;
+ }
+ else
+ {
+ structuredSelection = new StructuredSelection();
+ }
+
+ wizard.init(PlatformUI.getWorkbench(), structuredSelection);
+ WizardDialog dialog = new WizardDialog(shell, wizard);
+
+ dialog.setPageSize(WIZARD_WIDTH, SWT.DEFAULT);
+ shell.pack();
+ centralizeShell(shell);
+
+ dialog.open();
+ }
+ }
+
+ /**
+ * Centralizes a shell on the display
+ *
+ * @param shell The shell to be centralized
+ */
+ private void centralizeShell(Shell shell)
+ {
+ int displayWidth = shell.getDisplay().getClientArea().width;
+ int displayHeight = shell.getDisplay().getClientArea().height;
+
+ int x = (displayWidth - shell.getSize().x) / 2;
+ int y = (displayHeight - shell.getSize().y) / 2;
+
+ shell.setLocation(x, y);
+ }
+}
diff --git a/src/plugins/android/src/com/motorola/studio/android/command/OpenStringEditor.java b/src/plugins/android/src/com/motorola/studio/android/command/OpenStringEditor.java
new file mode 100644
index 0000000..a5e80ff
--- /dev/null
+++ b/src/plugins/android/src/com/motorola/studio/android/command/OpenStringEditor.java
@@ -0,0 +1,292 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.motorola.studio.android.command;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResourceChangeEvent;
+import org.eclipse.core.resources.IResourceChangeListener;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.jface.viewers.ILabelProvider;
+import org.eclipse.jface.viewers.ILabelProviderListener;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.IStructuredContentProvider;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.sequoyah.localization.tools.datamodel.LocaleInfo;
+import org.eclipse.sequoyah.localization.tools.extensions.classes.ILocalizationSchema;
+import org.eclipse.sequoyah.localization.tools.managers.LocalizationManager;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.PartInitException;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.dialogs.ListDialog;
+import org.eclipse.ui.handlers.HandlerUtil;
+import org.eclipse.ui.ide.IDE.SharedImages;
+import org.eclipse.ui.part.FileEditorInput;
+import org.w3c.dom.Document;
+
+import com.motorola.studio.android.AndroidPlugin;
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.common.log.UsageDataConstants;
+import com.motorola.studio.android.common.utilities.EclipseUtils;
+import com.motorola.studio.android.i18n.AndroidNLS;
+
+/**
+ * Open the Android Localization Files Editor
+ */
+public class OpenStringEditor extends AbstractHandler
+{
+ public static String STRING_EDITOR_ID =
+ "org.eclipse.sequoyah.localization.tools.extensions.implementation.android.localizationEditor";
+
+ public Object execute(ExecutionEvent event) throws ExecutionException
+ {
+
+ final List<IProject> supportedProjects =
+ LocalizationManager.getInstance().getSupportedProjects();
+
+ if (supportedProjects.size() == 0)
+ {
+ EclipseUtils.showErrorDialog(AndroidNLS.ERR_Localization_NoProjects_Title,
+ AndroidNLS.ERR_Localization_NoProjects_Description);
+ }
+ else
+ {
+
+ Shell shell = HandlerUtil.getActiveShell(event);
+
+ final ListDialog dialog = new ListDialog(shell);
+
+ dialog.setContentProvider(new IStructuredContentProvider()
+ {
+
+ public void dispose()
+ {
+ //do nothing
+ }
+
+ public void inputChanged(Viewer viewer, Object oldInput, Object newInput)
+ {
+ //do nothing
+ }
+
+ public Object[] getElements(Object inputElement)
+ {
+ return supportedProjects.toArray(new Object[supportedProjects.size()]);
+ }
+ });
+
+ dialog.setLabelProvider(new ILabelProvider()
+ {
+
+ public void removeListener(ILabelProviderListener listener)
+ {
+ //do nothing
+ }
+
+ public boolean isLabelProperty(Object element, String property)
+ {
+ return false;
+ }
+
+ public void dispose()
+ {
+ //do nothing
+ }
+
+ public void addListener(ILabelProviderListener listener)
+ {
+ //do nothing
+ }
+
+ public String getText(Object element)
+ {
+ IProject project = (IProject) element;
+ return project.getName();
+ }
+
+ public Image getImage(Object element)
+ {
+ return PlatformUI.getWorkbench().getSharedImages()
+ .getImage(SharedImages.IMG_OBJ_PROJECT);
+ }
+ });
+
+ dialog.setInput(supportedProjects);
+
+ dialog.setTitle(AndroidNLS.UI_Project_Selection);
+ dialog.create();
+ dialog.getOkButton().setEnabled(false);
+ dialog.getTableViewer().addSelectionChangedListener(new ISelectionChangedListener()
+ {
+
+ public void selectionChanged(SelectionChangedEvent event)
+ {
+ dialog.getOkButton().setEnabled(!event.getSelection().isEmpty());
+ }
+ });
+ dialog.open();
+ Object[] result = dialog.getResult();
+
+ if ((result != null) && (result.length > 0))
+ {
+
+ IProject project = (IProject) result[0];
+ try
+ {
+ ILocalizationSchema localizationSchema =
+ LocalizationManager.getInstance().getLocalizationSchema(project);
+
+ if (localizationSchema != null)
+ {
+ Map<LocaleInfo, IFile> files =
+ localizationSchema.getLocalizationFiles(project);
+ if (files.size() > 0)
+ {
+ List<String> malformedXMLFiles = new ArrayList<String>();
+ for (IFile file : files.values())
+ {
+ try
+ {
+ //Before opening check if XML is valid
+ DocumentBuilderFactory factory =
+ DocumentBuilderFactory.newInstance();
+ DocumentBuilder builder = factory.newDocumentBuilder();
+ Document document = builder.parse(file.getContents());
+ }
+ catch (Exception e)
+ {
+ malformedXMLFiles.add(file.getFullPath().toPortableString());
+ }
+ }
+ if (malformedXMLFiles.isEmpty())
+ {
+ //no malformed files - proceed opening editor
+ IFile inputFile = new ArrayList<IFile>(files.values()).get(0);
+
+ final FileEditorInput fileEditor = new FileEditorInput(inputFile);
+
+ final IWorkbenchWindow wbWindow =
+ PlatformUI.getWorkbench().getActiveWorkbenchWindow();
+ final IWorkbenchPage wbPage = wbWindow.getActivePage();
+
+ wbPage.openEditor(fileEditor, STRING_EDITOR_ID);
+
+ //listen to project close event
+ ResourcesPlugin.getWorkspace().addResourceChangeListener(
+ new ProjectCloseListener(fileEditor, wbWindow, wbPage),
+ IResourceChangeEvent.PRE_CLOSE);
+ }
+ else
+ {
+ StudioLogger
+ .error("Cannot open Localization Files Editor - XML(s) Malformed: "
+ + malformedXMLFiles);
+ EclipseUtils
+ .showErrorDialog(
+ AndroidNLS.ERR_Localization_XMLMalformed_Title,
+ NLS.bind(
+ AndroidNLS.ERR_Localization_XMLMalformed_Description,
+ malformedXMLFiles.toString()));
+ }
+ }
+ else
+ {
+ EclipseUtils.showErrorDialog(AndroidNLS.ERR_Localization_NoFiles_Title,
+ AndroidNLS.ERR_Localization_NoFiles_Description);
+ }
+
+ }
+
+ // UDC log
+ StudioLogger.collectUsageData("Localization Editor openned", //$NON-NLS-1$
+ "Localization Editor", UsageDataConstants.DESCRIPTION_DEFAULT, //$NON-NLS-1$
+ AndroidPlugin.PLUGIN_ID, AndroidPlugin.getDefault().getBundle()
+ .getVersion().toString());
+ }
+ catch (PartInitException e)
+ {
+ StudioLogger.error("Cannot open Localization Files Editor");
+ }
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * This listener handles a project close event. It closes the location files editor
+ * associated with the project, in case it is still opened.
+ */
+ private class ProjectCloseListener implements IResourceChangeListener
+ {
+ private FileEditorInput fileEditor;
+
+ private IWorkbenchWindow wbWindow;
+
+ private IWorkbenchPage wbPage;
+
+ public ProjectCloseListener(FileEditorInput fileEditor, IWorkbenchWindow wbWindow,
+ IWorkbenchPage wbPage)
+ {
+ this.fileEditor = fileEditor;
+ this.wbWindow = wbWindow;
+ this.wbPage = wbPage;
+ }
+
+ public void resourceChanged(IResourceChangeEvent event)
+ {
+ IProject closedProject = (IProject) event.getResource();
+
+ if (fileEditor.getFile().getProject().equals(closedProject))
+ {
+ final IEditorPart part = wbPage.findEditor(fileEditor);
+
+ //still opened
+ if (part != null)
+ {
+ //editor must be closed by a UI thread
+ wbWindow.getShell().getDisplay().syncExec(new Runnable()
+ {
+ public void run()
+ {
+ //saves the editor
+ wbPage.closeEditor(part, true);
+ }
+ });
+ }
+ //removes listener from workspace
+ ResourcesPlugin.getWorkspace().removeResourceChangeListener(this);
+ }
+ }
+ }
+}
diff --git a/src/plugins/android/src/com/motorola/studio/android/devices/AbstractDeviceDropSupportHandler.java b/src/plugins/android/src/com/motorola/studio/android/devices/AbstractDeviceDropSupportHandler.java
new file mode 100644
index 0000000..d020bb4
--- /dev/null
+++ b/src/plugins/android/src/com/motorola/studio/android/devices/AbstractDeviceDropSupportHandler.java
@@ -0,0 +1,185 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.devices;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.sequoyah.device.framework.model.IDeviceTypeDropSupport;
+import org.eclipse.sequoyah.device.framework.model.IInstance;
+import org.eclipse.swt.dnd.DropTargetEvent;
+import org.eclipse.swt.dnd.FileTransfer;
+import org.eclipse.swt.dnd.TransferData;
+import org.eclipse.ui.PlatformUI;
+
+import com.motorola.studio.android.adt.DDMSFacade;
+import com.motorola.studio.android.adt.DDMSUtils;
+import com.motorola.studio.android.adt.InstallPackageBean;
+import com.motorola.studio.android.i18n.AndroidNLS;
+import com.motorola.studio.android.wizards.installapp.DeployWizard.INSTALL_TYPE;
+
+public abstract class AbstractDeviceDropSupportHandler implements IDeviceTypeDropSupport
+{
+
+ private DropTargetEvent lastEvent = null;
+
+ private List<File> availableAPKs = null;
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * org.eclipse.sequoyah.device.framework.model.IDeviceTypeDropSupport#canDrop
+ * (org.eclipse.sequoyah.device.framework.model.IInstance,
+ * org.eclipse.swt.dnd.TransferData, org.eclipse.swt.dnd.DropTargetEvent)
+ */
+ public boolean canDrop(IInstance instance, TransferData data, DropTargetEvent event)
+ {
+ return (((getFiles(event) != null) && (getFiles(event).size() > 0)) || !Platform.OS_WIN32
+ .equals(Platform.getOS()));
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * org.eclipse.sequoyah.device.framework.model.IDeviceTypeDropSupport#drop
+ * (org.eclipse.sequoyah.device.framework.model.IInstance,
+ * org.eclipse.swt.dnd.TransferData, org.eclipse.swt.dnd.DropTargetEvent)
+ */
+ public void drop(final IInstance instance, TransferData data, DropTargetEvent event)
+ {
+ final List<File> files = getFiles(event);
+ Job installApksJob =
+ new Job(AndroidNLS.AbstractDeviceDropSupportHandler_InstallingApksJobName)
+ {
+ @Override
+ protected IStatus run(IProgressMonitor monitor)
+ {
+ final List<File> failedFiles = new ArrayList<File>();
+ availableAPKs = null;
+ if (files != null)
+ {
+ for (File apk : files)
+ {
+ IStatus installationStatus = installAPK(apk, instance);
+ if (!installationStatus.isOK())
+ {
+ failedFiles.add(apk);
+ }
+ }
+ }
+
+ if (failedFiles.size() > 0)
+ {
+ PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable()
+ {
+ public void run()
+ {
+ StringBuilder builder = new StringBuilder();
+
+ builder.append(AndroidNLS.AbstractDeviceDropSupportHandler_ApplicationsFailed);
+ builder.append("\n"); //$NON-NLS-1$
+ for (File f : failedFiles)
+ {
+ builder.append("\n"); //$NON-NLS-1$
+ builder.append(f.getName());
+ }
+ MessageDialog.openInformation(PlatformUI.getWorkbench()
+ .getActiveWorkbenchWindow().getShell(),
+ AndroidNLS.UI_InstallApp_InstallApp, builder.toString());
+
+ }
+ });
+ }
+
+ return Status.OK_STATUS;
+ }
+ };
+
+ installApksJob.schedule();
+ lastEvent = null;
+ }
+
+ private synchronized IStatus installAPK(File apk, final IInstance instance)
+ {
+ final InstallPackageBean installBean = new InstallPackageBean();
+ installBean.setCanOverwrite(INSTALL_TYPE.UNINSTALL);
+ installBean.setPackagePath(apk.getAbsolutePath());
+
+ return DDMSUtils.installPackage(DDMSFacade.getSerialNumberByName(instance.getName()),
+ installBean);
+
+ }
+
+ protected List<File> getFiles(DropTargetEvent event)
+ {
+
+ if ((lastEvent == null) || (lastEvent != event))
+ {
+ lastEvent = event;
+ availableAPKs = new ArrayList<File>();
+
+ if (FileTransfer.getInstance().isSupportedType(event.currentDataType))
+ {
+ String[] files =
+ (String[]) FileTransfer.getInstance().nativeToJava(event.currentDataType);
+
+ // On MacOSX the files are not available until drop
+ if (files == null)
+ {
+ try
+ {
+ files = (String[]) event.data;
+ }
+ catch (Exception e)
+ {
+ // do nothing
+ files = null;
+ }
+ }
+
+ if (files != null)
+ {
+
+ for (int i = 0; (i < files.length); i++)
+ {
+ String filePath = files[i];
+ if (filePath.toLowerCase().endsWith(".apk")) //$NON-NLS-1$
+ {
+ File f = new File(filePath);
+ if (f.exists() && f.isFile() && f.canRead())
+ {
+ availableAPKs.add(f);
+ }
+ }
+ }
+ }
+
+ }
+
+ }
+
+ return availableAPKs;
+ }
+}
diff --git a/src/plugins/android/src/com/motorola/studio/android/devices/AbstractDevicePropertyPage.java b/src/plugins/android/src/com/motorola/studio/android/devices/AbstractDevicePropertyPage.java
new file mode 100644
index 0000000..1aed097
--- /dev/null
+++ b/src/plugins/android/src/com/motorola/studio/android/devices/AbstractDevicePropertyPage.java
@@ -0,0 +1,298 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.devices;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.text.Collator;
+import java.util.Iterator;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Properties;
+
+import org.eclipse.core.resources.ResourcesPlugin;
+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.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.FileDialog;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.swt.widgets.TableColumn;
+import org.eclipse.swt.widgets.TableItem;
+import org.eclipse.ui.IWorkbenchPropertyPage;
+import org.eclipse.ui.dialogs.PropertyPage;
+
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.common.utilities.EclipseUtils;
+import com.motorola.studio.android.i18n.AndroidNLS;
+
+public abstract class AbstractDevicePropertyPage extends PropertyPage implements
+ IWorkbenchPropertyPage
+{
+
+ private Properties propValues;
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * org.eclipse.jface.preference.PreferencePage#createContents(org.eclipse
+ * .swt.widgets.Composite)
+ */
+ @SuppressWarnings("unchecked")
+ @Override
+ protected Control createContents(Composite parent)
+ {
+
+ final Composite parentComposite = new Composite(parent, SWT.NONE);
+ parentComposite.setLayout(new GridLayout(2, false));
+ parentComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+
+ final Table propTable = new Table(parentComposite, SWT.FILL | SWT.FULL_SELECTION);
+ final GridData data = new GridData();
+ data.horizontalSpan = 2;
+ data.heightHint = 150;
+ data.grabExcessVerticalSpace = true;
+ data.grabExcessHorizontalSpace = true;
+ data.horizontalAlignment = SWT.FILL;
+ data.verticalAlignment = SWT.FILL;
+
+ propTable.setLayoutData(data);
+ propTable.setLinesVisible(true);
+
+ final TableColumn keyColumn = new TableColumn(propTable, SWT.LEFT);
+ keyColumn.setText(AndroidNLS.AbstractDevicePropertyPage_Property);
+ keyColumn.setWidth(200);
+ TableColumn valueColumn = new TableColumn(propTable, SWT.LEFT);
+ valueColumn.setText(AndroidNLS.AbstractDevicePropertyPage_Value);
+ valueColumn.setWidth(200);
+ propTable.setHeaderVisible(true);
+
+ propValues = getDeviceProperties();
+
+ if ((propValues != null) && !propValues.isEmpty())
+ {
+ for (Map.Entry entry : propValues.entrySet())
+ {
+ TableItem item = new TableItem(propTable, SWT.NONE);
+ item.setText(new String[]
+ {
+ (String) entry.getKey(), (String) entry.getValue()
+ });
+ }
+
+ }
+
+ Button button = new Button(parentComposite, SWT.PUSH);
+ final GridData buttonData = new GridData();
+ buttonData.horizontalSpan = 1;
+ buttonData.grabExcessVerticalSpace = false;
+ buttonData.grabExcessHorizontalSpace = true;
+ buttonData.horizontalAlignment = SWT.END;
+ buttonData.verticalAlignment = SWT.CENTER;
+ button.setLayoutData(buttonData);
+ button.setText(AndroidNLS.AbstractDevicePropertyPage_CVS_Export);
+ button.pack();
+
+ if (propValues.isEmpty())
+ {
+ button.setEnabled(false);
+ }
+
+ button.addSelectionListener(new SelectionAdapter()
+ {
+ @Override
+ public void widgetSelected(SelectionEvent e)
+ {
+ FileDialog selectionDialog = new FileDialog(getShell(), SWT.SAVE | SWT.SHEET);
+ selectionDialog.setFilterExtensions(new String[]
+ {
+ "*.csv" //$NON-NLS-1$
+ });
+ selectionDialog.setFilterPath(ResourcesPlugin.getWorkspace().getRoot()
+ .getLocation().toOSString());
+ String csvPath = selectionDialog.open();
+ File csvFile = new File(csvPath);
+ String fileName = csvFile.getName();
+ if ((csvFile != null) && !"".equals(fileName)) //$NON-NLS-1$
+ {
+ int extensionIdx = fileName.lastIndexOf("."); //$NON-NLS-1$
+ if ((extensionIdx < 0) || "".equals(fileName.substring(extensionIdx))) //$NON-NLS-1$
+ {
+ csvFile = new File(csvFile.getAbsolutePath() + ".csv"); //$NON-NLS-1$
+ }
+ BufferedWriter outputWriter = null;
+ try
+ {
+ outputWriter = new BufferedWriter(new FileWriter(csvFile));
+ Iterator keyIt = propValues.keySet().iterator();
+ while (keyIt.hasNext())
+ {
+ String key = (String) keyIt.next();
+ String value = (String) propValues.get(key);
+ outputWriter.append(key);
+ outputWriter.append(","); //$NON-NLS-1$
+ if (value.contains(",")) //$NON-NLS-1$
+ {
+ outputWriter.append("\""); //$NON-NLS-1$
+ outputWriter.append(value);
+ outputWriter.append("\""); //$NON-NLS-1$
+ }
+ else
+ {
+ outputWriter.append(value);
+ }
+ outputWriter.newLine();
+ }
+ }
+ catch (FileNotFoundException fnf)
+ {
+ EclipseUtils.showErrorDialog(
+ AndroidNLS.AbstractDevicePropertyPage_Error_Title,
+ AndroidNLS.AbstractDevicePropertyPage_Error_Message);
+ }
+ catch (IOException ioex)
+ {
+ EclipseUtils.showErrorDialog(
+ AndroidNLS.AbstractDevicePropertyPage_Error_Title,
+ AndroidNLS.AbstractDevicePropertyPage_Error_Message);
+ }
+ finally
+ {
+ if (outputWriter != null)
+ {
+ try
+ {
+ outputWriter.flush();
+ outputWriter.close();
+ }
+ catch (IOException e1)
+ {
+ StudioLogger.error("Could not close stream. " + e1.getMessage());
+ }
+ }
+ }
+ }
+ else
+ {
+ EclipseUtils.showErrorDialog(AndroidNLS.AbstractDevicePropertyPage_Error_Title,
+ AndroidNLS.AbstractDevicePropertyPage_Error_Message_Valid_File);
+ }
+ }
+ });
+
+ propTable.pack();
+ parentComposite.pack();
+
+ SelectionAdapter sortListener = new SelectionAdapter()
+ {
+ @Override
+ public void widgetSelected(SelectionEvent e)
+ {
+
+ TableColumn sortColumn = propTable.getSortColumn();
+ TableColumn currentColumn = (TableColumn) e.widget;
+ int columnIndex = (currentColumn == keyColumn ? 0 : 1);
+
+ int dir = propTable.getSortDirection();
+ if (sortColumn == currentColumn)
+ {
+ if (dir == SWT.UP)
+ {
+ dir = SWT.DOWN;
+ }
+ else
+ {
+ dir = SWT.UP;
+ }
+ }
+ else
+ {
+ dir = SWT.UP;
+ }
+
+ sortTable(propTable, columnIndex, sortColumn, currentColumn, dir);
+ }
+ };
+ keyColumn.addSelectionListener(sortListener);
+ valueColumn.addSelectionListener(sortListener);
+ propTable.setSortColumn(keyColumn);
+ propTable.setSortDirection(SWT.UP);
+
+ sortTable(propTable, 0, keyColumn, keyColumn, SWT.UP);
+
+ noDefaultAndApplyButton();
+ return parentComposite;
+
+ }
+
+ abstract protected Properties getDeviceProperties();
+
+ private void sortTable(Table table, int columnIndex, TableColumn sortColumn,
+ TableColumn currentColumn, int dir)
+ {
+ table.setSortDirection(dir);
+ TableItem[] items = table.getItems();
+ Collator collator = Collator.getInstance(Locale.getDefault());
+ int index = columnIndex;
+ for (int i = 1; i < items.length; i++)
+ {
+ String value1 = items[i].getText(index);
+ for (int j = 0; j < i; j++)
+ {
+ String value2 = items[j].getText(index);
+ if (dir == SWT.UP)
+ {
+ if (collator.compare(value1, value2) < 0)
+ {
+ String[] values =
+ {
+ items[i].getText(0), items[i].getText(1)
+ };
+ items[i].dispose();
+ TableItem item = new TableItem(table, SWT.NONE, j);
+ item.setText(values);
+ items = table.getItems();
+ break;
+ }
+ }
+ else
+ {
+ if (collator.compare(value1, value2) > 0)
+ {
+ String[] values =
+ {
+ items[i].getText(0), items[i].getText(1)
+ };
+ items[i].dispose();
+ TableItem item = new TableItem(table, SWT.NONE, j);
+ item.setText(values);
+ items = table.getItems();
+ break;
+ }
+ }
+ }
+ }
+ table.setSortColumn(currentColumn);
+ }
+}
diff --git a/src/plugins/android/src/com/motorola/studio/android/devices/DevicesManager.java b/src/plugins/android/src/com/motorola/studio/android/devices/DevicesManager.java
new file mode 100644
index 0000000..18cd92a
--- /dev/null
+++ b/src/plugins/android/src/com/motorola/studio/android/devices/DevicesManager.java
@@ -0,0 +1,284 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.devices;
+
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.TreeSet;
+
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.sequoyah.device.common.utilities.exception.SequoyahException;
+import org.eclipse.sequoyah.device.framework.DeviceUtils;
+import org.eclipse.sequoyah.device.framework.factory.DeviceTypeRegistry;
+import org.eclipse.sequoyah.device.framework.factory.InstanceRegistry;
+import org.eclipse.sequoyah.device.framework.manager.InstanceManager;
+import org.eclipse.sequoyah.device.framework.model.IDeviceType;
+import org.eclipse.sequoyah.device.framework.model.IInstance;
+import org.eclipse.sequoyah.device.framework.model.IInstanceBuilder;
+import org.eclipse.sequoyah.device.framework.model.IService;
+import org.eclipse.sequoyah.device.framework.model.handler.IServiceHandler;
+
+import com.motorola.studio.android.adt.DDMSFacade;
+import com.motorola.studio.android.adt.ISerialNumbered;
+
+/**
+ * This manager provides information about device instances
+ * It shall work with TmL to be able to provide these information
+ */
+public class DevicesManager
+{
+
+ // instance
+ private static DevicesManager instance;
+
+ /**
+ * Singleton
+ *
+ * @return a unique DevicesManager instance
+ */
+ public static DevicesManager getInstance()
+ {
+ if (instance == null)
+ {
+ instance = new DevicesManager();
+ }
+ return instance;
+ }
+
+ /**
+ * Get all devices registered in TmL
+ *
+ * @return all devices registered in TmL
+ */
+ public Collection<ISerialNumbered> getAllDevices()
+ {
+ Collection<ISerialNumbered> devicesCollection = new LinkedHashSet<ISerialNumbered>();
+
+ List<IInstance> tmlDevices = InstanceRegistry.getInstance().getInstances();
+
+ for (IInstance tmlInstance : tmlDevices)
+ {
+ if (tmlInstance instanceof ISerialNumbered)
+ {
+ devicesCollection.add((ISerialNumbered) tmlInstance);
+ }
+ }
+ return devicesCollection;
+ }
+
+ /**
+ * Get all devices registered in TmL, sorted using the comparator passed as a parameter
+ *
+ * @param comparator the comparator that will be used to sort the devices list
+ * @return all devices registered in TmL, sorted using the comparator passed as a parameter
+ */
+ public Collection<ISerialNumbered> getAllDevices(Comparator<ISerialNumbered> comparator)
+ {
+ Collection<ISerialNumbered> sortedDevices = new TreeSet<ISerialNumbered>(comparator);
+ sortedDevices.addAll(getAllDevices());
+ return sortedDevices;
+ }
+
+ /**
+ * Get all devices registered in TmL, sorted using the default comparator
+ *
+ * @return all devices registered in TmL, sorted using the default comparator
+ */
+ public Collection<ISerialNumbered> getAllDevicesSorted()
+ {
+ return getAllDevices(getDefaultComparator());
+ }
+
+ /**
+ * Get the device, given its name
+ *
+ * @param name the device name
+ * @return a device instance
+ */
+ public ISerialNumbered getDeviceByName(String name)
+ {
+ ISerialNumbered instanceToReturn = null;
+
+ for (ISerialNumbered device : getAllDevices())
+ {
+ if (device.getDeviceName().equals(name))
+ {
+ instanceToReturn = device;
+ break;
+ }
+ }
+ return instanceToReturn;
+
+ }
+
+ /**
+ * Get the device, given its serial number
+ *
+ * @param serial the device serial name
+ * @return a device instance
+ */
+ public ISerialNumbered getDeviceBySerialNumber(String serial)
+ {
+ ISerialNumbered instanceToReturn = null;
+
+ String serialNumber = "";
+ for (ISerialNumbered device : getAllDevices())
+ {
+ if ((serialNumber = device.getSerialNumber()) != null)
+ {
+ if (serialNumber.equals(serial))
+ {
+ instanceToReturn = device;
+ break;
+ }
+ }
+ }
+ return instanceToReturn;
+
+ }
+
+ /**
+ * Get all devices of certain type
+ *
+ * @param type the device type
+ * @return all devices of the device passed as a parameter
+ */
+ @SuppressWarnings("unchecked")
+ public Collection<ISerialNumbered> getInstancesByType(Class type)
+ {
+ Collection<ISerialNumbered> instancesToReturn = new LinkedHashSet<ISerialNumbered>();
+
+ for (ISerialNumbered device : getAllDevices())
+ {
+ if (device.getClass().equals(type))
+ {
+ instancesToReturn.add(device);
+ }
+ }
+ return instancesToReturn;
+
+ }
+
+ /**
+ * Get all online devices of certain type
+ *
+ * @param type the device type
+ * @return all online devices of the device passed as a parameter
+ */
+ @SuppressWarnings("unchecked")
+ public Collection<ISerialNumbered> getOnlineDevicesByType(Class type)
+ {
+ Collection<ISerialNumbered> instancesToReturn = new HashSet<ISerialNumbered>();
+
+ String serialNumber = null;
+ for (ISerialNumbered device : getInstancesByType(type))
+ {
+ if (((serialNumber = device.getSerialNumber()) != null)
+ && (DDMSFacade.isDeviceOnline(serialNumber)))
+ {
+ instancesToReturn.add(device);
+ }
+ }
+ return instancesToReturn;
+
+ }
+
+ /**
+ * The default comparator to be used to sort ISerialNumbered instances
+ * It considers if the devices are online and after that it compares their names
+ * Online devices shall be placed in the beginning of the list
+ *
+ * @return a Comparator instance
+ */
+ public static Comparator<ISerialNumbered> getDefaultComparator()
+ {
+ return new Comparator<ISerialNumbered>()
+ {
+ public int compare(ISerialNumbered serial1, ISerialNumbered serial2)
+ {
+ int compareResult;
+
+ String name1 = serial1.getDeviceName();
+ String name2 = serial2.getDeviceName();
+ boolean dev1online = serial1.getSerialNumber() != null;
+ boolean dev2online = serial2.getSerialNumber() != null;
+
+ if ((dev1online && dev2online) || (!dev1online && !dev2online))
+ {
+ compareResult = name1.compareToIgnoreCase(name2);
+ }
+ else if (dev1online)
+ {
+ compareResult = -1;
+ }
+ else
+ // dev2online
+ {
+ compareResult = 1;
+ }
+
+ return compareResult;
+ }
+ };
+ }
+
+ /**
+ * Creates a TmL instance
+ *
+ * @param serialNumber The serial number of the device to create a TmL instance for
+ * @throws TmLException
+ */
+ public void createInstanceForDevice(String serialNumber, String deviceTypeID,
+ IInstanceBuilder instanceBuilder, String initServiceID) throws SequoyahException
+ {
+
+ ISerialNumbered instance = getDeviceBySerialNumber(serialNumber);
+ if (instance == null)
+ {
+ IDeviceType tmlDeviceType =
+ DeviceTypeRegistry.getInstance().getDeviceTypeById(deviceTypeID);
+
+ InstanceManager
+ .createProject(tmlDeviceType, instanceBuilder, new NullProgressMonitor());
+
+ IInstance newInstance = (IInstance) getDeviceBySerialNumber(serialNumber);
+ IService service = DeviceUtils.getServiceById(tmlDeviceType, initServiceID);
+ IServiceHandler handler = service.getHandler();
+ handler.run(newInstance);
+ }
+
+ }
+
+ /**
+ * Destroys the TmL instance
+ *
+ * @param device The device to delete the correspondent TmL instance
+ */
+ public void deleteInstanceOfDevice(String serialNumber)
+ {
+
+ IInstance instanceToDelete = (IInstance) getDeviceBySerialNumber(serialNumber);
+ if (instanceToDelete != null)
+ {
+ InstanceManager.deleteInstance(instanceToDelete);
+ }
+
+ }
+
+}
diff --git a/src/plugins/android/src/com/motorola/studio/android/help/handlers/OpenHelpAndroidHandler.java b/src/plugins/android/src/com/motorola/studio/android/help/handlers/OpenHelpAndroidHandler.java
new file mode 100644
index 0000000..d28ce7b
--- /dev/null
+++ b/src/plugins/android/src/com/motorola/studio/android/help/handlers/OpenHelpAndroidHandler.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.help.handlers;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.ui.PartInitException;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.browser.IWebBrowser;
+import org.eclipse.ui.browser.IWorkbenchBrowserSupport;
+
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.common.utilities.FileUtil;
+
+public class OpenHelpAndroidHandler extends AbstractHandler
+{
+ private static final String URL_STRING = "http://community.developer.motorola.com/mtrl/";
+
+ public Object execute(ExecutionEvent event) throws ExecutionException
+ {
+ IWorkbenchBrowserSupport browserSupport = PlatformUI.getWorkbench().getBrowserSupport();
+
+ /*
+ * open the browser
+ */
+ IWebBrowser browser;
+ try
+ {
+ // always use external browser on Linux
+ if (FileUtil.getOS() == FileUtil.OS_LINUX)
+ {
+ browser =
+ browserSupport.createBrowser(IWorkbenchBrowserSupport.LOCATION_BAR
+ | IWorkbenchBrowserSupport.NAVIGATION_BAR
+ | IWorkbenchBrowserSupport.AS_EXTERNAL, "MOTODEVHelpAndroid", null,
+ null);
+ }
+ else
+ {
+ browser =
+ browserSupport.createBrowser(IWorkbenchBrowserSupport.LOCATION_BAR
+ | IWorkbenchBrowserSupport.NAVIGATION_BAR, "MOTODEVHelpAndroid",
+ null, null);
+ }
+
+ browser.openURL(new URL(URL_STRING));
+
+ }
+ catch (PartInitException e)
+ {
+ StudioLogger.error("Error opening MOTODEV Discussion Board - Android page: "
+ + e.getMessage());
+ }
+ catch (MalformedURLException e)
+ {
+ StudioLogger.error("Error opening MOTODEV Discussion Board - Android page: "
+ + e.getMessage());
+ }
+ return null;
+ }
+
+}
diff --git a/src/plugins/android/src/com/motorola/studio/android/help/handlers/OpenHelpStudioHandler.java b/src/plugins/android/src/com/motorola/studio/android/help/handlers/OpenHelpStudioHandler.java
new file mode 100644
index 0000000..aeba36c
--- /dev/null
+++ b/src/plugins/android/src/com/motorola/studio/android/help/handlers/OpenHelpStudioHandler.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.help.handlers;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.ui.PartInitException;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.browser.IWebBrowser;
+import org.eclipse.ui.browser.IWorkbenchBrowserSupport;
+
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.common.utilities.FileUtil;
+
+public class OpenHelpStudioHandler extends AbstractHandler
+{
+ private static final String URL_STRING =
+ "http://community.developer.motorola.com/t5/MOTODEV-Studio-for-Android/bd-p/Studio_Android";
+
+ public Object execute(ExecutionEvent event) throws ExecutionException
+ {
+ IWorkbenchBrowserSupport browserSupport = PlatformUI.getWorkbench().getBrowserSupport();
+
+ /*
+ * open the browser
+ */
+ IWebBrowser browser;
+ try
+ {
+ // always use external browser on Linux
+ if (FileUtil.getOS() == FileUtil.OS_LINUX)
+ {
+ browser =
+ browserSupport.createBrowser(IWorkbenchBrowserSupport.LOCATION_BAR
+ | IWorkbenchBrowserSupport.NAVIGATION_BAR
+ | IWorkbenchBrowserSupport.AS_EXTERNAL, "MOTODEVHelpStudio", null,
+ null);
+ }
+ else
+ {
+ browser =
+ browserSupport.createBrowser(IWorkbenchBrowserSupport.LOCATION_BAR
+ | IWorkbenchBrowserSupport.NAVIGATION_BAR, "MOTODEVHelpStudio",
+ null, null);
+ }
+
+ browser.openURL(new URL(URL_STRING));
+
+ }
+ catch (PartInitException e)
+ {
+ StudioLogger.error("Error opening MOTODEV Studio Discussion Board - Studio page: "
+ + e.getMessage());
+ }
+ catch (MalformedURLException e)
+ {
+ StudioLogger.error("Error opening MOTODEV Studio Discussion Board - Studio page: "
+ + e.getMessage());
+ }
+ return null;
+ }
+
+}
diff --git a/src/plugins/android/src/com/motorola/studio/android/help/handlers/OpenOnlineHelpStudioHandler.java b/src/plugins/android/src/com/motorola/studio/android/help/handlers/OpenOnlineHelpStudioHandler.java
new file mode 100644
index 0000000..e7dd8ec
--- /dev/null
+++ b/src/plugins/android/src/com/motorola/studio/android/help/handlers/OpenOnlineHelpStudioHandler.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.help.handlers;
+
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.help.IWorkbenchHelpSystem;
+
+import com.motorola.studio.android.common.log.StudioLogger;
+
+public class OpenOnlineHelpStudioHandler extends AbstractHandler
+{
+
+ private static final String HREF =
+ "/com.motorola.studio.android.tooldocs.studio.helpbase/topics/c_android-studio.html";
+
+ public final Object execute(final ExecutionEvent event)
+ {
+ try
+ {
+ final IWorkbenchHelpSystem helpSystem = PlatformUI.getWorkbench().getHelpSystem();
+
+ if (helpSystem != null)
+ {
+ helpSystem.displayHelpResource(HREF);
+ }
+
+ }
+ catch (Exception e)
+ {
+ StudioLogger.error("Error opening Help Contents through MOTODEV menu: "
+ + e.getMessage());
+ }
+ return null;
+ }
+
+}
diff --git a/src/plugins/android/src/com/motorola/studio/android/i18n/AndroidNLS.java b/src/plugins/android/src/com/motorola/studio/android/i18n/AndroidNLS.java
new file mode 100644
index 0000000..94c9cc4
--- /dev/null
+++ b/src/plugins/android/src/com/motorola/studio/android/i18n/AndroidNLS.java
@@ -0,0 +1,479 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.motorola.studio.android.i18n;
+
+import org.eclipse.osgi.util.NLS;
+
+/**
+ * Class that contains the localized messages to be used through the
+ * Android SDK support and project creation
+ */
+public class AndroidNLS extends NLS
+{
+ static
+ {
+ NLS.initializeMessages("com.motorola.studio.android.i18n.androidNLS", AndroidNLS.class);
+ }
+
+ public static String AbstractDeviceDropSupportHandler_ApplicationsFailed;
+
+ public static String AbstractDeviceDropSupportHandler_InstallingApksJobName;
+
+ /*
+ * Abstract Device strings
+ */
+ public static String AbstractDevicePropertyPage_CVS_Export;
+
+ public static String AbstractDevicePropertyPage_Error_Message;
+
+ public static String AbstractDevicePropertyPage_Error_Message_Valid_File;
+
+ public static String AbstractDevicePropertyPage_Error_Title;
+
+ public static String AbstractDevicePropertyPage_Property;
+
+ public static String AbstractDevicePropertyPage_Value;
+
+ public static String AndroidProject_MsgSDKVersionIsPreview;
+
+ public static String MonkeyWizardPage_CountCommand;
+
+ public static String MotodevHProfDumpHandler_saveHProfFile;
+
+ public static String MotodevHProfDumpHandler_warnAboutHprofSavePrefMsg;
+
+ public static String MotodevHProfDumpHandler_warnAboutHprofSavePrefTitle;
+
+ public static String MotodevStudioPropertyPage_ChangeProguardSettingsProblem;
+
+ public static String UninstallAppWizardPage_ColumnPackageKiind;
+
+ public static String UninstallAppWizardPage_ColumnPackageName;
+
+ public static String UninstallAppWizardPage_Loading_Applications;
+
+ public static String UninstallAppWizardPage_PageTitle;
+
+ public static String UninstallAppWizardPage_PageDescription;
+
+ public static String UninstallAppWizardPage_SystemLabel;
+
+ public static String UninstallAppWizardPage_UserLabel;
+
+ public static String DDMSFacade_MsgConnectingToDeviceViaTCPIP;
+
+ public static String DDMSFacade_MsgSwitchingDeviceFromTCPIPToUSB;
+
+ public static String DDMSFacade_MsgSwitchingFromUSBConnection;
+
+ public static String DDMSFacade_MsgTimeoutReachedSwitchingFromTCPToUSB;
+
+ public static String DDMSFacade_Remote_File_Not_Found;
+
+ public static String DumpHprofPage_PageTitle;
+
+ public static String DumpHprofPage_PageDescription;
+
+ public static String DumpHprofPage_ColumnAppName;
+
+ public static String DumpHPROFWizardPage__Message_LoadingRunningApplications;
+
+ public static String NewAndroidProjectWizard_Message_CreatingAndroidProject;
+
+ public static String NewAndroidProjectWizard_OPhonePromptMessage;
+
+ public static String NewAndroidProjectWizard_OPhonePromptTitle;
+
+ public static String DumpHprofFile_GeneratingMemoryAnalysisOutput;
+
+ public static String DumpHprofFile_CreatingTempFile;
+
+ public static String DumpHprofFile_GettingRunningApplications;
+
+ public static String DumpHprofFile_SettingApplicationToAnalyse;
+
+ public static String DumpHprofFile_DumpingHprofFile;
+
+ public static String DumpHprofFile_GettingFileFromRemoteDevice;
+
+ public static String DumpHprofFile_OpeningMemoryAnalysisFile;
+
+ public static String DumpHprofFile_SavingFile;
+
+ public static String DumpHprofFile_SavingTempFile;
+
+ /*
+ * Warning strings area
+ */
+ public static String WRN_Obfuscation_ProjectLocationContainWhitespaces;
+
+ /*
+ * Error strings
+ */
+
+ public static String ERR_CommandError;
+
+ public static String ERR_DDMSFacade_UninstallPackageException;
+
+ public static String ERR_DDMSFacade_UninstallPackage;
+
+ public static String ERR_DDMSFacade_UninstallPackageError;
+
+ public static String ERR_DDMSFacade_SerialNumberNullPointer;
+
+ public static String ERR_DDMSFacade_IncompatibleFileLists;
+
+ public static String ERR_DDMSFacade_MonkeyError;
+
+ public static String ERR_DDMSFacade_FileNotFound;
+
+ public static String ERR_GenericTimeout;
+
+ public static String ERR_Localization_NoProjects_Title;
+
+ public static String ERR_Localization_NoProjects_Description;
+
+ public static String ERR_Localization_NoFiles_Title;
+
+ public static String ERR_Localization_NoFiles_Description;
+
+ public static String ERR_Localization_XMLMalformed_Title;
+
+ public static String ERR_Localization_XMLMalformed_Description;
+
+ public static String ERR_MonkeyWizardPage_Package;
+
+ public static String ERR_MonkeyWizardPage_CountCommand;
+
+ public static String ERR_MonkeyWizardPage_Device;
+
+ public static String ERR_PropertiesMainComposite_Monkey_NoQuotes;
+
+ public static String ERR_PropertiesMainComposite_Monkey_TextBlank;
+
+ public static String ERR_PropertiesMainComposite_Monkey_NumberRequired;
+
+ public static String ERR_PropertiesMainComposite_Monkey_NumberMustBePositiveInteger;
+
+ public static String ERR_PropertiesMainComposite_Monkey_NumberIntRange;
+
+ public static String ERR_PropertiesMainComposite_Monkey_NumberMustBeInteger;
+
+ public static String ERR_PropertiesMainComposite_Monkey_PathRequired;
+
+ public static String ERR_PropertiesMainComposite_Monkey_PathDirNotExist;
+
+ public static String ERR_PropertiesMainComposite_Monkey_PathMustBeDir;
+
+ public static String ERR_PropertiesMainComposite_Monkey_PathFileNotExist;
+
+ public static String ERR_PropertiesMainComposite_Monkey_PathMustBeFile;
+
+ public static String ERR_PropertiesMainComposite_Monkey_PathIncorrectFileType;
+
+ public static String ERR_RemoteDevice_TimeoutWhileConnecting;
+
+ public static String ERR_RemoteDevice_TimeoutWhileDisconnecting;
+
+ public static String EXC_SdkUtils_CannotCreateTheVMInstance;
+
+ public static String EXC_NewAndroidProjectWizard_AnErrorHasOccurredWhenCreatingTheProject;
+
+ /*
+ * UI strings
+ */
+ public static String UI_GenericErrorDialogTitle;
+
+ public static String UI_Preferences_Dialogs_Group_Title;
+
+ public static String UI_Preferences_Dialogs_Group_Message;
+
+ public static String UI_Preferences_Dialogs_Group_Button;
+
+ public static String UI_Preferences_Dialogs_Clean_Message;
+
+ public static String UI_InstallApp_InstallApp;
+
+ public static String UI_UninstallApp_SucessDialogTitle;
+
+ public static String UI_UninstallApp_Message;
+
+ public static String UI_UninstallApp_ERRDialogTitle;
+
+ public static String UI_UninstallApp_ERRUninstallApp;
+
+ public static String UI_ChangeLang_Language;
+
+ public static String UI_ChangeLang_Country;
+
+ public static String UI_ChangeLang_Restart_Device_Manually;
+
+ public static String UI_Project_Selection;
+
+ public static String UI_General_BrowseButtonLabel;
+
+ public static String UI_NewAndroidWidgetProjectWizard_TitleNewProjectWizard;
+
+ public static String UI_NewAndroidWidgetProjectMainPage_TitleCreateProject;
+
+ public static String UI_NewAndroidWidgetProjectMainPage_WizardProjectDescription;
+
+ public static String UI_NewAndroidWidgetProjectMainPage_SubtitleCreateProject;
+
+ public static String UI_NewAndroidWidgetProjectMainPage_LabelContents;
+
+ public static String UI_NewAndroidProjectMainPage_SubtitleCreateProject;
+
+ public static String UI_NewAndroidProjectMainPage_TitleCreateProject;
+
+ public static String UI_NewAndroidProjectMainPage_WizardProjectDescription;
+
+ public static String UI_NewAndroidProjectMainPage_LabelContents;
+
+ public static String UI_NewAndroidProjectMainPage_LabelApplication;
+
+ public static String UI_NewAndroidProjectMainPage_LabelTarget;
+
+ public static String UI_NewAndroidProjectWizard_TitleNewProjectWizard;
+
+ public static String UI_SampleSelectionPage_TitleSourcePage;
+
+ public static String UI_SampleSelectionPage_WizardTitle;
+
+ public static String UI_SampleSelectionPage_WizardDescription;
+
+ public static String UI_SampleSelectionPage_SamplesTreeLabel;
+
+ public static String UI_LocationGroup_NewProjectRadioLabel;
+
+ public static String UI_LocationGroup_NewFromSampleRadioLabel;
+
+ public static String UI_LocationGroup_NewFromExistentProjectRadioLabel;
+
+ public static String UI_LocationGroup_UseDefaultLocationCheckLabel;
+
+ public static String UI_LocationGroup_LocationLabel;
+
+ public static String UI_LocationGroup_BrowseDialogMessage;
+
+ public static String UI_ProjectNameGroup_ProjectNameLabel;
+
+ public static String UI_SdkTargetSelector_SdkTargetNameColumn;
+
+ public static String UI_SdkTargetSelector_VendorNameColumn;
+
+ public static String UI_SdkTargetSelector_APILevelColumn;
+
+ public static String UI_SdkTargetSelector_SDKVersionColumn;
+
+ public static String UI_SdkTargetSelector_EmptyValue;
+
+ public static String UI_SdkTargetSelector_NoTargetAvailable;
+
+ public static String UI_ApplicationGroup_PackageNameLabel;
+
+ public static String UI_ApplicationGroup_ActivityNameLabel;
+
+ public static String UI_ApplicationGroup_ApplicationNameLabel;
+
+ public static String UI_ApplicationGroup_MinSDKVersionLabel;
+
+ public static String UI_DeployWizard_SelectMessage;
+
+ public static String UI_DeployWizard_WizardDescription;
+
+ public static String UI_DeployWizard_WizardTitle;
+
+ public static String UI_DeployWizard_BrowseButtonText;
+
+ public static String UI_DeployWizard_PackageText;
+
+ public static String UI_DeployWizardPage_ReplaceApp;
+
+ public static String UI_DeployWizardPage_NotSignedMessage;
+
+ public static String UI_DeployWizardPage_PackageIsAFolder;
+
+ public static String UI_DeployWizardPage_InvalidPath;
+
+ public static String UI_DeployWizardPage_FileDoesNotExist;
+
+ public static String UI_DeployWizardPage_UninstallApp;
+
+ public static String UI_DeployWizardPage_DoNothingApp;
+
+ public static String UI_MonkeyOptions_CommandLine;
+
+ public static String UI_MonkeyWizardOptionsPage_PageMessage;
+
+ public static String UI_MonkeyComposite_DeviceNameLabel;
+
+ public static String UI_MonkeyComposite_SelectDeviceScreenTitle;
+
+ public static String UI_MonkeyComposite_SelectDeviceScreenMessage;
+
+ public static String UI_MonkeyComposite_TabMainName;
+
+ public static String UI_MonkeyComposite_TabOtherCmdName;
+
+ public static String UI_MonkeyError_Msg;
+
+ public static String UI_MonkeyError_Title;
+
+ public static String UI_Hprof_Handler_Dialog_Error_Title;
+
+ public static String UI_Hprof_Handler_Dialog_Unable_to_create_Hprof;
+
+ public static String UI_Hprof_Handler_Dialog_Unable_to_download_Hprof;
+
+ public static String UI_Hprof_Handler_Dialog_Error_Check_Log_Cat;
+
+ public static String UI_Hprof_Handler_Dialog_Unable_to_Save_Hprof_Data;
+
+ public static String UI_Hprof_Handler_Save_Prompt;
+
+ public static String EmulatorPreferencePage_EmulatorViewGroup;
+
+ public static String EmulatorPreferencePage_UnembedCheckBox;
+
+ public static String EmulatorPreferencePage_UnembedNote;
+
+ public static String UI_CleanProjectsJob_Name;
+
+ public static String UI_CleanProjectsJob_Description;
+
+ /*
+ * Console
+ */
+ public static String CON_ConsolePush;
+
+ public static String CON_ConsolePull;
+
+ /*
+ * Model strings area
+ */
+
+ public static String SdkUtils_COULD_NOT_REPAIR_AVD;
+
+ public static String TableWithLoadingInfo__UI_LoadingData;
+
+ public static String UI_ProjectCreation_NativeSupport;
+
+ public static String ERR_WirelessRemoteDevice_TimeoutWhileConnecting;
+
+ public static String ObfuscateProjectsHandler_1;
+
+ public static String ObfuscateProjectsHandler_2;
+
+ public static String ObfuscateProjectsHandler_3;
+
+ public static String UI_ProjectCreation_Obfuscate;
+
+ public static String UI_ProjectPropertyPage_Obfuscate;
+
+ public static String UI_ProjectPropertyPage_ObfuscateGroup;
+
+ public static String UI_Logger_ApplicationValidatorFolder;
+
+ /*
+ * Android Project
+ */
+ public static String EXC_AndroidProject_NoSamplesAvailable;
+
+ public static String EXC_AndroidProject_AnErrorHasOccurredWhenCreatingTheProject;
+
+ public static String EXC_AndroidProject_InvalidMinimumSdkVersion;
+
+ public static String ERR_AndroidProject_ProjectNameMustBeSpecified;
+
+ public static String ERR_AndroidProject_InvalidProjectName;
+
+ public static String ERR_AndroidProject_ProjectNameTooLong;
+
+ public static String ERR_AndroidProject_InvalidCharsInPackageName;
+
+ public static String ERR_AndroidProject_EmptyPackageName;
+
+ public static String ERR_AndroidProject_ActivityNameMustBeSpecified;
+
+ public static String ERR_AndroidProject_InvalidApplicationName;
+
+ public static String ERR_AndroidProject_InvalidActivityName;
+
+ public static String ERR_AndroidProject_DuplicatedProjectNameInWorkspace;
+
+ public static String ERR_AndroidProject_FileNotFoundError;
+
+ public static String ERR_AndroidProject_EmptySourceLocation;
+
+ public static String ERR_AndroidProject_LocationContainsWhitespaces;
+
+ public static String ERR_AndroidProject_ASdkTargetMustBeSpecified;
+
+ public static String ERR_AndroidProject_InvalidSdkVersion;
+
+ public static String ERR_AndroidProject_InvalidApiLevel;
+
+ public static String ERR_AndroidProject_InvalidPackageName;
+
+ public static String WARN_AndroidProject_ApplicationNameIsEmpty;
+
+ /*
+ * Project Creation
+ */
+ public static String EXC_ProjectCreationSupport_CannotCreateProjectReadOnlyWorkspace;
+
+ public static String EXC_ProjectCreationSupport_CannotCreateFolderReadOnlyWorkspace;
+
+ public static String EXC_ProjectCreationSupport_CannotCreateProjectReadOnlyDestination;
+
+ public static String UI_ProjectCreationSupport_CopyingSamplesMonitorTaskTitle;
+
+ public static String UI_ProjectCreationSupport_CopyingSamplesMonitorMessage;
+
+ public static String UI_ProjectCreationSupport_Creating_Directory_Task;
+
+ public static String UI_ProjectCreationSupport_Creating_Manifest_File_Task;
+
+ public static String UI_ProjectCreationSupport_NonEmptyFolder;
+
+ public static String UI_ProjectCreationSupport_Preparing_Java_Packages_Task;
+
+ public static String UI_ProjectCreationSupport_Preparing_Source_Folders_Task;
+
+ public static String UI_ProjectCreationSupport_Preparing_Template_File_Task;
+
+ public static String UI_ProjectCreationSupport_Reading_Template_File_Task;
+
+ public static String UI_ProjectCreationSupport_Verifying_Directory_Task;
+
+ public static String UI_ProjectCreationSupport_Configuring_Sample_Source_Task;
+
+ public static String UI_ProjectCreationSupport_Configuring_Project_Icon_Task;
+
+ public static String UI_ProjectCreationSupport_Configuring_Sample_Activity_Task;
+
+ public static String UI_ProjectCreationSupport_Configuring_Sample_Widget_Provider;
+
+ public static String ERR_ProjectCreationSupport_CaseVariantExistsError;
+
+ public static String GEN_ProjectCreationSupport_HelloWorldSimple;
+
+ public static String GEN_ProjectCreationSupport_HelloWorldWithName;
+
+ public static String GEN_Error;
+}
diff --git a/src/plugins/android/src/com/motorola/studio/android/i18n/androidNLS.properties b/src/plugins/android/src/com/motorola/studio/android/i18n/androidNLS.properties
new file mode 100644
index 0000000..7aa0617
--- /dev/null
+++ b/src/plugins/android/src/com/motorola/studio/android/i18n/androidNLS.properties
@@ -0,0 +1,258 @@
+#
+# Copyright (C) 2012 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+AbstractDeviceDropSupportHandler_ApplicationsFailed=Installation of the following applications FAILED:
+AbstractDeviceDropSupportHandler_InstallingApksJobName=Installing APKs...
+AbstractDevicePropertyPage_CVS_Export=CSV Export
+AbstractDevicePropertyPage_Error_Message=Could not create CSV file
+AbstractDevicePropertyPage_Error_Message_Valid_File=Select a valid file.
+AbstractDevicePropertyPage_Error_Title=Properties export
+AbstractDevicePropertyPage_Property=Property
+AbstractDevicePropertyPage_Value=Value
+
+AndroidProject_MsgSDKVersionIsPreview=The SDK target is a preview release. android:minSdkVersion must be set to {0}.
+
+UI_NewAndroidProjectWizard_TitleNewProjectWizard=New Android Project
+UI_LocationGroup_NewProjectRadioLabel=Create new project
+UI_LocationGroup_NewFromSampleRadioLabel=Create new project using sample
+UI_LocationGroup_UseDefaultLocationCheckLabel=Use default location
+UI_LocationGroup_NewFromExistentProjectRadioLabel=Create project from existing source
+UI_LocationGroup_LocationLabel=Location:
+UI_LocationGroup_BrowseButtonLabel=Browse...
+UI_LocationGroup_BrowseDialogMessage=Browse folder
+UI_ProjectNameGroup_ProjectNameLabel=Project name:
+UI_SdkTargetSelector_APILevelColumn=API Level
+UI_SdkTargetSelector_SdkTargetNameColumn=SDK Target
+UI_SdkTargetSelector_SDKVersionColumn=Platform
+UI_SdkTargetSelector_VendorNameColumn=Vendor
+
+DDMSFacade_MsgConnectingToDeviceViaTCPIP=Connecting to device via TCP/IP...
+DDMSFacade_MsgSwitchingDeviceFromTCPIPToUSB=Switching device connection mode from TCP/IP to USB...
+DDMSFacade_MsgSwitchingFromUSBConnection=Switching device connection mode from USB to TCP/IP...
+DDMSFacade_MsgTimeoutReachedSwitchingFromTCPToUSB=The timeout was reached while trying to switch the Android device {0} connection mode from TCP/IP to USB.
+DDMSFacade_Remote_File_Not_Found=Remote file not found {0}
+DumpHprofPage_PageTitle=Analyze Memory using MAT
+DumpHprofPage_PageDescription=Select which running application will be used to generate the HPROF file.
+DumpHprofPage_ColumnAppName=Application Name
+DumpHPROFWizardPage__Message_LoadingRunningApplications=Loading running applications...
+
+MonkeyWizardPage_CountCommand=Event count
+MotodevHProfDumpHandler_saveHProfFile=Save HPROF file
+MotodevHProfDumpHandler_warnAboutHprofSavePrefMsg=The HPROF Action preference is set to "Save to disk".\nIf you want to analyze this with MAT without manually opening the file, change the preference to "Open in Eclipse".\n\nDo you want to open the preference page now?
+MotodevHProfDumpHandler_warnAboutHprofSavePrefTitle=Default HPROF Action
+MotodevStudioPropertyPage_ChangeProguardSettingsProblem=A problem occurred while changing Proguard settings
+WRN_Obfuscation_ProjectLocationContainWhitespaces=The project location contains whitespace characters that will cause problems during project obfuscation.
+NewAndroidProjectWizard_Message_CreatingAndroidProject=Creating Android project...
+
+NewAndroidProjectWizard_OPhonePromptMessage=Do you want to add the file oms.jar to the project's classpath?
+NewAndroidProjectWizard_OPhonePromptTitle=OPhone target selected
+UninstallAppWizardPage_ColumnPackageKiind=Kind
+UninstallAppWizardPage_ColumnPackageName=Package Name
+UninstallAppWizardPage_Loading_Applications=Loading Applications
+UninstallAppWizardPage_PageDescription=Select the packages you want to uninstall from your device
+UninstallAppWizardPage_PageTitle=Uninstall Application
+UninstallAppWizardPage_SystemLabel=System
+UninstallAppWizardPage_UserLabel=User
+
+
+ERR_CommandError=The command stopped with error code: {0}
+ERR_DDMSFacade_SerialNumberNullPointer=Android Emulator was not defined. The operation will not be performed.
+ERR_DDMSFacade_IncompatibleFileLists=The file lists provided for push or pull operations differ in size. The operation is being aborted.
+ERR_DDMSFacade_UninstallPackageException=An exception occurred while uninstalling the application
+ERR_DDMSFacade_UninstallPackage=Package file does not exist
+ERR_DDMSFacade_UninstallPackageError=Error uninstalling package
+ERR_DDMSFacade_MonkeyError=Error executing monkey
+ERR_DDMSFacade_FileNotFound=File not found
+ERR_RemoteDevice_TimeoutWhileConnecting=The timeout was reached while trying to connect to "{0}" Android remote device.
+ERR_WirelessRemoteDevice_TimeoutWhileConnecting=The timeout was reached while trying to switch the connection mode of "{0}" to TCP/IP.
+ERR_RemoteDevice_TimeoutWhileDisconnecting=The timeout was reached while trying to disconnect from "{0}" Android remote device.
+ERR_GenericTimeout=Timeout has been reached
+ERR_Localization_NoProjects_Title=Localization Files Editor
+ERR_Localization_NoProjects_Description=There are no supported projects in the workspace
+ERR_Localization_NoFiles_Title=Missing localization files
+ERR_Localization_NoFiles_Description=The selected project is supported but has no localization files
+ERR_Localization_XMLMalformed_Title = Malformed XML from localization file
+ERR_Localization_XMLMalformed_Description = The selected project contains the following localization file(s) with malformed XML: {0}.
+ERR_MonkeyWizardPage_CountCommand=Event count must be supplied.
+ERR_MonkeyWizardPage_Package=Select the packages Monkey should visit.
+ERR_MonkeyWizardPage_Device=Invalid device.
+
+ERR_PropertiesMainComposite_Monkey_NoQuotes=The value for {0} contains quotes, which are not allowed.
+ERR_PropertiesMainComposite_Monkey_TextBlank=Provide a value for {0}.
+ERR_PropertiesMainComposite_Monkey_NumberRequired=Provide a value for {0}.
+ERR_PropertiesMainComposite_Monkey_NumberMustBeInteger=The value for {0} must be an integer.
+ERR_PropertiesMainComposite_Monkey_NumberMustBePositiveInteger=The value for {0} must be a positive integer.
+ERR_PropertiesMainComposite_Monkey_NumberIntRange=The value for {0} must be a value between {1} and {2}.
+ERR_PropertiesMainComposite_Monkey_PathRequired=Provide a path for {0}.
+ERR_PropertiesMainComposite_Monkey_PathDirNotExist=The directory provided for {0} doesn't exist. Specify a valid folder.
+ERR_PropertiesMainComposite_Monkey_PathMustBeDir=Select a folder for {0}, not a file.
+ERR_PropertiesMainComposite_Monkey_PathFileNotExist=The file specified for {0} doesn't exist. Specify a valid path.
+ERR_PropertiesMainComposite_Monkey_PathMustBeFile=Select a file for {0}, not a folder.
+ERR_PropertiesMainComposite_Monkey_PathIncorrectFileType=The file specified for {0} is not a {1} file. Specify a valid path.
+
+EXC_SdkUtils_CannotCreateTheVMInstance=Could not create the Android Virtual Device: {0}
+
+
+EXC_NewAndroidProjectWizard_AnErrorHasOccurredWhenCreatingTheProject=An error has occurred while creating the project: {0}
+UI_SdkTargetSelector_EmptyValue=--
+UI_SdkTargetSelector_NoTargetAvailable=No target available
+UI_NewAndroidProjectMainPage_SubtitleCreateProject=Creates a new Android project resource.
+UI_NewAndroidWidgetProjectWizard_TitleNewProjectWizard=New Android Widget Project
+UI_NewAndroidWidgetProjectMainPage_TitleCreateProject=Create an Android widget project using MOTODEV Studio for Android
+UI_NewAndroidWidgetProjectMainPage_WizardProjectDescription=Creates an Android widget project in your workspace or another location.
+UI_NewAndroidWidgetProjectMainPage_SubtitleCreateProject=Creates a new Android widget project resource.
+UI_NewAndroidWidgetProjectMainPage_LabelContents=Contents
+UI_NewAndroidProjectMainPage_TitleCreateProject=Create an Android project using MOTODEV Studio for Android
+UI_NewAndroidProjectMainPage_WizardProjectDescription=Creates an Android project in your workspace or another location.
+UI_NewAndroidProjectMainPage_LabelContents=Contents
+UI_NewAndroidProjectMainPage_LabelApplication=Application
+UI_NewAndroidProjectMainPage_LabelTarget=Target
+UI_SampleSelectionPage_TitleSourcePage=Source Page
+UI_SampleSelectionPage_WizardTitle=Create projects based on source templates
+UI_SampleSelectionPage_WizardDescription=Create an Android project based on a sample.
+UI_SampleSelectionPage_SamplesTreeLabel=Available templates:
+UI_ApplicationGroup_MinSDKVersionLabel=Min SDK version:
+UI_ApplicationGroup_ApplicationNameLabel = Application name:
+UI_ApplicationGroup_ActivityNameLabel = Activity name:
+UI_ApplicationGroup_PackageNameLabel=Package name:
+
+UI_GenericErrorDialogTitle=An error occurred during project creation
+UI_Preferences_Dialogs_Group_Title = MOTODEV Studio for Android Dialogs
+UI_Preferences_Dialogs_Group_Message = Clear all 'Do not show me again' and 'Always proceed without asking' settings and show all hidden dialogs again
+UI_Preferences_Dialogs_Group_Button = Clear
+UI_Preferences_Dialogs_Clean_Message = Resetting the dialog preferences will show all hidden dialogs. Do you want to continue?
+UI_InstallApp_InstallApp=Install application.
+UI_UninstallApp_SucessDialogTitle=Uninstall applications
+UI_UninstallApp_Message=Applications successfully uninstalled
+UI_UninstallApp_ERRDialogTitle=Error uninstalling applications
+UI_UninstallApp_ERRUninstallApp=Some of the applications could not be uninstalled
+UI_Project_Selection=Project Selection
+UI_ChangeLang_Restart_Device_Manually=The language change will take effect only after the device is restarted. Manually stop and start the emulator.
+UI_ChangeLang_Language=Language:
+UI_ChangeLang_Country=Country:
+UI_MonkeyWizardOptionsPage_PageMessage=Supply the options Monkey should use
+UI_MonkeyComposite_DeviceNameLabel=Device
+UI_MonkeyComposite_SelectDeviceScreenTitle=Device Selection
+UI_MonkeyComposite_SelectDeviceScreenMessage=Select a device instance
+UI_MonkeyComposite_TabMainName=Main
+UI_MonkeyComposite_TabOtherCmdName=Options
+UI_MonkeyOptions_CommandLine=Monkey Arguments:
+UI_MonkeyError_Msg=An error occurred while creating the Monkey launch configuration.
+UI_MonkeyError_Title=Error Creating Monkey Launch Configuration
+
+UI_DeployWizard_SelectMessage=Select the package
+UI_DeployWizard_WizardDescription=Installs an Android application on an emulator
+UI_DeployWizard_WizardTitle=Install Application
+UI_DeployWizard_BrowseButtonText=Browse
+UI_DeployWizard_PackageText=Application package
+UI_DeployWizardPage_NotSignedMessage=Package is not signed or is malformed
+UI_DeployWizardPage_PackageIsAFolder=The selected package is not a valid APK file
+UI_DeployWizardPage_InvalidPath=The entered path is invalid
+UI_DeployWizardPage_FileDoesNotExist=The file does not exist
+UI_DeployWizardPage_ReplaceApp=Overwrite if application already exists
+UI_DeployWizardPage_UninstallApp = Uninstall application before install
+UI_DeployWizardPage_DoNothingApp = Do nothing if application is already installed
+
+CON_ConsolePush=Push {0} to {1}
+CON_ConsolePull=Pull {0} to {1}
+
+TableWithLoadingInfo__UI_LoadingData=Loading data
+
+DumpHprofFile_GeneratingMemoryAnalysisOutput = Generating Memory Analysis output
+DumpHprofFile_GettingRunningApplications = Getting running applications
+DumpHprofFile_SettingApplicationToAnalyse = Setting application to analyze
+DumpHprofFile_DumpingHprofFile = Dumping HPROF file
+
+DumpHprofFile_CreatingTempFile = Creating temp file on local machine
+DumpHprofFile_GettingFileFromRemoteDevice = Getting file from remote device
+DumpHprofFile_OpeningMemoryAnalysisFile = Opening Memory Analysis file
+DumpHprofFile_SavingFile = Saving file
+DumpHprofFile_SavingTempFile = Saving temp file
+SdkUtils_COULD_NOT_REPAIR_AVD=Could not repair AVD
+
+UI_Hprof_Handler_Dialog_Error_Title=HPROF Error
+UI_Hprof_Handler_Dialog_Unable_to_create_Hprof=Unable to create HPROF file for application
+UI_Hprof_Handler_Dialog_Unable_to_download_Hprof=Unable to retrieve HPROF file from device
+UI_Hprof_Handler_Dialog_Error_Check_Log_Cat=Check the logcat view for more information.
+
+UI_Hprof_Handler_Dialog_Unable_to_Save_Hprof_Data=Failed to save HPROF data into temp file
+UI_Hprof_Handler_Save_Prompt=Save HPROF file
+
+EmulatorPreferencePage_EmulatorViewGroup=Android Emulator View
+EmulatorPreferencePage_UnembedCheckBox=Show emulators externally upon closing the Emulator view.
+EmulatorPreferencePage_UnembedNote=Note: Uncheck this if you are experiencing problems with the emulator unexpectedly stopping after closing the view.\nThis option only affects the native emulator window solution.
+ObfuscateProjectsHandler_1=Enable/Disable Obfuscation
+ObfuscateProjectsHandler_2=Obfuscated projects
+ObfuscateProjectsHandler_3=Select the projects you want to obfuscate when exporting.
+
+UI_ProjectCreation_NativeSupport=Add native support
+
+UI_CleanProjectsJob_Name=Clean project(s) job
+UI_CleanProjectsJob_Description=Cleaning projects...
+
+UI_ProjectCreation_Obfuscate=Obfuscate Java classes
+UI_ProjectPropertyPage_Obfuscate=Obfuscate Java classes
+UI_ProjectPropertyPage_ObfuscateGroup=Obfuscation
+UI_Logger_ApplicationValidatorFolder=App Validator
+
+EXC_AndroidProject_AnErrorHasOccurredWhenCreatingTheProject=An error has occurred while creating the project.
+EXC_AndroidProject_InvalidMinimumSdkVersion=The minimum required SDK version must not be greater than the maximum SDK version provided by the target.
+EXC_AndroidProject_NoSamplesAvailable = There are no available samples for this target.
+
+ERR_AndroidProject_ProjectNameMustBeSpecified=Project name must be specified
+ERR_AndroidProject_DuplicatedProjectNameInWorkspace=A project with that name already exists in the workspace.
+ERR_AndroidProject_FileNotFoundError=File {0} not found in {1}.
+ERR_AndroidProject_EmptySourceLocation=Select a valid source location.
+ERR_AndroidProject_LocationContainsWhitespaces=Invalid project location. The native project path must not contain whitespace characters.
+ERR_AndroidProject_ASdkTargetMustBeSpecified=An SDK target must be specified.
+ERR_AndroidProject_InvalidSdkVersion=The minimum SDK version must be an integer greater than 0.
+ERR_AndroidProject_InvalidApiLevel=The API level for the selected SDK target does not match the minimum SDK version.
+ERR_AndroidProject_InvalidPackageName=The package name must have at least two identifiers.
+ERR_AndroidProject_InvalidProjectName=The project name ''{0}'' is invalid.
+ERR_AndroidProject_ProjectNameTooLong=The combination of project name and package name will result in a file name that is too long.
+ERR_AndroidProject_InvalidCharsInPackageName=The package name ''{0}'' is invalid.
+ERR_AndroidProject_EmptyPackageName=The package name is empty.
+ERR_AndroidProject_ActivityNameMustBeSpecified = Activity name must be specified
+ERR_AndroidProject_InvalidActivityName = The activity name ''{0}'' is invalid.
+ERR_AndroidProject_InvalidApplicationName = The application name ''{0}'' is invalid.
+
+WARN_AndroidProject_ApplicationNameIsEmpty=The application name is empty.
+
+EXC_ProjectCreationSupport_CannotCreateProjectReadOnlyWorkspace=Cannot create the project because the workspace is not writable.
+EXC_ProjectCreationSupport_CannotCreateFolderReadOnlyWorkspace=Cannot create the folder ''{0}'' because the workspace is not writable.
+EXC_ProjectCreationSupport_CannotCreateProjectReadOnlyDestination=Cannot create the project because the destination folder ''{0}'' is not writable.
+UI_ProjectCreationSupport_CopyingSamplesMonitorTaskTitle=Creating Sample Project
+UI_ProjectCreationSupport_Configuring_Project_Icon_Task=Configuring Project Icon
+UI_ProjectCreationSupport_Configuring_Sample_Activity_Task=Creating Sample Activity
+UI_ProjectCreationSupport_Configuring_Sample_Widget_Provider=Creating Sample Widget Provider
+UI_ProjectCreationSupport_Configuring_Sample_Source_Task=Configuring Sample Source Code
+UI_ProjectCreationSupport_CopyingSamplesMonitorMessage=Copying Samples
+UI_ProjectCreationSupport_Creating_Directory_Task=Creating directory
+UI_ProjectCreationSupport_Creating_Manifest_File_Task=Creating Manifest File
+ERR_ProjectCreationSupport_CaseVariantExistsError=A project with the same name already exists
+GEN_ProjectCreationSupport_HelloWorldSimple=Hello
+GEN_ProjectCreationSupport_HelloWorldWithName=Hello World
+GEN_Error=Error
+
+UI_General_BrowseButtonLabel=Browse...
+
+UI_ProjectCreationSupport_NonEmptyFolder=Selected location folder is not empty.
+UI_ProjectCreationSupport_Preparing_Java_Packages_Task=Preparing Java packages
+UI_ProjectCreationSupport_Preparing_Source_Folders_Task=Preparing source folders
+UI_ProjectCreationSupport_Preparing_Template_File_Task=Preparing template file
+UI_ProjectCreationSupport_Reading_Template_File_Task=Reading template file
+UI_ProjectCreationSupport_Verifying_Directory_Task=Verifying directory and permissions
+
diff --git a/src/plugins/android/src/com/motorola/studio/android/logger/AppValidatorLogCollector.java b/src/plugins/android/src/com/motorola/studio/android/logger/AppValidatorLogCollector.java
new file mode 100644
index 0000000..23dd907
--- /dev/null
+++ b/src/plugins/android/src/com/motorola/studio/android/logger/AppValidatorLogCollector.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.motorola.studio.android.logger;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.Path;
+
+import com.motorola.studio.android.i18n.AndroidNLS;
+import com.motorola.studio.android.logger.collector.core.ILogFile;
+
+/**
+ * Class to collect log from application validator
+ */
+public class AppValidatorLogCollector implements ILogFile
+{
+
+ public String getLogName()
+ {
+ return AndroidNLS.UI_Logger_ApplicationValidatorFolder;
+ }
+
+ public List<IPath> getLogFilePath()
+ {
+ ArrayList<IPath> logs = new ArrayList<IPath>();
+ String userHomeProp = System.getProperty("user.home");
+ File userHomeFile = new File(userHomeProp, "appValidator.log");
+ if (userHomeFile.exists())
+ {
+ IPath path = new Path(userHomeFile.getAbsolutePath());
+ logs.add(path);
+ }
+ return logs;
+ }
+
+ public String getOutputSubfolderName()
+ {
+ return "ApplicationValidator";
+ }
+
+}
diff --git a/src/plugins/android/src/com/motorola/studio/android/logger/DevicePropertyLogger.java b/src/plugins/android/src/com/motorola/studio/android/logger/DevicePropertyLogger.java
new file mode 100644
index 0000000..e4cdc56
--- /dev/null
+++ b/src/plugins/android/src/com/motorola/studio/android/logger/DevicePropertyLogger.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.logger;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.NullProgressMonitor;
+
+import com.motorola.studio.android.AndroidPlugin;
+import com.motorola.studio.android.adt.DDMSFacade;
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.logger.collector.core.ILogFile;
+
+/**
+ * This class is the Implementation of ILogFile that is responsible to collect device properties and write them to files.
+ * These files will be used by the Collect Logs functionality
+ */
+public class DevicePropertyLogger implements ILogFile
+{
+
+ private final Collection<String> serialNumbers;
+
+ private final Map<String, Map<String, String>> properties;
+
+ public DevicePropertyLogger()
+ {
+ serialNumbers = DDMSFacade.getConnectedSerialNumbers();
+ properties = new HashMap<String, Map<String, String>>(serialNumbers.size());
+ for (String serialNumber : serialNumbers)
+ {
+ Map<String, String> propertiesMap = new HashMap<String, String>(140);
+ String deviceName = DDMSFacade.getNameBySerialNumber(serialNumber);
+ try
+ {
+ Collection<String> lines =
+ DDMSFacade
+ .execRemoteApp(serialNumber, "getprop", new NullProgressMonitor());
+ for (String line : lines)
+ {
+ String[] split = line.split(":");
+ StringBuffer buffer = new StringBuffer();
+ for (int i = 1; i < split.length; i++)
+ {
+ buffer.append(split[i]);
+ }
+
+ if (!"".equals(split[0]))
+ {
+ propertiesMap.put(split[0], buffer.toString());
+ }
+ }
+ }
+ catch (IOException e)
+ {
+ StudioLogger.error(getClass(), "Unable to execute getprop command on device "
+ + deviceName, e);
+ }
+ properties.put((deviceName), propertiesMap);
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.logger.collector.core.ILogFile#getLogFilePath()
+ */
+ public List<IPath> getLogFilePath()
+ {
+ ArrayList<IPath> logs = new ArrayList<IPath>(properties.keySet().size());
+ IPath LOG_PATH = AndroidPlugin.getDefault().getStateLocation();
+ for (String devicename : properties.keySet())
+ {
+ IPath log = LOG_PATH.append(devicename + "_devProperties.log");
+ writeLogFile(log, properties.get(devicename));
+ logs.add(log);
+ }
+
+ return logs;
+ }
+
+ private void writeLogFile(IPath log, Map<String, String> devProperties)
+ {
+ File logFile = log.toFile();
+ BufferedWriter bw = null;
+ try
+ {
+ bw = new BufferedWriter(new FileWriter(logFile));
+
+ for (String propKey : devProperties.keySet())
+ {
+ bw.append(propKey + "=" + devProperties.get(propKey)
+ + System.getProperty("line.separator"));
+ }
+ }
+ catch (IOException e)
+ {
+ StudioLogger.error(getClass(),
+ "An error occurred while trying to write device Properties log file, "
+ + logFile.getAbsolutePath(), e);
+ }
+ finally
+ {
+ try
+ {
+ bw.flush();
+ bw.close();
+ }
+ catch (IOException e)
+ {
+ StudioLogger.error("Could not close stream while writing device property log. "
+ + e.getMessage());
+ }
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.logger.collector.core.ILogFile#getLogName()
+ */
+ public String getLogName()
+ {
+ return "Device properties";
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.logger.collector.core.ILogFile#getOutputSubfolderName()
+ */
+ public String getOutputSubfolderName()
+ {
+ return "Devices";
+ }
+}
diff --git a/src/plugins/android/src/com/motorola/studio/android/model/AndroidProject.java b/src/plugins/android/src/com/motorola/studio/android/model/AndroidProject.java
new file mode 100644
index 0000000..b4ff017
--- /dev/null
+++ b/src/plugins/android/src/com/motorola/studio/android/model/AndroidProject.java
@@ -0,0 +1,953 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.model;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+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.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.jdt.core.JavaConventions;
+import org.eclipse.jface.wizard.IWizardContainer;
+import org.eclipse.osgi.util.NLS;
+
+import com.android.sdklib.IAndroidTarget;
+import com.motorola.studio.android.AndroidPlugin;
+import com.motorola.studio.android.adt.Sample;
+import com.motorola.studio.android.adt.SdkUtils;
+import com.motorola.studio.android.common.IAndroidConstants;
+import com.motorola.studio.android.common.exception.AndroidException;
+import com.motorola.studio.android.common.utilities.AndroidStatus;
+import com.motorola.studio.android.common.utilities.EclipseUtils;
+import com.motorola.studio.android.i18n.AndroidNLS;
+
+/**
+ * Class that implements the model for the new project wizard
+ */
+public class AndroidProject implements IWizardModel
+{
+ private static final IWorkspace WORKSPACE = ResourcesPlugin.getWorkspace();
+
+ private static final String SDK_VERSION = "1.5"; //$NON-NLS-1$
+
+ private static final String BIN_FOLDER = "bin"; //$NON-NLS-1$
+
+ private static final String CLASS_EXTENSION = ".class"; //$NON-NLS-1$
+
+ private static final String R_DRAWABLE_CLASS = "R$drawable" + CLASS_EXTENSION; //$NON-NLS-1$
+
+ private static final String SETTINGS_FOLDER = ".settings"; //$NON-NLS-1$
+
+ private static final String SETTINGS_FILE = "org.eclipse.core.resources.prefs"; //$NON-NLS-1$
+
+ public static final String ANDROID_NATURE = "com.android.ide.eclipse.adt.AndroidNature"; //$NON-NLS-1$
+
+ private static final int MAX_PATH_LENGTH = 255;
+
+ /**
+ * Represents the Type of new Projects
+ */
+ public static enum SourceTypes
+ {
+ SAMPLE, EXISTING, NEW, WIDGET
+ };
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#finalize()
+ */
+ @Override
+ public void finalize() throws Throwable
+ {
+ if (listener != null)
+ {
+ AndroidPlugin.getDefault().removeSDKLoaderListener(listener);
+ }
+ super.finalize();
+ }
+
+ private SourceTypes sourceType = SourceTypes.NEW;
+
+ private Sample sample = null;
+
+ private String location = null;
+
+ private boolean useDefaultLocation = true;
+
+ private boolean addNativeSupport = false;
+
+ private boolean needToObfuscate = false;
+
+ /**
+ * Return the Type of the new Project
+ *
+ * @return
+ */
+ public SourceTypes getSourceType()
+ {
+ return sourceType;
+ }
+
+ /**
+ * Change the Type of the new Project
+ *
+ * @param sourceType
+ */
+ public void setSourceType(SourceTypes sourceType)
+ {
+ this.sourceType = sourceType;
+ }
+
+ /**
+ * Check if adding native support
+ *
+ * @return
+ */
+ public boolean isAddingNativeSupport()
+ {
+ return addNativeSupport;
+ }
+
+ /**
+ * Set add native support property
+ *
+ * @param hasNativeSupport
+ */
+ public void setAddNativeSupport(boolean addNativeSupport)
+ {
+ this.addNativeSupport = addNativeSupport;
+ }
+
+ public void setNeedToObfuscate(boolean needToObfuscate)
+ {
+ this.needToObfuscate = needToObfuscate;
+ }
+
+ public boolean needToObfuscate()
+ {
+ return needToObfuscate;
+ }
+
+ /**
+ * Returns the Project location.
+ *
+ * @see #isUsingDefaultLocation()
+ * @return
+ */
+ public String getLocation()
+ {
+ return location;
+ }
+
+ /**
+ * Return the sample to use
+ *
+ * @see #getSourceType()
+ * @return
+ */
+ public Sample getSample()
+ {
+ return sample;
+ }
+
+ /**
+ * Change the used sample
+ *
+ * @see #getSourceType()
+ * @param sample
+ */
+ public void setSample(Sample sample)
+ {
+ this.sample = sample;
+ }
+
+ /**
+ * Change the Project Location
+ *
+ * @param location
+ */
+ public void setLocation(String location)
+ {
+ this.location = location;
+ }
+
+ /**
+ * Check if using the default location
+ *
+ * @return
+ */
+ public boolean isUsingDefaultLocation()
+ {
+ return useDefaultLocation;
+ }
+
+ /**
+ * Set use default location property
+ *
+ * @param useDefaultLocation
+ */
+ public void setUseDefaultLocation(boolean useDefaultLocation)
+ {
+ this.useDefaultLocation = useDefaultLocation;
+ }
+
+ private String minSdkVersion = null;
+
+ private String name = ""; //$NON-NLS-1$
+
+ private String activityName = ""; //$NON-NLS-1$
+
+ private String packageName = ""; //$NON-NLS-1$
+
+ private IAndroidTarget sdkTarget = null;
+
+ private List<String> sourceFolders = null;
+
+ private boolean usingDefaultPackage = true;
+
+ private final String PACKAGE_ROOT = "com."; //$NON-NLS-1$
+
+ private Runnable listener = null;
+
+ private String appName;
+
+ /**
+ * Check if using the defaul package name
+ *
+ * @return
+ */
+ public boolean isUsingDefaultPackage()
+ {
+ return usingDefaultPackage;
+ }
+
+ /**
+ * Set if using the Default package name
+ *
+ * @param usingDefaultPackage
+ */
+ public void setUsingDefaultPackage(boolean usingDefaultPackage)
+ {
+ this.usingDefaultPackage = usingDefaultPackage;
+ }
+
+ /**
+ * Creates a new AndroidProject
+ */
+ public AndroidProject()
+ {
+ // initialize SDK Targets
+ if (SdkUtils.getCurrentSdk() == null)
+ {
+ // this listener will be called when a SDK is configured
+ listener = new Runnable()
+ {
+
+ public void run()
+ {
+ IAndroidTarget[] targets = SdkUtils.getAllTargets();
+ if ((targets != null) && (targets.length > 0))
+ {
+ int maxApiVersion = -1;
+ for (IAndroidTarget aTarget : targets)
+ {
+ int apiVersion = aTarget.getVersion().getApiLevel();
+ if (maxApiVersion < apiVersion)
+ {
+ sdkTarget = aTarget;
+ maxApiVersion = apiVersion;
+ }
+ }
+ // set the API string as the min SDK version - this is the way ADT does
+ minSdkVersion = sdkTarget.getVersion().getApiString();
+ }
+ AndroidPlugin.getDefault().removeSDKLoaderListener(listener);
+ listener = null;
+ }
+
+ };
+ AndroidPlugin.getDefault().addSDKLoaderListener(listener);
+ }
+ else
+ {
+ IAndroidTarget[] targets = SdkUtils.getAllTargets();
+ if ((targets != null) && (targets.length > 0))
+ {
+ int maxApiVersion = -1;
+ for (IAndroidTarget aTarget : targets)
+ {
+ int apiVersion = aTarget.getVersion().getApiLevel();
+ if (maxApiVersion < apiVersion)
+ {
+ sdkTarget = aTarget;
+ maxApiVersion = apiVersion;
+ }
+ }
+ // set the API string as the min SDK version - this is the way ADT does
+ minSdkVersion = sdkTarget.getVersion().getApiString();
+ }
+ }
+
+ sourceFolders = new ArrayList(3);
+ sourceFolders.add(IAndroidConstants.FD_SOURCES);
+ sourceFolders.add(IAndroidConstants.FD_GEN_SOURCES);
+ }
+
+ /**
+ * Return the Default Package Name.
+ *
+ * @return
+ */
+ private String getDefaultPackageName()
+ {
+ return PACKAGE_ROOT + name2package(name);
+ }
+
+ /**
+ * Return the default name for activities.
+ *
+ * @return
+ */
+ public String getDefaultActivityName()
+ {
+ String activityName = name;
+ activityName = activityName.replaceAll("^[0-9]+", "_"); //$NON-NLS-1$ //$NON-NLS-2$
+ activityName = activityName.replaceAll("[\\.]+", "_"); //$NON-NLS-1$ //$NON-NLS-2$
+ return activityName;
+ }
+
+ /**
+ * Returns a valid package name from the Project Name
+ *
+ * @param name
+ * @return
+ */
+ protected String name2package(String name)
+ {
+ String packageName = getDefaultActivityName().toLowerCase();
+ packageName = packageName.replaceAll("[^A-Za-z0-9_]+", "_"); //$NON-NLS-1$ //$NON-NLS-2$
+ if (packageName.endsWith("_") && (packageName.length() > 1)) //$NON-NLS-1$
+ {
+ packageName = packageName.substring(0, packageName.length() - 1);
+ }
+ return packageName;
+ }
+
+ /**
+ * Return the Minimum SDK Version
+ *
+ * @return
+ */
+ public String getMinSdkVersion()
+ {
+ return minSdkVersion;
+ }
+
+ /**
+ * Returns <code>true</code> in case the SDK is a preview,
+ * <code>false</code> otherwise.
+ *
+ * @return Returns <code>true</code> in case the SDK is a preview,
+ * <code>false</code> otherwise.
+ */
+ public boolean isSdkPreview()
+ {
+ return getSdkTarget().getVersion().isPreview();
+ }
+
+ /**
+ * Return the project Name
+ *
+ * @return
+ */
+ public String getName()
+ {
+ return name;
+ }
+
+ /**
+ * Return the package name. Or the Default package name, if using it.
+ *
+ * @return
+ */
+ public String getPackageName()
+ {
+ String packageName;
+ if (isUsingDefaultPackage())
+ {
+ packageName = getDefaultPackageName();
+ }
+ else
+ {
+ packageName = this.packageName;
+ }
+ return packageName;
+ }
+
+ /**
+ * Return the SDK target used by this project.
+ *
+ * @return
+ */
+ public IAndroidTarget getSdkTarget()
+ {
+ return sdkTarget;
+ }
+
+ /**
+ * Return the Source Folder to be created in the project.
+ *
+ * @return
+ */
+ public List<String> getSourceFolders()
+ {
+ return sourceFolders;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.motorola.studio.android.model.IWizardModel#getStatus()
+ */
+ public IStatus getStatus()
+ {
+ IStatus status = Status.OK_STATUS;
+ // Validating Project Name
+ if ((name == null) || (name.length() == 0))
+ {
+ status =
+ new AndroidStatus(IStatus.ERROR,
+ AndroidNLS.ERR_AndroidProject_ProjectNameMustBeSpecified);
+ }
+ else
+ {
+ Pattern pattern = Pattern.compile("([A-Za-z0-9_\\.])+"); //$NON-NLS-1$
+ Matcher matcher = pattern.matcher(name);
+
+ if (!matcher.matches())
+ {
+ String errMsg = NLS.bind(AndroidNLS.ERR_AndroidProject_InvalidProjectName, name);
+ status = new AndroidStatus(IStatus.ERROR, errMsg);
+ }
+ else if (WORKSPACE.getRoot().getProject(name).exists())
+ {
+ status =
+ new AndroidStatus(IStatus.ERROR,
+ AndroidNLS.ERR_AndroidProject_DuplicatedProjectNameInWorkspace);
+ }
+ else
+ {
+ status = WORKSPACE.validateName(name, IResource.PROJECT);
+ }
+ }
+
+ if (status.isOK() && (sourceType == SourceTypes.EXISTING))
+ {
+
+ // Check first if the path is empty. If true, we show a info
+ // message to the user
+ if (getLocation().length() == 0)
+ {
+ status =
+ new AndroidStatus(IStatus.INFO,
+ AndroidNLS.ERR_AndroidProject_EmptySourceLocation);
+ }
+ else
+ {
+ // Check if the manifest exists
+ Path path = new Path(getLocation());
+ String osPath = path.append(IAndroidConstants.FN_ANDROID_MANIFEST).toOSString();
+ File manifest = new File(osPath);
+ if (!manifest.exists() || !manifest.isFile())
+ {
+ String errMsg =
+ NLS.bind(AndroidNLS.ERR_AndroidProject_FileNotFoundError,
+ IAndroidConstants.FN_ANDROID_MANIFEST, path.lastSegment());
+ status = new AndroidStatus(IStatus.ERROR, errMsg);
+ }
+ }
+ }
+
+ // Validating Project Location
+ if (status.isOK() && !isUsingDefaultLocation())
+ {
+ IProject handle = WORKSPACE.getRoot().getProject(name);
+ status = WORKSPACE.validateProjectLocation(handle, new Path(location));
+ if (status.isOK() && isNewProject() && (getLocation() != null))
+ {
+ boolean projectLocationIsEmpty =
+ ProjectCreationSupport.validateNewProjectLocationIsEmpty(new Path(
+ getLocation()));
+ if (!projectLocationIsEmpty)
+ {
+ status =
+ new AndroidStatus(IStatus.ERROR,
+ AndroidNLS.UI_ProjectCreationSupport_NonEmptyFolder);
+ }
+ }
+ }
+
+ // Validating Project Location when adding Native Support on Windows
+ if (status.isOK() && isAddingNativeSupport() && Platform.getOS().equals(Platform.OS_WIN32))
+ {
+ if ((isUsingDefaultLocation() && ResourcesPlugin.getWorkspace().getRoot().getLocation()
+ .toOSString().contains(" ")) //$NON-NLS-1$
+ || (!isUsingDefaultLocation() && location.contains(" "))) //$NON-NLS-1$
+ {
+
+ status =
+ new AndroidStatus(IStatus.ERROR,
+ AndroidNLS.ERR_AndroidProject_LocationContainsWhitespaces);
+ }
+
+ }
+
+ // Validating if the project could create a "file name too long"
+ // structure
+ if (status.isOK())
+ {
+ String basePath = isUsingDefaultLocation() ? getDefaultLocation() : getLocation();
+ String relativePackagePath = getPackageName().replace('.', File.separatorChar);
+ String binPath =
+ basePath + File.separatorChar + BIN_FOLDER + File.separatorChar
+ + relativePackagePath;
+
+ String rDrawableBinClass = binPath + File.separatorChar + R_DRAWABLE_CLASS;
+
+ String settingsFilePath =
+ basePath + File.separatorChar + SETTINGS_FOLDER + File.separatorChar
+ + SETTINGS_FILE;
+
+ String defaultClassBin =
+ binPath + File.separator + getDefaultActivityName() + CLASS_EXTENSION;
+
+ int maxFilenameLength = getMax(new int[]
+ {
+ rDrawableBinClass.length(), settingsFilePath.length(), defaultClassBin.length()
+ });
+
+ if (maxFilenameLength > MAX_PATH_LENGTH)
+ {
+ status =
+ new AndroidStatus(IStatus.ERROR,
+ AndroidNLS.ERR_AndroidProject_ProjectNameTooLong);
+ }
+ }
+
+ // Validating SDK Target
+ if (status.isOK() && (sdkTarget == null))
+ {
+ status =
+ new AndroidStatus(IStatus.ERROR,
+ AndroidNLS.ERR_AndroidProject_ASdkTargetMustBeSpecified);
+ }
+
+ // Validates the application name
+ if (status.isOK()
+ && ((sourceType == SourceTypes.NEW) || (sourceType == SourceTypes.WIDGET)))
+ {
+ if (appName.trim().length() == 0)
+ {
+ status =
+ new AndroidStatus(IStatus.WARNING,
+ AndroidNLS.WARN_AndroidProject_ApplicationNameIsEmpty);
+ }
+ if (appName.contains("&")) //$NON-NLS-1$
+ {
+ status =
+ new AndroidStatus(IStatus.ERROR, NLS.bind(
+ AndroidNLS.ERR_AndroidProject_InvalidApplicationName, appName));
+ }
+
+ }
+
+ // Validating PackageName
+ if (status.isOK()
+ && ((sourceType == SourceTypes.NEW) || (sourceType == SourceTypes.WIDGET))
+ && !isUsingDefaultPackage())
+ {
+ if (getPackageName().length() == 0)
+ {
+ status =
+ new AndroidStatus(IStatus.ERROR,
+ AndroidNLS.ERR_AndroidProject_EmptyPackageName);
+ }
+ else if (status.isOK())
+ {
+ status =
+ JavaConventions.validatePackageName(getPackageName(), SDK_VERSION,
+ SDK_VERSION);
+ }
+
+ if (status.isOK() && (getPackageName().indexOf('.') == -1))
+ {
+ // The Android Activity Manager does not accept packages names
+ // with only one
+ // identifier. Check the package name has at least one dot in
+ // them (the previous rule
+ // validated that if such a dot exist, it's not the first nor
+ // last characters of the
+ // string.)
+ status =
+ new AndroidStatus(IStatus.ERROR,
+ AndroidNLS.ERR_AndroidProject_InvalidPackageName);
+ }
+
+ if (status.isOK())
+ {
+ Pattern pattern = Pattern.compile("[A-Za-z0-9_\\.]+"); //$NON-NLS-1$
+ Matcher matcher = pattern.matcher(getPackageName());
+
+ if (!matcher.matches())
+ {
+ String errMsg =
+ NLS.bind(AndroidNLS.ERR_AndroidProject_InvalidCharsInPackageName,
+ getPackageName());
+
+ status = new AndroidStatus(IStatus.ERROR, errMsg);
+ }
+ }
+ }
+
+ // validating activity name
+ if ((this.getSourceType() == SourceTypes.NEW)
+ || (this.getSourceType() == SourceTypes.WIDGET))
+ {
+ if ((activityName == null) || (activityName.length() == 0))
+ {
+ status =
+ new AndroidStatus(IStatus.ERROR,
+ AndroidNLS.ERR_AndroidProject_ActivityNameMustBeSpecified);
+ }
+ else
+ {
+ String onlyChars = "[A-Za-z_]"; //$NON-NLS-1$
+ Pattern pattern = Pattern.compile("(" + onlyChars + ")(\\w)+"); //$NON-NLS-1$ //$NON-NLS-2$
+ Matcher matcher = pattern.matcher(activityName);
+
+ if (!matcher.matches())
+ {
+ String errMsg =
+ NLS.bind(AndroidNLS.ERR_AndroidProject_InvalidActivityName,
+ activityName);
+ status = new AndroidStatus(IStatus.ERROR, errMsg);
+ }
+ }
+ }
+
+ // Validating Min Sdk Version
+ if (status.isOK()
+ && ((sourceType == SourceTypes.NEW) || (sourceType == SourceTypes.WIDGET)))
+ {
+ // validate in case the sdk is preview
+ if (isSdkPreview())
+ {
+ String sdkAPI = getSdkTarget().getVersion().getApiString();
+ if (!sdkAPI.equals(getMinSdkVersion()))
+ {
+ status =
+ new AndroidStatus(IStatus.ERROR, NLS.bind(
+ AndroidNLS.AndroidProject_MsgSDKVersionIsPreview, sdkAPI));
+ }
+ }
+ // since it is not a preview, validate it normally
+ else
+ {
+ int version = -1;
+ try
+ {
+ // If not empty, it must be a valid integer > 0
+ version = Integer.parseInt(getMinSdkVersion());
+ }
+ catch (NumberFormatException nfe)
+ {
+ status =
+ new AndroidStatus(IStatus.ERROR,
+ AndroidNLS.ERR_AndroidProject_InvalidSdkVersion);
+ }
+
+ if (status.isOK() && (version < 1))
+ {
+ status =
+ new AndroidStatus(IStatus.ERROR,
+ AndroidNLS.ERR_AndroidProject_InvalidSdkVersion);
+ }
+
+ if (status.isOK() && (getSdkTarget() != null))
+ {
+ if (getSdkTarget().getVersion().getApiLevel() > version)
+ {
+ status =
+ new AndroidStatus(IStatus.WARNING,
+ AndroidNLS.ERR_AndroidProject_InvalidApiLevel);
+ }
+ else if (getSdkTarget().getVersion().getApiLevel() < version)
+ {
+ status =
+ new AndroidStatus(IStatus.ERROR,
+ AndroidNLS.EXC_AndroidProject_InvalidMinimumSdkVersion);
+ }
+ }
+ }
+ }
+
+ //validate if there are available samples
+ if (status.isOK() && (sourceType == SourceTypes.SAMPLE) && (sdkTarget != null)
+ && (SdkUtils.getSamples(sdkTarget).length == 0))
+ {
+ status =
+ new AndroidStatus(IStatus.ERROR,
+ AndroidNLS.EXC_AndroidProject_NoSamplesAvailable);
+ }
+
+ // Validating Project Location when adding Obfuscation Support
+ if (status.isOK() && needToObfuscate())
+ {
+ if ((isUsingDefaultLocation() && ResourcesPlugin.getWorkspace().getRoot().getLocation()
+ .toOSString().contains(" ")) //$NON-NLS-1$
+ || (!isUsingDefaultLocation() && location.contains(" "))) //$NON-NLS-1$
+ {
+
+ status =
+ new AndroidStatus(IStatus.WARNING,
+ AndroidNLS.WRN_Obfuscation_ProjectLocationContainWhitespaces);
+ }
+ }
+
+ return status;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.motorola.studio.android.model.IWizardModel#needMoreInformation()
+ */
+ public boolean needMoreInformation()
+ {
+ boolean needMoreInformation =
+ (name == null) || (name.length() == 0)
+ || !WORKSPACE.validateName(name, IResource.PROJECT).isOK();
+
+ if (!needMoreInformation)
+ {
+ boolean projectPathInformation =
+ (location == null)
+ || ((location.length() == 0) || !WORKSPACE.validateProjectLocation(
+ WORKSPACE.getRoot().getProject(name), new Path(location))
+ .isOK());
+ boolean newProjectInformation = !useDefaultLocation && projectPathInformation;
+ switch (sourceType)
+ {
+ case NEW:
+ needMoreInformation = newProjectInformation;
+ break;
+ case SAMPLE:
+ needMoreInformation = newProjectInformation || (sample == null);
+ break;
+ case EXISTING:
+ needMoreInformation = projectPathInformation;
+ break;
+ case WIDGET:
+ needMoreInformation = newProjectInformation;
+ }
+ }
+
+ if (!needMoreInformation)
+ {
+ if (((sourceType == SourceTypes.NEW) || (sourceType == SourceTypes.WIDGET))
+ && !isUsingDefaultPackage())
+ {
+ needMoreInformation =
+ (getPackageName().length() == 0)
+ || !JavaConventions.validatePackageName(getPackageName(),
+ SDK_VERSION, SDK_VERSION).isOK()
+ || (getPackageName().indexOf('.') == -1);
+ }
+ }
+
+ if (!needMoreInformation)
+ {
+ needMoreInformation = sdkTarget == null;
+ }
+ return needMoreInformation;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.model.IWizardModel#save(org.eclipse.jface.wizard.IWizardContainer, org.eclipse.core.runtime.IProgressMonitor)
+ */
+ public boolean save(IWizardContainer container, IProgressMonitor monitor)
+ {
+ try
+ {
+ return ProjectCreationSupport.createProject(this, container);
+ }
+ catch (AndroidException e)
+ {
+ IStatus status =
+ new Status(IStatus.ERROR, AndroidPlugin.PLUGIN_ID, e.getLocalizedMessage());
+
+ EclipseUtils.showErrorDialog(AndroidNLS.GEN_Error,
+ AndroidNLS.EXC_AndroidProject_AnErrorHasOccurredWhenCreatingTheProject, status);
+ return false;
+ }
+ }
+
+ /**
+ * Change the SDK
+ *
+ * @param minSdkVersion
+ */
+ public void setMinSdkVersion(String minSdkVersion)
+ {
+ this.minSdkVersion = minSdkVersion;
+ }
+
+ /**
+ * Change the project name
+ *
+ * @param name
+ */
+ public void setName(String name)
+ {
+ this.name = name.trim();
+ }
+
+ /**
+ * Change the package name. If using default package name will throw an
+ * {@link IllegalStateException}
+ *
+ * @param packageName
+ */
+ public void setPackageName(String packageName)
+ {
+ if (isUsingDefaultPackage())
+ {
+ throw new IllegalStateException();
+ }
+ this.packageName = packageName.trim();
+ }
+
+ /**
+ * Change the SDK Target
+ *
+ * @param sdkTarget
+ */
+ public void setSdkTarget(IAndroidTarget sdkTarget)
+ {
+ this.sdkTarget = sdkTarget;
+ }
+
+ /**
+ * Change the Source Folder
+ *
+ * @param sourceFolder
+ */
+ public void setSourceFolder(List<String> sourceFolders)
+ {
+ this.sourceFolders.clear();
+ this.sourceFolders.addAll(sourceFolders);
+ }
+
+ /**
+ * Return the Default location for this project
+ *
+ * @return
+ */
+ public String getDefaultLocation()
+ {
+ return ResourcesPlugin.getWorkspace().getRoot().getLocation().toOSString() + File.separator
+ + name;
+ }
+
+ /**
+ * Check if this project is a new project.
+ *
+ * @return
+ */
+ public boolean isNewProject()
+ {
+ return getSourceType() != SourceTypes.EXISTING;
+ }
+
+ /**
+ * Set the name of the activity being created with the project
+ * This attribute is only valid for New Projects.
+ * Other kind of project will ignore this attribute.
+ */
+ public void setActivityName(String activityName)
+ {
+ this.activityName = activityName;
+ }
+
+ /**
+ * Return this project default activity name
+ * @return an Activity name as string
+ */
+ public String getActivityName()
+ {
+ return this.activityName;
+ }
+
+ /**
+ * Retrieves the maximum value from an int array
+ *
+ * @param values
+ * the int array
+ *
+ * @return the maximum value from the int array
+ */
+ private int getMax(int[] values)
+ {
+ int max = values[0];
+
+ for (int i = 1; i < values.length; i++)
+ {
+ max = Math.max(max, values[i]);
+ }
+
+ return max;
+ }
+
+ /**
+ * Set the application name to be used
+ * The app name will be used only when the project is new
+ * @param app_name
+ */
+ public void setApplicationName(String app_name)
+ {
+ this.appName = app_name;
+ }
+
+ /**
+ * get the user defined application name
+ * @return
+ */
+ public String getApplicationName()
+ {
+ return this.appName;
+ }
+}
diff --git a/src/plugins/android/src/com/motorola/studio/android/model/IWizardModel.java b/src/plugins/android/src/com/motorola/studio/android/model/IWizardModel.java
new file mode 100644
index 0000000..9f3f563
--- /dev/null
+++ b/src/plugins/android/src/com/motorola/studio/android/model/IWizardModel.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.model;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.jface.wizard.IWizard;
+import org.eclipse.jface.wizard.IWizardContainer;
+
+import com.motorola.studio.android.common.exception.AndroidException;
+
+/**
+ * Controller interface. It should communicate with the Wizard UI
+ * to create on workspace the wizard output.
+ */
+public interface IWizardModel
+{
+ public static final int MODIFIED = 1546;
+
+ /**
+ * Return the Model Status.
+ * This Status must contains the
+ * {@link IStatus#getSeverity()}
+ * according with the needed
+ * values, and must contain the
+ * message for not OK Status.
+ *
+ * @return The Model Status
+ */
+ public IStatus getStatus();
+
+ /**
+ * Save Contents in Workspace;
+ * @param container
+ * @return
+ * @throws AndroidException
+ */
+ /**
+ * Save Contents in Workspace;
+ * @param container
+ * @param monitor
+ * @return
+ * @throws AndroidException
+ */
+ boolean save(IWizardContainer container, IProgressMonitor monitor) throws AndroidException;
+
+ /**
+ * Check if need more information to finish.
+ * @see IWizard#canFinish()
+ * @return
+ */
+ boolean needMoreInformation();
+}
diff --git a/src/plugins/android/src/com/motorola/studio/android/model/ProjectCreationSupport.java b/src/plugins/android/src/com/motorola/studio/android/model/ProjectCreationSupport.java
new file mode 100644
index 0000000..8551e17
--- /dev/null
+++ b/src/plugins/android/src/com/motorola/studio/android/model/ProjectCreationSupport.java
@@ -0,0 +1,1377 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.model;
+
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.reflect.InvocationTargetException;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.eclipse.core.resources.IContainer;
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IFolder;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IProjectDescription;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.IResourceStatus;
+import org.eclipse.core.resources.IWorkspace;
+import org.eclipse.core.resources.IWorkspaceRoot;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.core.runtime.OperationCanceledException;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.core.runtime.SubProgressMonitor;
+import org.eclipse.jdt.core.IClasspathEntry;
+import org.eclipse.jdt.core.IJavaProject;
+import org.eclipse.jdt.core.JavaCore;
+import org.eclipse.jdt.core.JavaModelException;
+import org.eclipse.jface.dialogs.ErrorDialog;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.jface.wizard.IWizardContainer;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.ui.actions.WorkspaceModifyOperation;
+
+import com.android.ide.eclipse.adt.AdtPlugin;
+import com.motorola.studio.android.AndroidPlugin;
+import com.motorola.studio.android.adt.ProjectUtils;
+import com.motorola.studio.android.adt.SdkUtils;
+import com.motorola.studio.android.common.IAndroidConstants;
+import com.motorola.studio.android.common.exception.AndroidException;
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.common.utilities.AndroidStatus;
+import com.motorola.studio.android.common.utilities.EclipseUtils;
+import com.motorola.studio.android.common.utilities.FileUtil;
+import com.motorola.studio.android.i18n.AndroidNLS;
+import com.motorola.studio.android.model.AndroidProject.SourceTypes;
+
+/**
+ * Project Creation Support.
+ */
+public class ProjectCreationSupport
+{
+ /**
+ * Only static calls
+ */
+ private ProjectCreationSupport()
+ {
+ }
+
+ private static final String PACKAGE_NAME = "PACKAGE"; //$NON-NLS-1$
+
+ private static final String APP_NAME = "app_name"; //$NON-NLS-1$
+
+ private static final String APPLICATION_NAME = "APPLICATION_NAME"; //$NON-NLS-1$
+
+ private static final String STRING_RSRC_PREFIX = "@string/"; //$NON-NLS-1$
+
+ private static final String MIN_SDK_VERSION = "MIN_SDK_VERSION"; //$NON-NLS-1$
+
+ private static final String BIN_DIR = IAndroidConstants.FD_OUTPUT + IPath.SEPARATOR;
+
+ private static final String RES_DIR = IAndroidConstants.FD_RESOURCES + IPath.SEPARATOR;
+
+ private static final String ASSETS_DIR = IAndroidConstants.FD_ASSETS + IPath.SEPARATOR;
+
+ private static final String DRAWABLE_DIR = IAndroidConstants.FD_DRAWABLE;
+
+ private static final String LAYOUT_DIR = IAndroidConstants.FD_LAYOUT + IPath.SEPARATOR;
+
+ private static final String VALUES_DIR = IAndroidConstants.FD_VALUES + IPath.SEPARATOR;
+
+ private static final String GEN_DIR = IAndroidConstants.FD_GEN_SOURCES + IPath.SEPARATOR;
+
+ private static final String XML_DIR = "xml" + IPath.SEPARATOR;
+
+ private static final String TEMPLATES_DIRECTORY = "templates/"; //$NON-NLS-1$
+
+ private static final String MANIFEST_TEMPLATE = TEMPLATES_DIRECTORY
+ + "AndroidManifest.template"; //$NON-NLS-1$
+
+ private static final String ACTIVITY_NAME = "ACTIVITY_NAME"; //$NON-NLS-1$
+
+ private static final String ACTIVITY_TEMPLATE = TEMPLATES_DIRECTORY + "activity.template"; //$NON-NLS-1$
+
+ private static final String LAUNCHER_INTENT_TEMPLATE = TEMPLATES_DIRECTORY
+ + "launcher_intent_filter.template"; //$NON-NLS-1$
+
+ private static final String INTENT_FILTERS = "INTENT_FILTERS"; //$NON-NLS-1$
+
+ private static final String ACTIVITIES = "ACTIVITIES"; //$NON-NLS-1$
+
+ private static final String USES_SDK_TEMPLATE = TEMPLATES_DIRECTORY + "uses-sdk.template"; //$NON-NLS-1$
+
+ private static final String USES_SDK = "USES-SDK"; //$NON-NLS-1$
+
+ private static final String ICON = "ic_launcher.png"; //$NON-NLS-1$
+
+ private static final String JAVA_ACTIVITY_TEMPLATE = "java_file.template"; //$NON-NLS-1$
+
+ private static final String MAIN_LAYOUT_XML = "main.xml"; //$NON-NLS-1$
+
+ private static final String LAYOUT_TEMPLATE = "layout.template"; //$NON-NLS-1$
+
+ private static final String STRING_HELLO_WORLD = "hello"; //$NON-NLS-1$
+
+ private static final String TEST_USES_LIBRARY = "TEST-USES-LIBRARY"; //$NON-NLS-1$
+
+ private static final String TEST_INSTRUMENTATION = "TEST-INSTRUMENTATION"; //$NON-NLS-1$
+
+ private static final String[] DPIS =
+ {
+ "hdpi", "ldpi", "mdpi"
+ };
+
+ /*
+ * Widget Project manifest creation constants
+ */
+
+ private static final String WIDGET_TEMPLATE_FOLDER = "templates/widget_project/";
+
+ private static final String WIDGET_MANIFEST_TEMPLATE_PATH =
+ "templates/widget_project/AndroidWidgetManifest.template"; //$NON-NLS-1$
+
+ private static final String WIDGET_ACTIVITY_TEMPLATE_PATH =
+ "templates/widget_project/activity.template"; //$NON-NLS-1$
+
+ private static final String WIDGET_RECEIVER_TEMPLATE_PATH =
+ "templates/widget_project/receiver.template"; //$NON-NLS-1$
+
+ private static final String WIDGET_USES_SDK_TEMPLATE_PATH =
+ "templates/widget_project/uses-sdk.template"; //$NON-NLS-1$
+
+ private static final String RECEIVERS = "RECEIVERS"; //$NON-NLS-1$
+
+ private static final String WIDGET_INITIAL_LAYOUT_XML = "widget_initial_layout.xml"; //$NON-NLS-1$
+
+ private static final String WIDGET_INFO_XML = "widget_info.xml"; //$NON-NLS-1$
+
+ private static final String WIDGET_PROVIDER_SAMPLE_NAME = "WidgetProvider"; //$NON-NLS-1$
+
+ private static final String WIDGET_PROVIDER_SAMPLE_TEMPLATE = "WidgetProvider.template"; //$NON-NLS-1$
+
+ private static final String IMPORT_RESOURCE_CLASS = "IMPORT_RESOURCE_CLASS";
+
+ /**
+ * Create a new Android Project
+ * @param androidProject
+ * @param container
+ * @return
+ * @throws AndroidException
+ */
+ public static boolean createProject(final AndroidProject androidProject,
+ IWizardContainer container) throws AndroidException
+ {
+ boolean created = true;
+
+ IWorkspace workspace = ResourcesPlugin.getWorkspace();
+ final IProject project = workspace.getRoot().getProject(androidProject.getName());
+
+ if (!canCreateProject(workspace.getRoot(), androidProject.getName()))
+ {
+ throw new AndroidException(
+ AndroidNLS.EXC_ProjectCreationSupport_CannotCreateProjectReadOnlyWorkspace);
+ }
+ else
+ {
+
+ final IProjectDescription description =
+ workspace.newProjectDescription(project.getName());
+
+ final Map<String, Object> parameters = new HashMap<String, Object>();
+ parameters.put(MIN_SDK_VERSION, androidProject.getMinSdkVersion());
+
+ if ((androidProject.getSourceType() == SourceTypes.NEW)
+ || (androidProject.getSourceType() == SourceTypes.WIDGET))
+ {
+ /*
+ * 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 = androidProject.getActivityName();
+ if (activityName.startsWith(".")) { //$NON-NLS-1$
+ activityName = activityName.substring(1);
+ }
+ parameters.put(ACTIVITY_NAME, androidProject.getActivityName());
+ parameters.put(PACKAGE_NAME, androidProject.getPackageName());
+ parameters.put(APPLICATION_NAME, STRING_RSRC_PREFIX + APP_NAME);
+ parameters.put(IMPORT_RESOURCE_CLASS, "");
+ }
+
+ /*
+ * create a dictionary of string that will contain name+content.
+ * we'll put all the strings into values/strings.xml
+ */
+ final HashMap<String, String> stringDictionary = new HashMap<String, String>();
+ stringDictionary.put(APP_NAME, androidProject.getApplicationName());
+
+ if (!androidProject.isUsingDefaultLocation() && androidProject.isNewProject())
+ {
+ Path destination = new Path(androidProject.getLocation());
+ description.setLocation(destination);
+
+ if (!FileUtil.canWrite(destination.toFile()))
+ {
+ String errMsg =
+ NLS.bind(
+ AndroidNLS.EXC_ProjectCreationSupport_CannotCreateProjectReadOnlyDestination,
+ destination.toOSString());
+ throw new AndroidException(errMsg);
+ }
+
+ if (!validateNewProjectLocationIsEmpty(destination))
+ {
+ throw new AndroidException(AndroidNLS.UI_ProjectCreationSupport_NonEmptyFolder);
+ }
+ }
+
+ if (androidProject.getSourceType() == SourceTypes.EXISTING)
+ {
+ Path destination = new Path(androidProject.getLocation());
+ description.setLocation(destination);
+ }
+
+ /*
+ * Create a monitored operation to create the actual project
+ */
+ WorkspaceModifyOperation op = new WorkspaceModifyOperation()
+ {
+ @Override
+ protected void execute(IProgressMonitor monitor) throws InvocationTargetException
+ {
+
+ createProjectAsync(project, androidProject, description, monitor, parameters,
+ stringDictionary);
+ }
+ };
+
+ /*
+ * Run the operation in a different thread
+ */
+ created = runAsyncOperation(op, container);
+ }
+
+ return created;
+
+ }
+
+ /**
+ * Create android project.
+ * @param project
+ * @param androidProject
+ * @param description
+ * @param monitor
+ * @param parameters
+ * @param stringDictionary
+ * @throws InvocationTargetException
+ */
+ protected static void createProjectAsync(IProject project, AndroidProject androidProject,
+ IProjectDescription description, IProgressMonitor monitor,
+ Map<String, Object> parameters, Map<String, String> stringDictionary)
+ throws InvocationTargetException
+ {
+ monitor.beginTask(AndroidNLS.UI_ProjectCreationSupport_CopyingSamplesMonitorTaskTitle, 1000);
+ try
+ {
+ // Create project and open it
+ project.create(description, new SubProgressMonitor(monitor, 100));
+ if (monitor.isCanceled())
+ {
+ undoProjectCreation(project);
+ throw new OperationCanceledException();
+ }
+ project.open(IResource.BACKGROUND_REFRESH, new SubProgressMonitor(monitor, 100));
+
+ ProjectUtils.setupAndroidNatures(project, monitor);
+
+ // Create folders in the project if they don't already exist
+ createDefaultDir(project, IAndroidConstants.WS_ROOT, BIN_DIR, new SubProgressMonitor(
+ monitor, 40));
+ createDefaultDir(project, IAndroidConstants.WS_ROOT, RES_DIR, new SubProgressMonitor(
+ monitor, 40));
+ createDefaultDir(project, IAndroidConstants.WS_ROOT, ASSETS_DIR,
+ new SubProgressMonitor(monitor, 40));
+ createDefaultDir(project, IAndroidConstants.WS_ROOT, GEN_DIR, new SubProgressMonitor(
+ monitor, 40));
+
+ switch (androidProject.getSourceType())
+ {
+ case NEW:
+ // Create the source folders in the project if they don't already exist
+ List<String> sourceFolders = androidProject.getSourceFolders();
+ for (String sourceFolder : sourceFolders)
+ {
+ createDefaultDir(project, IAndroidConstants.WS_ROOT, sourceFolder,
+ new SubProgressMonitor(monitor, 40));
+ }
+
+ // Create the resource folders in the project if they don't already exist.
+ int apiLevel = androidProject.getSdkTarget().getVersion().getApiLevel();
+ if (apiLevel < 4)
+ {
+ createDefaultDir(project, RES_DIR, DRAWABLE_DIR + File.separator,
+ new SubProgressMonitor(monitor, 40));
+ }
+ else
+ {
+ for (String dpi : DPIS)
+ {
+ createDefaultDir(project, RES_DIR, DRAWABLE_DIR + "-" + dpi
+ + File.separator, new SubProgressMonitor(monitor, 40));
+ }
+ }
+ createDefaultDir(project, RES_DIR, LAYOUT_DIR, new SubProgressMonitor(monitor,
+ 40));
+ createDefaultDir(project, RES_DIR, VALUES_DIR, new SubProgressMonitor(monitor,
+ 40));
+
+ // Create files in the project if they don't already exist
+ createManifest(project, parameters, stringDictionary, new SubProgressMonitor(
+ monitor, 80));
+ // add the default app icon
+ addIcon(project, apiLevel, new SubProgressMonitor(monitor, 100));
+ // Create the default package components
+ String primarySrcFolder = IAndroidConstants.FD_SOURCES;
+ if (!sourceFolders.contains(IAndroidConstants.FD_SOURCES))
+ {
+ primarySrcFolder = sourceFolders.get(0);
+ }
+ addInitialCode(project, primarySrcFolder, parameters, stringDictionary,
+ new SubProgressMonitor(monitor, 200));
+ // add the string definition file if needed
+ if (stringDictionary.size() > 0)
+ {
+ EclipseUtils.createOrUpdateDictionaryFile(project, stringDictionary, null,
+ new SubProgressMonitor(monitor, 100));
+ }
+
+ break;
+ case EXISTING:
+ createDefaultDir(project, IAndroidConstants.WS_ROOT, GEN_DIR,
+ new SubProgressMonitor(monitor, 650));
+ break;
+ case SAMPLE:
+ monitor.setTaskName(AndroidNLS.UI_ProjectCreationSupport_CopyingSamplesMonitorMessage);
+ FileUtil.copyDir(androidProject.getSample().getFolder(), project.getLocation()
+ .toFile());
+ project.refreshLocal(IResource.DEPTH_INFINITE, new SubProgressMonitor(monitor,
+ 650));
+ break;
+ case WIDGET:
+ // Create the source folders in the project if they don't already exist
+ List<String> widgetSourceFolders = androidProject.getSourceFolders();
+ for (String sourceFolder : widgetSourceFolders)
+ {
+ createDefaultDir(project, IAndroidConstants.WS_ROOT, sourceFolder,
+ new SubProgressMonitor(monitor, 40));
+ }
+
+ // Create the resource folders in the project if they don't already exist.
+ int widgetApiLevel = androidProject.getSdkTarget().getVersion().getApiLevel();
+ if (widgetApiLevel < 4)
+ {
+ createDefaultDir(project, RES_DIR, DRAWABLE_DIR + File.separator,
+ new SubProgressMonitor(monitor, 40));
+ }
+ else
+ {
+ for (String dpi : DPIS)
+ {
+ createDefaultDir(project, RES_DIR, DRAWABLE_DIR + "-" + dpi
+ + File.separator, new SubProgressMonitor(monitor, 40));
+ }
+ }
+ createDefaultDir(project, RES_DIR, LAYOUT_DIR, new SubProgressMonitor(monitor,
+ 40));
+ createDefaultDir(project, RES_DIR, VALUES_DIR, new SubProgressMonitor(monitor,
+ 40));
+ createDefaultDir(project, RES_DIR, XML_DIR, new SubProgressMonitor(monitor, 40));
+
+ // Create files in the project if they don't already exist
+ createWidgetManifest(project, parameters, stringDictionary,
+ new SubProgressMonitor(monitor, 80));
+ // add the default app icon
+ addIcon(project, widgetApiLevel, new SubProgressMonitor(monitor, 100));
+ // Create the default package components
+ String widgetPrimarySrcFolder = IAndroidConstants.FD_SOURCES;
+ if (!widgetSourceFolders.contains(IAndroidConstants.FD_SOURCES))
+ {
+ primarySrcFolder = widgetSourceFolders.get(0);
+ }
+ addInitialWidgetCode(project, widgetPrimarySrcFolder, parameters,
+ stringDictionary, new SubProgressMonitor(monitor, 200));
+ // add the string definition file if needed
+ if (stringDictionary.size() > 0)
+ {
+ EclipseUtils.createOrUpdateDictionaryFile(project, stringDictionary, null,
+ new SubProgressMonitor(monitor, 100));
+ }
+
+ break;
+ }
+
+ // Setup class path
+ IJavaProject javaProject = JavaCore.create(project);
+ setupSourceFolders(javaProject, androidProject.getSourceFolders(),
+ new SubProgressMonitor(monitor, 40));
+
+ // Set output location
+ javaProject.setOutputLocation(project.getFolder(BIN_DIR).getFullPath(),
+ new SubProgressMonitor(monitor, 40));
+ SdkUtils.associate(project, androidProject.getSdkTarget());
+ ProjectUtils.fixProject(project);
+ }
+ catch (CoreException e)
+ {
+ undoProjectCreation(project);
+ throw new InvocationTargetException(e);
+ }
+ catch (IOException e)
+ {
+ undoProjectCreation(project);
+ throw new InvocationTargetException(e);
+ }
+ finally
+ {
+ monitor.done();
+ }
+ }
+
+ /**
+ * Add initial code
+ * @param project
+ * @param sourceFolder
+ * @param parameters
+ * @param stringDictionary
+ * @param monitor
+ * @throws CoreException
+ * @throws IOException
+ */
+ private static void addInitialCode(IProject project, String sourceFolder,
+ Map<String, Object> parameters, Map<String, String> stringDictionary,
+ IProgressMonitor monitor) throws CoreException, IOException
+ {
+ monitor.beginTask(AndroidNLS.UI_ProjectCreationSupport_Configuring_Sample_Source_Task, 700);
+
+ try
+ {
+ IFolder pkgFolder = project.getFolder(sourceFolder);
+
+ Map<String, Object> processed_parameters = processSampleActivity(parameters);
+ String activityName = (String) processed_parameters.get(ACTIVITY_NAME);
+ String packageName = (String) processed_parameters.get(PACKAGE_NAME);
+
+ pkgFolder =
+ createPackageFolders(new SubProgressMonitor(monitor, 300), pkgFolder,
+ packageName);
+
+ if (activityName != null)
+ {
+ createSampleActivity(new SubProgressMonitor(monitor, 200), pkgFolder,
+ processed_parameters, activityName);
+ }
+
+ IFolder layoutfolder = project.getFolder(RES_DIR + LAYOUT_DIR);
+ IFile file = layoutfolder.getFile(MAIN_LAYOUT_XML);
+ if (!file.exists())
+ {
+ copyTemplateFile(LAYOUT_TEMPLATE, file, parameters, new SubProgressMonitor(monitor,
+ 100));
+ if (activityName != null)
+ {
+ stringDictionary
+ .put(STRING_HELLO_WORLD, NLS.bind(
+ AndroidNLS.GEN_ProjectCreationSupport_HelloWorldWithName,
+ activityName));
+ }
+ else
+ {
+ stringDictionary.put(STRING_HELLO_WORLD,
+ AndroidNLS.GEN_ProjectCreationSupport_HelloWorldSimple);
+ }
+ monitor.worked(100);
+ }
+ }
+ finally
+ {
+ monitor.done();
+ }
+ }
+
+ /**
+ * Add initial widget code
+ * @param project
+ * @param sourceFolder
+ * @param parameters
+ * @param stringDictionary
+ * @param monitor
+ * @throws CoreException
+ * @throws IOException
+ */
+ private static void addInitialWidgetCode(IProject project, String sourceFolder,
+ Map<String, Object> parameters, Map<String, String> stringDictionary,
+ IProgressMonitor monitor) throws CoreException, IOException
+ {
+ monitor.beginTask(AndroidNLS.UI_ProjectCreationSupport_Configuring_Sample_Source_Task, 800);
+
+ try
+ {
+ IFolder pkgFolder = project.getFolder(sourceFolder);
+
+ Map<String, Object> processed_parameters = processSampleActivity(parameters);
+ String activityName = (String) processed_parameters.get(ACTIVITY_NAME);
+ String packageName = (String) processed_parameters.get(PACKAGE_NAME);
+
+ pkgFolder =
+ createPackageFolders(new SubProgressMonitor(monitor, 200), pkgFolder,
+ packageName);
+
+ // Create sample activity
+ if (activityName != null)
+ {
+ createSampleActivity(new SubProgressMonitor(monitor, 100), pkgFolder,
+ processed_parameters, activityName);
+ }
+
+ // Create sample widget provider
+ createSampleWidgetProvider(new SubProgressMonitor(monitor, 100), pkgFolder,
+ processed_parameters);
+
+ // Layout xml file
+ IFolder layoutfolder = project.getFolder(RES_DIR + LAYOUT_DIR);
+
+ IFile file = layoutfolder.getFile(MAIN_LAYOUT_XML);
+ if (!file.exists())
+ {
+ copyTemplateFile(LAYOUT_TEMPLATE, file, parameters, new SubProgressMonitor(monitor,
+ 100));
+ if (activityName != null)
+ {
+ stringDictionary
+ .put(STRING_HELLO_WORLD, NLS.bind(
+ AndroidNLS.GEN_ProjectCreationSupport_HelloWorldWithName,
+ activityName));
+ }
+ else
+ {
+ stringDictionary.put(STRING_HELLO_WORLD,
+ AndroidNLS.GEN_ProjectCreationSupport_HelloWorldSimple);
+ }
+ monitor.worked(100);
+ }
+
+ // Widget initial layout xml file
+ IFile initial_layout_file = layoutfolder.getFile(WIDGET_INITIAL_LAYOUT_XML);
+ if (!initial_layout_file.exists())
+ {
+ copyWidgetTemplateFile(WIDGET_INITIAL_LAYOUT_XML, initial_layout_file,
+ processed_parameters, new SubProgressMonitor(monitor, 100));
+ monitor.worked(100);
+ }
+
+ // Widget info xml file
+ IFolder xmlFolder = project.getFolder(RES_DIR + XML_DIR);
+
+ IFile widget_info_file = xmlFolder.getFile(WIDGET_INFO_XML);
+ if (!widget_info_file.exists())
+ {
+ copyWidgetTemplateFile(WIDGET_INFO_XML, widget_info_file, processed_parameters,
+ new SubProgressMonitor(monitor, 100));
+ monitor.worked(100);
+ }
+
+ }
+ finally
+ {
+ monitor.done();
+ }
+ }
+
+ private static void createSampleActivity(IProgressMonitor monitor, IFolder pkgFolder,
+ Map<String, Object> processed_parameters, String activityName) throws CoreException,
+ IOException
+ {
+ monitor.beginTask(AndroidNLS.UI_ProjectCreationSupport_Configuring_Sample_Activity_Task,
+ 100);
+ try
+ {
+ IFile file = pkgFolder.getFile(activityName + IAndroidConstants.DOT_JAVA);
+ if (!file.exists())
+ {
+ monitor.worked(10);
+ copyTemplateFile(JAVA_ACTIVITY_TEMPLATE, file, processed_parameters,
+ new SubProgressMonitor(monitor, 90));
+ }
+ }
+ finally
+ {
+ monitor.done();
+ }
+ }
+
+ private static void createSampleWidgetProvider(IProgressMonitor monitor, IFolder pkgFolder,
+ Map<String, Object> processed_parameters) throws CoreException, IOException
+ {
+ monitor.beginTask(AndroidNLS.UI_ProjectCreationSupport_Configuring_Sample_Widget_Provider,
+ 100);
+ try
+ {
+ IFile file =
+ pkgFolder.getFile(WIDGET_PROVIDER_SAMPLE_NAME + IAndroidConstants.DOT_JAVA);
+ if (!file.exists())
+ {
+ monitor.worked(10);
+ copyWidgetTemplateFile(WIDGET_PROVIDER_SAMPLE_TEMPLATE, file, processed_parameters,
+ new SubProgressMonitor(monitor, 90));
+ }
+ }
+ finally
+ {
+ monitor.done();
+ }
+ }
+
+ private static IFolder createPackageFolders(IProgressMonitor monitor, IFolder pkgFolder,
+ String packageName) throws CoreException
+ {
+ String[] components = packageName.split(IAndroidConstants.RE_DOT);
+ monitor.beginTask(AndroidNLS.UI_ProjectCreationSupport_Preparing_Java_Packages_Task,
+ components.length * 100);
+ try
+ {
+ for (String component : components)
+ {
+ pkgFolder = pkgFolder.getFolder(component);
+ if (!pkgFolder.exists())
+ {
+ pkgFolder.create(true, true, new SubProgressMonitor(monitor, 100));
+ }
+ }
+ }
+ finally
+ {
+ monitor.done();
+ }
+
+ return pkgFolder;
+ }
+
+ private static Map<String, Object> processSampleActivity(Map<String, Object> parameters)
+ {
+ String activityName = (String) parameters.get(ACTIVITY_NAME);
+
+ Map<String, Object> processed_parameters = new HashMap<String, Object>(parameters);
+ if ((activityName != null) && activityName.contains(".")) //$NON-NLS-1$
+ {
+ String packageName = (String) parameters.get(PACKAGE_NAME);
+ packageName += "." + activityName.substring(0, activityName.lastIndexOf('.')); //$NON-NLS-1$
+ activityName = activityName.substring(activityName.lastIndexOf('.'));
+
+ processed_parameters.put(PACKAGE_NAME, packageName);
+ processed_parameters.put(ACTIVITY_NAME, activityName);
+ }
+
+ return processed_parameters;
+ }
+
+ /**
+ * Copy template files
+ * @param resourceFilename
+ * @param destFile
+ * @param parameters
+ * @param monitor
+ * @throws CoreException
+ * @throws IOException
+ */
+ private static void copyTemplateFile(String resourceFilename, IFile destFile,
+ Map<String, Object> parameters, IProgressMonitor monitor) throws CoreException,
+ IOException
+ {
+ monitor.beginTask(AndroidNLS.UI_ProjectCreationSupport_Preparing_Template_File_Task, 150);
+ InputStream stream = null;
+ try
+ {
+ String template =
+ readEmbeddedTextFileADT(TEMPLATES_DIRECTORY + resourceFilename, parameters);
+ monitor.worked(50);
+ stream = new ByteArrayInputStream(template.getBytes("UTF-8")); //$NON-NLS-1$
+ destFile.create(stream, false, new SubProgressMonitor(monitor, 100));
+
+ }
+ finally
+ {
+ if (stream != null)
+ {
+ stream.close();
+ }
+ monitor.done();
+ }
+ }
+
+ /**
+ * Copy widget template files
+ * @param resourceFilename
+ * @param destFile
+ * @param parameters
+ * @param monitor
+ * @throws CoreException
+ * @throws IOException
+ */
+ private static void copyWidgetTemplateFile(String resourceFilename, IFile destFile,
+ Map<String, Object> parameters, IProgressMonitor monitor) throws CoreException,
+ IOException
+ {
+ monitor.beginTask(AndroidNLS.UI_ProjectCreationSupport_Preparing_Template_File_Task, 150);
+ InputStream stream = null;
+ try
+ {
+ String template =
+ readEmbeddedTextFileStudio(WIDGET_TEMPLATE_FOLDER + resourceFilename,
+ parameters);
+ monitor.worked(50);
+ stream = new ByteArrayInputStream(template.getBytes("UTF-8")); //$NON-NLS-1$
+ destFile.create(stream, false, new SubProgressMonitor(monitor, 100));
+
+ }
+ finally
+ {
+ if (stream != null)
+ {
+ stream.close();
+ }
+ monitor.done();
+ }
+ }
+
+ /**
+ * Add Icon to the project
+ * @param project
+ * @param apiLevel
+ * @param monitor
+ * @throws CoreException
+ */
+ private static void addIcon(IProject project, int apiLevel, IProgressMonitor monitor)
+ throws CoreException
+ {
+ monitor.beginTask(AndroidNLS.UI_ProjectCreationSupport_Configuring_Project_Icon_Task, 1000);
+ try
+ {
+ if (apiLevel < 4)
+ {
+ IFile imageFile =
+ project.getFile(RES_DIR + IPath.SEPARATOR + DRAWABLE_DIR + IPath.SEPARATOR
+ + ICON);
+ if (!imageFile.exists())
+ {
+ String fileName =
+ ICON.substring(0, ICON.length() - 4) + "_" + DPIS[2]
+ + ICON.substring(ICON.length() - 4);
+ createImageFromTemplate(monitor, imageFile, fileName);
+ }
+ }
+ else
+ {
+ for (String dpi : DPIS)
+ {
+ IFile imageFile =
+ project.getFile(RES_DIR + IPath.SEPARATOR + DRAWABLE_DIR + "-" + dpi
+ + IPath.SEPARATOR + ICON);
+ if (!imageFile.exists())
+ {
+ String fileName =
+ ICON.substring(0, ICON.length() - 4) + "_" + dpi
+ + ICON.substring(ICON.length() - 4);
+ createImageFromTemplate(monitor, imageFile, fileName);
+ }
+ }
+ }
+ }
+ finally
+ {
+ monitor.done();
+ }
+
+ }
+
+ private static void createImageFromTemplate(IProgressMonitor monitor, IFile imageFile,
+ String fileName) throws CoreException
+ {
+ byte[] buffer = AdtPlugin.readEmbeddedFile(TEMPLATES_DIRECTORY + fileName);
+
+ if (buffer != null)
+ {
+ InputStream stream = null;
+ try
+ {
+ stream = new ByteArrayInputStream(buffer);
+ imageFile.create(stream, IResource.NONE, new SubProgressMonitor(monitor, 1000));
+ }
+ finally
+ {
+ try
+ {
+ stream.close();
+ }
+ catch (IOException e)
+ {
+ StudioLogger.info("Create image from template could not close stream. "
+ + e.getMessage());
+ }
+ }
+ }
+ }
+
+ /**
+ * Adds the manifest to the project.
+ * @param project
+ * @param parameters
+ * @param stringDictionary
+ * @param monitor
+ * @throws CoreException
+ * @throws IOException
+ */
+ private static void createManifest(IProject project, Map<String, Object> parameters,
+ Map<String, String> stringDictionary, IProgressMonitor monitor) throws CoreException,
+ IOException
+ {
+ monitor.beginTask(AndroidNLS.UI_ProjectCreationSupport_Creating_Manifest_File_Task, 300);
+ try
+ {
+ IFile manifestFile = project.getFile(IAndroidConstants.FN_ANDROID_MANIFEST);
+ if (!manifestFile.exists())
+ {
+ monitor.setTaskName(AndroidNLS.UI_ProjectCreationSupport_Reading_Template_File_Task);
+ String manifestTemplate = readEmbeddedTextFileADT(MANIFEST_TEMPLATE, parameters);
+ monitor.worked(10);
+ if (parameters.containsKey(ACTIVITY_NAME))
+ {
+ String activities = readEmbeddedTextFileADT(ACTIVITY_TEMPLATE, parameters);
+ String intent = AdtPlugin.readEmbeddedTextFile(LAUNCHER_INTENT_TEMPLATE);
+ activities = activities.replaceAll(INTENT_FILTERS, intent);
+ manifestTemplate = manifestTemplate.replaceAll(ACTIVITIES, activities);
+ monitor.worked(90);
+ }
+ else
+ {
+ manifestTemplate = manifestTemplate.replaceAll(ACTIVITIES, ""); //$NON-NLS-1$
+ monitor.worked(90);
+ }
+
+ //We don't currently supports the TEST parameters. So let's just remove the unused tags.
+ manifestTemplate = manifestTemplate.replaceAll(TEST_USES_LIBRARY, ""); //$NON-NLS-1$
+ manifestTemplate = manifestTemplate.replaceAll(TEST_INSTRUMENTATION, ""); //$NON-NLS-1$
+
+ String minSdkVersion = (String) parameters.get(MIN_SDK_VERSION);
+ if ((minSdkVersion != null) && (minSdkVersion.length() > 0))
+ {
+ String usesSdk = readEmbeddedTextFileADT(USES_SDK_TEMPLATE, parameters);
+ manifestTemplate = manifestTemplate.replaceAll(USES_SDK, usesSdk);
+ monitor.worked(50);
+ }
+ else
+ {
+ manifestTemplate = manifestTemplate.replaceAll(USES_SDK, ""); //$NON-NLS-1$
+ monitor.worked(50);
+ }
+
+ InputStream stream = null;
+ try
+ {
+ stream = new ByteArrayInputStream(manifestTemplate.getBytes("UTF-8")); //$NON-NLS-1$
+ manifestFile.create(stream, IResource.NONE,
+ new SubProgressMonitor(monitor, 150));
+ }
+ finally
+ {
+ try
+ {
+ if (stream != null)
+ {
+ stream.close();
+ }
+ }
+ catch (IOException e)
+ {
+ StudioLogger.info("Could not close stream while creating manifest",
+ e.getMessage());
+ }
+ }
+ }
+ }
+ finally
+ {
+ monitor.done();
+ }
+ }
+
+ /**
+ * Adds the widget manifest to the project.
+ * @param project
+ * @param parameters
+ * @param stringDictionary
+ * @param monitor
+ * @throws CoreException
+ * @throws IOException
+ */
+ private static void createWidgetManifest(IProject project, Map<String, Object> parameters,
+ Map<String, String> stringDictionary, IProgressMonitor monitor) throws CoreException,
+ IOException
+ {
+ monitor.beginTask(AndroidNLS.UI_ProjectCreationSupport_Creating_Manifest_File_Task, 300);
+ try
+ {
+ IFile manifestFile = project.getFile(IAndroidConstants.FN_ANDROID_MANIFEST);
+ if (!manifestFile.exists())
+ {
+ monitor.setTaskName(AndroidNLS.UI_ProjectCreationSupport_Reading_Template_File_Task);
+
+ // Manifest skeleton
+ String manifestTemplate =
+ readEmbeddedTextFileStudio(WIDGET_MANIFEST_TEMPLATE_PATH, parameters);
+ monitor.worked(10);
+ // Activity information
+ if (parameters.containsKey(ACTIVITY_NAME))
+ {
+ String activities =
+ readEmbeddedTextFileStudio(WIDGET_ACTIVITY_TEMPLATE_PATH, parameters);
+ manifestTemplate = manifestTemplate.replaceAll(ACTIVITIES, activities);
+ monitor.worked(70);
+ }
+ else
+ {
+ manifestTemplate = manifestTemplate.replaceAll(ACTIVITIES, ""); //$NON-NLS-1$
+ monitor.worked(70);
+ }
+
+ // Receiver information
+ String receivers =
+ readEmbeddedTextFileStudio(WIDGET_RECEIVER_TEMPLATE_PATH, parameters);
+ manifestTemplate = manifestTemplate.replaceAll(RECEIVERS, receivers);
+ monitor.worked(70);
+
+ // Min Sdk information
+ String minSdkVersion = (String) parameters.get(MIN_SDK_VERSION);
+ if ((minSdkVersion != null) && (minSdkVersion.length() > 0))
+ {
+ String usesSdk =
+ readEmbeddedTextFileStudio(WIDGET_USES_SDK_TEMPLATE_PATH, parameters);
+ manifestTemplate = manifestTemplate.replaceAll(USES_SDK, usesSdk);
+ monitor.worked(50);
+ }
+ else
+ {
+ manifestTemplate = manifestTemplate.replaceAll(USES_SDK, ""); //$NON-NLS-1$
+ monitor.worked(50);
+ }
+
+ InputStream stream = null;
+
+ try
+ {
+ stream = new ByteArrayInputStream(manifestTemplate.getBytes("UTF-8")); //$NON-NLS-1$
+ manifestFile.create(stream, IResource.NONE,
+ new SubProgressMonitor(monitor, 100));
+ }
+ finally
+ {
+ try
+ {
+ if (stream != null)
+ {
+ stream.close();
+ }
+ }
+ catch (IOException e)
+ {
+ StudioLogger.info(
+ "Could not close stream while creating manifest for widget",
+ e.getMessage());
+ }
+ }
+ }
+ }
+ finally
+ {
+ monitor.done();
+ }
+ }
+
+ private static String readEmbeddedTextFileADT(String template, Map<String, Object> parameters)
+ {
+ String loadedTemplate = AdtPlugin.readEmbeddedTextFile(template);
+
+ for (String key : parameters.keySet())
+ {
+ if (parameters.get(key) instanceof String)
+ {
+ loadedTemplate = loadedTemplate.replaceAll(key, (String) parameters.get(key));
+ }
+ }
+
+ return loadedTemplate;
+ }
+
+ private static String readEmbeddedTextFileStudio(String template, Map<String, Object> parameters)
+ {
+ String loadedTemplate =
+ EclipseUtils.readEmbeddedResource(AndroidPlugin.getDefault().getBundle(), template);
+
+ for (String key : parameters.keySet())
+ {
+ if (parameters.get(key) instanceof String)
+ {
+ loadedTemplate = loadedTemplate.replaceAll(key, (String) parameters.get(key));
+ }
+ }
+
+ return loadedTemplate;
+ }
+
+ /**
+ * Setup src folders
+ * @param javaProject
+ * @param sourceFolder
+ * @param monitor
+ * @throws JavaModelException
+ */
+ private static void setupSourceFolders(IJavaProject javaProject, List<String> sourceFolders,
+ IProgressMonitor monitor) throws JavaModelException
+ {
+ monitor.beginTask(AndroidNLS.UI_ProjectCreationSupport_Preparing_Source_Folders_Task,
+ (sourceFolders.size() * 100) + 100);
+ try
+ {
+ IProject project = javaProject.getProject();
+ IClasspathEntry[] entries = javaProject.getRawClasspath();
+
+ for (String sourceFolder : sourceFolders)
+ {
+ IFolder srcFolder = project.getFolder(sourceFolder);
+ entries = removeClasspathEntry(entries, srcFolder);
+ entries = removeClasspathEntry(entries, srcFolder.getParent());
+ entries =
+ ProjectUtils.addEntryToClasspath(entries,
+ JavaCore.newSourceEntry(srcFolder.getFullPath()));
+ monitor.worked(100);
+ }
+
+ javaProject.setRawClasspath(entries, new SubProgressMonitor(monitor, 100));
+ }
+ finally
+ {
+ monitor.done();
+ }
+ }
+
+ /**
+ * Remove source folder from classpath
+ * @param entries
+ * @param folder
+ * @return
+ */
+ private static IClasspathEntry[] removeClasspathEntry(IClasspathEntry[] entries,
+ IContainer folder)
+ {
+
+ IClasspathEntry[] newClassPath = null;
+
+ if (folder != null)
+ {
+ IClasspathEntry removeEntry = JavaCore.newSourceEntry(folder.getFullPath());
+ List<IClasspathEntry> entriesList = Arrays.asList(entries);
+
+ if (entriesList.contains(removeEntry))
+ {
+ newClassPath = new IClasspathEntry[entries.length - 1];
+ int i = 0;
+ for (IClasspathEntry entry : entriesList)
+ {
+ if (!entry.equals(removeEntry))
+ {
+ newClassPath[i] = entry;
+ i++;
+ }
+ }
+ }
+ else
+ {
+ newClassPath = entries;
+ }
+ }
+ else
+ {
+ newClassPath = entries;
+ }
+ return newClassPath;
+ }
+
+ /**
+ * Add default directory to Project
+ * @param project
+ * @param parentFolder
+ * @param folderName
+ * @param monitor
+ * @throws CoreException
+ */
+ private static void createDefaultDir(IProject project, String parentFolder, String folderName,
+ IProgressMonitor monitor) throws CoreException
+ {
+ monitor.beginTask(
+ AndroidNLS.UI_ProjectCreationSupport_Creating_Directory_Task + folderName, 100);
+
+ try
+ {
+ monitor.setTaskName(AndroidNLS.UI_ProjectCreationSupport_Verifying_Directory_Task);
+ if (folderName.length() > 0)
+ {
+ monitor.worked(10);
+ IFolder folder = project.getFolder(parentFolder + folderName);
+ monitor.worked(10);
+ if (!folder.exists())
+ {
+ monitor.worked(10);
+ if (FileUtil.canWrite(folder.getLocation().toFile()))
+ {
+ monitor.worked(10);
+ monitor.setTaskName(AndroidNLS.UI_ProjectCreationSupport_Creating_Directory_Task);
+ folder.create(true, true, new SubProgressMonitor(monitor, 60));
+ }
+ else
+ {
+ String errMsg =
+ NLS.bind(
+ AndroidNLS.EXC_ProjectCreationSupport_CannotCreateFolderReadOnlyWorkspace,
+ folder.getLocation().toFile().toString());
+ IStatus status = new AndroidStatus(IStatus.ERROR, errMsg);
+ throw new CoreException(status);
+ }
+ }
+ }
+ }
+ finally
+ {
+ monitor.done();
+ }
+ }
+
+ /**
+ * Validate new Project Location.
+ * @param destination
+ * @param display
+ * @return
+ */
+ public static boolean validateNewProjectLocationIsEmpty(IPath destination)
+ {
+ File f = new File(destination.toOSString());
+ if (f.isDirectory() && (f.list().length > 0))
+ {
+ // EclipseUtils.showErrorDialog(
+ // AndroidNLS.UI_ProjectCreationSupport_NonEmptyFolderQuestionDialogTitle,
+ // AndroidNLS.UI_ProjectCreationSupport_NonEmptyFolderQuestion);
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Run the operation in async thread.
+ * @param op
+ * @param container
+ *
+ * @return true if no errors occur during the operation or false otherwise
+ */
+ private static boolean runAsyncOperation(WorkspaceModifyOperation op, IWizardContainer container)
+ {
+ boolean created = false;
+
+ try
+ {
+ container.run(true, true, op);
+ created = true;
+ }
+ catch (InvocationTargetException ite)
+ {
+ Throwable t = ite.getTargetException();
+ if (t instanceof CoreException)
+ {
+ CoreException core = (CoreException) t;
+ if (core.getStatus().getCode() == IResourceStatus.CASE_VARIANT_EXISTS)
+ {
+ MessageDialog.openError(container.getShell(),
+ AndroidNLS.UI_GenericErrorDialogTitle,
+ AndroidNLS.ERR_ProjectCreationSupport_CaseVariantExistsError);
+ }
+ else
+ {
+ ErrorDialog.openError(container.getShell(),
+ AndroidNLS.UI_GenericErrorDialogTitle, null, core.getStatus());
+ }
+ }
+ else
+ {
+ MessageDialog.openError(container.getShell(),
+ AndroidNLS.UI_GenericErrorDialogTitle, t.getMessage());
+ }
+ }
+ catch (InterruptedException e)
+ {
+ StudioLogger.error(ProjectCreationSupport.class, "Error creating project.", e); //$NON-NLS-1$
+ }
+
+ return created;
+ }
+
+ /**
+ * Checks if a project can be created on workspace
+ *
+ * @param root The workspace root
+ * @param projectName The project name
+ *
+ * @return true if the project can be created or false otherwise
+ */
+ private static boolean canCreateProject(IWorkspaceRoot root, String projectName)
+ {
+ File rootFolder = root.getLocation().toFile();
+ File projectFolder = new File(rootFolder, projectName);
+
+ return FileUtil.canWrite(projectFolder);
+ }
+
+ /**
+ * Undoes a project creation. Removes all files created by the project creation process
+ * and keeps any other previous files
+ *
+ * @param project The failed project
+ * @param existingResources A set containing the path of pre-existing resources before creating
+ * the project
+ */
+ private static void undoProjectCreation(IProject project)
+ {
+ File projectPath =
+ new File(project.getWorkspace().getRoot().getLocation().toFile(), project.getName());
+ Set<String> existingResources = getExistingResources(projectPath);
+
+ try
+ {
+ project.delete(false, true, new NullProgressMonitor());
+ }
+ catch (CoreException e1)
+ {
+ // Do nothing
+ StudioLogger.error(ProjectCreationSupport.class, e1.getLocalizedMessage(), e1);
+ }
+
+ if (existingResources.isEmpty())
+ {
+ try
+ {
+ FileUtil.deleteDirRecursively(project.getLocation().toFile());
+ }
+ catch (IOException e)
+ {
+ // Do nothing
+ StudioLogger.error(ProjectCreationSupport.class, e.getLocalizedMessage(), e);
+ }
+ }
+ else
+ {
+ File root =
+ new File(project.getWorkspace().getRoot().getLocation().toFile(),
+ project.getName());
+ removeCreatedResources(root, existingResources);
+ }
+ }
+
+ /**
+ * Retrieves a list of existing sub-resources from a folder
+ *
+ * @param folder the File object representing the folder
+ *
+ * @return a list of existing sub-resources from the folder
+ */
+ private static Set<String> getExistingResources(File folder)
+ {
+ Set<String> existing = new HashSet<String>();
+
+ if ((folder != null) && folder.exists() && folder.isDirectory())
+ {
+ existing.add(folder.toString());
+
+ File[] children = folder.listFiles();
+
+ if (children != null)
+ {
+ for (File child : children)
+ {
+ if (child.isDirectory())
+ {
+ existing.addAll(getExistingResources(child));
+ }
+ else
+ {
+ existing.add(child.toString());
+ }
+ }
+ }
+ }
+
+ return existing;
+ }
+
+ /**
+ * Removes the created resources by a failed project creation process
+ *
+ * @param startingPoint The project root folder (File object)
+ * @param existingResources The set containing the previous existing resources in the project root folder
+ */
+ private static void removeCreatedResources(File startingPoint, Set<String> existingResources)
+ {
+ File[] members = startingPoint.listFiles();
+
+ if (members != null)
+ {
+ for (File child : members)
+ {
+ if (child.isFile())
+ {
+ if (!existingResources.contains(child.toString()))
+ {
+ child.delete();
+ }
+ }
+ else
+ {
+ removeCreatedResources(child, existingResources);
+ }
+ }
+ }
+
+ if (!existingResources.contains(startingPoint.toString()))
+ {
+ startingPoint.delete();
+ }
+ }
+}
diff --git a/src/plugins/android/src/com/motorola/studio/android/monkey/options/IMonkeyOptionsConstants.java b/src/plugins/android/src/com/motorola/studio/android/monkey/options/IMonkeyOptionsConstants.java
new file mode 100644
index 0000000..8e7d858
--- /dev/null
+++ b/src/plugins/android/src/com/motorola/studio/android/monkey/options/IMonkeyOptionsConstants.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.monkey.options;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * This interface contains constants used for the Monkey Options Management
+ */
+@SuppressWarnings("serial")
+public interface IMonkeyOptionsConstants
+{
+
+ /*
+ * XML Path
+ */
+ public final String MONKEY_OPTIONS_XML_PATH = "resources/monkey_options.xml";
+
+ /*
+ * XML tags
+ */
+ public final String ROOT_TAG = "monkeyOptions";
+
+ public final String GROUP_TAG = "group";
+
+ public final String GROUP_TAG_ID = "id";
+
+ public final String MONKEY_OPT_TAG = "monkeyOption";
+
+ public final String MONKEY_OPT_TAG_NAME = "name";
+
+ public final String MONKEY_OPT_TAG_FRIENDLY_NAME = "fName";
+
+ public final String MONKEY_OPT_TAG_TYPE = "type";
+
+ public final String MONKEY_OPT_TAG_TYPE_DETAILS = "typeDetails";
+
+ public final String MONKEY_OPT_TAG_DESCRIPTION = "description";
+
+ public final String PREDEFINED_VALUES_TAG = "values";
+
+ public final String PREDEFINED_VALUE_TAG = "value";
+
+ /*
+ * Monkey option value type
+ */
+ public final int TYPE_NONE = 0;
+
+ public final int TYPE_TEXT = 1;
+
+ public final int TYPE_PATH = 2;
+
+ public final int TYPE_NUMBER = 3;
+
+ public final String TYPE_PATH_DIR = "dir";
+
+ public final Map<String, Integer> TYPE_MAP = new HashMap<String, Integer>()
+ {
+ {
+ put("none", TYPE_NONE);
+ put("text", TYPE_TEXT);
+ put("path", TYPE_PATH);
+ put("int", TYPE_NUMBER);
+ }
+
+ };
+
+ /*
+ * Other options
+ */
+ public final String OTHERS_GROUP = "Others";
+
+ public final String OTHERS_OTHER = "other";
+
+ /*
+ * Categories options
+ */
+ public final String CATEGORY_OPTION = "-c";
+}
diff --git a/src/plugins/android/src/com/motorola/studio/android/monkey/options/MonkeyOption.java b/src/plugins/android/src/com/motorola/studio/android/monkey/options/MonkeyOption.java
new file mode 100644
index 0000000..54963b4
--- /dev/null
+++ b/src/plugins/android/src/com/motorola/studio/android/monkey/options/MonkeyOption.java
@@ -0,0 +1,306 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.monkey.options;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Combo;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.swt.widgets.Widget;
+
+/**
+ * Bean that represents an monkey option
+ */
+public class MonkeyOption
+{
+
+ // Checked status (whether the monkey options is being used or not)
+ private boolean checked;
+
+ // Widget that represents the checked status in the UI
+ private Widget checkedWidget;
+
+ // monkey option name
+ private String name;
+
+ // monkey option user-friendly name
+ private String userFriendlyName;
+
+ // monkey option description (user-friendly description)
+ private String description;
+
+ // monkey option type (which type of values that the monkey option accepts)
+ private int type;
+
+ // monkey option type details (details of the values that the monkey option accepts)
+ private String typeDetails;
+
+ // monkey option value (monkey option configured value)
+ private String value;
+
+ // Widget that represents the monkey option value in the UI
+ private Widget valueWidget;
+
+ // monkey option predefined values (list of values the monkey option accepts)
+ private List<String> preDefinedValues;
+
+ /**
+ * Constructor
+ *
+ * @param name
+ * @param type
+ */
+ public MonkeyOption(String name, int type)
+ {
+ this.checked = false;
+ this.name = name;
+ this.type = type;
+ this.value = "";
+ this.preDefinedValues = new ArrayList<String>();
+ }
+
+ /**
+ * Get monkey option name
+ *
+ * @return monkey option name
+ */
+ public String getName()
+ {
+ return name;
+ }
+
+ /**
+ * Set monkey option name
+ *
+ * @param name monkey option name
+ */
+ public void setName(String name)
+ {
+ this.name = name;
+ }
+
+ /**
+ * Get monkey option user-friendly name
+ *
+ * @return
+ */
+ public String getUserFriendlyName()
+ {
+ return userFriendlyName;
+ }
+
+ /**
+ * Set monkey option user-friendly name
+ *
+ * @param userFriendlyName
+ */
+ public void setUserFriendlyName(String userFriendlyName)
+ {
+ this.userFriendlyName = userFriendlyName;
+ }
+
+ /**
+ * Get monkey option type
+ *
+ * @return monkey option type
+ */
+ public int getType()
+ {
+ return type;
+ }
+
+ /**
+ * Set monkey option type
+ *
+ * @param type monkey option type
+ */
+ public void setType(int type)
+ {
+ this.type = type;
+ }
+
+ /**
+ * Get monkey option value
+ *
+ * @return monkey option value
+ */
+ public String getValue()
+ {
+ return value;
+ }
+
+ /**
+ * Set monkey option value
+ *
+ * @param value monkey option value
+ */
+ public void setValue(String value)
+ {
+ this.value = value;
+ }
+
+ /**
+ * Get monkey option pre-defined values
+ *
+ * @return monkey option pre-defined values
+ */
+ public List<String> getPreDefinedValues()
+ {
+ return preDefinedValues;
+ }
+
+ /**
+ * Set monkey option pre-defined values
+ *
+ * @param preDefinedValues monkey option pre-defined values
+ */
+ public void setPreDefinedValues(List<String> preDefinedValues)
+ {
+ this.preDefinedValues = preDefinedValues;
+ }
+
+ /**
+ * Check if the monkey option is being used
+ *
+ * @return true if the monkey option is being used, false otherwise
+ */
+ public boolean isChecked()
+ {
+ return checked;
+ }
+
+ /**
+ * Set that the monkey option is being used or not
+ *
+ * @param checked true if the monkey option is being used, false otherwise
+ */
+ public void setChecked(boolean checked)
+ {
+ this.checked = checked;
+ }
+
+ /**
+ * Get monkey option description
+ *
+ * @return monkey option description
+ */
+ public String getDescription()
+ {
+ return description;
+ }
+
+ /**
+ * Set monkey option description
+ *
+ * @param description monkey option description
+ */
+ public void setDescription(String description)
+ {
+ this.description = description;
+ }
+
+ /**
+ * Get monkey option type details
+ *
+ * @return monkey option type details
+ */
+ public String getTypeDetails()
+ {
+ return typeDetails;
+ }
+
+ /**
+ * Get monkey option type details
+ *
+ * @param typeDetails valuable information for validating if the value assigned is correct
+ */
+ public void setTypeDetails(String typeDetails)
+ {
+ this.typeDetails = typeDetails;
+ }
+
+ /**
+ * Get the widget that represents the checked status in the UI
+ *
+ * @return widget that represents the checked status in the UI
+ */
+ public Widget getCheckedWidget()
+ {
+ return checkedWidget;
+ }
+
+ /**
+ * Set the widget that represents the checked status in the UI
+ *
+ * @param checkedWidget widget that represents the checked status in the UI
+ */
+ public void setCheckedWidget(Widget checkedWidget)
+ {
+ this.checkedWidget = checkedWidget;
+ }
+
+ /**
+ * Get the widget that represents the monkey option value in the UI
+ *
+ * @return widget that represents the monkey option value in the UI
+ */
+ public Widget getValueWidget()
+ {
+ return valueWidget;
+ }
+
+ /**
+ * Set the widget that represents the monkey option value in the UI
+ *
+ * @param valueWidget widget that represents the monkey option value in the UI
+ */
+ public void setValueWidget(Widget valueWidget)
+ {
+ this.valueWidget = valueWidget;
+ }
+
+ /**
+ * Update the widgets that represent this monkey options in the UI
+ * by changing their state to match the current values for checked and value
+ */
+ public void updateUI()
+ {
+ if ((checkedWidget != null) && !checkedWidget.isDisposed())
+ {
+ ((Button) this.checkedWidget).setSelection(this.checked);
+ }
+ if ((valueWidget != null) && !checkedWidget.isDisposed())
+ {
+ if (this.valueWidget instanceof Text)
+ {
+ ((Text) this.valueWidget).setText(this.value);
+ }
+ else if (this.valueWidget instanceof Combo)
+ {
+ if ((this.value == null) || (this.value.equals("")))
+ {
+ ((Combo) this.valueWidget).deselectAll();
+ }
+ else
+ {
+ ((Combo) this.valueWidget).select(getPreDefinedValues().indexOf(this.value));
+ }
+ }
+ }
+ }
+}
diff --git a/src/plugins/android/src/com/motorola/studio/android/monkey/options/MonkeyOptionsGroup.java b/src/plugins/android/src/com/motorola/studio/android/monkey/options/MonkeyOptionsGroup.java
new file mode 100644
index 0000000..7b69aec
--- /dev/null
+++ b/src/plugins/android/src/com/motorola/studio/android/monkey/options/MonkeyOptionsGroup.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.monkey.options;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Bean that represents an monkey options group
+ */
+public class MonkeyOptionsGroup
+{
+ // Group ID
+ private String id;
+
+ // Group Title (user-friendly title)
+ private String title;
+
+ // Monkey options (list of the monkey options in this group)
+ private List<MonkeyOption> monkeyOptions = new ArrayList<MonkeyOption>();
+
+ /**
+ * Constructor
+ *
+ * @param id
+ */
+ public MonkeyOptionsGroup(String id)
+ {
+ this.id = id;
+ }
+
+ /**
+ * Get monkey option group ID
+ *
+ * @return monkey option group ID
+ */
+ public String getId()
+ {
+ return id;
+ }
+
+ /**
+ * Set monkey option group ID
+ *
+ * @param id monkey option group ID
+ */
+ public void setId(String id)
+ {
+ this.id = id;
+ }
+
+ /**
+ * Get the monkey options in this group
+ *
+ * @return monkey options in this group
+ */
+ public List<MonkeyOption> getMonkeyOptions()
+ {
+ return monkeyOptions;
+ }
+
+ /**
+ * Set the monkey options in this group
+ *
+ * @param monkeyOptions monkey options in this group
+ */
+ public void setMonkeyOptions(List<MonkeyOption> monkeyOptions)
+ {
+ this.monkeyOptions = monkeyOptions;
+ }
+
+ /**
+ * Get monkey option group title
+ *
+ * @return monkey option group title
+ */
+ public String getTitle()
+ {
+ return title;
+ }
+
+ /**
+ * Set monkey option group title
+ *
+ * @param title monkey option group title
+ */
+ public void setTitle(String title)
+ {
+ this.title = title;
+ }
+
+}
diff --git a/src/plugins/android/src/com/motorola/studio/android/monkey/options/MonkeyOptionsMgt.java b/src/plugins/android/src/com/motorola/studio/android/monkey/options/MonkeyOptionsMgt.java
new file mode 100644
index 0000000..7162a32
--- /dev/null
+++ b/src/plugins/android/src/com/motorola/studio/android/monkey/options/MonkeyOptionsMgt.java
@@ -0,0 +1,829 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.monkey.options;
+
+import java.io.File;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+
+import org.eclipse.core.runtime.Path;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.osgi.util.NLS;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NodeList;
+
+import com.motorola.studio.android.AndroidPlugin;
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.i18n.AndroidNLS;
+
+/**
+ * This class provides methods to manage monkey options
+ */
+public class MonkeyOptionsMgt implements IMonkeyOptionsConstants
+{
+
+ /**
+ * List of all monkey options groups (the monkey options themselves will
+ * be accessed through the use of a method in the group object that returns
+ * the monkey options in that group)
+ */
+ private static List<MonkeyOptionsGroup> monkeyOptionsGroupsList = null;
+
+ /**
+ * List of all monkey options, indexed by their names, for fast access
+ */
+ private static Map<String, MonkeyOption> monkeyOptionsMap = new HashMap<String, MonkeyOption>();
+
+ /*
+ * Load the monkey options / groups list
+ */
+ static
+ {
+ load();
+ }
+
+ /**
+ * Get the monkey options groups list
+ *
+ * @return monkey options groups list
+ */
+ public static List<MonkeyOptionsGroup> getMonkeyOptionsGroupsList()
+ {
+ return monkeyOptionsGroupsList;
+ }
+
+ /**
+ * Read all groups and monkey options available for editing from a XML
+ * and stores the information in the correspondent beans
+ */
+ public static void load()
+ {
+
+ try
+ {
+ // Clear monkey options groups list
+ monkeyOptionsGroupsList = new ArrayList<MonkeyOptionsGroup>();
+
+ // Define XML path
+ InputStream xmlStream =
+ AndroidPlugin.getDefault().getBundle().getEntry(MONKEY_OPTIONS_XML_PATH)
+ .openStream();
+
+ // Load XML
+ DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+ DocumentBuilder builder = factory.newDocumentBuilder();
+ Document document = builder.parse(xmlStream);
+
+ /*
+ * Iterate through Monkey Groups
+ */
+ Element rootNode = document.getDocumentElement();
+ NodeList monkeyOptionsGroups = rootNode.getElementsByTagName(GROUP_TAG);
+ for (int i = 0; i < monkeyOptionsGroups.getLength(); i++)
+ {
+ /*
+ * Create group
+ */
+ Element group = (Element) monkeyOptionsGroups.item(i);
+ String strKey = group.getAttributeNode(GROUP_TAG_ID).getNodeValue();
+
+ strKey =
+ Platform.getResourceString(AndroidPlugin.getDefault().getBundle(),
+ strKey.trim());
+
+ MonkeyOptionsGroup monkeyOptionsGroup = new MonkeyOptionsGroup(strKey);
+ monkeyOptionsGroup.setTitle(monkeyOptionsGroup.getId());
+
+ /*
+ * Iterate through Monkey Options in this group
+ */
+ NodeList monkeyOptions = group.getElementsByTagName(MONKEY_OPT_TAG);
+ monkeyOptionsGroup.setMonkeyOptions(new ArrayList<MonkeyOption>()); // clear monkey options
+ for (int j = 0; j < monkeyOptions.getLength(); j++)
+ {
+ /*
+ * Create monkey option
+ */
+ Element option = (Element) monkeyOptions.item(j);
+ MonkeyOption monkeyOption =
+ new MonkeyOption(option.getAttributeNode(MONKEY_OPT_TAG_NAME)
+ .getNodeValue(), getMonkeyOptionType(option.getAttributeNode(
+ MONKEY_OPT_TAG_TYPE).getNodeValue())); // name and type
+
+ strKey = option.getAttributeNode(MONKEY_OPT_TAG_FRIENDLY_NAME).getNodeValue();
+
+ strKey =
+ Platform.getResourceString(AndroidPlugin.getDefault().getBundle(),
+ strKey.trim());
+
+ monkeyOption.setUserFriendlyName(strKey); // friendly name
+
+ strKey =
+ option.getElementsByTagName(MONKEY_OPT_TAG_DESCRIPTION).item(0)
+ .getTextContent();
+
+ strKey =
+ Platform.getResourceString(AndroidPlugin.getDefault().getBundle(),
+ strKey.trim());
+
+ monkeyOption.setDescription(strKey); // description
+ if (option.getAttributeNode(MONKEY_OPT_TAG_TYPE_DETAILS) != null)
+ {
+ monkeyOption.setTypeDetails(option.getAttributeNode(
+ MONKEY_OPT_TAG_TYPE_DETAILS).getNodeValue()); // type details
+ }
+ // Iterate through monkey option pre-defined values, if any
+ NodeList preDefinedValuesContainer =
+ option.getElementsByTagName(PREDEFINED_VALUES_TAG);
+ monkeyOption.setPreDefinedValues(new ArrayList<String>()); // clear pre-defined values
+ if (preDefinedValuesContainer.getLength() > 0)
+ {
+ NodeList preDefinedValues =
+ ((Element) preDefinedValuesContainer.item(0))
+ .getElementsByTagName(PREDEFINED_VALUE_TAG);
+ for (int k = 0; k < preDefinedValues.getLength(); k++)
+ {
+ // Add pre-defined values to the option
+ Element preDefinedValue = (Element) preDefinedValues.item(k);
+ monkeyOption.getPreDefinedValues()
+ .add(preDefinedValue.getTextContent());
+ }
+ }
+
+ /*
+ * Add monkey options to the group
+ */
+ monkeyOptionsGroup.getMonkeyOptions().add(monkeyOption);
+
+ monkeyOptionsMap.put(monkeyOption.getName(), monkeyOption);
+
+ }
+
+ /*
+ * Add groups to the groups list
+ */
+ monkeyOptionsGroupsList.add(monkeyOptionsGroup);
+ }
+
+ }
+ catch (Exception e)
+ {
+ StudioLogger.error("Failed to load monkey options");
+ }
+
+ }
+
+ /**
+ * Validate the values assigned for the monkey options marked as checked (the ones that are
+ * being used), according to its type and type details.
+ *
+ * @return Status object with the result of the validation
+ */
+ public static Status validate()
+ {
+ Status status = (Status) Status.OK_STATUS;
+ String msg = null;
+
+ /*
+ * Iterate through Monkey Groups
+ */
+ for (MonkeyOptionsGroup group : getMonkeyOptionsGroupsList())
+ {
+ /*
+ * Iterate through monkey options in this group
+ */
+ for (MonkeyOption monkeyOption : group.getMonkeyOptions())
+ {
+ /*
+ * Check if the Monkey Option is checked
+ */
+ if (monkeyOption.isChecked() && (status.isOK()))
+ {
+
+ String name = monkeyOption.getName(); // monkey option name
+ String ufname = monkeyOption.getUserFriendlyName(); // user-friendly monkey option name
+ String value = monkeyOption.getValue(); // monkey option value
+ String typeDetails = monkeyOption.getTypeDetails(); // monkey option type detail
+
+ /*
+ * General validation: no quotes in values
+ */
+ if ((!monkeyOption.getName().equals(OTHERS_OTHER))
+ && (value.indexOf("\"") >= 0))
+ {
+ msg =
+ NLS.bind(AndroidNLS.ERR_PropertiesMainComposite_Monkey_NoQuotes,
+ ufname);
+ }
+ else
+ {
+
+ /*
+ * Call the appropriate validation method
+ */
+ switch (monkeyOption.getType())
+ {
+ case TYPE_TEXT:
+ msg = validateTextField(name, ufname, value, typeDetails);
+ break;
+
+ case TYPE_NUMBER:
+ msg = validadeNumberField(name, ufname, value, typeDetails);
+ break;
+
+ case TYPE_PATH:
+ msg = validadePathField(name, ufname, value, typeDetails);
+ break;
+ }
+ }
+
+ /*
+ * If some validation has failed, return with an error message
+ */
+ if (msg != null)
+ {
+ status = new Status(Status.ERROR, AndroidPlugin.PLUGIN_ID, msg);
+ break;
+ }
+
+ }
+ }
+ }
+
+ return status;
+
+ }
+
+ /**
+ * Validate the monkey option value for an monkey option of "text" type
+ *
+ * @param name the monkey option name
+ * @param ufname the user-friendly monkey option name
+ * @param value the current assigned value for the monkey option
+ * @param typeDetails any special requirements that the assigned value must be match
+ * @return null if the value assigned for the monkey option is a valid one or an error message otherwise
+ */
+ private static String validateTextField(String name, String ufName, String value,
+ String typeDetails)
+ {
+ String msg = null;
+
+ // Check if the value is blank
+ if ((value == null) || (value.equals("")))
+ {
+ msg = NLS.bind(AndroidNLS.ERR_PropertiesMainComposite_Monkey_TextBlank, ufName);
+ }
+
+ return msg;
+
+ }
+
+ /**
+ * Validate the monkey option value for an monkey option of "number" type
+ *
+ * @param name the monkey option name
+ * @param ufname the user-friendly monkey option name
+ * @param value the current assigned value for the monkey option
+ * @param typeDetails any special requirements that the assigned value must be match
+ * @return null if the value assigned for the monkey option is a valid one or an error message otherwise
+ */
+ private static String validadeNumberField(String name, String ufName, String value,
+ String typeDetails)
+ {
+ String msg = null;
+
+ // Check if the value is blank
+ if ((value == null) || (value.equals("")))
+ {
+ msg = NLS.bind(AndroidNLS.ERR_PropertiesMainComposite_Monkey_NumberRequired, ufName);
+ }
+ else
+ {
+
+ try
+ {
+ /*
+ * Check if it's an Integer.
+ * If it's not, an exception will be thrown
+ */
+ int intValue = Integer.parseInt(value);
+
+ /*
+ * Check if it's positive
+ */
+ if (intValue < 0)
+ {
+ msg =
+ NLS.bind(
+ AndroidNLS.ERR_PropertiesMainComposite_Monkey_NumberMustBePositiveInteger,
+ ufName);
+ }
+ else
+ {
+
+ /*
+ * Check if the value is in the correct range
+ */
+ if (typeDetails != null)
+ {
+ String[] valueRange = typeDetails.split(";");
+ if ((intValue < Integer.parseInt(valueRange[0]))
+ || (intValue > Integer.parseInt(valueRange[1])))
+ {
+ // the value is not in the correct range
+ msg =
+ NLS.bind(
+ AndroidNLS.ERR_PropertiesMainComposite_Monkey_NumberIntRange,
+ new String[]
+ {
+ ufName, valueRange[0], valueRange[1]
+ });
+ }
+ }
+ }
+
+ }
+ catch (NumberFormatException ex)
+ {
+ // it's not a number
+ msg =
+ NLS.bind(AndroidNLS.ERR_PropertiesMainComposite_Monkey_NumberMustBeInteger,
+ ufName);
+ }
+ }
+
+ return msg;
+
+ }
+
+ /**
+ * Validate the monkey option value for an monkey option of "path" type
+ *
+ * @param name the monkey option name
+ * @param ufname the user-friendly monkey option name
+ * @param value the current assigned value for the monkey option
+ * @param typeDetails any special requirements that the assigned value must be match
+ * @return null if the value assigned for the monkey option is a valid one or an error message otherwise
+ */
+ private static String validadePathField(String name, String ufName, String value,
+ String typeDetails)
+ {
+ String msg = null;
+
+ if ((value == null) || (value.equals("")))
+ {
+ msg = NLS.bind(AndroidNLS.ERR_PropertiesMainComposite_Monkey_PathRequired, ufName);
+ }
+ else
+ {
+
+ File file = new File(value);
+
+ /*
+ * Validate folder
+ */
+ if (typeDetails.equals(TYPE_PATH_DIR))
+ {
+ /*
+ * Check if the path exists
+ */
+ if (!file.exists())
+ {
+ // the folder doesn't exist
+ msg =
+ NLS.bind(AndroidNLS.ERR_PropertiesMainComposite_Monkey_PathDirNotExist,
+ ufName);
+ }
+ else
+ {
+ if (file.isFile())
+ {
+ // it's not a folder
+ msg =
+ NLS.bind(
+ AndroidNLS.ERR_PropertiesMainComposite_Monkey_PathMustBeDir,
+ ufName);
+ }
+ }
+ }
+ /*
+ * Validate file
+ */
+ else
+ {
+ if (!file.exists())
+ {
+ // the file doesn't exist
+ msg =
+ NLS.bind(
+ AndroidNLS.ERR_PropertiesMainComposite_Monkey_PathFileNotExist,
+ ufName);
+ }
+ else
+ {
+ // it's not a file
+ if (file.isDirectory())
+ {
+ msg =
+ NLS.bind(
+ AndroidNLS.ERR_PropertiesMainComposite_Monkey_PathMustBeFile,
+ ufName);
+ }
+ // it doesn't have the correct extension
+ else
+ {
+ if (!typeDetails.equals("." + (new Path(value)).getFileExtension()))
+ {
+ msg =
+ NLS.bind(
+ AndroidNLS.ERR_PropertiesMainComposite_Monkey_PathIncorrectFileType,
+ new String[]
+ {
+ ufName, typeDetails
+ });
+ }
+ }
+ }
+ }
+ }
+ return msg;
+
+ }
+
+ /**
+ * Generates the list of parameters that shall to be sent to adb shell
+ *
+ * @return the list of parameters that shall to be sent to the adb shell
+ */
+ public static String getParamList()
+ {
+ String paramList = "";
+
+ /*
+ * Iterate through Monkey Groups
+ */
+ for (MonkeyOptionsGroup group : getMonkeyOptionsGroupsList())
+ {
+ /*
+ * Iterate through Monkey Options in this group
+ */
+ int monkeyOptionType;
+ for (MonkeyOption monkeyOption : group.getMonkeyOptions())
+ {
+ monkeyOptionType = monkeyOption.getType();
+ if (monkeyOption.isChecked()) // check if the monkey option is being used
+ {
+ if (monkeyOptionType == TYPE_NONE)
+ {
+ paramList += ((paramList.equals("")) ? "" : " ") + monkeyOption.getName();
+ }
+ else
+ {
+ if ((monkeyOption.getName().equals(OTHERS_OTHER)))
+ {
+
+ paramList +=
+ ((paramList.equals("")) ? "" : " ") + monkeyOption.getValue();
+
+ }
+ else
+ {
+ if ((monkeyOption.getName().equals(CATEGORY_OPTION)))
+ {
+ String[] values = monkeyOption.getValue().split(" ");
+ for (int i = 0; i < values.length; i++)
+ {
+ if (values[i].trim().length() > 0)
+ {
+ paramList +=
+ ((paramList.equals("")) ? "" : " ")
+ + monkeyOption.getName() + " " + values[i];
+ }
+ }
+ }
+ else
+ {
+ String value = monkeyOption.getValue();
+
+ if (!value.equals(""))
+ {
+ if (Platform.getOS().equals(Platform.OS_WIN32))
+ {
+ if (value.contains(" "))
+ {
+ value = "\"" + value + "\"";
+ }
+ }
+ else
+ {
+ if (value.contains("\\"))
+ {
+ value = value.replace("\\", "\\\\");
+ }
+
+ if (value.contains(" "))
+ {
+ value = value.replace(" ", "\\ ");
+ }
+ }
+
+ paramList +=
+ ((paramList.equals("")) ? "" : " ")
+ + monkeyOption.getName()
+ + (value.trim().length() > 0 ? " " + value : "");
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return paramList;
+
+ }
+
+ /**
+ * Load values from a Properties object
+ *
+ * @param properties properties object containing the values that must be loaded into de model
+ */
+ private static void loadValues(Properties properties)
+ {
+ /*
+ * Iterate through Monkey Groups
+ */
+ for (MonkeyOptionsGroup group : getMonkeyOptionsGroupsList())
+ {
+ /*
+ * Iterate through monkey options in this group
+ */
+ String soValue = "";
+ for (MonkeyOption monkeyOption : group.getMonkeyOptions())
+ {
+ soValue = properties.getProperty(monkeyOption.getName());
+ if (soValue != null)
+ {
+ monkeyOption.setChecked(true);
+ monkeyOption.setValue(soValue);
+ }
+ else
+ {
+ monkeyOption.setChecked(false);
+ monkeyOption.setValue("");
+ }
+ monkeyOption.updateUI();
+ }
+ }
+
+ }
+
+ /**
+ * Create a properties object with the information contained in a command line
+ *
+ * @param commandLine the command line used to start the emulator
+ * @return properties object with the information contained in a command line
+ */
+ public static Properties parseCommandLine(String commandLine)
+ {
+ Properties properties = new Properties();
+
+ if (!commandLine.equals(""))
+ {
+
+ /*
+ * Iterate through Monkey Groups
+ */
+ for (MonkeyOptionsGroup group : getMonkeyOptionsGroupsList())
+ {
+ /*
+ * Iterate through monkey options in this group
+ */
+ String soName, soValue = "";
+ int soType, shift = 0;
+ for (MonkeyOption monkeyOption : group.getMonkeyOptions())
+ {
+ soName = monkeyOption.getName();
+ soType = monkeyOption.getType();
+ if (commandLine.startsWith(soName))
+ {
+ if (soType == TYPE_NONE)
+ {
+ soValue = new Boolean(true).toString();
+ shift = soName.length() + 1;
+ }
+ else
+ {
+ if ((monkeyOption.getName().equals(CATEGORY_OPTION)))
+ {
+ String soValueCat = "";
+ while (commandLine.startsWith(CATEGORY_OPTION))
+ {
+ commandLine =
+ commandLine.substring(soName.length() + 1,
+ commandLine.length());
+ ParameterBean param = getNextParameterValue(commandLine);
+ soValue = param.getValue();
+ shift = param.getLastPosition() + 1;
+
+ soValueCat =
+ (soValueCat.equals("") ? soValueCat : soValueCat + " ")
+ + soValue;
+
+ if (shift < (commandLine.length() - 1))
+ {
+ commandLine =
+ commandLine.substring(shift, commandLine.length());
+ }
+ else
+ {
+ commandLine = "";
+ }
+ }
+ shift = 0;
+ soValue = soValueCat;
+ }
+ else
+ {
+ commandLine =
+ commandLine.substring(soName.length() + 1,
+ commandLine.length());
+ ParameterBean param = getNextParameterValue(commandLine);
+ soValue = param.getValue();
+ shift = param.getLastPosition() + 1;
+ }
+ }
+
+ properties.put(monkeyOption.getName(), soValue);
+
+ if (shift < (commandLine.length() - 1))
+ {
+ commandLine = commandLine.substring(shift, commandLine.length());
+ }
+ else
+ {
+ commandLine = "";
+ }
+ }
+ }
+ }
+
+ if (!commandLine.equals(""))
+ {
+ properties.put(OTHERS_OTHER, commandLine);
+ }
+ }
+
+ return properties;
+
+ }
+
+ /**
+ * Load values from a command line
+ *
+ * @param commandLine the command line used to start the emulator
+ */
+ public static void loadFromCommandLine(String commandLine)
+ {
+ loadValues(parseCommandLine(commandLine));
+ }
+
+ /**
+ * Convert the type of the monkey option from a string
+ * to a number
+ *
+ * @param type string that represents the type
+ * @return number that represents the type
+ */
+ private static int getMonkeyOptionType(String type)
+ {
+ return (TYPE_MAP.get(type)).intValue();
+ }
+
+ /**
+ * Parses the next parameter value on a command line
+ *
+ * @param commandLine the command line
+ *
+ * @return a bean containing the parameter value and the last position looked at
+ * the command line
+ */
+ private static ParameterBean getNextParameterValue(String commandLine)
+ {
+ boolean isWin32 = Platform.getOS().equals(Platform.OS_WIN32);
+ boolean escaped = false;
+ boolean quoted = false;
+
+ char c;
+ String value = "";
+ int i;
+
+ for (i = 0; i < commandLine.length(); i++)
+ {
+ c = commandLine.charAt(i);
+
+ if (escaped)
+ {
+ value += c;
+ escaped = false;
+ }
+ else if ((c == '\\') && !isWin32)
+ {
+ escaped = true;
+ }
+ else if ((c == '"') && isWin32)
+ {
+ if (value.length() == 0)
+ {
+ quoted = true;
+ }
+ else if (quoted)
+ {
+ break;
+ }
+ else
+ {
+ value += c;
+ }
+ }
+ else if ((c == ' ') && (!quoted))
+ {
+ break;
+ }
+ else
+ {
+ value += c;
+ }
+ }
+
+ return new ParameterBean(value, ((quoted) ? i + 1 : i));
+ }
+
+ /**
+ * Bean used to identify a parameter value when parsing the
+ * monkey options
+ */
+ private static class ParameterBean
+ {
+ private final String value;
+
+ private final int lastPosition;
+
+ /**
+ * Constructor
+ *
+ * @param value The parameter value
+ * @param lastPosition The last position looked at the command line before stopping
+ * the parse operation
+ */
+ public ParameterBean(String value, int lastPosition)
+ {
+ this.value = value;
+ this.lastPosition = lastPosition;
+ }
+
+ /**
+ * Retrieves the parameter value
+ *
+ * @return the parameter value
+ */
+ public String getValue()
+ {
+ return value;
+ }
+
+ /**
+ * Retrieves the last position looked at the command line before stopping
+ * the parse operation
+ *
+ * @return the last position looked at the command line before stopping
+ * the parse operation
+ */
+ public int getLastPosition()
+ {
+ return lastPosition;
+ }
+ }
+}
diff --git a/src/plugins/android/src/com/motorola/studio/android/nativeos/IDevicePropertiesOSConstants.java b/src/plugins/android/src/com/motorola/studio/android/nativeos/IDevicePropertiesOSConstants.java
new file mode 100644
index 0000000..53c101c
--- /dev/null
+++ b/src/plugins/android/src/com/motorola/studio/android/nativeos/IDevicePropertiesOSConstants.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.nativeos;
+
+public interface IDevicePropertiesOSConstants
+{
+ /**
+ * The key that identifies the useVnc property of an Android Emulator
+ * device instance
+ */
+ String useVnc = "UseVnc";
+}
diff --git a/src/plugins/android/src/com/motorola/studio/android/nativeos/INativeUI.java b/src/plugins/android/src/com/motorola/studio/android/nativeos/INativeUI.java
new file mode 100644
index 0000000..24b103b
--- /dev/null
+++ b/src/plugins/android/src/com/motorola/studio/android/nativeos/INativeUI.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.nativeos;
+
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.widgets.Composite;
+
+public interface INativeUI
+{
+ public String getDefaultUseVnc();
+
+ public String getDefaultCommandLine();
+
+ public long getWindowHandle(String windowName);
+
+ public long getWindowProperties(long windowHandle);
+
+ public void setWindowProperties(long windowHandle, long originalProperties);
+
+ public long embedWindow(long windowHandle, Composite composite);
+
+ public void unembedWindow(long windowHandle, long originalParent);
+
+ public Point getWindowSize(long originalParentHandle, long windowHandle);
+
+ public void setWindowStyle(long windowHandle);
+
+ public void hideWindow(long windowHandle);
+
+ public void showWindow(long windowHandle);
+
+ public void restoreWindow(long windowHandle);
+
+ public void sendNextLayoutCommand(long originalParent, long windowHandle);
+
+ public boolean isWindowEnabled(long windowHandle);
+
+ public void setWindowFocus(long windowHandle);
+} \ No newline at end of file
diff --git a/src/plugins/android/src/com/motorola/studio/android/nativeos/NativeUIUtils.java b/src/plugins/android/src/com/motorola/studio/android/nativeos/NativeUIUtils.java
new file mode 100644
index 0000000..7b9ac44
--- /dev/null
+++ b/src/plugins/android/src/com/motorola/studio/android/nativeos/NativeUIUtils.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.nativeos;
+
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.widgets.Composite;
+
+import com.motorola.studio.android.adt.SdkUtils;
+import com.motorola.studio.android.common.log.StudioLogger;
+
+public class NativeUIUtils
+{
+ private static INativeUI instance;
+
+ private NativeUIUtils()
+ {
+
+ }
+
+ public static INativeUI getInstance()
+ {
+ if (instance == null)
+ {
+ instance = (INativeUI) getClass("com.motorola.studio.android.nativeos.NativeUI");
+ }
+ return instance;
+ }
+
+ public static Object getClass(String nameClass)
+ {
+ try
+ {
+ return (Class.forName(nameClass)).newInstance();
+ }
+ catch (Exception ex)
+ {
+ StudioLogger.error(NativeUIUtils.class,
+ "Error resolving OS dependent class for native windows feature", ex);
+ return null;
+ }
+ }
+
+ public static String getDefaultCommandLine()
+ {
+ return getInstance().getDefaultCommandLine();
+ }
+
+ public static String getDefaultUseVnc()
+ {
+ return getInstance().getDefaultUseVnc();
+ }
+
+ public static long getWindowHandle(String avdName, int instancePort)
+ {
+ String windowName = SdkUtils.getEmulatorWindowName(avdName, instancePort);
+ return getInstance().getWindowHandle(windowName);
+ }
+
+ public static long getWindowProperties(long windowHandle)
+ {
+ return getInstance().getWindowProperties(windowHandle);
+ }
+
+ public static Point getWindowSize(long originalParentHandle, long windowHandle)
+ {
+ return getInstance().getWindowSize(originalParentHandle, windowHandle);
+ }
+
+ public static void hideWindow(long windowHandle)
+ {
+ getInstance().hideWindow(windowHandle);
+ }
+
+ public static boolean isWindowEnabled(long windowHandle)
+ {
+ return getInstance().isWindowEnabled(windowHandle);
+ }
+
+ public static void restoreWindow(long windowHandle)
+ {
+ getInstance().restoreWindow(windowHandle);
+ }
+
+ public static void sendNextLayoutCommand(long originalParent, long windowHandle)
+ {
+ getInstance().sendNextLayoutCommand(originalParent, windowHandle);
+ }
+
+ public static void setWindowFocus(long windowHandle)
+ {
+ getInstance().setWindowFocus(windowHandle);
+ }
+
+ public static long embedWindow(long windowHandle, Composite composite)
+ {
+ return getInstance().embedWindow(windowHandle, composite);
+ }
+
+ public static void unembedWindow(long windowHandle, long originalParent)
+ {
+ getInstance().unembedWindow(windowHandle, originalParent);
+ }
+
+ public static void setWindowProperties(long windowHandle, long originalProperties)
+ {
+ getInstance().setWindowProperties(windowHandle, originalProperties);
+ }
+
+ public static void setWindowStyle(long windowHandle)
+ {
+ getInstance().setWindowStyle(windowHandle);
+ }
+
+ public static void showWindow(long windowHandle)
+ {
+ getInstance().showWindow(windowHandle);
+ }
+
+}
diff --git a/src/plugins/android/src/com/motorola/studio/android/obfuscate/ObfuscatorManager.java b/src/plugins/android/src/com/motorola/studio/android/obfuscate/ObfuscatorManager.java
new file mode 100644
index 0000000..3bfaec2
--- /dev/null
+++ b/src/plugins/android/src/com/motorola/studio/android/obfuscate/ObfuscatorManager.java
@@ -0,0 +1,378 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.obfuscate;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Scanner;
+
+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.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.SubMonitor;
+import org.osgi.framework.Bundle;
+
+import com.motorola.studio.android.AndroidPlugin;
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.common.utilities.AndroidUtils;
+
+/**
+ * Manager to set Proguard files inside project
+ * Manipulates the file on <project_root>/default.properties
+ * Copy proguard.cfg
+ */
+public class ObfuscatorManager
+{
+
+ private static final String PROGUARD_PATH = "/files/proguard.cfg";
+
+ private static final String PROGUARD_SDK_PATH = "tools/lib/proguard.cfg";
+
+ private static final String PROGUARD_FILENAME = "proguard.cfg";
+
+ private static final String PROGUARD_CONFIG_STATEMENT = "proguard.config=proguard.cfg";
+
+ private static final String DEFAULT_PROPERTIES_FILENAME = "default.properties";
+
+ private static final String PROJECT_PROPERTIES_FILENAME = "project.properties";
+
+ private static final String NL = System.getProperty("line.separator");
+
+ /**
+ * Prepares the project with the Proguard settings
+ * to obfuscate when the project is exported in release mode
+ * @param project
+ * @param monitor
+ * @return
+ */
+ public static IStatus obfuscate(IProject project, IProgressMonitor monitor)
+ {
+ IStatus status = Status.OK_STATUS;
+
+ /*
+ * Project properties file is the new filename for ADT
+ */
+ File projectPropertiesFile =
+ project.getFile(PROJECT_PROPERTIES_FILENAME).getLocation().toFile();
+ File defaultPropertiesFile =
+ project.getFile(DEFAULT_PROPERTIES_FILENAME).getLocation().toFile();
+ if (projectPropertiesFile.canWrite())
+ {
+ defaultPropertiesFile = projectPropertiesFile;
+ }
+
+ File proguardFile = project.getFile(PROGUARD_FILENAME).getLocation().toFile();
+
+ try
+ {
+ addProguardLine(defaultPropertiesFile);
+ if (!fileExists(proguardFile))
+ {
+ copyProguardFile(project, monitor);
+ }
+ try
+ {
+ project.refreshLocal(IResource.DEPTH_ONE, null);
+ }
+ catch (CoreException e)
+ {
+ // Do nothing, user just have to press F5
+ }
+ }
+ catch (Exception e)
+ {
+ StudioLogger.error(ObfuscatorManager.class,
+ "Error while setting Proguard to obfuscate", e);
+ status =
+ new Status(IStatus.ERROR, AndroidPlugin.PLUGIN_ID,
+ "Could not set Proguard to obfuscate", e);
+ }
+
+ StudioLogger.collectUsageData(StudioLogger.WHAT_OBFUSCATE, StudioLogger.KIND_OBFUSCATE,
+ StudioLogger.DESCRIPTION_DEFAULT, AndroidPlugin.PLUGIN_ID, AndroidPlugin
+ .getDefault().getBundle().getVersion().toString());
+
+ return status;
+ }
+
+ /**
+ * Removes Proguard settings
+ * (Project will not be obfuscated when the is exported in release mode)
+ * @param project
+ * @return
+ */
+ public static IStatus unobfuscate(IProject project)
+ {
+ IStatus status = Status.OK_STATUS;
+
+ try
+ {
+ File projectPropertiesFile =
+ project.getFile(PROJECT_PROPERTIES_FILENAME).getLocation().toFile();
+ File defaultPropertiesFile =
+ project.getFile(DEFAULT_PROPERTIES_FILENAME).getLocation().toFile();
+ if (isProguardSet(defaultPropertiesFile))
+ {
+ removeProguardLine(defaultPropertiesFile);
+ }
+ if (isProguardSet(projectPropertiesFile))
+ {
+ removeProguardLine(projectPropertiesFile);
+ }
+ try
+ {
+ project.refreshLocal(IResource.DEPTH_ONE, null);
+ }
+ catch (CoreException e)
+ {
+ // Do nothing, user just have to press F5
+ }
+ }
+ catch (Exception e)
+ {
+ StudioLogger
+ .error(ObfuscatorManager.class, "Error while removing Proguard settings", e);
+ status =
+ new Status(IStatus.ERROR, AndroidPlugin.PLUGIN_ID,
+ "Could not remove Proguard settings", e);
+ }
+
+ StudioLogger.collectUsageData(StudioLogger.WHAT_OBFUSCATE, StudioLogger.KIND_DESOBFUSCATE,
+ StudioLogger.DESCRIPTION_DEFAULT, AndroidPlugin.PLUGIN_ID, AndroidPlugin
+ .getDefault().getBundle().getVersion().toString());
+
+ return status;
+ }
+
+ /*
+ * @param propertiesFile file to write
+ * @param newContent content to write (replaces entire file)
+ * @throws IOException
+ */
+ private static void write(File propertiesFile, String newContent) throws IOException
+ {
+ Writer out = new OutputStreamWriter(new FileOutputStream(propertiesFile));
+ try
+ {
+ out.write(newContent);
+ }
+ finally
+ {
+ if (out != null)
+ {
+ out.close();
+ }
+ }
+ }
+
+ /*
+ * @param ignoreProguardStatement true it does not return the line with proguard.config=proguard.cfg
+ * @return
+ * @throws IOException
+ */
+ private static String read(File propertiesFile, boolean ignoreProguardStatement)
+ throws IOException
+ {
+ StringBuilder text = new StringBuilder();
+ Scanner scanner = null;
+ FileInputStream stream = null;
+ try
+ {
+ stream = new FileInputStream(propertiesFile);
+ scanner = new Scanner(stream);
+ while (scanner.hasNextLine())
+ {
+ String line = scanner.nextLine();
+ if (!ignoreProguardStatement || !line.contains(PROGUARD_CONFIG_STATEMENT))
+ {
+ text.append(line + NL);
+ }
+ }
+ }
+ finally
+ {
+ try
+ {
+ if (scanner != null)
+ {
+ scanner.close();
+ }
+ if (stream != null)
+ {
+ stream.close();
+ }
+ }
+ catch (IOException e)
+ {
+ StudioLogger
+ .info("Could not close stream while reading obfuscator properties file. "
+ + e.getMessage());
+ }
+ }
+ return text.toString();
+ }
+
+ private static boolean fileExists(File proguardFile)
+ {
+ return (proguardFile != null) && proguardFile.exists();
+ }
+
+ /**
+ * Checks if default.properties have Proguard statement
+ * and if the proguard.cfg exists in the project
+ * @param project
+ * @return
+ */
+ public static boolean isProguardSet(IProject project)
+ {
+ File projectPropertiesFile =
+ project.getFile(PROJECT_PROPERTIES_FILENAME).getLocation().toFile();
+ File defaultPropertiesFile =
+ project.getFile(DEFAULT_PROPERTIES_FILENAME).getLocation().toFile();
+ File proguardFile = project.getFile(PROGUARD_FILENAME).getLocation().toFile();
+
+ return (isProguardSet(projectPropertiesFile) || isProguardSet(defaultPropertiesFile))
+ && fileExists(proguardFile);
+ }
+
+ /**
+ * Checks if default.properties have the Proguard statement
+ * @param propertiesFile
+ * @return
+ */
+ private static boolean isProguardSet(File propertiesFile)
+ {
+ String defaultPropertiesContent = null;
+ try
+ {
+ defaultPropertiesContent = read(propertiesFile, false);
+ }
+ catch (IOException e)
+ {
+ StudioLogger.error(ObfuscatorManager.class, e.getMessage(), e);
+ }
+ return ((defaultPropertiesContent != null) && defaultPropertiesContent
+ .contains(PROGUARD_CONFIG_STATEMENT));
+ }
+
+ /**
+ * Add the following line to file
+ * proguard.config=proguard.cfg
+ * if it does not exist yet
+ * @param project
+ * @throws IOException
+ */
+ private static void addProguardLine(File propertiesFile) throws IOException
+ {
+ String currentContent = null;
+ currentContent = read(propertiesFile, false);
+ if (!currentContent.toString().contains(PROGUARD_CONFIG_STATEMENT))
+ {
+ String newContent =
+ currentContent.endsWith(NL) ? currentContent + PROGUARD_CONFIG_STATEMENT
+ : currentContent + NL + PROGUARD_CONFIG_STATEMENT;
+ write(propertiesFile, newContent);
+ }
+ }
+
+ /**
+ * Remove the following line to file
+ * proguard.config=proguard.cfg
+ * if it exists
+ * @param project
+ * @throws IOException
+ */
+ private static void removeProguardLine(File propertiesFile) throws IOException
+ {
+ String contentWithoutProguardStatement = null;
+ contentWithoutProguardStatement = read(propertiesFile, true);
+ write(propertiesFile, contentWithoutProguardStatement);
+ }
+
+ private static void copyProguardFile(IProject project, IProgressMonitor monitor)
+ throws IOException, CoreException
+ {
+ SubMonitor subMonitor = SubMonitor.convert(monitor);
+
+ URL proguardURL = getProguardFileURL();
+ if (proguardURL != null)
+ {
+ InputStream is = null;
+ try
+ {
+ is = proguardURL.openStream();
+ IFile destFile = project.getFile(PROGUARD_FILENAME);
+ destFile.create(is, IResource.NONE, subMonitor);
+ }
+ finally
+ {
+ if (is != null)
+ {
+ is.close();
+ }
+ }
+ }
+ }
+
+ /**
+ * Get the most suitable proguard file, either from SDK or our plug-in
+ * @return the URL of the best proguard file found
+ */
+ private static URL getProguardFileURL()
+ {
+ String sdkPath = AndroidUtils.getSDKPathByPreference();
+ File sdkPathFile = sdkPath.isEmpty() ? new File(sdkPath) : null;
+ File proguardFromSDK = null;
+ if (sdkPathFile != null)
+ {
+ proguardFromSDK = new File(sdkPathFile, PROGUARD_SDK_PATH);
+ }
+
+ URL fileURL = null;
+ // copy newest proguard file from SDK
+ if ((proguardFromSDK != null) && proguardFromSDK.canRead())
+ {
+ try
+ {
+ fileURL = proguardFromSDK.toURI().toURL();
+ }
+ catch (MalformedURLException e)
+ {
+ StudioLogger.warn(ObfuscatorManager.class,
+ "Exception converting proguard template file to URL", e);
+ }
+ }
+ //copy file bundled with android plugin
+ else
+ {
+ Bundle bundle = AndroidPlugin.getDefault().getBundle();
+ fileURL = bundle.getEntry(PROGUARD_PATH);
+ }
+
+ return fileURL;
+ }
+}
diff --git a/src/plugins/android/src/com/motorola/studio/android/obfuscate/handlers/ObfuscateProjectsHandler.java b/src/plugins/android/src/com/motorola/studio/android/obfuscate/handlers/ObfuscateProjectsHandler.java
new file mode 100644
index 0000000..bb675be
--- /dev/null
+++ b/src/plugins/android/src/com/motorola/studio/android/obfuscate/handlers/ObfuscateProjectsHandler.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.obfuscate.handlers;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.jface.dialogs.TitleAreaDialog;
+import org.eclipse.ui.PlatformUI;
+
+import com.motorola.studio.android.model.AndroidProject;
+import com.motorola.studio.android.obfuscate.ObfuscatorManager;
+import com.motorola.studio.android.obfuscate.ui.ObfuscateDialog;
+
+/**
+ *
+ * This class is responsible for handling the command that marks/unmarks Android
+ * project to be obfuscated.
+ *
+ * Its proposal at first is to be called from the motodev menu
+ */
+public class ObfuscateProjectsHandler extends AbstractHandler
+{
+
+ public Object execute(ExecutionEvent event) throws ExecutionException
+ {
+
+ PlatformUI.getWorkbench().getDisplay().syncExec(new Runnable()
+ {
+ public void run()
+ {
+ ObfuscateDialog dialog =
+ new ObfuscateDialog(PlatformUI.getWorkbench().getActiveWorkbenchWindow()
+ .getShell());
+ if (dialog.open() == TitleAreaDialog.OK)
+ {
+ List<IProject> obfuscatedProjects = new ArrayList<IProject>();
+ ArrayList<IProject> selectedProjects = dialog.getSelectedProjects();
+
+ List<IProject> toDesobfuscate = new ArrayList<IProject>();
+ IProject[] allProjects = ResourcesPlugin.getWorkspace().getRoot().getProjects();
+
+ for (int i = 0; i < allProjects.length; i++)
+ {
+ IProject projectN = allProjects[i];
+
+ try
+ {
+ //it is an android project and is opened
+ if ((projectN.getNature(AndroidProject.ANDROID_NATURE) != null)
+ && projectN.isOpen())
+ {
+ //it is obfuscated
+ if (ObfuscatorManager.isProguardSet(projectN))
+ {
+ obfuscatedProjects.add(projectN);
+ //it was not selected -> desobfuscate it
+ if (!selectedProjects.contains(projectN))
+ {
+ toDesobfuscate.add(projectN);
+ }
+ }
+ }
+ }
+ catch (CoreException e)
+ {
+ // do nothing
+ }
+ }
+
+ //It only makes sense to perform some action if there is a project to obfuscate/desobfuscate
+ if (!(obfuscatedProjects.containsAll(selectedProjects) && toDesobfuscate
+ .isEmpty()))
+ {
+ toggleObfuscateMode(selectedProjects, toDesobfuscate);
+ }
+ }
+ }
+ });
+
+ return null;
+ }
+
+ /**
+ * Receives a list of project and toogles obfscation mode of these projects:
+ * if it is set to be obfuscated, than unset. (and vice-versa)
+ *
+ * @param selection
+ */
+ private void toggleObfuscateMode(List<IProject> _obfuscatedProjects,
+ List<IProject> _notObfuscatedProjects)
+ {
+
+ for (Iterator<IProject> iterator = _notObfuscatedProjects.iterator(); iterator.hasNext();)
+ {
+ IProject iProject = iterator.next();
+ ObfuscatorManager.unobfuscate(iProject);
+ }
+
+ for (Iterator<IProject> iterator = _obfuscatedProjects.iterator(); iterator.hasNext();)
+ {
+ IProject iProject = iterator.next();
+ ObfuscatorManager.obfuscate(iProject, new NullProgressMonitor());
+ }
+
+ }
+}
diff --git a/src/plugins/android/src/com/motorola/studio/android/obfuscate/ui/ObfuscateDialog.java b/src/plugins/android/src/com/motorola/studio/android/obfuscate/ui/ObfuscateDialog.java
new file mode 100644
index 0000000..f770e1a
--- /dev/null
+++ b/src/plugins/android/src/com/motorola/studio/android/obfuscate/ui/ObfuscateDialog.java
@@ -0,0 +1,227 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.motorola.studio.android.obfuscate.ui;
+
+import java.util.ArrayList;
+
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.jface.dialogs.IMessageProvider;
+import org.eclipse.jface.dialogs.TitleAreaDialog;
+import org.eclipse.jface.viewers.CheckboxTreeViewer;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.ITreeContentProvider;
+import org.eclipse.jface.viewers.LabelProvider;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Monitor;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.ui.PlatformUI;
+
+import com.motorola.studio.android.AndroidPlugin;
+import com.motorola.studio.android.i18n.AndroidNLS;
+import com.motorola.studio.android.model.AndroidProject;
+import com.motorola.studio.android.obfuscate.ObfuscatorManager;
+
+public class ObfuscateDialog extends TitleAreaDialog
+{
+
+ private final String OBFUSCATION_DIALOG_HELP = AndroidPlugin.PLUGIN_ID + ".obfuscation-dialog";
+
+ private CheckboxTreeViewer treeViewer;
+
+ private Object[] selectedProjects;
+
+ public ObfuscateDialog(Shell parentShell)
+ {
+ super(parentShell);
+
+ setTitleImage(AndroidPlugin.imageDescriptorFromPlugin(AndroidPlugin.PLUGIN_ID,
+ "icons/wizban/obfuscate.gif").createImage()); //$NON-NLS-1$
+ }
+
+ @Override
+ protected void configureShell(Shell newShell)
+ {
+ super.configureShell(newShell);
+ newShell.setSize(330, 300);
+ newShell.setText(AndroidNLS.ObfuscateProjectsHandler_1);
+ }
+
+ /**
+ * Center the dialog.
+ */
+ @Override
+ protected void initializeBounds()
+ {
+ super.initializeBounds();
+ Shell shell = this.getShell();
+ Monitor primary = shell.getMonitor();
+ Rectangle bounds = primary.getBounds();
+ Rectangle rect = shell.getBounds();
+ int x = bounds.x + ((bounds.width - rect.width) / 2);
+ int y = bounds.y + ((bounds.height - rect.height) / 2);
+ shell.setLocation(x, y);
+ }
+
+ @Override
+ protected Control createDialogArea(Composite parent)
+ {
+ super.createDialogArea(parent).setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+ Composite mainComposite = new Composite(parent, SWT.NONE);
+ mainComposite.setLayoutData(new GridData(GridData.FILL_BOTH));
+ GridLayout layout = new GridLayout(1, false);
+ mainComposite.setLayout(layout);
+
+ treeViewer = new CheckboxTreeViewer(mainComposite, SWT.CHECK | SWT.BORDER | SWT.V_SCROLL);
+ treeViewer.getControl().setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+
+ // Set content and label provider
+ treeViewer.setLabelProvider(new LabelProvider()
+ {
+ @Override
+ public String getText(Object element)
+ {
+ String label = null;
+ if (element instanceof IProject)
+ {
+ label = ((IProject) element).getName();
+ }
+ return label;
+ }
+ });
+ treeViewer.setContentProvider(new TreeViewerContentProvider());
+
+ ArrayList<IProject> projectsList = generateInputForTree();
+ treeViewer.setInput(projectsList);
+
+ for (IProject p : projectsList)
+ {
+ treeViewer.setChecked(p, ObfuscatorManager.isProguardSet(p));
+ }
+
+ treeViewer.addSelectionChangedListener(new ISelectionChangedListener()
+ {
+ public void selectionChanged(SelectionChangedEvent event)
+ {
+ selectedProjects = treeViewer.getCheckedElements();
+ }
+ });
+ treeViewer.expandAll();
+ mainComposite.layout(true);
+
+ setTitle(AndroidNLS.ObfuscateProjectsHandler_2);
+ setMessage(AndroidNLS.ObfuscateProjectsHandler_3, IMessageProvider.NONE);
+
+ PlatformUI.getWorkbench().getHelpSystem().setHelp(mainComposite, OBFUSCATION_DIALOG_HELP);
+
+ return mainComposite;
+ }
+
+ private ArrayList<IProject> generateInputForTree()
+ {
+ ArrayList<IProject> androidProjects = new ArrayList<IProject>();
+ IProject[] allProjectsList = ResourcesPlugin.getWorkspace().getRoot().getProjects();
+
+ for (int i = 0; i < allProjectsList.length; i++)
+ {
+ IProject currentProject = allProjectsList[i];
+ try
+ {
+ if ((currentProject.getNature(AndroidProject.ANDROID_NATURE) != null)
+ && currentProject.isOpen())
+ {
+ androidProjects.add(currentProject);
+ }
+ }
+ catch (CoreException e)
+ {
+ // do nothing
+ }
+ }
+ return androidProjects;
+ }
+
+ public ArrayList<IProject> getSelectedProjects()
+ {
+ ArrayList<IProject> list = new ArrayList<IProject>();
+ if (selectedProjects != null)
+ {
+ for (int i = 0; i < selectedProjects.length; i++)
+ {
+ list.add((IProject) selectedProjects[i]);
+ }
+ }
+ return list;
+ }
+}
+
+class TreeViewerContentProvider implements ITreeContentProvider
+{
+ public void dispose()
+ {
+ // do nothing
+ }
+
+ public void inputChanged(Viewer viewer, Object oldInput, Object newInput)
+ {
+ // do nothing
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.jface.viewers.ITreeContentProvider#getElements(java.lang.Object)
+ */
+ public Object[] getElements(Object inputElement)
+ {
+ Object[] elem = null;
+ if (inputElement instanceof ArrayList)
+ {
+ elem = new Object[((ArrayList) inputElement).size()];
+ elem = ((ArrayList) inputElement).toArray();
+ }
+
+ return elem;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.jface.viewers.ITreeContentProvider#getChildren(java.lang.Object)
+ */
+ public Object[] getChildren(Object parentElement)
+ {
+ //no children
+ return new Object[0];
+ }
+
+ public Object getParent(Object element)
+ {
+ return null;
+ }
+
+ public boolean hasChildren(Object element)
+ {
+ return false;
+ }
+}
diff --git a/src/plugins/android/src/com/motorola/studio/android/perspective/MotodevStudioAndroidPerspective.java b/src/plugins/android/src/com/motorola/studio/android/perspective/MotodevStudioAndroidPerspective.java
new file mode 100644
index 0000000..843aec9
--- /dev/null
+++ b/src/plugins/android/src/com/motorola/studio/android/perspective/MotodevStudioAndroidPerspective.java
@@ -0,0 +1,356 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.motorola.studio.android.perspective;
+
+import java.net.URL;
+
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.jdt.internal.ui.JavaPerspectiveFactory;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.ui.IFolderLayout;
+import org.eclipse.ui.IPageLayout;
+import org.eclipse.ui.IPageListener;
+import org.eclipse.ui.IPerspectiveDescriptor;
+import org.eclipse.ui.IPerspectiveFactory;
+import org.eclipse.ui.IPerspectiveListener;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.IWorkbenchPartReference;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.PerspectiveAdapter;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.internal.intro.IIntroConstants;
+import org.osgi.framework.BundleContext;
+
+import com.motorola.studio.android.AndroidPlugin;
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.common.utilities.EclipseUtils;
+
+@SuppressWarnings("restriction")
+public class MotodevStudioAndroidPerspective extends JavaPerspectiveFactory
+{
+ private static String VIEW_PACKAGE_EXPLORER = "org.eclipse.jdt.ui.PackageExplorer";
+
+ private static String VIEW_PROBLEM = "org.eclipse.ui.views.ProblemView";
+
+ private static String VIEW_CONSOLE = "org.eclipse.ui.console.ConsoleView";
+
+ private static String VIEW_OUTLINE = "org.eclipse.ui.views.ContentOutline";
+
+ private static String VIEW_ANDROID_EMULATOR =
+ "com.motorola.studio.android.emulator.androidView";
+
+ private static String VIEW_SNIPPETS =
+ "org.eclipse.wst.common.snippets.internal.ui.SnippetsView";
+
+ private static String VIEW_RSS = "biz.junginger.newsfeed.eclipse.FeedView";
+
+ // DDMS Views
+
+ private static String DDMSVIEW_EMULATOR_CONTROL =
+ "com.android.ide.eclipse.ddms.views.EmulatorControlView";
+
+ private static String DDMSVIEW_LOGCAT = "com.android.ide.eclipse.ddms.views.LogCatView";
+
+ private static String DDMSVIEW_FILE_EXPLORER =
+ "com.android.ide.eclipse.ddms.views.FileExplorerView";
+
+ private static String PERSPECTIVE_ANDROID = "com.motorola.studio.android.perspective";
+
+ private static String PERSPECTIVE_OPHONE = "com.motorola.studio.android.ophone.perspective";
+
+ private static String PERSPECTIVE_DDMS = "com.android.ide.eclipse.ddms.Perspective";
+
+ private static String PERSPECTIVE_EMULATOR = "com.motorola.studio.android.emulator.perspective";
+
+ private static String PERSPECTIVE_DEBUG = "org.eclipse.debug.ui.DebugPerspective";
+
+ private static String LAUNCH_COOLBAR_SHORTCUT = "org.eclipse.debug.ui.launchActionSet";
+
+ private static String VIEW_TML_DEV_MGT =
+ "org.eclipse.sequoyah.device.framework.ui.InstanceMgtView";
+
+ private static String WIZARD_PROJECT = "com.motorola.studio.android.wizards.newProjectWizard";
+
+ private static String WIZARD_WIDGET_PROJECT =
+ "com.motorola.studio.android.wizards.newWidgetProjectWizard";
+
+ private static String WIZARD_ACTIVITY = "com.motorola.studio.android.wizards.newActivityWizard";
+
+ private static String WIZARD_ACTIVITY_BASED_ON_TEMPLATE =
+ "com.motorola.studio.android.wizards.newActivityBasedOnTemplateWizard";
+
+ private static String WIZARD_RECEIVER = "com.motorola.studio.android.wizards.newReceiverWizard";
+
+ private static String WIZARD_SERVICE = "com.motorola.studio.android.wizards.newServiceWizard";
+
+ private static String WIZARD_PROVIDER = "com.motorola.studio.android.wizards.newProviderWizard";
+
+ private static String WIZARD_ANDROID_XML =
+ "com.android.ide.eclipse.editors.wizards.NewXmlFileWizard";
+
+ private static String WIZARD_WIDGET_PROVIDER =
+ "com.motorola.studio.android.wizard.newWidgetProviderWizard";
+
+ private static String WIZARD_JAVA_PACKAGE =
+ "org.eclipse.jdt.ui.wizards.NewPackageCreationWizard";
+
+ private static String WIZARD_JAVA_CLASS = "org.eclipse.jdt.ui.wizards.NewClassCreationWizard";
+
+ private static String WIZARD_JAVA_INTERFACE =
+ "org.eclipse.jdt.ui.wizards.NewInterfaceCreationWizard";
+
+ private static String WIZARD_NEW_FOLDER = "org.eclipse.ui.wizards.new.folder";
+
+ private static String VIEW_APPLICATION_SIGNING_TOOL =
+ "com.motorola.studio.android.packaging.ui.signingview";
+
+ private static final String STUDIO_INFO_INITIAL_PAGE_PROPERTY = "studio.android.initial.page";
+
+ private static final String STUDIO_INFO_INITIAL_PAGE_FILE = "MOTODEV/index.html";
+
+ private static IPerspectiveListener perspectiveListener = null;
+
+ /**
+ * Creates the initial layout for a page.
+ *
+ * @param layout
+ * the page layout
+ *
+ * @see IPerspectiveFactory#createInitialLayout(IPageLayout)
+ */
+ @Override
+ public void createInitialLayout(final IPageLayout layout)
+ {
+ String editorArea = layout.getEditorArea();
+ layout.setEditorAreaVisible(true);
+
+ IFolderLayout left = layout.createFolder("left", IPageLayout.LEFT, 0.2f, editorArea);
+ left.addView(VIEW_PACKAGE_EXPLORER);
+ left.addView(DDMSVIEW_FILE_EXPLORER);
+
+ IFolderLayout leftBottom =
+ layout.createFolder("leftBottom", IPageLayout.BOTTOM, 0.59f, "left");
+ leftBottom.addView(VIEW_SNIPPETS);
+ leftBottom.addView(VIEW_OUTLINE);
+
+ IFolderLayout right = layout.createFolder("right", IPageLayout.RIGHT, 0.6f, editorArea);
+ right.addView(VIEW_ANDROID_EMULATOR);
+ right.addView(VIEW_RSS);
+
+ IFolderLayout bottomMiddle =
+ layout.createFolder("bottomMiddle", IPageLayout.BOTTOM, 0.59f, editorArea);
+ bottomMiddle.addView(VIEW_TML_DEV_MGT);
+ bottomMiddle.addView(DDMSVIEW_EMULATOR_CONTROL);
+ bottomMiddle.addView(DDMSVIEW_LOGCAT);
+ bottomMiddle.addView(VIEW_CONSOLE);
+ bottomMiddle.addView(VIEW_PROBLEM);
+ bottomMiddle.addView(VIEW_APPLICATION_SIGNING_TOOL);
+
+ layout.addShowViewShortcut(VIEW_TML_DEV_MGT);
+ layout.addShowViewShortcut(VIEW_ANDROID_EMULATOR);
+ layout.addShowViewShortcut(VIEW_SNIPPETS);
+ layout.addShowViewShortcut(VIEW_APPLICATION_SIGNING_TOOL);
+ // layout.addShowViewShortcut(VIEW_VIDEOS);
+
+ layout.addPerspectiveShortcut(PERSPECTIVE_ANDROID);
+ layout.addPerspectiveShortcut(PERSPECTIVE_OPHONE);
+ layout.addPerspectiveShortcut(PERSPECTIVE_DDMS);
+ layout.addPerspectiveShortcut(PERSPECTIVE_DEBUG);
+ layout.addPerspectiveShortcut(PERSPECTIVE_EMULATOR);
+
+ layout.addActionSet(LAUNCH_COOLBAR_SHORTCUT);
+
+ layout.addNewWizardShortcut(WIZARD_PROJECT);
+ layout.addNewWizardShortcut(WIZARD_WIDGET_PROJECT);
+ layout.addNewWizardShortcut(WIZARD_ACTIVITY);
+ layout.addNewWizardShortcut(WIZARD_ACTIVITY_BASED_ON_TEMPLATE);
+ layout.addNewWizardShortcut(WIZARD_RECEIVER);
+ layout.addNewWizardShortcut(WIZARD_SERVICE);
+ layout.addNewWizardShortcut(WIZARD_PROVIDER);
+ layout.addNewWizardShortcut(WIZARD_WIDGET_PROVIDER);
+ layout.addNewWizardShortcut(WIZARD_JAVA_PACKAGE);
+ layout.addNewWizardShortcut(WIZARD_JAVA_CLASS);
+ layout.addNewWizardShortcut(WIZARD_JAVA_INTERFACE);
+ layout.addNewWizardShortcut(WIZARD_ANDROID_XML);
+ layout.addNewWizardShortcut(WIZARD_NEW_FOLDER);
+
+ final IWorkbenchWindow activeWindow = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
+ IWorkbenchPage activePage = null;
+ if (activeWindow != null)
+ {
+ activePage = activeWindow.getActivePage();
+ addPerspectiveListener(activeWindow);
+ firePerspectiveInitActions(activePage, layout);
+ }
+
+ IPageListener pageListener = new IPageListener()
+ {
+ public void pageActivated(IWorkbenchPage page)
+ {
+ firePerspectiveInitActions(page, layout);
+ Display disp = PlatformUI.getWorkbench().getDisplay();
+ final IPageListener thisListener = this;
+ disp.asyncExec(new Runnable()
+ {
+ public void run()
+ {
+ if (activeWindow != null)
+ {
+ activeWindow.removePageListener(thisListener);
+ }
+ }
+ });
+ }
+
+ public void pageClosed(IWorkbenchPage page)
+ {
+ //do nothing
+ }
+
+ public void pageOpened(IWorkbenchPage page)
+ {
+ //do nothing
+ }
+
+ };
+ if (activeWindow != null)
+ {
+ activeWindow.addPageListener(pageListener);
+ }
+
+ StudioLogger.debug(MotodevStudioAndroidPerspective.class,
+ "MOTODEV Studio Perspective created.");
+ }
+
+ /**
+ * Creates and adds a perspective listener that will remove the
+ * "maximized state" of the Welcome (intro) view before hiding it.
+ *
+ * @param activeWindow
+ * Workbench Window where the listener will be added.
+ */
+ private synchronized static void addPerspectiveListener(IWorkbenchWindow activeWindow)
+ {
+ if (perspectiveListener == null)
+ {
+ perspectiveListener = new PerspectiveAdapter()
+ {
+
+ @Override
+ public void perspectiveChanged(IWorkbenchPage page,
+ IPerspectiveDescriptor perspective, IWorkbenchPartReference partRef,
+ String changeId)
+ {
+ if (PERSPECTIVE_ANDROID.equals(perspective.getId()))
+ {
+ if (IIntroConstants.INTRO_VIEW_ID.equals(partRef.getId()))
+ {
+ if (IWorkbenchPage.CHANGE_VIEW_HIDE.equals(changeId))
+ {
+ if (IWorkbenchPage.STATE_MAXIMIZED == page.getPartState(partRef))
+ {
+ page.toggleZoom(partRef);
+ }
+ }
+ }
+ }
+ }
+
+ };
+ activeWindow.addPerspectiveListener(perspectiveListener);
+ }
+
+ }
+
+ /**
+ *
+ * @param activeWindow
+ * @param layout
+ */
+ private void firePerspectiveInitActions(final IWorkbenchPage activePage,
+ final IPageLayout layout)
+ {
+ if (activePage != null)
+ {
+ // Open MOTODEV Web Resources on a Web Browser Editor
+ openStudioInfoOnWebBrowserEditor(activePage);
+ }
+
+ }
+
+ /**
+ * Opens a web browser with useful information from Studio for Android
+ */
+ public static void openStudioInfoOnWebBrowserEditor(IWorkbenchPage page)
+ {
+
+ URL initialPageURL = getWebResourcesURL();
+
+ if ((initialPageURL != null) && (!Platform.getOS().equals(Platform.OS_LINUX)))
+ {
+ EclipseUtils.openedWebEditor(page, initialPageURL);
+ }
+ }
+
+ /**
+ * @return
+ */
+ public static URL getWebResourcesURL()
+ {
+ URL initialPageURL = null;
+
+ try
+ {
+ BundleContext context = AndroidPlugin.getDefault().getBundle().getBundleContext();
+ String initialPage = context.getProperty(STUDIO_INFO_INITIAL_PAGE_PROPERTY);
+ StudioLogger.debug(MotodevStudioAndroidPerspective.class, "Read initial page property:"
+ + STUDIO_INFO_INITIAL_PAGE_PROPERTY + " = " + initialPage);
+ if (initialPage != null)
+ {
+ StudioLogger.debug(MotodevStudioAndroidPerspective.class,
+ "Using the customized URL to be opened in the Web Browser Editor:"
+ + initialPage);
+ initialPageURL = new URL(initialPage);
+ }
+ }
+ catch (Exception e)
+ {
+ StudioLogger.error(
+ MotodevStudioAndroidPerspective.class,
+ "Unable to read customized URL to be opened in the Web Browser Editor..."
+ + e.getMessage());
+ }
+
+ if (initialPageURL == null)
+ {
+ try
+ {
+ StudioLogger.debug(MotodevStudioAndroidPerspective.class,
+ "Use the default URL to be opened in the Web Browser Editor.");
+ URL installDir = Platform.getInstallLocation().getURL();
+ initialPageURL = new URL(installDir, STUDIO_INFO_INITIAL_PAGE_FILE);
+ }
+ catch (Exception e)
+ {
+ StudioLogger.error(MotodevStudioAndroidPerspective.class,
+ "Unable to show Web Browser Editor with URL: " + e.getMessage());
+ }
+ }
+ return initialPageURL;
+ }
+
+}
diff --git a/src/plugins/android/src/com/motorola/studio/android/perspective/OpenWebResources.java b/src/plugins/android/src/com/motorola/studio/android/perspective/OpenWebResources.java
new file mode 100644
index 0000000..d575ed9
--- /dev/null
+++ b/src/plugins/android/src/com/motorola/studio/android/perspective/OpenWebResources.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.perspective;
+
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.core.runtime.Platform;
+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 com.motorola.studio.android.common.log.StudioLogger;
+
+/**
+ * Command to open the MOTODEV Web Resources in a Web Browser Editor
+ */
+public class OpenWebResources extends AbstractHandler
+{
+ /* (non-Javadoc)
+ * @see org.eclipse.core.commands.IHandler#execute(org.eclipse.core.commands.ExecutionEvent)
+ */
+ public Object execute(ExecutionEvent event) throws ExecutionException
+ {
+ if (!Platform.getOS().equals(Platform.OS_LINUX))
+ {
+ IWorkbenchWindow activeWindow = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
+ if (activeWindow != null)
+ {
+ IWorkbenchPage activePage = activeWindow.getActivePage();
+ MotodevStudioAndroidPerspective.openStudioInfoOnWebBrowserEditor(activePage);
+ }
+ }
+ else
+ {
+
+ IWorkbenchBrowserSupport browserSupport = PlatformUI.getWorkbench().getBrowserSupport();
+
+ /*
+ * open the browser
+ */
+ IWebBrowser browser;
+ try
+ {
+ browser =
+ browserSupport.createBrowser(IWorkbenchBrowserSupport.LOCATION_BAR
+ | IWorkbenchBrowserSupport.NAVIGATION_BAR
+ | IWorkbenchBrowserSupport.AS_EXTERNAL, "MOTODEV", null, null);
+
+ browser.openURL(MotodevStudioAndroidPerspective.getWebResourcesURL());
+
+ }
+ catch (PartInitException e)
+ {
+ StudioLogger.error("Error opening the Web Resources page: " + e.getMessage());
+ }
+
+ }
+ return null;
+ }
+
+}
diff --git a/src/plugins/android/src/com/motorola/studio/android/preferences/ui/AndroidPreferencePage.java b/src/plugins/android/src/com/motorola/studio/android/preferences/ui/AndroidPreferencePage.java
new file mode 100644
index 0000000..3884df7
--- /dev/null
+++ b/src/plugins/android/src/com/motorola/studio/android/preferences/ui/AndroidPreferencePage.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.preferences.ui;
+
+import org.eclipse.jface.preference.PreferencePage;
+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.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Group;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.IWorkbenchPreferencePage;
+import org.eclipse.ui.PlatformUI;
+
+import com.motorola.studio.android.AndroidPlugin;
+import com.motorola.studio.android.common.preferences.DialogWithToggleUtils;
+import com.motorola.studio.android.common.utilities.EclipseUtils;
+import com.motorola.studio.android.i18n.AndroidNLS;
+
+/**
+ * DESCRIPTION:
+ * This class represents the preference page for Android Emulator.
+ * It only gives to the user the option to reset all dialog configuration
+ * clearing all 'Do not show me again' settings and showing all hidden dialogs
+ * again.
+ * <br>
+ * RESPONSIBILITY:
+ * Create the preference page for Android Emulator.
+ * <br>
+ * COLABORATORS:
+ * none
+ * <br>
+ * USAGE:
+ * The plugin.xml file links this class to the corresponding preference page
+ * extension point.
+ *
+ * @see PreferencePage
+ * @see IWorkbenchPreferencePage
+ */
+public class AndroidPreferencePage extends PreferencePage implements IWorkbenchPreferencePage
+{
+ private final String PREFERENCE_PAGE_HELP = AndroidPlugin.PLUGIN_ID
+ + ".preference-android-emulator"; //$NON-NLS-1$
+
+ private boolean cleanActionRequired = false;
+
+ /**
+ * @see PreferencePage#createContents(Composite)
+ */
+ @Override
+ protected Control createContents(Composite parent)
+ {
+ PlatformUI.getWorkbench().getHelpSystem().setHelp(parent, PREFERENCE_PAGE_HELP);
+
+ Composite entryTable = new Composite(parent, SWT.NULL);
+ GridData data = new GridData(SWT.FILL, SWT.TOP, true, false);
+ entryTable.setLayoutData(data);
+
+ GridLayout layout = new GridLayout();
+ entryTable.setLayout(layout);
+
+ layout = new GridLayout(2, false);
+
+ Group dontAskGroup = new Group(entryTable, SWT.NONE);
+ dontAskGroup.setLayout(layout);
+ dontAskGroup.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false));
+ dontAskGroup.setText(AndroidNLS.UI_Preferences_Dialogs_Group_Title);
+
+ Label label = new Label(dontAskGroup, SWT.WRAP);
+ label.setText(AndroidNLS.UI_Preferences_Dialogs_Group_Message);
+ data = new GridData(SWT.FILL, SWT.CENTER, true, false);
+ data.widthHint = 100;
+ label.setLayoutData(data);
+
+ Button clearButton = new Button(dontAskGroup, SWT.PUSH);
+ clearButton.setText(AndroidNLS.UI_Preferences_Dialogs_Group_Button);
+ data = new GridData(SWT.CENTER, SWT.CENTER, false, false);
+ data.widthHint = 80;
+ clearButton.setLayoutData(data);
+ clearButton.addSelectionListener(new SelectionAdapter()
+ {
+ @Override
+ public void widgetSelected(SelectionEvent e)
+ {
+ cleanActionRequired =
+ EclipseUtils.displayPrompt(PlatformUI.getWorkbench().getDisplay(),
+ AndroidNLS.UI_Preferences_Dialogs_Group_Title,
+ AndroidNLS.UI_Preferences_Dialogs_Clean_Message);
+ ;
+ }
+ });
+ clearButton.pack();
+
+ return entryTable;
+ }
+
+ /**
+ * @see IWorkbenchPreferencePage#init(IWorkbench)
+ */
+ public void init(IWorkbench workbench)
+ {
+ noDefaultAndApplyButton();
+ }
+
+ /**
+ * @see PreferencePage#performOk()
+ */
+ @Override
+ public boolean performOk()
+ {
+ if (cleanActionRequired)
+ {
+ DialogWithToggleUtils.resetAllDialogsConfiguration();
+ }
+
+ return super.performOk();
+ }
+}
diff --git a/src/plugins/android/src/com/motorola/studio/android/preferences/ui/EmulatorPreferencePage.java b/src/plugins/android/src/com/motorola/studio/android/preferences/ui/EmulatorPreferencePage.java
new file mode 100644
index 0000000..a13c9f2
--- /dev/null
+++ b/src/plugins/android/src/com/motorola/studio/android/preferences/ui/EmulatorPreferencePage.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.preferences.ui;
+
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.jface.preference.PreferencePage;
+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.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Group;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.IWorkbenchPreferencePage;
+import org.eclipse.ui.PlatformUI;
+
+import com.motorola.studio.android.AndroidPlugin;
+import com.motorola.studio.android.i18n.AndroidNLS;
+
+/**
+ * This class draws the preference page that allow user to chose between display the
+ * native emulator outside the view after it's closed or not.
+ */
+public class EmulatorPreferencePage extends PreferencePage implements IWorkbenchPreferencePage
+{
+
+ private final String PREFERENCE_PAGE_HELP = AndroidPlugin.PLUGIN_ID
+ + ".preference-emulator-view"; //$NON-NLS-1$
+
+ protected boolean shallUnembedEmulators;
+
+ private Button unembedCheckBox;
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.IWorkbenchPreferencePage#init(org.eclipse.ui.IWorkbench)
+ */
+ public void init(IWorkbench workbench)
+ {
+ super.getPreferenceStore().setDefault(AndroidPlugin.SHALL_UNEMBED_EMULATORS_PREF_KEY, true);
+ shallUnembedEmulators =
+ super.getPreferenceStore().getBoolean(
+ AndroidPlugin.SHALL_UNEMBED_EMULATORS_PREF_KEY);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.preference.PreferencePage#createContents(org.eclipse.swt.widgets.Composite)
+ */
+ @Override
+ protected Control createContents(Composite parent)
+ {
+ PlatformUI.getWorkbench().getHelpSystem().setHelp(parent, PREFERENCE_PAGE_HELP);
+
+ Composite mainComposite = new Composite(parent, SWT.NONE);
+
+ GridLayout layout = new GridLayout();
+ mainComposite.setLayout(layout);
+
+ Group emulatorViewGroup = new Group(mainComposite, SWT.NONE);
+ GridLayout emulatorViewGroupLayout = new GridLayout();
+ GridData emulatorViewGroupLayoutData =
+ new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1);
+ emulatorViewGroup.setLayoutData(emulatorViewGroupLayoutData);
+ emulatorViewGroup.setLayout(emulatorViewGroupLayout);
+ emulatorViewGroup.setText(AndroidNLS.EmulatorPreferencePage_EmulatorViewGroup);
+
+ unembedCheckBox = new Button(emulatorViewGroup, SWT.CHECK);
+ GridData unembedCheckBoxData = new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1);
+ unembedCheckBox.setLayoutData(unembedCheckBoxData);
+ unembedCheckBox.setText(AndroidNLS.EmulatorPreferencePage_UnembedCheckBox);
+ unembedCheckBox.setSelection(shallUnembedEmulators);
+ unembedCheckBox.addSelectionListener(new SelectionAdapter()
+ {
+ @Override
+ public void widgetSelected(SelectionEvent e)
+ {
+ Button source = (Button) e.getSource();
+ shallUnembedEmulators = source.getSelection();
+ super.widgetSelected(e);
+ }
+ });
+
+ Label noteLabel = new Label(emulatorViewGroup, SWT.WRAP);
+ noteLabel.setText(AndroidNLS.EmulatorPreferencePage_UnembedNote);
+ GridData noteLabelLayoutData = new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1);
+ noteLabelLayoutData.widthHint = 100;
+ noteLabel.setLayoutData(noteLabelLayoutData);
+
+ return mainComposite;
+ }
+
+ @Override
+ public boolean performOk()
+ {
+ getPreferenceStore().setValue(AndroidPlugin.SHALL_UNEMBED_EMULATORS_PREF_KEY,
+ shallUnembedEmulators);
+ return super.performOk();
+ }
+
+ @Override
+ protected void performDefaults()
+ {
+ shallUnembedEmulators = true;
+ unembedCheckBox.setSelection(shallUnembedEmulators);
+ super.performDefaults();
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.jface.preference.PreferencePage#doGetPreferenceStore()
+ */
+ @Override
+ protected IPreferenceStore doGetPreferenceStore()
+ {
+ return AndroidPlugin.getDefault().getPreferenceStore();
+ }
+
+}
diff --git a/src/plugins/android/src/com/motorola/studio/android/preferences/ui/MotodevStudioPreference.java b/src/plugins/android/src/com/motorola/studio/android/preferences/ui/MotodevStudioPreference.java
new file mode 100644
index 0000000..3ff3384
--- /dev/null
+++ b/src/plugins/android/src/com/motorola/studio/android/preferences/ui/MotodevStudioPreference.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.preferences.ui;
+
+import org.eclipse.jface.preference.PreferencePage;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.IWorkbenchPreferencePage;
+
+/**
+ * This class represents the root preference page (MOTODEV Studio), implemented
+ * according User Interface Standards.
+ */
+public class MotodevStudioPreference extends PreferencePage implements IWorkbenchPreferencePage
+{
+
+ /**
+ * Default constructor
+ */
+ public MotodevStudioPreference()
+ {
+ // Empty
+ }
+
+ /**
+ * Constructor
+ *
+ * @param title The preference page title
+ */
+ public MotodevStudioPreference(String title)
+ {
+ super(title);
+ }
+
+ /**
+ * Constructor
+ *
+ * @param title The preference page title
+ * @param image The prefence page image
+ */
+ public MotodevStudioPreference(String title, ImageDescriptor image)
+ {
+ super(title, image);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see
+ * org.eclipse.jface.preference.PreferencePage#createContents(Composite)
+ */
+ @Override
+ protected Control createContents(Composite parent)
+ {
+ noDefaultAndApplyButton();
+ return null;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.ui.IWorkbenchPreferencePage#init(IWorkbench)
+ */
+ public void init(IWorkbench workbench)
+ {
+ // Empty
+ }
+}
diff --git a/src/plugins/android/src/com/motorola/studio/android/propertypage/MotodevStudioPropertyPage.java b/src/plugins/android/src/com/motorola/studio/android/propertypage/MotodevStudioPropertyPage.java
new file mode 100644
index 0000000..c633d44
--- /dev/null
+++ b/src/plugins/android/src/com/motorola/studio/android/propertypage/MotodevStudioPropertyPage.java
@@ -0,0 +1,226 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.propertypage;
+
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.jdt.internal.core.JavaProject;
+import org.eclipse.jface.dialogs.IMessageProvider;
+import org.eclipse.jface.preference.PreferencePage;
+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.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Group;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.ui.IWorkbenchPropertyPage;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.dialogs.PropertyPage;
+
+import com.motorola.studio.android.AndroidPlugin;
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.common.utilities.EclipseUtils;
+import com.motorola.studio.android.i18n.AndroidNLS;
+import com.motorola.studio.android.obfuscate.ObfuscatorManager;
+
+/**
+ * Motodev Studio for Android properties (obfuscate)
+ */
+public class MotodevStudioPropertyPage extends PropertyPage implements IWorkbenchPropertyPage
+{
+ private final String PROPERTY_PAGE_HELP = AndroidPlugin.PLUGIN_ID + ".obuscation_property"; //$NON-NLS-1$
+
+ private Button obfuscateCkbox;
+
+ private IProject project = null;
+
+ private void addFirstSection(Composite parent)
+ {
+ Composite group = createDefaultComposite(parent);
+
+ Composite composite = new Composite(group, SWT.NULL);
+ GridLayout layout = new GridLayout();
+ layout.numColumns = 2;
+ composite.setLayout(layout);
+
+ GridData data = new GridData();
+ data.verticalAlignment = GridData.FILL;
+ data.horizontalAlignment = GridData.FILL;
+ composite.setLayoutData(data);
+
+ obfuscateCkbox = new Button(composite, SWT.CHECK);
+
+ setDefaultObfuscate();
+
+ Label obfuscateLabel = new Label(composite, SWT.NONE);
+ obfuscateLabel.setText(AndroidNLS.UI_ProjectPropertyPage_Obfuscate);
+
+ obfuscateCkbox.addSelectionListener(new SelectionAdapter()
+ {
+ /* (non-Javadoc)
+ * @see org.eclipse.swt.events.SelectionAdapter#widgetSelected(org.eclipse.swt.events.SelectionEvent)
+ */
+ @Override
+ public void widgetSelected(SelectionEvent e)
+ {
+ super.widgetSelected(e);
+
+ boolean showWarningMessage = false;
+ if (obfuscateCkbox.getSelection())
+ {
+ if ((project != null) && project.getLocation().toOSString().contains(" ")) //$NON-NLS-1$
+ {
+ showWarningMessage = true;
+ }
+ }
+ if (showWarningMessage)
+ {
+ setMessage(AndroidNLS.WRN_Obfuscation_ProjectLocationContainWhitespaces,
+ IMessageProvider.WARNING);
+
+ }
+ else
+ {
+ setMessage("MOTODEV Studio"); //$NON-NLS-1$
+ }
+ }
+ });
+ }
+
+ /**
+ * Checks if project have Proguard settings and, if so, update checkbox state
+ */
+ private void setDefaultObfuscate()
+ {
+ project = null;
+ if (getElement() instanceof IResource)
+ {
+ IResource resource = (IResource) getElement();
+ if (resource != null)
+ {
+ project = resource.getProject();
+ }
+ }
+ else if (getElement() instanceof JavaProject)
+ {
+ JavaProject javaProject = (JavaProject) getElement();
+ project = javaProject.getProject();
+ }
+
+ if (project != null)
+ {
+ obfuscateCkbox.setSelection(ObfuscatorManager.isProguardSet(project));
+ }
+ else
+ {
+ //project not found
+ obfuscateCkbox.setSelection(false);
+ }
+ }
+
+ /**
+ * @see PreferencePage#createContents(Composite)
+ */
+ @Override
+ protected Control createContents(Composite parent)
+ {
+ Composite composite = new Composite(parent, SWT.NONE);
+ GridLayout layout = new GridLayout();
+ composite.setLayout(layout);
+ GridData data = new GridData(GridData.FILL);
+ data.grabExcessHorizontalSpace = true;
+ composite.setLayoutData(data);
+
+ addFirstSection(composite);
+
+ PlatformUI.getWorkbench().getHelpSystem().setHelp(composite, PROPERTY_PAGE_HELP);
+ PlatformUI.getWorkbench().getHelpSystem().setHelp(parent, PROPERTY_PAGE_HELP);
+
+ return composite;
+ }
+
+ private Composite createDefaultComposite(Composite parent)
+ {
+ GridData data = new GridData(SWT.FILL, SWT.NONE, true, false);
+ Group groupForObfuscation = new Group(parent, SWT.SHADOW_ETCHED_IN);
+ groupForObfuscation.setLayout(new GridLayout());
+ groupForObfuscation.setLayoutData(data);
+ groupForObfuscation.setFont(parent.getFont());
+ groupForObfuscation.setText(AndroidNLS.UI_ProjectPropertyPage_ObfuscateGroup);
+
+ return groupForObfuscation;
+ }
+
+ @Override
+ protected void performDefaults()
+ {
+ super.performDefaults();
+ setDefaultObfuscate();
+ }
+
+ /**
+ * Add or remove Proguard setting depending on obfuscateCkbox checkbox state
+ */
+ @Override
+ public boolean performOk()
+ {
+ IProject project = null;
+ Boolean needToObfuscate = obfuscateCkbox.getSelection();
+ if (getElement() instanceof IResource)
+ {
+ IResource resource = (IResource) getElement();
+ if (resource != null)
+ {
+ project = resource.getProject();
+ }
+ }
+ else if (getElement() instanceof JavaProject)
+ {
+ JavaProject javaProject = (JavaProject) getElement();
+ project = javaProject.getProject();
+ }
+ if (project != null)
+ {
+ IStatus status = null;
+ try
+ {
+ if (needToObfuscate.booleanValue())
+ {
+ status = ObfuscatorManager.obfuscate(project, null);
+ }
+ else
+ {
+ status = ObfuscatorManager.unobfuscate(project);
+ }
+ project.refreshLocal(IResource.DEPTH_INFINITE, null);
+ }
+ catch (Exception e)
+ {
+ EclipseUtils.showErrorDialog(
+ AndroidNLS.MotodevStudioPropertyPage_ChangeProguardSettingsProblem,
+ status.getMessage(), status);
+ StudioLogger.error(MotodevStudioPropertyPage.class, e.getMessage(), e);
+ return false;
+ }
+ }
+ return true;
+ }
+}
diff --git a/src/plugins/android/src/com/motorola/studio/android/sdkmanager/IAndroidSDKManagerConstants.java b/src/plugins/android/src/com/motorola/studio/android/sdkmanager/IAndroidSDKManagerConstants.java
new file mode 100644
index 0000000..5f9b288
--- /dev/null
+++ b/src/plugins/android/src/com/motorola/studio/android/sdkmanager/IAndroidSDKManagerConstants.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.sdkmanager;
+
+public interface IAndroidSDKManagerConstants
+{
+ String EMULATOR_OPTION_PREFERENCE_KEY = "com.android.ide.eclipse.adt.emuOptions";
+
+ String EMULATOR_OPTION_PREFERENCE_INITIAL_VALUE = "-no-window";
+
+}
diff --git a/src/plugins/android/src/com/motorola/studio/android/utilities/AndroidStatus.java b/src/plugins/android/src/com/motorola/studio/android/utilities/AndroidStatus.java
new file mode 100644
index 0000000..dd910f6
--- /dev/null
+++ b/src/plugins/android/src/com/motorola/studio/android/utilities/AndroidStatus.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.utilities;
+
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+
+import com.motorola.studio.android.AndroidPlugin;
+
+/**
+ * Status for Android Plugin.
+ */
+public class AndroidStatus extends Status
+{
+ /**
+ * Constructor for "OK" Status
+ */
+ public AndroidStatus()
+ {
+ super(IStatus.OK, AndroidPlugin.PLUGIN_ID, null);
+ }
+
+ /**
+ * Constructor for others status.
+ * @param severity
+ * @param msg
+ */
+ public AndroidStatus(int severity, String msg)
+ {
+ super(severity, AndroidPlugin.PLUGIN_ID, msg);
+ }
+
+}
diff --git a/src/plugins/android/src/com/motorola/studio/android/utilities/TelnetFrameworkAndroid.java b/src/plugins/android/src/com/motorola/studio/android/utilities/TelnetFrameworkAndroid.java
new file mode 100644
index 0000000..f92a1a3
--- /dev/null
+++ b/src/plugins/android/src/com/motorola/studio/android/utilities/TelnetFrameworkAndroid.java
@@ -0,0 +1,225 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.utilities;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+
+import org.apache.commons.net.telnet.TelnetClient;
+
+/**
+ * DESCRIPTION: This class defines the framework for telnet services to
+ * comunicate with phoe or emulator devices. <BR>
+ * RESPONSIBILITY: Provide telnet connection <BR>
+ * COLABORATORS: none <BR>
+ * USAGE: This class should be instantiated whenever a telnet connection to a
+ * phone or emulator device is needed. <BR>
+ */
+public class TelnetFrameworkAndroid
+{
+
+ //private InputStreamReader responseReader;
+
+ //private PrintWriter commandWriter;
+
+ private TelnetClient telnetClient;
+
+ private long timeout = 5000L;
+
+ /**
+ * Connect to a device using telnet.
+ *
+ * @param telnetHost
+ * the telnet host IP address
+ * @param telnetPort
+ * the telnet port
+ *
+ * @throws MotodevException
+ * when the connection cannot be established
+ */
+ public synchronized void connect(String telnetHost, int telnetPort) throws IOException
+ {
+ if ((telnetClient == null) || ((telnetClient != null) && (!telnetClient.isConnected())))
+ {
+ telnetClient = new TelnetClient(telnetHost);
+ telnetClient.connect(telnetHost, telnetPort);
+ }
+ }
+
+ /**
+ * Disconnect a telnet connection to a device
+ *
+ * @throws MotodevException
+ * when the disconnection cannot be executed
+ */
+ public synchronized void disconnect() throws IOException
+ {
+ if ((telnetClient != null) && (telnetClient.isConnected()))
+ {
+ telnetClient.disconnect();
+ }
+ }
+
+ /**
+ * @return
+ *
+ */
+ public synchronized String write(String telnetInputText, String[] waitFor) throws IOException
+ {
+ PrintWriter commandWriter = null;
+ try
+ {
+ commandWriter = new PrintWriter(telnetClient.getOutputStream());
+ commandWriter.println(telnetInputText);
+ commandWriter.flush();
+ if (waitFor != null)
+ {
+ return waitFor(waitFor);
+ }
+ }
+ finally
+ {
+ if (commandWriter != null)
+ {
+ commandWriter.close();
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Tests if the telnet client instance is connected
+ *
+ * @return true if it is connected; false otherwise
+ */
+ public boolean isConnected()
+ {
+ boolean connected = false;
+ if (telnetClient != null)
+ {
+ connected = telnetClient.isConnected();
+ }
+ return connected;
+ }
+
+ /**
+ *
+ */
+ public String waitFor(String[] waitForArray) throws IOException
+ {
+ InputStreamReader responseReader = null;
+ StringBuffer answerFromRemoteHost = new StringBuffer();
+
+ try
+ {
+ responseReader = new InputStreamReader(telnetClient.getInputStream());
+
+ boolean found = false;
+
+ do
+ {
+ char readChar = 0;
+ long currentTime = System.currentTimeMillis();
+ long timeoutTime = currentTime + timeout;
+
+ while (readChar == 0)
+ {
+ if (responseReader == null)
+ {
+ // responseReader can only be set to null if method
+ // releaseTelnetInputStreamReader()
+ // has been called, which should happen if host becomes
+ // unavailable.
+ throw new IOException(
+ "Telnet host is unavailable; stopped waiting for answer.");
+ }
+
+ if (responseReader.ready())
+ {
+ readChar = (char) responseReader.read();
+ }
+ else
+ {
+ try
+ {
+ Thread.sleep(50);
+ }
+ catch (InterruptedException e)
+ {
+ // Do nothing
+ }
+ }
+
+ currentTime = System.currentTimeMillis();
+
+ if ((!responseReader.ready()) && (currentTime > timeoutTime))
+ {
+ throw new IOException(
+ "A timeout has occured when trying to read the telnet stream");
+ }
+ }
+
+ answerFromRemoteHost.append(readChar);
+
+ for (String aWaitFor : waitForArray)
+ {
+ found = answerFromRemoteHost.toString().contains(aWaitFor);
+ }
+
+ }
+ while (!found);
+ }
+ finally
+ {
+ if (responseReader != null)
+ {
+ responseReader.close();
+ }
+ }
+
+ return answerFromRemoteHost.toString();
+ }
+
+ /**
+ * Retrieves the input stream associated to this telnet connection
+ */
+ public InputStream getInputStream()
+ {
+ InputStream s = null;
+ if (telnetClient != null)
+ {
+ s = telnetClient.getInputStream();
+ }
+ return s;
+ }
+
+ /**
+ * Retrieves the output stream associated to this telnet connection
+ */
+ public OutputStream getOutputStream()
+ {
+ OutputStream s = null;
+ if (telnetClient != null)
+ {
+ s = telnetClient.getOutputStream();
+ }
+ return s;
+ }
+}
diff --git a/src/plugins/android/src/com/motorola/studio/android/wizards/elements/ApplicationGroup.java b/src/plugins/android/src/com/motorola/studio/android/wizards/elements/ApplicationGroup.java
new file mode 100644
index 0000000..ca74a7c
--- /dev/null
+++ b/src/plugins/android/src/com/motorola/studio/android/wizards/elements/ApplicationGroup.java
@@ -0,0 +1,306 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.wizards.elements;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.DisposeEvent;
+import org.eclipse.swt.events.DisposeListener;
+import org.eclipse.swt.events.ModifyEvent;
+import org.eclipse.swt.events.ModifyListener;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Text;
+
+import com.motorola.studio.android.AndroidPlugin;
+import com.motorola.studio.android.i18n.AndroidNLS;
+import com.motorola.studio.android.model.AndroidProject;
+import com.motorola.studio.android.model.AndroidProject.SourceTypes;
+import com.motorola.studio.android.model.IWizardModel;
+
+/**
+ * Element for NewAndroidProject Wizard Page
+ */
+public class ApplicationGroup extends Composite
+{
+
+ private final AndroidProject project;
+
+ private Text packageName = null;
+
+ private Text activityName = null;
+
+ private Text applicationName = null;
+
+ private Text minSdkVersionText;
+
+ private String userChosenPackageName = null;
+
+ private String userChosenActivityName = null;
+
+ private String userChosenMinSdkVersion = null;
+
+ private String userChosenApplicationName = null;
+
+ private final String DEFAULT_ACTIVITY_NAME = "MainActivity";
+
+ private final String DEFAULT_APP_NAME = "User Application";
+
+ /**
+ * Constructor
+ * @param parent
+ * @param project
+ */
+ public ApplicationGroup(Composite parent, AndroidProject project)
+ {
+ super(parent, SWT.NONE);
+ this.project = project;
+ createContents(this);
+ }
+
+ /**
+ * Create Controls
+ * @param mainComposite
+ */
+ protected void createContents(Composite mainComposite)
+ {
+ setLayout(new GridLayout());
+ setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+
+ // package group
+ Composite packageComposite = new Composite(mainComposite, SWT.NONE);
+ packageComposite.setLayout(new GridLayout(2, false));
+ GridData data = new GridData(SWT.FILL, SWT.NONE, true, false);
+ packageComposite.setLayoutData(data);
+
+ // application name
+ final Label applicationNameLabel = new Label(packageComposite, SWT.NONE);
+ applicationNameLabel.setText(AndroidNLS.UI_ApplicationGroup_ApplicationNameLabel);
+ applicationNameLabel.setFont(mainComposite.getParent().getFont());
+
+ applicationName = new Text(packageComposite, SWT.BORDER);
+ data = new GridData(SWT.FILL, SWT.NONE, true, false);
+ applicationName.setLayoutData(data);
+ applicationName.setFont(mainComposite.getParent().getFont());
+ applicationName.setText(DEFAULT_APP_NAME);
+ project.setApplicationName(DEFAULT_APP_NAME);
+
+ // new package label
+ final Label packageLabel = new Label(packageComposite, SWT.NONE);
+ packageLabel.setText(AndroidNLS.UI_ApplicationGroup_PackageNameLabel);
+ packageLabel.setFont(mainComposite.getParent().getFont());
+
+ // new package name entry field
+ packageName = new Text(packageComposite, SWT.BORDER);
+ data = new GridData(SWT.FILL, SWT.NONE, true, false);
+ packageName.setLayoutData(data);
+ packageName.setFont(mainComposite.getParent().getFont());
+ packageName.setText(project.getPackageName());
+
+ final Label activityLabel = new Label(packageComposite, SWT.NONE);
+ activityLabel.setText(AndroidNLS.UI_ApplicationGroup_ActivityNameLabel);
+ activityLabel.setFont(mainComposite.getParent().getFont());
+
+ activityName = new Text(packageComposite, SWT.BORDER);
+ data = new GridData(SWT.FILL, SWT.NONE, true, false);
+ activityName.setLayoutData(data);
+ activityName.setFont(mainComposite.getParent().getFont());
+ activityName.setText(DEFAULT_ACTIVITY_NAME);
+ project.setActivityName(DEFAULT_ACTIVITY_NAME);
+
+ Label minSDKVersionLabel = new Label(packageComposite, SWT.NONE);
+ minSDKVersionLabel.setText(AndroidNLS.UI_ApplicationGroup_MinSDKVersionLabel);
+ minSdkVersionText = new Text(packageComposite, SWT.BORDER);
+ data = new GridData(SWT.FILL, SWT.NONE, true, false);
+ minSdkVersionText.setLayoutData(data);
+ minSdkVersionText.setFont(mainComposite.getParent().getFont());
+ String minSdkVersion = project.getMinSdkVersion();
+ if (minSdkVersion != null)
+ {
+ minSdkVersionText.setText(minSdkVersion);
+ }
+ else
+ {
+ minSdkVersionText.setEnabled(false);
+ final Runnable listener = new Runnable()
+ {
+
+ public void run()
+ {
+ AndroidPlugin.getDefault().removeSDKLoaderListener(this);
+ minSdkVersionText.getDisplay().asyncExec(new Runnable()
+ {
+ public void run()
+ {
+ minSdkVersionText.setEnabled(true);
+ minSdkVersionText.setText(project.getMinSdkVersion());
+ }
+ });
+ }
+ };
+ AndroidPlugin.getDefault().addSDKLoaderListener(listener);
+ minSdkVersionText.addDisposeListener(new DisposeListener()
+ {
+ public void widgetDisposed(DisposeEvent e)
+ {
+ AndroidPlugin.getDefault().removeSDKLoaderListener(listener);
+ }
+ });
+ }
+
+ minSdkVersionText.addModifyListener(new ModifyListener()
+ {
+ public void modifyText(ModifyEvent event)
+ {
+ project.setMinSdkVersion(minSdkVersionText.getText().trim());
+ if (minSdkVersionText.isFocusControl())
+ {
+ userChosenMinSdkVersion = minSdkVersionText.getText();
+ }
+ notifyListeners(IWizardModel.MODIFIED, new Event());
+ }
+ });
+
+ applicationName.addModifyListener(new ModifyListener()
+ {
+
+ public void modifyText(ModifyEvent e)
+ {
+ // application name only will be used if the project is a new project. If it is using existing source this field will be ignored
+ project.setApplicationName(applicationName.getText());
+ if (applicationName.isFocusControl())
+ {
+ userChosenApplicationName = applicationName.getText();
+ }
+ notifyListeners(IWizardModel.MODIFIED, new Event());
+ }
+
+ });
+
+ activityName.addModifyListener(new ModifyListener()
+ {
+
+ public void modifyText(ModifyEvent e)
+ {
+ // activity name only will be used if the project is a new project. If it is using existing source this field will be ignored
+ project.setActivityName(activityName.getText());
+ if (activityName.isFocusControl())
+ {
+ userChosenActivityName = activityName.getText();
+ }
+ notifyListeners(IWizardModel.MODIFIED, new Event());
+ }
+
+ });
+
+ packageName.addModifyListener(new ModifyListener()
+ {
+ public void modifyText(ModifyEvent event)
+ {
+ // only change package name if the project isn't using default package name (it is some source based project)
+ if (packageName.isFocusControl())
+ {
+ userChosenPackageName = packageName.getText();
+ project.setUsingDefaultPackage(false);
+ }
+ if (!project.isUsingDefaultPackage())
+ {
+ project.setPackageName(packageName.getText());
+ }
+ notifyListeners(IWizardModel.MODIFIED, new Event());
+ }
+ });
+
+ }
+
+ /**
+ * Set the enable state for all fields within this widget
+ * @param enabled
+ */
+ private void setElementsEnabled(boolean enabled)
+ {
+ packageName.setEnabled(enabled);
+ activityName.setEnabled(enabled);
+ applicationName.setEnabled(enabled);
+ }
+
+ /**
+ * Update Default Package name.
+ */
+ public void updateDefaultName()
+ {
+ if ((project.getSourceType() != SourceTypes.NEW)
+ && (project.getSourceType() != SourceTypes.WIDGET) && (packageName != null))
+ {
+ project.setUsingDefaultPackage(true);
+ project.setActivityName("");
+ packageName.setText(""); //$NON-NLS-1$
+ activityName.setText("");
+ applicationName.setText("");
+ setElementsEnabled(false);
+
+ }
+ else
+ {
+ project.setUsingDefaultPackage(userChosenPackageName == null);
+ packageName.setText(userChosenPackageName == null ? project.getPackageName()
+ : userChosenPackageName);
+ activityName.setText(userChosenActivityName == null ? DEFAULT_ACTIVITY_NAME
+ : userChosenActivityName);
+ applicationName.setText(userChosenApplicationName == null ? DEFAULT_APP_NAME
+ : userChosenApplicationName);
+ setElementsEnabled(true);
+ }
+
+ }
+
+ /**
+ * Updates the Min Sdk Version field
+ */
+ public void updateMinSdkVersion()
+ {
+ minSdkVersionText.setEnabled((project.getSourceType() == SourceTypes.NEW)
+ || (project.getSourceType() == SourceTypes.WIDGET));
+ if (project.getSdkTarget() != null)
+ {
+ if (userChosenMinSdkVersion == null)
+ {
+ minSdkVersionText.setText(project.getSdkTarget().getVersion().getApiString());
+ }
+ }
+ else
+ {
+ minSdkVersionText.setText("");
+ }
+ }
+
+ /**
+ * Retrieves the Activity SWT Text control
+ * @return activityName
+ */
+ public Text getActivityText()
+ {
+ return activityName;
+ }
+
+ public boolean isElementsEnabled()
+ {
+ return packageName.isEnabled() & activityName.isEnabled() & applicationName.isEnabled();
+ }
+
+}
diff --git a/src/plugins/android/src/com/motorola/studio/android/wizards/elements/LocationGroup.java b/src/plugins/android/src/com/motorola/studio/android/wizards/elements/LocationGroup.java
new file mode 100644
index 0000000..b0f8d4e
--- /dev/null
+++ b/src/plugins/android/src/com/motorola/studio/android/wizards/elements/LocationGroup.java
@@ -0,0 +1,239 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.wizards.elements;
+
+import java.io.File;
+
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.DirectoryDialog;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.Text;
+
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.i18n.AndroidNLS;
+import com.motorola.studio.android.model.AndroidProject;
+import com.motorola.studio.android.model.AndroidProject.SourceTypes;
+import com.motorola.studio.android.model.IWizardModel;
+import com.motorola.studio.android.wizards.project.NewAndroidProjectMainPage;
+
+/**
+ * Class that implements the Location group to be used on the New Project Wizard
+ */
+public class LocationGroup extends Composite
+{
+ final private AndroidProject project;
+
+ final private NewAndroidProjectMainPage page;
+
+ private String lastSourcePathValue = null;
+
+ /**
+ * Constructor
+ *
+ * @param parent
+ * @param project
+ * @param page
+ */
+ public LocationGroup(Composite parent, AndroidProject project, NewAndroidProjectMainPage page)
+ {
+ super(parent, SWT.SHADOW_ETCHED_IN);
+ this.project = project;
+ this.page = page;
+ setLayout(new GridLayout());
+ setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+ createControl(this);
+ }
+
+ /**
+ * Create Controls
+ *
+ * @param parent
+ */
+ private void createControl(Composite parent)
+ {
+ final Button createNewProjectRadio = new Button(this, SWT.RADIO);
+ createNewProjectRadio.setText(AndroidNLS.UI_LocationGroup_NewProjectRadioLabel);
+ createNewProjectRadio.setSelection(true);
+
+ final Button createSampleRadio = new Button(this, SWT.RADIO);
+ createSampleRadio.setText(AndroidNLS.UI_LocationGroup_NewFromSampleRadioLabel);
+ createSampleRadio.setSelection(false);
+
+ final Button existing_project_radio = new Button(this, SWT.RADIO);
+ existing_project_radio
+ .setText(AndroidNLS.UI_LocationGroup_NewFromExistentProjectRadioLabel);
+ existing_project_radio.setSelection(false);
+
+ final Button useDefaultLocation = new Button(this, SWT.CHECK);
+ useDefaultLocation.setText(AndroidNLS.UI_LocationGroup_UseDefaultLocationCheckLabel);
+ useDefaultLocation.setSelection(true);
+
+ Composite location_group = new Composite(this, SWT.NONE);
+ location_group.setLayout(new GridLayout(4, false));
+ location_group.setLayoutData(new GridData(GridData.FILL_BOTH));
+ location_group.setFont(parent.getFont());
+
+ Label locationLabel = new Label(location_group, SWT.NONE);
+ locationLabel.setText(AndroidNLS.UI_LocationGroup_LocationLabel);
+
+ final Text projectPath = new Text(location_group, SWT.BORDER);
+ GridData data = new GridData(GridData.FILL, GridData.BEGINNING, true, false, 2, 1);
+ projectPath.setLayoutData(data);
+ projectPath.setFont(parent.getFont());
+ projectPath.setText(ResourcesPlugin.getWorkspace().getRoot().getLocation().toOSString());
+ projectPath.setEnabled(false);
+ projectPath.addListener(SWT.Modify, new Listener()
+ {
+ public void handleEvent(Event event)
+ {
+ if (projectPath.isEnabled())
+ {
+ project.setLocation(projectPath.getText());
+ notifyListeners(IWizardModel.MODIFIED, new Event());
+ }
+ }
+ });
+
+ final Button browseButton = new Button(location_group, SWT.PUSH);
+ browseButton.setText(AndroidNLS.UI_General_BrowseButtonLabel);
+ browseButton.setEnabled(false);
+ page.setButtonLayoutData(browseButton);
+ browseButton.addSelectionListener(new SelectionAdapter()
+ {
+ @Override
+ public void widgetSelected(SelectionEvent e)
+ {
+
+ String existing_dir = project.getLocation();
+
+ // Disable the path if it doesn't exist
+ if (existing_dir != null
+ && (existing_dir.length() == 0 || !new File(existing_dir).exists()))
+ {
+ existing_dir = null;
+ }
+
+ DirectoryDialog dd = new DirectoryDialog(projectPath.getShell());
+ dd.setMessage(AndroidNLS.UI_LocationGroup_BrowseDialogMessage);
+ dd.setFilterPath(existing_dir);
+ String customLocation = dd.open();
+ if (customLocation != null)
+ {
+ projectPath.setText(customLocation);
+ if (existing_project_radio.getSelection() && customLocation.length() > 0)
+ {
+ setLastSourcePath(customLocation);
+ }
+ }
+ }
+ });
+
+ SelectionListener location_listener = new SelectionAdapter()
+ {
+ @Override
+ public void widgetSelected(SelectionEvent e)
+ {
+ boolean isNewProject = createNewProjectRadio.getSelection();
+ boolean existentProject = existing_project_radio.getSelection();
+ boolean fromSample = createSampleRadio.getSelection();
+
+ if (isNewProject ^ existentProject ^ fromSample)
+ {
+ // After this point We know that only one option is true
+ boolean useDefault = useDefaultLocation.getSelection();
+ useDefaultLocation.setEnabled(!existentProject);
+
+ String path;
+ if (existentProject)
+ {
+ path = getLastSourcePath();
+ useDefaultLocation.setSelection(false);
+ useDefault = false;
+ }
+ else if (useDefault)
+ {
+ path = project.getDefaultLocation();
+ }
+ else
+ {
+ path = project.getLocation();
+ }
+ boolean enablePath = !((isNewProject || fromSample) && useDefault);
+ projectPath.setEnabled(enablePath);
+ browseButton.setEnabled(enablePath);
+
+ project.setSourceType(isNewProject ? SourceTypes.NEW : existentProject
+ ? SourceTypes.EXISTING : SourceTypes.SAMPLE);
+
+ project.setUseDefaultLocation(useDefault);
+ if (enablePath && (path == null || path.length() == 0))
+ {
+ projectPath.setText(""); //$NON-NLS-1$
+ projectPath.setFocus();
+ }
+ else
+ {
+ projectPath.setText(path);
+ }
+
+ notifyListeners(IWizardModel.MODIFIED, new Event());
+ }
+ else
+ {
+ StudioLogger.error(LocationGroup.class,
+ "Ilegal State with New Project radio buttons."); //$NON-NLS-1$
+ }
+ }
+ };
+
+ createNewProjectRadio.addSelectionListener(location_listener);
+ existing_project_radio.addSelectionListener(location_listener);
+ useDefaultLocation.addSelectionListener(location_listener);
+ }
+
+ /**
+ * Get last path value defined by the user for the
+ * "Create from existing source" case
+ *
+ * @return The last path value defined by the user.
+ */
+ public String getLastSourcePath()
+ {
+ return this.lastSourcePathValue;
+ }
+
+ /**
+ * Set the last path value defined by the user for the
+ * "Create from existing source" case
+ *
+ * @param path
+ * - The last path defined by the user
+ */
+ public void setLastSourcePath(String path)
+ {
+ this.lastSourcePathValue = path;
+ }
+}
diff --git a/src/plugins/android/src/com/motorola/studio/android/wizards/elements/ProjectNameGroup.java b/src/plugins/android/src/com/motorola/studio/android/wizards/elements/ProjectNameGroup.java
new file mode 100644
index 0000000..94c2676
--- /dev/null
+++ b/src/plugins/android/src/com/motorola/studio/android/wizards/elements/ProjectNameGroup.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.wizards.elements;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.Text;
+
+import com.motorola.studio.android.i18n.AndroidNLS;
+import com.motorola.studio.android.model.AndroidProject;
+import com.motorola.studio.android.model.IWizardModel;
+
+/**
+ * Project Name SWT Element for Wizards
+ */
+public class ProjectNameGroup extends Composite
+{
+ private final AndroidProject project;
+
+ private Text projectNameField = null;
+
+ /**
+ * Constructor
+ * @param parent
+ * @param project
+ */
+ public ProjectNameGroup(Composite parent, AndroidProject project)
+ {
+ super(parent, SWT.NONE);
+ this.project = project;
+ createControl(parent);
+ }
+
+ /**
+ * Create Controls
+ * @param parent
+ */
+ private void createControl(Composite parent)
+ {
+ GridLayout layout = new GridLayout();
+ layout.numColumns = 2;
+ setLayout(layout);
+ setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+
+ // new project Label
+ Label label = new Label(this, SWT.NONE);
+ label.setText(AndroidNLS.UI_ProjectNameGroup_ProjectNameLabel);
+ label.setFont(parent.getFont());
+
+ // new project Name Field
+ final Text projectName = new Text(this, SWT.BORDER);
+ GridData data = new GridData(GridData.FILL_HORIZONTAL);
+ projectName.setLayoutData(data);
+ projectName.setFont(parent.getFont());
+ projectName.addListener(SWT.Modify, new Listener()
+ {
+ public void handleEvent(Event event)
+ {
+ project.setName(projectName.getText().trim());
+ notifyListeners(IWizardModel.MODIFIED, new Event());
+ }
+ });
+
+ projectNameField = projectName;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.swt.widgets.Control#forceFocus()
+ */
+ @Override
+ public boolean forceFocus()
+ {
+ boolean hasFocus = false;
+
+ if (projectNameField != null)
+ {
+ hasFocus = projectNameField.setFocus();
+ }
+
+ return hasFocus;
+ }
+}
diff --git a/src/plugins/android/src/com/motorola/studio/android/wizards/elements/SdkTargetSelector.java b/src/plugins/android/src/com/motorola/studio/android/wizards/elements/SdkTargetSelector.java
new file mode 100644
index 0000000..11d30a0
--- /dev/null
+++ b/src/plugins/android/src/com/motorola/studio/android/wizards/elements/SdkTargetSelector.java
@@ -0,0 +1,347 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.wizards.elements;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.ControlAdapter;
+import org.eclipse.swt.events.ControlEvent;
+import org.eclipse.swt.events.DisposeEvent;
+import org.eclipse.swt.events.DisposeListener;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+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.Composite;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.swt.widgets.TableColumn;
+import org.eclipse.swt.widgets.TableItem;
+
+import com.android.sdklib.IAndroidTarget;
+import com.motorola.studio.android.AndroidPlugin;
+import com.motorola.studio.android.adt.SdkUtils;
+import com.motorola.studio.android.i18n.AndroidNLS;
+import com.motorola.studio.android.model.AndroidProject;
+import com.motorola.studio.android.model.IWizardModel;
+
+/**
+ * SDK Selector for New Android Project Wizards
+ */
+public class SdkTargetSelector extends Composite
+{
+ private Table table;
+
+ private Label mDescription;
+
+ final private AndroidProject project;
+
+ private IAndroidTarget selection = null;
+
+ /**
+ * A selection listener that will check/uncheck items when they are double-clicked
+ */
+ private final SelectionListener listener = new SelectionListener()
+ {
+ /** Default selection means double-click on "most" platforms */
+ public void widgetDefaultSelected(SelectionEvent e)
+ {
+ if (e.item instanceof TableItem)
+ {
+ TableItem i = (TableItem) e.item;
+ i.setChecked(!i.getChecked());
+ enforceSingleSelection(i);
+ updateDescription(i);
+ IAndroidTarget newSelection = getSelection();
+ project.setSdkTarget(newSelection);
+
+ if (newSelection != null)
+ {
+ project.setMinSdkVersion(getSelection().getVersion().getApiString());
+ }
+ notifyListeners(IWizardModel.MODIFIED, new Event());
+ }
+
+ }
+
+ public void widgetSelected(SelectionEvent e)
+ {
+ if (e.item instanceof TableItem)
+ {
+ TableItem i = (TableItem) e.item;
+ enforceSingleSelection(i);
+ updateDescription(i);
+ IAndroidTarget newSelection = getSelection();
+ project.setSdkTarget(newSelection);
+ selection = newSelection;
+ project.setSample(null);
+
+ /*if ((newSelection != null)
+ && !selection.getFullName().equals(newSelection.getFullName()))
+ {
+
+ }*/
+
+ notifyListeners(IWizardModel.MODIFIED, new Event());
+ }
+
+ }
+
+ /**
+ * If we're not in multiple selection mode, uncheck all other
+ * items when this one is selected.
+ */
+ private void enforceSingleSelection(TableItem item)
+ {
+ if (item.getChecked())
+ {
+ Table parentTable = item.getParent();
+ for (TableItem i2 : parentTable.getItems())
+ {
+ if ((i2 != item) && i2.getChecked())
+ {
+ i2.setChecked(false);
+ }
+ }
+ }
+ }
+ };
+
+ /**
+ * Table Tool Tip Listener
+ */
+ private final Listener toolTipListener = new Listener()
+ {
+ public void handleEvent(Event event)
+ {
+ switch (event.type)
+ {
+ case SWT.MouseHover:
+ updateDescription(table.getItem(new Point(event.x, event.y)));
+ break;
+ case SWT.Selection:
+ if (event.item instanceof TableItem)
+ {
+ updateDescription((TableItem) event.item);
+ }
+ break;
+ default:
+ return;
+ }
+ }
+ };
+
+ /**
+ * Creates a new SDK Target Selector.
+ *
+ * @param parent The parent composite where the selector will be added.
+ * @param project the android project
+ */
+ public SdkTargetSelector(Composite parent, AndroidProject project)
+ {
+ super(parent, SWT.NONE);
+ this.project = project;
+
+ createContents(parent);
+ }
+
+ /**
+ * Create Contents
+ * @param parent
+ */
+ private void createContents(Composite parent)
+ {
+ setLayout(new GridLayout());
+ setLayoutData(new GridData(GridData.FILL_BOTH));
+ setFont(parent.getFont());
+
+ table = new Table(this, SWT.CHECK | SWT.FULL_SELECTION | SWT.SINGLE | SWT.BORDER);
+ table.setHeaderVisible(true);
+ table.setLinesVisible(false);
+ table.setLayoutData(new GridData(GridData.FILL_BOTH));
+
+ mDescription = new Label(this, SWT.WRAP);
+ mDescription.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+
+ createColumns(table);
+ table.addSelectionListener(listener);
+ fillTable();
+ setupTooltip(table);
+ }
+
+ /**
+ * Create Table Columns
+ * @param table
+ */
+ private void createColumns(final Table table)
+ {
+ // create the table columns
+ final TableColumn nameColumn = new TableColumn(table, SWT.NONE);
+ nameColumn.setText(AndroidNLS.UI_SdkTargetSelector_SdkTargetNameColumn);
+ final TableColumn vendorColumn = new TableColumn(table, SWT.NONE);
+ vendorColumn.setText(AndroidNLS.UI_SdkTargetSelector_VendorNameColumn);
+ final TableColumn apiColumn = new TableColumn(table, SWT.NONE);
+ apiColumn.setText(AndroidNLS.UI_SdkTargetSelector_APILevelColumn);
+ final TableColumn sdkColumn = new TableColumn(table, SWT.NONE);
+ sdkColumn.setText(AndroidNLS.UI_SdkTargetSelector_SDKVersionColumn);
+
+ table.addControlListener(new ControlAdapter()
+ {
+ @Override
+ public void controlResized(ControlEvent e)
+ {
+ Rectangle r = table.getClientArea();
+ nameColumn.setWidth((r.width * 25) / 100); // 25%
+ vendorColumn.setWidth((r.width * 50) / 100); // 50%
+ apiColumn.setWidth((r.width * 15) / 100); // 15%
+ sdkColumn.setWidth((r.width * 10) / 100); // 10%
+ }
+ });
+ }
+
+ /**
+ * Return table selection.
+ * @return
+ */
+ protected IAndroidTarget getSelection()
+ {
+ IAndroidTarget selectedItem = null;
+ for (TableItem item : table.getItems())
+ {
+ Object data = item.getData();
+ if (item.getChecked() && (data instanceof IAndroidTarget))
+ {
+ selectedItem = (IAndroidTarget) data;
+ break;
+ }
+ }
+ return selectedItem;
+ }
+
+ /**
+ * Fills the table with all SDK targets.
+ */
+ private void fillTable()
+ {
+ // get the targets from the sdk
+ IAndroidTarget[] targets = null;
+ if (SdkUtils.getCurrentSdk() != null)
+ {
+ targets = SdkUtils.getAllTargets();
+ }
+ else
+ {
+ final Runnable listener = new Runnable()
+ {
+ public void run()
+ {
+ table.getDisplay().asyncExec(new Runnable()
+ {
+ public void run()
+ {
+ table.removeAll();
+ fillTable();
+ AndroidPlugin.getDefault().removeSDKLoaderListener(this);
+ }
+ });
+ }
+ };
+ AndroidPlugin.getDefault().addSDKLoaderListener(listener);
+ table.addDisposeListener(new DisposeListener()
+ {
+ public void widgetDisposed(DisposeEvent e)
+ {
+ AndroidPlugin.getDefault().removeSDKLoaderListener(listener);
+ }
+ });
+ }
+
+ if ((targets != null) && (targets.length > 0))
+ {
+ table.setEnabled(true);
+ for (IAndroidTarget target : targets)
+ {
+ TableItem item = new TableItem(table, SWT.NONE);
+ item.setData(target);
+ item.setText(0, target.getName());
+ item.setText(1, target.getVendor());
+ item.setText(2, target.getVersion().getApiString());
+ item.setText(3, target.getVersionName());
+ if (target == project.getSdkTarget())
+ {
+ item.setChecked(true);
+ selection = target;
+ }
+ }
+ }
+ else
+ {
+ table.setEnabled(false);
+ TableItem item = new TableItem(table, SWT.NONE);
+ item.setData(null);
+ item.setText(0, AndroidNLS.UI_SdkTargetSelector_EmptyValue);
+ item.setText(1, AndroidNLS.UI_SdkTargetSelector_NoTargetAvailable);
+ item.setText(2, AndroidNLS.UI_SdkTargetSelector_EmptyValue);
+ item.setText(3, AndroidNLS.UI_SdkTargetSelector_EmptyValue);
+ }
+ }
+
+ /**
+ * Add Tool tip for table
+ * @param table
+ */
+ private void setupTooltip(final Table table)
+ {
+ table.addListener(SWT.Dispose, toolTipListener);
+ table.addListener(SWT.MouseHover, toolTipListener);
+ table.addListener(SWT.MouseMove, toolTipListener);
+ table.addListener(SWT.KeyDown, toolTipListener);
+ }
+
+ /**
+ * Updates the description label
+ */
+ private void updateDescription(TableItem item)
+ {
+ if (item != null)
+ {
+ Object data = item.getData();
+ if (data instanceof IAndroidTarget)
+ {
+ String newTooltip = ((IAndroidTarget) data).getDescription();
+ mDescription.setText(newTooltip == null ? "" : newTooltip); //$NON-NLS-1$
+ }
+ }
+ else
+ {
+ mDescription.setText("");
+ }
+ }
+
+ @Override
+ public void setEnabled(boolean enabled)
+ {
+ if (this.getEnabled() != enabled)
+ {
+ table.setEnabled(enabled);
+ mDescription.setEnabled(enabled);
+ super.setEnabled(enabled);
+ }
+ }
+}
diff --git a/src/plugins/android/src/com/motorola/studio/android/wizards/elements/TableWithLoadingInfo.java b/src/plugins/android/src/com/motorola/studio/android/wizards/elements/TableWithLoadingInfo.java
new file mode 100644
index 0000000..6803a47
--- /dev/null
+++ b/src/plugins/android/src/com/motorola/studio/android/wizards/elements/TableWithLoadingInfo.java
@@ -0,0 +1,597 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.wizards.elements;
+
+import java.util.Collection;
+
+import org.eclipse.jface.wizard.WizardPage;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Layout;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.swt.widgets.TableColumn;
+import org.eclipse.swt.widgets.TableItem;
+
+import com.motorola.studio.android.i18n.AndroidNLS;
+import com.motorola.studio.android.wizards.mat.DumpHPROFTable;
+import com.motorola.studio.android.wizards.mat.DumpHPROFWizardPage;
+
+/**
+ * <p>
+ * This widget displays a table with the ability of getting data asynchronously
+ * and showing it. Basically, while retrieving data, a Displaying info... is displayed.
+ * After the data is retrieved, its info is shown in the Table.
+ * </p>
+ * <p>
+ * To use this component, first one must implement it and at least fill in the methods
+ * {@link #callServiceForRetrievingDataToPopulateTable(Object)} and
+ * {@link #addTableData(Collection)}. The former aims to call the service and
+ * get its data. Note that the asynchronous process is left to this widget. The latter
+ * adds data to the widget´s table.
+ * </p>
+ * <p>
+ * When using this widget in the class, firstly is necessary to instantiate it, add its columns
+ * and finally call the method {@link #populateTableAsynchronously(Object)}. Note that
+ * the object is the parameter(s) for the service calling in method {@link #callServiceForRetrievingDataToPopulateTable(Object)};
+ * </p>
+ * <p>
+ * One should also note that there are several {@link Table} methods wrapped by this component.
+ * In case it is needed, one may add more. The {@link Table} itself is exposed by the method
+ * {@link #getTable()}. The only restriction that should be observed is that
+ * no data should be added manually, only by the method {@link #addTableData(Collection)}.
+ * </p>
+ * <p>
+ * For an example of this widget in action, please consult {@link DumpHPROFTable} and {@link DumpHPROFWizardPage}.
+ * </p>
+ *
+ * @param <P> Class which is the type of the Calling Page, retrieved by
+ * the method {@link TableWithLoadingInfo#getCallingPage()}.
+ * @param <E> Class which is the type of the Element list retrieved by the asynchronous
+ * service. This service is called by the method {@link TableWithLoadingInfo#callServiceForRetrievingDataToPopulateTable(Object)}.
+ * @param <V> The type of value which represents the parameter entered as a parameter
+ * to call the asynchronous service: {@link TableWithLoadingInfo#callServiceForRetrievingDataToPopulateTable(Object)}.
+ */
+public abstract class TableWithLoadingInfo<P, E, V> extends Composite
+{
+ /**
+ * This thread displays the Loading info... message in the table.
+ */
+ private final class LoadingInfoAnimationThread extends Thread
+ {
+ private TableItem item;
+
+ private final Table table;
+
+ /**
+ * Instantiates the Loading info message.
+ *
+ * @param threadName Thread name.
+ * @param table Table which holds the Loading Info... message.
+ * @param item Table item which holds the Loading Info... message.
+ */
+ private LoadingInfoAnimationThread(String threadName, Table table, TableItem item)
+ {
+ super(threadName);
+ this.table = table;
+ this.item = item;
+ }
+
+ /**
+ * Add animated text of Loading Data...
+ */
+ @Override
+ public void run()
+ {
+ int i = 0;
+ StringBuffer text;
+ while (!isInterrupted())
+ {
+ text =
+ new StringBuffer(animatedTextLabel != null ? animatedTextLabel
+ : AndroidNLS.TableWithLoadingInfo__UI_LoadingData);
+ for (int j = 0; j < (i % 4); j++)
+ {
+ text = text.append("."); //$NON-NLS-1$
+ }
+ final StringBuffer finalText = new StringBuffer(text);
+ Display.getDefault().syncExec(new Runnable()
+ {
+ public void run()
+ {
+ // in case the item is disposed, re-create it.
+ if (item.isDisposed())
+ {
+ item = new TableItem(table, SWT.NONE);
+ }
+ item.setText(0, finalText.toString());
+ }
+ });
+ i++;
+ try
+ {
+ sleep(1000);
+ }
+ catch (InterruptedException e)
+ {
+ break;
+ }
+ }
+ }
+ }
+
+ /**
+ * This listener implements the functionality of deselecting all
+ * data from a determined table.
+ */
+ private class DeselectListener implements SelectionListener
+ {
+ Table table;
+
+ /**
+ * Instantiates the Listener for deselecting all data from
+ * the entered table.
+ *
+ * @param table Table which all rows will be deselected.
+ */
+ public DeselectListener(Table table)
+ {
+ this.table = table;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.swt.events.SelectionListener#widgetDefaultSelected(org.eclipse.swt.events.SelectionEvent)
+ */
+ public void widgetDefaultSelected(SelectionEvent e)
+ {
+ this.table.deselectAll();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.swt.events.SelectionListener#widgetSelected(org.eclipse.swt.events.SelectionEvent)
+ */
+ public void widgetSelected(SelectionEvent e)
+ {
+ this.table.deselectAll();
+ }
+
+ }
+
+ E elementList;
+
+ private Thread animatedText;
+
+ private String animatedTextLabel = null;
+
+ private final SelectionListener deselectionItem;
+
+ private final Table table;
+
+ private P callingPage;
+
+ /**
+ * Set the page which called this widget.
+ *
+ * @param The page which called this widget.
+ */
+ public void setCallingPage(P callingPage)
+ {
+ this.callingPage = callingPage;
+ }
+
+ /**
+ * Get the page which called this widget.
+ *
+ * @return The page which called this widget.
+ */
+ public P getCallingPage()
+ {
+ return callingPage;
+ }
+
+ /**
+ * Get the table which is populated. It is strongly recommended that
+ * the insertion of data in this table should be done by the method
+ * {@link TableWithLoadingInfo#addTableData(Collection)}. This table
+ * is exposed in order to allow its customization.
+ *
+ * @return The table to be populated.
+ */
+ public Table getTable()
+ {
+ return table;
+ }
+
+ /**
+ * Get the collection of data of this table, after the table is populated
+ * by calling the service asynchronously.
+ *
+ * @return List of elements retrieved by the service called by the method
+ * {@link #populateTableAsynchronously()}.
+ *
+ * @see TableWithLoadingInfo#callServiceForRetrievingDataToPopulateTable()
+ * @see TableWithLoadingInfo#populateTableAsynchronously()
+ */
+ public E getElementList()
+ {
+ return elementList;
+ }
+
+ /**
+ * Set the "collection" of elements for populating this table. When this method is called,
+ * the Loading info... message is replaced by the list of elements to be inserted, in case
+ * this list is not null. If null is entered as a parameter, the Loading info...
+ * message is displayed.
+ *
+ * @param elementList The Set of elements to populate this table to set
+ */
+ public void populateTable(E elementList)
+ {
+ this.elementList = elementList;
+ // stop the animation
+ if (animatedText != null)
+ {
+ animatedText.interrupt();
+ }
+ // populate and display the list of elements
+ Display.getDefault().syncExec(new Runnable()
+ {
+ /*
+ * (non-Javadoc)
+ * @see java.lang.Runnable#run()
+ */
+ public void run()
+ {
+ if (!table.isDisposed())
+ {
+ // remove the Loading info... message
+ table.removeAll();
+ // populate the table and display the data
+ populateTable();
+ }
+ }
+ });
+
+ this.elementList = elementList;
+ }
+
+ /**
+ * Instantiates a {@link Table} with Loading info component. It holds
+ * the composite parent the the {@link SWT} style. This constructor can
+ * call the method for starting the {@link Table}´s population process.
+ *
+ * @param parent Composite parent.
+ * @param style {@link SWT} style.
+ * @param animatedTextLabel Text to be displayed when data is being loaded.
+ * @param callingPage The page which called this widget.
+ */
+ public TableWithLoadingInfo(Composite parent, int style, String animatedTextLabel, P callingPage)
+ {
+ super(parent, SWT.FILL);
+ // create the layout
+ Layout layout = new GridLayout();
+ this.setLayout(layout);
+
+ // add table
+ this.table = new Table(this, style);
+ this.table.setLayoutData(new GridData(GridData.FILL, GridData.FILL, true, true));
+
+ // set attributes
+ this.animatedTextLabel = animatedTextLabel;
+ this.setCallingPage(callingPage);
+
+ // instantiate listeners
+ deselectionItem = new DeselectListener(table);
+ }
+
+ /**
+ * Remove all items from the {@link Table}.
+ *
+ * @see Table#removeAll()
+ */
+ public void removeAllTableItems()
+ {
+ table.removeAll();
+ }
+
+ /**
+ * Add a column to the {@link Table} of this component.
+ *
+ * @param columnStyle Column style.
+ * @param tableIndex {@link Table} index where the column will be added.
+ *
+ * @return The added {@link TableColumn}
+ *
+ * @see TableColumn#TableColumn(Table, int, int)
+ */
+ public TableColumn addTableColumn(int columnStyle, int tableIndex)
+ {
+ return new TableColumn(table, columnStyle, tableIndex);
+ }
+
+ /**
+ * Add a column the the {@link Table} of this component.
+ *
+ * @param columnStyle Column style.
+ *
+ * @return The added {@link TableColumn}
+ *
+ * @see TableColumn#TableColumn(Table, int)
+ */
+ public TableColumn addTableColumn(int columnStyle)
+ {
+ return new TableColumn(table, columnStyle);
+ }
+
+ /**
+ * Add a {@link SelectionListener} to the table of this component.
+ *
+ * @param selectionListener Selection Listener to be added.
+ *
+ * @see Table#addSelectionListener(SelectionListener)
+ */
+ public void addTableSelectionListener(SelectionListener selectionListener)
+ {
+ table.addSelectionListener(selectionListener);
+ }
+
+ /**
+ * Get the number of selected elements in the table.
+ *
+ * @return Number of selected elements.
+ *
+ * @see Table#getSelectionCount()
+ */
+ public int getTableSelectionCont()
+ {
+ return table.getSelectionCount();
+ }
+
+ /**
+ * Get the index of a selected item.
+ *
+ * @return Index of a selected item
+ *
+ * @see Table#getSelectionIndex()
+ */
+ public int getTableSelectionIndex()
+ {
+ return table.getSelectionIndex();
+ }
+
+ /**
+ * The the list of selected indices.
+ *
+ * @return List of selection indices.
+ *
+ * @see Table#getSelectionIndices()
+ */
+ public int[] getSelectionIndices()
+ {
+ return table.getSelectionIndices();
+ }
+
+ /**
+ * The the list of selected elements.
+ *
+ * @return List of selected Elements
+ *
+ * @see Table#getSelection()
+ */
+ public TableItem[] getTableSelection()
+ {
+ return table.getSelection();
+ }
+
+ /**
+ * Get all Table Items from a {@link Table}.
+ *
+ * @return Array of Table Items ({@link TableItem}).
+ *
+ * @see Table#getItems()
+ */
+ public TableItem[] getTableItems()
+ {
+ return table.getItems();
+ }
+
+ /**
+ * Get a {@link TableItem} based on its index on the {@link Table}.
+ *
+ * @param index Index on the table to retrieve the {@link TableItem}.
+ *
+ * @return Retrieved {@link TableItem}.
+ *
+ * @see Table#getItem(int)
+ */
+ public TableItem getTableItem(int index)
+ {
+ return table.getItem(index);
+ }
+
+ /**
+ * <p>
+ * Marks the {@link Table}´s receiver's lines as visible if the argument is true,
+ * and marks it invisible otherwise. Note that some platforms draw
+ * grid lines while others may draw alternating row colors.
+ * </p>
+ * <p>
+ * If one of the receiver's ancestors is not visible or some other
+ * condition makes
+ * </p>
+ *
+ * @param The new visibility state.
+ *
+ * see {@link Table#setLinesVisible(boolean)}
+ */
+ public void setTableLinesVisible(boolean show)
+ {
+ table.setLinesVisible(show);
+ }
+
+ /**
+ * Makes the {@link Table}´s header visible, is <code>true</code> is set.
+ * Otherwise, mark it not visible.
+ *
+ * @param show Parameter which determines whether tha {@link Table} will be
+ * marked as visible. It will be so, if <code>true</code> is entered.
+ *
+ * @see Table#setHeaderVisible(boolean)
+ */
+ public void setTableHeaderVisible(boolean show)
+ {
+ table.setHeaderVisible(show);
+ }
+
+ /**
+ * Get the number of selected items in a {@link Table}.
+ *
+ * @return Number of selected items in a {@link Table}.
+ *
+ * @see Table#getSelectionCount()
+ */
+ public int getTableSelectionCount()
+ {
+ return table.getSelectionCount();
+ }
+
+ /**
+ * Deselect all items from a {@link Table}.
+ *
+ * @see Table#deselectAll()
+ */
+ public void deselectAllTableItems()
+ {
+ table.deselectAll();
+ }
+
+ /**
+ * Set all items entered as selected.
+ *
+ * @param item Items to be selected.
+ *
+ * @see Table#setSelection(TableItem[])
+ */
+ public void setTableSelection(TableItem[] item)
+ {
+ table.setSelection(item);
+ }
+
+ /**
+ * This method is responsible for calling the service which will populate the {@link Table}. Note
+ * that no asynchronous task is to be done here. This feature is implemented by this widget. Thus,
+ * basically this method should simply call the service for populating the {@link Table}
+ * and return its data as this method´s parameter.
+ *
+ * @param parameters Parameters used by the service
+ *
+ * @return The data retrieved by the service.
+ */
+ protected abstract E callServiceForRetrievingDataToPopulateTable(V parameters);
+
+ /**
+ * This method is responsible for adding data to the {@link Table}. Here the user has
+ * to worry only with inserting elements in the table. The mechanism for
+ * creating the Loading info... is taken care by this table implementation. The data
+ * in inserted in the table refereed by the object retrieved by the method {@link TableWithLoadingInfo#getTable()}.
+ */
+ protected abstract void addTableData(E elementList);
+
+ /**
+ * This method executes needed operations after the table is populated. Here,
+ * thinks like {@link WizardPage#setPageComplete(boolean)} and layout wrap ups ought to be
+ * executed. Note that this method can be empty. Moreover, observe that this method
+ * can be called even if the {@link Table} is empty, because the service could not yet have been called.
+ * It means this method could be called twice, firstly when the Loading info... message is displayed and secondly
+ * when the data is retrieved from the asynchronous service and populates the table.
+ */
+ protected abstract void executeOperationsAfterTableIsPopulated();
+
+ /**
+ * This method populates the Table asynchronously. It means, that it
+ * call the service implemented by the method {@link TableWithLoadingInfo#callServiceForRetrievingDataToPopulateTable(V)}
+ * and while nothing is retrieved, the Loading info... message is displayed. When
+ * the service retrieves data, its value populates the table.
+ *
+ * @param asynchronousServiceParameters The parameters to be passed to be
+ * asynchronous service which will be called.
+ */
+ public void populateTableAsynchronously(final V asynchronousServiceParameters)
+ {
+ // reset element list
+ elementList = null;
+ // populate the table with nothing
+ populateTable();
+ // call the service for populating the table asynchronously
+ Thread callServiceThread = new Thread("listPackages")
+ {
+ /**
+ * Call the service implemented by {@link TableWithLoadingInfo#callServiceForRetrievingDataToPopulateTable(V)}
+ * and populate the Table in this widget.
+ */
+ @Override
+ public void run()
+ {
+ // retrieve the list of running processes in the device
+ E elementList =
+ callServiceForRetrievingDataToPopulateTable(asynchronousServiceParameters);
+ // populate the table
+ populateTable(elementList);
+ };
+ };
+ // start thread for retrieving data and populating the table
+ callServiceThread.start();
+ }
+
+ /**
+ * Populate the table with data provided by {@link TableWithLoadingInfo#addTableData()}. This method
+ * is responsible for creating the Loading info... within the table. In the end,
+ * the method {@link TableWithLoadingInfo#executeOperationsAfterTableIsPopulated()} is executed in order
+ * to perform operations needed after data population is performed. This method, of course, can be empty.
+ */
+ private synchronized void populateTable()
+ {
+ // add data to the table in case there are elements to do so
+ if (elementList != null)
+ {
+ // clear table
+ table.clearAll();
+ if (deselectionItem != null)
+ {
+ table.removeSelectionListener(deselectionItem);
+ }
+ // add data to the table
+ addTableData(elementList);
+ }
+ // since there is no data yet retrieve, add the animation
+ else
+ {
+ // add listeners
+ table.addSelectionListener(deselectionItem);
+
+ // add animated text
+ TableItem item = new TableItem(table, SWT.NONE);
+ animatedText = new LoadingInfoAnimationThread("TextAnimator", table, item);
+ animatedText.start();
+ }
+ // execute final operations
+ executeOperationsAfterTableIsPopulated();
+ }
+}
diff --git a/src/plugins/android/src/com/motorola/studio/android/wizards/elements/WidgetLocationGroup.java b/src/plugins/android/src/com/motorola/studio/android/wizards/elements/WidgetLocationGroup.java
new file mode 100644
index 0000000..536d9aa
--- /dev/null
+++ b/src/plugins/android/src/com/motorola/studio/android/wizards/elements/WidgetLocationGroup.java
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.wizards.elements;
+
+import java.io.File;
+
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.DirectoryDialog;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.Text;
+
+import com.motorola.studio.android.i18n.AndroidNLS;
+import com.motorola.studio.android.model.AndroidProject;
+import com.motorola.studio.android.model.IWizardModel;
+import com.motorola.studio.android.wizards.widget.NewAndroidWidgetProjectMainPage;
+
+/**
+ * Class that implements the Location group to be used in the New Widget Project Wizard
+ */
+public class WidgetLocationGroup extends Composite
+{
+ final private AndroidProject project;
+
+ final private NewAndroidWidgetProjectMainPage page;
+
+ private String lastSourcePathValue = null;
+
+ /**
+ * Constructor
+ *
+ * @param parent
+ * @param project
+ * @param page
+ */
+ public WidgetLocationGroup(Composite parent, AndroidProject project,
+ NewAndroidWidgetProjectMainPage page)
+ {
+ super(parent, SWT.SHADOW_ETCHED_IN);
+ this.project = project;
+ this.page = page;
+ setLayout(new GridLayout());
+ setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+ createControl(this);
+ }
+
+ /**
+ * Create Controls
+ *
+ * @param parent
+ */
+ private void createControl(Composite parent)
+ {
+ final Button useDefaultLocation = new Button(this, SWT.CHECK);
+ useDefaultLocation.setText(AndroidNLS.UI_LocationGroup_UseDefaultLocationCheckLabel);
+ useDefaultLocation.setSelection(true);
+
+ Composite location_group = new Composite(this, SWT.NONE);
+ location_group.setLayout(new GridLayout(4, false));
+ location_group.setLayoutData(new GridData(GridData.FILL_BOTH));
+ location_group.setFont(parent.getFont());
+
+ Label locationLabel = new Label(location_group, SWT.NONE);
+ locationLabel.setText(AndroidNLS.UI_LocationGroup_LocationLabel);
+
+ final Text projectPath = new Text(location_group, SWT.BORDER);
+ GridData data = new GridData(GridData.FILL, GridData.BEGINNING, true, false, 2, 1);
+ projectPath.setLayoutData(data);
+ projectPath.setFont(parent.getFont());
+ projectPath.setText(ResourcesPlugin.getWorkspace().getRoot().getLocation().toOSString());
+ projectPath.setEnabled(false);
+ projectPath.addListener(SWT.Modify, new Listener()
+ {
+ public void handleEvent(Event event)
+ {
+ if (projectPath.isEnabled())
+ {
+ project.setLocation(projectPath.getText());
+ notifyListeners(IWizardModel.MODIFIED, new Event());
+ }
+ }
+ });
+
+ final Button browseButton = new Button(location_group, SWT.PUSH);
+ browseButton.setText(AndroidNLS.UI_General_BrowseButtonLabel);
+ browseButton.setEnabled(false);
+ page.setButtonLayoutData(browseButton);
+ browseButton.addSelectionListener(new SelectionAdapter()
+ {
+ @Override
+ public void widgetSelected(SelectionEvent e)
+ {
+
+ String existing_dir = project.getLocation();
+
+ // Disable the path if it doesn't exist
+ if (existing_dir != null
+ && (existing_dir.length() == 0 || !new File(existing_dir).exists()))
+ {
+ existing_dir = null;
+ }
+
+ DirectoryDialog dd = new DirectoryDialog(projectPath.getShell());
+ dd.setMessage(AndroidNLS.UI_LocationGroup_BrowseDialogMessage);
+ dd.setFilterPath(existing_dir);
+ String customLocation = dd.open();
+ if (customLocation != null)
+ {
+ projectPath.setText(customLocation);
+ }
+ }
+ });
+
+ // Listen to the default location checkbox.
+
+ SelectionListener location_listener = new SelectionAdapter()
+ {
+ @Override
+ public void widgetSelected(SelectionEvent e)
+ {
+
+ boolean useDefault = useDefaultLocation.getSelection();
+
+ String path;
+ if (useDefault)
+ {
+ path = project.getDefaultLocation();
+ }
+ else
+ {
+ path = project.getLocation();
+ }
+ boolean enablePath = !useDefault;
+ projectPath.setEnabled(enablePath);
+ browseButton.setEnabled(enablePath);
+
+ project.setUseDefaultLocation(useDefault);
+
+ if (enablePath && (path == null || path.length() == 0))
+ {
+ projectPath.setText(""); //$NON-NLS-1$
+ projectPath.setFocus();
+ }
+ else
+ {
+ projectPath.setText(path);
+ }
+
+ notifyListeners(IWizardModel.MODIFIED, new Event());
+ }
+
+ };
+
+ useDefaultLocation.addSelectionListener(location_listener);
+ }
+
+ /**
+ * Get last path value defined by the user for the
+ * "Create from existing source" case
+ *
+ * @return The last path value defined by the user.
+ */
+ public String getLastSourcePath()
+ {
+ return this.lastSourcePathValue;
+ }
+
+ /**
+ * Set the last path value defined by the user for the
+ * "Create from existing source" case
+ *
+ * @param path
+ * - The last path defined by the user
+ */
+ public void setLastSourcePath(String path)
+ {
+ this.lastSourcePathValue = path;
+ }
+}
diff --git a/src/plugins/android/src/com/motorola/studio/android/wizards/elements/sorting/TableItemSortStringSetActionListener.java b/src/plugins/android/src/com/motorola/studio/android/wizards/elements/sorting/TableItemSortStringSetActionListener.java
new file mode 100644
index 0000000..c04b23f
--- /dev/null
+++ b/src/plugins/android/src/com/motorola/studio/android/wizards/elements/sorting/TableItemSortStringSetActionListener.java
@@ -0,0 +1,182 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.wizards.elements.sorting;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.swt.widgets.TableColumn;
+import org.eclipse.swt.widgets.TableItem;
+
+import com.motorola.studio.android.wizards.elements.sorting.TableItemStringComparator.SortDirection;
+
+/**
+ * This selection listener sorts the columns of a {@link Table}. It assumes
+ * the columns being sorted are {@link String}s.
+ */
+public class TableItemSortStringSetActionListener implements SelectionListener
+{
+
+ /* (non-Javadoc)
+ * @see org.eclipse.swt.events.SelectionListener#widgetDefaultSelected(org.eclipse.swt.events.SelectionEvent)
+ */
+ public void widgetDefaultSelected(SelectionEvent e)
+ {
+ // sort the table
+ sortTable(e);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.swt.events.SelectionListener#widgetSelected(org.eclipse.swt.events.SelectionEvent)
+ */
+ public void widgetSelected(SelectionEvent e)
+ {
+ // sort the table
+ sortTable(e);
+ }
+
+ /**
+ * Execute the {@link Table} sorting.
+ *
+ * @param e Event object of the sort.
+ */
+ private void sortTable(SelectionEvent e)
+ {
+ // get column to be sorted
+ TableColumn column = (TableColumn) e.widget;
+ // get table belonging to to sorted column
+ Table table = column.getParent();
+
+ // get selected items because the sort messes with them, so they will be restored later
+ TableItem[] selectedItems = table.getSelection();
+ // create a list of new selected items - items which will be selected after the sorting
+ List<TableItem> selectedItemsAfterSorting = new ArrayList<TableItem>();
+
+ // get the sort direction from the table
+ int sortDirection = table.getSortDirection();
+ // determine the new sorting direction according to the old one
+ sortDirection = sortDirection == SWT.UP ? SWT.DOWN : SWT.UP;
+
+ // get Table items
+ TableItem[] items = table.getItems();
+ if ((items != null) && (items.length > 0))
+ {
+ // sort the items - assume you always sort them by String
+ Arrays.sort(items, new TableItemStringComparator(table.indexOf(column),
+ sortDirection == SWT.UP ? SortDirection.ASCENDING : SortDirection.DESCENDING));
+
+ // rearrange the table according to the sorted items - the iteration is in the new sorted order
+ TableItem newItem = null;
+ TableItem item = null;
+ for (int rowIndex = 0; rowIndex < items.length; rowIndex++)
+ {
+ // get current item - to be removed
+ item = items[rowIndex];
+ // create a new table item to be inserted in the table
+ newItem = new TableItem(table, SWT.NONE, rowIndex);
+ // set the text for this new item based on the item in the order
+ newItem.setText(getTableItemData(item));
+ // in case the current item is a selected one, put the new one in the selection list
+ if (isElementInArray(item, selectedItems))
+ {
+ selectedItemsAfterSorting.add(newItem);
+ }
+ // remove the current item
+ item.dispose();
+ }
+ }
+ // set the new selection items
+ TableItem[] selectedItemsAfterSortingArray =
+ new TableItem[selectedItemsAfterSorting.size()];
+ for (int rowIndex = 0; rowIndex < selectedItemsAfterSortingArray.length; rowIndex++)
+ {
+ selectedItemsAfterSortingArray[rowIndex] = selectedItemsAfterSorting.get(rowIndex);
+ }
+ table.setSelection(selectedItemsAfterSortingArray);
+
+ // set the new sorting direction and sorted column
+ table.setSortDirection(sortDirection);
+ table.setSortColumn((column));
+ table.setRedraw(true);
+ }
+
+ /**
+ * Verify whether an element belongs to an array.
+ *
+ * @param <E> Any type of object or primitive
+ * @param element Element to be verified whether it is in a certain array.
+ * @param array Array which the element will be verified to be within.
+ *
+ * @return Returns <code>true</code> in case the element is in the array, <code>false</code>
+ * otherwise.
+ */
+ private <E> boolean isElementInArray(E element, E[] array)
+ {
+ boolean hasElement = false;
+
+ // first all items must not be null and the list not empty
+ if ((element != null) && (array != null) && (array.length > 0))
+ {
+ // verify whether the element is in the array
+ for (E arrayItem : array)
+ {
+ // the element was found, set the flag and quit the loop
+ if (arrayItem.equals(element))
+ {
+ hasElement = true;
+ break;
+ }
+ }
+ }
+
+ return hasElement;
+ }
+
+ /**
+ * Get an array of string holding the data of a {@link TableItem}.
+ *
+ * @param tableItem Table item in which data will be retrieved.
+ *
+ * @return Array of data from the Table Item.
+ */
+ private String[] getTableItemData(TableItem tableItem)
+ {
+ String[] itemArray = new String[0];
+ // get the related table
+ Table table = tableItem.getParent();
+ if (table != null)
+ {
+ // get the number of columns in the table
+ int columnCount = table.getColumnCount();
+ if (columnCount > 0)
+ {
+ // set a string of data to be returned, based on the number of columns
+ itemArray = new String[columnCount];
+ // for each column item put in the array to be returned
+ for (int columnIndex = 0; columnIndex < columnCount; columnIndex++)
+ {
+ itemArray[columnIndex] = tableItem.getText(columnIndex);
+ }
+ }
+ }
+ return itemArray;
+ }
+}
diff --git a/src/plugins/android/src/com/motorola/studio/android/wizards/elements/sorting/TableItemStringComparator.java b/src/plugins/android/src/com/motorola/studio/android/wizards/elements/sorting/TableItemStringComparator.java
new file mode 100644
index 0000000..29f12a9
--- /dev/null
+++ b/src/plugins/android/src/com/motorola/studio/android/wizards/elements/sorting/TableItemStringComparator.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.wizards.elements.sorting;
+
+import java.text.Collator;
+import java.util.Comparator;
+
+import org.eclipse.swt.widgets.TableItem;
+
+/**
+ * This class compares {@link TableItem}´s which have {@link String}s within
+ * them.
+ */
+public class TableItemStringComparator implements Comparator<TableItem>
+{
+ /**
+ * Enumerator representing the sort direction for {@link TableItemStringComparator}.
+ */
+ public enum SortDirection
+ {
+ /**
+ * Ascending direction
+ */
+ ASCENDING(1),
+
+ /**
+ * Descending direction
+ */
+ DESCENDING(-1);
+
+ int sortDirection = 1;
+
+ SortDirection(int sortDirection)
+ {
+ this.sortDirection = sortDirection;
+ }
+
+ public int getSortDirectionIntValue()
+ {
+ return sortDirection;
+ }
+ }
+
+ int columnIndex = 0;
+
+ SortDirection sortDirection = SortDirection.ASCENDING;
+
+ /**
+ * Instantiates a {@link Comparator} for a {@link TableItem} which
+ * compares {@link String}s. The index of the column to be compared
+ * must be entered. Moreover, the sort direction must also be provided.
+ *
+ * @param columnIndex Column index which will be compared.
+ * @param sortDirection Sort direction - ascending or descending
+ */
+ public TableItemStringComparator(int columnIndex, SortDirection sortDirection)
+ {
+ // set fields
+ this.columnIndex = columnIndex;
+ this.sortDirection = sortDirection;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object)
+ */
+ public int compare(TableItem first, TableItem second)
+ {
+ // get table´s items
+ TableItem firstItem = first;
+ TableItem secondItem = second;
+ // get their determined column textx
+ String firstString = (firstItem.getText(columnIndex));
+ String secondString = (secondItem.getText(columnIndex));
+ // compare the strings multiplying by the "sort direction" factor, for ascending or descending purposes
+ return Collator.getInstance().compare(firstString, secondString)
+ * sortDirection.getSortDirectionIntValue();
+ }
+
+}
diff --git a/src/plugins/android/src/com/motorola/studio/android/wizards/installapp/DeployWizard.java b/src/plugins/android/src/com/motorola/studio/android/wizards/installapp/DeployWizard.java
new file mode 100644
index 0000000..b5d8d4f
--- /dev/null
+++ b/src/plugins/android/src/com/motorola/studio/android/wizards/installapp/DeployWizard.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.wizards.installapp;
+
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.wizard.Wizard;
+import org.eclipse.ui.IWorkbench;
+
+import com.motorola.studio.android.AndroidPlugin;
+import com.motorola.studio.android.i18n.AndroidNLS;
+
+/**
+ * DESCRIPTION:
+ * This class implements the Deploy Wizard
+ *
+ * RESPONSIBILITY:
+ * This class is responsible for provide a Wizard that allows the user to select a package and a device instance
+ *
+ */
+public class DeployWizard extends Wizard
+{
+ private String packagePath = null;
+
+ private String selectMessage = null;
+
+ private String wizardDescription = null;
+
+ private String wizardTitle = null;
+
+ private String browseButtonText = null;
+
+ private String packagetext = null;
+
+ private DeployWizardPage page;
+
+ private final String WIZARD_IMAGE_PATH = "icons/wizban/deploy_wizard.png"; //$NON-NLS-1$
+
+ public static enum INSTALL_TYPE
+ {
+ DO_NOTHING, OVERWRITE, UNINSTALL
+ };
+
+ /**
+ * The constructor
+ *
+ * @param packagePath Location of the package containing the application
+ */
+ public DeployWizard(String packagePath)
+ {
+ this.packagePath = packagePath;
+ super.setDefaultPageImageDescriptor(AndroidPlugin.getImageDescriptor(WIZARD_IMAGE_PATH));
+ initializeMessages();
+ }
+
+ /**
+ * Initializes all the texts that will be used within the wizard
+ */
+ private void initializeMessages()
+ {
+ selectMessage = AndroidNLS.UI_DeployWizard_SelectMessage;
+ wizardDescription = AndroidNLS.UI_DeployWizard_WizardDescription;
+ wizardTitle = AndroidNLS.UI_DeployWizard_WizardTitle;
+ browseButtonText = AndroidNLS.UI_DeployWizard_BrowseButtonText;
+ packagetext = AndroidNLS.UI_DeployWizard_PackageText;
+ this.setWindowTitle(wizardTitle);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.jface.wizard.Wizard#addPages()
+ */
+ @Override
+ public void addPages()
+ {
+ page = new DeployWizardPage(packagePath, selectMessage, browseButtonText, packagetext);
+ page.setDescription(wizardDescription);
+ page.setTitle(wizardTitle);
+ super.addPage(page);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.ui.IWorkbenchWizard#init(org.eclipse.ui.IWorkbench, org.eclipse.jface.viewers.IStructuredSelection)
+ */
+ public void init(IWorkbench workbench, IStructuredSelection selection)
+ {
+ // Do nothing
+ }
+
+ @Override
+ public boolean canFinish()
+ {
+ return page.isPageComplete();
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.jface.wizard.Wizard#performFinish()
+ */
+ @Override
+ public boolean performFinish()
+ {
+ packagePath = page.getPackagePath();
+ return true;
+ }
+
+ public String getPackagePath()
+ {
+ return packagePath;
+ }
+
+ /**
+ * Return true if the application
+ * should be replaced in the case it is
+ * already installed on the device
+ *
+ */
+ public INSTALL_TYPE canOverwrite()
+ {
+ return page.canOverwrite();
+ }
+}
diff --git a/src/plugins/android/src/com/motorola/studio/android/wizards/installapp/DeployWizardPage.java b/src/plugins/android/src/com/motorola/studio/android/wizards/installapp/DeployWizardPage.java
new file mode 100644
index 0000000..53fb85a
--- /dev/null
+++ b/src/plugins/android/src/com/motorola/studio/android/wizards/installapp/DeployWizardPage.java
@@ -0,0 +1,430 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.wizards.installapp;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+
+import org.eclipse.core.runtime.Path;
+import org.eclipse.jface.dialogs.DialogPage;
+import org.eclipse.jface.wizard.WizardPage;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.ModifyEvent;
+import org.eclipse.swt.events.ModifyListener;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Group;
+import org.eclipse.ui.PlatformUI;
+
+import com.motorola.studio.android.AndroidPlugin;
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.i18n.AndroidNLS;
+import com.motorola.studio.android.wizards.elements.FileChooser;
+import com.motorola.studio.android.wizards.installapp.DeployWizard.INSTALL_TYPE;
+
+/**
+ * Wizard Page used by Deploy Wizard
+ */
+public class DeployWizardPage extends WizardPage
+{
+ private FileChooser fileChooser = null;
+
+ private String initialPackagePath = null;
+
+ private String packageSelectionMessage = null;
+
+ private String packagetext = null;
+
+ private final String packageExtension = "apk";
+
+ private Button overwiteRadio = null;
+
+ private Button uninstallRadio = null;
+
+ private Button doNothingRadio = null;
+
+ private static final String contextId = AndroidPlugin.PLUGIN_ID + ".install_app";
+
+ private static INSTALL_TYPE installType;
+
+ private final String DSA_FILE_EXTENSION = ".DSA";
+
+ private final String RSA_FILE_EXTENSION = ".RSA";
+
+ private final String SF_FILE_EXTENSION = ".SF";
+
+ private static String lastUsedPackage = null;
+
+ /**
+ * Constructor
+ *
+ * @param initialPackagePath
+ * @param selectPCKMessage
+ * Message asking for package selection
+ */
+ public DeployWizardPage(String initialPackagePath, String selectPCKMessage,
+ String browseButtonText, String packagetext)
+ {
+ super("");
+
+ if ((browseButtonText == null) || (packagetext == null) || (selectPCKMessage == null))
+ {
+ throw new IllegalArgumentException("Could not create Deploy Wizard: null argument");
+ }
+
+ if (initialPackagePath == null)
+ {
+ this.initialPackagePath = lastUsedPackage != null ? lastUsedPackage : "";
+ }
+ else
+ {
+ this.initialPackagePath = initialPackagePath;
+ }
+
+ this.packagetext = packagetext;
+ packageSelectionMessage = selectPCKMessage;
+
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * org.eclipse.jface.dialogs.IDialogPage#createControl(org.eclipse.swt.widgets
+ * .Composite)
+ */
+ public void createControl(Composite parent)
+ {
+ // Main composite for the UI
+ Composite mainComposite = new Composite(parent, SWT.FILL);
+
+ PlatformUI.getWorkbench().getHelpSystem().setHelp(mainComposite, contextId);
+
+ mainComposite.setLayout(new GridLayout());
+ mainComposite.setLayoutData(new GridData(GridData.FILL_HORIZONTAL, GridData.FILL_VERTICAL,
+ true, true));
+
+ // Package group
+ Group packageGroup = new Group(mainComposite, SWT.NONE);
+ packageGroup.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
+ packageGroup.setLayout(new GridLayout(3, false));
+ packageGroup.setText(packagetext);
+
+ fileChooser = new FileChooser(packageGroup, SWT.NONE, null);
+ fileChooser.setFilterExtensions(new String[]
+ {
+ "*." + packageExtension
+ });
+ fileChooser.setLayoutData(new GridData(SWT.FILL, SWT.NONE, true, false));
+ fileChooser.addModifyListener(new ModifyListener()
+ {
+ public void modifyText(ModifyEvent e)
+ {
+ validateSelection();
+ }
+ });
+ createOptionButtons(mainComposite);
+
+ mainComposite.pack();
+
+ setPageComplete(false);
+ loadInitialValues();
+
+ setControl(mainComposite);
+ }
+
+ private void createOptionButtons(Composite mainComposite)
+ {
+
+ GridData data = new GridData(SWT.FILL, SWT.FILL, true, false, 3, 1);
+ overwiteRadio = new Button(mainComposite, SWT.RADIO);
+ overwiteRadio.setText(AndroidNLS.UI_DeployWizardPage_ReplaceApp);
+ overwiteRadio.setSelection(true);
+ overwiteRadio.setLayoutData(data);
+ overwiteRadio.setData(INSTALL_TYPE.OVERWRITE);
+ overwiteRadio.addSelectionListener(new SelectionAdapter()
+ {
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * org.eclipse.swt.events.SelectionAdapter#widgetSelected(org.eclipse
+ * .swt.events.SelectionEvent)
+ */
+ @Override
+ public void widgetSelected(SelectionEvent e)
+ {
+ installType = (INSTALL_TYPE) overwiteRadio.getData();
+ }
+
+ });
+
+ uninstallRadio = new Button(mainComposite, SWT.RADIO);
+ uninstallRadio.setText(AndroidNLS.UI_DeployWizardPage_UninstallApp);
+ uninstallRadio.setSelection(true);
+ uninstallRadio.setLayoutData(data);
+ uninstallRadio.setData(INSTALL_TYPE.UNINSTALL);
+ uninstallRadio.addSelectionListener(new SelectionAdapter()
+ {
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * org.eclipse.swt.events.SelectionAdapter#widgetSelected(org.eclipse
+ * .swt.events.SelectionEvent)
+ */
+ @Override
+ public void widgetSelected(SelectionEvent e)
+ {
+ installType = (INSTALL_TYPE) uninstallRadio.getData();
+ }
+
+ });
+
+ doNothingRadio = new Button(mainComposite, SWT.RADIO);
+ doNothingRadio.setText(AndroidNLS.UI_DeployWizardPage_DoNothingApp);
+ doNothingRadio.setSelection(true);
+ doNothingRadio.setLayoutData(data);
+ doNothingRadio.setData(INSTALL_TYPE.DO_NOTHING);
+ doNothingRadio.addSelectionListener(new SelectionAdapter()
+ {
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * org.eclipse.swt.events.SelectionAdapter#widgetSelected(org.eclipse
+ * .swt.events.SelectionEvent)
+ */
+ @Override
+ public void widgetSelected(SelectionEvent e)
+ {
+ installType = (INSTALL_TYPE) doNothingRadio.getData();
+ }
+
+ });
+ overwiteRadio.setSelection(true);
+ uninstallRadio.setSelection(false);
+ doNothingRadio.setSelection(false);
+ installType = (INSTALL_TYPE.OVERWRITE);
+
+ }
+
+ /**
+ * Load the initial values to be filled in the wizard
+ */
+ private void loadInitialValues()
+ {
+ if ((initialPackagePath != null) && (initialPackagePath.length() != 0))
+ {
+ fileChooser.setText(initialPackagePath);
+ validateSelection();
+ }
+ else
+ {
+ setMessage(packageSelectionMessage, DialogPage.NONE);
+ }
+ }
+
+ /**
+ * Validates the selected package and device instance setting the
+ * appropriated messages and errors
+ */
+ private synchronized void validateSelection()
+ {
+ String packagePath = fileChooser.getText();
+
+ if (isValidPackage(packagePath))
+ {
+ setErrorMessage(null);
+ setPageComplete(true);
+ }
+ else
+ {
+ setPageComplete(false);
+ }
+ }
+
+ /**
+ * Verify if a package is valid MPKG
+ *
+ * @param packagePath
+ * @return TRUE if the package is valid or FALSE otherwise
+ */
+ private boolean isValidPackage(String packagePath)
+ {
+ boolean result = false;
+ Path path = new Path(packagePath);
+ String extension = path.getFileExtension();
+
+ // testing if the entered path is a folder
+ result = path.toFile().isFile();
+ if (!result)
+ {
+ setErrorMessage(AndroidNLS.UI_DeployWizardPage_PackageIsAFolder);
+ }
+ else
+ {
+ result =
+ ((extension != null) && extension.equals(packageExtension) && path
+ .isValidPath(path.toString()));
+
+ if (!result)
+ {
+ setErrorMessage(AndroidNLS.UI_DeployWizardPage_InvalidPath);
+ }
+ else
+ {
+ // Test if file exists
+ result = path.toFile().exists();
+ if (!result)
+ {
+ setErrorMessage(AndroidNLS.UI_DeployWizardPage_FileDoesNotExist);
+ }
+ }
+ }
+
+ if (result)
+ {
+ setMessage("", DialogPage.NONE);
+
+ //Test if the package is valid
+ result = isPackageSigned(packagePath);
+ if (!result)
+ {
+ setErrorMessage(AndroidNLS.UI_DeployWizardPage_NotSignedMessage);
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Verify if the package is signed based on the
+ * existence of an .SF file and a corresponding
+ * DSA or RSA file.
+ *
+ * @param packagePath
+ * @return TRUE if the package is signed
+ */
+ private synchronized boolean isPackageSigned(String packagePath)
+ {
+ // Temporary placeholders for the package entries
+ List<String> SFFiles = new ArrayList<String>();
+ List<String> RSAFiles = new ArrayList<String>();
+ List<String> DSAFiles = new ArrayList<String>();
+
+ //Temporary result
+ boolean result = false;
+ JarFile jar = null;
+ try
+ {
+ jar = new JarFile(packagePath, false);
+ Enumeration<JarEntry> enu = jar.entries();
+
+ //interact over the elements of the package
+ while (enu.hasMoreElements())
+ {
+ JarEntry entry = enu.nextElement();
+ if (entry.getName().toUpperCase().endsWith(SF_FILE_EXTENSION))
+ {
+ // Mounts the list of SF files
+ SFFiles.add(entry.getName().toUpperCase());
+ }
+ else if (entry.getName().toUpperCase().endsWith(RSA_FILE_EXTENSION))
+ {
+ // Mounts the list of RSA files
+ RSAFiles.add(entry.getName().toUpperCase());
+ }
+ else if (entry.getName().toUpperCase().endsWith(DSA_FILE_EXTENSION))
+ {
+ // Mounts the list of DSA files
+ DSAFiles.add(entry.getName().toUpperCase());
+ }
+ }
+
+ if (!SFFiles.isEmpty())
+ {
+ for (String sfFile : SFFiles)
+ {
+ // Interacts over the list of SF files until it ends or until a correspondent DSA or RSA is found
+ Path p = new Path(sfFile);
+ sfFile = p.removeFileExtension().toString();
+ result =
+ (DSAFiles.contains(sfFile + DSA_FILE_EXTENSION) || RSAFiles
+ .contains(sfFile + RSA_FILE_EXTENSION));
+ }
+ }
+
+ }
+ catch (Exception e)
+ {
+ // Could not read the jar file
+ StudioLogger.error(DeployWizardPage.class, "Deploy: Could not verify file "
+ + packagePath, e);
+ }
+ finally
+ {
+ if (jar != null)
+ {
+ try
+ {
+ jar.close();
+ }
+ catch (IOException e)
+ {
+ StudioLogger.error(DeployWizardPage.class,
+ "Error closing package after verification", e);
+ }
+ }
+ }
+
+ return result;
+
+ }
+
+ /**
+ * Gets the selected package path
+ *
+ * @return the package Path
+ */
+ public String getPackagePath()
+ {
+ String packagePath = fileChooser.getText();
+ if (isValidPackage(packagePath))
+ {
+ lastUsedPackage = packagePath;
+ }
+ return packagePath;
+ }
+
+ /**
+ * Return true if the application should be replaced in the case it is
+ * already installed on the device
+ *
+ */
+ public INSTALL_TYPE canOverwrite()
+ {
+ return installType;
+ }
+} \ No newline at end of file
diff --git a/src/plugins/android/src/com/motorola/studio/android/wizards/installapp/UninstallAppWizard.java b/src/plugins/android/src/com/motorola/studio/android/wizards/installapp/UninstallAppWizard.java
new file mode 100644
index 0000000..a8364a3
--- /dev/null
+++ b/src/plugins/android/src/com/motorola/studio/android/wizards/installapp/UninstallAppWizard.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.wizards.installapp;
+
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.jface.wizard.Wizard;
+
+import com.motorola.studio.android.AndroidPlugin;
+import com.motorola.studio.android.i18n.AndroidNLS;
+
+public class UninstallAppWizard extends Wizard
+{
+ private UninstallAppWizardPage mainPage = null;
+
+ private List<String> packagesToUninstall = null;
+
+ private Map<String, String> availablePackages = null;
+
+ private final String WIZARD_IMAGE_PATH = "icons/wizban/undeploy_wizard.png"; //$NON-NLS-1$
+
+ public UninstallAppWizard()
+ {
+ setWindowTitle(AndroidNLS.UninstallAppWizardPage_PageTitle);
+ super.setDefaultPageImageDescriptor(AndroidPlugin.getImageDescriptor(WIZARD_IMAGE_PATH));
+ setHelpAvailable(false);
+ }
+
+ @Override
+ public void addPages()
+ {
+ mainPage = new UninstallAppWizardPage(availablePackages);
+ addPage(mainPage);
+ }
+
+ @Override
+ public boolean performFinish()
+ {
+ packagesToUninstall = mainPage.getPackageList();
+ return packagesToUninstall.size() > 0;
+ }
+
+ public void init(Map<String, String> applicationList)
+ {
+ availablePackages = applicationList;
+ }
+
+ public void setAvailablePackages(Map<String, String> applicationList)
+ {
+ init(applicationList);
+ mainPage.setAvailablePackages(applicationList);
+ }
+
+ public List<String> getSelectedPackages()
+ {
+ return packagesToUninstall;
+ }
+
+}
diff --git a/src/plugins/android/src/com/motorola/studio/android/wizards/installapp/UninstallAppWizardPage.java b/src/plugins/android/src/com/motorola/studio/android/wizards/installapp/UninstallAppWizardPage.java
new file mode 100644
index 0000000..b278eb4
--- /dev/null
+++ b/src/plugins/android/src/com/motorola/studio/android/wizards/installapp/UninstallAppWizardPage.java
@@ -0,0 +1,258 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.wizards.installapp;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.jface.wizard.WizardPage;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.swt.widgets.TableColumn;
+import org.eclipse.swt.widgets.TableItem;
+import org.eclipse.ui.PlatformUI;
+
+import com.motorola.studio.android.AndroidPlugin;
+import com.motorola.studio.android.i18n.AndroidNLS;
+
+public class UninstallAppWizardPage extends WizardPage
+{
+
+ private Table packageTable;
+
+ private Map<String, String> availablePackages;
+
+ private static final String contextId = AndroidPlugin.PLUGIN_ID + ".uninstall_app"; //$NON-NLS-1$
+
+ private boolean filterSystem;
+
+ private Thread animatedText = null;
+
+ /**
+ * Create a new instance of this page, given the list of items to be
+ * displayed.
+ *
+ * @param applicationList
+ * : the list of app
+ * @param filterSystem
+ * : show only user applications if true
+ */
+ public UninstallAppWizardPage(Map<String, String> applicationList, boolean filterSystem)
+ {
+ super("uninstall app page"); //$NON-NLS-1$
+ this.filterSystem = filterSystem;
+ availablePackages = applicationList;
+ setTitle(AndroidNLS.UninstallAppWizardPage_PageTitle);
+ setDescription(AndroidNLS.UninstallAppWizardPage_PageDescription);
+ }
+
+ public UninstallAppWizardPage(Map<String, String> applicationList)
+ {
+ this(applicationList, true);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * org.eclipse.jface.dialogs.IDialogPage#createControl(org.eclipse.swt.widgets
+ * .Composite)
+ */
+ public void createControl(Composite parent)
+ {
+ Composite mainComposite = new Composite(parent, SWT.FILL);
+ mainComposite.setLayout(new GridLayout());
+ packageTable = new Table(mainComposite, SWT.BORDER | SWT.MULTI | SWT.FULL_SELECTION);
+ packageTable.setHeaderVisible(true);
+ GridData layoutData = new GridData(SWT.FILL, SWT.FILL, true, true);
+ packageTable.setLayoutData(layoutData);
+ TableColumn packageNameColumn = new TableColumn(packageTable, SWT.CENTER);
+ TableColumn isSystemColumn = new TableColumn(packageTable, SWT.CENTER);
+ packageNameColumn.setText(AndroidNLS.UninstallAppWizardPage_ColumnPackageName);
+ isSystemColumn.setText(AndroidNLS.UninstallAppWizardPage_ColumnPackageKiind);
+ packageNameColumn.setWidth(200);
+ isSystemColumn.setWidth(100);
+ packageNameColumn.addSelectionListener(new SelectionListener()
+ {
+
+ public void widgetSelected(SelectionEvent e)
+ {
+ sortItems(0);
+ }
+
+ public void widgetDefaultSelected(SelectionEvent e)
+ {
+ // do nothing
+ }
+ });
+ isSystemColumn.addSelectionListener(new SelectionListener()
+ {
+
+ public void widgetSelected(SelectionEvent e)
+ {
+ sortItems(1);
+ }
+
+ public void widgetDefaultSelected(SelectionEvent e)
+ {
+ // do nothing
+ }
+ });
+
+ populate();
+ PlatformUI.getWorkbench().getHelpSystem().setHelp(mainComposite, contextId);
+ setControl(mainComposite);
+ }
+
+ private void populate()
+ {
+ if (availablePackages != null)
+ {
+ Iterator<String> it = availablePackages.keySet().iterator();
+
+ while (it.hasNext())
+ {
+ String packageName = it.next();
+ String packagePath = availablePackages.get(packageName);
+ if (!filterSystem || !packagePath.toLowerCase().contains("system")) //$NON-NLS-1$
+ {
+ TableItem item = new TableItem(packageTable, SWT.NONE);
+ item.setText(0, packageName);
+ item.setText(1, packagePath.contains("system") //$NON-NLS-1$
+ ? AndroidNLS.UninstallAppWizardPage_SystemLabel
+ : AndroidNLS.UninstallAppWizardPage_UserLabel);
+
+ }
+ }
+ packageTable.setLinesVisible(false);
+ packageTable.addSelectionListener(new SelectionListener()
+ {
+
+ public void widgetSelected(SelectionEvent e)
+ {
+ validatePage();
+ }
+
+ public void widgetDefaultSelected(SelectionEvent e)
+ {
+ // do nothing
+ }
+ });
+ }
+ else
+ {
+ final TableItem item = new TableItem(packageTable, SWT.NONE);
+ animatedText = new Thread("TextAnimator") //$NON-NLS-1$
+ {
+ @Override
+ public void run()
+ {
+ int i = 0;
+ while (!isInterrupted())
+ {
+ String text =
+ AndroidNLS.UninstallAppWizardPage_Loading_Applications;
+ for (int j = 0; j < (i % 4); j++)
+ {
+ text += "."; //$NON-NLS-1$
+ }
+ final String finalText = text;
+ Display.getDefault().syncExec(new Runnable()
+ {
+ public void run()
+ {
+ if (!item.isDisposed())
+ {
+ item.setText(0, finalText);
+ }
+ }
+ });
+ i++;
+ try
+ {
+ sleep(1000);
+ }
+ catch (InterruptedException e)
+ {
+ break;
+ }
+ }
+ }
+ };
+ animatedText.start();
+ }
+ packageTable.layout(true);
+ validatePage();
+ }
+
+ private void validatePage()
+ {
+ setPageComplete(availablePackages != null ? packageTable.getSelection().length > 0 : false);
+ }
+
+ /**
+ * get the list of selected packages to the uninstallation
+ *
+ * @return
+ */
+ public List<String> getPackageList()
+ {
+ List<String> selectedPackages = new ArrayList<String>();
+ for (TableItem item : packageTable.getSelection())
+ {
+ selectedPackages.add(item.getText(0));
+ }
+
+ return selectedPackages;
+ }
+
+ /**
+ * Sort items based on the given column
+ *
+ * @param column
+ */
+ private void sortItems(int column)
+ {
+ // Improvement. Nothing to do here for now.
+ }
+
+ public void setAvailablePackages(Map<String, String> applicationList)
+ {
+ availablePackages = applicationList;
+ if (animatedText != null)
+ {
+ animatedText.interrupt();
+ }
+ Display.getDefault().syncExec(new Runnable()
+ {
+
+ public void run()
+ {
+ packageTable.removeAll();
+ populate();
+ }
+ });
+
+ }
+}
diff --git a/src/plugins/android/src/com/motorola/studio/android/wizards/mat/DumpHPROFTable.java b/src/plugins/android/src/com/motorola/studio/android/wizards/mat/DumpHPROFTable.java
new file mode 100644
index 0000000..6f3f6bf
--- /dev/null
+++ b/src/plugins/android/src/com/motorola/studio/android/wizards/mat/DumpHPROFTable.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.wizards.mat;
+
+import java.util.Collection;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.TableItem;
+
+import com.motorola.studio.android.adt.DDMSFacade;
+import com.motorola.studio.android.wizards.elements.TableWithLoadingInfo;
+
+/**
+ * This table is used for Dump HPROF.
+ */
+public class DumpHPROFTable extends
+ TableWithLoadingInfo<DumpHPROFWizardPage, Collection<String>, String>
+{
+
+ /**
+ * @see DumpHPROFTable#DumpHPROFTable(Composite, int, String, Object, boolean, Object)
+ */
+ public DumpHPROFTable(Composite parent, int style, String animatedTextLabel,
+ DumpHPROFWizardPage callingPage)
+ {
+ super(parent, style, animatedTextLabel, callingPage);
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.wizards.elements.tablewithloadinginfo.TableWithLoadingInfo#addTableData(java.util.Collection)
+ */
+ @Override
+ protected void addTableData(Collection<String> elementList)
+ {
+ Collection<String> runningApps = getElementList();
+
+ // Populate table with the info
+ if (runningApps != null)
+ {
+ for (String appName : runningApps)
+ {
+ TableItem item = new TableItem(getTable(), SWT.NONE);
+ if (appName != null)
+ {
+ item.setText(0, appName);
+ }
+ }
+ }
+ }
+
+ /**
+ * Set the wizard page completion status after data is added to
+ * the page.
+ */
+ @Override
+ protected void executeOperationsAfterTableIsPopulated()
+ {
+ // set the page to completed
+ getCallingPage().setPageComplete(getTable().getSelection().length > 0);
+ }
+
+ /**
+ * Retrieve all running applications given a serial number.
+ */
+ @Override
+ protected Collection<String> callServiceForRetrievingDataToPopulateTable(String serialNumber)
+ {
+ // get the running applications
+ return DDMSFacade.getRunningApplications(serialNumber);
+ }
+}
diff --git a/src/plugins/android/src/com/motorola/studio/android/wizards/mat/DumpHPROFWizard.java b/src/plugins/android/src/com/motorola/studio/android/wizards/mat/DumpHPROFWizard.java
new file mode 100644
index 0000000..cfa5a66
--- /dev/null
+++ b/src/plugins/android/src/com/motorola/studio/android/wizards/mat/DumpHPROFWizard.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.wizards.mat;
+
+import org.eclipse.jface.wizard.Wizard;
+
+import com.motorola.studio.android.AndroidPlugin;
+import com.motorola.studio.android.i18n.AndroidNLS;
+
+public class DumpHPROFWizard extends Wizard
+{
+ private DumpHPROFWizardPage mainPage = null;
+
+ private String selectedApp = null;
+
+ private String serialNumber = null;
+
+ private final String WIZARD_IMAGE_PATH = "icons/wizban/dump_hprof_wizard.png"; //$NON-NLS-1$
+
+ public DumpHPROFWizard(String serialNumber)
+ {
+ setWindowTitle(AndroidNLS.DumpHprofPage_PageTitle);
+ super.setDefaultPageImageDescriptor(AndroidPlugin.getImageDescriptor(WIZARD_IMAGE_PATH));
+ setHelpAvailable(false);
+ // set parameters
+ this.serialNumber = serialNumber;
+ }
+
+ @Override
+ public void addPages()
+ {
+ mainPage = new DumpHPROFWizardPage(serialNumber);
+ addPage(mainPage);
+ }
+
+ @Override
+ public boolean performFinish()
+ {
+ selectedApp = mainPage.getSelectedApp();
+ return selectedApp != null;
+ }
+
+ public String getSelectedApp()
+ {
+ return selectedApp;
+ }
+
+}
diff --git a/src/plugins/android/src/com/motorola/studio/android/wizards/mat/DumpHPROFWizardPage.java b/src/plugins/android/src/com/motorola/studio/android/wizards/mat/DumpHPROFWizardPage.java
new file mode 100644
index 0000000..a35c01e
--- /dev/null
+++ b/src/plugins/android/src/com/motorola/studio/android/wizards/mat/DumpHPROFWizardPage.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.wizards.mat;
+
+import org.eclipse.jface.wizard.WizardPage;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.TableColumn;
+import org.eclipse.ui.PlatformUI;
+
+import com.motorola.studio.android.AndroidPlugin;
+import com.motorola.studio.android.i18n.AndroidNLS;
+import com.motorola.studio.android.wizards.elements.sorting.TableItemSortStringSetActionListener;
+
+public class DumpHPROFWizardPage extends WizardPage
+{
+
+ /**
+ * Page name
+ */
+ private static final String PAGE_NAME = "Dump HPROF page"; //$NON-NLS-1$
+
+ /**
+ * SWT table control
+ */
+ private DumpHPROFTable appsTable;
+
+ /**
+ * Serial Number
+ */
+ private final String serialNumber;
+
+ /**
+ * Help Id
+ */
+ private static final String helpContextId = AndroidPlugin.PLUGIN_ID + ".dump_hprof"; //$NON-NLS-1$
+
+ /**
+ * Constructor
+ *
+ * @param serialNumber Serial Number.
+ */
+ public DumpHPROFWizardPage(String serialNumber)
+ {
+ super(PAGE_NAME);
+ this.serialNumber = serialNumber;
+ setTitle(AndroidNLS.DumpHprofPage_PageTitle);
+ setDescription(AndroidNLS.DumpHprofPage_PageDescription);
+ }
+
+ public void createControl(Composite parent)
+ {
+ this.initializeDialogUnits(parent);
+
+ Composite mainComposite = new Composite(parent, SWT.FILL);
+ mainComposite.setLayout(new GridLayout());
+
+ // Running apps table
+ appsTable =
+ new DumpHPROFTable(mainComposite, SWT.BORDER | SWT.SINGLE | SWT.FULL_SELECTION,
+ AndroidNLS.DumpHPROFWizardPage__Message_LoadingRunningApplications, this);
+
+ appsTable.setTableHeaderVisible(true);
+
+ GridData layoutData = new GridData(GridData.FILL, GridData.FILL, true, true);
+ appsTable.setLayoutData(layoutData);
+
+ TableColumn appNameColumn = appsTable.addTableColumn(SWT.CENTER);
+ appNameColumn.setText(AndroidNLS.DumpHprofPage_ColumnAppName);
+
+ appNameColumn.setWidth(this.convertWidthInCharsToPixels(70));
+ appNameColumn.addSelectionListener(new TableItemSortStringSetActionListener());
+
+ appsTable.setTableLinesVisible(false);
+ appsTable.addTableSelectionListener(new SelectionListener()
+ {
+
+ public void widgetSelected(SelectionEvent e)
+ {
+ validatePage();
+ }
+
+ public void widgetDefaultSelected(SelectionEvent e)
+ {
+ //do nothing
+ }
+ });
+
+ appsTable.populateTableAsynchronously(serialNumber);
+ PlatformUI.getWorkbench().getHelpSystem().setHelp(mainComposite, helpContextId);
+ validatePage();
+ setControl(mainComposite);
+ }
+
+ /**
+ * Validates the page
+ */
+ private void validatePage()
+ {
+ setPageComplete(appsTable.getTable().getSelection().length > 0);
+ }
+
+ /**
+ * Get the selected application. This will be used to generate the HPROF file
+ * @return
+ */
+ public String getSelectedApp()
+ {
+ String selectedApp = null;
+
+ if (appsTable.getTableSelectionCont() == 1)
+ {
+ selectedApp = appsTable.getTableSelection()[0].getText(0);
+ }
+
+ return selectedApp;
+ }
+}
diff --git a/src/plugins/android/src/com/motorola/studio/android/wizards/monkey/AbstractPropertiesComposite.java b/src/plugins/android/src/com/motorola/studio/android/wizards/monkey/AbstractPropertiesComposite.java
new file mode 100644
index 0000000..b8cd709
--- /dev/null
+++ b/src/plugins/android/src/com/motorola/studio/android/wizards/monkey/AbstractPropertiesComposite.java
@@ -0,0 +1,185 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.wizards.monkey;
+
+import java.util.Collection;
+import java.util.EventListener;
+import java.util.EventObject;
+import java.util.LinkedHashSet;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+
+/**
+ * DESCRIPTION:
+ * <br>
+ * This class is an abstract implementation of a Composite extension that specific composites
+ * for making Composites listeners.
+ * <br>
+ * It provides common functionalities to those subclasses which assist content validation,
+ * layout, etc.
+ * <br>
+ * RESPONSIBILITY:
+ * <br>
+ * - Provide common functionalities to classes implementing Composites
+ * <br>
+ * COLABORATORS:
+ * <br>
+ * Composite: extends this class
+ * <br>
+ * MagxPropertyCompositeChangeListener: declares this interface for other classes to be able to
+ * listen to change events on the content
+ * <br>
+ * MagxPropertyCompositeChangeEvent: declares the class and uses this kind of event for
+ * notifying content change to listeners
+ * <br>
+ * USAGE:
+ * <br>
+ * This class should be extended by UI classes implementing Composites (property pages, wizards, etc).
+ */
+public abstract class AbstractPropertiesComposite extends Composite
+{
+ /**
+ *
+ * This class represents events for notifying content change on AbstractPropertiesComposite
+ * extending classes to registered listeners.
+ *
+ */
+ @SuppressWarnings("serial")
+ public class PropertyCompositeChangeEvent extends EventObject
+ {
+ /**
+ * Creates a new MagxPropertyCompositeChangeEvent object with the composite
+ * that changed as its data.
+ *
+ * @param composite the composite that changed
+ */
+ PropertyCompositeChangeEvent(AbstractPropertiesComposite composite)
+ {
+ super(composite);
+ }
+ }
+
+ /**
+ *
+ * This interface must be implemented by classes that wish to listen to
+ * changes on AbstractPropertiesComposite extending classes.
+ *
+ */
+ public interface PropertyCompositeChangeListener extends EventListener
+ {
+ /**
+ * Notifies the event of change on AbstractPropertiesComposite extending classes.
+ *
+ * @param e the change event
+ */
+ public void compositeChanged(PropertyCompositeChangeEvent e);
+ }
+
+ // The default value to use for margins, both vertical and horizontal
+ private static final int DEFAULT_MARGIN_SIZE = 20;
+
+ // collection of registered listeners to this composite
+ private static final Collection<PropertyCompositeChangeListener> listeners =
+ new LinkedHashSet<PropertyCompositeChangeListener>();
+
+ public AbstractPropertiesComposite(Composite parent)
+ {
+ super(parent, SWT.NONE);
+ }
+
+ /**
+ * Adds the given listener to the list of registered listeners of this composite's changes.
+ *
+ * @param listener the listener to be registered
+ */
+ public static void addCompositeChangeListener(PropertyCompositeChangeListener listener)
+ {
+ listeners.add(listener);
+ }
+
+ /**
+ * Removes the given listener from the list of registered listeners of this composite's changes.
+ *
+ * @param listener the listener to be unregistered
+ */
+ public static void removeCompositeChangeListener(PropertyCompositeChangeListener listener)
+ {
+ listeners.remove(listener);
+ }
+
+ /**
+ * Notifies an event of change on the composite to all registered listeners.
+ *
+ * This method must be called by extending classes whenever a change is made to them
+ * that registered listeners should be aware of (examples: typed text, button press, etc)
+ */
+ protected void notifyCompositeChangeListeners()
+ {
+ PropertyCompositeChangeEvent event = new PropertyCompositeChangeEvent(this);
+
+ for (PropertyCompositeChangeListener listener : listeners)
+ {
+ listener.compositeChanged(event);
+ }
+ }
+
+ /**
+ * Given a collection of controls, turn all their enabled status to
+ * the one provided
+ *
+ * @param enabled True to enable all controls in the collection
+ * @param controlsToUpdate A collection of all controls to apply the state provided by enabled parameter
+ */
+ protected void updateWidgetEnableStatus(boolean enabled, Collection<Control> controlsToUpdate)
+ {
+ for (Control control : controlsToUpdate)
+ {
+ if (!control.isDisposed())
+ {
+ control.setEnabled(enabled);
+ }
+ }
+ }
+
+ /**
+ * Sets the main layout to this composite as a GridLayout with the
+ * given number of columns and with columns not the same width.
+ *
+ * @param numColumns the number of columns for the GridLayout
+ */
+ protected final void setMainLayout(int numColumns)
+ {
+ GridLayout mainLayout = new GridLayout(numColumns, false);
+ mainLayout.marginWidth = DEFAULT_MARGIN_SIZE;
+ mainLayout.marginHeight = DEFAULT_MARGIN_SIZE;
+ this.setLayout(mainLayout);
+ }
+
+ /**
+ * Declaration of the abstract getErrorMessage() method.
+ * Retrieves the error message associated with the current extending composite
+ * state.
+ * If no error is found with the current state, <code>null</code> <b>must</b>
+ * be returned by the extending composite.
+ *
+ * @return the error message, or <code>null</code> if there are no errors
+ */
+ public abstract String getErrorMessage();
+
+}
diff --git a/src/plugins/android/src/com/motorola/studio/android/wizards/monkey/DeviceSelectionDialog.java b/src/plugins/android/src/com/motorola/studio/android/wizards/monkey/DeviceSelectionDialog.java
new file mode 100644
index 0000000..31bbe9c
--- /dev/null
+++ b/src/plugins/android/src/com/motorola/studio/android/wizards/monkey/DeviceSelectionDialog.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.wizards.monkey;
+
+import java.util.Collection;
+import java.util.LinkedList;
+
+import org.eclipse.jface.viewers.LabelProvider;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.dialogs.ElementListSelectionDialog;
+
+import com.motorola.studio.android.AndroidPlugin;
+import com.motorola.studio.android.adt.DDMSFacade;
+import com.motorola.studio.android.adt.ISerialNumbered;
+import com.motorola.studio.android.devices.DevicesManager;
+import com.motorola.studio.android.i18n.AndroidNLS;
+
+/**
+ * DESCRIPTION:
+ * <br>
+ * This class implements the device selection dialog
+ *<br>
+ * RESPONSIBILITY:
+ * <br>
+ * Provides a dialog populated with the available device instances
+ *<br>
+ * COLABORATORS:
+ * <br>
+ * None.
+ *<br>
+ * USAGE:
+ * <br>
+ * This class is intended to be used by Eclipse only
+ */
+public class DeviceSelectionDialog extends ElementListSelectionDialog
+{
+ private static final String DEV_SELECTION_CONTEXT_HELP_ID = AndroidPlugin.PLUGIN_ID
+ + ".deviceSelectionDialog";
+
+ /**
+ * Default constructor
+ *
+ * @param parent Parent shell
+ * @param description Dialog description
+ */
+ public DeviceSelectionDialog(Shell parent, String description)
+ {
+ super(parent, new LabelProvider()
+ {
+ @Override
+ public String getText(Object element)
+ {
+ String result = "";
+ if (element instanceof ISerialNumbered)
+ {
+ ISerialNumbered serialNumbered = (ISerialNumbered) element;
+ result = serialNumbered.getFullName();
+ }
+ return result;
+ }
+
+ });
+
+ this.setTitle(AndroidNLS.UI_MonkeyComposite_SelectDeviceScreenTitle);
+ this.setMessage(description);
+
+ Collection<ISerialNumbered> instances = DevicesManager.getInstance().getAllDevices();
+
+ if ((instances != null) && (instances.size() > 0))
+ {
+
+ Collection<ISerialNumbered> filteredInstances = new LinkedList<ISerialNumbered>();
+
+ for (ISerialNumbered instance : instances)
+ {
+ if (DDMSFacade.isDeviceOnline(instance.getSerialNumber()))
+ {
+ filteredInstances.add(instance);
+ }
+ }
+ Object[] filteredInstancesArray = filteredInstances.toArray();
+ this.setElements(filteredInstancesArray);
+ }
+
+ this.setHelpAvailable(true);
+ PlatformUI.getWorkbench().getHelpSystem().setHelp(parent, DEV_SELECTION_CONTEXT_HELP_ID);
+ }
+}
diff --git a/src/plugins/android/src/com/motorola/studio/android/wizards/monkey/IMonkeyConfigurationConstants.java b/src/plugins/android/src/com/motorola/studio/android/wizards/monkey/IMonkeyConfigurationConstants.java
new file mode 100644
index 0000000..cf1a4b6
--- /dev/null
+++ b/src/plugins/android/src/com/motorola/studio/android/wizards/monkey/IMonkeyConfigurationConstants.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.wizards.monkey;
+
+/**
+ * This interface holds the constants for MonkeyConfiguration
+ */
+
+public interface IMonkeyConfigurationConstants
+{
+
+ public final static String LAUNCH_CONFIGURATION_TYPE_EXTENSION_ID =
+ "monkeyLaunchConfigurationType";
+
+ public final static String MOTODEV_APP_ICO = "icons/monkey/motodevapp.gif";
+
+ public final static String DEFAULT_VALUE = "";
+
+ public final static String DEFAULT_COUNT_VALUE = "50";
+
+ public final static String DEFAULT_VERBOSE_VALUE = "-v";
+
+ public final static boolean DEFAULT_BOOL_VALUE = false;
+
+ public final static String ATTR_DEVICE_INSTANCE_NAME =
+ "com.motorola.studio.android.monkey.instanceName";
+
+ public final static String ANDROID_CONSOLE_ID = "Android";
+
+ public static final String ATTR_EVENT_COUNT_NAME = "";
+
+ public static final String ATTR_OTHER_CMDS = "com.motorola.studio.android.monkey.otherCmds";
+
+ public static final String ATTR_SELECTED_PACKAGES =
+ "com.motorola.studio.android.monkey.selectedPackages";
+
+ public static final String NEW_CONFIGURATION_NAME = "New_configuration";
+
+}
diff --git a/src/plugins/android/src/com/motorola/studio/android/wizards/monkey/MonkeyConfigurationDelegate.java b/src/plugins/android/src/com/motorola/studio/android/wizards/monkey/MonkeyConfigurationDelegate.java
new file mode 100644
index 0000000..df24395
--- /dev/null
+++ b/src/plugins/android/src/com/motorola/studio/android/wizards/monkey/MonkeyConfigurationDelegate.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.wizards.monkey;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.debug.core.ILaunch;
+import org.eclipse.debug.core.ILaunchConfiguration;
+import org.eclipse.debug.core.model.ILaunchConfigurationDelegate;
+
+import com.motorola.studio.android.adt.DDMSUtils;
+import com.motorola.studio.android.adt.ISerialNumbered;
+import com.motorola.studio.android.devices.DevicesManager;
+
+/**
+ * Performs launch for a Monkey Launch Configuration.
+ */
+public class MonkeyConfigurationDelegate implements ILaunchConfigurationDelegate
+{
+
+ public void launch(ILaunchConfiguration configuration, String mode, ILaunch launch,
+ IProgressMonitor monitor) throws CoreException
+ {
+ String deviceName =
+ configuration.getAttribute(IMonkeyConfigurationConstants.ATTR_DEVICE_INSTANCE_NAME,
+ (String) null);
+ ISerialNumbered serialNumber = DevicesManager.getInstance().getDeviceByName(deviceName);
+
+ String otherCmds =
+ configuration.getAttribute(IMonkeyConfigurationConstants.ATTR_OTHER_CMDS,
+ (String) null)
+ + " "
+ + configuration.getAttribute(
+ IMonkeyConfigurationConstants.ATTR_EVENT_COUNT_NAME, (String) null);
+
+ if (serialNumber != null)
+ {
+
+ ArrayList<String> t = new ArrayList<String>();
+
+ List<?> c =
+ configuration.getAttribute(
+ IMonkeyConfigurationConstants.ATTR_SELECTED_PACKAGES, (List<?>) null);
+ if (c != null)
+ {
+ for (int i = 0; i < c.size(); i++)
+ {
+ t.add((String) c.get(i));
+ }
+ }
+
+ DDMSUtils.runMonkey(serialNumber.getSerialNumber(), t, otherCmds);
+
+ }
+ }
+
+}
diff --git a/src/plugins/android/src/com/motorola/studio/android/wizards/monkey/MonkeyConfigurationOtherCmdsTab.java b/src/plugins/android/src/com/motorola/studio/android/wizards/monkey/MonkeyConfigurationOtherCmdsTab.java
new file mode 100644
index 0000000..89d0e01
--- /dev/null
+++ b/src/plugins/android/src/com/motorola/studio/android/wizards/monkey/MonkeyConfigurationOtherCmdsTab.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.wizards.monkey;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.debug.core.ILaunchConfiguration;
+import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
+import org.eclipse.debug.internal.ui.IInternalDebugUIConstants;
+import org.eclipse.debug.ui.AbstractLaunchConfigurationTab;
+import org.eclipse.debug.ui.DebugUITools;
+import org.eclipse.debug.ui.ILaunchConfigurationTab;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.i18n.AndroidNLS;
+import com.motorola.studio.android.monkey.options.MonkeyOptionsMgt;
+import com.motorola.studio.android.nativeos.NativeUIUtils;
+import com.motorola.studio.android.wizards.monkey.AbstractPropertiesComposite.PropertyCompositeChangeEvent;
+import com.motorola.studio.android.wizards.monkey.AbstractPropertiesComposite.PropertyCompositeChangeListener;
+
+/**
+ * This class implements the Options tab of the Monkey Launch Configuration.
+ */
+public class MonkeyConfigurationOtherCmdsTab extends AbstractLaunchConfigurationTab
+{
+
+ private MonkeyOptionsComposite monkeyOptionsComposite;
+
+ // handle changes
+ private final PropertyCompositeChangeListener compositeChangeListener =
+ new PropertyCompositeChangeListener()
+ {
+ public void compositeChanged(PropertyCompositeChangeEvent e)
+ {
+ String errorMessage = monkeyOptionsComposite.getErrorMessage();
+ if (errorMessage != null)
+ {
+ setErrorMessage(errorMessage);
+ updateLaunchConfigurationDialog();
+ }
+ else
+ {
+ setErrorMessage(null);
+ updateLaunchConfigurationDialog();
+ }
+ }
+ };
+
+ public void createControl(Composite parent)
+ {
+ setErrorMessage(null);
+ setMessage(AndroidNLS.UI_MonkeyWizardOptionsPage_PageMessage);
+
+ // Define layout
+ GridLayout mainLayout = new GridLayout(1, false);
+ mainLayout.marginTop = 5;
+ mainLayout.marginWidth = 5;
+ mainLayout.marginHeight = 5;
+
+ // Create Monkey Options area
+ monkeyOptionsComposite =
+ new MonkeyOptionsComposite(parent, NativeUIUtils.getDefaultCommandLine());
+
+ AbstractPropertiesComposite.addCompositeChangeListener(compositeChangeListener);
+
+ // Set layout
+ monkeyOptionsComposite.setLayout(mainLayout);
+
+ setControl(monkeyOptionsComposite);
+ }
+
+ public String getName()
+ {
+ return AndroidNLS.UI_MonkeyComposite_TabOtherCmdName;
+ }
+
+ public void initializeFrom(ILaunchConfiguration configuration)
+ {
+ String otherCmds = "";
+ try
+ {
+ otherCmds =
+ configuration.getAttribute(IMonkeyConfigurationConstants.ATTR_OTHER_CMDS,
+ IMonkeyConfigurationConstants.DEFAULT_VERBOSE_VALUE);
+ monkeyOptionsComposite.reloadValues(otherCmds);
+ }
+ catch (CoreException e)
+ {
+ StudioLogger
+ .error(MonkeyConfigurationOtherCmdsTab.class,
+ "Failed to initialize Monkey Launch Configuration Other Cmds:"
+ + e.getMessage());
+ }
+
+ }
+
+ public void performApply(ILaunchConfigurationWorkingCopy configuration)
+ {
+ configuration.setAttribute(IMonkeyConfigurationConstants.ATTR_OTHER_CMDS,
+ MonkeyOptionsMgt.getParamList());
+
+ }
+
+ public void setDefaults(ILaunchConfigurationWorkingCopy configuration)
+ {
+ //do nothing
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.debug.ui.ILaunchConfigurationTab#getImage()
+ */
+ @Override
+ public Image getImage()
+ {
+ return DebugUITools.getImage(IInternalDebugUIConstants.IMG_OBJS_COMMON_TAB);
+ }
+
+ /**
+ * @see ILaunchConfigurationTab#isValid(ILaunchConfiguration)
+ */
+ @Override
+ public boolean isValid(ILaunchConfiguration launchConfig)
+ {
+
+ boolean isValid;
+ String errorMessage = monkeyOptionsComposite.getErrorMessage();
+ if (errorMessage != null)
+ {
+ setErrorMessage(errorMessage);
+ isValid = false;
+ }
+ else
+ {
+ setErrorMessage(null);
+ isValid = true;
+ }
+ return isValid;
+ }
+
+ @Override
+ public void dispose()
+ {
+ AbstractPropertiesComposite.removeCompositeChangeListener(compositeChangeListener);
+ super.dispose();
+ }
+
+}
diff --git a/src/plugins/android/src/com/motorola/studio/android/wizards/monkey/MonkeyConfigurationTab.java b/src/plugins/android/src/com/motorola/studio/android/wizards/monkey/MonkeyConfigurationTab.java
new file mode 100644
index 0000000..fb169d8
--- /dev/null
+++ b/src/plugins/android/src/com/motorola/studio/android/wizards/monkey/MonkeyConfigurationTab.java
@@ -0,0 +1,666 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.wizards.monkey;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.debug.core.ILaunchConfiguration;
+import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
+import org.eclipse.debug.ui.AbstractLaunchConfigurationTab;
+import org.eclipse.debug.ui.ILaunchConfigurationTab;
+import org.eclipse.jface.dialogs.IDialogConstants;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.sequoyah.device.framework.events.IInstanceListener;
+import org.eclipse.sequoyah.device.framework.events.InstanceEvent;
+import org.eclipse.sequoyah.device.framework.events.InstanceEventManager;
+import org.eclipse.sequoyah.device.framework.model.IInstance;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.ModifyEvent;
+import org.eclipse.swt.events.ModifyListener;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.graphics.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.Event;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.TableColumn;
+import org.eclipse.swt.widgets.TableItem;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.dialogs.ISelectionStatusValidator;
+import org.eclipse.ui.plugin.AbstractUIPlugin;
+
+import com.motorola.studio.android.AndroidPlugin;
+import com.motorola.studio.android.adt.DDMSFacade;
+import com.motorola.studio.android.adt.ISerialNumbered;
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.devices.DevicesManager;
+import com.motorola.studio.android.i18n.AndroidNLS;
+import com.motorola.studio.android.wizards.elements.sorting.TableItemSortStringSetActionListener;
+
+/**
+ * Implements the Main tab of the Monkey Launch Configuration.
+ */
+public class MonkeyConfigurationTab extends AbstractLaunchConfigurationTab
+{
+ private MonkeyConfigurationTabTable packageTable;
+
+ private Map<String, String> availablePackages = null;
+
+ private static final String contextId = AndroidPlugin.PLUGIN_ID + ".monkey";
+
+ private final boolean filterSystem;
+
+ private static final Object UPDATE_WIDGETS_EVENT = new Object();
+
+ private Text countCombo = null;
+
+ private String deviceName = "";
+
+ private String eventCount = "";
+
+ private Button deviceNameBrowseButton = null;
+
+ private final Button defaultLauncherButton = null;
+
+ private Composite mainComposite;
+
+ private List<?> selectedPackagesPreference = null;
+
+ /**
+ * Determines whether the filter is active.
+ *
+ * @return Returns whether the filter is active.
+ */
+ boolean isFilterSystem()
+ {
+ return filterSystem;
+ }
+
+ /**
+ * Get the list of selected packages preference.
+ *
+ * @return Returns the list of selected packages preference.
+ */
+ List<?> getSelectedPackagesPreference()
+ {
+ return selectedPackagesPreference;
+ }
+
+ /**
+ * Call the method {@link #updateLaunchConfigurationDialog}.
+ */
+ void callUpdateLaunchConfigurationDialog()
+ {
+ updateLaunchConfigurationDialog();
+ }
+
+ private boolean firstTime = false;
+
+ /*
+ * This listener follows changes made on devices and fires updates.
+ */
+ private final IInstanceListener instanceListener = new IInstanceListener()
+ {
+
+ private void fireUpdate()
+ {
+ PlatformUI.getWorkbench().getDisplay().syncExec(new Runnable()
+ {
+
+ public void run()
+ {
+ updateDeviceChooserButton();
+ updateLaunchConfigurationDialog();
+ }
+ });
+ }
+
+ public void instanceUpdated(InstanceEvent instanceevent)
+ {
+ fireUpdate();
+ }
+
+ public void instanceUnloaded(InstanceEvent instanceevent)
+ {
+ fireUpdate();
+ }
+
+ public void instanceTransitioned(InstanceEvent instanceevent)
+ {
+ fireUpdate();
+ }
+
+ public void instanceLoaded(InstanceEvent instanceevent)
+ {
+ fireUpdate();
+ }
+
+ public void instanceDeleted(InstanceEvent instanceevent)
+ {
+ fireUpdate();
+ }
+
+ public void instanceCreated(InstanceEvent instanceevent)
+ {
+ fireUpdate();
+ }
+
+ public void instanceAboutToTransition(InstanceEvent instanceevent)
+ {
+ fireUpdate();
+ }
+ };
+
+ private void updateDeviceChooserButton()
+ {
+ // button is always enabled
+ deviceNameBrowseButton.setEnabled(true);
+
+ }
+
+ public MonkeyConfigurationTab(boolean filterSystem)
+ {
+ this.filterSystem = filterSystem;
+ }
+
+ public void createControl(Composite parent)
+ {
+ Composite mainComposite = new Composite(parent, SWT.FILL);
+
+ this.mainComposite = mainComposite;
+
+ mainComposite.setLayout(new GridLayout(3, false));
+ /*
+ * Device
+ */
+ // Device Name Label
+ Label deviceNameLabel = new Label(mainComposite, SWT.NONE);
+ deviceNameLabel.setText(AndroidNLS.UI_MonkeyComposite_DeviceNameLabel);
+ GridData deviceGridData = new GridData(SWT.LEFT, SWT.CENTER, false, false);
+ deviceNameLabel.setLayoutData(deviceGridData);
+
+ // Device Name Text
+ final Text deviceNameText = new Text(mainComposite, SWT.SINGLE | SWT.BORDER);
+ deviceNameText.setText(deviceName);
+ deviceGridData = new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1);
+ deviceNameText.setLayoutData(deviceGridData);
+
+ addDeviceNameTextListeners(deviceNameText);
+
+ // Device Name Browse Button
+ deviceNameBrowseButton = new Button(mainComposite, SWT.PUSH);
+ deviceGridData = new GridData(SWT.FILL, SWT.CENTER, false, false, 1, 1);
+ deviceNameBrowseButton.setLayoutData(deviceGridData);
+ deviceNameBrowseButton.setText(AndroidNLS.UI_General_BrowseButtonLabel);
+ deviceNameBrowseButton.addSelectionListener(new SelectionAdapter()
+ {
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.swt.events.SelectionAdapter#widgetSelected(org.eclipse.swt.events.SelectionEvent)
+ */
+ @Override
+ public void widgetSelected(SelectionEvent e)
+ {
+ DeviceSelectionDialog dialog =
+ new DeviceSelectionDialog(getShell(),
+ AndroidNLS.UI_MonkeyComposite_SelectDeviceScreenMessage);
+ dialog.setTitle(AndroidNLS.UI_MonkeyComposite_SelectDeviceScreenTitle);
+ dialog.setMultipleSelection(false);
+ dialog.setValidator(new ISelectionStatusValidator()
+ {
+
+ public IStatus validate(Object[] selection)
+ {
+ IStatus status = new Status(IStatus.OK, AndroidPlugin.PLUGIN_ID, "");
+ if (selection.length == 0)
+ {
+ status =
+ new Status(IStatus.ERROR, AndroidPlugin.PLUGIN_ID,
+ "No selected instance");
+ }
+ return status;
+ }
+ });
+ int res = dialog.open();
+ if (res == IDialogConstants.OK_ID)
+ {
+ ISerialNumbered serialNumbered = (ISerialNumbered) dialog.getFirstResult();
+ String selectedDevice = ((IInstance) serialNumbered).getName();
+ packageTable.removeAllTableItems();
+ deviceNameText.setText(selectedDevice);
+ }
+ }
+
+ });
+
+ InstanceEventManager.getInstance().addInstanceListener(instanceListener);
+ /*
+ *Device end
+ */
+ packageTable =
+ new MonkeyConfigurationTabTable(mainComposite, SWT.BORDER | SWT.MULTI
+ | SWT.FULL_SELECTION,
+ AndroidNLS.UninstallAppWizardPage_Loading_Applications, this);
+ packageTable.setTableHeaderVisible(true);
+ GridData layoutData = new GridData(SWT.FILL, SWT.FILL, true, true, 3, 1);
+ packageTable.setLayoutData(layoutData);
+ TableColumn packageNameColumn = packageTable.addTableColumn(SWT.CENTER);
+ TableColumn isSystemColumn = packageTable.addTableColumn(SWT.CENTER);
+ packageNameColumn.setText(AndroidNLS.UninstallAppWizardPage_ColumnPackageName);
+ isSystemColumn.setText(AndroidNLS.UninstallAppWizardPage_ColumnPackageKiind);
+ packageNameColumn.setWidth(200);
+ isSystemColumn.setWidth(200);
+ packageNameColumn.addSelectionListener(new TableItemSortStringSetActionListener());
+ isSystemColumn.addSelectionListener(new TableItemSortStringSetActionListener());
+
+ packageTable.setTableLinesVisible(false);
+ packageTable.pack();
+
+ packageTable.redraw();
+ packageTable.addTableSelectionListener(new SelectionListener()
+ {
+
+ public void widgetSelected(SelectionEvent e)
+ {
+ firstTime = false;
+
+ if ((packageTable != null)
+ && (packageTable.getTableItems().length > 0)
+ && (!packageTable.getTableItem(0).getText(0)
+ .contains(AndroidNLS.UninstallAppWizardPage_Loading_Applications)))
+ {
+ selectedPackagesPreference = getPackageList();
+ updateLaunchConfigurationDialog();
+ }
+ }
+
+ public void widgetDefaultSelected(SelectionEvent e)
+ {
+ //do nothing
+ }
+ });
+
+ layoutData = new GridData(SWT.NONE, SWT.CENTER, false, false, 1, 1);
+ layoutData.verticalIndent = 3;
+
+ Label label = new Label(mainComposite, SWT.LEFT);
+ label.setText(AndroidNLS.MonkeyWizardPage_CountCommand);
+ label.setLayoutData(layoutData);
+
+ layoutData = new GridData(SWT.FILL, SWT.FILL, true, false, 2, 1);
+ layoutData.verticalIndent = 3;
+ countCombo = new Text(mainComposite, SWT.SINGLE | SWT.WRAP | SWT.BORDER);
+
+ countCombo.addModifyListener(new ModifyListener()
+ {
+ public void modifyText(ModifyEvent e)
+ {
+ if (e.data == UPDATE_WIDGETS_EVENT)
+ {
+ countCombo.setText(eventCount);
+ }
+ else
+ {
+ eventCount = countCombo.getText();
+ updateLaunchConfigurationDialog();
+ }
+ }
+
+ });
+ countCombo.setLayoutData(layoutData);
+
+ mainComposite.addListener(SWT.Modify, new Listener()
+ {
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.swt.widgets.Listener#handleEvent(org.eclipse.swt.widgets.Event)
+ */
+ public void handleEvent(Event e)
+ {
+
+ countCombo.notifyListeners(SWT.Modify, e);
+
+ deviceNameText.notifyListeners(SWT.Modify, e);
+
+ if (defaultLauncherButton != null)
+ {
+ defaultLauncherButton.notifyListeners(SWT.Selection, e);
+ }
+
+ }
+ });
+
+ PlatformUI.getWorkbench().getHelpSystem().setHelp(mainComposite, contextId);
+ setControl(mainComposite);
+
+ }
+
+ /**
+ * Add a listener to the deviceNameText field.
+ * @param deviceNameText
+ */
+ private void addDeviceNameTextListeners(final Text deviceNameText)
+ {
+ deviceNameText.addModifyListener(new ModifyListener()
+ {
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.swt.events.ModifyListener#modifyText(org.eclipse.swt.events.ModifyEvent)
+ */
+ public void modifyText(ModifyEvent e)
+ {
+ if (e.data == UPDATE_WIDGETS_EVENT)
+ {
+ //handling when pressing Revert button
+ if ((deviceName != null) && (!deviceName.equals(deviceNameText.getText())))
+ {
+ firstTime = true;
+ }
+ deviceNameText.setText(deviceName);
+ }
+ else
+ {
+ if (!deviceName.equals(deviceNameText.getText()) || (firstTime))
+ {
+ deviceName = deviceNameText.getText();
+ packageTable.removeAllTableItems();
+ if (!deviceName.equals(""))
+ {
+ DevicesManager deviceManager = DevicesManager.getInstance();
+ if (deviceManager != null)
+ {
+ ISerialNumbered serialNumbered =
+ deviceManager.getDeviceByName(deviceName);
+ if (serialNumbered != null)
+ {
+ final String serialNumber = serialNumbered.getSerialNumber();
+ if (serialNumber != null)
+ {
+ // initiating Loading thread
+ packageTable.removeAllTableItems();
+ if (availablePackages != null)
+ {
+ availablePackages.clear();
+ }
+ // call service to populate the table
+ packageTable.populateTableAsynchronously(serialNumber);
+ }
+ }
+ }
+ }
+ }
+ updateLaunchConfigurationDialog();
+ }
+
+ }
+
+ });
+ }
+
+ /**
+ * Verify if the deviceName is a valid online device instance.
+ * @param deviceName
+ * @return true if the deviceName is a valid instance.
+ */
+ private boolean validDevice(String deviceName)
+ {
+ boolean valid = false;
+ ISerialNumbered sequoyahInstance = DevicesManager.getInstance().getDeviceByName(deviceName);
+ if ((sequoyahInstance != null)
+ && DDMSFacade.isDeviceOnline(sequoyahInstance.getSerialNumber()))
+ {
+ valid = true;
+ }
+ return valid;
+ }
+
+ /**
+ * Verify if all required fields are fulfilled with valid values.
+ * @return true if the required fields are correctly fulfilled.
+ */
+ private boolean validatePage()
+ {
+ boolean complete = true;
+
+ setErrorMessage(null);
+
+ setMessage(null);
+
+ if ((!deviceName.equals("")) && (!validDevice(deviceName)))
+ {
+ setErrorMessage(AndroidNLS.ERR_MonkeyWizardPage_Device);
+ packageTable.removeAllTableItems();
+ complete = false;
+ }
+ if ((complete) && (!countCombo.getText().equals("")))
+ {
+ try
+ {
+ int i = Integer.parseInt(countCombo.getText().toString());
+
+ if (i <= 0)
+ {
+ complete = false;
+ }
+
+ }
+ catch (Exception e)
+ {
+ complete = false;
+ }
+ if (!complete)
+ {
+ String msg =
+ NLS.bind(
+ AndroidNLS.ERR_PropertiesMainComposite_Monkey_NumberMustBePositiveInteger,
+ AndroidNLS.MonkeyWizardPage_CountCommand);
+ setErrorMessage(msg);
+ }
+ }
+ if (complete)
+ {
+ if (deviceName.equals(""))
+ {
+ setMessage(AndroidNLS.UI_MonkeyComposite_SelectDeviceScreenMessage);
+ complete = false;
+ }
+ else if ((packageTable != null) && (packageTable.getTableSelectionCount() <= 0))
+ {
+ setMessage(AndroidNLS.ERR_MonkeyWizardPage_Package);
+ complete = false;
+ }
+ else if (countCombo.getText().equals(""))
+ {
+ setMessage(AndroidNLS.ERR_MonkeyWizardPage_CountCommand);
+ complete = false;
+ }
+ }
+
+ return complete;
+ }
+
+ /**
+ * get the value for the event count
+ * @return
+ */
+ public String getCount()
+ {
+ return countCombo.getText().toString();
+ }
+
+ /**
+ * get the list of selected packages to run monkey
+ * @return
+ */
+ public List<String> getPackageList()
+ {
+ List<String> selectedPackages = new ArrayList<String>();
+ if (packageTable != null)
+ {
+ for (TableItem item : packageTable.getTableSelection())
+ {
+ selectedPackages.add(item.getText(0));
+ }
+ }
+
+ return selectedPackages;
+ }
+
+ public String getName()
+ {
+ return AndroidNLS.UI_MonkeyComposite_TabMainName;
+ }
+
+ /**
+ * @see org.eclipse.debug.ui.ILaunchConfigurationTab#getImage()
+ */
+ @Override
+ public Image getImage()
+ {
+ return AbstractUIPlugin.imageDescriptorFromPlugin(AndroidPlugin.PLUGIN_ID,
+ IMonkeyConfigurationConstants.MOTODEV_APP_ICO).createImage();
+ }
+
+ @Override
+ public void dispose()
+ {
+ InstanceEventManager.getInstance().removeInstanceListener(instanceListener);
+
+ super.dispose();
+ }
+
+ public void initializeFrom(ILaunchConfiguration configuration)
+ {
+
+ try
+ {
+
+ if (deviceName.equals(""))
+ {
+ firstTime = true;
+ }
+ else
+ {
+ firstTime = false;
+ }
+ deviceName =
+ configuration.getAttribute(
+ IMonkeyConfigurationConstants.ATTR_DEVICE_INSTANCE_NAME,
+ IMonkeyConfigurationConstants.DEFAULT_VALUE);
+
+ eventCount =
+ configuration.getAttribute(IMonkeyConfigurationConstants.ATTR_EVENT_COUNT_NAME,
+ IMonkeyConfigurationConstants.DEFAULT_COUNT_VALUE);
+
+ selectedPackagesPreference =
+ configuration.getAttribute(
+ IMonkeyConfigurationConstants.ATTR_SELECTED_PACKAGES, (List<?>) null);
+
+ // Handling Revert button effect on the list of packages
+ if (((packageTable != null) && (packageTable.getTableItems().length > 0) && (!packageTable
+ .getTableItem(0).getText(0)
+ .contains(AndroidNLS.UninstallAppWizardPage_Loading_Applications))))
+ {
+ List<?> selectedPackages = getPackageList();
+ if ((selectedPackagesPreference == null)
+ || (!selectedPackagesPreference.containsAll(selectedPackages)))
+ {
+ packageTable.deselectAllTableItems();
+ if (selectedPackagesPreference != null)
+ {
+ TableItem[] itemsP = new TableItem[selectedPackagesPreference.size()];
+ int i = 0;
+ for (TableItem item : packageTable.getTableItems())
+ {
+ if (selectedPackagesPreference.contains(item.getText(0)))
+ {
+ itemsP[i] = item;
+ i++;
+ }
+ }
+ packageTable.setTableSelection(itemsP);
+ }
+ }
+ }
+
+ Event e = new Event();
+ e.type = SWT.Modify;
+ e.data = UPDATE_WIDGETS_EVENT;
+ mainComposite.notifyListeners(SWT.Modify, e);
+ }
+ catch (CoreException e)
+ {
+ StudioLogger.error(MonkeyConfigurationTab.class,
+ "Failed to initialize Monkey Launch Configuration:" + e.getMessage());
+ }
+
+ }
+
+ public void performApply(ILaunchConfigurationWorkingCopy configuration)
+ {
+
+ configuration.setAttribute(IMonkeyConfigurationConstants.ATTR_DEVICE_INSTANCE_NAME,
+ deviceName);
+
+ configuration.setAttribute(IMonkeyConfigurationConstants.ATTR_EVENT_COUNT_NAME, eventCount);
+
+ configuration.setAttribute(IMonkeyConfigurationConstants.ATTR_SELECTED_PACKAGES,
+ selectedPackagesPreference);
+
+ }
+
+ public void setDefaults(ILaunchConfigurationWorkingCopy configuration)
+ {
+
+ configuration.setAttribute(IMonkeyConfigurationConstants.ATTR_DEVICE_INSTANCE_NAME,
+ IMonkeyConfigurationConstants.DEFAULT_VALUE);
+
+ configuration.setAttribute(IMonkeyConfigurationConstants.ATTR_EVENT_COUNT_NAME,
+ IMonkeyConfigurationConstants.DEFAULT_COUNT_VALUE);
+ configuration.setAttribute(IMonkeyConfigurationConstants.ATTR_SELECTED_PACKAGES,
+ (List<?>) null);
+
+ if (mainComposite != null)
+ {
+ Event e = new Event();
+ e.type = SWT.Modify;
+ e.data = UPDATE_WIDGETS_EVENT;
+ mainComposite.notifyListeners(SWT.Modify, e);
+ }
+
+ }
+
+ /**
+ * @see ILaunchConfigurationTab#isValid(ILaunchConfiguration)
+ */
+ @Override
+ public boolean isValid(ILaunchConfiguration launchConfig)
+ {
+ return validatePage();
+ }
+} \ No newline at end of file
diff --git a/src/plugins/android/src/com/motorola/studio/android/wizards/monkey/MonkeyConfigurationTabGroup.java b/src/plugins/android/src/com/motorola/studio/android/wizards/monkey/MonkeyConfigurationTabGroup.java
new file mode 100644
index 0000000..d5c0bf3
--- /dev/null
+++ b/src/plugins/android/src/com/motorola/studio/android/wizards/monkey/MonkeyConfigurationTabGroup.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.wizards.monkey;
+
+import org.eclipse.debug.ui.AbstractLaunchConfigurationTabGroup;
+import org.eclipse.debug.ui.ILaunchConfigurationDialog;
+import org.eclipse.debug.ui.ILaunchConfigurationTab;
+
+/**
+ * Implements a Monkey launch configuration tab group.
+ */
+public class MonkeyConfigurationTabGroup extends AbstractLaunchConfigurationTabGroup
+{
+
+ public void createTabs(ILaunchConfigurationDialog dialog, String mode)
+ {
+
+ ILaunchConfigurationTab mainLaunchTab = new MonkeyConfigurationTab(false);
+
+ // Main and Options monkey launch configuration tabs
+ setTabs(new ILaunchConfigurationTab[]
+ {
+ mainLaunchTab, new MonkeyConfigurationOtherCmdsTab()
+ });
+
+ }
+
+}
diff --git a/src/plugins/android/src/com/motorola/studio/android/wizards/monkey/MonkeyConfigurationTabTable.java b/src/plugins/android/src/com/motorola/studio/android/wizards/monkey/MonkeyConfigurationTabTable.java
new file mode 100644
index 0000000..81359bb
--- /dev/null
+++ b/src/plugins/android/src/com/motorola/studio/android/wizards/monkey/MonkeyConfigurationTabTable.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.wizards.monkey;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.swt.widgets.TableItem;
+
+import com.motorola.studio.android.adt.DDMSUtils;
+import com.motorola.studio.android.i18n.AndroidNLS;
+import com.motorola.studio.android.wizards.elements.TableWithLoadingInfo;
+
+/**
+ * Table with loading info for monkey.
+ */
+public class MonkeyConfigurationTabTable extends
+ TableWithLoadingInfo<MonkeyConfigurationTab, Map<String, String>, String>
+{
+
+ /**
+ * @see TableWithLoadingInfo#TableWithLoadingInfo(Composite, int, String, Object)
+ */
+ public MonkeyConfigurationTabTable(Composite parent, int style, String animatedTextLabel,
+ MonkeyConfigurationTab callingPage)
+ {
+ super(parent, style, animatedTextLabel, callingPage);
+ }
+
+ /**
+ * Populates the table with available packages.
+ *
+ * @see com.motorola.studio.android.wizards.elements.TableWithLoadingInfo#addTableData(Object)
+ */
+ @Override
+ protected void addTableData(Map<String, String> elementList)
+ {
+ // retrieve the available packages
+ Map<String, String> availablePackages = elementList;
+ // retrieve the monkey wizard page
+ MonkeyConfigurationTab monkeyWizardPage = getCallingPage();
+ // get the list of selected packages preference
+ List<?> selectedPackagesPreference = monkeyWizardPage.getSelectedPackagesPreference();
+ // get the table to be populated
+ Table table = getTable();
+ // iterate through the available packages
+ String packageName = null;
+ String packagePath = null;
+ Iterator<String> it = availablePackages.keySet().iterator();
+ while (it.hasNext())
+ {
+ // the the package name and path
+ packageName = it.next();
+ packagePath = availablePackages.get(packageName);
+ if (!monkeyWizardPage.isFilterSystem() || !packagePath.toLowerCase().contains("system"))
+ {
+ // add data
+ TableItem item = new TableItem(table, SWT.NONE);
+ item.setText(0, packageName);
+ item.setText(1, packagePath.contains("system")
+ ? AndroidNLS.UninstallAppWizardPage_SystemLabel
+ : AndroidNLS.UninstallAppWizardPage_UserLabel);
+ if (selectedPackagesPreference != null)
+ {
+ if (selectedPackagesPreference.contains(packageName))
+ {
+ item.setChecked(true);
+ getTable().select(table.getItemCount() - 1);
+ }
+ }
+
+ }
+ }
+ }
+
+ /**
+ * Get the List of installed packages from the device based on its serial number.
+ *
+ * @see com.motorola.studio.android.wizards.elements.TableWithLoadingInfo#callServiceForRetrievingDataToPopulateTable(java.lang.Object)
+ */
+ @Override
+ protected Map<String, String> callServiceForRetrievingDataToPopulateTable(String serialNumber)
+ {
+ // installed packages to be returned
+ Map<String, String> installedPackages = null;
+ // based on the serial number, the the installed packages
+ if (serialNumber != null)
+ {
+ try
+ {
+ installedPackages = DDMSUtils.listInstalledPackages(serialNumber);
+ }
+ catch (IOException e)
+ {
+ installedPackages = new HashMap<String, String>(0);
+ }
+ }
+ return installedPackages;
+ }
+
+ /**
+ * Update the Launching configuration dialog.
+ *
+ * @see com.motorola.studio.android.wizards.elements.TableWithLoadingInfo#executeOperationsAfterTableIsPopulated()
+ */
+ @Override
+ protected void executeOperationsAfterTableIsPopulated()
+ {
+ // update the launching configuration dialog
+ MonkeyConfigurationTab monkeyWizardPage = getCallingPage();
+ monkeyWizardPage.callUpdateLaunchConfigurationDialog();
+ }
+
+}
diff --git a/src/plugins/android/src/com/motorola/studio/android/wizards/monkey/MonkeyOptionsComposite.java b/src/plugins/android/src/com/motorola/studio/android/wizards/monkey/MonkeyOptionsComposite.java
new file mode 100644
index 0000000..63e9062
--- /dev/null
+++ b/src/plugins/android/src/com/motorola/studio/android/wizards/monkey/MonkeyOptionsComposite.java
@@ -0,0 +1,362 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.wizards.monkey;
+
+import java.util.List;
+
+import org.eclipse.core.runtime.Status;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.ModifyEvent;
+import org.eclipse.swt.events.ModifyListener;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Combo;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.DirectoryDialog;
+import org.eclipse.swt.widgets.FileDialog;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Layout;
+import org.eclipse.swt.widgets.TabFolder;
+import org.eclipse.swt.widgets.TabItem;
+import org.eclipse.swt.widgets.Text;
+
+import com.motorola.studio.android.i18n.AndroidNLS;
+import com.motorola.studio.android.monkey.options.IMonkeyOptionsConstants;
+import com.motorola.studio.android.monkey.options.MonkeyOption;
+import com.motorola.studio.android.monkey.options.MonkeyOptionsGroup;
+import com.motorola.studio.android.monkey.options.MonkeyOptionsMgt;
+
+/**
+ * DESCRIPTION:
+ * <br>
+ * This class implements the UI for showing all monkey options information.
+ * <br>
+ * It extends the AbstractPropertiesComposite so as to use its common functionalities.
+ * <br>
+ * RESPONSIBILITY:
+ * <br>
+ * - Show monkey information on the UI
+ * <br>
+ * COLABORATORS:
+ * <br>
+ * AbstractPropertiesComposite: extends this class
+ * <br>
+ * USAGE:
+ * <br>
+ * This class should be added as a regular composite whenever monkey options information is necessary to be shown and edited on the UI.
+ */
+public class MonkeyOptionsComposite extends AbstractPropertiesComposite implements
+ IMonkeyOptionsConstants
+{
+ // The widget which displays the current command line used to pass the monkey options
+ private Text commandLine;
+
+ private final int TABFOLDER_HEIGHT_HINT = 290;
+
+ /**
+ * Creates a MonkeyOptionsComposite object.
+ *
+ * @param parent the parent composite
+ */
+ public MonkeyOptionsComposite(Composite parent, String monkeyOptions)
+ {
+ super(parent);
+
+ MonkeyOptionsMgt.loadFromCommandLine(monkeyOptions);
+ createUI();
+
+ // Set context Help
+ // PlatformUI.getWorkbench().getHelpSystem().setHelp(parent,
+ // IAndroidDeviceConstants.STARTUP_OPTIONS_HELP);
+ }
+
+ /**
+ * Create widgets for monkey options
+ */
+ private void createUI()
+ {
+
+ Composite mainComposite = this;
+ Layout mainLayout = new GridLayout();
+ mainComposite.setLayout(mainLayout);
+
+ // list of monkey options groups
+ List<MonkeyOptionsGroup> monkeyOptionsGroupsList =
+ MonkeyOptionsMgt.getMonkeyOptionsGroupsList();
+
+ // list of monkey options in each group
+ List<MonkeyOption> monkeyOptions = null;
+
+ // Create Tab Folder
+ final TabFolder tabFolder = new TabFolder(mainComposite, SWT.NULL);
+ GridData data = new GridData(SWT.FILL, SWT.FILL, true, false);
+ data.heightHint = TABFOLDER_HEIGHT_HINT;
+ tabFolder.setLayoutData(data);
+
+ /*
+ * Iterate through Monkey Groups
+ */
+ for (MonkeyOptionsGroup monkeyOptionGroup : monkeyOptionsGroupsList)
+ {
+
+ // Create Tab Item
+ TabItem tabItem = new TabItem(tabFolder, SWT.NULL);
+ tabItem.setText(monkeyOptionGroup.getTitle());
+ Composite group = new Composite(tabFolder, SWT.NULL);
+ group.setLayout(new GridLayout(3, false));
+ group.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
+ tabItem.setControl(group);
+
+ // get monkey options in this group
+ monkeyOptions = monkeyOptionGroup.getMonkeyOptions();
+
+ /*
+ * Iterate through Monkey Options in this group
+ */
+ for (final MonkeyOption monkeyOption : monkeyOptions)
+ {
+
+ // create a checkbox for each monkey option
+ Button checkbox = new Button(group, SWT.CHECK);
+ checkbox.setSelection(monkeyOption.isChecked());
+ checkbox.setText(monkeyOption.getUserFriendlyName());
+ checkbox.setToolTipText(monkeyOption.getName() + ": "
+ + monkeyOption.getDescription());
+
+ monkeyOption.setCheckedWidget(checkbox);
+ checkbox.addSelectionListener(new SelectionAdapter()
+ {
+ @Override
+ public void widgetSelected(SelectionEvent e)
+ {
+ boolean checkedStatus = ((Button) e.widget).getSelection();
+ monkeyOption.setChecked(checkedStatus);
+ notifyCompositeChangeListeners();
+ }
+ });
+ GridData checkboxData = new GridData(SWT.NULL, SWT.FILL, false, false);
+ checkbox.setLayoutData(checkboxData);
+
+ // Create input fields depending on the monkey option type
+ switch (monkeyOption.getType())
+ {
+ case TYPE_NONE:
+ // extend checkbox area along the line
+ checkboxData.widthHint = SWT.DEFAULT;
+ checkboxData.horizontalSpan = 3;
+ checkbox.setLayoutData(checkboxData);
+ break;
+
+ case TYPE_TEXT:
+ case TYPE_NUMBER:
+ createWidgetsForTextOrNumberType(group, monkeyOption);
+ break;
+
+ case TYPE_PATH:
+ createWidgetsForPathType(group, monkeyOption);
+ break;
+
+ default:
+ // none
+
+ }
+ }
+ }
+
+ /*
+ * Command Line area
+ */
+ Composite commandLineArea = new Composite(mainComposite, SWT.NONE); // composite
+ commandLineArea.setLayout(new GridLayout(2, false));
+ data = new GridData(SWT.FILL, SWT.FILL, true, true);
+ commandLineArea.setLayoutData(data);
+
+ Label commandLineLabel = new Label(commandLineArea, SWT.NONE); // label
+ commandLineLabel.setText(AndroidNLS.UI_MonkeyOptions_CommandLine);
+ data = new GridData(SWT.FILL, SWT.FILL, false, true);
+ commandLineLabel.setLayoutData(data);
+
+ commandLine = new Text(commandLineArea, SWT.MULTI | SWT.WRAP | SWT.BORDER | SWT.V_SCROLL); // text
+ data = new GridData(SWT.FILL, SWT.FILL, true, true);
+ commandLineArea.pack();
+ data.widthHint = commandLineArea.getBounds().width - commandLineLabel.getBounds().width;
+ data.heightHint = commandLineArea.getBounds().height;
+ commandLine.setLayoutData(data);
+ commandLine.setText(MonkeyOptionsMgt.getParamList());
+ commandLine.setEditable(false);
+ }
+
+ /**
+ * Create widgets to enable user to input data for fields of text or number type
+ *
+ * @param parent composite where the widgets will be attached to
+ * @param monkeyOption the corresponding monkey option
+ */
+ private void createWidgetsForTextOrNumberType(final Composite parent,
+ final MonkeyOption monkeyOption)
+ {
+ // create input text
+ if ((monkeyOption.getPreDefinedValues() == null)
+ || (monkeyOption.getPreDefinedValues().size() == 0))
+ {
+ final Text inputText = new Text(parent, SWT.SINGLE | SWT.BORDER);
+ inputText.setText(monkeyOption.getValue());
+ monkeyOption.setValueWidget(inputText);
+ inputText.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false, 2, 1));
+ inputText.addModifyListener(new ModifyListener()
+ {
+ public void modifyText(ModifyEvent e)
+ {
+ monkeyOption.setValue(inputText.getText());
+ notifyCompositeChangeListeners();
+ }
+ });
+ }
+ // create combobox
+ else
+ {
+ final Combo combo = new Combo(parent, SWT.READ_ONLY);
+ monkeyOption.setValueWidget(combo);
+ int selectedIndex = 0;
+ for (String preDefinedValue : monkeyOption.getPreDefinedValues())
+ {
+ combo.add(preDefinedValue);
+ if (monkeyOption.getValue().equals(preDefinedValue))
+ {
+ combo.select(selectedIndex);
+ }
+ else
+ {
+ selectedIndex++;
+ }
+ }
+ combo.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false, 2, 1));
+ combo.addModifyListener(new ModifyListener()
+ {
+ public void modifyText(ModifyEvent e)
+ {
+ monkeyOption.setValue(combo.getText());
+ notifyCompositeChangeListeners();
+ }
+ });
+ }
+ }
+
+ /**
+ * Create widgets to enable user to input data for fields of path type
+ *
+ * @param parent composite where the widgets will be attached to
+ * @param monkeyOption the corresponding monkey option
+ */
+ private void createWidgetsForPathType(final Composite parent, final MonkeyOption monkeyOption)
+ {
+ // create input text
+ final Text pathText = new Text(parent, SWT.SINGLE | SWT.BORDER);
+ pathText.setText(monkeyOption.getValue());
+ monkeyOption.setValueWidget(pathText);
+ pathText.setLayoutData(new GridData(SWT.FILL, SWT.NULL, true, false));
+ pathText.addModifyListener(new ModifyListener()
+ {
+ public void modifyText(ModifyEvent e)
+ {
+ monkeyOption.setValue(pathText.getText());
+ notifyCompositeChangeListeners();
+ }
+ });
+ // create browse button
+ Button pathBrowseButton = new Button(parent, SWT.PUSH);
+ pathBrowseButton.setText(AndroidNLS.UI_General_BrowseButtonLabel);
+ GridData data = new GridData(SWT.NULL, SWT.NULL, false, false);
+ pathBrowseButton.setLayoutData(data);
+ pathBrowseButton.addSelectionListener(new SelectionAdapter()
+ {
+ @Override
+ public void widgetSelected(SelectionEvent e)
+ {
+ String selectedPath = null;
+
+ if (monkeyOption.getTypeDetails().equals(TYPE_PATH_DIR))
+ {
+ DirectoryDialog directoryDialog = new DirectoryDialog(getShell(), SWT.OPEN);
+ selectedPath = directoryDialog.open();
+ }
+ else
+ {
+ FileDialog fileDialog = new FileDialog(getShell(), SWT.OPEN);
+ String[] filterExtensions =
+ {
+ "*" + monkeyOption.getTypeDetails()
+ };
+ fileDialog.setFilterExtensions(filterExtensions);
+ selectedPath = fileDialog.open();
+ }
+
+ if (selectedPath != null)
+ {
+ pathText.setText(selectedPath);
+ }
+ }
+ });
+ }
+
+ /**
+ * Update command line value
+ *
+ * @see com.motorola.studio.android.emulator.device.ui.AbstractPropertiesComposite#notifyCompositeChangeListeners()
+ */
+ @Override
+ protected void notifyCompositeChangeListeners()
+ {
+ commandLine.setText(MonkeyOptionsMgt.getParamList());
+ super.notifyCompositeChangeListeners();
+ }
+
+ /**
+ * Retrieves the error message associated to this composites current state.
+ * The order of precedence of error is the same as the fields displayed on the
+ * UI, which means errors on fields drawn first are shown with a higher precedence
+ * than the errors of fields drawn last.
+ * The instance description field is the only non required field.
+ *
+ * @return the error message, or <code>null</code> if there are no errors
+ */
+ @Override
+ public String getErrorMessage()
+ {
+ String errMsg = null;
+ Status status = MonkeyOptionsMgt.validate();
+ if (status.getSeverity() == Status.ERROR)
+ {
+ errMsg = status.getMessage();
+ }
+ return errMsg;
+ }
+
+ /**
+ * Reload the values being displayed in the UI as well as the ones
+ * in the model.
+ *
+ * @param monkeyOptions commandLine the command line used to start the emulator
+ */
+ public void reloadValues(String commandLine)
+ {
+ MonkeyOptionsMgt.loadFromCommandLine(commandLine);
+ }
+
+}
diff --git a/src/plugins/android/src/com/motorola/studio/android/wizards/project/NewAndroidProjectMainPage.java b/src/plugins/android/src/com/motorola/studio/android/wizards/project/NewAndroidProjectMainPage.java
new file mode 100644
index 0000000..bf14327
--- /dev/null
+++ b/src/plugins/android/src/com/motorola/studio/android/wizards/project/NewAndroidProjectMainPage.java
@@ -0,0 +1,307 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.wizards.project;
+
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.jface.dialogs.DialogPage;
+import org.eclipse.jface.wizard.IWizardPage;
+import org.eclipse.jface.wizard.WizardPage;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.ScrolledComposite;
+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.Event;
+import org.eclipse.swt.widgets.Group;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.ui.PlatformUI;
+
+import com.motorola.studio.android.AndroidPlugin;
+import com.motorola.studio.android.i18n.AndroidNLS;
+import com.motorola.studio.android.model.AndroidProject;
+import com.motorola.studio.android.model.AndroidProject.SourceTypes;
+import com.motorola.studio.android.model.IWizardModel;
+import com.motorola.studio.android.wizards.elements.ApplicationGroup;
+import com.motorola.studio.android.wizards.elements.LocationGroup;
+import com.motorola.studio.android.wizards.elements.ProjectNameGroup;
+import com.motorola.studio.android.wizards.elements.SdkTargetSelector;
+
+/**
+ * Class that represents the main page in the New Project Wizard
+ */
+public class NewAndroidProjectMainPage extends WizardPage
+{
+ private static final String PAGE_NAME = "Main Page";
+
+ private final AndroidProject project;
+
+ private final String NEW_PROJECT_HELP = AndroidPlugin.PLUGIN_ID + ".newproj";
+
+ //private boolean isNativeChecked = false;
+
+ private boolean hasNativePage = false;
+
+ private Button nativeCkb;
+
+ private Button obfuscateCkbox;
+
+ /**
+ * Listener for the wizard changes
+ */
+ private final Listener modelListener = new Listener()
+ {
+ public void handleEvent(Event arg0)
+ {
+ IStatus status = project.getStatus();
+ int severity = status.getSeverity();
+ setPageComplete(severity != IStatus.ERROR);
+
+ int msgType;
+ switch (severity)
+ {
+ case IStatus.OK:
+ msgType = DialogPage.NONE;
+ break;
+ case IStatus.ERROR:
+ msgType = DialogPage.ERROR;
+ break;
+ case IStatus.WARNING:
+ msgType = DialogPage.WARNING;
+ break;
+ default:
+ msgType = DialogPage.NONE;
+ break;
+ }
+ String defaultMessage = AndroidNLS.UI_NewAndroidProjectMainPage_SubtitleCreateProject;
+ setMessage(status.isOK() ? defaultMessage : status.getMessage(), msgType);
+ }
+ };
+
+ /**
+ * Constructor
+ *
+ * @param project The selected project
+ */
+ public NewAndroidProjectMainPage(AndroidProject project, boolean hasNativePage)
+ {
+ super(PAGE_NAME);
+ this.project = project;
+ this.hasNativePage = hasNativePage;
+ setTitle(AndroidNLS.UI_NewAndroidProjectMainPage_TitleCreateProject);
+ setDescription(AndroidNLS.UI_NewAndroidProjectMainPage_WizardProjectDescription);
+ setPageComplete(false);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.dialogs.IDialogPage#createControl(org.eclipse.swt.widgets.Composite)
+ */
+ public void createControl(Composite parent)
+ {
+ Composite mainComposite = new Composite(parent, SWT.NULL);
+ mainComposite.setFont(parent.getFont());
+ mainComposite.setLayout(new FillLayout(SWT.VERTICAL));
+
+ initializeDialogUnits(mainComposite);
+
+ final ScrolledComposite scroll =
+ new ScrolledComposite(mainComposite, SWT.H_SCROLL | SWT.V_SCROLL);
+ final Composite innerScrollComposite = new Composite(scroll, SWT.NONE);
+
+ innerScrollComposite.setLayout(new GridLayout());
+ GridData data = new GridData(SWT.FILL, SWT.FILL, true, true);
+ innerScrollComposite.setLayoutData(data);
+
+ //only put the project and location groups: no vertical resize, no vertical style
+ data = new GridData(SWT.FILL, SWT.NONE, true, false);
+
+ ProjectNameGroup projectNameGroup = new ProjectNameGroup(innerScrollComposite, project);
+ projectNameGroup.setLayoutData(data);
+
+ // Create Location Group
+ data = new GridData(SWT.FILL, SWT.NONE, true, false);
+ Group groupForLocation = new Group(innerScrollComposite, SWT.SHADOW_ETCHED_IN);
+ // Layout has 1 column
+ groupForLocation.setLayout(new GridLayout());
+ groupForLocation.setLayoutData(data);
+ groupForLocation.setFont(innerScrollComposite.getFont());
+ groupForLocation.setText(AndroidNLS.UI_NewAndroidProjectMainPage_LabelContents);
+
+ LocationGroup locationGroup = new LocationGroup(groupForLocation, project, this);
+ // End of Location Group
+
+ // Create SDK Group
+ //create sdk group with vertical resize, grabbing excedding space
+ data = new GridData(SWT.FILL, SWT.FILL, true, true);
+ data.heightHint = 150;
+ Group groupForTarget = new Group(innerScrollComposite, SWT.SHADOW_ETCHED_IN);
+ groupForTarget.setLayout(new GridLayout());
+ groupForTarget.setLayoutData(data);
+ groupForTarget.setFont(innerScrollComposite.getFont());
+ groupForTarget.setText(AndroidNLS.UI_NewAndroidProjectMainPage_LabelTarget);
+
+ final SdkTargetSelector mSdkTargetSelector = new SdkTargetSelector(groupForTarget, project);
+ //End of Target Creation
+
+ // Create Package Group
+ Group group = new Group(innerScrollComposite, SWT.SHADOW_ETCHED_IN);
+ data = new GridData(SWT.FILL, SWT.NONE, true, false);
+ // Layout has 1 column
+ group.setLayout(new GridLayout());
+ group.setLayoutData(data);
+ group.setFont(innerScrollComposite.getFont());
+ group.setText(AndroidNLS.UI_NewAndroidProjectMainPage_LabelApplication);
+
+ final ApplicationGroup applicationGroup = new ApplicationGroup(group, project);
+ Listener listener = new Listener()
+ {
+ public void handleEvent(Event arg0)
+ {
+ applicationGroup.updateDefaultName();
+ applicationGroup.updateMinSdkVersion();
+ getContainer().updateButtons();
+
+ if (hasNativePage)
+ {
+ if (project.getSourceType() == SourceTypes.SAMPLE)
+ {
+ nativeCkb.setEnabled(false);
+ nativeCkb.setSelection(false);
+ project.setAddNativeSupport(false);
+ }
+ else
+ {
+ nativeCkb.setEnabled(true);
+ project.setAddNativeSupport(nativeCkb.getSelection());
+ }
+ }
+
+ project.setNeedToObfuscate(obfuscateCkbox.getSelection());
+ }
+ };
+
+ nativeCkb = new Button(innerScrollComposite, SWT.CHECK);
+ nativeCkb.setText(AndroidNLS.UI_ProjectCreation_NativeSupport);
+ if (!hasNativePage)
+ {
+ nativeCkb.setEnabled(false);
+ }
+
+ obfuscateCkbox = new Button(innerScrollComposite, SWT.CHECK);
+ obfuscateCkbox.setText(AndroidNLS.UI_ProjectCreation_Obfuscate);
+ obfuscateCkbox.setEnabled(true);
+
+ projectNameGroup.addListener(IWizardModel.MODIFIED, modelListener);
+ projectNameGroup.addListener(IWizardModel.MODIFIED, listener);
+ locationGroup.addListener(IWizardModel.MODIFIED, listener);
+ locationGroup.addListener(IWizardModel.MODIFIED, modelListener);
+ applicationGroup.addListener(IWizardModel.MODIFIED, modelListener);
+ mSdkTargetSelector.addListener(IWizardModel.MODIFIED, modelListener);
+ mSdkTargetSelector.addListener(IWizardModel.MODIFIED, listener);
+ nativeCkb.addListener(SWT.Selection, listener);
+ nativeCkb.addListener(SWT.Selection, modelListener);
+ obfuscateCkbox.addListener(SWT.Selection, listener);
+ obfuscateCkbox.addListener(SWT.Selection, modelListener);
+
+ //create application group with no vertical resize
+ setPageComplete(false);
+
+ // Show description the first time
+ setErrorMessage(null);
+ setMessage(null);
+
+ projectNameGroup.forceFocus();
+
+ setControl(innerScrollComposite);
+ innerScrollComposite.layout(true);
+ PlatformUI.getWorkbench().getHelpSystem().setHelp(parent, NEW_PROJECT_HELP);
+
+ // set up scroll
+ scroll.setContent(innerScrollComposite);
+
+ scroll.setExpandHorizontal(true);
+ scroll.setExpandVertical(true);
+
+ scroll.setMinSize(innerScrollComposite.computeSize(SWT.DEFAULT, SWT.DEFAULT));
+ //
+ // set control
+ setControl(mainComposite);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.jface.dialogs.DialogPage#setButtonLayoutData(org.eclipse.swt.widgets.Button)
+ */
+ @Override
+ public GridData setButtonLayoutData(Button button)
+ {
+ return super.setButtonLayoutData(button);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.jface.wizard.WizardPage#getNextPage()
+ */
+ @Override
+ public IWizardPage getNextPage()
+ {
+
+ //returns null if there is no next page
+ IWizardPage page = null;
+ if (project.isAddingNativeSupport())
+ {
+ //calls native page
+ page = getWizard().getPage(NewAndroidProjectWizard.NATIVE_PAGE_NAME);
+ }
+ else
+ {
+ //calls source page when selected
+ if (project.getSourceType() == SourceTypes.SAMPLE)
+ {
+ page = getWizard().getPage(NewAndroidProjectWizard.SAMPLE_PAGE_NAME);
+ }
+ }
+
+ return page;
+ }
+
+ @Override
+ public boolean isPageComplete()
+ {
+ boolean canFinish = true;
+ if ((project.isAddingNativeSupport()) || (project.getSourceType() == SourceTypes.SAMPLE)
+ || !super.isPageComplete())
+ {
+ canFinish = false;
+ }
+ return canFinish;
+ }
+
+ @Override
+ public boolean canFlipToNextPage()
+ {
+ boolean canFlip = false;
+ if ((project.isAddingNativeSupport()) || (project.getSourceType() == SourceTypes.SAMPLE))
+ {
+ if (super.isPageComplete())
+ {
+ canFlip = true;
+ }
+ }
+ return canFlip;
+ }
+}
diff --git a/src/plugins/android/src/com/motorola/studio/android/wizards/project/NewAndroidProjectWizard.java b/src/plugins/android/src/com/motorola/studio/android/wizards/project/NewAndroidProjectWizard.java
new file mode 100644
index 0000000..e44b40e
--- /dev/null
+++ b/src/plugins/android/src/com/motorola/studio/android/wizards/project/NewAndroidProjectWizard.java
@@ -0,0 +1,441 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.wizards.project;
+
+import java.io.File;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.IWorkspaceDescription;
+import org.eclipse.core.resources.IncrementalProjectBuilder;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.core.runtime.QualifiedName;
+import org.eclipse.core.runtime.SubMonitor;
+import org.eclipse.jdt.core.IClasspathEntry;
+import org.eclipse.jdt.core.IJavaProject;
+import org.eclipse.jdt.core.JavaCore;
+import org.eclipse.jdt.core.JavaModelException;
+import org.eclipse.jface.operation.IRunnableWithProgress;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.wizard.WizardPage;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.ui.INewWizard;
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.wizards.newresource.BasicNewProjectResourceWizard;
+import org.osgi.framework.Bundle;
+
+import com.android.sdklib.IAndroidTarget;
+import com.motorola.studio.android.AndroidPlugin;
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.common.preferences.DialogWithToggleUtils;
+import com.motorola.studio.android.common.utilities.EclipseUtils;
+import com.motorola.studio.android.i18n.AndroidNLS;
+import com.motorola.studio.android.model.AndroidProject;
+import com.motorola.studio.android.obfuscate.ObfuscatorManager;
+
+/**
+ * Class that represents the Android New Project Wizard
+ */
+public class NewAndroidProjectWizard extends BasicNewProjectResourceWizard implements INewWizard
+{
+ private static final String WIZARD_BANNER = "icons/wizban/newprjwiz.png"; //$NON-NLS-1$
+
+ protected static final String NATIVE_PAGE_NAME = "native_page"; //$NON-NLS-1$
+
+ protected static final String SAMPLE_PAGE_NAME =
+ AndroidNLS.UI_SampleSelectionPage_TitleSourcePage;
+
+ private final AndroidProject project = new AndroidProject();
+
+ private WizardPage nativePage = null;
+
+ private NewAndroidProjectMainPage mainPage = null;
+
+ private Class<?> nativePageClass = null;
+
+ private Object classInstance = null;
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.wizard.Wizard#canFinish()
+ */
+
+ @Override
+ public boolean canFinish()
+ {
+ boolean canFinish =
+ (project.getStatus().getSeverity() != IStatus.ERROR)
+ && !project.needMoreInformation();
+
+ if ((nativePage != null) && !nativePage.isPageComplete() && project.isAddingNativeSupport())
+ {
+ canFinish = false;
+ }
+
+ return canFinish;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.wizards.newresource.BasicNewProjectResourceWizard#performFinish()
+ */
+ @Override
+ public boolean performFinish()
+ {
+ DoSave doSave = new DoSave();
+
+ try
+ {
+ getContainer().run(false, false, doSave);
+ }
+ catch (Exception e)
+ {
+ String errMsg =
+ NLS.bind(
+ AndroidNLS.EXC_NewAndroidProjectWizard_AnErrorHasOccurredWhenCreatingTheProject,
+ e.getLocalizedMessage());
+ StudioLogger.error(NewAndroidProjectWizard.class, errMsg, e);
+
+ EclipseUtils.showErrorDialog(AndroidNLS.UI_GenericErrorDialogTitle, errMsg, null);
+ }
+ boolean success = doSave.isSaved();
+
+ if (success)
+ {
+ // Collecting usage data for statistical purposes
+ try
+ {
+ StudioLogger.collectUsageData(StudioLogger.WHAT_APP_MANAGEMENT_CREATE,
+ StudioLogger.KIND_APP_MANAGEMENT, this.project.getSourceType().name()
+ .toLowerCase(), AndroidPlugin.PLUGIN_ID, AndroidPlugin.getDefault()
+ .getBundle().getVersion().toString());
+ }
+ catch (Throwable e)
+ {
+ //Do nothing, but error on the log should never prevent app from working
+ }
+ }
+
+ return success;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.wizard.Wizard#performCancel()
+ */
+ @Override
+ public boolean performCancel()
+ {
+ try
+ {
+ project.finalize();
+ }
+ catch (Throwable e)
+ {
+ StudioLogger.error(NewAndroidProjectWizard.class, e.getLocalizedMessage(), e);
+ }
+ return super.performCancel();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.wizards.newresource.BasicNewProjectResourceWizard#addPages()
+ */
+
+ @SuppressWarnings(
+ {
+ "unchecked"
+ })
+ @Override
+ public void addPages()
+ {
+
+ classInstance = null;
+
+ try
+ {
+ Bundle seqBundle = Platform.getBundle("org.eclipse.sequoyah.android.cdt.build.ui"); //$NON-NLS-1$
+ if (seqBundle != null)
+ {
+ nativePageClass =
+ seqBundle
+ .loadClass("org.eclipse.sequoyah.android.cdt.internal.build.ui.AddNativeProjectPage"); //$NON-NLS-1$
+
+ Class<Boolean> bClass = boolean.class;
+ Class[] paramTypes =
+ {
+ bClass
+ };
+
+ Constructor cs = nativePageClass.getConstructor(paramTypes);
+ classInstance = cs.newInstance(true);
+ nativePage = (WizardPage) classInstance;
+ }
+ }
+ catch (Exception e)
+ {
+ StudioLogger.error(NewAndroidProjectWizard.class, e.getMessage(), e);
+ }
+
+ if (nativePage != null)
+ {
+ mainPage = new NewAndroidProjectMainPage(project, true);
+ addPage(mainPage);
+ addPage(nativePage);
+ }
+ else
+ {
+ mainPage = new NewAndroidProjectMainPage(project, false);
+ addPage(mainPage);
+ }
+ addPage(new SampleSelectionPage(project));
+
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.wizards.newresource.BasicNewProjectResourceWizard#init(org.eclipse.ui.IWorkbench, org.eclipse.jface.viewers.IStructuredSelection)
+ */
+ @Override
+ public void init(IWorkbench workbench, IStructuredSelection selection)
+ {
+ setWindowTitle(AndroidNLS.UI_NewAndroidProjectWizard_TitleNewProjectWizard);
+ setNeedsProgressMonitor(true);
+ setDefaultPageImageDescriptor(AndroidPlugin.getImageDescriptor(WIZARD_BANNER));
+ }
+
+ /**
+ * Implements an IRunnableWithProgress to run the save process
+ */
+ private class DoSave implements IRunnableWithProgress
+ {
+ private static final String OPHONE_JAR = "oms.jar"; //$NON-NLS-1$
+
+ private static final String OPHONESDK_PROMPT_KEY = "OphoneSDK"; //$NON-NLS-1$
+
+ private boolean saved = false;
+
+ /**
+ * Returns whether the project was saved/created successfuly.
+ *
+ * @return Returns <code>true</code> in case the project is saved/creates successfully.
+ */
+ public boolean isSaved()
+ {
+ return saved;
+ }
+
+ public void run(IProgressMonitor monitor) throws InvocationTargetException,
+ InterruptedException
+ {
+ SubMonitor subMonitor = SubMonitor.convert(monitor, 20);
+
+ subMonitor.beginTask(AndroidNLS.NewAndroidProjectWizard_Message_CreatingAndroidProject,
+ 10);
+
+ // Gets the auto-building configuration to set it back in the end
+ final boolean autoBuild = ResourcesPlugin.getWorkspace().isAutoBuilding();
+
+ final IWorkspaceDescription wsd = ResourcesPlugin.getWorkspace().getDescription();
+ wsd.setAutoBuilding(false);
+ try
+ {
+ // Set auto-build off for performance reasons
+ ResourcesPlugin.getWorkspace().setDescription(wsd);
+ }
+ catch (CoreException e)
+ {
+ // there is no need to stop the process because auto-build only improves performance, it does not interferes with the new project creation.
+ StudioLogger.error(NewAndroidProjectWizard.class,
+ "Error cleaning workspace after project creation: " + e.getMessage()); //$NON-NLS-1$
+ }
+
+ // worked 1
+ subMonitor.worked(1);
+
+ saved = project.save(getContainer(), subMonitor);
+ updatePerspective();
+
+ IProject newProject =
+ ResourcesPlugin.getWorkspace().getRoot().getProject(project.getName());
+
+ addOphoneSDK(newProject, subMonitor);
+
+ try
+ {
+ newProject.build(IncrementalProjectBuilder.CLEAN_BUILD, subMonitor);
+ }
+ catch (Exception e1)
+ {
+ // even if the build fais, the project could still be created, therefore it must continue
+ StudioLogger.error(NewAndroidProjectWizard.class,
+ "Sleep error when cleaning workspace after project creation: " //$NON-NLS-1$
+ + e1.getMessage());
+ }
+
+ // worked 4
+ subMonitor.worked(3);
+
+ wsd.setAutoBuilding(autoBuild);
+ try
+ {
+ // rollback the auto-building setting to the original state
+ ResourcesPlugin.getWorkspace().setDescription(wsd);
+ }
+ catch (CoreException e)
+ {
+ // the auto-building does not interfere with the project creation, therefore in case it fails, the process may contine
+ StudioLogger.error(NewAndroidProjectWizard.class,
+ "Error cleaning workspace after project creation: " + e.getMessage()); //$NON-NLS-1$
+ }
+
+ // worked 5
+ subMonitor.worked(1);
+
+ if ((nativePage != null) && project.isAddingNativeSupport() && saved)
+ {
+ try
+ {
+ Class<IWorkbenchWindow> workbenchClass = IWorkbenchWindow.class;
+ Class<IProject> projectClass = IProject.class;
+ Class<IProgressMonitor> progressMonitorClass = IProgressMonitor.class;
+
+ IProject createdProject =
+ ResourcesPlugin.getWorkspace().getRoot().getProject(project.getName());
+ IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
+
+ QualifiedName libQN =
+ new QualifiedName(AndroidPlugin.PLUGIN_ID,
+ AndroidPlugin.LIB_LOCATION_PROPERTY);
+
+ //get library name field from wizard
+ Method getLibraryName =
+ nativePageClass.getMethod("getLibraryName", (Class[]) null); //$NON-NLS-1$
+ getLibraryName.setAccessible(true);
+ Object returnValue = getLibraryName.invoke(classInstance, (Object[]) null);
+
+ //set project library name property
+ createdProject.setPersistentProperty(libQN, returnValue.toString());
+
+ // worked 6
+ subMonitor.worked(1);
+
+ Object[] performFinishMethodArguments =
+ {
+ window, createdProject, subMonitor.newChild(4)
+ };
+
+ Class<?>[] performFinishMethodParameterTypes =
+ {
+ workbenchClass, projectClass, progressMonitorClass
+ };
+
+ //invoke page perform finish that will add native support to the brand new project
+ Method performFinish = nativePageClass.getMethod("performFinish", //$NON-NLS-1$
+ performFinishMethodParameterTypes);
+ performFinish.setAccessible(true);
+
+ returnValue = performFinish.invoke(classInstance, performFinishMethodArguments);
+
+ //update success flag
+ saved = saved && (Boolean) returnValue;
+ }
+ catch (Exception e)
+ {
+ // the project may be in a inconsistent state - throw an exception
+ saved = false;
+ StudioLogger.error(NewAndroidProjectWizard.class, e.getMessage(), e);
+ throw new InvocationTargetException(e);
+ }
+ }
+
+ //add proguard file inside project
+ try
+ {
+ if (project.needToObfuscate())
+ {
+ ObfuscatorManager.obfuscate(newProject, subMonitor.newChild(10));
+ }
+ newProject.refreshLocal(IResource.DEPTH_INFINITE, subMonitor);
+ }
+ catch (Exception e)
+ {
+ StudioLogger.error(NewAndroidProjectWizard.class, e.getMessage(), e);
+ throw new InvocationTargetException(e);
+ }
+ }
+
+ private void addOphoneSDK(IProject p, IProgressMonitor monitor)
+ {
+ IAndroidTarget sdkTarget = project.getSdkTarget();
+ File platformLocation = new File(sdkTarget.getLocation());
+ File[] listFiles = platformLocation.listFiles();
+
+ boolean found = false;
+ int i = 0;
+ File file = null;
+ while (!found && (i < listFiles.length))
+ {
+ file = listFiles[i];
+ if (file.getName().equals(OPHONE_JAR))
+ {
+ found = true;
+ }
+ i++;
+ }
+
+ if (found)
+ {
+ boolean addClasspath =
+ DialogWithToggleUtils.showQuestion(OPHONESDK_PROMPT_KEY,
+ AndroidNLS.NewAndroidProjectWizard_OPhonePromptTitle,
+ AndroidNLS.NewAndroidProjectWizard_OPhonePromptMessage);
+
+ if (addClasspath)
+ {
+ IJavaProject javaProject = JavaCore.create(p);
+ if ((javaProject != null) && javaProject.exists())
+ {
+ try
+ {
+ javaProject.open(monitor);
+ IClasspathEntry[] rawClasspath = javaProject.getRawClasspath();
+ IClasspathEntry[] newClasspath =
+ new IClasspathEntry[rawClasspath.length + 1];
+
+ System.arraycopy(rawClasspath, 0, newClasspath, 0, rawClasspath.length);
+ newClasspath[newClasspath.length - 1] =
+ JavaCore.newLibraryEntry(new Path(file.getAbsolutePath()),
+ null, null);
+ javaProject.setRawClasspath(newClasspath, monitor);
+ }
+ catch (JavaModelException e)
+ {
+ StudioLogger.error(NewAndroidProjectWizard.class,
+ "Error while setting up the oms.jar on the project classpath: " //$NON-NLS-1$
+ + e.getMessage());
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/src/plugins/android/src/com/motorola/studio/android/wizards/project/SampleSelectionPage.java b/src/plugins/android/src/com/motorola/studio/android/wizards/project/SampleSelectionPage.java
new file mode 100644
index 0000000..90141db
--- /dev/null
+++ b/src/plugins/android/src/com/motorola/studio/android/wizards/project/SampleSelectionPage.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.wizards.project;
+
+import org.eclipse.jface.viewers.TreeViewer;
+import org.eclipse.jface.wizard.IWizardPage;
+import org.eclipse.jface.wizard.WizardPage;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Tree;
+import org.eclipse.swt.widgets.TreeItem;
+import org.eclipse.ui.PlatformUI;
+
+import com.motorola.studio.android.AndroidPlugin;
+import com.motorola.studio.android.adt.Sample;
+import com.motorola.studio.android.adt.SdkUtils;
+import com.motorola.studio.android.i18n.AndroidNLS;
+import com.motorola.studio.android.model.AndroidProject;
+
+/**
+ * Sample Selection Wizard Page.
+ */
+public class SampleSelectionPage extends WizardPage
+{
+ private TreeViewer treeViewer;
+
+ final private AndroidProject project;
+
+ private Sample selection = null;
+
+ private final String NEW_PROJECT_SAMPLE_HELP = AndroidPlugin.PLUGIN_ID + ".newproj";
+
+ /**
+ * Constructor
+ * @param project
+ */
+ public SampleSelectionPage(AndroidProject project)
+ {
+ super(AndroidNLS.UI_SampleSelectionPage_TitleSourcePage);
+ this.project = project;
+ setTitle(AndroidNLS.UI_SampleSelectionPage_WizardTitle);
+ setDescription(AndroidNLS.UI_SampleSelectionPage_WizardDescription);
+ setPageComplete(true);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.jface.dialogs.IDialogPage#createControl(org.eclipse.swt.widgets.Composite)
+ */
+ public void createControl(Composite parent)
+ {
+ initializeDialogUnits(parent);
+ Composite mainComposite = new Composite(parent, SWT.NONE);
+ mainComposite.setLayout(new GridLayout());
+ mainComposite.setLayoutData(new GridData(GridData.FILL, GridData.FILL, true, true));
+ mainComposite.setLayout(new GridLayout());
+
+ // Samples Tree Label
+ Label itemsTableLabel = new Label(mainComposite, SWT.NONE);
+ itemsTableLabel.setText(AndroidNLS.UI_SampleSelectionPage_SamplesTreeLabel);
+
+ Composite composite = new Composite(mainComposite, SWT.NONE);
+ composite.setLayout(new GridLayout());
+ composite.setLayoutData(new GridData(GridData.FILL_BOTH));
+
+ // Samples Tree Viewer
+ treeViewer = new TreeViewer(composite, SWT.BORDER | SWT.SINGLE | SWT.V_SCROLL);
+ GridData layoutData = new GridData(SWT.FILL, SWT.FILL, true, false);
+ layoutData.heightHint = 250;
+ treeViewer.getControl().setLayoutData(layoutData);
+ treeViewer.setContentProvider(new TreeContentProvider(project));
+ treeViewer.setLabelProvider(new TreeLabelProvider());
+ treeViewer.setInput(SdkUtils.getCurrentSdk());
+ treeViewer.expandAll();
+ treeViewer.getTree().addSelectionListener(new SamplesSelectionAdapter(this, project));
+
+ setControl(mainComposite);
+ PlatformUI.getWorkbench().getHelpSystem().setHelp(parent, NEW_PROJECT_SAMPLE_HELP);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.dialogs.DialogPage#setVisible(boolean)
+ */
+ @Override
+ public void setVisible(boolean visible)
+ {
+ if (visible)
+ {
+ treeViewer.refresh();
+ Tree tree = treeViewer.getTree();
+ tree.deselectAll();
+
+ if (selection != null)
+ {
+ for (TreeItem item : treeViewer.getTree().getItems())
+ {
+ if (item.getData() instanceof Sample)
+ {
+ Sample sample = (Sample) item.getData();
+ if (sample.getName().equals(selection.getName()))
+ {
+ tree.setSelection(item);
+ tree.select(item);
+ project.setSample(sample);
+ setPageComplete(true);
+ }
+ }
+ }
+ }
+ if ((tree.getSelection().length == 0) && (tree.getItemCount() > 0))
+ {
+ tree.setSelection(tree.getItem(0));
+ tree.select(tree.getItem(0));
+ project.setSample((Sample) tree.getItem(0).getData());
+ setPageComplete(true);
+ }
+ }
+ else
+ {
+ selection = project.getSample();
+ setPageComplete(false);
+ }
+ super.setVisible(visible);
+ getContainer().updateButtons();
+ }
+
+ //returns native page when selected or main page
+ @Override
+ public IWizardPage getPreviousPage()
+ {
+ return super.getPreviousPage();
+ }
+
+} \ No newline at end of file
diff --git a/src/plugins/android/src/com/motorola/studio/android/wizards/project/SamplesSelectionAdapter.java b/src/plugins/android/src/com/motorola/studio/android/wizards/project/SamplesSelectionAdapter.java
new file mode 100644
index 0000000..efead7c
--- /dev/null
+++ b/src/plugins/android/src/com/motorola/studio/android/wizards/project/SamplesSelectionAdapter.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.wizards.project;
+
+import org.eclipse.jface.dialogs.DialogPage;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+
+import com.motorola.studio.android.adt.Sample;
+import com.motorola.studio.android.i18n.AndroidNLS;
+import com.motorola.studio.android.model.AndroidProject;
+
+/**
+ * Class that implements a Selection Adapter for the
+ * Sample Selection Page
+ */
+class SamplesSelectionAdapter extends SelectionAdapter
+{
+ private final SampleSelectionPage sampleSelectionPage;
+
+ private final AndroidProject project;
+
+ /**
+ * Default constructor
+ *
+ * @param sampleSelectionPage The sample selection page
+ * @param treeViewer The tree viewer of the selection adapter
+ * @param project The project
+ */
+ public SamplesSelectionAdapter(SampleSelectionPage sampleSelectionPage, AndroidProject project)
+ {
+ this.sampleSelectionPage = sampleSelectionPage;
+ this.project = project;
+
+ sampleSelectionPage.setMessage(AndroidNLS.UI_SampleSelectionPage_WizardDescription,
+ DialogPage.NONE);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.swt.events.SelectionAdapter#widgetSelected(org.eclipse.swt.events.SelectionEvent)
+ */
+ @Override
+ public void widgetSelected(SelectionEvent e)
+ {
+ if (e.item != null)
+ {
+ project.setSample((Sample) e.item.getData());
+ sampleSelectionPage.getWizard().getContainer().updateButtons();
+ }
+ }
+}
diff --git a/src/plugins/android/src/com/motorola/studio/android/wizards/project/TreeContentProvider.java b/src/plugins/android/src/com/motorola/studio/android/wizards/project/TreeContentProvider.java
new file mode 100644
index 0000000..5fbd2e9
--- /dev/null
+++ b/src/plugins/android/src/com/motorola/studio/android/wizards/project/TreeContentProvider.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.wizards.project;
+
+import org.eclipse.jface.viewers.ITreeContentProvider;
+import org.eclipse.jface.viewers.Viewer;
+
+import com.android.ide.eclipse.adt.internal.sdk.Sdk;
+import com.android.sdklib.IAndroidTarget;
+import com.motorola.studio.android.adt.SdkUtils;
+import com.motorola.studio.android.model.AndroidProject;
+
+/**
+ * Class that implements a content provider for the Samples Tree viewers.
+ */
+@SuppressWarnings("restriction")
+class TreeContentProvider implements ITreeContentProvider
+{
+ AndroidProject project = null;
+
+ public TreeContentProvider(AndroidProject project)
+ {
+ this.project = project;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.jface.viewers.ITreeContentProvider#getChildren(java.lang.Object)
+ */
+ public Object[] getChildren(Object arg0)
+ {
+ Object[] objects;
+
+ if (arg0 instanceof IAndroidTarget)
+ {
+ objects = SdkUtils.getSamples((IAndroidTarget) arg0);
+ }
+ else
+ {
+ objects = new Object[0];
+ }
+ return objects;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.jface.viewers.ITreeContentProvider#getParent(java.lang.Object)
+ */
+ public Object getParent(Object arg0)
+ {
+ return null;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.jface.viewers.ITreeContentProvider#hasChildren(java.lang.Object)
+ */
+ public boolean hasChildren(Object arg0)
+ {
+ Object[] obj = getChildren(arg0);
+ return obj == null ? false : obj.length > 0;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.jface.viewers.IStructuredContentProvider#getElements(java.lang.Object)
+ */
+ public Object[] getElements(Object arg0)
+ {
+ Object[] objs = null;
+
+ if (arg0 instanceof Sdk)
+ {
+ Sdk sdk = (Sdk) arg0;
+ Object[] targets = SdkUtils.getTargets(sdk);
+ if (targets.length > 0)
+ {
+ for (IAndroidTarget target : (IAndroidTarget[]) targets)
+ {
+ if (target.equals(project.getSdkTarget()))
+ {
+ objs = SdkUtils.getSamples(target);
+ }
+ }
+ }
+ else
+ {
+ objs = new Object[0];
+ }
+ }
+ return objs;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.jface.viewers.IContentProvider#dispose()
+ */
+ public void dispose()
+ {
+ //do nothing
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.jface.viewers.IContentProvider#inputChanged(org.eclipse.jface.viewers.Viewer, java.lang.Object, java.lang.Object)
+ */
+ public void inputChanged(Viewer arg0, Object arg1, Object arg2)
+ {
+ //do nothing
+ }
+}
diff --git a/src/plugins/android/src/com/motorola/studio/android/wizards/project/TreeLabelProvider.java b/src/plugins/android/src/com/motorola/studio/android/wizards/project/TreeLabelProvider.java
new file mode 100644
index 0000000..742715e
--- /dev/null
+++ b/src/plugins/android/src/com/motorola/studio/android/wizards/project/TreeLabelProvider.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.wizards.project;
+
+import org.eclipse.jface.viewers.LabelProvider;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.ide.IDE.SharedImages;
+
+import com.android.ide.eclipse.adt.AdtPlugin;
+import com.android.sdklib.IAndroidTarget;
+import com.motorola.studio.android.AndroidPlugin;
+import com.motorola.studio.android.adt.Sample;
+
+/**
+ * Label Provider for the Tree of Project Templates
+ */
+public class TreeLabelProvider extends LabelProvider
+{
+ private static final String MOTOROLA_BRAND = "motorola";
+
+ private static final Image category = new Image(null, PlatformUI.getWorkbench()
+ .getSharedImages().getImage(SharedImages.IMG_OBJ_PROJECT).getImageData());
+
+ private static final Image studio = new Image(null, AndroidPlugin.getImageDescriptor(
+ AndroidPlugin.ANDROID_MOTOROLA_BRAND_ICON_PATH).getImageData());
+
+ private static final Image android = AdtPlugin.getAndroidLogo();
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.jface.viewers.LabelProvider#getImage(java.lang.Object)
+ */
+ @Override
+ public Image getImage(Object obj)
+ {
+
+ Image image = null;
+ if (obj instanceof Sample)
+ {
+ String targetName = ((Sample) obj).getTarget().getVendor();
+ if (targetName.toLowerCase().contains(MOTOROLA_BRAND))
+ {
+ image = studio;
+ }
+ else
+ {
+ image = android;
+ }
+ }
+ else if (obj instanceof IAndroidTarget)
+ {
+ image = category;
+ }
+ return image;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.jface.viewers.LabelProvider#getText(java.lang.Object)
+ */
+ @Override
+ public String getText(Object obj)
+ {
+ String text;
+ if (obj instanceof Sample)
+ {
+ text = ((Sample) obj).getName();
+ }
+ else if (obj instanceof IAndroidTarget)
+ {
+ text = ((IAndroidTarget) obj).getName();
+ }
+ else
+ {
+ text = ""; //$NON-NLS-1$
+ }
+ return text;
+ }
+}
diff --git a/src/plugins/android/src/com/motorola/studio/android/wizards/widget/NewAndroidWidgetProjectMainPage.java b/src/plugins/android/src/com/motorola/studio/android/wizards/widget/NewAndroidWidgetProjectMainPage.java
new file mode 100644
index 0000000..0112120
--- /dev/null
+++ b/src/plugins/android/src/com/motorola/studio/android/wizards/widget/NewAndroidWidgetProjectMainPage.java
@@ -0,0 +1,196 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.wizards.widget;
+
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.jface.dialogs.DialogPage;
+import org.eclipse.jface.wizard.WizardPage;
+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.Event;
+import org.eclipse.swt.widgets.Group;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.ui.PlatformUI;
+
+import com.motorola.studio.android.AndroidPlugin;
+import com.motorola.studio.android.i18n.AndroidNLS;
+import com.motorola.studio.android.model.AndroidProject;
+import com.motorola.studio.android.model.IWizardModel;
+import com.motorola.studio.android.wizards.elements.ApplicationGroup;
+import com.motorola.studio.android.wizards.elements.ProjectNameGroup;
+import com.motorola.studio.android.wizards.elements.SdkTargetSelector;
+import com.motorola.studio.android.wizards.elements.WidgetLocationGroup;
+
+/**
+ * Class that represents the main page in the New Widget Project Wizard
+ */
+public class NewAndroidWidgetProjectMainPage extends WizardPage
+{
+
+ private static final String PAGE_NAME = "Main Page";
+
+ private final AndroidProject project;
+
+ private final String NEW_WIDGET_PROJECT_HELP = AndroidPlugin.PLUGIN_ID + ".newwdgproj";
+
+ /**
+ * Listener for the wizard changes
+ */
+ private final Listener modelListener = new Listener()
+ {
+ public void handleEvent(Event arg0)
+ {
+ IStatus status = project.getStatus();
+ int severity = status.getSeverity();
+ setPageComplete(severity != IStatus.ERROR);
+
+ int msgType;
+ switch (severity)
+ {
+ case IStatus.OK:
+ msgType = DialogPage.NONE;
+ break;
+ case IStatus.ERROR:
+ msgType = DialogPage.ERROR;
+ break;
+ case IStatus.WARNING:
+ msgType = DialogPage.WARNING;
+ break;
+ default:
+ msgType = DialogPage.NONE;
+ break;
+ }
+ String defaultMessage =
+ AndroidNLS.UI_NewAndroidWidgetProjectMainPage_SubtitleCreateProject;
+ setMessage(status.isOK() ? defaultMessage : status.getMessage(), msgType);
+ }
+ };
+
+ /**
+ * Constructor
+ *
+ * @param project The selected project
+ */
+ public NewAndroidWidgetProjectMainPage(AndroidProject project)
+ {
+ super(PAGE_NAME);
+ this.project = project;
+ setTitle(AndroidNLS.UI_NewAndroidWidgetProjectMainPage_TitleCreateProject);
+ setDescription(AndroidNLS.UI_NewAndroidWidgetProjectMainPage_WizardProjectDescription);
+ setPageComplete(false);
+
+ }
+
+ /**
+ * Create the page SWT controls.
+ *
+ * @param parent The parent composite
+ */
+ public void createControl(Composite parent)
+ {
+ Composite composite = new Composite(parent, SWT.NULL);
+ composite.setFont(parent.getFont());
+
+ initializeDialogUnits(parent);
+
+ composite.setLayout(new GridLayout());
+ composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+
+ //only put the project and location groups: no vertical resize, no vertical style
+
+ ProjectNameGroup projectNameGroup = new ProjectNameGroup(composite, project);
+ projectNameGroup.setLayoutData(new GridData(SWT.FILL, SWT.NONE, true, false));
+
+ // Create Location Group
+ Group groupForLocation = new Group(composite, SWT.SHADOW_ETCHED_IN);
+ // Layout has 1 column
+ groupForLocation.setLayout(new GridLayout());
+ groupForLocation.setLayoutData(new GridData(SWT.FILL, SWT.NONE, true, false));
+ groupForLocation.setFont(composite.getFont());
+ groupForLocation.setText(AndroidNLS.UI_NewAndroidWidgetProjectMainPage_LabelContents);
+
+ WidgetLocationGroup locationGroup =
+ new WidgetLocationGroup(groupForLocation, project, this);
+ // End of Location Group
+
+ // Create SDK Group
+ //create sdk group with vertical resize, grabbing excedding space
+ Group groupForTarget = new Group(composite, SWT.SHADOW_ETCHED_IN);
+ groupForTarget.setLayout(new GridLayout());
+ groupForTarget.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+ groupForTarget.setFont(composite.getFont());
+ groupForTarget.setText(AndroidNLS.UI_NewAndroidProjectMainPage_LabelTarget);
+
+ final SdkTargetSelector mSdkTargetSelector = new SdkTargetSelector(groupForTarget, project);
+ //End of Target Creation
+
+ // Create Package Group
+ Group group = new Group(composite, SWT.SHADOW_ETCHED_IN);
+
+ // Layout has 1 column
+ group.setLayout(new GridLayout());
+ group.setLayoutData(new GridData(SWT.FILL, SWT.NONE, true, false));
+ group.setFont(composite.getFont());
+ group.setText(AndroidNLS.UI_NewAndroidProjectMainPage_LabelApplication);
+
+ final ApplicationGroup applicationGroup = new ApplicationGroup(group, project);
+
+ Listener listener = new Listener()
+ {
+
+ public void handleEvent(Event arg0)
+ {
+ applicationGroup.updateDefaultName();
+ applicationGroup.updateMinSdkVersion();
+ getContainer().updateButtons();
+ }
+ };
+
+ projectNameGroup.addListener(IWizardModel.MODIFIED, modelListener);
+ projectNameGroup.addListener(IWizardModel.MODIFIED, listener);
+ locationGroup.addListener(IWizardModel.MODIFIED, listener);
+ locationGroup.addListener(IWizardModel.MODIFIED, modelListener);
+ applicationGroup.addListener(IWizardModel.MODIFIED, modelListener);
+ mSdkTargetSelector.addListener(IWizardModel.MODIFIED, modelListener);
+ mSdkTargetSelector.addListener(IWizardModel.MODIFIED, listener);
+
+ //create application group with no vertical resize
+ setPageComplete(false);
+
+ // Show description the first time
+ setErrorMessage(null);
+ setMessage(null);
+
+ projectNameGroup.forceFocus();
+ setControl(composite);
+ composite.layout(true);
+ PlatformUI.getWorkbench().getHelpSystem().setHelp(parent, NEW_WIDGET_PROJECT_HELP);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.jface.dialogs.DialogPage#setButtonLayoutData(org.eclipse.swt.widgets.Button)
+ */
+ @Override
+ public GridData setButtonLayoutData(Button button)
+ {
+ return super.setButtonLayoutData(button);
+ }
+
+}
diff --git a/src/plugins/android/src/com/motorola/studio/android/wizards/widget/NewAndroidWidgetProjectWizard.java b/src/plugins/android/src/com/motorola/studio/android/wizards/widget/NewAndroidWidgetProjectWizard.java
new file mode 100644
index 0000000..5276c77
--- /dev/null
+++ b/src/plugins/android/src/com/motorola/studio/android/wizards/widget/NewAndroidWidgetProjectWizard.java
@@ -0,0 +1,283 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.motorola.studio.android.wizards.widget;
+
+import java.io.File;
+import java.lang.reflect.InvocationTargetException;
+
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IWorkspaceDescription;
+import org.eclipse.core.resources.IncrementalProjectBuilder;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.jdt.core.IClasspathEntry;
+import org.eclipse.jdt.core.IJavaProject;
+import org.eclipse.jdt.core.JavaCore;
+import org.eclipse.jdt.core.JavaModelException;
+import org.eclipse.jface.operation.IRunnableWithProgress;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.ui.INewWizard;
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.wizards.newresource.BasicNewProjectResourceWizard;
+
+import com.android.sdklib.IAndroidTarget;
+import com.motorola.studio.android.AndroidPlugin;
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.common.preferences.DialogWithToggleUtils;
+import com.motorola.studio.android.common.utilities.EclipseUtils;
+import com.motorola.studio.android.i18n.AndroidNLS;
+import com.motorola.studio.android.model.AndroidProject;
+import com.motorola.studio.android.model.AndroidProject.SourceTypes;
+
+/**
+ * Class that represents the Android New Widget Project Wizard
+ */
+public class NewAndroidWidgetProjectWizard extends BasicNewProjectResourceWizard implements
+ INewWizard
+{
+
+ private static final String WIZARD_BANNER = "icons/wizban/widget_provider_prj_wiz.png"; //$NON-NLS-1$
+
+ private final AndroidProject project = new AndroidProject();
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.wizard.Wizard#canFinish()
+ */
+ @Override
+ public boolean canFinish()
+ {
+ return (project.getStatus().getSeverity() != IStatus.ERROR)
+ && !project.needMoreInformation();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.wizards.newresource.BasicNewProjectResourceWizard#performFinish()
+ */
+ @Override
+ public boolean performFinish()
+ {
+ DoSave doSave = new DoSave();
+
+ try
+ {
+ getContainer().run(false, false, doSave);
+ }
+ catch (InvocationTargetException e)
+ {
+ String errMsg =
+ NLS.bind(
+ AndroidNLS.EXC_NewAndroidProjectWizard_AnErrorHasOccurredWhenCreatingTheProject,
+ e.getCause().getLocalizedMessage());
+ StudioLogger.error(NewAndroidWidgetProjectWizard.class, errMsg, e);
+
+ EclipseUtils.showErrorDialog(AndroidNLS.UI_GenericErrorDialogTitle, errMsg, null);
+ }
+ catch (InterruptedException e)
+ {
+ String errMsg =
+ NLS.bind(
+ AndroidNLS.EXC_NewAndroidProjectWizard_AnErrorHasOccurredWhenCreatingTheProject,
+ e.getLocalizedMessage());
+ StudioLogger.error(NewAndroidWidgetProjectWizard.class, errMsg, e);
+
+ EclipseUtils.showErrorDialog(AndroidNLS.UI_GenericErrorDialogTitle, errMsg, null);
+ }
+
+ if (doSave.saved)
+ {
+ // Collecting usage data for statistical purposes
+ try
+ {
+ StudioLogger.collectUsageData(StudioLogger.WHAT_APP_MANAGEMENT_CREATE,
+ StudioLogger.KIND_APP_MANAGEMENT, this.project.getSourceType().name()
+ .toLowerCase(), AndroidPlugin.PLUGIN_ID, AndroidPlugin.getDefault()
+ .getBundle().getVersion().toString());
+ }
+ catch (Throwable e)
+ {
+ //Do nothing, but error on the log should never prevent app from working
+ }
+ }
+
+ return doSave.saved;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.wizards.newresource.BasicNewProjectResourceWizard#init(org.eclipse.ui.IWorkbench, org.eclipse.jface.viewers.IStructuredSelection)
+ */
+ @Override
+ public void init(IWorkbench workbench, IStructuredSelection selection)
+ {
+ setWindowTitle(AndroidNLS.UI_NewAndroidWidgetProjectWizard_TitleNewProjectWizard);
+ setNeedsProgressMonitor(true);
+ setDefaultPageImageDescriptor(AndroidPlugin.getImageDescriptor(WIZARD_BANNER));
+
+ // Set project type to widget
+ project.setSourceType(SourceTypes.WIDGET);
+
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.wizard.Wizard#performCancel()
+ */
+ @Override
+ public boolean performCancel()
+ {
+ try
+ {
+ project.finalize();
+ }
+ catch (Throwable e)
+ {
+ StudioLogger.error(NewAndroidWidgetProjectWizard.class, e.getLocalizedMessage(), e);
+ }
+ return super.performCancel();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.wizards.newresource.BasicNewProjectResourceWizard#addPages()
+ */
+ @Override
+ public void addPages()
+ {
+ addPage(new NewAndroidWidgetProjectMainPage(project));
+ }
+
+ /**
+ * Implements an IRunnableWithProgress to run the save process
+ */
+ private class DoSave implements IRunnableWithProgress
+ {
+ private static final String OPHONE_JAR = "oms.jar";
+
+ private static final String OPHONESDK_PROMPT_KEY = "OphoneSDK"; //$NON-NLS-1$
+
+ boolean saved = false;
+
+ public void run(IProgressMonitor monitor) throws InvocationTargetException,
+ InterruptedException
+ {
+ // Gets the auto-building configuration to set it back in the end
+ final boolean autoBuild = ResourcesPlugin.getWorkspace().isAutoBuilding();
+
+ final IWorkspaceDescription wsd = ResourcesPlugin.getWorkspace().getDescription();
+ wsd.setAutoBuilding(false);
+ try
+ {
+ // Set auto-build off for performance reasons
+ ResourcesPlugin.getWorkspace().setDescription(wsd);
+ }
+ catch (CoreException e)
+ {
+ StudioLogger.error(NewAndroidWidgetProjectWizard.class,
+ "Error cleaning workspace after project creation: " + e.getMessage()); //$NON-NLS-1$
+ }
+
+ saved = project.save(getContainer(), monitor);
+ updatePerspective();
+
+ IProject p = ResourcesPlugin.getWorkspace().getRoot().getProject(project.getName());
+
+ addOphoneSDK(p);
+
+ try
+ {
+ p.build(IncrementalProjectBuilder.CLEAN_BUILD, new NullProgressMonitor());
+ }
+ catch (Exception e1)
+ {
+ StudioLogger.error(NewAndroidWidgetProjectWizard.class,
+ "Sleep error when cleaning workspace after project creation: " //$NON-NLS-1$
+ + e1.getMessage());
+ }
+
+ wsd.setAutoBuilding(autoBuild);
+ try
+ {
+ // roollback the auto-bulding setting to the original state
+ ResourcesPlugin.getWorkspace().setDescription(wsd);
+ }
+ catch (CoreException e)
+ {
+ StudioLogger.error(NewAndroidWidgetProjectWizard.class,
+ "Error cleaning workspace after project creation: " + e.getMessage()); //$NON-NLS-1$
+ }
+
+ }
+
+ private void addOphoneSDK(IProject p)
+ {
+ IAndroidTarget sdkTarget = project.getSdkTarget();
+ File platformLocation = new File(sdkTarget.getLocation());
+ File[] listFiles = platformLocation.listFiles();
+
+ boolean found = false;
+ int i = 0;
+ File file = null;
+ while (!found && (i < listFiles.length))
+ {
+ file = listFiles[i];
+ if (file.getName().equals(OPHONE_JAR))
+ {
+ found = true;
+ }
+ i++;
+ }
+
+ if (found)
+ {
+ boolean addClasspath =
+ DialogWithToggleUtils.showQuestion(OPHONESDK_PROMPT_KEY,
+ AndroidNLS.NewAndroidProjectWizard_OPhonePromptTitle,
+ AndroidNLS.NewAndroidProjectWizard_OPhonePromptMessage);
+
+ if (addClasspath)
+ {
+ IJavaProject javaProject = JavaCore.create(p);
+ if ((javaProject != null) && javaProject.exists())
+ {
+ try
+ {
+ javaProject.open(new NullProgressMonitor());
+ IClasspathEntry[] rawClasspath = javaProject.getRawClasspath();
+ IClasspathEntry[] newClasspath =
+ new IClasspathEntry[rawClasspath.length + 1];
+
+ System.arraycopy(rawClasspath, 0, newClasspath, 0, rawClasspath.length);
+ newClasspath[newClasspath.length - 1] =
+ JavaCore.newLibraryEntry(new Path(file.getAbsolutePath()),
+ null, null);
+ javaProject.setRawClasspath(newClasspath, new NullProgressMonitor());
+ }
+ catch (JavaModelException e)
+ {
+ StudioLogger.error(NewAndroidWidgetProjectWizard.class,
+ "Error while setting up the oms.jar on the project classpath: " //$NON-NLS-1$
+ + e.getMessage());
+ }
+ }
+ }
+ }
+ }
+ }
+
+}
diff --git a/src/plugins/android/templates/widget_project/AndroidWidgetManifest.template b/src/plugins/android/templates/widget_project/AndroidWidgetManifest.template
new file mode 100644
index 0000000..84b88e9
--- /dev/null
+++ b/src/plugins/android/templates/widget_project/AndroidWidgetManifest.template
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="PACKAGE"
+ android:versionCode="1"
+ android:versionName="1.0">
+ <application android:icon="@drawable/ic_launcher" android:label="APPLICATION_NAME">
+ACTIVITIES
+RECEIVERS
+ </application>
+USES-SDK
+</manifest>
diff --git a/src/plugins/android/templates/widget_project/WidgetProvider.template b/src/plugins/android/templates/widget_project/WidgetProvider.template
new file mode 100644
index 0000000..f8ced0e
--- /dev/null
+++ b/src/plugins/android/templates/widget_project/WidgetProvider.template
@@ -0,0 +1,30 @@
+package PACKAGE;
+
+import android.app.PendingIntent;
+import android.appwidget.AppWidgetManager;
+import android.appwidget.AppWidgetProvider;
+import android.content.Context;
+import android.content.Intent;
+import android.widget.RemoteViews;
+
+public class WidgetProvider extends AppWidgetProvider {
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ super.onReceive(context, intent);
+ }
+
+ @Override
+ public void onUpdate(Context context, AppWidgetManager appWidgetManager,
+ int[] appWidgetIds) {
+ RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.widget_initial_layout);
+ Intent intent = new Intent(context, ACTIVITY_NAME.class);
+ PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0);
+ views.setOnClickPendingIntent(R.id.widget_root, pendingIntent);
+
+ appWidgetManager.updateAppWidget(appWidgetIds[0], views);
+ }
+
+
+
+}
diff --git a/src/plugins/android/templates/widget_project/activity.template b/src/plugins/android/templates/widget_project/activity.template
new file mode 100644
index 0000000..11dadbc
--- /dev/null
+++ b/src/plugins/android/templates/widget_project/activity.template
@@ -0,0 +1,2 @@
+ <activity android:name=".ACTIVITY_NAME" android:label="APPLICATION_NAME"/>
+ \ No newline at end of file
diff --git a/src/plugins/android/templates/widget_project/receiver.template b/src/plugins/android/templates/widget_project/receiver.template
new file mode 100644
index 0000000..0f46332
--- /dev/null
+++ b/src/plugins/android/templates/widget_project/receiver.template
@@ -0,0 +1,6 @@
+ <receiver android:name="WidgetProvider">
+ <intent-filter>
+ <action android:name="android.appwidget.action.APPWIDGET_UPDATE"/>
+ </intent-filter>
+ <meta-data android:name="android.appwidget.provider" android:resource="@xml/widget_info"/>
+ </receiver> \ No newline at end of file
diff --git a/src/plugins/android/templates/widget_project/uses-sdk.template b/src/plugins/android/templates/widget_project/uses-sdk.template
new file mode 100644
index 0000000..8adae71
--- /dev/null
+++ b/src/plugins/android/templates/widget_project/uses-sdk.template
@@ -0,0 +1 @@
+ <uses-sdk android:minSdkVersion="MIN_SDK_VERSION" />
diff --git a/src/plugins/android/templates/widget_project/widget_info.xml b/src/plugins/android/templates/widget_project/widget_info.xml
new file mode 100644
index 0000000..853c627
--- /dev/null
+++ b/src/plugins/android/templates/widget_project/widget_info.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<appwidget-provider
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:minWidth="142dp"
+ android:minHeight="72dp"
+ android:updatePeriodMillis="86400000"
+ android:initialLayout="@layout/widget_initial_layout"
+ >
+</appwidget-provider>
diff --git a/src/plugins/android/templates/widget_project/widget_initial_layout.xml b/src/plugins/android/templates/widget_project/widget_initial_layout.xml
new file mode 100644
index 0000000..2024c25
--- /dev/null
+++ b/src/plugins/android/templates/widget_project/widget_initial_layout.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/widget_root"
+ android:layout_width="142dp"
+ android:orientation="vertical"
+ android:layout_height="72dp"
+ >
+ <ImageView
+ android:id="@+id/ImageView01"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:src="@drawable/ic_launcher"
+ android:layout_gravity="center_vertical|center_horizontal"/>
+ <TextView
+ android:text="MotoWidget"
+ android:id="@+id/TextView01"
+ android:layout_gravity="center_vertical|center_horizontal"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textColor="#FFFFFF"
+ android:background="#000000"/>
+
+</LinearLayout>
+
diff --git a/src/plugins/certmanager/.classpath b/src/plugins/certmanager/.classpath
new file mode 100644
index 0000000..10667af
--- /dev/null
+++ b/src/plugins/certmanager/.classpath
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry exported="true" kind="lib" path="lib/bcpkix-jdk15on-147.jar"/>
+ <classpathentry exported="true" kind="lib" path="lib/bcprov-jdk15on-147.jar"/>
+ <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/src/plugins/certmanager/.project b/src/plugins/certmanager/.project
new file mode 100644
index 0000000..ada541b
--- /dev/null
+++ b/src/plugins/certmanager/.project
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>com.motorolamobility.studio.android.certmanager</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.ManifestBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.SchemaBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.pde.PluginNature</nature>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+</projectDescription>
diff --git a/src/plugins/certmanager/META-INF/MANIFEST.MF b/src/plugins/certmanager/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..f646d4e
--- /dev/null
+++ b/src/plugins/certmanager/META-INF/MANIFEST.MF
@@ -0,0 +1,33 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: %pluginName
+Bundle-SymbolicName: com.motorolamobility.studio.android.certmanager;singleton:=true
+Bundle-Version: 5.0.0.qualifier
+Bundle-Activator: com.motorolamobility.studio.android.certmanager.CertificateManagerActivator
+Bundle-Vendor: %providerName
+Require-Bundle: org.eclipse.ui,
+ org.eclipse.core.runtime,
+ com.motorola.studio.android.common,
+ org.junit,
+ org.eclipse.ltk.ui.refactoring,
+ org.eclipse.equinox.security,
+ org.eclipse.core.resources,
+ org.eclipse.ui.ide,
+ org.eclipse.core.expressions
+Bundle-RequiredExecutionEnvironment: JavaSE-1.6
+Bundle-ActivationPolicy: lazy
+Bundle-Localization: plugin
+Bundle-ClassPath: .,
+ lib/bcprov-jdk15on-147.jar,
+ lib/bcpkix-jdk15on-147.jar
+Import-Package: org.eclipse.ui.actions
+Export-Package: com.motorolamobility.studio.android.certmanager,
+ com.motorolamobility.studio.android.certmanager.command,
+ com.motorolamobility.studio.android.certmanager.core,
+ com.motorolamobility.studio.android.certmanager.exception,
+ com.motorolamobility.studio.android.certmanager.job,
+ com.motorolamobility.studio.android.certmanager.packaging,
+ com.motorolamobility.studio.android.certmanager.packaging.sign,
+ com.motorolamobility.studio.android.certmanager.ui.model,
+ com.motorolamobility.studio.android.certmanager.ui.wizards,
+ com.motorolamobility.studio.android.certmanager.views
diff --git a/src/plugins/certmanager/build.properties b/src/plugins/certmanager/build.properties
new file mode 100644
index 0000000..f8f918c
--- /dev/null
+++ b/src/plugins/certmanager/build.properties
@@ -0,0 +1,9 @@
+source.. = src/
+output.. = bin/
+bin.includes = META-INF/,\
+ .,\
+ plugin.properties,\
+ plugin.xml,\
+ lib/bcprov-jdk15on-147.jar,\
+ lib/bcpkix-jdk15on-147.jar,\
+ icons/
diff --git a/src/plugins/certmanager/icons/backup_keystore.png b/src/plugins/certmanager/icons/backup_keystore.png
new file mode 100644
index 0000000..6621a9f
--- /dev/null
+++ b/src/plugins/certmanager/icons/backup_keystore.png
Binary files differ
diff --git a/src/plugins/certmanager/icons/certificate.png b/src/plugins/certmanager/icons/certificate.png
new file mode 100644
index 0000000..64bf06b
--- /dev/null
+++ b/src/plugins/certmanager/icons/certificate.png
Binary files differ
diff --git a/src/plugins/certmanager/icons/change_keystore_type.png b/src/plugins/certmanager/icons/change_keystore_type.png
new file mode 100644
index 0000000..f82f15b
--- /dev/null
+++ b/src/plugins/certmanager/icons/change_keystore_type.png
Binary files differ
diff --git a/src/plugins/certmanager/icons/change_keystore_type_disabled.png b/src/plugins/certmanager/icons/change_keystore_type_disabled.png
new file mode 100644
index 0000000..c9c5945
--- /dev/null
+++ b/src/plugins/certmanager/icons/change_keystore_type_disabled.png
Binary files differ
diff --git a/src/plugins/certmanager/icons/change_password_key.png b/src/plugins/certmanager/icons/change_password_key.png
new file mode 100644
index 0000000..dc1396b
--- /dev/null
+++ b/src/plugins/certmanager/icons/change_password_key.png
Binary files differ
diff --git a/src/plugins/certmanager/icons/change_password_keystore.png b/src/plugins/certmanager/icons/change_password_keystore.png
new file mode 100644
index 0000000..8f55c6d
--- /dev/null
+++ b/src/plugins/certmanager/icons/change_password_keystore.png
Binary files differ
diff --git a/src/plugins/certmanager/icons/create_key.png b/src/plugins/certmanager/icons/create_key.png
new file mode 100644
index 0000000..c8131b1
--- /dev/null
+++ b/src/plugins/certmanager/icons/create_key.png
Binary files differ
diff --git a/src/plugins/certmanager/icons/create_keystore.png b/src/plugins/certmanager/icons/create_keystore.png
new file mode 100644
index 0000000..c2ffade
--- /dev/null
+++ b/src/plugins/certmanager/icons/create_keystore.png
Binary files differ
diff --git a/src/plugins/certmanager/icons/delete_key.png b/src/plugins/certmanager/icons/delete_key.png
new file mode 100644
index 0000000..85e7de0
--- /dev/null
+++ b/src/plugins/certmanager/icons/delete_key.png
Binary files differ
diff --git a/src/plugins/certmanager/icons/delete_keystore.png b/src/plugins/certmanager/icons/delete_keystore.png
new file mode 100644
index 0000000..7bdab30
--- /dev/null
+++ b/src/plugins/certmanager/icons/delete_keystore.png
Binary files differ
diff --git a/src/plugins/certmanager/icons/import_entries.png b/src/plugins/certmanager/icons/import_entries.png
new file mode 100644
index 0000000..ebb6a11
--- /dev/null
+++ b/src/plugins/certmanager/icons/import_entries.png
Binary files differ
diff --git a/src/plugins/certmanager/icons/import_keystore.png b/src/plugins/certmanager/icons/import_keystore.png
new file mode 100644
index 0000000..e425ae9
--- /dev/null
+++ b/src/plugins/certmanager/icons/import_keystore.png
Binary files differ
diff --git a/src/plugins/certmanager/icons/key.png b/src/plugins/certmanager/icons/key.png
new file mode 100644
index 0000000..64bf06b
--- /dev/null
+++ b/src/plugins/certmanager/icons/key.png
Binary files differ
diff --git a/src/plugins/certmanager/icons/key_saved_password.png b/src/plugins/certmanager/icons/key_saved_password.png
new file mode 100644
index 0000000..59daec4
--- /dev/null
+++ b/src/plugins/certmanager/icons/key_saved_password.png
Binary files differ
diff --git a/src/plugins/certmanager/icons/keystore.png b/src/plugins/certmanager/icons/keystore.png
new file mode 100644
index 0000000..d9e5e90
--- /dev/null
+++ b/src/plugins/certmanager/icons/keystore.png
Binary files differ
diff --git a/src/plugins/certmanager/icons/keystore_incorrect_type.png b/src/plugins/certmanager/icons/keystore_incorrect_type.png
new file mode 100644
index 0000000..7b9325b
--- /dev/null
+++ b/src/plugins/certmanager/icons/keystore_incorrect_type.png
Binary files differ
diff --git a/src/plugins/certmanager/icons/keystore_saved_password.png b/src/plugins/certmanager/icons/keystore_saved_password.png
new file mode 100644
index 0000000..128ce8b
--- /dev/null
+++ b/src/plugins/certmanager/icons/keystore_saved_password.png
Binary files differ
diff --git a/src/plugins/certmanager/icons/ovr16/error_ovr.png b/src/plugins/certmanager/icons/ovr16/error_ovr.png
new file mode 100644
index 0000000..32319c2
--- /dev/null
+++ b/src/plugins/certmanager/icons/ovr16/error_ovr.png
Binary files differ
diff --git a/src/plugins/certmanager/icons/ovr16/warning_ovr.png b/src/plugins/certmanager/icons/ovr16/warning_ovr.png
new file mode 100644
index 0000000..f523652
--- /dev/null
+++ b/src/plugins/certmanager/icons/ovr16/warning_ovr.png
Binary files differ
diff --git a/src/plugins/certmanager/icons/properties.png b/src/plugins/certmanager/icons/properties.png
new file mode 100644
index 0000000..07c9c1c
--- /dev/null
+++ b/src/plugins/certmanager/icons/properties.png
Binary files differ
diff --git a/src/plugins/certmanager/icons/properties_disabled.png b/src/plugins/certmanager/icons/properties_disabled.png
new file mode 100644
index 0000000..8039b85
--- /dev/null
+++ b/src/plugins/certmanager/icons/properties_disabled.png
Binary files differ
diff --git a/src/plugins/certmanager/icons/refresh.png b/src/plugins/certmanager/icons/refresh.png
new file mode 100644
index 0000000..fa0204e
--- /dev/null
+++ b/src/plugins/certmanager/icons/refresh.png
Binary files differ
diff --git a/src/plugins/certmanager/icons/restore_keystore.png b/src/plugins/certmanager/icons/restore_keystore.png
new file mode 100644
index 0000000..2feac28
--- /dev/null
+++ b/src/plugins/certmanager/icons/restore_keystore.png
Binary files differ
diff --git a/src/plugins/certmanager/icons/sign_package.png b/src/plugins/certmanager/icons/sign_package.png
new file mode 100644
index 0000000..84efb17
--- /dev/null
+++ b/src/plugins/certmanager/icons/sign_package.png
Binary files differ
diff --git a/src/plugins/certmanager/icons/unsign_package.png b/src/plugins/certmanager/icons/unsign_package.png
new file mode 100644
index 0000000..759118b
--- /dev/null
+++ b/src/plugins/certmanager/icons/unsign_package.png
Binary files differ
diff --git a/src/plugins/certmanager/icons/view_icon.png b/src/plugins/certmanager/icons/view_icon.png
new file mode 100644
index 0000000..db32254
--- /dev/null
+++ b/src/plugins/certmanager/icons/view_icon.png
Binary files differ
diff --git a/src/plugins/certmanager/icons/wizban/backup_keystore_wiz.png b/src/plugins/certmanager/icons/wizban/backup_keystore_wiz.png
new file mode 100644
index 0000000..7370480
--- /dev/null
+++ b/src/plugins/certmanager/icons/wizban/backup_keystore_wiz.png
Binary files differ
diff --git a/src/plugins/certmanager/icons/wizban/change_keystore_type_wiz.png b/src/plugins/certmanager/icons/wizban/change_keystore_type_wiz.png
new file mode 100644
index 0000000..ff58711
--- /dev/null
+++ b/src/plugins/certmanager/icons/wizban/change_keystore_type_wiz.png
Binary files differ
diff --git a/src/plugins/certmanager/icons/wizban/create_key_wiz.png b/src/plugins/certmanager/icons/wizban/create_key_wiz.png
new file mode 100644
index 0000000..df7b681
--- /dev/null
+++ b/src/plugins/certmanager/icons/wizban/create_key_wiz.png
Binary files differ
diff --git a/src/plugins/certmanager/icons/wizban/create_keystore_wiz.png b/src/plugins/certmanager/icons/wizban/create_keystore_wiz.png
new file mode 100644
index 0000000..9134c5b
--- /dev/null
+++ b/src/plugins/certmanager/icons/wizban/create_keystore_wiz.png
Binary files differ
diff --git a/src/plugins/certmanager/icons/wizban/import_entries_wiz.png b/src/plugins/certmanager/icons/wizban/import_entries_wiz.png
new file mode 100644
index 0000000..bc3b978
--- /dev/null
+++ b/src/plugins/certmanager/icons/wizban/import_entries_wiz.png
Binary files differ
diff --git a/src/plugins/certmanager/icons/wizban/import_keystore_wiz.png b/src/plugins/certmanager/icons/wizban/import_keystore_wiz.png
new file mode 100644
index 0000000..0af83c4
--- /dev/null
+++ b/src/plugins/certmanager/icons/wizban/import_keystore_wiz.png
Binary files differ
diff --git a/src/plugins/certmanager/icons/wizban/restore_keystore_wiz.png b/src/plugins/certmanager/icons/wizban/restore_keystore_wiz.png
new file mode 100644
index 0000000..8e36046
--- /dev/null
+++ b/src/plugins/certmanager/icons/wizban/restore_keystore_wiz.png
Binary files differ
diff --git a/src/plugins/certmanager/icons/wizban/sign_package_wiz.png b/src/plugins/certmanager/icons/wizban/sign_package_wiz.png
new file mode 100644
index 0000000..7fcccb9
--- /dev/null
+++ b/src/plugins/certmanager/icons/wizban/sign_package_wiz.png
Binary files differ
diff --git a/src/plugins/certmanager/icons/wizban/unsign_package_wiz.png b/src/plugins/certmanager/icons/wizban/unsign_package_wiz.png
new file mode 100644
index 0000000..e99b408
--- /dev/null
+++ b/src/plugins/certmanager/icons/wizban/unsign_package_wiz.png
Binary files differ
diff --git a/src/plugins/certmanager/lib/bcpkix-jdk15on-147.jar b/src/plugins/certmanager/lib/bcpkix-jdk15on-147.jar
new file mode 100644
index 0000000..38fb503
--- /dev/null
+++ b/src/plugins/certmanager/lib/bcpkix-jdk15on-147.jar
Binary files differ
diff --git a/src/plugins/certmanager/lib/bcprov-jdk15on-147.jar b/src/plugins/certmanager/lib/bcprov-jdk15on-147.jar
new file mode 100644
index 0000000..0b80922
--- /dev/null
+++ b/src/plugins/certmanager/lib/bcprov-jdk15on-147.jar
Binary files differ
diff --git a/src/plugins/certmanager/plugin.properties b/src/plugins/certmanager/plugin.properties
new file mode 100644
index 0000000..eae5177
--- /dev/null
+++ b/src/plugins/certmanager/plugin.properties
@@ -0,0 +1,54 @@
+#
+# Copyright (C) 2012 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+pluginName=MOTODEV Studio for Android Key Manager Plug-in
+providerName=Motorola Mobility, Inc.
+
+certificate_manager_view_name=Signing and Keys
+import_keystore=Import Keystore
+import_keystore_tooltip=Import a keystore
+delete_keystore=Delete Keystore
+delete_keystore_tooltip=Delete a keystore
+unsign_package=Remove Package Signatures
+unsign_package_tooltip=Remove the signatures from Android packages
+sign_package=Sign Android Packages
+sign_package_tooltip=Sign Android packages
+certificate_properties=View Key Properties
+certificate_properties_tooltip=View a key's properties
+create_key=Create Key
+create_key_tooltip=Create key
+create_keystore=Create Keystore
+create_keytore_tooltip=Create keystore
+refresh=Refresh Keystore
+refresh_tooltip=Refresh a keystore
+delete_key=Delete Key
+delete_key_tooltip=Delete a key
+backup=Back Up Keystores
+restoreBackup=Restore From Backup
+backup_tooltip=Back up one or more keystores
+restoreBackup_tooltip=Restore a keystore from a backup
+
+decorator_error_node_label=Decoration for nodes with error
+
+change_keystore_passwd=Change Keystore Password
+change_keystore_passwd_tooltip=Change keystore password
+change_keystore_type=Change Keystore Type
+change_keystore_type_tooltip=Change keystore type
+change_key_password=Change Key Password
+change_key_password_tooltip=Change the password for a key
+decorator_warning_node_label=Decoration for nodes with warning
+import_keystore_entries=Import Entries
+import_keystore_entries_tooltip=Import entries \ No newline at end of file
diff --git a/src/plugins/certmanager/plugin.xml b/src/plugins/certmanager/plugin.xml
new file mode 100644
index 0000000..e05adbc
--- /dev/null
+++ b/src/plugins/certmanager/plugin.xml
@@ -0,0 +1,750 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?eclipse version="3.4"?>
+
+<!--
+ Copyright (C) 2012 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<plugin>
+ <extension
+ point="org.eclipse.ui.views">
+ <view
+ category="studioAndroidViewCategory"
+ class="com.motorolamobility.studio.android.certmanager.views.KeystoreManagerView"
+ icon="icons/view_icon.png"
+ id="com.motorola.studio.android.packaging.ui.signingview"
+ name="%certificate_manager_view_name"
+ restorable="true">
+ </view>
+ </extension>
+ <extension
+ point="org.eclipse.ui.perspectiveExtensions">
+ <perspectiveExtension
+ targetID="org.eclipse.jdt.ui.JavaPerspective">
+ <viewShortcut
+ id="com.motorola.studio.android.packaging.ui.signingview">
+ </viewShortcut>
+ <view
+ id="com.motorola.studio.android.packaging.ui.signingview"
+ minimized="false"
+ relationship="stack"
+ relative="org.eclipse.ui.views.ProblemView"
+ visible="true">
+ </view>
+ </perspectiveExtension>
+ </extension>
+ <extension point="org.eclipse.ui.menus">
+ <menuContribution
+ allPopups="false"
+ locationURI="toolbar:com.motorola.studio.android.packaging.ui.signingview">
+ <command
+ commandId="com.motorolamobility.studio.android.certmanager.refresh"
+ icon="icons/refresh.png"
+ label="%refresh"
+ style="push"
+ tooltip="%refresh_tooltip">
+ </command>
+ <command
+ commandId="com.motorolamobility.studio.android.certmanager.createkeystore"
+ icon="icons/create_keystore.png"
+ id="com.motorolamobility.studio.android.certmanager.createkeystore"
+ label="%create_keystore"
+ tooltip="%create_keytore_tooltip">
+ </command>
+ <command
+ commandId="com.motorolamobility.studio.android.certmanager.deleteKeystore"
+ icon="icons/delete_keystore.png"
+ label="%delete_keystore"
+ style="push"
+ tooltip="%delete_keystore_tooltip">
+ </command>
+ <command
+ commandId="com.motorolamobility.studio.android.certmanager.changeKeystorePassword"
+ icon="icons/change_password_keystore.png"
+ label="%change_keystore_passwd"
+ style="push"
+ tooltip="%change_keystore_passwd_tooltip">
+ </command>
+ <command
+ commandId="com.motorolamobility.studio.android.certmanager.changeKeystoreType"
+ disabledIcon="icons/change_keystore_type_disabled.png"
+ icon="icons/change_keystore_type.png"
+ label="%change_keystore_type"
+ style="push"
+ tooltip="%change_keystore_type_tooltip">
+ </command>
+ <command
+ commandId="com.motorolamobility.studio.android.certmanager.importkeystore"
+ icon="icons/import_keystore.png"
+ id="com.motorolamobility.studio.android.certmanager.importkeystoreinview"
+ label="%import_keystore"
+ tooltip="%import_keystore_tooltip">
+ </command>
+ <command
+ commandId="com.motorolamobility.studio.android.certmanager.importKeystoreEntries"
+ icon="icons/import_entries.png"
+ id="com.motorolamobility.studio.android.certmanager.importkeystoreentries"
+ label="%import_keystore_entries"
+ tooltip="%import_keystore_entries_tooltip">
+ </command>
+ <command
+ commandId="com.motorolamobility.studio.android.certmanager.backup"
+ icon="icons/backup_keystore.png"
+ label="%backup"
+ tooltip="%backup_tooltip">
+ </command>
+ <command
+ commandId="com.motorolamobility.studio.android.certmanager.restoreBackup"
+ icon="icons/restore_keystore.png"
+ id="com.motorolamobility.studio.android.certmanager.restoreBackup"
+ label="%restoreBackup"
+ tooltip="%restoreBackup_tooltip">
+ </command>
+ <separator
+ name="com.motorolamobility.studio.android.certmanager.separator1"
+ visible="true">
+ </separator>
+ <command
+ commandId="com.motorolamobility.studio.android.certmanager.createKey"
+ icon="icons/create_key.png"
+ label="%create_key"
+ style="push"
+ tooltip="%create_key_tooltip">
+ </command>
+ <command
+ commandId="com.motorolamobility.studio.android.certmanager.changeKeyPassword"
+ icon="icons/change_password_key.png"
+ label="%change_key_password"
+ style="push"
+ tooltip="%change_key_password_tooltip">
+ </command>
+ <command
+ commandId="com.motorolamobility.studio.android.certmanager.deleteEntry"
+ icon="icons/delete_key.png"
+ label="%delete_key"
+ style="push"
+ tooltip="%delete_key_tooltip">
+ </command>
+ <command
+ commandId="com.motorolamobility.studio.android.certmanager.certificateProperties"
+ disabledIcon="icons/properties_disabled.png"
+ icon="icons/properties.png"
+ label="%certificate_properties"
+ style="push"
+ tooltip="%certificate_properties_tooltip">
+ </command>
+ <separator
+ name="com.motorolamobility.studio.android.certmanager.separator2"
+ visible="true">
+ </separator>
+ <command
+ commandId="com.motorolamobility.studio.android.certmanager.addSignature"
+ icon="icons/sign_package.png"
+ id="com.motorolamobility.studio.android.certmanager.addsignatureinview"
+ label="%sign_package"
+ tooltip="%sign_package_tooltip">
+ </command>
+ <command
+ commandId="com.motorolamobility.studio.android.certmanager.removeSignature"
+ icon="icons/unsign_package.png"
+ id="com.motorolamobility.studio.android.certmanager.removesignatureinview"
+ label="%unsign_package"
+ tooltip="%unsign_package_tooltip">
+ </command>
+ </menuContribution>
+ <menuContribution
+ allPopups="false"
+ locationURI="com.motorola.studio.android.packaging.ui.signingview">
+ </menuContribution>
+ </extension>
+ <extension
+ point="org.eclipse.ui.commands">
+ <command
+ defaultHandler="com.motorolamobility.studio.android.certmanager.command.ImportKeystoreHandler"
+ description="%import_keystore"
+ id="com.motorolamobility.studio.android.certmanager.importkeystore"
+ name="%import_keystore">
+ </command>
+ <command
+ description="%delete_keystore"
+ id="com.motorolamobility.studio.android.certmanager.deleteKeystore"
+ name="%delete_keystore">
+ </command>
+ <command defaultHandler="com.motorolamobility.studio.android.certmanager.command.CreateKeystoreHandler"
+ description="%create_keystore"
+ id="com.motorolamobility.studio.android.certmanager.createkeystore"
+ name="%create_keystore">
+ </command>
+ <command
+ description="%certificate_properties"
+ id="com.motorolamobility.studio.android.certmanager.certificateProperties"
+ name="%certificate_properties">
+ </command>
+ <command
+ defaultHandler="com.motorolamobility.studio.android.certmanager.command.UnsignExternalPackagesHandler"
+ description="%unsign_package"
+ id="com.motorolamobility.studio.android.certmanager.removeSignature"
+ name="%unsign_package">
+ </command>
+ <command
+ description="%sign_package"
+ id="com.motorolamobility.studio.android.certmanager.addSignature"
+ name="%sign_package">
+ </command>
+ <command
+ description="%create_key"
+ id="com.motorolamobility.studio.android.certmanager.createKey"
+ name="%create_key">
+ </command>
+ <command
+ description="%refresh"
+ id="com.motorolamobility.studio.android.certmanager.refresh"
+ name="%refresh">
+ </command>
+ <command
+ description="%backup"
+ id="com.motorolamobility.studio.android.certmanager.backup"
+ name="%backup">
+ </command>
+ <command
+ defaultHandler="com.motorolamobility.studio.android.certmanager.command.RestoreBackupHandler"
+ description="%restoreBackup"
+ id="com.motorolamobility.studio.android.certmanager.restoreBackup"
+ name="%restoreBackup">
+ </command>
+ <command
+ description="%delete_key"
+ id="com.motorolamobility.studio.android.certmanager.deleteEntry"
+ name="%delete_key">
+ </command>
+ <command
+ description="%change_keystore_passwd"
+ id="com.motorolamobility.studio.android.certmanager.changeKeystorePassword"
+ name="%change_keystore_passwd">
+ </command>
+ <command
+ description="%change_key_password"
+ id="com.motorolamobility.studio.android.certmanager.changeKeyPassword"
+ name="%change_key_password">
+ </command>
+ <command
+ description="%change_keystore_type"
+ id="com.motorolamobility.studio.android.certmanager.changeKeystoreType"
+ name="%change_keystore_type">
+ </command>
+ <command
+ description="%import_keystore_entries"
+ id="com.motorolamobility.studio.android.certmanager.importKeystoreEntries"
+ name="%import_keystore_entries">
+ </command>
+ </extension>
+ <extension
+ point="org.eclipse.ui.popupMenus">
+ <objectContribution
+ adaptable="false"
+ id="com.motorolamobility.studio.android.certmanager.core.ui.action.KeyStoreModel"
+ objectClass="com.motorolamobility.studio.android.certmanager.ui.model.KeyStoreNode">
+ <action
+ class="com.motorolamobility.studio.android.certmanager.ui.action.PopupMenuActionDelegate"
+ definitionId="com.motorolamobility.studio.android.certmanager.backup"
+ icon="icons/backup_keystore.png"
+ id="com.motorolamobility.studio.android.certmanager.core.ui.action.backupKeystore"
+ label="%backup">
+ <enablement>
+ <objectState
+ name="com.motorolamobility.studio.android.certmanager.core.property.nodeStatus"
+ value="com.motorolamobility.studio.android.certmanager.core.property.keystoreTypeOk">
+ </objectState>
+ </enablement>
+ </action>
+ <action
+ class="com.motorolamobility.studio.android.certmanager.ui.action.PopupMenuActionDelegate"
+ definitionId="com.motorolamobility.studio.android.certmanager.deleteKeystore"
+ icon="icons/delete_keystore.png"
+ id="com.motorolamobility.studio.android.certmanager.core.ui.action.deleteKeystore"
+ label="%delete_keystore">
+ </action>
+ <action
+ class="com.motorolamobility.studio.android.certmanager.ui.action.PopupMenuActionDelegate"
+ definitionId="com.motorolamobility.studio.android.certmanager.refresh"
+ enablesFor="1"
+ icon="icons/refresh.png"
+ id="com.motorolamobility.studio.android.certmanager.core.ui.action.refresh"
+ label="%refresh">
+ </action>
+ <action
+ class="com.motorolamobility.studio.android.certmanager.ui.action.PopupMenuActionDelegate"
+ definitionId="com.motorolamobility.studio.android.certmanager.changeKeystorePassword"
+ enablesFor="1"
+ icon="icons/change_password_keystore.png"
+ id="com.motorolamobility.studio.android.certmanager.core.ui.action.changeKeystorePassword"
+ label="%change_keystore_passwd">
+ <enablement>
+ <objectState
+ name="com.motorolamobility.studio.android.certmanager.core.property.nodeStatus"
+ value="com.motorolamobility.studio.android.certmanager.core.property.keystoreTypeOk">
+ </objectState>
+ </enablement>
+ </action>
+ <action
+ class="com.motorolamobility.studio.android.certmanager.ui.action.PopupMenuActionDelegate"
+ definitionId="com.motorolamobility.studio.android.certmanager.changeKeystoreType"
+ enablesFor="1"
+ icon="icons/change_keystore_type.png"
+ id="com.motorolamobility.studio.android.certmanager.core.ui.action.changeKeystoreType"
+ label="%change_keystore_type">
+ <enablement>
+ <objectState
+ name="com.motorolamobility.studio.android.certmanager.core.property.nodeStatus"
+ value="com.motorolamobility.studio.android.certmanager.core.property.keystoreTypeOk">
+ </objectState>
+ </enablement>
+ </action>
+ <action
+ class="com.motorolamobility.studio.android.certmanager.ui.action.PopupMenuActionDelegate"
+ definitionId="com.motorolamobility.studio.android.certmanager.importKeystoreEntries"
+ enablesFor="1"
+ icon="icons/import_entries.png"
+ id="com.motorolamobility.studio.android.certmanager.core.ui.action.importKeystoreEntries"
+ label="%import_keystore_entries">
+ <enablement>
+ <objectState
+ name="com.motorolamobility.studio.android.certmanager.core.property.nodeStatus"
+ value="com.motorolamobility.studio.android.certmanager.core.property.keystoreTypeOk">
+ </objectState>
+ </enablement>
+ </action>
+ <action
+ class="com.motorolamobility.studio.android.certmanager.ui.action.PopupMenuActionDelegate"
+ definitionId="com.motorolamobility.studio.android.certmanager.createKey"
+ enablesFor="1"
+ icon="icons/create_key.png"
+ id="com.motorolamobility.studio.android.certmanager.core.ui.action.createKey"
+ label="%create_key">
+ <enablement>
+ <objectState
+ name="com.motorolamobility.studio.android.certmanager.core.property.nodeStatus"
+ value="com.motorolamobility.studio.android.certmanager.core.property.keystoreTypeOk">
+ </objectState>
+ </enablement>
+ </action>
+ <action
+ class="com.motorolamobility.studio.android.certmanager.ui.action.PopupMenuActionDelegate"
+ definitionId="com.motorolamobility.studio.android.certmanager.addSignature"
+ enablesFor="1"
+ icon="icons/sign_package.png"
+ id="com.motorolamobility.studio.android.certmanager.core.ui.action.addSignature"
+ label="%sign_package">
+ <enablement>
+ <objectState
+ name="com.motorolamobility.studio.android.certmanager.core.property.nodeStatus"
+ value="com.motorolamobility.studio.android.certmanager.core.property.keystoreTypeOk">
+ </objectState>
+ </enablement>
+ </action>
+ </objectContribution>
+ <objectContribution
+ adaptable="false"
+ id="com.motorolamobility.studio.android.certmanager.core.ui.action.KeyStoreEntry"
+ objectClass="com.motorolamobility.studio.android.certmanager.ui.model.EntryNode">
+ <action
+ class="com.motorolamobility.studio.android.certmanager.ui.action.PopupMenuActionDelegate"
+ definitionId="com.motorolamobility.studio.android.certmanager.certificateProperties"
+ enablesFor="1"
+ icon="icons/properties.png"
+ id="com.motorolamobility.studio.android.certmanager.core.ui.action.certificateProperties"
+ label="%certificate_properties">
+ </action>
+ <action
+ class="com.motorolamobility.studio.android.certmanager.ui.action.PopupMenuActionDelegate"
+ definitionId="com.motorolamobility.studio.android.certmanager.deleteEntry"
+ icon="icons/delete_key.png"
+ id="com.motorolamobility.studio.android.certmanager.core.ui.action.deleteEntry"
+ label="%delete_key">
+ </action>
+ <action
+ class="com.motorolamobility.studio.android.certmanager.ui.action.PopupMenuActionDelegate"
+ definitionId="com.motorolamobility.studio.android.certmanager.changeKeyPassword"
+ enablesFor="1"
+ icon="icons/change_password_key.png"
+ id="com.motorolamobility.studio.android.certmanager.core.ui.action.changeKeyPassword"
+ label="%change_key_password">
+ </action>
+ </objectContribution>
+ </extension>
+ <extension
+ point="org.eclipse.ui.decorators">
+ <decorator
+ adaptable="false"
+ icon="icons/ovr16/error_ovr.png"
+ id="com.motorolamobility.studio.android.certmanager.core.ui.decorator.errorNode"
+ label="%decorator_error_node_label"
+ lightweight="true"
+ location="BOTTOM_RIGHT"
+ state="true">
+ <enablement>
+ <and>
+ <objectClass
+ name="com.motorolamobility.studio.android.certmanager.ui.model.ITreeNode">
+ </objectClass>
+ <objectState
+ name="com.motorolamobility.studio.android.certmanager.core.property.nodeStatus"
+ value="com.motorolamobility.studio.android.certmanager.core.property.nodeStatusError">
+ </objectState>
+ </and>
+ </enablement>
+ </decorator>
+ <decorator
+ adaptable="false"
+ icon="icons/ovr16/warning_ovr.png"
+ id="com.motorolamobility.studio.android.certmanager.core.ui.decorator.warningNode"
+ label="%decorator_warning_node_label"
+ lightweight="true"
+ location="BOTTOM_RIGHT"
+ state="true">
+ <enablement>
+ <and>
+ <objectClass
+ name="com.motorolamobility.studio.android.certmanager.ui.model.ITreeNode">
+ </objectClass>
+ <objectState
+ name="com.motorolamobility.studio.android.certmanager.core.property.nodeStatus"
+ value="com.motorolamobility.studio.android.certmanager.core.property.nodeStatusWarning">
+ </objectState>
+ </and>
+ </enablement>
+ </decorator>
+ </extension>
+ <extension
+ point="org.eclipse.ui.handlers">
+ <handler
+ class="com.motorolamobility.studio.android.certmanager.command.DeleteKeystoreHandler"
+ commandId="com.motorolamobility.studio.android.certmanager.deleteKeystore">
+ <activeWhen>
+ <with
+ variable="activeWorkbenchWindow">
+ <instanceof
+ value="org.eclipse.ui.IWorkbenchWindow">
+ </instanceof>
+ </with>
+ </activeWhen>
+ <enabledWhen>
+ <with
+ variable="selection">
+ <iterate
+ ifEmpty="false">
+ <adapt
+ type="com.motorolamobility.studio.android.certmanager.ui.model.IKeyStore">
+ </adapt>
+ </iterate>
+ <count
+ value="+">
+ </count>
+ </with>
+ </enabledWhen>
+ </handler>
+ <handler
+ class="com.motorolamobility.studio.android.certmanager.command.CreateKeyHandler"
+ commandId="com.motorolamobility.studio.android.certmanager.createKey">
+ <activeWhen>
+ <with
+ variable="activeWorkbenchWindow">
+ <instanceof
+ value="org.eclipse.ui.IWorkbenchWindow">
+ </instanceof>
+ </with>
+ </activeWhen>
+ <enabledWhen>
+ <with
+ variable="selection">
+ <iterate
+ ifEmpty="false">
+ <adapt
+ type="com.motorolamobility.studio.android.certmanager.ui.model.IKeyStore">
+ </adapt>
+ <test
+ property="com.motorolamobility.studio.android.certmanager.core.property.nodeStatus"
+ value="com.motorolamobility.studio.android.certmanager.core.property.keystoreTypeOk">
+ </test>
+ </iterate>
+ <count
+ value="1">
+ </count>
+ </with>
+ </enabledWhen>
+ </handler>
+ <handler
+ class="com.motorolamobility.studio.android.certmanager.command.CertificatePropertiesHandler"
+ commandId="com.motorolamobility.studio.android.certmanager.certificateProperties">
+ <activeWhen>
+ <with
+ variable="activeWorkbenchWindow">
+ <instanceof
+ value="org.eclipse.ui.IWorkbenchWindow">
+ </instanceof>
+ </with>
+ </activeWhen>
+ <enabledWhen>
+ <with
+ variable="selection">
+ <iterate
+ ifEmpty="false">
+ <adapt
+ type="com.motorolamobility.studio.android.certmanager.ui.model.IKeyStoreEntry">
+ </adapt>
+ </iterate>
+ <count
+ value="1">
+ </count>
+ </with>
+ </enabledWhen>
+ </handler>
+ <handler
+ class="com.motorolamobility.studio.android.certmanager.command.RefreshHandler"
+ commandId="com.motorolamobility.studio.android.certmanager.refresh">
+ <activeWhen>
+ <with
+ variable="activeWorkbenchWindow">
+ <instanceof
+ value="org.eclipse.ui.IWorkbenchWindow">
+ </instanceof>
+ </with>
+ </activeWhen>
+ <enabledWhen>
+ <with
+ variable="selection">
+ <iterate
+ ifEmpty="false">
+ <adapt
+ type="com.motorolamobility.studio.android.certmanager.ui.model.IKeyStore">
+ </adapt>
+ </iterate>
+ <count
+ value="1">
+ </count>
+ </with>
+ </enabledWhen>
+ </handler>
+ <handler
+ class="com.motorolamobility.studio.android.certmanager.command.ChangePasswordKeystoreHandler"
+ commandId="com.motorolamobility.studio.android.certmanager.changeKeystorePassword">
+ <activeWhen>
+ <with
+ variable="activeWorkbenchWindow">
+ <instanceof
+ value="org.eclipse.ui.IWorkbenchWindow">
+ </instanceof>
+ </with>
+ </activeWhen>
+ <enabledWhen>
+ <with
+ variable="selection">
+ <iterate
+ ifEmpty="false">
+ <adapt
+ type="com.motorolamobility.studio.android.certmanager.ui.model.IKeyStore">
+ </adapt>
+ <test
+ property="com.motorolamobility.studio.android.certmanager.core.property.nodeStatus"
+ value="com.motorolamobility.studio.android.certmanager.core.property.keystoreTypeOk">
+ </test>
+ </iterate>
+ <count
+ value="1">
+ </count>
+ </with>
+ </enabledWhen>
+ </handler>
+ <handler
+ class="com.motorolamobility.studio.android.certmanager.command.DeleteKeyHandler"
+ commandId="com.motorolamobility.studio.android.certmanager.deleteEntry">
+ <activeWhen>
+ <with
+ variable="activeWorkbenchWindow">
+ <instanceof
+ value="org.eclipse.ui.IWorkbenchWindow">
+ </instanceof>
+ </with>
+ </activeWhen>
+ <enabledWhen>
+ <with
+ variable="selection">
+ <iterate
+ ifEmpty="false">
+ <adapt
+ type="com.motorolamobility.studio.android.certmanager.ui.model.IKeyStoreEntry">
+ </adapt>
+ </iterate>
+ <count
+ value="+">
+ </count>
+ </with>
+ </enabledWhen>
+ </handler>
+ <handler
+ class="com.motorolamobility.studio.android.certmanager.command.ChangePasswordKeyHandler"
+ commandId="com.motorolamobility.studio.android.certmanager.changeKeyPassword">
+ <activeWhen>
+ <with
+ variable="activeWorkbenchWindow">
+ <instanceof
+ value="org.eclipse.ui.IWorkbenchWindow">
+ </instanceof>
+ </with>
+ </activeWhen>
+ <enabledWhen>
+ <with
+ variable="selection">
+ <iterate
+ ifEmpty="false">
+ <adapt
+ type="com.motorolamobility.studio.android.certmanager.ui.model.IKeyStoreEntry">
+ </adapt>
+ </iterate>
+ <count
+ value="1">
+ </count>
+ </with>
+ </enabledWhen>
+ </handler>
+ <handler
+ class="com.motorolamobility.studio.android.certmanager.command.ChangeKeyStoreTypeHandler"
+ commandId="com.motorolamobility.studio.android.certmanager.changeKeystoreType">
+ <activeWhen>
+ <with
+ variable="activeWorkbenchWindow">
+ <instanceof
+ value="org.eclipse.ui.IWorkbenchWindow">
+ </instanceof>
+ </with>
+ </activeWhen>
+ <enabledWhen>
+ <with
+ variable="selection">
+ <iterate
+ ifEmpty="false">
+ <adapt
+ type="com.motorolamobility.studio.android.certmanager.ui.model.IKeyStore">
+ </adapt>
+ <test
+ property="com.motorolamobility.studio.android.certmanager.core.property.nodeStatus"
+ value="com.motorolamobility.studio.android.certmanager.core.property.keystoreTypeOk">
+ </test>
+ </iterate>
+ <count
+ value="1">
+ </count>
+ </with>
+ </enabledWhen>
+ </handler>
+ <handler
+ class="com.motorolamobility.studio.android.certmanager.command.ImportKeyStoreEntriesHandler"
+ commandId="com.motorolamobility.studio.android.certmanager.importKeystoreEntries">
+ <activeWhen>
+ <with
+ variable="activeWorkbenchWindow">
+ <instanceof
+ value="org.eclipse.ui.IWorkbenchWindow">
+ </instanceof>
+ </with>
+ </activeWhen>
+ <enabledWhen>
+ <with
+ variable="selection">
+ <iterate
+ ifEmpty="true">
+ <adapt
+ type="com.motorolamobility.studio.android.certmanager.ui.model.IKeyStore">
+ </adapt>
+ <test
+ property="com.motorolamobility.studio.android.certmanager.core.property.nodeStatus"
+ value="com.motorolamobility.studio.android.certmanager.core.property.keystoreTypeOk">
+ </test>
+ </iterate>
+ <count
+ value="1">
+ </count>
+ </with>
+ </enabledWhen>
+ </handler>
+ <handler
+ class="com.motorolamobility.studio.android.certmanager.command.BackupHandler"
+ commandId="com.motorolamobility.studio.android.certmanager.backup">
+ <activeWhen>
+ <with
+ variable="activeWorkbenchWindow">
+ <instanceof
+ value="org.eclipse.ui.IWorkbenchWindow">
+ </instanceof>
+ </with>
+ </activeWhen>
+ <enabledWhen>
+ <with
+ variable="selection">
+ <iterate
+ ifEmpty="false">
+ <adapt
+ type="com.motorolamobility.studio.android.certmanager.ui.model.IKeyStore">
+ </adapt>
+ <test
+ property="com.motorolamobility.studio.android.certmanager.core.property.nodeStatus"
+ value="com.motorolamobility.studio.android.certmanager.core.property.keystoreTypeOk">
+ </test>
+ </iterate>
+ </with>
+ </enabledWhen>
+ </handler>
+ <handler
+ class="com.motorolamobility.studio.android.certmanager.command.SignExternalPackagesHandler"
+ commandId="com.motorolamobility.studio.android.certmanager.addSignature">
+ <activeWhen>
+ <with
+ variable="activeWorkbenchWindow">
+ <instanceof
+ value="org.eclipse.ui.IWorkbenchWindow">
+ </instanceof>
+ </with>
+ </activeWhen>
+ <enabledWhen>
+ <with
+ variable="selection">
+ <iterate
+ ifEmpty="false">
+ <adapt
+ type="com.motorolamobility.studio.android.certmanager.ui.model.IKeyStore">
+ </adapt>
+ <test
+ property="com.motorolamobility.studio.android.certmanager.core.property.nodeStatus"
+ value="com.motorolamobility.studio.android.certmanager.core.property.keystoreTypeOk">
+ </test>
+ </iterate>
+ <count
+ value="1">
+ </count>
+ </with>
+ </enabledWhen>
+ </handler>
+ </extension>
+ <extension
+ point="org.eclipse.core.expressions.propertyTesters">
+ <propertyTester
+ class="com.motorolamobility.studio.android.certmanager.property.tester.TreeNodeTester"
+ id="com.motorolamobility.studio.android.certmanager.property.TreeNodeTesterId"
+ namespace="com.motorolamobility.studio.android.certmanager.core.property"
+ properties="nodeStatus"
+ type="com.motorolamobility.studio.android.certmanager.ui.model.IKeyStore">
+ </propertyTester>
+ </extension>
+</plugin>
diff --git a/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/CertificateManagerActivator.java b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/CertificateManagerActivator.java
new file mode 100644
index 0000000..156c5f3
--- /dev/null
+++ b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/CertificateManagerActivator.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.motorolamobility.studio.android.certmanager;
+
+import org.eclipse.ui.IViewPart;
+import org.eclipse.ui.plugin.AbstractUIPlugin;
+import org.osgi.framework.BundleContext;
+
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.common.utilities.EclipseUtils;
+import com.motorolamobility.studio.android.certmanager.views.KeystoreManagerView;
+
+/**
+ * The activator class controls the plug-in life cycle
+ */
+public class CertificateManagerActivator extends AbstractUIPlugin
+{
+
+ public static final String PLUGIN_ID = "com.motorolamobility.studio.android.certmanager"; //$NON-NLS-1$
+
+ public static final String UNSIGN_EXTERNAL_PKG_WIZARD_CONTEXT_HELP_ID = PLUGIN_ID
+ + ".unsign_external_pkg_wiz";
+
+ public static final String REMOVE_SIGNATURE_WIZ_BAN = "icons/wizban/unsign_package_wiz.png";
+
+ public static final String SIGN_EXTERNAL_PKG_WIZARD_CONTEXT_HELP_ID = PLUGIN_ID
+ + ".sign_external_pkg_wiz";
+
+ public static final String SIGNATURE_WIZ_BAN = "icons/wizban/sign_package_wiz.png";
+
+ /**
+ * The manifest version
+ */
+ public static final String MANIFEST_VERSION = "1.0";
+
+ /**
+ * Manifest attribute created by
+ */
+ public static final String CREATED_BY_FIELD = "Created-By";
+
+ /**
+ * Value of Created by attribute
+ */
+ public static final String CREATED_BY_FIELD_VALUE = "MOTODEV Studio for Android";
+
+ /**
+ * Package metainf directory name
+ */
+ public static final String METAFILES_DIR = "META-INF";
+
+ /**
+ * The package manifest file name
+ */
+ public static final String MANIFEST_FILE_NAME = "MANIFEST.MF";
+
+ /**
+ * Jar separator
+ */
+ public static final String JAR_SEPARATOR = "/";
+
+ /**
+ * Prefix to be used in temp files.
+ * */
+ public static final String TEMP_FILE_PREFIX = "tmppkg_";
+
+ /**
+ * The package extension.
+ */
+ public static final String PACKAGE_EXTENSION = "apk";
+
+ /**
+ * The default package destination extension
+ */
+ public static final String PACKAGE_PROJECT_DESTINATION = "dist";
+
+ // The shared instance
+ private static CertificateManagerActivator plugin;
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.ui.plugin.AbstractUIPlugin#start(org.osgi.framework.BundleContext)
+ */
+ @Override
+ public void start(BundleContext context) throws Exception
+ {
+ StudioLogger.debug(CertificateManagerActivator.class,
+ "Starting MOTODEV Studio for Android Key Manager Plugin...");
+
+ super.start(context);
+ plugin = this;
+
+ StudioLogger.debug(CertificateManagerActivator.class,
+ "MOTODEV Studio for Android Key Manager Plugin started.");
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.ui.plugin.AbstractUIPlugin#stop(org.osgi.framework.BundleContext)
+ */
+ @Override
+ public void stop(BundleContext context) throws Exception
+ {
+ plugin = null;
+ super.stop(context);
+ }
+
+ /**
+ * Returns the shared instance
+ *
+ * @return the shared instance
+ */
+ public static CertificateManagerActivator getDefault()
+ {
+ return plugin;
+ }
+
+ /**
+ * The certificate manager plugin declares one view that is used to show the keystores.
+ * */
+ public static KeystoreManagerView getKeyStoremManagerView()
+ {
+ IViewPart view = EclipseUtils.getActiveView(KeystoreManagerView.ID);
+
+ if (view instanceof KeystoreManagerView)
+ {
+ return (KeystoreManagerView) view;
+ }
+
+ return null;
+ }
+
+}
diff --git a/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/command/AbstractHandler2.java b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/command/AbstractHandler2.java
new file mode 100644
index 0000000..358c511
--- /dev/null
+++ b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/command/AbstractHandler2.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.motorolamobility.studio.android.certmanager.command;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.IHandler2;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.ui.PlatformUI;
+
+import com.motorolamobility.studio.android.certmanager.ui.model.ITreeNode;
+
+/**
+ * This abstract handler adds convenient methods, like methods to retrieve the current selection.
+ */
+public abstract class AbstractHandler2 extends AbstractHandler implements IHandler2
+{
+
+ /**
+ * Retrieves the list of selected nodes.
+ * */
+ @SuppressWarnings("unchecked")
+ protected List<ITreeNode> getSelection()
+ {
+ List<ITreeNode> selectedNodes = new ArrayList<ITreeNode>(1);
+ ISelection selection =
+ PlatformUI.getWorkbench().getActiveWorkbenchWindow().getSelectionService()
+ .getSelection();
+ if (selection instanceof IStructuredSelection)
+ {
+ IStructuredSelection treeSelection = (IStructuredSelection) selection;
+ List<Object> selectedElements = treeSelection.toList();
+
+ for (Object selectedObject : selectedElements)
+ {
+ if (selectedObject instanceof ITreeNode)
+ {
+ selectedNodes.add((ITreeNode) selectedObject);
+ }
+ }
+ }
+
+ return selectedNodes;
+ }
+
+}
diff --git a/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/command/BackupHandler.java b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/command/BackupHandler.java
new file mode 100644
index 0000000..925a69d
--- /dev/null
+++ b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/command/BackupHandler.java
@@ -0,0 +1,265 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.studio.android.certmanager.command;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.GregorianCalendar;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Properties;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipOutputStream;
+
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.core.commands.IHandler;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.jface.dialogs.Dialog;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.ui.PlatformUI;
+
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.common.utilities.EclipseUtils;
+import com.motorola.studio.android.common.utilities.FileUtil;
+import com.motorolamobility.studio.android.certmanager.core.KeyStoreManager;
+import com.motorolamobility.studio.android.certmanager.exception.KeyStoreManagerException;
+import com.motorolamobility.studio.android.certmanager.i18n.CertificateManagerNLS;
+import com.motorolamobility.studio.android.certmanager.ui.dialogs.BackupDialog;
+import com.motorolamobility.studio.android.certmanager.ui.model.IKeyStore;
+import com.motorolamobility.studio.android.certmanager.ui.model.ITreeNode;
+
+/**
+ * Handler to execute the backup wizard.
+ * */
+public class BackupHandler extends AbstractHandler2 implements IHandler
+{
+
+ public static final String KS_TYPES_FILENAME = "KsTypes.csv"; //$NON-NLS-1$
+
+ private static final String KEYSTORE_EXT = ".keystore"; //$NON-NLS-1$
+
+ private static Date lastBackupDate = new Date();
+
+ private final Calendar cal = GregorianCalendar.getInstance();
+
+ @Override
+ public Object execute(ExecutionEvent event) throws ExecutionException
+ {
+ BackupDialog dialog =
+ new BackupDialog(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell());
+ try
+ {
+ List<IKeyStore> keyStores = KeyStoreManager.getInstance().getKeyStores();
+ List<String> initialInputList = new ArrayList<String>(keyStores.size());
+ for (int i = 0; i < keyStores.size(); i++)
+ {
+ boolean insert = true;
+ IKeyStore keyStore = keyStores.get(i);
+ if (keyStore instanceof ITreeNode)
+ {
+ ITreeNode ksNode = (ITreeNode) keyStore;
+ IStatus nodeStatus = ksNode.getNodeStatus();
+ if (!nodeStatus.isOK()
+ && (nodeStatus.getCode() == IKeyStore.WRONG_KEYSTORE_TYPE_ERROR_CODE))
+ {
+ insert = false;
+ }
+ }
+ if (insert)
+ {
+ initialInputList.add(keyStore.getFile().getAbsolutePath());
+ }
+ }
+
+ dialog.setInput(initialInputList.toArray(new String[initialInputList.size()]));
+ dialog.selectKeyStores(getSelection());
+ }
+ catch (KeyStoreManagerException e)
+ {
+ throw new ExecutionException(e.getLocalizedMessage());
+ }
+
+ int diagReturn = dialog.open();
+ if (diagReturn == Dialog.OK)
+ {
+ File archiveFile = dialog.getArchiveFile();
+ List<String> selectedKeyStores = dialog.getSelectedKeyStores();
+ if (FileUtil.canWrite(archiveFile))
+ {
+ try
+ {
+ updateBackupDate(selectedKeyStores);
+ createZipArchive(archiveFile, selectedKeyStores);
+
+ }
+ catch (KeyStoreManagerException e)
+ {
+ EclipseUtils.showErrorDialog(
+ CertificateManagerNLS.BackupHandler_Error_BackUp_Title,
+ CertificateManagerNLS.BackupHandler_Error_Setting_Date);
+ }
+ catch (IOException e)
+ {
+ EclipseUtils.showErrorDialog(
+ CertificateManagerNLS.BackupHandler_Error_BackUp_Title, NLS.bind(
+ CertificateManagerNLS.BackupHandler_Error_Writing_Archive,
+ archiveFile));
+ }
+ }
+ else
+ {
+ EclipseUtils.showErrorDialog(
+ CertificateManagerNLS.BackupHandler_Error_BackUp_Title, NLS.bind(
+ CertificateManagerNLS.BackupHandler_Error_Writing_Archive,
+ archiveFile));
+ }
+
+ }
+ return null;
+ }
+
+ private void updateBackupDate(List<String> selectedKeyStores) throws KeyStoreManagerException
+ {
+ KeyStoreManager keyStoreManager = KeyStoreManager.getInstance();
+ List<IKeyStore> keyStores = keyStoreManager.getKeyStores();
+ Long lastDateInMillis = cal.getTimeInMillis();
+ lastBackupDate.setTime(lastDateInMillis);
+
+ for (IKeyStore keyStore : keyStores)
+ {
+ if (selectedKeyStores.contains(keyStore.getFile().getAbsolutePath()))
+ {
+ keyStore.setLastBackupDate(lastBackupDate);
+ }
+ }
+
+ }
+
+ private void createZipArchive(File zipFile, List<String> filePaths) throws IOException,
+ KeyStoreManagerException
+ {
+ ZipOutputStream zos = null;
+ FileInputStream in = null;
+ try
+ {
+ zos = new ZipOutputStream(new FileOutputStream(zipFile));
+ Iterator<String> it = filePaths.iterator();
+ List<String> entriesNames = new ArrayList<String>(filePaths.size());
+ Properties typeProperties = new Properties();
+
+ int nameSuffix = 1;
+ while (it.hasNext())
+ {
+ String keyStore = it.next();
+ File keyStoreFile = new File(keyStore);
+ if (keyStoreFile.exists())
+ {
+ String entryName = keyStoreFile.getName();
+ while (entriesNames.contains(entryName))
+ {
+ if (entryName.toLowerCase().endsWith(KEYSTORE_EXT))
+ {
+ entryName =
+ entryName
+ .replace(KEYSTORE_EXT, "_" + nameSuffix + KEYSTORE_EXT); //$NON-NLS-1$
+ }
+ else
+ {
+ entryName = entryName.concat(Integer.toString(nameSuffix));
+ }
+ nameSuffix++;
+ }
+ putKsType(typeProperties, entryName, keyStore);
+ ZipEntry zipEntry = new ZipEntry(entryName);
+ zos.putNextEntry(zipEntry);
+ entriesNames.add(entryName);
+
+ byte[] buf = new byte[1024 * 4];
+ int len;
+ in = new FileInputStream(keyStoreFile);
+ try
+ {
+ while ((len = in.read(buf)) > 0)
+ {
+ zos.write(buf, 0, len);
+ }
+ }
+ finally
+ {
+ if (in != null)
+ {
+ in.close();
+ }
+ }
+ zos.flush();
+ }
+ }
+ zos.putNextEntry(new ZipEntry(KS_TYPES_FILENAME));
+ putLastBackupDate(typeProperties);
+ typeProperties.store(zos, "KeyStore types"); //$NON-NLS-1$
+ }
+ finally
+ {
+ if (zos != null)
+ {
+ try
+ {
+ zos.flush();
+ zos.close();
+ }
+ catch (IOException e)
+ {
+ StudioLogger.error("Could not close steam while creating zip archive. "
+ + e.getMessage());
+ }
+ }
+ }
+ }
+
+ private void putKsType(Properties properties, String entryName, String filePaths)
+ throws KeyStoreManagerException
+ {
+ List<IKeyStore> keyStores = KeyStoreManager.getInstance().getKeyStores();
+ for (IKeyStore keyStore : keyStores)
+ {
+ String keyStorePath = keyStore.getFile().getAbsolutePath();
+ if (filePaths.contains(keyStorePath))
+ {
+ String type = keyStore.getType();
+ if (type != null)
+ {
+ properties.put(entryName, type);
+ }
+ break;
+ }
+ }
+
+ }
+
+ private void putLastBackupDate(Properties properties)
+ {
+ Long time = lastBackupDate.getTime();
+ String timeString = time.toString();
+ properties.put("lastBackupDate", timeString);
+ }
+
+}
diff --git a/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/command/CertificatePropertiesHandler.java b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/command/CertificatePropertiesHandler.java
new file mode 100644
index 0000000..e50aeac
--- /dev/null
+++ b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/command/CertificatePropertiesHandler.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.motorolamobility.studio.android.certmanager.command;
+
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.core.commands.IHandler;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.PlatformUI;
+
+import com.motorolamobility.studio.android.certmanager.ui.composite.KeyPropertiesBlock;
+import com.motorolamobility.studio.android.certmanager.ui.dialogs.CertificateInfoDialog;
+import com.motorolamobility.studio.android.certmanager.ui.model.EntryNode;
+import com.motorolamobility.studio.android.certmanager.ui.model.IKeyStoreEntry;
+import com.motorolamobility.studio.android.certmanager.ui.model.ITreeNode;
+
+/**
+ * This class implements the command to display certificate properties
+ * */
+public class CertificatePropertiesHandler extends AbstractHandler2 implements IHandler
+{
+
+ @Override
+ public Object execute(ExecutionEvent event) throws ExecutionException
+ {
+ //retrieves the first element of the selection
+ //note that this command should be enabled only when selection.count == 1.
+ ITreeNode node = getSelection().get(0);
+
+ if (node instanceof IKeyStoreEntry)
+ {
+ final EntryNode keyStoreEntry = (EntryNode) node;
+ PlatformUI.getWorkbench().getDisplay().syncExec(new Runnable()
+ {
+ @Override
+ public void run()
+ {
+ IWorkbench workbench = PlatformUI.getWorkbench();
+ IWorkbenchWindow ww = workbench.getActiveWorkbenchWindow();
+ Shell shell = ww.getShell();
+
+ CertificateInfoDialog dialog =
+ new CertificateInfoDialog(shell, new KeyPropertiesBlock(),
+ keyStoreEntry);
+
+ dialog.open();
+ }
+ });
+ }
+
+ return null;
+ }
+}
diff --git a/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/command/ChangeKeyStoreTypeHandler.java b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/command/ChangeKeyStoreTypeHandler.java
new file mode 100644
index 0000000..794831b
--- /dev/null
+++ b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/command/ChangeKeyStoreTypeHandler.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.studio.android.certmanager.command;
+
+import java.io.File;
+import java.util.Map;
+
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.jface.dialogs.Dialog;
+import org.eclipse.ui.PlatformUI;
+
+import com.motorola.studio.android.common.utilities.EclipseUtils;
+import com.motorolamobility.studio.android.certmanager.core.KeyStoreUtils;
+import com.motorolamobility.studio.android.certmanager.event.KeyStoreModelEvent.EventType;
+import com.motorolamobility.studio.android.certmanager.event.KeyStoreModelEventManager;
+import com.motorolamobility.studio.android.certmanager.exception.InvalidPasswordException;
+import com.motorolamobility.studio.android.certmanager.exception.KeyStoreManagerException;
+import com.motorolamobility.studio.android.certmanager.ui.dialogs.importks.ConvertKeyStoreTypeDialog;
+import com.motorolamobility.studio.android.certmanager.ui.model.IKeyStore;
+import com.motorolamobility.studio.android.certmanager.ui.model.ITreeNode;
+
+/**
+ * Handler to execute the change keystore type wizard.
+ * */
+public class ChangeKeyStoreTypeHandler extends AbstractHandler2
+{
+
+ @Override
+ public Object execute(ExecutionEvent event) throws ExecutionException
+ {
+ ITreeNode node = getSelection().get(0);
+
+ if (node instanceof IKeyStore)
+ {
+ ConvertKeyStoreTypeDialog dialog =
+ new ConvertKeyStoreTypeDialog(PlatformUI.getWorkbench()
+ .getModalDialogShellProvider().getShell(), (IKeyStore) node);
+ int diagStatus = dialog.open();
+ if (diagStatus == Dialog.OK)
+ {
+ IKeyStore keyStore = dialog.getKeyStore();
+ String newType = dialog.getNewType();
+ Map<String, String> aliases = dialog.getAliases();
+ String password = dialog.getKeystorePassword();
+
+ File keyStoreFile = keyStore.getFile();
+ try
+ {
+ if (password != null)
+ {
+ //user entered some password
+ KeyStoreUtils.changeKeyStoreType(keyStoreFile, password.toCharArray(),
+ keyStore.getType(), newType, aliases);
+ keyStore.setType(newType);
+ keyStore.forceReload(password.toCharArray(), false);
+ KeyStoreModelEventManager.getInstance().fireEvent((ITreeNode) keyStore,
+ EventType.UPDATE);
+
+ }
+ }
+ catch (KeyStoreManagerException e)
+ {
+ EclipseUtils.showErrorDialog(e);
+ }
+ catch (InvalidPasswordException e)
+ {
+ EclipseUtils.showErrorDialog(e);
+ }
+ }
+ }
+
+ return null;
+ }
+}
diff --git a/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/command/ChangePasswordKeyHandler.java b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/command/ChangePasswordKeyHandler.java
new file mode 100644
index 0000000..19c1574
--- /dev/null
+++ b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/command/ChangePasswordKeyHandler.java
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.motorolamobility.studio.android.certmanager.command;
+
+import java.security.KeyStore;
+import java.security.KeyStore.Entry;
+import java.security.UnrecoverableKeyException;
+
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.core.commands.IHandler;
+import org.eclipse.jface.window.Window;
+import org.eclipse.ui.PlatformUI;
+
+import com.motorola.studio.android.common.utilities.EclipseUtils;
+import com.motorola.studio.android.common.utilities.ui.PasswordInputDialog;
+import com.motorolamobility.studio.android.certmanager.core.KeyStoreUtils;
+import com.motorolamobility.studio.android.certmanager.core.PasswordProvider;
+import com.motorolamobility.studio.android.certmanager.event.KeyStoreModelEvent.EventType;
+import com.motorolamobility.studio.android.certmanager.event.KeyStoreModelEventManager;
+import com.motorolamobility.studio.android.certmanager.exception.InvalidPasswordException;
+import com.motorolamobility.studio.android.certmanager.i18n.CertificateManagerNLS;
+import com.motorolamobility.studio.android.certmanager.ui.model.EntryNode;
+import com.motorolamobility.studio.android.certmanager.ui.model.ITreeNode;
+import com.motorolamobility.studio.android.certmanager.ui.model.KeyStoreNode;
+import com.motorolamobility.studio.android.certmanager.views.KeystoreManagerView;
+
+/**
+ * This class changes the password of a key/certificate from the tree of the {@link KeystoreManagerView}
+ * */
+public class ChangePasswordKeyHandler extends AbstractHandler2 implements IHandler
+{
+
+ @Override
+ public Object execute(ExecutionEvent event) throws ExecutionException
+ {
+
+ //retrieves the first element of the selection
+ //note that this command should be enabled only when selection.count == 1.
+ ITreeNode entry = getSelection().get(0);
+
+ if (entry instanceof EntryNode)
+ {
+ //must enter old and new password
+ KeyStoreNode keyStoreNode = (KeyStoreNode) entry.getParent();
+ PasswordProvider passwordProvider = new PasswordProvider(keyStoreNode.getFile());
+ EntryNode entryNode = (EntryNode) entry;
+
+ PasswordInputDialog passwordInputDialog = null;
+
+ boolean cancelledOrChangedPassword = false;
+
+ do
+ {
+ passwordInputDialog =
+ new PasswordInputDialog(PlatformUI.getWorkbench()
+ .getActiveWorkbenchWindow().getShell(),
+ CertificateManagerNLS.PasswordInput_EnterOldKeyPasssword_Message,
+ true, null, EntryNode.KEY_PASSWORD_MIN_SIZE);
+ if (passwordInputDialog.open() == Window.OK)
+ {
+ String newPassword = passwordInputDialog.getNewPassword();
+ String oldPassword = passwordInputDialog.getOldPassword();
+
+ if ((newPassword != null) && (oldPassword != null))
+ {
+ try
+ {
+ Entry keyEntry = entryNode.getKeyEntry(oldPassword);
+ if (keyEntry != null)
+ {
+ boolean tryAgain = false;
+ boolean useSavedPass = true;
+ String keystorePassword = null;
+ KeyStore keyStore = null;
+ do
+ {
+ if (tryAgain)
+ {
+ useSavedPass = false;
+ }
+ keystorePassword =
+ passwordProvider
+ .getKeyStorePassword(true, useSavedPass);
+ tryAgain = false;
+ if (keystorePassword != null)
+ {
+ try
+ {
+ if (keyStoreNode.isPasswordValid(keystorePassword))
+ {
+ keyStore =
+ keyStoreNode.getKeyStore(keystorePassword);
+ }
+ else
+ {
+ tryAgain = true;
+ }
+ }
+ catch (InvalidPasswordException e)
+ {
+ tryAgain = true;
+ }
+ }
+ }
+ while (tryAgain);
+ if (keyStore != null)
+ {
+ KeyStoreUtils.changeEntryPassword(keyStore,
+ keystorePassword.toCharArray(), keyStoreNode.getFile(),
+ entryNode.getAlias(), keyEntry,
+ newPassword.toCharArray());
+
+ //delete old save password - if there is one
+ if (passwordProvider.isPasswordSaved(entryNode.getAlias()))
+ {
+ passwordProvider.deleteSavedPassword(entryNode.getAlias());
+ }
+
+ if (passwordInputDialog.needToStorePassword())
+ {
+ //store new password at provider
+ passwordProvider.savePassword(entryNode.getAlias(),
+ newPassword);
+ }
+
+ //success
+ cancelledOrChangedPassword = true;
+ EclipseUtils.showInformationDialog(
+ CertificateManagerNLS.PasswordChanged_Info_Title,
+ CertificateManagerNLS.PasswordChanged_KeyInfo_Message);
+
+ //update tooltip and image
+ KeyStoreModelEventManager.getInstance().fireEvent(entryNode,
+ EventType.UPDATE);
+ }
+ }
+ }
+ catch (UnrecoverableKeyException e)
+ {
+ //error - notify on screen
+ cancelledOrChangedPassword = false;
+ EclipseUtils
+ .showErrorDialog(
+ CertificateManagerNLS.ChangePasswordKeyHandler_Error_WrongOldKeyPassword,
+ CertificateManagerNLS.ChangePasswordKeyHandler_Wrong_Key_Password);
+ }
+ catch (Exception e)
+ {
+ //error - notify on screen
+ cancelledOrChangedPassword = false;
+ EclipseUtils
+ .showErrorDialog(
+ CertificateManagerNLS.ChangePasswordKeyHandler_Error_WrongOldKeyPassword,
+ e.getMessage());
+ }
+ }
+ }
+ else
+ {
+ //user cancelled screen
+ cancelledOrChangedPassword = true;
+ }
+
+ }
+ while (!cancelledOrChangedPassword);
+
+ }
+
+ return null;
+ }
+}
diff --git a/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/command/ChangePasswordKeystoreHandler.java b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/command/ChangePasswordKeystoreHandler.java
new file mode 100644
index 0000000..9b1948b
--- /dev/null
+++ b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/command/ChangePasswordKeystoreHandler.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.motorolamobility.studio.android.certmanager.command;
+
+import java.security.KeyStore;
+
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.core.commands.IHandler;
+import org.eclipse.jface.window.Window;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.ui.PlatformUI;
+
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.common.utilities.EclipseUtils;
+import com.motorola.studio.android.common.utilities.ui.PasswordInputDialog;
+import com.motorolamobility.studio.android.certmanager.core.KeyStoreUtils;
+import com.motorolamobility.studio.android.certmanager.core.PasswordProvider;
+import com.motorolamobility.studio.android.certmanager.event.KeyStoreModelEvent.EventType;
+import com.motorolamobility.studio.android.certmanager.event.KeyStoreModelEventManager;
+import com.motorolamobility.studio.android.certmanager.exception.KeyStoreManagerException;
+import com.motorolamobility.studio.android.certmanager.i18n.CertificateManagerNLS;
+import com.motorolamobility.studio.android.certmanager.ui.model.IKeyStore;
+import com.motorolamobility.studio.android.certmanager.ui.model.ITreeNode;
+import com.motorolamobility.studio.android.certmanager.ui.model.KeyStoreNode;
+import com.motorolamobility.studio.android.certmanager.views.KeystoreManagerView;
+
+/**
+ * This class deletes the keystore from the tree of the {@link KeystoreManagerView}
+ * */
+public class ChangePasswordKeystoreHandler extends AbstractHandler2 implements IHandler
+{
+
+ @Override
+ public Object execute(ExecutionEvent event) throws ExecutionException
+ {
+
+ //retrieves the first element of the selection
+ //note that this command should be enabled only when selection.count == 1.
+ ITreeNode node = getSelection().get(0);
+
+ if (node instanceof KeyStoreNode)
+ {
+ KeyStoreNode keyStoreNode = (KeyStoreNode) node;
+ PasswordProvider passwordProvider = new PasswordProvider(keyStoreNode.getFile());
+
+ //must enter old and new password
+ PasswordInputDialog passwordInputDialog = null;
+
+ boolean cancelledOrChangedPassword = false;
+
+ do
+ {
+ passwordInputDialog =
+ new PasswordInputDialog(
+ PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(),
+ NLS.bind(
+ CertificateManagerNLS.Passwordinput_EnterOldKeystorePasssword_Message,
+ keyStoreNode.getName()), true, null,
+ IKeyStore.KEYSTORE_PASSWORD_MIN_SIZE);
+ if (passwordInputDialog.open() == Window.OK)
+ {
+ String newPassword = passwordInputDialog.getNewPassword();
+ String oldPassword = passwordInputDialog.getOldPassword();
+
+ if ((newPassword != null) && (oldPassword != null))
+ {
+ KeyStore keyStore = null;
+ try
+ {
+ keyStore =
+ KeyStoreUtils.loadKeystore(keyStoreNode.getFile(),
+ oldPassword.toCharArray(), keyStoreNode.getType());
+
+ try
+ {
+ //rewrite keystore with new password
+ KeyStoreUtils.changeKeystorePasswd(keyStore,
+ keyStoreNode.getFile(), oldPassword.toCharArray(),
+ newPassword.toCharArray());
+
+ //deletes old password from provider
+ if (passwordProvider.isPasswordSaved())
+ {
+ passwordProvider.deleteKeyStoreSavedPassword();
+ }
+
+ if (passwordInputDialog.needToStorePassword())
+ {
+ //store new password at provider
+ passwordProvider.saveKeyStorePassword(newPassword);
+ }
+
+ //success
+ cancelledOrChangedPassword = true;
+ EclipseUtils.showInformationDialog(
+ CertificateManagerNLS.PasswordChanged_Info_Title,
+ CertificateManagerNLS.bind(
+ CertificateManagerNLS.PasswordChanged_Info_Message,
+ keyStoreNode.getFile()));
+
+ //update tooltip and image
+ KeyStoreModelEventManager.getInstance().fireEvent(node,
+ EventType.UPDATE);
+ }
+ catch (KeyStoreManagerException e)
+ {
+ //error to change password - notify on screen
+ cancelledOrChangedPassword = false;
+ EclipseUtils
+ .showErrorDialog(
+ CertificateManagerNLS.ChangePasswordKeystoreHandler_Error_ChangingKeystorePassword,
+ e.getMessage());
+ }
+ }
+ catch (Exception e1)
+ {
+ //invalid old password
+ StudioLogger.error(ChangePasswordKeystoreHandler.class,
+ e1.getMessage(), e1);
+ cancelledOrChangedPassword = false;
+ EclipseUtils
+ .showErrorDialog(
+ CertificateManagerNLS.ChangePasswordKeystoreHandler_Error_WrongOldKeystorePassword,
+ CertificateManagerNLS
+ .bind(CertificateManagerNLS.ChangePasswordKeystoreHandler_InvalidOldPassword,
+ keyStoreNode.getFile()));
+ }
+ }
+ }
+ else
+ {
+ //user cancelled screen
+ cancelledOrChangedPassword = true;
+ }
+
+ }
+ while (!cancelledOrChangedPassword);
+ }
+
+ return null;
+ }
+}
diff --git a/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/command/CreateKeyHandler.java b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/command/CreateKeyHandler.java
new file mode 100644
index 0000000..37a91d5
--- /dev/null
+++ b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/command/CreateKeyHandler.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.studio.android.certmanager.command;
+
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.core.commands.IHandler;
+
+import com.motorola.studio.android.common.utilities.ui.WidgetsUtil;
+import com.motorolamobility.studio.android.certmanager.ui.model.IKeyStore;
+import com.motorolamobility.studio.android.certmanager.ui.model.ITreeNode;
+import com.motorolamobility.studio.android.certmanager.ui.wizards.CreateKeyWizard;
+
+/**
+ * Handler to execute the create key wizard.
+ */
+public class CreateKeyHandler extends AbstractHandler2 implements IHandler
+{
+
+ @Override
+ public Object execute(ExecutionEvent event) throws ExecutionException
+ {
+ //retrieves the first element of the selection
+ //note that this command should be enabled only when selection.count == 1.
+ ITreeNode node = getSelection().get(0);
+
+ if (node instanceof IKeyStore)
+ {
+ //get keystore where to create the new key
+ final IKeyStore keyStore = (IKeyStore) node;
+ CreateKeyWizard createKeyWizard = new CreateKeyWizard(keyStore);
+ WidgetsUtil.runWizard(createKeyWizard);
+ }
+
+ return null;
+ }
+}
diff --git a/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/command/CreateKeystoreHandler.java b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/command/CreateKeystoreHandler.java
new file mode 100644
index 0000000..d56981f
--- /dev/null
+++ b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/command/CreateKeystoreHandler.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.motorolamobility.studio.android.certmanager.command;
+
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.core.commands.IHandler2;
+import org.eclipse.jface.wizard.WizardDialog;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.ui.PlatformUI;
+
+import com.motorolamobility.studio.android.certmanager.ui.wizards.CreateKeystoreWizard;
+
+/**
+ * Handler to execute the create keystore wizard.
+ * */
+public class CreateKeystoreHandler extends AbstractHandler implements IHandler2
+{
+
+ @Override
+ public Object execute(ExecutionEvent event) throws ExecutionException
+ {
+ Shell shell = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell();
+
+ CreateKeystoreWizard createKeystoreWizard = new CreateKeystoreWizard();
+
+ WizardDialog dialog = new WizardDialog(shell, createKeystoreWizard);
+
+ //open the wizard to create keystores
+ //the result doesn't need to be handled as it is self-contained
+ dialog.create();
+ dialog.open();
+
+ return null;
+ }
+}
diff --git a/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/command/DeleteKeyHandler.java b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/command/DeleteKeyHandler.java
new file mode 100644
index 0000000..ab73f89
--- /dev/null
+++ b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/command/DeleteKeyHandler.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.motorolamobility.studio.android.certmanager.command;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.core.commands.IHandler;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.PlatformUI;
+
+import com.motorola.studio.android.common.utilities.EclipseUtils;
+import com.motorolamobility.studio.android.certmanager.exception.KeyStoreManagerException;
+import com.motorolamobility.studio.android.certmanager.i18n.CertificateManagerNLS;
+import com.motorolamobility.studio.android.certmanager.ui.model.EntryNode;
+import com.motorolamobility.studio.android.certmanager.ui.model.IKeyStore;
+import com.motorolamobility.studio.android.certmanager.ui.model.ITreeNode;
+import com.motorolamobility.studio.android.certmanager.views.KeystoreManagerView;
+
+/**
+ * This class deletes the key entry from the tree of the {@link KeystoreManagerView}
+ * */
+public class DeleteKeyHandler extends AbstractHandler2 implements IHandler
+{
+
+ /*
+ * Question dialog confirming deletion of the key with a toggle
+ * asking if the key should also be deleted from the filesystem
+ * @return true if the deletion is confirmed, false otherwise and true in the
+ * toggleState if it can also be deleted from the filesystem, false otherwise
+ */
+ private boolean showQuestion(List<ITreeNode> nodesToDelete)
+ {
+
+ final Boolean[] reply = new Boolean[1];
+
+ final String entryName = nodesToDelete.size() == 1 ? nodesToDelete.get(0).getName() : null;
+
+ PlatformUI.getWorkbench().getDisplay().syncExec(new Runnable()
+ {
+ @Override
+ public void run()
+ {
+ IWorkbench workbench = PlatformUI.getWorkbench();
+ IWorkbenchWindow ww = workbench.getActiveWorkbenchWindow();
+ Shell shell = ww.getShell();
+
+ reply[0] =
+ MessageDialog
+ .openQuestion(
+ shell,
+ CertificateManagerNLS.DeleteKeyHandler_ConfirmationQuestionDialog_Title,
+ entryName != null
+ ? CertificateManagerNLS
+ .bind(CertificateManagerNLS.DeleteKeyHandler_ConfirmationQuestionDialog_Description,
+ entryName)
+ : CertificateManagerNLS.DeleteKeyHandler_Delete_Selected_Keys);
+ }
+ });
+
+ return reply[0];
+ }
+
+ @Override
+ public Object execute(ExecutionEvent event) throws ExecutionException
+ {
+ List<ITreeNode> nodesToDelete = getSelection();
+
+ if (!nodesToDelete.isEmpty())
+ {
+ boolean shouldProceed = showQuestion(nodesToDelete);
+ if (shouldProceed)
+ {
+ Map<IKeyStore, List<String>> deleteNodesMap = getKeysMap(nodesToDelete);
+
+ for (IKeyStore keyStore : deleteNodesMap.keySet())
+ {
+ try
+ {
+ keyStore.removeKeys(deleteNodesMap.get(keyStore));
+ }
+ catch (KeyStoreManagerException e)
+ {
+ EclipseUtils.showErrorDialog(e);
+ throw new ExecutionException(e.getMessage(), e);
+ }
+ }
+ }
+ }
+
+ nodesToDelete.clear();
+
+ return null;
+ }
+
+ private Map<IKeyStore, List<String>> getKeysMap(List<ITreeNode> nodesToDelete)
+ {
+ Map<IKeyStore, List<String>> deleteNodesMap = new HashMap<IKeyStore, List<String>>();
+ for (ITreeNode node2Delete : nodesToDelete)
+ {
+ if (node2Delete instanceof EntryNode)
+ {
+ EntryNode entryNode = (EntryNode) node2Delete;
+ IKeyStore iKeyStore = (IKeyStore) entryNode.getParent();
+ List<String> keyList = deleteNodesMap.get(iKeyStore);
+ if (keyList == null)
+ {
+ keyList = new ArrayList<String>();
+ }
+ keyList.add(entryNode.getAlias());
+ deleteNodesMap.put(iKeyStore, keyList);
+ }
+ }
+ return deleteNodesMap;
+ }
+}
diff --git a/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/command/DeleteKeystoreHandler.java b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/command/DeleteKeystoreHandler.java
new file mode 100644
index 0000000..4188408
--- /dev/null
+++ b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/command/DeleteKeystoreHandler.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.motorolamobility.studio.android.certmanager.command;
+
+import java.util.List;
+
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.core.commands.IHandler2;
+import org.eclipse.jface.dialogs.IDialogConstants;
+import org.eclipse.jface.dialogs.MessageDialogWithToggle;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.PlatformUI;
+
+import com.motorolamobility.studio.android.certmanager.i18n.CertificateManagerNLS;
+import com.motorolamobility.studio.android.certmanager.ui.model.ITreeNode;
+import com.motorolamobility.studio.android.certmanager.ui.model.KeyStoreNode;
+import com.motorolamobility.studio.android.certmanager.ui.model.SigningAndKeysModelManager;
+import com.motorolamobility.studio.android.certmanager.views.KeystoreManagerView;
+
+/**
+ * This class deletes the keystore from the tree of the {@link KeystoreManagerView}
+ * */
+public class DeleteKeystoreHandler extends AbstractHandler2 implements IHandler2
+{
+
+ private static boolean toggleState = false;
+
+ /*
+ * Question dialog confirming deletion of the keystore with a toggle
+ * asking if the keystore should also be deleted from the filesystem
+ * @return true if the deletion is confirmed, false otherwise and true in the
+ * toggleState if it can also be deleted from the filesystem, false otherwise
+ */
+ private boolean showQuestion(List<ITreeNode> nodesToDelete)
+ {
+
+ final Boolean[] reply = new Boolean[2];
+
+ final String keystoreName =
+ nodesToDelete.size() == 1 ? nodesToDelete.get(0).getName() : null;
+
+ PlatformUI.getWorkbench().getDisplay().syncExec(new Runnable()
+ {
+ @Override
+ public void run()
+ {
+ IWorkbench workbench = PlatformUI.getWorkbench();
+ IWorkbenchWindow ww = workbench.getActiveWorkbenchWindow();
+ Shell shell = ww.getShell();
+
+ MessageDialogWithToggle dialog =
+ MessageDialogWithToggle
+ .openYesNoQuestion(
+ shell,
+ CertificateManagerNLS.DeleteKeystoreHandler_ConfirmationQuestionDialog_Title,
+ keystoreName != null
+ ? CertificateManagerNLS
+ .bind(CertificateManagerNLS.DeleteKeystoreHandler_ConfirmationQuestionDialog_Description,
+ keystoreName)
+ : CertificateManagerNLS.DeleteKeystoreHandler_Delete_Selected_Keystores,
+ CertificateManagerNLS.DeleteKeystoreHandler_ConfirmationQuestionDialog_Toggle,
+ false, null, null);
+ reply[0] = (dialog.getReturnCode() == IDialogConstants.YES_ID);
+ reply[1] = dialog.getToggleState();
+ }
+ });
+
+ toggleState = reply[1];
+
+ return reply[0];
+ }
+
+ @Override
+ public Object execute(ExecutionEvent event) throws ExecutionException
+ {
+ List<ITreeNode> nodesToDelete = getSelection();
+
+ if (!nodesToDelete.isEmpty())
+ {
+ boolean shouldProceed = showQuestion(nodesToDelete);
+ if (shouldProceed)
+ {
+ for (ITreeNode node2Delete : nodesToDelete)
+ {
+ KeyStoreNode keyStoreNode = (KeyStoreNode) node2Delete;
+
+ // remove from the tree
+ SigningAndKeysModelManager.getInstance().unmapKeyStore(keyStoreNode);
+
+ if (toggleState)
+ {
+ keyStoreNode.getFile().delete();
+ }
+ }
+ }
+ }
+
+ nodesToDelete.clear();
+
+ return null;
+ }
+
+}
diff --git a/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/command/ImportKeyStoreEntriesHandler.java b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/command/ImportKeyStoreEntriesHandler.java
new file mode 100644
index 0000000..c915318
--- /dev/null
+++ b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/command/ImportKeyStoreEntriesHandler.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.studio.android.certmanager.command;
+
+import java.util.Map;
+
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.jface.dialogs.Dialog;
+import org.eclipse.ui.PlatformUI;
+
+import com.motorola.studio.android.common.utilities.EclipseUtils;
+import com.motorolamobility.studio.android.certmanager.core.KeyStoreUtils;
+import com.motorolamobility.studio.android.certmanager.core.PasswordProvider;
+import com.motorolamobility.studio.android.certmanager.exception.InvalidPasswordException;
+import com.motorolamobility.studio.android.certmanager.exception.KeyStoreManagerException;
+import com.motorolamobility.studio.android.certmanager.ui.dialogs.importks.ImportEntriesDialog;
+import com.motorolamobility.studio.android.certmanager.ui.model.IKeyStore;
+import com.motorolamobility.studio.android.certmanager.ui.model.ITreeNode;
+
+/**
+ * Handler to execute the wizard that import keys from one keystore to another.
+ * */
+public class ImportKeyStoreEntriesHandler extends AbstractHandler2
+{
+
+ @Override
+ public Object execute(ExecutionEvent event) throws ExecutionException
+ {
+ ITreeNode node = getSelection().get(0);
+
+ if (node instanceof IKeyStore)
+ {
+ IKeyStore keyStore = (IKeyStore) node;
+ ImportEntriesDialog dialog =
+ new ImportEntriesDialog(PlatformUI.getWorkbench().getModalDialogShellProvider()
+ .getShell(), keyStore);
+ int diagStatus = dialog.open();
+ if (diagStatus == Dialog.OK)
+ {
+ IKeyStore sourceKeyStore = dialog.getKeyStore();
+ String sourcePassword = dialog.getPassword();
+ Map<String, String> aliases = dialog.getAliases();
+ IKeyStore targetKeyStore = dialog.getTargetKeyStore();
+
+ PasswordProvider passwordProvider = targetKeyStore.getPasswordProvider();
+ String password;
+ boolean invalidPassword = false;
+ do
+ {
+ try
+ {
+ password = passwordProvider.getKeyStorePassword(true);
+ if (password != null)
+ {
+ KeyStoreUtils
+ .importKeys(targetKeyStore.getKeyStore(),
+ targetKeyStore.getFile(), targetKeyStore.getType(),
+ password.toCharArray(), sourceKeyStore.getKeyStore(),
+ sourceKeyStore.getFile(), sourcePassword.toCharArray(),
+ aliases);
+ invalidPassword = false;
+ targetKeyStore.forceReload(password.toCharArray(), true);
+ }
+ else
+ {
+ break;
+ }
+ }
+ catch (KeyStoreManagerException e)
+ {
+ EclipseUtils.showErrorDialog(e);
+ }
+ catch (InvalidPasswordException e)
+ {
+ invalidPassword = true;
+ }
+ }
+ while (invalidPassword);
+
+ }
+ }
+ return null;
+ }
+}
diff --git a/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/command/ImportKeystoreHandler.java b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/command/ImportKeystoreHandler.java
new file mode 100644
index 0000000..6c83e66
--- /dev/null
+++ b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/command/ImportKeystoreHandler.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.motorolamobility.studio.android.certmanager.command;
+
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.core.commands.IHandler;
+import org.eclipse.jface.wizard.WizardDialog;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.ui.PlatformUI;
+
+import com.motorolamobility.studio.android.certmanager.ui.wizards.ImportKeystoreWizard;
+
+/**
+ * This class maps the keystore file into the tree of the KeyStore Manager View.
+ * */
+public class ImportKeystoreHandler extends AbstractHandler implements IHandler
+{
+
+ @Override
+ public Object execute(ExecutionEvent event) throws ExecutionException
+ {
+ Shell shell = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell();
+
+ ImportKeystoreWizard createKeystoreWizard = new ImportKeystoreWizard();
+
+ WizardDialog dialog = new WizardDialog(shell, createKeystoreWizard);
+
+ //open the wizard to import keystores
+ //the result doesn't need to be handled as it is self-contained
+ dialog.create();
+ dialog.open();
+
+ return null;
+ }
+
+}
diff --git a/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/command/RefreshHandler.java b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/command/RefreshHandler.java
new file mode 100644
index 0000000..908f7ea
--- /dev/null
+++ b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/command/RefreshHandler.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.studio.android.certmanager.command;
+
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.core.commands.IHandler;
+
+import com.motorolamobility.studio.android.certmanager.event.KeyStoreModelEvent.EventType;
+import com.motorolamobility.studio.android.certmanager.event.KeyStoreModelEventManager;
+
+/**
+ * Handler to execute the refresh operation.
+ * */
+public class RefreshHandler extends AbstractHandler2 implements IHandler
+{
+
+ @Override
+ public Object execute(ExecutionEvent event) throws ExecutionException
+ {
+ KeyStoreModelEventManager.getInstance().fireEvent(getSelection().get(0), EventType.REFRESH);
+
+ return null;
+ }
+
+}
diff --git a/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/command/RestoreBackupHandler.java b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/command/RestoreBackupHandler.java
new file mode 100644
index 0000000..33ada45
--- /dev/null
+++ b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/command/RestoreBackupHandler.java
@@ -0,0 +1,306 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.studio.android.certmanager.command;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.security.KeyStore;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.List;
+import java.util.Properties;
+
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.core.commands.IHandler;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.jface.dialogs.Dialog;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.ui.PlatformUI;
+
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.common.utilities.EclipseUtils;
+import com.motorola.studio.android.common.utilities.FileUtil;
+import com.motorolamobility.studio.android.certmanager.CertificateManagerActivator;
+import com.motorolamobility.studio.android.certmanager.core.KeyStoreManager;
+import com.motorolamobility.studio.android.certmanager.event.KeyStoreModelEvent.EventType;
+import com.motorolamobility.studio.android.certmanager.event.KeyStoreModelEventManager;
+import com.motorolamobility.studio.android.certmanager.exception.KeyStoreManagerException;
+import com.motorolamobility.studio.android.certmanager.i18n.CertificateManagerNLS;
+import com.motorolamobility.studio.android.certmanager.ui.dialogs.RestoreBackupDialog;
+import com.motorolamobility.studio.android.certmanager.ui.model.IKeyStore;
+import com.motorolamobility.studio.android.certmanager.ui.model.ITreeNode;
+import com.motorolamobility.studio.android.certmanager.ui.model.KeyStoreNode;
+import com.motorolamobility.studio.android.certmanager.ui.model.SigningAndKeysModelManager;
+
+/**
+ * Handler to execute the restore backup wizard.
+ * */
+public class RestoreBackupHandler extends AbstractHandler implements IHandler
+{
+
+ Date lastBackupDate = new Date();
+
+ @Override
+ public Object execute(ExecutionEvent event) throws ExecutionException
+ {
+ RestoreBackupDialog dialog =
+ new RestoreBackupDialog(PlatformUI.getWorkbench().getActiveWorkbenchWindow()
+ .getShell());
+
+ int diagReturn = dialog.open();
+ if (diagReturn == Dialog.OK)
+ {
+ File archiveFile = dialog.getArchiveFile();
+ File destinationFile = dialog.getDestinationDir();
+ List<String> selectedKeyStores = dialog.getSelectedKeyStores();
+
+ restoreBackup(archiveFile, destinationFile, selectedKeyStores);
+ }
+
+ return null;
+ }
+
+ private void restoreBackup(File archiveFile, File destinationFile,
+ List<String> selectedKeyStores)
+ {
+ boolean extractionSuccess = false;
+ File typePropertiesFile = new File(destinationFile, BackupHandler.KS_TYPES_FILENAME); //$NON-NLS-1$
+ try
+ {
+ extractionSuccess =
+ FileUtil.extractZipArchive(archiveFile, destinationFile,
+ Arrays.asList(new String[]
+ {
+ BackupHandler.KS_TYPES_FILENAME
+ }), new NullProgressMonitor());
+
+ extractionSuccess =
+ FileUtil.extractZipArchive(archiveFile, destinationFile, selectedKeyStores,
+ new NullProgressMonitor());
+ }
+ catch (IOException e)
+ {
+ //roll back: delete files, if they were created
+ rollBackDeleteExtractedFiles(destinationFile, selectedKeyStores);
+ EclipseUtils
+ .showErrorDialog(
+ CertificateManagerNLS.RestoreBackupHandler_Error_Restoring_Backup_Title,
+ NLS.bind(
+ CertificateManagerNLS.RestoreBackupHandler_Error_Restoring_Backup_Message,
+ archiveFile),
+ new Status(
+ IStatus.ERROR,
+ CertificateManagerNLS.RestoreBackupHandler_Error_Restoring_Backup_Status,
+ CertificateManagerActivator.PLUGIN_ID, e));
+ }
+
+ if (extractionSuccess)
+ {
+ Properties properties = null;
+
+ if ((typePropertiesFile != null) && !typePropertiesFile.exists())
+ {
+ showWarningAboutNonIdentifiedKeystoreType(selectedKeyStores);
+ }
+ properties = loadTypeProperties(typePropertiesFile, properties);
+ //recover last backup date
+ getDateFromProperties(properties);
+
+ List<String> nonIdentifiedKeystoreTypes = new ArrayList<String>();
+
+ for (String keyStoreFileName : selectedKeyStores)
+ {
+ File keyStoreFile = new File(destinationFile, keyStoreFileName);
+ String ksType = null;
+ if (properties != null)
+ {
+ ksType = (String) properties.get(keyStoreFileName);
+ if (ksType == null)
+ {
+ //type not found at metadata
+ nonIdentifiedKeystoreTypes.add(keyStoreFileName);
+ }
+ }
+
+ if (ksType == null)
+ {
+ ksType = KeyStoreManager.getInstance().getDefaultType(); // set the keystore type to be the default type
+ }
+ KeyStoreNode keyStoreNode = new KeyStoreNode(keyStoreFile, ksType);
+ keyStoreNode.setLastBackupDate(lastBackupDate);
+
+ boolean map = true;
+ try
+ {
+ List<IKeyStore> keyStores = KeyStoreManager.getInstance().getKeyStores();
+ for (IKeyStore keyStore : keyStores)
+ {
+ if (keyStore.getFile().equals(keyStoreFile))
+ {
+ //updates keystore type if necessary
+ if ((ksType != null)
+ && (ksType.compareToIgnoreCase(keyStore.getType()) != 0))
+ {
+ keyStore.setType(ksType);
+ }
+ KeyStoreModelEventManager.getInstance().fireEvent((ITreeNode) keyStore,
+ EventType.COLLAPSE);
+ map = false;
+ keyStore.setLastBackupDate(lastBackupDate);
+ }
+ }
+
+ if (map)
+ {
+ SigningAndKeysModelManager.getInstance().mapKeyStore(keyStoreNode);
+ }
+ }
+ catch (KeyStoreManagerException e)
+ {
+ EclipseUtils
+ .showErrorDialog(
+ CertificateManagerNLS.RestoreBackupHandler_Error_Mapping_Title,
+ CertificateManagerNLS.RestoreBackupHandler_Error_Mapping_Message,
+ new Status(
+ IStatus.ERROR,
+ CertificateManagerNLS.RestoreBackupHandler_Error_Mapping_Status,
+ CertificateManagerActivator.PLUGIN_ID, e));
+ if (map)
+ {
+ //roll back operation - undo mapping if keystore was mapped during backup
+ SigningAndKeysModelManager.getInstance().unmapKeyStore(keyStoreNode);
+ }
+ }
+ }
+
+ if ((nonIdentifiedKeystoreTypes != null) && !nonIdentifiedKeystoreTypes.isEmpty())
+ {
+ showWarningAboutNonIdentifiedKeystoreType(nonIdentifiedKeystoreTypes);
+ }
+
+ }
+ else
+ {
+ //roll back: delete files, if they were created
+ rollBackDeleteExtractedFiles(destinationFile, selectedKeyStores);
+ EclipseUtils
+ .showErrorDialog(
+ CertificateManagerNLS.RestoreBackupHandler_Error_Restoring_Backup_Title,
+ NLS.bind(
+ CertificateManagerNLS.RestoreBackupHandler_Error_Restoring_Backup_Message,
+ archiveFile));
+ }
+ }
+
+ private void rollBackDeleteExtractedFiles(File destinationFile, List<String> selectedKeyStores)
+ {
+ //roll back: delete files, if they were created
+ File ksTypeDest = new File(destinationFile, BackupHandler.KS_TYPES_FILENAME);
+ if (ksTypeDest.exists())
+ {
+ //roll back: delete Kstypes
+ ksTypeDest.delete();
+ }
+ for (String keyStoreFileName : selectedKeyStores)
+ {
+ //roll back: delete extract files
+ File keyStoreFile = new File(destinationFile, keyStoreFileName);
+ if (keyStoreFile.exists())
+ {
+ keyStoreFile.delete();
+ }
+ }
+ }
+
+ private void getDateFromProperties(Properties properties)
+ {
+ if (properties != null)
+ {
+ //KsTypes.csv available
+ String lastString = properties.getProperty("lastBackupDate");
+ Long time = new Long(lastString);
+ lastBackupDate.setTime(time);
+ }
+ else
+ {
+ //KsTypes.csv not available
+ StudioLogger.debug("KsTypes.csv not available to get lastBackupDate properties");
+ }
+ }
+
+ /**
+ * Shows warning stating that some keystores will use default keystore type.
+ * @param selectedKeyStores
+ */
+ private void showWarningAboutNonIdentifiedKeystoreType(List<String> selectedKeyStores)
+ {
+ EclipseUtils
+ .showWarningDialog(
+ CertificateManagerNLS.RestoreBackupHandler_RestoreIssue_WarningTitle,
+ CertificateManagerNLS
+ .bind(CertificateManagerNLS.RestoreBackupHandler_RestoreIssue_MissingMetadataFile_WarningDescription,
+ selectedKeyStores, KeyStore.getDefaultType()));
+ }
+
+ private Properties loadTypeProperties(File typePropertiesFile, Properties properties)
+ {
+ if ((typePropertiesFile != null) && typePropertiesFile.exists())
+ {
+ FileInputStream propInStream = null;
+ properties = new Properties();
+ try
+ {
+ propInStream = new FileInputStream(typePropertiesFile);
+ properties.load(propInStream);
+
+ typePropertiesFile.delete();
+ }
+ catch (FileNotFoundException e)
+ {
+ properties = null;
+ }
+ catch (IOException e)
+ {
+ properties = null;
+ }
+ finally
+ {
+ if (propInStream != null)
+ {
+ try
+ {
+ propInStream.close();
+ }
+ catch (IOException e)
+ {
+ StudioLogger.error("Could not close steam while loading type properties. "
+ + e.getMessage());
+ }
+ typePropertiesFile.delete();
+ }
+ }
+
+ }
+ return properties;
+ }
+}
diff --git a/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/command/SignExternalPackagesHandler.java b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/command/SignExternalPackagesHandler.java
new file mode 100644
index 0000000..3551842
--- /dev/null
+++ b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/command/SignExternalPackagesHandler.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.studio.android.certmanager.command;
+
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.core.commands.IHandler2;
+import org.eclipse.jface.action.Action;
+
+import com.motorolamobility.studio.android.certmanager.ui.action.SignCreatedPackageAction;
+
+/**
+ * Handler to execute the sign package wizard.
+ * */
+public class SignExternalPackagesHandler extends AbstractHandler2 implements IHandler2
+{
+
+ @Override
+ public Object execute(ExecutionEvent event) throws ExecutionException
+ {
+ Action action = new SignCreatedPackageAction();
+ action.run();
+ return null;
+ }
+
+}
diff --git a/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/command/UnsignExternalPackagesHandler.java b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/command/UnsignExternalPackagesHandler.java
new file mode 100644
index 0000000..72ae028
--- /dev/null
+++ b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/command/UnsignExternalPackagesHandler.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.studio.android.certmanager.command;
+
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.core.commands.IHandler;
+import org.eclipse.jface.action.Action;
+
+import com.motorolamobility.studio.android.certmanager.ui.action.RemoveSignatureAction;
+
+/**
+ * Handler to execute the unsign package wizard.
+ * */
+public class UnsignExternalPackagesHandler extends AbstractHandler implements IHandler
+{
+
+ @Override
+ public Object execute(ExecutionEvent event) throws ExecutionException
+ {
+ Action action = new RemoveSignatureAction();
+ action.run();
+ return null;
+ }
+
+}
diff --git a/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/core/BackwardKeystoreManager.java b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/core/BackwardKeystoreManager.java
new file mode 100644
index 0000000..319e46c
--- /dev/null
+++ b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/core/BackwardKeystoreManager.java
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.studio.android.certmanager.core;
+
+import java.io.File;
+import java.util.List;
+
+import org.eclipse.equinox.security.storage.ISecurePreferences;
+import org.eclipse.equinox.security.storage.SecurePreferencesFactory;
+import org.eclipse.equinox.security.storage.StorageException;
+
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.common.utilities.EclipseUtils;
+import com.motorolamobility.studio.android.certmanager.i18n.CertificateManagerNLS;
+import com.motorolamobility.studio.android.certmanager.ui.model.IKeyStoreEntry;
+import com.motorolamobility.studio.android.certmanager.ui.model.KeyStoreNode;
+import com.motorolamobility.studio.android.certmanager.ui.model.SigningAndKeysModelManager;
+
+/**
+ * Manages old MOTODEV keystore available at <user_home>\motodevstudio\tools\motodev.keystore
+ * (to keep backward compatibility)
+ */
+public class BackwardKeystoreManager
+{
+ /**
+ * MOTODEV Studio directory.
+ */
+ private static final String MOTODEV_TOOLS_PATH = File.separator
+ + "motodevstudio" + File.separator + "tools"; //$NON-NLS-1$ //$NON-NLS-2$
+
+ /**
+ * Path to user home directory.
+ */
+ private static final String USER_HOME_PATH = System.getProperty("user.home"); //$NON-NLS-1$
+
+ /**
+ * Full path where the files will be.
+ */
+ private static final String TOOLS_FULL_PATH = USER_HOME_PATH + MOTODEV_TOOLS_PATH;
+
+ private static final String KS_FILENAME_NEW = TOOLS_FULL_PATH + File.separatorChar
+ + "motodev.keystore"; //$NON-NLS-1$
+
+ /**
+ * The constant contains the keysotre type.
+ */
+ private static final String KS_TYPE = "JCEKS"; //$NON-NLS-1$
+
+ /**
+ * The constant contains the keystore password.
+ */
+ private static final String KS_DEFAULT_PASSWD = "passwd"; //$NON-NLS-1$
+
+ /**
+ * The constant contains the keystore password node on secure storage.
+ */
+ private static final String KS_PASSWD_NODE = "com.motorola.studio.platform.tools.sign.core"; //$NON-NLS-1$
+
+ /**
+ * The constant contains the keystore password attribute on secure storage.
+ */
+ private static final String KS_PASSWD_ATTRIBUTE = "kspasswd"; //$NON-NLS-1$
+
+ /*
+ * Removes old keystore password from Eclipse secure preferences (old format)
+ * @return the stored password (before removal),
+ * {@link SigningCoreConstants#KS_DEFAULT_PASSWD} if found node {@link SigningCoreConstants#KS_PASSWD_NODE} but {@link SigningCoreConstants#KS_PASSWD_ATTRIBUTE} does not exist,
+ * null if node {@link SigningCoreConstants#KS_PASSWD_NODE} not found
+ */
+ private String removeOldKeystorePassword()
+ {
+ String oldPassword = KS_DEFAULT_PASSWD;
+ ISecurePreferences preferences = SecurePreferencesFactory.getDefault();
+ ISecurePreferences node = null;
+ try
+ {
+ if (preferences.nodeExists(KS_PASSWD_NODE))
+ {
+ //old format node exists = try to get from attribute
+ node = preferences.node(KS_PASSWD_NODE);
+ try
+ {
+ oldPassword = node.get(KS_PASSWD_ATTRIBUTE, KS_DEFAULT_PASSWD);
+ }
+ catch (StorageException e)
+ {
+ oldPassword = KS_DEFAULT_PASSWD;
+ }
+ node.remove(KS_PASSWD_ATTRIBUTE);
+ preferences.remove(KS_PASSWD_NODE);
+ node.flush();
+ }
+ else
+ {
+ //node already removed => password is in the new format
+ oldPassword = null;
+ }
+ }
+ catch (Exception e)
+ {
+ StudioLogger.error(BackwardKeystoreManager.class, e.getMessage(), e);
+ }
+ return oldPassword;
+ }
+
+ /**
+ * Maps old keystore if the <user_home>\motodevstudio\tools\motodev.keystore file exists.
+ */
+ public void mapOldKeystore()
+ {
+ File motodevKeystoreFile = new File(KS_FILENAME_NEW);
+ if (motodevKeystoreFile.exists())
+ {
+ //we found backward (old default MOTODEV keystore) => import it
+ KeyStoreNode keyStoreNode = new KeyStoreNode(motodevKeystoreFile, KS_TYPE);
+
+ try
+ {
+ SigningAndKeysModelManager.getInstance().mapKeyStore(keyStoreNode);
+
+ //remove old password from keystore
+ String oldPassword = removeOldKeystorePassword();
+
+ if (oldPassword != null)
+ {
+ //oldPassword was in old format => add password for keystore in the new format
+ PasswordProvider passwordProvider =
+ new PasswordProvider(keyStoreNode.getFile());
+ passwordProvider.saveKeyStorePassword(oldPassword);
+
+ //for each child key (save the same password as the keystore to maintain backward compatibility)
+ List<IKeyStoreEntry> iKeyStoreEntries = keyStoreNode.getEntries(oldPassword);
+ if (iKeyStoreEntries != null)
+ {
+ for (IKeyStoreEntry keyStoreEntry : iKeyStoreEntries)
+ {
+ passwordProvider.savePassword(keyStoreEntry.getAlias(), oldPassword);
+ }
+ }
+ }
+ }
+ catch (Exception e)
+ {
+ //error
+ EclipseUtils.showErrorDialog(CertificateManagerNLS.bind(
+ CertificateManagerNLS.KeystoreManagerView_ErrorImportingBackwardKeystore,
+ motodevKeystoreFile), e.getMessage());
+ }
+ }
+ }
+}
diff --git a/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/core/KeyStoreManager.java b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/core/KeyStoreManager.java
new file mode 100644
index 0000000..1d8bae6
--- /dev/null
+++ b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/core/KeyStoreManager.java
@@ -0,0 +1,303 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.studio.android.certmanager.core;
+
+import java.io.File;
+import java.io.IOException;
+import java.security.KeyStore;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorolamobility.studio.android.certmanager.exception.InvalidPasswordException;
+import com.motorolamobility.studio.android.certmanager.exception.KeyStoreManagerException;
+import com.motorolamobility.studio.android.certmanager.ui.model.IKeyStore;
+import com.motorolamobility.studio.android.certmanager.ui.model.KeyStoreNode;
+import com.motorolamobility.studio.android.certmanager.ui.model.KeyStoreRootNode;
+import com.motorolamobility.studio.android.certmanager.views.KeystoreManagerView;
+
+/**
+ * Provides a common interface to manipulate keystores.
+ * Other plugins need to use it (to avoid knowing {@link KeyStoreRootNode} model and {@link SaveStateManager}).
+ *
+ * The {@link KeystoreManagerView} also need to call its methods to guarantee persistence of its operations.
+ */
+public class KeyStoreManager
+{
+ public static final String KEYSTORE_TYPE_PKCS12 = "PKCS12";
+
+ public static final String KEYSTORE_TYPE_JCEKS = "JCEKS";
+
+ public static final String KEYSTORE_TYPE_JKS = "JKS";
+
+ private static final String ERROR_TO_ACCESS_KEYSTORE_MAPPING_PERSISTENCE =
+ "Error to access keystore mapping persistence";
+
+ private static KeyStoreManager _instance;
+
+ private List<IKeyStore> keyStores = null;
+
+ /**
+ * This class is a singleton.
+ * @return The unique instance of this class.
+ * */
+ public synchronized static KeyStoreManager getInstance()
+ {
+ if (_instance == null)
+ {
+ _instance = new KeyStoreManager();
+ }
+ return _instance;
+ }
+
+ private KeyStoreManager()
+ {
+ }
+
+ /**
+ * Add a new keystore to the manager.
+ * @param keystore The keystore to be added.
+ * @throws KeyStoreManagerException if an error occurs while accessing persistence file where keystores are mapped.
+ */
+ public void addKeyStore(IKeyStore keystore) throws KeyStoreManagerException
+ {
+ SaveStateManager manager = null;
+ try
+ {
+ manager = SaveStateManager.getInstance();
+ manager.addEntry(keystore.getFile(), keystore.getType());
+ getKeyStores().add(keystore);
+ }
+ catch (Exception e)
+ {
+ throw new KeyStoreManagerException(ERROR_TO_ACCESS_KEYSTORE_MAPPING_PERSISTENCE, e);
+ }
+ }
+
+ /**
+ * Remove a keystore from the manager.
+ * @param keystore The keystore to be removed.
+ * @throws KeyStoreManagerException if an error occurs while accessing persistence file where keystores are mapped.
+ */
+ public void removeKeyStore(IKeyStore keystore) throws KeyStoreManagerException
+ {
+ SaveStateManager manager = null;
+ try
+ {
+ manager = SaveStateManager.getInstance();
+ manager.removeEntry(keystore.getFile());
+ getKeyStores().remove(keystore);
+ }
+ catch (Exception e)
+ {
+ throw new KeyStoreManagerException(ERROR_TO_ACCESS_KEYSTORE_MAPPING_PERSISTENCE, e);
+ }
+ }
+
+ /**
+ * Set the date which the keystore was added to a backup file.
+ * @param keyStore The keystore to be set.
+ * @param backupDate The date of the backup.
+ * @throws KeyStoreManagerException If there were problems while persisting the information.
+ * */
+ public void setBackupDate(IKeyStore keyStore, Date backupDate) throws KeyStoreManagerException
+ {
+ if ((keyStore != null) && (backupDate != null))
+ {
+ try
+ {
+ SaveStateManager manager = SaveStateManager.getInstance();
+ manager.setBackupDate(keyStore.getFile(), backupDate);
+ }
+ catch (Exception e)
+ {
+ throw new KeyStoreManagerException(ERROR_TO_ACCESS_KEYSTORE_MAPPING_PERSISTENCE, e);
+ }
+ }
+ }
+
+ /**
+ * Update the type of a managed keystore, using solely the information provided by the keystore.
+ * @param keystore The keystore which type needs to be updated.
+ * @throws KeyStoreManagerException If there were problems while persisting the information.
+ * */
+ public void updateKeyStoreType(IKeyStore keyStore) throws KeyStoreManagerException
+ {
+ SaveStateManager manager;
+ try
+ {
+ manager = SaveStateManager.getInstance();
+ if (manager.isKeystoreMapped(keyStore.getFile()))
+ {
+ manager.addEntry(keyStore.getFile(), keyStore.getType());
+ }
+ }
+ catch (IOException e)
+ {
+ throw new KeyStoreManagerException(ERROR_TO_ACCESS_KEYSTORE_MAPPING_PERSISTENCE, e);
+ }
+
+ }
+
+ /**
+ * @return The list of mapped keystores in the persistence, or empty list if there is no keystore mapped.
+ * @throws KeyStoreManagerException if an error occurs to access persistence file where keystores are mapped.
+ */
+ public List<IKeyStore> getKeyStores() throws KeyStoreManagerException
+ {
+ if (keyStores == null)
+ {
+ keyStores = new ArrayList<IKeyStore>();
+ SaveStateManager manager = null;
+ try
+ {
+ manager = SaveStateManager.getInstance();
+ if (manager.getMappedKeystores() != null)
+ {
+ for (File keystoreFile : manager.getMappedKeystores())
+ {
+ SaveStateManager.ViewStateEntry stateEntry = manager.getEntry(keystoreFile);
+ if (stateEntry != null)
+ {
+ IKeyStore keyStoreNode = new KeyStoreNode(keystoreFile);
+ keyStoreNode.setType(stateEntry.getKeystoreType());
+ keyStoreNode.setLastBackupDate(stateEntry.getBackupDate());
+ keyStores.add(keyStoreNode);
+ }
+ }
+ }
+ }
+ catch (IOException e)
+ {
+ throw new KeyStoreManagerException(ERROR_TO_ACCESS_KEYSTORE_MAPPING_PERSISTENCE, e);
+ }
+ }
+ return keyStores;
+ }
+
+ /**
+ * Check if a keystore file is already mapped.
+ * The input parameter is a File, instead of a String, to ensure that File.getCanonicalPath() will be used in the filenames comparison.
+ * @param keystoreFile A file representing the keystore.
+ * @return True if the file is already mapped, false otherwise.
+ * */
+ public boolean isKeystoreMapped(File keystoreFile)
+ {
+ boolean result = false;
+ SaveStateManager manager = null;
+
+ try
+ {
+ manager = SaveStateManager.getInstance();
+ if (manager.getMappedKeystores() != null)
+ {
+ for (File mappedKeystoreFile : manager.getMappedKeystores())
+ {
+ if (mappedKeystoreFile.getCanonicalPath().equals(
+ keystoreFile.getCanonicalPath()))
+ {
+ result = true;
+ break;
+ }
+ }
+ }
+ }
+ catch (IOException e)
+ {
+ result = false;
+ StudioLogger
+ .error(getClass(),
+ "IOException while trying to check if a file is mapped on Signing and Keys view.");
+ }
+
+ return result;
+ }
+
+ /**
+ * The current available keystore types are:
+ * <ul>
+ * <li>JKS</li>
+ * <li>JCEKS</li>
+ * <li>PKCS12</li>
+ * </ul>
+ * @return The list of available keystore types.
+ * */
+ public List<String> getAvailableTypes()
+ {
+ List<String> availableKeystoreTypes = new ArrayList<String>();
+
+ availableKeystoreTypes.add(KEYSTORE_TYPE_JKS);
+ availableKeystoreTypes.add(KEYSTORE_TYPE_JCEKS);
+ availableKeystoreTypes.add(KEYSTORE_TYPE_PKCS12);
+
+ if (!availableKeystoreTypes.contains(getDefaultType()))
+ {
+ availableKeystoreTypes.add(getDefaultType());
+ }
+
+ return availableKeystoreTypes;
+ }
+
+ /**
+ * When no store type is specified, the manager define a type that should be used as the default one.
+ * @return The default keystore type used in the Signing and Keys view.
+ * */
+ public String getDefaultType()
+ {
+ return KeyStore.getDefaultType().toUpperCase();
+ }
+
+ /**
+ * Create a new keystore given a file, a store type and a password.
+ */
+ public static IKeyStore createKeyStore(File keyStoreFile, String keyStoreType, char[] password)
+ throws KeyStoreManagerException
+ {
+ IKeyStore keyStoreNode = null;
+ try
+ {
+ KeyStore keyStore = KeyStoreUtils.createKeystore(keyStoreFile, keyStoreType, password);
+ keyStoreNode = new KeyStoreNode(keyStoreFile, keyStore);
+ }
+ catch (InvalidPasswordException e)
+ {
+ StudioLogger.error("Invalid password when creating a keystore: " + e.getMessage());
+ }
+ return keyStoreNode;
+ }
+
+ /**
+ * Create a new keystore given a file and a password.
+ * The store type is set to be the default.
+ */
+ public static IKeyStore createKeyStore(File keyStoreFile, char[] password)
+ throws KeyStoreManagerException
+ {
+ IKeyStore keyStoreNode = null;
+ try
+ {
+ KeyStore keyStore = KeyStoreUtils.createKeystore(keyStoreFile, password);
+ keyStoreNode = new KeyStoreNode(keyStoreFile, keyStore);
+ }
+ catch (InvalidPasswordException e)
+ {
+ StudioLogger.error("Invalid password when creating a keystore: " + e.getMessage());
+ }
+
+ return keyStoreNode;
+ }
+}
diff --git a/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/core/KeyStoreUtils.java b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/core/KeyStoreUtils.java
new file mode 100644
index 0000000..46f6fc5
--- /dev/null
+++ b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/core/KeyStoreUtils.java
@@ -0,0 +1,686 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.studio.android.certmanager.core;
+
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.math.BigInteger;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.KeyStore;
+import java.security.KeyStore.Builder;
+import java.security.KeyStore.Entry;
+import java.security.KeyStore.PasswordProtection;
+import java.security.KeyStore.PrivateKeyEntry;
+import java.security.KeyStore.ProtectionParameter;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.SecureRandom;
+import java.security.UnrecoverableKeyException;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.security.interfaces.RSAPrivateKey;
+import java.security.interfaces.RSAPublicKey;
+import java.util.Calendar;
+import java.util.GregorianCalendar;
+import java.util.Map;
+
+import org.bouncycastle.asn1.ASN1InputStream;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.x500.X500Name;
+import org.bouncycastle.asn1.x500.X500NameBuilder;
+import org.bouncycastle.asn1.x500.style.BCStrictStyle;
+import org.bouncycastle.asn1.x500.style.BCStyle;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.bouncycastle.cert.X509CertificateHolder;
+import org.bouncycastle.cert.X509v3CertificateBuilder;
+import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
+import org.bouncycastle.crypto.params.RSAKeyParameters;
+import org.bouncycastle.operator.ContentSigner;
+import org.bouncycastle.operator.DefaultDigestAlgorithmIdentifierFinder;
+import org.bouncycastle.operator.DefaultSignatureAlgorithmIdentifierFinder;
+import org.bouncycastle.operator.OperatorCreationException;
+import org.bouncycastle.operator.bc.BcContentSignerBuilder;
+import org.bouncycastle.operator.bc.BcRSAContentSignerBuilder;
+import org.eclipse.osgi.util.NLS;
+
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.common.utilities.FileUtil;
+import com.motorolamobility.studio.android.certmanager.exception.InvalidPasswordException;
+import com.motorolamobility.studio.android.certmanager.exception.KeyStoreManagerException;
+import com.motorolamobility.studio.android.certmanager.i18n.CertificateManagerNLS;
+import com.motorolamobility.studio.android.certmanager.ui.model.CertificateDetailsInfo;
+
+public class KeyStoreUtils
+{
+ private static final String ERROR_DELETING_ALIAS =
+ CertificateManagerNLS.KeyStoreUtils_ErrorDeletingAlias;
+
+ /**
+ * Creates a new empty KeyStore, from the default type, located at keyStoreFile with the password, password
+ * @param keyStoreFile The file pointing o where the new KeyStore will be located
+ * @param password the password for the new KeyStore
+ * @return the {@link KeyStore} representing the new KeyStore
+ * @throws InvalidPasswordException
+ * @throws KeyStoreException if KeyStore can't be created
+ */
+ public static KeyStore createKeystore(File keyStoreFile, char[] password)
+ throws KeyStoreManagerException, InvalidPasswordException
+ {
+ return createKeystore(keyStoreFile, KeyStore.getDefaultType(), password);
+ }
+
+ /**
+ * Creates a new empty KeyStore, located at keyStoreFile with the password, password
+ * @param keyStoreFile The file pointing o where the new KeyStore will be located
+ * @param keyStoreType The type of the new KeyStore
+ * @param password the password for the new KeyStore
+ * @return the {@link KeyStore} representing the new KeyStore
+ * @throws InvalidPasswordException
+ * @throws KeyStoreException if KeyStore can't be created
+ */
+ public static KeyStore createKeystore(File keyStoreFile, String keyStoreType, char[] password)
+ throws KeyStoreManagerException, InvalidPasswordException
+ {
+ KeyStore keyStore = null;
+ if ((keyStoreFile != null) && !keyStoreFile.exists())
+ {
+ keyStore = loadKeystore(keyStoreFile, password, keyStoreType);
+ try
+ {
+ writeKeyStore(keyStore, password, keyStoreFile);
+ }
+ catch (Exception e)
+ {
+ throw new KeyStoreManagerException(NLS.bind(
+ CertificateManagerNLS.KeyStoreUtils_Error_WriteKeyStore, keyStoreFile), e);
+ }
+ }
+ else
+ {
+ throw new KeyStoreManagerException(NLS.bind(
+ CertificateManagerNLS.KeyStoreUtils_Error_FileAlreadyExists, keyStoreFile));
+ }
+
+ return keyStore;
+ }
+
+ public static void writeKeyStore(KeyStore keyStore, char[] password, File keyStoreFile)
+ throws FileNotFoundException, KeyStoreException, IOException, NoSuchAlgorithmException,
+ CertificateException, KeyStoreManagerException, InvalidPasswordException
+ {
+
+ writeKeyStore(keyStore, null, password, keyStoreFile);
+ }
+
+ private static void writeKeyStore(KeyStore keyStore, char[] oldPassword, char[] newPassword,
+ File keyStoreFile) throws FileNotFoundException, KeyStoreException, IOException,
+ NoSuchAlgorithmException, CertificateException, KeyStoreManagerException,
+ InvalidPasswordException
+ {
+ FileOutputStream fos = null;
+ try
+ {
+ if (oldPassword != null)
+ {
+ if (loadKeystore(keyStoreFile, oldPassword, keyStore.getType()) != null)
+ {
+ fos = new FileOutputStream(keyStoreFile);
+ keyStore.store(fos, newPassword);
+ }
+ }
+ else
+ {
+ fos = new FileOutputStream(keyStoreFile);
+ keyStore.store(fos, newPassword);
+ }
+ }
+ finally
+ {
+ if (fos != null)
+ {
+ try
+ {
+ fos.close();
+ }
+ catch (IOException e)
+ {
+ StudioLogger.error("Could not close steam while writing keystore file. "
+ + e.getMessage());
+ }
+ }
+ }
+ }
+
+ /**
+ * Loads a KeyStore from a given file from the default type, usually JKS.
+ * If keyStoreFile path don't exist then a new empty KeyStore will be created on the given location.
+ * <b>Note:</b> Calling this method is the same as calling loadKeystore(keyStoreFile, password, KeyStore.getDefaultType())
+ * @param keyStoreFile The keyStore location.
+ * @param password The KeyStore password
+ * @return the {@link KeyStore} representing the file.
+ * @throws KeyStoreManagerException
+ * @throws InvalidPasswordException
+ */
+ public static KeyStore loadKeystore(File keyStoreFile, char[] password)
+ throws KeyStoreManagerException, InvalidPasswordException
+ {
+ return loadKeystore(keyStoreFile, password, KeyStore.getDefaultType());
+ }
+
+ /**
+ * Loads a KeyStore from a given file.
+ * If keyStoreFile path don't exist then a new empty KeyStore will be created on memory.
+ * If you want o create a new KeyStore file, calling createStore is recommended.
+ * @param keyStoreFile The keyStore location.
+ * @param password The KeyStore password
+ * @param storeType The Type of the keystore o be loaded.
+ * @return the {@link KeyStore} representing the file.
+ * @throws KeyStoreManagerException
+ * @throws InvalidPasswordException
+ */
+ public static KeyStore loadKeystore(File keyStoreFile, char[] password, String storeType)
+ throws KeyStoreManagerException, InvalidPasswordException
+ {
+ KeyStore keyStore = null;
+ FileInputStream fis = null;
+ try
+ {
+ keyStore = KeyStore.getInstance(storeType);
+
+ if ((keyStoreFile != null) && keyStoreFile.exists() && (keyStoreFile.length() > 0))
+ {
+ fis = new FileInputStream(keyStoreFile);
+ }
+
+ //fis = null means a new keyStore will be created
+ keyStore.load(fis, password);
+ }
+ catch (IOException e)
+ {
+ if (e.getMessage().contains("password was incorrect")
+ || (e.getCause() instanceof UnrecoverableKeyException))
+ {
+ throw new InvalidPasswordException(e.getMessage());
+ }
+ else
+ {
+ throw new KeyStoreManagerException(NLS.bind(
+ CertificateManagerNLS.KeyStoreUtils_Error_LoadKeyStore, keyStoreFile), e);
+ }
+ }
+ catch (Exception e)
+ {
+ throw new KeyStoreManagerException(NLS.bind(
+ CertificateManagerNLS.KeyStoreUtils_Error_LoadKeyStore, keyStoreFile), e);
+ }
+ finally
+ {
+ if (fis != null)
+ {
+ try
+ {
+ fis.close();
+ }
+ catch (IOException e)
+ {
+ StudioLogger.error("Could not close steam while loading keystore. "
+ + e.getMessage());
+ }
+ }
+ }
+
+ return keyStore;
+ }
+
+ /**
+ * Simply deletes the KeyStore File
+ * @param keyStoreFile teh KeyStore file to be deleted.
+ * @throws KeyStoreException If any error occur.
+ */
+ public static void deleteKeystore(File keyStoreFile) throws KeyStoreManagerException
+ {
+ try
+ {
+ FileUtil.deleteFile(keyStoreFile);
+ }
+ catch (IOException e)
+ {
+ throw new KeyStoreManagerException(NLS.bind(
+ CertificateManagerNLS.KeyStoreUtils_Error_DeleteKeyStore, keyStoreFile), e);
+ }
+ }
+
+ /**
+ * Write the keyStore in to the given file, protecting it with password.
+ * Warn: Since there's actually no way to change the password this method will overwrite the existing file with the keyStore contents,
+ * without further warning.
+ * @param keyStore the {@link KeyStore} to be written.
+ * @param keyStoreFile The KeyStore location
+ * @param oldPassword
+ * @param sourcePassword the new Password
+ * @throws KeyStoreException If file could no be write.
+ */
+ public static void changeKeystorePasswd(KeyStore keyStore, File keyStoreFile,
+ char[] oldPassword, char[] newPassword) throws KeyStoreManagerException
+ {
+ try
+ {
+ keyStore = loadKeystore(keyStoreFile, oldPassword, keyStore.getType());
+ writeKeyStore(keyStore, oldPassword, newPassword, keyStoreFile);
+ }
+ catch (Exception e)
+ {
+ throw new KeyStoreManagerException(NLS.bind(
+ CertificateManagerNLS.KeyStoreUtils_Error_WriteKeyStore, keyStoreFile), e);
+ }
+ }
+
+ /**
+ * Adds a new enty to a given keyStore.
+ * @param keyStore The Keystore that will receive the entry
+ * @param keyStorePassword The KeyStore password
+ * @param keyStoreFile The KeyStore file path
+ * @param alias The new entry alias
+ * @param entry The Entry to be added
+ * @param entryPassword The password to protect the entry
+ * @throws KeyStoreManagerException if any error occurs.
+ */
+ public static void addEntry(KeyStore keyStore, char[] keyStorePassword, File keyStoreFile,
+ String alias, Entry entry, char[] entryPassword) throws KeyStoreManagerException
+ {
+ try
+ {
+ PasswordProtection passwordProtection = new KeyStore.PasswordProtection(entryPassword);
+ keyStore = loadKeystore(keyStoreFile, keyStorePassword, keyStore.getType());
+
+ if (!keyStore.containsAlias(alias))
+ {
+ keyStore.setEntry(alias, entry, passwordProtection);
+ writeKeyStore(keyStore, keyStorePassword, keyStoreFile);
+ }
+ else
+ {
+ throw new KeyStoreManagerException(NLS.bind("Alias \"{0}\" already exists.", alias));
+ }
+
+ }
+ catch (KeyStoreManagerException e)
+ {
+ throw e;
+ }
+ catch (Exception e)
+ {
+ throw new KeyStoreManagerException(NLS.bind(
+ CertificateManagerNLS.KeyStoreUtils_Error_AddEntryToKeyStore, alias), e);
+ }
+ }
+
+ /**
+ * Adds a new enty to a given keyStore.
+ * @param keyStore The Keystore that will receive the entry
+ * @param keyStorePassword The KeyStore password
+ * @param keyStoreFile The KeyStore file path
+ * @param alias The new entry alias
+ * @param entry The Entry to be added
+ * @param entryPassword The password to protect the entry
+ * @throws KeyStoreManagerException if any error occurs.
+ */
+ public static void changeEntryPassword(KeyStore keyStore, char[] keyStorePassword,
+ File keyStoreFile, String alias, Entry entry, char[] entryPassword)
+ throws KeyStoreManagerException
+ {
+ try
+ {
+ PasswordProtection passwordProtection = new KeyStore.PasswordProtection(entryPassword);
+ keyStore.setEntry(alias, entry, passwordProtection);
+ writeKeyStore(keyStore, keyStorePassword, keyStoreFile);
+ }
+ catch (Exception e)
+ {
+ throw new KeyStoreManagerException(NLS.bind(
+ "Error attempting to change password for {0}", alias), e);
+ }
+ }
+
+ /**
+ * Create a new X509 certificate for a given KeyPair
+ * @param keyPair the {@link KeyPair} used to create the certificate,
+ * RSAPublicKey and RSAPrivateKey are mandatory on keyPair, IllegalArgumentExeption will be thrown otherwise.
+ * @param issuerName The issuer name to be used on the certificate
+ * @param ownerName The owner name to be used on the certificate
+ * @param expireDate The expire date
+ * @return The {@link X509Certificate}
+ * @throws IOException
+ * @throws OperatorCreationException
+ * @throws CertificateException
+ */
+ public static X509Certificate createX509Certificate(KeyPair keyPair,
+ CertificateDetailsInfo certDetails) throws IOException, OperatorCreationException,
+ CertificateException
+ {
+
+ PublicKey publicKey = keyPair.getPublic();
+ PrivateKey privateKey = keyPair.getPrivate();
+ if (!(publicKey instanceof RSAPublicKey) || !(privateKey instanceof RSAPrivateKey))
+ {
+ throw new IllegalArgumentException(
+ CertificateManagerNLS.KeyStoreUtils_RSA_Keys_Expected);
+ }
+
+ RSAPublicKey rsaPublicKey = (RSAPublicKey) publicKey;
+ RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) privateKey;
+
+ //Transform the PublicKey into the BouncyCastle expected format
+ ASN1InputStream asn1InputStream = null;
+ X509Certificate x509Certificate = null;
+
+ try
+ {
+ asn1InputStream =
+ new ASN1InputStream(new ByteArrayInputStream(rsaPublicKey.getEncoded()));
+ SubjectPublicKeyInfo pubKey =
+ new SubjectPublicKeyInfo((ASN1Sequence) asn1InputStream.readObject());
+
+ X500NameBuilder nameBuilder = new X500NameBuilder(new BCStrictStyle());
+ addField(BCStyle.C, certDetails.getCountry(), nameBuilder);
+ addField(BCStyle.ST, certDetails.getState(), nameBuilder);
+ addField(BCStyle.L, certDetails.getLocality(), nameBuilder);
+ addField(BCStyle.O, certDetails.getOrganization(), nameBuilder);
+ addField(BCStyle.OU, certDetails.getOrganizationUnit(), nameBuilder);
+ addField(BCStyle.CN, certDetails.getCommonName(), nameBuilder);
+
+ X500Name subjectName = nameBuilder.build();
+ X500Name issuerName = subjectName;
+ X509v3CertificateBuilder certBuilder =
+ new X509v3CertificateBuilder(issuerName, BigInteger.valueOf(new SecureRandom()
+ .nextInt()), GregorianCalendar.getInstance().getTime(),
+ certDetails.getExpirationDate(), subjectName, pubKey);
+
+ AlgorithmIdentifier sigAlgId =
+ new DefaultSignatureAlgorithmIdentifierFinder().find("SHA1withRSA"); //$NON-NLS-1$
+ AlgorithmIdentifier digAlgId =
+ new DefaultDigestAlgorithmIdentifierFinder().find(sigAlgId);
+ BcContentSignerBuilder sigGen = new BcRSAContentSignerBuilder(sigAlgId, digAlgId);
+
+ //Create RSAKeyParameters, the private key format expected by Bouncy Castle
+ RSAKeyParameters keyParams =
+ new RSAKeyParameters(true, rsaPrivateKey.getPrivateExponent(),
+ rsaPrivateKey.getModulus());
+
+ ContentSigner contentSigner = sigGen.build(keyParams);
+ X509CertificateHolder certificateHolder = certBuilder.build(contentSigner);
+
+ //Convert the X509Certificate from BouncyCastle format to the java.security format
+ JcaX509CertificateConverter certConverter = new JcaX509CertificateConverter();
+ x509Certificate = certConverter.getCertificate(certificateHolder);
+ }
+ finally
+ {
+ if (asn1InputStream != null)
+ {
+ try
+ {
+ asn1InputStream.close();
+ }
+ catch (IOException e)
+ {
+ StudioLogger.error("Could not close stream while creating X509 certificate. "
+ + e.getMessage());
+ }
+ }
+ }
+
+ return x509Certificate;
+ }
+
+ private static void addField(ASN1ObjectIdentifier objectId, String value,
+ X500NameBuilder nameBuilder)
+ {
+ if (value.length() > 0)
+ {
+ nameBuilder.addRDN(objectId, value);
+ }
+ }
+
+ /**
+ * Creates a new RSA KeyPair
+ * @return the new {@link KeyPair}
+ */
+ public static KeyPair genKeyPair() throws NoSuchAlgorithmException
+ {
+ KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA"); //$NON-NLS-1$
+ keyPairGen.initialize(2048); //As recommended by Android guys, key is created with 2048 bits.
+ KeyPair keyPair = keyPairGen.genKeyPair();
+ return keyPair;
+ }
+
+ /**
+ * Create a new private key entry inside the key pair
+ * @param keyPair
+ * @param x509Certificate
+ * @return
+ */
+ public static PrivateKeyEntry createPrivateKeyEntry(KeyPair keyPair,
+ X509Certificate x509Certificate)
+ {
+ Certificate[] certChain = new Certificate[]
+ {
+ x509Certificate
+ };
+ PrivateKeyEntry privateKeyEntry =
+ new KeyStore.PrivateKeyEntry(keyPair.getPrivate(), certChain);
+ return privateKeyEntry;
+ }
+
+ public static void deleteEntry(KeyStore keyStore, char[] password, File keyStoreFile,
+ String alias) throws KeyStoreManagerException
+ {
+ try
+ {
+ keyStore = loadKeystore(keyStoreFile, password, keyStore.getType());
+
+ keyStore.deleteEntry(alias);
+ writeKeyStore(keyStore, password, keyStoreFile);
+ }
+ catch (Exception e)
+ {
+ StudioLogger.error(KeyStoreUtils.class, ERROR_DELETING_ALIAS + alias, e);
+ throw new KeyStoreManagerException(ERROR_DELETING_ALIAS + alias, e);
+ }
+ }
+
+ /**
+ * Change a keyStore type.
+ * @param keyStoreFile The KeyStoreFile
+ * @param password The KeyStore Password
+ * @param originalType the original Type
+ * @param destinationType the new KeyStore Type
+ * @throws KeyStoreManagerException If any error occurs, the operation will be canceled and reverted automatically.
+ * @throws InvalidPasswordException
+ */
+ public static void changeKeyStoreType(File keyStoreFile, char[] password, String originalType,
+ String destinationType, Map<String, String> aliases) throws KeyStoreManagerException,
+ InvalidPasswordException
+ {
+ boolean rollBack = false;
+ String timeStamp = Long.toString(Calendar.getInstance().getTimeInMillis());
+ File oldKsFile = new File(keyStoreFile.getAbsolutePath() + "_" + timeStamp);
+ oldKsFile.delete();
+ boolean renamed = false;
+ renamed = keyStoreFile.renameTo(oldKsFile);
+ if (renamed)
+ {
+ try
+ {
+ Builder oldKsBuilder =
+ KeyStore.Builder.newInstance(originalType, null, oldKsFile,
+ new PasswordProtection(password));
+ KeyStore oldKeyStore = oldKsBuilder.getKeyStore();
+
+ KeyStore newKeyStore = createKeystore(keyStoreFile, destinationType, password);
+ for (String alias : aliases.keySet())
+ {
+ ProtectionParameter protectionParameter =
+ new PasswordProtection(aliases.get(alias).toCharArray());
+ Entry entry = oldKeyStore.getEntry(alias, protectionParameter);
+ newKeyStore.setEntry(alias, entry, protectionParameter);
+ }
+ writeKeyStore(newKeyStore, password, keyStoreFile);
+ }
+ catch (InvalidPasswordException e)
+ {
+ rollBack = true;
+ StudioLogger
+ .error(KeyStoreUtils.class,
+ "Invalid password while trying to create a new keystore, changing a keyStore type.",
+ e);
+
+ }
+ catch (Exception e)
+ {
+ if (e.getMessage().contains("password was incorrect")
+ || e.getCause().getMessage().contains("password was incorrect"))
+ {
+ keyStoreFile.delete();
+ oldKsFile.renameTo(keyStoreFile);
+ throw new InvalidPasswordException(e.getMessage());
+ }
+ else
+ {
+ StudioLogger.error(KeyStoreUtils.class,
+ "Exception occurred while attempting to change a keyStore type.", e);
+ rollBack = true;
+ }
+ }
+
+ if (rollBack)
+ {
+ keyStoreFile.delete();
+ oldKsFile.renameTo(keyStoreFile);
+
+ throw new KeyStoreManagerException(NLS.bind(
+ "Could not convert the KeyStore {0} to type {1}", keyStoreFile,
+ destinationType));
+ }
+ }
+ else
+ {
+ throw new KeyStoreManagerException(
+ NLS.bind(
+ "Could not convert the KeyStore {0} to type {1}, could not backup the current keyStore file, maybe it's in use by another program.",
+ keyStoreFile, destinationType));
+ }
+ oldKsFile.delete();
+ }
+
+ /**
+ * Import a set of entries from sourcekeystore into the targetkeystore.
+ * If alias already exists on the target keystore then the alias is concatenated with the
+ * source keystore file name.
+ * @param targetKeyStore
+ * @param targetFile
+ * @param targetType
+ * @param targetPasswd
+ * @param sourceKeyStore
+ * @param sourceKeyStoreFile
+ * @param sourcePasswd
+ * @param aliases a map<String, String> containing alias as key and its password as value. this method assume that the password is correct
+ * @throws InvalidPasswordException
+ * @throws KeyStoreManagerException
+ */
+ public static void importKeys(KeyStore targetKeyStore, File targetFile, String targetType,
+ char[] targetPasswd, KeyStore sourceKeyStore, File sourceKeyStoreFile,
+ char[] sourcePasswd, Map<String, String> aliases) throws InvalidPasswordException,
+ KeyStoreManagerException
+ {
+ if (!isValidKeyStorePasswd(targetFile, targetType, targetPasswd))
+ {
+ throw new InvalidPasswordException(
+ CertificateManagerNLS.PasswordChanged_InvalidKeystorePassword);
+ }
+
+ try
+ {
+ for (String alias : aliases.keySet())
+ {
+ if (sourceKeyStore.containsAlias(alias))
+ {
+ ProtectionParameter protectionParameter =
+ new PasswordProtection(aliases.get(alias).toCharArray());
+ Entry entry = sourceKeyStore.getEntry(alias, protectionParameter);
+ if (targetKeyStore.containsAlias(alias))
+ {
+ alias += "_" + sourceKeyStoreFile.getName();
+ }
+ int i = 1;
+ while (targetKeyStore.containsAlias(alias))
+ {
+ alias += "_" + i;
+ i++;
+ }
+ targetKeyStore.setEntry(alias, entry, protectionParameter);
+ }
+ else
+ {
+ StudioLogger
+ .error(KeyStoreUtils.class,
+ NLS.bind(
+ "Alias {0} could not be imported because it doesn't exists on originKeyStore",
+ alias));
+ }
+ }
+ writeKeyStore(targetKeyStore, targetPasswd, targetFile);
+ }
+ catch (Exception e)
+ {
+ throw new KeyStoreManagerException("Could not import the selected aliases into "
+ + targetFile.getName(), e);
+ }
+ }
+
+ /**
+ * Verifies if the password if valid
+ * @param keyStoreFile
+ * @param keyStoreType
+ * @param passwd
+ * @return true if password is valid, false otherwise.
+ * @throws KeyStoreManagerException
+ */
+ public static boolean isValidKeyStorePasswd(File keyStoreFile, String keyStoreType,
+ char[] passwd) throws KeyStoreManagerException
+ {
+ KeyStore keystore = null;
+ try
+ {
+ keystore = loadKeystore(keyStoreFile, passwd, keyStoreType);
+ }
+ catch (InvalidPasswordException e)
+ {
+ //Do nothing, password is invalid
+ }
+ return keystore != null;
+ }
+}
diff --git a/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/core/KeyStoreUtilsTest.java b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/core/KeyStoreUtilsTest.java
new file mode 100644
index 0000000..9ec1abf
--- /dev/null
+++ b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/core/KeyStoreUtilsTest.java
@@ -0,0 +1,243 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.studio.android.certmanager.core;
+
+import java.io.File;
+import java.io.IOException;
+import java.security.KeyPair;
+import java.security.KeyStore;
+import java.security.KeyStore.PrivateKeyEntry;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.util.HashMap;
+import java.util.Map;
+
+import junit.framework.TestCase;
+
+import org.bouncycastle.operator.OperatorCreationException;
+import org.junit.Test;
+
+import com.motorolamobility.studio.android.certmanager.exception.InvalidPasswordException;
+import com.motorolamobility.studio.android.certmanager.exception.KeyStoreManagerException;
+import com.motorolamobility.studio.android.certmanager.ui.model.CertificateDetailsInfo;
+
+public class KeyStoreUtilsTest extends TestCase
+{
+
+ File keyStoreFile = null;
+
+ private String passwd;
+
+ /* (non-Javadoc)
+ * @see junit.framework.TestCase#setUp()
+ */
+ @Override
+ protected void setUp() throws Exception
+ {
+ keyStoreFile = File.createTempFile("testKeystore", ".tmp");
+ passwd = "passwd";
+ super.setUp();
+ }
+
+ @Test
+ public void testCreateKeystore()
+ {
+
+ KeyStore keyStore = null;
+ try
+ {
+ keyStore = KeyStoreUtils.createKeystore(keyStoreFile, passwd.toCharArray());
+ }
+ catch (KeyStoreManagerException e)
+ {
+ assert (false);
+ }
+ catch (InvalidPasswordException e)
+ {
+ assert (false);
+ }
+
+ assert (keyStore != null);
+ assert (keyStoreFile.length() > 0);
+
+ }
+
+ @Test
+ public void testLoadKeyStore()
+ {
+ try
+ {
+ KeyStore keyStore =
+ KeyStoreUtils.loadKeystore(keyStoreFile, passwd.toCharArray(), "JKS");
+ keyStore.aliases();
+ }
+ catch (KeyStoreException e)
+ {
+ assert false;
+ }
+ catch (KeyStoreManagerException e)
+ {
+ assert false;
+ }
+ catch (InvalidPasswordException e)
+ {
+ assert false;
+ }
+ }
+
+ @Test
+ public void testCreateCertificate()
+ {
+ try
+ {
+ KeyStore keyStore =
+ KeyStoreUtils.loadKeystore(keyStoreFile, passwd.toCharArray(), "JKS");
+
+ KeyPair keyPair = KeyStoreUtils.genKeyPair();
+ X509Certificate x509Certificate =
+ KeyStoreUtils.createX509Certificate(keyPair, new CertificateDetailsInfo("test",
+ "nome", "org", "orgUn", "testUni", "country", "Estate", "30", ""));
+
+ PrivateKeyEntry privateKeyEntry =
+ KeyStoreUtils.createPrivateKeyEntry(keyPair, x509Certificate);
+
+ KeyStoreUtils.addEntry(keyStore, passwd.toCharArray(), keyStoreFile, "aliasTest",
+ privateKeyEntry, "aliaspass".toCharArray());
+ }
+ catch (Exception e)
+ {
+ assert (false);
+ }
+ }
+
+ @Test
+ public void testChangePasswd()
+ {
+
+ KeyStore keyStore = null;
+ try
+ {
+ keyStore = KeyStoreUtils.loadKeystore(keyStoreFile, passwd.toCharArray());
+ }
+ catch (KeyStoreManagerException e)
+ {
+ assert false;
+ }
+ catch (InvalidPasswordException e)
+ {
+ assert false;
+ }
+
+ File keyStoreFile2 = new File(keyStoreFile + "_2");
+ try
+ {
+ KeyStoreUtils.changeKeystorePasswd(keyStore, keyStoreFile2, passwd.toCharArray(),
+ "newPasswd2".toCharArray());
+ }
+ catch (KeyStoreManagerException e)
+ {
+ assert (false);
+ }
+
+ assert (keyStore != null);
+ assert (keyStoreFile2.length() > 0);
+ }
+
+ @Test
+ public void testChangeKsType()
+ {
+ try
+ {
+ KeyStoreUtils.createKeystore(keyStoreFile, "JKS", passwd.toCharArray());
+ KeyStoreUtils.changeKeyStoreType(keyStoreFile, passwd.toCharArray(), "JKS", "JCEKS",
+ new HashMap<String, String>(0));
+ }
+ catch (KeyStoreManagerException e)
+ {
+ e.printStackTrace();
+ }
+ catch (InvalidPasswordException e)
+ {
+ e.printStackTrace();
+ }
+ }
+
+ @Test
+ public void testImportKeys()
+ {
+ File keyStoreFile1 = new File(keyStoreFile.getAbsolutePath() + "_import");
+ try
+ {
+ KeyStoreUtils.createKeystore(keyStoreFile1, "pass1".toCharArray());
+ }
+ catch (KeyStoreManagerException e)
+ {
+ e.printStackTrace();
+ }
+ catch (InvalidPasswordException e)
+ {
+ e.printStackTrace();
+ }
+ try
+ {
+ KeyStore keyStore =
+ KeyStoreUtils.loadKeystore(keyStoreFile, passwd.toCharArray(), "JKS");
+ KeyPair keyPair = KeyStoreUtils.genKeyPair();
+ X509Certificate x509Certificate =
+ KeyStoreUtils.createX509Certificate(keyPair, new CertificateDetailsInfo("test",
+ "nome", "org", "orgUn", "testUni", "country", "Estate", "30", ""));
+
+ PrivateKeyEntry privateKeyEntry =
+ KeyStoreUtils.createPrivateKeyEntry(keyPair, x509Certificate);
+
+ KeyStoreUtils.addEntry(keyStore, passwd.toCharArray(), keyStoreFile, "aliasTest",
+ privateKeyEntry, passwd.toCharArray());
+
+ keyStore = KeyStoreUtils.loadKeystore(keyStoreFile, passwd.toCharArray(), "JKS");
+
+ Map<String, String> aliases = new HashMap<String, String>(1);
+ aliases.put("aliasTest", passwd);
+ // KeyStoreUtils.importKeys(keyStore1, keyStoreFile1, "pass1".toCharArray(), keyStore, keyStoreFile1
+ // passwd.toCharArray(), aliases);
+ }
+ catch (KeyStoreManagerException e)
+ {
+ e.printStackTrace();
+ }
+ catch (InvalidPasswordException e)
+ {
+ e.printStackTrace();
+ }
+ catch (NoSuchAlgorithmException e)
+ {
+ e.printStackTrace();
+ }
+ catch (OperatorCreationException e)
+ {
+ e.printStackTrace();
+ }
+ catch (CertificateException e)
+ {
+ e.printStackTrace();
+ }
+ catch (IOException e)
+ {
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/core/PasswordProvider.java b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/core/PasswordProvider.java
new file mode 100644
index 0000000..ce73f09
--- /dev/null
+++ b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/core/PasswordProvider.java
@@ -0,0 +1,484 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.studio.android.certmanager.core;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+
+import org.eclipse.equinox.security.storage.ISecurePreferences;
+import org.eclipse.equinox.security.storage.SecurePreferencesFactory;
+import org.eclipse.equinox.security.storage.StorageException;
+import org.eclipse.jface.dialogs.Dialog;
+import org.eclipse.osgi.util.NLS;
+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.Control;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.ui.PlatformUI;
+
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.common.utilities.EclipseUtils;
+import com.motorolamobility.studio.android.certmanager.CertificateManagerActivator;
+import com.motorolamobility.studio.android.certmanager.exception.KeyStoreManagerException;
+import com.motorolamobility.studio.android.certmanager.i18n.CertificateManagerNLS;
+
+/**
+ * This class is responsible to retrieve passwords for a keystore and its entries.
+ * Usage:
+ * Instantiate with a keyStoreFile, call the methods getPassword.
+ * If needed a dialog will be shown, asking user to type the password.
+ */
+public class PasswordProvider
+{
+
+ private static final String PREF_ROOT_NODE = CertificateManagerActivator.PLUGIN_ID
+ + "_passwords"; //$NON-NLS-1$
+
+ private static final String KS_PASSWORD_KEY = "KS_PASSWORD"; //$NON-NLS-1$
+
+ private final class KeyStorePasswdDialog extends Dialog
+ {
+ private final File keyStoreFile;
+
+ private String passwd;
+
+ private boolean savePasswd;
+
+ private Text paswordText;
+
+ private Button saveCheckBox;
+
+ private final String alias;
+
+ private KeyStorePasswdDialog(Shell parentShell, File keyStoreFile, String alias)
+ {
+ super(parentShell);
+ this.keyStoreFile = keyStoreFile;
+ this.alias = alias;
+ }
+
+ @Override
+ protected Control createDialogArea(Composite parent)
+ {
+
+ Composite mainComposite = new Composite(parent, SWT.NONE);
+ GridLayout gridLayout = new GridLayout(2, false);
+ mainComposite.setLayout(gridLayout);
+
+ //Creates the message
+ Label messageLabel = new Label(mainComposite, SWT.NONE);
+ GridData gridData = new GridData(SWT.LEFT, SWT.CENTER, false, false, 2, 2);
+ messageLabel.setLayoutData(gridData);
+
+ if (this.alias.equals(KS_PASSWORD_KEY))
+ {
+ getShell().setText(CertificateManagerNLS.PasswordProvider_DialogTitle);
+ messageLabel.setText(NLS.bind(CertificateManagerNLS.PasswordProvider_MessageLabel,
+ keyStoreFile.getName()));
+ }
+ else
+ {
+ getShell().setText(CertificateManagerNLS.CertificateBlock_KeyPassword_Label);
+ messageLabel.setText(NLS.bind(
+ CertificateManagerNLS.PasswordProvider_Key_MessageLabel, alias));
+ }
+
+ //Creates the text field label
+ Label passwdLabel = new Label(mainComposite, SWT.NONE);
+ gridData = new GridData(SWT.LEFT, SWT.CENTER, false, false);
+ passwdLabel.setLayoutData(gridData);
+ passwdLabel.setText(CertificateManagerNLS.PasswordProvider_PasswordLabel);
+
+ //Creates the password text
+ paswordText = new Text(mainComposite, SWT.BORDER | SWT.PASSWORD);
+ gridData = new GridData(SWT.FILL, SWT.CENTER, true, false);
+ paswordText.setLayoutData(gridData);
+
+ //Creates the save password checkbox
+ saveCheckBox = new Button(mainComposite, SWT.CHECK);
+ saveCheckBox.setText(CertificateManagerNLS.PasswordProvider_SaveThisPassword);
+ saveCheckBox.setSelection(false);
+ gridData = new GridData(SWT.LEFT, SWT.CENTER, false, false, 2, 1);
+ saveCheckBox.setLayoutData(gridData);
+ saveCheckBox.setVisible(KeyStoreManager.getInstance().isKeystoreMapped(keyStoreFile));
+
+ return super.createDialogArea(parent);
+ }
+
+ @Override
+ protected void okPressed()
+ {
+ passwd = paswordText.getText();
+ savePasswd = saveCheckBox.getSelection();
+ super.okPressed();
+ }
+
+ public String getPasswd()
+ {
+ return passwd;
+ }
+
+ public boolean mustSavePasswd()
+ {
+ return savePasswd;
+ }
+ }
+
+ private final File keyStoreFile;
+
+ private final ISecurePreferences securePreferences;
+
+ private boolean canSavePassword = true;
+
+ public PasswordProvider(File keyStoreFile)
+ {
+ this(keyStoreFile, KeyStoreManager.getInstance().isKeystoreMapped(keyStoreFile));
+ }
+
+ public PasswordProvider(File keyStoreFile, boolean canSavePassword)
+ {
+ this.keyStoreFile = keyStoreFile;
+ this.securePreferences = SecurePreferencesFactory.getDefault();
+ this.canSavePassword = canSavePassword;
+ }
+
+ /**
+ * Retrieves the KeyStore password.
+ * @param promptPassword whether the password entry dialog will be shown or not
+ * @param useSavedPassword whether to use the keyStore saved password
+ * @return the password string or null if user canceled the dialog
+ * @throws KeyStoreManagerException
+ */
+ public String getKeyStorePassword(boolean promptPassword, boolean useSavedPassword)
+ throws KeyStoreManagerException
+ {
+ return getPassword(KS_PASSWORD_KEY, promptPassword, useSavedPassword);
+ }
+
+ /**
+ * Retrieves the KeyStore password.
+ * This method will always attempt to retrieve the saved password.
+ * It's behavior is the same as of calling the method getPassword(promptPassword, true)
+ * @param promptPassword whether the password entry dialog will be shown or not
+ * @return the password string or null if user canceled the dialog
+ * @throws KeyStoreManagerException
+ */
+ public String getKeyStorePassword(boolean promptPassword) throws KeyStoreManagerException
+ {
+ return getPassword(KS_PASSWORD_KEY, promptPassword, true);
+ }
+
+ /**
+ * Retrieves the password for a given alias within a keyStore.
+ * This method will always attempt to retrieve the saved password.
+ * It's behavior is the same as of calling the method getPassword(promptPassword, true)
+ * @param promptPassword whether the password entry dialog will be shown or not
+ * @return the password string or null if user canceled the dialog
+ * @throws KeyStoreManagerException
+ */
+ public String getPassword(String alias, boolean promptPassword) throws KeyStoreManagerException
+ {
+ return getPassword(alias, promptPassword, true);
+ }
+
+ /**
+ * Retrieves the password for a given alias within a keyStore.
+ * This method will always attempt to retrieve the saved password.
+ * It's behavior is the same as of calling the method getPassword(promptPassword, true)
+ * @param promptPassword whether the password entry dialog will be shown or not
+ * @param useSavedPassword whether to use the keyStore saved password
+ * @return the password string or null if user canceled the dialog
+ * @throws KeyStoreManagerException
+ */
+ public String getPassword(String alias, boolean promptPassword, boolean useSavedPassword)
+ throws KeyStoreManagerException
+ {
+ String password = null;
+
+ if (useSavedPassword)
+ {
+ if (securePreferences != null)
+ {
+ String prefKey = alias;
+ password = getSavedPasswd(prefKey);
+ }
+ else
+ {
+ throw new KeyStoreManagerException(
+ CertificateManagerNLS.PasswordProvider_Error_WhileSaving);
+ }
+ }
+
+ if ((password == null) && promptPassword)
+ {
+ password = promptPassword(alias);
+ }
+
+ return password;
+ }
+
+ private String getSavedPasswd(String prefKey)
+ {
+ String password = null;
+ // Try to get the password from secure storage
+ if (securePreferences.nodeExists(PREF_ROOT_NODE))
+ {
+ ISecurePreferences node = securePreferences.node(PREF_ROOT_NODE);
+ try
+ {
+ if (node.nodeExists(keyStoreFile.getAbsolutePath()))
+ {
+ ISecurePreferences ksNode = node.node(keyStoreFile.getAbsolutePath());
+ password = ksNode.get(prefKey, null);
+ }
+ }
+ catch (StorageException e)
+ {
+ //Do nothing, password will be null.
+ }
+ }
+ return password;
+ }
+
+ private String promptPassword(final String alias) throws KeyStoreManagerException
+ {
+ final String[] result = new String[1];
+ final Boolean[] canProceed = new Boolean[1];
+
+ Display.getDefault().syncExec(new Runnable()
+ {
+ @Override
+ public void run()
+ {
+ KeyStorePasswdDialog dialog =
+ new KeyStorePasswdDialog(PlatformUI.getWorkbench()
+ .getModalDialogShellProvider().getShell(), keyStoreFile, alias);
+
+ int diagStatus = dialog.open();
+
+ if (diagStatus == Dialog.OK)
+ {
+ //Read the values from the dialog and do the actions, return passwd and save if required
+ result[0] = dialog.getPasswd();
+
+ canSavePassword = KeyStoreManager.getInstance().isKeystoreMapped(keyStoreFile);
+ canProceed[0] = dialog.mustSavePasswd();
+ }
+ else
+ {
+ //dialog cancelled
+ canProceed[0] = false;
+ result[0] = null;
+ }
+ }
+ });
+
+ if (canProceed[0] && canSavePassword)
+ {
+ if (securePreferences != null)
+ {
+ savePassword(alias, result[0]);
+ }
+ else
+ {
+ EclipseUtils.showWarningDialog(CertificateManagerNLS.PasswordProvider_DialogTitle,
+ CertificateManagerNLS.PasswordProvider_Error_WhileSaving);
+ }
+ }
+
+ return result[0];
+ }
+
+ public void saveKeyStorePassword(String password) throws KeyStoreManagerException
+ {
+ savePassword(KS_PASSWORD_KEY, password);
+ }
+
+ public void savePassword(final String alias, String password) throws KeyStoreManagerException
+ {
+ String prefKey;
+ canSavePassword = KeyStoreManager.getInstance().isKeystoreMapped(keyStoreFile);
+ if (canSavePassword) //protect from saving
+ {
+ if (alias != null)
+ {
+ prefKey = alias;
+ }
+ else
+ {
+ prefKey = KS_PASSWORD_KEY;
+ }
+
+ ISecurePreferences rootNode = securePreferences.node(PREF_ROOT_NODE);
+ try
+ {
+ ISecurePreferences ksNode = rootNode.node(keyStoreFile.getAbsolutePath());
+ ksNode.put(prefKey, password, true);
+ ksNode.flush();
+ }
+ catch (Exception e)
+ {
+ throw new KeyStoreManagerException(
+ CertificateManagerNLS.PasswordProvider_Error_WhileSaving);
+ }
+ }
+ }
+
+ /**
+ * Deletes the entire node (including KS_PASSWORD_KEY and children aliases)
+ * @throws KeyStoreManagerException
+ */
+ public void deleteKeyStoreSavedPasswordNode() throws KeyStoreManagerException
+ {
+ deleteSavedPassword(null);
+ }
+
+ /**
+ * Deletes only KS_PASSWORD_KEY (not children aliases)
+ */
+ public void deleteKeyStoreSavedPassword() throws KeyStoreManagerException
+ {
+ deleteSavedPassword(KS_PASSWORD_KEY);
+ }
+
+ public void deleteSavedPassword(String alias) throws KeyStoreManagerException
+ {
+
+ ISecurePreferences ksNode = getKeyStoreNode();
+ if (ksNode != null)
+ {
+ if (alias == null)
+ {
+ ksNode.removeNode();
+ }
+ else
+ {
+ ksNode.remove(alias);
+ //if no item has no child, then we can remove the node
+ if (ksNode.keys().length == 0)
+ {
+ ksNode.removeNode();
+ }
+ }
+
+ try
+ {
+ ksNode.flush();
+ }
+ catch (IllegalStateException e)
+ {
+ //Do nothing, node has already been removed
+ }
+ catch (IOException e)
+ {
+ throw new KeyStoreManagerException(NLS.bind(
+ CertificateManagerNLS.PasswordProvider_Error_WhileRemovingPassword,
+ keyStoreFile.getName()));
+ }
+ }
+ }
+
+ /**
+ * This method will remove all saved entries for this keystore file that is not listed on the aliasList.
+ * The idea is to remove all saved passwords that makes reference to non-existant entries.
+ * @param aliasList the list of alias to be kept if available on the security keystore
+ * @throws KeyStoreManagerException if writing the security keystore fails for some reason
+ */
+ public void cleanModel(List<String> aliasList) throws KeyStoreManagerException
+ {
+ ISecurePreferences keyStoreNode = getKeyStoreNode();
+ if (keyStoreNode != null)
+ {
+ String[] savedKeys = keyStoreNode.keys();
+ for (String savedAlias : savedKeys)
+ {
+ if (!savedAlias.equals(KS_PASSWORD_KEY) && !aliasList.contains(savedAlias))
+ {
+ keyStoreNode.remove(savedAlias);
+ }
+ }
+ try
+ {
+ keyStoreNode.flush();
+ }
+ catch (IOException e)
+ {
+ throw new KeyStoreManagerException(NLS.bind(
+ CertificateManagerNLS.PasswordProvider_Error_WhileRemovingPassword,
+ keyStoreFile.getName()));
+ }
+ }
+ }
+
+ /*
+ * @return the keystore node if it exists
+ */
+ private ISecurePreferences getKeyStoreNode()
+ {
+ ISecurePreferences ksNode = null;
+ if (securePreferences.nodeExists(PREF_ROOT_NODE))
+ {
+ ISecurePreferences rootNode = securePreferences.node(PREF_ROOT_NODE);
+
+ if (rootNode.nodeExists(keyStoreFile.getAbsolutePath()))
+ {
+ ksNode = rootNode.node(keyStoreFile.getAbsolutePath());
+ }
+ }
+ return ksNode;
+ }
+
+ /**
+ * If keystore password is saved.
+ */
+ public boolean isPasswordSaved()
+ {
+ return isPasswordSaved(KS_PASSWORD_KEY);
+ }
+
+ /**
+ * If alias password is saved.
+ */
+ public boolean isPasswordSaved(String prefKey)
+ {
+ ISecurePreferences ksNode = null;
+ boolean isSaved = false;
+ if (securePreferences.nodeExists(PREF_ROOT_NODE))
+ {
+ ISecurePreferences rootNode = securePreferences.node(PREF_ROOT_NODE);
+ ksNode = rootNode.node(keyStoreFile.getAbsolutePath());
+ try
+ {
+ String value = ksNode.get(prefKey, null);
+ isSaved = value != null; //password is saved if it is not the default value (because password length should be at least 6
+ }
+ catch (StorageException e)
+ {
+ StudioLogger.debug("It was not possible to get if the " + prefKey
+ + " is saved or not");
+ isSaved = false;
+ }
+ }
+ return isSaved;
+ }
+}
diff --git a/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/core/SaveStateManager.java b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/core/SaveStateManager.java
new file mode 100644
index 0000000..f482d4c
--- /dev/null
+++ b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/core/SaveStateManager.java
@@ -0,0 +1,328 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.studio.android.certmanager.core;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Properties;
+import java.util.Set;
+import java.util.StringTokenizer;
+
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorolamobility.studio.android.certmanager.CertificateManagerActivator;
+import com.motorolamobility.studio.android.certmanager.views.KeystoreManagerView;
+
+/**
+ * Saves the state of the {@link KeystoreManagerView} with:
+ * - the keystores mapped
+ * - the last backup date from a keystore
+ */
+class SaveStateManager
+{
+ private static final String FILE_COMMENT = "-- Signing and keys view persistence --";
+
+ private static final String STATE_FILENAME = "state.properties";
+
+ private final File persistenceFile = new File(System.getProperty("user.home") + File.separator
+ + "." + CertificateManagerActivator.PLUGIN_ID, STATE_FILENAME);
+
+ private static final String KEYSTORE_TYPE = "keystore_type=";
+
+ private static final String BACKUP_DATE = "backup_date=";
+
+ protected class ViewStateEntry
+ {
+ private final File keystoreFile;
+
+ private String keystoreType;
+
+ private Date backupDate;
+
+ public ViewStateEntry(File keystoreFile)
+ {
+
+ this.keystoreFile = keystoreFile;
+ }
+
+ public ViewStateEntry(File keystoreFile, String keystoreType)
+ {
+
+ this.keystoreFile = keystoreFile;
+ this.keystoreType = keystoreType;
+ }
+
+ /**
+ * @return the backupDate
+ */
+ public Date getBackupDate()
+ {
+ return backupDate;
+ }
+
+ /**
+ * @param backupDate the backupDate to set
+ */
+ public void setBackupDate(Date backupDate)
+ {
+ this.backupDate = backupDate;
+ }
+
+ /**
+ * @return the keystoreType
+ */
+ public String getKeystoreType()
+ {
+ return keystoreType;
+ }
+
+ /**
+ * @param keystoreType the keystoreType to set
+ */
+ public void setKeystoreType(String keystoreType)
+ {
+ this.keystoreType = keystoreType;
+ }
+
+ /* (non-Javadoc)
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString()
+ {
+ return "ViewStateEntry [keystoreFile=" + keystoreFile + ", keystoreType="
+ + keystoreType + ", backupDate=" + backupDate + "]";
+ }
+
+ /**
+ * @return the keystoreFile
+ */
+ protected File getKeystoreFile()
+ {
+ return keystoreFile;
+ }
+ }
+
+ private static SaveStateManager _instance;
+
+ /**
+ * This class is a singleton.
+ * @return The unique instance of this class.
+ * */
+ public synchronized static SaveStateManager getInstance() throws IOException
+ {
+ if (_instance == null)
+ {
+ _instance = new SaveStateManager();
+ }
+ return _instance;
+ }
+
+ private SaveStateManager() throws IOException
+ {
+ //create folder if it does not exist
+ if (!persistenceFile.getParentFile().exists())
+ {
+ persistenceFile.getParentFile().mkdirs();
+ }
+ if (!persistenceFile.exists())
+ {
+ //init file
+ persistenceFile.createNewFile();
+ store(new Properties());
+ }
+ }
+
+ private String write(ViewStateEntry entry)
+ {
+ StringBuffer buffer = new StringBuffer();
+ if (entry.getKeystoreType() != null)
+ {
+ buffer.append(KEYSTORE_TYPE + entry.getKeystoreType() + File.pathSeparator);
+ }
+ if (entry.getBackupDate() != null)
+ {
+ buffer.append(BACKUP_DATE + entry.getBackupDate().getTime());
+ }
+ return buffer.toString();
+ }
+
+ /**
+ * List all mapped keystores.
+ * @throws IOException If there are problems loading the persistence file.
+ */
+ public Set<File> getMappedKeystores() throws IOException
+ {
+ Set<File> mappedKeystores = new HashSet<File>();
+ Properties properties = load();
+ Enumeration<Object> enumeration = properties.keys();
+ while (enumeration.hasMoreElements())
+ {
+ Object k = enumeration.nextElement();
+ if (k instanceof String)
+ {
+ String key = (String) k;
+ File keystoreFile = new File(key);
+ mappedKeystores.add(keystoreFile);
+ }
+ }
+ return mappedKeystores;
+ }
+
+ /**
+ * Get a representation of the entry saved in the persistence.
+ * @param keystoreFile The file that will have its state retrieved.
+ * @return
+ * @throws IOException If there are problems loading the persistence file.
+ */
+ public ViewStateEntry getEntry(File keystoreFile) throws IOException
+ {
+ Properties properties = load();
+ Object v = properties.get(keystoreFile.getAbsolutePath());
+ ViewStateEntry entry = null;
+ if (v instanceof String)
+ {
+ String value = (String) v;
+ StringTokenizer stringTokenizer = new StringTokenizer(value, File.pathSeparator);
+ entry = new ViewStateEntry(keystoreFile);
+ while (stringTokenizer.hasMoreTokens())
+ {
+ String token = stringTokenizer.nextToken();
+ if (token.contains(KEYSTORE_TYPE))
+ {
+ token = token.substring(KEYSTORE_TYPE.length());
+ entry.setKeystoreType(token);
+ }
+ else if (token.contains(BACKUP_DATE))
+ {
+ token = token.substring(BACKUP_DATE.length());
+ Calendar calendar = Calendar.getInstance();
+ calendar.setTimeInMillis(Long.parseLong(token));
+ Date date = calendar.getTime();
+ entry.setBackupDate(date);
+ }
+ }
+ }
+ return entry;
+ }
+
+ /**
+ * Check if a keystore is mapped on the persistence mechanism.
+ * @param keystoreFile The keystore to be checked.
+ * @return true if keystore mapped, false otherwise
+ * @throws IOException If there are problems loading the persistence file.
+ */
+ public boolean isKeystoreMapped(File keystoreFile) throws IOException
+ {
+ Properties properties = load();
+ return properties.containsKey(keystoreFile.getAbsolutePath());
+ }
+
+ /**
+ * Adds (maps) a keystore.
+ * @param keystoreFile The keystore to be added to the persistence mechanism.
+ * @param keystoreType The type of the keystore.
+ * @throws IOException If there are problems loading the persistence file.
+ */
+ public void addEntry(File keystoreFile, String keystoreType) throws IOException
+ {
+ ViewStateEntry stateEntry = new ViewStateEntry(keystoreFile, keystoreType);
+ addEntry(keystoreFile, stateEntry);
+ }
+
+ private void addEntry(File keystoreFile, ViewStateEntry stateEntry) throws IOException
+ {
+ Properties prop = load();
+ prop.setProperty(keystoreFile.getAbsolutePath(), write(stateEntry));
+ store(prop);
+ }
+
+ /**
+ * Removes a keystore from the persistence mechanism.
+ * @param keystoreFile
+ * @throws IOException
+ */
+ public void removeEntry(File keystoreFile) throws IOException
+ {
+ Properties properties = load();
+ properties.remove(keystoreFile.getAbsolutePath());
+ store(properties);
+ }
+
+ /**
+ * Sets backup date from a keystore
+ * @param keystoreFile The keystore file.
+ * @param backupDate The date of the backup.
+ * @throws IOException If there are problems loading the persistence file.
+ */
+ public void setBackupDate(File keystoreFile, Date backupDate) throws IOException
+ {
+ ViewStateEntry entry = getEntry(keystoreFile);
+ entry.setBackupDate(backupDate);
+ addEntry(keystoreFile, entry);
+ }
+
+ private Properties load() throws IOException
+ {
+ FileInputStream in = null;
+ Properties props = new Properties();
+
+ try
+ {
+ in = new FileInputStream(persistenceFile);
+ props.loadFromXML(in);
+ }
+ finally
+ {
+ if (in != null)
+ {
+ in.close();
+ }
+ }
+ return props;
+ }
+
+ private void store(Properties prop) throws IOException
+ {
+ FileOutputStream out = null;
+
+ try
+ {
+ out = new FileOutputStream(persistenceFile);
+ prop.storeToXML(out, FILE_COMMENT);
+ }
+ finally
+ {
+ if (out != null)
+ {
+ try
+ {
+ out.close();
+ }
+ catch (IOException e)
+ {
+ StudioLogger.error("Could not close stream while saving properties. "
+ + e.getMessage());
+ }
+ }
+ }
+ }
+}
diff --git a/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/core/SaveStateManagerTest.java b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/core/SaveStateManagerTest.java
new file mode 100644
index 0000000..9376d47
--- /dev/null
+++ b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/core/SaveStateManagerTest.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.studio.android.certmanager.core;
+
+import java.io.File;
+import java.util.Calendar;
+import java.util.Date;
+
+import junit.framework.TestCase;
+
+import org.junit.Test;
+
+/**
+ * See {@link SaveStateManager}
+ */
+public class SaveStateManagerTest extends TestCase
+{
+ private static final String JKS = "JKS";
+
+ SaveStateManager manager;
+
+ File adtKeystoreFile = null;
+
+ File motodevKeystoreFile = null;
+
+ /* (non-Javadoc)
+ * @see junit.framework.TestCase#setUp()
+ */
+ @Override
+ protected void setUp() throws Exception
+ {
+ manager = SaveStateManager.getInstance();
+ adtKeystoreFile = new File("C:\\Users\\gdpr78\\motodevstudio\\tools\\adt.keystore");
+ motodevKeystoreFile = new File("C:\\Users\\gdpr78\\motodevstudio\\tools\\motodev.keystore");
+
+ super.setUp();
+ }
+
+ @Override
+ protected void tearDown() throws Exception
+ {
+ super.tearDown();
+ manager.removeEntry(adtKeystoreFile);
+ manager.removeEntry(motodevKeystoreFile);
+ }
+
+ @Test
+ public void testAddEntryWithoutBackupDate()
+ {
+ try
+ {
+ manager.addEntry(adtKeystoreFile, JKS);
+
+ SaveStateManager.ViewStateEntry entry = manager.getEntry(adtKeystoreFile);
+
+ assert ((entry != null) && (entry.getKeystoreFile() != null) && entry.getKeystoreFile()
+ .equals(adtKeystoreFile));
+ assert ((entry != null) && (entry.getKeystoreType() != null) && entry.getKeystoreType()
+ .equals(JKS));
+ }
+ catch (Exception e)
+ {
+ //error
+ assert (false);
+ }
+ }
+
+ @Test
+ public void testListKeystoresMapped()
+ {
+ try
+ {
+ manager.addEntry(adtKeystoreFile, JKS);
+ manager.addEntry(motodevKeystoreFile, "JKS");
+
+ assert ((manager.getMappedKeystores() != null)
+ && (manager.getMappedKeystores().size() == 2)
+ && manager.getMappedKeystores().contains(adtKeystoreFile) && manager
+ .getMappedKeystores().contains(motodevKeystoreFile));
+ }
+ catch (Exception e)
+ {
+ //error
+ assert (false);
+ }
+ }
+
+ @Test
+ public void testSetBackupDate()
+ {
+ try
+ {
+ Date date = Calendar.getInstance().getTime();
+ manager.addEntry(adtKeystoreFile, JKS);
+ manager.setBackupDate(adtKeystoreFile, date);
+
+ SaveStateManager.ViewStateEntry entry = manager.getEntry(adtKeystoreFile);
+
+ assert ((entry != null) && (entry.getBackupDate() != null) && entry.getBackupDate()
+ .equals(date));
+ }
+ catch (Exception e)
+ {
+ //error
+ assert (false);
+ }
+ }
+
+ @Test
+ public void testIsMappedKeystore()
+ {
+ try
+ {
+ manager.addEntry(adtKeystoreFile, JKS);
+ boolean result = manager.isKeystoreMapped(adtKeystoreFile);
+ assert (result == true);
+ }
+ catch (Exception e)
+ {
+ //error
+ assert (false);
+ }
+ }
+
+ @Test
+ public void testRemoveEntry()
+ {
+ try
+ {
+ manager.addEntry(motodevKeystoreFile, "JKS");
+ assert (manager.isKeystoreMapped(motodevKeystoreFile) == true);
+
+ manager.removeEntry(motodevKeystoreFile);
+ assert (manager.isKeystoreMapped(motodevKeystoreFile) == false);
+ }
+ catch (Exception e)
+ {
+ //error
+ assert (false);
+ }
+ }
+
+}
diff --git a/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/event/IKeyStoreModelListener.java b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/event/IKeyStoreModelListener.java
new file mode 100644
index 0000000..19e24a4
--- /dev/null
+++ b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/event/IKeyStoreModelListener.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.motorolamobility.studio.android.certmanager.event;
+
+import com.motorolamobility.studio.android.certmanager.ui.model.ITreeNode;
+
+/**
+ * This interface must be implemented by listeners to events occurred on the {@link ITreeNode}.
+ */
+public interface IKeyStoreModelListener
+{
+ /**
+ * Handles the event {@link KeyStoreModelEvent#EventType} to add a node.
+ * @param keyStoreModelEvent {@link KeyStoreModelEvent#getTreeNodeItem()} contains the node to be added.
+ */
+ public void handleNodeAdditionEvent(KeyStoreModelEvent keyStoreModeEvent);
+
+ /**
+ * Handles the event {@link KeyStoreModelEvent#EventType} to remove a node.
+ * @param keyStoreModelEvent {@link KeyStoreModelEvent#getTreeNodeItem()} contains the node to be added.
+ */
+ public void handleNodeRemovalEvent(KeyStoreModelEvent keyStoreModeEvent);
+
+ /**
+ * Handles the event {@link KeyStoreModelEvent#EventType} to update a node.
+ * @param keyStoreModelEvent {@link KeyStoreModelEvent#getTreeNodeItem()} contains the node to be added.
+ */
+ public void handleNodeUpdateEvent(KeyStoreModelEvent keyStoreModeEvent);
+
+ /**
+ * Handles the event {@link KeyStoreModelEvent#EventType} to collapse a node.
+ * @param keyStoreModelEvent {@link KeyStoreModelEvent#getTreeNodeItem()} contains the node to be collapsed.
+ */
+ public void handleNodeCollapseEvent(KeyStoreModelEvent keyStoreModelEvent);
+
+ /**
+ * Handles the event {@link KeyStoreModelEvent#EventType} to refresh a node.
+ * @param keyStoreModelEvent {@link KeyStoreModelEvent#getTreeNodeItem()} contains the node to be refreshed.
+ */
+ public void handleNodeRefreshEvent(KeyStoreModelEvent keyStoreModelEvent);
+
+ /**
+ * Handles the event {@link KeyStoreModelEvent#EventType} to clear a node.
+ * @param keyStoreModelEvent {@link KeyStoreModelEvent#getTreeNodeItem()} contains the node to be cleared.
+ */
+ public void handleNodeClearEvent(KeyStoreModelEvent keyStoreModelEvent);
+
+}
diff --git a/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/event/KeyStoreModelEvent.java b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/event/KeyStoreModelEvent.java
new file mode 100644
index 0000000..9ff4dd0
--- /dev/null
+++ b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/event/KeyStoreModelEvent.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.motorolamobility.studio.android.certmanager.event;
+
+import com.motorolamobility.studio.android.certmanager.ui.model.ITreeNode;
+
+/**
+ * This class represents an event occurred on the {@link ITreeNode}.
+ */
+public class KeyStoreModelEvent
+{
+ private final EventType eventType;
+
+ private final ITreeNode treeNodeItem;
+
+ /**
+ * Represents the change in the model. The {@link KeyStoreModelEvent#treeNodeItem} in each event may vary as specified below:
+ * <ul>
+ * <li>{@link EVENT_TYPE#ADD} it is the item that needs to be added</li>
+ * <li>{@link EVENT_TYPE#REMOVE} it is the item that needs to be deleted</li>
+ * <li>{@link EVENT_TYPE#UPDATE} it is the item that needs to be updated</li>
+ * </ul>
+ */
+ public enum EventType
+ {
+ ADD, REMOVE, UPDATE, COLLAPSE, REFRESH, CLEAR
+ }
+
+ /**
+ * Returns the event type.
+ * */
+ public EventType getEventType()
+ {
+ return eventType;
+ }
+
+ /**
+ * Returns the tree node item related to the event.
+ * */
+ public ITreeNode getTreeNodeItem()
+ {
+ return treeNodeItem;
+ }
+
+ /**
+ * Constructs a new event given an {@link ITreeNode} and a {@link KeyStoreModelEvent#EventType}.
+ * */
+ public KeyStoreModelEvent(ITreeNode treeNodeItem, EventType eventType)
+ {
+ this.eventType = eventType;
+ this.treeNodeItem = treeNodeItem;
+ }
+}
diff --git a/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/event/KeyStoreModelEventManager.java b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/event/KeyStoreModelEventManager.java
new file mode 100644
index 0000000..5e2b49b
--- /dev/null
+++ b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/event/KeyStoreModelEventManager.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.motorolamobility.studio.android.certmanager.event;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.motorolamobility.studio.android.certmanager.ui.model.ITreeNode;
+
+/**
+ * Manager which notifies {@link IKeyStoreModelListener} registered that a {@link KeyStoreModelEvent} occurred.
+ * It is a singleton that needs to be called by the hierarchy of {@link ITreeNode} items when they modify the {@link ITreeNode}.
+ */
+public class KeyStoreModelEventManager
+{
+ private static KeyStoreModelEventManager _instance;
+
+ private final List<IKeyStoreModelListener> listeners = new ArrayList<IKeyStoreModelListener>();
+
+ private KeyStoreModelEventManager()
+ {
+ // Singleton - private default constructor prevents instantiations by other classes.
+ }
+
+ /**
+ * Return the singleton instance of KeyStoreModelEventManager.
+ * */
+ public synchronized static KeyStoreModelEventManager getInstance()
+ {
+ if (_instance == null)
+ {
+ _instance = new KeyStoreModelEventManager();
+ }
+ return _instance;
+ }
+
+ /**
+ * Add the parameter {@code listener} to the list of KeyStore event listeners.
+ * @param listener The listener to be added.
+ * */
+ public void addListener(IKeyStoreModelListener listener)
+ {
+ synchronized (listener)
+ {
+ listeners.add(listener);
+ }
+ }
+
+ /**
+ * Remove the parameter {@code listener} to the list of KeyStore event listeners.
+ * @param listener The listener to be removed.
+ * */
+ public void removeListener(IKeyStoreModelListener listener)
+ {
+ synchronized (listener)
+ {
+ listeners.remove(listener);
+ }
+ }
+
+ /**
+ * Fire/notify/deliver the event to registered listeners.
+ * @param node {@link ITreeNode} that needs to refresh the view based on the model
+ * @param eventType Event that occurred.
+ */
+ public void fireEvent(ITreeNode treeNodeItem, KeyStoreModelEvent.EventType eventType)
+ {
+ KeyStoreModelEvent keyStoreModelEvent = new KeyStoreModelEvent(treeNodeItem, eventType);
+ synchronized (listeners)
+ {
+ if (listeners != null)
+ {
+ for (IKeyStoreModelListener listener : listeners)
+ {
+ switch (eventType)
+ {
+ case ADD:
+ listener.handleNodeAdditionEvent(keyStoreModelEvent);
+ break;
+ case REMOVE:
+ listener.handleNodeRemovalEvent(keyStoreModelEvent);
+ break;
+ case UPDATE:
+ listener.handleNodeUpdateEvent(keyStoreModelEvent);
+ break;
+ case COLLAPSE:
+ listener.handleNodeCollapseEvent(keyStoreModelEvent);
+ break;
+ case REFRESH:
+ listener.handleNodeRefreshEvent(keyStoreModelEvent);
+ break;
+ case CLEAR:
+ listener.handleNodeClearEvent(keyStoreModelEvent);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/exception/InvalidPasswordException.java b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/exception/InvalidPasswordException.java
new file mode 100644
index 0000000..66e69bc
--- /dev/null
+++ b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/exception/InvalidPasswordException.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.studio.android.certmanager.exception;
+
+import com.motorola.studio.android.common.exception.AndroidException;
+
+/**
+ * Exception thrown when the password to access a keystore is invalid or wrong.
+ * */
+@SuppressWarnings("serial")
+public class InvalidPasswordException extends AndroidException
+{
+
+ /*
+ * Create a new empty exception.
+ * */
+ @SuppressWarnings("unused")
+ private InvalidPasswordException()
+ {
+ //prevent methods to throw this exception without further information
+ }
+
+ /**
+ * Create a new exception with a message indicating the problem.
+ * */
+ public InvalidPasswordException(String message)
+ {
+ super(message);
+ }
+
+ /**
+ * Create a new exception with a message indicating the problem,
+ * and append some other exception that is being replaced by this one.
+ * */
+ public InvalidPasswordException(String message, Throwable cause)
+ {
+ super(message, cause);
+ }
+
+}
diff --git a/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/exception/KeyStoreManagerException.java b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/exception/KeyStoreManagerException.java
new file mode 100644
index 0000000..4252691
--- /dev/null
+++ b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/exception/KeyStoreManagerException.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.studio.android.certmanager.exception;
+
+import com.motorola.studio.android.common.exception.AndroidException;
+import com.motorolamobility.studio.android.certmanager.core.KeyStoreManager;
+
+/**
+ * Exception thrown when trying to access the {@link KeyStoreManager}.
+ * */
+@SuppressWarnings("serial")
+public class KeyStoreManagerException extends AndroidException
+{
+ /*
+ * Create a new empty exception.
+ * */
+ @SuppressWarnings("unused")
+ private KeyStoreManagerException()
+ {
+ //prevent methods to throw this exception without further information
+ }
+
+ /**
+ * Create a new exception with a message indicating the problem.
+ * */
+ public KeyStoreManagerException(String message)
+ {
+ super(message);
+ }
+
+ /**
+ * Create a new exception with a message indicating the problem,
+ * and append some other exception that is being replaced by this one.
+ * */
+ public KeyStoreManagerException(String message, Throwable cause)
+ {
+ super(message, cause);
+ }
+
+}
diff --git a/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/i18n/CertificateManagerNLS.java b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/i18n/CertificateManagerNLS.java
new file mode 100644
index 0000000..26fc44f
--- /dev/null
+++ b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/i18n/CertificateManagerNLS.java
@@ -0,0 +1,522 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.studio.android.certmanager.i18n;
+
+import org.eclipse.osgi.util.NLS;
+
+/**
+ * This class is the NLS component for Certificate Manager plug-in
+ */
+public class CertificateManagerNLS extends NLS
+{
+
+ public static String AbstractTreeNode_SavedPassword_Tooltip;
+
+ public static String AbstractTreeNode_UnsavedPassword_Tooltip;
+
+ public static String BackupDialog_Archive_Exists_Message;
+
+ public static String BackupDialog_Archive_Exists_Title;
+
+ public static String BackupDialog_Backup_File;
+
+ public static String BackupDialog_Browse;
+
+ public static String BackupDialog_Default_Message;
+
+ public static String BackupDialog_Diag_Title;
+
+ public static String BackupDialog_DialogTitle;
+
+ public static String BackupDialog_Fail_Writing_Archive_Message;
+
+ public static String BackupDialog_Fail_Writing_Archive_Title;
+
+ public static String BackupDialog_Invalid_Destination_Message;
+
+ public static String BackupDialog_Invalid_Destination_Title;
+
+ public static String BackupDialog_KeyStores;
+
+ public static String BackupDialog_Non_Absolute_Path;
+
+ public static String BackupDialog_Path;
+
+ public static String BackupDialog_Select_All;
+
+ public static String BackupDialog_Select_KeyStore;
+
+ public static String BackupHandler_Error_BackUp_Title;
+
+ public static String BackupHandler_Error_Setting_Date;
+
+ public static String BackupHandler_Error_Writing_Archive;
+
+ public static String SelectExistentKeystorePage_CheckboxText_AlsoImportIntoSigningView;
+
+ public static String SelectExistentKeystorePage_KeystorePasswordLabel;
+
+ public static String SelectExistentKeystorePage_WizardPageMessage;
+
+ public static String SelectExistentKeystorePage_WizardPageTitle;
+
+ public static String SelectExistentKeystoreWizard_BrowseExistentKeystore_PageTitle;
+
+ public static String SelectExistentKeystoreWizard_Error_InvalidPassword;
+
+ public static String SelectExistentKeystoreWizard_Error_KeystoreType;
+
+ public static String SIGN_EXTERNAL_PKG_WIZARD_LOAD;
+
+ public static String SIGN_EXTERNAL_PKG_WIZARD_WINDOW_TITLE;
+
+ public static String SIGN_EXTERNAL_PKG_WIZARD_NO_CERTIFICATE_ERROR;
+
+ public static String SIGN_EXTERNAL_PKG_WIZARD_OPERATION;
+
+ public static String SIGN_EXTERNAL_PKG_WIZARD_ERROR;
+
+ public static String UNSIGN_EXTERNAL_PKG_WIZARD_DESCRIPTION;
+
+ public static String UNSIGN_EXTERNAL_PKG_WIZARD_WINDOW_TITLE;
+
+ public static String UNSIGN_EXTERNAL_PKG_WIZARD_ERROR;
+
+ public static String UNSIGN_EXTERNAL_PKG_WIZARD_ERROR_REASON;
+
+ public static String UNSIGN_EXTERNAL_PKG_WIZARD_OPERATION;
+
+ public static String UNSIGN_EXTERNAL_PKG_WIZARD_NO_PACKAGES_SELECTED;
+
+ public static String SIGN_EXTERNAL_PKG_WIZARD_SOURCE_DIR_LABEL;
+
+ public static String SIGN_EXTERNAL_PKG_WIZARD_SOURCE_DIR_INVALID;
+
+ public static String SIGN_EXTERNAL_PKG_WIZARD_SOURCE_DIR_NOT_DIRECTORY;
+
+ public static String SIGN_EXTERNAL_PKG_WIZARD_DESCRIPTION;
+
+ public static String SIGN_EXTERNAL_PKG_WIZARD_NO_AVAILABLE_PACKAGES;
+
+ public static String SIGN_EXTERNAL_PKG_WIZARD_SOURCE_DIR_EMPTY;
+
+ public static String PACKAGE_EXPORT_WIZARD_AREA_SELECT_ALL_BUTTON;
+
+ public static String PACKAGE_EXPORT_WIZARD_AREA_DESELECT_ALL_BUTTON;
+
+ public static String PasswordProvider_DialogTitle;
+
+ public static String PasswordProvider_Error_WhileRemovingPassword;
+
+ public static String PasswordProvider_Error_WhileSaving;
+
+ public static String PasswordProvider_MessageLabel;
+
+ public static String PasswordProvider_PasswordLabel;
+
+ public static String PasswordProvider_SaveThisPassword;
+
+ public static String PasswordProvider_Key_MessageLabel;
+
+ public static String RemoveExternalPackageSignaturePage_Package_Tree_Label;
+
+ public static String RestoreBackupDialog_BackUp_File;
+
+ public static String RestoreBackupDialog_BackUpFile_Not_Exist;
+
+ public static String RestoreBackupDialog_Browse_Button;
+
+ public static String RestoreBackupDialog_Default_Message;
+
+ public static String RestoreBackupDialog_Destination;
+
+ public static String RestoreBackupDialog_Dialog_Title;
+
+ public static String RestoreBackupDialog_Error_Loading_Entries;
+
+ public static String RestoreBackupDialog_Invalid_Dest_Path;
+
+ public static String RestoreBackupDialog_KeyStores;
+
+ public static String RestoreBackupDialog_Path_Group;
+
+ public static String RestoreBackupDialog_Select_All;
+
+ public static String RestoreBackupDialog_Select_KeyStore;
+
+ public static String RestoreBackupDialog_TitleArea_Message;
+
+ public static String RestoreBackupHandler_Error_Mapping_Message;
+
+ public static String RestoreBackupHandler_Error_Mapping_Status;
+
+ public static String RestoreBackupHandler_Error_Mapping_Title;
+
+ public static String RestoreBackupHandler_Error_Restoring_Backup_Message;
+
+ public static String RestoreBackupHandler_Error_Restoring_Backup_Status;
+
+ public static String RestoreBackupHandler_Error_Restoring_Backup_Title;
+
+ public static String RestoreBackupHandler_RestoreIssue_MissingMetadataFile_WarningDescription;
+
+ public static String RestoreBackupHandler_RestoreIssue_WarningTitle;
+
+ public static String READ_ONLY_TEXT;
+
+ public static String SELECTOR_MESSAGE_LOCATION_ERROR_PATH_TOO_LONG;
+
+ public static String SELECTOR_MESSAGE_LOCATION_ERROR_INVALID_DEVICE;
+
+ public static String DeleteKeystoreHandler_ConfirmationQuestionDialog_Description;
+
+ public static String DeleteKeystoreHandler_ConfirmationQuestionDialog_Title;
+
+ public static String DeleteKeystoreHandler_ConfirmationQuestionDialog_Toggle;
+
+ public static String DeleteKeystoreHandler_Delete_Selected_Keystores;
+
+ public static String ImportKeyStoreDialog_Alias_Column;
+
+ public static String ImportKeyStoreDialog_Default_Message;
+
+ public static String ImportKeyStoreDialog_Dialog_Title;
+
+ public static String ImportKeyStoreDialog_Error_Loading_Entries;
+
+ public static String ImportKeyStoreDialog_Error_Loading_Keystores;
+
+ public static String ImportKeyStoreDialog_Invalid_Keystore_Passwd;
+
+ public static String ImportKeyStoreDialog_KeyStore_Label;
+
+ public static String ImportKeyStoreDialog_Load_Button;
+
+ public static String ImportKeyStoreDialog_No_Entries_To_Import;
+
+ public static String ImportKeyStoreDialog_Passwd_Column;
+
+ public static String ImportKeyStoreDialog_Password_Label;
+
+ public static String ImportKeyStoreDialog_Select_Source_Ks;
+
+ public static String ImportKeyStoreDialog_Select_Target_Kesytore;
+
+ public static String ImportKeyStoreDialog_Source_Group;
+
+ public static String ImportKeyStoreDialog_Target_Group;
+
+ public static String ImportKeyStoreDialog_Type_SourceKs_Passwd;
+
+ public static String ImportKeyStoreDialog_Verified_Column;
+
+ public static String ImportKeyStoreDialog_Verified_Pass_Wrong;
+
+ public static String ImportKeyStoreDialog_Verified_Pass_Yes;
+
+ public static String ImportKeyStoreDialog_Wrong_Entries_Passwd;
+
+ public static String ImportKeystorePage_CouldNotImportKeystore;
+
+ public static String ImportKeystorePage_Description;
+
+ public static String ImportKeystorePage_DirectoryNotAllowedErrorMsg;
+
+ public static String ImportKeystorePage_FileDoesNotExist;
+
+ public static String ImportKeystorePage_FilenameCannotBeEmpty;
+
+ public static String ImportKeystorePage_KeystoreAlreadyMapped;
+
+ public static String ImportKeystorePage_KeystoreTypeCannotBeEmpty;
+
+ public static String ImportKeystorePage_Title;
+
+ public static String ImportKeystoreWizard_ImportKeystore;
+
+ public static String ImportKeystorePage_FileEmpty;
+
+ public static String DeleteKeyHandler_ConfirmationQuestionDialog_Description;
+
+ public static String DeleteKeyHandler_ConfirmationQuestionDialog_Title;
+
+ public static String DeleteKeyHandler_Delete_Selected_Keys;
+
+ /**
+ * The bundle location.
+ * It refers to messages.properties file inside this package
+ */
+ static
+ {
+ NLS.initializeMessages(
+ "com.motorolamobility.studio.android.certmanager.i18n.certificateManagerNLS",
+ CertificateManagerNLS.class);
+ }
+
+ public static String CertificateManagerView_ExpiresIn_ColumnName;
+
+ public static String CertificateManagerView_LastBackupDate_ColumnName;
+
+ public static String CertificateManagerView_NameAlias_ColumnName;
+
+ public static String CertificateManagerView_Type_ColumnName;
+
+ public static String CertificateManagerView_Path_ColumnName;
+
+ public static String CreateKeystorePage_ConfirmFileOverwrite;
+
+ public static String CreateKeystorePage_ConfirmPasswordInfoMsg;
+
+ public static String CreateKeystorePage_ConfirmReplaceFile;
+
+ public static String CreateKeystorePage_CouldNotSavePassword;
+
+ public static String CreateKeystorePage_CreateKeystore;
+
+ public static String CreateKeystorePage_DefaultKeystoreFilename;
+
+ public static String CreateKeystorePage_DefaultKeystoreFilenameExtension;
+
+ public static String CreateKeystorePage_ErrorCreatingKeystore;
+
+ public static String CreateKeystorePage_ErrorOnKeyStoreFileCreation;
+
+ public static String CreateKeystorePage_FilenameSyntaxError;
+
+ public static String CreateKeystorePage_KeystoreConfirmPasswordLabel;
+
+ public static String CreateKeystorePage_KeystoreFilenameBrowse;
+
+ public static String CreateKeystorePage_KeystoreFilenameLabel;
+
+ public static String CreateKeystorePage_KeystorePasswordLabel;
+
+ public static String CreateKeystorePage_KeystoreType;
+
+ public static String CreateKeystorePage_PasswordDoesNotMatch;
+
+ public static String CreateKeystorePage_PasswordMinSizeMessage;
+
+ public static String CreateKeystorePage_SaveThisPassword;
+
+ public static String CreateKeystorePage_SetKeystoreType;
+
+ public static String CreateKeystorePage_SetPasswordInfoMsg;
+
+ public static String CreateKeystorePage_UseKeystoreTypeAsExtension;
+
+ public static String CreateKeystorePage_WizardDefaultMessage;
+
+ public static String CreateKeystoreWizard_CreateNewKeyStore;
+
+ /*
+ * CertificateBlock
+ */
+ public static String CertificateBlock_AliasName;
+
+ public static String CertificateBlock_BasicInfoGroupTitle;
+
+ public static String CertificateBlock_FirstAndLastName;
+
+ public static String CertificateBlock_Organization;
+
+ public static String CertificateBlock_OrganizationUnit;
+
+ public static String CertificateBlock_CityOrLocality;
+
+ public static String CertificateBlock_StateOrProvince;
+
+ public static String CertificateBlock_ConfirmKeyPassword_Label;
+
+ public static String CertificateBlock_EnterPassword_InfoMessage;
+
+ public static String CertificateBlock_CountryCode;
+
+ public static String CertificateBlock_FieldIsEmpty;
+
+ public static String CertificateBlock_Validity;
+
+ public static String CertificateBlock_Validity_Error;
+
+ public static String CertificateBlock_ExpirationDate;
+
+ public static String CertificateBlock_KeyPassword_Label;
+
+ public static String CertificateBlock_KeyTooltip;
+
+ public static String CertificateInfoDialog_NotAvailableProperty;
+
+ public static String CertificateInfoDialog_ShellTitle;
+
+ public static String CertificateInfoDialog_UnknownCertificateKeypairType;
+
+ /*
+ * Certificate properties handler
+ */
+ public static String CertificatePropertiesHandler_ErrorGettingCertificateOrKeypairProperties;
+
+ public static String CreateKeyWizard_ErrorCreatingKey_DialogTitle;
+
+ /*
+ * Create self signed wizard page
+ */
+ public static String CreateSelfSignedCertificateWizardPage_Description;
+
+ public static String CreateSelfSignedCertificateWizardPage_Title;
+
+ public static String KeystoreManagerView_ErrorImportingBackwardKeystore;
+
+ public static String KeystoreManagerView_ErrorLoadingMappedKeystoresFromPersistence;
+
+ public static String KeyStoreModel_Error_GettingAliasesFromKeystore;
+
+ public static String KeyStoreNode_CouldNotGetKeyStorePassword;
+
+ public static String KeyStoreNode_CouldNotLoadKeystore_Tooltip;
+
+ public static String KeyStoreNode_ErrorKeystoreNotFound;
+
+ public static String KeyStoreNode_IncorrectPasswordToDeleteEntries_Error;
+
+ public static String KeyStoreNode_InvalidPassword;
+
+ public static String KeyStoreNode_KeyPairNotMapped_Message;
+
+ public static String KeyStoreNode_KeyPairNotMapped_Title;
+
+ public static String KeyStoreNode_KeyPairNotMapped_LogMessage;
+
+ public static String KeyStoreNode_KeystoreFileNotFound;
+
+ public static String KeyStoreNode_KeystoreTypeWrong_NodeStatus;
+
+ public static String KeyStoreNode_NotFoundOrIncorrectPasswordToDeleteEntry;
+
+ public static String KeyStoreNode_Password_NotNull;
+
+ public static String KeyStoreNode_UseRefresh_StatusNode;
+
+ public static String KeyStoreNode_Wrong_KeystoreType_Message;
+
+ public static String KeyStoreNode_Wrong_KeystoreType_Title;
+
+ public static String KeyStoreRootNode_Error_AlreadyMappedKeystorePath;
+
+ public static String KeyStoreUtils_RSA_Keys_Expected;
+
+ public static String KeyStoreUtils_Error_AddEntryToKeyStore;
+
+ public static String KeyStoreUtils_Error_WriteKeyStore;
+
+ public static String SIGN_WIZARD_AREA_SIGN_KEYSTORE_LABEL;
+
+ public static String SIGN_WIZARD_AREA_SIGN_KEYS_LABEL;
+
+ public static String KeyStoreUtils_Error_DeleteKeyStore;
+
+ public static String KeyStoreUtils_Error_FileAlreadyExists;
+
+ public static String KeyStoreUtils_Error_LoadKeyStore;
+
+ public static String KeyStoreUtils_ErrorDeletingAlias;
+
+ public static String CertificatePeriodExpired_Issue;
+
+ public static String CertificatePeriodNotYeatValid_Issue;
+
+ public static String ChangePasswordKeyHandler_Error_WrongOldKeyPassword;
+
+ public static String ChangePasswordKeyHandler_Wrong_Key_Password;
+
+ public static String ChangePasswordKeystoreHandler_Error_ChangingKeystorePassword;
+
+ public static String ChangePasswordKeystoreHandler_Error_WrongOldKeystorePassword;
+
+ public static String ChangePasswordKeystoreHandler_InvalidOldPassword;
+
+ public static String EntryNode_ErrorGettingCertificateFromEntry;
+
+ public static String EntryNode_NotFoundOrTypeWrong;
+
+ public static String Passwordinput_EnterOldKeystorePasssword_Message;
+
+ public static String PasswordChanged_Info_Title;
+
+ public static String PasswordChanged_Info_Message;
+
+ public static String PasswordInput_EnterOldKeyPasssword_Message;
+
+ public static String PasswordChanged_KeyInfo_Message;
+
+ public static String PasswordChanged_InvalidKeystorePassword;
+
+ public static String CertificateBlock_CreationDate;
+
+ public static String CertificateBlock_DetailedInfoGroupTitle;
+
+ public static String CertificateBlock_DetailedInfoNonEmptyFieldsRestriction;
+
+ public static String ConvertKeyStoreTypeDialog_Alias_Column;
+
+ public static String ConvertKeyStoreTypeDialog_Choose_KeyStore_Msg;
+
+ public static String ConvertKeyStoreTypeDialog_Choose_New_Type_Msg;
+
+ public static String ConvertKeyStoreTypeDialog_CouldNotLoad_Keystores_Error;
+
+ public static String ConvertKeyStoreTypeDialog_DefaultMessage;
+
+ public static String ConvertKeyStoreTypeDialog_DialogTitle;
+
+ public static String ConvertKeyStoreTypeDialog_Entries_Group;
+
+ public static String ConvertKeyStoreTypeDialog_Error_Loading_Keystore;
+
+ public static String ConvertKeyStoreTypeDialog_Incorrect_Entry_Pass;
+
+ public static String ConvertKeyStoreTypeDialog_Invalid_Keystore_Pass;
+
+ public static String ConvertKeyStoreTypeDialog_KeyStoreLabel;
+
+ public static String ConvertKeyStoreTypeDialog_Load_Button;
+
+ public static String ConvertKeyStoreTypeDialog_NewType_Label;
+
+ public static String ConvertKeyStoreTypeDialog_Original_Type_Label;
+
+ public static String ConvertKeyStoreTypeDialog_Password_Column;
+
+ public static String ConvertKeyStoreTypeDialog_Password_Label;
+
+ public static String ConvertKeyStoreTypeDialog_Verified_Column;
+
+ public static String ConvertKeyStoreTypeDialog_Verified_Pass_Wrong;
+
+ public static String ConvertKeyStoreTypeDialog_Verified_Pass_Yes;
+
+ public static String NewKeyBlock_PasswordGroupTitle;
+
+ public static String SIGN_EXTERNAL_PKG_WIZARD_FILESYSTEM;
+
+ public static String SIGN_EXTERNAL_PKG_WIZARD_WORKSPACE;
+
+ public static String SIGN_EXTERNAL_PKG_WIZARD_CHOOSE;
+
+ public static String SIGN_EXTERNAL_PKG_WIZARD_WORKSPACE_SIMPLE;
+
+ public static String SignExternalPackagePage_Package_Tree_Label;
+
+}
diff --git a/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/i18n/certificateManagerNLS.properties b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/i18n/certificateManagerNLS.properties
new file mode 100644
index 0000000..4481dee
--- /dev/null
+++ b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/i18n/certificateManagerNLS.properties
@@ -0,0 +1,285 @@
+#
+# Copyright (C) 2012 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+AbstractTreeNode_SavedPassword_Tooltip=[password saved]
+AbstractTreeNode_UnsavedPassword_Tooltip=[password not saved]
+BackupDialog_Archive_Exists_Message=The chosen destination file, {0}, already exists.\nDo you want to overwrite it?
+BackupDialog_Archive_Exists_Title=File already exists.
+BackupDialog_Backup_File=Backup File
+BackupDialog_Browse=Browse...
+BackupDialog_Default_Message=Specify an archive (zip) file and the keystores that should be backed up to that archive
+BackupDialog_Diag_Title=Back Up Keystores
+BackupDialog_DialogTitle=Back up keystores
+BackupDialog_Fail_Writing_Archive_Message=Can't write to file {0}.\nVerify that the path is valid, you have the right permissions and that the file has not been opened by another program.
+BackupDialog_Fail_Writing_Archive_Title=Unable to Write Archive
+BackupDialog_Invalid_Destination_Message=The archive file is invalid. It should be a zip file.
+BackupDialog_Invalid_Destination_Title=Invalid Archive File
+BackupDialog_KeyStores=Keystores
+BackupDialog_Non_Absolute_Path=Selected path is not absolute. Backup archive will be created at:\n{0}
+BackupDialog_Path=Path:
+BackupDialog_Select_All=Select all
+BackupDialog_Select_KeyStore=Select at least one keystore.
+BackupHandler_Error_BackUp_Title=Error while creating keystore backup
+BackupHandler_Error_Setting_Date=Could not set backup dates
+BackupHandler_Error_Writing_Archive=Unable to write archive file {0}
+
+ImportKeyStoreDialog_Alias_Column=Alias
+ImportKeyStoreDialog_Default_Message=Import selected keys (entries) from one keystore to another.
+ImportKeyStoreDialog_Dialog_Title=Import Entries
+ImportKeyStoreDialog_Error_Loading_Entries=An error has occurred while attempting to load source keystore entries.
+ImportKeyStoreDialog_Error_Loading_Keystores=Could not load mapped keystores
+ImportKeyStoreDialog_Invalid_Keystore_Passwd=Invalid source keystore password.
+ImportKeyStoreDialog_KeyStore_Label=Keystore:
+ImportKeyStoreDialog_Load_Button=Load
+ImportKeyStoreDialog_No_Entries_To_Import=Select at least one entry to import
+ImportKeyStoreDialog_Passwd_Column=Password
+ImportKeyStoreDialog_Password_Label=Password:
+ImportKeyStoreDialog_Select_Source_Ks=Select a source keystore
+ImportKeyStoreDialog_Select_Target_Kesytore=Select a target keystore
+ImportKeyStoreDialog_Source_Group=Source Keystore
+ImportKeyStoreDialog_Target_Group=Target Keystore
+ImportKeyStoreDialog_Type_SourceKs_Passwd=Enter the source keystore password.
+ImportKeyStoreDialog_Verified_Column=Verified
+ImportKeyStoreDialog_Verified_Pass_Wrong=Not verified
+ImportKeyStoreDialog_Verified_Pass_Yes=Yes
+ImportKeyStoreDialog_Wrong_Entries_Passwd=Supply the correct passwords for the entries being imported.
+ImportKeystorePage_CouldNotImportKeystore=Could not import keystore
+ImportKeystorePage_Description=Import an existing keystore into Signing and Keys view.
+ImportKeystorePage_DirectoryNotAllowedErrorMsg={0} is a directory
+ImportKeystorePage_FileDoesNotExist=The keystore file {0} does not exist.
+ImportKeystorePage_FileEmpty=The keystore file {0} is empty.
+ImportKeystorePage_FilenameCannotBeEmpty=Filename cannot be empty.
+ImportKeystorePage_KeystoreAlreadyMapped=The keystore file {0} is already present in the Signing and Keys view.
+ImportKeystorePage_KeystoreTypeCannotBeEmpty=Keystore type cannot be empty.
+ImportKeystorePage_Title=Import Keystore
+ImportKeystoreWizard_ImportKeystore=Import an existing keystore into the Signing and Keys view
+DeleteKeystoreHandler_ConfirmationQuestionDialog_Description=Delete keystore {0}?\nNOTE: consider making a backup before deleting this keystore.
+DeleteKeystoreHandler_ConfirmationQuestionDialog_Title=Delete Keystore
+DeleteKeystoreHandler_ConfirmationQuestionDialog_Toggle=Delete content on disk? (Cannot be undone).
+DeleteKeystoreHandler_Delete_Selected_Keystores=Delete selected keystores?\nNOTE: consider making a backup before deleting keystores.
+
+CertificateManagerView_ExpiresIn_ColumnName=Expires on
+CertificateManagerView_LastBackupDate_ColumnName=Last backup date
+CertificateManagerView_NameAlias_ColumnName=Name (Alias)
+CertificateManagerView_Type_ColumnName=Type
+CertificateManagerView_Path_ColumnName=Path
+
+DeleteKeyHandler_ConfirmationQuestionDialog_Description=Delete key {0}? (Cannot be undone).\nNOTE: consider making a backup before deleting this key.
+DeleteKeyHandler_ConfirmationQuestionDialog_Title=Delete Key
+DeleteKeyHandler_Delete_Selected_Keys=Delete selected keys? (Cannot be undone).\nNOTE: consider making a backup before deleting this key.
+
+#------------------------------------------------------------------------------
+# Create keystore Wizard
+#------------------------------------------------------------------------------
+CreateKeystorePage_ConfirmFileOverwrite=Confirm File Overwrite
+CreateKeystorePage_ConfirmPasswordInfoMsg=Confirm the password.
+CreateKeystorePage_ConfirmReplaceFile={0} already exists.\nDo you want to replace it?
+CreateKeystorePage_CouldNotSavePassword=Could not save password
+CreateKeystorePage_CreateKeystore=Create Keystore
+CreateKeystorePage_DefaultKeystoreFilename=keystore_{0}
+CreateKeystorePage_DefaultKeystoreFilenameExtension=keystore
+CreateKeystorePage_ErrorCreatingKeystore=Error creating keystore
+CreateKeystorePage_ErrorOnKeyStoreFileCreation=Error writing file \"{0}\" to disk. Check filename syntax and file system permissions.
+CreateKeystorePage_FilenameSyntaxError=The filename, directory name, or volume label syntax is incorrect.
+CreateKeystorePage_KeystoreConfirmPasswordLabel=Confirm Password:
+CreateKeystorePage_KeystoreFilenameBrowse=Browse...
+CreateKeystorePage_KeystoreFilenameLabel=Keystore Filename:
+CreateKeystorePage_KeystorePasswordLabel=Keystore Password:
+CreateKeystorePage_KeystoreType=Keystore Type:
+CreateKeystorePage_PasswordDoesNotMatch=Password does not match.
+CreateKeystorePage_PasswordMinSizeMessage=Password must have at least {0} characters.
+CreateKeystorePage_SaveThisPassword=Save this password
+CreateKeystorePage_SetKeystoreType=Set a keystore type.
+CreateKeystorePage_SetPasswordInfoMsg=Set a keystore password.
+CreateKeystorePage_UseKeystoreTypeAsExtension=Use keystore type as keystore filename extension
+CreateKeystorePage_WizardDefaultMessage=A keystore is an encrypted file protected by a password that groups key pairs - public/private keys.
+CreateKeystoreWizard_CreateNewKeyStore=Create Keystore
+
+#------------------------------------------------------------------------------
+# CertificateBlock
+#------------------------------------------------------------------------------
+CertificateBlock_AliasName=Alias name
+CertificateBlock_BasicInfoGroupTitle=Basic Info
+CertificateBlock_FirstAndLastName=First and Last Name
+CertificateBlock_Organization=Organization
+CertificateBlock_OrganizationUnit=Organization Unit
+CertificateBlock_CityOrLocality=City or Locality
+CertificateBlock_StateOrProvince=State or Province
+CertificateBlock_ConfirmKeyPassword_Label=Confirm Password
+CertificateBlock_EnterPassword_InfoMessage=Enter keystore password
+CertificateBlock_CountryCode=Country Code (XX)
+CertificateBlock_FieldIsEmpty={0} cannot be empty.
+CertificateBlock_Validity=Validity (years)
+CertificateBlock_Validity_Error=Validity should be a positive integer value.
+CertificateBlock_ExpirationDate=Expiration Date
+CertificateBlock_KeyPassword_Label=Key Password
+CertificateBlock_KeyTooltip=Organization: {0}\nFirst and Last Name: {1}
+CertificateInfoDialog_NotAvailableProperty=Not available
+CertificateInfoDialog_ShellTitle=Key Properties
+CertificateInfoDialog_UnknownCertificateKeypairType=Unknown key type
+CertificateBlock_CreationDate=Creation Date
+CertificateBlock_DetailedInfoGroupTitle=Detailed Info
+CertificateBlock_DetailedInfoNonEmptyFieldsRestriction=Validity and at least one other piece of detailed information must be supplied.
+
+#------------------------------------------------------------------------------
+# Certificate Properties Handler
+#------------------------------------------------------------------------------
+CertificatePropertiesHandler_ErrorGettingCertificateOrKeypairProperties=Error getting key properties
+
+#------------------------------------------------------------------------------
+# Create Self Signed wizard page
+#------------------------------------------------------------------------------
+CreateKeyWizard_ErrorCreatingKey_DialogTitle=Error creating key
+CreateSelfSignedCertificateWizardPage_Description=Provide the self-signed key information. Required fields are marked with '*'.
+CreateSelfSignedCertificateWizardPage_Title=Create Self-Signed Key
+KeystoreManagerView_ErrorImportingBackwardKeystore=Could not import the old MOTODEV keystore (for backward compatibility) from {0}
+KeystoreManagerView_ErrorLoadingMappedKeystoresFromPersistence=Error loading mapped keystores
+KeyStoreModel_Error_GettingAliasesFromKeystore=Cannot get aliases from keystore {0}.
+KeyStoreNode_CouldNotGetKeyStorePassword=Could not get keystore password:
+KeyStoreNode_CouldNotLoadKeystore_Tooltip=Could not load keystore file. Use Refresh to try again.
+KeyStoreNode_ErrorKeystoreNotFound=Keystore file not found
+KeyStoreNode_IncorrectPasswordToDeleteEntries_Error=Incorrect password
+KeyStoreNode_InvalidPassword=Invalid password. Try again.
+KeyStoreNode_KeyPairNotMapped_LogMessage=Entry with alias: {0} is a key pair, which is not supported for this view.
+KeyStoreNode_KeyPairNotMapped_Message=Keystore contains unsupported entries. Check log for details.
+KeyStoreNode_KeyPairNotMapped_Title=Entry not mapped
+KeyStoreNode_KeystoreFileNotFound=Keystore file not found
+KeyStoreNode_KeystoreTypeWrong_NodeStatus=Keystore type is wrong. Re-import keystore with the correct type.
+KeyStoreNode_NotFoundOrIncorrectPasswordToDeleteEntry=Password not found or incorrect when deleting entry with alias:
+KeyStoreNode_Password_NotNull=Password cannot be null
+KeyStoreNode_UseRefresh_StatusNode=Unable to read keystore. Refresh, and try again.
+KeyStoreNode_Wrong_KeystoreType_Message=The keystore {0} has been mapped with the incorrect type JCEKS. Keystore type has been changed to the correct type JKS.
+KeyStoreNode_Wrong_KeystoreType_Title=Wrong Keystore Type
+KeyStoreRootNode_Error_AlreadyMappedKeystorePath=Keystore in path: {0} is already mapped in the view.
+KeyStoreUtils_RSA_Keys_Expected=RSA keys are expected; use RSAPublicKey and RSAPrivateKey
+KeyStoreUtils_Error_AddEntryToKeyStore=An error has ocurred while attempting to add the entry {0}
+KeyStoreUtils_Error_DeleteKeyStore=Could not delete the keystore file {0}
+KeyStoreUtils_Error_FileAlreadyExists=Keystore file {0} already exists. Choose a new file path.
+KeyStoreUtils_Error_LoadKeyStore=An error has ocurred while attempting to load the keystore file {0}
+KeyStoreUtils_Error_WriteKeyStore=An error has ocurred while attempting to write the keystore file {0}
+KeyStoreUtils_ErrorDeletingAlias=Error deleting alias:
+
+
+UNSIGN_EXTERNAL_PKG_WIZARD_DESCRIPTION = Remove package signatures
+UNSIGN_EXTERNAL_PKG_WIZARD_WINDOW_TITLE = Package Signature Removal
+UNSIGN_EXTERNAL_PKG_WIZARD_OPERATION = Removing Signature from Package:
+UNSIGN_EXTERNAL_PKG_WIZARD_ERROR = Signatures could not be removed from the following packages
+UNSIGN_EXTERNAL_PKG_WIZARD_ERROR_REASON = Unable to write files
+SelectExistentKeystorePage_CheckboxText_AlsoImportIntoSigningView=Add keystore to Signing and Keys view
+SelectExistentKeystorePage_KeystorePasswordLabel=Keystore Password:
+SelectExistentKeystorePage_WizardPageMessage=Select an existing keystore to use for exporting.
+SelectExistentKeystorePage_WizardPageTitle=Select existing keystore
+SelectExistentKeystoreWizard_BrowseExistentKeystore_PageTitle=Browse existing keystore
+SelectExistentKeystoreWizard_Error_InvalidPassword=Invalid password. Check password again
+SelectExistentKeystoreWizard_Error_KeystoreType=Verify that the keystore type is correct and that the keystore file exists.
+SIGN_EXTERNAL_PKG_WIZARD_SOURCE_DIR_LABEL = Packages folder:
+SIGN_EXTERNAL_PKG_WIZARD_SOURCE_DIR_INVALID = The chosen folder is invalid or does not exist
+SIGN_EXTERNAL_PKG_WIZARD_SOURCE_DIR_NOT_DIRECTORY = The chosen source directory is not a directory
+SIGN_EXTERNAL_PKG_WIZARD_DESCRIPTION = Sign packages with the chosen key
+SIGN_EXTERNAL_PKG_WIZARD_SOURCE_DIR_EMPTY = Select a folder containing Android packages.
+SIGN_EXTERNAL_PKG_WIZARD_NO_CERTIFICATE_ERROR = There are no keys available. Obtain a key to sign the selected packages.
+SIGN_EXTERNAL_PKG_WIZARD_NO_AVAILABLE_PACKAGES = This folder does not contain any packages
+UNSIGN_EXTERNAL_PKG_WIZARD_NO_PACKAGES_SELECTED = Select at least one package
+SIGN_EXTERNAL_PKG_WIZARD_WINDOW_TITLE = Package Signing
+SIGN_EXTERNAL_PKG_WIZARD_OPERATION = Signing Package:
+SIGN_EXTERNAL_PKG_WIZARD_ERROR = The following packages could not be signed
+SIGN_EXTERNAL_PKG_WIZARD_LOAD = Load
+SIGN_EXTERNAL_PKG_WIZARD_FILESYSTEM = Filesystem...
+SIGN_EXTERNAL_PKG_WIZARD_WORKSPACE = Workspace...
+SIGN_EXTERNAL_PKG_WIZARD_WORKSPACE_SIMPLE = Workspace
+PACKAGE_EXPORT_WIZARD_AREA_SELECT_ALL_BUTTON = Select All
+PACKAGE_EXPORT_WIZARD_AREA_DESELECT_ALL_BUTTON = Deselect All
+SIGN_WIZARD_AREA_SIGN_KEYSTORE_LABEL = Keystore:
+SIGN_WIZARD_AREA_SIGN_KEYS_LABEL = Key:
+SIGN_EXTERNAL_PKG_WIZARD_CHOOSE=Select the directory that contains the APK
+PasswordProvider_DialogTitle=Keystore password
+PasswordProvider_Error_WhileRemovingPassword=Could not open secure Preferences. Failed to remove saved password from keystore {0}.
+PasswordProvider_Error_WhileSaving=Could not open secure Preferences. It will not be possible to save or restore passwords.
+PasswordProvider_MessageLabel=Supply the password for the keystore file: {0}.\n
+PasswordProvider_Key_MessageLabel=When signing a package you must supply the key password.\nEnter the password for the key: {0}.\n
+PasswordProvider_PasswordLabel=Password:
+PasswordProvider_SaveThisPassword=Save password
+RemoveExternalPackageSignaturePage_Package_Tree_Label=Packages to be unsigned:
+RestoreBackupDialog_BackUp_File=Backup File:
+RestoreBackupDialog_BackUpFile_Not_Exist=Selected backup file does not exist.
+RestoreBackupDialog_Browse_Button=Browse...
+RestoreBackupDialog_Default_Message=Choose an archive file and select the keystore files that you want to restore
+RestoreBackupDialog_Destination=Destination:
+RestoreBackupDialog_Dialog_Title=Restore From Backup
+RestoreBackupDialog_Error_Loading_Entries=Unable to load entries from the zip file {0}
+RestoreBackupDialog_Invalid_Dest_Path=Invalid destination path
+RestoreBackupDialog_KeyStores=Keystores
+RestoreBackupDialog_Path_Group=Paths
+RestoreBackupDialog_Select_All=Select All
+RestoreBackupDialog_Select_KeyStore=Select at least one keystore to restore from backup
+RestoreBackupDialog_TitleArea_Message=Restore keystores from a backup
+RestoreBackupHandler_Error_Mapping_Message=Unable to map restored keystores
+RestoreBackupHandler_Error_Mapping_Status=Failed to import restored keystores
+RestoreBackupHandler_Error_Mapping_Title=Error Mapping Nodes
+RestoreBackupHandler_Error_Restoring_Backup_Message=Could not restore keystores from file {0}
+RestoreBackupHandler_Error_Restoring_Backup_Status=Could not extract zip file
+RestoreBackupHandler_Error_Restoring_Backup_Title=Error while restoring backup
+RestoreBackupHandler_RestoreIssue_MissingMetadataFile_WarningDescription=Missing backup metadata file.\nIt wasn't possible to determine the type of keystore(s): {0}.\nThe default type: {1} will be assumed for each.
+RestoreBackupHandler_RestoreIssue_WarningTitle=Restore Issue
+READ_ONLY_TEXT = Read Only
+
+SELECTOR_MESSAGE_LOCATION_ERROR_INVALID_DEVICE = The device you are trying to use is invalid
+SELECTOR_MESSAGE_LOCATION_ERROR_PATH_TOO_LONG = The path is too long
+SignExternalPackagePage_Package_Tree_Label=Packages to be signed:
+
+#------------------------------------------------------------------------------
+# Tree Decoration
+#------------------------------------------------------------------------------
+CertificatePeriodExpired_Issue=Warning for Android developers: the key will expire on {0}, but Google Play requires it to expire after October 22, 2033.
+CertificatePeriodNotYeatValid_Issue=Key is not yet valid. It is only valid after {0}.
+ChangePasswordKeyHandler_Error_WrongOldKeyPassword=Old password is invalid
+ChangePasswordKeyHandler_Wrong_Key_Password=Could not retrieve the key. Make sure that the old password is correct.
+ChangePasswordKeystoreHandler_Error_ChangingKeystorePassword=Failed to change keystore password
+ChangePasswordKeystoreHandler_Error_WrongOldKeystorePassword=Old password is invalid
+ChangePasswordKeystoreHandler_InvalidOldPassword=The old password is invalid. Could not access keystore {0} to change keystore password.
+ConvertKeyStoreTypeDialog_Alias_Column=Alias
+ConvertKeyStoreTypeDialog_Choose_KeyStore_Msg=Choose a new keystore.
+ConvertKeyStoreTypeDialog_Choose_New_Type_Msg=Choose a new type for the keystore.
+ConvertKeyStoreTypeDialog_CouldNotLoad_Keystores_Error=Could not load mapped keystores
+ConvertKeyStoreTypeDialog_DefaultMessage=Select a keystore along with its new type.
+ConvertKeyStoreTypeDialog_DialogTitle=Change Keystore Type
+ConvertKeyStoreTypeDialog_Entries_Group=Entries
+ConvertKeyStoreTypeDialog_Error_Loading_Keystore=An error has occurred while attempting to load keystore entries.
+ConvertKeyStoreTypeDialog_Incorrect_Entry_Pass=Enter the password for entries with non-verified passwords.
+ConvertKeyStoreTypeDialog_Invalid_Keystore_Pass=Invalid keystore password.
+ConvertKeyStoreTypeDialog_KeyStoreLabel=Keystore:
+ConvertKeyStoreTypeDialog_Load_Button=Load
+ConvertKeyStoreTypeDialog_NewType_Label=New Type:
+ConvertKeyStoreTypeDialog_Original_Type_Label=Original Type:
+ConvertKeyStoreTypeDialog_Password_Column=Password
+ConvertKeyStoreTypeDialog_Password_Label=Password:
+ConvertKeyStoreTypeDialog_Verified_Column=Verified
+ConvertKeyStoreTypeDialog_Verified_Pass_Wrong=Wrong password
+ConvertKeyStoreTypeDialog_Verified_Pass_Yes=Yes
+EntryNode_ErrorGettingCertificateFromEntry=Error getting key from entry: {0}
+EntryNode_NotFoundOrTypeWrong=Not found or type wrong for alias: {0}
+NewKeyBlock_PasswordGroupTitle=Password
+
+#------------------------------------------------------------------------------
+# Change password
+#------------------------------------------------------------------------------
+Passwordinput_EnterOldKeystorePasssword_Message=Change the password for keystore {0}.\nThe keystore password protects they keystore's keys.
+PasswordChanged_Info_Title=Password changed
+PasswordChanged_Info_Message=The password for keystore {0} was successfully changed.
+PasswordInput_EnterOldKeyPasssword_Message=The key password protects your key.
+PasswordChanged_KeyInfo_Message=Your key password was successfully changed.
+PasswordChanged_InvalidKeystorePassword=The password for this keystore is invalid.
+ToolsException_GenericError=An unexpected error occurred. \ No newline at end of file
diff --git a/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/job/CreateKeyJob.java b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/job/CreateKeyJob.java
new file mode 100644
index 0000000..c519235
--- /dev/null
+++ b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/job/CreateKeyJob.java
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.studio.android.certmanager.job;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.SubMonitor;
+import org.eclipse.core.runtime.jobs.Job;
+
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.common.utilities.EclipseUtils;
+import com.motorolamobility.studio.android.certmanager.core.PasswordProvider;
+import com.motorolamobility.studio.android.certmanager.exception.InvalidPasswordException;
+import com.motorolamobility.studio.android.certmanager.exception.KeyStoreManagerException;
+import com.motorolamobility.studio.android.certmanager.i18n.CertificateManagerNLS;
+import com.motorolamobility.studio.android.certmanager.ui.composite.NewKeyBlock;
+import com.motorolamobility.studio.android.certmanager.ui.model.CertificateDetailsInfo;
+import com.motorolamobility.studio.android.certmanager.ui.model.EntryNode;
+import com.motorolamobility.studio.android.certmanager.ui.model.IKeyStore;
+import com.motorolamobility.studio.android.certmanager.ui.model.IKeyStoreEntry;
+import com.motorolamobility.studio.android.certmanager.ui.wizards.CreateKeyWizard;
+
+/**
+ * Creating a key is a time expensive task.
+ * In order to not block the UI while creating the key, a new
+ * job is creating and executed in the background.
+ */
+public class CreateKeyJob extends Job
+{
+ private static final String KEY_PASSWORD_SAVED = "Key password saved";
+
+ private static final String SAVING_KEY_PASSWORD = "Saving key password";
+
+ private static final String KEY_CREATED = "Key created";
+
+ private static final String CREATING_KEY = "Creating key";
+
+ private static final String GETTING_KEY_INFO = "Getting key info";
+
+ private static final int NUMBER_OF_TASKS = 5;
+
+ /*
+ * Key block, used to retrieve information from the create key wizard.
+ */
+ private NewKeyBlock newKeyBlock = null;
+
+ /*
+ * Information to create key
+ */
+ private CertificateDetailsInfo certificateDetailsInfo = null;
+
+ private IKeyStore keystore = null;
+
+ private String keyStorePass;
+
+ public CreateKeyJob(String jobName, NewKeyBlock newKeyBlock,
+ CertificateDetailsInfo certificateDetailsInfo, IKeyStore keystore, String keyStorePass)
+ {
+ super(jobName);
+ this.certificateDetailsInfo = certificateDetailsInfo;
+ this.newKeyBlock = newKeyBlock;
+ this.keystore = keystore;
+ this.keyStorePass = keyStorePass;
+ if (this.keyStorePass == null)
+ {
+ try
+ {
+ this.keyStorePass = keystore.getPasswordProvider().getKeyStorePassword(false);
+ }
+ catch (KeyStoreManagerException e)
+ {
+ StudioLogger.error("Error while accessing keystore manager. " + e.getMessage());
+ }
+ }
+ }
+
+ @Override
+ protected IStatus run(IProgressMonitor monitor)
+ {
+ SubMonitor subMonitor = SubMonitor.convert(monitor);
+ subMonitor.beginTask(CREATING_KEY, NUMBER_OF_TASKS);
+
+ StudioLogger.debug(GETTING_KEY_INFO);
+ subMonitor.worked(1);
+
+ IKeyStoreEntry entryNode = null;
+ try
+ {
+ StudioLogger.debug(CREATING_KEY);
+ subMonitor.worked(1);
+ String keystorePassword = getKeyStorePassword();
+
+ if (keystorePassword != null)
+ {
+ entryNode =
+ EntryNode.createSelfSignedNode(keystore, keystorePassword,
+ certificateDetailsInfo.getAlias(), certificateDetailsInfo);
+ StudioLogger.debug(KEY_CREATED);
+ subMonitor.worked(1);
+
+ if (newKeyBlock.needToSaveKeyPassword())
+ {
+ StudioLogger.debug(SAVING_KEY_PASSWORD);
+ subMonitor.worked(1);
+ PasswordProvider passwordProvider = new PasswordProvider(keystore.getFile());
+ passwordProvider.savePassword(entryNode.getAlias(),
+ newKeyBlock.getKeyPassword());
+ StudioLogger.debug(KEY_PASSWORD_SAVED);
+ subMonitor.worked(1);
+ }
+ }
+
+ }
+ catch (KeyStoreManagerException e)
+ {
+ EclipseUtils.showErrorDialog(
+ CertificateManagerNLS.CreateKeyWizard_ErrorCreatingKey_DialogTitle,
+ e.getMessage());
+ StudioLogger.error(CreateKeyWizard.class,
+ CertificateManagerNLS.CreateKeyWizard_ErrorCreatingKey_DialogTitle, e);
+ }
+
+ subMonitor.done();
+ return Status.OK_STATUS;
+ }
+
+ private String getKeyStorePassword() throws KeyStoreManagerException
+ {
+ boolean invalidPass = true;
+ String keystorePassword = this.keyStorePass;
+
+ try
+ {
+ if (keystorePassword != null)
+ {
+ this.keystore.isPasswordValid(keystorePassword);
+ invalidPass = false;
+ }
+ }
+ catch (InvalidPasswordException e1)
+ {
+ invalidPass = true;
+ }
+ while (invalidPass)
+ {
+ try
+ {
+ this.keystore.isPasswordValid(keystorePassword);
+ invalidPass = false;
+ }
+ catch (InvalidPasswordException e)
+ {
+ invalidPass = true;
+ PasswordProvider passwordProvider = new PasswordProvider(this.keystore.getFile());
+ keystorePassword = passwordProvider.getKeyStorePassword(true, false);
+
+ if (keystorePassword == null)
+ {
+ invalidPass = false;
+ }
+ }
+ }
+
+ return keystorePassword;
+ }
+
+ public String getCreatedKeyAlias()
+ {
+ return this.certificateDetailsInfo.getAlias();
+ }
+
+ public IKeyStore getKeyStore()
+ {
+ return this.keystore;
+ }
+
+}
diff --git a/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/packaging/PackageFile.java b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/packaging/PackageFile.java
new file mode 100644
index 0000000..27a565d
--- /dev/null
+++ b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/packaging/PackageFile.java
@@ -0,0 +1,648 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.motorolamobility.studio.android.certmanager.packaging;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.ByteBuffer;
+import java.nio.channels.Channels;
+import java.nio.channels.FileChannel;
+import java.nio.channels.WritableByteChannel;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.jar.Attributes;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+import java.util.jar.JarOutputStream;
+import java.util.jar.Manifest;
+import java.util.zip.CRC32;
+
+import org.eclipse.core.runtime.IBundleGroup;
+import org.eclipse.core.runtime.IBundleGroupProvider;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.core.runtime.Platform;
+
+import com.motorola.studio.android.common.IAndroidConstants;
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.common.utilities.AndroidUtils;
+import com.motorola.studio.android.common.utilities.FileUtil;
+import com.motorolamobility.studio.android.certmanager.CertificateManagerActivator;
+
+/**
+ * This class is an in-memory package file representation.
+ */
+public class PackageFile
+{
+ private static final String COM_MOTOROLA_STUDIO_ANDROID_FEATURE =
+ "com.motorola.studio.android.feature";
+
+ /*
+ * Map of entries contained in this package file
+ */
+ private final Map<String, File> entryMap = new HashMap<String, File>();
+
+ /*
+ * Map of temporary entries contained in this package file (it duplicates
+ * the entries on entryMap)
+ */
+ private final Map<String, File> tempEntryMap = new HashMap<String, File>();
+
+ /*
+ * Package manifest
+ */
+ private Manifest manifest;
+
+ private int apiVersion;
+
+ private String targetName;
+
+ private Set<String> rawFiles = new HashSet<String>();
+
+ /**
+ * Creates an empty PackageFile.
+ *
+ * @param createdBy
+ * Created-By manifest attribute
+ */
+ public PackageFile(String createdBy)
+ {
+ targetName = "";
+ apiVersion = -1;
+ manifest = createManifest(createdBy);
+ }
+
+ /**
+ * Creates an empty PackageFile
+ *
+ * @param createdBy
+ * Created-By manifest attribute
+ */
+ public PackageFile(String createdBy, String targetName, int apiVersion)
+ {
+ this.targetName = targetName;
+ this.apiVersion = apiVersion;
+ manifest = createManifest(createdBy);
+ }
+
+ /**
+ * Creates a PackageFile from an existing JarFile
+ *
+ * @param jarFile
+ * the base jar file
+ * @param apiVersion
+ * @param targetName
+ * @throws IOException
+ * if an I/O error occurs when reading the contents of the base
+ * jar file
+ */
+ public PackageFile(JarFile jarFile) throws IOException
+ {
+ this(jarFile, "", -1);
+ }
+
+ /**
+ * Creates a PackageFile from an existing JarFile
+ *
+ * @param jarFile
+ * the base jar file
+ * @param apiVersion
+ * @param targetName
+ * @throws IOException
+ * if an I/O error occurs when reading the contents of the base
+ * jar file
+ */
+ public PackageFile(JarFile jarFile, String targetName, int apiVersion) throws IOException
+ {
+ manifest = jarFile.getManifest();
+ this.targetName = targetName;
+ this.apiVersion = apiVersion;
+ String createdBy = generateStudioFingerprint();
+ if (manifest == null)
+ {
+ manifest = createManifest(createdBy);
+ }
+
+ // go through all the entries in the base jar file
+ Enumeration<JarEntry> entryEnum = jarFile.entries();
+
+ while (entryEnum.hasMoreElements())
+ {
+ JarEntry entry = entryEnum.nextElement();
+ if (!entry.getName().equalsIgnoreCase(
+ CertificateManagerActivator.METAFILES_DIR
+ + CertificateManagerActivator.JAR_SEPARATOR
+ + CertificateManagerActivator.MANIFEST_FILE_NAME))
+ {
+ // create a temporary file for this entry
+ InputStream is = jarFile.getInputStream(entry);
+ File tempFile =
+ File.createTempFile(CertificateManagerActivator.TEMP_FILE_PREFIX, null);
+ tempFile.deleteOnExit();
+
+ // copy contents from the original file to the temporary file
+ BufferedInputStream bis = null;
+ BufferedOutputStream bos = null;
+
+ try
+ {
+ bis = new BufferedInputStream(is);
+ bos = new BufferedOutputStream(new FileOutputStream(tempFile));
+
+ int c;
+ while ((c = bis.read()) >= 0)
+ {
+ bos.write(c);
+ }
+ }
+ finally
+ {
+ if (bis != null)
+ {
+ bis.close();
+ }
+
+ if (bos != null)
+ {
+ bos.close();
+ }
+ }
+
+ // add the temporary file to the package file
+ setTempEntryFile(entry.getName(), tempFile);
+
+ //check if the entry is not compressed to keep it this way
+ if (entry.getMethod() == JarEntry.STORED)
+ {
+ rawFiles.add(entry.getName());
+ }
+ }
+ }
+ }
+
+ private String generateStudioFingerprint()
+ {
+ IBundleGroupProvider[] providers = Platform.getBundleGroupProviders();
+ List<IBundleGroup> groups = new ArrayList<IBundleGroup>();
+ if (providers != null)
+ {
+ for (int i = 0; i < providers.length; ++i)
+ {
+ IBundleGroup[] bundleGroups = providers[i].getBundleGroups();
+ groups.addAll(Arrays.asList(bundleGroups));
+ }
+ }
+ String version = "";
+ for (IBundleGroup group : groups)
+ {
+ if (group.getIdentifier().equals(COM_MOTOROLA_STUDIO_ANDROID_FEATURE))
+ {
+ version = group.getVersion();
+ break;
+ }
+ }
+
+ StringBuilder stringBuilder =
+ new StringBuilder(CertificateManagerActivator.CREATED_BY_FIELD_VALUE);
+ stringBuilder.append(" v");
+ stringBuilder.append(version);
+ stringBuilder.append(" - ");
+ stringBuilder.append(Platform.getOS());
+ stringBuilder.append(", ");
+ stringBuilder.append(Platform.getOSArch());
+ stringBuilder.append(". ");
+ if (targetName.trim().length() > 0)
+ {
+ stringBuilder.append("Android target - ");
+ stringBuilder.append(targetName);
+ stringBuilder.append(", ");
+ }
+ if (apiVersion >= 0)
+ {
+ stringBuilder.append("API version - ");
+ stringBuilder.append(apiVersion);
+ stringBuilder.append(".");
+ }
+ return stringBuilder.toString();
+ }
+
+ /**
+ * Gets the names for all the entries in this package file
+ *
+ * @return Set containing the names for all the entries in this package file
+ */
+ public Set<String> getEntryNames()
+ {
+ return Collections.unmodifiableSet(entryMap.keySet());
+ }
+
+ /**
+ * Gets the File object for a given entry
+ *
+ * @param entryName
+ * the entry name
+ * @return the File object corresponding to entryName
+ */
+ public File getEntryFile(String entryName)
+ {
+ return entryMap.get(entryName);
+ }
+
+ /**
+ * Puts a File object as a named entry in this package file
+ *
+ * @param entryName
+ * the entry name
+ * @param file
+ * the File object corresponding to entryName
+ */
+ public void setEntryFile(String entryName, File file)
+ {
+ entryMap.put(entryName, file);
+ }
+
+ /**
+ * Puts a Temporary File object as a named entry in this package file
+ *
+ * @param entryName
+ * the entry name
+ * @param tempFile
+ * the temporary file object corresponding to entryName
+ */
+ public void setTempEntryFile(String entryName, File tempFile)
+ {
+ entryMap.put(entryName, tempFile);
+ tempEntryMap.put(entryName, tempFile);
+ }
+
+ /**
+ * Remove the named entry of files map of this package
+ *
+ * @param entryName
+ * the name of entry to be removed
+ * @throws IOException
+ */
+ public void removeEntryFile(String entryName) throws IOException
+ {
+ File entryFile = entryMap.get(entryName);
+ if (entryFile != null)
+ {
+ entryMap.remove(entryName);
+ if (tempEntryMap.containsKey(entryName))
+ {
+ tempEntryMap.remove(entryName);
+ deleteFile(entryFile);
+ }
+ }
+ }
+
+ /**
+ * Remove the meta entry files (files under META-INF folder)
+ *
+ * @throws IOException
+ */
+ public void removeMetaEntryFiles() throws IOException
+ {
+ String createdBy =
+ manifest.getMainAttributes().getValue(CertificateManagerActivator.CREATED_BY_FIELD);
+ Set<String> entries = new HashSet<String>(getEntryNames());
+ for (String entry : entries)
+ {
+ if (entry.startsWith(CertificateManagerActivator.METAFILES_DIR))
+ {
+ removeEntryFile(entry);
+ }
+ }
+ Manifest cleanManifest = new Manifest();
+ cleanManifest.getMainAttributes().putAll(manifest.getMainAttributes());
+ if ("".equals(targetName) && (apiVersion <= 0)) //Just removing signatures.
+ {
+ cleanManifest.getMainAttributes().putValue(
+ CertificateManagerActivator.CREATED_BY_FIELD, createdBy);
+ }
+ else
+ {
+ cleanManifest.getMainAttributes().putValue(
+ CertificateManagerActivator.CREATED_BY_FIELD, generateStudioFingerprint());
+ }
+ manifest = cleanManifest;
+ }
+
+ private void writeCompressed(JarOutputStream jarOut, String entryName) throws IOException
+ {
+ File file = entryMap.get(entryName);
+ if ((file.exists()) && (file.isFile()))
+ {
+ JarEntry entry = new JarEntry(entryName);
+ jarOut.putNextEntry(entry);
+ WritableByteChannel outputChannel = null;
+ FileChannel readFromFileChannel = null;
+ FileInputStream inputStream = null;
+ try
+ {
+ outputChannel = Channels.newChannel(jarOut);
+ inputStream = new FileInputStream(file);
+ readFromFileChannel = inputStream.getChannel();
+ readFromFileChannel.transferTo(0, file.length(), outputChannel);
+ }
+ finally
+ {
+ try
+ {
+ if (jarOut != null)
+ {
+ jarOut.closeEntry();
+ }
+ if (readFromFileChannel != null)
+ {
+ readFromFileChannel.close();
+ }
+ if (inputStream != null)
+ {
+ inputStream.close();
+ }
+ }
+ catch (IOException e)
+ {
+ StudioLogger.error("Could not close stream: ", e.getMessage()); //$NON-NLS-1$
+ }
+ }
+ }
+
+ }
+
+ private void writeRaw(JarOutputStream jarOut, String entryName) throws IOException
+ {
+ FileInputStream inputStream = null;
+ File file = entryMap.get(entryName);
+ if ((file.exists()) && (file.isFile()))
+ {
+ CRC32 crc = new CRC32();
+ JarEntry entry = new JarEntry(entryName);
+ entry.setMethod(JarEntry.STORED);
+ entry.setSize(file.length());
+ WritableByteChannel outputChannel = null;
+ FileChannel readFromFileChannel = null;
+ try
+ {
+ outputChannel = Channels.newChannel(jarOut);
+ inputStream = new FileInputStream(file);
+ readFromFileChannel = inputStream.getChannel();
+
+ ByteBuffer buffer = ByteBuffer.allocate(1024 * 1024);
+ crc.reset();
+ while (readFromFileChannel.read(buffer) > 0)
+ {
+ buffer.flip();
+ byte[] byteArray = new byte[buffer.limit()];
+ buffer.get(byteArray, 0, buffer.limit());
+ crc.update(byteArray);
+ buffer.clear();
+ }
+ entry.setCrc(crc.getValue());
+ jarOut.putNextEntry(entry);
+ readFromFileChannel.transferTo(0, file.length(), outputChannel);
+ }
+ finally
+ {
+ if (inputStream != null)
+ {
+ inputStream.close();
+ }
+ if (readFromFileChannel != null)
+ {
+ readFromFileChannel.close();
+ }
+ jarOut.closeEntry();
+ }
+ }
+ }
+
+ /**
+ * Writes this package file to an output stream
+ *
+ * @param outputStream
+ * the stream to write the package to
+ * @throws IOException
+ * if an I/O error occurs when writing the package contents to
+ * the output stream
+ */
+ public void write(OutputStream outputStream) throws IOException
+ {
+ // create a jar output stream
+ JarOutputStream jarOut = null;
+
+ try
+ {
+ jarOut = new JarOutputStream(outputStream, manifest);
+
+ // for each entry in the package file
+ for (String jarEntryName : entryMap.keySet())
+ {
+ if (jarEntryName.contains("raw/") || rawFiles.contains(jarEntryName))
+ {
+ writeRaw(jarOut, jarEntryName);
+ }
+ else
+ {
+ writeCompressed(jarOut, jarEntryName);
+ }
+ }
+ }
+ finally
+ {
+ if (jarOut != null)
+ {
+ try
+ {
+ jarOut.finish();
+ jarOut.close();
+ }
+ catch (IOException e)
+ {
+ StudioLogger.error("Could not close stream while writing jar file. "
+ + e.getMessage());
+ }
+ }
+ }
+ }
+
+ /**
+ * Calculate the package total size returns long
+ */
+ public long getTotalSize()
+ {
+ long totalSize = 0;
+
+ for (String jarEntryName : entryMap.keySet())
+ {
+ File file = entryMap.get(jarEntryName);
+ if ((file.exists()) && (file.isFile()))
+ {
+ totalSize += file.length();
+ }
+ }
+
+ return totalSize;
+ }
+
+ /**
+ * Gets the package manifest
+ *
+ * @return the package manifest
+ */
+ public Manifest getManifest()
+ {
+ return manifest;
+ }
+
+ /**
+ * Remove the temporary entry files
+ *
+ * @throws IOException
+ */
+ public void removeTemporaryEntryFiles() throws IOException
+ {
+ Set<String> tempEntries =
+ new HashSet<String>(Collections.unmodifiableSet(tempEntryMap.keySet()));
+ for (String tempEntry : tempEntries)
+ {
+ removeEntryFile(tempEntry);
+ }
+ }
+
+ /*
+ * Delete a single file from the filesystem.
+ *
+ * @param fileToDelete
+ * A <code>File</code> object representing the file to be
+ * deleted.
+ * @throws IOException
+ * if any problem occurs deleting the file.
+ */
+ private void deleteFile(File fileToDelete) throws IOException
+ {
+ if ((fileToDelete != null) && fileToDelete.exists() && fileToDelete.isFile()
+ && fileToDelete.canWrite())
+ {
+ fileToDelete.delete();
+ }
+ else
+ {
+ String errorMessage = "";
+ if (fileToDelete == null)
+ {
+ errorMessage = "Null pointer for file to delete.";
+ }
+ else
+ {
+ if (!fileToDelete.exists())
+ {
+ errorMessage = "The file " + fileToDelete.getName() + " does not exist.";
+ }
+ else
+ {
+ if (!fileToDelete.isFile())
+ {
+ errorMessage = fileToDelete.getName() + " is not a file.";
+ }
+ else
+ {
+ if (!fileToDelete.canWrite())
+ {
+ errorMessage = "Cannot write to " + fileToDelete.getName();
+ }
+ }
+ }
+
+ }
+ throw new IOException("Cannot delete file: " + errorMessage);
+ }
+ }
+
+ /**
+ * Create a new Manifest with the basic values and the created by string
+ * @param createdBy who is creating this manifest
+ * @return a new Manifest with basic values and desired created by string
+ */
+ public Manifest createManifest(String createdBy)
+ {
+ Manifest newManifest = new Manifest();
+ Attributes mainAttributes = newManifest.getMainAttributes();
+ mainAttributes.putValue(Attributes.Name.MANIFEST_VERSION.toString(),
+ CertificateManagerActivator.MANIFEST_VERSION);
+ mainAttributes.putValue(CertificateManagerActivator.CREATED_BY_FIELD, createdBy);
+ return newManifest;
+ }
+
+ /**
+ * Execute the zipalign for a certain apk
+ * @param apk
+ */
+ public static void zipAlign(File apk)
+ {
+ // String zipAlign = SdkUtils.getSdkToolsPath();
+ String zipAlign =
+ AndroidUtils.getSDKPathByPreference() + Path.SEPARATOR + IAndroidConstants.FD_TOOLS;
+ try
+ {
+ File tempFile = File.createTempFile("_tozipalign", ".apk");
+ FileUtil.copyFile(apk, tempFile);
+
+ if (!zipAlign.endsWith(File.separator))
+ {
+ zipAlign += File.separator;
+ }
+ zipAlign += Platform.getOS().equals(Platform.OS_WIN32) ? "zipalign.exe" : "zipalign";
+
+ String[] command = new String[]
+ {
+ zipAlign, "-f", "-v", "4", tempFile.getAbsolutePath(), apk.getAbsolutePath()
+ };
+ StringBuilder commandLine = new StringBuilder();
+ for (String commandPart : command)
+ {
+ commandLine.append(commandPart);
+ commandLine.append(" ");
+ }
+
+ StudioLogger.info(PackageFile.class, "Zipaligning package: " + commandLine.toString());
+ Runtime.getRuntime().exec(command);
+ }
+ catch (IOException e)
+ {
+ StudioLogger.error(PackageFile.class, "Error while zipaligning package", e);
+ }
+ catch (Exception e)
+ {
+ StudioLogger.error(PackageFile.class,
+ "Zipalign application cannot be executed - insuficient permissions", e);
+ }
+ }
+}
diff --git a/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/packaging/sign/ISignConstants.java b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/packaging/sign/ISignConstants.java
new file mode 100644
index 0000000..cb8e4df
--- /dev/null
+++ b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/packaging/sign/ISignConstants.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.motorolamobility.studio.android.certmanager.packaging.sign;
+
+public interface ISignConstants
+{
+ /**
+ * md5 algorithm
+ */
+ public final String MD5 = "MD5";
+
+ /**
+ * rsa algorithm
+ */
+ public final String RSA = "RSA";
+
+ /**
+ * dsa algorithm
+ */
+ public final String DSA = "DSA";
+
+ /**
+ * sha1 algorithm
+ */
+ public final String SHA1 = "SHA1";
+
+ /**
+ * algorithm connector
+ */
+ public final String ALGORITHM_CONNECTOR = "with";
+
+ /**
+ * Signature file name extension
+ */
+ public final String SIGNATURE_FILE_NAME_EXTENSION = ".SF";
+
+ /**
+ * Signature Version
+ */
+ public final String SIGNATURE_VERSION_KEY = "Signature-Version";
+
+ public final String SIGNATURE_VERSION_VALUE = "1.0";
+
+ /**
+ * Digest algorithm entry in manifest
+ */
+ public final String SHA1_DIGEST = "SHA1-Digest";
+
+ /**
+ * sha1-digest-manifest signature entry
+ */
+ public final String SHA1_DIGEST_MANIFEST = "SHA1-Digest-Manifest";
+
+ /**
+ * manifest main attributes signature entry
+ */
+ public final String SHA1_DIGEST_MANIFEST_MAIN = "SHA1-Digest-Manifest-Main-Attributes";
+
+ /**
+ * Name of the signature files (.SF and .RSA)
+ */
+ public final String SIGNATURE_FILE_NAME = "CERT";
+}
diff --git a/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/packaging/sign/ManifestDigester.java b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/packaging/sign/ManifestDigester.java
new file mode 100644
index 0000000..9ebcee6
--- /dev/null
+++ b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/packaging/sign/ManifestDigester.java
@@ -0,0 +1,185 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.motorolamobility.studio.android.certmanager.packaging.sign;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.jar.Attributes;
+import java.util.jar.Manifest;
+
+import com.motorola.studio.android.common.log.StudioLogger;
+
+/**
+ * This class is a Manifest digester.
+ * It generates digested hashes for each entry in the manifest file.
+ */
+public class ManifestDigester
+{
+ private final Manifest manifest;
+
+ private HashMap<String, ManifestEntry> entries = null;
+
+ /**
+ * Create a new manifest digester with the given manifest
+ * @param manifest
+ */
+ public ManifestDigester(final Manifest manifest)
+ {
+ this.manifest = manifest;
+ initialize();
+ }
+
+ /**
+ * Initialize the digester creating internal entries for each manifest entry
+ */
+ private void initialize()
+ {
+ Map<String, Attributes> manifestEntries = manifest.getEntries();
+
+ // initialize internal entries list
+ entries = new HashMap<String, ManifestEntry>(manifestEntries.size());
+ for (String entryName : manifestEntries.keySet())
+ {
+ entries.put(entryName, new ManifestEntry(entryName, manifestEntries.get(entryName)));
+ }
+ }
+
+ /**
+ * Get this Manifest file digested
+ * @throws IOException if some error occurs during entries encoding
+ */
+ public String getDigestedString() throws IOException
+ {
+ StringBuilder builder = new StringBuilder();
+ for (ManifestEntry entry : entries.values())
+ {
+ builder.append(entry.toDigestedManifestEntry());
+ builder.append(ManifestEntry.MANIFEST_NEW_LINE);
+ }
+ return builder.toString();
+ }
+
+ public HashMap<String, ManifestEntry> getEntries()
+ {
+ return entries;
+ }
+
+ /**
+ * Computes the digest for the main manifest attributes
+ *
+ * @return the digest of the main manifest attributes or null otherwise
+ * @throws SignException
+ * if a processing error occurs when computing the digest
+ */
+ public byte[] getDigestedManifestMainAttributes() throws SignException
+ {
+ // create an auxiliary manifest that contain only the main attributes
+ // of the original manifest
+ Manifest auxManifest = new Manifest();
+ byte[] result;
+ Attributes auxMainAttributes = auxManifest.getMainAttributes();
+ Attributes mainAttributes = manifest.getMainAttributes();
+ for (Object attributeKey : mainAttributes.keySet())
+ {
+ String name = attributeKey.toString();
+ String value = mainAttributes.getValue(name);
+ auxMainAttributes.putValue(name, value);
+ }
+
+ result = getDigestedManifest(auxManifest);
+
+ StudioLogger.info(SignatureFile.class, "Created digest for main manifest attributes");
+
+ return result;
+ }
+
+ /**
+ * Get this Manifest digested
+ * @return
+ * @throws SignException
+ */
+ public byte[] getDigestedManifest() throws SignException
+ {
+ return getDigestedManifest(manifest);
+ }
+
+ /**
+ * Computes the digest for the manifest
+ *
+ * @param manifest
+ * the manifest to be digested
+ * @return the digest of the entire manifest or null otherwise
+ * @throws SignException
+ * if a processing error occurs when computing the digest
+ */
+ public static byte[] getDigestedManifest(Manifest manifest) throws SignException
+ {
+ byte[] digestedManifestBytes = null;
+ ByteArrayOutputStream baos = null;
+ try
+ {
+ MessageDigest messageDigest = MessageDigest.getInstance(ISignConstants.SHA1);
+ baos = new ByteArrayOutputStream();
+ manifest.write(baos);
+
+ messageDigest.reset();
+ digestedManifestBytes = messageDigest.digest(baos.toByteArray());
+ }
+ catch (IOException e)
+ {
+ StudioLogger.error(SignatureFile.class,
+ "I/O error encoding manifest digest: " + e.getMessage());
+
+ throw new SignException("I/O error encoding manifest digest", e);
+ }
+ catch (NoSuchAlgorithmException e)
+ {
+ StudioLogger.error(SignatureFile.class, "Error getting message digester");
+
+ throw new SignException("Could digest the manifest");
+ }
+ finally
+ {
+ try
+ {
+ if (baos != null)
+ {
+ baos.close();
+ }
+ }
+ catch (IOException e)
+ {
+ //do nothing
+ }
+ }
+
+ if (digestedManifestBytes == null)
+ {
+ StudioLogger.error(SignatureFile.class, "Error encoding manifest digest");
+
+ throw new SignException("Could not encode manifest digest");
+ }
+
+ StudioLogger.info(SignatureFile.class, "Created manifest digest");
+
+ return digestedManifestBytes;
+ }
+} \ No newline at end of file
diff --git a/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/packaging/sign/ManifestEntry.java b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/packaging/sign/ManifestEntry.java
new file mode 100644
index 0000000..b7223ec
--- /dev/null
+++ b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/packaging/sign/ManifestEntry.java
@@ -0,0 +1,243 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.studio.android.certmanager.packaging.sign;
+
+import java.io.ByteArrayOutputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.Iterator;
+import java.util.jar.Attributes;
+
+import org.bouncycastle.util.encoders.Base64Encoder;
+
+import com.motorola.studio.android.common.log.StudioLogger;
+
+/**
+ * A Class representing a Manifest Entry.
+ */
+public class ManifestEntry
+{
+ /**
+ * New line string according Jar specification
+ */
+ public static final String MANIFEST_NEW_LINE = "\r\n";
+
+ public static final String ENTRY_NAME_ATTRIBUTE = "Name: ";
+
+ public static final String UTF8_CHARSET = "UTF-8";
+
+ /**
+ * Safe line size limit according Jar Specification
+ */
+ public static final int SAFE_LIMIT = 72;
+
+ private final String name;
+
+ private final Attributes attributes;
+
+ /**
+ * Create a new ManifestEntry with the desired name and attributes
+ * @param name
+ * @param attr
+ */
+ public ManifestEntry(String name, Attributes attr)
+ {
+ this.name = name;
+ this.attributes = attr;
+ }
+
+ /**
+ * Get the manifest as it will be written in the Manifest file
+ * @return a byte array representing the manifest entry
+ */
+ public byte[] toManifestEntryBytes()
+ {
+ byte[] result = null;
+ DataOutputStream dataOut = null;
+ ByteArrayOutputStream stream = null;
+ try
+ {
+ stream = new ByteArrayOutputStream();
+ dataOut = new DataOutputStream(stream);
+ String nameField = wrap72bytes(ENTRY_NAME_ATTRIBUTE + name);
+ dataOut.writeBytes(nameField);
+ dataOut.writeBytes(MANIFEST_NEW_LINE);
+ dataOut.writeBytes(getAttributesString());
+ dataOut.writeBytes(MANIFEST_NEW_LINE);
+ dataOut.writeBytes(MANIFEST_NEW_LINE);
+ result = stream.toString().getBytes(UTF8_CHARSET);
+
+ }
+ catch (IOException e)
+ {
+ StudioLogger.error(ManifestEntry.class, "Error getting manifest like bytes");
+ }
+ finally
+ {
+ try
+ {
+ if (dataOut != null)
+ {
+ dataOut.close();
+ }
+ if (stream != null)
+ {
+ stream.close();
+ }
+ }
+ catch (IOException e)
+ {
+ StudioLogger
+ .error("Could not close stream while writing manifest" + e.getMessage());
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Digest the entire entry
+ * @return a byte array with the SHA-1 sum of this entry
+ */
+ public byte[] digest()
+ {
+ byte[] digested = null;
+ try
+ {
+ MessageDigest digester = MessageDigest.getInstance("SHA-1");
+ digester.reset();
+ digester.update(toManifestEntryBytes());
+ digested = digester.digest();
+ }
+ catch (NoSuchAlgorithmException e)
+ {
+ StudioLogger.error(ManifestEntry.class, "Error digesting manifest bytes");
+ }
+ return digested;
+ }
+
+ /**
+ * Get this Entry attributes as it will be written in the Manifest file
+ * @return
+ */
+ private String getAttributesString()
+ {
+ StringBuilder builder = new StringBuilder();
+ Iterator<Object> it = attributes.keySet().iterator();
+ while (it.hasNext())
+ {
+ Object next = it.next();
+ String line = wrap72bytes(next + ": " + attributes.get(next));
+ if (it.hasNext())
+ {
+ line += MANIFEST_NEW_LINE;
+ }
+ builder.append(line);
+ }
+ return builder.toString();
+ }
+
+ /**
+ * Wrap a string into another string with at most 72 bytes per line
+ * According Jar spec, each line must have at most 72 bytes
+ * @param line
+ * @return the wrapped string
+ */
+ public static String wrap72bytes(String line)
+ {
+ String returnString = line;
+
+ if (line.length() > SAFE_LIMIT)
+ {
+ int maximumLength = SAFE_LIMIT - MANIFEST_NEW_LINE.length();
+ StringBuilder wrapped = new StringBuilder();
+ int index = maximumLength;
+ String first = line.substring(0, index); //get first SAFE_LIMIT - MANIFEST_NEW_LINE.length() bytes
+ first += MANIFEST_NEW_LINE;
+ wrapped.append(first);
+ while (index < line.length())
+ {
+ String medium = " ";
+ if ((index + maximumLength) < line.length())
+ {
+ medium += line.substring(index, index + maximumLength); //get the maximum length minus the blank space size
+ }
+ else
+ {
+ medium += line.substring(index);
+ }
+ wrapped.append(medium);
+ index += maximumLength;
+ }
+ returnString = wrapped.toString();
+ }
+
+ return returnString;
+ }
+
+ /**
+ * Get this entry name
+ * @return this entry name
+ */
+ public String getName()
+ {
+ return name;
+ }
+
+ /**
+ * Get this entry ready to be written in the Signature File
+ * @return this entry ready to be written in the Signature File
+ * @throws IOException if some error occurs during encoding
+ */
+ public String toDigestedManifestEntry() throws IOException
+ {
+ Base64Encoder encoder = new Base64Encoder();
+ StringBuilder builder = new StringBuilder();
+ ByteArrayOutputStream output = null;
+
+ try
+ {
+ output = new ByteArrayOutputStream();
+
+ builder.append(wrap72bytes(ENTRY_NAME_ATTRIBUTE + name));
+ builder.append(MANIFEST_NEW_LINE);
+ builder.append(ISignConstants.SHA1_DIGEST + ": ");
+
+ byte[] digest = digest();
+ encoder.encode(digest, 0, digest.length, output);
+
+ builder.append(output.toString());
+ builder.append(MANIFEST_NEW_LINE);
+ }
+ finally
+ {
+ if (output != null)
+ {
+ try
+ {
+ output.close();
+ }
+ catch (IOException e)
+ {
+ StudioLogger.error("Could not close stream: " + e.getMessage());
+ }
+ }
+ }
+ return builder.toString();
+ }
+}
diff --git a/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/packaging/sign/PackageFileSigner.java b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/packaging/sign/PackageFileSigner.java
new file mode 100644
index 0000000..32a5b3f
--- /dev/null
+++ b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/packaging/sign/PackageFileSigner.java
@@ -0,0 +1,263 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.motorolamobility.studio.android.certmanager.packaging.sign;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+import java.nio.channels.Channels;
+import java.nio.channels.ReadableByteChannel;
+import java.security.KeyStoreException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.UnrecoverableKeyException;
+import java.util.jar.Attributes;
+
+import org.bouncycastle.util.encoders.Base64Encoder;
+
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorolamobility.studio.android.certmanager.CertificateManagerActivator;
+import com.motorolamobility.studio.android.certmanager.exception.KeyStoreManagerException;
+import com.motorolamobility.studio.android.certmanager.packaging.PackageFile;
+import com.motorolamobility.studio.android.certmanager.ui.model.IKeyStoreEntry;
+
+/**
+ * Utility class used to sign package files.
+ */
+public class PackageFileSigner
+{
+ public static final String MOTODEV_STUDIO = "MOTODEV Studio";
+
+ /**
+ * Signs a package file
+ *
+ * @param packageFile
+ * the package file to sign
+ * @param certificateAlias
+ * the signing certificate alias
+ * @param createdBy
+ * Created-By manifest attribute
+ * @throws SignException
+ * if a processing error occurs during the signing process
+ * @throws UnrecoverableKeyException
+ */
+ public static void signPackage(PackageFile packageFile, IKeyStoreEntry keystoreEntry,
+ String keyEntryPassword, String createdBy) throws SignException,
+ UnrecoverableKeyException
+ {
+
+ try
+ {
+ Base64Encoder encoder = new Base64Encoder();
+ MessageDigest messageDigest = MessageDigest.getInstance(ISignConstants.SHA1);
+
+ addFilesDigestsToManifest(packageFile, encoder, messageDigest);
+
+ addSignatureFiles(packageFile, keystoreEntry, keyEntryPassword, encoder, createdBy);
+ }
+ catch (UnrecoverableKeyException e)
+ {
+ throw e;
+ }
+ catch (Exception e)
+ {
+ StudioLogger.error(PackageFileSigner.class, "Error signing package", e);
+ throw new SignException(e.getMessage(), e);
+ }
+
+ }
+
+ /**
+ * Remove package signature files
+ *
+ * @param packageFile
+ * @throws IOException
+ */
+ public static void removePackageSignature(PackageFile packageFile) throws IOException
+ {
+ packageFile.removeMetaEntryFiles();
+ }
+
+ /**
+ * Generates the digests for all the files in the package and puts them in
+ * the manifest
+ *
+ * @param packageFile
+ * the package file being signed
+ * @param encoder
+ * the BASE64 encoder
+ * @param messageDigest
+ * the message digest
+ * @throws IOException
+ * if an I/O error occurs when reading the files contained in
+ * the package
+ */
+ private static void addFilesDigestsToManifest(PackageFile packageFile, Base64Encoder encoder,
+ MessageDigest messageDigest) throws IOException
+ {
+ InputStream fileInputStream = null;
+ ReadableByteChannel rc = null;
+ ByteArrayOutputStream encodedStream = null;
+
+ // for each entry in the package file
+ for (String entryName : packageFile.getEntryNames())
+ {
+ File file = packageFile.getEntryFile(entryName);
+ if (file.isFile())
+ {
+ try
+ {
+ // read the file contents
+ fileInputStream = new FileInputStream(file);
+ rc = Channels.newChannel(fileInputStream);
+ ByteBuffer byteBuffer = ByteBuffer.allocate((int) file.length());
+ rc.read(byteBuffer);
+
+ // compute the digest
+ messageDigest.reset();
+ byte[] digestedArray = messageDigest.digest(byteBuffer.array());
+
+ encodedStream = new ByteArrayOutputStream();
+ encoder.encode(digestedArray, 0, digestedArray.length, encodedStream);
+ String digestedMessage = encodedStream.toString();
+
+ // put the digest in the manifest file
+ Attributes jarEntryAttributes = new Attributes();
+ jarEntryAttributes.putValue(ISignConstants.SHA1_DIGEST, digestedMessage);
+ packageFile.getManifest().getEntries().put(entryName, jarEntryAttributes);
+ }
+ finally
+ {
+ try
+ {
+ if (encodedStream != null)
+ {
+ encodedStream.close();
+ }
+ if (rc != null)
+ {
+ rc.close();
+ }
+ if (fileInputStream != null)
+ {
+ fileInputStream.close();
+ }
+ }
+ catch (IOException e)
+ {
+ StudioLogger.error("Could not close stream while signing package. "
+ + e.getMessage());
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Adds the signature file and the signature block file to the package
+ *
+ * @param packageFile
+ * the package file being signed
+ * @param certificateAlias
+ * the signing certificate alias
+ * @param encoder
+ * the BASE64 encoder
+ * @param messageDigest
+ * the message digest
+ * @param createdBy
+ * Created-By manifest attribute
+ * @throws SignException
+ * if a processing error occurs during the signing process
+ * @throws IOException
+ * if an I/O error occurs during the signing process
+ * @throws NoSuchAlgorithmException
+ * @throws KeyStoreManagerException
+ * @throws KeyStoreException
+ * @throws UnrecoverableKeyException
+ */
+ private static void addSignatureFiles(PackageFile packageFile, IKeyStoreEntry keystoreEntry,
+ String keyEntryPassword, Base64Encoder encoder, String createdBy) throws IOException,
+ SignException, UnrecoverableKeyException, KeyStoreException, KeyStoreManagerException,
+ NoSuchAlgorithmException
+ {
+ // signature file
+ SignatureFile signatureFile =
+ new SignatureFile(packageFile, keystoreEntry.getAlias(), encoder, createdBy);
+
+ File sigFile = File.createTempFile(CertificateManagerActivator.TEMP_FILE_PREFIX, null);
+ FileOutputStream sigFileOutStream = null;
+
+ try
+ {
+ sigFileOutStream = new FileOutputStream(sigFile);
+ signatureFile.write(sigFileOutStream);
+ }
+ finally
+ {
+ if (sigFileOutStream != null)
+ {
+ try
+ {
+ sigFileOutStream.close();
+ }
+ catch (IOException e)
+ {
+ StudioLogger
+ .error("Could not close stream while adding signature files to package. "
+ + e.getMessage());
+ }
+ }
+ }
+
+ packageFile.setTempEntryFile(signatureFile.toString(), sigFile);
+
+ // signature block file
+ SignatureBlockFile signatureBlockFile =
+ new SignatureBlockFile(signatureFile, keystoreEntry, keyEntryPassword);
+
+ File sigBlockFile = File.createTempFile(CertificateManagerActivator.TEMP_FILE_PREFIX, null);
+ FileOutputStream sigBlockFileOutStream = null;
+
+ try
+ {
+ sigBlockFileOutStream = new FileOutputStream(sigBlockFile);
+ signatureBlockFile.write(sigBlockFileOutStream);
+ }
+ finally
+ {
+ if (sigBlockFileOutStream != null)
+ {
+ try
+ {
+ sigBlockFileOutStream.close();
+ }
+ catch (IOException e)
+ {
+ StudioLogger
+ .error("Could not close stream while adding signature files to package. "
+ + e.getMessage());
+ }
+ }
+ }
+
+ packageFile.setTempEntryFile(signatureBlockFile.toString(), sigBlockFile);
+ }
+}
diff --git a/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/packaging/sign/SignException.java b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/packaging/sign/SignException.java
new file mode 100644
index 0000000..901a420
--- /dev/null
+++ b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/packaging/sign/SignException.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.motorolamobility.studio.android.certmanager.packaging.sign;
+
+/**
+ * Signing exception
+ */
+@SuppressWarnings("serial")
+public class SignException extends Exception
+{
+ /**
+ * Creates a SignException with a detail message
+ *
+ * @param message the detail message
+ */
+ public SignException(String message)
+ {
+ super(message);
+ }
+
+ /**
+ * Creates a SignException with a detail message and a cause
+ *
+ * @param message the detail message
+ * @param cause the cause
+ */
+ public SignException(String message, Throwable cause)
+ {
+ super(message, cause);
+ }
+}
diff --git a/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/packaging/sign/SignatureBlockFile.java b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/packaging/sign/SignatureBlockFile.java
new file mode 100644
index 0000000..f8fdcad
--- /dev/null
+++ b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/packaging/sign/SignatureBlockFile.java
@@ -0,0 +1,264 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.studio.android.certmanager.packaging.sign;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.math.BigInteger;
+import java.security.InvalidKeyException;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.Signature;
+import java.security.SignatureException;
+import java.security.UnrecoverableKeyException;
+import java.security.cert.X509Certificate;
+
+import javax.security.auth.x500.X500Principal;
+
+import sun.security.pkcs.ContentInfo;
+import sun.security.pkcs.PKCS7;
+import sun.security.pkcs.SignerInfo;
+import sun.security.x509.AlgorithmId;
+import sun.security.x509.X500Name;
+
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorolamobility.studio.android.certmanager.CertificateManagerActivator;
+import com.motorolamobility.studio.android.certmanager.exception.KeyStoreManagerException;
+import com.motorolamobility.studio.android.certmanager.ui.model.IKeyStoreEntry;
+
+/**
+ * This class implements the signature block file from jar mechanism used in packaging
+ */
+public class SignatureBlockFile
+{
+
+ /**
+ * the signature file
+ */
+ private SignatureFile signatureFile;
+
+ /**
+ * A certificate from keystore
+ */
+ private IKeyStoreEntry keystoreEntry;
+
+ /**
+ * The password of the certificate.
+ */
+ private String keyEntryPassword;
+
+ /**
+ * Default Constructor
+ *
+ * @param signatureFile the signature file
+ * @param alias the certificate alias
+ */
+ public SignatureBlockFile(SignatureFile signatureFile, IKeyStoreEntry keystoreEntry,
+ String keyEntryPassword)
+ {
+ this.keyEntryPassword = keyEntryPassword;
+ this.keystoreEntry = keystoreEntry;
+ this.signatureFile = signatureFile;
+ }
+
+ /**
+ * To string method override
+ *
+ * @return the signature block file name with relative path from root. Frequently META-INF/alias.RSA or .DSA
+ */
+ @Override
+ public String toString()
+ {
+ String result = new String();
+ try
+ {
+ result =
+ new StringBuilder(CertificateManagerActivator.METAFILES_DIR)
+ .append(CertificateManagerActivator.JAR_SEPARATOR)
+ .append(ISignConstants.SIGNATURE_FILE_NAME).append(".")
+ .append(getBlockAlgorithm()).toString();
+ }
+ catch (UnrecoverableKeyException e)
+ {
+ StudioLogger.error("Could not generate signature block file name.");
+ }
+ catch (KeyStoreException e)
+ {
+ StudioLogger.error("Could not generate signature block file name.");
+ }
+ catch (NoSuchAlgorithmException e)
+ {
+ StudioLogger.error("Could not generate signature block file name.");
+ }
+ catch (KeyStoreManagerException e)
+ {
+ StudioLogger.error("Could not generate signature block file name.");
+ }
+
+ return result;
+ }
+
+ /**
+ * Gets the block file algorithm
+ *
+ * @return the signature block file algorithm to be used
+ * @throws KeyStoreManagerException
+ * @throws NoSuchAlgorithmException
+ * @throws KeyStoreException
+ * @throws UnrecoverableKeyException
+ * @throws
+ */
+ private String getBlockAlgorithm() throws UnrecoverableKeyException, KeyStoreException,
+ NoSuchAlgorithmException, KeyStoreManagerException
+ {
+ return keystoreEntry.getKey(this.keyEntryPassword).getAlgorithm();
+ }
+
+ /**
+ * Writes this file to an output stream
+ *
+ * @param outputStream the output stream to write the file
+ * @throws IOException if an I/O error occurs during the signing process
+ * @throws SignException if a processing error occurs during the signing process
+ * @throws KeyStoreManagerException
+ * @throws KeyStoreException
+ * @throws UnrecoverableKeyException
+ * @throws NoSuchAlgorithmException
+ */
+ public void write(OutputStream outputStream) throws IOException, SignException,
+ UnrecoverableKeyException, KeyStoreException, KeyStoreManagerException,
+ NoSuchAlgorithmException
+ {
+ // get certificate from entry
+ X509Certificate[] certChain =
+ {
+ keystoreEntry.getX509Certificate()
+ };
+ if (certChain.length > 0)
+ {
+ //get some info from certificate as issuer, serial and algorithm
+ X500Principal issuer = certChain[0].getIssuerX500Principal();
+ String serial = certChain[0].getSerialNumber().toString();
+ String blockalgorithm = getBlockAlgorithm();
+
+ String digestAlgotithm = null;
+ // determine the algorithm to be used to cipher the block file
+ if (blockalgorithm.equalsIgnoreCase(ISignConstants.DSA))
+ {
+ digestAlgotithm = ISignConstants.SHA1;
+ }
+ else if (blockalgorithm.equalsIgnoreCase(ISignConstants.RSA))
+ {
+ digestAlgotithm = ISignConstants.MD5;
+ }
+ else
+ {
+ StudioLogger.error(SignatureBlockFile.class,
+ "Signing block algorithm not supported. Key algorithm must be DSA or RSA");
+
+ throw new SignException("Signing block algorithm not supported");
+ }
+
+ String signatureAlgorithm =
+ digestAlgotithm + ISignConstants.ALGORITHM_CONNECTOR + blockalgorithm;
+
+ AlgorithmId digestAlg;
+ try
+ {
+ digestAlg = AlgorithmId.get(digestAlgotithm);
+ AlgorithmId privateKeyAlg = AlgorithmId.get(blockalgorithm);
+ // write the certificate with signature file and cipher it
+ Signature sig = Signature.getInstance(signatureAlgorithm);
+ sig.initSign(keystoreEntry.getPrivateKey(this.keyEntryPassword));
+
+ PKCS7 block = null;
+ ByteArrayOutputStream baos = null;
+ ByteArrayOutputStream signature = null;
+
+ try
+ {
+ baos = new ByteArrayOutputStream();
+ signatureFile.write(baos);
+
+ ContentInfo contentInfo = new ContentInfo(ContentInfo.DATA_OID, null);
+ sig.update(baos.toByteArray());
+
+ signature = new ByteArrayOutputStream();
+ signature.write(sig.sign());
+
+ SignerInfo si =
+ new SignerInfo(new X500Name(issuer.getName()), new BigInteger(serial),
+ digestAlg, privateKeyAlg, signature.toByteArray());
+
+ AlgorithmId[] algs =
+ {
+ digestAlg
+ };
+ SignerInfo[] infos =
+ {
+ si
+ };
+
+ block = new PKCS7(algs, contentInfo, certChain, infos);
+ }
+ catch (IOException e)
+ {
+ StudioLogger.error(SignatureBlockFile.class,
+ "I/O error creating signature block file: " + e.getMessage());
+
+ throw new SignException("I/O error creating signature block file", e);
+ }
+ finally
+ {
+ if (baos != null)
+ {
+ baos.close();
+ }
+ if (signature != null)
+ {
+ signature.close();
+ }
+ }
+
+ // I/O exceptions below are thrown unmodified
+ block.encodeSignedData(outputStream);
+ }
+ catch (NoSuchAlgorithmException nsae)
+ {
+ StudioLogger.error(SignatureBlockFile.class, "Signing algorithm not supported: "
+ + nsae.getMessage());
+
+ throw new SignException("Signing algorithm not supported", nsae);
+ }
+ catch (InvalidKeyException ike)
+ {
+ StudioLogger.error(SignatureBlockFile.class,
+ "Invalid key when creating signature block file: " + ike.getMessage());
+
+ throw new SignException("Invalid key when creating signature block file", ike);
+ }
+ catch (SignatureException se)
+ {
+ StudioLogger.error(SignatureBlockFile.class,
+ "Signature error when creating signature block file: " + se.getMessage());
+
+ throw new SignException("Signature error creating signature block file", se);
+ }
+ }
+ StudioLogger.info(SignatureBlockFile.class, "Created signature block file");
+ }
+}
diff --git a/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/packaging/sign/SignatureFile.java b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/packaging/sign/SignatureFile.java
new file mode 100644
index 0000000..669cf70
--- /dev/null
+++ b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/packaging/sign/SignatureFile.java
@@ -0,0 +1,226 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.studio.android.certmanager.packaging.sign;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.jar.Attributes;
+import java.util.jar.Manifest;
+
+import org.bouncycastle.util.encoders.Base64Encoder;
+
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorolamobility.studio.android.certmanager.CertificateManagerActivator;
+import com.motorolamobility.studio.android.certmanager.packaging.PackageFile;
+
+/**
+ * This class implements the package signature file, that follows the jar
+ * signing process.
+ */
+public class SignatureFile
+{
+ /**
+ * The package file
+ */
+ private final PackageFile packageFile;
+
+ /**
+ * The base encoder
+ */
+ private Base64Encoder encoder = new Base64Encoder();
+
+ /**
+ * Manifest Created-By attribute
+ */
+ private final String createdBy;
+
+ /**
+ * Default Constructor
+ *
+ * @param packageFile
+ * the signed package file to be signed
+ * @param alias
+ * the certificate alias
+ * @param encoder
+ * the BASE64 encoder
+ * @param createdBy
+ * Created-By manifest attribute
+ */
+ public SignatureFile(PackageFile packageFile, String alias, Base64Encoder encoder,
+ String createdBy)
+ {
+ this.packageFile = packageFile;
+ this.encoder = encoder;
+ this.createdBy = createdBy;
+ }
+
+ /**
+ * Return the filename with relative path from root (normally
+ * META-INF/alias.SF).
+ */
+ @Override
+ public String toString()
+ {
+ return CertificateManagerActivator.METAFILES_DIR
+ + CertificateManagerActivator.JAR_SEPARATOR + ISignConstants.SIGNATURE_FILE_NAME
+ + ISignConstants.SIGNATURE_FILE_NAME_EXTENSION;
+
+ }
+
+ /**
+ * Writes this file to an output stream.
+ *
+ * @param outputStream
+ * the stream to write this file
+ * @throws IOException
+ * if an I/O error occurs during the signing process
+ * @throws SignException
+ * if a processing error occurs during the signing process
+ */
+ public void write(OutputStream outputStream) throws IOException, SignException
+ {
+ // the manifest file
+ Manifest manifestFile = this.packageFile.getManifest();
+
+ // the manifest digester
+ ManifestDigester manifestDigester = new ManifestDigester(manifestFile);
+
+ // the signature file to be constructed
+ Manifest signatureFile = new Manifest();
+
+ // the manifest digested main attributes
+ byte[] digestedMainAttributes = manifestDigester.getDigestedManifestMainAttributes();
+
+ // the digest of entire manifest
+ byte[] digestedManifest = manifestDigester.getDigestedManifest();
+
+ // put the required main attributes to a valid signature file
+ // (Version, CreatedBy, Main Attrib digest, Manifest digest)
+ Attributes signatureFileMainAtt = signatureFile.getMainAttributes();
+ signatureFileMainAtt.putValue(ISignConstants.SIGNATURE_VERSION_KEY,
+ ISignConstants.SIGNATURE_VERSION_VALUE);
+ signatureFileMainAtt.putValue(CertificateManagerActivator.CREATED_BY_FIELD, this.createdBy);
+
+ ByteArrayOutputStream stream = null;
+
+ try
+ {
+ stream = new ByteArrayOutputStream();
+ encoder.encode(digestedMainAttributes, 0, digestedMainAttributes.length, stream);
+ String encodedMainAttributesDigest = stream.toString();
+
+ stream.reset();
+ encoder.encode(digestedManifest, 0, digestedManifest.length, stream);
+ String encodedManifestDigest = stream.toString();
+
+ signatureFileMainAtt.putValue(ISignConstants.SHA1_DIGEST_MANIFEST_MAIN,
+ encodedMainAttributesDigest);
+ signatureFileMainAtt.putValue(ISignConstants.SHA1_DIGEST_MANIFEST,
+ encodedManifestDigest);
+ }
+ finally
+ {
+ if (stream != null)
+ {
+ try
+ {
+ stream.close();
+ }
+ catch (IOException e)
+ {
+ StudioLogger.error("Could not close stream writing signature file. "
+ + e.getMessage());
+ }
+ }
+ }
+
+ // calculate the digest from each entry of manifest
+ ByteArrayOutputStream baos = null;
+ try
+ {
+ baos = new ByteArrayOutputStream();
+ manifestFile.write(baos);
+
+ Map<String, Attributes> manifestEntries = manifestFile.getEntries();
+ Map<String, Attributes> signatureFileEntries = signatureFile.getEntries();
+ HashMap<String, ManifestEntry> entries = manifestDigester.getEntries();
+
+ for (String manifestEntryKey : manifestEntries.keySet())
+ {
+ ManifestEntry signatureFileEntry = entries.get(manifestEntryKey);
+
+ byte[] digestedArray = signatureFileEntry.digest();
+
+ ByteArrayOutputStream encodedStream = null;
+
+ try
+ {
+ encodedStream = new ByteArrayOutputStream();
+ this.encoder.encode(digestedArray, 0, digestedArray.length, encodedStream);
+
+ String digestedValue = encodedStream.toString();
+
+ Attributes signatureFileAtt = new Attributes();
+ signatureFileAtt.putValue(ISignConstants.SHA1_DIGEST, digestedValue);
+ signatureFileEntries.put(manifestEntryKey, signatureFileAtt);
+ }
+ finally
+ {
+ try
+ {
+ if (encodedStream != null)
+ {
+ encodedStream.close();
+ }
+ }
+ catch (IOException e)
+ {
+ StudioLogger.error("Could not close stream: " + e.getMessage());
+ }
+ }
+ }
+ }
+ catch (IOException e)
+ {
+ StudioLogger.error(SignatureFile.class,
+ "I/O error digesting manifest entries: " + e.getMessage());
+
+ throw new SignException("I/O error digesting manifest entries", e);
+ }
+ finally
+ {
+ try
+ {
+ if (baos != null)
+ {
+ baos.close();
+ }
+ }
+ catch (IOException e)
+ {
+ StudioLogger.error("Could not close stream: " + e.getMessage());
+ }
+ }
+
+ // I/O exceptions below are thrown unmodified
+ signatureFile.write(outputStream);
+
+ StudioLogger.info(SignatureFile.class, "Signature file was written");
+ }
+}
diff --git a/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/property/tester/TreeNodeTester.java b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/property/tester/TreeNodeTester.java
new file mode 100644
index 0000000..033dc01
--- /dev/null
+++ b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/property/tester/TreeNodeTester.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.studio.android.certmanager.property.tester;
+
+import org.eclipse.core.expressions.PropertyTester;
+
+import com.motorolamobility.studio.android.certmanager.ui.model.ITreeNode;
+
+/**
+ * Property testers are used via extension point to test object's state properties.
+ * This tester checks {@link ITreeNode} properties.
+ * */
+public class TreeNodeTester extends PropertyTester
+{
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.core.expressions.IPropertyTester#test(Object,String,Object[],Object)
+ */
+ @Override
+ public boolean test(Object receiver, String property, Object[] args, Object expectedValue)
+ {
+ boolean result = false;
+
+ if (receiver instanceof ITreeNode)
+ {
+ ITreeNode treeNode = (ITreeNode) receiver;
+ result =
+ treeNode.testAttribute(receiver,
+ ITreeNode.PROP_NAMESPACE.concat("." + property),
+ expectedValue.toString());
+ }
+
+ return result;
+ }
+
+}
diff --git a/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/action/PopupMenuActionDelegate.java b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/action/PopupMenuActionDelegate.java
new file mode 100644
index 0000000..ad2c4d2
--- /dev/null
+++ b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/action/PopupMenuActionDelegate.java
@@ -0,0 +1,224 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.motorolamobility.studio.android.certmanager.ui.action;
+
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.core.commands.IHandler;
+import org.eclipse.jface.action.IAction;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.ui.IObjectActionDelegate;
+import org.eclipse.ui.IWorkbenchPart;
+
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.common.utilities.EclipseUtils;
+import com.motorolamobility.studio.android.certmanager.command.BackupHandler;
+import com.motorolamobility.studio.android.certmanager.command.CertificatePropertiesHandler;
+import com.motorolamobility.studio.android.certmanager.command.ChangeKeyStoreTypeHandler;
+import com.motorolamobility.studio.android.certmanager.command.ChangePasswordKeyHandler;
+import com.motorolamobility.studio.android.certmanager.command.ChangePasswordKeystoreHandler;
+import com.motorolamobility.studio.android.certmanager.command.CreateKeyHandler;
+import com.motorolamobility.studio.android.certmanager.command.DeleteKeyHandler;
+import com.motorolamobility.studio.android.certmanager.command.DeleteKeystoreHandler;
+import com.motorolamobility.studio.android.certmanager.command.ImportKeyStoreEntriesHandler;
+import com.motorolamobility.studio.android.certmanager.command.RefreshHandler;
+import com.motorolamobility.studio.android.certmanager.command.SignExternalPackagesHandler;
+
+public class PopupMenuActionDelegate implements IObjectActionDelegate
+{
+
+ /**
+ * Enum type for ActionHandlers. If you need to add a new ActionHandler, just include
+ * a new type to this enum with the action id that you defined on your action extension point
+ */
+ enum ActionHandlers
+ {
+ SIGN_PACKAGE("com.motorolamobility.studio.android.certmanager.core.ui.action.addSignature") //$NON-NLS-1$
+ {
+ @Override
+ public IHandler getHandler()
+ {
+ return new SignExternalPackagesHandler();
+ }
+ },
+ BACKUP_KEYSTORE(
+ "com.motorolamobility.studio.android.certmanager.core.ui.action.backupKeystore") //$NON-NLS-1$
+ {
+ @Override
+ public IHandler getHandler()
+ {
+ //this handler will delegate the operation to another action that, in turn,
+ //will retrieve the current selection
+ //therefore, the parameter treeNode is not used here.
+ return new BackupHandler();
+ }
+
+ },
+ DELETE_KEYSTORE(
+ "com.motorolamobility.studio.android.certmanager.core.ui.action.deleteKeystore") //$NON-NLS-1$
+ {
+ @Override
+ public IHandler getHandler()
+ {
+ return new DeleteKeystoreHandler();
+ }
+
+ },
+ PROPERTIES_KEYSTORE(
+ "com.motorolamobility.studio.android.certmanager.core.ui.action.certificateProperties") //$NON-NLS-1$
+ {
+ @Override
+ public IHandler getHandler()
+ {
+ return new CertificatePropertiesHandler();
+ }
+
+ },
+ CHANGE_KEYSTORE_PASSWORD(
+ "com.motorolamobility.studio.android.certmanager.core.ui.action.changeKeystorePassword") //$NON-NLS-1$
+ {
+ @Override
+ public IHandler getHandler()
+ {
+ return new ChangePasswordKeystoreHandler();
+ }
+
+ },
+ CHANGE_KEY_PASSWORD(
+ "com.motorolamobility.studio.android.certmanager.core.ui.action.changeKeyPassword") //$NON-NLS-1$
+ {
+ @Override
+ public IHandler getHandler()
+ {
+ return new ChangePasswordKeyHandler();
+ }
+
+ },
+ CREATE_KEY("com.motorolamobility.studio.android.certmanager.core.ui.action.createKey") //$NON-NLS-1$
+ {
+ @Override
+ public IHandler getHandler()
+ {
+ return new CreateKeyHandler();
+ }
+
+ },
+ DELETE_KEY("com.motorolamobility.studio.android.certmanager.core.ui.action.deleteEntry") //$NON-NLS-1$
+ {
+ @Override
+ public IHandler getHandler()
+ {
+ return new DeleteKeyHandler();
+ }
+
+ },
+ REFRESH("com.motorolamobility.studio.android.certmanager.core.ui.action.refresh") //$NON-NLS-1$
+ {
+ @Override
+ public IHandler getHandler()
+ {
+ return new RefreshHandler();
+ }
+
+ },
+ CHANGE_KEYSTORE_TYPE(
+ "com.motorolamobility.studio.android.certmanager.core.ui.action.changeKeystoreType")
+ {
+ @Override
+ public IHandler getHandler()
+ {
+ return new ChangeKeyStoreTypeHandler();
+ }
+ },
+ IMPORT_KEYSTORE_ENTRIES(
+ "com.motorolamobility.studio.android.certmanager.core.ui.action.importKeystoreEntries")
+ {
+ @Override
+ public IHandler getHandler()
+ {
+ return new ImportKeyStoreEntriesHandler();
+ }
+ };
+
+ private final String actionId;
+
+ private ActionHandlers(String actionId)
+ {
+ this.actionId = actionId;
+ }
+
+ public abstract IHandler getHandler();
+
+ public static ActionHandlers getActionHandlerbyId(String id)
+ {
+
+ Object ret = null;
+ for (ActionHandlers h : ActionHandlers.values())
+ {
+ if (h.actionId.equals(id))
+ {
+ ret = h;
+ break;
+ }
+ }
+
+ return (ActionHandlers) ret;
+ }
+ }
+
+ @Override
+ public void run(IAction action)
+ {
+
+ ActionHandlers type = ActionHandlers.getActionHandlerbyId(action.getId());
+
+ IHandler handler = null;
+
+ if (type != null)
+ {
+ handler = type.getHandler();
+ }
+
+ if (handler != null)
+ {
+ ExecutionEvent event = new ExecutionEvent();
+ try
+ {
+ handler.execute(event);
+ }
+ catch (ExecutionException e)
+ {
+ StudioLogger.error(PopupMenuActionDelegate.class, e.getMessage(), e);
+ EclipseUtils.showErrorDialog("Execution error", e.getMessage());
+ }
+ }
+
+ }
+
+ @Override
+ public void selectionChanged(IAction action, ISelection selection)
+ {
+ //selection is retrieved by the handlers
+ }
+
+ @Override
+ public void setActivePart(IAction action, IWorkbenchPart targetPart)
+ {
+ //do nothing
+ }
+
+}
diff --git a/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/action/RemoveSignatureAction.java b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/action/RemoveSignatureAction.java
new file mode 100644
index 0000000..dc029cd
--- /dev/null
+++ b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/action/RemoveSignatureAction.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.studio.android.certmanager.ui.action;
+
+import org.eclipse.jface.action.Action;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.StructuredSelection;
+import org.eclipse.jface.wizard.WizardDialog;
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.PlatformUI;
+
+import com.motorolamobility.studio.android.certmanager.ui.wizards.RemoveExternalPackageSignatureWizard;
+
+/**
+ * This class gets the current workspace selection and calls the wizard to remove a package signature
+ */
+public class RemoveSignatureAction extends Action
+{
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.jface.action.Action#run()
+ */
+ @Override
+ public void run()
+ {
+ IWorkbench workbench = PlatformUI.getWorkbench();
+
+ if ((workbench != null) && !workbench.isClosing())
+ {
+
+ IWorkbenchWindow window = workbench.getActiveWorkbenchWindow();
+
+ if (window != null)
+ {
+ ISelection selection = window.getSelectionService().getSelection();
+ IStructuredSelection structureSelection = null;
+
+ if (selection instanceof IStructuredSelection)
+ {
+ structureSelection = (IStructuredSelection) selection;
+ }
+ else
+ {
+ structureSelection = new StructuredSelection();
+ }
+ WizardDialog dialog =
+ new WizardDialog(window.getShell(),
+ new RemoveExternalPackageSignatureWizard(structureSelection));
+ dialog.open();
+ }
+ }
+
+ }
+} \ No newline at end of file
diff --git a/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/action/SignCreatedPackageAction.java b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/action/SignCreatedPackageAction.java
new file mode 100644
index 0000000..c02b20a
--- /dev/null
+++ b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/action/SignCreatedPackageAction.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.studio.android.certmanager.ui.action;
+
+import org.eclipse.jface.action.Action;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.StructuredSelection;
+import org.eclipse.jface.wizard.WizardDialog;
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.PlatformUI;
+
+import com.motorolamobility.studio.android.certmanager.ui.model.IKeyStore;
+import com.motorolamobility.studio.android.certmanager.ui.model.IKeyStoreEntry;
+import com.motorolamobility.studio.android.certmanager.ui.wizards.SignExternalPackageWizard;
+
+/**
+ * This class gets the current workspace selection and calls the wizard to Sign an existent package
+ */
+public class SignCreatedPackageAction extends Action
+{
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.jface.action.Action#run()
+ */
+ @Override
+ public void run()
+ {
+ IWorkbench workbench = PlatformUI.getWorkbench();
+
+ if ((workbench != null) && !workbench.isClosing())
+ {
+
+ IWorkbenchWindow window = workbench.getActiveWorkbenchWindow();
+
+ if (window != null)
+ {
+ ISelection selection = window.getSelectionService().getSelection();
+ IStructuredSelection structureSelection = null;
+ IKeyStore selectedIKeyStore = null;
+ IKeyStoreEntry selectedEntry = null;
+ if (selection instanceof IStructuredSelection)
+ {
+ structureSelection = (IStructuredSelection) selection;
+ Object selectedItem = structureSelection.getFirstElement();
+ if (selectedItem instanceof IKeyStore)
+ {
+ selectedIKeyStore = (IKeyStore) selectedItem;
+ }
+ else if (selectedItem instanceof IKeyStoreEntry)
+ {
+ selectedEntry = (IKeyStoreEntry) selectedItem;
+ selectedIKeyStore = selectedEntry.getKeyStoreNode();
+ }
+ }
+ else
+ {
+ structureSelection = new StructuredSelection();
+ }
+ WizardDialog dialog =
+ new WizardDialog(window.getShell(), new SignExternalPackageWizard(
+ structureSelection, selectedIKeyStore, selectedEntry));
+ dialog.open();
+
+ }
+ }
+ }
+
+}
diff --git a/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/composite/CertificateBlock.java b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/composite/CertificateBlock.java
new file mode 100644
index 0000000..2e1f70e
--- /dev/null
+++ b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/composite/CertificateBlock.java
@@ -0,0 +1,495 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.studio.android.certmanager.ui.composite;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.FocusEvent;
+import org.eclipse.swt.events.FocusListener;
+import org.eclipse.swt.widgets.Combo;
+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.Shell;
+import org.eclipse.swt.widgets.Text;
+
+import com.motorola.studio.android.common.utilities.ui.Country;
+import com.motorola.studio.android.common.utilities.ui.ToolsCountries;
+import com.motorola.studio.android.common.utilities.ui.WidgetsFactory;
+import com.motorola.studio.android.common.utilities.ui.WidgetsUtil;
+import com.motorola.studio.android.wizards.elements.IBaseBlock;
+import com.motorolamobility.studio.android.certmanager.i18n.CertificateManagerNLS;
+
+/**
+ * This class shows the properties to for certificate.
+ */
+public class CertificateBlock implements IBaseBlock
+{
+ private static final String LABEL_DECORATOR = "*"; //$NON-NLS-1$
+
+ private static final int LONG_TEXT_SIZE = 256;
+
+ private static final int MEDIUM_TEXT_SIZE = 128;
+
+ private static final int SMALL_TEXT_SIZE = 64;
+
+ /**
+ * The attribute representing the window with which the user interacts.
+ */
+ private Shell shell;
+
+ /**
+ * Store the composite.
+ */
+ protected Composite parent;
+
+ /**
+ * The key pair alias text field.
+ */
+ private Text textAlias;
+
+ /**
+ * The common name text field.
+ */
+ private Text commonNameText;
+
+ /**
+ * The organization text field.
+ */
+ private Text textOrganization;
+
+ /**
+ * The organization unit text field.
+ */
+ private Text textOrganizationUnit;
+
+ /**
+ * The locality text field.
+ */
+ private Text textLocality;
+
+ /**
+ * The state text field.
+ */
+ private Text textState;
+
+ /**
+ * The country combo.
+ */
+ private Combo comboCountry;
+
+ private Label labelAlias;
+
+ private Control controlToFocus = null;
+
+ /**
+ * This listener is used to save the field that has the focus.
+ * */
+ protected FocusListener focusListener = new FocusListener()
+ {
+
+ @Override
+ public void focusLost(FocusEvent e)
+ {
+ //do nothing...
+ }
+
+ @Override
+ public void focusGained(FocusEvent e)
+ {
+ if (e.widget instanceof Control)
+ {
+ controlToFocus = (Control) e.widget;
+ }
+ }
+ };
+
+ /*
+ * (non-Javadoc)
+ * @seecom.motorola.studio.android.wizards.BaseWizard#
+ * createContentPlugin(org.eclipse.swt.widgets.Composite)
+ */
+ @Override
+ public Composite createContent(Composite parent)
+ {
+ Composite toReturn = WidgetsFactory.createComposite(parent, 1);
+
+ this.parent = parent;
+
+ createBasicInfoArea(toReturn);
+ createDetailedInfoArea(toReturn);
+ createCustomArea(toReturn);
+ setDefaultFocus();
+
+ return toReturn;
+ }
+
+ protected void createCustomArea(Composite parent)
+ {
+ //by default, do nothing
+ }
+
+ /**
+ * Creates Custom Fields to be appended in the properties block
+ * @param dnFieldsArea
+ */
+ protected void createCustomDetailedInfoArea(Composite dnFieldsArea)
+ {
+ //by default, do nothing
+ }
+
+ /**
+ * Create all fields of the distinguished name.
+ *
+ * @param parent The parent composite to add the created fields.
+ */
+ private void createDetailedInfoArea(Composite parent)
+ {
+ //Composite bottomComposite = WidgetsFactory.createComposite(parent);
+
+ Group detailsGroup =
+ WidgetsFactory.createTitledGroup(parent,
+ CertificateManagerNLS.CertificateBlock_DetailedInfoGroupTitle, 2);
+
+ // Creating the common name field
+ WidgetsFactory.createLabel(detailsGroup,
+ CertificateManagerNLS.CertificateBlock_FirstAndLastName + ":"); //$NON-NLS-1$
+ commonNameText = WidgetsFactory.createText(detailsGroup);
+ commonNameText.setTextLimit(MEDIUM_TEXT_SIZE);
+ commonNameText.addListener(SWT.Modify, this);
+ commonNameText.addFocusListener(focusListener);
+
+ // Creating the organization field
+ WidgetsFactory.createLabel(detailsGroup,
+ CertificateManagerNLS.CertificateBlock_Organization + ":"); //$NON-NLS-1$
+ textOrganization = WidgetsFactory.createText(detailsGroup);
+ textOrganization.setTextLimit(MEDIUM_TEXT_SIZE);
+ textOrganization.addListener(SWT.Modify, this);
+ textOrganization.addFocusListener(focusListener);
+
+ // Creating the organization unit field
+ WidgetsFactory.createLabel(detailsGroup,
+ CertificateManagerNLS.CertificateBlock_OrganizationUnit + ":"); //$NON-NLS-1$
+ textOrganizationUnit = WidgetsFactory.createText(detailsGroup);
+ textOrganizationUnit.setTextLimit(MEDIUM_TEXT_SIZE);
+ textOrganizationUnit.addListener(SWT.Modify, this);
+ textOrganizationUnit.addFocusListener(focusListener);
+
+ // Creating the locality field
+ WidgetsFactory.createLabel(detailsGroup,
+ CertificateManagerNLS.CertificateBlock_CityOrLocality + ":"); //$NON-NLS-1$
+ textLocality = WidgetsFactory.createText(detailsGroup);
+ textLocality.setTextLimit(LONG_TEXT_SIZE);
+ textLocality.addListener(SWT.Modify, this);
+ textLocality.addFocusListener(focusListener);
+
+ // Creating the state field
+ WidgetsFactory.createLabel(detailsGroup,
+ CertificateManagerNLS.CertificateBlock_StateOrProvince + ":"); //$NON-NLS-1$
+ textState = WidgetsFactory.createText(detailsGroup);
+ textState.setTextLimit(LONG_TEXT_SIZE);
+ textState.addListener(SWT.Modify, this);
+ textState.addFocusListener(focusListener);
+
+ // Creating the country field
+ WidgetsFactory.createLabel(detailsGroup, CertificateManagerNLS.CertificateBlock_CountryCode
+ + ":"); //$NON-NLS-1$
+ comboCountry = WidgetsFactory.createCombo(detailsGroup);
+ comboCountry.addListener(SWT.Selection, this);
+ comboCountry.addFocusListener(focusListener);
+ for (Country country : ToolsCountries.getInstance().getCountries())
+ {
+ this.comboCountry.add(country.getCountryName());
+ this.comboCountry.setData(country.getCountryName(), country.getCountryCode());
+ }
+ comboCountry.setVisibleItemCount(SMALL_TEXT_SIZE);
+
+ createCustomDetailedInfoArea(detailsGroup);
+ }
+
+ /**
+ * Create the key pair alias field.
+ *
+ * @param parent The parent composite to add the key pair alias field.
+ */
+ private void createBasicInfoArea(Composite parent)
+ {
+ Group groupBasicInfo =
+ WidgetsFactory.createTitledGroup(parent,
+ CertificateManagerNLS.CertificateBlock_BasicInfoGroupTitle, 2);
+
+ labelAlias =
+ WidgetsFactory.createLabel(groupBasicInfo,
+ CertificateManagerNLS.CertificateBlock_AliasName + ":"); //$NON-NLS-1$
+ textAlias = WidgetsFactory.createText(groupBasicInfo);
+ textAlias.setTextLimit(SMALL_TEXT_SIZE);
+ textAlias.addListener(SWT.Modify, this);
+ textAlias.addFocusListener(focusListener);
+
+ createCustomBasicInfoArea(groupBasicInfo);
+ }
+
+ /**
+ * Allow subclasses to add custom required fields to the certificate block.
+ * @param parent The parent composite for the custom required fields.
+ */
+ protected void createCustomBasicInfoArea(Composite parent)
+ {
+ //default implementation does nothing.
+ }
+
+ /**
+ * Decorate required fields using {@link CertificateBlock#decorateText(String)}. Always call the {@code super} implementation when overriding this method.
+ * <br/>
+ * Default required fields are:
+ * <ul>
+ * <li>Alias</li>
+ * </ul>
+ * */
+ protected void decorateRequiredFields()
+ {
+ labelAlias.setText(decorateText(labelAlias.getText()));
+ }
+
+ /**
+ * Decorate required fields by prefixing it with {@link CertificateBlock#LABEL_DECORATOR}.
+ *
+ * @return The decorated text.
+ * */
+ protected String decorateText(String strToDecorate)
+ {
+ return LABEL_DECORATOR.concat(strToDecorate);
+ }
+
+ /**
+ * Obtains the key pair alias defined by user.
+ *
+ * @return The key pair alias.
+ */
+ public String getAlias()
+ {
+ return textAlias.getText().toLowerCase();
+ }
+
+ /**
+ * Obtains the common name defined by user.
+ *
+ * @return The common name.
+ */
+ public String getCommonName()
+ {
+ return commonNameText.getText();
+ }
+
+ /**
+ * Obtains the organization defined by user.
+ *
+ * @return The organization.
+ */
+ public String getOrganization()
+ {
+ return textOrganization.getText();
+ }
+
+ /**
+ * Obtains the organization unit defined by user.
+ *
+ * @return The organization unit.
+ */
+ public String getOrganizationUnit()
+ {
+ return textOrganizationUnit.getText();
+ }
+
+ /**
+ * Obtains the locality defined by user.
+ *
+ * @return The locality.
+ */
+ public String getLocality()
+ {
+ return textLocality.getText();
+ }
+
+ /**
+ * Obtains the state defined by user.
+ *
+ * @return The state.
+ */
+ public String getState()
+ {
+ return textState.getText();
+ }
+
+ /**
+ * Obtains the country defined by user.
+ *
+ * @return The country.
+ */
+ public String getCountry()
+ {
+ String result = new String(); //empty string
+
+ //ensure that there is something selected in the combo before indexing its item list
+ if (comboCountry.getSelectionIndex() >= 0)
+ {
+ result =
+ (String) comboCountry.getData(comboCountry.getItem(comboCountry
+ .getSelectionIndex()));
+ }
+ return result;
+ }
+
+ /**
+ * Returns true if there is no error message set, the alias is not empty and there is at least one detail field non empty.
+ * @see com.motorola.studio.platform.tools.common.ui.composite.BaseBlock#isPageComplete()
+ */
+ @Override
+ public boolean isPageComplete()
+ {
+ return (getErrorMessage() == null)
+ && !WidgetsUtil.isNullOrEmpty(this.textAlias)
+ && (!WidgetsUtil.isNullOrEmpty(this.commonNameText)
+ || !WidgetsUtil.isNullOrEmpty(this.textOrganization)
+ || !WidgetsUtil.isNullOrEmpty(this.textOrganizationUnit)
+ || !WidgetsUtil.isNullOrEmpty(this.textLocality)
+ || !WidgetsUtil.isNullOrEmpty(this.textState) || !WidgetsUtil
+ .isNullOrDeselected(this.comboCountry));
+ }
+
+ /*
+ * (non-Javadoc)
+ * @seecom.motorola.studio.platform.tools.common.ui.composite.BaseBlock#
+ * canFlipToNextPage()
+ */
+ @Override
+ public boolean canFlipToNextPage()
+ {
+ return (getErrorMessage() == null) && isPageComplete();
+ }
+
+ /*
+ * (non-Javadoc)
+ * @seecom.motorola.studio.platform.tools.common.ui.composite.BaseBlock#
+ * getErrorMessage()
+ */
+ @Override
+ public String getErrorMessage()
+ {
+ String toReturn = null;
+
+ if (WidgetsUtil.isNullOrEmpty(textAlias))
+ {
+ toReturn =
+ CertificateManagerNLS.bind(CertificateManagerNLS.CertificateBlock_FieldIsEmpty,
+ CertificateManagerNLS.CertificateBlock_AliasName);
+ }
+ else if (WidgetsUtil.isNullOrEmpty(commonNameText)
+ && (WidgetsUtil.isNullOrEmpty(textOrganization))
+ && (WidgetsUtil.isNullOrEmpty(textOrganizationUnit))
+ && (WidgetsUtil.isNullOrEmpty(textLocality))
+ && (WidgetsUtil.isNullOrEmpty(textState))
+ && (WidgetsUtil.isNullOrDeselected(comboCountry)))
+ {
+ toReturn = CertificateManagerNLS.CertificateBlock_DetailedInfoNonEmptyFieldsRestriction;
+ }
+
+ return toReturn;
+ }
+
+ public Composite createInfoBlock(Composite parent, String alias, String name,
+ String organization, String organizationUnit, String country, String state,
+ String locality)
+ {
+ Composite toReturn = createContent(parent);
+ textAlias.setText(alias);
+ commonNameText.setText(name);
+ textOrganization.setText(organization);
+ textOrganizationUnit.setText(organizationUnit);
+ textLocality.setText(locality);
+ textState.setText(state);
+ int i = 0;
+ boolean found = false;
+ while ((i < comboCountry.getItemCount()) && !found)
+ {
+ String id = (String) comboCountry.getData(comboCountry.getItem(i));
+ if (id.equalsIgnoreCase(country))
+ {
+ comboCountry.select(i);
+ found = true;
+ }
+ i++;
+ }
+
+ textAlias.setEditable(false);
+ commonNameText.setEditable(false);
+ textOrganization.setEditable(false);
+ textOrganizationUnit.setEditable(false);
+ textLocality.setEditable(false);
+ textState.setEditable(false);
+ if (!found)
+ {
+ comboCountry.add(CertificateManagerNLS.CertificateInfoDialog_NotAvailableProperty, 0);
+ comboCountry.select(0);
+ }
+ comboCountry.setEnabled(false);
+
+ return toReturn;
+ }
+
+ @Override
+ public void handleEvent(Event event)
+ {
+ if (this.parent != null)
+ {
+ this.parent.notifyListeners(event.type, event);
+ }
+ }
+
+ @Override
+ public void refresh()
+ {
+ // Empty
+ }
+
+ @Override
+ public void setShell(Shell shell)
+ {
+ this.shell = shell;
+ }
+
+ @Override
+ public Shell getShell()
+ {
+ return shell;
+ }
+
+ @Override
+ public void setDefaultFocus()
+ {
+ controlToFocus = textAlias;
+ }
+
+ @Override
+ public void setFocus()
+ {
+ if (controlToFocus != null)
+ {
+ controlToFocus.setFocus();
+ }
+ }
+}
diff --git a/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/composite/KeyPropertiesBlock.java b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/composite/KeyPropertiesBlock.java
new file mode 100644
index 0000000..f0b97f6
--- /dev/null
+++ b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/composite/KeyPropertiesBlock.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.studio.android.certmanager.ui.composite;
+
+import java.util.Date;
+
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Text;
+
+import com.motorola.studio.android.common.utilities.ui.WidgetsFactory;
+import com.motorolamobility.studio.android.certmanager.i18n.CertificateManagerNLS;
+
+/**
+ * This class shows the properties to for Android certificates / keys.
+ */
+public class KeyPropertiesBlock extends CertificateBlock
+{
+ private Label labelExpirationDate = null;
+
+ private Text textExpirationDate = null;
+
+ private Label labelCreationDate = null;
+
+ private Text textCreationDate = null;
+
+ @Override
+ protected void createCustomDetailedInfoArea(Composite parent)
+ {
+ labelCreationDate =
+ WidgetsFactory.createLabel(parent,
+ CertificateManagerNLS.CertificateBlock_CreationDate + ":"); //$NON-NLS-1$
+ textCreationDate = WidgetsFactory.createText(parent);
+
+ labelExpirationDate =
+ WidgetsFactory.createLabel(parent,
+ CertificateManagerNLS.CertificateBlock_ExpirationDate + ":"); //$NON-NLS-1$
+ textExpirationDate = WidgetsFactory.createText(parent);
+ }
+
+ public Composite createInfoBlock(Composite parent, String alias, String name,
+ String organization, String organizationUnit, String country, String state,
+ String locality, Date validity, Date creationDate)
+ {
+ Composite toReturn =
+ super.createInfoBlock(parent, alias, name, organization, organizationUnit, country,
+ state, locality);
+ labelCreationDate.setText(CertificateManagerNLS.CertificateBlock_CreationDate + ":"); //$NON-NLS-1$
+ labelCreationDate.setVisible(true);
+ textCreationDate.setTextLimit(Text.LIMIT);
+ textCreationDate.setText(creationDate.toString());
+ textCreationDate.setEditable(false);
+
+ labelExpirationDate.setText(CertificateManagerNLS.CertificateBlock_ExpirationDate + ":"); //$NON-NLS-1$
+ textExpirationDate.setTextLimit(Text.LIMIT);
+ textExpirationDate.setText(validity.toString());
+ textExpirationDate.setEditable(false);
+
+ return toReturn;
+ }
+}
diff --git a/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/composite/NewKeyBlock.java b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/composite/NewKeyBlock.java
new file mode 100644
index 0000000..dfeea6f
--- /dev/null
+++ b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/composite/NewKeyBlock.java
@@ -0,0 +1,303 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.studio.android.certmanager.ui.composite;
+
+import java.util.Date;
+
+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.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Event;
+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 com.motorola.studio.android.common.utilities.ui.WidgetsFactory;
+import com.motorola.studio.android.common.utilities.ui.WidgetsUtil;
+import com.motorolamobility.studio.android.certmanager.core.KeyStoreManager;
+import com.motorolamobility.studio.android.certmanager.i18n.CertificateManagerNLS;
+import com.motorolamobility.studio.android.certmanager.ui.model.EntryNode;
+import com.motorolamobility.studio.android.certmanager.ui.model.IKeyStore;
+import com.motorolamobility.studio.android.certmanager.ui.wizards.CreateKeyWizardPage;
+
+/**
+ * This class shows the field to create a new Android certificates / keys.
+ */
+public class NewKeyBlock extends CertificateBlock
+{
+ private static final int VALIDITY_SIZE = 3;
+
+ private static final int SMALL_TEXT_SIZE = 64;
+
+ private CreateKeyWizardPage baseWizardPage;
+
+ private Label keyPasswordLabel;
+
+ private Label keyConfirmPasswordLabel;
+
+ private Text keyPassword;
+
+ private Text keyConfirmPassword;
+
+ private Button savePasswordCheckBox;
+
+ private Text textValidity = null;
+
+ private Label labelValidity = null;
+
+ private boolean canSavePassword = false;
+
+ private String keyPasswordText = new String();
+
+ private static final String DEFAULT_VALIDITY_IN_YEARS = "30"; //default //$NON-NLS-1$
+
+ @Override
+ public Composite createContent(Composite parent)
+ {
+ Composite composite = super.createContent(parent);
+
+ decorateRequiredFields();
+
+ return composite;
+ };
+
+ @Override
+ protected void createCustomDetailedInfoArea(Composite parent)
+ {
+ labelValidity =
+ WidgetsFactory.createLabel(parent, CertificateManagerNLS.CertificateBlock_Validity
+ + ":"); //$NON-NLS-1$
+ textValidity = WidgetsFactory.createText(parent);
+ textValidity.addListener(SWT.Modify, this);
+ textValidity.addFocusListener(focusListener);
+ textValidity.setTextLimit(VALIDITY_SIZE);
+ textValidity.setText(DEFAULT_VALIDITY_IN_YEARS);
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorolamobility.studio.android.certmanager.ui.composite.CertificateBlock#createCustomArea(org.eclipse.swt.widgets.Composite)
+ */
+ @Override
+ protected void createCustomArea(Composite parent)
+ {
+ Group passwordGroup =
+ WidgetsFactory.createTitledGroup(parent,
+ CertificateManagerNLS.NewKeyBlock_PasswordGroupTitle, 2);
+
+ //KEYSTORE PASSWORD SECTION
+ keyPasswordLabel = new Label(passwordGroup, SWT.NONE);
+ keyPasswordLabel.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false, 1, 1));
+ keyPasswordLabel.setText(CertificateManagerNLS.CertificateBlock_KeyPassword_Label + ":"); //$NON-NLS-2$ //$NON-NLS-1$
+
+ keyPassword = new Text(passwordGroup, SWT.SINGLE | SWT.BORDER | SWT.PASSWORD);
+ keyPassword.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));
+ keyPassword.setTextLimit(SMALL_TEXT_SIZE);
+ keyPassword.addListener(SWT.Modify, new Listener()
+ {
+ @Override
+ public void handleEvent(Event event)
+ {
+ keyPasswordText = keyPassword.getText();
+ NewKeyBlock.super.handleEvent(event);
+ }
+ });
+
+ keyPassword.addFocusListener(focusListener);
+
+ //CONFIRM PASSWORD SECTION
+ keyConfirmPasswordLabel = new Label(passwordGroup, SWT.NONE);
+ keyConfirmPasswordLabel
+ .setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false, 1, 1));
+ keyConfirmPasswordLabel
+ .setText(CertificateManagerNLS.CertificateBlock_ConfirmKeyPassword_Label + ":"); //$NON-NLS-2$ //$NON-NLS-1$
+
+ keyConfirmPassword = new Text(passwordGroup, SWT.SINGLE | SWT.BORDER | SWT.PASSWORD);
+ keyConfirmPassword.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));
+ keyConfirmPassword.addListener(SWT.Modify, this);
+ keyConfirmPassword.addFocusListener(focusListener);
+ keyConfirmPassword.setTextLimit(SMALL_TEXT_SIZE);
+
+ //Creates the save password checkbox
+ savePasswordCheckBox = new Button(passwordGroup, SWT.CHECK);
+ savePasswordCheckBox.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false, 2, 1));
+ savePasswordCheckBox.setText(CertificateManagerNLS.PasswordProvider_SaveThisPassword);
+ savePasswordCheckBox.setSelection(false);
+ savePasswordCheckBox.addFocusListener(focusListener);
+
+ if ((baseWizardPage != null) && (baseWizardPage.getKeystore() != null))
+ {
+ //we can not save password if keystore is not mapped in the view
+ savePasswordCheckBox.setVisible(KeyStoreManager.getInstance().isKeystoreMapped(
+ baseWizardPage.getKeystore().getFile()));
+ }
+ savePasswordCheckBox.addSelectionListener(new SelectionAdapter()
+ {
+ @Override
+ public void widgetSelected(SelectionEvent e)
+ {
+ //update according to check status
+ canSavePassword = savePasswordCheckBox.getSelection();
+ }
+
+ });
+
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorolamobility.studio.android.certmanager.ui.composite.CertificateBlock#decorateRequiredFields()
+ */
+ @Override
+ protected void decorateRequiredFields()
+ {
+ super.decorateRequiredFields();
+
+ labelValidity.setText(decorateText(labelValidity.getText()));
+ keyPasswordLabel.setText(decorateText(keyPasswordLabel.getText()));
+ keyConfirmPasswordLabel.setText(decorateText(keyConfirmPasswordLabel.getText()));
+ }
+
+ @Override
+ public boolean isPageComplete()
+ {
+ return (super.isPageComplete()) && !WidgetsUtil.isNullOrEmpty(this.textValidity)
+ && !WidgetsUtil.isNullOrEmpty(this.keyPassword)
+ && !WidgetsUtil.isNullOrEmpty(this.keyConfirmPassword);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @seecom.motorola.studio.platform.tools.common.ui.composite.BaseBlock#
+ * canFlipToNextPage()
+ */
+ @Override
+ public boolean canFlipToNextPage()
+ {
+ return super.canFlipToNextPage() && !WidgetsUtil.isNullOrEmpty(this.textValidity)
+ && !WidgetsUtil.isNullOrEmpty(this.keyPassword)
+ && !WidgetsUtil.isNullOrEmpty(this.keyConfirmPassword);
+ }
+
+ public Composite createInfoBlock(Composite parent, String alias, String name,
+ String organization, String organizationUnit, String country, String state,
+ String locality, Date validity, Date creationDate)
+ {
+ Composite toReturn =
+ super.createInfoBlock(parent, alias, name, organization, organizationUnit, country,
+ state, locality);
+ labelValidity.setText(CertificateManagerNLS.CertificateBlock_ExpirationDate + ":"); //$NON-NLS-1$
+ textValidity.setTextLimit(Text.LIMIT);
+ textValidity.setText(validity.toString());
+ textValidity.setEditable(false);
+
+ keyPasswordLabel.setVisible(false);
+ keyPassword.setVisible(false);
+ keyConfirmPasswordLabel.setVisible(false);
+ keyConfirmPassword.setVisible(false);
+ savePasswordCheckBox.setVisible(false);
+
+ return toReturn;
+ }
+
+ public String getKeyPassword()
+ {
+ return keyPasswordText;
+ }
+
+ public String getKeyConfirmPassword()
+ {
+ return keyConfirmPassword.getText();
+ }
+
+ public boolean needToSaveKeyPassword()
+ {
+ return canSavePassword;
+ }
+
+ @Override
+ public String getErrorMessage()
+ {
+ String message = super.getErrorMessage();
+
+ //if there is no error message on other items => check text validity field
+ if (message == null)
+ {
+ try
+ {
+ int validity = Integer.parseInt(textValidity.getText());
+ if (validity <= 0)
+ {
+ throw new NumberFormatException();
+ }
+ }
+ catch (NumberFormatException nfe)
+ {
+ message = CertificateManagerNLS.CertificateBlock_Validity_Error; //$NON-NLS-1$
+ }
+ }
+
+ //if there is no error message on other items => check password fields
+ if (message == null)
+ {
+ //password text and confirmation password text must match
+ if (!keyPassword.getText().equals(keyConfirmPassword.getText()))
+ {
+ message = CertificateManagerNLS.CreateKeystorePage_PasswordDoesNotMatch;
+ }
+ //check password size according to keytool specification
+ if (keyPassword.getText().length() < EntryNode.KEY_PASSWORD_MIN_SIZE)
+ {
+ message =
+ CertificateManagerNLS.bind(
+ CertificateManagerNLS.CreateKeystorePage_PasswordMinSizeMessage,
+ IKeyStore.KEYSTORE_PASSWORD_MIN_SIZE); //$NON-NLS-1$
+ }
+
+ }
+
+ return message;
+ }
+
+ /**
+ * Set messages (used to set information messages)
+ * @param message
+ * @param messageType IMessageProvider.INFORMATION
+ */
+ protected void setMessage(String message, int messageType)
+ {
+ if (baseWizardPage != null)
+ {
+ baseWizardPage.setMessage(message, messageType);
+ }
+ }
+
+ public String getValidity()
+ {
+ return textValidity.getText();
+ }
+
+ /**
+ * @param baseWizardPage the baseWizardPage to set
+ */
+ public void setBaseWizardPage(CreateKeyWizardPage baseWizardPage)
+ {
+ this.baseWizardPage = baseWizardPage;
+ }
+
+}
diff --git a/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/dialogs/BackupContentProvider.java b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/dialogs/BackupContentProvider.java
new file mode 100644
index 0000000..ec916ea
--- /dev/null
+++ b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/dialogs/BackupContentProvider.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.studio.android.certmanager.ui.dialogs;
+
+import java.util.List;
+
+import org.eclipse.jface.viewers.IStructuredContentProvider;
+import org.eclipse.jface.viewers.Viewer;
+
+public class BackupContentProvider implements IStructuredContentProvider
+{
+
+ @Override
+ public void dispose()
+ {
+ //do nothing
+ }
+
+ @Override
+ public void inputChanged(Viewer viewer, Object oldInput, Object newInput)
+ {
+ //do nothing
+ }
+
+ @Override
+ public Object[] getElements(Object inputElement)
+ {
+ Object[] elements = null;
+ if (inputElement instanceof List)
+ {
+ elements = ((List<?>) inputElement).toArray();
+ }
+ else if (inputElement instanceof String[])
+ {
+ elements = (String[]) inputElement;
+ }
+ return elements;
+ }
+
+} \ No newline at end of file
diff --git a/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/dialogs/BackupDialog.java b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/dialogs/BackupDialog.java
new file mode 100644
index 0000000..65cd6ed
--- /dev/null
+++ b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/dialogs/BackupDialog.java
@@ -0,0 +1,400 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.studio.android.certmanager.ui.dialogs;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.core.runtime.Path;
+import org.eclipse.jface.dialogs.IMessageProvider;
+import org.eclipse.jface.dialogs.TitleAreaDialog;
+import org.eclipse.jface.viewers.CheckStateChangedEvent;
+import org.eclipse.jface.viewers.CheckboxTableViewer;
+import org.eclipse.jface.viewers.IBaseLabelProvider;
+import org.eclipse.jface.viewers.ICheckStateListener;
+import org.eclipse.jface.viewers.IContentProvider;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.ModifyEvent;
+import org.eclipse.swt.events.ModifyListener;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.FileDialog;
+import org.eclipse.swt.widgets.Group;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.TableItem;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.ui.PlatformUI;
+
+import com.motorola.studio.android.common.utilities.EclipseUtils;
+import com.motorola.studio.android.common.utilities.FileUtil;
+import com.motorolamobility.studio.android.certmanager.CertificateManagerActivator;
+import com.motorolamobility.studio.android.certmanager.i18n.CertificateManagerNLS;
+import com.motorolamobility.studio.android.certmanager.ui.model.IKeyStore;
+import com.motorolamobility.studio.android.certmanager.ui.model.ITreeNode;
+
+public class BackupDialog extends TitleAreaDialog
+{
+
+ private static final String ZIP_EXT = ".zip"; //$NON-NLS-1$
+
+ private static final String WIZARD_BANNER = "icons/wizban/backup_keystore_wiz.png"; //$NON-NLS-1$
+
+ private static final String BACKUP_KEYSTORE_HELP_ID = CertificateManagerActivator.PLUGIN_ID
+ + ".backup_keystore"; //$NON-NLS-1$;
+
+ private final IContentProvider contentProvider;
+
+ private final IBaseLabelProvider labelProvider;
+
+ private CheckboxTableViewer tableViewer;
+
+ private Object input;
+
+ private final String title;
+
+ private File archiveFile;
+
+ private List<String> selectedKeyStores;
+
+ private String archivePath;
+
+ private boolean verifyOverwrite;
+
+ private Button selectAllButton;
+
+ public BackupDialog(Shell parentShell)
+ {
+ super(parentShell);
+ setShellStyle(getShellStyle() | SWT.RESIZE);
+ this.contentProvider = new BackupContentProvider();
+ this.labelProvider = new BackupLabelProvider();
+ this.title = CertificateManagerNLS.BackupDialog_Diag_Title;
+ this.archivePath = ""; //$NON-NLS-1$
+ selectedKeyStores = new ArrayList<String>();
+ setTitleImage(CertificateManagerActivator.imageDescriptorFromPlugin(
+ CertificateManagerActivator.PLUGIN_ID, WIZARD_BANNER).createImage());
+ }
+
+ @Override
+ protected Control createDialogArea(Composite parent)
+ {
+ getShell().setText(title);
+ setTitle(CertificateManagerNLS.BackupDialog_DialogTitle);
+ //the shell has the same help as its page
+ PlatformUI.getWorkbench().getHelpSystem()
+ .setHelp(getShell(), BackupDialog.BACKUP_KEYSTORE_HELP_ID);
+
+ Composite dialogArea = new Composite(parent, SWT.FILL);
+ dialogArea.setLayout(new GridLayout());
+ dialogArea.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+
+ Group pathGroup = new Group(dialogArea, SWT.SHADOW_NONE);
+ pathGroup.setText(CertificateManagerNLS.BackupDialog_Backup_File);
+ pathGroup.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+ pathGroup.setLayout(new GridLayout(3, false));
+
+ Label pathLabel = new Label(pathGroup, SWT.NONE);
+ pathLabel.setText(CertificateManagerNLS.BackupDialog_Path);
+ pathLabel.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false));
+
+ final Text pathText = new Text(pathGroup, SWT.BORDER);
+ pathText.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
+ pathText.addModifyListener(new ModifyListener()
+ {
+
+ @Override
+ public void modifyText(ModifyEvent e)
+ {
+ archivePath = pathText.getText();
+ archiveFile = new File(archivePath);
+ verifyOverwrite = true;
+ validate();
+ }
+ });
+
+ Button browseButton = new Button(pathGroup, SWT.PUSH);
+ browseButton.setText(CertificateManagerNLS.BackupDialog_Browse);
+ browseButton.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false));
+ browseButton.addSelectionListener(new SelectionAdapter()
+ {
+ @Override
+ public void widgetSelected(SelectionEvent e)
+ {
+ FileDialog fileDialog = new FileDialog(getShell(), SWT.SAVE);
+ fileDialog.setFilterExtensions(new String[]
+ {
+ "*" + ZIP_EXT //$NON-NLS-1$
+ });
+ fileDialog.setOverwrite(true);
+ String choosenPath = fileDialog.open();
+ pathText.setText(choosenPath);
+ verifyOverwrite = false;
+ super.widgetSelected(e);
+ }
+ });
+
+ Group keystoresGroup = new Group(dialogArea, SWT.SHADOW_NONE);
+ keystoresGroup.setText(CertificateManagerNLS.BackupDialog_KeyStores);
+ keystoresGroup.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+ keystoresGroup.setLayout(new GridLayout(1, false));
+
+ tableViewer =
+ CheckboxTableViewer.newCheckList(keystoresGroup, SWT.H_SCROLL | SWT.V_SCROLL
+ | SWT.CHECK | SWT.BORDER);
+ tableViewer.setContentProvider(contentProvider);
+ tableViewer.setLabelProvider(labelProvider);
+ tableViewer.setInput(input);
+ selectKeystores();
+ GridData layoutData = new GridData(SWT.FILL, SWT.FILL, true, true);
+ layoutData.widthHint = 400;
+ layoutData.heightHint = 200;
+ tableViewer.getControl().setLayoutData(layoutData);
+ tableViewer.addCheckStateListener(new ICheckStateListener()
+ {
+ @Override
+ public void checkStateChanged(CheckStateChangedEvent event)
+ {
+ String keyStore = null;
+ Object element = event.getElement();
+ if (element instanceof String)
+ {
+ keyStore = (String) element;
+ }
+
+ if (keyStore != null)
+ {
+ if (event.getChecked())
+ {
+ selectedKeyStores.add(keyStore);
+ }
+ else
+ {
+ selectedKeyStores.remove(keyStore);
+ }
+ }
+
+ updateSelectAllState();
+
+ validate();
+ }
+ });
+ Composite selectButtonArea = new Composite(keystoresGroup, SWT.NONE);
+ selectButtonArea.setLayout(new GridLayout(1, true));
+ selectButtonArea.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+ selectAllButton = new Button(selectButtonArea, SWT.CHECK);
+ selectAllButton.setText(CertificateManagerNLS.BackupDialog_Select_All);
+ selectAllButton.setLayoutData(new GridData(SWT.BEGINNING, SWT.CENTER, false, false));
+ selectAllButton.addSelectionListener(new SelectionAdapter()
+ {
+ @Override
+ public void widgetSelected(SelectionEvent e)
+ {
+ tableViewer.setAllChecked(selectAllButton.getSelection());
+ selectedKeyStores.clear();
+
+ for (Object element : tableViewer.getCheckedElements())
+ {
+ String keyStoreEl = (String) element;
+ selectedKeyStores.add(keyStoreEl);
+ }
+ validate();
+ super.widgetSelected(e);
+ }
+ });
+
+ updateSelectAllState();
+
+ setMessage(CertificateManagerNLS.BackupDialog_Default_Message);
+ return dialogArea;
+ }
+
+ private void updateSelectAllState()
+ {
+ if (tableViewer.getCheckedElements().length == tableViewer.getTable().getItems().length)
+ {
+ selectAllButton.setSelection(true);
+ }
+ else
+ {
+ selectAllButton.setSelection(false);
+ }
+ }
+
+ @Override
+ protected Control createButtonBar(Composite parent)
+ {
+ Control buttonBar = super.createButtonBar(parent);
+ getButton(OK).setEnabled(false);
+ return buttonBar;
+ }
+
+ @Override
+ protected void okPressed()
+ {
+ boolean canContinue = true;
+ if (verifyOverwrite && archiveFile.exists())
+ {
+ boolean canOvewrite =
+ EclipseUtils.showQuestionDialog(
+ CertificateManagerNLS.BackupDialog_Archive_Exists_Title, NLS.bind(
+ CertificateManagerNLS.BackupDialog_Archive_Exists_Message,
+ archiveFile));
+
+ if (!canOvewrite)
+ {
+ canContinue = false;
+ }
+ else
+ {
+ archiveFile.delete();
+ }
+ }
+
+ if (canContinue)
+ {
+ if (!FileUtil.canWrite(archiveFile))
+ {
+ EclipseUtils.showErrorDialog(
+ CertificateManagerNLS.BackupDialog_Fail_Writing_Archive_Title, NLS.bind(
+ CertificateManagerNLS.BackupDialog_Fail_Writing_Archive_Message,
+ archiveFile));
+
+ }
+ else
+ {
+ setErrorMessage(null);
+ super.okPressed();
+ }
+ }
+ }
+
+ private void validate()
+ {
+
+ boolean isValid = true;
+ boolean hasWarn = false;
+ Path path = new Path(archivePath);
+ try
+ {
+ if (archivePath.isEmpty() || !path.isValidPath(archiveFile.getCanonicalPath()))
+ {
+ setErrorMessage(CertificateManagerNLS.BackupDialog_Invalid_Destination_Title);
+ isValid = false;
+ }
+ }
+ catch (IOException e)
+ {
+ setErrorMessage(CertificateManagerNLS.BackupDialog_Invalid_Destination_Title);
+ isValid = false;
+ }
+
+ if (isValid)
+ {
+ if (archiveFile.exists() && (archiveFile.isDirectory()))
+ {
+ setErrorMessage(CertificateManagerNLS.BackupDialog_Invalid_Destination_Message);
+ isValid = false;
+ }
+ }
+
+ if (isValid)
+ {
+ if (!archiveFile.isAbsolute())
+ {
+ setMessage(
+ NLS.bind(CertificateManagerNLS.BackupDialog_Non_Absolute_Path,
+ archiveFile.getAbsolutePath()), IMessageProvider.WARNING);
+ hasWarn = true;
+ }
+ }
+
+ if (isValid)
+ {
+ if (selectedKeyStores.isEmpty())
+ {
+ setErrorMessage(CertificateManagerNLS.BackupDialog_Select_KeyStore);
+ isValid = false;
+ }
+ else
+ {
+ setErrorMessage(null);
+ if (!hasWarn)
+ {
+ setMessage(CertificateManagerNLS.BackupDialog_Default_Message);
+ }
+ isValid = true;
+ }
+ }
+
+ getButton(OK).setEnabled(isValid);
+ }
+
+ public void setInput(Object input)
+ {
+ if (tableViewer != null)
+ {
+ tableViewer.setInput(input);
+ }
+ this.input = input;
+ }
+
+ /**
+ * @return the Archive file to be created
+ */
+ public File getArchiveFile()
+ {
+ return archiveFile.getName().toLowerCase().endsWith(ZIP_EXT) ? archiveFile : new File(
+ archivePath + ZIP_EXT);
+ }
+
+ /**
+ * @return the Keystores file paths to be archived
+ */
+ public List<String> getSelectedKeyStores()
+ {
+ return selectedKeyStores;
+ }
+
+ public void selectKeyStores(List<ITreeNode> keystores)
+ {
+ for (ITreeNode treeNode : keystores)
+ {
+ if (treeNode instanceof IKeyStore)
+ {
+ IKeyStore keystore = (IKeyStore) treeNode;
+ selectedKeyStores.add(keystore.getFile().getAbsolutePath());
+ }
+ }
+ }
+
+ private void selectKeystores()
+ {
+ for (TableItem item : tableViewer.getTable().getItems())
+ {
+ item.setChecked(selectedKeyStores.contains(item.getText()));
+ }
+ }
+
+} \ No newline at end of file
diff --git a/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/dialogs/BackupLabelProvider.java b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/dialogs/BackupLabelProvider.java
new file mode 100644
index 0000000..6e4fa90
--- /dev/null
+++ b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/dialogs/BackupLabelProvider.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.studio.android.certmanager.ui.dialogs;
+
+import org.eclipse.jface.viewers.ILabelProvider;
+import org.eclipse.jface.viewers.ILabelProviderListener;
+import org.eclipse.swt.graphics.Image;
+
+public class BackupLabelProvider implements ILabelProvider
+{
+
+ @Override
+ public void addListener(ILabelProviderListener listener)
+ {
+ //do nothing
+ }
+
+ @Override
+ public void dispose()
+ {
+ //do nothing
+ }
+
+ @Override
+ public boolean isLabelProperty(Object element, String property)
+ {
+ return false;
+ }
+
+ @Override
+ public void removeListener(ILabelProviderListener listener)
+ {
+ //do nothing
+ }
+
+ @Override
+ public Image getImage(Object element)
+ {
+ return null;
+ }
+
+ @Override
+ public String getText(Object element)
+ {
+ String text = null;
+ if (element instanceof String)
+ {
+ text = (String) element;
+ }
+ return text;
+ }
+
+} \ No newline at end of file
diff --git a/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/dialogs/CertificateInfoDialog.java b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/dialogs/CertificateInfoDialog.java
new file mode 100644
index 0000000..53fbbf3
--- /dev/null
+++ b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/dialogs/CertificateInfoDialog.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.studio.android.certmanager.ui.dialogs;
+
+import java.security.cert.X509Certificate;
+
+import org.bouncycastle.asn1.x500.RDN;
+import org.bouncycastle.asn1.x500.X500Name;
+import org.bouncycastle.asn1.x500.style.BCStyle;
+import org.bouncycastle.cert.jcajce.JcaX509CertificateHolder;
+import org.eclipse.jface.dialogs.Dialog;
+import org.eclipse.jface.dialogs.IDialogConstants;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.ui.PlatformUI;
+
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.common.utilities.EclipseUtils;
+import com.motorolamobility.studio.android.certmanager.CertificateManagerActivator;
+import com.motorolamobility.studio.android.certmanager.i18n.CertificateManagerNLS;
+import com.motorolamobility.studio.android.certmanager.ui.composite.KeyPropertiesBlock;
+import com.motorolamobility.studio.android.certmanager.ui.model.EntryNode;
+
+/**
+ * Dialog to show certificate info.
+ */
+public class CertificateInfoDialog extends Dialog
+{
+ private static final String HELP_ID = CertificateManagerActivator.PLUGIN_ID
+ + ".certificate_info_dialog"; //$NON-NLS-1$
+
+ final private KeyPropertiesBlock block;
+
+ final private EntryNode entry;
+
+ public CertificateInfoDialog(Shell parentShell, KeyPropertiesBlock blk, EntryNode entry)
+ {
+ super(parentShell);
+ this.block = blk;
+ this.entry = entry;
+ }
+
+ @Override
+ protected Control createContents(Composite parent)
+ {
+ if (parent instanceof Shell)
+ {
+ ((Shell) parent).setText(CertificateManagerNLS.CertificateInfoDialog_ShellTitle);
+ PlatformUI.getWorkbench().getHelpSystem().setHelp(parent, HELP_ID);
+ }
+ return super.createContents(parent);
+ }
+
+ @Override
+ protected void createButtonsForButtonBar(Composite parent)
+ {
+ createButton(parent, IDialogConstants.OK_ID, IDialogConstants.OK_LABEL, true);
+ }
+
+ @Override
+ protected Control createDialogArea(Composite parent)
+ {
+ Composite newComposite = (Composite) super.createDialogArea(parent);
+ X509Certificate cert = null;
+
+ try
+ {
+ cert = entry.getX509Certificate();
+
+ if (cert != null)
+ {
+ X500Name x500name = new JcaX509CertificateHolder(cert).getSubject();
+ RDN commonName =
+ x500name.getRDNs(BCStyle.CN).length >= 1 ? x500name.getRDNs(BCStyle.CN)[0]
+ : null;
+ RDN organization =
+ x500name.getRDNs(BCStyle.O).length >= 1 ? x500name.getRDNs(BCStyle.O)[0]
+ : null;
+ RDN organizationUnit =
+ x500name.getRDNs(BCStyle.OU).length >= 1 ? x500name.getRDNs(BCStyle.OU)[0]
+ : null;
+ RDN country =
+ x500name.getRDNs(BCStyle.C).length >= 1 ? x500name.getRDNs(BCStyle.C)[0]
+ : null;
+ RDN state =
+ x500name.getRDNs(BCStyle.ST).length >= 1 ? x500name.getRDNs(BCStyle.ST)[0]
+ : null;
+ RDN locality =
+ x500name.getRDNs(BCStyle.L).length >= 1 ? x500name.getRDNs(BCStyle.L)[0]
+ : null;
+
+ block.createInfoBlock(newComposite, entry.getAlias(), printCertInfo(commonName),
+ printCertInfo(organization), printCertInfo(organizationUnit),
+ printCertInfo(country), printCertInfo(state), printCertInfo(locality),
+ cert.getNotAfter(), cert.getNotBefore());
+ }
+ else
+ {
+ //not found Android certificate expected (X509Certificate)
+ EclipseUtils
+ .showErrorDialog(
+ CertificateManagerNLS.CertificateInfoDialog_UnknownCertificateKeypairType,
+ CertificateManagerNLS.CertificatePropertiesHandler_ErrorGettingCertificateOrKeypairProperties);
+ }
+ }
+ catch (Exception e)
+ {
+ EclipseUtils
+ .showErrorDialog(
+ CertificateManagerNLS.CertificatePropertiesHandler_ErrorGettingCertificateOrKeypairProperties,
+ e.getMessage());
+ StudioLogger
+ .error(CertificateInfoDialog.class,
+ CertificateManagerNLS.CertificatePropertiesHandler_ErrorGettingCertificateOrKeypairProperties,
+ e);
+ }
+ return newComposite;
+ }
+
+ private String printCertInfo(RDN certItem)
+ {
+ return certItem != null ? certItem.getFirst().getValue().toString()
+ : CertificateManagerNLS.CertificateInfoDialog_NotAvailableProperty;
+ }
+}
diff --git a/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/dialogs/RestoreBackupDialog.java b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/dialogs/RestoreBackupDialog.java
new file mode 100644
index 0000000..64f364c
--- /dev/null
+++ b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/dialogs/RestoreBackupDialog.java
@@ -0,0 +1,437 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.studio.android.certmanager.ui.dialogs;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+
+import org.eclipse.jface.dialogs.TitleAreaDialog;
+import org.eclipse.jface.viewers.CheckStateChangedEvent;
+import org.eclipse.jface.viewers.CheckboxTableViewer;
+import org.eclipse.jface.viewers.IBaseLabelProvider;
+import org.eclipse.jface.viewers.ICheckStateListener;
+import org.eclipse.jface.viewers.IContentProvider;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.ModifyEvent;
+import org.eclipse.swt.events.ModifyListener;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.DirectoryDialog;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.FileDialog;
+import org.eclipse.swt.widgets.Group;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.ui.PlatformUI;
+
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorolamobility.studio.android.certmanager.CertificateManagerActivator;
+import com.motorolamobility.studio.android.certmanager.command.BackupHandler;
+import com.motorolamobility.studio.android.certmanager.i18n.CertificateManagerNLS;
+
+public class RestoreBackupDialog extends TitleAreaDialog
+{
+
+ private static final String ZIP_EXT = "*.zip"; //$NON-NLS-1$
+
+ private static final String WIZARD_BANNER = "icons/wizban/restore_keystore_wiz.png"; //$NON-NLS-1$
+
+ public static final String RESTORE_KEYSTORE_HELP_ID = CertificateManagerActivator.PLUGIN_ID
+ + ".restore_keystore"; //$NON-NLS-1$
+
+ private final IContentProvider contentProvider;
+
+ private final IBaseLabelProvider labelProvider;
+
+ private CheckboxTableViewer tableViewer;
+
+ private final String title;
+
+ private File archiveFile;
+
+ private List<String> selectedKeyStores;
+
+ private File destinationFile;
+
+ private Button selectAllButton;
+
+ private String destinationPath = ""; //$NON-NLS-1$
+
+ public RestoreBackupDialog(Shell parentShell)
+ {
+ super(parentShell);
+ setShellStyle(getShellStyle() | SWT.RESIZE);
+ this.contentProvider = new BackupContentProvider();
+ this.labelProvider = new BackupLabelProvider();
+ this.title = CertificateManagerNLS.RestoreBackupDialog_Dialog_Title;
+ selectedKeyStores = new ArrayList<String>();
+ setTitleImage(CertificateManagerActivator.imageDescriptorFromPlugin(
+ CertificateManagerActivator.PLUGIN_ID, WIZARD_BANNER).createImage());
+ }
+
+ @Override
+ protected Control createDialogArea(Composite parent)
+ {
+ getShell().setText(title);
+ setTitle(CertificateManagerNLS.RestoreBackupDialog_TitleArea_Message);
+
+ PlatformUI.getWorkbench().getHelpSystem()
+ .setHelp(getShell(), RestoreBackupDialog.RESTORE_KEYSTORE_HELP_ID);
+
+ Composite dialogArea = new Composite(parent, SWT.FILL);
+ dialogArea.setLayout(new GridLayout());
+ dialogArea.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+
+ Group pathGroup = new Group(dialogArea, SWT.SHADOW_NONE);
+ pathGroup.setText(CertificateManagerNLS.RestoreBackupDialog_Path_Group);
+ pathGroup.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+ pathGroup.setLayout(new GridLayout(3, false));
+
+ Label pathLabel = new Label(pathGroup, SWT.NONE);
+ pathLabel.setText(CertificateManagerNLS.RestoreBackupDialog_BackUp_File);
+ pathLabel.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false));
+
+ final Text pathText = new Text(pathGroup, SWT.BORDER);
+ pathText.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
+ pathText.addModifyListener(new ModifyListener()
+ {
+
+ @Override
+ public void modifyText(ModifyEvent e)
+ {
+ archiveFile = new File(pathText.getText());
+ selectAllButton.setSelection(false);
+ validate();
+ loadArchiveEntries();
+ }
+ });
+
+ Button browseButton = new Button(pathGroup, SWT.PUSH);
+ browseButton.setText(CertificateManagerNLS.RestoreBackupDialog_Browse_Button);
+ browseButton.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false));
+ browseButton.addSelectionListener(new SelectionAdapter()
+ {
+ @Override
+ public void widgetSelected(SelectionEvent e)
+ {
+ FileDialog fileDialog = new FileDialog(getShell());
+ fileDialog.setFilterExtensions(new String[]
+ {
+ ZIP_EXT
+ });
+ fileDialog.setOverwrite(false);
+ String choosenPath = fileDialog.open();
+ pathText.setText(choosenPath);
+ super.widgetSelected(e);
+ }
+ });
+
+ Label destinPath = new Label(pathGroup, SWT.NONE);
+ destinPath.setText(CertificateManagerNLS.RestoreBackupDialog_Destination);
+ destinPath.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false));
+
+ final Text destinText = new Text(pathGroup, SWT.BORDER);
+ destinText.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
+ destinText.addModifyListener(new ModifyListener()
+ {
+
+ @Override
+ public void modifyText(ModifyEvent e)
+ {
+ destinationPath = destinText.getText();
+ destinationFile = new File(destinationPath);
+ validate();
+ }
+ });
+
+ Button destinBrowseButton = new Button(pathGroup, SWT.PUSH);
+ destinBrowseButton.setText(CertificateManagerNLS.RestoreBackupDialog_Browse_Button);
+ destinBrowseButton.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false));
+ destinBrowseButton.addSelectionListener(new SelectionAdapter()
+ {
+ @Override
+ public void widgetSelected(SelectionEvent e)
+ {
+ DirectoryDialog directoryDialog = new DirectoryDialog(getShell());
+ String choosenPath = directoryDialog.open();
+ destinText.setText(choosenPath);
+ super.widgetSelected(e);
+ }
+ });
+
+ Group keystoresGroup = new Group(dialogArea, SWT.SHADOW_NONE);
+ keystoresGroup.setText(CertificateManagerNLS.RestoreBackupDialog_KeyStores);
+ keystoresGroup.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+ keystoresGroup.setLayout(new GridLayout(1, false));
+
+ tableViewer =
+ CheckboxTableViewer.newCheckList(keystoresGroup, SWT.H_SCROLL | SWT.V_SCROLL
+ | SWT.CHECK | SWT.BORDER);
+ tableViewer.setContentProvider(contentProvider);
+ tableViewer.setLabelProvider(labelProvider);
+ GridData layoutData = new GridData(SWT.FILL, SWT.FILL, true, true);
+ layoutData.widthHint = 400;
+ layoutData.heightHint = 200;
+ tableViewer.getControl().setLayoutData(layoutData);
+ tableViewer.addCheckStateListener(new ICheckStateListener()
+ {
+ @Override
+ public void checkStateChanged(CheckStateChangedEvent event)
+ {
+ String keyStore = null;
+ Object element = event.getElement();
+ if (element instanceof String)
+ {
+ keyStore = (String) element;
+ }
+
+ if (keyStore != null)
+ {
+ if (event.getChecked())
+ {
+ selectedKeyStores.add(keyStore);
+ }
+ else
+ {
+ selectedKeyStores.remove(keyStore);
+ }
+ }
+
+ if (tableViewer.getCheckedElements().length == tableViewer.getTable().getItems().length)
+ {
+ selectAllButton.setSelection(true);
+ }
+ else
+ {
+ selectAllButton.setSelection(false);
+ }
+
+ validate();
+ }
+ });
+
+ Composite selectButtonArea = new Composite(keystoresGroup, SWT.NONE);
+ selectButtonArea.setLayout(new GridLayout(1, true));
+ selectButtonArea.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+ selectAllButton = new Button(selectButtonArea, SWT.CHECK);
+ selectAllButton.setText(CertificateManagerNLS.RestoreBackupDialog_Select_All);
+ selectAllButton.setLayoutData(new GridData(SWT.BEGINNING, SWT.CENTER, false, false));
+ selectAllButton.addSelectionListener(new SelectionAdapter()
+ {
+ @Override
+ public void widgetSelected(SelectionEvent e)
+ {
+ tableViewer.setAllChecked(selectAllButton.getSelection());
+ selectedKeyStores.clear();
+ for (Object element : tableViewer.getCheckedElements())
+ {
+ String keyStoreEl = (String) element;
+ selectedKeyStores.add(keyStoreEl);
+ }
+ validate();
+ super.widgetSelected(e);
+ }
+ });
+
+ setMessage(CertificateManagerNLS.RestoreBackupDialog_Default_Message);
+ return dialogArea;
+ }
+
+ protected void loadArchiveEntries()
+ {
+ Runnable loadRunnable = new Runnable()
+ {
+ @Override
+ public void run()
+ {
+ if (archiveFile.exists())
+ {
+ @SuppressWarnings("rawtypes")
+ final List[] holderArray = new List[1];
+
+ ZipFile zipFile = null;
+ try
+ {
+ zipFile = new ZipFile(archiveFile, ZipFile.OPEN_READ);
+ ArrayList<String> keyStores = new ArrayList<String>(zipFile.size());
+ holderArray[0] = keyStores;
+ Enumeration<? extends ZipEntry> entries = zipFile.entries();
+ while (entries.hasMoreElements())
+ {
+ ZipEntry zipEntry = entries.nextElement();
+ if (!zipEntry.getName().equalsIgnoreCase(
+ BackupHandler.KS_TYPES_FILENAME))
+ {
+ keyStores.add(zipEntry.getName());
+ }
+ }
+ Display.getDefault().syncExec(new Runnable()
+ {
+ @Override
+ public void run()
+ {
+ tableViewer.setInput(holderArray[0]);
+ }
+ });
+ }
+ catch (Exception e)
+ {
+ Display.getDefault().asyncExec(new Runnable()
+ {
+ @Override
+ public void run()
+ {
+ clearKeystoresTableViewer();
+ setErrorMessage(NLS
+ .bind(CertificateManagerNLS.RestoreBackupDialog_Error_Loading_Entries,
+ archiveFile));
+ getButton(OK).setEnabled(false);
+ }
+ });
+ }
+ finally
+ {
+ if (zipFile != null)
+ {
+ try
+ {
+ zipFile.close();
+ }
+ catch (IOException e)
+ {
+ StudioLogger
+ .error("Could not close stream while restoring backup. "
+ + e.getMessage());
+ }
+ }
+ }
+ }
+ }
+ };
+
+ Thread thread = new Thread(loadRunnable);
+ thread.start();
+ }
+
+ @Override
+ protected Control createButtonBar(Composite parent)
+ {
+ Control buttonBar = super.createButtonBar(parent);
+ getButton(OK).setEnabled(false);
+ return buttonBar;
+ }
+
+ private void validate()
+ {
+
+ boolean isValid = true;
+
+ if (!archiveFile.exists())
+ {
+ clearKeystoresTableViewer();
+ setErrorMessage(CertificateManagerNLS.RestoreBackupDialog_BackUpFile_Not_Exist);
+ isValid = false;
+ }
+ else
+ {
+ setErrorMessage(null);
+ isValid = true;
+ }
+
+ if (isValid)
+ {
+ if (destinationPath.isEmpty())
+ {
+ setErrorMessage(CertificateManagerNLS.RestoreBackupDialog_Invalid_Dest_Path);
+ isValid = false;
+ }
+ else
+ {
+ if ((destinationFile != null) && destinationFile.isFile())
+ {
+ setErrorMessage(CertificateManagerNLS.RestoreBackupDialog_Invalid_Dest_Path);
+ isValid = false;
+ }
+ else
+ {
+ setErrorMessage(null);
+ isValid = true;
+ }
+ }
+ }
+
+ if (isValid)
+ {
+ if (selectedKeyStores.isEmpty())
+ {
+ setErrorMessage(CertificateManagerNLS.RestoreBackupDialog_Select_KeyStore);
+ isValid = false;
+ }
+ else
+ {
+ setErrorMessage(null);
+ isValid = true;
+ }
+ }
+
+ getButton(OK).setEnabled(isValid);
+ }
+
+ /**
+ * Remove all entries from keystores table viewer.
+ * */
+ private void clearKeystoresTableViewer()
+ {
+ tableViewer.setInput(null);
+ }
+
+ /**
+ * @return The back archive file
+ */
+ public File getArchiveFile()
+ {
+ return archiveFile;
+ }
+
+ /**
+ * @return The keystores to be restored
+ */
+ public List<String> getSelectedKeyStores()
+ {
+ return selectedKeyStores;
+ }
+
+ /**
+ * @return The destination directory
+ */
+ public File getDestinationDir()
+ {
+ return destinationFile;
+ }
+
+} \ No newline at end of file
diff --git a/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/dialogs/importks/ConvertKeyStoreTypeDialog.java b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/dialogs/importks/ConvertKeyStoreTypeDialog.java
new file mode 100644
index 0000000..aa697b4
--- /dev/null
+++ b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/dialogs/importks/ConvertKeyStoreTypeDialog.java
@@ -0,0 +1,633 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.studio.android.certmanager.ui.dialogs.importks;
+
+import java.io.File;
+import java.security.KeyStore.Entry;
+import java.security.KeyStore.PasswordProtection;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.jface.dialogs.IMessageProvider;
+import org.eclipse.jface.dialogs.TitleAreaDialog;
+import org.eclipse.jface.viewers.CellEditor;
+import org.eclipse.jface.viewers.ColumnLabelProvider;
+import org.eclipse.jface.viewers.ColumnViewer;
+import org.eclipse.jface.viewers.EditingSupport;
+import org.eclipse.jface.viewers.IStructuredContentProvider;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.jface.viewers.TableViewerColumn;
+import org.eclipse.jface.viewers.TextCellEditor;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.ModifyEvent;
+import org.eclipse.swt.events.ModifyListener;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Combo;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Group;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.swt.widgets.TableColumn;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.ui.PlatformUI;
+
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorolamobility.studio.android.certmanager.CertificateManagerActivator;
+import com.motorolamobility.studio.android.certmanager.core.KeyStoreManager;
+import com.motorolamobility.studio.android.certmanager.exception.InvalidPasswordException;
+import com.motorolamobility.studio.android.certmanager.exception.KeyStoreManagerException;
+import com.motorolamobility.studio.android.certmanager.i18n.CertificateManagerNLS;
+import com.motorolamobility.studio.android.certmanager.ui.model.IKeyStore;
+
+public class ConvertKeyStoreTypeDialog extends TitleAreaDialog
+{
+ public class EntryModel
+ {
+
+ private final String alias;
+
+ private String passwd;
+
+ private boolean verified;
+
+ public EntryModel(String alias)
+ {
+ this.alias = alias;
+ try
+ {
+ String savedPass = keyStore.getPasswordProvider().getPassword(alias, false);
+ setPasswd(savedPass != null ? savedPass : ""); //$NON-NLS-1$
+ }
+ catch (KeyStoreManagerException e)
+ {
+ setPasswd(""); //$NON-NLS-1$
+ }
+ }
+
+ @Override
+ public String toString()
+ {
+ return alias;
+ }
+
+ public String getPasswd()
+ {
+ return passwd;
+ }
+
+ public void setPasswd(String passwd)
+ {
+ this.passwd = passwd;
+ try
+ {
+ Entry entry =
+ keyStore.getKeyStore().getEntry(alias,
+ new PasswordProtection(passwd.toCharArray()));
+ setVerified(entry != null);
+ }
+ catch (Exception e)
+ {
+ setVerified(false);
+ }
+ aliaseMap.put(alias, passwd);
+ }
+
+ public String getAlias()
+ {
+ return alias;
+ }
+
+ public boolean isVerified()
+ {
+ return verified;
+ }
+
+ private void setVerified(boolean verified)
+ {
+ this.verified = verified;
+ validateUi();
+ }
+ }
+
+ public class EntriesContentProvider implements IStructuredContentProvider
+ {
+
+ @Override
+ public void dispose()
+ {
+ //do nothing
+ }
+
+ @Override
+ public void inputChanged(Viewer viewer, Object oldInput, Object newInput)
+ {
+ //do nothing
+ }
+
+ @Override
+ public Object[] getElements(Object inputElement)
+ {
+ List<EntryModel> modelList = null;
+ if (inputElement instanceof List<?>)
+ {
+ List<?> inputList = (List<?>) inputElement;
+ modelList = new ArrayList<EntryModel>(inputList.size());
+ Iterator<?> it = inputList.iterator();
+ while (it.hasNext())
+ {
+ Object element = it.next();
+ if (element instanceof String) //received an alias
+ {
+ String alias = (String) element;
+ EntryModel entryModel = new EntryModel(alias);
+ modelList.add(entryModel);
+ }
+ }
+
+ }
+ return modelList.toArray();
+ }
+
+ }
+
+ private final class PasswordEditingSupport extends EditingSupport
+ {
+ private PasswordEditingSupport(ColumnViewer viewer)
+ {
+ super(viewer);
+ }
+
+ @Override
+ protected boolean canEdit(Object element)
+ {
+ return true;
+ }
+
+ @Override
+ protected CellEditor getCellEditor(Object element)
+ {
+ return new TextCellEditor(entriesTable, SWT.PASSWORD);
+ }
+
+ @Override
+ protected Object getValue(Object element)
+ {
+ return ((EntryModel) element).getPasswd();
+ }
+
+ @Override
+ protected void setValue(Object element, Object value)
+ {
+ EntryModel model = (EntryModel) element;
+ model.setPasswd((String) value);
+ getViewer().update(element, null);
+ }
+ }
+
+ private static final String HELP_ID = CertificateManagerActivator.PLUGIN_ID
+ + ".convert_keystore_type"; //$NON-NLS-1$
+
+ private static final String WIZARD_BANNER = "icons/wizban/change_keystore_type_wiz.png"; //$NON-NLS-1$
+
+ private IKeyStore keyStore;
+
+ private String newType = ""; //$NON-NLS-1$
+
+ private Text passwdText;
+
+ private String password = ""; //$NON-NLS-1$
+
+ private Table entriesTable;
+
+ private TableViewer entriesTableViewer;
+
+ private final Map<String, String> aliaseMap = new HashMap<String, String>();
+
+ /**
+ * Create the dialog.
+ * @param parentShell
+ */
+ public ConvertKeyStoreTypeDialog(Shell parentShell, IKeyStore keyStore)
+ {
+ super(parentShell);
+ setShellStyle(SWT.DIALOG_TRIM | SWT.APPLICATION_MODAL);
+
+ this.keyStore = keyStore;
+ setTitleImage(CertificateManagerActivator.imageDescriptorFromPlugin(
+ CertificateManagerActivator.PLUGIN_ID, WIZARD_BANNER).createImage());
+ }
+
+ /**
+ * Create contents of the dialog.
+ * @param parent
+ */
+ @Override
+ protected Control createDialogArea(Composite parent)
+ {
+ parent.getShell().setText(CertificateManagerNLS.ConvertKeyStoreTypeDialog_DialogTitle);
+ setMessage(CertificateManagerNLS.ConvertKeyStoreTypeDialog_DefaultMessage);
+ setTitle(CertificateManagerNLS.ConvertKeyStoreTypeDialog_DialogTitle);
+ Composite area = (Composite) super.createDialogArea(parent);
+ Composite container = new Composite(area, SWT.NONE);
+ container.setLayout(new GridLayout(1, false));
+ container.setLayoutData(new GridData(GridData.FILL_BOTH));
+ Composite convertTopComposite = new Composite(container, SWT.NONE);
+ convertTopComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false, 1, 1));
+ convertTopComposite.setLayout(new GridLayout(1, false));
+
+ Composite keyStoreComposite = new Composite(convertTopComposite, SWT.NONE);
+ keyStoreComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1));
+ keyStoreComposite.setLayout(new GridLayout(3, false));
+
+ Label keyStoreLabel = new Label(keyStoreComposite, SWT.NONE);
+ keyStoreLabel.setText(CertificateManagerNLS.ConvertKeyStoreTypeDialog_KeyStoreLabel);
+
+ final Combo keyStoreCombo = new Combo(keyStoreComposite, SWT.READ_ONLY);
+ keyStoreCombo.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 2, 1));
+
+ final KeyStoreManager keyStoreManager = KeyStoreManager.getInstance();
+ try
+ {
+ List<IKeyStore> keyStores = keyStoreManager.getKeyStores();
+ for (IKeyStore keyStore : keyStores)
+ {
+ File ksFile = keyStore.getFile();
+ String comboItem = ksFile.getName() + " - " + ksFile.getAbsolutePath(); //$NON-NLS-1$
+ keyStoreCombo.add(comboItem);
+ keyStoreCombo.setData(comboItem, keyStore);
+ if (keyStore.equals(this.keyStore))
+ {
+ keyStoreCombo.select(keyStoreCombo.indexOf(comboItem));
+ }
+ }
+ }
+ catch (KeyStoreManagerException e1)
+ {
+ setErrorMessage(CertificateManagerNLS.ConvertKeyStoreTypeDialog_CouldNotLoad_Keystores_Error);
+ }
+
+ Label passwdLabel = new Label(keyStoreComposite, SWT.NONE);
+ passwdLabel.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1));
+ passwdLabel.setText(CertificateManagerNLS.ConvertKeyStoreTypeDialog_Password_Label);
+
+ passwdText = new Text(keyStoreComposite, SWT.BORDER | SWT.PASSWORD);
+ passwdText.addModifyListener(new ModifyListener()
+ {
+ @Override
+ public void modifyText(ModifyEvent e)
+ {
+ password = passwdText.getText();
+ }
+ });
+ passwdText.addSelectionListener(new SelectionAdapter()
+ {
+ @Override
+ public void widgetDefaultSelected(SelectionEvent e)
+ {
+ super.widgetDefaultSelected(e);
+ loadEntries();
+ }
+ });
+ passwdText.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));
+
+ Button loadButton = new Button(keyStoreComposite, SWT.NONE);
+ GridData gd_loadButton = new GridData(SWT.FILL, SWT.CENTER, false, false, 1, 1);
+ gd_loadButton.widthHint = 80;
+ loadButton.setLayoutData(gd_loadButton);
+ loadButton.setText(CertificateManagerNLS.ConvertKeyStoreTypeDialog_Load_Button);
+ loadButton.addSelectionListener(new SelectionAdapter()
+ {
+ @Override
+ public void widgetSelected(SelectionEvent e)
+ {
+ loadEntries();
+ }
+ });
+
+ Composite composite_1 = new Composite(convertTopComposite, SWT.NONE);
+ GridData gd_composite_1 = new GridData(SWT.FILL, SWT.FILL, true, false, 1, 1);
+ gd_composite_1.heightHint = 39;
+ composite_1.setLayoutData(gd_composite_1);
+ composite_1.setLayout(new GridLayout(4, false));
+
+ Label typeLabel = new Label(composite_1, SWT.NONE);
+ typeLabel.setText(CertificateManagerNLS.ConvertKeyStoreTypeDialog_Original_Type_Label);
+
+ final Label currentTypeLabel = new Label(composite_1, SWT.NONE);
+ GridData gd_currentTypeLabel = new GridData(SWT.LEFT, SWT.CENTER, false, false, 1, 1);
+ gd_currentTypeLabel.widthHint = 90;
+ gd_currentTypeLabel.minimumWidth = 90;
+ currentTypeLabel.setLayoutData(gd_currentTypeLabel);
+ currentTypeLabel.setText(keyStore.getType());
+
+ Label newTypeLabel = new Label(composite_1, SWT.NONE);
+ newTypeLabel.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1));
+ newTypeLabel.setText(CertificateManagerNLS.ConvertKeyStoreTypeDialog_NewType_Label);
+
+ final Combo typeCombo = new Combo(composite_1, SWT.READ_ONLY);
+ typeCombo.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));
+ loadTypeCombo(keyStoreManager, typeCombo);
+ new Label(composite_1, SWT.NONE);
+ new Label(composite_1, SWT.NONE);
+ new Label(composite_1, SWT.NONE);
+ new Label(composite_1, SWT.NONE);
+
+ typeCombo.addSelectionListener(new SelectionAdapter()
+ {
+ @Override
+ public void widgetSelected(SelectionEvent e)
+ {
+ super.widgetSelected(e);
+ newType = typeCombo.getText();
+ validateUi();
+ }
+ });
+
+ keyStoreCombo.addSelectionListener(new SelectionAdapter()
+ {
+ @Override
+ public void widgetSelected(SelectionEvent e)
+ {
+ super.widgetSelected(e);
+ updateUi(keyStoreCombo, keyStoreManager, currentTypeLabel, typeCombo);
+ validateUi();
+ }
+ });
+
+ Group entriesGroup = new Group(container, SWT.NONE);
+ entriesGroup.setLayout(new GridLayout(1, true));
+ GridData gd_entriesGroup = new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1);
+ gd_entriesGroup.heightHint = 230;
+ gd_entriesGroup.widthHint = 433;
+ entriesGroup.setLayoutData(gd_entriesGroup);
+ entriesGroup.setText(CertificateManagerNLS.ConvertKeyStoreTypeDialog_Entries_Group);
+
+ entriesTableViewer =
+ new TableViewer(entriesGroup, SWT.BORDER | SWT.SINGLE | SWT.FULL_SELECTION);
+ entriesTableViewer.setContentProvider(new EntriesContentProvider());
+ entriesTable = entriesTableViewer.getTable();
+ entriesTable.setHeaderVisible(true);
+ entriesTable.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1));
+
+ TableViewerColumn aliasViewerColumn = new TableViewerColumn(entriesTableViewer, SWT.NONE);
+ TableColumn tblclmnAlias = aliasViewerColumn.getColumn();
+ tblclmnAlias.setWidth(100);
+ tblclmnAlias.setText(CertificateManagerNLS.ConvertKeyStoreTypeDialog_Alias_Column);
+ aliasViewerColumn.setLabelProvider(new ColumnLabelProvider()
+ {
+ @Override
+ public String getText(Object element)
+ {
+ return ((EntryModel) element).getAlias();
+ }
+ });
+
+ TableViewerColumn passwordViewerColumn_1 =
+ new TableViewerColumn(entriesTableViewer, SWT.NONE);
+ passwordViewerColumn_1.setEditingSupport(new PasswordEditingSupport(entriesTableViewer));
+ TableColumn tblclmnPassword = passwordViewerColumn_1.getColumn();
+ tblclmnPassword.setWidth(100);
+ tblclmnPassword.setText(CertificateManagerNLS.ConvertKeyStoreTypeDialog_Password_Column);
+ passwordViewerColumn_1.setLabelProvider(new ColumnLabelProvider()
+ {
+ @Override
+ public String getText(Object element)
+ {
+ return ((EntryModel) element).getPasswd().replaceAll(".", "*"); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+ });
+
+ TableViewerColumn verifiedViewerColumn_2 =
+ new TableViewerColumn(entriesTableViewer, SWT.NONE);
+ TableColumn tblclmnVerified = verifiedViewerColumn_2.getColumn();
+ tblclmnVerified.setWidth(130);
+ tblclmnVerified.setText(CertificateManagerNLS.ConvertKeyStoreTypeDialog_Verified_Column);
+ verifiedViewerColumn_2.setLabelProvider(new ColumnLabelProvider()
+ {
+ @Override
+ public String getText(Object element)
+ {
+ return ((EntryModel) element).isVerified()
+ ? CertificateManagerNLS.ConvertKeyStoreTypeDialog_Verified_Pass_Yes
+ : CertificateManagerNLS.ConvertKeyStoreTypeDialog_Verified_Pass_Wrong;
+ }
+ });
+
+ updateUi(keyStoreCombo, keyStoreManager, currentTypeLabel, typeCombo);
+
+ return area;
+ }
+
+ @Override
+ protected Control createHelpControl(Composite parent)
+ {
+ PlatformUI.getWorkbench().getHelpSystem().setHelp(parent.getShell(), HELP_ID);
+ return super.createHelpControl(parent);
+ }
+
+ private void updateUi(final Combo keyStoreCombo, final KeyStoreManager keyStoreManager,
+ final Label currentTypeLabel, final Combo typeCombo)
+ {
+ keyStore = (IKeyStore) keyStoreCombo.getData(keyStoreCombo.getText());
+ try
+ {
+ password = keyStore.getPasswordProvider().getKeyStorePassword(false);
+ }
+ catch (KeyStoreManagerException e)
+ {
+ StudioLogger.error("Error while accessing keystore manager. " + e.getMessage());
+ }
+
+ if (password == null)
+ {
+ password = ""; //$NON-NLS-1$
+ }
+ passwdText.setText(password);
+ if (!password.isEmpty())
+ {
+ IKeyStore keyStore = (IKeyStore) keyStoreCombo.getData(keyStoreCombo.getText());
+ currentTypeLabel.setText(keyStore.getType());
+ loadTypeCombo(keyStoreManager, typeCombo);
+ aliaseMap.clear();
+ try
+ {
+ if (keyStore.isPasswordValid(password))
+ {
+ List<String> aliases;
+ aliases = keyStore.getAliases(password);
+ entriesTableViewer.setInput(aliases);
+ }
+ else
+ {
+ validateUi();
+ }
+ }
+ catch (KeyStoreManagerException e)
+ {
+ StudioLogger.error("Error while accessing keystore manager. " + e.getMessage());
+ }
+ catch (InvalidPasswordException e)
+ {
+ validateUi();
+ }
+ }
+ }
+
+ private void loadEntries()
+ {
+ try
+ {
+ aliaseMap.clear();
+ List<String> aliases = keyStore.getAliases(password);
+ entriesTableViewer.setInput(aliases);
+ }
+ catch (KeyStoreManagerException e1)
+ {
+ setErrorMessage(CertificateManagerNLS.ConvertKeyStoreTypeDialog_Error_Loading_Keystore);
+ entriesTableViewer.setInput(new ArrayList<String>());
+ }
+ catch (InvalidPasswordException e1)
+ {
+ setErrorMessage(CertificateManagerNLS.ConvertKeyStoreTypeDialog_Invalid_Keystore_Pass);
+ entriesTableViewer.setInput(new ArrayList<String>());
+ }
+ validateUi();
+ }
+
+ private void loadTypeCombo(KeyStoreManager keyStoreManager, final Combo typeCombo)
+ {
+ typeCombo.setItems(new String[0]);
+ List<String> availableTypes = keyStoreManager.getAvailableTypes();
+ for (String type : availableTypes)
+ {
+ if (!type.equals(keyStore.getType()))
+ {
+ typeCombo.add(type);
+ }
+ }
+ typeCombo.clearSelection();
+ }
+
+ @Override
+ protected Control createButtonBar(Composite parent)
+ {
+ Control bar = super.createButtonBar(parent);
+ getButton(OK).setEnabled(false);
+ return bar;
+ }
+
+ public void validateUi()
+ {
+ boolean isValid = true;
+ setErrorMessage(null);
+ if (isValid && (keyStore == null))
+ {
+ isValid = false;
+ setMessage(CertificateManagerNLS.ConvertKeyStoreTypeDialog_Choose_KeyStore_Msg);
+ }
+ if (isValid)
+ {
+ boolean passwordValid;
+ try
+ {
+ passwordValid = keyStore.isPasswordValid(password);
+ }
+ catch (KeyStoreManagerException e)
+ {
+ passwordValid = false;
+ }
+ catch (InvalidPasswordException e)
+ {
+ passwordValid = false;
+ }
+ if (!passwordValid)
+ {
+ isValid = false;
+ setErrorMessage(CertificateManagerNLS.ConvertKeyStoreTypeDialog_Invalid_Keystore_Pass);
+ }
+ }
+ if (isValid && newType.isEmpty())
+ {
+ isValid = false;
+ setMessage(CertificateManagerNLS.ConvertKeyStoreTypeDialog_Choose_New_Type_Msg);
+ }
+ if (isValid)
+ {
+ Object input = entriesTableViewer.getInput();
+ if (input != null)
+ {
+ int itemCount = ((List<?>) input).size();
+ for (int i = 0; i < itemCount; i++)
+ {
+ EntryModel entryModel = (EntryModel) entriesTableViewer.getElementAt(i);
+ if ((entryModel != null) && !entryModel.isVerified())
+ {
+ isValid = false;
+ setMessage(
+ CertificateManagerNLS.ConvertKeyStoreTypeDialog_Incorrect_Entry_Pass,
+ IMessageProvider.WARNING);
+ break;
+ }
+ }
+ }
+ }
+
+ Button okButton = getButton(OK);
+ if (okButton != null)
+ {
+ if (!isValid)
+ {
+ okButton.setEnabled(false);
+ }
+ else
+ {
+ getButton(OK).setEnabled(true);
+ setErrorMessage(null);
+ setMessage(CertificateManagerNLS.ConvertKeyStoreTypeDialog_DefaultMessage);
+ }
+ }
+ }
+
+ public IKeyStore getKeyStore()
+ {
+ return keyStore;
+ }
+
+ public String getNewType()
+ {
+ return newType;
+ }
+
+ public Map<String, String> getAliases()
+ {
+ return aliaseMap;
+ }
+
+ public String getKeystorePassword()
+ {
+ return this.password;
+ }
+}
diff --git a/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/dialogs/importks/ImportEntriesDialog.java b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/dialogs/importks/ImportEntriesDialog.java
new file mode 100644
index 0000000..0a1cf31
--- /dev/null
+++ b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/dialogs/importks/ImportEntriesDialog.java
@@ -0,0 +1,675 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.studio.android.certmanager.ui.dialogs.importks;
+
+import java.io.File;
+import java.security.KeyStore.Entry;
+import java.security.KeyStore.PasswordProtection;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.jface.dialogs.IMessageProvider;
+import org.eclipse.jface.dialogs.TitleAreaDialog;
+import org.eclipse.jface.viewers.CellEditor;
+import org.eclipse.jface.viewers.CheckboxTableViewer;
+import org.eclipse.jface.viewers.ColumnLabelProvider;
+import org.eclipse.jface.viewers.ColumnViewer;
+import org.eclipse.jface.viewers.EditingSupport;
+import org.eclipse.jface.viewers.IStructuredContentProvider;
+import org.eclipse.jface.viewers.TableViewerColumn;
+import org.eclipse.jface.viewers.TextCellEditor;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.ModifyEvent;
+import org.eclipse.swt.events.ModifyListener;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Combo;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Group;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.swt.widgets.TableColumn;
+import org.eclipse.swt.widgets.TableItem;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.ui.PlatformUI;
+
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorolamobility.studio.android.certmanager.CertificateManagerActivator;
+import com.motorolamobility.studio.android.certmanager.core.KeyStoreManager;
+import com.motorolamobility.studio.android.certmanager.exception.InvalidPasswordException;
+import com.motorolamobility.studio.android.certmanager.exception.KeyStoreManagerException;
+import com.motorolamobility.studio.android.certmanager.i18n.CertificateManagerNLS;
+import com.motorolamobility.studio.android.certmanager.ui.model.IKeyStore;
+
+public class ImportEntriesDialog extends TitleAreaDialog
+{
+ public class EntryModel
+ {
+ private final String alias;
+
+ private String passwd;
+
+ private boolean verified;
+
+ public EntryModel(String alias)
+ {
+ this.alias = alias;
+ try
+ {
+ String savedPass = sourceKeyStore.getPasswordProvider().getPassword(alias, false);
+ setPasswd(savedPass != null ? savedPass : ""); //$NON-NLS-1$
+ }
+ catch (KeyStoreManagerException e)
+ {
+ setPasswd(""); //$NON-NLS-1$
+ }
+ }
+
+ @Override
+ public String toString()
+ {
+ return alias;
+ }
+
+ public String getPasswd()
+ {
+ return passwd;
+ }
+
+ public void setPasswd(String passwd)
+ {
+ this.passwd = passwd;
+ try
+ {
+ Entry entry =
+ sourceKeyStore.getKeyStore().getEntry(alias,
+ new PasswordProtection(passwd.toCharArray()));
+ setVerified(entry != null);
+ }
+ catch (Exception e)
+ {
+ setVerified(false);
+ }
+ aliasMap.put(alias, passwd);
+ }
+
+ public String getAlias()
+ {
+ return alias;
+ }
+
+ public boolean isVerified()
+ {
+ return verified;
+ }
+
+ private void setVerified(boolean verified)
+ {
+ this.verified = verified;
+ // entriesTableViewer.update(this, null);
+ validateUi();
+ }
+ }
+
+ public class EntriesContentProvider implements IStructuredContentProvider
+ {
+
+ @Override
+ public void dispose()
+ {
+ //do nothing
+ }
+
+ @Override
+ public void inputChanged(Viewer viewer, Object oldInput, Object newInput)
+ {
+ validateUi();
+ }
+
+ @Override
+ public Object[] getElements(Object inputElement)
+ {
+ List<EntryModel> modelList = null;
+ if (inputElement instanceof List<?>)
+ {
+ List<?> inputList = (List<?>) inputElement;
+ modelList = new ArrayList<EntryModel>(inputList.size());
+ Iterator<?> it = inputList.iterator();
+ while (it.hasNext())
+ {
+ Object element = it.next();
+ if (element instanceof String) //received an alias
+ {
+ String alias = (String) element;
+ EntryModel entryModel = new EntryModel(alias);
+ modelList.add(entryModel);
+ }
+ }
+
+ }
+ return modelList.toArray();
+ }
+
+ }
+
+ private final class PasswordEditingSupport extends EditingSupport
+ {
+ private PasswordEditingSupport(ColumnViewer viewer)
+ {
+ super(viewer);
+ }
+
+ @Override
+ protected boolean canEdit(Object element)
+ {
+ return true;
+ }
+
+ @Override
+ protected CellEditor getCellEditor(Object element)
+ {
+ return new TextCellEditor(entriesTable, SWT.PASSWORD);
+ }
+
+ @Override
+ protected Object getValue(Object element)
+ {
+ return ((EntryModel) element).getPasswd();
+ }
+
+ @Override
+ protected void setValue(Object element, Object value)
+ {
+ EntryModel model = (EntryModel) element;
+ model.setPasswd((String) value);
+ getViewer().update(element, null);
+ }
+ }
+
+ private static final String WIZARD_BANNER = "icons/wizban/import_entries_wiz.png"; //$NON-NLS-1$
+
+ private static final String HELP_ID = CertificateManagerActivator.PLUGIN_ID
+ + ".import_entries_dialog"; //$NON-NLS-1$
+
+ private IKeyStore sourceKeyStore;
+
+ private IKeyStore targetKeyStore;
+
+ private Text passwdText;
+
+ protected String sourcePassword = ""; //$NON-NLS-1$
+
+ private Table entriesTable;
+
+ private CheckboxTableViewer entriesTableViewer;
+
+ private final Map<String, String> aliasMap = new HashMap<String, String>();
+
+ private Combo keyStoreCombo;
+
+ private Combo targetKsCombo;
+
+ protected List<String> selectedAlias = new ArrayList<String>();
+
+ /**
+ * Create the dialog.
+ * @param parentShell
+ */
+ public ImportEntriesDialog(Shell parentShell, IKeyStore keyStore)
+ {
+ super(parentShell);
+ setShellStyle(SWT.DIALOG_TRIM | SWT.APPLICATION_MODAL);
+
+ this.targetKeyStore = keyStore;
+ setTitleImage(CertificateManagerActivator.imageDescriptorFromPlugin(
+ CertificateManagerActivator.PLUGIN_ID, WIZARD_BANNER).createImage());
+ }
+
+ /**
+ * Create contents of the dialog.
+ * @param parent
+ */
+ @Override
+ protected Control createDialogArea(Composite parent)
+ {
+ parent.getShell().setText(CertificateManagerNLS.ImportKeyStoreDialog_Dialog_Title);
+ setMessage(CertificateManagerNLS.ImportKeyStoreDialog_Default_Message);
+ setTitle(CertificateManagerNLS.ImportKeyStoreDialog_Dialog_Title);
+ Composite area = (Composite) super.createDialogArea(parent);
+ Composite container = new Composite(area, SWT.NONE);
+ container.setLayout(new GridLayout(1, false));
+ container.setLayoutData(new GridData(GridData.FILL_BOTH));
+ Group SourceGroup = new Group(container, SWT.NONE);
+ SourceGroup.setText(CertificateManagerNLS.ImportKeyStoreDialog_Source_Group);
+ SourceGroup.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1));
+ SourceGroup.setLayout(new GridLayout(1, false));
+
+ Composite keyStoreComposite = new Composite(SourceGroup, SWT.NONE);
+ keyStoreComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false, 1, 1));
+ keyStoreComposite.setLayout(new GridLayout(3, false));
+
+ Label keyStoreLabel = new Label(keyStoreComposite, SWT.NONE);
+ keyStoreLabel.setText(CertificateManagerNLS.ImportKeyStoreDialog_KeyStore_Label);
+
+ keyStoreCombo = new Combo(keyStoreComposite, SWT.READ_ONLY);
+ keyStoreCombo.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 2, 1));
+
+ Label passwdLabel = new Label(keyStoreComposite, SWT.NONE);
+ passwdLabel.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1));
+ passwdLabel.setText(CertificateManagerNLS.ImportKeyStoreDialog_Password_Label);
+
+ passwdText = new Text(keyStoreComposite, SWT.BORDER | SWT.PASSWORD);
+ passwdText.addModifyListener(new ModifyListener()
+ {
+ @Override
+ public void modifyText(ModifyEvent e)
+ {
+ sourcePassword = passwdText.getText();
+ }
+ });
+ passwdText.addSelectionListener(new SelectionAdapter()
+ {
+ @Override
+ public void widgetDefaultSelected(SelectionEvent e)
+ {
+ super.widgetDefaultSelected(e);
+ loadEntries();
+ }
+ });
+ passwdText.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));
+
+ Button loadButton = new Button(keyStoreComposite, SWT.NONE);
+ GridData gd_loadButton = new GridData(SWT.FILL, SWT.CENTER, false, false, 1, 1);
+ gd_loadButton.widthHint = 80;
+ loadButton.setLayoutData(gd_loadButton);
+ loadButton.setText(CertificateManagerNLS.ImportKeyStoreDialog_Load_Button);
+
+ Composite entriesComposite = new Composite(SourceGroup, SWT.NONE);
+ GridData gd_entriesComposite = new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1);
+ gd_entriesComposite.heightHint = 200;
+ entriesComposite.setLayoutData(gd_entriesComposite);
+ entriesComposite.setLayout(new GridLayout(1, true));
+
+ entriesTableViewer =
+ CheckboxTableViewer.newCheckList(entriesComposite, SWT.BORDER | SWT.CHECK
+ | SWT.FULL_SELECTION);
+ entriesTableViewer.setContentProvider(new EntriesContentProvider());
+ entriesTable = entriesTableViewer.getTable();
+ entriesTable.addSelectionListener(new SelectionAdapter()
+ {
+ @Override
+ public void widgetSelected(SelectionEvent e)
+ {
+ if (e.detail == SWT.CHECK)
+ {
+ validateUi();
+ TableItem item = (TableItem) e.item;
+ if (item.getChecked())
+ {
+ selectedAlias.add(item.getText(0));
+ }
+ else
+ {
+ selectedAlias.remove(item.getText(0));
+ }
+ }
+ }
+ });
+ entriesTable.setHeaderVisible(true);
+ GridData gd_entriesTable = new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1);
+ gd_entriesTable.heightHint = 250;
+ entriesTable.setLayoutData(gd_entriesTable);
+ TableViewerColumn aliasViewerColumn = new TableViewerColumn(entriesTableViewer, SWT.NONE);
+ TableColumn tblclmnAlias = aliasViewerColumn.getColumn();
+ tblclmnAlias.setWidth(100);
+ tblclmnAlias.setText(CertificateManagerNLS.ImportKeyStoreDialog_Alias_Column);
+ aliasViewerColumn.setLabelProvider(new ColumnLabelProvider()
+ {
+ @Override
+ public String getText(Object element)
+ {
+ return ((EntryModel) element).getAlias();
+ }
+ });
+
+ TableViewerColumn passwordViewerColumn_1 =
+ new TableViewerColumn(entriesTableViewer, SWT.NONE);
+ passwordViewerColumn_1.setEditingSupport(new PasswordEditingSupport(entriesTableViewer));
+ TableColumn tblclmnPassword = passwordViewerColumn_1.getColumn();
+ tblclmnPassword.setWidth(100);
+ tblclmnPassword.setText(CertificateManagerNLS.ImportKeyStoreDialog_Passwd_Column);
+ passwordViewerColumn_1.setLabelProvider(new ColumnLabelProvider()
+ {
+ @Override
+ public String getText(Object element)
+ {
+ return ((EntryModel) element).getPasswd().replaceAll(".", "*"); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+ });
+
+ TableViewerColumn verifiedViewerColumn_2 =
+ new TableViewerColumn(entriesTableViewer, SWT.NONE);
+ TableColumn tblclmnVerified = verifiedViewerColumn_2.getColumn();
+ tblclmnVerified.setWidth(130);
+ tblclmnVerified.setText(CertificateManagerNLS.ImportKeyStoreDialog_Verified_Column);
+ verifiedViewerColumn_2.setLabelProvider(new ColumnLabelProvider()
+ {
+ @Override
+ public String getText(Object element)
+ {
+ return ((EntryModel) element).isVerified()
+ ? CertificateManagerNLS.ImportKeyStoreDialog_Verified_Pass_Yes
+ : CertificateManagerNLS.ImportKeyStoreDialog_Verified_Pass_Wrong;
+ }
+ });
+ loadButton.addSelectionListener(new SelectionAdapter()
+ {
+ @Override
+ public void widgetSelected(SelectionEvent e)
+ {
+ loadEntries();
+ }
+ });
+
+ keyStoreCombo.addSelectionListener(new SelectionAdapter()
+ {
+ @Override
+ public void widgetSelected(SelectionEvent e)
+ {
+ super.widgetSelected(e);
+ sourceKeyStore = (IKeyStore) keyStoreCombo.getData(keyStoreCombo.getText());
+ IKeyStore keyStore = (IKeyStore) keyStoreCombo.getData(keyStoreCombo.getText());
+ try
+ {
+ sourcePassword = keyStore.getPasswordProvider().getKeyStorePassword(false);
+ }
+ catch (KeyStoreManagerException e1)
+ {
+ StudioLogger.error("Error while accessing keystore manager. " + e1.getMessage());
+ }
+
+ if (sourcePassword == null)
+ {
+ sourcePassword = ""; //$NON-NLS-1$
+ }
+ passwdText.setText(sourcePassword);
+ loadEntries();
+ updateTargetCombo();
+ validateUi();
+ }
+ });
+
+ Group targetGroup = new Group(container, SWT.NONE);
+ targetGroup.setLayout(new GridLayout(2, false));
+ targetGroup.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));
+ targetGroup.setText(CertificateManagerNLS.ImportKeyStoreDialog_Target_Group);
+
+ Label targetKsLabel = new Label(targetGroup, SWT.NONE);
+ targetKsLabel.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1));
+ targetKsLabel.setText(CertificateManagerNLS.ImportKeyStoreDialog_KeyStore_Label);
+
+ targetKsCombo = new Combo(targetGroup, SWT.READ_ONLY);
+ targetKsCombo.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));
+
+ targetKsCombo.addSelectionListener(new SelectionAdapter()
+ {
+ @Override
+ public void widgetSelected(SelectionEvent e)
+ {
+ super.widgetSelected(e);
+ String selectedItem = targetKsCombo.getText();
+ targetKeyStore = (IKeyStore) targetKsCombo.getData(selectedItem);
+ validateUi();
+ }
+ });
+
+ final KeyStoreManager keyStoreManager = KeyStoreManager.getInstance();
+ try
+ {
+ List<IKeyStore> keyStores = keyStoreManager.getKeyStores();
+ for (IKeyStore keyStore : keyStores)
+ {
+ File ksFile = keyStore.getFile();
+ String comboItem = ksFile.getName() + " - " + ksFile.getAbsolutePath(); //$NON-NLS-1$
+ keyStoreCombo.add(comboItem);
+ keyStoreCombo.setData(comboItem, keyStore);
+ if (keyStore.equals(this.sourceKeyStore))
+ {
+ keyStoreCombo.select(keyStoreCombo.indexOf(comboItem));
+ }
+ else
+ {
+ targetKsCombo.add(comboItem);
+ targetKsCombo.setData(comboItem, keyStore);
+ if (keyStore.equals(this.targetKeyStore))
+ {
+ targetKsCombo.select(targetKsCombo.indexOf(comboItem));
+ }
+ }
+ }
+ }
+ catch (KeyStoreManagerException e1)
+ {
+ setErrorMessage(CertificateManagerNLS.ImportKeyStoreDialog_Error_Loading_Keystores);
+ }
+
+ return area;
+ }
+
+ @Override
+ protected Control createHelpControl(Composite parent)
+ {
+ PlatformUI.getWorkbench().getHelpSystem().setHelp(parent.getShell(), HELP_ID);
+ return super.createHelpControl(parent);
+ }
+
+ private void updateTargetCombo()
+ {
+ final KeyStoreManager keyStoreManager = KeyStoreManager.getInstance();
+ try
+ {
+ targetKsCombo.clearSelection();
+ targetKsCombo.setItems(new String[0]);
+ List<IKeyStore> keyStores = keyStoreManager.getKeyStores();
+ for (IKeyStore keyStore : keyStores)
+ {
+ if (keyStore != this.sourceKeyStore)
+ {
+ File ksFile = keyStore.getFile();
+ String comboItem = ksFile.getName() + " - " + ksFile.getAbsolutePath(); //$NON-NLS-1$
+ targetKsCombo.add(comboItem);
+ targetKsCombo.setData(comboItem, keyStore);
+ if (keyStore.equals(targetKeyStore))
+ {
+ targetKsCombo.select(targetKsCombo.indexOf(comboItem));
+ }
+ }
+ }
+ if (targetKsCombo.getSelectionIndex() == -1) //nothing is selected.
+ {
+ targetKeyStore = null;
+ }
+ }
+ catch (KeyStoreManagerException e1)
+ {
+ setErrorMessage(CertificateManagerNLS.ImportKeyStoreDialog_Error_Loading_Keystores);
+ }
+
+ }
+
+ private void loadEntries()
+ {
+ try
+ {
+ aliasMap.clear();
+ if (!sourcePassword.isEmpty())
+ {
+ List<String> aliases = sourceKeyStore.getAliases(sourcePassword);
+ entriesTableViewer.setInput(aliases);
+ }
+ else
+ {
+ entriesTableViewer.setInput(new ArrayList<String>());
+ }
+ }
+ catch (KeyStoreManagerException e1)
+ {
+ setErrorMessage(CertificateManagerNLS.ImportKeyStoreDialog_Error_Loading_Entries);
+ entriesTableViewer.setInput(new ArrayList<String>());
+ }
+ catch (InvalidPasswordException e1)
+ {
+ setErrorMessage(CertificateManagerNLS.ImportKeyStoreDialog_Invalid_Keystore_Passwd);
+ entriesTableViewer.setInput(new ArrayList<String>());
+ }
+ }
+
+ @Override
+ protected Control createButtonBar(Composite parent)
+ {
+ Control bar = super.createButtonBar(parent);
+ getButton(OK).setEnabled(false);
+ return bar;
+ }
+
+ public void validateUi()
+ {
+ boolean isValid = true;
+ setErrorMessage(null);
+ if (isValid && (sourceKeyStore == null))
+ {
+ isValid = false;
+ setMessage(CertificateManagerNLS.ImportKeyStoreDialog_Select_Source_Ks);
+ }
+ if (isValid && ((sourcePassword == null) || sourcePassword.isEmpty()))
+ {
+ isValid = false;
+ setMessage(CertificateManagerNLS.ImportKeyStoreDialog_Type_SourceKs_Passwd);
+ }
+ if (isValid)
+ {
+ try
+ {
+ if (!sourceKeyStore.isPasswordValid(sourcePassword))
+ {
+ isValid = false;
+ setErrorMessage("Wrong source keystore password.");
+ }
+ }
+ catch (KeyStoreManagerException e)
+ {
+ isValid = false;
+ setErrorMessage("Unable to access source keystore.\n" + e.getMessage());
+ }
+ catch (InvalidPasswordException e)
+ {
+ isValid = false;
+ setErrorMessage("Wrong source keystore password.");
+ }
+ }
+ if (isValid)
+ {
+ List<?> input = (List<?>) entriesTableViewer.getInput();
+ if (input != null)
+ {
+ int itemCount = input.size();
+ if (itemCount == 0)
+ {
+ isValid = false;
+ setMessage(CertificateManagerNLS.ImportKeyStoreDialog_No_Entries_To_Import,
+ IMessageProvider.WARNING);
+ }
+ if (entriesTableViewer.getCheckedElements().length == 0)
+ {
+ isValid = false;
+ setMessage(CertificateManagerNLS.ImportKeyStoreDialog_No_Entries_To_Import,
+ IMessageProvider.WARNING);
+ }
+ else
+ {
+ for (int i = 0; i < itemCount; i++)
+ {
+ EntryModel entryModel = (EntryModel) entriesTableViewer.getElementAt(i);
+ if (entriesTableViewer.getChecked(entryModel) && !entryModel.isVerified())
+ {
+ isValid = false;
+ setMessage(
+ CertificateManagerNLS.ImportKeyStoreDialog_Wrong_Entries_Passwd,
+ IMessageProvider.WARNING);
+ break;
+ }
+ }
+ }
+ }
+ else
+ {
+ isValid = false;
+ setMessage(CertificateManagerNLS.ImportKeyStoreDialog_No_Entries_To_Import,
+ IMessageProvider.WARNING);
+ }
+ }
+ if (isValid && (targetKeyStore == null))
+ {
+ isValid = false;
+ setMessage(CertificateManagerNLS.ImportKeyStoreDialog_Select_Target_Kesytore);
+ }
+
+ if (!isValid)
+ {
+ getButton(OK).setEnabled(false);
+ }
+ else
+ {
+ getButton(OK).setEnabled(true);
+ setErrorMessage(null);
+ setMessage(CertificateManagerNLS.ImportKeyStoreDialog_Default_Message);
+ }
+ }
+
+ public IKeyStore getKeyStore()
+ {
+ return sourceKeyStore;
+ }
+
+ public Map<String, String> getAliases()
+ {
+ Map<String, String> selectedAliasMap = new HashMap<String, String>(selectedAlias.size());
+ for (String alias : selectedAlias)
+ {
+ selectedAliasMap.put(alias, aliasMap.get(alias));
+ }
+ return selectedAliasMap;
+ }
+
+ public IKeyStore getTargetKeyStore()
+ {
+ return targetKeyStore;
+ }
+
+ public String getPassword()
+ {
+ return sourcePassword;
+ }
+}
diff --git a/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/model/AbstractTreeNode.java b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/model/AbstractTreeNode.java
new file mode 100644
index 0000000..bea7353
--- /dev/null
+++ b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/model/AbstractTreeNode.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.studio.android.certmanager.ui.model;
+
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+
+import com.motorolamobility.studio.android.certmanager.event.KeyStoreModelEvent.EventType;
+import com.motorolamobility.studio.android.certmanager.event.KeyStoreModelEventManager;
+import com.motorolamobility.studio.android.certmanager.exception.KeyStoreManagerException;
+import com.motorolamobility.studio.android.certmanager.i18n.CertificateManagerNLS;
+import com.motorolamobility.studio.android.certmanager.views.KeystoreManagerView;
+
+/**
+ * Abstract node for {@link KeystoreManagerView}
+ */
+public abstract class AbstractTreeNode implements ITreeNode
+{
+ private static final String UNSAVED_PASSWORD =
+ CertificateManagerNLS.AbstractTreeNode_UnsavedPassword_Tooltip;
+
+ private static final String SAVED_PASSWORD =
+ CertificateManagerNLS.AbstractTreeNode_SavedPassword_Tooltip;
+
+ private IStatus nodeStatus = Status.OK_STATUS;
+
+ private ITreeNode parent;
+
+ private String tooltip;
+
+ @Override
+ public String getTooltip()
+ {
+ if (tooltip != null)
+ {
+ return isPasswordSaved() ? SAVED_PASSWORD + "\n" + tooltip : UNSAVED_PASSWORD //$NON-NLS-1$
+ + "\n" + tooltip; //$NON-NLS-1$
+ }
+ else
+ {
+ return isPasswordSaved() ? SAVED_PASSWORD : UNSAVED_PASSWORD;
+ }
+ }
+
+ /**
+ * @return the nodeStatus
+ */
+ @Override
+ public IStatus getNodeStatus()
+ {
+ return nodeStatus;
+ }
+
+ /**
+ * @param nodeStatus the nodeStatus to set
+ */
+ @Override
+ public void setNodeStatus(IStatus nodeStatus)
+ {
+ this.nodeStatus = nodeStatus;
+ KeyStoreModelEventManager.getInstance().fireEvent(this, EventType.UPDATE);
+ }
+
+ /**
+ * @param tooltip the tooltip to set
+ */
+ @Override
+ public void setTooltip(String tooltip)
+ {
+ this.tooltip = tooltip;
+ }
+
+ /**
+ * @return the parent
+ */
+ @Override
+ public ITreeNode getParent()
+ {
+ return parent;
+ }
+
+ /**
+ * @param parent the parent to set
+ */
+ public void setParent(ITreeNode parent)
+ {
+ this.parent = parent;
+ }
+
+ @Override
+ public void addChild(ITreeNode newChild) throws KeyStoreManagerException
+ {
+ //Default implementation won't do nothing
+ }
+
+ @Override
+ public boolean testAttribute(Object target, String name, String value)
+ {
+ boolean result = false;
+ if (name.equals(PROP_NAME_NODE_STATUS))
+ {
+ if (value.equals(PROP_VALUE_NODE_STATUS_ERROR))
+ {
+ result = !getNodeStatus().isOK(); //true if there is an error
+ if (result)
+ {
+ setTooltip(getNodeStatus().getMessage());
+ }
+ }
+ else if (value.equals(PROP_VALUE_NODE_STATUS_OK))
+ {
+ result = getNodeStatus().isOK();
+ }
+ }
+ return result;
+ }
+
+ /**
+ * For dummy nodes and root nodes it is false. Override for other nodes: {@link KeyStoreNode} and {@link EntryNode}
+ * @return
+ */
+ protected boolean isPasswordSaved()
+ {
+ return false;
+ }
+}
diff --git a/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/model/CertificateDetailsInfo.java b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/model/CertificateDetailsInfo.java
new file mode 100644
index 0000000..3d83690
--- /dev/null
+++ b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/model/CertificateDetailsInfo.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.studio.android.certmanager.ui.model;
+
+import java.util.Calendar;
+import java.util.Date;
+
+public class CertificateDetailsInfo
+{
+ private String alias;
+
+ /**
+ * Issuer name
+ */
+ private String commonName;
+
+ /**
+ * Owner name
+ */
+ private String organization;
+
+ private String organizationUnit;
+
+ private String locality;
+
+ private String country;
+
+ private String state;
+
+ private String entryPassword; //this is NOT the keystore password
+
+ private Date expirationDate;
+
+ public CertificateDetailsInfo(String alias, String commonName, String organization,
+ String organizationUnit, String locality, String country, String state,
+ String validity, String entryPassword)
+ {
+ this.alias = alias;
+ this.commonName = commonName;
+ this.organization = organization;
+ this.organizationUnit = organizationUnit;
+ this.locality = locality;
+ this.country = country;
+ this.state = state;
+ this.entryPassword = entryPassword;
+
+ int validityYears = Integer.parseInt(validity);
+
+ Calendar cal = Calendar.getInstance();
+ cal.add(Calendar.YEAR, validityYears);
+
+ this.expirationDate = cal.getTime();
+ }
+
+ /**
+ * @return the alias
+ */
+ public String getAlias()
+ {
+ return alias;
+ }
+
+ /**
+ * @return the commonName
+ */
+ public String getCommonName()
+ {
+ return commonName;
+ }
+
+ /**
+ * @return the organization
+ */
+ public String getOrganization()
+ {
+ return organization;
+ }
+
+ /**
+ * @return the organizationUnit
+ */
+ public String getOrganizationUnit()
+ {
+ return organizationUnit;
+ }
+
+ /**
+ * @return the locality
+ */
+ public String getLocality()
+ {
+ return locality;
+ }
+
+ /**
+ * @return the country
+ */
+ public String getCountry()
+ {
+ return country;
+ }
+
+ /**
+ * @return the expirationDate
+ */
+ public Date getExpirationDate()
+ {
+ return expirationDate;
+ }
+
+ /**
+ * @return the state
+ */
+ public String getState()
+ {
+ return state;
+ }
+
+ /**
+ * @return the keyPassword
+ */
+ public String getEntryPassword()
+ {
+ return entryPassword;
+ }
+
+}
diff --git a/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/model/EntryDummyNode.java b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/model/EntryDummyNode.java
new file mode 100644
index 0000000..a35b495
--- /dev/null
+++ b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/model/EntryDummyNode.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.studio.android.certmanager.ui.model;
+
+import java.util.Collections;
+import java.util.List;
+
+import org.eclipse.jface.resource.ImageDescriptor;
+
+public class EntryDummyNode extends AbstractTreeNode implements ITreeNode
+{
+
+ public static final String DUMMY_NODE_ID = "DUMMY_NODE";
+
+ String alias = DUMMY_NODE_ID;
+
+ public EntryDummyNode(ITreeNode keyStoreModel)
+ {
+ setParent(keyStoreModel);
+ alias = DUMMY_NODE_ID;
+ }
+
+ @Override
+ public void refresh()
+ {
+ //default implementation does nothing.
+ }
+
+ @Override
+ public String getId()
+ {
+ return DUMMY_NODE_ID;
+ }
+
+ @Override
+ public String getName()
+ {
+ return "No Keys found";
+ }
+
+ @Override
+ public ImageDescriptor getIcon()
+ {
+ return null;
+ }
+
+ @Override
+ public boolean isLeaf()
+ {
+ return true;
+ }
+
+ @Override
+ public List<ITreeNode> getChildren()
+ {
+ return Collections.emptyList();
+ }
+}
diff --git a/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/model/EntryNode.java b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/model/EntryNode.java
new file mode 100644
index 0000000..1cf9d66
--- /dev/null
+++ b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/model/EntryNode.java
@@ -0,0 +1,546 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.studio.android.certmanager.ui.model;
+
+import java.security.InvalidKeyException;
+import java.security.Key;
+import java.security.KeyPair;
+import java.security.KeyStore;
+import java.security.KeyStore.Entry;
+import java.security.KeyStore.PrivateKeyEntry;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.UnrecoverableEntryException;
+import java.security.UnrecoverableKeyException;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.CertificateExpiredException;
+import java.security.cert.CertificateNotYetValidException;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.GregorianCalendar;
+import java.util.List;
+import java.util.Set;
+
+import org.bouncycastle.asn1.x500.RDN;
+import org.bouncycastle.asn1.x500.X500Name;
+import org.bouncycastle.asn1.x500.style.BCStyle;
+import org.bouncycastle.cert.jcajce.JcaX509CertificateHolder;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.osgi.util.NLS;
+
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorolamobility.studio.android.certmanager.CertificateManagerActivator;
+import com.motorolamobility.studio.android.certmanager.core.KeyStoreUtils;
+import com.motorolamobility.studio.android.certmanager.core.PasswordProvider;
+import com.motorolamobility.studio.android.certmanager.exception.KeyStoreManagerException;
+import com.motorolamobility.studio.android.certmanager.i18n.CertificateManagerNLS;
+import com.motorolamobility.studio.android.certmanager.views.KeystoreManagerView;
+
+/**
+ * Represents one keystore element in {@link KeystoreManagerView}. It can be
+ * {@link Certificate} or {@link Key}.
+ */
+public class EntryNode extends AbstractTreeNode implements IKeyStoreEntry
+{
+ /**
+ * The constant contains the key pair DER object identifier.
+ */
+ public static final String KEY_PAIR_DER_OBJ_ID = "2.16.840.1.113793.23"; //$NON-NLS-1$
+
+ public static final int KEY_PASSWORD_MIN_SIZE = 6;
+
+ protected String alias;
+
+ private final String KEY_NONSAVED_PASSWORD_ICON_PATH = "icons/key.png"; //$NON-NLS-1$
+
+ private final String KEY_SAVED_PASSWORD_ICON_PATH = "icons/key_saved_password.png"; //$NON-NLS-1$
+
+ protected EntryNode()
+ {
+
+ }
+
+ /**
+ *
+ * @param keyStoreModel
+ * @param alias
+ * @throws KeyStoreManagerException
+ * if the alias is already listed in the tree
+ */
+ public EntryNode(ITreeNode keyStoreModel, String alias) throws KeyStoreManagerException
+ {
+ this.alias = alias.toLowerCase();
+ setParent(keyStoreModel);
+ if (!isKeyPairEntry())
+ {
+ keyStoreModel.addChild(this);
+ }
+
+ // notify key entry addition
+ // KeyStoreModelEventManager.getInstance().fireEvent(this, KeyStoreModelEvent.EventType.ADD);
+
+ // Obtaining certificate to get tooltip information
+ X509Certificate cert = getX509Certificate();
+ if (cert != null)
+ {
+ X500Name x500name;
+ try
+ {
+ x500name = new JcaX509CertificateHolder(cert).getSubject();
+
+ RDN commonName =
+ x500name.getRDNs(BCStyle.CN).length >= 1 ? x500name.getRDNs(BCStyle.CN)[0]
+ : null;
+ RDN organization =
+ x500name.getRDNs(BCStyle.O).length >= 1 ? x500name.getRDNs(BCStyle.O)[0]
+ : null;
+
+ // Adding tooltip information
+ String org =
+ organization != null ? organization.getFirst().getValue().toString()
+ : CertificateManagerNLS.CertificateInfoDialog_NotAvailableProperty;
+ String name =
+ commonName != null ? commonName.getFirst().getValue().toString()
+ : CertificateManagerNLS.CertificateInfoDialog_NotAvailableProperty;
+ this.setTooltip(NLS.bind(CertificateManagerNLS.CertificateBlock_KeyTooltip, org,
+ name));
+ }
+ catch (CertificateEncodingException e)
+ {
+ String errorMsg = "Error getting data from certificate";
+ StudioLogger.error(EntryNode.class, errorMsg, e);
+ throw new KeyStoreManagerException(errorMsg, e);
+ }
+ }
+ }
+
+ /*(non-Javadoc)
+ *
+ * @see
+ * com.motorolamobility.studio.android.certmanager.ui.model.IKeyStoreEntry
+ * #getKeyStoreNode()
+ */
+ @Override
+ public IKeyStore getKeyStoreNode()
+ {
+ return (KeyStoreNode) getParent();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * com.motorolamobility.studio.android.certmanager.ui.model.IKeyStoreEntry
+ * #getAlias()
+ */
+ @Override
+ public String getAlias()
+ {
+ return alias;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * com.motorolamobility.studio.android.certmanager.ui.model.IKeyStoreEntry
+ * #isCertificateEntry()
+ */
+ @Override
+ public boolean isCertificateEntry() throws KeyStoreException, KeyStoreManagerException
+ {
+ return getKeyStoreNode().getKeyStore().isCertificateEntry(alias);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * com.motorolamobility.studio.android.certmanager.ui.model.IKeyStoreEntry
+ * #isKeyEntry()
+ */
+ @Override
+ public boolean isKeyEntry() throws KeyStoreException, KeyStoreManagerException
+ {
+ return getKeyStoreNode().getKeyStore().isKeyEntry(alias);
+ }
+
+ @Override
+ public boolean isKeyPairEntry()
+ {
+ X509Certificate certificate = getX509Certificate();
+ Set<String> criticalOIDs = certificate.getCriticalExtensionOIDs();
+ return (criticalOIDs != null) && criticalOIDs.contains(KEY_PAIR_DER_OBJ_ID);
+ }
+
+ /**
+ * @return {@link Certificate} if alias represents a certificate or null if
+ * the alias was not found (or if the type is not Certificate for
+ * the alias)
+ * @throws KeyStoreException
+ * if keystore not loaded yet
+ * @throws KeyStoreManagerException
+ */
+ private Certificate getCertificate() throws KeyStoreException, KeyStoreManagerException
+ {
+ Certificate certificate = null;
+ KeyStore keyStore = getKeyStoreNode().getKeyStore();
+ if (keyStore.isCertificateEntry(alias))
+ {
+ certificate = keyStore.getCertificate(alias);
+ }
+ else
+ {
+ // unknown type
+ StudioLogger.error(CertificateManagerNLS.bind(
+ CertificateManagerNLS.EntryNode_NotFoundOrTypeWrong, alias));
+ }
+ return certificate;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * com.motorolamobility.studio.android.certmanager.ui.model.IKeyStoreEntry
+ * #getKey(java.lang.String)
+ */
+ @Override
+ public Key getKey(String password) throws UnrecoverableKeyException, KeyStoreException,
+ NoSuchAlgorithmException, KeyStoreManagerException
+ {
+ Key key = null;
+ KeyStore keyStore = getKeyStoreNode().getKeyStore();
+ if (keyStore.isKeyEntry(alias))
+ {
+ key = keyStore.getKey(alias, password.toCharArray());
+ }
+
+ return key;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * com.motorolamobility.studio.android.certmanager.ui.model.IKeyStoreEntry
+ * #getPrivateKey(java.lang.String)
+ */
+ @Override
+ public PrivateKey getPrivateKey(String password) throws UnrecoverableKeyException,
+ KeyStoreException, NoSuchAlgorithmException, KeyStoreManagerException,
+ InvalidKeyException
+ {
+ Key key = this.getKey(password);
+
+ if (!(key instanceof PrivateKey))
+ {
+ throw new InvalidKeyException("This is not a private key");
+ }
+
+ return (PrivateKey) key;
+ }
+
+ public Entry getKeyEntry(String password) throws KeyStoreException, NoSuchAlgorithmException,
+ KeyStoreManagerException, UnrecoverableEntryException
+ {
+ Entry key = null;
+ KeyStore keyStore = getKeyStoreNode().getKeyStore();
+ if (keyStore.isKeyEntry(alias))
+ {
+ key = keyStore.getEntry(alias, new KeyStore.PasswordProtection(password.toCharArray()));
+ }
+ return key;
+ }
+
+ /**
+ * Get all the certificates associated to this entry
+ *
+ * @return an Array of {@link Certificate}
+ * @throws KeyStoreException
+ * @throws KeyStoreManagerException
+ */
+ private Certificate[] getCertificateChain() throws KeyStoreException, KeyStoreManagerException
+ {
+ KeyStore keyStore = getKeyStoreNode().getKeyStore();
+ return keyStore.getCertificateChain(alias);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode()
+ {
+ final int prime = 31;
+ int result = 1;
+ result = (prime * result) + ((alias == null) ? 0 : alias.hashCode());
+ result =
+ (prime * result) + ((getKeyStoreNode() == null) ? 0 : getKeyStoreNode().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 (!(obj instanceof EntryNode))
+ {
+ return false;
+ }
+ EntryNode other = (EntryNode) obj;
+ if (alias == null)
+ {
+ if (other.alias != null)
+ {
+ return false;
+ }
+ }
+ else if (!alias.equals(other.alias))
+ {
+ return false;
+ }
+ if (getKeyStoreNode() == null)
+ {
+ if (other.getKeyStoreNode() != null)
+ {
+ return false;
+ }
+ }
+ else if (!getKeyStoreNode().equals(other.getKeyStoreNode()))
+ {
+ return false;
+ }
+ return true;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString()
+ {
+ return "KeyStoreEntry [alias=" + alias + "]"; //$NON-NLS-1$ //$NON-NLS-2$
+ }
+
+ @Override
+ public void refresh()
+ {
+ // keys does not need to be refreshed
+ }
+
+ @Override
+ public String getId()
+ {
+ return alias;
+ }
+
+ @Override
+ public String getName()
+ {
+ return alias;
+ }
+
+ @Override
+ public ImageDescriptor getIcon()
+ {
+ //decision: we will not support key-pair, so we will have just key items below keystore node.
+ ImageDescriptor descr = null;
+ if (isPasswordSaved())
+ {
+ //saved password
+ descr =
+ CertificateManagerActivator.imageDescriptorFromPlugin(
+ CertificateManagerActivator.PLUGIN_ID, KEY_SAVED_PASSWORD_ICON_PATH);
+ }
+ else
+ {
+ //non saved password
+ descr =
+ CertificateManagerActivator.imageDescriptorFromPlugin(
+ CertificateManagerActivator.PLUGIN_ID, KEY_NONSAVED_PASSWORD_ICON_PATH);
+ }
+ return descr;
+ }
+
+ @Override
+ public boolean isLeaf()
+ {
+ return true;
+ }
+
+ @Override
+ public List<ITreeNode> getChildren()
+ {
+ return new ArrayList<ITreeNode>(0); // it is the leaf of the tree
+ }
+
+ public static IKeyStoreEntry createSelfSignedNode(IKeyStore keystore, String keyStorePass,
+ String alias, CertificateDetailsInfo certificateDetailsInfo)
+ throws KeyStoreManagerException
+ {
+ KeyPair keyPair = null;
+
+ try
+ {
+ keyPair = KeyStoreUtils.genKeyPair();
+ X509Certificate x509Certificate =
+ KeyStoreUtils.createX509Certificate(keyPair, certificateDetailsInfo);
+
+ if (keyStorePass == null)
+ {
+ PasswordProvider provider = new PasswordProvider(keystore.getFile());
+ keyStorePass = provider.getKeyStorePassword(true);
+ }
+
+ PrivateKeyEntry privateKeyEntry =
+ KeyStoreUtils.createPrivateKeyEntry(keyPair, x509Certificate);
+ KeyStoreUtils.addEntry(keystore.getKeyStore(), keyStorePass.toCharArray(), keystore
+ .getFile(), alias, privateKeyEntry, certificateDetailsInfo.getEntryPassword()
+ .toCharArray());
+
+ //force reload - because keystore cache can be old due to key entries additions/removals
+ keystore.forceReload(keyStorePass.toCharArray(), false);
+ }
+ catch (Exception e)
+ {
+ throw new KeyStoreManagerException(e.getMessage(), e);
+ }
+
+ return new EntryNode((ITreeNode) keystore, alias);
+ }
+
+ public static IKeyStoreEntry createSelfSignedNode(IKeyStore keystore, String alias,
+ CertificateDetailsInfo certificateDetailsInfo) throws KeyStoreManagerException
+ {
+ return createSelfSignedNode(keystore, null, alias, certificateDetailsInfo);
+ }
+
+ @Override
+ public boolean testAttribute(Object target, String name, String value)
+ {
+ boolean result = super.testAttribute(target, name, value);
+ if (name.equals(PROP_NAME_NODE_STATUS))
+ {
+ if (value.equals(PROP_VALUE_NODE_STATUS_WARNING))
+ {
+ X509Certificate x509Certificate = getX509Certificate();
+ try
+ {
+ // check validity concerning the current date
+ x509Certificate.checkValidity();
+
+ // now check validity related to magic date provided by
+ // Google
+ Calendar date = GregorianCalendar.getInstance();
+ date.clear();
+ date.set(2033, Calendar.OCTOBER, 22);
+ x509Certificate.checkValidity(date.getTime());
+ }
+ catch (CertificateExpiredException e)
+ {
+ // certificate has expired in the current date; or
+ // certificate has expired before 22 Oct 2033
+ setTooltip(CertificateManagerNLS.bind(
+ CertificateManagerNLS.CertificatePeriodExpired_Issue,
+ x509Certificate.getNotAfter()));
+ result = true; // decorate node
+ }
+ catch (CertificateNotYetValidException e)
+ {
+ // certificate is not yet valid in the current date; or
+ // certificate is not yet valid in 2033 => it must not
+ // happen but we need to deal with this case
+ setTooltip(CertificateManagerNLS.bind(
+ CertificateManagerNLS.CertificatePeriodNotYeatValid_Issue,
+ x509Certificate.getNotBefore()));
+ result = true; // decorate node
+ }
+ }
+ }
+ return result;
+
+ }
+
+ /**
+ * Get the first X509Certificate available in the entry
+ *
+ * @return
+ */
+ @Override
+ public X509Certificate getX509Certificate()
+ {
+ X509Certificate x509Certificate = null;
+ try
+ {
+ if (isCertificateEntry())
+ {
+ Certificate cert = getCertificate();
+ if (cert instanceof X509Certificate)
+ {
+ // Android certificate
+ x509Certificate = (X509Certificate) cert;
+ }
+ }
+ else if (isKeyEntry())
+ {
+ Certificate[] chain = getCertificateChain();
+ for (int i = 0; i < chain.length; i++)
+ {
+ Certificate cert = chain[i];
+ if (cert instanceof X509Certificate)
+ {
+ // Android certificate
+ x509Certificate = (X509Certificate) cert;
+ }
+ }
+ }
+ }
+ catch (Exception e)
+ {
+ StudioLogger.error(EntryNode.class, CertificateManagerNLS.bind(
+ CertificateManagerNLS.EntryNode_ErrorGettingCertificateFromEntry, getAlias()),
+ e);
+ }
+ return x509Certificate;
+ }
+
+ @Override
+ protected boolean isPasswordSaved()
+ {
+ PasswordProvider pp = new PasswordProvider(getKeyStoreNode().getFile());
+ return pp.isPasswordSaved(alias);
+ }
+}
diff --git a/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/model/IKeyStore.java b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/model/IKeyStore.java
new file mode 100644
index 0000000..53dcc5e
--- /dev/null
+++ b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/model/IKeyStore.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.studio.android.certmanager.ui.model;
+
+import java.io.File;
+import java.security.KeyStore;
+import java.util.Date;
+import java.util.List;
+
+import com.motorolamobility.studio.android.certmanager.core.PasswordProvider;
+import com.motorolamobility.studio.android.certmanager.exception.InvalidPasswordException;
+import com.motorolamobility.studio.android.certmanager.exception.KeyStoreManagerException;
+
+public interface IKeyStore
+{
+
+ public int KEYSTORE_PASSWORD_MIN_SIZE = 6;
+
+ public int WRONG_KEYSTORE_TYPE_ERROR_CODE = 1;
+
+ PasswordProvider getPasswordProvider();
+
+ List<String> getAliases(String password) throws KeyStoreManagerException,
+ InvalidPasswordException;
+
+ /**
+ * Returns the {@link KeyStore}
+ * @throws KeyStoreManagerException
+ */
+ KeyStore getKeyStore() throws KeyStoreManagerException;
+
+ /**
+ * Load if needed and returns all entries for this KeyStore
+ * @param password
+ * @return
+ * @throws KeyStoreManagerException
+ * @throws InvalidPasswordException
+ */
+ List<IKeyStoreEntry> getEntries(String password) throws KeyStoreManagerException,
+ InvalidPasswordException;
+
+ /**
+ * Load if needed and returns the entry with the given alias.
+ * @param alias The alias of the desired entry.
+ * @param keystorePassword the password of the keystore
+ * @return The desired entry.
+ * @throws KeyStoreManagerException
+ * @throws InvalidPasswordException
+ */
+ IKeyStoreEntry getEntry(String alias, String keystorePassword) throws KeyStoreManagerException,
+ InvalidPasswordException;
+
+ /**
+ * @param password from keystore
+ * Forces keystore reload
+ * @throws KeyStoreManagerException
+ * @throws InvalidPasswordException
+ */
+ void forceReload(char[] charArray, boolean updateUi) throws KeyStoreManagerException,
+ InvalidPasswordException;
+
+ /**
+ * Returns this key store file
+ * @return
+ */
+ File getFile();
+
+ /**
+ * @return this keystore type
+ */
+ String getType();
+
+ /**
+ * Set this keystore type. This is intended to be used only during creation.
+ * This method won't change the keystore type or convert it to another type.
+ * @param type
+ * @throws KeyStoreManagerException
+ */
+ void setType(String type) throws KeyStoreManagerException;
+
+ /**
+ * Set the backup date for this keystore
+ * @param lastBackupDate
+ */
+ void setLastBackupDate(Date lastBackupDate);
+
+ /**
+ * Gets the last backup date for this keystore
+ * @return null if not backed up yet, a date otherwise
+ */
+ Date getLastBackupDate();
+
+ /**
+ * Deletes a key entry with the given alias
+ * @param alias The alias representing the key to be removed.
+ * @throws KeyStoreManagerException
+ */
+ void removeKey(String alias) throws KeyStoreManagerException;
+
+ /**
+ * Deletes a list of key entries from the keystore.
+ * @param aliases The list of aliases representing the keys to be removed.
+ * @throws KeyStoreManagerException
+ */
+ void removeKeys(List<String> aliases) throws KeyStoreManagerException;
+
+ /**
+ * @param password
+ */
+ public boolean isPasswordValid(String password) throws KeyStoreManagerException,
+ InvalidPasswordException;
+
+ /**
+ * Return the password of the keystore.
+ * If the password is not saved and {@code promptPassword} is set to {@code true}, then a dialog will be opened so the user can enter the password.
+ * @return Return the password of the keystore.
+ */
+ String getKeyStorePassword(boolean promptPassword);
+}
diff --git a/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/model/IKeyStoreEntry.java b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/model/IKeyStoreEntry.java
new file mode 100644
index 0000000..7db617b
--- /dev/null
+++ b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/model/IKeyStoreEntry.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.studio.android.certmanager.ui.model;
+
+import java.security.InvalidKeyException;
+import java.security.Key;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.UnrecoverableKeyException;
+import java.security.cert.X509Certificate;
+
+import com.motorolamobility.studio.android.certmanager.exception.KeyStoreManagerException;
+
+public interface IKeyStoreEntry
+{
+ /**
+ * @return {@link Key} if the alias represents the private key associated to this entry
+ * or null if the alias was not found (or if the type is not Key for the alias)
+ * @throws NoSuchAlgorithmException if no algorithm to recover key not found
+ * @throws KeyStoreException if keystore not loaded yet
+ * @throws UnrecoverableKeyException if wrong password for the key
+ * @throws KeyStoreManagerException
+ */
+ Key getKey(String password) throws UnrecoverableKeyException, KeyStoreException,
+ NoSuchAlgorithmException, KeyStoreManagerException;
+
+ /**
+ * @return The key represented by this node as a PrivateKey.
+ * @throws NoSuchAlgorithmException if no algorithm to recover key not found
+ * @throws KeyStoreException if keystore not loaded yet
+ * @throws UnrecoverableKeyException if wrong password for the key
+ * @throws KeyStoreManagerException
+ * @throws InvalidKeyException If this key is not a {@link PrivateKey}.
+ * */
+ PrivateKey getPrivateKey(String password) throws UnrecoverableKeyException, KeyStoreException,
+ NoSuchAlgorithmException, KeyStoreManagerException, InvalidKeyException;
+
+ /**
+ * @return true if this entry contains a private key
+ * @throws KeyStoreException if the keystore has not been initialized (loaded).
+ * @throws KeyStoreManagerException
+ */
+ boolean isKeyEntry() throws KeyStoreException, KeyStoreManagerException;
+
+ /**
+ * @return true if this entry contains a certificate
+ * @throws KeyStoreException if the keystore has not been initialized (loaded).
+ * @throws KeyStoreManagerException
+ */
+ boolean isCertificateEntry() throws KeyStoreException, KeyStoreManagerException;
+
+ /**
+ * @return true if this entry is a key pair
+ */
+ boolean isKeyPairEntry();
+
+ /**
+ * @return the alias
+ */
+ String getAlias();
+
+ /**
+ * Get the first X509Certificate available in the entry
+ * @return
+ */
+ X509Certificate getX509Certificate();
+
+ /**
+ * @return The keystore node that holds this entry.
+ * */
+ public IKeyStore getKeyStoreNode();
+
+}
diff --git a/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/model/ITreeNode.java b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/model/ITreeNode.java
new file mode 100644
index 0000000..5edf9de
--- /dev/null
+++ b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/model/ITreeNode.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.studio.android.certmanager.ui.model;
+
+import java.security.KeyStoreException;
+import java.util.List;
+
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.ui.IActionFilter;
+
+import com.motorolamobility.studio.android.certmanager.exception.KeyStoreManagerException;
+
+public interface ITreeNode extends IActionFilter
+{
+
+ public static final String PROP_VALUE_NODE_STATUS_OK =
+ "com.motorolamobility.studio.android.certmanager.core.property.nodeStatusOk"; //$NON-NLS-1$
+
+ public static final String PROP_VALUE_NODE_STATUS_KEYSTORE_TYPE_OK =
+ "com.motorolamobility.studio.android.certmanager.core.property.keystoreTypeOk"; //$NON-NLS-1$
+
+ /**
+ * Property value used to check if the node has an error status.
+ */
+ public static final String PROP_VALUE_NODE_STATUS_ERROR =
+ "com.motorolamobility.studio.android.certmanager.core.property.nodeStatusError"; //$NON-NLS-1$
+
+ public static final String PROP_VALUE_NODE_STATUS_WARNING =
+ "com.motorolamobility.studio.android.certmanager.core.property.nodeStatusWarning"; //$NON-NLS-1$
+
+ /**
+ * Property name used to test the status of the node.
+ */
+ public static final String PROP_NAME_NODE_STATUS =
+ "com.motorolamobility.studio.android.certmanager.core.property.nodeStatus"; //$NON-NLS-1$
+
+ public static final String PROP_NAMESPACE =
+ "com.motorolamobility.studio.android.certmanager.core.property";
+
+ /**
+ * Method responsible to reload the node itself and its children
+ */
+ void refresh() throws KeyStoreManagerException;
+
+ /**
+ * @return the id
+ */
+ String getId();
+
+ /**
+ * @return the name
+ */
+ String getName();
+
+ /**
+ * @return the icon
+ */
+ ImageDescriptor getIcon();
+
+ /**
+ * @return true if it does not accept a child, false otherwise
+ */
+ boolean isLeaf();
+
+ /**
+ * Set the node Status, allowing the tree to decorate itself on errors.
+ * Is status is ERROR the icon will be decorated with a error image and tooltip will be replaced by status.getMessage() if available.
+ * @param status
+ */
+ void setNodeStatus(IStatus status);
+
+ /**
+ * Retrieves the current node status.
+ * @return
+ */
+ IStatus getNodeStatus();
+
+ /**
+ * Set the tooltip to be displayed for this node.
+ * @param tooltip
+ */
+ void setTooltip(String tooltip);
+
+ /**
+ * @return this node tooltip text
+ */
+ String getTooltip();
+
+ /**
+ * Get parent of the tree node
+ * @return null if it is the tree root, non-null if is a child node
+ */
+ ITreeNode getParent();
+
+ /**
+ * Retrieves list of children (without any filter)
+ * @return collection of {@link ITreeNode} that are child of this abstract tree node
+ * @throws KeyStoreException
+ * @throws KeyStoreManagerException
+ */
+ List<ITreeNode> getChildren() throws KeyStoreManagerException;
+
+ void addChild(ITreeNode newChild) throws KeyStoreManagerException;
+}
diff --git a/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/model/KeyStoreNode.java b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/model/KeyStoreNode.java
new file mode 100644
index 0000000..884fe84
--- /dev/null
+++ b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/model/KeyStoreNode.java
@@ -0,0 +1,786 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.studio.android.certmanager.ui.model;
+
+import java.io.File;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.Enumeration;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.osgi.util.NLS;
+
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.common.preferences.DialogWithToggleUtils;
+import com.motorola.studio.android.common.utilities.EclipseUtils;
+import com.motorolamobility.studio.android.certmanager.CertificateManagerActivator;
+import com.motorolamobility.studio.android.certmanager.core.KeyStoreManager;
+import com.motorolamobility.studio.android.certmanager.core.KeyStoreUtils;
+import com.motorolamobility.studio.android.certmanager.core.PasswordProvider;
+import com.motorolamobility.studio.android.certmanager.event.KeyStoreModelEvent.EventType;
+import com.motorolamobility.studio.android.certmanager.event.KeyStoreModelEventManager;
+import com.motorolamobility.studio.android.certmanager.exception.InvalidPasswordException;
+import com.motorolamobility.studio.android.certmanager.exception.KeyStoreManagerException;
+import com.motorolamobility.studio.android.certmanager.i18n.CertificateManagerNLS;
+import com.motorolamobility.studio.android.certmanager.views.KeystoreManagerView;
+
+/**
+ * Represents a keystore visual item for the {@link KeystoreManagerView}.
+ *
+ * @author gdpr78
+ *
+ */
+public class KeyStoreNode extends AbstractTreeNode implements IKeyStore
+{
+ public static final String WARN_ABOUT_UNSUPPORTED_ENTRIES_PREFERENCE =
+ CertificateManagerActivator.PLUGIN_ID + ".warnAboutUnsupportedEntries"; //$NON-NLS-1$
+
+ private static final String DUMMY_NODE = "DUMMY_NODE"; //$NON-NLS-1$
+
+ private final File keyStoreFile;
+
+ private KeyStore keyStore;
+
+ private Date lastBackupDate;
+
+ private String type;
+
+ /**
+ * Alias to {@link EntryNode}
+ */
+ private final Map<String, ITreeNode> entries = new LinkedHashMap<String, ITreeNode>();
+
+ private final String KEYSTORE_NONSAVED_PASSWORD_ICON_PATH = "icons/keystore.png"; //$NON-NLS-1$
+
+ private final String KEYSTORE_SAVED_PASSWORD_ICON_PATH = "icons/keystore_saved_password.png"; //$NON-NLS-1$
+
+ private static final String WRONG_KEYSTORE_TYPE_ICON_PATH = "icons/keystore_incorrect_type.png";
+
+ private final PasswordProvider passwordProvider;
+
+ private boolean ignoreRefresh;
+
+ private boolean quiet;
+
+ private boolean skipNextReload = false;
+
+ private boolean typeVerified;
+
+ public KeyStoreNode(File path)
+ {
+ this.keyStoreFile = path;
+ passwordProvider = new PasswordProvider(keyStoreFile);
+ updateStatus();
+ }
+
+ public KeyStoreNode(File path, String type)
+ {
+ this.keyStoreFile = path;
+ this.type = type;
+ passwordProvider = new PasswordProvider(keyStoreFile);
+ updateStatus();
+ }
+
+ public KeyStoreNode(File keyStoreFile, KeyStore keyStore)
+ {
+ this(keyStoreFile);
+ this.keyStore = keyStore;
+ this.type = keyStore.getType();
+ }
+
+ @Override
+ public PasswordProvider getPasswordProvider()
+ {
+ return passwordProvider;
+ }
+
+ @Override
+ public String getKeyStorePassword(boolean promptPassword)
+ {
+ String password = null;
+ boolean keepTrying = true;
+
+ //keep asking password until user either enter the correct password or cancel the operation
+ while (keepTrying)
+ {
+ try
+ {
+ try
+ {
+ keepTrying = false;
+ password = getPasswordProvider().getKeyStorePassword(promptPassword);
+ if (password != null)
+ {
+ isPasswordValid(password);
+ }
+ }
+ catch (InvalidPasswordException e)
+ {
+ getPasswordProvider().deleteKeyStoreSavedPasswordNode();
+ password = null;
+ keepTrying = true;
+ }
+ }
+ catch (KeyStoreManagerException e)
+ {
+ password = null;
+ keepTrying = false;
+
+ StudioLogger.info(
+ this.getClass(),
+ CertificateManagerNLS.KeyStoreNode_CouldNotGetKeyStorePassword
+ + e.getLocalizedMessage());
+ }
+ }
+
+ return password;
+ }
+
+ /**
+ * @return the path
+ */
+ @Override
+ public File getFile()
+ {
+ return keyStoreFile;
+ }
+
+ @Override
+ public KeyStore getKeyStore() throws KeyStoreManagerException
+ {
+ return getKeyStore(true);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.motorolamobility.studio.android.certmanager.ui.model.IKeyStore#
+ * getKeyStore()
+ */
+ public KeyStore getKeyStore(boolean load) throws KeyStoreManagerException
+ {
+ if (keyStore == null)
+ {
+ boolean tryAgain = false;
+ boolean useSavedPass = true;
+ String password = null;
+ do
+ {
+ if (tryAgain)
+ {
+ useSavedPass = false;
+ }
+ password = passwordProvider.getKeyStorePassword(true, useSavedPass);
+ tryAgain = false;
+ if (password != null)
+ {
+ try
+ {
+ keyStore = loadKeystore(password.toCharArray());
+ setTooltip(null);
+ if (load)
+ {
+ loadEntries();
+ }
+ }
+ catch (InvalidPasswordException e)
+ {
+ tryAgain = true;
+ }
+ }
+ else
+ {
+ setTooltip(CertificateManagerNLS.KeyStoreNode_CouldNotLoadKeystore_Tooltip);
+ }
+ }
+ while (tryAgain);
+ }
+
+ return keyStore;
+ }
+
+ public KeyStore getKeyStore(String password) throws KeyStoreManagerException,
+ InvalidPasswordException
+ {
+ if ((keyStore == null) && (password != null))
+ {
+ keyStore = loadKeystore(password.toCharArray());
+ loadEntries();
+ }
+ else
+ {
+ //just check if given password is valid for this keystore
+ isPasswordValid(password);
+ }
+
+ return keyStore;
+ }
+
+ @Override
+ public boolean isPasswordValid(String password) throws KeyStoreManagerException,
+ InvalidPasswordException
+ {
+ KeyStore myKeyStore = null;
+ if (password != null)
+ {
+ myKeyStore = loadKeystore(password.toCharArray());
+ }
+ else
+ {
+ throw new InvalidPasswordException(CertificateManagerNLS.KeyStoreNode_Password_NotNull);
+ }
+
+ return myKeyStore != null;
+ }
+
+ protected KeyStore loadKeystore(char[] password) throws KeyStoreManagerException,
+ InvalidPasswordException
+ {
+ KeyStore keyStore = null;
+ setNodeStatus(Status.OK_STATUS);
+ setTooltip(null);
+ try
+ {
+ if (!typeVerified && type.equalsIgnoreCase("jceks")) //$NON-NLS-1$
+ {
+ //Try to load this as JKS.
+ keyStore = KeyStoreUtils.loadKeystore(keyStoreFile, password, "JKS"); //$NON-NLS-1$
+ if (keyStore != null)
+ {
+ //Keystore type is actually wrong, it's a jks keystore.
+ EclipseUtils.showWarningDialog(
+ CertificateManagerNLS.KeyStoreNode_Wrong_KeystoreType_Title, NLS.bind(
+ CertificateManagerNLS.KeyStoreNode_Wrong_KeystoreType_Message,
+ getName()));
+ setType("JKS"); //$NON-NLS-1$
+ typeVerified = true;
+ }
+ }
+ }
+ catch (KeyStoreManagerException keyStoreManagerException)
+ {
+ //Do nothing, let's try with the correct type.
+ }
+ catch (InvalidPasswordException invalidPasswordException)
+ {
+ setNodeStatus(new Status(IStatus.ERROR, CertificateManagerActivator.PLUGIN_ID,
+ CertificateManagerNLS.KeyStoreNode_InvalidPassword));
+ throw invalidPasswordException;
+ }
+
+ try
+ {
+ keyStore = KeyStoreUtils.loadKeystore(keyStoreFile, password, type);
+ setNodeStatus(Status.OK_STATUS);
+ }
+ catch (KeyStoreManagerException keyStoreManagerException)
+ {
+ setNodeStatus(new Status(IStatus.ERROR, CertificateManagerActivator.PLUGIN_ID,
+ IKeyStore.WRONG_KEYSTORE_TYPE_ERROR_CODE,
+ CertificateManagerNLS.KeyStoreNode_KeystoreTypeWrong_NodeStatus, null));
+ throw keyStoreManagerException;
+ }
+ catch (InvalidPasswordException invalidPasswordException)
+ {
+ setNodeStatus(new Status(IStatus.ERROR, CertificateManagerActivator.PLUGIN_ID,
+ CertificateManagerNLS.KeyStoreNode_InvalidPassword));
+ throw invalidPasswordException;
+ }
+ return keyStore;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode()
+ {
+ final int prime = 31;
+ int result = 1;
+ result = (prime * result) + ((keyStoreFile == null) ? 0 : keyStoreFile.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 (!(obj instanceof KeyStoreNode))
+ {
+ return false;
+ }
+ KeyStoreNode other = (KeyStoreNode) obj;
+ if (keyStoreFile == null)
+ {
+ if (other.keyStoreFile != null)
+ {
+ return false;
+ }
+ }
+ else if (!keyStoreFile.equals(other.keyStoreFile))
+ {
+ return false;
+ }
+ return true;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString()
+ {
+ return getName() + " - ( " + getId() + " )"; //$NON-NLS-1$ //$NON-NLS-2$
+ }
+
+ @Override
+ public void refresh() throws KeyStoreManagerException
+ {
+ if (!ignoreRefresh)
+ {
+ if (!skipNextReload)
+ {
+ keyStore = null;
+ skipNextReload = false;
+ }
+ entries.clear();
+ updateStatus();
+ if (getNodeStatus().isOK())
+ {
+ quiet = true;
+ loadEntries();
+ quiet = false;
+ passwordProvider.cleanModel(new ArrayList<String>(entries.keySet()));
+ }
+ }
+ else
+ {
+ setIgnoreRefresh(false);
+ }
+ }
+
+ private void setIgnoreRefresh(boolean ignoreRefresh)
+ {
+ this.ignoreRefresh = ignoreRefresh;
+ }
+
+ @Override
+ public String getId()
+ {
+ return keyStoreFile.getAbsolutePath();
+ }
+
+ @Override
+ public String getName()
+ {
+ return keyStoreFile.getName();
+ }
+
+ @Override
+ public ImageDescriptor getIcon()
+ {
+ ImageDescriptor descr = null;
+ if (!isStoreTypeCorrect())
+ {
+ //wrong keystore type
+ descr =
+ CertificateManagerActivator.imageDescriptorFromPlugin(
+ CertificateManagerActivator.PLUGIN_ID, WRONG_KEYSTORE_TYPE_ICON_PATH);
+ }
+ else if (isPasswordSaved())
+ {
+ //saved password
+ descr =
+ CertificateManagerActivator.imageDescriptorFromPlugin(
+ CertificateManagerActivator.PLUGIN_ID,
+ KEYSTORE_SAVED_PASSWORD_ICON_PATH);
+ }
+ else
+ {
+ //non saved password
+ descr =
+ CertificateManagerActivator.imageDescriptorFromPlugin(
+ CertificateManagerActivator.PLUGIN_ID,
+ KEYSTORE_NONSAVED_PASSWORD_ICON_PATH);
+ }
+ return descr;
+ }
+
+ @Override
+ public boolean isLeaf()
+ {
+ return false;
+ }
+
+ @Override
+ public List<ITreeNode> getChildren() throws KeyStoreManagerException
+ {
+ ArrayList<ITreeNode> children = new ArrayList<ITreeNode>(entries.values());
+ return children;
+ }
+
+ private void loadEntries() throws KeyStoreManagerException
+ {
+ if (entries.size() == 1)
+ {
+ ITreeNode entryNode = entries.get(DUMMY_NODE); //$NON-NLS-1$
+ if (entryNode != null)
+ {
+ entries.remove(DUMMY_NODE); //$NON-NLS-1$
+ KeyStoreModelEventManager.getInstance().fireEvent(entryNode, EventType.REMOVE);
+ }
+ }
+ entries.clear();
+ KeyStore keyStore = getKeyStore(false);
+ if (keyStore != null)
+ {
+ Enumeration<String> aliases;
+ try
+ {
+ aliases = keyStore.aliases();
+ }
+ catch (KeyStoreException e)
+ {
+ throw new KeyStoreManagerException(CertificateManagerNLS.bind(
+ CertificateManagerNLS.KeyStoreModel_Error_GettingAliasesFromKeystore,
+ getName()), e);
+ }
+
+ List<String> keyPairEntries = new ArrayList<String>();
+ while (aliases.hasMoreElements())
+ {
+ String alias = aliases.nextElement();
+ EntryNode keyStoreEntry = new EntryNode(this, alias);
+ if (!keyStoreEntry.isKeyPairEntry())
+ {
+ //we will not support key pairs
+ entries.put(alias, keyStoreEntry);
+ }
+ else
+ {
+ //is key pair
+ keyPairEntries.add(alias);
+ String msg =
+ CertificateManagerNLS.bind(
+ CertificateManagerNLS.KeyStoreNode_KeyPairNotMapped_LogMessage,
+ alias);
+ StudioLogger.debug(msg);
+ }
+ }
+ if ((keyPairEntries != null) && !keyPairEntries.isEmpty())
+ {
+ //found key pairs
+ DialogWithToggleUtils.showInformation(WARN_ABOUT_UNSUPPORTED_ENTRIES_PREFERENCE,
+ CertificateManagerNLS.KeyStoreNode_KeyPairNotMapped_Title,
+ CertificateManagerNLS.KeyStoreNode_KeyPairNotMapped_Message);
+ }
+
+ if (entries.isEmpty())
+ {
+ entries.put(DUMMY_NODE, new EntryDummyNode(this)); //$NON-NLS-1$
+ }
+ }
+ else
+ {
+ setNodeStatus(new Status(IStatus.ERROR, CertificateManagerActivator.PLUGIN_ID,
+ CertificateManagerNLS.KeyStoreNode_UseRefresh_StatusNode));
+ }
+ }
+
+ private void updateStatus()
+ {
+ setNodeStatus(Status.OK_STATUS);
+ if (!keyStoreFile.exists())
+ {
+ setNodeStatus(new Status(IStatus.ERROR, CertificateManagerActivator.PLUGIN_ID,
+ CertificateManagerNLS.KeyStoreNode_KeystoreFileNotFound));
+ }
+ }
+
+ @Override
+ public void addChild(ITreeNode newChild)
+ {
+ if (entries.size() == 1)
+ {
+ ITreeNode entryNode = entries.get(DUMMY_NODE); //$NON-NLS-1$
+ if (entryNode != null)
+ {
+ entries.remove(DUMMY_NODE); //$NON-NLS-1$
+ KeyStoreModelEventManager.getInstance().fireEvent(entryNode, EventType.REMOVE);
+ }
+ }
+ if ((newChild instanceof IKeyStoreEntry) || (newChild instanceof EntryDummyNode))
+ {
+ EntryNode entryNode = (EntryNode) newChild;
+ String alias = entryNode.getAlias();
+ entries.put(alias, entryNode);
+ if (!quiet && !(newChild instanceof EntryDummyNode))
+ {
+ KeyStoreModelEventManager.getInstance().fireEvent(newChild, EventType.ADD);
+ }
+ }
+ }
+
+ /**
+ * @return the lastBackupDate
+ */
+ @Override
+ public Date getLastBackupDate()
+ {
+ return lastBackupDate;
+ }
+
+ /**
+ * @param lastBackupDate
+ * the lastBackupDate to set
+ */
+ @Override
+ public void setLastBackupDate(Date lastBackupDate)
+ {
+ this.lastBackupDate = lastBackupDate;
+ try
+ {
+ KeyStoreManager.getInstance().setBackupDate(this, lastBackupDate);
+ }
+ catch (KeyStoreManagerException e)
+ {
+ StudioLogger.error("Could not set backup date for keystore");
+ }
+ KeyStoreModelEventManager.getInstance().fireEvent(this, EventType.UPDATE);
+ }
+
+ /**
+ * @return the type
+ */
+ @Override
+ public String getType()
+ {
+ return type != null ? type : KeyStore.getDefaultType().toUpperCase();
+ }
+
+ /**
+ * @param type
+ * the type to set
+ * @throws KeyStoreManagerException
+ */
+ @Override
+ public void setType(String type) throws KeyStoreManagerException
+ {
+ this.type = type;
+ KeyStoreManager.getInstance().updateKeyStoreType(this);
+ }
+
+ @Override
+ public List<IKeyStoreEntry> getEntries(String password) throws KeyStoreManagerException,
+ InvalidPasswordException
+ {
+ getKeyStore(password);
+ ArrayList<IKeyStoreEntry> children = new ArrayList<IKeyStoreEntry>(entries.size());
+ for (ITreeNode treeNode : entries.values())
+ {
+ if (treeNode instanceof IKeyStoreEntry)
+ {
+ children.add((IKeyStoreEntry) treeNode);
+ }
+ }
+ return children;
+ }
+
+ @Override
+ public IKeyStoreEntry getEntry(String alias, String keystorePassword)
+ throws KeyStoreManagerException, InvalidPasswordException
+ {
+ IKeyStoreEntry result = null;
+ for (IKeyStoreEntry entry : getEntries(keystorePassword))
+ {
+ if (entry.getAlias().equalsIgnoreCase(alias))
+ {
+ result = entry;
+ }
+ }
+
+ return result;
+ }
+
+ @Override
+ public List<String> getAliases(String password) throws KeyStoreManagerException,
+ InvalidPasswordException
+ {
+ getKeyStore(password);
+
+ ArrayList<String> children = new ArrayList<String>(entries.size());
+ for (ITreeNode treeNode : entries.values())
+ {
+ if (treeNode instanceof IKeyStoreEntry)
+ {
+ children.add(((IKeyStoreEntry) treeNode).getAlias());
+ }
+ }
+ return children;
+ }
+
+ @Override
+ public void removeKey(String alias) throws KeyStoreManagerException
+ {
+ String password = passwordProvider.getKeyStorePassword(true, true);
+ if (password != null)
+ {
+ KeyStoreUtils.deleteEntry(keyStore, password.toCharArray(), keyStoreFile, alias);
+ try
+ {
+ forceReload(password.toCharArray(), false);
+ }
+ catch (InvalidPasswordException e)
+ {
+ //Should never happen.
+ StudioLogger.debug("Could reload ks after removing entry, invalid password"); //$NON-NLS-1$
+ }
+
+ ITreeNode entryNode = entries.remove(alias);
+ KeyStoreModelEventManager.getInstance().fireEvent(entryNode, EventType.REMOVE);
+ if (entries.isEmpty())
+ {
+ EntryDummyNode entryDummyNode = new EntryDummyNode(this);
+ entries.put(DUMMY_NODE, entryDummyNode); //$NON-NLS-1$
+ KeyStoreModelEventManager.getInstance().fireEvent(entryDummyNode, EventType.ADD);
+ }
+ }
+ else
+ {
+ // password not found
+ throw new KeyStoreManagerException(
+ CertificateManagerNLS.KeyStoreNode_NotFoundOrIncorrectPasswordToDeleteEntry
+ + alias);
+ }
+
+ }
+
+ @Override
+ public void removeKeys(List<String> aliases) throws KeyStoreManagerException
+ {
+ String password = passwordProvider.getKeyStorePassword(true, true);
+ if (password != null)
+ {
+ for (String alias : aliases)
+ {
+ KeyStoreUtils.deleteEntry(keyStore, password.toCharArray(), keyStoreFile, alias);
+
+ ITreeNode entryNode = entries.remove(alias);
+ KeyStoreModelEventManager.getInstance().fireEvent(entryNode, EventType.REMOVE);
+ }
+ try
+ {
+ forceReload(password.toCharArray(), false);
+ }
+ catch (InvalidPasswordException e)
+ {
+ //Should never happen.
+ StudioLogger.debug("Could reload ks after removing entry, invalid password"); //$NON-NLS-1$
+ }
+ if (entries.isEmpty())
+ {
+ EntryDummyNode entryDummyNode = new EntryDummyNode(this);
+ entries.put(DUMMY_NODE, entryDummyNode); //$NON-NLS-1$
+ KeyStoreModelEventManager.getInstance().fireEvent(entryDummyNode, EventType.ADD);
+ }
+ }
+ else
+ {
+ // password not found
+ throw new KeyStoreManagerException(
+ CertificateManagerNLS.KeyStoreNode_IncorrectPasswordToDeleteEntries_Error);
+ }
+
+ }
+
+ @Override
+ public boolean testAttribute(Object target, String name, String value)
+ {
+ boolean result = super.testAttribute(target, name, value);
+ if (name.equals(PROP_NAME_NODE_STATUS))
+ {
+ if (value.equals(PROP_VALUE_NODE_STATUS_ERROR))
+ {
+ if (!isStoreTypeCorrect())
+ {
+ //when store type is incorrect the icon is changed, not decorated.
+ result = false;
+ }
+ else if (!keyStoreFile.exists())
+ {
+ // keystore not found
+ result = true;
+ setTooltip(CertificateManagerNLS.KeyStoreNode_ErrorKeystoreNotFound);
+ }
+ }
+ else if (value.equals(PROP_VALUE_NODE_STATUS_KEYSTORE_TYPE_OK))
+ {
+ result = isStoreTypeCorrect();
+ }
+
+ }
+ return result;
+ }
+
+ @Override
+ public void forceReload(char[] password, boolean updateUi) throws KeyStoreManagerException,
+ InvalidPasswordException
+ {
+ keyStore = loadKeystore(password);
+
+ if (updateUi)
+ {
+ skipNextReload = true;
+ KeyStoreModelEventManager.getInstance().fireEvent(this, EventType.REFRESH);
+ }
+ }
+
+ @Override
+ protected boolean isPasswordSaved()
+ {
+ PasswordProvider pp = new PasswordProvider(getFile());
+ return pp.isPasswordSaved();
+ }
+
+ protected boolean isStoreTypeCorrect()
+ {
+ return getNodeStatus().getCode() != WRONG_KEYSTORE_TYPE_ERROR_CODE;
+ }
+
+}
diff --git a/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/model/KeyStoreRootNode.java b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/model/KeyStoreRootNode.java
new file mode 100644
index 0000000..5c07df7
--- /dev/null
+++ b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/model/KeyStoreRootNode.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.studio.android.certmanager.ui.model;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.jface.resource.ImageDescriptor;
+
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorolamobility.studio.android.certmanager.core.PasswordProvider;
+import com.motorolamobility.studio.android.certmanager.event.KeyStoreModelEvent;
+import com.motorolamobility.studio.android.certmanager.event.KeyStoreModelEventManager;
+import com.motorolamobility.studio.android.certmanager.exception.KeyStoreManagerException;
+import com.motorolamobility.studio.android.certmanager.i18n.CertificateManagerNLS;
+import com.motorolamobility.studio.android.certmanager.views.KeystoreManagerView;
+
+/**
+ * Node that is the parent of keyStores (root node of {@link KeystoreManagerView})
+ */
+public class KeyStoreRootNode extends AbstractTreeNode
+{
+ //Map from absolute file path to KeyStoreModel
+ private final List<ITreeNode> keyStores = new ArrayList<ITreeNode>();
+
+ /**
+ * Adds keystore to root node of the tree
+ * @param keyStoreModel
+ * @throws KeyStoreManagerException if the keystore is already listed in the tree
+ */
+ public void addKeyStoreNode(KeyStoreNode keyStoreModel) throws KeyStoreManagerException
+ {
+ if (!keyStores.contains(keyStoreModel))
+ {
+ keyStores.add(keyStoreModel);
+ keyStoreModel.setParent(this);
+
+ KeyStoreModelEventManager.getInstance().fireEvent(keyStoreModel,
+ KeyStoreModelEvent.EventType.ADD);
+ }
+ else
+ {
+ //error - notify
+ throw new KeyStoreManagerException(CertificateManagerNLS.bind(
+ CertificateManagerNLS.KeyStoreRootNode_Error_AlreadyMappedKeystorePath,
+ keyStoreModel.getFile().getAbsolutePath()));
+ }
+ }
+
+ public void removeKeyStore(KeyStoreNode keyStoreModel)
+ {
+ keyStores.remove(keyStoreModel);
+ KeyStoreModelEventManager.getInstance().fireEvent(keyStoreModel,
+ KeyStoreModelEvent.EventType.REMOVE);
+ File keysToreFile = keyStoreModel.getFile();
+
+ PasswordProvider password = new PasswordProvider(keysToreFile);
+
+ try
+ {
+ password.deleteKeyStoreSavedPasswordNode();
+ }
+ catch (KeyStoreManagerException e)
+ {
+ StudioLogger.error("Error while accessing keystore manager. " + e.getMessage());
+ }
+
+ }
+
+ @Override
+ public void refresh()
+ {
+ //Not necessary, root nod can't be refreshed
+ }
+
+ @Override
+ public String getId()
+ {
+ return ""; //not necessary - root node //$NON-NLS-1$
+ }
+
+ @Override
+ public String getName()
+ {
+ return ""; //not necessary - root node //$NON-NLS-1$
+ }
+
+ @Override
+ public ImageDescriptor getIcon()
+ {
+ return null; //not necessary - root node
+ }
+
+ @Override
+ public boolean isLeaf()
+ {
+ return false; //root node
+ }
+
+ @Override
+ public ITreeNode getParent()
+ {
+ return null; //invisible node
+ }
+
+ @Override
+ public List<ITreeNode> getChildren()
+ {
+ return new ArrayList<ITreeNode>(keyStores);
+ }
+
+}
diff --git a/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/model/SigningAndKeysModelManager.java b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/model/SigningAndKeysModelManager.java
new file mode 100644
index 0000000..d4493f6
--- /dev/null
+++ b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/model/SigningAndKeysModelManager.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.studio.android.certmanager.ui.model;
+
+import java.io.File;
+import java.util.List;
+
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.osgi.util.NLS;
+
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.common.utilities.EclipseUtils;
+import com.motorolamobility.studio.android.certmanager.CertificateManagerActivator;
+import com.motorolamobility.studio.android.certmanager.core.BackwardKeystoreManager;
+import com.motorolamobility.studio.android.certmanager.core.KeyStoreManager;
+import com.motorolamobility.studio.android.certmanager.core.PasswordProvider;
+import com.motorolamobility.studio.android.certmanager.exception.KeyStoreManagerException;
+import com.motorolamobility.studio.android.certmanager.i18n.CertificateManagerNLS;
+import com.motorolamobility.studio.android.certmanager.views.KeystoreManagerView;
+
+/**
+ * Provides services to map and unmap keystores.
+ * Also get access to the root node and populates on first access (based on {@link KeyStoreManager}).
+ *
+ */
+public class SigningAndKeysModelManager
+{
+ private KeyStoreRootNode keyStoresRootNode = new KeyStoreRootNode();
+
+ private static SigningAndKeysModelManager _instance = null;
+
+ private SigningAndKeysModelManager()
+ {
+ }
+
+ public static synchronized SigningAndKeysModelManager getInstance()
+ {
+ if (_instance == null)
+ {
+ _instance = new SigningAndKeysModelManager();
+ _instance.populateKeyStoreRootNode();
+ }
+ return _instance;
+ }
+
+ public File[] getKeystoreFiles()
+ {
+ List<ITreeNode> nodes = keyStoresRootNode.getChildren();
+
+ File[] files = new File[nodes.size()];
+ int i = 0;
+ for (ITreeNode node : nodes)
+ {
+
+ File file = ((KeyStoreNode) node).getFile();
+ files[i++] = file;
+ }
+
+ return files;
+ }
+
+ public KeyStoreRootNode populateKeyStoreRootNode()
+ {
+ try
+ {
+ List<IKeyStore> keyStores = KeyStoreManager.getInstance().getKeyStores();
+ if (keyStores != null)
+ {
+ if (keyStores.size() > 0)
+ {
+ //there are items mapped on persistence
+ for (IKeyStore keyStore : keyStores)
+ {
+ if (keyStore instanceof KeyStoreNode)
+ {
+ keyStoresRootNode.addKeyStoreNode((KeyStoreNode) keyStore);
+ }
+ }
+ }
+ else
+ {
+ //we do not have any item mapped in persistence
+ //try to import from old Motodev keystore
+ //(probably it is the first time user is trying to use the view)
+ BackwardKeystoreManager backwardKeystoreManager = new BackwardKeystoreManager();
+ backwardKeystoreManager.mapOldKeystore();
+ }
+ }
+ }
+ catch (Exception e)
+ {
+ StudioLogger.error(KeystoreManagerView.class, e.getMessage(), e);
+ EclipseUtils
+ .showErrorDialog(
+ CertificateManagerNLS.KeystoreManagerView_ErrorLoadingMappedKeystoresFromPersistence,
+ e.getMessage());
+ }
+
+ return keyStoresRootNode;
+ }
+
+ public void unmapKeyStore(KeyStoreNode keyStoreNode)
+ {
+ keyStoresRootNode.removeKeyStore(keyStoreNode);
+ try
+ {
+ File file = keyStoreNode.getFile();
+ PasswordProvider passwordProvider = new PasswordProvider(file);
+ passwordProvider.deleteKeyStoreSavedPasswordNode();
+ KeyStoreManager.getInstance().removeKeyStore(keyStoreNode);
+ }
+ catch (KeyStoreManagerException e)
+ {
+ EclipseUtils.showErrorDialog("Error unmapping KeyStore", NLS.bind(
+ "Could not unmap the keystore file {0}", keyStoreNode.getFile()), new Status(
+ IStatus.ERROR, "Error unmapping KeyStore",
+ CertificateManagerActivator.PLUGIN_ID, e));
+ }
+ }
+
+ public void mapKeyStore(KeyStoreNode keyStoreNode) throws KeyStoreManagerException
+ {
+ keyStoresRootNode.addKeyStoreNode(keyStoreNode);
+ KeyStoreManager.getInstance().addKeyStore(keyStoreNode);
+ }
+
+ /**
+ * @return the keyStoresRootNode (populated through {@link SigningAndKeysModelManager#getInstance(), in the first access}
+ */
+ public KeyStoreRootNode getKeyStoresRootNode()
+ {
+ return keyStoresRootNode;
+ }
+}
diff --git a/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/tree/ExpiresInColumnLabelProvider.java b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/tree/ExpiresInColumnLabelProvider.java
new file mode 100644
index 0000000..ed911c8
--- /dev/null
+++ b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/tree/ExpiresInColumnLabelProvider.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.studio.android.certmanager.ui.tree;
+
+import java.security.cert.X509Certificate;
+import java.text.DateFormat;
+import java.util.Locale;
+
+import org.eclipse.jface.viewers.ColumnLabelProvider;
+
+import com.motorolamobility.studio.android.certmanager.ui.model.EntryNode;
+import com.motorolamobility.studio.android.certmanager.ui.model.IKeyStoreEntry;
+
+public class ExpiresInColumnLabelProvider extends ColumnLabelProvider
+{
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.viewers.ColumnLabelProvider#getText(java.lang.Object)
+ */
+ @Override
+ public String getText(Object element)
+ {
+ if (element instanceof IKeyStoreEntry)
+ {
+ EntryNode keyStoreEntry = (EntryNode) element;
+ X509Certificate x509Certificate = keyStoreEntry.getX509Certificate();
+ //Android certificate
+ return getExpiresInDate(x509Certificate);
+ }
+ return "";
+ }
+
+ /**
+ * Returns the date where the certificate expires
+ * @param cert
+ * @return
+ */
+ private String getExpiresInDate(X509Certificate x509Certificate)
+ {
+ return (x509Certificate != null) ? DateFormat.getDateInstance(DateFormat.MEDIUM,
+ Locale.getDefault()).format(x509Certificate.getNotAfter()) : "";
+ }
+}
diff --git a/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/tree/KeystoreManagerTreeContentProvider.java b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/tree/KeystoreManagerTreeContentProvider.java
new file mode 100644
index 0000000..34a08a6
--- /dev/null
+++ b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/tree/KeystoreManagerTreeContentProvider.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.studio.android.certmanager.ui.tree;
+
+import java.util.List;
+
+import org.eclipse.jface.viewers.ILazyTreeContentProvider;
+import org.eclipse.jface.viewers.TreeViewer;
+import org.eclipse.jface.viewers.Viewer;
+
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorolamobility.studio.android.certmanager.exception.KeyStoreManagerException;
+import com.motorolamobility.studio.android.certmanager.ui.model.ITreeNode;
+
+public class KeystoreManagerTreeContentProvider implements ILazyTreeContentProvider
+{
+
+ private final TreeViewer treeViewer;
+
+ public KeystoreManagerTreeContentProvider(TreeViewer viewer)
+ {
+ this.treeViewer = viewer;
+ }
+
+ @Override
+ public void dispose()
+ {
+ //Nothing
+ }
+
+ @Override
+ public void inputChanged(Viewer viewer, Object oldInput, Object newInput)
+ {
+ //Nothing
+ }
+
+ @Override
+ public void updateElement(Object parent, int index)
+ {
+ if (parent instanceof ITreeNode)
+ {
+ ITreeNode parentNode = (ITreeNode) parent;
+ ITreeNode child = null;
+ try
+ {
+ List<ITreeNode> children = parentNode.getChildren();
+ if (!children.isEmpty())
+ {
+ child = children.get(index);
+ }
+ }
+ catch (Exception e)
+ {
+ child = null;
+ }
+
+ if (child != null)
+ {
+ treeViewer.replace(parent, index, child);
+ try
+ {
+ if (child.getChildren().isEmpty())
+ {
+ treeViewer.setHasChildren(child, !child.isLeaf());
+ }
+ else
+ {
+ treeViewer.setChildCount(child, child.getChildren().size());
+ }
+ }
+ catch (KeyStoreManagerException e)
+ {
+ StudioLogger.error("Error while accessing keystore manager. " + e.getMessage());
+ }
+ }
+ }
+
+ }
+
+ @Override
+ public void updateChildCount(Object element, int currentChildCount)
+ {
+ if (element instanceof ITreeNode)
+ {
+ ITreeNode treeNode = (ITreeNode) element;
+ int childCount = 0;
+ try
+ {
+ treeNode.refresh();
+ List<ITreeNode> children = treeNode.getChildren();
+ if (!children.isEmpty())
+ {
+ childCount = children.size();
+ }
+ }
+ catch (KeyStoreManagerException e)
+ {
+ StudioLogger.error(e.getMessage());
+ }
+
+ if (childCount != currentChildCount)
+ {
+ treeViewer.setChildCount(element, childCount);
+ }
+ }
+ }
+
+ @Override
+ public ITreeNode getParent(Object element)
+ {
+ if (element instanceof ITreeNode)
+ {
+ ITreeNode treeNode = (ITreeNode) element;
+ return treeNode.getParent();
+ }
+ return null;
+ }
+
+}
diff --git a/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/tree/LastBackupDateColumnLabelProvider.java b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/tree/LastBackupDateColumnLabelProvider.java
new file mode 100644
index 0000000..8fc4877
--- /dev/null
+++ b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/tree/LastBackupDateColumnLabelProvider.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.studio.android.certmanager.ui.tree;
+
+import java.text.SimpleDateFormat;
+import java.util.Locale;
+
+import org.eclipse.jface.viewers.ColumnLabelProvider;
+
+import com.motorolamobility.studio.android.certmanager.ui.model.IKeyStore;
+
+public class LastBackupDateColumnLabelProvider extends ColumnLabelProvider
+{
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.viewers.ColumnLabelProvider#getText(java.lang.Object)
+ */
+ @Override
+ public String getText(Object element)
+ {
+ if (element instanceof IKeyStore)
+ {
+ IKeyStore iKeyStore = (IKeyStore) element;
+
+ if (iKeyStore.getLastBackupDate() != null)
+ {
+ SimpleDateFormat formatter =
+ new SimpleDateFormat("MMM dd yyyy HH:mm:ss", Locale.getDefault());
+ return formatter.format(iKeyStore.getLastBackupDate());
+ }
+ }
+ return "";
+ }
+}
diff --git a/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/tree/NameAliasColumnLabelProvider.java b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/tree/NameAliasColumnLabelProvider.java
new file mode 100644
index 0000000..5377ab4
--- /dev/null
+++ b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/tree/NameAliasColumnLabelProvider.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.studio.android.certmanager.ui.tree;
+
+import org.eclipse.jface.viewers.ColumnLabelProvider;
+import org.eclipse.jface.viewers.ILabelProviderListener;
+import org.eclipse.jface.viewers.ViewerCell;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.ui.IDecoratorManager;
+import org.eclipse.ui.PlatformUI;
+
+import com.motorolamobility.studio.android.certmanager.ui.model.ITreeNode;
+
+public class NameAliasColumnLabelProvider extends ColumnLabelProvider
+{
+ final IDecoratorManager decorator;
+
+ public NameAliasColumnLabelProvider()
+ {
+ decorator = PlatformUI.getWorkbench().getDecoratorManager();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.viewers.ColumnLabelProvider#getImage(java.lang.Object)
+ */
+ @Override
+ public Image getImage(Object element)
+ {
+ Image result = null;
+ if (element instanceof ITreeNode)
+ {
+ ITreeNode node = (ITreeNode) element;
+ Image defaultImage = null;
+ if (node.getIcon() != null)
+ {
+ defaultImage = node.getIcon().createImage();
+ result = decorator.decorateImage(defaultImage, element);
+ }
+ if (result == null)
+ {
+ result = defaultImage;
+ }
+ }
+ return result;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.viewers.ColumnLabelProvider#getText(java.lang.Object)
+ */
+ @Override
+ public String getText(Object element)
+ {
+ if (element instanceof ITreeNode)
+ {
+ ITreeNode node = (ITreeNode) element;
+ return node.getName();
+ }
+ return ""; //other items do not need to show this column with data
+ }
+
+ @Override
+ public void update(ViewerCell cell)
+ {
+ Object cellElement = cell.getElement();
+ cell.setText(getText(cellElement));
+ if (getImage(cellElement) != null)
+ {
+ cell.setImage(getImage(cellElement));
+ }
+ }
+
+ @Override
+ public String getToolTipText(Object element)
+ {
+ if (element instanceof ITreeNode)
+ {
+ ITreeNode treeNode = (ITreeNode) element;
+
+ return treeNode.getTooltip();
+ }
+ return super.getToolTipText(element);
+ }
+
+ @Override
+ public int getToolTipTimeDisplayed(Object object)
+ {
+ return 4000;
+ }
+
+ @Override
+ public int getToolTipDisplayDelayTime(Object object)
+ {
+ return 500;
+ }
+
+ @Override
+ public Point getToolTipShift(Object object)
+ {
+ return new Point(5, 5);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.viewers.BaseLabelProvider#addListener(org.eclipse.jface.viewers.ILabelProviderListener)
+ */
+ @Override
+ public void addListener(ILabelProviderListener listener)
+ {
+ decorator.addListener(listener);
+ super.addListener(listener);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.viewers.BaseLabelProvider#removeListener(org.eclipse.jface.viewers.ILabelProviderListener)
+ */
+ @Override
+ public void removeListener(ILabelProviderListener listener)
+ {
+ decorator.removeListener(listener);
+ super.removeListener(listener);
+ }
+}
diff --git a/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/tree/PathColumnLabelProvider.java b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/tree/PathColumnLabelProvider.java
new file mode 100644
index 0000000..32fa02b
--- /dev/null
+++ b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/tree/PathColumnLabelProvider.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.studio.android.certmanager.ui.tree;
+
+import org.eclipse.jface.viewers.ColumnLabelProvider;
+
+import com.motorolamobility.studio.android.certmanager.ui.model.KeyStoreNode;
+
+public class PathColumnLabelProvider extends ColumnLabelProvider
+{
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.viewers.ColumnLabelProvider#getText(java.lang.Object)
+ */
+ @Override
+ public String getText(Object element)
+ {
+ if (element instanceof KeyStoreNode)
+ {
+ KeyStoreNode keyStoreModel = (KeyStoreNode) element;
+ return keyStoreModel.getFile().getAbsolutePath();
+ }
+ return ""; //other items do not need to show this column with data
+ }
+}
diff --git a/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/tree/TypeColumnLabelProvider.java b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/tree/TypeColumnLabelProvider.java
new file mode 100644
index 0000000..a8a9fd4
--- /dev/null
+++ b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/tree/TypeColumnLabelProvider.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.studio.android.certmanager.ui.tree;
+
+import org.eclipse.jface.viewers.ColumnLabelProvider;
+
+import com.motorolamobility.studio.android.certmanager.ui.model.IKeyStore;
+
+public class TypeColumnLabelProvider extends ColumnLabelProvider
+{
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.viewers.ColumnLabelProvider#getText(java.lang.Object)
+ */
+ @Override
+ public String getText(Object element)
+ {
+ if (element instanceof IKeyStore)
+ {
+ IKeyStore iKeyStore = (IKeyStore) element;
+ return iKeyStore.getType();
+ }
+ return ""; //other items do not need to show this column with data
+ }
+
+}
diff --git a/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/wizards/CreateKeyWizard.java b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/wizards/CreateKeyWizard.java
new file mode 100644
index 0000000..f949f0f
--- /dev/null
+++ b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/wizards/CreateKeyWizard.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.studio.android.certmanager.ui.wizards;
+
+import org.eclipse.core.runtime.jobs.IJobChangeListener;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.ui.PlatformUI;
+
+import com.motorola.studio.android.wizards.BaseWizard;
+import com.motorolamobility.studio.android.certmanager.CertificateManagerActivator;
+import com.motorolamobility.studio.android.certmanager.i18n.CertificateManagerNLS;
+import com.motorolamobility.studio.android.certmanager.ui.model.IKeyStore;
+
+/**
+ * Wizard to create an Android key.
+ */
+public class CreateKeyWizard extends BaseWizard
+{
+
+ private static final String WIZARD_BANNER = "icons/wizban/create_key_wiz.png"; //$NON-NLS-1$
+
+ private boolean success = false;
+
+ /**
+ * Wizard page to allow the user to inform the key pair alias and
+ * distinguished name.
+ */
+ private final CreateKeyWizardPage createkeyWizardPage;
+
+ public CreateKeyWizard(IKeyStore keystore)
+ {
+ setupWizardUi();
+ createkeyWizardPage = new CreateKeyWizardPage(keystore);
+ }
+
+ public CreateKeyWizard(IKeyStore keystore, String keystorePassword,
+ IJobChangeListener createKeyJobListener)
+ {
+ setupWizardUi();
+ createkeyWizardPage =
+ new CreateKeyWizardPage(keystore, keystorePassword, createKeyJobListener);
+ }
+
+ private void setupWizardUi()
+ {
+ setWindowTitle(CertificateManagerNLS.CreateSelfSignedCertificateWizardPage_Title);
+ setDefaultPageImageDescriptor(CertificateManagerActivator.imageDescriptorFromPlugin(
+ CertificateManagerActivator.PLUGIN_ID, WIZARD_BANNER));
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.wizard.Wizard#createPageControls(org.eclipse.swt.widgets.Composite)
+ */
+ @Override
+ public void createPageControls(Composite pageContainer)
+ {
+ super.createPageControls(pageContainer);
+
+ //the shell has the same help as its single page
+ PlatformUI.getWorkbench().getHelpSystem()
+ .setHelp(getShell(), CreateKeyWizardPage.CREATE_SELF_SIGNED_CERTIFICATE_HELP_ID);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @seecom.motorola.studio.android.wizards.BaseWizard#
+ * doPerformFinish()
+ */
+ @Override
+ protected boolean doPerformFinish()
+ {
+ success = createkeyWizardPage.createKey();
+ return success;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.jface.wizard.Wizard#addPages()
+ */
+ @Override
+ public void addPages()
+ {
+ super.addPages();
+ addPage(createkeyWizardPage);
+ }
+
+ /**
+ * Returns the alias of the just created key or null otherwise.
+ */
+ public String getAlias()
+ {
+ return success ? createkeyWizardPage.getTrueAlias() : null;
+ }
+}
diff --git a/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/wizards/CreateKeyWizardPage.java b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/wizards/CreateKeyWizardPage.java
new file mode 100644
index 0000000..286cbeb
--- /dev/null
+++ b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/wizards/CreateKeyWizardPage.java
@@ -0,0 +1,284 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.studio.android.certmanager.ui.wizards;
+
+import org.eclipse.core.runtime.jobs.IJobChangeListener;
+import org.eclipse.jface.wizard.IWizard;
+
+import com.motorola.studio.android.wizards.BaseWizardPage;
+import com.motorolamobility.studio.android.certmanager.CertificateManagerActivator;
+import com.motorolamobility.studio.android.certmanager.i18n.CertificateManagerNLS;
+import com.motorolamobility.studio.android.certmanager.job.CreateKeyJob;
+import com.motorolamobility.studio.android.certmanager.ui.composite.NewKeyBlock;
+import com.motorolamobility.studio.android.certmanager.ui.model.CertificateDetailsInfo;
+import com.motorolamobility.studio.android.certmanager.ui.model.IKeyStore;
+
+/**
+ * Wizard page to create an Android key.
+ */
+public class CreateKeyWizardPage extends BaseWizardPage
+{
+ private IKeyStore keystore = null;
+
+ private String alias = null;
+
+ private String keyStorePass;
+
+ public static final String CREATE_SELF_SIGNED_CERTIFICATE_HELP_ID =
+ CertificateManagerActivator.PLUGIN_ID + ".create-self-cert";
+
+ private IJobChangeListener createKeyJobListener = null;
+
+ /**
+ * The default constructor.
+ */
+ public CreateKeyWizardPage(IKeyStore keystore)
+ {
+ super(new NewKeyBlock(), CertificateManagerNLS.CreateSelfSignedCertificateWizardPage_Title,
+ CertificateManagerNLS.CreateSelfSignedCertificateWizardPage_Description,
+ CREATE_SELF_SIGNED_CERTIFICATE_HELP_ID); //$NON-NLS-2$
+ ((NewKeyBlock) block).setBaseWizardPage(this);
+ this.keystore = keystore;
+ }
+
+ public CreateKeyWizardPage(IKeyStore keystore, String keystorePassword,
+ IJobChangeListener createKeyJobListener)
+ {
+ this(keystore);
+ setKeyStorePass(keystorePassword);
+ this.createKeyJobListener = createKeyJobListener;
+ }
+
+ /**
+ * Obtains the key pair alias defined by user.
+ *
+ * @return The key pair alias.
+ */
+ public String getAlias()
+ {
+ return ((NewKeyBlock) block).getAlias();
+ }
+
+ /**
+ * Obtains the common name defined by user.
+ *
+ * @return The common name.
+ */
+ public String getCommonName()
+ {
+ return ((NewKeyBlock) block).getCommonName();
+ }
+
+ /**
+ * Obtains the organization defined by user.
+ *
+ * @return The organization.
+ */
+ public String getOrganization()
+ {
+ return ((NewKeyBlock) block).getOrganization();
+ }
+
+ /**
+ * Obtains the organization unit defined by user.
+ *
+ * @return The organization unit.
+ */
+ public String getOrganizationUnit()
+ {
+ return ((NewKeyBlock) block).getOrganizationUnit();
+ }
+
+ /**
+ * Obtains the locality defined by user.
+ *
+ * @return The locality.
+ */
+ public String getLocality()
+ {
+ return ((NewKeyBlock) block).getLocality();
+ }
+
+ /**
+ * Obtains the state defined by user.
+ *
+ * @return The state.
+ */
+ public String getState()
+ {
+ return ((NewKeyBlock) block).getState();
+ }
+
+ /**
+ * Obtains the country defined by user.
+ *
+ * @return The country.
+ */
+ public String getCountry()
+ {
+ return ((NewKeyBlock) block).getCountry();
+ }
+
+ /**
+ * Obtains the validity defined by user.
+ *
+ * @return The validity.
+ */
+ public String getValidity()
+ {
+ return ((NewKeyBlock) block).getValidity();
+ }
+
+ public String getEntryPassword()
+ {
+ return ((NewKeyBlock) block).getKeyPassword();
+ }
+
+ public String getEntryConfirmPassword()
+ {
+ return ((NewKeyBlock) block).getKeyConfirmPassword();
+ }
+
+ public boolean needToSaveKeyEntryPassword()
+ {
+ return ((NewKeyBlock) block).needToSaveKeyPassword();
+ }
+
+ public boolean createKey()
+ {
+ boolean successfullyCreated = true;
+ alias = getAlias();
+
+ if (isPageCompleteWithAllFieldsBlank())
+ {
+ successfullyCreated = true;
+ }
+ else
+ {
+ final CertificateDetailsInfo certificateDetailsInfo =
+ new CertificateDetailsInfo(alias, getCommonName(), getOrganization(),
+ getOrganizationUnit(), getLocality(), getCountry(), getState(),
+ getValidity(), getEntryPassword());
+ CreateKeyJob createKeyJob =
+ new CreateKeyJob("Create key job", (NewKeyBlock) block, certificateDetailsInfo,
+ keystore, keyStorePass);
+
+ if (createKeyJobListener != null)
+ {
+ createKeyJob.addJobChangeListener(createKeyJobListener);
+ }
+ createKeyJob.schedule();
+
+ successfullyCreated = true;
+ }
+
+ return successfullyCreated;
+ }
+
+ /**
+ * @param keystoreNode
+ */
+ public void setKeyStore(IKeyStore keystoreNode)
+ {
+ this.keystore = keystoreNode;
+ }
+
+ /*
+ * If all fields are blank and this page is under CreateKeystoreWizard context,
+ * then if all fields are blank the page is considered complete and no keypair will be created.
+ */
+ public boolean isPageCompleteWithAllFieldsBlank()
+ {
+ boolean result = false;
+ IWizard wizardContext = getWizard();
+
+ //in the context of CreateKeyStoreWizard and if this is NOT the current page, then allow all fields blank
+ //be a valid complete page
+ if ((wizardContext instanceof CreateKeystoreWizard) && !isCurrentPage()
+ && areAllFieldsBlank())
+ {
+ result = true;
+ }
+
+ return result;
+ }
+
+ /*
+ * return true if all fields are blank
+ */
+ private boolean areAllFieldsBlank()
+ {
+ boolean result = false;
+ if (getAlias().isEmpty() && getCommonName().isEmpty() && getOrganization().isEmpty()
+ && getOrganizationUnit().isEmpty() && getLocality().isEmpty()
+ && getState().isEmpty() && getCountry().isEmpty() && getEntryPassword().isEmpty()
+ && getEntryConfirmPassword().isEmpty())
+ {
+ result = true;
+ }
+ return result;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.jface.wizard.WizardPage#isPageComplete()
+ */
+ @Override
+ public boolean isPageComplete()
+ {
+ if (this.block == null)
+ {
+ return true;
+ }
+ return this.block.isPageComplete() || isPageCompleteWithAllFieldsBlank();
+ }
+
+ /**
+ * This method just works.
+ * @return
+ */
+ public String getTrueAlias()
+ {
+ return alias;
+ }
+
+ public void setKeyStorePass(String password)
+ {
+ this.keyStorePass = password;
+ }
+
+ /**
+ * @return the keystore
+ */
+ public IKeyStore getKeystore()
+ {
+ return keystore;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.dialogs.DialogPage#setVisible(boolean)
+ */
+ @Override
+ public void setVisible(boolean visible)
+ {
+ super.setVisible(visible);
+ if (visible)
+ {
+ ((NewKeyBlock) block).setFocus();
+ }
+
+ }
+}
diff --git a/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/wizards/CreateKeystorePage.java b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/wizards/CreateKeystorePage.java
new file mode 100644
index 0000000..9630e66
--- /dev/null
+++ b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/wizards/CreateKeystorePage.java
@@ -0,0 +1,672 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.motorolamobility.studio.android.certmanager.ui.wizards;
+
+import java.io.File;
+import java.io.IOException;
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.List;
+
+import org.eclipse.core.runtime.Path;
+import org.eclipse.jface.dialogs.IMessageProvider;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.jface.viewers.ComboViewer;
+import org.eclipse.jface.viewers.ILabelProvider;
+import org.eclipse.jface.viewers.ILabelProviderListener;
+import org.eclipse.jface.viewers.IStructuredContentProvider;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.jface.wizard.WizardPage;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.ModifyEvent;
+import org.eclipse.swt.events.ModifyListener;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.FileDialog;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.ui.PlatformUI;
+
+import com.motorola.studio.android.common.utilities.EclipseUtils;
+import com.motorolamobility.studio.android.certmanager.CertificateManagerActivator;
+import com.motorolamobility.studio.android.certmanager.core.KeyStoreManager;
+import com.motorolamobility.studio.android.certmanager.core.PasswordProvider;
+import com.motorolamobility.studio.android.certmanager.exception.KeyStoreManagerException;
+import com.motorolamobility.studio.android.certmanager.i18n.CertificateManagerNLS;
+import com.motorolamobility.studio.android.certmanager.ui.model.KeyStoreNode;
+import com.motorolamobility.studio.android.certmanager.ui.model.SigningAndKeysModelManager;
+
+public class CreateKeystorePage extends WizardPage
+{
+
+ private static final String CREATE_KEYSTORE_HELP_ID = CertificateManagerActivator.PLUGIN_ID
+ + ".new_keystore"; //$NON-NLS-1$
+
+ private Text keystoreFilenameText;
+
+ private ComboViewer keystoreTypeComboViewer;
+
+ private Text keystorePasswordText;
+
+ private Text keystoreConfirmPasswordText;
+
+ private String keystorePassword;
+
+ private boolean initialValidation = true;
+
+ private boolean userChangedPasswordConfirmation = false;
+
+ private boolean userChangedPassword = false;
+
+ SelectionListener selectionListener = new SelectionListener()
+ {
+
+ @Override
+ public void widgetSelected(SelectionEvent e)
+ {
+ validatePage();
+ }
+
+ @Override
+ public void widgetDefaultSelected(SelectionEvent e)
+ {
+ //nothing to do...
+ }
+ };
+
+ private Button savePassword;
+
+ private Button useTypeAsExtensionCheckBox;
+
+ protected boolean useTypeAsExtensionCheckBoxPreviousState = true;
+
+ /**
+ * @param pageName
+ */
+ protected CreateKeystorePage(String pageName)
+ {
+ super(pageName);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.dialogs.IDialogPage#createControl(org.eclipse.swt.widgets.Composite)
+ */
+ @Override
+ public void createControl(Composite parent)
+ {
+ Composite mainComposite = new Composite(parent, SWT.FILL);
+ mainComposite.setLayout(new GridLayout(3, false));
+ mainComposite.setLayoutData(new GridData(GridData.FILL_BOTH));
+
+ setTitle(CertificateManagerNLS.CreateKeystorePage_CreateKeystore);
+ setMessage(CertificateManagerNLS.CreateKeystorePage_WizardDefaultMessage);
+
+ createFilenameSection(mainComposite);
+ createKeystoreTypeSection(mainComposite);
+ createFilenameExtensionSection(mainComposite);
+
+ setKeystoreFilenameExtension();
+
+ //LINE TO SEPARATE PASSWORD SECTION FROM KEYSTORE DETAILS SECTION
+ Label separator1 = new Label(mainComposite, SWT.SEPARATOR | SWT.HORIZONTAL);
+ separator1.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 3, 1));
+
+ createKeystorePasswordSection(mainComposite);
+ createConfirmPasswordSection(mainComposite);
+ createSavePasswordSection(mainComposite);
+
+ validatePage();
+
+ setControl(mainComposite);
+
+ //set help id for this page
+ PlatformUI.getWorkbench().getHelpSystem().setHelp(parent, CREATE_KEYSTORE_HELP_ID);
+ PlatformUI.getWorkbench().getHelpSystem().setHelp(mainComposite, CREATE_KEYSTORE_HELP_ID);
+ }
+
+ /**
+ * @param mainComposite
+ */
+ private void createKeystoreTypeSection(Composite parent)
+ {
+ Label keystoreTypeLabel = new Label(parent, SWT.NONE);
+ keystoreTypeLabel.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false, 1, 1));
+ keystoreTypeLabel.setText(CertificateManagerNLS.CreateKeystorePage_KeystoreType);
+
+ keystoreTypeComboViewer = new ComboViewer(parent, SWT.READ_ONLY);
+ keystoreTypeComboViewer.getCombo().setLayoutData(
+ new GridData(SWT.FILL, SWT.NONE, true, false, 1, 1));
+ keystoreTypeComboViewer.setContentProvider(new IStructuredContentProvider()
+ {
+
+ @Override
+ public void inputChanged(Viewer viewer, Object oldInput, Object newInput)
+ {
+ //do nothing
+ }
+
+ @Override
+ public void dispose()
+ {
+ //do nothing
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public Object[] getElements(Object inputElement)
+ {
+ return ((List<String>) inputElement).toArray();
+ }
+ });
+ keystoreTypeComboViewer.setLabelProvider(new ILabelProvider()
+ {
+
+ @Override
+ public void removeListener(ILabelProviderListener listener)
+ {
+ //do nothing
+ }
+
+ @Override
+ public boolean isLabelProperty(Object element, String property)
+ {
+ return false;
+ }
+
+ @Override
+ public void dispose()
+ {
+ //do nothing
+ }
+
+ @Override
+ public void addListener(ILabelProviderListener listener)
+ {
+ //do nothing
+ }
+
+ @Override
+ public String getText(Object element)
+ {
+ return (String) element;
+ }
+
+ @Override
+ public Image getImage(Object element)
+ {
+ return null;
+ }
+ });
+
+ keystoreTypeComboViewer.setInput(KeyStoreManager.getInstance().getAvailableTypes());
+
+ keystoreTypeComboViewer.getCombo().addSelectionListener(new SelectionAdapter()
+ {
+ /* (non-Javadoc)
+ * @see org.eclipse.swt.events.SelectionAdapter#widgetSelected(org.eclipse.swt.events.SelectionEvent)
+ */
+ @Override
+ public void widgetSelected(SelectionEvent e)
+ {
+ useTypeAsExtensionCheckBox.setEnabled(true);
+ useTypeAsExtensionCheckBox.setSelection(useTypeAsExtensionCheckBoxPreviousState);
+
+ if (useTypeAsExtensionCheckBox.getSelection())
+ {
+ setKeystoreFilenameExtension();
+ }
+ }
+ });
+
+ for (int i = 0; i < keystoreTypeComboViewer.getCombo().getItemCount(); i++)
+ {
+ if (keystoreTypeComboViewer.getCombo().getItem(i)
+ .compareToIgnoreCase(KeyStoreManager.getInstance().getDefaultType()) == 0)
+ {
+ keystoreTypeComboViewer.getCombo().select(i);
+ }
+ }
+
+ keystoreTypeComboViewer.getCombo().addModifyListener(new ModifyListener()
+ {
+ @Override
+ public void modifyText(ModifyEvent e)
+ {
+ if (useTypeAsExtensionCheckBox != null)
+ {
+ useTypeAsExtensionCheckBox.setEnabled(false);
+ useTypeAsExtensionCheckBox.setSelection(false);
+ }
+ }
+ });
+
+ //fill the third column with a blank label
+ Label separator2 = new Label(parent, SWT.NONE);
+ separator2.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false, 1, 1));
+ }
+
+ /**
+ * Set the extension of the keystore based on selected keystore type and the user's choice to use it or not as the extension.
+ * If the user typed a custom keystore type, then the filename extension is set to ".keystore".
+ * */
+ protected void setKeystoreFilenameExtension()
+ {
+ String keystoreFilename = keystoreFilenameText.getText();
+ String keystoreType = keystoreTypeComboViewer.getCombo().getText();
+
+ List<String> availableTypes = KeyStoreManager.getInstance().getAvailableTypes();
+ availableTypes
+ .add(CertificateManagerNLS.CreateKeystorePage_DefaultKeystoreFilenameExtension);
+
+ for (String availableType : availableTypes)
+ {
+ String availableTypeExtension = "." + availableType.toLowerCase(); //$NON-NLS-1$
+ if (keystoreFilename.endsWith(availableTypeExtension))
+ {
+ keystoreFilename =
+ keystoreFilename.substring(0, keystoreFilename.length()
+ - availableTypeExtension.length());
+ break;
+ }
+ }
+
+ keystoreFilenameText.setText(keystoreFilename + "." + keystoreType.toLowerCase()); //$NON-NLS-1$
+ }
+
+ private void createFilenameExtensionSection(Composite mainComposite)
+ {
+ useTypeAsExtensionCheckBox = new Button(mainComposite, SWT.CHECK);
+ useTypeAsExtensionCheckBox.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false,
+ 3, 1));
+ useTypeAsExtensionCheckBox
+ .setText(CertificateManagerNLS.CreateKeystorePage_UseKeystoreTypeAsExtension);
+ useTypeAsExtensionCheckBox.setSelection(true);
+
+ useTypeAsExtensionCheckBox.addSelectionListener(new SelectionAdapter()
+ {
+ /* (non-Javadoc)
+ * @see org.eclipse.swt.events.SelectionAdapter#widgetSelected(org.eclipse.swt.events.SelectionEvent)
+ */
+ @Override
+ public void widgetSelected(SelectionEvent e)
+ {
+ useTypeAsExtensionCheckBoxPreviousState = useTypeAsExtensionCheckBox.getSelection();
+ if (useTypeAsExtensionCheckBox.getSelection())
+ {
+ setKeystoreFilenameExtension();
+ }
+ }
+ });
+ }
+
+ /**
+ * @param mainComposite
+ */
+ private void createSavePasswordSection(Composite mainComposite)
+ {
+ savePassword = new Button(mainComposite, SWT.CHECK);
+ savePassword.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false, 3, 1));
+ savePassword.setText(CertificateManagerNLS.CreateKeystorePage_SaveThisPassword);
+ savePassword.setSelection(false);
+ }
+
+ /**
+ * @param mainComposite
+ */
+ private void createConfirmPasswordSection(Composite mainComposite)
+ {
+ Label keystoreConfirmPasswordLabel = new Label(mainComposite, SWT.NONE);
+ keystoreConfirmPasswordLabel.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false,
+ 1, 1));
+ keystoreConfirmPasswordLabel
+ .setText(CertificateManagerNLS.CreateKeystorePage_KeystoreConfirmPasswordLabel);
+
+ keystoreConfirmPasswordText =
+ new Text(mainComposite, SWT.SINGLE | SWT.BORDER | SWT.PASSWORD);
+ keystoreConfirmPasswordText.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false,
+ 1, 1));
+ keystoreConfirmPasswordText.addSelectionListener(selectionListener);
+ keystoreConfirmPasswordText.addModifyListener(new ModifyListener()
+ {
+
+ @Override
+ public void modifyText(ModifyEvent e)
+ {
+ userChangedPasswordConfirmation = true;
+ validatePage();
+ }
+ });
+
+ //fill the third column with a blank label
+ Label separator2 = new Label(mainComposite, SWT.NONE);
+ separator2.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false, 1, 1));
+ }
+
+ /**
+ * @param mainComposite
+ */
+ private void createKeystorePasswordSection(Composite mainComposite)
+ {
+ Label keystorePasswordLabel = new Label(mainComposite, SWT.NONE);
+ keystorePasswordLabel.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false, 1, 1));
+ keystorePasswordLabel
+ .setText(CertificateManagerNLS.CreateKeystorePage_KeystorePasswordLabel);
+
+ keystorePasswordText = new Text(mainComposite, SWT.SINGLE | SWT.BORDER | SWT.PASSWORD);
+ keystorePasswordText.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));
+ keystorePasswordText.addSelectionListener(selectionListener);
+ keystorePasswordText.addModifyListener(new ModifyListener()
+ {
+
+ @Override
+ public void modifyText(ModifyEvent e)
+ {
+ keystorePassword = keystorePasswordText.getText();
+ userChangedPassword = true;
+ validatePage();
+ }
+ });
+
+ //fill the third column with a blank label
+ @SuppressWarnings("unused")
+ Label separator = new Label(mainComposite, SWT.NONE);
+ keystorePasswordLabel.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false, 1, 1));
+ }
+
+ /**
+ * @param mainComposite
+ */
+ private void createFilenameSection(Composite mainComposite)
+ {
+ Label keystoreFilenameLabel = new Label(mainComposite, SWT.NONE);
+ keystoreFilenameLabel.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false, 1, 1));
+ keystoreFilenameLabel
+ .setText(CertificateManagerNLS.CreateKeystorePage_KeystoreFilenameLabel);
+
+ keystoreFilenameText = new Text(mainComposite, SWT.SINGLE | SWT.BORDER);
+ keystoreFilenameText.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));
+ keystoreFilenameText.setText(generateKeyStoreFilename());
+ keystoreFilenameText.addSelectionListener(selectionListener);
+ keystoreFilenameText.addModifyListener(new ModifyListener()
+ {
+
+ @Override
+ public void modifyText(ModifyEvent e)
+ {
+ validatePage();
+ }
+ });
+
+ Button chooseLocation = new Button(mainComposite, SWT.PUSH);
+ chooseLocation.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, false, false, 1, 1));
+ chooseLocation.setText(CertificateManagerNLS.CreateKeystorePage_KeystoreFilenameBrowse);
+ chooseLocation.addSelectionListener(new SelectionAdapter()
+ {
+ /* (non-Javadoc)
+ * @see org.eclipse.swt.events.SelectionAdapter#widgetSelected(org.eclipse.swt.events.SelectionEvent)
+ */
+ @Override
+ public void widgetSelected(SelectionEvent e)
+ {
+ Shell shell = Display.getCurrent().getActiveShell();
+
+ FileDialog dialog = new FileDialog(shell, SWT.SAVE);
+
+ String keystoreFilenameStr = dialog.open();
+
+ if (keystoreFilenameStr != null)
+ {
+ keystoreFilenameText.setText(keystoreFilenameStr);
+ }
+ }
+ });
+ }
+
+ private void validatePage()
+ {
+ boolean pageComplete = true;
+
+ String errorMessage = null;
+
+ String message = CertificateManagerNLS.CreateKeystorePage_WizardDefaultMessage;
+
+ int messageType = IMessageProvider.NONE;
+
+ if (initialValidation == true)
+ {
+ //when the wizard opens, does not show any errors
+ pageComplete = false;
+ initialValidation = false;
+ }
+ else
+ {
+ //password text and confirmation password text must match
+ if (!keystorePasswordText.getText().equals(keystoreConfirmPasswordText.getText()))
+ {
+ //if the user hasn't started typing the confirmation password,
+ //then just show an info, instead of an error
+ if (userChangedPasswordConfirmation)
+ {
+ errorMessage = CertificateManagerNLS.CreateKeystorePage_PasswordDoesNotMatch;
+ pageComplete = false;
+ }
+ else
+ {
+ message = CertificateManagerNLS.CreateKeystorePage_ConfirmPasswordInfoMsg;
+ messageType = IMessageProvider.INFORMATION;
+ pageComplete = false;
+ }
+ }
+ //check password size according to keytool specification
+ if (keystorePasswordText.getText().length() < KeyStoreNode.KEYSTORE_PASSWORD_MIN_SIZE)
+ {
+ if (userChangedPassword)
+ {
+ errorMessage =
+ CertificateManagerNLS
+ .bind(CertificateManagerNLS.CreateKeystorePage_PasswordMinSizeMessage,
+ KeyStoreNode.KEYSTORE_PASSWORD_MIN_SIZE); //$NON-NLS-1$
+ pageComplete = false;
+ }
+ else
+ {
+ message = CertificateManagerNLS.CreateKeystorePage_SetPasswordInfoMsg;
+ messageType = IMessageProvider.INFORMATION;
+ pageComplete = false;
+ }
+ }
+
+ //check if store type is filled
+ if (keystoreTypeComboViewer.getCombo().getText().isEmpty())
+ {
+ errorMessage = CertificateManagerNLS.CreateKeystorePage_SetKeystoreType;
+ pageComplete = false;
+ }
+
+ //check if filename is valid
+ try
+ {
+ File keystoreFile = new File(keystoreFilenameText.getText().trim());
+ Path keystorePath = new Path(keystoreFilenameText.getText().trim());
+ if (!keystorePath.isValidPath(keystoreFile.getCanonicalPath()))
+ {
+ //throw the same exception as getCanonicalPath() in order to do not duplicate code
+ throw new IOException();
+ }
+ }
+ catch (IOException e)
+ {
+ errorMessage = CertificateManagerNLS.CreateKeystorePage_FilenameSyntaxError;
+ pageComplete = false;
+ }
+ if (keystoreFilenameText.getText().trim().isEmpty())
+ {
+ errorMessage = CertificateManagerNLS.ImportKeystorePage_FilenameCannotBeEmpty;
+ pageComplete = false;
+ }
+ }
+
+ setMessage(message, messageType);
+ setErrorMessage(errorMessage);
+ setPageComplete(pageComplete);
+ }
+
+ /**
+ * Generate a valid filename for a new keystore.
+ * The file must not exist, so a serial number is added to it as necessary.
+ * @return An standard keystore filename.
+ * */
+ private String generateKeyStoreFilename()
+ {
+ SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy_MM_dd-HH_mm_ss_SSS"); //$NON-NLS-1$
+ String timestamp = dateFormat.format(Calendar.getInstance().getTime());
+
+ //initial keystore filename with timestamp
+ String keystoreFilenameStr =
+ System.getProperty("user.home") + System.getProperty("file.separator") //$NON-NLS-1$ //$NON-NLS-2$
+ + CertificateManagerNLS.bind(
+ CertificateManagerNLS.CreateKeystorePage_DefaultKeystoreFilename,
+ timestamp);
+
+ File keystoreFile = new File(keystoreFilenameStr);
+
+ //while file already exists, generate a new one using a new timestamp
+ while (keystoreFile.exists())
+ {
+ timestamp = dateFormat.format(Calendar.getInstance().getTime());
+ keystoreFilenameStr =
+ System.getProperty("user.home") + System.getProperty("file.separator") //$NON-NLS-1$ //$NON-NLS-2$
+ + CertificateManagerNLS
+ .bind(CertificateManagerNLS.CreateKeystorePage_DefaultKeystoreFilename,
+ timestamp);
+ keystoreFile = new File(keystoreFilenameStr);
+ }
+
+ return keystoreFilenameStr;
+ }
+
+ /**
+ * As this page works independently of other pages, it has its own version of performFinish().
+ * Wizards that use this page must call this method to effectively create the new keystore.
+ * @return {@code true} if the keystore were successfully created, {@code false} otherwise.
+ * */
+ public KeyStoreNode createKeyStore()
+ {
+ boolean successfullyCreated = true;
+ File keystoreFile = null;
+ KeyStoreNode keystoreNode = null;
+
+ try
+ {
+ keystoreFile = new File(keystoreFilenameText.getText().trim());
+ if (validateKeyStoreFile(keystoreFile))
+ {
+ keystoreNode =
+ (KeyStoreNode) KeyStoreManager.createKeyStore(keystoreFile,
+ keystoreTypeComboViewer.getCombo().getText(), keystorePasswordText
+ .getText().toCharArray());
+
+ SigningAndKeysModelManager.getInstance().mapKeyStore(keystoreNode);
+ }
+ else
+ {
+ //file already exist and will not be overwritten
+ successfullyCreated = false;
+ }
+ }
+ catch (KeyStoreManagerException e)
+ {
+ //in case of error, the keystore wasn't properly created and the file should not be left on file system
+ if (keystoreFile != null)
+ {
+ keystoreFile.delete();
+ }
+
+ EclipseUtils.showErrorDialog(
+ CertificateManagerNLS.CreateKeystorePage_ErrorCreatingKeystore, NLS.bind(
+ CertificateManagerNLS.CreateKeystorePage_ErrorOnKeyStoreFileCreation,
+ keystoreFilenameText.getText()));
+ successfullyCreated = false;
+ }
+
+ if (successfullyCreated && savePassword.getSelection())
+ {
+ savePassword(keystoreFile);
+ }
+
+ return successfullyCreated ? keystoreNode : null;
+ }
+
+ /**
+ * @param keystoreFile
+ */
+ private void savePassword(File keystoreFile)
+ {
+ try
+ {
+ PasswordProvider passwordProvider = new PasswordProvider(keystoreFile);
+ passwordProvider.saveKeyStorePassword(keystorePasswordText.getText());
+ }
+ catch (KeyStoreManagerException e)
+ {
+ EclipseUtils.showWarningDialog(
+ CertificateManagerNLS.CreateKeystorePage_CouldNotSavePassword,
+ e.getLocalizedMessage());
+ }
+ }
+
+ /* If file exists and the user chooses to overwrite it, the key store file is valid and return value is true.
+ * If file exists and the user do not want to overwrite the file, then the keystore file is considered invalid and the return value is false.
+ * If file does not exist, then the file is valid and the return value is true.
+ * */
+ private boolean validateKeyStoreFile(File keystoreFile)
+ {
+ boolean result = true;
+
+ if (keystoreFile.exists())
+ {
+ Shell shell = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell();
+
+ result =
+ MessageDialog.openQuestion(shell,
+ CertificateManagerNLS.CreateKeystorePage_ConfirmFileOverwrite,
+ NLS.bind(CertificateManagerNLS.CreateKeystorePage_ConfirmReplaceFile,
+ keystoreFile.getAbsolutePath()));
+ if (result)
+ {
+ //file will be recreated
+ keystoreFile.delete();
+ }
+ }
+ return result;
+ }
+
+ public String getPassword()
+ {
+ return keystorePassword;
+ }
+
+}
diff --git a/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/wizards/CreateKeystoreWizard.java b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/wizards/CreateKeystoreWizard.java
new file mode 100644
index 0000000..12c0e48
--- /dev/null
+++ b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/wizards/CreateKeystoreWizard.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.motorolamobility.studio.android.certmanager.ui.wizards;
+
+import org.eclipse.core.runtime.jobs.IJobChangeListener;
+import org.eclipse.jface.wizard.Wizard;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.ui.PlatformUI;
+
+import com.motorolamobility.studio.android.certmanager.CertificateManagerActivator;
+import com.motorolamobility.studio.android.certmanager.i18n.CertificateManagerNLS;
+import com.motorolamobility.studio.android.certmanager.ui.model.KeyStoreNode;
+
+/**
+ *
+ */
+public class CreateKeystoreWizard extends Wizard
+{
+
+ private final CreateKeystorePage createKeystorePage;
+
+ private final CreateKeyWizardPage createKeyPairPage;
+
+ private static final String WIZARD_BANNER = "icons/wizban/create_keystore_wiz.png"; //$NON-NLS-1$
+
+ private static final String KEYSTORE_KEY_HELP_ID = CertificateManagerActivator.PLUGIN_ID
+ + ".keystore-key-help-id";
+
+ private KeyStoreNode createdKeystoreNode;
+
+ /**
+ *
+ */
+ public CreateKeystoreWizard()
+ {
+ this(null);
+ }
+
+ public CreateKeystoreWizard(IJobChangeListener createKeystoreJobListener)
+ {
+ setWindowTitle(CertificateManagerNLS.CreateKeystoreWizard_CreateNewKeyStore);
+ setDefaultPageImageDescriptor(CertificateManagerActivator.imageDescriptorFromPlugin(
+ CertificateManagerActivator.PLUGIN_ID, WIZARD_BANNER));
+
+ this.createKeyPairPage = new CreateKeyWizardPage(null, "", createKeystoreJobListener);
+ this.createKeystorePage =
+ new CreateKeystorePage(CertificateManagerNLS.CreateKeystoreWizard_CreateNewKeyStore);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.wizard.Wizard#createPageControls(org.eclipse.swt.widgets.Composite)
+ */
+ @Override
+ public void createPageControls(Composite pageContainer)
+ {
+ super.createPageControls(pageContainer);
+
+ //the shell has a generic help that talks about keystore and keys
+ PlatformUI.getWorkbench().getHelpSystem().setHelp(getShell(), KEYSTORE_KEY_HELP_ID);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.wizard.Wizard#performFinish()
+ */
+ @Override
+ public boolean performFinish()
+ {
+ createdKeystoreNode = createKeystorePage.createKeyStore();
+ if (createdKeystoreNode != null)
+ {
+ createKeyPairPage.setKeyStore(createdKeystoreNode);
+ createKeyPairPage.setKeyStorePass(createKeystorePage.getPassword());
+ createKeyPairPage.createKey();
+ }
+
+ //check if the keystore was created
+ return (createdKeystoreNode != null);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.wizard.Wizard#addPages()
+ */
+ @Override
+ public void addPages()
+ {
+ this.addPage(createKeystorePage);
+ this.addPage(createKeyPairPage);
+ }
+
+ /**
+ * @return the createdKeystoreNode
+ */
+ public KeyStoreNode getCreatedKeystoreNode()
+ {
+ return createdKeystoreNode;
+ }
+
+ public String getCreatedKeystorePassword()
+ {
+ String result = null;
+ if (createKeystorePage != null)
+ {
+ result = createKeystorePage.getPassword();
+ }
+
+ return result;
+ }
+}
diff --git a/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/wizards/ImportKeystorePage.java b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/wizards/ImportKeystorePage.java
new file mode 100644
index 0000000..b9b0582
--- /dev/null
+++ b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/wizards/ImportKeystorePage.java
@@ -0,0 +1,417 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.motorolamobility.studio.android.certmanager.ui.wizards;
+
+import java.io.File;
+import java.util.List;
+
+import org.eclipse.jface.viewers.ComboViewer;
+import org.eclipse.jface.viewers.ILabelProvider;
+import org.eclipse.jface.viewers.ILabelProviderListener;
+import org.eclipse.jface.viewers.IStructuredContentProvider;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.jface.wizard.WizardPage;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.ModifyEvent;
+import org.eclipse.swt.events.ModifyListener;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.FileDialog;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.ui.PlatformUI;
+
+import com.motorola.studio.android.common.utilities.EclipseUtils;
+import com.motorola.studio.android.common.utilities.FileUtil;
+import com.motorolamobility.studio.android.certmanager.CertificateManagerActivator;
+import com.motorolamobility.studio.android.certmanager.core.KeyStoreManager;
+import com.motorolamobility.studio.android.certmanager.core.PasswordProvider;
+import com.motorolamobility.studio.android.certmanager.exception.KeyStoreManagerException;
+import com.motorolamobility.studio.android.certmanager.i18n.CertificateManagerNLS;
+import com.motorolamobility.studio.android.certmanager.ui.model.IKeyStore;
+import com.motorolamobility.studio.android.certmanager.ui.model.KeyStoreNode;
+import com.motorolamobility.studio.android.certmanager.ui.model.SigningAndKeysModelManager;
+
+public class ImportKeystorePage extends WizardPage
+{
+
+ private Text keystoreFilename;
+
+ private ComboViewer keystoreType;
+
+ public static final String IMPORT_KEYSTORE_HELP_ID = CertificateManagerActivator.PLUGIN_ID
+ + ".import_keystore"; //$NON-NLS-1$
+
+ private SelectionListener selectionListener = new SelectionAdapter()
+ {
+
+ @Override
+ public void widgetSelected(SelectionEvent e)
+ {
+ validatePage();
+ }
+ };
+
+ private boolean userChangedFilename = false;
+
+ private Composite mainComposite;
+
+ private File keystoreFile;
+
+ private String keystoreTypeString;
+
+ protected boolean keystoreAlreadyMapped = false;
+
+ private IKeyStore keyStoreNode;
+
+ /**
+ * @param pageName
+ */
+ protected ImportKeystorePage(String pageName)
+ {
+ super(pageName);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.dialogs.IDialogPage#createControl(org.eclipse.swt.widgets.Composite)
+ */
+ @Override
+ public void createControl(Composite parent)
+ {
+ mainComposite = new Composite(parent, SWT.FILL);
+ mainComposite.setLayout(new GridLayout(3, false));
+ mainComposite.setLayoutData(new GridData(GridData.FILL_BOTH));
+
+ setTitle(CertificateManagerNLS.ImportKeystorePage_Title);
+ setMessage(CertificateManagerNLS.ImportKeystorePage_Description);
+
+ createFilenameSection(mainComposite);
+ createKeystoreTypeSection(mainComposite);
+
+ validatePage();
+
+ setControl(mainComposite);
+
+ //set help id for this page
+ PlatformUI.getWorkbench().getHelpSystem().setHelp(parent, IMPORT_KEYSTORE_HELP_ID);
+ PlatformUI.getWorkbench().getHelpSystem().setHelp(mainComposite, IMPORT_KEYSTORE_HELP_ID);
+ }
+
+ private void createFilenameSection(Composite mainComposite)
+ {
+ Label keystoreFilenameLabel = new Label(mainComposite, SWT.NONE);
+ keystoreFilenameLabel.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false, 1, 1));
+ keystoreFilenameLabel
+ .setText(CertificateManagerNLS.CreateKeystorePage_KeystoreFilenameLabel);
+
+ keystoreFilename = new Text(mainComposite, SWT.SINGLE | SWT.BORDER);
+ keystoreFilename.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));
+ keystoreFilename.addSelectionListener(selectionListener);
+ keystoreFilename.addModifyListener(new ModifyListener()
+ {
+
+ @Override
+ public void modifyText(ModifyEvent e)
+ {
+ userChangedFilename = true;
+ validatePage();
+ }
+ });
+
+ Button chooseLocation = new Button(mainComposite, SWT.PUSH);
+ chooseLocation.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, false, false, 1, 1));
+ chooseLocation.setText(CertificateManagerNLS.CreateKeystorePage_KeystoreFilenameBrowse);
+ chooseLocation.addSelectionListener(new SelectionAdapter()
+ {
+ /* (non-Javadoc)
+ * @see org.eclipse.swt.events.SelectionAdapter#widgetSelected(org.eclipse.swt.events.SelectionEvent)
+ */
+ @Override
+ public void widgetSelected(SelectionEvent e)
+ {
+ Shell shell = Display.getCurrent().getActiveShell();
+
+ FileDialog dialog = new FileDialog(shell, SWT.OPEN);
+
+ String keystoreFilenameStr = dialog.open();
+
+ if (keystoreFilenameStr != null)
+ {
+ keystoreFilename.setText(keystoreFilenameStr);
+ keystoreType.getCombo().setFocus();
+ }
+ }
+ });
+ }
+
+ private void createKeystoreTypeSection(Composite parent)
+ {
+ Label keystoreTypeLabel = new Label(parent, SWT.NONE);
+ keystoreTypeLabel.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false, 1, 1));
+ keystoreTypeLabel.setText(CertificateManagerNLS.CreateKeystorePage_KeystoreType);
+
+ keystoreType = new ComboViewer(parent, SWT.READ_ONLY);
+ keystoreType.getCombo().setLayoutData(new GridData(SWT.FILL, SWT.NONE, true, false, 1, 1));
+ keystoreType.setContentProvider(new IStructuredContentProvider()
+ {
+
+ @Override
+ public void inputChanged(Viewer viewer, Object oldInput, Object newInput)
+ {
+ //do nothing
+ }
+
+ @Override
+ public void dispose()
+ {
+ //do nothing
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public Object[] getElements(Object inputElement)
+ {
+ return ((List<String>) inputElement).toArray();
+ }
+ });
+ keystoreType.setLabelProvider(new ILabelProvider()
+ {
+
+ @Override
+ public void removeListener(ILabelProviderListener listener)
+ {
+ //do nothing
+ }
+
+ @Override
+ public boolean isLabelProperty(Object element, String property)
+ {
+ return false;
+ }
+
+ @Override
+ public void dispose()
+ {
+ //do nothing
+ }
+
+ @Override
+ public void addListener(ILabelProviderListener listener)
+ {
+ //do nothing
+ }
+
+ @Override
+ public String getText(Object element)
+ {
+ return (String) element;
+ }
+
+ @Override
+ public Image getImage(Object element)
+ {
+ return null;
+ }
+ });
+
+ keystoreType.setInput(KeyStoreManager.getInstance().getAvailableTypes());
+
+ for (int i = 0; i < keystoreType.getCombo().getItemCount(); i++)
+ {
+ if (keystoreType.getCombo().getItem(i)
+ .compareToIgnoreCase(KeyStoreManager.getInstance().getDefaultType()) == 0)
+ {
+ keystoreType.getCombo().select(i);
+ }
+ }
+
+ keystoreType.getCombo().addModifyListener(new ModifyListener()
+ {
+
+ @Override
+ public void modifyText(ModifyEvent e)
+ {
+ validatePage();
+ }
+ });
+
+ //fill the third column with a blank label
+ Label separator2 = new Label(parent, SWT.NONE);
+ separator2.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false, 1, 1));
+ }
+
+ protected boolean validatePage()
+ {
+ String errorMessage = null;
+ boolean pageComplete = true;
+ keystoreAlreadyMapped = false;
+
+ keystoreFile = new File(keystoreFilename.getText());
+ keystoreTypeString = keystoreType.getCombo().getText();
+
+ if (keystoreType.getCombo().getText().isEmpty())
+ {
+ errorMessage = CertificateManagerNLS.ImportKeystorePage_KeystoreTypeCannotBeEmpty;
+ pageComplete = false;
+ }
+
+ if (userChangedFilename && !keystoreFile.exists())
+ {
+ errorMessage =
+ NLS.bind(CertificateManagerNLS.ImportKeystorePage_FileDoesNotExist,
+ keystoreFilename.getText());
+ pageComplete = false;
+ }
+
+ if (keystoreFile.exists())
+ {
+ if (keystoreFile.isFile())
+ {
+ int fileSize = -1;
+ try
+ {
+ fileSize = FileUtil.getFileSize(keystoreFile);
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ }
+ if (fileSize <= 0)
+ {
+ errorMessage =
+ NLS.bind(CertificateManagerNLS.ImportKeystorePage_FileEmpty,
+ keystoreFilename.getText());
+ pageComplete = false;
+ }
+ }
+ else if (keystoreFile.isDirectory())
+ {
+ errorMessage =
+ NLS.bind(
+ CertificateManagerNLS.ImportKeystorePage_DirectoryNotAllowedErrorMsg,
+ keystoreFilename.getText());
+ pageComplete = false;
+ }
+ }
+
+ if (keystoreFilename.getText().isEmpty())
+ {
+ errorMessage = CertificateManagerNLS.ImportKeystorePage_FilenameCannotBeEmpty;
+ pageComplete = false;
+ }
+
+ if (KeyStoreManager.getInstance().isKeystoreMapped(keystoreFile))
+ {
+ errorMessage =
+ NLS.bind(CertificateManagerNLS.ImportKeystorePage_KeystoreAlreadyMapped,
+ keystoreFilename.getText());
+ keystoreAlreadyMapped = true;
+ pageComplete = false;
+ }
+
+ setErrorMessage(errorMessage);
+ setPageComplete(pageComplete);
+
+ return pageComplete;
+ }
+
+ /**
+ * Import the keystore using the information provided by the user.
+ * @return True if the keystore was imported, false otherwise.
+ * */
+ protected boolean importKeystore(String password, boolean savePassword)
+ {
+ boolean successfullyImported = true;
+
+ validatePage();
+
+ if (isPageComplete())
+ {
+
+ KeyStoreNode keyStoreNode =
+ new KeyStoreNode(keystoreFile, keystoreType.getCombo().getText());
+
+ try
+ {
+ SigningAndKeysModelManager.getInstance().mapKeyStore(keyStoreNode);
+
+ if (savePassword)
+ {
+ PasswordProvider passwordProvider = new PasswordProvider(keystoreFile);
+ passwordProvider.saveKeyStorePassword(password);
+ }
+ }
+ catch (KeyStoreManagerException e)
+ {
+ //keystore already mapped
+ EclipseUtils.showErrorDialog(
+ CertificateManagerNLS.ImportKeystorePage_CouldNotImportKeystore,
+ e.getMessage());
+ successfullyImported = false;
+ }
+ }
+ else
+ {
+ successfullyImported = false;
+ }
+
+ return successfullyImported;
+ }
+
+ /**
+ * Import the keystore using the information provided by the user.
+ * @return True if the keystore was imported, false otherwise.
+ * */
+ public boolean importKeystore()
+ {
+ return importKeystore(null, false);
+ }
+
+ /**
+ *
+ * @return
+ */
+ protected IKeyStore getSelectedKeystore()
+ {
+ if ((keyStoreNode == null)
+ || !keyStoreNode.getFile().equals(keystoreFile)
+ || ((keyStoreNode.getFile().equals(keystoreFile)) && keyStoreNode.getType()
+ .equalsIgnoreCase(keystoreTypeString)))
+ {
+ keyStoreNode = new KeyStoreNode(keystoreFile, keystoreTypeString);
+ }
+ return keyStoreNode;
+ }
+
+ /**
+ * @return the mainComposite
+ */
+ protected Composite getMainComposite()
+ {
+ return mainComposite;
+ }
+
+}
diff --git a/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/wizards/ImportKeystoreWizard.java b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/wizards/ImportKeystoreWizard.java
new file mode 100644
index 0000000..55e6d97
--- /dev/null
+++ b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/wizards/ImportKeystoreWizard.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.motorolamobility.studio.android.certmanager.ui.wizards;
+
+import org.eclipse.jface.wizard.Wizard;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.ui.PlatformUI;
+
+import com.motorolamobility.studio.android.certmanager.CertificateManagerActivator;
+import com.motorolamobility.studio.android.certmanager.i18n.CertificateManagerNLS;
+
+public class ImportKeystoreWizard extends Wizard
+{
+ ImportKeystorePage importKeystorePage = null;
+
+ private static final String WIZARD_BANNER = "icons/wizban/import_keystore_wiz.png"; //$NON-NLS-1$
+
+ public ImportKeystoreWizard()
+ {
+ setWindowTitle(CertificateManagerNLS.ImportKeystorePage_Title);
+ setDefaultPageImageDescriptor(CertificateManagerActivator.imageDescriptorFromPlugin(
+ CertificateManagerActivator.PLUGIN_ID, WIZARD_BANNER));
+
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.wizard.Wizard#createPageControls(org.eclipse.swt.widgets.Composite)
+ */
+ @Override
+ public void createPageControls(Composite pageContainer)
+ {
+ super.createPageControls(pageContainer);
+
+ //the shell has the same help as its page
+ PlatformUI.getWorkbench().getHelpSystem()
+ .setHelp(getShell(), ImportKeystorePage.IMPORT_KEYSTORE_HELP_ID);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.wizard.Wizard#performFinish()
+ */
+ @Override
+ public boolean performFinish()
+ {
+ return importKeystorePage.importKeystore();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.wizard.Wizard#addPages()
+ */
+ @Override
+ public void addPages()
+ {
+ importKeystorePage =
+ new ImportKeystorePage(CertificateManagerNLS.ImportKeystoreWizard_ImportKeystore);
+ addPage(importKeystorePage);
+ }
+}
diff --git a/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/wizards/RemoveExternalPackageSignaturePage.java b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/wizards/RemoveExternalPackageSignaturePage.java
new file mode 100644
index 0000000..cca8989
--- /dev/null
+++ b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/wizards/RemoveExternalPackageSignaturePage.java
@@ -0,0 +1,686 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.studio.android.certmanager.ui.wizards;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.eclipse.core.internal.resources.Folder;
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IFolder;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.jface.dialogs.IDialogConstants;
+import org.eclipse.jface.dialogs.IMessageProvider;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.jface.viewers.ViewerFilter;
+import org.eclipse.jface.wizard.WizardPage;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Color;
+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.DirectoryDialog;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.swt.widgets.Tree;
+import org.eclipse.swt.widgets.TreeItem;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.dialogs.ElementTreeSelectionDialog;
+import org.eclipse.ui.dialogs.ISelectionStatusValidator;
+import org.eclipse.ui.model.WorkbenchContentProvider;
+import org.eclipse.ui.model.WorkbenchLabelProvider;
+import org.eclipse.ui.views.navigator.ResourceComparator;
+
+import com.motorolamobility.studio.android.certmanager.CertificateManagerActivator;
+import com.motorolamobility.studio.android.certmanager.i18n.CertificateManagerNLS;
+
+@SuppressWarnings("restriction")
+public class RemoveExternalPackageSignaturePage extends WizardPage
+{
+ private Text sourceDirText = null;
+
+ private Button browseDirButton = null;
+
+ private Button workspaceDirButton = null;
+
+ private Tree packagesTree = null;
+
+ private Button selectAllButton = null;
+
+ private Button deselectAllButton = null;
+
+ private WizardSelection selection = null;
+
+ protected Composite mainComposite = null;
+
+ /**
+ * Create a new wizard page based on selection
+ *
+ * @param pageName
+ * the page name
+ * @param selection
+ * the selection
+ */
+ public RemoveExternalPackageSignaturePage(String pageName, IStructuredSelection selection)
+ {
+ super(pageName);
+ setDescription(CertificateManagerNLS.UNSIGN_EXTERNAL_PKG_WIZARD_DESCRIPTION);
+ setTitle(CertificateManagerNLS.UNSIGN_EXTERNAL_PKG_WIZARD_WINDOW_TITLE);
+ this.selection = new WizardSelection(selection);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * org.eclipse.jface.dialogs.IDialogPage#createControl(org.eclipse.swt.widgets
+ * .Composite)
+ */
+ @Override
+ public void createControl(Composite parent)
+ {
+ this.mainComposite = new Composite(parent, SWT.NULL);
+ // create new layout with 3 columns of different sizes
+ GridLayout layout = new GridLayout(4, false);
+ this.mainComposite.setLayout(layout);
+
+ Label sourceDirLabel = new Label(this.mainComposite, SWT.NONE);
+ sourceDirLabel.setText(CertificateManagerNLS.SIGN_EXTERNAL_PKG_WIZARD_SOURCE_DIR_LABEL);
+
+ GridData layoutData = new GridData(SWT.FILL, SWT.CENTER, false, false, 1, 1);
+ sourceDirLabel.setLayoutData(layoutData);
+
+ this.sourceDirText = new Text(this.mainComposite, SWT.BORDER);
+ layoutData = new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1);
+ this.sourceDirText.setLayoutData(layoutData);
+ this.sourceDirText.addListener(SWT.Modify, new SourceDirectoryTextListener());
+
+ this.browseDirButton = new Button(this.mainComposite, SWT.PUSH);
+ this.browseDirButton.setText(CertificateManagerNLS.SIGN_EXTERNAL_PKG_WIZARD_FILESYSTEM);
+ layoutData = new GridData(SWT.FILL, SWT.CENTER, false, false, 1, 1);
+ this.browseDirButton.setLayoutData(layoutData);
+ this.browseDirButton.addListener(SWT.Selection, new BrowseButtonListener());
+
+ this.workspaceDirButton = new Button(this.mainComposite, SWT.PUSH);
+ this.workspaceDirButton.setText(CertificateManagerNLS.SIGN_EXTERNAL_PKG_WIZARD_WORKSPACE);
+ layoutData = new GridData(SWT.FILL, SWT.CENTER, false, false, 1, 1);
+ this.workspaceDirButton.setLayoutData(layoutData);
+ this.workspaceDirButton.addListener(SWT.Selection, new WorkspaceButtonListener());
+
+ createExtendedArea(this.mainComposite);
+
+ createPackageTreeLabel();
+
+ this.packagesTree = new Tree(this.mainComposite, SWT.BORDER | SWT.CHECK | SWT.V_SCROLL);
+ layoutData = new GridData(SWT.FILL, SWT.FILL, true, true, 3, 2);
+ layoutData.heightHint = 150;
+ this.packagesTree.setLayoutData(layoutData);
+ this.packagesTree.addListener(SWT.Selection, new TreeSelectionListener());
+
+ Composite selectionButtons = new Composite(this.mainComposite, SWT.FILL);
+ layoutData = new GridData(SWT.FILL, SWT.TOP, false, true, 1, 2);
+ selectionButtons.setLayoutData(layoutData);
+ FillLayout row = new FillLayout(SWT.VERTICAL);
+ row.spacing = 3;
+ selectionButtons.setLayout(row);
+
+ this.selectAllButton = new Button(selectionButtons, SWT.PUSH);
+ this.selectAllButton
+ .setText(CertificateManagerNLS.PACKAGE_EXPORT_WIZARD_AREA_SELECT_ALL_BUTTON);
+ SelectionButtonsListener selectionButtonsListener = new SelectionButtonsListener();
+ this.selectAllButton.addListener(SWT.Selection, selectionButtonsListener);
+
+ this.deselectAllButton = new Button(selectionButtons, SWT.PUSH);
+ this.deselectAllButton
+ .setText(CertificateManagerNLS.PACKAGE_EXPORT_WIZARD_AREA_DESELECT_ALL_BUTTON);
+ this.deselectAllButton.addListener(SWT.Selection, selectionButtonsListener);
+
+ this.sourceDirText.setText(this.selection.getSelectedDirectory());
+ populateTree(this.selection.getSelectedPackages());
+ updatePageComplete();
+ setControl(this.mainComposite);
+ }
+
+ protected void createPackageTreeLabel()
+ {
+ GridData layoutData;
+ Label packagesLabel = new Label(this.mainComposite, SWT.NONE);
+ packagesLabel
+ .setText(CertificateManagerNLS.RemoveExternalPackageSignaturePage_Package_Tree_Label);
+ layoutData = new GridData(SWT.FILL, SWT.CENTER, false, false, 4, 1);
+ packagesLabel.setLayoutData(layoutData);
+ }
+
+ /**
+ * Create a composite area after the basic composite Subclasses that need
+ * more than the basic package selection screen should override this method
+ *
+ * @param mainComposite
+ */
+ protected void createExtendedArea(Composite parent)
+ {
+ PlatformUI
+ .getWorkbench()
+ .getHelpSystem()
+ .setHelp(parent,
+ CertificateManagerActivator.UNSIGN_EXTERNAL_PKG_WIZARD_CONTEXT_HELP_ID);
+ }
+
+ /**
+ * Populates the tree with the packages of base dir Requires a valid folder
+ * set as source dir
+ */
+ private void populateTree(List<String> selection)
+ {
+ File sourceDir = getSourcePath().toFile();
+ Color gray = new Color(null, 130, 130, 130);
+ this.packagesTree.removeAll();
+ if (sourceDir.isDirectory() && sourceDir.canWrite())
+ {
+ File[] list = sourceDir.listFiles();
+ for (File file : list)
+ {
+ if (file.canRead() && file.isFile() && file.getName().endsWith("apk")) //$NON-NLS-1$
+ {
+ TreeItem fileItem = new TreeItem(this.packagesTree, SWT.NONE);
+ String text = file.getName();
+ if (!file.canWrite())
+ {
+ text += " [" + CertificateManagerNLS.READ_ONLY_TEXT //$NON-NLS-1$
+ + "]"; //$NON-NLS-1$
+ fileItem.setForeground(gray);
+ }
+
+ fileItem.setText(text);
+ fileItem.setData(file);
+ if ((selection != null) && selection.contains(file.getName())
+ && file.canWrite())
+ {
+ fileItem.setChecked(true);
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Validates if the source directory is valid one
+ *
+ * @return true if the source dir text is valid, false otherwise
+ */
+ private boolean isSourceDirValid()
+ {
+
+ String messageAux = null;
+ int severity = IMessageProvider.NONE;
+
+ /*
+ * Check if the selected location is valid, even if non existent.
+ */
+ IPath path = new Path(this.sourceDirText.getText());
+
+ // Test if path is blank, to warn user instead of show an error message
+ if (this.sourceDirText.getText().equals("")) //$NON-NLS-1$
+ {
+ messageAux =
+
+ CertificateManagerNLS.SIGN_EXTERNAL_PKG_WIZARD_SOURCE_DIR_EMPTY;
+ severity = IMessageProvider.INFORMATION;
+ }
+
+ /*
+ * Do Win32 Validation
+ */
+ if ((messageAux == null) && Platform.getOS().equalsIgnoreCase(Platform.OS_WIN32))
+ {
+ // test path size
+ if (path.toString().length() > 255)
+ {
+ messageAux =
+
+ CertificateManagerNLS.SELECTOR_MESSAGE_LOCATION_ERROR_PATH_TOO_LONG;
+ severity = IMessageProvider.WARNING;
+ }
+ String device = path.getDevice();
+ File deviceFile = null;
+ if (device != null)
+ {
+ deviceFile = new File(path.getDevice());
+ }
+
+ if ((device != null) && !deviceFile.exists())
+ {
+ messageAux =
+
+ CertificateManagerNLS.SELECTOR_MESSAGE_LOCATION_ERROR_INVALID_DEVICE + " ["
+ + device + "]";
+ severity = IMessageProvider.ERROR;
+ }
+
+ }
+ // test if path is absolute
+ if (messageAux == null)
+ {
+ if (!path.isAbsolute() || !path.toFile().exists())
+ {
+ messageAux =
+
+ CertificateManagerNLS.SIGN_EXTERNAL_PKG_WIZARD_SOURCE_DIR_INVALID;
+ severity = IMessageProvider.ERROR;
+ }
+ }
+
+ if (messageAux == null)
+ {
+ for (String folderName : path.segments())
+ {
+ if (!ResourcesPlugin.getWorkspace().validateName(folderName, IResource.FOLDER)
+ .isOK())
+ {
+ messageAux =
+
+ CertificateManagerNLS.SIGN_EXTERNAL_PKG_WIZARD_SOURCE_DIR_INVALID;
+ severity = IMessageProvider.ERROR;
+
+ }
+ }
+ }
+
+ if ((messageAux == null) && ((path.toFile().exists() && !path.toFile().isDirectory())))
+ {
+ messageAux =
+
+ CertificateManagerNLS.SIGN_EXTERNAL_PKG_WIZARD_SOURCE_DIR_NOT_DIRECTORY;
+ severity = IMessageProvider.ERROR;
+
+ }
+
+ /*
+ * Setting message
+ */
+ if (messageAux == null)
+ {
+ messageAux = CertificateManagerNLS.SIGN_EXTERNAL_PKG_WIZARD_DESCRIPTION;
+ severity = IMessageProvider.NONE;
+ }
+ setMessage(messageAux, severity);
+ return severity == IMessageProvider.NONE;
+ }
+
+ /**
+ * @return the path of base dir where packages are located
+ */
+ public IPath getSourcePath()
+ {
+ return new Path(this.sourceDirText.getText());
+ }
+
+ /**
+ *
+ * @return the list with selected packages
+ */
+ public List<String> getSelectedPackages()
+ {
+ ArrayList<String> selected = new ArrayList<String>();
+ for (TreeItem item : this.packagesTree.getItems())
+ {
+ if (item.getChecked())
+ {
+ selected.add(item.getData().toString());
+ }
+ }
+
+ return selected;
+ }
+
+ /**
+ * Update the page status, validating each field of this page Subclasses
+ * that overrides createExtendedArea method should override this method too
+ * to validate the new fields
+ */
+ public void updatePageComplete()
+ {
+
+ String messageAux = null;
+ int severity = IMessageProvider.NONE;
+
+ /*
+ * Check if there are available certificates and if selection isn't null
+ */
+ if (isSourceDirValid())
+ {
+ if (this.packagesTree.getItemCount() == 0)
+ {
+ messageAux =
+
+ CertificateManagerNLS.SIGN_EXTERNAL_PKG_WIZARD_NO_AVAILABLE_PACKAGES;
+ severity = IMessageProvider.ERROR;
+ }
+ }
+ else
+ {
+ messageAux = getMessage();
+ severity = getMessageType();
+ }
+
+ if ((messageAux == null) && (getSelectedPackages().size() == 0))
+ {
+ messageAux =
+
+ CertificateManagerNLS.UNSIGN_EXTERNAL_PKG_WIZARD_NO_PACKAGES_SELECTED;
+ severity = IMessageProvider.INFORMATION;
+ }
+
+ if (messageAux == null)
+ {
+ messageAux =
+
+ CertificateManagerNLS.UNSIGN_EXTERNAL_PKG_WIZARD_DESCRIPTION;
+ severity = IMessageProvider.NONE;
+ }
+
+ setMessage(messageAux, severity);
+ setPageComplete(severity == IMessageProvider.NONE);
+
+ }
+
+ /**
+ * This class implements the listener of filesystem button, opening the browse
+ * window and updating the dir text
+ */
+ class BrowseButtonListener implements Listener
+ {
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * org.eclipse.swt.widgets.Listener#handleEvent(org.eclipse.swt.widgets
+ * .Event)
+ */
+ @Override
+ public void handleEvent(Event event)
+ {
+ DirectoryDialog dialog =
+ new DirectoryDialog(
+ RemoveExternalPackageSignaturePage.this.mainComposite.getShell());
+ dialog.setFilterPath(!RemoveExternalPackageSignaturePage.this.sourceDirText.getText()
+ .trim().equals("") ? RemoveExternalPackageSignaturePage.this.sourceDirText
+ .getText() : null);
+ String path = dialog.open();
+ if (path != null)
+ {
+ RemoveExternalPackageSignaturePage.this.sourceDirText.setText(path);
+ populateTree(null);
+ updatePageComplete();
+ }
+ }
+
+ }
+
+ /**
+ * This class implements the listener of workspace button, opening the browse
+ * window and updating the dir text
+ */
+ class WorkspaceButtonListener implements Listener
+ {
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * org.eclipse.swt.widgets.Listener#handleEvent(org.eclipse.swt.widgets
+ * .Event)
+ */
+ @Override
+ public void handleEvent(Event event)
+ {
+ ElementTreeSelectionDialog dialog =
+ new ElementTreeSelectionDialog(getShell(), new WorkbenchLabelProvider(),
+ new WorkbenchContentProvider());
+ dialog.setTitle(CertificateManagerNLS.SIGN_EXTERNAL_PKG_WIZARD_WORKSPACE_SIMPLE);
+ dialog.setMessage(CertificateManagerNLS.SIGN_EXTERNAL_PKG_WIZARD_CHOOSE);
+
+ // set the workspace as the limit
+ dialog.setInput(ResourcesPlugin.getWorkspace().getRoot());
+ dialog.setComparator(new ResourceComparator(ResourceComparator.NAME));
+
+ //don't display files
+ dialog.addFilter(new ViewerFilter()
+ {
+
+ @Override
+ public boolean select(Viewer viewer, Object parentElement, Object element)
+ {
+ boolean filtered = false;
+
+ if (element instanceof IFile)
+ {
+ filtered = true;
+ }
+ return !filtered;
+ }
+ });
+
+ //user can select only one folder
+ dialog.setValidator(new ISelectionStatusValidator()
+ {
+
+ @Override
+ public IStatus validate(Object[] selection)
+ {
+ IStatus valid =
+ new Status(IStatus.ERROR, CertificateManagerActivator.PLUGIN_ID, ""); //$NON-NLS-1$
+ if (selection.length == 1)
+ {
+ if (selection[0] instanceof Folder)
+ {
+ valid =
+ new Status(IStatus.OK, CertificateManagerActivator.PLUGIN_ID,
+ ""); //$NON-NLS-1$
+ }
+ }
+ return valid;
+ }
+ });
+
+ String path = null;
+ if (dialog.open() == IDialogConstants.OK_ID)
+ {
+ Folder resource = (Folder) dialog.getFirstResult();
+ path = resource.getLocation().toString();
+ }
+
+ if (path != null)
+ {
+ RemoveExternalPackageSignaturePage.this.sourceDirText.setText(path);
+ populateTree(null);
+ updatePageComplete();
+ }
+ }
+ }
+
+ /**
+ * Listener to validate any SourceDirectory text Change
+ */
+ class SourceDirectoryTextListener implements Listener
+ {
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * org.eclipse.swt.widgets.Listener#handleEvent(org.eclipse.swt.widgets
+ * .Event)
+ */
+ @Override
+ public void handleEvent(Event event)
+ {
+ RemoveExternalPackageSignaturePage.this.packagesTree.removeAll();
+ if (isSourceDirValid())
+ {
+ populateTree(null);
+ }
+ updatePageComplete();
+ }
+
+ }
+
+ /**
+ * This class handles clicks on select all and deselect all buttons
+ */
+ class SelectionButtonsListener implements Listener
+ {
+
+ /**
+ * Check/Uncheck all items
+ *
+ * @param check
+ * : true for check, false for unckeck
+ */
+ private void setCheckedAll(boolean check)
+ {
+ for (TreeItem item : RemoveExternalPackageSignaturePage.this.packagesTree.getItems())
+ {
+ item.setChecked(check);
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * org.eclipse.swt.widgets.Listener#handleEvent(org.eclipse.swt.widgets
+ * .Event)
+ */
+ @Override
+ public void handleEvent(Event event)
+ {
+ if (event.widget == RemoveExternalPackageSignaturePage.this.selectAllButton)
+ {
+ setCheckedAll(true);
+ }
+ else if (event.widget == RemoveExternalPackageSignaturePage.this.deselectAllButton)
+ {
+ setCheckedAll(false);
+ }
+ updatePageComplete();
+ }
+
+ }
+
+ /**
+ * Listener to update wizard status according tree selection
+ */
+ class TreeSelectionListener implements Listener
+ {
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * org.eclipse.swt.widgets.Listener#handleEvent(org.eclipse.swt.widgets
+ * .Event)
+ */
+ @Override
+ public void handleEvent(Event event)
+ {
+ updatePageComplete();
+ }
+
+ }
+
+ /**
+ * This class gets the workspace selection and makes a suitable selection to
+ * the wizard
+ */
+ class WizardSelection
+ {
+ private ArrayList<String> packages = null;
+
+ private IPath directory = null;
+
+ public WizardSelection(IStructuredSelection selection)
+ {
+ this.packages = new ArrayList<String>();
+ Iterator<?> iterator = selection.iterator();
+ while (iterator.hasNext())
+ {
+ Object obj = iterator.next();
+ if (obj instanceof IFile)
+ {
+ IFile file = (IFile) obj;
+
+ if (file.getLocation().getFileExtension().equals("apk")) //$NON-NLS-1$
+ {
+ if (this.directory == null)
+ {
+ this.directory = file.getLocation().removeLastSegments(1);
+ this.packages.add(file.getName());
+ }
+ else
+ {
+ if (file.getLocation().matchingFirstSegments(this.directory) == this.directory
+ .segmentCount())
+ {
+ this.packages.add(file.getName());
+ }
+ }
+ }
+ }
+ else if (obj instanceof IFolder)
+ {
+ if (this.directory == null)
+ {
+ this.directory = ((IFolder) obj).getLocation();
+ }
+ }
+ }
+ if (this.directory == null)
+ {
+ this.directory = new Path(""); //$NON-NLS-1$
+ }
+ }
+
+ /**
+ *
+ * @return the selected directory
+ */
+ public String getSelectedDirectory()
+ {
+ return this.directory.toOSString();
+ }
+
+ /**
+ *
+ * @return the selected packages
+ */
+ public List<String> getSelectedPackages()
+ {
+ return this.packages;
+ }
+
+ }
+}
diff --git a/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/wizards/RemoveExternalPackageSignatureWizard.java b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/wizards/RemoveExternalPackageSignatureWizard.java
new file mode 100644
index 0000000..7783262
--- /dev/null
+++ b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/wizards/RemoveExternalPackageSignatureWizard.java
@@ -0,0 +1,282 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.studio.android.certmanager.ui.wizards;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.lang.reflect.InvocationTargetException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.jar.JarFile;
+
+import org.eclipse.core.resources.IContainer;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.MultiStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.jface.dialogs.ErrorDialog;
+import org.eclipse.jface.dialogs.ProgressMonitorDialog;
+import org.eclipse.jface.operation.IRunnableWithProgress;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.wizard.Wizard;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.plugin.AbstractUIPlugin;
+
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorolamobility.studio.android.certmanager.CertificateManagerActivator;
+import com.motorolamobility.studio.android.certmanager.i18n.CertificateManagerNLS;
+import com.motorolamobility.studio.android.certmanager.packaging.PackageFile;
+
+/**
+ * This Wizard removes a signature of a package. based on a root dir, It shows a
+ * list of packages to remove signature
+ */
+public class RemoveExternalPackageSignatureWizard extends Wizard
+{
+ private RemoveExternalPackageSignaturePage page = null;
+
+ public RemoveExternalPackageSignatureWizard(IStructuredSelection selection)
+ {
+ setWindowTitle(CertificateManagerNLS.UNSIGN_EXTERNAL_PKG_WIZARD_WINDOW_TITLE);
+ setNeedsProgressMonitor(true);
+ this.page = new RemoveExternalPackageSignaturePage("removeSigPage", selection);
+ setDefaultPageImageDescriptor(AbstractUIPlugin.imageDescriptorFromPlugin(
+ CertificateManagerActivator.PLUGIN_ID,
+ CertificateManagerActivator.REMOVE_SIGNATURE_WIZ_BAN));
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.jface.wizard.Wizard#addPages()
+ */
+ @Override
+ public void addPages()
+ {
+ addPage(this.page);
+ }
+
+ /**
+ * Finishes this wizard removing packages signatures
+ */
+ @Override
+ public boolean performFinish()
+ {
+ final List<String> defectivePackages = new ArrayList<String>();
+ IRunnableWithProgress finishAction = new IRunnableWithProgress()
+ {
+
+ @Override
+ public void run(IProgressMonitor monitor) throws InvocationTargetException,
+ InterruptedException
+ {
+ List<String> selectedFiles =
+ RemoveExternalPackageSignatureWizard.this.page.getSelectedPackages();
+ monitor.beginTask(CertificateManagerNLS.UNSIGN_EXTERNAL_PKG_WIZARD_WINDOW_TITLE,
+ selectedFiles.size());
+ for (String selected : selectedFiles)
+ {
+ File file = new File(selected);
+ monitor.setTaskName(CertificateManagerNLS.UNSIGN_EXTERNAL_PKG_WIZARD_OPERATION
+ + " " + file.getName());
+ if ((file != null) && file.exists() && file.isFile() && file.canWrite())
+ {
+ OutputStream fileToWrite = null;
+ JarFile jar = null;
+ PackageFile pack = null;
+ try
+ {
+ // Open package and remove signature
+ jar = new JarFile(file);
+ pack = new PackageFile(jar);
+ try
+ {
+ pack.removeMetaEntryFiles();
+ }
+ catch (IOException e)
+ {
+ StudioLogger.error(
+ RemoveExternalPackageSignatureWizard.class.toString(),
+ "Impossible to delete temporary files");
+ throw e;
+ }
+
+ // Write the new package file
+ fileToWrite = new FileOutputStream(file);
+ pack.write(fileToWrite);
+ PackageFile.zipAlign(file);
+ }
+ catch (IOException e)
+ {
+ defectivePackages.add(selected);
+ StudioLogger.error(
+ RemoveExternalPackageSignatureWizard.class.toString(),
+ "Impossible write to package: " + selected + " "
+ + e.getMessage());
+ }
+ catch (SecurityException e)
+ {
+ defectivePackages.add(selected);
+ StudioLogger.error(
+ RemoveExternalPackageSignatureWizard.class.toString(),
+ "Impossible write to package: " + selected + " "
+ + e.getMessage());
+ }
+ finally
+ {
+
+ System.gc(); // Force garbage collector to avoid
+ // errors when deleting temp files
+
+ try
+ {
+ if (jar != null)
+ {
+ jar.close();
+ }
+
+ if (pack != null)
+ {
+ pack.removeTemporaryEntryFiles();
+ }
+
+ if (fileToWrite != null)
+ {
+ fileToWrite.close();
+ }
+ }
+ catch (IOException e)
+ {
+ // Silent exception. Only log the deletion
+ // exception.
+ StudioLogger.error(CertificateManagerActivator.PLUGIN_ID,
+ "Deleting temporary files");
+ }
+ }
+ }
+ else
+ {
+ defectivePackages.add(selected);
+ }
+ monitor.worked(1);
+ }
+ monitor.done();
+ }
+
+ };
+
+ try
+ {
+ PlatformUI.getWorkbench().getProgressService()
+ .runInUI(new ProgressMonitorDialog(getShell()), finishAction, null);
+ }
+ catch (InvocationTargetException e1)
+ {
+ StudioLogger.error(RemoveExternalPackageSignatureWizard.class.toString(),
+ "Error running finish actions");
+ }
+ catch (InterruptedException e1)
+ {
+ StudioLogger.error(RemoveExternalPackageSignatureWizard.class.toString(),
+ "Error running finish actions");
+ }
+
+ if (ResourcesPlugin.getWorkspace().getRoot().getLocation()
+ .isPrefixOf(this.page.getSourcePath()))
+ {
+ org.eclipse.ui.actions.WorkspaceModifyOperation op =
+ new org.eclipse.ui.actions.WorkspaceModifyOperation()
+ {
+
+ @Override
+ protected void execute(IProgressMonitor monitor) throws CoreException,
+ InvocationTargetException, InterruptedException
+ {
+ for (IContainer container : ResourcesPlugin
+ .getWorkspace()
+ .getRoot()
+ .findContainersForLocation(
+ RemoveExternalPackageSignatureWizard.this.page
+ .getSourcePath()))
+ {
+
+ container.refreshLocal(IResource.DEPTH_INFINITE, monitor);
+ }
+
+ }
+
+ };
+ try
+ {
+ PlatformUI.getWorkbench().getProgressService().run(false, false, op);
+ }
+ catch (InvocationTargetException e)
+ {
+ StudioLogger.error(RemoveExternalPackageSignatureWizard.class.toString(),
+ "Error refreshing workspace");
+ }
+ catch (InterruptedException e)
+ {
+ StudioLogger.error(RemoveExternalPackageSignatureWizard.class.toString(),
+ "Error refreshing workspace");
+ }
+ }
+
+ if (!defectivePackages.isEmpty())
+ {
+ MultiStatus errors =
+ new MultiStatus(CertificateManagerActivator.PLUGIN_ID, IStatus.ERROR,
+ CertificateManagerNLS.UNSIGN_EXTERNAL_PKG_WIZARD_ERROR_REASON, null);
+ for (String defect : defectivePackages)
+ {
+ errors.add(new Status(IStatus.ERROR, CertificateManagerActivator.PLUGIN_ID, defect));
+ }
+
+ ErrorDialog errorBox =
+ new ErrorDialog(getShell(),
+ CertificateManagerNLS.UNSIGN_EXTERNAL_PKG_WIZARD_WINDOW_TITLE,
+ CertificateManagerNLS.UNSIGN_EXTERNAL_PKG_WIZARD_ERROR, errors,
+ IStatus.ERROR);
+ errorBox.open();
+ }
+
+ return true;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * org.eclipse.jface.wizard.Wizard#createPageControls(org.eclipse.swt.widgets
+ * .Composite)
+ */
+ @Override
+ public void createPageControls(Composite pageContainer)
+ {
+ super.createPageControls(pageContainer);
+ PlatformUI
+ .getWorkbench()
+ .getHelpSystem()
+ .setHelp(getShell(),
+ CertificateManagerActivator.UNSIGN_EXTERNAL_PKG_WIZARD_CONTEXT_HELP_ID);
+ }
+}
diff --git a/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/wizards/SelectExistentKeystorePage.java b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/wizards/SelectExistentKeystorePage.java
new file mode 100644
index 0000000..71ecaef
--- /dev/null
+++ b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/wizards/SelectExistentKeystorePage.java
@@ -0,0 +1,200 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.motorolamobility.studio.android.certmanager.ui.wizards;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.ModifyEvent;
+import org.eclipse.swt.events.ModifyListener;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.ui.PlatformUI;
+
+import com.motorolamobility.studio.android.certmanager.CertificateManagerActivator;
+import com.motorolamobility.studio.android.certmanager.i18n.CertificateManagerNLS;
+
+/**
+ * Enables selection of a keystore (similar to {@link ImportKeystorePage} functionality.
+ * It adds a checkbox button that enables user to add the imported keystore into Signing and Keys view.
+ */
+public class SelectExistentKeystorePage extends ImportKeystorePage
+{
+ private static final int SMALL_TEXT_SIZE = 64;
+
+ public static final String SELECT_KEYSTORE_HELP_ID = CertificateManagerActivator.PLUGIN_ID
+ + ".select_keystore"; //$NON-NLS-1$
+
+ private Button alsoImportIntoView = null;
+
+ private boolean importIntoView = true;
+
+ private Label keystorePasswordLabel;
+
+ private Text keystorePassword;
+
+ private Button savePasswordCheckBox;
+
+ private boolean canSavePassword = false;
+
+ private String password = null;
+
+ private SelectionListener selectionListener = new SelectionAdapter()
+ {
+
+ @Override
+ public void widgetSelected(SelectionEvent e)
+ {
+ validatePage();
+ }
+ };
+
+ protected SelectExistentKeystorePage(String pageName)
+ {
+ super(pageName);
+ }
+
+ @Override
+ public void createControl(Composite parent)
+ {
+ super.createControl(parent);
+ setTitle(CertificateManagerNLS.SelectExistentKeystorePage_WizardPageTitle);
+ setMessage(CertificateManagerNLS.SelectExistentKeystorePage_WizardPageMessage);
+
+ //KEYSTORE PASSWORD SECTION
+ keystorePasswordLabel = new Label(getMainComposite(), SWT.NONE);
+ keystorePasswordLabel
+ .setText(CertificateManagerNLS.SelectExistentKeystorePage_KeystorePasswordLabel); //$NON-NLS-2$
+
+ keystorePassword = new Text(getMainComposite(), SWT.SINGLE | SWT.BORDER | SWT.PASSWORD);
+ keystorePassword.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 2, 1));
+ keystorePassword.setTextLimit(SMALL_TEXT_SIZE);
+ keystorePassword.addSelectionListener(selectionListener);
+ keystorePassword.addModifyListener(new ModifyListener()
+ {
+
+ @Override
+ public void modifyText(ModifyEvent e)
+ {
+ password = keystorePassword.getText();
+ validatePage();
+ }
+ });
+
+ //Creates the save password checkbox
+ savePasswordCheckBox = new Button(getMainComposite(), SWT.CHECK);
+ savePasswordCheckBox.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false, 3, 1));
+ savePasswordCheckBox.setText(CertificateManagerNLS.PasswordProvider_SaveThisPassword);
+ savePasswordCheckBox.setSelection(false);
+ savePasswordCheckBox.setVisible(importIntoView);
+ savePasswordCheckBox.addSelectionListener(new SelectionAdapter()
+ {
+ @Override
+ public void widgetSelected(SelectionEvent e)
+ {
+ //update according to check status
+ canSavePassword = savePasswordCheckBox.getSelection();
+ }
+
+ });
+
+ alsoImportIntoView = new Button(getMainComposite(), SWT.CHECK);
+ alsoImportIntoView.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false, 3, 1));
+ alsoImportIntoView
+ .setText(CertificateManagerNLS.SelectExistentKeystorePage_CheckboxText_AlsoImportIntoSigningView);
+ alsoImportIntoView.setSelection(importIntoView);
+ alsoImportIntoView.addSelectionListener(new SelectionAdapter()
+ {
+ @Override
+ public void widgetSelected(SelectionEvent e)
+ {
+ //update according to check status
+ importIntoView = alsoImportIntoView.getSelection();
+ savePasswordCheckBox.setEnabled(importIntoView);
+ canSavePassword = importIntoView && savePasswordCheckBox.getSelection();
+ }
+
+ });
+
+ //set help id for this page
+ PlatformUI.getWorkbench().getHelpSystem()
+ .setHelp(getMainComposite(), SELECT_KEYSTORE_HELP_ID);
+ }
+
+ /**
+ * @return the importIntoView
+ */
+ public boolean needToImportIntoView()
+ {
+ return importIntoView;
+ }
+
+ /**
+ * @return the password
+ */
+ protected String getPassword()
+ {
+ return password;
+ }
+
+ @Override
+ protected boolean validatePage()
+ {
+ boolean pageComplete = super.validatePage();
+
+ String infoMessage = CertificateManagerNLS.SelectExistentKeystorePage_WizardPageMessage;
+ String errorMessage = null;
+
+ if (pageComplete)
+ {
+ if (!keystoreAlreadyMapped)
+ {
+ if (keystorePassword.getText().isEmpty())
+ {
+ pageComplete = false;
+ errorMessage = CertificateManagerNLS.CertificateBlock_EnterPassword_InfoMessage;
+ }
+ setMessage(infoMessage);
+ setErrorMessage(errorMessage);
+ setPageComplete(pageComplete);
+ }
+ }
+
+ return pageComplete;
+ }
+
+ /**
+ * @return the canSavePassword
+ */
+ protected boolean canSavePassword()
+ {
+ return canSavePassword;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorolamobility.studio.android.certmanager.ui.wizards.ImportKeystorePage#importKeystore()
+ */
+ @Override
+ public boolean importKeystore()
+ {
+ return importKeystore(getPassword(), canSavePassword());
+ }
+}
diff --git a/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/wizards/SelectExistentKeystoreWizard.java b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/wizards/SelectExistentKeystoreWizard.java
new file mode 100644
index 0000000..d5612df
--- /dev/null
+++ b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/wizards/SelectExistentKeystoreWizard.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.motorolamobility.studio.android.certmanager.ui.wizards;
+
+import org.eclipse.jface.wizard.Wizard;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.ui.PlatformUI;
+
+import com.motorolamobility.studio.android.certmanager.CertificateManagerActivator;
+import com.motorolamobility.studio.android.certmanager.exception.InvalidPasswordException;
+import com.motorolamobility.studio.android.certmanager.exception.KeyStoreManagerException;
+import com.motorolamobility.studio.android.certmanager.i18n.CertificateManagerNLS;
+import com.motorolamobility.studio.android.certmanager.ui.model.IKeyStore;
+import com.motorolamobility.studio.android.certmanager.ui.model.KeyStoreNode;
+
+/**
+ * Enables selection of a keystore (similar to {@link ImportKeystoreWizard} functionality.
+ * It adds a checkbox button that enables user to add the imported keystore into Signing and Keys view.
+ */
+public class SelectExistentKeystoreWizard extends Wizard
+{
+ protected SelectExistentKeystorePage selectExistentKeystorePage = null;
+
+ private static final String WIZARD_BANNER = "icons/wizban/import_keystore_wiz.png"; //$NON-NLS-1$
+
+ public static final String SELECT_KEYSTORE_HELP_ID = CertificateManagerActivator.PLUGIN_ID
+ + ".select_keystore"; //$NON-NLS-1$
+
+ public SelectExistentKeystoreWizard()
+ {
+ setWindowTitle(CertificateManagerNLS.ImportKeystoreWizard_ImportKeystore);
+ setDefaultPageImageDescriptor(CertificateManagerActivator.imageDescriptorFromPlugin(
+ CertificateManagerActivator.PLUGIN_ID, WIZARD_BANNER));
+
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.wizard.Wizard#createPageControls(org.eclipse.swt.widgets.Composite)
+ */
+ @Override
+ public void createPageControls(Composite pageContainer)
+ {
+ super.createPageControls(pageContainer);
+
+ //the shell has the same help as its page
+ PlatformUI.getWorkbench().getHelpSystem().setHelp(getShell(), SELECT_KEYSTORE_HELP_ID);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.wizard.Wizard#performFinish()
+ */
+ @Override
+ public boolean performFinish()
+ {
+ //check password and keystore type before importing
+ IKeyStore iKeyStore = selectExistentKeystorePage.getSelectedKeystore();
+ if (iKeyStore instanceof KeyStoreNode)
+ {
+ KeyStoreNode keyStoreNode = (KeyStoreNode) iKeyStore;
+ try
+ {
+ keyStoreNode.isPasswordValid(selectExistentKeystorePage.getPassword());
+ }
+ catch (KeyStoreManagerException e)
+ {
+ selectExistentKeystorePage
+ .setErrorMessage(CertificateManagerNLS.SelectExistentKeystoreWizard_Error_KeystoreType);
+ //let dialog opened
+ return false;
+ }
+ catch (InvalidPasswordException e)
+ {
+ selectExistentKeystorePage
+ .setErrorMessage(CertificateManagerNLS.SelectExistentKeystoreWizard_Error_InvalidPassword);
+ //let dialog opened
+ return false;
+ }
+ }
+
+ if (selectExistentKeystorePage.needToImportIntoView())
+ {
+ //if user asked to import item in the Signing and keys view
+ selectExistentKeystorePage.importKeystore();
+ }
+ return true;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.wizard.Wizard#addPages()
+ */
+ @Override
+ public void addPages()
+ {
+ selectExistentKeystorePage =
+ new SelectExistentKeystorePage(
+ CertificateManagerNLS.SelectExistentKeystoreWizard_BrowseExistentKeystore_PageTitle);
+ addPage(selectExistentKeystorePage);
+ }
+
+ /**
+ *
+ * @return
+ */
+ public IKeyStore getSelectedKeystore()
+ {
+ IKeyStore iKeyStore = selectExistentKeystorePage.getSelectedKeystore();
+ return iKeyStore;
+ }
+
+ /**
+ *
+ * @return true if need to import into view, false otherwise
+ */
+ public boolean canSavePassword()
+ {
+ return selectExistentKeystorePage.canSavePassword();
+ }
+
+ /**
+ *
+ * @return true if need to import into view, false otherwise
+ */
+ public String getPassword()
+ {
+ return selectExistentKeystorePage.getPassword();
+ }
+}
diff --git a/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/wizards/SignExternalPackagePage.java b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/wizards/SignExternalPackagePage.java
new file mode 100644
index 0000000..75898eb
--- /dev/null
+++ b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/wizards/SignExternalPackagePage.java
@@ -0,0 +1,559 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.studio.android.certmanager.ui.wizards;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+
+import org.eclipse.jface.dialogs.IMessageProvider;
+import org.eclipse.jface.viewers.IStructuredSelection;
+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.widgets.Button;
+import org.eclipse.swt.widgets.Combo;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.ui.PlatformUI;
+
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorolamobility.studio.android.certmanager.CertificateManagerActivator;
+import com.motorolamobility.studio.android.certmanager.core.KeyStoreManager;
+import com.motorolamobility.studio.android.certmanager.core.PasswordProvider;
+import com.motorolamobility.studio.android.certmanager.exception.InvalidPasswordException;
+import com.motorolamobility.studio.android.certmanager.exception.KeyStoreManagerException;
+import com.motorolamobility.studio.android.certmanager.i18n.CertificateManagerNLS;
+import com.motorolamobility.studio.android.certmanager.ui.model.EntryNode;
+import com.motorolamobility.studio.android.certmanager.ui.model.IKeyStore;
+import com.motorolamobility.studio.android.certmanager.ui.model.IKeyStoreEntry;
+import com.motorolamobility.studio.android.certmanager.ui.model.KeyStoreNode;
+
+/**
+ * This class implements the page of signature of external packages wizard It
+ * extends the page that removes the signature and implements the needed fields
+ */
+public class SignExternalPackagePage extends RemoveExternalPackageSignaturePage
+{
+
+ private Label keystoreLabel = null;
+
+ private Label keysLabel = null;
+
+ private Combo keystoreCombo = null;
+
+ private Text keystorePassword = null;
+
+ private Button loadKeystore = null;
+
+ private Button savePassword = null;
+
+ private Combo keysCombo = null;
+
+ private String keyEntryPassword;
+
+ private String keyStoreType;
+
+ PasswordProvider pP = null;
+
+ private IKeyStore initialSelectedKeyStore = null;
+
+ private IKeyStoreEntry initialSelectedEntry = null;
+
+ private IKeyStore selectedKeystore = null;
+
+ SelectionAdapter loadKeysSelectionAdapter = new SelectionAdapter()
+ {
+ @Override
+ public void widgetDefaultSelected(SelectionEvent e)
+ {
+ widgetSelected(e);
+ };
+
+ /* (non-Javadoc)
+ * @see org.eclipse.swt.events.SelectionAdapter#widgetSelected(org.eclipse.swt.events.SelectionEvent)
+ */
+ @Override
+ public void widgetSelected(SelectionEvent e)
+ {
+ setKeyEntries();
+ }
+ };
+
+ /**
+ * Create a new wizard page based on selection
+ *
+ * @param pageName
+ * the page name
+ * @param selection
+ * the selection
+ */
+ public SignExternalPackagePage(String pageName, IStructuredSelection selection,
+ IKeyStore selectedIKeyStore, IKeyStoreEntry selectedEntry)
+ {
+ super(pageName, selection);
+ this.initialSelectedKeyStore = selectedIKeyStore;
+ this.initialSelectedEntry = selectedEntry;
+ setDescription(CertificateManagerNLS.SIGN_EXTERNAL_PKG_WIZARD_DESCRIPTION);
+ setTitle(CertificateManagerNLS.SIGN_EXTERNAL_PKG_WIZARD_WINDOW_TITLE);
+ }
+
+ private HashMap<String, IKeyStore> getAvailableKeystores()
+ {
+ HashMap<String, IKeyStore> keystores = new HashMap<String, IKeyStore>();
+ Iterator<IKeyStore> iterator = null;
+ try
+ {
+ if ((KeyStoreManager.getInstance() != null)
+ && (KeyStoreManager.getInstance().getKeyStores() != null))
+ {
+ iterator = KeyStoreManager.getInstance().getKeyStores().iterator();
+ }
+ }
+ catch (KeyStoreManagerException e)
+ {
+ StudioLogger.error(this.getClass(), "Error retrieving keystore list", e); //$NON-NLS-1$
+ }
+
+ while ((iterator != null) && (iterator.hasNext()))
+ {
+ KeyStoreNode keystore = (KeyStoreNode) iterator.next();
+
+ keystores.put(keystore.toString(), keystore);
+ }
+ return keystores;
+
+ }
+
+ /**
+ * @param keystorePath
+ * @return key strings for the selected keystore
+ */
+ private final String[] getAvailableEntriesForKeystore(IKeyStore keystore)
+ {
+ ArrayList<String> entries = new ArrayList<String>();
+
+ if (keystore != null)
+ {
+
+ pP = new PasswordProvider(keystore.getFile());
+ String password = null;
+ try
+ {
+ // retrieve the saved password
+ password = pP.getKeyStorePassword(false);
+ }
+ catch (KeyStoreManagerException e1)
+ {
+ StudioLogger.error(this.getClass(), "Error retrieving keys from keystore", e1); //$NON-NLS-1$
+ }
+
+ if (password == null)
+ {
+ // password is not saved
+ if (!this.keystorePassword.getText().isEmpty())
+ {
+ // get the password from the wizard
+ password = this.keystorePassword.getText();
+ }
+ }
+ else
+ {
+ // the password was saved
+ try
+ {
+ keystore.isPasswordValid(password);
+ }
+ catch (InvalidPasswordException e)
+ {
+ if (!this.keystorePassword.getText().isEmpty())
+ {
+ // the saved password is invalid, get the password from the wizard
+ password = this.keystorePassword.getText();
+ }
+ }
+ catch (KeyStoreManagerException e)
+ {
+ //this exception should never happen here as it was handled at some point before
+ StudioLogger.error("This keystore was imported with wrong store type"); //$NON-NLS-1$
+ }
+ }
+ try
+ {
+ try
+ {
+ if (password != null)
+ {
+ // validate the password that was saved or from the wizard
+ keystore.isPasswordValid(password);
+ if (this.keystorePassword.getText().isEmpty())
+ {
+ // block the password fields if the password saved is valid
+ this.keystorePassword.setText(password);
+ this.keystorePassword.setEnabled(false);
+ this.savePassword.setSelection(true);
+ this.savePassword.setEnabled(false);
+ this.loadKeystore.setEnabled(false);
+ }
+ }
+ List<IKeyStoreEntry> keys = keystore.getEntries(password);
+
+ if ((keys.size() > 0) && (password != null))
+ {
+ this.keyStoreType = keystore.getType();
+ Iterator<IKeyStoreEntry> iterator2 = keys.iterator();
+ while ((iterator2 != null) && (iterator2.hasNext()))
+ {
+ EntryNode keyEntry = (EntryNode) iterator2.next();
+ entries.add(keyEntry.getId());
+ }
+
+ }
+
+ }
+ catch (KeyStoreManagerException e)
+ {
+ StudioLogger.error(this.getClass(), "Error retrieving keys from keystore", e); //$NON-NLS-1$
+ }
+
+ }
+ catch (InvalidPasswordException e)
+ {
+ setErrorMessage(CertificateManagerNLS.ConvertKeyStoreTypeDialog_Invalid_Keystore_Pass);
+ this.keystorePassword.setText(""); //$NON-NLS-1$
+ this.keystorePassword.setFocus(); //select the password text box so the user can retype the password
+ }
+ }
+
+ return entries.toArray(new String[0]);
+ }
+
+ /**
+ * Fill the key entries combo
+ *
+ */
+ private void setKeyEntries()
+ {
+ setErrorMessage(null);
+
+ String keystoreSelected = keystoreCombo.getItem(keystoreCombo.getSelectionIndex());
+
+ this.selectedKeystore = (IKeyStore) keystoreCombo.getData(keystoreSelected);
+ String[] availableKeys = getAvailableEntriesForKeystore(this.selectedKeystore);
+
+ if (availableKeys.length > 0)
+ {
+ keysCombo.setItems(availableKeys);
+ int selectedEntryIndex = 0;
+
+ if (initialSelectedEntry != null)
+ {
+ selectedEntryIndex = keysCombo.indexOf(initialSelectedEntry.getAlias());
+ initialSelectedEntry = null; //the selectedEntry only serves as first selection
+ }
+
+ keysCombo.select(selectedEntryIndex > 0 ? selectedEntryIndex : 0);
+
+ updatePageComplete();
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * com.motorola.studio.android.packaging.ui.wizards.RemoveExternalPackageSignaturePage
+ * #createExtendedArea(org.eclipse.swt.widgets.Composite)
+ */
+ @Override
+ protected void createExtendedArea(Composite parent)
+ {
+ GridData layoutData = new GridData(SWT.FILL, SWT.CENTER, false, false, 1, 1);
+
+ // Keystore label
+ this.keystoreLabel = new Label(parent, SWT.NONE);
+ this.keystoreLabel.setText(CertificateManagerNLS.SIGN_WIZARD_AREA_SIGN_KEYSTORE_LABEL);
+ this.keystoreLabel.setLayoutData(layoutData);
+
+ // Keystore combo
+ this.keystoreCombo = new Combo(parent, SWT.DROP_DOWN | SWT.READ_ONLY | SWT.SINGLE);
+ layoutData = new GridData(SWT.FILL, SWT.CENTER, true, false, 3, 1);
+ this.keystoreCombo.setLayoutData(layoutData);
+ keystoreCombo.addSelectionListener(new SelectionAdapter()
+ {
+ /* (non-Javadoc)
+ * @see org.eclipse.swt.events.SelectionAdapter#widgetSelected(org.eclipse.swt.events.SelectionEvent)
+ */
+ @Override
+ public void widgetSelected(SelectionEvent e)
+ {
+ keystorePassword.setText(""); //$NON-NLS-1$
+ keystorePassword.setEnabled(true);
+ savePassword.setSelection(false);
+ savePassword.setEnabled(false);
+ loadKeystore.setEnabled(false);
+ keysCombo.removeAll();
+ setKeyEntries();
+ if (keystorePassword.getEnabled())
+ {
+ keystorePassword.setFocus();
+ }
+ //it only serves as
+ initialSelectedKeyStore = null;
+ }
+
+ });
+
+ // Keystore password label
+ Label keystorePasswordLabel = new Label(parent, SWT.NONE);
+ keystorePasswordLabel
+ .setText(CertificateManagerNLS.CreateKeystorePage_KeystorePasswordLabel);
+ layoutData = new GridData(SWT.FILL, SWT.CENTER, false, false, 1, 1);
+ keystorePasswordLabel.setLayoutData(layoutData);
+
+ // Keystore password combo
+ this.keystorePassword = new Text(parent, SWT.BORDER | SWT.PASSWORD);
+ layoutData = new GridData(SWT.FILL, SWT.CENTER, true, false, 2, 1);
+ this.keystorePassword.setLayoutData(layoutData);
+ this.keystorePassword.addListener(SWT.Modify, new Listener()
+ {
+ @Override
+ public void handleEvent(Event event)
+ {
+ if (keystorePassword.getText().isEmpty())
+ {
+ loadKeystore.setEnabled(false);
+ savePassword.setEnabled(false);
+ }
+ else
+ {
+ loadKeystore.setEnabled(true);
+ savePassword.setEnabled(true);
+ }
+ keysCombo.removeAll();
+ updatePageComplete();
+ }
+ });
+ this.keystorePassword.addSelectionListener(loadKeysSelectionAdapter);
+
+ // Load key entries Button
+ this.loadKeystore = new Button(parent, SWT.PUSH);
+ layoutData = new GridData(SWT.FILL, SWT.CENTER, false, false, 1, 1);
+ this.loadKeystore.setLayoutData(layoutData);
+ this.loadKeystore.setText(CertificateManagerNLS.SIGN_EXTERNAL_PKG_WIZARD_LOAD);
+ this.loadKeystore.setEnabled(false);
+ this.loadKeystore.addSelectionListener(loadKeysSelectionAdapter);
+
+ // Save Keystore Password checkbox
+ this.savePassword = new Button(parent, SWT.CHECK);
+ layoutData = new GridData(SWT.FILL, SWT.CENTER, false, false, 4, 1);
+ this.savePassword.setLayoutData(layoutData);
+ this.savePassword.setText(CertificateManagerNLS.PasswordProvider_SaveThisPassword);
+ this.savePassword.setEnabled(false);
+ this.savePassword.setSelection(false);
+
+ // key entry label
+ this.keysLabel = new Label(parent, SWT.NONE);
+ this.keysLabel.setText(CertificateManagerNLS.SIGN_WIZARD_AREA_SIGN_KEYS_LABEL);
+ layoutData = new GridData(SWT.FILL, SWT.CENTER, false, false, 1, 1);
+ this.keysLabel.setLayoutData(layoutData);
+
+ // key entry combo
+ this.keysCombo = new Combo(parent, SWT.DROP_DOWN | SWT.READ_ONLY | SWT.SINGLE);
+ layoutData = new GridData(SWT.FILL, SWT.CENTER, true, false, 3, 1);
+ this.keysCombo.setLayoutData(layoutData);
+
+ PlatformUI
+ .getWorkbench()
+ .getHelpSystem()
+ .setHelp(parent,
+ CertificateManagerActivator.SIGN_EXTERNAL_PKG_WIZARD_CONTEXT_HELP_ID);
+
+ populateKeyStoreCombo();
+ }
+
+ @Override
+ protected void createPackageTreeLabel()
+ {
+ GridData layoutData;
+ Label packagesLabel = new Label(this.mainComposite, SWT.NONE);
+ packagesLabel.setText(CertificateManagerNLS.SignExternalPackagePage_Package_Tree_Label);
+ layoutData = new GridData(SWT.FILL, SWT.CENTER, false, false, 4, 1);
+ packagesLabel.setLayoutData(layoutData);
+ }
+
+ protected void populateKeyStoreCombo()
+ {
+ HashMap<String, IKeyStore> availableKeystores = getAvailableKeystores();
+ if (availableKeystores.size() > 0)
+ {
+
+ for (String keystoreKey : availableKeystores.keySet())
+ {
+ IKeyStore newKeystore = availableKeystores.get(keystoreKey);
+ this.keystoreCombo.setData(keystoreKey, availableKeystores.get(keystoreKey));
+ this.keystoreCombo.add(newKeystore.toString());
+
+ if (initialSelectedKeyStore != null)
+ {
+ if (initialSelectedKeyStore.equals(newKeystore))
+ {
+ //select combo with the item selected in Signing and keys view
+ keystoreCombo.select(keystoreCombo.indexOf(newKeystore.toString()));
+ setKeyEntries();
+ }
+ }
+ }
+
+ if (initialSelectedKeyStore == null)
+ {
+ this.keystoreCombo.select(0);
+ }
+ }
+ }
+
+ /**
+ *
+ * @return the key entry selected by user
+ */
+ public IKeyStoreEntry getSelectedKeyEntry()
+ {
+ IKeyStoreEntry result = null;
+ try
+ {
+ result =
+ this.selectedKeystore.getEntry(
+ this.keysCombo.getItem(this.keysCombo.getSelectionIndex()),
+ getKeystorePassword());
+ }
+ catch (KeyStoreManagerException e)
+ {
+ // should never happen
+ StudioLogger.error("Could not retrieve entry while signing package"); //$NON-NLS-1$
+ }
+ catch (InvalidPasswordException e)
+ {
+ // should never happen
+ StudioLogger.error("Invalid password while retrieving entry to sign package"); //$NON-NLS-1$
+ }
+
+ return result;
+ }
+
+ /**
+ *
+ * @return the keystore selected by user
+ */
+ public IKeyStore getSelectedKeyStore()
+ {
+ return this.selectedKeystore;
+ }
+
+ /**
+ *
+ * @return the keystore password entered by user
+ */
+ public String getKeystorePassword()
+ {
+ return this.keystorePassword.getText();
+ }
+
+ /**
+ *
+ * @return key entry password
+ */
+ public String getKeyEntryPassword()
+ {
+
+ try
+ {
+ this.keyEntryPassword =
+ pP.getPassword(this.keysCombo.getItem(this.keysCombo.getSelectionIndex()), true);
+ }
+ catch (KeyStoreManagerException e)
+ {
+ StudioLogger.error(this.getClass(), "Error retrieving keys entry password", e); //$NON-NLS-1$
+ }
+ return this.keyEntryPassword;
+ }
+
+ public PasswordProvider getPasswordProvider()
+ {
+ return pP;
+ }
+
+ /**
+ *
+ * @return the keystore type
+ */
+ public String getKeyStoreType()
+ {
+ return this.keyStoreType;
+ }
+
+ /**
+ * Update the page status, validating each field of this page The basic
+ * validation is made by superclass
+ */
+ @Override
+ public void updatePageComplete()
+ {
+ super.updatePageComplete();
+ int severity = getMessageType();
+ String messageAux = severity == IMessageProvider.NONE ? null : getMessage();
+
+ if (messageAux == null)
+ {
+ if (!(((this.keystoreCombo != null) && (this.keystoreCombo.getItemCount() > 0)
+ && (this.keystoreCombo.getItem(this.keystoreCombo.getSelectionIndex()) != null) && !this.keystoreCombo
+ .getItem(this.keystoreCombo.getSelectionIndex()).equalsIgnoreCase("")) && ((this.keysCombo != null) //$NON-NLS-1$
+ && (this.keysCombo.getItemCount() > 0)
+ && (this.keysCombo.getItem(this.keysCombo.getSelectionIndex()) != null) && !this.keysCombo
+ .getItem(this.keysCombo.getSelectionIndex()).equalsIgnoreCase("")))) //$NON-NLS-1$
+ {
+ messageAux =
+
+ CertificateManagerNLS.SIGN_EXTERNAL_PKG_WIZARD_NO_CERTIFICATE_ERROR;
+ severity = IMessageProvider.ERROR;
+ }
+
+ if (messageAux == null)
+ {
+ messageAux =
+
+ CertificateManagerNLS.SIGN_EXTERNAL_PKG_WIZARD_DESCRIPTION;
+ severity = IMessageProvider.NONE;
+ }
+
+ setMessage(messageAux, severity);
+ setPageComplete(severity == IMessageProvider.NONE);
+ }
+
+ }
+
+ /**
+ * @return checkbox save password
+ */
+ public boolean getSavePasswordSelection()
+ {
+ return this.savePassword.getSelection();
+ }
+}
diff --git a/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/wizards/SignExternalPackageWizard.java b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/wizards/SignExternalPackageWizard.java
new file mode 100644
index 0000000..ca9541f
--- /dev/null
+++ b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/ui/wizards/SignExternalPackageWizard.java
@@ -0,0 +1,346 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.studio.android.certmanager.ui.wizards;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.lang.reflect.InvocationTargetException;
+import java.security.UnrecoverableKeyException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.jar.JarFile;
+
+import org.eclipse.core.resources.IContainer;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.MultiStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.jface.dialogs.ErrorDialog;
+import org.eclipse.jface.dialogs.ProgressMonitorDialog;
+import org.eclipse.jface.operation.IRunnableWithProgress;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.wizard.Wizard;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.actions.WorkspaceModifyOperation;
+import org.eclipse.ui.plugin.AbstractUIPlugin;
+
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorolamobility.studio.android.certmanager.CertificateManagerActivator;
+import com.motorolamobility.studio.android.certmanager.i18n.CertificateManagerNLS;
+import com.motorolamobility.studio.android.certmanager.packaging.PackageFile;
+import com.motorolamobility.studio.android.certmanager.packaging.sign.PackageFileSigner;
+import com.motorolamobility.studio.android.certmanager.packaging.sign.SignException;
+import com.motorolamobility.studio.android.certmanager.ui.model.IKeyStore;
+import com.motorolamobility.studio.android.certmanager.ui.model.IKeyStoreEntry;
+
+/**
+ * This Wizard signs a package. based on a root dir, It shows a list o packages
+ * to sign and let user choose a certificate to use
+ */
+public class SignExternalPackageWizard extends Wizard
+{
+ private SignExternalPackagePage page = null;
+
+ public SignExternalPackageWizard(IStructuredSelection selection, IKeyStore selectedKeyStore)
+ {
+ this(selection, selectedKeyStore, null);
+ }
+
+ public SignExternalPackageWizard(IStructuredSelection selection, IKeyStore selectedKeyStore,
+ IKeyStoreEntry selectedEntry)
+ {
+ setWindowTitle(CertificateManagerNLS.SIGN_EXTERNAL_PKG_WIZARD_WINDOW_TITLE);
+ setNeedsProgressMonitor(true);
+ setHelpAvailable(false);
+ this.page =
+ new SignExternalPackagePage("signPage", selection, selectedKeyStore, selectedEntry);
+ setDefaultPageImageDescriptor(AbstractUIPlugin.imageDescriptorFromPlugin(
+ CertificateManagerActivator.PLUGIN_ID,
+ CertificateManagerActivator.SIGNATURE_WIZ_BAN));
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.jface.wizard.Wizard#addPages()
+ */
+ @Override
+ public void addPages()
+ {
+ addPage(this.page);
+ }
+
+ /**
+ * Finishes this wizard, signing the selected packages
+ */
+ @Override
+ public boolean performFinish()
+ {
+ final List<String> defectivePackages = new ArrayList<String>();
+ IRunnableWithProgress finishAction = new IRunnableWithProgress()
+ {
+
+ @Override
+ public void run(IProgressMonitor monitor) throws InvocationTargetException,
+ InterruptedException
+ {
+ List<String> selectedFiles =
+ SignExternalPackageWizard.this.page.getSelectedPackages();
+ monitor.beginTask(CertificateManagerNLS.SIGN_EXTERNAL_PKG_WIZARD_WINDOW_TITLE,
+ selectedFiles.size() * 2);
+ for (String selected : selectedFiles)
+ {
+ File file = new File(selected);
+ monitor.setTaskName(CertificateManagerNLS.SIGN_EXTERNAL_PKG_WIZARD_OPERATION
+ + " " + file.getName());
+ if ((file != null) && file.exists() && file.isFile() && file.canWrite())
+ {
+ OutputStream fileToWrite = null;
+ PackageFile pack = null;
+ JarFile jar = null;
+ try
+ {
+
+ // Update monitor
+ monitor.worked(1);
+ monitor.setTaskName(CertificateManagerNLS.SIGN_EXTERNAL_PKG_WIZARD_OPERATION
+ + " " + file.getName());
+
+ String keyStorePassword =
+ SignExternalPackageWizard.this.page.getKeystorePassword();
+ if (SignExternalPackageWizard.this.page.getSavePasswordSelection())
+ {
+ SignExternalPackageWizard.this.page.getPasswordProvider()
+ .saveKeyStorePassword(keyStorePassword);
+ }
+ String keyEntryPassword =
+ SignExternalPackageWizard.this.page.getKeyEntryPassword();
+ boolean keepTrying;
+ if (keyEntryPassword != null)
+ {
+ keepTrying = true;
+ }
+ else
+ {
+ keepTrying = false;
+ throw new Exception();
+ }
+ while (keepTrying)
+ {
+ try
+ {
+ // Open package and remove signature
+ jar = new JarFile(file);
+ pack = new PackageFile(jar);
+ pack.removeMetaEntryFiles();
+
+ // Sign the new package
+ PackageFileSigner.signPackage(pack,
+ SignExternalPackageWizard.this.page
+ .getSelectedKeyEntry(), keyEntryPassword,
+ PackageFileSigner.MOTODEV_STUDIO);
+ keepTrying = false;
+ }
+ catch (UnrecoverableKeyException sE)
+ {
+ keyEntryPassword =
+ SignExternalPackageWizard.this.page
+ .getPasswordProvider().getPassword(
+ SignExternalPackageWizard.this.page
+ .getSelectedKeyEntry()
+ .getAlias(), true, false);
+ if (keyEntryPassword == null)
+ {
+ keepTrying = false;
+ }
+ else
+ {
+ keepTrying = true;
+ }
+ }
+ }
+
+ // Write the new package file
+ fileToWrite = new FileOutputStream(file);
+ pack.write(fileToWrite);
+ PackageFile.zipAlign(file);
+ }
+ catch (IOException e)
+ {
+ defectivePackages.add(selected);
+ StudioLogger.error(
+ SignExternalPackageWizard.class.toString(),
+ "Impossible write to package: " + selected + " "
+ + e.getMessage());
+ }
+ catch (SignException e)
+ {
+ defectivePackages.add(selected);
+ StudioLogger.error(
+ SignExternalPackageWizard.class.toString(),
+ "Impossible sign the package: " + selected + " "
+ + e.getMessage());
+ }
+ catch (SecurityException e)
+ {
+ defectivePackages.add(selected);
+ StudioLogger.error(
+ SignExternalPackageWizard.class.toString(),
+ "Impossible sign the package: " + selected + " "
+ + e.getMessage());
+ }
+ catch (Exception e)
+ {
+ defectivePackages.add(selected);
+ StudioLogger.error(
+ SignExternalPackageWizard.class.toString(),
+ "Impossible sign the package: " + selected + " "
+ + e.getMessage());
+ }
+ finally
+ {
+ System.gc(); // Force garbage collector to avoid
+ // errors when deleting temp files
+
+ try
+ {
+ if (pack != null)
+ {
+ pack.removeTemporaryEntryFiles();
+ }
+
+ if (fileToWrite != null)
+ {
+ fileToWrite.close();
+ }
+
+ if (jar != null)
+ {
+ jar.close();
+ }
+ }
+ catch (IOException e)
+ {
+ // Silent exception. Only log the deletion
+ // exception.
+ StudioLogger.error(CertificateManagerActivator.PLUGIN_ID,
+ "Deleting temporary files");
+ }
+ }
+ }
+ else
+ {
+ defectivePackages.add(selected);
+ }
+ monitor.worked(1);
+ }
+ monitor.done();
+ }
+
+ };
+
+ try
+ {
+ PlatformUI.getWorkbench().getProgressService()
+ .runInUI(new ProgressMonitorDialog(getShell()), finishAction, null);
+ }
+ catch (InvocationTargetException e1)
+ {
+ StudioLogger.error(SignExternalPackageWizard.class.toString(),
+ "Error running finish actions");
+ }
+ catch (InterruptedException e1)
+ {
+ StudioLogger.error(SignExternalPackageWizard.class.toString(),
+ "Error running finish actions");
+ }
+
+ if (ResourcesPlugin.getWorkspace().getRoot().getLocation()
+ .isPrefixOf(this.page.getSourcePath()))
+ {
+ WorkspaceModifyOperation op = new WorkspaceModifyOperation()
+ {
+
+ @Override
+ protected void execute(IProgressMonitor monitor) throws CoreException,
+ InvocationTargetException, InterruptedException
+ {
+ for (IContainer container : ResourcesPlugin
+ .getWorkspace()
+ .getRoot()
+ .findContainersForLocation(
+ SignExternalPackageWizard.this.page.getSourcePath()))
+ {
+
+ container.refreshLocal(IResource.DEPTH_INFINITE, monitor);
+ }
+
+ }
+
+ };
+ try
+ {
+ PlatformUI.getWorkbench().getProgressService().run(false, false, op);
+ }
+ catch (InvocationTargetException e)
+ {
+ StudioLogger.error(SignExternalPackageWizard.class.toString(),
+ "Error refreshing workspace");
+ }
+ catch (InterruptedException e)
+ {
+ StudioLogger.error(SignExternalPackageWizard.class.toString(),
+ "Error refreshing workspace");
+ }
+ }
+ if (!defectivePackages.isEmpty())
+ {
+ MultiStatus errors =
+ new MultiStatus(CertificateManagerActivator.PLUGIN_ID, IStatus.ERROR,
+ CertificateManagerNLS.UNSIGN_EXTERNAL_PKG_WIZARD_ERROR_REASON, null);
+ for (String defect : defectivePackages)
+ {
+ errors.add(new Status(IStatus.ERROR, CertificateManagerActivator.PLUGIN_ID, defect));
+ }
+
+ ErrorDialog errorBox =
+ new ErrorDialog(getShell(),
+ CertificateManagerNLS.SIGN_EXTERNAL_PKG_WIZARD_WINDOW_TITLE,
+ CertificateManagerNLS.SIGN_EXTERNAL_PKG_WIZARD_ERROR, errors,
+ IStatus.ERROR);
+ errorBox.open();
+ }
+ return true;
+
+ }
+
+ @Override
+ public void createPageControls(Composite pageContainer)
+ {
+ super.createPageControls(pageContainer);
+ PlatformUI
+ .getWorkbench()
+ .getHelpSystem()
+ .setHelp(getShell(),
+ CertificateManagerActivator.SIGN_EXTERNAL_PKG_WIZARD_CONTEXT_HELP_ID);
+ }
+}
diff --git a/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/views/KeystoreManagerView.java b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/views/KeystoreManagerView.java
new file mode 100644
index 0000000..2cd4988
--- /dev/null
+++ b/src/plugins/certmanager/src/com/motorolamobility/studio/android/certmanager/views/KeystoreManagerView.java
@@ -0,0 +1,378 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.studio.android.certmanager.views;
+
+import java.util.Collections;
+import java.util.List;
+
+import org.eclipse.jface.action.IMenuListener;
+import org.eclipse.jface.action.IMenuManager;
+import org.eclipse.jface.action.MenuManager;
+import org.eclipse.jface.action.Separator;
+import org.eclipse.jface.viewers.ColumnViewerToolTipSupport;
+import org.eclipse.jface.viewers.TreeViewer;
+import org.eclipse.jface.viewers.TreeViewerColumn;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Menu;
+import org.eclipse.swt.widgets.Tree;
+import org.eclipse.swt.widgets.TreeColumn;
+import org.eclipse.ui.IWorkbenchActionConstants;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.part.ViewPart;
+
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorolamobility.studio.android.certmanager.event.IKeyStoreModelListener;
+import com.motorolamobility.studio.android.certmanager.event.KeyStoreModelEvent;
+import com.motorolamobility.studio.android.certmanager.event.KeyStoreModelEventManager;
+import com.motorolamobility.studio.android.certmanager.exception.KeyStoreManagerException;
+import com.motorolamobility.studio.android.certmanager.i18n.CertificateManagerNLS;
+import com.motorolamobility.studio.android.certmanager.ui.model.ITreeNode;
+import com.motorolamobility.studio.android.certmanager.ui.model.KeyStoreRootNode;
+import com.motorolamobility.studio.android.certmanager.ui.model.SigningAndKeysModelManager;
+import com.motorolamobility.studio.android.certmanager.ui.tree.ExpiresInColumnLabelProvider;
+import com.motorolamobility.studio.android.certmanager.ui.tree.KeystoreManagerTreeContentProvider;
+import com.motorolamobility.studio.android.certmanager.ui.tree.LastBackupDateColumnLabelProvider;
+import com.motorolamobility.studio.android.certmanager.ui.tree.NameAliasColumnLabelProvider;
+import com.motorolamobility.studio.android.certmanager.ui.tree.PathColumnLabelProvider;
+import com.motorolamobility.studio.android.certmanager.ui.tree.TypeColumnLabelProvider;
+
+/**
+ * View to manage certificates under MOTODEV Studio for Android
+ */
+public class KeystoreManagerView extends ViewPart implements IKeyStoreModelListener
+{
+ /**
+ * The ID of the view as specified by the extension.
+ */
+ public static final String ID = "com.motorola.studio.android.packaging.ui.signingview"; //$NON-NLS-1$
+
+ private TreeViewer viewer;
+
+ /**
+ * This is a callback that will allow us
+ * to create the viewer and initialize it.
+ */
+ @Override
+ public void createPartControl(Composite parent)
+ {
+ viewer =
+ new TreeViewer(parent, SWT.H_SCROLL | SWT.V_SCROLL | SWT.BORDER
+ | SWT.FULL_SELECTION | SWT.VIRTUAL | SWT.MULTI); //Virtual is required due to ILazyTreeContentProvider
+ viewer.setUseHashlookup(true);
+ viewer.setAutoExpandLevel(0);
+
+ viewer.setContentProvider(new KeystoreManagerTreeContentProvider(viewer));
+ viewer.setInput(getInitalInput());
+ viewer.expandToLevel(getInitalInput(), 1);
+ ColumnViewerToolTipSupport.enableFor(viewer);
+
+ getSite().setSelectionProvider(viewer);
+
+ Tree tree = viewer.getTree();
+ tree.setLinesVisible(true);
+ tree.setHeaderVisible(true);
+ tree.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1));
+ {
+ TreeViewerColumn treeViewerColumn = new TreeViewerColumn(viewer, SWT.NONE);
+ treeViewerColumn.setLabelProvider(new NameAliasColumnLabelProvider());
+ TreeColumn trclmnNewColumn = treeViewerColumn.getColumn();
+ trclmnNewColumn.setWidth(250);
+ trclmnNewColumn
+ .setText(CertificateManagerNLS.CertificateManagerView_NameAlias_ColumnName);
+
+ tree.setSortColumn(treeViewerColumn.getColumn());
+ tree.setSortDirection(SWT.DOWN);
+ }
+ {
+ TreeViewerColumn treeViewerColumn = new TreeViewerColumn(viewer, SWT.NONE);
+ treeViewerColumn.setLabelProvider(new TypeColumnLabelProvider());
+ TreeColumn trclmnNewColumn = treeViewerColumn.getColumn();
+ trclmnNewColumn.setWidth(75);
+ trclmnNewColumn.setText(CertificateManagerNLS.CertificateManagerView_Type_ColumnName);
+ }
+ {
+ TreeViewerColumn treeViewerColumn = new TreeViewerColumn(viewer, SWT.NONE);
+ treeViewerColumn.setLabelProvider(new ExpiresInColumnLabelProvider());
+ TreeColumn trclmnExpiresIn = treeViewerColumn.getColumn();
+ trclmnExpiresIn.setWidth(100);
+ trclmnExpiresIn
+ .setText(CertificateManagerNLS.CertificateManagerView_ExpiresIn_ColumnName);
+ }
+ {
+ TreeViewerColumn treeViewerColumn = new TreeViewerColumn(viewer, SWT.NONE);
+ treeViewerColumn.setLabelProvider(new LastBackupDateColumnLabelProvider());
+ TreeColumn trclmnLastBackupDate = treeViewerColumn.getColumn();
+ trclmnLastBackupDate.setWidth(125);
+ trclmnLastBackupDate
+ .setText(CertificateManagerNLS.CertificateManagerView_LastBackupDate_ColumnName);
+ }
+ {
+ TreeViewerColumn treeViewerColumn = new TreeViewerColumn(viewer, SWT.LEFT);
+ treeViewerColumn.setLabelProvider(new PathColumnLabelProvider());
+ TreeColumn trclmnPath = treeViewerColumn.getColumn();
+ trclmnPath.setWidth(500);
+ trclmnPath.setText(CertificateManagerNLS.CertificateManagerView_Path_ColumnName);
+ }
+
+ // Create the help context id for the viewer's control
+ PlatformUI
+ .getWorkbench()
+ .getHelpSystem()
+ .setHelp(viewer.getControl(),
+ "com.motorolamobility.studio.android.certmanager.viewer"); //$NON-NLS-1$
+
+ hookContextMenu();
+
+ getSite().setSelectionProvider(viewer);
+
+ //register listener for model changes
+ KeyStoreModelEventManager.getInstance().addListener(this);
+ }
+
+ private void hookContextMenu()
+ {
+ MenuManager menuMgr = new MenuManager("#PopupMenu"); //$NON-NLS-1$
+ menuMgr.setRemoveAllWhenShown(true);
+ menuMgr.addMenuListener(new IMenuListener()
+ {
+ @Override
+ public void menuAboutToShow(IMenuManager manager)
+ {
+ fillContextMenu(manager);
+ }
+ });
+ Menu menu = menuMgr.createContextMenu(viewer.getControl());
+ viewer.getControl().setMenu(menu);
+ getSite().registerContextMenu(menuMgr, viewer);
+ }
+
+ private void fillContextMenu(IMenuManager manager)
+ {
+ // Other plug-ins can contribute there actions here
+ // manager.add(openClose);
+ manager.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS));
+ }
+
+ /**
+ * Loads the keystores from preference and <user_home>\motodevstudio\tools\motodev.keystore
+ * @return root node (invisible) that contains as children the keystores.
+ */
+ private Object getInitalInput()
+ {
+ return SigningAndKeysModelManager.getInstance().getKeyStoresRootNode();
+ }
+
+ /**
+ * Passing the focus request to the viewer's control.
+ */
+ @Override
+ public void setFocus()
+ {
+ viewer.getControl().setFocus();
+ }
+
+ public TreeViewer getTreeViewer()
+ {
+ return viewer;
+ }
+
+ /**
+ * Closing the view
+ */
+ @Override
+ public void dispose()
+ {
+ super.dispose();
+ //remove listener for model changes
+ KeyStoreModelEventManager.getInstance().removeListener(this);
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorolamobility.studio.android.certmanager.event.IKeyStoreModelListener#handleNodeAdditionEvent(com.motorolamobility.studio.android.certmanager.event.KeyStoreModelEvent)
+ */
+ @Override
+ public void handleNodeAdditionEvent(final KeyStoreModelEvent keyStoreModeEvent)
+ {
+ Display display = Display.getDefault();
+ if (!display.isDisposed())
+ {
+ display.syncExec(new Runnable()
+ {
+ @Override
+ public void run()
+ {
+ ITreeNode treeNodeItem = keyStoreModeEvent.getTreeNodeItem();
+ ITreeNode parentNode = treeNodeItem.getParent();
+
+ /* (parentNode instanceof KeyStoreRootNode) is a workaround to add nodes to the root node
+ * since getTreeViewer().getExpandedState(parentNode) always return false when the parentNode
+ * is the root node (KeyStoreRootNode in this case)
+ */
+ if (getTreeViewer().getExpandedState(parentNode)
+ || (parentNode instanceof KeyStoreRootNode))
+ {
+ getTreeViewer().add(parentNode, treeNodeItem);
+ }
+ else
+ {
+ List<ITreeNode> children;
+ try
+ {
+ children = parentNode.getChildren();
+ }
+ catch (KeyStoreManagerException e)
+ {
+ children = Collections.emptyList();
+ }
+
+ if (children.size() > 0)
+ {
+ getTreeViewer().setChildCount(parentNode, children.size());
+ }
+ }
+ }
+ });
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorolamobility.studio.android.certmanager.event.IKeyStoreModelListener#handleNodeRemovalEvent(com.motorolamobility.studio.android.certmanager.event.KeyStoreModelEvent)
+ */
+ @Override
+ public void handleNodeRemovalEvent(final KeyStoreModelEvent keyStoreModeEvent)
+ {
+ Display display = Display.getDefault();
+ if (!display.isDisposed())
+ {
+ display.syncExec(new Runnable()
+ {
+ @Override
+ public void run()
+ {
+ if (!getTreeViewer().getTree().isDisposed())
+ {
+ getTreeViewer().remove(keyStoreModeEvent.getTreeNodeItem());
+ }
+ }
+ });
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorolamobility.studio.android.certmanager.event.IKeyStoreModelListener#handleNodeUpdateEvent(com.motorolamobility.studio.android.certmanager.event.KeyStoreModelEvent)
+ */
+ @Override
+ public void handleNodeUpdateEvent(final KeyStoreModelEvent keyStoreModeEvent)
+ {
+ Display display = Display.getDefault();
+ if (!display.isDisposed())
+ {
+ display.asyncExec(new Runnable()
+ {
+ @Override
+ public void run()
+ {
+ ITreeNode treeNode = keyStoreModeEvent.getTreeNodeItem();
+ getTreeViewer().update(treeNode, null);
+ }
+ });
+ }
+ }
+
+ @Override
+ public void handleNodeCollapseEvent(final KeyStoreModelEvent keyStoreModelEvent)
+ {
+ Display display = Display.getDefault();
+ if (!display.isDisposed())
+ {
+ display.asyncExec(new Runnable()
+ {
+ @Override
+ public void run()
+ {
+ ITreeNode treeNode = keyStoreModelEvent.getTreeNodeItem();
+ //Ugly workaround to avoid JFace treeViewer to enter in a infinite loop asking for children all the time.
+ getTreeViewer().remove(treeNode);
+ getTreeViewer().add(treeNode.getParent(), treeNode);
+ }
+ });
+ }
+ }
+
+ @Override
+ public void handleNodeRefreshEvent(final KeyStoreModelEvent keyStoreModelEvent)
+ {
+ Display display = Display.getDefault();
+ if (!display.isDisposed())
+ {
+ display.asyncExec(new Runnable()
+ {
+ @Override
+ public void run()
+ {
+ ITreeNode treeNode = keyStoreModelEvent.getTreeNodeItem();
+ if (treeNode != null)
+ {
+ try
+ {
+ getTreeViewer().remove(treeNode, treeNode.getChildren().toArray());
+ }
+ catch (KeyStoreManagerException e)
+ {
+ StudioLogger.error("Error while accessing keystore manager. "
+ + e.getMessage());
+ }
+
+ getTreeViewer().refresh(treeNode, true);
+ }
+ }
+ });
+ }
+ }
+
+ @Override
+ public void handleNodeClearEvent(final KeyStoreModelEvent keyStoreModelEvent)
+ {
+ Display display = Display.getDefault();
+ if (!display.isDisposed())
+ {
+ display.syncExec(new Runnable()
+ {
+ @Override
+ public void run()
+ {
+ ITreeNode treeNode = keyStoreModelEvent.getTreeNodeItem();
+ if (treeNode != null)
+ {
+ try
+ {
+ getTreeViewer().remove(treeNode, treeNode.getChildren().toArray());
+ getTreeViewer().setChildCount(treeNode, 0);
+ }
+ catch (KeyStoreManagerException e)
+ {
+ StudioLogger.error("Error while accessing keystore manager. "
+ + e.getMessage());
+ }
+ }
+ }
+ });
+ }
+ }
+} \ No newline at end of file
diff --git a/src/plugins/common/.classpath b/src/plugins/common/.classpath
new file mode 100644
index 0000000..24f672e
--- /dev/null
+++ b/src/plugins/common/.classpath
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry exported="true" kind="lib" path="commons-net-1.4.1.jar"/>
+ <classpathentry exported="true" kind="lib" path="jakarta-oro-2.0.8.jar"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>
+ <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/src/plugins/common/.project b/src/plugins/common/.project
new file mode 100644
index 0000000..92bc607
--- /dev/null
+++ b/src/plugins/common/.project
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>com.motorola.studio.android.common</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.ManifestBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.SchemaBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.pde.PluginNature</nature>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+</projectDescription>
diff --git a/src/plugins/common/META-INF/MANIFEST.MF b/src/plugins/common/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..6d6c87a
--- /dev/null
+++ b/src/plugins/common/META-INF/MANIFEST.MF
@@ -0,0 +1,44 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: %pluginName
+Bundle-SymbolicName: com.motorola.studio.android.common;singleton:=true
+Bundle-Version: 5.0.0.qualifier
+Bundle-Activator: com.motorola.studio.android.common.CommonPlugin
+Bundle-Vendor: %providerName
+Bundle-Localization: plugin
+Require-Bundle: org.eclipse.ui,
+ org.eclipse.core.runtime,
+ org.eclipse.core.resources,
+ org.eclipse.ui.editors,
+ org.eclipse.ui.browser,
+ org.eclipse.text,
+ com.motorolamobility.studio.android.logger,
+ org.eclipse.core.net,
+ org.eclipse.ui.net,
+ org.apache.xerces,
+ org.eclipse.jdt.core,
+ org.eclipse.sequoyah.localization.tools,
+ org.eclipse.sequoyah.device.common.utilities,
+ org.eclipse.ui.console,
+ org.eclipse.ui.ide,
+ com.android.ide.eclipse.adt;resolution:=optional,
+ org.apache.commons.httpclient,
+ com.android.ide.eclipse.base;resolution:=optional
+Bundle-RequiredExecutionEnvironment: JavaSE-1.6
+Export-Package: com.motorola.studio.android.common,
+ com.motorola.studio.android.common.exception,
+ com.motorola.studio.android.common.log,
+ com.motorola.studio.android.common.preferences,
+ com.motorola.studio.android.common.proxy,
+ com.motorola.studio.android.common.utilities,
+ com.motorola.studio.android.common.utilities.i18n,
+ com.motorola.studio.android.common.utilities.ui,
+ com.motorola.studio.android.manifest,
+ com.motorola.studio.android.model.manifest,
+ com.motorola.studio.android.model.manifest.dom,
+ com.motorola.studio.android.wizards,
+ com.motorola.studio.android.wizards.elements
+Bundle-ClassPath: commons-net-1.4.1.jar,
+ jakarta-oro-2.0.8.jar,
+ .
+Bundle-ActivationPolicy: lazy
diff --git a/src/plugins/common/about.ini b/src/plugins/common/about.ini
new file mode 100644
index 0000000..68ecccf
--- /dev/null
+++ b/src/plugins/common/about.ini
@@ -0,0 +1,24 @@
+# about.ini
+# contains information about a feature
+# java.io.Properties file (ISO 8859-1 with "\" escapes)
+# "%key" are externalized strings defined in about.properties
+# This file does not need to be translated.
+
+# Property "aboutText" contains blurb for "About" dialog (translated)
+aboutText=%blurb
+
+# Property "windowImage" contains path to window icon (16x16)
+# needed for primary features only
+
+# Property "featureImage" contains path to feature image (32x32)
+featureImage=res/plate32.png
+
+# Property "aboutImage" contains path to product image (500x330 or 115x164)
+# needed for primary features only
+
+# Property "appName" contains name of the application (translated)
+# needed for primary features only
+
+# Property "welcomePerspective" contains the id of the perspective in which the
+# welcome page is to be opened.
+# optional \ No newline at end of file
diff --git a/src/plugins/common/about.mappings b/src/plugins/common/about.mappings
new file mode 100644
index 0000000..0adc8f9
--- /dev/null
+++ b/src/plugins/common/about.mappings
@@ -0,0 +1 @@
+0=@studioversion@
diff --git a/src/plugins/common/about.properties b/src/plugins/common/about.properties
new file mode 100644
index 0000000..8b98f4a
--- /dev/null
+++ b/src/plugins/common/about.properties
@@ -0,0 +1,7 @@
+blurb=MOTODEV Studio\n\
+\n\
+Version: {featureVersion}\n\
+\n\
+Build id: {1}\n\
+\n\
+Copyright (C) 2012 The Android Open Source Project \ No newline at end of file
diff --git a/src/plugins/common/build.properties b/src/plugins/common/build.properties
new file mode 100644
index 0000000..9026737
--- /dev/null
+++ b/src/plugins/common/build.properties
@@ -0,0 +1,13 @@
+source.. = src/
+output.. = bin/
+bin.includes = META-INF/,\
+ .,\
+ commons-net-1.4.1.jar,\
+ jakarta-oro-2.0.8.jar,\
+ plugin.xml,\
+ about.ini,\
+ about.mappings,\
+ about.properties,\
+ plugin.properties,\
+ res/,\
+ files/
diff --git a/src/plugins/common/commons-net-1.4.1.jar b/src/plugins/common/commons-net-1.4.1.jar
new file mode 100644
index 0000000..9666a92
--- /dev/null
+++ b/src/plugins/common/commons-net-1.4.1.jar
Binary files differ
diff --git a/src/plugins/common/files/permissions.txt b/src/plugins/common/files/permissions.txt
new file mode 100644
index 0000000..55d9c26
--- /dev/null
+++ b/src/plugins/common/files/permissions.txt
@@ -0,0 +1,119 @@
+ACCESS_CHECKIN_PROPERTIES
+ACCESS_COARSE_LOCATION
+ACCESS_FINE_LOCATION
+ACCESS_LOCATION_EXTRA_COMMANDS
+ACCESS_MOCK_LOCATION
+ACCESS_NETWORK_STATE
+ACCESS_SURFACE_FLINGER
+ACCESS_WIFI_STATE
+ACCOUNT_MANAGER
+ADD_VOICEMAIL
+AUTHENTICATE_ACCOUNTS
+BATTERY_STATS
+BIND_APPWIDGET
+BIND_DEVICE_ADMIN
+BIND_INPUT_METHOD
+BIND_REMOTEVIEWS
+BIND_WALLPAPER
+BLUETOOTH
+BLUETOOTH_ADMIN
+BRICK
+BROADCAST_PACKAGE_REMOVED
+BROADCAST_SMS
+BROADCAST_STICKY
+BROADCAST_WAP_PUSH
+CALL_PHONE
+CALL_PRIVILEGED
+CAMERA
+CHANGE_COMPONENT_ENABLED_STATE
+CHANGE_CONFIGURATION
+CHANGE_NETWORK_STATE
+CHANGE_WIFI_MULTICAST_STATE
+CHANGE_WIFI_STATE
+CLEAR_APP_CACHE
+CLEAR_APP_USER_DATA
+CONTROL_LOCATION_UPDATES
+DELETE_CACHE_FILES
+DELETE_PACKAGES
+DEVICE_POWER
+DIAGNOSTIC
+DISABLE_KEYGUARD
+DUMP
+EXPAND_STATUS_BAR
+FACTORY_TEST
+FLASHLIGHT
+FORCE_BACK
+GET_ACCOUNTS
+GET_PACKAGE_SIZE
+GET_TASKS
+GLOBAL_SEARCH
+HARDWARE_TEST
+INJECT_EVENTS
+INSTALL_LOCATION_PROVIDER
+INSTALL_PACKAGES
+INTERNAL_SYSTEM_WINDOW
+INTERNET
+KILL_BACKGROUND_PROCESSES
+MANAGE_ACCOUNTS
+MANAGE_APP_TOKENS
+MASTER_CLEAR
+MODIFY_AUDIO_SETTINGS
+MODIFY_PHONE_STATE
+MOUNT_FORMAT_FILESYSTEMS
+MOUNT_UNMOUNT_FILESYSTEMS
+NFC
+PERSISTENT_ACTIVITY
+PROCESS_OUTGOING_CALLS
+READ_CALENDAR
+READ_CONTACTS
+READ_FRAME_BUFFER
+READ_HISTORY_BOOKMARKS
+READ_INPUT_STATE
+READ_LOGS
+READ_OWNER_DATA
+READ_PHONE_STATE
+READ_SMS
+READ_SYNC_SETTINGS
+READ_SYNC_STATS
+REBOOT
+RECEIVE_BOOT_COMPLETED
+RECEIVE_MMS
+RECEIVE_SMS
+RECEIVE_WAP_PUSH
+RECORD_AUDIO
+REORDER_TASKS
+RESTART_PACKAGES
+SEND_SMS
+SET_ACTIVITY_WATCHER
+SET_ALARM
+SET_ALWAYS_FINISH
+SET_ANIMATION_SCALE
+SET_DEBUG_APP
+SET_ORIENTATION
+SET_PREFERRED_APPLICATIONS
+SET_PROCESS_LIMIT
+SET_TIME
+SET_TIME_ZONE
+SET_WALLPAPER
+SET_WALLPAPER_HINTS
+SIGNAL_PERSISTENT_PROCESSES
+STATUS_BAR
+SUBSCRIBED_FEEDS_READ
+SUBSCRIBED_FEEDS_WRITE
+SYSTEM_ALERT_WINDOW
+UPDATE_DEVICE_STATS
+USE_CREDENTIALS
+USE_SIP
+VIBRATE
+WAKE_LOCK
+WRITE_APN_SETTINGS
+WRITE_CALENDAR
+WRITE_CONTACTS
+WRITE_EXTERNAL_STORAGE
+WRITE_GSERVICES
+WRITE_HISTORY_BOOKMARKS
+WRITE_OWNER_DATA
+WRITE_SECURE_SETTINGS
+WRITE_SETTINGS
+WRITE_SMS
+WRITE_SYNC_SETTINGS
diff --git a/src/plugins/common/jakarta-oro-2.0.8.jar b/src/plugins/common/jakarta-oro-2.0.8.jar
new file mode 100644
index 0000000..23488d2
--- /dev/null
+++ b/src/plugins/common/jakarta-oro-2.0.8.jar
Binary files differ
diff --git a/src/plugins/common/plugin.properties b/src/plugins/common/plugin.properties
new file mode 100644
index 0000000..36c15b1
--- /dev/null
+++ b/src/plugins/common/plugin.properties
@@ -0,0 +1,10 @@
+#########################################################
+#
+# Properties file for com.motorola.studio.android.common
+#
+#########################################################
+
+
+pluginName=MOTODEV Studio for Android Common
+providerName=Motorola Mobility, Inc.
+studioAndroidCategoryLabel=MOTODEV Studio
diff --git a/src/plugins/common/plugin.xml b/src/plugins/common/plugin.xml
new file mode 100644
index 0000000..23c47ca
--- /dev/null
+++ b/src/plugins/common/plugin.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?eclipse version="3.4"?>
+<plugin>
+ <extension
+ point="org.eclipse.ui.startup">
+ <startup
+ class="com.motorola.studio.android.common.proxy.NetworkProxySettingStartup">
+ </startup>
+ </extension>
+ <extension
+ point="org.eclipse.ui.views">
+ <category
+ id="studioAndroidViewCategory"
+ name="%studioAndroidCategoryLabel">
+ </category>
+ </extension>
+</plugin>
diff --git a/src/plugins/common/res/androidjdbc.jar b/src/plugins/common/res/androidjdbc.jar
new file mode 100644
index 0000000..6b5e0d8
--- /dev/null
+++ b/src/plugins/common/res/androidjdbc.jar
Binary files differ
diff --git a/src/plugins/common/res/plate32.png b/src/plugins/common/res/plate32.png
new file mode 100644
index 0000000..4bff905
--- /dev/null
+++ b/src/plugins/common/res/plate32.png
Binary files differ
diff --git a/src/plugins/common/src/com/motorola/studio/android/common/CommonPlugin.java b/src/plugins/common/src/com/motorola/studio/android/common/CommonPlugin.java
new file mode 100644
index 0000000..d5faea6
--- /dev/null
+++ b/src/plugins/common/src/com/motorola/studio/android/common/CommonPlugin.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.motorola.studio.android.common;
+
+import static com.motorola.studio.android.common.log.StudioLogger.debug;
+import static com.motorola.studio.android.common.log.StudioLogger.error;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URL;
+
+import org.eclipse.core.runtime.FileLocator;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.ui.plugin.AbstractUIPlugin;
+import org.osgi.framework.BundleContext;
+
+import com.motorola.studio.android.common.log.StudioLogger;
+
+/**
+ * The activator class controls the plug-in life cycle
+ */
+public class CommonPlugin extends AbstractUIPlugin
+{
+
+ // The plug-in ID
+ public static final String PLUGIN_ID = "com.motorola.studio.android.common"; //$NON-NLS-1$
+
+ private static final String JDBC_DRIVER_PATH = "res/androidjdbc.jar";
+
+ public static final String JDBC_DRIVER_INSTANCE_NAME = "motodev_jdbc_driver";
+
+ // The shared instance
+ private static CommonPlugin plugin;
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.ui.plugin.AbstractUIPlugin#start(org.osgi.framework.BundleContext)
+ */
+ @Override
+ public void start(BundleContext context) throws Exception
+ {
+ StudioLogger.debug(CommonPlugin.class, "Starting MOTODEV Android Common Plugin...");
+
+ super.start(context);
+ plugin = this;
+
+ StudioLogger.debug(CommonPlugin.class, "MOTODEV Android Common Plugin started.");
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.ui.plugin.AbstractUIPlugin#stop(org.osgi.framework.BundleContext)
+ */
+ @Override
+ public void stop(BundleContext context) throws Exception
+ {
+ plugin = null;
+ super.stop(context);
+ }
+
+ /**
+ * Returns the shared instance
+ *
+ * @return the shared instance
+ */
+ public static CommonPlugin getDefault()
+ {
+ return plugin;
+ }
+
+ /**
+ * Retrieves the location of the driver
+ * @return
+ */
+ public String getDriverPath()
+ {
+ String driverPath = "";
+ if (getDbResourceFile(JDBC_DRIVER_PATH) != null)
+ {
+ driverPath = getDbResourceFile(JDBC_DRIVER_PATH).getAbsolutePath();
+ }
+ return driverPath;
+ }
+
+ private File getDbResourceFile(String pathAtPlugin)
+ {
+ URL location = getBundle().getEntry(pathAtPlugin);
+
+ debug("JDBC Driver Location:" + location + " JDBC Driver getBundle().getLocation():"
+ + getBundle().getLocation());
+
+ File file = null;
+ try
+ {
+ IPath p = new Path(FileLocator.toFileURL(location).getFile());
+ debug("JDBC Driver Path:" + p.toOSString());
+ file = p.toFile();
+ }
+ catch (IOException e)
+ {
+ error("Error while trying to locate jdbc driver into db plugin:" + e.getMessage());
+ }
+ return file;
+
+ }
+
+}
diff --git a/src/plugins/common/src/com/motorola/studio/android/common/IAndroidConstants.java b/src/plugins/common/src/com/motorola/studio/android/common/IAndroidConstants.java
new file mode 100644
index 0000000..ebadbcb
--- /dev/null
+++ b/src/plugins/common/src/com/motorola/studio/android/common/IAndroidConstants.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.common;
+
+import org.eclipse.core.runtime.IPath;
+
+/**
+ * Interface that holds common constants to be used in the Android Plug-in
+ */
+public interface IAndroidConstants
+{
+ String RES_DIR = "res" + IPath.SEPARATOR;
+
+ String VALUES_DIR = "values" + IPath.SEPARATOR;
+
+ String STRINGS_FILE = "strings.xml"; //$NON-NLS-1$
+
+ String MENU_FILE_EXTENSION = "xml";
+
+ String FN_ANDROID_MANIFEST = "AndroidManifest.xml";
+
+ String GEN_SRC_FOLDER = "gen";
+
+ String OPHONE_JAR = "oms.jar";
+
+ String JIL_JAR = "internal.jar";
+
+ String WS_ROOT = "/";
+
+ String FD_SOURCES = "src";
+
+ String RE_DOT = "\\.";
+
+ String DOT_JAVA = ".java";
+
+ String ANDROID_NATURE = "com.android.ide.eclipse.adt.AndroidNature";
+
+ String CLASS_ACTIVITY = "android.app.Activity";
+
+ String CLASS_CONTENTPROVIDER = "android.content.ContentProvider";
+
+ String CLASS_SERVICE = "android.app.Service";
+
+ String CLASS_BROADCASTRECEIVER = "android.content.BroadcastReceiver";
+
+ String FD_RES = "res";
+
+ String FD_XML = "xml";
+
+ String FD_TOOLS = "tools";
+
+ String FD_PLATFORM_TOOLS = "platform-tools";
+
+ String FD_VALUES = "values";
+
+ String FD_ANIM = "anim";
+
+ String FD_DRAWABLE = "drawable";
+
+ String FD_LAYOUT = "layout";
+
+ String FD_MENU = "menu";
+
+ String FD_GEN_SOURCES = "gen";
+
+ String FD_ASSETS = "assets";
+
+ String FD_RESOURCES = "res";
+
+ String FD_OUTPUT = "bin";
+
+ static final int API_LEVEL_FOR_PLATFORM_VERSION_3_0_0 = 11;
+
+ String ACTION_MAIN = "android.intent.action.MAIN";
+
+ String CATEGORY_LAUNCHER = "android.intent.category.LAUNCHER";
+}
diff --git a/src/plugins/common/src/com/motorola/studio/android/common/exception/AndroidException.java b/src/plugins/common/src/com/motorola/studio/android/common/exception/AndroidException.java
new file mode 100644
index 0000000..4051957
--- /dev/null
+++ b/src/plugins/common/src/com/motorola/studio/android/common/exception/AndroidException.java
@@ -0,0 +1,62 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.common.exception;
+
+/**
+ * Represents the base exception to be used in MOTODEV Studio for Android
+ */
+public class AndroidException extends Exception
+{
+ private static final long serialVersionUID = 1854601362994563511L;
+
+ /**
+ * Creates a new AndroidException object.
+ */
+ public AndroidException()
+ {
+ }
+
+ /**
+ * Creates a new AndroidException object.
+ *
+ * @param message the message used by the Exception.
+ */
+ public AndroidException(String message)
+ {
+ super(message);
+ }
+
+ /**
+ * Creates a new AndroidException object.
+ *
+ * @param cause the associated cause.
+ */
+ public AndroidException(Throwable cause)
+ {
+ super(cause);
+ }
+
+ /**
+ * Creates a new AndroidException object.
+ *
+ * @param message the message used by the Exception.
+ * @param cause the associated cause.
+ */
+ public AndroidException(String message, Throwable cause)
+ {
+ super(message, cause);
+ }
+}
diff --git a/src/plugins/common/src/com/motorola/studio/android/common/log/StudioLogger.java b/src/plugins/common/src/com/motorola/studio/android/common/log/StudioLogger.java
new file mode 100644
index 0000000..bc3a3a6
--- /dev/null
+++ b/src/plugins/common/src/com/motorola/studio/android/common/log/StudioLogger.java
@@ -0,0 +1,125 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+package com.motorola.studio.android.common.log;
+
+import com.motorola.studio.android.logger.Logger;
+
+public abstract class StudioLogger implements UsageDataConstants
+{
+ private static final Logger logger = Logger.getLogger("Studio for Android");
+
+ public static void debug(String message)
+ {
+ logger.debug(message);
+ }
+
+ public static void info(String message)
+ {
+ logger.info(message);
+ }
+
+ public static void warn(String message)
+ {
+ logger.warn(message);
+ }
+
+ public static void error(String message)
+ {
+ logger.error(message);
+ }
+
+ public static void fatal(String message)
+ {
+ logger.fatal(message);
+ }
+
+ public static void info(String logger, String message)
+ {
+ Logger.getLogger(logger).info(message);
+ }
+
+ public static void debug(Object obj, String message)
+ {
+ Logger.getLogger(obj.getClass().toString()).debug(message);
+ }
+
+ public static void info(Class<?> aClass, String message)
+ {
+ Logger.getLogger(aClass.toString()).info(message);
+ }
+
+ public static void warn(String logger, String message)
+ {
+ Logger.getLogger(logger).warn(message);
+ }
+
+ public static void warn(String logger, String message, Throwable throwable)
+ {
+ Logger.getLogger(logger).warn(message, throwable);
+ }
+
+ public static void warn(Class<?> aClass, String message)
+ {
+ Logger.getLogger(aClass.toString()).warn(message);
+ }
+
+ public static void warn(Class<?> aClass, String message, Throwable throwable)
+ {
+ Logger.getLogger(aClass.toString()).warn(message, throwable);
+ }
+
+ public static void error(String logger, String message)
+ {
+ Logger.getLogger(logger).error(message);
+ }
+
+ public static void error(Class<?> aClass, String message)
+ {
+ Logger.getLogger(aClass.toString()).error(message);
+ }
+
+ public static void error(String logger, String message, Throwable error)
+ {
+ Logger.getLogger(logger).error(message, error);
+ }
+
+ public static void error(Class<?> aClass, String message, Throwable error)
+ {
+ Logger.getLogger(aClass.toString()).error(message, error);
+ }
+
+ public static void fatal(String logger, String message)
+ {
+ Logger.getLogger(logger).fatal(message);
+ }
+
+ /**
+ * Meanwhile, this method does nothing because eclipse UDC dependency was removed.
+ * A new approach to collect usage data need to be implemented.
+ *
+ * @param what ID of what happened.
+ * @param kind ID of the type of the event that happened.
+ * @param description Short description of what happened.
+ * @param pluginID Plugin identifier (that generated the event).
+ * @param pluginVersion Plugin version.
+ */
+ public static void collectUsageData(String what, String kind, String description,
+ String pluginID, String pluginVersion)
+ {
+ //Do nothing, see the javadoc.
+ }
+}
diff --git a/src/plugins/common/src/com/motorola/studio/android/common/log/UsageDataConstants.java b/src/plugins/common/src/com/motorola/studio/android/common/log/UsageDataConstants.java
new file mode 100644
index 0000000..95adb3f
--- /dev/null
+++ b/src/plugins/common/src/com/motorola/studio/android/common/log/UsageDataConstants.java
@@ -0,0 +1,192 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.common.log;
+
+/**
+ * Constants used for the UDC component to collect anonymous usage data.
+ * This data is periodically sent to servers if the user
+ * agreed with that. If user does not agree, the data is never
+ * used.
+ *
+ * Example of the use of KIND and DESCRIPTION when logging:
+ * The KIND <emulator> can be used with a set of different WHATs,
+ * thas is, <started>, <stoped>, <refresh> and so on. Which means
+ * that the kind of occurrence emulator can have different events,
+ * such as: the emulator was started.
+ */
+public interface UsageDataConstants
+{
+ public static final String DESCRIPTION_DEFAULT = "operation_executed";
+
+ public static final String KIND_DOWNLOAD_ADDON = "addondownload";
+
+ public static final String VALUE_EMULATOR = "emulator";
+
+ public static final String VALUE_HANDSET = "handset";
+
+ public static final String KIND_DOWNLOADSDK = "sdkdownload";
+
+ public static final String KIND_APP_MANAGEMENT = "application_management";
+
+ public static final String WHAT_APP_MANAGEMENT_CREATE = "created";
+
+ public static final String WHAT_APP_MANAGEMENT_PACKAGE = "package";
+
+ public static final String KIND_EMULATOR = "emulator";
+
+ public static final String WHAT_EMULATOR_STOP = "stopped";
+
+ public static final String WHAT_EMULATOR_RESET = "reset";
+
+ public static final String WHAT_EMULATOR_START = "started";
+
+ public static final String WHAT_EMULATOR_CREATION_WIZARD = "new_device_wizard";
+
+ public static final String WHAT_EMULATOR_LANGUAGE = "change_language";
+
+ public static final String KIND_BUILDINGBLOCK = "building_blocks";
+
+ public static final String WHAT_BUILDINGBLOCK_PROVIDER = "create_content_provider";
+
+ public static final String WHAT_BUILDINGBLOCK_ACTIVITY = "create_activity";
+
+ public static final String WHAT_BUILDINGBLOCK_RECEIVER = "create_receiver";
+
+ public static final String WHAT_BUILDINGBLOCK_SERVICE = "create_service";
+
+ public static final String WHAT_BUILDINGBLOCK_WIDGET_PROVIDER = "create_widget_provider";
+
+ public static final String KIND_LOCALIZATION = "localization";
+
+ public static final String WHAT_LOCALIZATION_AUTOMATICTRANSLATION = "automatic_translator";
+
+ public static final String KEY_TARGET = "target=";
+
+ public static final String KEY_TRANSLATION_PROVIDER = "provider=";
+
+ public static final String KEY_TRANSLATION_FROM_LANG = "from_lang=";
+
+ public static final String KEY_TRANSLATION_TO_LANG = "to_lang=";
+
+ public static final String VALUE_GOOGLE = "google";
+
+ public static final String KEY_PRJ_TARGET = "prj_target=";
+
+ public static final String KEY_TARGETLIST = "target_list=";
+
+ public static final String KEY_DEVICE_TYPE = "device=";
+
+ public static final String KEY_USE_VDL = "use_vdl=";
+
+ public static final String KEY_ISOPHONE = "isophone=";
+
+ public static final String VALUE_YES = "y";
+
+ public static final String VALUE_NO = "n";
+
+ public static final String SEPARATOR = "|";
+
+ public static final String WHAT_DATABASE_MANAGMT_CLASSES = "created";
+
+ public static final String KIND_DATABASE_MANAGMT_CLASSES = "database";
+
+ public static final String WHAT_CODESNIPPET = "inserted";
+
+ public static final String KIND_CODESNIPPET = "codesnippet";
+
+ public static final String WHAT_OPENHELPER = "created";
+
+ public static final String KIND_OPENHELPER = "codesnippet";
+
+ public static final String WHAT_TABLEWIZARD = "created";
+
+ public static final String KIND_TABLEWIZARD = "table_wizard";
+
+ public static final String WHAT_DBACTION = "created_device";
+
+ public static final String KIND_DBACTION = "dbcreate_device";
+
+ public static final String WHAT_INSTALLADDON = "install_addon";
+
+ public static final String WHAT_INSTALLDOCS = "install_docs";
+
+ public static final String KIND_INSTALL = "install";
+
+ public static final String KIND_INSTALLADDON = KIND_INSTALL;
+
+ public static final String WHAT_INSTALLSDK = "install_sdk";
+
+ public static final String KIND_INSTALLSDK = KIND_INSTALL;
+
+ public static final String KIND_INSTALLDOCS = KIND_INSTALL;
+
+ public static final String WHAT_INSTALLPLATFORM = "install_platform";
+
+ public static final String KIND_INSTALLPLATFORM = KIND_INSTALL;
+
+ public static final String WHAT_INSTALLPLATFORM_TOOLS = "install_platform_tools";
+
+ public static final String KIND_INSTALLPLATFORM_TOOLS = KIND_INSTALL;
+
+ public static final String WHAT_INSTALLSAMPLE = "install_sample";
+
+ public static final String KIND_INSTALLSAMPLE = KIND_INSTALL;
+
+ public static final String WHAT_MONKEY_EXEC = "executed";
+
+ public static final String KIND_MONKEY_EXEC = "monkey";
+
+ public static final String WHAT_VIEW_BY_LAYOUT_EXEC = "what_view_by_layout_exec";
+
+ public static final String KIND_VIEW_BY_LAYOUT_EXEC = "kind_view_by_layout_exec";
+
+ public static final String WHAT_SAMPLE_ACTIVITY_CREATED = "created";
+
+ public static final String KIND_SAMPLE_ACTIVITY_CREATED = "activity";
+
+ public static final String WHAT_BUILDINGBLOCK_PERMISSION = "permission_used";
+
+ public static final String KIND_BUILDINGBLOCK_PERMISSION = "buildingblock";
+
+ public static final String WHAT_WIDGETPROVIDER_CREATED = "created";
+
+ public static final String KIND_WIDGETPROVIDER = "widget_provider";
+
+ public static final String KIND_REMOTE_DEVICE = "remote_device";
+
+ public static final String WHAT_REMOTE_WIRELESS = "switched_to_wireless";
+
+ public static final String WHAT_REMOTE_USB = "switched_to_usb";
+
+ public static final String WHAT_OBFUSCATE = "obfuscation";
+
+ public static final String KIND_OBFUSCATE = "obfuscate_project";
+
+ public static final String KIND_DESOBFUSCATE = "desobfuscate_project";
+
+ public static final String WHAT_VIDEOS_PLAY = "play";
+
+ public static final String WHAT_VIDEOS_PLAYER_SUPPORT = "player_support";
+
+ public static final String WHAT_VIDEOS_FLASH_SUPPORT = "flash_support";
+
+ public static final String KIND_VIDEOS = "videos";
+
+ public static final String WHAT_VIEW_BY_MENU_EXEC = "what_view_by_menu_exec";
+
+ public static final String KIND_VIEW_BY_MENU_EXEC = "kind_view_by_menu_exec";
+
+}
diff --git a/src/plugins/common/src/com/motorola/studio/android/common/preferences/DialogWithToggleUtils.java b/src/plugins/common/src/com/motorola/studio/android/common/preferences/DialogWithToggleUtils.java
new file mode 100644
index 0000000..114147b
--- /dev/null
+++ b/src/plugins/common/src/com/motorola/studio/android/common/preferences/DialogWithToggleUtils.java
@@ -0,0 +1,346 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.common.preferences;
+
+import org.eclipse.core.runtime.preferences.IEclipsePreferences;
+import org.eclipse.core.runtime.preferences.IScopeContext;
+import org.eclipse.core.runtime.preferences.InstanceScope;
+import org.eclipse.jface.dialogs.IDialogConstants;
+import org.eclipse.jface.dialogs.MessageDialogWithToggle;
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.plugin.AbstractUIPlugin;
+import org.osgi.service.prefs.BackingStoreException;
+
+import com.motorola.studio.android.common.CommonPlugin;
+import com.motorola.studio.android.common.utilities.i18n.UtilitiesNLS;
+
+/**
+ * DESCRIPTION:
+ * This class provides static methods to create toggle dialogs, storing the
+ * user action into preference file. It allows resetting the preference to
+ * their original status.
+ * <br>
+ * RESPONSIBILITY:
+ * Provides the basic functionalities for questioning/informing user and
+ * persisting the choice made
+ * <br>
+ * COLABORATORS:
+ * none
+ * <br>
+ * USAGE:
+ * - use showQuestion method to provide behavior for user choice between "yes"
+ * and "no" option.
+ * - use showInformation method to provide behavior for showing informational
+ * dialog to user
+ * - use resetAllDialogsConfiguration method to reset to initial dialog
+ * configuration.
+ */
+public class DialogWithToggleUtils
+{
+
+ // suffix of the preference key of the all toggle dialogs.
+ private final static String TOGGLE_DIALOG = ".toggle.dialog";
+
+ private static IEclipsePreferences getPreferences()
+ {
+ IScopeContext scope = InstanceScope.INSTANCE;
+ return scope.getNode(CommonPlugin.PLUGIN_ID);
+ }
+
+ /**
+ * Shows a dialog with "Yes" and "No" buttons.
+ * The dialog is opened only if it is the first time that the dialog is
+ * shown or if the user did not check the option "Do not show this window
+ * again" when the dialog had been opened previously.
+ *
+ * @param preferenceKey the key to use when persisting the user's preference.
+ * @param title the dialog's title, or <code>null</code> if none.
+ * @param message the dialog's message.
+ * @return if the dialog was opened: true, if the user pressed "Yes".
+ * if the dialog was not opened: true, if the option was set to "Always".
+ */
+ public static boolean showQuestion(final String preferenceKey, final String title,
+ final String message)
+ {
+ final Boolean[] reply = new Boolean[1];
+
+ final String prefKey = preferenceKey + TOGGLE_DIALOG;
+
+ PlatformUI.getWorkbench().getDisplay().syncExec(new Runnable()
+ {
+ public void run()
+ {
+ AbstractUIPlugin plugin = CommonPlugin.getDefault();
+ IPreferenceStore store = plugin.getPreferenceStore();
+
+ String preferenceValue = store.getString(prefKey);
+
+ if (MessageDialogWithToggle.PROMPT.equals(preferenceValue)
+ || (preferenceValue.length() == 0))
+ {
+
+ IWorkbench workbench = PlatformUI.getWorkbench();
+ IWorkbenchWindow ww = workbench.getActiveWorkbenchWindow();
+ Shell shell = ww.getShell();
+
+ MessageDialogWithToggle dialog =
+ MessageDialogWithToggle.openYesNoQuestion(shell, title, message,
+ UtilitiesNLS.UI_DoNotShowMEAgain, false, store, prefKey);
+ reply[0] = (dialog.getReturnCode() == IDialogConstants.YES_ID);
+ }
+ else
+ {
+ reply[0] = preferenceValue.equals(MessageDialogWithToggle.ALWAYS);
+ }
+ }
+ });
+
+ return reply[0];
+ }
+
+ /**
+ * Shows an information dialog to user. The dialog is opened
+ * only if it is the first time that the dialog is shown or if the user did not checked
+ * the option "Do not show this window again" when the dialog had been opened previously.
+ *
+ * @param preferenceKey the key to use when persisting the user's preference
+ * @param title the dialog's title, or <code>null</code> if none
+ * @param message the dialog´s message
+ *
+ */
+ public static void showInformation(final String preferenceKey, final String title,
+ final String message)
+ {
+ final String prefKey = preferenceKey + TOGGLE_DIALOG;
+
+ PlatformUI.getWorkbench().getDisplay().syncExec(new Runnable()
+ {
+ public void run()
+ {
+ AbstractUIPlugin plugin = CommonPlugin.getDefault();
+ IPreferenceStore store = plugin.getPreferenceStore();
+
+ String preferenceValue = store.getString(prefKey);
+
+ if (MessageDialogWithToggle.PROMPT.equals(preferenceValue)
+ || (preferenceValue.length() == 0))
+ {
+ IWorkbench workbench = PlatformUI.getWorkbench();
+ IWorkbenchWindow ww = workbench.getActiveWorkbenchWindow();
+ Shell shell = ww.getShell();
+
+ MessageDialogWithToggle.openInformation(shell, title, message,
+ UtilitiesNLS.UI_DoNotShowMEAgain, false, store, prefKey);
+ }
+ }
+ });
+ }
+
+ /**
+ * Shown a warning dialog. The dialog is opened
+ * only if it is the first time that the dialog is shown or if the user did not checked
+ * the option "Do not show this window again" when the dialog had been opened previously.
+ *
+ * @param preferenceKey the key to use when persisting the user's preference
+ * @param title the dialog's title, or <code>null</code> if none
+ * @param message the dialog´s message
+ *
+ */
+ public static void showWarning(final String preferenceKey, final String title,
+ final String message)
+ {
+ final String prefKey = preferenceKey + TOGGLE_DIALOG;
+
+ PlatformUI.getWorkbench().getDisplay().syncExec(new Runnable()
+ {
+ public void run()
+ {
+ AbstractUIPlugin plugin = CommonPlugin.getDefault();
+ IPreferenceStore store = plugin.getPreferenceStore();
+
+ String preferenceValue = store.getString(prefKey);
+
+ if (MessageDialogWithToggle.PROMPT.equals(preferenceValue)
+ || (preferenceValue.length() == 0))
+ {
+ IWorkbench workbench = PlatformUI.getWorkbench();
+ IWorkbenchWindow ww = workbench.getActiveWorkbenchWindow();
+ Shell shell = ww.getShell();
+
+ MessageDialogWithToggle.openWarning(shell, title, message,
+ UtilitiesNLS.UI_DoNotShowMEAgain, false, store, prefKey);
+ }
+ }
+ });
+ }
+
+ /**
+ * Show a confirmation message.
+ *
+ * @param preferenceKey the key to use when persisting the user's preference;
+ * <code>null</code> if you don't want it persisted.
+ * @param title the dialog's title, or <code>null</code> if none
+ * @param message the dialog´s message
+ */
+ public static boolean showConfirmation(final String preferenceKey, final String title,
+ final String message)
+ {
+ final Boolean[] reply = new Boolean[1];
+
+ final String prefKey = preferenceKey + TOGGLE_DIALOG;
+
+ PlatformUI.getWorkbench().getDisplay().syncExec(new Runnable()
+ {
+ public void run()
+ {
+ AbstractUIPlugin plugin = CommonPlugin.getDefault();
+ IPreferenceStore store = plugin.getPreferenceStore();
+
+ String preferenceValue = store.getString(prefKey);
+
+ if (MessageDialogWithToggle.PROMPT.equals(preferenceValue)
+ || (preferenceValue.length() == 0))
+ {
+
+ IWorkbench workbench = PlatformUI.getWorkbench();
+ IWorkbenchWindow ww = workbench.getActiveWorkbenchWindow();
+ Shell shell = ww.getShell();
+
+ MessageDialogWithToggle dialog =
+ MessageDialogWithToggle.openOkCancelConfirm(shell, title, message,
+ UtilitiesNLS.UI_AlwaysProceed, false, store, prefKey);
+ reply[0] = (dialog.getReturnCode() == IDialogConstants.OK_ID);
+ }
+ else
+ {
+ reply[0] = preferenceValue.equals(MessageDialogWithToggle.ALWAYS);
+ }
+ }
+ });
+
+ return reply[0];
+ }
+
+ /**
+ * Shows an error dialog. The dialog is opened
+ * only if it is the first time that the dialog is shown or if the user did not checked
+ * the option "Do not show this window again" when the dialog had been opened previously.
+ *
+ * @param preferenceKey the key to use when persisting the user's preference
+ * @param title the dialog's title, or <code>null</code> if none
+ * @param message the dialog´s message
+ *
+ */
+ public static void showError(final String preferenceKey, final String title,
+ final String message)
+ {
+ final String prefKey = preferenceKey + TOGGLE_DIALOG;
+
+ PlatformUI.getWorkbench().getDisplay().syncExec(new Runnable()
+ {
+ public void run()
+ {
+ AbstractUIPlugin plugin = CommonPlugin.getDefault();
+ IPreferenceStore store = plugin.getPreferenceStore();
+
+ String preferenceValue = store.getString(prefKey);
+
+ if (MessageDialogWithToggle.PROMPT.equals(preferenceValue)
+ || (preferenceValue.length() == 0))
+ {
+ IWorkbench workbench = PlatformUI.getWorkbench();
+ IWorkbenchWindow ww = workbench.getActiveWorkbenchWindow();
+ Shell shell = ww.getShell();
+
+ MessageDialogWithToggle.openError(shell, title, message,
+ UtilitiesNLS.UI_DoNotShowMEAgain, false, store, prefKey);
+ }
+ }
+ });
+ }
+
+ /**
+ * Resets all dialog configuration clearing all 'Do not show me again'
+ * settings and showing all hidden dialogs again.
+ */
+ public static void resetAllDialogsConfiguration()
+ {
+ IEclipsePreferences preferences = getPreferences();
+ String[] propertyNames;
+ try
+ {
+ propertyNames = preferences.keys();
+ }
+ catch (BackingStoreException e)
+ {
+ propertyNames = new String[0];
+ }
+
+ for (String propertyName : propertyNames)
+ {
+ if (propertyName.contains(TOGGLE_DIALOG))
+ {
+ preferences.put(propertyName, MessageDialogWithToggle.PROMPT);
+ }
+ }
+ try
+ {
+ preferences.flush();
+ }
+ catch (BackingStoreException e)
+ {
+ //do nothing
+ }
+ }
+
+ /**
+ * Set a preference key to a certain value
+ * This key is used to toggle dialogs. Do not use it for general proposes
+ * @param preferenceKey
+ * @param value
+ */
+ public static void setToggleDialogPreferenceKey(final String preferenceKey, final String value)
+ {
+ final String prefKey = preferenceKey + TOGGLE_DIALOG;
+ AbstractUIPlugin plugin = CommonPlugin.getDefault();
+ IPreferenceStore store = plugin.getPreferenceStore();
+
+ store.setValue(prefKey, value);
+ }
+
+ /**
+ * Get a preference toggle dialog preference key
+ * This key is used to toggle dialogs. Do not use it for general proposes
+ * @param preferenceKey
+ * @return the key, or null if it is default (not set)
+ */
+ public static String getToggleDialogPreferenceKey(final String preferenceKey)
+ {
+ final String prefKey = preferenceKey + TOGGLE_DIALOG;
+ IPreferenceStore store = CommonPlugin.getDefault().getPreferenceStore();
+
+ String prefValue = null;
+ if (!store.isDefault(prefKey))
+ {
+ prefValue = store.getString(prefKey);
+ }
+ return prefValue;
+ }
+} \ No newline at end of file
diff --git a/src/plugins/common/src/com/motorola/studio/android/common/proxy/NetworkProxySettingStartup.java b/src/plugins/common/src/com/motorola/studio/android/common/proxy/NetworkProxySettingStartup.java
new file mode 100644
index 0000000..532a8af
--- /dev/null
+++ b/src/plugins/common/src/com/motorola/studio/android/common/proxy/NetworkProxySettingStartup.java
@@ -0,0 +1,42 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+package com.motorola.studio.android.common.proxy;
+
+import java.net.Authenticator;
+
+import org.eclipse.core.internal.net.ProxyManager;
+import org.eclipse.core.net.proxy.IProxyService;
+import org.eclipse.ui.IStartup;
+
+/**
+ * Startup class that prepares the proxy settings so the user is not bothered
+ * by login/password dialogs while using Studio.
+ */
+@SuppressWarnings("restriction")
+public class NetworkProxySettingStartup implements IStartup
+{
+ public void earlyStartup()
+ {
+ // Try to retrieve proxy configuration to use if necessary
+ IProxyService proxyService = ProxyManager.getProxyManager();
+ if (proxyService.isProxiesEnabled() || proxyService.isSystemProxiesEnabled())
+ {
+ Authenticator.setDefault(new ProxyAuthenticator());
+ }
+ }
+
+}
diff --git a/src/plugins/common/src/com/motorola/studio/android/common/proxy/ProxyAuthenticator.java b/src/plugins/common/src/com/motorola/studio/android/common/proxy/ProxyAuthenticator.java
new file mode 100644
index 0000000..e85be5e
--- /dev/null
+++ b/src/plugins/common/src/com/motorola/studio/android/common/proxy/ProxyAuthenticator.java
@@ -0,0 +1,84 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+package com.motorola.studio.android.common.proxy;
+
+import java.net.PasswordAuthentication;
+import java.net.URL;
+
+import org.eclipse.core.internal.net.ProxyManager;
+import org.eclipse.core.net.proxy.IProxyData;
+import org.eclipse.core.net.proxy.IProxyService;
+import org.eclipse.ui.internal.net.auth.NetAuthenticator;
+
+/**
+ * This class uses the Eclipse proxy settings for the JVM to be able to
+ * access network without bothering the user with login/password dialogs.
+ * If retrieving/setting Eclipse proxy settings to the JVM, the default
+ * behavior will kick in (which is prompting the user).
+ */
+@SuppressWarnings("restriction")
+public class ProxyAuthenticator extends NetAuthenticator
+{
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.internal.net.auth.NetAuthenticator#getPasswordAuthentication()
+ */
+ @Override
+ protected PasswordAuthentication getPasswordAuthentication()
+ {
+ // return value
+ PasswordAuthentication authentication = null;
+
+ // retrieve Eclipse proxy settings
+ IProxyService proxyService = ProxyManager.getProxyManager();
+
+ IProxyData proxyData;
+
+ // verify if the connection is https or http
+ URL url = getRequestingURL();
+ String urlStr = (url != null ? url.toString() : null);
+ if ((urlStr != null) && (urlStr.length() > 0) && urlStr.startsWith("https"))
+ {
+ proxyData = proxyService.getProxyData(IProxyData.HTTPS_PROXY_TYPE);
+ }
+ else
+ {
+ proxyData = proxyService.getProxyData(IProxyData.HTTP_PROXY_TYPE);
+ }
+
+ // proxy data retrieved; return values
+ if (proxyData != null)
+ {
+ String userId = proxyData.getUserId();
+ if ((userId != null) && (userId.trim().length() > 0))
+ {
+ String password = proxyData.getPassword();
+ authentication =
+ new PasswordAuthentication(userId,
+ (password == null ? "" : password).toCharArray());
+ }
+ }
+
+ // if setting Eclipse proxy fails, let the superclass open the user/password prompt dialog
+ if (authentication == null)
+ {
+ authentication = super.getPasswordAuthentication();
+ }
+
+ return authentication;
+ }
+}
diff --git a/src/plugins/common/src/com/motorola/studio/android/common/utilities/AndroidStatus.java b/src/plugins/common/src/com/motorola/studio/android/common/utilities/AndroidStatus.java
new file mode 100644
index 0000000..0a2392d
--- /dev/null
+++ b/src/plugins/common/src/com/motorola/studio/android/common/utilities/AndroidStatus.java
@@ -0,0 +1,48 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.common.utilities;
+
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+
+import com.motorola.studio.android.common.CommonPlugin;
+
+/**
+ * Status for Android Plugin.
+ * @deprecated use the standard {@link Status}.
+ */
+@Deprecated
+public class AndroidStatus extends Status
+{
+ /**
+ * Constructor for "OK" Status
+ */
+ public AndroidStatus()
+ {
+ super(IStatus.OK, CommonPlugin.PLUGIN_ID, null);
+ }
+
+ /**
+ * Constructor for others status.
+ * @param severity
+ * @param msg
+ */
+ public AndroidStatus(int severity, String msg)
+ {
+ super(severity, CommonPlugin.PLUGIN_ID, msg);
+ }
+
+}
diff --git a/src/plugins/common/src/com/motorola/studio/android/common/utilities/AndroidUtils.java b/src/plugins/common/src/com/motorola/studio/android/common/utilities/AndroidUtils.java
new file mode 100644
index 0000000..1a522d2
--- /dev/null
+++ b/src/plugins/common/src/com/motorola/studio/android/common/utilities/AndroidUtils.java
@@ -0,0 +1,612 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.common.utilities;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.lang.reflect.Method;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.StringTokenizer;
+
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.core.runtime.preferences.IEclipsePreferences;
+import org.eclipse.core.runtime.preferences.InstanceScope;
+import org.osgi.framework.Bundle;
+
+import com.motorola.studio.android.common.CommonPlugin;
+import com.motorola.studio.android.common.exception.AndroidException;
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.common.utilities.i18n.UtilitiesNLS;
+
+public class AndroidUtils
+{
+ private static final String CLASS_COM_ANDROID_IDE_ECLIPSE_ADT_INTERNAL_SDK_ANDROID_TARGET_DATA =
+ "com.android.ide.eclipse.adt.internal.sdk.AndroidTargetData"; //$NON-NLS-1$
+
+ private static final String CLASS_COM_ANDROID_SDKLIB_I_ANDROID_TARGET =
+ "com.android.sdklib.IAndroidTarget"; //$NON-NLS-1$
+
+ private static final String CLASS_COM_ANDROID_SDKLIB_ANDROID_VERSION =
+ "com.android.sdklib.AndroidVersion"; //$NON-NLS-1$
+
+ private static final String CLASS_COM_ANDROID_IDE_ECLIPSE_ADT_INTERNAL_SDK_SDK =
+ "com.android.ide.eclipse.adt.internal.sdk.Sdk"; //$NON-NLS-1$
+
+ //Constants
+ private static final String ANDROID_VERSION_API_LEVEL = "AndroidVersion.ApiLevel"; //$NON-NLS-1$
+
+ private static final String SOURCE_PROPERTIES = "source.properties"; //$NON-NLS-1$
+
+ private static final String ANDROID_JAR = "android.jar"; //$NON-NLS-1$
+
+ private static final String PLATFORMS = "platforms"; //$NON-NLS-1$
+
+ private static final String ANDROID = "android-"; //$NON-NLS-1$
+
+ private static final String TARGET = "target"; //$NON-NLS-1$
+
+ private static final String DEFAULT_PROPERTIES = "default.properties"; //$NON-NLS-1$
+
+ private static final String PROJECT_PROPERTIES = "project.properties"; //$NON-NLS-1$
+
+ /*
+ * Contains the exceptions to the general rule (that prepends android.permission. to the permissionName)
+ */
+ private static final Map<String, String> permissionNameToPrefixToAppend =
+ new HashMap<String, String>();
+
+ static
+ {
+ permissionNameToPrefixToAppend.put("SET_ALARM", "com.android.alarm.permission"); //$NON-NLS-1$ //$NON-NLS-2$
+ permissionNameToPrefixToAppend.put("READ_HISTORY_BOOKMARKS", //$NON-NLS-1$
+ "com.android.browser.permission"); //$NON-NLS-1$
+ permissionNameToPrefixToAppend.put("WRITE_HISTORY_BOOKMARKS", //$NON-NLS-1$
+ "com.android.browser.permission"); //$NON-NLS-1$
+ permissionNameToPrefixToAppend.put("ADD_VOICEMAIL", "com.android.voicemail.permission"); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+
+ /**
+ * Gets Android Sdk set in the preference page
+ * @return
+ */
+ public static String getSDKPathByPreference()
+ {
+ IEclipsePreferences pref = InstanceScope.INSTANCE.getNode("com.android.ide.eclipse.adt"); //$NON-NLS-1$
+ return pref.get("com.android.ide.eclipse.adt.sdk", ""); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+
+ /**
+ * Gets android jar from the project
+ * @param projectDir
+ * @return path to android.jar
+ * @throws AndroidException
+ */
+ public static File getAndroidJar(File projectDir) throws AndroidException
+ {
+ File androidTargetFolder = getAndroidTargetPathForProject(projectDir);
+ return new File(androidTargetFolder, "android.jar"); //$NON-NLS-1$
+ }
+
+ /**
+ * Returns all available intent filter permissions from a given project
+ *
+ * @return String array containing the available permissions
+ */
+ public static String[] getIntentFilterPermissions(IProject project)
+ {
+ String[] attributeValues = new String[0];
+
+ if ((project != null) && project.isOpen())
+ {
+ try
+ {
+ //reflection for: Sdk sdk = Sdk.getCurrent();
+ Class<?> clsSdk = Class.forName(CLASS_COM_ANDROID_IDE_ECLIPSE_ADT_INTERNAL_SDK_SDK);
+ Method mtdGetCurrent = clsSdk.getMethod("getCurrent", (Class[]) null); //$NON-NLS-1$
+ Object sdk = mtdGetCurrent.invoke(null, (Object[]) null);
+
+ //reflection for: IAndroidTarget target = sdk.getTarget(project);
+ Method mtdGetTarget = clsSdk.getMethod("getTarget", IProject.class); //$NON-NLS-1$
+ Object target = mtdGetTarget.invoke(sdk, project);
+
+ //reflection for: AndroidTargetData targetData = sdk.getTargetData(target);
+ Class<?> interfaceIAndroidTarget =
+ Class.forName(CLASS_COM_ANDROID_SDKLIB_I_ANDROID_TARGET);
+ Method mtdGetTargetData =
+ clsSdk.getMethod("getTargetData", interfaceIAndroidTarget); //$NON-NLS-1$
+ Object targetData = mtdGetTargetData.invoke(sdk, target);
+
+ if (targetData != null)
+ {
+ //reflection for: attributeValues = targetData.getAttributeValues("uses-permission", "android:name"); //$NON-NLS-1$ //$NON-NLS-2$
+ Class<?> clsAndroidTargetData =
+ Class.forName(CLASS_COM_ANDROID_IDE_ECLIPSE_ADT_INTERNAL_SDK_ANDROID_TARGET_DATA);
+ Method mtdGetAttributeValues =
+ clsAndroidTargetData.getMethod("getAttributeValues", String.class, //$NON-NLS-1$
+ String.class);
+ attributeValues =
+ (String[]) mtdGetAttributeValues.invoke(targetData, "uses-permission", //$NON-NLS-1$
+ "android:name"); //$NON-NLS-1$
+ }
+ }
+ catch (Exception e)
+ {
+ StudioLogger.warn("It was not possible to reach ADT methods (reflection break)",
+ e.getMessage());
+ try
+ {
+ attributeValues = getIntentFilterPermissions().toArray(attributeValues);
+ }
+ catch (IOException e1)
+ {
+ StudioLogger.error(
+ UtilitiesNLS.AndroidUtils_NotPossibleToReachPermissionsFile_Error,
+ e1.getMessage());
+ }
+ EclipseUtils.showWarningDialog(
+ UtilitiesNLS.AndroidUtils_ERROR_GETINTENTPERMISSIONSBYREFLECTION_TITLE,
+ UtilitiesNLS.AndroidUtils_ERROR_GETINTENTPERMISSIONSBYREFLECTION_MESSAGE);
+ }
+ }
+
+ return attributeValues;
+ }
+
+ /**
+ * Retrieves the path to platform/$target$, where
+ * $target$ is defined inside default.properties/project.properties file
+ *
+ * @param projectDir
+ * @return file with path to target folder
+ * @throws AndroidException
+ * problem to read default.properties/project.properties
+ */
+ public static File getAndroidTargetPathForProject(File projectDir) throws AndroidException
+ {
+ File androidTarget = null;
+ Properties properties = new Properties();
+ // changed from default.properties to project.properties after R14
+ File defaultPropertiesFile = new File(projectDir, PROJECT_PROPERTIES);
+ if (!defaultPropertiesFile.exists())
+ {
+ // WARNING: do not remove statement below assigning
+ // default.properties file to keep compatibility with projects
+ // created with ADTs before R14
+ defaultPropertiesFile = new File(projectDir, DEFAULT_PROPERTIES);
+ }
+ if (defaultPropertiesFile.exists())
+ {
+ FileInputStream fileInputStream = null;
+ try
+ {
+ fileInputStream = new FileInputStream(defaultPropertiesFile);
+ properties.load(fileInputStream);
+ }
+ catch (IOException e)
+ {
+ throw new AndroidException(
+ UtilitiesNLS.AndroidUtils_ErrorReadingDefaultPropertiesFile, e);
+ }
+ finally
+ {
+ if (fileInputStream != null)
+ {
+ try
+ {
+ fileInputStream.close();
+ }
+ catch (Exception e)
+ {
+ //Do nothing.
+ }
+ }
+ }
+ if (properties.containsKey(TARGET))
+ {
+ String targetValue = properties.getProperty(TARGET);
+ if (targetValue != null)
+ {
+ if (!targetValue.startsWith(ANDROID))
+ {
+ try
+ {
+ // add-on => <name>:<model>:<version>
+ int colonIndex = targetValue.lastIndexOf(":"); //$NON-NLS-1$
+ if (colonIndex >= 0)
+ {
+ targetValue = ANDROID + targetValue.substring(colonIndex + 1);
+ }
+ }
+ catch (Exception e)
+ {
+ //Do nothing.
+ }
+ }
+ }
+
+ boolean androidSdkPreferenceDefined = !getSDKPathByPreference().equals(""); //$NON-NLS-1$
+ String sdkPath = getSdkPath(androidSdkPreferenceDefined);
+
+ if (sdkPath != null)
+ {
+ // found sdk path
+ androidTarget =
+ new File(sdkPath + File.separator + PLATFORMS + File.separator
+ + targetValue);
+ if (!androidTarget.exists())
+ {
+ //if not found the exact version, then look for one version of android that is greater than target value (and retrieve platform folder)
+ File baseFolder = new File(sdkPath + File.separator + PLATFORMS);
+ File[] androidPlatforms = baseFolder.listFiles();
+ boolean foundJar = false;
+ if (androidPlatforms.length > 0)
+ {
+ for (File androidPlatform : androidPlatforms)
+ {
+ File sourcePropsFile = new File(androidPlatform, SOURCE_PROPERTIES);
+ File jar = new File(androidPlatform, ANDROID_JAR);
+ if (sourcePropsFile.exists() && jar.exists())
+ {
+ Properties sourceProperties = new Properties();
+ try
+ {
+ fileInputStream = new FileInputStream(sourcePropsFile);
+ sourceProperties.load(fileInputStream);
+ }
+ catch (IOException e)
+ {
+ throw new AndroidException(
+ UtilitiesNLS.AndroidUtils_ErrorReadingDefaultPropertiesFile,
+ e);
+ }
+ finally
+ {
+ if (fileInputStream != null)
+ {
+ try
+ {
+ fileInputStream.close();
+ }
+ catch (Exception e)
+ {
+ //Do nothing.
+ }
+ }
+ }
+ //platform api level
+ String apiLevel =
+ sourceProperties.getProperty(ANDROID_VERSION_API_LEVEL);
+ int index = targetValue.indexOf("-"); //$NON-NLS-1$
+ if ((index >= 0) && (apiLevel != null))
+ {
+ //project target declared
+ String versionName = targetValue.substring(index + 1);
+ try
+ {
+ Integer version = Integer.valueOf(versionName);
+ Integer apiLevelVersion = Integer.valueOf(apiLevel);
+ if (apiLevelVersion >= version)
+ {
+ //found a compatible platform
+ foundJar = true;
+ androidTarget = androidPlatform;
+ break;
+ }
+ }
+ catch (NumberFormatException nfe)
+ {
+ //ignore this folder (add-on or preview)
+ }
+ }
+ }
+ }
+ if (!foundJar)
+ {
+ throw new AndroidException(
+ androidTarget.getAbsolutePath()
+ + UtilitiesNLS.AndroidUtils_ERROR_SDK_TARGETPLATFORM_NOTFOUND);
+ }
+ }
+ }
+ }
+ else
+ {
+ throw new AndroidException(UtilitiesNLS.AndroidUtils_ERROR_SDKPATHNOTFOUND);
+ }
+ }
+ }
+ else
+ {
+ throw new AndroidException(UtilitiesNLS.AndroidUtils_ERROR_DEFAULTPROPERTIESNOTFOUND);
+ }
+ return androidTarget;
+ }
+
+ /**
+ * Returns the path for Android SDK
+ *
+ * @param preferenceDefined
+ * true if ADT preference for Android SDK is defined (take sdk path from it),
+ * false otherwise (take sdk path from environment variable)
+ * @return resolved path to Android SDK
+ */
+ private static String getSdkPath(boolean preferenceDefined)
+ {
+ String sdkPath = null;
+ if (!preferenceDefined)
+ {
+ // sdk was not defined - take from environment variable
+ String pathVariable = System.getenv("PATH"); //$NON-NLS-1$
+ String pathSeparator = System.getProperty("path.separator"); //$NON-NLS-1$
+ String subPath = null;
+ File checkedPath = null;
+ String[] folderList = null;
+
+ StringTokenizer token = new StringTokenizer(pathVariable, pathSeparator);
+ while (token.hasMoreTokens())
+ {
+ subPath = token.nextToken();
+ checkedPath = new File(subPath);
+ if (checkedPath.isDirectory())
+ {
+ folderList = checkedPath.list();
+ for (String s : folderList)
+ {
+ if (s.equals("emulator") || s.equals("emulator.exe") || s.equals("adb") //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ || s.equals("adb.exe")) //$NON-NLS-1$
+ {
+ File root = checkedPath.getParentFile();
+ sdkPath = root.getAbsolutePath();
+ break;
+ }
+ }
+ }
+ }
+
+ }
+ else
+ {
+ // sdk was defined on execution
+ sdkPath = getSDKPathByPreference();
+ }
+ return sdkPath;
+ }
+
+ /**
+ * Retrieves all activity actions from a given {@link IProject}.
+ * @param project The android project.
+ * @return An {@link String} array containing all the activity actions from the given project.
+ * @throws AndroidException if an error occurred while attempting to get the activity actions.
+ */
+ public static String[] getActivityActions(IProject project) throws AndroidException
+ {
+ String[] activityActions = new String[1];
+ File androidTargetFile =
+ AndroidUtils.getAndroidTargetPathForProject(project.getLocation().toFile());
+ TargetDataReader targetDataReader = new TargetDataReader(androidTargetFile);
+ try
+ {
+ activityActions = targetDataReader.getActivityActions().toArray(activityActions);
+ }
+ catch (IOException e)
+ {
+ throw new AndroidException(e);
+ }
+ return activityActions;
+ }
+
+ /**
+ * Retrieves all receiver actions from a given {@link IProject}.
+ * @param project The android project.
+ * @return An {@link String} array containing all the receiver actions from the given project.
+ * @throws AndroidException if an error occurred while attempting to get the receiver actions.
+ */
+ public static String[] getReceiverActions(IProject project) throws AndroidException
+ {
+ String[] receiverActions = new String[1];
+ File androidTargetFile =
+ AndroidUtils.getAndroidTargetPathForProject(project.getLocation().toFile());
+ TargetDataReader targetDataReader = new TargetDataReader(androidTargetFile);
+ try
+ {
+ receiverActions = targetDataReader.getReceiverActions().toArray(receiverActions);
+ }
+ catch (IOException e)
+ {
+ throw new AndroidException(e);
+ }
+ return receiverActions;
+ }
+
+ /**
+ * Retrieves all intent filter categories from a given {@link IProject}.
+ * @param project The android project.
+ * @return An {@link String} array containing all the intent filter categories from the given project.
+ * @throws AndroidException if an error occurred while attempting to get the intent filter categories.
+ */
+ public static String[] getIntentFilterCategories(IProject project) throws AndroidException
+ {
+ String[] intentFilterCategories = new String[1];
+ File androidTargetFile =
+ AndroidUtils.getAndroidTargetPathForProject(project.getLocation().toFile());
+ TargetDataReader targetDataReader = new TargetDataReader(androidTargetFile);
+ try
+ {
+ intentFilterCategories =
+ targetDataReader.getIntentFilterCategories().toArray(intentFilterCategories);
+ }
+ catch (IOException e)
+ {
+ throw new AndroidException(e);
+ }
+ return intentFilterCategories;
+ }
+
+ /**
+ * Retrieves all service actions from a given {@link IProject}.
+ * @param project The android project.
+ * @return An {@link String} array containing all the service actions from the given project.
+ * @throws AndroidException if an error occurred while attempting to get the service actions.
+ */
+ public static String[] getServiceActions(IProject project) throws AndroidException
+ {
+ String[] serviceActions = new String[1];
+ File androidTargetFile =
+ AndroidUtils.getAndroidTargetPathForProject(project.getLocation().toFile());
+ TargetDataReader targetDataReader = new TargetDataReader(androidTargetFile);
+ try
+ {
+ serviceActions = targetDataReader.getServiceActions().toArray(serviceActions);
+ }
+ catch (IOException e)
+ {
+ throw new AndroidException(e);
+ }
+ return serviceActions;
+ }
+
+ /**
+ * Get the api version number for a given project.
+ * This method accesses ADT via reflection. If an error occurred an AndroidException will be thrown.
+ * @param project: the project
+ * @return the api version number or 0 if some error occurs
+ * @throws Exception if any error occurred while attempting to access the ADT via reflection.
+ */
+ public static int getApiVersionNumberForProject(IProject project) throws AndroidException
+ {
+ int api = 0;
+
+ try
+ {
+ //reflection for: Sdk sdk = Sdk.getCurrent();
+ Class<?> clsSdk = Class.forName(CLASS_COM_ANDROID_IDE_ECLIPSE_ADT_INTERNAL_SDK_SDK);
+ Method mtdGetCurrent = clsSdk.getMethod("getCurrent", (Class[]) null); //$NON-NLS-1$
+ Object sdk = mtdGetCurrent.invoke(null, (Object[]) null);
+
+ //reflection for: IAndroidTarget target = sdk.getTarget(project);
+ Method mtdGetTarget = clsSdk.getMethod("getTarget", IProject.class); //$NON-NLS-1$
+ Object target = mtdGetTarget.invoke(sdk, project);
+
+ //reflection for: AndroidVersion version = target.getVersion(target);
+ Class<?> clsAndroidVersion = Class.forName(CLASS_COM_ANDROID_SDKLIB_ANDROID_VERSION);
+
+ Class<?> interfaceIAndroidTarget =
+ Class.forName(CLASS_COM_ANDROID_SDKLIB_I_ANDROID_TARGET);
+ Method mtdGetVersion = interfaceIAndroidTarget.getMethod("getVersion", (Class[]) null); //$NON-NLS-1$
+
+ Object version = mtdGetVersion.invoke(target, (Object[]) null);
+
+ if (version != null)
+ {
+ //reflection for: version.getApiLevel();
+ Method mtdGetApiLevel = clsAndroidVersion.getMethod("getApiLevel", (Class[]) null); //$NON-NLS-1$
+ Object apiLevel = mtdGetApiLevel.invoke(version, (Object[]) null);
+ api = (Integer) apiLevel;
+ }
+ }
+ catch (Exception e)
+ {
+ StudioLogger.info("It was not possible to reach ADT methods (reflection break)",
+ e.getMessage());
+ throw new AndroidException(
+ UtilitiesNLS.AndroidUtils_NotPossibleToGetAPIVersionNumber_Error);
+ }
+ return api;
+ }
+
+ /**
+ * Reads permissions.txt file inside plugin and creates the list of permissions available in the target
+ * (the list is based on the class <code>Manifest.Permission</code> from Android)
+ *
+ * <br><br>
+ * It appends android.permission. to the items,
+ * the unique exceptions are:
+ * -com.android.alarm.permission.SET_ALARM
+ * -com.android.browser.permission.READ_HISTORY_BOOKMARKS
+ * -com.android.browser.permission.WRITE_HISTORY_BOOKMARKS
+ * -com.android.voicemail.permission.ADD_VOICEMAIL
+ *
+ * @see http://developer.android.com/reference/android/Manifest.permission.html
+ * @return list of Intent Filters Permissions available
+ * @throws IOException if file not found, or if there is any problem reading the permissions file
+ */
+ private static List<String> getIntentFilterPermissions() throws IOException
+ {
+ //path inside <plugin>\files\permissions.txt
+ URL permissionsURL = null;
+
+ Bundle bundle = Platform.getBundle(CommonPlugin.PLUGIN_ID);
+ permissionsURL =
+ bundle.getEntry((new StringBuilder(IPath.SEPARATOR)).append(
+ "files" + IPath.SEPARATOR + "permissions.txt") //$NON-NLS-1$ //$NON-NLS-2$
+ .toString());
+
+ List<String> items = new ArrayList<String>();
+ InputStream is = null;
+ BufferedReader bufferedReader = null;
+ try
+ {
+ if (permissionsURL != null)
+ {
+ is = permissionsURL.openStream();
+ }
+
+ if (is != null)
+ {
+ bufferedReader = new BufferedReader(new InputStreamReader(is));
+ String line;
+ while ((line = bufferedReader.readLine()) != null)
+ {
+ String prefix = ""; //$NON-NLS-1$
+ if (!permissionNameToPrefixToAppend.containsKey(line.trim()))
+ {
+ //general rule - append android.permission.
+ prefix = "android.permission"; //$NON-NLS-1$
+ }
+ else
+ {
+ //exception rule - append the prefix available in the map
+ prefix = permissionNameToPrefixToAppend.get(line.trim());
+ }
+ items.add(prefix + "." + line.trim()); //$NON-NLS-1$
+ }
+ }
+ }
+ finally
+ {
+ if (is != null)
+ {
+ is.close();
+ }
+ if (bufferedReader != null)
+ {
+ bufferedReader.close();
+ }
+ }
+ return items;
+ }
+}
diff --git a/src/plugins/common/src/com/motorola/studio/android/common/utilities/EclipseUtils.java b/src/plugins/common/src/com/motorola/studio/android/common/utilities/EclipseUtils.java
new file mode 100644
index 0000000..e6a6224
--- /dev/null
+++ b/src/plugins/common/src/com/motorola/studio/android/common/utilities/EclipseUtils.java
@@ -0,0 +1,1411 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.common.utilities;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.FileLocator;
+import org.eclipse.core.runtime.IConfigurationElement;
+import org.eclipse.core.runtime.IExtension;
+import org.eclipse.core.runtime.IExtensionPoint;
+import org.eclipse.core.runtime.IExtensionRegistry;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.MultiStatus;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.SubMonitor;
+import org.eclipse.jdt.core.IClasspathEntry;
+import org.eclipse.jdt.core.IJavaProject;
+import org.eclipse.jdt.core.IPackageFragment;
+import org.eclipse.jdt.core.IPackageFragmentRoot;
+import org.eclipse.jdt.core.JavaCore;
+import org.eclipse.jdt.core.JavaModelException;
+import org.eclipse.jface.dialogs.ErrorDialog;
+import org.eclipse.jface.dialogs.IDialogConstants;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.jface.layout.GridDataFactory;
+import org.eclipse.jface.preference.IPreferenceNode;
+import org.eclipse.jface.preference.PreferenceDialog;
+import org.eclipse.jface.preference.PreferenceManager;
+import org.eclipse.sequoyah.localization.tools.datamodel.LocaleInfo;
+import org.eclipse.sequoyah.localization.tools.datamodel.LocalizationFile;
+import org.eclipse.sequoyah.localization.tools.datamodel.LocalizationFileBean;
+import org.eclipse.sequoyah.localization.tools.datamodel.StringLocalizationFile;
+import org.eclipse.sequoyah.localization.tools.datamodel.node.StringArrayNode;
+import org.eclipse.sequoyah.localization.tools.datamodel.node.StringNode;
+import org.eclipse.sequoyah.localization.tools.extensions.classes.ILocalizationSchema;
+import org.eclipse.sequoyah.localization.tools.managers.LocalizationManager;
+import org.eclipse.sequoyah.localization.tools.managers.ProjectLocalizationManager;
+import org.eclipse.swt.SWT;
+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.Control;
+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.ui.IEditorPart;
+import org.eclipse.ui.IEditorReference;
+import org.eclipse.ui.IViewPart;
+import org.eclipse.ui.IViewReference;
+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.console.ConsolePlugin;
+import org.eclipse.ui.console.IConsole;
+import org.eclipse.ui.console.IOConsole;
+import org.eclipse.ui.console.IOConsoleOutputStream;
+import org.eclipse.ui.internal.browser.WebBrowserEditor;
+import org.eclipse.ui.internal.browser.WebBrowserEditorInput;
+import org.eclipse.ui.internal.dialogs.WorkbenchPreferenceDialog;
+import org.osgi.framework.Bundle;
+
+import com.motorola.studio.android.common.CommonPlugin;
+import com.motorola.studio.android.common.IAndroidConstants;
+import com.motorola.studio.android.common.exception.AndroidException;
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.common.utilities.i18n.UtilitiesNLS;
+import com.motorola.studio.android.manifest.AndroidProjectManifestFile;
+import com.motorola.studio.android.model.manifest.AndroidManifestFile;
+
+/**
+ * Class that contains methods to do common tasks through the Eclipse Platform
+ */
+@SuppressWarnings("restriction")
+public class EclipseUtils
+{
+
+ private static final String STUDIO_ANDROID_CONSOLE_ID = "Studio for Android";
+
+ private static final String ORG_ECLIPSE_UI_NET_NET_PREFERENCES =
+ "org.eclipse.ui.net.NetPreferences"; //$NON-NLS-1$
+
+ private static final String ORG_ECLIPSE_EQUINOX_SECURE_STORAGE_PREFERENCES =
+ "org.eclipse.equinox.security.ui.storage"; //$NON-NLS-1$
+
+ private static final String LOCALIZATION_FILE_TYPE =
+ "org.eclipse.sequoyah.localization.android.datamodel.AndroidStringLocalizationFile"; //$NON-NLS-1$
+
+ /**
+ * Shows an error dialog
+ * @param dialogTitle
+ * The dialog title
+ * @param dialogMessage
+ * The dialog message
+ * @param status
+ * The IStatus object containing the error
+ */
+ public static void showErrorDialog(final String dialogTitle, final String dialogMessage,
+ final IStatus status)
+ {
+ PlatformUI.getWorkbench().getDisplay().syncExec(new Runnable()
+ {
+ public void run()
+ {
+ Shell aShell = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell();
+
+ Shell shell = new Shell(aShell);
+
+ Rectangle parentSize =
+ aShell.getParent() != null ? aShell.getParent().getBounds() : shell
+ .getBounds();
+
+ ErrorDialog errorDlg =
+ new ErrorDialog(shell, dialogTitle, dialogMessage, status, IStatus.ERROR);
+
+ Rectangle dialogSize = shell.getBounds();
+
+ int x = ((parentSize.width - dialogSize.width) / 2) + parentSize.x;
+ int y = ((parentSize.height - dialogSize.height) / 2) + parentSize.y;
+
+ shell.setLocation(x, y);
+
+ errorDlg.open();
+ }
+
+ });
+ }
+
+ /**
+ * Shows an information dialog
+ * @param dialogTitle
+ * The dialog title
+ * @param dialogMessage
+ * The dialog message
+ * @param buttonLabels
+ * The labels to be displayed as buttons in the dialog, or
+ * <code>null</code> to have only an "OK" button.
+ *
+ * @return The code of the button pressed by the user
+ */
+ public static int showInformationDialog(String dialogTitle, String dialogMessage,
+ String[] buttonLabels)
+ {
+ return showInformationDialog(dialogTitle, dialogMessage, null, buttonLabels,
+ MessageDialog.INFORMATION);
+ }
+
+ public static int showInformationDialog(String dialogTitle, String dialogMessage,
+ String[] buttonLabels, int dialogImageType)
+ {
+ return showInformationDialog(dialogTitle, dialogMessage, null, buttonLabels,
+ dialogImageType);
+ }
+
+ /**
+ * Shows an information dialog
+ * @param dialogTitle
+ * The dialog title
+ * @param dialogMessage
+ * The dialog message
+ * @param detailsMessage
+ * The details message
+ * @param buttonLabels
+ * The labels to be displayed as buttons in the dialog, or
+ * <code>null</code> to have only an "OK" button.
+ * @param dialogImageType
+ * The image to be displayed right before the dialogMessage
+ *
+ * @return The code of the button pressed by the user
+ */
+ public static int showInformationDialogWithDetails(String dialogTitle, String dialogMessage,
+ String detailsMessage, String[] buttonLabels)
+ {
+ return showInformationDialog(dialogTitle, dialogMessage, detailsMessage, buttonLabels,
+ MessageDialog.INFORMATION);
+ }
+
+ private static int showInformationDialog(final String dialogTitle, final String dialogMessage,
+ final String detailsMessage, final String[] buttonLabels, final int dialogImageType)
+ {
+ final int[] returnCode = new int[1];
+ PlatformUI.getWorkbench().getDisplay().syncExec(new Runnable()
+ {
+ public void run()
+ {
+ Shell shell = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell();
+
+ Rectangle parentSize;
+ if (shell.getParent() != null)
+ {
+ parentSize = shell.getParent().getBounds();
+ }
+ else
+ {
+
+ parentSize = shell.getBounds();
+ }
+
+ MessageDialog dlg;
+ String[] internButtonLabels = buttonLabels;
+ if (internButtonLabels == null)
+ {
+ internButtonLabels = new String[]
+ {
+ "OK"
+ };
+ }
+
+ if (detailsMessage == null)
+ {
+ dlg =
+ new MessageDialog(shell, dialogTitle, null, dialogMessage,
+ dialogImageType, internButtonLabels, 0);
+ }
+ else
+ {
+ dlg =
+ new MessageDialog(shell, dialogTitle, null, dialogMessage,
+ dialogImageType, internButtonLabels, 0)
+ {
+ @Override
+ protected Control createCustomArea(Composite parent)
+ {
+ final Composite main = new Composite(parent, parent.getStyle());
+ GridLayout layout = new GridLayout();
+ main.setLayout(layout);
+ GridData data = new GridData(SWT.FILL, SWT.FILL, true, false);
+ main.setLayoutData(data);
+
+ final Button detailsButton = new Button(main, SWT.PUSH);
+ detailsButton.setText(UtilitiesNLS.UI_EclipseUtils_OpenDetails);
+ data = new GridData(SWT.RIGHT, SWT.CENTER, true, false);
+ detailsButton.setLayoutData(data);
+ detailsButton.addSelectionListener(new SelectionAdapter()
+ {
+ private Label detailsText;
+
+ @Override
+ public void widgetSelected(SelectionEvent e)
+ {
+ if ((detailsText != null) && !detailsText.isDisposed())
+ {
+ detailsButton
+ .setText(UtilitiesNLS.UI_EclipseUtils_OpenDetails);
+ detailsText.dispose();
+ }
+ else
+ {
+ detailsButton
+ .setText(UtilitiesNLS.UI_EclipseUtils_CloseDetails);
+ detailsText =
+ new Label(main, SWT.WRAP | SWT.BORDER);
+ detailsText.setText(detailsMessage);
+ GridData data =
+ new GridData(SWT.FILL, SWT.FILL, true, true);
+ detailsText.setLayoutData(data);
+ GridDataFactory
+ .fillDefaults()
+ .align(SWT.FILL, SWT.BEGINNING)
+ .grab(true, false)
+ .hint(convertHorizontalDLUsToPixels(IDialogConstants.MINIMUM_MESSAGE_AREA_WIDTH),
+ SWT.DEFAULT).applyTo(detailsText);
+ }
+ getShell().pack();
+ }
+ });
+
+ return main;
+ }
+ };
+ }
+
+ Rectangle dialogSize = shell.getBounds();
+
+ int x = ((parentSize.width - dialogSize.width) / 2) + parentSize.x;
+ int y = ((parentSize.height - dialogSize.height) / 2) + parentSize.y;
+
+ shell.setLocation(x, y);
+
+ returnCode[0] = dlg.open();
+ }
+ });
+
+ return returnCode[0];
+ }
+
+ /**
+ * Display a yes/no question dialog box
+ *
+ * @param title
+ * The title of the dialog box
+ * @param message
+ * The error message
+ * @param display
+ * the parent display
+ * @return true if OK was clicked.
+ */
+ public final static boolean displayPrompt(final Display display, final String title,
+ final String message)
+ {
+ /*
+ * Sometimes we need to ask the user what he wants to do.
+ */
+ final boolean[] result = new boolean[1];
+ display.syncExec(new Runnable()
+ {
+
+ public void run()
+ {
+ Shell shell = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell();
+ result[0] = MessageDialog.openQuestion(shell, title, message);
+ }
+ });
+ return result[0];
+ }
+
+ /**
+ * Opens a Yes/No dialog
+ *
+ * @param dialogTitle
+ * The dialog title
+ * @param dialogMessage
+ * The dialog message
+ *
+ * @return true if the user answers yes and false otherwise
+ */
+ public static boolean openYesNoDialog(final String dialogTitle, final String dialogMessage)
+ {
+ final Boolean[] answer = new Boolean[1];
+
+ PlatformUI.getWorkbench().getDisplay().syncExec(new Runnable()
+ {
+
+ public void run()
+ {
+ Shell shell = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell();
+ MessageBox msgBox = new MessageBox(shell, SWT.ICON_INFORMATION | SWT.YES | SWT.NO);
+ msgBox.setText(dialogTitle);
+ msgBox.setMessage(dialogMessage);
+
+ answer[0] = msgBox.open() == SWT.YES;
+ }
+ });
+
+ return answer[0];
+ }
+
+ /**
+ * Show the error message for the given AndroidException
+ *
+ * @param e
+ * The AndroidException that generated the error to be displayed.
+ */
+ public static void showErrorDialog(AndroidException e)
+ {
+ String title = UtilitiesNLS.ERR_Gen_ErrorTitle;
+ showErrorDialog(title, e.getMessage());
+ }
+
+ /**
+ * Show the error message using the given title and message
+ *
+ * @param title
+ * of the error dialog
+ * @param message
+ * to be displayed in the error dialog.
+ */
+ public static void showErrorDialog(final String title, final String message)
+ {
+ Display.getDefault().asyncExec(new Runnable()
+ {
+
+ public void run()
+ {
+ IWorkbenchWindow ww = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
+ MessageDialog.openError(ww.getShell(), title, message);
+ }
+ });
+ }
+
+ /**
+ * Show an information message using the given title and message
+ *
+ * @param title
+ * of the dialog
+ * @param message
+ * to be displayed in the dialog.
+ */
+ public static void showInformationDialog(final String title, final String message)
+ {
+ Display.getDefault().asyncExec(new Runnable()
+ {
+
+ public void run()
+ {
+ IWorkbench workbench = PlatformUI.getWorkbench();
+ IWorkbenchWindow ww = workbench.getActiveWorkbenchWindow();
+ Shell shell = ww.getShell();
+ MessageDialog.openInformation(shell, title, message);
+ }
+ });
+ }
+
+ /**
+ * Show a warning message using the given title and message
+ *
+ * @param title
+ * of the dialog
+ * @param message
+ * to be displayed in the dialog.
+ */
+ public static void showWarningDialog(final String title, final String message)
+ {
+ Display.getDefault().asyncExec(new Runnable()
+ {
+
+ public void run()
+ {
+ IWorkbench workbench = PlatformUI.getWorkbench();
+ IWorkbenchWindow ww = workbench.getActiveWorkbenchWindow();
+ Shell shell = ww.getShell();
+ MessageDialog.openWarning(shell, title, message);
+ }
+ });
+ }
+
+ /**
+ * Show a question message using the given title and message
+ *
+ * @param title
+ * of the dialog
+ * @param message
+ * to be displayed in the dialog.
+ */
+ public static boolean showQuestionDialog(final String title, final String message)
+ {
+ class BooleanWrapper
+ {
+ public boolean bool = false;
+ }
+
+ final BooleanWrapper boolWrapper = new BooleanWrapper();
+ Display.getDefault().syncExec(new Runnable()
+ {
+
+ public void run()
+ {
+ IWorkbench workbench = PlatformUI.getWorkbench();
+ IWorkbenchWindow ww = workbench.getActiveWorkbenchWindow();
+ Shell shell = ww.getShell();
+ boolWrapper.bool = MessageDialog.openQuestion(shell, title, message);
+ }
+ });
+
+ return boolWrapper.bool;
+ }
+
+ /**
+ * Show a question message using the given title and message
+ *
+ * @param title
+ * of the dialog
+ * @param message
+ * to be displayed in the dialog.
+ */
+ public static int showQuestionWithCancelDialog(final String title, final String message)
+ {
+ class IntWrapper
+ {
+ public int diagReturn = 0;
+ }
+
+ final IntWrapper intWrapper = new IntWrapper();
+ Display.getDefault().syncExec(new Runnable()
+ {
+
+ public void run()
+ {
+ IWorkbench workbench = PlatformUI.getWorkbench();
+ IWorkbenchWindow ww = workbench.getActiveWorkbenchWindow();
+ Shell shell = ww.getShell();
+ MessageDialog dialog =
+ new MessageDialog(shell, title, null, message, MessageDialog.QUESTION,
+ new String[]
+ {
+ IDialogConstants.YES_LABEL, IDialogConstants.NO_LABEL,
+ IDialogConstants.CANCEL_LABEL
+ }, 0);
+ int diagResults = dialog.open();
+ switch (diagResults)
+ {
+ case 0:
+ intWrapper.diagReturn = SWT.YES;
+ break;
+ case 1:
+ intWrapper.diagReturn = SWT.NO;
+ break;
+ case 2:
+ default:
+ intWrapper.diagReturn = SWT.CANCEL;
+ break;
+ }
+ }
+ });
+
+ return intWrapper.diagReturn;
+ }
+
+ /**
+ * Show a question message using the given title and message
+ *
+ * @param title
+ * of the dialog
+ * @param message
+ * to be displayed in the dialog.
+ */
+ public static int showQuestionYesAllCancelDialog(final String title, final String message)
+ {
+ class IntWrapper
+ {
+ public int diagReturn = 0;
+ }
+
+ final IntWrapper intWrapper = new IntWrapper();
+ Display.getDefault().syncExec(new Runnable()
+ {
+
+ public void run()
+ {
+ IWorkbench workbench = PlatformUI.getWorkbench();
+ IWorkbenchWindow ww = workbench.getActiveWorkbenchWindow();
+ Shell shell = ww.getShell();
+ MessageDialog dialog =
+ new MessageDialog(shell, title, null, message, MessageDialog.QUESTION,
+ new String[]
+ {
+ IDialogConstants.YES_LABEL,
+ IDialogConstants.YES_TO_ALL_LABEL,
+ IDialogConstants.NO_LABEL
+ }, 0);
+ int diagResults = dialog.open();
+ switch (diagResults)
+ {
+ case 0:
+ intWrapper.diagReturn = IDialogConstants.YES_ID;
+ break;
+ case 1:
+ intWrapper.diagReturn = IDialogConstants.YES_TO_ALL_ID;
+ break;
+ case 2:
+ default:
+ intWrapper.diagReturn = IDialogConstants.NO_ID;
+ break;
+ }
+ }
+ });
+
+ return intWrapper.diagReturn;
+ }
+
+ /**
+ * Returns a plugin attribute using the extension as parameter.
+ *
+ * @param extensionId
+ * the extension from which the attribute should be collected
+ * @param elementName
+ * the extension element
+ *
+ * @return The executable class associated to the provided element
+ *
+ * @throws CoreException
+ */
+ public static Object getExecutable(String extensionId, String elementName) throws CoreException
+ {
+ Object executable = null;
+
+ IExtensionRegistry registry = Platform.getExtensionRegistry();
+ IExtension fromExtension = registry.getExtension(extensionId);
+
+ if ((fromExtension != null) && (elementName != null))
+ {
+ IConfigurationElement[] elements = fromExtension.getConfigurationElements();
+
+ for (IConfigurationElement element : elements)
+ {
+ if (elementName.equals(element.getName()))
+ {
+ executable = element.createExecutableExtension("class");
+ }
+ }
+ }
+
+ return executable;
+ }
+
+ /**
+ * Returns an array of extensions that are plugged in a provided extension
+ * point
+ *
+ * @param extensionPointId
+ * the id of the extension point to look for extensions at
+ *
+ * @return an array containing the plugins plugged at the extension point
+ */
+ public static IExtension[] getInstalledPlugins(String extensionPointId)
+ {
+ IExtensionRegistry registry = Platform.getExtensionRegistry();
+ IExtensionPoint extensionPoint = registry.getExtensionPoint(extensionPointId);
+ IExtension[] pluggedExtensions;
+
+ if (extensionPoint != null)
+ {
+ pluggedExtensions = extensionPoint.getExtensions();
+ }
+ else
+ {
+ pluggedExtensions = new IExtension[0];
+ }
+
+ return pluggedExtensions;
+ }
+
+ /**
+ * Retrieves a view object, showing it at the IDE if hidden
+ *
+ * @param viewId
+ * The identifier of the view to be shown
+ *
+ * @return The view that was just opened, or a reference to an already
+ * opened view
+ *
+ * @throws PartInitException
+ * If the view cannot be shown at the workbench part
+ */
+ public static IViewPart showView(final String viewId) throws PartInitException
+ {
+ final Object[] tempObj = new Object[1];
+
+ Display.getDefault().syncExec(new Runnable()
+ {
+
+ public void run()
+ {
+ IWorkbenchWindow activeWindow =
+ PlatformUI.getWorkbench().getActiveWorkbenchWindow();
+ if (activeWindow != null)
+ {
+ IWorkbenchPage activePage = activeWindow.getActivePage();
+ if (activePage != null)
+ {
+ try
+ {
+ tempObj[0] = activePage.showView(viewId);
+ }
+ catch (PartInitException e)
+ {
+ tempObj[0] = e;
+ }
+ }
+ }
+ }
+ });
+
+ if (tempObj[0] instanceof PartInitException)
+ {
+ throw (PartInitException) tempObj[0];
+ }
+
+ return (IViewPart) tempObj[0];
+ }
+
+ /**
+ * Retrieves a view object from the active window, but do not show if it is
+ * hidden
+ *
+ * @param viewId
+ * The identifier of the view to be retrieved
+ *
+ * @return A reference to the view identified by viewId if available; null
+ * otherwise
+ */
+ public static IViewPart getActiveView(final String viewId)
+ {
+ final Object[] tempObj = new Object[1];
+
+ Display.getDefault().syncExec(new Runnable()
+ {
+
+ public void run()
+ {
+ IWorkbenchWindow activeWindow =
+ PlatformUI.getWorkbench().getActiveWorkbenchWindow();
+ if (activeWindow != null)
+ {
+ IWorkbenchPage activePage = activeWindow.getActivePage();
+ if (activePage != null)
+ {
+ IViewReference ref = activePage.findViewReference(viewId);
+ if (ref != null)
+ {
+ IViewPart part = ref.getView(false);
+ tempObj[0] = part;
+ }
+
+ }
+ }
+ }
+ });
+
+ return (IViewPart) tempObj[0];
+ }
+
+ /**
+ * Retrieves a view object, but do not show it if hidden
+ *
+ * @param viewId
+ * The identifier of the view object to be retrieved
+ *
+ * @return A collection of views with provided id that are being shown in
+ * any opened perspective
+ */
+ public static Collection<IViewPart> getAllOpenedViewsWithId(final String viewId)
+ {
+ final Collection<IViewPart> openedViews = new LinkedHashSet<IViewPart>();
+
+ Display.getDefault().syncExec(new Runnable()
+ {
+
+ public void run()
+ {
+ IWorkbenchWindow[] allWindows = PlatformUI.getWorkbench().getWorkbenchWindows();
+ for (IWorkbenchWindow window : allWindows)
+ {
+ IWorkbenchPage[] allPagesInWindow = window.getPages();
+
+ for (IWorkbenchPage page : allPagesInWindow)
+ {
+ IViewPart view = page.findView(viewId);
+ if (view != null)
+ {
+ openedViews.add(view);
+ }
+ }
+ }
+ }
+ });
+
+ return openedViews;
+ }
+
+ /**
+ * Retrieves all editor objects
+ *
+ * @return A collection of all editors
+ */
+ public static Collection<IEditorPart> getAllOpenedEditors()
+ {
+ final Collection<IEditorPart> editors = new LinkedHashSet<IEditorPart>();
+
+ Display.getDefault().syncExec(new Runnable()
+ {
+
+ public void run()
+ {
+ IWorkbenchWindow[] allWindows = PlatformUI.getWorkbench().getWorkbenchWindows();
+ for (IWorkbenchWindow window : allWindows)
+ {
+ IWorkbenchPage[] allPagesInWindow = window.getPages();
+
+ for (IWorkbenchPage page : allPagesInWindow)
+ {
+ IEditorReference[] editorRefs = page.getEditorReferences();
+ for (IEditorReference editorRef : editorRefs)
+ {
+ editors.add(editorRef.getEditor(false));
+ }
+ }
+ }
+ }
+ });
+
+ return editors;
+ }
+
+ /**
+ * Retrieves the page for the given editor
+ *
+ * @param editor
+ *
+ * @return A page
+ */
+ public static IWorkbenchPage getPageForEditor(final IEditorPart editor)
+ {
+
+ final Object[] tempObj = new Object[1];
+
+ Display.getDefault().syncExec(new Runnable()
+ {
+
+ public void run()
+ {
+ IWorkbenchWindow[] allWindows = PlatformUI.getWorkbench().getWorkbenchWindows();
+ for (IWorkbenchWindow window : allWindows)
+ {
+ IWorkbenchPage[] allPagesInWindow = window.getPages();
+
+ for (IWorkbenchPage page : allPagesInWindow)
+ {
+ if (page.findEditor(editor.getEditorInput()) != null)
+ {
+ tempObj[0] = page;
+ break;
+ }
+ }
+ }
+ }
+ });
+
+ return (IWorkbenchPage) tempObj[0];
+ }
+
+ /**
+ * Open a web browser editor to display the given URL. If there is already
+ * an opened Web Browser Editor for the given URL, it is activated and a new
+ * one is NOT opened.
+ *
+ * @param wantedUrl
+ * URL to be opened.
+ * @return the opened Web Browser Editor
+ */
+ public static IEditorReference openedWebEditor(IWorkbenchPage page, URL wantedUrl)
+ {
+ IEditorReference wantedWebEditor = null;
+
+ if (page != null)
+ {
+ for (IEditorReference editor : page.getEditorReferences())
+ {
+ if (WebBrowserEditor.WEB_BROWSER_EDITOR_ID.equals(editor.getId()))
+ {
+ try
+ {
+ WebBrowserEditorInput webEditorInput =
+ (WebBrowserEditorInput) editor.getEditorInput();
+ URL openedURL = webEditorInput.getURL();
+ if ((openedURL != null) && openedURL.equals(wantedUrl))
+ {
+ wantedWebEditor = editor;
+ break;
+ }
+ }
+ catch (Exception e)
+ {
+ StudioLogger.error(EclipseUtils.class,
+ "Failed to get URL displayed by the opened Web Editor");
+ }
+ }
+ }
+ }
+
+ if (wantedWebEditor != null)
+ {
+ StudioLogger
+ .debug(EclipseUtils.class,
+ "There is already an opened Web Browser Editor displaying the wanted URL. Simply activate it.");
+ page.activate(wantedWebEditor.getEditor(true));
+ }
+ else
+ {
+ StudioLogger.debug(EclipseUtils.class, "Open new Web Browser Editor for: " + wantedUrl);
+ WebBrowserEditorInput input = new WebBrowserEditorInput(wantedUrl);
+
+ WebBrowserEditor.open(input);
+ }
+
+ return wantedWebEditor;
+ }
+
+ /**
+ * Retrieves the install location on the filesystem based on the given plug-in identifier
+ * @param identifier the plug-in id.
+ * @return A string containing the install path for the bundle with id - identifier.
+ */
+ public static String getInstallLocation(String identifier)
+ {
+ return getInstallLocation(Platform.getBundle(identifier));
+ }
+
+ /**
+ * Retrieves the install location for the given bundle.
+ * @param bundle
+ * @return the bundle install location.
+ */
+ public static String getInstallLocation(Bundle bundle)
+ {
+ String installLocation = "";
+ try
+ {
+ URL locationUrl = FileLocator.find(bundle, new Path("/"), null);
+ URL fileUrl = FileLocator.toFileURL(locationUrl);
+ installLocation = (new File(fileUrl.getFile())).getAbsolutePath();
+ }
+ catch (Exception e)
+ {
+ StudioLogger.error(EclipseUtils.class, "Error finding install location for bundle: "
+ + bundle.getBundleId(), e);
+ }
+ return installLocation;
+ }
+
+ /**
+ * Open the preference page with the specified ID
+ * @param nodeID the id of preference page to show
+ */
+ @SuppressWarnings("unchecked")
+ public static void openPreference(Shell shell, String nodeID)
+ {
+ // Makes the network preferences dialog manager
+ PreferenceManager manager = PlatformUI.getWorkbench().getPreferenceManager();
+ IPreferenceNode networkNode = null;
+ for (IPreferenceNode node : (List<IPreferenceNode>) manager
+ .getElements(PreferenceManager.PRE_ORDER))
+ {
+ if (node.getId().equals(nodeID))
+ {
+ networkNode = node;
+ break;
+ }
+ }
+ PreferenceManager prefMan = new PreferenceManager();
+ if (networkNode != null)
+ {
+ prefMan.addToRoot(networkNode);
+ }
+ PreferenceDialog preferencesDialog = new WorkbenchPreferenceDialog(shell, prefMan);
+ preferencesDialog.create();
+ preferencesDialog.open();
+ }
+
+ /**
+ * Convenience method to open the preferences dialog with the secure storage preference page
+ */
+ public static void openSecureStoragePreferences(Shell shell)
+ {
+ openPreference(shell, ORG_ECLIPSE_EQUINOX_SECURE_STORAGE_PREFERENCES);
+ }
+
+ /**
+ * Convenience method to open the preferences dialog with the network preferences preference page
+ */
+ public static void openNetworkPreferences(Shell shell)
+ {
+ openPreference(shell, ORG_ECLIPSE_UI_NET_NET_PREFERENCES);
+ }
+
+ /**
+ * Looks for the most severe {@link Status} within a {@link MultiStatus}.
+ * @param errorStatus.
+ * @return the most severe status of them all.
+ */
+ public static IStatus findMostSevereError(final MultiStatus errorStatus)
+ {
+ IStatus mostSevere = null;
+ if (!errorStatus.isOK())
+ {
+ for (IStatus status : errorStatus.getChildren())
+ {
+ if (mostSevere == null)
+ {
+ mostSevere = status;
+ }
+ if (status.getSeverity() > mostSevere.getSeverity())
+ {
+ mostSevere = status;
+ }
+ }
+ }
+ return mostSevere;
+ }
+
+ /**
+ * Reads a resource located inside the plugin, such as a template file.
+ * @param resourcePath - The path to the resource.
+ * @return An array of bytes from the resource
+ * @throws IOException
+ */
+ public static String readEmbeddedResource(Bundle bundle, String resourcePath)
+ {
+
+ InputStream is = null;
+ BufferedReader bufferedReader = null;
+ String embeddedResourcePath = null;
+ try
+ {
+
+ URL url = bundle.getEntry((new StringBuilder("/")).append(resourcePath).toString());
+ if (url != null)
+ {
+ is = url.openStream();
+ }
+
+ if (is != null)
+ {
+ bufferedReader = new BufferedReader(new InputStreamReader(is));
+ StringBuilder result = new StringBuilder(bufferedReader.readLine());
+ String line;
+ while ((line = bufferedReader.readLine()) != null)
+ {
+ result.append('\n');
+ result.append(line);
+ }
+ embeddedResourcePath = result.toString();
+ }
+ }
+ catch (IOException ioEx)
+ {
+
+ StudioLogger
+ .error(CommonPlugin.class, "Error while reading an embedded resource", ioEx); //$NON-NLS-1$
+
+ }
+ finally
+ {
+ if (bufferedReader != null)
+ {
+ try
+ {
+ bufferedReader.close();
+ }
+ catch (IOException e)
+ {
+ //Do nothing.
+ }
+ }
+ if (is != null)
+ {
+ try
+ {
+ is.close();
+ }
+ catch (IOException e)
+ {
+ //Do nothing.
+ }
+ }
+ }
+
+ return embeddedResourcePath;
+ }
+
+ /**
+ * Gets the default package from project.
+ * @param javaProject
+ * @return the project's default package.
+ * @throws JavaModelException
+ */
+ public static IPackageFragment getDefaultPackageFragment(IJavaProject javaProject)
+ throws JavaModelException
+ {
+ IPackageFragment pack = null;
+ AndroidManifestFile manifest = null;
+
+ if ((javaProject != null) && javaProject.isOpen())
+ {
+ // First, tries to get the default package from the AndroidManifest.xml file
+ try
+ {
+ manifest = AndroidProjectManifestFile.getFromProject(javaProject.getProject());
+ }
+ catch (AndroidException e)
+ {
+ // Do nothing
+ }
+ catch (CoreException e)
+ {
+ // Do nothing
+ }
+
+ if (manifest != null)
+ {
+ String defaultPackage = manifest.getManifestNode().getPackageName();
+
+ if ((defaultPackage != null) && (defaultPackage.trim().length() > 0))
+ {
+ IPackageFragment[] allPacks = javaProject.getPackageFragments();
+
+ if (allPacks != null)
+ {
+ for (IPackageFragment frag : allPacks)
+ {
+ if (frag.getElementName().equals(defaultPackage))
+ {
+ pack = frag;
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ // If the default package could not get from the AndroidManifest.xml file, search for
+ // one in the project
+ if (pack == null)
+ {
+ IPackageFragment[] packs = javaProject.getPackageFragments();
+ if (packs != null)
+ {
+ for (int i = 0; (i < packs.length) && (pack == null); i++)
+ {
+ if (packs[i].getKind() != IPackageFragmentRoot.K_BINARY)
+ {
+ if (!isInsideGenFolder(packs[i]) && !packs[i].isDefaultPackage()
+ && packs[i].getElementName().contains(".") && packs[i].exists()) //$NON-NLS-1$
+ {
+ pack = packs[i];
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return pack;
+ }
+
+ /**
+ * Checks if a package fragment is inside the "gen" folder
+ * @param fragment The package fragment to be checked
+ * @return true if the package fragment is inside the "gen" folder or false otherwise
+ */
+ private static boolean isInsideGenFolder(IPackageFragment fragment)
+ {
+ boolean isInside =
+ (fragment.getParent() instanceof IPackageFragmentRoot)
+ && fragment.getParent().getElementName()
+ .equals(IAndroidConstants.GEN_SRC_FOLDER);
+
+ return isInside;
+ }
+
+ /**
+ * This method adds a list of paths to all projects classpaths settings.
+ * @param javaProjects List of projects that will have the classpath changed
+ * @param libsPaths List of lib paths to be added to Projects' classpaths
+ * @param monitor Monitor to track progress or null if it's not necessary.
+ * @return IStatus The status of the operation. This method stops processing at the first error found.
+ */
+ public static IStatus addLibsToProjects(List<IJavaProject> javaProjects, List<IPath> libsPaths,
+ IProgressMonitor monitor)
+ {
+ SubMonitor subMonitor = SubMonitor.convert(monitor);
+ subMonitor.beginTask(UtilitiesNLS.ProjectUtils_AddLibsProgress_ConfiguringClassPaths,
+ ((javaProjects.size() * 2) + libsPaths.size()) * 1000);
+ IStatus status = Status.OK_STATUS;
+ IClasspathEntry[] classPathEntries = new IClasspathEntry[libsPaths.size()];
+ int i = 0;
+ subMonitor.subTask(UtilitiesNLS.ProjectUtils_AddLibsProgress_PreparingPaths);
+ for (IPath libPath : libsPaths)
+ {
+ IClasspathEntry classpathEntry = JavaCore.newLibraryEntry(libPath, null, null);
+ classPathEntries[i] = classpathEntry;
+ i++;
+ subMonitor.worked(1000);
+ }
+
+ subMonitor.subTask(UtilitiesNLS.ProjectUtils_AddLibsProgress_ConfiguringProjects);
+ for (IJavaProject javaProject : javaProjects)
+ {
+ IClasspathEntry[] rawClasspath;
+ try
+ {
+ rawClasspath = javaProject.getRawClasspath();
+ int length = rawClasspath.length;
+ int newEntriesLength = classPathEntries.length;
+ int newLenght = length + newEntriesLength;
+ IClasspathEntry[] newClassPath = new IClasspathEntry[newLenght];
+
+ System.arraycopy(rawClasspath, 0, newClassPath, 0, length); //Copy the existent classPath to the new array.
+ System.arraycopy(classPathEntries, 0, newClassPath, length, newEntriesLength); //Copy the new entries to the new array
+ subMonitor.worked(1000);
+ javaProject.setRawClasspath(newClassPath, subMonitor.newChild(1000)); // Set the Project's classpath.
+ }
+ catch (JavaModelException e)
+ {
+ status =
+ new Status(IStatus.ERROR, CommonPlugin.PLUGIN_ID,
+ UtilitiesNLS.ProjectUtils_AddLibsProgress_ErrorSettingClasspaths, e);
+ break;
+ }
+ }
+ subMonitor.done();
+ return status;
+ }
+
+ /**
+ * Verifies if a given libPath is already available on the project classpath.
+ * @param javaProject
+ * @param libPath
+ * @return true if present, false otherwise
+ */
+ public static boolean isLibOnClasspath(IJavaProject javaProject, IPath libPath)
+ {
+ try
+ {
+ IClasspathEntry[] rawClasspath = javaProject.getRawClasspath();
+ for (IClasspathEntry classpathEntry : rawClasspath)
+ {
+ if (classpathEntry.getPath().equals(libPath))
+ {
+ return true;
+ }
+ }
+ }
+ catch (JavaModelException e)
+ {
+ return false;
+ }
+ return false;
+ }
+
+ /**
+ * Add strings and array to string.xml file
+ * @param project
+ * @param strings string entries to add
+ * @param arrays array entries to add
+ * @param monitor array entries to add
+ * @throws CoreException
+ * @throws IOException
+ */
+ public static void createOrUpdateDictionaryFile(IProject project, Map<String, String> strings,
+ Map<String, List<String>> arrays, IProgressMonitor monitor) throws IOException,
+ CoreException
+ {
+ List<StringNode> stringNodes = new ArrayList<StringNode>();
+ List<StringArrayNode> arrayNodes = new ArrayList<StringArrayNode>();
+
+ if (strings != null)
+ {
+ Set<String> stringSet = strings.keySet();
+ for (String key : stringSet)
+ {
+ String strValue = strings.get(key);
+ stringNodes.add(new StringNode(key, strValue));
+ }
+ }
+ if (arrays != null)
+ {
+ Set<String> arraySet = arrays.keySet();
+ for (String key : arraySet)
+ {
+ List<String> arrayValues = arrays.get(key);
+ StringArrayNode strArray = new StringArrayNode(key);
+ for (String value : arrayValues)
+ {
+ strArray.addValue(value);
+ }
+ arrayNodes.add(strArray);
+ }
+ }
+ createOrUpdateDictionaryFile(project, stringNodes, arrayNodes, monitor);
+ return;
+ }
+
+ /**
+ * Add strings and array to string.xml file
+ * @param project
+ * @param strings string entries to add
+ * @param arrays array entries to add
+ * @param monitor array entries to add
+ * @throws CoreException
+ * @throws IOException
+ */
+ public static void createOrUpdateDictionaryFile(IProject project, List<StringNode> strings,
+ List<StringArrayNode> arrays, IProgressMonitor monitor) throws IOException,
+ CoreException
+ {
+ int taskSize = strings != null ? strings.size() : 0;
+ taskSize += arrays != null ? arrays.size() : 0;
+
+ monitor.beginTask(UtilitiesNLS.UI_ProjectCreationSupport_Creating_Strings_Task,
+ (taskSize * 100) + 100);
+
+ IFile projectStringXmlFile =
+ project.getFile(IAndroidConstants.RES_DIR + IAndroidConstants.VALUES_DIR
+ + IAndroidConstants.STRINGS_FILE);
+
+ LocalizationFile locFile = null;
+
+ if (projectStringXmlFile.exists())
+ {
+
+ ProjectLocalizationManager projManager =
+ LocalizationManager.getInstance().getProjectLocalizationManager(project, true);
+
+ //load localization file
+ locFile =
+ projManager.getProjectLocalizationSchema().loadFile(LOCALIZATION_FILE_TYPE,
+ projectStringXmlFile);
+ if (locFile.getLocalizationProject() == null)
+ {
+ locFile.setLocalizationProject(projManager.getLocalizationProject());
+ }
+
+ //add new string nodes
+ for (StringNode strNode : strings)
+ {
+ ((StringLocalizationFile) locFile).addStringNode(strNode);
+ }
+ List<StringArrayNode> currentArrays =
+ ((StringLocalizationFile) locFile).getStringArrays();
+ List<StringArrayNode> newArrays = new ArrayList<StringArrayNode>();
+ newArrays.addAll(currentArrays);
+
+ //add new array nodes
+ for (StringArrayNode strArray : arrays)
+ {
+ newArrays.add(strArray);
+ }
+ ((StringLocalizationFile) locFile).setStringArrayNodes(newArrays);
+
+ //update file
+ LocalizationManager.getInstance().getLocalizationSchema(project).updateFile(locFile);
+ }
+ else
+ {
+ ILocalizationSchema locSchema =
+ LocalizationManager.getInstance().getLocalizationSchema(project);
+
+ LocalizationFileBean bean =
+ new LocalizationFileBean(LOCALIZATION_FILE_TYPE, projectStringXmlFile,
+ new LocaleInfo(), strings, arrays);
+
+ locFile = locSchema.createLocalizationFile(bean);
+
+ locSchema.createLocalizationFile(locFile);
+
+ }
+ }
+
+ /**
+ * Retrieves the Studio console {@link IOConsoleOutputStream}. The console with name: STUDIO_ANDROID_CONSOLE_ID
+ * @param activate boolean stating whether the console must be activated or not, brought to front.
+ * @return the {@link IOConsoleOutputStream} for the Studio console.
+ */
+ public static IOConsoleOutputStream getStudioConsoleOutputStream(boolean activate)
+ {
+ IConsole activeConsole = null;
+ IConsole[] consoles = ConsolePlugin.getDefault().getConsoleManager().getConsoles();
+ for (IConsole console : consoles)
+ {
+ if (console.getName().equals(STUDIO_ANDROID_CONSOLE_ID))
+ {
+ activeConsole = console;
+ }
+ }
+
+ if (activeConsole == null)
+ {
+ activeConsole = new IOConsole(STUDIO_ANDROID_CONSOLE_ID, null);
+ ConsolePlugin.getDefault().getConsoleManager().addConsoles(new IConsole[]
+ {
+ activeConsole
+ });
+ }
+ if (activate)
+ {
+ ((IOConsole) activeConsole).activate();
+ }
+ IOConsoleOutputStream consoleOut = ((IOConsole) activeConsole).newOutputStream();
+ return consoleOut;
+ }
+
+}
diff --git a/src/plugins/common/src/com/motorola/studio/android/common/utilities/FileUtil.java b/src/plugins/common/src/com/motorola/studio/android/common/utilities/FileUtil.java
new file mode 100644
index 0000000..a3126fb
--- /dev/null
+++ b/src/plugins/common/src/com/motorola/studio/android/common/utilities/FileUtil.java
@@ -0,0 +1,1862 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.common.utilities;
+
+import static com.motorola.studio.android.common.log.StudioLogger.warn;
+
+import java.io.BufferedOutputStream;
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.LineNumberReader;
+import java.io.OutputStream;
+import java.io.UnsupportedEncodingException;
+import java.math.BigInteger;
+import java.nio.channels.FileChannel;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.StringTokenizer;
+import java.util.zip.CRC32;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+
+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.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.SubMonitor;
+import org.eclipse.core.runtime.SubProgressMonitor;
+import org.eclipse.jface.dialogs.IDialogConstants;
+import org.eclipse.jface.text.Document;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.ui.editors.text.TextFileDocumentProvider;
+
+import com.motorola.studio.android.common.CommonPlugin;
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.common.utilities.i18n.UtilitiesNLS;
+
+/**
+ * DESCRIPTION: This class provides utility methods to handle files, like
+ * copying and deleting directories sub-trees.
+ *
+ * USAGE: See public methods
+ */
+public class FileUtil
+{
+ public static final int OS_WINDOWS = 0;
+
+ public static final int OS_LINUX = 1;
+
+ public static final char[] MAC_SPECIAL_CHAR =
+ {
+ '\\', ' ', '\'', '"', '!', '@', '$', '&', '*', '(', ')', '=', '`', '[', ']', '{', '}',
+ '^', '<', '>', ':', ';', '?', '|'
+ };
+
+ public static final char[] LINUX_SPECIAL_CHAR =
+ {
+ '\\', ' ', '\'', '"', '!', '$', '&', '*', '(', ')', '=', '`', '[', ']', '{', '}', '^',
+ '<', '>', ':', ';', '?', '|'
+ };
+
+ public static final char ESCAPE_CHAR = '\\';
+
+ private static final int BUFFER_SIZE = 1024;
+
+ /**
+ * Copy full list of contents from a directory to another. The source
+ * directory is not created within the target one.
+ *
+ * @param fromDir
+ * Source directory.
+ * @param toDir
+ * Target directory.
+ *
+ * @param IOException if I/O occurs
+ */
+ public static void copyDir(File fromDir, File toDir) throws IOException
+ {
+ if ((fromDir != null) && fromDir.isDirectory() && fromDir.canRead() && (toDir != null)
+ && toDir.isDirectory() && toDir.canWrite())
+ {
+ for (File child : fromDir.listFiles())
+ {
+ if (child.isFile())
+ {
+ copyFile(child, new File(toDir, child.getName()));
+ }
+ else
+ {
+ // create directory and copy its children recursively
+ File newDir = new File(toDir.getAbsolutePath(), child.getName());
+ newDir.mkdir();
+ copyDir(child, newDir);
+ }
+ }
+
+ StudioLogger.info("The directory " + fromDir.getName() + " was successfully copied to " //$NON-NLS-1$ //$NON-NLS-2$
+ + toDir.getName() + "."); //$NON-NLS-1$
+
+ }
+ else
+ {
+ //error detected
+ String errorMessage = ""; //$NON-NLS-1$
+ if (fromDir == null)
+ {
+ errorMessage = "Null pointer for source directory."; //$NON-NLS-1$
+ }
+ else
+ {
+ if (!fromDir.isDirectory())
+ {
+ errorMessage = fromDir.getName() + " is not a directory."; //$NON-NLS-1$
+ }
+ else
+ {
+ if (!fromDir.canRead())
+ {
+ errorMessage = "Cannot read from " + fromDir.getName() + "."; //$NON-NLS-1$ //$NON-NLS-2$
+ }
+ else
+ {
+ if (toDir == null)
+ {
+ errorMessage = "Null pointer for destination directory."; //$NON-NLS-1$
+ }
+ else
+ {
+ if (!toDir.isDirectory())
+ {
+ errorMessage = toDir.getName() + " is not a directory."; //$NON-NLS-1$
+ }
+ else
+ {
+ if (!toDir.canWrite())
+ {
+ errorMessage = "Cannot write to" + toDir.getName() + "."; //$NON-NLS-1$ //$NON-NLS-2$
+ }
+ }
+ }
+ }
+ }
+ }
+ StudioLogger.error(errorMessage);
+ throw new IOException("Error copying directory: " + errorMessage); //$NON-NLS-1$
+ }
+ }
+
+ /**
+ * Copies the source file to the given target.
+ *
+ * @param source -
+ * the absolute path of the source file.
+ * @param target -
+ * the absolute path of the target file.
+ */
+ public static void copyFile(File source, File target) throws IOException
+ {
+ copyFile(source.getAbsolutePath(), target.getAbsolutePath());
+ }
+
+ /**
+ * Copies the source file to the given target.
+ *
+ * @param source -
+ * the absolute path of the source file.
+ * @param target -
+ * the absolute path of the target file.
+ */
+ private static void copyFile(String source, String target) throws IOException
+ {
+ FileChannel sourceFileChannel = null;
+ FileChannel targetFileChannel = null;
+ FileInputStream sourceFileInStream = null;
+ FileOutputStream targetFileOutStream = null;
+ try
+ {
+ sourceFileInStream = new FileInputStream(source);
+ sourceFileChannel = sourceFileInStream.getChannel();
+ targetFileOutStream = new FileOutputStream(target);
+ targetFileChannel = targetFileOutStream.getChannel();
+ targetFileChannel.transferFrom(sourceFileChannel, 0, sourceFileChannel.size());
+ StudioLogger.info("The file " + source + " was successfully copied to " + target + "."); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+ catch (IOException e)
+ {
+ StudioLogger.error("Error copying file" + source + "to " + target + "."); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ throw e;
+ }
+ finally
+ {
+ try
+ {
+ if (sourceFileChannel != null)
+ {
+ sourceFileChannel.close();
+ }
+ }
+ catch (IOException e)
+ {
+ StudioLogger.error("Error closing file " + source + "."); //$NON-NLS-1$ //$NON-NLS-2$
+ throw e;
+ }
+
+ try
+ {
+ if (targetFileChannel != null)
+ {
+ targetFileChannel.close();
+ }
+ }
+ catch (IOException e)
+ {
+ StudioLogger.error("Error closing file" + target + "."); //$NON-NLS-1$ //$NON-NLS-2$
+ throw e;
+ }
+
+ try
+ {
+ if (sourceFileInStream != null)
+ {
+ sourceFileInStream.close();
+ }
+ }
+ catch (IOException e)
+ {
+ StudioLogger.error("Error closing file" + source + "."); //$NON-NLS-1$ //$NON-NLS-2$
+ throw e;
+ }
+
+ try
+ {
+ if (targetFileOutStream != null)
+ {
+ targetFileOutStream.close();
+ }
+ }
+ catch (IOException e)
+ {
+ StudioLogger.error("Error closing file" + target + "."); //$NON-NLS-1$ //$NON-NLS-2$
+ throw e;
+ }
+
+ }
+ }
+
+ /**
+ * This method deletes the directory, all files and all subdirectories under
+ * it. If a deletion fails, the method stops attempting to delete and
+ * returns false.
+ *
+ * @param directory
+ * The directory to be deleted
+ * @return Returns true if all deletions were successful. If the directory
+ * doesn't exist returns false.
+ * @throws IOException
+ * When the parameter isn't a directory
+ */
+ public static boolean deleteDirRecursively(File directory) throws IOException
+ {
+ String dirName = ""; //$NON-NLS-1$
+
+ boolean success = true;
+
+ if (directory.exists())
+ {
+ if (directory.isDirectory())
+ {
+ dirName = directory.getName();
+ File[] children = directory.listFiles();
+
+ for (File element : children)
+ {
+ if (element.isFile())
+ {
+ success = success && element.delete();
+ }
+ else
+ {
+ success = success && deleteDirRecursively(element);
+ }
+ }
+
+ success = success && directory.delete();
+ }
+ else
+ {
+ String errorMessage = directory.getName() + " is not a diretory."; //$NON-NLS-1$
+ StudioLogger.error(errorMessage);
+ throw new IOException(errorMessage);
+ }
+ }
+ else
+ {
+ String errorMessage = "The directory does not exist."; //$NON-NLS-1$
+ StudioLogger.error(errorMessage);
+ success = false;
+ throw new IOException(errorMessage);
+ }
+
+ if ((success) && (!dirName.equals(""))) //$NON-NLS-1$
+ {
+ StudioLogger.info("The directory " + dirName + "was successfully deleted."); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+
+ return success;
+ }
+
+ /**
+ * Delete a single file from the filesystem.
+ *
+ * @param fileToDelete
+ * A <code>File</code> object representing the file to be
+ * deleted.
+ * @throws IOException
+ * if any problem occurs deleting the file.
+ */
+ public static void deleteFile(File fileToDelete) throws IOException
+ {
+ if ((fileToDelete != null) && fileToDelete.exists() && fileToDelete.isFile()
+ && fileToDelete.canWrite())
+ {
+ fileToDelete.delete();
+ StudioLogger.info("The file " + fileToDelete.getName() + "was successfully deleted."); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+ else
+ {
+ String errorMessage = ""; //$NON-NLS-1$
+ if (fileToDelete == null)
+ {
+ errorMessage = "Null pointer for file to delete."; //$NON-NLS-1$
+ }
+ else
+ {
+ if (!fileToDelete.exists())
+ {
+ errorMessage = "The file " + fileToDelete.getName() + " does not exist."; //$NON-NLS-1$ //$NON-NLS-2$
+ }
+ else
+ {
+ if (!fileToDelete.isFile())
+ {
+ errorMessage = fileToDelete.getName() + " is not a file."; //$NON-NLS-1$
+ }
+ else
+ {
+ if (!fileToDelete.canWrite())
+ {
+ errorMessage = "Cannot write to " + fileToDelete.getName(); //$NON-NLS-1$
+ }
+ }
+ }
+
+ }
+
+ StudioLogger.error(errorMessage);
+ throw new IOException("Cannot delete file: " + errorMessage); //$NON-NLS-1$
+ }
+ }
+
+ /**
+ * Delete a list of files from the filesystem.
+ *
+ * @param filesToDelete
+ * A list of <code>File</code> objects representing the files
+ * to be deleted.
+ * @throws IOException
+ * if any problem occurs deleting the files.
+ */
+ public static void deleteFilesOnList(List<File> filesToDelete) throws IOException
+ {
+ for (File element : filesToDelete)
+ {
+ if (element.exists())
+ {
+ deleteFile((element));
+ }
+ }
+ }
+
+ /**
+ * Return the File Size in Bytes.
+ *
+ * @param root The root File, it can be a directory
+ * @return The size of the file in bytes
+ * @throws IOException
+ */
+ public static int getFileSize(File root) throws IOException
+ {
+ int size = 0;
+ if (root.isDirectory())
+ {
+ for (File child : root.listFiles())
+ {
+ size += FileUtil.getFileSize(child);
+ }
+ }
+ else if (root.isFile())
+ {
+ FileInputStream fis = new FileInputStream(root);
+ int available;
+ try
+ {
+ available = fis.available();
+ }
+ finally
+ {
+ try
+ {
+ fis.close();
+ }
+ catch (IOException e)
+ {
+ //Do thing.
+ }
+ }
+ size = available;
+ }
+ return size;
+ }
+
+ /**
+ * getExtension(String fileName)
+ *
+ * @param fileName
+ * returns the extension of a given file. "extension" here means
+ * the final part of the string after the last dot.
+ *
+ * @return String containing the extension
+ */
+ public static String getExtension(String fileName)
+ {
+ if (fileName != null)
+ {
+ int i = fileName.lastIndexOf(".") + 1; //$NON-NLS-1$
+ return (i == 0) ? "" : fileName.substring(i); //$NON-NLS-1$
+ }
+ else
+ {
+ StudioLogger.error("The file " + fileName + " does not exist."); //$NON-NLS-1$ //$NON-NLS-2$
+ return null;
+ }
+ }
+
+ /**
+ * Get the list of all File objects that compose the path to the given File
+ * object
+ *
+ * @param aFile
+ * the file whose path must be retrieved.
+ * @return a List with all the File objects that compose the path to the
+ * given File object.
+ */
+ public static List<File> getFilesComposingPath(File aFile)
+ {
+ List<File> fileList;
+
+ if (aFile == null)
+ {
+ fileList = new ArrayList<File>();
+ }
+ else
+ {
+ fileList = getFilesComposingPath(aFile.getParentFile());
+ fileList.add(aFile);
+ }
+
+ return fileList;
+ }
+
+ /**
+ * Retrieve the relative filename to access a targetFile from a homeFile
+ * parent directory. Notice that to actualy use a relative File object you
+ * must use the following new File(homeDir, relativeFilename) because using
+ * only new File(relativeFilename) would give you a file whose directory is
+ * the one set in the "user.dir" property.
+ *
+ * @param homeDir
+ * the directory from where you want to access the targetFile
+ * @param targetFile
+ * the absolute file or dir that you want to access via relative
+ * filename from the homeFile
+ * @return the relative filename that describes the location of the
+ * targetFile referenced from the homeFile dir
+ * @throws IOException
+ */
+ public static String getRelativeFilename(File homeDir, File targetFile) throws IOException
+ {
+ StringBuffer relativePath = new StringBuffer();
+
+ List<File> homeDirList = getFilesComposingPath(getCanonicalFile(homeDir));
+ List<File> targetDirList = getFilesComposingPath(getCanonicalFile(targetFile));
+
+ if (homeDirList.size() == 0)
+ {
+ StudioLogger.info("Home Dir has no parent."); //$NON-NLS-1$
+ }
+
+ if (targetDirList.size() == 0)
+ {
+ StudioLogger.info("Target Dir has no parent."); //$NON-NLS-1$
+ }
+
+ // get the index of the last common directory between sourceFile and
+ // targetFile
+ int commonIndex = -1;
+
+ for (int i = 0; (i < homeDirList.size()) && (i < targetDirList.size()); i++)
+ {
+ File aHomeDir = homeDirList.get(i);
+ File aTargetDir = targetDirList.get(i);
+
+ if (aHomeDir.equals(aTargetDir))
+ {
+ commonIndex = i;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ // return from all remaining directories of the homeFile
+ for (int i = commonIndex + 1; i < homeDirList.size(); i++)
+ {
+ relativePath.append(".."); //$NON-NLS-1$
+ relativePath.append(File.separatorChar);
+ }
+
+ // enter into all directories of the target file
+ // stops when reachs the file name and extension
+ for (int i = commonIndex + 1; i < targetDirList.size(); i++)
+ {
+ File targetDir = targetDirList.get(i);
+ relativePath.append(targetDir.getName());
+
+ if (i != (targetDirList.size() - 1))
+ {
+ relativePath.append(File.separatorChar);
+ }
+ }
+
+ return relativePath.toString();
+ }
+
+ /**
+ * Return a list of file absolute paths under "baseDir" and under its subdirectories,
+ * recursively.
+ *
+ * @param baseDirToList
+ * A string that represents the BaseDir to initial search.
+ * @return A List of filepaths of files under the "baseDir".
+ * @throws IOException
+ * If the "baseDir" can not be read.
+ */
+ public static List<String> listFilesRecursively(String baseDirToList) throws IOException
+ {
+ File baseDirToListFiles = new File(baseDirToList);
+ List<String> listOfFiles = listFilesRecursively(baseDirToListFiles);
+
+ return listOfFiles;
+ }
+
+ /**
+ * Return a list of file absolute paths under "baseDir" and under its subdirectories,
+ * recursively.
+ *
+ * @param baseDirToList
+ * A file object that represents the "baseDir".
+ * @return A List of filepaths of files under the "baseDir".
+ * @throws IOException
+ * If the "baseDir" can not be read.
+ */
+ public static List<String> listFilesRecursively(File baseDirToList) throws IOException
+ {
+ List<String> listOfFiles = new ArrayList<String>();
+
+ if (baseDirToList.exists() && baseDirToList.isDirectory() && baseDirToList.canRead())
+ {
+ File[] children = baseDirToList.listFiles();
+
+ for (File child : children)
+ {
+ if (child.isFile())
+ {
+ listOfFiles.add(child.getAbsolutePath());
+ }
+ else
+ {
+ List<String> temporaryList = listFilesRecursively(child);
+ listOfFiles.addAll(temporaryList);
+ }
+ }
+ }
+ else
+ {
+ String errorMessage = ""; //$NON-NLS-1$
+ if (!baseDirToList.exists())
+ {
+ errorMessage = "The base dir does not exist."; //$NON-NLS-1$
+ }
+ else
+ {
+ if (!baseDirToList.isDirectory())
+ {
+ errorMessage = baseDirToList.getName() + "is not a directory."; //$NON-NLS-1$
+ }
+ else
+ {
+ if (!baseDirToList.canRead())
+ {
+ errorMessage = "Cannot fread from " + baseDirToList.getName() + "."; //$NON-NLS-1$ //$NON-NLS-2$
+ }
+ }
+ }
+
+ StudioLogger.error(errorMessage);
+ throw new IOException("Error listing files: " + errorMessage); //$NON-NLS-1$
+ }
+
+ return listOfFiles;
+ }
+
+ /**
+ * Calculate the canonical (an absolute filename without "\.\" and "\..\")
+ * that describe the file described by the absoluteFilename.
+ * @param absoluteFilename a file name that describe the full path of the file to use.
+ * @return the canonical File objecta
+ */
+ public static File getCanonicalFile(String absoluteFilename)
+ {
+ return getCanonicalFile(new File(absoluteFilename));
+ }
+
+ /**
+ * Calculate the canonical (an absolute filename without "\.\" and "\..\")
+ * that describe the file described by the given location and filename.
+ * @param location the directory of the file to be used
+ * @param filename (or a relative filename) of the file to be used
+ * @return the canonical File objecta
+ */
+ public static File getCanonicalFile(File location, String filename)
+ {
+ return getCanonicalFile(new File(location, filename));
+ }
+
+ /**
+ * Calculate the canonical (an absolute filename without "\.\" and "\..\")
+ * that describe the given file.
+ * @param aFile the file whose cannonical path will be calculated
+ * @return the canonical File objecta
+ */
+ public static File getCanonicalFile(File aFile)
+ {
+ File f = null;
+
+ try
+ {
+ f = aFile.getCanonicalFile();
+ }
+ catch (IOException e)
+ {
+ // this should never happens
+ StudioLogger.error(FileUtil.class, "FileUtil.getCanonicalFile: IOException e", e); //$NON-NLS-1$
+
+ // since it's not possible to read from filesystem, return a File using String
+ String filename = aFile.getAbsolutePath();
+
+ StringTokenizer st = new StringTokenizer(filename, File.separator);
+
+ StringBuffer sb = new StringBuffer();
+
+ while (st.hasMoreTokens())
+ {
+ String token = (String) st.nextElement();
+
+ if (token.equals("..")) //$NON-NLS-1$
+ {
+ int lastDirIndex = sb.lastIndexOf(File.separator);
+
+ // do not go back currently on the root directory
+ if (lastDirIndex > 2)
+ {
+ sb.delete(lastDirIndex, sb.length());
+ }
+ }
+ else if (!token.equals(".")) //$NON-NLS-1$
+ {
+ if (sb.length() > 0)
+ {
+ sb.append(File.separator);
+ }
+
+ sb.append(token);
+
+ if (token.endsWith(":")) //$NON-NLS-1$
+ {
+ sb.append(File.separator);
+ }
+ }
+ }
+
+ f = new File(sb.toString());
+ }
+
+ return f;
+ }
+
+ /**
+ * Returns which is the OS.
+ * @return
+ * a code corresponding to the proper OS
+ */
+ public static int getOS()
+ {
+ int result = -1;
+
+ String osName = System.getProperty("os.name").toLowerCase(); //$NON-NLS-1$
+ if (osName.indexOf("linux") > -1) //$NON-NLS-1$
+ {
+ result = OS_LINUX;
+ }
+ else if (osName.indexOf("windows") > -1) //$NON-NLS-1$
+ {
+ result = OS_WINDOWS;
+ }
+
+ return result;
+ }
+
+ /**
+ * Returns true if the OS is windows
+ * @return true if the OS is windows
+ */
+ public static boolean isWindows()
+ {
+ return getOS() == OS_WINDOWS;
+ }
+
+ /**
+ * Opens the stream;
+ *
+ * @param stream File Stream
+ *
+ * @return StringBuffer with the file content
+ *
+ * @throws IOException
+ */
+ public static StringBuffer openFile(InputStream stream) throws IOException
+ {
+ InputStreamReader streamReader = null;
+ StringBuffer fileBuffer = new StringBuffer();
+ BufferedReader reader = null;
+ try
+ {
+ streamReader = new InputStreamReader(stream);
+ reader = new BufferedReader(streamReader);
+ char[] buffer = new char[1024];
+ int line = reader.read(buffer);
+
+ while (line > 0)
+ {
+ fileBuffer.append(buffer, 0, line);
+ line = reader.read(buffer);
+ }
+ }
+ finally
+ {
+ if (streamReader != null)
+ {
+ try
+ {
+ streamReader.close();
+ }
+ catch (Exception e)
+ {
+ //Do nothing.
+ }
+ }
+ if (reader != null)
+ {
+ try
+ {
+ reader.close();
+ }
+ catch (Exception e)
+ {
+ //Do nothing.
+ }
+ }
+ }
+
+ return fileBuffer;
+ }
+
+ /**
+ * Reads a file into a string array
+ *
+ * @param filename The file name
+ * @return The file contents as a string array
+ * @throws IOException
+ */
+ public static String[] readFileAsArray(String filename) throws IOException
+ {
+ LinkedList<String> file = new LinkedList<String>();
+ String[] lines = new String[0];
+ String line;
+ FileReader reader = null;
+ LineNumberReader lineReader = null;
+
+ try
+ {
+ reader = new FileReader(filename);
+ lineReader = new LineNumberReader(reader);
+
+ while ((line = lineReader.readLine()) != null)
+ {
+ file.add(line);
+ }
+
+ lines = new String[file.size()];
+ lines = file.toArray(lines);
+ }
+ finally
+ {
+ try
+ {
+ lineReader.close();
+ reader.close();
+ }
+ catch (Exception e)
+ {
+ // Do nothing
+ }
+ }
+
+ return lines;
+ }
+
+ /**
+ * Reads a file on workspace and returns an IDocument object with its content
+ *
+ * @param file The file to read
+ * @return The IDocument object containing the file contents
+ *
+ * @throws CoreException
+ */
+ public static IDocument readFile(IFile file) throws CoreException
+ {
+ if (!canRead(file))
+ {
+ String errMsg = NLS.bind(UtilitiesNLS.EXC_FileUtil_TheFileCannotBeRead, file.getName());
+ IStatus status = new Status(IStatus.ERROR, CommonPlugin.PLUGIN_ID, errMsg);
+
+ throw new CoreException(status);
+ }
+
+ TextFileDocumentProvider documentProvider = new TextFileDocumentProvider();
+ IDocument document = new Document();
+
+ documentProvider.connect(file);
+ document = documentProvider.getDocument(file);
+ documentProvider.disconnect(file);
+
+ return document;
+ }
+
+ /**
+ * Saves the content of an IDocument object to a file on workspace
+ * @param file The file
+ * @param document The IDocument object
+ * @param encoding The file encoding
+ * @param overwrite If the file can be overwritten
+ * @throws CoreException
+ */
+ public static void saveFile(IFile file, IDocument document, String encoding, boolean overwrite)
+ throws CoreException
+ {
+ if (file.exists() && !overwrite)
+ {
+ String errMsg =
+ NLS.bind(UtilitiesNLS.EXC_FileUtil_CannotOverwriteTheFile, file.getName());
+ IStatus status = new Status(IStatus.ERROR, CommonPlugin.PLUGIN_ID, errMsg);
+
+ throw new CoreException(status);
+ }
+
+ if (!canWrite(file))
+ {
+ String errMsg = NLS.bind(UtilitiesNLS.EXC_FileUtil_ErrorWritingTheFile, file.getName());
+ IStatus status = new Status(IStatus.ERROR, CommonPlugin.PLUGIN_ID, errMsg);
+
+ throw new CoreException(status);
+ }
+
+ ByteArrayInputStream bais = null;
+
+ try
+ {
+ bais = new ByteArrayInputStream(document.get().getBytes(encoding));
+ file.setCharset(encoding, new NullProgressMonitor());
+ file.setContents(bais, true, false, new NullProgressMonitor());
+ }
+ catch (UnsupportedEncodingException e1)
+ {
+ String errMsg =
+ NLS.bind(UtilitiesNLS.EXC_FileUtil_ErrorSettingTheFileEncoding, file.getName());
+ IStatus status = new Status(IStatus.ERROR, CommonPlugin.PLUGIN_ID, errMsg);
+
+ throw new CoreException(status);
+ }
+ finally
+ {
+ if (bais != null)
+ {
+ try
+ {
+ bais.close();
+ }
+ catch (IOException e)
+ {
+ // Do nothing.
+ }
+ }
+ }
+ }
+
+ /**
+ * Checks if a file can be read
+ *
+ * @param file The file to be checked
+ * @return true if the file can be read or false otherwise
+ */
+ public static boolean canRead(IFile file)
+ {
+ boolean canRead = true;
+ InputStream is = null;
+
+ try
+ {
+ if (file.exists())
+ {
+ file.refreshLocal(IResource.DEPTH_ZERO, new NullProgressMonitor());
+ is = file.getContents();
+ is.read();
+ }
+ }
+ catch (CoreException e)
+ {
+ canRead = false;
+ }
+ catch (IOException e)
+ {
+ canRead = false;
+ }
+ finally
+ {
+ if (is != null)
+ {
+ try
+ {
+ is.close();
+ }
+ catch (IOException e)
+ {
+ // do nothing
+ }
+ }
+ }
+
+ return canRead;
+ }
+
+ /**
+ * Checks if a file can be written
+ *
+ * @param file the file to be checked
+ * @return true if the file can be written or false otherwise
+ */
+ public static boolean canWrite(IFile file)
+ {
+ boolean canWrite = true;
+
+ if (file.exists() && canRead(file))
+ {
+ canWrite = !file.isReadOnly();
+ }
+ else
+ {
+ IFolder parent = (IFolder) file.getParent();
+
+ if (!parent.isAccessible())
+ {
+ canWrite = false;
+ }
+ else
+ {
+ try
+ {
+ if (parent.members() == null)
+ {
+ canWrite = false;
+ }
+ else
+ {
+ NullProgressMonitor nullProgressMonitor = new NullProgressMonitor();
+ file.create(null, true, nullProgressMonitor);
+ file.refreshLocal(IResource.DEPTH_ZERO, nullProgressMonitor);
+ file.delete(true, nullProgressMonitor);
+ }
+ }
+ catch (CoreException e)
+ {
+ canWrite = false;
+ }
+ }
+ }
+
+ return canWrite;
+ }
+
+ /**
+ * Checks if a File object can be read
+ *
+ * @param file the File object
+ *
+ * @return true if the File object can be read or false otherwise
+ */
+ public static boolean canRead(File file)
+ {
+ boolean canRead = false;
+
+ if ((file != null) && file.exists())
+ {
+ FileInputStream fis = null;
+
+ try
+ {
+ if (file.isFile())
+ {
+ fis = new FileInputStream(file);
+ fis.read();
+ canRead = true;
+ }
+ else
+ {
+ String[] children = file.list();
+
+ if (children != null)
+ {
+ canRead = true;
+ }
+ }
+ }
+ catch (Exception e)
+ {
+ // Do nothing. canRead is false already
+ }
+ finally
+ {
+ try
+ {
+ if (fis != null)
+ {
+ fis.close();
+ }
+ }
+ catch (IOException e)
+ {
+ // Do nothing
+ }
+ }
+ }
+
+ return canRead;
+ }
+
+ /**
+ * Checks if a File object can be written
+ *
+ * @param file the File object
+ *
+ * @return true if the File object can be written or false otherwise
+ */
+ public static boolean canWrite(File file)
+ {
+ boolean canWrite = false;
+
+ if (file != null)
+ {
+ FileOutputStream fos = null;
+
+ try
+ {
+ if (!file.exists())
+ {
+ canWrite = file.createNewFile();
+
+ if (canWrite)
+ {
+ file.delete();
+ }
+ }
+ else if (file.isDirectory())
+ {
+ File tempFile = File.createTempFile("StudioForAndroidFSChecking", null, file); //$NON-NLS-1$
+
+ if (tempFile.exists())
+ {
+ canWrite = true;
+ tempFile.delete();
+ }
+ }
+ else if (file.isFile())
+ {
+ fos = new FileOutputStream(file);
+ fos.getFD();
+ canWrite = true;
+ }
+ }
+ catch (Exception e)
+ {
+ // Do nothing. canWrite is false already
+ }
+ finally
+ {
+ if (fos != null)
+ {
+ try
+ {
+ fos.close();
+ }
+ catch (IOException e)
+ {
+ // Do nothing
+ }
+ }
+ }
+ }
+
+ return canWrite;
+ }
+
+ /**
+ * Unpack a zip file.
+ *
+ * @param file the file
+ * @param destination the destination path or null to unpack at the same directory of file
+ * @return true if unpacked, false otherwise
+ */
+ public static boolean unpackZipFile(File file, String destination, IProgressMonitor monitor)
+ {
+ SubMonitor subMonitor = SubMonitor.convert(monitor);
+ ZipFile zipFile = null;
+
+ String extractDestination = destination != null ? destination : file.getParent();
+ if (!extractDestination.endsWith(File.separator))
+ {
+ extractDestination += File.separator;
+ }
+
+ boolean unziped = true;
+ try
+ {
+ zipFile = new ZipFile(file);
+ }
+ catch (Throwable e)
+ {
+ unziped = false;
+ StudioLogger.error(FileUtil.class, "Error extracting file: " + file.getAbsolutePath() //$NON-NLS-1$
+ + " to " + extractDestination, e); //$NON-NLS-1$
+
+ }
+ if (zipFile != null)
+ {
+ Enumeration<? extends ZipEntry> entries = zipFile.entries();
+
+ subMonitor.beginTask("Extracting files", Collections.list(entries).size()); //$NON-NLS-1$
+ entries = zipFile.entries();
+ InputStream input = null;
+ OutputStream output = null;
+ while (entries.hasMoreElements())
+ {
+ try
+ {
+ ZipEntry entry = entries.nextElement();
+ File newFile = new File(extractDestination + entry.getName());
+ if (entry.isDirectory())
+ {
+ newFile.mkdirs();
+ }
+ else
+ {
+ newFile.getParentFile().mkdirs();
+ if (newFile.createNewFile())
+ {
+ input = zipFile.getInputStream(entry);
+ output = new BufferedOutputStream(new FileOutputStream(newFile));
+ copyStreams(input, output);
+ }
+ }
+ }
+ catch (Throwable t)
+ {
+ unziped = false;
+ StudioLogger.error(FileUtil.class,
+ "Error extracting file: " + file.getAbsolutePath() + " to " //$NON-NLS-1$ //$NON-NLS-2$
+ + extractDestination, t);
+ }
+ finally
+ {
+ try
+ {
+ if (input != null)
+ {
+ input.close();
+ }
+ if (output != null)
+ {
+ output.close();
+ }
+ }
+ catch (Throwable t)
+ {
+ //do nothing
+ }
+ subMonitor.worked(1);
+ }
+ }
+ }
+ return unziped;
+ }
+
+ public static boolean extractZipArchive(File file, File destination,
+ List<String> selectedEntries, IProgressMonitor monitor) throws IOException
+ {
+ SubMonitor subMonitor = SubMonitor.convert(monitor);
+ ZipFile zipFile = null;
+ CRC32 crc = new CRC32();
+ byte[] buf = new byte[BUFFER_SIZE];
+
+ File extractDestination = destination != null ? destination : file.getParentFile();
+
+ if (!extractDestination.exists())
+ {
+ extractDestination.mkdirs();
+ }
+
+ boolean unziped = true;
+ try
+ {
+ zipFile = new ZipFile(file);
+ }
+ catch (Throwable e)
+ {
+ unziped = false;
+ StudioLogger.error(FileUtil.class, "Error extracting file: " + file.getAbsolutePath() //$NON-NLS-1$
+ + " to " + extractDestination, e); //$NON-NLS-1$
+
+ }
+ if (zipFile != null)
+ {
+ Enumeration<? extends ZipEntry> entries = zipFile.entries();
+
+ subMonitor.beginTask("Extracting files", Collections.list(entries).size()); //$NON-NLS-1$
+ entries = zipFile.entries();
+ InputStream input = null;
+ FileOutputStream output = null;
+ int diagReturn = IDialogConstants.YES_ID;
+ while (entries.hasMoreElements())
+ {
+ crc.reset();
+ try
+ {
+ ZipEntry entry = entries.nextElement();
+ if (selectedEntries.contains(entry.getName()))
+ {
+ File newFile = new File(extractDestination, entry.getName());
+ if ((diagReturn != IDialogConstants.YES_TO_ALL_ID) && newFile.exists())
+ {
+ diagReturn =
+ EclipseUtils.showQuestionYesAllCancelDialog(
+ UtilitiesNLS.FileUtil_File_Exists_Title, NLS.bind(
+ UtilitiesNLS.FileUtil_File_Exists_Message,
+ newFile.getAbsolutePath()));
+ }
+
+ if ((diagReturn == IDialogConstants.YES_ID)
+ || (diagReturn == IDialogConstants.YES_TO_ALL_ID))
+ {
+ newFile.delete();
+ if (entry.isDirectory())
+ {
+ newFile.mkdirs();
+ }
+ else
+ {
+ newFile.getParentFile().mkdirs();
+ if (newFile.createNewFile())
+ {
+ input = zipFile.getInputStream(entry);
+ output = new FileOutputStream(newFile);
+ int length = 0;
+ while ((length = input.read(buf, 0, BUFFER_SIZE)) > 1)
+ {
+ output.write(buf, 0, length);
+ crc.update(buf, 0, length);
+ }
+
+ if (crc.getValue() != entry.getCrc())
+ {
+ throw new IOException();
+ }
+
+ }
+ }
+ }
+ else
+ {
+ diagReturn = IDialogConstants.YES_ID; //Attempt to extract next entry
+ }
+ }
+ }
+ catch (IOException e)
+ {
+ unziped = false;
+ StudioLogger.error(FileUtil.class,
+ "Error extracting file: " + file.getAbsolutePath() + " to " //$NON-NLS-1$ //$NON-NLS-2$
+ + extractDestination, e);
+ throw e;
+ }
+ catch (Throwable t)
+ {
+ unziped = false;
+ StudioLogger.error(FileUtil.class,
+ "Error extracting file: " + file.getAbsolutePath() + " to " //$NON-NLS-1$ //$NON-NLS-2$
+ + extractDestination, t);
+ }
+ finally
+ {
+ try
+ {
+ if (input != null)
+ {
+ input.close();
+ }
+ if (output != null)
+ {
+ output.close();
+ }
+ }
+ catch (Throwable t)
+ {
+ //do nothing
+ }
+ subMonitor.worked(1);
+ }
+ }
+ }
+ return unziped;
+
+ }
+
+ /**
+ * Unpack a tar file.
+ *
+ * @param file the file
+ * @param destination the destination path or null to unpack at the same directory of file
+ * @return true if unpacked, false otherwise
+ */
+ public static boolean unpackTarFile(File artifactFile, String destination)
+ {
+ boolean unpacked = true;
+
+ String extractDestination = destination != null ? destination : artifactFile.getParent();
+ if (!extractDestination.endsWith(File.separator))
+ {
+ extractDestination += File.separator;
+ }
+
+ List<String> commandList = new LinkedList<String>();
+ commandList.add("tar"); //$NON-NLS-1$
+
+ String fileName = artifactFile.getName();
+
+ //tar.gz or tgz
+ if (fileName.endsWith("gz")) //$NON-NLS-1$
+ {
+ commandList.add("xzf"); //$NON-NLS-1$
+ }
+ //tar.bz2
+ else if (fileName.endsWith("bz2")) //$NON-NLS-1$
+ {
+ commandList.add("xjf"); //$NON-NLS-1$
+ }
+ //tar
+ else if (fileName.endsWith("tar")) //$NON-NLS-1$
+ {
+ commandList.add("xf"); //$NON-NLS-1$
+ }
+ else
+ {
+ unpacked = false;
+ }
+
+ if (unpacked)
+ {
+ commandList.add(artifactFile.getAbsolutePath());
+ File target = new File(extractDestination);
+ if (target.exists() && target.isDirectory() && target.canWrite())
+ {
+ try
+ {
+ Process p =
+ Runtime.getRuntime().exec(commandList.toArray(new String[0]), null,
+ target);
+ try
+ {
+ p.waitFor();
+ }
+ catch (InterruptedException e)
+ {
+ //do nothing
+ }
+ if (p.exitValue() != 0)
+ {
+ unpacked = false;
+
+ }
+ }
+ catch (IOException e)
+ {
+ unpacked = false;
+ }
+ }
+ }
+
+ return unpacked;
+ }
+
+ /**
+ * Copy the input stream to the output stream
+ * @param inputStream
+ * @param outputStream
+ * @throws IOException
+ */
+ public static void copyStreams(InputStream inputStream, OutputStream outputStream)
+ throws IOException
+ {
+ byte[] buffer = new byte[1024];
+ int length;
+
+ while ((length = inputStream.read(buffer)) >= 0)
+ {
+ outputStream.write(buffer, 0, length);
+ }
+ }
+
+ /**
+ * Add a directory to a Project
+ * @param project
+ * @param parentFolder
+ * @param folderName
+ * @param monitor
+ * @throws CoreException
+ */
+ public static void createProjectFolder(IProject project, String parentFolder,
+ String folderName, IProgressMonitor monitor) throws CoreException
+ {
+ monitor.beginTask(UtilitiesNLS.UI_Project_Creating_Folder_Task, 100);
+
+ try
+ {
+ monitor.setTaskName(UtilitiesNLS.UI_Project_Verifying_Folder_Task);
+ if (folderName.length() > 0)
+ {
+ monitor.worked(10);
+ IFolder folder = project.getFolder(parentFolder + folderName);
+ monitor.worked(10);
+ if (!folder.exists())
+ {
+ monitor.worked(10);
+ if (FileUtil.canWrite(folder.getLocation().toFile()))
+ {
+ monitor.worked(10);
+ monitor.setTaskName(UtilitiesNLS.UI_Project_Creating_Folder_Task);
+ folder.create(true, true, new SubProgressMonitor(monitor, 60));
+ }
+ else
+ {
+ String errMsg =
+ NLS.bind(
+ UtilitiesNLS.EXC_Project_CannotCreateFolderReadOnlyWorkspace,
+ folder.getLocation().toFile().toString());
+ IStatus status = new Status(IStatus.ERROR, CommonPlugin.PLUGIN_ID, errMsg);
+ throw new CoreException(status);
+ }
+ }
+ }
+ }
+ finally
+ {
+ monitor.done();
+ }
+ }
+
+ /**
+ * Given a directory descriptor represented by a {@link File}, creates it
+ * only if it does not exist. In case it does, try to create another
+ * one with its name plus "-1". If it does exists, try to create it with
+ * its name plus "+2", and so on...
+ * <br>
+ * Note that the directory is not fisically created. To do so, on must
+ * use the method {@link File#mkdir()}.
+ *
+ * @param directory Directory to be created.
+ *
+ * @return Returns the created directory as a {@link File}.
+ */
+ public static File createUniqueDirectoryDescriptor(File directory)
+ {
+ if (directory.exists())
+ {
+ boolean exists = true;
+ int counter = 1;
+ String rootPath = directory.getAbsolutePath();
+ while (exists)
+ {
+ directory = new File(rootPath + "-" + counter); //$NON-NLS-1$
+ exists = directory.exists();
+ counter++;
+ }
+ }
+
+ return directory;
+ }
+
+ /**
+ * Return path with special characters escaped.
+ * Special characters are system dependent, there is a set for linux and another for mac.
+ * If {@code operationalSystem} is windows, the path is returned unchanged.
+ *
+ * @param path to be escaped.
+ * @param operatingSystem the target operation system that the path will be used.
+ * @return path with special characters escaped.
+ * */
+ public static String getEscapedPath(String path, String operatingSystem)
+ {
+ char[] specialCharSet = null;
+
+ if (operatingSystem.equals(Platform.OS_LINUX))
+ {
+ specialCharSet = LINUX_SPECIAL_CHAR;
+ }
+ else if (operatingSystem.equals(Platform.OS_MACOSX))
+ {
+ specialCharSet = MAC_SPECIAL_CHAR;
+ }
+
+ if ((path != null) && (specialCharSet != null))
+ {
+ for (char c : specialCharSet)
+ {
+ CharSequence target = String.valueOf(c);
+ CharSequence replacement = new String("\\" + String.valueOf(c)); //$NON-NLS-1$
+ path = path.replace(target, replacement);
+ }
+ }
+
+ return path;
+ }
+
+ /**
+ * Return path with special characters escaped.
+ * Special characters are system dependent, there is a set for linux and another for mac.
+ * If the system is windows, returns the path unchanged.
+ *
+ * @param path to be escaped
+ * @return path with special characters escaped.
+ * */
+ public static String getEscapedPath(String path)
+ {
+ return getEscapedPath(path, Platform.getOS());
+ }
+
+ /**
+ * Return path with special characters unescaped.
+ * Special characters are system dependent, there is a set for linux and another for mac.
+ * If the system is windows, returns the path unchanged.
+ *
+ * @param path to be unescaped
+ * @return path with special characters unescaped.
+ * */
+ public static String getUnescapedPath(String path)
+ {
+ char[] specialCharSet = null;
+
+ if (Platform.getOS().equals(Platform.OS_LINUX))
+ {
+ specialCharSet = LINUX_SPECIAL_CHAR;
+ }
+ else if (Platform.getOS().equals(Platform.OS_MACOSX))
+ {
+ specialCharSet = MAC_SPECIAL_CHAR;
+ }
+
+ if ((path != null) && (specialCharSet != null))
+ {
+ for (char c : specialCharSet)
+ {
+ CharSequence target = new String("\\") + String.valueOf(c); //$NON-NLS-1$
+ CharSequence replacement = String.valueOf(c);
+ path = path.replace(target, replacement);
+ }
+ }
+
+ return path;
+ }
+
+ public static String removeUnescapedQuotes(String path, String quoteReplacement)
+ {
+ //remove quotes and double quotes
+ char quotes[] =
+ {
+ '\'', '"'
+ };
+
+ boolean escaped = false;
+
+ for (int i = 0; i < path.length(); i++)
+ {
+ if (escaped == false)
+ {
+ if (path.charAt(i) == ESCAPE_CHAR)
+ {
+ escaped = true;
+ }
+ else
+ {
+ for (char quote : quotes)
+ {
+ if (path.charAt(i) == quote)
+ {
+ //split the string in two parts:
+ // - part1: before the quote
+ String part1 = path.substring(0, i);
+ // - part2: after the quote
+ String part2 = path.substring(i + 1, path.length());
+
+ //concatenate part1 and part2 with quoteReplacement in-between
+ //if quoteReplacement is the empty string (""), then part1 and part2 are juxtaposed
+ path = part1.concat(quoteReplacement).concat(part2);
+ }
+ }
+ }
+ }
+ else
+ {
+ //current character is escaped, next character can't be escaped
+ escaped = false;
+ }
+ }
+ return path;
+ }
+
+ /**
+ * Unescape characters and remove quotes and double quotes.
+ * Special characters are system dependent, there is a set for linux and another for mac.
+ * If the system is windows, returns the path unchanged.
+ *
+ * @param path to be cleaned.
+ * @param quoteReplacement string that will replace quotes and double quotes.
+ * @return path without quotes, double quotes and special characters unescaped.
+ * */
+ public static String getCleanPath(String path, String quoteReplacement)
+ {
+
+ path = removeUnescapedQuotes(path, quoteReplacement);
+ path = getUnescapedPath(path);
+
+ return path;
+ }
+
+ public static String calculateMd5Sum(File file) throws IOException
+ {
+ String md5Sum = null;
+
+ BigInteger hash = null;
+ FileInputStream fis;
+ fis = new FileInputStream(file);
+ byte[] buf = new byte[1500000];
+
+ try
+ {
+ MessageDigest digest = java.security.MessageDigest.getInstance("MD5"); //$NON-NLS-1$
+ int bytesRead = 0;
+ while ((bytesRead = fis.read(buf)) > 0)
+ {
+ digest.update(buf, 0, bytesRead);
+ }
+
+ hash = new BigInteger(1, digest.digest());
+ md5Sum = hash.toString(16);
+ }
+ catch (NoSuchAlgorithmException e)
+ {
+ // This exception should not happen, because we are using a valid
+ // hard
+ // coded value for the algorithm name. However, if it happens, log
+ // it.
+ warn("MOTODEV Studio could not find an instance of the MessageDigest for the MD5 algorithm"); //$NON-NLS-1$
+ throw new IOException(UtilitiesNLS.FileUtil_Get_MD5_Algorithm_Failed);
+ }
+ finally
+ {
+ if (fis != null)
+ {
+ fis.close();
+ }
+ }
+
+ if (md5Sum == null)
+ {
+ throw new IOException(NLS.bind(UtilitiesNLS.FileUtil_MD5_Calculation_Failed,
+ file.getAbsolutePath()));
+ }
+
+ return md5Sum;
+ }
+
+ /**
+ * This method is responsible to copy informed source file to informed
+ * target.
+ *
+ * @param sourceFile
+ * @param targetFile
+ * @throws IOException
+ */
+ public static void copy(File sourceFile, File targetFile) throws IOException
+ {
+ OutputStream outputStream = new FileOutputStream(targetFile);
+ InputStream inputStream = new FileInputStream(sourceFile);
+ try
+ {
+ int length;
+ byte[] buffer = new byte[FileUtil.BUFFER_SIZE];
+ while ((length = inputStream.read(buffer)) >= 0)
+ {
+ outputStream.write(buffer, 0, length);
+ }
+ }
+ catch (IOException e)
+ {
+ throw new IOException("Error copying file:" + sourceFile.getAbsolutePath() + //$NON-NLS-1$
+ " to " + targetFile.getAbsolutePath()); //$NON-NLS-1$
+ }
+ finally
+ {
+ outputStream.close();
+ inputStream.close();
+ }
+ }
+
+ /**
+ * This method normalize a directory path.
+ *
+ * @param folder Full path to a directory
+ * @return The normalized path.
+ */
+ public static String normalizePath(String folder)
+ {
+ return folder.endsWith(File.separator) ? folder : folder + File.separator;
+ }
+
+ /**
+ * Delete the specified file, recursively as necessary.
+ *
+ * @param file The file to delete
+ */
+ public static void delete(File file)
+ {
+ if (file.exists())
+ {
+ if (file.isDirectory())
+ {
+ File[] files = file.listFiles();
+ for (int i = 0; i < files.length; i++)
+ {
+ delete(files[i]);
+ }
+ }
+ file.delete();
+ }
+ }
+
+ /**
+ * Delete the specified file, recursively as necessary.
+ *
+ * @param fileName The file to delete
+ */
+ public static void delete(String fileName)
+ {
+ delete(new File(fileName));
+ }
+
+ /**
+ * This method creates the specified directory.
+ *
+ * @param directory The directory to create.
+ * @throws IOException
+ */
+ public static void mkdir(String directory) throws IOException
+ {
+ File f = new File(directory);
+ if (f.exists())
+ {
+ if (f.isFile())
+ {
+ throw new IOException("Error creating directory:" + directory); //$NON-NLS-1$
+ }
+ }
+ else
+ {
+ if (!f.mkdirs())
+ {
+ throw new IOException("Error creating directory:" + directory); //$NON-NLS-1$
+ }
+ }
+ }
+
+}
diff --git a/src/plugins/common/src/com/motorola/studio/android/common/utilities/HttpUtils.java b/src/plugins/common/src/com/motorola/studio/android/common/utilities/HttpUtils.java
new file mode 100644
index 0000000..8fb7238
--- /dev/null
+++ b/src/plugins/common/src/com/motorola/studio/android/common/utilities/HttpUtils.java
@@ -0,0 +1,300 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+package com.motorola.studio.android.common.utilities;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.Authenticator;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.commons.httpclient.Credentials;
+import org.apache.commons.httpclient.HttpClient;
+import org.apache.commons.httpclient.HttpStatus;
+import org.apache.commons.httpclient.UsernamePasswordCredentials;
+import org.apache.commons.httpclient.auth.AuthScope;
+import org.apache.commons.httpclient.auth.AuthState;
+import org.apache.commons.httpclient.methods.GetMethod;
+import org.apache.commons.httpclient.params.HttpClientParams;
+import org.apache.commons.httpclient.params.HttpMethodParams;
+import org.eclipse.core.internal.net.ProxyManager;
+import org.eclipse.core.net.proxy.IProxyData;
+import org.eclipse.core.net.proxy.IProxyService;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.SubMonitor;
+import org.eclipse.ui.internal.net.auth.NetAuthenticator;
+
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.common.utilities.i18n.UtilitiesNLS;
+import com.motorola.studio.android.common.utilities.ui.LoginPasswordDialogCreator;
+
+/**
+ * Class for opening an input stream with the given URL.
+ */
+@SuppressWarnings("restriction")
+public class HttpUtils
+{
+ /**
+ * 1 second if the unit is milliseconds.
+ */
+ private static final int ONE_SECOND = 1000;
+
+ // map of credentials authentication so the user is not repeatedly asked for them
+ private static final Map<String, Credentials> authenticationRealmCache =
+ new HashMap<String, Credentials>();
+
+ private GetMethod getMethod;
+
+ /**
+ * Retrieves an open InputStream with the contents of the file pointed by the given url.
+ *
+ * @param url The address from where to retrieve the InputStream
+ * @param monitor The monitor to progress while accessing the file
+ *
+ * @return The open InputStream object, or <code>null</code> if no file was found
+ *
+ * @throws IOException if some error occurs with the network communication
+ */
+ public InputStream getInputStreamForUrl(String url, IProgressMonitor monitor)
+ throws IOException
+ {
+ return getInputStreamForUrl(url, monitor, true);
+ }
+
+ private InputStream getInputStreamForUrl(String url, IProgressMonitor monitor,
+ boolean returnStream) throws IOException
+ {
+ SubMonitor subMonitor = SubMonitor.convert(monitor);
+
+ subMonitor.beginTask(UtilitiesNLS.HttpUtils_MonitorTask_PreparingConnection, 300);
+
+ StudioLogger.debug(HttpUtils.class, "Verifying proxy usage for opening http connection"); //$NON-NLS-1$
+
+ // Try to retrieve proxy configuration to use if necessary
+ IProxyService proxyService = ProxyManager.getProxyManager();
+ IProxyData proxyData = null;
+ if (proxyService.isProxiesEnabled() || proxyService.isSystemProxiesEnabled())
+ {
+ Authenticator.setDefault(new NetAuthenticator());
+ if (url.startsWith("https"))
+ {
+ proxyData = proxyService.getProxyData(IProxyData.HTTPS_PROXY_TYPE);
+ StudioLogger.debug(HttpUtils.class, "Using https proxy"); //$NON-NLS-1$
+ }
+ else if (url.startsWith("http"))
+ {
+ proxyData = proxyService.getProxyData(IProxyData.HTTP_PROXY_TYPE);
+ StudioLogger.debug(HttpUtils.class, "Using http proxy"); //$NON-NLS-1$
+ }
+ else
+ {
+ StudioLogger.debug(HttpUtils.class, "Not using any proxy"); //$NON-NLS-1$
+ }
+ }
+
+ // Creates the http client and the method to be executed
+ HttpClient client = null;
+ client = new HttpClient();
+
+ // If there is proxy data, work with it
+ if (proxyData != null)
+ {
+ if (proxyData.getHost() != null)
+ {
+ // Sets proxy host and port, if any
+ client.getHostConfiguration().setProxy(proxyData.getHost(), proxyData.getPort());
+ }
+
+ if ((proxyData.getUserId() != null) && (proxyData.getUserId().trim().length() > 0))
+ {
+ // Sets proxy user and password, if any
+ Credentials cred =
+ new UsernamePasswordCredentials(proxyData.getUserId(),
+ proxyData.getPassword() == null ? "" : proxyData.getPassword()); //$NON-NLS-1$
+ client.getState().setProxyCredentials(AuthScope.ANY, cred);
+ }
+ }
+
+ InputStream streamForUrl = null;
+ getMethod = new GetMethod(url);
+ getMethod.setFollowRedirects(true);
+
+ // set a 30 seconds timeout
+ HttpMethodParams params = getMethod.getParams();
+ params.setSoTimeout(15 * ONE_SECOND);
+ getMethod.setParams(params);
+
+ boolean trying = true;
+ Credentials credentials = null;
+ subMonitor.worked(100);
+ subMonitor.setTaskName(UtilitiesNLS.HttpUtils_MonitorTask_ContactingSite);
+ do
+ {
+ StudioLogger.info(HttpUtils.class, "Attempting to make a connection"); //$NON-NLS-1$
+
+ // retry to connect to the site once, also set the timeout for 5 seconds
+ HttpClientParams clientParams = client.getParams();
+ clientParams.setIntParameter(HttpClientParams.MAX_REDIRECTS, 1);
+ clientParams.setSoTimeout(5 * ONE_SECOND);
+ client.setParams(clientParams);
+
+ client.executeMethod(getMethod);
+ if (subMonitor.isCanceled())
+ {
+ break;
+ }
+ else
+ {
+ AuthState authorizationState = getMethod.getHostAuthState();
+ String authenticationRealm = authorizationState.getRealm();
+
+ if (getMethod.getStatusCode() == HttpStatus.SC_UNAUTHORIZED)
+ {
+ StudioLogger.debug(HttpUtils.class,
+ "Client requested authentication; retrieving credentials"); //$NON-NLS-1$
+
+ credentials = authenticationRealmCache.get(authenticationRealm);
+
+ if (credentials == null)
+ {
+ StudioLogger.debug(HttpUtils.class,
+ "Credentials not found; prompting user for login/password"); //$NON-NLS-1$
+
+ subMonitor
+ .setTaskName(UtilitiesNLS.HttpUtils_MonitorTask_WaitingAuthentication);
+
+ LoginPasswordDialogCreator dialogCreator =
+ new LoginPasswordDialogCreator(url);
+ if (dialogCreator.openLoginPasswordDialog() == LoginPasswordDialogCreator.OK)
+ {
+
+ credentials =
+ new UsernamePasswordCredentials(dialogCreator.getTypedLogin(),
+ dialogCreator.getTypedPassword());
+ }
+ else
+ {
+ // cancel pressed; stop trying
+ trying = false;
+
+ // set the monitor canceled to be able to stop process
+ subMonitor.setCanceled(true);
+ }
+
+ }
+
+ if (credentials != null)
+ {
+ AuthScope scope = new AuthScope(null, -1, authenticationRealm);
+ client.getState().setCredentials(scope, credentials);
+ }
+
+ subMonitor.worked(100);
+ }
+ else if (getMethod.getStatusCode() == HttpStatus.SC_OK)
+ {
+ StudioLogger.debug(HttpUtils.class, "Http connection suceeded"); //$NON-NLS-1$
+
+ subMonitor
+ .setTaskName(UtilitiesNLS.HttpUtils_MonitorTask_RetrievingSiteContent);
+ if ((authenticationRealm != null) && (credentials != null))
+ {
+ authenticationRealmCache.put(authenticationRealm, credentials);
+ }
+ else
+ {
+ // no authentication was necessary, just work the monitor
+ subMonitor.worked(100);
+ }
+
+ StudioLogger.info(HttpUtils.class, "Retrieving site content"); //$NON-NLS-1$
+
+ // if the stream should not be returned (ex: only testing the connection is
+ // possible), then null will be returned
+ if (returnStream)
+ {
+ streamForUrl = getMethod.getResponseBodyAsStream();
+ }
+
+ // succeeded; stop trying
+ trying = false;
+
+ subMonitor.worked(100);
+ }
+ else
+ {
+ // unhandled return status code
+ trying = false;
+
+ subMonitor.worked(200);
+ }
+ }
+ }
+ while (trying);
+
+ subMonitor.done();
+
+ return streamForUrl;
+ }
+
+ /**
+ * Check if a connection with the given URL can be established.
+ *
+ * @param url The URL to test the connection.
+ *
+ * @return <code>true</code> if the connection can be established; <code>false</code> otherwise
+ */
+ public boolean isConnectionOk(String url)
+ {
+ try
+ {
+ getInputStreamForUrl(url, null, false);
+ // no need to release connection since the stream has not been retrieved
+ // if the code above does not throw any exception, the connection is fine
+ return true;
+ }
+ catch (Exception e)
+ {
+ return false;
+ }
+ }
+
+ /**
+ * Release the http connection after users finished reading the InputStream
+ * provided by the {@link #getInputStreamForUrl(String, IProgressMonitor)}
+ * method.
+ */
+ public void releaseConnection()
+ {
+ if (getMethod != null)
+ {
+ Thread t = new Thread()
+ {
+ /* (non-Javadoc)
+ * @see java.lang.Thread#run()
+ */
+ @Override
+ public void run()
+ {
+ getMethod.releaseConnection();
+ }
+ };
+ t.start();
+
+ }
+ }
+}
diff --git a/src/plugins/common/src/com/motorola/studio/android/common/utilities/PluginUtils.java b/src/plugins/common/src/com/motorola/studio/android/common/utilities/PluginUtils.java
new file mode 100644
index 0000000..2389f43
--- /dev/null
+++ b/src/plugins/common/src/com/motorola/studio/android/common/utilities/PluginUtils.java
@@ -0,0 +1,631 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.common.utilities;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.Map;
+
+import org.eclipse.core.runtime.FileLocator;
+import org.eclipse.core.runtime.IConfigurationElement;
+import org.eclipse.core.runtime.IExtension;
+import org.eclipse.core.runtime.IExtensionPoint;
+import org.eclipse.core.runtime.IExtensionRegistry;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.core.runtime.Plugin;
+import org.eclipse.osgi.util.NLS;
+import org.osgi.framework.Bundle;
+
+import com.motorola.studio.android.common.exception.AndroidException;
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.common.utilities.i18n.UtilitiesNLS;
+
+/**
+ * DESCRIPTION: This class serves as an utility class containing only static methods such as getters
+ * for plugin attributes, plugin resources, installation path etc
+ *
+ * USAGE: Import this class then use the static methods.
+ */
+public class PluginUtils
+{
+ public static final int OS_WINDOWS = 0;
+
+ public static final int OS_LINUX = 1;
+
+ /**
+ * Returns a plugin attribute using the extension as parameter.
+ *
+ * @param fromExtension
+ * the extension from which the attribute should be collected
+ * @param element
+ * the extension element
+ * @param attribute
+ * the extension attribute
+ *
+ * @return
+ * the value of the extension attribute
+ *
+ * @throws MotodevExtensionException if the executable cannot be created for any reason
+ */
+ public static Object getExecutable(String extensionId, String elementName, String executableName)
+ throws Exception
+
+ {
+ Object executable = null;
+
+ IExtension fromExtension = getExtension(extensionId);
+
+ if ((fromExtension != null) && (elementName != null))
+ {
+ IConfigurationElement[] elements = fromExtension.getConfigurationElements();
+
+ for (IConfigurationElement element : elements)
+ {
+ if (elementName.equals(element.getName()))
+ {
+ try
+ {
+ executable = element.createExecutableExtension(executableName);
+ }
+ catch (Exception e)
+ {
+ String errMsg =
+ NLS.bind(
+ UtilitiesNLS.EXC_PluginUtils_ErrorGettingTheExecutableFromExtensionPoint,
+ new Object[]
+ {
+ executableName, elementName, extensionId
+ });
+ StudioLogger.error(PluginUtils.class, errMsg, e);
+
+ throw new Exception(errMsg, e);
+ }
+ }
+ }
+ }
+
+ return executable;
+ }
+
+ /**
+ * Returns a plugin attribute using the extension as parameter.
+ *
+ * @param fromExtension
+ * the extension from which the attribute should be collected
+ * @param element
+ * the extension element
+ * @param attribute
+ * the extension attribute
+ *
+ * @return
+ * the value of the extension attribute
+ *
+ * @throws MotodevExtensionException if the executable cannot be created for any reason
+ */
+ public static Object getExecutable(String extensionId, String elementName) throws Exception
+ {
+ return getExecutable(extensionId, elementName, "class");
+ }
+
+ /**
+ * Returns the extension using as parameters the id of the extension
+ * and the id of its extension point.
+ *
+ * @param extensionPointId
+ * the id of the extension point
+ * @param extensionId
+ * the id of the extension
+ *
+ * @return
+ * the extension
+ */
+ public static IExtension getExtension(String extensionPointId, String extensionId)
+ {
+ IExtensionRegistry registry = Platform.getExtensionRegistry();
+ IExtension extension = registry.getExtension(extensionPointId, extensionId);
+
+ return extension;
+ }
+
+ /**
+ * Returns the extension using as parameter only the id of the extension.
+ *
+ * @param extensionId
+ * the id of the extension
+ *
+ * @return
+ * the extension
+ */
+ public static IExtension getExtension(String extensionId)
+ {
+ IExtensionRegistry registry = Platform.getExtensionRegistry();
+ IExtension extension = registry.getExtension(extensionId);
+
+ return extension;
+ }
+
+ /**
+ * Returns the label for the extension (extension name) using as parameters
+ * the id of the extension and the id of its extension point.
+ *
+ * @param extensionPointId
+ * the id of the extension point
+ * @param extensionId
+ * the id of the extension
+ *
+ * @return
+ * the extension label
+ */
+ public static String getExtensionLabel(String extensionPointId, String extensionId)
+ {
+ IExtension extension = getExtension(extensionPointId, extensionId);
+ String extensionLabel;
+
+ if (extension != null)
+ {
+ extensionLabel = extension.getLabel();
+ }
+ else
+ {
+ extensionLabel = extensionId;
+ }
+
+ return extensionLabel;
+ }
+
+ /**
+ * Returns the label for the extension (extension name) using as parameter only
+ * the id of the extension.
+ *
+ * @param extensionId
+ * the id of the extension
+ *
+ * @return
+ * the extension label
+ */
+ public static String getExtensionLabel(String extensionId)
+ {
+ IExtension extension = getExtension(extensionId);
+ String extensionLabel;
+
+ if (extension != null)
+ {
+ extensionLabel = extension.getLabel();
+ }
+ else
+ {
+ extensionLabel = extensionId;
+ }
+
+ return extensionLabel;
+ }
+
+ /**
+ * Returns a collection of strings containing the ids of installed plugins.
+ *
+ * @param extensionPointId
+ * the id of the extension point
+ *
+ * @return
+ * a collection object containing the ids of the installed plugins
+ */
+ public static Collection<String> getInstalledPlugins(String extensionPointId)
+ {
+ IExtensionRegistry registry = Platform.getExtensionRegistry();
+ IExtensionPoint extensionPoint = registry.getExtensionPoint(extensionPointId);
+ Collection<String> pluginIds = new LinkedHashSet<String>();
+
+ if (extensionPoint != null)
+ {
+ for (IExtension extension : extensionPoint.getExtensions())
+ {
+ pluginIds.add(extension.getUniqueIdentifier());
+ }
+ }
+
+ return pluginIds;
+ }
+
+ /**
+ * Fills an array object with the ids contained in the collection object returned by
+ * {@link #getInstalledPlugins(String)}.
+ *
+ * @param extensionPointId
+ * the id of the extension point
+ *
+ * @return
+ * an array object containing the ids of the installed plugins
+ */
+ public static String[] getInstalledPluginsAsArray(String extensionPointId)
+ {
+ Collection<String> sampleAppPluginIds = getInstalledPlugins(extensionPointId);
+ String[] sampleAppPluginIdsArray = new String[sampleAppPluginIds.size()];
+
+ return sampleAppPluginIds.toArray(sampleAppPluginIdsArray);
+ }
+
+ /**
+ * Retrieves the namespaces used by the platform.
+ *
+ *
+ * @return a collection with the namespaces used by the platform
+ */
+ public static Collection<String> getPlatformNamespaces()
+ {
+ IExtensionRegistry extensionRegistry = Platform.getExtensionRegistry();
+ String[] namespaces = extensionRegistry.getNamespaces();
+
+ return Arrays.asList(namespaces);
+ }
+
+ /**
+ * Returns a plugin attribute using the extension id and the extension point id as parameters.
+ *
+ * @param extensionPointId
+ * the id of the extension point of the exten sion
+ * @param extensionId
+ * the id of the extension
+ * @param element
+ * the extension element
+ * @param attribute
+ * the extension attribute
+ *
+ * @return
+ * the value of the extension attribute
+ */
+ public static String getPluginAttribute(String extensionPointId, String extensionId,
+ String element, String attribute)
+ {
+ IExtension fromPlugin = getExtension(extensionPointId, extensionId);
+
+ return getPluginAttribute(fromPlugin, element, attribute);
+ }
+
+ /**
+ * Returns a plugin attribute using the extension id and the extension point id as parameters.
+ *
+ * @param extensionId
+ * the id of the extension
+ * @param element
+ * the extension element
+ * @param attribute
+ * the extension attribute
+ *
+ * @return
+ * the value of the extension attribute
+ */
+ public static String getPluginAttribute(String extensionId, String element, String attribute)
+ {
+ IExtension fromPlugin = getExtension(extensionId);
+
+ return getPluginAttribute(fromPlugin, element, attribute);
+ }
+
+ /**
+ * Returns a plugin attribute using the extension as parameter.
+ *
+ * @param fromExtension
+ * the extension from which the attribute should be collected
+ * @param element
+ * the extension element
+ * @param attribute
+ * the extension attribute
+ *
+ * @return
+ * the value of the extension attribute
+ */
+ public static String getPluginAttribute(IExtension fromExtension, String element,
+ String attribute)
+ {
+ String attributeValue = null;
+
+ if (fromExtension != null)
+ {
+ IConfigurationElement[] ceArray = fromExtension.getConfigurationElements();
+
+ for (IConfigurationElement ce : ceArray)
+ {
+ if ((ce != null) && ce.getName().equals(element))
+ {
+ attributeValue = ce.getAttribute(attribute);
+ }
+ }
+ }
+
+ return attributeValue;
+ }
+
+ /**
+ * DOCUMENT ME
+ *
+ * @param extensionId DOCUMENT ME!
+ * @param element DOCUMENT ME!
+ *
+ * @return DOCUMENT ME!
+ */
+ public static Collection<Map<String, String>> getPluginAttributes(String extensionId,
+ String element)
+ {
+ IExtension fromExtension = getExtension(extensionId);
+
+ return getPluginAttributes(fromExtension, element);
+ }
+
+ /**
+ * DOCUMENT ME
+ *
+ * @param fromExtension DOCUMENT ME!
+ * @param element DOCUMENT ME!
+ *
+ * @return DOCUMENT ME!
+ */
+ public static Collection<Map<String, String>> getPluginAttributes(IExtension fromExtension,
+ String element)
+ {
+ Collection<Map<String, String>> elementValues = new LinkedHashSet<Map<String, String>>();
+
+ if (fromExtension != null)
+ {
+ IConfigurationElement[] ceArray = fromExtension.getConfigurationElements();
+
+ for (IConfigurationElement ce : ceArray)
+ {
+ if ((ce != null) && ce.getName().equals(element))
+ {
+ String[] attributes = ce.getAttributeNames();
+
+ if ((attributes != null) && (attributes.length > 0))
+ {
+ int attributesLenght = attributes.length;
+ Map<String, String> attributesMap =
+ new LinkedHashMap<String, String>(attributesLenght,
+ attributesLenght);
+
+ for (String attribute : attributes)
+ {
+ String attributeValue = ce.getAttribute(attribute);
+
+ if ((attributeValue != null) && (!attributeValue.equals("")))
+ {
+ attributesMap.put(attribute, attributeValue);
+ }
+ }
+
+ // only add to elementValues if all values were read correctly
+ if (attributesMap.size() == attributesLenght)
+ {
+ elementValues.add(attributesMap);
+ }
+ }
+ }
+ }
+ }
+
+ return elementValues;
+ }
+
+ /**
+ * Returns the absolute path of installation as a file object using the plugin as parameter.
+ *
+ * @param plugin
+ * the plugin installed
+ *
+ * @return
+ * a file object pointing to the installation path of the plugin
+ */
+ public static File getPluginInstallationPath(Plugin plugin)
+ {
+ Bundle pluginBundle = plugin.getBundle();
+
+ return getPluginInstallationPath(pluginBundle);
+ }
+
+ /**
+ * Returns the absolute path of installation as a file object using the ids of the extension
+ * and extension point as parameters.
+ *
+ * @param extensionPointId
+ * the id of the extension point
+ * @param extensionId
+ * the id of the extension
+ *
+ * @return
+ * a file object pointing to the installation path of the plugin
+ */
+ public static File getPluginInstallationPath(String extensionPointId, String extensionId)
+ {
+ IExtension extension = getExtension(extensionPointId, extensionId);
+
+ return getPluginInstallationPath(extension);
+ }
+
+ /**
+ * Returns the absolute path of installation as a file object using the extension as parameter.
+ *
+ * @param extension
+ * the extension object
+ *
+ * @return
+ * a file object pointing to the installation path of the plugin
+ */
+ public static File getPluginInstallationPath(IExtension extension)
+ {
+ String pluginId = extension.getNamespaceIdentifier();
+ Bundle pluginBundle = Platform.getBundle(pluginId);
+
+ return getPluginInstallationPath(pluginBundle);
+ }
+
+ /**
+ * Returns the absolute path of installation as a file object using the plugin bundle as parameter.
+ *
+ * @param pluginBundle
+ * the plugin bundle
+ *
+ * @return
+ * a file object pointing to the installation path of the plugin
+ */
+ public static File getPluginInstallationPath(Bundle pluginBundle)
+ {
+ //get file using FileLocator
+ File relativeInstalationPath = null;
+ try
+ {
+ relativeInstalationPath =
+ new File(FileLocator.toFileURL(pluginBundle.getEntry("")).getFile());
+ }
+ catch (IOException e)
+ {
+ StudioLogger.warn("Illegal state while getting plugin installation path ("
+ + e.getMessage() + ").");
+ }
+
+ //if failed to get the file using FileLocator
+ if (relativeInstalationPath == null)
+ {
+ String platformPath = Platform.getInstallLocation().getURL().getPath();
+ String pluginPath = pluginBundle.getLocation();
+ int removeIndex = pluginPath.indexOf("@");
+ pluginPath = pluginPath.substring(removeIndex + 1);
+
+ relativeInstalationPath = new File(platformPath, pluginPath);
+ }
+
+ return FileUtil.getCanonicalFile(relativeInstalationPath);
+ }
+
+ /**
+ * Returns a file object from the path: $installationPath\resource
+ *
+ * @param plugin
+ * the plugin object
+ *
+ * @param resource
+ * the plugin resource
+ *
+ * @return
+ * a file object pointing to the path of the resource
+ *
+ * @throws MotodevResourceNotAvailable
+ * throws an exception if it occurs an I/O exception with the path $installationPath\resource
+ */
+ public static File getPluginResource(Plugin plugin, String resource) throws Exception
+ {
+ File pluginPath = getPluginInstallationPath(plugin);
+ File resourceFile = new File(pluginPath, resource);
+ File canonicalFile = null;
+
+ canonicalFile = FileUtil.getCanonicalFile(resourceFile);
+
+ return canonicalFile;
+ }
+
+ /**
+ * Checks if an extension is installed using the extension point id and extension id as parameters.
+ *
+ * @param extensionPointId
+ * the id of the extension point
+ * @param extensionId
+ * the id of the extension
+ *
+ * @return
+ * true if the extension is installed or false otherwise
+ */
+ public static boolean isInstalled(String extensionPointId, String extensionId)
+ {
+ return getExtension(extensionPointId, extensionId) != null;
+ }
+
+ /**
+ * Checks if an extension is installed using the extension id as parameter.
+ *
+ * @param extensionId
+ * the id of the extension
+ *
+ * @return
+ * true if the extension is installed or false otherwise
+ */
+ public static boolean isInstalled(String extensionId)
+ {
+ return getExtension(extensionId) != null;
+ }
+
+ /**
+ * Returns which is the OS.
+ * @return
+ * a code corresponding to the proper OS
+ */
+ public static int getOS()
+ {
+ int result = -1;
+
+ String osName = System.getProperty("os.name").toLowerCase();
+
+ if (osName.indexOf("linux") > -1)
+ {
+ result = OS_LINUX;
+ }
+
+ else if (osName.indexOf("windows") > -1)
+ {
+ result = OS_WINDOWS;
+ }
+
+ return result;
+ }
+
+ /**
+ * Retrieves the File object representing a file stored into the
+ * preferences area of the given plugin
+ *
+ * @return the File inside the given plugin preferences area.
+ * @throws MotodevException if it fails to determine the preferences directory of the given plugin
+ */
+ public final static File getFileOnPreferenceDirectory(Plugin plugin, String filename)
+ throws Exception
+ {
+ File targetXmlFile = null;
+
+ try
+ {
+ IPath path = plugin.getStateLocation();
+
+ if (path != null)
+ {
+ path = path.append(filename);
+ targetXmlFile = path.toFile().getAbsoluteFile();
+ }
+ }
+ catch (IllegalStateException e)
+ {
+ StudioLogger.warn("Illegal state while getting file on preferences directory ("
+ + e.getMessage() + ").");
+ }
+
+ if (targetXmlFile == null)
+ {
+ throw new AndroidException("Could use file " + filename + " on preferences plug-in "
+ + plugin.getBundle().getBundleId());
+ }
+
+ return targetXmlFile;
+ }
+}
diff --git a/src/plugins/common/src/com/motorola/studio/android/common/utilities/TargetDataReader.java b/src/plugins/common/src/com/motorola/studio/android/common/utilities/TargetDataReader.java
new file mode 100644
index 0000000..01cfea8
--- /dev/null
+++ b/src/plugins/common/src/com/motorola/studio/android/common/utilities/TargetDataReader.java
@@ -0,0 +1,124 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.common.utilities;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Knows how to get activity, receiver and/or service actions information from a target
+ */
+public class TargetDataReader
+{
+ //path inside <Android_SDK>\platforms\<Target>\data\activity_actions.txt
+ private final File activityActionsFile;
+
+ //path inside <Android_SDK>\platforms\<Target>\data\broadcast_actions.txt
+ private final File broadCastActionsFile;
+
+ //path inside <Android_SDK>\platforms\<Target>\data\service_actions.txt
+ private final File serviceActionsFile;
+
+ //path inside <Android_SDK>\platforms\<Target>\data\categories.txt
+ private final File categoriesFile;
+
+ /**
+ * @param androidTarget path to <android_sdk_root>/platforms/<target_name>
+ */
+ public TargetDataReader(File androidTarget)
+ {
+ File dataFolder = new File(androidTarget, "data"); //$NON-NLS-1$
+ this.activityActionsFile = new File(dataFolder, "activity_actions.txt"); //$NON-NLS-1$
+ this.broadCastActionsFile = new File(dataFolder, "broadcast_actions.txt"); //$NON-NLS-1$
+ this.serviceActionsFile = new File(dataFolder, "service_actions.txt"); //$NON-NLS-1$
+ this.categoriesFile = new File(dataFolder, "categories.txt"); //$NON-NLS-1$
+ }
+
+ private List<String> readItems(File file) throws IOException
+ {
+ List<String> items = new ArrayList<String>();
+ BufferedReader bufferedReader = null;
+ FileReader reader = null;
+ try
+ {
+ reader = new FileReader(file);
+ bufferedReader = new BufferedReader(reader);
+
+ String line;
+ while ((line = bufferedReader.readLine()) != null)
+ {
+ items.add(line.trim());
+ }
+ }
+ finally
+ {
+ if (reader != null)
+ {
+ reader.close();
+ }
+ if (bufferedReader != null)
+ {
+ bufferedReader.close();
+ }
+ }
+ return items;
+ }
+
+ /**
+ * Reads activity actions file and creates the list of activity actions
+ * @return list of activityActions available
+ * @throws IOException if file not found, or if there is any problem reading the activity_actions file
+ */
+ public List<String> getActivityActions() throws IOException
+ {
+ return readItems(activityActionsFile);
+ }
+
+ /**
+ * Reads service actions file and creates the list of service actions
+ * @return list of serviceActions available
+ * @throws IOException if file not found, or if there is any problem reading the service_actions file
+ */
+ public List<String> getServiceActions() throws IOException
+ {
+ return readItems(serviceActionsFile);
+ }
+
+ /**
+ * Reads broadcast receiver actions file and creates the list of broadcast receiver actions
+ * @return list of broadcastReceiverActions available
+ * @throws IOException if file not found, or if there is any problem reading the broadcast_actions file
+ */
+ public List<String> getReceiverActions() throws IOException
+ {
+ return readItems(broadCastActionsFile);
+ }
+
+ /**
+ * Reads categories file and creates the list of categories available in the target
+ * @return list of Intent Filters Categories available
+ * @throws IOException if file not found, or if there is any problem reading the broadcast_actions file
+ */
+ public List<String> getIntentFilterCategories() throws IOException
+ {
+ return readItems(categoriesFile);
+ }
+
+}
diff --git a/src/plugins/common/src/com/motorola/studio/android/common/utilities/i18n/UtilitiesNLS.java b/src/plugins/common/src/com/motorola/studio/android/common/utilities/i18n/UtilitiesNLS.java
new file mode 100644
index 0000000..3ff1a76
--- /dev/null
+++ b/src/plugins/common/src/com/motorola/studio/android/common/utilities/i18n/UtilitiesNLS.java
@@ -0,0 +1,203 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.common.utilities.i18n;
+
+import org.eclipse.osgi.util.NLS;
+
+/**
+ * Class that contains the localized messages to be used through the
+ * Utilities package
+ */
+public class UtilitiesNLS extends NLS
+{
+ static
+ {
+ NLS.initializeMessages("com.motorola.studio.android.common.utilities.i18n.utilitiesNLS",
+ UtilitiesNLS.class);
+ }
+
+ public static String ERR_Gen_ErrorTitle;
+
+ /*
+ * Exception strings area
+ */
+ public static String EXC_PluginUtils_ErrorGettingTheExecutableFromExtensionPoint;
+
+ public static String EXC_FileUtil_ErrorSettingTheFileEncoding;
+
+ public static String EXC_FileUtil_ErrorWritingTheFile;
+
+ public static String EXC_FileUtil_CannotOverwriteTheFile;
+
+ public static String EXC_FileUtil_TheFileCannotBeRead;
+
+ public static String EXC_Project_CannotCreateFolderReadOnlyWorkspace;
+
+ /*
+ * UI strings area
+ */
+
+ public static String UI_EclipseUtils_OpenDetails;
+
+ public static String UI_EclipseUtils_CloseDetails;
+
+ public static String UI_Project_Creating_Folder_Task;
+
+ public static String UI_Project_Verifying_Folder_Task;
+
+ public static String UI_DoNotShowMEAgain;
+
+ public static String UI_AlwaysProceed;
+
+ /*
+ * Manifest file
+ */
+ public static String ERR_AndroidProjectManifest_AndroidManifestDoesNotExist;
+
+ public static String EXC_CommentNode_ChildNodesCannotBeAddedToACommentNode;
+
+ public static String ERR_AndroidManifestNodeParser_ErrorParsingPriority;
+
+ public static String ERR_AndroidManifestNodeParser_ErrorParsingInitOrder;
+
+ public static String WARN_AndroidManifestNode_TheNodeContainsAnInvalidAttribute;
+
+ public static String EXC_AndroidManifestFile_ErrorCreatingTheDocumentBuilder;
+
+ public static String EXC_AndroidManifestFile_ErrorFormattingTheXMLOutput;
+
+ public static String EXC_AndroidManifestNodeParser_ErrorParsingTheXMLFile;
+
+ public static String EXC_AndroidManifestNodeParser_ErrorReadingTheXMLContent;
+
+ public static String ERR_AndroidManifestNodeParser_ErrorParsingVersionCode;
+
+ public static String ERR_AndroidManifestFile_TheFileAndroidManifestXmlIsMalFormed;
+
+ /*
+ * Project Utils
+ */
+ public static String ProjectUtils_AddLibsProgress_ConfiguringClassPaths;
+
+ public static String ProjectUtils_AddLibsProgress_ConfiguringProjects;
+
+ public static String ProjectUtils_AddLibsProgress_ErrorSettingClasspaths;
+
+ public static String ProjectUtils_AddLibsProgress_PreparingPaths;
+
+ public static String UI_ProjectCreationSupport_Creating_Strings_Task;
+
+ /*
+ * Add remove buttons GUI
+ */
+ public static String UI_AddRemoveButtons_AddButtonLabel;
+
+ public static String UI_AddRemoveButtons_RemoveButtonLabel;
+
+ public static String AddInputRemoveButtons_InputButtonLabel;
+
+ /*
+ * Android Utils
+ */
+ public static String AndroidUtils_ErrorReadingDefaultPropertiesFile;
+
+ /*
+ * UI File Chooser
+ */
+ public static String UI_FileChooser_Dialog_Message;
+
+ public static String UI_FileChooser_Dialog_Title;
+
+ public static String UI_FileChooser_Filesystem;
+
+ public static String UI_FileChooser_Workspace;
+
+ /*
+ * UI General
+ */
+ public static String UI_General_BrowseButtonLabel;
+
+ public static String UI_General_ProjectLabel;
+
+ /*
+ * Project Chooser
+ */
+ public static String ProjectChooser_UI_ChooseAProject;
+
+ public static String ProjectChooser_UI_Selection;
+
+ /*
+ * Android Utils - error messages
+ */
+ public static String AndroidUtils_ERROR_DEFAULTPROPERTIESNOTFOUND;
+
+ public static String AndroidUtils_ERROR_GETINTENTPERMISSIONSBYREFLECTION_MESSAGE;
+
+ public static String AndroidUtils_ERROR_GETINTENTPERMISSIONSBYREFLECTION_TITLE;
+
+ public static String AndroidUtils_ERROR_SDK_TARGETPLATFORM_NOTFOUND;
+
+ public static String AndroidUtils_ERROR_SDKPATHNOTFOUND;
+
+ public static String AndroidUtils_NotPossibleToGetAPIVersionNumber_Error;
+
+ public static String AndroidUtils_NotPossibleToReachPermissionsFile_Error;
+
+ public static String FileUtil_File_Exists_Message;
+
+ public static String FileUtil_File_Exists_Title;
+
+ public static String FileUtil_Get_MD5_Algorithm_Failed;
+
+ public static String FileUtil_MD5_Calculation_Failed;
+
+ /*
+ * UI Utils
+ */
+ public static String Passwordinput_Enterpassword_Label;
+
+ public static String Passwordinput_Enternewpassword_Label;
+
+ public static String Passwordinput_Reenterpassword_Label;
+
+ public static String Passwordinput_Title;
+
+ public static String Passwordinput_Error_Passwordnotmatch;
+
+ public static String PasswordProvider_SaveThisPassword;
+
+ public static String Passwordinput_Error_PasswordMinimumSize;
+
+ public static String LoginPasswordDialogCreator_DialogTItle0;
+
+ public static String SDKLoginPasswordDialog_LoginInformationMessage;
+
+ public static String SDKLoginPasswordDialog_PasswordLabel;
+
+ public static String SDKLoginPasswordDialog_UsernameLabel;
+
+ /*
+ * Http Utils
+ */
+ public static String HttpUtils_MonitorTask_ContactingSite;
+
+ public static String HttpUtils_MonitorTask_PreparingConnection;
+
+ public static String HttpUtils_MonitorTask_RetrievingSiteContent;
+
+ public static String HttpUtils_MonitorTask_WaitingAuthentication;
+
+}
diff --git a/src/plugins/common/src/com/motorola/studio/android/common/utilities/i18n/utilitiesNLS.properties b/src/plugins/common/src/com/motorola/studio/android/common/utilities/i18n/utilitiesNLS.properties
new file mode 100644
index 0000000..2b2f056
--- /dev/null
+++ b/src/plugins/common/src/com/motorola/studio/android/common/utilities/i18n/utilitiesNLS.properties
@@ -0,0 +1,83 @@
+ERR_Gen_ErrorTitle=Error
+EXC_PluginUtils_ErrorGettingTheExecutableFromExtensionPoint=An error occurred while getting the executable {0} from element {1} of the extension point {2}.
+
+EXC_FileUtil_ErrorSettingTheFileEncoding=Cannot set the text encoding to the file content: {0}
+EXC_FileUtil_ErrorWritingTheFile=The file exists but cannot be written to: {0}
+EXC_FileUtil_CannotOverwriteTheFile=The file exists but 'do not overwrite' was selected: {0}
+EXC_FileUtil_TheFileCannotBeRead=The file cannot be read: {0}
+EXC_Project_CannotCreateFolderReadOnlyWorkspace=Cannot create the folder ''{0}'' because the workspace is not writable.
+
+
+UI_EclipseUtils_OpenDetails= Details >>
+UI_EclipseUtils_CloseDetails= << Details
+
+UI_Project_Creating_Folder_Task=Creating directory
+
+UI_Project_Verifying_Folder_Task=Verifying directory and permissions
+UI_DoNotShowMEAgain=Do not show me again
+UI_AlwaysProceed=Always proceed without asking
+
+ERR_AndroidProjectManifest_AndroidManifestDoesNotExist=The AndroidManifest.xml file does not exist in the project {0}.
+EXC_CommentNode_ChildNodesCannotBeAddedToACommentNode=Child nodes cannot be added to a comment node.
+ERR_AndroidManifestNodeParser_ErrorParsingPriority=The value ''{0}'' cannot be parsed as an integer. The priority property of the <intent-filter> node will be removed.
+ERR_AndroidManifestNodeParser_ErrorParsingInitOrder=The value ''{0}'' cannot be parsed as an integer. The initOrder property of the <provider> node will be removed.
+WARN_AndroidManifestNode_TheNodeContainsAnInvalidAttribute=The node {0} contains an invalid attribute: ''{1}''.
+EXC_AndroidManifestFile_ErrorCreatingTheDocumentBuilder=Cannot create the XML document builder. It will not be possible to get the XML content.
+EXC_AndroidManifestFile_ErrorFormattingTheXMLOutput=An error occurred while formatting the XML output. It will not be possible to get the XML content.
+EXC_AndroidManifestNodeParser_ErrorParsingTheXMLFile=An error occurred while parsing AndroidManifest.xml: {0}
+EXC_AndroidManifestNodeParser_ErrorReadingTheXMLContent=An error occurred while reading AndroidManifest.xml: {0}
+ERR_AndroidManifestNodeParser_ErrorParsingVersionCode=The value ''{0}'' cannot be parsed as an integer. The versionCode property of the <manifest> node must be an integer. It has been changed to {1}.
+ERR_AndroidManifestFile_TheFileAndroidManifestXmlIsMalFormed=AndroidManifest.xml is malformed. The <manifest> node is missing.
+
+ProjectUtils_AddLibsProgress_ConfiguringClassPaths=Configuring classpaths
+ProjectUtils_AddLibsProgress_ConfiguringProjects=Configuring projects
+ProjectUtils_AddLibsProgress_ErrorSettingClasspaths=Could not set the project's classpath
+ProjectUtils_AddLibsProgress_PreparingPaths=Preparing paths
+
+UI_ProjectCreationSupport_Creating_Strings_Task=Creating Strings resource file
+
+UI_AddRemoveButtons_AddButtonLabel=Choose...
+UI_AddRemoveButtons_RemoveButtonLabel=Remove
+AddInputRemoveButtons_InputButtonLabel=Input...
+
+AndroidUtils_ERROR_DEFAULTPROPERTIESNOTFOUND=default.properties file not found.
+AndroidUtils_ERROR_GETINTENTPERMISSIONSBYREFLECTION_MESSAGE=The permissions list may not be accurate. An update of MOTODEV Studio should fix it.
+AndroidUtils_ERROR_GETINTENTPERMISSIONSBYREFLECTION_TITLE=Problem getting intent filter permissions
+AndroidUtils_ERROR_SDK_TARGETPLATFORM_NOTFOUND=not found, check your sdk or your application target platform.
+AndroidUtils_ERROR_SDKPATHNOTFOUND=SDK path not found.
+AndroidUtils_ErrorReadingDefaultPropertiesFile=Some problems were found with your project's default.properties.
+AndroidUtils_NotPossibleToGetAPIVersionNumber_Error=It was not possible to get API version number for project
+AndroidUtils_NotPossibleToReachPermissionsFile_Error=Not possible to reach permissions.txt inside the plugin
+FileUtil_File_Exists_Message=The file {0} already exists.\nDo you want to overwrite it?
+FileUtil_File_Exists_Title=File already exists
+FileUtil_Get_MD5_Algorithm_Failed=Could not get the MD5 algorithm from Java Security
+FileUtil_MD5_Calculation_Failed=Could not calculate md5sum for file {0}
+
+UI_FileChooser_Dialog_Message=Choose file to open
+UI_FileChooser_Dialog_Title=Open
+UI_FileChooser_Filesystem=Filesystem...
+UI_FileChooser_Workspace=Workspace...
+
+UI_General_BrowseButtonLabel=Browse...
+UI_General_ProjectLabel=Project
+
+ProjectChooser_UI_ChooseAProject=Choose a Project
+ProjectChooser_UI_Selection=Project Selection
+
+Passwordinput_Enterpassword_Label=Old password:
+Passwordinput_Enternewpassword_Label=New password:
+Passwordinput_Reenterpassword_Label=Confirm new password:
+Passwordinput_Title=Enter Password
+Passwordinput_Error_Passwordnotmatch=New passwords don't match
+PasswordProvider_SaveThisPassword=Save this password
+
+Passwordinput_Error_PasswordMinimumSize=New password must be at least {0} characters long.
+LoginPasswordDialogCreator_DialogTItle0=Login
+SDKLoginPasswordDialog_LoginInformationMessage=Enter login information for
+SDKLoginPasswordDialog_PasswordLabel=Password:
+SDKLoginPasswordDialog_UsernameLabel=Username:
+
+HttpUtils_MonitorTask_ContactingSite=Contacting site
+HttpUtils_MonitorTask_PreparingConnection=Preparing connection
+HttpUtils_MonitorTask_RetrievingSiteContent=Retrieving site content
+HttpUtils_MonitorTask_WaitingAuthentication=Waiting for authentication \ No newline at end of file
diff --git a/src/plugins/common/src/com/motorola/studio/android/common/utilities/ui/Country.java b/src/plugins/common/src/com/motorola/studio/android/common/utilities/ui/Country.java
new file mode 100644
index 0000000..ff583a5
--- /dev/null
+++ b/src/plugins/common/src/com/motorola/studio/android/common/utilities/ui/Country.java
@@ -0,0 +1,75 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.common.utilities.ui;
+
+/**
+ * This class represents a country.
+ */
+public class Country implements Comparable<Country>
+{
+
+ /*
+ * The two letters country code.
+ */
+ private final String countryCode;
+
+ /*
+ * The country name.
+ */
+ private final String countryName;
+
+ /**
+ * Creates a country instance with the given country code and country name.
+ *
+ * @param countryCode The two letters country code.
+ * @param countryName Th two letters country name.
+ */
+ public Country(String countryCode, String countryName)
+ {
+ this.countryCode = countryCode;
+ this.countryName = countryName;
+ }
+
+ /**
+ * Returns the two letters country code.
+ *
+ * @return The two letters country code.
+ */
+ public String getCountryCode()
+ {
+ return countryCode;
+ }
+
+ /**
+ * Returns the country name.
+ *
+ * @return The country name.
+ */
+ public String getCountryName()
+ {
+ return countryName;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see java.lang.Comparable#compareTo(java.lang.Object)
+ */
+ public int compareTo(Country otherCountry)
+ {
+ return this.countryName.compareToIgnoreCase(otherCountry.countryName);
+ }
+
+}
diff --git a/src/plugins/common/src/com/motorola/studio/android/common/utilities/ui/LoginPasswordDialogCreator.java b/src/plugins/common/src/com/motorola/studio/android/common/utilities/ui/LoginPasswordDialogCreator.java
new file mode 100644
index 0000000..2521ef9
--- /dev/null
+++ b/src/plugins/common/src/com/motorola/studio/android/common/utilities/ui/LoginPasswordDialogCreator.java
@@ -0,0 +1,172 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.common.utilities.ui;
+
+import org.eclipse.jface.dialogs.Dialog;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.ModifyEvent;
+import org.eclipse.swt.events.ModifyListener;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.ui.PlatformUI;
+
+import com.motorola.studio.android.common.utilities.i18n.UtilitiesNLS;
+
+public class LoginPasswordDialogCreator
+{
+ private LoginPasswordDialog dialog;
+
+ private final String url;
+
+ // dialog return values
+ public static final int OK = LoginPasswordDialog.OK;
+
+ public static final int CANCEL = LoginPasswordDialog.CANCEL;
+
+ public LoginPasswordDialogCreator(String url)
+ {
+
+ this.url = url;
+
+ }
+
+ public int openLoginPasswordDialog()
+ {
+ final Integer[] dialogReturnValue = new Integer[1];
+ PlatformUI.getWorkbench().getDisplay().syncExec(new Runnable()
+ {
+
+ public void run()
+ {
+ dialog =
+ new LoginPasswordDialog(PlatformUI.getWorkbench().getDisplay()
+ .getActiveShell());
+ dialogReturnValue[0] = dialog.open();
+ }
+ });
+
+ return dialogReturnValue[0];
+ }
+
+ public String getTypedLogin()
+ {
+ String login = dialog != null ? dialog.getTypedLogin() : null;
+ return login;
+ }
+
+ public String getTypedPassword()
+ {
+ String password = dialog != null ? dialog.getTypedPassword() : null;
+ return password;
+ }
+
+ private class LoginPasswordDialog extends Dialog
+ {
+ private String login;
+
+ private String password;
+
+ /**
+ * @param parentShell
+ */
+ protected LoginPasswordDialog(Shell parentShell)
+ {
+
+ super(parentShell);
+
+ setShellStyle(getShellStyle() | SWT.DIALOG_TRIM);
+
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.dialogs.Dialog#createDialogArea(org.eclipse.swt.widgets.Composite)
+ */
+ @Override
+ protected Control createDialogArea(Composite parent)
+ {
+
+ parent.getShell().setText(UtilitiesNLS.LoginPasswordDialogCreator_DialogTItle0);
+
+ Composite topComposite = new Composite(parent, SWT.FILL);
+ GridLayout layout = new GridLayout(1, false);
+ GridData layoutData = new GridData(SWT.FILL, SWT.FILL, true, true);
+ topComposite.setLayout(layout);
+ topComposite.setLayoutData(layoutData);
+
+ Label messageLabel = new Label(topComposite, SWT.NONE);
+ layoutData = new GridData(SWT.FILL, SWT.CENTER, false, false);
+ messageLabel.setLayoutData(layoutData);
+ messageLabel
+ .setText(" \n" + UtilitiesNLS.SDKLoginPasswordDialog_LoginInformationMessage + " " + url.substring(0, url.lastIndexOf("/")) + " "); //$NON-NLS-1$ //$NON-NLS-3$ //$NON-NLS-4$
+
+ Composite mainComposite = new Composite(topComposite, SWT.FILL);
+ layout = new GridLayout(2, false);
+ mainComposite.setLayout(layout);
+ mainComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+
+ Label loginLabel = new Label(mainComposite, SWT.NONE);
+ layoutData = new GridData(SWT.LEFT, SWT.CENTER, false, false);
+ loginLabel.setLayoutData(layoutData);
+ loginLabel.setText(UtilitiesNLS.SDKLoginPasswordDialog_UsernameLabel);
+
+ final Text loginText = new Text(mainComposite, SWT.SINGLE | SWT.BORDER);
+ layoutData = new GridData(SWT.FILL, SWT.CENTER, true, false);
+ loginText.setLayoutData(layoutData);
+ loginText.addModifyListener(new ModifyListener()
+ {
+ public void modifyText(ModifyEvent e)
+ {
+ login = loginText.getText();
+ }
+ });
+
+ Label passwordLabel = new Label(mainComposite, SWT.NONE);
+ layoutData = new GridData(SWT.LEFT, SWT.CENTER, false, false);
+ passwordLabel.setLayoutData(layoutData);
+ passwordLabel.setText(UtilitiesNLS.SDKLoginPasswordDialog_PasswordLabel);
+
+ final Text passwordText =
+ new Text(mainComposite, SWT.SINGLE | SWT.BORDER | SWT.PASSWORD);
+ layoutData = new GridData(SWT.FILL, SWT.CENTER, true, false);
+ passwordText.setLayoutData(layoutData);
+ passwordText.addModifyListener(new ModifyListener()
+ {
+ public void modifyText(ModifyEvent e)
+ {
+ password = passwordText.getText();
+ }
+ });
+
+ return topComposite;
+ }
+
+ public String getTypedLogin()
+ {
+ return login;
+ }
+
+ public String getTypedPassword()
+ {
+ return password;
+ }
+
+ }
+}
diff --git a/src/plugins/common/src/com/motorola/studio/android/common/utilities/ui/PasswordInputDialog.java b/src/plugins/common/src/com/motorola/studio/android/common/utilities/ui/PasswordInputDialog.java
new file mode 100644
index 0000000..de80b33
--- /dev/null
+++ b/src/plugins/common/src/com/motorola/studio/android/common/utilities/ui/PasswordInputDialog.java
@@ -0,0 +1,324 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.common.utilities.ui;
+
+import org.eclipse.jface.dialogs.Dialog;
+import org.eclipse.jface.dialogs.IMessageProvider;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.ModifyEvent;
+import org.eclipse.swt.events.ModifyListener;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.ui.ISharedImages;
+import org.eclipse.ui.PlatformUI;
+
+import com.motorola.studio.android.common.utilities.i18n.UtilitiesNLS;
+
+public class PasswordInputDialog extends Dialog
+{
+ private static final int SMALL_TEXT_SIZE = 64;
+
+ private String newPassword = null;
+
+ private String oldPassword = null;
+
+ private final boolean changePassword;
+
+ private Text newPasswordConfirmText;
+
+ private Text newPasswordText;
+
+ private Text oldPasswordText;
+
+ private Label message;
+
+ private Label image;
+
+ private final String description;
+
+ private Button saveCheckBox;
+
+ private boolean needToStorePassword = true;
+
+ private int passwordMinimumSize = 1; //1 is default, it is recommended to change in the constructor
+
+ /**
+ * Create a new Input dialog
+ * @param parent: the parent shell
+ * @param description: the textual description of dialog
+ * @param changePassword: true if you want this dialog be a change password dialog (it will present old, new and confirm new fields). False to show only enter password field
+ * @param oldPassword: null if user must enter oldpassword. Anything to create dialog with preentered password (only if changePassword is true);
+ */
+ public PasswordInputDialog(Shell parent, String description, boolean changePassword,
+ String oldPassword, int passwordMinimumSize)
+ {
+ super(parent);
+ this.changePassword = changePassword;
+ this.description = description;
+ if (changePassword)
+ {
+ this.oldPassword = oldPassword;
+ }
+ this.passwordMinimumSize = passwordMinimumSize;
+ }
+
+ @Override
+ protected Control createDialogArea(Composite parent)
+ {
+ Composite mainComposite = new Composite(parent, SWT.NONE);
+ mainComposite.setLayoutData(new GridData(GridData.FILL_BOTH));
+ mainComposite.setLayout(new GridLayout(2, false));
+
+ /**
+ * The Message Area (With an image and a text)
+ */
+ Composite messageComposite = new Composite(mainComposite, SWT.NONE);
+ GridData layoutData = new GridData(SWT.FILL, SWT.CENTER, true, false, 2, 1);
+ messageComposite.setLayoutData(layoutData);
+ GridLayout layout = new GridLayout(2, false);
+ layout.marginWidth = 0;
+ messageComposite.setLayout(layout);
+
+ image = new Label(messageComposite, SWT.NONE);
+ image.setVisible(false);
+ layoutData = new GridData(SWT.LEFT, SWT.CENTER, false, false, 1, 1);
+ image.setLayoutData(layoutData);
+
+ message = new Label(messageComposite, SWT.NONE);
+ message.setText(description);
+ layoutData = new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1);
+ message.setLayoutData(layoutData);
+
+ Label oldPasswordLabel = new Label(mainComposite, SWT.NONE);
+ layoutData = new GridData(SWT.LEFT, SWT.CENTER, false, false, 1, 1);
+ oldPasswordLabel.setLayoutData(layoutData);
+ oldPasswordLabel.setText(UtilitiesNLS.Passwordinput_Enterpassword_Label);
+
+ oldPasswordText = new Text(mainComposite, SWT.BORDER | SWT.SINGLE | SWT.PASSWORD);
+ oldPasswordText.setTextLimit(SMALL_TEXT_SIZE);
+ layoutData = new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1);
+ layoutData.minimumWidth = 150;
+ oldPasswordText.setLayoutData(layoutData);
+ if (oldPassword != null)
+ {
+ oldPasswordText.setEnabled(false);
+ oldPasswordText.setText(oldPassword);
+ oldPasswordLabel.setEnabled(false);
+ }
+ oldPasswordText.addModifyListener(new ModifyListener()
+ {
+ public void modifyText(ModifyEvent e)
+ {
+ updateStatus();
+ }
+ });
+
+ /**
+ * If the input is to change the passwords, create all other fields
+ */
+ if (changePassword)
+ {
+ Label separator = new Label(mainComposite, SWT.HORIZONTAL | SWT.SEPARATOR);
+ layoutData = new GridData(SWT.FILL, SWT.CENTER, true, false, 2, 1);
+ separator.setLayoutData(layoutData);
+
+ /**
+ * The newPassword area
+ */
+ Label passwordLabel = new Label(mainComposite, SWT.NONE);
+ layoutData = new GridData(SWT.LEFT, SWT.CENTER, false, false, 1, 1);
+ passwordLabel.setLayoutData(layoutData);
+ passwordLabel.setText(UtilitiesNLS.Passwordinput_Enternewpassword_Label);
+
+ newPasswordText = new Text(mainComposite, SWT.BORDER | SWT.SINGLE | SWT.PASSWORD);
+ newPasswordText.setTextLimit(SMALL_TEXT_SIZE);
+ layoutData = new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1);
+ layoutData.minimumWidth = 150;
+ newPasswordText.setLayoutData(layoutData);
+
+ Label passwordConfirmLabel = new Label(mainComposite, SWT.NONE);
+ layoutData = new GridData(SWT.LEFT, SWT.CENTER, false, false, 1, 1);
+ passwordConfirmLabel.setLayoutData(layoutData);
+ passwordConfirmLabel.setText(UtilitiesNLS.Passwordinput_Reenterpassword_Label);
+
+ newPasswordConfirmText =
+ new Text(mainComposite, SWT.BORDER | SWT.SINGLE | SWT.PASSWORD);
+ newPasswordConfirmText.setTextLimit(SMALL_TEXT_SIZE);
+ layoutData = new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1);
+ layoutData.minimumWidth = 150;
+ newPasswordConfirmText.setLayoutData(layoutData);
+
+ newPasswordText.addModifyListener(new ModifyListener()
+ {
+
+ public void modifyText(ModifyEvent e)
+ {
+ updateStatus();
+ }
+ });
+
+ newPasswordConfirmText.addModifyListener(new ModifyListener()
+ {
+
+ public void modifyText(ModifyEvent e)
+ {
+ updateStatus();
+ }
+ });
+ }
+
+ //Creates the save password checkbox
+ saveCheckBox = new Button(mainComposite, SWT.CHECK);
+ saveCheckBox.setText(UtilitiesNLS.PasswordProvider_SaveThisPassword);
+ saveCheckBox.setSelection(false);
+ GridData gridData = new GridData(SWT.LEFT, SWT.CENTER, false, false, 2, 1);
+ saveCheckBox.setLayoutData(gridData);
+
+ return mainComposite;
+ }
+
+ public void updateStatus()
+ {
+ String errorMessage = null;
+ int severity = IMessageProvider.NONE;
+
+ if (oldPasswordText.getText().length() > 0)
+ {
+ oldPassword = oldPasswordText.getText();
+ }
+ else
+ {
+ errorMessage = "";
+ oldPassword = null;
+ }
+
+ if ((errorMessage == null) && changePassword)
+ {
+ if (newPasswordText.getText().length() < passwordMinimumSize)
+ {
+ errorMessage =
+ UtilitiesNLS.bind(UtilitiesNLS.Passwordinput_Error_PasswordMinimumSize,
+ passwordMinimumSize);
+ severity = IMessageProvider.ERROR;
+ }
+ else
+ {
+ if (!newPasswordText.getText().equals(newPasswordConfirmText.getText()))
+ {
+ errorMessage = UtilitiesNLS.Passwordinput_Error_Passwordnotmatch;
+ severity = IMessageProvider.ERROR;
+ }
+ else
+ {
+ newPassword = newPasswordText.getText();
+ }
+ }
+ }
+
+ setErrorMessage(errorMessage, severity);
+
+ }
+
+ private void setErrorMessage(String errorMsg, int severity)
+ {
+ if ((errorMsg == null) || (severity == IMessageProvider.NONE))
+ {
+ image.setImage(null);
+ image.setVisible(false);
+ message.setText(description);
+ getButton(OK).setEnabled(errorMsg == null);
+ }
+ else
+ {
+ message.setText(errorMsg);
+ message.setVisible(true);
+ switch (severity)
+ {
+ case IMessageProvider.ERROR:
+ image.setImage(PlatformUI.getWorkbench().getSharedImages()
+ .getImage(ISharedImages.IMG_OBJS_ERROR_TSK));
+ break;
+
+ case IMessageProvider.INFORMATION:
+ image.setImage(PlatformUI.getWorkbench().getSharedImages()
+ .getImage(ISharedImages.IMG_OBJS_INFO_TSK));
+ break;
+
+ case IMessageProvider.WARNING:
+ image.setImage(PlatformUI.getWorkbench().getSharedImages()
+ .getImage(ISharedImages.IMG_OBJS_WARN_TSK));
+ break;
+
+ default:
+ image.setImage(PlatformUI.getWorkbench().getSharedImages()
+ .getImage(ISharedImages.IMG_OBJS_ERROR_TSK));
+ break;
+ }
+ image.setVisible(true);
+ getButton(OK).setEnabled(false);
+ }
+ message.getParent().layout();
+ }
+
+ @Override
+ protected void createButtonsForButtonBar(Composite parent)
+ {
+ super.createButtonsForButtonBar(parent);
+ getButton(OK).setEnabled(false);
+ }
+
+ public String getNewPassword()
+ {
+ return newPassword;
+ }
+
+ public String getOldPassword()
+ {
+ return oldPassword;
+ }
+
+ public boolean needToStorePassword()
+ {
+ return needToStorePassword;
+ }
+
+ @Override
+ protected void configureShell(Shell newShell)
+ {
+ super.configureShell(newShell);
+ newShell.setText(UtilitiesNLS.Passwordinput_Title);
+ newShell.setMinimumSize(500, 200);
+ newShell.layout(true);
+
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.dialogs.Dialog#okPressed()
+ */
+ @Override
+ protected void okPressed()
+ {
+ needToStorePassword = saveCheckBox.getSelection();
+ super.okPressed();
+ }
+
+}
diff --git a/src/plugins/common/src/com/motorola/studio/android/common/utilities/ui/ToolsCountries.java b/src/plugins/common/src/com/motorola/studio/android/common/utilities/ui/ToolsCountries.java
new file mode 100644
index 0000000..e0ff37e
--- /dev/null
+++ b/src/plugins/common/src/com/motorola/studio/android/common/utilities/ui/ToolsCountries.java
@@ -0,0 +1,92 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.common.utilities.ui;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+
+/**
+ * Class to handle the countries.
+ */
+public class ToolsCountries
+{
+
+ /**
+ * The shared instance.
+ */
+ private static ToolsCountries instance = null;
+
+ /**
+ * Bundle to get countries from module (properties file).
+ */
+ private final ResourceBundle bundle;
+
+ /**
+ * Default constructor.
+ */
+ private ToolsCountries()
+ {
+ this.bundle = ResourceBundle.getBundle("countries"); //$NON-NLS-1$
+ }
+
+ /**
+ * Returns the single instance.
+ *
+ * @return The singleton instance.
+ */
+ public static synchronized ToolsCountries getInstance()
+ {
+ if (instance == null)
+ {
+ instance = new ToolsCountries();
+ }
+ return instance;
+ }
+
+ /**
+ * Returns all countries.
+ *
+ * @return All countries.
+ */
+ public List<Country> getCountries()
+ {
+ List<Country> toReturn = new ArrayList<Country>();
+
+ try
+ {
+ Enumeration<String> keys = bundle.getKeys();
+ while (keys.hasMoreElements())
+ {
+ String countryCode = keys.nextElement();
+ String countryName = bundle.getString(countryCode);
+ toReturn.add(new Country(countryCode, countryName));
+ }
+
+ Collections.sort(toReturn);
+ }
+ catch (MissingResourceException e)
+ {
+ toReturn = new ArrayList<Country>();
+ }
+
+ return toReturn;
+ }
+
+}
diff --git a/src/plugins/common/src/com/motorola/studio/android/common/utilities/ui/WidgetsFactory.java b/src/plugins/common/src/com/motorola/studio/android/common/utilities/ui/WidgetsFactory.java
new file mode 100644
index 0000000..768cf22
--- /dev/null
+++ b/src/plugins/common/src/com/motorola/studio/android/common/utilities/ui/WidgetsFactory.java
@@ -0,0 +1,454 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.common.utilities.ui;
+
+import org.eclipse.jface.dialogs.IDialogConstants;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Combo;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Group;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.List;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.swt.widgets.TableColumn;
+import org.eclipse.swt.widgets.TableItem;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.swt.widgets.ToolBar;
+import org.eclipse.swt.widgets.ToolItem;
+import org.eclipse.swt.widgets.TreeItem;
+
+/**
+ * Class factory to create widgets.
+ */
+public class WidgetsFactory
+{
+
+ public static final int GROUP_MARGIN_HEIGHT = 10;
+
+ public static final int GROUP_MARGIN_WIDTH = 10;
+
+ /**
+ * Create a new composite with two columns.
+ *
+ * @param parent The parent composite.
+ * @return A composite with two columns.
+ */
+ public static Composite createComposite(Composite parent)
+ {
+ Composite toReturn = new Composite(parent, SWT.NULL);
+ toReturn.setLayout(createGridLayout());
+ toReturn.setLayoutData(new GridData(GridData.GRAB_HORIZONTAL | GridData.FILL_HORIZONTAL));
+
+ return toReturn;
+ }
+
+ /**
+ * Create a new composite with the given column count.
+ *
+ * @param parent The parent composite.
+ * @param numColumns The column count for the new composite.
+ * @return A composite with the given column count.
+ */
+ public static Composite createComposite(Composite parent, int numColumns)
+ {
+ Composite toReturn = createComposite(parent);
+ ((GridLayout) toReturn.getLayout()).numColumns = numColumns;
+
+ return toReturn;
+ }
+
+ /**
+ * Creates a new line.
+ *
+ * @param parent The parent composite.
+ * @return A label with a line.
+ */
+ public static Label createLine(Composite parent)
+ {
+ Label toReturn = new Label(parent, SWT.SEPARATOR | SWT.HORIZONTAL | SWT.BOLD);
+ GridData gridData = new GridData(GridData.FILL_HORIZONTAL);
+ gridData.horizontalSpan = ((GridLayout) parent.getLayout()).numColumns;
+ toReturn.setLayoutData(gridData);
+
+ return toReturn;
+ }
+
+ /**
+ * Creates a new GridLayout with two columns.
+ *
+ * @return A new GridLayout with two columns.
+ */
+ public static GridLayout createGridLayout()
+ {
+ GridLayout toReturn = new GridLayout();
+ toReturn.numColumns = 2;
+ toReturn.makeColumnsEqualWidth = false;
+ toReturn.marginWidth = 0;
+ toReturn.marginHeight = 0;
+
+ return toReturn;
+ }
+
+ /**
+ * Creates a new GridLayout with the given number of columns.
+ *
+ * @return A new GridLayout with the given number of columns.
+ */
+ public static GridLayout createGridLayout(int numColumns)
+ {
+ GridLayout toReturn = new GridLayout();
+ toReturn.numColumns = numColumns;
+ toReturn.makeColumnsEqualWidth = false;
+ toReturn.marginWidth = 0;
+ toReturn.marginHeight = 0;
+
+ return toReturn;
+ }
+
+ /**
+ * Creates a new GridLayout with the given number of columns for the given
+ * composite.
+ *
+ * @return A new GridLayout with the given number of columns for the given
+ * composite.
+ */
+ public static GridLayout createGridLayout(int numColumns, Composite composite)
+ {
+ GridLayout toReturn = new GridLayout();
+ toReturn.numColumns = numColumns;
+ toReturn.makeColumnsEqualWidth = false;
+ toReturn.marginWidth = 0;
+ toReturn.marginHeight = 0;
+
+ composite.setLayout(toReturn);
+ return toReturn;
+ }
+
+ /**
+ * Creates a new label.
+ *
+ * @param parent The parent composite.
+ * @param text Text used in label.
+ * @return A new label
+ */
+ public static Label createLabel(Composite parent, String text)
+ {
+ return createLabel(parent, text, SWT.NONE);
+ }
+
+ /**
+ * Creates a new label.
+ *
+ * @param parent The parent composite.
+ * @param text Text used in label.
+ * @param style The style used in label.
+ * @return A new label
+ */
+ public static Label createLabel(Composite parent, String text, int style)
+ {
+ Label toReturn = new Label(parent, style);
+ if (text != null)
+ {
+ toReturn.setText(text);
+ }
+ toReturn.setFont(parent.getFont());
+
+ return toReturn;
+ }
+
+ /**
+ * Creates a new combo.
+ *
+ * @param parent The parent composite.
+ * @return The new combo
+ */
+ public static Combo createCombo(Composite parent)
+ {
+ Combo toReturn = new Combo(parent, SWT.READ_ONLY);
+ GridData data = new GridData(GridData.FILL_HORIZONTAL);
+ data.widthHint = IDialogConstants.ENTRY_FIELD_WIDTH;
+ data.horizontalSpan = 1;
+ toReturn.setLayoutData(data);
+
+ return toReturn;
+ }
+
+ /**
+ * Creates two labels used to show a read only value in screens.
+ *
+ * @param parent The parent composite.
+ * @param text The text used in both labels.
+ * @return The label that will show a read only value.
+ */
+ public static Label createValueLabel(Composite parent, String text)
+ {
+ createLabel(parent, text);
+
+ return createLabel(parent, null);
+ }
+
+ /**
+ * Creates a new button.
+ *
+ * @param parent The parent composite.
+ * @param text The text of the button.
+ * @return A new button
+ */
+ public static Button createButton(Composite parent, String text)
+ {
+ Button toReturn = new Button(parent, SWT.PUSH);
+ toReturn.setFont(parent.getFont());
+ toReturn.setText(text);
+ toReturn.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+
+ return toReturn;
+ }
+
+ /**
+ * Creates a new table widget.
+ *
+ * @param parent The parent composite.
+ * @return The new table
+ */
+ public static Table createTable(Composite parent)
+ {
+ Table toReturn =
+ new Table(parent, SWT.H_SCROLL | SWT.V_SCROLL | SWT.MULTI | SWT.FULL_SELECTION
+ | SWT.BORDER);
+ GridData data = new GridData(GridData.FILL_BOTH);
+ data.widthHint = IDialogConstants.ENTRY_FIELD_WIDTH;
+ data.heightHint = toReturn.getItemHeight();
+ data.horizontalSpan = 1;
+ toReturn.setLayoutData(data);
+
+ return toReturn;
+ }
+
+ /**
+ * Creates a new table widget.
+ *
+ * @param parent The parent composite.
+ * @return The new table
+ */
+ public static Table createTableMultiSelection(Composite parent)
+ {
+ Table toReturn =
+ new Table(parent, SWT.H_SCROLL | SWT.V_SCROLL | SWT.FULL_SELECTION | SWT.BORDER
+ | SWT.MULTI);
+ GridData data = new GridData(GridData.FILL_BOTH);
+ data.widthHint = IDialogConstants.ENTRY_FIELD_WIDTH;
+ data.heightHint = toReturn.getItemHeight();
+ data.horizontalSpan = 1;
+ toReturn.setLayoutData(data);
+
+ return toReturn;
+ }
+
+ /**
+ * Creates a new table column.
+ *
+ * @param table The table of the column.
+ * @param text The column text.
+ * @return A new table column
+ */
+ public static TableColumn createTableColumn(Table table, String text)
+ {
+ TableColumn toReturn = new TableColumn(table, SWT.NONE);
+ toReturn.setText(text);
+
+ return toReturn;
+ }
+
+ /**
+ * Creates a new table item.
+ *
+ * @param table The table of the table item.
+ * @param image The image of the item.
+ * @param s The text of the item.
+ * @return The new table item.
+ */
+ public static TableItem createTableItem(Table table, Image image, String s)
+ {
+ TableItem toReturn = new TableItem(table, SWT.NONE);
+ toReturn.setText(s);
+ if (image != null)
+ {
+ toReturn.setImage(image);
+ }
+
+ return toReturn;
+ }
+
+ /**
+ * Creates a collection of table items.
+ *
+ * @param table The table of the table item.
+ * @param image The image of the item.
+ * @param s The text of the item.
+ * @return The new table item.
+ */
+ public static TableItem createTableItem(Table table, Image image, String... s)
+ {
+ TableItem toReturn = new TableItem(table, SWT.NONE);
+ toReturn.setText(s);
+ if (image != null)
+ {
+ toReturn.setImage(image);
+ }
+
+ return toReturn;
+ }
+
+ /**
+ * Creates a new text.
+ *
+ * @param parent The parent composite.
+ * @return The new text.
+ */
+ public static Text createText(Composite parent)
+ {
+ Text toReturn = new Text(parent, SWT.BORDER);
+ GridData data = new GridData(GridData.FILL_HORIZONTAL);
+ data.widthHint = IDialogConstants.ENTRY_FIELD_WIDTH;
+ toReturn.setLayoutData(data);
+
+ return toReturn;
+ }
+
+ /**
+ * Creates a new tree item. If the text parameter is null, it will appear is
+ * after the field parameter.
+ *
+ * @param itemParent The parent tree item
+ * @param image The image of the tree item.
+ * @param field The field.
+ * @param text The text of the tree item.
+ * @return The new tree item.
+ */
+ public static TreeItem createTreeItem(TreeItem itemParent, Image image, String field,
+ String text)
+ {
+ TreeItem toReturn = new TreeItem(itemParent, 0);
+ toReturn.setText((text == null) ? field : field + " (" + text + ")"); //$NON-NLS-1$ //$NON-NLS-2$
+ toReturn.setImage(image);
+ toReturn.setExpanded(true);
+
+ return toReturn;
+ }
+
+ /**
+ * Creates a new horizontal tool bar.
+ *
+ * @param parent The parent tool bar item.
+ * @return A new tool bar item.
+ */
+ public static ToolBar createHorizontalToolBar(Composite parent)
+ {
+ return new ToolBar(parent, SWT.HORIZONTAL);
+ }
+
+ /**
+ * Creates a new tool item like a push button
+ *
+ * @param toolBar The parent tool bar item.
+ * @return A new tool item.
+ */
+ public static ToolItem createPushToolItem(ToolBar toolBar)
+ {
+ return new ToolItem(toolBar, SWT.PUSH);
+ }
+
+ /**
+ * Creates a titled group.
+ *
+ * @param parent The parent composite
+ * @param title The desired title.
+ * @return The titled group
+ */
+ public static Group createTitledGroup(Composite parent, String title)
+ {
+ Group toReturn = new Group(parent, SWT.SHADOW_NONE);
+ toReturn.setText(title);
+ toReturn.setLayout(new GridLayout());
+ toReturn.setLayoutData(new GridData(GridData.FILL_BOTH));
+ return toReturn;
+ }
+
+ /**
+ * Creates a titled group with the given number of columns.
+ *
+ * @param parent The parent composite.
+ * @param title The group title.
+ * @param numColumns The number of columns.
+ * @return The created group.
+ */
+ public static Group createTitledGroup(Composite parent, String title, int numColumns)
+ {
+ Group toReturn = new Group(parent, SWT.SHADOW_NONE);
+ toReturn.setText(title);
+
+ toReturn.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+
+ GridLayout layout = createGridLayout(numColumns);
+ layout.marginHeight = GROUP_MARGIN_HEIGHT;
+ layout.marginWidth = GROUP_MARGIN_WIDTH;
+ toReturn.setLayout(layout);
+ return toReturn;
+ }
+
+ /**
+ * Creates a group
+ *
+ * @param parent The parent composite
+ * @return The group
+ */
+ public static Group createGroup(Composite parent)
+ {
+ Group toReturn = new Group(parent, SWT.SHADOW_NONE);
+ return toReturn;
+ }
+
+ /**
+ * Creates a radio button
+ *
+ * @param parent The parent composite
+ * @param text The given text.
+ * @return The radio button
+ */
+ public static Button createRadioButton(Composite parent, String text)
+ {
+ Button toReturn = new Button(parent, SWT.RADIO);
+ toReturn.setText(text);
+ toReturn.setLayoutData(new GridData(GridData.FILL_BOTH));
+ return toReturn;
+ }
+
+ /**
+ * Creates a list.
+ *
+ * @param parent The parent composite.
+ * @return The list.
+ */
+ public static List createList(Composite parent)
+ {
+ return new List(parent, SWT.BORDER | SWT.SINGLE);
+ }
+
+}
diff --git a/src/plugins/common/src/com/motorola/studio/android/common/utilities/ui/WidgetsUtil.java b/src/plugins/common/src/com/motorola/studio/android/common/utilities/ui/WidgetsUtil.java
new file mode 100644
index 0000000..523c375
--- /dev/null
+++ b/src/plugins/common/src/com/motorola/studio/android/common/utilities/ui/WidgetsUtil.java
@@ -0,0 +1,392 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.common.utilities.ui;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.jface.dialogs.Dialog;
+import org.eclipse.jface.preference.FileFieldEditor;
+import org.eclipse.jface.preference.PreferenceDialog;
+import org.eclipse.jface.preference.PreferenceManager;
+import org.eclipse.jface.preference.StringFieldEditor;
+import org.eclipse.jface.wizard.IWizard;
+import org.eclipse.jface.wizard.WizardDialog;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.Combo;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Monitor;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.swt.widgets.TableItem;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.swt.widgets.Tree;
+import org.eclipse.swt.widgets.TreeItem;
+import org.eclipse.ui.PlatformUI;
+
+import com.motorola.studio.android.common.log.StudioLogger;
+
+/**
+ * Class with useful methods for widgets.
+ */
+public class WidgetsUtil
+{
+
+ /**
+ * This method test if a given String is null or empty.
+ *
+ * @param s The String
+ * @return <code>true</code> if the String is null or empty,
+ * <code>false</code> otherwise.
+ */
+ private static boolean isNullOrEmpty(String s)
+ {
+ return ((s != null) && s.trim().equals("")); //$NON-NLS-1$
+ }
+
+ /**
+ * The method verify if the file exist.
+ *
+ * @param fileName The full path for file.
+ * @return <code>true</code> if the file exist, <code>false</code>
+ * otherwise.
+ */
+ public static boolean fileExist(String fileName)
+ {
+ return !isNullOrEmpty(fileName) && new File(fileName).exists();
+ }
+
+ /**
+ * This method test if some StringFieldEditor value of the given collection
+ * is null or empty.
+ *
+ * @param editors
+ * @return <code>true</code> if some StringFieldEditor value is null or
+ * empty, <code>false</code> otherwise.
+ */
+ public static boolean isNullOrEmpty(StringFieldEditor... editors)
+ {
+ for (StringFieldEditor editor : editors)
+ {
+ if (isNullOrEmpty(editor))
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * This method test if a StringFieldEditor value is null or empty.
+ *
+ * @param editor The StringFieldEditor
+ * @return <code>true</code> if the StringFieldEditor value is null or
+ * empty, <code>false</code> otherwise.
+ */
+ public static boolean isNullOrEmpty(StringFieldEditor editor)
+ {
+ return ((editor != null) && isNullOrEmpty(editor.getStringValue()));
+ }
+
+ /**
+ * This method test if a StringFieldEditor value contains a invalid
+ * character.
+ *
+ * @param editor The StringFieldEditor
+ * @return <code>true</code> if the StringFieldEditor value contains invalid
+ * character, <code>false</code> otherwise.
+ */
+ public static boolean checkExistInvalidCharacter(StringFieldEditor editor, String invalidChars)
+ {
+ for (int i = 0; i < invalidChars.length(); i++)
+ {
+ String invalidChar = invalidChars.substring(i, i + 1);
+ if (editor.getStringValue().contains(invalidChar))
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * This method test if a Text value is null or empty.
+ *
+ * @param text The Text
+ * @return <code>true</code> if the Text value is null or empty,
+ * <code>false</code> otherwise.
+ */
+ public static boolean isNullOrEmpty(Text text)
+ {
+ return ((text != null) && isNullOrEmpty(text.getText()));
+ }
+
+ /**
+ * This method test if a FileFieldEditor value is null or empty.
+ *
+ * @param editor The FileFieldEditor
+ * @return <code>true</code> if the FileFieldEditor value is null or empty,
+ * <code>false</code> otherwise.
+ */
+ public static boolean isEmpty(FileFieldEditor editor)
+ {
+ return isNullOrEmpty(editor.getStringValue());
+ }
+
+ /**
+ * This method test if a Combo value is null or empty.
+ *
+ * @param combo The Combo
+ * @return <code>true</code> if the Combo value is null or not selected,
+ * <code>false</code> otherwise.
+ */
+ public static boolean isNullOrDeselected(Combo combo)
+ {
+ return ((combo != null) && (combo.getSelectionIndex() == -1));
+ }
+
+ /**
+ * Returns the size of file.
+ *
+ * @param fileName The file name.
+ * @return The size of file.
+ */
+ public static long fileSize(String fileName)
+ {
+ return new File(fileName).length();
+ }
+
+ /**
+ * This method test if a Table has one or more lines.
+ *
+ * @param table The table
+ * @return <code>true</code> if the Table has one or more lines,
+ * <code>false</code> otherwise.
+ */
+ public static boolean isNullOrEmpty(Table table)
+ {
+ return table.getItemCount() > 0;
+ }
+
+ /**
+ * Executes a wizard.
+ *
+ * @param wizard The wizard.
+ * @return <code>true</code> if the Wizard dialog has constant OK,
+ * <code>false</code> otherwise .
+ */
+ public static boolean runWizard(IWizard wizard)
+ {
+ Shell activeShell = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(); //Display.getCurrent().getActiveShell();
+ WizardDialog dialog = new WizardDialog(activeShell, wizard);
+
+ try
+ {
+ dialog.create();
+ }
+ catch (Throwable e)
+ {
+ StudioLogger.error("Error opening dialog");
+ }
+ centerDialog(dialog);
+ return dialog.open() == WizardDialog.OK;
+ }
+
+ /**
+ * Opens the Eclipse preferences dialog and selects the page of the given
+ * id.
+ *
+ * @param shell The shell.
+ * @param selectedNode The preferences page to selec.
+ * @return <code>true</code> if the Wizard dialog has constant OK,
+ * <code>false</code> otherwise .
+ */
+ public static boolean runPreferencePage(Shell shell, String selectedNode)
+ {
+ PreferenceManager manager = PlatformUI.getWorkbench().getPreferenceManager();
+ PreferenceDialog dialog = new PreferenceDialog(shell, manager);
+ dialog.setSelectedNode(selectedNode);
+ WidgetsUtil.centerDialog(shell);
+ return dialog.open() == PreferenceDialog.OK;
+ }
+
+ /**
+ * Center the dialog.
+ *
+ * @param shell The shell.
+ */
+ public static void centerDialog(Shell shell)
+ {
+ Monitor primary = shell.getMonitor();
+ Rectangle bounds = primary.getBounds();
+ Rectangle rect = shell.getBounds();
+ int x = bounds.x + ((bounds.width - rect.width) / 2);
+ int y = bounds.y + ((bounds.height - rect.height) / 2);
+ shell.setLocation(x, y);
+ }
+
+ /**
+ * Center the dialog.
+ *
+ * @param dialog The dialog.
+ */
+ public static void centerDialog(Dialog dialog)
+ {
+ centerDialog(dialog.getShell());
+ }
+
+ /**
+ * Check the leaf items of the given tree.
+ *
+ * @param tree The tree.
+ * @return A collection containing the leaf tree items.
+ */
+ public static List<TreeItem> getCheckedLeafItems(Tree tree)
+ {
+ List<TreeItem> toReturn = new ArrayList<TreeItem>();
+ selectCheckedLeafItems(tree.getItems(), toReturn);
+ return toReturn;
+ }
+
+ /**
+ * Returns a list of the leaf nodes that are checked.
+ *
+ * @param items The parent items.
+ * @param list A list of the leaf nodes that are checked.
+ */
+ private static void selectCheckedLeafItems(TreeItem[] items, List<TreeItem> list)
+ {
+ int len = items.length;
+ for (int i = 0; i < len; i++)
+ {
+ if (items[i].getItemCount() > 0)
+ {
+ selectCheckedLeafItems(items[i].getItems(), list);
+ }
+ else if (items[i].getChecked())
+ {
+ list.add(items[i]);
+ }
+ }
+ }
+
+ /**
+ * Expand all the given tree items.
+ *
+ * @param items The tree items.
+ */
+ public static void expandAll(TreeItem[] items)
+ {
+ for (int i = 0; i < items.length; i++)
+ {
+ if (items[i].getItems().length > 0)
+ {
+ items[i].setExpanded(true);
+ expandAll(items[i].getItems());
+ }
+ }
+ }
+
+ /**
+ * Returns the full path of a given tree item.
+ *
+ * @param item The tree item.
+ * @return The full path of a given tree item.
+ */
+ public static String getFullPathTreeItem(TreeItem item)
+ {
+ String toReturn = item.getText();
+ if (item != null)
+ {
+ if (item.getParentItem() != null)
+ {
+ toReturn = getFullPathTreeItem(item.getParentItem()) + "." + toReturn; //$NON-NLS-1$
+ }
+ }
+ return toReturn;
+ }
+
+ /**
+ * This method verifies if a given file can be read.
+ *
+ * @param fileName the full file path.
+ * @return <code>true</code> if read permission is granted,
+ * <code>false</code> otherwise.
+ */
+ public static boolean canRead(String fileName)
+ {
+ return !isNullOrEmpty(fileName) && new File(fileName).canRead();
+ }
+
+ /**
+ * This method verifies if a given file has the read and write permissions
+ * granted.
+ *
+ * @param fileName The file
+ * @return <code>true</code> if permissions are granted, <code>false</code>
+ * otherwise.
+ */
+ public static boolean canReadWrite(String fileName)
+ {
+ File file = new File(fileName);
+ return file.canRead() && file.canWrite();
+ }
+
+ /**
+ * This method simulates a refresh in a Composite object.
+ *
+ * @param composite A composite object.
+ */
+ public static void refreshComposite(Composite composite)
+ {
+ for (Composite parent = composite.getParent(); parent != null; parent = parent.getParent())
+ {
+ parent.layout();
+ }
+ }
+
+ /**
+ * Check the leaf items of the given table.
+ *
+ * @param table The table.
+ * @return A collection containing the leaf table items.
+ */
+ public static List<TableItem> getCheckedLeafItems(Table table)
+ {
+ List<TableItem> toReturn = new ArrayList<TableItem>();
+ selectCheckedLeafItems(table.getItems(), toReturn);
+ return toReturn;
+ }
+
+ /**
+ * Returns a list of the leaf nodes that are checked.
+ *
+ * @param items The parent items.
+ * @param list A list of the leaf nodes that are checked.
+ */
+ private static void selectCheckedLeafItems(TableItem[] items, List<TableItem> list)
+ {
+ int len = items.length;
+ for (int i = 0; i < len; i++)
+ {
+ if (items[i].getChecked())
+ {
+ list.add(items[i]);
+ }
+ }
+ }
+}
diff --git a/src/plugins/common/src/com/motorola/studio/android/manifest/AndroidProjectManifestFile.java b/src/plugins/common/src/com/motorola/studio/android/manifest/AndroidProjectManifestFile.java
new file mode 100644
index 0000000..ae51379
--- /dev/null
+++ b/src/plugins/common/src/com/motorola/studio/android/manifest/AndroidProjectManifestFile.java
@@ -0,0 +1,113 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.manifest;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.osgi.util.NLS;
+
+import com.motorola.studio.android.common.exception.AndroidException;
+import com.motorola.studio.android.common.utilities.FileUtil;
+import com.motorola.studio.android.common.utilities.i18n.UtilitiesNLS;
+import com.motorola.studio.android.model.manifest.AndroidManifestFile;
+
+/**
+ * Class that contains methods to deal with AndroidManifest.xml file in projects
+ */
+public class AndroidProjectManifestFile
+{
+ /**
+ * The AndroidManifest.xml file name
+ */
+ private static final String ANDROID_MANIFEST_FILENAME = "AndroidManifest.xml";
+
+ /**
+ * Retrieves the project AndroidManifest.xml file
+ *
+ * @param project The project
+ * @return the AndroidManifestFile object representing the file
+ * @throws AndroidException
+ * @throws CoreException
+ */
+ public static AndroidManifestFile getFromProject(IProject project) throws AndroidException,
+ CoreException
+ {
+ Assert.isLegal(project != null);
+
+ AndroidManifestFile androidManifestFile = null;
+
+ IResource resManifest = project.findMember(IPath.SEPARATOR + ANDROID_MANIFEST_FILENAME);
+
+ if ((resManifest != null) && (resManifest instanceof IFile))
+ {
+ if (resManifest.exists())
+ {
+ IFile manifestFile = (IFile) resManifest;
+ IDocument document = FileUtil.readFile(manifestFile);
+
+ androidManifestFile = new AndroidManifestFile();
+ androidManifestFile.parseDocument(document);
+ }
+ else
+ {
+ String errMsg =
+ NLS.bind(
+ UtilitiesNLS.ERR_AndroidProjectManifest_AndroidManifestDoesNotExist,
+ project.getName());
+
+ throw new AndroidException(errMsg);
+ }
+ }
+ else
+ {
+ String errMsg =
+ NLS.bind(UtilitiesNLS.ERR_AndroidProjectManifest_AndroidManifestDoesNotExist,
+ project.getName());
+
+ throw new AndroidException(errMsg);
+ }
+
+ return androidManifestFile;
+ }
+
+ /**
+ * Saves an AndroidManifestFile object to the AndroidManifest.xml file
+ *
+ * @param project The project
+ * @param androidManifestFile The AndroidManifestFile object
+ * @param overwrite If the file must be overwritten
+ * @throws AndroidException
+ * @throws CoreException
+ */
+ public static void saveToProject(IProject project, AndroidManifestFile androidManifestFile,
+ boolean overwrite) throws AndroidException, CoreException
+ {
+ Assert.isLegal(project != null);
+ Assert.isLegal(androidManifestFile != null);
+
+ final String UTF8_ENCODING = "UTF-8"; //$NON-NLS-1$
+
+ IFile manifestFile = project.getFile(IPath.SEPARATOR + ANDROID_MANIFEST_FILENAME);
+ IDocument document = androidManifestFile.getContent();
+
+ FileUtil.saveFile(manifestFile, document, UTF8_ENCODING, overwrite);
+ }
+}
diff --git a/src/plugins/common/src/com/motorola/studio/android/model/manifest/AndroidManifestFile.java b/src/plugins/common/src/com/motorola/studio/android/model/manifest/AndroidManifestFile.java
new file mode 100644
index 0000000..ed24742
--- /dev/null
+++ b/src/plugins/common/src/com/motorola/studio/android/model/manifest/AndroidManifestFile.java
@@ -0,0 +1,533 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+package com.motorola.studio.android.model.manifest;
+
+import java.io.IOException;
+import java.io.StringWriter;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+
+import org.apache.xml.serialize.OutputFormat;
+import org.apache.xml.serialize.XMLSerializer;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.jface.text.IDocument;
+import org.w3c.dom.Attr;
+import org.w3c.dom.Document;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+
+import com.motorola.studio.android.common.exception.AndroidException;
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.common.utilities.AndroidStatus;
+import com.motorola.studio.android.common.utilities.i18n.UtilitiesNLS;
+import com.motorola.studio.android.model.manifest.dom.AbstractBuildingBlockNode;
+import com.motorola.studio.android.model.manifest.dom.ActionNode;
+import com.motorola.studio.android.model.manifest.dom.ActivityNode;
+import com.motorola.studio.android.model.manifest.dom.AndroidManifestNode;
+import com.motorola.studio.android.model.manifest.dom.AndroidManifestNode.NodeType;
+import com.motorola.studio.android.model.manifest.dom.ApplicationNode;
+import com.motorola.studio.android.model.manifest.dom.CommentNode;
+import com.motorola.studio.android.model.manifest.dom.IntentFilterNode;
+import com.motorola.studio.android.model.manifest.dom.ManifestNode;
+import com.motorola.studio.android.model.manifest.parser.AndroidManifestParser;
+
+/**
+ * Class that represents an AndroidManifest.xml file
+ */
+@SuppressWarnings("deprecation")
+public class AndroidManifestFile extends AndroidManifestParser
+{
+ /**
+ * Adds an AndroidManifestNode to the file
+ *
+ * @param node The node to be added
+ */
+ public void addNode(AndroidManifestNode node)
+ {
+ if ((node != null) && !rootNodes.contains(node))
+ {
+ rootNodes.add(node);
+ }
+ }
+
+ /**
+ * Removes an AndroidManifestNode from the file
+ *
+ * @param node The node to be removed
+ */
+ public void removeNode(AndroidManifestNode node)
+ {
+ if ((node != null) && !rootNodes.contains(node))
+ {
+ rootNodes.add(node);
+ }
+ }
+
+ /**
+ * Retrieves an array containing all nodes present on xml root.
+ * If the file is valid, there will be only one node, the <manifest> node
+ *
+ * @return an array containing all nodes present on xml root.
+ */
+ public AndroidManifestNode[] getNodes()
+ {
+ AndroidManifestNode[] nodes = new AndroidManifestNode[rootNodes.size()];
+ nodes = rootNodes.toArray(nodes);
+
+ return nodes;
+ }
+
+ /**
+ * Retrieves the <manifest> node
+ *
+ * @return the <manifest> node
+ */
+ public ManifestNode getManifestNode()
+ {
+ ManifestNode manifestNode = null;
+
+ for (AndroidManifestNode node : rootNodes)
+ {
+ if (node.getNodeType() == NodeType.Manifest)
+ {
+ manifestNode = (ManifestNode) node;
+ break;
+ }
+ }
+
+ return manifestNode;
+ }
+
+ /**
+ * Retrieves the <application> node from the manifest file
+ *
+ * @return The <application> node of the manifest file.
+ */
+ public ApplicationNode getApplicationNode()
+ {
+ // Retrieve <manifest> node and return application node
+ return getManifestNode().getApplicationNode();
+
+ }
+
+ /**
+ * Retrieves a building block node, which can be of the following types:
+ * NodeType.Activity
+ * NodeType.Provider
+ * NodeType.Receiver
+ * NodeType.Service
+ *
+ * @param type The NodeType.
+ * @param androidName The android:name property value. Should be the fully qualified name of the building block class.
+ * For example, for the Activity class "Test" located in the package "com.motorola", the androidName parameter should be "com.motorola.Teste"
+ *
+ * @return A AbstractBuildingBlockNode that represents the building block. If no matching node is found or
+ * the type passed is invalid, null is returned.
+ */
+ public AbstractBuildingBlockNode getBuildingBlockNode(NodeType type, String androidName)
+ {
+ // Result
+ AbstractBuildingBlockNode resultNode = null;
+
+ // Candidate list of nodes to iterate through
+ List<AbstractBuildingBlockNode> candidateList = new LinkedList<AbstractBuildingBlockNode>();
+
+ // Retrieve the Manifest node to check the default package
+ ManifestNode manifestNode = getManifestNode();
+ String manifestPackage = manifestNode.getNodeProperties().get(PROP_PACKAGE);
+
+ // Compare the qualified name from the parameter with the manifest package. If equal, we can use the class name for comparison purposes.
+ String androidNamePackage = androidName.substring(0, androidName.lastIndexOf('.'));
+ String shortAndroidName = new String();
+
+ if (manifestPackage.equals(androidNamePackage))
+ {
+ shortAndroidName = androidName.substring(androidName.lastIndexOf('.'));
+ }
+
+ // Retrieve the application node
+ ApplicationNode applicationNode = getApplicationNode();
+
+ // Check the building block type
+ switch (type)
+ {
+ case Activity:
+ candidateList.addAll(applicationNode.getActivityNodes());
+ break;
+ case Provider:
+ candidateList.addAll(applicationNode.getProviderNodes());
+ break;
+ case Receiver:
+ candidateList.addAll(applicationNode.getReceiverNodes());
+ break;
+ case Service:
+ candidateList.addAll(applicationNode.getServiceNodes());
+ break;
+ default:
+ break;
+ }
+
+ // Search the candidate list for the target node
+ for (AbstractBuildingBlockNode node : candidateList)
+ {
+ // In the case that shortAndroidName is not null or empty, we check if it's like that in the manifest first
+ if ((shortAndroidName != null) && (shortAndroidName.length() > 0))
+ {
+ if (node.getNodeProperties().get(PROP_NAME).equals(shortAndroidName))
+ {
+ resultNode = node;
+ break;
+ }
+ }
+
+ if (node.getNodeProperties().get(PROP_NAME).equals(androidName))
+ {
+ // We found the node!
+ resultNode = node;
+ break;
+ }
+ }
+
+ return resultNode;
+ }
+
+ /**
+ * Retrieves an IDocument object containing the xml content for the file
+ *
+ * @return an IDocument object containing the xml content for the file
+ */
+ public IDocument getContent() throws AndroidException
+ {
+ IDocument document = null;
+ DocumentBuilder documentBuilder = null;
+
+ try
+ {
+ documentBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
+ }
+ catch (ParserConfigurationException e)
+ {
+ StudioLogger.error(AndroidManifestFile.class,
+ UtilitiesNLS.EXC_AndroidManifestFile_ErrorCreatingTheDocumentBuilder, e);
+ throw new AndroidException(
+ UtilitiesNLS.EXC_AndroidManifestFile_ErrorCreatingTheDocumentBuilder);
+ }
+
+ Document xmlDocument = documentBuilder.newDocument();
+
+ for (AndroidManifestNode node : rootNodes)
+ {
+ addNode(xmlDocument, null, node);
+ }
+
+ document = new org.eclipse.jface.text.Document(getXmlContent(xmlDocument));
+
+ return document;
+ }
+
+ /**
+ * Recursive function to build a XML file from AndroidManifestNode objects
+ *
+ * @param xmlDocument The XML Document
+ * @param xmlParentNode The XML parent node
+ * @param nodeToAdd The AndroidManifestNode to be added
+ */
+ private void addNode(Document xmlDocument, Node xmlParentNode, AndroidManifestNode nodeToAdd)
+ {
+ Node xmlNode;
+
+ if (nodeToAdd instanceof CommentNode)
+ {
+ CommentNode commentNode = (CommentNode) nodeToAdd;
+ xmlNode = xmlDocument.createComment(commentNode.getComment());
+ }
+ else
+ {
+ xmlNode = xmlDocument.createElement(nodeToAdd.getNodeName());
+ Map<String, String> attributes = nodeToAdd.getNodeProperties();
+ Map<String, String> unknownAttributes = nodeToAdd.getNodeUnknownProperties();
+ AndroidManifestNode[] children = nodeToAdd.getChildren();
+ AndroidManifestNode[] unknown = nodeToAdd.getUnkownChildren();
+
+ // Adds valid attributes
+ if ((attributes != null) && (attributes.size() > 0))
+ {
+ NamedNodeMap xmlAttributes = xmlNode.getAttributes();
+
+ for (String attrName : attributes.keySet())
+ {
+ Attr attr = xmlDocument.createAttribute(attrName);
+ attr.setValue(attributes.get(attrName));
+ xmlAttributes.setNamedItem(attr);
+ }
+ }
+
+ // Adds invalid attributes
+ if ((unknownAttributes != null) && (unknownAttributes.size() > 0))
+ {
+ NamedNodeMap xmlAttributes = xmlNode.getAttributes();
+
+ for (String attrName : unknownAttributes.keySet())
+ {
+ Attr attr = xmlDocument.createAttribute(attrName);
+ attr.setNodeValue(unknownAttributes.get(attrName));
+ xmlAttributes.setNamedItem(attr);
+ }
+ }
+
+ // Adds known child nodes
+ for (AndroidManifestNode child : children)
+ {
+ addNode(xmlDocument, xmlNode, child);
+ }
+
+ // Adds unknown child nodes
+ for (AndroidManifestNode child : unknown)
+ {
+ addNode(xmlDocument, xmlNode, child);
+ }
+ }
+
+ if (xmlParentNode == null)
+ {
+ xmlDocument.appendChild(xmlNode);
+ }
+ else
+ {
+ xmlParentNode.appendChild(xmlNode);
+ }
+ }
+
+ /**
+ * Creates the XML content from a XML Document
+ *
+ * @param xmlDocument The XML Document
+ * @return a String object containing the XML content
+ */
+ private String getXmlContent(Document xmlDocument) throws AndroidException
+ {
+ // Despite Xerces is deprecated, its formatted xml source output works
+ // better than W3C xml output classes
+ OutputFormat outputFormat = new OutputFormat();
+ XMLSerializer xmlSerializer = new XMLSerializer();
+ StringWriter writer = new StringWriter();
+ String content = null;
+
+ outputFormat.setEncoding("UTF-8");
+ outputFormat.setLineSeparator(System.getProperty("line.separator"));
+ outputFormat.setIndenting(true);
+
+ xmlSerializer.setOutputCharStream(writer);
+ xmlSerializer.setOutputFormat(outputFormat);
+
+ try
+ {
+ xmlSerializer.serialize(xmlDocument);
+ content = writer.toString();
+ }
+ catch (IOException e)
+ {
+ StudioLogger.error(AndroidManifestFile.class,
+ UtilitiesNLS.EXC_AndroidManifestFile_ErrorFormattingTheXMLOutput, e);
+ throw new AndroidException(
+ UtilitiesNLS.EXC_AndroidManifestFile_ErrorFormattingTheXMLOutput);
+ }
+ finally
+ {
+ if (writer != null)
+ {
+ try
+ {
+ writer.close();
+ }
+ catch (IOException e)
+ {
+ //Do nothing.
+ }
+ }
+ }
+
+ return content;
+ }
+
+ /**
+ * Gets all file problems: Errors and Warnings
+ *
+ * @return all file problems
+ */
+ public IStatus[] getProblems()
+ {
+ ManifestNode manifestNode = getManifestNode();
+ IStatus[] errors;
+
+ if (manifestNode == null)
+ {
+ errors =
+ new IStatus[]
+ {
+ new AndroidStatus(
+ IStatus.ERROR,
+ UtilitiesNLS.ERR_AndroidManifestFile_TheFileAndroidManifestXmlIsMalFormed)
+ };
+ }
+ else
+ {
+ errors = getManifestNode().getRecursiveNodeErrors();
+ }
+
+ return errors;
+ }
+
+ /**
+ * Gets all file errors
+ *
+ * @return all file errors
+ */
+ public IStatus[] getErrors()
+ {
+ List<IStatus> errors = new LinkedList<IStatus>();
+
+ for (IStatus status : getProblems())
+ {
+ if (status.getSeverity() == IStatus.ERROR)
+ {
+ errors.add(status);
+ }
+ }
+
+ return errors.toArray(new IStatus[0]);
+ }
+
+ /**
+ * Checks if the file has errors in the model
+ *
+ * @return true if the file has errors in the model and false otherwise
+ */
+ public boolean hasErrors()
+ {
+ boolean hasErrors = false;
+
+ for (IStatus status : getProblems())
+ {
+ if (status.getSeverity() == IStatus.ERROR)
+ {
+ hasErrors = true;
+ break;
+ }
+ }
+
+ return hasErrors;
+ }
+
+ public AndroidManifestNode getNode(NodeType nodeType)
+ {
+ AndroidManifestNode requiredNode = null;
+ AndroidManifestNode[] manifestChildren = null;
+ for (AndroidManifestNode node : rootNodes)
+ {
+ if (node instanceof ManifestNode)
+ {
+ manifestChildren = ((ManifestNode) node).getChildren();
+ break;
+ }
+ }
+
+ if ((manifestChildren != null) && (manifestChildren.length > 0))
+ {
+ for (AndroidManifestNode manifestChild : manifestChildren)
+ {
+ if (manifestChild.getNodeType().equals(nodeType))
+ {
+ requiredNode = manifestChild;
+ break;
+ }
+ }
+ }
+ return requiredNode;
+ }
+
+ /**
+ * This method sets the main activity of and android project be the class identified by {@code className}.
+ *
+ * @param className The name of the class to be set as the main activity.
+ * @param isMainActivity If true, the activity will be set as main activity. If false, the activity will no longer be a main activity.
+ * @return True if the activity exist, is declared on the manifest and was successfully set as the main activity. False otherwise.
+ * */
+ public boolean setAsMainActivity(String className, boolean isMainActivity)
+ {
+ boolean result = false;
+
+ List<ActivityNode> activityNodes = getApplicationNode().getActivityNodes();
+
+ for (ActivityNode activityNode : activityNodes)
+ {
+ if (activityNode.getName().equals(className))
+ {
+ result = activityNode.setAsMainActivity(isMainActivity);
+ break;
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Convenience method that returns the main activity of the application.
+ * The main activity is the one declared with the intent filter
+ * <action android:name="android.intent.action.MAIN"/>
+ * If more than one main activity is declared, then the first one declared is returned.
+ * This behavior follows the android behavior to choose the main activity.
+ * */
+ public ActivityNode getMainActivity()
+ {
+ ActivityNode mainActivity = null;
+ ApplicationNode appNode = getApplicationNode();
+ List<ActivityNode> activities = appNode.getActivityNodes();
+
+ for (ActivityNode activity : activities)
+ {
+ for (IntentFilterNode intent : activity.getIntentFilterNodes())
+ {
+ for (ActionNode actionNode : intent.getActionNodes())
+ {
+ if (actionNode.getNodeProperties().get("android:name")
+ .equals("android.intent.action.MAIN"))
+ {
+ mainActivity = activity;
+ break;
+ }
+ }
+ if (mainActivity != null)
+ {
+ break;
+ }
+ }
+ if (mainActivity != null)
+ {
+ break;
+ }
+ }
+
+ return mainActivity;
+ }
+
+}
diff --git a/src/plugins/common/src/com/motorola/studio/android/model/manifest/dom/AbstractBuildingBlockNode.java b/src/plugins/common/src/com/motorola/studio/android/model/manifest/dom/AbstractBuildingBlockNode.java
new file mode 100644
index 0000000..78b8d76
--- /dev/null
+++ b/src/plugins/common/src/com/motorola/studio/android/model/manifest/dom/AbstractBuildingBlockNode.java
@@ -0,0 +1,167 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.model.manifest.dom;
+
+/**
+ * Abstract class used to define the building blocks node classes
+ */
+public abstract class AbstractBuildingBlockNode extends AbstractIconLabelNameNode
+{
+ static
+ {
+ defaultProperties.add(PROP_ENABLED);
+ defaultProperties.add(PROP_EXPORTED);
+ defaultProperties.add(PROP_PERMISSION);
+ defaultProperties.add(PROP_PROCESS);
+ }
+
+ /**
+ * The enabled property
+ */
+ private Boolean propEnabled = null;
+
+ /**
+ * The exported property
+ */
+ private Boolean propExported = null;
+
+ /**
+ * The permission property
+ */
+ private String propPermission = null;
+
+ /**
+ * The process property
+ */
+ private String propProcess = null;
+
+ /**
+ * Default constructor
+ *
+ * @param name the name property. It must not be null.
+ */
+ protected AbstractBuildingBlockNode(String name)
+ {
+ super(name);
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.model.manifest.dom.AbstractIconLabelNameNode#addAdditionalProperties()
+ */
+ @Override
+ protected void addAdditionalProperties()
+ {
+ if (propEnabled != null)
+ {
+ properties.put(PROP_ENABLED, propEnabled.toString());
+ }
+
+ if (propExported != null)
+ {
+ properties.put(PROP_EXPORTED, propExported.toString());
+ }
+
+ if (propPermission != null)
+ {
+ properties.put(PROP_PERMISSION, propPermission);
+ }
+
+ if (propProcess != null)
+ {
+ properties.put(PROP_PROCESS, propProcess);
+ }
+ }
+
+ /**
+ * Gets the enabled property value
+ *
+ * @return the enabled property value
+ */
+ public Boolean getEnabled()
+ {
+ return propEnabled;
+ }
+
+ /**
+ * Sets the enabled property value. Set it to null to remove it.
+ *
+ * @param enabled the enabled property value
+ */
+ public void setEnabled(Boolean enabled)
+ {
+ this.propEnabled = enabled;
+ }
+
+ /**
+ * Gets the exported property value
+ *
+ * @return the exported property value
+ */
+ public Boolean getExported()
+ {
+ return propExported;
+ }
+
+ /**
+ * Sets the exported property value. Set it to null to remove it.
+ *
+ * @param exported the exported property value
+ */
+ public void setExported(Boolean exported)
+ {
+ this.propExported = exported;
+ }
+
+ /**
+ * Gets the permission property value
+ *
+ * @return the permission property value
+ */
+ public String getPermission()
+ {
+ return propPermission;
+ }
+
+ /**
+ * Sets the permission property value. Set it to null to remove it.
+ *
+ * @param permission the permission property value
+ */
+ public void setPermission(String permission)
+ {
+ this.propPermission = permission;
+ }
+
+ /**
+ * Gets the process property value
+ *
+ * @return the process property value
+ */
+ public String getProcess()
+ {
+ return propProcess;
+ }
+
+ /**
+ * Sets the process property value. Set it to null to remove it.
+ *
+ * @param process the process property value
+ */
+ public void setProcess(String process)
+ {
+ this.propProcess = process;
+ }
+}
diff --git a/src/plugins/common/src/com/motorola/studio/android/model/manifest/dom/AbstractIconLabelNameNode.java b/src/plugins/common/src/com/motorola/studio/android/model/manifest/dom/AbstractIconLabelNameNode.java
new file mode 100644
index 0000000..3fa72b9
--- /dev/null
+++ b/src/plugins/common/src/com/motorola/studio/android/model/manifest/dom/AbstractIconLabelNameNode.java
@@ -0,0 +1,167 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.model.manifest.dom;
+
+import java.util.Map;
+
+import org.eclipse.core.runtime.Assert;
+
+/**
+ * Abstract class to be used to create nodes that contains the properties
+ * "icon", "label" and "name"
+ */
+public abstract class AbstractIconLabelNameNode extends AndroidManifestNode implements
+ IAndroidManifestProperties
+{
+ static
+ {
+ // Adds the node properties to the list
+ defaultProperties.add(PROP_ICON);
+ defaultProperties.add(PROP_LABEL);
+ defaultProperties.add(PROP_NAME);
+ }
+
+ /**
+ * The icon property
+ */
+ protected String propIcon = null;
+
+ /**
+ * The label property
+ */
+ protected String propLabel = null;
+
+ /**
+ * The name property
+ */
+ protected String propName = null;
+
+ /**
+ * Default constructor
+ *
+ * @param name The name property. It must not be null.
+ * @param newProperties The new properties that are accepted by the child class
+ */
+ protected AbstractIconLabelNameNode(String name)
+ {
+ Assert.isLegal(name != null);
+
+ this.propName = name;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.model.manifest.dom.AndroidManifestNode#isNodeValid()
+ */
+ @Override
+ protected boolean isNodeValid()
+ {
+ return propName.trim().length() > 0;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.model.manifest.dom.AndroidManifestNode#getNodeProperties()
+ */
+ @Override
+ public Map<String, String> getNodeProperties()
+ {
+ properties.clear();
+
+ if ((propName != null) && (propName.trim().length() > 0))
+ {
+ properties.put(PROP_NAME, propName);
+ }
+
+ if ((propIcon != null) && (propIcon.trim().length() > 0))
+ {
+ properties.put(PROP_ICON, propIcon);
+ }
+
+ if ((propLabel != null) && (propLabel.trim().length() > 0))
+ {
+ properties.put(PROP_LABEL, propLabel);
+ }
+
+ addAdditionalProperties();
+
+ return properties;
+ }
+
+ /**
+ * Adds the additional properties to the properties variable
+ */
+ protected abstract void addAdditionalProperties();
+
+ /**
+ * Gets the icon property value
+ *
+ * @return the icon property value
+ */
+ public String getIcon()
+ {
+ return propIcon;
+ }
+
+ /**
+ * Sets the icon property value. Set it to null to remove it.
+ *
+ * @param icon the icon property value
+ */
+ public void setIcon(String icon)
+ {
+ this.propIcon = icon;
+ }
+
+ /**
+ * Gets the label property value
+ *
+ * @return the label property value
+ */
+ public String getLabel()
+ {
+ return propLabel;
+ }
+
+ /**
+ * Sets the label property value. Set it to null to remove it.
+ *
+ * @param label the label property value
+ */
+ public void setLabel(String label)
+ {
+ this.propLabel = label;
+ }
+
+ /**
+ * Gets the name property value
+ *
+ * @return the name property value
+ */
+ public String getName()
+ {
+ return propName;
+ }
+
+ /**
+ * Sets the name property value. It must not be set to null.
+ *
+ * @param name the name property value
+ */
+ public void setName(String name)
+ {
+ Assert.isLegal(name != null);
+ this.propName = name;
+ }
+}
diff --git a/src/plugins/common/src/com/motorola/studio/android/model/manifest/dom/AbstractSimpleNameNode.java b/src/plugins/common/src/com/motorola/studio/android/model/manifest/dom/AbstractSimpleNameNode.java
new file mode 100644
index 0000000..69796b4
--- /dev/null
+++ b/src/plugins/common/src/com/motorola/studio/android/model/manifest/dom/AbstractSimpleNameNode.java
@@ -0,0 +1,104 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.model.manifest.dom;
+
+import java.util.Map;
+
+import org.eclipse.core.runtime.Assert;
+
+/**
+ * Abstract class to be used to create nodes that contains only the property "name"
+ */
+public abstract class AbstractSimpleNameNode extends AndroidManifestNode implements
+ IAndroidManifestProperties
+{
+ static
+ {
+ defaultProperties.add(PROP_NAME);
+ }
+
+ /**
+ * The name property
+ */
+ private String propName = null;
+
+ /**
+ * Default constructor
+ *
+ * @param name the name property
+ */
+ public AbstractSimpleNameNode(String name)
+ {
+ Assert.isLegal(name != null);
+ this.propName = name;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.model.manifest.dom.AndroidManifestNode#canContains(com.motorola.studio.android.model.manifest.dom.AndroidManifestNode.NodeType)
+ */
+ @Override
+ protected boolean canContains(NodeType nodeType)
+ {
+ // Always returns false. This node can not contain children.
+ return false;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.model.manifest.dom.AndroidManifestNode#getNodeProperties()
+ */
+ @Override
+ public Map<String, String> getNodeProperties()
+ {
+ properties.clear();
+
+ if ((propName != null) && (propName.trim().length() > 0))
+ {
+ properties.put(PROP_NAME, propName);
+ }
+
+ return properties;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.model.manifest.dom.AndroidManifestNode#isNodeValid()
+ */
+ @Override
+ protected boolean isNodeValid()
+ {
+ return propName.trim().length() > 0;
+ }
+
+ /**
+ * Returns the name property
+ *
+ * @return the name property
+ */
+ public String getName()
+ {
+ return propName;
+ }
+
+ /**
+ * Sets the name property. This value must not be null.
+ *
+ * @param name the name property
+ */
+ public void setName(String name)
+ {
+ Assert.isLegal(name != null);
+ this.propName = name;
+ }
+}
diff --git a/src/plugins/common/src/com/motorola/studio/android/model/manifest/dom/AbstractUsesNode.java b/src/plugins/common/src/com/motorola/studio/android/model/manifest/dom/AbstractUsesNode.java
new file mode 100644
index 0000000..a95377f
--- /dev/null
+++ b/src/plugins/common/src/com/motorola/studio/android/model/manifest/dom/AbstractUsesNode.java
@@ -0,0 +1,118 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.model.manifest.dom;
+
+import java.util.Map;
+
+import org.eclipse.core.runtime.Assert;
+
+/**
+ * Abstract class to be used to create nodes that contains only the property "name"
+ */
+public abstract class AbstractUsesNode extends AndroidManifestNode implements
+ IAndroidManifestProperties
+{
+ static
+ {
+ defaultProperties.add(PROP_NAME);
+ defaultProperties.add(PROP_REQUIRED);
+ }
+
+ /**
+ * The name property
+ */
+ private String propName = null;
+
+ private boolean isRequired = true; //it is true by default, need to be declared explicitly to false
+
+ /**
+ * Default constructor
+ *
+ * @param name the name property
+ */
+ public AbstractUsesNode(String name)
+ {
+ Assert.isLegal(name != null);
+ this.propName = name;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.model.manifest.dom.AndroidManifestNode#canContains(com.motorola.studio.android.model.manifest.dom.AndroidManifestNode.NodeType)
+ */
+ @Override
+ protected boolean canContains(NodeType nodeType)
+ {
+ // Always returns false. This node can not contain children.
+ return false;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.model.manifest.dom.AndroidManifestNode#getNodeProperties()
+ */
+ @Override
+ public Map<String, String> getNodeProperties()
+ {
+ properties.clear();
+
+ if ((propName != null) && (propName.trim().length() > 0))
+ {
+ properties.put(PROP_NAME, propName);
+ }
+ properties.put(PROP_REQUIRED, Boolean.toString(isRequired));
+
+ return properties;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.model.manifest.dom.AndroidManifestNode#isNodeValid()
+ */
+ @Override
+ protected boolean isNodeValid()
+ {
+ return propName.trim().length() > 0;
+ }
+
+ /**
+ * Returns the name property
+ *
+ * @return the name property
+ */
+ public String getName()
+ {
+ return propName;
+ }
+
+ /**
+ * Sets the name property. This value must not be null.
+ *
+ * @param name the name property
+ */
+ public void setName(String name)
+ {
+ Assert.isLegal(name != null);
+ this.propName = name;
+ }
+
+ public boolean isRequired()
+ {
+ return isRequired;
+ }
+
+ public void setRequired(boolean isRequired)
+ {
+ this.isRequired = isRequired;
+ }
+}
diff --git a/src/plugins/common/src/com/motorola/studio/android/model/manifest/dom/ActionNode.java b/src/plugins/common/src/com/motorola/studio/android/model/manifest/dom/ActionNode.java
new file mode 100644
index 0000000..030aa9d
--- /dev/null
+++ b/src/plugins/common/src/com/motorola/studio/android/model/manifest/dom/ActionNode.java
@@ -0,0 +1,54 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.model.manifest.dom;
+
+import java.util.List;
+
+import org.eclipse.core.runtime.IStatus;
+
+/**
+ * Class that represents an <action> node on AndroidManifest.xml file
+ */
+public class ActionNode extends AbstractSimpleNameNode
+{
+ /**
+ * The default constructor
+ *
+ * @param name The name property
+ */
+ public ActionNode(String name)
+ {
+ super(name);
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.model.manifest.dom.AndroidManifestNode#getNodeType()
+ */
+ @Override
+ public NodeType getNodeType()
+ {
+ return NodeType.Action;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.model.manifest.dom.AndroidManifestNode#getSpecificNodeErrors()
+ */
+ @Override
+ protected List<IStatus> getSpecificNodeProblems()
+ {
+ return null;
+ }
+}
diff --git a/src/plugins/common/src/com/motorola/studio/android/model/manifest/dom/ActivityAliasNode.java b/src/plugins/common/src/com/motorola/studio/android/model/manifest/dom/ActivityAliasNode.java
new file mode 100644
index 0000000..09beb81
--- /dev/null
+++ b/src/plugins/common/src/com/motorola/studio/android/model/manifest/dom/ActivityAliasNode.java
@@ -0,0 +1,304 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.model.manifest.dom;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.IStatus;
+
+/**
+ * Class that represents an <activity-alias> node on AndroidManifest.xml file
+ */
+public class ActivityAliasNode extends AbstractIconLabelNameNode
+{
+ static
+ {
+ defaultProperties.add(PROP_ENABLED);
+ defaultProperties.add(PROP_EXPORTED);
+ defaultProperties.add(PROP_PERMISSION);
+ defaultProperties.add(PROP_TARGETACTIVITY);
+ }
+
+ /**
+ * The enabled property
+ */
+ private Boolean propEnabled = null;
+
+ /**
+ * The exported property
+ */
+ private Boolean propExported = null;
+
+ /**
+ * The permission property
+ */
+ private String propPermission = null;
+
+ /**
+ * The targetActivity property
+ */
+ private String propTargetActivity = null;
+
+ /**
+ * Default constructor
+ *
+ * @param name the name property (must not be null)
+ * @param targetActivity the targetActivity property (must not be null)
+ */
+ public ActivityAliasNode(String name, String targetActivity)
+ {
+ super(name);
+
+ Assert.isLegal(targetActivity != null);
+ this.propTargetActivity = targetActivity;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.model.manifest.dom.AndroidManifestNode#canContains(com.motorola.studio.android.model.manifest.dom.AndroidManifestNode.NodeType)
+ */
+ @Override
+ protected boolean canContains(NodeType nodeType)
+ {
+ return (nodeType == NodeType.IntentFilter) || (nodeType == NodeType.MetaData)
+ || (nodeType == NodeType.Comment);
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.model.manifest.dom.AbstractIconLabelNameNode#addAdditionalProperties()
+ */
+ @Override
+ protected void addAdditionalProperties()
+ {
+ properties.put(PROP_TARGETACTIVITY, propTargetActivity);
+
+ if (propEnabled != null)
+ {
+ properties.put(PROP_ENABLED, propEnabled.toString());
+ }
+
+ if (propExported != null)
+ {
+ properties.put(PROP_EXPORTED, propExported.toString());
+ }
+
+ if (propPermission != null)
+ {
+ properties.put(PROP_PERMISSION, propPermission);
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.model.manifest.dom.AndroidManifestNode#getNodeType()
+ */
+ @Override
+ public NodeType getNodeType()
+ {
+ return NodeType.ActivityAlias;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.model.manifest.dom.AndroidManifestNode#isNodeValid()
+ */
+ @Override
+ protected boolean isNodeValid()
+ {
+ return super.isNodeValid() && (propTargetActivity.trim().length() > 0);
+ }
+
+ /**
+ * Gets the enabled property value
+ *
+ * @return the enabled property value
+ */
+ public Boolean getEnabled()
+ {
+ return propEnabled;
+ }
+
+ /**
+ * Sets the enabled property value. Set it to null to remove it.
+ *
+ * @param enabled the enabled property value
+ */
+ public void setEnabled(Boolean enabled)
+ {
+ this.propEnabled = enabled;
+ }
+
+ /**
+ * Gets the exported property value
+ *
+ * @return the exported property value
+ */
+ public Boolean getExported()
+ {
+ return propExported;
+ }
+
+ /**
+ * Sets the exported property value. Set it to null to remove it.
+ *
+ * @param exported the exported property value
+ */
+ public void setExported(Boolean exported)
+ {
+ this.propExported = exported;
+ }
+
+ /**
+ * Gets the permission property value
+ *
+ * @return the permission property value
+ */
+ public String getPermission()
+ {
+ return propPermission;
+ }
+
+ /**
+ * Sets the permission property value. Set it to null to remove it.
+ *
+ * @param permission the permission property value
+ */
+ public void setPermission(String permission)
+ {
+ this.propPermission = permission;
+ }
+
+ /**
+ * Gets the targetActivity property value
+ *
+ * @return the targetActivity property value
+ */
+ public String getTargetActivity()
+ {
+ return propTargetActivity;
+ }
+
+ /**
+ * Sets the targetActivity property value.
+ *
+ * @param targetActivity the targetActivity property value
+ */
+ public void setTargetActivity(String targetActivity)
+ {
+ Assert.isLegal(targetActivity != null);
+ this.propTargetActivity = targetActivity;
+ }
+
+ /**
+ * Adds an Intent Filter Node to the Activity Alias Node
+ *
+ * @param intentFilter The Intent Filter Node
+ */
+ public void addIntentFilterNode(IntentFilterNode intentFilter)
+ {
+ if (intentFilter != null)
+ {
+ if (!children.contains(intentFilter))
+ {
+ children.add(intentFilter);
+ }
+ }
+ }
+
+ /**
+ * Retrieves all Intent Filter Nodes from the Activity Alias Node
+ *
+ * @return all Intent Filter Nodes from the Activity Alias Node
+ */
+ public List<IntentFilterNode> getIntentFilterNodes()
+ {
+ List<IntentFilterNode> intentFilters = new LinkedList<IntentFilterNode>();
+
+ for (AndroidManifestNode node : getAllChildrenFromType(NodeType.IntentFilter))
+ {
+ intentFilters.add((IntentFilterNode) node);
+ }
+
+ return intentFilters;
+ }
+
+ /**
+ * Removes an Intent Filter Node from the Activity Alias Node
+ *
+ * @param intentFilter the Intent Filter Node to be removed
+ */
+ public void removeIntentFilterNode(IntentFilterNode intentFilter)
+ {
+ if (intentFilter != null)
+ {
+ children.remove(intentFilter);
+ }
+ }
+
+ /**
+ * Adds a Metadata Node to the Activity Alias Node
+ *
+ * @param metadata The Metadata Node
+ */
+ public void addMetadataNode(MetadataNode metadata)
+ {
+ if (metadata != null)
+ {
+ if (!children.contains(metadata))
+ {
+ children.add(metadata);
+ }
+ }
+ }
+
+ /**
+ * Retrieves all Metadata Nodes from the Activity Alias Node
+ *
+ * @return all Metadata Nodes from the Activity Alias Node
+ */
+ public List<MetadataNode> getMetadataNodes()
+ {
+ List<MetadataNode> metadatas = new LinkedList<MetadataNode>();
+
+ for (AndroidManifestNode node : getAllChildrenFromType(NodeType.MetaData))
+ {
+ metadatas.add((MetadataNode) node);
+ }
+
+ return metadatas;
+ }
+
+ /**
+ * Removes a Metadata Node from the Activity Alias Node
+ *
+ * @param metadata the Metadata Node to be removed
+ */
+ public void removeMetadataNode(MetadataNode metadata)
+ {
+ if (metadata != null)
+ {
+ children.remove(metadata);
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.model.manifest.dom.AndroidManifestNode#getSpecificNodeErrors()
+ */
+ @Override
+ protected List<IStatus> getSpecificNodeProblems()
+ {
+ return null;
+ }
+}
diff --git a/src/plugins/common/src/com/motorola/studio/android/model/manifest/dom/ActivityNode.java b/src/plugins/common/src/com/motorola/studio/android/model/manifest/dom/ActivityNode.java
new file mode 100644
index 0000000..c410067
--- /dev/null
+++ b/src/plugins/common/src/com/motorola/studio/android/model/manifest/dom/ActivityNode.java
@@ -0,0 +1,882 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.model.manifest.dom;
+
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.core.runtime.IStatus;
+
+import com.motorola.studio.android.common.IAndroidConstants;
+
+/**
+ * Class that represents an <activity> node on AndroidManifest.xml file
+ */
+public class ActivityNode extends AbstractBuildingBlockNode
+{
+ static
+ {
+ defaultProperties.add(PROP_ALLOWTASKREPARENTING);
+ defaultProperties.add(PROP_ALWAYSRETAINTASKSTATE);
+ defaultProperties.add(PROP_CLEARTASKONLAUNCH);
+ defaultProperties.add(PROP_CONFIGCHANGES);
+ defaultProperties.add(PROP_EXCLUDEFROMRECENTS);
+ defaultProperties.add(PROP_FINISHONTASKLAUNCH);
+ defaultProperties.add(PROP_LAUNCHMODE);
+ defaultProperties.add(PROP_MULTIPROCESS);
+ defaultProperties.add(PROP_SCREENORIENTATION);
+ defaultProperties.add(PROP_STATENOTNEEDED);
+ defaultProperties.add(PROP_TASKAFFINITY);
+ defaultProperties.add(PROP_THEME);
+ }
+
+ /**
+ * Enumeration for configChanges property
+ */
+ public static enum ConfigChanges
+ {
+ mcc, mnc, locale, touchscreen, keyboard, keyboardHidden, navigation, orientation, fontscale
+ }
+
+ /**
+ * Enumeration for launchMode property
+ */
+ public static enum LaunchMode
+ {
+ standard, singleTop, singleTask, singleInstance
+ }
+
+ /**
+ * Enumeration for screenOrientation property
+ */
+ public static enum ScreenOrientation
+ {
+ unspecified, user, behind, landscape, portrait, sensor, nonsensor
+ }
+
+ /**
+ * Map to resolve the string<->enumeration association of configChanges property
+ */
+ private static Map<String, ConfigChanges> configChanges;
+
+ /**
+ * Map to resolve the string<->enumeration association of launchMode property
+ */
+ private static Map<String, LaunchMode> launchModes;
+
+ /**
+ * Map to resolve the string<->enumeration association of screenOrientation property
+ */
+ private static Map<String, ScreenOrientation> screenOrientations;
+
+ static
+ {
+ configChanges = new HashMap<String, ConfigChanges>();
+
+ // Loads the map for configChanges
+ for (ConfigChanges configChange : ConfigChanges.values())
+ {
+ configChanges.put(configChange.toString().toLowerCase(), configChange);
+ }
+
+ launchModes = new HashMap<String, LaunchMode>();
+
+ // Loads the map for launchMode
+ for (LaunchMode launchMode : LaunchMode.values())
+ {
+ launchModes.put(launchMode.toString().toLowerCase(), launchMode);
+ }
+
+ screenOrientations = new HashMap<String, ScreenOrientation>();
+
+ // Loads the map for screenOrientation
+ for (ScreenOrientation screenOrientation : ScreenOrientation.values())
+ {
+ screenOrientations.put(screenOrientation.toString().toLowerCase(), screenOrientation);
+ }
+ }
+
+ /**
+ * Gets the configChange parameter name from a given ConfigChanges enumeration value
+ *
+ * @param configChange the enumeration value
+ * @return the configChange parameter name
+ */
+ public static String getConfigChangeName(ConfigChanges configChange)
+ {
+ String name = "";
+
+ if (configChange != null)
+ {
+ name = configChange.toString();
+ }
+
+ return name;
+ }
+
+ /**
+ * Gets the enumeration value of the ConfigChanges enumeration from a given name
+ *
+ * @param configChangeName the configChanges name
+ * @return the enumeration value of the ConfigChanges enumeration
+ */
+ public static ConfigChanges getConfigChangeFromName(String configChangeName)
+ {
+ ConfigChanges configChange = null;
+
+ if (configChangeName != null)
+ {
+ String ccn = configChangeName.trim().toLowerCase();
+ configChange = configChanges.get(ccn);
+ }
+
+ return configChange;
+ }
+
+ /**
+ * Gets the launchMode parameter name from a given LaunchMode enumeration value
+ *
+ * @param launchMode the enumeration value
+ * @return the LaunchMode parameter name
+ */
+ public static String getLaunchModeName(LaunchMode launchMode)
+ {
+ String name = "";
+
+ if (launchMode != null)
+ {
+ name = launchMode.toString();
+ }
+
+ return name;
+ }
+
+ /**
+ * Gets the enumeration value of the LaunchMode enumeration from a given name
+ *
+ * @param launchModeName the launchMode name
+ * @return the enumeration value of the LaunchMode enumeration
+ */
+ public static LaunchMode getLaunchModeFromName(String launchModeName)
+ {
+ LaunchMode launchMode = null;
+
+ if (launchModeName != null)
+ {
+ String lmn = launchModeName.trim().toLowerCase();
+ launchMode = launchModes.get(lmn);
+ }
+
+ return launchMode;
+ }
+
+ /**
+ * Gets the screenOrientation parameter name from a given ScreenOrientation enumeration value
+ *
+ * @param screenOrientation the enumeration value
+ * @return the ScreenOrientation parameter name
+ */
+ public static String getScreenOrientationName(ScreenOrientation screenOrientation)
+ {
+ String name = "";
+
+ if (screenOrientation != null)
+ {
+ name = screenOrientation.toString();
+ }
+
+ return name;
+ }
+
+ /**
+ * Gets the enumeration value of the ScreenOrientation enumeration from a given name
+ *
+ * @param screenOrientationName the screenOrientation name
+ * @return the enumeration value of the ScreenOrientation enumeration
+ */
+ public static ScreenOrientation getScreenOrientationFromName(String screenOrientationName)
+ {
+ ScreenOrientation screenOrientation = null;
+
+ if (screenOrientationName != null)
+ {
+ String son = screenOrientationName.trim().toLowerCase();
+ screenOrientation = screenOrientations.get(son);
+ }
+
+ return screenOrientation;
+ }
+
+ /**
+ * The allowTaskReparenting property
+ */
+ private Boolean propAllowTaskReparenting = null;
+
+ /**
+ * The alwaysRetainTaskState property
+ */
+ private Boolean propAlwaysRetainTaskState = null;
+
+ /**
+ * The clearTaskOnLaunch property
+ */
+ private Boolean propClearTaskOnLaunch = null;
+
+ /**
+ * The configChanges property (can hold more than one value)
+ */
+ private final List<ConfigChanges> propConfigChanges = new LinkedList<ConfigChanges>();
+
+ /**
+ * The excludeFromRecents property
+ */
+ private Boolean propExcludeFromRecents = null;
+
+ /**
+ * The finishOnTaskLaunch property
+ */
+ private Boolean propFinishOnTaskLaunch = null;
+
+ /**
+ * The launchMode property
+ */
+ private LaunchMode propLaunchMode = null;
+
+ /**
+ * The multiProcess property
+ */
+ private Boolean propMultiprocess = null;
+
+ /**
+ * The screenOrientation property
+ */
+ private ScreenOrientation propScreenOrientation = null;
+
+ /**
+ * The stateNotNeeded property
+ */
+ private Boolean propStateNotNeeded = null;
+
+ /**
+ * The taskAffinity property
+ */
+ private String propTaskAffinity = null;
+
+ /**
+ * The theme property
+ */
+ private String propTheme = null;
+
+ /**
+ * Default constructor
+ *
+ * @param name The name property. It must not be null.
+ */
+ public ActivityNode(String name)
+ {
+ super(name);
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.model.manifest.dom.AndroidManifestNode#canContains(com.motorola.studio.android.model.manifest.dom.AndroidManifestNode.NodeType)
+ */
+ @Override
+ protected boolean canContains(NodeType nodeType)
+ {
+ return (nodeType == NodeType.IntentFilter) || (nodeType == NodeType.MetaData)
+ || (nodeType == NodeType.Comment);
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.model.manifest.dom.AbstractIconLabelNameNode#addAdditionalProperties()
+ */
+ @Override
+ protected void addAdditionalProperties()
+ {
+ super.addAdditionalProperties();
+
+ if (propAllowTaskReparenting != null)
+ {
+ properties.put(PROP_ALLOWTASKREPARENTING, propAllowTaskReparenting.toString());
+ }
+
+ if (propAlwaysRetainTaskState != null)
+ {
+ properties.put(PROP_ALWAYSRETAINTASKSTATE, propAlwaysRetainTaskState.toString());
+ }
+
+ if (propClearTaskOnLaunch != null)
+ {
+ properties.put(PROP_CLEARTASKONLAUNCH, propClearTaskOnLaunch.toString());
+ }
+
+ if ((propConfigChanges != null) && !propConfigChanges.isEmpty())
+ {
+ String configChangesString = "";
+
+ for (int i = 0; i < (propConfigChanges.size() - 1); i++)
+ {
+ configChangesString += getConfigChangeName(propConfigChanges.get(i)) + "|";
+ }
+
+ configChangesString +=
+ getConfigChangeName(propConfigChanges.get(propConfigChanges.size() - 1));
+
+ properties.put(PROP_CONFIGCHANGES, configChangesString);
+ }
+
+ if (propExcludeFromRecents != null)
+ {
+ properties.put(PROP_EXCLUDEFROMRECENTS, propExcludeFromRecents.toString());
+ }
+
+ if (propFinishOnTaskLaunch != null)
+ {
+ properties.put(PROP_FINISHONTASKLAUNCH, propFinishOnTaskLaunch.toString());
+ }
+
+ if (propLaunchMode != null)
+ {
+ properties.put(PROP_LAUNCHMODE, getLaunchModeName(propLaunchMode));
+ }
+
+ if (propMultiprocess != null)
+ {
+ properties.put(PROP_MULTIPROCESS, propMultiprocess.toString());
+ }
+
+ if (propScreenOrientation != null)
+ {
+ properties.put(PROP_SCREENORIENTATION, getScreenOrientationName(propScreenOrientation));
+ }
+
+ if (propStateNotNeeded != null)
+ {
+ properties.put(PROP_STATENOTNEEDED, propStateNotNeeded.toString());
+ }
+
+ if (propTaskAffinity != null)
+ {
+ properties.put(PROP_TASKAFFINITY, propTaskAffinity);
+ }
+
+ if (propTheme != null)
+ {
+ properties.put(PROP_THEME, propTheme);
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.model.manifest.dom.AndroidManifestNode#getNodeType()
+ */
+ @Override
+ public NodeType getNodeType()
+ {
+ return NodeType.Activity;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.model.manifest.dom.AndroidManifestNode#isNodeValid()
+ */
+ @Override
+ protected boolean isNodeValid()
+ {
+ return super.isNodeValid();
+ }
+
+ /**
+ * Gets the allowTaskReparenting property value
+ *
+ * @return the allowTaskReparenting property value
+ */
+ public Boolean getAllowTaskReparenting()
+ {
+ return propAllowTaskReparenting;
+ }
+
+ /**
+ * Sets the allowTaskReparenting property value. Set it to null to remove it.
+ *
+ * @param allowTaskReparenting the allowTaskReparenting property value
+ */
+ public void setAllowTaskReparenting(Boolean allowTaskReparenting)
+ {
+ this.propAllowTaskReparenting = allowTaskReparenting;
+ }
+
+ /**
+ * Gets the alwaysRetainTaskState property value
+ *
+ * @return the alwaysRetainTaskState property value
+ */
+ public Boolean getAlwaysRetainTaskState()
+ {
+ return propAlwaysRetainTaskState;
+ }
+
+ /**
+ * Sets the alwaysRetainTaskState property value. Set it to null to remove it.
+ *
+ * @param alwaysRetainTaskState the alwaysRetainTaskState property value
+ */
+ public void setAlwaysRetainTaskState(Boolean alwaysRetainTaskState)
+ {
+ this.propAlwaysRetainTaskState = alwaysRetainTaskState;
+ }
+
+ /**
+ * Gets the clearTaskOnLaunch property value
+ *
+ * @return the clearTaskOnLaunch property value
+ */
+ public Boolean getClearTaskOnLaunch()
+ {
+ return propClearTaskOnLaunch;
+ }
+
+ /**
+ * Sets the clearTaskOnLaunch property value. Set it to null to remove it.
+ *
+ * @param clearTaskOnLaunch the clearTaskOnLaunch property value
+ */
+ public void setClearTaskOnLaunch(Boolean clearTaskOnLaunch)
+ {
+ this.propClearTaskOnLaunch = clearTaskOnLaunch;
+ }
+
+ /**
+ * Gets the configChanges property value as an array
+ *
+ * @return the configChanges property value as an array
+ */
+ public ConfigChanges[] getConfigChanges()
+ {
+ ConfigChanges[] configChanges = null;
+
+ if (propConfigChanges != null)
+ {
+ configChanges = new ConfigChanges[propConfigChanges.size()];
+ configChanges = propConfigChanges.toArray(configChanges);
+ }
+
+ return configChanges;
+ }
+
+ /**
+ * Adds a new config change to the configChanges property
+ *
+ * @param configChanges the new config change to be added
+ */
+ public void addConfigChanges(ConfigChanges configChanges)
+ {
+ if (configChanges != null)
+ {
+ if (!propConfigChanges.contains(configChanges))
+ {
+ propConfigChanges.add(configChanges);
+ }
+ }
+ }
+
+ /**
+ * Gets the excludeFromRecents property value
+ *
+ * @return the excludeFromRecents property value
+ */
+ public Boolean getExcludeFromRecents()
+ {
+ return propExcludeFromRecents;
+ }
+
+ /**
+ * Sets the excludeFromRecents property value. Set it to null to remove it.
+ *
+ * @param excludeFromRecents the excludeFromRecents property value
+ */
+ public void setExcludeFromRecents(Boolean excludeFromRecents)
+ {
+ this.propExcludeFromRecents = excludeFromRecents;
+ }
+
+ /**
+ * Gets the finishOnTaskLaunch property value
+ *
+ * @return the finishOnTaskLaunch property value
+ */
+ public Boolean getFinishOnTaskLaunch()
+ {
+ return propFinishOnTaskLaunch;
+ }
+
+ /**
+ * Sets the finishOnTaskLaunch property value. Set it to null to remove it.
+ *
+ * @param finishOnTaskLaunch the finishOnTaskLaunch property value
+ */
+ public void setFinishOnTaskLaunch(Boolean finishOnTaskLaunch)
+ {
+ this.propFinishOnTaskLaunch = finishOnTaskLaunch;
+ }
+
+ /**
+ * Gets the launchMode property value
+ *
+ * @return the launchMode property value
+ */
+ public LaunchMode getLaunchMode()
+ {
+ return propLaunchMode;
+ }
+
+ /**
+ * Sets the launchMode property value. Set it to null to remove it.
+ *
+ * @param launchMode the launchMode property value
+ */
+ public void setLaunchMode(LaunchMode launchMode)
+ {
+ this.propLaunchMode = launchMode;
+ }
+
+ /**
+ * Gets the multiProcess property value
+ *
+ * @return the multiProcess property value
+ */
+ public Boolean getMultiprocess()
+ {
+ return propMultiprocess;
+ }
+
+ /**
+ * Sets the multiProcess property value. Set it to null to remove it.
+ *
+ * @param multiProcess the multiProcess property value
+ */
+ public void setMultiprocess(Boolean multiProcess)
+ {
+ this.propMultiprocess = multiProcess;
+ }
+
+ /**
+ * Gets the screenOrientation property value
+ *
+ * @return the screenOrientation property value
+ */
+ public ScreenOrientation getScreenOrientation()
+ {
+ return propScreenOrientation;
+ }
+
+ /**
+ * Sets the screenOrientation property value. Set it to null to remove it.
+ *
+ * @param screenOrientation the screenOrientation property value
+ */
+ public void setScreenOrientation(ScreenOrientation screenOrientation)
+ {
+ this.propScreenOrientation = screenOrientation;
+ }
+
+ /**
+ * Gets the stateNotNeeded property value
+ *
+ * @return the stateNotNeeded property value
+ */
+ public Boolean getStateNotNeeded()
+ {
+ return propStateNotNeeded;
+ }
+
+ /**
+ * Sets the stateNotNeeded property value. Set it to null to remove it.
+ *
+ * @param stateNotNeeded the stateNotNeeded property value
+ */
+ public void setStateNotNeeded(Boolean stateNotNeeded)
+ {
+ this.propStateNotNeeded = stateNotNeeded;
+ }
+
+ /**
+ * Gets the taskAffinity property value
+ *
+ * @return the taskAffinity property value
+ */
+ public String getTaskAffinity()
+ {
+ return propTaskAffinity;
+ }
+
+ /**
+ * Sets the taskAffinity property value. Set it to null to remove it.
+ *
+ * @param taskAffinity the taskAffinity property value
+ */
+ public void setTaskAffinity(String taskAffinity)
+ {
+ this.propTaskAffinity = taskAffinity;
+ }
+
+ /**
+ * Gets the theme property value
+ *
+ * @return the theme property value
+ */
+ public String getTheme()
+ {
+ return propTheme;
+ }
+
+ /**
+ * Sets the theme property value. Set it to null to remove it.
+ *
+ * @param theme the theme property value
+ */
+ public void setTheme(String theme)
+ {
+ this.propTheme = theme;
+ }
+
+ /**
+ * Adds an Intent Filter Node to the Activity Node
+ *
+ * @param intentFilter The Intent Filter Node
+ */
+ public void addIntentFilterNode(IntentFilterNode intentFilter)
+ {
+ if (intentFilter != null)
+ {
+ if (!children.contains(intentFilter))
+ {
+ children.add(intentFilter);
+ }
+ }
+ }
+
+ /**
+ * Retrieves all Intent Filter Nodes from the Activity Node
+ *
+ * @return all Intent Filter Nodes from the Activity Node
+ */
+ public List<IntentFilterNode> getIntentFilterNodes()
+ {
+ List<IntentFilterNode> intentFilters = new LinkedList<IntentFilterNode>();
+
+ for (AndroidManifestNode node : getAllChildrenFromType(NodeType.IntentFilter))
+ {
+ intentFilters.add((IntentFilterNode) node);
+ }
+
+ return intentFilters;
+ }
+
+ /**
+ * Removes an Intent Filter Node from the Activity Node
+ *
+ * @param intentFilter the Intent Filter Node to be removed
+ */
+ public void removeIntentFilterNode(IntentFilterNode intentFilter)
+ {
+ if (intentFilter != null)
+ {
+ children.remove(intentFilter);
+ }
+ }
+
+ /**
+ * If parameter {@code isMainActivity} is true, then the intent-filters that represent main activities are added to this activity.
+ * If parameter {@code isMainActivity} is false, then the intent-filters that represent main activities are removed from this activity.
+ * @param isMainActivity True is this activity should be set as the main activity. False if the activity should not be set as the main activity.
+ * @return True if the operation requested was successfully performed. False otherwise. Possible scenarios are:
+ * This activity is the main activity and {@code isMainActivity} is true. In this case, this method returns true.
+ * This activity is the main activity and {@code isMainActivity} is false. In this case, this method returns true if the intent-filters were successfully removed from this activity node, false otherwise.
+ * This activity is not the main activity and {@code isMainActivity} is true. In this case, this method returns true if the required intent-filters were successfully added to this activity node, false otherwise.
+ * This activity is not the main activity and {@code isMainActivity} is false. In this case, this method returns true.
+ * */
+ public boolean setAsMainActivity(boolean isMainActivity)
+ {
+ boolean result = false;
+
+ // check if this this activity should be a main activity or not
+ if (isMainActivity)
+ {
+ // set this activity to be a main activity
+ if (this.isMainActivity())
+ {
+ // this already is a main activity
+ result = true;
+ }
+ else
+ {
+ // set as main activity
+ IntentFilterNode intentFilterNode = new IntentFilterNode();
+ ActionNode actionMainNode = new ActionNode(IAndroidConstants.ACTION_MAIN);
+ CategoryNode categoryLauncherNode =
+ new CategoryNode(IAndroidConstants.CATEGORY_LAUNCHER);
+
+ intentFilterNode.addActionNode(actionMainNode);
+ intentFilterNode.addCategoryNode(categoryLauncherNode);
+ this.addIntentFilterNode(intentFilterNode);
+
+ result = true;
+ }
+ }
+ else
+ {
+ // unset this activity as main activity
+ if (!this.isMainActivity())
+ {
+ //this activity is not a main activity
+ result = true;
+ }
+ else
+ {
+ // unset this activity as main activity, i.e., remove action main and category launcher from its intent-filters
+ for (IntentFilterNode intentFilterNode : getIntentFilterNodes())
+ {
+ ActionNode actionMainNode =
+ intentFilterNode.getActionNode(IAndroidConstants.ACTION_MAIN);
+ CategoryNode categoryLauncherNode =
+ intentFilterNode.getCategoryNode(IAndroidConstants.CATEGORY_LAUNCHER);
+
+ if ((actionMainNode != null) && (categoryLauncherNode != null))
+ {
+ // effectivelly remove the nodes
+ intentFilterNode.removeActionNode(actionMainNode);
+ intentFilterNode.removeCategoryNode(categoryLauncherNode);
+
+ if (intentFilterNode.isEmpty())
+ {
+ //also remove the intent-filter node if it was left empty
+ this.removeIntentFilterNode(intentFilterNode);
+ }
+
+ result = true;
+ }
+ }
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Check if this activity is set as the main activity, i.e., if it has the required intent-filter that represent main activities.
+ * @return True if this activity is the main activity of the project. False otherwise.
+ * */
+ public boolean isMainActivity()
+ {
+ boolean isMainActivity = false;
+ boolean hasActionMain = false;
+ boolean hasCategoryLauncher = false;
+
+ //iterate over intent-filter nodes
+ //looking for action and category nodes that represents main activities
+ for (IntentFilterNode intent : getIntentFilterNodes())
+ {
+ hasActionMain = false;
+ hasCategoryLauncher = false;
+
+ //iterate over action nodes
+ for (ActionNode actionNode : intent.getActionNodes())
+ {
+ if (actionNode.getName().equals(IAndroidConstants.ACTION_MAIN))
+ {
+ //action node for main activities
+ hasActionMain = true;
+ break;
+ }
+ }
+
+ //iterate over category nodes
+ for (CategoryNode categoryNode : intent.getCategoryNodes())
+ {
+ if (categoryNode.getName().equals(IAndroidConstants.CATEGORY_LAUNCHER))
+ {
+ //category node for main activities
+ hasCategoryLauncher = true;
+ }
+ }
+
+ //check if the intent-filter has the action and category nodes that represent main activities
+ if (hasActionMain && hasCategoryLauncher)
+ {
+ isMainActivity = true;
+ break;
+ }
+ }
+
+ return isMainActivity;
+ }
+
+ /**
+ * Adds a Metadata Node to the Activity Node
+ *
+ * @param metadata The Metadata Node
+ */
+ public void addMetadataNode(MetadataNode metadata)
+ {
+ if (metadata != null)
+ {
+ if (!children.contains(metadata))
+ {
+ children.add(metadata);
+ }
+ }
+ }
+
+ /**
+ * Retrieves all Metadata Nodes from the Activity Node
+ *
+ * @return all Metadata Nodes from the Activity Node
+ */
+ public List<MetadataNode> getMetadataNodes()
+ {
+ List<MetadataNode> metadatas = new LinkedList<MetadataNode>();
+
+ for (AndroidManifestNode node : getAllChildrenFromType(NodeType.MetaData))
+ {
+ metadatas.add((MetadataNode) node);
+ }
+
+ return metadatas;
+ }
+
+ /**
+ * Removes a Metadata Node from the Activity Node
+ *
+ * @param metadata the Metadata Node to be removed
+ */
+ public void removeMetadataNode(MetadataNode metadata)
+ {
+ if (metadata != null)
+ {
+ children.remove(metadata);
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.model.manifest.dom.AndroidManifestNode#getSpecificNodeErrors()
+ */
+ @Override
+ protected List<IStatus> getSpecificNodeProblems()
+ {
+ return null;
+ }
+}
diff --git a/src/plugins/common/src/com/motorola/studio/android/model/manifest/dom/AndroidManifestNode.java b/src/plugins/common/src/com/motorola/studio/android/model/manifest/dom/AndroidManifestNode.java
new file mode 100644
index 0000000..43caec5
--- /dev/null
+++ b/src/plugins/common/src/com/motorola/studio/android/model/manifest/dom/AndroidManifestNode.java
@@ -0,0 +1,513 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.model.manifest.dom;
+
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.osgi.util.NLS;
+
+import com.motorola.studio.android.common.CommonPlugin;
+import com.motorola.studio.android.common.utilities.i18n.UtilitiesNLS;
+
+/**
+ * Abstract class that represents a xml node on AndroidManifest.xml file.
+ */
+public abstract class AndroidManifestNode
+{
+ /**
+ * List that contains the node properties
+ */
+ protected static final List<String> defaultProperties = new LinkedList<String>();
+
+ /**
+ * Array that contains all node properties
+ */
+ private String[] ALL_PROPERTIES = null;
+
+ /**
+ * Enumeration to identify all types of xml nodes
+ */
+ public enum NodeType
+ {
+ Action, Activity, ActivityAlias, Application, Category, Data, GrantUriPermission,
+ Instrumentation, IntentFilter, Manifest, MetaData, Permission, PermissionGroup,
+ PermissionTree, Provider, Receiver, Service, UsesLibrary, UsesPermission, UsesSdk, Comment,
+ Unknown, UsesFeature
+ }
+
+ /**
+ * Retrieves the node name from its type. This name is the same as shown on AndroidManifest.xml file
+ *
+ * @param nodeType The node type
+ *
+ * @return the node name
+ */
+ public static String getNodeName(NodeType nodeType)
+ {
+ String nodeName;
+
+ switch (nodeType)
+ {
+ case Action:
+ nodeName = "action";
+ break;
+ case Activity:
+ nodeName = "activity";
+ break;
+ case ActivityAlias:
+ nodeName = "activity-alias";
+ break;
+ case Application:
+ nodeName = "application";
+ break;
+ case Category:
+ nodeName = "category";
+ break;
+ case Data:
+ nodeName = "data";
+ break;
+ case GrantUriPermission:
+ nodeName = "grant-uri-permission";
+ break;
+ case Instrumentation:
+ nodeName = "instrumentation";
+ break;
+ case IntentFilter:
+ nodeName = "intent-filter";
+ break;
+ case Manifest:
+ nodeName = "manifest";
+ break;
+ case MetaData:
+ nodeName = "meta-data";
+ break;
+ case Permission:
+ nodeName = "permission";
+ break;
+ case PermissionGroup:
+ nodeName = "permission-group";
+ break;
+ case PermissionTree:
+ nodeName = "permission-tree";
+ break;
+ case Provider:
+ nodeName = "provider";
+ break;
+ case Receiver:
+ nodeName = "receiver";
+ break;
+ case Service:
+ nodeName = "service";
+ break;
+ case UsesLibrary:
+ nodeName = "uses-library";
+ break;
+ case UsesPermission:
+ nodeName = "uses-permission";
+ break;
+ case UsesSdk:
+ nodeName = "uses-sdk";
+ break;
+ case Comment:
+ nodeName = "comment";
+ break;
+ case UsesFeature:
+ nodeName = "uses-feature";
+ break;
+ default:
+ nodeName = "unknown";
+ }
+
+ return nodeName;
+ }
+
+ /**
+ * All valid children nodes
+ */
+ protected final List<AndroidManifestNode> children = new LinkedList<AndroidManifestNode>();
+
+ /**
+ * All valid parent nodes
+ */
+ protected AndroidManifestNode parent = null;
+
+ /**
+ * All valid node properties
+ */
+ protected final Map<String, String> properties = new HashMap<String, String>();
+
+ /**
+ * All invalid children nodes
+ */
+ protected final List<AndroidManifestNode> unknownChildren =
+ new LinkedList<AndroidManifestNode>();
+
+ /**
+ * All invalid children nodes
+ */
+ protected final Map<String, String> unknownProperties = new HashMap<String, String>();
+
+ /**
+ * Checks if a node type can be a child of this node
+ *
+ * @param nodeType The node type
+ * @return true if the type is accept as child or false otherwise
+ */
+ protected abstract boolean canContains(NodeType nodeType);
+
+ /**
+ * Checks if the node is valid, i.e., contains all required information to be
+ * valid on AndroidManifest.xml file
+ *
+ * @return true if the node is valid or false otherwise
+ */
+ protected abstract boolean isNodeValid();
+
+ /**
+ * Retrieves the node type
+ *
+ * @return the node type
+ */
+ public abstract NodeType getNodeType();
+
+ /**
+ * Retrieves all node properties, ready to be written to the AndroidManifest.xml file
+ *
+ * @return all node properties, ready to be written to the AndroidManifest.xml file
+ */
+ public abstract Map<String, String> getNodeProperties();
+
+ /**
+ * Retrieves the specific node errors. These errors are related to the
+ * manifest model, excluding those related to unknown child nodes and
+ * unknown attributes.
+ * For example, this method can return an error related to the lack of
+ * a required child node.
+ *
+ * @return the specific node errors.
+ */
+ protected abstract List<IStatus> getSpecificNodeProblems();
+
+ /**
+ * Default constructor
+ */
+ protected AndroidManifestNode()
+ {
+ // Do nothing
+ }
+
+ /**
+ * Retrieves the node name from its type. This name is the same as shown on AndroidManifest.xml file
+ *
+ * @return the node name
+ */
+ public String getNodeName()
+ {
+ return getNodeName(getNodeType());
+ }
+
+ /**
+ * Adds a child to the node. If the node is accepted as valid child, it will
+ * be treated this way. Otherwise, the node is treated as unknown.
+ *
+ * @param child The child node to be added
+ */
+ public void addChild(AndroidManifestNode child)
+ {
+ Assert.isLegal(child != null);
+
+ if (canContains(child.getNodeType()))
+ {
+ children.add(child);
+
+ }
+ else
+ {
+ unknownChildren.add(child);
+
+ }
+
+ // Set the parent
+ child.setParent(this);
+ }
+
+ /**
+ * Adds a parent to the node.
+ *
+ * @param parent The parent node to be added
+ */
+ public void setParent(AndroidManifestNode parent)
+ {
+ Assert.isLegal(parent != null);
+
+ this.parent = parent;
+ }
+
+ /**
+ * Gets the parent of the node.
+ */
+ public AndroidManifestNode getParent()
+ {
+ return parent;
+ }
+
+ /**
+ * Retrieves all valid children nodes
+ *
+ * @return all valid children nodes
+ */
+ public AndroidManifestNode[] getChildren()
+ {
+ AndroidManifestNode[] childrenArray = new AndroidManifestNode[children.size()];
+ childrenArray = children.toArray(childrenArray);
+ return childrenArray;
+ }
+
+ /**
+ * Retrieves all unknown children nodes
+ *
+ * @return all unknown children nodes
+ */
+ public AndroidManifestNode[] getUnkownChildren()
+ {
+ AndroidManifestNode[] unknownChildrenArray =
+ new AndroidManifestNode[unknownChildren.size()];
+ unknownChildrenArray = unknownChildren.toArray(unknownChildrenArray);
+ return unknownChildrenArray;
+ }
+
+ /**
+ * Adds an unknown property to the node
+ *
+ * @param property The property name
+ * @param value The property value
+ * @return true if the property has been added or false otherwise
+ */
+ public boolean addUnknownProperty(String property, String value)
+ {
+ boolean added = false;
+
+ if ((property != null) && (property.trim().length() > 0) && (value != null)
+ && canAddUnknownProperty(property))
+ {
+ unknownProperties.put(property, value);
+ }
+
+ return added;
+ }
+
+ /**
+ * Checks if an unknown property can be added, based on the valid properties
+ *
+ * @param allProperties the array containing all valid property names
+ * @param property the property to be checked
+ * @return true if the unknown property can be added or false otherwise
+ */
+ protected boolean canAddUnknownProperty(String property)
+ {
+ boolean canAdd = true;
+
+ if (ALL_PROPERTIES == null)
+ {
+ ALL_PROPERTIES = new String[defaultProperties.size()];
+ ALL_PROPERTIES = defaultProperties.toArray(ALL_PROPERTIES);
+ }
+
+ for (String prop : ALL_PROPERTIES)
+ {
+ if (prop.trim().equalsIgnoreCase(property))
+ {
+ canAdd = false;
+ break;
+ }
+ }
+
+ return canAdd;
+ }
+
+ /**
+ * Retrieves all unknown node properties, ready to be written to the AndroidManifest.xml file
+ *
+ * @return all unknown node properties, ready to be written to the AndroidManifest.xml file
+ */
+ public Map<String, String> getNodeUnknownProperties()
+ {
+ return unknownProperties;
+ }
+
+ /**
+ * Retrieves all children nodes from a specific type
+ *
+ * @param type The children type
+ *
+ * @return all children nodes from the specific type
+ */
+ protected AndroidManifestNode[] getAllChildrenFromType(NodeType type)
+ {
+ List<AndroidManifestNode> nodes = new LinkedList<AndroidManifestNode>();
+
+ for (AndroidManifestNode node : children)
+ {
+ if (node.getNodeType() == type)
+ {
+ nodes.add(node);
+ }
+ }
+
+ AndroidManifestNode[] arrayNodes = new AndroidManifestNode[nodes.size()];
+ arrayNodes = nodes.toArray(arrayNodes);
+ return arrayNodes;
+ }
+
+ /**
+ * Retrieves all node errors
+ *
+ * @return an IStatus array containing all node errors
+ */
+ public IStatus[] getNodeErrors()
+ {
+ List<IStatus> nodeErrors = new LinkedList<IStatus>();
+
+ if ((getNodeType() != NodeType.Unknown) && (getNodeType() != NodeType.Comment))
+ {
+
+ // Adds specific node errors
+ List<IStatus> specificErrors = getSpecificNodeProblems();
+ if ((specificErrors != null) && !specificErrors.isEmpty())
+ {
+ nodeErrors.addAll(specificErrors);
+ }
+
+ }
+
+ return nodeErrors.toArray(new IStatus[0]);
+ }
+
+ /**
+ * Retrieves all node warnings
+ *
+ * @return an IStatus array containing all node warnings
+ */
+ public IStatus[] getNodeWarnings()
+ {
+ List<IStatus> nodeWarnings = new LinkedList<IStatus>();
+
+ if ((getNodeType() != NodeType.Unknown) && (getNodeType() != NodeType.Comment))
+ {
+ String thisNode = "<" + getNodeName() + ">";
+ // Adds errors about unknown properties
+ for (String attribute : getNodeUnknownProperties().keySet())
+ {
+ String errMsg =
+ NLS.bind(
+ UtilitiesNLS.WARN_AndroidManifestNode_TheNodeContainsAnInvalidAttribute,
+ thisNode, attribute);
+
+ nodeWarnings.add(new Status(IStatus.WARNING, CommonPlugin.PLUGIN_ID, errMsg));
+ }
+ }
+
+ return nodeWarnings.toArray(new IStatus[0]);
+ }
+
+ /**
+ * Retrieves the errors for this node and for its children
+ *
+ * @return an IStatus array containing all node errors
+ */
+ public IStatus[] getRecursiveNodeErrors()
+ {
+ List<IStatus> nodeErrors = new LinkedList<IStatus>();
+ IStatus[] thisNodeErrors = getNodeErrors();
+
+ if (thisNodeErrors != null)
+ {
+ for (IStatus status : thisNodeErrors)
+ {
+ nodeErrors.add(status);
+ }
+ }
+
+ for (AndroidManifestNode node : getChildren())
+ {
+ IStatus[] childrenErrors = node.getNodeErrors();
+
+ if (childrenErrors != null)
+ {
+ for (IStatus status : childrenErrors)
+ {
+ nodeErrors.add(status);
+ }
+ }
+ }
+
+ return nodeErrors.toArray(new IStatus[0]);
+ }
+
+ /**
+ * Checks if a know property can be added or updated, based on the valid properties.
+ *
+ * @param property the property to be checked
+ * @return true if the property can be added/updated or false otherwise
+ */
+ protected boolean canAddOrUpdateProperty(String property)
+ {
+ boolean canAdd = false;
+
+ if (ALL_PROPERTIES == null)
+ {
+ ALL_PROPERTIES = new String[defaultProperties.size()];
+ ALL_PROPERTIES = defaultProperties.toArray(ALL_PROPERTIES);
+ }
+
+ for (String prop : ALL_PROPERTIES)
+ {
+ if (prop.trim().equalsIgnoreCase(property))
+ {
+ canAdd = true;
+ break;
+ }
+ }
+
+ return canAdd;
+ }
+
+ /**
+ * Adds a known, valid property to the node
+ *
+ * @param property The property name
+ * @param value The property value
+ * @return true if the property has been added or false otherwise
+ */
+ public boolean addOrUpdateKnownProperty(String property, String value)
+ {
+ boolean added = false;
+
+ if ((property != null) && (property.trim().length() > 0) && (value != null)
+ && canAddOrUpdateProperty(property))
+ {
+ properties.put(property, value);
+ }
+
+ return added;
+ }
+}
diff --git a/src/plugins/common/src/com/motorola/studio/android/model/manifest/dom/ApplicationNode.java b/src/plugins/common/src/com/motorola/studio/android/model/manifest/dom/ApplicationNode.java
new file mode 100644
index 0000000..d4cac34
--- /dev/null
+++ b/src/plugins/common/src/com/motorola/studio/android/model/manifest/dom/ApplicationNode.java
@@ -0,0 +1,735 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.model.manifest.dom;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import org.eclipse.core.runtime.IStatus;
+
+/**
+ * Class that represents an <application> node on AndroidManifest.xml file
+ */
+public class ApplicationNode extends AbstractIconLabelNameNode
+{
+ static
+ {
+ defaultProperties.add(PROP_ALLOWCLEARUSERDATA);
+ defaultProperties.add(PROP_ALLOWTASKREPARENTING);
+ defaultProperties.add(PROP_DEBUGGABLE);
+ defaultProperties.add(PROP_DESCRIPTION);
+ defaultProperties.add(PROP_ENABLED);
+ defaultProperties.add(PROP_HASCODE);
+ defaultProperties.add(PROP_MANAGESPACEACTIVITY);
+ defaultProperties.add(PROP_PERMISSION);
+ defaultProperties.add(PROP_PERSISTENT);
+ defaultProperties.add(PROP_PROCESS);
+ defaultProperties.add(PROP_TASKAFFINITY);
+ defaultProperties.add(PROP_THEME);
+ }
+
+ /**
+ * The allowClearUserData property
+ */
+ private Boolean propAllowClearUserData = null;
+
+ /**
+ * The allowTaskReparenting property
+ */
+ private Boolean propAllowTaskReparenting = null;
+
+ /**
+ * The debuggable property
+ */
+ private Boolean propDebuggable = null;
+
+ /**
+ * The description property
+ */
+ private String propDescription = null;
+
+ /**
+ * The enabled property
+ */
+ private Boolean propEnabled = null;
+
+ /**
+ * The hasCode property
+ */
+ private Boolean propHasCode = null;
+
+ /**
+ * The manageSpaceActivity property
+ */
+ private String propManageSpaceActivity = null;
+
+ /**
+ * The permission property
+ */
+ private String propPermission = null;
+
+ /**
+ * The persistent property
+ */
+ private Boolean propPersistent = null;
+
+ /**
+ * The process property
+ */
+ private String propProcess = null;
+
+ /**
+ * The taskAffinity property
+ */
+ private String propTaskAffinity = null;
+
+ /**
+ * The theme property
+ */
+ private String propTheme = null;
+
+ /**
+ * Default constructor
+ *
+ * @param name The name property. It must not be null.
+ */
+ public ApplicationNode(String name)
+ {
+ super(name);
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.model.manifest.dom.AndroidManifestNode#canContains(com.motorola.studio.android.model.manifest.dom.AndroidManifestNode.NodeType)
+ */
+ @Override
+ protected boolean canContains(NodeType nodeType)
+ {
+ return (nodeType == NodeType.Activity) || (nodeType == NodeType.ActivityAlias)
+ || (nodeType == NodeType.Service) || (nodeType == NodeType.Receiver)
+ || (nodeType == NodeType.Provider) || (nodeType == NodeType.UsesLibrary)
+ || (nodeType == NodeType.Comment) || (nodeType == NodeType.MetaData);
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.model.manifest.dom.AbstractIconLabelNameNode#addAdditionalProperties()
+ */
+ @Override
+ protected void addAdditionalProperties()
+ {
+ if (propAllowClearUserData != null)
+ {
+ properties.put(PROP_ALLOWCLEARUSERDATA, propAllowClearUserData.toString());
+ }
+
+ if (propAllowTaskReparenting != null)
+ {
+ properties.put(PROP_ALLOWTASKREPARENTING, propAllowTaskReparenting.toString());
+ }
+
+ if (propDebuggable != null)
+ {
+ properties.put(PROP_DEBUGGABLE, propDebuggable.toString());
+ }
+
+ if (propDescription != null)
+ {
+ properties.put(PROP_DESCRIPTION, propDescription);
+ }
+
+ if (propEnabled != null)
+ {
+ properties.put(PROP_ENABLED, propEnabled.toString());
+ }
+
+ if (propHasCode != null)
+ {
+ properties.put(PROP_HASCODE, propHasCode.toString());
+ }
+
+ if (propManageSpaceActivity != null)
+ {
+ properties.put(PROP_MANAGESPACEACTIVITY, propManageSpaceActivity);
+ }
+
+ if (propPermission != null)
+ {
+ properties.put(PROP_PERMISSION, propPermission);
+ }
+
+ if (propPersistent != null)
+ {
+ properties.put(PROP_PERSISTENT, propPersistent.toString());
+ }
+
+ if (propProcess != null)
+ {
+ properties.put(PROP_PROCESS, propProcess);
+ }
+
+ if (propTaskAffinity != null)
+ {
+ properties.put(PROP_TASKAFFINITY, propTaskAffinity);
+ }
+
+ if (propTheme != null)
+ {
+ properties.put(PROP_THEME, propTheme);
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.model.manifest.dom.AndroidManifestNode#getNodeType()
+ */
+ @Override
+ public NodeType getNodeType()
+ {
+ return NodeType.Application;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.model.manifest.dom.AndroidManifestNode#isNodeValid()
+ */
+ @Override
+ protected boolean isNodeValid()
+ {
+ return super.isNodeValid();
+ }
+
+ /**
+ * Gets the allowClearUserData property value
+ *
+ * @return the allowClearUserData property value
+ */
+ public Boolean getAllowClearUserData()
+ {
+ return propAllowClearUserData;
+ }
+
+ /**
+ * Sets the allowClearUserData property value. Set it to null to remove it.
+ *
+ * @param allowClearUserData the allowClearUserData property value
+ */
+ public void setAllowClearUserData(Boolean allowClearUserData)
+ {
+ this.propAllowClearUserData = allowClearUserData;
+ }
+
+ /**
+ * Gets the allowTaskReparenting property value
+ *
+ * @return the allowTaskReparenting property value
+ */
+ public Boolean getAllowTaskReparenting()
+ {
+ return propAllowTaskReparenting;
+ }
+
+ /**
+ * Sets the allowTaskReparenting property value. Set it to null to remove it.
+ *
+ * @param allowTaskReparenting the allowTaskReparenting property value
+ */
+ public void setAllowTaskReparenting(Boolean allowTaskReparenting)
+ {
+ this.propAllowTaskReparenting = allowTaskReparenting;
+ }
+
+ /**
+ * Gets the debuggable property value
+ *
+ * @return the debuggable property value
+ */
+ public Boolean getDebuggable()
+ {
+ return propDebuggable;
+ }
+
+ /**
+ * Sets the debuggable property value. Set it to null to remove it.
+ *
+ * @param debuggable the debuggable property value
+ */
+ public void setDebuggable(Boolean debuggable)
+ {
+ this.propDebuggable = debuggable;
+ }
+
+ /**
+ * Gets the description property value
+ *
+ * @return the description property value
+ */
+ public String getDescription()
+ {
+ return propDescription;
+ }
+
+ /**
+ * Sets the description property value. Set it to null to remove it.
+ *
+ * @param description the description property value
+ */
+ public void setDescription(String description)
+ {
+ this.propDescription = description;
+ }
+
+ /**
+ * Gets the enabled property value
+ *
+ * @return the enabled property value
+ */
+ public Boolean getEnabled()
+ {
+ return propEnabled;
+ }
+
+ /**
+ * Sets the enabled property value. Set it to null to remove it.
+ *
+ * @param enabled the enabled property value
+ */
+ public void setEnabled(Boolean enabled)
+ {
+ this.propEnabled = enabled;
+ }
+
+ /**
+ * Gets the hasCode property value
+ *
+ * @return the hasCode property value
+ */
+ public Boolean getHasCode()
+ {
+ return propHasCode;
+ }
+
+ /**
+ * Sets the hasCode property value. Set it to null to remove it.
+ *
+ * @param hasCode the hasCode property value
+ */
+ public void setHasCode(Boolean hasCode)
+ {
+ this.propHasCode = hasCode;
+ }
+
+ /**
+ * Gets the manageSpaceActivity property value
+ *
+ * @return the manageSpaceActivity property value
+ */
+ public String getManageSpaceActivity()
+ {
+ return propManageSpaceActivity;
+ }
+
+ /**
+ * Sets the manageSpaceActivity property value. Set it to null to remove it.
+ *
+ * @param manageSpaceActivity the manageSpaceActivity property value
+ */
+ public void setManageSpaceActivity(String manageSpaceActivity)
+ {
+ this.propManageSpaceActivity = manageSpaceActivity;
+ }
+
+ /**
+ * Gets the permission property value
+ *
+ * @return the permission property value
+ */
+ public String getPermission()
+ {
+ return propPermission;
+ }
+
+ /**
+ * Sets the permission property value. Set it to null to remove it.
+ *
+ * @param permission the permission property value
+ */
+ public void setPermission(String permission)
+ {
+ this.propPermission = permission;
+ }
+
+ /**
+ * Gets the persistent property value
+ *
+ * @return the persistent property value
+ */
+ public Boolean getPersistent()
+ {
+ return propPersistent;
+ }
+
+ /**
+ * Sets the persistent property value. Set it to null to remove it.
+ *
+ * @param persistent the persistent property value
+ */
+ public void setPersistent(Boolean persistent)
+ {
+ this.propPersistent = persistent;
+ }
+
+ /**
+ * Gets the process property value
+ *
+ * @return the process property value
+ */
+ public String getProcess()
+ {
+ return propProcess;
+ }
+
+ /**
+ * Sets the process property value. Set it to null to remove it.
+ *
+ * @param process the process property value
+ */
+ public void setProcess(String process)
+ {
+ this.propProcess = process;
+ }
+
+ /**
+ * Gets the taskAffinity property value
+ *
+ * @return the taskAffinity property value
+ */
+ public String getTaskAffinity()
+ {
+ return propTaskAffinity;
+ }
+
+ /**
+ * Sets the taskAffinity property value. Set it to null to remove it.
+ *
+ * @param taskAffinity the taskAffinity property value
+ */
+ public void setTaskAffinity(String taskAffinity)
+ {
+ this.propTaskAffinity = taskAffinity;
+ }
+
+ /**
+ * Gets the theme property value
+ *
+ * @return the theme property value
+ */
+ public String getTheme()
+ {
+ return propTheme;
+ }
+
+ /**
+ * Sets the theme property value. Set it to null to remove it.
+ *
+ * @param theme the theme property value
+ */
+ public void setTheme(String theme)
+ {
+ this.propTheme = theme;
+ }
+
+ /**
+ * Adds an Activity Node to the Application Node
+ *
+ * @param activity The Activity Node
+ */
+ public void addActivityNode(ActivityNode activity)
+ {
+ if (activity != null)
+ {
+ if (!children.contains(activity))
+ {
+ children.add(activity);
+ }
+ }
+ }
+
+ /**
+ * Retrieves all Activity Nodes from the Application Node
+ *
+ * @return all Activity Nodes from the Application Node
+ */
+ public List<ActivityNode> getActivityNodes()
+ {
+ List<ActivityNode> activities = new LinkedList<ActivityNode>();
+
+ for (AndroidManifestNode node : getAllChildrenFromType(NodeType.Activity))
+ {
+ activities.add((ActivityNode) node);
+ }
+
+ return activities;
+ }
+
+ /**
+ * Removes an Activity Node from the Application Node
+ *
+ * @param activity the Activity Node to be removed
+ */
+ public void removeActivityNode(ActivityNode activity)
+ {
+ if (activity != null)
+ {
+ children.remove(activity);
+ }
+ }
+
+ /**
+ * Adds an Activity Alias Node to the Application Node
+ *
+ * @param activityAlias The Activity Alias Node
+ */
+ public void addActivityAliasNode(ActivityAliasNode activityAlias)
+ {
+ if (activityAlias != null)
+ {
+ if (!children.contains(activityAlias))
+ {
+ children.add(activityAlias);
+ }
+ }
+ }
+
+ /**
+ * Retrieves all Activity Alias Nodes from the Application Node
+ *
+ * @return all Activity Alias Nodes from the Application Node
+ */
+ public List<ActivityAliasNode> getActivityAliasNodes()
+ {
+ List<ActivityAliasNode> activityAliases = new LinkedList<ActivityAliasNode>();
+
+ for (AndroidManifestNode node : getAllChildrenFromType(NodeType.ActivityAlias))
+ {
+ activityAliases.add((ActivityAliasNode) node);
+ }
+
+ return activityAliases;
+ }
+
+ /**
+ * Removes an Activity Alias Node from the Application Node
+ *
+ * @param activityAlias the Activity Alias Node to be removed
+ */
+ public void removeActivityAliasNode(ActivityAliasNode activityAlias)
+ {
+ if (activityAlias != null)
+ {
+ children.remove(activityAlias);
+ }
+ }
+
+ /**
+ * Adds a Service Node to the Application Node
+ *
+ * @param service The Service Node
+ */
+ public void addServiceNode(ServiceNode service)
+ {
+ if (service != null)
+ {
+ if (!children.contains(service))
+ {
+ children.add(service);
+ }
+ }
+ }
+
+ /**
+ * Retrieves all Service Nodes from the Application Node
+ *
+ * @return all Service Nodes from the Application Node
+ */
+ public List<ServiceNode> getServiceNodes()
+ {
+ List<ServiceNode> services = new LinkedList<ServiceNode>();
+
+ for (AndroidManifestNode node : getAllChildrenFromType(NodeType.Service))
+ {
+ services.add((ServiceNode) node);
+ }
+
+ return services;
+ }
+
+ /**
+ * Removes a Service Node from the Application Node
+ *
+ * @param service the Service Node to be removed
+ */
+ public void removeServiceNode(ServiceNode service)
+ {
+ if (service != null)
+ {
+ children.remove(service);
+ }
+ }
+
+ /**
+ * Adds a Receiver Node to the Application Node
+ *
+ * @param receiver The Receiver Node
+ */
+ public void addReceiverNode(ReceiverNode receiver)
+ {
+ if (receiver != null)
+ {
+ if (!children.contains(receiver))
+ {
+ children.add(receiver);
+ }
+ }
+ }
+
+ /**
+ * Retrieves all Receiver Nodes from the Application Node
+ *
+ * @return all Receiver Nodes from the Application Node
+ */
+ public List<ReceiverNode> getReceiverNodes()
+ {
+ List<ReceiverNode> receivers = new LinkedList<ReceiverNode>();
+
+ for (AndroidManifestNode node : getAllChildrenFromType(NodeType.Receiver))
+ {
+ receivers.add((ReceiverNode) node);
+ }
+
+ return receivers;
+ }
+
+ /**
+ * Removes a Receiver Node from the Application Node
+ *
+ * @param receiver the Receiver Node to be removed
+ */
+ public void removeReceiverNode(ReceiverNode receiver)
+ {
+ if (receiver != null)
+ {
+ children.remove(receiver);
+ }
+ }
+
+ /**
+ * Adds a Provider Node to the Application Node
+ *
+ * @param provider The Provider Node
+ */
+ public void addProviderNode(ProviderNode provider)
+ {
+ if (provider != null)
+ {
+ if (!children.contains(provider))
+ {
+ children.add(provider);
+ }
+ }
+ }
+
+ /**
+ * Retrieves all Provider Nodes from the Application Node
+ *
+ * @return all Provider Nodes from the Application Node
+ */
+ public List<ProviderNode> getProviderNodes()
+ {
+ List<ProviderNode> providers = new LinkedList<ProviderNode>();
+
+ for (AndroidManifestNode node : getAllChildrenFromType(NodeType.Provider))
+ {
+ providers.add((ProviderNode) node);
+ }
+
+ return providers;
+ }
+
+ /**
+ * Removes a Provider Node from the Application Node
+ *
+ * @param provider the Provider Node to be removed
+ */
+ public void removeProviderNode(ProviderNode provider)
+ {
+ if (provider != null)
+ {
+ children.remove(provider);
+ }
+ }
+
+ /**
+ * Adds an Uses Library Node to the Application Node
+ *
+ * @param usesLibrary The Uses Library Node
+ */
+ public void addUsesLibraryNode(UsesLibraryNode usesLibrary)
+ {
+ if (usesLibrary != null)
+ {
+ if (!children.contains(usesLibrary))
+ {
+ children.add(usesLibrary);
+ }
+ }
+ }
+
+ /**
+ * Retrieves all Uses Library Nodes from the Application Node
+ *
+ * @return all Uses Library Nodes from the Application Node
+ */
+ public List<UsesLibraryNode> getUsesLibraryNodes()
+ {
+ List<UsesLibraryNode> usesLibraries = new LinkedList<UsesLibraryNode>();
+
+ for (AndroidManifestNode node : getAllChildrenFromType(NodeType.UsesLibrary))
+ {
+ usesLibraries.add((UsesLibraryNode) node);
+ }
+
+ return usesLibraries;
+ }
+
+ /**
+ * Removes an Uses Library Node from the Application Node
+ *
+ * @param usesLibrary the Uses Library Node to be removed
+ */
+ public void removeUsesLibraryNode(UsesLibraryNode usesLibrary)
+ {
+ if (usesLibrary != null)
+ {
+ children.remove(usesLibrary);
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.model.manifest.dom.AndroidManifestNode#getSpecificNodeErrors()
+ */
+ @Override
+ protected List<IStatus> getSpecificNodeProblems()
+ {
+ return null;
+ }
+}
diff --git a/src/plugins/common/src/com/motorola/studio/android/model/manifest/dom/CategoryNode.java b/src/plugins/common/src/com/motorola/studio/android/model/manifest/dom/CategoryNode.java
new file mode 100644
index 0000000..59e17dd
--- /dev/null
+++ b/src/plugins/common/src/com/motorola/studio/android/model/manifest/dom/CategoryNode.java
@@ -0,0 +1,54 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.model.manifest.dom;
+
+import java.util.List;
+
+import org.eclipse.core.runtime.IStatus;
+
+/**
+ * Class that represents a <category> node on AndroidManifest.xml file
+ */
+public class CategoryNode extends AbstractSimpleNameNode
+{
+ /**
+ * Default constructor
+ *
+ * @param name The name property
+ */
+ public CategoryNode(String name)
+ {
+ super(name);
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.model.manifest.dom.AndroidManifestNode#getNodeType()
+ */
+ @Override
+ public NodeType getNodeType()
+ {
+ return NodeType.Category;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.model.manifest.dom.AndroidManifestNode#getSpecificNodeErrors()
+ */
+ @Override
+ protected List<IStatus> getSpecificNodeProblems()
+ {
+ return null;
+ }
+}
diff --git a/src/plugins/common/src/com/motorola/studio/android/model/manifest/dom/CommentNode.java b/src/plugins/common/src/com/motorola/studio/android/model/manifest/dom/CommentNode.java
new file mode 100644
index 0000000..a87f68f
--- /dev/null
+++ b/src/plugins/common/src/com/motorola/studio/android/model/manifest/dom/CommentNode.java
@@ -0,0 +1,139 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.model.manifest.dom;
+
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.core.runtime.IStatus;
+
+import com.motorola.studio.android.common.utilities.i18n.UtilitiesNLS;
+
+/**
+ * Class that represents a comment on the AndroidManifest.xml file
+ */
+public class CommentNode extends AndroidManifestNode
+{
+ private String comment = null;
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.model.manifest.dom.AndroidManifestNode#canContains(com.motorola.studio.android.model.manifest.dom.AndroidManifestNode.NodeType)
+ */
+ @Override
+ protected boolean canContains(NodeType nodeType)
+ {
+ // Always return false. No child node can be added
+ return false;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.model.manifest.dom.AndroidManifestNode#getNodeProperties()
+ */
+ @Override
+ public Map<String, String> getNodeProperties()
+ {
+ properties.clear();
+
+ return properties;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.model.manifest.dom.AndroidManifestNode#getNodeType()
+ */
+ @Override
+ public NodeType getNodeType()
+ {
+ return NodeType.Comment;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.model.manifest.dom.AndroidManifestNode#isNodeValid()
+ */
+ @Override
+ protected boolean isNodeValid()
+ {
+ // Always returns true
+ return true;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.model.manifest.dom.AndroidManifestNode#addChild(com.motorola.studio.android.model.manifest.dom.AndroidManifestNode)
+ */
+ @Override
+ public void addChild(AndroidManifestNode child)
+ {
+ throw new IllegalArgumentException(
+ UtilitiesNLS.EXC_CommentNode_ChildNodesCannotBeAddedToACommentNode);
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.model.manifest.dom.AndroidManifestNode#addUnknownProperty(java.lang.String, java.lang.String)
+ */
+ @Override
+ public boolean addUnknownProperty(String property, String value)
+ {
+ // Comments do not have properties
+ return false;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.model.manifest.dom.AndroidManifestNode#canAddUnknownProperty(java.lang.String)
+ */
+ @Override
+ protected boolean canAddUnknownProperty(String property)
+ {
+ // Comments do not have properties
+ return false;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.model.manifest.dom.AndroidManifestNode#getAllChildrenFromType(com.motorola.studio.android.model.manifest.dom.AndroidManifestNode.NodeType)
+ */
+ @Override
+ protected AndroidManifestNode[] getAllChildrenFromType(NodeType type)
+ {
+ return new AndroidManifestNode[0];
+ }
+
+ /**
+ * Sets the comment node content
+ *
+ * @param comment the comment node content
+ */
+ public void setComment(String comment)
+ {
+ this.comment = comment;
+ }
+
+ /**
+ * Gets the comment node content
+ *
+ * @return the comment node content
+ */
+ public String getComment()
+ {
+ return comment;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.model.manifest.dom.AndroidManifestNode#getSpecificNodeErrors()
+ */
+ @Override
+ protected List<IStatus> getSpecificNodeProblems()
+ {
+ return null;
+ }
+}
diff --git a/src/plugins/common/src/com/motorola/studio/android/model/manifest/dom/DataNode.java b/src/plugins/common/src/com/motorola/studio/android/model/manifest/dom/DataNode.java
new file mode 100644
index 0000000..bd483b0
--- /dev/null
+++ b/src/plugins/common/src/com/motorola/studio/android/model/manifest/dom/DataNode.java
@@ -0,0 +1,298 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.model.manifest.dom;
+
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.core.runtime.IStatus;
+
+/**
+ * Class that represents a <data> node on AndroidManifest.xml file
+ */
+public class DataNode extends AndroidManifestNode implements IAndroidManifestProperties
+{
+ static
+ {
+ defaultProperties.add(PROP_HOST);
+ defaultProperties.add(PROP_MIMETYPE);
+ defaultProperties.add(PROP_PATH);
+ defaultProperties.add(PROP_PATHPATTERN);
+ defaultProperties.add(PROP_PATHPREFIX);
+ defaultProperties.add(PROP_PORT);
+ defaultProperties.add(PROP_SCHEME);
+ }
+
+ /**
+ * The host property
+ */
+ private String propHost = null;
+
+ /**
+ * The mimeType property
+ */
+ private String propMimeType = null;
+
+ /**
+ * The path property
+ */
+ private String propPath = null;
+
+ /**
+ * The pathPattern property
+ */
+ private String propPathPattern = null;
+
+ /**
+ * The pathPrefix property
+ */
+ private String propPathPrefix = null;
+
+ /**
+ * The port property
+ */
+ private String propPort = null;
+
+ /**
+ * The scheme property
+ */
+ private String propScheme = null;
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.model.manifest.dom.AndroidManifestNode#canContains(com.motorola.studio.android.model.manifest.dom.AndroidManifestNode.NodeType)
+ */
+ @Override
+ protected boolean canContains(NodeType nodeType)
+ {
+ // Always returns false. This node can not contain children.
+ return false;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.model.manifest.dom.AndroidManifestNode#getNodeProperties()
+ */
+ @Override
+ public Map<String, String> getNodeProperties()
+ {
+ properties.clear();
+
+ if ((propHost != null) && (propHost.trim().length() > 0))
+ {
+ properties.put(PROP_HOST, propHost);
+ }
+
+ if ((propMimeType != null) && (propMimeType.trim().length() > 0))
+ {
+ properties.put(PROP_MIMETYPE, propMimeType);
+ }
+
+ if ((propPath != null) && (propPath.trim().length() > 0))
+ {
+ properties.put(PROP_PATH, propPath);
+ }
+
+ if ((propPathPattern != null) && (propPathPattern.trim().length() > 0))
+ {
+ properties.put(PROP_PATHPATTERN, propPathPattern);
+ }
+
+ if ((propPathPrefix != null) && (propPathPrefix.trim().length() > 0))
+ {
+ properties.put(PROP_PATHPREFIX, propPathPrefix);
+ }
+
+ if ((propPort != null) && (propPort.trim().length() > 0))
+ {
+ properties.put(PROP_PORT, propPort);
+ }
+
+ if ((propScheme != null) && (propScheme.trim().length() > 0))
+ {
+ properties.put(PROP_SCHEME, propScheme);
+ }
+
+ return properties;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.model.manifest.dom.AndroidManifestNode#getNodeType()
+ */
+ @Override
+ public NodeType getNodeType()
+ {
+ return NodeType.Data;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.model.manifest.dom.AndroidManifestNode#isNodeValid()
+ */
+ @Override
+ protected boolean isNodeValid()
+ {
+ // Every property is optional. One data element can have just one.
+ return !getNodeProperties().isEmpty();
+ }
+
+ /**
+ * Gets the host property value
+ *
+ * @return the host property value
+ */
+ public String getHost()
+ {
+ return propHost;
+ }
+
+ /**
+ * Sets the host property value. Set it to null to remove it.
+ *
+ * @param host the host property value
+ */
+ public void setHost(String host)
+ {
+ this.propHost = host;
+ }
+
+ /**
+ * Gets the mimeType property value
+ *
+ * @return the mimeType property value
+ */
+ public String getMimeType()
+ {
+ return propMimeType;
+ }
+
+ /**
+ * Sets the mimeType property value. Set it to null to remove it.
+ *
+ * @param mimeType the mimeType property value
+ */
+ public void setMimeType(String mimeType)
+ {
+ this.propMimeType = mimeType;
+ }
+
+ /**
+ * Gets the path property value
+ *
+ * @return the path property value
+ */
+ public String getPath()
+ {
+ return propPath;
+ }
+
+ /**
+ * Sets the path property value. Set it to null to remove it.
+ *
+ * @param path the path property value
+ */
+ public void setPath(String path)
+ {
+ this.propPath = path;
+ }
+
+ /**
+ * Gets the pathPattern property value
+ *
+ * @return the pathPattern property value
+ */
+ public String getPathPattern()
+ {
+ return propPathPattern;
+ }
+
+ /**
+ * Sets the pathPattern property value. Set it to null to remove it.
+ *
+ * @param pathPattern the pathPattern property value
+ */
+ public void setPathPattern(String pathPattern)
+ {
+ this.propPathPattern = pathPattern;
+ }
+
+ /**
+ * Gets the pathPrefix property value
+ *
+ * @return the pathPrefix property value
+ */
+ public String getPathPrefix()
+ {
+ return propPathPrefix;
+ }
+
+ /**
+ * Sets the pathPrefix property value. Set it to null to remove it.
+ *
+ * @param pathPrefix the pathPrefix property value
+ */
+ public void setPathPrefix(String pathPrefix)
+ {
+ this.propPathPrefix = pathPrefix;
+ }
+
+ /**
+ * Gets the port property value
+ *
+ * @return the port property value
+ */
+ public String getPort()
+ {
+ return propPort;
+ }
+
+ /**
+ * Sets the port property value. Set it to null to remove it.
+ *
+ * @param port the port property value
+ */
+ public void setPort(String port)
+ {
+ this.propPort = port;
+ }
+
+ /**
+ * Gets the scheme property value
+ *
+ * @return the scheme property value
+ */
+ public String getScheme()
+ {
+ return propScheme;
+ }
+
+ /**
+ * Sets the scheme property value. Set it to null to remove it.
+ *
+ * @param scheme the scheme property value
+ */
+ public void setScheme(String scheme)
+ {
+ this.propScheme = scheme;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.model.manifest.dom.AndroidManifestNode#getSpecificNodeErrors()
+ */
+ @Override
+ protected List<IStatus> getSpecificNodeProblems()
+ {
+ return null;
+ }
+
+}
diff --git a/src/plugins/common/src/com/motorola/studio/android/model/manifest/dom/GrantUriPermissionNode.java b/src/plugins/common/src/com/motorola/studio/android/model/manifest/dom/GrantUriPermissionNode.java
new file mode 100644
index 0000000..64ffb4c
--- /dev/null
+++ b/src/plugins/common/src/com/motorola/studio/android/model/manifest/dom/GrantUriPermissionNode.java
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.motorola.studio.android.model.manifest.dom;
+
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.core.runtime.IStatus;
+
+/**
+ * Class that represents a <grant-uri-permission> node on AndroidManifest.xml file
+ */
+public class GrantUriPermissionNode extends AndroidManifestNode implements
+ IAndroidManifestProperties
+{
+ static
+ {
+ defaultProperties.add(PROP_PATH);
+ defaultProperties.add(PROP_PATHPREFIX);
+ defaultProperties.add(PROP_PATHPATTERN);
+ }
+
+ /**
+ * The path property
+ */
+ private String propPath = null;
+
+ /**
+ * The pathPrefix property
+ */
+ private String propPathPrefix = null;
+
+ /**
+ * The pathPattern property
+ */
+ private String propPathPattern = null;
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.model.manifest.dom.AndroidManifestNode#canContains(com.motorola.studio.android.model.manifest.dom.AndroidManifestNode.NodeType)
+ */
+ @Override
+ protected boolean canContains(NodeType nodeType)
+ {
+ // Always returns false. This node can not contain children.
+ return false;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.model.manifest.dom.AndroidManifestNode#getNodeProperties()
+ */
+ @Override
+ public Map<String, String> getNodeProperties()
+ {
+ properties.clear();
+
+ if ((propPath != null) && (propPath.trim().length() > 0))
+ {
+ properties.put(PROP_PATH, propPath);
+ }
+
+ if ((propPathPattern != null) && (propPathPattern.trim().length() > 0))
+ {
+ properties.put(PROP_PATHPATTERN, propPathPattern);
+ }
+
+ if ((propPathPrefix != null) && (propPathPrefix.trim().length() > 0))
+ {
+ properties.put(PROP_PATHPREFIX, propPathPrefix);
+ }
+
+ return properties;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.model.manifest.dom.AndroidManifestNode#getNodeType()
+ */
+ @Override
+ public NodeType getNodeType()
+ {
+ return NodeType.GrantUriPermission;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.model.manifest.dom.AndroidManifestNode#isNodeValid()
+ */
+ @Override
+ protected boolean isNodeValid()
+ {
+ return ((propPath != null) && (propPath.trim().length() > 0))
+ || ((propPathPattern != null) && (propPathPattern.trim().length() > 0))
+ || ((propPathPrefix != null) && (propPathPrefix.trim().length() > 0));
+ }
+
+ /**
+ * Gets the path property value
+ *
+ * @return the path property value
+ */
+ public String getPath()
+ {
+ return propPath;
+ }
+
+ /**
+ * Sets the path property value. Set it to null to remove it.
+ *
+ * @param path the path property value
+ */
+ public void setPath(String path)
+ {
+ this.propPath = path;
+ }
+
+ /**
+ * Gets the pathPrefix property value
+ *
+ * @return the pathPrefix property value
+ */
+ public String getPathPrefix()
+ {
+ return propPathPrefix;
+ }
+
+ /**
+ * Sets the pathPrefix property value. Set it to null to remove it.
+ *
+ * @param pathPrefix the pathPrefix property value
+ */
+ public void setPathPrefix(String pathPrefix)
+ {
+ this.propPathPrefix = pathPrefix;
+ }
+
+ /**
+ * Gets the pathPattern property value
+ *
+ * @return the pathPattern property value
+ */
+ public String getPathPattern()
+ {
+ return propPathPattern;
+ }
+
+ /**
+ * Sets the pathPattern property value. Set it to null to remove it.
+ *
+ * @param pathPattern the pathPattern property value
+ */
+ public void setPathPattern(String pathPattern)
+ {
+ this.propPathPattern = pathPattern;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.model.manifest.dom.AndroidManifestNode#getSpecificNodeErrors()
+ */
+ @Override
+ protected List<IStatus> getSpecificNodeProblems()
+ {
+ return null;
+ }
+}
diff --git a/src/plugins/common/src/com/motorola/studio/android/model/manifest/dom/IAndroidManifestProperties.java b/src/plugins/common/src/com/motorola/studio/android/model/manifest/dom/IAndroidManifestProperties.java
new file mode 100644
index 0000000..48c18bd
--- /dev/null
+++ b/src/plugins/common/src/com/motorola/studio/android/model/manifest/dom/IAndroidManifestProperties.java
@@ -0,0 +1,138 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.model.manifest.dom;
+
+/**
+ * Interface that contains the node properties from AndroidManifest.xml file
+ */
+public interface IAndroidManifestProperties
+{
+ String ANDROID_QUALIFIER = "android:";
+
+ String PROP_ALLOWCLEARUSERDATA = ANDROID_QUALIFIER + "allowClearUserData";
+
+ String PROP_ALLOWTASKREPARENTING = ANDROID_QUALIFIER + "allowTaskReparenting";
+
+ String PROP_ALWAYSRETAINTASKSTATE = ANDROID_QUALIFIER + "alwaysRetainTaskState";
+
+ String PROP_AUTHORITIES = ANDROID_QUALIFIER + "authorities";
+
+ String PROP_CLEARTASKONLAUNCH = ANDROID_QUALIFIER + "clearTaskOnLaunch";
+
+ String PROP_CONFIGCHANGES = ANDROID_QUALIFIER + "configChanges";
+
+ String PROP_DEBUGGABLE = ANDROID_QUALIFIER + "debuggable";
+
+ String PROP_DESCRIPTION = ANDROID_QUALIFIER + "description";
+
+ String PROP_ENABLED = ANDROID_QUALIFIER + "enabled";
+
+ String PROP_EXCLUDEFROMRECENTS = ANDROID_QUALIFIER + "excludeFromRecents";
+
+ String PROP_EXPORTED = ANDROID_QUALIFIER + "exported";
+
+ String PROP_FINISHONTASKLAUNCH = ANDROID_QUALIFIER + "finishOnTaskLaunch";
+
+ String PROP_FUNCTIONALTEST = ANDROID_QUALIFIER + "functionalTest";
+
+ String PROP_GRANTURIPERMISSIONS = ANDROID_QUALIFIER + "grantUriPermissions";
+
+ String PROP_HANDLEPROFILING = ANDROID_QUALIFIER + "handleProfiling";
+
+ String PROP_HASCODE = ANDROID_QUALIFIER + "hasCode";
+
+ String PROP_HOST = ANDROID_QUALIFIER + "host";
+
+ String PROP_ICON = ANDROID_QUALIFIER + "icon";
+
+ String PROP_INITORDER = ANDROID_QUALIFIER + "initOrder";
+
+ String PROP_LABEL = ANDROID_QUALIFIER + "label";
+
+ String PROP_LAUNCHMODE = ANDROID_QUALIFIER + "launchMode";
+
+ String PROP_MANAGESPACEACTIVITY = ANDROID_QUALIFIER + "manageSpaceActivity";
+
+ String PROP_MIMETYPE = ANDROID_QUALIFIER + "mimeType";
+
+ String PROP_MINSDKVERSION = ANDROID_QUALIFIER + "minSdkVersion";
+
+ String PROP_MAXSDKVERSION = ANDROID_QUALIFIER + "maxSdkVersion";
+
+ String PROP_MULTIPROCESS = ANDROID_QUALIFIER + "multiprocess";
+
+ String PROP_NAME = ANDROID_QUALIFIER + "name";
+
+ // Without the android qualifier
+ String PROP_PACKAGE = "package";
+
+ String PROP_PATH = ANDROID_QUALIFIER + "path";
+
+ String PROP_PATHPATTERN = ANDROID_QUALIFIER + "pathPattern";
+
+ String PROP_PATHPREFIX = ANDROID_QUALIFIER + "pathPrefix";
+
+ String PROP_PERMISSION = ANDROID_QUALIFIER + "permission";
+
+ String PROP_PERMISSIONGROUP = ANDROID_QUALIFIER + "permissionGroup";
+
+ String PROP_PERSISTENT = ANDROID_QUALIFIER + "persistent";
+
+ String PROP_PORT = ANDROID_QUALIFIER + "port";
+
+ String PROP_PRIORITY = ANDROID_QUALIFIER + "priority";
+
+ String PROP_PROCESS = ANDROID_QUALIFIER + "process";
+
+ String PROP_PROTECTIONLEVEL = ANDROID_QUALIFIER + "protectionLevel";
+
+ String PROP_READPERMISSION = ANDROID_QUALIFIER + "readPermission";
+
+ String PROP_REQUIRED = ANDROID_QUALIFIER + "required";
+
+ String PROP_RESOURCE = ANDROID_QUALIFIER + "resource";
+
+ String PROP_SCHEME = ANDROID_QUALIFIER + "scheme";
+
+ String PROP_SCREENORIENTATION = ANDROID_QUALIFIER + "screenOrientation";
+
+ String PROP_SHAREDUSERID = ANDROID_QUALIFIER + "sharedUserId";
+
+ String PROP_STATENOTNEEDED = ANDROID_QUALIFIER + "stateNotNeeded";
+
+ String PROP_SYNCABLE = ANDROID_QUALIFIER + "syncable";
+
+ String PROP_TARGETACTIVITY = ANDROID_QUALIFIER + "targetActivity";
+
+ String PROP_TARGETPACKAGE = ANDROID_QUALIFIER + "targetPackage";
+
+ String PROP_TARGETSDKVERSION = ANDROID_QUALIFIER + "targetSdkVersion";
+
+ String PROP_TASKAFFINITY = ANDROID_QUALIFIER + "taskAffinity";
+
+ String PROP_THEME = ANDROID_QUALIFIER + "theme";
+
+ String PROP_VALUE = ANDROID_QUALIFIER + "value";
+
+ String PROP_VERSIONCODE = ANDROID_QUALIFIER + "versionCode";
+
+ String PROP_VERSIONNAME = ANDROID_QUALIFIER + "versionName";
+
+ String PROP_XMLNS = "xmlns:android";
+
+ String PROP_WRITEPERMISSION = ANDROID_QUALIFIER + "writePermission";
+
+}
diff --git a/src/plugins/common/src/com/motorola/studio/android/model/manifest/dom/InstrumentationNode.java b/src/plugins/common/src/com/motorola/studio/android/model/manifest/dom/InstrumentationNode.java
new file mode 100644
index 0000000..5705217
--- /dev/null
+++ b/src/plugins/common/src/com/motorola/studio/android/model/manifest/dom/InstrumentationNode.java
@@ -0,0 +1,168 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.model.manifest.dom;
+
+import java.util.List;
+
+import org.eclipse.core.runtime.IStatus;
+
+/**
+ * Class that represents an <instrumentation> node on AndroidManifest.xml file
+ */
+public class InstrumentationNode extends AbstractIconLabelNameNode
+{
+ static
+ {
+ defaultProperties.add(PROP_FUNCTIONALTEST);
+ defaultProperties.add(PROP_HANDLEPROFILING);
+ defaultProperties.add(PROP_TARGETPACKAGE);
+ }
+
+ /**
+ * The functionalTest property
+ */
+ private Boolean propFunctionalTest = null;
+
+ /**
+ * The handleProfiling property
+ */
+ private Boolean propHandleProfiling = null;
+
+ /**
+ * The targetPackage property
+ */
+ private String propTargetPackage = null;
+
+ /**
+ * Default constructor
+ *
+ * @param name The name property. It must not be null.
+ */
+ public InstrumentationNode(String name)
+ {
+ super(name);
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.model.manifest.dom.AndroidManifestNode#canContains(com.motorola.studio.android.model.manifest.dom.AndroidManifestNode.NodeType)
+ */
+ @Override
+ protected boolean canContains(NodeType nodeType)
+ {
+ // Always returns false. This node can not contain children.
+ return false;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.model.manifest.dom.AbstractIconLabelNameNode#addAdditionalProperties()
+ */
+ @Override
+ protected void addAdditionalProperties()
+ {
+ if (propFunctionalTest != null)
+ {
+ properties.put(PROP_FUNCTIONALTEST, propFunctionalTest.toString());
+ }
+
+ if (propHandleProfiling != null)
+ {
+ properties.put(PROP_HANDLEPROFILING, propHandleProfiling.toString());
+ }
+
+ if ((propTargetPackage != null) && (propTargetPackage.trim().length() > 0))
+ {
+ properties.put(PROP_TARGETPACKAGE, propTargetPackage);
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.model.manifest.dom.AndroidManifestNode#getNodeType()
+ */
+ @Override
+ public NodeType getNodeType()
+ {
+ return NodeType.Instrumentation;
+ }
+
+ /**
+ * Gets the functionalTest property value
+ *
+ * @return the functionalTest property value
+ */
+ public Boolean getFunctionalTest()
+ {
+ return propFunctionalTest;
+ }
+
+ /**
+ * Sets the functionalTest property value. Set it to null to remove it.
+ *
+ * @param functionalTest the functionalTest property value
+ */
+ public void setFunctionalTest(Boolean functionalTest)
+ {
+ this.propFunctionalTest = functionalTest;
+ }
+
+ /**
+ * Gets the handleProfiling property value
+ *
+ * @return the handleProfiling property value
+ */
+ public Boolean getHandleProfiling()
+ {
+ return propHandleProfiling;
+ }
+
+ /**
+ * Sets the handleProfiling property value. Set it to null to remove it.
+ *
+ * @param handleProfiling the handleProfiling property value
+ */
+ public void setHandleProfiling(Boolean handleProfiling)
+ {
+ this.propHandleProfiling = handleProfiling;
+ }
+
+ /**
+ * Gets the targetPackage property value
+ *
+ * @return the targetPackage property value
+ */
+ public String getTargetPackage()
+ {
+ return propTargetPackage;
+ }
+
+ /**
+ * Sets the targetPackage property value. Set it to null to remove it.
+ *
+ * @param targetPackage the targetPackage property value
+ */
+ public void setTargetPackage(String targetPackage)
+ {
+ this.propTargetPackage = targetPackage;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.model.manifest.dom.AndroidManifestNode#getSpecificNodeErrors()
+ */
+ @Override
+ protected List<IStatus> getSpecificNodeProblems()
+ {
+ return null;
+ }
+}
diff --git a/src/plugins/common/src/com/motorola/studio/android/model/manifest/dom/IntentFilterNode.java b/src/plugins/common/src/com/motorola/studio/android/model/manifest/dom/IntentFilterNode.java
new file mode 100644
index 0000000..72e88f0
--- /dev/null
+++ b/src/plugins/common/src/com/motorola/studio/android/model/manifest/dom/IntentFilterNode.java
@@ -0,0 +1,403 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.model.manifest.dom;
+
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.core.runtime.IStatus;
+
+/**
+ * Class that represents an <intent-filter> node on AndroidManifest.xml file
+ */
+public class IntentFilterNode extends AndroidManifestNode implements IAndroidManifestProperties
+{
+ static
+ {
+ defaultProperties.add(PROP_ICON);
+ defaultProperties.add(PROP_LABEL);
+ defaultProperties.add(PROP_PRIORITY);
+ }
+
+ /**
+ * The icon property
+ */
+ private String propIcon = null;
+
+ /**
+ * The label property
+ */
+ private String propLabel = null;
+
+ /**
+ * The priority property
+ */
+ private Integer propPriority = null;
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.model.manifest.dom.AndroidManifestNode#canContains(com.motorola.studio.android.model.manifest.dom.AndroidManifestNode.NodeType)
+ */
+ @Override
+ protected boolean canContains(NodeType nodeType)
+ {
+ return (nodeType == NodeType.Action) || (nodeType == NodeType.Category)
+ || (nodeType == NodeType.Data) || (nodeType == NodeType.Comment);
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.model.manifest.dom.AndroidManifestNode#getNodeProperties()
+ */
+ @Override
+ public Map<String, String> getNodeProperties()
+ {
+ properties.clear();
+
+ if ((propIcon != null) && (propIcon.trim().length() > 0))
+ {
+ properties.put(PROP_ICON, propIcon);
+ }
+
+ if ((propLabel != null) && (propLabel.length() > 0))
+ {
+ properties.put(PROP_LABEL, propLabel);
+ }
+
+ if (propPriority != null)
+ {
+ properties.put(PROP_PRIORITY, propPriority.toString());
+ }
+
+ return properties;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.model.manifest.dom.AndroidManifestNode#getNodeType()
+ */
+ @Override
+ public NodeType getNodeType()
+ {
+ return NodeType.IntentFilter;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.model.manifest.dom.AndroidManifestNode#isNodeValid()
+ */
+ @Override
+ protected boolean isNodeValid()
+ {
+ AndroidManifestNode[] actions = getAllChildrenFromType(NodeType.Action);
+
+ return actions.length > 0;
+ }
+
+ /**
+ * Gets the icon property value
+ *
+ * @return the icon property value
+ */
+ public String getIcon()
+ {
+ return propIcon;
+ }
+
+ /**
+ * Sets the icon property value. Set it to null to remove it.
+ *
+ * @param icon the icon property value
+ */
+ public void setIcon(String icon)
+ {
+ this.propIcon = icon;
+ }
+
+ /**
+ * Gets the label property value
+ *
+ * @return the label property value
+ */
+ public String getLabel()
+ {
+ return propLabel;
+ }
+
+ /**
+ * Sets the label property value. Set it to null to remove it.
+ *
+ * @param label the label property value
+ */
+ public void setLabel(String label)
+ {
+ this.propLabel = label;
+ }
+
+ /**
+ * Gets the priority property value
+ *
+ * @return the priority property value
+ */
+ public Integer getPriority()
+ {
+ return propPriority;
+ }
+
+ /**
+ * Sets the priority property value. Set it to null to remove it.
+ *
+ * @param priority the priority property value
+ */
+ public void setPriority(Integer priority)
+ {
+ this.propPriority = priority;
+ }
+
+ /**
+ * Adds an Action Node to the Intent Filter Node
+ *
+ * @param action The Action Node
+ */
+ public void addActionNode(ActionNode action)
+ {
+ if (action != null)
+ {
+ if (!children.contains(action))
+ {
+ children.add(action);
+ }
+ }
+ }
+
+ /**
+ * Retrieves all Action Nodes from the Intent Filter Node
+ *
+ * @return all Action Nodes from the Intent Filter Node
+ */
+ public List<ActionNode> getActionNodes()
+ {
+ List<ActionNode> actions = new LinkedList<ActionNode>();
+
+ for (AndroidManifestNode node : getAllChildrenFromType(NodeType.Action))
+ {
+ actions.add((ActionNode) node);
+ }
+
+ return actions;
+ }
+
+ /**
+ * Removes an Action Node from the Intent Filter Node
+ *
+ * @param action the Action Node to be removed
+ */
+ public void removeActionNode(ActionNode action)
+ {
+ if (action != null)
+ {
+ children.remove(action);
+ }
+ }
+
+ /**
+ * Adds a Category Node to the Intent Filter Node
+ *
+ * @param category The Category Node
+ */
+ public void addCategoryNode(CategoryNode category)
+ {
+ if (category != null)
+ {
+ if (!children.contains(category))
+ {
+ children.add(category);
+ }
+ }
+ }
+
+ /**
+ * Adds a Category Node to the Intent Filter Node
+ *
+ * @param category The Category Node
+ */
+ public void addUsesPermissionNode(UsesPermissionNode permission)
+ {
+ if (permission != null)
+ {
+ if (!children.contains(permission))
+ {
+ children.add(permission);
+ }
+ }
+ }
+
+ /**
+ * Retrieves all Category Nodes from the Intent Filter Node
+ *
+ * @return all Category Nodes from the Intent Filter Node
+ */
+ public List<CategoryNode> getCategoryNodes()
+ {
+ List<CategoryNode> categories = new LinkedList<CategoryNode>();
+
+ for (AndroidManifestNode node : getAllChildrenFromType(NodeType.Category))
+ {
+ categories.add((CategoryNode) node);
+ }
+
+ return categories;
+ }
+
+ /**
+ * Retrieves all Uses permission Nodes from the Intent Filter Node
+ *
+ * @return all Category Nodes from the Intent Filter Node
+ */
+ public List<UsesPermissionNode> getUsesPermissionNodes()
+ {
+ List<UsesPermissionNode> permissions = new LinkedList<UsesPermissionNode>();
+
+ for (AndroidManifestNode node : getAllChildrenFromType(NodeType.UsesPermission))
+ {
+ permissions.add((UsesPermissionNode) node);
+ }
+
+ return permissions;
+ }
+
+ /**
+ * Removes a Category Node from the Intent Filter Node
+ *
+ * @param category the Category Node to be removed
+ */
+ public void removeCategoryNode(CategoryNode category)
+ {
+ if (category != null)
+ {
+ children.remove(category);
+ }
+ }
+
+ /**
+ * Removes a UsesPermission Node from the Intent Filter Node
+ *
+ * @param permission the Category Node to be removed
+ */
+ public void removeUsesPermissionNode(UsesPermissionNode permission)
+ {
+ if (permission != null)
+ {
+ children.remove(permission);
+ }
+ }
+
+ /**
+ * Adds a Data Node to the Intent Filter Node
+ *
+ * @param data The Data Node
+ */
+ public void addDataNode(DataNode data)
+ {
+ if (data != null)
+ {
+ if (!children.contains(data))
+ {
+ children.add(data);
+ }
+ }
+ }
+
+ /**
+ * Retrieves all Data Nodes from the Intent Filter Node
+ *
+ * @return all Data Nodes from the Intent Filter Node
+ */
+ public List<DataNode> getDataNodes()
+ {
+ List<DataNode> datas = new LinkedList<DataNode>();
+
+ for (AndroidManifestNode node : getAllChildrenFromType(NodeType.Data))
+ {
+ datas.add((DataNode) node);
+ }
+
+ return datas;
+ }
+
+ /**
+ * Removes a Data Node from the Intent Filter Node
+ *
+ * @param data the Data Node to be removed
+ */
+ public void removeDataNode(DataNode data)
+ {
+ if (data != null)
+ {
+ children.remove(data);
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.model.manifest.dom.AndroidManifestNode#getSpecificNodeErrors()
+ */
+ @Override
+ protected List<IStatus> getSpecificNodeProblems()
+ {
+ return null;
+ }
+
+ /**
+ * Check if this intent-filter node has any information or if it is empty.
+ * @return True if this node has no information, false otherwise.
+ * */
+ public boolean isEmpty()
+ {
+ //an intent-filter node may have action nodes, category nodes, data nodes or attributes (properties).
+ return getActionNodes().isEmpty() && getCategoryNodes().isEmpty()
+ && getDataNodes().isEmpty() && getNodeProperties().isEmpty();
+ }
+
+ public ActionNode getActionNode(String name)
+ {
+ ActionNode result = null;
+
+ //iterate over action nodes
+ for (ActionNode actionNode : getActionNodes())
+ {
+ if (actionNode.getName().equals(name))
+ {
+ result = actionNode;
+ }
+ }
+
+ return result;
+ }
+
+ public CategoryNode getCategoryNode(String name)
+ {
+ CategoryNode result = null;
+
+ //iterate over action nodes
+ for (CategoryNode categoryNode : getCategoryNodes())
+ {
+ if (categoryNode.getName().equals(name))
+ {
+ result = categoryNode;
+ }
+ }
+
+ return result;
+ }
+
+}
diff --git a/src/plugins/common/src/com/motorola/studio/android/model/manifest/dom/ManifestNode.java b/src/plugins/common/src/com/motorola/studio/android/model/manifest/dom/ManifestNode.java
new file mode 100644
index 0000000..65caeaf
--- /dev/null
+++ b/src/plugins/common/src/com/motorola/studio/android/model/manifest/dom/ManifestNode.java
@@ -0,0 +1,636 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.model.manifest.dom;
+
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.IStatus;
+
+/**
+ * Class that represents a <manifest> node on AndroidManifest.xml file
+ */
+public class ManifestNode extends AndroidManifestNode implements IAndroidManifestProperties
+{
+ static
+ {
+ defaultProperties.add(PROP_XMLNS);
+ defaultProperties.add(PROP_PACKAGE);
+ defaultProperties.add(PROP_SHAREDUSERID);
+ defaultProperties.add(PROP_VERSIONCODE);
+ defaultProperties.add(PROP_VERSIONNAME);
+ }
+
+ /**
+ * Default value for xmlns:android property
+ */
+ private static final String PROP_XMLNS_DEFAULTVALUE =
+ "http://schemas.android.com/apk/res/android";
+
+ /**
+ * The package property
+ */
+ private String propPackage = null;
+
+ /**
+ * The sharedUserId property
+ */
+ private String propSharedUserId = null;
+
+ /**
+ * The versionCode property
+ */
+ private Integer propVersionCode = null;
+
+ /**
+ * The versionName property
+ */
+ private String propVersionName = null;
+
+ public ManifestNode(String packageName)
+ {
+ setPackage(packageName);
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.model.manifest.dom.AndroidManifestNode#canContains(com.motorola.studio.android.model.manifest.dom.AndroidManifestNode.NodeType)
+ */
+ @Override
+ protected boolean canContains(NodeType nodeType)
+ {
+ return (nodeType == NodeType.Application) || (nodeType == NodeType.Instrumentation)
+ || (nodeType == NodeType.Permission) || (nodeType == NodeType.PermissionGroup)
+ || (nodeType == NodeType.PermissionTree) || (nodeType == NodeType.UsesPermission)
+ || (nodeType == NodeType.UsesFeature) || (nodeType == NodeType.UsesSdk)
+ || (nodeType == NodeType.Comment) || (nodeType == NodeType.MetaData)
+ || (nodeType == NodeType.Unknown);
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.model.manifest.dom.AndroidManifestNode#getNodeType()
+ */
+ @Override
+ public NodeType getNodeType()
+ {
+ return NodeType.Manifest;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.model.manifest.dom.AndroidManifestNode#getNodeProperties()
+ */
+ @Override
+ public Map<String, String> getNodeProperties()
+ {
+ properties.clear();
+
+ properties.put(PROP_XMLNS, PROP_XMLNS_DEFAULTVALUE);
+ properties.put(PROP_PACKAGE, propPackage);
+
+ if (propSharedUserId != null)
+ {
+ properties.put(PROP_SHAREDUSERID, propSharedUserId);
+ }
+
+ if (propVersionCode != null)
+ {
+ properties.put(PROP_VERSIONCODE, propVersionCode.toString());
+ }
+
+ if (propVersionName != null)
+ {
+ properties.put(PROP_VERSIONNAME, propVersionName);
+ }
+
+ return properties;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.model.manifest.dom.AndroidManifestNode#isNodeValid()
+ */
+ @Override
+ protected boolean isNodeValid()
+ {
+ int applicationCount = 0;
+ boolean allAcceptable = true;
+
+ for (AndroidManifestNode child : children)
+ {
+ if (child.getNodeType() == NodeType.Application)
+ {
+ applicationCount++;
+ }
+ else if (!canContains(child.getNodeType()))
+ {
+ allAcceptable = false;
+ break;
+ }
+ }
+
+ return allAcceptable && (applicationCount == 1) && isValidPackageName(propPackage);
+ }
+
+ /**
+ * Checks if a package name is valid
+ *
+ * @param packageName The package name to be verified
+ *
+ * @return true if the package name is valid or false otherwise
+ */
+ private boolean isValidPackageName(String packageName)
+ {
+ boolean isValid = false;
+
+ String[] parts = packageName.split("\\.");
+
+ if (parts.length > 1)
+ {
+ isValid = true;
+
+ for (String part : parts)
+ {
+ isValid &= part.length() > 0;
+ }
+ }
+
+ return isValid;
+ }
+
+ /**
+ * Sets the package property value.
+ *
+ * @param packageName the package property value
+ */
+ public void setPackage(String packageName)
+ {
+ Assert.isLegal(packageName != null);
+ propPackage = packageName;
+ }
+
+ /**
+ * Gets the package property value.
+ *
+ * @return the package property value
+ */
+ public String getPackageName()
+ {
+ return propPackage;
+ }
+
+ /**
+ * Sets the android:sharedUserId property value. Set it to null
+ * if you do not want to use it.
+ *
+ * @param sharedUserId the sharedUserId property value
+ */
+ public void setSharedUserId(String sharedUserId)
+ {
+ propSharedUserId = sharedUserId;
+ }
+
+ /**
+ * Gets the android:sharedUserId property value.
+ *
+ * @return the android:sharedUserId property value
+ */
+ public String getSharedUserId()
+ {
+ return propSharedUserId;
+ }
+
+ /**
+ * Sets the android:versionCode property value. Set it to null
+ * if you do not want to use it.
+ *
+ * @param versionCode the versionCode property value
+ */
+ public void setVersionCode(Integer versionCode)
+ {
+ propVersionCode = versionCode;
+ }
+
+ /**
+ * Gets the android:versionCode property value.
+ *
+ * @return the android:versionCode property value.
+ */
+ public Integer getVersionCode()
+ {
+ return propVersionCode;
+ }
+
+ /**
+ * Sets the android:versionName property value. Set it to null
+ * if you do not want to use it.
+ *
+ * @param versionName the versionName property value
+ */
+ public void setVersionName(String versionName)
+ {
+ propVersionName = versionName;
+ }
+
+ /**
+ * Gets the android:versionName property value.
+ *
+ * @return the android:versionName property value.
+ */
+ public String getVersionName()
+ {
+ return propVersionName;
+ }
+
+ /**
+ * Retrieves the application node. Creates a new if it does not exist.
+ *
+ * @return the application node
+ */
+ public ApplicationNode getApplicationNode()
+ {
+ ApplicationNode applicationNode = null;
+
+ for (AndroidManifestNode appNode : getAllChildrenFromType(NodeType.Application))
+ {
+ applicationNode = (ApplicationNode) appNode;
+ break;
+ }
+
+ if (applicationNode == null)
+ {
+ applicationNode = new ApplicationNode("");
+ addChild(applicationNode);
+ }
+
+ return applicationNode;
+ }
+
+ /**
+ * Adds an Instrumentation Node to the Manifest Node
+ *
+ * @param instrumentation The Instrumentation Node
+ */
+ public void addInstrumentationNode(InstrumentationNode instrumentation)
+ {
+ if (instrumentation != null)
+ {
+ if (!children.contains(instrumentation))
+ {
+ children.add(instrumentation);
+ }
+ }
+ }
+
+ /**
+ * Retrieves all Instrumentation Nodes from the Manifest Node
+ *
+ * @return all Instrumentation Nodes from the Manifest Node
+ */
+ public List<InstrumentationNode> getInstrumentationNodes()
+ {
+ List<InstrumentationNode> instrumentations = new LinkedList<InstrumentationNode>();
+
+ for (AndroidManifestNode node : getAllChildrenFromType(NodeType.Instrumentation))
+ {
+ instrumentations.add((InstrumentationNode) node);
+ }
+
+ return instrumentations;
+ }
+
+ /**
+ * Removes an Instrumentation Node from the Manifest Node
+ *
+ * @param instrumentation the Instrumentation Node to be removed
+ */
+ public void removeInstrumentationNode(InstrumentationNode instrumentation)
+ {
+ if (instrumentation != null)
+ {
+ children.remove(instrumentation);
+ }
+ }
+
+ /**
+ * Adds a Permission Node to the Manifest Node
+ *
+ * @param permission The Permission Node
+ */
+ public void addPermissionNode(PermissionNode permission)
+ {
+ if (permission != null)
+ {
+ if (!children.contains(permission))
+ {
+ children.add(permission);
+ }
+ }
+ }
+
+ /**
+ * Retrieves all Permission Nodes from the Manifest Node
+ *
+ * @return all Permission Nodes from the Manifest Node
+ */
+ public List<PermissionNode> getPermissionNodes()
+ {
+ List<PermissionNode> permissions = new LinkedList<PermissionNode>();
+
+ for (AndroidManifestNode node : getAllChildrenFromType(NodeType.Permission))
+ {
+ permissions.add((PermissionNode) node);
+ }
+
+ return permissions;
+ }
+
+ /**
+ * Removes a Permission Node from the Manifest Node
+ *
+ * @param permission the Permission Node to be removed
+ */
+ public void removePermissionNode(PermissionNode permission)
+ {
+ if (permission != null)
+ {
+ children.remove(permission);
+ }
+ }
+
+ /**
+ * Adds a Permission Group Node to the Manifest Node
+ *
+ * @param permissionGroup The Permission Group Node
+ */
+ public void addPermissionGroupNode(PermissionGroupNode permissionGroup)
+ {
+ if (permissionGroup != null)
+ {
+ if (!children.contains(permissionGroup))
+ {
+ children.add(permissionGroup);
+ }
+ }
+ }
+
+ /**
+ * Retrieves all Permission Group Nodes from the Manifest Node
+ *
+ * @return all Permission Group Nodes from the Manifest Node
+ */
+ public List<PermissionGroupNode> getPermissionGroupNodes()
+ {
+ List<PermissionGroupNode> permissionGroups = new LinkedList<PermissionGroupNode>();
+
+ for (AndroidManifestNode node : getAllChildrenFromType(NodeType.PermissionGroup))
+ {
+ permissionGroups.add((PermissionGroupNode) node);
+ }
+
+ return permissionGroups;
+ }
+
+ /**
+ * Removes a Permission Group Node from the Manifest Node
+ *
+ * @param permissionGroup the Permission Group Node to be removed
+ */
+ public void removePermissionGroupNode(PermissionGroupNode permissionGroup)
+ {
+ if (permissionGroup != null)
+ {
+ children.remove(permissionGroup);
+ }
+ }
+
+ /**
+ * Adds a Permission Tree Node to the Manifest Node
+ *
+ * @param permissionTree The Permission Tree Node
+ */
+ public void addPermissionTreeNode(PermissionTreeNode permissionTree)
+ {
+ if (permissionTree != null)
+ {
+ if (!children.contains(permissionTree))
+ {
+ children.add(permissionTree);
+ }
+ }
+ }
+
+ /**
+ * Retrieves all Permission Tree Nodes from the Manifest Node
+ *
+ * @return all Permission Tree Nodes from the Manifest Node
+ */
+ public List<PermissionTreeNode> getPermissionTreeNodes()
+ {
+ List<PermissionTreeNode> permissionTrees = new LinkedList<PermissionTreeNode>();
+
+ for (AndroidManifestNode node : getAllChildrenFromType(NodeType.PermissionTree))
+ {
+ permissionTrees.add((PermissionTreeNode) node);
+ }
+
+ return permissionTrees;
+ }
+
+ /**
+ * Removes a Permission Tree Node from the Manifest Node
+ *
+ * @param permissionTree the Permission Tree Node to be removed
+ */
+ public void removePermissionTreeNode(PermissionTreeNode permissionTree)
+ {
+ if (permissionTree != null)
+ {
+ children.remove(permissionTree);
+ }
+ }
+
+ /**
+ * Adds an Uses Permission Node to the Manifest Node
+ *
+ * @param usesPermission The Uses Permission Node
+ */
+ public void addUsesPermissionNode(UsesPermissionNode usesPermission)
+ {
+ if (usesPermission != null)
+ {
+ if (!children.contains(usesPermission))
+ {
+ addBeforeApplicationNode(usesPermission);
+ }
+ }
+ }
+
+ /**
+ * Retrieves all Uses Permission Nodes from the Manifest Node
+ *
+ * @return all Uses Permission Nodes from the Manifest Node
+ */
+ public List<UsesPermissionNode> getUsesPermissionNodes()
+ {
+ List<UsesPermissionNode> usesPermissions = new LinkedList<UsesPermissionNode>();
+
+ for (AndroidManifestNode node : getAllChildrenFromType(NodeType.UsesPermission))
+ {
+ usesPermissions.add((UsesPermissionNode) node);
+ }
+
+ return usesPermissions;
+ }
+
+ /**
+ * Removes an Uses Permission Node from the Manifest Node
+ *
+ * @param usesPermission the Uses Permission Node to be removed
+ */
+ public void removeUsesPermissionNode(UsesPermissionNode usesPermission)
+ {
+ if (usesPermission != null)
+ {
+ children.remove(usesPermission);
+ }
+ }
+
+ /**
+ * Removes an Used Permission from the Manifest Node.
+ * Convenience method to remove an used permission using its name.
+ * Note: all uses-permission nodes of the given permission are removed.
+ *
+ * @param permission the used permission name to be removed
+ * @return the number of uses-permission nodes removed
+ */
+ public int removeUsesPermissionNode(String permission)
+ {
+ int nodesRemoved = 0;
+ for (UsesPermissionNode node : getUsesPermissionNodes())
+ {
+ if (node.getName().equals(permission))
+ {
+ removeUsesPermissionNode(node);
+ nodesRemoved++;
+ }
+ }
+ return nodesRemoved;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.model.manifest.dom.AndroidManifestNode#getSpecificNodeErrors()
+ */
+ @Override
+ protected List<IStatus> getSpecificNodeProblems()
+ {
+ return null;
+ }
+
+ /**
+ * Adds an Uses Feature Node to the Manifest Node
+ *
+ * @param usesFeature The Uses Feature Node
+ */
+ public void addUsesFeatureNode(UsesFeatureNode usesFeature)
+ {
+ if (usesFeature != null)
+ {
+ if (!children.contains(usesFeature))
+ {
+ addBeforeApplicationNode(usesFeature);
+ }
+ }
+ }
+
+ /**
+ * Retrieves Uses Feature Node from the Manifest Node
+ *
+ * @return null if no node if the featureName is found, or the refence to the node if it exists
+ */
+ public UsesFeatureNode getUsesFeatureNode(String featureName)
+ {
+ UsesFeatureNode usesFeature = null;
+
+ for (AndroidManifestNode node : getAllChildrenFromType(NodeType.UsesFeature))
+ {
+ if ((node.getNodeProperties() != null)
+ && node.getNodeProperties().containsKey("android:name")
+ && node.getNodeProperties().get("android:name").equals(featureName))
+ {
+ usesFeature = (UsesFeatureNode) node;
+ break;
+ }
+ }
+
+ return usesFeature;
+ }
+
+ /**
+ * Retrieves Uses SDK Node from the Manifest Node
+ *
+ * @return null if no uses-sdk node is found, or the refence to the node if it exists
+ */
+ public UsesSDKNode getUsesSdkNode()
+ {
+ UsesSDKNode usesSDKNode = null;
+
+ for (AndroidManifestNode node : getAllChildrenFromType(NodeType.UsesSdk))
+ {
+ if (node instanceof UsesSDKNode)
+ {
+ //get the first node - it should NOT have more than once
+ usesSDKNode = (UsesSDKNode) node;
+ break;
+ }
+ }
+
+ return usesSDKNode;
+ }
+
+ /**
+ * Adds an Uses SDK Node to the Manifest Node
+ *
+ * @param usesSDK The Uses SDK Node
+ */
+ public void addUsesSdkNode(UsesSDKNode usesSdk)
+ {
+ if (usesSdk != null)
+ {
+ if (!children.contains(usesSdk))
+ {
+ addBeforeApplicationNode(usesSdk);
+ }
+ }
+ }
+
+ /**
+ * Adds before application node (if it exists), otherwise adds after
+ * @param node
+ */
+ private void addBeforeApplicationNode(AndroidManifestNode node)
+ {
+ ApplicationNode applicationNode = getApplicationNode();
+ if (applicationNode != null)
+ {
+ int appNodeIdx = children.indexOf(applicationNode);
+ children.add(appNodeIdx, node);
+ }
+ else
+ {
+ children.add(node);
+ }
+ }
+}
diff --git a/src/plugins/common/src/com/motorola/studio/android/model/manifest/dom/MetadataNode.java b/src/plugins/common/src/com/motorola/studio/android/model/manifest/dom/MetadataNode.java
new file mode 100644
index 0000000..a7eea28
--- /dev/null
+++ b/src/plugins/common/src/com/motorola/studio/android/model/manifest/dom/MetadataNode.java
@@ -0,0 +1,185 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.model.manifest.dom;
+
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.IStatus;
+
+/**
+ * Class that represents a <meta-data> node on AndroidManifest.xml file
+ */
+public class MetadataNode extends AndroidManifestNode implements IAndroidManifestProperties
+{
+ static
+ {
+ defaultProperties.add(PROP_NAME);
+ defaultProperties.add(PROP_RESOURCE);
+ defaultProperties.add(PROP_VALUE);
+ }
+
+ /**
+ * The name property
+ */
+ private String propName = null;
+
+ /**
+ * The resource property
+ */
+ private String propResource = null;
+
+ /**
+ * The value property
+ */
+ private String propValue = null;
+
+ /**
+ * Default constructor
+ *
+ * @param name the name property. It must not be null.
+ */
+ public MetadataNode(String name)
+ {
+ Assert.isLegal(name != null);
+ this.propName = name;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.model.manifest.dom.AndroidManifestNode#canContains(com.motorola.studio.android.model.manifest.dom.AndroidManifestNode.NodeType)
+ */
+ @Override
+ protected boolean canContains(NodeType nodeType)
+ {
+ // Always returns false. This node can not contain children.
+ return false;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.model.manifest.dom.AndroidManifestNode#getNodeProperties()
+ */
+ @Override
+ public Map<String, String> getNodeProperties()
+ {
+ properties.clear();
+
+ properties.put(PROP_NAME, propName);
+
+ if ((propResource != null) && (propResource.trim().length() > 0))
+ {
+ properties.put(PROP_RESOURCE, propResource);
+ }
+
+ if ((propValue != null) && (propValue.length() > 0))
+ {
+ properties.put(PROP_VALUE, propValue);
+ }
+
+ return properties;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.model.manifest.dom.AndroidManifestNode#getNodeType()
+ */
+ @Override
+ public NodeType getNodeType()
+ {
+ return NodeType.MetaData;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.model.manifest.dom.AndroidManifestNode#isNodeValid()
+ */
+ @Override
+ protected boolean isNodeValid()
+ {
+ boolean containsOnlyResource = (propResource != null) && (propValue == null);
+ boolean containsOnlyValue = (propResource == null) && (propValue != null);
+
+ return (propName.trim().length() > 0) && (containsOnlyResource || containsOnlyValue);
+ }
+
+ /**
+ * Gets the name property value
+ *
+ * @return the name property value
+ */
+ public String getName()
+ {
+ return propName;
+ }
+
+ /**
+ * Sets the name property value. This property must not be null.
+ *
+ * @param name the name property value
+ */
+ public void setName(String name)
+ {
+ Assert.isLegal(name != null);
+ this.propName = name;
+ }
+
+ /**
+ * Gets the resource property value
+ *
+ * @return the resource property value
+ */
+ public String getResource()
+ {
+ return propResource;
+ }
+
+ /**
+ * Sets the resource property value. This property must not be null.
+ *
+ * @param resource the resource property value
+ */
+ public void setResource(String resource)
+ {
+ this.propResource = resource;
+ }
+
+ /**
+ * Gets the value property value
+ *
+ * @return the value property value
+ */
+ public String getValue()
+ {
+ return propValue;
+ }
+
+ /**
+ * Sets the value property value. This property must not be null.
+ *
+ * @param value the value property value
+ */
+ public void setValue(String value)
+ {
+ this.propValue = value;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.model.manifest.dom.AndroidManifestNode#getSpecificNodeErrors()
+ */
+ @Override
+ protected List<IStatus> getSpecificNodeProblems()
+ {
+ return null;
+ }
+}
diff --git a/src/plugins/common/src/com/motorola/studio/android/model/manifest/dom/PermissionGroupNode.java b/src/plugins/common/src/com/motorola/studio/android/model/manifest/dom/PermissionGroupNode.java
new file mode 100644
index 0000000..bed3268
--- /dev/null
+++ b/src/plugins/common/src/com/motorola/studio/android/model/manifest/dom/PermissionGroupNode.java
@@ -0,0 +1,106 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.model.manifest.dom;
+
+import java.util.List;
+
+import org.eclipse.core.runtime.IStatus;
+
+/**
+ * Class that represents a <permission-group> node on AndroidManifest.xml file
+ */
+public class PermissionGroupNode extends AbstractIconLabelNameNode
+{
+ static
+ {
+ defaultProperties.add(PROP_DESCRIPTION);
+ }
+
+ /**
+ * The description property
+ */
+ private String propDescription = null;
+
+ /**
+ * Default constructor
+ *
+ * @param name the name property. It must not be null;
+ */
+ public PermissionGroupNode(String name)
+ {
+ super(name);
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.model.manifest.dom.AndroidManifestNode#canContains(com.motorola.studio.android.model.manifest.dom.AndroidManifestNode.NodeType)
+ */
+ @Override
+ protected boolean canContains(NodeType nodeType)
+ {
+ // Always returns false. This node can not contain children.
+ return false;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.model.manifest.dom.AbstractIconLabelNameNode#addAdditionalProperties()
+ */
+ @Override
+ protected void addAdditionalProperties()
+ {
+ if ((propDescription != null) && (propDescription.length() > 0))
+ {
+ properties.put(PROP_DESCRIPTION, propDescription);
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.model.manifest.dom.AndroidManifestNode#getNodeType()
+ */
+ @Override
+ public NodeType getNodeType()
+ {
+ return NodeType.PermissionGroup;
+ }
+
+ /**
+ * Gets the description property value
+ *
+ * @return the description property value
+ */
+ public String getDescription()
+ {
+ return propDescription;
+ }
+
+ /**
+ * Sets the description property value. Set it to null to remove it.
+ *
+ * @param description the description property value
+ */
+ public void setDescription(String description)
+ {
+ this.propDescription = description;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.model.manifest.dom.AndroidManifestNode#getSpecificNodeErrors()
+ */
+ @Override
+ protected List<IStatus> getSpecificNodeProblems()
+ {
+ return null;
+ }
+}
diff --git a/src/plugins/common/src/com/motorola/studio/android/model/manifest/dom/PermissionNode.java b/src/plugins/common/src/com/motorola/studio/android/model/manifest/dom/PermissionNode.java
new file mode 100644
index 0000000..157a502
--- /dev/null
+++ b/src/plugins/common/src/com/motorola/studio/android/model/manifest/dom/PermissionNode.java
@@ -0,0 +1,243 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.model.manifest.dom;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.core.runtime.IStatus;
+
+/**
+ * Class that represents a <permission> node on AndroidManifest.xml file
+ */
+public class PermissionNode extends AbstractIconLabelNameNode
+{
+ static
+ {
+ defaultProperties.add(PROP_DESCRIPTION);
+ defaultProperties.add(PROP_PERMISSIONGROUP);
+ defaultProperties.add(PROP_PROTECTIONLEVEL);
+ }
+
+ /**
+ * Enumeration for protectionLevel property
+ */
+ public enum ProtectionLevel
+ {
+ normal, dangerous, signature, signatureOrSystem
+ }
+
+ /**
+ * Map to resolve the string<->enumeration association of protectionLevel property
+ */
+ private static Map<String, ProtectionLevel> protectionLevelMap;
+
+ static
+ {
+ // Loads the map for protectionLevel
+ protectionLevelMap = new HashMap<String, ProtectionLevel>();
+ protectionLevelMap.put(ProtectionLevel.normal.toString().toLowerCase(),
+ ProtectionLevel.normal);
+ protectionLevelMap.put(ProtectionLevel.dangerous.toString().toLowerCase(),
+ ProtectionLevel.dangerous);
+ protectionLevelMap.put(ProtectionLevel.signature.toString().toLowerCase(),
+ ProtectionLevel.signature);
+ protectionLevelMap.put(ProtectionLevel.signatureOrSystem.toString().toLowerCase(),
+ ProtectionLevel.signatureOrSystem);
+ }
+
+ /**
+ * Gets the enumeration value from the ProtectionLevel enumeration from a given name
+ *
+ * @param name the protectionLevel name
+ * @return the enumeration value from ProtectionLevel enumeration
+ */
+ public static ProtectionLevel getProtectionLevel(String name)
+ {
+ ProtectionLevel protectionLevel = null;
+
+ if (name != null)
+ {
+ String pl = name.trim().toLowerCase();
+ protectionLevel = protectionLevelMap.get(pl);
+ }
+
+ return protectionLevel;
+ }
+
+ /**
+ * Gets the protectionLevel parameter name from a given ProtectionLevel enumeration value
+ *
+ * @param protectionLevel the enumeration value
+ * @return the parameter name
+ */
+ public static String getProtectionLevelName(ProtectionLevel protectionLevel)
+ {
+ String name = "";
+
+ if (protectionLevel != null)
+ {
+ name = protectionLevel.toString();
+ }
+
+ return name;
+ }
+
+ /**
+ * The description property
+ */
+ private String propDescription = null;
+
+ /**
+ * The permissionGroup property
+ */
+ private String propPermissionGroup = null;
+
+ /**
+ * The protectionLevel property
+ */
+ private ProtectionLevel propProtectionLevel = null;
+
+ /**
+ * Default constructor
+ *
+ * @param name the name property. It must not be set to null
+ */
+ public PermissionNode(String name)
+ {
+ super(name);
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.model.manifest.dom.AndroidManifestNode#canContains(com.motorola.studio.android.model.manifest.dom.AndroidManifestNode.NodeType)
+ */
+ @Override
+ protected boolean canContains(NodeType nodeType)
+ {
+ // Always returns false. This node can not contain children.
+ return false;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.model.manifest.dom.AbstractIconLabelNameNode#addAdditionalProperties()
+ */
+ @Override
+ protected void addAdditionalProperties()
+ {
+ String protectionLevelName = getProtectionLevelName(propProtectionLevel);
+ properties.put(PROP_PROTECTIONLEVEL, protectionLevelName);
+
+ if ((propDescription != null) && (propDescription.length() > 0))
+ {
+ properties.put(PROP_DESCRIPTION, propDescription);
+ }
+
+ if ((propPermissionGroup != null) && (propPermissionGroup.trim().length() > 0))
+ {
+ properties.put(PROP_PERMISSIONGROUP, propPermissionGroup);
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.model.manifest.dom.AndroidManifestNode#getNodeType()
+ */
+ @Override
+ public NodeType getNodeType()
+ {
+ return NodeType.Permission;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.model.manifest.dom.AndroidManifestNode#isNodeValid()
+ */
+ @Override
+ protected boolean isNodeValid()
+ {
+ boolean isProtectionLevelValid = propProtectionLevel != null;
+
+ return super.isNodeValid() && isProtectionLevelValid;
+ }
+
+ /**
+ * Gets the description property value
+ *
+ * @return the description property value
+ */
+ public String getDescription()
+ {
+ return propDescription;
+ }
+
+ /**
+ * Sets the description property value. Set it to null to remove it.
+ *
+ * @param description the description property value
+ */
+ public void setDescription(String description)
+ {
+ this.propDescription = description;
+ }
+
+ /**
+ * Gets the permissionGroup property value
+ *
+ * @return the permissionGroup property value
+ */
+ public String getPermissionGroup()
+ {
+ return propPermissionGroup;
+ }
+
+ /**
+ * Sets the permissionGroup property value. Set it to null to remove it.
+ *
+ * @param % the permissionGroup property value
+ */
+ public void setPermissionGroup(String permissionGroup)
+ {
+ this.propPermissionGroup = permissionGroup;
+ }
+
+ /**
+ * Gets the protectionLevel property value
+ *
+ * @return the protectionLevel property value
+ */
+ public ProtectionLevel getProtectionLevel()
+ {
+ return propProtectionLevel;
+ }
+
+ /**
+ * Sets the protectionLevel property value. Set it to null to remove it.
+ *
+ * @param % the protectionLevel property value
+ */
+ public void setProtectionLevel(ProtectionLevel protectionLevel)
+ {
+ this.propProtectionLevel = protectionLevel;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.model.manifest.dom.AndroidManifestNode#getSpecificNodeErrors()
+ */
+ @Override
+ protected List<IStatus> getSpecificNodeProblems()
+ {
+ return null;
+ }
+}
diff --git a/src/plugins/common/src/com/motorola/studio/android/model/manifest/dom/PermissionTreeNode.java b/src/plugins/common/src/com/motorola/studio/android/model/manifest/dom/PermissionTreeNode.java
new file mode 100644
index 0000000..b0d23fb
--- /dev/null
+++ b/src/plugins/common/src/com/motorola/studio/android/model/manifest/dom/PermissionTreeNode.java
@@ -0,0 +1,73 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.model.manifest.dom;
+
+import java.util.List;
+
+import org.eclipse.core.runtime.IStatus;
+
+/**
+ * Class that represents a <permission-tree> node on AndroidManifest.xml file
+ */
+public class PermissionTreeNode extends AbstractIconLabelNameNode
+{
+ /**
+ * Default constructor
+ *
+ * @param name the name property. It must not be null;
+ */
+ public PermissionTreeNode(String name)
+ {
+ super(name);
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.model.manifest.dom.AndroidManifestNode#canContains(com.motorola.studio.android.model.manifest.dom.AndroidManifestNode.NodeType)
+ */
+ @Override
+ protected boolean canContains(NodeType nodeType)
+ {
+ // Always returns false. This node can not contain children.
+ return false;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.model.manifest.dom.AbstractIconLabelNameNode#addAdditionalProperties()
+ */
+ @Override
+ protected void addAdditionalProperties()
+ {
+ // Do nothing
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.model.manifest.dom.AndroidManifestNode#getNodeType()
+ */
+ @Override
+ public NodeType getNodeType()
+ {
+ return NodeType.PermissionTree;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.model.manifest.dom.AndroidManifestNode#getSpecificNodeErrors()
+ */
+ @Override
+ protected List<IStatus> getSpecificNodeProblems()
+ {
+ return null;
+ }
+}
diff --git a/src/plugins/common/src/com/motorola/studio/android/model/manifest/dom/Property.java b/src/plugins/common/src/com/motorola/studio/android/model/manifest/dom/Property.java
new file mode 100644
index 0000000..f9050e0
--- /dev/null
+++ b/src/plugins/common/src/com/motorola/studio/android/model/manifest/dom/Property.java
@@ -0,0 +1,108 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.model.manifest.dom;
+
+/**
+ * Class that represents a property of a node on AndroidManifest.xml file.
+ * For now, it will be only used by the BuildingBlockExplorer view.
+ */
+public class Property
+{
+ /**
+ * Property Label
+ *
+ * For example: "android:name"
+ */
+ private String propertyLabel;
+
+ /**
+ * Property Value
+ *
+ * For example: ".MainActivity"
+ */
+ private String propertyValue;
+
+ /**
+ * Parent node of the property.
+ */
+ private AndroidManifestNode parent;
+
+ /**
+ * Constructor
+ * @param propertyLabel The property label
+ * @param propertyValue The property value
+ * @param parent The property node parent
+ */
+ public Property(String propertyLabel, String propertyValue, AndroidManifestNode parent)
+ {
+ this.propertyLabel = propertyLabel;
+ this.propertyValue = propertyValue;
+ this.parent = parent;
+ }
+
+ /**
+ * Get the label of the property.
+ * @return Property label
+ */
+ public String getPropertyLabel()
+ {
+ return propertyLabel;
+ }
+
+ /**
+ * Set the label of the property.
+ * @param propertyLabel
+ */
+ public void setPropertyLabel(String propertyLabel)
+ {
+ this.propertyLabel = propertyLabel;
+ }
+
+ /**
+ * Get the value of the property.
+ * @return Property value
+ */
+ public String getPropertyValue()
+ {
+ return propertyValue;
+ }
+
+ /**
+ * Set the value of the property.
+ * @param propertyValue
+ */
+ public void setPropertyValue(String propertyValue)
+ {
+ this.propertyValue = propertyValue;
+ }
+
+ /**
+ * @return the parent
+ */
+ public AndroidManifestNode getParent()
+ {
+ return parent;
+ }
+
+ /**
+ * @param parent the parent to set
+ */
+ public void setParent(AndroidManifestNode parent)
+ {
+ this.parent = parent;
+ }
+
+}
diff --git a/src/plugins/common/src/com/motorola/studio/android/model/manifest/dom/ProviderNode.java b/src/plugins/common/src/com/motorola/studio/android/model/manifest/dom/ProviderNode.java
new file mode 100644
index 0000000..40c7cbd
--- /dev/null
+++ b/src/plugins/common/src/com/motorola/studio/android/model/manifest/dom/ProviderNode.java
@@ -0,0 +1,480 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.model.manifest.dom;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.IStatus;
+
+/**
+ * Class that represents a <provider> node on AndroidManifest.xml file
+ */
+public class ProviderNode extends AbstractBuildingBlockNode
+{
+ static
+ {
+ defaultProperties.add(PROP_AUTHORITIES);
+ defaultProperties.add(PROP_GRANTURIPERMISSIONS);
+ defaultProperties.add(PROP_INITORDER);
+ defaultProperties.add(PROP_MULTIPROCESS);
+ defaultProperties.add(PROP_READPERMISSION);
+ defaultProperties.add(PROP_SYNCABLE);
+ defaultProperties.add(PROP_WRITEPERMISSION);
+ }
+
+ /**
+ * The authorities property
+ */
+ private List<String> propAuthorities = null;
+
+ /**
+ * The grantUriPermissions property
+ */
+ private Boolean propGrantUriPermissions = null;
+
+ /**
+ * The initOrder property
+ */
+ private Integer propInitOrder = null;
+
+ /**
+ * The multiprocess property
+ */
+ private Boolean propMultiprocess = null;
+
+ /**
+ * The readPermission property
+ */
+ private String propReadPermission = null;
+
+ /**
+ * The syncable property
+ */
+ private Boolean propSyncable = null;
+
+ /**
+ * The writePermission property
+ */
+ private String propWritePermission = null;
+
+ /**
+ * Default constructor
+ *
+ * @param name The name property. It must not be null.
+ * @param initialAuthority The first authority to be added to the provider.
+ * It must not be null.
+ */
+ public ProviderNode(String name, String initialAuthority)
+ {
+ super(name);
+
+ Assert.isLegal(initialAuthority != null);
+ propAuthorities = new LinkedList<String>();
+ propAuthorities.add(initialAuthority);
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.model.manifest.dom.AndroidManifestNode#canContains(com.motorola.studio.android.model.manifest.dom.AndroidManifestNode.NodeType)
+ */
+ @Override
+ protected boolean canContains(NodeType nodeType)
+ {
+ return (nodeType == NodeType.GrantUriPermission) || (nodeType == NodeType.MetaData)
+ || (nodeType == NodeType.Comment);
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.model.manifest.dom.AbstractIconLabelNameNode#addAdditionalProperties()
+ */
+ @Override
+ protected void addAdditionalProperties()
+ {
+ super.addAdditionalProperties();
+
+ properties.put(PROP_AUTHORITIES, getAuthoritiesList());
+
+ if (propGrantUriPermissions != null)
+ {
+ properties.put(PROP_GRANTURIPERMISSIONS, propGrantUriPermissions.toString());
+ }
+
+ if (propInitOrder != null)
+ {
+ properties.put(PROP_INITORDER, propInitOrder.toString());
+ }
+
+ if (propMultiprocess != null)
+ {
+ properties.put(PROP_MULTIPROCESS, propMultiprocess.toString());
+ }
+
+ if (propReadPermission != null)
+ {
+ properties.put(PROP_READPERMISSION, propReadPermission);
+ }
+
+ if (propSyncable != null)
+ {
+ properties.put(PROP_SYNCABLE, propSyncable.toString());
+ }
+
+ if (propWritePermission != null)
+ {
+ properties.put(PROP_WRITEPERMISSION, propWritePermission);
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.model.manifest.dom.AndroidManifestNode#getNodeType()
+ */
+ @Override
+ public NodeType getNodeType()
+ {
+ return NodeType.Provider;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.model.manifest.dom.AndroidManifestNode#isNodeValid()
+ */
+ @Override
+ protected boolean isNodeValid()
+ {
+ return super.isNodeValid() && (getAuthoritiesList().trim().length() > 0);
+ }
+
+ /**
+ * Gets the Content Provider authorities as a list separated by semicolons, ready to be
+ * used on manifest file
+ *
+ * @return the Content Provider authorities as a list separated by semicolons
+ */
+ private String getAuthoritiesList()
+ {
+ String authorities = "";
+
+ if ((propAuthorities != null) && !propAuthorities.isEmpty())
+ {
+ for (int i = 0; i < (propAuthorities.size() - 1); i++)
+ {
+ authorities += propAuthorities.get(i) + ";";
+ }
+
+ authorities += propAuthorities.get(propAuthorities.size() - 1);
+ }
+
+ return authorities;
+ }
+
+ /**
+ * Adds an authority to the Content Provider
+ *
+ * @param authority The authority name
+ *
+ * @return true if the authority has been added (if it does not exist) or false otherwise
+ */
+ public boolean addAuthority(String authority)
+ {
+ Assert.isLegal(authority != null);
+
+ boolean added = false;
+ String auth = authority.trim();
+
+ if (!propAuthorities.contains(auth))
+ {
+ propAuthorities.add(auth);
+ added = true;
+ }
+
+ return added;
+ }
+
+ /**
+ * Retrieves an array containing all authorities
+ *
+ * @return an array containing all authorities
+ */
+ public String[] getAuthorities()
+ {
+ String[] authorities = new String[propAuthorities.size()];
+
+ authorities = propAuthorities.toArray(authorities);
+
+ return authorities;
+ }
+
+ /**
+ * Removes an authority from the authority list. If the given authority is the last one,
+ * it will not be removed.
+ *
+ * @param authority The authority to be removed
+ *
+ * @return true if the authority has been removed and false otherwise
+ */
+ public boolean removeAuthority(String authority)
+ {
+ Assert.isLegal(authority != null);
+
+ boolean removed = false;
+ String auth = authority.trim();
+
+ if ((propAuthorities.size() > 1) && propAuthorities.contains(auth))
+ {
+ propAuthorities.remove(auth);
+ removed = true;
+ }
+
+ return removed;
+ }
+
+ /**
+ * Gets the grantUriPermissions property value
+ *
+ * @return the grantUriPermissions property value
+ */
+ public Boolean getGrantUriPermissions()
+ {
+ return propGrantUriPermissions;
+ }
+
+ /**
+ * Sets the grantUriPermissions property value. Set it to null to remove it.
+ *
+ * @param grantUriPermissions the grantUriPermissions property value
+ */
+ public void setGrantUriPermissions(Boolean grantUriPermissions)
+ {
+ this.propGrantUriPermissions = grantUriPermissions;
+ }
+
+ /**
+ * Gets the initOrder property value
+ *
+ * @return the initOrder property value
+ */
+ public Integer getInitOrder()
+ {
+ return propInitOrder;
+ }
+
+ /**
+ * Sets the initOrder property value. Set it to null to remove it.
+ *
+ * @param initOrder the initOrder property value
+ */
+ public void setInitOrder(Integer initOrder)
+ {
+ this.propInitOrder = initOrder;
+ }
+
+ /**
+ * Gets the multiprocess property value
+ *
+ * @return the multiprocess property value
+ */
+ public Boolean getMultiprocess()
+ {
+ return propMultiprocess;
+ }
+
+ /**
+ * Sets the multiprocess property value. Set it to null to remove it.
+ *
+ * @param multiprocess the multiprocess property value
+ */
+ public void setMultiprocess(Boolean multiprocess)
+ {
+ this.propMultiprocess = multiprocess;
+ }
+
+ /**
+ * Gets the readPermission property value
+ *
+ * @return the readPermission property value
+ */
+ public String getReadPermission()
+ {
+ return propReadPermission;
+ }
+
+ /**
+ * Sets the readPermission property value. Set it to null to remove it.
+ *
+ * @param readPermission the readPermission property value
+ */
+ public void setReadPermission(String readPermission)
+ {
+ this.propReadPermission = readPermission;
+ }
+
+ /**
+ * Gets the syncable property value
+ *
+ * @return the syncable property value
+ */
+ public Boolean getSyncable()
+ {
+ return propSyncable;
+ }
+
+ /**
+ * Sets the syncable property value. Set it to null to remove it.
+ *
+ * @param syncable the syncable property value
+ */
+ public void setSyncable(Boolean syncable)
+ {
+ this.propSyncable = syncable;
+ }
+
+ /**
+ * Gets the writePermission property value
+ *
+ * @return the writePermission property value
+ */
+ public String getWritePermission()
+ {
+ return propWritePermission;
+ }
+
+ /**
+ * Sets the writePermission property value. Set it to null to remove it.
+ *
+ * @param writePermission the writePermission property value
+ */
+ public void setWritePermission(String writePermission)
+ {
+ this.propWritePermission = writePermission;
+ }
+
+ /**
+ * Adds a Metadata Node to the Provider Node
+ *
+ * @param metadata The Metadata Node
+ */
+ public void addMetadataNode(MetadataNode metadata)
+ {
+ if (metadata != null)
+ {
+ if (!children.contains(metadata))
+ {
+ children.add(metadata);
+ }
+ }
+ }
+
+ /**
+ * Retrieves all Metadata Nodes from the Provider Node
+ *
+ * @return all Metadata Nodes from the Provider Node
+ */
+ public List<MetadataNode> getMetadataNodes()
+ {
+ List<MetadataNode> metadatas = new LinkedList<MetadataNode>();
+
+ for (AndroidManifestNode node : getAllChildrenFromType(NodeType.MetaData))
+ {
+ metadatas.add((MetadataNode) node);
+ }
+
+ return metadatas;
+ }
+
+ /**
+ * Removes a Metadata Node from the Provider Node
+ *
+ * @param metadata the Metadata Node to be removed
+ */
+ public void removeMetadataNode(MetadataNode metadata)
+ {
+ if (metadata != null)
+ {
+ children.remove(metadata);
+ }
+ }
+
+ /**
+ * Adds a Grant Uri Permission Node to the Provider Node
+ *
+ * @param grantUriPermission The Grant Uri Permission Node
+ */
+ public void addGrantUriPermissionNode(GrantUriPermissionNode grantUriPermission)
+ {
+ if (grantUriPermission != null)
+ {
+ if (!children.contains(grantUriPermission))
+ {
+ children.add(grantUriPermission);
+ }
+ }
+ }
+
+ /**
+ * Retrieves all Grant Uri Permission Nodes from the Provider Node
+ *
+ * @return all Grant Uri Permission Nodes from the Provider Node
+ */
+ public List<GrantUriPermissionNode> getGrantUriPermissionsNodes()
+ {
+ List<GrantUriPermissionNode> grantUriPermissions = new LinkedList<GrantUriPermissionNode>();
+
+ for (AndroidManifestNode node : getAllChildrenFromType(NodeType.GrantUriPermission))
+ {
+ grantUriPermissions.add((GrantUriPermissionNode) node);
+ }
+
+ return grantUriPermissions;
+ }
+
+ /**
+ * Removes a Grant Uri Permission Node from the Provider Node
+ *
+ * @param grantUriPermission the Grant Uri Permission Node to be removed
+ */
+ public void removeGrantUriPermissionNode(GrantUriPermissionNode grantUriPermission)
+ {
+ if (grantUriPermission != null)
+ {
+ children.remove(grantUriPermission);
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.model.manifest.dom.AndroidManifestNode#getSpecificNodeErrors()
+ */
+ @Override
+ protected List<IStatus> getSpecificNodeProblems()
+ {
+ return null;
+ }
+
+ /**
+ * Adds an Intent Filter Node to the Provider Node
+ *
+ * @param intentFilter The Intent Filter Node
+ */
+ public void addIntentFilterNode(IntentFilterNode intentFilter)
+ {
+ if (intentFilter != null)
+ {
+ if (!children.contains(intentFilter))
+ {
+ children.add(intentFilter);
+ }
+ }
+ }
+}
diff --git a/src/plugins/common/src/com/motorola/studio/android/model/manifest/dom/ReceiverNode.java b/src/plugins/common/src/com/motorola/studio/android/model/manifest/dom/ReceiverNode.java
new file mode 100644
index 0000000..ac6ed0f
--- /dev/null
+++ b/src/plugins/common/src/com/motorola/studio/android/model/manifest/dom/ReceiverNode.java
@@ -0,0 +1,41 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.model.manifest.dom;
+
+/**
+ * Class that represents a <receiver> node on AndroidManifest.xml file
+ */
+public class ReceiverNode extends ServiceNode
+{
+ /**
+ * Default constructor
+ *
+ * @param name the name property. It must not be null.
+ */
+ public ReceiverNode(String name)
+ {
+ super(name);
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.model.manifest.dom.AndroidManifestNode#getNodeType()
+ */
+ @Override
+ public NodeType getNodeType()
+ {
+ return NodeType.Receiver;
+ }
+}
diff --git a/src/plugins/common/src/com/motorola/studio/android/model/manifest/dom/ServiceNode.java b/src/plugins/common/src/com/motorola/studio/android/model/manifest/dom/ServiceNode.java
new file mode 100644
index 0000000..3a26188
--- /dev/null
+++ b/src/plugins/common/src/com/motorola/studio/android/model/manifest/dom/ServiceNode.java
@@ -0,0 +1,152 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.model.manifest.dom;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import org.eclipse.core.runtime.IStatus;
+
+/**
+ * Class that represents a <service> node on AndroidManifest.xml file
+ */
+public class ServiceNode extends AbstractBuildingBlockNode
+{
+ public ServiceNode(String name)
+ {
+ super(name);
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.model.manifest.dom.AndroidManifestNode#canContains(com.motorola.studio.android.model.manifest.dom.AndroidManifestNode.NodeType)
+ */
+ @Override
+ protected boolean canContains(NodeType nodeType)
+ {
+ return (nodeType == NodeType.IntentFilter) || (nodeType == NodeType.MetaData)
+ || (nodeType == NodeType.Comment);
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.model.manifest.dom.AndroidManifestNode#getNodeType()
+ */
+ @Override
+ public NodeType getNodeType()
+ {
+ return NodeType.Service;
+ }
+
+ /**
+ * Adds an Intent Filter Node to the Service Node
+ *
+ * @param intentFilter The Intent Filter Node
+ */
+ public void addIntentFilterNode(IntentFilterNode intentFilter)
+ {
+ if (intentFilter != null)
+ {
+ if (!children.contains(intentFilter))
+ {
+ children.add(intentFilter);
+ }
+ }
+ }
+
+ /**
+ * Retrieves all Intent Filter Nodes from the Service Node
+ *
+ * @return all Intent Filter Nodes from the Service Node
+ */
+ public List<IntentFilterNode> getIntentFilterNodes()
+ {
+ List<IntentFilterNode> intentFilters = new LinkedList<IntentFilterNode>();
+
+ for (AndroidManifestNode node : getAllChildrenFromType(NodeType.IntentFilter))
+ {
+ intentFilters.add((IntentFilterNode) node);
+ }
+
+ return intentFilters;
+ }
+
+ /**
+ * Removes an Intent Filter Node from the Service Node
+ *
+ * @param intentFilter the Intent Filter Node to be removed
+ */
+ public void removeIntentFilterNode(IntentFilterNode intentFilter)
+ {
+ if (intentFilter != null)
+ {
+ children.remove(intentFilter);
+ }
+ }
+
+ /**
+ * Adds a Metadata Node to the Service Node
+ *
+ * @param metadata The Metadata Node
+ */
+ public void addMetadataNode(MetadataNode metadata)
+ {
+ if (metadata != null)
+ {
+ if (!children.contains(metadata))
+ {
+ children.add(metadata);
+ }
+ }
+ }
+
+ /**
+ * Retrieves all Metadata Nodes from the Service Node
+ *
+ * @return all Metadata Nodes from the Service Node
+ */
+ public List<MetadataNode> getMetadataNodes()
+ {
+ List<MetadataNode> metadatas = new LinkedList<MetadataNode>();
+
+ for (AndroidManifestNode node : getAllChildrenFromType(NodeType.MetaData))
+ {
+ metadatas.add((MetadataNode) node);
+ }
+
+ return metadatas;
+ }
+
+ /**
+ * Removes a Metadata Node from the Service Node
+ *
+ * @param metadata the Metadata Node to be removed
+ */
+ public void removeMetadataNode(MetadataNode metadata)
+ {
+ if (metadata != null)
+ {
+ children.remove(metadata);
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.model.manifest.dom.AndroidManifestNode#getSpecificNodeErrors()
+ */
+ @Override
+ protected List<IStatus> getSpecificNodeProblems()
+ {
+ return null;
+ }
+}
diff --git a/src/plugins/common/src/com/motorola/studio/android/model/manifest/dom/UnknownNode.java b/src/plugins/common/src/com/motorola/studio/android/model/manifest/dom/UnknownNode.java
new file mode 100644
index 0000000..e729b48
--- /dev/null
+++ b/src/plugins/common/src/com/motorola/studio/android/model/manifest/dom/UnknownNode.java
@@ -0,0 +1,123 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.model.manifest.dom;
+
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.IStatus;
+
+/**
+ * Class that represents a unknown nodes found on AndroidManifest.xml file
+ */
+public class UnknownNode extends AndroidManifestNode
+{
+ /**
+ * The node name
+ */
+ private String nodeName = null;
+
+ /**
+ * Default constructor
+ *
+ * @param name The node name. It must not be null.
+ */
+ public UnknownNode(String name)
+ {
+ Assert.isLegal(name != null);
+ this.nodeName = name;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.model.manifest.dom.AndroidManifestNode#canContains(com.motorola.studio.android.model.manifest.dom.AndroidManifestNode.NodeType)
+ */
+ @Override
+ protected boolean canContains(NodeType nodeType)
+ {
+ // If we don't know the node type, it is invalid and we
+ // should keep all information
+ return true;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.model.manifest.dom.AndroidManifestNode#getNodeType()
+ */
+ @Override
+ public NodeType getNodeType()
+ {
+ return NodeType.Unknown;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.model.manifest.dom.AndroidManifestNode#getNodeProperties()
+ */
+ @Override
+ public Map<String, String> getNodeProperties()
+ {
+ // An unknown node does not have formal properties
+ return null;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.model.manifest.dom.AndroidManifestNode#isNodeValid()
+ */
+ @Override
+ protected boolean isNodeValid()
+ {
+ // An unknown node is always valid
+ return true;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.model.manifest.dom.AndroidManifestNode#canAddUnknownProperty(java.lang.String)
+ */
+ @Override
+ public boolean canAddUnknownProperty(String property)
+ {
+ // It always possible to add unknown properties
+ return true;
+ }
+
+ /**
+ * Sets the node name
+ *
+ * @param name The node name
+ */
+ public void setNodeName(String name)
+ {
+ Assert.isLegal(name != null);
+ this.nodeName = name;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.model.manifest.dom.AndroidManifestNode#getNodeName()
+ */
+ @Override
+ public String getNodeName()
+ {
+ return nodeName;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.model.manifest.dom.AndroidManifestNode#getSpecificNodeErrors()
+ */
+ @Override
+ protected List<IStatus> getSpecificNodeProblems()
+ {
+ return null;
+ }
+}
diff --git a/src/plugins/common/src/com/motorola/studio/android/model/manifest/dom/UsesFeatureNode.java b/src/plugins/common/src/com/motorola/studio/android/model/manifest/dom/UsesFeatureNode.java
new file mode 100644
index 0000000..8eb5a5f
--- /dev/null
+++ b/src/plugins/common/src/com/motorola/studio/android/model/manifest/dom/UsesFeatureNode.java
@@ -0,0 +1,55 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.model.manifest.dom;
+
+import java.util.List;
+
+import org.eclipse.core.runtime.IStatus;
+
+/**
+ * Class that represents a <uses-permission> node on AndroidManifest.xml file
+ */
+public class UsesFeatureNode extends AbstractUsesNode
+{
+
+ /**
+ * Default constructor
+ *
+ * @param name the name property. It must not be null;
+ */
+ public UsesFeatureNode(String name)
+ {
+ super(name);
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.model.manifest.dom.AndroidManifestNode#getNodeType()
+ */
+ @Override
+ public NodeType getNodeType()
+ {
+ return NodeType.UsesFeature;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.model.manifest.dom.AndroidManifestNode#getSpecificNodeErrors()
+ */
+ @Override
+ protected List<IStatus> getSpecificNodeProblems()
+ {
+ return null;
+ }
+}
diff --git a/src/plugins/common/src/com/motorola/studio/android/model/manifest/dom/UsesLibraryNode.java b/src/plugins/common/src/com/motorola/studio/android/model/manifest/dom/UsesLibraryNode.java
new file mode 100644
index 0000000..ad5c14b
--- /dev/null
+++ b/src/plugins/common/src/com/motorola/studio/android/model/manifest/dom/UsesLibraryNode.java
@@ -0,0 +1,54 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.model.manifest.dom;
+
+import java.util.List;
+
+import org.eclipse.core.runtime.IStatus;
+
+/**
+ * Class that represents a <uses-library> node on AndroidManifest.xml file
+ */
+public class UsesLibraryNode extends AbstractUsesNode
+{
+ /**
+ * Default constructor
+ *
+ * @param name the name property. It must not be null;
+ */
+ public UsesLibraryNode(String name)
+ {
+ super(name);
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.model.manifest.dom.AndroidManifestNode#getNodeType()
+ */
+ @Override
+ public NodeType getNodeType()
+ {
+ return NodeType.UsesLibrary;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.model.manifest.dom.AndroidManifestNode#getSpecificNodeErrors()
+ */
+ @Override
+ protected List<IStatus> getSpecificNodeProblems()
+ {
+ return null;
+ }
+}
diff --git a/src/plugins/common/src/com/motorola/studio/android/model/manifest/dom/UsesPermissionNode.java b/src/plugins/common/src/com/motorola/studio/android/model/manifest/dom/UsesPermissionNode.java
new file mode 100644
index 0000000..5c41e7c
--- /dev/null
+++ b/src/plugins/common/src/com/motorola/studio/android/model/manifest/dom/UsesPermissionNode.java
@@ -0,0 +1,69 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.model.manifest.dom;
+
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.core.runtime.IStatus;
+
+/**
+ * Class that represents a <uses-permission> node on AndroidManifest.xml file
+ */
+public class UsesPermissionNode extends AbstractUsesNode
+{
+ /**
+ * Default constructor
+ *
+ * @param name the name property. It must not be null;
+ */
+ public UsesPermissionNode(String name)
+ {
+ super(name);
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.model.manifest.dom.AndroidManifestNode#getNodeType()
+ */
+ @Override
+ public NodeType getNodeType()
+ {
+ return NodeType.UsesPermission;
+ }
+
+ @Override
+ public Map<String, String> getNodeProperties()
+ {
+ properties.clear();
+
+ String propname = getName();
+ if ((propname != null) && (propname.trim().length() > 0))
+ {
+ properties.put(PROP_NAME, propname);
+ }
+
+ return properties;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.model.manifest.dom.AndroidManifestNode#getSpecificNodeErrors()
+ */
+ @Override
+ protected List<IStatus> getSpecificNodeProblems()
+ {
+ return null;
+ }
+}
diff --git a/src/plugins/common/src/com/motorola/studio/android/model/manifest/dom/UsesSDKNode.java b/src/plugins/common/src/com/motorola/studio/android/model/manifest/dom/UsesSDKNode.java
new file mode 100644
index 0000000..ef84168
--- /dev/null
+++ b/src/plugins/common/src/com/motorola/studio/android/model/manifest/dom/UsesSDKNode.java
@@ -0,0 +1,164 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/package com.motorola.studio.android.model.manifest.dom;
+
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.core.runtime.IStatus;
+
+/**
+ * Class that represents a <uses-sdk> node on AndroidManifest.xml file
+ */
+public class UsesSDKNode extends AndroidManifestNode implements IAndroidManifestProperties
+{
+ static
+ {
+ defaultProperties.add(PROP_MINSDKVERSION);
+ defaultProperties.add(PROP_MAXSDKVERSION);
+ defaultProperties.add(PROP_TARGETSDKVERSION);
+ }
+
+ /**
+ * The minSdkVersion property
+ */
+ private String propMinSdkVersion = null;
+
+ /**
+ * The maxSdkVersion property
+ */
+ private String propMaxSdkVersion = null;
+
+ /**
+ * The targetSdkVersion property
+ */
+ private String propTargetSdkVersion = null;
+
+ protected static final String INVALID_VERSION_VALUE = "";
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.model.manifest.dom.AndroidManifestNode#canContains(com.motorola.studio.android.model.manifest.dom.AndroidManifestNode.NodeType)
+ */
+ @Override
+ protected boolean canContains(NodeType nodeType)
+ {
+ // Always returns false. This node can not contain children.
+ return false;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.model.manifest.dom.AndroidManifestNode#getNodeProperties()
+ */
+ @Override
+ public Map<String, String> getNodeProperties()
+ {
+ properties.clear();
+ if (propMinSdkVersion != null)
+ {
+ properties.put(PROP_MINSDKVERSION, propMinSdkVersion);
+ }
+ if (propMaxSdkVersion != null)
+ {
+ properties.put(PROP_MAXSDKVERSION, propMaxSdkVersion);
+ }
+ if (propTargetSdkVersion != null)
+ {
+ properties.put(PROP_TARGETSDKVERSION, propTargetSdkVersion);
+ }
+ return properties;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.model.manifest.dom.AndroidManifestNode#getNodeType()
+ */
+ @Override
+ public NodeType getNodeType()
+ {
+ return NodeType.UsesSdk;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.model.manifest.dom.AndroidManifestNode#isNodeValid()
+ */
+ @Override
+ protected boolean isNodeValid()
+ {
+ return (propMinSdkVersion != null) && (!INVALID_VERSION_VALUE.equals(propMinSdkVersion));
+ }
+
+ /**
+ * Gets the minSdkVersion property value
+ *
+ * @return the minSdkVersion property value
+ */
+ public String getMinSdkVersion()
+ {
+ return propMinSdkVersion != null ? propMinSdkVersion : INVALID_VERSION_VALUE;
+ }
+
+ /**
+ * Sets the minSdkVersion property value. Set it to null to remove it.
+ *
+ * @param minSdkVersion the minSdkVersion property value
+ */
+ public void setMinSdkVersion(String minSdkVersion)
+ {
+ this.propMinSdkVersion = minSdkVersion;
+ }
+
+ /**
+ * Sets the maxSdkVersion property value. Set it to null to remove it.
+ *
+ * @param maxSdkVersion the maxSdkVersion property value
+ */
+ public void setPropMaxSdkVersion(String propMaxSdkVersion)
+ {
+ this.propMaxSdkVersion = propMaxSdkVersion;
+ }
+
+ /**
+ * Sets the targetSdkVersion property value. Set it to null to remove it.
+ *
+ * @param targetSdkVersion the targetSdkVersion property value
+ */
+ public void setPropTargetSdkVersion(String propTargetSdkVersion)
+ {
+ this.propTargetSdkVersion = propTargetSdkVersion;
+ }
+
+ public String getPropMinSdkVersion()
+ {
+ return propMinSdkVersion;
+ }
+
+ public String getPropMaxSdkVersion()
+ {
+ return propMaxSdkVersion;
+ }
+
+ public String getPropTargetSdkVersion()
+ {
+ return propTargetSdkVersion;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.model.manifest.dom.AndroidManifestNode#getSpecificNodeErrors()
+ */
+ @Override
+ protected List<IStatus> getSpecificNodeProblems()
+ {
+ return null;
+ }
+}
diff --git a/src/plugins/common/src/com/motorola/studio/android/model/manifest/parser/AndroidManifestNodeParser.java b/src/plugins/common/src/com/motorola/studio/android/model/manifest/parser/AndroidManifestNodeParser.java
new file mode 100644
index 0000000..0cd1e25
--- /dev/null
+++ b/src/plugins/common/src/com/motorola/studio/android/model/manifest/parser/AndroidManifestNodeParser.java
@@ -0,0 +1,1099 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.model.manifest.parser;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import org.eclipse.osgi.util.NLS;
+import org.w3c.dom.Comment;
+import org.w3c.dom.NamedNodeMap;
+
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.common.utilities.i18n.UtilitiesNLS;
+import com.motorola.studio.android.model.manifest.dom.AbstractBuildingBlockNode;
+import com.motorola.studio.android.model.manifest.dom.AbstractIconLabelNameNode;
+import com.motorola.studio.android.model.manifest.dom.AbstractSimpleNameNode;
+import com.motorola.studio.android.model.manifest.dom.ActionNode;
+import com.motorola.studio.android.model.manifest.dom.ActivityAliasNode;
+import com.motorola.studio.android.model.manifest.dom.ActivityNode;
+import com.motorola.studio.android.model.manifest.dom.ApplicationNode;
+import com.motorola.studio.android.model.manifest.dom.CategoryNode;
+import com.motorola.studio.android.model.manifest.dom.CommentNode;
+import com.motorola.studio.android.model.manifest.dom.DataNode;
+import com.motorola.studio.android.model.manifest.dom.GrantUriPermissionNode;
+import com.motorola.studio.android.model.manifest.dom.IAndroidManifestProperties;
+import com.motorola.studio.android.model.manifest.dom.InstrumentationNode;
+import com.motorola.studio.android.model.manifest.dom.IntentFilterNode;
+import com.motorola.studio.android.model.manifest.dom.ManifestNode;
+import com.motorola.studio.android.model.manifest.dom.MetadataNode;
+import com.motorola.studio.android.model.manifest.dom.PermissionGroupNode;
+import com.motorola.studio.android.model.manifest.dom.PermissionNode;
+import com.motorola.studio.android.model.manifest.dom.PermissionTreeNode;
+import com.motorola.studio.android.model.manifest.dom.ProviderNode;
+import com.motorola.studio.android.model.manifest.dom.ReceiverNode;
+import com.motorola.studio.android.model.manifest.dom.ServiceNode;
+import com.motorola.studio.android.model.manifest.dom.UnknownNode;
+import com.motorola.studio.android.model.manifest.dom.UsesFeatureNode;
+import com.motorola.studio.android.model.manifest.dom.UsesLibraryNode;
+import com.motorola.studio.android.model.manifest.dom.UsesPermissionNode;
+import com.motorola.studio.android.model.manifest.dom.UsesSDKNode;
+
+/**
+ * Abstract class that contains methods to parse the AndroidManifest.xml file nodes
+ */
+abstract class AndroidManifestNodeParser implements IAndroidManifestProperties
+{
+ /**
+ * Errors when parsing
+ */
+ protected final List<String> parseErrors;
+
+ /**
+ * Default constructor
+ */
+ public AndroidManifestNodeParser()
+ {
+ parseErrors = new LinkedList<String>();
+ }
+
+ /**
+ * Parses a <manifest> node
+ *
+ * @param attributes The node attributes
+ * @return a ManifestNode object corresponding to the node
+ */
+ protected ManifestNode parseManifestNode(NamedNodeMap attributes)
+ {
+ ManifestNode manifestNode = new ManifestNode("");
+ String attrName, attrValue;
+
+ for (int i = 0; i < attributes.getLength(); i++)
+ {
+ attrName = attributes.item(i).getNodeName().trim();
+ attrValue = attributes.item(i).getNodeValue();
+
+ if (attrName.equalsIgnoreCase(PROP_PACKAGE))
+ {
+ manifestNode.setPackage(attrValue);
+ }
+ else if (attrName.equalsIgnoreCase(PROP_SHAREDUSERID))
+ {
+ manifestNode.setSharedUserId(attrValue);
+ }
+ else if (attrName.equalsIgnoreCase(PROP_VERSIONCODE))
+ {
+ Integer versionCode;
+
+ try
+ {
+ versionCode = Integer.parseInt(attrValue);
+ }
+ catch (NumberFormatException nfe)
+ {
+ versionCode = 1;
+ String errMsg =
+ NLS.bind(
+ UtilitiesNLS.ERR_AndroidManifestNodeParser_ErrorParsingVersionCode,
+ attrValue, versionCode.toString());
+ parseErrors.add(errMsg);
+ StudioLogger.error(AndroidManifestNodeParser.class, errMsg, nfe);
+ }
+
+ manifestNode.setVersionCode(versionCode);
+ }
+ else if (attrName.equalsIgnoreCase(PROP_VERSIONNAME))
+ {
+ manifestNode.setVersionName(attrValue);
+ }
+ else if (!attrName.equalsIgnoreCase(PROP_XMLNS))
+ {
+ manifestNode.addUnknownProperty(attrName, attrValue);
+ }
+ }
+
+ return manifestNode;
+ }
+
+ /**
+ * Parses an <uses-permission> node
+ *
+ * @param attributes The node attributes
+ * @return a UsesPermissionNode object corresponding to the node
+ */
+ protected UsesPermissionNode parseUsesPermissionNode(NamedNodeMap attributes)
+ {
+ UsesPermissionNode usesPermissionNode = new UsesPermissionNode("");
+ String attrName, attrValue;
+
+ for (int i = 0; i < attributes.getLength(); i++)
+ {
+ attrName = attributes.item(i).getNodeName().trim();
+ attrValue = attributes.item(i).getNodeValue();
+
+ if (attrName.equalsIgnoreCase(PROP_NAME))
+ {
+ usesPermissionNode.setName(attrValue);
+ }
+ else
+ {
+ usesPermissionNode.addUnknownProperty(attrName, attrValue);
+ }
+ }
+
+ return usesPermissionNode;
+ }
+
+ /**
+ * Parses a <permission> node
+ *
+ * @param attributes The node attributes
+ * @return a PermissionNode object corresponding to the node
+ */
+ protected PermissionNode parsePermissionNode(NamedNodeMap attributes)
+ {
+ PermissionNode permissionNode = new PermissionNode("");
+ String attrName, attrValue;
+
+ parseAbstractIconLabelNameNode(attributes, permissionNode);
+
+ for (int i = 0; i < attributes.getLength(); i++)
+ {
+ attrName = attributes.item(i).getNodeName().trim();
+ attrValue = attributes.item(i).getNodeValue();
+
+ if (attrName.equalsIgnoreCase(PROP_DESCRIPTION))
+ {
+ permissionNode.setDescription(attrValue);
+ }
+ else if (attrName.equalsIgnoreCase(PROP_PERMISSIONGROUP))
+ {
+ permissionNode.setPermissionGroup(attrValue);
+ }
+ else if (attrName.equalsIgnoreCase(PROP_PROTECTIONLEVEL))
+ {
+ permissionNode.setProtectionLevel(PermissionNode.getProtectionLevel(attrValue));
+ }
+ else
+ {
+ permissionNode.addUnknownProperty(attrName, attrValue);
+ }
+ }
+
+ return permissionNode;
+ }
+
+ /**
+ * Parses a <permission-tree> node
+ *
+ * @param attributes The node attributes
+ * @return a PermissionTreeNode object corresponding to the node
+ */
+ protected PermissionTreeNode parsePermissionTreeNode(NamedNodeMap attributes)
+ {
+ PermissionTreeNode permissionTreeNode = new PermissionTreeNode("");
+ String attrName, attrValue;
+
+ parseAbstractIconLabelNameNode(attributes, permissionTreeNode);
+
+ for (int i = 0; i < attributes.getLength(); i++)
+ {
+ attrName = attributes.item(i).getNodeName().trim();
+ attrValue = attributes.item(i).getNodeValue();
+ permissionTreeNode.addUnknownProperty(attrName, attrValue);
+ }
+
+ return permissionTreeNode;
+ }
+
+ /**
+ * Parses a <permission-group> node
+ *
+ * @param attributes The node attributes
+ * @return a PermissionGroupNode object corresponding to the node
+ */
+ protected PermissionGroupNode parsePermissionGroupNode(NamedNodeMap attributes)
+ {
+ PermissionGroupNode permissionGroupNode = new PermissionGroupNode("");
+ String attrName, attrValue;
+
+ parseAbstractIconLabelNameNode(attributes, permissionGroupNode);
+
+ for (int i = 0; i < attributes.getLength(); i++)
+ {
+ attrName = attributes.item(i).getNodeName().trim();
+ attrValue = attributes.item(i).getNodeValue();
+
+ if (attrName.equalsIgnoreCase(PROP_DESCRIPTION))
+ {
+ permissionGroupNode.setDescription(attrValue);
+ }
+ else
+ {
+ permissionGroupNode.addUnknownProperty(attrName, attrValue);
+ }
+ }
+
+ return permissionGroupNode;
+ }
+
+ /**
+ * Parses an <instrumentation> node
+ *
+ * @param attributes The node attributes
+ * @return an InstrumentationNode object corresponding to the node
+ */
+ protected InstrumentationNode parseInstrumentationNode(NamedNodeMap attributes)
+ {
+ InstrumentationNode instrumentationNode = new InstrumentationNode("");
+ String attrName, attrValue;
+
+ parseAbstractIconLabelNameNode(attributes, instrumentationNode);
+
+ for (int i = 0; i < attributes.getLength(); i++)
+ {
+ attrName = attributes.item(i).getNodeName().trim();
+ attrValue = attributes.item(i).getNodeValue();
+
+ if (attrName.equalsIgnoreCase(PROP_FUNCTIONALTEST))
+ {
+ Boolean functionalTest = Boolean.parseBoolean(attrValue);
+ instrumentationNode.setFunctionalTest(functionalTest);
+ }
+ else if (attrName.equalsIgnoreCase(PROP_HANDLEPROFILING))
+ {
+ Boolean handleProfiling = Boolean.parseBoolean(attrValue);
+ instrumentationNode.setHandleProfiling(handleProfiling);
+ }
+ else if (attrName.equalsIgnoreCase(PROP_TARGETPACKAGE))
+ {
+ instrumentationNode.setTargetPackage(attrValue);
+ }
+ else
+ {
+ instrumentationNode.addUnknownProperty(attrName, attrValue);
+ }
+ }
+
+ return instrumentationNode;
+ }
+
+ /**
+ * Parses an <uses-sdk> node
+ *
+ * @param attributes The node attributes
+ * @return an UsesNode object corresponding to the node
+ */
+ protected UsesSDKNode parseUsesSdkNode(NamedNodeMap attributes)
+ {
+ UsesSDKNode usesSDKNode = new UsesSDKNode();
+ String attrName, attrValue;
+ String minSdkVersion;
+
+ for (int i = 0; i < attributes.getLength(); i++)
+ {
+ attrName = attributes.item(i).getNodeName().trim();
+ attrValue = attributes.item(i).getNodeValue();
+
+ if (attrName.equalsIgnoreCase(PROP_MINSDKVERSION))
+ {
+ minSdkVersion = attrValue;
+ usesSDKNode.setMinSdkVersion(minSdkVersion);
+ }
+ else if (attrName.equalsIgnoreCase(PROP_MAXSDKVERSION))
+ {
+ usesSDKNode.setPropMaxSdkVersion(attrValue);
+ }
+ else if (attrName.equalsIgnoreCase(PROP_TARGETSDKVERSION))
+ {
+ usesSDKNode.setPropTargetSdkVersion(attrValue);
+ }
+ else
+ {
+ usesSDKNode.addUnknownProperty(attrName, attrValue);
+ }
+ }
+
+ return usesSDKNode;
+ }
+
+ /**
+ * Parses an <application> node
+ *
+ * @param attributes The node attributes
+ * @return an ApplicationNode object corresponding to the node
+ */
+ protected ApplicationNode parseApplicationNode(NamedNodeMap attributes)
+ {
+ ApplicationNode applicationNode = new ApplicationNode("");
+ String attrName, attrValue;
+
+ parseAbstractIconLabelNameNode(attributes, applicationNode);
+
+ for (int i = 0; i < attributes.getLength(); i++)
+ {
+ attrName = attributes.item(i).getNodeName().trim();
+ attrValue = attributes.item(i).getNodeValue();
+
+ if (attrName.equalsIgnoreCase(PROP_ALLOWCLEARUSERDATA))
+ {
+ Boolean allowClearUserData = Boolean.parseBoolean(attrValue);
+ applicationNode.setAllowClearUserData(allowClearUserData);
+ }
+ else if (attrName.equalsIgnoreCase(PROP_ALLOWTASKREPARENTING))
+ {
+ Boolean allowTaskReparentig = Boolean.parseBoolean(attrValue);
+ applicationNode.setAllowTaskReparenting(allowTaskReparentig);
+ }
+ else if (attrName.equalsIgnoreCase(PROP_DEBUGGABLE))
+ {
+ Boolean debbugable = Boolean.parseBoolean(attrValue);
+ applicationNode.setDebuggable(debbugable);
+ }
+ else if (attrName.equalsIgnoreCase(PROP_DESCRIPTION))
+ {
+ applicationNode.setDescription(attrValue);
+ }
+ else if (attrName.equalsIgnoreCase(PROP_ENABLED))
+ {
+ Boolean enabled = Boolean.parseBoolean(attrValue);
+ applicationNode.setEnabled(enabled);
+ }
+ else if (attrName.equalsIgnoreCase(PROP_HASCODE))
+ {
+ Boolean hasCode = Boolean.parseBoolean(attrValue);
+ applicationNode.setHasCode(hasCode);
+ }
+ else if (attrName.equalsIgnoreCase(PROP_MANAGESPACEACTIVITY))
+ {
+ applicationNode.setManageSpaceActivity(attrValue);
+ }
+ else if (attrName.equalsIgnoreCase(PROP_PERMISSION))
+ {
+ applicationNode.setPermission(attrValue);
+ }
+ else if (attrName.equalsIgnoreCase(PROP_PERSISTENT))
+ {
+ Boolean persistent = Boolean.parseBoolean(attrValue);
+ applicationNode.setPersistent(persistent);
+ }
+ else if (attrName.equalsIgnoreCase(PROP_PROCESS))
+ {
+ applicationNode.setProcess(attrValue);
+ }
+ else if (attrName.equalsIgnoreCase(PROP_TASKAFFINITY))
+ {
+ applicationNode.setTaskAffinity(attrValue);
+ }
+ else if (attrName.equalsIgnoreCase(PROP_THEME))
+ {
+ applicationNode.setTheme(attrValue);
+ }
+ else
+ {
+ applicationNode.addUnknownProperty(attrName, attrValue);
+ }
+ }
+
+ return applicationNode;
+ }
+
+ /**
+ * Parses an <activity> node
+ *
+ * @param attributes The node attributes
+ * @return an ActivityNode object corresponding to the node
+ */
+ protected ActivityNode parseActivityNode(NamedNodeMap attributes)
+ {
+ ActivityNode activityNode = new ActivityNode("");
+ String attrName, attrValue;
+
+ parseAbstractBuildingBlockNode(attributes, activityNode);
+
+ for (int i = 0; i < attributes.getLength(); i++)
+ {
+ attrName = attributes.item(i).getNodeName().trim();
+ attrValue = attributes.item(i).getNodeValue();
+
+ if (attrName.equalsIgnoreCase(PROP_ALLOWTASKREPARENTING))
+ {
+ Boolean allowTaskReparentig = Boolean.parseBoolean(attrValue);
+ activityNode.setAllowTaskReparenting(allowTaskReparentig);
+ }
+ else if (attrName.equalsIgnoreCase(PROP_ALWAYSRETAINTASKSTATE))
+ {
+ Boolean alwaysRetainTaskState = Boolean.parseBoolean(attrValue);
+ activityNode.setAlwaysRetainTaskState(alwaysRetainTaskState);
+ }
+ else if (attrName.equalsIgnoreCase(PROP_CLEARTASKONLAUNCH))
+ {
+ Boolean clearTaskOnLaunch = Boolean.parseBoolean(attrValue);
+ activityNode.setClearTaskOnLaunch(clearTaskOnLaunch);
+ }
+ else if (attrName.equalsIgnoreCase(PROP_CONFIGCHANGES))
+ {
+ String configChanges[] = attrValue.split("\\|");
+
+ for (String configChange : configChanges)
+ {
+ activityNode.addConfigChanges(ActivityNode
+ .getConfigChangeFromName(configChange));
+ }
+ }
+ else if (attrName.equalsIgnoreCase(PROP_EXCLUDEFROMRECENTS))
+ {
+ Boolean excludeFromRecents = Boolean.parseBoolean(attrValue);
+ activityNode.setExcludeFromRecents(excludeFromRecents);
+ }
+ else if (attrName.equalsIgnoreCase(PROP_FINISHONTASKLAUNCH))
+ {
+ Boolean finishOnTaskLaunch = Boolean.parseBoolean(attrValue);
+ activityNode.setFinishOnTaskLaunch(finishOnTaskLaunch);
+ }
+ else if (attrName.equalsIgnoreCase(PROP_LAUNCHMODE))
+ {
+ activityNode.setLaunchMode(ActivityNode.getLaunchModeFromName(attrValue));
+ }
+ else if (attrName.equalsIgnoreCase(PROP_MULTIPROCESS))
+ {
+ Boolean multiprocess = Boolean.parseBoolean(attrValue);
+ activityNode.setMultiprocess(multiprocess);
+ }
+ else if (attrName.equalsIgnoreCase(PROP_SCREENORIENTATION))
+ {
+ activityNode.setScreenOrientation(ActivityNode
+ .getScreenOrientationFromName(attrValue));
+ }
+ else if (attrName.equalsIgnoreCase(PROP_STATENOTNEEDED))
+ {
+ Boolean stateNotNeeded = Boolean.parseBoolean(attrValue);
+ activityNode.setStateNotNeeded(stateNotNeeded);
+ }
+ else if (attrName.equalsIgnoreCase(PROP_TASKAFFINITY))
+ {
+ activityNode.setTaskAffinity(attrValue);
+ }
+ else if (attrName.equalsIgnoreCase(PROP_THEME))
+ {
+ activityNode.setTheme(attrValue);
+ }
+ else
+ {
+ activityNode.addUnknownProperty(attrName, attrValue);
+ }
+ }
+
+ return activityNode;
+ }
+
+ /**
+ * Parses an <intent-filter> node
+ *
+ * @param attributes The node attributes
+ * @return an IntentFilterNode object corresponding to the node
+ */
+ protected IntentFilterNode parseIntentFilterNode(NamedNodeMap attributes)
+ {
+ IntentFilterNode intentFilterNode = new IntentFilterNode();
+ String attrName, attrValue;
+
+ for (int i = 0; i < attributes.getLength(); i++)
+ {
+ attrName = attributes.item(i).getNodeName().trim();
+ attrValue = attributes.item(i).getNodeValue();
+
+ if (attrName.equalsIgnoreCase(PROP_ICON))
+ {
+ intentFilterNode.setIcon(attrValue);
+ }
+ else if (attrName.equalsIgnoreCase(PROP_LABEL))
+ {
+ intentFilterNode.setLabel(attrValue);
+ }
+ else if (attrName.equalsIgnoreCase(PROP_PRIORITY))
+ {
+ try
+ {
+ Integer priority = Integer.parseInt(attrValue);
+ intentFilterNode.setPriority(priority);
+ }
+ catch (NumberFormatException nfe)
+ {
+ String errMsg =
+ NLS.bind(
+ UtilitiesNLS.ERR_AndroidManifestNodeParser_ErrorParsingPriority,
+ attrValue);
+ parseErrors.add(errMsg);
+ StudioLogger.error(AndroidManifestNodeParser.class, errMsg, nfe);
+ }
+ }
+ else
+ {
+ intentFilterNode.addUnknownProperty(attrName, attrValue);
+ }
+ }
+
+ return intentFilterNode;
+ }
+
+ /**
+ * Parses an <action> node
+ *
+ * @param attributes The node attributes
+ * @return an ActionNode object corresponding to the node
+ */
+ protected ActionNode parseActionNode(NamedNodeMap attributes)
+ {
+ ActionNode actionNode = new ActionNode("");
+ String attrName, attrValue;
+
+ parseAbstractSimpleNameNode(attributes, actionNode);
+
+ for (int i = 0; i < attributes.getLength(); i++)
+ {
+ attrName = attributes.item(i).getNodeName().trim();
+ attrValue = attributes.item(i).getNodeValue();
+
+ actionNode.addUnknownProperty(attrName, attrValue);
+ }
+
+ return actionNode;
+ }
+
+ /**
+ * Parses a <category> node
+ *
+ * @param attributes The node attributes
+ * @return a CategoryNode object corresponding to the node
+ */
+ protected CategoryNode parseCategoryNode(NamedNodeMap attributes)
+ {
+ CategoryNode categoryNode = new CategoryNode("");
+ String attrName, attrValue;
+
+ parseAbstractSimpleNameNode(attributes, categoryNode);
+
+ for (int i = 0; i < attributes.getLength(); i++)
+ {
+ attrName = attributes.item(i).getNodeName().trim();
+ attrValue = attributes.item(i).getNodeValue();
+
+ categoryNode.addUnknownProperty(attrName, attrValue);
+ }
+
+ return categoryNode;
+ }
+
+ /**
+ * Parses a <data> node
+ *
+ * @param attributes The node attributes
+ * @return a DataNode object corresponding to the node
+ */
+ protected DataNode parseDataNode(NamedNodeMap attributes)
+ {
+ DataNode dataNode = new DataNode();
+ String attrName, attrValue;
+
+ for (int i = 0; i < attributes.getLength(); i++)
+ {
+ attrName = attributes.item(i).getNodeName().trim();
+ attrValue = attributes.item(i).getNodeValue();
+
+ if (attrName.equalsIgnoreCase(PROP_HOST))
+ {
+ dataNode.setHost(attrValue);
+ }
+ else if (attrName.equalsIgnoreCase(PROP_MIMETYPE))
+ {
+ dataNode.setMimeType(attrValue);
+ }
+ else if (attrName.equalsIgnoreCase(PROP_PATH))
+ {
+ dataNode.setPath(attrValue);
+ }
+ else if (attrName.equalsIgnoreCase(PROP_PATHPATTERN))
+ {
+ dataNode.setPathPattern(attrValue);
+ }
+ else if (attrName.equalsIgnoreCase(PROP_PATHPREFIX))
+ {
+ dataNode.setPathPrefix(attrValue);
+ }
+ else if (attrName.equalsIgnoreCase(PROP_PORT))
+ {
+ dataNode.setPort(attrValue);
+ }
+ else if (attrName.equalsIgnoreCase(PROP_SCHEME))
+ {
+ dataNode.setScheme(attrValue);
+ }
+ else
+ {
+ dataNode.addUnknownProperty(attrName, attrValue);
+ }
+ }
+
+ return dataNode;
+ }
+
+ /**
+ * Parses a <metadata> node
+ *
+ * @param attributes The node attributes
+ * @return a MetadataNode object corresponding to the node
+ */
+ protected MetadataNode parseMetadataNode(NamedNodeMap attributes)
+ {
+ MetadataNode metadataNode = new MetadataNode("");
+ String attrName, attrValue;
+
+ for (int i = 0; i < attributes.getLength(); i++)
+ {
+ attrName = attributes.item(i).getNodeName().trim();
+ attrValue = attributes.item(i).getNodeValue();
+
+ if (attrName.equalsIgnoreCase(PROP_NAME))
+ {
+ metadataNode.setName(attrValue);
+ }
+ else if (attrName.equalsIgnoreCase(PROP_RESOURCE))
+ {
+ metadataNode.setResource(attrValue);
+ }
+ else if (attrName.equals(PROP_VALUE))
+ {
+ metadataNode.setValue(attrValue);
+ }
+ else
+ {
+ metadataNode.addUnknownProperty(attrName, attrValue);
+ }
+ }
+
+ return metadataNode;
+ }
+
+ /**
+ * Parses an <activity-alias> node
+ *
+ * @param attributes The node attributes
+ * @return an ActivityNode object corresponding to the node
+ */
+ protected ActivityAliasNode parseActivityAliasNode(NamedNodeMap attributes)
+ {
+ ActivityAliasNode activityAliasNode = new ActivityAliasNode("", "");
+ String attrName, attrValue;
+
+ parseAbstractIconLabelNameNode(attributes, activityAliasNode);
+
+ for (int i = 0; i < attributes.getLength(); i++)
+ {
+ attrName = attributes.item(i).getNodeName().trim();
+ attrValue = attributes.item(i).getNodeValue();
+
+ if (attrName.equalsIgnoreCase(PROP_ENABLED))
+ {
+ Boolean enabled = Boolean.parseBoolean(attrValue);
+ activityAliasNode.setEnabled(enabled);
+ }
+ else if (attrName.equalsIgnoreCase(PROP_EXPORTED))
+ {
+ Boolean exported = Boolean.parseBoolean(attrValue);
+ activityAliasNode.setExported(exported);
+ }
+ else if (attrName.equalsIgnoreCase(PROP_PERMISSION))
+ {
+ activityAliasNode.setPermission(attrValue);
+ }
+ else if (attrName.equalsIgnoreCase(PROP_TARGETACTIVITY))
+ {
+ activityAliasNode.setTargetActivity(attrValue);
+ }
+ else
+ {
+ activityAliasNode.addUnknownProperty(attrName, attrValue);
+ }
+ }
+
+ return activityAliasNode;
+ }
+
+ /**
+ * Parses a <service> node
+ *
+ * @param attributes The node attributes
+ * @return a ServiceNode object corresponding to the node
+ */
+ protected ServiceNode parseServiceNode(NamedNodeMap attributes)
+ {
+ ServiceNode serviceNode = new ServiceNode("");
+ String attrName, attrValue;
+
+ parseAbstractBuildingBlockNode(attributes, serviceNode);
+
+ for (int i = 0; i < attributes.getLength(); i++)
+ {
+ attrName = attributes.item(i).getNodeName().trim();
+ attrValue = attributes.item(i).getNodeValue();
+
+ serviceNode.addUnknownProperty(attrName, attrValue);
+ }
+
+ return serviceNode;
+ }
+
+ /**
+ * Parses a <receiver> node
+ *
+ * @param attributes The node attributes
+ * @return a ReceiverNode object corresponding to the node
+ */
+ protected ReceiverNode parseReceiverNode(NamedNodeMap attributes)
+ {
+ ReceiverNode receiverNode = new ReceiverNode("");
+ String attrName, attrValue;
+
+ parseAbstractBuildingBlockNode(attributes, receiverNode);
+
+ for (int i = 0; i < attributes.getLength(); i++)
+ {
+ attrName = attributes.item(i).getNodeName().trim();
+ attrValue = attributes.item(i).getNodeValue();
+
+ receiverNode.addUnknownProperty(attrName, attrValue);
+ }
+
+ return receiverNode;
+ }
+
+ /**
+ * Parses a <provider> node
+ *
+ * @param attributes The node attributes
+ * @return a ProviderNode object corresponding to the node
+ */
+ protected ProviderNode parseProviderNode(NamedNodeMap attributes)
+ {
+ ProviderNode providerNode = new ProviderNode("", "");
+ String attrName, attrValue;
+
+ parseAbstractBuildingBlockNode(attributes, providerNode);
+
+ for (int i = 0; i < attributes.getLength(); i++)
+ {
+ attrName = attributes.item(i).getNodeName().trim();
+ attrValue = attributes.item(i).getNodeValue();
+
+ if (attrName.equalsIgnoreCase(PROP_AUTHORITIES))
+ {
+ String[] authorities = attrValue.split(";");
+
+ for (String authority : authorities)
+ {
+ providerNode.addAuthority(authority);
+ }
+
+ providerNode.removeAuthority("");
+ }
+ else if (attrName.equalsIgnoreCase(PROP_GRANTURIPERMISSIONS))
+ {
+ Boolean grantUriPermissions = Boolean.parseBoolean(attrValue);
+ providerNode.setGrantUriPermissions(grantUriPermissions);
+ }
+ else if (attrName.equalsIgnoreCase(PROP_INITORDER))
+ {
+ try
+ {
+ Integer initOrder = Integer.parseInt(attrValue);
+ providerNode.setInitOrder(initOrder);
+ }
+ catch (NumberFormatException nfe)
+ {
+ String errMsg =
+ NLS.bind(
+ UtilitiesNLS.ERR_AndroidManifestNodeParser_ErrorParsingInitOrder,
+ attrValue);
+ parseErrors.add(errMsg);
+ StudioLogger.error(AndroidManifestNodeParser.class, errMsg, nfe);
+ }
+ }
+ else if (attrName.equalsIgnoreCase(PROP_MULTIPROCESS))
+ {
+ Boolean multiprocess = Boolean.parseBoolean(attrValue);
+ providerNode.setMultiprocess(multiprocess);
+ }
+ else if (attrName.equalsIgnoreCase(PROP_READPERMISSION))
+ {
+ providerNode.setReadPermission(attrValue);
+ }
+ else if (attrName.equalsIgnoreCase(PROP_SYNCABLE))
+ {
+ Boolean syncable = Boolean.parseBoolean(attrValue);
+ providerNode.setSyncable(syncable);
+ }
+ else if (attrName.equalsIgnoreCase(PROP_WRITEPERMISSION))
+ {
+ providerNode.setWritePermission(attrValue);
+ }
+ else
+ {
+ providerNode.addUnknownProperty(attrName, attrValue);
+ }
+ }
+
+ return providerNode;
+ }
+
+ /**
+ * Parses a <grant-uri-permission> node
+ *
+ * @param attributes The node attributes
+ * @return a GrantUriPermissionNode object corresponding to the node
+ */
+ protected GrantUriPermissionNode parseGrantUriPermissionNode(NamedNodeMap attributes)
+ {
+ GrantUriPermissionNode grantUriPermissionNode = new GrantUriPermissionNode();
+ String attrName, attrValue;
+
+ for (int i = 0; i < attributes.getLength(); i++)
+ {
+ attrName = attributes.item(i).getNodeName().trim();
+ attrValue = attributes.item(i).getNodeValue();
+
+ if (attrName.equalsIgnoreCase(PROP_PATH))
+ {
+ grantUriPermissionNode.setPath(attrValue);
+ }
+ else if (attrName.equalsIgnoreCase(PROP_PATHPATTERN))
+ {
+ grantUriPermissionNode.setPathPattern(attrValue);
+ }
+ else if (attrName.equalsIgnoreCase(PROP_PATHPREFIX))
+ {
+ grantUriPermissionNode.setPathPrefix(attrValue);
+ }
+ else
+ {
+ grantUriPermissionNode.addUnknownProperty(attrName, attrValue);
+ }
+ }
+
+ return grantUriPermissionNode;
+ }
+
+ /**
+ * Parses an <uses-library> node
+ *
+ * @param attributes The node attributes
+ * @return an UsesLibraryNode object corresponding to the node
+ */
+ protected UsesLibraryNode parseUsesLibraryNode(NamedNodeMap attributes)
+ {
+ UsesLibraryNode usesLibraryNode = new UsesLibraryNode("");
+ String attrName, attrValue;
+
+ for (int i = 0; i < attributes.getLength(); i++)
+ {
+ attrName = attributes.item(i).getNodeName().trim();
+ attrValue = attributes.item(i).getNodeValue();
+
+ if (attrName.equalsIgnoreCase(PROP_NAME))
+ {
+ usesLibraryNode.setName(attrValue);
+ }
+ else if (attrName.equalsIgnoreCase(PROP_REQUIRED))
+ {
+ usesLibraryNode.setRequired(Boolean.parseBoolean(attrValue));
+ }
+ else
+ {
+ usesLibraryNode.addUnknownProperty(attrName, attrValue);
+ }
+ }
+
+ return usesLibraryNode;
+ }
+
+ /**
+ * Parses an <uses-feature> node
+ *
+ * @param attributes The node attributes
+ * @return an UsesFeatureNode object corresponding to the node
+ */
+ protected UsesFeatureNode parseUsesFeatureNode(NamedNodeMap attributes)
+ {
+ UsesFeatureNode usesFeatureNode = new UsesFeatureNode("");
+ String attrName, attrValue;
+
+ for (int i = 0; i < attributes.getLength(); i++)
+ {
+ attrName = attributes.item(i).getNodeName().trim();
+ attrValue = attributes.item(i).getNodeValue();
+
+ if (attrName.equalsIgnoreCase(PROP_NAME))
+ {
+ usesFeatureNode.setName(attrValue);
+ }
+ else if (attrName.equalsIgnoreCase(PROP_REQUIRED))
+ {
+ usesFeatureNode.setRequired(Boolean.parseBoolean(attrValue));
+ }
+ else
+ {
+ usesFeatureNode.addUnknownProperty(attrName, attrValue);
+ }
+ }
+
+ return usesFeatureNode;
+ }
+
+ /**
+ * Parses a comment node (<!-- This is a comment -->)
+ *
+ * @param comment The xml comment node
+ *
+ * @return a CommentNode object representing the xml comment node
+ */
+ protected CommentNode parseCommentNode(Comment comment)
+ {
+ CommentNode commentNode = new CommentNode();
+ commentNode.setComment(comment.getTextContent());
+
+ return commentNode;
+ }
+
+ /**
+ * Creates an unknown node based on its attributes. An node is classified as
+ * unknown when it is not specified by the AndroidManifest.xml file specification
+ * or when it is under another node that could not contain it.
+ *
+ * @param attributes The node attributes
+ * @return a UnknownNode object corresponding to the node
+ */
+ protected UnknownNode parseUnknownNode(String nodeName, NamedNodeMap attributes)
+ {
+ UnknownNode unknownNode = new UnknownNode(nodeName);
+ String attrName, attrValue;
+
+ for (int i = 0; i < attributes.getLength(); i++)
+ {
+ attrName = attributes.item(i).getNodeName().trim();
+ attrValue = attributes.item(i).getNodeValue();
+
+ unknownNode.addUnknownProperty(attrName, attrValue);
+ }
+
+ return unknownNode;
+ }
+
+ /**
+ * Sets all attributes relative to an AbstractIconLabelNameNode from an
+ * attributes list
+ *
+ * @param attributes The node attributes
+ * @param amNode The AbstractIconLabelNameNode where the attributes must be in
+ */
+ private void parseAbstractIconLabelNameNode(NamedNodeMap attributes,
+ AbstractIconLabelNameNode amNode)
+ {
+ String attrName, attrValue;
+
+ for (int i = 0; i < attributes.getLength(); i++)
+ {
+ attrName = attributes.item(i).getNodeName().trim();
+ attrValue = attributes.item(i).getNodeValue();
+
+ if (attrName.equalsIgnoreCase(PROP_ICON))
+ {
+ amNode.setIcon(attrValue);
+ }
+ else if (attrName.equalsIgnoreCase(PROP_LABEL))
+ {
+ amNode.setLabel(attrValue);
+ }
+ else if (attrName.equalsIgnoreCase(PROP_NAME))
+ {
+ amNode.setName(attrValue);
+ }
+ }
+ }
+
+ /**
+ * Sets all attributes relative to an AbstractBuildingBlockNode from an
+ * attributes list
+ *
+ * @param attributes The node attributes
+ * @param amNode The AbstractBuildingBlockNode where the attributes must be in
+ */
+ private void parseAbstractBuildingBlockNode(NamedNodeMap attributes,
+ AbstractBuildingBlockNode amNode)
+ {
+ String attrName, attrValue;
+ Boolean boolValue;
+
+ parseAbstractIconLabelNameNode(attributes, amNode);
+
+ for (int i = 0; i < attributes.getLength(); i++)
+ {
+ attrName = attributes.item(i).getNodeName().trim();
+ attrValue = attributes.item(i).getNodeValue();
+
+ if (attrName.equalsIgnoreCase(PROP_ENABLED))
+ {
+ boolValue = Boolean.parseBoolean(attrValue);
+ amNode.setEnabled(boolValue);
+ }
+ else if (attrName.equalsIgnoreCase(PROP_EXPORTED))
+ {
+ boolValue = Boolean.parseBoolean(attrValue);
+ amNode.setExported(boolValue);
+ }
+ else if (attrName.equalsIgnoreCase(PROP_PERMISSION))
+ {
+ amNode.setPermission(attrValue);
+ }
+ else if (attrName.equalsIgnoreCase(PROP_PROCESS))
+ {
+ amNode.setProcess(attrValue);
+ }
+ }
+ }
+
+ /**
+ * Sets all attributes relative to an AbstractSimpleNameNode from an
+ * attributes list
+ *
+ * @param attributes The node attributes
+ * @param amNode The AbstractSimpleNameNode where the attributes must be in
+ */
+ private void parseAbstractSimpleNameNode(NamedNodeMap attributes, AbstractSimpleNameNode amNode)
+ {
+ String attrName, attrValue;
+
+ for (int i = 0; i < attributes.getLength(); i++)
+ {
+ attrName = attributes.item(i).getNodeName().trim();
+ attrValue = attributes.item(i).getNodeValue();
+
+ if (attrName.equalsIgnoreCase(PROP_NAME))
+ {
+ amNode.setName(attrValue);
+ }
+ }
+ }
+}
diff --git a/src/plugins/common/src/com/motorola/studio/android/model/manifest/parser/AndroidManifestParser.java b/src/plugins/common/src/com/motorola/studio/android/model/manifest/parser/AndroidManifestParser.java
new file mode 100644
index 0000000..d987e6f
--- /dev/null
+++ b/src/plugins/common/src/com/motorola/studio/android/model/manifest/parser/AndroidManifestParser.java
@@ -0,0 +1,243 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.model.manifest.parser;
+
+import java.io.IOException;
+import java.io.StringReader;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.apache.xerces.parsers.DOMParser;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.osgi.util.NLS;
+import org.w3c.dom.Comment;
+import org.w3c.dom.Element;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+
+import com.motorola.studio.android.common.exception.AndroidException;
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.common.utilities.i18n.UtilitiesNLS;
+import com.motorola.studio.android.model.manifest.dom.AndroidManifestNode;
+import com.motorola.studio.android.model.manifest.dom.AndroidManifestNode.NodeType;
+
+/**
+ * Abstract class used to parse an AndroidManifest.xml file
+ */
+public abstract class AndroidManifestParser extends AndroidManifestNodeParser
+{
+ /**
+ * The nodes present on the xml file root
+ */
+ protected List<AndroidManifestNode> rootNodes = new LinkedList<AndroidManifestNode>();
+
+ /**
+ * Parses an IDocument object containing the AndroidManifest.xml into a DOM
+ *
+ * @param document the IDocument object
+ * @throws SAXException When a parsing error occurs
+ * @throws IOException When a reading error occurs
+ */
+ public void parseDocument(IDocument document) throws AndroidException
+ {
+ Node node;
+ DOMParser domParser = new DOMParser();
+
+ rootNodes.clear();
+
+ StringReader stringReader = null;
+ try
+ {
+ stringReader = new StringReader(document.get());
+ domParser.parse(new InputSource(stringReader));
+ }
+ catch (SAXException e)
+ {
+ String errMsg =
+ NLS.bind(UtilitiesNLS.EXC_AndroidManifestNodeParser_ErrorParsingTheXMLFile,
+ e.getLocalizedMessage());
+ StudioLogger.error(AndroidManifestParser.class, errMsg, e);
+ throw new AndroidException(errMsg);
+ }
+ catch (IOException e)
+ {
+ String errMsg =
+ NLS.bind(UtilitiesNLS.EXC_AndroidManifestNodeParser_ErrorReadingTheXMLContent,
+ e.getLocalizedMessage());
+ StudioLogger.error(AndroidManifestParser.class, errMsg, e);
+ throw new AndroidException(errMsg);
+ }
+ finally
+ {
+ if (stringReader != null)
+ {
+ stringReader.close();
+ }
+ }
+
+ NodeList children = domParser.getDocument().getChildNodes();
+
+ for (int i = 0; i < children.getLength(); i++)
+ {
+ node = children.item(i);
+ parseNode(node, null);
+ }
+ }
+
+ /**
+ * Parses a XML Node
+ *
+ * @param element The XML Node
+ * @param rootNode The XML Node parent (An AndroidManifestNode that has been parsed)
+ */
+ private void parseNode(Node node, AndroidManifestNode rootNode)
+ {
+ AndroidManifestNode amNode;
+ NodeType nodeType = identifyNode(node);
+ Node xmlNode;
+ NodeList xmlChildNodes;
+ NamedNodeMap attributes = node.getAttributes();
+
+ switch (nodeType)
+ {
+ case Manifest:
+ amNode = parseManifestNode(attributes);
+ break;
+ case UsesPermission:
+ amNode = parseUsesPermissionNode(attributes);
+ break;
+ case Permission:
+ amNode = parsePermissionNode(attributes);
+ break;
+ case PermissionTree:
+ amNode = parsePermissionTreeNode(attributes);
+ break;
+ case PermissionGroup:
+ amNode = parsePermissionGroupNode(attributes);
+ break;
+ case Instrumentation:
+ amNode = parseInstrumentationNode(attributes);
+ break;
+ case UsesSdk:
+ amNode = parseUsesSdkNode(attributes);
+ break;
+ case Application:
+ amNode = parseApplicationNode(attributes);
+ break;
+ case Activity:
+ amNode = parseActivityNode(attributes);
+ break;
+ case IntentFilter:
+ amNode = parseIntentFilterNode(attributes);
+ break;
+ case Action:
+ amNode = parseActionNode(attributes);
+ break;
+ case Category:
+ amNode = parseCategoryNode(attributes);
+ break;
+ case Data:
+ amNode = parseDataNode(attributes);
+ break;
+ case MetaData:
+ amNode = parseMetadataNode(attributes);
+ break;
+ case ActivityAlias:
+ amNode = parseActivityAliasNode(attributes);
+ break;
+ case Service:
+ amNode = parseServiceNode(attributes);
+ break;
+ case Receiver:
+ amNode = parseReceiverNode(attributes);
+ break;
+ case Provider:
+ amNode = parseProviderNode(attributes);
+ break;
+ case GrantUriPermission:
+ amNode = parseGrantUriPermissionNode(attributes);
+ break;
+ case UsesLibrary:
+ amNode = parseUsesLibraryNode(attributes);
+ break;
+ case UsesFeature:
+ amNode = parseUsesFeatureNode(attributes);
+ break;
+ case Comment:
+ amNode = parseCommentNode((Comment) node);
+ break;
+ default:
+ amNode = parseUnknownNode(node.getNodeName(), attributes);
+ }
+
+ xmlChildNodes = node.getChildNodes();
+
+ for (int i = 0; i < xmlChildNodes.getLength(); i++)
+ {
+ xmlNode = xmlChildNodes.item(i);
+
+ if ((xmlNode instanceof Element) || (xmlNode instanceof Comment))
+ {
+ parseNode(xmlNode, amNode);
+ }
+ }
+
+ if (rootNode == null)
+ {
+ rootNodes.add(amNode);
+ }
+ else
+ {
+ rootNode.addChild(amNode);
+ }
+ }
+
+ /**
+ * Identifies a XML Node type as an AndroidManifestNode type.
+ *
+ * @param xmlNode The XML Node
+ * @return The corresponding AndroidManifestNode type to the XML Node
+ */
+ private NodeType identifyNode(Node xmlNode)
+ {
+ NodeType identifiedType = NodeType.Unknown;
+ String nodeName = xmlNode.getNodeName();
+ String thisNodeName;
+
+ if (xmlNode instanceof Comment)
+ {
+ identifiedType = NodeType.Comment;
+ }
+ else
+ {
+ for (NodeType nodeType : NodeType.values())
+ {
+ thisNodeName = AndroidManifestNode.getNodeName(nodeType);
+
+ if (thisNodeName.equalsIgnoreCase(nodeName))
+ {
+ identifiedType = nodeType;
+ break;
+ }
+ }
+ }
+
+ return identifiedType;
+ }
+}
diff --git a/src/plugins/common/src/com/motorola/studio/android/wizards/BaseWizard.java b/src/plugins/common/src/com/motorola/studio/android/wizards/BaseWizard.java
new file mode 100644
index 0000000..a9f9146
--- /dev/null
+++ b/src/plugins/common/src/com/motorola/studio/android/wizards/BaseWizard.java
@@ -0,0 +1,63 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.wizards;
+
+import org.eclipse.jface.wizard.IWizardPage;
+import org.eclipse.jface.wizard.Wizard;
+
+/**
+ * Base class to simple wizards (without progress monitor and help).
+ */
+public abstract class BaseWizard extends Wizard
+{
+ public BaseWizard()
+ {
+ setNeedsProgressMonitor(true);
+ setHelpAvailable(false);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.jface.wizard.Wizard#performFinish()
+ */
+ @Override
+ public boolean performFinish()
+ {
+ return doPerformFinish();
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.jface.wizard.Wizard#canFinish()
+ */
+ @Override
+ public boolean canFinish()
+ {
+ IWizardPage currentPage = getContainer().getCurrentPage();
+ if (getNextPage(currentPage) == null)
+ {
+ return currentPage.isPageComplete();
+ }
+ return super.canFinish();
+ }
+
+ /**
+ * This method is executed when the user finishes the wizard.
+ *
+ * @return <code>true</code> if the method succeed.
+ */
+ protected abstract boolean doPerformFinish();
+}
diff --git a/src/plugins/common/src/com/motorola/studio/android/wizards/BaseWizardPage.java b/src/plugins/common/src/com/motorola/studio/android/wizards/BaseWizardPage.java
new file mode 100644
index 0000000..f47f863
--- /dev/null
+++ b/src/plugins/common/src/com/motorola/studio/android/wizards/BaseWizardPage.java
@@ -0,0 +1,176 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.wizards;
+
+import org.eclipse.jface.wizard.WizardPage;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.ui.PlatformUI;
+
+import com.motorola.studio.android.common.utilities.ui.WidgetsFactory;
+import com.motorola.studio.android.wizards.elements.IBaseBlock;
+
+/**
+ * Base class for the wizard pages.
+ */
+public abstract class BaseWizardPage extends WizardPage implements Listener
+{
+
+ /**
+ * Default wizard page Composite
+ */
+ private Composite composite;
+
+ /**
+ * Default wizard page GridLayout
+ */
+ private GridLayout layout;
+
+ /**
+ * The block used by this wizard page.
+ */
+ protected IBaseBlock block;
+
+ private String helpID = null;
+
+ /**
+ * Constructs a new instance using a given block.
+ *
+ * @param block Block used to render the screen.
+ * @param title Window title
+ * @param description Window description
+ */
+ public BaseWizardPage(IBaseBlock block, String title, String description)
+ {
+ this(block, title, description, null);
+ this.block = block;
+ }
+
+ /**
+ * Constructs a new instance using a given block.
+ *
+ * @param block Block used to render the screen.
+ * @param title Window title
+ * @param description Window description
+ * @param contextHelpID The context help id used to this page
+ */
+ public BaseWizardPage(IBaseBlock block, String title, String description, String contextHelpID)
+ {
+ super(title);
+ setTitle(title);
+ setDescription(description);
+ this.block = block;
+ helpID = contextHelpID;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see
+ * org.eclipse.jface.dialogs.IDialogPage#createControl(org.eclipse.swt.widgets
+ * .Composite)
+ */
+ public void createControl(Composite parent)
+ {
+ getComposite(parent).setLayout(getLayout());
+ if (this.block != null)
+ {
+ this.block.setShell(getShell());
+ Composite blockComposite = this.block.createContent(getComposite(parent));
+ if (helpID != null)
+ {
+ PlatformUI.getWorkbench().getHelpSystem().setHelp(blockComposite, helpID);
+ PlatformUI.getWorkbench().getHelpSystem().setHelp(getComposite(parent), helpID);
+ }
+ }
+ setControl(getComposite(parent));
+ setErrorMessage(null);
+ getComposite(parent).addListener(SWT.Modify, this);
+ getComposite(parent).addListener(SWT.Selection, this);
+ }
+
+ /**
+ * Returns the default composite of this wizard page.
+ *
+ * @param parent The parent composite.
+ * @return The composite of this wizard page.
+ */
+ protected Composite getComposite(Composite parent)
+ {
+ if (this.composite == null)
+ {
+ this.composite = WidgetsFactory.createComposite(parent);
+ }
+ return this.composite;
+ }
+
+ /**
+ * Returns the default grid layout of this wizard page.
+ *
+ * @return The default grid layout of this wizard page.
+ */
+ protected GridLayout getLayout()
+ {
+ if (this.layout == null)
+ {
+ this.layout = WidgetsFactory.createGridLayout();
+ }
+ return this.layout;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see
+ * org.eclipse.swt.widgets.Listener#handleEvent(org.eclipse.swt.widgets.
+ * Event)
+ */
+ public void handleEvent(Event event)
+ {
+ String message = this.block.getErrorMessage();
+ setErrorMessage(message);
+ setPageComplete(message == null);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.jface.wizard.WizardPage#isPageComplete()
+ */
+ @Override
+ public boolean isPageComplete()
+ {
+ if (this.block == null)
+ {
+ return true;
+ }
+ return this.block.isPageComplete();
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.jface.wizard.WizardPage#canFlipToNextPage()
+ */
+ @Override
+ public boolean canFlipToNextPage()
+ {
+ if (this.block == null)
+ {
+ return true;
+ }
+ return this.block.canFlipToNextPage() && (getNextPage() != null);
+ }
+}
diff --git a/src/plugins/common/src/com/motorola/studio/android/wizards/elements/AddInputRemoveButtons.java b/src/plugins/common/src/com/motorola/studio/android/wizards/elements/AddInputRemoveButtons.java
new file mode 100644
index 0000000..99d8f92
--- /dev/null
+++ b/src/plugins/common/src/com/motorola/studio/android/wizards/elements/AddInputRemoveButtons.java
@@ -0,0 +1,115 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.wizards.elements;
+
+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 com.motorola.studio.android.common.utilities.i18n.UtilitiesNLS;
+
+/**
+ * Add, Input and Remove buttons for wizards.
+ *
+ */
+public class AddInputRemoveButtons extends Composite
+{
+
+ private Button addButton;
+
+ private Button inputButton;
+
+ private Button removeButton;
+
+ /**
+ * Create Add, Input and Remove buttons for wizards.
+ * @param parent the composite parent
+ */
+ public AddInputRemoveButtons(Composite parent)
+ {
+ super(parent, SWT.NONE);
+ createContents();
+ }
+
+ /**
+ * Create Contents
+ */
+ private void createContents()
+ {
+ GridData gridData;
+ GridLayout gridLayout = new GridLayout();
+ gridLayout.marginWidth = 0;
+ gridLayout.marginHeight = 0;
+ setLayout(gridLayout);
+ gridData = new GridData();
+ gridData.verticalAlignment = GridData.BEGINNING;
+
+ setLayoutData(gridData);
+
+ createAddButton();
+
+ createInputButton();
+
+ createRemoveButton();
+ }
+
+ protected void createAddButton()
+ {
+ addButton = new Button(this, SWT.PUSH);
+ addButton.setText(UtilitiesNLS.UI_AddRemoveButtons_AddButtonLabel);
+ }
+
+ private void createInputButton()
+ {
+ inputButton = new Button(this, SWT.PUSH);
+ inputButton.setText(UtilitiesNLS.AddInputRemoveButtons_InputButtonLabel);
+ }
+
+ protected void createRemoveButton()
+ {
+ removeButton = new Button(this, SWT.PUSH);
+ removeButton.setText(UtilitiesNLS.UI_AddRemoveButtons_RemoveButtonLabel);
+ }
+
+ /**
+ * Return the Add button instance.
+ * @return the Add button instance
+ */
+ public Button getAddButton()
+ {
+ return addButton;
+ }
+
+ /**
+ * Return the Input button instance.
+ * @return the Input button instance
+ */
+ public Button getInputButton()
+ {
+ return inputButton;
+ }
+
+ /**
+ * Return the Remove button instance.
+ * @return the Remove button instance
+ */
+ public Button getRemoveButton()
+ {
+ return removeButton;
+ }
+}
diff --git a/src/plugins/common/src/com/motorola/studio/android/wizards/elements/AddRemoveButtons.java b/src/plugins/common/src/com/motorola/studio/android/wizards/elements/AddRemoveButtons.java
new file mode 100644
index 0000000..9ce9381
--- /dev/null
+++ b/src/plugins/common/src/com/motorola/studio/android/wizards/elements/AddRemoveButtons.java
@@ -0,0 +1,97 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.wizards.elements;
+
+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 com.motorola.studio.android.common.utilities.i18n.UtilitiesNLS;
+
+/**
+ * Add and Remove Buttons for Wizards
+ */
+public class AddRemoveButtons extends Composite
+{
+
+ private Button addButton;
+
+ private Button removeButton;
+
+ /**
+ * Create an Add and Remove Buttons for Wizards
+ * @param parent
+ */
+ public AddRemoveButtons(Composite parent)
+ {
+ super(parent, SWT.NONE);
+ createContents();
+ }
+
+ /**
+ * Create Contents
+ */
+ private void createContents()
+ {
+ GridData gridData;
+ GridLayout gridLayout = new GridLayout();
+ gridLayout.marginWidth = 0;
+ gridLayout.marginHeight = 0;
+ setLayout(gridLayout);
+ gridData = new GridData();
+ gridData.verticalAlignment = GridData.BEGINNING;
+
+ setLayoutData(gridData);
+ addButton = new Button(this, SWT.PUSH);
+ addButton.setText(UtilitiesNLS.UI_AddRemoveButtons_AddButtonLabel);
+
+ //Separator
+ Label separator = new Label(this, SWT.SEPARATOR | SWT.HORIZONTAL);
+ separator.setFont(getFont());
+ separator.setVisible(false);
+ GridData gd = new GridData();
+ gd.horizontalAlignment = GridData.FILL;
+ gd.verticalAlignment = GridData.BEGINNING;
+ gd.verticalIndent = 4;
+ separator.setLayoutData(gd);
+ // Separator
+
+ removeButton = new Button(this, SWT.PUSH);
+ removeButton.setText(UtilitiesNLS.UI_AddRemoveButtons_RemoveButtonLabel);
+ }
+
+ /**
+ * Return the Add button instance.
+ * @return
+ */
+ public Button getAddButton()
+ {
+ return addButton;
+ }
+
+ /**
+ * Return the Remove Button instance.
+ * @return
+ */
+ public Button getRemoveButton()
+ {
+ return removeButton;
+ }
+
+}
diff --git a/src/plugins/common/src/com/motorola/studio/android/wizards/elements/FileChooser.java b/src/plugins/common/src/com/motorola/studio/android/wizards/elements/FileChooser.java
new file mode 100644
index 0000000..566c2b0
--- /dev/null
+++ b/src/plugins/common/src/com/motorola/studio/android/wizards/elements/FileChooser.java
@@ -0,0 +1,305 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.wizards.elements;
+
+import org.eclipse.core.resources.IContainer;
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.jface.dialogs.IDialogConstants;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.jface.viewers.ViewerFilter;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.ModifyListener;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.layout.RowData;
+import org.eclipse.swt.layout.RowLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.FileDialog;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.ui.dialogs.ElementTreeSelectionDialog;
+import org.eclipse.ui.dialogs.ISelectionStatusValidator;
+import org.eclipse.ui.model.WorkbenchContentProvider;
+import org.eclipse.ui.model.WorkbenchLabelProvider;
+import org.eclipse.ui.views.navigator.ResourceComparator;
+
+import com.motorola.studio.android.common.CommonPlugin;
+import com.motorola.studio.android.common.utilities.i18n.UtilitiesNLS;
+
+/**
+ * Composite to allow users to choose between filesystem and workspace files
+ */
+public class FileChooser extends Composite
+{
+ /*
+ * Label: TextField
+ * Workspace... Filesystem...
+ *
+ */
+ private final String label;
+
+ private Label fileLabel = null;
+
+ private Text pathWidget = null;
+
+ private Button filesystem = null, workspace = null;
+
+ private String[] extensionFilter = null;
+
+ private IContainer container;
+
+ /**
+ * Constructs a new FileChooser object
+ * @param parent the parent composite
+ * @param style the style
+ * @param label A label to put before the path text or null to no label
+ */
+ public FileChooser(Composite parent, int style, String label)
+ {
+ super(parent, style);
+ this.label = label;
+ setupLayout();
+ createFields();
+ }
+
+ /**
+ * Constructs a new FileChooser object
+ *
+ * @param container Container in which limits the range of the filter. For instance
+ * if a project is passed as a container, all the search will be done within the passed-project.
+ * @param parent the parent composite
+ * @param style the style
+ * @param label A label to put before the path text or null to no label
+ */
+ public FileChooser(IContainer container, Composite parent, int style, String label)
+ {
+ // call constructor
+ this(parent, style, label);
+ // assign container
+ this.container = container;
+ }
+
+ /**
+ * Setup this composite layout
+ */
+ private void setupLayout()
+ {
+ GridLayout layout = new GridLayout(2, false);
+ layout.marginHeight = 0;
+ layout.marginWidth = 0;
+ this.setLayout(layout);
+ this.setLayoutData(new GridData(GridData.FILL_BOTH));
+ }
+
+ private void createFields()
+ {
+ GridData gridData;
+ if (label != null)
+ {
+ fileLabel = new Label(this, SWT.NONE);
+ fileLabel.setText(label);
+ gridData = new GridData(SWT.LEFT, SWT.CENTER, false, false, 1, 1);
+ fileLabel.setLayoutData(gridData);
+ }
+
+ pathWidget = new Text(this, SWT.BORDER);
+ gridData = new GridData(SWT.FILL, SWT.CENTER, true, false, label != null ? 1 : 2, 1);
+ pathWidget.setLayoutData(gridData);
+
+ Composite buttonsComposite = new Composite(this, SWT.NONE);
+ gridData = new GridData(SWT.RIGHT, SWT.CENTER, true, false, 2, 1);
+ buttonsComposite.setLayoutData(gridData);
+ buttonsComposite.setLayout(new RowLayout(SWT.HORIZONTAL));
+
+ filesystem = new Button(buttonsComposite, SWT.PUSH);
+ filesystem.setLayoutData(new RowData());
+ filesystem.setText(UtilitiesNLS.UI_FileChooser_Filesystem);
+ filesystem.addSelectionListener(new SelectionListener()
+ {
+
+ public void widgetSelected(SelectionEvent e)
+ {
+ FileDialog fileDialog = new FileDialog(getShell(), SWT.OPEN);
+ fileDialog.setFilterExtensions(extensionFilter);
+ fileDialog.setFilterPath(pathWidget.getText());
+ String returnedPath = fileDialog.open();
+ if (returnedPath != null)
+ {
+ pathWidget.setText(returnedPath);
+ }
+ }
+
+ public void widgetDefaultSelected(SelectionEvent e)
+ {
+ //do nothing
+ }
+ });
+
+ workspace = new Button(buttonsComposite, SWT.PUSH);
+ workspace.setLayoutData(new RowData());
+ workspace.setText(UtilitiesNLS.UI_FileChooser_Workspace);
+ workspace.addSelectionListener(new SelectionListener()
+ {
+
+ public void widgetSelected(SelectionEvent e)
+ {
+ ElementTreeSelectionDialog dialog =
+ new ElementTreeSelectionDialog(getShell(), new WorkbenchLabelProvider(),
+ new WorkbenchContentProvider());
+ dialog.setTitle(UtilitiesNLS.UI_FileChooser_Dialog_Title);
+ dialog.setMessage(UtilitiesNLS.UI_FileChooser_Dialog_Message);
+ // set the input depending whether the container exists
+ if (container != null)
+ {
+ // the container exists, set it as the limit
+ dialog.setInput(container);
+ }
+ else
+ {
+ // the container does not exists, set the workspace as the limit
+ dialog.setInput(ResourcesPlugin.getWorkspace().getRoot());
+ }
+ dialog.setComparator(new ResourceComparator(ResourceComparator.NAME));
+ //filter extensions
+ dialog.addFilter(new ViewerFilter()
+ {
+
+ @Override
+ public boolean select(Viewer viewer, Object parentElement, Object element)
+ {
+ boolean filtered = false;
+
+ if ((extensionFilter != null) && (element instanceof IFile))
+ {
+ IFile file = (IFile) element;
+ int i = 0;
+ while ((i < extensionFilter.length) && !filtered)
+ {
+ String fileExtensionToShow =
+ extensionFilter[i].substring(extensionFilter[i]
+ .lastIndexOf(".") + 1);
+ String fileExtension = file.getFileExtension();
+ if ((fileExtension != null)
+ && !fileExtension.equals(fileExtensionToShow)) //$NON-NLS-1$
+ {
+ filtered = true;
+ }
+ i++;
+ }
+
+ }
+ return !filtered;
+ }
+ });
+ //user can select only one FILE
+ dialog.setValidator(new ISelectionStatusValidator()
+ {
+
+ public IStatus validate(Object[] selection)
+ {
+ IStatus valid = new Status(IStatus.ERROR, CommonPlugin.PLUGIN_ID, ""); //$NON-NLS-1$
+ if (selection.length == 1)
+ {
+ if (selection[0] instanceof IFile)
+ {
+ valid = new Status(IStatus.OK, CommonPlugin.PLUGIN_ID, ""); //$NON-NLS-1$
+ }
+ }
+ return valid;
+ }
+ });
+
+ if (dialog.open() == IDialogConstants.OK_ID)
+ {
+ IResource resource = (IResource) dialog.getFirstResult();
+ pathWidget.setText(resource.getLocation().toOSString());
+ }
+ }
+
+ public void widgetDefaultSelected(SelectionEvent e)
+ {
+ //do nothing
+ }
+ });
+
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.swt.widgets.Control#setEnabled(boolean)
+ */
+ @Override
+ public void setEnabled(boolean enabled)
+ {
+ super.setEnabled(enabled);
+ if (fileLabel != null)
+ {
+ fileLabel.setEnabled(enabled);
+ }
+ workspace.setEnabled(enabled);
+ filesystem.setEnabled(enabled);
+ pathWidget.setEnabled(enabled);
+ }
+
+ /**
+ *
+ * @return the text with the path of the file selected
+ */
+ public String getText()
+ {
+ return pathWidget.getText();
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.swt.widgets.FileDialog#setFilterExtensions(String[])
+ */
+ public void setFilterExtensions(String[] filter)
+ {
+ extensionFilter = filter;
+ }
+
+ public void setText(String text)
+ {
+ pathWidget.setText(text);
+ }
+
+ /**
+ * Add modify text listener
+ * @param modifyListener
+ */
+ public void addModifyListener(ModifyListener modifyListener)
+ {
+ pathWidget.addModifyListener(modifyListener);
+ }
+
+ /**
+ * Sets the container.
+ * @param container
+ */
+ public void setContainer(IContainer container)
+ {
+ this.container = container;
+ }
+
+}
diff --git a/src/plugins/common/src/com/motorola/studio/android/wizards/elements/IBaseBlock.java b/src/plugins/common/src/com/motorola/studio/android/wizards/elements/IBaseBlock.java
new file mode 100644
index 0000000..7b3adb9
--- /dev/null
+++ b/src/plugins/common/src/com/motorola/studio/android/wizards/elements/IBaseBlock.java
@@ -0,0 +1,81 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.wizards.elements;
+
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Text;
+
+/**
+ * Base interface to create a screen block.
+ */
+public interface IBaseBlock extends Listener
+{
+
+ /**
+ * Create the content.
+ */
+ Composite createContent(Composite parent);
+
+ /**
+ * This method must be invoked when the user wants to refresh the screen.
+ */
+ void refresh();
+
+ /**
+ * Returns <code>true</code> if the user can finish the wizard. This is used
+ * when the block is used in wizards.
+ */
+ boolean isPageComplete();
+
+ /**
+ * Returns the error message in this block, null if there is no error.
+ */
+ String getErrorMessage();
+
+ /**
+ * Configures the shell used by this block.
+ *
+ * @param shell The shell.
+ */
+ void setShell(Shell shell);
+
+ /**
+ * Returns the shell used by this block.
+ *
+ * @return Rhe shell used by this block.
+ */
+ Shell getShell();
+
+ /**
+ * Returns <code>true</code> if the user can flip to the next page. This is
+ * used when the block is used in wizards.
+ */
+ boolean canFlipToNextPage();
+
+ /**
+ * Sets the widget that will receive focus when {@link IBaseBlock#setFocus()} is called on the first time the block is visible.
+ * */
+ void setDefaultFocus();
+
+ /**
+ * Sets the focus on the default widget with the block.
+ * This method should call {@link Control#setFocus()} on one of its control widget, for instance a {@link Text} control.
+ * */
+ void setFocus();
+}
diff --git a/src/plugins/common/src/com/motorola/studio/android/wizards/elements/InputRemoveButtons.java b/src/plugins/common/src/com/motorola/studio/android/wizards/elements/InputRemoveButtons.java
new file mode 100644
index 0000000..c8b6e67
--- /dev/null
+++ b/src/plugins/common/src/com/motorola/studio/android/wizards/elements/InputRemoveButtons.java
@@ -0,0 +1,107 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.wizards.elements;
+
+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 com.motorola.studio.android.common.utilities.i18n.UtilitiesNLS;
+
+/**
+ * Input and Remove buttons for wizards.
+ */
+public class InputRemoveButtons extends Composite
+{
+ private Button inputButton;
+
+ private Button removeButton;
+
+ /**
+ * Create Input and Remove buttons for wizards.
+ * @param parent
+ */
+ public InputRemoveButtons(Composite parent)
+ {
+ super(parent, SWT.NONE);
+ createContents();
+ }
+
+ /**
+ * Create Contents
+ */
+ protected void createContents()
+ {
+ GridData gridData;
+ GridLayout gridLayout = new GridLayout();
+ gridLayout.marginWidth = 0;
+ gridLayout.marginHeight = 0;
+ setLayout(gridLayout);
+ gridData = new GridData();
+ gridData.verticalAlignment = GridData.BEGINNING;
+
+ setLayoutData(gridData);
+
+ createInputButton();
+
+ //Separator
+ Label separator = new Label(this, SWT.SEPARATOR | SWT.HORIZONTAL);
+ separator.setFont(getFont());
+ separator.setVisible(false);
+ GridData gd = new GridData();
+ gd.horizontalAlignment = GridData.FILL;
+ gd.verticalAlignment = GridData.BEGINNING;
+ gd.verticalIndent = 4;
+ separator.setLayoutData(gd);
+ // Separator
+
+ createRemoveButton();
+ }
+
+ private void createInputButton()
+ {
+ inputButton = new Button(this, SWT.PUSH);
+ inputButton.setText(UtilitiesNLS.AddInputRemoveButtons_InputButtonLabel);
+ }
+
+ protected void createRemoveButton()
+ {
+ removeButton = new Button(this, SWT.PUSH);
+ removeButton.setText(UtilitiesNLS.UI_AddRemoveButtons_RemoveButtonLabel);
+ }
+
+ /**
+ * Return the Input button instance.
+ * @return the Input button instance
+ */
+ public Button getInputButton()
+ {
+ return inputButton;
+ }
+
+ /**
+ * Return the Remove button instance.
+ * @return the Remove button instance
+ */
+ public Button getRemoveButton()
+ {
+ return removeButton;
+ }
+
+}
diff --git a/src/plugins/common/src/com/motorola/studio/android/wizards/elements/ProjectChooser.java b/src/plugins/common/src/com/motorola/studio/android/wizards/elements/ProjectChooser.java
new file mode 100644
index 0000000..233742b
--- /dev/null
+++ b/src/plugins/common/src/com/motorola/studio/android/wizards/elements/ProjectChooser.java
@@ -0,0 +1,290 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.wizards.elements;
+
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.IWorkspaceRoot;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.jface.dialogs.IDialogConstants;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.jface.viewers.ViewerFilter;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.ModifyListener;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.dialogs.ElementTreeSelectionDialog;
+import org.eclipse.ui.dialogs.ISelectionStatusValidator;
+import org.eclipse.ui.model.WorkbenchContentProvider;
+import org.eclipse.ui.model.WorkbenchLabelProvider;
+import org.eclipse.ui.views.navigator.ResourceComparator;
+
+import com.motorola.studio.android.common.CommonPlugin;
+import com.motorola.studio.android.common.utilities.i18n.UtilitiesNLS;
+
+/**
+ * This widget displays a Project Chooser element.
+ */
+public class ProjectChooser extends Composite
+{
+
+ private Label lblProject;
+
+ private Text txtProject;
+
+ private Button btnBrowseProject;
+
+ IProject project;
+
+ /**
+ * Get the selected Project.
+ *
+ * @return The selected Project
+ */
+ public IProject getProject()
+ {
+ // get root workspace
+ IWorkspaceRoot workspaceRoot = ResourcesPlugin.getWorkspace().getRoot();
+ // get projects
+ IProject[] projects = workspaceRoot.getProjects();
+
+ // iterate through the projects
+ if ((projects != null) && (projects.length > 0))
+ {
+ for (IProject innerProject : projects)
+ {
+ if (innerProject.getName().equals(txtProject.getText()))
+ {
+ // the project was found, set it
+ this.project = innerProject;
+ break;
+ }
+ }
+ }
+ // return the project
+ return project;
+ }
+
+ /**
+ * Get the text filled in the Project´s text field.
+ *
+ * @return The text filled in the Project´s text field
+ */
+ public String getText()
+ {
+ return txtProject != null ? txtProject.getText() : ""; //$NON-NLS-1$
+ }
+
+ /**
+ * Set the text within this {@link ProjectChooser}.
+ *
+ * @param text The text to set
+ */
+ public void setText(String text)
+ {
+ if (txtProject != null)
+ {
+ this.txtProject.setText(text);
+ }
+ }
+
+ /**
+ * Add a modify listener to the Project´s text field. This listener
+ * is called every time the text is modified.
+ *
+ * @param modifyListener Listener to be added
+ *
+ * @see Text#addModifyListener(ModifyListener)
+ */
+ public void addModifyListener(ModifyListener modifyListener)
+ {
+ // add listener
+ txtProject.addModifyListener(modifyListener);
+ }
+
+ /**
+ * Enables or disables the text field.
+ *
+ * @param enabled <code>true</code> in case the Text Field is to
+ * be enabled, <code>false</code> otherwise.
+ */
+ public void setTextFieldEnabled(boolean enabled)
+ {
+ // enables/disables the text field
+ txtProject.setEnabled(enabled);
+ }
+
+ /**
+ * Constructor holding the parent composite and the stile.
+ *
+ * @param parent Parent Composite
+ * @param style Style
+ */
+ public ProjectChooser(Composite parent, int style)
+ {
+ // call super
+ super(parent, style);
+ // set up layout
+ setupLayout();
+ // add components
+ addComponents();
+ }
+
+ /**
+ * Set up this widget layout
+ */
+ private void setupLayout()
+ {
+ GridLayout layout = new GridLayout(5, false);
+ layout.marginHeight = 0;
+ layout.marginWidth = 0;
+ this.setLayout(layout);
+ this.setLayoutData(new GridData(GridData.FILL, GridData.CENTER, true, false));
+ }
+
+ /**
+ * Add components to this widget
+ */
+ private void addComponents()
+ {
+ // add project label
+ lblProject = new Label(this, SWT.NONE);
+ lblProject.setText(UtilitiesNLS.UI_General_ProjectLabel);
+ lblProject.setLayoutData(new GridData(GridData.FILL, GridData.CENTER, false, false, 1, 1));
+
+ // add text field
+ txtProject = new Text(this, SWT.BORDER);
+ txtProject.setText(""); //$NON-NLS-1$
+ txtProject.setLayoutData(new GridData(GridData.FILL, GridData.CENTER, true, false, 3, 1));
+
+ // add browner button
+ btnBrowseProject = new Button(this, SWT.PUSH);
+ btnBrowseProject.setText(UtilitiesNLS.UI_General_BrowseButtonLabel);
+ btnBrowseProject.setLayoutData(new GridData(GridData.FILL, GridData.CENTER, false, false,
+ 1, 1));
+
+ btnBrowseProject.addListener(SWT.Selection, new Listener()
+ {
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.swt.widgets.Listener#handleEvent(org.eclipse.swt.widgets.Event)
+ */
+
+ public void handleEvent(Event event)
+ {
+ // get the selected project
+ project = openProjectChooser();
+ // write the project in case there is one
+ if (project != null)
+ {
+ txtProject.setText(project.getName());
+ }
+ }
+ });
+ }
+
+ /**
+ * Opens dialog to choose a project
+ *
+ * @return Selected project
+ */
+ private IProject openProjectChooser()
+ {
+ IProject selectedProject = null;
+
+ // get shell
+ Shell shell = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell();
+ // crate package dialog
+ final ElementTreeSelectionDialog packageDialog =
+ new ElementTreeSelectionDialog(shell, new WorkbenchLabelProvider(),
+ new WorkbenchContentProvider());
+
+ // set title and message
+ packageDialog.setTitle(UtilitiesNLS.ProjectChooser_UI_Selection);
+ packageDialog.setMessage(UtilitiesNLS.ProjectChooser_UI_ChooseAProject);
+
+ // set workspace as root
+ packageDialog.setInput(ResourcesPlugin.getWorkspace().getRoot());
+ // set comparator
+ packageDialog.setComparator(new ResourceComparator(ResourceComparator.NAME));
+
+ //filter extensions
+ packageDialog.addFilter(new ViewerFilter()
+ {
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.jface.viewers.ViewerFilter#select(org.eclipse.jface.viewers.Viewer, java.lang.Object, java.lang.Object)
+ */
+ @Override
+ public boolean select(Viewer viewer, Object parentElement, Object element)
+ {
+ // the element must be a project
+ return element instanceof IProject;
+ }
+ });
+ //user can select only one PROJECT
+ packageDialog.setValidator(new ISelectionStatusValidator()
+ {
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.ui.dialogs.ISelectionStatusValidator#validate(java.lang.Object[])
+ */
+
+ public IStatus validate(Object[] selection)
+ {
+ // by default the status is an error
+ IStatus valid = new Status(IStatus.ERROR, CommonPlugin.PLUGIN_ID, ""); //$NON-NLS-1$
+ // one element must be selected
+ if (selection.length == 1)
+ {
+ // this element must be a project
+ if (selection[0] instanceof IProject)
+ {
+ // set status to valid because it is one element and a project
+ valid = new Status(IStatus.OK, CommonPlugin.PLUGIN_ID, ""); //$NON-NLS-1$
+ }
+ }
+ // return validation
+ return valid;
+ }
+ });
+ // open dialog
+ if (packageDialog.open() == IDialogConstants.OK_ID)
+ {
+ // get the first result
+ IResource resource = (IResource) packageDialog.getFirstResult();
+ // the resource must be a project
+ if (resource instanceof IProject)
+ {
+ // get the selected project
+ selectedProject = (IProject) resource;
+ }
+ }
+ // return the selected project
+ return selectedProject;
+ }
+}
diff --git a/src/plugins/common/src/countries.properties b/src/plugins/common/src/countries.properties
new file mode 100644
index 0000000..f04c378
--- /dev/null
+++ b/src/plugins/common/src/countries.properties
@@ -0,0 +1,246 @@
+AF=AFGHANISTAN (AF)
+AX=ÅLAND ISLANDS (AX)
+AL=ALBANIA (AL)
+DZ=ALGERIA (DZ)
+AS=AMERICAN SAMOA (AS)
+AD=ANDORRA (AD)
+AO=ANGOLA (AO)
+AI=ANGUILLA (AI)
+AQ=ANTARCTICA (AQ)
+AG=ANTIGUA AND BARBUDA (AG)
+AR=ARGENTINA (AR)
+AM=ARMENIA (AM)
+AW=ARUBA (AW)
+AU=AUSTRALIA (AU)
+AT=AUSTRIA (AT)
+AZ=AZERBAIJAN (AZ)
+BS=BAHAMAS (BS)
+BH=BAHRAIN (BH)
+BD=BANGLADESH (BD)
+BB=BARBADOS (BB)
+BY=BELARUS (BY)
+BE=BELGIUM (BE)
+BZ=BELIZE (BZ)
+BJ=BENIN (BJ)
+BM=BERMUDA (BM)
+BT=BHUTAN (BT)
+BO=BOLIVIA (BO)
+BA=BOSNIA AND HERZEGOVINA (BA)
+BW=BOTSWANA (BW)
+BV=BOUVET ISLAND (BV)
+BR=BRAZIL (BR)
+IO=BRITISH INDIAN OCEAN TERRITORY (IO)
+BN=BRUNEI DARUSSALAM (BN)
+BG=BULGARIA (BG)
+BF=BURKINA FASO (BF)
+BI=BURUNDI (BI)
+KH=CAMBODIA (KH)
+CM=CAMEROON (CM)
+CA=CANADA (CA)
+CV=CAPE VERDE (CV)
+KY=CAYMAN ISLANDS (KY)
+CF=CENTRAL AFRICAN REPUBLIC (CF)
+TD=CHAD (TD)
+CL=CHILE (CL)
+CN=CHINA (CN)
+CX=CHRISTMAS ISLAND (CX)
+CC=COCOS (KEELING) ISLANDS (CC)
+CO=COLOMBIA (CO)
+KM=COMOROS (KM)
+CG=CONGO (CG)
+CD =CONGO,THE DEMOCRATIC REPUBLIC OF THE (CD)
+CK=COOK ISLANDS (CK)
+CR=COSTA RICA (CR)
+CI=CÔTE D'IVOIRE (CI)
+HR=CROATIA (HR)
+CU=CUBA (CU)
+CY=CYPRUS (CY)
+CZ=CZECH REPUBLIC (CZ)
+DK=DENMARK (DK)
+DJ=DJIBOUTI (DJ)
+DM=DOMINICA (DM)
+DO=DOMINICAN REPUBLIC (DO)
+EC=ECUADOR (EC)
+EG=EGYPT (EG)
+SV=EL SALVADOR (SV)
+GQ=EQUATORIAL GUINEA (GQ)
+ER=ERITREA (ER)
+EE=ESTONIA (EE)
+ET=ETHIOPIA (ET)
+FK=FALKLAND ISLANDS (MALVINAS) (FK)
+FO=FAROE ISLANDS (FO)
+FJ =FIJI (FJ)
+FI=FINLAND (FI)
+FR=FRANCE (FR)
+GF=FRENCH GUIANA (GF)
+PF=FRENCH POLYNESIA (PF)
+TF=FRENCH SOUTHERN TERRITORIES (TF)
+GA=GABON (GA)
+GM=GAMBIA (GM)
+GE=GEORGIA (GE)
+DE=GERMANY (DE)
+GH=GHANA (GH)
+GI=GIBRALTAR (GI)
+GR=GREECE (GR)
+GL=GREENLAND (GL)
+GD=GRENADA (GD)
+GP=GUADELOUPE (GP)
+GU=GUAM (GU)
+GT=GUATEMALA (GT)
+GG=GUERNSEY (GG)
+GN=GUINEA (GN)
+GW=GUINEA-BISSAU (GW)
+GY=GUYANA (GY)
+HT=HAITI (HT)
+HM=HEARD ISLAND AND MCDONALD ISLANDS (HM)
+HN=HONDURAS (HN)
+HK=HONG KONG (HK)
+HU=HUNGARY (HU)
+IS=ICELAND (IS)
+IN=INDIA (IN)
+ID=INDONESIA (ID)
+IR=IRAN,ISLAMIC REPUBLIC OF (IR)
+IQ=IRAQ (IQ)
+IE=IRELAND (IE)
+IM=ISLE OF MAN (IM)
+IL=ISRAEL (IL)
+IT=ITALY (IT)
+JM=JAMAICA (JM)
+JP=JAPAN (JP)
+JE=JERSEY (JE)
+JO=JORDAN (JO)
+KZ=KAZAKHSTAN (KZ)
+KE=KENYA (KE)
+KI=KIRIBATI (KI)
+KP=KOREA,DEMOCRATIC PEOPLE'S REPUBLIC OF (KP)
+KR=KOREA,REPUBLIC OF (KR)
+KW=KUWAIT (KW)
+KG=KYRGYZSTAN (KG)
+LA=LAO PEOPLE'S DEMOCRATIC REPUBLIC (LA)
+LV=LATVIA (LV)
+LB=LEBANON (LB)
+LS=LESOTHO (LS)
+LR=LIBERIA (LR)
+LY=LIBYAN ARAB JAMAHIRIYA (LY)
+LI=LIECHTENSTEIN (LI)
+LT=LITHUANIA (LT)
+LU=LUXEMBOURG (LU)
+MO=MACAO (MO)
+MK=MACEDONIA,THE FORMER YUGOSLAV REPUBLIC OF (MK)
+MG=MADAGASCAR (MG)
+MW=MALAWI (MW)
+MY=MALAYSIA (MY)
+MV=MALDIVES (MV)
+ML=MALI (ML)
+MT=MALTA (MT)
+MH=MARSHALL ISLANDS (MH)
+MQ=MARTINIQUE (MQ)
+MR=MAURITANIA (MR)
+MU=MAURITIUS (MU)
+YT=MAYOTTE (YT)
+MX=MEXICO (MX)
+FM=MICRONESIA,FEDERATED STATES OF (FM)
+MD=MOLDOVA (MD)
+MC=MONACO (MC)
+MN=MONGOLIA (MN)
+ME=MONTENEGRO (ME)
+MS=MONTSERRAT (MS)
+MA=MOROCCO (MA)
+MZ=MOZAMBIQUE (MZ)
+MM=MYANMAR (MM)
+NA=NAMIBIA (NA)
+NR=NAURU (NR)
+NP=NEPAL (NP)
+NL=NETHERLANDS (NL)
+AN=NETHERLANDS ANTILLES (AN)
+NC=NEW CALEDONIA (NC)
+NZ=NEW ZEALAND (NZ)
+NI=NICARAGUA (NI)
+NE=NIGER (NE)
+NG=NIGERIA (NG)
+NU=NIUE (NU)
+NF=NORFOLK ISLAND (NF)
+MP=NORTHERN MARIANA ISLANDS (MP)
+NO=NORWAY (NO)
+OM=OMAN (OM)
+PK=PAKISTAN (PK)
+PW=PALAU (PW)
+PS=PALESTINIAN TERRITORY,OCCUPIED (PS)
+PA=PANAMA (PA)
+PG=PAPUA NEW GUINEA (PG)
+PY=PARAGUAY (PY)
+PE=PERU (PE)
+PH=PHILIPPINES (PH)
+PN=PITCAIRN (PN)
+PL=POLAND (PL)
+PT=PORTUGAL (PT)
+PR=PUERTO RICO (PT)
+QA=QATAR (QA)
+RE=RÉUNION (RE)
+RO=ROMANIA (RO)
+RU=RUSSIAN FEDERATION (RU)
+RW=RWANDA (RW)
+BL=SAINT BARTHÉLEMY (BL)
+SH=SAINT HELENA (SH)
+KN=SAINT KITTS AND NEVIS (KN)
+LC=SAINT LUCIA (LC)
+MF=SAINT MARTIN (MF)
+PM=SAINT PIERRE AND MIQUELON (PM)
+VC=SAINT VINCENT AND THE GRENADINES (VC)
+WS=SAMOA (WS)
+SM=SAN MARINO (SM)
+ST=SAO TOME AND PRINCIPE (ST)
+SA=SAUDI ARABIA (SA)
+SN=SENEGAL (SN)
+RS=SERBIA (RS)
+SC=SEYCHELLES (SC)
+SL=SIERRA LEONE (SL)
+SG=SINGAPORE (SG)
+SK=SLOVAKIA (SK)
+SI=SLOVENIA (SI)
+SB=SOLOMON ISLANDS (SB)
+SO=SOMALIA (SO)
+ZA=SOUTH AFRICA (ZA)
+GS=SOUTH GEORGIA AND THE SOUTH SANDWICH ISLANDS (GS)
+ES=SPAIN (ES)
+LK=SRI LANKA (LK)
+SD=SUDAN (SD)
+SR=SURINAME (SR)
+SJ=SVALBARD AND JAN MAYEN (SJ)
+SZ=SWAZILAND (SZ)
+SE=SWEDEN (SE)
+CH=SWITZERLAND (CH)
+SY=SYRIAN ARAB REPUBLIC (SY)
+TW=TAIWAN,PROVINCE OF CHINA (TW)
+TJ=TAJIKISTAN (TJ)
+TZ=TANZANIA,UNITED REPUBLIC OF (TZ)
+TH=THAILAND (TH)
+TL=TIMOR-LESTE (TL)
+TG=TOGO (TG)
+TK=TOKELAU (TK)
+TO=TONGA (TO)
+TT=TRINIDAD AND TOBAGO (TT)
+TN=TUNISIA (TN)
+TR=TURKEY (TR)
+TM=TURKMENISTAN (TM)
+TC=TURKS AND CAICOS ISLANDS (TC)
+TV=TUVALU (TV)
+UG=UGANDA (UG)
+UA=UKRAINE (UA)
+AE=UNITED ARAB EMIRATES (AE)
+GB=UNITED KINGDOM (GB)
+US=UNITED STATES (US)
+UM=UNITED STATES MINOR OUTLYING ISLANDS (UM)
+UY=URUGUAY (UY)
+UZ=UZBEKISTAN (UZ)
+VU=VANUATU (VU)
+VA=VATICAN CITY STATE (VA)
+VE=VENEZUELA (VE)
+VN=VIET NAM (VN)
+VG=VIRGIN ISLANDS,BRITISH (VG)
+VI=VIRGIN ISLANDS,U.S. (VI)
+WF=WALLIS AND FUTUNA (WF)
+EH=WESTERN SAHARA (EH)
+YE=YEMEN (YE)
+ZM=ZAMBIA (ZM)
+ZW=ZIMBABWE (ZW) \ No newline at end of file
diff --git a/src/plugins/db.core/.classpath b/src/plugins/db.core/.classpath
new file mode 100644
index 0000000..64c5e31
--- /dev/null
+++ b/src/plugins/db.core/.classpath
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.5"/>
+ <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/src/plugins/db.core/.project b/src/plugins/db.core/.project
new file mode 100644
index 0000000..9eb4de7
--- /dev/null
+++ b/src/plugins/db.core/.project
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>com.motorolamobility.studio.android.db.core</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.ManifestBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.SchemaBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>com.instantiations.assist.eclipse.coverage.instrumentationBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.pde.PluginNature</nature>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ <nature>com.instantiations.assist.eclipse.coverage.codeCoverageNature</nature>
+ </natures>
+</projectDescription>
diff --git a/src/plugins/db.core/META-INF/MANIFEST.MF b/src/plugins/db.core/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..df19452
--- /dev/null
+++ b/src/plugins/db.core/META-INF/MANIFEST.MF
@@ -0,0 +1,32 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: %pluginName
+Bundle-SymbolicName: com.motorolamobility.studio.android.db.core;singleton:=true
+Bundle-Version: 5.0.0.qualifier
+Bundle-Activator: com.motorolamobility.studio.android.db.core.DbCoreActivator
+Bundle-Vendor: %providerName
+Require-Bundle: org.eclipse.ui,
+ org.eclipse.core.runtime,
+ org.eclipse.datatools.connectivity,
+ com.motorola.studio.android.common,
+ org.eclipse.core.resources,
+ org.eclipse.datatools.connectivity.sqm.core,
+ org.eclipse.datatools.modelbase.sql,
+ org.junit,
+ org.eclipse.datatools.sqltools.data.ui,
+ com.motorola.studio.android.codeutils,
+ org.eclipse.datatools.sqltools.editor.core,
+ org.eclipse.datatools.sqltools.result,
+ org.eclipse.core.expressions,
+ org.eclipse.datatools.sqltools.ddlgen.ui
+Bundle-RequiredExecutionEnvironment: JavaSE-1.6
+Bundle-ActivationPolicy: lazy
+Bundle-Localization: plugin
+Export-Package: com.motorolamobility.studio.android.db.core,
+ com.motorolamobility.studio.android.db.core.command,
+ com.motorolamobility.studio.android.db.core.event,
+ com.motorolamobility.studio.android.db.core.exception,
+ com.motorolamobility.studio.android.db.core.model,
+ com.motorolamobility.studio.android.db.core.ui,
+ com.motorolamobility.studio.android.db.core.ui.action,
+ com.motorolamobility.studio.android.db.core.ui.view
diff --git a/src/plugins/db.core/build.properties b/src/plugins/db.core/build.properties
new file mode 100644
index 0000000..6003eb8
--- /dev/null
+++ b/src/plugins/db.core/build.properties
@@ -0,0 +1,8 @@
+source.. = src/
+output.. = bin/
+bin.includes = META-INF/,\
+ .,\
+ plugin.properties,\
+ plugin.xml,\
+ res/,\
+ icons/
diff --git a/src/plugins/db.core/icons/action_content_provider.png b/src/plugins/db.core/icons/action_content_provider.png
new file mode 100644
index 0000000..193bbed
--- /dev/null
+++ b/src/plugins/db.core/icons/action_content_provider.png
Binary files differ
diff --git a/src/plugins/db.core/icons/action_create_database.png b/src/plugins/db.core/icons/action_create_database.png
new file mode 100644
index 0000000..3e5f2c9
--- /dev/null
+++ b/src/plugins/db.core/icons/action_create_database.png
Binary files differ
diff --git a/src/plugins/db.core/icons/action_new_table.png b/src/plugins/db.core/icons/action_new_table.png
new file mode 100644
index 0000000..2cf041d
--- /dev/null
+++ b/src/plugins/db.core/icons/action_new_table.png
Binary files differ
diff --git a/src/plugins/db.core/icons/connect.png b/src/plugins/db.core/icons/connect.png
new file mode 100644
index 0000000..894ac86
--- /dev/null
+++ b/src/plugins/db.core/icons/connect.png
Binary files differ
diff --git a/src/plugins/db.core/icons/database_explorer_view.png b/src/plugins/db.core/icons/database_explorer_view.png
new file mode 100644
index 0000000..0dff946
--- /dev/null
+++ b/src/plugins/db.core/icons/database_explorer_view.png
Binary files differ
diff --git a/src/plugins/db.core/icons/dbplate.gif b/src/plugins/db.core/icons/dbplate.gif
new file mode 100644
index 0000000..0dff946
--- /dev/null
+++ b/src/plugins/db.core/icons/dbplate.gif
Binary files differ
diff --git a/src/plugins/db.core/icons/disconnect.png b/src/plugins/db.core/icons/disconnect.png
new file mode 100644
index 0000000..05bd5e5
--- /dev/null
+++ b/src/plugins/db.core/icons/disconnect.png
Binary files differ
diff --git a/src/plugins/db.core/icons/filesystem.png b/src/plugins/db.core/icons/filesystem.png
new file mode 100644
index 0000000..b9fc741
--- /dev/null
+++ b/src/plugins/db.core/icons/filesystem.png
Binary files differ
diff --git a/src/plugins/db.core/icons/map.png b/src/plugins/db.core/icons/map.png
new file mode 100644
index 0000000..da735e0
--- /dev/null
+++ b/src/plugins/db.core/icons/map.png
Binary files differ
diff --git a/src/plugins/db.core/icons/obj16/dbplate.gif b/src/plugins/db.core/icons/obj16/dbplate.gif
new file mode 100644
index 0000000..0dff946
--- /dev/null
+++ b/src/plugins/db.core/icons/obj16/dbplate.gif
Binary files differ
diff --git a/src/plugins/db.core/icons/obj16/plate16.png b/src/plugins/db.core/icons/obj16/plate16.png
new file mode 100644
index 0000000..8336aef
--- /dev/null
+++ b/src/plugins/db.core/icons/obj16/plate16.png
Binary files differ
diff --git a/src/plugins/db.core/icons/ovr16/error_ovr.png b/src/plugins/db.core/icons/ovr16/error_ovr.png
new file mode 100644
index 0000000..32319c2
--- /dev/null
+++ b/src/plugins/db.core/icons/ovr16/error_ovr.png
Binary files differ
diff --git a/src/plugins/db.core/icons/unmap.png b/src/plugins/db.core/icons/unmap.png
new file mode 100644
index 0000000..55193e5
--- /dev/null
+++ b/src/plugins/db.core/icons/unmap.png
Binary files differ
diff --git a/src/plugins/db.core/icons/wizban/create_database_ban.png b/src/plugins/db.core/icons/wizban/create_database_ban.png
new file mode 100644
index 0000000..395895d
--- /dev/null
+++ b/src/plugins/db.core/icons/wizban/create_database_ban.png
Binary files differ
diff --git a/src/plugins/db.core/icons/wizban/create_table.png b/src/plugins/db.core/icons/wizban/create_table.png
new file mode 100644
index 0000000..14ce647
--- /dev/null
+++ b/src/plugins/db.core/icons/wizban/create_table.png
Binary files differ
diff --git a/src/plugins/db.core/plugin.properties b/src/plugins/db.core/plugin.properties
new file mode 100644
index 0000000..f4331c4
--- /dev/null
+++ b/src/plugins/db.core/plugin.properties
@@ -0,0 +1,83 @@
+#################################################################################
+#
+# Database core properties
+#
+#################################################################################
+
+pluginName=MOTODEV Studio for Android Database Core Plug-in
+providerName= Motorola Mobility, Inc.
+workspaceExtensionName=Workspace
+motodevDatabaseViewName=MOTODEV Database Explorer
+
+create_db_action=Create &Database...
+create_database_command_label=Create Database
+
+browse_table_contents_action=&Browse Table Contents...
+
+action_refresh_project=&Refresh
+action_refresh_workspace=&Refresh
+action_refresh_filesystem=&Refresh
+action_refresh_db=&Refresh
+action_refresh_table=&Refresh
+action_delete_table=&Delete
+action_delete_database=&Delete
+action_refresh_db_mapper_node=&Refresh
+
+action_map_database=&Map Database...
+action_unmap_database=&Unmap Database...
+
+command_collapse_all=Collapse All
+
+command_map_database=Map Database
+command_unmap_database=Unmap Database
+
+create_table_action=Create &Table...
+
+connect_db_action=&Connect to the Database
+disconnect_db_action=D&isconnect from the Database
+
+command_connect_name=Connect to a database
+command_connect_description=Connect to a database using an instance of IDbNode
+command_disconnect_name=Disconnect from a database
+command_disconnect_description=Disconnect from a database using an instance of IDbNode
+
+command_refresh_node=Refresh
+open_sql_scrapbook=Open SQL Scrapbook
+open_sql_scrapbook_tooltip=Open scrapbook to edit SQL statements
+
+command_refresh_project_name=Refresh
+command_refresh_workspace_name=Refresh
+
+command_browse_table_contents_name=Browse Table Contents...
+command_browse_table_contents_description=Browse Table Contents using an instance of TableNode
+
+command_delete_table_name=Delete Table
+command_delete_table_description=Delete Selected Table
+
+command_delete_database_name=Delete Database
+command_delete_database_description=Delete Selected Database
+
+command_create_table_name=Create Table
+command_create_table_description=Create a New Table
+
+data_sub_menu=Data
+command_sample_data=&Sample Data...
+command_extract_data=&Extract Data...
+command_load_data=&Load Data...
+
+createDBManagementClassesCommandLabel=Create Database Management Classes...
+createDBManagementClassesCommandDescription=Create database management classes using an instance of IDbNode
+createDBManagementClassesAction=Create Database &Management Classes...
+db_perspective=MOTODEV Database
+manageDatabaseLabel=Manage Database
+openDatabasePerspectiveLabel=Open MOTODEV Database Perspective
+
+menu_collapse_all=Collapse All
+menu_refresh_node=Refresh
+
+menu_create_db_management_classes=Create Database Management Classes...
+menu_browse_table_contents=Browse Table Contents...
+
+filesystemRootNodeName=Filesystem
+
+decorator_error_node_label=Decoration for nodes with error \ No newline at end of file
diff --git a/src/plugins/db.core/plugin.xml b/src/plugins/db.core/plugin.xml
new file mode 100644
index 0000000..d15b676
--- /dev/null
+++ b/src/plugins/db.core/plugin.xml
@@ -0,0 +1,727 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?eclipse version="3.4"?>
+<plugin>
+ <extension-point id="com.motorolamobility.studio.android.db.core.dbRootNode" name="Workspace" schema="schema/com.motorolamobility.studio.android.db.core.dbRootNode.exsd"/>
+<extension
+ point="com.motorolamobility.studio.android.db.core.dbRootNode">
+ <rootNode
+ class="com.motorolamobility.studio.android.db.core.workspace.WorkspaceRootNode"
+ id="com.motorolamobility.studio.android.db.core.rootNode1"
+ name="%workspaceExtensionName">
+ </rootNode>
+ </extension>
+ <extension
+ point="org.eclipse.ui.views">
+ <view
+ category="studioAndroidViewCategory"
+ class="com.motorolamobility.studio.android.db.core.ui.view.MOTODEVDatabaseExplorerView"
+ icon="icons/database_explorer_view.png"
+ id="com.motorola.studio.android.db.databaseView"
+ name="%motodevDatabaseViewName"
+ restorable="true">me="%motodevDatabaseViewName"
+ restorable="true">
+ </view>
+ </extension>
+ <extension
+ point="org.eclipse.ui.popupMenus">
+ <objectContribution
+ adaptable="false"
+ id="com.motorolamobility.studio.android.db.core.ui.action.IDbCreatorNode"
+ objectClass="com.motorolamobility.studio.android.db.core.ui.action.IDbCreatorNode">
+ <action
+ class="com.motorolamobility.studio.android.db.core.ui.action.PopupMenuActionDelegate"
+ definitionId="com.motorolamobility.studio.android.db.core.createDatabaseCommand"
+ enablesFor="1"
+ icon="icons/action_create_database.png"
+ id="com.motorolamobility.studio.android.db.core.ui.action.createDbAction"
+ label="%create_db_action">
+ </action>
+ <action
+ class="com.motorolamobility.studio.android.db.core.ui.action.PopupMenuActionDelegate"
+ definitionId="com.motorolamobility.studio.android.db.core.refreshNode"
+ enablesFor="1"
+ icon="platform:/plugin/org.eclipse.datatools.sqltools.schemaobjecteditor.ui/icons/refresh_from_server.gif"
+ id="com.motorolamobility.studio.android.db.core.ui.action.refreshProjectNode"
+ label="%action_refresh_project">
+ </action>
+ </objectContribution>
+ <objectContribution
+ adaptable="false"
+ id="com.motorolamobility.studio.android.db.core.ui.action.IDbNode"
+ objectClass="com.motorolamobility.studio.android.db.core.ui.IDbNode">
+ <action
+ class="com.motorolamobility.studio.android.db.core.ui.action.PopupMenuActionDelegate"
+ definitionId="com.motorolamobility.studio.android.db.core.createTableCommand"
+ enablesFor="1"
+ icon="icons/action_new_table.png"
+ id="com.motorolamobility.studio.android.db.core.ui.action.createTableAction"
+ label="%create_table_action">
+ <enablement>
+ <objectState
+ name="com.motorolamobility.studio.android.db.core.databaseConnection"
+ value="com.motorolamobility.studio.android.db.core.databaseConnected">
+ </objectState>
+ </enablement>
+ </action>
+ <action
+ class="com.motorolamobility.studio.android.db.core.ui.action.PopupMenuActionDelegate"
+ definitionId="com.motorolamobility.studio.android.db.core.connect"
+ enablesFor="1"
+ icon="icons/connect.png"
+ id="com.motorolamobility.studio.android.db.core.ui.action.connect"
+ label="%connect_db_action">
+ <enablement>
+ <objectState
+ name="com.motorolamobility.studio.android.db.core.databaseConnection"
+ value="com.motorolamobility.studio.android.db.core.databaseDisconnected">
+ </objectState></enablement>
+ </action>
+ <action
+ class="com.motorolamobility.studio.android.db.core.ui.action.PopupMenuActionDelegate"
+ definitionId="com.motorolamobility.studio.android.db.core.disconnect"
+ enablesFor="1"
+ icon="icons/disconnect.png"
+ id="com.motorolamobility.studio.android.db.core.ui.action.disconnect"
+ label="%disconnect_db_action">
+ <enablement>
+ <objectState
+ name="com.motorolamobility.studio.android.db.core.databaseConnection"
+ value="com.motorolamobility.studio.android.db.core.databaseConnected">
+ </objectState>
+ </enablement>
+ </action>
+ <action
+ class="com.motorolamobility.studio.android.db.core.ui.action.PopupMenuActionDelegate"
+ definitionId="com.motorolamobility.studio.android.db.core.createDBManagementClasses"
+ enablesFor="1"
+ icon="icons/action_content_provider.png"
+ id="com.motorolamobility.studio.android.db.core.ui.action.createDatabaseManagementClasses"
+ label="%createDBManagementClassesAction">
+ </action>
+ <action
+ class="com.motorolamobility.studio.android.db.core.ui.action.PopupMenuActionDelegate"
+ definitionId="com.motorolamobility.studio.android.db.core.refreshNode"
+ enablesFor="1"
+ icon="platform:/plugin/org.eclipse.datatools.sqltools.schemaobjecteditor.ui/icons/refresh_from_server.gif"
+ id="com.motorolamobility.studio.android.db.core.ui.action.refreshDbNode"
+ label="%action_refresh_db">
+ </action>
+ <action
+ class="com.motorolamobility.studio.android.db.core.ui.action.PopupMenuActionDelegate"
+ definitionId="com.motorolamobility.studio.android.db.core.deleteDatabase"
+ icon="platform:/plugin/org.eclipse.datatools.connectivity.sqm.core.ui/icons/delete.gif"
+ id="com.motorolamobility.studio.android.db.core.ui.action.deleteDatabase"
+ label="%action_delete_database">
+ <enablement>
+ <not>
+ <objectState
+ name="com.motorolamobility.studio.android.db.core.IDbNodeType"
+ value="com.motorolamobility.studio.android.db.core.isExternalStorage">
+ </objectState>
+ </not>
+ </enablement>
+ </action>
+ </objectContribution>
+ <objectContribution
+ adaptable="false"
+ id="com.motorolamobility.studio.android.db.core.ui.action.IRootNode"
+ objectClass="com.motorolamobility.studio.android.db.core.ui.IRootNode">
+ <action
+ class="com.motorolamobility.studio.android.db.core.ui.action.PopupMenuActionDelegate"
+ definitionId="com.motorolamobility.studio.android.db.core.refreshNode"
+ enablesFor="1"
+ icon="platform:/plugin/org.eclipse.datatools.sqltools.schemaobjecteditor.ui/icons/refresh_from_server.gif"
+ id="com.motorolamobility.studio.android.db.core.ui.action.refreshWorkspaceNode"
+ label="%action_refresh_workspace">
+ </action>
+ </objectContribution>
+ <objectContribution
+ adaptable="false"
+ id="com.motorolamobility.studio.android.db.core.ui.action.ITableNode"
+ objectClass="com.motorolamobility.studio.android.db.core.ui.ITableNode">
+ <menu
+ id="com.motorolamobility.studio.android.db.core.dataSubMenu"
+ label="%data_sub_menu">
+ </menu>
+ <action
+ class="com.motorolamobility.studio.android.db.core.ui.action.PopupMenuActionDelegate"
+ definitionId="com.motorolamobility.studio.android.db.core.extractData"
+ enablesFor="1"
+ id="com.motorolamobility.studio.android.db.core.ui.action.extractData"
+ label="%command_extract_data"
+ menubarPath="com.motorolamobility.studio.android.db.core.dataSubMenu/data">
+ </action>
+ <action
+ class="com.motorolamobility.studio.android.db.core.ui.action.PopupMenuActionDelegate"
+ definitionId="com.motorolamobility.studio.android.db.core.loadData"
+ enablesFor="1"
+ id="com.motorolamobility.studio.android.db.core.ui.action.loadData"
+ label="%command_load_data"
+ menubarPath="com.motorolamobility.studio.android.db.core.dataSubMenu/data">
+ </action>
+ <action
+ class="com.motorolamobility.studio.android.db.core.ui.action.PopupMenuActionDelegate"
+ definitionId="com.motorolamobility.studio.android.db.core.browseTableContents"
+ enablesFor="1"
+ icon="platform:/plugin/org.eclipse.datatools.sqltools.data.ui/icons/table.gif"
+ id="com.motorolamobility.studio.android.db.core.ui.action.browseTableContents"
+ label="%browse_table_contents_action">
+ </action>
+ <action
+ class="com.motorolamobility.studio.android.db.core.ui.action.PopupMenuActionDelegate"
+ definitionId="com.motorolamobility.studio.android.db.core.refreshNode"
+ enablesFor="1"
+ icon="platform:/plugin/org.eclipse.datatools.sqltools.schemaobjecteditor.ui/icons/refresh_from_server.gif"
+ id="com.motorolamobility.studio.android.db.core.ui.action.refreshTableNode"
+ label="%action_refresh_table">
+ </action>
+ <action
+ class="com.motorolamobility.studio.android.db.core.ui.action.PopupMenuActionDelegate"
+ definitionId="com.motorolamobility.studio.android.db.core.deleteTable"
+ enablesFor="1"
+ icon="platform:/plugin/org.eclipse.datatools.connectivity.sqm.core.ui/icons/delete.gif"
+ id="com.motorolamobility.studio.android.db.core.ui.action.deleteTable"
+ label="%action_delete_table">
+ </action>
+ </objectContribution>
+ <objectContribution
+ adaptable="false"
+ id="com.motorolamobility.studio.android.db.core.ui.action.IDbMapperNode"
+ objectClass="com.motorolamobility.studio.android.db.core.ui.IDbMapperNode">
+ <action
+ class="com.motorolamobility.studio.android.db.core.ui.action.PopupMenuActionDelegate"
+ definitionId="com.motorolamobility.studio.android.db.core.mapDatabase"
+ enablesFor="1"
+ icon="icons/map.png"
+ id="com.motorolamobility.studio.android.db.core.ui.action.mapDbNode"
+ label="%action_map_database">
+ </action>
+ <action
+ class="com.motorolamobility.studio.android.db.core.ui.action.PopupMenuActionDelegate"
+ definitionId="com.motorolamobility.studio.android.db.core.unmapDatabase"
+ enablesFor="1"
+ icon="icons/unmap.png"
+ id="com.motorolamobility.studio.android.db.core.ui.action.unmapDbNode"
+ label="%action_unmap_database">
+ <enablement>
+ <objectState
+ name="com.motorolamobility.studio.android.db.core.mappedDatabases"
+ value="com.motorolamobility.studio.android.db.core.atLeastOne">
+ </objectState>
+ </enablement>
+ </action>
+ <action
+ class="com.motorolamobility.studio.android.db.core.ui.action.PopupMenuActionDelegate"
+ enablesFor="1"
+ icon="platform:/plugin/org.eclipse.datatools.sqltools.schemaobjecteditor.ui/icons/refresh_from_server.gif"
+ id="com.motorolamobility.studio.android.db.core.ui.action.refreshDbMapperNode"
+ label="%action_refresh_db_mapper_node">
+ </action>
+ </objectContribution>
+ <objectContribution
+ adaptable="false"
+ id="com.motorolamobility.studio.android.db.core.ui.action.IDataSampler"
+ objectClass="com.motorolamobility.studio.android.db.core.ui.IDataSampler">
+ <menu
+ id="com.motorolamobility.studio.android.db.core.dataSubMenu"
+ label="%data_sub_menu">
+ </menu>
+ <action
+ class="com.motorolamobility.studio.android.db.core.ui.action.PopupMenuActionDelegate"
+ definitionId="com.motorolamobility.studio.android.db.core.sampleContents"
+ enablesFor="1"
+ icon="platform:/plugin/org.eclipse.datatools.connectivity.sqm.core.ui/icons/sampleContents.gif"
+ id="com.motorolamobility.studio.android.db.core.ui.action.sampleContents"
+ label="%command_sample_data"
+ menubarPath="com.motorolamobility.studio.android.db.core.dataSubMenu/data">
+ </action>
+ </objectContribution>
+ </extension> <extension
+ point="org.eclipse.ui.commands">
+ <command
+ defaultHandler="com.motorolamobility.studio.android.db.core.command.DbConnectHandler"
+ description="%command_connect_description"
+ id="com.motorolamobility.studio.android.db.core.connect"
+ name="%command_connect_name">
+ </command>
+ <command
+ defaultHandler="com.motorolamobility.studio.android.db.core.command.DbDisconnectHandler"
+ description="%command_disconnect_description"
+ id="com.motorolamobility.studio.android.db.core.disconnect"
+ name="%command_disconnect_name">
+ </command>
+ <command
+ defaultHandler="com.motorolamobility.studio.android.db.core.command.DbCreateHandler"
+ id="com.motorolamobility.studio.android.db.core.createDatabaseCommand"
+ name="%create_database_command_label">
+ </command>
+ <command
+ defaultHandler="com.motorolamobility.studio.android.db.core.command.RefreshNodeHandler"
+ id="com.motorolamobility.studio.android.db.core.refreshNode"
+ name="%command_refresh_node">
+ </command>
+ <command
+ defaultHandler="com.motorolamobility.studio.android.db.core.command.SampleContentsHandler"
+ description="%command_sample_data"
+ id="com.motorolamobility.studio.android.db.core.browseTableContents"
+ name="%command_sample_data">
+ </command>
+ <command
+ defaultHandler="com.motorolamobility.studio.android.db.core.command.ExtractDataHandler"
+ description="%command_extract_data"
+ id="com.motorolamobility.studio.android.db.core.extractData"
+ name="%command_extract_data">
+ </command>
+ <command
+ defaultHandler="com.motorolamobility.studio.android.db.core.command.LoadDataHandler"
+ description="%command_load_data"
+ id="com.motorolamobility.studio.android.db.core.loadData"
+ name="%command_load_data">
+ </command>
+ <command
+ defaultHandler="com.motorolamobility.studio.android.db.core.command.BrowseTableContentsHandler"
+ description="%command_sample_data"
+ id="com.motorolamobility.studio.android.db.core.sampleContents"
+ name="%command_sample_data">
+ </command>
+ <command
+ defaultHandler="com.motorolamobility.studio.android.db.core.command.CreateDatabaseManagementClassesHandler"
+ description="%createDBManagementClassesCommandDescription"
+ id="com.motorolamobility.studio.android.db.core.createDBManagementClasses"
+ name="%createDBManagementClassesCommandLabel">
+ </command>
+ <command
+ defaultHandler="com.motorolamobility.studio.android.db.core.command.DeleteTableHandler"
+ description="%command_delete_table_description"
+ id="com.motorolamobility.studio.android.db.core.deleteTable"
+ name="%command_delete_table_name">
+ </command>
+ <command
+ defaultHandler="com.motorolamobility.studio.android.db.core.command.DeleteDatabaseHandler"
+ description="%command_delete_database_description"
+ id="com.motorolamobility.studio.android.db.core.deleteDatabase"
+ name="%command_delete_database_name">
+ </command>
+ <command
+ defaultHandler="com.motorolamobility.studio.android.db.core.command.MapDatabaseHandler"
+ id="com.motorolamobility.studio.android.db.core.mapDatabase"
+ name="%command_map_database">
+ </command>
+ <command
+ defaultHandler="com.motorolamobility.studio.android.db.core.command.UnmapDatabaseHandler"
+ id="com.motorolamobility.studio.android.db.core.unmapDatabase"
+ name="%command_unmap_database">
+ </command>
+ <command
+ defaultHandler="com.motorolamobility.studio.android.db.core.command.TableCreateHandler"
+ description="%command_create_table_description"
+ id="com.motorolamobility.studio.android.db.core.createTableCommand"
+ name="%command_create_table_name">
+ </command>
+ <command
+ defaultHandler="com.motorolamobility.studio.android.db.core.command.CollapseAllHandler"
+ id="com.motorolamobility.studio.android.db.core.collapseAll"
+ name="%command_collapse_all">
+ </command>
+ </extension>
+ <extension
+ point="org.eclipse.ui.menus">
+ <menuContribution
+ allPopups="false"
+ locationURI="toolbar:com.motorola.studio.android.db.databaseView">
+ <command
+ commandId="com.motorolamobility.studio.android.db.core.collapseAll"
+ icon="platform:/plugin/org.eclipse.datatools.connectivity.sqm.core.ui/icons/collapseall.gif"
+ label="%menu_collapse_all"
+ style="push">
+ </command>
+ <command
+ commandId="com.motorolamobility.studio.android.db.core.refreshNode"
+ icon="platform:/plugin/org.eclipse.datatools.sqltools.schemaobjecteditor.ui/icons/refresh_from_server.gif"
+ label="%menu_refresh_node"
+ style="push">
+ </command>
+ <separator
+ name="com.motorolamobility.studio.android.db.core.separatorTableManagement"
+ visible="true">
+ </separator>
+ <command
+ commandId="com.motorolamobility.studio.android.db.core.createTableCommand"
+ icon="icons/action_new_table.png"
+ label="%command_create_table_name"
+ style="push">
+ </command>
+ <command
+ commandId="com.motorolamobility.studio.android.db.core.createDBManagementClasses"
+ icon="icons/action_content_provider.png"
+ label="%menu_create_db_management_classes"
+ style="push">
+ </command>
+ <separator
+ name="com.motorolamobility.studio.android.db.core.separatorTableCommands"
+ visible="true">
+ </separator>
+ <command
+ commandId="com.motorolamobility.studio.android.db.core.browseTableContents"
+ icon="platform:/plugin/org.eclipse.datatools.sqltools.data.ui/icons/table.gif"
+ label="%menu_browse_table_contents"
+ style="push">
+ </command>
+ <separator
+ name="com.motorolamobility.studio.android.db.core.separatorOtherCommands"
+ visible="true">
+ </separator>
+ <command
+ commandId="org.eclipse.datatools.sqltools.sqlscrapbook.commands.openscrapbook"
+ icon="platform:/plugin/org.eclipse.datatools.sqltools.sqlscrapbook/images/scrapbook.gif"
+ id="com.motorola.studio.android.db.commands.openscrapbookinview"
+ label="%open_sql_scrapbook"
+ tooltip="%open_sql_scrapbook_tooltip">
+ </command>
+ </menuContribution>
+ <menuContribution locationURI="menu:motorolaMenu?after=manageDatabaseSeparator">
+ <menu
+ id="studioManageDatabaseMenu"
+ label="%manageDatabaseLabel">
+ <command
+ commandId="com.motorolamobility.studio.android.db.core.createDBManagementClasses"
+ disabledIcon="icons/action_content_provider.png"
+ hoverIcon="icons/action_content_provider.png"
+ icon="icons/action_content_provider.png"
+ label="%createDBManagementClassesCommandLabel"
+ style="push">
+ </command>
+ <command
+ commandId="org.eclipse.ui.perspectives.showPerspective"
+ icon="icons/dbplate.gif"
+ label="%openDatabasePerspectiveLabel"
+ style="push">
+ <parameter
+ name="org.eclipse.ui.perspectives.showPerspective.perspectiveId"
+ value="com.motorola.studio.android.db.perspective">
+ </parameter>
+ </command>
+ <command
+ commandId="com.motorolamobility.studio.android.db.core.createDatabaseCommand"
+ disabledIcon="icons/action_create_database.png"
+ hoverIcon="icons/action_create_database.png"
+ icon="icons/action_create_database.png"
+ label="%create_database_command_label"
+ style="push">
+ </command>
+ </menu>
+
+ </menuContribution>
+ <menuContribution
+ locationURI="popup:studioAndroidPopupMenu">
+ <command
+ commandId="com.motorolamobility.studio.android.db.core.createDBManagementClasses"
+ disabledIcon="icons/action_content_provider.png"
+ hoverIcon="icons/action_content_provider.png"
+ icon="icons/action_content_provider.png"
+ label="%menu_create_db_management_classes"
+ style="push">
+ <visibleWhen
+ checkEnabled="false">
+ <and>
+ <count
+ value="1">
+ </count>
+ <iterate
+ ifEmpty="false"
+ operator="and">
+ <or>
+ <and>
+ <not>
+ <and>
+ <instanceof
+ value="org.eclipse.core.resources.IResource">
+ </instanceof>
+ <test
+ property="org.eclipse.core.resources.extension"
+ value="java">
+ </test>
+ </and>
+ </not>
+ <not>
+ <instanceof
+ value="org.eclipse.jdt.core.ICompilationUnit">
+ </instanceof>
+ </not>
+ <not>
+ <and>
+ <instanceof
+ value="org.eclipse.core.resources.IResource">
+ </instanceof>
+ <test
+ property="org.eclipse.core.resources.extension"
+ value="apk">
+ </test>
+ </and>
+ </not>
+ <and>
+ <adapt
+ type="org.eclipse.core.resources.IProject">
+ <and>
+ <test
+ forcePluginActivation="true"
+ property="org.eclipse.core.resources.projectNature"
+ value="com.android.ide.eclipse.adt.AndroidNature">
+ </test>
+ <test
+ property="org.eclipse.core.resources.open">
+ </test>
+ </and>
+ </adapt>
+ </and>
+ </and>
+ <and>
+ <instanceof
+ value="org.eclipse.core.resources.IFile">
+ </instanceof>
+ <test
+ property="org.eclipse.core.resources.extension"
+ value="db">
+ </test>
+ </and>
+ </or></iterate>
+ </and>
+ </visibleWhen>
+ </command>
+ <command
+ commandId="com.motorolamobility.studio.android.db.core.createDatabaseCommand"
+ disabledIcon="icons/action_create_database.png"
+ hoverIcon="icons/action_create_database.png"
+ icon="icons/action_create_database.png"
+ label="%create_database_command_label"
+ style="push">
+ <visibleWhen
+ checkEnabled="false">
+ <and>
+ <not>
+ <and>
+ <instanceof
+ value="org.eclipse.core.resources.IResource">
+ </instanceof>
+ <test
+ property="org.eclipse.core.resources.extension"
+ value="java">
+ </test>
+ </and>
+ </not>
+ <not>
+ <instanceof
+ value="org.eclipse.jdt.core.ICompilationUnit">
+ </instanceof>
+ </not>
+ <not>
+ <and>
+ <instanceof
+ value="org.eclipse.core.resources.IResource">
+ </instanceof>
+ <test
+ property="org.eclipse.core.resources.extension"
+ value="apk">
+ </test>
+ </and>
+ </not>
+ </and>
+ </visibleWhen>
+ </command>
+ </menuContribution>
+ </extension>
+ <extension
+ point="org.eclipse.ui.perspectives">
+ <perspective
+ class="com.motorolamobility.studio.android.db.core.DbPerspective"
+ fixed="false"
+ icon="icons/obj16/dbplate.gif"
+ id="com.motorola.studio.android.db.perspective"
+ name="%db_perspective">
+ </perspective>
+ </extension>
+ <extension
+ point="org.eclipse.ui.perspectiveExtensions">
+ <perspectiveExtension
+ targetID="com.motorola.studio.android.perspective">
+ <perspectiveShortcut
+ id="com.motorola.studio.android.db.perspective">
+ </perspectiveShortcut>
+ </perspectiveExtension>
+ <perspectiveExtension
+ targetID="org.eclipse.jdt.ui.JavaPerspective">
+ <perspectiveShortcut
+ id="com.motorola.studio.android.db.perspective">
+ </perspectiveShortcut>
+ </perspectiveExtension>
+ </extension>
+ <extension
+ point="org.eclipse.ui.handlers">
+ <handler
+ class="com.motorolamobility.studio.android.db.core.command.TableCreateHandler"
+ commandId="com.motorolamobility.studio.android.db.core.createTableCommand">
+ <activeWhen>
+ <with
+ variable="activeWorkbenchWindow">
+ <instanceof
+ value="org.eclipse.ui.IWorkbenchWindow">
+ </instanceof>
+ </with>
+ </activeWhen>
+ <enabledWhen>
+ <reference
+ definitionId="com.motorolamobility.studio.android.db.core.definitionIDbNodeSelected">
+ </reference>
+ </enabledWhen>
+ </handler>
+ <handler
+ class="com.motorolamobility.studio.android.db.core.command.BrowseTableContentsHandler"
+ commandId="com.motorolamobility.studio.android.db.core.browseTableContents">
+ <activeWhen>
+ <reference
+ definitionId="com.motorolamobility.studio.android.db.core.definitionIWorkbenchWindowActive">
+ </reference>
+ </activeWhen>
+ <enabledWhen>
+ <reference
+ definitionId="com.motorolamobility.studio.android.db.core.definitionITableNodeSelected">
+ </reference></enabledWhen>
+ </handler>
+ <handler
+ class="com.motorolamobility.studio.android.db.core.command.DbCreateHandler"
+ commandId="com.motorolamobility.studio.android.db.core.createDatabaseCommand">
+ <activeWhen>
+ <reference
+ definitionId="com.motorolamobility.studio.android.db.core.definitionIWorkbenchWindowActive">
+ </reference></activeWhen>
+ <enabledWhen>
+ <or>
+ <reference
+ definitionId="com.motorolamobility.studio.android.db.core.definitionIDbNodeCreatorSelected">
+ </reference>
+ <reference
+ definitionId="com.motorolamobility.studio.android.db.core.definitionIResourceSelected">
+ </reference>
+ </or></enabledWhen>
+ </handler>
+ </extension>
+ <extension
+ point="org.eclipse.core.expressions.definitions">
+ <definition
+ id="com.motorolamobility.studio.android.db.core.definitionIDbNodeSelected">
+ <with
+ variable="selection">
+ <count
+ value="1">
+ </count>
+ <iterate
+ ifEmpty="false">
+ <adapt
+ type="com.motorolamobility.studio.android.db.core.ui.IDbNode">
+ </adapt>
+ <test
+ forcePluginActivation="false"
+ property="com.motorolamobility.studio.android.db.core.databaseConnection"
+ value="com.motorolamobility.studio.android.db.core.databaseConnected">
+ </test>
+ </iterate>
+ </with>
+ </definition>
+ <definition
+ id="com.motorolamobility.studio.android.db.core.definitionIWorkbenchWindowActive">
+ <with
+ variable="activeWorkbenchWindow">
+ <instanceof
+ value="org.eclipse.ui.IWorkbenchWindow">
+ </instanceof>
+ </with>
+ </definition>
+ <definition
+ id="com.motorolamobility.studio.android.db.core.definitionITableNodeSelected">
+ <with
+ variable="selection">
+ <count
+ value="1">
+ </count>
+ <iterate
+ ifEmpty="false">
+ <adapt
+ type="com.motorolamobility.studio.android.db.core.ui.ITableNode">
+ </adapt>
+ </iterate>
+ </with>
+ </definition>
+ <definition
+ id="com.motorolamobility.studio.android.db.core.definitionIDbNodeCreatorSelected">
+ <with
+ variable="selection">
+ <count
+ value="1">
+ </count>
+ <iterate
+ ifEmpty="false">
+ <adapt
+ type="com.motorolamobility.studio.android.db.core.ui.action.IDbCreatorNode">
+ </adapt>
+ </iterate>
+ </with>
+ </definition>
+ <definition
+ id="com.motorolamobility.studio.android.db.core.definitionIResourceSelected">
+ <with
+ variable="selection">
+ <count
+ value="1">
+ </count>
+ <iterate
+ ifEmpty="false">
+ <adapt
+ type="org.eclipse.core.resources.IResource">
+ </adapt>
+ </iterate>
+ </with>
+ </definition>
+ </extension>
+ <extension
+ point="com.motorolamobility.studio.android.db.core.dbRootNode">
+ <rootNode
+ class="com.motorolamobility.studio.android.db.core.filesystem.FilesystemRootNode"
+ id="com.motorolamobility.studio.android.db.core.filesystemRoot"
+ name="%filesystemRootNodeName">
+ </rootNode>
+ </extension>
+ <extension
+ point="org.eclipse.ui.decorators">
+ <decorator
+ adaptable="false"
+ icon="icons/ovr16/error_ovr.png"
+ id="com.motorolamobility.studio.android.db.core.decorator.errorNode"
+ label="%decorator_error_node_label"
+ lightweight="true"
+ location="BOTTOM_RIGHT"
+ state="true">
+ <enablement>
+ <and>
+ <objectClass
+ name="com.motorolamobility.studio.android.db.core.ui.ITreeNode">
+ </objectClass>
+ <objectState
+ name="com.motorolamobility.studio.android.db.core.nodeStatus"
+ value="com.motorolamobility.studio.android.db.core.nodeStatusError">
+ </objectState>
+ </and>
+ </enablement>
+ </decorator>
+ </extension>
+ <extension
+ id="com.motorolamobility.studio.android.db.core.propertyTesters"
+ point="org.eclipse.core.expressions.propertyTesters">
+ <propertyTester
+ class="com.motorolamobility.studio.android.db.core.command.DbNodeTester"
+ id="com.motorolamobility.studio.android.db.core.propertyTester.IDbNode"
+ namespace="com.motorolamobility.studio.android.db.core"
+ properties="databaseConnection"
+ type="com.motorolamobility.studio.android.db.core.ui.IDbNode">
+ </propertyTester>
+ </extension>
+</plugin>
diff --git a/src/plugins/db.core/res/template.db b/src/plugins/db.core/res/template.db
new file mode 100644
index 0000000..f6778c7
--- /dev/null
+++ b/src/plugins/db.core/res/template.db
Binary files differ
diff --git a/src/plugins/db.core/schema/com.motorolamobility.studio.android.db.core.dbRootNode.exsd b/src/plugins/db.core/schema/com.motorolamobility.studio.android.db.core.dbRootNode.exsd
new file mode 100644
index 0000000..6dae503
--- /dev/null
+++ b/src/plugins/db.core/schema/com.motorolamobility.studio.android.db.core.dbRootNode.exsd
@@ -0,0 +1,134 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<!-- Schema file written by PDE -->
+<schema targetNamespace="com.motorolamobility.studio.android.db.core" xmlns="http://www.w3.org/2001/XMLSchema">
+<annotation>
+ <appinfo>
+ <meta.schema plugin="com.motorolamobility.studio.android.db.core" id="com.motorolamobility.studio.android.db.core.dbRootNode" name="Database Root Node"/>
+ </appinfo>
+ <documentation>
+ Contributions to root nodes of the tree on DB explorer view.
+ </documentation>
+ </annotation>
+
+ <element name="extension">
+ <annotation>
+ <appinfo>
+ <meta.element />
+ </appinfo>
+ </annotation>
+ <complexType>
+ <sequence>
+ <element ref="rootNode"/>
+ </sequence>
+ <attribute name="point" type="string" use="required">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ </annotation>
+ </attribute>
+ <attribute name="id" type="string">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ </annotation>
+ </attribute>
+ <attribute name="name" type="string">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ <appinfo>
+ <meta.attribute translatable="true"/>
+ </appinfo>
+ </annotation>
+ </attribute>
+ </complexType>
+ </element>
+
+ <element name="rootNode">
+ <complexType>
+ <attribute name="id" type="string" use="required">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ </annotation>
+ </attribute>
+ <attribute name="name" type="string" use="required">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ </annotation>
+ </attribute>
+ <attribute name="icon" type="string">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ <appinfo>
+ <meta.attribute kind="resource"/>
+ </appinfo>
+ </annotation>
+ </attribute>
+ <attribute name="class" type="string" use="required">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ <appinfo>
+ <meta.attribute kind="java" basedOn="com.motorolamobility.studio.android.db.core.ui.AbstractTreeNode:"/>
+ </appinfo>
+ </annotation>
+ </attribute>
+ </complexType>
+ </element>
+
+ <annotation>
+ <appinfo>
+ <meta.section type="since"/>
+ </appinfo>
+ <documentation>
+ 4.0.0
+ </documentation>
+ </annotation>
+
+ <annotation>
+ <appinfo>
+ <meta.section type="examples"/>
+ </appinfo>
+ <documentation>
+ [Enter extension point usage example here.]
+ </documentation>
+ </annotation>
+
+ <annotation>
+ <appinfo>
+ <meta.section type="apiinfo"/>
+ </appinfo>
+ <documentation>
+ [Enter API information here.]
+ </documentation>
+ </annotation>
+
+ <annotation>
+ <appinfo>
+ <meta.section type="implementation"/>
+ </appinfo>
+ <documentation>
+ [Enter information about supplied implementation of this extension point.]
+ </documentation>
+ </annotation>
+
+ <annotation>
+ <appinfo>
+ <meta.section type="copyright"/>
+ </appinfo>
+ <documentation>
+ Copyright (C) 2012 The Android Open Source Project
+ </documentation>
+ </annotation>
+
+</schema>
diff --git a/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/CanRefreshStatus.java b/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/CanRefreshStatus.java
new file mode 100644
index 0000000..d6a134a
--- /dev/null
+++ b/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/CanRefreshStatus.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.studio.android.db.core;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.MultiStatus;
+
+public class CanRefreshStatus extends MultiStatus
+{
+
+ /**
+ * Status type severity (bit mask, value 16) indicating that a user interaction will be needed.
+ * in this case a Yes/no dialog must be displayed using this status messages as dialog message.
+ * @see #getSeverity()
+ * @see #matches(int)
+ */
+ public static final int ASK_USER = 0x10;
+
+ /**
+ * Status type severity (bit mask, value 16) indicating that a user interaction will be needed.
+ * in this case a Yes/no/cancel dialog must be displayed using this status messages as dialog message.
+ * Yes/No response will be sent to refresh, cancel will cancel the refresh
+ * @see #getSeverity()
+ * @see #matches(int)
+ */
+ public static final int CANCELABLE = 0x20;
+
+ /**
+ * Builds a new {@link CanRefreshStatus} instance
+ * @param pluginId the plug-in id.
+ * @param severity the severity code.
+ * @param newChildren new Status's children. (can't be null)
+ * @param message The new status message.
+ * @param exception The {@link Throwable} that caused the error, if any. (Can be null).
+ */
+ public CanRefreshStatus(int severity, String pluginId, IStatus[] newChildren, String message,
+ Throwable exception)
+ {
+ super(pluginId, OK, newChildren, message, exception);
+ setSeverity(severity);
+ }
+
+ /**
+ * Builds a new {@link CanRefreshStatus} instance
+ * @param pluginId the plug-in id.
+ * @param severity the severity code.
+ * @param message The new status message.
+ * @param exception The {@link Throwable} that caused the error, if any. (Can be null).
+ */
+ public CanRefreshStatus(int severity, String pluginId, String message, Throwable exception)
+ {
+ super(pluginId, OK, message, exception);
+ setSeverity(severity);
+ }
+
+ /**
+ * Builds a new {@link CanRefreshStatus} instance
+ * @param pluginId the plug-in id.
+ * @param severity the severity code.
+ * @param newChildren new Status's children. (can't be null)
+ * @param message The new status message.
+ */
+ public CanRefreshStatus(int severity, String pluginId, IStatus[] newChildren, String message)
+ {
+ super(pluginId, OK, newChildren, message, null);
+ setSeverity(severity);
+ }
+
+ /**
+ * Builds a new {@link CanRefreshStatus} instance
+ * @param pluginId the plug-in id.
+ * @param severity the severity code.
+ * @param message The new status message.
+ */
+ public CanRefreshStatus(int severity, String pluginId, String message)
+ {
+ super(pluginId, OK, message, null);
+ setSeverity(severity);
+ }
+
+ /**
+ * Sets the severity.
+ *
+ * @param severity the severity; one of <code>OK</code>, <code>ERROR</code>,
+ * <code>INFO</code>, <code>WARNING</code>, or <code>CANCEL</code>
+ */
+ @Override
+ protected void setSeverity(int severity)
+ {
+ Assert.isLegal((severity == OK) || (severity == ERROR) || (severity == WARNING)
+ || (severity == INFO) || (severity == CANCEL) || (severity == ASK_USER)
+ || (severity == (ASK_USER | CANCELABLE)));
+
+ if ((severity & ASK_USER) != 0)
+ {
+ super.setSeverity(WARNING);
+ }
+
+ setCode(severity);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.runtime.Status#matches(int)
+ */
+ @Override
+ public boolean matches(int severityMask)
+ {
+ if ((severityMask & ASK_USER) != 0)
+ {
+ return (getCode() & severityMask) == severityMask;
+ }
+ return super.matches(severityMask);
+ }
+
+}
diff --git a/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/DbCoreActivator.java b/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/DbCoreActivator.java
new file mode 100644
index 0000000..6251372
--- /dev/null
+++ b/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/DbCoreActivator.java
@@ -0,0 +1,229 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.studio.android.db.core;
+
+import static com.motorola.studio.android.common.log.StudioLogger.debug;
+import static com.motorola.studio.android.common.log.StudioLogger.error;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.core.runtime.FileLocator;
+import org.eclipse.core.runtime.IConfigurationElement;
+import org.eclipse.core.runtime.IExtension;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.ui.IViewPart;
+import org.eclipse.ui.plugin.AbstractUIPlugin;
+import org.osgi.framework.BundleContext;
+
+import com.motorola.studio.android.common.CommonPlugin;
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.common.utilities.EclipseUtils;
+import com.motorola.studio.android.common.utilities.FileUtil;
+import com.motorolamobility.studio.android.db.core.model.DbModel;
+import com.motorolamobility.studio.android.db.core.ui.view.MOTODEVDatabaseExplorerView;
+
+/**
+ * The activator class controls the plug-in life cycle
+ */
+public class DbCoreActivator extends AbstractUIPlugin
+{
+
+ private static final String DB_PROPERTY_TESTER_ATT_PROPERTIES = "properties";
+
+ private static final String DB_PROPERTY_TESTER_ATT_NAMESPACE = "namespace";
+
+ private static final String DB_PROPERTY_TESTER_EXTENSION_ID =
+ "com.motorolamobility.studio.android.db.core.propertyTesters";
+
+ // The plug-in ID
+ public static final String PLUGIN_ID = "com.motorolamobility.studio.android.db.core"; //$NON-NLS-1$
+
+ private static final String DB_TEMPLATE_PATH = "res/template.db"; //$NON-NLS-1$
+
+ private static List<String> pluginProperties = null;
+
+ // The shared instance
+ private static DbCoreActivator plugin;
+
+ public static final String DATATOOLS_UI_PLUGIN_ID =
+ "org.eclipse.datatools.connectivity.sqm.core.ui"; //$NON-NLS-1$
+
+ public static final String TABLE_ICON = "icons/table.gif"; //$NON-NLS-1$
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.ui.plugin.AbstractUIPlugin#start(org.osgi.framework.BundleContext)
+ */
+ @Override
+ public void start(BundleContext context) throws Exception
+ {
+ StudioLogger.debug(DbCoreActivator.class,
+ "Starting MOTODEV Studio for Android Database Core Plugin...");
+
+ super.start(context);
+ plugin = this;
+
+ DbModel.assertDriverExistsAtModel();
+ DbModel.cleanPreviousProfiles();
+
+ StudioLogger.debug(DbCoreActivator.class,
+ "MOTODEV Studio for Android Database Core Plugin started.");
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.ui.plugin.AbstractUIPlugin#stop(org.osgi.framework.BundleContext)
+ */
+ @Override
+ public void stop(BundleContext context) throws Exception
+ {
+ plugin = null;
+ DbModel.deleteDriverFromModel();
+ super.stop(context);
+ }
+
+ /**
+ * Returns the shared instance
+ *
+ * @return the shared instance
+ */
+ public static DbCoreActivator getDefault()
+ {
+ return plugin;
+ }
+
+ /*
+ * Returns a File object representing a path inside this plug-in
+ */
+ private File getResourceFile(String pathAtPlugin)
+ {
+ URL location = this.getBundle().getEntry(pathAtPlugin);
+
+ File file = null;
+ try
+ {
+ IPath p = new Path(FileLocator.toFileURL(location).getFile());
+ file = p.toFile();
+ }
+ catch (IOException e)
+ {
+ error(NLS.bind("Could not get resource file {0}", pathAtPlugin)); //$NON-NLS-1$
+ }
+
+ return file;
+
+ }
+
+ /**
+ * Retrieves the location of the SQLITE3 jdbc driver
+ * @return
+ */
+ public String getDriverPath()
+ {
+ return CommonPlugin.getDefault().getDriverPath();
+ }
+
+ /*
+ * Retrieves the templateDbFile
+ */
+ private File getTemplateDbFile()
+ {
+ return getResourceFile(DB_TEMPLATE_PATH);
+ }
+
+ /**
+ * Copy the template db file to a given target file
+ * @param target
+ * @throws IOException If file path already exists or if the copy operation failed for some reason.
+ */
+ public void copyTemplateDbFile(File target, boolean overwrite) throws IOException
+ {
+ debug(NLS.bind("Attempting to copy db template file to {0}", target.getAbsolutePath())); //$NON-NLS-1$
+ File templateDbFile = getTemplateDbFile();
+ if (overwrite && target.exists())
+ {
+ target.delete();
+ }
+
+ if (overwrite || !target.exists())
+ {
+ debug(NLS.bind("Creating parent folders for file: {0}", target.getAbsolutePath())); //$NON-NLS-1$
+ target.getParentFile().mkdirs(); //Create parent folder if needed.
+ FileUtil.copyFile(templateDbFile, target);
+ }
+ else
+ {
+ throw new IOException("Target file already exists"); //$NON-NLS-1$
+ }
+ debug(NLS.bind("DB template file succesfully copyed to {0}", target.getAbsolutePath())); //$NON-NLS-1$
+ }
+
+ /**
+ * Retrieves the {@link MOTODEVDatabaseExplorerView} instance if it's active.
+ * @return the active instance of the {@link MOTODEVDatabaseExplorerView}, or null if there's no active db view.
+ */
+ public static MOTODEVDatabaseExplorerView getMOTODEVDatabaseExplorerView()
+ {
+ IViewPart view = EclipseUtils.getActiveView(MOTODEVDatabaseExplorerView.VIEW_ID);
+
+ if (view instanceof MOTODEVDatabaseExplorerView)
+ {
+ return (MOTODEVDatabaseExplorerView) view;
+ }
+
+ return null;
+ }
+
+ /**
+ * @return all namespaced properties defined by org.eclipse.core.expressions.propertyTesters extensions in this plugin.xml file.
+ *
+ * */
+ public static List<String> getPluginProperties()
+ {
+ //only load properties from extension registry once
+ if (pluginProperties == null)
+ {
+ pluginProperties = new ArrayList<String>();
+
+ IExtension propertyTesters =
+ Platform.getExtensionRegistry().getExtension(DB_PROPERTY_TESTER_EXTENSION_ID);
+
+ IConfigurationElement[] confElements = propertyTesters.getConfigurationElements();
+
+ for (IConfigurationElement confElement : confElements)
+ {
+ String namespace = confElement.getAttribute(DB_PROPERTY_TESTER_ATT_NAMESPACE);
+ String property = confElement.getAttribute(DB_PROPERTY_TESTER_ATT_PROPERTIES);
+
+ String[] properties = property.split(",");
+ for (String prop : properties)
+ {
+ String namespacedProperty = namespace.concat(".").concat(prop);
+ pluginProperties.add(namespacedProperty);
+ }
+ }
+ }
+
+ return pluginProperties;
+ }
+}
diff --git a/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/DbPerspective.java b/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/DbPerspective.java
new file mode 100644
index 0000000..a78adcb
--- /dev/null
+++ b/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/DbPerspective.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.studio.android.db.core;
+
+import org.eclipse.ui.IFolderLayout;
+import org.eclipse.ui.IPageLayout;
+import org.eclipse.ui.IPerspectiveFactory;
+import org.eclipse.ui.IPlaceholderFolderLayout;
+
+/**
+ * The MOTODEV Database Perspective factory
+ */
+public class DbPerspective implements IPerspectiveFactory
+{
+ private static String VIEW_SQL_RESULTS = "org.eclipse.datatools.sqltools.result.resultView"; //$NON-NLS-1$
+
+ private static String VIEW_TML_DEV_MGT =
+ "org.eclipse.sequoyah.device.framework.ui.InstanceMgtView"; //$NON-NLS-1$
+
+ public static String VIEW_MOTODEV_DATABASE = "com.motorola.studio.android.db.databaseView"; //$NON-NLS-1$
+
+ private static String VIEW_FILE_EXPLORER =
+ "com.android.ide.eclipse.ddms.views.FileExplorerView"; //$NON-NLS-1$
+
+ private static String VIEW_CONSOLE = "org.eclipse.ui.console.ConsoleView"; //$NON-NLS-1$
+
+ private static String ACTIONSET_LAUNCH = "org.eclipse.debug.ui.launchActionSet"; //$NON-NLS-1$
+
+ private static String ACTIONSET_NAVIGATE = "org.eclipse.ui.NavigateActionSet"; //$NON-NLS-1$
+
+ private static String PERSPECTIVE_MOTODEV = "com.motorola.studio.android.perspective"; //$NON-NLS-1$
+
+ private static String VIEW_SNIPPETS =
+ "org.eclipse.wst.common.snippets.internal.ui.SnippetsView"; //$NON-NLS-1$
+
+ private static String VIEW_ANDROID_EMULATOR =
+ "com.motorola.studio.android.emulator.androidView"; //$NON-NLS-1$
+
+ /**
+ * Creates the initial layout for a page.
+ *
+ * @param layout the page layout
+ *
+ * @see IPerspectiveFactory#createInitialLayout(IPageLayout)
+ */
+ public void createInitialLayout(IPageLayout layout)
+ {
+ String editorArea = layout.getEditorArea();
+ layout.setEditorAreaVisible(true);
+
+ IFolderLayout left = layout.createFolder("left", IPageLayout.LEFT, 0.25f, editorArea); //$NON-NLS-1$
+ left.addView(VIEW_MOTODEV_DATABASE);
+
+ IFolderLayout leftBottom =
+ layout.createFolder("leftBottom", IPageLayout.BOTTOM, 0.59f, "left"); //$NON-NLS-1$ //$NON-NLS-2$
+ leftBottom.addView(VIEW_SNIPPETS);
+
+ IFolderLayout bottom = layout.createFolder("bottom", IPageLayout.BOTTOM, 0.7f, editorArea); //$NON-NLS-1$
+ bottom.addView(VIEW_SQL_RESULTS);
+ bottom.addView(VIEW_TML_DEV_MGT);
+ bottom.addView(VIEW_CONSOLE);
+
+ IPlaceholderFolderLayout right =
+ layout.createPlaceholderFolder("right", IPageLayout.RIGHT, 0.7f, editorArea); //$NON-NLS-1$
+ right.addPlaceholder(VIEW_FILE_EXPLORER);
+ right.addPlaceholder(VIEW_ANDROID_EMULATOR);
+
+ layout.addActionSet(ACTIONSET_LAUNCH);
+ layout.addActionSet(ACTIONSET_NAVIGATE);
+
+ layout.addShowViewShortcut(VIEW_MOTODEV_DATABASE);
+ layout.addShowViewShortcut(VIEW_SQL_RESULTS);
+ layout.addShowViewShortcut(VIEW_TML_DEV_MGT);
+ layout.addShowViewShortcut(VIEW_FILE_EXPLORER);
+ layout.addShowViewShortcut(VIEW_CONSOLE);
+
+ layout.addPerspectiveShortcut(PERSPECTIVE_MOTODEV);
+ }
+}
diff --git a/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/DbRootNodeReader.java b/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/DbRootNodeReader.java
new file mode 100644
index 0000000..aed0154
--- /dev/null
+++ b/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/DbRootNodeReader.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.studio.android.db.core;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IConfigurationElement;
+import org.eclipse.core.runtime.IExtension;
+import org.eclipse.core.runtime.IExtensionPoint;
+import org.eclipse.core.runtime.IExtensionRegistry;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.ui.PartInitException;
+
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorolamobility.studio.android.db.core.ui.AbstractTreeNode;
+
+public abstract class DbRootNodeReader
+{
+ public static final String DB_ROOT_NODE_EXTENSION_POINT_ID =
+ "com.motorolamobility.studio.android.db.core.dbRootNode"; //$NON-NLS-1$
+
+ public static final String DB_ROOT_NODE_EXTENSION_POINT_ATTRIBUTE_ID = "id"; //$NON-NLS-1$
+
+ public static final String DB_ROOT_NODE_EXTENSION_POINT_ATTRIBUTE_NAME = "name"; //$NON-NLS-1$
+
+ public static final String DB_ROOT_NODE_EXTENSION_POINT_ATTRIBUTE_ICON = "icon"; //$NON-NLS-1$
+
+ public static final String DB_ROOT_NODE_EXTENSION_POINT_ATTRIBUTE_CLASS = "class"; //$NON-NLS-1$
+
+ /**
+ * Load existing root nodes, mapped by extension id
+ *
+ * @param treeNodeList The map of root nodes to be populated
+ *
+ */
+ public static void loadRootNode(HashMap<String, AbstractTreeNode> treeNodeList)
+ throws PartInitException
+
+ {
+ List<String> failedNodeNames = new ArrayList<String>();
+ treeNodeList.clear();
+
+ IExtensionRegistry extReg = Platform.getExtensionRegistry();
+ IExtensionPoint extPoint = extReg.getExtensionPoint(DB_ROOT_NODE_EXTENSION_POINT_ID);
+
+ String id = null;
+ String name = null;
+ if (extPoint != null)
+ {
+
+ IExtension[] extensions = extPoint.getExtensions();
+
+ for (IExtension aExtension : extensions)
+ {
+ IConfigurationElement[] configElements = aExtension.getConfigurationElements();
+ for (IConfigurationElement aConfig : configElements)
+ {
+
+ id = aConfig.getAttribute(DB_ROOT_NODE_EXTENSION_POINT_ATTRIBUTE_ID);
+ name = aConfig.getAttribute(DB_ROOT_NODE_EXTENSION_POINT_ATTRIBUTE_NAME);
+
+ String nodeClassName =
+ aConfig.getAttribute(DB_ROOT_NODE_EXTENSION_POINT_ATTRIBUTE_CLASS);
+
+ AbstractTreeNode treeNode = null;
+ if (nodeClassName != null)
+ {
+
+ try
+ {
+ treeNode =
+ (AbstractTreeNode) aConfig
+ .createExecutableExtension(DB_ROOT_NODE_EXTENSION_POINT_ATTRIBUTE_CLASS);
+ treeNode.setId(id);
+ treeNode.setName(name);
+ if (aConfig.getAttribute(DB_ROOT_NODE_EXTENSION_POINT_ATTRIBUTE_ICON) != null)
+ {
+ ImageDescriptor icon =
+ DbCoreActivator
+ .imageDescriptorFromPlugin(
+ DbCoreActivator.PLUGIN_ID,
+ aConfig.getAttribute(DB_ROOT_NODE_EXTENSION_POINT_ATTRIBUTE_ICON));
+ treeNode.setIcon(icon);
+ }
+
+ treeNodeList.put(id, treeNode);
+ }
+ catch (CoreException e)
+ {
+ StudioLogger
+ .error(DbRootNodeReader.class,
+ "Unexpected error with the root node extension point. Name:" + name + " ID:" + id, e); //$NON-NLS-1$ //$NON-NLS-2$
+ failedNodeNames.add(name);
+ }
+ }
+ }
+ }
+
+ if (!failedNodeNames.isEmpty())
+ {
+ throw new PartInitException("The following nodes could not be loaded : " //$NON-NLS-1$
+ + failedNodeNames);
+ }
+ }
+ }
+}
diff --git a/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/command/BrowseTableContentsHandler.java b/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/command/BrowseTableContentsHandler.java
new file mode 100644
index 0000000..3cdec81
--- /dev/null
+++ b/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/command/BrowseTableContentsHandler.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.studio.android.db.core.command;
+
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.core.commands.IHandler;
+
+import com.motorolamobility.studio.android.db.core.DbCoreActivator;
+import com.motorolamobility.studio.android.db.core.ui.ITableNode;
+import com.motorolamobility.studio.android.db.core.ui.ITreeNode;
+import com.motorolamobility.studio.android.db.core.ui.TableNode;
+
+/**
+ * This class implements the command to browse a table content using an instance of {@link TableNode} object.
+ */
+public class BrowseTableContentsHandler extends AbstractHandler implements IHandler
+{
+
+ private ITableNode tableNode = null;
+
+ public BrowseTableContentsHandler()
+ {
+ }
+
+ public BrowseTableContentsHandler(ITableNode tableNode)
+ {
+ this.tableNode = tableNode;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.commands.IHandler#execute(org.eclipse.core.commands.ExecutionEvent)
+ */
+ public Object execute(ExecutionEvent event) throws ExecutionException
+ {
+
+ if (tableNode == null)
+ {
+ tableNode = getSelectedItem();
+ }
+
+ if (tableNode != null)
+ {
+ tableNode.browseTableContents();
+ tableNode = null; //clear selected node to force getSelectedItem to be called when calling via toolbar
+ }
+
+ return null;
+ }
+
+ private ITableNode getSelectedItem()
+ {
+ ITableNode selectedNode = null;
+ ITreeNode selectedItem =
+ DbCoreActivator.getMOTODEVDatabaseExplorerView().getSelectedItemOnTree();
+
+ if (selectedItem instanceof ITableNode)
+ {
+ selectedNode = (ITableNode) selectedItem;
+ }
+
+ return selectedNode;
+ }
+
+}
diff --git a/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/command/CollapseAllHandler.java b/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/command/CollapseAllHandler.java
new file mode 100644
index 0000000..0006222
--- /dev/null
+++ b/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/command/CollapseAllHandler.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.studio.android.db.core.command;
+
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+
+import com.motorolamobility.studio.android.db.core.DbCoreActivator;
+
+public class CollapseAllHandler extends AbstractHandler
+{
+
+ public Object execute(ExecutionEvent event) throws ExecutionException
+ {
+ DbCoreActivator.getMOTODEVDatabaseExplorerView().collapseAllTreeItems();
+ return null;
+ }
+
+}
diff --git a/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/command/CreateDatabaseManagementClassesHandler.java b/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/command/CreateDatabaseManagementClassesHandler.java
new file mode 100644
index 0000000..4d766b8
--- /dev/null
+++ b/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/command/CreateDatabaseManagementClassesHandler.java
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.studio.android.db.core.command;
+
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.core.commands.IHandler;
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.IAdaptable;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.StructuredSelection;
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.PlatformUI;
+
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorolamobility.studio.android.db.core.DbCoreActivator;
+import com.motorolamobility.studio.android.db.core.exception.MotodevDbException;
+import com.motorolamobility.studio.android.db.core.project.ProjectNode;
+import com.motorolamobility.studio.android.db.core.ui.DbNode;
+import com.motorolamobility.studio.android.db.core.ui.IDbNode;
+import com.motorolamobility.studio.android.db.core.ui.ITreeNode;
+import com.motorolamobility.studio.android.db.core.ui.action.CreateDatabaseManagementClassesAction;
+import com.motorolamobility.studio.android.db.core.ui.view.MOTODEVDatabaseExplorerView;
+
+/**
+ * This class implements the command to create database management classes using an instance of {@link IDbNode} object.
+ */
+public class CreateDatabaseManagementClassesHandler extends AbstractHandler implements IHandler
+{
+
+ private IDbNode selectedDbNode = null;
+
+ private ProjectNode selectedProjectNode;
+
+ private void setSelectedNode()
+ {
+
+ this.selectedDbNode = null;
+
+ this.selectedProjectNode = null;
+
+ MOTODEVDatabaseExplorerView view = DbCoreActivator.getMOTODEVDatabaseExplorerView();
+
+ if (view != null)
+ {
+ // if the Database view is active, looks for the db and project nodes
+ ITreeNode items = view.getSelectedItemOnTree();
+
+ if (items != null)
+ {
+
+ if (items instanceof IDbNode)
+ {
+
+ this.selectedDbNode = (IDbNode) items;
+
+ }
+ else if (items instanceof ProjectNode)
+ {
+ this.selectedProjectNode = (ProjectNode) items;
+ }
+ }
+ }
+ else
+ {
+ // looks for the project and db resources at the package explorer view
+ IWorkbench workbench = PlatformUI.getWorkbench();
+
+ if ((workbench != null) && !workbench.isClosing())
+ {
+
+ IWorkbenchWindow window = workbench.getActiveWorkbenchWindow();
+
+ if (window != null)
+ {
+ ISelection selection = window.getSelectionService().getSelection();
+ IStructuredSelection structureSelection = null;
+ if (selection instanceof IStructuredSelection)
+ {
+ structureSelection = (IStructuredSelection) selection;
+ }
+ else
+ {
+ structureSelection = new StructuredSelection();
+ }
+
+ Object selectionElement = structureSelection.getFirstElement();
+
+ if (selectionElement != null)
+ {
+
+ //the wizard was requested from a click on .db file or project
+ IResource resource = null;
+ // in case the item as a resource, retrieve it
+ if (selectionElement instanceof IResource)
+ {
+ resource = (IResource) selectionElement;
+ }
+
+ else if (selectionElement instanceof IAdaptable)
+ {
+ try
+ {
+ resource =
+ (IResource) ((IAdaptable) selectionElement)
+ .getAdapter(IResource.class);
+ }
+ catch (Exception e)
+ {
+ StudioLogger.error(CreateDatabaseManagementClassesAction.class,
+ e.getMessage());
+ }
+ }
+
+ if (resource != null)
+ {
+ this.selectedProjectNode = new ProjectNode(resource.getProject(), null);
+
+ if (resource instanceof IFile)
+ {
+ try
+ {
+ this.selectedDbNode =
+ new DbNode(new Path(resource.getLocation().toFile()
+ .getAbsolutePath()), this.selectedProjectNode);
+ }
+ catch (MotodevDbException e)
+ {
+ StudioLogger.error(CreateDatabaseManagementClassesAction.class,
+ e.getMessage());
+ }
+ }
+ }
+ }
+
+ }
+ }
+ }
+ }
+
+ /**
+ * Constructor when there is NOT a node selected on {@link MOTODEVDatabaseExplorerView}
+ */
+ public CreateDatabaseManagementClassesHandler()
+ {
+ setSelectedNode();
+ }
+
+ /**
+ * Constructor when there is a node selected on {@link MOTODEVDatabaseExplorerView}
+ * @param node
+ */
+ public CreateDatabaseManagementClassesHandler(IDbNode node)
+ {
+ this.selectedDbNode = node;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.commands.IHandler#execute(org.eclipse.core.commands.ExecutionEvent)
+ */
+ public Object execute(ExecutionEvent event) throws ExecutionException
+ {
+ CreateDatabaseManagementClassesAction action = new CreateDatabaseManagementClassesAction();
+ setSelectedNode();
+ action.setDbNodeSelected(selectedDbNode);
+
+ action.setProjectNodeSelected(selectedProjectNode);
+
+ action.run();
+
+ return null;
+ }
+
+}
diff --git a/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/command/DbConnectHandler.java b/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/command/DbConnectHandler.java
new file mode 100644
index 0000000..0892cca
--- /dev/null
+++ b/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/command/DbConnectHandler.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.motorolamobility.studio.android.db.core.command;
+
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.core.commands.IHandler;
+
+import com.motorolamobility.studio.android.db.core.event.DatabaseModelEvent.EVENT_TYPE;
+import com.motorolamobility.studio.android.db.core.event.DatabaseModelEventManager;
+import com.motorolamobility.studio.android.db.core.ui.IDbNode;
+
+/**
+ * This class implements the command to connect to a database using an IDbNode object.
+ * */
+public class DbConnectHandler extends AbstractHandler implements IHandler
+{
+
+ private IDbNode dbNode;
+
+ public DbConnectHandler()
+ {
+
+ }
+
+ public DbConnectHandler(IDbNode dbNode)
+ {
+ this.dbNode = dbNode;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.core.commands.IHandler#execute(ExecutionEvent)
+ * */
+ public Object execute(ExecutionEvent event) throws ExecutionException
+ {
+ DatabaseModelEventManager.getInstance().fireEvent(dbNode, EVENT_TYPE.EXPAND);
+ return null;
+ }
+}
diff --git a/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/command/DbCreateHandler.java b/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/command/DbCreateHandler.java
new file mode 100644
index 0000000..66810fd
--- /dev/null
+++ b/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/command/DbCreateHandler.java
@@ -0,0 +1,226 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.studio.android.db.core.command;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.core.commands.IHandler;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.IAdaptable;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.StructuredSelection;
+import org.eclipse.jface.wizard.WizardDialog;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.IWorkbenchPart;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.PlatformUI;
+
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.common.utilities.EclipseUtils;
+import com.motorolamobility.studio.android.db.core.DbCoreActivator;
+import com.motorolamobility.studio.android.db.core.i18n.DbCoreNLS;
+import com.motorolamobility.studio.android.db.core.model.TableModel;
+import com.motorolamobility.studio.android.db.core.project.ProjectNode;
+import com.motorolamobility.studio.android.db.core.ui.ITreeNode;
+import com.motorolamobility.studio.android.db.core.ui.action.IDbCreatorNode;
+import com.motorolamobility.studio.android.db.core.ui.view.MOTODEVDatabaseExplorerView;
+import com.motorolamobility.studio.android.db.core.ui.wizards.createdb.CreateDatabaseWizard;
+
+public class DbCreateHandler extends AbstractHandler implements IHandler
+{
+ private IDbCreatorNode dbCreatorNode;
+
+ /**
+ * @param dbCreatorNode
+ */
+ public DbCreateHandler()
+ {
+ setDbCreatorNode();
+ }
+
+ /**
+ * @param dbCreatorNode
+ */
+ public DbCreateHandler(IDbCreatorNode dbCreatorNode)
+ {
+ this.dbCreatorNode = dbCreatorNode;
+ }
+
+ private void setDbCreatorNode()
+ {
+
+ this.dbCreatorNode = null;
+
+ IWorkbenchPart activePart =
+ PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage()
+ .getActivePart();
+
+ MOTODEVDatabaseExplorerView view = DbCoreActivator.getMOTODEVDatabaseExplorerView();
+
+ if ((view != null) && activePart.equals(view))
+ {
+ ITreeNode items = view.getSelectedItemOnTree();
+
+ if (items != null)
+ {
+
+ if (items instanceof IDbCreatorNode)
+ {
+
+ this.dbCreatorNode = (IDbCreatorNode) items;
+
+ ITreeNode node = view.getSelectedItemOnTree();
+
+ if ((node != null))
+ {
+ try
+ {
+ IDbCreatorNode dbCreatorNode = (IDbCreatorNode) node;
+
+ if (node instanceof IDbCreatorNode)
+ {
+ this.dbCreatorNode = dbCreatorNode;
+ }
+ }
+ catch (Exception e)
+ {
+ StudioLogger.info(DbCreateHandler.class, e.getMessage());
+ }
+
+ }
+ }
+ }
+ }
+ else
+ {
+ // looks for the project and db resources at the package explorer view
+ IWorkbench workbench = PlatformUI.getWorkbench();
+
+ if ((workbench != null) && !workbench.isClosing())
+ {
+
+ IWorkbenchWindow window = workbench.getActiveWorkbenchWindow();
+
+ if (window != null)
+ {
+ ISelection selection = window.getSelectionService().getSelection();
+ IStructuredSelection structureSelection = null;
+ if (selection instanceof IStructuredSelection)
+ {
+ structureSelection = (IStructuredSelection) selection;
+ }
+ else
+ {
+ structureSelection = new StructuredSelection();
+ }
+
+ Object selectionElement = structureSelection.getFirstElement();
+
+ if (selectionElement != null)
+ {
+
+ //the wizard was requested from a click on .db file or project
+ IResource resource = null;
+ // in case the item as a resource, retrieve it
+ if (selectionElement instanceof IResource)
+ {
+ resource = (IResource) selectionElement;
+ }
+
+ else if (selectionElement instanceof IAdaptable)
+ {
+ try
+ {
+ resource =
+ (IResource) ((IAdaptable) selectionElement)
+ .getAdapter(IResource.class);
+ }
+ catch (Exception e)
+ {
+ StudioLogger.error(DbCreateHandler.class, e.getMessage());
+ }
+ }
+
+ if (resource != null)
+ {
+ this.dbCreatorNode = new ProjectNode(resource.getProject(), null);
+ }
+ }
+
+ }
+ }
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.commands.IHandler#execute(org.eclipse.core.commands.ExecutionEvent)
+ */
+ public Object execute(ExecutionEvent event) throws ExecutionException
+ {
+ setDbCreatorNode();
+
+ if (this.dbCreatorNode != null)
+ {
+ List<ITreeNode> children = dbCreatorNode.getChildren();
+ List<String> alreadyAvailableDbs;
+ alreadyAvailableDbs = new ArrayList<String>(children.size());
+ for (ITreeNode child : children)
+ {
+ alreadyAvailableDbs.add(child.getName());
+ }
+
+ Shell shell = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell();
+ CreateDatabaseWizard createDbWizard = new CreateDatabaseWizard(alreadyAvailableDbs);
+ WizardDialog dialog = new WizardDialog(shell, createDbWizard);
+ dialog.create();
+ dialog.open();
+
+ String dbName = createDbWizard.getDbName();
+ List<TableModel> tables = createDbWizard.getTables();
+ IStatus status = null;
+ try
+ {
+ status = dbCreatorNode.createDb(dbName, tables);
+ if (dbCreatorNode instanceof ProjectNode)
+ {
+ ((ProjectNode) dbCreatorNode).refreshAssetsFolder();
+ }
+ }
+ catch (Exception e)
+ {
+ StudioLogger.error(DbCreateHandler.class, e.getMessage());
+ }
+ if ((status != null) && (!status.isOK()))
+ {
+ EclipseUtils.showErrorDialog(
+ DbCoreNLS.UI_CreateDatabaseWizardPage_CreateDatabase_Error, NLS.bind(
+ DbCoreNLS.UI_CreateDatabaseWizardPage_CreateDatabase_Error_New,
+ dbName), status);
+ }
+
+ }
+ return null;
+ }
+
+}
diff --git a/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/command/DbDisconnectHandler.java b/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/command/DbDisconnectHandler.java
new file mode 100644
index 0000000..6614849
--- /dev/null
+++ b/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/command/DbDisconnectHandler.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.motorolamobility.studio.android.db.core.command;
+
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.core.commands.IHandler;
+
+import com.motorolamobility.studio.android.db.core.ui.IDbNode;
+
+/**
+ * This class implements the command to disconnect from a database using an IDbNode object.
+ * */
+public class DbDisconnectHandler extends AbstractHandler implements IHandler
+{
+
+ private IDbNode dbNode;
+
+ public DbDisconnectHandler()
+ {
+ }
+
+ public DbDisconnectHandler(IDbNode dbNode)
+ {
+ this.dbNode = dbNode;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.core.commands.IHandler#execute(ExecutionEvent)
+ * */
+ public Object execute(ExecutionEvent event) throws ExecutionException
+ {
+ return dbNode.disconnect();
+ }
+}
diff --git a/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/command/DbNodeTester.java b/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/command/DbNodeTester.java
new file mode 100644
index 0000000..50b1237
--- /dev/null
+++ b/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/command/DbNodeTester.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.motorolamobility.studio.android.db.core.command;
+
+import org.eclipse.core.expressions.PropertyTester;
+
+import com.motorolamobility.studio.android.db.core.ui.IDbNode;
+
+public class DbNodeTester extends PropertyTester
+{
+
+ public boolean test(Object receiver, String property, Object[] args, Object expectedValue)
+ {
+ boolean result = false;
+ if (receiver instanceof IDbNode)
+ {
+ IDbNode dbNode = (IDbNode) receiver;
+ result = dbNode.testAttribute(dbNode, property, expectedValue.toString());
+ }
+
+ return result;
+ }
+}
diff --git a/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/command/DeleteDatabaseHandler.java b/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/command/DeleteDatabaseHandler.java
new file mode 100644
index 0000000..c87e178
--- /dev/null
+++ b/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/command/DeleteDatabaseHandler.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.studio.android.db.core.command;
+
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.core.commands.IHandler;
+import org.eclipse.core.runtime.IStatus;
+
+import com.motorola.studio.android.common.utilities.EclipseUtils;
+import com.motorolamobility.studio.android.db.core.i18n.DbCoreNLS;
+import com.motorolamobility.studio.android.db.core.ui.IDbNode;
+import com.motorolamobility.studio.android.db.core.ui.ITreeNode;
+import com.motorolamobility.studio.android.db.core.ui.action.IDbCreatorNode;
+
+public class DeleteDatabaseHandler extends AbstractHandler implements IHandler
+{
+
+ private ITreeNode node;
+
+ public DeleteDatabaseHandler()
+ {
+ }
+
+ public DeleteDatabaseHandler(ITreeNode node)
+ {
+ this.node = node;
+ }
+
+ public Object execute(ExecutionEvent event) throws ExecutionException
+ {
+
+ if (node instanceof IDbNode)
+ {
+ IDbCreatorNode dbCreatorNode = (IDbCreatorNode) node.getParent();
+
+ boolean shouldProceed =
+ EclipseUtils
+ .showQuestionDialog(
+ DbCoreNLS.DeleteDatabaseHandler_ConfirmationQuestionDialog_Title,
+ DbCoreNLS
+ .bind(DbCoreNLS.DeleteDatabaseHandler_ConfirmationQuestionDialog_Description,
+ node.getName()));
+
+ if (shouldProceed)
+ {
+ IStatus status = dbCreatorNode.deleteDb((IDbNode) node);
+ if ((status != null) && !status.isOK())
+ {
+ EclipseUtils.showErrorDialog(
+ DbCoreNLS.DeleteDatabaseHandler_CouldNotDeleteDatabase,
+ status.getMessage());
+ }
+ }
+
+ }
+
+ return null;
+ }
+}
diff --git a/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/command/DeleteTableHandler.java b/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/command/DeleteTableHandler.java
new file mode 100644
index 0000000..b99d0e6
--- /dev/null
+++ b/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/command/DeleteTableHandler.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.studio.android.db.core.command;
+
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.core.commands.IHandler;
+import org.eclipse.core.runtime.IStatus;
+
+import com.motorola.studio.android.common.utilities.EclipseUtils;
+import com.motorolamobility.studio.android.db.core.i18n.DbCoreNLS;
+import com.motorolamobility.studio.android.db.core.ui.IDbNode;
+import com.motorolamobility.studio.android.db.core.ui.ITableNode;
+import com.motorolamobility.studio.android.db.core.ui.ITreeNode;
+
+public class DeleteTableHandler extends AbstractHandler implements IHandler
+{
+
+ private ITreeNode node;
+
+ public DeleteTableHandler()
+ {
+ }
+
+ public DeleteTableHandler(ITreeNode node)
+ {
+ this.node = node;
+ }
+
+ public Object execute(ExecutionEvent event) throws ExecutionException
+ {
+ if (node instanceof ITableNode)
+ {
+ ITableNode tableNode = (ITableNode) node;
+ IDbNode dbNode = (IDbNode) tableNode.getParent();
+
+ boolean shouldProceed =
+ EclipseUtils
+ .showQuestionDialog(
+ DbCoreNLS.DeleteTableHandler_ConfirmationQuestionDialog_Title,
+ DbCoreNLS
+ .bind(DbCoreNLS.DeleteTableHandler_ConfirmationQuestionDialog_Description,
+ node.getName()));
+
+ if (shouldProceed)
+ {
+ IStatus status = dbNode.deleteTable(tableNode);
+
+ if ((status != null) && !status.isOK())
+ {
+ //something went wrong when deleting the table...
+ EclipseUtils.showErrorDialog(DbCoreNLS.DbNode_CouldNotDeleteTable,
+ status.getMessage());
+ }
+
+ }
+ }
+
+ return null;
+ }
+}
diff --git a/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/command/ExtractDataHandler.java b/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/command/ExtractDataHandler.java
new file mode 100644
index 0000000..4630f1d
--- /dev/null
+++ b/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/command/ExtractDataHandler.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.studio.android.db.core.command;
+
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.core.commands.IHandler;
+
+import com.motorolamobility.studio.android.db.core.ui.ITableNode;
+
+public class ExtractDataHandler extends AbstractHandler implements IHandler
+{
+
+ private final ITableNode node;
+
+ public ExtractDataHandler(ITableNode node)
+ {
+ this.node = node;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.commands.IHandler#execute(org.eclipse.core.commands.ExecutionEvent)
+ */
+ public Object execute(ExecutionEvent event) throws ExecutionException
+ {
+ node.extractData();
+ return null;
+ }
+
+}
diff --git a/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/command/LoadDataHandler.java b/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/command/LoadDataHandler.java
new file mode 100644
index 0000000..509077e
--- /dev/null
+++ b/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/command/LoadDataHandler.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.studio.android.db.core.command;
+
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.core.commands.IHandler;
+
+import com.motorolamobility.studio.android.db.core.ui.ITableNode;
+
+public class LoadDataHandler extends AbstractHandler implements IHandler
+{
+
+ private final ITableNode node;
+
+ public LoadDataHandler(ITableNode tableNode)
+ {
+ node = tableNode;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.commands.IHandler#execute(org.eclipse.core.commands.ExecutionEvent)
+ */
+ public Object execute(ExecutionEvent event) throws ExecutionException
+ {
+ node.loadData();
+ return null;
+ }
+
+}
diff --git a/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/command/MapDatabaseHandler.java b/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/command/MapDatabaseHandler.java
new file mode 100644
index 0000000..6efb615
--- /dev/null
+++ b/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/command/MapDatabaseHandler.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.studio.android.db.core.command;
+
+import java.io.File;
+
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.core.commands.IHandler;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.FileDialog;
+import org.eclipse.swt.widgets.Shell;
+
+import com.motorola.studio.android.common.utilities.EclipseUtils;
+import com.motorolamobility.studio.android.db.core.i18n.DbCoreNLS;
+import com.motorolamobility.studio.android.db.core.model.DbModel;
+import com.motorolamobility.studio.android.db.core.ui.IDbMapperNode;
+
+public class MapDatabaseHandler extends AbstractHandler implements IHandler
+{
+
+ private IDbMapperNode dbMapperNode;
+
+ public MapDatabaseHandler()
+ {
+ }
+
+ public MapDatabaseHandler(IDbMapperNode node)
+ {
+ this.dbMapperNode = node;
+ }
+
+ public Object execute(ExecutionEvent event) throws ExecutionException
+ {
+ if (dbMapperNode != null)
+ {
+ IPath dbFilePath = null;
+
+ Shell shell = Display.getCurrent().getActiveShell();
+
+ FileDialog dialog = new FileDialog(shell);
+ String[] filterExt =
+ {
+ "*.db", "*.*" //$NON-NLS-1$ //$NON-NLS-2$
+ };
+ dialog.setFilterExtensions(filterExt);
+
+ String dbFilePathString = dialog.open();
+
+ if (dbFilePathString != null)
+ {
+ File dbFile = new File(dbFilePathString);
+ dbFilePath = new Path(dbFile.getAbsolutePath());
+
+ if (dbFile.exists() && DbModel.isValidSQLiteDatabase(dbFile))
+ {
+ dbMapperNode.map(dbFilePath);
+ }
+ else
+ {
+ //Notify db does not exist or it is invalid
+ EclipseUtils.showErrorDialog(DbCoreNLS.MapDatabaseHandler_Title_Error,
+ DbCoreNLS.bind(DbCoreNLS.Invalid_Db_Error, dbFilePath));
+ }
+ }
+ }
+ return null;
+ }
+
+}
diff --git a/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/command/RefreshNodeHandler.java b/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/command/RefreshNodeHandler.java
new file mode 100644
index 0000000..e27aa40
--- /dev/null
+++ b/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/command/RefreshNodeHandler.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.studio.android.db.core.command;
+
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.core.commands.IHandler;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.swt.SWT;
+
+import com.motorola.studio.android.common.utilities.EclipseUtils;
+import com.motorolamobility.studio.android.db.core.CanRefreshStatus;
+import com.motorolamobility.studio.android.db.core.DbCoreActivator;
+import com.motorolamobility.studio.android.db.core.i18n.DbCoreNLS;
+import com.motorolamobility.studio.android.db.core.ui.ITreeNode;
+
+public class RefreshNodeHandler extends AbstractHandler implements IHandler
+{
+
+ private ITreeNode node;
+
+ public RefreshNodeHandler()
+ {
+ }
+
+ public RefreshNodeHandler(ITreeNode node)
+ {
+ this.node = node;
+ }
+
+ public Object execute(ExecutionEvent event) throws ExecutionException
+ {
+ if (node == null)
+ {
+ node = getSelectedItem();
+ }
+
+ Runnable asyncRefresh = new Runnable()
+ {
+
+ public void run()
+ {
+ if (node != null)
+ {
+ boolean canRefresh = false;
+ boolean canRefreshInput = true;
+
+ IStatus status = node.canRefresh();
+ if (status.isOK())
+ {
+ canRefresh = true;
+ }
+ else
+ {
+ if ((status instanceof CanRefreshStatus)
+ && status.matches(CanRefreshStatus.ASK_USER))
+ {
+ if (status.matches(CanRefreshStatus.ASK_USER
+ | CanRefreshStatus.CANCELABLE))
+ {
+ int dialogResults =
+ EclipseUtils
+ .showQuestionWithCancelDialog(
+ NLS.bind(
+ DbCoreNLS.RefreshNodeHandler_RefreshingNode_Msg_Title,
+ node.getName()), status
+ .getMessage());
+ if (dialogResults != SWT.CANCEL)
+ {
+ canRefreshInput = dialogResults == SWT.YES;
+ canRefresh = true;
+ }
+ }
+ else
+ {
+ canRefresh =
+ EclipseUtils
+ .showQuestionDialog(
+ NLS.bind(
+ DbCoreNLS.RefreshNodeHandler_RefreshingNode_Msg_Title,
+ node.getName()), status
+ .getMessage());
+ }
+ }
+ else
+ {
+ EclipseUtils.showErrorDialog(NLS.bind(
+ DbCoreNLS.RefreshNodeHandler_RefreshingNode_Error_Msg,
+ node.getName()), status.getMessage(), status);
+ }
+ }
+
+ if (canRefresh)
+ {
+ node.refreshAsync(canRefreshInput);
+ node = null; //clear selected node to force getSelectedItem to be called when calling via toolbar
+ }
+
+ }
+
+ }
+ };
+ Thread refreshThread = new Thread(asyncRefresh);
+ refreshThread.start();
+
+ return null;
+ }
+
+ private ITreeNode getSelectedItem()
+ {
+ ITreeNode selectedNode =
+ DbCoreActivator.getMOTODEVDatabaseExplorerView().getSelectedItemOnTree();
+
+ return selectedNode;
+ }
+
+}
diff --git a/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/command/SampleContentsHandler.java b/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/command/SampleContentsHandler.java
new file mode 100644
index 0000000..cd4df54
--- /dev/null
+++ b/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/command/SampleContentsHandler.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.studio.android.db.core.command;
+
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+
+import com.motorolamobility.studio.android.db.core.ui.IDataSampler;
+
+public class SampleContentsHandler extends AbstractHandler
+{
+
+ private final IDataSampler tableNode;
+
+ public SampleContentsHandler(IDataSampler tableNode)
+ {
+ this.tableNode = tableNode;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.commands.IHandler#execute(org.eclipse.core.commands.ExecutionEvent)
+ */
+ public Object execute(ExecutionEvent event) throws ExecutionException
+ {
+ tableNode.sampleDbContents();
+ return null;
+ }
+
+}
diff --git a/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/command/TableCreateHandler.java b/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/command/TableCreateHandler.java
new file mode 100644
index 0000000..4d0f997
--- /dev/null
+++ b/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/command/TableCreateHandler.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.studio.android.db.core.command;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.datatools.modelbase.sql.tables.Table;
+import org.eclipse.jface.dialogs.Dialog;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.jface.wizard.WizardDialog;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.ui.PlatformUI;
+
+import com.motorolamobility.studio.android.db.core.DbCoreActivator;
+import com.motorolamobility.studio.android.db.core.i18n.DbCoreNLS;
+import com.motorolamobility.studio.android.db.core.model.TableModel;
+import com.motorolamobility.studio.android.db.core.ui.ITreeNode;
+import com.motorolamobility.studio.android.db.core.ui.action.ITableCreatorNode;
+import com.motorolamobility.studio.android.db.core.ui.wizards.CreateTableWizard;
+
+public class TableCreateHandler extends AbstractHandler
+{
+ private ITableCreatorNode tableCreatorNode = null;
+
+ public TableCreateHandler()
+ {
+ }
+
+ public TableCreateHandler(ITableCreatorNode tableCreatorNode)
+ {
+ this.tableCreatorNode = tableCreatorNode;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.commands.IHandler#execute(org.eclipse.core.commands.ExecutionEvent)
+ */
+ public Object execute(ExecutionEvent event) throws ExecutionException
+ {
+ if (tableCreatorNode == null)
+ {
+ tableCreatorNode = getSelectedItem();
+ }
+
+ //tableCreatorNode may be null if the action come from toolbar
+ //and the selected item is not an ITableCreatorNode
+ if (tableCreatorNode != null)
+ {
+ Shell shell = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell();
+
+ boolean tableAdded = false;
+
+ // loop used to validate the new table name. If it already exists
+ // tell the user and open the table wizard again.
+ while (!tableAdded)
+ {
+ //repeat while table not added and dialog not cancelled
+ CreateTableWizard createTableWizard = new CreateTableWizard();
+ Set<String> notAllowedNames = getNotAllowedNames(tableCreatorNode.getTables());
+ createTableWizard.setNotAllowedNames(notAllowedNames);
+ WizardDialog dialog = new WizardDialog(shell, createTableWizard);
+ dialog.open();
+
+ if (dialog.getReturnCode() == Dialog.OK)
+ {
+ TableModel newTable = createTableWizard.getTable();
+ if (newTable != null)
+ {
+ boolean tableNameAlreadyExists = false;
+ for (Table table : tableCreatorNode.getTables())
+ {
+ if (table.getName().equalsIgnoreCase(newTable.getName()))
+ {
+ tableNameAlreadyExists = true;
+ break;
+ }
+ }
+ if (!tableNameAlreadyExists)
+ {
+ tableCreatorNode.createTable(newTable);
+ tableCreatorNode = null; //clear selected node to force getSelectedItem to be called when calling via toolbar
+ tableAdded = true;
+ }
+ else
+ {
+ //notify error that table already exists
+ MessageDialog
+ .openError(
+ PlatformUI.getWorkbench().getActiveWorkbenchWindow()
+ .getShell(),
+ DbCoreNLS.CreateDatabaseWizardPage_Table_Already_Exists_Title,
+ NLS.bind(
+ DbCoreNLS.CreateDatabaseWizardPage_Table_Already_Exists_Msg,
+ newTable.getName()));
+ }
+ }
+ }
+ else
+ {
+ //exit the loop if the user cancel dialog
+ break;
+ }
+ }
+
+ }
+
+ return null;
+ }
+
+ private ITableCreatorNode getSelectedItem()
+ {
+ ITableCreatorNode selectedNode = null;
+ ITreeNode selectedItem =
+ DbCoreActivator.getMOTODEVDatabaseExplorerView().getSelectedItemOnTree();
+
+ if (selectedItem instanceof ITableCreatorNode)
+ {
+ selectedNode = (ITableCreatorNode) selectedItem;
+ }
+
+ return selectedNode;
+ }
+
+ private Set<String> getNotAllowedNames(List<Table> list)
+ {
+
+ Set<String> names = new HashSet<String>();
+
+ for (Table table : list)
+ {
+ names.add(table.getName().toUpperCase());
+ }
+ return names;
+ }
+}
diff --git a/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/command/UnmapDatabaseHandler.java b/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/command/UnmapDatabaseHandler.java
new file mode 100644
index 0000000..6ce8ea8
--- /dev/null
+++ b/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/command/UnmapDatabaseHandler.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.studio.android.db.core.command;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.core.commands.IHandler;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.jface.viewers.LabelProvider;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.dialogs.ElementListSelectionDialog;
+
+import com.motorola.studio.android.common.utilities.EclipseUtils;
+import com.motorolamobility.studio.android.db.core.i18n.DbCoreNLS;
+import com.motorolamobility.studio.android.db.core.ui.IDbMapperNode;
+import com.motorolamobility.studio.android.db.core.ui.IDbNode;
+import com.motorolamobility.studio.android.db.core.ui.ITreeNode;
+
+public class UnmapDatabaseHandler extends AbstractHandler implements IHandler
+{
+
+ private IDbMapperNode dbMapperNode;
+
+ public UnmapDatabaseHandler()
+ {
+
+ }
+
+ public UnmapDatabaseHandler(IDbMapperNode node)
+ {
+ this.dbMapperNode = node;
+ }
+
+ public Object execute(ExecutionEvent event) throws ExecutionException
+ {
+ List<ITreeNode> mappedDbNodes = dbMapperNode.getChildren();
+ List<ITreeNode> dbNodesToUnmap = null;
+ if (!mappedDbNodes.isEmpty())
+ {
+ dbNodesToUnmap = queryDbPath(mappedDbNodes);
+ }
+ IStatus status = dbMapperNode.unmap(dbNodesToUnmap);
+ if ((status.getCode() != IStatus.CANCEL) && !status.isOK())
+ {
+ EclipseUtils.showErrorDialog(DbCoreNLS.UnmapDatabaseHandler_Error_Title,
+ DbCoreNLS.UnmapDatabaseHandler_Error_Description, status);
+ }
+
+ return null;
+ }
+
+ @SuppressWarnings(
+ {
+ "unchecked", "rawtypes"
+ })
+ private List<ITreeNode> queryDbPath(List<ITreeNode> mappedDbNodes)
+ {
+ Shell shell = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell();
+
+ ElementListSelectionDialog listDialog =
+ new ElementListSelectionDialog(shell, new LabelProvider()
+ {
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.viewers.LabelProvider#getText(java.lang.Object)
+ */
+ @Override
+ public String getText(Object element)
+ {
+ if (element instanceof IDbNode)
+ {
+ IDbNode dbNode = (IDbNode) element;
+ return dbNode.getName();
+ }
+ return super.getText(element);
+ }
+ });
+ listDialog.setElements(mappedDbNodes.toArray());
+ listDialog.setTitle(DbCoreNLS.UI_UnmapDatabaseAction_Title);
+ listDialog.setBlockOnOpen(true);
+ listDialog.setMultipleSelection(true);
+ listDialog.open();
+ Object[] result = listDialog.getResult();
+ List asList = Arrays.asList(result);
+ List<ITreeNode> checkedList = Collections.checkedList(asList, ITreeNode.class);
+ return checkedList;
+ }
+
+}
diff --git a/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/event/DatabaseModelEvent.java b/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/event/DatabaseModelEvent.java
new file mode 100644
index 0000000..f654198
--- /dev/null
+++ b/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/event/DatabaseModelEvent.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.studio.android.db.core.event;
+
+import com.motorolamobility.studio.android.db.core.ui.ITreeNode;
+
+public class DatabaseModelEvent
+{
+ private final ITreeNode treeNodeItem;
+
+ private final EVENT_TYPE eventType;
+
+ /**
+ * Represents the change in the model. The {@link DatabaseModelEvent#treeNodeItem} in each event may vary as specified below:
+ * <ul>
+ * <li>{@link EVENT_TYPE#ADD} it is the item that needs to be added</li>
+ * <li>{@link EVENT_TYPE#REMOVE} it is the item that needs to be removed</li>
+ * <li>{@link EVENT_TYPE#UPDATE} it is the item that needs to be updated</li>
+ * <li>{@link EVENT_TYPE#CLEAR} it is the parent node that needs to clear its children</li>
+ * <li>{@link EVENT_TYPE#EXPAND} it is the item that needs to be expanded</li>
+ * <li>{@link EVENT_TYPE#SELECT} it is the item that needs to be selected</li>
+ * </ul>
+ */
+ public enum EVENT_TYPE
+ {
+ ADD, REMOVE, UPDATE, CLEAR, EXPAND, SELECT
+ }
+
+ /**
+ * @return the eventType
+ */
+ protected EVENT_TYPE getEventType()
+ {
+ return eventType;
+ }
+
+ /**
+ * @return the treeNodeItem
+ */
+ public ITreeNode getTreeNodeItem()
+ {
+ return treeNodeItem;
+ }
+
+ /**
+ * Creates a new event from database model over
+ * @param item an object implementing {@link ITreeNode}
+ * @param eventType {@link EVENT_TYPE}
+ */
+ public DatabaseModelEvent(ITreeNode item, EVENT_TYPE eventType)
+ {
+ this.treeNodeItem = item;
+ this.eventType = eventType;
+ }
+}
diff --git a/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/event/DatabaseModelEventManager.java b/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/event/DatabaseModelEventManager.java
new file mode 100644
index 0000000..e102a7c
--- /dev/null
+++ b/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/event/DatabaseModelEventManager.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.studio.android.db.core.event;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.motorolamobility.studio.android.db.core.ui.ITreeNode;
+
+/**
+ * Manager which notifies {@link IDatabaseModelListener} registered that a {@link DatabaseModelEvent} occurred.
+ * It is a singleton that needs to be called by the hierarchy of {@link ITreeNode} items when they modify the database model.
+ */
+public class DatabaseModelEventManager
+{
+ private static DatabaseModelEventManager _instance;
+
+ private final List<IDatabaseModelListener> listeners = new ArrayList<IDatabaseModelListener>();
+
+ private DatabaseModelEventManager()
+ {
+ //Singleton - avoid extensions and other means to construct an instance
+ }
+
+ /**
+ * @return single instance of {@link DatabaseModelEvent}
+ */
+ public synchronized static DatabaseModelEventManager getInstance()
+ {
+ if (_instance == null)
+ {
+ _instance = new DatabaseModelEventManager();
+ }
+ return _instance;
+ }
+
+ /**
+ * Adds a new {@link IDatabaseModelListener}
+ * @param listener
+ */
+ public void addListener(IDatabaseModelListener listener)
+ {
+ synchronized (listener)
+ {
+ listeners.add(listener);
+ }
+ }
+
+ /**
+ * Removes the {@link IDatabaseModelListener}
+ * @param listener
+ */
+ public void removeListeners(IDatabaseModelListener listener)
+ {
+ synchronized (listener)
+ {
+ listeners.remove(listener);
+ }
+ }
+
+ /**
+ * Fires/notifies/deliver the event to the listeners registered.
+ * @param node {@link ITreeNode} that needs to refresh the view based on the model
+ * @param eventType
+ */
+ public void fireEvent(ITreeNode node, DatabaseModelEvent.EVENT_TYPE eventType)
+ {
+ DatabaseModelEvent databaseModelEvent = new DatabaseModelEvent(node, eventType);
+ synchronized (listeners)
+ {
+ if (listeners != null)
+ {
+ for (IDatabaseModelListener listener : listeners)
+ {
+ switch (eventType)
+ {
+ case ADD:
+ listener.handleNodeAdditionEvent(databaseModelEvent);
+ break;
+ case REMOVE:
+ listener.handleNodeRemovalEvent(databaseModelEvent);
+ break;
+ case UPDATE:
+ listener.handleNodeUpdateEvent(databaseModelEvent);
+ break;
+ case CLEAR:
+ listener.handleNodeClearEvent(databaseModelEvent);
+ break;
+ case EXPAND:
+ listener.handleNodeExpandEvent(databaseModelEvent);
+ break;
+ case SELECT:
+ listener.handleNodeSelectEvent(databaseModelEvent);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/event/IDatabaseModelListener.java b/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/event/IDatabaseModelListener.java
new file mode 100644
index 0000000..c455e24
--- /dev/null
+++ b/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/event/IDatabaseModelListener.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.studio.android.db.core.event;
+
+public interface IDatabaseModelListener
+{
+ /**
+ * Handles the event {@link DatabaseModelEvent#EVENT_TYPE#ADD} of the addition of a new node.
+ * @param databaseModelEvent {@link DatabaseModelEvent#getTreeNodeItem()} contains the node to be added.
+ */
+ public void handleNodeAdditionEvent(DatabaseModelEvent databaseModelEvent);
+
+ /**
+ * Handles the event {@link DatabaseModelEvent#EVENT_TYPE#REMOVE} of the removal of a node.
+ * @param databaseModelEvent {@link DatabaseModelEvent#getTreeNodeItem()} contains the node to be removed.
+ */
+ public void handleNodeRemovalEvent(DatabaseModelEvent databaseModelEvent);
+
+ /**
+ * Handles the event {@link DatabaseModelEvent#EVENT_TYPE#UPDATE} of the update of a node.
+ * @param databaseModelEvent {@link DatabaseModelEvent#getTreeNodeItem()} contains the node to be updated.
+ */
+ public void handleNodeUpdateEvent(DatabaseModelEvent databaseModelEvent);
+
+ /**
+ * Handles the event {@link DatabaseModelEvent#EVENT_TYPE#CLEAR} of the clear of the children of a node.
+ * @param databaseModelEvent {@link DatabaseModelEvent#getTreeNodeItem()} contains the parent node to clear its children.
+ */
+ public void handleNodeClearEvent(DatabaseModelEvent databaseModelEvent);
+
+ /**
+ * Handles the event {@link DatabaseModelEvent#EVENT_TYPE#CLEAR} of the refresh of a node.
+ * @param databaseModelEvent {@link DatabaseModelEvent#getTreeNodeItem()} contains the node to be refreshed.
+ */
+ public void handleNodeRefreshEvent(DatabaseModelEvent databaseModelEvent);
+
+ /**
+ * Handles the event {@link DatabaseModelEvent#EVENT_TYPE#EXPAND} of a node.
+ * @param databaseModelEvent {@link DatabaseModelEvent#getTreeNodeItem()} contains the node to be expanded.
+ */
+ public void handleNodeExpandEvent(DatabaseModelEvent databaseModelEvent);
+
+ /**
+ * Handles the event {@link DatabaseModelEvent#EVENT_TYPE#SELECT} of a node.
+ * @param databaseModelEvent {@link DatabaseModelEvent#getTreeNodeItem()} contains the node to be selected.
+ */
+ public void handleNodeSelectEvent(DatabaseModelEvent databaseModelEvent);
+}
diff --git a/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/exception/MotodevDbException.java b/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/exception/MotodevDbException.java
new file mode 100644
index 0000000..3a403de
--- /dev/null
+++ b/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/exception/MotodevDbException.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.studio.android.db.core.exception;
+
+import com.motorola.studio.android.common.exception.AndroidException;
+
+public class MotodevDbException extends AndroidException
+{
+
+ private static final long serialVersionUID = 1148147648131562077L;
+
+ /**
+ * Creates a new MotodevDbException object.
+ */
+ public MotodevDbException()
+ {
+
+ }
+
+ /**
+ * Creates a new MotodevDbException object.
+ *
+ * @param message the message used by the Exception.
+ */
+ public MotodevDbException(String message)
+ {
+ super(message);
+ }
+
+ /**
+ * Creates a new MotodevDbException object.
+ *
+ * @param cause the associated cause.
+ */
+ public MotodevDbException(Throwable cause)
+ {
+ super(cause);
+ }
+
+ /**
+ * Creates a new MotodevDbException object.
+ *
+ * @param message the message used by the Exception.
+ * @param cause the associated cause.
+ */
+ public MotodevDbException(String message, Throwable cause)
+ {
+ super(message, cause);
+ }
+
+}
diff --git a/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/filesystem/FilesystemRootNode.java b/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/filesystem/FilesystemRootNode.java
new file mode 100644
index 0000000..b361761
--- /dev/null
+++ b/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/filesystem/FilesystemRootNode.java
@@ -0,0 +1,279 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.motorolamobility.studio.android.db.core.filesystem;
+
+import java.util.List;
+
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.MultiStatus;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.preferences.IEclipsePreferences;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.osgi.service.prefs.BackingStoreException;
+import org.osgi.service.prefs.Preferences;
+
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorolamobility.studio.android.db.core.DbCoreActivator;
+import com.motorolamobility.studio.android.db.core.event.DatabaseModelEvent.EVENT_TYPE;
+import com.motorolamobility.studio.android.db.core.event.DatabaseModelEventManager;
+import com.motorolamobility.studio.android.db.core.exception.MotodevDbException;
+import com.motorolamobility.studio.android.db.core.i18n.DbCoreNLS;
+import com.motorolamobility.studio.android.db.core.ui.AbstractTreeNode;
+import com.motorolamobility.studio.android.db.core.ui.DbNode;
+import com.motorolamobility.studio.android.db.core.ui.IDbMapperNode;
+import com.motorolamobility.studio.android.db.core.ui.IDbNode;
+import com.motorolamobility.studio.android.db.core.ui.ISaveStateTreeNode;
+import com.motorolamobility.studio.android.db.core.ui.ITreeNode;
+import com.motorolamobility.studio.android.db.core.ui.view.SaveStateManager;
+
+/**
+ * Root node for the filesystem, that allows database mapping/unmapping.
+ *
+ */
+public class FilesystemRootNode extends AbstractTreeNode implements IDbMapperNode,
+ ISaveStateTreeNode
+{
+
+ private static final String MEMENTO_KEY = "FileSystemNode"; //$NON-NLS-1$
+
+ private static final String MEMENTO_PATH_PREFIX = "MappedPath_"; //$NON-NLS-1$
+
+ private static final String ICON_PATH = "icons/filesystem.png"; //$NON-NLS-1$
+
+ public FilesystemRootNode()
+ {
+ }
+
+ public FilesystemRootNode(ITreeNode parent)
+ {
+ super(parent);
+ }
+
+ public FilesystemRootNode(String id, String name, ITreeNode parent)
+ {
+ super(id, name, parent);
+ }
+
+ public FilesystemRootNode(String id, String name, ITreeNode parent, ImageDescriptor icon)
+ {
+ super(id, name, parent, icon);
+ }
+
+ @Override
+ public void refresh()
+ {
+ List<ITreeNode> children = getChildren();
+ for (ITreeNode child : children)
+ {
+ if (child instanceof DbNode)
+ {
+ DbNode dbNode = (DbNode) child;
+ if (!dbNode.existsDbFile())
+ {
+ //Set error node! Filesystem path does not exist anymore.
+ IStatus error =
+ new Status(IStatus.ERROR, DbCoreActivator.PLUGIN_ID,
+ DbCoreNLS.FilesystemRootNode_Mapped_Db_Not_Found);
+ dbNode.setNodeStatus(error);
+ }
+ else
+ {
+ dbNode.setNodeStatus(Status.OK_STATUS);
+ }
+ }
+ }
+ }
+
+ @Override
+ public boolean isLeaf()
+ {
+ return getChildren().isEmpty();
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorolamobility.studio.android.db.core.ui.AbstractTreeNode#getIcon()
+ */
+ @Override
+ public ImageDescriptor getIcon()
+ {
+ return DbCoreActivator.imageDescriptorFromPlugin(DbCoreActivator.PLUGIN_ID, ICON_PATH);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.IActionFilter#testAttribute(java.lang.Object, java.lang.String, java.lang.String)
+ */
+ @Override
+ public boolean testAttribute(Object target, String name, String value)
+ {
+ boolean canUnmap = false;
+ if (name.equals(IDbMapperNode.UNMAP_ACTIONFILTER_NAME)
+ && value.equals(IDbMapperNode.UNMAP_ACTIONFILTER_VALUE))
+ {
+ if (!getChildren().isEmpty())
+ {
+ canUnmap = true;
+ }
+ }
+ return canUnmap;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorolamobility.studio.android.db.core.ui.IDbMapperNode#map(org.eclipse.core.runtime.IPath)
+ */
+ public IStatus map(IPath dbFilePath)
+ {
+ IStatus status =
+ new Status(IStatus.OK, DbCoreActivator.PLUGIN_ID,
+ DbCoreNLS.FilesystemRootNode_Map_Successful);
+ DbNode dbNode = null;
+ try
+ {
+ dbNode = new DbNode(dbFilePath, this);
+ putChild(dbNode);
+ DatabaseModelEventManager.getInstance().fireEvent(dbNode, EVENT_TYPE.SELECT);
+ saveState(SaveStateManager.getInstance().getPrefNode());
+ }
+ catch (MotodevDbException e)
+ {
+ status =
+ new Status(IStatus.ERROR, DbCoreActivator.PLUGIN_ID, DbCoreNLS.bind(
+ DbCoreNLS.FilesystemRootNode_Error_Mapping_Description, dbFilePath));
+ }
+ return status;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorolamobility.studio.android.db.core.ui.IDbMapperNode#unmap(com.motorolamobility.studio.android.db.core.ui.ITreeNode)
+ */
+ public IStatus unmap(ITreeNode dbNode)
+ {
+ IStatus status =
+ new Status(IStatus.OK, DbCoreActivator.PLUGIN_ID,
+ DbCoreNLS.FilesystemRootNode_Unmapping_Successful);
+ if (dbNode instanceof IDbNode)
+ {
+ IDbNode node = (IDbNode) dbNode;
+ if (node.isConnected())
+ {
+ //node connected => disconnect
+ status = node.disconnect();
+ }
+ //node disconnected => remove the node from the tree
+ removeChild(node);
+ saveState(SaveStateManager.getInstance().getPrefNode());
+ }
+ return status;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorolamobility.studio.android.db.core.ui.IDbMapperNode#unmap(java.util.List)
+ */
+ public IStatus unmap(List<ITreeNode> dbNodeList)
+ {
+ MultiStatus operationsStatus = null;
+ if (dbNodeList != null)
+ {
+ boolean hasError = false;
+ IStatus[] children = new IStatus[dbNodeList.size()];
+ int i = 0;
+ for (ITreeNode treeNode : dbNodeList)
+ {
+ IStatus operationStatus = unmap(treeNode);
+ if (!operationStatus.isOK())
+ {
+ hasError = true;
+ }
+ children[i] = operationStatus;
+ i++;
+ }
+ int code = hasError ? IStatus.ERROR : IStatus.OK;
+ String msg =
+ hasError ? DbCoreNLS.FilesystemRootNode_UnmappingList_Error
+ : DbCoreNLS.FilesystemRootNode_UnmappingList_Successful;
+ operationsStatus =
+ new MultiStatus(DbCoreActivator.PLUGIN_ID, code, children, msg, null);
+ if (operationsStatus.isOK())
+ {
+ saveState(SaveStateManager.getInstance().getPrefNode());
+ }
+ }
+ return operationsStatus;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorolamobility.studio.android.db.core.ui.ISaveStateTreeNode#saveState(org.eclipse.core.runtime.preferences.IEclipsePreferences)
+ */
+ public void saveState(IEclipsePreferences preferences)
+ {
+ Preferences node = preferences.node(MEMENTO_KEY);
+
+ int i = 1;
+ List<ITreeNode> children = getChildren();
+ for (ITreeNode child : children)
+ {
+ DbNode dbNode = (DbNode) child;
+ node.put(MEMENTO_PATH_PREFIX + i, dbNode.getPath().toString());
+ i++;
+ }
+ try
+ {
+ preferences.flush();
+ }
+ catch (BackingStoreException e)
+ {
+ StudioLogger.debug(this, "Unable to save preferences, flush failed.");
+ }
+
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorolamobility.studio.android.db.core.ui.ISaveStateTreeNode#restoreState(org.eclipse.core.runtime.preferences.IEclipsePreferences)
+ */
+ public void restoreState(IEclipsePreferences preferences)
+ {
+ try
+ {
+ if (preferences.nodeExists(MEMENTO_KEY))
+ {
+ Preferences node = preferences.node(MEMENTO_KEY);
+ String[] attributeKeys = node.keys();
+ if (attributeKeys.length > 0)
+ {
+ for (String key : attributeKeys)
+ {
+ if (key.startsWith(MEMENTO_PATH_PREFIX))
+ {
+ String mappedPath = node.get(key, null);
+ if (mappedPath != null)
+ {
+ map(new Path(mappedPath));
+ }
+ }
+ }
+ }
+ }
+ }
+ catch (BackingStoreException e)
+ {
+ StudioLogger.debug(this, "Unable to restore preferences.");
+ }
+ }
+}
diff --git a/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/i18n/DbCoreNLS.java b/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/i18n/DbCoreNLS.java
new file mode 100644
index 0000000..6986352
--- /dev/null
+++ b/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/i18n/DbCoreNLS.java
@@ -0,0 +1,230 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.studio.android.db.core.i18n;
+
+import org.eclipse.osgi.util.NLS;
+
+/**
+ * This class is the NLS component for DB Core plug-in
+ */
+public class DbCoreNLS extends NLS
+{
+ /**
+ * The bundle location.
+ * It refers to messages.properties file inside this package
+ */
+ private static final String BUNDLE_NAME =
+ "com.motorolamobility.studio.android.db.core.i18n.dbcoreNLS"; //$NON-NLS-1$
+
+ static
+ {
+ NLS.initializeMessages(BUNDLE_NAME, DbCoreNLS.class);
+ }
+
+ /*
+ * DB Management classes wizard
+ */
+
+ public static String DatabaseCreationFieldValidator_DB_Already_Exists_Msg;
+
+ public static String DatabaseCreationFieldValidator_ValidChars;
+
+ public static String DatabaseExplorerTreeLabelProvider_Error_Tooltip_Prefix;
+
+ public static String Field_ErrorAutoIncrementNotAllowed;
+
+ public static String AbstractTreeNode_Loading_Job_Name;
+
+ public static String AddTableFieldDialog_InvalidName;
+
+ public static String Table_ErrorUnamedColumns;
+
+ public static String Table_ErrorConflictingNames;
+
+ public static String Table_ErrorMoreThanOnePrimaryKey;
+
+ public static String TableNode_BrowsingTableContentsErrorStatus;
+
+ public static String TableNode_BrowsingTableContentsSuccessStatus;
+
+ public static String FilesystemRootNode_Error_Mapping_Description;
+
+ public static String FilesystemRootNode_Map_Successful;
+
+ public static String FilesystemRootNode_Mapped_Db_Not_Found;
+
+ public static String FilesystemRootNode_Unmapping_Successful;
+
+ public static String FilesystemRootNode_UnmappingList_Error;
+
+ public static String FilesystemRootNode_UnmappingList_Successful;
+
+ public static String AddTableFieldDialog_FieldDefaultValueLabel;
+
+ public static String AddTableFieldDialog_FieldNameLabel;
+
+ public static String AddTableFieldDialog_FieldTypeLabel;
+
+ public static String AddTableFieldDialog_PrimaryKeyAutomaticBehaviour_DecrementalLabel;
+
+ public static String AddTableFieldDialog_PrimaryKeyAutomaticBehaviour_IncrementalLabel;
+
+ public static String AddTableFieldDialog_PrimaryKeyAutomaticBehaviour_NoneLabel;
+
+ public static String AddTableFieldDialog_PrimaryKeyAutomaticBehaviourLabel;
+
+ public static String AddTableFieldDialog_PrimaryKeyLabel;
+
+ /*
+ * Error strings
+ */
+
+ public static String DbModel_Could_Not_Delete_DbFile;
+
+ public static String DbModel_Could_Not_Disconnect_Profile;
+
+ public static String DbModel_Could_Not_Execute_Statement;
+
+ public static String DbModel_Not_Valid_Database;
+
+ public static String DbModel_Sampling_Contents_From;
+
+ public static String DbNode_Canceled_Save_Operation;
+
+ public static String DbNode_Close_Editor_Msg;
+
+ public static String DbNode_Close_Editor_Msg_Title;
+
+ public static String DbNode_CouldNotDeleteTable;
+
+ public static String DbNode_Tooltip_Prefix;
+
+ /*
+ * UI Strings
+ */
+
+ public static String Invalid_Db_Error;
+
+ public static String MapDatabaseHandler_Title_Error;
+
+ /*
+ * Console Strings
+ */
+
+ public static String ColumnNode_UnknownType;
+
+ public static String CreateDatabaseWizardPage_Add_Button;
+
+ public static String CreateDatabaseWizardPage_DB_Name_Label;
+
+ public static String CreateDatabaseWizardPage_Edit_Button;
+
+ public static String CreateDatabaseWizardPage_Remove_Button;
+
+ public static String CreateDatabaseWizardPage_Table_Already_Exists_Msg;
+
+ public static String CreateDatabaseWizardPage_Table_Already_Exists_Title;
+
+ public static String CreateDatabaseWizardPage_Table_Group;
+
+ public static String CreateDatabaseWizardPage_UI_PageTitle;
+
+ public static String CreateDatabaseWizardPage_UI_CreateNewDatabase;
+
+ public static String CreateDatabaseWizardPage_UI_CreateNewDBAddingItsFields;
+
+ public static String CreateTableWizard_UI_Message_ErrorCreatingTable;
+
+ public static String CreateTableWizardPage_AddEditField_DialogTitle;
+
+ public static String CreateTableWizardPage_UI_Add;
+
+ public static String CreateTableWizardPage_UI_CreateNewTable;
+
+ public static String CreateTableWizardPage_UI_CreateNewTableAddingItsFields;
+
+ public static String CreateTableWizardPage_UI_PageTitle;
+
+ public static String CreateTableWizardPage_UI_Default;
+
+ public static String CreateTableWizardPage_UI_Edit;
+
+ public static String CreateTableWizardPage_UI_InvalidTableName;
+
+ public static String CreateTableWizardPage_UI_Name;
+
+ public static String CreateTableWizardPage_UI_Primary;
+
+ public static String CreateTableWizardPage_UI_Remove;
+
+ public static String CreateTableWizardPage_UI_TableName;
+
+ public static String CreateTableWizardPage_UI_TableNameCannotBeEmpty;
+
+ public static String CreateTableWizardPage_UI_Type;
+
+ public static String CreateTableWizardPage_UI_YouMustSupplyAtLeastOneField;
+
+ /*
+ * Database deployment
+ */
+
+ public static String TableWizardLabelProvider_isPrimary_False;
+
+ public static String TableWizardLabelProvider_isPrimary_true;
+
+ /* Map and unmap databases on file system */
+
+ public static String UI_UnmapDatabaseAction_Title;
+
+ public static String DeleteDatabaseHandler_ConfirmationQuestionDialog_Description;
+
+ public static String DeleteDatabaseHandler_ConfirmationQuestionDialog_Title;
+
+ public static String DeleteDatabaseHandler_CouldNotDeleteDatabase;
+
+ public static String DeleteTableHandler_ConfirmationQuestionDialog_Description;
+
+ public static String DeleteTableHandler_ConfirmationQuestionDialog_Title;
+
+ public static String UI_DeleteProjectDialogTitle;
+
+ public static String UI_DeleteProjectDialogMsg;
+
+ public static String ERR_CreateDatabaseWizardPage_TableAlreadyExistTitle;
+
+ public static String UI_CreateDatabaseWizard_ChangePerspectiveQuestion;
+
+ public static String UI_CreateDatabaseWizard_ChangePerspectiveTitle;
+
+ public static String LoadingNode_nodeName;
+
+ public static String ProjectNode_Error_While_Creating_DB;
+
+ public static String ProjectNode_Failed_ToVerify_If_DB_Is_Valid;
+
+ public static String RefreshNodeHandler_RefreshingNode_Error_Msg;
+
+ public static String RefreshNodeHandler_RefreshingNode_Msg_Title;
+
+ public static String UI_CreateDatabaseWizardPage_CreateDatabase_Error_New;
+
+ public static String UI_CreateDatabaseWizardPage_CreateDatabase_Error;
+
+ public static String UnmapDatabaseHandler_Error_Description;
+
+ public static String UnmapDatabaseHandler_Error_Title;
+}
diff --git a/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/i18n/dbcoreNLS.properties b/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/i18n/dbcoreNLS.properties
new file mode 100644
index 0000000..8d8b78d
--- /dev/null
+++ b/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/i18n/dbcoreNLS.properties
@@ -0,0 +1,100 @@
+Field_ErrorAutoIncrementNotAllowed=Auto increment/decrement can only be used with Integer or Real types.
+AbstractTreeNode_Loading_Job_Name=Loading {0}
+AddTableFieldDialog_InvalidName=Invalid column name:
+Table_ErrorUnamedColumns=There are one or more unnamed columns
+Table_ErrorConflictingNames=There are conflicting field names:
+Table_ErrorMoreThanOnePrimaryKey=Two or more fields are set as primary keys:
+TableNode_BrowsingTableContentsErrorStatus=Error browsing table contents
+TableNode_BrowsingTableContentsSuccessStatus=Success browsing table contents
+
+FilesystemRootNode_Error_Mapping_Description=It was not possible to map the database {0} because it does not contain a valid SQLite3 database file
+FilesystemRootNode_Map_Successful=Filesystem database successfully mapped
+FilesystemRootNode_Mapped_Db_Not_Found=Mapped database no longer exists in the filesystem.
+FilesystemRootNode_Unmapping_Successful=Filesystem database successfully unmapped
+FilesystemRootNode_UnmappingList_Error=Some databases could not be unmapped. Please check the details
+FilesystemRootNode_UnmappingList_Successful=Filesystem databases successfully unmapped
+
+DatabaseCreationFieldValidator_DB_Already_Exists_Msg=There is already a database named {0}
+DatabaseCreationFieldValidator_ValidChars=Valid characters are: a-z A-Z 0-9 . _ - <space>
+DatabaseExplorerTreeLabelProvider_Error_Tooltip_Prefix=Error details: {0}
+
+AddTableFieldDialog_FieldDefaultValueLabel=Default Value
+AddTableFieldDialog_FieldNameLabel=Name
+AddTableFieldDialog_FieldTypeLabel=Type
+AddTableFieldDialog_PrimaryKeyAutomaticBehaviour_DecrementalLabel=Decremental
+AddTableFieldDialog_PrimaryKeyAutomaticBehaviour_IncrementalLabel=Incremental
+AddTableFieldDialog_PrimaryKeyAutomaticBehaviour_NoneLabel=None
+AddTableFieldDialog_PrimaryKeyAutomaticBehaviourLabel=Primary key automatic behaviour
+AddTableFieldDialog_PrimaryKeyLabel=Primary key
+
+Invalid_Db_Error=Database {0} does not exist or is invalid.
+MapDatabaseHandler_Title_Error=Error mapping database
+
+ColumnNode_UnknownType=UNKNOWN TYPE
+CreateDatabaseWizardPage_Add_Button=Add
+CreateDatabaseWizardPage_DB_Name_Label=Name:
+CreateDatabaseWizardPage_Edit_Button=Edit
+CreateDatabaseWizardPage_Remove_Button=Remove
+CreateDatabaseWizardPage_Table_Already_Exists_Msg=The chosen table name, {0}, already exists. Choose a new name and try again.
+CreateDatabaseWizardPage_Table_Already_Exists_Title=Table already exists
+CreateDatabaseWizardPage_Table_Group=Tables:
+CreateDatabaseWizardPage_UI_PageTitle=New Database
+CreateDatabaseWizardPage_UI_CreateNewDatabase=Create New Database
+CreateDatabaseWizardPage_UI_CreateNewDBAddingItsFields=Create a new database, adding its fields
+
+CreateTableWizard_UI_Message_ErrorCreatingTable=Error creating a table
+CreateTableWizardPage_AddEditField_DialogTitle=Add/Edit Field
+CreateTableWizardPage_UI_Add=Add...
+CreateTableWizardPage_UI_CreateNewTable=Create New Table
+CreateTableWizardPage_UI_CreateNewTableAddingItsFields=Create a new table, adding its fields
+CreateTableWizardPage_UI_PageTitle=New Table
+CreateTableWizardPage_UI_Default=Default
+CreateTableWizardPage_UI_Edit=Edit...
+CreateTableWizardPage_UI_InvalidTableName=Invalid Table Name
+CreateTableWizardPage_UI_Name=Name
+CreateTableWizardPage_UI_Primary=Primary
+CreateTableWizardPage_UI_Remove=Remove
+CreateTableWizardPage_UI_TableName=Table Name
+CreateTableWizardPage_UI_TableNameCannotBeEmpty=Table name cannot be empty
+CreateTableWizardPage_UI_Type=Type
+CreateTableWizardPage_UI_YouMustSupplyAtLeastOneField=You must supply at least one field
+
+TableWizardLabelProvider_isPrimary_False=false
+TableWizardLabelProvider_isPrimary_true=true
+
+UI_UnmapDatabaseAction_Title = Choose database to unmap
+
+UI_DeleteProjectDialogTitle=Opened database connections
+
+UI_DeleteProjectDialogMsg=Some databases could not be deleted. \nClose their connections and try again.
+
+UI_CreateDatabaseWizardPage_CreateDatabase_Error=Could not create database
+UI_CreateDatabaseWizardPage_CreateDatabase_Error_New=An unexpected error has occurred while attempting to create the database {0}
+#UI_CreateDatabaseWizardPage_CreateDatabase=Create Database
+ERR_CreateDatabaseWizardPage_TableAlreadyExistTitle=Table name already exists
+
+UI_CreateDatabaseWizard_ChangePerspectiveQuestion=Database is associated with the Database perspective. Do you want to open this perspective now?
+UI_CreateDatabaseWizard_ChangePerspectiveTitle=Open Associated Perspective?
+UnmapDatabaseHandler_Error_Description=Could not unmap database.
+UnmapDatabaseHandler_Error_Title=MOTODEV Studio for Android
+
+DbModel_Could_Not_Delete_DbFile=Could not delete dbFile {0}
+DbModel_Could_Not_Disconnect_Profile=Could not disconnect profile {0}
+DbModel_Could_Not_Execute_Statement=Could not execute SQL statement {0}
+DbModel_Not_Valid_Database=File {0} is not a valid SQLite database
+DbModel_Sampling_Contents_From=Sampling Contents from
+DbNode_Canceled_Save_Operation=User cancelled the operation during editor closing
+DbNode_Close_Editor_Msg=Database file {0} contains unsaved changes.\nDo you want to save?
+DbNode_Close_Editor_Msg_Title=Save Editor
+DbNode_CouldNotDeleteTable=Could not delete the selected table.
+DbNode_Tooltip_Prefix=Filesystem path: {0}
+DeleteDatabaseHandler_ConfirmationQuestionDialog_Description=This action will permanently remove the database {0}.\nWould you like to proceed?
+DeleteDatabaseHandler_ConfirmationQuestionDialog_Title=Database deletion confirmation
+DeleteDatabaseHandler_CouldNotDeleteDatabase=Could not delete selected database
+DeleteTableHandler_ConfirmationQuestionDialog_Description=This action will permanently remove the table {0}.\nWould you like to proceed?
+DeleteTableHandler_ConfirmationQuestionDialog_Title=Table deletion confirmation
+LoadingNode_nodeName=Loading...
+ProjectNode_Error_While_Creating_DB=An Unexpected error has occurred while trying to create the database {0}
+ProjectNode_Failed_ToVerify_If_DB_Is_Valid=It was not possible verify that the file is a valid SQLite database: {0}
+RefreshNodeHandler_RefreshingNode_Error_Msg=Cannot refresh node {0}
+RefreshNodeHandler_RefreshingNode_Msg_Title=Refreshing node {0}
diff --git a/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/junit/DbModelTest.java b/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/junit/DbModelTest.java
new file mode 100644
index 0000000..f16d991
--- /dev/null
+++ b/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/junit/DbModelTest.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.studio.android.db.core.junit;
+
+import static org.junit.Assert.assertTrue;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Path;
+import org.junit.Test;
+
+import com.motorolamobility.studio.android.db.core.exception.MotodevDbException;
+import com.motorolamobility.studio.android.db.core.model.DbModel;
+import com.motorolamobility.studio.android.db.core.model.Field;
+import com.motorolamobility.studio.android.db.core.model.Field.AutoIncrementType;
+import com.motorolamobility.studio.android.db.core.model.Field.DataType;
+import com.motorolamobility.studio.android.db.core.model.TableModel;
+
+public class DbModelTest
+{
+
+ @Test
+ public void testCreateTable()
+ {
+
+ Path path = new Path("/Users/danielbfranco/temp/ranking.db");
+ DbModel model = null;
+ try
+ {
+ model = new DbModel(path);
+ }
+ catch (MotodevDbException e)
+ {
+ e.printStackTrace();
+ }
+ IStatus s = model.connect();
+ assertTrue(s.getCode() == IStatus.OK);
+
+ Field idField = new Field("_id", DataType.INTEGER, true, AutoIncrementType.ASCENDING, null);
+ Field textField =
+ new Field("_text", DataType.TEXT, false, AutoIncrementType.NONE, "DanDan");
+ Field numField = new Field("_num", DataType.INTEGER, false, AutoIncrementType.NONE, "5");
+
+ List<Field> fields = new ArrayList<Field>(2);
+ fields.add(idField);
+ fields.add(textField);
+ fields.add(numField);
+
+ TableModel table = new TableModel("mablinhos3", fields);
+
+ s = model.createTable(table);
+ assertTrue(s.getCode() == IStatus.OK);
+ }
+}
diff --git a/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/junit/TableNodeTest.java b/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/junit/TableNodeTest.java
new file mode 100644
index 0000000..44421ba
--- /dev/null
+++ b/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/junit/TableNodeTest.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.studio.android.db.core.junit;
+
+import static org.junit.Assert.assertTrue;
+
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.datatools.modelbase.sql.tables.Table;
+import org.junit.Test;
+
+import com.motorolamobility.studio.android.db.core.exception.MotodevDbException;
+import com.motorolamobility.studio.android.db.core.model.DbModel;
+import com.motorolamobility.studio.android.db.core.ui.TableNode;
+
+public class TableNodeTest
+{
+
+ @Test
+ public void test()
+ {
+
+ Path path = new Path("/Users/danielbfranco/temp/ranking.db");
+ DbModel model = null;
+ try
+ {
+ model = new DbModel(path);
+ }
+ catch (MotodevDbException e)
+ {
+ e.printStackTrace();
+ }
+ IStatus s = model.connect();
+ assertTrue(s.getCode() == IStatus.OK);
+
+ Table table = model.getTable("mablinhos3");
+
+ TableNode node = new TableNode(table, model, null);
+ node.browseTableContents();
+
+ try
+ {
+ Thread.sleep(5000);
+ }
+ catch (InterruptedException e)
+ {
+ e.printStackTrace();
+ }
+
+ }
+}
diff --git a/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/model/DbModel.java b/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/model/DbModel.java
new file mode 100644
index 0000000..f0eab06
--- /dev/null
+++ b/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/model/DbModel.java
@@ -0,0 +1,728 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.studio.android.db.core.model;
+
+import static com.motorola.studio.android.common.log.StudioLogger.error;
+import static com.motorola.studio.android.common.log.StudioLogger.info;
+
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.sql.Connection;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Properties;
+import java.util.Set;
+
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.datatools.connectivity.ConnectionProfileException;
+import org.eclipse.datatools.connectivity.IConnectionProfile;
+import org.eclipse.datatools.connectivity.IManagedConnection;
+import org.eclipse.datatools.connectivity.ProfileManager;
+import org.eclipse.datatools.connectivity.drivers.DriverManager;
+import org.eclipse.datatools.connectivity.sqm.core.connection.ConnectionInfo;
+import org.eclipse.datatools.connectivity.sqm.core.connection.DatabaseConnectionRegistry;
+import org.eclipse.datatools.connectivity.sqm.core.rte.ICatalogObject;
+import org.eclipse.datatools.connectivity.sqm.internal.core.connection.ConnectionInfoImpl;
+import org.eclipse.datatools.modelbase.sql.schema.Catalog;
+import org.eclipse.datatools.modelbase.sql.schema.Database;
+import org.eclipse.datatools.modelbase.sql.schema.SQLSchemaPackage;
+import org.eclipse.datatools.modelbase.sql.schema.Schema;
+import org.eclipse.datatools.modelbase.sql.tables.Column;
+import org.eclipse.datatools.modelbase.sql.tables.Table;
+import org.eclipse.datatools.sqltools.data.internal.ui.editor.TableDataEditor;
+import org.eclipse.datatools.sqltools.internal.refresh.ICatalogObject2;
+import org.eclipse.datatools.sqltools.result.OperationCommand;
+import org.eclipse.datatools.sqltools.result.ResultsViewAPI;
+import org.eclipse.emf.common.util.EList;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.ui.IEditorPart;
+
+import com.motorola.studio.android.common.CommonPlugin;
+import com.motorola.studio.android.common.utilities.EclipseUtils;
+import com.motorolamobility.studio.android.db.core.DbCoreActivator;
+import com.motorolamobility.studio.android.db.core.exception.MotodevDbException;
+import com.motorolamobility.studio.android.db.core.i18n.DbCoreNLS;
+import com.motorolamobility.studio.android.db.core.model.Field.AutoIncrementType;
+
+/**
+ * This class represents a datamodel. Is responsible to hold a IConnectionProfile to a database and execute operations with this profile.
+ */
+public class DbModel
+{
+ static final String JDBC_DRIVER_INSTANCE_NAME = CommonPlugin.JDBC_DRIVER_INSTANCE_NAME;
+
+ public static final String DBNAME_PROPERTY =
+ "org.eclipse.datatools.connectivity.db.databaseName"; //$NON-NLS-1$
+
+ public static final String PROVIDER_ID =
+ "org.eclipse.datatools.enablement.sqlite.connectionProfile"; //$NON-NLS-1$
+
+ public static final String URL_PROPERTY = "org.eclipse.datatools.connectivity.db.URL"; //$NON-NLS-1$
+
+ public static final String JDBC_SQLITE_PREFIX = "jdbc:sqlite:"; //$NON-NLS-1$
+
+ public static final String LOCALPATH_PROPERTY = "com.motorola.studio.db.localPathProperty"; //$NON-NLS-1$
+
+ private final IConnectionProfile connProfile;
+
+ private final IPath dbPath;
+
+ /**
+ * Creates a new DbModel Object based on the sqlite database at dbPath
+ * @param dbPath SQLite3 database file path
+ * @throws MotodevDbException if dbPath does not contains a valid SQLite3 database file
+ */
+ public DbModel(IPath dbPath) throws MotodevDbException
+ {
+ this(dbPath, false);
+ }
+
+ /**
+ * Creates a new DbModel Object creating a empty database file at dbPath if create is true.
+ * If create is false the behavior is the same as DbModel(Path dbPath)
+ * @param dbPath SQLite3 database file path
+ * @param create flag indicating if the database should be created
+ * @throws MotodevDbException if dbPath does not contains a valid SQLite3 database file
+ */
+ public DbModel(IPath dbPath, boolean create) throws MotodevDbException
+ {
+ this(dbPath, create, false);
+ }
+
+ /**
+ * Creates a new DbModel Object creating a empty database file at dbPath if create is true.
+ * If create is false the behavior is the same as DbModel(Path dbPath)
+ * @param dbPath SQLite3 database file path
+ * @param create flag indicating if the database should be created
+ * @throws MotodevDbException if dbPath does not contains a valid SQLite3 database file
+ */
+ public DbModel(IPath dbPath, boolean create, boolean overwrite) throws MotodevDbException
+ {
+ if (create)
+ {
+ File dbFile = dbPath.toFile();
+ try
+ {
+ DbCoreActivator.getDefault().copyTemplateDbFile(dbFile, overwrite);
+ }
+ catch (IOException e)
+ {
+ throw new MotodevDbException(e);
+ }
+ }
+
+ this.dbPath = dbPath;
+ if (isValidSQLiteDatabase(dbPath.toFile()))
+ {
+ connProfile = getProfile(dbPath);
+ }
+ else
+ {
+ throw new MotodevDbException(NLS.bind(DbCoreNLS.DbModel_Not_Valid_Database,
+ dbPath.toOSString()));
+ }
+ }
+
+ /**
+ * Checks if a compatible JDBC driver is registered. If not, registers one
+ */
+ public static void assertDriverExistsAtModel()
+ {
+ DriverManager driverMan = DriverManager.getInstance();
+ String allDrivers = driverMan.getFullJarList();
+ String driverPath = getDriverPath();
+ if ((allDrivers == null) || (!allDrivers.contains(driverPath)))
+ {
+ String templateId = "org.eclipse.datatools.enablement.sqlite.3_5_9.driver"; //$NON-NLS-1$
+ driverMan.createNewDriverInstance(templateId, JDBC_DRIVER_INSTANCE_NAME, driverPath);
+ info("Created a MOTODEV Studio JDBC driver instance at Data Tools."); //$NON-NLS-1$
+ }
+ }
+
+ /**
+ * If a compatible JDBC driver is registered, removes it
+ */
+ public static void deleteDriverFromModel()
+ {
+ DriverManager driverMan = DriverManager.getInstance();
+ String jarList = driverMan.getFullJarList();
+ if ((jarList != null) && (jarList.contains(DbModel.JDBC_DRIVER_INSTANCE_NAME)))
+ {
+ driverMan.removeDriverInstance(DbModel.JDBC_DRIVER_INSTANCE_NAME);
+ info("Removed the MOTODEV Studio JDBC driver instance from Data Tools."); //$NON-NLS-1$
+ }
+ }
+
+ private IConnectionProfile getProfile(IPath dbPath) throws MotodevDbException
+ {
+ String fullPath = dbPath.toOSString();
+ IConnectionProfile profile = null;
+ profile = ProfileManager.getInstance().getProfileByFullPath(fullPath);
+
+ if (profile == null)
+ {
+
+ Properties prop =
+ getBaseConnProperties(getDriverPath(), dbPath.lastSegment(),
+ dbPath.toOSString());
+
+ try
+ {
+ profile =
+ ProfileManager.getInstance().createProfile(fullPath, "", PROVIDER_ID, prop); //$NON-NLS-1$
+ profile.setBaseProperties(prop);
+ }
+ catch (ConnectionProfileException e)
+ {
+ throw new MotodevDbException(NLS.bind("Unable to create Profile for db {0}",
+ dbPath.toOSString()));
+ }
+
+ }
+
+ return profile;
+ }
+
+ /**
+ * Retrieves the location of the driver
+ * @return
+ */
+ private static String getDriverPath()
+ {
+ String driverPath = null; //$NON-NLS-1$
+
+ driverPath = CommonPlugin.getDefault().getDriverPath();
+ if (driverPath == null)
+ {
+ driverPath = "";
+ }
+ return driverPath;
+ }
+
+ // /*
+ // * Retrieves the JDBC Sqlite3 driver file
+ // */
+ // private static File getDriver(String pathAtPlugin)
+ // {
+ // URL location = DbCoreActivator.getDefault().getBundle().getEntry(pathAtPlugin);
+ //
+ // debug("JDBC Driver Location:" + location + " JDBC Driver getBundle().getLocation():" //$NON-NLS-1$ //$NON-NLS-2$
+ // + DbCoreActivator.getDefault().getBundle().getLocation());
+ //
+ // File file = null;
+ // try
+ // {
+ // IPath p = new Path(FileLocator.toFileURL(location).getFile());
+ // debug("JDBC Driver Path:" + p.toOSString()); //$NON-NLS-1$
+ // file = p.toFile();
+ // }
+ // catch (IOException e)
+ // {
+ // error("Error while trying to locate jdbc driver into db plugin:" + e.getMessage()); //$NON-NLS-1$
+ // }
+ // return file;
+ //
+ // }
+
+ public static Properties getBaseConnProperties(String driverPath, String dbName, String dbPath)
+ {
+
+ Properties prop = new Properties();
+ prop.put("org.eclipse.datatools.connectivity.db.vendor", "SQLITE"); //$NON-NLS-1$ //$NON-NLS-2$
+ prop.put("org.eclipse.datatools.connectivity.db.password", ""); //$NON-NLS-1$ //$NON-NLS-2$
+ prop.put("org.eclipse.datatools.connectivity.driverDefinitionID", //$NON-NLS-1$
+ "DriverDefn.org.eclipse.datatools.enablement.sqlite.3_5_9.driver." //$NON-NLS-1$
+ + JDBC_DRIVER_INSTANCE_NAME);
+ prop.put("org.eclipse.datatools.connectivity.drivers.defnType", //$NON-NLS-1$
+ "org.eclipse.datatools.enablement.sqlite.3_5_9.driver"); //$NON-NLS-1$
+ prop.put("org.eclipse.datatools.connectivity.db.savePWD", "false"); //$NON-NLS-1$ //$NON-NLS-2$
+ prop.put("org.eclipse.datatools.connectivity.db.connectionProperties", ""); //$NON-NLS-1$ //$NON-NLS-2$
+ prop.put("org.eclipse.datatools.connectivity.db.version", "3.5.9"); //$NON-NLS-1$ //$NON-NLS-2$
+ prop.put(DBNAME_PROPERTY, dbName);
+ prop.put("jarList", driverPath); //$NON-NLS-1$
+ prop.put("org.eclipse.datatools.connectivity.db.username", ""); //$NON-NLS-1$ //$NON-NLS-2$
+ prop.put("org.eclipse.datatools.connectivity.db.driverClass", "org.sqlite.JDBC"); //$NON-NLS-1$ //$NON-NLS-2$
+
+ prop.put(URL_PROPERTY, JDBC_SQLITE_PREFIX + dbPath); //$NON-NLS-1$
+ prop.put(LOCALPATH_PROPERTY, dbPath);
+ return prop;
+ }
+
+ public IStatus connect()
+ {
+ IPath dbPath = getDbPath();
+ File file = dbPath.toFile();
+ if (file.exists() && isValidSQLiteDatabase(file))
+ {
+ return connProfile.connect();
+ }
+ else
+ {
+ return new Status(IStatus.ERROR, DbCoreActivator.PLUGIN_ID, DbCoreNLS.bind(
+ DbCoreNLS.Invalid_Db_Error, dbPath));
+ }
+ }
+
+ public IStatus disconnect()
+ {
+ return connProfile.disconnect();
+ }
+
+ /**
+ * Create a table based on the infomration provided via param table, on this db model.
+ * @param table The {@link TableModel}
+ * @return IStatus.OK if the table was created successfully, IStatus.ERROR otherwise. The status message explains the fail reason.
+ */
+ public IStatus createTable(TableModel table)
+ {
+ StringBuilder strBuilder = new StringBuilder("CREATE TABLE "); //$NON-NLS-1$
+ strBuilder.append(table.getName());
+ strBuilder.append("("); //$NON-NLS-1$
+
+ List<Field> fields = table.getFields();
+
+ boolean firstField = true;
+ for (Field field : fields)
+ {
+ if (firstField)
+ {
+ firstField = false;
+ }
+ else
+ {
+ strBuilder.append(", "); //$NON-NLS-1$
+ }
+
+ strBuilder.append(field.getName());
+ strBuilder.append(" "); //$NON-NLS-1$
+ strBuilder.append(field.getType().toString());
+ if (field.isPrimaryKey())
+ {
+ strBuilder.append(" PRIMARY KEY"); //$NON-NLS-1$
+ if (field.getAutoIncrementType() != AutoIncrementType.NONE)
+ {
+ strBuilder.append(" "); //$NON-NLS-1$
+ strBuilder.append(field.getAutoIncrementType().toString());
+ }
+ }
+ if ((field.getDefaultValue() != null) && (!field.getDefaultValue().equals(""))) //$NON-NLS-1$
+ {
+ strBuilder.append(" default \'"); //$NON-NLS-1$
+ strBuilder.append(field.getDefaultValue());
+ strBuilder.append("\'"); //$NON-NLS-1$
+ }
+ }
+
+ strBuilder.append(")"); //$NON-NLS-1$
+
+ return executeSingleStatement(strBuilder.toString());
+ }
+
+ /**
+ * Delete a table from this dbModel with the name tableName
+ * @param tableName The name of the table to be deleted
+ * @return IStatus.OK if the table was deleted successfully, IStatus.ERROR otherwise. The status message explains the fail reason.
+ */
+ public IStatus deleteTable(String tableName)
+ {
+ return executeSingleStatement("DROP TABLE " + tableName); //$NON-NLS-1$
+ }
+
+ /**
+ * Retrieves the {@link Table} with name tableName from this dbModel
+ * @param tableName the name of the table within this dbModel to be retrieved.
+ * @return The {@link Table} object if existent, null otherwise.
+ */
+ public Table getTable(String tableName)
+ {
+ List<Table> tables = getTables();
+ Table table = null;
+
+ for (Table t : tables)
+ {
+ if (t.getName().toUpperCase().equals(tableName.toUpperCase()))
+ {
+ table = t;
+ break;
+ }
+ }
+
+ return table;
+ }
+
+ /**
+ * Retrieves all tables from this dbModel.
+ * @return all tables from found on this dbModel. All catalogs and schemas are used during the search.
+ * An empty list is returned if there's no table on this dbModel.
+ */
+ @SuppressWarnings("unchecked")
+ public List<Table> getTables()
+ {
+ List<Table> tables = new ArrayList<Table>();
+ ConnectionInfo connectionInfo = getConnectionInfo();
+ if (connectionInfo != null)
+ {
+ Database database = connectionInfo.getSharedDatabase();
+
+ connectionInfo = DatabaseConnectionRegistry.getConnectionForDatabase(database);
+ EList<Catalog> catalogs = database.getCatalogs();
+ for (Catalog catalog : catalogs)
+ {
+ EList<Schema> schemas = catalog.getSchemas();
+ schemas.addAll(database.getSchemas());
+ for (Schema schema : schemas)
+ {
+ //Schema must be refreshed in order to retrieve the latest information, instead of the cached info.
+ if (schema instanceof ICatalogObject2)
+ {
+ String context =
+ ((ICatalogObject2) schema).getRefreshContext(new Integer(
+ SQLSchemaPackage.SCHEMA__TABLES));
+ ((ICatalogObject2) schema).refresh(context);
+ }
+ else
+ {
+ ((ICatalogObject) schema).refresh();
+ }
+
+ EList<Table> schemaTables = schema.getTables();
+ tables.addAll(schemaTables);
+ }
+ }
+ }
+
+ return tables;
+ }
+
+ /**
+ * Delete the db file represented by this dbModel.
+ * @return Status.OK if operation is successful, Status.ERROR otherwise. The status message explains the fail reason.
+ */
+ public IStatus deleteDb()
+ {
+ IStatus status = Status.OK_STATUS;
+ if (connProfile.getConnectionState() != IConnectionProfile.DISCONNECTED_STATE)
+ {
+ disconnect();
+ }
+ try
+ {
+ cleanModel();
+ boolean deleteSuccesfull = dbPath.toFile().delete();
+ if (!deleteSuccesfull)
+ {
+ status =
+ new Status(IStatus.ERROR, DbCoreActivator.PLUGIN_ID, NLS.bind(
+ DbCoreNLS.DbModel_Could_Not_Delete_DbFile, dbPath.toOSString()));
+ }
+ }
+ catch (ConnectionProfileException e)
+ {
+ status =
+ new Status(IStatus.ERROR, DbCoreActivator.PLUGIN_ID, NLS.bind(
+ DbCoreNLS.DbModel_Could_Not_Disconnect_Profile, connProfile.getName()),
+ e);
+ }
+
+ return status;
+ }
+
+ /**
+ * @throws ConnectionProfileException
+ */
+ public void cleanModel() throws ConnectionProfileException
+ {
+ disconnect();
+ ProfileManager.getInstance().deleteProfile(connProfile);
+ }
+
+ /**
+ * Verifies if the databaseFile is a valid SQLite3 file.
+ * @param databaseFile the SQLIte3 db file to be verified
+ * @return true if the file is a valid SQLite3 file or false otherwise.
+ */
+ public static boolean isValidSQLiteDatabase(File databaseFile)
+ {
+ boolean result = true;
+
+ final int BYTE_ARRAY_SIZE = 16;
+
+ byte[] headerByteArray = new byte[16];
+ headerByteArray[0] = 0x53;
+ headerByteArray[1] = 0x51;
+ headerByteArray[2] = 0x4c;
+ headerByteArray[3] = 0x69;
+ headerByteArray[4] = 0x74;
+ headerByteArray[5] = 0x65;
+ headerByteArray[6] = 0x20;
+ headerByteArray[7] = 0x66;
+ headerByteArray[8] = 0x6f;
+ headerByteArray[9] = 0x72;
+ headerByteArray[10] = 0x6d;
+ headerByteArray[11] = 0x61;
+ headerByteArray[12] = 0x74;
+ headerByteArray[13] = 0x20;
+ headerByteArray[14] = 0x33;
+ headerByteArray[15] = 0x00;
+
+ byte[] fileByteArray = new byte[BYTE_ARRAY_SIZE];
+ FileInputStream fis = null;
+
+ try
+ {
+ fis = new FileInputStream(databaseFile);
+ fis.read(fileByteArray);
+ }
+ catch (Exception e)
+ {
+ result = false;
+ }
+ finally
+ {
+ if (fis != null)
+ {
+ try
+ {
+ fis.close();
+ }
+ catch (IOException e)
+ {
+ //Do nothing.
+ }
+ }
+ }
+
+ ByteArrayInputStream bais = null;
+ try
+ {
+ bais = new ByteArrayInputStream(fileByteArray);
+ for (int aux = 0; aux < BYTE_ARRAY_SIZE; aux++)
+ {
+ int myByte = bais.read();
+ if (myByte != headerByteArray[aux])
+ {
+ result = false;
+ }
+ }
+ }
+ finally
+ {
+ if (bais != null)
+ {
+ try
+ {
+ bais.close();
+ }
+ catch (IOException e)
+ {
+ //Do nothing.
+ }
+ }
+ }
+ return result;
+ }
+
+ private ConnectionInfo getConnectionInfo()
+ {
+ if (!isConnected())
+ {
+ connProfile.connect();
+ }
+ IManagedConnection managedConnection =
+ connProfile.getManagedConnection(ConnectionInfo.class.getName()); //$NON-NLS-1$
+ ConnectionInfo connectionInfo = null;
+ if (managedConnection != null)
+ {
+ connectionInfo = (ConnectionInfo) managedConnection.getConnection().getRawConnection();
+ }
+ return connectionInfo;
+ }
+
+ /**
+ * @return true if the profile is connected, false otherwise
+ */
+ public boolean isConnected()
+ {
+ return connProfile.getConnectionState() == IConnectionProfile.CONNECTED_STATE;
+ }
+
+ private IStatus executeSingleStatement(String statement)
+ {
+ IStatus status = Status.OK_STATUS;
+
+ Connection connection = getManagedSqlConnection();
+ try
+ {
+ Statement sqlStatement = connection.createStatement();
+ sqlStatement.execute(statement);
+ }
+ catch (Exception e)
+ {
+ status =
+ new Status(IStatus.ERROR, DbCoreActivator.PLUGIN_ID, NLS.bind(
+ DbCoreNLS.DbModel_Could_Not_Execute_Statement, statement), e);
+ }
+
+ return status;
+ }
+
+ private Connection getManagedSqlConnection()
+ {
+ if (!isConnected())
+ {
+ connProfile.connect();
+ }
+ IManagedConnection managedConnection =
+ connProfile.getManagedConnection(Connection.class.getName());
+ Connection connection = (Connection) managedConnection.getConnection().getRawConnection();
+ return connection;
+ }
+
+ /**
+ * @return the absolute path for the db file
+ */
+ public IPath getDbPath()
+ {
+ return dbPath;
+ }
+
+ /**
+ * @return the name of the associated connection profile
+ */
+ public String getProfileName()
+ {
+ return connProfile.getName();
+ }
+
+ /**
+ * @return
+ */
+ public Set<IEditorPart> getAssociatedEditors()
+ {
+ Collection<IEditorPart> allEditors = EclipseUtils.getAllOpenedEditors();
+ Set<IEditorPart> selectedEditors = new HashSet<IEditorPart>();
+ for (IEditorPart e : allEditors)
+ {
+ if (e instanceof TableDataEditor)
+ {
+ TableDataEditor tde = (TableDataEditor) e;
+ Table table = tde.getSqlTable();
+ Catalog cat = table.getSchema().getCatalog();
+ Database database =
+ cat != null ? cat.getDatabase() : table.getSchema().getDatabase();
+ ConnectionInfo connInfo =
+ DatabaseConnectionRegistry.getConnectionForDatabase(database);
+ if (connInfo != null)
+ {
+ IConnectionProfile editorProfile =
+ ((ConnectionInfoImpl) connInfo).getConnectionProfile();
+ if (editorProfile == connProfile)
+ {
+ selectedEditors.add(e);
+ }
+ }
+ }
+ }
+
+ return selectedEditors;
+ }
+
+ private void sampleContents(Table table, Column column)
+ {
+ String tableName = table.getName();
+ StringBuilder queryBuilder = new StringBuilder("select "); //$NON-NLS-1$
+ if (column != null)
+ {
+ queryBuilder.append(column.getName());
+ }
+ else
+ {
+ queryBuilder.append("*"); //$NON-NLS-1$
+ }
+ queryBuilder.append(" from "); //$NON-NLS-1$
+ queryBuilder.append(tableName);
+
+ String queryString = queryBuilder.toString();
+ executeSingleStatement(queryString);
+ String cosummerName = null;
+ String dbName = null;
+ OperationCommand cmd =
+ new OperationCommand(OperationCommand.ACTION_EXECUTE, queryString, cosummerName,
+ connProfile.getName(), dbName);
+ ResultsViewAPI resultsView = ResultsViewAPI.getInstance();
+ resultsView.createNewInstance(cmd, null);
+ resultsView.appendStatusMessage(cmd, DbCoreNLS.DbModel_Sampling_Contents_From + tableName);
+ Connection managedSqlConnection = getManagedSqlConnection();
+ try
+ {
+ Statement statement = managedSqlConnection.createStatement();
+ ResultSet resultSet = statement.executeQuery(queryString);
+ resultsView.appendResultSet(cmd, resultSet);
+ resultsView.updateStatus(cmd, OperationCommand.STATUS_SUCCEEDED);
+ }
+ catch (SQLException e)
+ {
+ resultsView.appendThrowable(cmd, e);
+ resultsView.updateStatus(cmd, OperationCommand.STATUS_FAILED);
+ }
+ }
+
+ /**
+ * @param table
+ */
+ public void sampleContents(Table table)
+ {
+ sampleContents(table, null);
+ }
+
+ /**
+ * @param column
+ */
+ public void sampleContents(Column column)
+ {
+ sampleContents(column.getTable(), column);
+ }
+
+ /**
+ *
+ */
+ public static void cleanPreviousProfiles()
+ {
+ ProfileManager profileManager = ProfileManager.getInstance();
+ IConnectionProfile[] profiles = profileManager.getProfiles();
+ for (IConnectionProfile profile : profiles)
+ {
+ try
+ {
+ profileManager.deleteProfile(profile);
+ }
+ catch (ConnectionProfileException e)
+ {
+ error(DbModel.class, "Could not delete all profiles", e); //$NON-NLS-1$
+ }
+ }
+ }
+
+}
diff --git a/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/model/Field.java b/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/model/Field.java
new file mode 100644
index 0000000..095c24b
--- /dev/null
+++ b/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/model/Field.java
@@ -0,0 +1,220 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.studio.android.db.core.model;
+
+import com.motorolamobility.studio.android.db.core.i18n.DbCoreNLS;
+
+/**
+ * This class represents a table field
+ */
+public class Field
+{
+ /**
+ * This enum represents the auto increment value for primary key field.
+ * Use the toString method to retrieve the SQL syntax keyword
+ */
+ public enum AutoIncrementType
+ {
+ NONE("NONE"), ASCENDING("ASC"), DESCENDING("DESC"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+
+ private String strValue;
+
+ private AutoIncrementType(String strValue)
+ {
+ this.strValue = strValue;
+ }
+
+ /* (non-Javadoc)
+ * @see java.lang.Enum#toString()
+ */
+ @Override
+ public String toString()
+ {
+ return strValue;
+ }
+ }
+
+ /**
+ * This enum represents the DataType of a table field
+ * Use the toString method to retrieve the SQL syntax keyword
+ */
+ public enum DataType
+ {
+ INTEGER("INTEGER"), TEXT("TEXT"), REAL("REAL"), BLOB("BLOB"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
+
+ private String strValue;
+
+ private DataType(String dataType)
+ {
+ this.strValue = dataType;
+ }
+
+ /* (non-Javadoc)
+ * @see java.lang.Enum#toString()
+ */
+ @Override
+ public String toString()
+ {
+ return strValue;
+ }
+ }
+
+ private String name;
+
+ private boolean primaryKey;
+
+ private AutoIncrementType autoIncrementType;
+
+ private DataType type;
+
+ private String defaultValue;
+
+ @Override
+ public boolean equals(Object other)
+ {
+ boolean equals = false;
+
+ if (other instanceof Field)
+ {
+ equals = this.getName().equals(((Field) other).getName());
+ }
+
+ return equals;
+ }
+
+ /**
+ * Creates a new field representation based on the given params
+ * @param name
+ * @param primaryKey
+ * @param autoIncrementType
+ * @param type
+ * @param defaultValue
+ */
+ public Field(String name, DataType type, boolean primaryKey,
+ AutoIncrementType autoIncrementType, String defaultValue)
+ {
+ this.name = name;
+ this.primaryKey = primaryKey;
+ this.autoIncrementType = autoIncrementType;
+ this.type = type;
+ this.defaultValue = defaultValue;
+ }
+
+ public Field()
+ {
+ this("", DataType.TEXT, false, AutoIncrementType.NONE, null); //$NON-NLS-1$
+ }
+
+ /**
+ * @return the name
+ */
+ public String getName()
+ {
+ return name;
+ }
+
+ /**
+ * @param name the name to set
+ */
+ public void setName(String name)
+ {
+ this.name = name;
+ }
+
+ /**
+ * @return the primaryKey
+ */
+ public boolean isPrimaryKey()
+ {
+ return primaryKey;
+ }
+
+ /**
+ * @param primaryKey the primaryKey to set
+ */
+ public void setPrimaryKey(boolean primaryKey)
+ {
+ this.primaryKey = primaryKey;
+ }
+
+ /**
+ * @return the autoIncrementType
+ */
+ public AutoIncrementType getAutoIncrementType()
+ {
+ return autoIncrementType;
+ }
+
+ /**
+ * @param autoIncrementType the autoIncrementType to set
+ */
+ public void setAutoIncrementType(AutoIncrementType autoIncrementType)
+ {
+ this.autoIncrementType = autoIncrementType;
+ }
+
+ /**
+ * @return the type
+ */
+ public DataType getType()
+ {
+ return type;
+ }
+
+ /**
+ * @param type the type to set
+ */
+ public void setType(DataType type)
+ {
+ this.type = type;
+ }
+
+ /**
+ * @return the defaultValue
+ */
+ public String getDefaultValue()
+ {
+ return defaultValue;
+ }
+
+ /**
+ * @param defaultValue the defaultValue to set
+ */
+ public void setDefaultValue(String defaultValue)
+ {
+ this.defaultValue = defaultValue;
+ }
+
+ public String getErrorMessage()
+ {
+ String message = null;
+
+ if (!type.equals(DataType.INTEGER) && !type.equals(DataType.REAL) //$NON-NLS-1$ //$NON-NLS-2$
+ && !autoIncrementType.equals(AutoIncrementType.NONE))
+ {
+ message = DbCoreNLS.Field_ErrorAutoIncrementNotAllowed;
+ }
+
+ // Validate name to don't use sqlite keywords
+ if ((message == null) && !TableModel.validateName(getName()))
+ {
+
+ message = DbCoreNLS.AddTableFieldDialog_InvalidName + getName();
+ }
+
+ return message;
+ }
+}
diff --git a/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/model/TableModel.java b/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/model/TableModel.java
new file mode 100644
index 0000000..a592e97
--- /dev/null
+++ b/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/model/TableModel.java
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.studio.android.db.core.model;
+
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+
+import com.motorolamobility.studio.android.db.core.i18n.DbCoreNLS;
+
+/**
+ * This class represents a database table
+ */
+public class TableModel
+{
+
+ private String name = "SampleTable";
+
+ private List<Field> fields;
+
+ public TableModel()
+ {
+ fields = new LinkedList<Field>();
+ }
+
+ /**
+ * Creates a new Table representation
+ * @param name The table name
+ * @param fields The table fields
+ */
+ public TableModel(String name, List<Field> fields)
+ {
+ this.name = name;
+ this.fields = fields;
+ }
+
+ /**
+ * @return the name
+ */
+ public String getName()
+ {
+ return name;
+ }
+
+ /**
+ * @param name the name to set
+ */
+ public void setName(String name)
+ {
+ this.name = name;
+ }
+
+ /**
+ * @return the fields
+ */
+ public List<Field> getFields()
+ {
+ return fields;
+ }
+
+ /**
+ * @param fields the fields to set
+ */
+ public void setFields(List<Field> fields)
+ {
+ this.fields = fields;
+ }
+
+ public void addField(Field field)
+ {
+ fields.add(field);
+
+ }
+
+ public void removeField(Field field)
+ {
+ fields.remove(field);
+
+ }
+
+ /**
+ * Validates the name to be different from SQLite keywords.
+ * Reference: http://www.sqlite.org/lang_keywords.html
+ *
+ * @param name
+ * The table name
+ * @return True is a valid table name, false otherwise
+ */
+ public static boolean validateName(String name)
+ {
+
+ boolean isValid = true;
+ String[] keywords =
+ {
+ "ABORT", "ACTION", "ADD", "AFTER", "ALL", "ALTER", "ANALYZE", "AND", "AS",
+ "ASC", "ATTACH", "AUTOINCREMENT", "BEFORE", "BEGIN", "BETWEEN", "BY",
+ "CASCADE", "CASE", "CAST", "CHECK", "COLLATE", "COLUMN", "COMMIT",
+ "CONFLICT", "CONSTRAINT", "CREATE", "CROSS", "CURRENT_DATE",
+ "CURRENT_TIME", "CURRENT_TIMESTAMP", "DATABASE", "DEFAULT", "DEFERRABLE",
+ "DEFERRED", "DELETE", "DESC", "DETACH", "DISTINCT", "DROP", "EACH", "ELSE",
+ "END", "ESCAPE", "EXCEPT", "EXCLUSIVE", "EXISTS", "EXPLAIN", "FAIL", "FOR",
+ "FOREIGN", "FROM", "FULL", "GLOB", "GROUP", "HAVING", "IF", "IGNORE",
+ "IMMEDIATE", "IN", "INDEX", "INDEXED", "INITIALLY", "INNER", "INSERT",
+ "INSTEAD", "INTERSECT", "INTO", "IS", "ISNULL", "JOIN", "KEY", "LEFT",
+ "LIKE", "LIMIT", "MATCH", "NATURAL", "NO", "NOT", "NOTNULL", "NULL", "OF",
+ "OFFSET", "ON", "OR", "ORDER", "OUTER", "PLAN", "PRAGMA", "PRIMARY",
+ "QUERY", "RAISE", "REFERENCES", "REGEXP", "REINDEX", "RELEASE", "RENAME",
+ "REPLACE", "RESTRICT", "RIGHT", "ROLLBACK", "ROW", "SAVEPOINT", "SELECT",
+ "SET", "TABLE", "TEMP", "TEMPORARY", "THEN", "TO", "TRANSACTION",
+ "TRIGGER", "UNION", "UNIQUE", "UPDATE", "USING", "VACUUM", "VALUES",
+ "VIEW", "VIRTUAL", "WHEN", "WHERE"
+ };
+
+ for (String keyword : keywords)
+ {
+ // Found. It is a keyword
+ if (keyword.toLowerCase().compareTo(name.toLowerCase()) == 0)
+ {
+ isValid = false;
+ }
+ }
+
+ return isValid;
+ }
+
+ public String getErrorMessage()
+ {
+
+ Iterator<Field> it = fields.iterator();
+ String msg = null;
+
+ while (it.hasNext() && (msg == null))
+ {
+ Field field = it.next();
+
+ msg = field.getErrorMessage();
+
+ if ((msg == null) && field.getName().trim().contains(" ")) //$NON-NLS-1$
+ {
+ msg = DbCoreNLS.Table_ErrorUnamedColumns;
+ }
+ else if (msg == null)
+ {
+
+ Iterator<Field> iterator = fields.iterator();
+
+ while (iterator.hasNext() && (msg == null))
+ {
+ Field testField = iterator.next();
+ if (field != testField)
+ {
+ if (field.getName().equalsIgnoreCase(testField.getName()))
+ {
+ msg = DbCoreNLS.Table_ErrorConflictingNames + field.getName() + ", " //$NON-NLS-2$
+ + testField.getName();
+ }
+ else if (field.isPrimaryKey() && testField.isPrimaryKey())
+ {
+ msg =
+ DbCoreNLS.Table_ErrorMoreThanOnePrimaryKey + field.getName()
+ + ", " + testField.getName(); //$NON-NLS-1$
+ }
+
+ }
+
+ }
+ }
+ }
+ return msg;
+ }
+}
diff --git a/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/project/ProjectNode.java b/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/project/ProjectNode.java
new file mode 100644
index 0000000..4bfa141
--- /dev/null
+++ b/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/project/ProjectNode.java
@@ -0,0 +1,408 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.studio.android.db.core.project;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IFolder;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.IResourceChangeEvent;
+import org.eclipse.core.resources.IResourceChangeListener;
+import org.eclipse.core.resources.IResourceDelta;
+import org.eclipse.core.resources.IResourceDeltaVisitor;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.osgi.util.NLS;
+
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.common.utilities.EclipseUtils;
+import com.motorolamobility.studio.android.db.core.DbCoreActivator;
+import com.motorolamobility.studio.android.db.core.event.DatabaseModelEvent;
+import com.motorolamobility.studio.android.db.core.event.DatabaseModelEventManager;
+import com.motorolamobility.studio.android.db.core.exception.MotodevDbException;
+import com.motorolamobility.studio.android.db.core.i18n.DbCoreNLS;
+import com.motorolamobility.studio.android.db.core.model.DbModel;
+import com.motorolamobility.studio.android.db.core.model.TableModel;
+import com.motorolamobility.studio.android.db.core.ui.AbstractTreeNode;
+import com.motorolamobility.studio.android.db.core.ui.DbNode;
+import com.motorolamobility.studio.android.db.core.ui.IDbNode;
+import com.motorolamobility.studio.android.db.core.ui.ITreeNode;
+import com.motorolamobility.studio.android.db.core.ui.action.IDbCreatorNode;
+
+/**
+ * Implements the Project Node of the database tree view.
+ * It listens for changes in project such as .db deletion/rename/addition that reflect in database model.
+ */
+public class ProjectNode extends AbstractTreeNode implements IDbCreatorNode,
+ IResourceChangeListener
+{
+
+ public static final String DB_FOLDER = "assets"; //$NON-NLS-1$
+
+ private IProject project;
+
+ @SuppressWarnings("unused")
+ private ProjectNode()
+ {
+ //Forcing user to use a proper constructor
+ }
+
+ public ProjectNode(IProject project, ITreeNode parent)
+ {
+ this(project.getName(), parent);
+ this.project = project;
+ }
+
+ public ProjectNode(String id, ITreeNode parent)
+ {
+
+ super(parent);
+ init(id);
+ ResourcesPlugin.getWorkspace().addResourceChangeListener(this,
+ IResourceChangeEvent.POST_CHANGE);
+ }
+
+ @Override
+ public void refresh()
+ {
+ clear();
+ loadContent();
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorolamobility.studio.android.db.core.ui.IDbCreatorNode#createDb(java.lang.String)
+ */
+ public IStatus createDb(String dbName)
+ {
+ return createDb(dbName, null);
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorolamobility.studio.android.db.core.ui.IDbCreatorNode#createDb(java.lang.String, java.util.List)
+ */
+ public IStatus createDb(String dbName, List<TableModel> tables)
+ {
+ IStatus status = Status.OK_STATUS;
+ dbName = !dbName.endsWith(".db") ? dbName + ".db" : dbName; //$NON-NLS-1$ //$NON-NLS-2$
+ IPath projectPath = project.getLocation();
+ File assetsFolder = new File(projectPath.toFile(), "assets"); //$NON-NLS-1$
+ if (!assetsFolder.exists())
+ {
+ assetsFolder.mkdirs();
+ }
+ IPath dbPath = new Path(assetsFolder.getAbsolutePath()).append(File.separator + dbName);
+ try
+ {
+ DbNode dbNode = new DbNode(dbPath, this, true);
+ if (tables != null)
+ {
+ status = dbNode.createTables(tables);
+ }
+ putChild(dbNode);
+ }
+ catch (MotodevDbException e)
+ {
+ status =
+ new Status(IStatus.ERROR, DbCoreActivator.PLUGIN_ID, NLS.bind(
+ DbCoreNLS.ProjectNode_Error_While_Creating_DB, dbName), e);
+ }
+
+ return status;
+ }
+
+ private void init(String id)
+ {
+ setId(id);
+ setName(id);
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorolamobility.studio.android.db.core.ui.AbstractTreeNode#getIcon()
+ */
+ @Override
+ public ImageDescriptor getIcon()
+ {
+ return getSpecificIcon("com.android.ide.eclipse.adt", "icons/android.png"); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+
+ /**
+ * Loads the db files from the Android projects of the current workspace
+ * @return list of db nodes
+ */
+ private void loadContent()
+ {
+ File folder =
+ ResourcesPlugin.getWorkspace().getRoot().getProject(getName()).getFile(DB_FOLDER)
+ .getLocation().toFile();
+ if (folder.exists() && (folder.list().length > 0))
+ {
+ File[] foundFiles = folder.listFiles();
+ List<ITreeNode> dbNodes = new ArrayList<ITreeNode>(foundFiles.length);
+ for (File file : foundFiles)
+ {
+ if (DbModel.isValidSQLiteDatabase(file))
+ {
+ DbNode dbNode;
+ try
+ {
+ dbNode = new DbNode(new Path(file.getAbsolutePath()), this);
+ dbNodes.add(dbNode);
+ }
+ catch (MotodevDbException e)
+ {
+ //Invalid db file found do nothing with it.
+ }
+ }
+ }
+ putChildren(dbNodes);
+ }
+
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorolamobility.studio.android.db.core.ui.AbstractTreeNode#isLeaf()
+ */
+ @Override
+ public boolean isLeaf()
+ {
+ return false;
+ }
+
+ /**
+ * @return the project
+ */
+ protected IProject getProject()
+ {
+ return project;
+ }
+
+ /**
+ *
+ * @param file
+ * @return
+ * @throws MotodevDbException if dbPath does not contains a valid SQLite3 database file
+ */
+ public void addDb(File file)
+ {
+ try
+ {
+ if (DbModel.isValidSQLiteDatabase(file))
+ {
+ IDbNode dN = null;
+ Path dbPath = new Path(file.getAbsolutePath());
+ dN = getDatabaseNodeFromFile(file);
+ if (dN == null)
+ {
+ dN = new DbNode(dbPath, this);
+ putChild(dN);
+ }
+ }
+ }
+ catch (MotodevDbException e)
+ {
+ String message =
+ NLS.bind(DbCoreNLS.ProjectNode_Failed_ToVerify_If_DB_Is_Valid, file.getName());
+ StudioLogger.error(ProjectNode.class, message, e);
+ }
+ }
+
+ /**
+ * Deletes the database from filesystem
+ */
+ public IStatus deleteDb(IDbNode dbNode)
+ {
+ IStatus status = dbNode.deleteDb();
+
+ if (status.isOK())
+ {
+ removeDb(dbNode);
+ }
+ return status;
+ }
+
+ /**
+ * Removes the node from database (e.g. when project is closed)
+ * @param dbNode
+ * @param dbName
+ */
+ public void removeDb(IDbNode dbNode)
+ {
+ removeChild(dbNode);
+ }
+
+ protected void updateDatabase(File dbFile)
+ {
+ ITreeNode databaseNode = getDatabaseNodeFromFile(dbFile);
+ if (databaseNode != null)
+ {
+ DatabaseModelEventManager.getInstance().fireEvent(databaseNode,
+ DatabaseModelEvent.EVENT_TYPE.UPDATE);
+ }
+ }
+
+ private IDbNode getDatabaseNodeFromFile(File dbFile)
+ {
+ Path dbFilePath = new Path(dbFile.getAbsolutePath());
+ String dbFileName = dbFilePath.lastSegment();
+ String id = dbFilePath.toFile().getParent() + "." + dbFileName; //$NON-NLS-1$
+ IDbNode databaseNode = null;
+ ITreeNode node = getChildById(id);
+ if (node instanceof IDbNode)
+ {
+ databaseNode = (IDbNode) node;
+ }
+ return databaseNode;
+ }
+
+ public void resourceChanged(IResourceChangeEvent event)
+ {
+ IResource res = event.getResource();
+ switch (event.getType())
+ {
+ case IResourceChangeEvent.PRE_DELETE:
+ //listener warns user about an opened database connection related to this project (when it tries to delete the project).
+ //The user has to close the connection itself.
+ boolean openedConnection = false;
+ try
+ {
+ if (res instanceof IProject)
+ {
+ IProject project = (IProject) res;
+ IFolder assetsFolder = project.getFolder(DB_FOLDER);
+ if (assetsFolder.exists())
+ {
+ IResource assetsRes[] = assetsFolder.members();
+ for (int i = 0; i < assetsRes.length; i++)
+ {
+ if (assetsRes[i] instanceof IFile)
+ {
+ IFile f = (IFile) assetsRes[i];
+ IDbNode dbNode =
+ getDatabaseNodeFromFile(f.getLocation().toFile());
+ if (dbNode.isConnected())
+ {
+ openedConnection = true;
+ }
+ }
+ }
+ }
+ }
+ }
+ catch (Exception e)
+ {
+ //do nothing
+ }
+ if (openedConnection)
+ {
+ EclipseUtils.showWarningDialog(DbCoreNLS.UI_DeleteProjectDialogTitle,
+ DbCoreNLS.UI_DeleteProjectDialogMsg);
+ }
+
+ break;
+ case IResourceChangeEvent.POST_CHANGE:
+ try
+ {
+ event.getDelta().accept(new ResourceDeltaVisior(this));
+ }
+ catch (CoreException e)
+ {
+ StudioLogger.error(ProjectNode.class,
+ "Error listening to changes in resources", e); //$NON-NLS-1$
+ }
+ break;
+ }
+
+ }
+
+ /**
+ * Refresh assets folder under the android project.
+ * @return true if success false otherwise.
+ */
+ public boolean refreshAssetsFolder()
+ {
+ IFolder assetsFolder = this.project.getFolder(DB_FOLDER);
+ if (assetsFolder.exists())
+ {
+ try
+ {
+ assetsFolder.refreshLocal(IResource.DEPTH_INFINITE, null);
+ }
+ catch (CoreException e)
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ class ResourceDeltaVisior implements IResourceDeltaVisitor
+ {
+ private final ProjectNode projectNode;
+
+ ResourceDeltaVisior(ProjectNode projectNode)
+ {
+ this.projectNode = projectNode;
+ }
+
+ public boolean visit(IResourceDelta delta)
+ {
+ IResource res = delta.getResource();
+ if ((res instanceof IResource) && (res.getFileExtension() != null)
+ && res.getProject().equals(projectNode.getProject())
+ && res.getFileExtension().equalsIgnoreCase("db")) //$NON-NLS-1$
+ {
+ //avoid to add db to the wrong project - it may happen in case of copying db from one project to another
+ switch (delta.getKind())
+ {
+ case IResourceDelta.ADDED:
+ StudioLogger.info("Database added: " + res.getFullPath()); //$NON-NLS-1$
+ addDb(res.getLocation().toFile());
+ break;
+ case IResourceDelta.REMOVED:
+ StudioLogger.info("Database deleted: " + res.getFullPath()); //$NON-NLS-1$
+ IDbNode dbNode = getDatabaseNodeFromFile(res.getLocation().toFile());
+ if (dbNode != null)
+ {
+ removeDb(dbNode);
+ }
+ break;
+ }
+ }
+
+ return true; // visit the children
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorolamobility.studio.android.db.core.ui.AbstractTreeNode#clean()
+ */
+ @Override
+ public void cleanUp()
+ {
+ super.cleanUp();
+ ResourcesPlugin.getWorkspace().removeResourceChangeListener(this);
+ }
+
+}
diff --git a/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/ui/AbstractDbResultManagerAdapter.java b/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/ui/AbstractDbResultManagerAdapter.java
new file mode 100644
index 0000000..9278193
--- /dev/null
+++ b/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/ui/AbstractDbResultManagerAdapter.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.studio.android.db.core.ui;
+
+import java.util.List;
+
+import org.eclipse.datatools.sqltools.result.OperationCommand;
+import org.eclipse.datatools.sqltools.result.core.IResultManagerListener;
+import org.eclipse.datatools.sqltools.result.model.IResultInstance;
+import org.eclipse.datatools.sqltools.result.model.ResultItem;
+
+public abstract class AbstractDbResultManagerAdapter implements IResultManagerListener
+{
+
+ /* (non-Javadoc)
+ * @see org.eclipse.datatools.sqltools.result.core.IResultManagerListener#resultInstanceCreated(org.eclipse.datatools.sqltools.result.model.IResultInstance)
+ */
+ public void resultInstanceCreated(IResultInstance instance)
+ {
+ //Do nothing.
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.datatools.sqltools.result.core.IResultManagerListener#resultInstanceRemoved(org.eclipse.datatools.sqltools.result.model.IResultInstance)
+ */
+ public void resultInstanceRemoved(IResultInstance instance)
+ {
+ //Do nothing.
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.datatools.sqltools.result.core.IResultManagerListener#resultInstancesRemoved(org.eclipse.datatools.sqltools.result.model.IResultInstance[])
+ */
+ public void resultInstancesRemoved(IResultInstance[] instances)
+ {
+ //Do nothing.
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.datatools.sqltools.result.core.IResultManagerListener#resultInstanceAppended(org.eclipse.datatools.sqltools.result.model.IResultInstance, org.eclipse.datatools.sqltools.result.model.ResultItem, int)
+ */
+ public void resultInstanceAppended(IResultInstance instance, ResultItem result, int index)
+ {
+ //Do nothing.
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.datatools.sqltools.result.core.IResultManagerListener#allResultInstancesRemoved()
+ */
+ public void allResultInstancesRemoved()
+ {
+ //Do nothing.
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.datatools.sqltools.result.core.IResultManagerListener#resultInstanceStatusUpdated(org.eclipse.datatools.sqltools.result.model.IResultInstance)
+ */
+ public void resultInstanceStatusUpdated(IResultInstance instance)
+ {
+ if (instance.getStatus() == OperationCommand.STATUS_SUCCEEDED)
+ {
+ OperationCommand cmd = instance.getOperationCommand();
+ String profilename = cmd.getProfileName();
+ String sqlStatement = cmd.getDisplayString();
+ statementExecuted(profilename, sqlStatement);
+ }
+ }
+
+ /**
+ * This method will be called everytime a statement is successfully executed
+ * @param profilename the name of the connection profile
+ * @param sqlStatement the statement executed
+ */
+ public abstract void statementExecuted(String profilename, String sqlStatement);
+
+ /* (non-Javadoc)
+ * @see org.eclipse.datatools.sqltools.result.core.IResultManagerListener#resultInstanceReset(org.eclipse.datatools.sqltools.result.model.IResultInstance)
+ */
+ public void resultInstanceReset(IResultInstance instance)
+ {
+ //Do nothing.
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.datatools.sqltools.result.core.IResultManagerListener#parametersShow(org.eclipse.datatools.sqltools.result.model.IResultInstance, java.util.List)
+ */
+ @SuppressWarnings("rawtypes")
+ public void parametersShow(IResultInstance instance, List params)
+ {
+ //Do nothing.
+ }
+
+}
diff --git a/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/ui/AbstractLoadingNodeJob.java b/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/ui/AbstractLoadingNodeJob.java
new file mode 100644
index 0000000..d1ce89d
--- /dev/null
+++ b/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/ui/AbstractLoadingNodeJob.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.studio.android.db.core.ui;
+
+import org.eclipse.core.runtime.jobs.Job;
+
+/**
+ * This class represents a job that must be used in order to load the model in background.
+ * Caller must implement the run method with all logic to load the node children.
+ * There's no need to take care of loading node and/or setting the node loading flag.
+ * There's also no need to add a jobChange listener.
+ */
+public abstract class AbstractLoadingNodeJob extends Job
+{
+ protected ITreeNode node = null;
+
+ public AbstractLoadingNodeJob(String name, ITreeNode node)
+ {
+ super(name);
+ this.node = node;
+ addJobChangeListener(new LoadingJobListener());
+ }
+
+ /**
+ * @return the node
+ */
+ public ITreeNode getNode()
+ {
+ return node;
+ }
+
+} \ No newline at end of file
diff --git a/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/ui/AbstractTreeNode.java b/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/ui/AbstractTreeNode.java
new file mode 100644
index 0000000..f7191e5
--- /dev/null
+++ b/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/ui/AbstractTreeNode.java
@@ -0,0 +1,459 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.studio.android.db.core.ui;
+
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.preferences.IEclipsePreferences;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.jface.viewers.ILazyTreeContentProvider;
+import org.eclipse.osgi.util.NLS;
+import org.osgi.framework.Bundle;
+
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorolamobility.studio.android.db.core.DbCoreActivator;
+import com.motorolamobility.studio.android.db.core.event.DatabaseModelEvent;
+import com.motorolamobility.studio.android.db.core.event.DatabaseModelEvent.EVENT_TYPE;
+import com.motorolamobility.studio.android.db.core.event.DatabaseModelEventManager;
+import com.motorolamobility.studio.android.db.core.i18n.DbCoreNLS;
+import com.motorolamobility.studio.android.db.core.ui.view.SaveStateManager;
+
+/**
+ * Node abstraction to be contributed/implemented through extension point com.motorolamobility.studio.android.db.core.dbRootNode.
+ */
+public abstract class AbstractTreeNode implements ITreeNode
+{
+ /**
+ * Property value used to check if the node has an error status.
+ */
+ public static final String PROP_VALUE_NODE_STATUS_ERROR =
+ "com.motorolamobility.studio.android.db.core.nodeStatusError"; //$NON-NLS-1$
+
+ /**
+ * Property name used to test the status of the node.
+ */
+ public static final String PROP_NAME_NODE_STATUS =
+ "com.motorolamobility.studio.android.db.core.nodeStatus"; //$NON-NLS-1$
+
+ private static final String DEFAULT_ICON_PATH = "icons/obj16/plate16.png"; //$NON-NLS-1$
+
+ /*
+ * id, name, and icon will come from extension point com.motorolamobility.studio.android.db.core.dbRootNode
+ */
+ private String id;
+
+ private String name;
+
+ private ITreeNode parent;
+
+ private ImageDescriptor icon = DbCoreActivator.imageDescriptorFromPlugin(
+ DbCoreActivator.PLUGIN_ID, DEFAULT_ICON_PATH);
+
+ private IStatus nodeStatus = Status.OK_STATUS;
+
+ /**
+ * We need to guarantee the order of the inserted items to use {@link ILazyTreeContentProvider}
+ * We need to guarantee the change operations are thread-safe by using synchronized list
+ */
+ private final List<ITreeNode> children = new CopyOnWriteArrayList<ITreeNode>();
+
+ private volatile boolean isLoading = false;
+
+ private String tooltip;
+
+ /**
+ * Default constructor
+ *
+ * Warning: If the node comes is declared through extension point com.motorolamobility.studio.android.db.core.dbRootNode
+ * this constructor is mandatory to exist and be the unique to be used.
+ */
+ public AbstractTreeNode()
+ {
+ this(null, null, null);
+ }
+
+ public AbstractTreeNode(ITreeNode parent)
+ {
+ this(null, null, parent);
+ }
+
+ public AbstractTreeNode(String id, String name, ITreeNode parent)
+ {
+ this(id, name, parent, null);
+ }
+
+ public AbstractTreeNode(String id, String name, ITreeNode parent, ImageDescriptor icon)
+ {
+ this.id = id;
+ this.name = name;
+ this.parent = parent;
+ this.icon = icon;
+
+ if (this instanceof ISaveStateTreeNode)
+ {
+ ISaveStateTreeNode saveStateTreeNode = (ISaveStateTreeNode) this;
+ SaveStateManager saveStateManager = SaveStateManager.getInstance();
+ saveStateManager.registerSaveStateNode(saveStateTreeNode);
+ IEclipsePreferences prefNode = saveStateManager.getPrefNode();
+ if (prefNode != null)
+ {
+ saveStateTreeNode.restoreState(prefNode);
+ }
+ }
+ }
+
+ public final void refreshAsync()
+ {
+ refreshAsync(true);
+ }
+
+ public final void refreshAsync(final boolean canRefreshYesResponse)
+ {
+ if (!isLoading())
+ {
+ AbstractLoadingNodeJob loadingJob =
+ new AbstractLoadingNodeJob(NLS.bind(
+ DbCoreNLS.AbstractTreeNode_Loading_Job_Name, getName()), this)
+ {
+ @Override
+ protected IStatus run(IProgressMonitor monitor)
+ {
+ refresh(canRefreshYesResponse);
+ return Status.OK_STATUS;
+ }
+ };
+ loadingJob.schedule();
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorolamobility.studio.android.db.core.ui.ITreeNode#refresh()
+ */
+ public abstract void refresh();
+
+ /**
+ * @param canRefreshYesResponse
+ */
+ public void refresh(boolean canRefreshYesResponse)
+ {
+ refresh();
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorolamobility.studio.android.db.core.ui.ITreeNode#getParent()
+ */
+ public ITreeNode getParent()
+ {
+ return parent;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorolamobility.studio.android.db.core.ui.ITreeNode#setParent(com.motorolamobility.studio.android.db.core.ui.ITreeNode)
+ */
+ public void setParent(ITreeNode parent)
+ {
+ this.parent = parent;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorolamobility.studio.android.db.core.ui.ITreeNode#getChildren()
+ */
+ public List<ITreeNode> getChildren()
+ {
+ return children;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorolamobility.studio.android.db.core.ui.ITreeNode#clear()
+ */
+ public void clear()
+ {
+ DatabaseModelEventManager.getInstance().fireEvent(this, EVENT_TYPE.CLEAR);
+ for (ITreeNode child : getChildren())
+ {
+ child.cleanUp();
+ child.clear();
+ }
+ children.clear();
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorolamobility.studio.android.db.core.ui.ITreeNode#getChild(int)
+ */
+ public ITreeNode getChild(int index)
+ {
+ return children.size() > index ? children.get(index) : null;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorolamobility.studio.android.db.core.ui.ITreeNode#getChildById(String)
+ */
+ public ITreeNode getChildById(String id)
+ {
+ ITreeNode foundChild = null;
+ for (ITreeNode node : children)
+ {
+ if ((node != null) && (node.getId() != null) && node.getId().equals(id))
+ {
+ foundChild = node;
+ break;
+ }
+ }
+ return foundChild;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorolamobility.studio.android.db.core.ui.ITreeNode#getFilteredChildren(java.lang.String)
+ */
+ public List<ITreeNode> getFilteredChildren(String regex)
+ {
+ List<ITreeNode> filteredChildren = new ArrayList<ITreeNode>();
+ if (regex == null)
+ {
+ filteredChildren = getChildren();
+ }
+ else
+ {
+ for (ITreeNode child : children)
+ {
+ if ((regex != null) && child.getId().matches(regex))
+ {
+ filteredChildren.add(child);
+ }
+ }
+ }
+ return filteredChildren;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorolamobility.studio.android.db.core.ui.ITreeNode#putChild(java.lang.String, com.motorolamobility.studio.android.db.core.ui.AbstractTreeNode)
+ */
+ public void putChild(ITreeNode treeNode)
+ {
+ treeNode.setParent(this);
+ //node does not exist yet as a child => add it
+ children.add(treeNode);
+ DatabaseModelEventManager.getInstance().fireEvent(treeNode,
+ DatabaseModelEvent.EVENT_TYPE.ADD);
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorolamobility.studio.android.db.core.ui.ITreeNode#putChildren(java.util.List)
+ */
+ public void putChildren(List<ITreeNode> childrenList)
+ {
+ children.addAll(childrenList);
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorolamobility.studio.android.db.core.ui.ITreeNode#removeChild(ITreeNode)
+ */
+ public void removeChild(ITreeNode node)
+ {
+ node.cleanUp();
+ children.remove(node);
+ DatabaseModelEventManager.getInstance().fireEvent(node,
+ DatabaseModelEvent.EVENT_TYPE.REMOVE);
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorolamobility.studio.android.db.core.ui.ITreeNode#isLoading()
+ */
+ public boolean isLoading()
+ {
+ return isLoading;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorolamobility.studio.android.db.core.ui.ITreeNode#setLoading(boolean)
+ */
+ public void setLoading(boolean isLoading)
+ {
+ this.isLoading = isLoading;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorolamobility.studio.android.db.core.ui.ITreeNode#getId()
+ */
+ public String getId()
+ {
+ return id;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorolamobility.studio.android.db.core.ui.ITreeNode#setId(java.lang.String)
+ */
+ public void setId(String id)
+ {
+ this.id = id;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorolamobility.studio.android.db.core.ui.ITreeNode#getName()
+ */
+ public String getName()
+ {
+ return name;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorolamobility.studio.android.db.core.ui.ITreeNode#setName(java.lang.String)
+ */
+ public void setName(String name)
+ {
+ this.name = name;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorolamobility.studio.android.db.core.ui.ITreeNode#getIcon()
+ */
+ public ImageDescriptor getIcon()
+ {
+ return icon;
+ }
+
+ /**
+ * Retrieves an icon image descriptor from other Eclipse bundles (JDT, Datatools, ADT, etc).
+ *
+ * @param bundleId The id of the bundle where the icon should be retrieved from
+ * @param iconPath The path of the icon inside the bundle
+ *
+ * @return The image descriptor for the desired icon, or the default icon from the db code plugin
+ * in case the process of retrieving the desired icon fails
+ */
+ protected final ImageDescriptor getSpecificIcon(String bundleId, String iconPath)
+ {
+ ImageDescriptor imgDesc = null;
+
+ try
+ {
+ Bundle bundle = Platform.getBundle(bundleId);
+ URL path = bundle.getEntry("/"); //$NON-NLS-1$
+ URL fullPathString = new URL(path, iconPath);
+ imgDesc = ImageDescriptor.createFromURL(fullPathString);
+ }
+ catch (Exception e)
+ {
+ StudioLogger.error(getClass(), "Error retrieving icon for tree node", e); //$NON-NLS-1$
+ }
+ if (imgDesc == null)
+ {
+ imgDesc = getIcon();
+ }
+
+ return imgDesc;
+
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorolamobility.studio.android.db.core.ui.ITreeNode#setIcon(org.eclipse.jface.resource.ImageDescriptor)
+ */
+ public void setIcon(ImageDescriptor icon)
+ {
+ this.icon = icon;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorolamobility.studio.android.db.core.ui.ITreeNode#canRefresh()
+ */
+ public IStatus canRefresh()
+ {
+ return Status.OK_STATUS;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorolamobility.studio.android.db.core.ui.ITreeNode#isLeaf()
+ */
+ public abstract boolean isLeaf();
+
+ /* (non-Javadoc)
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString()
+ {
+ return "AbstractTreeNode [id=" + id + ", name=" + name + ", parent=" + parent + "]"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorolamobility.studio.android.db.core.ui.ITreeNode#clean()
+ */
+ public void cleanUp()
+ {
+ for (ITreeNode node : children)
+ {
+ node.cleanUp();
+ }
+ if (this instanceof ISaveStateTreeNode)
+ {
+ SaveStateManager.getInstance().unregisterSaveStateNode((ISaveStateTreeNode) this);
+ }
+ }
+
+ public void setNodeStatus(IStatus status)
+ {
+ nodeStatus = status;
+ //update node label/icon/decoration given that its status has changed
+ DatabaseModelEventManager.getInstance().fireEvent(this, EVENT_TYPE.UPDATE);
+ }
+
+ public IStatus getNodeStatus()
+ {
+ return nodeStatus;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.IActionFilter#testAttribute(java.lang.Object, java.lang.String, java.lang.String)
+ */
+ public boolean testAttribute(Object target, String name, String value)
+ {
+ boolean result = false;
+
+ if (name.equals(PROP_NAME_NODE_STATUS))
+ {
+ if (value.equals(PROP_VALUE_NODE_STATUS_ERROR))
+ {
+ result = !getNodeStatus().isOK();
+ }
+ }
+
+ return result;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorolamobility.studio.android.db.core.ui.ITreeNode#setTooltip(java.lang.String)
+ */
+ public void setTooltip(String tooltip)
+ {
+ this.tooltip = tooltip;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorolamobility.studio.android.db.core.ui.ITreeNode#getTooltip()
+ */
+ public String getTooltip()
+ {
+ return tooltip;
+ }
+
+}
diff --git a/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/ui/ColumnNode.java b/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/ui/ColumnNode.java
new file mode 100644
index 0000000..a5f33e1
--- /dev/null
+++ b/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/ui/ColumnNode.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.studio.android.db.core.ui;
+
+import org.eclipse.datatools.modelbase.sql.tables.Column;
+import org.eclipse.jface.resource.ImageDescriptor;
+
+import com.motorolamobility.studio.android.db.core.i18n.DbCoreNLS;
+import com.motorolamobility.studio.android.db.core.model.DbModel;
+
+public class ColumnNode extends AbstractTreeNode implements IDataSampler
+{
+ private boolean isPrimKey = false;
+
+ private final Column column;
+
+ private final DbModel model;
+
+ /**
+ * @param column
+ * @param tableNode
+ */
+ public ColumnNode(Column column, DbModel model, ITreeNode parent)
+ {
+ super(parent);
+ this.column = column;
+ this.model = model;
+ setId(column.getName());
+
+ StringBuilder nameBuilder =
+ column.getDataType() != null ? new StringBuilder(column.getName() + " [" //$NON-NLS-1$
+ + column.getDataType().getName()) : new StringBuilder(column.getName()
+ + " [" + DbCoreNLS.ColumnNode_UnknownType); //$NON-NLS-1$
+ if (column.isPartOfPrimaryKey())
+ {
+ nameBuilder.append(" PK"); //$NON-NLS-1$
+ isPrimKey = true;
+ }
+ nameBuilder.append("]"); //$NON-NLS-1$
+ setName(nameBuilder.toString());
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorolamobility.studio.android.db.core.ui.AbstractTreeNode#refresh()
+ */
+ @Override
+ public void refresh()
+ {
+ // Do nothing!
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorolamobility.studio.android.db.core.ui.AbstractTreeNode#isLeaf()
+ */
+ @Override
+ public boolean isLeaf()
+ {
+ return true;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorolamobility.studio.android.db.core.ui.AbstractTreeNode#getIcon()
+ */
+ @Override
+ public ImageDescriptor getIcon()
+ {
+ String iconPath;
+ if (isPrimKey)
+ {
+ iconPath = "icons/pkColumn.gif"; //$NON-NLS-1$
+ }
+ else
+ {
+ iconPath = "icons/columns.gif"; //$NON-NLS-1$
+ }
+ return getSpecificIcon("org.eclipse.datatools.connectivity.sqm.core.ui", //$NON-NLS-1$
+ iconPath);
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorolamobility.studio.android.db.core.ui.IDataSampler#sampleDbContents()
+ */
+ public void sampleDbContents()
+ {
+ model.sampleContents(column);
+ }
+
+}
diff --git a/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/ui/DbNode.java b/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/ui/DbNode.java
new file mode 100644
index 0000000..5faa9c0
--- /dev/null
+++ b/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/ui/DbNode.java
@@ -0,0 +1,561 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.studio.android.db.core.ui;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.datatools.connectivity.ConnectionProfileException;
+import org.eclipse.datatools.modelbase.sql.tables.Table;
+import org.eclipse.datatools.sqltools.result.ResultsViewAPI;
+import org.eclipse.datatools.sqltools.result.core.IResultManagerListener;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.IWorkbenchPage;
+
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.common.utilities.EclipseUtils;
+import com.motorolamobility.studio.android.db.core.DbCoreActivator;
+import com.motorolamobility.studio.android.db.core.exception.MotodevDbException;
+import com.motorolamobility.studio.android.db.core.i18n.DbCoreNLS;
+import com.motorolamobility.studio.android.db.core.model.DbModel;
+import com.motorolamobility.studio.android.db.core.model.TableModel;
+
+/**
+ * This class represents a database node on the DB Explorer tree view
+ */
+public class DbNode extends AbstractTreeNode implements IDbNode
+{
+ /**
+ * Properties name space.
+ */
+ public static final String PROP_NAMESPACE = "com.motorolamobility.studio.android.db.core"; //$NON-NLS-1$
+
+ /**
+ * Property value used to check if the database is disconnected.
+ */
+ public static final String PROP_VALUE_DB_DISCONNECTED =
+ "com.motorolamobility.studio.android.db.core.databaseDisconnected"; //$NON-NLS-1$
+
+ /**
+ * Property value used to check if the database is connected.
+ */
+ public static final String PROP_VALUE_DB_CONNECTED =
+ "com.motorolamobility.studio.android.db.core.databaseConnected"; //$NON-NLS-1$
+
+ /**
+ * Property name used to check database connection status (connected/disconnected).
+ */
+ public static final String PROP_NAME_DB_CONNECTION =
+ "com.motorolamobility.studio.android.db.core.databaseConnection"; //$NON-NLS-1$
+
+ /**
+ * Property name used to check database connection status (connected/disconnected).
+ */
+ public static final String PROP_NAME_DB_NODE_TYPE =
+ "com.motorolamobility.studio.android.db.core.IDbNodeType"; //$NON-NLS-1$
+
+ /**
+ * Property value used to check if the database is connected.
+ */
+ public static final String PROP_VALUE_DB_NODE_IS_EXT_STORAGE =
+ "com.motorolamobility.studio.android.db.core.isExternalStorage"; //$NON-NLS-1$
+
+ private class ResultManagerAdapter extends AbstractDbResultManagerAdapter
+ {
+ /* (non-Javadoc)
+ * @see com.motorolamobility.studio.android.db.core.ui.AbstractDbResultManagerAdapter#statementExecuted(java.lang.String, java.lang.String)
+ */
+ @Override
+ public void statementExecuted(String profileName, String sqlStatement)
+ {
+ if (model.getProfileName().equals(profileName))
+ {
+ //Ignore group execution and read access to table(Select). We'll handle only db changes.
+ if ((!sqlStatement.equals("Group Execution")) //$NON-NLS-1$
+ && (sqlStatement.indexOf("select") != 0) && (!sqlStatement.equals(""))) //$NON-NLS-1$ //$NON-NLS-2$
+ {
+ if (sqlStatement.startsWith("drop table") //$NON-NLS-1$
+ || sqlStatement.startsWith("create table")) //$NON-NLS-1$
+ {
+ //A new table has been created let's refresh the dbNode in order to get a update copy
+ refreshAsync();
+ }
+ else if (sqlStatement.startsWith("alter table")) //$NON-NLS-1$
+ {
+ if (!sqlStatement.contains("rename")) //$NON-NLS-1$
+ {
+ //Table has been altered but not renamed, let's refresh the table node, loading the possible changes
+ String tableName = sqlStatement.replace("alter table ", ""); //$NON-NLS-1$ //$NON-NLS-2$
+ tableName.substring(0, tableName.indexOf(" ")); //$NON-NLS-1$
+ List<ITreeNode> children = getChildren();
+ for (ITreeNode child : children)
+ {
+ if (child.getName().equalsIgnoreCase(tableName))
+ {
+ child.refreshAsync();
+ break;
+ }
+ }
+ }
+ else
+ {
+ //Since a name has been renamed a refresh on this db node will force to use the latest information.
+ refreshAsync();
+ }
+ }
+ }
+ }
+ }
+ };
+
+ public static final String ICON_PATH = "icons/obj16/dbplate.gif"; //$NON-NLS-1$
+
+ protected DbModel model;
+
+ private IResultManagerListener resultManagerListener;
+
+ protected boolean forceCloseEditors;
+
+ @SuppressWarnings("unused")
+ private DbNode()
+ {
+ //Forcing user to use a proper constructor (with a parent)
+ }
+
+ protected DbNode(ITreeNode parent)
+ {
+ super(parent);
+ }
+
+ /**
+ * Creates a new DBNode by using a existent db file.
+ * @param dbFilePath The SQLite database File
+ * @param parent The parent of the new node.
+ * @throws MotodevDbException
+ */
+ public DbNode(IPath dbFilePath, ITreeNode parent) throws MotodevDbException
+ {
+ this(parent);
+ init(dbFilePath);
+ model = new DbModel(dbFilePath);
+ }
+
+ /**
+ * Creates a new DBNode by creating a new SQLite3 database file if requested.
+ * @param dbPath The SQLite database File
+ * @param parent The parent of the new node.
+ * @param create set this flag to true if you want to create a new db file, if the flag is false the behavior is the same as the constructor DbNode(Path dbFilePath, AbstractTreeNode parent)
+ * @throws MotodevDbException
+ */
+ public DbNode(IPath dbPath, ITreeNode parent, boolean create) throws MotodevDbException
+ {
+ this(parent);
+ init(dbPath);
+ try
+ {
+ model = new DbModel(dbPath, create);
+ }
+ catch (MotodevDbException e)
+ {
+ throw new MotodevDbException("Could not create DBNode", e); //$NON-NLS-1$
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorolamobility.studio.android.db.core.ui.IDBNode#connect()
+ */
+ public IStatus connect()
+ {
+ IStatus status = model.connect();
+ if (status.isOK())
+ {
+ if (resultManagerListener == null)
+ {
+ resultManagerListener = new ResultManagerAdapter();
+ ResultsViewAPI.getInstance().getResultManager()
+ .addResultManagerListener(resultManagerListener);
+ }
+ }
+
+ setNodeStatus(status);
+ return status;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorolamobility.studio.android.db.core.ui.IDBNode#disconnect()
+ */
+ public IStatus disconnect()
+ {
+ IStatus status = closeAssociatedEditors();
+ if (status.isOK())
+ {
+ status = model.disconnect();
+ if (status.isOK())
+ {
+ if (resultManagerListener != null)
+ {
+ ResultsViewAPI.getInstance().getResultManager()
+ .removeResultManagerListener(resultManagerListener);
+ resultManagerListener = null;
+ }
+ clear();
+ }
+ if (status.getSeverity() != IStatus.CANCEL)
+ {
+ setNodeStatus(status);
+ }
+ }
+
+ if (status.getSeverity() != IStatus.CANCEL)
+ {
+ setNodeStatus(status);
+ }
+
+ return status;
+ }
+
+ public IStatus createTables(List<TableModel> tables)
+ {
+ IStatus status = Status.OK_STATUS;
+ List<ITreeNode> tableNodes = new ArrayList<ITreeNode>(tables.size());
+ for (TableModel table : tables)
+ {
+ status = model.createTable(table);
+ if (status.isOK())
+ {
+ TableNode tableNode = new TableNode(getTable(table.getName()), model, this);
+ tableNodes.add(tableNode);
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ if (status.isOK())
+ {
+ putChildren(tableNodes);
+ }
+
+ return status;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorolamobility.studio.android.db.core.ui.IDBNode#createTable(java.lang.String, java.lang.String)
+ */
+ public IStatus createTable(TableModel table)
+ {
+ IStatus status = model.createTable(table);
+ if (status.isOK())
+ {
+ TableNode tableNode = new TableNode(getTable(table.getName()), model, this);
+ putChild(tableNode);
+ }
+
+ return status;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorolamobility.studio.android.db.core.ui.IDBNode#deleteTable(java.lang.String)
+ */
+ public IStatus deleteTable(ITableNode tableNode)
+ {
+ IStatus status = model.deleteTable(tableNode.getName());
+ if (status.isOK())
+ {
+ removeChild(tableNode);
+ }
+
+ return status;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorolamobility.studio.android.db.core.ui.IDBNode#getTables()
+ */
+ public List<Table> getTables()
+ {
+ return model.getTables();
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorolamobility.studio.android.db.core.ui.AbstractTreeNode#refresh()
+ */
+ @Override
+ public void refresh()
+ {
+ if (!model.isConnected())
+ {
+ connect();
+ }
+ clear();
+ List<Table> tables = getTables();
+ List<ITreeNode> tableNodes = new ArrayList<ITreeNode>(tables.size());
+ for (Table table : tables)
+ {
+ TableNode tableNode = new TableNode(table, model, this);
+ tableNodes.add(tableNode);
+ }
+ putChildren(tableNodes);
+ }
+
+ protected IStatus closeAssociatedEditors(final boolean quiet, final boolean forceClose)
+ {
+ Set<IEditorPart> associatedEditors = getAssociatedEditors();
+ IStatus status = Status.OK_STATUS;
+ if (!associatedEditors.isEmpty())
+ {
+ final boolean[] success = new boolean[]
+ {
+ true
+ };
+ for (final IEditorPart editor : associatedEditors)
+ {
+ final IWorkbenchPage page = EclipseUtils.getPageForEditor(editor);
+ Display.getDefault().syncExec(new Runnable()
+ {
+ public void run()
+ {
+ if (!quiet)
+ {
+ page.bringToTop(editor);
+ if (!forceClose)
+ {
+ //Use the default Eclipse behavior
+ if (!page.closeEditor(editor, true))
+ {
+ success[0] = false;
+ }
+ }
+ else
+ {
+ if (editor.isDirty())
+ {
+ //Use our dialog, because the operation can't be cancelled.
+ boolean shallSave =
+ EclipseUtils.showQuestionDialog(
+ DbCoreNLS.DbNode_Close_Editor_Msg_Title,
+ NLS.bind(DbCoreNLS.DbNode_Close_Editor_Msg,
+ getName()));
+ if (shallSave)
+ {
+ editor.doSave(new NullProgressMonitor());
+ }
+ }
+ page.closeEditor(editor, false);
+ }
+ }
+ else
+ {
+ page.closeEditor(editor, false);
+ }
+ }
+ });
+
+ if (!success[0])
+ {
+ break;
+ }
+ }
+
+ if (!success[0])
+ {
+ status =
+ new Status(IStatus.CANCEL, DbCoreActivator.PLUGIN_ID,
+ DbCoreNLS.DbNode_Canceled_Save_Operation);
+ }
+ }
+ return status;
+ }
+
+ /**
+ * @param status
+ * @return
+ */
+ protected IStatus closeAssociatedEditors()
+ {
+ return closeAssociatedEditors(false, forceCloseEditors);
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorolamobility.studio.android.db.core.ui.IDBNode#getTable(java.lang.String)
+ */
+ public Table getTable(String tableName)
+ {
+ return model.getTable(tableName);
+ }
+
+ private void init(IPath dbFilePath)
+ {
+ String dbFileName = dbFilePath.lastSegment();
+ String id = dbFilePath.toFile().getParent() + "." + dbFileName; //$NON-NLS-1$
+
+ setId(id);
+ setName(dbFileName);
+ ImageDescriptor icon =
+ DbCoreActivator.imageDescriptorFromPlugin(DbCoreActivator.PLUGIN_ID, ICON_PATH);
+ setIcon(icon);
+ setToolTip(dbFilePath);
+ }
+
+ /*
+ * Sets the tool tip for the node given its path.
+ */
+ private void setToolTip(IPath dbPath)
+ {
+ //for mapped nodes (i.e., children of IDbMapperNode) the tool tip is its path
+ if (getParent() instanceof IDbMapperNode)
+ {
+ setTooltip(NLS.bind(DbCoreNLS.DbNode_Tooltip_Prefix, dbPath.toString()));
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorolamobility.studio.android.db.core.ui.AbstractTreeNode#isLeaf()
+ */
+ @Override
+ public boolean isLeaf()
+ {
+ return false;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorolamobility.studio.android.db.core.ui.IDbNode#deleteDb()
+ */
+ public IStatus deleteDb()
+ {
+ closeAssociatedEditors(true, forceCloseEditors);
+ disconnect();
+ return model.deleteDb();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.IActionFilter#testAttribute(java.lang.Object, java.lang.String, java.lang.String)
+ */
+ @Override
+ public boolean testAttribute(Object target, String name, String value)
+ {
+ boolean result = false;
+
+ //check if 'name' is a specific DbNode property
+ if (name.equals(PROP_NAME_DB_CONNECTION)
+ || PROP_NAME_DB_CONNECTION.equals(PROP_NAMESPACE + '.' + name))
+ {
+ if (value.equals(PROP_VALUE_DB_CONNECTED))
+ {
+ result = isConnected();
+ }
+ else if (value.equals(PROP_VALUE_DB_DISCONNECTED))
+ {
+ result = !isConnected();
+ }
+ }
+ else if (name.equals(PROP_NAME_DB_NODE_TYPE))
+ {
+ if (value.equals(PROP_VALUE_DB_NODE_IS_EXT_STORAGE))
+ {
+ result = (getParent() instanceof IDbMapperNode);
+ }
+ }
+ else
+ {
+ //check if 'name' is a generic ITreeNode property
+ result = super.testAttribute(target, name, value);
+ }
+
+ return result;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorolamobility.studio.android.db.core.ui.AbstractTreeNode#getIcon()
+ */
+ @Override
+ public ImageDescriptor getIcon()
+ {
+ return getSpecificIcon("org.eclipse.datatools.connectivity.sqm.core.ui", //$NON-NLS-1$
+ "icons/database.gif"); //$NON-NLS-1$
+ }
+
+ public boolean isConnected()
+ {
+ return model != null ? model.isConnected() : false;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorolamobility.studio.android.db.core.ui.AbstractTreeNode#clean()
+ */
+ @Override
+ public void cleanUp()
+ {
+ setForceCloseEditors(true);
+ disconnect();
+ if (model != null)
+ {
+ try
+ {
+ model.cleanModel();
+ }
+ catch (ConnectionProfileException e)
+ {
+ StudioLogger.debug(this, "Unable to cleanup db model.");
+ }
+ }
+ super.cleanUp();
+ }
+
+ /**
+ * Retrieves the open editors that is used to edit the given profile, if any
+ *
+ * @param profile
+ * The profile that owns the requested editor
+ * @return The open dirty editor for the given profile, or <code>null</code>
+ * if there is no editor in this condition
+ */
+ public Set<IEditorPart> getAssociatedEditors()
+ {
+ return model != null ? model.getAssociatedEditors() : new HashSet<IEditorPart>(0);
+ }
+
+ /**
+ * Checks if the db file exists in filesystem
+ * @return
+ */
+ public boolean existsDbFile()
+ {
+ return model != null ? model.getDbPath().toFile().exists() : false;
+ }
+
+ public IPath getPath()
+ {
+ return model != null ? model.getDbPath() : null;
+ }
+
+ /**
+ * @param forceCloseEditors the forceCloseEditors to set
+ */
+ protected void setForceCloseEditors(boolean forceCloseEditors)
+ {
+ this.forceCloseEditors = forceCloseEditors;
+ }
+}
diff --git a/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/ui/IDataSampler.java b/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/ui/IDataSampler.java
new file mode 100644
index 0000000..fcdf502
--- /dev/null
+++ b/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/ui/IDataSampler.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.studio.android.db.core.ui;
+
+/**
+ * Represents a node that accepts sample data command
+ */
+public interface IDataSampler extends ITreeNode
+{
+
+ public abstract void sampleDbContents();
+
+} \ No newline at end of file
diff --git a/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/ui/IDbMapperNode.java b/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/ui/IDbMapperNode.java
new file mode 100644
index 0000000..00df2e3
--- /dev/null
+++ b/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/ui/IDbMapperNode.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.studio.android.db.core.ui;
+
+import java.util.List;
+
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.ui.IActionFilter;
+
+/**
+ * Interface for nodes that are responsible for mapping databases.
+ */
+public interface IDbMapperNode extends ITreeNode, IActionFilter
+{
+
+ public static final String UNMAP_ACTIONFILTER_NAME =
+ "com.motorolamobility.studio.android.db.core.mappedDatabases"; //$NON-NLS-1$
+
+ public static final String UNMAP_ACTIONFILTER_VALUE =
+ "com.motorolamobility.studio.android.db.core.atLeastOne"; //$NON-NLS-1$
+
+ public IStatus map(IPath dbFilePath);
+
+ public IStatus unmap(ITreeNode dbNode);
+
+ public IStatus unmap(List<ITreeNode> dbNodeList);
+}
diff --git a/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/ui/IDbNode.java b/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/ui/IDbNode.java
new file mode 100644
index 0000000..30ff785
--- /dev/null
+++ b/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/ui/IDbNode.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.studio.android.db.core.ui;
+
+import java.util.List;
+
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.datatools.modelbase.sql.tables.Table;
+
+import com.motorolamobility.studio.android.db.core.model.TableModel;
+import com.motorolamobility.studio.android.db.core.ui.action.ITableCreatorNode;
+
+/**
+ * This interface is meant to be implemented by databse nodes.
+ * it defines standard methods for manipulating a database.
+ */
+public interface IDbNode extends ITreeNode, ITableCreatorNode
+{
+
+ /**
+ * Connect to the database.
+ * Implementors must connect to the database, load the tables and notify the UI.
+ * @return
+ */
+ IStatus connect();
+
+ /**
+ * Connect to the database.
+ * Implementors must disconnect from the database, free this node children and notify the UI.
+ * @return
+ */
+ IStatus disconnect();
+
+ /**
+ * Checks if db is connected
+ * @return true if db is connected, false otherwise
+ */
+ boolean isConnected();
+
+ /**
+ * @param tables
+ * @return
+ */
+ IStatus createTables(List<TableModel> tables);
+
+ /**
+ * Create a table in this db.
+ * Implementors must create the table and add a TableNode representing the new table as a new child.
+ * UI must be notified after child is added.
+ * @param table The new table representation
+ * @return
+ */
+ IStatus createTable(TableModel table);
+
+ /**
+ * Delete a table from this db.
+ * The table node representing this table must also be removed from this dbNode children.
+ * UI must be notified.
+ * @param tableNode The node table to be removed.
+ * @return
+ */
+ IStatus deleteTable(ITableNode tableNode);
+
+ /**
+ * Get all tables from this db.
+ * @return A {@link List<Table>} with all tables from this database
+ */
+ List<Table> getTables();
+
+ /**
+ * Get a table from this db with the specified name.
+ * @return A {@link List<Table>} with all tables from this database
+ */
+ Table getTable(String tableName);
+
+ /**
+ * Delete the database represented by this node.
+ * @return Status.OK if operation is successful, Status.ERROR otherwise. The status message explains the fail reason.
+ */
+ IStatus deleteDb();
+} \ No newline at end of file
diff --git a/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/ui/IRootNode.java b/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/ui/IRootNode.java
new file mode 100644
index 0000000..a6efa61
--- /dev/null
+++ b/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/ui/IRootNode.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.studio.android.db.core.ui;
+
+/**
+ * Interface that identifies a root node on the tree.
+ * Initial version is empty since it was initially created to reuse popup menu commands.
+ */
+public interface IRootNode extends ITreeNode
+{
+
+}
diff --git a/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/ui/ISaveStateTreeNode.java b/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/ui/ISaveStateTreeNode.java
new file mode 100644
index 0000000..015e18a
--- /dev/null
+++ b/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/ui/ISaveStateTreeNode.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.studio.android.db.core.ui;
+
+import org.eclipse.core.runtime.preferences.IEclipsePreferences;
+
+public interface ISaveStateTreeNode extends ITreeNode
+{
+ void saveState(IEclipsePreferences preferences);
+
+ void restoreState(IEclipsePreferences preferences);
+}
diff --git a/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/ui/ITableNode.java b/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/ui/ITableNode.java
new file mode 100644
index 0000000..80ff62d
--- /dev/null
+++ b/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/ui/ITableNode.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.studio.android.db.core.ui;
+
+import org.eclipse.core.runtime.IStatus;
+
+public interface ITableNode extends IDataSampler
+{
+ public IStatus browseTableContents();
+
+ public void extractData();
+
+ public void loadData();
+}
diff --git a/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/ui/ITreeNode.java b/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/ui/ITreeNode.java
new file mode 100644
index 0000000..bf2c0ff
--- /dev/null
+++ b/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/ui/ITreeNode.java
@@ -0,0 +1,182 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.studio.android.db.core.ui;
+
+import java.util.List;
+import java.util.regex.Pattern;
+
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.ui.IActionFilter;
+
+/**
+ * This interface represents a db tree node.
+ */
+public interface ITreeNode extends IActionFilter
+{
+
+ /**
+ * Method responsible to reload the node itself and its children
+ */
+ void refresh();
+
+ /**
+ * Get parent of the tree node
+ * @return null if it is the tree root, non-null if is a child node
+ */
+ ITreeNode getParent();
+
+ /**
+ * @param parent null if it is the tree root, non-null if is a child node
+ */
+ void setParent(ITreeNode parent);
+
+ /**
+ * Retrieves list of children (without any filter)
+ * @return collection of {@link AbstractTreeNode} that are child of this abstract tree node
+ */
+ List<ITreeNode> getChildren();
+
+ /**
+ * Clear the children (e.g. before reloading again)
+ */
+ void clear();
+
+ /**
+ * Retrieve the child for the given node index
+ * @param index
+ * @return {@link AbstractTreeNode} if child with the given index was found, null if node not found as a child
+ */
+ ITreeNode getChild(int index);
+
+ /**
+ * Retrieve the child for the given node ID
+ * @param id node ID as specified in the extension point com.motorolamobility.studio.android.db.core.dbRootNode
+ * @return {@link AbstractTreeNode} if child with the given ID was found, null if node not found as a child
+ */
+ ITreeNode getChildById(String id);
+
+ /**
+ * Get list of children nodes that matches a regular expression
+ * @param regex regular expression to filter nodes, see {@link Pattern} for the constructs
+ * if null, returns all items
+ * @return list of {@link AbstractTreeNode} that matches the view filter
+ */
+ List<ITreeNode> getFilteredChildren(String regex);
+
+ void putChild(ITreeNode treeNode);
+
+ void putChildren(List<ITreeNode> childrenList);
+
+ /**
+ * Remove a child with the given node
+ * @param node
+ */
+ void removeChild(ITreeNode node);
+
+ /**
+ * @return true if node is getting data to be added in the tree, false otherwise
+ */
+ boolean isLoading();
+
+ /**
+ * @param isLoading true if node is getting data to be added in the tree, false otherwise
+ */
+ void setLoading(boolean isLoading);
+
+ /**
+ * @return the id
+ */
+ String getId();
+
+ /**
+ * @param id the id to set
+ */
+ void setId(String id);
+
+ /**
+ * @return the name
+ */
+ String getName();
+
+ /**
+ * @param name the name to set
+ */
+ void setName(String name);
+
+ /**
+ * @return the icon
+ */
+ ImageDescriptor getIcon();
+
+ /**
+ * @param icon the icon to set
+ */
+ void setIcon(ImageDescriptor icon);
+
+ /**
+ * @return the canRefresh
+ */
+ IStatus canRefresh();
+
+ /**
+ * @return true if it does not accept a child, false otherwise
+ */
+ boolean isLeaf();
+
+ /**
+ * Refreshes this node in a background task
+ * @param canRefreshInput
+ */
+ void refreshAsync();
+
+ /**
+ * Refreshes this node in a background task.
+ * @param canRefreshInput is an optional parameter, is intended to be set used by refresh handler.
+ */
+ void refreshAsync(boolean canRefreshYesResponse);
+
+ /**
+ * Clean method is intended to be called right before removing this node from it's parent.
+ * All resource cleaning must be done in this method.
+ */
+ void cleanUp();
+
+ /**
+ * Set the node Status, allowing the tree to decorate itself on errors.
+ * Is status is ERROR the icon will be decorated with a error image and tooltip will be replaced by status.getMessage() if available.
+ * @param status
+ */
+ void setNodeStatus(IStatus status);
+
+ /**
+ * Retrieves the current node status.
+ * @return
+ */
+ IStatus getNodeStatus();
+
+ /**
+ * Set the tooltip to be displayed for this node.
+ * @param tooltip
+ */
+ void setTooltip(String tooltip);
+
+ /**
+ * @return this node tooltip text
+ */
+ String getTooltip();
+
+} \ No newline at end of file
diff --git a/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/ui/LoadingJobListener.java b/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/ui/LoadingJobListener.java
new file mode 100644
index 0000000..3da89e2
--- /dev/null
+++ b/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/ui/LoadingJobListener.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.studio.android.db.core.ui;
+
+import java.util.List;
+
+import org.eclipse.core.runtime.jobs.IJobChangeEvent;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.core.runtime.jobs.JobChangeAdapter;
+import org.eclipse.jface.viewers.ILazyTreeContentProvider;
+import org.eclipse.jface.viewers.TreeViewer;
+import org.eclipse.swt.widgets.Display;
+
+import com.motorola.studio.android.common.utilities.PluginUtils;
+import com.motorolamobility.studio.android.db.core.DbCoreActivator;
+import com.motorolamobility.studio.android.db.core.ui.view.MOTODEVDatabaseExplorerView;
+
+/**
+ * This class is responsible to set the loading attribute on a tree node when a loadingJob is scheduled and set it to false
+ * before updating the treeView after the loadingJob finished execution.
+ * The node child count is update on job done. TreeView contentProvider will take care of the rest.
+ */
+public final class LoadingJobListener extends JobChangeAdapter
+{
+ /* (non-Javadoc)
+ * @see org.eclipse.core.runtime.jobs.JobChangeAdapter#scheduled(org.eclipse.core.runtime.jobs.IJobChangeEvent)
+ */
+ @Override
+ public void scheduled(IJobChangeEvent event)
+ {
+ Job job = event.getJob();
+ if (job instanceof AbstractLoadingNodeJob)
+ {
+ AbstractLoadingNodeJob loadingNodeJob = (AbstractLoadingNodeJob) job;
+ ITreeNode node = loadingNodeJob.getNode();
+
+ //Since load job has been scheduled, the loading flag must be set to true (This will help the content provider to show the loading node).
+ node.setLoading(true);
+ }
+ super.scheduled(event);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.runtime.jobs.JobChangeAdapter#done(org.eclipse.core.runtime.jobs.IJobChangeEvent)
+ */
+ @Override
+ public void done(IJobChangeEvent event)
+ {
+ MOTODEVDatabaseExplorerView view = DbCoreActivator.getMOTODEVDatabaseExplorerView();
+ if (view != null)
+ {
+ final TreeViewer treeViewer = view.getTreeViewer();
+ Job job = event.getJob();
+ if (job instanceof AbstractLoadingNodeJob)
+ {
+ AbstractLoadingNodeJob loadingNodeJob = (AbstractLoadingNodeJob) job;
+ ITreeNode node = loadingNodeJob.getNode();
+
+ //Job is done, so we can set the loading flag to false.
+ node.setLoading(false);
+ final ITreeNode[] treeNodeContainer =
+ {
+ node
+ };
+
+ //TreeViewer operations must be executed on the UI Thread.
+ Display.getDefault().syncExec(new Runnable()
+ {
+ public void run()
+ {
+ ITreeNode node = treeNodeContainer[0];
+ List<ITreeNode> children = node.getChildren();
+ if (PluginUtils.getOS() != PluginUtils.OS_LINUX)
+ {
+ treeViewer.setChildCount(node, 0);
+ }
+ int size = children.size();
+ if (size > 0)
+ {
+ ((ILazyTreeContentProvider) treeViewer.getContentProvider())
+ .updateElement(node, 0); //Force removal of loading node.
+ }
+ //updating the child count is sufficient to allow the tree to call the content provider and retrieve the new nodes.
+ treeViewer.setChildCount(node, size);
+ //updating the node so if needed the label/icon will be updated
+ treeViewer.update(node, null);
+ }
+ });
+ }
+ super.done(event);
+ }
+ }
+} \ No newline at end of file
diff --git a/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/ui/LoadingNode.java b/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/ui/LoadingNode.java
new file mode 100644
index 0000000..2ef57ea
--- /dev/null
+++ b/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/ui/LoadingNode.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.studio.android.db.core.ui;
+
+import org.eclipse.jface.resource.ImageDescriptor;
+
+import com.motorolamobility.studio.android.db.core.i18n.DbCoreNLS;
+
+public final class LoadingNode extends AbstractTreeNode
+{
+
+ public static String ID = "LOADING_NODE"; //$NON-NLS-1$
+
+ public LoadingNode(ITreeNode parent)
+ {
+ super(parent);
+ setName(DbCoreNLS.LoadingNode_nodeName);
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorolamobility.studio.android.db.core.ui.AbstractTreeNode#getIcon()
+ */
+ @Override
+ public ImageDescriptor getIcon()
+ {
+ return getSpecificIcon("org.eclipse.datatools.connectivity.sqm.core.ui", //$NON-NLS-1$
+ "icons/refresh.gif"); //$NON-NLS-1$
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorolamobility.studio.android.db.core.ui.AbstractTreeNode#refresh()
+ */
+ @Override
+ public void refresh()
+ {
+ //Does Nothing!
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorolamobility.studio.android.db.core.ui.AbstractTreeNode#isLeaf()
+ */
+ @Override
+ public boolean isLeaf()
+ {
+ return true;
+ }
+
+}
diff --git a/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/ui/RootNode.java b/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/ui/RootNode.java
new file mode 100644
index 0000000..8b52084
--- /dev/null
+++ b/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/ui/RootNode.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.studio.android.db.core.ui;
+
+/**
+ * Root node of the tree
+ */
+public class RootNode extends AbstractTreeNode
+{
+
+ /* (non-Javadoc)
+ * @see com.motorolamobility.studio.android.db.core.ui.AbstractTreeNode#refresh()
+ */
+ @Override
+ public void refresh()
+ {
+ //Do nothing.
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorolamobility.studio.android.db.core.ui.AbstractTreeNode#isLeaf()
+ */
+ @Override
+ public boolean isLeaf()
+ {
+ return false;
+ }
+
+}
diff --git a/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/ui/TableNode.java b/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/ui/TableNode.java
new file mode 100644
index 0000000..5219268
--- /dev/null
+++ b/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/ui/TableNode.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.studio.android.db.core.ui;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.datatools.modelbase.sql.tables.Column;
+import org.eclipse.datatools.modelbase.sql.tables.Table;
+import org.eclipse.datatools.sqltools.data.internal.ui.editor.TableDataEditorInput;
+import org.eclipse.datatools.sqltools.data.internal.ui.extract.ExtractDataWizard;
+import org.eclipse.datatools.sqltools.data.internal.ui.load.LoadDataWizard;
+import org.eclipse.emf.common.util.EList;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.jface.wizard.WizardDialog;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.PartInitException;
+import org.eclipse.ui.PlatformUI;
+
+import com.motorolamobility.studio.android.db.core.DbCoreActivator;
+import com.motorolamobility.studio.android.db.core.i18n.DbCoreNLS;
+import com.motorolamobility.studio.android.db.core.model.DbModel;
+
+public class TableNode extends AbstractTreeNode implements ITableNode
+{
+
+ private final Table table;
+
+ private final DbModel model;
+
+ /**
+ * @param table
+ * @param parent
+ */
+ public TableNode(Table table, DbModel dbModel, IDbNode parent)
+ {
+ super(table.getName(), table.getName(), parent);
+ this.table = table;
+ this.model = dbModel;
+ }
+
+ public IStatus browseTableContents()
+ {
+ IStatus browseTableContentsStatus =
+ new Status(IStatus.OK, DbCoreActivator.PLUGIN_ID,
+ DbCoreNLS.TableNode_BrowsingTableContentsSuccessStatus);
+ final IWorkbenchPage workbenchPage =
+ PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage();
+
+ try
+ {
+ workbenchPage.openEditor(new TableDataEditorInput(table),
+ "org.eclipse.datatools.sqltools.data.internal.ui.editor.tableDataEditor"); //$NON-NLS-1$
+
+ }
+ catch (PartInitException e)
+ {
+ //Display error message!
+ browseTableContentsStatus =
+ new Status(IStatus.ERROR, DbCoreActivator.PLUGIN_ID,
+ DbCoreNLS.TableNode_BrowsingTableContentsErrorStatus, e);
+ }
+ return browseTableContentsStatus;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorolamobility.studio.android.db.core.ui.AbstractTreeNode#refresh()
+ */
+ @SuppressWarnings("unchecked")
+ @Override
+ public void refresh()
+ {
+ clear();
+
+ EList<Column> columns = table.getColumns();
+ List<ITreeNode> columnNodes = new ArrayList<ITreeNode>(columns.size());
+ for (Column column : columns)
+ {
+ ColumnNode columnNode = new ColumnNode(column, model, this);
+ columnNodes.add(columnNode);
+ }
+ putChildren(columnNodes);
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorolamobility.studio.android.db.core.ui.AbstractTreeNode#isLeaf()
+ */
+ @Override
+ public boolean isLeaf()
+ {
+ return false;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorolamobility.studio.android.db.core.ui.AbstractTreeNode#getIcon()
+ */
+ @Override
+ public ImageDescriptor getIcon()
+ {
+ return getSpecificIcon("org.eclipse.datatools.connectivity.sqm.core.ui", //$NON-NLS-1$
+ "icons/table.gif"); //$NON-NLS-1$
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorolamobility.studio.android.db.core.ui.ITableNode#sampleDbContents()
+ */
+ public void sampleDbContents()
+ {
+ model.sampleContents(table);
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorolamobility.studio.android.db.core.ui.ITableNode#extractData()
+ */
+ public void extractData()
+ {
+ ExtractDataWizard wiz = new ExtractDataWizard(table);
+ WizardDialog dialog = new WizardDialog(Display.getCurrent().getActiveShell(), wiz);
+ dialog.create();
+ dialog.open();
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorolamobility.studio.android.db.core.ui.ITableNode#loadData()
+ */
+ public void loadData()
+ {
+ LoadDataWizard wiz = new LoadDataWizard(table);
+ WizardDialog dialog = new WizardDialog(Display.getCurrent().getActiveShell(), wiz);
+ dialog.create();
+ dialog.open();
+ }
+}
diff --git a/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/ui/action/CreateDatabaseManagementClassesAction.java b/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/ui/action/CreateDatabaseManagementClassesAction.java
new file mode 100644
index 0000000..85e1098
--- /dev/null
+++ b/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/ui/action/CreateDatabaseManagementClassesAction.java
@@ -0,0 +1,232 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.studio.android.db.core.ui.action;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.IAdaptable;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.jface.action.Action;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.StructuredSelection;
+import org.eclipse.jface.wizard.WizardDialog;
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.PlatformUI;
+
+import com.motorola.studio.android.codeutils.wizards.DatabaseManagementClassesCreationWizard;
+import com.motorolamobility.studio.android.db.core.project.ProjectNode;
+import com.motorolamobility.studio.android.db.core.ui.IDbNode;
+
+/**
+ *
+ * This class is responsible for executing the action of creating the classes
+ * responsible for deploying a database file automatically.
+ */
+public class CreateDatabaseManagementClassesAction extends Action
+{
+
+ private IDbNode dbNodeSelected;
+
+ private ProjectNode dbProjectNodeSelected;
+
+ /**
+ * @see org.eclipse.jface.action.Action#run()
+ */
+ @Override
+ public void run()
+ {
+ IResource resource = null;
+ IWorkbench workbench = PlatformUI.getWorkbench();
+ if ((workbench != null) && !workbench.isClosing())
+ {
+ IWorkbenchWindow window = workbench.getActiveWorkbenchWindow();
+ if (window != null)
+ {
+ if (dbNodeSelected != null)
+ {
+ //db node selected => get parent to retrieve the resource or the project (if the resource does not exist)
+ if (dbNodeSelected.getParent() instanceof ProjectNode)
+ {
+ ProjectNode pNode = (ProjectNode) dbNodeSelected.getParent();
+ resource =
+ ResourcesPlugin
+ .getWorkspace()
+ .getRoot()
+ .getProject(pNode.getName())
+ .getFile(
+ ProjectNode.DB_FOLDER + IPath.SEPARATOR
+ + dbNodeSelected.getName());
+ if (!resource.exists())
+ {
+ resource = resource.getProject();
+ }
+ }
+ }
+ else if (dbProjectNodeSelected != null)
+ {
+
+ ProjectNode pNode = dbProjectNodeSelected;
+ resource =
+ ResourcesPlugin
+ .getWorkspace()
+ .getRoot()
+ .getProject(pNode.getName())
+ .getFile(
+ ProjectNode.DB_FOLDER + IPath.SEPARATOR
+ + dbProjectNodeSelected.getName());
+ if (!resource.exists())
+ {
+ resource = resource.getProject();
+ }
+ }
+ else
+ {
+ //db node not selected on tree => try to get resource based on selection from package explorer
+ Object selectionElement = getSelectionElement(window);
+ if (selectionElement == null)
+ {
+ //the wizard was requested to open from MOTODEV menu - open wizard without selecting project or .db file
+ resource = null;
+ }
+ else
+ {
+ //there is an item selected, get the resource associated
+ resource = getResourceFromSelection(selectionElement);
+ }
+
+ }
+ openDialogBasedOnResourceSelected(resource, window);
+ }
+ }
+
+ }
+
+ /**
+ * Opens dialog based on the resource selected
+ * @param resource
+ * @param window
+ */
+ private void openDialogBasedOnResourceSelected(IResource resource, IWorkbenchWindow window)
+ {
+ WizardDialog dialog = null;
+ if (resource != null)
+ {
+ // in case there is a resource, go on
+ if (resource instanceof IFile)
+ {
+ // get wizard for database file
+ dialog =
+ new WizardDialog(window.getShell(),
+ new DatabaseManagementClassesCreationWizard(resource.getProject(),
+ resource));
+ }
+ else
+ {
+ // get wizard with no database file
+ dialog =
+ new WizardDialog(window.getShell(),
+ new DatabaseManagementClassesCreationWizard(resource.getProject(),
+ null));
+ }
+ }
+ else
+ {
+ //resource is null, set the wizard with nothing selected
+ dialog =
+ new WizardDialog(window.getShell(),
+ new DatabaseManagementClassesCreationWizard(null, null));
+ }
+ if (dialog != null)
+ {
+ //open the wizard
+ dialog.open();
+ }
+ }
+
+ /**
+ * Get the resource based on the selection of item inside workbench
+ * @param selectionElement
+ * @return
+ */
+ private IResource getResourceFromSelection(Object selectionElement)
+ {
+ //the wizard was requested from a click on .db file or project
+ IResource resource = null;
+ // in case the item as a resource, retrieve it
+ if (selectionElement instanceof IResource)
+ {
+ resource = (IResource) selectionElement;
+ }
+ // in case the item is an adaptable, retrieve it
+ else if (selectionElement instanceof IAdaptable)
+ {
+ try
+ {
+ resource = (IResource) ((IAdaptable) selectionElement).getAdapter(IResource.class);
+ }
+ catch (Exception e)
+ {
+ //Do nothing, return null;
+ }
+ }
+ return resource;
+ }
+
+ /**
+ * Get selected item base on selection of item inside workbench
+ * @param window
+ * @return
+ */
+ private Object getSelectionElement(IWorkbenchWindow window)
+ {
+ ISelection selection = window.getSelectionService().getSelection();
+ IStructuredSelection structureSelection = null;
+ if (selection instanceof IStructuredSelection)
+ {
+ structureSelection = (IStructuredSelection) selection;
+ }
+ else
+ {
+ structureSelection = new StructuredSelection();
+ }
+ Object selectionElement = structureSelection.getFirstElement();
+ return selectionElement;
+ }
+
+ /**
+ * @param dbNodeSelected the dbNodeSelected to set
+ */
+ public void setDbNodeSelected(IDbNode dbNodeSelected)
+ {
+ this.dbNodeSelected = dbNodeSelected;
+ }
+
+ /**
+ * @return the dbNodeSelected
+ */
+ protected IDbNode getDbNodeSelected()
+ {
+ return dbNodeSelected;
+ }
+
+ public void setProjectNodeSelected(ProjectNode selectedProjectNode)
+ {
+ this.dbProjectNodeSelected = selectedProjectNode;
+ }
+}
diff --git a/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/ui/action/IDbCreatorNode.java b/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/ui/action/IDbCreatorNode.java
new file mode 100644
index 0000000..4f0f081
--- /dev/null
+++ b/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/ui/action/IDbCreatorNode.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.studio.android.db.core.ui.action;
+
+import java.util.List;
+
+import org.eclipse.core.runtime.IStatus;
+
+import com.motorolamobility.studio.android.db.core.model.TableModel;
+import com.motorolamobility.studio.android.db.core.ui.IDbNode;
+import com.motorolamobility.studio.android.db.core.ui.ITreeNode;
+
+public interface IDbCreatorNode extends ITreeNode
+{
+
+ IStatus createDb(String dbName);
+
+ IStatus createDb(String dbName, List<TableModel> tables);
+
+ IStatus deleteDb(IDbNode dbNode);
+
+}
diff --git a/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/ui/action/ITableCreatorNode.java b/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/ui/action/ITableCreatorNode.java
new file mode 100644
index 0000000..36bf97c
--- /dev/null
+++ b/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/ui/action/ITableCreatorNode.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.studio.android.db.core.ui.action;
+
+import java.util.List;
+
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.datatools.modelbase.sql.tables.Table;
+
+import com.motorolamobility.studio.android.db.core.model.TableModel;
+import com.motorolamobility.studio.android.db.core.ui.ITreeNode;
+
+/**
+ * Represents a node that can create tables
+ */
+public interface ITableCreatorNode extends ITreeNode
+{
+
+ IStatus createTable(TableModel table);
+
+ List<Table> getTables();
+
+}
diff --git a/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/ui/action/PopupMenuActionDelegate.java b/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/ui/action/PopupMenuActionDelegate.java
new file mode 100644
index 0000000..d16c6a9
--- /dev/null
+++ b/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/ui/action/PopupMenuActionDelegate.java
@@ -0,0 +1,316 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.studio.android.db.core.ui.action;
+
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.core.commands.IHandler;
+import org.eclipse.jface.action.IAction;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.ui.IObjectActionDelegate;
+import org.eclipse.ui.IWorkbenchPart;
+
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorolamobility.studio.android.db.core.command.BrowseTableContentsHandler;
+import com.motorolamobility.studio.android.db.core.command.CreateDatabaseManagementClassesHandler;
+import com.motorolamobility.studio.android.db.core.command.DbConnectHandler;
+import com.motorolamobility.studio.android.db.core.command.DbCreateHandler;
+import com.motorolamobility.studio.android.db.core.command.DbDisconnectHandler;
+import com.motorolamobility.studio.android.db.core.command.DeleteDatabaseHandler;
+import com.motorolamobility.studio.android.db.core.command.DeleteTableHandler;
+import com.motorolamobility.studio.android.db.core.command.ExtractDataHandler;
+import com.motorolamobility.studio.android.db.core.command.LoadDataHandler;
+import com.motorolamobility.studio.android.db.core.command.MapDatabaseHandler;
+import com.motorolamobility.studio.android.db.core.command.RefreshNodeHandler;
+import com.motorolamobility.studio.android.db.core.command.SampleContentsHandler;
+import com.motorolamobility.studio.android.db.core.command.TableCreateHandler;
+import com.motorolamobility.studio.android.db.core.command.UnmapDatabaseHandler;
+import com.motorolamobility.studio.android.db.core.ui.AbstractTreeNode;
+import com.motorolamobility.studio.android.db.core.ui.IDataSampler;
+import com.motorolamobility.studio.android.db.core.ui.IDbMapperNode;
+import com.motorolamobility.studio.android.db.core.ui.IDbNode;
+import com.motorolamobility.studio.android.db.core.ui.ITableNode;
+import com.motorolamobility.studio.android.db.core.ui.ITreeNode;
+import com.motorolamobility.studio.android.db.core.ui.TableNode;
+
+public class PopupMenuActionDelegate implements IObjectActionDelegate
+{
+
+ /**
+ * Enum type for ActionHandlers. If you need to add a new ActionHandler, just include
+ * a new type to this enum with the action id that you defined on your action extension point
+ */
+ enum ActionHandlers
+ {
+ CREATE_DB("com.motorolamobility.studio.android.db.core.ui.action.createDbAction") //$NON-NLS-1$
+ {
+ @Override
+ public IHandler getHandler(ITreeNode node)
+ {
+ return new DbCreateHandler((IDbCreatorNode) node);
+ }
+ },
+ CREATE_TABLE("com.motorolamobility.studio.android.db.core.ui.action.createTableAction") //$NON-NLS-1$
+ {
+ @Override
+ public IHandler getHandler(ITreeNode node)
+ {
+ return new TableCreateHandler((ITableCreatorNode) node);
+ }
+ },
+ CONNECT("com.motorolamobility.studio.android.db.core.ui.action.connect") //$NON-NLS-1$
+ {
+ @Override
+ public IHandler getHandler(ITreeNode node)
+ {
+ return new DbConnectHandler((IDbNode) node);
+ }
+ },
+ DISCONNECT("com.motorolamobility.studio.android.db.core.ui.action.disconnect") //$NON-NLS-1$
+ {
+ @Override
+ public IHandler getHandler(ITreeNode node)
+ {
+ return new DbDisconnectHandler((IDbNode) node);
+ }
+ },
+ REFRESH_PROJECT("com.motorolamobility.studio.android.db.core.ui.action.refreshProjectNode") //$NON-NLS-1$
+ {
+
+ @Override
+ public IHandler getHandler(ITreeNode node)
+ {
+ return new RefreshNodeHandler(node);
+ }
+
+ },
+ REFRESH_WORKSPACE(
+ "com.motorolamobility.studio.android.db.core.ui.action.refreshWorkspaceNode") //$NON-NLS-1$
+ {
+
+ @Override
+ public IHandler getHandler(ITreeNode node)
+ {
+ return new RefreshNodeHandler(node);
+ }
+
+ },
+ REFRESH_FILESYSTEM(
+ "com.motorolamobility.studio.android.db.core.ui.action.refreshFileSystemNode") //$NON-NLS-1$
+ {
+
+ @Override
+ public IHandler getHandler(ITreeNode node)
+ {
+ return new RefreshNodeHandler(node);
+ }
+
+ },
+ REFRESH_DB("com.motorolamobility.studio.android.db.core.ui.action.refreshDbNode") //$NON-NLS-1$
+ {
+
+ @Override
+ public IHandler getHandler(ITreeNode node)
+ {
+ return new RefreshNodeHandler(node);
+ }
+
+ },
+ REFRESH_TABLE("com.motorolamobility.studio.android.db.core.ui.action.refreshTableNode") //$NON-NLS-1$
+ {
+
+ @Override
+ public IHandler getHandler(ITreeNode node)
+ {
+ return new RefreshNodeHandler(node);
+ }
+
+ },
+ REFRESH_DB_MAPPER_NODE(
+ "com.motorolamobility.studio.android.db.core.ui.action.refreshDbMapperNode") //$NON-NLS-1$
+ {
+
+ @Override
+ public IHandler getHandler(ITreeNode node)
+ {
+ return new RefreshNodeHandler(node);
+ }
+
+ },
+ DELETE_TABLE("com.motorolamobility.studio.android.db.core.ui.action.deleteTable") //$NON-NLS-1$
+ {
+
+ @Override
+ public IHandler getHandler(ITreeNode node)
+ {
+ return new DeleteTableHandler(node);
+ }
+
+ },
+ DELETE_DATABASE("com.motorolamobility.studio.android.db.core.ui.action.deleteDatabase") //$NON-NLS-1$
+ {
+
+ @Override
+ public IHandler getHandler(ITreeNode node)
+ {
+ return new DeleteDatabaseHandler(node);
+ }
+
+ },
+ BROWSE_TABLE_CONTENTS(
+ "com.motorolamobility.studio.android.db.core.ui.action.browseTableContents") //$NON-NLS-1$
+ {
+ @Override
+ public IHandler getHandler(ITreeNode node)
+ {
+ return new BrowseTableContentsHandler((TableNode) node);
+ }
+ },
+ CREATE_DB_MANAGEMENT_CLASSES(
+ "com.motorolamobility.studio.android.db.core.ui.action.createDatabaseManagementClasses") //$NON-NLS-1$
+ {
+ @Override
+ public IHandler getHandler(ITreeNode node)
+ {
+ return new CreateDatabaseManagementClassesHandler((IDbNode) node);
+ }
+ },
+ MAP_DATABASE("com.motorolamobility.studio.android.db.core.ui.action.mapDbNode") //$NON-NLS-1$
+ {
+
+ @Override
+ public IHandler getHandler(ITreeNode node)
+ {
+ return new MapDatabaseHandler((IDbMapperNode) node);
+ }
+
+ },
+ UNMAP_DATABASE("com.motorolamobility.studio.android.db.core.ui.action.unmapDbNode") //$NON-NLS-1$
+ {
+
+ @Override
+ public IHandler getHandler(ITreeNode node)
+ {
+ return new UnmapDatabaseHandler((IDbMapperNode) node);
+ }
+
+ },
+ SAMPLE_CONTENTS("com.motorolamobility.studio.android.db.core.ui.action.sampleContents") //$NON-NLS-1$
+ {
+
+ @Override
+ public IHandler getHandler(ITreeNode node)
+ {
+ return new SampleContentsHandler((IDataSampler) node);
+ }
+
+ },
+ EXTRACT_DATA("com.motorolamobility.studio.android.db.core.ui.action.extractData") //$NON-NLS-1$
+ {
+
+ @Override
+ public IHandler getHandler(ITreeNode node)
+ {
+ return new ExtractDataHandler((ITableNode) node);
+ }
+
+ },
+ LOAD_DATA("com.motorolamobility.studio.android.db.core.ui.action.loadData") //$NON-NLS-1$
+ {
+
+ @Override
+ public IHandler getHandler(ITreeNode node)
+ {
+ return new LoadDataHandler((ITableNode) node);
+ }
+
+ };
+ private final String actionId;
+
+ private ActionHandlers(String actionId)
+ {
+ this.actionId = actionId;
+ }
+
+ public abstract IHandler getHandler(ITreeNode node);
+
+ public static ActionHandlers getActionHandlerbyId(String id)
+ {
+
+ Object ret = null;
+ for (ActionHandlers h : ActionHandlers.values())
+ {
+ if (h.actionId.equals(id))
+ {
+ ret = h;
+ break;
+ }
+ }
+
+ return (ActionHandlers) ret;
+ }
+ }
+
+ private ITreeNode currentNode;
+
+ public void run(IAction action)
+ {
+
+ ActionHandlers type = ActionHandlers.getActionHandlerbyId(action.getId());
+
+ IHandler handler = null;
+
+ if (type != null)
+ {
+ handler = type.getHandler(currentNode);
+ }
+
+ if (handler != null)
+ {
+ ExecutionEvent event = new ExecutionEvent();
+ try
+ {
+ handler.execute(event);
+ }
+ catch (ExecutionException e)
+ {
+ StudioLogger.debug("Could not execute popupHandler");
+ }
+ }
+
+ }
+
+ public void selectionChanged(IAction action, ISelection selection)
+ {
+ if (selection instanceof IStructuredSelection)
+ {
+ IStructuredSelection structuredSelection = (IStructuredSelection) selection;
+ Object selectedObject = structuredSelection.getFirstElement();
+ if (selectedObject instanceof AbstractTreeNode)
+ {
+ currentNode = (ITreeNode) selectedObject;
+ }
+ }
+
+ }
+
+ public void setActivePart(IAction action, IWorkbenchPart targetPart)
+ {
+ //Do nothing.
+ }
+
+}
diff --git a/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/ui/tree/DatabaseExplorerTreeContentProvider.java b/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/ui/tree/DatabaseExplorerTreeContentProvider.java
new file mode 100644
index 0000000..45b22db
--- /dev/null
+++ b/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/ui/tree/DatabaseExplorerTreeContentProvider.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.studio.android.db.core.ui.tree;
+
+import org.eclipse.jface.viewers.ILazyTreeContentProvider;
+import org.eclipse.jface.viewers.TreeViewer;
+import org.eclipse.jface.viewers.Viewer;
+
+import com.motorolamobility.studio.android.db.core.ui.AbstractTreeNode;
+import com.motorolamobility.studio.android.db.core.ui.ITreeNode;
+import com.motorolamobility.studio.android.db.core.ui.LoadingNode;
+
+/**
+ * Content provider that uses {@link AbstractTreeNode} as the model
+ */
+public class DatabaseExplorerTreeContentProvider implements ILazyTreeContentProvider
+{
+
+ private final TreeViewer treeViewer;
+
+ public DatabaseExplorerTreeContentProvider(TreeViewer viewer)
+ {
+ this.treeViewer = viewer;
+ }
+
+ public void dispose()
+ {
+ //Nothing
+ }
+
+ public void inputChanged(Viewer viewer, Object oldInput, Object newInput)
+ {
+ //Nothing
+ }
+
+ public Object getParent(Object element)
+ {
+ if (element instanceof AbstractTreeNode)
+ {
+ ITreeNode treeNode = (ITreeNode) element;
+ return treeNode.getParent();
+ }
+ return null;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.viewers.ILazyTreeContentProvider#updateElement(java.lang.Object, int)
+ */
+ public void updateElement(Object parent, int index)
+ {
+ if (parent instanceof ITreeNode)
+ {
+ ITreeNode parentTreeNode = (ITreeNode) parent;
+ ITreeNode child = null;
+ if ((index == 0) && parentTreeNode.isLoading())
+ {
+ child = new LoadingNode(parentTreeNode);
+ }
+ else
+ {
+ child = parentTreeNode.getChild(index);
+ }
+ if (child != null)
+ {
+ treeViewer.replace(parent, index, child);
+ if (!child.isLeaf())
+ {
+ treeViewer.setHasChildren(child, true);
+ }
+ }
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.viewers.ILazyTreeContentProvider#updateChildCount(java.lang.Object, int)
+ */
+ public void updateChildCount(Object element, int currentChildCount)
+ {
+ if (element instanceof AbstractTreeNode)
+ {
+ ITreeNode treeNode = (ITreeNode) element;
+ treeNode.refreshAsync();
+ int childCount = 0;
+ if (treeNode.isLoading())
+ {
+ childCount = 1;
+ }
+ else
+ {
+ childCount = treeNode.getChildren().size();
+ }
+ if (childCount != currentChildCount)
+ {
+ treeViewer.setChildCount(element, childCount);
+ }
+ }
+ }
+}
diff --git a/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/ui/tree/DatabaseExplorerTreeLabelProvider.java b/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/ui/tree/DatabaseExplorerTreeLabelProvider.java
new file mode 100644
index 0000000..6373f33
--- /dev/null
+++ b/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/ui/tree/DatabaseExplorerTreeLabelProvider.java
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.studio.android.db.core.ui.tree;
+
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.jface.viewers.CellLabelProvider;
+import org.eclipse.jface.viewers.ILabelProviderListener;
+import org.eclipse.jface.viewers.ViewerCell;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.ui.IDecoratorManager;
+import org.eclipse.ui.PlatformUI;
+
+import com.motorolamobility.studio.android.db.core.i18n.DbCoreNLS;
+import com.motorolamobility.studio.android.db.core.ui.AbstractTreeNode;
+import com.motorolamobility.studio.android.db.core.ui.ITreeNode;
+import com.motorolamobility.studio.android.db.core.ui.view.MOTODEVDatabaseExplorerView;
+
+/**
+ * Provides label for the tree into {@link MOTODEVDatabaseExplorerView} by encapsulating {@link AbstractTreeNode}
+ */
+public class DatabaseExplorerTreeLabelProvider extends CellLabelProvider
+{
+ final IDecoratorManager decorator;
+
+ public enum LabelProperty
+ {
+ TEXT, IMAGE;
+ }
+
+ public DatabaseExplorerTreeLabelProvider()
+ {
+ decorator = PlatformUI.getWorkbench().getDecoratorManager();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.viewers.BaseLabelProvider#addListener(org.eclipse.jface.viewers.ILabelProviderListener)
+ */
+ @Override
+ public void addListener(ILabelProviderListener listener)
+ {
+ decorator.addListener(listener);
+ super.addListener(listener);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.viewers.BaseLabelProvider#removeListener(org.eclipse.jface.viewers.ILabelProviderListener)
+ */
+ @Override
+ public void removeListener(ILabelProviderListener listener)
+ {
+ decorator.removeListener(listener);
+ super.removeListener(listener);
+ }
+
+ public Image getImage(Object element)
+ {
+ Image result = null;
+ if (element instanceof AbstractTreeNode)
+ {
+ ITreeNode treeNode = (ITreeNode) element;
+ Image defaultImage = treeNode.getIcon().createImage();
+ result = decorator.decorateImage(defaultImage, element);
+ if (result == null)
+ {
+ result = defaultImage;
+ }
+ }
+
+ return result;
+ }
+
+ public String getText(Object element)
+ {
+ if (element instanceof AbstractTreeNode)
+ {
+ ITreeNode treeNode = (ITreeNode) element;
+ return treeNode.getName();
+ }
+ return null;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.viewers.BaseLabelProvider#isLabelProperty(java.lang.Object, java.lang.String)
+ */
+ @Override
+ public boolean isLabelProperty(Object element, String property)
+ {
+ boolean isLabelProperty = false;
+ LabelProperty labelProperty = LabelProperty.valueOf(property);
+ if (labelProperty != null)
+ {
+ isLabelProperty = true;
+ }
+
+ return isLabelProperty;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.viewers.CellLabelProvider#getToolTipText(java.lang.Object)
+ */
+ @Override
+ public String getToolTipText(Object element)
+ {
+ if (element instanceof ITreeNode)
+ {
+ ITreeNode treeNode = (ITreeNode) element;
+ String tooltipText = treeNode.getTooltip();
+ IStatus nodeStatus = treeNode.getNodeStatus();
+ if ((nodeStatus != null) && !nodeStatus.isOK())
+ {
+ String statusMessage = nodeStatus.getMessage();
+ if ((statusMessage != null) && !statusMessage.isEmpty())
+ {
+ tooltipText =
+ NLS.bind(
+ DbCoreNLS.DatabaseExplorerTreeLabelProvider_Error_Tooltip_Prefix,
+ statusMessage);
+ }
+ }
+ return tooltipText;
+ }
+ return super.getToolTipText(element);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.viewers.CellLabelProvider#getToolTipTimeDisplayed(java.lang.Object)
+ */
+ @Override
+ public int getToolTipTimeDisplayed(Object object)
+ {
+ return 4000;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.jface.viewers.CellLabelProvider#getToolTipDisplayDelayTime(java.lang.Object)
+ */
+ @Override
+ public int getToolTipDisplayDelayTime(Object object)
+ {
+ return 500;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.viewers.CellLabelProvider#getToolTipShift(java.lang.Object)
+ */
+ @Override
+ public Point getToolTipShift(Object object)
+ {
+ return new Point(5, 5);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.viewers.CellLabelProvider#update(org.eclipse.jface.viewers.ViewerCell)
+ */
+ @Override
+ public void update(ViewerCell cell)
+ {
+ Object cellElement = cell.getElement();
+ cell.setText(getText(cellElement));
+ cell.setImage(getImage(cellElement));
+ }
+
+}
diff --git a/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/ui/view/MOTODEVDatabaseExplorerView.java b/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/ui/view/MOTODEVDatabaseExplorerView.java
new file mode 100644
index 0000000..7f873a9
--- /dev/null
+++ b/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/ui/view/MOTODEVDatabaseExplorerView.java
@@ -0,0 +1,409 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.studio.android.db.core.ui.view;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+import org.eclipse.jface.action.IMenuListener;
+import org.eclipse.jface.action.IMenuManager;
+import org.eclipse.jface.action.MenuManager;
+import org.eclipse.jface.action.Separator;
+import org.eclipse.jface.viewers.ColumnViewerToolTipSupport;
+import org.eclipse.jface.viewers.DoubleClickEvent;
+import org.eclipse.jface.viewers.IDoubleClickListener;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.ITreeSelection;
+import org.eclipse.jface.viewers.StructuredSelection;
+import org.eclipse.jface.viewers.TreeSelection;
+import org.eclipse.jface.viewers.TreeViewer;
+import org.eclipse.swt.SWT;
+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.Menu;
+import org.eclipse.ui.IMemento;
+import org.eclipse.ui.IWorkbenchActionConstants;
+import org.eclipse.ui.PartInitException;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.part.ViewPart;
+import org.eclipse.ui.services.IEvaluationService;
+
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorolamobility.studio.android.db.core.DbCoreActivator;
+import com.motorolamobility.studio.android.db.core.DbRootNodeReader;
+import com.motorolamobility.studio.android.db.core.event.DatabaseModelEvent;
+import com.motorolamobility.studio.android.db.core.event.DatabaseModelEventManager;
+import com.motorolamobility.studio.android.db.core.event.IDatabaseModelListener;
+import com.motorolamobility.studio.android.db.core.ui.AbstractTreeNode;
+import com.motorolamobility.studio.android.db.core.ui.ITreeNode;
+import com.motorolamobility.studio.android.db.core.ui.RootNode;
+import com.motorolamobility.studio.android.db.core.ui.tree.DatabaseExplorerTreeContentProvider;
+import com.motorolamobility.studio.android.db.core.ui.tree.DatabaseExplorerTreeLabelProvider;
+
+/**
+ * View containing a {@link TreeViewer} that is expanded as the nodes from type {@link AbstractTreeNode} are double-clicked
+ */
+public class MOTODEVDatabaseExplorerView extends ViewPart implements IDatabaseModelListener
+{
+ public static final String VIEW_ID = "com.motorola.studio.android.db.databaseView"; //$NON-NLS-1$
+
+ public static final String DB_EXPLORER_VIEW_HELP = DbCoreActivator.PLUGIN_ID + ".dbexplorer"; //$NON-NLS-1$
+
+ public static final String DB_EXPLORER_VIEW_CONTR_BROWSE_TABLE_ID =
+ "com.motorolamobility.studio.android.db.core.createTableCommand";
+
+ private TreeViewer treeViewer;
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.part.WorkbenchPart#createPartControl(org.eclipse.swt.widgets.Composite)
+ */
+ @Override
+ public void createPartControl(Composite parent)
+ {
+ GridLayout layout = new GridLayout();
+ layout.numColumns = 1;
+ layout.verticalSpacing = 1;
+ layout.marginWidth = 0;
+ layout.marginHeight = 2;
+ parent.setLayout(layout);
+
+ GridData layoutData = new GridData();
+ layoutData = new GridData();
+ layoutData.grabExcessHorizontalSpace = true;
+ layoutData.grabExcessVerticalSpace = true;
+ layoutData.horizontalAlignment = GridData.FILL;
+ layoutData.verticalAlignment = GridData.FILL;
+
+ treeViewer = new TreeViewer(parent, SWT.SINGLE | SWT.VIRTUAL); //It is required due to ILazyTreeContentProvider
+ treeViewer.setContentProvider(new DatabaseExplorerTreeContentProvider(treeViewer));
+ treeViewer.setUseHashlookup(true);
+ treeViewer.setAutoExpandLevel(0);
+ treeViewer.setLabelProvider(new DatabaseExplorerTreeLabelProvider());
+ ColumnViewerToolTipSupport.enableFor(treeViewer);
+ treeViewer.setInput(getInitalInput());
+
+ treeViewer.getControl().setLayoutData(layoutData);
+
+ treeViewer.addDoubleClickListener(new IDoubleClickListener()
+ {
+ public void doubleClick(DoubleClickEvent event)
+ {
+ ISelection selection = event.getSelection();
+ if (selection instanceof TreeSelection)
+ {
+ TreeSelection treeSelection = (TreeSelection) selection;
+ Object element = treeSelection.getFirstElement();
+ if (element instanceof AbstractTreeNode)
+ {
+ if (!treeViewer.getExpandedState(element))
+ {
+ treeViewer.expandToLevel(element, 1);
+ }
+ else
+ {
+ treeViewer.collapseToLevel(element, 1);
+ }
+ }
+ }
+ }
+ });
+ hookContextMenu();
+
+ getSite().setSelectionProvider(treeViewer);
+
+ //register listener for model changes
+ DatabaseModelEventManager.getInstance().addListener(this);
+
+ // add context help
+ PlatformUI.getWorkbench().getHelpSystem().setHelp(parent, DB_EXPLORER_VIEW_HELP);
+ PlatformUI.getWorkbench().getHelpSystem()
+ .setHelp(treeViewer.getTree(), DB_EXPLORER_VIEW_HELP);
+ }
+
+ /**
+ * Loads the contributions from extension point com.motorolamobility.studio.android.db.core.dbRootNode
+ * @return root node (invisible) that contains as children the contributed tree nodes subclassing {@link AbstractTreeNode}.
+ */
+ private Object getInitalInput()
+ {
+ HashMap<String, AbstractTreeNode> model = new HashMap<String, AbstractTreeNode>();
+ RootNode rootNode = new RootNode();
+ try
+ {
+ DbRootNodeReader.loadRootNode(model);
+ Set<Entry<String, AbstractTreeNode>> modelEntries = model.entrySet();
+ List<ITreeNode> childrenList = new ArrayList<ITreeNode>(modelEntries.size());
+ for (Map.Entry<String, AbstractTreeNode> childNode : modelEntries)
+ {
+ childrenList.add(childNode.getValue());
+ }
+
+ rootNode.putChildren(childrenList);
+ }
+ catch (PartInitException e)
+ {
+ StudioLogger.error(MOTODEVDatabaseExplorerView.class,
+ "Problem creating MOTODEV Database Explorer Tree", e); //$NON-NLS-1$
+ }
+ return rootNode;
+ }
+
+ private void hookContextMenu()
+ {
+ MenuManager menuMgr = new MenuManager("#PopupMenu"); //$NON-NLS-1$
+ menuMgr.setRemoveAllWhenShown(true);
+ menuMgr.addMenuListener(new IMenuListener()
+ {
+ public void menuAboutToShow(IMenuManager manager)
+ {
+ fillContextMenu(manager);
+ }
+ });
+ Menu menu = menuMgr.createContextMenu(treeViewer.getControl());
+ treeViewer.getControl().setMenu(menu);
+ getSite().registerContextMenu(menuMgr, treeViewer);
+ }
+
+ private void fillContextMenu(IMenuManager manager)
+ {
+ // Other plug-ins can contribute there actions here
+ // manager.add(openClose);
+ manager.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS));
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.part.WorkbenchPart#setFocus()
+ */
+ @Override
+ public void setFocus()
+ {
+ treeViewer.getTree().setFocus();
+ }
+
+ /**
+ * @return the treeViewer
+ */
+ public TreeViewer getTreeViewer()
+ {
+ return treeViewer;
+ }
+
+ /**
+ * Get items selected in the tree
+ * @return
+ */
+ public ITreeNode getSelectedItemOnTree()
+ {
+ ITreeNode result = null;
+ ITreeSelection treeSelection = null;
+ ISelection selection = treeViewer.getSelection();
+
+ if (selection instanceof ITreeSelection)
+ {
+ treeSelection = (ITreeSelection) selection;
+ result = (ITreeNode) treeSelection.getFirstElement();
+ }
+
+ return result;
+
+ }
+
+ /**
+ * Collapse all nodes of the tree viewer.
+ **/
+ public void collapseAllTreeItems()
+ {
+ treeViewer.collapseAll();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.part.ViewPart#saveState(org.eclipse.ui.IMemento)
+ */
+ @Override
+ public void saveState(IMemento memento)
+ {
+ SaveStateManager.getInstance().saveState();
+ super.saveState(memento);
+ }
+
+ public void handleNodeAdditionEvent(final DatabaseModelEvent databaseModelEvent)
+ {
+ Display display = Display.getDefault();
+ if (!display.isDisposed())
+ {
+ display.syncExec(new Runnable()
+ {
+ public void run()
+ {
+ ITreeNode treeNodeItem = databaseModelEvent.getTreeNodeItem();
+ ITreeNode parentNode = treeNodeItem.getParent();
+ if (getTreeViewer().getExpandedState(parentNode))
+ {
+ getTreeViewer().add(parentNode, treeNodeItem);
+ }
+ else
+ {
+ List<ITreeNode> children = parentNode.getChildren();
+ if (children.size() > 0)
+ {
+ getTreeViewer().setChildCount(parentNode, children.size());
+ }
+ }
+ }
+ });
+ }
+ }
+
+ public void handleNodeRemovalEvent(final DatabaseModelEvent databaseModelEvent)
+ {
+ Display display = Display.getDefault();
+ if (!display.isDisposed())
+ {
+ display.asyncExec(new Runnable()
+ {
+ public void run()
+ {
+ if (!treeViewer.getTree().isDisposed())
+ {
+ getTreeViewer().remove(databaseModelEvent.getTreeNodeItem());
+ ITreeNode parentNode = databaseModelEvent.getTreeNodeItem().getParent();
+ getTreeViewer().setChildCount(parentNode, parentNode.getChildren().size());
+ }
+ }
+ });
+ }
+ }
+
+ public void handleNodeUpdateEvent(final DatabaseModelEvent databaseModelEvent)
+ {
+ Display display = Display.getDefault();
+ if (!display.isDisposed())
+ {
+ display.asyncExec(new Runnable()
+ {
+ public void run()
+ {
+ ITreeNode treeNode = databaseModelEvent.getTreeNodeItem();
+ getTreeViewer().update(treeNode, null);
+
+ //re-evaluate all properties to properly update commands status
+ //it's required since nodes may change theirs state/status without firing SelectionChanged events
+ for (String property : DbCoreActivator.getPluginProperties())
+ {
+ IEvaluationService service =
+ (IEvaluationService) PlatformUI.getWorkbench().getService(
+ IEvaluationService.class);
+ service.requestEvaluation(property); //properties are defined in plugin.xml (propertyTesters)
+ }
+ }
+ });
+ }
+ }
+
+ public void handleNodeClearEvent(final DatabaseModelEvent databaseModelEvent)
+ {
+ Display display = Display.getDefault();
+ if (!display.isDisposed())
+ {
+ display.asyncExec(new Runnable()
+ {
+ public void run()
+ {
+ ITreeNode parentNode = databaseModelEvent.getTreeNodeItem();
+ if (parentNode.isLoading())
+ {
+ treeViewer.remove(parentNode, parentNode.getChildren().toArray()); //Removing all children before calling refresh seems to resolve the AccessViolation - see bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=131209
+ treeViewer.refresh(parentNode);
+ }
+ else
+ {
+ treeViewer.collapseToLevel(parentNode, TreeViewer.ALL_LEVELS);
+ }
+ }
+ });
+ }
+ }
+
+ public void handleNodeRefreshEvent(final DatabaseModelEvent databaseModelEvent)
+ {
+ Display display = Display.getDefault();
+ if (!display.isDisposed())
+ {
+ display.asyncExec(new Runnable()
+ {
+ public void run()
+ {
+ ITreeNode node = databaseModelEvent.getTreeNodeItem();
+ getTreeViewer().refresh(node);
+ }
+ });
+ }
+ }
+
+ /**
+ * Closing the view
+ */
+ @Override
+ public void dispose()
+ {
+ super.dispose();
+ //remove listener for model changes
+ DatabaseModelEventManager.getInstance().removeListeners(this);
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorolamobility.studio.android.db.core.event.IDatabaseModelListener#handleNodeExpandEvent(com.motorolamobility.studio.android.db.core.event.DatabaseModelEvent)
+ */
+ public void handleNodeExpandEvent(final DatabaseModelEvent databaseModelEvent)
+ {
+ Display display = Display.getDefault();
+ if (!display.isDisposed())
+ {
+ display.asyncExec(new Runnable()
+ {
+ public void run()
+ {
+ treeViewer.expandToLevel(databaseModelEvent.getTreeNodeItem(), 1);
+ }
+ });
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorolamobility.studio.android.db.core.event.IDatabaseModelListener#handleNodeSelectEvent(com.motorolamobility.studio.android.db.core.event.DatabaseModelEvent)
+ */
+ public void handleNodeSelectEvent(final DatabaseModelEvent databaseModelEvent)
+ {
+ Display display = Display.getDefault();
+ if (!display.isDisposed())
+ {
+ display.asyncExec(new Runnable()
+ {
+ public void run()
+ {
+ treeViewer.setSelection(
+ new StructuredSelection(databaseModelEvent.getTreeNodeItem()), true);
+ }
+ });
+ }
+ }
+}
diff --git a/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/ui/view/SaveStateManager.java b/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/ui/view/SaveStateManager.java
new file mode 100644
index 0000000..13c8b10
--- /dev/null
+++ b/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/ui/view/SaveStateManager.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.studio.android.db.core.ui.view;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.eclipse.core.runtime.preferences.IEclipsePreferences;
+import org.eclipse.core.runtime.preferences.InstanceScope;
+
+import com.motorolamobility.studio.android.db.core.DbCoreActivator;
+import com.motorolamobility.studio.android.db.core.ui.ISaveStateTreeNode;
+
+/**
+ * Manager class responsible to handle save state for tree nodes.
+ * Note: The saveState method will be called only after workbench quits, quiting the view will not fire the save event state.
+ * This class is a singleton.
+ * Users must register the ISaveStateTreenode by using resiterSaveStateNode method.
+ * Don't forget to unregister the node if node is removed.
+ */
+public class SaveStateManager
+{
+
+ private static SaveStateManager instance;
+
+ private final Set<ISaveStateTreeNode> registeredTreeNodes;
+
+ private final IEclipsePreferences prefNode;
+
+ /*
+ * Private constructor, this is a singleton.
+ */
+ private SaveStateManager()
+ {
+ registeredTreeNodes = Collections.synchronizedSet(new HashSet<ISaveStateTreeNode>());
+ prefNode = InstanceScope.INSTANCE.getNode(DbCoreActivator.PLUGIN_ID);
+ }
+
+ /**
+ * @return the single instance of {@link SaveStateManager}
+ */
+ public static SaveStateManager getInstance()
+ {
+ if (instance == null)
+ {
+ instance = new SaveStateManager();
+ }
+
+ return instance;
+ }
+
+ /**
+ * Register a node to be saved.
+ * @param treeNode to be asked for save
+ */
+ public void registerSaveStateNode(ISaveStateTreeNode treeNode)
+ {
+ synchronized (registeredTreeNodes)
+ {
+ registeredTreeNodes.add(treeNode);
+ }
+ }
+
+ /**
+ * Calls the saveState method for all registered nodes.
+ * @param memento
+ */
+ public void saveState()
+ {
+ synchronized (registeredTreeNodes)
+ {
+ for (ISaveStateTreeNode registeredTreeNode : registeredTreeNodes)
+ {
+ registeredTreeNode.saveState(prefNode);
+ }
+ }
+ }
+
+ /**
+ * Unregister the given node from this manager.
+ * @param node
+ */
+ public void unregisterSaveStateNode(ISaveStateTreeNode node)
+ {
+ synchronized (registeredTreeNodes)
+ {
+ registeredTreeNodes.remove(node);
+ }
+ }
+
+ /**
+ * Retrieves the {@link IEclipsePreferences} prefNode.
+ * @return The {@link IEclipsePreferences} node used bu this manager to save the states.
+ */
+ public IEclipsePreferences getPrefNode()
+ {
+ return prefNode;
+ }
+}
diff --git a/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/ui/wizards/AddTableFieldDialog.java b/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/ui/wizards/AddTableFieldDialog.java
new file mode 100644
index 0000000..fd137c3
--- /dev/null
+++ b/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/ui/wizards/AddTableFieldDialog.java
@@ -0,0 +1,291 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.studio.android.db.core.ui.wizards;
+
+import org.eclipse.jface.dialogs.Dialog;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.ModifyEvent;
+import org.eclipse.swt.events.ModifyListener;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Combo;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Text;
+
+import com.motorolamobility.studio.android.db.core.i18n.DbCoreNLS;
+import com.motorolamobility.studio.android.db.core.model.Field;
+import com.motorolamobility.studio.android.db.core.model.Field.AutoIncrementType;
+import com.motorolamobility.studio.android.db.core.model.Field.DataType;
+
+public class AddTableFieldDialog extends Dialog
+{
+
+ private Field field;
+
+ private Text nameText;
+
+ private Combo typeCombo;
+
+ private Text defaultText;
+
+ private Button isPrimaryButton;
+
+ private Composite primaryKeyOptions;
+
+ private Button noneButton;
+
+ private Button incrementalButton;
+
+ private Button decrementalButton;
+
+ private Label primaryKeyBehavior;
+
+ public AddTableFieldDialog(Shell parentShell)
+ {
+ super(parentShell);
+ }
+
+ public AddTableFieldDialog(Shell parentShell, Field newField)
+ {
+ super(parentShell);
+ field = newField;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.jface.dialogs.Dialog#createDialogArea(org.eclipse.swt.widgets.Composite)
+ */
+ @Override
+ public Control createDialogArea(Composite parent)
+ {
+ this.getShell().setText(DbCoreNLS.CreateTableWizardPage_AddEditField_DialogTitle);
+ Composite composite = new Composite(parent, SWT.FILL);
+ GridData layoutData = new GridData(SWT.FILL, SWT.FILL, true, true);
+ composite.setLayout(new GridLayout(2, false));
+ composite.setLayoutData(layoutData);
+ layoutData = new GridData(SWT.LEFT, SWT.CENTER, false, false);
+
+ Label nameLabel = new Label(composite, SWT.NONE);
+ nameLabel.setText(DbCoreNLS.AddTableFieldDialog_FieldNameLabel);
+ nameLabel.setLayoutData(layoutData);
+
+ layoutData = new GridData(SWT.FILL, SWT.CENTER, true, false);
+ nameText = new Text(composite, SWT.BORDER);
+ nameText.setLayoutData(layoutData);
+ if (field != null)
+ {
+ nameText.setText(field.getName());
+ }
+
+ nameText.addModifyListener(new ModifyListener()
+ {
+
+ public void modifyText(ModifyEvent e)
+ {
+ getButton(OK).setEnabled(
+ (nameText.getText().trim().length() > 0)
+ && !(nameText.getText().trim().contains(" "))); //$NON-NLS-1$
+
+ }
+ });
+
+ layoutData = new GridData(SWT.FILL, SWT.CENTER, true, false, 2, 1);
+ isPrimaryButton = new Button(composite, SWT.CHECK);
+ isPrimaryButton.setLayoutData(layoutData);
+ isPrimaryButton.setText(DbCoreNLS.AddTableFieldDialog_PrimaryKeyLabel);
+ if (field != null)
+ {
+ isPrimaryButton.setSelection(field.isPrimaryKey());
+
+ }
+
+ isPrimaryButton.addSelectionListener(new SelectionAdapter()
+ {
+ @Override
+ public void widgetSelected(SelectionEvent e)
+ {
+ defaultText.setEnabled(!(isPrimaryButton.getSelection()));
+ primaryKeyOptions.setEnabled((isPrimaryButton.getSelection()));
+ }
+ });
+ primaryKeyOptions = new Composite(composite, SWT.BORDER)
+ {
+ @Override
+ public void setEnabled(boolean enabled)
+ {
+ noneButton.setEnabled(enabled);
+ incrementalButton.setEnabled(enabled);
+ decrementalButton.setEnabled(enabled);
+ primaryKeyBehavior.setEnabled(enabled);
+
+ super.setEnabled(enabled);
+ }
+ };
+ layoutData = new GridData(SWT.FILL, SWT.FILL, true, true, 2, 1);
+ primaryKeyOptions.setLayout(new GridLayout(1, false));
+ primaryKeyOptions.setLayoutData(layoutData);
+
+ primaryKeyBehavior = new Label(primaryKeyOptions, SWT.NONE);
+ primaryKeyBehavior.setLayoutData(layoutData);
+ primaryKeyBehavior.setText(DbCoreNLS.AddTableFieldDialog_PrimaryKeyAutomaticBehaviourLabel);
+
+ layoutData = new GridData(SWT.FILL, SWT.CENTER, true, false, 2, 1);
+ noneButton = new Button(primaryKeyOptions, SWT.RADIO);
+ noneButton.setLayoutData(layoutData);
+ noneButton.setText(DbCoreNLS.AddTableFieldDialog_PrimaryKeyAutomaticBehaviour_NoneLabel);
+
+ incrementalButton = new Button(primaryKeyOptions, SWT.RADIO);
+ incrementalButton.setLayoutData(layoutData);
+ incrementalButton
+ .setText(DbCoreNLS.AddTableFieldDialog_PrimaryKeyAutomaticBehaviour_IncrementalLabel);
+
+ decrementalButton = new Button(primaryKeyOptions, SWT.RADIO);
+ decrementalButton.setLayoutData(layoutData);
+ decrementalButton
+ .setText(DbCoreNLS.AddTableFieldDialog_PrimaryKeyAutomaticBehaviour_DecrementalLabel);
+
+ Boolean isNew = (field != null) && (field.isPrimaryKey());
+ if (isNew)
+ {
+ noneButton.setSelection((field.getAutoIncrementType() == AutoIncrementType.NONE));
+ incrementalButton
+ .setSelection((field.getAutoIncrementType() == AutoIncrementType.ASCENDING));
+ decrementalButton
+ .setSelection((field.getAutoIncrementType() == AutoIncrementType.DESCENDING));
+
+ }
+ else
+ {
+ noneButton.setSelection(true);
+ }
+
+ primaryKeyOptions.setEnabled(isNew);
+
+ layoutData = new GridData(SWT.LEFT, SWT.CENTER, false, false);
+ Label typeLabel = new Label(composite, SWT.NONE);
+ typeLabel.setText(DbCoreNLS.AddTableFieldDialog_FieldTypeLabel);
+ typeLabel.setLayoutData(layoutData);
+
+ layoutData = new GridData(SWT.FILL, SWT.CENTER, true, false);
+ typeCombo = new Combo(composite, SWT.READ_ONLY);
+ typeCombo.setLayoutData(layoutData);
+
+ int integerTypeIndex = 0;
+
+ for (DataType type : DataType.values())
+ {
+ typeCombo.add(type.toString());
+
+ if (type.equals(DataType.INTEGER))
+ {
+ integerTypeIndex = typeCombo.getItemCount() - 1;
+ }
+
+ }
+
+ typeCombo.select(integerTypeIndex);
+
+ if (field != null)
+ {
+ typeCombo.select(typeCombo.indexOf(field.getType().toString()));
+ }
+
+ layoutData = new GridData(SWT.LEFT, SWT.CENTER, false, false);
+ Label defaultLabel = new Label(composite, SWT.NONE);
+ defaultLabel.setText(DbCoreNLS.AddTableFieldDialog_FieldDefaultValueLabel);
+ defaultLabel.setLayoutData(layoutData);
+
+ layoutData = new GridData(SWT.FILL, SWT.CENTER, true, false);
+ defaultText = new Text(composite, SWT.BORDER);
+ defaultText.setLayoutData(layoutData);
+ if (field != null)
+ {
+ defaultText.setText(field.getDefaultValue());
+ }
+
+ if (field != null)
+ {
+ defaultText.setEnabled(!field.isPrimaryKey());
+
+ primaryKeyOptions.setEnabled(field.isPrimaryKey());
+
+ }
+
+ composite.layout();
+
+ return composite;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.jface.dialogs.Dialog#okPressed()
+ */
+ @Override
+ protected void okPressed()
+ {
+ if (field == null)
+ {
+ field = new Field();
+ }
+
+ field.setName(nameText.getText());
+ field.setPrimaryKey(isPrimaryButton.getSelection());
+ field.setDefaultValue(defaultText.getText().trim());
+ field.setType(DataType.valueOf(typeCombo.getItem(typeCombo.getSelectionIndex())));
+
+ if (noneButton.getSelection())
+ {
+
+ field.setAutoIncrementType(Field.AutoIncrementType.NONE);
+ }
+ else if (incrementalButton.getSelection())
+ {
+ field.setAutoIncrementType(Field.AutoIncrementType.ASCENDING);
+ }
+ else if (decrementalButton.getSelection())
+ {
+ field.setAutoIncrementType(Field.AutoIncrementType.DESCENDING);
+ }
+
+ super.okPressed();
+
+ }
+
+ public Field getField()
+ {
+ return field;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.jface.dialogs.Dialog#createButton(org.eclipse.swt.widgets.Composite, int, java.lang.String, boolean)
+ */
+ @Override
+ protected Button createButton(Composite parent, int id, String label, boolean defaultButton)
+ {
+ Button button = super.createButton(parent, id, label, defaultButton);
+ button.setEnabled((id != OK) || (field != null));
+ return button;
+
+ }
+
+}
diff --git a/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/ui/wizards/CreateTableWizard.java b/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/ui/wizards/CreateTableWizard.java
new file mode 100644
index 0000000..50527cd
--- /dev/null
+++ b/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/ui/wizards/CreateTableWizard.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.studio.android.db.core.ui.wizards;
+
+import java.util.Set;
+
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.jface.wizard.Wizard;
+
+import com.motorolamobility.studio.android.db.core.DbCoreActivator;
+import com.motorolamobility.studio.android.db.core.i18n.DbCoreNLS;
+import com.motorolamobility.studio.android.db.core.model.TableModel;
+
+/**
+ * Class that represents the Create Table Wizard
+ */
+public class CreateTableWizard extends Wizard
+{
+ private TableModel table = null;
+
+ private CreateTableWizardPage tableWizardPage = null;
+
+ private Set<String> notAllowedNames = null;
+
+ private static final String WIZARD_BANNER = "icons/wizban/create_table.png"; //$NON-NLS-1$
+
+ public CreateTableWizard()
+ {
+ setWindowTitle(DbCoreNLS.CreateTableWizardPage_UI_PageTitle);
+ setDefaultPageImageDescriptor(DbCoreActivator.imageDescriptorFromPlugin(
+ DbCoreActivator.PLUGIN_ID, WIZARD_BANNER));
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.jface.wizard.Wizard#performFinish()
+ */
+ @Override
+ public boolean performFinish()
+ {
+ table = tableWizardPage.getTable();
+ boolean isOK = true;
+ if ((notAllowedNames != null)
+ && notAllowedNames.contains(tableWizardPage.getTable().getName().toUpperCase()))
+ {
+ MessageDialog.openError(getShell(),
+ DbCoreNLS.CreateTableWizard_UI_Message_ErrorCreatingTable,
+ DbCoreNLS.ERR_CreateDatabaseWizardPage_TableAlreadyExistTitle);
+ isOK = false;
+ }
+
+ return isOK;
+
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.jface.wizard.Wizard#addPages()
+ */
+ @Override
+ public void addPages()
+ {
+ tableWizardPage = new CreateTableWizardPage();
+ addPage(tableWizardPage);
+
+ if (table != null)
+ {
+ tableWizardPage.setTable(table);
+ }
+ }
+
+ /**
+ * Used when the user wants to retrieve a table object and not create the table itself
+ * @param table
+ */
+ public void init(TableModel table)
+ {
+ this.table = table;
+ }
+
+ /**
+ * @return the table
+ */
+ public TableModel getTable()
+ {
+ return table;
+ }
+
+ public void setNotAllowedNames(Set<String> notAllowedNames)
+ {
+ this.notAllowedNames = notAllowedNames;
+
+ }
+
+}
diff --git a/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/ui/wizards/CreateTableWizardPage.java b/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/ui/wizards/CreateTableWizardPage.java
new file mode 100644
index 0000000..e368058
--- /dev/null
+++ b/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/ui/wizards/CreateTableWizardPage.java
@@ -0,0 +1,333 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.studio.android.db.core.ui.wizards;
+
+import org.eclipse.jface.dialogs.Dialog;
+import org.eclipse.jface.viewers.DoubleClickEvent;
+import org.eclipse.jface.viewers.IDoubleClickListener;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.jface.viewers.TableViewerColumn;
+import org.eclipse.jface.wizard.WizardPage;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.ModifyEvent;
+import org.eclipse.swt.events.ModifyListener;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.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.Label;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.ui.PlatformUI;
+
+import com.motorolamobility.studio.android.db.core.DbCoreActivator;
+import com.motorolamobility.studio.android.db.core.i18n.DbCoreNLS;
+import com.motorolamobility.studio.android.db.core.model.Field;
+import com.motorolamobility.studio.android.db.core.model.TableModel;
+
+public class CreateTableWizardPage extends WizardPage
+{
+ private TableViewer viewer;
+
+ private Text tableName;
+
+ private TableModel table = null;
+
+ private final String TABLE_CONTEXT_HELP_ID = DbCoreActivator.PLUGIN_ID + ".create_table_wizard"; //$NON-NLS-1$
+
+ protected CreateTableWizardPage()
+ {
+ super(DbCoreNLS.CreateTableWizardPage_UI_PageTitle);
+ setTitle(DbCoreNLS.CreateTableWizardPage_UI_CreateNewTable);
+ setMessage(DbCoreNLS.CreateTableWizardPage_UI_CreateNewTableAddingItsFields);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.jface.dialogs.IDialogPage#createControl(org.eclipse.swt.widgets.Composite)
+ */
+ public void createControl(Composite parent)
+ {
+ Composite composite = new Composite(parent, SWT.FILL);
+ composite.setLayout(new GridLayout(2, false));
+
+ GridData layoutData = new GridData(SWT.FILL, SWT.NONE, true, false);
+
+ Composite nameComposite = new Composite(composite, SWT.FILL);
+ nameComposite.setLayout(new GridLayout(2, false));
+ nameComposite.setLayoutData(layoutData);
+
+ layoutData = new GridData(SWT.LEFT, SWT.NONE, false, false);
+
+ Label tableNameLabel = new Label(nameComposite, SWT.NONE);
+ tableNameLabel.setLayoutData(layoutData);
+ tableNameLabel.setText(DbCoreNLS.CreateTableWizardPage_UI_TableName);
+
+ layoutData = new GridData(SWT.FILL, SWT.NONE, true, false);
+ tableName = new Text(nameComposite, SWT.BORDER | SWT.SINGLE);
+ tableName.setLayoutData(layoutData);
+ tableName.addModifyListener(new ModifyListener()
+ {
+
+ public void modifyText(ModifyEvent e)
+ {
+ if (viewer != null)
+ {
+ viewer.refresh();
+
+ validatePage();
+
+ ((TableModel) viewer.getInput()).setName(tableName.getText());
+ }
+
+ }
+ });
+
+ if ((table != null) && (table.getName() != null))
+ {
+ tableName.setText(table.getName());
+ }
+
+ Composite emptyComposite = new Composite(composite, SWT.RIGHT);
+ emptyComposite.setLayout(new GridLayout(1, false));
+ emptyComposite.layout();
+
+ viewer =
+ new TableViewer(composite, SWT.BORDER | SWT.MULTI | SWT.READ_ONLY
+ | SWT.FULL_SELECTION);
+ layoutData = new GridData(SWT.FILL, SWT.FILL, true, true);
+
+ TableViewerColumn column = new TableViewerColumn(viewer, SWT.NONE);
+ column.getColumn().setText(DbCoreNLS.CreateTableWizardPage_UI_Name);
+ column.getColumn().setResizable(true);
+ column.getColumn().setWidth(100);
+
+ column = new TableViewerColumn(viewer, SWT.NONE);
+ column.getColumn().setText(DbCoreNLS.CreateTableWizardPage_UI_Type);
+ column.getColumn().setResizable(true);
+ column.getColumn().setWidth(100);
+
+ column = new TableViewerColumn(viewer, SWT.NONE);
+ column.getColumn().setText(DbCoreNLS.CreateTableWizardPage_UI_Default);
+ column.getColumn().setResizable(true);
+ column.getColumn().setWidth(100);
+
+ column = new TableViewerColumn(viewer, SWT.NONE);
+ column.getColumn().setText(DbCoreNLS.CreateTableWizardPage_UI_Primary);
+ column.getColumn().setResizable(true);
+ column.getColumn().setWidth(100);
+
+ viewer.getTable().setHeaderVisible(true);
+ viewer.getTable().setLinesVisible(true);
+ viewer.getTable().setLayoutData(layoutData);
+
+ viewer.setContentProvider(new TableWizardContentProvider());
+ viewer.setLabelProvider(new TableWizardLabelProvider());
+
+ Composite buttonBar = new Composite(composite, SWT.NONE);
+ layoutData = new GridData(SWT.RIGHT, SWT.TOP, false, true);
+ buttonBar.setLayoutData(layoutData);
+
+ buttonBar.setLayout(new FillLayout(SWT.VERTICAL));
+ Button add = new Button(buttonBar, SWT.PUSH);
+ add.setText(DbCoreNLS.CreateTableWizardPage_UI_Add);
+ add.addSelectionListener(new SelectionListener()
+ {
+
+ public void widgetSelected(SelectionEvent e)
+ {
+ AddTableFieldDialog diag = new AddTableFieldDialog(getShell());
+
+ if (diag.open() == Dialog.OK)
+ {
+ ((TableModel) viewer.getInput()).addField(diag.getField());
+ viewer.refresh();
+ validatePage();
+ }
+
+ }
+
+ public void widgetDefaultSelected(SelectionEvent e)
+ {
+
+ }
+ });
+
+ final Button edit = new Button(buttonBar, SWT.PUSH);
+ edit.setText(DbCoreNLS.CreateTableWizardPage_UI_Edit);
+ edit.setEnabled(false);
+ edit.addSelectionListener(new SelectionAdapter()
+ {
+ @Override
+ public void widgetSelected(SelectionEvent e)
+ {
+ if (viewer.getTable().getSelectionCount() == 1)
+ {
+ AddTableFieldDialog diag =
+ new AddTableFieldDialog(getShell(), ((TableModel) viewer.getInput())
+ .getFields().get(viewer.getTable().getSelectionIndex()));
+ if (diag.open() == Dialog.OK)
+ {
+ viewer.update(diag.getField(), null);
+ validatePage();
+ }
+
+ }
+
+ }
+ });
+
+ final Button remove = new Button(buttonBar, SWT.PUSH);
+ remove.setText(DbCoreNLS.CreateTableWizardPage_UI_Remove);
+ remove.setEnabled(false);
+ remove.addSelectionListener(new SelectionAdapter()
+ {
+ @Override
+ public void widgetSelected(SelectionEvent e)
+ {
+ ISelection selection = viewer.getSelection();
+ if (selection instanceof IStructuredSelection)
+ {
+ IStructuredSelection structuredSelection = (IStructuredSelection) selection;
+
+ for (Object obj : structuredSelection.toList())
+ {
+ if (obj instanceof Field)
+ {
+ ((TableModel) viewer.getInput()).removeField(((Field) obj));
+ }
+ }
+ viewer.refresh();
+ validatePage();
+
+ }
+ }
+ });
+
+ if (table == null)
+ {
+ table = new TableModel();
+ }
+ viewer.setInput(table);
+ viewer.addSelectionChangedListener(new ISelectionChangedListener()
+ {
+
+ public void selectionChanged(SelectionChangedEvent event)
+ {
+ int selectionCount = viewer.getTable().getSelectionCount();
+ remove.setEnabled(selectionCount > 0);
+ edit.setEnabled(selectionCount == 1);
+
+ }
+ });
+
+ viewer.addDoubleClickListener(new IDoubleClickListener()
+ {
+
+ public void doubleClick(DoubleClickEvent event)
+ {
+ if (viewer.getTable().getSelectionCount() == 1)
+ {
+ AddTableFieldDialog diag =
+ new AddTableFieldDialog(getShell(), ((TableModel) viewer.getInput())
+ .getFields().get(viewer.getTable().getSelectionIndex()));
+ if (diag.open() == Dialog.OK)
+ {
+
+ viewer.update(diag.getField(), null);
+ validatePage();
+ }
+
+ }
+
+ }
+ });
+ viewer.refresh();
+
+ composite.pack();
+ composite.layout();
+ setPageComplete(false);
+ setErrorMessage(null);
+
+ setControl(composite);
+
+ //table fields will be not empty at this point when user is editing a table, so page must be validated to enable finish button
+ if (!table.getFields().isEmpty())
+ {
+ validatePage();
+ }
+
+ PlatformUI.getWorkbench().getHelpSystem().setHelp(parent, TABLE_CONTEXT_HELP_ID);
+ PlatformUI.getWorkbench().getHelpSystem().setHelp(composite, TABLE_CONTEXT_HELP_ID);
+ }
+
+ public void setTable(TableModel table)
+ {
+ this.table = table;
+ }
+
+ public TableModel getTable()
+ {
+ return table;
+ }
+
+ public String getTableName()
+ {
+ return tableName.getText();
+ }
+
+ private void validatePage()
+ {
+ String errMsg = null;
+
+ if (tableName.getText().trim().length() == 0)
+ {
+ errMsg = DbCoreNLS.CreateTableWizardPage_UI_TableNameCannotBeEmpty;
+ }
+
+ if (((errMsg == null) && (tableName.getText().trim().contains(" ")))) //$NON-NLS-1$
+ {
+ errMsg = DbCoreNLS.CreateTableWizardPage_UI_InvalidTableName;
+ }
+
+ // Validate table name to don't use sqlite keyword
+ if (!TableModel.validateName(tableName.getText()))
+ {
+ errMsg = DbCoreNLS.CreateTableWizardPage_UI_InvalidTableName;
+ }
+
+ if ((errMsg == null) && (viewer.getTable().getItemCount() == 0))
+ {
+ errMsg = DbCoreNLS.CreateTableWizardPage_UI_YouMustSupplyAtLeastOneField;
+ }
+ if (errMsg == null)
+ {
+ errMsg = ((TableModel) viewer.getInput()).getErrorMessage();
+ }
+
+ setErrorMessage(errMsg);
+ setPageComplete(errMsg == null);
+
+ }
+
+}
diff --git a/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/ui/wizards/TableLabelProvider.java b/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/ui/wizards/TableLabelProvider.java
new file mode 100644
index 0000000..e3a1957
--- /dev/null
+++ b/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/ui/wizards/TableLabelProvider.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.studio.android.db.core.ui.wizards;
+
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.jface.viewers.LabelProvider;
+import org.eclipse.jface.viewers.TreeNode;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.ui.plugin.AbstractUIPlugin;
+
+import com.motorolamobility.studio.android.db.core.DbCoreActivator;
+import com.motorolamobility.studio.android.db.core.model.TableModel;
+
+public class TableLabelProvider extends LabelProvider
+{
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.viewers.LabelProvider#getImage(java.lang.Object)
+ */
+ @Override
+ public Image getImage(Object element)
+ {
+ if (element instanceof TableModel)
+ {
+ ImageDescriptor desc =
+ AbstractUIPlugin.imageDescriptorFromPlugin(
+ DbCoreActivator.DATATOOLS_UI_PLUGIN_ID, DbCoreActivator.TABLE_ICON);
+ Image resultImage = desc.createImage();
+ return resultImage;
+ }
+ else
+ {
+ return super.getImage(element);
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.viewers.LabelProvider#getText(java.lang.Object)
+ */
+ @Override
+ public String getText(Object element)
+ {
+ if (element instanceof TreeNode)
+ {
+ TreeNode treeNode = (TreeNode) element;
+ Object value = treeNode.getValue();
+ if (value instanceof TableModel)
+ {
+ TableModel tableModel = (TableModel) value;
+ return tableModel.getName();
+ }
+ }
+ return super.getText(element);
+ }
+}
diff --git a/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/ui/wizards/TableWizardContentProvider.java b/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/ui/wizards/TableWizardContentProvider.java
new file mode 100644
index 0000000..468ff0c
--- /dev/null
+++ b/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/ui/wizards/TableWizardContentProvider.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.studio.android.db.core.ui.wizards;
+
+import org.eclipse.jface.viewers.IStructuredContentProvider;
+import org.eclipse.jface.viewers.Viewer;
+
+import com.motorolamobility.studio.android.db.core.model.TableModel;
+
+public class TableWizardContentProvider implements IStructuredContentProvider
+{
+
+ public void dispose()
+ {
+ //Do nothing.
+ }
+
+ public void inputChanged(Viewer viewer, Object oldInput, Object newInput)
+ {
+ //Do nothing.
+ }
+
+ public Object[] getElements(Object inputElement)
+ {
+ Object[] children = new Object[0];
+
+ if (inputElement instanceof TableModel)
+ {
+ TableModel table = (TableModel) inputElement;
+ children = table.getFields().toArray();
+ }
+ return children;
+ }
+
+}
diff --git a/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/ui/wizards/TableWizardLabelProvider.java b/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/ui/wizards/TableWizardLabelProvider.java
new file mode 100644
index 0000000..9166873
--- /dev/null
+++ b/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/ui/wizards/TableWizardLabelProvider.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.studio.android.db.core.ui.wizards;
+
+import org.eclipse.jface.viewers.ILabelProviderListener;
+import org.eclipse.jface.viewers.ITableLabelProvider;
+import org.eclipse.swt.graphics.Image;
+
+import com.motorolamobility.studio.android.db.core.i18n.DbCoreNLS;
+import com.motorolamobility.studio.android.db.core.model.Field;
+
+public class TableWizardLabelProvider implements ITableLabelProvider
+{
+
+ public Image getColumnImage(Object element, int columnIndex)
+ {
+ return null;
+ }
+
+ public String getColumnText(Object element, int columnIndex)
+ {
+ String output = new String();
+ if (element instanceof Field)
+ {
+ switch (columnIndex)
+ {
+ case 0:
+ output = ((Field) element).getName();
+ break;
+ case 1:
+ output = ((Field) element).getType().toString();
+ break;
+ case 2:
+ output =
+ ((Field) element).isPrimaryKey()
+ ? "" : ((Field) element).getDefaultValue(); //$NON-NLS-1$
+ break;
+ case 3:
+ output =
+ ((Field) element).isPrimaryKey()
+ ? DbCoreNLS.TableWizardLabelProvider_isPrimary_true
+ : DbCoreNLS.TableWizardLabelProvider_isPrimary_False;
+ break;
+
+ }
+
+ }
+ return output;
+ }
+
+ public void addListener(ILabelProviderListener listener)
+ {
+ //Do nothing.
+ }
+
+ public void dispose()
+ {
+ //Do nothing.
+ }
+
+ public boolean isLabelProperty(Object element, String property)
+ {
+ return false;
+ }
+
+ public void removeListener(ILabelProviderListener listener)
+ {
+ //Do nothing.
+ }
+
+}
diff --git a/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/ui/wizards/createdb/CreateDatabaseWizard.java b/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/ui/wizards/createdb/CreateDatabaseWizard.java
new file mode 100644
index 0000000..a560700
--- /dev/null
+++ b/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/ui/wizards/createdb/CreateDatabaseWizard.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.motorolamobility.studio.android.db.core.ui.wizards.createdb;
+
+import java.util.List;
+
+import org.eclipse.jface.dialogs.IDialogConstants;
+import org.eclipse.jface.dialogs.MessageDialogWithToggle;
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.jface.wizard.Wizard;
+import org.eclipse.ui.IPerspectiveDescriptor;
+import org.eclipse.ui.IPerspectiveRegistry;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.PlatformUI;
+
+import com.motorolamobility.studio.android.db.core.DbCoreActivator;
+import com.motorolamobility.studio.android.db.core.i18n.DbCoreNLS;
+import com.motorolamobility.studio.android.db.core.model.TableModel;
+
+public class CreateDatabaseWizard extends Wizard
+{
+ private static final String WIZBAN_ICON = "icons/wizban/create_database_ban.png"; //$NON-NLS-1$
+
+ //it is an old plugin because of backward compatibility
+ private static final String DB_PERSPECTIVE = "com.motorola.studio.android.db.perspective"; //$NON-NLS-1$
+
+ private static final String SWITCH_MOTODEV_DATABASE_PERSPECTIVE =
+ "switch.perspective.to.motodevstudio.database"; //$NON-NLS-1$
+
+ private final List<String> alreadyAvailableDbs;
+
+ private CreateDatabaseWizardPage createDatabaseWizardPage;
+
+ private String dbName;
+
+ private List<TableModel> tables;
+
+ public CreateDatabaseWizard(final List<String> alreadyAvailableDbs)
+ {
+ this.alreadyAvailableDbs = alreadyAvailableDbs;
+
+ setWindowTitle(DbCoreNLS.CreateDatabaseWizardPage_UI_PageTitle);
+ setDefaultPageImageDescriptor(DbCoreActivator.imageDescriptorFromPlugin(
+ DbCoreActivator.PLUGIN_ID, WIZBAN_ICON));
+ setNeedsProgressMonitor(true);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.wizard.Wizard#addPages()
+ */
+ @Override
+ public void addPages()
+ {
+ createDatabaseWizardPage = new CreateDatabaseWizardPage(alreadyAvailableDbs);
+ addPage(createDatabaseWizardPage);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.wizard.Wizard#performFinish()
+ */
+ @Override
+ public boolean performFinish()
+ {
+ dbName = createDatabaseWizardPage.getDatabaseName();
+ tables = createDatabaseWizardPage.getTables();
+ boolean canProceed = (dbName != null) && !"".equals(dbName); //$NON-NLS-1$
+ if (canProceed)
+ {
+ changePerspective();
+ }
+ return canProceed; //$NON-NLS-1$
+ }
+
+ /**
+ * @return the dbName
+ */
+ public String getDbName()
+ {
+ return dbName;
+ }
+
+ /**
+ * @return the tables
+ */
+ public List<TableModel> getTables()
+ {
+ return tables;
+ }
+
+ private boolean confirmPerspectiveSwitch(IWorkbenchWindow window,
+ IPerspectiveDescriptor perspective)
+ {
+ IPreferenceStore store = DbCoreActivator.getDefault().getPreferenceStore();
+ String preference = store.getString(SWITCH_MOTODEV_DATABASE_PERSPECTIVE);
+
+ if (preference.equals("")) //$NON-NLS-1$
+ {
+ store.setValue(SWITCH_MOTODEV_DATABASE_PERSPECTIVE, MessageDialogWithToggle.PROMPT);
+ preference = MessageDialogWithToggle.PROMPT;
+ }
+
+ boolean result;
+
+ if (MessageDialogWithToggle.ALWAYS.equals(preference))
+ {
+ result = true;
+ }
+ else if (MessageDialogWithToggle.NEVER.equals(preference))
+ {
+ result = false;
+ }
+ else
+ {
+ MessageDialogWithToggle dialog =
+ MessageDialogWithToggle.openYesNoQuestion(window.getShell(),
+ DbCoreNLS.UI_CreateDatabaseWizard_ChangePerspectiveTitle,
+ DbCoreNLS.UI_CreateDatabaseWizard_ChangePerspectiveQuestion, null,
+ false, store, SWITCH_MOTODEV_DATABASE_PERSPECTIVE);
+ int dialogResult = dialog.getReturnCode();
+
+ result = dialogResult == IDialogConstants.YES_ID;
+ }
+
+ return result;
+ }
+
+ /**
+ * Changes the perspective
+ *
+ */
+ private void changePerspective()
+ {
+ IPerspectiveRegistry reg = PlatformUI.getWorkbench().getPerspectiveRegistry();
+ IPerspectiveDescriptor perspective = reg.findPerspectiveWithId(DB_PERSPECTIVE);
+
+ IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
+ IWorkbenchPage page = window.getActivePage();
+ if (page != null)
+ {
+ IPerspectiveDescriptor currentPersp = page.getPerspective();
+
+ if ((currentPersp != null) && !DB_PERSPECTIVE.contains(currentPersp.getId()))
+ {
+ boolean changePerspective = confirmPerspectiveSwitch(window, perspective);
+ if (changePerspective)
+ {
+ page.setPerspective(perspective);
+ }
+ }
+ }
+ }
+
+}
diff --git a/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/ui/wizards/createdb/CreateDatabaseWizardPage.java b/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/ui/wizards/createdb/CreateDatabaseWizardPage.java
new file mode 100644
index 0000000..ffe4d62
--- /dev/null
+++ b/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/ui/wizards/createdb/CreateDatabaseWizardPage.java
@@ -0,0 +1,385 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.motorolamobility.studio.android.db.core.ui.wizards.createdb;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import org.eclipse.jface.dialogs.Dialog;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.ITreeSelection;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+import org.eclipse.jface.viewers.TreeNode;
+import org.eclipse.jface.viewers.TreeNodeContentProvider;
+import org.eclipse.jface.viewers.TreeViewer;
+import org.eclipse.jface.wizard.WizardDialog;
+import org.eclipse.jface.wizard.WizardPage;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.ModifyEvent;
+import org.eclipse.swt.events.ModifyListener;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.layout.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.Group;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.ui.PlatformUI;
+
+import com.motorolamobility.studio.android.db.core.DbCoreActivator;
+import com.motorolamobility.studio.android.db.core.i18n.DbCoreNLS;
+import com.motorolamobility.studio.android.db.core.model.TableModel;
+import com.motorolamobility.studio.android.db.core.ui.wizards.CreateTableWizard;
+import com.motorolamobility.studio.android.db.core.ui.wizards.TableLabelProvider;
+
+public class CreateDatabaseWizardPage extends WizardPage
+{
+
+ private Text databaseName;
+
+ private Button addButton = null;
+
+ private Button editButton = null;
+
+ private Button removeButton = null;
+
+ private boolean isPageComplete = false;
+
+ // private final String DATABASE_CONTEXT_HELP_ID = DbPlugin.PLUGIN_ID + ".create_database_wizard";
+
+ /**
+ * This page's tree viewer
+ */
+ private TreeViewer viewer;
+
+ /**
+ * Tree viewer input
+ */
+ private final TreeNode[] treeNodeArray = new TreeNode[0];
+
+ private final List<String> alreadyAvailableDbs;
+
+ private final List<TableModel> tables = new ArrayList<TableModel>();
+
+ private final String DATABASE_CONTEXT_HELP_ID = DbCoreActivator.PLUGIN_ID
+ + ".create_database_wizard"; //$NON-NLS-1$
+
+ /**
+ * @param alreadyAvailableDbs
+ * @param pageName
+ */
+ protected CreateDatabaseWizardPage(final List<String> alreadyAvailableDbs)
+ {
+ super(DbCoreNLS.CreateDatabaseWizardPage_UI_PageTitle);
+ setTitle(DbCoreNLS.CreateDatabaseWizardPage_UI_CreateNewDatabase);
+ setMessage(DbCoreNLS.CreateDatabaseWizardPage_UI_CreateNewDBAddingItsFields);
+ this.alreadyAvailableDbs = alreadyAvailableDbs;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.dialogs.IDialogPage#createControl(org.eclipse.swt.widgets.Composite)
+ */
+ public void createControl(Composite parent)
+ {
+ Composite composite = new Composite(parent, SWT.FILL);
+ composite.setLayout(new GridLayout(2, false));
+
+ GridData layoutData = new GridData(SWT.FILL, SWT.NONE, true, false);
+
+ Composite nameComposite = new Composite(composite, SWT.FILL);
+ nameComposite.setLayout(new GridLayout(2, false));
+ nameComposite.setLayoutData(layoutData);
+
+ layoutData = new GridData(SWT.LEFT, SWT.NONE, false, false);
+
+ Label dbNameLabel = new Label(nameComposite, SWT.NONE);
+ dbNameLabel.setLayoutData(layoutData);
+ dbNameLabel.setText(DbCoreNLS.CreateDatabaseWizardPage_DB_Name_Label);
+
+ layoutData = new GridData(SWT.FILL, SWT.NONE, true, false);
+ databaseName = new Text(nameComposite, SWT.BORDER | SWT.SINGLE);
+ databaseName.setLayoutData(layoutData);
+ databaseName.addModifyListener(new ModifyListener()
+ {
+
+ public void modifyText(ModifyEvent e)
+ {
+ validatePage();
+ getContainer().updateButtons();
+
+ }
+ });
+
+ Composite emptyComposite = new Composite(composite, SWT.RIGHT);
+ emptyComposite.setLayout(new GridLayout(1, false));
+ emptyComposite.layout();
+
+ Group tableGroup = new Group(composite, SWT.FILL);
+
+ GridLayout gridLayout = new GridLayout(2, false);
+ GridData gridData = new GridData(SWT.FILL, SWT.FILL, true, true);
+
+ tableGroup.setLayout(gridLayout);
+ tableGroup.setLayoutData(gridData);
+
+ tableGroup.setText(DbCoreNLS.CreateDatabaseWizardPage_Table_Group);
+
+ viewer = new TreeViewer(tableGroup, SWT.BORDER | SWT.SINGLE | SWT.V_SCROLL);
+ viewer.getControl().setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+
+ // Set content and label provider
+ viewer.setLabelProvider(new TableLabelProvider());
+ viewer.setContentProvider(new TreeNodeContentProvider());
+
+ viewer.setInput(treeNodeArray);
+
+ layoutData = new GridData(SWT.FILL, SWT.FILL, true, true);
+
+ viewer.getTree().setLayoutData(layoutData);
+
+ viewer.addSelectionChangedListener(new TreeViewerListener());
+
+ Composite buttonBar = new Composite(tableGroup, SWT.NONE);
+ layoutData = new GridData(SWT.RIGHT, SWT.TOP, false, true);
+ buttonBar.setLayoutData(layoutData);
+
+ buttonBar.setLayout(new FillLayout(SWT.VERTICAL));
+ addButton = new Button(buttonBar, SWT.PUSH);
+ addButton.setText(DbCoreNLS.CreateDatabaseWizardPage_Add_Button);
+ addButton.addSelectionListener(new SelectionAdapter()
+ {
+
+ @Override
+ public void widgetSelected(SelectionEvent e)
+ {
+ boolean tableAdded = false;
+
+ // loop used to validate the new table name. If it already exists
+ // tell the user and open the table wizard again.
+ while (!tableAdded)
+ {
+ CreateTableWizard createTableWizard = new CreateTableWizard();
+ WizardDialog dialog = new WizardDialog(getShell(), createTableWizard);
+ dialog.open();
+ if (dialog.getReturnCode() == Dialog.OK)
+ {
+ TableModel newTable = createTableWizard.getTable();
+ if (newTable != null)
+ {
+ boolean tableNameAlreadyExists = false;
+ for (TableModel tableModel : tables)
+ {
+ if (tableModel.getName().equalsIgnoreCase(newTable.getName()))
+ {
+ tableNameAlreadyExists = true;
+ break;
+ }
+ }
+ if (!tableNameAlreadyExists)
+ {
+ tables.add(newTable);
+
+ ArrayList<TreeNode> treeNodeColletion = new ArrayList<TreeNode>();
+ treeNodeColletion.addAll(Arrays.asList((TreeNode[]) viewer
+ .getInput()));
+ TreeNode treeNode = new TreeNode(newTable);
+ treeNodeColletion.add(treeNode);
+ viewer.setInput(treeNodeColletion.toArray(new TreeNode[0]));
+ tableAdded = true;
+ }
+ else
+ {
+ MessageDialog
+ .openError(
+ getShell(),
+ DbCoreNLS.CreateDatabaseWizardPage_Table_Already_Exists_Title,
+ NLS.bind(
+ DbCoreNLS.CreateDatabaseWizardPage_Table_Already_Exists_Msg,
+ newTable.getName()));
+ }
+ }
+ }
+ else
+ {
+ break;
+ }
+ }
+ }
+
+ });
+
+ editButton = new Button(buttonBar, SWT.PUSH);
+ editButton.setText(DbCoreNLS.CreateDatabaseWizardPage_Edit_Button);
+ editButton.setEnabled(false);
+ editButton.addSelectionListener(new SelectionAdapter()
+ {
+
+ @Override
+ public void widgetSelected(SelectionEvent e)
+ {
+ TreeNode selectedNode = null;
+
+ if (viewer.getSelection() instanceof ITreeSelection)
+ {
+ ITreeSelection treeSelection = (ITreeSelection) viewer.getSelection();
+ selectedNode = (TreeNode) treeSelection.getFirstElement();
+ TableModel table = (TableModel) selectedNode.getValue();
+
+ CreateTableWizard createTableWizard = new CreateTableWizard();
+ createTableWizard.init(table);
+ WizardDialog dialog = new WizardDialog(getShell(), createTableWizard);
+ dialog.open();
+ TableModel newTable = createTableWizard.getTable();
+ if (newTable != null)
+ {
+ tables.add(newTable);
+ }
+ viewer.refresh();
+ }
+ }
+ });
+
+ removeButton = new Button(buttonBar, SWT.PUSH);
+ removeButton.setText(DbCoreNLS.CreateDatabaseWizardPage_Remove_Button);
+ removeButton.setEnabled(false);
+ removeButton.addSelectionListener(new SelectionAdapter()
+ {
+
+ @Override
+ public void widgetSelected(SelectionEvent e)
+ {
+ ArrayList<TreeNode> treeNodeColletion = new ArrayList<TreeNode>();
+ treeNodeColletion.addAll(Arrays.asList((TreeNode[]) viewer.getInput()));
+
+ TreeNode selectedNode = null;
+
+ if (viewer.getSelection() instanceof ITreeSelection)
+ {
+ ITreeSelection treeSelection = (ITreeSelection) viewer.getSelection();
+ selectedNode = (TreeNode) treeSelection.getFirstElement();
+
+ treeNodeColletion.remove(selectedNode);
+ viewer.setInput(treeNodeColletion.toArray(new TreeNode[0]));
+ }
+ }
+ });
+
+ composite.pack();
+ composite.layout();
+ setPageComplete(false);
+ setErrorMessage(null);
+
+ setControl(composite);
+
+ PlatformUI.getWorkbench().getHelpSystem().setHelp(parent, DATABASE_CONTEXT_HELP_ID);
+ PlatformUI.getWorkbench().getHelpSystem().setHelp(composite, DATABASE_CONTEXT_HELP_ID);
+ }
+
+ /**
+ * Validates the database name.
+ */
+ private void validatePage()
+ {
+ DatabaseCreationFieldValidator validator =
+ new DatabaseCreationFieldValidator(alreadyAvailableDbs);
+ String errorMessage = validator.isValid(getDatabaseName());
+ if (errorMessage != null)
+ {
+ setErrorMessage(errorMessage);
+ isPageComplete = false;
+ }
+ else
+ {
+ setErrorMessage(null);
+ isPageComplete = true;
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.wizard.WizardPage#isPageComplete()
+ */
+ @Override
+ public boolean isPageComplete()
+ {
+ return isPageComplete;
+ }
+
+ /**
+ * Return the database name that is in databaseName field.
+ * @return
+ */
+ public String getDatabaseName()
+ {
+ return databaseName.getText().trim();
+ }
+
+ /**
+ * Return tables that are in the tree viewer.
+ *
+ * @return
+ */
+ public List<TableModel> getTables()
+ {
+
+ List<TreeNode> treeNodeColletion = new ArrayList<TreeNode>();
+ treeNodeColletion.addAll(Arrays.asList((TreeNode[]) viewer.getInput()));
+
+ List<TableModel> tableCollection = new ArrayList<TableModel>();
+ for (TreeNode node : treeNodeColletion)
+ {
+ tableCollection.add((TableModel) node.getValue());
+ }
+ return tableCollection;
+ }
+
+ /**
+ * Selection listener for the tree viewer
+ */
+ class TreeViewerListener implements ISelectionChangedListener
+ {
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.viewers.ISelectionChangedListener#selectionChanged(org.eclipse.jface.viewers.SelectionChangedEvent)
+ */
+ public void selectionChanged(SelectionChangedEvent event)
+ {
+ if (event.getSelection() instanceof ITreeSelection)
+ {
+ ITreeSelection treeSelection = (ITreeSelection) event.getSelection();
+
+ if (!treeSelection.isEmpty())
+ {
+ editButton.setEnabled(true);
+ removeButton.setEnabled(true);
+ }
+ else
+ {
+ editButton.setEnabled(false);
+ removeButton.setEnabled(false);
+ }
+ }
+
+ }
+
+ }
+}
diff --git a/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/ui/wizards/createdb/DatabaseCreationFieldValidator.java b/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/ui/wizards/createdb/DatabaseCreationFieldValidator.java
new file mode 100644
index 0000000..6a34aed
--- /dev/null
+++ b/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/ui/wizards/createdb/DatabaseCreationFieldValidator.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.studio.android.db.core.ui.wizards.createdb;
+
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.eclipse.jface.dialogs.IInputValidator;
+import org.eclipse.osgi.util.NLS;
+
+import com.motorolamobility.studio.android.db.core.i18n.DbCoreNLS;
+
+/**
+ * This class represents a database name validator. The name must not
+ * not already exists in the Database View tree and must follow a certain pattern.
+ */
+public class DatabaseCreationFieldValidator implements IInputValidator
+{
+
+ /**
+ * The regular expression pattern used to validate the database name provided by the user
+ */
+ private static final String VALIDATOR_PATTERN = "[a-zA-Z0-9 ._-]+(.db)?"; //$NON-NLS-1$
+
+ /**
+ * The extension of the database files at the device
+ */
+ private static final String DB_EXTENSION = ".db"; //$NON-NLS-1$
+
+ /**
+ * Expression pattern used to validate the database name provided by the user
+ */
+ private final Pattern pattern = Pattern.compile(VALIDATOR_PATTERN);
+
+ private final List<String> alreadyAvailableDbs;
+
+ public DatabaseCreationFieldValidator(final List<String> alreadyAvailableDbs)
+ {
+ this.alreadyAvailableDbs = alreadyAvailableDbs;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.jface.dialogs.IInputValidator#isValid(java.lang.String)
+ */
+ public String isValid(String newText)
+ {
+ String msg = null;
+ Matcher patternMatcher = pattern.matcher(newText);
+ if (!patternMatcher.matches())
+ {
+ msg = DbCoreNLS.DatabaseCreationFieldValidator_ValidChars;
+ }
+ else
+ // check whether the database already exists
+ {
+ String dbFileName = newText + DB_EXTENSION;
+ if (alreadyAvailableDbs.contains(dbFileName))
+ {
+ msg =
+ NLS.bind(DbCoreNLS.DatabaseCreationFieldValidator_DB_Already_Exists_Msg,
+ dbFileName);
+ }
+ }
+
+ return msg;
+ }
+}
diff --git a/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/workspace/WorkspaceRootNode.java b/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/workspace/WorkspaceRootNode.java
new file mode 100644
index 0000000..c55d68d
--- /dev/null
+++ b/src/plugins/db.core/src/com/motorolamobility/studio/android/db/core/workspace/WorkspaceRootNode.java
@@ -0,0 +1,206 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.studio.android.db.core.workspace;
+
+import java.util.Collection;
+
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.IResourceChangeEvent;
+import org.eclipse.core.resources.IResourceChangeListener;
+import org.eclipse.core.resources.IResourceDelta;
+import org.eclipse.core.resources.IResourceDeltaVisitor;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.jface.resource.ImageDescriptor;
+
+import com.motorola.studio.android.common.IAndroidConstants;
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorolamobility.studio.android.db.core.project.ProjectNode;
+import com.motorolamobility.studio.android.db.core.ui.AbstractTreeNode;
+import com.motorolamobility.studio.android.db.core.ui.IRootNode;
+import com.motorolamobility.studio.android.db.core.ui.ITreeNode;
+
+/**
+ * Implements the Workspace Root Node of the database tree view.
+ * It listens for changes in workspace such as project deletion/open/close that reflect in database model.
+ */
+public class WorkspaceRootNode extends AbstractTreeNode implements IRootNode,
+ IResourceChangeListener
+{
+ class ResourceDeltaVisior implements IResourceDeltaVisitor
+ {
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.core.resources.IResourceDeltaVisitor#visit(org.eclipse.core.resources.IResourceDelta)
+ */
+ public boolean visit(IResourceDelta delta)
+ {
+ IResource res = delta.getResource();
+ if (res instanceof IProject)
+ {
+ IProject project = (IProject) res;
+ switch (delta.getKind())
+ {
+ case IResourceDelta.ADDED:
+ StudioLogger.info("Project added: " + res.getFullPath()); //$NON-NLS-1$
+ addProject(project);
+ break;
+ case IResourceDelta.CHANGED:
+ int flags = delta.getFlags();
+ if ((flags & IResourceDelta.OPEN) != 0)
+ {
+ StudioLogger.info("Project opened: " + res.getFullPath()); //$NON-NLS-1$
+ addProject(project);
+ }
+ break;
+ }
+ }
+
+ return true; // visit the children
+ }
+ }
+
+ public WorkspaceRootNode()
+ {
+ //listener for project changes
+ ResourcesPlugin.getWorkspace().addResourceChangeListener(
+ this,
+ IResourceChangeEvent.PRE_CLOSE | IResourceChangeEvent.PRE_DELETE
+ | IResourceChangeEvent.POST_CHANGE);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorolamobility.studio.android.db.core.ui.AbstractTreeNode#refresh()
+ */
+ @Override
+ public void refresh()
+ {
+ clear();
+ loadContent();
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorolamobility.studio.android.db.core.ui.AbstractTreeNode#getIcon()
+ */
+ @Override
+ public ImageDescriptor getIcon()
+ {
+ return getSpecificIcon("org.eclipse.jdt.ui", //$NON-NLS-1$
+ "icons/full/elcl16/prj_mode.gif"); //$NON-NLS-1$
+ }
+
+ /**
+ * Loads the Android projects from the current workspace
+ * @return list of project nodes
+ */
+ public Collection<ITreeNode> loadContent()
+ {
+ for (IProject project : ResourcesPlugin.getWorkspace().getRoot().getProjects())
+ {
+ addProject(project);
+ }
+ return this.getChildren();
+ }
+
+ /**
+ * Adds a new project in the workspace and notifies the event
+ * @param project
+ * @throws CoreException
+ */
+ public void addProject(IProject project)
+ {
+ ProjectNode projectNode = null;
+ try
+ {
+ if ((project != null) && (project.isOpen())
+ && (project.getNature(IAndroidConstants.ANDROID_NATURE) != null))
+ {
+ projectNode = new ProjectNode(project, this);
+ putChild(projectNode);
+ }
+ }
+ catch (CoreException e)
+ {
+ StudioLogger.error(WorkspaceRootNode.class,
+ "Unable to retrieve nature from project:" + project.getName(), e); //$NON-NLS-1$
+ }
+ }
+
+ /**
+ * Removes the project from workspace and notifies the event
+ * @param project
+ */
+ public void removeProject(IProject project)
+ {
+ ITreeNode projectNode = getChildById(project.getName());
+ if (projectNode != null)
+ {
+ removeChild(projectNode);
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorolamobility.studio.android.db.core.ui.AbstractTreeNode#isLeaf()
+ */
+ @Override
+ public boolean isLeaf()
+ {
+ return false;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.core.resources.IResourceChangeListener#resourceChanged(org.eclipse.core.resources.IResourceChangeEvent)
+ */
+ public void resourceChanged(IResourceChangeEvent event)
+ {
+ IResource res = event.getResource();
+ IProject project = null;
+ switch (event.getType())
+ {
+ case IResourceChangeEvent.PRE_CLOSE:
+ case IResourceChangeEvent.PRE_DELETE:
+ //project being closed or deleted => remove project from model
+ StudioLogger.info("Project about to close/delete: " + res.getFullPath()); //$NON-NLS-1$
+ project = res.getProject();
+ removeProject(project);
+ break;
+ case IResourceChangeEvent.POST_CHANGE:
+ try
+ {
+ event.getDelta().accept(new ResourceDeltaVisior());
+ }
+ catch (CoreException e)
+ {
+ StudioLogger.error(WorkspaceRootNode.class,
+ "Error listening to changes in resources", e); //$NON-NLS-1$
+ }
+ break;
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorolamobility.studio.android.db.core.ui.AbstractTreeNode#clean()
+ */
+ @Override
+ public void cleanUp()
+ {
+ super.cleanUp();
+ ResourcesPlugin.getWorkspace().removeResourceChangeListener(this);
+ }
+}
diff --git a/src/plugins/db.devices/.classpath b/src/plugins/db.devices/.classpath
new file mode 100644
index 0000000..8a8f166
--- /dev/null
+++ b/src/plugins/db.devices/.classpath
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>
+ <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/src/plugins/db.devices/.project b/src/plugins/db.devices/.project
new file mode 100644
index 0000000..1796f16
--- /dev/null
+++ b/src/plugins/db.devices/.project
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>com.motorolamobility.studio.android.db.devices</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.ManifestBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.SchemaBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.pde.PluginNature</nature>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+</projectDescription>
diff --git a/src/plugins/db.devices/META-INF/MANIFEST.MF b/src/plugins/db.devices/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..fb5c30b
--- /dev/null
+++ b/src/plugins/db.devices/META-INF/MANIFEST.MF
@@ -0,0 +1,21 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: %pluginName
+Bundle-SymbolicName: com.motorolamobility.studio.android.db.devices;singleton:=true
+Bundle-Version: 5.0.0.qualifier
+Bundle-Activator: com.motorolamobility.studio.android.db.devices.DbDevicesPlugin
+Bundle-Vendor: %providerName
+Require-Bundle: org.eclipse.ui,
+ org.eclipse.core.runtime,
+ com.motorolamobility.studio.android.db.core,
+ com.motorola.studio.android,
+ org.eclipse.jdt.core,
+ org.eclipse.datatools.modelbase.sql,
+ com.motorola.studio.android.common,
+ org.eclipse.ui.console,
+ org.eclipse.datatools.sqltools.result,
+ org.eclipse.sequoyah.device.framework,
+ org.eclipse.core.resources
+Bundle-RequiredExecutionEnvironment: JavaSE-1.6
+Bundle-Localization: plugin
+Bundle-ActivationPolicy: lazy
diff --git a/src/plugins/db.devices/build.properties b/src/plugins/db.devices/build.properties
new file mode 100644
index 0000000..3380185
--- /dev/null
+++ b/src/plugins/db.devices/build.properties
@@ -0,0 +1,23 @@
+#
+# Copyright (C) 2012 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+source.. = src/
+output.. = bin/
+bin.includes = META-INF/,\
+ .,\
+ plugin.properties,\
+ plugin.xml,\
+ icons/
diff --git a/src/plugins/db.devices/icons/get_from_local_file.png b/src/plugins/db.devices/icons/get_from_local_file.png
new file mode 100644
index 0000000..89d6635
--- /dev/null
+++ b/src/plugins/db.devices/icons/get_from_local_file.png
Binary files differ
diff --git a/src/plugins/db.devices/icons/map.png b/src/plugins/db.devices/icons/map.png
new file mode 100644
index 0000000..da735e0
--- /dev/null
+++ b/src/plugins/db.devices/icons/map.png
Binary files differ
diff --git a/src/plugins/db.devices/icons/obj16/card.png b/src/plugins/db.devices/icons/obj16/card.png
new file mode 100644
index 0000000..7236b55
--- /dev/null
+++ b/src/plugins/db.devices/icons/obj16/card.png
Binary files differ
diff --git a/src/plugins/db.devices/icons/obj16/device.png b/src/plugins/db.devices/icons/obj16/device.png
new file mode 100644
index 0000000..3303876
--- /dev/null
+++ b/src/plugins/db.devices/icons/obj16/device.png
Binary files differ
diff --git a/src/plugins/db.devices/icons/obj16/device_root.gif b/src/plugins/db.devices/icons/obj16/device_root.gif
new file mode 100644
index 0000000..2b83024
--- /dev/null
+++ b/src/plugins/db.devices/icons/obj16/device_root.gif
Binary files differ
diff --git a/src/plugins/db.devices/icons/obj16/devices.png b/src/plugins/db.devices/icons/obj16/devices.png
new file mode 100644
index 0000000..7875ec5
--- /dev/null
+++ b/src/plugins/db.devices/icons/obj16/devices.png
Binary files differ
diff --git a/src/plugins/db.devices/icons/save_to_local_file.png b/src/plugins/db.devices/icons/save_to_local_file.png
new file mode 100644
index 0000000..b405bfe
--- /dev/null
+++ b/src/plugins/db.devices/icons/save_to_local_file.png
Binary files differ
diff --git a/src/plugins/db.devices/plugin.properties b/src/plugins/db.devices/plugin.properties
new file mode 100644
index 0000000..e4be346
--- /dev/null
+++ b/src/plugins/db.devices/plugin.properties
@@ -0,0 +1,38 @@
+#
+# Copyright (C) 2012 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+#################################################################################
+#
+# Database Devices Support properties
+#
+#################################################################################
+
+pluginName=MOTODEV Studio for Android Database Devices Support Plug-in
+providerName= Motorola Mobility, Inc.
+
+#Devices Node
+devicesNodeName=Devices
+
+action_refresh_device_node=&Refresh
+action_filter_db_application_node=&Filter applications without DB
+action_map_database=&Map Database...
+saveToLocalFile=&Save to local file...
+
+action_unmap_database=&Unmap Database...
+
+command_filter_db_application_node=Filter applications without DB
+
+preferencePageName=MOTODEV Database for Devices
diff --git a/src/plugins/db.devices/plugin.xml b/src/plugins/db.devices/plugin.xml
new file mode 100644
index 0000000..0138431
--- /dev/null
+++ b/src/plugins/db.devices/plugin.xml
@@ -0,0 +1,136 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?eclipse version="3.4"?>
+
+<!--
+ Copyright (C) 2012 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<plugin>
+ <extension
+ point="com.motorolamobility.studio.android.db.core.dbRootNode">
+ <rootNode
+ class="com.motorolamobility.studio.android.db.devices.model.DevicesRootNode"
+ id="com.motorolamobility.studio.android.db.devices.devicesRootNode"
+ name="%devicesNodeName">
+ </rootNode>
+ </extension>
+ <extension
+ point="org.eclipse.ui.popupMenus">
+ <objectContribution
+ adaptable="false"
+ id="com.motorolamobility.studio.android.db.devices.ui.action.IDeviceNode"
+ objectClass="com.motorolamobility.studio.android.db.devices.model.IDeviceNode">
+ <action
+ class="com.motorolamobility.studio.android.db.devices.ui.action.PopupMenuActionDelegate"
+ definitionId="com.motorolamobility.studio.android.db.core.refreshNode"
+ enablesFor="1"
+ icon="platform:/plugin/org.eclipse.datatools.sqltools.schemaobjecteditor.ui/icons/refresh_from_server.gif"
+ id="com.motorolamobility.studio.android.db.devices.ui.action.refreshDeviceNode"
+ label="%action_refresh_device_node"
+ menubarPath="com.motorolamobility.studio.android.db.devices.ui.action.IDeviceNodeGroup2">
+ </action>
+
+ </objectContribution>
+ <objectContribution
+ adaptable="false"
+ id="com.motorolamobility.studio.android.db.devices.ui.action.IDbMapperNode"
+ objectClass="com.motorolamobility.studio.android.db.devices.model.IDbDeviceMapperNode">
+ <action
+ class="com.motorolamobility.studio.android.db.devices.ui.action.PopupMenuActionDelegate"
+ enablesFor="1"
+ icon="icons/map.png"
+ id="com.motorolamobility.studio.android.db.devices.ui.action.mapDeviceDbNode"
+ label="%action_map_database"
+ overrideActionId="com.motorolamobility.studio.android.db.core.ui.action.mapDbNode">
+ </action>
+ </objectContribution>
+ <objectContribution
+ adaptable="false"
+ id="com.motorolamobility.studio.android.db.devices.ui.action.DeviceDbNode"
+ objectClass="com.motorolamobility.studio.android.db.devices.model.DeviceDbNode">
+ <action
+ class="com.motorolamobility.studio.android.db.devices.ui.action.PopupMenuActionDelegate"
+ enablesFor="1"
+ icon="icons/save_to_local_file.png"
+ id="com.motorolamobility.studio.android.db.devices.ui.action.saveToLocalFile"
+ label="%saveToLocalFile">
+ <enablement>
+ <objectState
+ name="com.motorolamobility.studio.android.db.core.databaseConnection"
+ value="com.motorolamobility.studio.android.db.core.databaseConnected">
+ </objectState>
+ </enablement>
+ </action>
+ </objectContribution>
+ </extension>
+ <extension
+ point="org.eclipse.ui.menus">
+ <menuContribution
+ allPopups="false"
+ locationURI="popup:com.motorolamobility.studio.android.db.devices.ui.action.IDeviceNode?after=com.motorolamobility.studio.android.db.devices.ui.action.filterDbApplicationNode">
+ <separator
+ name="com.motorolamobility.studio.android.db.devices.ui.action.IDeviceNodeGroup2">
+ </separator>
+ </menuContribution>
+ <menuContribution
+ allPopups="false"
+ locationURI="popup:com.motorola.studio.android.db.databaseView">
+ <command
+ commandId="com.motorolamobility.studio.android.db.devices.command.filterDbApplicationNode"
+ label="%action_filter_db_application_node"
+ style="toggle">
+ <visibleWhen
+ checkEnabled="false">
+ <with
+ variable="selection">
+ <count
+ value="1">
+ </count>
+ <iterate
+ ifEmpty="false"
+ operator="and">
+ <adapt
+ type="com.motorolamobility.studio.android.db.devices.model.IDeviceNode">
+ </adapt>
+ </iterate>
+ </with>
+ </visibleWhen>
+ </command>
+ </menuContribution>
+ </extension>
+ <extension
+ point="org.eclipse.ui.commands">
+ <command
+ defaultHandler="com.motorolamobility.studio.android.db.devices.ui.action.FilterDbApplicationHandler"
+ id="com.motorolamobility.studio.android.db.devices.command.filterDbApplicationNode"
+ name="%command_filter_db_application_node">
+ <state
+ class="com.motorolamobility.studio.android.db.devices.ui.action.PersistentToggleState"
+ id="org.eclipse.ui.commands.toggleState">
+ </state>
+ </command>
+ </extension>
+
+ <extension
+ point="org.eclipse.ui.preferencePages">
+ <page
+ category="com.motorola.studio.platform.ui.preference"
+ class="com.motorolamobility.studio.android.db.devices.ui.preferences.DbPreferencePage"
+ id="com.motorola.studio.android.db.preferencepage"
+ name="%preferencePageName">
+ </page>
+ </extension>
+
+</plugin>
diff --git a/src/plugins/db.devices/src/com/motorolamobility/studio/android/db/devices/DbDevicesPlugin.java b/src/plugins/db.devices/src/com/motorolamobility/studio/android/db/devices/DbDevicesPlugin.java
new file mode 100644
index 0000000..6d06f52
--- /dev/null
+++ b/src/plugins/db.devices/src/com/motorolamobility/studio/android/db/devices/DbDevicesPlugin.java
@@ -0,0 +1,78 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorolamobility.studio.android.db.devices;
+
+import org.eclipse.ui.plugin.AbstractUIPlugin;
+import org.osgi.framework.BundleContext;
+
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorolamobility.studio.android.db.core.DbCoreActivator;
+import com.motorolamobility.studio.android.db.devices.ui.preferences.DbPreferencePage;
+
+/**
+ * The activator class controls the plug-in life cycle
+ */
+public class DbDevicesPlugin extends AbstractUIPlugin
+{
+
+ // The plug-in ID
+ public static final String PLUGIN_ID = "com.motorolamobility.studio.android.db.devices"; //$NON-NLS-1$
+
+ public static final String DB_TEMP_PATH_PREFERENCE = PLUGIN_ID + ".dbstudiotemppath"; //$NON-NLS-1$
+
+ // The shared instance
+ private static DbDevicesPlugin plugin;
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.ui.plugin.AbstractUIPlugin#start(org.osgi.framework.BundleContext)
+ */
+ @Override
+ public void start(BundleContext context) throws Exception
+ {
+ StudioLogger.debug(DbCoreActivator.class,
+ "Starting MOTODEV Studio for Android Database Devices Support Plugin...");
+
+ super.start(context);
+ DbPreferencePage.restoreBackWardPref(this.getPreferenceStore());
+ plugin = this;
+
+ StudioLogger.debug(DbCoreActivator.class,
+ "MOTODEV Studio for Android Database Devices Support Plugin started...");
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.ui.plugin.AbstractUIPlugin#stop(org.osgi.framework.BundleContext)
+ */
+ @Override
+ public void stop(BundleContext context) throws Exception
+ {
+ plugin = null;
+ super.stop(context);
+ }
+
+ /**
+ * Returns the shared instance
+ *
+ * @return the shared instance
+ */
+ public static DbDevicesPlugin getDefault()
+ {
+ return plugin;
+ }
+
+}
diff --git a/src/plugins/db.devices/src/com/motorolamobility/studio/android/db/devices/i18n/DbDevicesNLS.java b/src/plugins/db.devices/src/com/motorolamobility/studio/android/db/devices/i18n/DbDevicesNLS.java
new file mode 100644
index 0000000..bee8f5e
--- /dev/null
+++ b/src/plugins/db.devices/src/com/motorolamobility/studio/android/db/devices/i18n/DbDevicesNLS.java
@@ -0,0 +1,109 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorolamobility.studio.android.db.devices.i18n;
+
+import org.eclipse.osgi.util.NLS;
+
+/**
+ *
+ */
+public class DbDevicesNLS extends NLS
+{
+ private static final String BUNDLE_NAME =
+ "com.motorolamobility.studio.android.db.devices.i18n.messages"; //$NON-NLS-1$
+
+ public static String ApplicationNode_ConnectedDbs_Refresh_Message;
+
+ public static String ApplicationNode_Could_Not_Create_Database;
+
+ public static String ExternalStorageNode_ConnectedDbs_Refresh_Message;
+
+ public static String DeviceDbNode_Application_Running_Msg_Text;
+
+ public static String DeviceDbNode_Application_Running_Msg_Title;
+
+ public static String DeviceDbNode_Calculate_Local_Md5_Failed;
+
+ public static String DeviceDbNode_Could_Not_Create_DeviceDbNode;
+
+ public static String DeviceDbNode_Create_Device_Db_Failed;
+
+ public static String DeviceDbNode_Create_Temp_Local_Db_Failed;
+
+ public static String DeviceDbNode_DBOutOfSync_Refresh_Message;
+
+ public static String DeviceDbNode_Delete_Remote_File_Failed;
+
+ public static String DeviceDbNode_Md5Sum_Differs;
+
+ public static String DeviceDbNode_Push_Local_File_To_Device_Failed;
+
+ public static String DeviceDbNode_RefreshQuestion;
+
+ public static String DeviceDbNode_Remote_File_Modified_Msg;
+
+ public static String DeviceDbNode_Remote_File_Modified_Title;
+
+ public static String DeviceDbNode_Tootip_Prefix;
+
+ public static String DeviceDbNode_User_Canceled_Overwrite;
+
+ public static String DeviceNode_Cant_Refresh_Node;
+
+ public static String DeviceNode_CouldNotLoadInstalledApps;
+
+ public static String DeviceNode_CouldNotVerifySdCard;
+
+ public static String DeviceNode_X_Apps_Filtered;
+
+ public static String DevicesRootNode_Cant_Refresh_Node;
+
+ public static String UI_MapDatabaseAction_QueryDbPath_DialogTitle;
+
+ public static String UI_MapDatabaseAction_QueryDbPath_DialogMessage;
+
+ public static String MapDatabaseAction_Error_WrongDatabasePlace;
+
+ public static String UI_PreferencePage_PathLabel;
+
+ public static String ERR_DbPrefPage_InvalidDir;
+
+ public static String ERR_DbUtils_Local_Db_Title;
+
+ public static String ERR_DbUtils_Local_Db_Msg;
+
+ public static String ExtStorageNode_Disconnect_Failed;
+
+ public static String ExtStorageNode_Node_Name;
+
+ public static String ExtStorageNode_RemoteFile_Not_Exist;
+
+ public static String SaveDatabaseToFile_AllFiles;
+
+ public static String SaveDatabaseToFile_CopyDatabase_Error;
+
+ public static String SaveDatabaseToFile_DbFiles;
+
+ static
+ {
+ // initialize resource bundle
+ NLS.initializeMessages(BUNDLE_NAME, DbDevicesNLS.class);
+ }
+
+ private DbDevicesNLS()
+ {
+ }
+}
diff --git a/src/plugins/db.devices/src/com/motorolamobility/studio/android/db/devices/i18n/messages.properties b/src/plugins/db.devices/src/com/motorolamobility/studio/android/db/devices/i18n/messages.properties
new file mode 100644
index 0000000..02af5f6
--- /dev/null
+++ b/src/plugins/db.devices/src/com/motorolamobility/studio/android/db/devices/i18n/messages.properties
@@ -0,0 +1,54 @@
+#
+# Copyright (C) 2012 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+ApplicationNode_ConnectedDbs_Refresh_Message=There are connected databases within application {0}. During refresh all databases will be disconnected and opened editors closed.\nDo you want to continue?
+ApplicationNode_Could_Not_Create_Database=Could not create database {0} at device with serial number {1}
+ExternalStorageNode_ConnectedDbs_Refresh_Message=There are connected databases mapped to external storage. During refresh all databases will be disconnected and opened editors closed.\nDo you want to continue?
+DeviceDbNode_Application_Running_Msg_Text=The application {0} is currently running.\nChanges to this database will not take effect until you stop the application and restart it.
+DeviceDbNode_Application_Running_Msg_Title=Application Running
+DeviceDbNode_Calculate_Local_Md5_Failed=Could not calculate local DB file md5sum
+DeviceDbNode_Could_Not_Create_DeviceDbNode=Could not create DeviceDbNode
+
+DeviceDbNode_Create_Device_Db_Failed=Failed to create database file on device
+DeviceDbNode_Create_Temp_Local_Db_Failed=Could not create local database temp file.
+DeviceDbNode_DBOutOfSync_Refresh_Message=The database {0} is currently out of sync with the device.\n\nDo you want to discard the local settings and download the latest version from the device?\nNote: Selecting 'No' will discard the device changes and upload the local version to the device.
+DeviceDbNode_Delete_Remote_File_Failed=Could not delete file {0} from device {1}
+DeviceDbNode_Md5Sum_Differs=Local and remote database md5 sums differ
+DeviceDbNode_Push_Local_File_To_Device_Failed=Could not push local database file to device {0}
+DeviceDbNode_RefreshQuestion=There are open table editors for database "{0}" that will be closed during refresh. \nDo you want to continue?
+DeviceDbNode_Remote_File_Modified_Msg=The {0} file has been modified on the device since the last time it was updated by MOTODEV Studio for Android.\n\nDo you want to overwrite the device's file?
+DeviceDbNode_Remote_File_Modified_Title=The file has been modified
+DeviceDbNode_Tootip_Prefix=Path on device: {0}
+DeviceDbNode_User_Canceled_Overwrite=User doesn't want to overwrite the device database file.
+DeviceNode_Cant_Refresh_Node=Cannot refresh device node {0}
+DeviceNode_CouldNotLoadInstalledApps=Could not load installed apps from device {0}
+DeviceNode_CouldNotVerifySdCard=Could not verify if device {0} contains a sd card
+DeviceNode_X_Apps_Filtered=\ ({0} applications filtered)
+DevicesRootNode_Cant_Refresh_Node=Cannot refresh devices node
+UI_MapDatabaseAction_QueryDbPath_DialogTitle = Map database...
+UI_MapDatabaseAction_QueryDbPath_DialogMessage = Supply the path to the database file
+MapDatabaseAction_Error_WrongDatabasePlace=The database must be on /sdcard or /mnt/sdcard
+ERR_DbPrefPage_InvalidDir=Invalid directory location.
+UI_PreferencePage_PathLabel=Database Temporary Location:
+ERR_DbUtils_Local_Db_Title=Error Creating Local Database
+ERR_DbUtils_Local_Db_Msg=It was not possible to copy the remote database to: {0}. The MOTODEV Database Temporary Location preference has been reset to its default value.
+
+ExtStorageNode_Disconnect_Failed=Failed to disconnect from DB {0}. Details:{1}
+ExtStorageNode_Node_Name=External Storage
+ExtStorageNode_RemoteFile_Not_Exist=Remote file does not exist.
+SaveDatabaseToFile_AllFiles=All Files (*.*)
+SaveDatabaseToFile_CopyDatabase_Error=Database {0} could not be saved to specified path: {1}
+SaveDatabaseToFile_DbFiles=*.db files
diff --git a/src/plugins/db.devices/src/com/motorolamobility/studio/android/db/devices/model/ApplicationNode.java b/src/plugins/db.devices/src/com/motorolamobility/studio/android/db/devices/model/ApplicationNode.java
new file mode 100644
index 0000000..6fe0c4d
--- /dev/null
+++ b/src/plugins/db.devices/src/com/motorolamobility/studio/android/db/devices/model/ApplicationNode.java
@@ -0,0 +1,196 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorolamobility.studio.android.db.devices.model;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.ui.plugin.AbstractUIPlugin;
+
+import com.motorola.studio.android.adt.DDMSUtils;
+import com.motorolamobility.studio.android.db.core.CanRefreshStatus;
+import com.motorolamobility.studio.android.db.core.exception.MotodevDbException;
+import com.motorolamobility.studio.android.db.core.model.TableModel;
+import com.motorolamobility.studio.android.db.core.ui.AbstractTreeNode;
+import com.motorolamobility.studio.android.db.core.ui.IDbNode;
+import com.motorolamobility.studio.android.db.core.ui.ITreeNode;
+import com.motorolamobility.studio.android.db.core.ui.action.IDbCreatorNode;
+import com.motorolamobility.studio.android.db.devices.DbDevicesPlugin;
+import com.motorolamobility.studio.android.db.devices.i18n.DbDevicesNLS;
+import com.motorolamobility.studio.android.db.devices.utils.DeviceDbUtils;
+
+/**
+ * This class represents a tree node for an application installed on a device.
+ */
+public class ApplicationNode extends AbstractTreeNode implements IDbCreatorNode
+{
+
+ private String appName;
+
+ private String serialNumber;
+
+ @SuppressWarnings("unused")
+ private ApplicationNode()
+ {
+ //Forcing user to use a proper constructor
+ }
+
+ /**
+ * Creates a new Application node for the app
+ * @param appName the application name
+ * @param parent this node parent
+ */
+ public ApplicationNode(String appName, DeviceNode parent)
+ {
+ super(parent);
+ serialNumber = parent.getSerialNumber();
+ this.appName = appName;
+ setId(appName);
+ setName(appName);
+ ImageDescriptor icon =
+ AbstractUIPlugin.imageDescriptorFromPlugin("com.android.ide.eclipse.adt", //$NON-NLS-1$
+ "icons/android.png"); //$NON-NLS-1$
+ setIcon(icon);
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorolamobility.studio.android.db.core.ui.AbstractTreeNode#canRefresh()
+ */
+ @Override
+ public IStatus canRefresh()
+ {
+ IStatus status = null;
+ boolean hasConnectedDb = false;
+
+ //Search for connected dbs
+ for (ITreeNode treeNode : getChildren())
+ {
+ if (treeNode instanceof DeviceDbNode)
+ {
+ DeviceDbNode dbNode = (DeviceDbNode) treeNode;
+ hasConnectedDb = dbNode.isConnected();
+ break;
+ }
+ }
+
+ if (hasConnectedDb)
+ {
+ status =
+ new CanRefreshStatus(CanRefreshStatus.ASK_USER, DbDevicesPlugin.PLUGIN_ID,
+ NLS.bind(
+ DbDevicesNLS.ApplicationNode_ConnectedDbs_Refresh_Message, getName()));
+ }
+
+ return status != null ? status : Status.OK_STATUS;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorolamobility.studio.andkroid.db.core.ui.AbstractTreeNode#refresh()
+ */
+ @Override
+ public void refresh()
+ {
+ clear();
+ try
+ {
+ List<String> applicationDatabases =
+ DDMSUtils.getApplicationDatabases(serialNumber, appName);
+
+ List<ITreeNode> dbNodes = new ArrayList<ITreeNode>(applicationDatabases.size());
+
+ for (String dbLocation : applicationDatabases)
+ {
+ IPath dbPath = DeviceDbUtils.getRemoteDbPath(appName, dbLocation);
+ DeviceDbNode deviceDbNode = new DeviceDbNode(dbPath, serialNumber, this);
+ dbNodes.add(deviceDbNode);
+ }
+
+ putChildren(dbNodes);
+ setNodeStatus(Status.OK_STATUS);
+ }
+ catch (IOException e)
+ {
+ setNodeStatus(new Status(IStatus.ERROR, DbDevicesPlugin.PLUGIN_ID, e.getMessage()));
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorolamobility.studio.android.db.core.ui.action.IDbCreatorNode#createDb(java.lang.String)
+ */
+ public IStatus createDb(String dbName)
+ {
+ return createDb(dbName, null);
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorolamobility.studio.android.db.core.ui.action.IDbCreatorNode#createDb(java.lang.String, java.util.List)
+ */
+ public IStatus createDb(String dbName, List<TableModel> tables)
+ {
+ IStatus status = null;
+
+ IPath remoteDbPath = DeviceDbUtils.getRemoteDbPath(appName, dbName);
+ try
+ {
+ DeviceDbNode dbNode = new DeviceDbNode(remoteDbPath, serialNumber, this, true);
+ if (tables != null)
+ {
+ dbNode.createTables(tables);
+ }
+ putChild(dbNode);
+ }
+ catch (MotodevDbException e)
+ {
+ status =
+ new Status(IStatus.ERROR, DbDevicesPlugin.PLUGIN_ID, NLS.bind(
+ DbDevicesNLS.ApplicationNode_Could_Not_Create_Database,
+ remoteDbPath.toString(), serialNumber));
+ }
+
+ return status != null ? status : Status.OK_STATUS;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorolamobility.studio.android.db.core.ui.AbstractTreeNode#isLeaf()
+ */
+ @Override
+ public boolean isLeaf()
+ {
+ return false;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorolamobility.studio.android.db.core.ui.action.IDbCreatorNode#deleteDb(com.motorolamobility.studio.android.db.core.ui.IDbNode)
+ */
+ public IStatus deleteDb(IDbNode dbNode)
+ {
+ IStatus status = dbNode.deleteDb();
+
+ if (status.isOK())
+ {
+ removeChild(dbNode);
+ }
+
+ return status;
+ }
+
+}
diff --git a/src/plugins/db.devices/src/com/motorolamobility/studio/android/db/devices/model/DeviceDbNode.java b/src/plugins/db.devices/src/com/motorolamobility/studio/android/db/devices/model/DeviceDbNode.java
new file mode 100644
index 0000000..d498d66
--- /dev/null
+++ b/src/plugins/db.devices/src/com/motorolamobility/studio/android/db/devices/model/DeviceDbNode.java
@@ -0,0 +1,720 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorolamobility.studio.android.db.devices.model;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Set;
+
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.datatools.sqltools.result.ResultsViewAPI;
+import org.eclipse.datatools.sqltools.result.core.IResultManagerListener;
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.console.IOConsoleOutputStream;
+
+import com.motorola.studio.android.adt.DDMSFacade;
+import com.motorola.studio.android.adt.DDMSUtils;
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.common.utilities.EclipseUtils;
+import com.motorola.studio.android.common.utilities.FileUtil;
+import com.motorolamobility.studio.android.db.core.CanRefreshStatus;
+import com.motorolamobility.studio.android.db.core.DbCoreActivator;
+import com.motorolamobility.studio.android.db.core.exception.MotodevDbException;
+import com.motorolamobility.studio.android.db.core.model.DbModel;
+import com.motorolamobility.studio.android.db.core.model.TableModel;
+import com.motorolamobility.studio.android.db.core.ui.AbstractDbResultManagerAdapter;
+import com.motorolamobility.studio.android.db.core.ui.DbNode;
+import com.motorolamobility.studio.android.db.core.ui.IDbNode;
+import com.motorolamobility.studio.android.db.core.ui.ITableNode;
+import com.motorolamobility.studio.android.db.core.ui.ITreeNode;
+import com.motorolamobility.studio.android.db.devices.DbDevicesPlugin;
+import com.motorolamobility.studio.android.db.devices.i18n.DbDevicesNLS;
+
+/**
+ * This class represents a tree node for a given SQLite3 database file located on a Android device.
+ */
+public class DeviceDbNode extends DbNode implements IDbNode
+{
+ /**
+ *
+ */
+ private static final int REMOTE_OPERATIONS_TIMEOUT = 2000;
+
+ private final IPath remoteDbPath;
+
+ private final String serialNumber;
+
+ private IResultManagerListener resultManagerListener;
+
+ private String localFileMd5;
+
+ public boolean isDirty;
+
+ private class ResultManagerListener extends AbstractDbResultManagerAdapter
+ {
+ /* (non-Javadoc)
+ * @see com.motorolamobility.studio.android.db.core.ui.AbstractDbResultManagerAdapter#statementExecuted(java.lang.String, java.lang.String)
+ */
+ @Override
+ public void statementExecuted(String profilename, String sqlStatement)
+ {
+ if ((model != null) && model.getProfileName().equals(profilename))
+ {
+ if ((!sqlStatement.equals("Group Execution")) //$NON-NLS-1$
+ && (sqlStatement.trim().toLowerCase().indexOf("select") != 0) //$NON-NLS-1$
+ && (!sqlStatement.trim().equals(""))) //$NON-NLS-1$
+ {
+ IStatus status = checkMd5Sum(true);
+
+ if (status.isOK())
+ {
+ status = pushLocalDbFile();
+ }
+ if (!status.isOK())
+ {
+ isDirty = true;
+ }
+
+ }
+ }
+ }
+
+ };
+
+ /**
+ * Creates a new DeviceDbNode for an already existent SQLite3 database
+ * @param remoteDbPath the SQLite3 database file location at the device
+ * @param parent this node parent
+ */
+ public DeviceDbNode(IPath remoteDbPath, String serialNumber, ITreeNode parent)
+ {
+ super(parent);
+ setId(serialNumber + "." + remoteDbPath.toString()); //$NON-NLS-1$
+ this.remoteDbPath = remoteDbPath;
+ this.serialNumber = serialNumber;
+ setName(remoteDbPath.lastSegment());
+ ImageDescriptor icon =
+ DbDevicesPlugin.imageDescriptorFromPlugin(DbCoreActivator.PLUGIN_ID,
+ DbNode.ICON_PATH);
+ setIcon(icon);
+ setTooltip(NLS.bind(DbDevicesNLS.DeviceDbNode_Tootip_Prefix, remoteDbPath.toString()));
+ }
+
+ /**
+ * Creates a new DeviceDbNode by creating a new SQLite3 database file if requested.
+ * This constructor will create a local temp file with the new SQLite3 database. the temp file will then be copied to the remotePath at the device.
+ * @param remoteDbPath The SQLite database File location at the device
+ * @param parent The parent of the new node.
+ * @param create set this flag to true if you want to create a new db file, if the flag is false the behavior is the same as the constructor DeviceDbNode(IPath remoteDbPath, String serialNumber, ITreeNode parent)
+ * @throws MotodevDbException if a problem occurred during database creation.
+ */
+ public DeviceDbNode(IPath remoteDbPath, String serialNumber, ITreeNode parent, boolean create)
+ throws MotodevDbException
+ {
+ this(remoteDbPath, serialNumber, parent);
+ if (create)
+ {
+ try
+ {
+ File tempFile = getLocalTempFile();
+ Path localDbPath = null;
+ if (tempFile != null)
+ {
+ localDbPath = new Path(tempFile.getAbsolutePath());
+ model = new DbModel(localDbPath, create, true);
+ IStatus status = pushLocalDbFile(false);
+ if (!status.isOK())
+ {
+ deleteLocalDbModel();
+ throw new MotodevDbException(
+ DbDevicesNLS.DeviceDbNode_Create_Device_Db_Failed);
+ }
+ }
+ else
+ {
+ throw new MotodevDbException(
+ DbDevicesNLS.DeviceDbNode_Could_Not_Create_DeviceDbNode);
+ }
+
+ }
+ catch (IOException e)
+ {
+ throw new MotodevDbException(
+ DbDevicesNLS.DeviceDbNode_Could_Not_Create_DeviceDbNode, e);
+ }
+ }
+ }
+
+ /**
+ * @return
+ * @throws IOException
+ */
+ private File getLocalTempFile() throws IOException
+ {
+ IPreferenceStore preferenceStore = DbDevicesPlugin.getDefault().getPreferenceStore();
+ File tempLocationFile = null;
+
+ if (!preferenceStore.isDefault(DbDevicesPlugin.DB_TEMP_PATH_PREFERENCE))
+ {
+ String tempLocation =
+ preferenceStore.getString(DbDevicesPlugin.DB_TEMP_PATH_PREFERENCE);
+ tempLocationFile = new File(tempLocation);
+
+ if (!tempLocationFile.isDirectory() || !FileUtil.canWrite(tempLocationFile))
+ {
+ EclipseUtils.showErrorDialog(DbDevicesNLS.ERR_DbUtils_Local_Db_Title,
+ NLS.bind(DbDevicesNLS.ERR_DbUtils_Local_Db_Msg, tempLocation));
+ preferenceStore.setToDefault(DbDevicesPlugin.DB_TEMP_PATH_PREFERENCE);
+ }
+
+ }
+
+ //If tempLocationFile is null the file will be created on system's default temp dir.
+ File tempFile =
+ File.createTempFile(serialNumber + "_" + remoteDbPath.segment(1) + "_" + getName(), //$NON-NLS-1$ //$NON-NLS-2$
+ "db", tempLocationFile); //$NON-NLS-1$
+ tempFile.deleteOnExit();
+ return tempFile;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorolamobility.studio.android.db.core.ui.IDbNode#connect()
+ */
+ @Override
+ public IStatus connect()
+ {
+ IStatus status = null;
+
+ File tempFile = null;
+ try
+ {
+ tempFile = getLocalTempFile();
+ status = pullRemoteTempFile(tempFile);
+ }
+ catch (IOException e)
+ {
+ status =
+ new Status(IStatus.ERROR, DbDevicesPlugin.PLUGIN_ID,
+ DbDevicesNLS.DeviceDbNode_Create_Temp_Local_Db_Failed, e);
+ }
+
+ if ((model != null) && status.isOK()) //Local model already exists, we must verify the md5 and update the localDbModel if needed.
+ {
+ try
+ {
+ String newMd5Sum = FileUtil.calculateMd5Sum(tempFile);
+ if (!newMd5Sum.equals(localFileMd5))
+ {
+ deleteLocalDbModel(); //Remote file has been changed. localDbModel must be updated
+ }
+ }
+ catch (IOException e)
+ {
+ status =
+ new Status(IStatus.ERROR, DbDevicesPlugin.PLUGIN_ID,
+ DbDevicesNLS.DeviceDbNode_Calculate_Local_Md5_Failed, e);
+ }
+
+ }
+
+ //model will be null if the remote file has been changed.
+ if ((model == null) && status.isOK())
+ {
+ try
+ {
+ model = new DbModel(Path.fromOSString(tempFile.getAbsolutePath()));
+ }
+ catch (MotodevDbException e)
+ {
+ status = new Status(IStatus.ERROR, DbDevicesPlugin.PLUGIN_ID, e.getMessage());
+ }
+ }
+
+ if ((model != null) && status.isOK())
+ {
+ try
+ {
+ localFileMd5 = getLocalMd5Sum();
+ model.connect();
+ }
+ catch (IOException e)
+ {
+ status =
+ new Status(IStatus.ERROR, DbDevicesPlugin.PLUGIN_ID,
+ DbDevicesNLS.DeviceDbNode_Calculate_Local_Md5_Failed, e);
+ }
+ }
+
+ if (status.isOK())
+ {
+ if (resultManagerListener == null)
+ {
+ resultManagerListener = new ResultManagerListener();
+ ResultsViewAPI.getInstance().getResultManager()
+ .addResultManagerListener(resultManagerListener);
+ }
+ isDirty = false;
+ }
+
+ setNodeStatus(status);
+
+ return status != null ? status : Status.OK_STATUS;
+ }
+
+ /**
+ * @return
+ * @throws IOException
+ */
+ private String getLocalMd5Sum() throws IOException
+ {
+ return FileUtil.calculateMd5Sum(model.getDbPath().toFile());
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorolamobility.studio.android.db.core.ui.IDbNode#disconnect()
+ */
+ @Override
+ public IStatus disconnect()
+ {
+ IStatus status = Status.OK_STATUS;
+ if ((model != null) && model.isConnected())
+ {
+
+ boolean canDisconnect = true;
+ status = closeAssociatedEditors();
+
+ canDisconnect = status.isOK();
+ if (canDisconnect)
+ {
+ status = model.disconnect();
+ if (status.isOK())
+ {
+ deleteLocalDbModel();
+ if (resultManagerListener != null)
+ {
+ ResultsViewAPI.getInstance().getResultManager()
+ .removeResultManagerListener(resultManagerListener);
+ resultManagerListener = null;
+ }
+ }
+ clear();
+ setNodeStatus(status);
+ }
+ }
+
+ return status;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorolamobility.studio.android.db.core.ui.IDbNode#createTables(java.util.List)
+ */
+ @Override
+ public IStatus createTables(List<TableModel> tables)
+ {
+ IStatus status;
+ status = checkMd5Sum(true);
+
+ if (status.isOK())
+ {
+ status = super.createTables(tables);
+ if (status.isOK())
+ {
+ pushLocalDbFile();
+ }
+ }
+
+ return status;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorolamobility.studio.android.db.core.ui.IDbNode#createTable(com.motorolamobility.studio.android.db.core.model.TableModel)
+ */
+ @Override
+ public IStatus createTable(TableModel table)
+ {
+ IStatus status;
+ status = checkMd5Sum(true);
+
+ if (status.isOK())
+ {
+ status = super.createTable(table);
+ if (status.isOK())
+ {
+ pushLocalDbFile();
+ }
+ }
+
+ return status;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorolamobility.studio.android.db.core.ui.IDbNode#deleteTable(java.lang.String)
+ */
+ @Override
+ public IStatus deleteTable(ITableNode tableNode)
+ {
+ IStatus status;
+ status = checkMd5Sum(true);
+
+ if (status.isOK())
+ {
+ status = super.deleteTable(tableNode);
+ if (status.isOK())
+ {
+ pushLocalDbFile();
+ }
+ }
+
+ return status;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorolamobility.studio.android.db.core.ui.IDbNode#deleteDb()
+ */
+ @Override
+ public IStatus deleteDb()
+ {
+ IStatus status = null;
+ try
+ {
+ closeAssociatedEditors(true, forceCloseEditors);
+ DDMSFacade.deleteFile(serialNumber, remoteDbPath.toString());
+ disconnect();
+ }
+ catch (IOException e)
+ {
+ status =
+ new Status(IStatus.ERROR, DbDevicesPlugin.PLUGIN_ID,
+ NLS.bind(DbDevicesNLS.DeviceDbNode_Delete_Remote_File_Failed,
+ remoteDbPath.toString(),
+ DDMSFacade.getNameBySerialNumber(serialNumber)));
+ }
+ return status != null ? status : Status.OK_STATUS;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorolamobility.studio.android.db.core.ui.AbstractTreeNode#canRefresh()
+ */
+ @Override
+ public IStatus canRefresh()
+ {
+ IStatus status = null;
+ if (isDirty)
+ {
+ status = checkMd5Sum(false);
+ if (!status.isOK())
+ {
+ status =
+ new CanRefreshStatus(CanRefreshStatus.ASK_USER
+ | CanRefreshStatus.CANCELABLE, DbDevicesPlugin.PLUGIN_ID, NLS.bind(
+ DbDevicesNLS.DeviceDbNode_DBOutOfSync_Refresh_Message, getName()));
+ }
+ }
+ else
+ {
+ Set<IEditorPart> associatedEditors = getAssociatedEditors();
+ if (!associatedEditors.isEmpty())
+ {
+ status =
+ new CanRefreshStatus(CanRefreshStatus.ASK_USER, DbDevicesPlugin.PLUGIN_ID,
+ NLS.bind(DbDevicesNLS.DeviceDbNode_RefreshQuestion, getName()));
+ }
+ }
+ return status != null ? status : Status.OK_STATUS;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorolamobility.studio.android.db.core.ui.AbstractTreeNode#refresh()
+ */
+ @Override
+ public void refresh()
+ {
+ if (model != null)
+ {
+ if (model.isConnected())
+ {
+ IStatus checkMd5Sum = checkMd5Sum(false);
+ if (!checkMd5Sum.isOK())
+ {
+ model.disconnect();
+ deleteLocalDbModel();
+ clear();
+ }
+ }
+ }
+
+ IStatus status = Status.OK_STATUS;
+ if ((model == null) || !model.isConnected())
+ {
+ status = connect(); //Force getting a fresh device db file
+ }
+
+ if (status.isOK())
+ {
+ super.refresh();
+ }
+ }
+
+ private boolean deleteLocalDbModel()
+ {
+ IStatus deleteDb = model.deleteDb();
+ model = null;
+
+ return deleteDb.isOK();
+ }
+
+ private IStatus pullRemoteTempFile(File tempFile)
+ {
+ IStatus status = null;
+ IOConsoleOutputStream stream = null;
+ try
+ {
+ IPath localDbPath = new Path(tempFile.getAbsolutePath());
+ List<File> localList = Arrays.asList(new File[]
+ {
+ localDbPath.toFile()
+ });
+ List<String> remoteList = Arrays.asList(new String[]
+ {
+ remoteDbPath.toString()
+ });
+
+ stream = EclipseUtils.getStudioConsoleOutputStream(false);
+ status =
+ DDMSFacade.pullFiles(serialNumber, localList, remoteList,
+ REMOTE_OPERATIONS_TIMEOUT, new NullProgressMonitor(), stream);
+ }
+ catch (Exception e)
+ {
+ status =
+ new Status(IStatus.ERROR, DbDevicesPlugin.PLUGIN_ID,
+ DbDevicesNLS.DeviceDbNode_Create_Temp_Local_Db_Failed, e);
+ }
+ finally
+ {
+ if (stream != null)
+ {
+ try
+ {
+ stream.close();
+ }
+ catch (IOException e)
+ {
+ StudioLogger.error("Could not close stream: ", e.getMessage()); //$NON-NLS-1$
+ }
+ }
+ }
+
+ return status != null ? status : Status.OK_STATUS;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorolamobility.studio.android.db.core.ui.AbstractTreeNode#refresh(boolean)
+ */
+ @Override
+ public void refresh(boolean canRefreshYesResponse)
+ {
+ if (canRefreshYesResponse)
+ {
+ closeAssociatedEditors(false, true);
+ }
+ else
+ {
+ pushLocalDbFile(false);
+ }
+
+ refresh();
+ }
+
+ private IStatus pushLocalDbFile()
+ {
+ return pushLocalDbFile(true);
+ }
+
+ private IStatus pushLocalDbFile(boolean warnUser)
+ {
+ IStatus status = null;
+ IOConsoleOutputStream stream = null;
+ try
+ {
+ IPath localDbPath = model.getDbPath();
+ File localDbFile = localDbPath.toFile();
+ List<File> localList = Arrays.asList(new File[]
+ {
+ localDbFile
+ });
+ List<String> remoteList = Arrays.asList(new String[]
+ {
+ remoteDbPath.toString()
+ });
+ stream = EclipseUtils.getStudioConsoleOutputStream(false);
+ status =
+ DDMSFacade.pushFiles(serialNumber, localList, remoteList,
+ REMOTE_OPERATIONS_TIMEOUT, new NullProgressMonitor(), stream);
+ if (status.isOK())
+ {
+ isDirty = false;
+ }
+
+ //Update the local Md5Sum everytime the file is pushed to the device.
+ localFileMd5 = FileUtil.calculateMd5Sum(localDbFile);
+
+ String appName = getParent().getName();
+ if (warnUser)
+ {
+ boolean applicationRunning = DDMSFacade.isApplicationRunning(serialNumber, appName);
+
+ if (applicationRunning)
+ {
+ EclipseUtils.showInformationDialog(
+ DbDevicesNLS.DeviceDbNode_Application_Running_Msg_Title, NLS
+ .bind(DbDevicesNLS.DeviceDbNode_Application_Running_Msg_Text,
+ appName));
+ }
+ }
+ }
+ catch (Exception e)
+ {
+ status =
+ new Status(IStatus.ERROR, DbDevicesPlugin.PLUGIN_ID, NLS.bind(
+ DbDevicesNLS.DeviceDbNode_Push_Local_File_To_Device_Failed,
+ serialNumber), e);
+ }
+ finally
+ {
+ if (stream != null)
+ {
+ try
+ {
+ stream.close();
+ }
+ catch (IOException e)
+ {
+ StudioLogger.error("Could not close stream: ", e.getMessage()); //$NON-NLS-1$
+ }
+ }
+ }
+
+ return status != null ? status : Status.OK_STATUS;
+ }
+
+ /**
+ * @param status
+ * @return
+ */
+ private IStatus checkMd5Sum(boolean warnUser)
+ {
+ File tempFile = null;
+ IStatus status = null;
+ if (localFileMd5 != null) //It will be null during create Db process.
+ {
+ try
+ {
+ tempFile = getLocalTempFile(); //Create a new tempFile, different from the local db model file, in order to compare MD5 sum.
+ status = pullRemoteTempFile(tempFile);
+ String newMd5Sum = FileUtil.calculateMd5Sum(tempFile);
+ if (!localFileMd5.equals(newMd5Sum))
+ {
+ if (warnUser)
+ {
+ boolean canOverwrite =
+ EclipseUtils.showQuestionDialog(
+ DbDevicesNLS.DeviceDbNode_Remote_File_Modified_Title,
+ NLS.bind(
+ DbDevicesNLS.DeviceDbNode_Remote_File_Modified_Msg,
+ getName()));
+ if (!canOverwrite)
+ {
+ status =
+ new Status(IStatus.CANCEL, DbDevicesPlugin.PLUGIN_ID,
+ DbDevicesNLS.DeviceDbNode_User_Canceled_Overwrite);
+ }
+ }
+ else
+ {
+ status =
+ new Status(IStatus.ERROR, DbDevicesPlugin.PLUGIN_ID,
+ DbDevicesNLS.DeviceDbNode_Md5Sum_Differs);
+ }
+ }
+
+ }
+ catch (IOException e)
+ {
+ status =
+ new Status(IStatus.ERROR, DbDevicesPlugin.PLUGIN_ID,
+ DbDevicesNLS.DeviceDbNode_Create_Temp_Local_Db_Failed, e);
+ }
+ finally
+ {
+ if (tempFile != null)
+ {
+ tempFile.delete();
+ }
+ }
+ }
+
+ return status != null ? status : Status.OK_STATUS;
+ }
+
+ public boolean remoteFileExists()
+ {
+ boolean remoteFileExists = false;
+ try
+ {
+ remoteFileExists = DDMSUtils.remoteFileExists(serialNumber, remoteDbPath.toString());
+ }
+ catch (IOException e)
+ {
+ //Return false on error
+ }
+ return remoteFileExists;
+ }
+
+ /**
+ * @return the remoteDbPath
+ */
+ public IPath getRemoteDbPath()
+ {
+ return remoteDbPath;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorolamobility.studio.android.db.core.ui.DbNode#clean()
+ */
+ @Override
+ public void cleanUp()
+ {
+ if (DDMSFacade.isDeviceOnline(serialNumber))
+ {
+ super.cleanUp();
+ }
+ else
+ {
+ closeAssociatedEditors(true, forceCloseEditors);
+ clear();
+ }
+ }
+}
diff --git a/src/plugins/db.devices/src/com/motorolamobility/studio/android/db/devices/model/DeviceNode.java b/src/plugins/db.devices/src/com/motorolamobility/studio/android/db/devices/model/DeviceNode.java
new file mode 100644
index 0000000..4519770
--- /dev/null
+++ b/src/plugins/db.devices/src/com/motorolamobility/studio/android/db/devices/model/DeviceNode.java
@@ -0,0 +1,381 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorolamobility.studio.android.db.devices.model;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.preferences.IEclipsePreferences;
+import org.eclipse.jdt.core.JavaConventions;
+import org.eclipse.jdt.core.JavaCore;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.osgi.util.NLS;
+import org.osgi.service.prefs.BackingStoreException;
+import org.osgi.service.prefs.Preferences;
+
+import com.motorola.studio.android.adt.DDMSFacade;
+import com.motorola.studio.android.adt.DdmsRunnable;
+import com.motorola.studio.android.adt.StudioAndroidEventManager;
+import com.motorola.studio.android.adt.StudioAndroidEventManager.EventType;
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorolamobility.studio.android.db.core.CanRefreshStatus;
+import com.motorolamobility.studio.android.db.core.ui.AbstractTreeNode;
+import com.motorolamobility.studio.android.db.core.ui.ISaveStateTreeNode;
+import com.motorolamobility.studio.android.db.core.ui.ITreeNode;
+import com.motorolamobility.studio.android.db.core.ui.view.SaveStateManager;
+import com.motorolamobility.studio.android.db.devices.DbDevicesPlugin;
+import com.motorolamobility.studio.android.db.devices.i18n.DbDevicesNLS;
+import com.motorolamobility.studio.android.db.devices.utils.DeviceDbUtils;
+
+/**
+ * This class represents a tree node for a given Android Device.
+ */
+public class DeviceNode extends AbstractTreeNode implements IDeviceNode, ISaveStateTreeNode
+{
+ private static final String MEMENTO_FILTER_TYPE = "filterAppsWithDb"; //$NON-NLS-1$
+
+ private static final String MEMENTO_FILTER_KEY = "filterEnabled"; //$NON-NLS-1$
+
+ private static final String ICON_PATH = "icons/obj16/device.png"; //$NON-NLS-1$
+
+ private String serialNumber;
+
+ private boolean filterAppsWithDb;
+
+ private String deviceName;
+
+ private final PackageChangedListener listener = new PackageChangedListener(this);
+
+ @SuppressWarnings("unused")
+ private DeviceNode()
+ {
+ //Forcing user to use a proper constructor
+ }
+
+ /**
+ * Creates a new Devicenode based on it's serial number.
+ * @param serialNumber the device's serial number.
+ * @param parent this node parent.
+ */
+ public DeviceNode(String serialNumber, ITreeNode parent)
+ {
+ super(serialNumber, DDMSFacade.getNameBySerialNumber(serialNumber), parent);
+ this.serialNumber = serialNumber;
+ this.deviceName = getName();
+ ImageDescriptor icon =
+ DbDevicesPlugin.imageDescriptorFromPlugin(DbDevicesPlugin.PLUGIN_ID, ICON_PATH);
+ setIcon(icon);
+
+ StudioAndroidEventManager.addEventListener(EventType.PACKAGE_INSTALLED, listener);
+ StudioAndroidEventManager.addEventListener(EventType.PACKAGE_UNINSTALLED, listener);
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorolamobility.studio.android.db.core.ui.AbstractTreeNode#canRefresh()
+ */
+ @Override
+ public IStatus canRefresh()
+ {
+ List<IStatus> childrenStatus = new ArrayList<IStatus>(getChildren().size());
+ String message = NLS.bind(DbDevicesNLS.DeviceNode_Cant_Refresh_Node, getName());
+ for (ITreeNode treeNode : getChildren())
+ {
+ IStatus nodecanRefresh = treeNode.canRefresh();
+ if (!nodecanRefresh.isOK())
+ {
+ childrenStatus.add(nodecanRefresh);
+ message = nodecanRefresh.getMessage();
+ break;
+ }
+ }
+
+ IStatus status = null;
+ if (!childrenStatus.isEmpty())
+ {
+ status =
+ new CanRefreshStatus(CanRefreshStatus.ASK_USER, DbDevicesPlugin.PLUGIN_ID,
+ childrenStatus.toArray(new IStatus[0]), message);
+ }
+ return status != null ? status : Status.OK_STATUS;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorolamobility.studio.android.db.core.ui.AbstractTreeNode#refresh()
+ */
+ @SuppressWarnings("unchecked")
+ @Override
+ public void refresh()
+ {
+ clear();
+
+ IStatus status = null;
+
+ Collection<String> loadedApps = null;
+ Map<String, String> listInstalledPackages = null;
+ Integer totalFiltered = 0;
+ try
+ {
+ Object[] installedPackagesContainer =
+ DeviceDbUtils.listInstalledPackages(serialNumber, mustFilterAppsWithDb());
+ listInstalledPackages = (Map<String, String>) installedPackagesContainer[0];
+ totalFiltered = (Integer) installedPackagesContainer[1];
+
+ }
+ catch (IOException e)
+ {
+ status =
+ new Status(IStatus.ERROR, DbDevicesPlugin.PLUGIN_ID, NLS.bind(
+ DbDevicesNLS.DeviceNode_CouldNotLoadInstalledApps, deviceName));
+ }
+
+ if (listInstalledPackages != null)
+ {
+ loadedApps = listInstalledPackages.keySet();
+ }
+
+ List<ITreeNode> childNodes = new ArrayList<ITreeNode>(loadedApps != null ? loadedApps.size() + 1 : 1);
+ try
+ {
+ if (DDMSFacade.hasSDCard(serialNumber))
+ {
+ ExtStorageNode extStorageNode = new ExtStorageNode(this);
+ childNodes.add(extStorageNode);
+ }
+ }
+ catch (IOException e1)
+ {
+ status =
+ new Status(IStatus.ERROR, DbDevicesPlugin.PLUGIN_ID, NLS.bind(
+ DbDevicesNLS.DeviceNode_CouldNotVerifySdCard, deviceName));
+ }
+
+ if (loadedApps != null)
+ {
+ for (String app : loadedApps)
+ {
+ if (JavaConventions.validatePackageName(app, JavaCore.VERSION_1_5,
+ JavaCore.VERSION_1_5).isOK())
+ {
+ ApplicationNode appNode = new ApplicationNode(app, DeviceNode.this);
+ childNodes.add(appNode);
+ }
+ }
+
+ if (!childNodes.isEmpty())
+ {
+ putChildren(childNodes);
+ }
+ }
+
+ updateName(totalFiltered);
+ setNodeStatus(status != null ? status : Status.OK_STATUS);
+ }
+
+ /**
+ * @param totalFiltered
+ */
+ private void updateName(Integer totalFiltered)
+ {
+ if (mustFilterAppsWithDb() && (totalFiltered > 0) && !getChildren().isEmpty())
+ {
+ setName(deviceName + NLS.bind(DbDevicesNLS.DeviceNode_X_Apps_Filtered, totalFiltered));
+ }
+ else
+ {
+ DeviceNode.this.setName(deviceName);
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorolamobility.studio.android.db.core.ui.AbstractTreeNode#isLeaf()
+ */
+ @Override
+ public boolean isLeaf()
+ {
+ return false;
+ }
+
+ /**
+ * @return the filterAppWithDb
+ */
+ public boolean mustFilterAppsWithDb()
+ {
+ return this.filterAppsWithDb;
+ }
+
+ /**
+ * @param filterAppWithDb the filterAppWithDb to set
+ */
+ public void setFilterAppWithDb(boolean filterAppWithDb)
+ {
+ this.filterAppsWithDb = filterAppWithDb;
+ saveState(SaveStateManager.getInstance().getPrefNode());
+ }
+
+ /**
+ * @return the serialNumber
+ */
+ public String getSerialNumber()
+ {
+ return serialNumber;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorolamobility.studio.android.db.core.ui.ISaveStateTreeNode#saveState(org.eclipse.core.runtime.preferences.IEclipsePreferences)
+ */
+ public void saveState(IEclipsePreferences preferences)
+ {
+ String id = deviceName != null ? deviceName : getName();
+ Preferences filterNode = preferences.node(MEMENTO_FILTER_TYPE);
+ Preferences deviceNode = filterNode.node(id);
+ deviceNode.putBoolean(MEMENTO_FILTER_KEY, mustFilterAppsWithDb());
+
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorolamobility.studio.android.db.core.ui.ISaveStateTreeNode#restoreState(org.eclipse.core.runtime.preferences.IEclipsePreferences)
+ */
+ public void restoreState(IEclipsePreferences preferences)
+ {
+ boolean filterDbApps = true;
+ String deviceName = this.deviceName != null ? this.deviceName : getName();
+ try
+ {
+ if (preferences.nodeExists(MEMENTO_FILTER_TYPE))
+ {
+ Preferences filterNode = preferences.node(MEMENTO_FILTER_TYPE);
+ if (filterNode.nodeExists(deviceName))
+ {
+ Preferences deviceNode = filterNode.node(deviceName);
+ filterDbApps = deviceNode.getBoolean(MEMENTO_FILTER_KEY, true);
+ }
+ }
+ }
+ catch (BackingStoreException e)
+ {
+ StudioLogger.error("Could not contact backing store: ", e.getMessage()); //$NON-NLS-1$
+ }
+ setFilterAppWithDb(filterDbApps);
+ }
+
+ /**
+ * Listener called when a package (application) is installed / uninstalled
+ */
+ private static class PackageChangedListener implements DdmsRunnable
+ {
+ private final DeviceNode deviceNode;
+
+ public PackageChangedListener(DeviceNode deviceNode)
+ {
+ this.deviceNode = deviceNode;
+ }
+
+ public void run(final String serialNumber)
+ {
+ Thread thread = new Thread(new Runnable()
+ {
+ @SuppressWarnings("unchecked")
+ public void run()
+ {
+ IStatus status = null;
+
+ //it is an expensive operation
+ Map<String, String> listInstalledPackages = null;
+ try
+ {
+ Object[] installedPackagesContainer =
+ DeviceDbUtils.listInstalledPackages(serialNumber,
+ deviceNode.mustFilterAppsWithDb());
+ listInstalledPackages = (Map<String, String>) installedPackagesContainer[0];
+ Integer totalFiltered = (Integer) installedPackagesContainer[1];
+
+ List<ITreeNode> childNodes = deviceNode.getChildren();
+
+ List<ITreeNode> toRemoveNodes = new ArrayList<ITreeNode>();
+ Set<String> newApplications = new HashSet<String>();
+ newApplications.addAll(listInstalledPackages.keySet());
+
+ if (childNodes != null)
+ {
+ Iterator<ITreeNode> iterator = childNodes.iterator();
+ while (iterator.hasNext())
+ {
+ ITreeNode node = iterator.next();
+ if (node instanceof ApplicationNode)
+ {
+ ApplicationNode appNode = (ApplicationNode) node;
+ if (!listInstalledPackages.containsKey(appNode.getId()))
+ {
+ //app was removed => mark to remove node from the tree
+ toRemoveNodes.add(node);
+ }
+ else
+ {
+ //app remains installed => remove it from newApplication set
+ newApplications.remove(appNode.getId());
+ }
+ }
+ }
+ //now the items in newApplication set must be the new applications => add nodes to each one of them
+ for (String newApp : newApplications)
+ {
+ ApplicationNode appNode = new ApplicationNode(newApp, deviceNode);
+ deviceNode.putChild(appNode);
+ }
+ for (ITreeNode node : toRemoveNodes)
+ {
+ //remove the nodes marked
+ deviceNode.removeChild(node);
+ }
+ deviceNode.updateName(totalFiltered);
+ }
+ }
+ catch (IOException e)
+ {
+ status =
+ new Status(IStatus.ERROR, DbDevicesPlugin.PLUGIN_ID, NLS.bind(
+ DbDevicesNLS.DeviceNode_CouldNotLoadInstalledApps,
+ deviceNode.getName()));
+ }
+
+ deviceNode.setNodeStatus(status != null ? status : Status.OK_STATUS);
+ }
+ });
+ thread.start();
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorolamobility.studio.android.db.core.ui.AbstractTreeNode#clean()
+ */
+ @Override
+ public void cleanUp()
+ {
+ super.cleanUp();
+ StudioAndroidEventManager.removeEventListener(EventType.PACKAGE_INSTALLED, listener);
+ StudioAndroidEventManager.removeEventListener(EventType.PACKAGE_UNINSTALLED, listener);
+ }
+}
diff --git a/src/plugins/db.devices/src/com/motorolamobility/studio/android/db/devices/model/DevicesRootNode.java b/src/plugins/db.devices/src/com/motorolamobility/studio/android/db/devices/model/DevicesRootNode.java
new file mode 100644
index 0000000..797da63
--- /dev/null
+++ b/src/plugins/db.devices/src/com/motorolamobility/studio/android/db/devices/model/DevicesRootNode.java
@@ -0,0 +1,176 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorolamobility.studio.android.db.devices.model;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.osgi.util.NLS;
+
+import com.motorola.studio.android.adt.DDMSFacade;
+import com.motorola.studio.android.adt.DdmsRunnable;
+import com.motorola.studio.android.adt.StudioAndroidEventManager;
+import com.motorolamobility.studio.android.db.core.CanRefreshStatus;
+import com.motorolamobility.studio.android.db.core.ui.AbstractTreeNode;
+import com.motorolamobility.studio.android.db.core.ui.IRootNode;
+import com.motorolamobility.studio.android.db.core.ui.ITreeNode;
+import com.motorolamobility.studio.android.db.devices.DbDevicesPlugin;
+import com.motorolamobility.studio.android.db.devices.i18n.DbDevicesNLS;
+
+/**
+ * This class represents the devices root tree node and will be initialized by the extension point.
+ * Is responsible to load the connected devices and listen to device events(connected/disconnected)
+ * It listens to device events and update its children accordingly.
+ */
+public class DevicesRootNode extends AbstractTreeNode implements IRootNode
+{
+ private static final String ICON_PATH = "icons/obj16/devices.png"; //$NON-NLS-1$
+
+ private ConnectDeviceListener connectedListener = new ConnectDeviceListener(this);
+
+ private DisconnectDeviceListener disconnectedListener = new DisconnectDeviceListener(this);
+
+ public DevicesRootNode()
+ {
+ StudioAndroidEventManager.asyncAddDeviceChangeListeners(connectedListener,
+ disconnectedListener);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorolamobility.studio.android.db.core.ui.AbstractTreeNode#canRefresh()
+ */
+ public IStatus canRefresh()
+ {
+ List<IStatus> childrenStatus = new ArrayList<IStatus>(getChildren().size());
+ String message = NLS.bind(DbDevicesNLS.DevicesRootNode_Cant_Refresh_Node, getName());
+ for (ITreeNode treeNode : getChildren())
+ {
+ IStatus nodecanRefresh = treeNode.canRefresh();
+ if (!nodecanRefresh.isOK())
+ {
+ childrenStatus.add(nodecanRefresh);
+ message = nodecanRefresh.getMessage();
+ break;
+ }
+ }
+
+ IStatus status = null;
+ if (!childrenStatus.isEmpty())
+ {
+ status =
+ new CanRefreshStatus(CanRefreshStatus.ASK_USER, DbDevicesPlugin.PLUGIN_ID,
+ childrenStatus.toArray(new IStatus[0]), message);
+ }
+ return status != null ? status : Status.OK_STATUS;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorolamobility.studio.android.db.core.ui.AbstractTreeNode#refresh()
+ */
+ @Override
+ public void refresh()
+ {
+ clear();
+ Collection<String> connectedSerialNumbers = DDMSFacade.getConnectedSerialNumbers();
+ List<ITreeNode> deviceNodes = new ArrayList<ITreeNode>();
+ for (String serialNumber : connectedSerialNumbers)
+ {
+ DeviceNode deviceNode = new DeviceNode(serialNumber, this);
+ deviceNodes.add(deviceNode);
+ }
+ putChildren(deviceNodes);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorolamobility.studio.android.db.core.ui.AbstractTreeNode#isLeaf()
+ */
+ @Override
+ public boolean isLeaf()
+ {
+ return false;
+ }
+
+ /**
+ * Listener called when a device is disconnected
+ */
+ private static class DisconnectDeviceListener implements DdmsRunnable
+ {
+ private DevicesRootNode devicesRootNode;
+
+ public DisconnectDeviceListener(DevicesRootNode devicesRootNode)
+ {
+ this.devicesRootNode = devicesRootNode;
+ }
+
+ public void run(String serialNumber)
+ {
+ ITreeNode treeNode = devicesRootNode.getChildById(serialNumber);
+ if (treeNode instanceof DeviceNode)
+ {
+ DeviceNode deviceNode = (DeviceNode) treeNode;
+ devicesRootNode.removeChild(deviceNode);
+ }
+ }
+ };
+
+ /**
+ * Listener called when a new device is connected
+ */
+ private static class ConnectDeviceListener implements DdmsRunnable
+ {
+ private DevicesRootNode devicesRootNode;
+
+ public ConnectDeviceListener(DevicesRootNode devicesRootNode)
+ {
+ this.devicesRootNode = devicesRootNode;
+ }
+
+ public void run(String serialNumber)
+ {
+ DeviceNode deviceNode = new DeviceNode(serialNumber, devicesRootNode);
+ devicesRootNode.putChild(deviceNode);
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorolamobility.studio.android.db.core.ui.AbstractTreeNode#getIcon()
+ */
+ @Override
+ public ImageDescriptor getIcon()
+ {
+ return DbDevicesPlugin.imageDescriptorFromPlugin(DbDevicesPlugin.PLUGIN_ID, ICON_PATH);
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorolamobility.studio.android.db.core.ui.AbstractTreeNode#clean()
+ */
+ @Override
+ public void cleanUp()
+ {
+ super.cleanUp();
+ StudioAndroidEventManager.removeEventListener(
+ StudioAndroidEventManager.EventType.DEVICE_CONNECTED, connectedListener);
+ StudioAndroidEventManager.removeEventListener(
+ StudioAndroidEventManager.EventType.DEVICE_DISCONNECTED, disconnectedListener);
+ }
+}
diff --git a/src/plugins/db.devices/src/com/motorolamobility/studio/android/db/devices/model/ExtStorageNode.java b/src/plugins/db.devices/src/com/motorolamobility/studio/android/db/devices/model/ExtStorageNode.java
new file mode 100644
index 0000000..f3147ac
--- /dev/null
+++ b/src/plugins/db.devices/src/com/motorolamobility/studio/android/db/devices/model/ExtStorageNode.java
@@ -0,0 +1,323 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorolamobility.studio.android.db.devices.model;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.MultiStatus;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.preferences.IEclipsePreferences;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.osgi.util.NLS;
+import org.osgi.service.prefs.BackingStoreException;
+import org.osgi.service.prefs.Preferences;
+
+import com.motorola.studio.android.adt.DDMSFacade;
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorolamobility.studio.android.db.core.CanRefreshStatus;
+import com.motorolamobility.studio.android.db.core.DbCoreActivator;
+import com.motorolamobility.studio.android.db.core.event.DatabaseModelEvent.EVENT_TYPE;
+import com.motorolamobility.studio.android.db.core.event.DatabaseModelEventManager;
+import com.motorolamobility.studio.android.db.core.ui.AbstractTreeNode;
+import com.motorolamobility.studio.android.db.core.ui.IDbMapperNode;
+import com.motorolamobility.studio.android.db.core.ui.IDbNode;
+import com.motorolamobility.studio.android.db.core.ui.ISaveStateTreeNode;
+import com.motorolamobility.studio.android.db.core.ui.ITreeNode;
+import com.motorolamobility.studio.android.db.core.ui.view.SaveStateManager;
+import com.motorolamobility.studio.android.db.devices.DbDevicesPlugin;
+import com.motorolamobility.studio.android.db.devices.i18n.DbDevicesNLS;
+
+/**
+ * This node represents a external storage node, sd card on devices that have it.
+ */
+public class ExtStorageNode extends AbstractTreeNode implements IDbDeviceMapperNode,
+ ISaveStateTreeNode
+{
+
+ private static final String MEMENTO_EXTERNAL_STORAGE = "ExternalStorageMapping"; //$NON-NLS-1$
+
+ private static final String MEMENTO_KEY_PREFIX = "MappedPath_"; //$NON-NLS-1$
+
+ private static final String ID_SUFFIX = "_EXT_STOR"; //$NON-NLS-1$
+
+ private static final String ICON_PATH = "icons/obj16/card.png"; //$NON-NLS-1$
+
+ private String serialNumber;
+
+ private Set<IPath> dbNodes;
+
+ private static final Map<String, Set<IPath>> dbNodesMap = new HashMap<String, Set<IPath>>();
+
+ public ExtStorageNode(DeviceNode parent)
+ {
+ super(parent);
+ setId(serialNumber + ID_SUFFIX); //$NON-NLS-1$
+ serialNumber = parent.getSerialNumber();
+ setName(DbDevicesNLS.ExtStorageNode_Node_Name);
+ ImageDescriptor icon =
+ DbDevicesPlugin.imageDescriptorFromPlugin(DbDevicesPlugin.PLUGIN_ID, ICON_PATH);
+ setIcon(icon);
+
+ Set<IPath> dbNodes = new HashSet<IPath>(getDbNodes(serialNumber));
+
+ for (IPath path : dbNodes)
+ {
+ map(path);
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorolamobility.studio.android.db.core.ui.AbstractTreeNode#canRefresh()
+ */
+ @Override
+ public IStatus canRefresh()
+ {
+ IStatus status = null;
+ boolean hasConnectedDb = false;
+
+ //Search for connected dbs
+ for (ITreeNode treeNode : getChildren())
+ {
+ if (treeNode instanceof DeviceDbNode)
+ {
+ DeviceDbNode dbNode = (DeviceDbNode) treeNode;
+ hasConnectedDb = dbNode.isConnected();
+ break;
+ }
+ }
+
+ if (hasConnectedDb)
+ {
+ status =
+ new CanRefreshStatus(CanRefreshStatus.ASK_USER, DbDevicesPlugin.PLUGIN_ID,
+ DbDevicesNLS.ExternalStorageNode_ConnectedDbs_Refresh_Message);
+ }
+
+ return status != null ? status : Status.OK_STATUS;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorolamobility.studio.android.db.core.ui.AbstractTreeNode#refresh()
+ */
+ @Override
+ public void refresh()
+ {
+ List<ITreeNode> children = getChildren();
+ for (ITreeNode child : children)
+ {
+ DeviceDbNode dbNode = (DeviceDbNode) child;
+ if (!dbNode.remoteFileExists())
+ {
+ dbNode.clear();
+ dbNode.setNodeStatus(new Status(IStatus.ERROR, DbDevicesPlugin.PLUGIN_ID,
+ DbDevicesNLS.ExtStorageNode_RemoteFile_Not_Exist));
+ }
+ else
+ {
+ dbNode.disconnect();
+ }
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorolamobility.studio.android.db.core.ui.AbstractTreeNode#isLeaf()
+ */
+ @Override
+ public boolean isLeaf()
+ {
+ return getChildren().isEmpty();
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorolamobility.studio.android.db.core.ui.IDbMapperNode#map()
+ */
+ public IStatus map(IPath remoteDbPath)
+ {
+ DeviceDbNode deviceDbNode = new DeviceDbNode(remoteDbPath, serialNumber, this);
+ putChild(deviceDbNode);
+
+ dbNodes.add(remoteDbPath);
+
+ DatabaseModelEventManager.getInstance().fireEvent(deviceDbNode, EVENT_TYPE.SELECT);
+ saveState(SaveStateManager.getInstance().getPrefNode());
+ return Status.OK_STATUS;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.IActionFilter#testAttribute(java.lang.Object, java.lang.String, java.lang.String)
+ */
+ @Override
+ public boolean testAttribute(Object target, String name, String value)
+ {
+ boolean canUnmap = false;
+ if (name.equals(IDbMapperNode.UNMAP_ACTIONFILTER_NAME)
+ && value.equals(IDbMapperNode.UNMAP_ACTIONFILTER_VALUE))
+ {
+ if (!getChildren().isEmpty())
+ {
+ canUnmap = true;
+ }
+ }
+ else
+ {
+ super.testAttribute(target, name, value);
+ }
+ return canUnmap;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorolamobility.studio.android.db.core.ui.IDbMapperNode#unmap(com.motorolamobility.studio.android.db.core.ui.ITreeNode)
+ */
+ public IStatus unmap(ITreeNode treeNode)
+ {
+ IStatus status = Status.OK_STATUS;
+ if (treeNode instanceof IDbNode)
+ {
+ IDbNode dbNode = (IDbNode) treeNode;
+ if (dbNode.isConnected())
+ {
+ status = dbNode.disconnect();
+ }
+ if (status.isOK())
+ {
+ removeChild(dbNode);
+ dbNodes.remove(((DeviceDbNode) treeNode).getRemoteDbPath());
+
+ }
+ else
+ {
+ status =
+ new Status(IStatus.ERROR, DbCoreActivator.PLUGIN_ID, NLS.bind(
+ DbDevicesNLS.ExtStorageNode_Disconnect_Failed, dbNode.getName(),
+ status.getMessage()));
+ }
+ }
+ saveState(SaveStateManager.getInstance().getPrefNode());
+ return status != null ? status : Status.OK_STATUS;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorolamobility.studio.android.db.core.ui.IDbMapperNode#unmap(java.util.List)
+ */
+ public IStatus unmap(List<ITreeNode> dbNodeList)
+ {
+ MultiStatus status = new MultiStatus(DbDevicesPlugin.PLUGIN_ID, IStatus.OK, "", null); //$NON-NLS-1$
+ for (ITreeNode dbNode : dbNodeList)
+ {
+ status.add(unmap(dbNode));
+ }
+ saveState(SaveStateManager.getInstance().getPrefNode());
+ return status != null ? status : Status.OK_STATUS;
+ }
+
+ private Set<IPath> getDbNodes(String serialNumber_)
+ {
+ dbNodes = dbNodesMap.get(serialNumber_);
+
+ if (dbNodes == null)
+ {
+ dbNodes = new HashSet<IPath>();
+ dbNodesMap.put(serialNumber_, dbNodes);
+
+ }
+ return dbNodes;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorolamobility.studio.android.db.core.ui.ISaveStateTreeNode#saveState(org.eclipse.core.runtime.preferences.IEclipsePreferences)
+ */
+ public void saveState(IEclipsePreferences preferences)
+ {
+ Preferences node = preferences.node(MEMENTO_EXTERNAL_STORAGE);
+ Preferences serialNode = node.node(DDMSFacade.getNameBySerialNumber(serialNumber));
+
+ int i = 1;
+ List<ITreeNode> children = getChildren();
+ for (ITreeNode child : children)
+ {
+ DeviceDbNode dbNode = (DeviceDbNode) child;
+ serialNode.put(MEMENTO_KEY_PREFIX + i, dbNode.getRemoteDbPath().toString());
+ i++;
+ }
+ try
+ {
+ preferences.flush();
+ }
+ catch (BackingStoreException e)
+ {
+ StudioLogger.error("Could not contact backing store: ", e.getMessage()); //$NON-NLS-1$
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorolamobility.studio.android.db.core.ui.ISaveStateTreeNode#restoreState(org.eclipse.core.runtime.preferences.IEclipsePreferences)
+ */
+ public void restoreState(IEclipsePreferences preferences)
+ {
+ if (serialNumber == null)
+ {
+ serialNumber = ((DeviceNode) getParent()).getSerialNumber();
+ }
+
+ boolean firstTime = !dbNodesMap.containsKey(serialNumber);
+
+ Set<IPath> dbNodes = getDbNodes(serialNumber);
+
+ if (firstTime)
+ {
+ try
+ {
+ if (preferences.nodeExists(MEMENTO_EXTERNAL_STORAGE))
+ {
+ Preferences node = preferences.node(MEMENTO_EXTERNAL_STORAGE);
+ String deviceName = DDMSFacade.getNameBySerialNumber(serialNumber);
+ if (node.nodeExists(deviceName))
+ {
+ Preferences deviceNode = node.node(deviceName);
+ String[] attributeKeys = deviceNode.keys();
+ if (attributeKeys.length > 0)
+ {
+ for (String key : attributeKeys)
+ {
+ if (key.startsWith(MEMENTO_KEY_PREFIX)) //$NON-NLS-1$
+ {
+ String mappedPath = deviceNode.get(key, null);
+ if (mappedPath != null)
+ {
+ dbNodes.add(new Path(mappedPath));
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ catch (BackingStoreException e)
+ {
+ StudioLogger.error("Could not contact backing store: ", e.getMessage()); //$NON-NLS-1$
+ }
+ }
+ }
+}
diff --git a/src/plugins/db.devices/src/com/motorolamobility/studio/android/db/devices/model/IDbDeviceMapperNode.java b/src/plugins/db.devices/src/com/motorolamobility/studio/android/db/devices/model/IDbDeviceMapperNode.java
new file mode 100644
index 0000000..10831cd
--- /dev/null
+++ b/src/plugins/db.devices/src/com/motorolamobility/studio/android/db/devices/model/IDbDeviceMapperNode.java
@@ -0,0 +1,27 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorolamobility.studio.android.db.devices.model;
+
+import com.motorolamobility.studio.android.db.core.ui.IDbMapperNode;
+
+/**
+ * Interface to allow mapper nodes from devices to have a separate Map action from the popup menu.
+ *
+ */
+public interface IDbDeviceMapperNode extends IDbMapperNode
+{
+
+}
diff --git a/src/plugins/db.devices/src/com/motorolamobility/studio/android/db/devices/model/IDeviceNode.java b/src/plugins/db.devices/src/com/motorolamobility/studio/android/db/devices/model/IDeviceNode.java
new file mode 100644
index 0000000..4452d99
--- /dev/null
+++ b/src/plugins/db.devices/src/com/motorolamobility/studio/android/db/devices/model/IDeviceNode.java
@@ -0,0 +1,39 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorolamobility.studio.android.db.devices.model;
+
+import com.motorolamobility.studio.android.db.core.ui.ITreeNode;
+
+public interface IDeviceNode extends ITreeNode
+{
+
+ /**
+ * enables/disables the filter apps with db
+ * @param filterAppWithDb
+ */
+ void setFilterAppWithDb(boolean filterAppWithDb);
+
+ /**
+ * @return the state of the filter apps with db, true for enabled, false otherwise
+ */
+ boolean mustFilterAppsWithDb();
+
+ /**
+ * @return the device serial number
+ */
+ String getSerialNumber();
+
+}
diff --git a/src/plugins/db.devices/src/com/motorolamobility/studio/android/db/devices/ui/action/FilterDbApplicationHandler.java b/src/plugins/db.devices/src/com/motorolamobility/studio/android/db/devices/ui/action/FilterDbApplicationHandler.java
new file mode 100644
index 0000000..50eda0d
--- /dev/null
+++ b/src/plugins/db.devices/src/com/motorolamobility/studio/android/db/devices/ui/action/FilterDbApplicationHandler.java
@@ -0,0 +1,67 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorolamobility.studio.android.db.devices.ui.action;
+
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.core.commands.IHandler;
+import org.eclipse.ui.handlers.HandlerUtil;
+
+import com.motorolamobility.studio.android.db.core.DbCoreActivator;
+import com.motorolamobility.studio.android.db.core.ui.ITreeNode;
+import com.motorolamobility.studio.android.db.devices.model.IDeviceNode;
+
+/**
+ * Handler responsible for toggling filter apps with dbs.
+ */
+public class FilterDbApplicationHandler extends AbstractHandler implements IHandler
+{
+
+ private ITreeNode node;
+
+ public FilterDbApplicationHandler()
+ {
+ //do nothing
+ }
+
+ public FilterDbApplicationHandler(ITreeNode node)
+ {
+ this.node = node;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.commands.IHandler#execute(org.eclipse.core.commands.ExecutionEvent)
+ */
+ public Object execute(ExecutionEvent event) throws ExecutionException
+ {
+ if (node == null)
+ {
+ node = DbCoreActivator.getMOTODEVDatabaseExplorerView().getSelectedItemOnTree();
+ }
+ if(node instanceof IDeviceNode)
+ {
+ boolean oldValue = HandlerUtil.toggleCommandState(event.getCommand());
+ IDeviceNode deviceNode = (IDeviceNode) node;
+ deviceNode.setFilterAppWithDb(!oldValue);
+ deviceNode.refreshAsync();
+ }
+ node = null;
+
+ return null;
+ }
+
+}
diff --git a/src/plugins/db.devices/src/com/motorolamobility/studio/android/db/devices/ui/action/MapDeviceDatabaseHandler.java b/src/plugins/db.devices/src/com/motorolamobility/studio/android/db/devices/ui/action/MapDeviceDatabaseHandler.java
new file mode 100644
index 0000000..2fcf9e9
--- /dev/null
+++ b/src/plugins/db.devices/src/com/motorolamobility/studio/android/db/devices/ui/action/MapDeviceDatabaseHandler.java
@@ -0,0 +1,110 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorolamobility.studio.android.db.devices.ui.action;
+
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.core.commands.IHandler;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.jface.dialogs.Dialog;
+import org.eclipse.jface.dialogs.IInputValidator;
+import org.eclipse.jface.dialogs.InputDialog;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
+
+import com.motorolamobility.studio.android.db.core.ui.IDbMapperNode;
+import com.motorolamobility.studio.android.db.core.ui.ITreeNode;
+import com.motorolamobility.studio.android.db.devices.i18n.DbDevicesNLS;
+
+public class MapDeviceDatabaseHandler extends AbstractHandler implements IHandler
+{
+
+ private IDbMapperNode dbMapperNode;
+
+ public MapDeviceDatabaseHandler()
+ {
+ //do nothing
+ }
+
+ public MapDeviceDatabaseHandler(ITreeNode node)
+ {
+ if (node instanceof IDbMapperNode)
+ {
+ this.dbMapperNode = (IDbMapperNode) node;
+ }
+ }
+
+ public Object execute(ExecutionEvent event) throws ExecutionException
+ {
+ if (dbMapperNode != null)
+ {
+
+ Shell shell = Display.getCurrent().getActiveShell();
+ InputDialog dialog =
+ new InputDialog(shell,
+ DbDevicesNLS.UI_MapDatabaseAction_QueryDbPath_DialogTitle,
+ DbDevicesNLS.UI_MapDatabaseAction_QueryDbPath_DialogMessage, "", //$NON-NLS-1$
+ new IInputValidator()
+ {
+
+ public String isValid(String newText)
+ {
+ String errorMsg = null;
+ boolean isValid = false;
+
+ if (newText.startsWith("/sdcard/") //$NON-NLS-1$
+ && newText.length() > "/sdcard/".length()) //$NON-NLS-1$
+ {
+ isValid = true;
+ }
+ if (!isValid)
+ {
+ if (newText.startsWith("/mnt/sdcard/") //$NON-NLS-1$
+ && newText.length() > "/mnt/sdcard/".length()) //$NON-NLS-1$
+ {
+ isValid = true;
+ }
+ }
+
+ if (!isValid)
+ {
+ errorMsg =
+ DbDevicesNLS.MapDatabaseAction_Error_WrongDatabasePlace;
+ }
+
+ return errorMsg;
+ }
+ });
+
+ dialog.setBlockOnOpen(true);
+ int result = dialog.open();
+
+ String dbPath = null;
+
+ if (result == Dialog.OK)
+ {
+ dbPath = dialog.getValue();
+ }
+
+ if (dbPath != null)
+ {
+ dbMapperNode.map(new Path(dbPath));
+ }
+ }
+ return null;
+ }
+}
diff --git a/src/plugins/db.devices/src/com/motorolamobility/studio/android/db/devices/ui/action/PersistentToggleState.java b/src/plugins/db.devices/src/com/motorolamobility/studio/android/db/devices/ui/action/PersistentToggleState.java
new file mode 100644
index 0000000..7a7c149
--- /dev/null
+++ b/src/plugins/db.devices/src/com/motorolamobility/studio/android/db/devices/ui/action/PersistentToggleState.java
@@ -0,0 +1,48 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorolamobility.studio.android.db.devices.ui.action;
+
+import org.eclipse.core.commands.State;
+
+import com.motorolamobility.studio.android.db.core.DbCoreActivator;
+import com.motorolamobility.studio.android.db.core.ui.ITreeNode;
+import com.motorolamobility.studio.android.db.devices.model.DeviceNode;
+
+public class PersistentToggleState extends State
+{
+
+ public PersistentToggleState()
+ {
+ setValue(true);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.commands.State#getValue()
+ */
+ @Override
+ public Object getValue()
+ {
+ ITreeNode treeNode =
+ DbCoreActivator.getMOTODEVDatabaseExplorerView().getSelectedItemOnTree();
+ if (treeNode instanceof DeviceNode)
+ {
+ DeviceNode devNode = (DeviceNode) treeNode;
+ boolean filterEnabled = devNode.mustFilterAppsWithDb();
+ setValue(filterEnabled);
+ }
+ return super.getValue();
+ }
+}
diff --git a/src/plugins/db.devices/src/com/motorolamobility/studio/android/db/devices/ui/action/PopupMenuActionDelegate.java b/src/plugins/db.devices/src/com/motorolamobility/studio/android/db/devices/ui/action/PopupMenuActionDelegate.java
new file mode 100644
index 0000000..66a8c9a
--- /dev/null
+++ b/src/plugins/db.devices/src/com/motorolamobility/studio/android/db/devices/ui/action/PopupMenuActionDelegate.java
@@ -0,0 +1,159 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorolamobility.studio.android.db.devices.ui.action;
+
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.core.commands.IHandler;
+import org.eclipse.jface.action.IAction;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.ui.IObjectActionDelegate;
+import org.eclipse.ui.IWorkbenchPart;
+
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorolamobility.studio.android.db.core.command.RefreshNodeHandler;
+import com.motorolamobility.studio.android.db.core.ui.AbstractTreeNode;
+import com.motorolamobility.studio.android.db.core.ui.ITreeNode;
+
+public class PopupMenuActionDelegate implements IObjectActionDelegate
+{
+
+ /**
+ * Enum type for ActionHandlers. If you need to add a new ActionHandler, just include
+ * a new type to this enum with the action id that you defined on your action extension point
+ */
+ enum ActionHandlers
+ {
+ REFRESH_DEVICE("com.motorolamobility.studio.android.db.devices.ui.action.refreshDeviceNode") //$NON-NLS-1$
+ {
+
+ @Override
+ public IHandler getHandler(ITreeNode node)
+ {
+ return new RefreshNodeHandler(node);
+ }
+
+ },
+ FILTER_DB_APPNODE(
+ "com.motorolamobility.studio.android.db.devices.ui.action.filterDbApplicationNode") //$NON-NLS-1$
+ {
+
+ @Override
+ public IHandler getHandler(ITreeNode node)
+ {
+ return new FilterDbApplicationHandler(node);
+ }
+
+ },
+ MAP_DEVICE_DB_APPNODE(
+ "com.motorolamobility.studio.android.db.devices.ui.action.mapDeviceDbNode") //$NON-NLS-1$
+ {
+
+ @Override
+ public IHandler getHandler(ITreeNode node)
+ {
+ return new MapDeviceDatabaseHandler(node);
+ }
+
+ },
+ SAVE_TO_LOCAL_FILE(
+ "com.motorolamobility.studio.android.db.devices.ui.action.saveToLocalFile") //$NON-NLS-1$
+ {
+
+ @Override
+ public IHandler getHandler(ITreeNode node)
+ {
+ return new SaveDatabaseToFileHandler(node);
+ }
+
+ };
+
+ private final String actionId;
+
+ private ActionHandlers(String actionId)
+ {
+ this.actionId = actionId;
+ }
+
+ public abstract IHandler getHandler(ITreeNode node);
+
+ public static ActionHandlers getActionHandlerbyId(String id)
+ {
+
+ Object ret = null;
+ for (ActionHandlers h : ActionHandlers.values())
+ {
+ if (h.actionId.equals(id))
+ {
+ ret = h;
+ break;
+ }
+ }
+
+ return (ActionHandlers) ret;
+ }
+ }
+
+ private ITreeNode currentNode;
+
+ public void run(IAction action)
+ {
+
+ ActionHandlers type = ActionHandlers.getActionHandlerbyId(action.getId());
+
+ IHandler handler = null;
+
+ if (type != null)
+ {
+ handler = type.getHandler(currentNode);
+ }
+
+ if (handler != null)
+ {
+ ExecutionEvent event = new ExecutionEvent();
+ try
+ {
+ handler.execute(event);
+ }
+ catch (ExecutionException e)
+ {
+ StudioLogger.error("Could not execute command: ", e.getMessage()); //$NON-NLS-1$
+ }
+ }
+
+ }
+
+ public void selectionChanged(IAction action, ISelection selection)
+ {
+ if (selection instanceof IStructuredSelection)
+ {
+ IStructuredSelection structuredSelection = (IStructuredSelection) selection;
+ Object selectedObject = structuredSelection.getFirstElement();
+ if (selectedObject instanceof AbstractTreeNode)
+ {
+ currentNode = (ITreeNode) selectedObject;
+ }
+ }
+
+ }
+
+ public void setActivePart(IAction action, IWorkbenchPart targetPart)
+ {
+ //do nothing
+ }
+
+}
diff --git a/src/plugins/db.devices/src/com/motorolamobility/studio/android/db/devices/ui/action/SaveDatabaseToFileHandler.java b/src/plugins/db.devices/src/com/motorolamobility/studio/android/db/devices/ui/action/SaveDatabaseToFileHandler.java
new file mode 100644
index 0000000..4abbae8
--- /dev/null
+++ b/src/plugins/db.devices/src/com/motorolamobility/studio/android/db/devices/ui/action/SaveDatabaseToFileHandler.java
@@ -0,0 +1,91 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorolamobility.studio.android.db.devices.ui.action;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.core.commands.IHandler;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.FileDialog;
+import org.eclipse.swt.widgets.Shell;
+
+import com.motorola.studio.android.common.utilities.FileUtil;
+import com.motorolamobility.studio.android.db.core.ui.ITreeNode;
+import com.motorolamobility.studio.android.db.devices.i18n.DbDevicesNLS;
+import com.motorolamobility.studio.android.db.devices.model.DeviceDbNode;
+
+public class SaveDatabaseToFileHandler extends AbstractHandler implements IHandler
+{
+ private DeviceDbNode deviceDbNode;
+
+ public SaveDatabaseToFileHandler(ITreeNode node)
+ {
+ if (node instanceof DeviceDbNode)
+ {
+ this.deviceDbNode = (DeviceDbNode) node;
+ }
+ }
+
+ public Object execute(ExecutionEvent event) throws ExecutionException
+ {
+ if (deviceDbNode != null)
+ {
+ Shell shell = Display.getCurrent().getActiveShell();
+ FileDialog dialog = new FileDialog(shell, SWT.SAVE);
+ dialog.setFileName(deviceDbNode.getRemoteDbPath().lastSegment());
+ dialog.setFilterNames(new String[]
+ {
+ DbDevicesNLS.SaveDatabaseToFile_DbFiles,
+ DbDevicesNLS.SaveDatabaseToFile_AllFiles
+ });
+ dialog.setFilterExtensions(new String[]
+ {
+ "*.db", "*.*" //$NON-NLS-1$ //$NON-NLS-2$
+ });
+
+ String selectedFilePath = dialog.open(); //returns null if dialog is cancelled
+
+ if (selectedFilePath != null)
+ {
+ //dialog confirmed
+ File targetFile = new File(selectedFilePath);
+
+ IPath temporaryFilePath = deviceDbNode.getPath();
+ File sourceFile = temporaryFilePath.toFile();
+
+ try
+ {
+ FileUtil.copyFile(sourceFile, targetFile);
+ }
+ catch (IOException e)
+ {
+ throw new ExecutionException(DbDevicesNLS.bind(
+ DbDevicesNLS.SaveDatabaseToFile_CopyDatabase_Error,
+ deviceDbNode.getName(), targetFile), e);
+ }
+ }
+
+ }
+ return null;
+ }
+
+}
diff --git a/src/plugins/db.devices/src/com/motorolamobility/studio/android/db/devices/ui/preferences/DbPreferencePage.java b/src/plugins/db.devices/src/com/motorolamobility/studio/android/db/devices/ui/preferences/DbPreferencePage.java
new file mode 100644
index 0000000..0b1ff0d
--- /dev/null
+++ b/src/plugins/db.devices/src/com/motorolamobility/studio/android/db/devices/ui/preferences/DbPreferencePage.java
@@ -0,0 +1,173 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorolamobility.studio.android.db.devices.ui.preferences;
+
+import java.io.File;
+
+import org.eclipse.core.runtime.Path;
+import org.eclipse.core.runtime.preferences.InstanceScope;
+import org.eclipse.jface.preference.DirectoryFieldEditor;
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.jface.preference.PreferencePage;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.ModifyEvent;
+import org.eclipse.swt.events.ModifyListener;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.IWorkbenchPreferencePage;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.preferences.ScopedPreferenceStore;
+
+import com.motorolamobility.studio.android.db.devices.DbDevicesPlugin;
+import com.motorolamobility.studio.android.db.devices.i18n.DbDevicesNLS;
+
+public class DbPreferencePage extends PreferencePage implements IWorkbenchPreferencePage
+{
+
+ public static final String LEGACY_PLUGIN_ID = "com.motorola.studio.android.db"; //$NON-NLS-1$
+
+ public static final String LEGACY_DB_PATH_PREFERENCE = LEGACY_PLUGIN_ID + ".dbstudiopath"; //$NON-NLS-1$
+
+ private DirectoryFieldEditor directoryEditor;
+
+ private static final String PREFERENCE_PAGE_HELP = DbDevicesPlugin.PLUGIN_ID
+ + ".preference-database"; //$NON-NLS-1$
+
+ public DbPreferencePage()
+ {
+ setPreferenceStore(DbDevicesPlugin.getDefault().getPreferenceStore());
+ }
+
+ @Override
+ protected Control createContents(Composite parent)
+ {
+ Composite main = new Composite(parent, SWT.FILL);
+ main.setLayoutData(new GridData(GridData.FILL_BOTH));
+
+ main.setLayout(new GridLayout(1, false));
+ directoryEditor =
+ new DirectoryFieldEditor(DbDevicesPlugin.DB_TEMP_PATH_PREFERENCE,
+ DbDevicesNLS.UI_PreferencePage_PathLabel, main);
+ directoryEditor.getTextControl(main).addModifyListener(new ModifyListener()
+ {
+ public void modifyText(ModifyEvent e)
+ {
+ validateDirectory();
+ }
+ });
+
+ directoryEditor.setStringValue(getPreferenceStore().getString(
+ DbDevicesPlugin.DB_TEMP_PATH_PREFERENCE));
+
+ PlatformUI.getWorkbench().getHelpSystem().setHelp(main, PREFERENCE_PAGE_HELP);
+ PlatformUI.getWorkbench().getHelpSystem().setHelp(parent, PREFERENCE_PAGE_HELP);
+
+ return main;
+ }
+
+ public boolean validateDirectory()
+ {
+ boolean valid = false;
+ if (directoryEditor.getStringValue().trim().length() == 0)
+ {
+ valid = true;
+ }
+ else
+ {
+ try
+ {
+ File f = new File(directoryEditor.getStringValue());
+ if (f.isDirectory())
+ {
+ File f2 = new File(directoryEditor.getStringValue() + Path.SEPARATOR + "test"); //$NON-NLS-1$
+ f2.createNewFile();
+ f2.delete();
+ valid = true;
+ }
+ }
+ catch (Exception e)
+ {
+ valid = false;
+ }
+ }
+ if (!valid)
+ {
+ setErrorMessage(DbDevicesNLS.ERR_DbPrefPage_InvalidDir);
+ setValid(false);
+ return false;
+ }
+ else
+ {
+ setErrorMessage(null);
+ setValid(true);
+ return true;
+ }
+ }
+
+ @Override
+ protected void performDefaults()
+ {
+ getPreferenceStore().setToDefault(DbDevicesPlugin.DB_TEMP_PATH_PREFERENCE);
+ directoryEditor.setStringValue(getPreferenceStore().getString(
+ DbDevicesPlugin.DB_TEMP_PATH_PREFERENCE));
+ }
+
+ @Override
+ public boolean performOk()
+ {
+ boolean canReturn = true;
+ if (directoryEditor.getStringValue().trim().length() == 0)
+ {
+ performDefaults();
+ }
+ else
+ {
+ if (!validateDirectory())
+ {
+ canReturn = false;
+ }
+ else
+ {
+ getPreferenceStore().setValue(DbDevicesPlugin.DB_TEMP_PATH_PREFERENCE,
+ directoryEditor.getStringValue());
+ canReturn = super.performOk();
+ }
+ }
+ return canReturn;
+ }
+
+ public void init(IWorkbench workbench)
+ {
+ //do nothing
+ }
+
+ public static void restoreBackWardPref(IPreferenceStore currentPrefStore)
+ {
+ IPreferenceStore preferenceStore =
+ new ScopedPreferenceStore(InstanceScope.INSTANCE, LEGACY_PLUGIN_ID);
+ String backwardDbTempPath = preferenceStore.getString(LEGACY_DB_PATH_PREFERENCE);
+ if (!backwardDbTempPath.isEmpty())
+ {
+ currentPrefStore.setValue(DbDevicesPlugin.DB_TEMP_PATH_PREFERENCE, backwardDbTempPath);
+
+ preferenceStore.setValue(LEGACY_DB_PATH_PREFERENCE, ""); //$NON-NLS-1$
+ }
+ }
+
+}
diff --git a/src/plugins/db.devices/src/com/motorolamobility/studio/android/db/devices/utils/DeviceDbUtils.java b/src/plugins/db.devices/src/com/motorolamobility/studio/android/db/devices/utils/DeviceDbUtils.java
new file mode 100644
index 0000000..124be0a
--- /dev/null
+++ b/src/plugins/db.devices/src/com/motorolamobility/studio/android/db/devices/utils/DeviceDbUtils.java
@@ -0,0 +1,122 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorolamobility.studio.android.db.devices.utils;
+
+import java.io.IOException;
+import java.util.Collection;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.core.runtime.Path;
+
+import com.motorola.studio.android.adt.DDMSFacade;
+import com.motorola.studio.android.adt.DDMSUtils;
+
+public class DeviceDbUtils
+{
+ /**
+ *
+ */
+ private static final String DB_EXTENSION = "db"; //$NON-NLS-1$
+ private static final String APPNAME_KEY = "#APP_NAME"; //$NON-NLS-1$
+ private static final String remoteDbPathMask = "/data/data/" + APPNAME_KEY + "/databases/"; //$NON-NLS-1$ //$NON-NLS-2$
+
+ /**
+ * Retrieves the databases path for a given application on the android file system
+ */
+ public static IPath getRemoteDbFolder(String appName)
+ {
+ String remoteDbFolder = remoteDbPathMask.replace(APPNAME_KEY, appName);
+ return new Path(remoteDbFolder);
+ }
+
+ /**
+ * Retrieves the database file path for a given application's db file on the android file system.
+ * @param appName the application name
+ * @param dbFileName the db file name
+ * @return the full db file path on a android file system
+ */
+ public static IPath getRemoteDbPath(String appName, String dbFileName)
+ {
+ IPath remoteDbFolder = getRemoteDbFolder(appName);
+ IPath remoteDbPath = remoteDbFolder.append(dbFileName);
+ String fileExtension = remoteDbPath.getFileExtension();
+ if(fileExtension == null || !fileExtension.equals(DB_EXTENSION))
+ {
+ remoteDbPath = remoteDbPath.addFileExtension(DB_EXTENSION);
+ }
+ return remoteDbPath;
+ }
+
+ /**
+ * List the installed packages in the device with the serial number Each
+ * package entry carries their package location
+ *
+ * @param serialNumber
+ * @return an Object array that contains:
+ * Item 0: a map<String, String> with the app package as a key and the app path as value.
+ * Item 1: number of applications not included on map due to filterDbApplications being true
+ * @throws IOException
+ */
+ public static Object[] listInstalledPackages(String serialNumber, boolean filterDBbApplications) throws IOException
+ {
+ Object[] returnArray = new Object[2];
+
+ Map<String, String> allPackages = DDMSUtils.listInstalledPackages(serialNumber);
+
+ if(filterDBbApplications)
+ {
+ Map<String, String> filteredPackages = new LinkedHashMap<String, String>();
+
+ Collection<String> appDataDirs =
+ DDMSFacade.execRemoteApp(serialNumber,
+ "ls /data/data/", new NullProgressMonitor()); //$NON-NLS-1$
+
+ for(String appPackage : appDataDirs)
+ {
+ IPath remoteDbFolder = getRemoteDbFolder(appPackage);
+ Collection<String> databases =
+ DDMSFacade.execRemoteApp(serialNumber,
+ "ls " + remoteDbFolder.toString(), new NullProgressMonitor()); //$NON-NLS-1$
+
+ for (String commandOutline : databases)
+ {
+ String[] strings = commandOutline.split(" "); //$NON-NLS-1$
+ for (String string : strings)
+ {
+ if (string.trim().endsWith("." + DB_EXTENSION)) //$NON-NLS-1$
+ {
+ filteredPackages.put(appPackage, allPackages.get(appPackage));
+ }
+ }
+ }
+ }
+ returnArray[0] = filteredPackages;
+ returnArray[1] = allPackages.size() - filteredPackages.size();
+ }
+ else
+ {
+ returnArray[0] = allPackages;
+ returnArray[1] = 0;
+ }
+
+
+ return returnArray;
+ }
+
+}
diff --git a/src/plugins/devices.services/.classpath b/src/plugins/devices.services/.classpath
new file mode 100644
index 0000000..8a8f166
--- /dev/null
+++ b/src/plugins/devices.services/.classpath
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>
+ <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/src/plugins/devices.services/.project b/src/plugins/devices.services/.project
new file mode 100644
index 0000000..f5272f0
--- /dev/null
+++ b/src/plugins/devices.services/.project
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>com.motorola.studio.android.devices.services</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.ManifestBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.SchemaBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.pde.PluginNature</nature>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+</projectDescription>
diff --git a/src/plugins/devices.services/META-INF/MANIFEST.MF b/src/plugins/devices.services/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..fdab6ce
--- /dev/null
+++ b/src/plugins/devices.services/META-INF/MANIFEST.MF
@@ -0,0 +1,21 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: %pluginName
+Bundle-SymbolicName: com.motorola.studio.android.devices.services;singleton:=true
+Bundle-Version: 5.0.0.qualifier
+Bundle-Activator: com.motorola.studio.android.devices.services.DeviceServicesPlugin
+Bundle-Vendor: %providerName
+Require-Bundle: com.motorola.studio.android,
+ com.motorola.studio.android.handset,
+ com.motorola.studio.android.emulator,
+ com.motorola.studio.android.common,
+ org.eclipse.sequoyah.device.framework,
+ org.eclipse.sequoyah.device.common.utilities,
+ org.eclipse.ui.console,
+ org.eclipse.ui,
+ org.eclipse.core.expressions,
+ org.eclipse.core.runtime
+Bundle-RequiredExecutionEnvironment: JavaSE-1.6
+Bundle-ActivationPolicy: lazy
+Bundle-Localization: plugin
+Export-Package: com.motorola.studio.android.devices.services.i18n
diff --git a/src/plugins/devices.services/build.properties b/src/plugins/devices.services/build.properties
new file mode 100644
index 0000000..7af5c82
--- /dev/null
+++ b/src/plugins/devices.services/build.properties
@@ -0,0 +1,8 @@
+source.. = src/
+output.. = bin/
+bin.includes = META-INF/,\
+ .,\
+ plugin.xml,\
+ icons/,\
+ plugin.properties,\
+ resources/
diff --git a/src/plugins/devices.services/icons/adb_shell.png b/src/plugins/devices.services/icons/adb_shell.png
new file mode 100644
index 0000000..1872706
--- /dev/null
+++ b/src/plugins/devices.services/icons/adb_shell.png
Binary files differ
diff --git a/src/plugins/devices.services/icons/deploy.png b/src/plugins/devices.services/icons/deploy.png
new file mode 100644
index 0000000..c2d878d
--- /dev/null
+++ b/src/plugins/devices.services/icons/deploy.png
Binary files differ
diff --git a/src/plugins/devices.services/icons/emulator_console.png b/src/plugins/devices.services/icons/emulator_console.png
new file mode 100644
index 0000000..972640b
--- /dev/null
+++ b/src/plugins/devices.services/icons/emulator_console.png
Binary files differ
diff --git a/src/plugins/devices.services/icons/monkey_16.png b/src/plugins/devices.services/icons/monkey_16.png
new file mode 100644
index 0000000..9afbe36
--- /dev/null
+++ b/src/plugins/devices.services/icons/monkey_16.png
Binary files differ
diff --git a/src/plugins/devices.services/icons/screenshot_16.jpg b/src/plugins/devices.services/icons/screenshot_16.jpg
new file mode 100644
index 0000000..624d0c7
--- /dev/null
+++ b/src/plugins/devices.services/icons/screenshot_16.jpg
Binary files differ
diff --git a/src/plugins/devices.services/icons/uninstall.png b/src/plugins/devices.services/icons/uninstall.png
new file mode 100644
index 0000000..b11d0ab
--- /dev/null
+++ b/src/plugins/devices.services/icons/uninstall.png
Binary files differ
diff --git a/src/plugins/devices.services/plugin.properties b/src/plugins/devices.services/plugin.properties
new file mode 100644
index 0000000..85ac43b
--- /dev/null
+++ b/src/plugins/devices.services/plugin.properties
@@ -0,0 +1,24 @@
+pluginName=MOTODEV Studio for Android Console Services for Android Devices Plug-in
+providerName=Motorola Mobility, Inc.
+copyright=Copyright (C) 2012 The Android Open Source Project
+
+adbShellName=ADB Shell
+adbShellDescription=Opens an ADB Shell for an Android device
+
+emulatorConsoleName=Console
+emulatorConsoleDescription=Opens an Android Emulator Console
+
+deployServiceName=Install App
+deployDescription=Service to install an application on an Android Device
+
+uninstallAppServiceName = Uninstall App
+uninstallAppServiceDescription = Service to uninstall an application from an Android Device
+
+langServiceName=Language
+langServiceDescription=Service to change the language of an Android Device
+
+monkeyServiceName=Monkey
+monkeyDescription=Test events with Monkey
+
+takeScreenshotServiceName=Take Screenshot
+takeScreenshotDescription=Take a screenshot from the device \ No newline at end of file
diff --git a/src/plugins/devices.services/plugin.xml b/src/plugins/devices.services/plugin.xml
new file mode 100644
index 0000000..1efdbe0
--- /dev/null
+++ b/src/plugins/devices.services/plugin.xml
@@ -0,0 +1,497 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?eclipse version="3.4"?>
+<plugin>
+<!--START adb shell and console services START-->
+ <extension
+ id="com.motorola.studio.android.devices.services.adbShell"
+ name="%adbShellName"
+ point="org.eclipse.sequoyah.device.framework.service">
+ <service
+ description="%adbShellDescription"
+ handler="com.motorola.studio.android.devices.services.console.ADBShellHandler"
+ icon="icons/adb_shell.png"
+ id="com.motorola.studio.android.devices.services.adbShell"
+ name="%adbShellName"
+ parallelized="true"
+ provider="%providerName"
+ version="0.4.0"
+ visible="true">
+ </service>
+ </extension>
+<extension
+ id="com.motorola.studio.android.devices.services.emulatorConsole"
+ name="%emulatorConsoleName"
+ point="org.eclipse.sequoyah.device.framework.service">
+ <service
+ description="%emulatorConsoleDescription"
+ handler="com.motorola.studio.android.devices.services.console.EmulatorConsoleHandler"
+ icon="icons/emulator_console.png"
+ id="com.motorola.studio.android.devices.services.emulatorConsole"
+ name="%emulatorConsoleName"
+ parallelized="true"
+ provider="%providerName"
+ version="0.4.0"
+ visible="true">
+ </service>
+ </extension>
+ <extension
+ id="com.motorola.studio.android.emulator.androidDevice"
+ name="%adbShellName"
+ point="org.eclipse.sequoyah.device.framework.serviceDefinition">
+ <service
+ id="com.motorola.studio.android.devices.services.adbShell">
+ <status
+ endId="com.motorola.studio.android.emulator.status.online"
+ haltId="com.motorola.studio.android.emulator.status.online"
+ startId="com.motorola.studio.android.emulator.status.online">
+ </status></service>
+ </extension>
+ <extension
+ id="com.motorola.studio.android.handset.androidHandset"
+ name="%adbShellName"
+ point="org.eclipse.sequoyah.device.framework.serviceDefinition">
+ <service
+ id="com.motorola.studio.android.devices.services.adbShell">
+ <status
+ endId="com.motorola.studio.android.handset.status.handsetonline"
+ haltId="com.motorola.studio.android.handset.status.handsetonline"
+ startId="com.motorola.studio.android.handset.status.handsetonline">
+ </status></service>
+ </extension>
+ <extension
+ id="com.motorola.studio.android.emulator.androidDevice"
+ name="%emulatorConsoleName"
+ point="org.eclipse.sequoyah.device.framework.serviceDefinition">
+ <service
+ id="com.motorola.studio.android.devices.services.emulatorConsole">
+ <status
+ endId="com.motorola.studio.android.emulator.status.online"
+ haltId="com.motorola.studio.android.emulator.status.online"
+ startId="com.motorola.studio.android.emulator.status.online">
+ </status>
+ </service>
+ </extension>
+ <extension
+ id="com.motorola.studio.android.remote.androidRemoteDevice"
+ name="%adbShellName"
+ point="org.eclipse.sequoyah.device.framework.serviceDefinition">
+ <service
+ id="com.motorola.studio.android.devices.services.adbShell">
+ <status
+ endId="com.motorola.studio.android.remote.status.connected"
+ haltId="com.motorola.studio.android.remote.status.connected"
+ startId="com.motorola.studio.android.remote.status.connected">
+ </status></service>
+ </extension>
+ <extension
+ point="org.eclipse.ui.commands">
+ <command
+ defaultHandler="com.motorola.studio.android.devices.services.console.ADBShellCommand"
+ id="com.motorola.studio.android.device.adb.shell"
+ name="%adbShellName">
+ </command>
+ <command
+ defaultHandler="com.motorola.studio.android.devices.services.console.EmulatorConsoleCommand"
+ id="com.motorola.studio.android.device.emulator.console"
+ name="%emulatorConsoleName">
+ </command>
+ </extension>
+ <extension
+ point="org.eclipse.ui.menus">
+ <menuContribution
+ locationURI="popup:com.motorola.studio.android.emulator.view.popup">
+ <command
+ commandId="com.motorola.studio.android.device.adb.shell"
+ icon="icons/adb_shell.png"
+ id="adb.shell"
+ label="%adbShellName"
+ style="push"
+ tooltip="%adbShellDescription">
+ </command>
+ <command
+ commandId="com.motorola.studio.android.device.emulator.console"
+ icon="icons/emulator_console.png"
+ id="emulator.console"
+ label="%emulatorConsoleName"
+ style="push"
+ tooltip="%emulatorConsoleDescription">
+ </command>
+ </menuContribution>
+ </extension>
+
+ <!--END adb shell and console services END-->
+ <!--START deploy and uninstall services START-->
+
+ <extension
+ id="deployService"
+ name="%deployServiceName"
+ point="org.eclipse.sequoyah.device.framework.service">
+ <service
+ copyright="%copyright"
+ description="%deployDescription"
+ handler="com.motorola.studio.android.devices.services.deploy.DeployServiceHandler"
+ icon="icons/deploy.png"
+ id="com.motorola.studio.android.devices.services.deployService"
+ interval="1000"
+ name="%deployServiceName"
+ parallelized="true"
+ provider="%providerName"
+ version="0.1.0"
+ visible="true">
+ </service>
+ </extension>
+
+ <extension
+ id="uninstallAppService"
+ name="%uninstallAppServiceName"
+ point="org.eclipse.sequoyah.device.framework.service">
+ <service
+ copyright="%copyright"
+ description="%uninstallAppServiceDescription"
+ handler="com.motorola.studio.android.devices.services.deploy.UninstallAppServiceHandler"
+ icon="icons/uninstall.png"
+ id="com.motorola.studio.android.devices.services.uninstallAppService"
+ name="%uninstallAppServiceName"
+ parallelized="false"
+ provider="%providerName"
+ version="0.1.0"
+ visible="true">
+ </service>
+ </extension>
+ <extension
+ id="com.motorola.studio.android.emulator.androidDevice"
+ name="%deployServiceName"
+ point="org.eclipse.sequoyah.device.framework.serviceDefinition">
+ <service
+ id="com.motorola.studio.android.devices.services.deployService">
+ <status
+ endId="com.motorola.studio.android.emulator.status.online"
+ haltId="com.motorola.studio.android.emulator.status.online"
+ startId="com.motorola.studio.android.emulator.status.online">
+ </status></service>
+
+ </extension>
+
+ <extension
+ id="com.motorola.studio.android.emulator.androidDevice"
+ name="%uninstallAppServiceName"
+ point="org.eclipse.sequoyah.device.framework.serviceDefinition">
+ <service
+ id="com.motorola.studio.android.devices.services.uninstallAppService">
+ <status
+ endId="com.motorola.studio.android.emulator.status.online"
+ haltId="com.motorola.studio.android.emulator.status.online"
+ startId="com.motorola.studio.android.emulator.status.online">
+ </status></service>
+ </extension>
+
+ <extension
+ id="com.motorola.studio.android.handset.androidHandset"
+ name="%deployServiceName"
+ point="org.eclipse.sequoyah.device.framework.serviceDefinition">
+ <service
+ id="com.motorola.studio.android.devices.services.deployService">
+ <status
+ endId="com.motorola.studio.android.handset.status.handsetonline"
+ haltId="com.motorola.studio.android.handset.status.handsetonline"
+ startId="com.motorola.studio.android.handset.status.handsetonline">
+ </status>
+ </service>
+ </extension>
+
+ <extension
+ id="com.motorola.studio.android.handset.androidHandset"
+ name="%uninstallAppServiceName"
+ point="org.eclipse.sequoyah.device.framework.serviceDefinition">
+ <service
+ id="com.motorola.studio.android.devices.services.uninstallAppService">
+ <status
+ endId="com.motorola.studio.android.handset.status.handsetonline"
+ haltId="com.motorola.studio.android.handset.status.handsetonline"
+ startId="com.motorola.studio.android.handset.status.handsetonline">
+ </status></service>
+ </extension>
+ <extension
+ id="com.motorola.studio.android.remote.androidRemoteDevice"
+ name="%deployServiceName"
+ point="org.eclipse.sequoyah.device.framework.serviceDefinition">
+ <service
+ id="com.motorola.studio.android.devices.services.deployService">
+ <status
+ endId="com.motorola.studio.android.remote.status.connected"
+ haltId="com.motorola.studio.android.remote.status.connected"
+ startId="com.motorola.studio.android.remote.status.connected">
+ </status>
+ </service>
+ </extension>
+
+ <extension
+ id="com.motorola.studio.android.remote.androidRemoteDevice"
+ name="%uninstallAppServiceName"
+ point="org.eclipse.sequoyah.device.framework.serviceDefinition">
+ <service
+ id="com.motorola.studio.android.devices.services.uninstallAppService">
+ <status
+ endId="com.motorola.studio.android.remote.status.connected"
+ haltId="com.motorola.studio.android.remote.status.connected"
+ startId="com.motorola.studio.android.remote.status.connected">
+ </status></service>
+ </extension>
+
+ <extension
+ point="org.eclipse.ui.commands">
+ <command
+ defaultHandler="com.motorola.studio.android.devices.services.deploy.DeployServiceCommand"
+ id="com.motorola.studio.android.device.install.app"
+ name="%deployServiceName">
+ </command>
+ <command
+ defaultHandler="com.motorola.studio.android.devices.services.deploy.UninstallAppServiceCommand"
+ id="com.motorola.studio.android.device.uninstall.app"
+ name="%uninstallAppServiceName">
+ </command>
+ </extension>
+ <extension
+ point="org.eclipse.ui.menus">
+ <menuContribution
+ locationURI="popup:com.motorola.studio.android.emulator.view.popup">
+ <command
+ commandId="com.motorola.studio.android.device.install.app"
+ icon="icons/deploy.png"
+ id="deploy.command"
+ label="%deployServiceName"
+ style="push">
+ </command>
+ <command
+ commandId="com.motorola.studio.android.device.uninstall.app"
+ icon="icons/uninstall.png"
+ id="uninstall.command"
+ label="%uninstallAppServiceName"
+ style="push">
+ </command>
+ </menuContribution>
+ </extension>
+ <extension
+ point="org.eclipse.core.expressions.propertyTesters">
+ <propertyTester
+ class="com.motorola.studio.android.devices.services.deploy.EmulatorTester"
+ id="hasActiveInstanceTester"
+ namespace="com.motorola.studio.android.emulator"
+ properties="hasActiveInstance"
+ type="java.lang.Object">
+ </propertyTester>
+ </extension>
+ <!--END deploy and uninstall services END-->
+ <!--START monkey and screenshot services START-->
+
+ <extension
+ id="takescreenshot"
+ name="%takeScreenshotServiceName"
+ point="org.eclipse.sequoyah.device.framework.service">
+ <service
+ copyright="%copyright"
+ description="%takeScreenshotDescription"
+ handler="com.motorola.studio.android.devices.services.ddms.ScreenshotServiceHandler"
+ icon="icons/screenshot_16.jpg"
+ id="com.motorola.studio.android.devices.services.takescreenshot"
+ name="%takeScreenshotServiceName"
+ parallelized="true"
+ provider="%providerName"
+ version="0.1.0"
+ visible="true">
+ </service>
+ </extension>
+ <extension
+ id="monkey"
+ name="%monkeyServiceName"
+ point="org.eclipse.sequoyah.device.framework.service">
+ <service
+ copyright="%copyright"
+ description="%monkeyDescription"
+ handler="com.motorola.studio.android.devices.services.ddms.MonkeyServiceHandler"
+ icon="icons/monkey_16.png"
+ id="com.motorola.studio.android.devices.services.monkey"
+ name="%monkeyDescription"
+ parallelized="false"
+ provider="%providerName"
+ version="0.1.0"
+ visible="true">
+ </service>
+ </extension>
+<extension
+ id="com.motorola.studio.android.emulator.androidDevice"
+ name="%takeScreenshotServiceName"
+ point="org.eclipse.sequoyah.device.framework.serviceDefinition">
+ <service
+ id="com.motorola.studio.android.devices.services.takescreenshot">
+ <status
+ endId="com.motorola.studio.android.emulator.status.online"
+ haltId="com.motorola.studio.android.emulator.status.online"
+ startId="com.motorola.studio.android.emulator.status.online">
+ </status>
+ </service>
+ </extension>
+ <extension
+ id="com.motorola.studio.android.emulator.androidDevice"
+ name="%monkeyServiceName"
+ point="org.eclipse.sequoyah.device.framework.serviceDefinition">
+ <service
+ id="com.motorola.studio.android.devices.services.monkey">
+ <status
+ endId="com.motorola.studio.android.emulator.status.online"
+ haltId="com.motorola.studio.android.emulator.status.online"
+ startId="com.motorola.studio.android.emulator.status.online">
+ </status>
+ </service>
+ </extension>
+
+ <extension
+ id="com.motorola.studio.android.handset.androidHandset"
+ name="%takeScreenshotServiceName"
+ point="org.eclipse.sequoyah.device.framework.serviceDefinition">
+ <service
+ id="com.motorola.studio.android.devices.services.takescreenshot">
+ <status
+ endId="com.motorola.studio.android.handset.status.handsetonline"
+ haltId="com.motorola.studio.android.handset.status.handsetonline"
+ startId="com.motorola.studio.android.handset.status.handsetonline">
+ </status>
+ </service>
+ </extension>
+ <extension
+ id="com.motorola.studio.android.handset.androidHandset"
+ name="%monkeyServiceName"
+ point="org.eclipse.sequoyah.device.framework.serviceDefinition">
+ <service
+ id="com.motorola.studio.android.devices.services.monkey">
+ <status
+ endId="com.motorola.studio.android.handset.status.handsetonline"
+ haltId="com.motorola.studio.android.handset.status.handsetonline"
+ startId="com.motorola.studio.android.handset.status.handsetonline">
+ </status>
+ </service>
+ </extension>
+ <extension
+ id="com.motorola.studio.android.remote.androidRemoteDevice"
+ name="%takeScreenshotServiceName"
+ point="org.eclipse.sequoyah.device.framework.serviceDefinition">
+ <service
+ id="com.motorola.studio.android.devices.services.takescreenshot">
+ <status
+ endId="com.motorola.studio.android.remote.status.connected"
+ haltId="com.motorola.studio.android.remote.status.connected"
+ startId="com.motorola.studio.android.remote.status.connected">
+ </status>
+ </service>
+ </extension>
+ <extension
+ id="com.motorola.studio.android.remote.androidRemoteDevice"
+ name="%monkeyServiceName"
+ point="org.eclipse.sequoyah.device.framework.serviceDefinition">
+ <service
+ id="com.motorola.studio.android.devices.services.monkey">
+ <status
+ endId="com.motorola.studio.android.remote.status.connected"
+ haltId="com.motorola.studio.android.remote.status.connected"
+ startId="com.motorola.studio.android.remote.status.connected">
+ </status>
+ </service>
+ </extension>
+ <extension
+ point="org.eclipse.ui.commands">
+ <command
+ defaultHandler="com.motorola.studio.android.devices.services.ddms.ScreenshotServiceCommand"
+ id="com.motorola.studio.android.device.take.screenshot"
+ name="%takeScreenshotServiceName">
+ </command>
+ <command
+ defaultHandler="com.motorola.studio.android.devices.services.ddms.MonkeyServiceCommand"
+ id="com.motorola.studio.android.device.monkey"
+ name="%monkeyDescription">
+ </command>
+ </extension>
+ <extension
+ point="org.eclipse.ui.menus">
+ <menuContribution
+ locationURI="popup:com.motorola.studio.android.emulator.view.popup">
+ <command
+ commandId="com.motorola.studio.android.device.take.screenshot"
+ icon="icons/screenshot_16.jpg"
+ id="takescreenshot.command"
+ style="push">
+ </command>
+ <command
+ commandId="com.motorola.studio.android.device.monkey"
+ icon="icons/monkey_16.png"
+ id="monkey.command"
+ style="push">
+ </command>
+ </menuContribution>
+ </extension>
+
+ <!--END monkey and screenshot services END-->
+ <!--START language services START-->
+
+ <extension
+ id="changeLanguageService"
+ name="%langServiceName"
+ point="org.eclipse.sequoyah.device.framework.service">
+ <service
+ copyright="%copyright"
+ description="%langServiceDescription"
+ handler="com.motorola.studio.android.devices.services.lang.LangServiceHandler"
+ icon="resources/flag_small.png"
+ id="com.motorola.studio.android.devices.services.changeLanguageService"
+ name="%langServiceName"
+ parallelized="true"
+ provider="%providerName"
+ version="0.1.0"
+ visible="true">
+ </service>
+ </extension>
+ <extension
+ id="com.motorola.studio.android.emulator.androidDevice"
+ name="%langServiceName"
+ point="org.eclipse.sequoyah.device.framework.serviceDefinition">
+ <service
+ id="com.motorola.studio.android.devices.services.changeLanguageService">
+ <status
+ endId="com.motorola.studio.android.emulator.status.online"
+ haltId="com.motorola.studio.android.emulator.status.online"
+ startId="com.motorola.studio.android.emulator.status.online">
+ </status></service>
+ </extension>
+ <!-- <extension
+ id="com.motorola.studio.android.handset.androidHandset"
+ name="%deployServiceName"
+ point="org.eclipse.sequoyah.device.framework.serviceDefinition">
+ <service
+ id="com.motorola.studio.android.service.deploy.deployService">
+ <status
+ endId="com.motorola.studio.android.handset.status.handsetonline"
+ haltId="com.motorola.studio.android.handset.status.handsetonline"
+ startId="com.motorola.studio.android.handset.status.handsetonline">
+ </status></service>
+ </extension> -->
+ <extension
+ point="org.eclipse.ui.commands">
+ <command
+ defaultHandler="com.motorola.studio.android.devices.services.lang.LangServiceCommand"
+ id="com.motorola.studio.android.device.change.emulator.language"
+ name="%langServiceName">
+ </command>
+ </extension>
+ <extension
+ point="org.eclipse.ui.menus">
+ <menuContribution
+ locationURI="popup:com.motorola.studio.android.emulator.view.popup">
+ <command
+ commandId="com.motorola.studio.android.device.change.emulator.language"
+ icon="resources/flag_small.png"
+ id="lang.command"
+ style="push">
+ </command>
+ </menuContribution>
+ </extension>
+ <!--END language services END-->
+</plugin>
diff --git a/src/plugins/devices.services/resources/ISO-639-2_utf-8.txt b/src/plugins/devices.services/resources/ISO-639-2_utf-8.txt
new file mode 100644
index 0000000..e605d25
--- /dev/null
+++ b/src/plugins/devices.services/resources/ISO-639-2_utf-8.txt
@@ -0,0 +1,485 @@
+aar||aa|Afar|afar
+abk||ab|Abkhazian|abkhaze
+ace|||Achinese|aceh
+ach|||Acoli|acoli
+ada|||Adangme|adangme
+ady|||Adyghe; Adygei|adyghé
+afa|||Afro-Asiatic languages|afro-asiatiques, langues
+afh|||Afrihili|afrihili
+afr||af|Afrikaans|afrikaans
+ain|||Ainu|aïnou
+aka||ak|Akan|akan
+akk|||Akkadian|akkadien
+alb|sqi|sq|Albanian|albanais
+ale|||Aleut|aléoute
+alg|||Algonquian languages|algonquines, langues
+alt|||Southern Altai|altai du Sud
+amh||am|Amharic|amharique
+ang|||English, Old (ca.450-1100)|anglo-saxon (ca.450-1100)
+anp|||Angika|angika
+apa|||Apache languages|apaches, langues
+ara||ar|Arabic|arabe
+arc|||Official Aramaic (700-300 BCE); Imperial Aramaic (700-300 BCE)|araméen d'empire (700-300 BCE)
+arg||an|Aragonese|aragonais
+arm|hye|hy|Armenian|arménien
+arn|||Mapudungun; Mapuche|mapudungun; mapuche; mapuce
+arp|||Arapaho|arapaho
+art|||Artificial languages|artificielles, langues
+arw|||Arawak|arawak
+asm||as|Assamese|assamais
+ast|||Asturian; Bable; Leonese; Asturleonese|asturien; bable; léonais; asturoléonais
+ath|||Athapascan languages|athapascanes, langues
+aus|||Australian languages|australiennes, langues
+ava||av|Avaric|avar
+ave||ae|Avestan|avestique
+awa|||Awadhi|awadhi
+aym||ay|Aymara|aymara
+aze||az|Azerbaijani|azéri
+bad|||Banda languages|banda, langues
+bai|||Bamileke languages|bamiléké, langues
+bak||ba|Bashkir|bachkir
+bal|||Baluchi|baloutchi
+bam||bm|Bambara|bambara
+ban|||Balinese|balinais
+baq|eus|eu|Basque|basque
+bas|||Basa|basa
+bat|||Baltic languages|baltes, langues
+bej|||Beja; Bedawiyet|bedja
+bel||be|Belarusian|biélorusse
+bem|||Bemba|bemba
+ben||bn|Bengali|bengali
+ber|||Berber languages|berbères, langues
+bho|||Bhojpuri|bhojpuri
+bih||bh|Bihari|bihari
+bik|||Bikol|bikol
+bin|||Bini; Edo|bini; edo
+bis||bi|Bislama|bichlamar
+bla|||Siksika|blackfoot
+bnt|||Bantu (Other)|bantoues, autres langues
+bos||bs|Bosnian|bosniaque
+bra|||Braj|braj
+bre||br|Breton|breton
+btk|||Batak languages|batak, langues
+bua|||Buriat|bouriate
+bug|||Buginese|bugi
+bul||bg|Bulgarian|bulgare
+bur|mya|my|Burmese|birman
+byn|||Blin; Bilin|blin; bilen
+cad|||Caddo|caddo
+cai|||Central American Indian languages|amérindiennes de L'Amérique centrale, langues
+car|||Galibi Carib|karib; galibi; carib
+cat||ca|Catalan; Valencian|catalan; valencien
+cau|||Caucasian languages|caucasiennes, langues
+ceb|||Cebuano|cebuano
+cel|||Celtic languages|celtiques, langues; celtes, langues
+cha||ch|Chamorro|chamorro
+chb|||Chibcha|chibcha
+che||ce|Chechen|tchétchène
+chg|||Chagatai|djaghataï
+chi|zho|zh|Chinese|chinois
+chk|||Chuukese|chuuk
+chm|||Mari|mari
+chn|||Chinook jargon|chinook, jargon
+cho|||Choctaw|choctaw
+chp|||Chipewyan; Dene Suline|chipewyan
+chr|||Cherokee|cherokee
+chu||cu|Church Slavic; Old Slavonic; Church Slavonic; Old Bulgarian|slavon d'église; vieux slave; slavon liturgique; vieux bulgare
+chv||cv|Chuvash|tchouvache
+chy|||Cheyenne|cheyenne
+cmc|||Chamic languages|chames, langues
+cop|||Coptic|copte
+cor||kw|Cornish|cornique
+cos||co|Corsican|corse
+cpe|||Creoles and pidgins, English based|créoles et pidgins basés sur l'anglais
+cpf|||Creoles and pidgins, French-based |créoles et pidgins basés sur le français
+cpp|||Creoles and pidgins, Portuguese-based |créoles et pidgins basés sur le portugais
+cre||cr|Cree|cree
+crh|||Crimean Tatar; Crimean Turkish|tatar de Crimé
+crp|||Creoles and pidgins |créoles et pidgins
+csb|||Kashubian|kachoube
+cus|||Cushitic languages|couchitiques, langues
+cze|ces|cs|Czech|tchèque
+dak|||Dakota|dakota
+dan||da|Danish|danois
+dar|||Dargwa|dargwa
+day|||Land Dayak languages|dayak, langues
+del|||Delaware|delaware
+den|||Slave (Athapascan)|esclave (athapascan)
+dgr|||Dogrib|dogrib
+din|||Dinka|dinka
+div||dv|Divehi; Dhivehi; Maldivian|maldivien
+doi|||Dogri|dogri
+dra|||Dravidian languages|dravidiennes, langues
+dsb|||Lower Sorbian|bas-sorabe
+dua|||Duala|douala
+dum|||Dutch, Middle (ca.1050-1350)|néerlandais moyen (ca. 1050-1350)
+dut|nld|nl|Dutch; Flemish|néerlandais; flamand
+dyu|||Dyula|dioula
+dzo||dz|Dzongkha|dzongkha
+efi|||Efik|efik
+egy|||Egyptian (Ancient)|égyptien
+eka|||Ekajuk|ekajuk
+elx|||Elamite|élamite
+eng||en|English|anglais
+enm|||English, Middle (1100-1500)|anglais moyen (1100-1500)
+epo||eo|Esperanto|espéranto
+est||et|Estonian|estonien
+ewe||ee|Ewe|éwé
+ewo|||Ewondo|éwondo
+fan|||Fang|fang
+fao||fo|Faroese|féroïen
+fat|||Fanti|fanti
+fij||fj|Fijian|fidjien
+fil|||Filipino; Pilipino|filipino; pilipino
+fin||fi|Finnish|finnois
+fiu|||Finno-Ugrian languages|finno-ougriennes, langues
+fon|||Fon|fon
+fre|fra|fr|French|français
+frm|||French, Middle (ca.1400-1600)|français moyen (1400-1600)
+fro|||French, Old (842-ca.1400)|français ancien (842-ca.1400)
+frr|||Northern Frisian|frison septentrional
+frs|||Eastern Frisian|frison oriental
+fry||fy|Western Frisian|frison occidental
+ful||ff|Fulah|peul
+fur|||Friulian|frioulan
+gaa|||Ga|ga
+gay|||Gayo|gayo
+gba|||Gbaya|gbaya
+gem|||Germanic languages|germaniques, langues
+geo|kat|ka|Georgian|géorgien
+ger|deu|de|German|allemand
+gez|||Geez|guèze
+gil|||Gilbertese|kiribati
+gla||gd|Gaelic; Scottish Gaelic|gaélique; gaélique écossais
+gle||ga|Irish|irlandais
+glg||gl|Galician|galicien
+glv||gv|Manx|manx; mannois
+gmh|||German, Middle High (ca.1050-1500)|allemand, moyen haut (ca. 1050-1500)
+goh|||German, Old High (ca.750-1050)|allemand, vieux haut (ca. 750-1050)
+gon|||Gondi|gond
+gor|||Gorontalo|gorontalo
+got|||Gothic|gothique
+grb|||Grebo|grebo
+grc|||Greek, Ancient (to 1453)|grec ancien (jusqu'à 1453)
+gre|ell|el|Greek, Modern (1453-)|grec moderne (après 1453)
+grn||gn|Guarani|guarani
+gsw|||Swiss German; Alemannic; Alsatian|suisse alémanique; alémanique; alsacien
+guj||gu|Gujarati|goudjrati
+gwi|||Gwich'in|gwich'in
+hai|||Haida|haida
+hat||ht|Haitian; Haitian Creole|haïtien; créole haïtien
+hau||ha|Hausa|haoussa
+haw|||Hawaiian|hawaïen
+heb||he|Hebrew|hébreu
+her||hz|Herero|herero
+hil|||Hiligaynon|hiligaynon
+him|||Himachali|himachali
+hin||hi|Hindi|hindi
+hit|||Hittite|hittite
+hmn|||Hmong|hmong
+hmo||ho|Hiri Motu|hiri motu
+hrv||hr|Croatian|croate
+hsb|||Upper Sorbian|haut-sorabe
+hun||hu|Hungarian|hongrois
+hup|||Hupa|hupa
+iba|||Iban|iban
+ibo||ig|Igbo|igbo
+ice|isl|is|Icelandic|islandais
+ido||io|Ido|ido
+iii||ii|Sichuan Yi; Nuosu|yi de Sichuan
+ijo|||Ijo languages|ijo, langues
+iku||iu|Inuktitut|inuktitut
+ile||ie|Interlingue; Occidental|interlingue
+ilo|||Iloko|ilocano
+ina||ia|Interlingua (International Auxiliary Language Association)|interlingua (langue auxiliaire internationale)
+inc|||Indic languages|indo-aryennes, langues
+ind||id|Indonesian|indonésien
+ine|||Indo-European languages|indo-européennes, langues
+inh|||Ingush|ingouche
+ipk||ik|Inupiaq|inupiaq
+ira|||Iranian languages|iraniennes, langues
+iro|||Iroquoian languages|iroquoises, langues
+ita||it|Italian|italien
+jav||jv|Javanese|javanais
+jbo|||Lojban|lojban
+jpn||ja|Japanese|japonais
+jpr|||Judeo-Persian|judéo-persan
+jrb|||Judeo-Arabic|judéo-arabe
+kaa|||Kara-Kalpak|karakalpak
+kab|||Kabyle|kabyle
+kac|||Kachin; Jingpho|kachin; jingpho
+kal||kl|Kalaallisut; Greenlandic|groenlandais
+kam|||Kamba|kamba
+kan||kn|Kannada|kannada
+kar|||Karen languages|karen, langues
+kas||ks|Kashmiri|kashmiri
+kau||kr|Kanuri|kanouri
+kaw|||Kawi|kawi
+kaz||kk|Kazakh|kazakh
+kbd|||Kabardian|kabardien
+kha|||Khasi|khasi
+khi|||Khoisan languages|khoïsan, langues
+khm||km|Central Khmer|khmer central
+kho|||Khotanese; Sakan|khotanais; sakan
+kik||ki|Kikuyu; Gikuyu|kikuyu
+kin||rw|Kinyarwanda|rwanda
+kir||ky|Kirghiz; Kyrgyz|kirghiz
+kmb|||Kimbundu|kimbundu
+kok|||Konkani|konkani
+kom||kv|Komi|kom
+kon||kg|Kongo|kongo
+kor||ko|Korean|coréen
+kos|||Kosraean|kosrae
+kpe|||Kpelle|kpellé
+krc|||Karachay-Balkar|karatchai balkar
+krl|||Karelian|carélien
+kro|||Kru languages|krou, langues
+kru|||Kurukh|kurukh
+kua||kj|Kuanyama; Kwanyama|kuanyama; kwanyama
+kum|||Kumyk|koumyk
+kur||ku|Kurdish|kurde
+kut|||Kutenai|kutenai
+lad|||Ladino|judéo-espagnol
+lah|||Lahnda|lahnda
+lam|||Lamba|lamba
+lao||lo|Lao|lao
+lat||la|Latin|latin
+lav||lv|Latvian|letton
+lez|||Lezghian|lezghien
+lim||li|Limburgan; Limburger; Limburgish|limbourgeois
+lin||ln|Lingala|lingala
+lit||lt|Lithuanian|lituanien
+lol|||Mongo|mongo
+loz|||Lozi|lozi
+ltz||lb|Luxembourgish; Letzeburgesch|luxembourgeois
+lua|||Luba-Lulua|luba-lulua
+lub||lu|Luba-Katanga|luba-katanga
+lug||lg|Ganda|ganda
+lui|||Luiseno|luiseno
+lun|||Lunda|lunda
+luo|||Luo (Kenya and Tanzania)|luo (Kenya et Tanzanie)
+lus|||Lushai|lushai
+mac|mkd|mk|Macedonian|macédonien
+mad|||Madurese|madourais
+mag|||Magahi|magahi
+mah||mh|Marshallese|marshall
+mai|||Maithili|maithili
+mak|||Makasar|makassar
+mal||ml|Malayalam|malayalam
+man|||Mandingo|mandingue
+mao|mri|mi|Maori|maori
+map|||Austronesian languages|austronésiennes, langues
+mar||mr|Marathi|marathe
+mas|||Masai|massaï
+may|msa|ms|Malay|malais
+mdf|||Moksha|moksa
+mdr|||Mandar|mandar
+men|||Mende|mendé
+mga|||Irish, Middle (900-1200)|irlandais moyen (900-1200)
+mic|||Mi'kmaq; Micmac|mi'kmaq; micmac
+min|||Minangkabau|minangkabau
+mis|||Uncoded languages|langues non codées
+mkh|||Mon-Khmer languages|môn-khmer, langues
+mlg||mg|Malagasy|malgache
+mlt||mt|Maltese|maltais
+mnc|||Manchu|mandchou
+mni|||Manipuri|manipuri
+mno|||Manobo languages|manobo, langues
+moh|||Mohawk|mohawk
+mon||mn|Mongolian|mongol
+mos|||Mossi|moré
+mul|||Multiple languages|multilingue
+mun|||Munda languages|mounda, langues
+mus|||Creek|muskogee
+mwl|||Mirandese|mirandais
+mwr|||Marwari|marvari
+myn|||Mayan languages|maya, langues
+myv|||Erzya|erza
+nah|||Nahuatl languages|nahuatl, langues
+nai|||North American Indian languages|nord-amérindiennes, langues
+nap|||Neapolitan|napolitain
+nau||na|Nauru|nauruan
+nav||nv|Navajo; Navaho|navaho
+nbl||nr|Ndebele, South; South Ndebele|ndébélé du Sud
+nde||nd|Ndebele, North; North Ndebele|ndébélé du Nord
+ndo||ng|Ndonga|ndonga
+nds|||Low German; Low Saxon; German, Low; Saxon, Low|bas allemand; bas saxon; allemand, bas; saxon, bas
+nep||ne|Nepali|népalais
+new|||Nepal Bhasa; Newari|nepal bhasa; newari
+nia|||Nias|nias
+nic|||Niger-Kordofanian languages|nigéro-kordofaniennes, langues
+niu|||Niuean|niué
+nno||nn|Norwegian Nynorsk; Nynorsk, Norwegian|norvégien nynorsk; nynorsk, norvégien
+nob||nb|Bokmål, Norwegian; Norwegian Bokmål|norvégien bokmål
+nog|||Nogai|nogaï; nogay
+non|||Norse, Old|norrois, vieux
+nor||no|Norwegian|norvégien
+nqo|||N'Ko|n'ko
+nso|||Pedi; Sepedi; Northern Sotho|pedi; sepedi; sotho du Nord
+nub|||Nubian languages|nubiennes, langues
+nwc|||Classical Newari; Old Newari; Classical Nepal Bhasa|newari classique
+nya||ny|Chichewa; Chewa; Nyanja|chichewa; chewa; nyanja
+nym|||Nyamwezi|nyamwezi
+nyn|||Nyankole|nyankolé
+nyo|||Nyoro|nyoro
+nzi|||Nzima|nzema
+oci||oc|Occitan (post 1500); Provençal|occitan (après 1500); provençal
+oji||oj|Ojibwa|ojibwa
+ori||or|Oriya|oriya
+orm||om|Oromo|galla
+osa|||Osage|osage
+oss||os|Ossetian; Ossetic|ossète
+ota|||Turkish, Ottoman (1500-1928)|turc ottoman (1500-1928)
+oto|||Otomian languages|otomi, langues
+paa|||Papuan languages|papoues, langues
+pag|||Pangasinan|pangasinan
+pal|||Pahlavi|pahlavi
+pam|||Pampanga; Kapampangan|pampangan
+pan||pa|Panjabi; Punjabi|pendjabi
+pap|||Papiamento|papiamento
+pau|||Palauan|palau
+peo|||Persian, Old (ca.600-400 B.C.)|perse, vieux (ca. 600-400 av. J.-C.)
+per|fas|fa|Persian|persan
+phi|||Philippine languages|philippines, langues
+phn|||Phoenician|phénicien
+pli||pi|Pali|pali
+pol||pl|Polish|polonais
+pon|||Pohnpeian|pohnpei
+por||pt|Portuguese|portugais
+pra|||Prakrit languages|prâkrit, langues
+pro|||Provençal, Old (to 1500)|provençal ancien (jusqu'à 1500)
+pus||ps|Pushto; Pashto|pachto
+qaa-qtz|||Reserved for local use|réservée à l'usage local
+que||qu|Quechua|quechua
+raj|||Rajasthani|rajasthani
+rap|||Rapanui|rapanui
+rar|||Rarotongan; Cook Islands Maori|rarotonga; maori des îles Cook
+roa|||Romance languages|romanes, langues
+roh||rm|Romansh|romanche
+rom|||Romany|tsigane
+rum|ron|ro|Romanian; Moldavian; Moldovan|roumain; moldave
+run||rn|Rundi|rundi
+rup|||Aromanian; Arumanian; Macedo-Romanian|aroumain; macédo-roumain
+rus||ru|Russian|russe
+sad|||Sandawe|sandawe
+sag||sg|Sango|sango
+sah|||Yakut|iakoute
+sai|||South American Indian (Other)|indiennes d'Amérique du Sud, autres langues
+sal|||Salishan languages|salishennes, langues
+sam|||Samaritan Aramaic|samaritain
+san||sa|Sanskrit|sanskrit
+sas|||Sasak|sasak
+sat|||Santali|santal
+scn|||Sicilian|sicilien
+sco|||Scots|écossais
+sel|||Selkup|selkoupe
+sem|||Semitic languages|sémitiques, langues
+sga|||Irish, Old (to 900)|irlandais ancien (jusqu'à 900)
+sgn|||Sign Languages|langues des signes
+shn|||Shan|chan
+sid|||Sidamo|sidamo
+sin||si|Sinhala; Sinhalese|singhalais
+sio|||Siouan languages|sioux, langues
+sit|||Sino-Tibetan languages|sino-tibétaines, langues
+sla|||Slavic languages|slaves, langues
+slo|slk|sk|Slovak|slovaque
+slv||sl|Slovenian|slovène
+sma|||Southern Sami|sami du Sud
+sme||se|Northern Sami|sami du Nord
+smi|||Sami languages|sames, langues
+smj|||Lule Sami|sami de Lule
+smn|||Inari Sami|sami d'Inari
+smo||sm|Samoan|samoan
+sms|||Skolt Sami|sami skolt
+sna||sn|Shona|shona
+snd||sd|Sindhi|sindhi
+snk|||Soninke|soninké
+sog|||Sogdian|sogdien
+som||so|Somali|somali
+son|||Songhai languages|songhai, langues
+sot||st|Sotho, Southern|sotho du Sud
+spa||es|Spanish; Castilian|espagnol; castillan
+srd||sc|Sardinian|sarde
+srn|||Sranan Tongo|sranan tongo
+srp||sr|Serbian|serbe
+srr|||Serer|sérère
+ssa|||Nilo-Saharan languages|nilo-sahariennes, langues
+ssw||ss|Swati|swati
+suk|||Sukuma|sukuma
+sun||su|Sundanese|soundanais
+sus|||Susu|soussou
+sux|||Sumerian|sumérien
+swa||sw|Swahili|swahili
+swe||sv|Swedish|suédois
+syc|||Classical Syriac|syriaque classique
+syr|||Syriac|syriaque
+tah||ty|Tahitian|tahitien
+tai|||Tai languages|tai, langues
+tam||ta|Tamil|tamoul
+tat||tt|Tatar|tatar
+tel||te|Telugu|télougou
+tem|||Timne|temne
+ter|||Tereno|tereno
+tet|||Tetum|tetum
+tgk||tg|Tajik|tadjik
+tgl||tl|Tagalog|tagalog
+tha||th|Thai|thaï
+tib|bod|bo|Tibetan|tibétain
+tig|||Tigre|tigré
+tir||ti|Tigrinya|tigrigna
+tiv|||Tiv|tiv
+tkl|||Tokelau|tokelau
+tlh|||Klingon; tlhIngan-Hol|klingon
+tli|||Tlingit|tlingit
+tmh|||Tamashek|tamacheq
+tog|||Tonga (Nyasa)|tonga (Nyasa)
+ton||to|Tonga (Tonga Islands)|tongan (ÃŽles Tonga)
+tpi|||Tok Pisin|tok pisin
+tsi|||Tsimshian|tsimshian
+tsn||tn|Tswana|tswana
+tso||ts|Tsonga|tsonga
+tuk||tk|Turkmen|turkmène
+tum|||Tumbuka|tumbuka
+tup|||Tupi languages|tupi, langues
+tur||tr|Turkish|turc
+tut|||Altaic languages|altaïques, langues
+tvl|||Tuvalu|tuvalu
+twi||tw|Twi|twi
+tyv|||Tuvinian|touva
+udm|||Udmurt|oudmourte
+uga|||Ugaritic|ougaritique
+uig||ug|Uighur; Uyghur|ouïgour
+ukr||uk|Ukrainian|ukrainien
+umb|||Umbundu|umbundu
+und|||Undetermined|indéterminée
+urd||ur|Urdu|ourdou
+uzb||uz|Uzbek|ouszbek
+vai|||Vai|vaï
+ven||ve|Venda|venda
+vie||vi|Vietnamese|vietnamien
+vol||vo|Volapük|volapük
+vot|||Votic|vote
+wak|||Wakashan languages|wakashanes, langues
+wal|||Walamo|walamo
+war|||Waray|waray
+was|||Washo|washo
+wel|cym|cy|Welsh|gallois
+wen|||Sorbian languages|sorabes, langues
+wln||wa|Walloon|wallon
+wol||wo|Wolof|wolof
+xal|||Kalmyk; Oirat|kalmouk; oïrat
+xho||xh|Xhosa|xhosa
+yao|||Yao|yao
+yap|||Yapese|yapois
+yid||yi|Yiddish|yiddish
+yor||yo|Yoruba|yoruba
+ypk|||Yupik languages|yupik, langues
+zap|||Zapotec|zapotèque
+zbl|||Blissymbols; Blissymbolics; Bliss|symboles Bliss; Bliss
+zen|||Zenaga|zenaga
+zha||za|Zhuang; Chuang|zhuang; chuang
+znd|||Zande languages|zandé, langues
+zul||zu|Zulu|zoulou
+zun|||Zuni|zuni
+zxx|||No linguistic content; Not applicable|pas de contenu linguistique; non applicable
+zza|||Zaza; Dimili; Dimli; Kirdki; Kirmanjki; Zazaki|zaza; dimili; dimli; kirdki; kirmanjki; zazaki \ No newline at end of file
diff --git a/src/plugins/devices.services/resources/flag.png b/src/plugins/devices.services/resources/flag.png
new file mode 100644
index 0000000..7c934a9
--- /dev/null
+++ b/src/plugins/devices.services/resources/flag.png
Binary files differ
diff --git a/src/plugins/devices.services/resources/flag_small.png b/src/plugins/devices.services/resources/flag_small.png
new file mode 100644
index 0000000..23e19aa
--- /dev/null
+++ b/src/plugins/devices.services/resources/flag_small.png
Binary files differ
diff --git a/src/plugins/devices.services/resources/iso_3166-1_list_en.xml b/src/plugins/devices.services/resources/iso_3166-1_list_en.xml
new file mode 100644
index 0000000..5607ee2
--- /dev/null
+++ b/src/plugins/devices.services/resources/iso_3166-1_list_en.xml
@@ -0,0 +1,987 @@
+<?xml version="1.0" encoding="ISO-8859-1" standalone="yes"?>
+<ISO_3166-1_List_en xml:lang="en">
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>AFGHANISTAN</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>AF</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>ÅLAND ISLANDS</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>AX</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>ALBANIA</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>AL</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>ALGERIA</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>DZ</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>AMERICAN SAMOA</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>AS</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>ANDORRA</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>AD</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>ANGOLA</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>AO</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>ANGUILLA</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>AI</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>ANTARCTICA</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>AQ</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>ANTIGUA AND BARBUDA</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>AG</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>ARGENTINA</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>AR</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>ARMENIA</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>AM</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>ARUBA</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>AW</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>AUSTRALIA</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>AU</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>AUSTRIA</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>AT</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>AZERBAIJAN</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>AZ</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>BAHAMAS</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>BS</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>BAHRAIN</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>BH</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>BANGLADESH</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>BD</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>BARBADOS</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>BB</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>BELARUS</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>BY</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>BELGIUM</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>BE</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>BELIZE</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>BZ</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>BENIN</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>BJ</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>BERMUDA</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>BM</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>BHUTAN</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>BT</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>BOLIVIA, PLURINATIONAL STATE OF</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>BO</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>BOSNIA AND HERZEGOVINA</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>BA</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>BOTSWANA</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>BW</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>BOUVET ISLAND</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>BV</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>BRAZIL</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>BR</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>BRITISH INDIAN OCEAN TERRITORY</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>IO</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>BRUNEI DARUSSALAM</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>BN</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>BULGARIA</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>BG</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>BURKINA FASO</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>BF</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>BURUNDI</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>BI</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>CAMBODIA</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>KH</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>CAMEROON</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>CM</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>CANADA</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>CA</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>CAPE VERDE</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>CV</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>CAYMAN ISLANDS</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>KY</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>CENTRAL AFRICAN REPUBLIC</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>CF</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>CHAD</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>TD</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>CHILE</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>CL</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>CHINA</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>CN</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>CHRISTMAS ISLAND</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>CX</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>COCOS (KEELING) ISLANDS</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>CC</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>COLOMBIA</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>CO</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>COMOROS</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>KM</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>CONGO</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>CG</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>CONGO, THE DEMOCRATIC REPUBLIC OF THE</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>CD</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>COOK ISLANDS</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>CK</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>COSTA RICA</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>CR</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>CÔTE D'IVOIRE</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>CI</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>CROATIA</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>HR</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>CUBA</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>CU</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>CYPRUS</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>CY</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>CZECH REPUBLIC</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>CZ</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>DENMARK</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>DK</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>DJIBOUTI</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>DJ</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>DOMINICA</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>DM</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>DOMINICAN REPUBLIC</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>DO</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>ECUADOR</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>EC</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>EGYPT</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>EG</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>EL SALVADOR</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>SV</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>EQUATORIAL GUINEA</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>GQ</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>ERITREA</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>ER</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>ESTONIA</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>EE</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>ETHIOPIA</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>ET</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>FALKLAND ISLANDS (MALVINAS)</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>FK</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>FAROE ISLANDS</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>FO</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>FIJI</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>FJ</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>FINLAND</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>FI</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>FRANCE</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>FR</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>FRENCH GUIANA</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>GF</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>FRENCH POLYNESIA</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>PF</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>FRENCH SOUTHERN TERRITORIES</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>TF</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>GABON</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>GA</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>GAMBIA</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>GM</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>GEORGIA</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>GE</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>GERMANY</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>DE</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>GHANA</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>GH</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>GIBRALTAR</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>GI</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>GREECE</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>GR</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>GREENLAND</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>GL</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>GRENADA</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>GD</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>GUADELOUPE</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>GP</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>GUAM</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>GU</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>GUATEMALA</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>GT</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>GUERNSEY</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>GG</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>GUINEA</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>GN</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>GUINEA-BISSAU</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>GW</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>GUYANA</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>GY</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>HAITI</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>HT</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>HEARD ISLAND AND MCDONALD ISLANDS</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>HM</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>HOLY SEE (VATICAN CITY STATE)</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>VA</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>HONDURAS</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>HN</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>HONG KONG</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>HK</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>HUNGARY</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>HU</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>ICELAND</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>IS</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>INDIA</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>IN</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>INDONESIA</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>ID</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>IRAN, ISLAMIC REPUBLIC OF</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>IR</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>IRAQ</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>IQ</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>IRELAND</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>IE</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>ISLE OF MAN</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>IM</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>ISRAEL</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>IL</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>ITALY</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>IT</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>JAMAICA</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>JM</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>JAPAN</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>JP</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>JERSEY</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>JE</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>JORDAN</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>JO</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>KAZAKHSTAN</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>KZ</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>KENYA</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>KE</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>KIRIBATI</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>KI</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>KOREA, DEMOCRATIC PEOPLE'S REPUBLIC OF</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>KP</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>KOREA, REPUBLIC OF</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>KR</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>KUWAIT</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>KW</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>KYRGYZSTAN</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>KG</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>LAO PEOPLE'S DEMOCRATIC REPUBLIC</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>LA</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>LATVIA</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>LV</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>LEBANON</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>LB</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>LESOTHO</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>LS</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>LIBERIA</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>LR</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>LIBYAN ARAB JAMAHIRIYA</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>LY</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>LIECHTENSTEIN</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>LI</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>LITHUANIA</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>LT</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>LUXEMBOURG</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>LU</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>MACAO</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>MO</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>MACEDONIA, THE FORMER YUGOSLAV REPUBLIC OF</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>MK</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>MADAGASCAR</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>MG</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>MALAWI</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>MW</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>MALAYSIA</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>MY</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>MALDIVES</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>MV</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>MALI</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>ML</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>MALTA</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>MT</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>MARSHALL ISLANDS</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>MH</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>MARTINIQUE</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>MQ</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>MAURITANIA</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>MR</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>MAURITIUS</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>MU</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>MAYOTTE</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>YT</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>MEXICO</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>MX</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>MICRONESIA, FEDERATED STATES OF</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>FM</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>MOLDOVA, REPUBLIC OF</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>MD</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>MONACO</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>MC</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>MONGOLIA</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>MN</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>MONTENEGRO</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>ME</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>MONTSERRAT</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>MS</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>MOROCCO</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>MA</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>MOZAMBIQUE</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>MZ</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>MYANMAR</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>MM</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>NAMIBIA</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>NA</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>NAURU</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>NR</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>NEPAL</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>NP</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>NETHERLANDS</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>NL</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>NETHERLANDS ANTILLES</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>AN</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>NEW CALEDONIA</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>NC</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>NEW ZEALAND</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>NZ</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>NICARAGUA</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>NI</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>NIGER</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>NE</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>NIGERIA</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>NG</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>NIUE</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>NU</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>NORFOLK ISLAND</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>NF</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>NORTHERN MARIANA ISLANDS</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>MP</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>NORWAY</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>NO</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>OMAN</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>OM</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>PAKISTAN</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>PK</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>PALAU</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>PW</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>PALESTINIAN TERRITORY, OCCUPIED</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>PS</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>PANAMA</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>PA</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>PAPUA NEW GUINEA</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>PG</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>PARAGUAY</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>PY</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>PERU</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>PE</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>PHILIPPINES</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>PH</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>PITCAIRN</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>PN</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>POLAND</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>PL</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>PORTUGAL</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>PT</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>PUERTO RICO</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>PR</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>QATAR</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>QA</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>REUNION</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>RE</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>ROMANIA</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>RO</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>RUSSIAN FEDERATION</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>RU</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>RWANDA</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>RW</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>SAINT BARTHÉLEMY</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>BL</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>SAINT HELENA</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>SH</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>SAINT KITTS AND NEVIS</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>KN</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>SAINT LUCIA</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>LC</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>SAINT MARTIN</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>MF</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>SAINT PIERRE AND MIQUELON</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>PM</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>SAINT VINCENT AND THE GRENADINES</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>VC</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>SAMOA</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>WS</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>SAN MARINO</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>SM</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>SAO TOME AND PRINCIPE</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>ST</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>SAUDI ARABIA</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>SA</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>SENEGAL</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>SN</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>SERBIA</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>RS</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>SEYCHELLES</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>SC</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>SIERRA LEONE</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>SL</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>SINGAPORE</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>SG</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>SLOVAKIA</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>SK</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>SLOVENIA</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>SI</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>SOLOMON ISLANDS</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>SB</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>SOMALIA</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>SO</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>SOUTH AFRICA</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>ZA</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>SOUTH GEORGIA AND THE SOUTH SANDWICH ISLANDS</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>GS</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>SPAIN</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>ES</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>SRI LANKA</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>LK</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>SUDAN</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>SD</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>SURINAME</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>SR</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>SVALBARD AND JAN MAYEN</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>SJ</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>SWAZILAND</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>SZ</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>SWEDEN</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>SE</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>SWITZERLAND</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>CH</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>SYRIAN ARAB REPUBLIC</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>SY</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>TAIWAN, PROVINCE OF CHINA</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>TW</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>TAJIKISTAN</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>TJ</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>TANZANIA, UNITED REPUBLIC OF</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>TZ</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>THAILAND</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>TH</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>TIMOR-LESTE</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>TL</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>TOGO</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>TG</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>TOKELAU</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>TK</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>TONGA</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>TO</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>TRINIDAD AND TOBAGO</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>TT</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>TUNISIA</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>TN</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>TURKEY</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>TR</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>TURKMENISTAN</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>TM</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>TURKS AND CAICOS ISLANDS</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>TC</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>TUVALU</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>TV</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>UGANDA</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>UG</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>UKRAINE</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>UA</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>UNITED ARAB EMIRATES</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>AE</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>UNITED KINGDOM</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>GB</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>UNITED STATES</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>US</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>UNITED STATES MINOR OUTLYING ISLANDS</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>UM</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>URUGUAY</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>UY</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>UZBEKISTAN</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>UZ</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>VANUATU</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>VU</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>VENEZUELA, BOLIVARIAN REPUBLIC OF</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>VE</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>VIET NAM</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>VN</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>VIRGIN ISLANDS, BRITISH</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>VG</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>VIRGIN ISLANDS, U.S.</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>VI</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>WALLIS AND FUTUNA</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>WF</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>WESTERN SAHARA</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>EH</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>YEMEN</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>YE</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>ZAMBIA</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>ZM</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+ <ISO_3166-1_Entry>
+ <ISO_3166-1_Country_name>ZIMBABWE</ISO_3166-1_Country_name>
+ <ISO_3166-1_Alpha-2_Code_element>ZW</ISO_3166-1_Alpha-2_Code_element>
+ </ISO_3166-1_Entry>
+</ISO_3166-1_List_en>
diff --git a/src/plugins/devices.services/src/com/motorola/studio/android/devices/services/DeviceServicesPlugin.java b/src/plugins/devices.services/src/com/motorola/studio/android/devices/services/DeviceServicesPlugin.java
new file mode 100644
index 0000000..e568e18
--- /dev/null
+++ b/src/plugins/devices.services/src/com/motorola/studio/android/devices/services/DeviceServicesPlugin.java
@@ -0,0 +1,477 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.devices.services;
+
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.sequoyah.device.framework.factory.DeviceTypeRegistry;
+import org.eclipse.sequoyah.device.framework.model.IDeviceType;
+import org.eclipse.sequoyah.device.framework.model.IService;
+import org.eclipse.sequoyah.device.framework.model.handler.IServiceHandler;
+import org.eclipse.sequoyah.device.framework.model.handler.ServiceHandler;
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.IWorkbenchListener;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.console.ConsolePlugin;
+import org.eclipse.ui.console.IConsole;
+import org.eclipse.ui.console.IOConsole;
+import org.eclipse.ui.console.IOConsoleInputStream;
+import org.eclipse.ui.console.IOConsoleOutputStream;
+import org.eclipse.ui.plugin.AbstractUIPlugin;
+import org.osgi.framework.BundleContext;
+
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.devices.services.console.ADBShellHandler;
+import com.motorola.studio.android.devices.services.console.EmulatorConsoleHandler;
+import com.motorola.studio.android.emulator.EmulatorPlugin;
+
+/**
+ * The activator class controls the plug-in life cycle
+ */
+public class DeviceServicesPlugin extends AbstractUIPlugin
+{
+
+ public static final String PLUGIN_ID = "com.motorola.studio.android.devices.services";
+
+ public static final String DEPLOY_SERVICE_ID =
+ "com.motorola.studio.android.devices.services.deployService";
+
+ public static final String UNINSTALL_APP_SERVICE_ID =
+ "com.motorola.studio.android.devices.services.uninstallAppService";
+
+ private static ServiceHandler deployServiceHandler = null;
+
+ private static ServiceHandler uninstallAppServiceHandler = null;
+
+ public static final boolean IS_WIN32 = Platform.getOS().equals(Platform.OS_WIN32);
+
+ private static String EMULATOR_CONSOLE_SERVICE_ID =
+ "com.motorola.studio.android.devices.services.emulatorConsole";
+
+ private static String ADB_SHELL_SERVICE_ID =
+ "com.motorola.studio.android.devices.services.adbShell";
+
+ private static DeviceServicesPlugin plugin;
+
+ private static final String SCREENSHOT_SERVICE_ID = PLUGIN_ID + ".takescreenshot";
+
+ private static final String MONKEY_SERVICE_ID = PLUGIN_ID + ".monkey";
+
+ private static ServiceHandler screenshotServiceHandler = null;
+
+ private static ServiceHandler monkeyServiceHandler = null;
+
+ private static ServiceHandler adbShellServiceHandler = null;
+
+ private static ServiceHandler emulatorConsoleServiceHandler = null;
+
+ public static final String ANDROID_LANG_SERVICE_ID = PLUGIN_ID + ".changeLanguageService";
+
+ public static final String LANG_PAGE_CONTEXT_HELP_ID = PLUGIN_ID + ".langPage";
+
+ private static Collection<IConsoleKilledListener> listeners =
+ new ArrayList<IConsoleKilledListener>();
+
+ private final IWorkbenchListener workbenchListener = new IWorkbenchListener()
+ {
+
+ // killllll all consoles
+ public boolean preShutdown(IWorkbench workbench, boolean forced)
+ {
+ List<IConsole> consolesToClose = new ArrayList<IConsole>();
+ List<IConsoleKilledListener> copy = new ArrayList<IConsoleKilledListener>(listeners);
+ for (IConsole console : ConsolePlugin.getDefault().getConsoleManager().getConsoles()
+ .clone())
+ {
+ if (console.getName().contains(EmulatorConsoleHandler.CONSOLE_NAME)
+ || console.getName().contains(ADBShellHandler.CONSOLE_NAME))
+ {
+ for (IConsoleKilledListener listener : copy)
+ {
+ listener.consoleKilled(console.getName());
+ }
+
+ consolesToClose.add(console);
+ }
+
+ }
+ if (consolesToClose.size() > 0)
+ {
+
+ ConsolePlugin.getDefault().getConsoleManager()
+ .removeConsoles(consolesToClose.toArray(new IConsole[0]));
+ }
+ return true;
+ }
+
+ public void postShutdown(IWorkbench workbench)
+ {
+ //do nothing;
+ }
+ };
+
+ public interface IConsoleKilledListener
+ {
+ void consoleKilled(String consoleName);
+ }
+
+ public static void addConsoleKilledListener(IConsoleKilledListener listener)
+ {
+ /*
+ * Keep the entire list, even if elements are the same object.
+ * This will ensure that the last console killed will have their proper listener.
+ */
+ if (!listeners.contains(listener))
+ {
+ listeners.add(listener);
+ }
+ }
+
+ public static void removeConsoleKilledListener(IConsoleKilledListener listener)
+ {
+ listeners.remove(listener);
+ }
+
+ public DeviceServicesPlugin()
+ {
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.ui.plugin.AbstractUIPlugin#start(org.osgi.framework.BundleContext)
+ */
+ @Override
+ public void start(BundleContext context) throws Exception
+ {
+ StudioLogger.debug(DeviceServicesPlugin.class,
+ "Starting MOTODEV Android Device Services Plugin...");
+
+ super.start(context);
+ plugin = this;
+ PlatformUI.getWorkbench().addWorkbenchListener(workbenchListener);
+
+ StudioLogger.debug(DeviceServicesPlugin.class,
+ "MOTODEV Android Device Services Plugin started.");
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.ui.plugin.AbstractUIPlugin#stop(org.osgi.framework.BundleContext)
+ */
+ @Override
+ public void stop(BundleContext context) throws Exception
+ {
+ PlatformUI.getWorkbench().removeWorkbenchListener(workbenchListener);
+ plugin = null;
+ super.stop(context);
+ }
+
+ /**
+ * Returns the shared instance
+ *
+ * @return the shared instance
+ */
+ public static DeviceServicesPlugin getDefault()
+ {
+ return plugin;
+ }
+
+ /**
+ * Creates a console for a process
+ *
+ * @param p The process
+ */
+ public static void redirectProcessStreamsToConsole(Process p, String consoleName)
+ {
+ InputStream processIn = p.getInputStream();
+ OutputStream processOut = p.getOutputStream();
+ redirectStreamsToConsole(processIn, processOut, consoleName);
+ }
+
+ /**
+ * Creates a console for a process
+ *
+ * @param p The process
+ */
+ public static void redirectStreamsToConsole(final InputStream in, final OutputStream out,
+ final String consoleName)
+ {
+ final IOConsole console = new IOConsole(consoleName, null);
+
+ console.activate();
+ ConsolePlugin.getDefault().getConsoleManager().addConsoles(new IConsole[]
+ {
+ console
+ });
+
+ final IOConsoleOutputStream consoleOut = console.newOutputStream();
+ final IOConsoleInputStream consoleIn = console.getInputStream();
+
+ new Thread(new Runnable()
+ {
+ public void run()
+ {
+ boolean carriageReturn = false;
+
+ while (true)
+ {
+ try
+ {
+ int byteRead = in.read();
+ if (byteRead == -1)
+ {
+ throw new Exception();
+ }
+
+ if (carriageReturn && (byteRead != 13))
+ {
+ consoleOut.write(13);
+ consoleOut.write(byteRead);
+ carriageReturn = false;
+ }
+ else if (!carriageReturn && (byteRead == 13))
+ {
+ carriageReturn = true;
+ }
+ else
+ {
+ consoleOut.write(byteRead);
+ carriageReturn = false;
+ }
+ consoleOut.flush();
+ }
+ catch (Exception e)
+ {
+ ConsolePlugin.getDefault().getConsoleManager()
+ .removeConsoles(new IConsole[]
+ {
+ console
+ });
+
+ Collection<IConsoleKilledListener> cloneListeners =
+ new ArrayList<IConsoleKilledListener>(listeners);
+ for (IConsoleKilledListener listener : cloneListeners)
+ {
+ listener.consoleKilled(consoleName);
+ }
+
+ break;
+ }
+ }
+ }
+ }).start();
+
+ new Thread(new Runnable()
+ {
+ public void run()
+ {
+ while (true)
+ {
+ try
+ {
+ int byteRead = consoleIn.read();
+ out.write(byteRead);
+ out.flush();
+ }
+ catch (Exception e)
+ {
+ break;
+ }
+ }
+ }
+ }).start();
+ }
+
+ /**
+ * Retrieves the adb shell service handler.
+ *
+ * @return The currently registered stop service handler, or <null> if no handler is registered.
+ */
+ public static ServiceHandler getADBShellServiceHandler()
+ {
+ if ((adbShellServiceHandler == null) && (ADB_SHELL_SERVICE_ID != null))
+ {
+ // find the appropriate stop service handler
+ IDeviceType device =
+
+ DeviceTypeRegistry.getInstance().getDeviceTypeById(EmulatorPlugin.DEVICE_ID);
+ List<IService> services = device.getServices();
+
+ for (IService service : services)
+ {
+ IServiceHandler handler = service.getHandler();
+ if (handler.getService().getId().equals(ADB_SHELL_SERVICE_ID))
+ {
+ adbShellServiceHandler = (ServiceHandler) handler;
+ break;
+ }
+ }
+ }
+ return adbShellServiceHandler;
+ }
+
+ /**
+ * Retrieves the emulator console service handler.
+ *
+ * @return The currently registered stop service handler, or <null> if no handler is registered.
+ */
+ public static ServiceHandler getEmulatorConsoleServiceHandler()
+ {
+ if ((emulatorConsoleServiceHandler == null) && (EMULATOR_CONSOLE_SERVICE_ID != null))
+ {
+ // find the appropriate stop service handler
+ IDeviceType device =
+ DeviceTypeRegistry.getInstance().getDeviceTypeById(EmulatorPlugin.DEVICE_ID);
+ List<IService> services = device.getServices();
+ for (IService service : services)
+ {
+ IServiceHandler handler = service.getHandler();
+ if (handler.getService().getId().equals(EMULATOR_CONSOLE_SERVICE_ID))
+ {
+ emulatorConsoleServiceHandler = (ServiceHandler) handler;
+ break;
+ }
+ }
+ }
+
+ return emulatorConsoleServiceHandler;
+ }
+
+ /**
+ * Creates and returns a new image descriptor for an image file in this plug-in.
+ * @param path the relative path of the image file, relative to the root of the plug-in; the path must be legal
+ * @return an image descriptor, or null if no image could be found
+ */
+ public static ImageDescriptor getImageDescriptor(String path)
+ {
+ return imageDescriptorFromPlugin(PLUGIN_ID, path);
+ }
+
+ /**
+ * Retrieves the deploy service handler.
+ *
+ * @return The currently registered stop service handler, or <null> if no handler is registered.
+ */
+ public static ServiceHandler getDeployServiceHandler()
+ {
+ if ((deployServiceHandler == null) && (DEPLOY_SERVICE_ID != null))
+ {
+ // find the appropriate stop service handler
+ IDeviceType device =
+ DeviceTypeRegistry.getInstance().getDeviceTypeById(EmulatorPlugin.DEVICE_ID);
+ List<IService> services = device.getServices();
+ for (IService service : services)
+ {
+ IServiceHandler handler = service.getHandler();
+ if (handler.getService().getId().equals(DEPLOY_SERVICE_ID))
+ {
+ deployServiceHandler = (ServiceHandler) handler;
+ break;
+ }
+ }
+ }
+
+ return deployServiceHandler;
+ }
+
+ /**
+ * Retrieves the deploy service handler.
+ *
+ * @return The currently registered stop service handler, or <null> if no handler is registered.
+ */
+ public static ServiceHandler getUninstallAppServiceHandler()
+ {
+ if ((uninstallAppServiceHandler == null) && (UNINSTALL_APP_SERVICE_ID != null))
+ {
+ // find the appropriate stop service handler
+ IDeviceType device =
+ DeviceTypeRegistry.getInstance().getDeviceTypeById(EmulatorPlugin.DEVICE_ID);
+ List<IService> services = device.getServices();
+ for (IService service : services)
+ {
+ IServiceHandler handler = service.getHandler();
+ if (handler.getService().getId().equals(UNINSTALL_APP_SERVICE_ID))
+ {
+ uninstallAppServiceHandler = (ServiceHandler) handler;
+ break;
+ }
+ }
+ }
+
+ return uninstallAppServiceHandler;
+ }
+
+ /**
+ * Retrieves the deploy service handler.
+ *
+ * @return The currently registered stop service handler, or <null> if no handler is registered.
+ */
+ public static ServiceHandler getScreenshotServiceHandler()
+ {
+ if ((screenshotServiceHandler == null) && (SCREENSHOT_SERVICE_ID != null))
+ {
+ // find the appropriate stop service handler
+ IDeviceType device =
+ DeviceTypeRegistry.getInstance().getDeviceTypeById(EmulatorPlugin.DEVICE_ID);
+ List<IService> services = device.getServices();
+ for (IService service : services)
+ {
+ IServiceHandler handler = service.getHandler();
+ if (handler.getService().getId().equals(SCREENSHOT_SERVICE_ID))
+ {
+ screenshotServiceHandler = (ServiceHandler) handler;
+ break;
+ }
+ }
+ }
+
+ return screenshotServiceHandler;
+ }
+
+ /**
+ * Retrieves the monkey service handler.
+ *
+ * @return The currently registered stop service handler, or <null> if no handler is registered.
+ */
+ public static ServiceHandler getMonkeyServiceHandler()
+ {
+ if ((monkeyServiceHandler == null) && (MONKEY_SERVICE_ID != null))
+ {
+ // find the appropriate stop service handler
+ IDeviceType device =
+ DeviceTypeRegistry.getInstance().getDeviceTypeById(EmulatorPlugin.DEVICE_ID);
+ List<IService> services = device.getServices();
+ IServiceHandler handler = null;
+ for (IService service : services)
+ {
+ handler = service.getHandler();
+ if (handler.getService().getId().equals(MONKEY_SERVICE_ID))
+ {
+ monkeyServiceHandler = (ServiceHandler) handler;
+ break;
+ }
+ }
+ }
+
+ return monkeyServiceHandler;
+ }
+}
diff --git a/src/plugins/devices.services/src/com/motorola/studio/android/devices/services/console/ADBShellCommand.java b/src/plugins/devices.services/src/com/motorola/studio/android/devices/services/console/ADBShellCommand.java
new file mode 100644
index 0000000..b9f0cad
--- /dev/null
+++ b/src/plugins/devices.services/src/com/motorola/studio/android/devices/services/console/ADBShellCommand.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.devices.services.console;
+
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.sequoyah.device.common.utilities.exception.SequoyahException;
+import org.eclipse.sequoyah.device.framework.model.IInstance;
+
+import com.motorola.studio.android.devices.services.DeviceServicesPlugin;
+import com.motorola.studio.android.emulator.core.model.IAndroidEmulatorInstance;
+import com.motorola.studio.android.emulator.ui.view.AbstractAndroidView;
+
+public class ADBShellCommand extends AbstractHandler
+{
+
+ public Object execute(ExecutionEvent event) throws ExecutionException
+ {
+ IAndroidEmulatorInstance instance = AbstractAndroidView.getActiveInstance();
+ if (instance instanceof IInstance)
+ {
+ try
+ {
+ DeviceServicesPlugin.getADBShellServiceHandler().run((IInstance) instance);
+ }
+ catch (SequoyahException e)
+ {
+ //do nothing
+ }
+ }
+ return null;
+ }
+
+}
diff --git a/src/plugins/devices.services/src/com/motorola/studio/android/devices/services/console/ADBShellHandler.java b/src/plugins/devices.services/src/com/motorola/studio/android/devices/services/console/ADBShellHandler.java
new file mode 100644
index 0000000..9464955
--- /dev/null
+++ b/src/plugins/devices.services/src/com/motorola/studio/android/devices/services/console/ADBShellHandler.java
@@ -0,0 +1,256 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.devices.services.console;
+
+import java.io.File;
+import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.SubMonitor;
+import org.eclipse.jface.dialogs.ProgressMonitorDialog;
+import org.eclipse.jface.operation.IRunnableWithProgress;
+import org.eclipse.sequoyah.device.framework.model.IInstance;
+import org.eclipse.sequoyah.device.framework.model.handler.IServiceHandler;
+import org.eclipse.sequoyah.device.framework.model.handler.ServiceHandler;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.ui.PlatformUI;
+
+import com.motorola.studio.android.adt.ISerialNumbered;
+import com.motorola.studio.android.adt.SdkUtils;
+import com.motorola.studio.android.devices.services.DeviceServicesPlugin;
+import com.motorola.studio.android.devices.services.DeviceServicesPlugin.IConsoleKilledListener;
+import com.motorola.studio.android.devices.services.i18n.ServicesNLS;
+
+/**
+ * Class responsible to implement the handler for the service
+ * "ADB Shell"
+ */
+public class ADBShellHandler extends ServiceHandler
+{
+ public static final String CONSOLE_NAME = "ADB Shell"; //$NON-NLS-1$
+
+ private static final String SERIAL_PARAMETER = "-s"; //$NON-NLS-1$
+
+ private static final String SHELL_COMMAND = "shell"; //$NON-NLS-1$
+
+ private static final Map<String, Integer> consolesCache = new HashMap<String, Integer>();
+
+ private static final Map<String, Process> consolesProcesses = new HashMap<String, Process>();
+
+ public ADBShellHandler()
+ {
+
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.sequoyah.device.framework.model.handler.ServiceHandler#newInstance()
+ */
+ @Override
+ public IServiceHandler newInstance()
+ {
+ return new ADBShellHandler();
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.sequoyah.device.framework.model.handler.ServiceHandler#runService(org.eclipse.sequoyah.device.framework.model.IInstance, java.util.Map, org.eclipse.core.runtime.IProgressMonitor)
+ */
+ @Override
+ public IStatus runService(IInstance theInstance, Map<Object, Object> arguments,
+ IProgressMonitor monitor)
+ {
+ IStatus status = Status.OK_STATUS;
+ List<String> command = new LinkedList<String>();
+ final IInstance instance = theInstance;
+
+ File sdkPath = new File(SdkUtils.getSdkPath());
+ String adbPath = SdkUtils.getAdbPath();
+ File adb = new File(adbPath);
+
+ if ((sdkPath != null) && sdkPath.exists() && sdkPath.isDirectory())
+ {
+ if (adb.exists() && adb.isFile())
+ {
+ if (instance instanceof ISerialNumbered)
+ {
+ final String[] serialNumber = new String[1];
+
+ serialNumber[0] = ((ISerialNumbered) instance).getSerialNumber();
+
+ if (serialNumber[0] == null)
+ {
+ PlatformUI.getWorkbench().getDisplay().syncExec(new Runnable()
+ {
+
+ public void run()
+ {
+ ProgressMonitorDialog dialog =
+ new ProgressMonitorDialog(new Shell(PlatformUI
+ .getWorkbench().getDisplay()));
+ try
+ {
+ dialog.run(false, false, new IRunnableWithProgress()
+ {
+
+ public void run(IProgressMonitor monitor)
+ throws InvocationTargetException,
+ InterruptedException
+ {
+ int limit = 20;
+
+ SubMonitor theMonitor = SubMonitor.convert(monitor);
+ theMonitor
+ .beginTask(
+ ServicesNLS.ADBShellHandler_WaitingDeviceToLoad,
+ limit);
+
+ int times = 0;
+
+ while ((serialNumber[0] == null) && (times < limit))
+ {
+ theMonitor.worked(1);
+ Thread.sleep(500);
+ serialNumber[0] =
+ ((ISerialNumbered) instance)
+ .getSerialNumber();
+ times++;
+ }
+
+ theMonitor.setWorkRemaining(0);
+ }
+ });
+ }
+ catch (Exception e)
+ {
+ //do nothing
+ }
+ }
+ });
+
+ }
+
+ // Fix a condition that Studio holds the UI thread forever
+ if (serialNumber[0] == null)
+ {
+ status =
+ new Status(IStatus.ERROR, DeviceServicesPlugin.PLUGIN_ID,
+ ServicesNLS.ERR_ADBShellHandler_CouldNotExecuteTheAdbShell);
+ return status;
+ }
+
+ if (adbPath.contains(" ")) //$NON-NLS-1$
+ {
+ if (DeviceServicesPlugin.IS_WIN32)
+ {
+ adbPath = "\"" + adbPath + "\""; //$NON-NLS-1$ //$NON-NLS-2$
+ }
+ else
+ {
+ adbPath = adbPath.replace(" ", "\\ "); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+ }
+
+ command.add(adbPath);
+ command.add(SERIAL_PARAMETER);
+ command.add(serialNumber[0]);
+ command.add(SHELL_COMMAND);
+
+ try
+ {
+ Integer i = consolesCache.get(serialNumber[0]);
+ i = (i == null ? 1 : ++i);
+ consolesCache.put(serialNumber[0], i);
+
+ String[] cmdArray = command.toArray(new String[4]);
+ StringBuffer sb = new StringBuffer();
+ for (String cmd : cmdArray)
+ {
+ sb.append(cmd);
+ sb.append(" "); //$NON-NLS-1$
+ }
+
+ Process p = Runtime.getRuntime().exec(cmdArray);
+
+ String consoleName = CONSOLE_NAME + " - " + serialNumber[0]; //$NON-NLS-1$
+
+ if (i != null)
+ {
+ consoleName += " (" + i + ")"; //$NON-NLS-1$ //$NON-NLS-2$
+ }
+ consolesProcesses.put(consoleName, p);
+ DeviceServicesPlugin.redirectProcessStreamsToConsole(p, consoleName);
+ DeviceServicesPlugin.addConsoleKilledListener(listener);
+ }
+ catch (IOException e)
+ {
+ status =
+ new Status(IStatus.ERROR, DeviceServicesPlugin.PLUGIN_ID,
+ ServicesNLS.ERR_ADBShellHandler_CouldNotExecuteTheAdbShell,
+ e);
+ }
+ }
+
+ }
+ else
+ {
+ status =
+ new Status(IStatus.ERROR, DeviceServicesPlugin.PLUGIN_ID,
+ ServicesNLS.ERR_ADBShellHandler_MissingAdbShell);
+ }
+ }
+ else
+ {
+ status =
+ new Status(IStatus.ERROR, DeviceServicesPlugin.PLUGIN_ID,
+ ServicesNLS.ERR_ADBShellHandler_AndroidSdkIsNotConfigured);
+ }
+
+ return status;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.sequoyah.device.framework.model.handler.ServiceHandler#updatingService(org.eclipse.sequoyah.device.framework.model.IInstance, org.eclipse.core.runtime.IProgressMonitor)
+ */
+ @Override
+ public IStatus updatingService(IInstance arg0, IProgressMonitor arg1)
+ {
+ return Status.OK_STATUS;
+ }
+
+ private final IConsoleKilledListener listener = new IConsoleKilledListener()
+ {
+ public void consoleKilled(String name)
+ {
+ if (consolesProcesses.containsKey(name))
+ {
+ Process p = consolesProcesses.get(name);
+ p.destroy();
+ DeviceServicesPlugin.removeConsoleKilledListener(listener);
+ consolesProcesses.remove(name);
+ }
+ }
+ };
+
+}
diff --git a/src/plugins/devices.services/src/com/motorola/studio/android/devices/services/console/EmulatorConsoleCommand.java b/src/plugins/devices.services/src/com/motorola/studio/android/devices/services/console/EmulatorConsoleCommand.java
new file mode 100644
index 0000000..d827ed0
--- /dev/null
+++ b/src/plugins/devices.services/src/com/motorola/studio/android/devices/services/console/EmulatorConsoleCommand.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.devices.services.console;
+
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.sequoyah.device.common.utilities.exception.SequoyahException;
+import org.eclipse.sequoyah.device.framework.model.IInstance;
+
+import com.motorola.studio.android.devices.services.DeviceServicesPlugin;
+import com.motorola.studio.android.emulator.core.model.IAndroidEmulatorInstance;
+import com.motorola.studio.android.emulator.ui.view.AbstractAndroidView;
+
+public class EmulatorConsoleCommand extends AbstractHandler
+{
+
+ public Object execute(ExecutionEvent event) throws ExecutionException
+ {
+ IAndroidEmulatorInstance instance = AbstractAndroidView.getActiveInstance();
+ if (instance instanceof IInstance)
+ {
+ try
+ {
+ DeviceServicesPlugin.getEmulatorConsoleServiceHandler().run((IInstance) instance);
+ }
+ catch (SequoyahException e)
+ {
+ //do nothing
+ }
+ }
+ return null;
+ }
+
+}
diff --git a/src/plugins/devices.services/src/com/motorola/studio/android/devices/services/console/EmulatorConsoleHandler.java b/src/plugins/devices.services/src/com/motorola/studio/android/devices/services/console/EmulatorConsoleHandler.java
new file mode 100644
index 0000000..c3bf3d9
--- /dev/null
+++ b/src/plugins/devices.services/src/com/motorola/studio/android/devices/services/console/EmulatorConsoleHandler.java
@@ -0,0 +1,234 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.devices.services.console;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.lang.reflect.InvocationTargetException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.SubMonitor;
+import org.eclipse.jface.dialogs.ProgressMonitorDialog;
+import org.eclipse.jface.operation.IRunnableWithProgress;
+import org.eclipse.sequoyah.device.framework.model.IInstance;
+import org.eclipse.sequoyah.device.framework.model.handler.IServiceHandler;
+import org.eclipse.sequoyah.device.framework.model.handler.ServiceHandler;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.ui.PlatformUI;
+
+import com.motorola.studio.android.adt.ISerialNumbered;
+import com.motorola.studio.android.common.utilities.EclipseUtils;
+import com.motorola.studio.android.devices.services.DeviceServicesPlugin;
+import com.motorola.studio.android.devices.services.DeviceServicesPlugin.IConsoleKilledListener;
+import com.motorola.studio.android.devices.services.i18n.ServicesNLS;
+import com.motorola.studio.android.utilities.TelnetFrameworkAndroid;
+
+/**
+ * Class responsible to implement the handler for the service
+ * "Emulator Console"
+ */
+public class EmulatorConsoleHandler extends ServiceHandler
+{
+ private static final Map<String, Integer> consolesCache = new HashMap<String, Integer>();
+
+ private static final Map<String, TelnetFrameworkAndroid> telnetsCache =
+ new HashMap<String, TelnetFrameworkAndroid>();
+
+ private final IConsoleKilledListener listener = new IConsoleKilledListener()
+ {
+ public void consoleKilled(String name)
+ {
+ if (telnetsCache.containsKey(name))
+ {
+ TelnetFrameworkAndroid telnet = telnetsCache.get(name);
+ if (telnet.isConnected())
+ {
+ try
+ {
+ telnet.disconnect();
+ }
+ catch (IOException e)
+ {
+ EclipseUtils
+ .showInformationDialog(
+ ServicesNLS.GEN_Warning,
+ ServicesNLS.WARN_EmulatorConsoleHandler_CouldNotCloseTheConsoleConnection);
+ }
+ }
+ telnetsCache.remove(name);
+ DeviceServicesPlugin.removeConsoleKilledListener(listener);
+ }
+ }
+ };
+
+ public static final String CONSOLE_NAME = "Emulator Console";
+
+ private static final String LOCALHOST = "localhost";
+
+ /**
+ * Constructor
+ */
+ public EmulatorConsoleHandler()
+ {
+
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.sequoyah.device.framework.model.handler.ServiceHandler#newInstance()
+ */
+ @Override
+ public IServiceHandler newInstance()
+ {
+ return new EmulatorConsoleHandler();
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.sequoyah.device.framework.model.handler.ServiceHandler#runService(org.eclipse.sequoyah.device.framework.model.IInstance, java.util.Map, org.eclipse.core.runtime.IProgressMonitor)
+ */
+ @Override
+ public IStatus runService(final IInstance instance, Map<Object, Object> arguments,
+ IProgressMonitor monitor)
+ {
+ IStatus status = Status.OK_STATUS;
+ if (instance instanceof ISerialNumbered)
+ {
+ // Retrieve the emulator port from its serial number
+ Pattern pattern = Pattern.compile("emulator-([0-9]+)");
+ final String[] serialNumber = new String[1];
+
+ serialNumber[0] = ((ISerialNumbered) instance).getSerialNumber();
+ if (serialNumber[0] == null)
+ {
+ PlatformUI.getWorkbench().getDisplay().syncExec(new Runnable()
+ {
+
+ public void run()
+ {
+ ProgressMonitorDialog dialog =
+ new ProgressMonitorDialog(new Shell(PlatformUI.getWorkbench()
+ .getDisplay()));
+ try
+ {
+ dialog.run(false, false, new IRunnableWithProgress()
+ {
+
+ public void run(IProgressMonitor monitor)
+ throws InvocationTargetException, InterruptedException
+ {
+ int limit = 20;
+
+ SubMonitor theMonitor = SubMonitor.convert(monitor);
+ theMonitor.beginTask(
+ ServicesNLS.ADBShellHandler_WaitingDeviceToLoad, limit);
+
+ int times = 0;
+
+ while ((serialNumber[0] == null) && (times < limit))
+ {
+ theMonitor.worked(1);
+ Thread.sleep(500);
+ serialNumber[0] =
+ ((ISerialNumbered) instance).getSerialNumber();
+ times++;
+ }
+
+ theMonitor.setWorkRemaining(0);
+ }
+ });
+ }
+ catch (Exception e)
+ {
+ //do nothing
+ }
+ }
+ });
+ }
+
+ // Fix a condition that Studio holds the UI thread forever
+ if (serialNumber[0] == null)
+ {
+ status =
+ new Status(IStatus.ERROR, DeviceServicesPlugin.PLUGIN_ID,
+ ServicesNLS.ERR_EmulatorConsoleHandler_CouldNotOpenTheConsoleShell);
+ return status;
+ }
+
+ Matcher matcher = pattern.matcher(serialNumber[0]);
+ if (matcher.matches())
+ {
+ String port = matcher.group(1);
+ final TelnetFrameworkAndroid telnet = new TelnetFrameworkAndroid();
+ try
+ {
+ Integer i = consolesCache.get(serialNumber[0]);
+ i = (i == null ? 1 : ++i);
+ consolesCache.put(serialNumber[0], i);
+
+ telnet.connect(LOCALHOST, Integer.parseInt(port));
+ InputStream in = telnet.getInputStream();
+ OutputStream out = telnet.getOutputStream();
+
+ String consoleName = CONSOLE_NAME + " - " + serialNumber[0];
+
+ if (i != null)
+ {
+ consoleName += " (" + i + ")";
+ }
+
+ telnetsCache.put(consoleName, telnet);
+ DeviceServicesPlugin.addConsoleKilledListener(listener);
+ DeviceServicesPlugin.redirectStreamsToConsole(in, out, consoleName);
+ }
+ catch (IOException e)
+ {
+ status =
+ new Status(
+ IStatus.ERROR,
+ DeviceServicesPlugin.PLUGIN_ID,
+ ServicesNLS.ERR_EmulatorConsoleHandler_CouldNotOpenTheConsoleShell,
+ e);
+ }
+ }
+ }
+ else
+ {
+ status =
+ new Status(IStatus.ERROR, DeviceServicesPlugin.PLUGIN_ID,
+ ServicesNLS.ERR_EmulatorConsoleHandler_CouldNotRetrieveTheEmulatorPort);
+ }
+
+ return status;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.sequoyah.device.framework.model.handler.ServiceHandler#updatingService(org.eclipse.sequoyah.device.framework.model.IInstance, org.eclipse.core.runtime.IProgressMonitor)
+ */
+ @Override
+ public IStatus updatingService(IInstance instance, IProgressMonitor monitor)
+ {
+ return Status.OK_STATUS;
+ }
+}
diff --git a/src/plugins/devices.services/src/com/motorola/studio/android/devices/services/ddms/MonkeyServiceCommand.java b/src/plugins/devices.services/src/com/motorola/studio/android/devices/services/ddms/MonkeyServiceCommand.java
new file mode 100644
index 0000000..ea44bd7
--- /dev/null
+++ b/src/plugins/devices.services/src/com/motorola/studio/android/devices/services/ddms/MonkeyServiceCommand.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.devices.services.ddms;
+
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.core.commands.IHandler;
+import org.eclipse.sequoyah.device.common.utilities.exception.SequoyahException;
+import org.eclipse.sequoyah.device.framework.model.IInstance;
+
+import com.motorola.studio.android.devices.services.DeviceServicesPlugin;
+import com.motorola.studio.android.emulator.core.model.IAndroidEmulatorInstance;
+import com.motorola.studio.android.emulator.ui.view.AbstractAndroidView;
+
+/**
+ * The default handler for Test events with Monkey command.
+ */
+public class MonkeyServiceCommand extends AbstractHandler implements IHandler
+{
+
+ public Object execute(ExecutionEvent event) throws ExecutionException
+ {
+ IAndroidEmulatorInstance instance = AbstractAndroidView.getActiveInstance();
+ if (instance instanceof IInstance)
+ {
+ try
+ {
+ DeviceServicesPlugin.getMonkeyServiceHandler().run((IInstance) instance);
+ }
+ catch (SequoyahException e)
+ {
+ //do nothing
+ }
+ }
+ return null;
+ }
+
+}
diff --git a/src/plugins/devices.services/src/com/motorola/studio/android/devices/services/ddms/MonkeyServiceHandler.java b/src/plugins/devices.services/src/com/motorola/studio/android/devices/services/ddms/MonkeyServiceHandler.java
new file mode 100644
index 0000000..f08c89d
--- /dev/null
+++ b/src/plugins/devices.services/src/com/motorola/studio/android/devices/services/ddms/MonkeyServiceHandler.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.devices.services.ddms;
+
+import java.util.Map;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.sequoyah.device.framework.model.IInstance;
+import org.eclipse.sequoyah.device.framework.model.handler.IServiceHandler;
+import org.eclipse.sequoyah.device.framework.model.handler.ServiceHandler;
+
+import com.motorola.studio.android.adt.DDMSUtils;
+import com.motorola.studio.android.adt.ISerialNumbered;
+import com.motorola.studio.android.devices.services.i18n.ServicesNLS;
+
+/**
+ * The default handler for Test events with Monkey service.
+ */
+public class MonkeyServiceHandler extends ServiceHandler
+{
+ @Override
+ public IServiceHandler newInstance()
+ {
+ return new MonkeyServiceHandler();
+ }
+
+ @Override
+ public IStatus runService(IInstance instance, Map<Object, Object> arg1, IProgressMonitor arg2)
+ {
+ if (instance instanceof ISerialNumbered)
+ {
+ final String serialNumber = ((ISerialNumbered) instance).getSerialNumber();
+ final String deviceName = instance.getName();
+ Job job = new Job(ServicesNLS.JOB_Name_Monkey)
+ {
+ @Override
+ protected IStatus run(IProgressMonitor monitor)
+ {
+ DDMSUtils.runMonkey(serialNumber, deviceName);
+ return Status.OK_STATUS;
+ }
+
+ };
+ job.schedule();
+ }
+ return Status.OK_STATUS;
+ }
+
+ @Override
+ public IStatus updatingService(IInstance arg0, IProgressMonitor arg1)
+ {
+ return Status.OK_STATUS;
+ }
+
+}
diff --git a/src/plugins/devices.services/src/com/motorola/studio/android/devices/services/ddms/ScreenshotServiceCommand.java b/src/plugins/devices.services/src/com/motorola/studio/android/devices/services/ddms/ScreenshotServiceCommand.java
new file mode 100644
index 0000000..e72f176
--- /dev/null
+++ b/src/plugins/devices.services/src/com/motorola/studio/android/devices/services/ddms/ScreenshotServiceCommand.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */package com.motorola.studio.android.devices.services.ddms;
+
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.core.commands.IHandler;
+import org.eclipse.sequoyah.device.common.utilities.exception.SequoyahException;
+import org.eclipse.sequoyah.device.framework.model.IInstance;
+
+import com.motorola.studio.android.devices.services.DeviceServicesPlugin;
+import com.motorola.studio.android.emulator.core.model.IAndroidEmulatorInstance;
+import com.motorola.studio.android.emulator.ui.view.AbstractAndroidView;
+
+public class ScreenshotServiceCommand extends AbstractHandler implements IHandler
+{
+
+ public Object execute(ExecutionEvent event) throws ExecutionException
+ {
+ IAndroidEmulatorInstance instance = AbstractAndroidView.getActiveInstance();
+ if (instance instanceof IInstance)
+ {
+ try
+ {
+ DeviceServicesPlugin.getScreenshotServiceHandler().run((IInstance) instance);
+ }
+ catch (SequoyahException e)
+ {
+ //do nothing
+ }
+ }
+ return null;
+ }
+
+}
diff --git a/src/plugins/devices.services/src/com/motorola/studio/android/devices/services/ddms/ScreenshotServiceHandler.java b/src/plugins/devices.services/src/com/motorola/studio/android/devices/services/ddms/ScreenshotServiceHandler.java
new file mode 100644
index 0000000..d70a6bd
--- /dev/null
+++ b/src/plugins/devices.services/src/com/motorola/studio/android/devices/services/ddms/ScreenshotServiceHandler.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */package com.motorola.studio.android.devices.services.ddms;
+
+import java.util.Map;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.sequoyah.device.framework.model.IInstance;
+import org.eclipse.sequoyah.device.framework.model.handler.IServiceHandler;
+import org.eclipse.sequoyah.device.framework.model.handler.ServiceHandler;
+
+import com.motorola.studio.android.adt.DDMSUtils;
+import com.motorola.studio.android.adt.ISerialNumbered;
+
+public class ScreenshotServiceHandler extends ServiceHandler
+{
+ @Override
+ public IServiceHandler newInstance()
+ {
+ return new ScreenshotServiceHandler();
+ }
+
+ @Override
+ public IStatus runService(IInstance instance, Map<Object, Object> arg1, IProgressMonitor arg2)
+ {
+ if (instance instanceof ISerialNumbered)
+ {
+ final String serialNumber = ((ISerialNumbered) instance).getSerialNumber();
+ Job job = new Job("Screenshot")
+ {
+ @Override
+ protected IStatus run(IProgressMonitor monitor)
+ {
+ DDMSUtils.takeScreenshot(serialNumber);
+ return Status.OK_STATUS;
+ }
+
+ };
+ job.schedule();
+ }
+ return Status.OK_STATUS;
+ }
+
+ @Override
+ public IStatus updatingService(IInstance arg0, IProgressMonitor arg1)
+ {
+ return Status.OK_STATUS;
+ }
+
+}
diff --git a/src/plugins/devices.services/src/com/motorola/studio/android/devices/services/deploy/DeployServiceCommand.java b/src/plugins/devices.services/src/com/motorola/studio/android/devices/services/deploy/DeployServiceCommand.java
new file mode 100644
index 0000000..f41c6e1
--- /dev/null
+++ b/src/plugins/devices.services/src/com/motorola/studio/android/devices/services/deploy/DeployServiceCommand.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.devices.services.deploy;
+
+import java.util.Arrays;
+
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.sequoyah.device.common.utilities.exception.SequoyahException;
+import org.eclipse.sequoyah.device.framework.manager.ServiceManager;
+import org.eclipse.sequoyah.device.framework.model.IInstance;
+
+import com.motorola.studio.android.devices.services.DeviceServicesPlugin;
+import com.motorola.studio.android.emulator.core.model.IAndroidEmulatorInstance;
+import com.motorola.studio.android.emulator.ui.view.AbstractAndroidView;
+
+public class DeployServiceCommand extends AbstractHandler
+{
+
+ public Object execute(ExecutionEvent event) throws ExecutionException
+ {
+ IAndroidEmulatorInstance emulatorInstance = AbstractAndroidView.getActiveInstance();
+ if (emulatorInstance instanceof IInstance)
+ {
+ try
+ {
+ IInstance instance = (IInstance) emulatorInstance;
+
+ ServiceManager.runServices(Arrays.asList(instance),
+ DeviceServicesPlugin.DEPLOY_SERVICE_ID);
+ }
+ catch (SequoyahException e)
+ {
+ //do nothing
+ }
+ }
+ return null;
+ }
+
+}
diff --git a/src/plugins/devices.services/src/com/motorola/studio/android/devices/services/deploy/DeployServiceHandler.java b/src/plugins/devices.services/src/com/motorola/studio/android/devices/services/deploy/DeployServiceHandler.java
new file mode 100644
index 0000000..edb38c6
--- /dev/null
+++ b/src/plugins/devices.services/src/com/motorola/studio/android/devices/services/deploy/DeployServiceHandler.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.devices.services.deploy;
+
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.sequoyah.device.framework.model.IInstance;
+import org.eclipse.sequoyah.device.framework.model.handler.IServiceHandler;
+import org.eclipse.sequoyah.device.framework.model.handler.ServiceHandler;
+
+import com.motorola.studio.android.adt.DDMSUtils;
+import com.motorola.studio.android.adt.ISerialNumbered;
+import com.motorola.studio.android.adt.InstallPackageBean;
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.devices.services.i18n.ServicesNLS;
+
+/**
+ * DESCRIPTION:
+ * This class plugs the deploy procedure to a TmL service
+ *
+ * RESPONSIBILITY:
+ * Implements the actions that will be triggered when
+ * user chose to Install a Android Application on an
+ * emulator instance.
+ *
+ * COLABORATORS:
+ * None.
+ *
+ * USAGE:
+ * This class is intended to be used by Eclipse only
+ * */
+public class DeployServiceHandler extends ServiceHandler
+{
+ protected InstallPackageBean installBean = null;
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.sequoyah.device.framework.model.handler.ServiceHandler#newInstance()
+ */
+ @Override
+ public IServiceHandler newInstance()
+ {
+ return new DeployServiceHandler();
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.sequoyah.device.framework.model.handler.ServiceHandler#runService(org.eclipse.sequoyah.device.framework.model.IInstance, java.util.Map, org.eclipse.core.runtime.IProgressMonitor)
+ */
+ @Override
+ public IStatus runService(IInstance instance, Map<Object, Object> arguments,
+ IProgressMonitor monitor)
+ {
+ if (instance instanceof ISerialNumbered)
+ {
+ final String serialNumber = ((ISerialNumbered) instance).getSerialNumber();
+ Job j = new Job(ServicesNLS.JOB_Name_Install_Application + " (" + serialNumber + ")")
+ {
+
+ @Override
+ protected IStatus run(IProgressMonitor monitor)
+ {
+ return DDMSUtils.installPackage(serialNumber, installBean);
+ }
+ };
+ j.schedule();
+ }
+
+ return Status.OK_STATUS;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.sequoyah.device.framework.model.handler.ServiceHandler#updatingService(org.eclipse.sequoyah.device.framework.model.IInstance, org.eclipse.core.runtime.IProgressMonitor)
+ */
+ @Override
+ public IStatus updatingService(IInstance instance, IProgressMonitor monitor)
+ {
+ StudioLogger.info("Updating reset service");
+ return Status.OK_STATUS;
+ }
+
+ @Override
+ public IStatus singleInit(List<IInstance> instances)
+ {
+
+ this.installBean = DDMSUtils.installPackageWizard();
+
+ return super.singleInit(instances);
+ }
+}
diff --git a/src/plugins/devices.services/src/com/motorola/studio/android/devices/services/deploy/EmulatorTester.java b/src/plugins/devices.services/src/com/motorola/studio/android/devices/services/deploy/EmulatorTester.java
new file mode 100644
index 0000000..b710ee7
--- /dev/null
+++ b/src/plugins/devices.services/src/com/motorola/studio/android/devices/services/deploy/EmulatorTester.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.devices.services.deploy;
+
+import org.eclipse.core.expressions.PropertyTester;
+
+import com.motorola.studio.android.emulator.ui.view.AbstractAndroidView;
+
+public class EmulatorTester extends PropertyTester
+{
+
+ public boolean test(Object receiver, String property, Object[] args, Object expectedValue)
+ {
+ return AbstractAndroidView.getActiveInstance() != null;
+ }
+
+}
diff --git a/src/plugins/devices.services/src/com/motorola/studio/android/devices/services/deploy/UninstallAppServiceCommand.java b/src/plugins/devices.services/src/com/motorola/studio/android/devices/services/deploy/UninstallAppServiceCommand.java
new file mode 100644
index 0000000..9a6f62a
--- /dev/null
+++ b/src/plugins/devices.services/src/com/motorola/studio/android/devices/services/deploy/UninstallAppServiceCommand.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.devices.services.deploy;
+
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.sequoyah.device.common.utilities.exception.SequoyahException;
+import org.eclipse.sequoyah.device.framework.model.IInstance;
+
+import com.motorola.studio.android.devices.services.DeviceServicesPlugin;
+import com.motorola.studio.android.emulator.core.model.IAndroidEmulatorInstance;
+import com.motorola.studio.android.emulator.ui.view.AbstractAndroidView;
+
+public class UninstallAppServiceCommand extends AbstractHandler
+{
+
+ public Object execute(ExecutionEvent event) throws ExecutionException
+ {
+ IAndroidEmulatorInstance instance = AbstractAndroidView.getActiveInstance();
+ if (instance instanceof IInstance)
+ {
+ try
+ {
+ DeviceServicesPlugin.getUninstallAppServiceHandler().run((IInstance) instance);
+ }
+ catch (SequoyahException e)
+ {
+ //do nothing
+ }
+ }
+ return null;
+ }
+
+}
diff --git a/src/plugins/devices.services/src/com/motorola/studio/android/devices/services/deploy/UninstallAppServiceHandler.java b/src/plugins/devices.services/src/com/motorola/studio/android/devices/services/deploy/UninstallAppServiceHandler.java
new file mode 100644
index 0000000..8075d29
--- /dev/null
+++ b/src/plugins/devices.services/src/com/motorola/studio/android/devices/services/deploy/UninstallAppServiceHandler.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.devices.services.deploy;
+
+import java.util.Map;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.sequoyah.device.framework.model.IInstance;
+import org.eclipse.sequoyah.device.framework.model.handler.IServiceHandler;
+import org.eclipse.sequoyah.device.framework.model.handler.ServiceHandler;
+
+import com.motorola.studio.android.adt.DDMSUtils;
+import com.motorola.studio.android.adt.ISerialNumbered;
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.devices.services.i18n.ServicesNLS;
+
+/**
+ * DESCRIPTION:
+ * This class plugs the deploy procedure to a TmL service
+ *
+ * RESPONSIBILITY:
+ * Implements the actions that will be triggered when
+ * user chose to Install a Android Application on an
+ * emulator instance.
+ *
+ * COLABORATORS:
+ * None.
+ *
+ * USAGE:
+ * This class is intended to be used by Eclipse only
+ * */
+public class UninstallAppServiceHandler extends ServiceHandler
+{
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.sequoyah.device.framework.model.handler.ServiceHandler#newInstance()
+ */
+ @Override
+ public IServiceHandler newInstance()
+ {
+ return new UninstallAppServiceHandler();
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.sequoyah.device.framework.model.handler.ServiceHandler#runService(org.eclipse.sequoyah.device.framework.model.IInstance, java.util.Map, org.eclipse.core.runtime.IProgressMonitor)
+ */
+ @Override
+ public IStatus runService(IInstance instance, Map<Object, Object> arguments,
+ IProgressMonitor monitor)
+ {
+ if (instance instanceof ISerialNumbered)
+ {
+ final String serialNumber = ((ISerialNumbered) instance).getSerialNumber();
+ Job j = new Job(ServicesNLS.JOB_Name_Uninstall_Application)
+ {
+
+ @Override
+ protected IStatus run(IProgressMonitor monitor)
+ {
+ return DDMSUtils.uninstallPackage(serialNumber);
+ }
+ };
+ j.schedule();
+ }
+
+ return Status.OK_STATUS;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.sequoyah.device.framework.model.handler.ServiceHandler#updatingService(org.eclipse.sequoyah.device.framework.model.IInstance, org.eclipse.core.runtime.IProgressMonitor)
+ */
+ @Override
+ public IStatus updatingService(IInstance instance, IProgressMonitor monitor)
+ {
+ StudioLogger.info("Updating reset service");
+ return Status.OK_STATUS;
+ }
+}
diff --git a/src/plugins/devices.services/src/com/motorola/studio/android/devices/services/i18n/ServicesNLS.java b/src/plugins/devices.services/src/com/motorola/studio/android/devices/services/i18n/ServicesNLS.java
new file mode 100644
index 0000000..9a96fe4
--- /dev/null
+++ b/src/plugins/devices.services/src/com/motorola/studio/android/devices/services/i18n/ServicesNLS.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.devices.services.i18n;
+
+import org.eclipse.osgi.util.NLS;
+
+/**
+ * NLS class for the plugin com.motorola.studio.android.device.service.console
+ */
+public class ServicesNLS extends NLS
+{
+ static
+ {
+ NLS.initializeMessages("com.motorola.studio.android.devices.services.i18n.servicesNLS",
+ ServicesNLS.class);
+ }
+
+ public static String ADBShellHandler_WaitingDeviceToLoad;
+
+ /*
+ * General Strings area
+ */
+ public static String GEN_Warning;
+
+ /*
+ * Error Strings area
+ */
+ public static String ERR_ADBShellHandler_CouldNotExecuteTheAdbShell;
+
+ public static String ERR_ADBShellHandler_MissingAdbShell;
+
+ public static String ERR_ADBShellHandler_AndroidSdkIsNotConfigured;
+
+ public static String ERR_EmulatorConsoleHandler_CouldNotOpenTheConsoleShell;
+
+ public static String ERR_EmulatorConsoleHandler_CouldNotRetrieveTheEmulatorPort;
+
+ /*
+ * Warning Strings area
+ */
+ public static String WARN_EmulatorConsoleHandler_CouldNotCloseTheConsoleConnection;
+
+ /*
+ * Deploy service area
+ */
+ public static String JOB_Name_Install_Application;
+
+ public static String JOB_Name_Uninstall_Application;
+
+ /*
+ * Languade service area
+ */
+
+ public static String UI_Language;
+
+ public static String UI_Country;
+
+ public static String UI_Wizard_Title;
+
+ public static String UI_Wizard_Page_Locale_Title;
+
+ public static String UI_Wizard_Page_Locale_Description;
+
+ /*
+ * Monkey service area
+ */
+ public static String JOB_Name_Monkey;
+}
diff --git a/src/plugins/devices.services/src/com/motorola/studio/android/devices/services/i18n/servicesNLS.properties b/src/plugins/devices.services/src/com/motorola/studio/android/devices/services/i18n/servicesNLS.properties
new file mode 100644
index 0000000..18ac12f
--- /dev/null
+++ b/src/plugins/devices.services/src/com/motorola/studio/android/devices/services/i18n/servicesNLS.properties
@@ -0,0 +1,30 @@
+ADBShellHandler_WaitingDeviceToLoad=Waiting for device to load...
+GEN_Warning=Warning
+
+ERR_ADBShellHandler_CouldNotExecuteTheAdbShell=Could not execute the adb shell.
+
+ERR_ADBShellHandler_MissingAdbShell=The adb executable is not in the Android SDK path.
+
+ERR_ADBShellHandler_AndroidSdkIsNotConfigured=The Android SDK is not configured.
+
+ERR_EmulatorConsoleHandler_CouldNotOpenTheConsoleShell=Could not open the console shell.
+
+ERR_EmulatorConsoleHandler_CouldNotRetrieveTheEmulatorPort=Could not retrieve the emulator port.
+
+WARN_EmulatorConsoleHandler_CouldNotCloseTheConsoleConnection=Could not close the Emulator Console connection. This will not keep you from working with MOTODEV Studio for Android, and will be fixed when you close MOTODEV Studio.
+
+JOB_Name_Install_Application=Install Application
+
+JOB_Name_Uninstall_Application=Uninstall Application
+
+JOB_Name_Monkey=Test events with Monkey
+
+UI_Language=Language
+
+UI_Country=Country
+
+UI_Wizard_Title=Language
+
+UI_Wizard_Page_Locale_Title=Emulator Language
+
+UI_Wizard_Page_Locale_Description=Change the device's language configuration. This will only take effect after you manually restart the emulator.
diff --git a/src/plugins/devices.services/src/com/motorola/studio/android/devices/services/lang/LangServiceCommand.java b/src/plugins/devices.services/src/com/motorola/studio/android/devices/services/lang/LangServiceCommand.java
new file mode 100644
index 0000000..5504f0c
--- /dev/null
+++ b/src/plugins/devices.services/src/com/motorola/studio/android/devices/services/lang/LangServiceCommand.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.devices.services.lang;
+
+import java.util.Arrays;
+
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.sequoyah.device.common.utilities.exception.SequoyahException;
+import org.eclipse.sequoyah.device.framework.manager.ServiceManager;
+import org.eclipse.sequoyah.device.framework.model.IInstance;
+
+import com.motorola.studio.android.devices.services.DeviceServicesPlugin;
+import com.motorola.studio.android.emulator.core.model.IAndroidEmulatorInstance;
+import com.motorola.studio.android.emulator.ui.view.AbstractAndroidView;
+
+/**
+ * Open Change language wizard
+ */
+public class LangServiceCommand extends AbstractHandler
+{
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.commands.AbstractHandler#execute(org.eclipse.core.commands.ExecutionEvent)
+ */
+ public Object execute(ExecutionEvent event) throws ExecutionException
+ {
+ IAndroidEmulatorInstance emulatorInstance = AbstractAndroidView.getActiveInstance();
+ if (emulatorInstance instanceof IInstance)
+ {
+ try
+ {
+ IInstance instance = (IInstance) emulatorInstance;
+
+ ServiceManager.runServices(Arrays.asList(instance),
+ DeviceServicesPlugin.ANDROID_LANG_SERVICE_ID);
+ }
+ catch (SequoyahException e)
+ {
+ //do nothing
+ }
+ }
+ return null;
+ }
+}
diff --git a/src/plugins/devices.services/src/com/motorola/studio/android/devices/services/lang/LangServiceHandler.java b/src/plugins/devices.services/src/com/motorola/studio/android/devices/services/lang/LangServiceHandler.java
new file mode 100644
index 0000000..c5b60b1
--- /dev/null
+++ b/src/plugins/devices.services/src/com/motorola/studio/android/devices/services/lang/LangServiceHandler.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.devices.services.lang;
+
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.jface.dialogs.Dialog;
+import org.eclipse.jface.wizard.WizardDialog;
+import org.eclipse.sequoyah.device.framework.model.IInstance;
+import org.eclipse.sequoyah.device.framework.model.handler.IServiceHandler;
+import org.eclipse.sequoyah.device.framework.model.handler.ServiceHandler;
+import org.eclipse.ui.PlatformUI;
+
+import com.motorola.studio.android.adt.DDMSUtils;
+import com.motorola.studio.android.adt.ISerialNumbered;
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.devices.services.DeviceServicesPlugin;
+import com.motorola.studio.android.devices.services.lang.model.LangWizard;
+
+/**
+ * This class plugs the change language procedure to a TmL service
+ */
+public class LangServiceHandler extends ServiceHandler
+{
+ private String languageID;
+
+ private String countryID;
+
+ /*
+ * (non-Javadoc)
+ *
+ * @seeorg.eclipse.sequoyah.device.framework.model.handler.ServiceHandler#
+ * newInstance()
+ */
+ @Override
+ public IServiceHandler newInstance()
+ {
+ return new LangServiceHandler();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * org.eclipse.sequoyah.device.framework.model.handler.ServiceHandler#runService
+ * (org.eclipse.sequoyah.device.framework.model.IInstance, java.util.Map,
+ * org.eclipse.core.runtime.IProgressMonitor)
+ */
+ @Override
+ public IStatus runService(IInstance instance, Map<Object, Object> arguments,
+ IProgressMonitor monitor)
+ {
+ IStatus status = Status.OK_STATUS;
+
+ final String serialNumber = ((ISerialNumbered) instance).getSerialNumber();
+
+ DDMSUtils.changeLanguage(serialNumber, languageID, countryID);
+
+ // Collecting usage data for statistical purpose
+ try
+ {
+ StudioLogger.collectUsageData(StudioLogger.WHAT_EMULATOR_LANGUAGE,
+ StudioLogger.KIND_EMULATOR, languageID, DeviceServicesPlugin.PLUGIN_ID,
+ DeviceServicesPlugin.getDefault().getBundle().getVersion().toString());
+ }
+ catch (Throwable e)
+ {
+ //Do nothing, but error on the log should never prevent app from working
+ }
+
+ return status;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @seeorg.eclipse.sequoyah.device.framework.model.handler.ServiceHandler#
+ * updatingService(org.eclipse.sequoyah.device.framework.model.IInstance,
+ * org.eclipse.core.runtime.IProgressMonitor)
+ */
+ @Override
+ public IStatus updatingService(IInstance instance, IProgressMonitor monitor)
+ {
+ StudioLogger.info("Updating change language service");
+ return Status.OK_STATUS;
+ }
+
+ @Override
+ public IStatus singleInit(final List<IInstance> instances)
+ {
+ PlatformUI.getWorkbench().getDisplay().syncExec(new Runnable()
+ {
+ public void run()
+ {
+ try
+ {
+ LangWizard wizard = new LangWizard();
+ WizardDialog dialog =
+ new WizardDialog(PlatformUI.getWorkbench().getActiveWorkbenchWindow()
+ .getShell(), wizard);
+ dialog.setPageSize(250, 75);
+
+ int ret = dialog.open();
+
+ if (ret == Dialog.OK)
+ {
+ languageID = wizard.getlanguageId();
+ countryID = wizard.getcountryId();
+ }
+ else
+ {
+ languageID = null;
+ countryID = null;
+ }
+
+ }
+ catch (Exception e)
+ {
+ StudioLogger.error("Change Language TmL Service: could not open UI");
+ }
+ }
+ });
+
+ return super.singleInit(instances);
+ }
+}
diff --git a/src/plugins/devices.services/src/com/motorola/studio/android/devices/services/lang/model/Country.java b/src/plugins/devices.services/src/com/motorola/studio/android/devices/services/lang/model/Country.java
new file mode 100644
index 0000000..f2bcb93
--- /dev/null
+++ b/src/plugins/devices.services/src/com/motorola/studio/android/devices/services/lang/model/Country.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.devices.services.lang.model;
+
+import java.io.InputStream;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NodeList;
+
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.devices.services.DeviceServicesPlugin;
+
+/**
+ * Country representation
+ *
+ * The list of countries has been downloaded from:
+ * http://www.iso.org/iso/country_codes/iso_3166_code_lists.htm
+ */
+public class Country
+{
+
+ private static final String FILE_PATH = "resources/iso_3166-1_list_en.xml";
+
+ private static final String COUNTRY_NODE = "ISO_3166-1_Entry";
+
+ private static final String COUNTRY_NAME = "ISO_3166-1_Country_name";
+
+ private static final String COUNTRY_ID = "ISO_3166-1_Alpha-2_Code_element";
+
+ private static List<Country> countryList = null;
+
+ private static Map<String, Country> countryMap = null;
+
+ private String name;
+
+ private String id;
+
+ /**
+ * Get the countries list
+ *
+ * @return the countries list
+ */
+ public static List<Country> getCountryList()
+ {
+ if (countryList == null)
+ {
+ loadCountries();
+ }
+ return countryList;
+ }
+
+ /*
+ * Load countries from XML file
+ */
+ private static void loadCountries()
+ {
+ countryList = new ArrayList<Country>();
+ countryMap = new HashMap<String, Country>();
+
+ URL countriesURL = DeviceServicesPlugin.getDefault().getBundle().getResource(FILE_PATH);
+
+ try
+ {
+
+ InputStream countriesIS = countriesURL.openStream();
+
+ DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+ DocumentBuilder builder = factory.newDocumentBuilder();
+ Document document = builder.parse(countriesIS);
+
+ NodeList list = document.getDocumentElement().getElementsByTagName(COUNTRY_NODE);
+
+ for (int i = 0; i < list.getLength(); i++)
+ {
+
+ Element countryNode = (Element) list.item(i);
+
+ String name =
+ countryNode.getElementsByTagName(COUNTRY_NAME).item(0).getChildNodes()
+ .item(0).getNodeValue();
+ String ID =
+ countryNode.getElementsByTagName(COUNTRY_ID).item(0).getChildNodes()
+ .item(0).getNodeValue();
+
+ if (((ID != null) && (!ID.equals(""))) && ((name != null) && (!name.equals(""))))
+ {
+ Country country = new Country(name, ID);
+ countryList.add(country);
+ countryMap.put(name, country);
+ }
+ }
+
+ }
+ catch (Exception e)
+ {
+ StudioLogger.error("Change Language TmL Service: could not load countries list");
+ }
+
+ }
+
+ /**
+ * Get country ID from country name
+ *
+ * @param countryName country name
+ * @return country ID
+ */
+ public static String getIdFromName(String countryName)
+ {
+ String id = null;
+ Country country = countryMap.get(countryName);
+ if (country != null)
+ {
+ id = country.getId();
+ }
+ return id;
+ }
+
+ /**
+ * Constructor
+ *
+ * @param name country name
+ * @param id country name
+ */
+ public Country(String name, String id)
+ {
+ super();
+ this.name = name;
+ this.id = id;
+ }
+
+ public String getName()
+ {
+ return name;
+ }
+
+ public void setName(String name)
+ {
+ this.name = name;
+ }
+
+ public String getId()
+ {
+ return id;
+ }
+
+ public void setId(String id)
+ {
+ this.id = id;
+ }
+
+}
diff --git a/src/plugins/devices.services/src/com/motorola/studio/android/devices/services/lang/model/LangWizard.java b/src/plugins/devices.services/src/com/motorola/studio/android/devices/services/lang/model/LangWizard.java
new file mode 100644
index 0000000..86266d6
--- /dev/null
+++ b/src/plugins/devices.services/src/com/motorola/studio/android/devices/services/lang/model/LangWizard.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.devices.services.lang.model;
+
+import org.eclipse.jface.wizard.Wizard;
+
+import com.motorola.studio.android.devices.services.DeviceServicesPlugin;
+import com.motorola.studio.android.devices.services.i18n.ServicesNLS;
+
+/**
+ * Change Language Wizard used to change the device language configuration
+ */
+public class LangWizard extends Wizard
+{
+
+ private final String WIZARD_IMAGE_PATH = "resources/flag.png";
+
+ private LangWizardPage page;
+
+ private final String[] currentLangAndCountry = null;
+
+ private String languageID;
+
+ private String countryID;
+
+ public LangWizard()
+ {
+ this.setWindowTitle(ServicesNLS.UI_Wizard_Title);
+ super.setDefaultPageImageDescriptor(DeviceServicesPlugin
+ .getImageDescriptor(WIZARD_IMAGE_PATH));
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.wizard.Wizard#addPages()
+ */
+ @Override
+ public void addPages()
+ {
+ page = new LangWizardPage(currentLangAndCountry);
+ super.addPage(page);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.wizard.Wizard#canFinish()
+ */
+ @Override
+ public boolean canFinish()
+ {
+ return page.isPageComplete();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.wizard.Wizard#performFinish()
+ */
+ @Override
+ public boolean performFinish()
+ {
+ languageID = Language.getIdFromName(page.getLanguage());
+ countryID = Country.getIdFromName(page.getCountry());
+
+ return true;
+ }
+
+ public String getlanguageId()
+ {
+ return languageID;
+ }
+
+ public String getcountryId()
+ {
+ return countryID;
+ }
+}
diff --git a/src/plugins/devices.services/src/com/motorola/studio/android/devices/services/lang/model/LangWizardPage.java b/src/plugins/devices.services/src/com/motorola/studio/android/devices/services/lang/model/LangWizardPage.java
new file mode 100644
index 0000000..6ab2296
--- /dev/null
+++ b/src/plugins/devices.services/src/com/motorola/studio/android/devices/services/lang/model/LangWizardPage.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.devices.services.lang.model;
+
+import org.eclipse.jface.wizard.WizardPage;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.ui.PlatformUI;
+
+import com.motorola.studio.android.devices.services.DeviceServicesPlugin;
+import com.motorola.studio.android.devices.services.i18n.ServicesNLS;
+
+/**
+ * Page where the user can choose the language the user wants
+ * to apply to the emulator
+ */
+public class LangWizardPage extends WizardPage
+{
+
+ // main composite
+ LocationComposite locationComposite = null;
+
+ private String[] currentLangAndCountry = null;
+
+ /**
+ * Constructor
+ */
+ public LangWizardPage()
+ {
+ this(null);
+ }
+
+ /**
+ * Constructor
+ */
+ public LangWizardPage(String[] currentLangAndCountry)
+ {
+ super("langWizardPage");
+ this.currentLangAndCountry = currentLangAndCountry;
+ setTitle(ServicesNLS.UI_Wizard_Page_Locale_Title);
+ setDescription(ServicesNLS.UI_Wizard_Page_Locale_Description);
+ setPageComplete(false);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.dialogs.IDialogPage#createControl(org.eclipse.swt.widgets.Composite)
+ */
+ public void createControl(Composite parent)
+ {
+
+ // Set Help ID
+ PlatformUI.getWorkbench().getHelpSystem()
+ .setHelp(parent, DeviceServicesPlugin.LANG_PAGE_CONTEXT_HELP_ID);
+
+ // Define layout
+ GridLayout mainLayout = new GridLayout(1, false);
+ mainLayout.marginTop = 0;
+ mainLayout.marginWidth = 0;
+ mainLayout.marginHeight = 0;
+
+ if (currentLangAndCountry != null)
+ {
+ locationComposite =
+ new LocationComposite(parent, currentLangAndCountry[0],
+ currentLangAndCountry[1]);
+ }
+ else
+ {
+ locationComposite = new LocationComposite(parent);
+ }
+
+ locationComposite.addListener(LocationComposite.LOCATION_CHANGE, new Listener()
+ {
+ public void handleEvent(Event arg0)
+ {
+ updatePageComplete();
+ }
+ });
+
+ locationComposite.setLayoutData(mainLayout);
+ setControl(locationComposite);
+ }
+
+ /**
+ * Check if the "Next" button can be enabled by checking if the user has filled all the fields
+ */
+ private void updatePageComplete()
+ {
+ setPageComplete(false);
+ String language = Language.getIdFromName(locationComposite.getLanguage());
+ String country = Country.getIdFromName(locationComposite.getCountry());
+ if ((currentLangAndCountry == null)
+ || (!currentLangAndCountry[0].equals(language) || !currentLangAndCountry[1]
+ .equals(country)))
+ {
+ setPageComplete(true);
+ }
+ }
+
+ /**
+ * Get the selected language
+ *
+ * @return selected language name
+ */
+ public String getLanguage()
+ {
+ return locationComposite.getLanguage();
+ }
+
+ /**
+ * Get the selected country
+ *
+ * @return selected country name
+ */
+ public String getCountry()
+ {
+ return locationComposite.getCountry();
+ }
+
+}
diff --git a/src/plugins/devices.services/src/com/motorola/studio/android/devices/services/lang/model/Language.java b/src/plugins/devices.services/src/com/motorola/studio/android/devices/services/lang/model/Language.java
new file mode 100644
index 0000000..ed659ce
--- /dev/null
+++ b/src/plugins/devices.services/src/com/motorola/studio/android/devices/services/lang/model/Language.java
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.devices.services.lang.model;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.core.runtime.FileLocator;
+
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.devices.services.DeviceServicesPlugin;
+
+/**
+ * Language representation
+ *
+ * The list of languages has been downloaded from:
+ * http://www.loc.gov/standards/iso639-2/ascii_8bits.html
+ */
+public class Language
+{
+
+ private static final String FILE_PATH = "resources/ISO-639-2_utf-8.txt";
+
+ private static List<Language> languageList = null;
+
+ private static Map<String, Language> languageMap = null;
+
+ private String name;
+
+ private String id;
+
+ /**
+ * Get the languages list
+ *
+ * @return the languages list
+ */
+ public static List<Language> getLanguageList()
+ {
+ if (languageList == null)
+ {
+ loadLanguages();
+ }
+ return languageList;
+ }
+
+ /**
+ * Load languages from TXT file
+ */
+ private static void loadLanguages()
+ {
+ languageList = new ArrayList<Language>();
+ languageMap = new HashMap<String, Language>();
+
+ URL languagesURL = DeviceServicesPlugin.getDefault().getBundle().getResource(FILE_PATH);
+
+ BufferedReader input = null;
+ try
+ {
+
+ File file = new File(FileLocator.toFileURL(languagesURL).getPath());
+
+ input = new BufferedReader(new FileReader(file));
+
+ String line = null;
+ String[] lineParts = null;
+ String name = null;
+ String ID = null;
+ while ((line = input.readLine()) != null)
+ {
+ lineParts = line.split("\\|");
+ ID = lineParts[2];
+ name = lineParts[3];
+ if (((ID != null) && (!ID.equals(""))) && ((name != null) && (!name.equals(""))))
+ {
+ Language language = new Language(name, ID);
+ languageList.add(language);
+ languageMap.put(name, language);
+ }
+ }
+ }
+ catch (Exception e)
+ {
+ StudioLogger.error("Change Language TmL Service: could not load languages list");
+ }
+ finally
+ {
+ if (input != null)
+ {
+ try
+ {
+ input.close();
+ }
+ catch (IOException e)
+ {
+ }
+ }
+ }
+
+ }
+
+ /**
+ * Get language ID from language name
+ *
+ * @param langName language name
+ * @return language ID
+ */
+ public static String getIdFromName(String langName)
+ {
+ String id = null;
+ Language language = languageMap.get(langName);
+ if (language != null)
+ {
+ id = language.getId();
+ }
+ return id;
+ }
+
+ /**
+ * Constructs a new Language instance, based on the given name and id.
+ * @param name language name
+ * @param id language id
+ */
+ public Language(String name, String id)
+ {
+ super();
+ this.name = name;
+ this.id = id;
+ }
+
+ public String getName()
+ {
+ return name;
+ }
+
+ public void setName(String name)
+ {
+ this.name = name;
+ }
+
+ public String getId()
+ {
+ return id;
+ }
+
+ public void setId(String id)
+ {
+ this.id = id;
+ }
+
+}
diff --git a/src/plugins/devices.services/src/com/motorola/studio/android/devices/services/lang/model/LocationComposite.java b/src/plugins/devices.services/src/com/motorola/studio/android/devices/services/lang/model/LocationComposite.java
new file mode 100644
index 0000000..ae2a97b
--- /dev/null
+++ b/src/plugins/devices.services/src/com/motorola/studio/android/devices/services/lang/model/LocationComposite.java
@@ -0,0 +1,321 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.devices.services.lang.model;
+
+import java.util.List;
+
+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.Combo;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Label;
+
+import com.motorola.studio.android.devices.services.i18n.ServicesNLS;
+
+/**
+ * Composite containing the current language in use and two combo boxes where
+ * the user can choose a language and country
+ */
+public class LocationComposite extends Composite
+{
+ // id for listeners
+ static final int LOCATION_CHANGE = 1234;
+
+ // selected language
+ private String language = "";
+
+ // selected country
+ private String country = "";
+
+ // language combobox
+ private Combo comboLanguage = null;
+
+ // country combobox
+ private Combo comboCountry = null;
+
+ /**
+ * Constructor
+ *
+ * @param parent the parent composite
+ */
+ public LocationComposite(Composite parent)
+ {
+ this(parent, null, null);
+ }
+
+ /**
+ * Constructor
+ *
+ * @param parent the parent composite
+ * @param currentLanguageId the id of the current language in use by given emulator instance.
+ * @param currentCountryId the id of the current country in use by given emulator instance.
+ */
+ public LocationComposite(Composite parent, String currentLanguageId, String currentCountryId)
+ {
+ super(parent, SWT.NONE);
+
+ Label label;
+ GridLayout gridLayout = new GridLayout();
+ gridLayout.numColumns = 2;
+ this.setLayout(gridLayout);
+
+ /*
+ * Language label and combobox
+ */
+ label = new Label(this, SWT.LEFT);
+ label.setText(ServicesNLS.UI_Language);
+
+ comboLanguage = new Combo(this, SWT.DROP_DOWN | SWT.READ_ONLY);
+ comboLanguage.addSelectionListener(new SelectionAdapter()
+ {
+ @Override
+ public void widgetSelected(SelectionEvent event)
+ {
+ language = comboLanguage.getText();
+ notifyListeners(LOCATION_CHANGE, null);
+ }
+ });
+ if ((currentLanguageId != null) && (!currentLanguageId.equals("")))
+ {
+ fillComboBoxAndSelectCurrentLanguage(currentLanguageId);
+ }
+ else
+ {
+ comboLanguage.setItems(getLanguagesList());
+ }
+ language = comboLanguage.getText();
+ comboLanguage.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
+ comboLanguage.setVisibleItemCount(20);
+
+ /*
+ * Country label and combobox
+ */
+ label = new Label(this, SWT.LEFT);
+ label.setText(ServicesNLS.UI_Country);
+
+ comboCountry = new Combo(this, SWT.DROP_DOWN | SWT.READ_ONLY);
+ comboCountry.addSelectionListener(new SelectionAdapter()
+ {
+ @Override
+ public void widgetSelected(SelectionEvent event)
+ {
+ country = comboCountry.getText();
+ notifyListeners(LOCATION_CHANGE, null);
+ }
+ });
+ if ((currentCountryId != null) && (!currentCountryId.equals("")))
+ {
+ fillComboBoxAndSelectCurrentCountry(currentCountryId);
+ }
+ else
+ {
+ comboCountry.setItems(getCountriesList());
+ }
+ country = comboCountry.getText();
+ comboCountry.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
+ comboCountry.setVisibleItemCount(20);
+ }
+
+ /*
+ * Sets an array of languages in the language combo box and select that item which is
+ * the current in use by emulator instance. <p>
+ *
+ * There are some needed explanations here: <p>
+ *
+ * 1. The currentLanguageId parameter was obtained through an ADB shell command sent
+ * to emulator. The ID returned is a String similar to "pt", "en", "fr" for Portuguese,
+ * English and French respectively. For more details, please see: <br>
+ * com.motorola.studio.android.adt.DDMSUtils.getCurrentEmulatorLanguageAndCountry
+ * method. <p>
+ *
+ * 2. The currentLanguageId parameter is passed to getLanguagesList method in order to
+ * be returned an array of Strings holding all languages available and, at the
+ * length-plus-one position of this array, the number representing the index of
+ * array regarding the current language name in use by emulator. <p>
+ *
+ * 3. The language combo box is filled with all languages contained in langNamesAndIndex
+ * array. Then the number, held at last position of array, is passed as parameter to
+ * select() method of the combo box. Thus, the current language appears as selected
+ * when combo box is rendered. <p>
+ *
+ * @param currentLanguageId The ID of the current language in use by emulator
+ * instance.
+ */
+ private void fillComboBoxAndSelectCurrentLanguage(String currentLanguageId)
+ {
+ String[] langNamesAndIndex = getLanguagesList(currentLanguageId);
+ int index = langNamesAndIndex.length - 1;
+ int currentLanguageIndex = Integer.parseInt(langNamesAndIndex[index]);
+ comboLanguage.setItems(langNamesAndIndex);
+ comboLanguage.remove(index);
+ comboLanguage.select(currentLanguageIndex);
+ }
+
+ /*
+ * Sets an array of countries in the country combo box and select that item which is
+ * the current in use by emulator instance. <p>
+ *
+ * There are some needed explanations here: <p>
+ *
+ * 1. The currentCountryId parameter was obtained through an ADB shell command sent
+ * to emulator. The ID returned is a String similar to "it", "us", "ru" for Italy,
+ * USA and Russia respectively. For more details, please see: <br>
+ * com.motorola.studio.android.adt.DDMSUtils.getCurrentEmulatorLanguageAndCountry
+ * method. <p>
+ *
+ * 2. The currentCountryId parameter is passed to getCountriesList method in order to
+ * be returned an array of Strings holding all country available and, at the
+ * length-plus-one position of this array, the number representing the index of
+ * array regarding the current country name in use by emulator. <p>
+ *
+ * 3. The country combo box is filled with all countries contained in countryNamesAndIndex
+ * array. Then the number, held at last position of array, is passed as parameter to
+ * select() method of the combo box. Thus, the current country appears as selected
+ * when combo box is rendered. <p>
+ *
+ * @param currentCountryId The ID of the current country in use by emulator
+ * instance.
+ */
+ private void fillComboBoxAndSelectCurrentCountry(String currentCountryId)
+ {
+ String[] countryNamesAndIndex = getCountriesList(currentCountryId);
+ int index = countryNamesAndIndex.length - 1;
+ int currentCountryIndex = Integer.parseInt(countryNamesAndIndex[index]);
+ comboCountry.setItems(countryNamesAndIndex);
+ comboCountry.remove(index);
+ comboCountry.select(currentCountryIndex);
+ }
+
+ /*
+ * Get the list of languages to be used to populate the combobox
+ *
+ * @return the list of languages to be used to populate the combobox
+ */
+ private String[] getLanguagesList()
+ {
+ List<Language> languageObjs = Language.getLanguageList();
+ String[] languages = new String[languageObjs.size()];
+
+ int i = 0;
+ for (Language language : languageObjs)
+ {
+ languages[i] = language.getName();
+ i++;
+ }
+ return languages;
+ }
+
+ /*
+ * Get the list of languages to be used to populate the combo box. Also,
+ * returns the index of the current language at last position of the array.
+ *
+ * @param currentlanguageId
+ * @return An array of Strings containing the list of languages and the
+ * current language index.
+ */
+ private String[] getLanguagesList(String currentlanguageId)
+ {
+ List<Language> languageObjs = Language.getLanguageList();
+ int size = languageObjs.size();
+ String[] langNamesAndIndex = new String[size + 1];
+ String languageIndex = null;
+ int i = 0;
+ for (Language language : languageObjs)
+ {
+ String name = language.getName();
+ String id = language.getId();
+ if (id.equalsIgnoreCase(currentlanguageId))
+ {
+ languageIndex = String.valueOf(i);
+ langNamesAndIndex[size] = languageIndex;
+ }
+ langNamesAndIndex[i] = name;
+ i++;
+ }
+ return langNamesAndIndex;
+ }
+
+ /*
+ * Get the list of countries to be used to populate the combobox
+ *
+ * @return the list of countries to be used to populate the combobox
+ */
+ private String[] getCountriesList()
+ {
+ List<Country> countryObjs = Country.getCountryList();
+ String[] countries = new String[countryObjs.size()];
+ int i = 0;
+ for (Country country : countryObjs)
+ {
+ countries[i] = country.getName();
+ i++;
+ }
+ return countries;
+ }
+
+ /*
+ * Get the list of countries to be used to populate the combo box. Also,
+ * returns the index of the current country at last position of the array.
+ *
+ * @param currentCountryId
+ * @return An array of Strings containing the list of countries and the
+ * current country index.
+ */
+ private String[] getCountriesList(String currentCountryId)
+ {
+ List<Country> countryObjs = Country.getCountryList();
+ int size = countryObjs.size();
+ String[] countryNamesAndIndex = new String[size + 1];
+ String countryIndex = null;
+ int i = 0;
+ for (Country country : countryObjs)
+ {
+ String name = country.getName();
+ String id = country.getId();
+ if (id.equalsIgnoreCase(currentCountryId))
+ {
+ countryIndex = String.valueOf(i);
+ countryNamesAndIndex[size] = countryIndex;
+ }
+ countryNamesAndIndex[i] = name;
+ i++;
+ }
+ return countryNamesAndIndex;
+ }
+
+ /**
+ * Get the selected language
+ *
+ * @return selected language name
+ */
+ public String getLanguage()
+ {
+ return language;
+ }
+
+ /**
+ * Get the selected country
+ *
+ * @return selected country name
+ */
+ public String getCountry()
+ {
+ return country;
+ }
+}
diff --git a/src/plugins/emulator/.classpath b/src/plugins/emulator/.classpath
new file mode 100644
index 0000000..a664140
--- /dev/null
+++ b/src/plugins/emulator/.classpath
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>
+ <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry exported="true" kind="lib" path="lib/commons-net-1.4.1.jar"/>
+ <classpathentry exported="true" kind="lib" path="lib/jakarta-oro-2.0.8.jar"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/src/plugins/emulator/.project b/src/plugins/emulator/.project
new file mode 100644
index 0000000..3b872fb
--- /dev/null
+++ b/src/plugins/emulator/.project
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>com.motorola.studio.android.emulator</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.ManifestBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.SchemaBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.pde.PluginNature</nature>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+</projectDescription>
diff --git a/src/plugins/emulator/META-INF/MANIFEST.MF b/src/plugins/emulator/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..107f2a7
--- /dev/null
+++ b/src/plugins/emulator/META-INF/MANIFEST.MF
@@ -0,0 +1,46 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: %pluginName
+Bundle-SymbolicName: com.motorola.studio.android.emulator;singleton:=true
+Bundle-Version: 5.0.0.qualifier
+Bundle-Activator: com.motorola.studio.android.emulator.EmulatorPlugin
+Bundle-Vendor: %providerName
+Bundle-RequiredExecutionEnvironment: JavaSE-1.6
+Bundle-Localization: plugin
+Require-Bundle: com.android.ide.eclipse.adt,
+ com.android.ide.eclipse.ddms,
+ com.motorola.studio.android,
+ com.motorola.studio.android.common,
+ org.eclipse.ui,
+ org.eclipse.core.runtime,
+ org.eclipse.core.net,
+ org.eclipse.sequoyah.device.common.utilities,
+ org.eclipse.sequoyah.device.framework,
+ org.eclipse.sequoyah.device.framework.ui,
+ org.eclipse.sequoyah.device.framework.wizard,
+ org.eclipse.sequoyah.vnc.vncviewer,
+ org.eclipse.sequoyah.vnc.protocol,
+ org.eclipse.sequoyah.vnc.vncviewer.vncviews,
+ com.android.ide.eclipse.base
+Export-Package: com.motorola.studio.android.emulator,
+ com.motorola.studio.android.emulator.core.devfrm,
+ com.motorola.studio.android.emulator.core.emulationui,
+ com.motorola.studio.android.emulator.core.exception,
+ com.motorola.studio.android.emulator.core.model,
+ com.motorola.studio.android.emulator.core.skin,
+ com.motorola.studio.android.emulator.core.utils,
+ com.motorola.studio.android.emulator.device,
+ com.motorola.studio.android.emulator.device.definition,
+ com.motorola.studio.android.emulator.device.handlers,
+ com.motorola.studio.android.emulator.device.instance,
+ com.motorola.studio.android.emulator.device.refresh,
+ com.motorola.studio.android.emulator.device.sync,
+ com.motorola.studio.android.emulator.i18n,
+ com.motorola.studio.android.emulator.logic,
+ com.motorola.studio.android.emulator.skin.android.parser,
+ com.motorola.studio.android.emulator.ui.view
+Bundle-ClassPath: lib/commons-net-1.4.1.jar,
+ lib/jakarta-oro-2.0.8.jar,
+ .
+Bundle-ActivationPolicy: lazy
+
diff --git a/src/plugins/emulator/build.properties b/src/plugins/emulator/build.properties
new file mode 100644
index 0000000..d6fd1d0
--- /dev/null
+++ b/src/plugins/emulator/build.properties
@@ -0,0 +1,27 @@
+#
+# Copyright (C) 2012 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+source.. = src/
+output.. = bin/
+bin.includes = META-INF/,\
+ .,\
+ plugin.xml,\
+ schema/,\
+ plugin.properties,\
+ resource/,\
+ lib/,\
+ icons/,\
+ res/
diff --git a/src/plugins/emulator/icons/adv-icon-16x16.png b/src/plugins/emulator/icons/adv-icon-16x16.png
new file mode 100644
index 0000000..b745e47
--- /dev/null
+++ b/src/plugins/emulator/icons/adv-icon-16x16.png
Binary files differ
diff --git a/src/plugins/emulator/icons/device.png b/src/plugins/emulator/icons/device.png
new file mode 100644
index 0000000..82fa84a
--- /dev/null
+++ b/src/plugins/emulator/icons/device.png
Binary files differ
diff --git a/src/plugins/emulator/icons/iniciate-icon-16x16.png b/src/plugins/emulator/icons/iniciate-icon-16x16.png
new file mode 100644
index 0000000..9db0afc
--- /dev/null
+++ b/src/plugins/emulator/icons/iniciate-icon-16x16.png
Binary files differ
diff --git a/src/plugins/emulator/icons/main-icon-16x16.png b/src/plugins/emulator/icons/main-icon-16x16.png
new file mode 100644
index 0000000..d8f439b
--- /dev/null
+++ b/src/plugins/emulator/icons/main-icon-16x16.png
Binary files differ
diff --git a/src/plugins/emulator/icons/notavailable-icon-16x16.png b/src/plugins/emulator/icons/notavailable-icon-16x16.png
new file mode 100644
index 0000000..55fd705
--- /dev/null
+++ b/src/plugins/emulator/icons/notavailable-icon-16x16.png
Binary files differ
diff --git a/src/plugins/emulator/icons/plate16.gif b/src/plugins/emulator/icons/plate16.gif
new file mode 100644
index 0000000..450f124
--- /dev/null
+++ b/src/plugins/emulator/icons/plate16.gif
Binary files differ
diff --git a/src/plugins/emulator/icons/refresh.png b/src/plugins/emulator/icons/refresh.png
new file mode 100644
index 0000000..ec901af
--- /dev/null
+++ b/src/plugins/emulator/icons/refresh.png
Binary files differ
diff --git a/src/plugins/emulator/icons/repair.png b/src/plugins/emulator/icons/repair.png
new file mode 100644
index 0000000..8deedbf
--- /dev/null
+++ b/src/plugins/emulator/icons/repair.png
Binary files differ
diff --git a/src/plugins/emulator/icons/start.png b/src/plugins/emulator/icons/start.png
new file mode 100644
index 0000000..1e51295
--- /dev/null
+++ b/src/plugins/emulator/icons/start.png
Binary files differ
diff --git a/src/plugins/emulator/icons/started-icon-16x16.png b/src/plugins/emulator/icons/started-icon-16x16.png
new file mode 100644
index 0000000..38f4f9e
--- /dev/null
+++ b/src/plugins/emulator/icons/started-icon-16x16.png
Binary files differ
diff --git a/src/plugins/emulator/icons/stop.png b/src/plugins/emulator/icons/stop.png
new file mode 100644
index 0000000..287a9e8
--- /dev/null
+++ b/src/plugins/emulator/icons/stop.png
Binary files differ
diff --git a/src/plugins/emulator/icons/stopped-icon-16x16.png b/src/plugins/emulator/icons/stopped-icon-16x16.png
new file mode 100644
index 0000000..4c77aee
--- /dev/null
+++ b/src/plugins/emulator/icons/stopped-icon-16x16.png
Binary files differ
diff --git a/src/plugins/emulator/icons/wizard-icon-64x64.png b/src/plugins/emulator/icons/wizard-icon-64x64.png
new file mode 100644
index 0000000..e24a377
--- /dev/null
+++ b/src/plugins/emulator/icons/wizard-icon-64x64.png
Binary files differ
diff --git a/src/plugins/emulator/lib/commons-net-1.4.1.jar b/src/plugins/emulator/lib/commons-net-1.4.1.jar
new file mode 100644
index 0000000..9666a92
--- /dev/null
+++ b/src/plugins/emulator/lib/commons-net-1.4.1.jar
Binary files differ
diff --git a/src/plugins/emulator/lib/jakarta-oro-2.0.8.jar b/src/plugins/emulator/lib/jakarta-oro-2.0.8.jar
new file mode 100644
index 0000000..23488d2
--- /dev/null
+++ b/src/plugins/emulator/lib/jakarta-oro-2.0.8.jar
Binary files differ
diff --git a/src/plugins/emulator/plugin.properties b/src/plugins/emulator/plugin.properties
new file mode 100644
index 0000000..023d769
--- /dev/null
+++ b/src/plugins/emulator/plugin.properties
@@ -0,0 +1,139 @@
+#
+# Copyright (C) 2012 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+#################################################################################
+#
+# Android Emulator Core Plugin properties
+#
+#################################################################################
+
+pluginName=MOTODEV Studio for Android Emulator
+providerName=Motorola Mobility, Inc.
+copyright=Copyright (C) 2012 The Android Open Source Project
+
+
+# Extension point related labels
+extensionPointSkin=Android Emulator Skin
+extensionPointVm=Android Emulator VM
+extensionPointEmulatorDefinition=Android Emulator Definition
+
+android.emulator.device.name=Android Virtual Device
+
+status.offlineNoData=Offline (no user data)
+status.offline=Offline
+status.online=Online
+status.notavailable=Not available
+
+android.emulator.device.wizard.name=Android Virtual Device
+android.emulator.device.property.page.name=Android Virtual Device
+android.emulator.device.property.page.startupOptions.name=Startup Options
+android.emulator.device.properties.page.name=Device Properties
+
+initEmulatorServiceName=Initialize Device
+
+command.newwizard.name=New Android Virtual Device
+
+toolbarRefreshList=Refresh Android Virtual Devices List
+
+newAvdWizardName=New Android Virtual Device...
+
+resetServiceName=Reset
+resetServiceDescription=Service to reset an Android Emulator
+stopServiceName=Stop
+stopDescription=Service to stop a Android Emulator
+startServiceName=Start
+startDescription=Service to start a Android Emulator
+repairServiceName=Repair
+repairDescription=Service to repair a Android Virtual Device
+
+# Perspective related labels
+androidEmulatorPerspective=Android Emulator
+
+# View related labels
+androidEmulatorViewCategory=Android Emulator
+androidEmulatorViewName=Android Emulator
+androidMainDisplayViewName=Emulator Main Display
+
+# View menu/toolbar related labels
+menuName=Android Emulator Menu
+submenuZoom=Zoom
+menuZoom25=25%
+menuZoom50=50%
+menuZoom75=75%
+menuZoom100=100%
+menuZoomFit=Fit To Window
+menuZoomIn = In
+menuZoomOut = Out
+menuZoomDefault = Default(100%)
+toolbarZoomIn = Zoom In
+toolbarZoomOut = Zoom Out
+
+# Switch layout labels
+submenuLayout=Emulator Layout
+toolbarSwitchLayout=Switch Layout
+menuSwitchToNextLayout=Switch to Next Layout
+
+# Wizard related labels
+android.emulator.wizard.category=Android Emulator
+emulatorDefName=Android Emulator 1.0
+
+StartUp_Options_DpiDevice_Label=Screen Resolution (dpi)
+StartUp_Options_DpiDevice_Description=Scale the resolution of the emulator to match the screen size of a physical device
+StartUp_Options_Scale_Label=Scale
+StartUp_Options_Scale_Description=Scale the emulator window
+StartUp_Options_BootAnim_Label=Disable Boot Animation
+StartUp_Options_BootAnim_Description=Disable the boot animation during emulator startup
+StartUp_Options_NoSkin_Label=Don't Show Emulator Skin
+StartUp_Options_NoSkin_Description=Disable the emulator's skin
+StartUp_Options_Cache_Label=Cache
+StartUp_Options_Cache_Description=Use a specific file as the working cache partition image
+StartUp_Options_UserData_Label=User Data
+StartUp_Options_UserData_Description=Use a specific file as the working user-data disk image
+StartUp_Options_InitData_Label=Init User Data
+StartUp_Options_InitData_Description=When resetting the user-data image (through -wipe-data), copy the contents of this file to the new user-data disk image. By default, the emulator copies -system/userdata.img.
+StartUp_Options_RAM_Label=RAM
+StartUp_Options_RAM_Description=Use a specific file as the ramdisk image.
+StartUp_Options_SDCard_Label=SD Card
+StartUp_Options_SDCard_Description=Use a specific file as the SD card image
+StartUp_Options_NoCache_Label=Disable Cache Partition
+StartUp_Options_NoCache_Description=Start the emulator without a cache partition
+StartUp_Options_WipeData_Label=Wipe User Data
+StartUp_Options_WipeData_Description=Reset the current user-data disk image (that is, the file specified by -datadir and -data, or the default file). The emulator deletes all data from the user-data image file, then copies the contents of the file at -inidata data to the image file before starting.
+StartUp_Options_DNSServer_Label=DNS Server
+StartUp_Options_DNSServer_Description=Use the specified DNS server(s)
+StartUp_Options_HTTPProxy_Label=HTTP Proxy
+StartUp_Options_HTTPProxy_Description=Make all TCP connections through a specified HTTP/HTTPS proxy
+StartUp_Options_NetDelay_Label=Network Latency
+StartUp_Options_NetDelay_Description=Set network latency emulation
+StartUp_Options_NetSpeed_Label=Network Speed
+StartUp_Options_NetSpeed_Description=Set network speed emulation
+StartUp_Options_Port_Label=Port Number
+StartUp_Options_Port_Description=Set the console port number for this emulator instance
+StartUp_Options_NetFast_Label=Max Network Speed / Min Network Latency
+StartUp_Options_NetFast_Description=Shortcut for -netspeed full -netdelay none
+StartUp_Options_OtherOptions_Label=Other Startup Options
+StartUp_Options_OtherOptions_Description=Other Emulator options
+StartUp_Options_NoJni_Label=Disable JNI checks
+StartUp_Options_NoJni_Description=Disable JNI checks in the Dalvik runtime
+StartUp_Options_GPS_Label=Redirect GPS
+StartUp_Options_GPS_Description=Redirect NMEA GPS to character device
+StartUp_Options_CPUDelay_Description=Slow down emulated CPU speed
+StartUp_Options_CPUDelay_Label=CPU Delay
+StartUp_UserInterface_Tab_Label=User Interface
+StartUp_DiskImages_Tab_Label=Disk Images
+StartUp_Network_Tab_Label=Network
+StartUp_System_Tab_Label=System
+StartUp_Others_Tab_Label=Others \ No newline at end of file
diff --git a/src/plugins/emulator/plugin.xml b/src/plugins/emulator/plugin.xml
new file mode 100644
index 0000000..d1efb79
--- /dev/null
+++ b/src/plugins/emulator/plugin.xml
@@ -0,0 +1,627 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?eclipse version="3.2"?>
+
+<!--
+ Copyright (C) 2012 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<plugin>
+ <extension-point id="skin" name="%extensionPointSkin" schema="schema/skin.exsd"/>
+ <extension-point id="deviceFramework" name="Android Supported Device Framework" schema="schema/deviceFramework.exsd"/>
+ <extension-point id="androidEmulatorDefinition" name="Android Emulator Definition" schema="schema/androidEmulatorDefinition.exsd"/>
+ <extension-point id="androidPerspectiveExtension" name="Android Perspective Extension" schema="schema/androidPerspectiveExtension.exsd"/>
+ <extension id="status.offlineNoData" point="org.eclipse.sequoyah.device.framework.status">
+ <status
+ canDeleteInstance="true"
+ canEditProperties="true"
+ defaultServiceId="com.motorola.studio.android.emulator.startService"
+ id="com.motorola.studio.android.emulator.status.offlineNoData"
+ image="icons/stopped-icon-16x16.png"
+ name="%status.offlineNoData">
+ </status>
+ </extension>
+ <extension id="status.offline" point="org.eclipse.sequoyah.device.framework.status">
+ <status
+ canDeleteInstance="true"
+ canEditProperties="true"
+ defaultServiceId="com.motorola.studio.android.emulator.startService"
+ id="com.motorola.studio.android.emulator.status.offline"
+ image="icons/stopped-icon-16x16.png"
+ name="%status.offline">
+ </status>
+ </extension>
+ <extension id="status.online" point="org.eclipse.sequoyah.device.framework.status">
+ <status
+ canDeleteInstance="false"
+ canEditProperties="true"
+ defaultServiceId="com.motorola.studio.android.emulator.stopService"
+ id="com.motorola.studio.android.emulator.status.online"
+ image="icons/started-icon-16x16.png"
+ name="%status.online">
+ </status>
+ </extension>
+ <extension id="status.notavailable" point="org.eclipse.sequoyah.device.framework.status">
+ <status
+ canDeleteInstance="true"
+ canEditProperties="false"
+ id="com.motorola.studio.android.emulator.status.notavailable"
+ image="icons/notavailable-icon-16x16.png"
+ name="%status.notavailable">
+ </status>
+ </extension>
+ <extension point="org.eclipse.ui.propertyPages">
+ <page class="com.motorola.studio.android.emulator.device.ui.AndroidPropertiesPage" id="android.emulator.device.property.page" name="%android.emulator.device.property.page.name">
+ <enabledWhen>
+ <instanceof value="com.motorola.studio.android.emulator.device.instance.AndroidDeviceInstance">
+ </instanceof>
+ </enabledWhen>
+ </page>
+ <page category="android.emulator.device.property.page" class="com.motorola.studio.android.emulator.device.ui.AndroidPropertiesStartupOptionsPage" id="android.emulator.device.property.page.startupOptions" name="%android.emulator.device.property.page.startupOptions.name">
+ <enabledWhen>
+ <instanceof value="com.motorola.studio.android.emulator.device.instance.AndroidDeviceInstance">
+ </instanceof>
+ </enabledWhen>
+ </page>
+ <page
+ category="android.emulator.device.property.page"
+ class="com.motorola.studio.android.emulator.device.ui.DevicePropertiesPage"
+ id="android.emulator.device.properties.page"
+ name="%android.emulator.device.properties.page.name">
+ <enabledWhen>
+ <instanceof
+ value="com.motorola.studio.android.emulator.device.instance.AndroidDeviceInstance">
+ </instanceof>
+ </enabledWhen>
+ </page>
+ </extension>
+ <extension id="initEmulatorService" name="initEmulatorService" point="org.eclipse.sequoyah.device.framework.service">
+ <service copyright="Copyright (C) 2012 The Android Open Source Project" description="Service to initialize an Android Emulator device" handler="com.motorola.studio.android.emulator.device.init.InitServiceHandler" icon="icons/iniciate-icon-16x16.png" id="com.motorola.studio.android.emulator.initEmulatorService" name="%initEmulatorServiceName" provider="Motorola Mobility, Inc." version="0.2.0" visible="false">
+ </service>
+ </extension>
+ <extension id="com.motorola.studio.android.emulator.androidDevice" name="%initEmulatorServiceName" point="org.eclipse.sequoyah.device.framework.serviceDefinition">
+ <service id="com.motorola.studio.android.emulator.initEmulatorService">
+ <status endId="com.motorola.studio.android.emulator.status.offline" haltId="com.motorola.studio.android.emulator.status.offlineNoData" startId="OFF">
+ </status></service>
+ </extension>
+ <extension point="com.motorola.studio.android.emulator.deviceFramework">
+ <deviceFramework class="com.motorola.studio.android.emulator.device.TmLDeviceFrameworkSupport">
+ </deviceFramework>
+ </extension>
+ <extension id="tmlInstanceViewContribution" point="com.motorola.studio.android.emulator.androidPerspectiveExtension">
+ <view area="devicemanagementviews" id="org.eclipse.sequoyah.device.framework.ui.InstanceMgtView">
+ </view>
+ <view area="emulationviews" id="com.android.ide.eclipse.ddms.views.DeviceView">
+ </view>
+ <view area="emulationviews" id="com.android.ide.eclipse.ddms.views.LogCatView">
+ </view>
+ <view area="emulationviews" id="com.android.ide.eclipse.ddms.views.ThreadView">
+ </view>
+ <view area="emulationviews" id="com.android.ide.eclipse.ddms.views.HeapView">
+ </view>
+ <view area="emulationviews" id="com.android.ide.eclipse.ddms.views.FileExplorerView">
+ </view>
+ <view area="emulationviews" id="com.android.ide.eclipse.ddms.views.EmulatorControlView">
+ </view>
+ </extension>
+ <extension id="androidDevice" name="androidDevice" point="org.eclipse.sequoyah.device.framework.deviceTypes">
+ <deviceType
+ dropSupportHandler="com.motorola.studio.android.emulator.device.EmulatorDropSupportHandler"
+ handler="com.motorola.studio.android.emulator.device.AndroidDeviceHandler"
+ icon="icons/plate16.gif"
+ id="com.motorola.studio.android.emulator.androidDevice"
+ isAbstract="false"
+ isPersistent="true"
+ label="%android.emulator.device.wizard.name"
+ name="%android.emulator.device.wizard.name">
+ </deviceType>
+ </extension>
+ <extension point="org.eclipse.sequoyah.device.framework.ui.newDeviceWizardPages">
+ <wizardPage id="com.motorola.studio.android.emulator.device.ui.wizard.mainPage" operationClass="com.motorola.studio.android.emulator.device.ui.wizard.WizardMainPageOperation" pageClass="com.motorola.studio.android.emulator.device.ui.wizard.WizardMainPage">
+ <deviceType deviceTypeId="com.motorola.studio.android.emulator.androidDevice">
+ </deviceType>
+ </wizardPage>
+ <wizardPage id="com.motorola.studio.android.emulator.device.ui.wizard.startupOptionsPage" pageClass="com.motorola.studio.android.emulator.device.ui.wizard.WizardStartupOptionsPage">
+ <deviceType deviceTypeId="com.motorola.studio.android.emulator.androidDevice">
+ </deviceType>
+ </wizardPage>
+ </extension>
+ <extension point="org.eclipse.ui.commands">
+ <command
+ defaultHandler="com.motorola.studio.android.emulator.device.refresh.InstancesListRefreshHandler"
+ id="com.motorola.studio.android.device.refresh.avd.list"
+ name="%toolbarRefreshList">
+ </command>
+ <command defaultHandler="com.motorola.studio.android.emulator.device.handlers.OpenNewDeviceWizardHandler" id="com.motorola.studio.android.device.new.avd.wizard" name="%newAvdWizardName">
+ </command>
+ </extension>
+ <extension point="org.eclipse.ui.menus">
+ <menuContribution locationURI="toolbar:org.eclipse.sequoyah.device.framework.ui.InstanceMgtView">
+ <command commandId="com.motorola.studio.android.device.refresh.avd.list" icon="icons/refresh.png" label="%toolbarRefreshList">
+ </command>
+ </menuContribution>
+ <menuContribution locationURI="menu:motorolaMenu?after=otherNewWizardsSeparator">
+ <command commandId="com.motorola.studio.android.device.new.avd.wizard" icon="icons/device.png" label="%newAvdWizardName" style="push">
+ </command>
+ </menuContribution>
+ </extension>
+ <extension
+ point="org.eclipse.ui.startup">
+ <startup
+ class="com.motorola.studio.android.emulator.device.CreateAVDOnStartupListener">
+ </startup>
+ <startup
+ class="com.motorola.studio.android.emulator.device.SequoyahInstanceBackward">
+ </startup>
+ </extension>
+ <extension
+ point="org.eclipse.sequoyah.device.framework.ui.deviceManagerView">
+ <Style
+ useDropDown="true"
+ viewLayout="horizontal">
+ </Style>
+ </extension>
+ <extension
+ id="resetService"
+ name="resetService"
+ point="org.eclipse.sequoyah.device.framework.service">
+ <service
+ copyright="%copyright"
+ description="%resetServiceDescription"
+ handler="com.motorola.studio.android.emulator.service.reset.ResetServiceHandler"
+ icon="resource/reset.png"
+ id="com.motorola.studio.android.emulator.resetService"
+ name="%resetServiceName"
+ parallelized="true"
+ provider="%providerName"
+ version="0.2.0"
+ visible="true">
+ </service>
+ </extension>
+ <extension
+ id="com.motorola.studio.android.emulator.androidDevice"
+ name="%resetServiceName"
+ point="org.eclipse.sequoyah.device.framework.serviceDefinition">
+ <service
+ id="com.motorola.studio.android.emulator.resetService">
+ <status
+ endId="com.motorola.studio.android.emulator.status.offlineNoData"
+ haltId="com.motorola.studio.android.emulator.status.offline"
+ startId="com.motorola.studio.android.emulator.status.offline">
+ </status></service>
+ </extension>
+ <extension
+ id="stopService"
+ name="%stopServiceName"
+ point="org.eclipse.sequoyah.device.framework.service">
+ <service
+ copyright="%copyright"
+ description="%stopDescription"
+ handler="com.motorola.studio.android.emulator.service.stop.StopEmulatorHandler"
+ icon="icons/stop.png"
+ id="com.motorola.studio.android.emulator.stopService"
+ name="%stopServiceName"
+ parallelized="true"
+ provider="%providerName"
+ version="0.2.0"
+ visible="true">
+ </service>
+ </extension>
+ <extension
+ id="com.motorola.studio.android.emulator.androidDevice"
+ name="%stopServiceName"
+ point="org.eclipse.sequoyah.device.framework.serviceDefinition">
+ <service
+ id="com.motorola.studio.android.emulator.stopService">
+ <status
+ endId="com.motorola.studio.android.emulator.status.offline"
+ haltId="com.motorola.studio.android.emulator.device.status.online"
+ startId="com.motorola.studio.android.emulator.status.online">
+ </status>
+ </service>
+ </extension>
+ <extension
+ id="com.motorola.studio.android.emulator.androidDevice"
+ name="%repairServiceName"
+ point="org.eclipse.sequoyah.device.framework.serviceDefinition">
+ <service
+ id="com.motorola.studio.android.emulator.repairService">
+ <status
+ endId="com.motorola.studio.android.emulator.status.offline"
+ haltId="com.motorola.studio.android.emulator.status.notavailable"
+ startId="com.motorola.studio.android.emulator.status.notavailable">
+ </status>
+ </service>
+ </extension>
+ <extension
+ id="repairService"
+ name="%repairServiceName"
+ point="org.eclipse.sequoyah.device.framework.service">
+ <service
+ copyright="%copyright"
+ description="%repairDescription"
+ handler="com.motorola.studio.android.emulator.service.repair.RepairAvdHandler"
+ icon="icons/repair.png"
+ id="com.motorola.studio.android.emulator.repairService"
+ name="%repairServiceName"
+ parallelized="true"
+ provider="%providerName"
+ version="0.1.0"
+ visible="true">
+ </service>
+ </extension>
+ <extension
+ point="org.eclipse.ui.menus">
+ <menuContribution
+ locationURI="popup:com.motorola.studio.android.emulator.view.popup">
+ <command
+ commandId="com.motorola.studio.android.device.stop.emulator"
+ icon="icons/stop.png"
+ id="blah"
+ label="%stopServiceName"
+ style="push">
+ </command>
+ </menuContribution>
+ </extension>
+ <extension
+ point="org.eclipse.ui.commands">
+ <command
+ defaultHandler="com.motorola.studio.android.emulator.service.stop.StopEmulatorCommand"
+ id="com.motorola.studio.android.device.stop.emulator"
+ name="%stopServiceName">
+ </command>
+ </extension>
+ <extension
+ id="com.motorola.studio.android.emulator10.defaultEmulatorDefinitions"
+ name="Default Emulator Definitions"
+ point="com.motorola.studio.android.emulator.androidEmulatorDefinition">
+ <skin
+ id="com.motorola.studio.android.emulator.skin.android.androidSkin"
+ size="HVGA">
+ </skin>
+ <startLogic
+ class="com.motorola.studio.android.emulator10.StartAndroidEmulatorLogic">
+ </startLogic>
+ <inputLogic
+ class="com.motorola.studio.android.emulator.core.utils.TelnetAndroidInput"></inputLogic>
+ </extension>
+ <extension
+ id="startService"
+ name="%startServiceName"
+ point="org.eclipse.sequoyah.device.framework.service">
+ <service
+ copyright="%copyright"
+ description="%startDescription"
+ handler="com.motorola.studio.android.emulator.service.start.StartEmulatorHandler"
+ icon="icons/start.png"
+ id="com.motorola.studio.android.emulator.startService"
+ interval="1000"
+ name="%startServiceName"
+ parallelized="true"
+ provider="%providerName"
+ version="0.2.0"
+ visible="true">
+ </service>
+ </extension>
+ <extension
+ id="com.motorola.studio.android.emulator.androidDevice"
+ name="%startServiceName"
+ point="org.eclipse.sequoyah.device.framework.serviceDefinition">
+ <service
+ id="com.motorola.studio.android.emulator.startService">
+ <status
+ endId="com.motorola.studio.android.emulator.status.online"
+ haltId="com.motorola.studio.android.emulator.status.offlineNoData"
+ startId="com.motorola.studio.android.emulator.status.offlineNoData">
+ </status>
+ <status
+ endId="com.motorola.studio.android.emulator.status.online"
+ haltId="com.motorola.studio.android.emulator.status.offline"
+ startId="com.motorola.studio.android.emulator.status.offline">
+ </status></service>
+ </extension>
+ <extension
+ id="com.motorola.studio.android.emulator.skin.android.androidSkin"
+ name=""
+ point="com.motorola.studio.android.emulator.skin">
+ <skinInfo
+ class="com.motorola.studio.android.emulator.skin.android.AndroidSkin"
+ skinId="com.motorola.studio.android.emulator.skin.android.androidSkin"
+ skinName="Android Google Skin">
+ </skinInfo>
+ </extension>
+ <extension
+ point="org.eclipse.ui.perspectiveExtensions">
+ <perspectiveExtension
+ targetID="org.eclipse.debug.ui.DebugPerspective">
+ <viewShortcut
+ id="com.motorola.studio.android.emulator.androidView">
+ </viewShortcut>
+ <view
+ id="com.motorola.studio.android.emulator.androidView"
+ minimized="false"
+ ratio="0.7"
+ relationship="stack"
+ relative="org.eclipse.ui.views.ContentOutline"
+ showTitle="true"
+ visible="true">
+ </view>
+ </perspectiveExtension>
+ </extension>
+ <extension
+ point="org.eclipse.ui.views">
+ <view
+ category="studioAndroidViewCategory"
+ class="com.motorola.studio.android.emulator.ui.view.AndroidView"
+ icon="resource/emulator.png"
+ id="com.motorola.studio.android.emulator.androidView"
+ name="%androidEmulatorViewName">
+ </view>
+ </extension>
+ <extension
+ point="org.eclipse.ui.handlers">
+ <handler
+ class="com.motorola.studio.android.emulator.ui.handlers.ChangeZoomHandler"
+ commandId="com.motorola.studio.android.emulator.ui.change.zoom">
+ </handler>
+ <handler
+ class="com.motorola.studio.android.emulator.ui.handlers.ZoomInOutHandler"
+ commandId="com.motorola.studio.android.emulator.ui.zoomInOut">
+ </handler>
+ <handler
+ class="com.motorola.studio.android.emulator.ui.handlers.ShowViewHandler"
+ commandId="com.motorola.studio.android.emulator.ui.show.android.emulator.view">
+ </handler>
+ <!--handler
+ class="com.motorola.studio.android.emulator.ui.handlers.ChangeEmulatorOrientationHandler"
+ commandId="changeEmulatorOrientationCommand">
+ </handler-->
+ <handler
+ class="com.motorola.studio.android.emulator.ui.handlers.ChangeEmulatorOrientationHandler"
+ commandId="switchToNextLayoutCommand">
+ </handler>
+ </extension>
+
+
+ <extension
+ point="org.eclipse.ui.commands">
+ <command
+ id="com.motorola.studio.android.emulator.ui.show.android.emulator.view"
+ name="%androidEmulatorViewName">
+ <commandParameter
+ id="activeViewId"
+ name="activeViewId"
+ optional="false">
+ </commandParameter>
+ </command>
+ <command
+ id="switchToNextLayoutCommand"
+ name="%menuSwitchToNextLayout">
+ <commandParameter
+ id="activeViewId"
+ name="activeViewId"
+ optional="false">
+ </commandParameter>
+ </command>
+ <command
+ id="com.motorola.studio.android.emulator.ui.zoomInOut"
+ name="%submenuZoom">
+ <commandParameter
+ id="activeViewId"
+ name="activeViewId"
+ optional="false">
+ </commandParameter>
+ <commandParameter
+ id="zoomChangeFactor"
+ name="zoomChangeFactor"
+ optional="false">
+ </commandParameter>
+ </command>
+ <command
+ id="com.motorola.studio.android.emulator.ui.change.zoom"
+ name="%submenuZoom">
+ <commandParameter
+ id="activeViewId"
+ name="activeViewId"
+ optional="false">
+ </commandParameter>
+ <commandParameter
+ id="zoomFactor"
+ name="zoomFactor"
+ optional="true">
+ </commandParameter>
+ </command>
+ </extension>
+
+
+ <extension
+ point="org.eclipse.ui.menus">
+ <menuContribution
+ locationURI="menu:com.motorola.studio.android.emulator.androidView">
+ <menu
+ label="%submenuZoom">
+ <command
+ commandId="com.motorola.studio.android.emulator.ui.change.zoom"
+ label="%menuZoom25"
+ style="radio">
+ <parameter
+ name="activeViewId"
+ value="com.motorola.studio.android.emulator.androidView">
+ </parameter>
+ <parameter
+ name="zoomFactor"
+ value="0.25">
+ </parameter>
+ </command>
+ <command
+ commandId="com.motorola.studio.android.emulator.ui.change.zoom"
+ label="%menuZoom50"
+ style="radio">
+ <parameter
+ name="activeViewId"
+ value="com.motorola.studio.android.emulator.androidView">
+ </parameter>
+ <parameter
+ name="zoomFactor"
+ value="0.50">
+ </parameter>
+ </command>
+ <command
+ commandId="com.motorola.studio.android.emulator.ui.change.zoom"
+ label="%menuZoom75"
+ style="radio">
+ <parameter
+ name="activeViewId"
+ value="com.motorola.studio.android.emulator.androidView">
+ </parameter>
+ <parameter
+ name="zoomFactor"
+ value="0.75">
+ </parameter>
+ </command>
+ <command
+ commandId="com.motorola.studio.android.emulator.ui.change.zoom"
+ label="%menuZoom100"
+ style="radio">
+ <parameter
+ name="activeViewId"
+ value="com.motorola.studio.android.emulator.androidView">
+ </parameter>
+ <parameter
+ name="zoomFactor"
+ value="1.00">
+ </parameter>
+ </command>
+ <command
+ commandId="com.motorola.studio.android.emulator.ui.change.zoom"
+ label="%menuZoomFit"
+ style="radio">
+ <parameter
+ name="activeViewId"
+ value="com.motorola.studio.android.emulator.androidView">
+ </parameter>
+ <parameter
+ name="zoomFactor"
+ value="0.00">
+ </parameter>
+ </command>
+ <separator
+ name="separator"
+ visible="true">
+ </separator>
+
+ <command
+ commandId="com.motorola.studio.android.emulator.ui.zoomInOut"
+ icon="resource/zoom-in.png"
+ label="%menuZoomIn"
+ style="push">
+ <parameter
+ name="activeViewId"
+ value="com.motorola.studio.android.emulator.androidView">
+ </parameter>
+ <parameter
+ name="zoomChangeFactor"
+ value="0.25">
+ </parameter>
+ </command>
+ <command
+ commandId="com.motorola.studio.android.emulator.ui.zoomInOut"
+ icon="resource/zoom-out.png"
+ label="%menuZoomOut"
+ style="push">
+ <parameter
+ name="activeViewId"
+ value="com.motorola.studio.android.emulator.androidView">
+ </parameter>
+ <parameter
+ name="zoomChangeFactor"
+ value="-0.25">
+ </parameter>
+ </command>
+ </menu>
+ <separator
+ name="emulator.separator"
+ visible="true">
+ </separator>
+ <command
+ commandId="switchToNextLayoutCommand"
+ icon="resource/flip.png"
+ id="androidViewSwitchOrientation"
+ label="%toolbarSwitchLayout"
+ style="push"
+ tooltip="%toolbarSwitchLayout">
+ <parameter
+ name="activeViewId"
+ value="com.motorola.studio.android.emulator.androidView">
+ </parameter>
+ </command>
+ </menuContribution>
+
+
+ <menuContribution
+ locationURI="toolbar:com.motorola.studio.android.emulator.androidView">
+ <command
+ commandId="com.motorola.studio.android.emulator.ui.zoomInOut"
+ icon="resource/zoom-in.png"
+ label="%menuZoomIn"
+ style="push">
+ <parameter
+ name="activeViewId"
+ value="com.motorola.studio.android.emulator.androidView">
+ </parameter>
+ <parameter
+ name="zoomChangeFactor"
+ value="0.25">
+ </parameter>
+ </command>
+ <command
+ commandId="com.motorola.studio.android.emulator.ui.zoomInOut"
+ icon="resource/zoom-out.png"
+ label="%menuZoomOut"
+ style="push">
+ <parameter
+ name="activeViewId"
+ value="com.motorola.studio.android.emulator.androidView">
+ </parameter>
+ <parameter
+ name="zoomChangeFactor"
+ value="-0.25">
+ </parameter>
+ </command>
+ <separator
+ name="separator"
+ visible="true">
+ </separator>
+ <command
+ commandId="switchToNextLayoutCommand"
+ icon="resource/flip.png"
+ id="androidViewSwitchOrientation"
+ label="%toolbarSwitchLayout"
+ style="push"
+ tooltip="%toolbarSwitchLayout">
+ <parameter
+ name="activeViewId"
+ value="com.motorola.studio.android.emulator.androidView">
+ </parameter>
+ </command>
+ </menuContribution>
+ </extension>
+ <extension
+ point="org.eclipse.ui.newWizards">
+ <category
+ id="com.motorola.studio.android.emulator.ui.wizardCategory"
+ name="%android.emulator.wizard.category">
+ </category>
+ </extension>
+</plugin>
diff --git a/src/plugins/emulator/res/AVRCP.properties b/src/plugins/emulator/res/AVRCP.properties
new file mode 100644
index 0000000..fc8a06d
--- /dev/null
+++ b/src/plugins/emulator/res/AVRCP.properties
@@ -0,0 +1,23 @@
+#
+# Copyright (C) 2012 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+# AVRCP codes as in <dev_root>/development/emulator/keymaps/AVRCP.kl
+PLAYPAUSE=KEY_PLAY
+STOP=128
+NEXTSONG=163
+PREVIOUSSONG=165
+REWIND=168
+FORWARD=159
diff --git a/src/plugins/emulator/res/fbvncserver b/src/plugins/emulator/res/fbvncserver
new file mode 100644
index 0000000..1cb92e0
--- /dev/null
+++ b/src/plugins/emulator/res/fbvncserver
Binary files differ
diff --git a/src/plugins/emulator/res/pseudolayout b/src/plugins/emulator/res/pseudolayout
new file mode 100644
index 0000000..b501d93
--- /dev/null
+++ b/src/plugins/emulator/res/pseudolayout
@@ -0,0 +1,21 @@
+layouts {
+ default {
+ event EV_SW:0:1
+
+ uniqueRef1 {
+ name ___UNIQUE___
+ x 0
+ y 0
+ }
+ }
+
+ rotated {
+ event EV_SW:0:0
+
+ uniqueRef2 {
+ name ___UNIQUE___
+ x 0
+ rotation 3
+ }
+ }
+}
diff --git a/src/plugins/emulator/res/qwerty.properties b/src/plugins/emulator/res/qwerty.properties
new file mode 100644
index 0000000..221629f
--- /dev/null
+++ b/src/plugins/emulator/res/qwerty.properties
@@ -0,0 +1,105 @@
+#
+# Copyright (C) 2012 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+# QWERTY codes as in <dev_root>/development/emulator/keymaps/qwerty.kl
+GRAVE=399
+1=KEY_1
+2=KEY_2
+3=KEY_3
+4=KEY_4
+5=KEY_5
+6=KEY_6
+7=KEY_7
+8=KEY_8
+9=KEY_9
+0=KEY_0
+BACK=KEY_BACK
+SOFT_RIGHT=KEY_SOFT2
+ENDCALL=KEY_END
+MENU=KEY_SOFT1
+SEARCH=KEY_SEARCH
+POUND=KEY_SHARP
+STAR=KEY_STAR
+CALL=KEY_SEND
+DPAD_CENTER=KEY_CENTER
+DPAD_DOWN=KEY_DOWN
+DPAD_UP=KEY_UP
+HOME=KEY_HOME
+DPAD_LEFT=KEY_LEFT
+DPAD_RIGHT=KEY_RIGHT
+VOLUME_UP=KEY_VOLUMEUP
+VOLUME_DOWN=KEY_VOLUMEDOWN
+POWER=KEY_POWER
+CAMERA=KEY_CAMERA
+Q=KEY_Q
+W=KEY_W
+E=KEY_E
+R=KEY_R
+T=KEY_T
+Y=KEY_Y
+U=KEY_U
+I=KEY_I
+O=KEY_O
+P=KEY_P
+LEFT_BRACKET=KEY_LEFTBRACE
+RIGHT_BRACKET=KEY_RIGHTBRACE
+BACKSLASH=KEY_BACKSLASH
+A=KEY_A
+S=KEY_S
+D=KEY_D
+F=KEY_F
+G=KEY_G
+H=KEY_H
+J=KEY_J
+K=KEY_K
+L=KEY_L
+SEMICOLON=KEY_SEMICOLON
+APOSTROPHE=KEY_APOSTROPHE
+DEL=KEY_BACKSPACE
+Z=KEY_Z
+X=KEY_X
+C=KEY_C
+V=KEY_V
+B=KEY_B
+N=KEY_N
+M=KEY_M
+COMMA=KEY_COMMA
+PERIOD=KEY_DOT
+SLASH=KEY_SLASH
+ENTER=KEY_ENTER
+ALT_LEFT=KEY_LEFTALT
+ALT_RIGHT=KEY_RIGHTALT
+SHIFT_LEFT=KEY_LEFTSHIFT
+SHIFT_RIGHT=KEY_RIGHTSHIFT
+TAB=KEY_TAB
+SPACE=KEY_SPACE
+EXPLORER=KEY_WWW
+ENVELOPE=KEY_MAIL
+MINUS=KEY_MINUS
+EQUALS=KEY_EQUAL
+AT=KEY_EMAIL
+
+# Alternative names for existent keys (as retrieved from default layout files)
+CAP=KEY_LEFTSHIFT
+CAP2=KEY_RIGHTSHIFT
+ALT=KEY_LEFTALT
+ALT2=KEY_RIGHTALT
+
+# Missing keys (codes discovered by using the "adb shell getevent" command)
+SOFT_LEFT=KEY_SOFT1
+SYM=KEY_COMPOSE
+PHONE_DIAL=KEY_SEND
+PHONE_HANGUP=KEY_END \ No newline at end of file
diff --git a/src/plugins/emulator/resource/emulator.png b/src/plugins/emulator/resource/emulator.png
new file mode 100644
index 0000000..f5da49a
--- /dev/null
+++ b/src/plugins/emulator/resource/emulator.png
Binary files differ
diff --git a/src/plugins/emulator/resource/flip.png b/src/plugins/emulator/resource/flip.png
new file mode 100644
index 0000000..1a85ace
--- /dev/null
+++ b/src/plugins/emulator/resource/flip.png
Binary files differ
diff --git a/src/plugins/emulator/resource/main_display.png b/src/plugins/emulator/resource/main_display.png
new file mode 100644
index 0000000..a6cb2bd
--- /dev/null
+++ b/src/plugins/emulator/resource/main_display.png
Binary files differ
diff --git a/src/plugins/emulator/resource/receivebyemulator.png b/src/plugins/emulator/resource/receivebyemulator.png
new file mode 100644
index 0000000..92567bb
--- /dev/null
+++ b/src/plugins/emulator/resource/receivebyemulator.png
Binary files differ
diff --git a/src/plugins/emulator/resource/reset.png b/src/plugins/emulator/resource/reset.png
new file mode 100644
index 0000000..95a400f
--- /dev/null
+++ b/src/plugins/emulator/resource/reset.png
Binary files differ
diff --git a/src/plugins/emulator/resource/sentbyemulator.png b/src/plugins/emulator/resource/sentbyemulator.png
new file mode 100644
index 0000000..33b595d
--- /dev/null
+++ b/src/plugins/emulator/resource/sentbyemulator.png
Binary files differ
diff --git a/src/plugins/emulator/resource/startup_options.xml b/src/plugins/emulator/resource/startup_options.xml
new file mode 100644
index 0000000..9ea6f16
--- /dev/null
+++ b/src/plugins/emulator/resource/startup_options.xml
@@ -0,0 +1,141 @@
+<!--
+ Copyright (C) 2012 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+
+<startupOptions>
+
+ <group id="%StartUp_UserInterface_Tab_Label">
+
+ <startupOption name="-dpi-device" fName="%StartUp_Options_DpiDevice_Label" type="int">
+ <description>%StartUp_Options_DpiDevice_Description</description>
+ </startupOption>
+
+ <startupOption name="-scale" fName="%StartUp_Options_Scale_Label" type="text">
+ <description>%StartUp_Options_Scale_Description</description>
+ </startupOption>
+
+ <startupOption name="-no-boot-anim" fName="%StartUp_Options_BootAnim_Label" type="none">
+ <description>%StartUp_Options_BootAnim_Description</description>
+ </startupOption>
+
+ <startupOption name="-no-skin" fName="%StartUp_Options_NoSkin_Label" type="none">
+ <description>%StartUp_Options_NoSkin_Description</description>
+ </startupOption>
+
+ </group>
+
+
+ <group id="%StartUp_DiskImages_Tab_Label">
+
+ <startupOption name="-cache" fName="%StartUp_Options_Cache_Label" type="path" typeDetails=".img">
+ <description>%StartUp_Options_Cache_Description</description>
+ </startupOption>
+
+ <startupOption name="-data" fName="%StartUp_Options_UserData_Label" type="path" typeDetails=".img">
+ <description>%StartUp_Options_UserData_Description</description>
+ </startupOption>
+
+ <startupOption name="-initdata" fName="%StartUp_Options_InitData_Label" type="path" typeDetails=".img">
+ <description>%StartUp_Options_InitData_Description</description>
+ </startupOption>
+
+ <startupOption name="-ramdisk" fName="%StartUp_Options_RAM_Label" type="path" typeDetails=".img">
+ <description>%StartUp_Options_RAM_Description</description>
+ </startupOption>
+
+ <startupOption name="-sdcard" fName="%StartUp_Options_SDCard_Label" type="path" typeDetails=".img">
+ <description>%StartUp_Options_SDCard_Description</description>
+ </startupOption>
+
+ <startupOption name="-nocache" fName="%StartUp_Options_NoCache_Label" type="none">
+ <description>%StartUp_Options_NoCache_Description</description>
+ </startupOption>
+
+ <startupOption name="-wipe-data" fName="%StartUp_Options_WipeData_Label" type="none">
+ <description>%StartUp_Options_WipeData_Description</description>
+ </startupOption>
+
+ </group>
+
+
+ <group id="%StartUp_Network_Tab_Label">
+
+ <startupOption name="-dns-server" fName="%StartUp_Options_DNSServer_Label" type="text" typeDetails="csv">
+ <description>%StartUp_Options_DNSServer_Description</description>
+ </startupOption>
+
+ <startupOption name="-netdelay" fName="%StartUp_Options_NetDelay_Label" type="text">
+ <description>%StartUp_Options_NetDelay_Description</description>
+ <values>
+ <value>gprs</value>
+ <value>edge</value>
+ <value>umts</value>
+ <value>none</value>
+ </values>
+ </startupOption>
+
+ <startupOption name="-netspeed" fName="%StartUp_Options_NetSpeed_Label" type="text">
+ <description>%StartUp_Options_NetSpeed_Description</description>
+ <values>
+ <value>gsm</value>
+ <value>hscsd</value>
+ <value>gprs</value>
+ <value>edge</value>
+ <value>umts</value>
+ <value>hsdpa</value>
+ <value>hsdpa</value>
+ <value>full</value>
+ </values>
+ </startupOption>
+
+ <startupOption name="-port" fName="%StartUp_Options_Port_Label" type="int" typeDetails="5554;5584">
+ <description>%StartUp_Options_Port_Description</description>
+ </startupOption>
+
+ <startupOption name="-netfast" fName="%StartUp_Options_NetFast_Label" type="none">
+ <description>%StartUp_Options_NetFast_Description</description>
+ </startupOption>
+
+ </group>
+
+
+ <group id="%StartUp_System_Tab_Label">
+
+ <startupOption name="-cpu-delay" fName="%StartUp_Options_CPUDelay_Label" type="int" typeDetails="0;1000">
+ <description>%StartUp_Options_CPUDelay_Description</description>
+ </startupOption>
+
+ <startupOption name="-gps" fName="%StartUp_Options_GPS_Label" type="text">
+ <description>%StartUp_Options_GPS_Description</description>
+ </startupOption>
+
+ <startupOption name="-nojni" fName="%StartUp_Options_NoJni_Label" type="none">
+ <description>%StartUp_Options_NoJni_Description</description>
+ </startupOption>
+
+ </group>
+
+
+ <group id="%StartUp_Others_Tab_Label">
+
+ <startupOption name="other" fName="%StartUp_Options_OtherOptions_Label" type="text">
+ <description>%StartUp_Options_OtherOptions_Description</description>
+ </startupOption>
+
+ </group>
+
+
+</startupOptions> \ No newline at end of file
diff --git a/src/plugins/emulator/resource/zoom-in.png b/src/plugins/emulator/resource/zoom-in.png
new file mode 100644
index 0000000..6d28a34
--- /dev/null
+++ b/src/plugins/emulator/resource/zoom-in.png
Binary files differ
diff --git a/src/plugins/emulator/resource/zoom-out.png b/src/plugins/emulator/resource/zoom-out.png
new file mode 100644
index 0000000..0c9062a
--- /dev/null
+++ b/src/plugins/emulator/resource/zoom-out.png
Binary files differ
diff --git a/src/plugins/emulator/schema/androidEmulatorDefinition.exsd b/src/plugins/emulator/schema/androidEmulatorDefinition.exsd
new file mode 100644
index 0000000..cfc5558
--- /dev/null
+++ b/src/plugins/emulator/schema/androidEmulatorDefinition.exsd
@@ -0,0 +1,152 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<!-- Schema file written by PDE -->
+<schema targetNamespace="com.motorola.studio.android.emulator" xmlns="http://www.w3.org/2001/XMLSchema">
+<annotation>
+ <appInfo>
+ <meta.schema plugin="com.motorola.studio.android.emulator" id="androidEmulatorDefinition" name="Android Emulator Definition"/>
+ </appInfo>
+ <documentation>
+ [Enter description of this extension point.]
+ </documentation>
+ </annotation>
+
+ <element name="extension">
+ <annotation>
+ <appInfo>
+ <meta.element />
+ </appInfo>
+ </annotation>
+ <complexType>
+ <sequence>
+ <element ref="skin"/>
+ <element ref="startLogic"/>
+ <element ref="inputLogic"/>
+ </sequence>
+ <attribute name="point" type="string" use="required">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ </annotation>
+ </attribute>
+ <attribute name="id" type="string" use="required">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ </annotation>
+ </attribute>
+ <attribute name="name" type="string" use="required">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ <appInfo>
+ <meta.attribute translatable="true"/>
+ </appInfo>
+ </annotation>
+ </attribute>
+ </complexType>
+ </element>
+
+ <element name="skin">
+ <complexType>
+ <attribute name="id" type="string" use="required">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ </annotation>
+ </attribute>
+ <attribute name="size" use="required">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ </annotation>
+ <simpleType>
+ <restriction base="string">
+ <enumeration value="HVGA">
+ </enumeration>
+ <enumeration value="HVGA-L">
+ </enumeration>
+ <enumeration value="HVGA-P">
+ </enumeration>
+ <enumeration value="QVGA-L">
+ </enumeration>
+ <enumeration value="QVGA-P">
+ </enumeration>
+ </restriction>
+ </simpleType>
+ </attribute>
+ </complexType>
+ </element>
+
+ <element name="startLogic">
+ <complexType>
+ <attribute name="class" type="string" use="required">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ <appInfo>
+ <meta.attribute kind="java" basedOn="com.motorola.studio.android.emulator.logic.AbstractStartAndroidEmulatorLogic:"/>
+ </appInfo>
+ </annotation>
+ </attribute>
+ </complexType>
+ </element>
+
+ <element name="inputLogic">
+ <complexType>
+ <attribute name="class" type="string">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ <appInfo>
+ <meta.attribute kind="java" basedOn=":com.motorola.studio.android.emulator.core.model.IInputLogic"/>
+ </appInfo>
+ </annotation>
+ </attribute>
+ </complexType>
+ </element>
+
+ <annotation>
+ <appInfo>
+ <meta.section type="since"/>
+ </appInfo>
+ <documentation>
+ [Enter the first release in which this extension point appears.]
+ </documentation>
+ </annotation>
+
+ <annotation>
+ <appInfo>
+ <meta.section type="examples"/>
+ </appInfo>
+ <documentation>
+ [Enter extension point usage example here.]
+ </documentation>
+ </annotation>
+
+ <annotation>
+ <appInfo>
+ <meta.section type="apiinfo"/>
+ </appInfo>
+ <documentation>
+ [Enter API information here.]
+ </documentation>
+ </annotation>
+
+ <annotation>
+ <appInfo>
+ <meta.section type="implementation"/>
+ </appInfo>
+ <documentation>
+ [Enter information about supplied implementation of this extension point.]
+ </documentation>
+ </annotation>
+
+
+</schema>
diff --git a/src/plugins/emulator/schema/androidPerspectiveExtension.exsd b/src/plugins/emulator/schema/androidPerspectiveExtension.exsd
new file mode 100644
index 0000000..caeefd3
--- /dev/null
+++ b/src/plugins/emulator/schema/androidPerspectiveExtension.exsd
@@ -0,0 +1,82 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<!-- Schema file written by PDE -->
+<schema targetNamespace="com.motorola.studio.android.emulator" xmlns="http://www.w3.org/2001/XMLSchema">
+<annotation>
+ <appInfo>
+ <meta.schema plugin="com.motorola.studio.android.emulator" id="androidPerspectiveExtension" name="Android Perspective Extension"/>
+ </appInfo>
+ <documentation>
+
+ </documentation>
+ </annotation>
+
+ <element name="extension">
+ <annotation>
+ <appInfo>
+ <meta.element />
+ </appInfo>
+ </annotation>
+ <complexType>
+ <sequence minOccurs="1" maxOccurs="unbounded">
+ <element ref="view"/>
+ </sequence>
+ <attribute name="point" type="string" use="required">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ </annotation>
+ </attribute>
+ <attribute name="id" type="string" use="required">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ </annotation>
+ </attribute>
+ <attribute name="name" type="string">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ <appInfo>
+ <meta.attribute translatable="true"/>
+ </appInfo>
+ </annotation>
+ </attribute>
+ </complexType>
+ </element>
+
+ <element name="view">
+ <complexType>
+ <attribute name="id" type="string" use="required">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ </annotation>
+ </attribute>
+ <attribute name="area" use="required">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ </annotation>
+ <simpleType>
+ <restriction base="string">
+ <enumeration value="emulationviews">
+ </enumeration>
+ <enumeration value="devicemanagementviews">
+ </enumeration>
+ </restriction>
+ </simpleType>
+ </attribute>
+ </complexType>
+ </element>
+
+
+
+
+
+
+</schema>
diff --git a/src/plugins/emulator/schema/deviceFramework.exsd b/src/plugins/emulator/schema/deviceFramework.exsd
new file mode 100644
index 0000000..f7a1f8c
--- /dev/null
+++ b/src/plugins/emulator/schema/deviceFramework.exsd
@@ -0,0 +1,102 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<!-- Schema file written by PDE -->
+<schema targetNamespace="com.motorola.studio.android.emulator" xmlns="http://www.w3.org/2001/XMLSchema">
+<annotation>
+ <appInfo>
+ <meta.schema plugin="com.motorola.studio.android.emulator" id="deviceFramework" name="Device Framework"/>
+ </appInfo>
+ <documentation>
+ [Enter description of this extension point.]
+ </documentation>
+ </annotation>
+
+ <element name="extension">
+ <annotation>
+ <appInfo>
+ <meta.element />
+ </appInfo>
+ </annotation>
+ <complexType>
+ <sequence minOccurs="1" maxOccurs="unbounded">
+ <element ref="deviceFramework" minOccurs="1" maxOccurs="unbounded"/>
+ </sequence>
+ <attribute name="point" type="string" use="required">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ </annotation>
+ </attribute>
+ <attribute name="id" type="string">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ </annotation>
+ </attribute>
+ <attribute name="name" type="string">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ <appInfo>
+ <meta.attribute translatable="true"/>
+ </appInfo>
+ </annotation>
+ </attribute>
+ </complexType>
+ </element>
+
+ <element name="deviceFramework">
+ <complexType>
+ <attribute name="class" type="string" use="required">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ <appInfo>
+ <meta.attribute kind="java" basedOn=":com.motorola.studio.android.emulator.core.devfrm.IDeviceFrameworkSupport"/>
+ </appInfo>
+ </annotation>
+ </attribute>
+ </complexType>
+ </element>
+
+ <annotation>
+ <appInfo>
+ <meta.section type="since"/>
+ </appInfo>
+ <documentation>
+ [Enter the first release in which this extension point appears.]
+ </documentation>
+ </annotation>
+
+ <annotation>
+ <appInfo>
+ <meta.section type="examples"/>
+ </appInfo>
+ <documentation>
+ [Enter extension point usage example here.]
+ </documentation>
+ </annotation>
+
+ <annotation>
+ <appInfo>
+ <meta.section type="apiinfo"/>
+ </appInfo>
+ <documentation>
+ [Enter API information here.]
+ </documentation>
+ </annotation>
+
+ <annotation>
+ <appInfo>
+ <meta.section type="implementation"/>
+ </appInfo>
+ <documentation>
+ [Enter information about supplied implementation of this extension point.]
+ </documentation>
+ </annotation>
+
+
+</schema>
diff --git a/src/plugins/emulator/schema/skin.exsd b/src/plugins/emulator/schema/skin.exsd
new file mode 100644
index 0000000..9f0093b
--- /dev/null
+++ b/src/plugins/emulator/schema/skin.exsd
@@ -0,0 +1,84 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<!-- Schema file written by PDE -->
+<schema targetNamespace="com.motorola.studio.android.emulator" xmlns="http://www.w3.org/2001/XMLSchema">
+<annotation>
+ <appInfo>
+ <meta.schema plugin="com.motorola.studio.android.emulator" id="skin" name="Android Emulator Skin"/>
+ </appInfo>
+ <documentation>
+
+ </documentation>
+ </annotation>
+
+ <element name="extension">
+ <annotation>
+ <appInfo>
+ <meta.element />
+ </appInfo>
+ </annotation>
+ <complexType>
+ <sequence>
+ <element ref="skinInfo"/>
+ </sequence>
+ <attribute name="point" type="string" use="required">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ </annotation>
+ </attribute>
+ <attribute name="id" type="string" use="required">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ </annotation>
+ </attribute>
+ <attribute name="name" type="string">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ <appInfo>
+ <meta.attribute translatable="true"/>
+ </appInfo>
+ </annotation>
+ </attribute>
+ </complexType>
+ </element>
+
+ <element name="skinInfo">
+ <complexType>
+ <attribute name="skinId" type="string" use="required">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ </annotation>
+ </attribute>
+ <attribute name="skinName" type="string" use="required">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ </annotation>
+ </attribute>
+ <attribute name="class" type="string" use="required">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ <appInfo>
+ <meta.attribute kind="java" basedOn=":com.motorola.studio.android.emulator.core.skin.IAndroidSkin"/>
+ </appInfo>
+ </annotation>
+ </attribute>
+ </complexType>
+ </element>
+
+
+
+
+
+
+</schema>
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/EmulatorPlugin.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/EmulatorPlugin.java
new file mode 100644
index 0000000..eaf7512
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/EmulatorPlugin.java
@@ -0,0 +1,456 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+package com.motorola.studio.android.emulator;
+
+import static com.motorola.studio.android.common.log.StudioLogger.error;
+import static com.motorola.studio.android.common.log.StudioLogger.info;
+
+import java.util.List;
+import java.util.Properties;
+
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.sequoyah.device.common.utilities.BasePlugin;
+import org.eclipse.sequoyah.device.framework.events.IInstanceListener;
+import org.eclipse.sequoyah.device.framework.events.InstanceAdapter;
+import org.eclipse.sequoyah.device.framework.events.InstanceEvent;
+import org.eclipse.sequoyah.device.framework.events.InstanceEvent.InstanceEventType;
+import org.eclipse.sequoyah.device.framework.events.InstanceEventManager;
+import org.eclipse.sequoyah.device.framework.factory.DeviceTypeRegistry;
+import org.eclipse.sequoyah.device.framework.model.IDeviceType;
+import org.eclipse.sequoyah.device.framework.model.IService;
+import org.eclipse.sequoyah.device.framework.model.handler.IServiceHandler;
+import org.eclipse.sequoyah.device.framework.model.handler.ServiceHandler;
+import org.eclipse.sequoyah.device.framework.ui.DeviceUIPlugin;
+import org.eclipse.sequoyah.device.framework.ui.view.InstanceMgtView;
+import org.eclipse.sequoyah.device.framework.ui.wizard.DefaultDeviceTypeMenuWizardPage;
+import org.eclipse.ui.plugin.AbstractUIPlugin;
+import org.osgi.framework.BundleContext;
+
+import com.motorola.studio.android.AndroidPlugin;
+import com.motorola.studio.android.adt.DDMSFacade;
+import com.motorola.studio.android.adt.DdmsRunnable;
+import com.motorola.studio.android.adt.StudioAndroidEventManager;
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.common.preferences.DialogWithToggleUtils;
+import com.motorola.studio.android.emulator.core.devfrm.DeviceFrameworkManager;
+import com.motorola.studio.android.emulator.core.model.IAndroidEmulatorInstance;
+import com.motorola.studio.android.emulator.device.AndroidDeviceUtils;
+import com.motorola.studio.android.emulator.device.IDevicePropertiesConstants;
+import com.motorola.studio.android.emulator.device.SequoyahLogRedirector;
+import com.motorola.studio.android.emulator.device.instance.AndroidDevInstListener;
+import com.motorola.studio.android.emulator.device.instance.AndroidDeviceInstance;
+import com.motorola.studio.android.emulator.device.refresh.InstancesListRefresh;
+import com.motorola.studio.android.emulator.device.sync.DeviceViewsSync;
+import com.motorola.studio.android.emulator.i18n.EmulatorNLS;
+import com.motorola.studio.android.emulator.ui.view.AbstractAndroidView;
+
+/**
+ * The activator class controls the plug-in life cycle
+ */
+public class EmulatorPlugin extends AbstractUIPlugin
+{
+ // The plug-in ID
+ public static final String PLUGIN_ID = "com.motorola.studio.android.emulator";
+
+ // The shared instance
+ private static EmulatorPlugin plugin;
+
+ // The ID of the device declared by this plug-in
+ public static final String DEVICE_ID = PLUGIN_ID + ".androidDevice";
+
+ // The ID of all the status declared by this plug-in
+ public static final String STATUS_ONLINE_ID = PLUGIN_ID + ".status.online";
+
+ public static final String STATUS_OFFLINE_NO_DATA = PLUGIN_ID + ".status.offlineNoData";
+
+ public static final String STATUS_OFFLINE = PLUGIN_ID + ".status.offline";
+
+ public static final String STATUS_NOT_AVAILABLE = PLUGIN_ID + ".status.notavailable";
+
+ public static final String SERVICE_INIT_ID = PLUGIN_ID + ".initEmulatorService";
+
+ public static final String STOP_SERVICE_ID = PLUGIN_ID + ".stopService";
+
+ public static final String START_SERVICE_ID = PLUGIN_ID + ".startService";
+
+ private static final String DEV_MANAGER_HELP = DeviceUIPlugin.PLUGIN_ID + ".devmgr";
+
+ private static final String NEW_DEVICE_HELP = DeviceUIPlugin.PLUGIN_ID + ".newdev";
+
+ /**
+ * Reference the id of the extension point with the default Android Emulator definitions...
+ */
+ public static String DEFAULT_EMULATOR_DEFINITION =
+ "com.motorola.studio.android.emulator10.defaultEmulatorDefinitions";
+
+ public static final String FORCE_ATTR = "force";
+
+ public static final String EMULATOR_UNEXPECTEDLY_STOPPED = "emulator.unexpectedly.stopped";
+
+ private static AndroidDevInstListener instanceListener;
+
+ private static DdmsRunnable connectedListener = new DdmsRunnable()
+ {
+ @Override
+ public void run(String serialNumber)
+ {
+ if (DDMSFacade.isEmulator(serialNumber))
+ {
+ InstancesListRefresh.refresh();
+
+ info("New Device connected at " + serialNumber);
+
+ String vmName = DDMSFacade.getNameBySerialNumber(serialNumber);
+
+ if (vmName != null)
+ {
+ DeviceFrameworkManager devFrameworkManager =
+ DeviceFrameworkManager.getInstance();
+
+ IAndroidEmulatorInstance instance =
+ devFrameworkManager.getInstanceByName(vmName);
+
+ if (instance instanceof AndroidDeviceInstance)
+ {
+ final AndroidDeviceInstance emulatorInstance =
+ (AndroidDeviceInstance) instance;
+
+ AndroidDeviceUtils.fireDummyStartTransition(emulatorInstance, serialNumber);
+
+ }
+ }
+ }
+ }
+ };
+
+ private static DdmsRunnable disconnectedListener = new DdmsRunnable()
+ {
+ @Override
+ public void run(String serialNum)
+ {
+ if (DDMSFacade.isEmulator(serialNum))
+ {
+ info("Device just disconnected from serial=" + serialNum);
+
+ String vmName = DDMSFacade.getNameBySerialNumber(serialNum);
+
+ if (vmName != null)
+ {
+ IAndroidEmulatorInstance instance =
+ DeviceFrameworkManager.getInstance().getInstanceByName(vmName);
+
+ if ((instance != null) && (instance.isStarted()))
+ {
+ try
+ {
+ instance.stop(true);
+ DialogWithToggleUtils.showError(EMULATOR_UNEXPECTEDLY_STOPPED,
+ EmulatorNLS.GEN_Error, NLS.bind(
+ EmulatorNLS.ERR_AndroidLogicPlugin_EmulatorStopped,
+ instance.getName()));
+
+ }
+ catch (Exception e)
+ {
+ error("Error trying to force the stop process on instance associated to disconnected device: "
+ + instance);
+ }
+ }
+
+ if (instance instanceof AndroidDeviceInstance)
+ {
+ ((AndroidDeviceInstance) instance).setNameSuffix(null);
+ InstanceEventManager.getInstance().notifyListeners(
+ new InstanceEvent(InstanceEventType.INSTANCE_UPDATED,
+ (AndroidDeviceInstance) instance));
+ }
+ }
+ else
+ {
+ // This block is executed if we get a vmName == null condition. This can happen if
+ // ADT updates the device in a way that it makes the name not accessible.
+ //
+ // What is needed to be done in such a case is to iterate on all TmL instances, looking for
+ // objects that contain serialNumber as the instance suffix. This guarantees that we will not
+ // leave a not consistent serial number being displayed at the Instance Management view.
+
+ for (IAndroidEmulatorInstance instance : DeviceFrameworkManager.getInstance()
+ .getAllInstances())
+ {
+ if (instance instanceof AndroidDeviceInstance)
+ {
+ AndroidDeviceInstance androidInstance =
+ (AndroidDeviceInstance) instance;
+ String instanceSuffix = androidInstance.getNameSuffix();
+
+ if ((instanceSuffix != null) && instanceSuffix.equals(serialNum))
+ {
+ androidInstance.setNameSuffix(null);
+
+ InstanceEventManager.getInstance().notifyListeners(
+ new InstanceEvent(InstanceEventType.INSTANCE_UPDATED,
+ androidInstance));
+ }
+ }
+ }
+ }
+ }
+ }
+ };
+
+ private static final Runnable sdkLoaderListener = new Runnable()
+ {
+ @Override
+ public void run()
+ {
+ InstancesListRefresh.refresh();
+ if (!Platform.getOS().equals(Platform.OS_MACOSX))
+ {
+ IPreferenceStore store = getDefault().getPreferenceStore();
+ boolean deviceStartupOptionsUpdated =
+ store.getBoolean("DeviceStartupOptionsUpdated");
+ if (!deviceStartupOptionsUpdated)
+ {
+ for (IAndroidEmulatorInstance instance : DeviceFrameworkManager.getInstance()
+ .getAllInstances())
+ {
+ if (instance instanceof AndroidDeviceInstance)
+ {
+ AndroidDeviceInstance androidInstance =
+ (AndroidDeviceInstance) instance;
+
+ Properties emuProperties = androidInstance.getProperties();
+
+ String commandline =
+ emuProperties.getProperty(
+ IDevicePropertiesConstants.commandline, "");
+ if (commandline.contains("-no-window"))
+ {
+ commandline = commandline.replace("-no-window", "");
+ }
+ emuProperties.setProperty(IDevicePropertiesConstants.commandline,
+ commandline);
+ androidInstance.setProperties(emuProperties);
+
+ InstanceEventManager.getInstance().notifyListeners(
+ new InstanceEvent(InstanceEventType.INSTANCE_UPDATED,
+ androidInstance));
+ }
+ }
+ store.setValue("DeviceStartupOptionsUpdated", true);
+ }
+ }
+ }
+ };
+
+ private static IInstanceListener sequoyahInstanceListener = new InstanceAdapter()
+ {
+ @Override
+ public void instanceUpdated(InstanceEvent e)
+ {
+ AbstractAndroidView.updateInstanceName(e.getInstance());
+ }
+ };
+
+ private static ServiceHandler stopServiceHandler = null;
+
+ private static ServiceHandler startServiceHandler = null;
+
+ private static String stopServiceId = null;
+
+ private static String startServiceId = null;
+
+ /**
+ * The constructor
+ */
+ public EmulatorPlugin()
+ {
+ plugin = this;
+ }
+
+ /**
+ * Activates the plug-in and initializes the logger
+ *
+ * @see org.eclipse.ui.plugin.AbstractUIPlugin#start(org.osgi.framework.BundleContext)
+ */
+ @Override
+ public void start(BundleContext context) throws Exception
+ {
+ StudioLogger.debug(EmulatorPlugin.class, "Starting MOTODEV Android Emulator Plugin...");
+
+ super.start(context);
+
+ start();
+
+ StudioLogger.debug(EmulatorPlugin.class, "MOTODEV Android Emulator Plugin started.");
+ }
+
+ private void start()
+ {
+ // Setting the TmL logger to redirect logs to the logger controlled
+ // by this class
+ SequoyahLogRedirector tmlLogger = new SequoyahLogRedirector();
+ org.eclipse.sequoyah.vnc.utilities.logger.Logger.setLogger(tmlLogger);
+ BasePlugin.getBaseDefault().setLogger(tmlLogger);
+
+ instanceListener = new AndroidDevInstListener();
+ InstanceEventManager.getInstance().addInstanceListener(instanceListener);
+ StudioAndroidEventManager.asyncAddDeviceChangeListeners(connectedListener,
+ disconnectedListener);
+
+ AndroidPlugin.getDefault().addSDKLoaderListener(sdkLoaderListener);
+ // Emulator Views synchronization
+ DeviceViewsSync.getInstance().initialize();
+ // Setting context sensitive help IDs for the TmL screens we use
+ DefaultDeviceTypeMenuWizardPage.setHelpContextId(NEW_DEVICE_HELP);
+ InstanceMgtView.setHelp(DEV_MANAGER_HELP);
+ InstanceEventManager.getInstance().addInstanceListener(sequoyahInstanceListener);
+ registerStopServiceId(STOP_SERVICE_ID);
+ registerStartServiceId(START_SERVICE_ID);
+ }
+
+ /**
+ * @see org.eclipse.ui.plugin.AbstractUIPlugin#stop(org.osgi.framework.BundleContext)
+ */
+ @Override
+ public void stop(BundleContext context) throws Exception
+ {
+ AndroidPlugin.getDefault().removeSDKLoaderListener(sdkLoaderListener);
+ InstanceEventManager.getInstance().removeInstanceListener(instanceListener);
+ StudioAndroidEventManager.asyncRemoveDeviceChangeListeners(connectedListener,
+ disconnectedListener);
+ InstanceEventManager.getInstance().removeInstanceListener(sequoyahInstanceListener);
+ unregisterStopServiceHandler();
+ unregisterStartServiceHandler();
+ plugin = null;
+ super.stop(context);
+ }
+
+ /**
+ * Registers a stop service id, through which the stop service handler will be found and
+ * used to delegate stop action of the instances
+ * if possible
+ *
+ * @param stopServiceId The stop service id to be registered
+ */
+ public static void registerStopServiceId(String stopServiceId)
+ {
+ EmulatorPlugin.stopServiceId = stopServiceId;
+ }
+
+ /**
+ * Unregisters the current stop service handler and stop service id.
+ *
+ * After this method is called, it will not be possible for the instance class to delegate the
+ * stop action to a handler.
+ */
+ public static void unregisterStopServiceHandler()
+ {
+ stopServiceHandler = null;
+ stopServiceId = null;
+ }
+
+ /**
+ * Retrieves the stop service handler.
+ *
+ * @return The currently registered stop service handler, or <null> if no handler is registered.
+ */
+ public static ServiceHandler getStopServiceHandler()
+ {
+ if ((stopServiceHandler == null) && (stopServiceId != null))
+ {
+ // find the appropriate stop service handler
+ IDeviceType device =
+ DeviceTypeRegistry.getInstance().getDeviceTypeById(EmulatorPlugin.DEVICE_ID);
+ List<IService> services = device.getServices();
+ for (IService service : services)
+ {
+ IServiceHandler handler = service.getHandler();
+ if (handler.getService().getId().equals(stopServiceId))
+ {
+ stopServiceHandler = (ServiceHandler) handler;
+ break;
+ }
+ }
+ }
+
+ return stopServiceHandler;
+ }
+
+ /**
+ * Registers a start service id, through which the stop service handler will be found and
+ * used to delegate start action of the instances
+ * if possible
+ *
+ * @param stopServiceId The stop service id to be registered
+ */
+ public static void registerStartServiceId(String startServiceId)
+ {
+ EmulatorPlugin.startServiceId = startServiceId;
+ }
+
+ /**
+ * Unregisters the current start service handler and stop service id.
+ *
+ * After this method is called, it will not be possible for the instance class to delegate the
+ * start action to a handler.
+ */
+ public static void unregisterStartServiceHandler()
+ {
+ startServiceHandler = null;
+ startServiceId = null;
+ }
+
+ /**
+ * Retrieves the start service handler.
+ *
+ * @return The currently registered start service handler, or <null> if no handler is registered.
+ */
+ public static ServiceHandler getStartServiceHandler()
+ {
+ if ((startServiceHandler == null) && (startServiceId != null))
+ {
+ // find the appropriate stop service handler
+ IDeviceType device =
+ DeviceTypeRegistry.getInstance().getDeviceTypeById(EmulatorPlugin.DEVICE_ID);
+ List<IService> services = device.getServices();
+ for (IService service : services)
+ {
+ IServiceHandler handler = service.getHandler();
+ if (handler.getService().getId().equals(startServiceId))
+ {
+ startServiceHandler = (ServiceHandler) handler;
+ break;
+ }
+ }
+ }
+
+ return startServiceHandler;
+ }
+
+ /**
+ * Returns the shared instance
+ *
+ * @return the shared instance
+ */
+ public static EmulatorPlugin getDefault()
+ {
+ return plugin;
+ }
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/devfrm/DeviceFrameworkManager.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/devfrm/DeviceFrameworkManager.java
new file mode 100644
index 0000000..401d3ff
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/devfrm/DeviceFrameworkManager.java
@@ -0,0 +1,315 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+package com.motorola.studio.android.emulator.core.devfrm;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
+import java.util.Set;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IConfigurationElement;
+import org.eclipse.core.runtime.IExtension;
+import org.eclipse.core.runtime.IExtensionPoint;
+import org.eclipse.core.runtime.IExtensionRegistry;
+import org.eclipse.core.runtime.Platform;
+
+import com.motorola.studio.android.emulator.EmulatorPlugin;
+import com.motorola.studio.android.emulator.core.model.IAndroidEmulatorInstance;
+
+/**
+ * DESCRIPTION:
+ * This class manages the device frameworks that extend the deviceFramework
+ * extension
+ *
+ * RESPONSIBILITY:
+ * Retrieve all deviceFramework extension data and provide a compiled
+ * view of the information provided by each extension implementer
+ *
+ * COLABORATORS:
+ * None.
+ *
+ * USAGE:
+ * Use each public method to get the desired information
+ */
+public class DeviceFrameworkManager
+{
+ /*
+ * Extension point related ids section
+ */
+ private static final String DEV_FRAMEWORK_EXTENSION_POINT_ID =
+ EmulatorPlugin.PLUGIN_ID + ".deviceFramework";
+
+ private static final String DEV_FRAMEWORK_ELEM = "deviceFramework";
+
+ private static final String DEV_FRAMEWORK_IMPL_CLASS_ATTR = "class";
+
+ /**
+ * This is a singleton class. The instance is stored in this attribute
+ */
+ private static DeviceFrameworkManager instance;
+
+ /**
+ * A collection containing the classes provided by each extension
+ * implementer for retrieving framework data
+ */
+ private Collection<IDeviceFrameworkSupport> allFrameworks =
+ new HashSet<IDeviceFrameworkSupport>();
+
+ /**
+ * Singleton private constructor
+ */
+ private DeviceFrameworkManager()
+ {
+ populateModel();
+ }
+
+ /**
+ * Gets the instance of the class
+ *
+ * @return The instance of the class
+ */
+ public static DeviceFrameworkManager getInstance()
+ {
+ if (instance == null)
+ {
+ instance = new DeviceFrameworkManager();
+ }
+
+ return instance;
+ }
+
+ /**
+ * Retrieves all instances managed by every device framework
+ * which contributes with the deviceFramework extension point
+ *
+ * @return A collection containing all instances from all frameworks
+ */
+ public Collection<IAndroidEmulatorInstance> getAllInstances()
+ {
+ Collection<IAndroidEmulatorInstance> allInstancesSet =
+ new LinkedHashSet<IAndroidEmulatorInstance>();
+ for (IDeviceFrameworkSupport devFramework : allFrameworks)
+ {
+ Collection<IAndroidEmulatorInstance> devFrmInstances = devFramework.getAllInstances();
+ if (devFrmInstances != null)
+ {
+ allInstancesSet.addAll(devFrmInstances);
+ }
+ }
+
+ return allInstancesSet;
+ }
+
+ /**
+ * Retrieve all registered and available instances
+ *
+ * @return List containing all registered and available instances
+ */
+ public Collection<IAndroidEmulatorInstance> getAvailableInstances()
+ {
+ Collection<IAndroidEmulatorInstance> allInstances = getAllInstances();
+ Collection<IAndroidEmulatorInstance> enabledInstances =
+ new ArrayList<IAndroidEmulatorInstance>(allInstances.size());
+
+ for (IAndroidEmulatorInstance emulatorInstance : allInstances)
+ {
+ if (emulatorInstance.isAvailable())
+ {
+ enabledInstances.add(emulatorInstance);
+ }
+ }
+ return enabledInstances;
+ }
+
+ /**
+ * Retrieve a collection of names of all the IAndroidEmulatorInstance
+ * of all Device frameworks...
+ * @return A collection of all instances of IAndroidEmulatorInstance.
+ */
+ public Collection<String> getAllInstanceNames()
+ {
+ Collection<String> allInstancesNames = new LinkedHashSet<String>();
+ for (IDeviceFrameworkSupport devFramework : allFrameworks)
+ {
+ for (IAndroidEmulatorInstance instance : devFramework.getAllInstances())
+ {
+ allInstancesNames.add(instance.getName());
+ }
+ }
+
+ return allInstancesNames;
+
+ }
+
+ /**
+ * Retrieves the first occurrence of a IAndroidEmulatorInstance with the given name
+ * provided by any framework.
+ * @param name of the emulator instance to be retrieved.
+ * @return reference to a IAndroidEmulatorInstance with the given name or a null
+ * is there are no emulator instance with the given name.
+ */
+ public IAndroidEmulatorInstance getInstanceByName(String name)
+ {
+ IAndroidEmulatorInstance instanceToReturn = null;
+
+ for (IAndroidEmulatorInstance instance : getAllInstances())
+ {
+ if (Platform.getOS().equals(Platform.WS_WIN32))
+ {
+ if (instance.getName().toLowerCase().equals(name.toLowerCase()))
+ {
+ instanceToReturn = instance;
+ break;
+ }
+ }
+ else
+ {
+ if (instance.getName().equals(name))
+ {
+ instanceToReturn = instance;
+ break;
+ }
+ }
+
+ }
+ return instanceToReturn;
+
+ }
+
+ /**
+ * Retrieves all <b>started</b> instances managed by every device framework
+ * which contributes with the deviceFramework extension point
+ *
+ * @return A collection containing all started instances from all frameworks
+ */
+ public Collection<IAndroidEmulatorInstance> getAllStartedInstances()
+ {
+ Collection<IAndroidEmulatorInstance> startedInstancesSet =
+ new HashSet<IAndroidEmulatorInstance>();
+ for (IDeviceFrameworkSupport devFramework : allFrameworks)
+ {
+ Collection<IAndroidEmulatorInstance> devFrmInstances = devFramework.getAllInstances();
+ if (devFrmInstances != null)
+ {
+ for (IAndroidEmulatorInstance instance : devFrmInstances)
+ {
+ if (instance.isStarted())
+ {
+ startedInstancesSet.add(instance);
+ }
+ }
+ }
+ }
+
+ return startedInstancesSet;
+ }
+
+ /**
+ * Retrieves all <b>connected</b> instances managed by every device framework
+ * which contributes with the deviceFramework extension point
+ *
+ * @return A collection containing all connected instances from all frameworks
+ */
+ public Collection<IAndroidEmulatorInstance> getAllConnectedInstances()
+ {
+ Collection<IAndroidEmulatorInstance> connectedInstancesSet =
+ new HashSet<IAndroidEmulatorInstance>();
+ for (IDeviceFrameworkSupport devFramework : allFrameworks)
+ {
+ Collection<IAndroidEmulatorInstance> devFrmInstances = devFramework.getAllInstances();
+ if (devFrmInstances != null)
+ {
+ for (IAndroidEmulatorInstance instance : devFrmInstances)
+ {
+ if (instance.isConnected())
+ {
+ connectedInstancesSet.add(instance);
+ }
+ }
+ }
+ }
+
+ return connectedInstancesSet;
+ }
+
+ /**
+ * Retrieves all started instances host addresses managed by every
+ * device framework which contributes with the deviceFramework extension point
+ *
+ * @return A collection containing all instances from all frameworks
+ */
+ public Set<String> getAllStartedInstancesHosts()
+ {
+ Set<String> hostSet = new HashSet<String>();
+ for (IDeviceFrameworkSupport devFramework : allFrameworks)
+ {
+ Collection<IAndroidEmulatorInstance> devFrmInstances = devFramework.getAllInstances();
+ if (devFrmInstances != null)
+ {
+ for (IAndroidEmulatorInstance instance : devFrmInstances)
+ {
+ if (instance.isStarted())
+ {
+ hostSet.add(instance.getInstanceIdentifier());
+ }
+ }
+ }
+ }
+
+ return hostSet;
+ }
+
+ /**
+ * Populates the allFrameworks collection with framework contributed
+ * classes for retrieving framework information.
+ */
+ private void populateModel()
+ {
+ IExtensionRegistry extReg = Platform.getExtensionRegistry();
+ IExtensionPoint extPoint = extReg.getExtensionPoint(DEV_FRAMEWORK_EXTENSION_POINT_ID);
+ IExtension[] extensions = extPoint.getExtensions();
+
+ for (IExtension aExtension : extensions)
+ {
+ IConfigurationElement[] configElements = aExtension.getConfigurationElements();
+ for (IConfigurationElement aConfig : configElements)
+ {
+ if (aConfig.getName().equals(DEV_FRAMEWORK_ELEM))
+ {
+ try
+ {
+ IDeviceFrameworkSupport devFramework =
+ (IDeviceFrameworkSupport) aConfig
+ .createExecutableExtension(DEV_FRAMEWORK_IMPL_CLASS_ATTR);
+ if (devFramework != null)
+ {
+ allFrameworks.add(devFramework);
+ }
+ }
+ catch (CoreException e)
+ {
+ // Do nothing.
+ // If a device framework cannot be instantiated, it will
+ // not be plugged to emulator core plugin.
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/devfrm/IDeviceFrameworkSupport.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/devfrm/IDeviceFrameworkSupport.java
new file mode 100644
index 0000000..427f106
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/devfrm/IDeviceFrameworkSupport.java
@@ -0,0 +1,46 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.emulator.core.devfrm;
+
+import java.util.Collection;
+
+import com.motorola.studio.android.emulator.core.model.IAndroidEmulatorInstance;
+
+/**
+ * DESCRIPTION:
+ * Interface that must be implemented by every device framework
+ * that wishes to use the Android Emulator plug-ins
+ *
+ * RESPONSIBILITY:
+ * Provide every information that the Android Emulator plug-ins need
+ * to work with the registered framework
+ *
+ * COLABORATORS:
+ * None.
+ *
+ * USAGE:
+ * The class should be used by Eclipse only
+ */
+public interface IDeviceFrameworkSupport
+{
+ /**
+ * Retrieves a collection of the Android Emulator instances
+ * managed by this device framework
+ *
+ * @return Collection of the Android Emulator instances
+ */
+ Collection<IAndroidEmulatorInstance> getAllInstances();
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/emulationui/AbstractEmuCtProvider.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/emulationui/AbstractEmuCtProvider.java
new file mode 100644
index 0000000..8f9b094
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/emulationui/AbstractEmuCtProvider.java
@@ -0,0 +1,248 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.emulator.core.emulationui;
+
+import static com.motorola.studio.android.common.log.StudioLogger.warn;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.eclipse.jface.viewers.ITreeContentProvider;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.ui.IViewSite;
+
+import com.motorola.studio.android.emulator.core.devfrm.DeviceFrameworkManager;
+
+/**
+ * DESCRIPTION:
+ * This class is the abstract parent of all emulation views content providers
+ *
+ * RESPONSIBILITY:
+ * Provide common method implementation for the several emulation views
+ * content providers
+ *
+ * COLABORATORS:
+ * None.
+ *
+ * USAGE:
+ * The class is used when a emulation view content provider is instantiated
+ */
+public abstract class AbstractEmuCtProvider implements ITreeContentProvider
+{
+ /**
+ * The id to be used when constructing a "sent to" node
+ */
+ public static final String SENT_TO_EMULATOR_ID = "sent_to";
+
+ /**
+ * The id to be used when constructing a "receive from" node
+ */
+ public static final String RECEIVE_FROM_EMULATOR_ID = "received_from";
+
+ /**
+ * The parent of the entire tree
+ */
+ private static IViewSite treeParent;
+
+ /**
+ * @see org.eclipse.jface.viewers.ITreeContentProvider#getParent(Object)
+ */
+ public Object getParent(Object element)
+ {
+
+ Object parent = null;
+
+ if (element instanceof EmuViewerNode)
+ {
+ EmuViewerNode nodeElement = (EmuViewerNode) element;
+
+ if (nodeElement instanceof EmuViewerRootNode)
+ {
+ // The IViewSite object is the parent of the whole tree
+ parent = treeParent;
+ }
+ else
+ {
+ parent = nodeElement.getParent();
+ }
+ }
+ else
+ {
+ warn("Tried to get parent of an object that is not an emulation tree node");
+ }
+
+ return parent;
+ }
+
+ /**
+ * @see org.eclipse.jface.viewers.ITreeContentProvider#hasChildren(Object)
+ */
+ public boolean hasChildren(Object object)
+ {
+
+ boolean hasChildren = false;
+
+ if (object instanceof EmuViewerNode)
+ {
+ EmuViewerNode nodeObject = (EmuViewerNode) object;
+
+ // The node has children if its children collection is bigger than 0 in size
+ hasChildren = (nodeObject.getChildren().size() > 0);
+
+ }
+ else
+ {
+ warn("Tried to test if an object that is not an emulation tree node has children");
+ }
+
+ return hasChildren;
+ }
+
+ /**
+ * @see org.eclipse.jface.viewers.IStructuredContentProvider#getElements(Object)
+ */
+ public Object[] getElements(Object parent)
+ {
+
+ Object[] elements;
+
+ if (parent instanceof IViewSite)
+ {
+
+ if (treeParent == null)
+ {
+ // Sets the treeParent attribute to store which view site is the parent of the
+ // whole tree. This is done only once for each content provider instance.
+ // Each provider instance is supposed to be used with a single view
+ treeParent = (IViewSite) parent;
+ }
+
+ Collection<EmuViewerRootNode> emuNodeCollection = new HashSet<EmuViewerRootNode>();
+
+ Set<String> hostSet =
+ DeviceFrameworkManager.getInstance().getAllStartedInstancesHosts();
+
+ // A root node will be added per active emulator at the tree viewer
+ for (String host : hostSet)
+ {
+ EmuViewerRootNode node = new EmuViewerRootNode(host);
+ emuNodeCollection.add(node);
+
+ addChildrenToRootNode(node);
+ }
+
+ // Creating the array of elements (in this case, emulator root nodes) to be
+ // returned, when the parent is the view site itself
+ Object[] emuNodeArray = emuNodeCollection.toArray(new Object[emuNodeCollection.size()]);
+ elements = emuNodeArray;
+
+ }
+ else
+ {
+ // When elements different from the view site are provided, the elements will be
+ // their children
+ elements = getChildren(parent);
+ }
+
+ return elements;
+ }
+
+ /**
+ * @see org.eclipse.jface.viewers.ITreeContentProvider#getChildren(Object)
+ */
+ public Object[] getChildren(Object parent)
+ {
+
+ Set<EmuViewerNode> childrenCollection;
+ Object[] returnArray;
+
+ if (parent instanceof EmuViewerNode)
+ {
+ // Firstly, try to retrieve the parent's children by means of the appropriate method
+ EmuViewerNode parentNode = (EmuViewerNode) parent;
+
+ childrenCollection = parentNode.getChildren();
+
+ // If the provided element is an emulator root node, it is needed to test if the
+ // intermediate nodes were already created (they are not created in the first request).
+ // If they were not created, assure that when the content framework requests, the
+ // intermediate nodes will be found.
+ //
+ // This procedure guarantees that once an emulator is started, it has the intermediate
+ // nodes constructed even if no emulation is being performed.
+ if (parentNode instanceof EmuViewerRootNode)
+ {
+ String host = ((EmuViewerRootNode) parentNode).getEmulatorIdentifier();
+ for (EmuViewerNode child : childrenCollection)
+ {
+ if (child.getChildren().size() == 0)
+ {
+
+ addChildrenToLeafParentNode(child, host);
+ }
+ }
+ }
+
+ // Creating the array of elements to be returned
+ returnArray = childrenCollection.toArray(new EmuViewerNode[childrenCollection.size()]);
+
+ }
+ else
+ {
+ warn("Tried to get children of an object that is not an emulation tree node");
+ returnArray = new Object[0];
+ }
+
+ return returnArray;
+ }
+
+ /**
+ * @see org.eclipse.jface.viewers.IContentProvider#dispose()
+ */
+ public void dispose()
+ {
+ // Do nothing
+ }
+
+ /**
+ * @see org.eclipse.jface.viewers.IContentProvider#inputChanged(Viewer, Object, Object)
+ */
+ public void inputChanged(Viewer v, Object oldInput, Object newInput)
+ {
+ // Do nothing
+ }
+
+ /**
+ * Given a root node, adds children nodes to it
+ *
+ * @param root The root node that will receive the children nodes
+ */
+ protected void addChildrenToRootNode(EmuViewerRootNode root)
+ {
+
+ root.addChild(new EmuViewerNode(root, RECEIVE_FROM_EMULATOR_ID));
+ root.addChild(new EmuViewerNode(root, SENT_TO_EMULATOR_ID));
+ }
+
+ /**
+ * Given a node, adds children leaf nodes to it
+ *
+ * @param leafParentNode The node that will receive the children leaf nodes
+ * @param host The identifier of the emulator that owns this sub-tree
+ */
+ protected abstract void addChildrenToLeafParentNode(EmuViewerNode leafParentNode, String host);
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/emulationui/AbstractEmuLabelProvider.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/emulationui/AbstractEmuLabelProvider.java
new file mode 100644
index 0000000..812b40f
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/emulationui/AbstractEmuLabelProvider.java
@@ -0,0 +1,286 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.emulator.core.emulationui;
+
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.jface.viewers.ColumnLabelProvider;
+import org.eclipse.jface.viewers.ViewerCell;
+import org.eclipse.jface.viewers.ViewerRow;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.widgets.Tree;
+import org.eclipse.swt.widgets.TreeItem;
+import org.eclipse.ui.PlatformUI;
+
+import com.motorola.studio.android.emulator.EmulatorPlugin;
+
+/**
+ * DESCRIPTION:
+ * This class contains the common logic for classes that retrieve the labels
+ * that are presented at the emulation views
+ *
+ * RESPONSIBILITY:
+ * Provide basic support to emulation label providers. This include
+ * - Maintaining the column index for determining which data is to be retrieved from the
+ * beans
+ * - Maintaining which is the column at the view left edge, to draw the tree correctly at
+ * that position
+ * - Updating the viewer cells in a standardized way, giving support to coloring a line
+ * in alternative color
+ *
+ * COLABORATORS:
+ * None.
+ *
+ * USAGE:
+ * The class is used when a emulation view content provider is instantiated
+ */
+public abstract class AbstractEmuLabelProvider extends ColumnLabelProvider
+{
+ /**
+ * The column that is being updated by this label provider
+ */
+ protected int columnIndex;
+
+ /**
+ * The index of the column that is currently in the left edge of the viewer
+ */
+ protected int firstColumnIndex;
+
+ /**
+ * The host that contains the bean that should be painted in an alternative color
+ * or <code>null</code> if no bean will have alternative color
+ */
+ private String alternativeColorHost;
+
+ /**
+ * The id that identifies the bean that should be painted in an alternative color
+ * or <code>null</code> if no bean will have alternative color
+ */
+ private long alternativeColorBeanId;
+
+ /**
+ * The color that is used to paint highlighted nodes
+ */
+ private final Color alternativeColor =
+ new Color(PlatformUI.getWorkbench().getDisplay(), 255, 255, 0);
+
+ /**
+ * Sets the index of the column that is currently at the left edge of the viewer
+ *
+ * @param firstColumnIndex The index of the column that is currently at the left
+ * edge of the viewer
+ */
+ public void setFirstColumnIndex(int firstColumnIndex)
+ {
+ this.firstColumnIndex = firstColumnIndex;
+ }
+
+ /**
+ * Retrieves the index of the column that is currently at the left edge of the viewer
+ *
+ * @return firstColumnIndex The index of the column that is currently at the left
+ * edge of the viewer
+ */
+ public int getFirstColumnIndex()
+ {
+ return firstColumnIndex;
+ }
+
+ /**
+ * Sets the host that contains the bean that should be painted in an alternative color
+ *
+ * @param host The host that contains the bean to be colored in alternative way, or
+ * <code>null</code> if no bean shall be colored in alternative way
+ */
+ public void setAlternativeColorHost(String host)
+ {
+ this.alternativeColorHost = host;
+ }
+
+ /**
+ * Sets the id that identifies the bean that should be painted in an alternative color
+ *
+ * @param beanId The id that identifies the bean that should be painted in an alternative
+ * color or <code>null</code> if no bean will have alternative color
+ */
+ public void setAlternativeColorBeanId(long beanId)
+ {
+ this.alternativeColorBeanId = beanId;
+ }
+
+ /**
+ * @see org.eclipse.jface.viewers.CellLabelProvider#update(org.eclipse.jface.viewers.ViewerCell)
+ */
+ @Override
+ public void update(ViewerCell cell)
+ {
+ // The instance column index is set with the current cell column index, as the logic
+ // contained in this class depends on this information. Then after the cell is
+ // updated according to the standard procedure, the column index field is reset so that
+ // it does not interfere with subsequent updates.
+ columnIndex = cell.getColumnIndex();
+ super.update(cell);
+ columnIndex = firstColumnIndex;
+
+ // Checks if the cell needs to be highlighted. This will be true if the values of
+ // alternativeColorHost and alternativeColorBeanId are different from null and -1
+ if ((alternativeColorHost != null) && (alternativeColorBeanId != -1))
+ {
+
+ Object element = cell.getElement();
+ // Only leaf nodes can be highlighted
+ if (element instanceof EmuViewerLeafNode)
+ {
+ // The next lines are used to check if the current element is the one to be
+ // highlighted. For that, the host and bean id needs to be compared to the
+ // alternativeColorHost and alternativeColorBeanId instance field values
+ EmuViewerLeafNode node = (EmuViewerLeafNode) element;
+ long beanId = node.getBeanId();
+ EmuViewerRootNode root = (EmuViewerRootNode) node.getParent().getParent();
+ String host = root.getEmulatorIdentifier();
+
+ if ((beanId == alternativeColorBeanId) && (host.equals(alternativeColorHost)))
+ {
+ // Highlighting the node
+
+ cell.setBackground(alternativeColor);
+
+ // Putting the node at the visible part of the tree
+
+ ViewerRow highlightedRow = cell.getViewerRow();
+ TreeItem highlightedItem = (TreeItem) highlightedRow.getItem();
+ Tree tree = (Tree) cell.getControl();
+ tree.showItem(highlightedItem);
+ }
+ }
+
+ }
+ }
+
+ /**
+ * @see org.eclipse.jface.viewers.ILabelProvider#getImage(Object)
+ */
+ @Override
+ public Image getImage(Object element)
+ {
+
+ Image imageToReturn = null;
+
+ // The image should appear near the label at the first cell in the row. That is why
+ // a test is being performed for the first column.
+ if ((element instanceof EmuViewerNode) && (isProvidingForFirstColumn()))
+ {
+ if (element instanceof EmuViewerRootNode)
+ {
+ // Get an common icon for emulator nodes
+
+ ImageDescriptor descriptor;
+ descriptor =
+ EmulatorPlugin.imageDescriptorFromPlugin(
+ EmulatorPlugin.PLUGIN_ID, IEmuIconPath.EMULATOR_ICON_PATH);
+ if (descriptor != null)
+ {
+ imageToReturn = descriptor.createImage();
+ }
+ }
+ else if (element instanceof EmuViewerLeafNode)
+ {
+ // Delegate the get method to the concrete class for the leaf node icon
+
+ imageToReturn = getLeafNodeIcon((EmuViewerLeafNode) element);
+ }
+ else
+ {
+ // Delegate the get method to the concrete class for the intermediate node icon
+
+ imageToReturn = getIntermediateNodeIcon((EmuViewerNode) element);
+ }
+ }
+
+ return imageToReturn;
+ }
+
+ /**
+ * Tests if the resource being retrieved (text or image) is for a cell at the first column
+ * of the tree viewer
+ *
+ * @return True if it is providing for the first column. False otherwise
+ */
+ protected boolean isProvidingForFirstColumn()
+ {
+ return firstColumnIndex == columnIndex;
+ }
+
+ /**
+ * Retrieves the icon that shall be displayed next to the provided node element. The
+ * provided node is one of the intermediate nodes in the tree, and does not represent
+ * neither the emulator itself nor the leaf element
+ *
+ * @param node The tree node that will have the returned icon by its side
+ *
+ * @return The icon that shall be displayed near the provided node
+ */
+ /**
+ * @see AbstractEmuLabelProvider#getIntermediateNodeIcon(EmuViewerNode)
+ */
+ protected Image getIntermediateNodeIcon(EmuViewerNode node)
+ {
+
+ Image imageToReturn = null;
+ ImageDescriptor descriptor;
+
+ if (node.getNodeId().equals(AbstractEmuCtProvider.SENT_TO_EMULATOR_ID))
+ {
+ descriptor =
+ EmulatorPlugin.imageDescriptorFromPlugin(EmulatorPlugin.PLUGIN_ID,
+ IEmuIconPath.SENT_TO_ICON_PATH);
+ }
+ else
+ {
+ descriptor =
+ EmulatorPlugin.imageDescriptorFromPlugin(EmulatorPlugin.PLUGIN_ID,
+ IEmuIconPath.RECEIVE_FROM_ICON_PATH);
+ }
+
+ if (descriptor != null)
+ {
+ imageToReturn = descriptor.createImage();
+ }
+
+ return imageToReturn;
+ }
+
+ /**
+ * Retrieves the icon that shall be displayed next to the provided leaf node element
+ *
+ * @param node The tree node that will have the returned icon by its side
+ *
+ * @return The icon that shall be displayed near the provided node
+ */
+ protected abstract Image getLeafNodeIcon(EmuViewerLeafNode node);
+
+ /**
+ * Gets the text referring to a particular leaf node, at the provided column.
+ *
+ * @param element The tree node that identifies the bean that needs to have information
+ * retrieved from
+ * @param columnIndex The id of the column that identifies which information to take from the
+ * bean
+ *
+ * @return The text from the bean at the specified column
+ */
+ public abstract String getText(EmuViewerLeafNode node, int columnIndex);
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/emulationui/EmuViewerLeafNode.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/emulationui/EmuViewerLeafNode.java
new file mode 100644
index 0000000..4f1da74
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/emulationui/EmuViewerLeafNode.java
@@ -0,0 +1,71 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.emulator.core.emulationui;
+
+/**
+ * DESCRIPTION:
+ * This is a utility class used to represent a leaf node of the emulation views
+ * tree viewer
+ *
+ * RESPONSIBILITY:
+ * Guarantee that no children is added to this leaf node
+ *
+ * COLABORATORS:
+ * None.
+ *
+ * USAGE:
+ * A class should construct an instance of this class whenever it wishes
+ * to add a leaf node to an emulation view tree
+ */
+public abstract class EmuViewerLeafNode extends EmuViewerNode
+{
+ /**
+ * Constructor
+ *
+ * @see EmuViewerNode#EmuViewerNode(EmuViewerNode, String)
+ */
+ public EmuViewerLeafNode(EmuViewerNode parent, String nodeId)
+ {
+ super(parent, nodeId);
+ }
+
+ /**
+ * @see EmuViewerNode#addChild(EmuViewerNode)
+ */
+ @Override
+ public void addChild(EmuViewerNode child)
+ {
+ // Do nothing
+ }
+
+ /**
+ * Retrieves the id that identifies the bean that provides data to this node
+ *
+ * @return The bean identifier
+ */
+ public abstract long getBeanId();
+
+ /**
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString()
+ {
+ // For leaf nodes, identify which is the concrete class name and append to that name
+ // the associated bean id
+ return getClass().getSimpleName() + ":" + getBeanId();
+ }
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/emulationui/EmuViewerNode.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/emulationui/EmuViewerNode.java
new file mode 100644
index 0000000..f452760
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/emulationui/EmuViewerNode.java
@@ -0,0 +1,152 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.emulator.core.emulationui;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * DESCRIPTION:
+ * This class represents a node in the tree presented in a emulation view
+ *
+ * RESPONSIBILITY:
+ * Guarantee the tree structure by maintaining the parent/child relationship
+ *
+ * COLABORATORS:
+ * None.
+ *
+ * USAGE:
+ * A class should construct an instance of this class whenever it wishes
+ * to add a node to an emulation view tree
+ */
+public class EmuViewerNode
+{
+ /**
+ * The parent node of this node
+ */
+ private final EmuViewerNode parent;
+
+ /**
+ * An id that identifies the node type.
+ * The id meaning is defined by the user
+ */
+ private final String nodeId;
+
+ /**
+ * The error message to use as the node label
+ * If <code>null</code>, use regular label resolution
+ */
+ private String errorMessage = null;
+
+ /**
+ * A set containing all children of this node
+ */
+ private final Set<EmuViewerNode> children = new HashSet<EmuViewerNode>();
+
+ /**
+ * Constructor.
+ *
+ * @param parent The parent node of this node
+ * @param nodeId An id that identifies the node type
+ */
+ public EmuViewerNode(EmuViewerNode parent, String nodeId)
+ {
+ this.parent = parent;
+ this.nodeId = nodeId;
+ }
+
+ /**
+ * Retrieves the node's parent
+ *
+ * @return The parent node
+ */
+ public EmuViewerNode getParent()
+ {
+ return parent;
+ }
+
+ /**
+ * Retrieves the id of this node. The id meaning is defined by the class user
+ *
+ * @return The id of this node
+ */
+ public String getNodeId()
+ {
+ return nodeId;
+ }
+
+ /**
+ * Adds a new child to this node
+ *
+ * @param child The child to be added to the node
+ */
+ public void addChild(EmuViewerNode child)
+ {
+ children.add(child);
+ }
+
+ /**
+ * Retrieves all this node's children
+ *
+ * @return A set containing all children of this node
+ */
+ public Set<EmuViewerNode> getChildren()
+ {
+ return children;
+ }
+
+ /**
+ * Sets an error message to display as the node label.
+ * If <code>null</code>, the regular label resolution is used
+ *
+ * @param errorMessage An error message to display or <code>null</code> if it is
+ * desired to have regular label resolution for this node
+ */
+ public void setErrorMessage(String errorMessage)
+ {
+ this.errorMessage = errorMessage;
+ }
+
+ /**
+ * Tests if this node has an error message assigned
+ *
+ * @return True if an error message was assigned; false otherwise
+ */
+ public boolean hasErrorMessage()
+ {
+ return errorMessage != null;
+ }
+
+ /**
+ * Retrieves the error message assigned to this node
+ *
+ * @return The error message, or <code>null</code> if no error message was assigned
+ */
+ public String getErrorMessage()
+ {
+ return errorMessage;
+ }
+
+ /**
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString()
+ {
+ // For generic/intermediate nodes use the node id itself
+ return getNodeId();
+ }
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/emulationui/EmuViewerRootNode.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/emulationui/EmuViewerRootNode.java
new file mode 100644
index 0000000..0188835
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/emulationui/EmuViewerRootNode.java
@@ -0,0 +1,94 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.emulator.core.emulationui;
+
+import static com.motorola.studio.android.common.log.StudioLogger.warn;
+
+import com.motorola.studio.android.emulator.core.exception.InstanceNotFoundException;
+import com.motorola.studio.android.emulator.core.model.IAndroidEmulatorInstance;
+import com.motorola.studio.android.emulator.core.utils.EmulatorCoreUtils;
+
+/**
+ * DESCRIPTION:
+ * This class represents the parent of all nodes in the tree presented in a emulation view
+ * It must have reference to the emulator host, so that the tree is separated in
+ * several emulator sub-trees
+ *
+ * RESPONSIBILITY:
+ * To be the root of the emulator tree and maintain information about which emulator is
+ * owner of the sub-tree that has this node as root
+ *
+ * COLABORATORS:
+ * None.
+ *
+ * USAGE:
+ * A class should construct an instance of this class whenever an emulator information is to be
+ * included at an emulation view
+ */
+public class EmuViewerRootNode extends EmuViewerNode
+{
+ /**
+ * The emulator identifier (serial port number)
+ */
+ private final String serial;
+
+ /**
+ * Constructor.
+ *
+ * @param identifier The identifier of the emulator that owns the sub-tree starting at this node
+ */
+ public EmuViewerRootNode(String identifier)
+ {
+ super(null, "ROOT");
+ this.serial = identifier;
+ }
+
+ /**
+ * Gets the host of the emulator that owns the sub-tree starting at this node
+ *
+ * @return The emulator host
+ */
+ public String getEmulatorIdentifier()
+ {
+ return serial;
+ }
+
+ /**
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString()
+ {
+ String classString;
+
+ // For emulator root nodes, the toString method should provide the emulator instance
+ // name. If it is not possible to retrieve the instance name, print the host itself
+ String serial = getEmulatorIdentifier();
+ try
+ {
+ IAndroidEmulatorInstance instance =
+ EmulatorCoreUtils.getAndroidInstanceByIdentifier(serial);
+ classString = instance.getName();
+ }
+ catch (InstanceNotFoundException e)
+ {
+ warn("The instance could not be found for retrieving its name. Using serial port instead.");
+ classString = serial;
+ }
+
+ return classString;
+ }
+} \ No newline at end of file
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/emulationui/IEmuIconPath.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/emulationui/IEmuIconPath.java
new file mode 100644
index 0000000..164f403
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/emulationui/IEmuIconPath.java
@@ -0,0 +1,30 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.emulator.core.emulationui;
+
+/**
+ * This interface contains the paths to the icons that are used by the emulation views
+ */
+public interface IEmuIconPath
+{
+ // Emulation root node icon
+ String EMULATOR_ICON_PATH = "resource/emulator.png";
+
+ // Emulation intermediate nodes icons
+ String SENT_TO_ICON_PATH = "resource/sentbyemulator.png";
+
+ String RECEIVE_FROM_ICON_PATH = "resource/receivebyemulator.png";
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/emulationui/SrcDestComposite.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/emulationui/SrcDestComposite.java
new file mode 100644
index 0000000..a70194e
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/emulationui/SrcDestComposite.java
@@ -0,0 +1,439 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.emulator.core.emulationui;
+
+import static com.motorola.studio.android.common.log.StudioLogger.debug;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.eclipse.jface.dialogs.Dialog;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.ModifyEvent;
+import org.eclipse.swt.events.ModifyListener;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.graphics.FontMetrics;
+import org.eclipse.swt.graphics.GC;
+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 com.motorola.studio.android.emulator.core.devfrm.DeviceFrameworkManager;
+import com.motorola.studio.android.emulator.core.model.IAndroidEmulatorInstance;
+import com.motorola.studio.android.emulator.i18n.EmulatorNLS;
+
+/**
+ * DESCRIPTION:
+ * This is a composite that is used by several emulation UI elements to choose source and
+ * destination elements
+ *
+ * RESPONSIBILITY:
+ * Provide means for the user to choose which emulator and phone number will be involved
+ * in a emulation
+ *
+ * COLABORATORS:
+ * None.
+ *
+ * USAGE:
+ * Add the composite to a UI element that needs to have a emulator and a phone number
+ * chosen by the user
+ */
+public class SrcDestComposite extends Composite
+{
+ /**
+ * Message that will be shown near the emulator combo
+ */
+ private String emulatorLabelStr;
+
+ /**
+ * Message that will be shown near the phone number text field
+ */
+ private String phoneNumberLabelStr;
+
+ /**
+ * Emulator currently selected
+ */
+ private String selectedEmulator;
+
+ /**
+ * Phone number currently selected
+ */
+ private String selectedPhoneNumber;
+
+ /**
+ * True if the composite is valid and can provide information to the user class
+ * False if not.
+ */
+ private boolean isValid = false;
+
+ /**
+ * Error message to be shown to the user if the composite data is not valid
+ */
+ private String errorMessage =
+ NLS.bind(EmulatorNLS.ERR_SrcDestComposite_InvalidFillingBase,
+ EmulatorNLS.ERR_SrcDestComposite_InvalidFillingPhoneNumber,
+ EmulatorNLS.ERR_SrcDestComposite_InvalidFillingEmulator);
+
+ // Widgets
+ private Combo runningEmulatorsCombo;
+
+ private Text phoneNumberText;
+
+ // attribute for calculating label sizes (for layout purposes)
+ private FontMetrics fontMetrics = null;
+
+ /**
+ * Constructor.
+ *
+ * @param parent The parent composite of this one
+ * @param style Style of the composite. See constants at SWT class
+ * @param showSrcControls True if this composite should show
+ * the emulation source controls. False otherwise
+ * @param isEmulatorSrc True if this composite will have the emulator
+ * part as source in emulation. False if the phone number
+ * will be the source
+ */
+ public SrcDestComposite(Composite parent, int style, boolean showSrcControls,
+ boolean isEmulatorSrc)
+ {
+ super(parent, style);
+
+ GridLayout layout = new GridLayout(2, false);
+ layout.marginHeight = 5;
+ layout.marginWidth = 5;
+ layout.verticalSpacing = 5;
+ layout.horizontalSpacing = 2;
+ this.setLayout(layout);
+ GridData data = new GridData(SWT.FILL, SWT.FILL, true, true);
+ this.setLayoutData(data);
+
+ // initialize font metrics
+ GC gc = new GC(this);
+ gc.setFont(this.getFont());
+ fontMetrics = gc.getFontMetrics();
+ gc.dispose();
+
+ if (isEmulatorSrc)
+ {
+ // When emulator is the source part, its UI is build prior to phone number UI,
+ // and appropriate labels are used for both
+ debug("Using emulator as source");
+ emulatorLabelStr = EmulatorNLS.UI_SrcDestComposite_OriginatingRunningEmulatorLabel;
+ phoneNumberLabelStr = EmulatorNLS.UI_SrcDestComposite_DestinationPhoneNumberLabel;
+ if (showSrcControls)
+ {
+ debug("Showing source controls");
+ createEmulatorUI();
+ }
+ createPhoneNumberUI();
+ }
+ else
+ {
+ // When phone number is the source part, its UI is build prior to emulator UI,
+ // and appropriate labels are used for both
+ debug("Using phone number as source");
+ emulatorLabelStr = EmulatorNLS.UI_SrcDestComposite_DestinationRunningEmulatorLabel;
+ phoneNumberLabelStr = EmulatorNLS.UI_SrcDestComposite_OriginatingPhoneNumberLabel;
+ if (showSrcControls)
+ {
+ debug("Showing source controls");
+ createPhoneNumberUI();
+ }
+ createEmulatorUI();
+ }
+
+ addListeners();
+
+ // call the check method to refresh error message.
+ checkData();
+
+ }
+
+ /**
+ * Build the emulator part controls
+ */
+ private void createEmulatorUI()
+ {
+
+ Label runningEmulatorsLabel = new Label(this, SWT.NONE);
+ runningEmulatorsLabel.setText(emulatorLabelStr);
+ GridData data = new GridData(SWT.FILL, SWT.CENTER, false, false);
+ data.widthHint = getLabelWidthHint(runningEmulatorsLabel);
+ runningEmulatorsLabel.setLayoutData(data);
+
+ this.runningEmulatorsCombo = new Combo(this, SWT.BORDER | SWT.READ_ONLY);
+ data = new GridData(SWT.FILL, SWT.FILL, true, false);
+ this.runningEmulatorsCombo.setLayoutData(data);
+ populateEmulatorCombo();
+
+ }
+
+ /**
+ * Build the phone number part controls
+ */
+ private void createPhoneNumberUI()
+ {
+
+ Label phoneNumberLabel = new Label(this, SWT.NONE);
+ phoneNumberLabel.setText(phoneNumberLabelStr);
+ GridData data = new GridData(SWT.FILL, SWT.CENTER, false, false);
+ data.widthHint = getLabelWidthHint(phoneNumberLabel);
+ phoneNumberLabel.setLayoutData(data);
+
+ this.phoneNumberText = new Text(this, SWT.BORDER);
+ data = new GridData(SWT.FILL, SWT.FILL, true, false);
+ this.phoneNumberText.setLayoutData(data);
+ this.phoneNumberText.setTextLimit(40);
+
+ }
+
+ /**
+ * Add listeners to the composite controls
+ */
+ private void addListeners()
+ {
+
+ if (runningEmulatorsCombo != null)
+ {
+ runningEmulatorsCombo.addSelectionListener(new SelectionAdapter()
+ {
+ @Override
+ public void widgetSelected(SelectionEvent e)
+ {
+ selectedEmulator = getCurrentlySelectedIdentifier();
+ checkData();
+ }
+ });
+ }
+
+ if (phoneNumberText != null)
+ {
+ phoneNumberText.addModifyListener(new ModifyListener()
+ {
+ public void modifyText(ModifyEvent e)
+ {
+ selectedPhoneNumber = phoneNumberText.getText();
+ checkData();
+ }
+ });
+ }
+ }
+
+ /**
+ * Defines the width hint to be used for the given label on a GridData object.
+ *
+ * @param label the label to calculate the width hint for
+ *
+ * @return the width hint
+ */
+ private int getLabelWidthHint(Label label)
+ {
+ int widthHint = Dialog.convertHorizontalDLUsToPixels(fontMetrics, label.getText().length());
+ return Math.max(widthHint, label.computeSize(SWT.DEFAULT, SWT.DEFAULT, true).x);
+ }
+
+ /**
+ * Populates the emulator combo box with the currently running emulators
+ */
+ private void populateEmulatorCombo()
+ {
+
+ // Populating emulator combo with all running emulator names
+ // Besides, keeping an array of the identifiers as combo data.
+ Map<String, String> identifiersAndNames = new HashMap<String, String>();
+ Collection<IAndroidEmulatorInstance> startedInstances =
+ DeviceFrameworkManager.getInstance().getAllStartedInstances();
+ for (IAndroidEmulatorInstance instance : startedInstances)
+ {
+ identifiersAndNames.put(instance.getInstanceIdentifier(), instance.getName());
+ }
+
+ String[] instanceNamesArray = new String[identifiersAndNames.size()];
+ String[] identifiersArray = new String[identifiersAndNames.size()];
+ int i = 0;
+
+ Set<String> identifiers = identifiersAndNames.keySet();
+ for (String identifier : identifiers)
+ {
+
+ String viewerName = identifiersAndNames.get(identifier);
+
+ // It is VERY important that the index used at the data array is equal to the
+ // index used at the items array. According to the selected item in the combo, the
+ // corresponding identifier is retrieved from the data array in the future
+ instanceNamesArray[i] = viewerName;
+ identifiersArray[i] = identifier;
+ i++;
+ }
+
+ runningEmulatorsCombo.setItems(instanceNamesArray);
+ runningEmulatorsCombo.setData(identifiersArray);
+
+ // if there is just one emulator in the combo list,
+ // it will be chose by default.
+ if (runningEmulatorsCombo.getItemCount() == 1)
+ {
+ runningEmulatorsCombo.select(0);
+ selectedEmulator = getCurrentlySelectedIdentifier();
+ checkData();
+ }
+
+ }
+
+ /**
+ * Retrieve the identifier of the selected instance at Android Emulator combo box
+ *
+ * @return The identifier, or an empty string if no emulator is selected
+ */
+ private String getCurrentlySelectedIdentifier()
+ {
+
+ String currentlySelectedSerial = "";
+ int index = runningEmulatorsCombo.getSelectionIndex();
+
+ if (index >= 0)
+ {
+ String[] serials = (String[]) runningEmulatorsCombo.getData();
+ currentlySelectedSerial = serials[index];
+
+ }
+
+ return currentlySelectedSerial;
+ }
+
+ /**
+ * Get the emulator identifier that was selected by the user
+ *
+ * @return The selected emulator identifier
+ */
+ public String getSelectedEmulator()
+ {
+ return selectedEmulator;
+ }
+
+ /**
+ * Get the phone number that was typed by the user
+ *
+ * @return The phone number typed by the user
+ */
+ public String getSelectedPhoneNumber()
+ {
+ return selectedPhoneNumber;
+ }
+
+ /**
+ * Tests if the values chosen/typed by the user are valid
+ * By invoking this method, the user class is able to know if it can proceed
+ *
+ * @return True if the user has chosen valid values. False otherwise
+ */
+ public boolean isValid()
+ {
+ return isValid;
+ }
+
+ /**
+ * Retrieves the error message to be shown to the user if the composite is
+ * not valid
+ *
+ * @return The error message if the composite is not valid, or <code>null</code> if
+ * the composite is valid and no error message should be displayed.
+ */
+ public String getErrorMessage()
+ {
+ return errorMessage;
+ }
+
+ /**
+ * Check if the data entered by the user is correct and set instance variables
+ * to store the test results
+ */
+ private void checkData()
+ {
+
+ isValid = false;
+
+ boolean isEmulatorValid = false;
+ boolean isPhoneNumberValid = false;
+
+ boolean isUsingPhoneNumber = (phoneNumberText != null);
+ boolean isUsingEmulator = (runningEmulatorsCombo != null);
+
+ // Tests if emulator selection is valid.
+ //
+ // If the emulator combo is null, that means that the user decided not to use it. In
+ // this case, it will always be valid. Otherwise, the combo selection needs to be
+ // not null and not blank
+ if ((!isUsingEmulator) || ((selectedEmulator != null) && (!selectedEmulator.equals(""))))
+ {
+ isEmulatorValid = true;
+ }
+
+ // Tests if phone number selection is valid.
+ //
+ // If the phone number text is null, that means that the user decided not to use it. In
+ // this case, it will always be valid. Otherwise, the text field selection needs to be
+ // not null, not blank and can be parsed to double (that means that the contents are
+ // composed by numerals only)
+ if (!isUsingPhoneNumber)
+ {
+ isPhoneNumberValid = true;
+ }
+ else if ((selectedPhoneNumber != null) && (!selectedPhoneNumber.equals("")))
+ {
+ Pattern p = Pattern.compile("(\\d)+");
+ Matcher m = p.matcher(selectedPhoneNumber);
+ isPhoneNumberValid = m.matches();
+ }
+
+ // Based on previous checks, determine if the composite state is valid
+ if (isEmulatorValid && isPhoneNumberValid)
+ {
+ isValid = true;
+ errorMessage = null;
+ }
+ else
+ {
+ // If not valid, an error message will be shown. The following calculations
+ // are for determining which error has happened to build the message
+ String phoneNumberError = "";
+ String emulatorError = "";
+
+ if (isUsingPhoneNumber && (!isPhoneNumberValid))
+ {
+ phoneNumberError = EmulatorNLS.ERR_SrcDestComposite_InvalidFillingPhoneNumber;
+ }
+ if (isUsingEmulator && (!isEmulatorValid))
+ {
+ emulatorError = EmulatorNLS.ERR_SrcDestComposite_InvalidFillingEmulator;
+ }
+
+ errorMessage =
+ NLS.bind(EmulatorNLS.ERR_SrcDestComposite_InvalidFillingBase,
+ phoneNumberError, emulatorError);
+ }
+
+ }
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/exception/InstanceNotFoundException.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/exception/InstanceNotFoundException.java
new file mode 100644
index 0000000..c58a9a3
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/exception/InstanceNotFoundException.java
@@ -0,0 +1,58 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.emulator.core.exception;
+
+import com.motorola.studio.android.common.exception.AndroidException;
+
+/**
+ * Represents an exception thrown every time a Android Emulator instance is not found
+ * at the device framework.
+ */
+@SuppressWarnings("serial")
+public class InstanceNotFoundException extends AndroidException
+{
+ /**
+ * Creates a new InstanceNotFoundException object.
+ */
+ public InstanceNotFoundException()
+ {
+ }
+
+ /**
+ * @param message the message used by the Exception.
+ */
+ public InstanceNotFoundException(String message)
+ {
+ super(message);
+ }
+
+ /**
+ * @param cause the associated cause.
+ */
+ public InstanceNotFoundException(Throwable cause)
+ {
+ super(cause);
+ }
+
+ /**
+ * @param message the message used by the Exception.
+ * @param cause the associated cause.
+ */
+ public InstanceNotFoundException(String message, Throwable cause)
+ {
+ super(message, cause);
+ }
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/exception/InstanceStartException.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/exception/InstanceStartException.java
new file mode 100644
index 0000000..2e328cf
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/exception/InstanceStartException.java
@@ -0,0 +1,57 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.emulator.core.exception;
+
+import com.motorola.studio.android.common.exception.AndroidException;
+
+/**
+ * Represents an exception thrown if the emulator instance cannot be started
+ */
+@SuppressWarnings("serial")
+public class InstanceStartException extends AndroidException
+{
+ /**
+ * Creates a new InstanceStartException object.
+ */
+ public InstanceStartException()
+ {
+ }
+
+ /**
+ * @param message the message used by the Exception.
+ */
+ public InstanceStartException(String message)
+ {
+ super(message);
+ }
+
+ /**
+ * @param cause the associated cause.
+ */
+ public InstanceStartException(Throwable cause)
+ {
+ super(cause);
+ }
+
+ /**
+ * @param message the message used by the Exception.
+ * @param cause the associated cause.
+ */
+ public InstanceStartException(String message, Throwable cause)
+ {
+ super(message, cause);
+ }
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/exception/InstanceStopException.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/exception/InstanceStopException.java
new file mode 100644
index 0000000..beadb1d
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/exception/InstanceStopException.java
@@ -0,0 +1,57 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.emulator.core.exception;
+
+import com.motorola.studio.android.common.exception.AndroidException;
+
+/**
+ * Represents an exception thrown if the emulator instance cannot be stopped
+ */
+@SuppressWarnings("serial")
+public class InstanceStopException extends AndroidException
+{
+ /**
+ * Creates a new InstanceStopException object.
+ */
+ public InstanceStopException()
+ {
+ }
+
+ /**
+ * @param message the message used by the Exception.
+ */
+ public InstanceStopException(String message)
+ {
+ super(message);
+ }
+
+ /**
+ * @param cause the associated cause.
+ */
+ public InstanceStopException(Throwable cause)
+ {
+ super(cause);
+ }
+
+ /**
+ * @param message the message used by the Exception.
+ * @param cause the associated cause.
+ */
+ public InstanceStopException(String message, Throwable cause)
+ {
+ super(message, cause);
+ }
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/exception/SkinException.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/exception/SkinException.java
new file mode 100644
index 0000000..79c96cd
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/exception/SkinException.java
@@ -0,0 +1,58 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.emulator.core.exception;
+
+import com.motorola.studio.android.common.exception.AndroidException;
+
+/**
+ * Represents an exception thrown every time the skin construction
+ * results in error.
+ */
+@SuppressWarnings("serial")
+public class SkinException extends AndroidException
+{
+ /**
+ * Creates a new SkinException object.
+ */
+ public SkinException()
+ {
+ }
+
+ /**
+ * @param message the message used by the Exception.
+ */
+ public SkinException(String message)
+ {
+ super(message);
+ }
+
+ /**
+ * @param cause the associated cause.
+ */
+ public SkinException(Throwable cause)
+ {
+ super(cause);
+ }
+
+ /**
+ * @param message the message used by the Exception.
+ * @param cause the associated cause.
+ */
+ public SkinException(String message, Throwable cause)
+ {
+ super(message, cause);
+ }
+} \ No newline at end of file
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/exception/StartCancelledException.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/exception/StartCancelledException.java
new file mode 100644
index 0000000..e9d8105
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/exception/StartCancelledException.java
@@ -0,0 +1,58 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.emulator.core.exception;
+
+import com.motorola.studio.android.common.exception.AndroidException;
+
+/**
+ * Represents the exception thrown when the user cancels the emulator
+ * start process
+ */
+@SuppressWarnings("serial")
+public class StartCancelledException extends AndroidException
+{
+ /**
+ * Creates a new StartCancelledException object.
+ */
+ public StartCancelledException()
+ {
+ }
+
+ /**
+ * @param message the message used by the Exception.
+ */
+ public StartCancelledException(String message)
+ {
+ super(message);
+ }
+
+ /**
+ * @param cause the associated cause.
+ */
+ public StartCancelledException(Throwable cause)
+ {
+ super(cause);
+ }
+
+ /**
+ * @param message the message used by the Exception.
+ * @param cause the associated cause.
+ */
+ public StartCancelledException(String message, Throwable cause)
+ {
+ super(message, cause);
+ }
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/exception/StartTimeoutException.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/exception/StartTimeoutException.java
new file mode 100644
index 0000000..dba69f0
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/exception/StartTimeoutException.java
@@ -0,0 +1,58 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.emulator.core.exception;
+
+import com.motorola.studio.android.common.exception.AndroidException;
+
+/**
+ * Represents the exception thrown when the emulator start process cannot end
+ * within the set timeout
+ */
+@SuppressWarnings("serial")
+public class StartTimeoutException extends AndroidException
+{
+ /**
+ * Creates a new StartTimeoutException object.
+ */
+ public StartTimeoutException()
+ {
+ }
+
+ /**
+ * @param message the message used by the Exception.
+ */
+ public StartTimeoutException(String message)
+ {
+ super(message);
+ }
+
+ /**
+ * @param cause the associated cause.
+ */
+ public StartTimeoutException(Throwable cause)
+ {
+ super(cause);
+ }
+
+ /**
+ * @param message the message used by the Exception.
+ * @param cause the associated cause.
+ */
+ public StartTimeoutException(String message, Throwable cause)
+ {
+ super(message, cause);
+ }
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/model/AbstractInputLogic.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/model/AbstractInputLogic.java
new file mode 100644
index 0000000..cb3cd60
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/model/AbstractInputLogic.java
@@ -0,0 +1,55 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.emulator.core.model;
+
+/**
+ * Basic implementation of the IInputLogic interface.
+ * Provides common methods for all types of input
+ *
+ */
+public abstract class AbstractInputLogic implements IInputLogic
+{
+ /**
+ * The instance associated to this input
+ */
+ private IAndroidEmulatorInstance instance;
+
+ /**
+ * Retrieves the instance associated with this input
+ * @return
+ */
+ public IAndroidEmulatorInstance getInstance()
+ {
+ return instance;
+ }
+
+ /**
+ * Executes whatever is necessary before sending keys
+ */
+ public void init(IAndroidEmulatorInstance instance)
+ {
+ this.instance = instance;
+ }
+
+ /**
+ * Executes whatever is necessary before disposing the object
+ */
+ public void dispose()
+ {
+ //do nothing
+ }
+
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/model/IAndroidEmulatorInstance.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/model/IAndroidEmulatorInstance.java
new file mode 100644
index 0000000..1542798
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/model/IAndroidEmulatorInstance.java
@@ -0,0 +1,228 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.emulator.core.model;
+
+import java.io.File;
+import java.util.Properties;
+
+import org.eclipse.sequoyah.vnc.protocol.lib.ProtocolHandle;
+import org.eclipse.swt.widgets.Composite;
+
+import com.motorola.studio.android.emulator.core.exception.InstanceStopException;
+
+/**
+ * DESCRIPTION:
+ * This class represents the Android Emulator instance contract.
+ *
+ * RESPONSIBILITY:
+ * Define which information is required from a device that wishes to use
+ * the Android Emulator viewer.
+ *
+ * COLABORATORS:
+ * None.
+ *
+ * USAGE:
+ * Use the methods to retrieve information from an Android Emulator device instance
+ */
+public interface IAndroidEmulatorInstance
+{
+ /**
+ * Gets the instance name in simplified format
+ *
+ * @return The instance name
+ */
+ String getName();
+
+ /**
+ * Gets the instance name in full format
+ *
+ * @return The instance name
+ */
+ String getFullName();
+
+ /**
+ * Gets the identifier for this instance, that is available when the emulator is started.
+ *
+ * @return the instance identifier
+ */
+ String getInstanceIdentifier();
+
+ /**
+ * Sets the current layout being used by this instance
+ *
+ * @param layoutName The new layout
+ */
+ void setCurrentLayout(String layoutName);
+
+ /**
+ * Gets the current known status of the flip or slide
+ *
+ * @return True if the flip/slide is closed; false otherwise
+ */
+ String getCurrentLayout();
+
+ /**
+ * Sets the parameter used to determine if this instance has CLI display or not
+ *
+ * @param hasCli True if the instance has CLI; false otherwise
+ */
+ void setHasCli(boolean hasCli);
+
+ /**
+ * Gets the parameter used to determine if this instance has CLI display or not
+ *
+ * @return True if the instance has CLI; false otherwise
+ */
+ boolean getHasCli();
+
+ /**
+ * Sets the Android protocol object created when connecting to the VM
+ *
+ * @param handle The Android protocol object provided when connecting
+ * the protocol
+ */
+ void setProtocolHandle(ProtocolHandle handle);
+
+ /**
+ * Gets the Android protocol object object that identifies the protocol connection
+ *
+ * @returns The Android protocol object representing the connection of this
+ * instance
+ */
+ ProtocolHandle getProtocolHandle();
+
+ /**
+ * Gets the id of the skin logic being used for this instance
+ *
+ * @return The skin id
+ */
+ String getSkinId();
+
+ /**
+ * Gets the path of the files being used to draw the skin for this instance
+ *
+ * @return A pointer to the folder that contains the files to be used
+ * to draw the skin for this instance
+ */
+ File getSkinPath();
+
+ /**
+ * Tests if the instance is started
+ *
+ * @return True if it is started; false otherwise
+ */
+ boolean isStarted();
+
+ /**
+ * Test if the instance is connected, i.e.
+ * The communication protocol is running
+ *
+ * @return True if it is connected; false otherwise
+ */
+ boolean isConnected();
+
+ /**
+ * Test if the instance is available, i.e.
+ * The instance type is available for the current SDK setup.
+ *
+ * @return True if it's available; false otherwise
+ */
+ boolean isAvailable();
+
+ /**
+ * Stops the Android Emulator instance
+ *
+ * @param force whether the stop should be forced or not
+ *
+ * @throws InstanceStopException If the instance fails to stop
+ */
+ void stop(boolean force) throws InstanceStopException;
+
+ /**
+ * Retrieves the collection of all instance properties
+ *
+ * @return The collection of instance properties
+ */
+ public Properties getProperties();
+
+ /**
+ * Retrieves the input logic used by this instance to send data to the emulator
+ *
+ * @return The input logic used by this instance
+ */
+ public IInputLogic getInputLogic();
+
+ /**
+ * Gets the emulator process associated to this instance when it's running
+ * @return the Process representing the emulator process
+ */
+ public Process getProcess();
+
+ /**
+ * Sets the emulator process associated to this emulator instance while it's running
+ * @param process
+ */
+ public void setProcess(Process process);
+
+ /**
+ * Performs any needed operations to change the instance orientation/rotation
+ *
+ * @param args Additional data provided by the skin to perform the operation
+ */
+ public void changeOrientation(String args);
+
+ /**
+ * Get the Android target that the instance is compliant to
+ *
+ * @return Android target that the instance is compliant to
+ */
+ public String getTarget();
+
+ /**
+ * Get the Android API level that the instance is compliant to
+ *
+ * @return Android API level that the instance is compliant to
+ */
+ public int getAPILevel();
+
+ /**
+ * Get the Android Emulator window handle
+ *
+ * @return Android target that the instance is compliant to
+ */
+ public long getWindowHandle();
+
+ /**
+ * Sets the handle of the emulator window associated with the instance
+ *
+ * @param handle
+ */
+ public void setWindowHandle(long handle);
+
+ /**
+ *
+ * @param contentComposite
+ */
+ public void setComposite(Composite composite);
+
+ /**
+ *
+ * @return
+ */
+ public Composite getComposite();
+
+
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/model/IEmulatorView.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/model/IEmulatorView.java
new file mode 100644
index 0000000..b87efa6
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/model/IEmulatorView.java
@@ -0,0 +1,36 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.emulator.core.model;
+
+/**
+ * DESCRIPTION:
+ * Defines the method that every Android Emulator view should have
+ * for self-updating
+ *
+ * RESPONSIBILITY:
+ * Define the method that allows the Android Emulator views to self
+ * update
+ *
+ * COLABORATORS:
+ * None.
+ *
+ * USAGE:
+ * Call the interface method for view refreshing.
+ */
+public interface IEmulatorView
+{
+ void refreshView();
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/model/IInputLogic.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/model/IInputLogic.java
new file mode 100644
index 0000000..2cb9c67
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/model/IInputLogic.java
@@ -0,0 +1,56 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.emulator.core.model;
+
+import java.util.Properties;
+
+public interface IInputLogic
+{
+ void init(IAndroidEmulatorInstance instance);
+
+ void dispose();
+
+ /**
+ * Send a key press event
+ *
+ * @param character the correspondent character
+ * @param keycode the keycode of the key pressed
+ * @param keyCodeMap the skin keycode map
+ */
+ void sendKey(int character, int keycode, Properties keyCodeMap);
+
+ /**
+ * Send a click event
+ *
+ * @param code the code to be sent
+ * @param pressed key pressed - yes or no
+ */
+ void sendClick(int code, boolean pressed);
+
+ /**
+ * Send a click event
+ *
+ * @param code the code to be sent
+ * @param pressed key pressed - yes or no
+ */
+ void sendClick(String code, boolean pressed);
+
+ void sendMouseUp(int x, int y);
+
+ void sendMouseDown(int x, int y);
+
+ void sendMouseMove(int x, int y);
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/skin/AndroidPressKey.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/skin/AndroidPressKey.java
new file mode 100644
index 0000000..e66bae0
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/skin/AndroidPressKey.java
@@ -0,0 +1,251 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.emulator.core.skin;
+
+import java.util.Collection;
+import java.util.LinkedHashSet;
+import java.util.StringTokenizer;
+
+import org.eclipse.swt.graphics.Rectangle;
+
+/**
+ * DESCRIPTION:
+ * This is a default implementation of the interface IAndroidEmulatorKey
+ *
+ * RESPONSIBILITY:
+ * - Provide an easy way to find the keys pressed during mouse interaction
+ * at skin
+ * - Provide means of retrieving the keysym associated with each key
+ *
+ * COLABORATORS:
+ * None.
+ *
+ * USAGE:
+ * Provide a coordinate to the isInsideKey methods to test if the coordinate
+ * is inside the key area
+ * Use the getKeysym of a key to retrieve the code that needs to be sent in a
+ * key event message to the server, informing that a key was pressed or released
+ */
+public class AndroidPressKey implements IAndroidKey
+{
+ // Constants used in the isFlipValid method
+ private static final int FLIP_SLIDE_OPENED_ONLY = 0;
+
+ private static final int FLIP_SLIDE_CLOSED_ONLY = 1;
+
+ private static final int FLIP_SLIDE_OPENED_AND_CLOSED = 2;
+
+ /**
+ * Fields that can be found in an ordinary key.xml file
+ */
+ private final String name;
+
+ private final String toolTip;
+
+ private final String keycode;
+
+
+
+ private final Rectangle keyArea;
+
+ private final int flipSlideEnabledCode;
+
+ private final Collection<String> morphingModeCollection = new LinkedHashSet<String>();
+
+ /**
+ * Creates a new AndroidPressKey object.
+ *
+ * @param name The key name. This is usually a human readable skin, that
+ * provides key identification
+ * @param keycode The code that will be sent to server if the key is pressed
+ * @param toolTip The text that will be shown as the key tool tip.
+ * @param startx X coordinate of the upper left corner of the key
+ * @param starty Y coordinate of the upper left corner of the key
+ * @param endx X coordinate of the lower right corner of the key
+ * @param endy Y coordinate of the lower right corner of the key
+ * @param morphingModes A comma separated list of morphing modes to which this
+ * key applies, or null if not applicable
+ * @param flipenabled true if this key is valid in closed flip mode;
+ * false if the key is valid in opened flip mode
+ */
+ public AndroidPressKey(String name, String keycode, String toolTip, int startx, int starty,
+ int endx, int endy, String morphingModes, int flipEnabledCode)
+ {
+ this(name, keycode, toolTip, new Rectangle(startx, starty, endx - startx, endy - starty),
+ morphingModes, flipEnabledCode);
+ }
+
+ /**
+ * Creates a new AndroidPressKey object.
+ *
+ * @param name The key name. This is usually a human readable skin, that
+ * provides key identification
+ * @param keycode The code that will be sent to server if the key is pressed
+ * @param toolTip The text that will be shown as the key tool tip.
+ * @param key A rectangle that represents the key area at skin
+ * @param morphingModes A comma separated list of morphing modes to which this
+ * key applies, or null if not applicable
+ * @param flipenabled true if this key is valid in closed flip mode;
+ * false if the key is valid in opened flip mode
+ */
+ public AndroidPressKey(String name, String keycode, String toolTip, Rectangle key,
+ String morphingModes, int flipEnabledCode)
+ {
+ this.name = name;
+ this.keycode = keycode;
+ this.toolTip = toolTip;
+ this.keyArea = key;
+ this.flipSlideEnabledCode = flipEnabledCode;
+
+ if (morphingModes != null)
+ {
+ StringTokenizer st = new StringTokenizer(morphingModes, ",");
+ String token;
+
+ while (st.hasMoreTokens())
+ {
+ token = st.nextToken();
+ morphingModeCollection.add(token);
+ }
+ }
+ }
+
+ /**
+ * @see IAndroidKey#getKeysym()
+ */
+ public String getKeysym()
+ {
+ return keycode;
+ }
+
+ /**
+ * @see IAndroidKey#isInsideKey(int, int)
+ */
+ public boolean isInsideKey(int x, int y)
+ {
+ return keyArea.contains(x, y);
+ }
+
+ /**
+ * Retrieves the X coordinate of the lower right corner of the key
+ *
+ * @return X coordinate of the lower right corner of the key
+ */
+ public int getEndx()
+ {
+ return keyArea.x + keyArea.width;
+ }
+
+ /**
+ * Retrieves the Y coordinate of the lower right corner of the key
+ *
+ * @return Y coordinate of the lower right corner of the key
+ */
+ public int getEndy()
+ {
+ return keyArea.y + keyArea.height;
+ }
+
+ /**
+ * Tests if the key is valid in the current flip/slide mode
+ *
+ * @param isFlipSlideClosed True if the flip/slide is currently closed
+ * False if the flip/slide is currently opened
+ *
+ * @return true if the key is valid in the current flip/slide mode; false otherwise
+ */
+ public boolean isFlipSlideValid(boolean isFlipSlideClosed)
+ {
+ boolean flipSlideValid = false;
+
+ if ((flipSlideEnabledCode == FLIP_SLIDE_OPENED_AND_CLOSED)
+ || (isFlipSlideClosed && (flipSlideEnabledCode == FLIP_SLIDE_CLOSED_ONLY))
+ || (!isFlipSlideClosed && (flipSlideEnabledCode == FLIP_SLIDE_OPENED_ONLY)))
+ {
+ flipSlideValid = true;
+ }
+
+ return flipSlideValid;
+ }
+
+ /**
+ * Retrieves the key name
+ *
+ * @return The key name
+ */
+ public String getName()
+ {
+ return name;
+ }
+
+ /**
+ * Retrieves the tool tip text of the key.
+ *
+ * @return The tool tip text of the key.
+ */
+ public String getToolTip()
+ {
+ return toolTip;
+ }
+
+ /**
+ * Retrieves the X coordinate of the upper left corner of the key
+ *
+ * @return X coordinate of the upper left corner of the key
+ */
+ public int getStartx()
+ {
+ return keyArea.x;
+ }
+
+ /**
+ * Retrieves the Y coordinate of the upper left corner of the key
+ *
+ * @return Y coordinate of the upper left corner of the key
+ */
+ public int getStarty()
+ {
+ return keyArea.y;
+ }
+
+ /**
+ * Retrieves a rectangle that represents the key area at skin
+ *
+ * @return A rectangle that represents the key area at skin
+ */
+ public Rectangle getKeyArea()
+ {
+ return keyArea;
+ }
+
+ /**
+ * Tests if the key applies to the provided morphing mode
+ *
+ * @param morphingMode The morphing mode name
+ * @return true if the key applies to the morphing mode; false otherwise
+ */
+ public boolean hasMorphingMode(String morphingMode)
+ {
+ if (morphingMode != null)
+ {
+ return morphingModeCollection.contains(morphingMode);
+ }
+ else
+ {
+ return false;
+ }
+ }
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/skin/AndroidSkinBean.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/skin/AndroidSkinBean.java
new file mode 100644
index 0000000..32d7262
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/skin/AndroidSkinBean.java
@@ -0,0 +1,123 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.emulator.core.skin;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * DESCRIPTION:
+ * This bean holds data from the skin.xml file
+ *
+ * RESPONSIBILITY:
+ * - Provide an easy way to retrieve data read from skin.xml files
+ *
+ * COLABORATORS:
+ * None.
+ *
+ * USAGE:
+ * Call any of the interface methods to add or retrieve data to the class model
+ */
+public class AndroidSkinBean
+{
+ private final Map<String, Integer> skinPropertiesMap = new HashMap<String, Integer>();
+
+ /**
+ * Adds a skin property to the bean
+ *
+ * @param key The skin property key to use
+ * @param value The value of the skin property
+ */
+ public void addSkinPropertyValue(String key, int value)
+ {
+ skinPropertiesMap.put(key, value);
+ }
+
+ /**
+ * Retrieves a value of a skin property identified by key
+ *
+ * @param key The key that identifies the desired property
+ *
+ * @return The value of the desired property
+ */
+ public int getSkinPropertyValue(String key)
+ {
+ if (skinPropertiesMap.get(key) != null)
+ {
+ return skinPropertiesMap.get(key);
+ }
+ else
+ {
+ return 0;
+ }
+ }
+
+ /**
+ * Tests if open external display information is available at the skin
+ * which properties are stored at this bean
+ *
+ * @return True if open external display information is available;
+ * false otherwise
+ */
+ public boolean isOpenExternalDisplayAvailable()
+ {
+ boolean result = true;
+ Integer testObj1 = skinPropertiesMap.get(ISkinKeyXmlTags.SKIN_OPEN_EXTERNAL_VIEW_WIDTH);
+ Integer testObj2 = skinPropertiesMap.get(ISkinKeyXmlTags.SKIN_OPEN_EXTERNAL_VIEW_HEIGHT);
+
+ // If any of the width and height information is not available
+ // it is considered that there is not enough information about
+ // the open external display
+ if ((testObj1 == null) || (testObj2 == null))
+ {
+ result = false;
+ }
+
+ return result;
+ }
+
+ /**
+ * Tests if external display information is available at the skin
+ * which properties are stored at this bean
+ *
+ * @return True if external display information is available;
+ * false otherwise
+ */
+ public boolean isExternalDisplayAvailable()
+ {
+ boolean result = true;
+ Integer testObj1 = skinPropertiesMap.get(ISkinKeyXmlTags.SKIN_EXTERNAL_VIEW_WIDTH);
+ Integer testObj2 = skinPropertiesMap.get(ISkinKeyXmlTags.SKIN_EXTERNAL_VIEW_HEIGHT);
+
+ // If any of the width and height information is not available
+ // it is considered that there is not enough information about
+ // the external display
+ if ((testObj1 == null) || (testObj2 == null))
+ {
+ result = false;
+ }
+
+ return result;
+ }
+
+ public double getEmbeddedViewScale()
+ {
+ Integer testObj1 = skinPropertiesMap.get("embeddedViewScale");
+
+ return testObj1.intValue() / 10.0;
+ }
+
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/skin/IAndroidKey.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/skin/IAndroidKey.java
new file mode 100644
index 0000000..9e3237e
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/skin/IAndroidKey.java
@@ -0,0 +1,57 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.emulator.core.skin;
+
+import org.eclipse.swt.graphics.Rectangle;
+
+/**
+ * This interface should be used by anyone who wishes to implement a class
+ * which contain key information for Motorola handsets
+ */
+public interface IAndroidKey
+{
+ /**
+ * This method returns the keysym code associated to this key
+ *
+ * @return The keysym code
+ */
+ String getKeysym();
+
+ /**
+ * This method tests if a certain (x, y) coordinate is inside this key
+ *
+ * @param x The X coordinate
+ * @param y The Y coordinate
+ *
+ * @return true if the provided coordinate is internal to the key; false otherwise
+ */
+ boolean isInsideKey(int x, int y);
+
+ /**
+ * Retrieves a rectangle that corresponds to the drawing area of the
+ * key at the skin
+ *
+ * @return The key area
+ */
+ Rectangle getKeyArea();
+
+ /**
+ * Retrieves the text tool tip of the key.
+ *
+ * @return The text tool tip of the key.
+ */
+ String getToolTip();
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/skin/IAndroidSkin.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/skin/IAndroidSkin.java
new file mode 100644
index 0000000..48b85f6
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/skin/IAndroidSkin.java
@@ -0,0 +1,163 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.emulator.core.skin;
+
+import java.util.Collection;
+import java.util.Properties;
+
+import org.eclipse.sequoyah.vnc.vncviewer.config.EclipsePropertiesFileHandler;
+import org.eclipse.sequoyah.vnc.vncviewer.config.IPropertiesFileHandler;
+import org.eclipse.swt.graphics.ImageData;
+import org.eclipse.swt.graphics.RGB;
+
+import com.motorola.studio.android.emulator.core.exception.SkinException;
+
+/**
+ * This interface must be implemented by anyone who wishes to contribute to the
+ * skin extension point
+ */
+public interface IAndroidSkin
+{
+ IPropertiesFileHandler DEFAULT_PROPS_HANDLER = new EclipsePropertiesFileHandler();
+
+ String DEFAULT_VNC_CONFIG_FILE = "resources/vnc_viewer.conf";
+
+ /**
+ * Retrieves an image data object containing one pressed image pixels and attributes
+ *
+ * @return An image data object containing pixels and image attributes
+ *
+ * @throws SkinException If any problem occurs while retrieving the image data.
+ * If a new exception is being created, it is expected that it provides a message
+ * to display to the user
+ */
+ ImageData getPressedImageData(String layoutName) throws SkinException;
+
+ /**
+ * Retrieves an image data object containing one released image pixels and attributes
+ *
+ * @return An image data object containing pixels and image attributes
+ *
+ * @throws SkinException If any problem occurs while retrieving the image data
+ * If a new exception is being created, it is expected that it provides a message
+ * to display to the user
+ */
+ ImageData getReleasedImageData(String layoutName) throws SkinException;
+
+ /**
+ * Retrieves an image data object containing one enter image pixels and attributes
+ *
+ * @return An image data object containing pixels and image attributes
+ *
+ * @throws SkinException If any problem occurs while retrieving the image data
+ * If a new exception is being created, it is expected that it provides a message
+ * to display to the user
+ */
+ ImageData getEnterImageData(String layoutName) throws SkinException;
+
+ /**
+ * Retrieves a collection containing all keys that are supported by the
+ * handset represented by this skin
+ *
+ * @return The key collection read from skin
+ */
+ Collection<IAndroidKey> getKeyDataCollection(String layoutName);
+
+ /**
+ * Retrieves a bean containing all skin data that does not refer to the keys
+ *
+ * @return The skin bean
+ *
+ * @throws SkinException If any problem occurs while retrieving the skin data
+ * If a new exception is being created, it is expected that it provides a message
+ * to display to the user
+ */
+ AndroidSkinBean getSkinBean(String layoutName) throws SkinException;
+
+ /**
+ * Tests if flip is supported by the phone represented by this skin
+ *
+ * @return true if flip is supported; false otherwise
+ */
+ boolean isFlipSupported();
+
+ /**
+ * Set where the skin files are located based on the emulator root dir
+ *
+ * @param emulatorInstallDir Root of emulator installation
+ *
+ * @throws SkinException If the path provided does not contain a valid skin
+ */
+ void setSkinFilesPath(String emulatorInstallDir) throws SkinException;
+
+ /**
+ * Retrieves the names of all available layouts of the skin
+ *
+ * @return A collection containing the names of all available layouts
+ */
+ public Collection<String> getAvailableLayouts();
+
+ /**
+ * Checks if the current layout is rotated (i.e. demands screen rotation)
+ */
+ boolean isSwapWidthHeightNeededAtLayout(String layoutName);
+
+ /**
+ * Retrieves the command to send to the emulator to switch screen
+ *
+ * @return The command to send to the emulator to switch screen
+ */
+ String getLayoutScreenCommand(String layoutName);
+
+ /**
+ * Finds which layout comes next to referenceLayout
+ *
+ * @param referenceLayout The layout to be used as reference on next layout calculation
+ *
+ * @return The next layout name
+ */
+ public String getNextLayout(String referenceLayout);
+
+ /**
+ * Finds which layout is previous to referenceLayout
+ *
+ * @param referenceLayout The layout to be used as reference on previous layout calculation
+ *
+ * @return The previous layout name
+ */
+ public String getPreviousLayout(String referenceLayout);
+
+ /**
+ * Retrieves what is the background color to be applied at the provided layout
+ *
+ * @param layoutName The layout name in which to apply the background color
+ *
+ * @return A RGB object describing the color
+ */
+ public RGB getBackgroundColor(String layoutName);
+
+ /**
+ * @return
+ */
+ Properties getKeyCodes();
+
+ /**
+ * Return the dpad-rotation if present on a given layout or 0 otherwise.
+ * @param layoutName
+ * @return
+ */
+ int getDpadRotation(String layoutName);
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/skin/ISkinFrameworkConstants.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/skin/ISkinFrameworkConstants.java
new file mode 100644
index 0000000..f32a7cb
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/skin/ISkinFrameworkConstants.java
@@ -0,0 +1,33 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.emulator.core.skin;
+
+import com.motorola.studio.android.emulator.EmulatorPlugin;
+
+/**
+ * Interface containing constants that are relevant to the Android Emulator Skin
+ * Contribution Framework
+ */
+interface ISkinFrameworkConstants
+{
+ String SKIN_EXTENSION_POINT_ID = EmulatorPlugin.PLUGIN_ID + ".skin";
+
+ String SKIN_INFO_ATTR = "skinInfo";
+
+ String SKIN_NAME_ATTR = "skinName";
+
+ String SKIN_ID_ATTR = "skinId";
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/skin/ISkinKeyXmlTags.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/skin/ISkinKeyXmlTags.java
new file mode 100644
index 0000000..68fe9fa
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/skin/ISkinKeyXmlTags.java
@@ -0,0 +1,90 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.emulator.core.skin;
+
+/**
+ * Interface that contains the tags used into skin.xml and key.xml files
+ */
+public interface ISkinKeyXmlTags
+{
+ String SKIN_CONFIG = "skinConfig";
+
+ String SKIN_DOUBLE_SCREEN = "doubleScreen";
+
+ String SKIN_DOUBLE_VIEW = "doubleView";
+
+ String SKIN_KEYPAD_AREAS_NUMBER = "keypadAreasNumber";
+
+ String SKIN_VIEW_ZOOM_SCALE = "viewZoomScale";
+
+ String SKIN_MEMORY_SIZE = "memorySize";
+
+ String SKIN_REFRESH_RATE = "refreshRate";
+
+ String SKIN_INTERNAL_VIEW_X = "internalViewX";
+
+ String SKIN_INTERNAL_VIEW_Y = "internalViewY";
+
+ String SKIN_INTERNAL_VIEW_WIDTH = "internalViewWidth";
+
+ String SKIN_INTERNAL_VIEW_HEIGHT = "internalViewHeight";
+
+ String SKIN_INTERNAL_VIEW_DEPTH = "internalViewDepth";
+
+ String SKIN_OPEN_EXTERNAL_VIEW_X = "openExternalViewX";
+
+ String SKIN_OPEN_EXTERNAL_VIEW_Y = "openExternalViewY";
+
+ String SKIN_OPEN_EXTERNAL_VIEW_WIDTH = "openExternalViewWidth";
+
+ String SKIN_OPEN_EXTERNAL_VIEW_HEIGHT = "openExternalViewHeight";
+
+ String SKIN_OPEN_EXTERNAL_COLOR_DEPTH = "openExternalColorDepth";
+
+ String SKIN_EMBEDDED_VIEW_SCALE = "embeddedViewScale";
+
+ String SKIN_EXTERNAL_VIEW_X = "externalViewX";
+
+ String SKIN_EXTERNAL_VIEW_Y = "externalViewY";
+
+ String SKIN_EXTERNAL_VIEW_WIDTH = "externalViewWidth";
+
+ String SKIN_EXTERNAL_VIEW_HEIGHT = "externalViewHeight";
+
+ String SKIN_EXTERNAL_VIEW_DEPTH = "externalViewDepth";
+
+ String KEY_SKIN_KEY_INFO = "skinkeyinfo";
+
+ String KEY_DATA = "keyData";
+
+ String KEY_NAME = "name";
+
+ String KEY_CODE = "keycode";
+
+ String KEY_TOOLTIP = "hint";
+
+ String KEY_START_X = "startx";
+
+ String KEY_START_Y = "starty";
+
+ String KEY_END_X = "endx";
+
+ String KEY_END_Y = "endy";
+
+ String KEY_MORPHING_MODE = "morphingMode";
+
+ String KEY_FLIP_ENABLED = "flipenabled";
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/skin/SkinFramework.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/skin/SkinFramework.java
new file mode 100644
index 0000000..77e3e09
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/skin/SkinFramework.java
@@ -0,0 +1,219 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.emulator.core.skin;
+
+import static com.motorola.studio.android.common.log.StudioLogger.error;
+import static com.motorola.studio.android.common.log.StudioLogger.warn;
+
+import java.io.File;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IConfigurationElement;
+import org.eclipse.core.runtime.IExtension;
+import org.eclipse.core.runtime.InvalidRegistryObjectException;
+import org.eclipse.osgi.util.NLS;
+
+import com.motorola.studio.android.common.utilities.EclipseUtils;
+import com.motorola.studio.android.emulator.core.exception.SkinException;
+import com.motorola.studio.android.emulator.i18n.EmulatorNLS;
+
+/**
+* DESCRIPTION:
+* This class is the entrance point to the Android Emulator Skin Contribution Framework
+* module. Through this, other modules are able to retrieve information about
+* all plugins plugged at com.motorola.studio.android.emulator.skin extension point.
+*
+* RESPONSIBILITY:
+* - Provide information about plugged skins
+*
+* COLABORATORS:
+* None.
+*
+* USAGE:
+* The user gets the instance of SkinFramework and calls one of its
+* public methods to get information regarding installed plugins
+*/
+public class SkinFramework implements ISkinFrameworkConstants
+{
+ /**
+ * A map containing the ids of all plugged skins
+ */
+ private final Map<String, String> skinIdMap = new HashMap<String, String>();
+
+ /**
+ * Creates a new SkinFramework object.
+ */
+ public SkinFramework()
+ {
+ populateSkinIdMap();
+ }
+
+ /**
+ * Retrieves the IDs of every skin that is plugged to this framework
+ *
+ * USAGE: Any client plugin that wishes to have a collection of available
+ * skins should call this method
+ *
+ * @return A collection of installed skin names
+ */
+ public Collection<String> getAllInstalledSkinIds()
+ {
+ return skinIdMap.keySet();
+ }
+
+ /**
+ * Retrieves a skin object identified by the provided ID
+ *
+ * @param skinId The ID of the skin to be retrieved
+ *
+ * @return The skin object that have the ID provided
+ *
+ * @throws SkinException If the skin cannot be loaded by the skin framework
+ */
+ public IAndroidSkin getSkinById(String skinId) throws SkinException
+ {
+ return getSkinById(skinId, null);
+ }
+
+ /**
+ * Retrieves a skin object identified by the provided ID
+ *
+ * @param skinId The ID of the skin to be retrieved
+ *
+ * @param emulatorInstallDir Root of emulator installation
+ *
+ * @return The skin object that have the ID provided
+ *
+ * @throws SkinException If the skin cannot be loaded by the skin framework
+ */
+ public IAndroidSkin getSkinById(String skinId, File emulatorInstallDir) throws SkinException
+ {
+ IAndroidSkin selectedSkin = null;
+
+ if (skinId != null)
+ {
+ try
+ {
+ // If a skin is not found at the already loaded skins collection,
+ // one must be created
+ String extensionId = skinIdMap.get(skinId);
+ if (extensionId != null)
+ {
+ selectedSkin =
+ (IAndroidSkin) EclipseUtils.getExecutable(extensionId, SKIN_INFO_ATTR);
+ if (emulatorInstallDir != null)
+ {
+ selectedSkin.setSkinFilesPath(emulatorInstallDir.getAbsolutePath());
+ }
+ }
+ else
+ {
+ warn("The skin " + skinId
+ + " was requested but not retrieved. It is not installed.");
+ throw new SkinException(NLS.bind(
+ EmulatorNLS.WARN_SkinFramework_SkinNotInstalled, skinId));
+ }
+ }
+ catch (CoreException e)
+ {
+ error("It was not possible to load the IAndroidSkin object associated to " + skinId
+ + " skin. Cause: " + e.getMessage());
+
+ throw new SkinException(NLS.bind(EmulatorNLS.EXC_SkinFramework_CreateIAndroidSkin,
+ skinId));
+ }
+ }
+ else
+ {
+ error("A null parameter as skin name was provided for retrieving a skin object");
+ throw new SkinException(NLS.bind(EmulatorNLS.WARN_SkinFramework_SkinNotInstalled,
+ "\"\""));
+ }
+
+ if (selectedSkin == null)
+ {
+ // If no exception is thrown until this moment and the skin object is still null,
+ // then it is assumed that the plugin has problems. Those are the reasons that explain the assumption:
+ // 1. The only situation in which the EclipseUtils.getExecutable method returns null is
+ // when the provided name is non existent;
+ // 2. SKIN_INFO_ATTR is a constant that matches the constant from the skin extension
+ // specification. If the plugin was loaded even with a different name, Eclipse has failed on detecting
+ // if the declaring plugin was correctly built
+
+ error("The skin plugin is not accordant with the skin extension point specification");
+ throw new SkinException(EmulatorNLS.EXC_SkinFramework_CreateIAndroidSkin);
+ }
+
+ return selectedSkin;
+ }
+
+ /**
+ * Populates the skinIdMap map with the association of each skin name
+ * and its declaring extension identifier
+ */
+ private void populateSkinIdMap()
+ {
+ String skinId;
+ String extensionId;
+
+ IExtension[] skinExtensions = EclipseUtils.getInstalledPlugins(SKIN_EXTENSION_POINT_ID);
+ String currentId = "";
+
+ try
+ {
+ for (IExtension skinExtension : skinExtensions)
+ {
+ currentId = skinExtension.getUniqueIdentifier();
+ IConfigurationElement[] elements = skinExtension.getConfigurationElements();
+
+ for (IConfigurationElement element : elements)
+ {
+ if (element.getName().equals(SKIN_INFO_ATTR))
+ {
+ extensionId = skinExtension.getUniqueIdentifier();
+ skinId = element.getAttribute(SKIN_ID_ATTR);
+ if (skinId != null)
+ {
+ skinIdMap.put(skinId, extensionId);
+ }
+ else
+ {
+ warn("A invalid skin extension was not loaded because it did not declare its ID");
+ String title = EmulatorNLS.GEN_Warning;
+ String message =
+ NLS
+ .bind(
+ EmulatorNLS.WARN_SkinFramework_InvalidInstalledSkinsNotLoaded,
+ currentId);
+ EclipseUtils.showErrorDialog(title, message);
+ }
+ }
+ }
+ }
+ }
+ catch (InvalidRegistryObjectException e)
+ {
+ warn("There are invalid skin extensions that were not loaded due to an exception. Cause: "
+ + e.getMessage());
+ String title = EmulatorNLS.GEN_Warning;
+ EclipseUtils.showErrorDialog(title, NLS.bind(
+ EmulatorNLS.WARN_SkinFramework_InvalidInstalledSkinsNotLoaded, currentId));
+ }
+ }
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/utils/EmulatorCoreUtils.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/utils/EmulatorCoreUtils.java
new file mode 100644
index 0000000..ec7f5cb
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/utils/EmulatorCoreUtils.java
@@ -0,0 +1,175 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.emulator.core.utils;
+
+import java.util.Collection;
+import java.util.HashSet;
+
+import org.eclipse.sequoyah.vnc.protocol.lib.ProtocolHandle;
+import org.eclipse.ui.IViewPart;
+import org.eclipse.ui.IViewReference;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.PlatformUI;
+
+import com.motorola.studio.android.emulator.core.devfrm.DeviceFrameworkManager;
+import com.motorola.studio.android.emulator.core.exception.InstanceNotFoundException;
+import com.motorola.studio.android.emulator.core.model.IAndroidEmulatorInstance;
+import com.motorola.studio.android.emulator.core.model.IEmulatorView;
+
+/**
+ * DESCRIPTION:
+ * Utilities for Android Emulator restricted use
+ *
+ * RESPONSIBILITY:
+ * Provide common utility methods that can be used by any
+ * Android Emulator plugin.
+ *
+ * COLABORATORS:
+ * None
+ *
+ * USAGE:
+ * This class should not be instantiated and its methods should be called statically.
+ */
+public class EmulatorCoreUtils
+{
+ /**
+ * Retrieves all views that implement IEmulatorView interface
+ *
+ * @return All views that implement IEmulatorView interface
+ */
+ public static Collection<IEmulatorView> getAllAndroidViews()
+ {
+ final Collection<IEmulatorView> allViews = new HashSet<IEmulatorView>();
+
+ PlatformUI.getWorkbench().getDisplay().syncExec(new Runnable()
+ {
+ public void run()
+ {
+ IWorkbenchWindow activeWindow =
+ PlatformUI.getWorkbench().getActiveWorkbenchWindow();
+ if (activeWindow != null)
+ {
+ IWorkbenchPage[] activePages = activeWindow.getPages();
+ if (activePages != null)
+ {
+ for (IWorkbenchPage activePage : activePages)
+ {
+ if (activePage != null)
+ {
+ IViewReference[] allReferences = activePage.getViewReferences();
+ for (IViewReference ref : allReferences)
+ {
+ IViewPart aView = ref.getView(false);
+ if ((aView != null) && (aView instanceof IEmulatorView))
+ {
+ allViews.add((IEmulatorView) aView);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ });
+
+ return allViews;
+ }
+
+ /**
+ * Refresh all Emulator views
+ */
+ public static void refreshEmulatorViews()
+ {
+ Collection<IEmulatorView> allViews = EmulatorCoreUtils.getAllAndroidViews();
+ for (IEmulatorView view : allViews)
+ {
+ view.refreshView();
+ }
+ }
+
+ /**
+ * Retrieves a Android Emulator instance mapped by the provided IP address
+ *
+ * @param identifier The IP address associated with the desired instance
+ *
+ * @return The Android Emulator instance that is working on the given address
+ *
+ * @throws InstanceNotFoundException If the instance is not found at
+ * device framework
+ */
+ public static IAndroidEmulatorInstance getAndroidInstanceByIdentifier(String identifier)
+ throws InstanceNotFoundException
+ {
+ IAndroidEmulatorInstance desiredDevice = null;
+
+ Collection<IAndroidEmulatorInstance> instanceList =
+ DeviceFrameworkManager.getInstance().getAllInstances();
+ for (IAndroidEmulatorInstance instance : instanceList)
+ {
+ String instanceIdentifier = instance.getInstanceIdentifier();
+ if ((instanceIdentifier != null) && instanceIdentifier.equals(identifier))
+ {
+ desiredDevice = instance;
+ break;
+ }
+ }
+
+ if (desiredDevice == null)
+ {
+ throw new InstanceNotFoundException();
+ }
+
+ return desiredDevice;
+ }
+
+ /**
+ * Retrieves a Android Emulator instance mapped by the provided
+ * protocol handle object
+ *
+ * @param handle The protocol handle object associated with the
+ * desired instance
+ *
+ * @return The Android Emulator instance that is working with
+ * the given protocol handle object
+ *
+ * @throws InstanceNotFoundException If the instance is not found at
+ * device framework
+ */
+ public static IAndroidEmulatorInstance getAndroidInstanceByHandle(ProtocolHandle handle)
+ throws InstanceNotFoundException
+ {
+ IAndroidEmulatorInstance desiredDevice = null;
+ Collection<IAndroidEmulatorInstance> instanceList =
+ DeviceFrameworkManager.getInstance().getAllInstances();
+ for (IAndroidEmulatorInstance instance : instanceList)
+ {
+ ProtocolHandle instanceHandle = instance.getProtocolHandle();
+ if ((instanceHandle != null) && instanceHandle.equals(handle))
+ {
+ desiredDevice = instance;
+ break;
+ }
+ }
+
+ if (desiredDevice == null)
+ {
+ throw new InstanceNotFoundException();
+ }
+
+ return desiredDevice;
+ }
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/utils/TelnetAndroidInput.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/utils/TelnetAndroidInput.java
new file mode 100644
index 0000000..f598218
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/utils/TelnetAndroidInput.java
@@ -0,0 +1,416 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.emulator.core.utils;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+
+import com.motorola.studio.android.adt.SdkUtils;
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.emulator.core.model.AbstractInputLogic;
+import com.motorola.studio.android.emulator.core.model.IAndroidEmulatorInstance;
+import com.motorola.studio.android.utilities.TelnetFrameworkAndroid;
+
+/**
+ * This class is responsible for sending input events to the Android Emulator
+ */
+@SuppressWarnings("serial")
+public class TelnetAndroidInput extends AbstractInputLogic
+{
+ /*
+ * Event types
+ */
+ public static final String EV_TYPE_SYN = "EV_SYN";
+
+ public static final String EV_TYPE_KEY = "EV_KEY";
+
+ public static final String EV_TYPE_ABS = "EV_ABS";
+
+ public static final String EV_TYPE_SW = "EV_SW";
+
+ public static final int EV_SW_LID = 0x00;
+
+ public static final String EV_SYN_REPORT = "0";
+
+ public static final String EV_ABS_X = "ABS_X";
+
+ public static final String EV_ABS_Y = "ABS_Y";
+
+ private static final String EV_TOUCH = "BTN_TOUCH";
+
+ private final Map<Integer, String> SPECIAL_KEY_MAPPING = new HashMap<Integer, String>()
+ {
+ {
+ put(16777219, "KEY_LEFT"); // left
+ put(16777217, "KEY_UP"); // top
+ put(16777220, "KEY_RIGHT"); // right
+ put(16777218, "KEY_DOWN"); // bottom
+ }
+ };
+
+ /*
+ * Event types
+ */
+ public static final int OPHONE_EV_TYPE_SYN = 0x00;
+
+ public static final int OPHONE_EV_TYPE_KEY = 0x01;
+
+ public static final int OPHONE_EV_TYPE_ABS = 0x03;
+
+ public static final int OPHONE_EV_TYPE_SW = 0x05;
+
+ public static final int OPHONE_EV_SW_LID = 0x00;
+
+ public static final int OPHONE_EV_SYN_REPORT = 0x00;
+
+ public static final int OPHONE_EV_ABS_X = 0x00;
+
+ public static final int OPHONE_EV_ABS_Y = 0x01;
+
+ private final Map<Integer, Integer> OPHONE_SPECIAL_KEY_MAPPING =
+ new HashMap<Integer, Integer>()
+ {
+ {
+ put(16777219, 0x069); // left
+ put(16777217, 0x067); // top
+ put(16777220, 0x06a); // right
+ put(16777218, 0x06c); // bottom
+
+ }
+ };
+
+ private static final int OPHONE_KEY_BACKSPACE = 0x00e;
+
+ private static final int OPHONE_KEY_SPACE = 0x039;
+
+ private static final int OPHONE_KEY_ENTER = 0x01c;
+
+ private static final int OPHONE_EV_TOUCH = 0x14A;
+
+ // mouse position x
+ private int oldX;
+
+ // mouse position y
+ private int oldY;
+
+ // telnet connection
+ private final TelnetFrameworkAndroid telnet = new TelnetFrameworkAndroid();
+
+ /**
+ * Open the Telnet connection and initialize the communication
+ *
+ * @see com.motorola.studio.android.emulator.core.model.AbstractInputLogic#init(com.motorola.studio.android.emulator.core.model.IAndroidEmulatorInstance)
+ */
+ @Override
+ public void init(IAndroidEmulatorInstance instance)
+ {
+ super.init(instance);
+
+ String deviceSerial = instance.getInstanceIdentifier();
+
+ String serial = deviceSerial.substring(deviceSerial.length() - 4, deviceSerial.length());
+ try
+ {
+ telnet.connect("localhost", Integer.parseInt(serial));
+ }
+ catch (IOException e)
+ {
+ //Do nothing
+ }
+ }
+
+ /**
+ * Close Telnet connection
+ *
+ * @see com.motorola.studio.android.emulator.core.model.AbstractInputLogic#dispose()
+ */
+ @Override
+ public void dispose()
+ {
+ try
+ {
+ telnet.disconnect();
+ }
+ catch (IOException e)
+ {
+ // Do nothing
+ }
+ }
+
+ /**
+ * Send mouse down event
+ *
+ * @see com.motorola.studio.android.emulator.core.model.IInputLogic#sendMouseDown(int, int)
+ */
+ public void sendMouseDown(int x, int y)
+ {
+ sendAndroidEvent(EV_TYPE_ABS, EV_ABS_X, x);
+ sendAndroidEvent(EV_TYPE_ABS, EV_ABS_Y, y);
+ if (SdkUtils.isOphoneSDK())
+ {
+ sendAndroidEvent(EV_TYPE_KEY, OPHONE_EV_TOUCH, true);
+ sendAndroidEvent(EV_TYPE_SYN, OPHONE_EV_SYN_REPORT, 0);
+ }
+ else
+ {
+ sendAndroidEvent(EV_TYPE_KEY, EV_TOUCH, true);
+ sendAndroidEvent(EV_TYPE_SYN, EV_SYN_REPORT, 0);
+ }
+
+ oldX = x;
+ oldY = y;
+ }
+
+ /**
+ * Send mouse up event
+ *
+ * @see com.motorola.studio.android.emulator.core.model.IInputLogic#sendMouseUp(int, int)
+ */
+ public void sendMouseUp(int x, int y)
+ {
+ if (SdkUtils.isOphoneSDK())
+ {
+ sendAndroidEvent(EV_TYPE_KEY, OPHONE_EV_TOUCH, false);
+ sendAndroidEvent(EV_TYPE_SYN, OPHONE_EV_SYN_REPORT, 0);
+ }
+ else
+ {
+ sendAndroidEvent(EV_TYPE_KEY, EV_TOUCH, false);
+ sendAndroidEvent(EV_TYPE_SYN, EV_SYN_REPORT, 0);
+ }
+ }
+
+ /**
+ * Send mouse move event
+ *
+ * @see com.motorola.studio.android.emulator.core.model.IInputLogic#sendMouseMove(int, int)
+ */
+ public void sendMouseMove(int x, int y)
+ {
+ if (oldX != x)
+ {
+ sendAndroidEvent(EV_TYPE_ABS, EV_ABS_X, x);
+ oldX = x;
+ }
+ if (oldY != y)
+ {
+ sendAndroidEvent(EV_TYPE_ABS, EV_ABS_Y, y);
+ oldY = y;
+ }
+ if (SdkUtils.isOphoneSDK())
+ {
+ sendAndroidEvent(EV_TYPE_SYN, OPHONE_EV_SYN_REPORT, 0);
+ }
+ else
+ {
+ sendAndroidEvent(EV_TYPE_SYN, EV_SYN_REPORT, 0);
+ }
+ }
+
+ /**
+ * Send a generic event to the emulator
+ *
+ * @param type event type
+ * @param keysym event definition
+ * @param pressed key pressed - yes or no
+ */
+ public void sendAndroidEvent(String type, String keysym, boolean pressed)
+ {
+ sendAndroidEvent(type, keysym, pressed ? 1 : 0);
+ }
+
+ public void sendAndroidEvent(String type, int keysym, boolean pressed)
+ {
+ sendAndroidEvent(type, keysym, pressed ? 1 : 0);
+ }
+
+ private void sendAndroidEvent(String type, int keysym, int i)
+ {
+ try
+ {
+ telnet.write("event send " + type + ":" + keysym + ":" + i, null);
+ }
+ catch (IOException e)
+ {
+ StudioLogger.error("Failed to send generic event to Emulator");
+ }
+ }
+
+ /**
+ * Send a complete event to the Emulator
+ * Some events, like a key press, need two events (key pressed/released)
+ * to be executed. This method send both in order to execute the event.
+ *
+ * @param type event type
+ * @param keysym event definition
+ */
+ public void sendAndroidEvent(String type, String keysym)
+ {
+ sendAndroidEvent(type, keysym, true);
+ sendAndroidEvent(type, keysym, false);
+ }
+
+ /**
+ * Send a complete event to the Emulator
+ * Some events, like a key press, need two events (key pressed/released)
+ * to be executed. This method send both in order to execute the event.
+ *
+ * @param type event type
+ * @param keysym event definition
+ */
+ public void sendAndroidEvent(String type, int keysym)
+ {
+ sendAndroidEvent(type, keysym, true);
+ sendAndroidEvent(type, keysym, false);
+ }
+
+ /**
+ * Send a generic event to the emulator
+ *
+ * @param type event type
+ * @param keysym event definition
+ * @param value parameter
+ */
+ private void sendAndroidEvent(String type, String keysym, int value)
+ {
+ try
+ {
+ telnet.write("event send " + type + ":" + keysym + ":" + value, null);
+ }
+ catch (IOException e)
+ {
+ StudioLogger.error("Failed to send generic event to Emulator");
+ }
+ }
+
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.emulator.core.model.IInputLogic#sendKey(int, int)
+ */
+ public void sendKey(int character, int keycode, Properties keyCodeMap)
+ {
+ /*
+ * Check if it's a character
+ */
+ if (character > 0)
+ {
+ String text = String.valueOf((char) character);
+ // check it's a blank space
+ if (text.equals(" "))
+ {
+ if (SdkUtils.isOphoneSDK())
+ {
+ sendAndroidEvent(EV_TYPE_KEY, OPHONE_KEY_SPACE);
+ }
+ else
+ {
+ sendAndroidEvent(EV_TYPE_KEY, "KEY_SPACE");
+ }
+ }
+ // check if it's a backspace
+ else if (text.equals("\b"))
+ {
+ if (SdkUtils.isOphoneSDK())
+ {
+ sendAndroidEvent(EV_TYPE_KEY, OPHONE_KEY_BACKSPACE);
+ }
+ else
+ {
+ sendAndroidEvent(EV_TYPE_KEY, "KEY_BACKSPACE");
+ }
+ }
+ // check if it's an enter
+ else if (text.equals("\r"))
+ {
+ if (SdkUtils.isOphoneSDK())
+ {
+ sendAndroidEvent(EV_TYPE_KEY, OPHONE_KEY_ENTER);
+ }
+ else
+ {
+ sendAndroidEvent(EV_TYPE_KEY, "KEY_ENTER");
+ }
+ }
+ else
+ {
+ if (keyCodeMap != null)
+ {
+ String keyCode = keyCodeMap.getProperty(text.toUpperCase().trim());
+ if (keyCode != null)
+ {
+ sendAndroidEvent(EV_TYPE_KEY, keyCode);
+ }
+ }
+ }
+ }
+ else
+ {
+ if (!SdkUtils.isOphoneSDK())
+ {
+ String keycode_str = null;
+ if (SPECIAL_KEY_MAPPING.containsKey(keycode))
+ {
+ keycode_str = SPECIAL_KEY_MAPPING.get(keycode);
+ }
+ if (keycode_str != null)
+ {
+ sendAndroidEvent(EV_TYPE_KEY, keycode_str);
+ }
+ }
+ else
+ {
+ if (OPHONE_SPECIAL_KEY_MAPPING.containsKey(keycode))
+ {
+ keycode = OPHONE_SPECIAL_KEY_MAPPING.get(keycode);
+ }
+ if (keycode != -1)
+ {
+ sendAndroidEvent(EV_TYPE_KEY, keycode);
+ }
+ }
+ }
+
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.emulator.core.model.IInputLogic#sendClick(int, boolean)
+ */
+ public void sendClick(String code, boolean pressed)
+ {
+ sendAndroidEvent(EV_TYPE_KEY, code, pressed ? 1 : 0);
+ }
+
+ public void sendClick(int code, boolean pressed)
+ {
+ sendAndroidEvent(EV_TYPE_KEY, code, pressed ? 1 : 0);
+ }
+
+ public void sendWindowScale(double zoomFactor)
+ {
+ try
+ {
+ telnet.write("window scale " + zoomFactor, null);
+ }
+ catch (IOException e)
+ {
+ StudioLogger.error("Failed to send window scale to Emulator");
+ }
+
+ }
+
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/utils/VncAndroidInput.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/utils/VncAndroidInput.java
new file mode 100644
index 0000000..b5383ca
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/utils/VncAndroidInput.java
@@ -0,0 +1,96 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.emulator.core.utils;
+
+import java.util.Properties;
+
+import org.eclipse.sequoyah.vnc.protocol.PluginProtocolActionDelegate;
+import org.eclipse.sequoyah.vnc.protocol.lib.ProtocolMessage;
+
+import com.motorola.studio.android.emulator.core.model.AbstractInputLogic;
+
+public class VncAndroidInput extends AbstractInputLogic
+{
+ private static final int VNC_KEYEVENT_MESSAGE_CODE = 0x04;
+
+ private static final int VNC_POINTEREVENT_MESSAGE_CODE = 0x05;
+
+ private boolean buttonPressed;
+
+ private void sendAndroidMouseEventMessage(int x, int y)
+ {
+ ProtocolMessage message = new ProtocolMessage(VNC_POINTEREVENT_MESSAGE_CODE);
+ message.setFieldValue("buttonMask", (buttonPressed ? 1 : 0));
+ message.setFieldValue("x-position", x);
+ message.setFieldValue("y-position", y);
+
+ try
+ {
+ PluginProtocolActionDelegate.sendMessageToServer(getInstance().getProtocolHandle(),
+ message);
+ }
+ catch (Exception e)
+ {
+ // Do nothing
+ }
+ }
+
+ public void sendKey(int character, int keycode, Properties keyCodeMap)
+ {
+ ProtocolMessage message = new ProtocolMessage(VNC_KEYEVENT_MESSAGE_CODE);
+ message.setFieldValue("padding", 0);
+ message.setFieldValue("downFlag", 1);
+ message.setFieldValue("key", keycode);
+
+ try
+ {
+ PluginProtocolActionDelegate.sendMessageToServer(getInstance().getProtocolHandle(),
+ message);
+ }
+ catch (Exception e)
+ {
+ // Do nothing
+ }
+ }
+
+ public void sendClick(int code, boolean pressed)
+ {
+ //do nothing
+ }
+
+ public void sendClick(String code, boolean pressed)
+ {
+ //do nothing
+ }
+
+ public void sendMouseDown(int x, int y)
+ {
+ buttonPressed = true;
+ sendAndroidMouseEventMessage(x, y);
+ }
+
+ public void sendMouseMove(int x, int y)
+ {
+ sendAndroidMouseEventMessage(x, y);
+ }
+
+ public void sendMouseUp(int x, int y)
+ {
+ buttonPressed = false;
+ sendAndroidMouseEventMessage(x, y);
+ }
+
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/AndroidDeviceHandler.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/AndroidDeviceHandler.java
new file mode 100644
index 0000000..da411f5
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/AndroidDeviceHandler.java
@@ -0,0 +1,61 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.emulator.device;
+
+import org.eclipse.sequoyah.device.framework.model.IDeviceLauncher;
+import org.eclipse.sequoyah.device.framework.model.IInstance;
+import org.eclipse.sequoyah.device.framework.model.handler.IDeviceHandler;
+
+import com.motorola.studio.android.emulator.device.instance.AndroidDeviceInstance;
+
+/**
+ * DESCRIPTION:
+ * <br>
+ * This class represents a TmL IDeviceHandler for Android Emulator Instances.
+ * <br>
+ * RESPONSIBILITY:
+ * <br>
+ * - Create an IInstance object for Android Emulator Device Instances
+ * <br>
+ * COLABORATORS:
+ * <br>
+ * IDeviceHandler: implements this interface
+ * <br>
+ * USAGE:
+ * <br>
+ * This class is declared by the plugin.xml for the Android Emulator Device Instance declaration.
+ */
+public class AndroidDeviceHandler implements IDeviceHandler
+{
+
+ /**
+ * Creates an Android Emulator Device Instance with the given id.
+ *
+ * @param id the instance id
+ */
+ public IInstance createDeviceInstance(String id)
+ {
+ IInstance instance = new AndroidDeviceInstance();
+ instance.setId(id);
+ return instance;
+ }
+
+ public IDeviceLauncher createDeviceLauncher(IInstance instance)
+ {
+ return null;
+ }
+
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/AndroidDeviceUtils.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/AndroidDeviceUtils.java
new file mode 100644
index 0000000..82078c2
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/AndroidDeviceUtils.java
@@ -0,0 +1,74 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.emulator.device;
+
+import static com.motorola.studio.android.common.log.StudioLogger.error;
+import static com.motorola.studio.android.common.log.StudioLogger.info;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.sequoyah.device.framework.events.InstanceEvent;
+import org.eclipse.sequoyah.device.framework.events.InstanceEventManager;
+import org.eclipse.sequoyah.device.framework.events.InstanceEvent.InstanceEventType;
+
+import com.motorola.studio.android.emulator.EmulatorPlugin;
+import com.motorola.studio.android.emulator.device.instance.AndroidDeviceInstance;
+import com.motorola.studio.android.emulator.logic.AbstractStartAndroidEmulatorLogic.LogicMode;
+
+public class AndroidDeviceUtils
+{
+ public static synchronized void fireDummyStartTransition(AndroidDeviceInstance instance,
+ String serialNumber)
+ {
+ // if instance is not already started, is not starting and is already associated to a VM...
+ boolean instanceStarted = instance.isStarted();
+ boolean instanceIsStarting = instance.getStateMachineHandler().isTransitioning();
+ boolean vmAlreadyUp = instance.hasDevice();
+ instance.setNameSuffix(serialNumber + ", " + instance.getTarget());
+ InstanceEventManager.getInstance().notifyListeners(
+ new InstanceEvent(InstanceEventType.INSTANCE_UPDATED, instance));
+
+ if (vmAlreadyUp && !instanceStarted && !instanceIsStarting)
+ {
+ info("The TmL Instance is not started/Starting, but the emulator/VM is already online. Execute a dummy start service to force a transition to start status...");
+
+ Map<Object, Object> attributes = new LinkedHashMap<Object, Object>();
+ attributes.put(LogicMode.class, LogicMode.DO_NOTHING);
+ try
+ {
+ EmulatorPlugin.getStartServiceHandler().run(instance, attributes,
+ new NullProgressMonitor());
+ }
+ catch (Exception e)
+ {
+ error("Failed to run the dummy start service on " + instance + " : "
+ + e.getMessage());
+ }
+ }
+ }
+
+ /**
+ * Verifies whether or not a given AndroidDeviceInstance is running.
+ * @param androidInstance
+ * @return true if instance is running, false otherwise.
+ */
+ public static boolean isInstanceStarting(AndroidDeviceInstance androidInstance)
+ {
+ return androidInstance.getStateMachineHandler().isTransitioning();
+ }
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/CreateAVDOnStartupListener.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/CreateAVDOnStartupListener.java
new file mode 100644
index 0000000..13a87da
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/CreateAVDOnStartupListener.java
@@ -0,0 +1,73 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.emulator.device;
+
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.ui.IStartup;
+
+import com.motorola.studio.android.AndroidPlugin;
+import com.motorola.studio.android.adt.SdkUtils;
+import com.motorola.studio.android.common.preferences.DialogWithToggleUtils;
+import com.motorola.studio.android.emulator.device.handlers.OpenNewDeviceWizardHandler;
+import com.motorola.studio.android.emulator.i18n.EmulatorNLS;
+
+public class CreateAVDOnStartupListener implements IStartup
+{
+ private static final String SDK_CREATE_NEW_AVD_KEY = "create.avd.on.startup";
+
+ private static final Lock lock = new ReentrantReadWriteLock().writeLock();
+
+ private static boolean executed = false;
+
+ public void earlyStartup()
+ {
+ AndroidPlugin.getDefault().addSDKLoaderListener(new Runnable()
+ {
+ public void run()
+ {
+ lock.lock();
+ if (!executed
+ && ((SdkUtils.getAllTargets() != null) && (SdkUtils.getAllTargets().length > 0))
+ && ((SdkUtils.getAllValidVms() != null) && (SdkUtils.getAllValidVms().length == 0)))
+ {
+ if (DialogWithToggleUtils.showQuestion(SDK_CREATE_NEW_AVD_KEY,
+ EmulatorNLS.UI_SdkSetup_CreateAVD_Title,
+ EmulatorNLS.UI_SdkSetup_CreateAVD_Message))
+ {
+ OpenNewDeviceWizardHandler handler = new OpenNewDeviceWizardHandler();
+ try
+ {
+ handler.execute(new ExecutionEvent());
+ }
+ catch (ExecutionException e)
+ {
+ //do nothing
+ lock.unlock();
+ }
+ }
+ executed = true;
+ }
+ lock.unlock();
+ }
+ });
+
+ }
+
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/EmulatorDropSupportHandler.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/EmulatorDropSupportHandler.java
new file mode 100644
index 0000000..81ba24a
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/EmulatorDropSupportHandler.java
@@ -0,0 +1,37 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.emulator.device;
+
+import org.eclipse.sequoyah.device.framework.model.IInstance;
+import org.eclipse.swt.dnd.DropTargetEvent;
+import org.eclipse.swt.dnd.TransferData;
+
+import com.motorola.studio.android.devices.AbstractDeviceDropSupportHandler;
+import com.motorola.studio.android.emulator.EmulatorPlugin;
+
+public class EmulatorDropSupportHandler extends AbstractDeviceDropSupportHandler
+{
+
+ /* (non-Javadoc)
+ * @see org.eclipse.sequoyah.device.framework.model.IDeviceTypeDropSupport#canDrop(org.eclipse.sequoyah.device.framework.model.IInstance, org.eclipse.swt.dnd.TransferData, org.eclipse.swt.dnd.DropTargetEvent)
+ */
+ @Override
+ public boolean canDrop(IInstance instance, TransferData data, DropTargetEvent event)
+ {
+ return super.canDrop(instance, data, event)
+ && EmulatorPlugin.STATUS_ONLINE_ID.equals(instance.getStatus());
+ }
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/IAndroidDeviceConstants.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/IAndroidDeviceConstants.java
new file mode 100644
index 0000000..bc6c7db
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/IAndroidDeviceConstants.java
@@ -0,0 +1,30 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.emulator.device;
+
+import com.motorola.studio.android.emulator.EmulatorPlugin;
+
+/**
+ * DESCRIPTION:
+ * This interface contains constants used by Android Device Plug-in
+ */
+public interface IAndroidDeviceConstants
+{
+ // CONTEXT HELP
+ String MAIN_PAGE_HELP = EmulatorPlugin.PLUGIN_ID + ".newdevmain";
+
+ String STARTUP_OPTIONS_HELP = EmulatorPlugin.PLUGIN_ID + ".newdevstartup";
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/IDevicePropertiesConstants.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/IDevicePropertiesConstants.java
new file mode 100644
index 0000000..3064cb4
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/IDevicePropertiesConstants.java
@@ -0,0 +1,132 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.emulator.device;
+
+import java.io.File;
+
+/**
+ * DESCRIPTION:
+ * This interface contains the name and some default values of the Device Instance Properties
+ */
+public interface IDevicePropertiesConstants
+{
+ /**
+ * The key that identifies the description property of an Android Emulator
+ * device instance
+ */
+ String deviceDescription = "Description";
+
+ /**
+ * The key that identifies the emulator type property of an Android Emulator
+ * device instance
+ */
+ String emulatorDefId = "Emulator_Type";
+
+ /**
+ * The key that identifies the skin property of an Android Emulator
+ * device instance
+ */
+ String skinId = "Skin_Plugin_Id";
+
+ /**
+ * The key that identifies the timeout property of an Android Emulator
+ * device instance
+ */
+ String timeout = "Timeout";
+
+ /**
+ * The key that identifies the useVnc property of an Android Emulator
+ * device instance
+ */
+ String useVnc = "UseVnc";
+
+ /**
+ * The key that identifies the useVnc property of an Android Emulator
+ * device instance
+ */
+ String useProxy = "UseProxy";
+
+ /**
+ * The key that identifies the VM Target property of an Android Emulator
+ * device instance
+ */
+ String vmTarget = "Vm_Target";
+
+ /**
+ * The key that identifies the VM ABI type property of an Android Emulator
+ * device instance
+ */
+ String abiType = "Abi_Type";
+
+ /**
+ * The key that identifies the VM Skin property of an Android Emulator
+ * device instance
+ */
+ String vmSkin = "Vm_Skin";
+
+ /**
+ * The key that identifies the VM Path property of an Android Emulator
+ * device instance
+ */
+ String vmPath = "Vm_Path";
+
+ /**
+ * The key that identifies the command line arguments to be used
+ * when starting the emulator
+ */
+ String commandline = "Command_Line";
+
+ /**
+ * AVD Config file properties
+ */
+ String configSDCardPath = "sdcard.path";
+
+ String configSDCardSize = "sdcard.size";
+
+ /**
+ * The default vm path
+ */
+ String defaultVmPath = System.getProperty("user.home") + File.separator + ".android"
+ + File.separator + "avd";
+
+ String defaultVmFolderSuffix = ".avd";
+
+ String defaultUseProxyValue = "false";
+
+ /**
+ * The default timeout value (ms), which must be used when creating a new emulator
+ * instance
+ */
+ String defaultTimeoutValue = "120";
+
+ /**
+ * Whether to use snapshots
+ */
+
+ String useSnapshots = "UseSnapshot";
+
+ String defaultUseSnapshotValue = "false";
+
+ String saveSnapshot = "SaveSnapshot";
+
+ String dafaultSaveSnapshotValue = "false";
+
+ String startFromSnapshot = "startFromSnapshot";
+
+ String defaultstartFromSnapshotValue = "false";
+
+ String defaulSaveSnapshot = "false";
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/SequoyahInstanceBackward.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/SequoyahInstanceBackward.java
new file mode 100644
index 0000000..6ffcce7
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/SequoyahInstanceBackward.java
@@ -0,0 +1,78 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.emulator.device;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.sequoyah.device.common.utilities.exception.SequoyahException;
+import org.eclipse.sequoyah.device.framework.DevicePlugin;
+import org.eclipse.sequoyah.device.framework.factory.InstanceRegistry;
+import org.eclipse.sequoyah.device.framework.manager.InstanceManager;
+import org.eclipse.sequoyah.device.framework.model.IInstance;
+import org.eclipse.ui.IStartup;
+
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.emulator.device.refresh.InstancesListRefresh;
+
+/**
+ * This startup intent to iterate over the list of Android Emulator instances and change the emulator ID
+ * due the change of the plugin ids for the 1.3.0 release
+ *
+ */
+public class SequoyahInstanceBackward implements IStartup
+{
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.ui.IStartup#earlyStartup()
+ */
+ public void earlyStartup()
+ {
+ boolean refreshNeeded = false;
+ List<IInstance> instances =
+ new ArrayList<IInstance>(InstanceRegistry.getInstance().getInstances());
+ for (IInstance oldInstance : instances)
+ {
+ if (oldInstance.getDeviceTypeId().equals(
+ "com.motorola.studio.android.emulator.device.androidDevice"))
+ {
+ try
+ {
+ InstanceRegistry.getInstance().addInstance(
+ InstanceManager.createInstance(oldInstance.getName(),
+ "com.motorola.studio.android.emulator.androidDevice",
+ DevicePlugin.SEQUOYAH_STATUS_OFF, oldInstance.getProperties()));
+ InstanceRegistry.getInstance().removeInstance(oldInstance);
+ }
+ catch (SequoyahException e)
+ {
+ StudioLogger.error(
+ SequoyahInstanceBackward.class,
+ "An error ocurred trying to backward old instance: "
+ + oldInstance.getName(), e);
+ }
+
+ refreshNeeded = true;
+ }
+ }
+ if (refreshNeeded)
+ {
+ InstancesListRefresh.refresh();
+ }
+ }
+
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/SequoyahLogRedirector.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/SequoyahLogRedirector.java
new file mode 100644
index 0000000..2180065
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/SequoyahLogRedirector.java
@@ -0,0 +1,237 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.emulator.device;
+
+import org.eclipse.sequoyah.device.common.utilities.logger.LoggerConstants;
+
+import com.motorola.studio.android.common.log.StudioLogger;
+
+/**
+ * DESCRIPTION:
+ * This class implements the TmL logger interface to redirect all logs from
+ * TmL to the log system used by the emulator
+ *
+ * RESPONSIBILITY:
+ * Delegate the logging requests from TmL to the same logger used by the emulator
+ *
+ * COLABORATORS:
+ * None.
+ *
+ * USAGE:
+ * An instance of this class is constructed during the emulator log startup.
+ * This class is not supposed to be constructed by clients
+ */
+public class SequoyahLogRedirector implements org.eclipse.sequoyah.vnc.utilities.logger.ILogger,
+ org.eclipse.sequoyah.device.common.utilities.logger.ILogger
+{
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.sequoyah.vnc.utilities.logger.ILogger#debug(java.lang.Object)
+ */
+ public void debug(Object message)
+ {
+ if (message instanceof String)
+ {
+ StudioLogger.debug((String) message);
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.sequoyah.vnc.utilities.logger.ILogger#error(java.lang.Object, java.lang.Object)
+ */
+ public void error(Object message, Object throwable)
+ {
+ if (message instanceof String)
+ {
+ StudioLogger.error((String) message);
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.sequoyah.vnc.utilities.logger.ILogger#error(java.lang.Object)
+ */
+ public void error(Object message)
+ {
+ if (message instanceof String)
+ {
+ StudioLogger.error((String) message);
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.sequoyah.vnc.utilities.logger.ILogger#fatal(java.lang.Object)
+ */
+ public void fatal(Object message)
+ {
+ if (message instanceof String)
+ {
+ StudioLogger.fatal((String) message);
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.sequoyah.vnc.utilities.logger.ILogger#info(java.lang.Object)
+ */
+ public void info(Object message)
+ {
+ if (message instanceof String)
+ {
+ StudioLogger.info((String) message);
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.sequoyah.vnc.utilities.logger.ILogger#log(java.lang.Object, java.lang.Object, java.lang.Object)
+ */
+ public void log(Object priority, Object message, Object throwable)
+ {
+ log(priority, message);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.sequoyah.vnc.utilities.logger.ILogger#log(java.lang.Object, java.lang.Object)
+ */
+ public void log(Object priority, Object message)
+ {
+ String priorityStr = (String) priority;
+ if (message instanceof String)
+ {
+ if (priorityStr.equals(LoggerConstants.FATAL))
+ {
+ StudioLogger.fatal((String) message);
+ }
+ else if (priorityStr.equals(LoggerConstants.ERROR))
+ {
+ StudioLogger.error((String) message);
+ }
+ else if (priorityStr.equals(LoggerConstants.WARNING))
+ {
+ StudioLogger.warn((String) message);
+ }
+ else if (priorityStr.equals(LoggerConstants.INFO))
+ {
+ StudioLogger.info((String) message);
+ }
+ else if (priorityStr.equals(LoggerConstants.DEBUG))
+ {
+ StudioLogger.debug((String) message);
+ }
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.sequoyah.vnc.utilities.logger.ILogger#warn(java.lang.Object)
+ */
+ public void warn(Object message)
+ {
+ if (message instanceof String)
+ {
+ StudioLogger.warn((String) message);
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.sequoyah.vnc.utilities.logger.ILogger#getCurrentLevel()
+ */
+ public Object getCurrentLevel()
+ {
+ return LoggerConstants.TXT_ALL;
+ }
+
+ //************************************************
+ // FROM THIS POINT, NO METHODS WILL BE IMPLEMENTED
+ //************************************************
+
+ /*
+ *
+ */
+ public void configureLogger(Object arg0)
+ {
+ //nothing to do here
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.sequoyah.vnc.utilities.logger.ILogger#log(java.lang.Object)
+ */
+ public void log(Object arg0)
+ {
+ //nothing to do here
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.sequoyah.vnc.utilities.logger.ILogger#setLevel(java.lang.Object)
+ */
+ public void setLevel(Object arg0)
+ {
+ //nothing to do here
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.sequoyah.vnc.utilities.logger.ILogger#setLogToConsole()
+ */
+ public void setLogToConsole()
+ {
+ //nothing to do here
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.sequoyah.vnc.utilities.logger.ILogger#setLogToFile(java.lang.String, java.lang.String)
+ */
+ public void setLogToFile(String arg0, String arg1)
+ {
+ //nothing to do here
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.sequoyah.vnc.utilities.logger.ILogger#setLogToFile(java.lang.String)
+ */
+ public void setLogToFile(String arg0)
+ {
+ //nothing to do here
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.sequoyah.vnc.utilities.logger.ILogger#setLogToHTMLFile(java.lang.String)
+ */
+ public void setLogToHTMLFile(String arg0)
+ {
+ //nothing to do here
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.sequoyah.device.common.utilities.logger.ILogger#setLogToDefault()
+ */
+ public void setLogToDefault()
+ {
+ //nothing to do here
+ }
+
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/TmLDeviceFrameworkSupport.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/TmLDeviceFrameworkSupport.java
new file mode 100644
index 0000000..1d400cd
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/TmLDeviceFrameworkSupport.java
@@ -0,0 +1,88 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.emulator.device;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.sequoyah.device.common.utilities.exception.SequoyahException;
+import org.eclipse.sequoyah.device.framework.DeviceUtils;
+import org.eclipse.sequoyah.device.framework.factory.InstanceRegistry;
+import org.eclipse.sequoyah.device.framework.model.IDeviceType;
+import org.eclipse.sequoyah.device.framework.model.IInstance;
+import org.eclipse.sequoyah.device.framework.model.IService;
+import org.eclipse.sequoyah.device.framework.model.handler.ServiceHandler;
+
+import com.motorola.studio.android.emulator.core.devfrm.IDeviceFrameworkSupport;
+import com.motorola.studio.android.emulator.core.model.IAndroidEmulatorInstance;
+
+/**
+ * DESCRIPTION:
+ * This class attaches the TmL device framework to the Android Emulator plug-ins
+ *
+ * RESPONSIBILITY:
+ * to work with the TmL device framework
+ *
+ * COLABORATORS:
+ * None.
+ *
+ * USAGE:
+ * The class should be used by Eclipse only
+ */
+public class TmLDeviceFrameworkSupport implements IDeviceFrameworkSupport
+{
+ /**
+ * @see IDeviceFrameworkSupport#getAllInstances()
+ */
+ public Collection<IAndroidEmulatorInstance> getAllInstances()
+ {
+ List<IInstance> tmlInstances = InstanceRegistry.getInstance().getInstances();
+ Collection<IAndroidEmulatorInstance> androidCollection =
+ new HashSet<IAndroidEmulatorInstance>();
+ for (IInstance tmlInstance : tmlInstances)
+ {
+ if (tmlInstance instanceof IAndroidEmulatorInstance)
+ {
+ androidCollection.add((IAndroidEmulatorInstance) tmlInstance);
+ }
+ }
+
+ return androidCollection;
+ }
+
+ // This should be a contribution to TmL.
+ public static IStatus runService(IInstance instance, String serviceID,
+ Map<Object, Object> arguments, IProgressMonitor monitor) throws SequoyahException
+ {
+ IStatus runStatus = null;
+ IDeviceType deviceType = DeviceUtils.getDeviceType(instance);
+ for (IService service : deviceType.getServices())
+ {
+ if (service.getId().equals(serviceID))
+ {
+ ServiceHandler serviceHandler = (ServiceHandler) service.getHandler();
+ runStatus = serviceHandler.run(instance, arguments, monitor);
+ break;
+ }
+ }
+
+ return runStatus;
+ }
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/definition/AndroidEmuDefBean.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/definition/AndroidEmuDefBean.java
new file mode 100644
index 0000000..7f3c898
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/definition/AndroidEmuDefBean.java
@@ -0,0 +1,102 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.emulator.device.definition;
+
+import com.motorola.studio.android.emulator.logic.AbstractStartAndroidEmulatorLogic;
+import com.motorola.studio.android.nativeos.NativeUIUtils;
+
+/**
+ * This class holds an emulator definition
+ *
+ */
+class AndroidEmuDefBean
+{
+
+ // emulator name
+ private String name;
+
+ // emulator skin ID
+ private String skinId;
+
+ // emulator start logic
+ private AbstractStartAndroidEmulatorLogic startLogic = null;
+
+ // startup emulator command arguments
+ private String arguments = NativeUIUtils.getDefaultCommandLine();
+
+ /**
+ * Create an emulator definition
+ *
+ * @param name emulator name
+ * @param skinId emulator skin ID
+ * @param skinSize emulator skin size
+ */
+ AndroidEmuDefBean(String name, String skinId, String skinSize)
+ {
+ this.name = name;
+ this.skinId = skinId;
+ }
+
+ /**
+ * Get emulator name
+ *
+ * @return emulator name
+ */
+ String getName()
+ {
+ return name;
+ }
+
+ /**
+ * Get emulator skin ID
+ *
+ * @return emulator skin ID
+ */
+ String getSkinId()
+ {
+ return skinId;
+ }
+
+ /**
+ * Get startup emulator command arguments
+ *
+ * @return emulator command line arguments
+ */
+ String getCommandLineArguments()
+ {
+ return arguments;
+ }
+
+ /**
+ * Get emulator start logic
+ *
+ * @return emulator start logic
+ */
+ public AbstractStartAndroidEmulatorLogic getStartLogic()
+ {
+ return startLogic;
+ }
+
+ /**
+ * Set emulator start logic
+ * @param startLogic emulator start logic class
+ */
+ public void setStartLogic(AbstractStartAndroidEmulatorLogic startLogic)
+ {
+ this.startLogic = startLogic;
+ }
+
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/definition/AndroidEmuDefMgr.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/definition/AndroidEmuDefMgr.java
new file mode 100644
index 0000000..a1eeada
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/definition/AndroidEmuDefMgr.java
@@ -0,0 +1,265 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.emulator.device.definition;
+
+import static com.motorola.studio.android.common.log.StudioLogger.error;
+
+import java.util.Collection;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import org.eclipse.core.runtime.IConfigurationElement;
+import org.eclipse.core.runtime.IExtension;
+import org.eclipse.sequoyah.device.common.utilities.PluginUtils;
+
+import com.motorola.studio.android.common.utilities.EclipseUtils;
+import com.motorola.studio.android.emulator.core.model.IAndroidEmulatorInstance;
+import com.motorola.studio.android.emulator.core.model.IInputLogic;
+import com.motorola.studio.android.emulator.logic.AbstractStartAndroidEmulatorLogic;
+
+/**
+ * Contains methods for managing Android emulator definitions
+ *
+ */
+public class AndroidEmuDefMgr implements IAndroidEmuDefConstants
+{
+ // emulator definitions
+ private static final Map<String, AndroidEmuDefBean> emuDefs =
+ new LinkedHashMap<String, AndroidEmuDefBean>();
+
+ private final static AndroidEmuDefMgr instance = new AndroidEmuDefMgr();
+
+ /**
+ * Initialize class
+ */
+ private AndroidEmuDefMgr()
+ {
+ readExtensions();
+ }
+
+ /**
+ * Return class instance
+ *
+ * @return AndroidEmuDefMgr instance
+ */
+ public static AndroidEmuDefMgr getInstance()
+ {
+ return instance;
+ }
+
+ /**
+ * Read extension points to add emulator definitions
+ */
+ private static void readExtensions()
+ {
+ IExtension[] emuDefExtensions =
+ EclipseUtils.getInstalledPlugins(EMULATOR_DEFINITION_EXTENSION_POINT);
+
+ for (IExtension emuDefExtension : emuDefExtensions)
+ {
+ String id = emuDefExtension.getUniqueIdentifier();
+
+ // elements
+ IConfigurationElement[] elements = emuDefExtension.getConfigurationElements();
+
+ AndroidEmuDefBean bean = null;
+
+ boolean extensionOk = true;
+ for (IConfigurationElement element : elements)
+ {
+ if (element.getName().equals(ELEMENT_SKIN))
+ {
+ String skinId = element.getAttribute(ATT_SKIN_ID);
+ String skinSize = element.getAttribute(ATT_SKIN_SIZE);
+
+ if (!skinId.equals("") && !skinSize.equals(""))
+ {
+ bean = new AndroidEmuDefBean(emuDefExtension.getLabel(), skinId, skinSize);
+ }
+ else
+ {
+ extensionOk = false;
+ }
+ }
+ }
+
+ if (extensionOk)
+ {
+ emuDefs.put(id, bean);
+ }
+ }
+ }
+
+ /**
+ * Get all emulator IDs
+ *
+ * @return all emulator IDs
+ */
+ public Collection<String> getAllIds()
+ {
+ return emuDefs.keySet();
+ }
+
+ /**
+ * Retrieves the default emulator definition id, which should be initially set
+ * to the emulator devices being created
+ *
+ * @return The default emulator definition id
+ */
+ public String getDefaultId()
+ {
+ String defaultId = "";
+
+ Collection<String> ids = getAllIds();
+
+ /*
+ * NOTE: This is not considering more than one type of device
+ */
+ if (!ids.isEmpty())
+ {
+ Object[] idsArray = ids.toArray();
+ defaultId = idsArray[0].toString();
+ }
+
+ return defaultId;
+ }
+
+ /**
+ * Get all emulator names
+ *
+ * @return all emulator names
+ */
+ public String[] getAllNames()
+ {
+ String[] allNames = new String[emuDefs.size()];
+
+ int i = 0;
+ for (AndroidEmuDefBean bean : emuDefs.values())
+ {
+ allNames[i++] = bean.getName();
+ }
+
+ return allNames;
+ }
+
+ /**
+ * Get emulator name given its ID
+ *
+ * @param emuDefId emulator ID
+ * @return emulator name
+ */
+ public String getName(String emuDefId)
+ {
+ String name = emuDefId;
+ AndroidEmuDefBean bean = emuDefs.get(emuDefId);
+
+ if (bean != null)
+ {
+ name = bean.getName();
+ }
+
+ return name;
+ }
+
+ /**
+ * Get emulator skin ID given its ID
+ *
+ * @param emuDefId emulator ID
+ * @return emulator skin ID
+ */
+ public String getSkinId(String emuDefId)
+ {
+ String skinId = "";
+ AndroidEmuDefBean bean = emuDefs.get(emuDefId);
+
+ if (bean != null)
+ {
+ skinId = bean.getSkinId();
+ }
+
+ return skinId;
+ }
+
+ /**
+ * Get startup emulator command arguments
+ *
+ * @param emuDefId emulator ID
+ * @return emulator command line arguments
+ */
+ public String getCommandLineArgumentsForEmuDefinition(String emuDefId)
+ {
+ String arguments = "";
+ AndroidEmuDefBean bean = emuDefs.get(emuDefId);
+
+ if (bean != null)
+ {
+ arguments = bean.getCommandLineArguments();
+ }
+
+ return arguments;
+ }
+
+ /**
+ * Retrieve the input logic of the given emulator definition
+ * @param emuDefId id of the extension that declare emulator definitions.
+ * @return the IAndroidLogic associated to the given emulator definitions
+ */
+ public IInputLogic getInputLogic(String emuDefId, IAndroidEmulatorInstance instance)
+ {
+
+ IInputLogic inputLogic = null;
+
+ try
+ {
+ inputLogic = (IInputLogic) PluginUtils.getExecutable(emuDefId, "inputLogic");
+ inputLogic.init(instance);
+ }
+ catch (Exception e)
+ {
+ error("Could not retrieve the input logic from definition " + emuDefId);
+ }
+
+ return inputLogic;
+ }
+
+ /**
+ * Get the start logic for the given emulator definition
+ * @param emuDefId id of the extension that declare emulator definitions.
+ * @return the IAndroidLogic associated to the given emulator definitions
+ */
+ public AbstractStartAndroidEmulatorLogic getStartLogic(String emuDefId)
+ {
+
+ AbstractStartAndroidEmulatorLogic startLogic = null;
+ AndroidEmuDefBean bean = emuDefs.get(emuDefId);
+
+ try
+ {
+ if (bean.getStartLogic() == null)
+ {
+ bean.setStartLogic((AbstractStartAndroidEmulatorLogic) PluginUtils.getExecutable(
+ emuDefId, "startLogic"));
+ }
+ startLogic = bean.getStartLogic();
+ }
+ catch (Exception e)
+ {
+ error("Could not retrieve the Start logic for " + emuDefId);
+ }
+
+ return startLogic;
+ }
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/definition/IAndroidEmuDefConstants.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/definition/IAndroidEmuDefConstants.java
new file mode 100644
index 0000000..92ef40a
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/definition/IAndroidEmuDefConstants.java
@@ -0,0 +1,42 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.emulator.device.definition;
+
+/**
+ * This interface contains constants used when managing Android emulator definitions
+ *
+ */
+public interface IAndroidEmuDefConstants
+{
+ String EMULATOR_DEFINITION_EXTENSION_POINT =
+ "com.motorola.studio.android.emulator.androidEmulatorDefinition";
+
+ String ELEMENT_SKIN = "skin";
+
+ String ATT_SKIN_ID = "id";
+
+ String ATT_SKIN_SIZE = "size";
+
+ String SKIN_SIZE_HVGA = "HVGA";
+
+ String SKIN_SIZE_HVGAL = "HVGA-L";
+
+ String SKIN_SIZE_HVGAP = "HVGA-P";
+
+ String SKIN_SIZE_QVGAL = "QVGA-L";
+
+ String SKIN_SIZE_QVGAP = "QVGA-P";
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/handlers/OpenNewDeviceWizardHandler.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/handlers/OpenNewDeviceWizardHandler.java
new file mode 100644
index 0000000..64c295b
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/handlers/OpenNewDeviceWizardHandler.java
@@ -0,0 +1,52 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.emulator.device.handlers;
+
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.jface.wizard.WizardDialog;
+import org.eclipse.sequoyah.device.framework.ui.wizard.NewDeviceMenuWizard;
+import org.eclipse.ui.PlatformUI;
+
+import com.motorola.studio.android.emulator.EmulatorPlugin;
+
+public class OpenNewDeviceWizardHandler extends AbstractHandler
+{
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.core.commands.AbstractHandler#execute(org.eclipse.core.commands.ExecutionEvent)
+ */
+ public Object execute(ExecutionEvent event) throws ExecutionException
+ {
+ PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable()
+ {
+
+ public void run()
+ {
+ NewDeviceMenuWizard wizard = new NewDeviceMenuWizard();
+ wizard.setCurrentDeviceTypeId(EmulatorPlugin.DEVICE_ID);
+ WizardDialog dialog =
+ new WizardDialog(PlatformUI.getWorkbench().getActiveWorkbenchWindow()
+ .getShell(), wizard);
+ dialog.open();
+ }
+ });
+
+ return null;
+ }
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/init/InitServiceHandler.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/init/InitServiceHandler.java
new file mode 100644
index 0000000..a79d214
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/init/InitServiceHandler.java
@@ -0,0 +1,145 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.emulator.device.init;
+
+import static com.motorola.studio.android.common.log.StudioLogger.info;
+
+import java.util.List;
+
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.sequoyah.device.framework.events.InstanceEvent;
+import org.eclipse.sequoyah.device.framework.events.InstanceEvent.InstanceEventType;
+import org.eclipse.sequoyah.device.framework.events.InstanceEventManager;
+import org.eclipse.sequoyah.device.framework.model.IInstance;
+import org.eclipse.sequoyah.device.framework.model.IService;
+import org.eclipse.sequoyah.device.framework.model.handler.IServiceHandler;
+
+import com.motorola.studio.android.emulator.EmulatorPlugin;
+import com.motorola.studio.android.emulator.logic.IAndroidLogicInstance;
+
+/**
+ * DESCRIPTION:
+ * This class plugs the init procedure to a TmL service. This service implements the
+ * interface directly, because it causes the instance to have a particular behavior
+ * at the state machine.
+ *
+ * RESPONSIBILITY:
+ * Provide the initialization procedure to apply to every instance that
+ * is loaded at TmL device framework
+ *
+ * COLABORATORS:
+ * None.
+ *
+ * USAGE:
+ * This class is intended to be used by Eclipse only
+ */
+public class InitServiceHandler implements IServiceHandler
+{
+ /**
+ * The parent service handler
+ */
+ private IServiceHandler parent;
+
+ /**
+ * The service that launches the handler
+ */
+ private IService service;
+
+ /**
+ * @see IServiceHandler#run(IInstance)
+ */
+ public void run(IInstance instance)
+ {
+ if (instance instanceof IAndroidLogicInstance)
+ {
+ // The service definition defined (by convention) that
+ // stopped-dirty is the success state, and not available
+ // is the failure state. The exception is being thrown for
+ // the framework to set the state correctly.
+ instance.setStatus(EmulatorPlugin.STATUS_NOT_AVAILABLE);
+ InstanceEventManager.getInstance().notifyListeners(
+ new InstanceEvent(InstanceEventType.INSTANCE_TRANSITIONED, instance));
+ }
+
+ }
+
+ /**
+ * @see IServiceHandler#newInstance()
+ */
+ public IServiceHandler newInstance()
+ {
+ return new InitServiceHandler();
+ }
+
+ /**
+ * @see IServiceHandler#setParent(IServiceHandler)
+ */
+ public void setParent(IServiceHandler handler)
+ {
+ this.parent = handler;
+ }
+
+ /**
+ * @see IServiceHandler#setService(IService)
+ */
+ public void setService(IService service)
+ {
+ this.service = service;
+ }
+
+ /**
+ * @see IServiceHandler#updatingService(IInstance)
+ */
+ public void updatingService(IInstance instance)
+ {
+ info("Updating init emulator service");
+ }
+
+ /**
+ * @see IServiceHandler#clone()
+ * @see Cloneable#clone()
+ */
+ @Override
+ public Object clone()
+ {
+ IServiceHandler newHandler = newInstance();
+ newHandler.setParent(parent);
+ newHandler.setService(service);
+ return newHandler;
+ }
+
+ /**
+ * @see IServiceHandler#getParent()
+ */
+ public IServiceHandler getParent()
+ {
+ return parent;
+ }
+
+ /**
+ * @see IServiceHandler#getService()
+ */
+ public IService getService()
+ {
+ return service;
+ }
+
+ public IStatus singleInit(List<IInstance> instances)
+ {
+ return Status.OK_STATUS;
+ }
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/instance/AndroidDevInstBuilder.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/instance/AndroidDevInstBuilder.java
new file mode 100644
index 0000000..cee670c
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/instance/AndroidDevInstBuilder.java
@@ -0,0 +1,108 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.emulator.device.instance;
+
+import java.util.Properties;
+
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.sequoyah.device.framework.model.IInstanceBuilder;
+
+/**
+ * DESCRIPTION:
+ * <br>
+ * This class represents a TmL IInstanceBuilder for Android Emulator Instances.
+ * <br>
+ * It is only necessary because TmL's AbstractNewEmulatorInstanceWizard could not be used
+ * for Android Emulator Device Instance New Wizard implementation, so an IInstanceBuilder
+ * became necessary for our Wizard implementation.
+ * <br>
+ * This class should be removed if TmL's Wizard begins to be used since this implementation
+ * is very similar to TmL DefaultInstanceBuilder implementation and Tml's Wizard would use
+ * it instead.
+ * <br>
+ * RESPONSIBILITY:
+ * <br>
+ * - Hold necessary information about a Android Emulator Device Instance for it to be
+ * created.
+ * <br>
+ * COLABORATORS:
+ * <br>
+ * IInstanceBuilder: implements this interface
+ * <br>
+ * USAGE:
+ * <br>
+ * The AndroidNewWizard uses this class to hold information for creating a Android Emulator Device
+ * Instance and passes it on to TmL's InstanceManager to carry on the creation.
+ */
+public class AndroidDevInstBuilder implements IInstanceBuilder
+{
+ private final Properties properties;
+
+ private final String name;
+
+ /**
+ * Creates a new Instance Builder with the given information.
+ *
+ * @param instanceName the name of the instance to be created using this builder
+ * @param properties the properties of the instance to be created using this builder
+ */
+ public AndroidDevInstBuilder(String instanceName, Properties properties)
+ {
+ this.properties = properties;
+ this.name = instanceName;
+ }
+
+ /**
+ * Always returns <code>null</code> since this information does
+ * not make sense for Android Emulator Instances.
+ */
+ public IPath getLocationPath()
+ {
+ return null;
+ }
+
+ /**
+ * Retrieves the name of the instance to be created using this builder
+ *
+ * @return the name of the instance to be created using this builder
+ */
+ public String getProjectName()
+ {
+ return name;
+ }
+
+ /**
+ * Retrieves the properties of the instance to be created using this builder
+ *
+ * @return the properties of the instance to be created using this builder
+ */
+ public Properties getProperties()
+ {
+ return properties;
+ }
+
+ /**
+ * Retrieves the value of the give property key.
+ *
+ * @param key the key of the property
+ *
+ * @return the value for the property for the instance to be created using this builder
+ */
+ public String getProperty(String key)
+ {
+ return properties.getProperty(key);
+ }
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/instance/AndroidDevInstListener.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/instance/AndroidDevInstListener.java
new file mode 100644
index 0000000..8e9ff9b
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/instance/AndroidDevInstListener.java
@@ -0,0 +1,130 @@
+/* Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.emulator.device.instance;
+
+import org.eclipse.sequoyah.device.framework.DevicePlugin;
+import org.eclipse.sequoyah.device.framework.events.IInstanceListener;
+import org.eclipse.sequoyah.device.framework.events.InstanceAdapter;
+import org.eclipse.sequoyah.device.framework.events.InstanceEvent;
+import org.eclipse.sequoyah.device.framework.model.IInstance;
+
+import com.motorola.studio.android.adt.SdkUtils;
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.emulator.EmulatorPlugin;
+import com.motorola.studio.android.emulator.core.model.IAndroidEmulatorInstance;
+import com.motorola.studio.android.emulator.core.utils.EmulatorCoreUtils;
+import com.motorola.studio.android.emulator.ui.view.AbstractAndroidView;
+
+/**
+ * DESCRIPTION:
+ * Implementation of IInstanceListener for device related actions that depend
+ * on the TmL instance registry state.
+ * <br>
+ * RESPONSIBILITY:
+ * Guarantee that the emulator views are updated
+ * Run the initialization service when an instance is loaded
+ * <br>
+ * COLABORATORS:
+ * None.
+ * <br>
+ * USAGE:
+ * This class shall be used by Eclipse only.
+ */
+public class AndroidDevInstListener extends InstanceAdapter
+{
+
+ /**
+ * @see IInstanceListener#instanceLoaded(InstanceEvent)
+ */
+ @Override
+ public void instanceLoaded(InstanceEvent e)
+ {
+ IInstance instance = e.getInstance();
+
+ if (instance instanceof IAndroidEmulatorInstance)
+ {
+ // The service definition defined (by convention) that
+ // stopped-dirty is the success state, and not available
+ // is the failure state. The exception is being thrown for
+ // the framework to set the state correctly.
+ if (instance.getStatus().equals(DevicePlugin.SEQUOYAH_STATUS_OFF))
+ {
+ instance.setStatus(EmulatorPlugin.STATUS_NOT_AVAILABLE);
+ }
+ }
+ }
+
+ /**
+ * @see IInstanceListener#instanceDeleted(InstanceEvent)
+ */
+ @Override
+ public void instanceDeleted(InstanceEvent ev)
+ {
+ IInstance instance = ev.getInstance();
+ if (instance instanceof AndroidDeviceInstance)
+ {
+ SdkUtils.deleteVm(instance.getName());
+ }
+ }
+
+ /**
+ * @see IInstanceListener#instanceTransitioned(InstanceEvent)
+ */
+ @Override
+ public void instanceTransitioned(InstanceEvent e)
+ {
+ IInstance instance = e.getInstance();
+
+ if (instance instanceof AndroidDeviceInstance)
+ {
+ final AndroidDeviceInstance androidDevice = (AndroidDeviceInstance) instance;
+ StudioLogger.info("The android device instance status was updated: " + instance
+ + " Status: " + instance.getStatus());
+
+ if (androidDevice.isStarted())
+ {
+ String transitionId = e.getTransitionId();
+ if ((transitionId != null)
+ && transitionId.equals("com.motorola.studio.android.emulator.startService"))
+ {
+ // If it is coming from other state than the started,
+ // connect to VNC server
+ StudioLogger
+ .info("The emulator "
+ + instance
+ + " transitioned to started state. Try to estabilish a VNC connection...");
+
+ new Thread(new Runnable()
+ {
+ @Override
+ public void run()
+ {
+ AbstractAndroidView.showView();
+ EmulatorCoreUtils.refreshEmulatorViews();
+ }
+ }).start();
+ }
+ }
+ else if (instance.getStatus().equals(EmulatorPlugin.STATUS_OFFLINE))
+ {
+ androidDevice.resetRuntimeVariables();
+ EmulatorCoreUtils.refreshEmulatorViews();
+ }
+
+ }
+
+ }
+
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/instance/AndroidDeviceInstance.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/instance/AndroidDeviceInstance.java
new file mode 100644
index 0000000..b08f556
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/instance/AndroidDeviceInstance.java
@@ -0,0 +1,776 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.emulator.device.instance;
+
+import static com.motorola.studio.android.common.log.StudioLogger.error;
+import static com.motorola.studio.android.common.log.StudioLogger.info;
+import static com.motorola.studio.android.common.log.StudioLogger.warn;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+
+import org.eclipse.core.runtime.IAdaptable;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.sequoyah.device.framework.model.AbstractMobileInstance;
+import org.eclipse.sequoyah.device.framework.model.handler.ServiceHandler;
+import org.eclipse.sequoyah.vnc.protocol.PluginProtocolActionDelegate;
+import org.eclipse.sequoyah.vnc.protocol.lib.ProtocolHandle;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.ui.model.IWorkbenchAdapter;
+
+import com.android.sdklib.IAndroidTarget;
+import com.android.sdklib.internal.avd.AvdInfo;
+import com.motorola.studio.android.adt.DDMSFacade;
+import com.motorola.studio.android.adt.ISerialNumbered;
+import com.motorola.studio.android.adt.SdkUtils;
+import com.motorola.studio.android.emulator.EmulatorPlugin;
+import com.motorola.studio.android.emulator.core.exception.InstanceStopException;
+import com.motorola.studio.android.emulator.core.model.IAndroidEmulatorInstance;
+import com.motorola.studio.android.emulator.core.model.IInputLogic;
+import com.motorola.studio.android.emulator.device.IDevicePropertiesConstants;
+import com.motorola.studio.android.emulator.device.definition.AndroidEmuDefMgr;
+import com.motorola.studio.android.emulator.device.instance.options.StartupOptionsMgt;
+import com.motorola.studio.android.emulator.i18n.EmulatorNLS;
+import com.motorola.studio.android.emulator.logic.AbstractStartAndroidEmulatorLogic;
+import com.motorola.studio.android.emulator.logic.AndroidLogicUtils;
+import com.motorola.studio.android.emulator.logic.IAndroidLogicInstance;
+import com.motorola.studio.android.emulator.logic.stop.AndroidEmulatorStopper;
+import com.motorola.studio.android.nativeos.NativeUIUtils;
+
+/**
+ * DESCRIPTION:
+ * This class represents a Android Emulator instance
+ *
+ * RESPONSIBILITY:
+ * - Hold all attributes of an Android Emulator instance
+ * - Provide methods for testing if started and to stop the instance
+ *
+ * COLABORATORS:
+ * None.
+ *
+ * USAGE:
+ * This class is not meant to be used directly by the user. All commands to the
+ * Android Emulator instance shall be provided through the device framework.
+ */
+public class AndroidDeviceInstance extends AbstractMobileInstance implements IAndroidLogicInstance,
+ IWorkbenchAdapter, ISerialNumbered
+{
+ /**
+ * The protocol that is executed by this instance
+ */
+ private ProtocolHandle handle;
+
+ /**
+ * True if this instance has and supports CLI display; false otherwise
+ */
+ private boolean hasCli = false;
+
+ /**
+ * Current layout of this instance
+ */
+ private String currentLayout;
+
+ private Process process;
+
+ private long windowHandle;
+
+ private Composite composite;
+
+ /**
+ * Tests if this instance is in started or stopped state
+ *
+ * @return true if started, false if stopped
+ */
+ public boolean isStarted()
+ {
+ boolean instanceStarted = false;
+ String status = getStatus();
+ if (EmulatorPlugin.STATUS_ONLINE_ID.equals(status))
+ {
+ instanceStarted = true;
+ }
+
+ return instanceStarted;
+ }
+
+ public boolean isConnected()
+ {
+ if (super.getProperties()
+ .getProperty(IDevicePropertiesConstants.useVnc, NativeUIUtils.getDefaultUseVnc())
+ .equals("true"))
+ {
+ ProtocolHandle protocolHandle = getProtocolHandle();
+ if (protocolHandle != null)
+ {
+ return PluginProtocolActionDelegate.isProtocolRunning(protocolHandle);
+ }
+ return false;
+ }
+ else
+ {
+ return isStarted();
+ }
+
+ }
+
+ /**
+ * Method used by the emulator core to stop the instance on errors
+ *
+ * @see IAndroidEmulatorInstance#stop(boolean)
+ *
+ * @param force True if no interaction with the user is desired to
+ * perform the stop operation; false otherwise
+ */
+ public void stop(boolean force) throws InstanceStopException
+ {
+
+ info("Stopping the Android Emulator instance: " + this);
+
+ // FIRST SCENARIO: THE INSTANCE IS STARTED AND FAILED DURING OPERATION
+ // If the instance is in started state, the stop handler is called to delegate the state
+ // maintenance to TmL, which is correct.
+ if (isStarted())
+ {
+ if (getStateMachineHandler().isTransitioning())
+ {
+ // Free other threads to continue their jobs if one is already running the procedure
+ info("The instance is already executing a stop process: " + this);
+ throw new InstanceStopException("The instance is already executing a stop process");
+ }
+ else
+ {
+ info("Instance is started. Run the regular transition to stopped status: " + this);
+ ServiceHandler stopHandler = EmulatorPlugin.getStopServiceHandler();
+ if (stopHandler != null)
+ {
+ try
+ {
+ Map<Object, Object> args = new HashMap<Object, Object>();
+ args.put(EmulatorPlugin.FORCE_ATTR, true);
+ stopHandler.run(this, args);
+ }
+ catch (Exception e)
+ {
+ // Should not enter here, because the state has been tested before
+ error("The instance is not in an appropriate state for stopping");
+ }
+ info("Finished stop process: " + this);
+ }
+ }
+ }
+ // SECOND SCENARIO: THE INSTANCE WAS STARTING AND FAILED DURING THE PROCESS
+ // If the instance is not in started state, TmL will not allow the stop service to run. In
+ // this case, the methods must be called manually, and the state does not need maintenance
+ // (it has never been updated, as updating happens in the end of a transition)
+ else
+ {
+ if (getStateMachineHandler().isTransitioning())
+ {
+
+ info("Instance is not fully started yet. Execute a stop process directly..." + this);
+ final Job haltJob = new Job(EmulatorNLS.UI_AndroidDeviceInstance_StopInstanceJob)
+ {
+ @Override
+ protected IStatus run(IProgressMonitor monitor)
+ {
+ AndroidEmulatorStopper.stopInstance(AndroidDeviceInstance.this, true, true,
+ monitor);
+
+ if (getStatus().equals(EmulatorPlugin.STATUS_OFFLINE_NO_DATA))
+ {
+ info("Instance was initially in stopped/clean status. Rollback if needed."
+ + this);
+
+ File userdataFile = getUserdata();
+ if ((userdataFile != null) && userdataFile.exists())
+ {
+ info("Deleted data created during the start tentative."
+ + userdataFile);
+ userdataFile.delete();
+ }
+ }
+
+ info("Finished stop process: " + AndroidDeviceInstance.this);
+ return Status.OK_STATUS;
+ }
+ };
+ haltJob.schedule();
+ }
+ }
+ }
+
+ /**
+ *
+ */
+ @Override
+ public Properties getProperties()
+ {
+ Properties properties = super.getProperties();
+
+ // synchronize instance properties data with current sdk...
+ File fromSdk = SdkUtils.getUserdataDir(getName());
+ if (fromSdk != null)
+ {
+ properties.put(IDevicePropertiesConstants.vmPath, fromSdk.getAbsolutePath());
+ }
+
+ return properties;
+ }
+
+ /**
+ * @see IAndroidEmulatorInstance#getHasCli()
+ */
+ public boolean getHasCli()
+ {
+ return hasCli;
+ }
+
+ /**
+ * @see IAndroidEmulatorInstance#getInstanceIdentifier()
+ */
+ public String getInstanceIdentifier()
+ {
+ return getSerialNumber();
+ }
+
+ /**
+ * @see IAndroidEmulatorInstance#getProtocolHandle()
+ */
+ public ProtocolHandle getProtocolHandle()
+ {
+ return handle;
+ }
+
+ /**
+ * @see IAndroidEmulatorInstance#getEmulatorDefId()
+ */
+ public String getEmulatorDefId()
+ {
+ return getProperties().getProperty(IDevicePropertiesConstants.emulatorDefId);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.emulator.core.model.IAndroidEmulatorInstance#getCurrentLayout()
+ */
+ public String getCurrentLayout()
+ {
+ return currentLayout;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.emulator.core.model.IAndroidEmulatorInstance#setCurrentLayout(java.lang.String)
+ */
+ public void setCurrentLayout(String layoutName)
+ {
+ currentLayout = layoutName;
+ }
+
+ /**
+ * @see IAndroidEmulatorInstance#setHasCli(boolean)
+ */
+ public void setHasCli(boolean hasCli)
+ {
+ this.hasCli = hasCli;
+ }
+
+ /**
+ * @see IAndroidEmulatorInstance#setProtocolHandle(ProtocolHandle)
+ */
+ public void setProtocolHandle(ProtocolHandle handle)
+ {
+ this.handle = handle;
+ }
+
+ /**
+ * Resets all the previous runtime state to clean them for next execution
+ */
+ void resetRuntimeVariables()
+ {
+ handle = null;
+ hasCli = false;
+ currentLayout = null;
+ }
+
+ /**
+ * This version of getAdapter needs to assure that only an Android
+ * Device instance is compatible with itself
+ *
+ * @see IAdaptable#getAdapter(Class)
+ */
+ @SuppressWarnings("rawtypes")
+ @Override
+ public Object getAdapter(Class adapter)
+ {
+ if (adapter.isInstance(this))
+ {
+ return this;
+ }
+ else
+ {
+ return null;
+ }
+ }
+
+ /**
+ * Retrieves the default properties to be used upon instance creation
+ *
+ * @param defaultProperties property object to be filled
+ * @param vmSkin Android VM skin
+ */
+ public static void populateWithDefaultProperties(Properties defaultProperties)
+ {
+
+ AndroidEmuDefMgr emuDefMgr = AndroidEmuDefMgr.getInstance();
+
+ // When removing the emulator definition extension, remove this hardcoded set as
+ // well as the constant declaration from the Activator. If the Mot skin plugin is used,
+ // find another way to set this variable
+ String emuDefId = EmulatorPlugin.DEFAULT_EMULATOR_DEFINITION;
+
+ // Emulator Definition
+ defaultProperties.setProperty(IDevicePropertiesConstants.emulatorDefId, emuDefId);
+
+ // skin from Emulator Definition
+ defaultProperties.setProperty(IDevicePropertiesConstants.skinId,
+ emuDefMgr.getSkinId(emuDefId));
+
+ // command line arguments from Emulator Definition
+ defaultProperties.setProperty(IDevicePropertiesConstants.commandline,
+ emuDefMgr.getCommandLineArgumentsForEmuDefinition(emuDefId));
+
+ // default timeout
+ defaultProperties.setProperty(IDevicePropertiesConstants.timeout,
+ IDevicePropertiesConstants.defaultTimeoutValue);
+
+ //default useVnc
+ defaultProperties.setProperty(IDevicePropertiesConstants.useVnc,
+ NativeUIUtils.getDefaultUseVnc());
+
+ //default useProxy
+ defaultProperties.setProperty(IDevicePropertiesConstants.useProxy,
+ IDevicePropertiesConstants.defaultUseProxyValue);
+ }
+
+ /**
+ * Populate VM Target and VM skin
+ *
+ * @param instanceName
+ * @param instanceProperties
+ */
+ public static void populateWithVMInfo(String instanceName, Properties instanceProperties)
+ {
+ AvdInfo vmInfo = SdkUtils.getValidVm(instanceName);
+
+ if (vmInfo != null)
+ {
+ // VM target
+ instanceProperties.setProperty(IDevicePropertiesConstants.vmTarget, vmInfo.getTarget()
+ .getName());
+
+ // ABI Type
+ instanceProperties.setProperty(IDevicePropertiesConstants.abiType, vmInfo.getAbiType());
+
+ // VM skin
+ instanceProperties.setProperty(IDevicePropertiesConstants.vmSkin,
+ SdkUtils.getSkin(vmInfo));
+
+ // VM path
+ instanceProperties.setProperty(IDevicePropertiesConstants.vmPath,
+ vmInfo.getDataFolderPath());
+ String useSnapShot = vmInfo.getProperties().get("snapshot.present");
+ if (useSnapShot == null)
+ {
+ useSnapShot = "false";
+ }
+
+ instanceProperties.setProperty(IDevicePropertiesConstants.useSnapshots, useSnapShot);
+ if (instanceProperties.getProperty(IDevicePropertiesConstants.startFromSnapshot) == null)
+ {
+ instanceProperties.setProperty(IDevicePropertiesConstants.startFromSnapshot,
+ useSnapShot);
+ }
+ if (instanceProperties.getProperty(IDevicePropertiesConstants.saveSnapshot) == null)
+ {
+ instanceProperties
+ .setProperty(IDevicePropertiesConstants.saveSnapshot, useSnapShot);
+ }
+ }
+ }
+
+ public String getSkinId()
+ {
+ return getProperties().getProperty(IDevicePropertiesConstants.skinId);
+ }
+
+ @SuppressWarnings("restriction")
+ public File getSkinPath()
+ {
+ File skinFile = null;
+
+ AvdInfo avdInfo = SdkUtils.getValidVm(getName());
+ if (avdInfo != null)
+ {
+ String skinPath = avdInfo.getProperties().get("skin.path");
+ skinPath = SdkUtils.getCurrentSdk().getSdkLocation() + skinPath;
+ IAndroidTarget target = avdInfo.getTarget();
+ File candidateFile = new File(skinPath);
+ //If path specified on the skin does not exist, try to retrieve it from the target.
+ if (!candidateFile.exists())
+ {
+ candidateFile =
+ SdkUtils.getCurrentSdk().getAvdManager()
+ .getSkinPath(SdkUtils.getSkin(avdInfo), target);
+ }
+ if (!target.isPlatform())
+ {
+ if (!candidateFile.isDirectory())
+ {
+ IAndroidTarget baseTarget = target.getParent();
+ skinPath = getSkinFolderPath(baseTarget);
+ skinFile = new File(skinPath);
+ }
+ else
+ {
+ skinFile = candidateFile;
+ }
+ }
+ else
+ {
+ skinFile = candidateFile;
+ }
+ }
+ return skinFile;
+ }
+
+ private String getSkinFolderPath(IAndroidTarget target)
+ {
+ String vmSkin = getProperties().getProperty(IDevicePropertiesConstants.vmSkin);
+ return target.getLocation() + File.separator + "skins" + File.separator + vmSkin;
+ }
+
+ /**
+ * Get the timeout in milliseconds
+ *
+ * @see com.motorola.studio.android.emulator.logic.IAndroidLogicInstance#getTimeout()
+ */
+ public int getTimeout()
+ {
+ int timeout = 0;
+ String timeoutString = null;
+ try
+ {
+ info("Try to get Timeout property from " + this);
+ Properties instanceProps = getProperties();
+ timeoutString = instanceProps.getProperty(IDevicePropertiesConstants.timeout);
+ timeout = Integer.parseInt(timeoutString) * 1000; //convert to milis
+ }
+ catch (Exception e)
+ {
+ warn("Unnable to parse timeout string:" + timeoutString);
+ timeout = Integer.parseInt(IDevicePropertiesConstants.defaultTimeoutValue) * 1000;
+ }
+
+ return timeout;
+ }
+
+ public IAndroidTarget getAndroidTarget()
+ {
+ IAndroidTarget result = null;
+ AvdInfo avdInfo = SdkUtils.getValidVm(getName());
+ if (avdInfo != null)
+ {
+ result = avdInfo.getTarget();
+ }
+ return result;
+ }
+
+ public String getTarget()
+ {
+ String result = null;
+ IAndroidTarget target = getAndroidTarget();
+ if (target != null)
+ {
+ result = target.getName();
+ }
+ return result;
+ }
+
+ public int getAPILevel()
+ {
+ int result = -1;
+ IAndroidTarget target = getAndroidTarget();
+ if (target != null)
+ {
+ result = target.getVersion().getApiLevel();
+ }
+ return result;
+ }
+
+ public String getCommandLineArguments()
+ {
+ return getProperties().getProperty(IDevicePropertiesConstants.commandline,
+ NativeUIUtils.getDefaultCommandLine());
+ }
+
+ public AbstractStartAndroidEmulatorLogic getStartLogic()
+ {
+ String emuDefinition = getEmulatorDefId();
+ AndroidEmuDefMgr definitionManager = AndroidEmuDefMgr.getInstance();
+ return definitionManager.getStartLogic(emuDefinition);
+ }
+
+ public IInputLogic getInputLogic()
+ {
+ String emuDefinition = getEmulatorDefId();
+ AndroidEmuDefMgr definitionManager = AndroidEmuDefMgr.getInstance();
+ return definitionManager.getInputLogic(emuDefinition, this);
+ }
+
+ public boolean hasDevice()
+ {
+ return (DDMSFacade.getDeviceBySerialNumber(getSerialNumber()) != null);
+ }
+
+ public void changeOrientation(final String parameters)
+ {
+
+ new Thread(new Runnable()
+ {
+
+ public void run()
+ {
+ try
+ {
+ DDMSFacade.execRemoteApp(getSerialNumber(),
+ AndroidLogicUtils.ORIENTATION_BASE_COMMAND + parameters,
+ new NullProgressMonitor());
+ }
+ catch (IOException e)
+ {
+ error("Failed to send the command to change the emulator display orientation to portrait.");
+ }
+
+ }
+ }).start();
+
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.emulator.logic.IAndroidLogicInstance#getUserdata()
+ */
+ public File getUserdata()
+ {
+ return SdkUtils.getUserdataFile(getName());
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.emulator.logic.IAndroidLogicInstance#getSnapshotOriginalFilePath()
+ */
+ public File getSnapshotOriginalFilePath()
+ {
+ String snapshotFilePath;
+ File snapshotOriginalFile = null;
+ snapshotFilePath =
+ SdkUtils.getSdkToolsPath() + "lib" + File.separator + "emulator" + File.separator
+ + "snapshots.img";
+ snapshotOriginalFile = new File(snapshotFilePath);
+ if (!snapshotOriginalFile.exists())
+ {
+ snapshotOriginalFile = null;
+ }
+ return snapshotOriginalFile;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.emulator.logic.IAndroidLogicInstance#getStateData()
+ */
+ public List<File> getStateData()
+ {
+ return SdkUtils.getStateDataFiles(getName());
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.emulator.logic.IAndroidLogicInstance#isClean()
+ */
+ public boolean isClean()
+ {
+ boolean userdataExists = false;
+ File userdataFile = getUserdata();
+ if ((userdataFile != null) && (userdataFile.exists()))
+ {
+ userdataExists = true;
+ }
+
+ return !userdataExists;
+ }
+
+ @Override
+ public String toString()
+ {
+ // Do not use getInstanceIdentifier method here (it is used by several
+ // logs - high exposure - and leads to synchronized methods that may
+ // cause deadlocks).
+ return getName();
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.emulator.core.model.IAndroidEmulatorInstance#getProcess()
+ */
+ public Process getProcess()
+ {
+ return process;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.emulator.core.model.IAndroidEmulatorInstance#setProcess(java.lang.Process)
+ */
+ public void setProcess(Process process)
+ {
+ this.process = process;
+
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.emulator.core.model.IAndroidEmulatorInstance#setWindowHandle(int)
+ */
+ public long getWindowHandle()
+ {
+ return windowHandle;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.emulator.core.model.IAndroidEmulatorInstance#getWindowHandle()
+ */
+ public void setWindowHandle(long handle)
+ {
+ windowHandle = handle;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.adt.ISerialNumbered#getDeviceName()
+ */
+ public String getDeviceName()
+ {
+ return getName();
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.emulator.core.model.IAndroidEmulatorInstance#getFullName()
+ */
+ public String getFullName()
+ {
+ String suffix = getNameSuffix();
+ if (suffix != null)
+ {
+ return getName() + " (" + suffix + ")";
+ }
+ else
+ {
+ return getName();
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.ui.model.IWorkbenchAdapter#getChildren(java.lang.Object)
+ */
+ public Object[] getChildren(Object o)
+ {
+ return new Object[0];
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.ui.model.IWorkbenchAdapter#getImageDescriptor(java.lang.Object)
+ */
+ public ImageDescriptor getImageDescriptor(Object object)
+ {
+ return null;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.ui.model.IWorkbenchAdapter#getLabel(java.lang.Object)
+ */
+ public String getLabel(Object o)
+ {
+ return getName();
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.ui.model.IWorkbenchAdapter#getParent(java.lang.Object)
+ */
+ public Object getParent(Object o)
+ {
+ return null;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.adt.ISerialNumbered#getSerialNumber()
+ */
+ public String getSerialNumber()
+ {
+ return DDMSFacade.getSerialNumberByName(getName());
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.emulator.core.model.IAndroidEmulatorInstance#isAvailable()
+ */
+ public boolean isAvailable()
+ {
+ return !getStatus().equals(EmulatorPlugin.STATUS_NOT_AVAILABLE);
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.emulator.logic.IAndroidLogicInstance#getCommandLineArgumentsAsProperties()
+ */
+ public Properties getCommandLineArgumentsAsProperties()
+ {
+ return StartupOptionsMgt.parseCommandLine(getCommandLineArguments());
+
+ }
+
+ public Composite getComposite()
+ {
+ return composite;
+ }
+
+ public void setComposite(Composite composite)
+ {
+ this.composite = composite;
+ }
+
+} \ No newline at end of file
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/instance/options/IStartupOptionsConstants.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/instance/options/IStartupOptionsConstants.java
new file mode 100644
index 0000000..c82796b
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/instance/options/IStartupOptionsConstants.java
@@ -0,0 +1,155 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.emulator.device.instance.options;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * This interface contains constants used for the Startup Options Management
+ *
+ */
+@SuppressWarnings("serial")
+public interface IStartupOptionsConstants
+{
+
+ /*
+ * XML Path
+ */
+ public final String STARTUP_OPTIONS_XML_PATH = "resource/startup_options.xml";
+
+ /*
+ * XML tags
+ */
+ public final String ROOT_TAG = "startupOptions";
+
+ public final String GROUP_TAG = "group";
+
+ public final String GROUP_TAG_ID = "id";
+
+ public final String STARTUP_OPT_TAG = "startupOption";
+
+ public final String STARTUP_OPT_TAG_NAME = "name";
+
+ public final String STARTUP_OPT_TAG_FRIENDLY_NAME = "fName";
+
+ public final String STARTUP_OPT_TAG_TYPE = "type";
+
+ public final String STARTUP_OPT_TAG_TYPE_DETAILS = "typeDetails";
+
+ public final String STARTUP_OPT_TAG_DESCRIPTION = "description";
+
+ public final String PREDEFINED_VALUES_TAG = "values";
+
+ public final String PREDEFINED_VALUE_TAG = "value";
+
+ /*
+ * Startup option value type
+ */
+ public final int TYPE_NONE = 0;
+
+ public final int TYPE_TEXT = 1;
+
+ public final int TYPE_PATH = 2;
+
+ public final int TYPE_NUMBER = 3;
+
+ public final String TYPE_PATH_DIR = "dir";
+
+ public final Map<String, Integer> TYPE_MAP = new HashMap<String, Integer>()
+ {
+ {
+ put("none", TYPE_NONE);
+ put("text", TYPE_TEXT);
+ put("path", TYPE_PATH);
+ put("int", TYPE_NUMBER);
+ }
+
+ };
+
+ /*
+ * Disk images options
+ */
+ public final String DISKIMAGES_GROUP = "Disk Images";
+
+ public final String DISKIMAGES_CACHE = "-cache";
+
+ public final String DISKIMAGES_DATA = "-data";
+
+ public final String DISKIMAGES_IMAGE = "-image";
+
+ public final String DISKIMAGES_INITDATA = "-initdata";
+
+ public final String DISKIMAGES_KERNEL = "-kernel";
+
+ public final String DISKIMAGES_NOCACHE = "-nocache";
+
+ public final String DISKIMAGES_RAMDISK = "-ramdisk";
+
+ public final String DISKIMAGES_SDCARD = "-sdcard";
+
+ public final String DISKIMAGES_SYSTEM = "-system";
+
+ public final String DISKIMAGES_WIPEDATA = "-wipe-data";
+
+ /*
+ * Network options
+ */
+ public final String NETWORK_GROUP = "Network";
+
+ public final String NETWORK_DNS_SERVER = "-dns-server";
+
+ public final String NETWORK_HTTP_PROXY = "-http-proxy";
+
+ public final String NETWORK_NETDELAY = "-netdelay";
+
+ public final String NETWORK_NETFAST = "-netfast";
+
+ public final String NETWORK_NETSPEED = "-netspeed";
+
+ public final String NETWORK_PORT = "-port";
+
+ /*
+ * System options
+ */
+ public final String SYSTEM_GROUP = "System";
+
+ public final String SYSTEM_CPU_DELAY = "-cpu-delay";
+
+ public final String SYSTEM_GPS = "-gps";
+
+ public final String SYSTEM_NO_JNI = "-nojni";
+
+ /*
+ * UI options
+ */
+ public final String UI_GROUP = "UI";
+
+ public final String UI_DPI_DEVICE = "-dpi-device";
+
+ public final String SCALE = "-scale";
+
+ public final String NO_BOOT_AIM = "-no-boot-anim";
+
+ public final String NO_SKIN = "-no-skin";
+
+ /*
+ * Other options
+ */
+ public final String OTHERS_GROUP = "Others";
+
+ public final String OTHERS_OTHER = "other";
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/instance/options/StartupOption.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/instance/options/StartupOption.java
new file mode 100644
index 0000000..12a15c7
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/instance/options/StartupOption.java
@@ -0,0 +1,307 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.emulator.device.instance.options;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Combo;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.swt.widgets.Widget;
+
+/**
+ * Bean that represents an startup option
+ *
+ */
+public class StartupOption
+{
+
+ // Checked status (whether the startup options is being used or not)
+ private boolean checked;
+
+ // Widget that represents the checked status in the UI
+ private Widget checkedWidget;
+
+ // Startup option name
+ private String name;
+
+ // Startup option user-friendly name
+ private String userFriendlyName;
+
+ // Startup option description (user-friendly description)
+ private String description;
+
+ // Startup option type (which type of values that the startup option accepts)
+ private int type;
+
+ // Startup option type details (details of the values that the startup option accepts)
+ private String typeDetails;
+
+ // Startup option value (startup option configured value)
+ private String value;
+
+ // Widget that represents the startup option value in the UI
+ private Widget valueWidget;
+
+ // Startup option predefined values (list of values the startup option accepts)
+ private List<String> preDefinedValues;
+
+ /**
+ * Constructor
+ *
+ * @param name
+ * @param type
+ */
+ public StartupOption(String name, int type)
+ {
+ this.checked = false;
+ this.name = name;
+ this.type = type;
+ this.value = "";
+ this.preDefinedValues = new ArrayList<String>();
+ }
+
+ /**
+ * Get startup option name
+ *
+ * @return startup option name
+ */
+ public String getName()
+ {
+ return name;
+ }
+
+ /**
+ * Set startup option name
+ *
+ * @param name startup option name
+ */
+ public void setName(String name)
+ {
+ this.name = name;
+ }
+
+ /**
+ * Get startup option user-friendly name
+ *
+ * @return
+ */
+ public String getUserFriendlyName()
+ {
+ return userFriendlyName;
+ }
+
+ /**
+ * Set startup option user-friendly name
+ *
+ * @param userFriendlyName
+ */
+ public void setUserFriendlyName(String userFriendlyName)
+ {
+ this.userFriendlyName = userFriendlyName;
+ }
+
+ /**
+ * Get startup option type
+ *
+ * @return startup option type
+ */
+ public int getType()
+ {
+ return type;
+ }
+
+ /**
+ * Set startup option type
+ *
+ * @param type startup option type
+ */
+ public void setType(int type)
+ {
+ this.type = type;
+ }
+
+ /**
+ * Get startup option value
+ *
+ * @return startup option value
+ */
+ public String getValue()
+ {
+ return value;
+ }
+
+ /**
+ * Set startup option value
+ *
+ * @param value startup option value
+ */
+ public void setValue(String value)
+ {
+ this.value = value;
+ }
+
+ /**
+ * Get startup option pre-defined values
+ *
+ * @return startup option pre-defined values
+ */
+ public List<String> getPreDefinedValues()
+ {
+ return preDefinedValues;
+ }
+
+ /**
+ * Set startup option pre-defined values
+ *
+ * @param preDefinedValues startup option pre-defined values
+ */
+ public void setPreDefinedValues(List<String> preDefinedValues)
+ {
+ this.preDefinedValues = preDefinedValues;
+ }
+
+ /**
+ * Check if the startup option is being used
+ *
+ * @return true if the startup option is being used, false otherwise
+ */
+ public boolean isChecked()
+ {
+ return checked;
+ }
+
+ /**
+ * Set that the startup option is being used or not
+ *
+ * @param checked true if the startup option is being used, false otherwise
+ */
+ public void setChecked(boolean checked)
+ {
+ this.checked = checked;
+ }
+
+ /**
+ * Get startup option description
+ *
+ * @return startup option description
+ */
+ public String getDescription()
+ {
+ return description;
+ }
+
+ /**
+ * Set startup option description
+ *
+ * @param description startup option description
+ */
+ public void setDescription(String description)
+ {
+ this.description = description;
+ }
+
+ /**
+ * Get startup option type details
+ *
+ * @return startup option type details
+ */
+ public String getTypeDetails()
+ {
+ return typeDetails;
+ }
+
+ /**
+ * Get startup option type details
+ *
+ * @param typeDetails valuable information for validating if the value assigned is correct
+ */
+ public void setTypeDetails(String typeDetails)
+ {
+ this.typeDetails = typeDetails;
+ }
+
+ /**
+ * Get the widget that represents the checked status in the UI
+ *
+ * @return widget that represents the checked status in the UI
+ */
+ public Widget getCheckedWidget()
+ {
+ return checkedWidget;
+ }
+
+ /**
+ * Set the widget that represents the checked status in the UI
+ *
+ * @param checkedWidget widget that represents the checked status in the UI
+ */
+ public void setCheckedWidget(Widget checkedWidget)
+ {
+ this.checkedWidget = checkedWidget;
+ }
+
+ /**
+ * Get the widget that represents the startup option value in the UI
+ *
+ * @return widget that represents the startup option value in the UI
+ */
+ public Widget getValueWidget()
+ {
+ return valueWidget;
+ }
+
+ /**
+ * Set the widget that represents the startup option value in the UI
+ *
+ * @param valueWidget widget that represents the startup option value in the UI
+ */
+ public void setValueWidget(Widget valueWidget)
+ {
+ this.valueWidget = valueWidget;
+ }
+
+ /**
+ * Update the widgets that represent this startup options in the UI
+ * by changing their state to match the current values for checked and value
+ */
+ public void updateUI()
+ {
+ if (checkedWidget != null && !checkedWidget.isDisposed())
+ {
+ ((Button) this.checkedWidget).setSelection(this.checked);
+ }
+ if (valueWidget != null && !checkedWidget.isDisposed())
+ {
+ if (this.valueWidget instanceof Text)
+ {
+ ((Text) this.valueWidget).setText(this.value);
+ }
+ else if (this.valueWidget instanceof Combo)
+ {
+ if ((this.value == null) || (this.value.equals("")))
+ {
+ ((Combo) this.valueWidget).deselectAll();
+ }
+ else
+ {
+ ((Combo) this.valueWidget).select(getPreDefinedValues().indexOf(this.value));
+ }
+ }
+ }
+ }
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/instance/options/StartupOptionsGroup.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/instance/options/StartupOptionsGroup.java
new file mode 100644
index 0000000..1348318
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/instance/options/StartupOptionsGroup.java
@@ -0,0 +1,106 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.emulator.device.instance.options;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Bean that represents an startup options group
+ *
+ */
+public class StartupOptionsGroup
+{
+ // Group ID
+ private String id;
+
+ // Group Title (user-friendly title)
+ private String title;
+
+ // Startup options (list of the startup options in this group)
+ private List<StartupOption> startupOptions = new ArrayList<StartupOption>();
+
+ /**
+ * Constructor
+ *
+ * @param id
+ */
+ public StartupOptionsGroup(String id)
+ {
+ this.id = id;
+ }
+
+ /**
+ * Get startup option group ID
+ *
+ * @return startup option group ID
+ */
+ public String getId()
+ {
+ return id;
+ }
+
+ /**
+ * Set startup option group ID
+ *
+ * @param id startup option group ID
+ */
+ public void setId(String id)
+ {
+ this.id = id;
+ }
+
+ /**
+ * Get the startup options in this group
+ *
+ * @return startup options in this group
+ */
+ public List<StartupOption> getStartupOptions()
+ {
+ return startupOptions;
+ }
+
+ /**
+ * Set the startup options in this group
+ *
+ * @param startupOptions startup options in this group
+ */
+ public void setStartupOptions(List<StartupOption> startupOptions)
+ {
+ this.startupOptions = startupOptions;
+ }
+
+ /**
+ * Get startup option group title
+ *
+ * @return startup option group title
+ */
+ public String getTitle()
+ {
+ return title;
+ }
+
+ /**
+ * Set startup option group title
+ *
+ * @param title startup option group title
+ */
+ public void setTitle(String title)
+ {
+ this.title = title;
+ }
+
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/instance/options/StartupOptionsMgt.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/instance/options/StartupOptionsMgt.java
new file mode 100644
index 0000000..bd5710d
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/instance/options/StartupOptionsMgt.java
@@ -0,0 +1,785 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.emulator.device.instance.options;
+
+import java.io.File;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+
+import org.eclipse.core.runtime.Path;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.osgi.util.NLS;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NodeList;
+
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.emulator.EmulatorPlugin;
+import com.motorola.studio.android.emulator.i18n.EmulatorNLS;
+
+/**
+ * This class provides methods to manage startup options
+ *
+ */
+public class StartupOptionsMgt implements IStartupOptionsConstants
+{
+
+ /**
+ * List of all startup options groups (the startup options themselves will
+ * be accessed through the use of a method in the group object that returns
+ * the startup options in that group)
+ */
+ private static List<StartupOptionsGroup> startupOptionsGroupsList = null;
+
+ /**
+ * List of all startup options, indexed by their names, for fast access
+ */
+ private static Map<String, StartupOption> startupOptionsMap =
+ new HashMap<String, StartupOption>();
+
+ /*
+ * Load the startup options / groups list
+ */
+ static
+ {
+ load();
+ }
+
+ /**
+ * Get the startup options groups list
+ *
+ * @return startup options groups list
+ */
+ public static List<StartupOptionsGroup> getStartupOptionsGroupsList()
+ {
+ return startupOptionsGroupsList;
+ }
+
+ /**
+ * Read all groups and startup options available for editing from a XML
+ * and stores the information in the correspondent beans
+ */
+ public static void load()
+ {
+
+ try
+ {
+ // Clear startup options groups list
+ startupOptionsGroupsList = new ArrayList<StartupOptionsGroup>();
+
+ // Define XML path
+ InputStream xmlStream =
+ EmulatorPlugin.getDefault().getBundle().getEntry(STARTUP_OPTIONS_XML_PATH)
+ .openStream();
+
+ // Load XML
+ DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+ DocumentBuilder builder = factory.newDocumentBuilder();
+ Document document = builder.parse(xmlStream);
+
+ /*
+ * Iterate through Startup Groups
+ */
+ Element rootNode = document.getDocumentElement();
+ NodeList startupOptionsGroups = rootNode.getElementsByTagName(GROUP_TAG);
+ for (int i = 0; i < startupOptionsGroups.getLength(); i++)
+ {
+ /*
+ * Create group
+ */
+ Element group = (Element) startupOptionsGroups.item(i);
+
+ String strKey = group.getAttributeNode(GROUP_TAG_ID).getNodeValue();
+ strKey =
+ Platform.getResourceString(EmulatorPlugin.getDefault().getBundle(), strKey);
+
+ StartupOptionsGroup startupOptionsGroup = new StartupOptionsGroup(strKey);
+ startupOptionsGroup.setTitle(startupOptionsGroup.getId());
+
+ /*
+ * Iterate through Startup Options in this group
+ */
+ NodeList startupOptions = group.getElementsByTagName(STARTUP_OPT_TAG);
+ startupOptionsGroup.setStartupOptions(new ArrayList<StartupOption>()); // clear startup options
+ for (int j = 0; j < startupOptions.getLength(); j++)
+ {
+ /*
+ * Create startup option
+ */
+ Element option = (Element) startupOptions.item(j);
+ StartupOption startupOption =
+ new StartupOption(option.getAttributeNode(STARTUP_OPT_TAG_NAME)
+ .getNodeValue(), getStartupOptionType(option.getAttributeNode(
+ STARTUP_OPT_TAG_TYPE).getNodeValue())); // name and type
+ strKey = option.getAttributeNode(STARTUP_OPT_TAG_FRIENDLY_NAME).getNodeValue();
+
+ strKey =
+ Platform.getResourceString(EmulatorPlugin.getDefault().getBundle(),
+ strKey);
+
+ startupOption.setUserFriendlyName(strKey); // friendly name
+
+ strKey =
+ option.getElementsByTagName(STARTUP_OPT_TAG_DESCRIPTION).item(0)
+ .getTextContent();
+
+ strKey =
+ Platform.getResourceString(EmulatorPlugin.getDefault().getBundle(),
+ strKey);
+
+ startupOption.setDescription(strKey); // description
+
+ if (option.getAttributeNode(STARTUP_OPT_TAG_TYPE_DETAILS) != null)
+ {
+ startupOption.setTypeDetails(option.getAttributeNode(
+ STARTUP_OPT_TAG_TYPE_DETAILS).getNodeValue()); // type details
+ }
+ // Iterate through startup option pre-defined values, if any
+ NodeList preDefinedValuesContainer =
+ option.getElementsByTagName(PREDEFINED_VALUES_TAG);
+ startupOption.setPreDefinedValues(new ArrayList<String>()); // clear pre-defined values
+ if (preDefinedValuesContainer.getLength() > 0)
+ {
+ NodeList preDefinedValues =
+ ((Element) preDefinedValuesContainer.item(0))
+ .getElementsByTagName(PREDEFINED_VALUE_TAG);
+ for (int k = 0; k < preDefinedValues.getLength(); k++)
+ {
+ // Add pre-defined values to the option
+ Element preDefinedValue = (Element) preDefinedValues.item(k);
+ startupOption.getPreDefinedValues().add(
+ preDefinedValue.getTextContent());
+ }
+ }
+
+ /*
+ * Add startup options to the group
+ */
+ startupOptionsGroup.getStartupOptions().add(startupOption);
+
+ startupOptionsMap.put(startupOption.getName(), startupOption);
+
+ }
+
+ /*
+ * Add groups to the groups list
+ */
+ startupOptionsGroupsList.add(startupOptionsGroup);
+ }
+
+ }
+ catch (Exception e)
+ {
+ StudioLogger.error("Failed to load startup options");
+ }
+
+ }
+
+ /**
+ * Validate the values assigned for the startup options marked as checked (the ones that are
+ * being used), according to its type and type details.
+ *
+ * @return Status object with the result of the validation
+ */
+ public static Status validate()
+ {
+ Status status = (Status) Status.OK_STATUS;
+ String msg = null;
+
+ /*
+ * Iterate through Startup Groups
+ */
+ for (StartupOptionsGroup group : getStartupOptionsGroupsList())
+ {
+ /*
+ * Iterate through Startup Options in this group
+ */
+ for (StartupOption startupOption : group.getStartupOptions())
+ {
+ /*
+ * Check if the Startup Option is checked
+ */
+ if (startupOption.isChecked() && (status.isOK()))
+ {
+
+ String name = startupOption.getName(); // startup option name
+ String ufname = startupOption.getUserFriendlyName(); // user-friendly startup option name
+ String value = startupOption.getValue(); // startup option value
+ String typeDetails = startupOption.getTypeDetails(); // startup option type detail
+
+ /*
+ * General validation: no quotes in values
+ */
+ if ((!startupOption.getName().equals(OTHERS_OTHER))
+ && (value.indexOf("\"") >= 0))
+ {
+ msg =
+ NLS.bind(
+ EmulatorNLS.ERR_PropertiesMainComposite_StartupOpt_NoQuotes,
+ ufname);
+ }
+ else
+ {
+
+ /*
+ * Call the appropriate validation method
+ */
+ switch (startupOption.getType())
+ {
+ case TYPE_TEXT:
+ msg = validateTextField(name, ufname, value, typeDetails);
+ break;
+
+ case TYPE_NUMBER:
+ msg = validadeNumberField(name, ufname, value, typeDetails);
+ break;
+
+ case TYPE_PATH:
+ msg = validadePathField(name, ufname, value, typeDetails);
+ break;
+ }
+ }
+
+ /*
+ * If some validation has failed, return with an error message
+ */
+ if (msg != null)
+ {
+ status = new Status(Status.ERROR, EmulatorPlugin.PLUGIN_ID, msg);
+ break;
+ }
+
+ }
+ }
+ }
+
+ return status;
+
+ }
+
+ /**
+ * Validate the startup option value for an startup option of "text" type
+ *
+ * @param name the startup option name
+ * @param ufname the user-friendly startup option name
+ * @param value the current assigned value for the startup option
+ * @param typeDetails any special requirements that the assigned value must be match
+ * @return null if the value assigned for the startup option is a valid one or an error message otherwise
+ */
+ private static String validateTextField(String name, String ufName, String value,
+ String typeDetails)
+ {
+ String msg = null;
+
+ // Check if the value is blank
+ if ((value == null) || (value.equals("")))
+ {
+ msg = NLS.bind(EmulatorNLS.ERR_PropertiesMainComposite_StartupOpt_TextBlank, ufName);
+ }
+
+ return msg;
+
+ }
+
+ /**
+ * Validate the startup option value for an startup option of "number" type
+ *
+ * @param name the startup option name
+ * @param ufname the user-friendly startup option name
+ * @param value the current assigned value for the startup option
+ * @param typeDetails any special requirements that the assigned value must be match
+ * @return null if the value assigned for the startup option is a valid one or an error message otherwise
+ */
+ private static String validadeNumberField(String name, String ufName, String value,
+ String typeDetails)
+ {
+ String msg = null;
+
+ // Check if the value is blank
+ if ((value == null) || (value.equals("")))
+ {
+ msg =
+ NLS.bind(EmulatorNLS.ERR_PropertiesMainComposite_StartupOpt_NumberRequired,
+ ufName);
+ }
+ else
+ {
+
+ try
+ {
+ /*
+ * Check if it's an Integer.
+ * If it's not, an exception will be thrown
+ */
+ int intValue = Integer.parseInt(value);
+
+ /*
+ * Check if it's positive
+ */
+ if (intValue < 0)
+ {
+ msg =
+ NLS.bind(
+ EmulatorNLS.ERR_PropertiesMainComposite_StartupOpt_NumberMustBePositiveInteger,
+ ufName);
+ }
+ else
+ {
+
+ /*
+ * Check if the value is in the correct range
+ */
+ if (typeDetails != null)
+ {
+ String[] valueRange = typeDetails.split(";");
+ if ((intValue < Integer.parseInt(valueRange[0]))
+ || (intValue > Integer.parseInt(valueRange[1])))
+ {
+ // the value is not in the correct range
+ msg =
+ NLS.bind(
+ EmulatorNLS.ERR_PropertiesMainComposite_StartupOpt_NumberIntRange,
+ new String[]
+ {
+ ufName, valueRange[0], valueRange[1]
+ });
+ }
+ }
+ }
+
+ }
+ catch (NumberFormatException ex)
+ {
+ // it's not a number
+ msg =
+ NLS.bind(
+ EmulatorNLS.ERR_PropertiesMainComposite_StartupOpt_NumberMustBeInteger,
+ ufName);
+ }
+ }
+
+ return msg;
+
+ }
+
+ /**
+ * Validate the startup option value for an startup option of "path" type
+ *
+ * @param name the startup option name
+ * @param ufname the user-friendly startup option name
+ * @param value the current assigned value for the startup option
+ * @param typeDetails any special requirements that the assigned value must be match
+ * @return null if the value assigned for the startup option is a valid one or an error message otherwise
+ */
+ private static String validadePathField(String name, String ufName, String value,
+ String typeDetails)
+ {
+ String msg = null;
+
+ if ((value == null) || (value.equals("")))
+ {
+ msg = NLS.bind(EmulatorNLS.ERR_PropertiesMainComposite_StartupOpt_PathRequired, ufName);
+ }
+ else
+ {
+
+ File file = new File(value);
+
+ /*
+ * Validate folder
+ */
+ if (typeDetails.equals(TYPE_PATH_DIR))
+ {
+ /*
+ * Check if the path exists
+ */
+ if (!file.exists())
+ {
+ // the folder doesn't exist
+ msg =
+ NLS.bind(
+ EmulatorNLS.ERR_PropertiesMainComposite_StartupOpt_PathDirNotExist,
+ ufName);
+ }
+ else
+ {
+ if (file.isFile())
+ {
+ // it's not a folder
+ msg =
+ NLS.bind(
+ EmulatorNLS.ERR_PropertiesMainComposite_StartupOpt_PathMustBeDir,
+ ufName);
+ }
+ }
+ }
+ /*
+ * Validate file
+ */
+ else
+ {
+ if (!file.exists())
+ {
+ // the file doesn't exist
+ msg =
+ NLS.bind(
+ EmulatorNLS.ERR_PropertiesMainComposite_StartupOpt_PathFileNotExist,
+ ufName);
+ }
+ else
+ {
+ // it's not a file
+ if (file.isDirectory())
+ {
+ msg =
+ NLS.bind(
+ EmulatorNLS.ERR_PropertiesMainComposite_StartupOpt_PathMustBeFile,
+ ufName);
+ }
+ // it doesn't have the correct extension
+ else
+ {
+ if (!typeDetails.equals("." + (new Path(value)).getFileExtension()))
+ {
+ msg =
+ NLS.bind(
+ EmulatorNLS.ERR_PropertiesMainComposite_StartupOpt_PathIncorrectFileType,
+ new String[]
+ {
+ ufName, typeDetails
+ });
+ }
+ }
+ }
+ }
+ }
+ return msg;
+
+ }
+
+ /**
+ * Generates the list of parameters that shall to be sent to the Emulator
+ *
+ * @return the list of parameters that shall to be sent to the Emulator
+ */
+ public static String getParamList()
+ {
+ String paramList = "";
+
+ /*
+ * Iterate through Startup Groups
+ */
+ for (StartupOptionsGroup group : getStartupOptionsGroupsList())
+ {
+ /*
+ * Iterate through Startup Options in this group
+ */
+ int startupOptionType;
+ for (StartupOption startupOption : group.getStartupOptions())
+ {
+ startupOptionType = startupOption.getType();
+ if (startupOption.isChecked()) // check if the startup option is being used
+ {
+ if (startupOptionType == TYPE_NONE)
+ {
+ paramList += ((paramList.equals("")) ? "" : " ") + startupOption.getName();
+ }
+ else
+ {
+ if ((startupOption.getName().equals(OTHERS_OTHER)))
+ {
+
+ paramList +=
+ ((paramList.equals("")) ? "" : " ") + startupOption.getValue();
+
+ }
+ else
+ {
+ String value = startupOption.getValue();
+
+ if (Platform.getOS().equals(Platform.OS_WIN32))
+ {
+ if (value.contains(" "))
+ {
+ value = "\"" + value + "\"";
+ }
+ }
+ else
+ {
+ if (value.contains("\\"))
+ {
+ value = value.replace("\\", "\\\\");
+ }
+
+ if (value.contains(" "))
+ {
+ value = value.replace(" ", "\\ ");
+ }
+ }
+
+ paramList +=
+ ((paramList.equals("")) ? "" : " ") + startupOption.getName()
+ + (value.trim().length() > 0 ? " " + value : "");
+ }
+ }
+ }
+ }
+ }
+
+ return paramList;
+
+ }
+
+ /**
+ * Load values from a Properties object
+ *
+ * @param properties properties object containing the values that must be loaded into de model
+ */
+ private static void loadValues(Properties properties)
+ {
+ /*
+ * Iterate through Startup Groups
+ */
+ for (StartupOptionsGroup group : getStartupOptionsGroupsList())
+ {
+ /*
+ * Iterate through Startup Options in this group
+ */
+ String soValue = "";
+ for (StartupOption startupOption : group.getStartupOptions())
+ {
+ soValue = properties.getProperty(startupOption.getName());
+ if (soValue != null)
+ {
+ startupOption.setChecked(true);
+ startupOption.setValue(soValue);
+ }
+ else
+ {
+ startupOption.setChecked(false);
+ startupOption.setValue("");
+ }
+ startupOption.updateUI();
+ }
+ }
+
+ }
+
+ /**
+ * Create a properties object with the information contained in a command line
+ *
+ * @param commandLine the command line used to start the emulator
+ * @return properties object with the information contained in a command line
+ */
+ public static Properties parseCommandLine(String commandLine)
+ {
+ Properties properties = new Properties();
+
+ if (!commandLine.equals(""))
+ {
+
+ /*
+ * Iterate through Startup Groups
+ */
+ for (StartupOptionsGroup group : getStartupOptionsGroupsList())
+ {
+ /*
+ * Iterate through Startup Options in this group
+ */
+ String soName, soValue = "";
+ int soType, shift = 0;
+ for (StartupOption startupOption : group.getStartupOptions())
+ {
+ soName = startupOption.getName();
+ soType = startupOption.getType();
+ if (commandLine.startsWith(soName))
+ {
+ if (soType == TYPE_NONE)
+ {
+ soValue = new Boolean(true).toString();
+ shift = soName.length() + 1;
+ }
+ else
+ {
+ commandLine =
+ commandLine
+ .substring(soName.length() + 1, commandLine.length());
+ //int endValueIndex = commandLine.indexOf("\"");
+ ParameterBean param = getNextParameterValue(commandLine);
+ soValue = param.getValue();
+ shift = param.getLastPosition() + 1;
+ }
+
+ properties.put(startupOption.getName(), soValue);
+
+ if (shift < commandLine.length() - 1)
+ {
+ commandLine = commandLine.substring(shift, commandLine.length());
+ }
+ else
+ {
+ commandLine = "";
+ }
+ }
+ }
+ }
+
+ if (!commandLine.equals(""))
+ {
+ properties.put(OTHERS_OTHER, commandLine);
+ }
+ }
+
+ return properties;
+
+ }
+
+ /**
+ * Load values from a command line
+ *
+ * @param commandLine the command line used to start the emulator
+ */
+ public static void loadFromCommandLine(String commandLine)
+ {
+ loadValues(parseCommandLine(commandLine));
+ }
+
+ /**
+ * Convert the type of the startup option from a string
+ * to a number
+ *
+ * @param type string that represents the type
+ * @return number that represents the type
+ */
+ private static int getStartupOptionType(String type)
+ {
+ return (TYPE_MAP.get(type)).intValue();
+ }
+
+ /**
+ * Parses the next parameter value on a command line
+ *
+ * @param commandLine the command line
+ *
+ * @return a bean containing the parameter value and the last position looked at
+ * the command line
+ */
+ private static ParameterBean getNextParameterValue(String commandLine)
+ {
+ boolean isWin32 = Platform.getOS().equals(Platform.OS_WIN32);
+ boolean escaped = false;
+ boolean quoted = false;
+
+ char c;
+ String value = "";
+ int i;
+
+ for (i = 0; i < commandLine.length(); i++)
+ {
+ c = commandLine.charAt(i);
+
+ if (escaped)
+ {
+ value += c;
+ escaped = false;
+ }
+ else if (c == '\\' && !isWin32)
+ {
+ escaped = true;
+ }
+ else if (c == '"' && isWin32)
+ {
+ if (value.length() == 0)
+ {
+ quoted = true;
+ }
+ else if (quoted)
+ {
+ break;
+ }
+ else
+ {
+ value += c;
+ }
+ }
+ else if ((c == ' ') && (!quoted))
+ {
+ break;
+ }
+ else
+ {
+ value += c;
+ }
+ }
+
+ return new ParameterBean(value, ((quoted) ? i + 1 : i));
+ }
+
+ /**
+ * Bean used to identify a parameter value when parsing the
+ * startup options
+ *
+ */
+ private static class ParameterBean
+ {
+ private final String value;
+
+ private final int lastPosition;
+
+ /**
+ * Constructor
+ *
+ * @param value The parameter value
+ * @param lastPosition The last position looked at the command line before stopping
+ * the parse operation
+ */
+ public ParameterBean(String value, int lastPosition)
+ {
+ this.value = value;
+ this.lastPosition = lastPosition;
+ }
+
+ /**
+ * Retrieves the parameter value
+ *
+ * @return the parameter value
+ */
+ public String getValue()
+ {
+ return value;
+ }
+
+ /**
+ * Retrieves the last position looked at the command line before stopping
+ * the parse operation
+ *
+ * @return the last position looked at the command line before stopping
+ * the parse operation
+ */
+ public int getLastPosition()
+ {
+ return lastPosition;
+ }
+ }
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/refresh/InstancesListRefresh.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/refresh/InstancesListRefresh.java
new file mode 100644
index 0000000..23295e9
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/refresh/InstancesListRefresh.java
@@ -0,0 +1,184 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.emulator.device.refresh;
+
+import static com.motorola.studio.android.common.log.StudioLogger.error;
+import static com.motorola.studio.android.common.log.StudioLogger.info;
+
+import java.util.Collection;
+import java.util.Properties;
+
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.sequoyah.device.common.utilities.exception.SequoyahException;
+import org.eclipse.sequoyah.device.framework.DevicePlugin;
+import org.eclipse.sequoyah.device.framework.factory.DeviceTypeRegistry;
+import org.eclipse.sequoyah.device.framework.manager.InstanceManager;
+import org.eclipse.sequoyah.device.framework.model.IDeviceType;
+
+import com.motorola.studio.android.adt.SdkUtils;
+import com.motorola.studio.android.emulator.EmulatorPlugin;
+import com.motorola.studio.android.emulator.core.devfrm.DeviceFrameworkManager;
+import com.motorola.studio.android.emulator.core.model.IAndroidEmulatorInstance;
+import com.motorola.studio.android.emulator.device.AndroidDeviceUtils;
+import com.motorola.studio.android.emulator.device.instance.AndroidDevInstBuilder;
+import com.motorola.studio.android.emulator.device.instance.AndroidDeviceInstance;
+
+/**
+ * This class is responsible for refreshing the TML Instances List It checks if
+ * the user has created more Android VMs by himself
+ *
+ */
+public class InstancesListRefresh
+{
+
+ /**
+ * If the number of Android VMs is different from the number of TML
+ * Instances, the TML Instances list is updated
+ */
+ public static synchronized void refresh()
+ {
+
+ SdkUtils.reloadAvds();
+
+ DeviceFrameworkManager devFramework = DeviceFrameworkManager.getInstance();
+
+ if (SdkUtils.getCurrentSdk() != null)
+ {
+ final Collection<String> vmInstances = SdkUtils.getAllVmNames();
+ final Collection<String> validVmInstances = SdkUtils.getAllValidVmNames();
+ final Collection<String> emulatorInstances = devFramework.getAllInstanceNames();
+
+ createAndUpdateEmulatorInstances(vmInstances, validVmInstances, emulatorInstances);
+ }
+ }
+
+ /**
+ * Creates Emulator instances to represent every VM available in the system.
+ * @param validVmInstances
+ **/
+ public static void createAndUpdateEmulatorInstances(Collection<String> vmInstances,
+ Collection<String> validVmInstances,
+ Collection<String> emulatorInstances)
+ {
+
+ IDeviceType device =
+ DeviceTypeRegistry.getInstance().getDeviceTypeById(EmulatorPlugin.DEVICE_ID);
+
+ for (String instanceName : vmInstances)
+ {
+ /*
+ * In case the is no TmL instances for a given VM, create the TmL
+ * Instance
+ */
+ if (!emulatorInstances.contains(instanceName))
+ {
+
+ Properties instanceProperties = new Properties();
+
+ AndroidDeviceInstance.populateWithVMInfo(instanceName, instanceProperties);
+
+ AndroidDeviceInstance.populateWithDefaultProperties(instanceProperties);
+
+ AndroidDevInstBuilder projectBuilder =
+ new AndroidDevInstBuilder(instanceName, instanceProperties);
+
+ try
+ {
+ InstanceManager
+ .createProject(device, projectBuilder, new NullProgressMonitor());
+ }
+ catch (SequoyahException e)
+ {
+ error("There was an error while creating an emulator instance: " + instanceName
+ + ". Message: " + e.getMessage());
+ }
+
+ refreshStatus(validVmInstances, instanceName);
+
+ info("Added instance " + instanceName + " using default emulator definitions ");
+ }
+
+ }
+
+ for (String emulatorInstance : emulatorInstances)
+ {
+ refreshStatus(validVmInstances, emulatorInstance);
+ }
+
+ }
+
+ public static void refreshStatus(Collection<String> vmInstances, String instanceName)
+ {
+ /*
+ * Refresh status
+ */
+ IAndroidEmulatorInstance instance =
+ DeviceFrameworkManager.getInstance().getInstanceByName(instanceName);
+
+ AndroidDeviceInstance androidDeviceInstance = (AndroidDeviceInstance) instance;
+
+ AndroidDeviceInstance.populateWithVMInfo(androidDeviceInstance.getName(),
+ androidDeviceInstance.getProperties());
+
+ String currentStatus = androidDeviceInstance.getStatus();
+
+ if (androidDeviceInstance.hasDevice())
+ {
+ if ((androidDeviceInstance.getStatus().equals(EmulatorPlugin.STATUS_NOT_AVAILABLE))
+ || (androidDeviceInstance.getStatus().equals(DevicePlugin.SEQUOYAH_STATUS_OFF)))
+ {
+ if (!EmulatorPlugin.STATUS_OFFLINE_NO_DATA.equals(currentStatus))
+ {
+ androidDeviceInstance.setNameSuffix(null);
+ androidDeviceInstance.setStatus(EmulatorPlugin.STATUS_OFFLINE_NO_DATA);
+ }
+ }
+ AndroidDeviceUtils.fireDummyStartTransition(androidDeviceInstance,
+ androidDeviceInstance.getSerialNumber());
+ }
+ else
+ {
+
+ if (vmInstances.contains(androidDeviceInstance.getName()))
+ {
+ if (androidDeviceInstance.isClean())
+ {
+ if (!EmulatorPlugin.STATUS_OFFLINE_NO_DATA.equals(currentStatus))
+ {
+ androidDeviceInstance.setNameSuffix(null);
+ androidDeviceInstance.setStatus(EmulatorPlugin.STATUS_OFFLINE_NO_DATA);
+ }
+ }
+ else
+ {
+ if (!EmulatorPlugin.STATUS_OFFLINE.equals(currentStatus))
+ {
+ androidDeviceInstance.setNameSuffix(null);
+ androidDeviceInstance.setStatus(EmulatorPlugin.STATUS_OFFLINE);
+ }
+ }
+ }
+ else
+ {
+ if (!EmulatorPlugin.STATUS_NOT_AVAILABLE.equals(currentStatus))
+ {
+ androidDeviceInstance.setNameSuffix(null);
+ androidDeviceInstance.setStatus(EmulatorPlugin.STATUS_NOT_AVAILABLE);
+ }
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/refresh/InstancesListRefreshHandler.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/refresh/InstancesListRefreshHandler.java
new file mode 100644
index 0000000..3d68c4b
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/refresh/InstancesListRefreshHandler.java
@@ -0,0 +1,39 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.emulator.device.refresh;
+
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+
+/**
+ * Class responsible for handling refresh actions
+ *
+ */
+public class InstancesListRefreshHandler extends AbstractHandler
+{
+
+ /**
+ * Call the method responsible for refreshing the TML Instances List
+ *
+ * @see org.eclipse.core.commands.AbstractHandler#execute(org.eclipse.core.commands.ExecutionEvent)
+ */
+ public Object execute(ExecutionEvent arg0) throws ExecutionException
+ {
+ InstancesListRefresh.refresh();
+ return null;
+ }
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/sync/DeviceViewsSync.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/sync/DeviceViewsSync.java
new file mode 100644
index 0000000..ec7c49e
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/sync/DeviceViewsSync.java
@@ -0,0 +1,326 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.emulator.device.sync;
+
+import java.lang.reflect.Method;
+import java.util.List;
+
+import org.eclipse.sequoyah.device.framework.factory.InstanceRegistry;
+import org.eclipse.sequoyah.device.framework.model.IInstance;
+import org.eclipse.sequoyah.device.framework.ui.view.InstanceMgtView;
+import org.eclipse.sequoyah.device.framework.ui.view.model.InstanceSelectionChangeEvent;
+import org.eclipse.sequoyah.device.framework.ui.view.model.InstanceSelectionChangeListener;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Listener;
+
+import com.android.ddmlib.Client;
+import com.android.ddmlib.IDevice;
+import com.android.ide.eclipse.ddms.DdmsPlugin;
+import com.motorola.studio.android.adt.DDMSFacade;
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.emulator.EmulatorPlugin;
+import com.motorola.studio.android.emulator.core.devfrm.DeviceFrameworkManager;
+import com.motorola.studio.android.emulator.core.model.IAndroidEmulatorInstance;
+import com.motorola.studio.android.emulator.ui.view.AbstractAndroidView;
+
+public class DeviceViewsSync
+{
+
+ /**
+ * DeviceViewsSync unique instance
+ */
+ private static DeviceViewsSync instance = null;
+
+ /**
+ * Views
+ */
+ public static final int EMULATOR_VIEW = 0; // Emulator View
+
+ public static final int DEVICE_VIEW = 1; // Device Management View
+
+ public static final int DDMS_VIEW = 2; // DDMS Device View
+
+ /**
+ * Methods used to update the Views
+ */
+ private Method[] syncMethods = null;
+
+ /**
+ * During the synchronization, it stores the instance
+ * that shall be set to avoid loops
+ */
+ private static String syncInstance = null;
+
+ /**
+ * Singleton
+ *
+ * @return DeviceViewsSync
+ */
+ public static DeviceViewsSync getInstance()
+ {
+ if (instance == null)
+ {
+ instance = new DeviceViewsSync();
+ }
+ return instance;
+ }
+
+ /*
+ * Constructor
+ *
+ * Define the synchronization methods
+ * Define the methods that retrieve the current selection in a View
+ */
+ @SuppressWarnings("rawtypes")
+ private DeviceViewsSync()
+ {
+
+ try
+ {
+
+ /*
+ * Register methods that update each view
+ */
+ Class parameterTypes[] = new Class[1];
+ parameterTypes[0] = String.class;
+
+ syncMethods = new Method[3];
+
+ syncMethods[EMULATOR_VIEW] =
+ this.getClass().getDeclaredMethod("syncEmulatorView", parameterTypes);
+ syncMethods[DEVICE_VIEW] =
+ this.getClass().getDeclaredMethod("syncDeviceView", parameterTypes);
+ syncMethods[DDMS_VIEW] =
+ this.getClass().getDeclaredMethod("syncDDMSView", parameterTypes);
+ }
+ catch (Exception e)
+ {
+ StudioLogger.error("Could not add syncronization method: " + e.getMessage());
+ }
+
+ }
+
+ /**
+ * Add listeners to events that must initiate the synchronization procedures
+ *
+ * #1) Emulator View
+ *
+ * #2) Device Management View
+ *
+ * #3) DDMS Device View
+ */
+ public void initialize()
+ {
+
+ /*
+ * Synchronization #1
+ * Add listener to Emulator View tab switch event
+ */
+ AbstractAndroidView.addTabSwitchListener(new Listener()
+ {
+ @Override
+ public void handleEvent(Event event)
+ {
+
+ IAndroidEmulatorInstance activeInstance = AbstractAndroidView.getActiveInstance();
+ if (activeInstance != null)
+ {
+ String selectedInstanceName = activeInstance.getName();
+ if ((selectedInstanceName != null)
+ && (!selectedInstanceName.equals(syncInstance)))
+ {
+ sync(EMULATOR_VIEW, selectedInstanceName);
+ }
+ }
+
+ }
+ });
+
+ /*
+ * Synchronization #2
+ */
+ InstanceMgtView.addInstanceSelectionChangeListener(new InstanceSelectionChangeListener()
+ {
+ @Override
+ public void instanceSelectionChanged(InstanceSelectionChangeEvent event)
+ {
+
+ IInstance instance = event.getInstance();
+ if ((instance != null)
+ && (EmulatorPlugin.STATUS_ONLINE_ID.equals(instance.getStatus())))
+ {
+ String selectedInstanceName = instance.getName();
+ if ((selectedInstanceName != null)
+ && (!selectedInstanceName.equals(syncInstance)))
+ {
+ sync(DEVICE_VIEW, selectedInstanceName);
+ }
+ }
+
+ }
+ });
+
+ /*
+ * Synchronization #3
+ */
+ DdmsPlugin.getDefault().addSelectionListener(new DdmsPlugin.ISelectionListener()
+ {
+ @Override
+ public void selectionChanged(Client client)
+ {
+ // none
+ }
+
+ @Override
+ public void selectionChanged(IDevice device)
+ {
+
+ if (device != null)
+ {
+ String selectedInstanceName = device.getAvdName();
+ if ((selectedInstanceName != null)
+ && (!selectedInstanceName.equals(syncInstance)))
+ {
+ sync(DDMS_VIEW, selectedInstanceName);
+ }
+ }
+
+ }
+ });
+ }
+
+ /*
+ * Run the synchronization procedures
+ *
+ * @param fireSyncView the View that has been changed and requires others to synchronize
+ * @param instanceName the Device Instance name
+ */
+ private void sync(Integer fireSyncView, String instanceName)
+ {
+ syncInstance = instanceName;
+
+ Object arglist[] = new Object[1];
+ arglist[0] = instanceName;
+
+ for (int i = 0; i < syncMethods.length; i++)
+ {
+ if (i != fireSyncView)
+ {
+ try
+ {
+ syncMethods[i].invoke(this, arglist);
+ }
+ catch (Exception e)
+ {
+ StudioLogger.error("Could not call syncronization method for " + i + " : "
+ + e.getMessage());
+ }
+ }
+ }
+
+ syncInstance = null;
+
+ }
+
+ /*
+ * Synchronize the Emulator View by setting the selected instance
+ *
+ * @param instanceName the Device Instance name
+ */
+ @SuppressWarnings("unused")
+ private void syncEmulatorView(String instanceName)
+ {
+ try
+ {
+ IAndroidEmulatorInstance emulatorInstance =
+ DeviceFrameworkManager.getInstance().getInstanceByName(instanceName);
+ if (emulatorInstance != null)
+ {
+ AbstractAndroidView.setInstance(emulatorInstance);
+ }
+ else
+ {
+ StudioLogger.warn("Could not synchronize with Emulator View: " + instanceName
+ + " not in DeviceFrameworkManager model");
+ }
+
+ }
+ catch (Exception e)
+ {
+ StudioLogger.error("Could not synchronize with Emulator View: " + e.getMessage());
+ }
+ }
+
+ /*
+ * Synchronize the Device Management View by setting the selected instance
+ *
+ * @param instanceName the Device Instance name
+ */
+ @SuppressWarnings("unused")
+ private void syncDeviceView(String instanceName)
+ {
+
+ try
+ {
+ InstanceRegistry registry = InstanceRegistry.getInstance();
+ List<IInstance> tmlInstances = registry.getInstancesByName(instanceName);
+ if (tmlInstances.size() > 0)
+ {
+ IInstance tmlInstance = tmlInstances.get(0);
+ InstanceMgtView.setSeletectedInstance(tmlInstance);
+ }
+ else
+ {
+ StudioLogger.warn("Could not synchronize with Device Management View: "
+ + instanceName + " not in TmL InstanceManager model");
+ }
+ }
+ catch (Exception e)
+ {
+ StudioLogger.error("Could not synchronize with Device Management View: "
+ + e.getMessage());
+ }
+
+ }
+
+ /*
+ * Synchronize the DDMS Device View by setting the selected instance
+ *
+ * @param instanceName the Device Instance name
+ */
+ @SuppressWarnings("unused")
+ private void syncDDMSView(String instanceName)
+ {
+ try
+ {
+ IDevice device = DDMSFacade.getDeviceWithVmName(instanceName);
+ if (device != null)
+ {
+ DdmsPlugin.getDefault().selectionChanged(device, null);
+ }
+ else
+ {
+ StudioLogger
+ .warn("Could not synchronize with DDMS Devices View: Could not retrieve Device object from ADT model");
+ }
+ }
+ catch (Exception e)
+ {
+ StudioLogger.error("Could not synchronize with DDMS Devices View: " + e.getMessage());
+ }
+
+ }
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/ui/AbstractPropertiesComposite.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/ui/AbstractPropertiesComposite.java
new file mode 100644
index 0000000..8909b19
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/ui/AbstractPropertiesComposite.java
@@ -0,0 +1,249 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.emulator.device.ui;
+
+import java.util.Collection;
+import java.util.EventListener;
+import java.util.EventObject;
+import java.util.LinkedHashSet;
+
+import org.eclipse.sequoyah.device.framework.events.IInstanceListener;
+import org.eclipse.sequoyah.device.framework.events.InstanceAdapter;
+import org.eclipse.sequoyah.device.framework.events.InstanceEvent;
+import org.eclipse.sequoyah.device.framework.events.InstanceEventManager;
+import org.eclipse.sequoyah.device.framework.model.IInstance;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Widget;
+import org.eclipse.ui.PlatformUI;
+
+import com.motorola.studio.android.emulator.EmulatorPlugin;
+
+/**
+ * DESCRIPTION:
+ * <br>
+ * This class is an abstract implementation of a Composite extension that specific composites
+ * for making Android Emulator Device Instance UI may use.
+ * <br>
+ * It provides common functionalities to those subclasses which assist content validation,
+ * layout, etc.
+ * <br>
+ * RESPONSIBILITY:
+ * <br>
+ * - Provide common functionalities to classes implementing Android Emulator Device Instance UI
+ * <br>
+ * COLABORATORS:
+ * <br>
+ * Composite: extends this class
+ * <br>
+ * MagxPropertyCompositeChangeListener: declares this interface for other classes to be able to
+ * listen to change events on the content
+ * <br>
+ * MagxPropertyCompositeChangeEvent: declares the class and uses this kind of event for
+ * notifying content change to listeners
+ * <br>
+ * USAGE:
+ * <br>
+ * This class should be extended by UI classes implementing Android Emulator Device Instance
+ * UI (property pages, wizards, etc).
+ */
+public abstract class AbstractPropertiesComposite extends Composite
+{
+ /**
+ *
+ * This class represents events for notifying content change on AbstractPropertiesComposite
+ * extending classes to registered listeners.
+ *
+ */
+ @SuppressWarnings("serial")
+ public class PropertyCompositeChangeEvent extends EventObject
+ {
+ /**
+ * Creates a new MagxPropertyCompositeChangeEvent object with the composite
+ * that changed as its data.
+ *
+ * @param composite the composite that changed
+ */
+ PropertyCompositeChangeEvent(AbstractPropertiesComposite composite)
+ {
+ super(composite);
+ }
+ }
+
+ /**
+ *
+ * This interface must be implemented by classes that wish to listen to
+ * changes on AbstractPropertiesComposite extending classes.
+ *
+ */
+ public interface PropertyCompositeChangeListener extends EventListener
+ {
+ /**
+ * Notifies the event of change on AbstractPropertiesComposite extending classes.
+ *
+ * @param e the change event
+ */
+ public void compositeChanged(PropertyCompositeChangeEvent e);
+ }
+
+ private class PropertyInstanceListener extends InstanceAdapter
+ {
+ private Collection<Control> affectedControls;
+
+ public PropertyInstanceListener(Collection<Control> affectedControls)
+ {
+ this.affectedControls = affectedControls;
+ }
+
+ @Override
+ public void instanceUpdated(final InstanceEvent e)
+ {
+ PlatformUI.getWorkbench().getDisplay().syncExec(new Runnable()
+ {
+ public void run()
+ {
+ IInstance instance = e.getInstance();
+ boolean editable;
+ if (instance.getStatus().equals(EmulatorPlugin.STATUS_ONLINE_ID))
+ {
+ editable = false;
+ }
+ else
+ {
+ editable = true;
+ }
+ updateWidgetEnableStatus(editable, affectedControls);
+ }
+ });
+ }
+ }
+
+ // The default value to use for margins, both vertical and horizontal
+ private static final int DEFAULT_MARGIN_SIZE = 20;
+
+ // collection of registered listeners to this composite
+ private static final Collection<PropertyCompositeChangeListener> listeners =
+ new LinkedHashSet<PropertyCompositeChangeListener>();
+
+ // The listener that guarantees that if a instance is started it cannot be
+ // edited by the tool
+ private IInstanceListener instanceListener;
+
+ public AbstractPropertiesComposite(Composite parent)
+ {
+ super(parent, SWT.NONE);
+ }
+
+ /**
+ * Adds the given listener to the list of registered listeners of this composite's changes.
+ *
+ * @param listener the listener to be registered
+ */
+ public static void addCompositeChangeListener(PropertyCompositeChangeListener listener)
+ {
+ listeners.add(listener);
+ }
+
+ /**
+ * Removes the given listener from the list of registered listeners of this composite's changes.
+ *
+ * @param listener the listener to be unregistered
+ */
+ public static void removeCompositeChangeListener(PropertyCompositeChangeListener listener)
+ {
+ listeners.remove(listener);
+ }
+
+ /**
+ * Notifies an event of change on the composite to all registered listeners.
+ *
+ * This method must be called by extending classes whenever a change is made to them
+ * that registered listeners should be aware of (examples: typed text, button press, etc)
+ */
+ protected void notifyCompositeChangeListeners()
+ {
+ PropertyCompositeChangeEvent event = new PropertyCompositeChangeEvent(this);
+
+ for (PropertyCompositeChangeListener listener : listeners)
+ {
+ listener.compositeChanged(event);
+ }
+ }
+
+ protected void addInstanceListener(Collection<Control> affectedControls)
+ {
+ instanceListener = new PropertyInstanceListener(affectedControls);
+ InstanceEventManager.getInstance().addInstanceListener(instanceListener);
+ }
+
+ /**
+ * Given a collection of controls, turn all their enabled status to
+ * the one provided
+ *
+ * @param enabled True to enable all controls in the collection
+ * @param controlsToUpdate A collection of all controls to apply the state provided by enabled parameter
+ */
+ protected void updateWidgetEnableStatus(boolean enabled, Collection<Control> controlsToUpdate)
+ {
+ for (Control control : controlsToUpdate)
+ {
+ if (!control.isDisposed())
+ {
+ control.setEnabled(enabled);
+ }
+ }
+ }
+
+ /**
+ * Sets the main layout to this composite as a GridLayout with the
+ * given number of columns and with columns not the same width.
+ *
+ * @param numColumns the number of columns for the GridLayout
+ */
+ protected final void setMainLayout(int numColumns)
+ {
+ GridLayout mainLayout = new GridLayout(numColumns, false);
+ mainLayout.marginWidth = DEFAULT_MARGIN_SIZE;
+ mainLayout.marginHeight = DEFAULT_MARGIN_SIZE;
+ this.setLayout(mainLayout);
+ }
+
+ /**
+ * Declaration of the abstract getErrorMessage() method.
+ * Retrieves the error message associated with the current extending composite
+ * state.
+ * If no error is found with the current state, <code>null</code> <b>must</b>
+ * be returned by the extending composite.
+ *
+ * @return the error message, or <code>null</code> if there are no errors
+ */
+ public abstract String getErrorMessage();
+
+ /**
+ * @see Widget#dispose()
+ */
+ @Override
+ public void dispose()
+ {
+ if (instanceListener != null)
+ {
+ InstanceEventManager.getInstance().removeInstanceListener(instanceListener);
+ }
+ super.dispose();
+ }
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/ui/AndroidPropertiesPage.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/ui/AndroidPropertiesPage.java
new file mode 100644
index 0000000..83571e0
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/ui/AndroidPropertiesPage.java
@@ -0,0 +1,185 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.emulator.device.ui;
+
+import org.eclipse.core.runtime.IAdaptable;
+import org.eclipse.jface.preference.PreferenceDialog;
+import org.eclipse.sequoyah.device.framework.events.InstanceEvent;
+import org.eclipse.sequoyah.device.framework.events.InstanceEventManager;
+import org.eclipse.sequoyah.device.framework.events.InstanceEvent.InstanceEventType;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.ui.IWorkbenchPropertyPage;
+import org.eclipse.ui.dialogs.PropertyPage;
+
+import com.motorola.studio.android.emulator.device.IDevicePropertiesConstants;
+import com.motorola.studio.android.emulator.device.instance.AndroidDeviceInstance;
+import com.motorola.studio.android.emulator.device.ui.AbstractPropertiesComposite.PropertyCompositeChangeEvent;
+import com.motorola.studio.android.emulator.device.ui.AbstractPropertiesComposite.PropertyCompositeChangeListener;
+
+/**
+ * DESCRIPTION:
+ * <br>
+ * This class implements the Property Page for Android Emulator Device Instances.
+ * <br>
+ * It shows all Android Emulator Device Instance properties on the UI so that the user
+ * is able to edit it (the instance name is not a property and will not be editable).
+ * <br>
+ * RESPONSIBILITY:
+ * <br>
+ * - Allow viewing and editing of Android Emulator Device Instance properties
+ * <br>
+ * COLABORATORS:
+ * <br>
+ * PropertyPage: extends this class
+ * <br>
+ * InfoComposite: uses this composite for exhibiting instance properties on the UI
+ * <br>
+ * USAGE:
+ * <br>
+ * This class should be defined by the plugin.xml file as a regular Eclipse Property Page.
+ * It should be enabled for AndroidEmulatorInstance objects.
+ */
+public class AndroidPropertiesPage extends PropertyPage implements IWorkbenchPropertyPage,
+ IDevicePropertiesConstants
+{
+ // the Android Emulator Device Instance to which this Property Page applies
+ private AndroidDeviceInstance emuInstance;
+
+ private InfoComposite infoComposite;
+
+ // whether this property page will need its default message to be reset
+ // this happens in case the initial state of the property page when it is
+ // opened is an erroneous state (any of the properties contain invalid value)
+ private boolean defaultMessageNeedsReset = false;
+
+ // the default message defined by Eclipse implementation for reset purposes
+ private String defaultMessage = getMessage();
+
+ // handle changes
+ private PropertyCompositeChangeListener compositeChangeListener =
+ new PropertyCompositeChangeListener()
+ {
+ public void compositeChanged(PropertyCompositeChangeEvent e)
+ {
+ String errorMessage = infoComposite.getErrorMessage();
+ setErrorMessage(errorMessage);
+ setValid((errorMessage == null) && (getMessage() == null));
+
+ if (defaultMessageNeedsReset)
+ {
+ defaultMessageNeedsReset = false;
+ setMessage(defaultMessage);
+ }
+ }
+ };
+
+ /**
+ * Creates the UI contents of this Property Page.
+ * It shows the Android Emulator Device Instance properties
+ * organized into tabs.
+ */
+ @Override
+ protected Control createContents(Composite parent)
+ {
+ ((PreferenceDialog) this.getContainer()).getTreeViewer().expandAll();
+
+ noDefaultAndApplyButton();
+
+ GridLayout mainLayout = new GridLayout(1, false);
+ mainLayout.marginWidth = 0;
+ mainLayout.marginHeight = 0;
+ Composite composite = new Composite(parent, SWT.NULL);
+ composite.setLayout(mainLayout);
+
+ infoComposite =
+ new InfoComposite(composite, emuInstance.getProperties(), emuInstance.getName(),
+ !emuInstance.isStarted());
+ infoComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+
+ AbstractPropertiesComposite.addCompositeChangeListener(compositeChangeListener);
+
+ // there may be some info message for the composite
+ String initialMessage = infoComposite.getInfoMessage();
+
+ // if no info message, check if there is some error message
+ if (initialMessage == null)
+ {
+ // if anything is not correct with instance property values,
+ // show the error message, but as an information to follow
+ // UI guidelines
+ initialMessage = infoComposite.getErrorMessage();
+
+ setValid((initialMessage == null));
+ }
+
+ if (initialMessage != null)
+ {
+ defaultMessageNeedsReset = true;
+ setMessage(initialMessage, INFORMATION);
+ }
+
+ return composite;
+ }
+
+ /**
+ * Sets the element that owns the properties
+ */
+ @Override
+ public void setElement(IAdaptable element)
+ {
+ // save the instance for direct use
+ if (element instanceof AndroidDeviceInstance)
+ {
+ emuInstance = (AndroidDeviceInstance) element;
+ }
+
+ super.setElement(element);
+ }
+
+ /**
+ * Performs the OK operation by setting the edited properties as the
+ * properties for the Android Emulator Device Instance to which this
+ * Property Page applies (the object for which it was created).
+ */
+ @Override
+ public boolean performOk()
+ {
+ if (emuInstance != null)
+ {
+ emuInstance.setProperties(infoComposite.getPropertiesWorkingCopy());
+ InstanceEventManager.getInstance().notifyListeners(
+ new InstanceEvent(InstanceEventType.INSTANCE_UPDATED, emuInstance));
+ }
+
+ return super.performOk();
+ }
+
+ /**
+ * Remove listeners and dispose widgets
+ */
+ @Override
+ public void dispose()
+ {
+ AbstractPropertiesComposite.removeCompositeChangeListener(compositeChangeListener);
+ infoComposite.dispose();
+ super.dispose();
+ }
+
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/ui/AndroidPropertiesStartupOptionsPage.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/ui/AndroidPropertiesStartupOptionsPage.java
new file mode 100644
index 0000000..5888ab9
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/ui/AndroidPropertiesStartupOptionsPage.java
@@ -0,0 +1,228 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.emulator.device.ui;
+
+import java.util.Properties;
+
+import org.eclipse.core.runtime.IAdaptable;
+import org.eclipse.sequoyah.device.framework.events.InstanceEvent;
+import org.eclipse.sequoyah.device.framework.events.InstanceEvent.InstanceEventType;
+import org.eclipse.sequoyah.device.framework.events.InstanceEventManager;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.ui.IWorkbenchPropertyPage;
+import org.eclipse.ui.dialogs.PropertyPage;
+
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.emulator.core.exception.SkinException;
+import com.motorola.studio.android.emulator.core.skin.IAndroidSkin;
+import com.motorola.studio.android.emulator.core.skin.SkinFramework;
+import com.motorola.studio.android.emulator.device.IDevicePropertiesConstants;
+import com.motorola.studio.android.emulator.device.instance.AndroidDeviceInstance;
+import com.motorola.studio.android.emulator.device.instance.options.StartupOptionsMgt;
+import com.motorola.studio.android.emulator.device.ui.AbstractPropertiesComposite.PropertyCompositeChangeEvent;
+import com.motorola.studio.android.emulator.device.ui.AbstractPropertiesComposite.PropertyCompositeChangeListener;
+import com.motorola.studio.android.nativeos.NativeUIUtils;
+
+/**
+ * DESCRIPTION:
+ * <br>
+ * This class implements the Startup Options Property Page for Android Emulator Device Instances.
+ * <br>
+ * It shows the Startup Options for the Android Emulator Device Instance on the UI so that the user
+ * is able to edit it.
+ * <br>
+ * RESPONSIBILITY:
+ * <br>
+ * - Allow viewing and editing Startup Options of an Android Emulator Device Instance
+ * <br>
+ * COLABORATORS:
+ * <br>
+ * PropertyPage: extends this class
+ * <br>
+ * StartupOptionsComposite: uses this composite for exhibiting startup options on the UI
+ * <br>
+ * USAGE:
+ * <br>
+ * This class should be defined by the plugin.xml file as a regular Eclipse Property Page.
+ * It should be enabled for AndroidEmulatorInstance objects.
+ *
+ */
+public class AndroidPropertiesStartupOptionsPage extends PropertyPage implements
+ IWorkbenchPropertyPage, IDevicePropertiesConstants
+{
+
+ // the Android Emulator Device Instance to which this Property Page applies
+ private AndroidDeviceInstance emuInstance;
+
+ private StartupOptionsComposite startupOptionsComposite;
+
+ // whether this property page will need its default message to be reset
+ // this happens in case the initial state of the property page when it is
+ // opened is an erroneous state (any of the properties contain invalid value)
+ private boolean defaultMessageNeedsReset = false;
+
+ // the default message defined by Eclipse implementation for reset purposes
+ private final String defaultMessage = getMessage();
+
+ // handle changes
+ private final PropertyCompositeChangeListener compositeChangeListener =
+ new PropertyCompositeChangeListener()
+ {
+ public void compositeChanged(PropertyCompositeChangeEvent e)
+ {
+ String errorMessage = startupOptionsComposite.getErrorMessage();
+ setErrorMessage(errorMessage);
+ setValid((errorMessage == null) && (getMessage() == null));
+
+ if (defaultMessageNeedsReset)
+ {
+ defaultMessageNeedsReset = false;
+ setMessage(defaultMessage);
+ }
+ }
+ };
+
+ /**
+ * Creates the UI contents of this Property Page.
+ * It shows the Android Emulator Device Instance properties
+ * organized into tabs.
+ */
+ @Override
+ protected Control createContents(Composite parent)
+ {
+ // Create Startup Options area
+
+ SkinFramework sm = new SkinFramework();
+ IAndroidSkin skin = null;
+ boolean canCalculateScale = true;
+ try
+ {
+ skin = sm.getSkinById(emuInstance.getSkinId(), emuInstance.getSkinPath());
+ }
+ catch (SkinException e)
+ {
+ StudioLogger.error(this.getClass(),
+ "Error reading instance skin during startup options page creation", e);
+ canCalculateScale = false;
+ }
+
+ startupOptionsComposite =
+ new StartupOptionsComposite(parent, emuInstance.getCommandLineArguments(), skin,
+ canCalculateScale);
+ startupOptionsComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+
+ AbstractPropertiesComposite.addCompositeChangeListener(compositeChangeListener);
+
+ // If anything is not correct with instance property values,
+ // show the error message, but as an information to follow
+ // UI guidelines
+ String errorMessage = startupOptionsComposite.getErrorMessage();
+ setValid((errorMessage == null));
+ if (errorMessage != null)
+ {
+ defaultMessageNeedsReset = true;
+ setMessage(errorMessage, INFORMATION);
+ }
+
+ return startupOptionsComposite;
+
+ }
+
+ /**
+ * Sets the element that owns the properties
+ */
+ @Override
+ public void setElement(IAdaptable element)
+ {
+ // save the instance for direct use
+ if (element instanceof AndroidDeviceInstance)
+ {
+ emuInstance = (AndroidDeviceInstance) element;
+ }
+
+ super.setElement(element);
+ }
+
+ /**
+ * Performs the OK operation by setting the edited startup options as the
+ * startup options for the Android Emulator Device Instance to which this
+ * Property Page applies (the object for which it was created).
+ *
+ * @see org.eclipse.jface.preference.PreferencePage#performOk()
+ */
+ @Override
+ public boolean performOk()
+ {
+ save();
+ return super.performOk();
+ }
+
+ /**
+ * Performs the Apply operation (which is the same as OK)
+ *
+ * @see org.eclipse.jface.preference.PreferencePage#performApply()
+ */
+ @Override
+ protected void performApply()
+ {
+ save();
+ super.performApply();
+ }
+
+ /**
+ * Save the edited startup options in the Android Emulator Device Instance
+ */
+ private void save()
+ {
+ if (emuInstance != null)
+ {
+ Properties emuProperties = emuInstance.getProperties();
+ emuProperties.setProperty(IDevicePropertiesConstants.commandline,
+ StartupOptionsMgt.getParamList());
+ emuInstance.setProperties(emuProperties);
+ InstanceEventManager.getInstance().notifyListeners(
+ new InstanceEvent(InstanceEventType.INSTANCE_UPDATED, emuInstance));
+ }
+ }
+
+ /**
+ * Set the default initial properties
+ *
+ * @see org.eclipse.jface.preference.PreferencePage#performDefaults()
+ */
+ @Override
+ protected void performDefaults()
+ {
+ startupOptionsComposite.reloadValues(NativeUIUtils.getDefaultCommandLine());
+ super.performDefaults();
+ }
+
+ /**
+ * Remove listeners and dispose widgets
+ */
+ @Override
+ public void dispose()
+ {
+ AbstractPropertiesComposite.removeCompositeChangeListener(compositeChangeListener);
+ startupOptionsComposite.dispose();
+ startupOptionsComposite = null;
+ super.dispose();
+ }
+
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/ui/DevicePropertiesPage.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/ui/DevicePropertiesPage.java
new file mode 100644
index 0000000..34915b6
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/ui/DevicePropertiesPage.java
@@ -0,0 +1,47 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.emulator.device.ui;
+
+import java.util.Properties;
+
+import org.eclipse.core.runtime.IAdaptable;
+import org.eclipse.ui.IWorkbenchPropertyPage;
+
+import com.motorola.studio.android.adt.DDMSFacade;
+import com.motorola.studio.android.adt.ISerialNumbered;
+import com.motorola.studio.android.devices.AbstractDevicePropertyPage;
+
+public class DevicePropertiesPage extends AbstractDevicePropertyPage implements
+ IWorkbenchPropertyPage
+{
+
+ private ISerialNumbered androidIntance;
+
+ @Override
+ public void setElement(IAdaptable element)
+ {
+
+ this.androidIntance = (ISerialNumbered) element;
+
+ super.setElement(element);
+ }
+
+ @Override
+ protected Properties getDeviceProperties()
+ {
+ return DDMSFacade.getDeviceProperties(androidIntance.getSerialNumber());
+ }
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/ui/DpiScaleCalculatorDialog.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/ui/DpiScaleCalculatorDialog.java
new file mode 100644
index 0000000..0e3a015
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/ui/DpiScaleCalculatorDialog.java
@@ -0,0 +1,383 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.emulator.device.ui;
+
+import java.awt.Dimension;
+import java.awt.Toolkit;
+import java.util.ArrayList;
+import java.util.Collection;
+
+import org.eclipse.jface.dialogs.Dialog;
+import org.eclipse.jface.dialogs.IDialogConstants;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.ModifyEvent;
+import org.eclipse.swt.events.ModifyListener;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.graphics.Color;
+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.Group;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Text;
+
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.emulator.core.exception.SkinException;
+import com.motorola.studio.android.emulator.core.skin.IAndroidSkin;
+import com.motorola.studio.android.emulator.core.skin.ISkinKeyXmlTags;
+import com.motorola.studio.android.emulator.i18n.EmulatorNLS;
+
+public class DpiScaleCalculatorDialog extends Dialog
+{
+ private Text screenSizeValue;
+
+ private Button monitorDpiValueButton;
+
+ private Text monitorDpiValue;
+
+ private Text monitorSizeText;
+
+ private Text resultDpivalueText;
+
+ private Integer resultDpiValue;
+
+ private Label resultScaleText;
+
+ private Double resultScaleValue;
+
+ private Text resultScaleValueText;
+
+ private Label errorLabel;
+
+ private final IAndroidSkin skin;
+
+ private final Collection<String> errors = new ArrayList<String>();
+
+ int size1 = -1;
+
+ int size2 = -1;
+
+ /**
+ * The Dialog constructor
+ *
+ * @param parentShell The shell
+ * @param skin The selected skin of the AVD being created/edited
+ */
+ protected DpiScaleCalculatorDialog(Shell parentShell, IAndroidSkin skin)
+ {
+ super(parentShell);
+ this.skin = skin;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.jface.window.Window#configureShell(org.eclipse.swt.widgets.Shell)
+ */
+ @Override
+ protected void configureShell(Shell newShell)
+ {
+ super.configureShell(newShell);
+ newShell.setText(EmulatorNLS.DPISCALECALCULATOR_Title);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.jface.dialogs.Dialog#createDialogArea(org.eclipse.swt.widgets.Composite)
+ */
+ @Override
+ protected Control createDialogArea(Composite parent)
+ {
+ //Main Composite
+ Composite mainComposite = new Composite(parent, SWT.FILL);
+ mainComposite.setLayout(new GridLayout(2, false));
+ GridData data;
+
+ //Error Area
+ errorLabel = new Label(mainComposite, SWT.READ_ONLY);
+ errorLabel.setForeground(new Color(null, 255, 0, 0));
+ data = new GridData(SWT.LEFT, SWT.CENTER, false, false, 2, 1);
+ errorLabel.setLayoutData(data);
+
+ //Screen Size
+ Label screenSizeLabel = new Label(mainComposite, SWT.READ_ONLY);
+ screenSizeLabel.setText(EmulatorNLS.DPISCALECALCULATOR_ScreenSize_Label);
+ data = new GridData(SWT.LEFT, SWT.CENTER, false, false);
+ screenSizeLabel.setLayoutData(data);
+
+ screenSizeValue = new Text(mainComposite, SWT.BORDER);
+ data = new GridData(SWT.FILL, SWT.NULL, true, false);
+ screenSizeValue.setLayoutData(data);
+ screenSizeValue.addModifyListener(new ModifyListener()
+ {
+ public void modifyText(ModifyEvent e)
+ {
+ validate();
+ }
+ });
+
+ //Monitor DPI Group
+ Group monitorDpiGroup = new Group(mainComposite, SWT.SHADOW_OUT);
+ monitorDpiGroup.setText(EmulatorNLS.DPISCALECALCULATOR_MonitorDpi_Label);
+ data = new GridData(SWT.FILL, SWT.CENTER, true, false, 2, 1);
+ monitorDpiGroup.setLayoutData(data);
+ monitorDpiGroup.setLayout(new GridLayout(3, false));
+
+ //Insert Monitor DPI value Option
+ monitorDpiValueButton = new Button(monitorDpiGroup, SWT.RADIO);
+ monitorDpiValueButton.setText(EmulatorNLS.DPISCALECALCULATOR_MonitorDpivalue_Label);
+ monitorDpiValueButton.setSelection(true);
+ monitorDpiValueButton.addSelectionListener(new SelectionAdapter()
+ {
+ @Override
+ public void widgetSelected(SelectionEvent e)
+ {
+ monitorDpiValue.setEnabled(monitorDpiValueButton.getSelection());
+ validate();
+ }
+ });
+ data = new GridData(SWT.LEFT, SWT.TOP, false, false, 1, 1);
+ monitorDpiValueButton.setLayoutData(data);
+
+ monitorDpiValue = new Text(monitorDpiGroup, SWT.SINGLE | SWT.BORDER);
+ data = new GridData(SWT.FILL, SWT.TOP, true, false, 2, 1);
+ data.widthHint = 100;
+ monitorDpiValue.setLayoutData(data);
+ monitorDpiValue.setEnabled(monitorDpiValueButton.getSelection());
+ monitorDpiValue.addModifyListener(new ModifyListener()
+ {
+ public void modifyText(ModifyEvent e)
+ {
+ validate();
+ }
+ });
+
+ //Calculate Monitor DPI Option
+ final Button calculateMonitorDpiButton = new Button(monitorDpiGroup, SWT.RADIO);
+ calculateMonitorDpiButton.setText(EmulatorNLS.DPISCALECALCULATOR_MonitorDpiSize_Label);
+ calculateMonitorDpiButton.setSelection(false);
+ calculateMonitorDpiButton.addSelectionListener(new SelectionAdapter()
+ {
+ @Override
+ public void widgetSelected(SelectionEvent e)
+ {
+ monitorSizeText.setEnabled(calculateMonitorDpiButton.getSelection());
+ validate();
+ }
+ });
+
+ monitorSizeText = new Text(monitorDpiGroup, SWT.SINGLE | SWT.BORDER);
+ data = new GridData(SWT.FILL, SWT.CENTER, true, false, 2, 1);
+ monitorSizeText.setLayoutData(data);
+ monitorSizeText.setEnabled(calculateMonitorDpiButton.getSelection());
+ monitorSizeText.addModifyListener(new ModifyListener()
+ {
+ public void modifyText(ModifyEvent e)
+ {
+ validate();
+ }
+ });
+
+ //Result Group
+ Group resultGroup = new Group(mainComposite, SWT.SHADOW_OUT);
+ resultGroup.setText(EmulatorNLS.DPISCALECALCULATOR_ResultGroup_Title);
+ data = new GridData(SWT.FILL, SWT.FILL, true, false, 2, 1);
+ resultGroup.setLayoutData(data);
+ resultGroup.setLayout(new GridLayout(4, false));
+
+ //Moitor DPI
+ Label resultDpi = new Label(resultGroup, SWT.READ_ONLY);
+ resultDpi.setText(EmulatorNLS.DPISCALECALCULATOR_ResultMonitorDpi_Label);
+ data = new GridData(SWT.LEFT, SWT.CENTER, false, false);
+ resultDpi.setLayoutData(data);
+
+ resultDpivalueText = new Text(resultGroup, SWT.WRAP | SWT.READ_ONLY);
+ data = new GridData(SWT.FILL, SWT.NULL, true, false);
+ resultDpivalueText.setLayoutData(data);
+
+ //Scale
+ resultScaleText = new Label(resultGroup, SWT.READ_ONLY);
+ resultScaleText.setText(EmulatorNLS.DPISCALECALCULATOR_ResultScale_Label);
+ data = new GridData(SWT.LEFT, SWT.CENTER, false, false);
+ resultScaleText.setLayoutData(data);
+
+ resultScaleValueText = new Text(resultGroup, SWT.WRAP | SWT.READ_ONLY);
+ data = new GridData(SWT.FILL, SWT.NULL, true, false);
+ resultScaleValueText.setLayoutData(data);
+
+ mainComposite.layout();
+ mainComposite.pack();
+
+ return null;
+ }
+
+ /**
+ * Updates the Monitor DPI and Scale results
+ *
+ */
+ private void updateResult()
+ {
+
+ if (monitorDpiValueButton.getSelection())
+ {
+ resultDpiValue = Integer.parseInt(monitorDpiValue.getText());
+ }
+ else
+ {
+ resultDpiValue = calculateMonitorDpi();
+ }
+ resultDpivalueText.setText(resultDpiValue.toString());
+
+ resultScaleValue = calculateScale();
+ resultScaleValueText.setText(resultScaleValue.toString());
+ }
+
+ /**
+ * Calculates the Monitor DPI using the user monitor size and the resolution
+ *
+ * @return int The calculated Monitor DPI
+ */
+ private int calculateMonitorDpi()
+ {
+ float monitorSize = Float.parseFloat(monitorSizeText.getText());
+ Dimension b = Toolkit.getDefaultToolkit().getScreenSize();
+ float width = b.width;
+ float height = b.height;
+ float ratio = width / height;
+
+ double dpi =
+ Math.round((width / (ratio * (Math.sqrt((Math.pow(monitorSize, 2))
+ / (1f + Math.pow(ratio, 2)))))));
+
+ return (int) dpi;
+ }
+
+ /**
+ * Calculates the scale to be used using the monitor dpi, the device screen size and the skin main display dimensions
+ *
+ * @return
+ */
+ private double calculateScale()
+ {
+ double dpi = resultDpiValue;
+
+ if ((skin != null) && (size1 == -1) && (size2 == -1))
+ {
+ try
+ {
+ Collection<String> layouts = skin.getAvailableLayouts();
+ String defLayout = layouts.toArray()[0].toString();
+
+ size1 =
+ skin.getSkinBean(defLayout).getSkinPropertyValue(
+ ISkinKeyXmlTags.SKIN_INTERNAL_VIEW_WIDTH);
+
+ size2 =
+ skin.getSkinBean(defLayout).getSkinPropertyValue(
+ ISkinKeyXmlTags.SKIN_INTERNAL_VIEW_HEIGHT);
+ }
+ catch (SkinException e)
+ {
+ StudioLogger.error(DpiScaleCalculatorDialog.class, "Error while calculating scale", //$NON-NLS-1$
+ e);
+ }
+
+ }
+ if ((size1 > 0) && (size2 > 0))
+ {
+ double diagonalPx = Math.sqrt(Math.pow(size1, 2) + Math.pow(size2, 2));
+ double screenSize = Double.parseDouble(screenSizeValue.getText());
+
+ double scale = (screenSize * dpi) / diagonalPx;
+ return (Math.round((scale * 100.0))) / 100.0;
+ }
+ else
+ {
+ getButton(IDialogConstants.OK_ID).setEnabled(false);
+ }
+ return 1;
+ }
+
+ /**
+ * Validates all the calculator fields
+ */
+ public void validate()
+ {
+ final String REGEX_1 = EmulatorNLS.DPISCALECALCULATOR_Regex_TwoDigits;
+ final String REGEX_2 = "\\d+"; //$NON-NLS-1$
+
+ final String ERROR_SCREEN_SIZE = EmulatorNLS.DPISCALECALCULATOR_Error_ScreenSize;
+ final String ERROR_DPI_VALUE = EmulatorNLS.DPISCALECALCULATOR_Error_MonitorDpi;
+ final String ERROR_MONITOR_SIZE = EmulatorNLS.DPISCALECALCULATOR_Error_MonitorSize;
+
+ errors.clear();
+ errorLabel.setText(""); //$NON-NLS-1$
+
+ if (!screenSizeValue.getText().matches(REGEX_1))
+ {
+ errors.add(ERROR_SCREEN_SIZE);
+ }
+
+ if (monitorDpiValueButton.getSelection() && !monitorDpiValue.getText().matches(REGEX_2))
+ {
+ errors.add(ERROR_DPI_VALUE);
+ }
+
+ if (!monitorDpiValueButton.getSelection() && !monitorSizeText.getText().matches(REGEX_1))
+ {
+ errors.add(ERROR_MONITOR_SIZE);
+ }
+
+ if (errors.size() > 0)
+ {
+ getButton(OK).setEnabled(false);
+ errorLabel.setText((String) errors.toArray()[0]);
+ errorLabel.pack();
+
+ resultDpivalueText.setText(""); //$NON-NLS-1$
+ resultScaleValueText.setText(""); //$NON-NLS-1$
+ }
+ else
+ {
+ getButton(OK).setEnabled(true);
+ updateResult();
+ }
+
+ }
+
+ /**
+ * Gets the calculated device dpi
+ *
+ * @return the device dpi to be used
+ */
+ public String getResultDpivalue()
+ {
+ return resultDpiValue.toString();
+ }
+
+ /**
+ * Gets the calculated scale
+ *
+ * @return the scale to be used
+ */
+ public String getResultScaleValue()
+ {
+ return resultScaleValue.toString();
+ }
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/ui/InfoComposite.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/ui/InfoComposite.java
new file mode 100644
index 0000000..278de88
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/ui/InfoComposite.java
@@ -0,0 +1,210 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.emulator.device.ui;
+
+import java.util.Properties;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.DisposeEvent;
+import org.eclipse.swt.events.DisposeListener;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+
+import com.motorola.studio.android.adt.SdkUtils;
+import com.motorola.studio.android.emulator.device.IDevicePropertiesConstants;
+import com.motorola.studio.android.emulator.i18n.EmulatorNLS;
+
+/**
+ * DESCRIPTION:
+ * <br>
+ * This class is a composite which shows all Android Emulator Device Instance information.
+ * All displayed information can be edited and kept on a Properties object, which can be
+ * later used to be set to an actual Android Emulator Device Instance object as its new values.
+ * <br>
+ * It extends the AbstractPropertiesComposite so as to use its common functionalities.
+ * <br>
+ * RESPONSIBILITY:
+ * <br>
+ * - Show all available UI information of a Android Emulator Device Instance
+ * <br>
+ * COLABORATORS:
+ * <br>
+ * AbstractPropertiesComposite: extends this class
+ * <br>
+ * IDevicePropertiesConstants: implements this interface in order to have direct use of its
+ * constants for populating the device instance's Properties object
+ * <br>
+ * USAGE:
+ * <br>
+ * This composite can be used for any UI that shows all Android Emulator Device Instance
+ * UI information. It can either allow of stop the editing of the name of the instance,
+ * while other information is always editable.
+ * It should be instantiated as a regular composite.
+ */
+public class InfoComposite extends AbstractPropertiesComposite implements
+ IDevicePropertiesConstants
+{
+
+ private boolean showEmulatorDefNotFoundMsg = false;
+
+ private Properties emuPropertiesWorkingCopy;
+
+ private String emuName;
+
+ private PropertiesMainComposite mainComposite;
+
+ // the listener to changes on all AbstractPropertiesComposite objects used on this composite
+ private PropertyCompositeChangeListener listener = new PropertyCompositeChangeListener()
+ {
+ public void compositeChanged(PropertyCompositeChangeEvent e)
+ {
+ if (e.getSource() instanceof PropertiesMainComposite)
+ {
+ emuPropertiesWorkingCopy.setProperty(timeout, mainComposite.getTimeout());
+ emuPropertiesWorkingCopy.setProperty(skinId, mainComposite.getSkinId());
+ emuPropertiesWorkingCopy.setProperty(useVnc, mainComposite.getUseVnc());
+ emuPropertiesWorkingCopy.setProperty(useProxy, mainComposite.getUseProxy());
+ emuPropertiesWorkingCopy.setProperty(startFromSnapshot,
+ mainComposite.getstartFromSnapshot());
+ emuPropertiesWorkingCopy.setProperty(saveSnapshot, mainComposite.getSaveSnapshot());
+ emuPropertiesWorkingCopy.setProperty(abiType, mainComposite.getAbiType());
+
+ notifyCompositeChangeListeners();
+ }
+ }
+ };
+
+ /**
+ * Creates a InfoComposite object.
+ *
+ * @param parent the parent composite
+ * @param emuProperties the instance properties to be shown on the UI
+ * @param emuName the name of the instance
+ * @param isNameEditable whether the instance name should be made editable or not
+ * @param areOtherFieldsEditable True if the user will be able to edit other data in the composite; false otherwise
+ */
+ public InfoComposite(Composite parent, Properties emuProperties, String emuName,
+ boolean areOtherFieldsEditable)
+ {
+ super(parent);
+
+ this.emuPropertiesWorkingCopy = (Properties) emuProperties.clone();
+ this.emuName = emuName;
+
+ createUI(areOtherFieldsEditable);
+
+ parent.addDisposeListener(new DisposeListener()
+ {
+ public void widgetDisposed(DisposeEvent e)
+ {
+ AbstractPropertiesComposite.removeCompositeChangeListener(listener);
+ InfoComposite.this.dispose();
+ }
+ });
+ }
+
+ /**
+ * Creates all the UI for this composite. The listener declared as attribute to this class
+ * is added to all composites extending AbstractPropertiesComposite.
+ * Information is organized on 3 tabs:<br>
+ * - "Main" tab: uses the PropertiesMainComposite;<br>
+ * - "Advanced" tab: uses the PropertiesAdvancedComposite;<br>
+ *
+ * @param editable True if the user will be able to edit data in the composite; false otherwise
+ */
+ private void createUI(boolean editable)
+ {
+ GridLayout mainLayout = new GridLayout(1, false);
+ mainLayout.marginWidth = 0;
+ mainLayout.marginHeight = 0;
+ this.setLayout(mainLayout);
+
+ mainComposite =
+ new PropertiesMainComposite(this, emuName, getProperty(emulatorDefId),
+ getProperty(timeout), Boolean.parseBoolean(getProperty(useVnc)),
+ Boolean.parseBoolean(getProperty(useProxy)),
+ Boolean.parseBoolean(getProperty(useSnapshots)),
+ Boolean.parseBoolean(getProperty(saveSnapshot)),
+ Boolean.parseBoolean(getProperty(startFromSnapshot)),
+ SdkUtils.getTargetByName(getProperty(vmTarget)), getProperty(vmSkin),
+ getProperty(vmPath), getProperty(abiType), true, false, editable);
+ mainComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+
+ AbstractPropertiesComposite.addCompositeChangeListener(listener);
+ }
+
+ /**
+ * Retrieves the given property from the instance.
+ *
+ * @param propertyName the name of the property (key)
+ *
+ * @return the property value
+ */
+ private String getProperty(String propertyName)
+ {
+ String emuProperty = emuPropertiesWorkingCopy.getProperty(propertyName);
+ if (emuProperty == null)
+ {
+ emuProperty = "";
+ }
+ return emuProperty;
+ }
+
+ /**
+ * Retrieves the (potentially) edited properties.
+ *
+ * @return the edited properties
+ */
+ public Properties getPropertiesWorkingCopy()
+ {
+ return emuPropertiesWorkingCopy;
+ }
+
+ /**
+ * Retrieves the error message associated to this composites current state.
+ * "Main" tab's error message is returned if any; if none, "Skin" tab's error
+ * message is returned if any; if none, "VM" tab's error message is returned if
+ * any; if none, <code>null</code> is returned to state there is no error with
+ * the current state.
+ *
+ * @return the error message, or <code>null</code> if there are no errors
+ */
+ @Override
+ public String getErrorMessage()
+ {
+ String errorMessage = mainComposite.getErrorMessage();
+
+ return errorMessage;
+ }
+
+ /**
+ * Retrieves the information message associated to this composites current state.
+ *
+ * @return the information message, or <code>null</code> if there are no information messages
+ */
+ public String getInfoMessage()
+ {
+ String infoMessage = null;
+
+ if (showEmulatorDefNotFoundMsg)
+ {
+ infoMessage = EmulatorNLS.INFO_InfoComposite_EmulatorDefinitionNotFound;
+ }
+
+ return infoMessage;
+ }
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/ui/PropertiesMainComposite.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/ui/PropertiesMainComposite.java
new file mode 100644
index 0000000..9c3b391
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/ui/PropertiesMainComposite.java
@@ -0,0 +1,1287 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.emulator.device.ui;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Properties;
+
+import org.eclipse.core.runtime.Path;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.jface.preference.IPreferenceNode;
+import org.eclipse.jface.preference.PreferenceDialog;
+import org.eclipse.jface.preference.PreferenceManager;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.ModifyEvent;
+import org.eclipse.swt.events.ModifyListener;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Combo;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.DirectoryDialog;
+import org.eclipse.swt.widgets.FileDialog;
+import org.eclipse.swt.widgets.Group;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Link;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.internal.dialogs.WorkbenchPreferenceDialog;
+
+import com.android.sdklib.IAndroidTarget;
+import com.android.sdklib.ISystemImage;
+import com.android.sdklib.SdkConstants;
+import com.android.sdklib.internal.avd.AvdInfo;
+import com.motorola.studio.android.adt.SdkUtils;
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.emulator.device.IAndroidDeviceConstants;
+import com.motorola.studio.android.emulator.device.IDevicePropertiesConstants;
+import com.motorola.studio.android.emulator.device.definition.AndroidEmuDefMgr;
+import com.motorola.studio.android.emulator.i18n.EmulatorNLS;
+
+/**
+ * DESCRIPTION:
+ * <br>
+ * This class implements the UI for showing all Android Emulator Device Instance main information,
+ * such as its name, description, etc.
+ * <br>
+ * It extends the AbstractPropertiesComposite so as to use its common functionalities.
+ * <br>
+ * RESPONSIBILITY:
+ * <br>
+ * - Show Android Emulator Device Instance main information on the UI
+ * <br>
+ * COLABORATORS:
+ * <br>
+ * AbstractPropertiesComposite: extends this class
+ * <br>
+ * USAGE:
+ * <br>
+ * This class should be added as a regular composite whenever main information on Android Emulator
+ * Device Instance is necessary to be shown and edited on the UI. It can either allow of stop the
+ * editing of the name of the instance, while other information is always editable.
+ */
+//@SuppressWarnings("restriction")
+@SuppressWarnings("restriction")
+public class PropertiesMainComposite extends AbstractPropertiesComposite
+{
+ private static final String ORG_ECLIPSE_UI_NET_NET_PREFERENCES =
+ "org.eclipse.ui.net.NetPreferences"; //$NON-NLS-1$
+
+ private final AndroidEmuDefMgr emuDefMgr = AndroidEmuDefMgr.getInstance();
+
+ private String name = ""; //$NON-NLS-1$
+
+ private String skinId = ""; //$NON-NLS-1$
+
+ private String timeout = ""; //$NON-NLS-1$
+
+ private boolean useVnc;
+
+ private boolean useProxy;
+
+ private boolean useSnapshots;
+
+ // VM Settings
+ private IAndroidTarget vmTarget = null;
+
+ private String vmSkin = ""; //$NON-NLS-1$
+
+ private String sdCardType = ""; //$NON-NLS-1$
+
+ private String sdCardValue = ""; //$NON-NLS-1$
+
+ private String vmPath = ""; //$NON-NLS-1$
+
+ private boolean usingDefaultVmPath;
+
+ private String abiType = "";
+
+ /*
+ * SD Card
+ */
+ private static final String SDCARD_TYPE_NONE = "NONE"; //$NON-NLS-1$
+
+ private static final String SDCARD_TYPE_PATH = "PATH"; //$NON-NLS-1$
+
+ private static final String SDCARD_TYPE_SIZE = "SIZE"; //$NON-NLS-1$
+
+ private static final String SDCARD_PATH_EXTENSION = ".img"; //$NON-NLS-1$
+
+ private static final String[] SDCARD_SIZE_UNITS = new String[]
+ {
+ "KB", "MB" //$NON-NLS-1$ //$NON-NLS-2$
+ };
+
+ private Button saveSnapshotButton;
+
+ private Button startFromSnapshotButton;
+
+ private boolean saveSnapshots;
+
+ private boolean startFromSnapshots;
+
+ private Combo abiTypeCombo;
+
+ /**
+ * Creates a PropertiesMainComposite object.
+ *
+ * @param parent the parent composite
+ * @param name the instance name
+ * @param description the instance description
+ * @param emulatorDefId the instance emulator definition id
+ * @param isNameEditable whether the name should be editable or not
+ * @param areOtherFieldsEditable True if the user will be able to edit other data in the composite; false otherwise
+ */
+ public PropertiesMainComposite(Composite parent, String name, String emulatorDefId,
+ String timeout, boolean useVnc, boolean useProxy, boolean useSnapshot,
+ boolean saveSnapshot, boolean startFromSnapshot, IAndroidTarget vmTarget,
+ String vmSkin, String vmPath, String abiType, boolean showName,
+ boolean areVmSettingsEditable, boolean areOtherFieldsEditable)
+
+ {
+ super(parent);
+
+ this.name = name;
+ this.timeout = timeout;
+ this.useVnc = useVnc;
+ this.useProxy = useProxy;
+ this.abiType = abiType;
+ skinId = emuDefMgr.getSkinId(emulatorDefId);
+
+ this.vmTarget = vmTarget;
+ this.vmSkin = vmSkin;
+ this.vmPath = vmPath;
+ this.useSnapshots = useSnapshot;
+ this.saveSnapshots = saveSnapshot;
+ this.startFromSnapshots = startFromSnapshot;
+
+ createUI(showName, areVmSettingsEditable, areOtherFieldsEditable);
+
+ //Set context Help (not available yet)
+ PlatformUI.getWorkbench().getHelpSystem()
+ .setHelp(parent, IAndroidDeviceConstants.MAIN_PAGE_HELP);
+ }
+
+ /**
+ * Creates a PropertiesMainComposite object with instance name editing not allowed.
+ *
+ * @param parent the parent composite
+ * @param name the instance name
+ * @param description the instance description
+ * @param emulatorDefId the instance emulator definition name
+ */
+ public PropertiesMainComposite(Composite parent, String emulatorDefId, String timeout,
+ boolean useVnc, boolean useProxy, boolean useSnapshot, boolean saveSnapshot,
+ boolean startFromSnapshot, IAndroidTarget vmTarget, String vmSkin, String vmPath,
+ String abiType)
+ {
+ this(parent, "", emulatorDefId, timeout, useVnc, useProxy, useSnapshot, saveSnapshot,
+ startFromSnapshot, vmTarget, vmSkin, vmPath, abiType, false, true, true);
+
+ //Set context Help (not available yet)
+ PlatformUI.getWorkbench().getHelpSystem()
+ .setHelp(parent, IAndroidDeviceConstants.MAIN_PAGE_HELP);
+ }
+
+ /**
+ * @param showName
+ * @param areVmSettingsEditable
+ * @param areOtherFieldsEditable
+ */
+ private void createUI(boolean showName, boolean areVmSettingsEditable,
+ boolean areOtherFieldsEditable)
+ {
+ GridData data;
+ Composite mainComposite = this;
+ Collection<Control> otherFields = new HashSet<Control>();
+
+ setMainLayout(2);
+
+ if (showName)
+ {
+ Label nameLabel = new Label(mainComposite, SWT.READ_ONLY);
+ nameLabel.setText(EmulatorNLS.UI_PropertiesMainComposite_NameLabel);
+ data = new GridData(SWT.LEFT, SWT.CENTER, false, false);
+ nameLabel.setLayoutData(data);
+
+ Text nameTextLabel = new Text(mainComposite, SWT.WRAP | SWT.READ_ONLY);
+ nameTextLabel.setText(name);
+ data = new GridData(SWT.FILL, SWT.NULL, true, false);
+ nameTextLabel.setLayoutData(data);
+ }
+
+ /*
+ * (Not) Editable area
+ */
+ if (areVmSettingsEditable)
+ {
+ createEditableVmUI();
+ }
+ else
+ {
+ createNotEditableVmUI();
+ }
+
+ /*
+ * Emulator proxy settings
+ */
+ Group proxyGroup = new Group(mainComposite, SWT.SHADOW_OUT);
+ proxyGroup.setText(EmulatorNLS.PropertiesMainComposite_ProxySettings_GroupTitle);
+ data = new GridData(SWT.FILL, SWT.FILL, true, false, 2, 1);
+ proxyGroup.setLayoutData(data);
+ proxyGroup.setLayout(new GridLayout(3, false));
+
+ final Button proxyChkbox = new Button(proxyGroup, SWT.CHECK);
+ proxyChkbox.setText(EmulatorNLS.PropertiesMainComposite_ProxySettings_CheckboxLabel);
+ data = new GridData(SWT.FILL, SWT.FILL, true, false, 3, 1);
+ proxyChkbox.setSelection(useProxy);
+ proxyChkbox.setLayoutData(data);
+ proxyChkbox.addSelectionListener(new SelectionAdapter()
+ {
+ @Override
+ public void widgetSelected(SelectionEvent e)
+ {
+ useProxy = proxyChkbox.getSelection();
+ notifyCompositeChangeListeners();
+ }
+ });
+
+ Link networkSettings = new Link(proxyGroup, SWT.NONE);
+ networkSettings.setText(EmulatorNLS.PropertiesMainComposite_ProxySettings_LinkToPreference);
+ data = new GridData(SWT.LEFT, SWT.CENTER, false, false, 3, 1);
+ networkSettings.setLayoutData(data);
+
+ networkSettings.addSelectionListener(new SelectionAdapter()
+ {
+ @Override
+ public void widgetSelected(SelectionEvent e)
+ {
+ openNetworkPreferences();
+ }
+ });
+
+ //Snapshot group
+
+ Group snapshotGroup = new Group(mainComposite, SWT.SHADOW_OUT);
+ snapshotGroup.setText(EmulatorNLS.PropertiesMainComposite_SnapshotSettings);
+ data = new GridData(SWT.FILL, SWT.FILL, true, false, 2, 1);
+ snapshotGroup.setLayoutData(data);
+ snapshotGroup.setLayout(new GridLayout(3, false));
+
+ final Button enableSnapshotButton = new Button(snapshotGroup, SWT.CHECK);
+ enableSnapshotButton.setText(EmulatorNLS.PropertiesMainComposite_UseSnapshot);
+ data = new GridData(SWT.FILL, SWT.FILL, true, false, 3, 1);
+ enableSnapshotButton.setLayoutData(data);
+ enableSnapshotButton.setEnabled(areVmSettingsEditable);
+ enableSnapshotButton.setSelection(useSnapshots);
+ enableSnapshotButton.addSelectionListener(new SelectionAdapter()
+ {
+ @Override
+ public void widgetSelected(SelectionEvent e)
+ {
+ useSnapshots = enableSnapshotButton.getSelection();
+ notifyCompositeChangeListeners();
+ startFromSnapshotButton.setEnabled(useSnapshots);
+ saveSnapshotButton.setEnabled(useSnapshots);
+ }
+ });
+
+ startFromSnapshotButton = new Button(snapshotGroup, SWT.CHECK);
+ startFromSnapshotButton.setText(EmulatorNLS.PropertiesMainComposite_startFromSnapshot);
+ data = new GridData(SWT.FILL, SWT.FILL, true, false, 3, 1);
+ startFromSnapshotButton.setLayoutData(data);
+ startFromSnapshotButton.setEnabled(useSnapshots);
+ startFromSnapshotButton.setSelection(startFromSnapshots);
+
+ startFromSnapshotButton.addSelectionListener(new SelectionAdapter()
+ {
+
+ @Override
+ public void widgetSelected(SelectionEvent e)
+ {
+ startFromSnapshots = startFromSnapshotButton.getSelection();
+ notifyCompositeChangeListeners();
+ }
+ });
+
+ saveSnapshotButton = new Button(snapshotGroup, SWT.CHECK);
+ saveSnapshotButton.setText(EmulatorNLS.PropertiesMainComposite_SaveSnapshot);
+ data = new GridData(SWT.FILL, SWT.FILL, true, false, 3, 1);
+ saveSnapshotButton.setLayoutData(data);
+ saveSnapshotButton.setEnabled(useSnapshots);
+ saveSnapshotButton.setSelection(saveSnapshots);
+
+ saveSnapshotButton.addSelectionListener(new SelectionAdapter()
+ {
+
+ @Override
+ public void widgetSelected(SelectionEvent e)
+ {
+ saveSnapshots = saveSnapshotButton.getSelection();
+ notifyCompositeChangeListeners();
+ }
+ });
+
+ otherFields.add(proxyGroup);
+ otherFields.add(proxyChkbox);
+ otherFields.add(networkSettings);
+ otherFields.add(snapshotGroup);
+
+ /*
+ * Emulator Window mode area
+ */
+
+ //Only for windows and linux platforms
+ if (!Platform.getOS().equals(Platform.OS_MACOSX))
+ {
+ Group windowGroup = new Group(mainComposite, SWT.SHADOW_OUT);
+ windowGroup
+ .setText(EmulatorNLS.UI_PropertiesMainComposite_EmulatorWindowMode_GroupTitle);
+ data = new GridData(SWT.FILL, SWT.FILL, true, false, 2, 1);
+ windowGroup.setLayoutData(data);
+ windowGroup.setLayout(new GridLayout(3, false));
+
+ final Button nativeRadio = new Button(windowGroup, SWT.RADIO);
+ nativeRadio
+ .setText(EmulatorNLS.UI_PropertiesMainComposite_EmulatorWindowMode_NativeLabel);
+ nativeRadio.setSelection(!useVnc);
+ data = new GridData(SWT.FILL, SWT.FILL, true, false, 3, 1);
+ nativeRadio.setLayoutData(data);
+ nativeRadio.addSelectionListener(new SelectionAdapter()
+ {
+ @Override
+ public void widgetSelected(SelectionEvent e)
+ {
+ useVnc = !nativeRadio.getSelection();
+ notifyCompositeChangeListeners();
+ }
+ });
+
+ final Button vncRadio = new Button(windowGroup, SWT.RADIO);
+ vncRadio.setText(EmulatorNLS.UI_PropertiesMainComposite_EmulatorWindowMode_VncLabel);
+ vncRadio.setSelection(useVnc);
+ data = new GridData(SWT.FILL, SWT.FILL, true, false, 3, 1);
+ vncRadio.setLayoutData(data);
+
+ otherFields.add(windowGroup);
+ otherFields.add(nativeRadio);
+ otherFields.add(vncRadio);
+ }
+
+ /*
+ * Timeout
+ */
+ Label timeoutLabel = new Label(mainComposite, SWT.READ_ONLY);
+ timeoutLabel.setText(EmulatorNLS.UI_PropertiesMainComposite_TimeoutLabel);
+ data = new GridData(SWT.LEFT, SWT.CENTER, false, false);
+ timeoutLabel.setLayoutData(data);
+
+ final Text timeoutText = new Text(mainComposite, SWT.SINGLE | SWT.BORDER);
+ otherFields.add(timeoutText);
+ timeoutText.setText(timeout);
+ data = new GridData(SWT.FILL, SWT.TOP, true, false);
+ timeoutText.setLayoutData(data);
+
+ timeoutText.addModifyListener(new ModifyListener()
+ {
+ public void modifyText(ModifyEvent e)
+ {
+ timeout = timeoutText.getText().trim();
+ notifyCompositeChangeListeners();
+ }
+ });
+
+ addInstanceListener(otherFields);
+ updateWidgetEnableStatus(areOtherFieldsEditable, otherFields);
+ }
+
+ @SuppressWarnings("unchecked")
+ protected void openNetworkPreferences()
+ {
+ // Makes the network preferences dialog manager
+ PreferenceManager manager = PlatformUI.getWorkbench().getPreferenceManager();
+ IPreferenceNode networkNode = null;
+ for (IPreferenceNode node : (List<IPreferenceNode>) manager
+ .getElements(PreferenceManager.PRE_ORDER))
+ {
+ if (node.getId().equals(ORG_ECLIPSE_UI_NET_NET_PREFERENCES))
+ {
+ networkNode = node;
+ break;
+ }
+ }
+ PreferenceManager prefMan = new PreferenceManager();
+ if (networkNode != null)
+ {
+ prefMan.addToRoot(networkNode);
+ }
+ PreferenceDialog networkPreferencesDialog =
+ new WorkbenchPreferenceDialog(getShell(), prefMan);
+ networkPreferencesDialog.create();
+ networkPreferencesDialog.open();
+ }
+
+ /**
+ * Creates the not editable UI for the VM settings.
+ */
+ private void createNotEditableVmUI()
+ {
+ GridData data;
+ Composite mainComposite = this;
+
+ Label tagetLabel = new Label(mainComposite, SWT.READ_ONLY);
+ tagetLabel.setText(EmulatorNLS.UI_PropertiesMainComposite_TargetLabel);
+ data = new GridData(SWT.LEFT, SWT.CENTER, false, false);
+ tagetLabel.setLayoutData(data);
+
+ final Text targetText = new Text(mainComposite, SWT.WRAP | SWT.READ_ONLY);
+ targetText.setText(vmTarget.getName());
+ data = new GridData(SWT.FILL, SWT.TOP, true, false);
+ targetText.setLayoutData(data);
+
+ Label abiTypeLabel = new Label(mainComposite, SWT.READ_ONLY);
+ abiTypeLabel.setText(EmulatorNLS.PropertiesMainComposite_ABITypeLabel);
+ data = new GridData(SWT.LEFT, SWT.CENTER, false, false);
+ abiTypeLabel.setLayoutData(data);
+
+ final Text abiTypeText = new Text(mainComposite, SWT.WRAP | SWT.READ_ONLY);
+ abiTypeText.setText(AvdInfo.getPrettyAbiType(abiType));
+ data = new GridData(SWT.FILL, SWT.TOP, true, false);
+ abiTypeText.setLayoutData(data);
+
+ Label skinLabel = new Label(mainComposite, SWT.READ_ONLY);
+ skinLabel.setText(EmulatorNLS.UI_PropertiesMainComposite_SkinLabel);
+ data = new GridData(SWT.LEFT, SWT.CENTER, false, false);
+ skinLabel.setLayoutData(data);
+
+ final Text vmSkinText = new Text(mainComposite, SWT.WRAP | SWT.READ_ONLY);
+ vmSkinText.setText(vmSkin);
+ data = new GridData(SWT.FILL, SWT.TOP, true, false);
+ vmSkinText.setLayoutData(data);
+
+ Label pathLabel = new Label(mainComposite, SWT.READ_ONLY);
+ pathLabel.setText(EmulatorNLS.UI_PropertiesMainComposite_PathLabel);
+ data = new GridData(SWT.LEFT, SWT.CENTER, false, false);
+ pathLabel.setLayoutData(data);
+
+ final Text pathText = new Text(mainComposite, SWT.WRAP | SWT.READ_ONLY);
+ pathText.setText(vmPath);
+ data = new GridData(SWT.FILL, SWT.TOP, true, false);
+ data.widthHint = 100;
+ pathText.setLayoutData(data);
+
+ /*
+ * SD Card info
+ */
+ AvdInfo vmInfo = SdkUtils.getValidVm(name);
+ Properties configFile = new Properties();
+ BufferedReader input = null;
+ try
+ {
+
+ // it's necessary to read the file instead of load the Properties file because
+ // there are "\" in the path, which are removed by the Properties class load method, since
+ // they mean line break in a properties file
+ input = new BufferedReader(new FileReader(vmInfo.getConfigFile()));
+
+ String line = null;
+ String[] property = null;
+ while ((line = input.readLine()) != null)
+ {
+ property = line.split("="); //$NON-NLS-1$
+ if ((property[0] != null) && (property[1] != null))
+ {
+ if ((!property[0].equals("")) && (!property[1].equals(""))) //$NON-NLS-1$ //$NON-NLS-2$
+ {
+ configFile.put(property[0], property[1]);
+ }
+ }
+ }
+
+ if (configFile.getProperty(IDevicePropertiesConstants.configSDCardPath) != null)
+ {
+
+ Label sdCardPathLabel = new Label(mainComposite, SWT.READ_ONLY);
+ sdCardPathLabel.setText(EmulatorNLS.UI_PropertiesMainComposite_SDCardPathLabel);
+ data = new GridData(SWT.LEFT, SWT.CENTER, false, false);
+ sdCardPathLabel.setLayoutData(data);
+
+ final Text sdCardPathText = new Text(mainComposite, SWT.WRAP | SWT.READ_ONLY);
+ sdCardPathText.setText(configFile
+ .getProperty(IDevicePropertiesConstants.configSDCardPath));
+ data = new GridData(SWT.FILL, SWT.TOP, true, false);
+ data.widthHint = 100;
+ sdCardPathText.setLayoutData(data);
+
+ }
+
+ if (configFile.getProperty(IDevicePropertiesConstants.configSDCardSize) != null)
+ {
+ Label sdCardPathLabel = new Label(mainComposite, SWT.READ_ONLY);
+ sdCardPathLabel.setText(EmulatorNLS.UI_PropertiesMainComposite_SDCardSizeLabel);
+ data = new GridData(SWT.LEFT, SWT.CENTER, false, false);
+ sdCardPathLabel.setLayoutData(data);
+
+ final Text sdCardPathText = new Text(mainComposite, SWT.WRAP | SWT.READ_ONLY);
+ sdCardPathText.setText(configFile
+ .getProperty(IDevicePropertiesConstants.configSDCardSize));
+ data = new GridData(SWT.FILL, SWT.TOP, true, false);
+ sdCardPathText.setLayoutData(data);
+ }
+
+ }
+ catch (FileNotFoundException e)
+ {
+ StudioLogger.error("Could not find config file for AVD: " + name); //$NON-NLS-1$
+ }
+ catch (IOException e)
+ {
+ StudioLogger.error("Could not read config file for AVD: " + name); //$NON-NLS-1$
+ }
+ finally
+ {
+ try
+ {
+ if (input != null)
+ {
+ input.close();
+ }
+ }
+ catch (IOException e)
+ {
+ StudioLogger.error("Could not close input stream: ", e.getMessage()); //$NON-NLS-1$
+ }
+ }
+ }
+
+ /**
+ * Creates the editable UI for the VM settings.
+ */
+ private void createEditableVmUI()
+ {
+ GridData data;
+ Composite mainComposite = this;
+
+ Label tagetLabel = new Label(mainComposite, SWT.READ_ONLY);
+ tagetLabel.setText(EmulatorNLS.UI_PropertiesMainComposite_TargetLabel);
+ data = new GridData(SWT.LEFT, SWT.CENTER, false, false);
+ tagetLabel.setLayoutData(data);
+
+ final Combo targetCombo = new Combo(mainComposite, SWT.READ_ONLY);
+ populateTargetCombo(targetCombo);
+ data = new GridData(SWT.FILL, SWT.TOP, true, false, 1, 1);
+ targetCombo.setLayoutData(data);
+
+ Label skinLabel = new Label(mainComposite, SWT.READ_ONLY);
+ skinLabel.setText(EmulatorNLS.UI_PropertiesMainComposite_SkinLabel);
+ data = new GridData(SWT.LEFT, SWT.CENTER, false, false);
+ skinLabel.setLayoutData(data);
+
+ final Combo vmSkinCombo = new Combo(mainComposite, SWT.READ_ONLY);
+ populateSkinCombo(vmSkinCombo);
+ data = new GridData(SWT.FILL, SWT.TOP, true, false, 1, 1);
+ vmSkinCombo.setLayoutData(data);
+
+ vmSkinCombo.addSelectionListener(new SelectionAdapter()
+ {
+ @Override
+ public void widgetSelected(SelectionEvent e)
+ {
+ if ((vmSkinCombo != null) && !"".equals(vmSkinCombo.getText())) //$NON-NLS-1$
+ {
+ vmSkin = (String) vmSkinCombo.getData(vmSkinCombo.getText());
+
+ }
+ else
+ {
+ vmSkin = ""; //$NON-NLS-1$
+ }
+ notifyCompositeChangeListeners();
+
+ }
+ });
+
+ Label abiTypeLabel = new Label(mainComposite, SWT.READ_ONLY);
+ abiTypeLabel.setText(EmulatorNLS.PropertiesMainComposite_ABITypeLabel);
+ data = new GridData(SWT.LEFT, SWT.CENTER, false, false);
+ abiTypeLabel.setLayoutData(data);
+
+ abiTypeCombo = new Combo(mainComposite, SWT.READ_ONLY);
+ populateAbiTypeCombo(abiTypeCombo);
+ data = new GridData(SWT.FILL, SWT.TOP, true, false, 1, 1);
+ abiTypeCombo.setLayoutData(data);
+
+ abiTypeCombo.addSelectionListener(new SelectionAdapter()
+ {
+ @Override
+ public void widgetSelected(SelectionEvent e)
+ {
+ if ((vmSkinCombo != null) && !"".equals(vmSkinCombo.getText())) //$NON-NLS-1$
+ {
+ abiType = (String) abiTypeCombo.getData(abiTypeCombo.getText());
+
+ }
+ else
+ {
+ abiType = SdkConstants.ABI_ARMEABI;
+ }
+ notifyCompositeChangeListeners();
+
+ }
+ });
+
+ targetCombo.addSelectionListener(new SelectionAdapter()
+ {
+ @Override
+ public void widgetSelected(SelectionEvent e)
+ {
+ IAndroidTarget newTarget = null;
+ if ((targetCombo != null) && !"".equals(targetCombo.getText())) //$NON-NLS-1$
+ {
+ newTarget = (IAndroidTarget) targetCombo.getData(targetCombo.getText());
+ }
+
+ if (newTarget != vmTarget)
+ {
+ vmTarget = newTarget;
+ populateSkinCombo(vmSkinCombo);
+ populateAbiTypeCombo(abiTypeCombo);
+ notifyCompositeChangeListeners();
+ }
+ }
+
+ });
+
+ Group vmPathGroup = new Group(mainComposite, SWT.SHADOW_OUT);
+ vmPathGroup.setText(EmulatorNLS.UI_PropertiesMainComposite_PathGroupTitle);
+ data = new GridData(SWT.FILL, SWT.FILL, true, false, 2, 1);
+ vmPathGroup.setLayoutData(data);
+ vmPathGroup.setLayout(new GridLayout(3, false));
+
+ usingDefaultVmPath = vmPath.equals(IDevicePropertiesConstants.defaultVmPath);
+
+ final Button vmPathCheckbox = new Button(vmPathGroup, SWT.CHECK);
+ vmPathCheckbox.setText(EmulatorNLS.UI_PropertiesMainComposite_UseDefaultPath);
+ vmPathCheckbox.setSelection(usingDefaultVmPath);
+ data = new GridData(SWT.FILL, SWT.FILL, true, false, 3, 1);
+ vmPathCheckbox.setLayoutData(data);
+
+ Label pathLabel = new Label(vmPathGroup, SWT.READ_ONLY);
+ pathLabel.setText(EmulatorNLS.UI_PropertiesMainComposite_PathLabel);
+ data = new GridData(SWT.LEFT, SWT.CENTER, false, false);
+ pathLabel.setLayoutData(data);
+
+ final Text pathText = new Text(vmPathGroup, SWT.SINGLE | SWT.BORDER);
+ pathText.setText(vmPath);
+ pathText.setEnabled(!usingDefaultVmPath);
+ data = new GridData(SWT.FILL, SWT.TOP, true, false);
+ data.widthHint = 100;
+ pathText.setLayoutData(data);
+
+ final Button pathBrowseButton = new Button(vmPathGroup, SWT.PUSH);
+ pathBrowseButton.setText(EmulatorNLS.UI_General_BrowseButtonLabel);
+ pathBrowseButton.setEnabled(!usingDefaultVmPath);
+ data = new GridData(SWT.FILL, SWT.FILL, false, false);
+ pathBrowseButton.setLayoutData(data);
+
+ vmPathCheckbox.addSelectionListener(new SelectionAdapter()
+ {
+ @Override
+ public void widgetSelected(SelectionEvent e)
+ {
+ usingDefaultVmPath = vmPathCheckbox.getSelection();
+ pathText.setEnabled(!usingDefaultVmPath);
+ pathBrowseButton.setEnabled(!usingDefaultVmPath);
+ if (usingDefaultVmPath)
+ {
+ vmPath = IDevicePropertiesConstants.defaultVmPath;
+ }
+ notifyCompositeChangeListeners();
+ }
+ });
+
+ pathText.addModifyListener(new ModifyListener()
+ {
+ public void modifyText(ModifyEvent e)
+ {
+ vmPath = pathText.getText().trim();
+ notifyCompositeChangeListeners();
+ }
+ });
+
+ pathBrowseButton.addSelectionListener(new SelectionAdapter()
+ {
+ @Override
+ public void widgetSelected(SelectionEvent e)
+ {
+ DirectoryDialog dirDialog = new DirectoryDialog(getShell(), SWT.OPEN);
+
+ if (!vmPath.trim().equals("")) //$NON-NLS-1$
+ {
+ dirDialog.setFilterPath(vmPath);
+ }
+ else
+ {
+ dirDialog.setFilterPath("/"); //$NON-NLS-1$
+ }
+
+ dirDialog.setText(EmulatorNLS.UI_PropertiesMainComposite_PathGroupTitle);
+
+ String vmPath = dirDialog.open();
+
+ if (vmPath != null)
+ {
+ pathText.setText(vmPath);
+ notifyCompositeChangeListeners();
+ }
+ }
+ });
+
+ /*
+ * SD Card Area
+ */
+
+ sdCardType = SDCARD_TYPE_NONE;
+
+ Group sdCardGroup = new Group(mainComposite, SWT.SHADOW_OUT);
+ sdCardGroup.setText(EmulatorNLS.UI_PropertiesMainComposite_SDCardLabel);
+ data = new GridData(SWT.FILL, SWT.FILL, true, false, 2, 1);
+ sdCardGroup.setLayoutData(data);
+ sdCardGroup.setLayout(new GridLayout(3, false));
+
+ // none
+ final Button noneSDCardCheckbox = new Button(sdCardGroup, SWT.RADIO);
+ noneSDCardCheckbox.setText(EmulatorNLS.UI_PropertiesMainComposite_SDCardNoneLabel);
+ noneSDCardCheckbox.setSelection(true);
+ data = new GridData(SWT.FILL, SWT.FILL, true, false, 3, 1);
+ noneSDCardCheckbox.setLayoutData(data);
+ noneSDCardCheckbox.addSelectionListener(new SelectionAdapter()
+ {
+ @Override
+ public void widgetSelected(SelectionEvent e)
+ {
+ sdCardType = SDCARD_TYPE_NONE;
+ sdCardValue = ""; //$NON-NLS-1$
+ notifyCompositeChangeListeners();
+ }
+ });
+
+ // existing
+ final Button existingSDCardCheckbox = new Button(sdCardGroup, SWT.RADIO);
+ existingSDCardCheckbox.setText(EmulatorNLS.UI_PropertiesMainComposite_SDCardExistingLabel);
+ final Text existingSDCardPath = new Text(sdCardGroup, SWT.SINGLE | SWT.BORDER);
+ data = new GridData(SWT.FILL, SWT.TOP, true, false);
+ data.widthHint = 100;
+ existingSDCardPath.setLayoutData(data);
+ existingSDCardPath.setEnabled(false);
+ final Button existingSDCardButton = new Button(sdCardGroup, SWT.PUSH);
+ existingSDCardButton.setText(EmulatorNLS.UI_General_BrowseButtonLabel);
+ data = new GridData(SWT.FILL, SWT.FILL, false, false);
+ existingSDCardButton.setLayoutData(data);
+ existingSDCardButton.setEnabled(false);
+
+ existingSDCardCheckbox.addSelectionListener(new SelectionAdapter()
+ {
+ @Override
+ public void widgetSelected(SelectionEvent e)
+ {
+ sdCardType = SDCARD_TYPE_PATH;
+ existingSDCardPath.setEnabled(existingSDCardCheckbox.getSelection());
+ existingSDCardButton.setEnabled(existingSDCardCheckbox.getSelection());
+ sdCardValue = existingSDCardPath.getText();
+ notifyCompositeChangeListeners();
+ }
+ });
+
+ existingSDCardPath.addModifyListener(new ModifyListener()
+ {
+ public void modifyText(ModifyEvent e)
+ {
+ sdCardValue = existingSDCardPath.getText();
+ notifyCompositeChangeListeners();
+ }
+ });
+
+ existingSDCardButton.addSelectionListener(new SelectionAdapter()
+ {
+ @Override
+ public void widgetSelected(SelectionEvent e)
+ {
+ String selectedPath = null;
+
+ FileDialog fileDialog = new FileDialog(getShell(), SWT.OPEN);
+ String[] filterExtensions =
+ {
+ "*" + SDCARD_PATH_EXTENSION //$NON-NLS-1$
+ };
+
+ fileDialog.setFilterExtensions(filterExtensions);
+ selectedPath = fileDialog.open();
+
+ if (selectedPath != null)
+ {
+ existingSDCardPath.setText(selectedPath);
+ }
+ }
+ });
+
+ // new
+ final Button newSDCardCheckbox = new Button(sdCardGroup, SWT.RADIO);
+ newSDCardCheckbox.setText(EmulatorNLS.UI_PropertiesMainComposite_SDCardNewLabel);
+ final Text newSDCardSize = new Text(sdCardGroup, SWT.SINGLE | SWT.BORDER);
+ data = new GridData(SWT.FILL, SWT.TOP, true, false);
+ data.widthHint = 100;
+ newSDCardSize.setLayoutData(data);
+ newSDCardSize.setEnabled(false);
+ final Combo newSDCardUnit = new Combo(sdCardGroup, SWT.READ_ONLY);
+ for (String unit : SDCARD_SIZE_UNITS)
+ {
+ newSDCardUnit.add(unit);
+ }
+ newSDCardUnit.select(0);
+ newSDCardUnit.setEnabled(false);
+ data = new GridData(SWT.FILL, SWT.FILL, false, false);
+ newSDCardUnit.setLayoutData(data);
+
+ newSDCardCheckbox.addSelectionListener(new SelectionAdapter()
+ {
+ @Override
+ public void widgetSelected(SelectionEvent e)
+ {
+ sdCardType = SDCARD_TYPE_SIZE;
+ sdCardValue = newSDCardSize.getText();
+ newSDCardSize.setEnabled(newSDCardCheckbox.getSelection());
+ newSDCardUnit.setEnabled(newSDCardCheckbox.getSelection());
+ sdCardValue = newSDCardSize.getText() + newSDCardUnit.getText().charAt(0);
+ notifyCompositeChangeListeners();
+ }
+ });
+
+ ModifyListener newSDCardListener = new ModifyListener()
+ {
+ public void modifyText(ModifyEvent e)
+ {
+ sdCardValue = newSDCardSize.getText() + newSDCardUnit.getText().charAt(0);
+ notifyCompositeChangeListeners();
+ }
+ };
+
+ newSDCardSize.addModifyListener(newSDCardListener);
+ newSDCardUnit.addModifyListener(newSDCardListener);
+ }
+
+ private void populateAbiTypeCombo(Combo abiTypeCombo)
+ {
+ // System Images represents the ABI types
+ ISystemImage[] images = vmTarget.getSystemImages();
+
+ // in case no images are found, get try its parent
+ if ((images == null) || ((images.length == 0) && !vmTarget.isPlatform()))
+ {
+ images = vmTarget.getParent().getSystemImages();
+ }
+
+ // always clean abi combo since it will be reloaded
+ abiTypeCombo.removeAll();
+
+ int i = 0;
+ if ((images != null) && (images.length > 0))
+ {
+ for (ISystemImage image : images)
+ {
+ String prettyAbiName = AvdInfo.getPrettyAbiType(image.getAbiType());
+ abiTypeCombo.add(prettyAbiName);
+ abiTypeCombo.setData(prettyAbiName, image.getAbiType());
+ if (image.getAbiType().equals(abiType))
+ {
+ abiTypeCombo.select(i);
+ }
+ i++;
+ }
+
+ if (abiTypeCombo.getSelectionIndex() == -1)
+ {
+ abiTypeCombo.select(0);
+ abiType = (String) abiTypeCombo.getData(abiTypeCombo.getItem(0));
+ }
+ }
+
+ }
+
+ /**
+ * Populate VM Target combo box.
+ *
+ * @param targetCombo
+ */
+ private void populateTargetCombo(Combo targetCombo)
+ {
+ IAndroidTarget[] targets = SdkUtils.getAllTargets();
+ if ((targets != null) && (targets.length > 0))
+ {
+ for (int i = 0; i < targets.length; i++)
+ {
+ String label =
+ targets[i].isPlatform() ? targets[i].getName() : targets[i].getName()
+ + " (" + targets[i].getParent().getName() + ")"; //$NON-NLS-1$ //$NON-NLS-2$
+ targetCombo.add(label);
+ targetCombo.setData(label, targets[i]);
+
+ if (targets[i].getName().equals(vmTarget.getName()))
+ {
+ targetCombo.select(i);
+ }
+ }
+ }
+ //Set context Help (not available yet)
+ PlatformUI.getWorkbench().getHelpSystem()
+ .setHelp(this, IAndroidDeviceConstants.MAIN_PAGE_HELP);
+ }
+
+ /**
+ * Populate VM Skin combo box.
+ *
+ * @param skinCombo
+ */
+ private void populateSkinCombo(Combo skinCombo)
+ {
+ skinCombo.removeAll();
+ skinCombo.clearSelection();
+
+ if (vmTarget != null)
+ {
+ String[] skins = vmTarget.getSkins();
+ String defaultSkin = vmTarget.getDefaultSkin();
+
+ for (int i = 0; i < skins.length; i++)
+ {
+ skinCombo.add(skins[i]);
+ skinCombo.setData(skins[i], skins[i]);
+
+ if (skins[i].equals(defaultSkin))
+ {
+ skinCombo.select(i);
+ vmSkin = defaultSkin;
+ }
+ }
+
+ // if there is no selection, select the first
+ if (skinCombo.getSelectionIndex() < 0)
+ {
+ if (skinCombo.getItemCount() > 0)
+ {
+ skinCombo.select(0);
+ vmSkin = skinCombo.getItem(0);
+ }
+ }
+
+ skinCombo.setEnabled(true);
+ }
+ else
+ {
+ skinCombo.setEnabled(false);
+ }
+
+ //Set context Help (not available yet)
+ PlatformUI.getWorkbench().getHelpSystem()
+ .setHelp(this, IAndroidDeviceConstants.MAIN_PAGE_HELP);
+ }
+
+ public String getSkinId()
+ {
+ return skinId;
+ }
+
+ /**
+ * Retrieves the timeout.
+ *
+ * @return the timeout
+ */
+ public String getTimeout()
+ {
+ return timeout;
+ }
+
+ public String getUseVnc()
+ {
+ return (useVnc ? Boolean.TRUE.toString() : Boolean.FALSE.toString()); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+
+ public String getUseProxy()
+ {
+ return (useProxy ? Boolean.TRUE.toString() : Boolean.FALSE.toString()); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+
+ /**
+ * Retrieves the error message associated to this composites current state.
+ * The order of precedence of error is the same as the fields displayed on the
+ * UI, which means errors on fields drawn first are shown with a higher precedence
+ * than the errors of fields drawn last.
+ * The instance description field is the only non required field.
+ *
+ * @return the error message, or <code>null</code> if there are no errors
+ */
+ @Override
+ public String getErrorMessage()
+ {
+ String errorMessage = null;
+
+ // VM Settings Check
+ if ("".equals(vmTarget)) //$NON-NLS-1$
+ {
+ //Check if Target isn't empty
+ errorMessage = EmulatorNLS.ERR_PropertiesMainComposite_VmTargetEmpty;
+ }
+ else if ("".equals(vmSkin)) //$NON-NLS-1$
+ {
+ //Check if Skin isn't empty
+ errorMessage = EmulatorNLS.ERR_PropertiesMainComposite_VmSkinEmpty;
+ }
+ else
+ {
+ if (!usingDefaultVmPath)
+ {
+ //Check if Path is valid ("" is valid too)
+ File vmPathLocation = new File(vmPath);
+ if ((!vmPathLocation.exists()) || (!vmPathLocation.isDirectory()))
+ {
+ errorMessage = EmulatorNLS.ERR_PropertiesMainComposite_VmPathInvalid;
+ }
+
+ }
+ }
+
+ //ABI Type
+ if (errorMessage == null)
+ {
+ if ((abiTypeCombo != null)
+ && ((abiTypeCombo.getItemCount() == 0) || (abiTypeCombo.getSelectionIndex() == -1))) //$NON-NLS-1$
+ {
+ //no item available or not ABI selected
+ errorMessage = EmulatorNLS.ERR_PropertiesMainComposite_ABINotAvailable;
+ }
+ }
+
+ // SD Card
+ if (errorMessage == null)
+ {
+ if (sdCardType.equalsIgnoreCase(SDCARD_TYPE_PATH))
+ {
+ if (sdCardValue == null) //$NON-NLS-1$
+ {
+ errorMessage = EmulatorNLS.ERR_PropertiesMainComposite_MissingSDCardPath;
+ }
+ else if (!isValidSDCard(sdCardValue))
+ {
+ errorMessage = EmulatorNLS.ERR_PropertiesMainComposite_SDCardPathIsNotValid;
+ }
+ }
+ else if (sdCardType.equalsIgnoreCase(SDCARD_TYPE_SIZE))
+ {
+
+ int sdcardSize = -1;
+ String unit =
+ sdCardValue.length() > 0 ? sdCardValue.substring(sdCardValue.length() - 1)
+ .toLowerCase() : "k"; //$NON-NLS-1$
+
+ if ((sdCardValue == null) || (sdCardValue.equals(""))) //$NON-NLS-1$
+ {
+ errorMessage = EmulatorNLS.ERR_PropertiesMainComposite_MissingSDCardSize;
+ }
+ else
+ {
+
+ try
+ {
+ sdcardSize =
+ Integer.parseInt(sdCardValue.substring(0, sdCardValue.length() - 1));
+ }
+ catch (NumberFormatException e)
+ {
+ //do nothing
+ }
+
+ if ((unit.equals("m") && (sdcardSize < 9)) //$NON-NLS-1$
+ || (unit.equals("k") && (sdcardSize < (9 * 1024)))) //$NON-NLS-1$
+ {
+ errorMessage =
+ EmulatorNLS.ERR_PropertiesMainComposite_SDCardSizeIsNotPositiveInteger;
+ }
+ }
+ }
+ }
+
+ //Timeout
+ if (errorMessage == null)
+ {
+ if (timeout.equals("")) //$NON-NLS-1$
+ {
+ errorMessage = EmulatorNLS.ERR_PropertiesMainComposite_MissingTimeoutValue;
+ }
+ else
+ {
+ if (!isPositiveInteger(timeout))
+ {
+ errorMessage =
+ EmulatorNLS.ERR_PropertiesMainComposite_TimeoutValueIsNotPositiveInteger;
+ }
+ }
+ }
+
+ return errorMessage;
+ }
+
+ /**
+ * Check if a string is a valid SD Card Path
+ *
+ * @param text the string to be analyzed
+ * @return true if the string is a valid SD Card path, false otherwise
+ */
+ private boolean isValidSDCard(String text)
+ {
+ boolean result = true;
+
+ File file = new File(text);
+
+ if ((!file.exists()) || (file.isDirectory())
+ || (!SDCARD_PATH_EXTENSION.equals("." + (new Path(text)).getFileExtension()))) //$NON-NLS-1$
+ {
+ result = false;
+ }
+
+ return result;
+ }
+
+ /**
+ * Check if a string is a positive integer
+ *
+ * @param text the string to be analyzed
+ * @return true if the string is a positive integer, false otherwise
+ */
+ private boolean isPositiveInteger(String text)
+ {
+ int intValue = 0;
+ boolean isPositive = true;
+
+ try
+ {
+ intValue = Integer.parseInt(text);
+ }
+ catch (NumberFormatException e)
+ {
+ isPositive = false;
+ }
+
+ if (intValue <= 0)
+ {
+ isPositive = false;
+ }
+
+ return isPositive;
+ }
+
+ /**
+ * Retrieves the VM Target.
+ *
+ * @return the vmTarget
+ */
+ public IAndroidTarget getVmTarget()
+ {
+ return vmTarget;
+ }
+
+ /**
+ * Retrieves the Abi Type.
+ *
+ * @return the VM Abi Type
+ */
+ public String getAbiType()
+ {
+ return abiType != null ? abiType : SdkConstants.ABI_ARMEABI;
+ }
+
+ /**
+ * Retrieves the VM Skin.
+ *
+ * @return the vmSkin
+ */
+ public String getVmSkin()
+ {
+ return vmSkin;
+ }
+
+ /**
+ * Retrieves the VM Path.
+ *
+ * @return the vmPath
+ */
+ public String getVmPath()
+ {
+ return vmPath + File.separator + name + IDevicePropertiesConstants.defaultVmFolderSuffix;
+ }
+
+ /**
+ * Retrieves the SD Card info
+ *
+ * @return the sdCard
+ */
+ public String getSDCard()
+ {
+ return sdCardValue;
+ }
+
+ /**
+ * Set the name of the AVD
+ * @param name: a not null String with the name of the AVD
+ */
+ public void setName(String name)
+ {
+ this.name = name == null ? "" : name; //$NON-NLS-1$
+ }
+
+ /**
+ * @return
+ */
+ public String getUseSnapshot()
+ {
+ return (useSnapshots ? Boolean.TRUE.toString() : Boolean.FALSE.toString());
+ }
+
+ /**
+ * @return
+ */
+ public String getstartFromSnapshot()
+ {
+ return (startFromSnapshots ? Boolean.TRUE.toString() : Boolean.FALSE.toString());
+ }
+
+ /**
+ * @return
+ */
+ public String getSaveSnapshot()
+ {
+ return (saveSnapshots ? Boolean.TRUE.toString() : Boolean.FALSE.toString());
+ }
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/ui/StartupOptionsComposite.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/ui/StartupOptionsComposite.java
new file mode 100644
index 0000000..b6f3f9c
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/ui/StartupOptionsComposite.java
@@ -0,0 +1,435 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.emulator.device.ui;
+
+import java.util.List;
+
+import org.eclipse.core.runtime.Status;
+import org.eclipse.jface.dialogs.Dialog;
+import org.eclipse.jface.fieldassist.ControlDecoration;
+import org.eclipse.jface.fieldassist.FieldDecoration;
+import org.eclipse.jface.fieldassist.FieldDecorationRegistry;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.ModifyEvent;
+import org.eclipse.swt.events.ModifyListener;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Combo;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.DirectoryDialog;
+import org.eclipse.swt.widgets.FileDialog;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Layout;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.TabFolder;
+import org.eclipse.swt.widgets.TabItem;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.ui.PlatformUI;
+
+import com.motorola.studio.android.emulator.core.skin.IAndroidSkin;
+import com.motorola.studio.android.emulator.device.IAndroidDeviceConstants;
+import com.motorola.studio.android.emulator.device.instance.options.IStartupOptionsConstants;
+import com.motorola.studio.android.emulator.device.instance.options.StartupOption;
+import com.motorola.studio.android.emulator.device.instance.options.StartupOptionsGroup;
+import com.motorola.studio.android.emulator.device.instance.options.StartupOptionsMgt;
+import com.motorola.studio.android.emulator.i18n.EmulatorNLS;
+
+/**
+ * DESCRIPTION:
+ * <br>
+ * This class implements the UI for showing all Android Emulator Device Instance startup options information.
+ * <br>
+ * It extends the AbstractPropertiesComposite so as to use its common functionalities.
+ * <br>
+ * RESPONSIBILITY:
+ * <br>
+ * - Show Android Emulator Device Instance main information on the UI
+ * <br>
+ * COLABORATORS:
+ * <br>
+ * AbstractPropertiesComposite: extends this class
+ * <br>
+ * USAGE:
+ * <br>
+ * This class should be added as a regular composite whenever startup options information on Android Emulator
+ * Device Instance is necessary to be shown and edited on the UI.
+ */
+public class StartupOptionsComposite extends AbstractPropertiesComposite implements
+ IStartupOptionsConstants
+{
+ // The widget which displays the current command line used to pass the startup options
+ private Text commandLine;
+
+ private final IAndroidSkin skin;
+
+ private final int TABFOLDER_HEIGHT_HINT = 350;
+
+ private boolean canCalculateScale = true;
+
+ /**
+ * Creates a StartupOptionsComposite object.
+ *
+ * @param parent the parent composite
+ * @param canCalculateScale
+ */
+ public StartupOptionsComposite(Composite parent, String startupOptions, IAndroidSkin skin,
+ boolean canCalculateScale)
+ {
+ super(parent);
+
+ this.skin = skin;
+ this.canCalculateScale = canCalculateScale;
+ StartupOptionsMgt.loadFromCommandLine(startupOptions);
+ createUI();
+
+ // Set context Help
+ PlatformUI.getWorkbench().getHelpSystem()
+ .setHelp(parent, IAndroidDeviceConstants.STARTUP_OPTIONS_HELP);
+ }
+
+ /**
+ * Create widgets for startup options
+ */
+ private void createUI()
+ {
+
+ Composite mainComposite = this;
+ Layout mainLayout = new GridLayout();
+ mainComposite.setLayout(mainLayout);
+
+ // list of startup options groups
+ List<StartupOptionsGroup> startupOptionsGroupsList =
+ StartupOptionsMgt.getStartupOptionsGroupsList();
+
+ // list of startup options in each group
+ List<StartupOption> startupOptions = null;
+
+ // Create Tab Folder
+ final TabFolder tabFolder = new TabFolder(mainComposite, SWT.NULL);
+ GridData data = new GridData(SWT.FILL, SWT.FILL, true, false);
+ data.heightHint = TABFOLDER_HEIGHT_HINT;
+ tabFolder.setLayoutData(data);
+
+ /*
+ * Iterate through Startup Groups
+ */
+ for (StartupOptionsGroup startupOptionGroup : startupOptionsGroupsList)
+ {
+
+ // Create Tab Item
+ TabItem tabItem = new TabItem(tabFolder, SWT.NULL);
+ tabItem.setText(startupOptionGroup.getTitle());
+ Composite group = new Composite(tabFolder, SWT.NULL);
+ group.setLayout(new GridLayout(3, false));
+ group.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
+ tabItem.setControl(group);
+
+ // get startup options in this group
+ startupOptions = startupOptionGroup.getStartupOptions();
+
+ /*
+ * Iterate through Startup Options in this group
+ */
+ for (final StartupOption startupOption : startupOptions)
+ {
+
+ // create a checkbox for each startup option
+ Button checkbox = new Button(group, SWT.CHECK);
+ checkbox.setSelection(startupOption.isChecked());
+ checkbox.setText(startupOption.getUserFriendlyName());
+ checkbox.setToolTipText(startupOption.getName() + ": " //$NON-NLS-1$
+ + startupOption.getDescription());
+ startupOption.setCheckedWidget(checkbox);
+ checkbox.addSelectionListener(new SelectionAdapter()
+ {
+ @Override
+ public void widgetSelected(SelectionEvent e)
+ {
+ boolean checkedStatus = ((Button) e.widget).getSelection();
+ startupOption.setChecked(checkedStatus);
+ notifyCompositeChangeListeners();
+ }
+ });
+ GridData checkboxData = new GridData(SWT.NULL, SWT.FILL, false, false);
+ checkbox.setLayoutData(checkboxData);
+
+ // Create input fields depending on the startup option type
+ switch (startupOption.getType())
+ {
+ case TYPE_NONE:
+ // extend checkbox area along the line
+ checkboxData.widthHint = SWT.DEFAULT;
+ checkboxData.horizontalSpan = 3;
+ checkbox.setLayoutData(checkboxData);
+ break;
+
+ case TYPE_TEXT:
+ case TYPE_NUMBER:
+ createWidgetsForTextOrNumberType(group, startupOption);
+ break;
+
+ case TYPE_PATH:
+ createWidgetsForPathType(group, startupOption);
+ break;
+
+ default:
+ // none
+
+ }
+ }
+ }
+
+ /*
+ * Command Line area
+ */
+ Composite commandLineArea = new Composite(mainComposite, SWT.NONE); // composite
+ commandLineArea.setLayout(new GridLayout(2, false));
+ data = new GridData(SWT.FILL, SWT.FILL, true, true);
+ commandLineArea.setLayoutData(data);
+
+ Label commandLineLabel = new Label(commandLineArea, SWT.NONE); // label
+ commandLineLabel.setText(""); //$NON-NLS-1$
+ data = new GridData(SWT.FILL, SWT.FILL, false, true);
+ commandLineLabel.setLayoutData(data);
+
+ commandLine = new Text(commandLineArea, SWT.MULTI | SWT.WRAP | SWT.BORDER | SWT.V_SCROLL); // text
+ data = new GridData(SWT.FILL, SWT.FILL, true, true);
+ commandLineArea.pack();
+ data.widthHint = commandLineArea.getBounds().width - commandLineLabel.getBounds().width;
+ data.heightHint = commandLineArea.getBounds().height;
+ commandLine.setLayoutData(data);
+ commandLine.setText(StartupOptionsMgt.getParamList());
+ commandLine.setEditable(false);
+ }
+
+ /**
+ * Create widgets to enable user to input data for fields of text or number type
+ *
+ * @param parent composite where the widgets will be attached to
+ * @param startupOption the corresponding startup option
+ */
+ private void createWidgetsForTextOrNumberType(final Composite parent,
+ final StartupOption startupOption)
+ {
+ // create input text with calc button
+ if (startupOption.getName().equals(SCALE))
+ {
+ final Text inputText = new Text(parent, SWT.SINGLE | SWT.BORDER);
+ inputText.setText(startupOption.getValue());
+ startupOption.setValueWidget(inputText);
+ inputText.setLayoutData(new GridData(SWT.FILL, SWT.NULL, true, false));
+ inputText.addModifyListener(new ModifyListener()
+ {
+ public void modifyText(ModifyEvent e)
+ {
+ startupOption.setValue(inputText.getText());
+ notifyCompositeChangeListeners();
+ }
+ });
+
+ Button calc = new Button(parent, SWT.PUSH);
+ calc.setText(EmulatorNLS.UI_DpiScale_Calculator);
+ GridData calcData = new GridData(SWT.NULL, SWT.NULL, false, false);
+ calc.setLayoutData(calcData);
+ calc.addSelectionListener(new SelectionAdapter()
+ {
+ @Override
+ public void widgetSelected(SelectionEvent e)
+ {
+ DpiScaleCalculatorDialog dialog =
+ new DpiScaleCalculatorDialog(new Shell(parent.getShell()), skin);
+ if (dialog.open() == Dialog.OK)
+ {
+ for (StartupOptionsGroup group : StartupOptionsMgt
+ .getStartupOptionsGroupsList())
+ {
+ for (StartupOption startupOption : group.getStartupOptions())
+ {
+ if (startupOption.getName().equals(SCALE))
+ {
+ startupOption.setChecked(true);
+ startupOption.setValue(dialog.getResultScaleValue());
+ startupOption.updateUI();
+ }
+ }
+ }
+ }
+ }
+ });
+ calc.setEnabled(canCalculateScale);
+ if (!canCalculateScale)
+ {
+ ControlDecoration controlDecoration =
+ new ControlDecoration(inputText, SWT.LEFT | SWT.TOP);
+ controlDecoration
+ .setDescriptionText(EmulatorNLS.StartupOptionsComposite_Error_Loading_Skin_Cant_Calculate_Scale);
+ FieldDecoration fieldDecoration =
+ FieldDecorationRegistry.getDefault().getFieldDecoration(
+ FieldDecorationRegistry.DEC_WARNING);
+ controlDecoration.setImage(fieldDecoration.getImage());
+ }
+ }
+ // create input text
+ else if ((startupOption.getPreDefinedValues() == null)
+ || (startupOption.getPreDefinedValues().size() == 0))
+ {
+ final Text inputText = new Text(parent, SWT.SINGLE | SWT.BORDER);
+ inputText.setText(startupOption.getValue());
+ startupOption.setValueWidget(inputText);
+ inputText.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false, 2, 1));
+ inputText.addModifyListener(new ModifyListener()
+ {
+ public void modifyText(ModifyEvent e)
+ {
+ startupOption.setValue(inputText.getText());
+ notifyCompositeChangeListeners();
+ }
+ });
+ }
+ // create combobox
+ else
+ {
+ final Combo combo = new Combo(parent, SWT.READ_ONLY);
+ startupOption.setValueWidget(combo);
+ int selectedIndex = 0;
+ for (String preDefinedValue : startupOption.getPreDefinedValues())
+ {
+ combo.add(preDefinedValue);
+ if (startupOption.getValue().equals(preDefinedValue))
+ {
+ combo.select(selectedIndex);
+ }
+ else
+ {
+ selectedIndex++;
+ }
+ }
+ combo.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false, 2, 1));
+ combo.addModifyListener(new ModifyListener()
+ {
+ public void modifyText(ModifyEvent e)
+ {
+ startupOption.setValue(combo.getText());
+ notifyCompositeChangeListeners();
+ }
+ });
+ }
+ }
+
+ /**
+ * Create widgets to enable user to input data for fields of path type
+ *
+ * @param parent composite where the widgets will be attached to
+ * @param startupOption the corresponding startup option
+ */
+ private void createWidgetsForPathType(final Composite parent, final StartupOption startupOption)
+ {
+ // create input text
+ final Text pathText = new Text(parent, SWT.SINGLE | SWT.BORDER);
+ pathText.setText(startupOption.getValue());
+ startupOption.setValueWidget(pathText);
+ pathText.setLayoutData(new GridData(SWT.FILL, SWT.NULL, true, false));
+ pathText.addModifyListener(new ModifyListener()
+ {
+ public void modifyText(ModifyEvent e)
+ {
+ startupOption.setValue(pathText.getText());
+ notifyCompositeChangeListeners();
+ }
+ });
+ // create browse button
+ Button pathBrowseButton = new Button(parent, SWT.PUSH);
+ pathBrowseButton.setText(EmulatorNLS.UI_General_BrowseButtonLabel);
+ GridData data = new GridData(SWT.NULL, SWT.NULL, false, false);
+ pathBrowseButton.setLayoutData(data);
+ pathBrowseButton.addSelectionListener(new SelectionAdapter()
+ {
+ @Override
+ public void widgetSelected(SelectionEvent e)
+ {
+ String selectedPath = null;
+
+ if (startupOption.getTypeDetails().equalsIgnoreCase(TYPE_PATH_DIR))
+ {
+ DirectoryDialog directoryDialog = new DirectoryDialog(getShell(), SWT.OPEN);
+ selectedPath = directoryDialog.open();
+ }
+ else
+ {
+ FileDialog fileDialog = new FileDialog(getShell(), SWT.OPEN);
+ String[] filterExtensions =
+ {
+ "*" + startupOption.getTypeDetails() //$NON-NLS-1$
+ };
+ fileDialog.setFilterExtensions(filterExtensions);
+ selectedPath = fileDialog.open();
+ }
+
+ if (selectedPath != null)
+ {
+ pathText.setText(selectedPath);
+ }
+ }
+ });
+ }
+
+ /**
+ * Update command line value
+ *
+ * @see com.motorola.studio.android.emulator.device.ui.AbstractPropertiesComposite#notifyCompositeChangeListeners()
+ */
+ @Override
+ protected void notifyCompositeChangeListeners()
+ {
+ commandLine.setText(StartupOptionsMgt.getParamList());
+ super.notifyCompositeChangeListeners();
+ }
+
+ /**
+ * Retrieves the error message associated to this composites current state.
+ * The order of precedence of error is the same as the fields displayed on the
+ * UI, which means errors on fields drawn first are shown with a higher precedence
+ * than the errors of fields drawn last.
+ * The instance description field is the only non required field.
+ *
+ * @return the error message, or <code>null</code> if there are no errors
+ */
+ @Override
+ public String getErrorMessage()
+ {
+ String errMsg = null;
+ Status status = StartupOptionsMgt.validate();
+ if (status.getSeverity() == Status.ERROR)
+ {
+ errMsg = status.getMessage();
+ }
+ return errMsg;
+ }
+
+ /**
+ * Reload the values being displayed in the UI as well as the ones
+ * in the model.
+ *
+ * @param startupOptions commandLine the command line used to start the emulator
+ */
+ public void reloadValues(String commandLine)
+ {
+ StartupOptionsMgt.loadFromCommandLine(commandLine);
+ }
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/ui/wizard/WizardMainPage.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/ui/wizard/WizardMainPage.java
new file mode 100644
index 0000000..35f7457
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/ui/wizard/WizardMainPage.java
@@ -0,0 +1,445 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.emulator.device.ui.wizard;
+
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Properties;
+
+import org.eclipse.jface.wizard.WizardPage;
+import org.eclipse.sequoyah.device.framework.ui.wizard.DefaultDeviceTypeMenuWizardPage;
+import org.eclipse.sequoyah.device.framework.ui.wizard.IInstanceProperties;
+import org.eclipse.swt.widgets.Composite;
+
+import com.android.sdklib.IAndroidTarget;
+import com.android.sdklib.SdkConstants;
+import com.motorola.studio.android.adt.SdkUtils;
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.emulator.EmulatorPlugin;
+import com.motorola.studio.android.emulator.device.IDevicePropertiesConstants;
+import com.motorola.studio.android.emulator.device.instance.AndroidDeviceInstance;
+import com.motorola.studio.android.emulator.device.ui.AbstractPropertiesComposite;
+import com.motorola.studio.android.emulator.device.ui.AbstractPropertiesComposite.PropertyCompositeChangeEvent;
+import com.motorola.studio.android.emulator.device.ui.AbstractPropertiesComposite.PropertyCompositeChangeListener;
+import com.motorola.studio.android.emulator.device.ui.PropertiesMainComposite;
+import com.motorola.studio.android.emulator.i18n.EmulatorNLS;
+
+/**
+ * DESCRIPTION:
+ * <br>
+ * This class represents the first wizard page for Android Emulator Device Instance creation.
+ * <br>
+ * It shows all main information and validates it, setting an appropriate error message when
+ * applicable.
+ * <br>
+ * RESPONSIBILITY:
+ * <br>
+ * - Allow user to enter main information for creating a new Android Emulator Device Instance
+ * <br>
+ * - Validate main information entered by user
+ * <br>
+ * COLABORATORS:
+ * <br>
+ * WizardPage: extends this class
+ * <br>
+ * PropertiesMainComposite: uses this composite as the main widget
+ * <br>
+ * USAGE:
+ * <br>
+ * This wizard page must be added as the first page on the class implementing the New Android
+ * Emulator Device Instance Wizard.
+ */
+public class WizardMainPage extends WizardPage implements IInstanceProperties
+{
+ private PropertiesMainComposite mainComposite;
+
+ DefaultDeviceTypeMenuWizardPage tmlPage = null;
+
+ private PropertyCompositeChangeListener listener = new PropertyCompositeChangeListener()
+ {
+ public void compositeChanged(PropertyCompositeChangeEvent e)
+ {
+
+ String errorMessage = mainComposite.getErrorMessage();
+
+ if (errorMessage != null)
+ {
+ setErrorMessage(errorMessage);
+ setPageComplete(false);
+ }
+ else
+ {
+ setErrorMessage(null);
+ setPageComplete(true);
+ setMessage(EmulatorNLS.UI_WizardMainPage_PageName);
+ }
+
+ }
+ };
+
+ /**
+ * Creates a WizardMainPage object.
+ */
+ public WizardMainPage()
+ {
+ super(EmulatorNLS.UI_WizardMainPage_PageName);
+ }
+
+ /**
+ * Creates the UI for this wizard page.
+ * It uses the PropertiesMainComposite only.
+ */
+ public void createControl(Composite parent)
+ {
+
+ // Collecting usage data for statistical purpose
+ try
+ {
+ StudioLogger.collectUsageData(StudioLogger.WHAT_EMULATOR_CREATION_WIZARD,
+ StudioLogger.KIND_EMULATOR, StudioLogger.DESCRIPTION_DEFAULT,
+ EmulatorPlugin.PLUGIN_ID, EmulatorPlugin.getDefault().getBundle().getVersion()
+ .toString());
+ }
+ catch (Throwable e)
+ {
+ //Do nothing, but error on the log should never prevent app from working
+ }
+
+ setTitle(EmulatorNLS.UI_General_WizardTitle);
+ setErrorMessage(null);
+ setMessage(EmulatorNLS.UI_WizardMainPage_PageName);
+
+ IAndroidTarget vmTarget = null;
+ String vmSkin = ""; //$NON-NLS-1$
+ String vmPath;
+ String timeout;
+ String useVnc;
+ String useProxy;
+ String abiType = SdkConstants.ABI_ARMEABI;
+ String useSnapshot;
+ String saveSnapshot;
+ String startFromSnapshot;
+
+ tmlPage = (DefaultDeviceTypeMenuWizardPage) this.getPreviousPage();
+
+ IAndroidTarget targets[] = SdkUtils.getAllTargets();
+
+ if ((targets != null) && (targets.length > 0))
+ {
+
+ // Sort the targets array by comparing the API level of them
+ Arrays.sort(targets, new Comparator<IAndroidTarget>()
+ {
+
+ public int compare(IAndroidTarget o1, IAndroidTarget o2)
+ {
+ int returnValue;
+ if (o1.getVersion().getApiLevel() == o2.getVersion().getApiLevel())
+ {
+ returnValue = 0;
+ }
+ else if (o1.getVersion().getApiLevel() > o2.getVersion().getApiLevel())
+ {
+ returnValue = 1;
+ }
+ else
+ {
+ returnValue = -1;
+ }
+
+ return returnValue;
+ }
+
+ });
+
+ // Gets the first target with the highest API
+
+ int maxAPILevel = targets[targets.length - 1].getVersion().getApiLevel();
+
+ for (IAndroidTarget t : targets)
+ {
+ if (t.getVersion().getApiLevel() == maxAPILevel)
+ {
+ vmTarget = t;
+ break;
+ }
+
+ }
+
+ String skins[] = vmTarget.getSkins();
+ vmSkin = vmTarget.getDefaultSkin();
+ List<String> skinsList = Arrays.asList(skins);
+
+ /*
+ * Workaround to select WVGA skin on JIL SDK because HVGA skin is broken
+ */
+ if (SdkUtils.isJILSdk())
+ {
+ String tmpVmSkin = null;
+ int i = 0;
+ while ((tmpVmSkin == null) && (i < skins.length))
+ {
+ if (skins[i].toLowerCase().trim().equals("wvga"))
+ {
+ tmpVmSkin = skins[i];
+ }
+ i++;
+ }
+ if (tmpVmSkin != null)
+ {
+ vmSkin = tmpVmSkin;
+ }
+ }
+
+ if (!skinsList.contains(vmSkin))
+ {
+ vmSkin = skins[0];
+ }
+ }
+
+ vmPath = IDevicePropertiesConstants.defaultVmPath;
+
+ // get the default properties value
+ Properties defaultProperties = new Properties();
+ AndroidDeviceInstance.populateWithDefaultProperties(defaultProperties);
+ timeout = defaultProperties.getProperty(IDevicePropertiesConstants.timeout);
+ useVnc = defaultProperties.getProperty(IDevicePropertiesConstants.useVnc);
+ useProxy = defaultProperties.getProperty(IDevicePropertiesConstants.useProxy);
+ useSnapshot = defaultProperties.getProperty(IDevicePropertiesConstants.useSnapshots);
+ saveSnapshot = defaultProperties.getProperty(IDevicePropertiesConstants.saveSnapshot);
+ startFromSnapshot = defaultProperties.getProperty(IDevicePropertiesConstants.startFromSnapshot);
+
+ // When removing the emulator definition extension, remove this hardcoded set as
+ // well as the constant declaration from the Activator. If the Mot skin plugin is used,
+ // find another way to set this variable
+ mainComposite =
+ new PropertiesMainComposite(parent, tmlPage.getInstanceName(),
+ EmulatorPlugin.DEFAULT_EMULATOR_DEFINITION, timeout,
+ Boolean.parseBoolean(useVnc), Boolean.parseBoolean(useProxy),
+ Boolean.parseBoolean(useSnapshot), Boolean.parseBoolean(saveSnapshot),
+ Boolean.parseBoolean(startFromSnapshot), vmTarget, vmSkin, vmPath, abiType,
+ false, true, true);
+
+ AbstractPropertiesComposite.addCompositeChangeListener(listener);
+
+ setControl(mainComposite);
+
+ if ((targets == null) || ((targets != null) && (targets.length <= 0)))
+ {
+ setMessage(EmulatorNLS.WizardMainPage_NO_SDK_CONFIGURED_MSG);
+ setPageComplete(false);
+ return;
+ }
+
+ String initialMessage = mainComposite.getErrorMessage();
+
+ if (initialMessage != null)
+ {
+ setMessage(initialMessage);
+ }
+ setPageComplete(initialMessage == null);
+ }
+
+ @Override
+ public void setVisible(boolean visible)
+ {
+ if (visible)
+ {
+ mainComposite.setName(tmlPage.getInstanceName());
+ }
+ super.setVisible(visible);
+ }
+
+ /**
+ * Retrieves the skin id associated with this instance
+ *
+ * @return the skin id
+ */
+ public String getSkinId()
+ {
+ return mainComposite.getSkinId();
+ }
+
+ /**
+ * Retrieves the timeout associated with this instance
+ *
+ * @return the timeout
+ */
+ public String getTimeout()
+ {
+ return mainComposite.getTimeout();
+ }
+
+ /**
+ * Retrieves the vnc option associated with this instance
+ *
+ * @return the timeout
+ */
+ public String getUseVnc()
+ {
+ return mainComposite.getUseVnc();
+ }
+
+ /**
+ * Retrieves the proxy option associated with this instance
+ *
+ * @return the timeout
+ */
+ public String getUseProxy()
+ {
+ return mainComposite.getUseProxy();
+ }
+
+ /**
+ * Retrieves the emulator definition name associated with this instance
+ *
+ * @return the emulator definition name
+ */
+ public String getEmulatorDefId()
+ {
+ // When removing the emulator definition extension, remove this hardcoded set as
+ // well as the constant declaration from the Activator. If the Mot skin plugin is used,
+ // find another way to set this variable
+ return EmulatorPlugin.DEFAULT_EMULATOR_DEFINITION;
+ }
+
+ public String getUseSnapshot()
+ {
+ return mainComposite.getUseSnapshot();
+ }
+
+ public void setInstanceName(String name)
+ {
+ mainComposite.setName(name);
+ }
+
+ /**
+ * Retrieves VM target
+ */
+ public IAndroidTarget getVmTarget()
+ {
+ return mainComposite.getVmTarget();
+ }
+
+ /**
+ * Retrieves VM target
+ */
+ public String getAbiType()
+ {
+ return mainComposite.getAbiType();
+ }
+
+ /**
+ * Retrieves VM skin
+ */
+ public String getVmSkin()
+ {
+ return mainComposite.getVmSkin();
+ }
+
+ /**
+ * Retrieves VM path
+ */
+ public String getVmPath()
+ {
+ return mainComposite.getVmPath();
+ }
+
+ /**
+ * Retrieves SD Card info
+ */
+ public String getSDCard()
+ {
+ return mainComposite.getSDCard();
+ }
+
+ /**
+ * Retrieves Command line
+ */
+ public String getCommandLine()
+ {
+ //The command line shall be editable through the GUI
+ Properties defaultProperties = new Properties();
+ AndroidDeviceInstance.populateWithDefaultProperties(defaultProperties);
+ return defaultProperties.getProperty(IDevicePropertiesConstants.commandline);
+ }
+
+ @Override
+ public boolean isPageComplete()
+ {
+ return (mainComposite != null) && (mainComposite.getErrorMessage() == null);
+ }
+
+ @Override
+ public void dispose()
+ {
+ AbstractPropertiesComposite.removeCompositeChangeListener(listener);
+ setControl(null);
+ if (mainComposite != null)
+ {
+ mainComposite.dispose();
+ mainComposite = null;
+ }
+
+ super.dispose();
+ }
+
+ public Properties getProperties()
+ {
+ Properties properties = new Properties();
+
+ properties.setProperty(IDevicePropertiesConstants.timeout, this.getTimeout());
+ properties.setProperty(IDevicePropertiesConstants.useVnc, this.getUseVnc());
+ properties.setProperty(IDevicePropertiesConstants.useProxy, this.getUseProxy());
+ properties.setProperty(IDevicePropertiesConstants.emulatorDefId, this.getEmulatorDefId());
+ properties.setProperty(IDevicePropertiesConstants.skinId, this.getSkinId());
+ properties.setProperty(IDevicePropertiesConstants.vmSkin, this.getVmSkin());
+ properties.setProperty(IDevicePropertiesConstants.vmPath, this.getVmPath());
+ properties.setProperty(IDevicePropertiesConstants.abiType, this.getAbiType());
+ properties.setProperty(IDevicePropertiesConstants.useSnapshots, this.getUseSnapshot());
+ properties.setProperty(IDevicePropertiesConstants.saveSnapshot, this.getSaveSnapshot());
+ properties.setProperty(IDevicePropertiesConstants.startFromSnapshot, this.getstartFromSnapshot());
+
+ if (this.getVmTarget() != null)
+ {
+ properties.setProperty(IDevicePropertiesConstants.vmTarget, this.getVmTarget()
+ .getName());
+ }
+ else
+ {
+ properties.setProperty(IDevicePropertiesConstants.vmTarget, ""); //$NON-NLS-1$
+ }
+
+ return properties;
+ }
+
+ /**
+ * @return
+ */
+ public String getstartFromSnapshot()
+ {
+ return mainComposite.getstartFromSnapshot();
+ }
+
+ /**
+ * @return
+ */
+ public String getSaveSnapshot()
+ {
+ return mainComposite.getSaveSnapshot();
+ }
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/ui/wizard/WizardMainPageOperation.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/ui/wizard/WizardMainPageOperation.java
new file mode 100644
index 0000000..772684c
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/ui/wizard/WizardMainPageOperation.java
@@ -0,0 +1,76 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.emulator.device.ui.wizard;
+
+import java.lang.reflect.InvocationTargetException;
+import java.util.Collection;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.sequoyah.device.framework.ui.wizard.DefaultDeviceTypeMenuWizardPage;
+import org.eclipse.sequoyah.device.framework.ui.wizard.DeviceWizardRunnable;
+
+import com.motorola.studio.android.adt.SdkUtils;
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.common.utilities.EclipseUtils;
+import com.motorola.studio.android.emulator.device.refresh.InstancesListRefresh;
+
+/**
+ * This class performs the wizard finish operation for the WizardMainPage,
+ * according to the extension point
+ * org.eclipse.sequoyah.device.framework.ui.newDeviceWizardPages
+ */
+public class WizardMainPageOperation extends DeviceWizardRunnable
+{
+ /**
+ * Action executed on the wizard finish
+ */
+ public void run(IProgressMonitor monitor) throws InvocationTargetException,
+ InterruptedException
+ {
+ // Get wizard pages
+ WizardMainPage page = (WizardMainPage) this.getWizardPage();
+ DefaultDeviceTypeMenuWizardPage tmlPage =
+ (DefaultDeviceTypeMenuWizardPage) page.getPreviousPage();
+
+ // Create VM
+ try
+ {
+ //TML should provide some instance name changed listener
+ if (!tmlPage.getInstanceName().equals(page.getName()))
+ {
+ page.setInstanceName(tmlPage.getInstanceName());
+ }
+
+ SdkUtils.createVm(page.getVmPath(), tmlPage.getInstanceName(), page.getVmTarget(), page
+
+ .getAbiType(), page.getVmSkin(), page.getUseSnapshot(), (page.getSDCard().length() == 0
+ ? null : page.getSDCard()));
+
+ }
+ catch (CoreException e)
+ {
+ EclipseUtils.showErrorDialog("Could not create instance ", e.getStatus().getMessage());
+
+ StudioLogger.error(WizardMainPageOperation.class,
+ "Could not create AVD: " + tmlPage.getInstanceName(), e);
+ }
+
+ Collection<String> vmInstances = SdkUtils.getAllValidVmNames();
+ InstancesListRefresh.refreshStatus(vmInstances, tmlPage.getInstanceName());
+
+ }
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/ui/wizard/WizardStartupOptionsPage.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/ui/wizard/WizardStartupOptionsPage.java
new file mode 100644
index 0000000..02bbc97
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/ui/wizard/WizardStartupOptionsPage.java
@@ -0,0 +1,195 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.emulator.device.ui.wizard;
+
+import java.io.File;
+import java.util.Properties;
+
+import org.eclipse.jface.wizard.WizardPage;
+import org.eclipse.sequoyah.device.framework.ui.wizard.IInstanceProperties;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.emulator.core.exception.SkinException;
+import com.motorola.studio.android.emulator.core.skin.IAndroidSkin;
+import com.motorola.studio.android.emulator.core.skin.SkinFramework;
+import com.motorola.studio.android.emulator.device.IDevicePropertiesConstants;
+import com.motorola.studio.android.emulator.device.instance.options.StartupOptionsMgt;
+import com.motorola.studio.android.emulator.device.ui.AbstractPropertiesComposite;
+import com.motorola.studio.android.emulator.device.ui.AbstractPropertiesComposite.PropertyCompositeChangeEvent;
+import com.motorola.studio.android.emulator.device.ui.AbstractPropertiesComposite.PropertyCompositeChangeListener;
+import com.motorola.studio.android.emulator.device.ui.StartupOptionsComposite;
+import com.motorola.studio.android.emulator.i18n.EmulatorNLS;
+import com.motorola.studio.android.nativeos.NativeUIUtils;
+
+/**
+ * DESCRIPTION:
+ * <br>
+ * This class represents the second wizard page for Android Emulator Device Instance creation.
+ * <br>
+ * It shows all startup options information and validates it, setting an appropriate error message when
+ * applicable.
+ * <br>
+ * RESPONSIBILITY:
+ * <br>
+ * - Allow user to enter startup options information for creating a new Android Emulator Device Instance
+ * <br>
+ * - Validates tartup options information entered by user
+ * <br>
+ * COLABORATORS:
+ * <br>
+ * WizardPage: extends this class
+ * <br>
+ * StartupOptionsMainComposite: uses this composite as the main widget
+ * <br>
+ * USAGE:
+ * <br>
+ * This wizard page must be added as the second page on the class implementing the New Android
+ * Emulator Device Instance Wizard.
+ */
+public class WizardStartupOptionsPage extends WizardPage implements IInstanceProperties
+{
+ private StartupOptionsComposite startupOptionsComposite;
+
+ private IAndroidSkin skin;
+
+ // handle changes
+ private final PropertyCompositeChangeListener compositeChangeListener =
+ new PropertyCompositeChangeListener()
+ {
+ public void compositeChanged(PropertyCompositeChangeEvent e)
+ {
+ String errorMessage = startupOptionsComposite.getErrorMessage();
+ if (errorMessage != null)
+ {
+ setErrorMessage(errorMessage);
+ setPageComplete(false);
+ }
+ else
+ {
+ setErrorMessage(null);
+ setPageComplete(true);
+ }
+ }
+ };
+
+ /**
+ * Creates a WizardMainPage object.
+ */
+ public WizardStartupOptionsPage()
+ {
+ super(EmulatorNLS.UI_WizardStartupOptionsPage_PageMessage);
+
+ }
+
+ /**
+ * Creates the UI for this wizard page.
+ * It uses the PropertiesMainComposite only.
+ */
+ public void createControl(Composite parent)
+ {
+ // Get selected Skin name
+ WizardMainPage page = (WizardMainPage) this.getPreviousPage();
+ boolean canCalculateScale = true;
+ try
+ {
+ if (page.getSkinId() != null)
+ {
+ SkinFramework sm = new SkinFramework();
+ skin =
+ sm.getSkinById(page.getSkinId(), new File(page.getVmTarget().getLocation()
+ + "skins" + File.separator + page.getVmSkin()));
+ }
+ }
+ catch (SkinException e)
+ {
+ StudioLogger.error(this.getClass(),
+ "Error reading instance skin during startup options page creation", e);
+ canCalculateScale = false;
+ }
+
+ setTitle(EmulatorNLS.UI_General_WizardTitle);
+ setErrorMessage(null);
+ if (getMessage() != null)
+ {
+ setMessage(EmulatorNLS.UI_WizardStartupOptionsPage_PageMessage);
+ }
+
+ // Define layout
+ GridLayout mainLayout = new GridLayout(1, false);
+ mainLayout.marginTop = 0;
+ mainLayout.marginWidth = 0;
+ mainLayout.marginHeight = 0;
+
+ // Create Startup Options area
+ startupOptionsComposite =
+ new StartupOptionsComposite(parent, NativeUIUtils.getDefaultCommandLine(), skin,
+ canCalculateScale);
+
+ AbstractPropertiesComposite.addCompositeChangeListener(compositeChangeListener);
+
+ // Set layout
+ startupOptionsComposite.setLayout(mainLayout);
+
+ setControl(startupOptionsComposite);
+
+ setPageComplete(true);
+ }
+
+ @Override
+ public boolean isPageComplete()
+ {
+ boolean isComplete = true;
+ if (startupOptionsComposite != null)
+ {
+ isComplete = (startupOptionsComposite.getErrorMessage() == null);
+ }
+ return isComplete;
+ }
+
+ @Override
+ public void dispose()
+ {
+ AbstractPropertiesComposite.removeCompositeChangeListener(compositeChangeListener);
+ setControl(null);
+ if (startupOptionsComposite != null)
+ {
+ startupOptionsComposite.dispose();
+ startupOptionsComposite = null;
+ }
+ super.dispose();
+ }
+
+ public Properties getProperties()
+ {
+ Properties properties = new Properties();
+
+ if (startupOptionsComposite == null)
+ {
+ properties.setProperty(IDevicePropertiesConstants.commandline,
+ NativeUIUtils.getDefaultCommandLine());
+
+ }
+ else
+ {
+ properties.setProperty(IDevicePropertiesConstants.commandline,
+ StartupOptionsMgt.getParamList());
+ }
+
+ return properties;
+ }
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/i18n/EmulatorNLS.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/i18n/EmulatorNLS.java
new file mode 100644
index 0000000..dc95c9c
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/i18n/EmulatorNLS.java
@@ -0,0 +1,428 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.emulator.i18n;
+
+import org.eclipse.osgi.util.NLS;
+
+/**
+ * DESCRIPTION:
+ * This class is the NLS component for Emulator Core plugin.
+ * It is the main class for internationalization
+ *
+ * RESPONSIBILITY:
+ * Provide local strings for using throughout the tool
+ *
+ * COLABORATORS:
+ * coremessages.properties: file that contains the strings that will be provided
+ * to the plugin
+ *
+ * USAGE:
+ * Use any of the public static variables for accessing the local strings
+ */
+public class EmulatorNLS extends NLS
+{
+
+ /**
+ * The bundle location.
+ * It refers to messages.properties file inside this package
+ */
+
+ static
+ {
+ NLS.initializeMessages("com.motorola.studio.android.emulator.i18n.emulatorNLS",
+ EmulatorNLS.class);
+ }
+
+ /*
+ * Generic string area
+ */
+
+ public static String GEN_Error;
+
+ public static String GEN_Warning;
+
+ public static String GEN_Question;
+
+ /*
+ * Exception string area
+ */
+
+ public static String EXC_SkinFramework_CreateIAndroidSkin;
+
+ /*
+ * Warning string area
+ */
+
+ public static String WARN_SkinFramework_SkinNotInstalled;
+
+ public static String WARN_SkinFramework_InvalidInstalledSkinsNotLoaded;
+
+ /*
+ * Error string area
+ */
+
+ public static String ERR_SrcDestComposite_InvalidFillingBase;
+
+ public static String ERR_SrcDestComposite_InvalidFillingPhoneNumber;
+
+ public static String ERR_SrcDestComposite_InvalidFillingEmulator;
+
+ /*
+ * Information string area
+ */
+
+ /*
+ * Question string area
+ */
+
+ /*
+ * UI string area
+ */
+
+ public static String UI_SrcDestComposite_OriginatingRunningEmulatorLabel;
+
+ public static String UI_SrcDestComposite_DestinationRunningEmulatorLabel;
+
+ public static String UI_SrcDestComposite_OriginatingPhoneNumberLabel;
+
+ public static String UI_SrcDestComposite_DestinationPhoneNumberLabel;
+
+ /*
+ * Exception string area
+ */
+
+ public static String EXC_AndroidEmulatorStarter_TimeoutWhileRunningProtocol;
+
+ public static String EXC_AndroidEmulatorStarter_EmulatorStartCanceled;
+
+ public static String EXC_AndroidEmulatorReseter_ErrorWhilePerformingDeleteOperation;
+
+ public static String EXC_AndroidEmulatorReseter_ErrorWhilePerformingSnapshotCopyOperation;
+
+ public static String EXC_AndroidEmulatorReseter_ErrorWhilePerformingDeleteSnapshotOperation;
+
+ public static String EXC_AndroidEmulatorStarter_ProcessTerminated;
+
+ public static String EXC_TimeoutWhileStarting;
+
+ public static String EXC_VncServerNotRunning;
+
+ public static String EXC_CouldNotStartProtocol;
+
+ public static String EXC_AndroidExceptionHandler_CannotRunStopService;
+
+ public static String EXC_AndroidLogicUtils_CannotStartProcess;
+
+ public static String EXC_AndroidLogicUtils_DeviceIsOffline;
+
+ /*
+ * Error string area
+ */
+
+ public static String ERR_AndroidEmulatorStarter_InstanceNullPointer;
+
+ public static String ERR_AndroidEmulatorStarter_NoLogicAvailableForStart;
+
+ public static String ERR_AndroidLogicPlugin_EmulatorStopped;
+
+ public static String ERR_AndroidLogicPlugin_InvalidTimeoutValue;
+
+ public static String ERR_TransferFilesLogic_NotEnoughInformation;
+
+ /*
+ * Question string area
+ */
+
+ public static String QUESTION_AndroidEmulatorReseter_ConfirmationText;
+
+ public static String QUESTION_AndroidEmulatorStopper_StopEmulatorQuestion;
+
+ public static String QUESTION_AndroidExceptionHandler_ImpossibleToReconnect;
+
+ public static String QUESTION_AndroidEmulatorReseter_Yes;
+
+ public static String QUESTION_AndroidEmulatorReseter_No;
+
+ /*
+ * Information string area
+ */
+
+ public static String INFO_ConnectVncLogic_UserCancelledVncServerStart;
+
+ /*
+ * Progress monitor string area
+ */
+
+ public static String MON_AndroidEmulatorStarter_ConnectingToEmulator;
+
+ public static String MON_AndroidEmulatorStopper_DisposingInstance;
+
+ public static String MON_AndroidEmulatorStopper_StopVm;
+
+ public static String MON_AndroidEmulatorStarter_Canceling;
+
+ public static String DPISCALECALCULATOR_Error_MonitorDpi;
+
+ public static String DPISCALECALCULATOR_Error_MonitorSize;
+
+ public static String DPISCALECALCULATOR_Error_ScreenSize;
+
+ public static String DPISCALECALCULATOR_MonitorDpi_Label;
+
+ public static String DPISCALECALCULATOR_MonitorDpiSize_Label;
+
+ public static String DPISCALECALCULATOR_MonitorDpivalue_Label;
+
+ public static String DPISCALECALCULATOR_Regex_TwoDigits;
+
+ public static String DPISCALECALCULATOR_ResultGroup_Title;
+
+ public static String DPISCALECALCULATOR_ResultMonitorDpi_Label;
+
+ public static String DPISCALECALCULATOR_ResultScale_Label;
+
+ public static String DPISCALECALCULATOR_ScreenSize_Label;
+
+ public static String DPISCALECALCULATOR_Title;
+
+ /*
+ * Error string area
+ */
+ public static String ERR_PropertiesMainComposite_MissingTimeoutValue;
+
+ public static String ERR_PropertiesMainComposite_TimeoutValueIsNotPositiveInteger;
+
+ public static String ERR_PropertiesMainComposite_MissingSDCardPath;
+
+ public static String ERR_PropertiesMainComposite_MissingSDCardSize;
+
+ public static String ERR_PropertiesMainComposite_SDCardPathIsNotValid;
+
+ public static String ERR_PropertiesMainComposite_SDCardSizeIsNotPositiveInteger;
+
+ public static String ERR_PropertiesMainComposite_ABINotAvailable;
+
+ // Startup options - all
+ public static String ERR_PropertiesMainComposite_StartupOpt_NoQuotes;
+
+ // Startup options - text
+ public static String ERR_PropertiesMainComposite_StartupOpt_TextBlank;
+
+ // Startup options - number
+ public static String ERR_PropertiesMainComposite_StartupOpt_NumberRequired;
+
+ public static String ERR_PropertiesMainComposite_StartupOpt_NumberMustBeInteger;
+
+ public static String ERR_PropertiesMainComposite_StartupOpt_NumberMustBePositiveInteger;
+
+ public static String ERR_PropertiesMainComposite_StartupOpt_NumberIntRange;
+
+ // Startup options - path
+ public static String ERR_PropertiesMainComposite_StartupOpt_PathRequired;
+
+ public static String ERR_PropertiesMainComposite_StartupOpt_PathDirNotExist;
+
+ public static String ERR_PropertiesMainComposite_StartupOpt_PathMustBeDir;
+
+ public static String ERR_PropertiesMainComposite_StartupOpt_PathFileNotExist;
+
+ public static String ERR_PropertiesMainComposite_StartupOpt_PathMustBeFile;
+
+ public static String ERR_PropertiesMainComposite_StartupOpt_PathIncorrectFileType;
+
+ /*
+ * Info string area
+ */
+ public static String INFO_InfoComposite_EmulatorDefinitionNotFound;
+
+ /*
+ * UI string area
+ */
+ public static String UI_General_BrowseButtonLabel;
+
+ public static String UI_General_WizardTitle;
+
+ public static String UI_PropertiesMainComposite_NameLabel;
+
+ public static String UI_PropertiesMainComposite_EmulatorWindowMode_GroupTitle;
+
+ public static String UI_PropertiesMainComposite_EmulatorWindowMode_NativeLabel;
+
+ public static String UI_PropertiesMainComposite_EmulatorWindowMode_VncLabel;
+
+ public static String UI_PropertiesMainComposite_TimeoutLabel;
+
+ public static String UI_WizardMainPage_PageName;
+
+ public static String UI_WizardStartupOptionsPage_PageMessage;
+
+ public static String UI_AndroidDeviceInstance_StopInstanceJob;
+
+ public static String UI_DpiScale_Calculator;
+
+ /*
+ * Wizard - VM area
+ */
+ public static String UI_PropertiesMainComposite_TargetLabel;
+
+ public static String UI_PropertiesMainComposite_SkinLabel;
+
+ public static String UI_PropertiesMainComposite_PathLabel;
+
+ public static String UI_PropertiesMainComposite_SDCardLabel;
+
+ public static String UI_PropertiesMainComposite_SDCardNoneLabel;
+
+ public static String UI_PropertiesMainComposite_SDCardExistingLabel;
+
+ public static String UI_PropertiesMainComposite_SDCardNewLabel;
+
+ public static String UI_PropertiesMainComposite_SDCardPathLabel;
+
+ public static String UI_PropertiesMainComposite_SDCardSizeLabel;
+
+ public static String UI_PropertiesMainComposite_PathGroupTitle;
+
+ public static String UI_PropertiesMainComposite_UseDefaultPath;
+
+ /*
+ * Wizard - VM area errors
+ */
+ public static String ERR_PropertiesMainComposite_VmTargetEmpty;
+
+ public static String ERR_PropertiesMainComposite_VmSkinEmpty;
+
+ public static String ERR_PropertiesMainComposite_VmPathInvalid;
+
+ /*
+ * Question string area
+ */
+ public static String WizardMainPage_NO_SDK_CONFIGURED_MSG;
+
+ public static String UI_SdkSetup_CreateAVD_Title;
+
+ public static String UI_SdkSetup_CreateAVD_Message;
+
+ /*
+ * Exception string area
+ */
+
+ public static String EXC_General_CannotRunStopService;
+
+ public static String EXC_AncroidView_CannotRunMultipleStopServices;
+
+ public static String EXC_AndroidView_ErrorStartingScreens;
+
+ public static String EXC_AbstractZoomHandler_InstanceNotFound;
+
+ public static String EXC_AndroidView_ViewNotFound;
+
+ /*
+ * Warning string area
+ */
+
+ /*
+ * Error string area
+ */
+
+ public static String ERR_AndroidView_ProtocolImplementerNotSupported;
+
+ public static String EXC_AbstractAndroidView_ViewNotAccessibleProgramatically;
+
+ /*
+ * Information string area
+ */
+
+ /*
+ * Question string area
+ */
+
+ public static String QUESTION_AndroidView_StopAllInstancesOnDisposeTitle;
+
+ public static String QUESTION_AndroidView_StopAllInstancesOnDisposeMessage;
+
+ public static String QUESTION_AbstractAndroidView_OpenViewForStartedEmulatorsTitle;
+
+ public static String QUESTION_AbstractAndroidView_OpenViewForStartedEmulatorsMessage;
+
+ public static String QUESTION_RunningInstancesOnClose_Title;
+
+ public static String QUESTION_RunningInstancesOnClose_Text;
+
+ public static String QUESTION_NativeWindow_LooseOriginalScale_Title;
+
+ public static String QUESTION_NativeWindow_LooseOriginalScale_Text;
+
+ /*
+ * Warn string area
+ */
+ public static String WARN_RunningInstancesOnClose_Linux_Title;
+
+ public static String WARN_RunningInstancesOnClose_Linux_Text;
+
+ /*
+ * UI string area
+ */
+
+ public static String UI_AbstractAndroidView_StopInstanceJob;
+
+ public static String UI_LayoutContributionItem_NoLayoutsAvailable;
+
+ /*
+ * Progress monitor string area
+ */
+ public static String ERR_CannotConnectToVNC;
+
+ public static String ERR_StopEmulatorHandler_NotAnAndroidEmulator;
+
+ public static String ERR_StartEmulatorHandler_NotAnAndroidEmulator;
+
+ public static String ERR_AndroidSkinTranslator_ErrorReadingKeycodeFile;
+
+ public static String ERR_AndroidSkin_NoLayoutLoaded;
+
+ public static String ERR_AndroidSkin_ProvidedSkinPathIsNotADirectory;
+
+ public static String ERR_AndroidSkin_InvalidLayoutProvided;
+
+ public static String ERR_LayoutFileParser_BracketsDoNotMatch;
+
+ public static String ERR_LayoutFileParser_LayoutFileCouldNotBeRead;
+
+ public static String PropertiesMainComposite_ABITypeLabel;
+
+ public static String PropertiesMainComposite_ProxySettings_CheckboxLabel;
+
+ public static String PropertiesMainComposite_ProxySettings_GroupTitle;
+
+ public static String PropertiesMainComposite_ProxySettings_LinkToPreference;
+
+ public static String PropertiesMainComposite_SaveSnapshot;
+
+ public static String PropertiesMainComposite_SDCard_Size_Invalid_Integer;
+
+ public static String PropertiesMainComposite_SnapshotSettings;
+
+ public static String PropertiesMainComposite_startFromSnapshot;
+
+ public static String PropertiesMainComposite_UseSnapshot;
+
+ public static String RepairAvdHandler_AVD_NOT_REPAIRABLE;
+
+ public static String RepairAvdHandler_Not_Android_Instance;
+
+ public static String StartupOptionsComposite_Error_Loading_Skin_Cant_Calculate_Scale;
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/i18n/emulatorNLS.properties b/src/plugins/emulator/src/com/motorola/studio/android/emulator/i18n/emulatorNLS.properties
new file mode 100644
index 0000000..44527f9
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/i18n/emulatorNLS.properties
@@ -0,0 +1,173 @@
+#
+# Copyright (C) 2012 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+GEN_Error=Error
+GEN_Warning=Warning
+GEN_Question=Question
+EXC_SkinFramework_CreateIAndroidSkin=There was an internal error while loading the {0} skin.\nAn application restart may solve this problem, but if it recurs, you may need to reinstall MOTODEV Studio.
+WARN_SkinFramework_SkinNotInstalled=The skin named {0} is not installed.\nAlthough this does not prevent the Android emulator from working, this skin is not accessible.\nIf this error recurs, you may need to reinstall MOTODEV Studio.
+WARN_SkinFramework_InvalidInstalledSkinsNotLoaded=Skin {0} is invalid and was not loaded.\nAlthough this does not prevent the Android emulator from working, this skin is not accessible.\nIf this error recurs, you may need to reinstall MOTODEV Studio.
+ERR_SrcDestComposite_InvalidFillingBase=Errors were found. Check the following:\n{0}{1}
+ERR_SrcDestComposite_InvalidFillingPhoneNumber=\n\t- In the phone number field, be sure you have entered only digits, with no additional symbols such as hyphens, parentheses, or spaces.
+ERR_SrcDestComposite_InvalidFillingEmulator=\n\t- In the emulator list, be sure you have selected a running emulator.
+UI_SrcDestComposite_OriginatingRunningEmulatorLabel=Originating Android Emulator :
+UI_SrcDestComposite_DestinationRunningEmulatorLabel=Destination Android Emulator :
+UI_SrcDestComposite_OriginatingPhoneNumberLabel=Originating Phone # :
+UI_SrcDestComposite_DestinationPhoneNumberLabel=Destination Phone # :
+EXC_AndroidEmulatorStarter_TimeoutWhileRunningProtocol=An error is preventing the Android Emulator from being displayed. \
+ Some possible causes are:\n\t- The timeout interval is too short. You may need to increase the timeout value on \
+ the Android Virtual Device property page.\n\nSince the emulator is not fully functional, MOTODEV Studio is \
+ aborting the emulator start process.
+EXC_AndroidEmulatorStarter_EmulatorStartCanceled=Android Emulator start process canceled by user.
+EXC_AndroidEmulatorReseter_ErrorWhilePerformingDeleteOperation=Reset operation could not be completed. Manually remove all \
+ files, except config.ini and snapshots.img (if it exists), from {0} to complete the process. If you don't have the needed permissions, contact your system administrator.
+EXC_AndroidEmulatorReseter_ErrorWhilePerformingDeleteSnapshotOperation=Reset operation could not be completed. Manually remove all \
+ files, except config.ini from {0} to complete the process. If the snapshots.img file exists, replace it by {1}. If you don't have the needed permissions, contact your system administrator.
+EXC_AndroidEmulatorReseter_ErrorWhilePerformingSnapshotCopyOperation=Error while resetting the snapshot file. Manually copy the file from {0} \
+ to {1} to complete the process. If you don't have the needed permissions, contact your system administrator.
+EXC_AndroidEmulatorStarter_ProcessTerminated=The emulator process terminated unexpectedly:
+EXC_TimeoutWhileStarting=The timeout was reached while trying to start Android Emulator {0}.
+EXC_VncServerNotRunning=The VNC server is not running on {0}.\n{1}
+EXC_CouldNotStartProtocol=Couldn't establish a connection to the Android emulator.
+EXC_AndroidExceptionHandler_CannotRunStopService=The device instance could not be stopped.
+EXC_AndroidLogicUtils_CannotStartProcess=It was not possible to start the emulator process. The instance start process will be aborted.
+EXC_AndroidLogicUtils_DeviceIsOffline=The device is not online.
+ERR_AndroidEmulatorStarter_InstanceNullPointer=Android emulator was not defined. The operation will not be performed.
+ERR_AndroidEmulatorStarter_NoLogicAvailableForStart=An error is preventing the Android emulator from being started. \
+ Try reinstalling or updating MOTODEV Studio.
+ERR_AndroidLogicPlugin_EmulatorStopped=The Android emulator process has unexpectedly stopped running. The instance {0} is now stopped.
+ERR_AndroidLogicPlugin_InvalidTimeoutValue=Invalid timeout value: {0}
+ERR_TransferFilesLogic_NotEnoughInformation=There is not enough information about the files being transferred to the emulator.
+INFO_ConnectVncLogic_UserCancelledVncServerStart=User canceled the execution of the VNC server!
+QUESTION_AndroidEmulatorReseter_ConfirmationText=WARNING: Resetting the emulator(s) will delete all user-specific data, such as installed applications, custom files (such as user databases) and any customizations performed on the device(s).\n\nDo you want to proceed?
+QUESTION_AndroidEmulatorStopper_StopEmulatorQuestion=Are you sure you want to stop "{0}"?
+QUESTION_AndroidExceptionHandler_ImpossibleToReconnect=Cannot reconnect to the emulator. Do you want to retry?
+MON_AndroidEmulatorStarter_ConnectingToEmulator=Connecting to Android emulator instance...
+MON_AndroidEmulatorStopper_DisposingInstance=Disposing of the Android emulator instance
+MON_AndroidEmulatorStopper_StopVm=Stopping the device instance. This may take a few minutes...
+MON_AndroidEmulatorStarter_Canceling=Canceling
+QUESTION_AndroidEmulatorReseter_Yes=Yes
+QUESTION_AndroidEmulatorReseter_No=No
+DPISCALECALCULATOR_Error_MonitorDpi=Monitor Dpi value should contain only digits
+DPISCALECALCULATOR_Error_MonitorSize=Monitor Size should contain only digits and dots
+DPISCALECALCULATOR_Error_ScreenSize=Screen size must contain only digits and dots
+DPISCALECALCULATOR_MonitorDpi_Label=Monitor Screen Resolution
+DPISCALECALCULATOR_MonitorDpiSize_Label=Based on Monitor Size (in)
+DPISCALECALCULATOR_MonitorDpivalue_Label=Value (dpi)
+DPISCALECALCULATOR_Regex_TwoDigits=\\d+(\\.\\d{1,2})*
+DPISCALECALCULATOR_ResultGroup_Title=Result
+DPISCALECALCULATOR_ResultMonitorDpi_Label=Resolution (dpi):
+DPISCALECALCULATOR_ResultScale_Label=Scale:
+DPISCALECALCULATOR_ScreenSize_Label=Device Screen Size(in) :
+DPISCALECALCULATOR_Title=Screen Resolution / Scale Calculator
+ERR_PropertiesMainComposite_MissingTimeoutValue=You must provide a timeout value
+ERR_PropertiesMainComposite_TimeoutValueIsNotPositiveInteger=The timeout value must be a positive integer
+ERR_PropertiesMainComposite_MissingSDCardPath=You must provide a valid SD Card path
+ERR_PropertiesMainComposite_MissingSDCardSize=You must provide the SD Card size
+ERR_PropertiesMainComposite_SDCardPathIsNotValid=The SD Card path is not valid
+ERR_PropertiesMainComposite_SDCardSizeIsNotPositiveInteger=The SD Card size must be greater than 9MB
+INFO_InfoComposite_EmulatorDefinitionNotFound=The selected emulator type was not found.\
+ The device was not created.\
+ The default type has been selected instead.
+UI_General_BrowseButtonLabel=Browse...
+UI_General_WizardTitle=New Android Virtual Device Instance
+UI_PropertiesMainComposite_NameLabel=Name :
+UI_PropertiesMainComposite_TimeoutLabel=Timeout (sec):
+UI_PropertiesMainComposite_EmulatorWindowMode_GroupTitle=Internal Emulator Window
+UI_PropertiesMainComposite_EmulatorWindowMode_NativeLabel=Show the native Emulator window within an Eclipse view (recommended)
+UI_PropertiesMainComposite_EmulatorWindowMode_VncLabel=Use VNC to show the Emulator within an Eclipse view
+UI_WizardMainPage_PageName=Enter Android Virtual Device Instance Main Information
+UI_WizardStartupOptionsPage_PageMessage=Enter the startup options you want the Android Virtual Device instance to use.
+UI_AndroidDeviceInstance_StopInstanceJob=Stop Device Instance
+UI_DpiScale_Calculator=Calculate
+UI_PropertiesMainComposite_TargetLabel=AVD Target :
+UI_PropertiesMainComposite_SkinLabel=AVD Skin :
+UI_PropertiesMainComposite_PathGroupTitle=AVD Path
+UI_PropertiesMainComposite_PathLabel=AVD Path :
+UI_PropertiesMainComposite_SDCardLabel=SD Card
+UI_PropertiesMainComposite_SDCardNoneLabel=None
+UI_PropertiesMainComposite_SDCardExistingLabel=Existing
+UI_PropertiesMainComposite_SDCardNewLabel=New
+UI_PropertiesMainComposite_SDCardPathLabel=SD Card Path :
+UI_PropertiesMainComposite_SDCardSizeLabel=SD Card Size :
+UI_PropertiesMainComposite_UseDefaultPath= Use default
+ERR_PropertiesMainComposite_VmTargetEmpty=You must provide a target for the AVD
+ERR_PropertiesMainComposite_VmSkinEmpty=You must provide a skin for the AVD
+ERR_PropertiesMainComposite_VmPathInvalid=You must provide a valid and existing path for the AVD
+ERR_PropertiesMainComposite_StartupOpt_NoQuotes=The value for {0} contains quotes, which are not allowed.
+ERR_PropertiesMainComposite_StartupOpt_TextBlank=Provide a value for {0}.
+ERR_PropertiesMainComposite_StartupOpt_NumberRequired=Provide a value for {0}.
+ERR_PropertiesMainComposite_StartupOpt_NumberMustBeInteger=The value for {0} must be an integer.
+ERR_PropertiesMainComposite_StartupOpt_NumberMustBePositiveInteger=The value for {0} must be a positive integer.
+ERR_PropertiesMainComposite_StartupOpt_NumberIntRange=The value for {0} must be a value between {1} and {2}.
+ERR_PropertiesMainComposite_StartupOpt_PathRequired=Provide a path for {0}.
+ERR_PropertiesMainComposite_StartupOpt_PathDirNotExist=The directory specified for {0} doesn't exist. Specify a valid directory.
+ERR_PropertiesMainComposite_StartupOpt_PathMustBeDir=Select a folder for {0}, not a file.
+ERR_PropertiesMainComposite_StartupOpt_PathFileNotExist=The file specified for {0} doesn't exist. Specify a valid path.
+ERR_PropertiesMainComposite_StartupOpt_PathMustBeFile=Select a file for {0}, not a folder.
+ERR_PropertiesMainComposite_StartupOpt_PathIncorrectFileType=The file specified for {0} is not a {1} file. Specify a valid path.
+WizardMainPage_NO_SDK_CONFIGURED_MSG=Configure an SDK before creating an AVD.
+
+UI_SdkSetup_CreateAVD_Title = Create AVD
+UI_SdkSetup_CreateAVD_Message = A valid AVD (Android Virtual Device) was not detected. Do you want to create one?
+EXC_General_CannotRunStopService=The instance could not be stopped automatically.
+EXC_AncroidView_CannotRunMultipleStopServices=One or more instances could not be stopped automatically.
+EXC_AndroidView_ErrorStartingScreens=There was an error while trying to refresh the emulator screen. \
+ Restart the emulator to refresh the screen.
+EXC_AbstractZoomHandler_InstanceNotFound=The currently displayed Android emulator instance is no longer \
+ available.\nThe view is being closed to reflect this.
+EXC_AndroidView_ViewNotFound=The Emulator view could not be accessed.\nThe requested operation will not \
+ be performed.
+ERR_AndroidView_ProtocolImplementerNotSupported=The device instance is not using a communication protocol \
+ supported by this viewer. The instance will be stopped.
+QUESTION_AndroidView_StopAllInstancesOnDisposeTitle=Close Android Emulator View
+QUESTION_AndroidView_StopAllInstancesOnDisposeMessage=Do you wish to stop the running Android emulator \
+ instances as well?
+UI_AbstractAndroidView_StopInstanceJob=Stop Device
+QUESTION_AbstractAndroidView_OpenViewForStartedEmulatorsTitle=Open Android Emulator View
+QUESTION_AbstractAndroidView_OpenViewForStartedEmulatorsMessage=Do you wish to display the Android Virtual Device(s) within an Eclipse view?
+QUESTION_RunningInstancesOnClose_Title = Running Android Virtual Devices
+QUESTION_RunningInstancesOnClose_Text = There are Android Virtual Devices running. Do you want to stop them?
+QUESTION_NativeWindow_LooseOriginalScale_Title = Start up arguments
+QUESTION_NativeWindow_LooseOriginalScale_Text = By zooming the emulator image you will lose the scale options used to start the Android Virtual Device. Do you want to continue?
+WARN_RunningInstancesOnClose_Linux_Title = Running Android Virtual Devices
+WARN_RunningInstancesOnClose_Linux_Text = The running Android Virtual Devices will be stopped
+UI_LayoutContributionItem_NoLayoutsAvailable=No layouts
+ERR_CannotConnectToVNC=Could not establish a connection to emulator "{0}"
+EXC_AbstractAndroidView_ViewNotAccessibleProgramatically=The Android Emulator view could not be opened automatically. \
+ Open it manually by selecting "Show View" from the "Window" menu.
+ERR_StopEmulatorHandler_NotAnAndroidEmulator=The service is being run with a device instance that is not supported by the service. Aborting execution.
+ERR_StartEmulatorHandler_NotAnAndroidEmulator=The service is being run with a device instance that is not supported by the service. Aborting execution.
+ERR_AndroidSkinTranslator_ErrorReadingKeycodeFile=It was not possible to retrieve key-related data from the skin. It will not be possible to use the mouse in this session.
+ERR_AndroidSkin_NoLayoutLoaded=No skin was loaded. Check if the skin assigned to the current AVD is available and not corrupted.
+ERR_AndroidSkin_InvalidLayoutProvided=The provided layout name does not exist in the skin being used by the instance. The current operation is being aborted.
+ERR_AndroidSkin_ProvidedSkinPathIsNotADirectory=The selected skin was not found. Check your AVD configuration and try again later.
+ERR_LayoutFileParser_BracketsDoNotMatch=The skin layout file is corrupted. It is not possible to load this skin. It will not be possible to use the mouse in this session.
+ERR_LayoutFileParser_LayoutFileCouldNotBeRead=The skin layout file could not be read. It is not possible to load this skin. It will not be possible to use the mouse in this session.
+PropertiesMainComposite_ABITypeLabel=ABI Type:
+PropertiesMainComposite_ProxySettings_CheckboxLabel=Use settings from Eclipse Network Settings
+PropertiesMainComposite_ProxySettings_GroupTitle=Proxy Settings
+PropertiesMainComposite_ProxySettings_LinkToPreference=<a>Configure network settings</a>
+PropertiesMainComposite_SaveSnapshot=Save to snapshot on exit
+PropertiesMainComposite_SDCard_Size_Invalid_Integer=The SD card size is not a valid integer.
+PropertiesMainComposite_SnapshotSettings=Snapshot Settings
+PropertiesMainComposite_startFromSnapshot=Launch emulator from snapshot
+PropertiesMainComposite_UseSnapshot=Enable snapshot
+RepairAvdHandler_AVD_NOT_REPAIRABLE=The selected AVD is not repairable.
+RepairAvdHandler_Not_Android_Instance=Aborting repair service. This is not an Android Emulator instance...
+
+ERR_PropertiesMainComposite_ABINotAvailable=You must select one ABI type to create an AVD. If none are available, use the Android SDK Manager to download the ABI system image.
+StartupOptionsComposite_Error_Loading_Skin_Cant_Calculate_Scale=Could not read emulator skin. Calculating scale will not be possible.
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/logic/AbstractStartAndroidEmulatorLogic.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/logic/AbstractStartAndroidEmulatorLogic.java
new file mode 100644
index 0000000..004ac7d
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/logic/AbstractStartAndroidEmulatorLogic.java
@@ -0,0 +1,69 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.emulator.logic;
+
+import static com.motorola.studio.android.common.log.StudioLogger.info;
+
+import java.io.IOException;
+import java.util.Collection;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.osgi.util.NLS;
+
+import com.motorola.studio.android.emulator.core.exception.InstanceStartException;
+import com.motorola.studio.android.emulator.core.exception.StartCancelledException;
+import com.motorola.studio.android.emulator.core.exception.StartTimeoutException;
+import com.motorola.studio.android.emulator.i18n.EmulatorNLS;
+
+public abstract class AbstractStartAndroidEmulatorLogic implements IAndroidLogic
+{
+
+ public static enum LogicMode
+ {
+ START_MODE, TRANSFER_AND_CONNECT_VNC, RESTART_VNC_SERVER, DO_NOTHING;
+ }
+
+ public final void execute(IAndroidLogicInstance instance, int timeout, IProgressMonitor monitor)
+ throws InstanceStartException, StartCancelledException, StartTimeoutException,
+ IOException
+ {
+ this.execute(instance, LogicMode.START_MODE, timeout, monitor);
+ }
+
+ public final void execute(IAndroidLogicInstance instance, LogicMode mode, int timeout,
+ IProgressMonitor monitor) throws InstanceStartException, StartCancelledException,
+ StartTimeoutException, IOException
+
+ {
+ for (IAndroidLogic logic : getLogicCollection(instance, mode))
+ {
+ long timeoutLimit = AndroidLogicUtils.getTimeoutLimit(timeout);
+ AndroidLogicUtils.testCanceled(monitor);
+ AndroidLogicUtils.testTimeout(timeoutLimit, NLS.bind(
+ EmulatorNLS.EXC_TimeoutWhileStarting, instance.getName()));
+ info("Executing " + logic.getClass().getSimpleName() + " for " + instance);
+ long startTime = System.currentTimeMillis();
+ logic.execute(instance, timeout, monitor);
+ long endTime = System.currentTimeMillis();
+ int duration = (int) (endTime - startTime);
+ timeout = timeout - duration;
+ }
+ }
+
+ public abstract Collection<IAndroidLogic> getLogicCollection(IAndroidLogicInstance instance,
+ LogicMode mode);
+
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/logic/AndroidExceptionHandler.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/logic/AndroidExceptionHandler.java
new file mode 100644
index 0000000..0bb31bd
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/logic/AndroidExceptionHandler.java
@@ -0,0 +1,392 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.emulator.logic;
+
+import static com.motorola.studio.android.common.log.StudioLogger.debug;
+import static com.motorola.studio.android.common.log.StudioLogger.error;
+import static com.motorola.studio.android.common.log.StudioLogger.info;
+import static com.motorola.studio.android.common.log.StudioLogger.warn;
+
+import java.io.IOException;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.core.runtime.jobs.IJobChangeEvent;
+import org.eclipse.core.runtime.jobs.IJobManager;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.core.runtime.jobs.JobChangeAdapter;
+import org.eclipse.sequoyah.vnc.protocol.PluginProtocolActionDelegate;
+import org.eclipse.sequoyah.vnc.protocol.lib.IProtocolExceptionHandler;
+import org.eclipse.sequoyah.vnc.protocol.lib.ProtocolHandle;
+import org.eclipse.sequoyah.vnc.protocol.lib.exceptions.InvalidDefinitionException;
+import org.eclipse.sequoyah.vnc.protocol.lib.exceptions.InvalidInputStreamDataException;
+import org.eclipse.sequoyah.vnc.protocol.lib.exceptions.InvalidMessageException;
+import org.eclipse.sequoyah.vnc.protocol.lib.exceptions.MessageHandleException;
+import org.eclipse.sequoyah.vnc.protocol.lib.exceptions.ProtocolHandshakeException;
+import org.eclipse.sequoyah.vnc.protocol.lib.exceptions.ProtocolRawHandlingException;
+
+import com.motorola.studio.android.adt.ISerialNumbered;
+import com.motorola.studio.android.common.utilities.EclipseUtils;
+import com.motorola.studio.android.emulator.core.exception.InstanceNotFoundException;
+import com.motorola.studio.android.emulator.core.exception.InstanceStopException;
+import com.motorola.studio.android.emulator.core.model.IAndroidEmulatorInstance;
+import com.motorola.studio.android.emulator.core.utils.EmulatorCoreUtils;
+import com.motorola.studio.android.emulator.i18n.EmulatorNLS;
+import com.motorola.studio.android.emulator.logic.AbstractStartAndroidEmulatorLogic.LogicMode;
+
+/**
+ * DESCRIPTION:
+ * Class that defines how to handle internal protocol exceptions
+ *
+ * RESPONSABILITY:
+ * Handle internal protocol exceptions
+ *
+ * COLABORATORS:
+ * None.
+ *
+ * USAGE:
+ * This class shall be used by Eclipse only
+ */
+public class AndroidExceptionHandler implements IProtocolExceptionHandler
+{
+ private int handlingLevel = 1;
+
+ private boolean checkThreadRunning = false;
+
+ private Lock lock = new ReentrantReadWriteLock().writeLock();
+
+ private static Collection<String> stoppedWithFailure = new HashSet<String>();
+
+ static
+ {
+
+ IJobManager manager = Job.getJobManager();
+ manager.addJobChangeListener(new JobChangeAdapter()
+ {
+ @Override
+ public void done(IJobChangeEvent event)
+ {
+ Job job = event.getJob();
+ if (job.belongsTo(StartVncServerLogic.VNC_SERVER_JOB_FAMILY))
+ {
+ IStatus result = event.getResult();
+ if (!result.isOK() && !(result.getSeverity() == IStatus.CANCEL))
+ {
+ stoppedWithFailure.add(job.getName());
+ }
+ }
+ }
+
+ @Override
+ public void scheduled(IJobChangeEvent event)
+ {
+ Job job = event.getJob();
+ if (job.belongsTo(StartVncServerLogic.VNC_SERVER_JOB_FAMILY))
+ {
+ stoppedWithFailure.remove(job.getName());
+ }
+ }
+ });
+ }
+
+ /**
+ * Handles internal IOExceptions caught by the protocol plugin during its execution.
+ *
+ * @see IProtocolExceptionHandler#handleIOException(ProtocolHandle, IOException)
+ */
+ public void handleIOException(ProtocolHandle handle, IOException e)
+ {
+ error("A socket was broken while communicating to server. Cause: " + e.getMessage());
+ handleException(handle);
+ }
+
+ /**
+ * Handles exceptions thrown by the protocol plugin when it detects an invalid message
+ * definition provided by the plugin which is extending it (in this case, the core plugin).
+ *
+ * @see IProtocolExceptionHandler#handleInvalidDefinitionException(ProtocolHandle, InvalidDefinitionException)
+ */
+ public void handleInvalidDefinitionException(ProtocolHandle handle, InvalidDefinitionException e)
+ {
+ // This exception should not happen, because the message definitions are provided
+ // by the development team.
+ warn("An invalid message definition was detected. Cause: " + e.getMessage());
+ handleException(handle);
+ }
+
+ /**
+ * Handles exceptions thrown by the protocol plugin when it detects that the data retrieved from
+ * the connection does not match the format defined in the message definition.
+ *
+ * @see IProtocolExceptionHandler#handleInvalidInputStreamDataException(ProtocolHandle, InvalidInputStreamDataException)
+ */
+ public void handleInvalidInputStreamDataException(ProtocolHandle handle,
+ InvalidInputStreamDataException e)
+ {
+ // If the data retrieved from the connection is not as expected (considering
+ // the message definition provided), there is a high chance of errors to happen.
+ // It is likely that the data from stream is no longer synchronized, so the
+ // exception handling for this case is to restart connection.
+
+ error("Some received data is not compatible with the expected definition. Restarting the protocol for synchronization.");
+ handleException(handle);
+ }
+
+ /**
+ * Handles exceptions thrown by the protocol plugin when it detects that the message
+ * provided for sending does not have enough or valid information, given a corresponding
+ * message definition
+ *
+ * @see IProtocolExceptionHandler#handleInvalidMessageException(ProtocolHandle, InvalidMessageException)
+ */
+ public void handleInvalidMessageException(ProtocolHandle handle, InvalidMessageException e)
+ {
+ // This exception should not happen, because the message object data is provided
+ // by the development team. Log only.
+ warn("A message was not constructed according to its definition. Cause: " + e.getMessage());
+ handleException(handle);
+ }
+
+ /**
+ * Handles exceptions thrown by any message handler when they discovers that it
+ * is not possible to handle the message and it is a fatal error for the protocol.
+ *
+ * @see IProtocolExceptionHandler#handleMessageHandleException(ProtocolHandle, MessageHandleException)
+ */
+ public void handleMessageHandleException(ProtocolHandle handle, MessageHandleException e)
+ {
+ // If a message handler throws a MessageHandleException, that means that it cannot
+ // continue. Restart the protocol to guarantee the synchronization.
+ error("A message handler has ended in error and has thrown an exception meaning the protocol cannot continue.");
+ handleException(handle);
+ }
+
+ /**
+ * Handles exceptions thrown by the protocol plugin when it is not possible to
+ * init the protocol, for example because the handshaking procedure has failed.
+ *
+ * @see IProtocolExceptionHandler#handleProtocolHandshakeException(ProtocolHandle, ProtocolHandshakeException)
+ */
+ public void handleProtocolHandshakeException(ProtocolHandle handle, ProtocolHandshakeException e)
+ {
+ error("Could not initialize the protocol.");
+ handleException(handle);
+ }
+
+ /**
+ * Handles exceptions thrown by any raw field handler when they discovers that it
+ * is not possible to handle the field and it is a fatal error for the protocol.
+ *
+ * @see IProtocolExceptionHandler#handleProtocolRawHandlingException(ProtocolHandle, ProtocolRawHandlingException)
+ */
+ public void handleProtocolRawHandlingException(ProtocolHandle handle,
+ ProtocolRawHandlingException e)
+ {
+ // This message should be thrown by raw field handlers when they cannot handle the
+ // raw field and need to abort the protocol execution. Restart the protocol to
+ // guarantee the synchronization.
+ error("A raw field handler has ended in error and has thrown an exception meaning the protocol cannot continue.");
+ handleException(handle);
+ }
+
+ /**
+ * This method will be called whenever an exception happens. It is important to find
+ * out if the failure happened during an start or restart procedure, so that we can
+ * do appropriate handling to each situation.
+ *
+ * @param handle The object that identifies the protocol instance
+ */
+ private void handleException(ProtocolHandle handle)
+ {
+ IAndroidEmulatorInstance instance = null;
+ if (lock.tryLock())
+ {
+ try
+ {
+ instance = EmulatorCoreUtils.getAndroidInstanceByHandle(handle);
+
+ try
+ {
+ debug("Check if device is online: " + instance);
+ if (instance instanceof ISerialNumbered)
+ {
+ String serialNumber = ((ISerialNumbered) instance).getSerialNumber();
+ AndroidLogicUtils.testDeviceStatus(serialNumber);
+ }
+ }
+ catch (Exception e)
+ {
+ error("Device is not online. Abort VNC session...");
+ abort(instance);
+ }
+
+ if ((handlingLevel == 3) && canRestartServer(instance))
+ {
+ handlingLevel--;
+ }
+
+ // Firstly, try to restart only the VNC client. If restarting the VNC client
+ // is not possible, try to restart the VNC server at the device and to connect
+ // to it again. If the start logic cannot be retrieved, delegate the decision
+ // to the user.
+
+ if (handlingLevel == 1)
+ {
+ restartClientOnly(handle);
+ handlingLevel++;
+ }
+ else if (handlingLevel == 2)
+ {
+ restartServerAndClient(instance, handle);
+ handlingLevel++;
+ }
+ else
+ {
+ if (delegateDecisionToUser(handle))
+ {
+ abort(instance);
+ }
+ }
+ }
+ catch (InstanceNotFoundException e)
+ {
+ // If the instance is not found, it means that the instance is stopped.
+ // In this case, a restart is not applicable.
+ }
+ finally
+ {
+ lock.unlock();
+ }
+ }
+ }
+
+ private boolean canRestartServer(IAndroidEmulatorInstance instance)
+ {
+ String name = StartVncServerLogic.VNC_SERVER_JOB_PREFIX + instance.getName();
+ return !stoppedWithFailure.contains(name);
+ }
+
+ private void restartClientOnly(final ProtocolHandle handle)
+ {
+ PluginProtocolActionDelegate.requestRestartProtocol(handle);
+
+ if (!checkThreadRunning)
+ {
+ Runnable r = new Runnable()
+ {
+ public void run()
+ {
+ checkThreadRunning = true;
+ while (checkThreadRunning)
+ {
+ if (PluginProtocolActionDelegate.isProtocolRunning(handle))
+ {
+ handlingLevel = 1;
+ checkThreadRunning = false;
+ }
+
+ try
+ {
+ Thread.sleep(500);
+ }
+ catch (InterruptedException e)
+ {
+ // Do nothing.
+ }
+ }
+ }
+ };
+ (new Thread(r)).start();
+ }
+ }
+
+ private void restartServerAndClient(IAndroidEmulatorInstance instance, ProtocolHandle handle)
+ {
+ try
+ {
+ if (instance instanceof IAndroidLogicInstance)
+ {
+ IAndroidLogicInstance logicInstance = (IAndroidLogicInstance) instance;
+ AbstractStartAndroidEmulatorLogic logic = logicInstance.getStartLogic();
+ logic.execute(logicInstance, LogicMode.TRANSFER_AND_CONNECT_VNC, logicInstance
+ .getTimeout(), new NullProgressMonitor());
+ try
+ {
+ Thread.sleep(1500);
+ }
+ catch (InterruptedException e)
+ {
+ // Do nothing.
+ }
+ PluginProtocolActionDelegate.requestRestartProtocol(handle);
+ }
+ else
+ {
+ handlingLevel = 3;
+ }
+ }
+ catch (Exception e1)
+ {
+ handlingLevel = 3;
+ }
+ }
+
+ /**
+ * In this method, the user is asked whether to retry or not. While the user does not
+ * give up, the instance retries to connect to the emulator. When the user gives up,
+ * the instance is stopped.
+ *
+ * @param handle The object that identifies the protocol instance
+ */
+ private boolean delegateDecisionToUser(ProtocolHandle handle)
+ {
+ boolean abort = true;
+ error("Cannot reconnect to VM. Asking to the user if he/she wants to retry.");
+ if (EclipseUtils.showQuestionDialog(EmulatorNLS.GEN_Question,
+ EmulatorNLS.QUESTION_AndroidExceptionHandler_ImpossibleToReconnect))
+ {
+ info("User chose to retry to reconnect to emulator VNC server.");
+ PluginProtocolActionDelegate.requestRestartProtocol(handle);
+ handlingLevel = 2;
+ abort = false;
+ }
+ return abort;
+ }
+
+ /**
+ *
+ */
+ private void abort(IAndroidEmulatorInstance instance)
+ {
+ info("User chose to stop the instance.");
+ try
+ {
+ checkThreadRunning = false;
+ instance.stop(true);
+ }
+ catch (InstanceStopException e1)
+ {
+ error("Error while running service for stopping virtual machine");
+ EclipseUtils.showErrorDialog(EmulatorNLS.GEN_Error,
+ EmulatorNLS.EXC_AndroidExceptionHandler_CannotRunStopService);
+ }
+ }
+
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/logic/AndroidLogicUtils.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/logic/AndroidLogicUtils.java
new file mode 100644
index 0000000..aad3b07
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/logic/AndroidLogicUtils.java
@@ -0,0 +1,360 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.emulator.logic;
+
+import static com.motorola.studio.android.common.log.StudioLogger.error;
+import static com.motorola.studio.android.common.log.StudioLogger.info;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.osgi.util.NLS;
+
+import com.motorola.studio.android.adt.DDMSFacade;
+import com.motorola.studio.android.adt.ISerialNumbered;
+import com.motorola.studio.android.common.exception.AndroidException;
+import com.motorola.studio.android.emulator.core.exception.InstanceStartException;
+import com.motorola.studio.android.emulator.core.exception.StartCancelledException;
+import com.motorola.studio.android.emulator.core.exception.StartTimeoutException;
+import com.motorola.studio.android.emulator.i18n.EmulatorNLS;
+
+/**
+ * This class is used as an utilities class for operations related to Android Devices
+ *
+ */
+public class AndroidLogicUtils
+{
+ private static final int INITIAL_VNC_PORT_VALUE = 5900;
+
+ public static final String ORIENTATION_BASE_COMMAND = "sendevent /dev/input/event0 ";
+
+ /**
+ * Execute the VM process
+ *
+ * @param cmd the command to be executed
+ * @return the VM process
+ *
+ * @throws AndroidException if the command failed to execute
+ */
+ public static Process executeProcess(final String cmd) throws AndroidException
+ {
+ try
+ {
+ info("Executing command " + cmd);
+ Process vmProcess = Runtime.getRuntime().exec(cmd);
+ return vmProcess;
+ }
+ catch (IOException e)
+ {
+ error("Falied to execute the command: " + cmd);
+ throw new AndroidException(NLS.bind(
+ EmulatorNLS.EXC_AndroidLogicUtils_CannotStartProcess, cmd));
+ }
+ }
+
+ /**
+ * Execute the VM process
+ *
+ * @param cmd the command to be executed, described as an array
+ * @return the VM process
+ *
+ * @throws AndroidException if the command failed to execute
+ */
+ public static Process executeProcess(final String[] cmd) throws AndroidException
+ {
+ String cmdString = "";
+ for (int i = 0; i < cmd.length; i++)
+ {
+ cmdString += cmd[i] + " ";
+
+ }
+
+ return executeProcess(cmd, cmdString);
+ }
+
+ /**
+ * Execute the VM process
+ *
+ * @param cmd The command to be executed, described as an array
+ * @param cmdToLog The command to be logged.
+ *
+ * @return the VM process
+ *
+ * @throws AndroidException if the command failed to execute
+ */
+ public static Process executeProcess(final String[] cmd, final String cmdToLog)
+ throws AndroidException
+ {
+ try
+ {
+ info("Executing command " + cmdToLog);
+ Process vmProcess = Runtime.getRuntime().exec(cmd);
+ return vmProcess;
+ }
+ catch (IOException e)
+ {
+ error("Falied to execute the command: " + cmd);
+ throw new AndroidException(NLS.bind(
+ EmulatorNLS.EXC_AndroidLogicUtils_CannotStartProcess, cmd));
+ }
+ }
+
+ /**
+ }
+
+ /**
+ * Checks if the user has canceled the VM startup
+ *
+ * @param monitor A progress monitor that will give the user feedback about this
+ * long running operation
+ * @param instanceHost The IP address of the started emulator instance
+ *
+ * @return True if the operation can proceed, false otherwise
+ *
+ * @throws StartCancelledException If the user has canceled the start process
+ */
+ public static void testCanceled(IProgressMonitor monitor) throws StartCancelledException
+ {
+ if (monitor.isCanceled())
+ {
+ info("Operation canceled by the user");
+ monitor.subTask(EmulatorNLS.MON_AndroidEmulatorStarter_Canceling);
+
+ throw new StartCancelledException(
+ EmulatorNLS.EXC_AndroidEmulatorStarter_EmulatorStartCanceled);
+ }
+ }
+
+ /**
+ * Checks if the timeout limit has reached
+ *
+ * @param timeoutLimit The system time limit that cannot be overtaken, in milliseconds
+ *
+ * @throws StartTimeoutException When the system time limit is overtaken
+ */
+ public static void testTimeout(long timeoutLimit, String timeoutErrorMessage)
+ throws StartTimeoutException
+ {
+ if (System.currentTimeMillis() > timeoutLimit)
+ {
+ error("The emulator was not up within the set timeout");
+ throw new StartTimeoutException(timeoutErrorMessage);
+ }
+ }
+
+ /**
+ * Get the relative timeout limit, which is the the current time plus the timeout value
+ *
+ * @param timeout timeout value (in milliseconds)
+ * @return Relative timeout limit
+ */
+ public static long getTimeoutLimit(int timeout)
+ {
+ return System.currentTimeMillis() + timeout;
+ }
+
+ /**
+ * Check if the given process is still up and running
+ *
+ * @param p process
+ * @throws InstanceStartException
+ */
+ public static void testProcessStatus(Process p) throws InstanceStartException
+ {
+
+ boolean isRunning;
+ int exitCode;
+
+ try
+ {
+ exitCode = p.exitValue();
+ isRunning = false;
+ }
+ catch (Exception e)
+ {
+ // emulator process is still running... so everything looks fine...
+ isRunning = true;
+ exitCode = 0;
+ }
+
+ if (!isRunning)
+ {
+ error("Emulator process is not running! Exit code:" + exitCode);
+ StringBuffer outBuf = null;
+ InputStream inStream = null;
+
+ int ch;
+
+ //Getting error output stream
+ String processAnswer = "";
+ inStream = p.getErrorStream();
+ outBuf = new StringBuffer();
+ try
+ {
+ while ((ch = inStream.read()) != -1)
+ {
+ outBuf.append((char) ch + "");
+ }
+ }
+ catch (IOException e)
+ {
+ error("Cannot read error output stream from Emulator proccess");
+ }
+
+ processAnswer = outBuf.toString();
+
+ if (processAnswer.length() == 0)
+ {
+ //if no error came from process, get standard output stream
+ inStream = p.getInputStream();
+ outBuf = new StringBuffer();
+ try
+ {
+ while ((ch = inStream.read()) != -1)
+ {
+ outBuf.append((char) ch + "");
+ }
+ }
+ catch (IOException e)
+ {
+ error("Cannot read standard output stream from Emulator proccess");
+ }
+
+ processAnswer = outBuf.toString();
+
+ }
+ String msg = EmulatorNLS.EXC_AndroidEmulatorStarter_ProcessTerminated;
+ msg += processAnswer;
+ throw new InstanceStartException(msg);
+ }
+
+ }
+
+ /**
+ * Kill the communication channel
+ *
+ * @param instance Android instance
+ */
+ public static void kill(IAndroidLogicInstance instance)
+ {
+ if (instance instanceof ISerialNumbered)
+ {
+ String serialNumber = ((ISerialNumbered) instance).getSerialNumber();
+ DDMSFacade.kill(serialNumber);
+ Process process = instance.getProcess();
+ if (process != null)
+ {
+ int tries = 0;
+ Integer exitValue = null;
+ while ((process != null) && (tries < 10) && (exitValue == null))
+ {
+ try
+ {
+ exitValue = process.exitValue();
+ }
+ catch (Throwable t)
+ {
+ tries++;
+ try
+ {
+ Thread.sleep(250);
+ }
+ catch (InterruptedException e)
+ {
+ //do nothing
+ }
+ }
+ }
+ process.destroy();
+ instance.setProcess(null);
+ }
+ }
+ }
+
+ /**
+ * Get the VNC port forward
+ *
+ * @param serial port number
+ * @return VNC port
+ */
+ public static int getVncServerPortFoward(String serial)
+ {
+ if (serial == null)
+ {
+ return 0;
+ }
+
+ int stringSize = serial.length();
+ String lastTwoNumbers = serial.substring(stringSize - 2, stringSize);
+
+ int port = INITIAL_VNC_PORT_VALUE;
+
+ try
+ {
+ port += Integer.valueOf(lastTwoNumbers);
+ }
+ catch (NumberFormatException e)
+ {
+ // do nothing (this should not happen)
+ }
+
+ return port;
+
+ }
+
+ public static int getEmulatorPort(String serial)
+ {
+ if (serial == null)
+ {
+ return 0;
+ }
+
+ int stringSize = serial.length();
+ String lastFourNumbers = serial.substring(stringSize - 4, stringSize);
+
+ int port = 0;
+
+ try
+ {
+ port = Integer.valueOf(lastFourNumbers);
+ }
+ catch (NumberFormatException e)
+ {
+ // do nothing (this should not happen)
+ }
+ return port;
+ }
+
+ /**
+ * Checks if the Device is still online...
+ * If the device is not online it is not possible to communicate with it.
+ * Notice it is a verification of the status of the Device wich may be different than the status of the Tml Instance...
+ *
+ * @param serialNumber serial number of the device
+ *
+ * @throws AndroidException If the device is not started
+ */
+ public static void testDeviceStatus(String serialNumber) throws AndroidException
+ {
+ if (!DDMSFacade.isDeviceOnline(serialNumber))
+ {
+ info("Device is offline: " + serialNumber);
+
+ throw new AndroidException(EmulatorNLS.EXC_AndroidLogicUtils_DeviceIsOffline);
+ }
+ }
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/logic/ConnectVncLogic.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/logic/ConnectVncLogic.java
new file mode 100644
index 0000000..37014db
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/logic/ConnectVncLogic.java
@@ -0,0 +1,227 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.emulator.logic;
+
+import static com.motorola.studio.android.common.log.StudioLogger.error;
+import static com.motorola.studio.android.common.log.StudioLogger.info;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.jobs.IJobChangeEvent;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.sequoyah.vnc.protocol.PluginProtocolActionDelegate;
+import org.eclipse.sequoyah.vnc.protocol.lib.IProtocolExceptionHandler;
+import org.eclipse.sequoyah.vnc.protocol.lib.ProtocolHandle;
+
+import com.motorola.studio.android.emulator.core.exception.InstanceStartException;
+import com.motorola.studio.android.emulator.core.exception.StartCancelledException;
+import com.motorola.studio.android.emulator.core.exception.StartTimeoutException;
+import com.motorola.studio.android.emulator.core.model.IAndroidEmulatorInstance;
+import com.motorola.studio.android.emulator.i18n.EmulatorNLS;
+
+/**
+ * This class contains the logic to stablish VNC connections
+ *
+ */
+public class ConnectVncLogic implements IAndroidLogic
+{
+ /**
+ * The port that is used to start the communication with the instance.
+ * It corresponds to the VNC display 1 port
+ */
+ private static final String LOCALHOST_IP_ADDRESS = "127.0.0.1";
+
+ public IJobChangeEvent vncServerDoneEvent = null;
+
+ /**
+ * Initialize by connecting to VNC
+ *
+ * @see com.motorola.studio.android.emulator.logic.IAndroidLogic#execute(IAndroidLogicInstance, int, IProgressMonitor)
+ */
+ public void execute(final IAndroidLogicInstance instance, final int timeout,
+ IProgressMonitor monitor) throws StartTimeoutException, StartCancelledException,
+ InstanceStartException
+ {
+ connectVnc(instance, timeout, monitor);
+ }
+
+ /**
+ * Connect to VNC
+ *
+ * @param instance instance to connect
+ * @param timeout timeout for the operation
+ * @param monitor monitor for this operation
+ *
+ * @throws InstanceStartException
+ */
+ private void connectVnc(IAndroidLogicInstance instance, int timeout, IProgressMonitor monitor)
+ throws StartTimeoutException, StartCancelledException, InstanceStartException
+ {
+ info("Trying to estabilish vnc connection with " + instance.getName());
+ long timeoutLimit = System.currentTimeMillis() + timeout;
+ try
+ {
+ startProtocol(instance, timeoutLimit, AndroidLogicUtils.getVncServerPortFoward(instance
+ .getInstanceIdentifier()), monitor);
+ }
+ catch (StartTimeoutException ise)
+ {
+ info("The protocol or the emulator services could not be launched. Stopping the instance.");
+ throw ise;
+ }
+ info("VNC Protocol is running for " + instance.getName());
+ }
+
+ /**
+ * Starts protocol connection
+ *
+ * @param instance The Android device instance
+ * @param timeoutLimit The timestamp of the time when timeout happens
+ * @param instanceHost The IP address of the started emulator instance
+ * @param monitor A progress monitor that will give the user feedback about this
+ * long running operation
+ *
+ * @throws InstanceStartException If some fatal error occurs during the start process,
+ * that may require status update at the clients
+ * @throws StartCancelledException If the user presses the "Cancel" button at the progress monitor
+ * @throws InstanceStartException
+ */
+ private void startProtocol(IAndroidEmulatorInstance instance, long timeoutLimit, int port,
+ IProgressMonitor monitor) throws StartTimeoutException, StartCancelledException,
+ InstanceStartException
+ {
+ try
+ {
+ monitor.beginTask(EmulatorNLS.MON_AndroidEmulatorStarter_ConnectingToEmulator, 100);
+ monitor.setTaskName(EmulatorNLS.MON_AndroidEmulatorStarter_ConnectingToEmulator);
+
+ testVncServer(instance);
+ AndroidLogicUtils.testCanceled(monitor);
+ requestStartProtocol(instance, port);
+ ProtocolHandle handle = instance.getProtocolHandle();
+
+ while (!PluginProtocolActionDelegate.isProtocolRunning(handle))
+ {
+ AndroidLogicUtils.testCanceled(monitor);
+
+ try
+ {
+ Thread.sleep(500);
+ }
+ catch (InterruptedException e1)
+ {
+ // Do nothing.
+ }
+
+ AndroidLogicUtils.testTimeout(timeoutLimit,
+ EmulatorNLS.EXC_AndroidEmulatorStarter_TimeoutWhileRunningProtocol);
+
+ }
+ monitor.worked(100);
+ }
+ finally
+ {
+ monitor.done();
+ }
+ }
+
+ /**
+ * Set if the job has been completed
+ *
+ * @param jobEvent event job
+ */
+ public void setVncServerDoneEvent(IJobChangeEvent jobEvent)
+ {
+ this.vncServerDoneEvent = jobEvent;
+ }
+
+ /**
+ * Test if the VNC server is up and running
+ *
+ * @param instance emulator instance
+ *
+ * @throws InstanceStartException
+ */
+ private void testVncServer(final IAndroidEmulatorInstance instance)
+ throws InstanceStartException
+ {
+ if (vncServerDoneEvent != null)
+ {
+ IStatus jobResult = vncServerDoneEvent.getResult();
+ String reason = "";
+ if (IStatus.ERROR == jobResult.getSeverity())
+ {
+ reason = jobResult.getMessage();
+ }
+ else if (Status.CANCEL_STATUS.equals(jobResult))
+ {
+ reason = EmulatorNLS.INFO_ConnectVncLogic_UserCancelledVncServerStart;
+ }
+
+ String message =
+ NLS.bind(EmulatorNLS.EXC_VncServerNotRunning, new String[]
+ {
+ instance.getName(), reason
+ });
+ throw new InstanceStartException(message);
+ }
+ }
+
+ /**
+ * Starts the protocol execution, connecting to the server accessible through
+ * the provided Android Emulator instance
+ *
+ * @param androidInstance The Android device instance
+ */
+ private void requestStartProtocol(IAndroidEmulatorInstance androidInstance, int port)
+ throws InstanceStartException
+ {
+ if (androidInstance != null)
+ {
+ ProtocolHandle handle = null;
+
+ try
+ {
+ // Start protocol and screen update
+ info("Requesting protocol start");
+ Map<String, Object> parameters = new HashMap<String, Object>();
+ parameters.put("password", "");
+ parameters.put("bypassProxy", new Boolean(true));
+ IProtocolExceptionHandler excHandler = new AndroidExceptionHandler();
+ handle =
+ PluginProtocolActionDelegate.requestStartProtocolAsClient("vncProtocol38",
+ excHandler, LOCALHOST_IP_ADDRESS, port, parameters);
+
+ androidInstance.setProtocolHandle(handle);
+ }
+ catch (Exception e)
+ {
+ error("There is an error at the protocol specification.");
+ throw new InstanceStartException(EmulatorNLS.EXC_CouldNotStartProtocol);
+ }
+ }
+ else
+ {
+ error("Could not start the protocol, because the provided instance is null");
+ throw new InstanceStartException(EmulatorNLS.EXC_CouldNotStartProtocol);
+ }
+ }
+
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/logic/ForwardVncPortLogic.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/logic/ForwardVncPortLogic.java
new file mode 100644
index 0000000..2884ff3
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/logic/ForwardVncPortLogic.java
@@ -0,0 +1,55 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.emulator.logic;
+
+import java.io.IOException;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+
+import com.motorola.studio.android.adt.DDMSFacade;
+import com.motorola.studio.android.adt.ISerialNumbered;
+import com.motorola.studio.android.common.exception.AndroidException;
+import com.motorola.studio.android.emulator.core.exception.InstanceStartException;
+
+public class ForwardVncPortLogic implements IAndroidLogic
+{
+
+ public void execute(IAndroidLogicInstance instance, int timeout, IProgressMonitor monitor)
+ throws IOException, InstanceStartException
+ {
+ int port = AndroidLogicUtils.getVncServerPortFoward(instance.getInstanceIdentifier());
+
+ if (instance instanceof ISerialNumbered)
+ {
+ String serialNumber = ((ISerialNumbered) instance).getSerialNumber();
+ try
+ {
+ AndroidLogicUtils.testDeviceStatus(serialNumber);
+ }
+ catch (AndroidException e)
+ {
+ throw new InstanceStartException(e.getMessage());
+ }
+
+ boolean forwardOk = DDMSFacade.createForward(serialNumber, port, 5901);
+ if (!forwardOk)
+ {
+ throw new IOException("Could not forward VNC port 5901 to " + port + " on "
+ + instance.getName());
+ }
+ }
+ }
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/logic/IAndroidLogic.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/logic/IAndroidLogic.java
new file mode 100644
index 0000000..d3028b2
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/logic/IAndroidLogic.java
@@ -0,0 +1,30 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.emulator.logic;
+
+import java.io.IOException;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+
+import com.motorola.studio.android.emulator.core.exception.InstanceStartException;
+import com.motorola.studio.android.emulator.core.exception.StartCancelledException;
+import com.motorola.studio.android.emulator.core.exception.StartTimeoutException;
+
+public interface IAndroidLogic
+{
+ void execute(IAndroidLogicInstance instance, int timeout, IProgressMonitor monitor)
+ throws InstanceStartException, StartTimeoutException, StartCancelledException, IOException;
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/logic/IAndroidLogicInstance.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/logic/IAndroidLogicInstance.java
new file mode 100644
index 0000000..5f67716
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/logic/IAndroidLogicInstance.java
@@ -0,0 +1,103 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.emulator.logic;
+
+import java.io.File;
+import java.util.List;
+import java.util.Properties;
+
+import com.motorola.studio.android.emulator.core.model.IAndroidEmulatorInstance;
+
+/**
+ * DESCRIPTION:
+ * This class adds to the Android Emulator instance contract
+ * some specific methods related to services logic.
+ *
+ * RESPONSIBILITY:
+ * Define which additional information is required from a services to
+ * plug to the Android Emulator viewer.
+ *
+ * COLABORATORS:
+ * None.
+ *
+ * USAGE:
+ * Use the methods to retrieve information from a Android Emulator device instance
+ */
+public interface IAndroidLogicInstance extends IAndroidEmulatorInstance
+{
+ /**
+ * Get the command lines arguments for the instance
+ *
+ * @return list of command line arguments
+ */
+ String getCommandLineArguments();
+
+ /**
+ * Get the command lines arguments for the instance, returned as a Property object
+ *
+ * @return list of command line arguments
+ */
+ Properties getCommandLineArgumentsAsProperties();
+
+ /**
+ * Get the Start logic for this instance, which is the commands used to start the instance
+ *
+ * @return Start logic class
+ */
+ AbstractStartAndroidEmulatorLogic getStartLogic();
+
+ /**
+ * Check if there is a device connected to this instance
+ *
+ * @return true if there is a device connected, false otherwise
+ */
+ boolean hasDevice();
+
+ /**
+ * Get the reference to the File that point to the filesystem location where the
+ * user data of the VM is.
+ *
+ * @return the File object that references the filesystem location where the userdata
+ * of the given VM should be. Returns a null reference if SDK is not configured
+ * or if there is no VM with the given name.
+ */
+ File getUserdata();
+
+ /**
+ * Get the reference to the files in the VM folder that contain state data.
+ *
+ * @return File objects that reference files in the VM folder that contain state data.
+ */
+ List<File> getStateData();
+
+ /**
+ * Tests if there is a userdata file for this android device instance or if it is clean,
+ * i.e, there is no user data file for this Android Device Instance.
+ *
+ * @return True if there is no working copy at that location; false otherwise
+ */
+ boolean isClean();
+
+ /**
+ * Get the timeout value to start the instance
+ *
+ * @return timeout value
+ */
+ int getTimeout();
+
+ File getSnapshotOriginalFilePath();
+
+} \ No newline at end of file
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/logic/StartEmulatorProcessLogic.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/logic/StartEmulatorProcessLogic.java
new file mode 100644
index 0000000..e71eba0
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/logic/StartEmulatorProcessLogic.java
@@ -0,0 +1,523 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.emulator.logic;
+
+import static com.motorola.studio.android.common.log.StudioLogger.debug;
+import static com.motorola.studio.android.common.log.StudioLogger.info;
+
+import java.io.File;
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Properties;
+import java.util.StringTokenizer;
+
+import org.eclipse.core.net.proxy.IProxyData;
+import org.eclipse.core.net.proxy.IProxyService;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.ui.IViewPart;
+import org.osgi.framework.ServiceReference;
+
+import com.android.ide.eclipse.adt.AdtPlugin;
+import com.android.ide.eclipse.adt.internal.preferences.AdtPrefs;
+import com.motorola.studio.android.adt.DDMSFacade;
+import com.motorola.studio.android.adt.SdkUtils;
+import com.motorola.studio.android.common.exception.AndroidException;
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.common.preferences.DialogWithToggleUtils;
+import com.motorola.studio.android.common.utilities.EclipseUtils;
+import com.motorola.studio.android.emulator.EmulatorPlugin;
+import com.motorola.studio.android.emulator.core.exception.InstanceStartException;
+import com.motorola.studio.android.emulator.core.exception.InstanceStopException;
+import com.motorola.studio.android.emulator.core.exception.StartCancelledException;
+import com.motorola.studio.android.emulator.core.exception.StartTimeoutException;
+import com.motorola.studio.android.emulator.device.IDevicePropertiesConstants;
+import com.motorola.studio.android.emulator.i18n.EmulatorNLS;
+import com.motorola.studio.android.nativeos.IDevicePropertiesOSConstants;
+import com.motorola.studio.android.nativeos.NativeUIUtils;
+
+@SuppressWarnings("restriction")
+public class StartEmulatorProcessLogic implements IAndroidLogic
+{
+ /**
+ *
+ */
+ private static final String EMULATOR_NO_SNAPSHOT_LOAD = "-no-snapshot-load";
+
+ /**
+ *
+ */
+ private static final String EMULATOR_NO_SNAPSHOT_SAVE = "-no-snapshot-save";
+
+ /**
+ *
+ */
+ private static final String EMULATOR_HTTP_PROXY_PARAMETER = "-http-proxy";
+
+ // Proxy constants
+ private static final String PROXY_AT = "@";
+
+ private static final String PROXY_COLON = ":";
+
+ private static final String PROXY_HTTP = "http://";
+
+ private static final String EMULATOR_VIEW = "com.motorola.studio.android.emulator.androidView";
+
+ // Strings used to build the command line for launching the emulator process
+ private static final String ARM_EMULATOR_RELATIVE_PATH = "/tools/emulator-arm";
+
+ private static final String x86_EMULATOR_RELATIVE_PATH = "/tools/emulator-x86";
+
+ private static final String EMULATOR_RELATIVE_PATH = "/tools/emulator";
+
+ private static final String EMULATOR_VM_PARAMETER = "-avd";
+
+ private static String selectedEmulatorPath = "";
+
+ public void execute(final IAndroidLogicInstance instance, int timeout, IProgressMonitor monitor)
+ throws InstanceStartException, StartTimeoutException, StartCancelledException
+ {
+
+ long timeoutLimit = AndroidLogicUtils.getTimeoutLimit(timeout);
+
+ info("Starting the Android Emulator process: " + instance);
+ instance.setWindowHandle(0);
+
+ File userData = instance.getUserdata();
+
+ if (userData != null)
+ {
+ File userdataDir = userData.getParentFile();
+ if ((userdataDir != null) && (!userdataDir.exists()))
+ {
+ userdataDir.mkdirs();
+ }
+ }
+
+ selectedEmulatorPath = retrieveEmulatorExecutableName(instance);
+
+ File emulatorExe = new File(SdkUtils.getSdkPath(), selectedEmulatorPath);
+
+ List<String> cmdList = new LinkedList<String>();
+
+ cmdList.add(emulatorExe.getAbsolutePath());
+ cmdList.add(EMULATOR_VM_PARAMETER);
+ cmdList.add(instance.getName());
+
+ Properties propArgs = instance.getCommandLineArgumentsAsProperties();
+ IPreferenceStore store = AdtPlugin.getDefault().getPreferenceStore();
+ String adtEmuOptions = store.getString(AdtPrefs.PREFS_EMU_OPTIONS);
+
+ StringTokenizer adtOptionsTokenizer = new StringTokenizer(adtEmuOptions, " ");
+ while (adtOptionsTokenizer.hasMoreTokens())
+ {
+ String nextToken = adtOptionsTokenizer.nextToken();
+ cmdList.add(nextToken);
+ }
+
+ for (Object key : propArgs.keySet())
+ {
+ String value = propArgs.getProperty(key.toString());
+
+ if (key.equals("other"))
+ {
+ StringTokenizer stringTokenizer = new StringTokenizer(value, " ");
+ while (stringTokenizer.hasMoreTokens())
+ {
+ cmdList.add(stringTokenizer.nextToken());
+ }
+ }
+ else
+ {
+ if ((value.trim().length() > 0) && !value.equals(Boolean.TRUE.toString()))
+ {
+ cmdList.add(key.toString());
+ if (Platform.getOS().equals(Platform.OS_MACOSX))
+ {
+ if (value.contains(" "))
+ {
+ value = "\"" + value + "\"";
+ }
+ }
+ else
+ {
+ if (value.contains("\\"))
+ {
+ value = value.replace("\\", "\\\\");
+ }
+
+ if (value.contains(" "))
+ {
+ value = value.replace(" ", "\\ ");
+ }
+ }
+
+ cmdList.add(value);
+ }
+ else if ((value.trim().length() > 0) && value.equals(Boolean.TRUE.toString()))
+ {
+ cmdList.add(key.toString());
+ }
+ }
+ }
+
+ // add proxy in case it is needed
+ Properties properties = instance.getProperties();
+ if (properties != null)
+ {
+ String useProxy =
+ properties.getProperty(IDevicePropertiesConstants.useProxy,
+ IDevicePropertiesConstants.defaultUseProxyValue);
+ if (Boolean.TRUE.toString().equals(useProxy))
+ {
+ addEmulatorProxyParameter(cmdList);
+ }
+ }
+
+ StringBuffer cmdLog = new StringBuffer("");
+
+ boolean httpProxyParamFound = false;
+ boolean logHttpProxyUsage = false;
+ for (String param : cmdList)
+ {
+ // Do not log -http-proxy information
+ if (!httpProxyParamFound)
+ {
+ if (!param.equals(EMULATOR_HTTP_PROXY_PARAMETER))
+ {
+ if (param.startsWith(emulatorExe.getAbsolutePath()))
+ {
+ // do not log emulator full path
+ cmdLog.append(selectedEmulatorPath + " ");
+ }
+ else
+ {
+ cmdLog.append(param + " ");
+ }
+ }
+ else
+ {
+ httpProxyParamFound = true;
+ logHttpProxyUsage = true;
+ }
+ }
+ else
+ {
+ httpProxyParamFound = false;
+ }
+ }
+
+ // Append http proxy usage to log
+ if (logHttpProxyUsage)
+ {
+ cmdLog.append("\nProxy settings are being used by the started emulator (-http-proxy parameter).");
+ }
+ // add command to not start from snapshot
+ if (properties != null)
+ {
+ String startFromSnapshot =
+ properties.getProperty(IDevicePropertiesConstants.startFromSnapshot,
+ IDevicePropertiesConstants.defaultstartFromSnapshotValue);
+ if (Boolean.FALSE.toString().equals(startFromSnapshot))
+ {
+ cmdList.add(EMULATOR_NO_SNAPSHOT_LOAD);
+ }
+ }
+
+ // Add the command to not save snapshot
+ if (properties != null)
+ {
+ String saveSnapshot =
+ properties.getProperty(IDevicePropertiesConstants.saveSnapshot,
+ IDevicePropertiesConstants.defaulSaveSnapshot);
+ if (Boolean.FALSE.toString().equals(saveSnapshot))
+ {
+ cmdList.add(EMULATOR_NO_SNAPSHOT_SAVE);
+ }
+ }
+
+ Process p;
+ try
+ {
+ p = AndroidLogicUtils.executeProcess(cmdList.toArray(new String[0]), cmdLog.toString());
+ }
+ catch (AndroidException e)
+ {
+ throw new InstanceStartException(e);
+ }
+ info("Wait until and emulator with the VM " + instance.getName() + " is up ");
+
+ AndroidLogicUtils.testProcessStatus(p);
+ instance.setProcess(p);
+ instance.setComposite(null);
+
+ final String avdName = instance.getName();
+
+ if (!Platform.getOS().equals(Platform.OS_MACOSX))
+ {
+ Collection<IViewPart> openedAndroidViews =
+ EclipseUtils.getAllOpenedViewsWithId(EMULATOR_VIEW);
+
+ if (!openedAndroidViews.isEmpty())
+ {
+ Runnable runnable = new Runnable()
+ {
+
+ public void run()
+ {
+ long windowHandle = -1;
+ long timeOutToFindWindow = System.currentTimeMillis() + 30000;
+
+ do
+ {
+ try
+ {
+ Thread.sleep(250);
+ }
+ catch (InterruptedException e)
+ {
+ // do nothing
+ }
+
+ try
+ {
+ AndroidLogicUtils.testTimeout(timeOutToFindWindow, "");
+ }
+ catch (StartTimeoutException e)
+ {
+ debug("Emulator window could not be found, instance :" + avdName);
+ break;
+ }
+
+ try
+ {
+ int port =
+ AndroidLogicUtils.getEmulatorPort(DDMSFacade
+ .getSerialNumberByName(instance.getName()));
+ if (port > 0)
+ {
+ windowHandle =
+ NativeUIUtils.getWindowHandle(instance.getName(), port);
+ }
+
+ }
+ catch (Exception t)
+ {
+ t.getCause().getMessage();
+ System.out.println(t.getCause().getMessage());
+ }
+ }
+ while (windowHandle <= 0);
+
+ if (windowHandle > 0)
+ {
+ instance.setWindowHandle(windowHandle);
+ NativeUIUtils.hideWindow(windowHandle);
+ }
+ }
+ };
+ Thread getHandleThread = new Thread(runnable, "Window Handle Thread");
+ getHandleThread.start();
+ }
+ }
+
+ if (instance.getProperties()
+ .getProperty(IDevicePropertiesOSConstants.useVnc, NativeUIUtils.getDefaultUseVnc())
+ .equals(Boolean.TRUE.toString()))
+ {
+ do
+ {
+ try
+ {
+ Thread.sleep(450);
+ }
+ catch (InterruptedException e)
+ {
+ // do nothing
+ }
+
+ AndroidLogicUtils.testCanceled(monitor);
+ try
+ {
+ AndroidLogicUtils.testTimeout(timeoutLimit,
+ NLS.bind(EmulatorNLS.EXC_TimeoutWhileStarting, avdName));
+ }
+ catch (StartTimeoutException e)
+ {
+ debug("Emulator start timeout has been reached, instance :" + avdName
+ + " has device: " + instance.hasDevice() + "isOnline? "
+ + DDMSFacade.isDeviceOnline(DDMSFacade.getSerialNumberByName(avdName)));
+ throw e;
+ }
+ }
+ while (!isEmulatorReady(avdName));
+
+ }
+
+ Thread t = new Thread("Process Error")
+ {
+ @Override
+ public void run()
+ {
+ boolean shouldTryAgain = true;
+ for (int i = 0; (i < 90) && shouldTryAgain; i++)
+ {
+ try
+ {
+ sleep(500);
+ Process p = instance.getProcess();
+ if (p != null)
+ {
+ AndroidLogicUtils.testProcessStatus(p);
+ }
+ }
+ catch (Exception e)
+ {
+ StudioLogger.info(StartEmulatorProcessLogic.class,
+ "Trying to stop the emulator process: execution stopped too early");
+ DialogWithToggleUtils.showError(
+ EmulatorPlugin.EMULATOR_UNEXPECTEDLY_STOPPED,
+ EmulatorNLS.GEN_Error, NLS.bind(
+ EmulatorNLS.ERR_AndroidLogicPlugin_EmulatorStopped,
+ instance.getName()));
+ shouldTryAgain = false;
+ try
+ {
+ instance.stop(true);
+ }
+ catch (InstanceStopException ise)
+ {
+ StudioLogger.error(StartEmulatorProcessLogic.class,
+ "Error trying to stop instance on process error", ise);
+ }
+ }
+ }
+ }
+ };
+ t.start();
+
+ debug("Emulator instance is now up and running... " + instance);
+ }
+
+ /**
+ * retrives the emulator executable name according to abi type property
+ *
+ * @param instance
+ * @return
+ */
+ private static String retrieveEmulatorExecutableName(IAndroidLogicInstance instance)
+ {
+ String emulatorPath = null;
+
+ Properties prop = instance.getProperties();
+ String abiType = prop.getProperty("Abi_Type");
+
+ if ((abiType == null) || (abiType.equals("")))
+ {
+ emulatorPath = EMULATOR_RELATIVE_PATH;
+ }
+ else if (abiType.toLowerCase().contains("arm"))
+ {
+ emulatorPath = ARM_EMULATOR_RELATIVE_PATH;
+ }
+ else
+ {
+ emulatorPath = x86_EMULATOR_RELATIVE_PATH;
+ }
+
+ File emulatorExe = new File(SdkUtils.getSdkPath(), emulatorPath + ".exe");
+
+ if (!emulatorExe.exists())
+ {
+ emulatorPath = EMULATOR_RELATIVE_PATH;
+ }
+
+ return emulatorPath;
+ }
+
+ /**
+ * Retrieve the Proxy service.
+ *
+ * @return IProxyService instance.
+ */
+ @SuppressWarnings({
+ "rawtypes", "unchecked"
+ })
+ private IProxyService retrieveProxyService()
+ {
+ IProxyService proxyService = null;
+
+ ServiceReference service =
+ EmulatorPlugin.getDefault().getBundle().getBundleContext()
+ .getServiceReference(IProxyService.class.getCanonicalName());
+ if (service != null)
+ {
+ proxyService =
+ (IProxyService) EmulatorPlugin.getDefault().getBundle().getBundleContext()
+ .getService(service);
+ }
+
+ return proxyService;
+ }
+
+ /**
+ * Add the http-proxy parameter to the emulator command line.
+ *
+ * @param cmdList
+ * List holding the commands to be called.
+ */
+ private void addEmulatorProxyParameter(List<String> cmdList)
+ {
+ IProxyService proxyService = retrieveProxyService();
+ if (proxyService != null)
+ {
+ String host = proxyService.getProxyData(IProxyData.HTTP_PROXY_TYPE).getHost();
+ int port = proxyService.getProxyData(IProxyData.HTTP_PROXY_TYPE).getPort();
+ boolean isAuthenticationRequired =
+ proxyService.getProxyData(IProxyData.HTTP_PROXY_TYPE)
+ .isRequiresAuthentication();
+ String userId = proxyService.getProxyData(IProxyData.HTTP_PROXY_TYPE).getUserId();
+ String password = proxyService.getProxyData(IProxyData.HTTP_PROXY_TYPE).getPassword();
+
+ // there must be a host in order to access via proxy
+ if (host != null)
+ {
+ cmdList.add(EMULATOR_HTTP_PROXY_PARAMETER);
+
+ // add proxy info to the command list - authentication needed
+ if (isAuthenticationRequired)
+ {
+ cmdList.add(PROXY_HTTP + userId + PROXY_COLON + password + PROXY_AT + host
+ + PROXY_COLON + Integer.valueOf(port).toString());
+ }
+ // add proxy info to the command list - no authentication needed
+ else
+ {
+ cmdList.add(PROXY_HTTP + host + PROXY_COLON + Integer.valueOf(port).toString());
+ }
+ }
+ }
+ }
+
+ private boolean isEmulatorReady(String avdName)
+ {
+ String serialNum = DDMSFacade.getSerialNumberByName(avdName);
+ return DDMSFacade.isDeviceOnline(serialNum);
+ }
+} \ No newline at end of file
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/logic/StartVncServerLogic.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/logic/StartVncServerLogic.java
new file mode 100644
index 0000000..b28cc9a
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/logic/StartVncServerLogic.java
@@ -0,0 +1,229 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.emulator.logic;
+
+import static com.motorola.studio.android.common.log.StudioLogger.error;
+import static com.motorola.studio.android.common.log.StudioLogger.info;
+
+import java.io.IOException;
+import java.util.Collection;
+import java.util.LinkedList;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.jobs.IJobChangeListener;
+import org.eclipse.core.runtime.jobs.IJobManager;
+import org.eclipse.core.runtime.jobs.ISchedulingRule;
+import org.eclipse.core.runtime.jobs.Job;
+
+import com.motorola.studio.android.adt.DDMSFacade;
+import com.motorola.studio.android.adt.ISerialNumbered;
+import com.motorola.studio.android.common.exception.AndroidException;
+import com.motorola.studio.android.emulator.EmulatorPlugin;
+import com.motorola.studio.android.emulator.core.exception.InstanceStartException;
+import com.motorola.studio.android.emulator.core.exception.StartCancelledException;
+import com.motorola.studio.android.emulator.core.exception.StartTimeoutException;
+import com.motorola.studio.android.emulator.core.model.IAndroidEmulatorInstance;
+
+/**
+ * This class contains the logic to start the VNC server on the given Emulator.
+ */
+public final class StartVncServerLogic implements IAndroidLogic
+{
+ public static final String VNC_SERVER_JOB_PREFIX = "VNC Server - ";
+
+ public static final Object VNC_SERVER_JOB_FAMILY = new Object();
+
+ /**
+ * Sequence of commands that must be executed on the emulator to start the VNC server
+ */
+ private final Collection<String> remoteCommands = new LinkedList<String>();
+
+ /**
+ * Collection of listeners for the job executing the VNC server.
+ */
+ private final Collection<IJobChangeListener> listeners = new LinkedList<IJobChangeListener>();
+
+ /**
+ * Executes the logic to start the vnc server.
+ */
+ public void execute(final IAndroidLogicInstance instance, int timeout,
+ final IProgressMonitor monitor) throws InstanceStartException, StartTimeoutException,
+ StartCancelledException, IOException
+ {
+ cancelCurrentVncServerJobs(instance);
+
+ // Creates and starts a job that will keep running as long as the VNC server is up on that Emulator instance.
+ // add listeners that will receive notifications about the Job life-cycle.
+ VncServerJob vncServerJob = new VncServerJob(instance, getRemoteCommands());
+ for (IJobChangeListener vncServerListener : listeners)
+ {
+ vncServerJob.addJobChangeListener(vncServerListener);
+ }
+ vncServerJob.schedule();
+
+ }
+
+ /**
+ * Cancel any VncServerJob that is currently running the VNC server on the given emulator instance.
+ * @param instance, the emulator instances where VNC server execution must be canceled.
+ **/
+ public static void cancelCurrentVncServerJobs(IAndroidEmulatorInstance instance)
+ {
+ // stop the previous VNC Server job for this instance if any...
+ IJobManager manager = Job.getJobManager();
+ Job[] allVncJobs = manager.find(StartVncServerLogic.VNC_SERVER_JOB_FAMILY);
+ if (allVncJobs.length > 0)
+ {
+ for (Job job : allVncJobs)
+ {
+ if (job.getName().equals(
+ StartVncServerLogic.VNC_SERVER_JOB_PREFIX + instance.getName()))
+ {
+ info("Cancel execution of the VNC Server on " + instance);
+ job.cancel();
+ }
+ }
+ }
+ }
+
+ /**
+ * Add job listener to receive state-change notifications from the job that runs the VNC Server.
+ * @param vncServerListener job listener that willl receive state change notifications from the VNC Serever job.
+ */
+ public void addVncServerJobListener(IJobChangeListener vncServerListener)
+ {
+ listeners.add(vncServerListener);
+ }
+
+ /**
+ * Add a command to be executed in the process of starting the VNC Server on the Emulator.
+ * @param remoteCommand
+ */
+ public void addRemoteCommand(String remoteCommand)
+ {
+ remoteCommands.add(remoteCommand);
+ }
+
+ /**
+ * Get the list of commands to be executed on the Emulator in order to start the VNC Server.
+ * @return the sequence of commands that must be executed on the Emulator to start the VNC Server.
+ */
+ public Collection<String> getRemoteCommands()
+ {
+ return remoteCommands;
+ }
+
+}
+
+/**
+ * Job that executes the VNC Server.
+ * It will keep running as long as the VNC Server process is running on the Emulator.
+ */
+class VncServerJob extends Job implements ISchedulingRule
+{
+ private String serialNumber;
+
+ /**
+ * Sequence of commands that must be executed on the emulator to start the VNC server
+ */
+ private final Collection<String> remoteCommands;
+
+ /**
+ * Creates a new job to execute the VNC server on the given emulator instance.
+ * @param instance, emulator instance where the VNC server will be started.
+ * @param remoteCommands, sequence of commands that must be executed on the given emulator instance to start the VNC Server.
+ * @throws InstanceStartException
+ */
+ public VncServerJob(IAndroidLogicInstance instance, Collection<String> remoteCommands)
+ throws InstanceStartException
+ {
+ super(StartVncServerLogic.VNC_SERVER_JOB_PREFIX + instance.getName());
+
+ this.serialNumber = ((ISerialNumbered) instance).getSerialNumber();
+
+ try
+ {
+ AndroidLogicUtils.testDeviceStatus(serialNumber);
+ }
+ catch (AndroidException e)
+ {
+ throw new InstanceStartException(e.getMessage());
+ }
+
+ this.remoteCommands = remoteCommands;
+ setSystem(true);
+ setRule(this);
+ }
+
+ /**
+ * @see org.eclipse.core.runtime.jobs.Job#run(IProgressMonitor)
+ */
+ @Override
+ public IStatus run(IProgressMonitor monitor)
+ {
+ IStatus status = Status.OK_STATUS;
+ try
+ {
+ info("Executing VNC Server on " + serialNumber);
+ AndroidLogicUtils.testDeviceStatus(serialNumber);
+ DDMSFacade.execRemoteApp(serialNumber, remoteCommands, monitor);
+
+ if (monitor.isCanceled())
+ {
+ status = Status.CANCEL_STATUS;
+ }
+ }
+ catch (Exception e)
+ {
+ String errorMessage = "Error while trying to run the VNC server on " + serialNumber;
+ error(errorMessage + " " + e.getMessage());
+ status = new Status(IStatus.CANCEL, EmulatorPlugin.PLUGIN_ID, errorMessage, e);
+ }
+
+ info("Finished the execution of the VNC Server on " + serialNumber + " with status "
+ + status);
+
+ return status;
+ }
+
+ /**
+ * @see org.eclipse.core.runtime.jobs.Job#belongsTo(Object)
+ */
+ @Override
+ public boolean belongsTo(Object family)
+ {
+ return StartVncServerLogic.VNC_SERVER_JOB_FAMILY.equals(family);
+ }
+
+ public boolean contains(ISchedulingRule rule)
+ {
+ boolean contains = false;
+ if (rule instanceof VncServerJob)
+ {
+ VncServerJob otherVncServerJob = (VncServerJob) rule;
+ contains = otherVncServerJob.serialNumber.equals(serialNumber);
+ }
+
+ return contains;
+ }
+
+ public boolean isConflicting(ISchedulingRule rule)
+ {
+ return contains(rule);
+ }
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/logic/TransferFilesLogic.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/logic/TransferFilesLogic.java
new file mode 100644
index 0000000..9df8eb6
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/logic/TransferFilesLogic.java
@@ -0,0 +1,97 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.emulator.logic;
+
+import static com.motorola.studio.android.common.log.StudioLogger.error;
+
+import java.io.IOException;
+import java.util.Collection;
+import java.util.LinkedList;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+
+import com.motorola.studio.android.adt.DDMSFacade;
+import com.motorola.studio.android.emulator.core.exception.InstanceStartException;
+import com.motorola.studio.android.emulator.core.exception.StartCancelledException;
+import com.motorola.studio.android.emulator.core.exception.StartTimeoutException;
+import com.motorola.studio.android.emulator.i18n.EmulatorNLS;
+
+public class TransferFilesLogic implements IAndroidLogic
+{
+ private String localDir = "";
+
+ private String remoteDir = "";
+
+ private final Collection<String> filenames = new LinkedList<String>();
+
+ public void execute(IAndroidLogicInstance instance, int timeout, IProgressMonitor monitor)
+ throws InstanceStartException, StartCancelledException, StartTimeoutException,
+ IOException
+ {
+ if ((instance != null) && (timeout > 0) && (localDir != null) && (!"".equals(localDir))
+ && (remoteDir != null) && ("".equals(remoteDir)))
+ {
+ error("Cannot transfer files because the parameters provided are not as expected.");
+ throw new InstanceStartException(
+ EmulatorNLS.ERR_TransferFilesLogic_NotEnoughInformation);
+ }
+
+ String serialNumber = DDMSFacade.getSerialNumberByName(instance.getName());
+ IStatus status =
+ DDMSFacade.pushFiles(serialNumber, getLocalDir(), getFilenames(), getRemoteDir(),
+ timeout, monitor, null);
+ if (status.getSeverity() == IStatus.CANCEL)
+ {
+ throw new StartCancelledException();
+ }
+ else if (status.getSeverity() == IStatus.ERROR)
+ {
+ throw new InstanceStartException(status.getMessage());
+ }
+ }
+
+ public void setLocalDir(String localDir)
+ {
+ this.localDir = localDir;
+ }
+
+ public String getLocalDir()
+ {
+ return localDir;
+ }
+
+ public void addFilename(String filename)
+ {
+ filenames.add(filename);
+ }
+
+ public Collection<String> getFilenames()
+ {
+ return filenames;
+ }
+
+ public void setRemoteDir(String remoteDir)
+ {
+ this.remoteDir = remoteDir;
+ }
+
+ public String getRemoteDir()
+ {
+ return remoteDir;
+ }
+
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/logic/reset/AndroidEmulatorReseter.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/logic/reset/AndroidEmulatorReseter.java
new file mode 100644
index 0000000..4f0ad44
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/logic/reset/AndroidEmulatorReseter.java
@@ -0,0 +1,189 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.emulator.logic.reset;
+
+import static com.motorola.studio.android.common.log.StudioLogger.error;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.osgi.util.NLS;
+
+import com.motorola.studio.android.emulator.EmulatorPlugin;
+import com.motorola.studio.android.emulator.i18n.EmulatorNLS;
+import com.motorola.studio.android.emulator.logic.IAndroidLogicInstance;
+
+/**
+ * DESCRIPTION:
+ * This class contains the business layer of the Android
+ * Emulator reset procedure
+ *
+ * RESPONSIBILITY:
+ * Reset any Android Emulator
+ *
+ * COLABORATORS:
+ * None.
+ *
+ * USAGE:
+ * Use the public method to reset a Android Emulator
+ */
+public class AndroidEmulatorReseter
+{
+ /**
+ * Resets a Android Emulator based at the provided folder
+ *
+ * @param userDataFolder The folder where a working copy of the emulator is located
+ * @param force Perform the reset without questioning the user
+ *
+ * @return IStatus the status of the operation
+ */
+
+ static String SNAPSHOT_FILE_NAME = "snapshots.img";
+
+ public static IStatus resetInstance(IAndroidLogicInstance instance)
+ {
+ IStatus resetStatus = Status.OK_STATUS;
+
+ File userData = instance.getUserdata();
+ List<File> stateData = instance.getStateData();
+
+ if ((userData != null) || (stateData != null))
+ {
+ List<File> allFiles = new ArrayList<File>();
+ if (stateData != null)
+ {
+ allFiles.addAll(stateData);
+ }
+ if (userData != null)
+ {
+ allFiles.add(userData);
+ }
+
+ for (File file : allFiles)
+ {
+ if (file.exists())
+ {
+ if (!file.delete())
+ {
+ error("There was an error when trying to remove the emulator instance user data files");
+ resetStatus =
+ new Status(
+ IStatus.ERROR,
+ EmulatorPlugin.PLUGIN_ID,
+ NLS.bind(
+ EmulatorNLS.EXC_AndroidEmulatorReseter_ErrorWhilePerformingDeleteOperation,
+ new Path(file.getPath()).removeLastSegments(1)
+ .toString()));
+ break;
+ }
+ }
+ }
+
+ // When the snapshots file is missing or corrupted after the reset, the snapshot operation will not work properly
+ // (when start and then closing the AVD after a reset operation,
+ /// the snapshots file will not be saved), that is why the error message should be shown.
+
+ if ((allFiles != null) && (allFiles.size() > 0))
+ {
+ File snapshot = instance.getSnapshotOriginalFilePath();
+
+ String snapshotToPath =
+ allFiles.get(0).getParentFile() + File.separator + SNAPSHOT_FILE_NAME;
+
+ File snapshotToFile = new File(snapshotToPath);
+
+ if ((snapshot != null) && (snapshotToFile.exists()))
+ {
+
+ BufferedInputStream bis = null;
+ BufferedOutputStream bos = null;
+
+ try
+ {
+ bis = new BufferedInputStream((new FileInputStream(snapshot)));
+ bos = new BufferedOutputStream(new FileOutputStream(snapshotToFile));
+
+ int c;
+ while ((c = bis.read()) >= 0)
+ {
+ bos.write(c);
+ }
+ }
+ catch (Exception e)
+ {
+ error("Error while copying the original snapshot file to the avd that is being reseted:"
+ + e.getMessage());
+ if (resetStatus.equals(Status.OK_STATUS))
+ {
+ resetStatus =
+ new Status(
+ IStatus.ERROR,
+ EmulatorPlugin.PLUGIN_ID,
+ NLS.bind(
+ EmulatorNLS.EXC_AndroidEmulatorReseter_ErrorWhilePerformingSnapshotCopyOperation,
+ snapshot.getPath(), allFiles.get(0)
+ .getParentFile()));
+ }
+ else
+ {
+
+ resetStatus =
+ new Status(
+ IStatus.ERROR,
+ EmulatorPlugin.PLUGIN_ID,
+ NLS.bind(
+ EmulatorNLS.EXC_AndroidEmulatorReseter_ErrorWhilePerformingDeleteSnapshotOperation,
+ allFiles.get(0).getParentFile(),
+ snapshot.getPath()));
+ }
+ }
+ finally
+ {
+ try
+ {
+ if (bis != null)
+ {
+ bis.close();
+ }
+
+ if (bos != null)
+ {
+ bos.close();
+ }
+ }
+ catch (Exception e)
+ {
+ error("Error while closing the snapshots file of the avd that is being reseted:"
+ + e.getMessage());
+ }
+ }
+
+ }
+ }
+
+ }
+
+ return resetStatus;
+ }
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/logic/start/AndroidEmulatorStarter.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/logic/start/AndroidEmulatorStarter.java
new file mode 100644
index 0000000..cc46a21
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/logic/start/AndroidEmulatorStarter.java
@@ -0,0 +1,173 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.emulator.logic.start;
+
+import static com.motorola.studio.android.common.log.StudioLogger.debug;
+import static com.motorola.studio.android.common.log.StudioLogger.error;
+import static com.motorola.studio.android.common.log.StudioLogger.info;
+
+import java.util.Map;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.osgi.util.NLS;
+
+import com.motorola.studio.android.emulator.EmulatorPlugin;
+import com.motorola.studio.android.emulator.core.exception.InstanceStartException;
+import com.motorola.studio.android.emulator.core.exception.InstanceStopException;
+import com.motorola.studio.android.emulator.core.exception.StartCancelledException;
+import com.motorola.studio.android.emulator.core.exception.StartTimeoutException;
+import com.motorola.studio.android.emulator.i18n.EmulatorNLS;
+import com.motorola.studio.android.emulator.logic.AbstractStartAndroidEmulatorLogic;
+import com.motorola.studio.android.emulator.logic.AndroidLogicUtils;
+import com.motorola.studio.android.emulator.logic.IAndroidLogicInstance;
+import com.motorola.studio.android.emulator.logic.AbstractStartAndroidEmulatorLogic.LogicMode;
+
+/**
+ * DESCRIPTION:
+ * This class contains the business layer of the Android
+ * Emulator start procedure
+ *
+ * RESPONSIBILITY:
+ * Start any Android Emulator
+ *
+ * COLABORATORS:
+ * None.
+ *
+ * USAGE:
+ * Use the public method to start a Android Emulator
+ */
+public class AndroidEmulatorStarter
+{
+
+ /**
+ * Starts this instance, after creating a clean VM copy at the provided location.
+ * Besides that, adds a new Android Emulator viewer inside the Android view.
+ * This method provides automatic VM startup.
+ *
+ * @param instance The Android device instance
+ * @param monitor A progress monitor that will give the user feedback about this
+ * long running operation
+ *
+ * @return the status of the operation (OK, Cancel or Error+ErrorMessage)
+ */
+ public static IStatus startInstance(IAndroidLogicInstance instance,
+ Map<Object, Object> arguments, IProgressMonitor monitor)
+ {
+ if (instance == null)
+ {
+ error("Abort start operation. Instance is null.");
+ return new Status(IStatus.ERROR, EmulatorPlugin.PLUGIN_ID,
+ EmulatorNLS.ERR_AndroidEmulatorStarter_InstanceNullPointer);
+ }
+
+ if (monitor == null)
+ {
+ monitor = new NullProgressMonitor();
+ }
+
+ IStatus status = Status.OK_STATUS;
+
+ if (!instance.isStarted())
+ {
+ int timeout = instance.getTimeout();
+ if (timeout <= 0)
+ {
+ status =
+ new Status(IStatus.ERROR, EmulatorPlugin.PLUGIN_ID, NLS.bind(
+ EmulatorNLS.ERR_AndroidLogicPlugin_InvalidTimeoutValue,
+ new Object[]
+ {
+ timeout
+ }));
+ return status;
+ }
+
+ try
+ {
+ try
+ {
+ LogicMode mode = LogicMode.START_MODE;
+ if (arguments != null)
+ {
+ Object modeObject = arguments.get(LogicMode.class);
+ if (modeObject instanceof LogicMode)
+ {
+ mode = (LogicMode) modeObject;
+ }
+ }
+
+ AbstractStartAndroidEmulatorLogic logic = instance.getStartLogic();
+ if (logic != null)
+ {
+ debug("Retrieved start logic: " + logic);
+ logic.execute(instance, mode, timeout, monitor);
+ AndroidLogicUtils.testCanceled(monitor);
+ }
+ else
+ {
+ error("Cannot start emulator because a logic is not provided for that.");
+ throw new InstanceStartException(
+ EmulatorNLS.ERR_AndroidEmulatorStarter_NoLogicAvailableForStart);
+ }
+ }
+ catch (Exception e)
+ {
+ error("An exception happened while trying to execute the start emulator logic for "
+ + instance + " Try to rollback by executing the stop process...");
+
+ try
+ {
+ instance.stop(true);
+ }
+ catch (InstanceStopException e1)
+ {
+ error("There was an error while forcing the stop the instance");
+ }
+
+ throw e;
+ }
+ }
+ catch (StartTimeoutException e)
+ {
+ error("A timeout has happeded during the start Android Emulator Instance. "
+ + instance + "Cause: " + e.getMessage());
+ status = new Status(IStatus.ERROR, EmulatorPlugin.PLUGIN_ID, e.getMessage(), e);
+ }
+ catch (InstanceStartException e)
+ {
+ error("It was not possible to start the Android Emulator Instance. " + instance
+ + "Cause: " + e.getMessage());
+ status = new Status(IStatus.ERROR, EmulatorPlugin.PLUGIN_ID, e.getMessage());
+ }
+ catch (StartCancelledException e)
+ {
+ info("Start operation was cancelled." + instance);
+ status = Status.CANCEL_STATUS;
+ }
+ catch (Exception e)
+ {
+ error("Unknown exception while starting the emulator instance: " + instance
+ + " Cause: " + e.getMessage());
+
+ status = new Status(IStatus.ERROR, EmulatorPlugin.PLUGIN_ID, e.getMessage());
+ }
+ }
+ return status;
+ }
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/logic/stop/AndroidEmulatorStopper.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/logic/stop/AndroidEmulatorStopper.java
new file mode 100644
index 0000000..4f8395e
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/logic/stop/AndroidEmulatorStopper.java
@@ -0,0 +1,188 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.emulator.logic.stop;
+
+import static com.motorola.studio.android.common.log.StudioLogger.error;
+import static com.motorola.studio.android.common.log.StudioLogger.info;
+import static com.motorola.studio.android.common.log.StudioLogger.warn;
+
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.sequoyah.vnc.protocol.PluginProtocolActionDelegate;
+import org.eclipse.sequoyah.vnc.protocol.lib.ProtocolHandle;
+import org.eclipse.sequoyah.vnc.vncviewer.registry.VNCProtocolRegistry;
+
+import com.motorola.studio.android.common.utilities.EclipseUtils;
+import com.motorola.studio.android.emulator.i18n.EmulatorNLS;
+import com.motorola.studio.android.emulator.logic.AndroidLogicUtils;
+import com.motorola.studio.android.emulator.logic.IAndroidLogicInstance;
+
+/**
+ * DESCRIPTION:
+ * This class contains the business layer of the Android
+ * Emulator stop procedure
+ *
+ * RESPONSIBILITY:
+ * Stop any Android Emulator
+ *
+ * COLABORATORS:
+ * None.
+ *
+ * USAGE:
+ * Use the public method to stop a Android Emulator
+ */
+public class AndroidEmulatorStopper
+{
+ private static Lock lock = new ReentrantReadWriteLock().writeLock();
+
+ private static final int MAX_LOCK_ATTEMPTS = 20;
+
+ /**
+ * Stops this instance, removing its viewer from Android Emulator View
+ *
+ * @param instance The device instance
+ * @param force If true do not ask the user if he/she wants to proceed
+ * @param monitor A progress monitor that will show the disposal
+ * action progress at UI
+ *
+ * @return True if the instance was stopped; false if the user chose not to stop it
+ */
+ public static boolean stopInstance(final IAndroidLogicInstance instance, boolean force,
+ boolean kill, IProgressMonitor monitor)
+ {
+
+ if (instance == null)
+ {
+ error("Could not stop the protocol because the provided instance is null");
+ return false;
+ }
+
+ boolean canProceed = true;
+
+ // decision whether to actually stop or not comes from user
+ if (!force)
+ {
+
+ canProceed =
+ EclipseUtils.showQuestionDialog(EmulatorNLS.GEN_Question, NLS.bind(
+ EmulatorNLS.QUESTION_AndroidEmulatorStopper_StopEmulatorQuestion,
+ instance.getName()));
+
+ }
+
+ if (canProceed)
+ {
+ int attempts = 0;
+ boolean locked = lock.tryLock();
+ while (!locked && (attempts < MAX_LOCK_ATTEMPTS))
+ {
+ try
+ {
+ Thread.sleep(125);
+ }
+ catch (InterruptedException e)
+ {
+ //Do nothing!
+ }
+ locked = lock.tryLock();
+ attempts++;
+ }
+
+ if (locked)
+ {
+ if (monitor == null)
+ {
+ monitor = new NullProgressMonitor();
+ }
+
+ try
+ {
+ info("Stopping the Android Emulator instance");
+
+ monitor
+ .beginTask(EmulatorNLS.MON_AndroidEmulatorStopper_DisposingInstance,
+ 200);
+ monitor.setTaskName(EmulatorNLS.MON_AndroidEmulatorStopper_DisposingInstance);
+
+ // Try to stop the protocol.
+ //
+ // This is not critical to the stop instance procedure, but it is
+ // desirable because the TmL's methods may do some cleanup
+ // before returning. The loop below tries to stop for some time (and
+ // give enough time for cleanup as well), but if TmL does not finish
+ // in an acceptable time, the stop instance procedure continues
+
+ try
+ {
+ info("Trying to stop the protocol");
+ // Try to implement a TmL independent code
+ ProtocolHandle handle = instance.getProtocolHandle();
+ if (handle != null)
+ {
+ PluginProtocolActionDelegate.requestStopProtocol(handle);
+
+ while (PluginProtocolActionDelegate.isProtocolRunning(handle))
+ {
+ Thread.sleep(250);
+ }
+ VNCProtocolRegistry.getInstance().unregister(handle);
+ info("Protocol stopped");
+ }
+ }
+ catch (Exception e)
+ {
+ error("There was an error while trying to stop the protocol");
+ }
+
+ // Try to implement a TmL independent code
+ instance.setProtocolHandle(null);
+
+ monitor.worked(100);
+ monitor.setTaskName(EmulatorNLS.MON_AndroidEmulatorStopper_StopVm);
+
+ if (kill)
+ {
+ AndroidLogicUtils.kill(instance);
+ }
+
+ info("Stopped the Android Emulator instance");
+ }
+ finally
+ {
+ monitor.done();
+ try
+ {
+ lock.unlock();
+ }
+ catch (Exception e)
+ {
+ warn("The thread that is releasing the lock is not the one which has it.");
+ }
+ }
+ }
+ else
+ {
+ canProceed = false;
+ }
+ }
+
+ return canProceed;
+ }
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/service/repair/RepairAvdHandler.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/service/repair/RepairAvdHandler.java
new file mode 100644
index 0000000..0a0d6a3
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/service/repair/RepairAvdHandler.java
@@ -0,0 +1,107 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.emulator.service.repair;
+
+import static com.motorola.studio.android.common.log.StudioLogger.error;
+
+import java.util.Map;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.sequoyah.device.framework.model.IInstance;
+import org.eclipse.sequoyah.device.framework.model.handler.IServiceHandler;
+import org.eclipse.sequoyah.device.framework.model.handler.ServiceHandler;
+
+import com.android.sdklib.internal.avd.AvdInfo;
+import com.android.sdklib.internal.avd.AvdInfo.AvdStatus;
+import com.motorola.studio.android.adt.SdkUtils;
+import com.motorola.studio.android.emulator.EmulatorPlugin;
+import com.motorola.studio.android.emulator.device.instance.AndroidDeviceInstance;
+import com.motorola.studio.android.emulator.device.refresh.InstancesListRefresh;
+import com.motorola.studio.android.emulator.i18n.EmulatorNLS;
+
+/**
+ * This is responsible for repair damaged avds.
+ */
+public class RepairAvdHandler extends ServiceHandler
+{
+
+ /* (non-Javadoc)
+ * @see org.eclipse.sequoyah.device.framework.model.handler.ServiceHandler#newInstance()
+ */
+ @Override
+ public IServiceHandler newInstance()
+ {
+ return new RepairAvdHandler();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.sequoyah.device.framework.model.handler.ServiceHandler#runService(org.eclipse.sequoyah.device.framework.model.IInstance, java.util.Map, org.eclipse.core.runtime.IProgressMonitor)
+ */
+ @Override
+ public IStatus runService(IInstance instance, Map<Object, Object> argumentos,
+ IProgressMonitor monitor)
+ {
+ IStatus status = Status.OK_STATUS;
+ if (!(instance instanceof AndroidDeviceInstance))
+ {
+ error(EmulatorNLS.RepairAvdHandler_Not_Android_Instance);
+
+ status =
+ new Status(IStatus.ERROR, EmulatorPlugin.PLUGIN_ID,
+ EmulatorNLS.ERR_StopEmulatorHandler_NotAnAndroidEmulator);
+ }
+ else
+ {
+ AndroidDeviceInstance androidDeviceInstance = (AndroidDeviceInstance) instance;
+ AvdInfo avdInfo = SdkUtils.getVm(androidDeviceInstance.getName());
+ if ((avdInfo != null) && isAvdRepairable(avdInfo.getStatus()))
+ {
+ status = SdkUtils.repairAvd(avdInfo);
+ if (status.getSeverity() != IStatus.OK)
+ {
+ error(getClass(), "IOException ocurred during repairAvd operation"); //$NON-NLS-1$
+ }
+ }
+ else
+ {
+ status =
+ new Status(IStatus.ERROR, EmulatorPlugin.PLUGIN_ID,
+ EmulatorNLS.RepairAvdHandler_AVD_NOT_REPAIRABLE);
+ }
+ }
+ if (status.getSeverity() == IStatus.OK)
+ {
+ InstancesListRefresh.refresh();
+ }
+ return status;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.sequoyah.device.framework.model.handler.ServiceHandler#updatingService(org.eclipse.sequoyah.device.framework.model.IInstance, org.eclipse.core.runtime.IProgressMonitor)
+ */
+ @Override
+ public IStatus updatingService(IInstance arg0, IProgressMonitor arg1)
+ {
+ return Status.OK_STATUS;
+ }
+
+ private boolean isAvdRepairable(AvdStatus avdStatus)
+ {
+ return avdStatus == AvdStatus.ERROR_IMAGE_DIR;
+ }
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/service/reset/ResetServiceHandler.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/service/reset/ResetServiceHandler.java
new file mode 100644
index 0000000..9a9618e
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/service/reset/ResetServiceHandler.java
@@ -0,0 +1,124 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.emulator.service.reset;
+
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.jface.dialogs.Dialog;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.sequoyah.device.framework.model.IInstance;
+import org.eclipse.sequoyah.device.framework.model.handler.IServiceHandler;
+import org.eclipse.sequoyah.device.framework.model.handler.ServiceHandler;
+
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.common.utilities.EclipseUtils;
+import com.motorola.studio.android.emulator.EmulatorPlugin;
+import com.motorola.studio.android.emulator.i18n.EmulatorNLS;
+import com.motorola.studio.android.emulator.logic.IAndroidLogicInstance;
+import com.motorola.studio.android.emulator.logic.reset.AndroidEmulatorReseter;
+
+/**
+ * DESCRIPTION:
+ * This class plugs the reset procedure to a TmL service
+ *
+ * RESPONSIBILITY:
+ * Provide access to the reset feature from TmL device framework
+ *
+ * COLABORATORS:
+ * None.
+ *
+ * USAGE:
+ * This class is intended to be used by Eclipse only
+ */
+public class ResetServiceHandler extends ServiceHandler
+{
+
+ private boolean userAgreed;
+
+ @Override
+ public IServiceHandler newInstance()
+ {
+ return new ResetServiceHandler();
+ }
+
+ @Override
+ public IStatus runService(IInstance instance, Map<Object, Object> arguments,
+ IProgressMonitor monitor)
+ {
+
+ boolean force = false;
+
+ if (arguments != null)
+ {
+ Object forceObj = arguments.get(EmulatorPlugin.FORCE_ATTR);
+ if (forceObj instanceof Boolean)
+ {
+ force = ((Boolean) forceObj).booleanValue();
+ }
+ }
+
+ IStatus status = Status.OK_STATUS;
+ if (!force && !userAgreed)
+ {
+ status = Status.CANCEL_STATUS;
+ }
+
+ if (status.isOK() && instance instanceof IAndroidLogicInstance)
+ {
+ status = AndroidEmulatorReseter.resetInstance((IAndroidLogicInstance) instance);
+ }
+
+ // Collecting usage data for statistical purposes
+ try
+ {
+ StudioLogger.collectUsageData(StudioLogger.WHAT_EMULATOR_RESET,
+ StudioLogger.KIND_EMULATOR, StudioLogger.DESCRIPTION_DEFAULT,
+ EmulatorPlugin.PLUGIN_ID, EmulatorPlugin.getDefault().getBundle().getVersion()
+ .toString());
+ }
+ catch (Throwable e)
+ {
+ //Do nothing, but error on the log should never prevent app from working
+ }
+ return status;
+ }
+
+ @Override
+ public IStatus updatingService(IInstance instance, IProgressMonitor monitor)
+ {
+ StudioLogger.info("Updating reset service");
+ return Status.OK_STATUS;
+ }
+
+ public IStatus singleInit(List<IInstance> instances)
+ {
+ int reset =
+ EclipseUtils.showInformationDialog(EmulatorNLS.GEN_Warning,
+ EmulatorNLS.QUESTION_AndroidEmulatorReseter_ConfirmationText, new String[]
+ {
+ EmulatorNLS.QUESTION_AndroidEmulatorReseter_Yes,
+ EmulatorNLS.QUESTION_AndroidEmulatorReseter_No
+ }, MessageDialog.WARNING);
+ userAgreed = reset == Dialog.OK;
+
+ return Status.OK_STATUS;
+ }
+
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/service/start/StartEmulatorHandler.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/service/start/StartEmulatorHandler.java
new file mode 100644
index 0000000..a7b3085
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/service/start/StartEmulatorHandler.java
@@ -0,0 +1,128 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.emulator.service.start;
+
+import static com.motorola.studio.android.common.log.StudioLogger.debug;
+import static com.motorola.studio.android.common.log.StudioLogger.error;
+
+import java.util.Map;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.sequoyah.device.framework.model.IInstance;
+import org.eclipse.sequoyah.device.framework.model.handler.IServiceHandler;
+import org.eclipse.sequoyah.device.framework.model.handler.ServiceHandler;
+
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.emulator.EmulatorPlugin;
+import com.motorola.studio.android.emulator.core.exception.StartCancelledException;
+import com.motorola.studio.android.emulator.device.instance.AndroidDeviceInstance;
+import com.motorola.studio.android.emulator.i18n.EmulatorNLS;
+import com.motorola.studio.android.emulator.logic.AndroidLogicUtils;
+import com.motorola.studio.android.emulator.logic.start.AndroidEmulatorStarter;
+
+/**
+ * DESCRIPTION: This class handles the start Android Emulator action, which
+ * transitions from both stopped states to started
+ *
+ * RESPONSIBILITY: Delegate the action to the AndroidEmulatorStarter, which
+ * contains the business layer logic for starting Android Emulators.
+ *
+ * COLABORATORS: None.
+ *
+ * USAGE: This class is intended to be used by Eclipse only
+ */
+public class StartEmulatorHandler extends ServiceHandler
+{
+
+ /**
+ * @see IServiceHandler#newInstance()
+ */
+ @Override
+ public IServiceHandler newInstance()
+ {
+ return new StartEmulatorHandler();
+ }
+
+ /**
+ * @see ServiceHandler#runService(IInstance, Map, IProgressMonitor)
+ */
+ @Override
+ public IStatus runService(IInstance instance, Map<Object, Object> arguments,
+ IProgressMonitor monitor)
+ {
+ IStatus status = Status.OK_STATUS;
+ try
+ {
+
+ String description = "";
+
+ // Tests if the given instance is a Android instance
+ if (!(instance instanceof AndroidDeviceInstance))
+ {
+ error("Aborting start service. This is not an Android Emulator instance...");
+ status =
+ new Status(IStatus.ERROR, EmulatorPlugin.PLUGIN_ID,
+ EmulatorNLS.ERR_StartEmulatorHandler_NotAnAndroidEmulator);
+ }
+ else
+ {
+ description = ((AndroidDeviceInstance) instance).getTarget();
+ try
+ {
+ AndroidLogicUtils.testCanceled(monitor);
+
+ status =
+ AndroidEmulatorStarter.startInstance((AndroidDeviceInstance) instance,
+ arguments, monitor);
+
+ debug("Finished Execution of the start emulator service... :" + instance
+ + " Status => " + status);
+ }
+ catch (StartCancelledException e)
+ {
+ monitor.done();
+ status = Status.CANCEL_STATUS;
+ }
+ }
+
+ description = StudioLogger.KEY_TARGET + description;
+ StudioLogger.collectUsageData(StudioLogger.WHAT_EMULATOR_START,
+ StudioLogger.KIND_EMULATOR, description, EmulatorPlugin.PLUGIN_ID,
+ EmulatorPlugin.getDefault().getBundle().getVersion().toString());
+ }
+ catch (Throwable t)
+ {
+ StudioLogger.error(StartEmulatorHandler.class.toString(),
+ "An exception ocurred during emulator start up process.", t);
+ status =
+ new Status(Status.ERROR, EmulatorPlugin.PLUGIN_ID,
+ "An exception ocurred during emulator start up process.");
+ }
+ return status;
+ }
+
+ /**
+ * @see ServiceHandler#updatingService(IInstance, IProgressMonitor)
+ */
+ @Override
+ public IStatus updatingService(IInstance instance, IProgressMonitor monitor)
+ {
+ return Status.OK_STATUS;
+ }
+
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/service/stop/StopEmulatorCommand.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/service/stop/StopEmulatorCommand.java
new file mode 100644
index 0000000..60162eb
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/service/stop/StopEmulatorCommand.java
@@ -0,0 +1,47 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.emulator.service.stop;
+
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.sequoyah.device.common.utilities.exception.SequoyahException;
+import org.eclipse.sequoyah.device.framework.model.IInstance;
+
+import com.motorola.studio.android.emulator.EmulatorPlugin;
+import com.motorola.studio.android.emulator.core.model.IAndroidEmulatorInstance;
+import com.motorola.studio.android.emulator.ui.view.AbstractAndroidView;
+
+public class StopEmulatorCommand extends AbstractHandler
+{
+
+ public Object execute(ExecutionEvent event) throws ExecutionException
+ {
+ IAndroidEmulatorInstance instance = AbstractAndroidView.getActiveInstance();
+ if (instance instanceof IInstance)
+ {
+ try
+ {
+ EmulatorPlugin.getStopServiceHandler().run((IInstance) instance);
+ }
+ catch (SequoyahException e)
+ {
+ //do nothing
+ }
+ }
+ return null;
+ }
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/service/stop/StopEmulatorHandler.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/service/stop/StopEmulatorHandler.java
new file mode 100644
index 0000000..b96706d
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/service/stop/StopEmulatorHandler.java
@@ -0,0 +1,130 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.emulator.service.stop;
+
+import static com.motorola.studio.android.common.log.StudioLogger.debug;
+import static com.motorola.studio.android.common.log.StudioLogger.error;
+
+import java.util.Map;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.sequoyah.device.framework.events.InstanceEvent;
+import org.eclipse.sequoyah.device.framework.events.InstanceEventManager;
+import org.eclipse.sequoyah.device.framework.events.InstanceEvent.InstanceEventType;
+import org.eclipse.sequoyah.device.framework.model.IInstance;
+import org.eclipse.sequoyah.device.framework.model.handler.IServiceHandler;
+import org.eclipse.sequoyah.device.framework.model.handler.ServiceHandler;
+
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.emulator.EmulatorPlugin;
+import com.motorola.studio.android.emulator.device.instance.AndroidDeviceInstance;
+import com.motorola.studio.android.emulator.i18n.EmulatorNLS;
+import com.motorola.studio.android.emulator.logic.stop.AndroidEmulatorStopper;
+
+/**
+ * DESCRIPTION:
+ * This class plugs the stop procedure to a TmL service
+ *
+ * RESPONSIBILITY:
+ * Provide access to the stop feature from TmL device framework
+ *
+ * COLABORATORS:
+ * None.
+ *
+ * USAGE:
+ * This class is intended to be used by Eclipse only
+ */
+public class StopEmulatorHandler extends ServiceHandler
+{
+ @Override
+ public IServiceHandler newInstance()
+ {
+ return new StopEmulatorHandler();
+ }
+
+ @Override
+ public IStatus runService(IInstance instance, Map<Object, Object> arguments,
+ IProgressMonitor monitor)
+ {
+ debug("Executing the stop emulator service... Instance:" + instance + " Arguments:"
+ + arguments);
+
+ IStatus status = Status.OK_STATUS;
+
+ // actually stop emulator
+ if (!(instance instanceof AndroidDeviceInstance))
+ {
+ error("Aborting start service. This is not an Android Emulator instance...");
+
+ status =
+ new Status(IStatus.ERROR, EmulatorPlugin.PLUGIN_ID,
+ EmulatorNLS.ERR_StopEmulatorHandler_NotAnAndroidEmulator);
+ }
+ else
+ {
+ boolean force = false;
+ if (arguments != null)
+ {
+ Object forceObj = arguments.get(EmulatorPlugin.FORCE_ATTR);
+ if (forceObj instanceof Boolean)
+ {
+ force = ((Boolean) forceObj).booleanValue();
+ }
+ }
+
+ boolean stopPerformed =
+ AndroidEmulatorStopper.stopInstance((AndroidDeviceInstance) instance, force,
+ true, monitor);
+
+ if (!stopPerformed)
+ {
+ // user decided not to stop; return a cancel status
+ status = Status.CANCEL_STATUS;
+ }
+ else
+ {
+ instance.setNameSuffix(null);
+ InstanceEventManager.getInstance().notifyListeners(
+ new InstanceEvent(InstanceEventType.INSTANCE_UPDATED, instance));
+ }
+ }
+
+ debug("Finished the execution of the stop emulator service: " + instance + " status: "
+ + status);
+
+ // Collecting usage data for statistical purposes
+ try
+ {
+ StudioLogger.collectUsageData(StudioLogger.WHAT_EMULATOR_STOP,
+ StudioLogger.KIND_EMULATOR, status.toString(), EmulatorPlugin.PLUGIN_ID,
+ EmulatorPlugin.getDefault().getBundle().getVersion().toString());
+ }
+ catch (Throwable e)
+ {
+ //Do nothing, but error on the log should never prevent app from working
+ }
+
+ return status;
+ }
+
+ @Override
+ public IStatus updatingService(IInstance instance, IProgressMonitor monitor)
+ {
+ return Status.OK_STATUS;
+ }
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/skin/android/AndroidSkin.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/skin/android/AndroidSkin.java
new file mode 100644
index 0000000..2eff106
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/skin/android/AndroidSkin.java
@@ -0,0 +1,428 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.emulator.skin.android;
+
+import static com.motorola.studio.android.common.log.StudioLogger.error;
+import static com.motorola.studio.android.common.log.StudioLogger.info;
+import static com.motorola.studio.android.common.log.StudioLogger.warn;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+
+import org.eclipse.swt.graphics.ImageData;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.RGB;
+
+import com.motorola.studio.android.common.utilities.EclipseUtils;
+import com.motorola.studio.android.emulator.core.exception.SkinException;
+import com.motorola.studio.android.emulator.core.skin.AndroidSkinBean;
+import com.motorola.studio.android.emulator.core.skin.IAndroidKey;
+import com.motorola.studio.android.emulator.core.skin.IAndroidSkin;
+import com.motorola.studio.android.emulator.core.skin.ISkinKeyXmlTags;
+import com.motorola.studio.android.emulator.i18n.EmulatorNLS;
+import com.motorola.studio.android.emulator.skin.android.parser.LayoutFileModel;
+import com.motorola.studio.android.emulator.skin.android.parser.LayoutFileParser;
+
+public class AndroidSkin implements IAndroidSkin
+{
+ /**
+ * The released images of the skin, as read from the skin files
+ */
+ private final Map<String, ImageData> releasedImagesPool = new HashMap<String, ImageData>();
+
+ /**
+ * The pressed images of the skin, as read from the skin files
+ */
+ private final Map<String, ImageData> pressedImagesPool = new HashMap<String, ImageData>();
+
+ /**
+ * The enter images of the skin, as read from the skin files
+ */
+ private final Map<String, ImageData> enterImagesPool = new HashMap<String, ImageData>();
+
+ /**
+ * The keys of the skin, as read from the skin files
+ */
+ private final Map<String, Collection<IAndroidKey>> androidKeysPool =
+ new HashMap<String, Collection<IAndroidKey>>();
+
+ /**
+ * The folder where to look for skin files
+ */
+ private File skinFilesPath;
+
+ /**
+ * A model containing the parsed layout file
+ */
+ private LayoutFileModel layoutFile;
+
+ private Properties keycodes;
+
+ /**
+ * Retrieves data being kept in a pool
+ *
+ * @param layoutName The name of the layout which object has been requested
+ * @param pool The pool where to look for data
+ *
+ * @return An object from the pool that matches the request
+ *
+ * @throws SkinException If either the layout file was not loaded, or if the images/keys cannot
+ * be generated
+ */
+ private ImageData getImageFromPool(String layoutName, Map<String, ImageData> pool)
+ throws SkinException
+ {
+ if (layoutFile == null)
+ {
+ error("User has tried to request skin data without setting a valid skin files path");
+ throw new SkinException(EmulatorNLS.ERR_AndroidSkin_NoLayoutLoaded);
+ }
+
+ // Tries to get data from the pool. If it is not available for the
+ // current layout yet, load all resources related to the layout to the pools.
+ ImageData id = pool.get(layoutName);
+
+ if (id == null)
+ {
+
+ addImagesToPools(layoutName);
+ id = pool.get(layoutName);
+ }
+
+ return id;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.emulator.core.skin.IAndroidSkin#getKeyDataCollection(java.lang.String)
+ */
+ public Collection<IAndroidKey> getKeyDataCollection(String layoutName)
+ {
+ Collection<IAndroidKey> androidKeys;
+ try
+ {
+ if (layoutFile == null)
+ {
+ error("User has tried to request skin data without setting a valid skin files path");
+ throw new SkinException(EmulatorNLS.ERR_AndroidSkin_NoLayoutLoaded);
+ }
+
+ // Tries to get data from the pool. If it is not available for the
+ // current layout yet, load all resources related to the layout to the pools.
+ androidKeys = androidKeysPool.get(layoutName);
+
+ if (androidKeys == null)
+ {
+
+ System.gc();
+ androidKeys =
+ AndroidSkinTranslator.generateAndroidKeys(layoutFile, layoutName,
+ skinFilesPath);
+ System.gc();
+ androidKeysPool.put(layoutName, androidKeys);
+ }
+ }
+ catch (SkinException e)
+ {
+ androidKeys = new HashSet<IAndroidKey>();
+ error("The key data could not be retrieved from skin files. Cause: " + e.getMessage());
+ EclipseUtils.showErrorDialog(e);
+ }
+
+ return androidKeys;
+ }
+
+ public Properties getKeyCodes()
+ {
+ if ((keycodes == null) || ((keycodes != null) && keycodes.isEmpty()))
+ {
+ try
+ {
+ keycodes = AndroidSkinTranslator.getKeycodes(layoutFile, skinFilesPath);
+ }
+ catch (SkinException e)
+ {
+ keycodes = new Properties();
+ error("The key data could not be retrieved from skin files. Cause: "
+ + e.getMessage());
+ }
+ }
+
+ return keycodes;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.emulator.core.skin.IAndroidSkin#getPressedImageData(java.lang.String)
+ */
+ public ImageData getPressedImageData(String layoutName) throws SkinException
+ {
+ return getImageFromPool(layoutName, pressedImagesPool);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.emulator.core.skin.IAndroidSkin#getEnterImageData(java.lang.String)
+ */
+ public ImageData getEnterImageData(String layoutName) throws SkinException
+ {
+ return getImageFromPool(layoutName, enterImagesPool);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.emulator.core.skin.IAndroidSkin#getReleasedImageData(java.lang.String)
+ */
+ public ImageData getReleasedImageData(String layoutName) throws SkinException
+ {
+ return getImageFromPool(layoutName, releasedImagesPool);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.emulator.core.skin.IAndroidSkin#getSkinBean(java.lang.String)
+ */
+ public AndroidSkinBean getSkinBean(String layoutName) throws SkinException
+ {
+ if (layoutFile == null)
+ {
+ error("User has tried to request additional skin data without setting a valid skin files path");
+ throw new SkinException(EmulatorNLS.ERR_AndroidSkin_NoLayoutLoaded);
+ }
+
+ AndroidSkinBean bean = new AndroidSkinBean();
+
+ // Fills the skin bean with information related to the part chosen for display. This bean must have
+ // at least display positioning information and scale.
+ String partName = layoutFile.getMainPartName(layoutName);
+ Point dPosition =
+ AndroidSkinTranslator.translateDisplayPosition(layoutFile, layoutName, partName,
+ skinFilesPath);
+ int dWidth = layoutFile.getDisplayWidth(partName);
+ int dHeight = layoutFile.getDisplayHeight(partName);
+
+ int dw, dh;
+ if (layoutFile.isSwapWidthHeightNeededAtLayout(layoutName))
+ {
+ dw = dHeight;
+ dh = dWidth;
+ }
+ else
+ {
+ dw = dWidth;
+ dh = dHeight;
+ }
+
+ bean.addSkinPropertyValue(ISkinKeyXmlTags.SKIN_INTERNAL_VIEW_X, dPosition.x);
+ bean.addSkinPropertyValue(ISkinKeyXmlTags.SKIN_INTERNAL_VIEW_Y, dPosition.y);
+ bean.addSkinPropertyValue(ISkinKeyXmlTags.SKIN_INTERNAL_VIEW_WIDTH, dw);
+ bean.addSkinPropertyValue(ISkinKeyXmlTags.SKIN_INTERNAL_VIEW_HEIGHT, dh);
+ bean.addSkinPropertyValue(ISkinKeyXmlTags.SKIN_EMBEDDED_VIEW_SCALE, 10);
+
+ return bean;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.emulator.core.skin.IAndroidSkin#isFlipSupported()
+ */
+ public boolean isFlipSupported()
+ {
+ return false;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.emulator.core.skin.IAndroidSkin#setSkinFilesPath(java.lang.String)
+ */
+ public void setSkinFilesPath(String skinFilesPath) throws SkinException
+ {
+
+ this.skinFilesPath = new File(skinFilesPath);
+ if (this.skinFilesPath.isDirectory())
+ {
+
+ layoutFile = LayoutFileParser.readLayout(this.skinFilesPath);
+
+ }
+ else
+ {
+ error("Provided skin files path is not a directory. Setting the skin files path operation has failed.");
+ throw new SkinException(EmulatorNLS.ERR_AndroidSkin_ProvidedSkinPathIsNotADirectory);
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.emulator.core.skin.IAndroidSkin#isRotatedLayout(java.lang.String)
+ */
+ public boolean isSwapWidthHeightNeededAtLayout(String layoutName)
+ {
+ return layoutFile.isSwapWidthHeightNeededAtLayout(layoutName);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.emulator.core.skin.IAndroidSkin#getAvailableLayouts()
+ */
+ public Collection<String> getAvailableLayouts()
+ {
+ return layoutFile.getLayoutNames();
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.emulator.core.skin.IAndroidSkin#getLayoutScreenCommand(java.lang.String)
+ */
+ public String getLayoutScreenCommand(String layoutName)
+ {
+ return layoutFile.getLayoutSwitchCommand(layoutName);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.emulator.core.skin.IAndroidSkin#nextLayout(java.lang.String)
+ */
+ public String getNextLayout(String referenceLayout)
+ {
+ info("Calculating the next layout");
+ String next = null;
+ if (referenceLayout != null)
+ {
+ List<String> layoutsList = new ArrayList<String>(getAvailableLayouts());
+
+ // Switches to the next layout if the skin supports it. The if/else clause
+ // implements a circular list
+ if (layoutsList.size() > 1)
+ {
+ int currentLayoutNum = layoutsList.indexOf(referenceLayout);
+ if (currentLayoutNum != layoutsList.size() - 1)
+ {
+ next = layoutsList.get(++currentLayoutNum);
+ }
+ else
+ {
+ next = layoutsList.get(0);
+ }
+ info("Next layout: " + next);
+ }
+ else
+ {
+ warn("The skin doesn't have multiple layouts. The operation was not performed");
+ }
+ }
+ else
+ {
+ warn("The skin doesn't have multiple layouts. The operation was not performed");
+ }
+
+ return next;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.emulator.core.skin.IAndroidSkin#previousLayout(java.lang.String)
+ */
+ public String getPreviousLayout(String referenceLayout)
+ {
+ info("Calculating the previous layout");
+ String previous = null;
+ if (referenceLayout != null)
+ {
+ List<String> layoutsList = new ArrayList<String>(getAvailableLayouts());
+
+ // Switches to the previous layout if the skin supports it. The if/else clause
+ // implements a circular list
+ if (layoutsList.size() > 1)
+ {
+ int currentLayoutNum = layoutsList.indexOf(referenceLayout);
+
+ if (currentLayoutNum != 0)
+ {
+ previous = layoutsList.get(--currentLayoutNum);
+ }
+ else
+ {
+ previous = layoutsList.get(layoutsList.size() - 1);
+ }
+ info("Previous layout: " + previous);
+ }
+ else
+ {
+ warn("The skin doesn't have multiple layouts. The operation was not performed");
+ }
+ }
+ else
+ {
+ warn("The skin doesn't have multiple layouts. The operation was not performed");
+ }
+
+ return previous;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.emulator.core.skin.IAndroidSkin#getBackgroundColor(java.lang.String)
+ */
+ public RGB getBackgroundColor(String layoutName)
+ {
+ return layoutFile.getLayoutColor(layoutName, skinFilesPath);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.emulator.core.skin.IAndroidSkin#getDpadRotation(java.lang.String)
+ */
+ public int getDpadRotation(String layoutName)
+ {
+ return layoutFile.getDpadRotation(layoutName);
+ }
+
+ /**
+ * Generates images/keys for the current layout (or main part, if a layout is not available)
+ * and store them at the appropriate pools
+ *
+ * @param key The key to be used to store data at the pools
+ *
+ * @throws SkinException If the layout file cannot be loaded
+ */
+ private void addImagesToPools(String key) throws SkinException
+ {
+ // Validates the provided string before proceeding
+ if ((key == null) || (!layoutFile.getLayoutNames().contains(key)))
+ {
+ throw new SkinException(EmulatorNLS.ERR_AndroidSkin_InvalidLayoutProvided);
+ }
+
+ System.gc();
+
+ ImageData[] images =
+ AndroidSkinTranslator.generateLayoutImages(layoutFile, key, skinFilesPath);
+
+ info("Adding released/pressed/hover images to the pool");
+ releasedImagesPool.put(key, images[0]);
+ pressedImagesPool.put(key, images[1]);
+ enterImagesPool.put(key, images[2]);
+
+ System.gc();
+ }
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/skin/android/AndroidSkinTranslator.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/skin/android/AndroidSkinTranslator.java
new file mode 100644
index 0000000..9e0ee89
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/skin/android/AndroidSkinTranslator.java
@@ -0,0 +1,1370 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.emulator.skin.android;
+
+import static com.motorola.studio.android.common.log.StudioLogger.error;
+import static com.motorola.studio.android.common.log.StudioLogger.warn;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.Collection;
+import java.util.LinkedHashSet;
+import java.util.Properties;
+
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.ImageData;
+import org.eclipse.swt.graphics.PaletteData;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.RGB;
+import org.eclipse.swt.widgets.Shell;
+
+import com.motorola.studio.android.adt.SdkUtils;
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.emulator.EmulatorPlugin;
+import com.motorola.studio.android.emulator.core.exception.SkinException;
+import com.motorola.studio.android.emulator.core.skin.AndroidPressKey;
+import com.motorola.studio.android.emulator.core.skin.IAndroidKey;
+import com.motorola.studio.android.emulator.i18n.EmulatorNLS;
+import com.motorola.studio.android.emulator.skin.android.parser.LayoutFileModel;
+
+/**
+ * DESCRIPTION:
+ * Utility class for translating Google skin files data into objects
+ * suitable to the viewer. This includes:
+ * - Generating image data objects in memory that represent the exact images that
+ * will be drawn using SWT Image/GC/Transform graphic objects. It is necessary to
+ * create new image data objects from scratch because the operations performed by
+ * Image/GC/Transform affects only the display, keeping the associated image data
+ * objects intact. It would also be very expensive to generate those images at
+ * screen, after every mouse event (including mouse moves, which are frequent)
+ * - Generating a key data collection suitable for a given layout. The key data
+ * objects must contain positioning information that depends on the position of
+ * elements inside the layout. It is this class job to discover what are the
+ * coordinates to set at the keys
+ * - Translate the display position for a given layout. As with the key data
+ * collection, the coordinates of the display depends on the layout description
+ * and must be calculated.
+ *
+ * RESPONSIBILITY:
+ * Provide translation functionalities to convert Google format to a format
+ * suitable to the viewer
+ *
+ * COLABORATORS:
+ * None.
+ *
+ * USAGE:
+ * This class is intended to be used by AndroidSkin class only
+ *
+ *
+ *
+ * This class uses several concepts, that are described below:
+ *
+ * LAYOUT FILE MODEL: A representation of the file "layout", saved as part
+ * of the skin in the skin folder. Attributes such as keyboard charmap and
+ * network are global to a layout file
+ * LAYOUT: A collection of parts, with some part integration features, such
+ * as part positioning and part rotation. Parts can be rotated independently
+ * in a layout.
+ * PART: The minimum skin view. It contains buttons and background image.
+ * A display is optional
+ * OFFSET: If a part is declared to be drawn in a layout using negative coordinates,
+ * or if a button is declared to be drawn in a part using negative coordinates,
+ * the calculated layout/part offset is different from 0 in either (x, y) directions.
+ * If such situation happen, it is mandatory to generate a bigger image in memory
+ * to have the layout/part drawn.
+ * EXPANDED PART IMAGE: Is how is called the image generated in memory due to part
+ * offset being different from zero. Layouts always have images generated in memory,
+ * but parts can be represented as a simple file load if no button demands an offset
+ * different from (0, 0)
+ *
+ * Considerations about the differences between Google format and viewer format:
+ *
+ * a) Google format supports negative coordinates for parts/buttons. To generate images
+ * that are renderable by SWT, the viewer module must translate the parts/buttons so
+ * that they fit in a single image data
+ * b) Viewer demands a pair of pressed, released and enter images and a set of IAndroidKeys.
+ * Google provides several images to use as filter when a button is pressed. To increase
+ * the performance, all the images/keys are generated during instance start time, being
+ * reused afterwards
+ * c) In Google format, the part position at the layout is ALWAYS set to the upper-left
+ * corner of the PART, not the layout. Therefore, depending on the rotation parameter
+ * we need to calculate what is the position to draw the part relative to the layout
+ *
+ * ---------------------------
+ * | | 1) OLX / OLY : Offset due to layout, if one or more part
+ * | ------------------ | position coordinates are less than 0
+ * | | ------------ | | 2) OPX / OPY : Offset due to part, if one of more button
+ * | | | | | | position coordinates are less than 0
+ * | |OPX| | | | 3) LAYOUT : Layout image area
+ * | | ----- | | | 4) PART : Part expanded image area
+ * | | |BTN| | | | 5) BACKGR : Original part background image, which demanded
+ * | | | | | | | expansion due to BTN
+ * | | ----- | | | 6) BTN : A button with negative x coordinate related to
+ * | | | BACKGR | | | BACKGR
+ * | | ------------ | |
+ * | | PART | |
+ * | OLX ------------------ |
+ * | LAYOUT |
+ * ---------------------------
+ *
+ */
+public class AndroidSkinTranslator
+{
+ /**
+ * Constant that describes a QWERTY charmap in the layout
+ */
+ private static final String CHARMAP_QWERTY = "qwerty";
+
+ /**
+ * Constant that describes a QWERTY2 charmap in the layout
+ */
+ private static final String CHARMAP_QWERTY2 = "qwerty2";
+
+ /**
+ * Path to the QWERTY codes inside the plugin
+ */
+ private static final String CHARMAP_QWERTY_FILE = "res/qwerty.properties";
+
+ /**
+ * Constant that describes a AVRCP charmap in the layout
+ */
+ private static final String CHARMAP_AVRCP = "AVRCP";
+
+ /**
+ * Path to the AVRCP codes inside the plugin
+ */
+ private static final String CHARMAP_AVRCP_FILE = "res/AVRCP.properties";
+
+ private static final String CHARMAP_OPHONE_QWERTY_FILE = "res/ophone_qwerty.properties";
+
+ private static final String CHARMAP_OPHONE_AVRCP_FILE = "res/ophone_AVRCP.properties";
+
+ /**
+ * d-pad keys
+ */
+ private static final String DPAD_DOWN = "DPAD_DOWN";
+
+ private static final String DPAD_UP = "DPAD_UP";
+
+ private static final String DPAD_LEFT = "DPAD_LEFT";
+
+ private static final String DPAD_RIGHT = "DPAD_RIGHT";
+
+ /**
+ * Translates the button information inside the layout file model into
+ * viewer compatible IAndroidKeys
+ * <br><br>
+ * @param layoutFile The model that represent the layout file
+ * @param layoutName The name of the layout where to look for buttons in the model,
+ * or <code>null</code> if no layout is available
+ * @param skinFilesPath The path to the skin dir, where skin files can be found
+ * <br><br>
+ * @return A collection of IAndroidKeys, describing the keys, codes and positions
+ * of the buttons in the skin
+ * <br><br>
+ * @throws SkinException If an error that prevents the translation happens, such as
+ * errors when opening required files or charmap not supported
+ */
+ static Collection<IAndroidKey> generateAndroidKeys(LayoutFileModel layoutFile,
+ String layoutName, File skinFilesPath) throws SkinException
+ {
+
+ // Retrieve a map of keycodes related to the keyboard declared at the layout
+ Collection<IAndroidKey> keyCollection = new LinkedHashSet<IAndroidKey>();
+
+ Properties keycodes = getKeycodes(layoutFile, skinFilesPath);
+
+ // Retrieve the names of the parts that compose the layout
+ Collection<String> partNames = layoutFile.getLayoutPartNames(layoutName);
+
+ // Gets the layout offset for position adjustments
+ Point offsetL = getLayoutOffset(layoutFile, layoutName, skinFilesPath);
+
+ // Iterates on the parts, looking for their buttons
+
+ for (String partName : partNames)
+ {
+
+ Point offsetP = getPartOffset(layoutFile, partName);
+ Point partSize = getPartImageSize(layoutFile, partName, skinFilesPath, offsetP);
+
+ // Iterates on the buttons of the part, creating an IAndroidKey for each in the end
+ Collection<String> buttonNames = layoutFile.getButtonNames(partName);
+
+ for (String buttonName : buttonNames)
+ {
+ String k = buttonName.toUpperCase().replace("-", "_");
+ String keyCodeStr = (String) keycodes.get(k);
+
+ // The IAndroidKey will only be generated if a keycode with the same name is found
+ if (keyCodeStr != null)
+ {
+
+ // Retrieve the button parameters needed for the key generation
+ // - The button position must be translated due to the layout and eventual offsets
+ // - The button w/h must be interpreted according to the rotation parameter. If the
+ // rotation is odd (landscape), the button width must be the key height and vice-versa.
+ // Otherwise (even, portrait), we can use the button w/h at the keys as is.
+ int buttonW = layoutFile.getButtonWidth(partName, buttonName, skinFilesPath);
+ int buttonH = layoutFile.getButtonHeight(partName, buttonName, skinFilesPath);
+ Point buttonPos =
+ translateButtonPosition(layoutFile, layoutName, partName, buttonName,
+ skinFilesPath, offsetP, partSize);
+
+ int startX = offsetL.x + buttonPos.x;
+ int startY = offsetL.y + buttonPos.y;
+ int endX, endY;
+
+ if (layoutFile.isSwapWidthHeightNeededAtLayout(layoutName, partName))
+ {
+ endX = startX + buttonH;
+ endY = startY + buttonW;
+ }
+ else
+ {
+ endX = startX + buttonW;
+ endY = startY + buttonH;
+ }
+
+ int dpadRotation = layoutFile.getDpadRotation(layoutName);
+
+ keyCodeStr = getRotatedKeyCode(k, keyCodeStr, dpadRotation, keycodes);
+
+ AndroidPressKey key =
+ new AndroidPressKey(buttonName, keyCodeStr, buttonName, startX, startY,
+ endX, endY, "", 0);
+
+ keyCollection.add(key);
+
+ }
+ }
+
+ }
+
+ return keyCollection;
+ }
+
+ /**
+ * @param keyCodeStr
+ * @param keyCodeStr2
+ * @param dpadRotation
+ * @param keycodes
+ * @return
+ */
+ private static String getRotatedKeyCode(String keyName, String keyCodeStr, int dpadRotation,
+ Properties keycodes)
+ {
+ String keyCode = keyCodeStr;
+ switch (dpadRotation % 4)
+ {
+ case 1:
+ if (DPAD_DOWN.equals(keyName))
+ {
+ keyCode = (String) keycodes.get(DPAD_RIGHT);
+ }
+ else if (DPAD_LEFT.equals(keyName))
+ {
+ keyCode = (String) keycodes.get(DPAD_DOWN);
+ }
+ else if (DPAD_RIGHT.equals(keyName))
+ {
+ keyCode = (String) keycodes.get(DPAD_UP);
+ }
+ else if (DPAD_UP.equals(keyName))
+ {
+ keyCode = (String) keycodes.get(DPAD_LEFT);
+ }
+ break;
+ case 2:
+ if (DPAD_DOWN.equals(keyName))
+ {
+ keyCode = (String) keycodes.get(DPAD_UP);
+ }
+ else if (DPAD_LEFT.equals(keyName))
+ {
+ keyCode = (String) keycodes.get(DPAD_RIGHT);
+ }
+ else if (DPAD_RIGHT.equals(keyName))
+ {
+ keyCode = (String) keycodes.get(DPAD_LEFT);
+ }
+ else if (DPAD_UP.equals(keyName))
+ {
+ keyCode = (String) keycodes.get(DPAD_DOWN);
+ }
+ break;
+ case 3:
+ if (DPAD_DOWN.equals(keyName))
+ {
+ keyCode = (String) keycodes.get(DPAD_LEFT);
+ }
+ else if (DPAD_LEFT.equals(keyName))
+ {
+ keyCode = (String) keycodes.get(DPAD_UP);
+ }
+ else if (DPAD_RIGHT.equals(keyName))
+ {
+ keyCode = (String) keycodes.get(DPAD_DOWN);
+ }
+ else if (DPAD_UP.equals(keyName))
+ {
+ keyCode = (String) keycodes.get(DPAD_RIGHT);
+ }
+ break;
+ default:
+ //Does nothing, no rotation needed.
+ break;
+ }
+ return keyCode;
+ }
+
+
+ /**
+ * Merge two ImageData objects and return the merge.
+ * <br><br>
+ * @param srcData The source ImageData
+ * @param dstData The destination ImageData
+ * @param dstX The x position in dstData where srcData will be merged
+ * @param dstY The y position in dstData where srcData will be merged
+ * <br><br>
+ * @return An array containing released and pressed image data at the positions 0 and 1, respectively
+ */
+ static ImageData mergeImageGC(ImageData srcData, ImageData dstData, int dstX, int dstY) {
+
+ Shell s = new Shell();
+
+ Image srcImg = new Image(s.getDisplay(), srcData);
+ Image dstImg = new Image(s.getDisplay(), dstData);
+
+ GC gc = new GC(dstImg);
+ gc.drawImage(srcImg, dstX, dstY);
+ gc.dispose();
+
+ return dstImg.getImageData();
+
+ }
+
+ /**
+ * Creates the layout images for released and pressed buttons
+ * <br><br>
+ * @param layoutFile The model that represents the layout file
+ * @param layoutName The name of the layout where to look for images in the model
+ * @param skinFilesPath The path to the skin dir, where skin files can be found
+ * <br><br>
+ * @return An array containing released and pressed image data at the positions 0 and 1, respectively
+ */
+ static ImageData[] generateLayoutImages(LayoutFileModel layoutFile, String layoutName,
+ File skinFilesPath)
+ {
+
+ ImageData[] layoutImgs = new ImageData[3];
+
+ // Gets the layout offset for position adjustments
+ Point offsetL = getLayoutOffset(layoutFile, layoutName, skinFilesPath);
+
+ // Iterates on the names of the parts that compose the layout
+ Collection<String> partNames = layoutFile.getLayoutPartNames(layoutName);
+
+ for (String partName : partNames)
+ {
+
+ if (!layoutFile.partHasBg(partName))
+ {
+ continue;
+ }
+
+ if (partName.equals("portrait") || partName.equals("landscape"))
+ {
+ if (!partName.equals(layoutName))
+ {
+ continue;
+ }
+ }
+
+ // Gets the part offset for position adjustments and images loading decision
+ Point offsetP = getPartOffset(layoutFile, partName);
+ Point partSize = getPartImageSize(layoutFile, partName, skinFilesPath, offsetP);
+ int bgH = layoutFile.getBackgroundHeight(partName, skinFilesPath);
+ int bgW = layoutFile.getBackgroundWidth(partName, skinFilesPath);
+
+ // Loads the part images for released and pressed. If there is a part offset, it is needed
+ // to generated an expanded part image data
+ ImageData[] bgDatas = new ImageData[3];
+ if ((offsetP.x > 0) || (offsetP.y > 0) || (partSize.x > offsetP.x + bgW)
+ || (partSize.y > offsetP.y + bgH))
+ {
+ bgDatas[0] =
+ generateExpandedPartImageData(layoutFile, layoutName, partName, offsetP,
+ skinFilesPath, offsetP, partSize);
+ }
+ else
+ {
+ bgDatas[0] = getImageData(layoutFile, partName, null, skinFilesPath);
+ }
+
+ bgDatas[1] =
+ generateMergedWithButtonsImage(layoutFile, (ImageData) bgDatas[0].clone(),
+ partName, skinFilesPath, offsetP, false);
+
+ bgDatas[2] =
+ generateMergedWithButtonsImage(layoutFile, (ImageData) bgDatas[0].clone(),
+ partName, skinFilesPath, offsetP, true);
+
+ // Loop for generating layout images based on the part images above
+ for (int img = 0; img < 3; img++)
+ {
+ // A layout image is created only if it wasn't yet, no matter how many parts the layout has.
+ if (layoutImgs[img] == null)
+ {
+ Point layoutSize =
+ getLayoutImageSize(layoutFile, layoutName, skinFilesPath, offsetL);
+ layoutImgs[img] =
+ generateImageDataWithBackground(layoutFile, layoutName, layoutSize.x,
+ layoutSize.y, bgDatas[img], skinFilesPath);
+
+ }
+
+ // Copy the part image pixels into the layout image
+ // - Rotation units rotates the image in CLOCKWISE direction
+ // - The part position must be translated because:
+ // a) At Google layout file, the (x,y) position represents the upper left corner of the
+ // part image, no matter what is the rotation value
+ // b) We are generating a layout image referenced at the upper left corner of the layout
+ // image, and we must know where we must place the part image in terms of the layout reference
+ // - The part is rotated before merged to the layout image
+ int rotation = layoutFile.getPartRotationAtLayout(layoutName, partName);
+ Point partPos =
+ translatePartPosition(layoutFile, layoutName, partName, skinFilesPath,
+ offsetP, partSize);
+ bgDatas[img] = generateRotatedImage(bgDatas[img], rotation);
+
+ int startX = offsetL.x - offsetP.x + partPos.x;
+ int startY = offsetL.y - offsetP.y + partPos.y;
+ ImageData merge = mergeImageGC(bgDatas[img], layoutImgs[img], startX, startY);
+ layoutImgs[img] = merge;
+
+
+ }
+ }
+
+ return layoutImgs;
+ }
+
+
+
+ /**
+ * Translates the display information from the layout file into an Point referenced at the
+ * upper left corner of the layout image (or part image, if layouts are not supported by the skin)
+ * <br><br>
+ * @param layoutFile The model that represents the layout file
+ * @param layoutName The name of the layout being used
+ * @param partName The name of the part which contains the display
+ * @param skinFilesPath The path to the skin dir, where skin files can be found
+ * <br><br>
+ * @return A point referenced at the upper left corner of the layout image, where to draw the display
+ */
+ static Point translateDisplayPosition(LayoutFileModel layoutFile, String layoutName,
+ String partName, File skinFilesPath)
+ {
+ // Gets the parameters necessary for calculation
+ Point displayPos = layoutFile.getDisplayPosition(partName);
+ int displayW = layoutFile.getDisplayWidth(partName);
+ int displayH = layoutFile.getDisplayHeight(partName);
+ Point offsetP = getPartOffset(layoutFile, partName);
+ Point partSize;
+ if (!layoutFile.partHasBg(partName))
+ {
+ partSize = getPartImageSize(layoutFile, layoutName, skinFilesPath, offsetP);
+ }
+ else
+ {
+ partSize = getPartImageSize(layoutFile, partName, skinFilesPath, offsetP);
+ }
+
+ // Update the display position, considering part offset/size and rotation
+ displayPos =
+ translatePartElementPosition(layoutFile, layoutName, partName, skinFilesPath,
+ displayPos, displayW, displayH, offsetP, partSize);
+
+ // Adjusts the position (according to the layout offset) and returns to the caller
+ Point offsetL = getLayoutOffset(layoutFile, layoutName, skinFilesPath);
+ displayPos.x += offsetL.x;
+ displayPos.y += offsetL.y;
+
+ return displayPos;
+ }
+
+ /**
+ * Retrieves the keycodes to be used at the keys.
+ * Uses as parameter the keyboard charmap declared at the layout file model
+ * <br><br>
+ * @param layoutFile The model that represents the layout file
+ * <br><br>
+ * @return A map of properties containing the keycodes for each key of the charmap
+ * declared at the layout file
+ * <br><br>
+ * @throws SkinException If the keycode file cannot be loaded
+ */
+ static Properties getKeycodes(LayoutFileModel layoutFile, File skinFilesPath)
+ throws SkinException
+ {
+ String charmap = layoutFile.getKeyboardCharmap();
+ Properties keycodes = new Properties();
+ InputStream is = null;
+ URL url = null;
+ try
+ {
+ // If nothing is specified, use the QWERTY charmap
+ // If it is specified QWERTY or QWERTY2, use QWERTY charmap too
+ if ((charmap == null) || (charmap.equals(CHARMAP_QWERTY))
+ || (charmap.equals(CHARMAP_QWERTY2)))
+ {
+
+ url =
+ EmulatorPlugin
+ .getDefault()
+ .getBundle()
+ .getResource(
+ SdkUtils.isOphoneSDK() ? CHARMAP_OPHONE_QWERTY_FILE
+ : CHARMAP_QWERTY_FILE);
+ }
+ else if (charmap.equals(CHARMAP_AVRCP))
+ {
+ url =
+ EmulatorPlugin
+ .getDefault()
+ .getBundle()
+ .getResource(
+ SdkUtils.isOphoneSDK() ? CHARMAP_OPHONE_AVRCP_FILE
+ : CHARMAP_AVRCP_FILE);
+ }
+ else
+ {
+ warn("The skin at " + skinFilesPath.getAbsolutePath()
+ + " does not use a supported charmap");
+ return keycodes;
+ }
+
+ is = url.openStream();
+ keycodes.load(is);
+ }
+ catch (IOException e)
+ {
+ error("There was an error reading the file " + url);
+ throw new SkinException(EmulatorNLS.ERR_AndroidSkinTranslator_ErrorReadingKeycodeFile);
+ }
+ finally
+ {
+ try
+ {
+ is.close();
+ }
+ catch (IOException e)
+ {
+ StudioLogger.error("Could not close input stream: ", e.getMessage()); //$NON-NLS-1$
+ }
+ }
+
+ return keycodes;
+ }
+
+ public static Properties getQwertyKeyMap()
+ {
+ Properties keycodes = new Properties();
+ URL url =
+ EmulatorPlugin
+ .getDefault()
+ .getBundle()
+ .getResource(
+ SdkUtils.isOphoneSDK() ? CHARMAP_OPHONE_QWERTY_FILE
+ : CHARMAP_QWERTY_FILE);
+
+ InputStream in;
+ try
+ {
+ in = url.openStream();
+ keycodes.load(in);
+ }
+ catch (IOException e)
+ {
+ error("There was an error reading the file " + url);
+ }
+
+ return keycodes;
+
+ }
+
+ /**
+ * Calculates and retrieves the layout offset.
+ * The offset is different from (0, 0) if any part has negative coordinates (x or y) according to the
+ * specification of the layout file
+ * <br><br>
+ * @param layoutFile The model that represents the layout file
+ * @param layoutName The layout that we wish to have the offset calculated
+ * @param skinFilesPath The path to the skin dir, where skin files can be found
+ * <br><br>
+ * @return The layout offset
+ */
+ private static Point getLayoutOffset(LayoutFileModel layoutFile, String layoutName,
+ File skinFilesPath)
+ {
+ int minX = 0;
+ int minY = 0;
+
+ Collection<String> partNames = layoutFile.getLayoutPartNames(layoutName);
+
+ for (String partName : partNames)
+ {
+ if (partName.equals("portrait") || partName.equals("landscape"))
+ {
+ if (!partName.equals(layoutName))
+ {
+ continue;
+ }
+ }
+
+ Point offsetP = getPartOffset(layoutFile, partName);
+
+ Point partSize;
+ if (layoutFile.partHasBg(partName))
+ {
+ partSize = getPartImageSize(layoutFile, partName, skinFilesPath, offsetP);
+ }
+ else
+ {
+ continue;
+ // partSize = getPartImageSize(layoutFile, layoutName, skinFilesPath, offsetP);
+ }
+
+ // The part position needs translation because its coordinates may change due to
+ // the buttons position (if the part offset is different from (0, 0))
+ Point partPos =
+ translatePartPosition(layoutFile, layoutName, partName, skinFilesPath, offsetP,
+ partSize);
+ if (partPos.x < minX)
+ {
+ minX = partPos.x;
+ }
+ if (partPos.y < minY)
+ {
+ minY = partPos.y;
+ }
+ }
+
+ Point layoutOffset = new Point(Math.abs(minX), Math.abs(minY));
+
+ return layoutOffset;
+ }
+
+ /**
+ * Calculates and retrieves a part offset.
+ * The offset is different from (0, 0) if any button that belong to the part has negative coordinates
+ * (x or y) according to the specification of the layout file
+ * <br><br>
+ * @param layoutFile The model that represents the layout file
+ * @param partName The part that we wish to have the offset calculated
+ * <br><br>
+ * @return The part offset
+ */
+ private static Point getPartOffset(LayoutFileModel layoutFile, String partName)
+ {
+ int minX = 0;
+ int minY = 0;
+
+ Collection<String> buttonNames = layoutFile.getButtonNames(partName);
+
+ for (String buttonName : buttonNames)
+ {
+ Point buttonPos = layoutFile.getButtonPosition(partName, buttonName);
+ if (buttonPos.x < minX)
+ {
+ minX = buttonPos.x;
+ }
+ if (buttonPos.y < minY)
+ {
+ minY = buttonPos.y;
+ }
+ }
+
+ Point offset = new Point(Math.abs(minX), Math.abs(minY));
+
+ return offset;
+ }
+
+ /**
+ * Retrieves the size of the layout image.
+ * <br><br>
+ * @param layoutFile The model that represents the layout file
+ * @param layoutName The name of the layout to have its size calculated
+ * @param skinFilesPath The path to the skin dir, where skin files can be found
+ * <br><br>
+ * @return The size of the layout image
+ */
+ private static Point getLayoutImageSize(LayoutFileModel layoutFile, String layoutName,
+ File skinFilesPath, Point layoutOffset)
+ {
+ int maxX = 0;
+ int maxY = 0;
+
+ // Iterates on the layout parts, looking for the area required by each of them
+
+ Collection<String> partNames = layoutFile.getLayoutPartNames(layoutName);
+ for (String partName : partNames)
+ {
+
+ if (partName.equals("portrait") || partName.equals("landscape"))
+ {
+ if (!partName.equals(layoutName))
+ {
+ continue;
+ }
+ }
+
+ // The part position must be translated because:
+ // - At Google layout file, the part (x,y) position represents the upper left corner of the
+ // PART image, no matter what is the rotation value
+ // - We are generating a layout image referenced at the upper left corner of the LAYOUT
+ // image, and we must know where we must place the part image in terms of the layout reference
+ Point offsetP = getPartOffset(layoutFile, partName);
+ Point partSize = getPartImageSize(layoutFile, partName, skinFilesPath, offsetP);
+ Point partPos =
+ translatePartPosition(layoutFile, layoutName, partName, skinFilesPath, offsetP,
+ partSize);
+ if (layoutFile.isSwapWidthHeightNeededAtLayout(layoutName, partName))
+ {
+ // If the part is in landscape direction, we sum the width at y and
+ // height at x
+ //
+ // --------------
+ // Y | width |
+ // | |
+ // A | h | --------------------------
+ // X | e | | height |
+ // I | i | | w |
+ // S | g | | i |
+ // | h | | d |
+ // | t | | t |
+ // | | | h ROTATED PART |
+ // | PART | --------------------------
+ // --------------
+ // X AXIS
+ //
+ maxX = Math.max(maxX, layoutOffset.x + partPos.x + partSize.y);
+ maxY = Math.max(maxY, layoutOffset.y + partPos.y + partSize.x);
+ }
+ else
+ {
+ // If the part is in portrait direction, we sum the w/h as is
+ maxX = Math.max(maxX, layoutOffset.x + partPos.x + partSize.x);
+ maxY = Math.max(maxY, layoutOffset.y + partPos.y + partSize.y);
+ }
+ }
+
+ Point imgSize = new Point(maxX, maxY);
+
+ return imgSize;
+ }
+
+ /**
+ * Retrieves the minimum size of the part image.
+ * <br><br>
+ * @param layoutFile The model that represents the layout file
+ * @param partName The name of the part to have its size calculated
+ * @param skinFilesPath The path to the skin dir, where skin files can be found
+ * <br><br>
+ * @return The minimum size of the part image
+ */
+ private static Point getPartImageSize(LayoutFileModel layoutFile, String partName,
+ File skinFilesPath, Point partOffset)
+ {
+ // The initial maximum is set to be the width/height of the original image + part offset
+ //
+ // It will be the final part size IF there are no buttons with negative coordinates AND
+ // there are no buttons that is positioned in a coordinate close to the edge enough for not
+ // fitting (considering their width/height)
+ int bgWidth = layoutFile.getBackgroundWidth(partName, skinFilesPath);
+ int bgHeight = layoutFile.getBackgroundHeight(partName, skinFilesPath);
+ Point bgPos = layoutFile.getBackgroundPosition(partName);
+ int maxX = partOffset.x + bgPos.x + bgWidth;
+ int maxY = partOffset.y + bgPos.y + bgHeight;
+
+ // Iterates on the buttons, looking for the area required by each of them
+ Collection<String> buttonNames = layoutFile.getButtonNames(partName);
+
+ for (String buttonName : buttonNames)
+ {
+ int btWidth = layoutFile.getButtonWidth(partName, buttonName, skinFilesPath);
+ int btHeight = layoutFile.getButtonHeight(partName, buttonName, skinFilesPath);
+ Point buttonPos = layoutFile.getButtonPosition(partName, buttonName);
+
+ // If a button is described to be drawn outside the current maximum
+ // (x,y) position, update the (x,y) to make it fit
+ //
+ // -------------- --------------
+ // (x,y)| | | |
+ // --- | | |
+ // | | button | | (x,y) |
+ // --- with | | ----
+ // | negative | | | | button positioned in coordinates
+ // | coordinate| | | | inside the part, but with width
+ // | | | ---- large enough not to fit
+ // | | | |
+ // | | | |
+ // | PART | | PART |
+ // -------------- --------------
+ //
+ //
+ maxX = Math.max(maxX, partOffset.x + buttonPos.x + btWidth);
+ maxY = Math.max(maxY, partOffset.y + buttonPos.y + btHeight);
+ }
+
+ Point imgSize = new Point(maxX, maxY);
+
+ return imgSize;
+ }
+
+ /**
+ * Utility method for loading a image, given the model and part/button
+ * <br><br>
+ * @param layoutFile The model that represents the layout file
+ * @param partName The name of the part to have its background image loaded, or that contains the button
+ * @param buttonName The name of the button we wish the image loaded, or <code>null</code> if we aim to
+ * load the part background image
+ * @param skinFilesPath The path to the skin dir, where skin files can be found
+ * <br><br>
+ * @return An image data object, containing the image pixels
+ */
+ private static ImageData getImageData(LayoutFileModel layoutFile, String partName,
+ String buttonName, File skinFilesPath)
+ {
+ File f;
+ if (buttonName == null)
+ {
+ f = layoutFile.getBackgroundImage(partName, skinFilesPath);
+ }
+ else
+ {
+ f = new File(skinFilesPath, layoutFile.getButtonImage(partName, buttonName).getName());
+ }
+
+ return new ImageData(f.getAbsolutePath());
+ }
+
+ /**
+ * Creates a new expanded part image that contains enough space for drawing all the buttons
+ * <br><br>
+ * @param layoutFile The model that represents the layout file
+ * @param layoutName The name of the layout containing the part, if there is one, or <code>null</code>
+ * @param partName The part to be expanded
+ * @param offset The part offset to use when positioning the part image at the expanded image
+ * @param skinFilesPath The path to the skin dir, where skin files can be found
+ * <br><br>
+ * @return An image data containing the part image with more space at the background area
+ */
+ private static ImageData generateExpandedPartImageData(LayoutFileModel layoutFile,
+ String layoutName, String partName, Point offset, File skinFilesPath, Point partOffset,
+ Point partSize)
+ {
+ ImageData img = null;
+ if (partName != null)
+ {
+ // Creates an image data with dimensions defined by partSize, and background color, depth
+ // and palette defined by bgImg
+ ImageData bgImg = getImageData(layoutFile, partName, null, skinFilesPath);
+ img =
+ generateImageDataWithBackground(layoutFile, layoutName, partSize.x, partSize.y,
+ bgImg, skinFilesPath);
+
+ // Merges the bgImg pixels at the image data
+ int[] row = new int[bgImg.width];
+ for (int i = 0; i < bgImg.height; i++)
+ {
+ bgImg.getPixels(0, i, bgImg.width, row, 0);
+ img.setPixels(offset.x, offset.y + i, bgImg.width, row, 0);
+ }
+
+ }
+
+ return img;
+ }
+
+ /**
+ * Creates an image data of dimensions x, y and the same background color as srcImg
+ * <br><br>
+ * @param layoutFile The model that represents the layout file
+ * @param layoutName The name of the layout that may have a background color defined
+ * @param width The width of the image
+ * @param height The height of the image
+ * @param srcImg An image that we wish to have its depth, palette (and perhaps background color)
+ * copied to the new image
+ * <br><br>
+ * @return An image of size (width, height), same depth and palette of srcImg and with filled with the
+ * background color defined by the model or srcImg
+ */
+ private static ImageData generateImageDataWithBackground(LayoutFileModel layoutFile,
+ String layoutName, int width, int height, ImageData srcImg, File skinFilesPath)
+ {
+ ImageData img = new ImageData(width, height, srcImg.depth, srcImg.palette);
+
+ // Discover what is the background color. This is needed to fill the
+ // spaces around the layout image
+ RGB bgColor = layoutFile.getLayoutColor(layoutName, skinFilesPath);
+ int bgPixel = srcImg.palette.getPixel(bgColor);
+
+ // Set the background color to the entire layout image
+ for (int i = 0; i < img.width; i++)
+ {
+ for (int j = 0; j < img.height; j++)
+ {
+ img.setPixel(i, j, bgPixel);
+ }
+ }
+
+ return img;
+ }
+
+ /**
+ * Creates a new image, based on imageToRotate, rotated according to rotation
+ * <br><br>
+ * @param imageToRotate The image that is used as source for rotation
+ * @param rotation (rotation * 90) results in how many degrees to rotate CLOCKWISE
+ * <br><br>
+ * @return The rotated image
+ */
+ private static ImageData generateRotatedImage(ImageData imageToRotate, int rotation)
+ {
+ // For each rotation case, generates an appropriate image data (with dimensions w/h or h/w
+ // depending on whether it is landscape or portrait) and copies the imageToRotate pixels at the
+ // appropriate positions
+ ImageData rotated;
+ switch (rotation % 4)
+ {
+ case 1:
+ // 0------- 0 j h
+ // | --- | ---------
+ // j| | | | | --- |
+ // | --- | | | | |
+ // | | | --- |
+ // h------- ---------
+ rotated =
+ new ImageData(imageToRotate.height, imageToRotate.width,
+ imageToRotate.depth, imageToRotate.palette);
+ for (int i = 0; i < imageToRotate.width; i++)
+ {
+ for (int j = 0; j < imageToRotate.height; j++)
+ {
+ rotated.setPixel(imageToRotate.height - j - 1, i,
+ imageToRotate.getPixel(i, j));
+ }
+ }
+ break;
+ case 2:
+ // 0 i w 0 i w
+ // 0------- 0-------
+ // | --- | | |
+ // j| | | | j| --- |
+ // | --- | | | | |
+ // | | | --- |
+ // h------- h-------
+ rotated =
+ new ImageData(imageToRotate.width, imageToRotate.height,
+ imageToRotate.depth, imageToRotate.palette);
+ for (int i = 0; i < imageToRotate.width; i++)
+ {
+ for (int j = 0; j < imageToRotate.height; j++)
+ {
+ rotated.setPixel(imageToRotate.width - i - 1, imageToRotate.height - j - 1,
+ imageToRotate.getPixel(i, j));
+ }
+ }
+ break;
+ case 3:
+ // 0 i w
+ // 0------- 0 j h
+ // | --- | 0---------
+ // j| | | | | --- |
+ // | --- | i| | | |
+ // | | | --- |
+ // h------- w---------
+ rotated =
+ new ImageData(imageToRotate.height, imageToRotate.width,
+ imageToRotate.depth, imageToRotate.palette);
+ for (int i = 0; i < imageToRotate.width; i++)
+ {
+ for (int j = 0; j < imageToRotate.height; j++)
+ {
+ rotated.setPixel(j, imageToRotate.width - i - 1,
+ imageToRotate.getPixel(i, j));
+ }
+ }
+ break;
+ default:
+ // If 0, there is no need to rotate
+ rotated = imageToRotate;
+ break;
+ }
+
+ return rotated;
+ }
+
+ /**
+ * Creates an image that contains buttons with proper transparency. The transparency is defined by
+ * the isEnter parameter
+ *
+ * @param layoutFile The model that represents the layout file
+ * @param baseImage The image to use as base for generation. Must be a copy, because it will be
+ * changed in-place.
+ * @param partName The name of the part where to look for buttons in the model
+ * @param skinFilesPath The path to the skin dir, where skin files can be found
+ * @param partOffset What is the calculated offset for the given part
+ * @param isEnter Whether the image being created will be used for enter or pressed
+ * @return
+ */
+ private static ImageData generateMergedWithButtonsImage(LayoutFileModel layoutFile,
+ ImageData baseImage, String partName, File skinFilesPath, Point partOffset,
+ boolean isEnter)
+ {
+ // Iterate on the buttons, merging the buttons pixels to the base image
+ Collection<String> buttonNames = layoutFile.getButtonNames(partName);
+ for (String buttonName : buttonNames)
+ {
+ ImageData buttonID = getImageData(layoutFile, partName, buttonName, skinFilesPath);
+ Point buttonPos = layoutFile.getButtonPosition(partName, buttonName);
+ buttonPos.x += partOffset.x;
+ buttonPos.y += partOffset.y;
+ mergeButtonData(baseImage, buttonID, buttonPos, isEnter);
+ }
+
+ return baseImage;
+ }
+
+ /**
+ * Merges the button data to the base image
+ * <br><br>
+ * @param baseImage The image that will be modified
+ * @param buttonImage The image that have the source pixels for the merge operation
+ * @param buttonPos Where the button is located
+ * @param isEnter Whether the image being created will be used for enter or pressed
+ */
+ private static void mergeButtonData(ImageData baseImage, ImageData buttonImage,
+ Point buttonPos, boolean isEnter)
+ {
+ // Pixel/alpha buffers
+ int[] baseImgPixels = new int[buttonImage.width];
+ int[] buttonImgPixels = new int[buttonImage.width];
+ byte[] buttonAlphas = new byte[buttonImage.width];
+ int[] intButtonAlphas = new int[buttonImage.width];
+
+ // For each pixel row, get the button pixel data, apply the transparency
+ // defined by alpha and copy data to the base image
+ for (int i = 0; i < buttonImage.height; i++)
+ {
+ baseImage.getPixels(buttonPos.x, buttonPos.y + i, buttonImage.width, baseImgPixels, 0);
+ buttonImage.getPixels(0, i, buttonImage.width, buttonImgPixels, 0);
+ buttonImage.getAlphas(0, i, buttonImage.width, buttonAlphas, 0);
+
+ for (int j = 0; j < buttonAlphas.length; j++)
+ {
+ // As buttonAlphas is a signed byte array with range -127 to 128, and alpha is
+ // an integer in the range 0 to 255, overflows can happen. This calculation assures
+ // that the alpha variable has correct value in an integer array.
+ intButtonAlphas[j] =
+ (buttonAlphas[j] >= 0 ? buttonAlphas[j]
+ : ((buttonAlphas[j]) & ((byte) 0x7F)) + 128);
+ }
+
+ if (!isEnter)
+ {
+ for (int j = 0; j < buttonAlphas.length; j++)
+ {
+ if (intButtonAlphas[j] > 0)
+ {
+ intButtonAlphas[j] += (255 - intButtonAlphas[j]) / 4;
+ }
+ }
+ }
+
+ addTransparency(baseImgPixels, buttonImgPixels, intButtonAlphas, baseImage.palette,
+ buttonImage.palette);
+
+ baseImage.setPixels(buttonPos.x, buttonPos.y + i, buttonImage.width, baseImgPixels, 0);
+ }
+ }
+
+ /**
+ * Calculates transparency for the button pixels and sets them to the base
+ * pixels buffer
+ * <br><br>
+ * @param basePixels The buffer containing pixels for a given line of the base image
+ * @param buttonPixels The buffer containing pixels for a given line of the button image
+ * @param buttonAlphas The buffer containing alpha information for a given line of the button image
+ * @param basePalette The color palette used by the base image
+ * @param buttonPalette The color palette used by the button image
+ */
+ private static void addTransparency(int[] basePixels, int[] buttonPixels, int[] buttonAlphas,
+ PaletteData basePalette, PaletteData buttonPalette)
+ {
+ for (int i = 0; i < buttonPixels.length; i++)
+ {
+ RGB buttonRgb = buttonPalette.getRGB(buttonPixels[i]);
+ RGB baseRgb = basePalette.getRGB(basePixels[i]);
+
+ RGB newRgb =
+ new RGB(calculateMerge(baseRgb.red, buttonRgb.red, buttonAlphas[i]),
+ calculateMerge(baseRgb.green, buttonRgb.green, buttonAlphas[i]),
+ calculateMerge(baseRgb.blue, buttonRgb.blue, buttonAlphas[i]));
+
+ basePixels[i] = basePalette.getPixel(newRgb);
+ }
+ }
+
+ /**
+ * Calculates the transparency for a single color component
+ * <br><br>
+ * @param background The background color component
+ * @param foreground The foreground color component
+ * @param alpha The alpha to be applied. 0 means pure transparent
+ * (background color is used). 255 means pure opaque (foreground color is used)
+ * <br><br>
+ * @return The resulting color
+ */
+ private static int calculateMerge(int background, int foreground, int alpha)
+ {
+ // weighted medium of foreground color and background color, with alpha as parameter
+ return (foreground * alpha + background * (255 - alpha)) / 255;
+ }
+
+ /**
+ * Translates the part position information from the Google format to the upper-left reference used
+ * by the viewer
+ *
+ * @param layoutFile The model that represents the layout file
+ * @param layoutName The layout where the part is included
+ * @param partName The part to have its position calculated
+ * @param skinFilesPath The path to the skin dir, where skin files can be found
+ *
+ * @return The point where the part must be drawn in the layout, using as reference the upper-left
+ * corner of the layout image
+ */
+ private static Point translatePartPosition(LayoutFileModel layoutFile, String layoutName,
+ String partName, File skinFilesPath, Point partOffset, Point partSize)
+ {
+ // Collect needed data
+ int rotation = layoutFile.getPartRotationAtLayout(layoutName, partName);
+ Point partPos = layoutFile.getPartPositionAtLayout(layoutName, partName, skinFilesPath);
+ int bgWidth;
+ int bgHeight;
+ if (layoutFile.partHasBg(partName))
+ {
+ bgWidth = layoutFile.getBackgroundWidth(partName, skinFilesPath);
+ bgHeight = layoutFile.getBackgroundHeight(partName, skinFilesPath);
+ }
+ else
+ {
+ bgWidth = layoutFile.getBackgroundWidth(layoutName, skinFilesPath);
+ bgHeight = layoutFile.getBackgroundHeight(layoutName, skinFilesPath);
+ }
+ int extraOnEndW = partSize.x - bgWidth - partOffset.x;
+ int extraOnEndH = partSize.y - bgHeight - partOffset.y;
+
+ // Calculate translation
+ switch (rotation % 4)
+ {
+ case 1:
+ // Landscape, top of part image is at the right (90 degrees clockwise rotation)
+ // The point we must return is the one at the bottom-left corner of the part, considering
+ // offset and extra space in the end of the part image (which was added so that buttons
+ // at the right side of the part fit)
+ //
+ // BEFORE AFTER
+ // (0,0) (0,0)
+ // --------- ---------
+ // | --- | | --- |
+ // | | | | | | | |
+ // | --- | | --- |
+ // --------- ---------
+ partPos.x = partPos.x - partOffset.y - bgHeight;
+ partPos.y = partPos.y - extraOnEndW;
+ break;
+ case 2:
+ // Portrait, top of part image is at the bottom (180 degrees clockwise rotation)
+ // The point we must return is the one at the bottom-right corner of the part
+ //
+ // BEFORE AFTER
+ // (0,0)
+ // ------- -------
+ // | | | |
+ // | --- | | --- |
+ // | | | | | | | |
+ // | --- | | --- |
+ // ------- -------
+ // (0,0)
+ partPos.x = partPos.x - bgWidth;
+ partPos.y = partPos.y - bgHeight;
+ break;
+ case 3:
+ // Landscape, top of part image is at the left (270 degrees clockwise rotation)
+ // The point we must return is the one at the top-right corner of the part, considering
+ // offset and extra space in the end of the part image (which was added so that buttons
+ // at the right side of the part fit)
+ //
+ // BEFORE AFTER
+ // (0,0)
+ // --------- ---------
+ // | --- | | --- |
+ // | | | | | | | |
+ // | --- | | --- |
+ // --------- ---------
+ //(0,0)
+ partPos.x = partPos.x - extraOnEndH;
+ partPos.y = partPos.y - partOffset.x - bgWidth;
+ break;
+ default:
+ // No translation is needed when there is no rotation
+ break;
+ }
+
+ return partPos;
+ }
+
+ /**
+ * Translates the button position information from the Google format to the upper-left reference used
+ * by the viewer
+ *
+ * @param layoutFile The model that represents the layout file
+ * @param layoutName The layout where the part is included, or <code>null</code> if the skin does
+ * not support layout
+ * @param partName The part where the button is included
+ * @param buttonName The button to have its position calculated
+ * @param skinFilesPath The path to the skin dir, where skin files can be found
+ *
+ * @return The point where the button must be drawn in the part, using as reference the upper-left
+ * corner of the part image
+ */
+ private static Point translateButtonPosition(LayoutFileModel layoutFile, String layoutName,
+ String partName, String buttonName, File skinFilesPath, Point partOffset, Point partSize)
+ {
+ // Collect button data
+ Point buttonPos = layoutFile.getButtonPosition(partName, buttonName);
+ int buttonW = layoutFile.getButtonWidth(partName, buttonName, skinFilesPath);
+ int buttonH = layoutFile.getButtonHeight(partName, buttonName, skinFilesPath);
+
+ // Update the button position, considering part offset/size and rotation
+ buttonPos =
+ translatePartElementPosition(layoutFile, layoutName, partName, skinFilesPath,
+ buttonPos, buttonW, buttonH, partOffset, partSize);
+
+ return buttonPos;
+ }
+
+ /**
+ * Translates a part element position (display/buttons) from the Google format to the upper-left
+ * reference used by the viewer
+ *
+ * @param layoutFile The model that represents the layout file
+ * @param layoutName The layout where the part is included, or <code>null</code> if the skin does
+ * not support layout
+ * @param partName The part where the element is included
+ * @param skinFilesPath The path to the skin dir, where skin files can be found
+ * @param partElementPos The position of the part element as described by layoutFile
+ * @param partElementWidth The width of the part element as described by layoutFile
+ * @param partElementHeight The height of the part element as described by layoutFile
+ *
+ * @return The point where the element must be drawn in the part, using as reference the upper-left
+ * corner of the part image
+ */
+ private static Point translatePartElementPosition(LayoutFileModel layoutFile,
+ String layoutName, String partName, File skinFilesPath, Point partElementPos,
+ int partElementWidth, int partElementHeight, Point partOffset, Point partSize)
+ {
+ Point translated = new Point(0, 0);
+ int rotation = layoutFile.getPartRotationAtLayout(layoutName, partName);
+
+ // Due to rotation, the part position will be referenced to a non-appropriate image corner.
+ // The following operation guarantees that the part position is still at the upper left corner
+ // even after rotation.
+ Point partPos =
+ translatePartPosition(layoutFile, layoutName, partName, skinFilesPath, partOffset,
+ partSize);
+
+ // Calculate position.
+ //
+ // OBS: Every time we need the part size for our the calculation, we must subtract the part offset
+ // as well. This is because during part size calculation, we have already summed the offset and we
+ // need to rework the offset due to rotation (i.e., sometimes we need to sum offset.y instead of
+ // offset.x due to rotation, and vice-versa). This is being illustrated at the lines below with
+ // parenthesis.
+ switch (rotation % 4)
+ {
+ case 1:
+ // BEFORE AFTER
+ //(0,0)
+ // --------- (0,0)
+ // | | -----------
+ // |(x,y) | (x,y) |
+ // | --- | | --- |
+ // | | | | | | | |
+ // | --- | | --- |
+ // --------- -----------
+ translated.x =
+ partPos.x - partOffset.x + (partSize.y - partOffset.y) - partElementPos.y
+ - partElementHeight;
+ translated.y = partPos.y - partOffset.y + partOffset.x + partElementPos.x;
+ break;
+ case 2:
+ // BEFORE AFTER
+ //(0,0) (0,0)
+ // --------- ---------
+ // | | (x,y) --- |
+ // |(x,y) | | | | |
+ // | --- | | --- |
+ // | | | | | |
+ // | --- | | |
+ // --------- ---------
+ translated.x =
+ partPos.x + (partSize.x - partOffset.x) - partOffset.x - partElementPos.x
+ - partElementWidth;
+ translated.y =
+ partPos.y + (partSize.y - partOffset.y) - partOffset.y - partElementPos.y
+ - partElementHeight;
+ break;
+ case 3:
+ // BEFORE AFTER
+ //(0,0)
+ // --------- (0,0)
+ // | | -----------
+ // |(x,y) | |(x,y)--- |
+ // | --- | | | | |
+ // | | | | | --- |
+ // | --- | | |
+ // --------- -----------
+ translated.x = partPos.x - partOffset.x + partOffset.y + partElementPos.y;
+ translated.y =
+ partPos.y - partOffset.y + (partSize.x - partOffset.x) - partElementPos.x
+ - partElementWidth;
+ break;
+ default:
+ translated.x = partElementPos.x + partPos.x;
+ translated.y = partElementPos.y + partPos.y;
+ break;
+ }
+
+ return translated;
+ }
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/skin/android/parser/ILayoutBean.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/skin/android/parser/ILayoutBean.java
new file mode 100644
index 0000000..70e9510
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/skin/android/parser/ILayoutBean.java
@@ -0,0 +1,27 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.emulator.skin.android.parser;
+
+public interface ILayoutBean
+{
+ /**
+ * Sets the key/value pair at the bean, according to the rules defined by each bean
+ *
+ * @param key The key that represents the item to be set
+ * @param value The value to be set to the item represented by key
+ */
+ void setKeyValue(String key, String value);
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/skin/android/parser/ILayoutConstants.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/skin/android/parser/ILayoutConstants.java
new file mode 100644
index 0000000..98f71f3
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/skin/android/parser/ILayoutConstants.java
@@ -0,0 +1,78 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.emulator.skin.android.parser;
+
+/**
+ * DESCRIPTION:
+ * This class lists all default constants contained into a layout file
+ *
+ * RESPONSIBILITY:
+ * Support on parsing of layout files
+ *
+ * COLABORATORS:
+ * None.
+ *
+ * USAGE:
+ * This class is intended to be used by the LayoutFileParser class only
+ */
+interface ILayoutConstants
+{
+ String OPEN_BRACKET = "{";
+
+ String CLOSE_BRACKET = "}";
+
+ String MAIN_LEVEL_PARTS = "parts";
+
+ String MAIN_LEVEL_LAYOUTS = "layouts";
+
+ String MAIN_LEVEL_KEYBOARD = "keyboard";
+
+ String MAIN_LEVEL_NETWORK = "network";
+
+ String MAIN_LEVEL_BACKGROUND = "background";
+
+ String MAIN_LEVEL_DISPLAY = "display";
+
+ String MAIN_LEVEL_BUTTON = "button";
+
+ String PART_BUTTONS = "buttons";
+
+ String KEYBOARD_CHARMAP = "charmap";
+
+ String NETWORK_SPEED = "speed";
+
+ String NETWORK_DELAY = "delay";
+
+ String ATTR_WIDTH = "width";
+
+ String ATTR_HEIGHT = "height";
+
+ String ATTR_X = "x";
+
+ String ATTR_Y = "y";
+
+ String ATTR_IMAGE = "image";
+
+ String ATTR_NAME = "name";
+
+ String LAYOUT_COLOR = "color";
+
+ String LAYOUT_EVENT = "event";
+
+ String DPAD_ROTATION = "dpad-rotation";
+
+ String PARTREF_ROTATION = "rotation";
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/skin/android/parser/ImagePositionBean.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/skin/android/parser/ImagePositionBean.java
new file mode 100644
index 0000000..d8a5c12
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/skin/android/parser/ImagePositionBean.java
@@ -0,0 +1,175 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.emulator.skin.android.parser;
+
+import java.io.File;
+
+import org.eclipse.swt.graphics.ImageData;
+import org.eclipse.ui.PlatformUI;
+
+/**
+ * DESCRIPTION:
+ * This class represents a node containing image, x and y data
+ *
+ * RESPONSIBILITY:
+ * Represent image nodes of the layout file
+ *
+ * COLABORATORS:
+ * None.
+ *
+ * USAGE:
+ * This class is intended to be used by the LayoutFileParser and LayoutFileModel classes only
+ */
+public class ImagePositionBean implements ILayoutConstants, ILayoutBean
+{
+ /**
+ * Name of the node
+ */
+ private String name;
+
+ /**
+ * Location of the file that contains the image
+ */
+ private File imageLocation;
+
+ /**
+ * X position where to draw the image
+ */
+ private String xPos;
+
+ /**
+ * Y position where to draw the image
+ */
+ private String yPos;
+
+ private int width = -1;
+
+ private int height = -1;
+
+ /**
+ * Constructor
+ * Creates the node and assign a name to it
+ */
+ ImagePositionBean(String name)
+ {
+ this.name = name;
+ }
+
+ /**
+ * Retrieves the image location
+ *
+ * @return The image location
+ */
+ File getImageLocation()
+ {
+ return imageLocation;
+ }
+
+ /**
+ * Retrieves the X position where to draw the image
+ *
+ * @return The image X position
+ */
+ String getXPos()
+ {
+ return xPos;
+ }
+
+ /**
+ * Retrieves the Y position where to draw the image
+ *
+ * @return The image Y position
+ */
+ String getYPos()
+ {
+ return yPos;
+ }
+
+ int getWidth(File skinFilesPath)
+ {
+ if (width == -1)
+ {
+ populateWidthHeight(skinFilesPath);
+ }
+
+ return width;
+ }
+
+ int getHeight(File skinFilesPath)
+ {
+ if (width == -1)
+ {
+ populateWidthHeight(skinFilesPath);
+ }
+
+ return height;
+ }
+
+ /**
+ * Retrieves the name of this node
+ *
+ * @return The node name
+ */
+ String getName()
+ {
+ return name;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString()
+ {
+ return "ImagePosition: " + name;
+ }
+
+ private void populateWidthHeight(final File skinFilesPath)
+ {
+ PlatformUI.getWorkbench().getDisplay().syncExec(new Runnable()
+ {
+ public void run()
+ {
+ ImageData id =
+ new ImageData(new File(skinFilesPath, imageLocation.getName())
+ .getAbsolutePath());
+ width = id.width;
+ height = id.height;
+ }
+ });
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.emulator.skin.android.parser.ILayoutBean#setKeyValue(java.lang.String, java.lang.String)
+ */
+ public void setKeyValue(String key, String value)
+ {
+ if (ATTR_IMAGE.equals(key))
+ {
+ imageLocation = new File(value);
+ }
+ else if (ATTR_X.equals(key))
+ {
+ xPos = value;
+ }
+ else if (ATTR_Y.equals(key))
+ {
+ yPos = value;
+ }
+ }
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/skin/android/parser/LayoutBean.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/skin/android/parser/LayoutBean.java
new file mode 100644
index 0000000..586f10e
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/skin/android/parser/LayoutBean.java
@@ -0,0 +1,231 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.emulator.skin.android.parser;
+
+import java.util.Collection;
+import java.util.LinkedHashSet;
+
+import org.eclipse.swt.graphics.RGB;
+
+/**
+ * DESCRIPTION:
+ * This class represents a layout structure from the layout file
+ *
+ * RESPONSIBILITY:
+ * Represent layout structures
+ *
+ * COLABORATORS:
+ * None.
+ *
+ * USAGE:
+ * This class is intended to be used by the LayoutFileParser and LayoutFileModel classes only
+ */
+public class LayoutBean implements ILayoutConstants, ILayoutBean
+{
+ /**
+ * Default name of a landscape layout (used when creating pseudo-layouts)
+ * Pseudo-layouts are created when the skin has only one part and no layouts
+ */
+ public static final String DEFAULT_LAYOUT_NAME = "default";
+
+ /**
+ * Default name of a portrait layout (used when creating pseudo-layouts)
+ * Pseudo-layouts are created when the skin has only one part and no layouts
+ */
+ public static final String ROTATED_LAYOUT_NAME = "rotated";
+
+ /**
+ * The layout structure name
+ */
+ private String name;
+
+ /**
+ * The layout width
+ */
+ private String width;
+
+ /**
+ * The layout height
+ */
+ private String height;
+
+ /**
+ * Thw layout color
+ */
+ private RGB color;
+
+ /**
+ * The event command used to switch to this layout
+ */
+ private String event;
+
+ /**
+ * Dpad rotation, steps of 90°
+ */
+ private int dpadRotation;
+
+ /**
+ * The collection of parts that integrate this layout
+ */
+ private Collection<PartRefBean> parts = new LinkedHashSet<PartRefBean>();
+
+ /**
+ * Constructor
+ * Builds a new layout structure with the given name
+ *
+ * @param name The layout name
+ */
+ LayoutBean(String name)
+ {
+ this.name = name;
+ }
+
+ /**
+ * Creates a new reference to a part, registers it and returns it to the user
+ *
+ * @param refName The name to assign to the part reference
+ *
+ * @return The part reference
+ */
+ PartRefBean newPartRef(String refName)
+ {
+ PartRefBean bean = new PartRefBean(refName);
+ parts.add(bean);
+ return bean;
+ }
+
+ /**
+ * Retrieves the layout width
+ *
+ * @return The layout width
+ */
+ String getWidth()
+ {
+ return width;
+ }
+
+ /**
+ * Retrieves the layout height
+ *
+ * @return The layout height
+ */
+ String getHeight()
+ {
+ return height;
+ }
+
+ /**
+ * Retrieves the layout color
+ *
+ * @return The layout color
+ */
+ RGB getColor()
+ {
+ return color;
+ }
+
+ /**
+ * Retrieves the event to switch to this layout
+ *
+ * @param event The event
+ */
+ String getEvent()
+ {
+ return event;
+ }
+
+ /**
+ * @return the dpadRotation
+ */
+ int getDpadRotation()
+ {
+ return dpadRotation;
+ }
+
+ /**
+ * Retrieves this layout name
+ *
+ * @return This layout name
+ */
+ String getName()
+ {
+ return name;
+ }
+
+ /**
+ * Retrieves all references to parts from this layout
+ *
+ * @return A collection containing part references
+ */
+ Collection<PartRefBean> getPartRefs()
+ {
+ return parts;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString()
+ {
+ return "Layout: " + name;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.emulator.skin.android.parser.ILayoutBean#setKeyValue(java.lang.String, java.lang.String)
+ */
+ public void setKeyValue(String key, String value)
+ {
+ if (ATTR_WIDTH.equals(key))
+ {
+ width = value;
+ }
+ else if (ATTR_HEIGHT.equals(key))
+ {
+ height = value;
+ }
+ else if (LAYOUT_COLOR.equals(key))
+ {
+ Integer colorInt = Integer.decode(value);
+ int blue = colorInt.intValue() & 0xFF;
+ int green = (colorInt.intValue() & 0xFF00) >> 8;
+ int red = (colorInt.intValue() & 0xFF0000) >> 16;
+ RGB rgb = new RGB(red, green, blue);
+
+ color = rgb;
+ }
+ else if (LAYOUT_EVENT.equals(key))
+ {
+ event = value;
+ }
+ else if (DPAD_ROTATION.equals(key))
+ {
+ int intValue;
+ try
+ {
+ intValue = Integer.parseInt(value);
+ }
+ catch (NumberFormatException e)
+ {
+ //Assume there's no rotation
+ intValue = 0;
+ }
+ dpadRotation = intValue;
+ }
+ }
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/skin/android/parser/LayoutFileModel.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/skin/android/parser/LayoutFileModel.java
new file mode 100644
index 0000000..922b396
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/skin/android/parser/LayoutFileModel.java
@@ -0,0 +1,706 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.emulator.skin.android.parser;
+
+import java.io.File;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.LinkedHashSet;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.eclipse.swt.graphics.ImageData;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.RGB;
+
+/**
+ * DESCRIPTION:
+ * This class represents a skin layout file
+ *
+ * RESPONSIBILITY:
+ * Represent all the contents of a skin layout file
+ *
+ * COLABORATORS:
+ * None.
+ *
+ * USAGE:
+ * Use the class public APIs to retrieve data of the skin
+ */
+public class LayoutFileModel
+{
+ /**
+ * Event to rotate the screen
+ */
+ private static final String ROTATE_SCREEN_EVENT = "EV_SW:0:0";
+
+ /**
+ * Command to rotate the screen
+ */
+ private static final String ROTATE_SCREEN_CMD = "5 0 0";
+
+ /**
+ * Command to get the screen back to its default orientation
+ */
+ private static final String RETURN_TO_DEFAULT_SCREEN_CMD = "5 0 1";
+
+ /**
+ * A collection containing all layouts read from the layout file
+ */
+ private Collection<LayoutBean> layouts;
+
+ /**
+ * A collection containing all parts read from the layout file
+ */
+ private Collection<PartBean> parts = new LinkedHashSet<PartBean>();
+
+ /**
+ * The keyboard charmap used by this skin
+ */
+ private String keyboardCharmap;
+
+ /**
+ * The default network speed used by this skin
+ */
+ private String networkSpeed;
+
+ /**
+ * The default network delay used by this skin
+ */
+ private String networkDelay;
+
+ /**
+ * Sets the keyboard charmap used by this skin
+ *
+ * @param keyboardCharmap The keyboard charmap name
+ */
+ void setKeyboardCharmap(String keyboardCharmap)
+ {
+ this.keyboardCharmap = keyboardCharmap;
+ }
+
+ /**
+ * Sets the default network speed used by this skin
+ *
+ * @param keyboardCharmap The default network speed used by this skin
+ */
+ void setNetworkSpeed(String networkSpeed)
+ {
+ this.networkSpeed = networkSpeed;
+ }
+
+ /**
+ * Sets the default network delay of this skin
+ *
+ * @param The default network delay
+ */
+ void setNetworkDelay(String networkDelay)
+ {
+ this.networkDelay = networkDelay;
+ }
+
+ /**
+ * Creates a new part, registers it and returns it to the user
+ *
+ * This version is used when the skin is simple, i.e. when there is a single
+ * part and no layouts defined. To support landscape/portrait rotation, we create
+ * "pseudo-layouts" at memory as well.
+ *
+ * @return The part
+ */
+ PartBean newPart()
+ {
+ return newPart(PartBean.UNIQUE_PART);
+ }
+
+ /**
+ * Creates a new part, registers it and returns it to the user
+ * Use this version when the skin have multiple parts
+ * (i.e., if there is a "parts" element in the layout file)
+ *
+ * @param name The part name
+ *
+ * @return The part
+ */
+ PartBean newPart(String name)
+ {
+ PartBean bean = new PartBean(name);
+ parts.add(bean);
+ return bean;
+ }
+
+ /**
+ * Creates a new layout, registers it and returns it to the user
+ *
+ * @param name The layout name
+ *
+ * @return The layout
+ */
+ LayoutBean newLayout(String name)
+ {
+ LayoutBean bean = new LayoutBean(name);
+ if (layouts == null)
+ {
+ layouts = new LinkedHashSet<LayoutBean>();
+ }
+ layouts.add(bean);
+
+ return bean;
+ }
+
+ /**
+ * Retrieves the keyboard charmap used by this skin
+ *
+ * @return The keyboard charmap name
+ */
+ public String getKeyboardCharmap()
+ {
+ return keyboardCharmap;
+ }
+
+ /**
+ * Retrieves the default network speed of this skin
+ *
+ * @return The default network speed
+ */
+ public String getNetworkSpeed()
+ {
+ return networkSpeed;
+ }
+
+ /**
+ * Retrieves the default network delay of this skin
+ *
+ * @return The default network delay
+ */
+ public String getNetworkDelay()
+ {
+ return networkDelay;
+ }
+
+ public List<String> getLayoutNames()
+ {
+ List<String> layoutNames = new LinkedList<String>();
+ if (layouts != null)
+ {
+ for (LayoutBean bean : layouts)
+ {
+ layoutNames.add(bean.getName());
+ }
+ }
+
+ return layoutNames;
+ }
+
+ public Collection<String> getPartNames()
+ {
+ Collection<String> partNames = new LinkedHashSet<String>();
+ for (PartBean bean : parts)
+ {
+ partNames.add(bean.getName());
+ }
+
+ return partNames;
+ }
+
+ public Collection<String> getLayoutPartNames(String layoutName)
+ {
+ Collection<String> layoutPartNames = new LinkedHashSet<String>();
+ LayoutBean bean = getLayoutByName(layoutName);
+ if (bean != null)
+ {
+ Collection<PartRefBean> partRefs = bean.getPartRefs();
+ if (partRefs != null)
+ {
+ for (PartRefBean aRef : partRefs)
+ {
+ layoutPartNames.add(aRef.getPartName());
+ }
+ }
+ }
+
+ return layoutPartNames;
+ }
+
+ public Point getPartPositionAtLayout(String layoutName, String partName, File skinFilesPath)
+ {
+ Point partPosition = null;
+
+ LayoutBean bean = getLayoutByName(layoutName);
+ if (bean != null)
+ {
+ Collection<PartRefBean> partRefs = bean.getPartRefs();
+ for (PartRefBean prBean : partRefs)
+ {
+ if (prBean.getPartName().equals(partName))
+ {
+ int x = Integer.parseInt(prBean.getX());
+ String yStr = prBean.getY();
+ int y;
+ if (yStr == null)
+ {
+ y = getBackgroundWidth(partName, skinFilesPath);
+ }
+ else
+ {
+ y = Integer.parseInt(yStr);
+ }
+
+ partPosition = new Point(x, y);
+ break;
+ }
+ }
+ }
+ else
+ {
+ partPosition = new Point(0, 0);
+ }
+
+ return partPosition;
+ }
+
+ public int getPartRotationAtLayout(String layoutName, String partName)
+ {
+ int partRotation = 0;
+
+ LayoutBean bean = getLayoutByName(layoutName);
+ if (bean != null)
+ {
+ Collection<PartRefBean> partRefs = bean.getPartRefs();
+ for (PartRefBean prBean : partRefs)
+ {
+ if (prBean.getPartName().equals(partName))
+ {
+ String rotStr = prBean.getRotation();
+ if (rotStr != null)
+ {
+ partRotation = Integer.parseInt(rotStr);
+ }
+ break;
+ }
+ }
+ }
+
+ return partRotation;
+ }
+
+ public int getDpadRotation(String layoutName)
+ {
+ int dPadRotation = 0;
+ LayoutBean bean = getLayoutByName(layoutName);
+ if (bean != null)
+ {
+ dPadRotation = bean.getDpadRotation();
+ }
+
+ return dPadRotation;
+ }
+
+ public Collection<String> getButtonNames(String partName)
+ {
+ Collection<String> buttonNames = new LinkedHashSet<String>();
+
+ PartBean bean = getPartByName(partName);
+ if (bean != null)
+ {
+ Collection<ImagePositionBean> buttons = bean.getButtons();
+ if (buttons != null)
+ {
+ for (ImagePositionBean button : buttons)
+ {
+ buttonNames.add(button.getName());
+ }
+ }
+ }
+ return buttonNames;
+ }
+
+ public int getLayoutWidth(String layoutName)
+ {
+ int width = 0;
+ LayoutBean layout = getLayoutByName(layoutName);
+ if (layout != null)
+ {
+ width = Integer.parseInt(layout.getWidth());
+ }
+
+ return width;
+ }
+
+ public int getLayoutHeight(String layoutName)
+ {
+ int height = 0;
+ LayoutBean layout = getLayoutByName(layoutName);
+ if (layout != null)
+ {
+ height = Integer.parseInt(layout.getHeight());
+ }
+
+ return height;
+ }
+
+ public RGB getLayoutColor(String layoutName, File skinFilesPath)
+ {
+ RGB color = null;
+ LayoutBean layout = getLayoutByName(layoutName);
+ if (layout != null)
+ {
+ color = layout.getColor();
+ if (color == null)
+ {
+ String mainPart = getMainPartName(layoutName);
+ File image = getBackgroundImage(mainPart, skinFilesPath);
+ ImageData img = new ImageData(image.getAbsolutePath());
+ int pixel = img.getPixel(0, 0);
+ color = img.palette.getRGB(pixel);
+ layout.setKeyValue(ILayoutConstants.LAYOUT_COLOR,
+ "0x" + Integer.toHexString(color.red) + Integer.toHexString(color.green)
+ + Integer.toHexString(color.blue));
+ }
+ }
+ else
+ {
+ color = new RGB(255, 255, 255);
+ }
+
+ return color;
+ }
+
+ public String getLayoutEvent(String layoutName)
+ {
+ String event = "";
+ LayoutBean layout = getLayoutByName(layoutName);
+ if (layout != null)
+ {
+ event = layout.getEvent();
+ }
+
+ return event;
+ }
+
+ public String getLayoutSwitchCommand(String layoutName)
+ {
+ LayoutBean bean = getLayoutByName(layoutName);
+ String event = bean.getEvent();
+ if (ROTATE_SCREEN_EVENT.equals(event))
+ {
+ return ROTATE_SCREEN_CMD;
+ }
+ else
+ {
+ return RETURN_TO_DEFAULT_SCREEN_CMD;
+ }
+ }
+
+ public boolean isSwapWidthHeightNeededAtLayout(String layoutName)
+ {
+ return isSwapWidthHeightNeededAtLayout(layoutName, getMainPartName(layoutName));
+ }
+
+ public boolean isSwapWidthHeightNeededAtLayout(String layoutName, String partName)
+ {
+ boolean isRotated = false;
+ if (partName != null)
+ {
+ int rotation = getPartRotationAtLayout(layoutName, partName);
+ isRotated = rotation % 2 != 0;
+ }
+ return isRotated;
+ }
+
+ public File getBackgroundImage(String partName, File skinFilesPath)
+ {
+ File backgroundFile = null;
+ PartBean part = getPartByName(partName);
+ if (part != null)
+ {
+ ImagePositionBean backgroundBean = part.getBackground();
+ if (backgroundBean != null)
+ {
+ backgroundFile =
+ new File(skinFilesPath, backgroundBean.getImageLocation().getName());
+ }
+ }
+
+ return backgroundFile;
+ }
+
+ public Point getBackgroundPosition(String partName)
+ {
+ Point bgPosition = null;
+ PartBean part = getPartByName(partName);
+ if (part != null)
+ {
+ ImagePositionBean bgBean = part.getBackground();
+ String xStr = null;
+ String yStr = null;
+ if (bgBean != null)
+ {
+
+ xStr = bgBean.getXPos();
+ yStr = bgBean.getYPos();
+ }
+
+ if ((xStr != null) && (yStr != null))
+ {
+ bgPosition = new Point(Integer.parseInt(xStr), Integer.parseInt(yStr));
+ }
+ else
+ {
+ bgPosition = new Point(0, 0);
+ }
+ }
+
+ return bgPosition;
+ }
+
+ public int getBackgroundWidth(String partName, File skinFilesPath)
+ {
+ int width = -1;
+ PartBean part = getPartByName(partName);
+ if (part != null)
+ {
+ ImagePositionBean bgBean = part.getBackground();
+ if (bgBean != null)
+ {
+ width = bgBean.getWidth(skinFilesPath);
+ }
+ }
+
+ return width;
+ }
+
+ public int getBackgroundHeight(String partName, File skinFilesPath)
+ {
+ int height = -1;
+ PartBean part = getPartByName(partName);
+ if (part != null)
+ {
+ ImagePositionBean bgBean = part.getBackground();
+ if (bgBean != null)
+ {
+ height = bgBean.getHeight(skinFilesPath);
+ }
+ }
+
+ return height;
+ }
+
+ public File getButtonImage(String partName, String buttonName)
+ {
+ File buttonFile = null;
+ ImagePositionBean button = getButtonByName(partName, buttonName);
+ if (button != null)
+ {
+ buttonFile = button.getImageLocation();
+ }
+
+ return buttonFile;
+ }
+
+ public Point getButtonPosition(String partName, String buttonName)
+ {
+ Point buttonPos = null;
+ ImagePositionBean button = getButtonByName(partName, buttonName);
+ if (button != null)
+ {
+ buttonPos =
+ new Point(Integer.parseInt(button.getXPos()),
+ Integer.parseInt(button.getYPos()));
+ }
+
+ return buttonPos;
+ }
+
+ public int getButtonWidth(String partName, String buttonName, File skinFilesPath)
+ {
+ int width = -1;
+ ImagePositionBean button = getButtonByName(partName, buttonName);
+ if (button != null)
+ {
+ width = button.getWidth(skinFilesPath);
+ }
+
+ return width;
+ }
+
+ public int getButtonHeight(String partName, String buttonName, File skinFilesPath)
+ {
+ int height = -1;
+ ImagePositionBean button = getButtonByName(partName, buttonName);
+ if (button != null)
+ {
+ height = button.getHeight(skinFilesPath);
+ }
+
+ return height;
+ }
+
+ public Point getDisplayPosition(String partName)
+ {
+ Point displayPos = null;
+
+ PartBean bean = getPartByName(partName);
+ if (bean != null)
+ {
+ RectangleBean dispBean = bean.getDisplay();
+ displayPos =
+ new Point(Integer.parseInt(dispBean.getXPos()), Integer.parseInt(dispBean
+ .getYPos()));
+ }
+
+ return displayPos;
+ }
+
+ public int getDisplayWidth(String partName)
+ {
+ int width = -1;
+
+ PartBean bean = getPartByName(partName);
+ if (bean != null)
+ {
+ RectangleBean dispBean = bean.getDisplay();
+ width = Integer.parseInt(dispBean.getWidth());
+ }
+
+ return width;
+ }
+
+ public int getDisplayHeight(String partName)
+ {
+ int height = -1;
+
+ PartBean bean = getPartByName(partName);
+ if (bean != null)
+ {
+ RectangleBean dispBean = bean.getDisplay();
+ height = Integer.parseInt(dispBean.getHeight());
+ }
+
+ return height;
+ }
+
+ private LayoutBean getLayoutByName(String layoutName)
+ {
+ LayoutBean layoutToReturn = null;
+
+ if (layouts != null)
+ {
+ Iterator<LayoutBean> it = layouts.iterator();
+ while (it.hasNext())
+ {
+ LayoutBean bean = it.next();
+ if (bean.getName().equals(layoutName))
+ {
+ layoutToReturn = bean;
+ break;
+ }
+ }
+ }
+
+ return layoutToReturn;
+ }
+
+ public PartBean getPartByName(String partName)
+ {
+ PartBean partToReturn = null;
+
+ Iterator<PartBean> it = parts.iterator();
+ while (it.hasNext())
+ {
+ PartBean bean = it.next();
+ if (bean.getName().equals(partName))
+ {
+ partToReturn = bean;
+ break;
+ }
+ }
+
+ return partToReturn;
+ }
+
+ private ImagePositionBean getButtonByName(String partName, String buttonName)
+ {
+ ImagePositionBean buttonToReturn = null;
+ PartBean pBean = getPartByName(partName);
+ if (pBean != null)
+ {
+ Collection<ImagePositionBean> buttons = pBean.getButtons();
+ if (buttons != null)
+ {
+ for (ImagePositionBean bBean : buttons)
+ {
+ if (bBean.getName().equals(buttonName))
+ {
+ buttonToReturn = bBean;
+ break;
+ }
+ }
+ }
+ }
+
+ return buttonToReturn;
+ }
+
+ /**
+ * Retrieves a layout main part, i.e. the first part that contains a display
+ * In future releases, check if it is needed to change this concept of "main part"
+ *
+ * @param layoutName The layout which main part is to be discovered
+ *
+ * @return The name of the layout main part
+ */
+ public String getMainPartName(String layoutName)
+ {
+ String mainPartName = null;
+ for (LayoutBean layout : layouts)
+ {
+ if (layout.getName().equals(layoutName))
+ {
+ Collection<PartRefBean> allRefs = layout.getPartRefs();
+ for (PartRefBean partRef : allRefs)
+ {
+ String partName = partRef.getPartName();
+ for (PartBean aPart : parts)
+ {
+ String aPartName = aPart.getName();
+ if ((aPartName.equals(partName)) && (aPart.getDisplay() != null))
+ {
+ mainPartName = aPartName;
+ break;
+ }
+ }
+ if (mainPartName != null)
+ {
+ break;
+ }
+ }
+ }
+ if (mainPartName != null)
+ {
+ break;
+ }
+ }
+
+ return mainPartName;
+ }
+
+ public boolean partHasBg(String partName)
+ {
+ PartBean part = getPartByName(partName);
+ ImagePositionBean background = part.getBackground();
+ return background != null;
+ }
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/skin/android/parser/LayoutFileParser.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/skin/android/parser/LayoutFileParser.java
new file mode 100644
index 0000000..92d7664
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/skin/android/parser/LayoutFileParser.java
@@ -0,0 +1,552 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.emulator.skin.android.parser;
+
+import static com.motorola.studio.android.common.log.StudioLogger.error;
+import static com.motorola.studio.android.common.log.StudioLogger.warn;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.StringReader;
+import java.net.URL;
+import java.nio.CharBuffer;
+import java.util.Collection;
+import java.util.EmptyStackException;
+import java.util.Stack;
+
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.emulator.EmulatorPlugin;
+import com.motorola.studio.android.emulator.core.exception.SkinException;
+import com.motorola.studio.android.emulator.i18n.EmulatorNLS;
+
+/**
+ * DESCRIPTION:
+ * This class parses a layout file into a LayoutFileModel object
+ *
+ * RESPONSIBILITY:
+ * Parse the layout file
+ *
+ * COLABORATORS:
+ * None.
+ *
+ * USAGE:
+ * Call readLayout method passing a file to be parsed and retrieve the
+ * correspondent LayoutFileModel object
+ */
+public class LayoutFileParser implements ILayoutConstants
+{
+ /**
+ * Name of the layout descriptor file at the skin folder
+ */
+ private static final String LAYOUT_FILE_NAME = "layout";
+
+ /**
+ * Name of the pseudo layout descriptor file at the res folder
+ */
+ private static final String PSEUDO_LAYOUT_FILE = "res/pseudolayout";
+
+ /**
+ * The pattern used for generating tokens out of the layout file
+ */
+ private static final String SPLIT_PATTERN = "[\n\r \t]+";
+
+ /**
+ * Parses a layout file
+ *
+ * @param skinFilesPath The path to the skin folder
+ *
+ * @return a model containing all data read from the layout file
+ *
+ * @throws SkinException If it is not possible to read the layout file
+ */
+ public static LayoutFileModel readLayout(File skinFilesPath) throws SkinException
+ {
+ LayoutFileModel model = new LayoutFileModel();
+ File layoutPath = new File(skinFilesPath, LAYOUT_FILE_NAME);
+ String fileContents;
+ if ((layoutPath != null) && (layoutPath.isFile()))
+ {
+ fileContents = getLayoutFileContents(layoutPath);
+ parseLayoutFile(fileContents, model);
+ }
+
+ Collection<String> partNames = model.getPartNames();
+ if ((model.getLayoutNames().size() == 0) && (partNames.size() == 1)
+ && (partNames.iterator().next().equals(PartBean.UNIQUE_PART)))
+ {
+ fileContents = getPseudoLayoutFileContents();
+ parseLayoutFile(fileContents, model);
+ }
+
+ return model;
+ }
+
+ /**
+ * Parses the provided layout file contents
+ *
+ * @param fileContents All the contents of a layout file
+ * @param model The model where to set the parsed data
+ *
+ * @throws SkinException If the layout file is corrupted, or has erroneous syntax
+ */
+ private static void parseLayoutFile(String fileContents, LayoutFileModel model)
+ throws SkinException
+ {
+ // process given string to remove comments
+ String cleanContents = "";
+ BufferedReader reader = null;
+ try
+ {
+ StringBuffer contentBuffer = new StringBuffer();
+ reader = new BufferedReader(new StringReader(fileContents));
+ String line = null;
+ do
+ {
+ line = reader.readLine();
+ String lineCopy = line;
+ if ((line != null) && !lineCopy.trim().startsWith("#"))
+ {
+ contentBuffer.append(line + '\n');
+ }
+ }
+ while (line != null);
+ cleanContents = contentBuffer.toString();
+ }
+ catch (IOException e)
+ {
+ //try to continue with the parser
+ cleanContents = fileContents;
+ }
+ finally
+ {
+ try
+ {
+ reader.close();
+ }
+ catch (IOException e)
+ {
+ StudioLogger.error("Could not close input stream: ", e.getMessage()); //$NON-NLS-1$
+ }
+ }
+
+ String[] tokens = cleanContents.split(SPLIT_PATTERN);
+
+ Stack<Object> stack = new Stack<Object>();
+
+ // At this point, the file has been read into hundreds of token, including blocks,
+ // "{", "}", keys and values
+
+ String currentTag = null;
+ String key = null;
+
+ // Iterate on the tokens
+ try
+ {
+ for (String aToken : tokens)
+ {
+ // When the token is a "{", that means we need to stack something. This "something"
+ // will be removed from stack when we find a matching "}"
+ if (OPEN_BRACKET.equals(aToken))
+ {
+ // Every word is interpreted as a key at first. If we find a "{", it must be
+ // re-interpreted as a tag instead
+ if (key != null)
+ {
+ currentTag = key;
+ key = null;
+ }
+ addElementsToStack(stack, model, currentTag);
+ }
+ // When the token is a "}" we must remove something from the stack
+ else if (CLOSE_BRACKET.equals(aToken))
+ {
+ removeElementsFromStack(stack);
+ }
+ else
+ {
+ // A word is interpreted as a key by default. If the key is already set, we will
+ // have a key-value pair and are able to assign it to something at the model
+ if (key == null)
+ {
+ key = aToken;
+ }
+ else
+ {
+ setKeyValuePair(stack, model, currentTag, key, aToken);
+ key = null;
+ }
+ }
+ }
+
+ }
+ catch (EmptyStackException e)
+ {
+ throw new SkinException(EmulatorNLS.ERR_LayoutFileParser_BracketsDoNotMatch);
+ }
+
+ if (!stack.isEmpty())
+ {
+ // When there is only a part bean at the first level, that means we have finished
+ // parsing a single part layout. Remove it from the stack as well.
+ //
+ // NOTE: when creating the single part layout, we have added this additional element
+ // to the stack
+ if ((stack.size() == 1) && (stack.get(0) instanceof PartBean)
+ && (((PartBean) stack.get(0)).getName().equals(PartBean.UNIQUE_PART)))
+ {
+ stack.pop();
+ }
+ else
+ {
+ throw new SkinException(EmulatorNLS.ERR_LayoutFileParser_BracketsDoNotMatch);
+ }
+ }
+
+ }
+
+ /**
+ * Reads the contents of the provided file into a String object
+ *
+ * @param layoutPath A file pointing to an "layout" file
+ *
+ * @return A string with all the contents of the file
+ *
+ * @throws SkinException If the file cannot be read
+ */
+ private static String getLayoutFileContents(File layoutPath) throws SkinException
+ {
+ int fileSize = (int) layoutPath.length();
+ char[] buffer = new char[fileSize];
+
+ FileReader fr = null;
+ try
+ {
+ fr = new FileReader(layoutPath);
+ fr.read(buffer);
+ }
+ catch (IOException e)
+ {
+ error("The file " + layoutPath.getAbsolutePath() + " could not be read. cause="
+ + e.getMessage());
+ throw new SkinException(EmulatorNLS.ERR_LayoutFileParser_LayoutFileCouldNotBeRead);
+ }
+ finally
+ {
+ try
+ {
+ if (fr != null)
+ {
+ fr.close();
+ }
+ }
+ catch (IOException e)
+ {
+ warn("The file " + layoutPath.getAbsolutePath()
+ + " could not be closed after reading");
+ }
+ }
+
+ return String.copyValueOf(buffer);
+ }
+
+ /**
+ * Gets the contents of the pseudo layout file, for merging to the current model
+ *
+ * @return A string containing all the contents of the pseudo layout file
+ *
+ * @throws SkinException If the file cannot be read
+ */
+ private static String getPseudoLayoutFileContents() throws SkinException
+ {
+ URL url = EmulatorPlugin.getDefault().getBundle().getResource(PSEUDO_LAYOUT_FILE);
+ CharBuffer buffer = CharBuffer.allocate(1024);
+ int readChars = 0;
+
+ InputStream is = null;
+ InputStreamReader isr = null;
+ try
+ {
+ is = url.openStream();
+ isr = new InputStreamReader(is);
+ while (readChars != -1)
+ {
+ readChars = isr.read(buffer);
+ }
+ buffer.flip();
+ }
+ catch (IOException e)
+ {
+ error("The file res/pseudolayout could not be read. cause=" + e.getMessage());
+ throw new SkinException(EmulatorNLS.ERR_LayoutFileParser_LayoutFileCouldNotBeRead);
+ }
+ finally
+ {
+ try
+ {
+ if (is != null)
+ {
+ is.close();
+ }
+ if (isr != null)
+ {
+ isr.close();
+ }
+ }
+ catch (IOException e)
+ {
+ warn("The file " + PSEUDO_LAYOUT_FILE + " could not be closed after reading");
+ }
+ }
+
+ return buffer.toString();
+ }
+
+ /**
+ * Stacks an element
+ * The stack rules are quite complex. We first start by special cases (in which we analyze the stack
+ * and the current element for accurate interpretation) and then move to the default cases.
+ *
+ * Summarizing, the stack will contain objects from this package (*Bean) as well as String objects.
+ * When a bean is at the top of the stack, we may perform actions on the given object. Strings are added
+ * to the stack for bracket matching and to add a mark for future actions.
+ *
+ * @param stack The stack where to add elements
+ * @param model The model being built
+ * @param elementName The name of the element to add to stack.
+ */
+ private static void addElementsToStack(Stack<Object> stack, LayoutFileModel model,
+ String elementName)
+ {
+ int stackSizeAtStart = stack.size();
+
+ //--------------
+ // SPECIAL CASES
+ //--------------
+
+ // When the stack size is equal to zero, we can have one of those two situations:
+ //
+ // a) THE LAYOUT FILE CONTAINS MULTIPLE LAYOUT AND/OR PARTS: It is possible to have the
+ // following tags: "parts", "layouts", "keyboard" or "network". All of them are handled in the
+ // else clause, by adding the tag name at the stack
+ //
+ // b) THE LAYOUT FILE IS SIMPLE (i.e. it doesn't contain layouts, neither a collection
+ // of parts): It is possible to have the following tags: "display", "background", "button",
+ // "keyboard", "network". The first three belong to a part definition, so we need to include a
+ // PartBean to the stack before the object representing the tag. The last two can be handled the same
+ // way as in item (a)
+ if (stack.size() == 0)
+ {
+ if ((MAIN_LEVEL_DISPLAY.equals(elementName))
+ || (MAIN_LEVEL_BACKGROUND.equals(elementName))
+ || (MAIN_LEVEL_BUTTON.equals(elementName)))
+ {
+ // This is a single part layout. Execute operation described at item (b) above
+ PartBean bean = model.newPart();
+ stack.push(bean);
+
+ if ((MAIN_LEVEL_BACKGROUND.equals(elementName))
+ || (MAIN_LEVEL_BUTTON.equals(elementName)))
+ {
+ stack.push(elementName);
+ }
+ else if (MAIN_LEVEL_DISPLAY.equals(elementName))
+ {
+ RectangleBean display = bean.newDisplay();
+ stack.push(display);
+ }
+ }
+ else
+ {
+ // PARTS, LAYOUTS, KEYBOARD, NETWORK
+ stack.push(elementName);
+ }
+ }
+
+ // When the stack size is equal to one, we can have one of those four situations:
+ //
+ // a) THE ELEMENT AT STACK IS NOT A STRING: In this case, we will handle as default case
+ // b) THE ELEMENT AT STACK IS THE "parts" STRING: It means that the element name denotes the name of
+ // a part. We must create a part with the name of the element, and add it to the stack
+ // c) THE ELEMENT AT STACK IS THE "layouts" STRING: It means that the element name denotes the name of
+ // a layout. We must create a layout with the name of the element, and add it to the stack
+ // d) THE ELEMENT AT STACK IS ANY OTHER STRING: In this case, we will handle as default case
+ else if (stack.size() == 1)
+ {
+ Object previousElement = stack.peek();
+ if (previousElement instanceof String)
+ {
+ if (MAIN_LEVEL_PARTS.equals((String) previousElement))
+ {
+ // elementName is the name of a new part
+ PartBean bean = model.newPart(elementName);
+ stack.push(bean);
+ }
+ else if (MAIN_LEVEL_LAYOUTS.equals((String) previousElement))
+ {
+ // elementName is the name of a new layout
+ LayoutBean bean = model.newLayout(elementName);
+ stack.push(bean);
+ }
+ }
+ }
+
+ //--------------
+ // DEFAULT CASES
+ //--------------
+
+ // Any other case will be handled below. The following clauses cover any other remaining cases not
+ // covered by the special cases. The beans created, when added to the stack, represents structures
+ // already known. If it is not possible to guess what structure we need at the current parse iteration
+ // or if we need an element at the stack to match a close bracket to come, we simply add it as string
+ //
+ // We only execute the following block if the previous cases didn't affect the stack
+ if (stackSizeAtStart == stack.size())
+ {
+ Object stackElem = stack.peek();
+ if (stackElem instanceof PartBean)
+ {
+ if (MAIN_LEVEL_DISPLAY.equals(elementName))
+ {
+ RectangleBean display = ((PartBean) stackElem).newDisplay();
+ stack.push(display);
+ }
+ else if (MAIN_LEVEL_BACKGROUND.equals(elementName))
+ {
+ ImagePositionBean background =
+ ((PartBean) stackElem).newBackground(elementName);
+ stack.push(background);
+ }
+ else
+ {
+ stack.push(elementName);
+ }
+ }
+ else if (stackElem instanceof LayoutBean)
+ {
+ PartRefBean bean = ((LayoutBean) stackElem).newPartRef(elementName);
+ stack.push(bean);
+ }
+ else if (stackElem instanceof String)
+ {
+ if ((MAIN_LEVEL_BUTTON.equals((String) stackElem) || (PART_BUTTONS
+ .equals((String) stackElem))))
+ {
+ Object nonStringObj = findFirstNonStringAtStack(stack);
+ if (nonStringObj != null)
+ {
+ PartBean bean = (PartBean) nonStringObj;
+ ImagePositionBean button = bean.newButton(elementName);
+ stack.push(button);
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Removes an element from the stack
+ *
+ * @param stack The stack from where to remove elements
+ */
+ private static void removeElementsFromStack(Stack<Object> stack)
+ {
+ stack.pop();
+ }
+
+ /**
+ * Set a key-value pair at the object at the top of the stack.
+ * Depending on the key-value pair, we may set attributes to the model itself
+ *
+ * @param stack The stack containing the element to have a property set
+ * @param model The model that can have a property set
+ * @param currentTag The name of the tag containing a model property
+ * @param key The property key
+ * @param value The property value
+ */
+ private static void setKeyValuePair(Stack<Object> stack, LayoutFileModel model,
+ String currentTag, String key, String value)
+ {
+ Object obj = stack.peek();
+ if (obj instanceof String)
+ {
+ Object notStringObj = findFirstNonStringAtStack(stack);
+
+ if (notStringObj instanceof ILayoutBean)
+ {
+ ((ILayoutBean) notStringObj).setKeyValue(key, value);
+ }
+ else
+ {
+ if (MAIN_LEVEL_NETWORK.equals(currentTag))
+ {
+ if (NETWORK_DELAY.equals(key))
+ {
+ model.setNetworkDelay(value);
+ }
+ else if (NETWORK_SPEED.equals(key))
+ {
+ model.setNetworkSpeed(value);
+ }
+ }
+ else if (MAIN_LEVEL_KEYBOARD.equals(currentTag))
+ {
+ if (KEYBOARD_CHARMAP.equals(key))
+ {
+ model.setKeyboardCharmap(value);
+ }
+ }
+ }
+ }
+ else
+ {
+ ((ILayoutBean) obj).setKeyValue(key, value);
+ }
+ }
+
+ /**
+ * Utility method for finding the first non-String object at the stack
+ *
+ * @param stack The stack were to find the first non-String at
+ *
+ * @return The non-String object
+ */
+ private static Object findFirstNonStringAtStack(Stack<Object> stack)
+ {
+ Object firstNonString = null;
+ Object tmpObj = null;
+
+ int i = stack.size() - 1;
+ while (i >= 0)
+ {
+ tmpObj = stack.get(i);
+ if (!(tmpObj instanceof String))
+ {
+ firstNonString = tmpObj;
+ break;
+ }
+ else
+ {
+ i--;
+ }
+ }
+
+ return firstNonString;
+ }
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/skin/android/parser/PartBean.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/skin/android/parser/PartBean.java
new file mode 100644
index 0000000..3a2625f
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/skin/android/parser/PartBean.java
@@ -0,0 +1,154 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.emulator.skin.android.parser;
+
+import java.util.Collection;
+import java.util.LinkedHashSet;
+
+/**
+ * DESCRIPTION:
+ * This class represents a part structure from the layout file
+ *
+ * RESPONSIBILITY:
+ * Represent part structures
+ *
+ * COLABORATORS:
+ * None.
+ *
+ * USAGE:
+ * This class is intended to be used by the LayoutFileParser and LayoutFileModel classes only
+ */
+public class PartBean
+{
+ /**
+ * The string used as name of a part, at skins that have a single part
+ */
+ public static final String UNIQUE_PART = "___UNIQUE___";
+
+ /**
+ * The part name
+ */
+ private String name;
+
+ /**
+ * The part background data
+ */
+ private ImagePositionBean background;
+
+ /**
+ * The part display data
+ */
+ private RectangleBean display;
+
+ /**
+ * The part buttons
+ */
+ private Collection<ImagePositionBean> buttons = new LinkedHashSet<ImagePositionBean>();
+
+ /**
+ * Constructor
+ * Builds a new part structure with the given name
+ *
+ * @param name The layout name
+ */
+ PartBean(String name)
+ {
+ this.name = name;
+ }
+
+ /**
+ * Creates a new button, registers it and returns it to the user
+ *
+ * @param buttonName The name to assign to the button
+ *
+ * @return The button
+ */
+ ImagePositionBean newButton(String buttonName)
+ {
+ ImagePositionBean bean = new ImagePositionBean(buttonName);
+ buttons.add(bean);
+ return bean;
+ }
+
+ /**
+ * Creates a new display and registers it
+ *
+ * @return The display
+ */
+ RectangleBean newDisplay()
+ {
+ display = new RectangleBean();
+ return display;
+ }
+
+ /**
+ * Creates a new background and registers it
+ *
+ * @param bgName The name of the background image
+ *
+ * @return The background
+ */
+ ImagePositionBean newBackground(String bgName)
+ {
+ background = new ImagePositionBean(bgName);
+ return background;
+ }
+
+ /**
+ * Retrieves the part background information
+ *
+ * @return The part background information
+ */
+ ImagePositionBean getBackground()
+ {
+ return background;
+ }
+
+ /**
+ * Retrieves the part display information
+ *
+ * @return The part display information
+ */
+ RectangleBean getDisplay()
+ {
+ return display;
+ }
+
+ /**
+ * Retrieves the part name
+ *
+ * @return The part name
+ */
+ String getName()
+ {
+ return name;
+ }
+
+ Collection<ImagePositionBean> getButtons()
+ {
+ return buttons;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString()
+ {
+ return "Part: " + name;
+ }
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/skin/android/parser/PartRefBean.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/skin/android/parser/PartRefBean.java
new file mode 100644
index 0000000..72e176d
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/skin/android/parser/PartRefBean.java
@@ -0,0 +1,149 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.emulator.skin.android.parser;
+
+/**
+ * DESCRIPTION:
+ * This class represents a part reference.
+ * It links a layout with a part from the same layout file
+ *
+ * RESPONSIBILITY:
+ * Represent part references and provide link information between parts and layouts
+ *
+ * COLABORATORS:
+ * None.
+ *
+ * USAGE:
+ * This class is intended to be used by the LayoutFileParser and LayoutFileModel classes only
+ */
+public class PartRefBean implements ILayoutConstants, ILayoutBean
+{
+ /**
+ * The name of this part reference
+ */
+ private String refName;
+
+ /**
+ * The name of the part that this reference points to
+ */
+ private String partName;
+
+ /**
+ * The X position of the part into the layout
+ */
+ private String x;
+
+ /**
+ * The Y position of the part into the layout
+ */
+ private String y;
+
+ /**
+ * The rotation applied to the part, when into a layout
+ */
+ private String rotation;
+
+ /**
+ * Constructor
+ * Creates a part reference with the given name
+ *
+ * @param refName The part reference name
+ */
+ PartRefBean(String refName)
+ {
+ this.refName = refName;
+ }
+
+ /**
+ * Retrieves the name of the part being referenced
+ *
+ * @return The part name
+ */
+ String getPartName()
+ {
+ return partName;
+ }
+
+ /**
+ * Retrieves the reference name
+ *
+ * @return The reference name
+ */
+ String getRefName()
+ {
+ return refName;
+ }
+
+ /**
+ * Retrieves the X position where to draw this part at the layout
+ *
+ * @return The X position
+ */
+ String getX()
+ {
+ return x;
+ }
+
+ /**
+ * Retrieves the Y position where to draw this part at the layout
+ *
+ * @return The Y position
+ */
+ String getY()
+ {
+ return y;
+ }
+
+ /**
+ * Retrieves how many rotations to perform on the part when drawing it into the layout
+ *
+ * @return How many rotations to perform on the part
+ */
+ String getRotation()
+ {
+ return rotation;
+ }
+
+ @Override
+ public String toString()
+ {
+ return "PartRef: " + refName;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.emulator.skin.android.parser.ILayoutBean#setKeyValue(java.lang.String, java.lang.String)
+ */
+ public void setKeyValue(String key, String value)
+ {
+ if (ATTR_X.equals(key))
+ {
+ x = value;
+ }
+ else if (ATTR_Y.equals(key))
+ {
+ y = value;
+ }
+ else if (ATTR_NAME.equals(key))
+ {
+ partName = value;
+ }
+ else if (PARTREF_ROTATION.equals(key))
+ {
+ rotation = value;
+ }
+ }
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/skin/android/parser/RectangleBean.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/skin/android/parser/RectangleBean.java
new file mode 100644
index 0000000..0d26f7b
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/skin/android/parser/RectangleBean.java
@@ -0,0 +1,127 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.emulator.skin.android.parser;
+
+/**
+ * DESCRIPTION:
+ * This class represents a node containing x, y, width and height data
+ *
+ * RESPONSIBILITY:
+ * Represent rectangles of the layout file
+ *
+ * COLABORATORS:
+ * None.
+ *
+ * USAGE:
+ * This class is intended to be used by the LayoutFileParser and LayoutFileModel classes only
+ */
+public class RectangleBean implements ILayoutConstants, ILayoutBean
+{
+ /**
+ * The X position of the rectangle origin
+ */
+ private String xPos;
+
+ /**
+ * The Y position of the rectangle origin
+ */
+ private String yPos;
+
+ /**
+ * The width of the rectangle
+ */
+ private String width;
+
+ /**
+ * The height of the rectangle
+ */
+ private String height;
+
+ /**
+ * Retrieves the X position of the rectangle origin
+ *
+ * @return the X position
+ */
+ String getXPos()
+ {
+ return xPos;
+ }
+
+ /**
+ * Retrieves the Y position of the rectangle origin
+ *
+ * @return the Y position
+ */
+ String getYPos()
+ {
+ return yPos;
+ }
+
+ /**
+ * Retrieves the width of the rectangle
+ *
+ * @return The rectangle width
+ */
+ String getWidth()
+ {
+ return width;
+ }
+
+ /**
+ * Retrieves the height of the rectangle
+ *
+ * @return The rectangle height
+ */
+ String getHeight()
+ {
+ return height;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString()
+ {
+ return "Rectangle";
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.emulator.skin.android.parser.ILayoutBean#setKeyValue(java.lang.String, java.lang.String)
+ */
+ public void setKeyValue(String key, String value)
+ {
+ if (ATTR_X.equals(key))
+ {
+ xPos = value;
+ }
+ else if (ATTR_Y.equals(key))
+ {
+ yPos = value;
+ }
+ else if (ATTR_WIDTH.equals(key))
+ {
+ width = value;
+ }
+ else if (ATTR_HEIGHT.equals(key))
+ {
+ height = value;
+ }
+
+ }
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/IAndroidUIConstants.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/IAndroidUIConstants.java
new file mode 100644
index 0000000..e79f700
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/IAndroidUIConstants.java
@@ -0,0 +1,24 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.emulator.ui;
+
+public interface IAndroidUIConstants
+{
+ /**
+ * Screens to skin proportion
+ */
+ public static final double DISPLAY_TO_SKIN_RATIO = 0.5;
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/IUIHelpConstants.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/IUIHelpConstants.java
new file mode 100644
index 0000000..df96437
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/IUIHelpConstants.java
@@ -0,0 +1,31 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.emulator.ui;
+
+import com.motorola.studio.android.emulator.EmulatorPlugin;
+
+/**
+ * DESCRIPTION:
+ * This interface contains constants used for building ids for context help at UI classes
+ */
+public interface IUIHelpConstants
+{
+ // Android Emulator viewer constants
+
+ String EMULATOR_VIEW_HELP = EmulatorPlugin.PLUGIN_ID + ".emulator";
+
+ String EMULATOR_VIEW_MAIN_DISPLAY_HELP = EmulatorPlugin.PLUGIN_ID + ".maindisplay";
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/controls/IAndroidComposite.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/controls/IAndroidComposite.java
new file mode 100644
index 0000000..1df03e7
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/controls/IAndroidComposite.java
@@ -0,0 +1,80 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.emulator.ui.controls;
+
+import org.eclipse.swt.events.KeyListener;
+import org.eclipse.swt.events.MouseListener;
+import org.eclipse.swt.events.MouseMoveListener;
+
+/**
+ * This interface defines the methods that must be implemented by the
+ * composites that holds Android Emulator instances.
+ */
+public interface IAndroidComposite
+{
+
+ /**
+ * Applies the zoom factor to the components of the composite.
+ */
+ void applyZoomFactor();
+
+ /**
+ * Gets the current zoom factor.
+ * @return the zoom factor
+ */
+ double getZoomFactor();
+
+ /**
+ * Sets the zoom factor.
+ * @param zoom the zoom factor
+ */
+ void setZoomFactor(double zoom);
+
+ /**
+ * Applies the layout to the components of the composite.
+ *
+ * @param layoutName The name of the layout to apply
+ */
+ void applyLayout(String layoutName);
+
+ /**
+ * Gets is the selected fit to window option.
+ * @return the zoom factor
+ */
+ boolean isFitToWindowSelected();
+
+ /**
+ * Retrieves the key listener to apply to the main display
+ *
+ * @return The key listener
+ */
+ KeyListener getKeyListener();
+
+ /**
+ * Retrieves the mouse listener to apply to the main display
+ *
+ * @return The mouse listener
+ */
+ MouseListener getMouseListener();
+
+ /**
+ * Retrieves the mouse move listener to apply to the main display
+ *
+ * @return The mouse move listener
+ */
+ MouseMoveListener getMouseMoveListener();
+
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/controls/RemoteCLIDisplay.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/controls/RemoteCLIDisplay.java
new file mode 100644
index 0000000..41e6083
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/controls/RemoteCLIDisplay.java
@@ -0,0 +1,277 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.emulator.ui.controls;
+
+import java.util.Timer;
+import java.util.TimerTask;
+
+import org.eclipse.sequoyah.vnc.vncviewer.graphics.swt.ISWTPainter;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.widgets.Canvas;
+import org.eclipse.swt.widgets.Composite;
+
+import com.motorola.studio.android.emulator.core.model.IAndroidEmulatorInstance;
+
+/**
+ * DESCRIPTION:
+ * This class implements the composite that displays the contents of the
+ * CLI display, according to the provided painter object.
+ *
+ * RESPONSIBILITY:
+ * - Display the contents of the CLI display at screen
+ *
+ * COLABORATORS:
+ * None.
+ *
+ * USAGE:
+ * Add the composite to a view or other equivalent SWT object for it to
+ * display the result of the communication through VNC Protocol
+ *
+ */
+public class RemoteCLIDisplay extends Composite
+{
+ private Canvas canvas;
+
+ private Image screen = null;
+
+ private ISWTPainter painter;
+
+ private Timer refreshTimer;
+
+ private static long FIRST_REFRESH_DELAY_MS = 500; /* Time in milliseconds for the first update */
+
+ private static long REFRESH_DELAY_PERIOD_MS = 300; /* Time in milliseconds between 2 updates */
+
+ private boolean active = false;
+
+ private double zoomFactor = 1;
+
+ /**
+ * Creates a new RemoteCLIDisplay object.
+ *
+ * @param parent The parent composite
+ * @param cliPainter The object where to retrieve pixels from
+ */
+ public RemoteCLIDisplay(Composite parent, ISWTPainter cliPainter)
+ {
+ super(parent, SWT.BACKGROUND);
+ this.painter = cliPainter;
+ this.setLayout(parent.getLayout());
+ canvas = new Canvas(this, SWT.BACKGROUND);
+ }
+
+ /**
+ * Starts the display refresh
+ */
+ synchronized public void start()
+ {
+ addRefreshTimer();
+ setRunning(true);
+ }
+
+ /**
+ * Stops the display refresh
+ */
+ synchronized public void stop()
+ {
+ setRunning(false);
+ refreshTimer.cancel();
+
+ if (!canvas.isDisposed())
+ {
+ GC gc = new GC(canvas);
+ canvas.drawBackground(gc, 0, 0, canvas.getSize().x, canvas.getSize().y);
+ gc.dispose();
+ }
+ }
+
+ /**
+ * Adds a timer that schedules the screen's update in a fixed period.
+ */
+ private void addRefreshTimer()
+ {
+ refreshTimer = new Timer();
+
+ final IAndroidEmulatorInstance instance = UIHelper.getInstanceAssociatedToControl(this);
+
+ refreshTimer.scheduleAtFixedRate(new TimerTask()
+ {
+ @Override
+ public void run()
+ {
+ if (instance.getHasCli())
+ {
+ // Request CLI Update here (when applicable)
+
+ getDisplay().syncExec(new Runnable()
+ {
+ public void run()
+ {
+ updateScreen();
+ }
+ });
+ }
+ }
+ }, FIRST_REFRESH_DELAY_MS, REFRESH_DELAY_PERIOD_MS);
+ }
+
+ /**
+ * Performs the screen update itself
+ */
+ private void updateScreen()
+ {
+ if (screen != null)
+ {
+ screen.dispose();
+ }
+
+ if ((!getDisplay().isDisposed()) && (isDisplayActive()))
+ {
+ if ((painter.getImageData() != null) && (!canvas.isDisposed()))
+ {
+ screen =
+ new Image(canvas.getDisplay(), painter.getImageData().scaledTo(
+ (int) (painter.getImageData().width * zoomFactor),
+ (int) (painter.getImageData().height * zoomFactor)));
+
+ GC gc = new GC(canvas);
+ gc.drawImage(screen, 0, 0);
+ gc.dispose();
+ }
+ }
+ else
+ {
+ stop();
+ }
+ }
+
+ /**
+ * Returns true if the component is running.
+ */
+ synchronized public boolean isDisplayActive()
+ {
+ return active;
+ }
+
+ /**
+ * Gets the Canvas used to show the screen.
+ *
+ * @return the Canvas object.
+ */
+ public Canvas getCanvas()
+ {
+ return canvas;
+ }
+
+ /**
+ * Retrieves the image being drawn at display
+ *
+ * @return The image being drawn at display
+ */
+ public Image getScreen()
+ {
+ return screen;
+ }
+
+ /**
+ * Retrieves the display width
+ *
+ * @return The display width
+ */
+ public int getScreenWidth()
+ {
+ return painter.getWidth();
+ }
+
+ /**
+ * Retrieves the display height
+ *
+ * @return The display height
+ */
+ public int getScreenHeight()
+ {
+ return painter.getHeight();
+ }
+
+ /**
+ * Sets the current state of the display
+ *
+ * @param running true if the display is refreshing; false otherwise
+ */
+ synchronized private void setRunning(boolean running)
+ {
+ this.active = running;
+ }
+
+ /**
+ * @see org.eclipse.swt.widgets.Widget#dispose()
+ */
+ @Override
+ public void dispose()
+ {
+ if (isDisplayActive())
+ {
+ stop();
+ }
+
+ if (screen != null)
+ {
+ screen.dispose();
+ }
+
+ canvas.dispose();
+ super.dispose();
+ }
+
+ /**
+ * @see org.eclipse.swt.widgets.Control#setBackground(Color)
+ */
+ @Override
+ public void setBackground(Color color)
+ {
+ super.setBackground(color);
+ canvas.setBackground(color);
+ }
+
+ /**
+ * Retrieves the current zoom factor being applied to the screen
+ *
+ * @return The current zoom factor
+ */
+ public double getZoomFactor()
+ {
+ return zoomFactor;
+ }
+
+ /**
+ * Sets a new zoom factor to the screen
+ *
+ * @param zoomFactor The zoom factor to set to the screen
+ */
+ public void setZoomFactor(double zoomFactor)
+ {
+ this.zoomFactor = zoomFactor;
+
+ IAndroidEmulatorInstance instance = UIHelper.getInstanceAssociatedToControl(this);
+ if (instance.getHasCli())
+ {
+ updateScreen();
+ }
+ }
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/controls/UIHelper.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/controls/UIHelper.java
new file mode 100644
index 0000000..751811d
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/controls/UIHelper.java
@@ -0,0 +1,145 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.emulator.ui.controls;
+
+import org.eclipse.sequoyah.vnc.vncviewer.graphics.IRemoteDisplay;
+import org.eclipse.sequoyah.vnc.vncviewer.graphics.swt.SWTRemoteDisplay;
+import org.eclipse.swt.custom.ScrolledComposite;
+import org.eclipse.swt.events.MouseEvent;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.TabFolder;
+import org.eclipse.swt.widgets.TabItem;
+
+import com.motorola.studio.android.emulator.core.model.IAndroidEmulatorInstance;
+import com.motorola.studio.android.emulator.ui.controls.maindisplay.MainDisplayComposite;
+
+/**
+ * DESCRIPTION:
+ * This class provides helper methods for Android Composite UI
+ *
+ * RESPONSIBILITY:
+ * - Provide utility methods related to Android Composite UI
+ *
+ * COLABORATORS:
+ * None.
+ *
+ * USAGE:
+ * Use any of the public methods for getting help with skin UI
+ */
+public class UIHelper
+{
+ /**
+ * Retrieves the instance related to a given control
+ *
+ * @param control The control associated to an instance, which is to be retrieved
+ *
+ * @return The instance associated with the control
+ */
+ public static IAndroidEmulatorInstance getInstanceAssociatedToControl(Control control)
+ {
+ IAndroidEmulatorInstance result = null;
+ Control composite = null;
+ TabFolder folder = null;
+
+ if (control instanceof ScrolledComposite)
+ {
+ composite = ((ScrolledComposite) control).getContent();
+ folder = (TabFolder) composite.getParent();
+ }
+ else if (control instanceof MainDisplayComposite)
+ {
+ composite = control.getParent();
+ folder = (TabFolder) control.getParent().getParent();
+ }
+ else if ((control instanceof Composite) && (control instanceof IAndroidComposite))
+ {
+ composite = control;
+ folder = (TabFolder) control.getParent();
+ }
+ else if ((control instanceof SWTRemoteDisplay) || (control instanceof RemoteCLIDisplay))
+ {
+ composite = control.getParent();
+ folder = (TabFolder) composite.getParent();
+ }
+
+ if (folder != null)
+ {
+ TabItem[] items = folder.getItems();
+ for (TabItem item : items)
+ {
+ if (item.getControl() == composite)
+ {
+ result = (IAndroidEmulatorInstance) item.getData();
+ break;
+ }
+ }
+ }
+
+ return result;
+ }
+
+ public static SWTRemoteDisplay getRemoteDisplayAssociatedToControl(Control control)
+ {
+
+ SWTRemoteDisplay remoteDisplay = null;
+
+ if ((control instanceof Composite) && (control instanceof IAndroidComposite))
+ {
+ for (Control childControl : ((Composite) control).getChildren())
+ {
+ if (childControl instanceof SWTRemoteDisplay)
+ {
+ remoteDisplay = (SWTRemoteDisplay) childControl;
+ break;
+ }
+ }
+ }
+
+ return remoteDisplay;
+ }
+
+ /**
+ * Ajusts the x,y coordinates of the mouse event according to the current zoom and rotation.
+ * Coordinates are originally set with the x,y coordinates of the UI element which may be resized according to zoom and rotation,
+ * but the x,y coordinates expected by the Android emulator is independent of the zoom and rotation.
+ * @param e the mouse event whose coordinates will be ajusted.
+ */
+ public static void ajustCoordinates(MouseEvent e, IAndroidComposite composite)
+ {
+ int x;
+ int y;
+
+ SWTRemoteDisplay mainDisplay =
+ UIHelper.getRemoteDisplayAssociatedToControl((Control) composite);
+ IRemoteDisplay.Rotation rotation = mainDisplay.getRotation();
+ double zoomFactor = composite.getZoomFactor();
+
+ switch (rotation)
+ {
+ case ROTATION_90DEG_COUNTERCLOCKWISE:
+ x = mainDisplay.getScreenWidth() - (int) ((double) e.y / zoomFactor);
+ y = (int) ((double) e.x / zoomFactor);
+ break;
+ default:
+ x = (int) ((double) e.x / zoomFactor);
+ y = (int) ((double) e.y / zoomFactor);
+ }
+
+ e.x = x;
+ e.y = y;
+ }
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/controls/maindisplay/MainDisplayComposite.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/controls/maindisplay/MainDisplayComposite.java
new file mode 100644
index 0000000..c5a4561
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/controls/maindisplay/MainDisplayComposite.java
@@ -0,0 +1,470 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.emulator.ui.controls.maindisplay;
+
+import static com.motorola.studio.android.common.log.StudioLogger.info;
+
+import java.util.Properties;
+
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.sequoyah.vnc.vncviewer.graphics.IRemoteDisplay;
+import org.eclipse.sequoyah.vnc.vncviewer.graphics.swt.SWTRemoteDisplay;
+import org.eclipse.swt.SWT;
+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.MouseAdapter;
+import org.eclipse.swt.events.MouseEvent;
+import org.eclipse.swt.events.MouseListener;
+import org.eclipse.swt.events.MouseMoveListener;
+import org.eclipse.swt.events.MouseWheelListener;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.Composite;
+
+import com.motorola.studio.android.adt.DDMSFacade;
+import com.motorola.studio.android.emulator.core.model.IAndroidEmulatorInstance;
+import com.motorola.studio.android.emulator.core.model.IInputLogic;
+import com.motorola.studio.android.emulator.logic.AndroidLogicUtils;
+import com.motorola.studio.android.emulator.skin.android.AndroidSkinTranslator;
+import com.motorola.studio.android.emulator.ui.controls.IAndroidComposite;
+import com.motorola.studio.android.emulator.ui.controls.UIHelper;
+import com.motorola.studio.android.emulator.ui.handlers.IHandlerConstants;
+import com.motorola.studio.android.nativeos.NativeUIUtils;
+
+/**
+ * This class is the composite that holds the main display and it is shown by
+ * the Emulator Main Display View.
+ */
+public class MainDisplayComposite extends Composite implements IAndroidComposite
+{
+
+ /**
+ * The zoom factor whose default value is 1.0 (100%)
+ */
+ private double zoomFactor = 1.0;
+
+ private double fitZoomfactor;
+
+ // Minimum value to be used as zoom factor. This is necessary to avoid
+ // divisions to zero
+ private static final double MINIMUM_ZOOM_FACTOR = 0.0001;
+
+ private static final double ZOOM_FIT = 0.0;
+
+ /**
+ * The flag indicating that Ctrl key is pressed
+ */
+ private boolean ctrlPressed = false;
+
+ /**
+ * SWT key pressed/released events listener.
+ */
+ private KeyListener keyListener;
+
+ private MouseListener mouseListener;
+
+ private MouseMoveListener mouseMoveListener;
+
+ private IInputLogic androidInput;
+
+ private boolean isMouseLeftButtonPressed;
+
+ private boolean isFitToWindow;
+
+ private IAndroidEmulatorInstance androidInstance;
+
+ private Properties keyMap;
+
+ /**
+ * Constructor
+ *
+ * @param parent
+ * composite
+ * @param style
+ * style
+ * @param baseWidth
+ * the default main display width
+ * @param baseHeight
+ * the default main display height
+ */
+ public MainDisplayComposite(Composite parent, int style, int baseWidth, int baseHeight,
+ IAndroidEmulatorInstance instance)
+ {
+ super(parent, style);
+
+ androidInput = instance.getInputLogic();
+
+ androidInstance = instance;
+
+ isMouseLeftButtonPressed = false;
+
+ keyMap = AndroidSkinTranslator.getQwertyKeyMap();
+
+ addListener();
+
+ if (!Platform.getOS().equals(Platform.OS_MACOSX))
+ {
+ hideEmulatorWindow();
+ }
+
+ }
+
+ private void hideEmulatorWindow()
+ {
+ int port =
+ AndroidLogicUtils.getEmulatorPort(DDMSFacade.getSerialNumberByName(androidInstance
+ .getName()));
+ long windowHandle = NativeUIUtils.getWindowHandle(androidInstance.getName(), port);
+ androidInstance.setWindowHandle(windowHandle);
+
+ NativeUIUtils.hideWindow(windowHandle);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.swt.widgets.Widget#dispose()
+ */
+ @Override
+ public void dispose()
+ {
+ if (androidInput != null)
+ {
+ androidInput.dispose();
+ }
+
+ keyListener = null;
+ mouseListener = null;
+ mouseMoveListener = null;
+
+ if (!Platform.getOS().equals(Platform.OS_MACOSX))
+ {
+ long hnd = androidInstance.getWindowHandle();
+ if (hnd > 0)
+ {
+ NativeUIUtils.showWindow(hnd);
+ NativeUIUtils.restoreWindow(hnd);
+ }
+
+ //Force update on redrawing
+ androidInstance.setWindowHandle(0);
+ }
+
+ super.dispose();
+ }
+
+ /**
+ * Updates the composite size when zoom is changed.
+ *
+ * @param zoom
+ * the zoom factor
+ */
+ public void setZoomFactor(double zoomFactor)
+ {
+ info("Update detached view composite size");
+ if (zoomFactor == ZOOM_FIT)
+ {
+ isFitToWindow = true;
+ }
+ else
+ {
+ isFitToWindow = false;
+ }
+ this.zoomFactor = zoomFactor;
+ }
+
+ /**
+ * Gets the zoom factor.
+ *
+ * @return zoom the zoom factor.
+ */
+ public double getZoomFactor()
+ {
+ return zoomFactor;
+ }
+
+ /**
+ * Applies the zoom factor to the components of the composite, updating the
+ * composite size to hold totally the main display.
+ */
+ public void applyZoomFactor()
+ {
+
+ SWTRemoteDisplay mainDisplay = UIHelper.getRemoteDisplayAssociatedToControl(this);
+ IRemoteDisplay.Rotation rotation = mainDisplay.getRotation();
+
+ int baseHeight;
+ int baseWidth;
+
+ switch (rotation)
+ {
+ case ROTATION_90DEG_COUNTERCLOCKWISE:
+ baseHeight = mainDisplay.getScreenWidth();
+ baseWidth = mainDisplay.getScreenHeight();
+ break;
+ default:
+ baseHeight = mainDisplay.getScreenHeight();
+ baseWidth = mainDisplay.getScreenWidth();
+
+ }
+
+ int width;
+ int height;
+ if (isFitToWindow)
+ {
+ Rectangle clientArea = getParent().getClientArea();
+ if ((clientArea.width == 0) || (clientArea.height == 0))
+ {
+ // zoom factor cannot be zero, otherwise an
+ // IllegalArgumentException
+ // is raised in some SWT methods
+ fitZoomfactor = MINIMUM_ZOOM_FACTOR;
+ }
+ else
+ {
+ double widthRatio = (double) (clientArea.width) / baseWidth;
+ double heightRatio = (double) (clientArea.height) / baseHeight;
+ fitZoomfactor = Math.min(widthRatio, heightRatio);
+ }
+ width = new Double(baseWidth * fitZoomfactor).intValue();
+ height = new Double(baseHeight * fitZoomfactor).intValue();
+
+ if (mainDisplay != null)
+ {
+ mainDisplay.setZoomFactor(fitZoomfactor);
+ }
+ }
+ else
+ {
+ width = new Double(baseWidth * zoomFactor).intValue();
+ height = new Double(baseHeight * zoomFactor).intValue();
+
+ if (mainDisplay != null)
+ {
+ mainDisplay.setZoomFactor(zoomFactor);
+ }
+ }
+
+ setSize(width, height);
+ }
+
+ /**
+ * Adds listener for SWT events.
+ */
+ private void addListener()
+ {
+ // add listener to handle keyboard key pressing
+ keyListener = new KeyListener()
+ {
+
+ public void keyPressed(KeyEvent arg0)
+ {
+
+ int keyCode = arg0.keyCode;
+
+ if (keyCode == SWT.CTRL)
+ {
+ ctrlPressed = true;
+ }
+ else
+ {
+ // send message to emulator
+ androidInput.sendKey(arg0.character, keyCode, keyMap);
+ }
+
+ }
+
+ public void keyReleased(KeyEvent arg0)
+ {
+ int keyCode = arg0.keyCode;
+
+ if (keyCode == SWT.CTRL)
+ {
+ ctrlPressed = false;
+ }
+ }
+
+ };
+
+ // listener to change the zoom factor using Ctrl + Mouse Wheel
+ addMouseWheelListener(new MouseWheelListener()
+ {
+
+ public void mouseScrolled(MouseEvent event)
+ {
+ if (ctrlPressed)
+ {
+
+ if ((event.count > 0) && (zoomFactor < IHandlerConstants.MAXIMUM_ZOOM))
+ {
+ // increase zoom factor
+ setZoomFactor(zoomFactor + IHandlerConstants.STEP_ZOOM);
+ applyZoomFactor();
+ }
+
+ else if ((event.count < 0) && (zoomFactor > IHandlerConstants.MINIMUM_ZOOM))
+ {
+ // decrease zoom factor
+ setZoomFactor(zoomFactor - IHandlerConstants.STEP_ZOOM);
+ applyZoomFactor();
+ }
+ }
+ }
+ });
+
+ mouseListener = new MouseAdapter()
+ {
+ /**
+ * @see org.eclipse.swt.events.MouseListener#mouseUp(MouseEvent)
+ */
+ @Override
+ public void mouseUp(MouseEvent e)
+ {
+ handleMouseUp(e);
+ }
+
+ /**
+ * @see org.eclipse.swt.events.MouseListener#mouseDown(MouseEvent)
+ */
+ @Override
+ public void mouseDown(MouseEvent e)
+ {
+ setFocus();
+ handleMouseDown(e);
+ }
+ };
+
+ mouseMoveListener = new MouseMoveListener()
+ {
+ /**
+ * @see org.eclipse.swt.events.MouseMoveListener#mouseMove(MouseEvent)
+ */
+ public void mouseMove(MouseEvent e)
+ {
+ handleMouseMove(e);
+ }
+ };
+
+ getParent().addControlListener(new ControlAdapter()
+ {
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.swt.events.ControlAdapter#controlResized(org.eclipse.swt.events.ControlEvent)
+ */
+ @Override
+ public void controlResized(ControlEvent event)
+ {
+ if (isFitToWindow)
+ {
+ applyZoomFactor();
+ }
+ }
+ });
+
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.emulator.ui.controls.IAndroidComposite#applyLayout(java.lang.String)
+ */
+ public void applyLayout(String layoutName)
+ {
+ //do nothing
+ }
+
+ /**
+ * Gets the listener that handles SWT key pressing and releasing events.
+ *
+ * @return the KeyListener object
+ */
+ public KeyListener getKeyListener()
+ {
+ return keyListener;
+ }
+
+ /**
+ * Gets the listener that handles SWT mouse clicking events.
+ *
+ * @return the MouseListener object
+ */
+ public MouseListener getMouseListener()
+ {
+ return mouseListener;
+ }
+
+ /**
+ * Gets the listener that handles SWT mouse moving events.
+ *
+ * @return the MouseMoveListener object
+ */
+ public MouseMoveListener getMouseMoveListener()
+ {
+ return mouseMoveListener;
+ }
+
+ /**
+ * Handles the mouse up event on the skin composite
+ *
+ * @param e
+ * The mouse up event
+ */
+ private void handleMouseUp(MouseEvent e)
+ {
+ if (e.button == 1)
+ {
+ isMouseLeftButtonPressed = false;
+ UIHelper.ajustCoordinates(e, this);
+ androidInput.sendMouseUp(e.x, e.y);
+ }
+ }
+
+ /**
+ * Handles the mouse down event on the skin composite
+ *
+ * @param e
+ * The mouse down event
+ */
+ private void handleMouseDown(MouseEvent e)
+ {
+ if (e.button == 1)
+ {
+ UIHelper.ajustCoordinates(e, this);
+ androidInput.sendMouseDown(e.x, e.y);
+ isMouseLeftButtonPressed = true;
+ }
+
+ }
+
+ /**
+ * Handles the mouse move event on the skin composite
+ *
+ * @param e
+ * The mouse move event
+ */
+ private void handleMouseMove(MouseEvent e)
+ {
+ if (isMouseLeftButtonPressed)
+ {
+ UIHelper.ajustCoordinates(e, this);
+ androidInput.sendMouseMove(e.x, e.y);
+ }
+ }
+
+ public boolean isFitToWindowSelected()
+ {
+ return isFitToWindow;
+ }
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/controls/nativewindow/NativeWindowComposite.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/controls/nativewindow/NativeWindowComposite.java
new file mode 100644
index 0000000..1658f98
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/controls/nativewindow/NativeWindowComposite.java
@@ -0,0 +1,621 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.emulator.ui.controls.nativewindow;
+
+import static com.motorola.studio.android.common.log.StudioLogger.error;
+import static com.motorola.studio.android.common.log.StudioLogger.info;
+
+import java.util.Timer;
+import java.util.TimerTask;
+
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.SWTException;
+import org.eclipse.swt.custom.ScrolledComposite;
+import org.eclipse.swt.events.ControlAdapter;
+import org.eclipse.swt.events.ControlEvent;
+import org.eclipse.swt.events.KeyListener;
+import org.eclipse.swt.events.MouseListener;
+import org.eclipse.swt.events.MouseMoveListener;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.layout.FillLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.ui.PlatformUI;
+
+import com.motorola.studio.android.AndroidPlugin;
+import com.motorola.studio.android.adt.DDMSFacade;
+import com.motorola.studio.android.common.preferences.DialogWithToggleUtils;
+import com.motorola.studio.android.emulator.core.model.IAndroidEmulatorInstance;
+import com.motorola.studio.android.emulator.core.model.IInputLogic;
+import com.motorola.studio.android.emulator.core.skin.IAndroidSkin;
+import com.motorola.studio.android.emulator.core.utils.TelnetAndroidInput;
+import com.motorola.studio.android.emulator.i18n.EmulatorNLS;
+import com.motorola.studio.android.emulator.logic.AndroidLogicUtils;
+import com.motorola.studio.android.emulator.ui.controls.IAndroidComposite;
+import com.motorola.studio.android.nativeos.NativeUIUtils;
+
+public class NativeWindowComposite extends ScrolledComposite implements IAndroidComposite
+{
+ /**
+ * Preference key of the Question Dialog about changing zoom
+ *
+ */
+ private static String LOOSE_ORIGINAL_SCALE_KEY_PREFERENCE = "loose.original.scale";
+
+ //Constants
+ private static final double MINIMUM_ZOOM_FACTOR = 0.10;
+
+ private static final double ZOOM_FIT = 0.0;
+
+ private Composite contentComposite;
+
+ private IAndroidEmulatorInstance androidInstance;
+
+ private long windowHandle;
+
+ private long originalParentHandle;
+
+ private long windowProperties;
+
+ private Point windowSize;
+
+ private Point nativeWindowSize;
+
+ private NativeWindowMonitor nativeWindowMonitor;
+
+ protected boolean resizing;
+
+ private boolean isFitToWindow;
+
+ private double zoomFactor = 0.99;
+
+ private double fitZoomFactor = ZOOM_FIT;
+
+ private boolean forceNativeWindowSizeUpdate;
+
+ private boolean isOriginalScale;
+
+ private boolean zoomLocked;
+
+ private class NativeWindowMonitor extends Timer
+ {
+ private Timer timer;
+
+ private MonitorTask monitorTask;
+
+ public NativeWindowMonitor(long interval)
+ {
+ timer = new Timer();
+ monitorTask = new MonitorTask();
+ timer.schedule(monitorTask, interval, interval);
+ }
+
+ private class MonitorTask extends TimerTask
+ {
+ @Override
+ public void run()
+ {
+ Point newWindowSize =
+ NativeUIUtils.getWindowSize(originalParentHandle, windowHandle);
+ if ((windowHandle <= 0) || !newWindowSize.equals(windowSize))
+ {
+ Display display = Display.getDefault();
+ if (!display.isDisposed())
+ {
+ try
+ {
+ display.syncExec(new Runnable()
+ {
+ public void run()
+ {
+ updateContentComposite();
+ }
+ });
+ }
+ catch (SWTException e)
+ {
+ //Do nothing in case the widget is disposed, occurs when the tool is closing.
+ }
+ }
+ }
+
+ if (NativeUIUtils.isWindowEnabled(windowHandle))
+ {
+ Display display = Display.getDefault();
+ if (!display.isDisposed())
+ {
+ try
+ {
+ display.syncExec(new Runnable()
+ {
+ public void run()
+ {
+ if (!contentComposite.isDisposed())
+ {
+ contentComposite.forceFocus();
+ }
+ }
+ });
+ }
+ catch (SWTException e)
+ {
+ //Do nothing in case the widget is disposed, occurs when the tool is closing.
+ }
+ }
+ }
+ }
+ }
+
+ public void stopMonitoring()
+ {
+ timer.cancel();
+ timer = null;
+ monitorTask = null;
+ }
+ }
+
+ public NativeWindowComposite(Composite parent, IAndroidSkin androidSkin,
+ final IAndroidEmulatorInstance instance)
+ {
+ super(parent, SWT.BORDER | SWT.H_SCROLL | SWT.V_SCROLL);
+
+ info("Creating Native Window Composite for " + instance.getName());
+
+ getVerticalBar().setEnabled(true);
+ getHorizontalBar().setEnabled(true);
+ this.setLayout(new FillLayout());
+
+ androidInstance = instance;
+
+ nativeWindowMonitor = new NativeWindowMonitor(500);
+
+ addControlListener(new ControlAdapter()
+ {
+ final boolean[] running = new boolean[1];
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.swt.events.ControlAdapter#controlResized(org.eclipse.swt.events.ControlEvent)
+ */
+ @Override
+ public void controlResized(ControlEvent event)
+ {
+ if (isFitToWindow)
+ {
+ try
+ {
+ Thread.sleep(200);
+ }
+ catch (InterruptedException e)
+ {
+ //do nothing
+ }
+
+ if (running[0])
+ {
+ return;
+ }
+ running[0] = true;
+ Display.getCurrent().asyncExec(new Runnable()
+ {
+ public void run()
+ {
+ running[0] = false;
+ if (!getShell().isDisposed())
+ {
+ calculateFitZoomFactor(forceNativeWindowSizeUpdate);
+ applyZoomFactor();
+ }
+
+ }
+ });
+ }
+ }
+ });
+
+ createContentComposite(instance);
+ info("Created Native Window Composite for " + instance.getName());
+ }
+
+ /**
+ * Creates the content composite that will be parent of emulator native window
+ *
+ * @param instance A android instance from which the composite could be retrieved if it is already created
+ */
+ public void createContentComposite(IAndroidEmulatorInstance instance)
+ {
+ contentComposite = instance.getComposite();
+ if (contentComposite != null)
+ {
+ info("Instance already has a composite");
+ contentComposite.setParent(this);
+ contentComposite.setVisible(true);
+ }
+ else
+ {
+ contentComposite = new Composite(this, SWT.EMBEDDED | SWT.NO_BACKGROUND);
+ }
+
+ this.setContent(contentComposite);
+ if (instance.getProperties().getProperty("Command_Line").contains("-scale"))
+ {
+ isOriginalScale = true;
+ }
+
+ //Force to update native window size at 100% when first using nativeWindowSize field
+ forceNativeWindowSizeUpdate = true;
+
+ //Avoid perform apply zoom factor when creating composite
+ zoomLocked = true;
+ draw();
+ }
+
+ /**
+ * Changes the parent from OS to content composite keeping the original properties and parent window reference
+ */
+ private void draw()
+ {
+ if (contentComposite != null)
+ {
+ windowHandle = androidInstance.getWindowHandle();
+
+ //If the instance does not contain the window handle, it should be retrieved
+ //from native emulator window and assigned to instance
+ if (windowHandle <= 0)
+ {
+ int port =
+ AndroidLogicUtils.getEmulatorPort(DDMSFacade
+ .getSerialNumberByName(androidInstance.getName()));
+ windowHandle = NativeUIUtils.getWindowHandle(androidInstance.getName(), port);
+
+ androidInstance.setWindowHandle(windowHandle);
+ }
+
+ if ((windowProperties <= 0) && (windowHandle > 0))
+ {
+ windowProperties = NativeUIUtils.getWindowProperties(windowHandle);
+ info("Native Window Properties:" + windowProperties);
+ }
+
+ //Set Window Style
+ if (windowHandle > 0)
+ {
+ NativeUIUtils.setWindowStyle(windowHandle);
+ }
+
+ if (originalParentHandle <= 0)
+ {
+ originalParentHandle = windowHandle;
+ }
+
+ //Retrieve window size before changing parent
+ if (windowHandle > 0)
+ {
+ windowSize = NativeUIUtils.getWindowSize(originalParentHandle, windowHandle);
+ }
+
+ //Set the new Parent and store the original parent
+ if ((originalParentHandle <= 0) || (originalParentHandle == windowHandle))
+ {
+ if (windowHandle > 0)
+ {
+ originalParentHandle =
+ NativeUIUtils.embedWindow(windowHandle, contentComposite);
+ info("Native Window Parent:" + originalParentHandle);
+ }
+ }
+ else
+ {
+ NativeUIUtils.embedWindow(windowHandle, contentComposite);
+ }
+
+ if (windowSize == null)
+ {
+ windowSize = new Point(700, 500);
+ }
+
+ //Update composite size
+ contentComposite
+ .setSize(contentComposite.computeSize(windowSize.x, windowSize.y, true));
+ contentComposite.redraw();
+ this.update();
+
+ this.setMinSize(contentComposite.computeSize(windowSize.x, windowSize.y));
+ this.layout();
+ }
+ else
+ {
+ createContentComposite(androidInstance);
+ }
+ }
+
+ public void changeToNextLayout()
+ {
+ contentComposite.setVisible(false);
+
+ contentComposite.setLocation(0, 0);
+
+ NativeUIUtils.sendNextLayoutCommand(originalParentHandle, windowHandle);
+
+ updateContentComposite();
+
+ forceNativeWindowSizeUpdate = true;
+ if (isFitToWindow)
+ {
+ //Force update to fit zoom factor
+ setZoomFactor(ZOOM_FIT);
+ }
+ applyZoomFactor();
+
+ try
+ {
+ Thread.sleep(200);
+ }
+ catch (InterruptedException e)
+ {
+ //do nothing
+ }
+
+ contentComposite.setVisible(true);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.swt.widgets.Widget#dispose()
+ */
+ @Override
+ public void dispose()
+ {
+ info("Disposing Native Window Composite");
+ if (nativeWindowMonitor != null)
+ {
+ nativeWindowMonitor.stopMonitoring();
+ nativeWindowMonitor = null;
+ info("Disposed Native Window Monitor");
+ }
+ if (windowHandle > 0)
+ {
+ info("Restoring original properties for window: " + windowHandle);
+ NativeUIUtils.setWindowProperties(windowHandle, windowProperties);
+
+ boolean shallUnembed =
+ AndroidPlugin.getDefault().getPreferenceStore()
+ .getBoolean(AndroidPlugin.SHALL_UNEMBED_EMULATORS_PREF_KEY);
+ if ((originalParentHandle > 0) && shallUnembed)
+ {
+ info("Setting original parent: " + originalParentHandle + " for window"
+ + windowHandle);
+ NativeUIUtils.unembedWindow(windowHandle, originalParentHandle);
+ //Force update when redrawing
+ androidInstance.setWindowHandle(0);
+ info("Restoring window: " + windowHandle);
+ NativeUIUtils.restoreWindow(windowHandle);
+ }
+
+ }
+
+ if (!Platform.getOS().equals(Platform.OS_WIN32))
+ {
+ info("Trying to store the content composite in instance");
+ if (contentComposite != null)
+ {
+ info("Is instance started? :" + androidInstance.isStarted());
+ if (androidInstance.isStarted())
+ {
+ try
+ {
+ contentComposite.setParent(PlatformUI.getWorkbench()
+ .getActiveWorkbenchWindow().getShell());
+ this.setContent(null);
+ contentComposite.setVisible(true);
+ androidInstance.setComposite(contentComposite);
+ }
+ catch (Exception e)
+ {
+ error("Error trying to store the content composite :" + e.getMessage());
+ }
+ }
+ }
+ }
+ super.dispose();
+ }
+
+ /**
+ * Apply the zoom factor to the instance
+ */
+ public void applyZoomFactor()
+ {
+ if (!isOriginalScale && !zoomLocked)
+ {
+ contentComposite.setLocation(0, 0);
+ IInputLogic inputLogic = androidInstance.getInputLogic();
+ TelnetAndroidInput telnetAndroidInput = (TelnetAndroidInput) inputLogic;
+ NativeUIUtils.hideWindow(windowHandle);
+
+ if (isFitToWindow)
+ {
+ telnetAndroidInput.sendWindowScale(fitZoomFactor);
+ }
+ else
+ {
+ telnetAndroidInput.sendWindowScale(zoomFactor);
+ }
+
+ try
+ {
+ Thread.sleep(200);
+ }
+ catch (InterruptedException e)
+ {
+ //do nothing
+ }
+ telnetAndroidInput.dispose();
+ NativeUIUtils.showWindow(windowHandle);
+ updateContentComposite();
+ }
+ }
+
+ void calculateFitZoomFactor(boolean requireNativeSizeUpdate)
+ {
+ // Compute new zoom factor if the zoom mode is "Fit to Window"
+ Rectangle clientArea = getClientArea();
+ if ((clientArea.width == 0) || (clientArea.height == 0))
+ {
+ // zoom factor cannot be zero, otherwise an
+ // IllegalArgumentException
+ // is raised in some SWT methods
+ fitZoomFactor = MINIMUM_ZOOM_FACTOR;
+ }
+ else
+ {
+ // if the layout was changed, it is needed to retrieve the native window size at 100%
+ // that size is required to the correct ratio calculus
+ if (requireNativeSizeUpdate)
+ {
+ forceNativeWindowSizeUpdate = false;
+ updateNativeWindowSize();
+ }
+ else
+ {
+ double widthRatio = (double) (clientArea.width) / nativeWindowSize.x;
+ double heightRatio = (double) (clientArea.height) / nativeWindowSize.y;
+ fitZoomFactor =
+ (Math.min(widthRatio, heightRatio) > MINIMUM_ZOOM_FACTOR ? Math.min(
+ widthRatio, heightRatio) : MINIMUM_ZOOM_FACTOR);
+ }
+ }
+ }
+
+ /**
+ * This method brings the emulator window to 100% zoom factor to retrieve their native size
+ */
+ private void updateNativeWindowSize()
+ {
+ info("Updating Native Window Size");
+ setZoomFactor(1.0d);
+ applyZoomFactor();
+
+ nativeWindowSize = NativeUIUtils.getWindowSize(originalParentHandle, windowHandle);
+
+ setZoomFactor(ZOOM_FIT);
+ applyZoomFactor();
+ info("Updated Native Window Size");
+ }
+
+ private void updateContentComposite()
+ {
+ if (!this.isDisposed())
+ {
+ windowSize = NativeUIUtils.getWindowSize(originalParentHandle, windowHandle);
+ if (windowSize != null)
+ {
+ if ((contentComposite != null) && !contentComposite.isDisposed())
+ {
+ contentComposite.setSize(windowSize.x, windowSize.y);
+ contentComposite.redraw();
+ }
+ this.setMinSize(windowSize.x, windowSize.y);
+ draw();
+ this.redraw();
+ info("Updated Content Composite");
+ }
+ }
+ }
+
+ /**
+ * Gets the current zoom factor.
+ *
+ * @return the zoom factor
+ */
+ public double getZoomFactor()
+ {
+ if (isFitToWindow)
+ {
+ return fitZoomFactor;
+ }
+ return zoomFactor;
+ }
+
+ /**
+ * Sets the zoom factor.
+ *
+ * @param zoom the zoom factor
+ *
+ */
+ public void setZoomFactor(double zoom)
+ {
+ boolean execute = true;
+ zoomLocked = false;
+ if (isOriginalScale)
+ {
+ execute =
+ DialogWithToggleUtils.showQuestion(LOOSE_ORIGINAL_SCALE_KEY_PREFERENCE,
+ EmulatorNLS.QUESTION_NativeWindow_LooseOriginalScale_Title,
+ EmulatorNLS.QUESTION_NativeWindow_LooseOriginalScale_Text);
+ }
+ if (execute)
+ {
+ isOriginalScale = false;
+
+ if (zoom == ZOOM_FIT)
+ {
+ isFitToWindow = true;
+ calculateFitZoomFactor(forceNativeWindowSizeUpdate);
+ }
+ else
+ {
+ isOriginalScale = false;
+ isFitToWindow = false;
+ }
+ zoomFactor = zoom;
+ }
+ }
+
+ @Override
+ public boolean setFocus()
+ {
+ NativeUIUtils.setWindowFocus(windowHandle);
+ return super.setFocus();
+ }
+
+ public void applyLayout(String layoutName)
+ {
+ setLayout(new FillLayout());
+ draw();
+ }
+
+ public KeyListener getKeyListener()
+ {
+ return null;
+ }
+
+ public MouseListener getMouseListener()
+ {
+ return null;
+ }
+
+ public MouseMoveListener getMouseMoveListener()
+ {
+ return null;
+ }
+
+ public boolean isFitToWindowSelected()
+ {
+ return isFitToWindow;
+ }
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/controls/skin/AndroidSkinLayout.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/controls/skin/AndroidSkinLayout.java
new file mode 100644
index 0000000..11fadd5
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/controls/skin/AndroidSkinLayout.java
@@ -0,0 +1,507 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.emulator.ui.controls.skin;
+
+import org.eclipse.sequoyah.vnc.vncviewer.graphics.swt.SWTRemoteDisplay;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Layout;
+
+import com.motorola.studio.android.emulator.core.model.IAndroidEmulatorInstance;
+import com.motorola.studio.android.emulator.core.skin.AndroidSkinBean;
+import com.motorola.studio.android.emulator.core.skin.ISkinKeyXmlTags;
+import com.motorola.studio.android.emulator.ui.IAndroidUIConstants;
+import com.motorola.studio.android.emulator.ui.controls.RemoteCLIDisplay;
+import com.motorola.studio.android.emulator.ui.controls.UIHelper;
+
+/**
+ * DESCRIPTION:
+ * This class implements the layout used to draw skins
+ * It is a very specific implementation that applies only to Android Emulator
+ * The position and size of the widgets are read from the skin bean provided
+ * during layout construction
+ *
+ * RESPONSIBILITY:
+ * - Draw the widgets that composes the skin in the correct position and
+ * with the correct size
+ *
+ * COLABORATORS:
+ * None.
+ *
+ * USAGE:
+ * Use of this class is restricted to the classes in this package
+ */
+class AndroidSkinLayout extends Layout
+{
+ /**
+ * Object that holds information from the skin.xml file
+ * of the skin being currently used
+ */
+ private final AndroidSkinBean skin;
+
+ /**
+ * Reference to the CLI display that will be placed by this layout
+ */
+ private RemoteCLIDisplay cliDisplayChild = null;
+
+ /**
+ * Reference to the main display that will be placed by this layout
+ */
+ private SWTRemoteDisplay mainDisplayChild = null;
+
+ /**
+ * Flag that indicates if the skin contains an open external display
+ * placeholder
+ */
+ private final boolean openExternalDisplayAvailable;
+
+ /**
+ * Flag that indicates if the skin contains an external display
+ * placeholder
+ */
+ private final boolean externalDisplayAvailable;
+
+ /**
+ * Flag that indicates that the flip is supported. This flag has nothing to do with slide
+ */
+ private final boolean isFlipSupported;
+
+ /**
+ * Creates a new AndroidSkinLayout object.
+ * As the image dimensions are not provided with this constructor, it is not possible
+ * to have automatic zoom factor calculation. Using this constructor will set the
+ * initial zoom policy to "100%"
+ *
+ * @param skin An skin bean containing the parameters used to place the
+ * widgets
+ * @param isFlipSupported Flag that indicates if flip (not slide) is supported
+ */
+ AndroidSkinLayout(AndroidSkinBean skin, boolean isFlipSupported)
+ {
+ this.skin = skin;
+
+ this.isFlipSupported = isFlipSupported;
+ openExternalDisplayAvailable = skin.isOpenExternalDisplayAvailable();
+ externalDisplayAvailable = skin.isExternalDisplayAvailable();
+ }
+
+ /**
+ * @see org.eclipse.swt.widgets.Layout#computeSize(Composite, int, int, boolean)
+ */
+ @Override
+ protected Point computeSize(Composite composite, int wHint, int hHint, boolean flushCache)
+ {
+ if (!(composite instanceof SkinComposite))
+ {
+ throw new IllegalArgumentException();
+ }
+
+ Point size;
+
+ // Retrieve needed data from composite
+ IAndroidEmulatorInstance instance = UIHelper.getInstanceAssociatedToControl(composite);
+ boolean flipSlideClosed = false;
+ if (instance != null)
+ {
+ //flipSlideClosed = instance.isFlipSlideClosed();
+ flipSlideClosed = false;
+ }
+ double zoomFactor = ((SkinComposite) composite).getZoomFactor();
+
+ if (externalDisplayAvailable)
+ {
+ if (openExternalDisplayAvailable)
+ {
+ if (!flipSlideClosed)
+ {
+ // Scenario 1:
+ // A) Available displays: INTERNAL, OPEN EXTERNAL, EXTERNAL
+ // B) Flip is open
+ // C) Displays being showed: INTERNAL, OPEN EXTERNAL
+ size = allAvailable(zoomFactor);
+ }
+ else
+ {
+ // Scenario 2:
+ // A) Available displays: INTERNAL, OPEN EXTERNAL, EXTERNAL
+ // B) Flip is closed
+ // C) Display being showed: EXTERNAL
+ size = flipClosed(zoomFactor);
+ }
+ }
+ else
+ {
+ if (!flipSlideClosed)
+ {
+ // Scenario 3:
+ // A) Available displays: INTERNAL, EXTERNAL
+ // B) Flip is opened
+ // C) Display being showed: INTERNAL
+ size = openExternalUnavailable(zoomFactor);
+ }
+ else
+ {
+ // Scenario 4:
+ // A) Available displays: INTERNAL, EXTERNAL
+ // B) Flip is closed
+ // C) Display being showed: EXTERNAL
+ size = openExternalUnavailableAndFlipClosed(zoomFactor);
+ }
+ }
+ }
+ else
+ {
+ // Scenario 5:
+ // A) Available display: INTERNAL
+ // B) Flip is opened or closed
+ // C) Display being showed: INTERNAL
+ size = onlyInternal(zoomFactor);
+ }
+
+ return size;
+ }
+
+ /**
+ * This method is called by computeSize when all displays are available and the flip is opened
+ *
+ * @param zoomFactor The zoom factor used to calculate the size
+ *
+ * @return The size
+ */
+ private Point allAvailable(double zoomFactor)
+ {
+ Point size;
+
+ int x1 = skin.getSkinPropertyValue(ISkinKeyXmlTags.SKIN_INTERNAL_VIEW_X);
+ int y1 =
+ Math.min(skin.getSkinPropertyValue(ISkinKeyXmlTags.SKIN_INTERNAL_VIEW_Y),
+ skin.getSkinPropertyValue(ISkinKeyXmlTags.SKIN_OPEN_EXTERNAL_VIEW_Y));
+ int x2 =
+ skin.getSkinPropertyValue(ISkinKeyXmlTags.SKIN_OPEN_EXTERNAL_VIEW_X)
+ + skin.getSkinPropertyValue(ISkinKeyXmlTags.SKIN_OPEN_EXTERNAL_VIEW_WIDTH);
+ int y2 =
+ Math.max(
+ skin.getSkinPropertyValue(ISkinKeyXmlTags.SKIN_INTERNAL_VIEW_Y)
+ + skin.getSkinPropertyValue(ISkinKeyXmlTags.SKIN_INTERNAL_VIEW_HEIGHT),
+ skin.getSkinPropertyValue(ISkinKeyXmlTags.SKIN_OPEN_EXTERNAL_VIEW_Y)
+ + skin.getSkinPropertyValue(ISkinKeyXmlTags.SKIN_OPEN_EXTERNAL_VIEW_HEIGHT));
+
+ size = new Point((int) ((x2 - x1) * zoomFactor), (int) ((y2 - y1) * zoomFactor));
+
+ return size;
+ }
+
+ /**
+ * This method is called by computeSize when all displays are available and the flip is closed
+ *
+ * @param zoomFactor The zoom factor used to calculate the size
+ *
+ * @return The size
+ */
+ private Point flipClosed(double zoomFactor)
+ {
+ Point size =
+ new Point(
+ (int) (skin.getSkinPropertyValue(ISkinKeyXmlTags.SKIN_EXTERNAL_VIEW_WIDTH) * zoomFactor),
+ (int) (skin.getSkinPropertyValue(ISkinKeyXmlTags.SKIN_EXTERNAL_VIEW_HEIGHT) * zoomFactor));
+
+ return size;
+ }
+
+ /**
+ * This method is called by computeSize when all displays but open external display are available
+ * and the flip is opened
+ *
+ * @param zoomFactor The zoom factor used to calculate the size
+ *
+ * @return The size
+ */
+ private Point openExternalUnavailable(double zoomFactor)
+ {
+ Point size =
+ new Point(
+ (int) (skin.getSkinPropertyValue(ISkinKeyXmlTags.SKIN_INTERNAL_VIEW_WIDTH) * zoomFactor),
+ (int) (skin.getSkinPropertyValue(ISkinKeyXmlTags.SKIN_INTERNAL_VIEW_HEIGHT) * zoomFactor));
+
+ return size;
+ }
+
+ /**
+ * This method is called by computeSize when all displays but open external display are available
+ * and the flip is closed
+ *
+ * @param zoomFactor The zoom factor used to calculate the size
+ *
+ * @return The size
+ */
+ private Point openExternalUnavailableAndFlipClosed(double zoomFactor)
+ {
+ Point size =
+ new Point(
+ (int) (skin.getSkinPropertyValue(ISkinKeyXmlTags.SKIN_EXTERNAL_VIEW_WIDTH) * zoomFactor),
+ (int) (skin.getSkinPropertyValue(ISkinKeyXmlTags.SKIN_EXTERNAL_VIEW_HEIGHT) * zoomFactor));
+
+ return size;
+ }
+
+ /**
+ * This method is called by computeSize when only the internal display is available
+ *
+ * @param zoomFactor The zoom factor used to calculate the size
+ *
+ * @return The size
+ */
+ private Point onlyInternal(double zoomFactor)
+ {
+ Point size =
+ new Point(
+ (int) (skin.getSkinPropertyValue(ISkinKeyXmlTags.SKIN_INTERNAL_VIEW_WIDTH) * zoomFactor),
+ (int) (skin.getSkinPropertyValue(ISkinKeyXmlTags.SKIN_INTERNAL_VIEW_HEIGHT) * zoomFactor));
+
+ return size;
+ }
+
+ /**
+ * @see org.eclipse.swt.widgets.Layout#layout(Composite, boolean)
+ */
+ @Override
+ protected void layout(Composite composite, boolean flushCache)
+ {
+ if (!(composite instanceof SkinComposite))
+ {
+ // If this composite is not a SkinComposite, no layout should be done
+ return;
+ }
+
+ boolean canProceed = true;
+
+ if (flushCache || (mainDisplayChild == null) || (cliDisplayChild == null))
+ {
+ Control[] children = composite.getChildren();
+ canProceed = checkChidren(children);
+ }
+
+ if (canProceed)
+ {
+ // Retrieve needed data from composite
+ SkinComposite skinComposite = (SkinComposite) composite;
+ IAndroidEmulatorInstance instance = UIHelper.getInstanceAssociatedToControl(composite);
+ boolean flipSlideClosed = false;
+ if (instance != null)
+ {
+ //flipSlideClosed = instance.isFlipSlideClosed();
+ flipSlideClosed = false;
+ }
+
+ skinComposite.recalculateZoomFactor();
+ skinComposite.updateDisplayRectangle();
+
+ double zoomFactor = skinComposite.getZoomFactor();
+ Rectangle displayRectangle = skinComposite.getDisplayRectangle();
+
+ // Handling main display case
+ if (mainDisplayChild != null)
+ {
+ layoutMainDisplay(zoomFactor, displayRectangle);
+
+ if ((isFlipSupported) && (flipSlideClosed))
+ {
+ // On phones that support flip, the main display is hidden
+ // when the flip is closed
+ mainDisplayChild.setVisible(false);
+ }
+ else
+ {
+ mainDisplayChild.setVisible(true);
+ }
+ }
+
+ // Handling CLI display case
+ if (cliDisplayChild != null)
+ {
+ layoutCliDisplay(zoomFactor, displayRectangle, flipSlideClosed);
+
+ if (((externalDisplayAvailable) && (!flipSlideClosed))
+ || ((openExternalDisplayAvailable) && (flipSlideClosed)))
+ {
+ // The CLI display is shown in 2 situations:
+ // 1. When the flip is closed and there is information about
+ // external display
+ // 2. When the flip is opened and there is information about
+ // open external display
+ cliDisplayChild.setVisible(true);
+ }
+ else
+ {
+ cliDisplayChild.setVisible(false);
+ }
+ }
+ }
+
+ }
+
+ /**
+ * Checks if the composite children are as expected by the layout
+ * It is needed to check if the composite's children are, at most:
+ * 1. One main display
+ * 2. One CLI display
+ *
+ * @param children Array of composite children to check
+ *
+ * @return true if children are as expected; false otherwise
+ */
+ private boolean checkChidren(Control[] children)
+ {
+
+ RemoteCLIDisplay cliDisplayInstance = null;
+ SWTRemoteDisplay mainDisplayInstance = null;
+ boolean childrenOk = true;
+
+ // We need to check if the composite's children are, at most:
+ //
+ // 1. One main display
+ // 2. One CLI display
+ for (Control child : children)
+ {
+ if (child instanceof SWTRemoteDisplay)
+ {
+ if (mainDisplayInstance == null)
+ {
+ mainDisplayInstance = (SWTRemoteDisplay) child;
+ mainDisplayChild = mainDisplayInstance;
+ }
+ else
+ {
+ childrenOk = false;
+
+ break;
+ }
+ }
+ else if (child instanceof RemoteCLIDisplay)
+ {
+ if (cliDisplayInstance == null)
+ {
+ cliDisplayInstance = (RemoteCLIDisplay) child;
+ cliDisplayChild = cliDisplayInstance;
+ }
+ else
+ {
+ childrenOk = false;
+
+ break;
+ }
+ }
+ else
+ {
+ childrenOk = false;
+
+ break;
+ }
+ }
+
+ return childrenOk;
+ }
+
+ /**
+ * Performs the layout for main display
+ *
+ * @param zoomFactor The zoom factor to use when dimensioning the main display.
+ * @param displayRectangle The area of skin that is being shown at the view. It can
+ * be only a part of the skin if the view client area
+ * is smaller than the skin size. It is used to calculate translation.
+ */
+ private void layoutMainDisplay(double zoomFactor, Rectangle displayRectangle)
+ {
+ // Computes main display position
+ int x =
+ (int) (skin.getSkinPropertyValue(ISkinKeyXmlTags.SKIN_INTERNAL_VIEW_X) * zoomFactor)
+ - displayRectangle.x;
+ int y =
+ (int) (skin.getSkinPropertyValue(ISkinKeyXmlTags.SKIN_INTERNAL_VIEW_Y) * zoomFactor)
+ - displayRectangle.y;
+ int width =
+ (int) (skin.getSkinPropertyValue(ISkinKeyXmlTags.SKIN_INTERNAL_VIEW_WIDTH)
+ * zoomFactor * skin.getEmbeddedViewScale());
+ int height =
+ (int) (skin.getSkinPropertyValue(ISkinKeyXmlTags.SKIN_INTERNAL_VIEW_HEIGHT)
+ * zoomFactor * skin.getEmbeddedViewScale());
+
+ // Sets main display size and position
+ mainDisplayChild.setZoomFactor(zoomFactor * skin.getEmbeddedViewScale());
+ mainDisplayChild.setBounds(x, y, width, height);
+ }
+
+ /**
+ * Performs the layout for CLI display
+ *
+ * @param zoomFactor The zoom factor to use when dimensioning the main display
+ * @param displayRectangle The area of skin that is being shown at the view. It can
+ * be only a part of the skin if the view client area
+ * is smaller than the skin size. It is used to calculate translation.
+ * @param isFlipSlideClosed True if the flip or slide is currently closed. False otherwise.
+ */
+ private void layoutCliDisplay(double zoomFactor, Rectangle displayRectangle,
+ boolean flipSlideClosed)
+ {
+ // Computes CLI display position
+ int x = 0;
+ int y = 0;
+ int width = 0;
+ int height = 0;
+
+ if (!flipSlideClosed && openExternalDisplayAvailable)
+ {
+ x =
+ (int) (skin.getSkinPropertyValue(ISkinKeyXmlTags.SKIN_OPEN_EXTERNAL_VIEW_X) * zoomFactor)
+ - displayRectangle.x;
+ y =
+ (int) (skin.getSkinPropertyValue(ISkinKeyXmlTags.SKIN_OPEN_EXTERNAL_VIEW_Y) * zoomFactor)
+ - displayRectangle.y;
+ width =
+ (int) (skin.getSkinPropertyValue(ISkinKeyXmlTags.SKIN_OPEN_EXTERNAL_VIEW_WIDTH)
+ * zoomFactor * IAndroidUIConstants.DISPLAY_TO_SKIN_RATIO);
+ height =
+ (int) (skin
+ .getSkinPropertyValue(ISkinKeyXmlTags.SKIN_OPEN_EXTERNAL_VIEW_HEIGHT)
+ * zoomFactor * IAndroidUIConstants.DISPLAY_TO_SKIN_RATIO);
+ }
+ else if (flipSlideClosed && externalDisplayAvailable)
+ {
+ x =
+ (int) ((skin.getSkinPropertyValue(ISkinKeyXmlTags.SKIN_EXTERNAL_VIEW_X) * zoomFactor) - displayRectangle.x);
+ y =
+ (int) ((skin.getSkinPropertyValue(ISkinKeyXmlTags.SKIN_EXTERNAL_VIEW_Y) * zoomFactor) - displayRectangle.y);
+ width =
+ (int) (skin.getSkinPropertyValue(ISkinKeyXmlTags.SKIN_EXTERNAL_VIEW_WIDTH)
+ * zoomFactor * IAndroidUIConstants.DISPLAY_TO_SKIN_RATIO);
+ height =
+ (int) (skin.getSkinPropertyValue(ISkinKeyXmlTags.SKIN_EXTERNAL_VIEW_HEIGHT)
+ * zoomFactor * IAndroidUIConstants.DISPLAY_TO_SKIN_RATIO);
+ }
+
+ // Sets cli display size
+ cliDisplayChild.setZoomFactor(zoomFactor * IAndroidUIConstants.DISPLAY_TO_SKIN_RATIO);
+ cliDisplayChild.setBounds(x, y, width, height);
+ }
+
+ void dispose()
+ {
+ cliDisplayChild = null;
+ mainDisplayChild = null;
+ }
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/controls/skin/SkinComposite.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/controls/skin/SkinComposite.java
new file mode 100644
index 0000000..fa50267
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/controls/skin/SkinComposite.java
@@ -0,0 +1,1300 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.emulator.ui.controls.skin;
+
+import static com.motorola.studio.android.common.log.StudioLogger.debug;
+import static com.motorola.studio.android.common.log.StudioLogger.error;
+import static com.motorola.studio.android.common.log.StudioLogger.info;
+
+import java.util.Collection;
+
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.ControlAdapter;
+import org.eclipse.swt.events.ControlEvent;
+import org.eclipse.swt.events.KeyAdapter;
+import org.eclipse.swt.events.KeyEvent;
+import org.eclipse.swt.events.KeyListener;
+import org.eclipse.swt.events.MouseAdapter;
+import org.eclipse.swt.events.MouseEvent;
+import org.eclipse.swt.events.MouseListener;
+import org.eclipse.swt.events.MouseMoveListener;
+import org.eclipse.swt.events.MouseTrackAdapter;
+import org.eclipse.swt.events.MouseWheelListener;
+import org.eclipse.swt.events.PaintEvent;
+import org.eclipse.swt.events.PaintListener;
+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;
+import org.eclipse.swt.graphics.ImageData;
+import org.eclipse.swt.graphics.RGB;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Layout;
+import org.eclipse.swt.widgets.ScrollBar;
+import org.eclipse.ui.PlatformUI;
+
+import com.motorola.studio.android.adt.DDMSFacade;
+import com.motorola.studio.android.common.utilities.EclipseUtils;
+import com.motorola.studio.android.emulator.core.exception.InstanceStopException;
+import com.motorola.studio.android.emulator.core.exception.SkinException;
+import com.motorola.studio.android.emulator.core.model.IAndroidEmulatorInstance;
+import com.motorola.studio.android.emulator.core.model.IInputLogic;
+import com.motorola.studio.android.emulator.core.skin.AndroidPressKey;
+import com.motorola.studio.android.emulator.core.skin.AndroidSkinBean;
+import com.motorola.studio.android.emulator.core.skin.IAndroidKey;
+import com.motorola.studio.android.emulator.core.skin.IAndroidSkin;
+import com.motorola.studio.android.emulator.i18n.EmulatorNLS;
+import com.motorola.studio.android.emulator.logic.AndroidLogicUtils;
+import com.motorola.studio.android.emulator.ui.controls.IAndroidComposite;
+import com.motorola.studio.android.emulator.ui.controls.UIHelper;
+import com.motorola.studio.android.emulator.ui.handlers.IHandlerConstants;
+import com.motorola.studio.android.nativeos.NativeUIUtils;
+
+/**
+ * DESCRIPTION: This class implements the UI part of the skin
+ *
+ * RESPONSIBILITY: - Provide the skin image with correct layout - Provide means
+ * for the user to send events to the emulator through the phone emulated
+ * keyboard - Provide means for the user to change the zoom and window scrolling
+ * properties
+ *
+ * COLABORATORS: None.
+ *
+ * USAGE: Create an instance of this class every time an phone instance is to be
+ * displayed Call the public methods to interact with the skin.
+ */
+public class SkinComposite extends Composite implements IAndroidComposite
+{
+ // Constants for defining interval between keystrokes
+ // when using long mouse click
+ public static final int FIRST_REFRESH_DELAY_MS = 300;
+
+ public static final int REFRESH_DELAY_PERIOD_MS = 100;
+
+ // Minimum value to be used as zoom factor. This is necessary to avoid
+ // divisions to zero
+ private static final double MINIMUM_ZOOM_FACTOR = 0.0001;
+
+ // The step increased or decreased from the zoom factor using mouse wheel
+ private static final double ZOOM_STEP = 0.5;
+
+ /**
+ * A rectangle that represents the part of the currentSkin image that fits
+ * into the view/screen. It must fit inside the currentSkinImage borders (0,
+ * 0, current skin image width, current skin image height)
+ */
+ private final Rectangle displayRectangle = new Rectangle(0, 0, 0, 0);
+
+ /**
+ * The skin elements provider
+ */
+ private IAndroidSkin skin;
+
+ /**
+ * The image that is currently drawn at screen. It is one image provided by
+ * the skin that is scaled by the current zoom factor
+ */
+ private Image currentSkinImage;
+
+ /**
+ * The key that mouse is over at the current moment
+ */
+ private IAndroidKey currentKey;
+
+ /**
+ * The flag indicating that Ctrl key is pressed
+ */
+ private boolean ctrlPressed = false;
+
+ /**
+ * SWT key pressed/released events listener
+ */
+ private KeyListener keyListener;
+
+ private MouseListener mainDisplayMouseListener;
+
+ private MouseMoveListener mainDisplayMouseMoveListener;
+
+ /**
+ * Flag that indicates if the skin can use the scroll bars to draw itself
+ * bigger than the view client area
+ */
+ private boolean scrollBarsUsed = true;
+
+ /**
+ * True if the mouse left button is pressed. False otherwise
+ */
+ private boolean isMouseLeftButtonPressed;
+
+ /**
+ * True if the mouse right button is pressed. False otherwise
+ */
+ private boolean isMouseRightButtonPressed;
+
+ /**
+ * The zoom factor whose default value is 1.0 (100%)
+ */
+ private double zoomFactor = 1.0;
+
+ private double embeddedViewScale = 1.0;
+
+ private IInputLogic androidInput;
+
+ private boolean isMouseMainDisplayLeftButtonPressed;
+
+ IAndroidEmulatorInstance androidInstance;
+
+ /**
+ * Creates a SkinComposite This composite holds the screens in the correct
+ * positions and maps the keys
+ *
+ * @param parent
+ * The parent composite in which the UI part of the instance
+ * shall be created
+ * @param androidSkin
+ * The skin object that contain data for getting skin information
+ */
+ public SkinComposite(Composite parent, IAndroidSkin androidSkin,
+ IAndroidEmulatorInstance instance)
+ {
+ super(parent, SWT.BACKGROUND | SWT.H_SCROLL | SWT.V_SCROLL);
+ skin = androidSkin;
+ androidInstance = instance;
+
+ // Add listeners
+ addListeners();
+ createListenersForMainDisplay();
+
+ setToolTipText(null);
+
+ androidInput = instance.getInputLogic();
+
+ // Init the scroll bars
+ initScrollBars();
+
+ if (!Platform.getOS().equals(Platform.OS_MACOSX))
+ {
+ hideEmulatorWindow();
+ }
+ }
+
+ private void hideEmulatorWindow()
+ {
+ int port =
+ AndroidLogicUtils.getEmulatorPort(DDMSFacade.getSerialNumberByName(androidInstance
+ .getName()));
+ long windowHandle = NativeUIUtils.getWindowHandle(androidInstance.getName(), port);
+ androidInstance.setWindowHandle(windowHandle);
+ NativeUIUtils.hideWindow(windowHandle);
+ }
+
+ /**
+ * Add listeners to the skin composite
+ */
+ private void addListeners()
+ {
+
+ addPaintListener(new PaintListener()
+ {
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.swt.events.PaintListener#paintControl(org.eclipse.swt.events.PaintEvent)
+ */
+ public void paintControl(PaintEvent e)
+ {
+ // This listener is invoked when a regular SWT redraw is invoked. In this case, no keys
+ // have changed. That's why we pass "null" as parameter
+ drawSkin(e.gc, null);
+ }
+ });
+
+ addMouseListener(new MouseAdapter()
+ {
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.swt.events.MouseAdapter#mouseUp(org.eclipse.swt.events.MouseEvent)
+ */
+ @Override
+ public void mouseUp(MouseEvent e)
+ {
+ if (e.button == 1)
+ {
+ isMouseLeftButtonPressed = false;
+
+ }
+ else if (e.button == 3)
+ {
+ isMouseRightButtonPressed = false;
+ }
+
+ // Handle left button mouse up event
+ if (e.button == 1)
+ {
+ cancelMouseSelection();
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.swt.events.MouseAdapter#mouseDown(org.eclipse.swt.events.MouseEvent)
+ */
+ @Override
+ public void mouseDown(MouseEvent e)
+ {
+ setFocus();
+ if (e.button == 1)
+ {
+ isMouseLeftButtonPressed = true;
+ }
+ else if (e.button == 3)
+ {
+ isMouseRightButtonPressed = true;
+ }
+
+ if (currentKey != null)
+ {
+ ImageData mergedImage = getKeyImageData(currentKey, false);
+ setSkinImage(mergedImage, currentKey, true);
+
+ // Handle left button mouse down event
+ if ((e.button == 1) && (!isMouseRightButtonPressed) && (currentKey != null))
+ {
+ androidInput.sendClick(currentKey.getKeysym(), true);
+ }
+
+ // Handle right button mouse down event
+ else if (e.button == 3)
+ {
+ cancelMouseSelection();
+ }
+
+ }
+ }
+ });
+
+ addMouseMoveListener(new MouseMoveListener()
+ {
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.swt.events.MouseMoveListener#mouseMove(org.eclipse.swt.events.MouseEvent)
+ */
+ public void mouseMove(MouseEvent e)
+ {
+ int posX = (int) ((e.x + displayRectangle.x) / zoomFactor);
+ int posY = (int) ((e.y + displayRectangle.y) / zoomFactor);
+ IAndroidKey keyData = getSkinKey(posX, posY);
+
+ if ((isMouseLeftButtonPressed) && (keyData != currentKey))
+ {
+ cancelMouseSelection();
+ }
+
+ if (!isMouseLeftButtonPressed && (currentKey != keyData))
+ {
+ changeCurrentKey(keyData);
+ }
+ }
+ });
+
+ // listener to change the zoom factor using Ctrl + Mouse Wheel
+ addMouseWheelListener(new MouseWheelListener()
+ {
+ public void mouseScrolled(MouseEvent event)
+ {
+ if (ctrlPressed)
+ {
+
+ // the floor and ceil are required if "fits to window" was
+ // checked.
+ double roundedZoomFactor = Math.floor(zoomFactor / ZOOM_STEP) * ZOOM_STEP;
+
+ if ((event.count > 0) && (roundedZoomFactor < IHandlerConstants.MAXIMUM_ZOOM))
+ {
+ // increase zoom factor
+ setZoomFactor(roundedZoomFactor + ZOOM_STEP);
+ applyZoomFactor();
+ }
+
+ else if ((event.count < 0)
+ && (roundedZoomFactor > IHandlerConstants.MINIMUM_ZOOM))
+ {
+ // decrease zoom factor
+ setZoomFactor(roundedZoomFactor - ZOOM_STEP);
+ applyZoomFactor();
+ }
+
+ }
+ }
+ });
+
+ addMouseTrackListener(new MouseTrackAdapter()
+ {
+ @Override
+ public void mouseExit(MouseEvent mouseevent)
+ {
+ changeCurrentKey(null);
+ }
+ });
+
+ addControlListener(new ControlAdapter()
+ {
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.swt.events.ControlAdapter#controlResized(org.eclipse.swt.events.ControlEvent)
+ */
+ @Override
+ public void controlResized(ControlEvent event)
+ {
+ if (scrollBarsUsed)
+ {
+ synchronizeScrollBars();
+ }
+ else
+ {
+ ImageData imageData = getImageData(false, false);
+ setSkinImage(imageData, null, false);
+ }
+ }
+ });
+
+ }
+
+ public void applyLayout(String layoutName)
+ {
+ // Populate the attributes with information from skin
+ AndroidSkinBean skinBean = null;
+
+ try
+ {
+ skinBean = skin.getSkinBean(layoutName);
+ }
+ catch (SkinException e)
+ {
+ error("The skin data could not be retrieved from skin files. Cause: " + e.getMessage());
+ EclipseUtils.showErrorDialog(e);
+ }
+
+ // Create layout and set it to composite
+ if (skinBean != null)
+ {
+ // When changing to a new layout, the key may move to another position
+ // It does not make sense to keep the old key object
+ currentKey = null;
+
+ // Change the background color to the one that applies to the layout being set
+ RGB color = skin.getBackgroundColor(layoutName);
+ setBackground(new Color(PlatformUI.getWorkbench().getDisplay(), color));
+
+ Layout prevLayout = getLayout();
+ if (prevLayout instanceof AndroidSkinLayout)
+ {
+ ((AndroidSkinLayout) prevLayout).dispose();
+ }
+
+ AndroidSkinLayout androidLayout =
+ new AndroidSkinLayout(skinBean, skin.isFlipSupported());
+ setLayout(androidLayout);
+
+ embeddedViewScale = skinBean.getEmbeddedViewScale();
+
+ layout();
+ redraw();
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.swt.widgets.Widget#dispose()
+ */
+ @Override
+ public void dispose()
+ {
+ if (androidInput != null)
+ {
+ androidInput.dispose();
+ }
+
+ if (currentSkinImage != null)
+ {
+ currentSkinImage.dispose();
+ }
+
+ Layout layout = getLayout();
+ if (layout instanceof AndroidSkinLayout)
+ {
+ ((AndroidSkinLayout) layout).dispose();
+ }
+
+ skin = null;
+ currentKey = null;
+ keyListener = null;
+ mainDisplayMouseListener = null;
+ mainDisplayMouseMoveListener = null;
+
+ if (!Platform.getOS().equals(Platform.OS_MACOSX))
+ {
+ long hnd = androidInstance.getWindowHandle();
+ if (hnd > 0)
+ {
+ NativeUIUtils.showWindow(hnd);
+ NativeUIUtils.restoreWindow(hnd);
+ }
+
+ //Force update on redrawing
+ androidInstance.setWindowHandle(0);
+ }
+
+ super.dispose();
+ }
+
+ /**
+ * Sets the zoom factor to use in the instance
+ */
+ public void applyZoomFactor()
+ {
+ if (getZoomFactor() == 0)
+ {
+ scrollBarsUsed = false;
+ getVerticalBar().setEnabled(false);
+ getHorizontalBar().setEnabled(false);
+ }
+ else
+ {
+ scrollBarsUsed = true;
+ getVerticalBar().setEnabled(true);
+ getHorizontalBar().setEnabled(true);
+ }
+
+ // Resets translation
+ displayRectangle.x = 0;
+ displayRectangle.y = 0;
+
+ redrawReleasedImage();
+
+ }
+
+ /**
+ * Sets the flip/slide status of the phone
+ */
+ private void redrawReleasedImage()
+ {
+ if (currentSkinImage != null)
+ {
+ ImageData imageData = getImageData(false, false);
+ setSkinImage(imageData, null, false);
+ layout();
+ redraw();
+ }
+
+ if (scrollBarsUsed)
+ {
+ synchronizeScrollBars();
+ }
+ }
+
+ /**
+ * Performs the skin draw operation.
+ *
+ * @param gcUsedToDraw
+ * The gc object associated with the skin composite that is used
+ * to draw the images
+ */
+ private void drawSkin(GC gcUsedToDraw, IAndroidKey changedKey)
+ {
+ if (currentSkinImage == null)
+ {
+ IAndroidEmulatorInstance instance = UIHelper.getInstanceAssociatedToControl(this);
+ ImageData initialSkinImage = getImageData(false, false);
+ setSkinImage(initialSkinImage, null, false);
+ applyLayout(instance.getCurrentLayout());
+
+ if (scrollBarsUsed)
+ {
+ synchronizeScrollBars();
+ }
+ }
+
+ if (displayRectangle != null)
+ {
+ int srcXPos, srcYPos, srcWidth, srcHeight;
+ int destXPos, destYPos, destWidth, destHeight;
+ if (changedKey == null)
+ {
+ srcXPos = displayRectangle.x;
+ srcYPos = displayRectangle.y;
+ srcWidth = displayRectangle.width;
+ srcHeight = displayRectangle.height;
+ destXPos = 0;
+ destYPos = 0;
+ destWidth = Math.min(currentSkinImage.getImageData().width, displayRectangle.width);
+ destHeight =
+ Math.min(currentSkinImage.getImageData().height, displayRectangle.height);
+ }
+ else
+ {
+ srcXPos =
+ ((int) (changedKey.getKeyArea().x > 0 ? changedKey.getKeyArea().x * zoomFactor
+ : 0));
+ srcYPos =
+ ((int) (changedKey.getKeyArea().y > 0 ? changedKey.getKeyArea().y * zoomFactor
+ : 0));
+ srcWidth = ((int) (changedKey.getKeyArea().width * zoomFactor));
+ srcHeight = ((int) (changedKey.getKeyArea().height * zoomFactor));
+ destXPos = srcXPos - displayRectangle.x;
+ destYPos = srcYPos - displayRectangle.y;
+ destWidth = srcWidth;
+ destHeight = srcHeight;
+ }
+
+ gcUsedToDraw.drawImage(currentSkinImage, srcXPos, srcYPos, srcWidth, srcHeight,
+ destXPos, destYPos, destWidth, destHeight);
+ }
+ }
+
+ /**
+ * Loads a new screen image to the currentSkin attribute. This action
+ * updates the skin image that is drawn as skin
+ *
+ * @param imageToSet
+ * The new skin pixel data, as retrieved from the skin plugin
+ * @param changedKey
+ * The key that has changed, if any. If one if provided, only the key area should be redrawn.
+ * Can be <code>null</code>.
+ * @param forceDraw
+ * true if a draw is needed after setting the new image; false if replacing skin image only
+ * will be performed.
+ */
+ private void setSkinImage(ImageData imageToSet, IAndroidKey changedKey, boolean forceDraw)
+ {
+
+ recalculateZoomFactor();
+
+ if (imageToSet != null)
+ {
+ if (currentSkinImage != null)
+ {
+ currentSkinImage.dispose();
+ }
+
+ // Scales the chosen image and sets to currentSkin attribute
+ //
+ // NOTE: width and height cannot be equal to MINIMUM_ZOOM_FACTOR,
+ // because this
+ // will raise an IllegalArgumentException when constructing the
+ // Image object
+ int width =
+ (zoomFactor == MINIMUM_ZOOM_FACTOR ? 1 : (int) (imageToSet.width * zoomFactor));
+ int height =
+ (zoomFactor == MINIMUM_ZOOM_FACTOR ? 1 : (int) (imageToSet.height * zoomFactor));
+ currentSkinImage = new Image(getDisplay(), imageToSet.scaledTo(width, height));
+
+ // It only makes sense to reset the translation if the skin image is really being changed
+ // It will happen if we set a image data without specifying a changed key
+ if (changedKey == null)
+ {
+ displayRectangle.x = 0;
+ displayRectangle.y = 0;
+ }
+
+ if (forceDraw)
+ {
+ layout();
+
+ GC gc = new GC(this);
+ drawSkin(gc, changedKey);
+ gc.dispose();
+ }
+ }
+ else
+ {
+ info("It was requested to set a skin image that was null. Operation aborted.");
+ }
+ }
+
+ /**
+ * This method is responsible to set the scroll bar attributes so that they
+ * reflect the size of the current image at the current zoom factor
+ */
+ private void synchronizeScrollBars()
+ {
+ // Syncronizing only makes sense if there is a skin being drawn
+ if (currentSkinImage != null)
+ {
+
+ // Retrieves the current image and client area sizes
+ Rectangle imageBound = currentSkinImage.getBounds();
+ int cw = getClientArea().width;
+ int ch = getClientArea().height;
+
+ // Updates horizontal scroll bar attributes
+ ScrollBar horizontal = getHorizontalBar();
+ horizontal.setIncrement((cw / 100));
+ horizontal.setPageIncrement((cw / 2));
+ horizontal.setMaximum(imageBound.width);
+ horizontal.setThumb(cw);
+ horizontal.setSelection(displayRectangle.x);
+
+ // Updates vertical scroll bar attributes
+ ScrollBar vertical = getVerticalBar();
+ vertical.setIncrement((ch / 100));
+ vertical.setPageIncrement((ch / 2));
+ vertical.setMaximum(imageBound.height);
+ vertical.setThumb(ch);
+ vertical.setSelection(displayRectangle.y);
+
+ if (horizontal.getMaximum() > cw) // Image is wider than client area
+ {
+ horizontal.setEnabled(true);
+ }
+ else
+ {
+ horizontal.setEnabled(false);
+ }
+
+ if (vertical.getMaximum() > ch) // Image is wider than client area
+ {
+ vertical.setEnabled(true);
+ }
+ else
+ {
+ vertical.setEnabled(false);
+ }
+
+ }
+ }
+
+ /**
+ * Initialize the scroll bars This include: a) setting the initial enabled
+ * state b) adding the necessary listeners
+ */
+ private void initScrollBars()
+ {
+
+ ScrollBar horizontal = getHorizontalBar();
+ horizontal.setEnabled(false);
+ horizontal.addSelectionListener(new SelectionAdapter()
+ {
+ /**
+ * @see org.eclipse.swt.events.SelectionListener#widgetSelected(SelectionEvent)
+ */
+ @Override
+ public void widgetSelected(SelectionEvent event)
+ {
+ // Updates the translation
+ displayRectangle.x = ((ScrollBar) event.widget).getSelection();
+
+ // Update the UI
+ layout();
+ redraw();
+ }
+ });
+
+ ScrollBar vertical = getVerticalBar();
+ vertical.setEnabled(false);
+ vertical.addSelectionListener(new SelectionAdapter()
+ {
+ /**
+ * @see org.eclipse.swt.events.SelectionListener#widgetSelected(SelectionEvent)
+ */
+ @Override
+ public void widgetSelected(SelectionEvent event)
+ {
+ // Updates the translation
+ displayRectangle.y = ((ScrollBar) event.widget).getSelection();
+
+ // Update the UI
+ layout();
+ redraw();
+ }
+ });
+
+ debug("Initialized scroll bars");
+ }
+
+ /**
+ * This method retrieves the key that is placed at the given x,y
+ * coordinates, considering the flip status
+ *
+ * @param x
+ * The X coordinate to use at key lookup
+ * @param y
+ * The Y coordinate to use at key lookup
+ *
+ * @return The key placed at the given coordinate, or null if none is found
+ */
+ private IAndroidKey getSkinKey(int x, int y)
+ {
+ IAndroidKey keyToReturn = null;
+ IAndroidEmulatorInstance instance = UIHelper.getInstanceAssociatedToControl(this);
+
+ Collection<IAndroidKey> keyAreas = skin.getKeyDataCollection(instance.getCurrentLayout());
+ if (keyAreas != null)
+ {
+ for (IAndroidKey key : keyAreas)
+ {
+ if (key.isInsideKey(x, y))
+ {
+ if (key instanceof AndroidPressKey)
+ {
+ AndroidPressKey defaultKeyData = (AndroidPressKey) key;
+
+ //if (!defaultKeyData.isFlipSlideValid(instance.isFlipSlideClosed()))
+ if (!defaultKeyData.isFlipSlideValid(false))
+ {
+ continue;
+ }
+
+ }
+
+ keyToReturn = key;
+ break;
+ }
+ }
+ }
+
+ return keyToReturn;
+ }
+
+ /**
+ * Retrieves an image data. If it has never been used at the current
+ * session, loads it from skin. Released image is retrieved if both parameters
+ * are false.
+ *
+ * @param isPressed
+ * true if the image to be retrieved is the pressed image
+ * @param isEnter
+ * true if the image to be retrieved is the enter image. It only has effect if
+ * isPressed == false
+ *
+ * @return An image data containing the desired image pixels
+ */
+ private ImageData getImageData(boolean isPressed, boolean isEnter)
+ {
+
+ ImageData imageData = null;
+
+ IAndroidEmulatorInstance instance = UIHelper.getInstanceAssociatedToControl(this);
+
+ try
+ {
+ if (isPressed)
+ {
+ imageData = skin.getPressedImageData(instance.getCurrentLayout());
+
+ }
+ else
+ {
+ if (isEnter)
+ {
+ imageData = skin.getEnterImageData(instance.getCurrentLayout());
+
+ }
+ else
+ {
+ imageData = skin.getReleasedImageData(instance.getCurrentLayout());
+
+ }
+ }
+ }
+ catch (SkinException e)
+ {
+ error("The image requested from skin could not be retrieved. isPressed=" + isPressed
+ + "; message=" + e.getMessage());
+ EclipseUtils.showErrorDialog(e);
+ error("The skin could not provide an important resource. Stopping the instance");
+ try
+ {
+ instance.stop(true);
+ }
+ catch (InstanceStopException e1)
+ {
+ error("Error while running service for stopping virtual machine");
+ EclipseUtils.showErrorDialog(EmulatorNLS.GEN_Error,
+ EmulatorNLS.EXC_General_CannotRunStopService);
+ }
+ }
+
+ return imageData;
+ }
+
+ /**
+ * Builds an image data that is based on the released image but has the
+ * pressed/enter key area painted with data from the pressed/enter image
+ *
+ * @param key
+ * The pressed key
+ * @param isEnter
+ * Whether the image being retrieved will be used for enter or pressed
+ *
+ * @return An image data built the way described at method description
+ */
+ private ImageData getKeyImageData(IAndroidKey key, boolean isEnter)
+ {
+
+ ImageData resultingImage;
+ ImageData releasedImage = getImageData(false, false);
+ ImageData keyImage = isEnter ? getImageData(false, true) : getImageData(true, false);
+
+ resultingImage = (ImageData) releasedImage.clone();
+
+ Rectangle keyArea = key.getKeyArea();
+ int resultingImageSize = resultingImage.width * keyArea.height;
+ int[] keyPixelBuffer = new int[resultingImageSize];
+
+ int startY = keyArea.y < 0 ? 0 : keyArea.y;
+
+ keyImage.getPixels(0, startY, resultingImageSize, keyPixelBuffer, 0);
+
+ for (int line = 0; line < keyArea.height; line++)
+ {
+ int pos = (line * resultingImage.width) + Math.abs(keyArea.x);
+ int startX = Math.abs(keyArea.x);
+ startY = (keyArea.y < 0 ? 0 : keyArea.y) + line;
+ if (startY < 0)
+ {
+ continue;
+ }
+
+ int putWidth = keyArea.x < 0 ? keyArea.width - startX : keyArea.width;
+ resultingImage.setPixels(startX, startY, putWidth, keyPixelBuffer, pos);
+ }
+
+ return resultingImage;
+ }
+
+ /**
+ * This method is called when a mouse selection needs to be canceled.
+ * Pressing the right button, releasing the left button or leaving the key
+ * area are examples of typical conditions.
+ */
+ private void cancelMouseSelection()
+ {
+ // If the mouse timer is different from null, that means that a key is
+ // pressed
+ // This check is important so that event messages are not sent by
+ // mistake
+
+ if (currentKey != null)
+ {
+
+ ImageData newImageData = getImageData(false, true);
+ setSkinImage(newImageData, currentKey, true);
+ androidInput.sendClick(currentKey.getKeysym(), false);
+ }
+ }
+
+ private void changeCurrentKey(IAndroidKey newKey)
+ {
+ // The following actions are executed only if the key has changed since the last
+ // time a mouse move event has happened.
+ ImageData newImage;
+
+ if (currentKey != null)
+ {
+ // If currentKey is different from null, we know that the mouse cursor has
+ // left the area defined by currentKey. That is because from the previous
+ // if clause we know that the key has changed, and currentKey attribute
+ // state is out-dated until we reach the "currentKey = keyData" statement.
+ // In this case, we must draw the RELEASED image version of the key at the
+ // key location
+ newImage = getImageData(false, false);
+ setSkinImage(newImage, currentKey, true);
+ setToolTipText(null);
+ }
+
+ if (newKey != null)
+ {
+ // If keyData is different from null, we know that the mouse cursor has
+ // entered the area defined by keyData.
+ // In this case, we must draw the ENTER image version of the key at the
+ // key location
+ newImage = getKeyImageData(newKey, true);
+ setSkinImage(newImage, newKey, true);
+ setToolTipText(newKey.getToolTip());
+ }
+ currentKey = newKey;
+ }
+
+ /**
+ * Retrieves the display rectangle defined at the current moment.
+ *
+ * @return The display rectangle
+ */
+ Rectangle getDisplayRectangle()
+ {
+ return displayRectangle;
+ }
+
+ /**
+ * Updates the size and location of the display rectangle, based on the
+ * current attributes of the skin (such as skin size and zoom factor) and
+ * view size
+ */
+ void updateDisplayRectangle()
+ {
+ // Updating the display rectangle only makes sense if we have a skin
+ // being drawn
+ if (currentSkinImage != null)
+ {
+
+ // Collect the variables used in computation
+ //
+ // - clientAreaWidth, clientAreaHeight: dimensions of the view,
+ // measured from
+ // the upper left corner point (0,0) to the lower right corner point
+ // (width, height)
+ // - currentSkinWidth, currentSkinHeight: dimensions of the skin
+ // picture, already scaled
+ // by the zoom factor
+ int clientAreaWidth = getClientArea().width;
+ int clientAreaHeight = getClientArea().height;
+ int currentSkinHeight = currentSkinImage.getImageData().height;
+ int currentSkinWidth = currentSkinImage.getImageData().width;
+
+ // Updates the display rectangle y and height
+ //
+ // FIRST STEP: determine the position of the rectangle's y
+ // coordinate.
+ // - It starts by calculating if there is any blank area at the
+ // bottom
+ // of the view.
+ // - If there is blank space (blankY > 0) then calculate which
+ // point,
+ // if set as the display rectangle's y coordinate, would make the
+ // blank
+ // space to disappear. Store this as a candidate Y.
+ // - Check if the candidate Y is valid. If not valid (candidateY <
+ // 0)
+ // then use the image origin as the final y coordinate
+ //
+ // SECOND STEP: determine the width dimension of the rectangle
+ // - It starts by calculating which would be the coordinate of the
+ // lower point, assuming that the image is big enough to contain
+ // that
+ // coordinate. That value (vEdge) is the sum of the Y coordinate and
+ // the view height.
+ // - If vEdge is bigger than the current view height, that means
+ // that the image will occupy part of the view. Itis necessary to
+ // make the rectangle fit in the skin image by making it smaller in
+ // height than the view height itself.
+ // - If vEdge is smaller than the current view height, that means
+ // that
+ // the image will not fit in the view. The solution is to make the
+ // display rectangle height the same size as the view height. In
+ // this
+ // second case, the display rectangle will fit in the view height,
+ // but
+ // will not be bigger than the skin image height itself
+ int blankY = clientAreaHeight - (currentSkinHeight - displayRectangle.y);
+ if (blankY > 0)
+ {
+ int candidateY = displayRectangle.y - blankY;
+ if (candidateY > 0)
+ {
+ displayRectangle.y = candidateY;
+ }
+ else
+ {
+ displayRectangle.y = 0;
+ }
+ }
+ int vEdge = displayRectangle.y + clientAreaHeight;
+ if (vEdge > currentSkinHeight)
+ {
+ displayRectangle.height = currentSkinHeight - displayRectangle.y;
+ }
+ else
+ {
+ displayRectangle.height = clientAreaHeight;
+ }
+
+ // Updates the display rectangle x and width
+ // NOTE: a similar logic to the previous one was applied in this
+ // case
+ int blankX = clientAreaWidth - (currentSkinWidth - displayRectangle.x);
+ if (blankX > 0)
+ {
+ int candidateX = displayRectangle.x - blankX;
+ if (candidateX > 0)
+ {
+ displayRectangle.x = candidateX;
+ }
+ else
+ {
+ displayRectangle.x = 0;
+ }
+ }
+ int hEdge = displayRectangle.x + clientAreaWidth;
+ if (hEdge > currentSkinWidth)
+ {
+ displayRectangle.width = currentSkinWidth - displayRectangle.x;
+ }
+ else
+ {
+ displayRectangle.width = clientAreaWidth;
+ }
+
+ }
+ }
+
+ /**
+ * Recalculates the zoom factor. This is necessary when the screen or image
+ * dimensions change.
+ */
+ void recalculateZoomFactor()
+ {
+
+ if (!scrollBarsUsed)
+ {
+ // Compute new zoom factor if the zoom mode is "Fit to Window"
+ Rectangle clientArea = getClientArea();
+ if ((clientArea.width == 0) || (clientArea.height == 0))
+ {
+ // zoom factor cannot be zero, otherwise an
+ // IllegalArgumentException
+ // is raised in some SWT methods
+ setZoomFactor(MINIMUM_ZOOM_FACTOR);
+ }
+ else
+ {
+ ImageData currentSkin = getImageData(false, false);
+ double widthRatio = (double) (clientArea.width) / currentSkin.width;
+ double heightRatio = (double) (clientArea.height) / currentSkin.height;
+ setZoomFactor(Math.min(widthRatio, heightRatio));
+ }
+ }
+
+ }
+
+ /**
+ * Gets the current zoom factor.
+ *
+ * @return the zoom factor
+ */
+ public double getZoomFactor()
+ {
+ return zoomFactor;
+ }
+
+ /**
+ * Checks if the current zoom configuration is "fit to screen";
+ * @return
+ */
+ public boolean isFitToWindowSelected()
+ {
+ return !scrollBarsUsed;
+ }
+
+ /**
+ * Sets the zoom factor.
+ *
+ * @param zoom
+ * the zoom factor
+ */
+ public void setZoomFactor(double zoom)
+ {
+ zoomFactor = zoom;
+ }
+
+ /**
+ * Gets the listener that handles SWT key pressing and releasing events.
+ *
+ * @return the KeyListener object
+ */
+ public KeyListener getKeyListener()
+ {
+ return keyListener;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.emulator.ui.controls.IAndroidComposite#getMouseListener()
+ */
+ public MouseListener getMouseListener()
+ {
+ return mainDisplayMouseListener;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.emulator.ui.controls.IAndroidComposite#getMouseMoveListener()
+ */
+ public MouseMoveListener getMouseMoveListener()
+ {
+ return mainDisplayMouseMoveListener;
+ }
+
+ private void createListenersForMainDisplay()
+ {
+ // create listener to handle keyboard key pressing highlighting the key
+ // in the skin.
+ keyListener = new KeyAdapter()
+ {
+
+ @Override
+ public void keyPressed(KeyEvent arg0)
+ {
+
+ int keyCode = arg0.keyCode;
+
+ if (keyCode == SWT.CTRL)
+ {
+ ctrlPressed = true;
+ }
+ else
+ {
+ if (keyCode == SWT.ARROW_DOWN || keyCode == SWT.ARROW_LEFT
+ || keyCode == SWT.ARROW_RIGHT || keyCode == SWT.ARROW_UP)
+ {
+ int dpadRotation = skin.getDpadRotation(androidInstance.getCurrentLayout());
+ keyCode = getRotatedKeyCode(keyCode, dpadRotation);
+ }
+ // send message to emulator
+ androidInput.sendKey(arg0.character, keyCode, skin.getKeyCodes());
+ }
+
+ }
+
+ @Override
+ public void keyReleased(KeyEvent arg0)
+ {
+ int keyCode = arg0.keyCode;
+
+ if (keyCode == SWT.CTRL)
+ {
+ ctrlPressed = false;
+ }
+ }
+
+ };
+
+ mainDisplayMouseMoveListener = new MouseMoveListener()
+ {
+ /**
+ * @see org.eclipse.swt.events.MouseMoveListener#mouseMove(MouseEvent)
+ */
+ public void mouseMove(MouseEvent e)
+ {
+ if (isMouseMainDisplayLeftButtonPressed)
+ {
+ UIHelper.ajustCoordinates(e, SkinComposite.this);
+ androidInput.sendMouseMove((int) (e.x / embeddedViewScale),
+ (int) (e.y / embeddedViewScale));
+ }
+ }
+ };
+
+ mainDisplayMouseListener = new MouseAdapter()
+ {
+ /**
+ * @see org.eclipse.swt.events.MouseListener#mouseUp(MouseEvent)
+ */
+ @Override
+ public void mouseUp(MouseEvent e)
+ {
+ if (e.button == 1)
+ {
+ isMouseMainDisplayLeftButtonPressed = false;
+
+ UIHelper.ajustCoordinates(e, SkinComposite.this);
+ androidInput.sendMouseUp((int) (e.x / embeddedViewScale),
+ (int) (e.y / embeddedViewScale));
+ }
+ }
+
+ /**
+ * @see org.eclipse.swt.events.MouseListener#mouseDown(MouseEvent)
+ */
+ @Override
+ public void mouseDown(MouseEvent e)
+ {
+ if (e.button == 1)
+ {
+ UIHelper.ajustCoordinates(e, SkinComposite.this);
+ androidInput.sendMouseDown((int) (e.x / embeddedViewScale),
+ (int) (e.y / embeddedViewScale));
+
+ isMouseMainDisplayLeftButtonPressed = true;
+ }
+ }
+ };
+ }
+
+ private int getRotatedKeyCode(int keyCode, int dpadRotation)
+ {
+ switch (dpadRotation % 4)
+ {
+ case 1:
+ switch (keyCode)
+ {
+ case SWT.ARROW_DOWN:
+ keyCode = SWT.ARROW_RIGHT;
+ break;
+ case SWT.ARROW_LEFT:
+ keyCode = SWT.ARROW_DOWN;
+ break;
+ case SWT.ARROW_RIGHT:
+ keyCode = SWT.ARROW_UP;
+ break;
+ case SWT.ARROW_UP:
+ keyCode = SWT.ARROW_LEFT;
+ break;
+ }
+ break;
+ case 2:
+ switch (keyCode)
+ {
+ case SWT.ARROW_DOWN:
+ keyCode = SWT.ARROW_UP;
+ break;
+ case SWT.ARROW_LEFT:
+ keyCode = SWT.ARROW_RIGHT;
+ break;
+ case SWT.ARROW_RIGHT:
+ keyCode = SWT.ARROW_LEFT;
+ break;
+ case SWT.ARROW_UP:
+ keyCode = SWT.ARROW_DOWN;
+ break;
+ }
+ break;
+ case 3:
+ switch (keyCode)
+ {
+ case SWT.ARROW_DOWN:
+ keyCode = SWT.ARROW_LEFT;
+ break;
+ case SWT.ARROW_LEFT:
+ keyCode = SWT.ARROW_UP;
+ break;
+ case SWT.ARROW_RIGHT:
+ keyCode = SWT.ARROW_DOWN;
+ break;
+ case SWT.ARROW_UP:
+ keyCode = SWT.ARROW_RIGHT;
+ break;
+ }
+ break;
+ default:
+ //Does nothing, no rotation needed.
+ break;
+ }
+ return keyCode;
+ }
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/handlers/AbstractZoomHandler.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/handlers/AbstractZoomHandler.java
new file mode 100644
index 0000000..f6a49a9
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/handlers/AbstractZoomHandler.java
@@ -0,0 +1,146 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.emulator.ui.handlers;
+
+import static com.motorola.studio.android.common.log.StudioLogger.error;
+import static com.motorola.studio.android.common.log.StudioLogger.info;
+
+import java.util.Map;
+
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.ui.IViewPart;
+import org.eclipse.ui.commands.IElementUpdater;
+import org.eclipse.ui.menus.UIElement;
+
+import com.motorola.studio.android.common.utilities.EclipseUtils;
+import com.motorola.studio.android.emulator.core.model.IAndroidEmulatorInstance;
+import com.motorola.studio.android.emulator.i18n.EmulatorNLS;
+import com.motorola.studio.android.emulator.ui.view.AbstractAndroidView;
+import com.motorola.studio.android.emulator.ui.view.AndroidViewData;
+
+/**
+ * DESCRIPTION:
+ * This class is a handler for the zoom actions
+ *
+ * RESPONSIBILITY:
+ * Execute the zoom operations on demand
+ *
+ * COLABORATORS:
+ * None.
+ *
+ * USAGE:
+ * This class is intended to be used by Eclipse only
+ */
+public abstract class AbstractZoomHandler extends AbstractHandler implements IHandlerConstants,
+ IElementUpdater
+{
+ /**
+ * @see org.eclipse.core.commands.IHandler#execute(ExecutionEvent)
+ */
+ public Object execute(ExecutionEvent event) throws ExecutionException
+ {
+ String viewId = event.getParameter(ACTIVE_VIEW_PARAMETER);
+ double zoomFactor = getZoomFactor(event.getParameters());
+
+ info("Setting zoom factor for " + viewId + " = " + zoomFactor);
+
+ IAndroidEmulatorInstance instance = AbstractAndroidView.getActiveInstance();
+ if (instance != null)
+ {
+ IViewPart viewPart = EclipseUtils.getActiveView(viewId);
+ if (viewPart instanceof AbstractAndroidView)
+ {
+ AbstractAndroidView view = (AbstractAndroidView) viewPart;
+ if (view.getZoomFactor(instance) != zoomFactor)
+ {
+ view.setZoomFactor(instance, zoomFactor);
+ view.updateActiveViewer();
+ }
+ }
+ }
+ else
+ {
+ error("The host being presented at the viewer is not available");
+ EclipseUtils.showErrorDialog(EmulatorNLS.GEN_Error,
+ EmulatorNLS.EXC_AbstractZoomHandler_InstanceNotFound);
+ }
+ return null;
+ }
+
+ /**
+ * When a different emulator tab have focus, then each of the zoom factor menu items need
+ * to update itself so that the radio selection continues at the correct place
+ *
+ * @see org.eclipse.ui.commands.IElementUpdater#updateElement(UIElement, Map)
+ */
+ @SuppressWarnings("rawtypes")
+ public void updateElement(UIElement element, Map parameters)
+ {
+ boolean checked = false;
+ IAndroidEmulatorInstance instance = AbstractAndroidView.getActiveInstance();
+ String viewId = (String) parameters.get(ACTIVE_VIEW_PARAMETER);
+
+ if ((instance != null) && (viewId != null))
+ {
+ IViewPart viewPart = EclipseUtils.getActiveView(viewId);
+ if (viewPart instanceof AbstractAndroidView)
+ {
+ AbstractAndroidView view = (AbstractAndroidView) viewPart;
+ AndroidViewData viewData = view.getViewData(instance);
+
+ double actualZoomFactor = view.getZoomFactor(instance);
+ if (testZoomFactor(viewData, parameters, actualZoomFactor))
+ {
+ checked = true;
+ }
+ }
+ }
+
+ element.setChecked(checked);
+ }
+
+ /**
+ * The zoom actions are enabled when a phone skin is being presented only
+ *
+ * @see org.eclipse.core.commands.IHandler#isEnabled()
+ */
+ @Override
+ public boolean isEnabled()
+ {
+ return AbstractAndroidView.getActiveInstance() != null;
+ }
+
+ /**
+ * Retrieves the zoom factor associated to this zoom handler instance
+ *
+ * @return The zoom factor, as defined in IEmulatorActionConstants class
+ */
+ @SuppressWarnings("rawtypes")
+ protected abstract double getZoomFactor(Map paramters);
+
+ /**
+ * Tests if the current zoom factor is the one handled by this zoom handler
+ *
+ * @param zoomFactor The active instance current zoom factor
+ *
+ * @return True if this handler handles the current zoom factor; false otherwise
+ */
+ @SuppressWarnings("rawtypes")
+ protected abstract boolean testZoomFactor(AndroidViewData viewData, Map parameters,
+ double zoomFactor);
+} \ No newline at end of file
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/handlers/ChangeEmulatorOrientationHandler.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/handlers/ChangeEmulatorOrientationHandler.java
new file mode 100644
index 0000000..6f6af4b
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/handlers/ChangeEmulatorOrientationHandler.java
@@ -0,0 +1,119 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.emulator.ui.handlers;
+
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.ui.IViewPart;
+
+import com.motorola.studio.android.common.utilities.EclipseUtils;
+import com.motorola.studio.android.emulator.core.model.IAndroidEmulatorInstance;
+import com.motorola.studio.android.emulator.core.skin.IAndroidSkin;
+import com.motorola.studio.android.emulator.ui.view.AbstractAndroidView;
+import com.motorola.studio.android.emulator.ui.view.AndroidView;
+import com.motorola.studio.android.nativeos.IDevicePropertiesOSConstants;
+import com.motorola.studio.android.nativeos.NativeUIUtils;
+
+/**
+ * This class executes the operation of setting an Emulator Layout with the Landscape orientation
+ **/
+public class ChangeEmulatorOrientationHandler extends AbstractHandler
+{
+
+ /**
+ * Sets a layout with the Landscape orientation for the active Emulator.
+ */
+ public Object execute(ExecutionEvent event)
+ {
+ IAndroidEmulatorInstance instance = AbstractAndroidView.getActiveInstance();
+
+ if (instance != null)
+ {
+ String viewId = event.getParameter(IHandlerConstants.ACTIVE_VIEW_PARAMETER);
+ if ((instance != null) && (viewId != null))
+ {
+ IViewPart viewPart = EclipseUtils.getActiveView(viewId);
+ if (viewPart instanceof AndroidView)
+ {
+ if ((instance.getProperties().getProperty(IDevicePropertiesOSConstants.useVnc,
+ NativeUIUtils.getDefaultUseVnc())).equals("true"))
+ {
+ String nextLayout =
+ AbstractAndroidView.getPreviousOrNextLayout(viewId,
+ AbstractAndroidView.LayoutOpp.NEXT);
+ AbstractAndroidView.changeLayout(nextLayout);
+
+ IAndroidSkin skin = ((AbstractAndroidView) viewPart).getSkin(instance);
+ String cmd = skin.getLayoutScreenCommand(instance.getCurrentLayout());
+ instance.changeOrientation(cmd);
+ }
+ else
+ {
+ AndroidView androidView = (AndroidView) viewPart;
+ androidView.changeToNextLayout();
+ }
+ }
+ }
+
+ }
+
+ return null;
+ }
+
+ /**
+ * Determines when the Landscape command can be executed.
+ */
+ @Override
+ public boolean isEnabled()
+ {
+ return AbstractAndroidView.getActiveInstance() != null;
+ }
+
+ // /**
+ // * Determines what layout is the current one and checks it
+ // */
+ // @SuppressWarnings("unchecked")
+ // public void updateElement(UIElement element, Map parameters)
+ // {
+ // boolean checked = false;
+ //
+ // IAndroidEmulatorInstance instance = AbstractAndroidView.getActiveInstance();
+ // String viewId = (String) parameters.get(IHandlerConstants.ACTIVE_VIEW_PARAMETER);
+ //
+ // if ((instance != null) && (viewId != null))
+ // {
+ // IViewPart viewPart = EclipseUtils.getActiveView(viewId);
+ // if (viewPart instanceof AbstractAndroidView)
+ // {
+ // AbstractAndroidView view = (AbstractAndroidView) viewPart;
+ // IAndroidSkin skin = view.getSkin(instance);
+ //
+ // if (skin != null)
+ // {
+ // String currentLayout = instance.getCurrentLayout();
+ // String setLayoutName =
+ // (String) parameters.get(IHandlerConstants.LAYOUT_TO_SET_PARAMETER);
+ // if ((setLayoutName != null) && (setLayoutName.equals(currentLayout)))
+ // {
+ // checked = true;
+ // }
+ // }
+ // }
+ // }
+ //
+ // element.setChecked(checked);
+ // }
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/handlers/ChangeZoomHandler.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/handlers/ChangeZoomHandler.java
new file mode 100644
index 0000000..e71c062
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/handlers/ChangeZoomHandler.java
@@ -0,0 +1,80 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.emulator.ui.handlers;
+
+import java.util.Map;
+
+import com.motorola.studio.android.emulator.ui.controls.IAndroidComposite;
+import com.motorola.studio.android.emulator.ui.view.AndroidViewData;
+
+public class ChangeZoomHandler extends AbstractZoomHandler
+{
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.emulator.ui.handlers.AbstractZoomHandler#getZoomFactor(java.util.Map)
+ */
+ @SuppressWarnings("rawtypes")
+ @Override
+ protected double getZoomFactor(Map parameter)
+ {
+ double zoomFactor = DEFAULT_ZOOM;
+ String factorString = (String) parameter.get(ZOOM_FACTOR_PARAMETER);
+ try
+ {
+ zoomFactor = Double.parseDouble(factorString);
+ }
+ catch (Exception e)
+ {
+ // Do nothing
+ // The parameter can always be parsed
+ }
+ return zoomFactor;
+ }
+
+ /**
+ * Tests if the current zoom factor is the one handled by this zoom handler
+ *
+ * @param zoomFactor The active instance current zoom factor
+ *
+ * @return True if this handler handles the current zoom factor; false otherwise
+ */
+ @SuppressWarnings("rawtypes")
+ @Override
+ protected boolean testZoomFactor(AndroidViewData viewData, Map parameters, double zoomFactor)
+ {
+ boolean testResult = false;
+ double expectedZoomFactor = getZoomFactor(parameters);
+ if (expectedZoomFactor == zoomFactor)
+ {
+ testResult = true;
+ }
+
+ if (expectedZoomFactor == ZOOM_FIT)
+ {
+ if (viewData != null)
+ {
+ IAndroidComposite composite = viewData.getComposite();
+
+ if (composite.isFitToWindowSelected())
+ {
+ testResult = true;
+ }
+ }
+ }
+
+ return testResult;
+ }
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/handlers/IHandlerConstants.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/handlers/IHandlerConstants.java
new file mode 100644
index 0000000..9a414fc
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/handlers/IHandlerConstants.java
@@ -0,0 +1,69 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.emulator.ui.handlers;
+
+/**
+ * This interface contain constants that are used by the action handlers
+ */
+public interface IHandlerConstants
+{
+ /**
+ * Constant used in plugin.xml file to identify change orientation command
+ */
+ String CHANGE_EMULATOR_ORIENTATION_COMMAND =
+ "com.motorola.studio.android.emulator.ui.change.layout";
+
+ /**
+ * Constant used in plugin.xml file to identify change zoom command
+ */
+ String CHANGE_EMULATOR_ZOOM_COMMAND = "com.motorola.studio.android.emulator.ui.change.zoom";
+
+ /**
+ * Parameter that determines to which view the command will be applied
+ */
+ String ACTIVE_VIEW_PARAMETER = "activeViewId";
+
+ /**
+ * Parameter to determines the zoom fact to be set in the
+ */
+ String ZOOM_FACTOR_PARAMETER = "zoomFactor";
+
+ /**
+ * Parameter to determine the increment/decrement to be applied in the current zoonFactor
+ */
+ String ZOOM_CHANGE_FACTOR_PARAMETER = "zoomChangeFactor";
+
+ /**
+ * Parameter to determine the emulator display orientation (next, previous, setlayout) to be set.
+ */
+ String EMULATOR_ORIENTATION_PARAMETER = "emulatorOrientation";
+
+ /**
+ * Parameter to determine the layout to be set, if EMULATOR_ORIENTATION_PARAMETER value is setlayout
+ */
+ String LAYOUT_TO_SET_PARAMETER = "layoutToSet";
+
+ // Zoom constants
+ double ZOOM_FIT = 0;
+
+ double MINIMUM_ZOOM = 0.25;
+
+ double MAXIMUM_ZOOM = 2.0;
+
+ double DEFAULT_ZOOM = 1.00;
+
+ double STEP_ZOOM = 0.25;
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/handlers/ShowViewHandler.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/handlers/ShowViewHandler.java
new file mode 100644
index 0000000..a2a15e2
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/handlers/ShowViewHandler.java
@@ -0,0 +1,79 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.emulator.ui.handlers;
+
+import static com.motorola.studio.android.common.log.StudioLogger.error;
+import static com.motorola.studio.android.common.log.StudioLogger.info;
+
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.ui.PartInitException;
+
+import com.motorola.studio.android.common.utilities.EclipseUtils;
+import com.motorola.studio.android.emulator.i18n.EmulatorNLS;
+import com.motorola.studio.android.emulator.ui.view.AndroidView;
+import com.motorola.studio.android.emulator.ui.view.MainDisplayView;
+
+/**
+ * DESCRIPTION:
+ * This class is a handler for the show view actions
+ *
+ * RESPONSIBILITY:
+ * Execute the show view operations on demand
+ *
+ * COLABORATORS:
+ * None.
+ *
+ * USAGE:
+ * This class is intended to be used by Eclipse only
+ */
+public class ShowViewHandler extends AbstractHandler implements IHandlerConstants
+{
+ /**
+ * @see org.eclipse.core.commands.IHandler#execute(ExecutionEvent)
+ */
+ public Object execute(ExecutionEvent event) throws ExecutionException
+ {
+ String viewId = event.getParameter(IHandlerConstants.ACTIVE_VIEW_PARAMETER);
+
+ try
+ {
+ if (viewId.equals(AndroidView.ANDROID_VIEW_ID))
+ {
+ info("Showing Main Display View by command execution");
+ EclipseUtils.showView(MainDisplayView.EMULATOR_MAIN_DISPLAY_VIEW_ID);
+ }
+ else if (viewId.equals(MainDisplayView.EMULATOR_MAIN_DISPLAY_VIEW_ID))
+ {
+ info("Showing Android View by command execution");
+ EclipseUtils.showView(AndroidView.ANDROID_VIEW_ID);
+ }
+ else
+ {
+ info("User tried to open an unknown view. Ignoring the action.");
+ }
+ }
+ catch (PartInitException e)
+ {
+ error("The views that were requested to be opened are not accessible programatically");
+ EclipseUtils.showErrorDialog(EmulatorNLS.GEN_Error,
+ EmulatorNLS.EXC_AndroidView_ViewNotFound);
+ }
+
+ return null;
+ }
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/handlers/ZoomInOutHandler.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/handlers/ZoomInOutHandler.java
new file mode 100644
index 0000000..a0d2df4
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/handlers/ZoomInOutHandler.java
@@ -0,0 +1,90 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.emulator.ui.handlers;
+
+import java.util.Map;
+
+import org.eclipse.ui.IViewPart;
+
+import com.motorola.studio.android.common.utilities.EclipseUtils;
+import com.motorola.studio.android.emulator.core.model.IAndroidEmulatorInstance;
+import com.motorola.studio.android.emulator.ui.view.AbstractAndroidView;
+import com.motorola.studio.android.emulator.ui.view.AndroidViewData;
+
+/**
+ * This class is responsible for increasing the default zoom factor
+ * to the current viewer of the Main Display View.
+ */
+public class ZoomInOutHandler extends AbstractZoomHandler
+{
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.emulator.ui.handlers.AbstractZoomHandler#getZoomFactor(java.util.Map)
+ */
+ @SuppressWarnings("rawtypes")
+ @Override
+ protected double getZoomFactor(Map parameters)
+ {
+ double zoomFactor = DEFAULT_ZOOM;
+ IAndroidEmulatorInstance instance = AbstractAndroidView.getActiveInstance();
+ String viewId = (String) parameters.get(ACTIVE_VIEW_PARAMETER);
+ String changeFactorString = (String) parameters.get(ZOOM_CHANGE_FACTOR_PARAMETER);
+
+ if ((instance != null) && (viewId != null) && (changeFactorString != null))
+ {
+ IViewPart viewPart = EclipseUtils.getActiveView(viewId);
+ if (viewPart instanceof AbstractAndroidView)
+ {
+ AbstractAndroidView view = (AbstractAndroidView) viewPart;
+ double currentZoomFactor = view.getZoomFactor(instance);
+
+ try
+ {
+ double changeZoomFactor = Double.parseDouble(changeFactorString);
+ zoomFactor = currentZoomFactor + changeZoomFactor;
+ }
+ catch (Exception e)
+ {
+ zoomFactor = currentZoomFactor;
+ }
+ }
+ }
+
+ if (zoomFactor < MINIMUM_ZOOM)
+ {
+ zoomFactor = MINIMUM_ZOOM;
+ }
+ else if (zoomFactor > MAXIMUM_ZOOM)
+ {
+ zoomFactor = MAXIMUM_ZOOM;
+ }
+
+ return zoomFactor;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.emulator.ui.handlers.AbstractZoomHandler#testZoomFactor(com.motorola.studio.android.emulator.ui.view.AndroidViewData, java.util.Map, double)
+ */
+ @SuppressWarnings("rawtypes")
+ @Override
+ protected boolean testZoomFactor(AndroidViewData viewData, Map parameters, double zoomFactor)
+ {
+ // It does not make sense to set as checked any of the UI Elements that use the zoom in/out command
+ // Those elements do not represent states, but actions.
+ return false;
+ }
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/perspective/AndroidEmulatorPerspective.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/perspective/AndroidEmulatorPerspective.java
new file mode 100644
index 0000000..e1ba398
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/perspective/AndroidEmulatorPerspective.java
@@ -0,0 +1,254 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.emulator.ui.perspective;
+
+import static com.motorola.studio.android.common.log.StudioLogger.error;
+import static com.motorola.studio.android.common.log.StudioLogger.warn;
+
+import java.util.Collection;
+import java.util.TreeSet;
+
+import org.eclipse.ui.IFolderLayout;
+import org.eclipse.ui.IPageLayout;
+import org.eclipse.ui.IPerspectiveFactory;
+import org.eclipse.ui.PartInitException;
+
+import com.motorola.studio.android.common.utilities.EclipseUtils;
+import com.motorola.studio.android.emulator.ui.perspective.extension.AndroidPerspectiveExtensionBean;
+import com.motorola.studio.android.emulator.ui.perspective.extension.AndroidPerspectiveExtensionBean.PerspectiveAreas;
+import com.motorola.studio.android.emulator.ui.perspective.extension.AndroidPerspectiveExtensionReader;
+import com.motorola.studio.android.emulator.ui.perspective.extension.IAndroidPerspectiveExtensionConstants;
+import com.motorola.studio.android.emulator.ui.view.AndroidView;
+import com.motorola.studio.android.emulator.ui.view.MainDisplayView;
+
+/**
+ * DESCRIPTION:
+ * This class represents the Android Emulator perspective.
+ *
+ * RESPONSIBILITY:
+ * Create the Android Emulator perspective.
+ *
+ * COLABORATORS:
+ * None
+ *
+ * USAGE:
+ * This class is referenced by the plugin.xml file of this plugin.
+ */
+public class AndroidEmulatorPerspective implements IPerspectiveFactory
+{
+ private static String LAUNCH_COOLBAR_SHORTCUT = "org.eclipse.debug.ui.launchActionSet";
+
+ /**
+ * Creates the initial layout for a page.
+ *
+ * @param layout the page layout
+ *
+ * @see IPerspectiveFactory#createInitialLayout(IPageLayout)
+ */
+ public void createInitialLayout(IPageLayout layout)
+ {
+ // ---------------HOW THE PERSPECTIVE IS LAID OUT---------------
+ //
+ // The Android Perspective will be dynamically populated according to
+ // the contributions declared to it through the androidPerspectiveExtension
+ // extension point.
+ // The perspective consists of three areas:
+ // - the area where the Android Emulator View is placed, on the right side
+ // - the area where device management related views are placed, on the left side
+ // - the area for emulation views, on the middle
+ // The areas that are dynamically populated are the two last ones, and they will
+ // be referred to as 'dynamic areas' by methods.
+ //
+ // The first area is filled by the code on this method by itself, and the two other
+ // ones are filled depending on what is found for the extension point.
+ // The following drawing illustrates the position of each area on the workbench:
+ //
+ // ____________________________________
+ // | | | |
+ // | | | |
+ // | Dev | | Moto |
+ // | Mgt | | magx |
+ // | Views | Emu Views Area | Emu |
+ // | Area | | View |
+ // | | | Area |
+ // | | | |
+ // |_______|___________________|_______|
+ //
+ // Device Management views are placed atop of each other on their respective area.
+ // Emulation views are laid out depending on the number of views declared. If there
+ // is only one emulation view, it is placed occupying the entire emulation views area.
+ // If there are two emulation views, they divide the emulation views area in half and
+ // occupy the area from bottom to top, as seen on the following drawing:
+ //
+ // ____________________________________
+ // | | | |
+ // | | | |
+ // | Dev | Emu Views part 2 | Moto |
+ // | Mgt | | magx |
+ // | Views |___________________| Emu |
+ // | Area | | View |
+ // | | Emu Views part 1 | Area |
+ // | | | |
+ // |_______|___________________|_______|
+ //
+ // If there are three emulation views or more, they divide the emulation views area in
+ // three parts, which are occupied from bottom to top, as seen on the following drawing:
+ //
+ // ____________________________________
+ // | | | |
+ // | | Emu Views part 3 | |
+ // | Dev |___________________| Moto |
+ // | Mgt | | magx |
+ // | Views | Emu Views part 2 | Emu |
+ // | Area |___________________| View |
+ // | | | Area |
+ // | | Emu Views part 1 | |
+ // |_______|___________________|_______|
+ //
+ // If there are no device management or no emulation views (or both), their folders are
+ // always created so that the workbench is always divided into the correct areas for
+ // better user experience.
+ //
+ // -------------------------------------------------------------
+
+ addEmulatorView(layout);
+ addRunCoolbar(layout);
+ createAndPopulateDynamicAreas(layout);
+
+ // hide the editor area (not necessary on this perspective)
+ layout.setEditorAreaVisible(false);
+ }
+
+ private void addEmulatorView(IPageLayout layout)
+ {
+ // emulator view is a sticky view, no place holder is necessary (only open it)
+ try
+ {
+ EclipseUtils.showView(AndroidView.ANDROID_VIEW_ID);
+ }
+ catch (PartInitException e)
+ {
+ error("Unable to open Android Emulator View on Android Emulator Perspective.");
+ }
+
+ layout.addShowViewShortcut(AndroidView.ANDROID_VIEW_ID);
+ layout.addShowViewShortcut(MainDisplayView.EMULATOR_MAIN_DISPLAY_VIEW_ID);
+ }
+
+ private void addRunCoolbar(IPageLayout layout)
+ {
+ layout.addActionSet(LAUNCH_COOLBAR_SHORTCUT);
+ }
+
+ private void createAndPopulateDynamicAreas(IPageLayout layout)
+ {
+ // read the extensions for this perspective, as declared by the androidPerspectiveExtension
+ // extension point
+ Collection<AndroidPerspectiveExtensionBean> perspectiveExtensionBeans =
+ AndroidPerspectiveExtensionReader.readAndroidPerspectiveExtensions();
+
+ // the folder for placing device management related views
+ IFolderLayout devMgtArea = createDeviceMgtViewsArea(layout);
+
+ // collection of emuy area view ids for later placement
+ // it is alphabetically ordered (so that a group of defined
+ // views always open on the same location)
+ Collection<String> emuAreaViewIds = new TreeSet<String>();
+
+ for (AndroidPerspectiveExtensionBean extensionBean : perspectiveExtensionBeans)
+ {
+ if (PerspectiveAreas.DEVICE_MANAGEMENT_VIEWS_AREA.equals(extensionBean.getArea()))
+ {
+ // put dev mgt views atop of each other on the folder
+ devMgtArea.addView(extensionBean.getViewId());
+ }
+ else if (PerspectiveAreas.EMULATION_VIEWS_AREA.equals(extensionBean.getArea()))
+ {
+ // collect emu views for later placement
+ emuAreaViewIds.add(extensionBean.getViewId());
+ }
+ else
+ {
+ // in case of something not expected, log the problem
+ warn("View of id " + extensionBean.getViewId()
+ + " could not be added to Android Emulator perspective");
+ }
+ }
+
+ // the number of emu views, for defining the number of folders necessary
+ int numEmuViews = emuAreaViewIds.size();
+
+ // create the emu area folders, which will be at most size 3
+ IFolderLayout[] emuAreaFolders = createEmuArea(layout, numEmuViews);
+
+ // place the views on the correct folder by using the leftover of dividing the
+ // number of the current view by number of maximum folders (3)
+ int i = 0;
+ for (String viewId : emuAreaViewIds)
+ {
+ emuAreaFolders[i % 3].addView(viewId);
+ i++; // next view
+ }
+ }
+
+ private IFolderLayout createDeviceMgtViewsArea(IPageLayout layout)
+ {
+ return layout.createFolder(IAndroidPerspectiveExtensionConstants.ATT_AREA_DEVMGT_VALUE,
+ IPageLayout.LEFT, 0.30f, IPageLayout.ID_EDITOR_AREA);
+ }
+
+ private IFolderLayout[] createEmuArea(IPageLayout layout, int numEmuViews)
+ {
+ // at most 3 folders are necessary
+ IFolderLayout[] emuAreaFolders = new IFolderLayout[3];
+
+ // the number of divisions the emu views area will be divided into
+ int div = (numEmuViews >= 3 ? 3 : (numEmuViews == 2 ? 2 : 1));
+
+ // the ratio for the first folder to be placed (the bottom one, which may turn
+ // to be the only one if div is equal to 1)
+ float ratio = 1.00f / div;
+
+ // the bottom folder (#1) is always necessary
+ // for 3 folders, ratio = 0.33f; for 2 folders, ratio = 0.5f
+ emuAreaFolders[0] =
+ layout.createFolder("emuAreaBottom", IPageLayout.BOTTOM, ratio,
+ IPageLayout.ID_EDITOR_AREA);
+
+ // create folder #2 only if necessary
+ if (numEmuViews >= 2)
+ {
+ // adjust ratio depending on the number of folders in total:
+ // for 3 folders, ratio = 0.67f; for 2 folders, ratio = 0.5f
+ if (numEmuViews > 2)
+ {
+ ratio = 2 * ratio;
+ }
+ emuAreaFolders[1] =
+ layout.createFolder("emuAreaMiddle", IPageLayout.TOP, ratio, "emuAreaBottom");
+ }
+
+ // create folder #3 only if necessary
+ if (numEmuViews >= 3)
+ {
+ // ratio is always half of folder #2 space
+ emuAreaFolders[2] =
+ layout.createFolder("emuAreaTop", IPageLayout.TOP, 0.50f, "emuAreaMiddle");
+ }
+
+ return emuAreaFolders;
+ }
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/perspective/extension/AndroidPerspectiveExtensionBean.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/perspective/extension/AndroidPerspectiveExtensionBean.java
new file mode 100644
index 0000000..cb109c1
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/perspective/extension/AndroidPerspectiveExtensionBean.java
@@ -0,0 +1,98 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.emulator.ui.perspective.extension;
+
+/**
+ * DESCRIPTION:
+ * <br>
+ * This class is a bean carrying information on a particular declaration of the
+ * androidPerspectiveExtension extension point.
+ * <br>
+ * RESPONSIBILITY:
+ * <br>
+ * Carry information about a declaration of androidPerspectiveExtension.
+ * <br>
+ * COLABORATORS:
+ * <br>
+ * None
+ * <br>
+ * USAGE:
+ * <br>
+ * This class should be instantiated only by the reader of the extension points.
+ * Clients may only get the values stored through the getter methods.
+ */
+public class AndroidPerspectiveExtensionBean
+{
+ private String viewId;
+
+ private PerspectiveAreas area;
+
+ /**
+ * Creates a new AndroidPerspectiveExtensionBean object with the given information.
+ *
+ * @param viewId the id of the view
+ * @param area the area to which it should be placed
+ */
+ AndroidPerspectiveExtensionBean(String viewId, String area)
+ {
+ this.viewId = viewId;
+
+ if (IAndroidPerspectiveExtensionConstants.ATT_AREA_DEVMGT_VALUE.equals(area))
+ {
+ this.area = PerspectiveAreas.DEVICE_MANAGEMENT_VIEWS_AREA;
+ }
+ else if (IAndroidPerspectiveExtensionConstants.ATT_AREA_EMU_VALUE.equals(area))
+ {
+ this.area = PerspectiveAreas.EMULATION_VIEWS_AREA;
+ }
+ else
+ {
+ this.area = PerspectiveAreas.UNKNOWN_AREA;
+ }
+ }
+
+ /**
+ * Retrieves the id of the view.
+ *
+ * @return the id of the view
+ */
+ public String getViewId()
+ {
+ return viewId;
+ }
+
+ /**
+ * Retrieves the area to which the view should be added.
+ *
+ * @return the area on the perspective
+ */
+ public PerspectiveAreas getArea()
+ {
+ return area;
+ }
+
+ /**
+ *
+ * This enum represents the available areas to which a view can be added to the Android
+ * Emulator Perspective.
+ * It has a value for UNKNOWN_AREA for robustness purpose.
+ *
+ */
+ public static enum PerspectiveAreas
+ {
+ DEVICE_MANAGEMENT_VIEWS_AREA, EMULATION_VIEWS_AREA, UNKNOWN_AREA
+ }
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/perspective/extension/AndroidPerspectiveExtensionReader.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/perspective/extension/AndroidPerspectiveExtensionReader.java
new file mode 100644
index 0000000..873f2f9
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/perspective/extension/AndroidPerspectiveExtensionReader.java
@@ -0,0 +1,90 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.emulator.ui.perspective.extension;
+
+import static com.motorola.studio.android.common.log.StudioLogger.warn;
+
+import java.util.Collection;
+import java.util.LinkedHashSet;
+
+import org.eclipse.core.runtime.IConfigurationElement;
+import org.eclipse.core.runtime.IExtension;
+
+import com.motorola.studio.android.common.utilities.EclipseUtils;
+
+/**
+ * DESCRIPTION:
+ * <br>
+ * This class is the reader for the androidPerspectiveExtension extension point declarations.
+ * <br>
+ * RESPONSIBILITY:
+ * <br>
+ * Retrieve information from plugins declaring the androidPerspectiveExtension.
+ * <br>
+ * COLABORATORS:
+ * <br>
+ * IAndroidPerspectiveExtensionConstants: implements this interface for using the constants directly
+ * <br>
+ * USAGE:
+ * <br>
+ * This class methods should be called statically from within the implementation of the Android
+ * Emulator Perspective.
+ */
+public class AndroidPerspectiveExtensionReader implements IAndroidPerspectiveExtensionConstants
+{
+
+ /**
+ * Reads the information from plug-ins declaring extensions to androidPerspectiveExtension
+ * and stores it into beans.
+ *
+ * @return a collection of beans containing androidPerspectiveExtension information
+ */
+ public static Collection<AndroidPerspectiveExtensionBean> readAndroidPerspectiveExtensions()
+ {
+ Collection<AndroidPerspectiveExtensionBean> beans =
+ new LinkedHashSet<AndroidPerspectiveExtensionBean>();
+
+ IExtension[] extensions = EclipseUtils.getInstalledPlugins(EXTENSION_POINT_ID);
+
+ for (IExtension extension : extensions)
+ {
+ IConfigurationElement[] viewElements = extension.getConfigurationElements();
+
+ for (IConfigurationElement viewElement : viewElements)
+ {
+ if (ELEMENT_VIEW.equals(viewElement.getName()))
+ {
+ String viewId = viewElement.getAttribute(ATT_ID);
+ String area = viewElement.getAttribute(ATT_AREA);
+
+ // create the bean only if information could be read
+ if ((viewId != null) && (!viewId.equals("")) && (area != null)
+ && (!area.equals("")))
+ {
+ beans.add(new AndroidPerspectiveExtensionBean(viewId, area));
+ }
+ else
+ {
+ warn("View not added to Android Emulator Perspective area for extension "
+ + extension.getExtensionPointUniqueIdentifier());
+ }
+ }
+ }
+ }
+
+ return beans;
+ }
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/perspective/extension/IAndroidPerspectiveExtensionConstants.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/perspective/extension/IAndroidPerspectiveExtensionConstants.java
new file mode 100644
index 0000000..3d1424b
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/perspective/extension/IAndroidPerspectiveExtensionConstants.java
@@ -0,0 +1,39 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.emulator.ui.perspective.extension;
+
+import com.motorola.studio.android.emulator.EmulatorPlugin;
+
+/**
+ *
+ * This interface provides the String values used by the androidPerspectiveExtension
+ * extension point for purpose of reading the declared extensions.
+ *
+ */
+public interface IAndroidPerspectiveExtensionConstants
+{
+ String EXTENSION_POINT_ID = EmulatorPlugin.PLUGIN_ID + ".androidPerspectiveExtension";
+
+ String ELEMENT_VIEW = "view";
+
+ String ATT_ID = "id";
+
+ String ATT_AREA = "area";
+
+ String ATT_AREA_DEVMGT_VALUE = "devicemanagementviews";
+
+ String ATT_AREA_EMU_VALUE = "emulationviews";
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/view/AbstractAndroidView.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/view/AbstractAndroidView.java
new file mode 100644
index 0000000..cf6cb0f
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/view/AbstractAndroidView.java
@@ -0,0 +1,1839 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.emulator.ui.view;
+
+import static com.motorola.studio.android.common.log.StudioLogger.debug;
+import static com.motorola.studio.android.common.log.StudioLogger.error;
+import static com.motorola.studio.android.common.log.StudioLogger.info;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.jobs.ISchedulingRule;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.jface.action.IContributionItem;
+import org.eclipse.jface.action.IMenuListener;
+import org.eclipse.jface.action.IMenuManager;
+import org.eclipse.jface.action.IToolBarManager;
+import org.eclipse.jface.action.MenuManager;
+import org.eclipse.jface.dialogs.ProgressMonitorDialog;
+import org.eclipse.jface.operation.IRunnableWithProgress;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.sequoyah.device.framework.model.IInstance;
+import org.eclipse.sequoyah.vnc.protocol.PluginProtocolActionDelegate;
+import org.eclipse.sequoyah.vnc.protocol.lib.ProtocolHandle;
+import org.eclipse.sequoyah.vnc.protocol.lib.ProtocolMessage;
+import org.eclipse.sequoyah.vnc.vncviewer.graphics.IRemoteDisplay;
+import org.eclipse.sequoyah.vnc.vncviewer.graphics.swt.SWTRemoteDisplay;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.DisposeEvent;
+import org.eclipse.swt.events.DisposeListener;
+import org.eclipse.swt.events.FocusAdapter;
+import org.eclipse.swt.events.FocusEvent;
+import org.eclipse.swt.events.KeyListener;
+import org.eclipse.swt.events.MouseEvent;
+import org.eclipse.swt.events.MouseListener;
+import org.eclipse.swt.events.MouseMoveListener;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.widgets.Canvas;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.TabFolder;
+import org.eclipse.swt.widgets.TabItem;
+import org.eclipse.ui.IActionBars;
+import org.eclipse.ui.IPartListener2;
+import org.eclipse.ui.IPerspectiveDescriptor;
+import org.eclipse.ui.IPerspectiveListener;
+import org.eclipse.ui.IViewPart;
+import org.eclipse.ui.IViewSite;
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.IWorkbenchListener;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.IWorkbenchPartReference;
+import org.eclipse.ui.PartInitException;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.part.ViewPart;
+
+import com.motorola.studio.android.common.preferences.DialogWithToggleUtils;
+import com.motorola.studio.android.common.utilities.EclipseUtils;
+import com.motorola.studio.android.common.utilities.PluginUtils;
+import com.motorola.studio.android.emulator.EmulatorPlugin;
+import com.motorola.studio.android.emulator.core.devfrm.DeviceFrameworkManager;
+import com.motorola.studio.android.emulator.core.exception.InstanceStopException;
+import com.motorola.studio.android.emulator.core.exception.SkinException;
+import com.motorola.studio.android.emulator.core.exception.StartCancelledException;
+import com.motorola.studio.android.emulator.core.model.IAndroidEmulatorInstance;
+import com.motorola.studio.android.emulator.core.model.IEmulatorView;
+import com.motorola.studio.android.emulator.core.skin.IAndroidSkin;
+import com.motorola.studio.android.emulator.i18n.EmulatorNLS;
+import com.motorola.studio.android.emulator.logic.AbstractStartAndroidEmulatorLogic;
+import com.motorola.studio.android.emulator.logic.AbstractStartAndroidEmulatorLogic.LogicMode;
+import com.motorola.studio.android.emulator.logic.IAndroidLogicInstance;
+import com.motorola.studio.android.emulator.logic.StartVncServerLogic;
+import com.motorola.studio.android.emulator.logic.stop.AndroidEmulatorStopper;
+import com.motorola.studio.android.emulator.ui.IUIHelpConstants;
+import com.motorola.studio.android.emulator.ui.controls.IAndroidComposite;
+import com.motorola.studio.android.emulator.ui.controls.RemoteCLIDisplay;
+import com.motorola.studio.android.emulator.ui.controls.nativewindow.NativeWindowComposite;
+import com.motorola.studio.android.nativeos.IDevicePropertiesOSConstants;
+import com.motorola.studio.android.nativeos.NativeUIUtils;
+
+/**
+ * DESCRIPTION:
+ * This class represents the Android Emulator view. It provides the
+ * generic methods of the Emulator Views. The specific ones must be defined
+ * by the classes that extend it.
+ *
+ * RESPONSIBILITY:
+ * - Show the viewers to the end user
+ *
+ * COLABORATORS:
+ * None.
+ *
+ * USAGE:
+ * The public interface provides static and dynamic methods:
+ * STATIC METHODS:
+ * - Call getActiveInstance to retrieve the instance that corresponds to
+ * the emulator running at the active tab
+ * - Call updateActiveViewers to guarantee that the active viewer of all views
+ * is up to date in all emulator views opened, but do not make further verifications.
+ * DYNAMIC METHODS:
+ * - Call refreshView to updates all viewers including creation of viewers
+ * for started instances and removal of viewers
+ * - Call updateActiveViewer to guarantee that the active viewer is up to
+ * date in all emulator views opened, but do not make further verifications
+ */
+public abstract class AbstractAndroidView extends ViewPart implements IEmulatorView
+{
+ private final MenuManager menuManager;
+
+ public static final String POPUP_MENU_ID = "com.motorola.studio.android.emulator.view.popup";
+
+ private MouseListener mouseClickListener;
+
+ /**
+ * Preference key of the Question Dialog about stopping the emulators by closing view
+ */
+ private static String STOP_BY_CLOSING_VIEW_KEY_PREFERENCE = "stop.by.closing.view";
+
+ /**
+ * Preference key of the Question Dialog about displaying all emulators in the IDE
+ *
+ */
+ private static String SHOW_EMULATOR_IN_THE_IDE_KEY_PREFERENCE =
+ "show.view.for.started.emulators";
+
+ /**
+ * Preference key of the Question Dialog about stopping all emulators in shutdown
+ *
+ */
+ private static String STOP_ALL_EMULATORS_IN_SHUTDOWN_KEY_PREFERENCE =
+ "stop.all.emulators.in.shutdown";
+
+ /**
+ * All event types handled by the listeners in this class
+ */
+ public static final int[] SWT_EVENT_TYPES = new int[]
+ {
+ SWT.KeyDown, SWT.KeyUp, SWT.MouseDown, SWT.MouseUp, SWT.MouseMove, SWT.MouseDoubleClick
+ };
+
+ /**
+ * All possible Layout Operations
+ */
+ public enum LayoutOpp
+ {
+ KEEP, NEXT
+ };
+
+ /**
+ * Tab folder where to place each instance tab
+ */
+ private TabFolder tabFolder;
+
+ Listener listener = new Listener()
+ {
+ public void handleEvent(Event event)
+ {
+ if (tabFolder.getItemCount() > 0)
+ {
+ TabItem activeTabItem = getActiveTabItem();
+
+ if ((activeTabItem != null) && (activeTabItem.getControl() != null))
+ {
+ info("Setting focus to Android Emulator " + activeTabItem.getData());
+ activeTabItem.getControl().setFocus();
+ }
+ }
+ }
+ };
+
+ /**
+ * Map to collect the emulator AndroidViewData committed to its emulator instance.
+ */
+ private final Map<IAndroidEmulatorInstance, AndroidViewData> instanceDataMap =
+ new LinkedHashMap<IAndroidEmulatorInstance, AndroidViewData>();
+
+ /**
+ * Listener required to code the work-around for Sticky Views on perspectiveChanged method.
+ */
+ private PerspectiveListenerImpl perspectiveListenerImpl;
+
+ /**
+ * Listener necessary to determine when the view is closed.
+ */
+ private PartListenerImpl partListenerImpl;
+
+ /**
+ * Listener used to know if the view is being closed due to workbench shutdown.
+ */
+ private static WorkbenchListenerImpl workbenchListenerImpl;
+
+ // the view is being closed during the Studio shutdown.
+ private boolean closingOnShutdown = false;
+
+ // Collection of the ids of the opened views that overwrite this class
+ private static final List<String> childrenIDs = new ArrayList<String>();
+
+ // the instance being currently active at the emulator views
+ private static IAndroidEmulatorInstance activeInstance;
+
+ /**
+ * Listeners of tab switch event
+ */
+ private static final Collection<Listener> tabSwitchListeners = new ArrayList<Listener>();
+
+ /**
+ * Lock to assure that only the first thread will display the show view question
+ */
+ private static Lock showViewLock = new ReentrantReadWriteLock().writeLock();
+
+ /**
+ * Add a listener to be called when the tab selection changes
+ *
+ * @param listener the listener to be added
+ */
+ public static void addTabSwitchListener(Listener listener)
+ {
+ tabSwitchListeners.add(listener);
+ }
+
+ /**
+ * Remove a listener that listen to tab switch events
+ *
+ * @param listener the listener to be removed
+ */
+ public static void removeTabSwitchListener(Listener listener)
+ {
+ tabSwitchListeners.remove(listener);
+ }
+
+ /**
+ * Call listeners of tab switch events
+ */
+ protected void handleTabSwitchEvent()
+ {
+ for (Listener listener : tabSwitchListeners)
+ {
+ listener.handleEvent(null);
+ }
+ }
+
+ /**
+ * Returns the View Identification.
+ * @return the unique ViewId
+ */
+ protected abstract String getViewId();
+
+ /**
+ * Creates the graphical elements representing the emulator that will be
+ * shown by the viewer in its tab item.
+ *
+ * @param tab the tab item that will hold the graphical elements that
+ * represents the emulator
+ * @param instance the emulator instance
+ * @param emulatorData the object to be defined with the elements created.
+ * @throws SkinException if the AVD configured skin does not exists
+ */
+ protected abstract void createWidgets(TabItem tab, final IAndroidEmulatorInstance instance,
+ final AndroidViewData emulatorData) throws SkinException;
+
+ /**
+ * Forces the refreshing of the menu elements.
+ */
+ protected abstract void refreshMenuElements();
+
+ /**
+ * Retrieves the instance being currently active at the emulator views.
+ *
+ * @return The active instance, or null if there is no active instance
+ */
+ public static IAndroidEmulatorInstance getActiveInstance()
+ {
+ return activeInstance;
+ }
+
+ /**
+ * Retrieves the instance being currently active at the emulator views.
+ *
+ * @return The active instance, or null if there is no active instance
+ */
+ public static void setInstance(IAndroidEmulatorInstance emulatorInstance)
+ {
+ if (!childrenIDs.isEmpty())
+ {
+ AbstractAndroidView view =
+ (AbstractAndroidView) EclipseUtils.getActiveView(childrenIDs.get(0));
+ if (view != null)
+ {
+ view.setActiveInstanceId(emulatorInstance.getInstanceIdentifier());
+ activeInstance = emulatorInstance;
+ }
+ }
+ }
+
+ /**
+ * Retrieves the information about the viewer used to display the given emulator instance
+ * and returns null if there is no AndroidViewData for the given emulator instance.
+ * viewer.
+ * @param the emulator instance whose Android Viewer data need to be retrieved.
+ * @return the AndroidViewerData for the given Emulator instance (null if none is available).
+ */
+ public AndroidViewData getViewData(IAndroidEmulatorInstance instance)
+ {
+ AndroidViewData viewData = instanceDataMap.get(instance);
+ return viewData;
+ }
+
+ public IAndroidSkin getSkin(IAndroidEmulatorInstance instance)
+ {
+ IAndroidSkin skin = null;
+ AndroidViewData viewData = getViewData(instance);
+ if (viewData != null)
+ {
+ skin = viewData.getSkin();
+ }
+ return skin;
+ }
+
+ /**
+ * Gets the layout to set, if opp is NEXT or PREVIOUS
+ *
+ * @param viewId The view that is currently active
+ * @param opp The layout operation to perform
+ */
+ public static String getPreviousOrNextLayout(String viewId, LayoutOpp opp)
+ {
+ String prevNextLayout = null;
+ AbstractAndroidView view = (AbstractAndroidView) EclipseUtils.getActiveView(viewId);
+ if (view != null)
+ {
+ prevNextLayout = view.getPreviousOrNextLayout(opp);
+ }
+
+ return prevNextLayout;
+ }
+
+ /**
+ * Gets the layout to set, if opp is NEXT or PREVIOUS
+ *
+ * @param opp The layout operation to perform
+ */
+ @SuppressWarnings("incomplete-switch")
+ private String getPreviousOrNextLayout(LayoutOpp opp)
+ {
+ String prevNextLayout = null;
+ if (activeInstance != null)
+ {
+ String referenceLayout = activeInstance.getCurrentLayout();
+ AndroidViewData viewData = instanceDataMap.get(activeInstance);
+ if (viewData != null)
+ {
+ IAndroidComposite androidComposite = viewData.getComposite();
+ if ((androidComposite != null))
+ {
+ IAndroidSkin androidSkin = viewData.getSkin();
+
+ if (androidSkin != null)
+ {
+ switch (opp)
+ {
+ case NEXT:
+ prevNextLayout = androidSkin.getNextLayout(referenceLayout);
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ return prevNextLayout;
+ }
+
+ /**
+ * Updates the zoom action that needs to be checked in all emulator views.
+ * This method must be called every time the focus changes to another
+ * viewer.
+ *
+ * @param layoutName The layout name to set
+ */
+ public static void changeLayout(String layoutName)
+ {
+ for (String viewId : childrenIDs)
+ {
+ AbstractAndroidView view = (AbstractAndroidView) EclipseUtils.getActiveView(viewId);
+ if (view != null)
+ {
+ view.updateActiveViewer(layoutName);
+ }
+ }
+ }
+
+ /**
+ * Gets the help ID to be used for attaching
+ * context sensitive help.
+ *
+ * Classes that extends this class and want to set
+ * their on help should override this method
+ */
+ protected String getHelpId()
+ {
+ return IUIHelpConstants.EMULATOR_VIEW_HELP;
+ }
+
+ /**
+ * @see org.eclipse.ui.IWorkbenchPart#createPartControl(Composite)
+ */
+ @Override
+ public void createPartControl(Composite parent)
+ {
+ PlatformUI.getWorkbench().getHelpSystem().setHelp(parent, getHelpId());
+
+ this.tabFolder = new TabFolder(parent, SWT.BORDER | SWT.BACKGROUND);
+ IViewSite viewSite = getViewSite();
+
+ // Add listeners
+ tabFolder.addSelectionListener(new SelectionAdapter()
+ {
+ @Override
+ public void widgetSelected(SelectionEvent event)
+ {
+ setActiveInstanceId();
+ updateMenuAndToolbars();
+ handleTabSwitchEvent();
+ }
+ });
+
+ tabFolder.addFocusListener(new FocusAdapter()
+ {
+
+ @Override
+ public void focusGained(FocusEvent e)
+ {
+ handleTabSwitchEvent();
+ }
+
+ });
+
+ perspectiveListenerImpl = new PerspectiveListenerImpl();
+ viewSite.getWorkbenchWindow().addPerspectiveListener(perspectiveListenerImpl);
+
+ partListenerImpl = new PartListenerImpl();
+ viewSite.getPage().addPartListener(partListenerImpl);
+
+ if (workbenchListenerImpl == null)
+ {
+ workbenchListenerImpl = new WorkbenchListenerImpl();
+ viewSite.getWorkbenchWindow().getWorkbench()
+ .addWorkbenchListener(workbenchListenerImpl);
+ }
+
+ // Update UI
+ refreshView();
+
+ IActionBars actionBars = viewSite.getActionBars();
+ if (actionBars != null)
+ {
+ IMenuManager menuManager = actionBars.getMenuManager();
+ if (menuManager != null)
+ {
+ menuManager.addMenuListener(new IMenuListener()
+ {
+ public void menuAboutToShow(IMenuManager manager)
+ {
+ // Calls the manager update method to guarantee that the command have its handler
+ // initialized. Otherwise, the next command will not work properly
+ if (manager != null)
+ {
+ manager.update(true);
+ }
+ updateMenuAndToolbars();
+ }
+ });
+ }
+ }
+
+ //register the popup menu
+ viewSite.registerContextMenu(POPUP_MENU_ID, menuManager, null);
+
+ //create listener
+ if (Platform.getOS().contains(Platform.OS_MACOSX))
+ {
+ mouseClickListener = new MouseListener()
+ {
+
+ public void mouseDoubleClick(MouseEvent e)
+ {
+ //do nothing
+ }
+
+ public void mouseDown(MouseEvent e)
+ {
+ if ((e.button == 1) && (e.stateMask == SWT.CONTROL))
+ {
+ menuManager.getMenu().setVisible(true);
+ }
+ }
+
+ public void mouseUp(MouseEvent e)
+ {
+ //do nothing
+ }
+
+ };
+ }
+ else
+ {
+ mouseClickListener = new MouseListener()
+ {
+
+ public void mouseDoubleClick(MouseEvent e)
+ {
+ //do nothing
+ }
+
+ public void mouseDown(MouseEvent e)
+ {
+ if (e.button == 3)
+ {
+ menuManager.getMenu().setVisible(true);
+ }
+ }
+
+ public void mouseUp(MouseEvent e)
+ {
+ //do nothing
+ }
+
+ };
+
+ }
+
+ }
+
+ /**
+ * Constructor default
+ */
+ public AbstractAndroidView()
+ {
+ childrenIDs.add(getViewId());
+ menuManager = new MenuManager("", POPUP_MENU_ID);
+ addTabSwitchListener(listener);
+ }
+
+ /**
+ * @see org.eclipse.ui.IWorkbenchPart#setFocus()
+ */
+ @Override
+ public void setFocus()
+ {
+ if (tabFolder.getItemCount() > 0)
+ {
+ TabItem activeTabItem = getActiveTabItem();
+
+ if ((activeTabItem != null) && (activeTabItem.getControl() != null))
+ {
+ info("Setting focus to Android Emulator " + activeTabItem.getData());
+ activeTabItem.getControl().setFocus();
+ }
+ else
+ {
+ info("Setting focus to Android Emulator View");
+ tabFolder.setFocus();
+ }
+ }
+ else
+ {
+ info("Setting focus to Android Emulator View");
+ tabFolder.setFocus();
+ }
+
+ updateMenuAndToolbars();
+ }
+
+ /**
+ * @see org.eclipse.ui.IWorkbenchPart#dispose()
+ */
+ @Override
+ public void dispose()
+ {
+ removeTabSwitchListener(listener);
+ debug("Disposing View: " + getClass());
+ getViewSite().getWorkbenchWindow().removePerspectiveListener(perspectiveListenerImpl);
+ getViewSite().getPage().removePartListener(partListenerImpl);
+ perspectiveListenerImpl = null;
+ partListenerImpl = null;
+ instanceDataMap.clear();
+ tabFolder.dispose();
+ childrenIDs.remove(getViewId());
+ super.dispose();
+ }
+
+ /**
+ * This method rebuilds the skin, adding a new tab in the Android Emulator View
+ * to show it.
+ *
+ * It should be used when the Android Emulator view is being created when the Android Emulator
+ * instance is not stopped.
+ */
+ public void refreshView()
+ {
+ Job refreshViews = new Job("Refresh Emulator View")
+ {
+ @Override
+ protected IStatus run(IProgressMonitor monitor)
+ {
+
+ info("Updating Android Emulator viewers");
+
+ final DeviceFrameworkManager framework = DeviceFrameworkManager.getInstance();
+
+ Collection<IAndroidEmulatorInstance> startedInstances =
+ framework.getAllStartedInstances();
+
+ for (final IAndroidEmulatorInstance instance : startedInstances)
+ {
+ if (instance
+ .getProperties()
+ .getProperty(IDevicePropertiesOSConstants.useVnc,
+ NativeUIUtils.getDefaultUseVnc()).equals("true"))
+ {
+ if (!instance.isConnected())
+ {
+ IStatus returnStatus = null;
+ returnStatus = connectVNC(instance, monitor);
+ if (returnStatus.isOK())
+ {
+ PlatformUI.getWorkbench().getDisplay().syncExec(new Runnable()
+ {
+ public void run()
+ {
+ createViewer(instance);
+ }
+ });
+ }
+ }
+ }
+ }
+
+ PlatformUI.getWorkbench().getDisplay().syncExec(new Runnable()
+ {
+ public void run()
+ {
+
+ Collection<IAndroidEmulatorInstance> connectedInstances =
+ framework.getAllConnectedInstances();
+
+ Collection<IAndroidEmulatorInstance> instancesWithViewerCollection =
+ getInstancesWithViewer();
+
+ for (IAndroidEmulatorInstance instance : connectedInstances)
+ {
+ if (!instancesWithViewerCollection.contains(instance))
+ {
+ createViewer(instance);
+ }
+ else
+ {
+ // update the collection for removing the stopped instances
+ instancesWithViewerCollection.remove(instance);
+ }
+ }
+
+ // Remove not started instances from viewer
+ for (IAndroidEmulatorInstance instance : instancesWithViewerCollection)
+ {
+ disposeViewer(instance);
+ info("Disposed viewer of " + instance);
+ }
+
+ // Update the active instance variable after any creation/disposal is
+ // made. Update the active viewer only if the active viewer is new
+ activeInstance = getActiveInstanceFromCurrentView();
+ if (activeInstance != null)
+ {
+ setActiveInstanceId();
+ handleTabSwitchEvent();
+ }
+
+ updateMenuAndToolbars();
+ }
+
+ });
+
+ return Status.OK_STATUS;
+ }
+ };
+ refreshViews.setRule(new RefreshRule());
+ refreshViews.schedule();
+ }
+
+ class RefreshRule implements ISchedulingRule
+ {
+ public boolean contains(ISchedulingRule rule)
+ {
+ return this == rule;
+ }
+
+ public boolean isConflicting(ISchedulingRule rule)
+ {
+ return rule instanceof RefreshRule;
+ }
+ }
+
+ /**
+ * Updates the zoom action that needs to be checked.
+ * This method must be called every time the focus changes to another
+ * viewer.
+ */
+ public void updateActiveViewer()
+ {
+ updateActiveViewer(null);
+ }
+
+ /**
+ * Updates the zoom action that needs to be checked, after performing a layout operation
+ *
+ * @param layoutName The name of the layout to set if opp is SETLAYOUT
+ */
+ public void updateActiveViewer(String layoutName)
+ {
+ info("Updating Android Emulator view");
+
+ if (activeInstance != null)
+ {
+ AndroidViewData viewData = instanceDataMap.get(activeInstance);
+ if (viewData != null)
+ {
+ IAndroidComposite androidComposite = viewData.getComposite();
+ if ((androidComposite != null))
+ {
+ if ((activeInstance.getProperties().getProperty(
+ IDevicePropertiesOSConstants.useVnc, NativeUIUtils.getDefaultUseVnc()))
+ .equals("true"))
+ {
+ IAndroidSkin androidSkin = viewData.getSkin();
+
+ if (androidSkin != null)
+ {
+ if (layoutName != null)
+ {
+ activeInstance.setCurrentLayout(layoutName);
+ }
+
+ boolean isNeeded =
+ androidSkin.isSwapWidthHeightNeededAtLayout(activeInstance
+ .getCurrentLayout());
+ IRemoteDisplay.Rotation rotation =
+ (isNeeded
+ ? IRemoteDisplay.Rotation.ROTATION_90DEG_COUNTERCLOCKWISE
+ : IRemoteDisplay.Rotation.ROTATION_0DEG);
+ viewData.getMainDisplay().setRotation(rotation);
+ androidComposite.applyLayout(activeInstance.getCurrentLayout());
+ }
+ }
+ androidComposite.applyZoomFactor();
+ }
+
+ }
+ }
+
+ updateMenuAndToolbars();
+
+ info("Updated Android Emulator view");
+ }
+
+ public void changeToNextLayout()
+ {
+ AndroidViewData viewData = instanceDataMap.get(activeInstance);
+ IAndroidComposite androidComposite = viewData.getComposite();
+ if (androidComposite instanceof NativeWindowComposite)
+ {
+ ((NativeWindowComposite) androidComposite).changeToNextLayout();
+ }
+ }
+
+ /**
+ * Retrieves the instance being currently displayed at this view.
+ *
+ * @return The active instance, or null if there is no active instance
+ */
+ private IAndroidEmulatorInstance getActiveInstanceFromCurrentView()
+ {
+ TabItem activeInstanceItem = getActiveTabItem();
+ IAndroidEmulatorInstance instance = null;
+ if (activeInstanceItem != null)
+ {
+ instance = (IAndroidEmulatorInstance) (activeInstanceItem.getData());
+ }
+ else
+ {
+ debug("No active instance being shown at emulator view");
+ }
+
+ return instance;
+ }
+
+ /**
+ * Executes the procedure to connect to the VNC
+ *
+ * @param androidDevice
+ * The device being connected
+ */
+ public IStatus connectVNC(final IAndroidEmulatorInstance instance, IProgressMonitor monitor)
+ {
+ IStatus statusToReturn = Status.OK_STATUS;
+
+ try
+ {
+ IAndroidLogicInstance logicInstance = (IAndroidLogicInstance) instance;
+ AbstractStartAndroidEmulatorLogic startLogic = logicInstance.getStartLogic();
+
+ startLogic.execute(logicInstance, LogicMode.TRANSFER_AND_CONNECT_VNC,
+ logicInstance.getTimeout(), monitor);
+ }
+ catch (StartCancelledException e1)
+ {
+ info("The user canceled the transfer/connect to VNC phase.");
+ statusToReturn = Status.CANCEL_STATUS;
+ }
+ catch (Exception e1)
+ {
+ error("Could not establish VNC Connection to " + instance);
+ statusToReturn =
+ new Status(IStatus.ERROR, EmulatorPlugin.PLUGIN_ID, NLS.bind(
+ EmulatorNLS.ERR_CannotConnectToVNC, instance.getName()));
+ }
+ return statusToReturn;
+ }
+
+ /**
+ * Shows the Android Emulator view, if not being shown
+ */
+ public static void showView()
+ {
+ info("Open and move focus to the emulator view");
+
+ boolean emulatorViewOpened =
+ !EclipseUtils.getAllOpenedViewsWithId(AndroidView.ANDROID_VIEW_ID).isEmpty();
+
+ try
+ {
+ // if emulator view is opened previously or if no emulator view is opened,
+ // show / refresh the emulator view.
+ if (emulatorViewOpened)
+ {
+ EclipseUtils.showView(AndroidView.ANDROID_VIEW_ID);
+ }
+ else
+ {
+ // Make sure only one open view (due to the transition to online) will occur at the same time.
+ // e.g. if the "question open dialog" is already opened, it is not needed one
+
+ if (showViewLock.tryLock())
+ {
+ try
+ {
+
+ boolean openEmulatorView =
+ DialogWithToggleUtils
+ .showQuestion(
+ SHOW_EMULATOR_IN_THE_IDE_KEY_PREFERENCE,
+ EmulatorNLS.QUESTION_AbstractAndroidView_OpenViewForStartedEmulatorsTitle,
+ EmulatorNLS.QUESTION_AbstractAndroidView_OpenViewForStartedEmulatorsMessage);
+ if (openEmulatorView)
+ {
+ EclipseUtils.showView(AndroidView.ANDROID_VIEW_ID);
+ }
+ }
+ finally
+ {
+ showViewLock.unlock();
+ }
+ }
+ }
+ }
+ catch (PartInitException e)
+ {
+ error("The Android Emulator View could not be opened programatically");
+ EclipseUtils.showErrorDialog(EmulatorNLS.GEN_Error,
+ EmulatorNLS.EXC_AbstractAndroidView_ViewNotAccessibleProgramatically);
+ }
+ }
+
+ /**
+ * Creates a viewer for the provided instance
+ *
+ * @param instance The instance that will have a viewer created at this view
+ */
+ private void createViewer(final IAndroidEmulatorInstance instance)
+ {
+ if (instance != null)
+ {
+ info("Creating tab for " + instance + " on " + getClass());
+
+ Set<IAndroidEmulatorInstance> currentInstancesWithTab =
+ getInstancesWithAtLeastOneViewer();
+
+ // Creates a tab item to hold the skin at the view
+ TabItem newTabItem = new TabItem(tabFolder, SWT.NONE);
+
+ // Set parameters at the tab item
+ newTabItem.setText(instance.getFullName());
+ newTabItem.setData(instance);
+ AndroidViewData emulatorData = new AndroidViewData();
+ instanceDataMap.put(instance, emulatorData);
+
+ try
+ {
+ createWidgets(newTabItem, instance, emulatorData);
+ tabFolder.setSelection(newTabItem);
+ setActiveInstanceId();
+
+ //add popup menu
+ if (newTabItem.getControl() != null)
+ {
+ menuManager.createContextMenu(newTabItem.getControl());
+ newTabItem.getControl().addMouseListener(mouseClickListener);
+ }
+
+ ProtocolMessage setEncodingMsg = new ProtocolMessage(2);
+ setEncodingMsg.setFieldValue("padding", 0);
+ setEncodingMsg.setFieldValue("number-of-encodings", 1);
+ setEncodingMsg.setFieldValue("encoding-type", "encoding-types", 0, 0);
+ PluginProtocolActionDelegate.sendMessageToServer(instance.getProtocolHandle(),
+ setEncodingMsg);
+
+ info("Created tab for " + instance);
+
+ if (instance
+ .getProperties()
+ .getProperty(IDevicePropertiesOSConstants.useVnc,
+ NativeUIUtils.getDefaultUseVnc()).toString().equals("true"))
+ {
+ startVncDisplays(instance);
+ info("Started displays for " + instance);
+
+ // overwrite original tml listeners
+ addListenersToMainDisplay(emulatorData);
+ }
+ else
+ {
+ IAndroidComposite parentComposite = emulatorData.getComposite();
+ ((NativeWindowComposite) parentComposite).addMouseListener(mouseClickListener);
+ }
+
+ IAndroidComposite androidComposite = emulatorData.getComposite();
+ if (androidComposite != null)
+ {
+ androidComposite.applyZoomFactor();
+ }
+
+ // If this is the first view to be opened, guarantee that the screen orientation is
+ // synchronized with the current layout (only when using VNC)
+ if (!currentInstancesWithTab.contains(instance)
+ && instance
+ .getProperties()
+ .getProperty(IDevicePropertiesOSConstants.useVnc,
+ NativeUIUtils.getDefaultUseVnc()).toString().equals("true"))
+ {
+ IAndroidSkin skin = getSkin(instance);
+ if (skin != null)
+ {
+ instance.changeOrientation(skin.getLayoutScreenCommand(instance
+ .getCurrentLayout()));
+ }
+ }
+
+ updateActiveViewer();
+
+ info("Created tab for Android Emulator " + instance);
+ }
+ catch (SkinException e)
+ {
+ error("The skin associated to this instance (" + instance.getName()
+ + ") is not installed or is corrupted.");
+ EclipseUtils.showErrorDialog(e);
+
+ try
+ {
+ instance.stop(true);
+ disposeViewer(instance);
+ }
+ catch (InstanceStopException e1)
+ {
+ error("Error while running service for stopping virtual machine");
+ EclipseUtils.showErrorDialog(EmulatorNLS.GEN_Error,
+ EmulatorNLS.EXC_General_CannotRunStopService);
+ }
+ }
+ }
+ }
+
+ private void addListenersToMainDisplay(AndroidViewData emulatorData)
+ {
+ // TmL registers listeners during start, and unregisters all of them during
+ // stop. To adapt the listeners to Studio needs, we are including the following
+ // operations after the start display call from TmL. With this we are achieving:
+ //
+ // 1. TmL registers several listeners at its canvas (TmL start method)
+ // 2. Studio unregisters the TmL listeners (this method)
+ // 3. Studio registers new listeners to replace the TmL ones (this method)
+ // 4. TmL unregisters Studio listeners instead of its own (TmL stop method)
+
+ SWTRemoteDisplay remoteDisplay = emulatorData.getMainDisplay();
+ final Canvas canvas = remoteDisplay.getCanvas();
+ IAndroidComposite parentComposite = emulatorData.getComposite();
+
+ for (int eventType : SWT_EVENT_TYPES)
+ {
+ for (Listener listener : canvas.getListeners(eventType))
+ {
+ canvas.removeListener(eventType, listener);
+ }
+ }
+
+ KeyListener keyListener = parentComposite.getKeyListener();
+ final MouseListener mouseListener = parentComposite.getMouseListener();
+ MouseMoveListener mouseMoveListener = parentComposite.getMouseMoveListener();
+
+ canvas.addKeyListener(keyListener);
+ canvas.addMouseListener(mouseListener);
+ canvas.addMouseMoveListener(mouseMoveListener);
+
+ // Due to the differences in listener registration between TmL and Studio, it will
+ // remain a registered listener when the viewer is disposed. For this reason, the
+ // following dispose listener is being registered.
+ DisposeListener disposeListener = new DisposeListener()
+ {
+ public void widgetDisposed(DisposeEvent arg0)
+ {
+ canvas.removeMouseListener(mouseListener);
+ canvas.removeMouseListener(mouseClickListener);
+ }
+ };
+ emulatorData.setDisposeListener(disposeListener);
+ canvas.addDisposeListener(disposeListener);
+ canvas.addMouseListener(mouseClickListener);
+ }
+
+ /**
+ * Disposes the viewer of the provided instance
+ *
+ * @param instance The instance that will have a viewer disposed from this view
+ */
+ private void disposeViewer(final IAndroidEmulatorInstance instance)
+ {
+ info("Disposing tab of Android Emulator at " + instance);
+
+ TabItem item = getTabItem(instance);
+ if (item != null)
+ {
+
+ stopVncDisplays(instance);
+
+ //if there are no other viewers, we can stop protocol and vnc server
+ if ((childrenIDs.size() == 1)
+ && (instance
+ .getProperties()
+ .getProperty(IDevicePropertiesOSConstants.useVnc,
+ NativeUIUtils.getDefaultUseVnc()).toString().equals("true")))
+ {
+ info("There is only one view opened, stop VNC protocol and VNC Server");
+ stopVncProtocol((IAndroidLogicInstance) instance);
+ stopVncServer(instance);
+ }
+
+ AndroidViewData data = instanceDataMap.get(instance);
+ if (data != null)
+ {
+ SWTRemoteDisplay mainDisplay = data.getMainDisplay();
+ if (mainDisplay != null)
+ {
+ Canvas canvas = mainDisplay.getCanvas();
+
+ if (canvas != null)
+ {
+ canvas.removeDisposeListener(data.getDisposeListener());
+ }
+ }
+ }
+
+ Control c = item.getControl();
+ if (c != null)
+ {
+ c.dispose();
+ }
+ item.setControl(null);
+
+ item.dispose();
+ instanceDataMap.remove(instance);
+ updateMenuAndToolbars();
+ info("Disposed tab of Android Emulator at " + instance);
+ }
+
+ }
+
+ /**
+ * Gets the list of instances with viewers associated.
+ * @return the collection of instances
+ */
+ private Collection<IAndroidEmulatorInstance> getInstancesWithViewer()
+ {
+ final Collection<IAndroidEmulatorInstance> instancesWithViewer =
+ new LinkedHashSet<IAndroidEmulatorInstance>();
+
+ if (!tabFolder.isDisposed())
+ {
+ final TabItem[] allItems = tabFolder.getItems();
+
+ for (TabItem item : allItems)
+ {
+ if (!item.isDisposed())
+ {
+ instancesWithViewer.add((IAndroidEmulatorInstance) item.getData());
+ }
+ }
+ }
+
+ return instancesWithViewer;
+ }
+
+ private static Set<IAndroidEmulatorInstance> getInstancesWithAtLeastOneViewer()
+ {
+ Set<IAndroidEmulatorInstance> instancesSet = new HashSet<IAndroidEmulatorInstance>();
+ for (String viewId : childrenIDs)
+ {
+ AbstractAndroidView view = (AbstractAndroidView) EclipseUtils.getActiveView(viewId);
+ if (view != null)
+ {
+ instancesSet.addAll(view.getInstancesWithViewer());
+ }
+ }
+
+ return instancesSet;
+ }
+
+ /**
+ * Gets the tab item related to the instance
+ * @param instance the emulator instance
+ * @return the tab item
+ */
+ private TabItem getTabItem(IAndroidEmulatorInstance instance)
+ {
+ TabItem result = null;
+ if (!tabFolder.isDisposed())
+ {
+ TabItem[] allItems = tabFolder.getItems();
+
+ for (TabItem item : allItems)
+ {
+ if (instance.equals(item.getData()))
+ {
+ result = item;
+ break;
+ }
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Gets the tab item related to the instance
+ * @param instance the emulator instance
+ * @return the tab item
+ */
+ private TabItem getTabItem(IInstance instance)
+ {
+ TabItem result = null;
+ if (!tabFolder.isDisposed())
+ {
+ TabItem[] allItems = tabFolder.getItems();
+
+ for (TabItem item : allItems)
+ {
+ if (instance.getName()
+ .equals(((IAndroidEmulatorInstance) item.getData()).getName()))
+ {
+ result = item;
+ break;
+ }
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Retrieves the active tab at view
+ *
+ * @return The active tab, or null of there is no tab at tab folder
+ */
+ private TabItem getActiveTabItem()
+ {
+ int activeInstanceIndex = this.tabFolder.getSelectionIndex();
+
+ TabItem activeTabItem = null;
+
+ if (activeInstanceIndex >= 0)
+ {
+ activeTabItem = this.tabFolder.getItem(activeInstanceIndex);
+ }
+
+ return activeTabItem;
+ }
+
+ /**
+ * Updates the zoom action that needs to be checked.
+ * This method must be called every time the focus changes to another
+ * viewer
+ */
+ private void updateMenuAndToolbars()
+ {
+ IViewSite viewSite = getViewSite();
+
+ if (viewSite != null)
+ {
+ IActionBars actionBars = viewSite.getActionBars();
+
+ if (actionBars != null)
+ {
+ IMenuManager menuManager = actionBars.getMenuManager();
+ updateMenuManager(menuManager, viewSite);
+
+ IToolBarManager toolbarManager = actionBars.getToolBarManager();
+ if (toolbarManager != null)
+ {
+ IContributionItem[] items = toolbarManager.getItems();
+ for (IContributionItem item : items)
+ {
+ item.update();
+ }
+ }
+ }
+
+ refreshMenuElements();
+ }
+ }
+
+ /**
+ * Recursive method to update items at menus. The recursion helps to update submenus
+ *
+ * @param manager The manager that holds a menu items
+ * @param viewSite The current view site.
+ */
+ private void updateMenuManager(IMenuManager manager, IViewSite viewSite)
+ {
+ // Update the items in menu manager
+ if (manager != null)
+ {
+ IContributionItem[] items = manager.getItems();
+ for (IContributionItem item : items)
+ {
+ if (item instanceof IMenuManager)
+ {
+ updateMenuManager((IMenuManager) item, viewSite);
+ }
+ else
+ {
+ item.update();
+ }
+ }
+ }
+ }
+
+ /**
+ * Stops all emulator instances with the Progress Monitor opened.
+ */
+ private void stopEmulatorInstances()
+ {
+ // defines the runnable object for stopping emulator instances.
+ final IRunnableWithProgress stopRunnable = new IRunnableWithProgress()
+ {
+ public void run(IProgressMonitor monitor)
+ {
+ Collection<IAndroidEmulatorInstance> startedInstances =
+ DeviceFrameworkManager.getInstance().getAllStartedInstances();
+ boolean errorsHappened = false;
+
+ for (IAndroidEmulatorInstance instance : startedInstances)
+ {
+ try
+ {
+ instance.stop(true);
+ }
+ catch (InstanceStopException e)
+ {
+ errorsHappened = true;
+ }
+ }
+
+ // if closing on shutdown, use a progress bar and stall UI
+ if (closingOnShutdown)
+ {
+ // start a progress monitor
+ monitor.beginTask("", IProgressMonitor.UNKNOWN);
+
+ // make sure the stop instance job finished
+ Job[] jobs = Job.getJobManager().find(null); // get all jobs
+ for (Job job : jobs)
+ {
+ if (job.getName()
+ .equals(EmulatorNLS.UI_AbstractAndroidView_StopInstanceJob))
+ {
+ // when job result is not null, it has finished
+ while (job.getResult() == null)
+ {
+ try
+ {
+ // sleep a little so the waiting is not too busy
+ Thread.sleep(1000);
+ }
+ catch (InterruptedException e)
+ {
+ // do nothing
+ }
+ }
+ }
+ }
+ }
+
+ if (errorsHappened)
+ {
+ EclipseUtils.showErrorDialog(EmulatorNLS.GEN_Error,
+ EmulatorNLS.EXC_AncroidView_CannotRunMultipleStopServices);
+ }
+
+ }
+ };
+
+ // executes the runnable defined above.
+ PlatformUI.getWorkbench().getDisplay().syncExec(new Runnable()
+ {
+ public void run()
+ {
+ Shell currentShell = getViewSite().getShell();
+ ProgressMonitorDialog dialog = new ProgressMonitorDialog(currentShell);
+ try
+ {
+ dialog.run(true, false, stopRunnable);
+ }
+ catch (Exception e)
+ {
+ // Should not have exceptions.
+ // The runnable is not interrupted and it handles exceptions internally
+ // Log runtime exceptions
+ error("Runtime exception was thrown: " + e.getClass().getSimpleName());
+ }
+ }
+ });
+ }
+
+ /**
+ * Sets the identifier of the instance being currently displayed at view
+ */
+ private void setActiveInstanceId(String activeHost)
+ {
+ for (String viewId : childrenIDs)
+ {
+ Collection<IViewPart> viewsToUpdateMenu = EclipseUtils.getAllOpenedViewsWithId(viewId);
+ for (IViewPart view : viewsToUpdateMenu)
+ {
+ AbstractAndroidView emulatorView = (AbstractAndroidView) view;
+ emulatorView.setSelection(activeHost);
+ }
+
+ }
+ }
+
+ /**
+ * Sets the identifier of the instance being currently displayed at view
+ */
+ private void setActiveInstanceId()
+ {
+ TabItem activeInstanceItem = getActiveTabItem();
+ if ((activeInstanceItem != null) && (activeInstanceItem.getData() != null))
+ {
+
+ activeInstance = (IAndroidEmulatorInstance) activeInstanceItem.getData();
+
+ String activeId =
+ ((IAndroidEmulatorInstance) activeInstanceItem.getData())
+ .getInstanceIdentifier();
+
+ setActiveInstanceId(activeId);
+ }
+ else
+ {
+ debug("No active instance being shown at emulator view");
+ }
+
+ }
+
+ /**
+ * Starts the main display associating it to the protocol.
+ * @param handle the protocol handle
+ * @param mainDisplay the main display object
+ */
+ private void startDisplay(ProtocolHandle handle, SWTRemoteDisplay mainDisplay)
+ {
+ // Stop any running screens
+ if ((mainDisplay.isActive()) && (!mainDisplay.isDisposed()))
+ {
+ mainDisplay.stop();
+ }
+
+ try
+ {
+ info("Starting main display refresh");
+ mainDisplay.start(handle);
+ }
+ catch (Exception e)
+ {
+ error("Viewers could not be started.");
+ EclipseUtils.showErrorDialog(EmulatorNLS.GEN_Error,
+ EmulatorNLS.EXC_AndroidView_ErrorStartingScreens);
+
+ GC gc = new GC(mainDisplay.getCanvas());
+ gc.fillRectangle(0, 0, mainDisplay.getScreenWidth(), mainDisplay.getScreenHeight());
+ gc.dispose();
+ }
+ }
+
+ /**
+ * Starts viewer (main display and CLI display) of the emulator instance.
+ * @param instance the emulator instance
+ */
+ protected void startVncDisplays(final IAndroidEmulatorInstance instance)
+ {
+ AndroidViewData viewData = instanceDataMap.get(instance);
+ if (viewData != null)
+ {
+ if (viewData.getMainDisplay() != null)
+ {
+ startDisplay(instance.getProtocolHandle(), viewData.getMainDisplay());
+ }
+ if ((viewData.getCliDisplay() != null) && instance.getHasCli())
+ {
+ viewData.getCliDisplay().start();
+ }
+ }
+ }
+
+ /**
+ * Stops viewer (main display and CLI display) of the emulator instance.
+ */
+ private void stopVncDisplays(final IAndroidEmulatorInstance instance)
+ {
+ info("Stop the VNC Display " + getViewId() + " for " + instance);
+ AndroidViewData viewData = instanceDataMap.get(instance);
+
+ if ((viewData != null))
+ {
+ SWTRemoteDisplay mainDisplay = viewData.getMainDisplay();
+ if ((mainDisplay != null) && mainDisplay.isActive() && !mainDisplay.isDisposed())
+ {
+ mainDisplay.stop();
+ if ((mainDisplay.getBackground() != null)
+ && !mainDisplay.getBackground().isDisposed())
+ {
+ mainDisplay.getBackground().dispose();
+ }
+ }
+
+ RemoteCLIDisplay cliDisplay = viewData.getCliDisplay();
+ if ((cliDisplay != null) && cliDisplay.isDisplayActive() && !cliDisplay.isDisposed())
+ {
+ cliDisplay.stop();
+ if ((cliDisplay.getBackground() != null)
+ && !cliDisplay.getBackground().isDisposed())
+ {
+ cliDisplay.getBackground().dispose();
+ }
+ }
+ }
+ }
+
+ /**
+ * @param instance
+ */
+ private void stopVncProtocol(IAndroidLogicInstance instance)
+ {
+ AndroidEmulatorStopper.stopInstance(instance, true, false, new NullProgressMonitor());
+
+ }
+
+ /**
+ * Stops the execution of the vnc server if it is running on the given instance.
+ * This acts as if the Control+C was pressed in the shell where the vnc server is executing...
+ * @param instance
+ */
+ private void stopVncServer(IAndroidEmulatorInstance instance)
+ {
+ StartVncServerLogic.cancelCurrentVncServerJobs(instance);
+ }
+
+ /**
+ * Class to implement the IPerspectiveListener that you be used as ParListener2 of
+ * current page when the Emulator View is opened. It is required to code the
+ * work-around for Sticky Views on perspectiveChanged method.
+ */
+ private class PerspectiveListenerImpl implements IPerspectiveListener
+ {
+
+ public void perspectiveActivated(IWorkbenchPage page, IPerspectiveDescriptor perspective)
+ {
+ // Nothing to do.
+ }
+
+ public void perspectiveChanged(IWorkbenchPage page, IPerspectiveDescriptor perspective,
+ String changeId)
+ {
+ if (changeId.equals(IWorkbenchPage.CHANGE_VIEW_HIDE))
+ {
+
+ // if the emulator view was hidden
+ if (page.findView(getViewId()) == null)
+ {
+
+ // This is a "sticky view" so when it is hidden or shown for one
+ // perspective, its state is remembered for the other ones.
+ // However, the view reference count is just updated when
+ // the current active perspective is changed. The code below
+ // forces the perspective changing in order to dispose the view
+ // immediately after it is hidden.
+ for (IPerspectiveDescriptor pd : page.getOpenPerspectives())
+ {
+ if (!pd.equals(perspective))
+ {
+ page.setPerspective(pd);
+ }
+ }
+ page.setPerspective(perspective);
+ }
+ }
+ }
+ }
+
+ /**
+ * Class to implement IPartListener2 that you be used as ParListener2 of current page
+ * when the Emulator View is opened. It is necessary to determine when the view is
+ * closed.
+ */
+ private class PartListenerImpl implements IPartListener2
+ {
+
+ public void partActivated(IWorkbenchPartReference partRef)
+ {
+ // Nothing to do.
+ }
+
+ public void partBroughtToTop(IWorkbenchPartReference partRef)
+ {
+ // Nothing to do.
+ }
+
+ public void partClosed(final IWorkbenchPartReference partRef)
+ {
+
+ // if view that is being closed is not THIS view.
+ if (!partRef.getId().equals(getViewId()))
+ {
+ return;
+ }
+
+ // executed on async mode to avoid UI blocking
+ PlatformUI.getWorkbench().getDisplay().syncExec(new Runnable()
+ {
+ public void run()
+ {
+ boolean openedViewsExist = false;
+
+ for (String viewId : childrenIDs)
+ {
+ if (!getViewId().equals(viewId)
+ && (partRef.getPage().findView(viewId) != null))
+ {
+ openedViewsExist = true;
+ break;
+ }
+ }
+
+ // stops all viewers and clear the tabs list
+ Collection<IAndroidEmulatorInstance> instances = getInstancesWithViewer();
+ for (IAndroidEmulatorInstance instance : instances)
+ {
+ disposeViewer(instance);
+ }
+
+ // if the tool is not being closed and there is no other emulator
+ // view opened.
+ if (!closingOnShutdown && !openedViewsExist)
+ {
+
+ Collection<IAndroidEmulatorInstance> startedInstances =
+ DeviceFrameworkManager.getInstance().getAllStartedInstances();
+
+ boolean oneInstanceStarted = (startedInstances.size() > 0);
+
+ if (oneInstanceStarted
+ && (DialogWithToggleUtils
+ .showQuestion(
+ STOP_BY_CLOSING_VIEW_KEY_PREFERENCE,
+ EmulatorNLS.QUESTION_AndroidView_StopAllInstancesOnDisposeTitle,
+ EmulatorNLS.QUESTION_AndroidView_StopAllInstancesOnDisposeMessage)))
+ {
+
+ stopEmulatorInstances();
+ }
+ }
+
+ }
+
+ });
+ }
+
+ public void partDeactivated(IWorkbenchPartReference partRef)
+ {
+ // Nothing to do.
+ }
+
+ public void partHidden(IWorkbenchPartReference partRef)
+ {
+ // Nothing to do.
+ }
+
+ public void partInputChanged(IWorkbenchPartReference partRef)
+ {
+ // Nothing to do.
+ }
+
+ public void partOpened(IWorkbenchPartReference partRef)
+ {
+ // Nothing to do.
+ }
+
+ public void partVisible(IWorkbenchPartReference partRef)
+ {
+ if (partRef.getId().equals(getViewId()))
+ {
+ refreshView();
+ }
+ }
+ }
+
+ /**
+ * Class to implement the IWorkbenchListener that you be used as WorkbenchListener of
+ * workbench when the Emulator View is opened. It is used to know if the view
+ * is being closed due to workbench shutdown.
+ */
+ private class WorkbenchListenerImpl implements IWorkbenchListener
+ {
+
+ public void postShutdown(IWorkbench workbench)
+ {
+ // Nothing to do.
+ }
+
+ public boolean preShutdown(IWorkbench workbench, boolean forced)
+ {
+ closingOnShutdown = true;
+
+ Collection<IAndroidEmulatorInstance> startedInstances =
+ DeviceFrameworkManager.getInstance().getAllStartedInstances();
+
+ if (startedInstances.size() > 0)
+ {
+
+ boolean stopEmulatorInstances = false;
+ if (PluginUtils.getOS() != PluginUtils.OS_LINUX)
+ {
+ stopEmulatorInstances =
+ DialogWithToggleUtils.showQuestion(
+ STOP_ALL_EMULATORS_IN_SHUTDOWN_KEY_PREFERENCE,
+ EmulatorNLS.QUESTION_RunningInstancesOnClose_Title,
+ EmulatorNLS.QUESTION_RunningInstancesOnClose_Text);
+ }
+ else
+ {
+ DialogWithToggleUtils.showWarning(
+ STOP_ALL_EMULATORS_IN_SHUTDOWN_KEY_PREFERENCE,
+ EmulatorNLS.WARN_RunningInstancesOnClose_Linux_Title,
+ EmulatorNLS.WARN_RunningInstancesOnClose_Linux_Text);
+ //stopEmulatorInstances = true;
+ }
+
+ if (stopEmulatorInstances)
+ {
+ stopEmulatorInstances();
+ }
+
+ }
+
+ return true;
+ }
+ }
+
+ /**
+ * Selects the tab that has this data host and set the activeHost
+ * @param host IP address
+ */
+ private void setSelection(final String host)
+ {
+
+ PlatformUI.getWorkbench().getDisplay().syncExec(new Runnable()
+ {
+ public void run()
+ {
+ TabItem selectedTab = null;
+
+ TabItem[] tabArray = tabFolder.getItems();
+
+ for (TabItem tabItem : tabArray)
+ {
+ String tabItemHost =
+ ((IAndroidEmulatorInstance) tabItem.getData()).getInstanceIdentifier();
+ if ((host != null) && (host.equals(tabItemHost)))
+ {
+ selectedTab = tabItem;
+ break;
+ }
+ }
+
+ if (selectedTab != null)
+ {
+ tabFolder.setSelection(selectedTab);
+ updateMenuAndToolbars();
+ }
+
+ }
+ });
+
+ }
+
+ public static void updateInstanceName(final IInstance instance)
+ {
+ PlatformUI.getWorkbench().getDisplay().syncExec(new Runnable()
+ {
+ public void run()
+ {
+ if (!childrenIDs.isEmpty())
+ {
+ AbstractAndroidView view =
+ (AbstractAndroidView) EclipseUtils.getActiveView(childrenIDs.get(0));
+ if (view != null)
+ {
+ if ((instance != null))
+ {
+ TabItem tabItem = view.getTabItem(instance);
+ if (tabItem != null)
+ {
+ tabItem.setText(((IAndroidEmulatorInstance) tabItem.getData())
+ .getFullName());
+ }
+ }
+ }
+ }
+ }
+ });
+ }
+
+ /**
+ * Sets the skin zoom factor
+ *
+ * @param instance the emulator instance
+ * @param zoom the zoom factor
+ */
+ public final void setZoomFactor(IAndroidEmulatorInstance instance, double zoom)
+ {
+ try
+ {
+ AndroidViewData viewData = instanceDataMap.get(instance);
+ if (viewData != null)
+ {
+ IAndroidComposite composite = viewData.getComposite();
+ if (composite != null)
+ {
+ composite.setZoomFactor(zoom);
+ }
+ }
+ }
+ catch (Exception e)
+ {
+ error("Detached zoom could not be set.");
+ }
+ }
+
+ /**
+ * Gets the skin zoom factor
+ *
+ * @param instance the emulator instance
+ * @return the zoom factor
+ */
+ public final double getZoomFactor(IAndroidEmulatorInstance instance)
+ {
+ double zoomFactor = 0.0;
+ AndroidViewData viewData = instanceDataMap.get(instance);
+ if (viewData != null)
+ {
+ IAndroidComposite composite = viewData.getComposite();
+ if (composite != null)
+ {
+ zoomFactor = composite.getZoomFactor();
+ }
+ }
+ return zoomFactor;
+ }
+} \ No newline at end of file
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/view/AndroidView.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/view/AndroidView.java
new file mode 100644
index 0000000..2a05611
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/view/AndroidView.java
@@ -0,0 +1,265 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.emulator.ui.view;
+
+import static com.motorola.studio.android.common.log.StudioLogger.error;
+
+import org.eclipse.sequoyah.vnc.protocol.lib.ProtocolHandle;
+import org.eclipse.sequoyah.vnc.vncviewer.config.IPropertiesFileHandler;
+import org.eclipse.sequoyah.vnc.vncviewer.config.VNCConfiguration;
+import org.eclipse.sequoyah.vnc.vncviewer.graphics.swt.ISWTPainter;
+import org.eclipse.sequoyah.vnc.vncviewer.graphics.swt.SWTRemoteDisplay;
+import org.eclipse.sequoyah.vnc.vncviewer.graphics.swt.img.SWTRemoteDisplayImg;
+import org.eclipse.sequoyah.vnc.vncviewer.network.VNCProtocolData;
+import org.eclipse.sequoyah.vnc.vncviewer.registry.VNCProtocolRegistry;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.ScrolledComposite;
+import org.eclipse.swt.events.DisposeEvent;
+import org.eclipse.swt.events.DisposeListener;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.layout.FillLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.TabItem;
+import org.eclipse.ui.IViewSite;
+import org.eclipse.ui.commands.ICommandService;
+
+import com.motorola.studio.android.common.utilities.EclipseUtils;
+import com.motorola.studio.android.emulator.EmulatorPlugin;
+import com.motorola.studio.android.emulator.core.exception.InstanceStopException;
+import com.motorola.studio.android.emulator.core.exception.SkinException;
+import com.motorola.studio.android.emulator.core.model.IAndroidEmulatorInstance;
+import com.motorola.studio.android.emulator.core.skin.IAndroidSkin;
+import com.motorola.studio.android.emulator.core.skin.ISkinKeyXmlTags;
+import com.motorola.studio.android.emulator.i18n.EmulatorNLS;
+import com.motorola.studio.android.emulator.ui.controls.IAndroidComposite;
+import com.motorola.studio.android.emulator.ui.controls.maindisplay.MainDisplayComposite;
+import com.motorola.studio.android.emulator.ui.controls.nativewindow.NativeWindowComposite;
+import com.motorola.studio.android.emulator.ui.controls.skin.SkinComposite;
+import com.motorola.studio.android.emulator.ui.handlers.IHandlerConstants;
+import com.motorola.studio.android.nativeos.IDevicePropertiesOSConstants;
+import com.motorola.studio.android.nativeos.NativeUIUtils;
+
+/**
+ * DESCRIPTION: This class represents the Android Emulator view
+ *
+ * RESPONSIBILITY: - Show the skin to the end user
+ *
+ * COLABORATORS: None.
+ *
+ * USAGE: All the public interface is extended from
+ * <code>AbstractAndroidView</code>.
+ */
+public class AndroidView extends AbstractAndroidView
+{
+ /**
+ * The Android Emulator view ID
+ */
+ public static final String ANDROID_VIEW_ID = EmulatorPlugin.PLUGIN_ID + ".androidView";
+
+ /**
+ * @see com.motorola.studio.android.emulator.ui.view.AbstractAndroidView#getViewId()
+ */
+ @Override
+ protected String getViewId()
+ {
+ return ANDROID_VIEW_ID;
+ }
+
+ /**
+ * Creates the skin composite, main display and CLI display related to
+ * emulator.
+ *
+ * @param tab
+ * the tab item that will hold the graphical elements that
+ * represents the emulator
+ * @param instance
+ * the emulator instance
+ * @param emulatorData
+ * the object to be defined with the elements created.
+ */
+ @Override
+ protected void createWidgets(TabItem tab, final IAndroidEmulatorInstance instance,
+ final AndroidViewData tabData) throws SkinException
+ {
+ tabData.loadSkin(instance);
+ IAndroidSkin skin = tabData.getSkin();
+
+ ProtocolHandle handle = instance.getProtocolHandle();
+ VNCProtocolData data = VNCProtocolRegistry.getInstance().get(handle);
+
+ final SWTRemoteDisplay mainDisplay;
+ Composite parentComposite;
+ if (instance.getProperties().getProperty(IDevicePropertiesOSConstants.useVnc,
+ NativeUIUtils.getDefaultUseVnc()).toString().equals("true"))
+ {
+ if (data != null)
+ {
+ if (instance.getProperties().getProperty("Command_Line").contains("-no-skin"))
+ {
+ int baseWidth =
+ skin.getSkinBean(instance.getCurrentLayout()).getSkinPropertyValue(
+ ISkinKeyXmlTags.SKIN_INTERNAL_VIEW_WIDTH);
+ int baseHeight =
+ skin.getSkinBean(instance.getCurrentLayout()).getSkinPropertyValue(
+ ISkinKeyXmlTags.SKIN_INTERNAL_VIEW_HEIGHT);
+
+ ScrolledComposite scrolledComposite =
+ new ScrolledComposite(tab.getParent(), SWT.H_SCROLL | SWT.V_SCROLL
+ | SWT.BACKGROUND);
+
+ final MainDisplayComposite composite =
+ new MainDisplayComposite(scrolledComposite, SWT.BACKGROUND, baseWidth,
+ baseHeight, instance);
+ composite.setLayout(new FillLayout());
+
+ mainDisplay =
+ createMainDisplay(composite, skin, instance, (ISWTPainter) data
+ .getVncPainter());
+ composite.setSize(baseWidth, baseHeight);
+
+ scrolledComposite.setContent(composite);
+ tab.setControl(scrolledComposite);
+
+ scrolledComposite.addDisposeListener(new DisposeListener()
+ {
+ public void widgetDisposed(DisposeEvent e)
+ {
+ composite.dispose();
+ }
+ });
+
+ tabData.setCliDisplay(null);
+ tabData.setComposite(composite);
+ tabData.setMainDisplay(mainDisplay);
+ }
+ else
+ {
+ parentComposite = createSkinComposite(tab.getParent(), skin, instance);
+ mainDisplay =
+ createMainDisplay(parentComposite, skin, instance, (ISWTPainter) data
+ .getVncPainter());
+
+ tabData.setComposite((IAndroidComposite) parentComposite);
+ tabData.setMainDisplay(mainDisplay);
+ tab.setControl(parentComposite);
+ }
+ }
+ else
+ {
+ error("The protocol object set in the device instance is not supported. Stopping the emulator instance...");
+ EclipseUtils.showErrorDialog(EmulatorNLS.GEN_Error,
+ EmulatorNLS.ERR_AndroidView_ProtocolImplementerNotSupported);
+
+ try
+ {
+ instance.stop(true);
+ }
+ catch (InstanceStopException e)
+ {
+ error("Error while running service for stopping virtual machine");
+ EclipseUtils.showErrorDialog(EmulatorNLS.GEN_Error,
+ EmulatorNLS.EXC_General_CannotRunStopService);
+ }
+ }
+
+ }
+ else
+ {
+ parentComposite = createNativeWindowComposite(tab.getParent(), skin, instance);
+ tabData.setComposite((IAndroidComposite) parentComposite);
+ tab.setControl(parentComposite);
+ }
+ }
+
+ /**
+ * Creates the skin composite used by this instance
+ *
+ * @param parent
+ * The parent composite where to place the widgets
+ * @param skin
+ * The object containing skin information used to draw the
+ * widgets
+ */
+ private SkinComposite createSkinComposite(Composite parent, IAndroidSkin skin,
+ IAndroidEmulatorInstance instance)
+ {
+
+ SkinComposite skinComposite = new SkinComposite(parent, skin, instance);
+ skinComposite.setBackground(new Color(skinComposite.getDisplay(), 255, 255, 255));
+
+ return skinComposite;
+ }
+
+ private NativeWindowComposite createNativeWindowComposite(Composite parent, IAndroidSkin skin,
+ IAndroidEmulatorInstance instance)
+ {
+ NativeWindowComposite nativeWindowComposite =
+ new NativeWindowComposite(parent, skin, instance);
+ // nativeWindowComposite.setBackground(new Color(nativeWindowComposite.getDisplay(), 255, 255,
+ // 255));
+
+ return nativeWindowComposite;
+ }
+
+ /**
+ * Creates the main display object used by this instance.
+ *
+ * @param parent
+ * The parent composite where to place the widgets
+ * @param skin
+ * The object containing skin information used to draw the
+ * widgets
+ * @param instance
+ * The emulator instance
+ * @param painter
+ * the painter used to draw the images
+ */
+ private SWTRemoteDisplay createMainDisplay(Composite parent, IAndroidSkin skin,
+ IAndroidEmulatorInstance instance, ISWTPainter painter)
+
+ {
+ // gets and reads the configuration files of the VNC session
+ IPropertiesFileHandler handler = IAndroidSkin.DEFAULT_PROPS_HANDLER;
+ String configFile = IAndroidSkin.DEFAULT_VNC_CONFIG_FILE;
+
+ VNCConfiguration config = new VNCConfiguration(configFile, handler);
+ final SWTRemoteDisplay mainDisplay;
+ mainDisplay =
+ new SWTRemoteDisplayImg(parent, config.getConfigurationProperties(), handler,
+ painter);
+
+ mainDisplay.setBackground(new Color(mainDisplay.getDisplay(), 0, 0, 0));
+ mainDisplay.setLayout(new FillLayout());
+
+ return mainDisplay;
+ }
+
+ /**
+ * Forces the refreshing of the menu elements.
+ */
+ @Override
+ protected void refreshMenuElements()
+ {
+ IViewSite viewSite = getViewSite();
+
+ // Update the radio button selection in the zoom menu
+ ICommandService service = (ICommandService) viewSite.getService(ICommandService.class);
+ service.refreshElements(IHandlerConstants.CHANGE_EMULATOR_ORIENTATION_COMMAND, null);
+ service.refreshElements(IHandlerConstants.CHANGE_EMULATOR_ZOOM_COMMAND, null);
+
+ }
+
+} \ No newline at end of file
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/view/AndroidViewData.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/view/AndroidViewData.java
new file mode 100644
index 0000000..1116bb4
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/view/AndroidViewData.java
@@ -0,0 +1,167 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.emulator.ui.view;
+
+import static com.motorola.studio.android.common.log.StudioLogger.debug;
+
+import java.io.File;
+import java.util.Collection;
+
+import org.eclipse.sequoyah.vnc.vncviewer.graphics.swt.SWTRemoteDisplay;
+import org.eclipse.swt.events.DisposeListener;
+
+import com.motorola.studio.android.emulator.core.exception.SkinException;
+import com.motorola.studio.android.emulator.core.model.IAndroidEmulatorInstance;
+import com.motorola.studio.android.emulator.core.skin.IAndroidSkin;
+import com.motorola.studio.android.emulator.core.skin.SkinFramework;
+import com.motorola.studio.android.emulator.ui.controls.IAndroidComposite;
+import com.motorola.studio.android.emulator.ui.controls.RemoteCLIDisplay;
+
+/**
+ * AndroidViewData: this class is responsible for having the information about Main Display,
+ * CLI Display and the composite where these displays will be drawn.
+ */
+public class AndroidViewData
+{
+ /**
+ * Implementation of the
+ */
+ private IAndroidSkin skin;
+
+ /**
+ * Composite that shows the contents of the mobile main display
+ */
+ private SWTRemoteDisplay mainDisplay;
+
+ /**
+ * Composite that shows the contents of the mobile CLI display
+ */
+ private RemoteCLIDisplay cliDisplay;
+
+ /**
+ * Composite for view's components.
+ */
+ private IAndroidComposite composite;
+
+ /**
+ * Dispose listener of this instance
+ */
+ private DisposeListener disposeListener;
+
+ /**
+ * Loads the Android emulator skin of the given the AVD/instance
+ * @param instance whose skin will be loaded
+ * @throws SkinException if it is no possible to load the skin
+ */
+ public synchronized void loadSkin(IAndroidEmulatorInstance instance) throws SkinException
+ {
+ String skinId = instance.getSkinId();
+ SkinFramework skinFw = new SkinFramework();
+ File skinPath = instance.getSkinPath();
+ skin = skinFw.getSkinById(skinId, skinPath);
+ Collection<String> layoutNames = skin.getAvailableLayouts();
+ String currentLayout = instance.getCurrentLayout();
+ if ((currentLayout == null) && (!layoutNames.isEmpty()))
+ {
+ String firstLayout = layoutNames.iterator().next();
+ instance.setCurrentLayout(firstLayout);
+ debug("The skin has multiple layouts. Setting " + firstLayout + " as the current one.");
+ }
+
+ }
+
+ /**
+ * Retrieves the loaded IAndroidSkin.
+ * Returns null if no skin is loaded.
+ * @return
+ */
+ public synchronized IAndroidSkin getSkin()
+ {
+ return skin;
+ }
+
+ /**
+ * Gets the dispose listener
+ * @return dispose listener
+ */
+ DisposeListener getDisposeListener()
+ {
+ return disposeListener;
+ }
+
+ /**
+ * Sets the dispose listener
+ * @param disposeListener dispose listener
+ */
+ void setDisposeListener(DisposeListener disposeListener)
+ {
+ this.disposeListener = disposeListener;
+ }
+
+ /**
+ * Gets Main Display
+ * @return main display
+ */
+ public SWTRemoteDisplay getMainDisplay()
+ {
+ return mainDisplay;
+ }
+
+ /**
+ * Sets main Display
+ * @param mainDisplay main display
+ */
+ void setMainDisplay(SWTRemoteDisplay mainDisplay)
+ {
+ this.mainDisplay = mainDisplay;
+ }
+
+ /**
+ * Gets CLI Display
+ * @return CLI display
+ */
+ RemoteCLIDisplay getCliDisplay()
+ {
+ return cliDisplay;
+ }
+
+ /**
+ * Sets CLI Display
+ * @param cliDisplay CLI display
+ */
+ void setCliDisplay(RemoteCLIDisplay cliDisplay)
+ {
+ this.cliDisplay = cliDisplay;
+ }
+
+ /**
+ * Gets view composite
+ * @return composite
+ */
+ public IAndroidComposite getComposite()
+ {
+ return composite;
+ }
+
+ /**
+ * Sets view composite
+ * @param composite composite
+ */
+ void setComposite(IAndroidComposite composite)
+ {
+ this.composite = composite;
+ }
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/view/LayoutContributionItem.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/view/LayoutContributionItem.java
new file mode 100644
index 0000000..640ee37
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/view/LayoutContributionItem.java
@@ -0,0 +1,143 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.emulator.ui.view;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.jface.action.IContributionItem;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.actions.CompoundContributionItem;
+import org.eclipse.ui.menus.CommandContributionItem;
+import org.eclipse.ui.menus.CommandContributionItemParameter;
+
+import com.motorola.studio.android.common.utilities.EclipseUtils;
+import com.motorola.studio.android.emulator.EmulatorPlugin;
+import com.motorola.studio.android.emulator.core.model.IAndroidEmulatorInstance;
+import com.motorola.studio.android.emulator.core.skin.IAndroidSkin;
+import com.motorola.studio.android.emulator.i18n.EmulatorNLS;
+import com.motorola.studio.android.emulator.ui.handlers.IHandlerConstants;
+
+/**
+ * This class builds dynamically the Emulator Layouts submenu from the Android Emulator and
+ * Main Display views
+ **/
+public class LayoutContributionItem extends CompoundContributionItem
+{
+ // Layout dynamic constants
+ public static final String ANDROID_VIEW_LAYOUT_DYNAMIC_ID = "androidView.layout.dynamic";
+
+ public static final String MAIN_DISPLAY_VIEW_LAYOUT_DYNAMIC_ID =
+ "mainDisplayView.layout.dynamic";
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.ui.actions.CompoundContributionItem#getContributionItems()
+ */
+ @Override
+ protected IContributionItem[] getContributionItems()
+ {
+ IContributionItem[] itemsArray;
+
+ AbstractAndroidView view = null;
+ String viewId = null;
+ if (getId().equals(ANDROID_VIEW_LAYOUT_DYNAMIC_ID))
+ {
+ viewId = AndroidView.ANDROID_VIEW_ID;
+ view = (AbstractAndroidView) EclipseUtils.getActiveView(viewId);
+ }
+ else if (getId().equals(MAIN_DISPLAY_VIEW_LAYOUT_DYNAMIC_ID))
+ {
+ viewId = MainDisplayView.EMULATOR_MAIN_DISPLAY_VIEW_ID;
+ view = (AbstractAndroidView) EclipseUtils.getActiveView(viewId);
+ }
+
+ if (view != null)
+ {
+ IAndroidEmulatorInstance instance = AbstractAndroidView.getActiveInstance();
+ IAndroidSkin skin = view.getSkin(instance);
+ if (skin != null)
+ {
+ Collection<String> layoutNames = skin.getAvailableLayouts();
+ itemsArray = new IContributionItem[layoutNames.size()];
+ populateContributionList(itemsArray, layoutNames, viewId);
+ }
+ else
+ {
+ itemsArray = new IContributionItem[1];
+ populateWithEmpty(itemsArray);
+ }
+ }
+ else
+ {
+ itemsArray = new IContributionItem[1];
+ populateWithEmpty(itemsArray);
+ }
+
+ return itemsArray;
+ }
+
+ /**
+ * Populates the array with a command item per layout name
+ *
+ * @param itemsArray The array to be populated
+ * @param layoutNames The items to be included at the array
+ * @param viewId The view that is active at the moment
+ */
+ @SuppressWarnings({
+ "rawtypes", "unchecked"
+ })
+ private void populateContributionList(IContributionItem[] itemsArray,
+ Collection<String> layoutNames, String viewId)
+ {
+ int i = 0;
+ for (String layoutName : layoutNames)
+ {
+ Map params = new HashMap();
+ params.put(IHandlerConstants.ACTIVE_VIEW_PARAMETER, viewId);
+
+ String id = EmulatorPlugin.PLUGIN_ID + ".layoutcmd." + layoutName;
+ IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
+ CommandContributionItemParameter itemParam =
+ new CommandContributionItemParameter(window, id,
+ IHandlerConstants.CHANGE_EMULATOR_ORIENTATION_COMMAND, params, null,
+ null, null, layoutName, null, null,
+ CommandContributionItem.STYLE_RADIO, null, true);
+ itemsArray[i++] = new CommandContributionItem(itemParam);
+ }
+ }
+
+ /**
+ * Populates the array with a single disabled command, indicating that there
+ * are no layouts to choose
+ *
+ * @param itemsArray The array to be populated
+ */
+ @SuppressWarnings("rawtypes")
+ private void populateWithEmpty(IContributionItem[] itemsArray)
+ {
+ String id = EmulatorPlugin.PLUGIN_ID + ".emptylayout";
+ IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
+ CommandContributionItemParameter itemParam =
+ new CommandContributionItemParameter(window, id,
+ IHandlerConstants.CHANGE_EMULATOR_ORIENTATION_COMMAND, new HashMap(), null,
+ null, null, EmulatorNLS.UI_LayoutContributionItem_NoLayoutsAvailable, null,
+ null, CommandContributionItem.STYLE_RADIO, null, false);
+ itemsArray[0] = new CommandContributionItem(itemParam);
+ }
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/view/MainDisplayView.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/view/MainDisplayView.java
new file mode 100644
index 0000000..2280b21
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/view/MainDisplayView.java
@@ -0,0 +1,224 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.emulator.ui.view;
+
+import static com.motorola.studio.android.common.log.StudioLogger.error;
+
+import org.eclipse.sequoyah.vnc.protocol.lib.ProtocolHandle;
+import org.eclipse.sequoyah.vnc.vncviewer.config.IPropertiesFileHandler;
+import org.eclipse.sequoyah.vnc.vncviewer.config.VNCConfiguration;
+import org.eclipse.sequoyah.vnc.vncviewer.graphics.swt.ISWTPainter;
+import org.eclipse.sequoyah.vnc.vncviewer.graphics.swt.SWTRemoteDisplay;
+import org.eclipse.sequoyah.vnc.vncviewer.graphics.swt.img.SWTRemoteDisplayImg;
+import org.eclipse.sequoyah.vnc.vncviewer.network.VNCProtocolData;
+import org.eclipse.sequoyah.vnc.vncviewer.registry.VNCProtocolRegistry;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.ScrolledComposite;
+import org.eclipse.swt.events.DisposeEvent;
+import org.eclipse.swt.events.DisposeListener;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.layout.FillLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.TabItem;
+import org.eclipse.ui.IViewSite;
+import org.eclipse.ui.commands.ICommandService;
+
+import com.motorola.studio.android.common.utilities.EclipseUtils;
+import com.motorola.studio.android.emulator.EmulatorPlugin;
+import com.motorola.studio.android.emulator.core.exception.InstanceStopException;
+import com.motorola.studio.android.emulator.core.exception.SkinException;
+import com.motorola.studio.android.emulator.core.model.IAndroidEmulatorInstance;
+import com.motorola.studio.android.emulator.core.skin.IAndroidSkin;
+import com.motorola.studio.android.emulator.core.skin.ISkinKeyXmlTags;
+import com.motorola.studio.android.emulator.i18n.EmulatorNLS;
+import com.motorola.studio.android.emulator.ui.IUIHelpConstants;
+import com.motorola.studio.android.emulator.ui.controls.maindisplay.MainDisplayComposite;
+import com.motorola.studio.android.emulator.ui.handlers.IHandlerConstants;
+
+/**
+ * This class represents the view that shows the Main Display (without skin)
+ * of the emulator to the end user.
+ */
+public class MainDisplayView extends AbstractAndroidView
+{
+
+ /**
+ * The Android Emulator Main Display View view ID
+ */
+ public static final String EMULATOR_MAIN_DISPLAY_VIEW_ID =
+ EmulatorPlugin.PLUGIN_ID + ".mainDisplayView";
+
+ /**
+ * @see com.motorola.studio.android.emulator.ui.view.AbstractAndroidView#getViewId()
+ */
+ @Override
+ public String getViewId()
+ {
+ return EMULATOR_MAIN_DISPLAY_VIEW_ID;
+ }
+
+ /**
+ * @see com.motorola.studio.android.emulator.ui.view.AbstractAndroidView#getHelpId()
+ */
+ @Override
+ protected String getHelpId()
+ {
+ return IUIHelpConstants.EMULATOR_VIEW_MAIN_DISPLAY_HELP;
+ }
+
+ /**
+ * Creates the scrolled composite, the detached composite and the main
+ * display related to emulator.
+ *
+ * @param tab the tab item that will hold the graphical elements that
+ * represents the emulator
+ * @param instance the emulator instance
+ * @param emulatorData the object to be defined with the elements created.
+ */
+ @Override
+ protected void createWidgets(TabItem tab, final IAndroidEmulatorInstance instance,
+ final AndroidViewData tabData)
+ {
+ try
+ {
+ tabData.loadSkin(instance);
+ IAndroidSkin skin = tabData.getSkin();
+
+ ProtocolHandle handle = instance.getProtocolHandle();
+ VNCProtocolData data = VNCProtocolRegistry.getInstance().get(handle);
+ if (data != null)
+ {
+
+ int baseWidth =
+ skin.getSkinBean(instance.getCurrentLayout()).getSkinPropertyValue(
+ ISkinKeyXmlTags.SKIN_INTERNAL_VIEW_WIDTH);
+ int baseHeight =
+ skin.getSkinBean(instance.getCurrentLayout()).getSkinPropertyValue(
+ ISkinKeyXmlTags.SKIN_INTERNAL_VIEW_HEIGHT);
+
+ ScrolledComposite scrolledComposite =
+ new ScrolledComposite(tab.getParent(), SWT.H_SCROLL | SWT.V_SCROLL
+ | SWT.BACKGROUND);
+
+ final MainDisplayComposite composite =
+ new MainDisplayComposite(scrolledComposite, SWT.BACKGROUND, baseWidth,
+ baseHeight, instance);
+ composite.setLayout(new FillLayout());
+
+ final SWTRemoteDisplay mainDisplay =
+ createMainDisplay(composite, skin, instance, (ISWTPainter) data
+ .getVncPainter());
+ composite.setSize(baseWidth, baseHeight);
+
+ scrolledComposite.setContent(composite);
+ tab.setControl(scrolledComposite);
+
+ scrolledComposite.addDisposeListener(new DisposeListener()
+ {
+ public void widgetDisposed(DisposeEvent e)
+ {
+ composite.dispose();
+ }
+ });
+
+ tabData.setCliDisplay(null);
+ tabData.setComposite(composite);
+ tabData.setMainDisplay(mainDisplay);
+ }
+ else
+ {
+ error("The protocol object set in the device instance is not supported. Stopping the emulator instance...");
+ EclipseUtils.showErrorDialog(EmulatorNLS.GEN_Error,
+ EmulatorNLS.ERR_AndroidView_ProtocolImplementerNotSupported);
+
+ try
+ {
+ instance.stop(true);
+ }
+ catch (InstanceStopException e)
+ {
+ error("Error while running service for stopping virtual machine");
+ EclipseUtils.showErrorDialog(EmulatorNLS.GEN_Error,
+ EmulatorNLS.EXC_General_CannotRunStopService);
+ }
+ }
+ }
+ catch (SkinException e)
+ {
+ error("The skin associated to this instance (" + instance.getName()
+ + ") is not installed or is corrupted.");
+ EclipseUtils.showErrorDialog(e);
+
+ try
+ {
+ instance.stop(true);
+ }
+ catch (InstanceStopException e1)
+ {
+ error("Error while running service for stopping virtual machine");
+ EclipseUtils.showErrorDialog(EmulatorNLS.GEN_Error,
+ EmulatorNLS.EXC_General_CannotRunStopService);
+ }
+ }
+
+ }
+
+ /**
+ * Creates the main display used by this instance
+ *
+ * @param parent The parent composite where to place the widgets
+ * @param skin The object containing skin information used to draw the widgets
+ * @param instance the emulator instance
+ * @param painter the painter used to draw the image
+ */
+ private SWTRemoteDisplay createMainDisplay(Composite parent, IAndroidSkin skin,
+ IAndroidEmulatorInstance instance, ISWTPainter painter)
+ {
+
+ // gets and reads the configuration files of the VNC session
+ IPropertiesFileHandler handler;
+ String configFile;
+
+ handler = IAndroidSkin.DEFAULT_PROPS_HANDLER;
+ configFile = IAndroidSkin.DEFAULT_VNC_CONFIG_FILE;
+
+ VNCConfiguration config = new VNCConfiguration(configFile, handler);
+
+ SWTRemoteDisplay mainDisplay =
+ new SWTRemoteDisplayImg(parent, config.getConfigurationProperties(), handler,
+ painter);
+ mainDisplay.setBackground(new Color(mainDisplay.getDisplay(), 0, 0, 0));
+ mainDisplay.setLayout(new FillLayout());
+
+ return mainDisplay;
+ }
+
+ /**
+ * Forces the refreshing of the menu elements.
+ */
+ @Override
+ protected void refreshMenuElements()
+ {
+ IViewSite viewSite = getViewSite();
+
+ // Update the radio button selection in the zoom menu
+ ICommandService service = (ICommandService) viewSite.getService(ICommandService.class);
+ service.refreshElements(IHandlerConstants.CHANGE_EMULATOR_ZOOM_COMMAND, null);
+ service.refreshElements(IHandlerConstants.CHANGE_EMULATOR_ORIENTATION_COMMAND, null);
+
+ }
+
+} \ No newline at end of file
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator10/StartAndroidEmulatorLogic.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator10/StartAndroidEmulatorLogic.java
new file mode 100644
index 0000000..089dd06
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator10/StartAndroidEmulatorLogic.java
@@ -0,0 +1,117 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.emulator10;
+
+import java.io.File;
+import java.util.Collection;
+import java.util.LinkedList;
+
+import org.eclipse.core.runtime.jobs.IJobChangeEvent;
+import org.eclipse.core.runtime.jobs.JobChangeAdapter;
+
+import com.motorola.studio.android.adt.SdkUtils;
+import com.motorola.studio.android.common.utilities.PluginUtils;
+import com.motorola.studio.android.emulator.EmulatorPlugin;
+import com.motorola.studio.android.emulator.logic.AbstractStartAndroidEmulatorLogic;
+import com.motorola.studio.android.emulator.logic.ConnectVncLogic;
+import com.motorola.studio.android.emulator.logic.ForwardVncPortLogic;
+import com.motorola.studio.android.emulator.logic.IAndroidLogic;
+import com.motorola.studio.android.emulator.logic.IAndroidLogicInstance;
+import com.motorola.studio.android.emulator.logic.StartEmulatorProcessLogic;
+import com.motorola.studio.android.emulator.logic.StartVncServerLogic;
+import com.motorola.studio.android.emulator.logic.TransferFilesLogic;
+
+public class StartAndroidEmulatorLogic extends AbstractStartAndroidEmulatorLogic
+{
+ @SuppressWarnings("incomplete-switch")
+ @Override
+ public Collection<IAndroidLogic> getLogicCollection(IAndroidLogicInstance instance,
+ LogicMode mode)
+ {
+ // When starting, all steps must be done. When restarting, only VNC server launch
+ // step will be performed.
+ Collection<IAndroidLogic> logicCollection = new LinkedList<IAndroidLogic>();
+
+ switch (mode)
+ {
+ case START_MODE:
+ logicCollection.add(new StartEmulatorProcessLogic());
+ break;
+ case TRANSFER_AND_CONNECT_VNC:
+ logicCollection.add(createTransferFilesLogic());
+ logicCollection.add(new ForwardVncPortLogic());
+ StartVncServerLogic startVncServerLogic = createStartVncServerLogic();
+ logicCollection.add(startVncServerLogic);
+ logicCollection.add(getConnectVncClientLogic(startVncServerLogic));
+ break;
+ case RESTART_VNC_SERVER:
+ logicCollection.add(createTransferFilesLogic());
+ logicCollection.add(new ForwardVncPortLogic());
+ logicCollection.add(createStartVncServerLogic());
+ break;
+ }
+
+ return logicCollection;
+ }
+
+ private String getResourceDir()
+ {
+ String resDir = "res";
+ if (SdkUtils.isOphoneSDK())
+ {
+ resDir = "res_OPhone";
+ }
+
+ return resDir;
+ }
+
+ protected IAndroidLogic createTransferFilesLogic()
+ {
+ File localDirParent = PluginUtils.getPluginInstallationPath(EmulatorPlugin.getDefault());
+ File localDir = new File(localDirParent, getResourceDir());
+
+ TransferFilesLogic transferLogic = new TransferFilesLogic();
+ transferLogic.setLocalDir(localDir.getAbsolutePath());
+ transferLogic.setRemoteDir("/data/local");
+ transferLogic.addFilename("fbvncserver");
+ return transferLogic;
+ }
+
+ protected StartVncServerLogic createStartVncServerLogic()
+ {
+ StartVncServerLogic logic = new StartVncServerLogic();
+ logic.addRemoteCommand("chmod 700 /data/local/fbvncserver");
+ logic.addRemoteCommand("/data/local/fbvncserver");
+ return logic;
+ }
+
+ protected IAndroidLogic getConnectVncClientLogic(StartVncServerLogic startVncServerLogic)
+ {
+ final ConnectVncLogic startVncClientLogic = new ConnectVncLogic();
+
+ startVncServerLogic.addVncServerJobListener(new JobChangeAdapter()
+ {
+ @Override
+ public void done(IJobChangeEvent ijobchangeevent)
+ {
+ startVncClientLogic.setVncServerDoneEvent(ijobchangeevent);
+ }
+ });
+
+ return startVncClientLogic;
+ }
+
+}
diff --git a/src/plugins/handset/.classpath b/src/plugins/handset/.classpath
new file mode 100644
index 0000000..8a8f166
--- /dev/null
+++ b/src/plugins/handset/.classpath
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>
+ <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/src/plugins/handset/.project b/src/plugins/handset/.project
new file mode 100644
index 0000000..e2ebeeb
--- /dev/null
+++ b/src/plugins/handset/.project
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>com.motorola.studio.android.handset</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.ManifestBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.SchemaBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.pde.PluginNature</nature>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+</projectDescription>
diff --git a/src/plugins/handset/META-INF/MANIFEST.MF b/src/plugins/handset/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..5d536a5
--- /dev/null
+++ b/src/plugins/handset/META-INF/MANIFEST.MF
@@ -0,0 +1,20 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: %pluginName
+Bundle-SymbolicName: com.motorola.studio.android.handset;singleton:=true
+Bundle-Version: 5.0.0.qualifier
+Bundle-Activator: com.motorola.studio.android.handset.HandsetPlugin
+Bundle-Vendor: %providerName
+Require-Bundle: com.motorola.studio.android,
+ com.motorola.studio.android.common,
+ org.eclipse.ui,
+ org.eclipse.ui.console,
+ org.eclipse.core.runtime,
+ org.eclipse.core.resources,
+ org.eclipse.sequoyah.device.framework,
+ org.eclipse.sequoyah.device.common.utilities
+Bundle-RequiredExecutionEnvironment: JavaSE-1.6
+Bundle-ActivationPolicy: lazy
+Bundle-Localization: plugin
+Export-Package: com.motorola.studio.android.handset,
+ com.motorola.studio.android.handset.i18n
diff --git a/src/plugins/handset/build.properties b/src/plugins/handset/build.properties
new file mode 100644
index 0000000..bef8e62
--- /dev/null
+++ b/src/plugins/handset/build.properties
@@ -0,0 +1,7 @@
+source.. = src/
+output.. = bin/
+bin.includes = META-INF/,\
+ .,\
+ plugin.xml,\
+ plugin.properties,\
+ icons/
diff --git a/src/plugins/handset/icons/iniciate-icon-16x16.png b/src/plugins/handset/icons/iniciate-icon-16x16.png
new file mode 100644
index 0000000..9db0afc
--- /dev/null
+++ b/src/plugins/handset/icons/iniciate-icon-16x16.png
Binary files differ
diff --git a/src/plugins/handset/icons/plate16.gif b/src/plugins/handset/icons/plate16.gif
new file mode 100644
index 0000000..2b83024
--- /dev/null
+++ b/src/plugins/handset/icons/plate16.gif
Binary files differ
diff --git a/src/plugins/handset/icons/started-icon-16x16.png b/src/plugins/handset/icons/started-icon-16x16.png
new file mode 100644
index 0000000..38f4f9e
--- /dev/null
+++ b/src/plugins/handset/icons/started-icon-16x16.png
Binary files differ
diff --git a/src/plugins/handset/plugin.properties b/src/plugins/handset/plugin.properties
new file mode 100644
index 0000000..a459f7f
--- /dev/null
+++ b/src/plugins/handset/plugin.properties
@@ -0,0 +1,14 @@
+#################################################################################
+#
+# Android Handset Plugin properties
+#
+#################################################################################
+
+pluginName=MOTODEV Studio for Android Handset Plug-in
+providerName=Motorola Mobility, Inc.
+
+android.handset.name=Android Handset
+status.online=Online
+initServiceName=Automatically Move to Online
+android.handset.device.property.page.name=Android Handset Properties
+android.handset.device.properties.page.name=Device Properties \ No newline at end of file
diff --git a/src/plugins/handset/plugin.xml b/src/plugins/handset/plugin.xml
new file mode 100644
index 0000000..bec8c60
--- /dev/null
+++ b/src/plugins/handset/plugin.xml
@@ -0,0 +1,83 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?eclipse version="3.2"?>
+<plugin>
+ <extension
+ id="status.handsetonline"
+ point="org.eclipse.sequoyah.device.framework.status">
+ <status
+ canDeleteInstance="false"
+ canEditProperties="true"
+ id="com.motorola.studio.android.handset.status.handsetonline"
+ image="icons/started-icon-16x16.png"
+ name="%status.online">
+ </status>
+ </extension>
+ <extension
+ id="androidHandset"
+ name="androidHandset"
+ point="org.eclipse.sequoyah.device.framework.deviceTypes">
+ <deviceType
+ dropSupportHandler="com.motorola.studio.android.handset.HandsetDropSupportHandler"
+ handler="com.motorola.studio.android.handset.AndroidHandsetHandler"
+ icon="icons/plate16.gif"
+ id="com.motorola.studio.android.handset.androidHandset"
+ isAbstract="false"
+ isPersistent="false"
+ label="%android.handset.name"
+ name="%android.handset.name"
+ supportsUserInstances="false">
+ </deviceType>
+ </extension>
+ <extension
+ id="initHandsetService"
+ name="initHandsetService"
+ point="org.eclipse.sequoyah.device.framework.service">
+ <service
+ copyright="Copyright (C) 2012 The Android Open Source Project"
+ description="Service to initialize an Android Handset"
+ handler="com.motorola.studio.android.handset.DummyServiceHandler"
+ icon="icons/iniciate-icon-16x16.png"
+ id="com.motorola.studio.android.handset.initHandsetService"
+ name="%initServiceName"
+ provider="Motorola Mobility, Inc."
+ version="0.1.0"
+ visible="false">
+ </service>
+ </extension>
+ <extension
+ id="com.motorola.studio.android.handset.androidHandset"
+ name="%initServiceName"
+ point="org.eclipse.sequoyah.device.framework.serviceDefinition">
+ <service
+ id="com.motorola.studio.android.handset.initHandsetService">
+ <status
+ endId="com.motorola.studio.android.handset.status.handsetonline"
+ haltId="com.motorola.studio.android.handset.status.handsetonline"
+ startId="OFF">
+ </status></service>
+ </extension>
+ <extension
+ point="org.eclipse.ui.propertyPages">
+ <page
+ class="com.motorola.studio.android.handset.ui.AndroidPropertiesPage"
+ id="android.handset.device.property.page"
+ name="%android.handset.device.property.page.name">
+ <enabledWhen>
+ <instanceof
+ value="com.motorola.studio.android.handset.AndroidHandsetInstance">
+ </instanceof>
+ </enabledWhen>
+ </page>
+ <page
+ category="android.handset.device.property.page"
+ class="com.motorola.studio.android.handset.ui.DevicePropertiesPage"
+ id="android.handset.device.properties.page"
+ name="%android.handset.device.properties.page.name">
+ <enabledWhen>
+ <instanceof
+ value="com.motorola.studio.android.handset.AndroidHandsetInstance">
+ </instanceof>
+ </enabledWhen>
+ </page>
+ </extension>
+</plugin>
diff --git a/src/plugins/handset/src/com/motorola/studio/android/handset/AndroidHandsetHandler.java b/src/plugins/handset/src/com/motorola/studio/android/handset/AndroidHandsetHandler.java
new file mode 100644
index 0000000..fe78ac3
--- /dev/null
+++ b/src/plugins/handset/src/com/motorola/studio/android/handset/AndroidHandsetHandler.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.handset;
+
+import org.eclipse.sequoyah.device.framework.model.IDeviceLauncher;
+import org.eclipse.sequoyah.device.framework.model.IInstance;
+import org.eclipse.sequoyah.device.framework.model.handler.IDeviceHandler;
+
+/**
+ * DESCRIPTION:
+ * <br>
+ * This class represents a TmL IDeviceHandler for Android Handsets.
+ * <br>
+ * RESPONSIBILITY:
+ * <br>
+ * - Create an IInstance object for Android Handsets
+ * <br>
+ * COLABORATORS:
+ * <br>
+ * IDeviceHandler: implements this interface
+ * <br>
+ * USAGE:
+ * <br>
+ * This class is declared by the plugin.xml for the Android Handsets declaration.
+ */
+public class AndroidHandsetHandler implements IDeviceHandler
+{
+ public IInstance createDeviceInstance(String id)
+ {
+ IInstance instance = new AndroidHandsetInstance();
+ instance.setId(id);
+ return instance;
+ }
+
+ public IDeviceLauncher createDeviceLauncher(IInstance arg0)
+ {
+ return null;
+ }
+
+}
diff --git a/src/plugins/handset/src/com/motorola/studio/android/handset/AndroidHandsetInstance.java b/src/plugins/handset/src/com/motorola/studio/android/handset/AndroidHandsetInstance.java
new file mode 100644
index 0000000..65ccab2
--- /dev/null
+++ b/src/plugins/handset/src/com/motorola/studio/android/handset/AndroidHandsetInstance.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.handset;
+
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.sequoyah.device.framework.model.AbstractMobileInstance;
+import org.eclipse.ui.model.IWorkbenchAdapter;
+
+import com.motorola.studio.android.adt.ISerialNumbered;
+
+/**
+ * DESCRIPTION: <br>
+ * This class represents a handset TmL instance <br>
+ * RESPONSIBILITY: <br>
+ * Keep handset data/properties <br>
+ * COLABORATORS: <br>
+ * None <br>
+ * USAGE: <br>
+ * This class is declared by the plugin.xml for the Android Handsets
+ * declaration.
+ */
+public class AndroidHandsetInstance extends AbstractMobileInstance implements ISerialNumbered,
+ IWorkbenchAdapter
+{
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.motorola.studio.android.adt.ISerialNumbered#getSerialNumber()
+ */
+ public String getSerialNumber()
+ {
+ return getName();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.motorola.studio.android.adt.ISerialNumbered#getDeviceName()
+ */
+ public String getDeviceName()
+ {
+ return getName();
+ }
+
+ public String getFullName()
+ {
+ return getName();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.ui.model.IWorkbenchAdapter#getChildren(java.lang.Object)
+ */
+ public Object[] getChildren(Object arg0)
+ {
+ return new Object[0];
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * org.eclipse.ui.model.IWorkbenchAdapter#getImageDescriptor(java.lang.Object
+ * )
+ */
+ public ImageDescriptor getImageDescriptor(Object arg0)
+ {
+ return null;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.ui.model.IWorkbenchAdapter#getLabel(java.lang.Object)
+ */
+ public String getLabel(Object arg0)
+ {
+ return getName();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.ui.model.IWorkbenchAdapter#getParent(java.lang.Object)
+ */
+ public Object getParent(Object arg0)
+ {
+ return null;
+ }
+
+}
diff --git a/src/plugins/handset/src/com/motorola/studio/android/handset/DummyServiceHandler.java b/src/plugins/handset/src/com/motorola/studio/android/handset/DummyServiceHandler.java
new file mode 100644
index 0000000..d92cf34
--- /dev/null
+++ b/src/plugins/handset/src/com/motorola/studio/android/handset/DummyServiceHandler.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.handset;
+
+import java.util.Map;
+import java.util.Properties;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.sequoyah.device.framework.events.InstanceEvent;
+import org.eclipse.sequoyah.device.framework.events.InstanceEvent.InstanceEventType;
+import org.eclipse.sequoyah.device.framework.events.InstanceEventManager;
+import org.eclipse.sequoyah.device.framework.model.IInstance;
+import org.eclipse.sequoyah.device.framework.model.handler.IServiceHandler;
+import org.eclipse.sequoyah.device.framework.model.handler.ServiceHandler;
+
+import com.motorola.studio.android.adt.DDMSFacade;
+import com.motorola.studio.android.handset.i18n.AndroidHandsetNLS;
+
+/**
+ * DESCRIPTION:
+ * <br>
+ * This class is a handler for the 0FF->Online transition. It always returns OK
+ * <br>
+ * RESPONSIBILITY:
+ * <br>
+ * Fill in the gap for the 0FF->Online transition for handsets
+ * <br>
+ * COLABORATORS:
+ * <br>
+ * None
+ * <br>
+ * USAGE:
+ * <br>
+ * This class is intended to be used by TmL only
+ */
+public class DummyServiceHandler extends ServiceHandler
+{
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.sequoyah.device.framework.model.handler.ServiceHandler#newInstance()
+ */
+ @Override
+ public IServiceHandler newInstance()
+ {
+ return new DummyServiceHandler();
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.sequoyah.device.framework.model.handler.ServiceHandler#runService(org.eclipse.sequoyah.device.framework.model.IInstance, java.util.Map, org.eclipse.core.runtime.IProgressMonitor)
+ */
+ @Override
+ public IStatus runService(IInstance arg0, Map<Object, Object> arg1, IProgressMonitor arg2)
+ {
+ String serialNumber = DDMSFacade.getSerialNumberByName(arg0.getName());
+ int tries = 0;
+ while (!DDMSFacade.isDeviceOnline(serialNumber) && ((tries >= 0) && (tries < 10)))
+ {
+ try
+ {
+ Thread.sleep(100);
+ tries++;
+ }
+ catch (InterruptedException e)
+ {
+ tries = 10;
+ }
+ }
+ Properties properties = arg0.getProperties();
+ if (properties != null)
+ {
+ String target = properties.getProperty("ro.build.version.release"); //$NON-NLS-1$
+ if (target != null)
+ {
+ arg0.setNameSuffix(AndroidHandsetNLS.DummyServiceHandler_androidSuffix + " "
+ + target);
+ }
+ else
+ {
+ arg0.setNameSuffix(AndroidHandsetNLS.DummyServiceHandler_VERSION_NA);
+ }
+ InstanceEventManager.getInstance().notifyListeners(
+ new InstanceEvent(InstanceEventType.INSTANCE_UPDATED, arg0));
+ }
+ return Status.OK_STATUS;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.sequoyah.device.framework.model.handler.ServiceHandler#updatingService(org.eclipse.sequoyah.device.framework.model.IInstance, org.eclipse.core.runtime.IProgressMonitor)
+ */
+ @Override
+ public IStatus updatingService(IInstance arg0, IProgressMonitor arg1)
+ {
+ return Status.OK_STATUS;
+ }
+}
diff --git a/src/plugins/handset/src/com/motorola/studio/android/handset/HandsetDropSupportHandler.java b/src/plugins/handset/src/com/motorola/studio/android/handset/HandsetDropSupportHandler.java
new file mode 100644
index 0000000..f4ae8bf
--- /dev/null
+++ b/src/plugins/handset/src/com/motorola/studio/android/handset/HandsetDropSupportHandler.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.handset;
+
+import org.eclipse.sequoyah.device.framework.model.IInstance;
+import org.eclipse.swt.dnd.DropTargetEvent;
+import org.eclipse.swt.dnd.TransferData;
+
+import com.motorola.studio.android.devices.AbstractDeviceDropSupportHandler;
+
+public class HandsetDropSupportHandler extends AbstractDeviceDropSupportHandler
+{
+
+ /* (non-Javadoc)
+ * @see org.eclipse.sequoyah.device.framework.model.IDeviceTypeDropSupport#canDrop(org.eclipse.sequoyah.device.framework.model.IInstance, org.eclipse.swt.dnd.TransferData, org.eclipse.swt.dnd.DropTargetEvent)
+ */
+ @Override
+ public boolean canDrop(IInstance instance, TransferData data, DropTargetEvent event)
+ {
+ return super.canDrop(instance, data, event)
+ && HandsetPlugin.STATUS_ONLINE_ID.equals(instance.getStatus());
+ }
+}
diff --git a/src/plugins/handset/src/com/motorola/studio/android/handset/HandsetInstanceBuilder.java b/src/plugins/handset/src/com/motorola/studio/android/handset/HandsetInstanceBuilder.java
new file mode 100644
index 0000000..0869d12
--- /dev/null
+++ b/src/plugins/handset/src/com/motorola/studio/android/handset/HandsetInstanceBuilder.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.handset;
+
+import java.util.Properties;
+
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.sequoyah.device.framework.model.IInstanceBuilder;
+
+/**
+ * DESCRIPTION:
+ * <br>
+ * This class knows how to build TmL instances of handsets
+ * <br>
+ * RESPONSIBILITY:
+ * <br>
+ * Creates TmL instances of handsets
+ * <br>
+ * COLABORATORS:
+ * <br>
+ * None
+ * <br>
+ * USAGE:
+ * <br>
+ * This class is intended to be used by TmL only
+ */
+public class HandsetInstanceBuilder implements IInstanceBuilder
+{
+ private final Properties properties;
+
+ private final String name;
+
+ /**
+ * Creates a new Instance Builder with the given information.
+ *
+ * @param instanceName the name of the instance to be created using this builder
+ * @param properties the properties of the instance to be created using this builder
+ */
+ public HandsetInstanceBuilder(String instanceName, Properties properties)
+ {
+ this.properties = properties;
+ this.name = instanceName;
+ }
+
+ /**
+ * Always returns <code>null</code> since this information does
+ * not make sense for Android Handset Instances.
+ */
+ public IPath getLocationPath()
+ {
+ return null;
+ }
+
+ /**
+ * Retrieves the name of the instance to be created using this builder
+ *
+ * @return the name of the instance to be created using this builder
+ */
+ public String getProjectName()
+ {
+ return name;
+ }
+
+ /**
+ * Retrieves the properties of the instance to be created using this builder
+ *
+ * @return the properties of the instance to be created using this builder
+ */
+ public Properties getProperties()
+ {
+ return properties;
+ }
+
+ /**
+ * Retrieves the value of the give property key.
+ *
+ * @param key the key of the property
+ *
+ * @return the value for the property for the instance to be created using this builder
+ */
+ public String getProperty(String key)
+ {
+ return properties.getProperty(key);
+ }
+}
diff --git a/src/plugins/handset/src/com/motorola/studio/android/handset/HandsetPlugin.java b/src/plugins/handset/src/com/motorola/studio/android/handset/HandsetPlugin.java
new file mode 100644
index 0000000..828df10
--- /dev/null
+++ b/src/plugins/handset/src/com/motorola/studio/android/handset/HandsetPlugin.java
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.handset;
+
+import java.util.Collection;
+import java.util.Properties;
+
+import org.eclipse.sequoyah.device.common.utilities.exception.SequoyahException;
+import org.eclipse.ui.plugin.AbstractUIPlugin;
+import org.osgi.framework.BundleContext;
+
+import com.motorola.studio.android.AndroidPlugin;
+import com.motorola.studio.android.adt.DDMSFacade;
+import com.motorola.studio.android.adt.DdmsRunnable;
+import com.motorola.studio.android.adt.StudioAndroidEventManager;
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.devices.DevicesManager;
+
+/**
+ * The activator class controls the plug-in life cycle
+ */
+public class HandsetPlugin extends AbstractUIPlugin
+{
+ // The plug-in ID
+ public static final String PLUGIN_ID = "com.motorola.studio.android.handset";
+
+ public static final String HANDSET_DEVICE_TYPE_ID = PLUGIN_ID + ".androidHandset";
+
+ public static final String STATUS_ONLINE_ID = PLUGIN_ID + ".status.handsetonline";
+
+ public static final String SERVICE_INIT_ID = PLUGIN_ID + ".initHandsetService";
+
+ private static final Runnable sdkLoaderListener = new Runnable()
+ {
+ public void run()
+ {
+ Collection<String> serialNumbers = DDMSFacade.getConnectedSerialNumbers();
+ for (String serial : serialNumbers)
+ {
+ createInstance(serial);
+ }
+ }
+ };
+
+ // The shared instance
+ private static HandsetPlugin plugin;
+
+ private static DdmsRunnable connectedListener = new DdmsRunnable()
+ {
+
+ public void run(String serialNumber)
+ {
+ createInstance(serialNumber);
+ }
+ };
+
+ private static DdmsRunnable disconnectedListener = new DdmsRunnable()
+ {
+
+ public void run(String serialNumber)
+ {
+ deleteInstance(serialNumber);
+ }
+ };
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.ui.plugin.AbstractUIPlugin#start(org.osgi.framework.BundleContext)
+ */
+ @Override
+ public void start(BundleContext context) throws Exception
+ {
+ StudioLogger.debug(HandsetPlugin.class, "Starting MOTODEV Android Handset Plugin...");
+
+ super.start(context);
+ plugin = this;
+ StudioAndroidEventManager.asyncAddDeviceChangeListeners(connectedListener,
+ disconnectedListener);
+ AndroidPlugin.getDefault().addSDKLoaderListener(sdkLoaderListener);
+
+ StudioLogger.debug(HandsetPlugin.class, "MOTODEV Android Handset Plugin started.");
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.ui.plugin.AbstractUIPlugin#stop(org.osgi.framework.BundleContext)
+ */
+ @Override
+ public void stop(BundleContext context) throws Exception
+ {
+ plugin = null;
+ AndroidPlugin.getDefault().removeSDKLoaderListener(sdkLoaderListener);
+ StudioAndroidEventManager.asyncRemoveDeviceChangeListeners(connectedListener,
+ disconnectedListener);
+ super.stop(context);
+ }
+
+ /**
+ * Returns the shared instance
+ *
+ * @return the shared instance
+ */
+ public static HandsetPlugin getDefault()
+ {
+ return plugin;
+ }
+
+ /**
+ * Creates a TmL instance for the given handset device
+ *
+ * @param serialNumber The serial number of the device to create a TmL instance for
+ */
+ private static void createInstance(String serialNumber)
+ {
+ if (!DDMSFacade.isEmulator(serialNumber) && !DDMSFacade.isRemote(serialNumber))
+ {
+
+ try
+ {
+ Properties instanceProperties = DDMSFacade.getDeviceProperties(serialNumber);
+
+ HandsetInstanceBuilder projectBuilder =
+ new HandsetInstanceBuilder(serialNumber, instanceProperties);
+
+ DevicesManager.getInstance().createInstanceForDevice(serialNumber,
+ HandsetPlugin.HANDSET_DEVICE_TYPE_ID, projectBuilder,
+ HandsetPlugin.SERVICE_INIT_ID);
+ }
+ catch (SequoyahException e)
+ {
+ StudioLogger.error(HandsetPlugin.class,
+ "Failed to create a TmL instance for device " + serialNumber, e);
+ }
+ }
+ }
+
+ /**
+ * Destroys the TmL instance of the given handset device
+ *
+ * @param device The device to delete the correspondent TmL instance
+ */
+ private static void deleteInstance(String serialNumber)
+ {
+ if (!DDMSFacade.isEmulator(serialNumber) && !DDMSFacade.isRemote(serialNumber))
+ {
+ DevicesManager.getInstance().deleteInstanceOfDevice(serialNumber);
+ }
+ }
+
+}
diff --git a/src/plugins/handset/src/com/motorola/studio/android/handset/i18n/AndroidHandsetNLS.java b/src/plugins/handset/src/com/motorola/studio/android/handset/i18n/AndroidHandsetNLS.java
new file mode 100644
index 0000000..403349c
--- /dev/null
+++ b/src/plugins/handset/src/com/motorola/studio/android/handset/i18n/AndroidHandsetNLS.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.handset.i18n;
+
+import org.eclipse.osgi.util.NLS;
+
+/**
+ * Class that contains the localized messages to be used through the
+ * Android Handset Plugin
+ */
+public class AndroidHandsetNLS extends NLS
+{
+ static
+ {
+ NLS.initializeMessages("com.motorola.studio.android.handset.i18n.androidHandsetNLS",
+ AndroidHandsetNLS.class);
+ }
+
+ /*
+ * Generic strings
+ */
+
+ public static String AndroidPropertiesPage_AndroidVersionLabel;
+
+ public static String AndroidPropertiesPage_APIVersionLabel;
+
+ public static String AndroidPropertiesPage_NA;
+
+ public static String AndroidPropertiesPage_SerialNumberLabel;
+
+ public static String DummyServiceHandler_androidSuffix;
+
+ public static String DummyServiceHandler_VERSION_NA;
+
+ /*
+ * Exception strings
+ */
+
+ /*
+ * Error strings
+ */
+
+ /*
+ * Information strings
+ */
+}
diff --git a/src/plugins/handset/src/com/motorola/studio/android/handset/i18n/androidHandsetNLS.properties b/src/plugins/handset/src/com/motorola/studio/android/handset/i18n/androidHandsetNLS.properties
new file mode 100644
index 0000000..6a897bd
--- /dev/null
+++ b/src/plugins/handset/src/com/motorola/studio/android/handset/i18n/androidHandsetNLS.properties
@@ -0,0 +1,6 @@
+AndroidPropertiesPage_AndroidVersionLabel=Android Version:
+AndroidPropertiesPage_APIVersionLabel=API Version:
+AndroidPropertiesPage_NA=N/A
+AndroidPropertiesPage_SerialNumberLabel=Serial number:
+DummyServiceHandler_androidSuffix=Android
+DummyServiceHandler_VERSION_NA=Version N/A
diff --git a/src/plugins/handset/src/com/motorola/studio/android/handset/ui/AndroidPropertiesPage.java b/src/plugins/handset/src/com/motorola/studio/android/handset/ui/AndroidPropertiesPage.java
new file mode 100644
index 0000000..270f463
--- /dev/null
+++ b/src/plugins/handset/src/com/motorola/studio/android/handset/ui/AndroidPropertiesPage.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.handset.ui;
+
+import java.util.Properties;
+
+import org.eclipse.core.runtime.IAdaptable;
+import org.eclipse.jface.preference.PreferenceDialog;
+import org.eclipse.sequoyah.device.framework.model.IInstance;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.ui.IWorkbenchPropertyPage;
+import org.eclipse.ui.dialogs.PropertyPage;
+
+import com.motorola.studio.android.adt.ISerialNumbered;
+import com.motorola.studio.android.handset.i18n.AndroidHandsetNLS;
+
+/**
+ *
+ *
+ * @author xrgc84
+ */
+public class AndroidPropertiesPage extends PropertyPage implements IWorkbenchPropertyPage
+{
+
+ // the Android Handset Instance to which this Property Page applies
+ private ISerialNumbered androidIntance;
+
+ @Override
+ public void setElement(IAdaptable element)
+ {
+
+ this.androidIntance = (ISerialNumbered) element;
+
+ super.setElement(element);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * org.eclipse.jface.preference.PreferencePage#createContents(org.eclipse
+ * .swt.widgets.Composite)
+ */
+ @Override
+ protected Control createContents(Composite parent)
+ {
+ ((PreferenceDialog) this.getContainer()).getTreeViewer().expandAll();
+
+ final Composite parentComposite = new Composite(parent, SWT.NONE);
+ parentComposite.setLayout(new GridLayout(2, false));
+ parentComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+
+ Label serialNumberLabel = new Label(parentComposite, SWT.NONE);
+ serialNumberLabel.setText(AndroidHandsetNLS.AndroidPropertiesPage_SerialNumberLabel);
+ Label serialNumberValue = new Label(parentComposite, SWT.NONE);
+ serialNumberValue.setText(this.androidIntance.getSerialNumber());
+
+ Label targetLabel = new Label(parentComposite, SWT.NONE);
+ targetLabel.setText(AndroidHandsetNLS.AndroidPropertiesPage_AndroidVersionLabel);
+ Label targetValue = new Label(parentComposite, SWT.NONE);
+
+ Label apiLabel = new Label(parentComposite, SWT.NONE);
+ apiLabel.setText(AndroidHandsetNLS.AndroidPropertiesPage_APIVersionLabel);
+ Label apiValue = new Label(parentComposite, SWT.NONE);
+
+ Properties propValues = ((IInstance) androidIntance).getProperties();
+ if ((propValues != null) && !propValues.isEmpty())
+ {
+ apiValue.setText(propValues.getProperty("ro.build.version.sdk")); //$NON-NLS-1$
+ targetValue.setText(propValues.getProperty("ro.build.version.release")); //$NON-NLS-1$
+ }
+ else
+ {
+ apiValue.setText(AndroidHandsetNLS.AndroidPropertiesPage_NA);
+ targetValue.setText(AndroidHandsetNLS.AndroidPropertiesPage_NA);
+ }
+ parentComposite.pack();
+
+ noDefaultAndApplyButton();
+ return parentComposite;
+
+ }
+}
diff --git a/src/plugins/handset/src/com/motorola/studio/android/handset/ui/DevicePropertiesPage.java b/src/plugins/handset/src/com/motorola/studio/android/handset/ui/DevicePropertiesPage.java
new file mode 100644
index 0000000..4700039
--- /dev/null
+++ b/src/plugins/handset/src/com/motorola/studio/android/handset/ui/DevicePropertiesPage.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.handset.ui;
+
+import java.util.Properties;
+
+import org.eclipse.core.runtime.IAdaptable;
+import org.eclipse.ui.IWorkbenchPropertyPage;
+
+import com.motorola.studio.android.adt.ISerialNumbered;
+import com.motorola.studio.android.devices.AbstractDevicePropertyPage;
+import com.motorola.studio.android.handset.AndroidHandsetInstance;
+
+/**
+ *
+ *
+ * @author xrgc84
+ */
+public class DevicePropertiesPage extends AbstractDevicePropertyPage implements
+ IWorkbenchPropertyPage
+{
+
+ private ISerialNumbered androidIntance;
+
+ @Override
+ public void setElement(IAdaptable element)
+ {
+
+ this.androidIntance = (ISerialNumbered) element;
+
+ super.setElement(element);
+ }
+
+ @Override
+ protected Properties getDeviceProperties()
+ {
+ return ((AndroidHandsetInstance) androidIntance).getProperties();
+ }
+}
diff --git a/src/plugins/installer/.classpath b/src/plugins/installer/.classpath
new file mode 100644
index 0000000..2d1a430
--- /dev/null
+++ b/src/plugins/installer/.classpath
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.5"/>
+ <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/src/plugins/installer/.project b/src/plugins/installer/.project
new file mode 100644
index 0000000..f314e00
--- /dev/null
+++ b/src/plugins/installer/.project
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>com.motorola.studio.android.installer</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.ManifestBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.SchemaBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.pde.PluginNature</nature>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+</projectDescription>
diff --git a/src/plugins/installer/META-INF/MANIFEST.MF b/src/plugins/installer/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..47a3c9a
--- /dev/null
+++ b/src/plugins/installer/META-INF/MANIFEST.MF
@@ -0,0 +1,40 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: %plugin.name
+Bundle-SymbolicName: com.motorola.studio.android.installer;singleton:=true
+Bundle-Version: 4.1.0.qualifier
+Bundle-Activator: com.motorola.studio.android.installer.InstallerPlugin
+Bundle-Vendor: %plugin.provider
+Require-Bundle: org.eclipse.ui,
+ org.eclipse.core.runtime,
+ org.eclipse.core.resources,
+ org.eclipse.equinox.p2.artifact.repository,
+ org.eclipse.equinox.p2.core,
+ org.eclipse.equinox.p2.engine,
+ org.eclipse.equinox.p2.metadata,
+ org.eclipse.equinox.p2.metadata.repository,
+ org.eclipse.equinox.p2.ui,
+ org.eclipse.equinox.p2.repository,
+ org.eclipse.equinox.p2.operations,
+ org.eclipse.equinox.p2.touchpoint.natives,
+ com.motorola.studio.android.common,
+ org.eclipse.jdt.launching,
+ com.android.ide.eclipse.adt,
+ org.apache.commons.httpclient,
+ org.eclipse.ui.browser,
+ org.eclipse.equinox.p2.ui.sdk,
+ com.android.ide.eclipse.base
+Bundle-RequiredExecutionEnvironment: JavaSE-1.6
+Bundle-ActivationPolicy: lazy
+Bundle-Localization: plugin
+Export-Package: com.motorola.studio.android.installer,
+ com.motorola.studio.android.installer.i18n,
+ com.motorola.studio.android.installer.ui.dialogs,
+ com.motorola.studio.android.installer.utilities
+Import-Package: com.motorola.studio.android.installer.utilities,
+ org.eclipse.core.internal.net,
+ org.eclipse.core.net.proxy,
+ org.eclipse.equinox.security.storage,
+ org.eclipse.jdt.core,
+ org.eclipse.jdt.internal.ui,
+ org.eclipse.ui.internal.net.auth
diff --git a/src/plugins/installer/build.properties b/src/plugins/installer/build.properties
new file mode 100644
index 0000000..0348924
--- /dev/null
+++ b/src/plugins/installer/build.properties
@@ -0,0 +1,7 @@
+source.. = src/
+output.. = bin/
+bin.includes = META-INF/,\
+ plugin.properties,\
+ icons/,\
+ plugin.xml,\
+ resources/
diff --git a/src/plugins/installer/icons/item_background.png b/src/plugins/installer/icons/item_background.png
new file mode 100644
index 0000000..3fb9047
--- /dev/null
+++ b/src/plugins/installer/icons/item_background.png
Binary files differ
diff --git a/src/plugins/installer/icons/item_background_hover.png b/src/plugins/installer/icons/item_background_hover.png
new file mode 100644
index 0000000..d30406d
--- /dev/null
+++ b/src/plugins/installer/icons/item_background_hover.png
Binary files differ
diff --git a/src/plugins/installer/icons/obj16/MOTODEV_update_icon_16x16.png b/src/plugins/installer/icons/obj16/MOTODEV_update_icon_16x16.png
new file mode 100644
index 0000000..ff77159
--- /dev/null
+++ b/src/plugins/installer/icons/obj16/MOTODEV_update_icon_16x16.png
Binary files differ
diff --git a/src/plugins/installer/icons/obj16/get_new_stuff_box_light.png b/src/plugins/installer/icons/obj16/get_new_stuff_box_light.png
new file mode 100644
index 0000000..ddb0fb2
--- /dev/null
+++ b/src/plugins/installer/icons/obj16/get_new_stuff_box_light.png
Binary files differ
diff --git a/src/plugins/installer/icons/obj16/sdk.png b/src/plugins/installer/icons/obj16/sdk.png
new file mode 100644
index 0000000..bc1a49a
--- /dev/null
+++ b/src/plugins/installer/icons/obj16/sdk.png
Binary files differ
diff --git a/src/plugins/installer/icons/wizban/installer_image_top.png b/src/plugins/installer/icons/wizban/installer_image_top.png
new file mode 100644
index 0000000..28ca2c0
--- /dev/null
+++ b/src/plugins/installer/icons/wizban/installer_image_top.png
Binary files differ
diff --git a/src/plugins/installer/plugin.properties b/src/plugins/installer/plugin.properties
new file mode 100644
index 0000000..7043f84
--- /dev/null
+++ b/src/plugins/installer/plugin.properties
@@ -0,0 +1,24 @@
+plugin.name = MOTODEV Studio for Android Two-Phase Installer Plug-in
+plugin.provider = Motorola Mobility, Inc.
+
+open2PICommandName = Download components...
+open2PICommandDescription = Download SDKs and more
+open2PICommandMnemonic = C
+
+sdk_download_page = SDK
+add_ons_page = Add-ons
+samples_page = Samples
+ndk_page = Native Support
+
+sdk_download_page_description = Install new Android SDKs or configure the use of an existing one.
+add_ons_page_description = Install new SDK add-ons
+samples_page_description = Install platform samples
+ndk_page_description = Enable Native Development Kit support
+command.update.name = Update MOTODEV Studio...
+update.name = Update MOTODEV Studio
+
+other_page_description=Download other components
+other_page=Other Components
+
+library_page=Third-Party Libraries
+library_page_description=Download third-party libraries for use by your applications
diff --git a/src/plugins/installer/plugin.xml b/src/plugins/installer/plugin.xml
new file mode 100644
index 0000000..82c8611
--- /dev/null
+++ b/src/plugins/installer/plugin.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?eclipse version="3.4"?>
+<plugin>
+ <extension
+ point="org.eclipse.ui.menus">
+ <menuContribution
+ locationURI="menu:motorolaMenu?after=updateSeparator">
+ <command
+ commandId="com.motorola.studio.android.update.studio"
+ icon="icons/obj16/MOTODEV_update_icon_16x16.png"
+ label="%command.update.name"
+ style="push">
+ </command>
+ </menuContribution>
+ </extension>
+ <extension
+ point="org.eclipse.ui.commands">
+ <command
+ defaultHandler="com.motorola.studio.android.installer.handlers.UpdateStudioHandler"
+ id="com.motorola.studio.android.update.studio"
+ name="%update.name">
+ </command>
+ </extension>
+</plugin>
diff --git a/src/plugins/installer/resources/openInputMac.scpt b/src/plugins/installer/resources/openInputMac.scpt
new file mode 100644
index 0000000..34d6d07
--- /dev/null
+++ b/src/plugins/installer/resources/openInputMac.scpt
@@ -0,0 +1,2 @@
+do shell script "cp /etc/launchd.conf /tmp/launchd.conf"
+
diff --git a/src/plugins/installer/resources/openOutputMac.scpt b/src/plugins/installer/resources/openOutputMac.scpt
new file mode 100644
index 0000000..ec0dcce
--- /dev/null
+++ b/src/plugins/installer/resources/openOutputMac.scpt
@@ -0,0 +1,2 @@
+do shell script "mv /tmp/launchd.temp /etc/launchd.conf; mv /tmp/androidSDK /etc/paths.d/androidSDK; rm -rf /tmp/androidSDK; rm -rf /tmp/launchd.conf; rm -rf /tmp/launchd.temp" with administrator privileges
+
diff --git a/src/plugins/installer/src/com/motorola/studio/android/installer/InstallerException.java b/src/plugins/installer/src/com/motorola/studio/android/installer/InstallerException.java
new file mode 100644
index 0000000..a001645
--- /dev/null
+++ b/src/plugins/installer/src/com/motorola/studio/android/installer/InstallerException.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.installer;
+
+public class InstallerException extends Exception
+{
+
+ private static final long serialVersionUID = 3876492546951746125L;
+
+ public InstallerException(Throwable t)
+ {
+ super(t);
+ }
+
+ public InstallerException(String message)
+ {
+ super(message);
+ }
+
+ public InstallerException(String message, Throwable t)
+ {
+ super(message, t);
+ }
+
+}
diff --git a/src/plugins/installer/src/com/motorola/studio/android/installer/InstallerPlugin.java b/src/plugins/installer/src/com/motorola/studio/android/installer/InstallerPlugin.java
new file mode 100644
index 0000000..a4c54d3
--- /dev/null
+++ b/src/plugins/installer/src/com/motorola/studio/android/installer/InstallerPlugin.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.installer;
+
+import java.util.Hashtable;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import org.eclipse.core.runtime.IConfigurationElement;
+import org.eclipse.core.runtime.IExtension;
+import org.eclipse.core.runtime.IExtensionPoint;
+import org.eclipse.core.runtime.IExtensionRegistry;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.equinox.p2.ui.Policy;
+import org.eclipse.ui.plugin.AbstractUIPlugin;
+import org.osgi.framework.BundleContext;
+
+import com.motorola.studio.android.installer.policy.MotodevPolicy;
+
+public class InstallerPlugin extends AbstractUIPlugin
+{
+
+ private static InstallerPlugin plugin;
+
+ public static final String PLUGIN_ID = "com.motorola.studio.android.installer"; //$NON-NLS-1$
+
+ private static final String CONTRIBUTED_PAGE_EXTENSION_POINT_ID = PLUGIN_ID + ".configuration"; //$NON-NLS-1$
+
+ private static final String CONTRIBUTED_PAGE_EXTENSION_ELEMENT = "page"; //$NON-NLS-1$
+
+ public static final String CONTRIBUTED_PAGE_EXTENSION_PAGENAME = "name"; //$NON-NLS-1$
+
+ public static final String CONTRIBUTED_PAGE_EXTENSION_PAGEDESCRIPTION = "description"; //$NON-NLS-1$
+
+ public static final String CONTRIBUTED_PAGE_EXTENSION_PAGEID = "id"; //$NON-NLS-1$
+
+ public static final String CONTRIBUTED_PAGE_EXTENSION_PAGECLASS = "class"; //$NON-NLS-1$
+
+ public static final String DEFAULT_P2_PROFILE_NAME = "MOTODEV_Profile_Android"; //$NON-NLS-1$
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.ui.plugin.AbstractUIPlugin#start(org.osgi.framework.BundleContext)
+ */
+ @Override
+ public void start(BundleContext context) throws Exception
+ {
+ super.start(context);
+ plugin = this;
+ if (canRegisterPolicy())
+ {
+ Hashtable<String, Object> serviceProperties = new Hashtable<String, Object>();
+ serviceProperties.put("service.ranking", new Integer(1500));
+ context.registerService(Policy.class.getName(), new MotodevPolicy(), serviceProperties);
+ }
+ }
+
+ private boolean canRegisterPolicy()
+ {
+ return "com.motorola.studio.android.product.android".equals(Platform.getProduct().getId());
+ }
+
+ /**
+ * Return the BundleContext for this bundle.
+ *
+ * @return BundleContext
+ */
+ public static BundleContext getContext()
+ {
+ return plugin.getBundle().getBundleContext();
+ }
+
+ /**
+ * Return the plugin instance.
+ *
+ * @return the plugin instance
+ */
+ public static InstallerPlugin getDefault()
+ {
+ return plugin;
+ }
+
+ /**
+ * Retrieve the map of configuration elements of contribution pages
+ * @return the elements of contribution pages, where the Key is the id of the page declared on extension
+ */
+ public static Map<String, IConfigurationElement> loadContributedPages()
+ {
+ Map<String, IConfigurationElement> pages =
+ new LinkedHashMap<String, IConfigurationElement>();
+ IExtensionRegistry registry = Platform.getExtensionRegistry();
+ if (registry != null)
+ {
+ IExtensionPoint extensionPoint =
+ registry.getExtensionPoint(CONTRIBUTED_PAGE_EXTENSION_POINT_ID);
+ for (IExtension extension : extensionPoint.getExtensions())
+ {
+ for (IConfigurationElement configElement : extension.getConfigurationElements())
+ {
+ if (configElement.getName().equals(CONTRIBUTED_PAGE_EXTENSION_ELEMENT))
+ {
+ pages.put(configElement.getAttribute(CONTRIBUTED_PAGE_EXTENSION_PAGEID),
+ configElement);
+ }
+ }
+ }
+ }
+
+ return pages;
+ }
+}
diff --git a/src/plugins/installer/src/com/motorola/studio/android/installer/handlers/UpdateStudioHandler.java b/src/plugins/installer/src/com/motorola/studio/android/installer/handlers/UpdateStudioHandler.java
new file mode 100644
index 0000000..6f1293d
--- /dev/null
+++ b/src/plugins/installer/src/com/motorola/studio/android/installer/handlers/UpdateStudioHandler.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.installer.handlers;
+
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.core.commands.IHandler;
+import org.eclipse.core.commands.IHandlerListener;
+import org.eclipse.core.runtime.jobs.Job;
+
+import com.motorola.studio.android.common.utilities.EclipseUtils;
+import com.motorola.studio.android.installer.i18n.InstallerNLS;
+import com.motorola.studio.android.installer.jobs.UpdateStudioJob;
+
+public class UpdateStudioHandler implements IHandler
+{
+
+ public void addHandlerListener(IHandlerListener handlerListener)
+ {
+ // Nothing to do
+
+ }
+
+ public void dispose()
+ {
+ // Nothing to do
+
+ }
+
+ public Object execute(ExecutionEvent event) throws ExecutionException
+ {
+
+ Job progressJob = UpdateStudioJob.getInstance();
+
+ if ((progressJob != null)
+ && ((progressJob.getState() == Job.WAITING) || (progressJob.getState() == Job.RUNNING)))
+ {
+ EclipseUtils.showInformationDialog(InstallerNLS.UpdateStudio_UpdateAlreadyRunningTitle,
+ InstallerNLS.UpdateStudio_UpdateAlreadyRunningMsg);
+ }
+ else
+ {
+ progressJob =
+ UpdateStudioJob
+ .createJob(InstallerNLS.UpdateStudio_CheckingForUpdatesJobDescription);
+ progressJob.setUser(true);
+ progressJob.schedule();
+ }
+
+ return null;
+ }
+
+ public boolean isEnabled()
+ {
+
+ return true;
+ }
+
+ public boolean isHandled()
+ {
+
+ return true;
+ }
+
+ public void removeHandlerListener(IHandlerListener handlerListener)
+ {
+ // Nothing to do
+ }
+
+}
diff --git a/src/plugins/installer/src/com/motorola/studio/android/installer/i18n/InstallerNLS.java b/src/plugins/installer/src/com/motorola/studio/android/installer/i18n/InstallerNLS.java
new file mode 100644
index 0000000..29ed5a8
--- /dev/null
+++ b/src/plugins/installer/src/com/motorola/studio/android/installer/i18n/InstallerNLS.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.installer.i18n;
+
+import org.eclipse.osgi.util.NLS;
+
+public class InstallerNLS extends NLS
+{
+ static
+ {
+ NLS.initializeMessages("com.motorola.studio.android.installer.i18n.installerNLS", //$NON-NLS-1$
+ InstallerNLS.class);
+ }
+
+ public static String AbstractConfigurationPage_LoadingRepositoriesTask;
+
+ public static String AcceptLicensesDialog_AcceptLicenseButton;
+
+ public static String AcceptLicensesDialog_Description;
+
+ public static String AcceptLicensesDialog_IUDescriptionLabel;
+
+ public static String AcceptLicensesDialog_RejectLicenseButton;
+
+ public static String AcceptLicensesDialog_Title;
+
+ public static String ConfigurationDialog_DialogTitle;
+
+ public static String P2Utilities_LoadingUnits;
+
+ public static String P2Utilities_Preparing;
+
+ public static String P2Utilities_PreparingEnvironment;
+
+ public static String P2Utilities_ErrorDuringUpdate;
+
+ public static String P2Utilities_ErrorWhileLaunchingP2Job;
+
+ public static String P2Utilities_AuthenticationFailed;
+
+ public static String UpdateStudio_AlreadyUpdatedInformationDialogText;
+
+ public static String UpdateStudio_AlreadyUpdatedInformationDialogTitle;
+
+ public static String UpdateStudio_CheckingForUpdatesJobDescription;
+
+ public static String UpdateStudio_UpdateErrorText;
+
+ public static String UpdateStudio_UpdateErrorTitle;
+
+ public static String UpdateStudio_UpdatingStudioJobDescription;
+
+ public static String UpdateStudio_MSG_RESTART_TITLE;
+
+ public static String UpdateStudio_MSG_RESTART_MESSAGE;
+
+ public static String UpdateStudio_UpdateAlreadyRunningTitle;
+
+ public static String UpdateStudio_UpdateAlreadyRunningMsg;
+
+ public static String UpdateStudio_LoadingRepositories;
+
+ public static String UpdateStudioJob_UpdateErrorMessage;
+
+ public static String InstallManager_Could_Not_Find_Proper_Backend;
+
+ public static String P2Installer_Could_Not_Find_Proper_Backend;
+
+ public static String P2Installer_Could_Not_Install_Selected_Items;
+
+ public static String P2Installer_Loading_Repositories;
+
+ public static String MotodevPolicy_Insufficient_Permissions_Message;
+
+ public static String MotodevPolicy_Insufficient_Permissions_Title;
+}
diff --git a/src/plugins/installer/src/com/motorola/studio/android/installer/i18n/installerNLS.properties b/src/plugins/installer/src/com/motorola/studio/android/installer/i18n/installerNLS.properties
new file mode 100644
index 0000000..028337d
--- /dev/null
+++ b/src/plugins/installer/src/com/motorola/studio/android/installer/i18n/installerNLS.properties
@@ -0,0 +1,36 @@
+AbstractConfigurationPage_LoadingRepositoriesTask=Loading repositories
+AcceptLicensesDialog_AcceptLicenseButton=I accept the terms of the license agreements
+AcceptLicensesDialog_Description=Accept the license of the software being installed in order to continue
+AcceptLicensesDialog_IUDescriptionLabel=Description
+AcceptLicensesDialog_RejectLicenseButton=I do not accept the terms of the license agreements
+AcceptLicensesDialog_Title=Accept License
+ConfigurationDialog_DialogTitle=Download components
+
+P2Utilities_LoadingUnits=Loading Installable Units
+P2Utilities_Preparing=Preparing...
+P2Utilities_PreparingEnvironment=Preparing environment
+P2Utilities_ErrorDuringUpdate=Could not execute update operation. Verify General > Network Connections preferences.
+P2Utilities_ErrorWhileLaunchingP2Job=Installation could not be launched due to an internal error.
+P2Utilities_AuthenticationFailed=Authentication failed.
+
+UpdateStudio_AlreadyUpdatedInformationDialogText=This installation of MOTODEV Studio is already up to date
+UpdateStudio_AlreadyUpdatedInformationDialogTitle=Update MOTODEV Studio
+UpdateStudio_CheckingForUpdatesJobDescription=Checking for MOTODEV Studio updates
+UpdateStudio_UpdateErrorText=Error retrieving the available updates for MOTODEV Studio
+UpdateStudio_UpdateErrorTitle=Error Updating MOTODEV Studio
+UpdateStudio_UpdatingStudioJobDescription=Updating MOTODEV Studio
+UpdateStudio_MSG_RESTART_TITLE=Restart Needed
+UpdateStudio_MSG_RESTART_MESSAGE=MOTODEV Studio needs to be restarted before you can use the new features.\nDo you want to restart it now?
+UpdateStudio_UpdateAlreadyRunningTitle=Update MOTODEV Studio
+UpdateStudio_UpdateAlreadyRunningMsg=There is another update operation in progress.
+UpdateStudio_LoadingRepositories=Loading repositories
+UpdateStudioJob_UpdateErrorMessage=Update failed due to an error
+
+InstallManager_Could_Not_Find_Proper_Backend=Could not find proper backend.
+
+P2Installer_Could_Not_Find_Proper_Backend=Could not find proper backend.
+P2Installer_Could_Not_Install_Selected_Items=Could not install selected items. Verify General > Network Connections preferences.
+P2Installer_Loading_Repositories=Loading repositories
+
+MotodevPolicy_Insufficient_Permissions_Message=MOTODEV Studio cannot write to the current install location.\nExit MOTODEV Studio and run it again as Administrator.
+MotodevPolicy_Insufficient_Permissions_Title=Insufficient user privileges
diff --git a/src/plugins/installer/src/com/motorola/studio/android/installer/jobs/UpdateStudioJob.java b/src/plugins/installer/src/com/motorola/studio/android/installer/jobs/UpdateStudioJob.java
new file mode 100644
index 0000000..eca880a
--- /dev/null
+++ b/src/plugins/installer/src/com/motorola/studio/android/installer/jobs/UpdateStudioJob.java
@@ -0,0 +1,266 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.installer.jobs;
+
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.MultiStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.SubMonitor;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.ui.PlatformUI;
+
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.common.utilities.EclipseUtils;
+import com.motorola.studio.android.installer.InstallerException;
+import com.motorola.studio.android.installer.InstallerPlugin;
+import com.motorola.studio.android.installer.i18n.InstallerNLS;
+import com.motorola.studio.android.installer.ui.dialogs.AcceptLicensesDialog;
+import com.motorola.studio.android.installer.utilities.IInstallManager.BACKEND;
+import com.motorola.studio.android.installer.utilities.IInstallManager.CATEGORY;
+import com.motorola.studio.android.installer.utilities.InstallManager;
+import com.motorola.studio.android.installer.utilities.InstallableItem;
+
+/**
+ * This {@link Job} execute studio update
+ */
+public class UpdateStudioJob extends Job
+{
+ private static final String STUDIO_UPDATE_SITE =
+ "https://studio-android.motodevupdate.com/android/4.0/";
+
+ private static Job updateJob;
+
+ /**
+ * Constructor which receives data in order to install components.
+ *
+ * @param name Name of the {@link Job}.
+ * @param stageSites Selected Stage Sites.
+ * @param itemsToInstall Items to Install.
+ */
+ private UpdateStudioJob(String name)
+ {
+ super(name);
+ }
+
+ /**
+ * Get the instance of the {@link UpdateStudioJob}. In case
+ * it has never been created by the method {@link UpdateStudioJob#createJob(String, List, Collection, MultiStatus, Map)},
+ * <code>null</code> will be returned.
+ *
+ * @return Returns the instance of {@link UpdateStudioJob}.
+ */
+ public static Job getInstance()
+ {
+ return updateJob;
+ }
+
+ /**
+ * Create a {@link UpdateStudioJob} job instance for installing components
+ * from the "Download Components".
+ *
+ * @param name Name of the {@link Job}.
+ * @param stageSites Selected Stage Sites.
+ * @param itemsToInstall Items to Install.
+ * @param finishStatus Status to be merged with this operation.
+ * @param pages Pages which hold the tasks that origins the installation.
+ * @param configurationDialog The Page which started this job.
+ */
+ public static Job createJob(String name)
+ {
+ updateJob = new UpdateStudioJob(name);
+
+ return updateJob;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.runtime.jobs.Job#run(org.eclipse.core.runtime.IProgressMonitor)
+ */
+ @Override
+ protected IStatus run(IProgressMonitor monitor)
+ {
+ InstallManager installManager = (InstallManager) InstallManager.getInstance();
+ IStatus status = null;
+
+ SubMonitor submonitor = SubMonitor.convert(monitor);
+ submonitor.beginTask(InstallerNLS.UpdateStudio_LoadingRepositories, 100);
+
+ Collection<InstallableItem> itemsToInstall = new ArrayList<InstallableItem>();
+ List<URI> updateSites = new ArrayList<URI>();
+ updateSites.add(URI.create(STUDIO_UPDATE_SITE));
+
+ try
+ {
+ status =
+ installManager.listAllAvailableUpdates(itemsToInstall, updateSites,
+ CATEGORY.UPDATE_STUDIO, BACKEND.P2, submonitor.newChild(40));
+
+ if (itemsToInstall.size() == 0)
+ {
+ StudioLogger.info(this.getClass(), "listAvailable updates returned an empty list"); //$NON-NLS-1$
+ }
+
+ if (!status.isOK())
+ {
+ StudioLogger.info(this.getClass(),
+ "Error listing available updates " + status.getMessage()); //$NON-NLS-1$
+ if (status.getSeverity() == Status.INFO)
+ {
+ EclipseUtils.showInformationDialog(
+ InstallerNLS.UpdateStudio_AlreadyUpdatedInformationDialogTitle,
+ InstallerNLS.UpdateStudio_AlreadyUpdatedInformationDialogText);
+ }
+ else if (status.getSeverity() != Status.CANCEL)
+ {
+ EclipseUtils.showErrorDialog(InstallerNLS.UpdateStudio_UpdateErrorTitle,
+ InstallerNLS.UpdateStudioJob_UpdateErrorMessage, status);
+ status = Status.CANCEL_STATUS;
+ }
+ }
+ else
+ {
+
+ final InstallableItem[] itemsToInstallArray =
+ new InstallableItem[itemsToInstall.size()];
+ int index = 0;
+ for (InstallableItem item : itemsToInstall)
+ {
+ itemsToInstallArray[index] = item;
+ index++;
+ }
+
+ final int[] result = new int[1];
+ if (!monitor.isCanceled())
+ {
+ PlatformUI.getWorkbench().getDisplay().syncExec(new Runnable()
+ {
+ public void run()
+ {
+ AcceptLicensesDialog licenceDialog =
+ new AcceptLicensesDialog(PlatformUI.getWorkbench().getDisplay()
+ .getActiveShell(), itemsToInstallArray, true, true);
+
+ result[0] = licenceDialog.open();
+ }
+ });
+ }
+
+ if (!monitor.isCanceled())
+ {
+ if (result[0] == AcceptLicensesDialog.OK)
+ {
+
+ if (status.isOK() && (itemsToInstallArray.length > 0))
+ {
+ submonitor
+ .setTaskName(InstallerNLS.UpdateStudio_UpdatingStudioJobDescription);
+ status =
+ installManager.updateStudio(updateSites, BACKEND.P2,
+ submonitor.newChild(60));
+
+ if (updateSites.isEmpty())
+ {
+ StudioLogger.info(this.getClass(),
+ "Tryed to update from Studio but updateSites[] is empty. Status message = " //$NON-NLS-1$
+ + status.getMessage());
+ }
+ else
+ {
+ StudioLogger.info(this.getClass(),
+ "Tryed to update from [" + updateSites.toString() //$NON-NLS-1$
+ + "]. Status message = " + status.getMessage()); //$NON-NLS-1$
+ }
+
+ }
+
+ if (status.isOK() && !monitor.isCanceled())
+ {
+ if (itemsToInstallArray.length > 0)
+ {
+ boolean restart =
+ EclipseUtils.showQuestionDialog(
+ InstallerNLS.UpdateStudio_MSG_RESTART_TITLE,
+ InstallerNLS.UpdateStudio_MSG_RESTART_MESSAGE);
+ if (restart)
+ {
+ PlatformUI.getWorkbench().getDisplay().syncExec(new Runnable()
+ {
+ public void run()
+ {
+ PlatformUI.getWorkbench().restart();
+ }
+ });
+
+ }
+ }
+
+ }
+ else if (monitor.isCanceled())
+ {
+ StudioLogger.info(this.getClass(),
+ "Setting status to CANCEL since monitor was canceled 1."); //$NON-NLS-1$
+ status = Status.CANCEL_STATUS;
+ }
+ else if (status.getSeverity() == Status.INFO)
+ {
+ EclipseUtils.showInformationDialog(
+ InstallerNLS.UpdateStudio_AlreadyUpdatedInformationDialogTitle,
+ InstallerNLS.UpdateStudio_AlreadyUpdatedInformationDialogText);
+ }
+ else
+ {
+ EclipseUtils.showErrorDialog(
+ InstallerNLS.UpdateStudio_UpdateErrorTitle,
+ InstallerNLS.UpdateStudioJob_UpdateErrorMessage, status);
+ status = Status.CANCEL_STATUS;
+ StudioLogger.info(this.getClass(), "Setting status to CANCEL 2."); //$NON-NLS-1$
+ }
+ }
+ }
+
+ if (monitor.isCanceled())
+ {
+ StudioLogger.info(this.getClass(),
+ "Setting status to CANCEL since monitor was canceled 3."); //$NON-NLS-1$
+ status = Status.CANCEL_STATUS;
+ }
+ }
+ }
+ catch (InstallerException e)
+ {
+ StudioLogger.error(this.getClass(),
+ "Error when retrieving installable units for update", e); //$NON-NLS-1$
+ status =
+ new Status(IStatus.ERROR, InstallerPlugin.PLUGIN_ID, 0,
+ InstallerNLS.UpdateStudio_UpdateErrorText, null);
+ }
+
+ if (!status.isOK())
+ {
+ StudioLogger.info(this.getClass(),
+ "Update Studio job exiting with status different from ok: " //$NON-NLS-1$
+ + status.getMessage());
+ }
+
+ return status;
+ }
+}
diff --git a/src/plugins/installer/src/com/motorola/studio/android/installer/policy/MotodevPolicy.java b/src/plugins/installer/src/com/motorola/studio/android/installer/policy/MotodevPolicy.java
new file mode 100644
index 0000000..54edf0d
--- /dev/null
+++ b/src/plugins/installer/src/com/motorola/studio/android/installer/policy/MotodevPolicy.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.installer.policy;
+
+import java.io.File;
+
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.equinox.internal.p2.ui.sdk.SDKPolicy;
+import org.eclipse.equinox.p2.operations.ProfileChangeOperation;
+import org.eclipse.swt.widgets.Shell;
+
+import com.motorola.studio.android.common.utilities.EclipseUtils;
+import com.motorola.studio.android.common.utilities.FileUtil;
+import com.motorola.studio.android.installer.i18n.InstallerNLS;
+
+/**
+ * This class replaces the P2 Policy on MOTODEV Studio. This is responsible to verify if writing on the current install directory is writable.
+ * A error message is displayed if this occurs and the operation is aborted.
+ */
+@SuppressWarnings("restriction")
+public class MotodevPolicy extends SDKPolicy
+{
+
+ @Override
+ public boolean continueWorkingWithOperation(ProfileChangeOperation operation, Shell shell)
+ {
+ boolean canContinue = super.continueWorkingWithOperation(operation, shell);
+
+ if (canContinue)
+ {
+ //Check if it's possible to write on the current workbench location
+ //If not... display a message and return false.
+
+ String installLocation = Platform.getInstallLocation().getURL().getFile();
+ File tmpFile = new File(installLocation + File.separator + "erase.me"); //$NON-NLS-1$
+ boolean canWrite = false;
+ canWrite = FileUtil.canWrite(tmpFile);
+
+ if (!canWrite)
+ {
+ EclipseUtils.showErrorDialog(
+ InstallerNLS.MotodevPolicy_Insufficient_Permissions_Title,
+ InstallerNLS.MotodevPolicy_Insufficient_Permissions_Message);
+ canContinue = false;
+ }
+ }
+
+ return canContinue;
+ }
+}
diff --git a/src/plugins/installer/src/com/motorola/studio/android/installer/ui/dialogs/AcceptLicensesDialog.java b/src/plugins/installer/src/com/motorola/studio/android/installer/ui/dialogs/AcceptLicensesDialog.java
new file mode 100644
index 0000000..a1ea3e0
--- /dev/null
+++ b/src/plugins/installer/src/com/motorola/studio/android/installer/ui/dialogs/AcceptLicensesDialog.java
@@ -0,0 +1,363 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.installer.ui.dialogs;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+import org.eclipse.jface.dialogs.TitleAreaDialog;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.SashForm;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.graphics.Color;
+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.Shell;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.swt.widgets.TableItem;
+import org.eclipse.swt.widgets.Text;
+
+import com.motorola.studio.android.installer.InstallerPlugin;
+import com.motorola.studio.android.installer.i18n.InstallerNLS;
+import com.motorola.studio.android.installer.utilities.InstallableItem;
+
+/**
+ * Accept Licenses Dialog
+ * Show all unaccepted licenses and a single radio to allow accept the licenses
+ */
+public class AcceptLicensesDialog extends TitleAreaDialog
+{
+
+ private Table table;
+
+ private Text licenseArea;
+
+ private Text detailsArea;
+
+ private final InstallableItem[] unitsToInstall;
+
+ private final List<InstallableItem> unitsWithUnacceptedLicenses;
+
+ private String license;
+
+ private boolean isDetailsDisplayed = false;
+
+ public AcceptLicensesDialog(Shell parentShell, InstallableItem[] iInstallableUnits,
+ boolean isDetailsDisplayed, boolean forceShowLicenseDialog)
+ {
+ super(parentShell);
+ this.unitsToInstall = iInstallableUnits;
+ this.isDetailsDisplayed = isDetailsDisplayed;
+ if (forceShowLicenseDialog)
+ {
+ unitsWithUnacceptedLicenses = Arrays.asList(iInstallableUnits);
+ }
+ else
+ {
+ unitsWithUnacceptedLicenses = new ArrayList<InstallableItem>();
+ findUnitsWithUnacceptedLicenses();
+ }
+ }
+
+ private void findUnitsWithUnacceptedLicenses()
+ {
+ for (InstallableItem iu : unitsToInstall)
+ {
+ if (iu.hasLicenseNotAccepted())
+ {
+ unitsWithUnacceptedLicenses.add(iu);
+ }
+
+ }
+ Collections.sort(unitsWithUnacceptedLicenses, new IUComparator());
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.jface.dialogs.TitleAreaDialog#createDialogArea(org.eclipse.swt.widgets.Composite)
+ */
+ @Override
+ protected Control createDialogArea(Composite parent)
+ {
+ //create main area
+ super.createDialogArea(parent).setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+ Composite mainComposite = new Composite(parent, SWT.NONE);
+ mainComposite.setLayoutData(new GridData(GridData.FILL_BOTH));
+ mainComposite.setLayout(new GridLayout());
+
+ //create sash
+ SashForm sash = new SashForm(mainComposite, SWT.HORIZONTAL);
+ sash.setLayoutData(new GridData(GridData.FILL_BOTH));
+
+ // lay out form for displaying details
+ if (isDetailsDisplayed)
+ {
+ table = new Table(sash, SWT.BORDER | SWT.SINGLE);
+ SashForm sashLeft = new SashForm(sash, SWT.VERTICAL);
+
+ licenseArea = new Text(sashLeft, SWT.MULTI | SWT.BORDER | SWT.V_SCROLL | SWT.WRAP);
+ detailsArea = new Text(sashLeft, SWT.MULTI | SWT.BORDER | SWT.V_SCROLL | SWT.WRAP);
+
+ sash.setWeights(new int[]
+ {
+ 35, 65
+ });
+
+ sashLeft.setWeights(new int[]
+ {
+ 80, 20
+ });
+ }
+ // lay out form NOT to display details
+ else
+ {
+ table = new Table(sash, SWT.BORDER | SWT.SINGLE);
+ licenseArea = new Text(sash, SWT.MULTI | SWT.BORDER | SWT.V_SCROLL | SWT.WRAP);
+
+ sash.setWeights(new int[]
+ {
+ 35, 65
+ });
+ }
+
+ Button acceptLicense = new Button(mainComposite, SWT.RADIO);
+ acceptLicense.setText(InstallerNLS.AcceptLicensesDialog_AcceptLicenseButton);
+ acceptLicense.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+ acceptLicense.setSelection(false);
+ acceptLicense.addSelectionListener(new SelectionListener()
+ {
+
+ public void widgetSelected(SelectionEvent e)
+ {
+ getButton(OK).setEnabled(true);
+ }
+
+ public void widgetDefaultSelected(SelectionEvent e)
+ {
+ //do nothing
+ }
+ });
+
+ Button doNotAcceptLicense = new Button(mainComposite, SWT.RADIO);
+ doNotAcceptLicense.setText(InstallerNLS.AcceptLicensesDialog_RejectLicenseButton);
+ doNotAcceptLicense.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+ doNotAcceptLicense.setSelection(true);
+ doNotAcceptLicense.addSelectionListener(new SelectionListener()
+ {
+
+ public void widgetSelected(SelectionEvent e)
+ {
+ getButton(OK).setEnabled(false);
+ }
+
+ public void widgetDefaultSelected(SelectionEvent e)
+ {
+ //do nothing
+ }
+ });
+
+ populateTable();
+
+ setTitle(InstallerNLS.AcceptLicensesDialog_Title);
+ setMessage(InstallerNLS.AcceptLicensesDialog_Description);
+ setTitleImage(InstallerPlugin.imageDescriptorFromPlugin(InstallerPlugin.PLUGIN_ID,
+ "icons/wizban/installer_image_top.png").createImage()); //$NON-NLS-1$
+ return mainComposite;
+ }
+
+ /**
+ * Populate the list of available ius
+ */
+ private void populateTable()
+ {
+
+ for (InstallableItem iu : unitsWithUnacceptedLicenses)
+ {
+ TableItem item = new TableItem(table, SWT.NONE);
+ item.setData(iu);
+ item.setText(iu.getDisplayName());
+ }
+
+ if (table.getItemCount() > 0)
+ {
+ table.select(0);
+ if (license != null)
+ {
+ showLicense(null);
+ }
+ else
+ {
+ showLicense((InstallableItem) table.getItem(0).getData());
+ }
+ // only add "show details" listener, in case details are to be displayed.
+ if (isDetailsDisplayed)
+ {
+ showDetails((InstallableItem) table.getItem(0).getData());
+ }
+ }
+
+ table.addSelectionListener(new SelectionListener()
+ {
+
+ public void widgetSelected(SelectionEvent e)
+ {
+ TableItem[] selection = ((Table) e.widget).getSelection();
+ TableItem item = selection[0];
+ if (item != null)
+ {
+ if (license != null)
+ {
+ showLicense(null);
+ }
+ else
+ {
+ showLicense((InstallableItem) item.getData());
+ }
+ // only add "show details" listener, in case details are to be displayed.
+ if (isDetailsDisplayed)
+ {
+ showDetails((InstallableItem) item.getData());
+ }
+ }
+ }
+
+ public void widgetDefaultSelected(SelectionEvent e)
+ {
+ //do nothing
+ }
+ });
+ }
+
+ /**
+ * Show the license in the text area
+ * @param iu
+ */
+ private void showLicense(InstallableItem iu)
+ {
+ StringBuffer buffer = new StringBuffer();
+ if (iu != null)
+ {
+ String licenseText = iu.getLicense();
+ buffer.append(licenseText);
+ }
+ else
+ {
+ buffer.append(license);
+ }
+
+ licenseArea.setText(buffer.toString());
+ Color originalColor = licenseArea.getBackground();
+ licenseArea.setEditable(false);
+ licenseArea.setBackground(originalColor);
+ }
+
+ /**
+ * Set details.
+ *
+ * @param iu Installable item.
+ */
+ private void showDetails(InstallableItem iu)
+ {
+ StringBuffer buffer = new StringBuffer();
+ if (iu != null)
+ {
+ String detailsText = iu.getDescription();
+ buffer.append(InstallerNLS.AcceptLicensesDialog_IUDescriptionLabel + ": " + detailsText); //$NON-NLS-2$
+ }
+
+ detailsArea.setText(buffer.toString());
+ }
+
+ /**
+ * Save the licenses accepted
+ */
+ private void acceptLicenses()
+ {
+ for (TableItem item : table.getItems())
+ {
+ InstallableItem ii = (InstallableItem) item.getData();
+ ii.acceptLicenses();
+ }
+ }
+
+ @Override
+ protected void createButtonsForButtonBar(Composite parent)
+ {
+ super.createButtonsForButtonBar(parent);
+ getButton(OK).setEnabled(false);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.jface.dialogs.Dialog#isResizable()
+ */
+ @Override
+ protected boolean isResizable()
+ {
+ return true;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.jface.window.Window#configureShell(org.eclipse.swt.widgets.Shell)
+ */
+ @Override
+ protected void configureShell(Shell newShell)
+ {
+ super.configureShell(newShell);
+ newShell.setSize(600, 600);
+ newShell.setText(InstallerNLS.ConfigurationDialog_DialogTitle);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.jface.dialogs.Dialog#okPressed()
+ */
+ @Override
+ protected void okPressed()
+ {
+ acceptLicenses();
+ super.okPressed();
+ }
+
+ @Override
+ public int open()
+ {
+ int returnCode = OK;
+ if (unitsWithUnacceptedLicenses.size() > 0)
+ {
+ returnCode = super.open();
+ }
+ return returnCode;
+ }
+
+ private class IUComparator implements Comparator<InstallableItem>
+ {
+
+ public int compare(InstallableItem arg0, InstallableItem arg1)
+ {
+ return arg0.getDisplayName().compareTo(arg1.getDisplayName());
+ }
+ }
+
+}
diff --git a/src/plugins/installer/src/com/motorola/studio/android/installer/utilities/IInstallManager.java b/src/plugins/installer/src/com/motorola/studio/android/installer/utilities/IInstallManager.java
new file mode 100644
index 0000000..54e10e3
--- /dev/null
+++ b/src/plugins/installer/src/com/motorola/studio/android/installer/utilities/IInstallManager.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.installer.utilities;
+
+import java.net.URI;
+import java.util.Collection;
+import java.util.List;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+
+import com.motorola.studio.android.installer.InstallerException;
+
+public interface IInstallManager
+{
+
+ /**
+ * What kind of backend technology
+ * should be used to read the links
+ * and find updates
+ */
+ public enum BACKEND
+ {
+ P2, HTTP, LIBRARY
+ }
+
+ /**
+ * Which category should be used
+ * for filter the results
+ */
+ public enum CATEGORY
+ {
+ NDK, LANG_PACKS, UPDATE_STUDIO, OTHER_COMPONENTS, LIBRARY
+ }
+
+ /**
+ * Checks for updates on the given link and using the given backend
+ *
+ * @param listToFill
+ * @param links
+ * @param category
+ * @param backEnd
+ * @param monitor
+ * @return
+ * @throws InstallerException
+ */
+ public IStatus listAllAvailableUpdates(Collection<InstallableItem> listToFill, List<URI> links,
+ CATEGORY category, BACKEND backEnd, IProgressMonitor monitor) throws InstallerException;
+
+ /**
+ * Update entire studio, combines list, download and Install)
+ * downloading all that is required for an update on studio
+ * @param links
+ * @param usedBackEnd
+ * @return
+ */
+ public IStatus updateStudio(List<URI> links, BACKEND backEnd, IProgressMonitor monitor);
+
+} \ No newline at end of file
diff --git a/src/plugins/installer/src/com/motorola/studio/android/installer/utilities/InstallManager.java b/src/plugins/installer/src/com/motorola/studio/android/installer/utilities/InstallManager.java
new file mode 100644
index 0000000..11cf24e
--- /dev/null
+++ b/src/plugins/installer/src/com/motorola/studio/android/installer/utilities/InstallManager.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.installer.utilities;
+
+import java.net.URI;
+import java.util.Collection;
+import java.util.List;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.installer.InstallerException;
+import com.motorola.studio.android.installer.InstallerPlugin;
+import com.motorola.studio.android.installer.i18n.InstallerNLS;
+
+/**
+ * Utility methods for downloading and installing updates
+ *
+ */
+public class InstallManager implements IInstallManager
+{
+
+ private static InstallManager instance;
+
+ P2Installer p2Installer = new P2Installer();
+
+ /*
+ * This class is a singleton.
+ */
+ private InstallManager()
+ {
+ //Do nothing, singleton class.
+ }
+
+ /**
+ * Returns an instance of the Install Manager
+ * @return
+ */
+ public static synchronized IInstallManager getInstance()
+ {
+ if (instance == null)
+ {
+ instance = new InstallManager();
+ }
+ return instance;
+ }
+
+ /**
+ * Updates studio in one single operation based in a link to look
+ * for the available updates. The backend parameter
+ * tells the manager how to look for updates on the site (for instance
+ * a P2 update site).
+ *
+ * IMPORTANT: the method listAllAvailableUpdates MUST be called first
+ */
+ public IStatus updateStudio(List<URI> links, BACKEND backEnd, IProgressMonitor monitor)
+ {
+ Status status =
+ new Status(IStatus.ERROR, InstallerPlugin.PLUGIN_ID, 0,
+ InstallerNLS.InstallManager_Could_Not_Find_Proper_Backend, null);
+
+ switch (backEnd)
+ {
+ case P2:
+ {
+ return p2Installer.updateStudio(monitor);
+ }
+
+ default:
+ {
+ StudioLogger.debug(this, "updateStudio felt back to default.");
+ break;
+ }
+ }
+
+ return status;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.installer.utilities.IInstallManager#listAllAvailableUpdates(java.util.Collection, java.util.List, com.motorola.studio.android.installer.utilities.IInstallManager.CATEGORY, com.motorola.studio.android.installer.utilities.IInstallManager.BACKEND, org.eclipse.core.runtime.IProgressMonitor)
+ */
+ public IStatus listAllAvailableUpdates(Collection<InstallableItem> listToFill, List<URI> links,
+ CATEGORY category, BACKEND backEnd, IProgressMonitor monitor) throws InstallerException
+ {
+
+ IStatus status = null;
+ switch (backEnd)
+ {
+ case P2:
+ status =
+ p2Installer.listAllAvailableUpdates(listToFill, links, category, backEnd,
+ monitor);
+ break;
+
+ default:
+ {
+ StudioLogger.debug(this, "listAllAvailableUpdates felt back to default.");
+ break;
+ }
+ }
+ return status;
+
+ }
+
+}
diff --git a/src/plugins/installer/src/com/motorola/studio/android/installer/utilities/InstallableItem.java b/src/plugins/installer/src/com/motorola/studio/android/installer/utilities/InstallableItem.java
new file mode 100644
index 0000000..468dbc7
--- /dev/null
+++ b/src/plugins/installer/src/com/motorola/studio/android/installer/utilities/InstallableItem.java
@@ -0,0 +1,231 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.installer.utilities;
+
+import java.util.List;
+
+public abstract class InstallableItem
+{
+ /**
+ * Example: IInstallableUnit iu
+ */
+ private Object data;
+
+ /**
+ * (will be the bundle_id in case of Eclipse installable components)
+ */
+ private String bundleID;
+
+ private Integer version;
+
+ private boolean isInstalled;
+
+ private String license;
+
+ private String displayName;
+
+ private String description;
+
+ private List<String> requirementsIds;
+
+ private int sizeInBytes;
+
+ private String provider;
+
+ private boolean canBeInstalled = true;
+
+ private String reasonNotToInstall = "";
+
+ public Object getData()
+ {
+ return data;
+ }
+
+ public void setData(Object data)
+ {
+ this.data = data;
+ }
+
+ public String getBundleID()
+ {
+ return bundleID;
+ }
+
+ public void setBundleID(String bundleID)
+ {
+ this.bundleID = bundleID;
+ }
+
+ public boolean isInstalled()
+ {
+ return isInstalled;
+ }
+
+ public void setInstalled(boolean isInstalled)
+ {
+ this.isInstalled = isInstalled;
+ }
+
+ public String getLicense()
+ {
+ return license;
+ }
+
+ public void setLicense(String license)
+ {
+ this.license = license;
+ }
+
+ public String getDisplayName()
+ {
+ return displayName;
+ }
+
+ public void setDisplayName(String displayName)
+ {
+ this.displayName = displayName;
+ }
+
+ public String getDescription()
+ {
+ return description;
+ }
+
+ public void setDescription(String description)
+ {
+ this.description = description;
+ }
+
+ public List<String> getRequirementsIds()
+ {
+ return requirementsIds;
+ }
+
+ public void setRequirementsIds(List<String> requirementsIds)
+ {
+ this.requirementsIds = requirementsIds;
+ }
+
+ public int getSizeInBytes()
+ {
+ return sizeInBytes;
+ }
+
+ public void setSizeInBytes(int sizeInBytes)
+ {
+ this.sizeInBytes = sizeInBytes;
+ }
+
+ public String getProvider()
+ {
+ return provider;
+ }
+
+ public void setProvider(String provider)
+ {
+ this.provider = provider;
+ }
+
+ public Integer getVersion()
+ {
+ return version;
+ }
+
+ public void setVersion(Integer version)
+ {
+ this.version = version;
+ }
+
+ /**
+ * @return true if any of the licenses are NOT accepted, false otherwise
+ */
+ public abstract boolean hasLicenseNotAccepted();
+
+ /**
+ * Do any action required after license is accepted
+ */
+ public abstract void acceptLicenses();
+
+ /**
+ * Gets the maximum buffer size for a reading using streams.
+ *
+ * @return Returns the maximum buffer size.
+ */
+ public abstract int getMaxBufferSize();
+
+ /**
+ * @param canBeInstalled the canBeInstalled to set
+ */
+ public void setCanBeInstalled(boolean canBeInstalled)
+ {
+ this.canBeInstalled = canBeInstalled;
+ }
+
+ /**
+ * @return the canBeInstalled
+ */
+ public boolean canBeInstalled()
+ {
+ return canBeInstalled;
+ }
+
+ /**
+ * @param reasonNotToInstall the reasonNotToInstall to set
+ */
+ public void setReasonNotToInstall(String reasonNotToInstall)
+ {
+ this.reasonNotToInstall = reasonNotToInstall;
+ }
+
+ /**
+ * @return the reasonNotToInstall
+ */
+ public String getReasonNotToInstall()
+ {
+ return reasonNotToInstall;
+ }
+
+ /**
+ * This method analyzes whether all pre-requirements have been
+ * fulfilled for this {@link InstallableItem}. In case it does,
+ * <code>true</code> is returned, otherwise <code>false</code>
+ * is given back.
+ *
+ * @return Returns <code>true</code> in case all pre-requirements
+ * have been fulfilled, <code>false</code> otherwise.
+ */
+ public abstract boolean hasPrerequirementsFulfilled();
+
+ /* (non-Javadoc)
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ @Override
+ public boolean equals(Object obj)
+ {
+ return (obj != null) && (obj instanceof InstallableItem) && (this.bundleID != null)
+ && (((InstallableItem) obj).bundleID != null)
+ && this.bundleID.equals(((InstallableItem) obj).bundleID);
+ }
+
+ /* (non-Javadoc)
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode()
+ {
+ return bundleID != null ? bundleID.hashCode() : 3;
+ }
+}
diff --git a/src/plugins/installer/src/com/motorola/studio/android/installer/utilities/P2InstallableItem.java b/src/plugins/installer/src/com/motorola/studio/android/installer/utilities/P2InstallableItem.java
new file mode 100644
index 0000000..8c61ef7
--- /dev/null
+++ b/src/plugins/installer/src/com/motorola/studio/android/installer/utilities/P2InstallableItem.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.installer.utilities;
+
+import org.eclipse.equinox.p2.metadata.IInstallableUnit;
+import org.eclipse.equinox.p2.metadata.ILicense;
+import org.eclipse.equinox.p2.ui.LicenseManager;
+import org.eclipse.equinox.p2.ui.ProvisioningUI;
+
+/**
+ * Encapsulates the specific items from P2
+ * such as controlling licenses accepted
+ */
+class P2InstallableItem extends InstallableItem
+{
+ @Override
+ public boolean hasLicenseNotAccepted()
+ {
+ LicenseManager manager = ProvisioningUI.getDefaultUI().getLicenseManager();
+ IInstallableUnit iu = (IInstallableUnit) getData();
+ for (ILicense license : iu.getLicenses())
+ {
+ if (!manager.isAccepted(license))
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public void acceptLicenses()
+ {
+ LicenseManager manager = ProvisioningUI.getDefaultUI().getLicenseManager();
+ IInstallableUnit iu = (IInstallableUnit) getData();
+ for (ILicense license : iu.getLicenses())
+ {
+ manager.accept(license);
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.installer.utilities.InstallableItem#hasPrerequrementsFulfilled()
+ */
+ @Override
+ public boolean hasPrerequirementsFulfilled()
+ {
+ // TODO Auto-generated method stub
+ return true;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.installer.utilities.InstallableItem#getMaxBufferSize()
+ */
+ @Override
+ public int getMaxBufferSize()
+ {
+ return 65536;
+ }
+
+}
diff --git a/src/plugins/installer/src/com/motorola/studio/android/installer/utilities/P2Installer.java b/src/plugins/installer/src/com/motorola/studio/android/installer/utilities/P2Installer.java
new file mode 100644
index 0000000..6dbd901
--- /dev/null
+++ b/src/plugins/installer/src/com/motorola/studio/android/installer/utilities/P2Installer.java
@@ -0,0 +1,520 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.installer.utilities;
+
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.SubMonitor;
+import org.eclipse.equinox.p2.metadata.IInstallableUnit;
+import org.eclipse.equinox.p2.operations.InstallOperation;
+import org.eclipse.equinox.p2.operations.Update;
+import org.eclipse.equinox.p2.operations.UpdateOperation;
+import org.eclipse.equinox.p2.query.IQuery;
+import org.eclipse.equinox.p2.query.QueryUtil;
+import org.eclipse.equinox.p2.repository.metadata.IMetadataRepository;
+
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.installer.InstallerException;
+import com.motorola.studio.android.installer.InstallerPlugin;
+import com.motorola.studio.android.installer.i18n.InstallerNLS;
+import com.motorola.studio.android.installer.utilities.IInstallManager.BACKEND;
+import com.motorola.studio.android.installer.utilities.IInstallManager.CATEGORY;
+
+/**
+ * Implements the methods for using the INstall framework with P2
+ */
+class P2Installer
+{
+
+ private final String LANGUAGE_PACK_QUERY =
+ "this.id ~= /com.motorola.studio.android.feature.nl*/";
+
+ private final String NKD_QUERY = "org.eclipse.sequoyah.android.cdt.feature.feature.group";
+
+ private final String SUBVERSION_QUERY = "org.eclipse.team.svn.feature.group";
+
+ private final String EGIT_QUERY = "org.eclipse.egit.feature.group";
+
+ private final String MYLYN_QUERY1 = "org.eclipse.mylyn.context_feature.feature.group";
+
+ private final String MYLYN_QUERY2 = "org.eclipse.mylyn_feature.feature.group";
+
+ private final String CVS_QUERY = "org.eclipse.cvs.feature.group";
+
+ private final HashMap<URI, IMetadataRepository> mainRepositories =
+ new HashMap<URI, IMetadataRepository>();
+
+ //will be used to record an instance of a InstallOperation. If it did not exist, we would have to instantiate
+ //the InstallOperation on validate and download methods
+ private InstallOperation installOp;
+
+ private UpdateOperation up;
+
+ /**
+ *
+ */
+ public void resetP2Installer()
+ {
+ mainRepositories.clear();
+ installOp = null;
+ }
+
+ private boolean isAllRepositoriesLoaded(Collection<URI> links)
+ {
+
+ boolean isAllLoaded = false;
+
+ if (!mainRepositories.isEmpty())
+ {
+ isAllLoaded = true;
+ for (URI uri : links)
+ {
+ if (!mainRepositories.containsKey(uri))
+ {
+ isAllLoaded = false;
+ break;
+ }
+ }
+ }
+ return isAllLoaded;
+ }
+
+ /**
+ * Load the P2 repositories.
+ * Initially the method verifies if the repositories were already instantiated and is
+ * on the metadata and arfacts maps (metadataRepositoriesMap, artifactRepositoriesMap).
+ * If yes, they should not be instantiated again. Otherwise, the repository and its references
+ * will be loaded.
+ *
+ * If the global maps did not exist, every time that methods validateInstallation, listAvailableUpdates
+ * and downloadAndInstall were called, all the repositories would be loaded again.
+ */
+ private IStatus loadRepositories(List<URI> links, IProgressMonitor monitor)
+ {
+ StudioLogger.debug(this, "loading repositories...");
+ IStatus status = Status.OK_STATUS;
+
+ StudioLogger.debug(this, "there are not loaded repositories");
+ SubMonitor submonitor = SubMonitor.convert(monitor);
+
+ int reposSize = 0;
+ if (links != null)
+ {
+ reposSize += links.size();
+ }
+
+ submonitor.beginTask(InstallerNLS.AbstractConfigurationPage_LoadingRepositoriesTask,
+ reposSize * 100);
+
+ boolean isAllRepositoriesLoaded = isAllRepositoriesLoaded(links);
+
+ if (!isAllRepositoriesLoaded)
+ {
+ P2RepositoriesFactory p2RepositoriesFactory = P2RepositoriesFactory.getInstance();
+ for (URI uri : links)
+ {
+ IMetadataRepository metadataRepository = null;
+ //loads metadata repositories from URIs
+ try
+ {
+ metadataRepository =
+ p2RepositoriesFactory.getMetadataRepository(uri, true,
+ submonitor.newChild(100));
+ mainRepositories.put(uri, metadataRepository);
+
+ }
+ catch (Exception e)
+ {
+ status = new Status(IStatus.WARNING, InstallerPlugin.PLUGIN_ID, e.getMessage());
+ StudioLogger.error(this.getClass(),
+ "could not instantiate repository from URI " + uri);
+ }
+
+ }
+ }
+ else
+ {
+ submonitor.done();
+ }
+
+ return status;
+ }
+
+ /**
+ * Loads InstallableItems based on category and links. They are put into the collection listToFill
+ *
+ * @param listToFill
+ * @param links
+ * @param category
+ * @param monitor
+ * @return
+ * @throws InstallerException
+ */
+ public IStatus listAllAvailableInstallItems(Collection<InstallableItem> listToFill,
+ List<URI> uriList, CATEGORY category, IProgressMonitor monitor)
+ throws InstallerException
+ {
+
+ StudioLogger.debug(this, "listing available installable items...");
+ IStatus status =
+ new Status(IStatus.ERROR, InstallerPlugin.PLUGIN_ID, 0,
+ InstallerNLS.P2Installer_Could_Not_Find_Proper_Backend, null);
+
+ //this links list is created because the method loadRepositories remove some items of the list
+ //if the repositorie is already loaded.
+ List<URI> links = new ArrayList<URI>();
+ links.addAll(uriList);
+
+ SubMonitor submonitor = SubMonitor.convert(monitor);
+ submonitor.beginTask(InstallerNLS.P2Installer_Loading_Repositories, 100);
+
+ status = loadRepositories(links, submonitor.newChild(20));
+
+ Collection<IInstallableUnit> units = new HashSet<IInstallableUnit>();
+
+ IQuery<IInstallableUnit> query = null;
+
+ //category is used to create the correct query
+ switch (category)
+ {
+ case LANG_PACKS:
+ {
+ //Filter IUs in order to receive only lang packs
+ query = QueryUtil.createMatchQuery(LANGUAGE_PACK_QUERY); //$NON-NLS-1$
+ break;
+ }
+
+ case NDK:
+ {
+ //Filter IUs in order to receive only NDK related
+ query = QueryUtil.createIUQuery(NKD_QUERY); //$NON-NLS-1$
+ break;
+ }
+
+ case UPDATE_STUDIO:
+ {
+ //No special query needed
+ break;
+ }
+
+ case OTHER_COMPONENTS:
+ {
+ Collection<IQuery<IInstallableUnit>> queries =
+ new ArrayList<IQuery<IInstallableUnit>>();
+ queries.add(QueryUtil.createIUQuery(SUBVERSION_QUERY));
+ queries.add(QueryUtil.createIUQuery(MYLYN_QUERY1));
+ queries.add(QueryUtil.createIUQuery(MYLYN_QUERY2));
+ queries.add(QueryUtil.createIUQuery(CVS_QUERY));
+ queries.add(QueryUtil.createIUQuery(EGIT_QUERY));
+
+ query = QueryUtil.createCompoundQuery(queries, false);
+ break;
+ }
+ default:
+ {
+ // No specific query to use as filter, download them all!
+ break;
+ }
+ }
+
+ int monitorWorkSize = 0;
+ try
+ {
+ monitorWorkSize = 40 / mainRepositories.values().size();
+ }
+ catch (ArithmeticException e)
+ {
+ // Do nothing
+ }
+ for (Iterator<IMetadataRepository> iterator = mainRepositories.values().iterator(); iterator
+ .hasNext();)
+ {
+ IMetadataRepository repository = iterator.next();
+ try
+ {
+
+ Collection<IInstallableUnit> ius =
+ P2Utilities.getInstallableUnits(repository, query,
+ submonitor.newChild(monitorWorkSize));
+ units.addAll(ius);
+
+ status = Status.OK_STATUS;
+ }
+ catch (InstallerException e)
+ {
+ StudioLogger.error(this.getClass(), "could not retrieve installable units");
+ status =
+ new Status(IStatus.ERROR, InstallerPlugin.PLUGIN_ID, 0,
+ "Error retrieving available installable units", null);
+
+ }
+ }
+
+ monitorWorkSize = 0;
+ try
+ {
+ monitorWorkSize = 40 / units.size();
+ }
+ catch (ArithmeticException e)
+ {
+ // Do nothing
+ }
+ for (Iterator<IInstallableUnit> iterator = units.iterator(); iterator.hasNext();)
+ {
+ IInstallableUnit iInstallableUnit = iterator.next();
+
+ if (P2Utilities.isGroup(iInstallableUnit))
+ {
+ // I'm only returning groups since only groups
+ // are listed for the user to select what to install
+ InstallableItem item = iu2InstallableItem(iInstallableUnit, monitor);
+ listToFill.add(item);
+ }
+
+ }
+
+ if (status.getMessage().equals("org.eclipse.core.runtime.OperationCanceledException"))
+ {
+ StudioLogger.debug(this, "operation was canceled");
+ status =
+ new Status(Status.CANCEL, status.getPlugin(), status.getCode(),
+ status.getMessage(), status.getException());
+ }
+
+ submonitor.done();
+
+ return status;
+ }
+
+ //Translates a P2 installable unit to a InstallableItem object
+ private InstallableItem iu2InstallableItem(IInstallableUnit unit, IProgressMonitor monitor)
+ throws InstallerException
+ {
+ InstallableItem item = new P2InstallableItem();
+ item.setData(unit);
+ item.setBundleID(unit.getId());
+ item.setInstalled(P2Utilities.isInstalled(unit, monitor));
+ item.setLicense(P2Utilities.getLicenseText(unit));
+ item.setDisplayName(P2Utilities.getIUExternalizedValue(unit, IInstallableUnit.PROP_NAME));
+ item.setDescription(P2Utilities.getIUExternalizedValue(unit,
+ IInstallableUnit.PROP_DESCRIPTION));
+ item.setProvider(P2Utilities.getIUExternalizedValue(unit, IInstallableUnit.PROP_PROVIDER));
+ item.setRequirementsIds(P2Utilities.getRequirements(unit));
+ return item;
+ }
+
+ /**
+ * Method used to install the installable items. The repositories and installOp should be already loaded,
+ * otherwise they will be.
+ *
+ * @param links
+ * @param itemsToDownloadAndInstall
+ * @param monitor
+ * @return
+ */
+ public IStatus downloadAndInstall(List<URI> links,
+ Collection<InstallableItem> itemsToDownloadAndInstall, IProgressMonitor monitor)
+ {
+ StudioLogger.debug(this, "downloadAndInstall: installing selected installable items");
+ IStatus status =
+ new Status(IStatus.ERROR, InstallerPlugin.PLUGIN_ID, 0,
+ InstallerNLS.P2Installer_Could_Not_Install_Selected_Items, null);
+
+ if ((itemsToDownloadAndInstall != null) && (!itemsToDownloadAndInstall.isEmpty()))
+ {
+
+ final List<IInstallableUnit> installableUnits = new ArrayList<IInstallableUnit>();
+
+ for (InstallableItem item : itemsToDownloadAndInstall)
+ {
+ IInstallableUnit unit = (IInstallableUnit) item.getData();
+ installableUnits.add(unit);
+ }
+
+ try
+ {
+ if (installOp == null)
+ {
+ status = loadRepositories(links, monitor);
+
+ status =
+ P2Utilities.installIu(installableUnits, mainRepositories.values(),
+ monitor);
+ }
+ else
+ {
+ status = P2Utilities.installIu(installableUnits, installOp, monitor);
+ }
+ }
+ catch (InstallerException e)
+ {
+ StudioLogger.error(this.getClass(), "could not install selected installable unit");
+ status = new Status(IStatus.WARNING, InstallerPlugin.PLUGIN_ID, e.getMessage());
+ }
+
+ //clean installOp and maps. After the installation has occurred, they can be cleaned.
+ installOp = null;
+ mainRepositories.clear();
+
+ return status;
+ }
+ return status;
+ }
+
+ /**
+ * Updates studio.
+ * IMPORTANT: the method listAllAvailableUpdates MUST be called first
+ *
+ * @param monitor
+ * @return
+ */
+ public IStatus updateStudio(IProgressMonitor monitor)
+ {
+ StudioLogger.debug(this, "updateStudio: installing selected installable items");
+ IStatus status =
+ new Status(IStatus.ERROR, InstallerPlugin.PLUGIN_ID, 0,
+ InstallerNLS.P2Installer_Could_Not_Install_Selected_Items, null);
+
+ try
+ {
+ status = P2Utilities.updateIu(up, monitor);
+
+ }
+ catch (Exception e)
+ {
+ StudioLogger.error(this.getClass(), "could not install selected installable unit.", e);
+ status = new Status(IStatus.WARNING, InstallerPlugin.PLUGIN_ID, e.getMessage());
+ }
+
+ return status;
+ }
+
+ /**
+ * @param itemsToDownloadAndInstall
+ * @param backEnd
+ * @param monitor
+ * @return
+ */
+ public IStatus validateInstallation(List<URI> links,
+ Collection<InstallableItem> itemsToDownloadAndInstall, BACKEND backEnd,
+ IProgressMonitor monitor)
+ {
+ // installOp will be loaded as global variable because will be used on method downloadAndInstall.
+ // It must not be instantiated twice if it were already validated.
+ installOp = null;
+
+ List<URI> allURIs = new ArrayList<URI>(mainRepositories.keySet());
+ for (Iterator<URI> iterator = allURIs.iterator(); iterator.hasNext();)
+ {
+ URI uri = iterator.next();
+ if (!links.contains(uri))
+ {
+ mainRepositories.remove(uri);
+ }
+
+ }
+
+ Collection<IInstallableUnit> temp = new HashSet<IInstallableUnit>();
+
+ loadRepositories(links, monitor);
+
+ for (Iterator<InstallableItem> iterator = itemsToDownloadAndInstall.iterator(); iterator
+ .hasNext();)
+ {
+ InstallableItem iInstallableItem = iterator.next();
+ temp.add((IInstallableUnit) iInstallableItem.getData());
+
+ }
+ if ((itemsToDownloadAndInstall != null) && (itemsToDownloadAndInstall.size() > 0))
+ {
+ try
+ {
+ installOp =
+ P2Utilities.getInstallOperation(temp, mainRepositories.values(), monitor);
+ }
+ catch (InstallerException e)
+ {
+ StudioLogger.error(this.getClass(), "Could not retrieve install operation");
+
+ return new Status(IStatus.ERROR, InstallerPlugin.PLUGIN_ID, 0, e.getMessage(), null);
+
+ }
+
+ }
+
+ return installOp != null ? installOp.getResolutionResult() : Status.CANCEL_STATUS;
+
+ }
+
+ /**
+ * Lists all available updates given a link.
+ * After this method the method updateStudio can be invoked
+ *
+ * @param listToFill
+ * @param links
+ * @param category
+ * @param backEnd
+ * @param monitor
+ * @return
+ * @throws InstallerException
+ */
+ public IStatus listAllAvailableUpdates(Collection<InstallableItem> listToFill, List<URI> links,
+ CATEGORY category, BACKEND backEnd, IProgressMonitor monitor) throws InstallerException
+ {
+ IStatus result = Status.OK_STATUS;
+
+ try
+ {
+ up = P2Utilities.getUpdateOperation(links, monitor);
+ if (up != null)
+ {
+
+ result = up.getResolutionResult();
+ Update[] updates = up.getSelectedUpdates();
+
+ if (listToFill != null)
+ {
+ for (int i = 0; i < updates.length; i++)
+ {
+ InstallableItem item = iu2InstallableItem(updates[i].replacement, monitor);
+ listToFill.add(item);
+ }
+ }
+ }
+ else
+ {
+ result = Status.CANCEL_STATUS;
+ }
+ }
+ catch (Exception e)
+ {
+ StudioLogger.error(this.getClass(), "Error looking for updates. ", e);
+ result = new Status(IStatus.ERROR, InstallerPlugin.PLUGIN_ID, 0, e.getMessage(), null);
+ }
+
+ return result;
+ }
+
+}
diff --git a/src/plugins/installer/src/com/motorola/studio/android/installer/utilities/P2RepositoriesFactory.java b/src/plugins/installer/src/com/motorola/studio/android/installer/utilities/P2RepositoriesFactory.java
new file mode 100644
index 0000000..a1dacfb
--- /dev/null
+++ b/src/plugins/installer/src/com/motorola/studio/android/installer/utilities/P2RepositoriesFactory.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.installer.utilities;
+
+import java.net.URI;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.equinox.p2.repository.artifact.IArtifactRepository;
+import org.eclipse.equinox.p2.repository.metadata.IMetadataRepository;
+
+import com.motorola.studio.android.installer.InstallerException;
+
+/**
+ * Factory for {@link IMetadataRepository} and {@link IArtifactRepository}.
+ * Will retrieve P2 repositories based on it's URI, keeping a cached version of the repositories in order to increase speed.
+ * Note: This class is a singleton, use the getInstance() method.
+ */
+class P2RepositoriesFactory
+{
+
+ private static P2RepositoriesFactory instance;
+
+ private final Map<URI, IMetadataRepository> metadataRepositories;
+
+ private final Map<URI, IArtifactRepository> artifactRepositories;
+
+ /*
+ * This class is a singleton.
+ */
+ private P2RepositoriesFactory()
+ {
+ metadataRepositories = new HashMap<URI, IMetadataRepository>();
+ artifactRepositories = new HashMap<URI, IArtifactRepository>();
+ }
+
+ public static P2RepositoriesFactory getInstance()
+ {
+ if (instance == null)
+ {
+ instance = new P2RepositoriesFactory();
+ }
+
+ return instance;
+ }
+
+ /**
+ * Retrieves a {@link IMetadataRepository} from a given URI.
+ * If the repository is found on the internal cache then the cached version is retrieved,
+ * otherwise the repository is loaded and added to the cache.
+ * The behavior can be overridden by setting the force flag to true. When this happens
+ * the repository will be loaded again and a new version will be put on cache.
+ * @param uri repository URI
+ * @param force force loading the repository from the P2 mechanism.
+ * @param monitor
+ * @return {@link IMetadataRepository} from the given URI, if none is found null is returned.
+ * @throws InstallerException
+ */
+ public IMetadataRepository getMetadataRepository(URI uri, boolean force,
+ IProgressMonitor monitor) throws InstallerException
+ {
+ IMetadataRepository repository = null;
+
+ repository = metadataRepositories.get(uri);
+
+ if ((repository == null) || force)
+ {
+ repository = P2Utilities.getMetadataRepository(uri, monitor);
+ metadataRepositories.put(uri, repository);
+ }
+ else
+ {
+ monitor.done();
+ }
+ return repository;
+ }
+
+ /**
+ * Retrieves a {@link IArtifactRepository} from a given URI.
+ * If the repository is found on the internal cache then the cached version is retrieved,
+ * otherwise the repository is loaded and added to the cache.
+ * The behavior can be overridden by setting the force flag to true. When this happens
+ * the repository will be loaded again and a new version will be put on cache.
+ * @param uri repository URI
+ * @param force force loading the repository from the P2 mechanism.
+ * @param monitor
+ * @return {@link IArtifactRepository} from the given URI, if none is found null is returned.
+ * @throws InstallerException
+ */
+ public IArtifactRepository getArtifactRepository(URI uri, boolean force,
+ IProgressMonitor monitor) throws InstallerException
+ {
+ IArtifactRepository repository = null;
+
+ repository = artifactRepositories.get(uri);
+
+ if ((repository == null) || force)
+ {
+ repository = P2Utilities.getArtifactRepository(uri, monitor);
+ artifactRepositories.put(uri, repository);
+ }
+ else
+ {
+ monitor.done();
+ }
+ return repository;
+ }
+
+}
diff --git a/src/plugins/installer/src/com/motorola/studio/android/installer/utilities/P2Utilities.java b/src/plugins/installer/src/com/motorola/studio/android/installer/utilities/P2Utilities.java
new file mode 100644
index 0000000..eabe512
--- /dev/null
+++ b/src/plugins/installer/src/com/motorola/studio/android/installer/utilities/P2Utilities.java
@@ -0,0 +1,787 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.installer.utilities;
+
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.OperationCanceledException;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.SubMonitor;
+import org.eclipse.equinox.p2.core.IProvisioningAgent;
+import org.eclipse.equinox.p2.core.IProvisioningAgentProvider;
+import org.eclipse.equinox.p2.core.ProvisionException;
+import org.eclipse.equinox.p2.engine.IProfile;
+import org.eclipse.equinox.p2.engine.IProfileRegistry;
+import org.eclipse.equinox.p2.metadata.IInstallableUnit;
+import org.eclipse.equinox.p2.metadata.ILicense;
+import org.eclipse.equinox.p2.metadata.IRequirement;
+import org.eclipse.equinox.p2.metadata.Version;
+import org.eclipse.equinox.p2.operations.InstallOperation;
+import org.eclipse.equinox.p2.operations.ProfileModificationJob;
+import org.eclipse.equinox.p2.operations.ProvisioningJob;
+import org.eclipse.equinox.p2.operations.ProvisioningSession;
+import org.eclipse.equinox.p2.operations.UpdateOperation;
+import org.eclipse.equinox.p2.query.IQuery;
+import org.eclipse.equinox.p2.query.IQueryResult;
+import org.eclipse.equinox.p2.query.QueryUtil;
+import org.eclipse.equinox.p2.repository.IRepository;
+import org.eclipse.equinox.p2.repository.IRepositoryReference;
+import org.eclipse.equinox.p2.repository.artifact.IArtifactRepository;
+import org.eclipse.equinox.p2.repository.artifact.IArtifactRepositoryManager;
+import org.eclipse.equinox.p2.repository.metadata.IMetadataRepository;
+import org.eclipse.equinox.p2.repository.metadata.IMetadataRepositoryManager;
+import org.eclipse.equinox.p2.ui.ProvisioningUI;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.ui.PlatformUI;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.installer.InstallerException;
+import com.motorola.studio.android.installer.InstallerPlugin;
+import com.motorola.studio.android.installer.i18n.InstallerNLS;
+
+/**
+ * Utilities class with P2 auxiliary methods
+ */
+class P2Utilities
+{
+ /**
+ * Verify if a given IInstallableUnit is installed on the system (iterates over all known profiles)
+ * @param iu IInstallableUnit to be checked
+ * @return true if iu is installed, false otherwise.
+ * @throws InstallerException
+ */
+ protected static boolean isInstalled(IInstallableUnit iu, IProgressMonitor progressMonitor)
+ throws InstallerException
+ {
+ return isInstalled(iu.getId(), null, progressMonitor);
+ }
+
+ /**
+ * Verify if a InstallableUnit with the given id and version is installed on the system (iterates over all known profiles)
+ * @param iuId id of the InstallableUnit to be checked
+ * @param version version of the wanted installableUnit
+ * @return true if iu is installed, false otherwise.
+ * @throws InstallerException
+ */
+ protected static boolean isInstalled(String iuId, Version version,
+ IProgressMonitor progressMonitor) throws InstallerException
+ {
+ IQuery<IInstallableUnit> query = QueryUtil.createIUQuery(iuId, version);
+ return isInstalled(query, progressMonitor);
+ }
+
+ /**
+ * Execute a query on every profile found, looking for InstallableUnits
+ * @param query IQuery<IInstallableUnit> to be executed.
+ * @return true if query results are available, false otherwise.
+ * @throws InstallerException
+ */
+ protected static boolean isInstalled(IQuery<IInstallableUnit> query,
+ IProgressMonitor progressMonitor) throws InstallerException
+ {
+ IProvisioningAgent agent = getProvisioningAgent();
+ IProfileRegistry profileRegistry =
+ (IProfileRegistry) agent.getService(IProfileRegistry.SERVICE_NAME);
+ IProfile[] profiles = profileRegistry.getProfiles();
+ SubMonitor subMonitor = SubMonitor.convert(progressMonitor, profiles.length * 100);
+ for (IProfile profile : profiles)
+ {
+ IQueryResult<IInstallableUnit> available =
+ profile.query(query, subMonitor.newChild(100));
+ if (!available.isEmpty())
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Get the IProvisioningAgent for the current context.
+ * @return
+ * @throws InstallerException
+ */
+ private static IProvisioningAgent getProvisioningAgent() throws InstallerException
+ {
+ BundleContext context = InstallerPlugin.getContext();
+ IProvisioningAgent agent = getProvisioningAgent(context);
+ return agent;
+ }
+
+ /**
+ * Loads every IInstallableUnit that matches the criteria specified by filter on a given IMetadataRepository
+ * @param metadatarepository Repository containing the IUs
+ * @param query criteria to be used to filter the InstallableUnits on the given MetadataRepository, if null them all IUs
+ * available are returned
+ * @param progressMonitor
+ * @return collection containing all IInstallableUnits from the given repository.
+ * @throws InstallerException
+ */
+ protected static Collection<IInstallableUnit> getInstallableUnits(
+ IMetadataRepository metadataRepository, IQuery<IInstallableUnit> query,
+ IProgressMonitor progressMonitor) throws InstallerException
+ {
+ SubMonitor subMonitor = SubMonitor.convert(progressMonitor);
+ subMonitor.beginTask(InstallerNLS.P2Utilities_LoadingUnits, 100);
+ Collection<IInstallableUnit> installableUnits;
+ installableUnits = null;
+ if (metadataRepository != null)
+ {
+ if (query == null)
+ {
+ query = QueryUtil.createIUAnyQuery();
+ }
+ IQueryResult<IInstallableUnit> queryResult =
+ metadataRepository.query(query, subMonitor.newChild(100));
+ installableUnits = queryResult.toSet();
+ }
+
+ return installableUnits;
+ }
+
+ /**
+ * Retrieves the IMetadataRepository object based on the repository URI
+ * @param metadatarepoUri URI pointing to the MetadataRepository
+ * @param progressMonitor
+ * @return the IMetadataRepository object, if there's no repository on the given URI null will be returned.
+ */
+ protected static IMetadataRepository getMetadataRepository(URI metadatarepoUri,
+ IProgressMonitor progressMonitor) throws InstallerException, OperationCanceledException
+ {
+ SubMonitor subMonitor = SubMonitor.convert(progressMonitor);
+ subMonitor.beginTask(InstallerNLS.P2Utilities_Preparing, 100);
+ IMetadataRepository metadataRepository = null;
+
+ IProvisioningAgent agent = getProvisioningAgent();
+ subMonitor.worked(10);
+ IMetadataRepositoryManager metadataRepositoryManagermanager =
+ (IMetadataRepositoryManager) agent
+ .getService(IMetadataRepositoryManager.SERVICE_NAME);
+ subMonitor.worked(15);
+
+ try
+ {
+ metadataRepository =
+ metadataRepositoryManagermanager.loadRepository(metadatarepoUri,
+ subMonitor.newChild(75));
+ }
+ catch (ProvisionException e)
+ {
+ InstallerException installerException;
+
+ if (e.getStatus().getCode() == ProvisionException.REPOSITORY_FAILED_AUTHENTICATION)
+ {
+ installerException =
+ new InstallerException(InstallerNLS.P2Utilities_AuthenticationFailed);
+ }
+ else
+ {
+ installerException = new InstallerException(e);
+ }
+
+ throw installerException;
+ }
+
+ return metadataRepository;
+ }
+
+ /**
+ * Retrieves the IArtifactRepository object based on the repository URI
+ * @param artifactRepoUri URI pointing to the MetadataRepository
+ * @param progressMonitor
+ * @return the IArtifactRepository object, if there's no repository on the given URI null will be returned.
+ */
+ protected static IArtifactRepository getArtifactRepository(URI artifactRepoUri,
+ IProgressMonitor progressMonitor) throws InstallerException
+ {
+ SubMonitor subMonitor = SubMonitor.convert(progressMonitor, 175);
+ subMonitor.beginTask(InstallerNLS.P2Utilities_Preparing, 25);
+
+ IArtifactRepository artifactRepository = null;
+
+ IProvisioningAgent agent = getProvisioningAgent();
+ subMonitor.worked(10);
+
+ IArtifactRepositoryManager artifactRepositoryManagermanager =
+ (IArtifactRepositoryManager) agent
+ .getService(IArtifactRepositoryManager.SERVICE_NAME);
+ subMonitor.worked(15);
+
+ try
+ {
+ artifactRepository =
+ artifactRepositoryManagermanager.loadRepository(artifactRepoUri,
+ subMonitor.newChild(150));
+ }
+ catch (ProvisionException e)
+ {
+ throw new InstallerException(e);
+ }
+ catch (OperationCanceledException e)
+ {
+ throw new InstallerException(e);
+ }
+
+ return artifactRepository;
+ }
+
+ /**
+ * Retrieves the InstallOperation that is necessary to install the given IUS on the current Profile.
+ * @param installableUnits to be installed.
+ * @param metadataRepositories metadataRepositories to be used
+ * @param artifactRepositories artifactRepositories to be used
+ * @param progressMonitor
+ * @return the InstallOperation
+ * @throws InstallerException if any error occurs
+ */
+ protected static InstallOperation getInstallOperation(
+ Collection<IInstallableUnit> installableUnits,
+ Collection<IMetadataRepository> metadataRepositories, IProgressMonitor progressMonitor)
+ throws InstallerException
+ {
+ SubMonitor subMonitor = SubMonitor.convert(progressMonitor);
+ subMonitor.beginTask(InstallerNLS.P2Utilities_PreparingEnvironment, 200);
+
+ final ProvisioningUI provisioningUI = ProvisioningUI.getDefaultUI();
+
+ ProvisioningSession session = provisioningUI.getSession();
+
+ subMonitor.worked(50);
+
+ final InstallOperation op = new InstallOperation(session, installableUnits);
+ if (metadataRepositories != null)
+ {
+ List<URI> metadataRepositoriesURIs = new ArrayList<URI>();
+ List<URI> artifactRepositoriesURIs = new ArrayList<URI>();
+ for (IMetadataRepository repository : metadataRepositories)
+ {
+ metadataRepositoriesURIs.add(repository.getLocation());
+ artifactRepositoriesURIs.add(repository.getLocation());
+ Collection<IRepositoryReference> references = repository.getReferences();
+ for (IRepositoryReference reference : references)
+ {
+ if ((reference.getOptions() == IRepository.ENABLED)
+ && (reference.getType() == IRepository.TYPE_METADATA))
+ {
+ metadataRepositoriesURIs.add(reference.getLocation());
+ }
+ else if ((reference.getOptions() == IRepository.ENABLED)
+ && (reference.getType() == IRepository.TYPE_ARTIFACT))
+ {
+ artifactRepositoriesURIs.add(reference.getLocation());
+ }
+ }
+ }
+ op.getProvisioningContext().setMetadataRepositories(
+ metadataRepositoriesURIs.toArray(new URI[0]));
+ op.getProvisioningContext().setArtifactRepositories(
+ artifactRepositoriesURIs.toArray(new URI[0]));
+ }
+
+ subMonitor.worked(50);
+ op.resolveModal(subMonitor.newChild(80));
+ final boolean[] canContinue = new boolean[]
+ {
+ true
+ };
+ Display.getDefault().syncExec(new Runnable()
+ {
+ public void run()
+ {
+ canContinue[0] =
+ provisioningUI.getPolicy().continueWorkingWithOperation(op,
+ PlatformUI.getWorkbench().getModalDialogShellProvider().getShell());
+ }
+ });
+ subMonitor.done();
+ return canContinue[0] ? op : null;
+ }
+
+ /**
+ * Installs the given InstallableUnits, from the given repositories, on the current profile.
+ * @param installableUnits to be installed
+ * @param metadataRepositories metadataRepositories to be used
+ * @param artifactRepositories artifactRepositories to be used
+ * @param progressMonitor
+ * @return
+ * @throws InstallerException
+ */
+ protected static IStatus installIu(Collection<IInstallableUnit> installableUnits,
+ InstallOperation installOperation, IProgressMonitor progressMonitor)
+ throws InstallerException
+ {
+ IStatus result = installOperation.getResolutionResult();
+
+ if (installOperation.hasResolved() && installOperation.getResolutionResult().isOK())
+ {
+ ProvisioningJob provisioningJob = installOperation.getProvisioningJob(progressMonitor);
+
+ if (provisioningJob instanceof ProfileModificationJob)
+ {
+ ((ProfileModificationJob) provisioningJob)
+ .setRestartPolicy(ProvisioningJob.RESTART_NONE);
+ }
+
+ ProvisioningUI.getDefaultUI().manageJob(provisioningJob, ProvisioningJob.RESTART_NONE);
+ provisioningJob.schedule();
+
+ try
+ {
+ provisioningJob.join();
+ result = provisioningJob.getResult();
+ if (result.getSeverity() == IStatus.ERROR)
+ {
+ String installableItensNames = "";
+ for (IInstallableUnit iu : installableUnits)
+ {
+ if (installableItensNames.equals(""))
+ {
+ installableItensNames =
+ P2Utilities.getIUExternalizedValue(iu,
+ IInstallableUnit.PROP_NAME);
+ }
+ else
+ {
+ installableItensNames =
+ installableItensNames
+ + ", "
+ + P2Utilities.getIUExternalizedValue(iu,
+ IInstallableUnit.PROP_NAME);
+ }
+ }
+ result =
+ new Status(IStatus.ERROR, InstallerPlugin.PLUGIN_ID,
+ "Some components (" + installableItensNames
+ + ") could not be downloaded.");
+ }
+ }
+ catch (InterruptedException e)
+ {
+ StudioLogger.error("Error while trying to launch p2 job");
+ result =
+ new Status(IStatus.ERROR, InstallerPlugin.PLUGIN_ID, 0,
+ InstallerNLS.P2Utilities_ErrorWhileLaunchingP2Job, null);
+ }
+
+ }
+
+ return result;
+ }
+
+ /**
+ * Installs the given InstallableUnits, from the given repositories, on the current profile.
+ * @param installableUnits to be installed
+ * @param metadataRepositories metadataRepositories to be used
+ * @param artifactRepositories artifactRepositories to be used
+ * @param progressMonitor
+ * @return
+ * @throws InstallerException
+ */
+ protected static IStatus installIu(Collection<IInstallableUnit> installableUnits,
+ Collection<IMetadataRepository> metadataRepositories, IProgressMonitor progressMonitor)
+ throws InstallerException
+ {
+ final InstallOperation op =
+ getInstallOperation(installableUnits, metadataRepositories, progressMonitor);
+ IStatus result = null;
+ if (op != null)
+ {
+
+ result = op.getResolutionResult();
+
+ if (op.hasResolved() && op.getResolutionResult().isOK())
+ {
+ ProvisioningUI defaultUI = ProvisioningUI.getDefaultUI();
+ ProvisioningJob provisioningJob = op.getProvisioningJob(progressMonitor);
+
+ if (provisioningJob instanceof ProfileModificationJob)
+ {
+ ((ProfileModificationJob) provisioningJob)
+ .setRestartPolicy(ProvisioningJob.RESTART_NONE);
+ }
+
+ defaultUI.manageJob(provisioningJob, ProvisioningJob.RESTART_NONE);
+ provisioningJob.schedule();
+
+ try
+ {
+ provisioningJob.join();
+ result = provisioningJob.getResult();
+ if (result.getSeverity() == IStatus.ERROR)
+ {
+ String installableItensNames = "";
+ for (IInstallableUnit iu : installableUnits)
+ {
+ if (installableItensNames.equals(""))
+ {
+ installableItensNames =
+ P2Utilities.getIUExternalizedValue(iu,
+ IInstallableUnit.PROP_NAME);
+ }
+ else
+ {
+ installableItensNames =
+ installableItensNames
+ + ", "
+ + P2Utilities.getIUExternalizedValue(iu,
+ IInstallableUnit.PROP_NAME);
+ }
+ }
+ result =
+ new Status(IStatus.ERROR, InstallerPlugin.PLUGIN_ID,
+ "Some components (" + installableItensNames
+ + ") could not be downloaded.");
+ }
+ }
+ catch (InterruptedException e)
+ {
+ StudioLogger.error("Error while trying to launch p2 job");
+ result =
+ new Status(IStatus.ERROR, InstallerPlugin.PLUGIN_ID, 0,
+ InstallerNLS.P2Utilities_ErrorWhileLaunchingP2Job, null);
+ }
+
+ }
+ }
+ else
+ {
+ result = Status.CANCEL_STATUS;
+ }
+
+ return result;
+ }
+
+ /**
+ * Retrieves a provisioning agent for the given context.
+ * @param context
+ * @return the IprovisioningAgent
+ * @throws InstallerException
+ */
+ protected static IProvisioningAgent getProvisioningAgent(BundleContext context)
+ throws InstallerException
+ {
+ ServiceReference<?> agentProviderRef =
+ context.getServiceReference(IProvisioningAgentProvider.SERVICE_NAME);
+ IProvisioningAgentProvider agentProvider = null;
+ if (agentProviderRef != null)
+ {
+ agentProvider = (IProvisioningAgentProvider) context.getService(agentProviderRef);
+ }
+ IProvisioningAgent agent = null;
+ try
+ {
+ agent = agentProvider.createAgent(null);
+ }
+ catch (ProvisionException e)
+ {
+ throw new InstallerException(e);
+ }
+ return agent;
+ }
+
+ /**
+ * Get the possible localized iu name. Return the iu property name if none translatable name was found
+ * @param iu
+ * @return the name of the IU
+ */
+ protected static String getIUExternalizedValue(IInstallableUnit iu, String property)
+ {
+ String iuNameProperty = iu.getProperty(property, null);
+ String iuName = iuNameProperty;
+
+ if (iuNameProperty == null)
+ {
+ String iuNameTemp = null;
+ String currentLang = Platform.getNL();
+ if (currentLang.contains("_"))
+ {
+ currentLang = currentLang.split("_")[0];
+ iuNameTemp = iu.getProperty(currentLang + "." + iuNameProperty);
+ }
+
+ if (iuNameTemp == null)
+ {
+ iuNameTemp = iu.getProperty("df_LT." + iuNameProperty);
+ }
+
+ if (iuNameTemp != null)
+ {
+ iuName = iuNameTemp;
+ }
+ else
+ {
+ iuName = iu.getProperty(property);
+ }
+
+ }
+
+ return iuName;
+ }
+
+ /**
+ * Retrieves a collection containing all the licences
+ * ILicense given an Installable Unit
+ * @param iu
+ * @return
+ */
+ protected static Collection<ILicense> getLicenses(IInstallableUnit iu)
+ {
+ Collection<ILicense> licenses = iu.getLicenses(null);
+
+ if (licenses.isEmpty())
+ {
+ String currentLang = Platform.getNL();
+ if (currentLang.contains("_"))
+ {
+ currentLang = currentLang.split("_")[0];
+ licenses = iu.getLicenses(currentLang);
+ }
+
+ if (licenses.isEmpty())
+ {
+ licenses = iu.getLicenses("df_LT");
+ }
+
+ if (licenses.isEmpty())
+ {
+ licenses = iu.getLicenses();
+ }
+ }
+
+ return licenses;
+ }
+
+ /**
+ * Check if the installable unit is a group IU
+ * @param unit
+ * @return true if it contains the group property, false otherwise
+ */
+ protected static boolean isGroup(IInstallableUnit unit)
+ {
+ return unit.getProperty(QueryUtil.PROP_TYPE_GROUP) != null ? unit.getProperty(
+ QueryUtil.PROP_TYPE_GROUP).equals("true") : false; //$NON-NLS-1$
+ }
+
+ /**
+ * Given a Installable Unit, retrieves
+ * a list of the requirements descriptions.
+ *
+ * @param iu
+ * @return
+ */
+ static List<String> getRequirements(IInstallableUnit iu)
+ {
+ List<String> results = new ArrayList<String>();
+
+ Collection<IRequirement> requirements = iu.getRequirements();
+ for (Iterator<IRequirement> iterator = requirements.iterator(); iterator.hasNext();)
+ {
+ IRequirement iRequirement = iterator.next();
+ results.add(iRequirement.getDescription());
+ }
+ return results;
+ }
+
+ /**
+ * Mounts a only string with the all the licenses texts.
+ * Uses the '\n' char for new lines between licenses
+ *
+ * @param iu
+ * @return
+ */
+ static String getLicenseText(IInstallableUnit iu)
+ {
+ StringBuffer buffer = new StringBuffer();
+ if (iu != null)
+ {
+ Collection<ILicense> licenses = P2Utilities.getLicenses(iu);
+ for (ILicense license : licenses)
+ {
+ buffer.append(license.getBody());
+ buffer.append("\n\n\n"); //$NON-NLS-1$
+ }
+ }
+ return buffer.toString();
+ }
+
+ /**
+ * Retrieves the UpdateOperation that is necessary to update available IUS on the current Profile.
+ *
+ * @param repositories where the update should be searched
+ * @param progressMonitor
+ * @return
+ * @throws InstallerException
+ */
+ protected static UpdateOperation getUpdateOperation(Collection<URI> repositories,
+ IProgressMonitor progressMonitor) throws InstallerException
+ {
+
+ final ProvisioningUI provisioningUI = ProvisioningUI.getDefaultUI();
+ ProvisioningSession session = provisioningUI.getSession();
+
+ final UpdateOperation op = new UpdateOperation(session);
+
+ final boolean[] canContinue = new boolean[]
+ {
+ false
+ };
+
+ ArrayList<IRepositoryReference> references = new ArrayList<IRepositoryReference>();
+ ArrayList<URI> metadataRepositories = new ArrayList<URI>();
+ ArrayList<URI> artifactRepositories = new ArrayList<URI>();
+ IMetadataRepository repository = null;
+
+ for (Iterator<URI> iterator = repositories.iterator(); iterator.hasNext();)
+ {
+ URI uri = iterator.next();
+
+ try
+ {
+ repository = getMetadataRepository(uri, progressMonitor);
+ metadataRepositories.add(uri);
+ }
+ catch (OperationCanceledException e)
+ {
+ progressMonitor.setCanceled(true);
+ }
+ }
+ if (repository != null)
+ {
+ references.addAll(repository.getReferences());
+ }
+
+ for (IRepositoryReference reference : references)
+ {
+ if (reference.getOptions() == IRepository.ENABLED)
+ {
+ if (reference.getType() == IRepository.TYPE_METADATA)
+ {
+ metadataRepositories.add(reference.getLocation());
+ }
+ else
+ {
+ artifactRepositories.add(reference.getLocation());
+ }
+ }
+ }
+
+ if (!progressMonitor.isCanceled())
+ {
+
+ SubMonitor subMonitor = SubMonitor.convert(progressMonitor);
+ subMonitor.beginTask(InstallerNLS.P2Utilities_PreparingEnvironment, 200);
+
+ op.getProvisioningContext().setMetadataRepositories(
+ metadataRepositories.toArray(new URI[0]));
+
+ op.getProvisioningContext().setArtifactRepositories(
+ artifactRepositories.toArray(new URI[0]));
+
+ subMonitor.worked(100);
+ op.resolveModal(subMonitor.newChild(100));
+ Display.getDefault().syncExec(new Runnable()
+ {
+ public void run()
+ {
+ canContinue[0] =
+ provisioningUI.getPolicy().continueWorkingWithOperation(
+ op,
+ PlatformUI.getWorkbench().getModalDialogShellProvider()
+ .getShell());
+ }
+ });
+ subMonitor.done();
+ }
+ else
+ {
+ canContinue[0] = false;
+ }
+
+ return canContinue[0] ? op : null;
+ }
+
+ /**
+ * Method which will receive an UpdateOperation and will start the update action.
+ *
+ * @param up
+ * @param progressMonitor
+ * @return
+ * @throws InstallerException
+ */
+ protected static IStatus updateIu(UpdateOperation up, IProgressMonitor progressMonitor)
+ throws InstallerException
+ {
+
+ IStatus result =
+ new Status(IStatus.ERROR, InstallerPlugin.PLUGIN_ID, 0,
+ InstallerNLS.P2Utilities_ErrorDuringUpdate, null);
+
+ if ((up != null) && up.hasResolved() && up.getResolutionResult().isOK())
+ {
+
+ ProvisioningJob provisioningJob = up.getProvisioningJob(progressMonitor);
+
+ if (provisioningJob instanceof ProfileModificationJob)
+ {
+ ((ProfileModificationJob) provisioningJob)
+ .setRestartPolicy(ProvisioningJob.RESTART_NONE);
+ }
+
+ try
+ {
+ ProvisioningUI.getDefaultUI().manageJob(provisioningJob,
+ ProvisioningJob.RESTART_NONE);
+ provisioningJob.schedule();
+ }
+ catch (Exception e)
+ {
+ StudioLogger.error(P2Utilities.class, "updateIu error when schedulling Job. ", e);
+ }
+
+ try
+ {
+ provisioningJob.join();
+ result = provisioningJob.getResult();
+ }
+ catch (InterruptedException e)
+ {
+
+ StudioLogger.error(P2Utilities.class, "Error while trying to launch p2 job.", e);
+ result =
+ new Status(IStatus.ERROR, InstallerPlugin.PLUGIN_ID, 0,
+ InstallerNLS.P2Utilities_ErrorWhileLaunchingP2Job, null);
+ }
+ }
+
+ if (!result.isOK())
+ {
+ StudioLogger.error(P2Utilities.class,
+ "updateIu exiting with status different from ok. " + result.toString() + " - "
+ + result.getMessage());
+ }
+ return result;
+ }
+
+}
diff --git a/src/plugins/launch/.classpath b/src/plugins/launch/.classpath
new file mode 100644
index 0000000..8a8f166
--- /dev/null
+++ b/src/plugins/launch/.classpath
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>
+ <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/src/plugins/launch/.project b/src/plugins/launch/.project
new file mode 100644
index 0000000..a0226f5
--- /dev/null
+++ b/src/plugins/launch/.project
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>com.motorola.studio.android.launch</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.ManifestBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.SchemaBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.pde.PluginNature</nature>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+</projectDescription>
diff --git a/src/plugins/launch/META-INF/MANIFEST.MF b/src/plugins/launch/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..b1e35a1
--- /dev/null
+++ b/src/plugins/launch/META-INF/MANIFEST.MF
@@ -0,0 +1,25 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: %pluginName
+Bundle-SymbolicName: com.motorola.studio.android.launch;singleton:=true
+Bundle-Version: 5.0.0.qualifier
+Bundle-Activator: com.motorola.studio.android.launch.LaunchPlugin
+Bundle-Vendor: %providerName
+Require-Bundle: com.motorola.studio.android,
+ com.motorola.studio.android.emulator,
+ com.motorola.studio.android.common,
+ org.eclipse.debug.ui,
+ org.eclipse.jdt.launching,
+ org.eclipse.jdt.core,
+ org.eclipse.sequoyah.device.framework,
+ org.eclipse.sequoyah.device.common.utilities,
+ com.android.ide.eclipse.ddms,
+ com.android.ide.eclipse.adt,
+ org.eclipse.core.runtime,
+ org.eclipse.ui.console,
+ org.eclipse.ui,
+ com.android.ide.eclipse.base
+Bundle-RequiredExecutionEnvironment: JavaSE-1.6
+Bundle-Localization: plugin
+Bundle-ActivationPolicy: lazy
+Export-Package: com.motorola.studio.android.launch.i18n
diff --git a/src/plugins/launch/build.properties b/src/plugins/launch/build.properties
new file mode 100644
index 0000000..cfb221b
--- /dev/null
+++ b/src/plugins/launch/build.properties
@@ -0,0 +1,23 @@
+#
+# Copyright (C) 2012 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+source.. = src/
+output.. = bin/
+bin.includes = META-INF/,\
+ .,\
+ icons/,\
+ plugin.xml,\
+ plugin.properties
diff --git a/src/plugins/launch/icons/category.gif b/src/plugins/launch/icons/category.gif
new file mode 100644
index 0000000..add6a8b
--- /dev/null
+++ b/src/plugins/launch/icons/category.gif
Binary files differ
diff --git a/src/plugins/launch/icons/choose_compatible_avd_instance.png b/src/plugins/launch/icons/choose_compatible_avd_instance.png
new file mode 100644
index 0000000..fd61345
--- /dev/null
+++ b/src/plugins/launch/icons/choose_compatible_avd_instance.png
Binary files differ
diff --git a/src/plugins/launch/icons/motodevapp.gif b/src/plugins/launch/icons/motodevapp.gif
new file mode 100644
index 0000000..add6a8b
--- /dev/null
+++ b/src/plugins/launch/icons/motodevapp.gif
Binary files differ
diff --git a/src/plugins/launch/plugin.properties b/src/plugins/launch/plugin.properties
new file mode 100644
index 0000000..e8d50d1
--- /dev/null
+++ b/src/plugins/launch/plugin.properties
@@ -0,0 +1,39 @@
+#
+# Copyright (C) 2012 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+#################################################################################
+#
+# MOTODEV Studio for Android Launch Plug-in properties
+#
+#################################################################################
+
+pluginName=MOTODEV Studio for Android Launch Plug-in
+providerName=Motorola Mobility, Inc.
+
+launchConfigurationType.name = Android Application using MOTODEV Studio
+
+launchDelegate.name=Android Application MOTODEV Studio Launcher
+launchDelegate.description=The MOTODEV Studio for Android Launcher supports running remote Android applications on an emulator.
+
+launchTabGroup.description=Create a configuration to launch an Android application using MOTODEV Studio
+
+launchShortuctRun.description=Runs an Android Application
+
+command.launchshortcut.run.desc = Deploy and launch an Android application on a device, using MOTODEV Studio
+command.launchshortcut.run.name = Run an Android application using MOTODEV Studio
+
+command.launchshortcut.debug.desc =Deploy and debug an Android application on a device, using MOTODEV Studio
+command.launchshortcut.debug.name = Debug an Android application using MOTODEV Studio \ No newline at end of file
diff --git a/src/plugins/launch/plugin.xml b/src/plugins/launch/plugin.xml
new file mode 100644
index 0000000..eb1ba35
--- /dev/null
+++ b/src/plugins/launch/plugin.xml
@@ -0,0 +1,103 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?eclipse version="3.2"?>
+
+<!--
+ Copyright (C) 2012 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<plugin>
+ <extension
+ point="org.eclipse.debug.core.launchConfigurationTypes">
+ <launchConfigurationType
+ id="androidLaunchConfigurationType"
+ modes="run"
+ name="%launchConfigurationType.name"
+ public="true">
+ </launchConfigurationType>
+ </extension>
+ <extension
+ point="org.eclipse.debug.core.launchDelegates">
+ <launchDelegate
+ delegate="com.motorola.studio.android.launch.StudioAndroidConfigurationDelegate"
+ delegateDescription="%launchDelegate.description"
+ id="androidLaunchDelegate"
+ modes="run, debug"
+ name="%launchDelegate.name"
+ sourceLocatorId="org.eclipse.jdt.launching.sourceLocator.JavaSourceLookupDirector"
+ sourcePathComputerId="org.eclipse.jdt.launching.sourceLookup.javaSourcePathComputer"
+ type="androidLaunchConfigurationType">
+ </launchDelegate>
+ </extension>
+ <extension
+ point="org.eclipse.debug.ui.launchConfigurationTabGroups">
+ <launchConfigurationTabGroup
+ class="com.motorola.studio.android.launch.ui.LaunchConfigurationTabGroup"
+ description="%launchTabGroup.description"
+ id="androidLaunchConfigurationTabGroup"
+ type="androidLaunchConfigurationType">
+ </launchConfigurationTabGroup>
+ </extension>
+ <extension
+ point="org.eclipse.debug.ui.launchConfigurationTypeImages">
+ <launchConfigurationTypeImage
+ configTypeID="androidLaunchConfigurationType"
+ icon="icons/category.gif"
+ id="androidLaunchConfigurationTypeImage">
+ </launchConfigurationTypeImage>
+ </extension>
+ <extension
+ point="org.eclipse.debug.ui.launchShortcuts">
+ <shortcut
+ class="com.motorola.studio.android.launch.LaunchConfigurationShortcut"
+ icon="icons/motodevapp.gif"
+ id="com.motorola.studio.android.launch.android.app"
+ label="%launchConfigurationType.name"
+ modes="run, debug">
+<contextualLaunch>
+ <enablement>
+ <with variable="selection">
+ <count value="1"/>
+ <iterate>
+ <and>
+ <test property="org.eclipse.jdt.launching.hasProjectNature" args="com.android.ide.eclipse.adt.AndroidNature"/>
+ </and>
+ </iterate>
+ </with>
+ </enablement>
+ </contextualLaunch>
+<description
+ description="%launchShortuctRun.description"
+ mode="run, debug">
+</description>
+ </shortcut>
+ </extension>
+ <extension
+ point="org.eclipse.ui.commands">
+ <command
+ categoryId="org.eclipse.debug.ui.category.run"
+ description="%command.launchshortcut.run.desc"
+ id="com.motorola.studio.android.launch.android.app.run"
+ name="%command.launchshortcut.run.name">
+ </command>
+ <command
+ categoryId="org.eclipse.debug.ui.category.run"
+ description="%command.launchshortcut.debug.desc"
+ id="com.motorola.studio.android.launch.android.app.debug"
+ name="%command.launchshortcut.debug.name">
+ </command>
+ </extension>
+
+
+</plugin>
diff --git a/src/plugins/launch/src/com/motorola/studio/android/launch/ILaunchConfigurationConstants.java b/src/plugins/launch/src/com/motorola/studio/android/launch/ILaunchConfigurationConstants.java
new file mode 100644
index 0000000..58bbdbf
--- /dev/null
+++ b/src/plugins/launch/src/com/motorola/studio/android/launch/ILaunchConfigurationConstants.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.motorola.studio.android.launch;
+
+import org.eclipse.jdt.launching.IJavaLaunchConfigurationConstants;
+
+import com.android.ide.eclipse.adt.internal.launch.AndroidLaunchConfiguration.TargetMode;
+import com.android.ide.eclipse.adt.internal.launch.LaunchConfigDelegate;
+
+/**
+ * This interface holds the constants for Launch Configuration
+ */
+@SuppressWarnings("restriction")
+public interface ILaunchConfigurationConstants
+{
+
+ /**
+ * Launch configuration id
+ */
+ public final static String LAUNCH_CONFIGURATION_TYPE_EXTENSION_ID =
+ "androidLaunchConfigurationType";
+
+ public final static String MOTODEV_APP_ICO = "icons/motodevapp.gif";
+
+ public final static String DEFAULT_VALUE = "";
+
+ public final static boolean DEFAULT_BOOL_VALUE = false;
+
+ /**
+ * Launch Configuration attribute ID: Project Name
+ */
+ public final static String ATTR_PROJECT_NAME =
+ IJavaLaunchConfigurationConstants.ATTR_PROJECT_NAME;
+
+ /**
+ * Launch Configuration attribute ID : Terminate Boolean If true, the VM
+ * supports terminate action.
+ */
+ public final static String ATTR_ALLOW_TERMINATE =
+ IJavaLaunchConfigurationConstants.ATTR_ALLOW_TERMINATE;
+
+ public final static boolean ATTR_ALLOW_TERMINATE_DEFAULT = true;
+
+ /**
+ * Launch Configuration attribute ID : Launch Action. The type of launch to
+ * be performed.
+ * 0: launch default activity.
+ * 1: launch specified activity.
+ * 2: Do Nothing
+ *
+ * Should always be 'activity' for now.
+ */
+ public final static String ATTR_LAUNCH_ACTION = LaunchConfigDelegate.ATTR_LAUNCH_ACTION;
+
+ public final static int ATTR_LAUNCH_ACTION_DEFAULT = LaunchConfigDelegate.ACTION_DEFAULT;
+
+ public final static int ATTR_LAUNCH_ACTION_DO_NOTHING = LaunchConfigDelegate.ACTION_DO_NOTHING;
+
+ public final static int ATTR_LAUNCH_ACTION_ACTIVITY = LaunchConfigDelegate.ACTION_ACTIVITY;
+
+ /**
+ * Launch Configuration attribute ID: Activity Name
+ */
+ public final static String ATTR_ACTIVITY = LaunchConfigDelegate.ATTR_ACTIVITY;
+
+ /**
+ * Launch Configuration attribute ID: Target Mode
+ * True: Automatic
+ * False: Manual
+ */
+ public final static String ATTR_TARGET_MODE = LaunchConfigDelegate.ATTR_TARGET_MODE;
+
+ public final static TargetMode ATTR_TARGET_MODE_DEFAULT =
+ LaunchConfigDelegate.DEFAULT_TARGET_MODE;
+
+ /**
+ * This is the attribute we use to store the name of the device. We could use ADT's directly if we were
+ * not forced to remove ADT's entry for it to work with handsets. If we don't store in our own key, the
+ * device name not to be restored the next time the user opens the Run As window, which is against
+ * Eclipse standards.
+ */
+ public final static String ATTR_DEVICE_INSTANCE_NAME =
+ "com.motorola.studio.android.launch.instanceName";
+
+ /**
+ * Launch Configuration attribute ID: Instance Name (VM Name for ADT)
+ */
+ public final static String ATTR_ADT_DEVICE_INSTANCE_NAME = LaunchConfigDelegate.ATTR_AVD_NAME;
+
+ /**
+ * Launch Configuration attribute ID: Emulator Network Speed
+ *
+ * Default value is 0.
+ */
+ public final static String ATTR_SPEED = LaunchConfigDelegate.ATTR_SPEED;
+
+ public final static int ATTR_SPEED_DEFAULT = LaunchConfigDelegate.DEFAULT_SPEED;
+
+ /**
+ * Launch Configuration attribute ID: Emulator Network Latency
+ */
+ public final static String ATTR_DELAY = LaunchConfigDelegate.ATTR_DELAY;
+
+ public final static int ATTR_DELAY_DEFAULT = LaunchConfigDelegate.DEFAULT_DELAY;
+
+ /**
+ * Launch Configuration attribute ID: Wipe Data
+ *
+ * Default value is FALSE.
+ *
+ */
+ public final static String ATTR_WIPE_DATA = LaunchConfigDelegate.ATTR_WIPE_DATA;
+
+ public final static boolean ATTR_WIPE_DATA_DEFAULT = LaunchConfigDelegate.DEFAULT_WIPE_DATA;
+
+ /**
+ * Launch Configuration attribute ID: Boot Animation
+ *
+ * Default value is FALSE.
+ */
+ public final static String ATTR_NO_BOOT_ANIM = LaunchConfigDelegate.ATTR_NO_BOOT_ANIM;
+
+ public final static boolean ATTR_NO_BOOT_ANIM_DEFAULT =
+ LaunchConfigDelegate.DEFAULT_NO_BOOT_ANIM;
+
+ /**
+ * Launch Configuration attribute ID: Command Line
+ *
+ * Additional command line options. Default value is empty.
+ */
+ public final static String ATTR_COMMANDLINE = LaunchConfigDelegate.ATTR_COMMANDLINE;
+
+ /*
+ * Console View ID
+ */
+ public final static String ANDROID_CONSOLE_ID = "Android";
+}
diff --git a/src/plugins/launch/src/com/motorola/studio/android/launch/LaunchConfigurationShortcut.java b/src/plugins/launch/src/com/motorola/studio/android/launch/LaunchConfigurationShortcut.java
new file mode 100644
index 0000000..b849a0f
--- /dev/null
+++ b/src/plugins/launch/src/com/motorola/studio/android/launch/LaunchConfigurationShortcut.java
@@ -0,0 +1,408 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.motorola.studio.android.launch;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.debug.core.DebugPlugin;
+import org.eclipse.debug.core.ILaunchConfiguration;
+import org.eclipse.debug.core.ILaunchConfigurationType;
+import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
+import org.eclipse.debug.core.ILaunchManager;
+import org.eclipse.debug.ui.ILaunchShortcut;
+import org.eclipse.jdt.core.IJavaElement;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.ui.IEditorPart;
+
+import com.motorola.studio.android.adt.ISerialNumbered;
+import com.motorola.studio.android.adt.SdkUtils;
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.devices.DevicesManager;
+import com.motorola.studio.android.launch.i18n.LaunchNLS;
+
+public class LaunchConfigurationShortcut implements ILaunchShortcut
+{
+ /* (non-Javadoc)
+ * @see org.eclipse.debug.ui.ILaunchShortcut#launch(org.eclipse.jface.viewers.ISelection, java.lang.String)
+ */
+ public void launch(ISelection selection, String mode)
+ {
+ ILaunchConfiguration launchConfiguration =
+ getLaunchConfigurationForSelection(selection, true);
+ handleLaunch(mode, launchConfiguration);
+
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.debug.ui.ILaunchShortcut#launch(org.eclipse.ui.IEditorPart, java.lang.String)
+ */
+ public void launch(IEditorPart editor, String mode)
+ {
+ IResource resource = (IResource) editor.getEditorInput().getAdapter(IResource.class);
+ if (resource != null)
+ {
+ ILaunchConfiguration launchConfiguration =
+ getLaunchConfigurationForResource(resource, true);
+ handleLaunch(mode, launchConfiguration);
+ }
+ }
+
+ private void handleLaunch(String mode, ILaunchConfiguration launchConfiguration)
+ {
+ if (launchConfiguration != null)
+ {
+ final ILaunchConfiguration config = launchConfiguration;
+ final String launchMode = mode;
+
+ Job job = new Job("Launch Job")
+ {
+
+ @Override
+ protected IStatus run(IProgressMonitor monitor)
+ {
+ IStatus status = Status.OK_STATUS;
+ try
+ {
+ config.launch(launchMode, monitor);
+ }
+ catch (CoreException e)
+ {
+ status =
+ new Status(
+ IStatus.ERROR,
+ LaunchPlugin.PLUGIN_ID,
+ LaunchNLS.ERR_LaunchConfigurationShortcut_CannotLaunchSelectedResourceMsg,
+ e);
+ }
+ return status;
+ }
+ };
+
+ job.schedule();
+ }
+ else
+ {
+ LaunchUtils.showErrorDialog(LaunchNLS.ERR_LaunchConfigurationShortcut_MsgTitle,
+ LaunchNLS.ERR_LaunchConfigurationShortcut_CannotLaunchSelectedResourceMsg);
+ }
+ }
+
+ /**
+ * Gets a launch configuration for a desired selection
+ *
+ * @param selection The selection
+ * @param create If the launch configuration does not exist, does it must be created?
+ *
+ * @return The launch configuration for the selection
+ */
+
+ private ILaunchConfiguration getLaunchConfigurationForSelection(ISelection selection,
+ boolean create)
+ {
+ ILaunchConfiguration config = null;
+ IStructuredSelection newSelection;
+ Object selectedObject;
+ IResource selectedResource = null;
+
+ if (selection instanceof IStructuredSelection)
+ {
+ newSelection = (IStructuredSelection) selection;
+ selectedObject = newSelection.getFirstElement();
+
+ if (selectedObject instanceof IResource)
+ {
+ selectedResource = (IResource) selectedObject;
+ }
+ else if (selectedObject instanceof IJavaElement)
+ {
+ selectedResource = ((IJavaElement) selectedObject).getResource();
+ }
+
+ if (selectedResource != null)
+ {
+ config = getLaunchConfigurationForResource(selectedResource, create);
+ }
+ }
+
+ return config;
+ }
+
+ /**
+ * Gets a launch configuration for a resource
+ *
+ * @param resource The resource
+ * @param create If the launch configuration does not exist, does it must be created?
+ *
+ * @return The launch configuration for the resource
+ */
+ private ILaunchConfiguration getLaunchConfigurationForResource(IResource resource,
+ boolean create)
+ {
+ IResource app;
+ IResource project;
+ ILaunchConfiguration config = null;
+
+ if (resource != null)
+ {
+ if (resource.getType() == IResource.PROJECT)
+ {
+ project = resource;
+ }
+ else
+ {
+ project = resource.getProject();
+ }
+ // Try to retrieve an existent launch configuration
+ config = findLaunchConfiguration(project);
+
+ if ((config == null) && create)
+ {
+ // No launch configuration could be found. Try to create a
+ // launch configuration with the first runnable activity
+ app = getFirstActivity((IProject) project);
+
+ // If no application could be found, use the project
+ // to create the launch configuration
+ app = app == null ? resource : app;
+ config = createLaunchConfiguration(app);
+ }
+
+ }
+
+ return config;
+ }
+
+ /**
+ * Finds a launch configuration for a descriptor, a mpkg file or a project
+ *
+ * @param resource A descriptor, a mpkg file or a project
+ *
+ * @return A launch configuration or null if it could not be found
+ */
+ private ILaunchConfiguration findLaunchConfiguration(IResource resource)
+ {
+ ILaunchConfiguration launchConfig = null;
+
+ if (resource != null)
+ {
+ try
+ {
+ List<ILaunchConfiguration> projectLC =
+ getProjectLaunchConfigurations(resource.getProject());
+
+ if ((resource.getType() == IResource.PROJECT)
+ || (resource.getType() == IResource.FILE))
+ {
+ // If the resource is a project, return the first launch configuration found
+ // for the project
+ if (!projectLC.isEmpty())
+ {
+ launchConfig = projectLC.iterator().next();
+ }
+ }
+ }
+ catch (CoreException e)
+ {
+ StudioLogger.error(
+ LaunchConfigurationShortcut.class,
+ "Error searching for launch configuration for resource: "
+ + resource.getName(), e);
+ }
+ }
+
+ return launchConfig;
+ }
+
+ /**
+ * Scan for all LaunchConfigurations associated with a project.
+ * @param selectedResource, the project itself or any file within the project to be scanned
+ * @return List with all LaunchConfiguration associated with a project or an empty List if none is found.
+ * @throws CoreException
+ */
+ protected List<ILaunchConfiguration> getProjectLaunchConfigurations(IProject project)
+ throws CoreException
+ {
+ List<ILaunchConfiguration> matches;
+
+ ILaunchManager launchManager = DebugPlugin.getDefault().getLaunchManager();
+ ILaunchConfigurationType motodevLaunchType =
+ launchManager
+ .getLaunchConfigurationType(ILaunchConfigurationConstants.LAUNCH_CONFIGURATION_TYPE_EXTENSION_ID);
+
+ ILaunchConfiguration[] motodevLaunchConfigurations =
+ launchManager.getLaunchConfigurations(motodevLaunchType);
+ matches = new ArrayList<ILaunchConfiguration>(motodevLaunchConfigurations.length);
+ for (ILaunchConfiguration launchConfiguration : motodevLaunchConfigurations)
+ {
+ if (launchConfiguration.getAttribute(ILaunchConfigurationConstants.ATTR_PROJECT_NAME,
+ "").equals(project.getName())) //$NON-NLS-1$
+ {
+ matches.add(launchConfiguration);
+ }
+ }
+
+ return matches;
+ }
+
+ /**
+ * Gets the first runnable application/widget for a project. It can be a
+ * application/widget root folder or a mpkg file
+ *
+ * @param project The project
+ *
+ * @return The first runnable application/widget or null if it does not exist
+ */
+ private IResource getFirstActivity(IProject project)
+ {
+ IResource app = null;
+
+ String[] allActivities = LaunchUtils.getProjectActivities(project);
+
+ if ((allActivities != null) && (allActivities.length >= 1))
+ {
+ app = project.getFile(allActivities[0]);
+ }
+
+ return app;
+ }
+
+ /**
+ * Creates a launch configuration based on a resource
+ *
+ * @param resource The resource
+ *
+ * @return A launch configuration
+ */
+ private ILaunchConfiguration createLaunchConfiguration(IResource resource)
+ {
+ ILaunchConfiguration config = null;
+ ILaunchManager launchManager = DebugPlugin.getDefault().getLaunchManager();
+ ILaunchConfigurationType motodevLaunchType =
+ launchManager
+ .getLaunchConfigurationType(ILaunchConfigurationConstants.LAUNCH_CONFIGURATION_TYPE_EXTENSION_ID);
+ String projectName;
+
+ String configBaseName = resource.getName();
+
+ String launchConfigurationName =
+ launchManager.generateLaunchConfigurationName(configBaseName);
+ try
+ {
+ ILaunchConfigurationWorkingCopy workingCopy =
+ motodevLaunchType.newInstance(null, launchConfigurationName);
+
+ //Set Defaults
+ workingCopy.setAttribute(ILaunchConfigurationConstants.ATTR_PROJECT_NAME,
+ ILaunchConfigurationConstants.DEFAULT_VALUE);
+ workingCopy.setAttribute(ILaunchConfigurationConstants.ATTR_ACTIVITY,
+ ILaunchConfigurationConstants.DEFAULT_VALUE);
+ // It is default not to exist Preferred AVD attribute, so we just set the Studio's
+ // device instance name attribute here
+ workingCopy.setAttribute(ILaunchConfigurationConstants.ATTR_DEVICE_INSTANCE_NAME,
+ (String) null);
+ LaunchUtils.setADTLaunchConfigurationDefaults(workingCopy);
+
+ //Launch Settings
+ IProject project = resource.getProject();
+ projectName = project.getName();
+ workingCopy.setAttribute(ILaunchConfigurationConstants.ATTR_PROJECT_NAME, projectName);
+
+ if (resource.getType() != IResource.PROJECT)
+ {
+ workingCopy.setAttribute(ILaunchConfigurationConstants.ATTR_ACTIVITY,
+ resource.getName());
+ }
+
+ String deviceName = getSelectedInstanceName(project);
+ workingCopy.setAttribute(ILaunchConfigurationConstants.ATTR_DEVICE_INSTANCE_NAME,
+ deviceName);
+ // Preferred AVD name shall only exist in the launch configuration if an AVD is selected
+ Collection<String> validAvds = SdkUtils.getAllValidVmNames();
+ if (validAvds.contains(deviceName))
+ {
+ workingCopy.setAttribute(
+ ILaunchConfigurationConstants.ATTR_ADT_DEVICE_INSTANCE_NAME, deviceName);
+ }
+
+ if (workingCopy.getAttribute(ILaunchConfigurationConstants.ATTR_ACTIVITY,
+ ILaunchConfigurationConstants.DEFAULT_VALUE).equals(""))
+ {
+ workingCopy.setAttribute(ILaunchConfigurationConstants.ATTR_LAUNCH_ACTION,
+ ILaunchConfigurationConstants.ATTR_LAUNCH_ACTION_DEFAULT);
+ }
+
+ config = workingCopy.doSave();
+ }
+ catch (CoreException e)
+ {
+ StudioLogger.error(LaunchConfigurationShortcut.class,
+ "Error creating launch configuration for resource: " + resource.getName(), e);
+ }
+
+ return config;
+ }
+
+ /**
+ * Get a available and compatible instance name.
+ * This method seeks within all registered instances, following the criteria:
+ * Phone device with "full" compatibility (API version = project min. API)
+ * Phone device with "partial" compatibility (API version > project min. API)
+ * Emulator device with "full" compatibility (API version = project min. API)
+ * Emulator device with "partial" compatibility (API version = project min. API)
+ * @param project
+ */
+ protected String getSelectedInstanceName(IProject project)
+ {
+ String selectedDevice = "";
+
+ //get all instances according ddms
+ Collection<ISerialNumbered> instances = DevicesManager.getInstance().getAllDevicesSorted();
+ String candidate = "";
+ for (ISerialNumbered instance : instances)
+ {
+
+ IStatus compatible = LaunchUtils.isCompatible(project, instance);
+ if (compatible.isOK())
+ {
+ selectedDevice = instance.getDeviceName();
+ break;
+ }
+ else if (compatible.getSeverity() == IStatus.WARNING)
+ {
+ candidate = instance.getDeviceName();
+ }
+
+ }
+ if ((selectedDevice.equals("")) && !candidate.equals(""))
+ {
+ selectedDevice = candidate;
+ }
+
+ return selectedDevice;
+ }
+
+}
diff --git a/src/plugins/launch/src/com/motorola/studio/android/launch/LaunchPlugin.java b/src/plugins/launch/src/com/motorola/studio/android/launch/LaunchPlugin.java
new file mode 100644
index 0000000..106a460
--- /dev/null
+++ b/src/plugins/launch/src/com/motorola/studio/android/launch/LaunchPlugin.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.motorola.studio.android.launch;
+
+import org.eclipse.ui.plugin.AbstractUIPlugin;
+import org.osgi.framework.BundleContext;
+
+import com.motorola.studio.android.common.log.StudioLogger;
+
+/**
+ * The activator class controls the plug-in life cycle
+ */
+public class LaunchPlugin extends AbstractUIPlugin
+{
+
+ // The plug-in ID
+ public static final String PLUGIN_ID = "com.motorola.studio.android.launch";
+
+ // The shared instance
+ private static LaunchPlugin plugin;
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * org.eclipse.ui.plugin.AbstractUIPlugin#start(org.osgi.framework.BundleContext
+ * )
+ */
+ @Override
+ public void start(BundleContext context) throws Exception
+ {
+ StudioLogger.debug(LaunchPlugin.class, "Starting MOTODEV Android Launch Plugin...");
+
+ super.start(context);
+ plugin = this;
+
+ StudioLogger.debug(LaunchPlugin.class, "MOTODEV Android Launch Plugin started.");
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * org.eclipse.ui.plugin.AbstractUIPlugin#stop(org.osgi.framework.BundleContext
+ * )
+ */
+ @Override
+ public void stop(BundleContext context) throws Exception
+ {
+ plugin = null;
+ super.stop(context);
+ }
+
+ /**
+ * Returns the shared instance
+ *
+ * @return the shared instance
+ */
+ public static LaunchPlugin getDefault()
+ {
+ return plugin;
+ }
+
+}
diff --git a/src/plugins/launch/src/com/motorola/studio/android/launch/LaunchUtils.java b/src/plugins/launch/src/com/motorola/studio/android/launch/LaunchUtils.java
new file mode 100644
index 0000000..10b7d27
--- /dev/null
+++ b/src/plugins/launch/src/com/motorola/studio/android/launch/LaunchUtils.java
@@ -0,0 +1,515 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.motorola.studio.android.launch;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.ResourcesPlugin;
+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.debug.core.ILaunchConfigurationWorkingCopy;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.sequoyah.device.framework.factory.InstanceRegistry;
+import org.eclipse.sequoyah.device.framework.model.IInstance;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.PlatformUI;
+
+import com.android.ide.eclipse.adt.io.IFolderWrapper;
+import com.android.sdklib.xml.AndroidManifestParser;
+import com.android.sdklib.xml.ManifestData;
+import com.android.sdklib.xml.ManifestData.Activity;
+import com.motorola.studio.android.AndroidPlugin;
+import com.motorola.studio.android.adt.DDMSFacade;
+import com.motorola.studio.android.adt.ISerialNumbered;
+import com.motorola.studio.android.adt.SdkUtils;
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.emulator.core.devfrm.DeviceFrameworkManager;
+import com.motorola.studio.android.emulator.core.model.IAndroidEmulatorInstance;
+import com.motorola.studio.android.emulator.logic.IAndroidLogicInstance;
+import com.motorola.studio.android.launch.i18n.LaunchNLS;
+
+/**
+ * DESCRIPTION: Utilities for Studio for Android Launch use
+ *
+ * RESPONSIBILITY: Provide common utility methods that can be used by any Studio
+ * for Android Launch plugin.
+ *
+ * COLABORATORS: None
+ *
+ * USAGE: This class should not be instantiated and its methods should be called
+ * statically.
+ */
+@SuppressWarnings("restriction")
+public class LaunchUtils
+{
+ /**
+ * Retrieve a instance by name
+ *
+ * @param instanceName
+ * @return IInstance with the given name or null if none is found, or it's not available.
+ */
+ public static String getSerialNumberForInstance(String instanceName)
+ {
+ String serial = null;
+ List<IInstance> list = InstanceRegistry.getInstance().getInstances();
+ for (IInstance inst : list)
+ {
+ if ((inst.getName().equals(instanceName)) && (inst instanceof ISerialNumbered))
+ {
+ serial = ((ISerialNumbered) inst).getSerialNumber();
+ }
+ }
+ return serial;
+ }
+
+ /**
+ * Get a project in the current workspace based on its projectName
+ *
+ * @param projectName
+ * @return the IProject representing the project, or null if none is found
+ */
+ public static IProject getProject(String projectName)
+ {
+ IProject project = null;
+
+ Path projectPath = new Path(projectName);
+ if (projectPath.isValidSegment(projectName))
+ {
+ project = ResourcesPlugin.getWorkspace().getRoot().getProject(projectName);
+ }
+
+ return project;
+ }
+
+ /**
+ * Verify if a given project is supported by the Studio for Android
+ * Launcher, checking if the project is a Android project
+ *
+ * @param project
+ * to be verified
+ * @return true if project is a an Android project, false otherwise.
+ */
+ public static boolean isProjectSupported(IProject project)
+ {
+ boolean hasNature = false;
+ boolean isLibrary = true;
+
+ if ((project != null) && project.isOpen())
+ {
+ try
+ {
+ hasNature = project.hasNature(AndroidPlugin.Android_Nature);
+ isLibrary = SdkUtils.isLibraryProject(project);
+ }
+ catch (CoreException e)
+ {
+ // Do nothing
+ }
+ }
+
+ return hasNature && !isLibrary;
+ }
+
+ /**
+ * Get all Android Projects within the current workspace.
+ *
+ * @return IProject array with all Android projects in the current
+ * workspace, or an empty array if none is found
+ */
+ public static IProject[] getSupportedProjects()
+ {
+ Collection<IProject> projectCollection = new ArrayList<IProject>();
+ IProject[] projectsName = ResourcesPlugin.getWorkspace().getRoot().getProjects();
+
+ /* select only Android projects */
+ for (IProject project : projectsName)
+ {
+ if (project.isAccessible())
+ {
+ if (LaunchUtils.isProjectSupported(project))
+ {
+ projectCollection.add(project);
+ }
+ }
+ }
+
+ return projectCollection.toArray(new IProject[projectCollection.size()]);
+ }
+
+ /**
+ * Retrieve the project activities from the MANIFEST.xml file
+ *
+ * @param project
+ * @return An array of activities.
+ */
+ public static String[] getProjectActivities(IProject project)
+ {
+
+ String[] activities = null;
+ Activity[] adtActivities = null;
+
+ // parse the manifest for the list of activities.
+ try
+ {
+ ManifestData manifestParser = AndroidManifestParser.parse(new IFolderWrapper(project));
+
+ if (manifestParser != null)
+ {
+ adtActivities = manifestParser.getActivities();
+ }
+
+ if ((adtActivities != null) && (adtActivities.length > 0))
+ {
+ activities = new String[adtActivities.length];
+ for (int i = 0; i < adtActivities.length; i++)
+ {
+ activities[i] = adtActivities[i].getName();
+ }
+ }
+
+ }
+ catch (Exception e)
+ {
+ StudioLogger.error(LaunchUtils.class,
+ "An error occurred trying to parse AndroidManifest", e);
+ }
+
+ return activities;
+
+ }
+
+ /**
+ * Set the default launch configuration values
+ */
+ public static void setADTLaunchConfigurationDefaults(
+ ILaunchConfigurationWorkingCopy configuration)
+ {
+ configuration.setAttribute(ILaunchConfigurationConstants.ATTR_ALLOW_TERMINATE,
+ ILaunchConfigurationConstants.ATTR_ALLOW_TERMINATE_DEFAULT);
+ configuration.setAttribute(ILaunchConfigurationConstants.ATTR_LAUNCH_ACTION,
+ ILaunchConfigurationConstants.ATTR_LAUNCH_ACTION_DEFAULT);
+ configuration.setAttribute(ILaunchConfigurationConstants.ATTR_TARGET_MODE,
+ ILaunchConfigurationConstants.ATTR_TARGET_MODE_DEFAULT.toString());
+ configuration.setAttribute(ILaunchConfigurationConstants.ATTR_SPEED,
+ ILaunchConfigurationConstants.ATTR_SPEED_DEFAULT);
+ configuration.setAttribute(ILaunchConfigurationConstants.ATTR_DELAY,
+ ILaunchConfigurationConstants.ATTR_DELAY_DEFAULT);
+ configuration.setAttribute(ILaunchConfigurationConstants.ATTR_WIPE_DATA,
+ ILaunchConfigurationConstants.ATTR_WIPE_DATA_DEFAULT);
+ configuration.setAttribute(ILaunchConfigurationConstants.ATTR_NO_BOOT_ANIM,
+ ILaunchConfigurationConstants.ATTR_NO_BOOT_ANIM_DEFAULT);
+ configuration.setAttribute(ILaunchConfigurationConstants.ATTR_COMMANDLINE,
+ ILaunchConfigurationConstants.DEFAULT_VALUE);
+
+ }
+
+ /**
+ * Update the launch configuration values
+ */
+ public static void updateLaunchConfigurationDefaults(
+ ILaunchConfigurationWorkingCopy configuration)
+ {
+ try
+ {
+ String deviceName =
+ configuration.getAttribute(
+ ILaunchConfigurationConstants.ATTR_DEVICE_INSTANCE_NAME, "");
+
+ if ((deviceName != null) && !deviceName.equals(""))
+ {
+ IAndroidEmulatorInstance deviceInstance =
+ DeviceFrameworkManager.getInstance().getInstanceByName(deviceName);
+
+ if (deviceInstance instanceof IAndroidLogicInstance)
+ {
+ String commandLine =
+ ((IAndroidLogicInstance) deviceInstance).getCommandLineArguments();
+ configuration.setAttribute(ILaunchConfigurationConstants.ATTR_COMMANDLINE,
+ commandLine);
+ }
+ }
+ }
+ catch (CoreException e)
+ {
+ StudioLogger.error(LaunchUtils.class,
+ "Error updating launch configuration values for : " + configuration.getName(),
+ e);
+ }
+ }
+
+ /**
+ * Get the shell of the active workbench or null if there is no active
+ * workbench.
+ *
+ * @return the active workbench shell
+ */
+ public static Shell getActiveWorkbenchShell()
+ {
+ class ActiveShellRunnable implements Runnable
+ {
+ private Shell shell = null;
+
+ public Shell getShell()
+ {
+ return shell;
+ }
+
+ public void run()
+ {
+ IWorkbenchWindow activeWorkbench =
+ PlatformUI.getWorkbench().getActiveWorkbenchWindow();
+
+ if (activeWorkbench != null)
+ {
+ shell = activeWorkbench.getShell();
+ }
+ }
+ }
+ ;
+
+ ActiveShellRunnable runnable = new ActiveShellRunnable();
+ PlatformUI.getWorkbench().getDisplay().syncExec(runnable);
+
+ return runnable.getShell();
+ }
+
+ /**
+ * Show the error message using the given title and message
+ *
+ * @param title
+ * of the error dialog
+ * @param message
+ * to be displayed in the error dialog.
+ */
+ public static void showErrorDialog(final String title, final String message)
+ {
+ PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable()
+ {
+ public void run()
+ {
+ IWorkbenchWindow ww = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
+ MessageDialog.openError(ww.getShell(), title, message);
+ }
+ });
+ }
+
+ public static ISerialNumbered resolveInstance(List<IInstance> instances)
+ {
+ ISerialNumbered theInstance = null;
+ Iterator<IInstance> it = instances.iterator();
+ while (it.hasNext() && (theInstance == null))
+ {
+ IInstance anInstance = it.next();
+ if (anInstance instanceof ISerialNumbered)
+ {
+ theInstance = (ISerialNumbered) anInstance;
+ }
+ }
+ return theInstance;
+ }
+
+ /**
+ * Check if an instanceName is compatible with some project
+ * @param project
+ * @param instanceName
+ * @return {@link IStatus#OK} if fully compatible, {@link IStatus#WARNING} if can be compatible and {@link IStatus#ERROR} if not compatible. Return <code>null</code> if the instance does not exists
+ */
+
+ public static IStatus isCompatible(IProject project, String instanceName)
+ {
+ IStatus status = null;
+ List<IInstance> instances = InstanceRegistry.getInstance().getInstances();
+ for (IInstance instance : instances)
+ {
+ if (instanceName.equals(instance.getName()))
+ {
+ if (instance instanceof ISerialNumbered)
+ {
+ status = isCompatible(project, (ISerialNumbered) instance);
+ break;
+ }
+ }
+ }
+ return status;
+ }
+
+ /**
+ * Check if the given instance name is compatible with the given project
+ * @param project
+ * @param instance
+ * @return {@link IStatus#OK} if fully compatible, {@link IStatus#WARNING} if can be compatible and {@link IStatus#ERROR} if not compatible. Return <code>null</code> if the instance does not exists
+ */
+ public static IStatus isCompatible(IProject project, ISerialNumbered instance)
+ {
+ IStatus compatible = null;
+ int projectAPILevel = SdkUtils.getApiVersionNumberForProject(project);
+ String minSdkVersionStr = SdkUtils.getMinSdkVersion(project);
+ int minSdkVersion;
+ boolean isProjectTargetAPlatform =
+ SdkUtils.getTarget(project) != null ? SdkUtils.getTarget(project).isPlatform()
+ : true;
+
+ try
+ {
+ minSdkVersion = Integer.parseInt(minSdkVersionStr);
+ }
+ catch (Exception e)
+ {
+ // the projectAPILevel will be used and minSdkVersion will be ignored
+ minSdkVersion = projectAPILevel;
+ }
+ String projectTarget = SdkUtils.getTargetNameForProject(project);
+
+ // if the instance is an emulator add the instance only if they have the same target and at least the same APILevel
+ if (instance instanceof IAndroidEmulatorInstance)
+ {
+ IAndroidEmulatorInstance emulatorInstance = (IAndroidEmulatorInstance) instance;
+ int emulatorApi = emulatorInstance.getAPILevel();
+ String emulatorTarget = emulatorInstance.getTarget();
+
+ if (emulatorApi >= minSdkVersion)
+ {
+ String emulatorInstanceName = emulatorInstance.getName();
+ String emulatorInstanceBaseTarget = SdkUtils.getBaseTarget(emulatorInstanceName);
+ boolean isEmulatorTargetAPlatform = SdkUtils.isPlatformTarget(emulatorInstanceName);
+
+ // if they have same target its ok
+ if (emulatorTarget.equals(projectTarget))
+ {
+ compatible = Status.OK_STATUS;
+ }
+ //if the emulator isn't a platform, but the base target is the same as the project, everything is ok
+ else if (!isEmulatorTargetAPlatform
+ && emulatorInstanceBaseTarget.equals(projectTarget))
+ {
+ compatible = Status.OK_STATUS;
+ }
+ else
+ {
+ compatible =
+ new Status(IStatus.WARNING, LaunchPlugin.PLUGIN_ID, NLS.bind(
+ LaunchNLS.UI_LaunchConfigurationTab_WARN_DEVICE_INCOMPATIBLE,
+ emulatorApi, projectAPILevel));
+ }
+ }
+ else
+ {
+ compatible =
+ new Status(IStatus.ERROR, LaunchPlugin.PLUGIN_ID, NLS.bind(
+ LaunchNLS.UI_LaunchConfigurationTab_ERR_EMULATOR_INCOMPATIBLE,
+ emulatorTarget, projectTarget));
+ }
+ }
+ else
+ {
+ if (instance != null)
+ {
+ int deviceSdkVersion = -1;
+ int tries = 0;
+
+ //wait the device to be online
+ while ((tries < 5) && (deviceSdkVersion <= 0))
+ {
+ try
+ {
+ deviceSdkVersion =
+ Integer.parseInt(DDMSFacade.getDeviceProperty(
+ instance.getSerialNumber(), "ro.build.version.sdk"));
+ }
+ catch (NumberFormatException e)
+ {
+ deviceSdkVersion = 0;
+ try
+ {
+ Thread.sleep(100);
+ }
+ catch (InterruptedException e1)
+ {
+ //do nothing
+ }
+ }
+ tries++;
+ }
+
+ if (deviceSdkVersion < minSdkVersion)
+ {
+ compatible =
+ new Status(IStatus.ERROR, LaunchPlugin.PLUGIN_ID, NLS.bind(
+ LaunchNLS.UI_LaunchConfigurationTab_ERR_DEVICE_INCOMPATIBLE,
+ deviceSdkVersion, projectAPILevel));
+ }
+ else if (deviceSdkVersion == projectAPILevel)
+ {
+ if (!isProjectTargetAPlatform)
+ {
+ compatible =
+ new Status(
+ IStatus.WARNING,
+ LaunchPlugin.PLUGIN_ID,
+ LaunchNLS.UI_LaunchConfigurationTab_WARN_DEVICE_TARGET_MISSING);
+ }
+ else
+ {
+ compatible = Status.OK_STATUS;
+ }
+ }
+ else
+ {
+ compatible =
+ new Status(IStatus.WARNING, LaunchPlugin.PLUGIN_ID, NLS.bind(
+ LaunchNLS.UI_LaunchConfigurationTab_WARN_DEVICE_INCOMPATIBLE,
+ deviceSdkVersion, projectAPILevel));
+ }
+ }
+
+ }
+ return compatible;
+ }
+
+ /**
+ * Filter instances the compatible with the given project
+ * @param project whose compatible instances need to be retrieved
+ * @return a new collection containing only the instances that are compatible with the given project
+ **/
+ public static Collection<ISerialNumbered> filterInstancesByProject(
+ Collection<ISerialNumbered> allInstances, IProject project)
+ {
+ Collection<ISerialNumbered> filteredInstances = new LinkedList<ISerialNumbered>();
+
+ for (ISerialNumbered instance : allInstances)
+ {
+ IStatus compatible = LaunchUtils.isCompatible(project, instance);
+
+ if (compatible.getSeverity() != IStatus.ERROR)
+ {
+ filteredInstances.add(instance);
+ }
+ }
+
+ return filteredInstances;
+ }
+
+} \ No newline at end of file
diff --git a/src/plugins/launch/src/com/motorola/studio/android/launch/StudioAndroidConfigurationDelegate.java b/src/plugins/launch/src/com/motorola/studio/android/launch/StudioAndroidConfigurationDelegate.java
new file mode 100644
index 0000000..aec89e5
--- /dev/null
+++ b/src/plugins/launch/src/com/motorola/studio/android/launch/StudioAndroidConfigurationDelegate.java
@@ -0,0 +1,762 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.motorola.studio.android.launch;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.debug.core.ILaunch;
+import org.eclipse.debug.core.ILaunchConfiguration;
+import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
+import org.eclipse.debug.core.ILaunchManager;
+import org.eclipse.debug.ui.DebugUITools;
+import org.eclipse.debug.ui.IDebugUIConstants;
+import org.eclipse.debug.ui.ILaunchGroup;
+import org.eclipse.jface.dialogs.IDialogConstants;
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.jface.viewers.StructuredSelection;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.sequoyah.device.framework.model.IInstance;
+import org.eclipse.sequoyah.device.framework.model.handler.ServiceHandler;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.console.ConsolePlugin;
+import org.eclipse.ui.console.IConsole;
+
+import com.android.ddmlib.AndroidDebugBridge.IClientChangeListener;
+import com.android.ddmlib.Client;
+import com.android.ddmlib.ClientData;
+import com.android.ide.eclipse.adt.AdtPlugin;
+import com.android.ide.eclipse.adt.internal.launch.AndroidLaunch;
+import com.android.ide.eclipse.adt.internal.launch.AndroidLaunchController;
+import com.android.ide.eclipse.adt.internal.launch.LaunchConfigDelegate;
+import com.android.ide.eclipse.adt.internal.preferences.AdtPrefs;
+import com.android.ide.eclipse.adt.internal.sdk.Sdk;
+import com.android.ide.eclipse.adt.io.IFolderWrapper;
+import com.android.sdklib.xml.AndroidManifestParser;
+import com.android.sdklib.xml.ManifestData;
+import com.android.sdklib.xml.ManifestData.Activity;
+import com.motorola.studio.android.adt.DDMSFacade;
+import com.motorola.studio.android.adt.SdkUtils;
+import com.motorola.studio.android.adt.StudioAndroidEventManager;
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.common.preferences.DialogWithToggleUtils;
+import com.motorola.studio.android.emulator.EmulatorPlugin;
+import com.motorola.studio.android.emulator.core.devfrm.DeviceFrameworkManager;
+import com.motorola.studio.android.emulator.core.model.IAndroidEmulatorInstance;
+import com.motorola.studio.android.launch.i18n.LaunchNLS;
+import com.motorola.studio.android.launch.ui.StartedInstancesDialog;
+
+/**
+ * DESCRIPTION: This class is responsible to execute the launch process <br>
+ * RESPONSIBILITY: Perform application launch on a device. <br>
+ * COLABORATORS: none <br>
+ */
+@SuppressWarnings("restriction")
+public class StudioAndroidConfigurationDelegate extends LaunchConfigDelegate
+{
+
+ private static final String ERRONEOUS_LAUNCH_CONFIGURATION = "erroneous.launch.config.dialog";
+
+ private static final String NO_COMPATIBLE_DEVICE = "no.compatible.device.dialog";
+
+ IAndroidEmulatorInstance compatibleInstance = null;
+
+ IAndroidEmulatorInstance initialEmulatorInstance = null;
+
+ public List<Client> waitingDebugger = new ArrayList<Client>();
+
+ private class RunAsClientListener implements IClientChangeListener
+ {
+ /**
+ *
+ */
+ private final IAndroidEmulatorInstance instance;
+
+ /**
+ *
+ */
+ private final String appToLaunch;
+
+ /**
+ *
+ * @param instance
+ */
+ RunAsClientListener(IAndroidEmulatorInstance instance, String appToLaunch)
+ {
+ this.instance = instance;
+ this.appToLaunch = appToLaunch;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.android.ddmlib.AndroidDebugBridge.IClientChangeListener#clientChanged(com.android.ddmlib.Client, int)
+ */
+ public void clientChanged(Client client, int changeMask)
+ {
+ if ((changeMask & Client.CHANGE_NAME) == Client.CHANGE_NAME)
+ {
+ String applicationName = client.getClientData().getClientDescription();
+ if (applicationName != null)
+ {
+ IPreferenceStore store = AdtPlugin.getDefault().getPreferenceStore();
+ String home = store.getString(AdtPrefs.PREFS_HOME_PACKAGE);
+ if (home.equals(applicationName))
+ {
+ String serialNumber = client.getDevice().getSerialNumber();
+ String avdName = DDMSFacade.getNameBySerialNumber(serialNumber);
+ if ((instance != null) && instance.getName().equals(avdName))
+ {
+ StudioLogger.info(StudioAndroidConfigurationDelegate.class,
+ "Delegating launch session to ADT... ");
+
+ synchronized (StudioAndroidConfigurationDelegate.this)
+ {
+ StudioAndroidConfigurationDelegate.this.notify();
+ }
+ }
+ }
+
+ Client removeClient = null;
+ for (Client waiting : waitingDebugger)
+ {
+ int pid = waiting.getClientData().getPid();
+ if (pid == client.getClientData().getPid())
+ {
+ client.getDebuggerListenPort();
+ synchronized (StudioAndroidConfigurationDelegate.this)
+ {
+ StudioAndroidConfigurationDelegate.this.notify();
+ }
+ removeClient = waiting;
+ break;
+ }
+ }
+
+ if (removeClient != null)
+ {
+ waitingDebugger.remove(removeClient);
+ }
+ }
+ }
+
+ if ((changeMask & Client.CHANGE_DEBUGGER_STATUS) == Client.CHANGE_DEBUGGER_STATUS)
+ {
+ ClientData clientData = client.getClientData();
+ String applicationName = clientData.getClientDescription();
+ if (clientData.getDebuggerConnectionStatus() == ClientData.DebuggerStatus.DEFAULT)
+ {
+ if (((appToLaunch != null) && (applicationName != null))
+ && applicationName.equals(appToLaunch.substring(0,
+ appToLaunch.lastIndexOf("."))))
+ {
+ client.getDebuggerListenPort();
+ synchronized (StudioAndroidConfigurationDelegate.this)
+ {
+ StudioAndroidConfigurationDelegate.this.notify();
+ }
+ }
+ else if (appToLaunch != null)
+ {
+ waitingDebugger.add(client);
+ }
+ }
+ }
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.debug.core.model.LaunchConfigurationDelegate#preLaunchCheck(org.eclipse.debug.core.ILaunchConfiguration, java.lang.String, org.eclipse.core.runtime.IProgressMonitor)
+ */
+ @Override
+ public boolean preLaunchCheck(ILaunchConfiguration configuration, String mode,
+ IProgressMonitor monitor) throws CoreException
+ {
+ initialEmulatorInstance = null;
+ boolean isOk = super.preLaunchCheck(configuration, mode, monitor);
+
+ if (isOk)
+ {
+ final String instanceName =
+ configuration.getAttribute(
+ ILaunchConfigurationConstants.ATTR_DEVICE_INSTANCE_NAME, (String) null);
+
+ // we found an instance
+ if ((instanceName != null) && (instanceName.length() > 0))
+ {
+ IAndroidEmulatorInstance instance =
+ DeviceFrameworkManager.getInstance().getInstanceByName(instanceName);
+ if (instance == null)
+ {
+ String serialNumber = LaunchUtils.getSerialNumberForInstance(instanceName);
+ if (!DDMSFacade.isDeviceOnline(serialNumber))
+ {
+ isOk = false;
+ handleErrorDuringLaunch(configuration, mode, instanceName);
+ }
+ }
+ else
+ {
+ if (!instance.isAvailable())
+ {
+ isOk = false;
+ handleErrorDuringLaunch(configuration, mode, instanceName);
+ }
+
+ if (!instance.isStarted())
+ {
+ initialEmulatorInstance = instance;
+ //updates the compatible instance with user response
+ isOk = checkForCompatibleRunningInstances(configuration);
+ }
+ }
+ }
+ else
+ {
+ isOk = false;
+ handleErrorDuringLaunch(configuration, mode, null);
+ }
+ }
+ // validate if the project isn't a library project
+ if (isOk)
+ {
+ String projectName =
+ configuration.getAttribute(ILaunchConfigurationConstants.ATTR_PROJECT_NAME,
+ (String) null);
+ if (projectName != null)
+ {
+ IProject project = ResourcesPlugin.getWorkspace().getRoot().getProject(projectName);
+ if ((project != null) && SdkUtils.isLibraryProject(project))
+ {
+ handleProjectError(configuration, project, mode);
+ isOk = false;
+ }
+ }
+ }
+
+ return isOk;
+ }
+
+ private void handleProjectError(final ILaunchConfiguration config, final IProject project,
+ final String mode)
+ {
+ PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable()
+ {
+
+ public void run()
+ {
+ Shell shell = LaunchUtils.getActiveWorkbenchShell();
+
+ String message = LaunchNLS.UI_LaunchConfigurationTab_ERR_PROJECT_IS_LIBRARY;
+
+ String prefKey = ERRONEOUS_LAUNCH_CONFIGURATION;
+
+ DialogWithToggleUtils.showInformation(prefKey,
+ LaunchNLS.ERR_LaunchConfigurationShortcut_MsgTitle, message);
+
+ StructuredSelection struturedSelection;
+
+ String groupId = IDebugUIConstants.ID_RUN_LAUNCH_GROUP;
+
+ ILaunchGroup group = DebugUITools.getLaunchGroup(config, mode);
+ groupId = group.getIdentifier();
+ struturedSelection = new StructuredSelection(config);
+
+ DebugUITools.openLaunchConfigurationDialogOnGroup(shell, struturedSelection,
+ groupId);
+ }
+ });
+ }
+
+ private void handleErrorDuringLaunch(final ILaunchConfiguration config, final String mode,
+ final String instanceName)
+ {
+ PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable()
+ {
+
+ public void run()
+ {
+ Shell shell = LaunchUtils.getActiveWorkbenchShell();
+
+ String message =
+ instanceName != null ? NLS.bind(
+ LaunchNLS.ERR_LaunchDelegate_InvalidDeviceInstance, instanceName)
+ : NLS.bind(LaunchNLS.ERR_LaunchDelegate_No_Compatible_Device,
+ config.getName());
+
+ String prefKey =
+ instanceName != null ? ERRONEOUS_LAUNCH_CONFIGURATION
+ : NO_COMPATIBLE_DEVICE;
+
+ DialogWithToggleUtils.showInformation(prefKey,
+ LaunchNLS.ERR_LaunchConfigurationShortcut_MsgTitle, message);
+
+ StructuredSelection struturedSelection;
+
+ String groupId = IDebugUIConstants.ID_RUN_LAUNCH_GROUP;
+
+ ILaunchGroup group = DebugUITools.getLaunchGroup(config, mode);
+ groupId = group.getIdentifier();
+ struturedSelection = new StructuredSelection(config);
+
+ DebugUITools.openLaunchConfigurationDialogOnGroup(shell, struturedSelection,
+ groupId);
+ }
+ });
+ }
+
+ /**
+ * Launches an Android application based on the given launch configuration.
+ */
+ @Override
+ public void launch(ILaunchConfiguration configuration, String mode, ILaunch launch,
+ IProgressMonitor monitor) throws CoreException
+
+ {
+ //use a working copy because it can be changed and these changes should not be propagated to the original copy
+ ILaunchConfigurationWorkingCopy configurationWorkingCopy = configuration.getWorkingCopy();
+
+ StudioLogger.info(StudioAndroidConfigurationDelegate.class,
+ "Launch Android Application using Studio for Android wizard. Configuration: "
+ + configurationWorkingCopy + " mode:" + mode + " launch: " + launch);
+ try
+ {
+
+ String projectName =
+ configurationWorkingCopy.getAttribute(
+ ILaunchConfigurationConstants.ATTR_PROJECT_NAME, (String) null);
+ int launchAction =
+ configurationWorkingCopy.getAttribute(
+ ILaunchConfigurationConstants.ATTR_LAUNCH_ACTION,
+ ILaunchConfigurationConstants.ATTR_LAUNCH_ACTION_DEFAULT);
+
+ String instanceName =
+ configurationWorkingCopy.getAttribute(
+ ILaunchConfigurationConstants.ATTR_DEVICE_INSTANCE_NAME, (String) null);
+
+ if ((projectName != null) && (instanceName != null))
+ {
+ IProject project = ResourcesPlugin.getWorkspace().getRoot().getProject(projectName);
+ if (project == null)
+ {
+ IStatus status =
+ new Status(Status.ERROR, LaunchPlugin.PLUGIN_ID,
+ "Could not retrieve project: " + projectName);
+ throw new CoreException(status);
+ }
+
+ String appToLaunch = null;
+ if (launchAction == ILaunchConfigurationConstants.ATTR_LAUNCH_ACTION_DEFAULT)
+ {
+ ManifestData manifestParser =
+ AndroidManifestParser.parse(new IFolderWrapper(project));
+ Activity launcherActivity = manifestParser.getLauncherActivity();
+ String activityName = null;
+ if (launcherActivity != null)
+ {
+ activityName = launcherActivity.getName();
+ }
+
+ // if there's no default activity. Then there's nothing to be launched.
+ if (activityName != null)
+ {
+ appToLaunch = activityName;
+ }
+ }
+ // case for a specific activity
+ else if (launchAction == ILaunchConfigurationConstants.ATTR_LAUNCH_ACTION_ACTIVITY)
+ {
+ appToLaunch =
+ configurationWorkingCopy.getAttribute(
+ ILaunchConfigurationConstants.ATTR_ACTIVITY, (String) null);
+
+ if ((appToLaunch == null) || "".equals(appToLaunch))
+ {
+ IStatus status =
+ new Status(
+ Status.ERROR,
+ LaunchPlugin.PLUGIN_ID,
+ "Activity field cannot be empty. Specify an activity or use the default activity on launch configuration.");
+ throw new CoreException(status);
+ }
+ }
+ // for the do nothing case there is nothing to do
+
+ IAndroidEmulatorInstance emuInstance =
+ DeviceFrameworkManager.getInstance().getInstanceByName(instanceName);
+
+ RunAsClientListener list = null;
+
+ //if initialEmulatorInstance is not null it means that it was offline and user has interacted with StartedInstancesDialog.
+ //The emuInstance variable should be overrided by the initialEmulatorInstance because the emuInstance can be the new
+ //user choice (in case he has selected the check box in dialog asking to update the run configuration)
+ if (initialEmulatorInstance != null)
+ {
+ emuInstance = initialEmulatorInstance;
+ }
+
+ try
+ {
+ if (appToLaunch != null)
+ {
+ list = new RunAsClientListener(emuInstance, appToLaunch);
+ StudioAndroidEventManager.asyncAddClientChangeListener(list);
+ }
+
+ // The instance from the launch configuration is an emulator (because the query returned
+ // something different from null) and is not started.
+ if ((emuInstance != null) && (!emuInstance.isStarted()))
+ {
+ if (compatibleInstance != null)
+ {
+ emuInstance = compatibleInstance;
+ instanceName = emuInstance.getName();
+ configurationWorkingCopy.setAttribute(
+ ILaunchConfigurationConstants.ATTR_DEVICE_INSTANCE_NAME,
+ emuInstance.getName());
+ configurationWorkingCopy.setAttribute(
+ ILaunchConfigurationConstants.ATTR_ADT_DEVICE_INSTANCE_NAME,
+ emuInstance.getName());
+ }
+ else
+ {
+ startEmuInstance(emuInstance);
+ }
+ }
+
+ StudioLogger.info(StudioAndroidConfigurationDelegate.class,
+ "AVD where the application will be executed: " + instanceName);
+
+
+ String serialNumber = LaunchUtils.getSerialNumberForInstance(instanceName);
+ if (serialNumber == null)
+ {
+ IStatus status =
+ new Status(Status.ERROR, LaunchPlugin.PLUGIN_ID,
+ "Could not retrieve AVD instance: " + instanceName);
+ throw new CoreException(status);
+ }
+
+ bringConsoleView();
+
+ // Determining if it is an emulator or handset and creating the description
+ //to be used for usage data collection
+ String descriptionToLog = "";
+ if (emuInstance != null)
+ {
+ descriptionToLog = StudioLogger.VALUE_EMULATOR;
+ }
+ else
+ {
+ if ((serialNumber != null) && (!serialNumber.equals("")))
+ {
+ descriptionToLog = StudioLogger.VALUE_HANDSET;
+ }
+ }
+
+ if (!descriptionToLog.equals(""))
+ {
+ descriptionToLog =
+ StudioLogger.KEY_DEVICE_TYPE + descriptionToLog
+ + StudioLogger.SEPARATOR;
+ }
+
+ descriptionToLog = descriptionToLog + StudioLogger.KEY_USE_VDL;
+
+ descriptionToLog = descriptionToLog + StudioLogger.VALUE_NO;
+ super.launch(configurationWorkingCopy, mode, launch, monitor);
+
+ // Collecting usage data for statistical purposes
+ try
+ {
+ String prjTarget = "";
+ if (project != null)
+ {
+ prjTarget = Sdk.getCurrent().getTarget(project).getName();
+ }
+
+ if (!descriptionToLog.equals(""))
+ {
+ descriptionToLog = descriptionToLog + StudioLogger.SEPARATOR;
+ }
+
+ descriptionToLog =
+ descriptionToLog + StudioLogger.KEY_PRJ_TARGET + prjTarget;
+
+ if (emuInstance != null)
+ {
+ String emuTarget = emuInstance.getTarget();
+ descriptionToLog = descriptionToLog + StudioLogger.SEPARATOR;
+ descriptionToLog =
+ descriptionToLog + StudioLogger.KEY_TARGET + emuTarget;
+ }
+
+ StudioLogger.collectUsageData(mode, StudioLogger.KIND_APP_MANAGEMENT,
+ descriptionToLog, LaunchPlugin.PLUGIN_ID, LaunchPlugin.getDefault()
+ .getBundle().getVersion().toString());
+ }
+ catch (Throwable e)
+ {
+ //Do nothing, but error on the log should never prevent app from working
+ }
+
+ }
+ finally
+ {
+ if (list != null)
+ {
+ StudioAndroidEventManager.asyncRemoveClientChangeListener(list);
+ }
+ StudioAndroidEventManager.asyncAddClientChangeListener(AndroidLaunchController
+ .getInstance());
+ }
+ }
+ else
+ {
+ throw new CoreException(new Status(IStatus.ERROR, LaunchPlugin.PLUGIN_ID,
+ "Missing parameters for launch"));
+ }
+ }
+ catch (CoreException e)
+ {
+ AndroidLaunch androidLaunch = (AndroidLaunch) launch;
+ androidLaunch.stopLaunch();
+ StudioLogger.error(StudioAndroidConfigurationDelegate.class, "Error while lauching "
+ + configurationWorkingCopy.getName(), e);
+ throw e;
+ }
+ catch (Exception e)
+ {
+ StudioLogger.error(LaunchUtils.class,
+ "An error occurred trying to parse AndroidManifest", e);
+ }
+ finally
+ {
+ if (mode.equals(ILaunchManager.RUN_MODE))
+ {
+ AndroidLaunch androidLaunch = (AndroidLaunch) launch;
+ androidLaunch.stopLaunch();
+ }
+ }
+ }
+
+ /**
+ * @param project
+ * @param emuInstance
+ * @throws CoreException
+ */
+ private boolean checkForCompatibleRunningInstances(ILaunchConfiguration configuration)
+ throws CoreException
+ {
+ IProject project = null;
+ compatibleInstance = null;
+
+ final String projectName =
+ configuration.getAttribute(ILaunchConfigurationConstants.ATTR_PROJECT_NAME,
+ (String) null);
+
+ project = ResourcesPlugin.getWorkspace().getRoot().getProject(projectName);
+ if (project == null)
+ {
+ IStatus status =
+ new Status(Status.ERROR, LaunchPlugin.PLUGIN_ID, "Could not retrieve project: "
+ + projectName);
+ throw new CoreException(status);
+ }
+
+ //Check if there is a compatible instance running to launch the app
+ Collection<IAndroidEmulatorInstance> startedInstances =
+ DeviceFrameworkManager.getInstance().getAllStartedInstances();
+
+ final Collection<IAndroidEmulatorInstance> compatibleStartedInstances =
+ new HashSet<IAndroidEmulatorInstance>();
+
+ boolean continueLaunch = true;
+
+ for (IAndroidEmulatorInstance i : startedInstances)
+ {
+ IStatus resultStatus = LaunchUtils.isCompatible(project, i.getName());
+ if ((resultStatus.getSeverity() == Status.OK)
+ || (resultStatus.getSeverity() == Status.WARNING))
+ {
+ compatibleStartedInstances.add(i);
+ }
+ }
+ if (compatibleStartedInstances.size() > 0)
+ {
+ //show a dialog with compatible running instances so the user can
+ //choose one to run the app, or he can choose to run the preferred AVD
+
+ StartedInstancesDialogProxy proxy =
+ new StartedInstancesDialogProxy(compatibleStartedInstances, configuration,
+ project);
+ PlatformUI.getWorkbench().getDisplay().syncExec(proxy);
+
+ compatibleInstance = proxy.getSelectedInstance();
+ continueLaunch = proxy.continueLaunch();
+ }
+ return continueLaunch;
+ }
+
+ private class StartedInstancesDialogProxy implements Runnable
+ {
+ private IAndroidEmulatorInstance selectedInstance = null;
+
+ private boolean continueLaunch = true;
+
+ private final ILaunchConfiguration configuration;
+
+ Collection<IAndroidEmulatorInstance> compatibleStartedInstances = null;
+
+ IProject project = null;
+
+ /**
+ *
+ */
+ public StartedInstancesDialogProxy(
+ Collection<IAndroidEmulatorInstance> compatibleStartedInstances,
+ ILaunchConfiguration configuration, IProject project)
+ {
+ this.compatibleStartedInstances = compatibleStartedInstances;
+ this.configuration = configuration;
+ this.project = project;
+ }
+
+ public void run()
+ {
+ Shell aShell = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell();
+ Shell shell = new Shell(aShell);
+ StartedInstancesDialog dialog;
+ try
+ {
+ dialog =
+ new StartedInstancesDialog(shell, compatibleStartedInstances,
+ configuration, project);
+ dialog.setBlockOnOpen(true);
+ dialog.open();
+
+ selectedInstance = null;
+ if (dialog.getReturnCode() == IDialogConstants.OK_ID)
+ {
+ selectedInstance = dialog.getSelectedInstance();
+ }
+ else if (dialog.getReturnCode() == IDialogConstants.ABORT_ID)
+ {
+ continueLaunch = false;
+ }
+ }
+ catch (CoreException e)
+ {
+ StudioLogger.error(StudioAndroidConfigurationDelegate.class,
+ "It was not possible to open Started Instance Dialog", e);
+ }
+ }
+
+ public IAndroidEmulatorInstance getSelectedInstance()
+ {
+ return selectedInstance;
+ }
+
+ public boolean continueLaunch()
+ {
+ return continueLaunch;
+ }
+ }
+
+ /**
+ * Bring Console View to the front and activate the appropriate stream
+ *
+ */
+ private void bringConsoleView()
+ {
+ IConsole activeConsole = null;
+
+ IConsole[] consoles = ConsolePlugin.getDefault().getConsoleManager().getConsoles();
+ for (IConsole console : consoles)
+ {
+ if (console.getName().equals(ILaunchConfigurationConstants.ANDROID_CONSOLE_ID))
+ {
+ activeConsole = console;
+ }
+ }
+
+ // Bring Console View to the front
+ if (activeConsole != null)
+ {
+ ConsolePlugin.getDefault().getConsoleManager().showConsoleView(activeConsole);
+ }
+
+ }
+
+ /**
+ *
+ * @param instance
+ * @throws CoreException
+ */
+ private void startEmuInstance(IAndroidEmulatorInstance instance) throws CoreException
+ {
+ StudioLogger.info(StudioAndroidConfigurationDelegate.class,
+ "Needs to Start the AVD instance before launching... ");
+
+ ServiceHandler startHandler = EmulatorPlugin.getStartServiceHandler();
+ IStatus status = startHandler.run((IInstance) instance, null, new NullProgressMonitor());
+
+ StudioLogger.info(StudioAndroidConfigurationDelegate.class,
+ "Status of the launch service: " + status);
+
+ if (status.getSeverity() == Status.ERROR)
+ {
+ throw new CoreException(status);
+ }
+ else if (status.getSeverity() == Status.CANCEL)
+ {
+ StudioLogger.info(StudioAndroidConfigurationDelegate.class,
+ "Abort launch session because the AVD start was canceled. ");
+ return;
+ }
+
+ if (!instance.isStarted())
+ {
+ status =
+ new Status(Status.ERROR, LaunchPlugin.PLUGIN_ID,
+ "The Android Virtual Device is not started: " + instance.getName());
+ throw new CoreException(status);
+ }
+
+ synchronized (this)
+ {
+ try
+ {
+ wait();
+ }
+ catch (InterruptedException e)
+ {
+ StudioLogger.info("Could not wait: ", e.getMessage());
+ }
+ }
+ }
+}
diff --git a/src/plugins/launch/src/com/motorola/studio/android/launch/i18n/LaunchNLS.java b/src/plugins/launch/src/com/motorola/studio/android/launch/i18n/LaunchNLS.java
new file mode 100644
index 0000000..9894057
--- /dev/null
+++ b/src/plugins/launch/src/com/motorola/studio/android/launch/i18n/LaunchNLS.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.motorola.studio.android.launch.i18n;
+
+import org.eclipse.osgi.util.NLS;
+
+/**
+ * DESCRIPTION: This class is the NLS component for Launch plugin. It is the
+ * main class for internationalization
+ *
+ * RESPONSIBILITY: Provide local strings for using throughout the tool
+ *
+ * COLABORATORS: messages.properties: file that contains the strings that will
+ * be provided to the plugin
+ *
+ * USAGE: Use any of the public static variables for accessing the local strings
+ */
+public class LaunchNLS extends NLS
+{
+ /**
+ * The bundle location. It refers to messages.properties file inside this
+ * package
+ */
+
+ static
+ {
+ LaunchNLS.initializeMessages("com.motorola.studio.android.launch.i18n.launchNLS",
+ LaunchNLS.class);
+ }
+
+ /*
+ * UI string area
+ */
+
+ public static String LaunchComposite_UI_LaunchComposite_DestinationGroupText;
+
+ public static String LaunchConfigurationTab_CreateNewAVDLink;
+
+ public static String LaunchConfigurationTab_DoNothingButton;
+
+ public static String LaunchConfigurationTab_LaunchButton;
+
+ public static String UI_LaunchComposite_ProjectNameLabel;
+
+ public static String UI_LaunchComposite_ActivityDefaultButton;
+
+ public static String UI_LaunchComposite_ActivityGroupLabel;
+
+ public static String UI_LaunchComposite_DeviceNameLabel;
+
+ public static String UI_LaunchComposite_BrowseButton;
+
+ public static String UI_LaunchComposite_ProjectRequiredMessage;
+
+ public static String UI_LaunchComposite_ProjectRequiredTitle;
+
+ public static String UI_LaunchComposite_SelectProjectScreenTitle;
+
+ public static String UI_LaunchComposite_SelectProjectScreenMessage;
+
+ public static String UI_LaunchComposite_SelectActivityScreenTitle;
+
+ public static String UI_LaunchComposite_SelectActivityScreenMessage;
+
+ public static String UI_LaunchComposite_SelectDeviceScreenTitle;
+
+ public static String UI_LaunchComposite_SelectDeviceScreenMessage;
+
+ public static String UI_LaunchConfigurationTab_ERR_DEVICE_INEXISTENT;
+
+ public static String UI_LaunchConfigurationTab_ERR_DEVICE_INCOMPATIBLE;
+
+ public static String UI_LaunchConfigurationTab_ERR_INVALID_ACTIVITY;
+
+ public static String UI_LaunchConfigurationTab_ERR_ACTIVITY_NOT_EXIST;
+
+ public static String UI_LaunchConfigurationTab_ERR_PROJECT_NOT_EXIST;
+
+ public static String UI_LaunchConfigurationTab_WARN_DEVICE_INCOMPATIBLE;
+
+ public static String UI_LaunchConfigurationTab_Tab_Name;
+
+ public static String UI_LaunchConfigurationTab_InfoSelectInstance;
+
+ public static String UI_LaunchConfigurationTab_InfoSelectActivity;
+
+ public static String UI_LaunchConfigurationTab_InfoSelectProject;
+
+ public static String ERR_LaunchConfigurationShortcut_MsgTitle;
+
+ public static String ERR_LaunchConfigurationShortcut_CannotLaunchSelectedResourceMsg;
+
+ public static String ERR_LaunchDelegate_InvalidDeviceInstance;
+
+ public static String ERR_LaunchDelegate_No_Compatible_Device;
+
+ public static String UI_LaunchConfigurationTab_ERR_EMULATOR_INCOMPATIBLE;
+
+ public static String UI_LaunchConfigurationTab_WARN_DEVICE_TARGET_MISSING;
+
+ public static String UI_LaunchConfigurationTab_ERR_PROJECT_IS_LIBRARY;
+
+ public static String UI_StartedInstancesDialog_CompatibleAvdsColumnName;
+
+ public static String UI_StartedInstancesDialog_Message;
+
+ public static String UI_StartedInstancesDialog_Title;
+
+ public static String UI_StartedInstancesDialog_WindowTitle;
+
+ public static String UI_StartedInstancesDialog_ApiLevel;
+
+ public static String UI_StartedInstancesDialog_Tooltip;
+
+ public static String UI_StartedInstancesDialog_UpdateRunConfigurarion;
+}
diff --git a/src/plugins/launch/src/com/motorola/studio/android/launch/i18n/launchNLS.properties b/src/plugins/launch/src/com/motorola/studio/android/launch/i18n/launchNLS.properties
new file mode 100644
index 0000000..6a7113f
--- /dev/null
+++ b/src/plugins/launch/src/com/motorola/studio/android/launch/i18n/launchNLS.properties
@@ -0,0 +1,57 @@
+#
+# Copyright (C) 2012 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LaunchComposite_UI_LaunchComposite_DestinationGroupText=Basic settings
+LaunchConfigurationTab_CreateNewAVDLink=<a>Create new AVD...</a>
+LaunchConfigurationTab_DoNothingButton=Do Nothing
+LaunchConfigurationTab_LaunchButton=Launch:
+UI_LaunchComposite_ProjectNameLabel=Project:
+UI_LaunchComposite_ActivityGroupLabel=Activity
+UI_LaunchComposite_DeviceNameLabel=Device:
+UI_LaunchComposite_BrowseButton=Browse
+UI_LaunchComposite_ActivityDefaultButton=Launch Default Activity
+UI_LaunchComposite_ProjectRequiredMessage=A project must be selected before browsing for an activity
+UI_LaunchComposite_ProjectRequiredTitle=Project required
+UI_LaunchComposite_SelectProjectScreenTitle=Project Selection
+UI_LaunchComposite_SelectProjectScreenMessage=Select a project
+UI_LaunchComposite_SelectActivityScreenTitle=Activity Selection
+UI_LaunchComposite_SelectActivityScreenMessage=Select an activity
+UI_LaunchComposite_SelectDeviceScreenTitle=Device Selection
+UI_LaunchComposite_SelectDeviceScreenMessage=Select a device instance
+UI_LaunchConfigurationTab_ERR_DEVICE_INEXISTENT=The selected device instance does not exist or is invalid.
+UI_LaunchConfigurationTab_ERR_DEVICE_INCOMPATIBLE=The selected device instance is not compatible. Its API level is {0} while the project API level is {1}
+UI_LaunchConfigurationTab_ERR_EMULATOR_INCOMPATIBLE=The selected device instance is not compatible. Its Device target is {0} while the Project target is {1}
+UI_LaunchConfigurationTab_ERR_INVALID_ACTIVITY=The selected file is not a valid activity.
+UI_LaunchConfigurationTab_ERR_ACTIVITY_NOT_EXIST=The selected activity does not exist.
+UI_LaunchConfigurationTab_ERR_PROJECT_NOT_EXIST=The selected project does not exist.
+UI_LaunchConfigurationTab_ERR_PROJECT_IS_LIBRARY=Cannot launch library projects.
+UI_LaunchConfigurationTab_WARN_DEVICE_INCOMPATIBLE=The selected device instance has an API level ({0}) higher than the project API level ({1})
+UI_LaunchConfigurationTab_WARN_DEVICE_TARGET_MISSING=The selected instance isn't an emulator and its target cannot be checked
+UI_LaunchConfigurationTab_Tab_Name=Main
+UI_LaunchConfigurationTab_InfoSelectInstance=Select a device instance
+UI_LaunchConfigurationTab_InfoSelectActivity=Select an activity
+UI_LaunchConfigurationTab_InfoSelectProject=Select a project
+UI_StartedInstancesDialog_CompatibleAvdsColumnName=Compatible online AVDs
+UI_StartedInstancesDialog_Message=Select an online AVD instance and click OK to launch the project in that AVD. \nClick Ignore to start the preferred AVD, or click Abort to cancel the launch.
+UI_StartedInstancesDialog_Title=AVD "{0}" is offline.
+UI_StartedInstancesDialog_WindowTitle=Preferred AVD offline
+UI_StartedInstancesDialog_ApiLevel=API Level
+UI_StartedInstancesDialog_Tooltip=The project and device API level are not an exact match
+UI_StartedInstancesDialog_UpdateRunConfigurarion=Update configuration to use selected AVD
+ERR_LaunchConfigurationShortcut_MsgTitle=MOTODEV Studio For Android
+ERR_LaunchConfigurationShortcut_CannotLaunchSelectedResourceMsg=The selected resource cannot be launched.
+ERR_LaunchDelegate_InvalidDeviceInstance=Device instance "{0}" does not exist or is not available.\nPlease verify and try again.
+ERR_LaunchDelegate_No_Compatible_Device=No compatible device instance was found to run "{0}". Review your configuration and try again. \ No newline at end of file
diff --git a/src/plugins/launch/src/com/motorola/studio/android/launch/ui/AndroidProjectsSelectionDialog.java b/src/plugins/launch/src/com/motorola/studio/android/launch/ui/AndroidProjectsSelectionDialog.java
new file mode 100644
index 0000000..c775271
--- /dev/null
+++ b/src/plugins/launch/src/com/motorola/studio/android/launch/ui/AndroidProjectsSelectionDialog.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.motorola.studio.android.launch.ui;
+
+import org.eclipse.jface.viewers.ILabelProvider;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.dialogs.ElementListSelectionDialog;
+import org.eclipse.ui.model.WorkbenchLabelProvider;
+
+import com.motorola.studio.android.launch.LaunchPlugin;
+import com.motorola.studio.android.launch.LaunchUtils;
+import com.motorola.studio.android.launch.i18n.LaunchNLS;
+
+/**
+ * DESCRIPTION:
+ * Selection Dialog with only opened MOTOMAGX projects
+ * <br>
+ * RESPONSIBILITY:
+ * Provides a Element Selection Dialog to select a MOTOMAGX project
+ * <br>
+ * COLABORATORS:
+ * none
+ * <br>
+ * USAGE:
+ * This should be instanced when the user must choose one of a MOTOMAGX project list
+ */
+public class AndroidProjectsSelectionDialog extends ElementListSelectionDialog
+{
+
+ private static final String PRJ_SELECTION_CONTEXT_HELP_ID =
+ LaunchPlugin.PLUGIN_ID + ".projectSelectionDialog";
+
+ /**
+ * Create a new Project Selection Dialog
+ * @param parent the parent shell
+ * @param renderer the label provider
+ */
+ public AndroidProjectsSelectionDialog(Shell parent, ILabelProvider renderer)
+ {
+ super(parent, renderer);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.ui.dialogs.ElementListSelectionDialog#createDialogArea(org.eclipse.swt.widgets.Composite)
+ */
+ @Override
+ protected Control createDialogArea(Composite parent)
+ {
+ Control control = super.createDialogArea(parent);
+
+ setHelpAvailable(true);
+ PlatformUI.getWorkbench().getHelpSystem().setHelp(control, PRJ_SELECTION_CONTEXT_HELP_ID);
+ return control;
+ }
+
+ /**
+ * Creates a selection dialog with the workbench label provider
+ * @param parent The parent composite
+ */
+ public AndroidProjectsSelectionDialog(Shell parent)
+ {
+ super(parent, new WorkbenchLabelProvider());
+ }
+
+ /**
+ * Sets the default elements: the list of all opened Studio for Android projects
+ */
+ public void setDefaultElements()
+ {
+ this.setElements(LaunchUtils.getSupportedProjects());
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.ui.dialogs.AbstractElementListSelectionDialog#open()
+ */
+ @Override
+ public int open()
+ {
+ this.setTitle(LaunchNLS.UI_LaunchComposite_SelectProjectScreenTitle);
+ this.setMessage(LaunchNLS.UI_LaunchComposite_SelectProjectScreenMessage);
+
+ setDefaultElements();
+ return super.open();
+ }
+}
diff --git a/src/plugins/launch/src/com/motorola/studio/android/launch/ui/DeviceSelectionDialog.java b/src/plugins/launch/src/com/motorola/studio/android/launch/ui/DeviceSelectionDialog.java
new file mode 100644
index 0000000..1659966
--- /dev/null
+++ b/src/plugins/launch/src/com/motorola/studio/android/launch/ui/DeviceSelectionDialog.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.motorola.studio.android.launch.ui;
+
+import java.util.Collection;
+import java.util.Properties;
+
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.jface.viewers.LabelProvider;
+import org.eclipse.sequoyah.device.framework.model.IInstance;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.ui.ISharedImages;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.dialogs.ElementListSelectionDialog;
+
+import com.motorola.studio.android.adt.ISerialNumbered;
+import com.motorola.studio.android.devices.DevicesManager;
+import com.motorola.studio.android.emulator.core.model.IAndroidEmulatorInstance;
+import com.motorola.studio.android.launch.LaunchPlugin;
+import com.motorola.studio.android.launch.LaunchUtils;
+import com.motorola.studio.android.launch.i18n.LaunchNLS;
+
+/**
+ * DESCRIPTION:
+ * This class implements the device selection dialog
+ *
+ * RESPONSIBILITY:
+ * Provides a dialog populated with the available device instances
+ *
+ * COLABORATORS:
+ * None.
+ *
+ * USAGE:
+ * This class is intended to be used by Eclipse only
+ */
+public class DeviceSelectionDialog extends ElementListSelectionDialog
+{
+ private static final String DEV_SELECTION_CONTEXT_HELP_ID = LaunchPlugin.PLUGIN_ID
+ + ".deviceSelectionDialog";
+
+ /**
+ * Default constructor
+ *
+ * @param parent Parent shell
+ * @param description Dialog description
+ */
+ public DeviceSelectionDialog(Shell parent, String description, final IProject project)
+ {
+ super(parent, new LabelProvider()
+ {
+ @Override
+ public String getText(Object element)
+ {
+ String result = "";
+ if (element instanceof ISerialNumbered)
+ {
+ ISerialNumbered serialNumbered = (ISerialNumbered) element;
+ result = serialNumbered.getDeviceName();
+ if (serialNumbered instanceof IAndroidEmulatorInstance)
+ {
+ IAndroidEmulatorInstance emulatorInstance =
+ (IAndroidEmulatorInstance) serialNumbered;
+ int emulatorApi = emulatorInstance.getAPILevel();
+ String emulatorTarget = emulatorInstance.getTarget();
+ result += " (" + emulatorTarget + ", Api version " + emulatorApi + ")";
+ }
+ else if (serialNumbered instanceof IInstance)
+ {
+ IInstance instance = (IInstance) serialNumbered;
+ Properties properties = instance.getProperties();
+ if (properties != null)
+ {
+ String target = properties.getProperty("ro.build.version.release"); //$NON-NLS-1$
+ if (target != null)
+ {
+ result += " (Android " + target + ")";
+ }
+ }
+ }
+ }
+ return result;
+ }
+
+ @Override
+ public Image getImage(Object element)
+ {
+
+ Image img = null;
+
+ ISerialNumbered serialNumbered = (ISerialNumbered) element;
+ IStatus compatible = LaunchUtils.isCompatible(project, serialNumbered);
+
+ // notify the warning state
+ if (compatible.getSeverity() == IStatus.WARNING)
+ {
+ img =
+ PlatformUI.getWorkbench().getSharedImages()
+ .getImage(ISharedImages.IMG_OBJS_WARN_TSK);
+ }
+
+ return img;
+ }
+ });
+
+ this.setTitle(LaunchNLS.UI_LaunchComposite_SelectDeviceScreenTitle);
+ this.setMessage(description);
+
+ Collection<ISerialNumbered> instances = DevicesManager.getInstance().getAllDevicesSorted();
+ if ((project != null) && (instances != null) && (instances.size() > 0))
+ {
+ Collection<ISerialNumbered> filteredInstances =
+ LaunchUtils.filterInstancesByProject(instances, project);
+ Object[] filteredInstancesArray = filteredInstances.toArray();
+ this.setElements(filteredInstancesArray);
+ }
+
+ this.setHelpAvailable(true);
+ PlatformUI.getWorkbench().getHelpSystem().setHelp(parent, DEV_SELECTION_CONTEXT_HELP_ID);
+ }
+}
diff --git a/src/plugins/launch/src/com/motorola/studio/android/launch/ui/LaunchConfigurationTab.java b/src/plugins/launch/src/com/motorola/studio/android/launch/ui/LaunchConfigurationTab.java
new file mode 100644
index 0000000..1d68897
--- /dev/null
+++ b/src/plugins/launch/src/com/motorola/studio/android/launch/ui/LaunchConfigurationTab.java
@@ -0,0 +1,981 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.motorola.studio.android.launch.ui;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.ResourcesPlugin;
+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.debug.core.ILaunchConfiguration;
+import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
+import org.eclipse.debug.ui.AbstractLaunchConfigurationTab;
+import org.eclipse.debug.ui.ILaunchConfigurationTab;
+import org.eclipse.jface.dialogs.Dialog;
+import org.eclipse.jface.dialogs.IDialogConstants;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.jface.viewers.LabelProvider;
+import org.eclipse.sequoyah.device.framework.events.IInstanceListener;
+import org.eclipse.sequoyah.device.framework.events.InstanceEvent;
+import org.eclipse.sequoyah.device.framework.events.InstanceEventManager;
+import org.eclipse.sequoyah.device.framework.model.IInstance;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.ModifyEvent;
+import org.eclipse.swt.events.ModifyListener;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Group;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Link;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.dialogs.ElementListSelectionDialog;
+import org.eclipse.ui.dialogs.ISelectionStatusValidator;
+import org.eclipse.ui.plugin.AbstractUIPlugin;
+
+import com.android.ide.eclipse.adt.io.IFolderWrapper;
+import com.android.sdklib.xml.AndroidManifestParser;
+import com.android.sdklib.xml.ManifestData;
+import com.android.sdklib.xml.ManifestData.Activity;
+import com.motorola.studio.android.adt.ISerialNumbered;
+import com.motorola.studio.android.adt.SdkUtils;
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.emulator.device.handlers.OpenNewDeviceWizardHandler;
+import com.motorola.studio.android.emulator.device.refresh.InstancesListRefresh;
+import com.motorola.studio.android.launch.ILaunchConfigurationConstants;
+import com.motorola.studio.android.launch.LaunchPlugin;
+import com.motorola.studio.android.launch.LaunchUtils;
+import com.motorola.studio.android.launch.i18n.LaunchNLS;
+
+/**
+ * DESCRIPTION: This class implements the tab that is shown when the user is
+ * editing the configuration to run a MOTODEV Studio for Android application
+ *
+ * RESPONSIBILITY: User interface to allow the user to enter information to
+ * launch the application.
+ *
+ * COLABORATORS: This class is one of the tabs of the
+ * LaunchConfigurationTabGroup
+ *
+ * USAGE: This class should be created/used by the LaunchConfigurationTabGroup
+ * only.
+ */
+@SuppressWarnings("restriction")
+public class LaunchConfigurationTab extends AbstractLaunchConfigurationTab
+{
+ private static final String NAME = LaunchNLS.UI_LaunchConfigurationTab_Tab_Name;
+
+ private static final Object UPDATE_WIDGETS_EVENT = new Object();
+
+ private Composite mainComposite;
+
+ private String projectName = ""; //$NON-NLS-1$
+
+ private String activityName = ""; //$NON-NLS-1$
+
+ private String deviceName = ""; //$NON-NLS-1$
+
+ private boolean activitySpecified = false;
+
+ private boolean runDefaultActivity = true;
+
+ private final String LAUNCH_DIALOG_HELP = LaunchPlugin.PLUGIN_ID + ".mainLaunchTab"; //$NON-NLS-1$
+
+ private Button defaultLauncherButton = null;
+
+ private Button vdlLauncherButton = null;
+
+ private Button deviceNameBrowseButton = null;
+
+ private final IInstanceListener instanceListener = new IInstanceListener()
+ {
+
+ private void fireUpdate()
+ {
+ Display currentDisplay = PlatformUI.getWorkbench().getDisplay();
+ if (!currentDisplay.isDisposed())
+ {
+ currentDisplay.syncExec(new Runnable()
+ {
+
+ public void run()
+ {
+ updateDeviceChooserButton();
+ updateLaunchConfigurationDialog();
+ }
+ });
+ }
+ }
+
+ public void instanceUpdated(InstanceEvent instanceevent)
+ {
+ fireUpdate();
+ }
+
+ public void instanceUnloaded(InstanceEvent instanceevent)
+ {
+ fireUpdate();
+ }
+
+ public void instanceTransitioned(InstanceEvent instanceevent)
+ {
+ fireUpdate();
+ }
+
+ public void instanceLoaded(InstanceEvent instanceevent)
+ {
+ fireUpdate();
+ }
+
+ public void instanceDeleted(InstanceEvent instanceevent)
+ {
+ fireUpdate();
+ }
+
+ public void instanceCreated(InstanceEvent instanceevent)
+ {
+ fireUpdate();
+ }
+
+ public void instanceAboutToTransition(InstanceEvent instanceevent)
+ {
+ fireUpdate();
+ }
+ };
+
+ /**
+ * @see org.eclipse.debug.ui.ILaunchConfigurationTab#createControl(org.eclipse.swt.widgets.Composite)
+ */
+ public void createControl(Composite parent)
+ {
+ Composite main = new Composite(parent, SWT.NONE);
+
+ GridLayout layout = new GridLayout(1, false);
+ GridData gd = new GridData(SWT.FILL, SWT.FILL, true, true);
+ gd.widthHint = 430;
+ gd.heightHint = 130;
+ main.setLayout(layout);
+ main.setLayoutData(gd);
+
+ createMainInfoGroup(main);
+ setControl(main);
+ }
+
+ private void updateDeviceChooserButton()
+ {
+ // button is always enabled
+ if (!deviceNameBrowseButton.isDisposed())
+ {
+ deviceNameBrowseButton.setEnabled(true);
+ }
+ }
+
+ /**
+ * Create the main information selection group
+ * @param mainComposite: the parent composite
+ */
+ private void createMainInfoGroup(Composite mainComposite)
+ {
+ this.mainComposite = mainComposite;
+
+ // create destination group
+ Group destinationGroup = new Group(mainComposite, SWT.NONE);
+ GridLayout layout = new GridLayout(3, false);
+ destinationGroup.setLayout(layout);
+ GridData defaultDestGridData = new GridData(SWT.FILL, SWT.CENTER, true, false, 3, 1);
+ destinationGroup.setLayoutData(defaultDestGridData);
+ destinationGroup.setText(LaunchNLS.LaunchComposite_UI_LaunchComposite_DestinationGroupText);
+
+ // Project Name Label
+ Label projectNameLabel = new Label(destinationGroup, SWT.NONE);
+ projectNameLabel.setText(LaunchNLS.UI_LaunchComposite_ProjectNameLabel);
+ GridData folderGridData = new GridData(SWT.LEFT, SWT.CENTER, false, false);
+ projectNameLabel.setLayoutData(folderGridData);
+
+ // Project Name Text
+ final Text projectNameText = new Text(destinationGroup, SWT.SINGLE | SWT.BORDER);
+ projectNameText.setText(projectName);
+ folderGridData = new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1);
+ projectNameText.setLayoutData(folderGridData);
+ projectNameText.addModifyListener(new ModifyListener()
+ {
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.swt.events.ModifyListener#modifyText(org.eclipse.swt.events.ModifyEvent)
+ */
+ public void modifyText(ModifyEvent e)
+ {
+ if (e.data == UPDATE_WIDGETS_EVENT)
+ {
+ projectNameText.setText(projectName);
+ }
+ else
+ {
+ projectName = projectNameText.getText();
+ updateLaunchConfigurationDialog();
+ }
+ }
+ });
+
+ // Project Name Browse Button
+ Button projectNameBrowseButton = new Button(destinationGroup, SWT.PUSH);
+ folderGridData = new GridData(SWT.FILL, SWT.CENTER, false, false, 1, 1);
+ projectNameBrowseButton.setLayoutData(folderGridData);
+ projectNameBrowseButton.setText(LaunchNLS.UI_LaunchComposite_BrowseButton);
+ projectNameBrowseButton.addSelectionListener(new SelectionAdapter()
+ {
+ @Override
+ public void widgetSelected(SelectionEvent e)
+ {
+ AndroidProjectsSelectionDialog dialog =
+ new AndroidProjectsSelectionDialog(getShell());
+ int result = dialog.open();
+ if (result == Dialog.OK)
+ {
+ Object resultProject = dialog.getFirstResult();
+ if (resultProject instanceof IProject)
+ {
+ IProject project = (IProject) resultProject;
+ projectNameText.setText(project.getName());
+ }
+ }
+ }
+
+ });
+
+ Group activityGroup = new Group(mainComposite, SWT.NONE);
+ GridLayout activityLayout = new GridLayout(3, false);
+ activityGroup.setLayout(activityLayout);
+ GridData activityGrid = new GridData(SWT.FILL, SWT.CENTER, true, false, 3, 1);
+ activityGroup.setLayoutData(activityGrid);
+ activityGroup.setText(LaunchNLS.UI_LaunchComposite_ActivityGroupLabel);
+
+ final Button defaultActivityButton = new Button(activityGroup, SWT.RADIO);
+ defaultActivityButton.setText(LaunchNLS.UI_LaunchComposite_ActivityDefaultButton);
+ GridData gridData = new GridData(GridData.FILL_HORIZONTAL);
+ gridData.horizontalSpan = 3;
+ defaultActivityButton.setLayoutData(gridData);
+
+ // Activity Name Button
+ final Button specificActivityButton = new Button(activityGroup, SWT.RADIO);
+ specificActivityButton.setText(LaunchNLS.LaunchConfigurationTab_LaunchButton);
+ GridData activityData = new GridData(SWT.LEFT, SWT.CENTER, false, false);
+ specificActivityButton.setLayoutData(activityData);
+
+ // Activity Name Text
+ final Text activityNameText = new Text(activityGroup, SWT.SINGLE | SWT.BORDER);
+ activityNameText.setText(activityName);
+ activityData = new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1);
+ activityNameText.setLayoutData(activityData);
+ activityNameText.addModifyListener(new ModifyListener()
+ {
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.swt.events.ModifyListener#modifyText(org.eclipse.swt.events.ModifyEvent)
+ */
+ public void modifyText(ModifyEvent e)
+ {
+ if (e.data == UPDATE_WIDGETS_EVENT)
+ {
+ activityNameText.setText(activityName);
+ }
+ else
+ {
+ activityName = activityNameText.getText();
+ updateLaunchConfigurationDialog();
+ }
+ }
+ });
+
+ // Activity Name Browse Button
+ final Button activityNameBrowseButton = new Button(activityGroup, SWT.PUSH);
+ activityData = new GridData(SWT.FILL, SWT.CENTER, false, false, 1, 1);
+ activityNameBrowseButton.setLayoutData(activityData);
+ activityNameBrowseButton.setText(LaunchNLS.UI_LaunchComposite_BrowseButton);
+ activityNameBrowseButton.addSelectionListener(new SelectionAdapter()
+ {
+ /**
+ * Retrieve all activities of a given project
+ * @return All the activities of a given project
+ */
+ private Set<String> getAllActivities(String projectName)
+ {
+ String[] tempActivities = null;
+ Set<String> activities = new HashSet<String>();
+
+ if (projectName.length() != 0)
+ {
+ IProject selectedProject = LaunchUtils.getProject(projectName);
+
+ tempActivities = LaunchUtils.getProjectActivities(selectedProject);
+ for (String s : tempActivities)
+ {
+ activities.add(s);
+ }
+ }
+ return activities;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.swt.events.SelectionAdapter#widgetSelected(org.eclipse.swt.events.SelectionEvent)
+ */
+ @Override
+ public void widgetSelected(SelectionEvent e)
+ {
+ if (projectName.length() == 0)
+ {
+ IWorkbenchWindow ww = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
+ MessageDialog.openInformation(ww.getShell(),
+ LaunchNLS.UI_LaunchComposite_ProjectRequiredTitle,
+ LaunchNLS.UI_LaunchComposite_ProjectRequiredMessage);
+ }
+ else
+ {
+
+ ElementListSelectionDialog dialog =
+ new ElementListSelectionDialog(getShell(), new LabelProvider()
+ {
+ @Override
+ public String getText(Object element)
+ {
+ String activity = (String) element;
+ return activity;
+ }
+ })
+ {
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.ui.dialogs.ElementListSelectionDialog#createDialogArea(org.eclipse.swt.widgets.Composite)
+ */
+ @Override
+ protected Control createDialogArea(Composite parent)
+ {
+ PlatformUI.getWorkbench().getHelpSystem()
+ .setHelp(parent, ACTIVITY_SELECTION_DIALOG_HELPID);
+ return super.createDialogArea(parent);
+ }
+
+ };
+
+ dialog.setTitle(LaunchNLS.UI_LaunchComposite_SelectActivityScreenTitle);
+ dialog.setMessage(LaunchNLS.UI_LaunchComposite_SelectActivityScreenMessage);
+
+ Object[] allActivities = getAllActivities(projectNameText.getText()).toArray();
+ if (allActivities.length == 0)
+ {
+ activityNameText.setText(""); //$NON-NLS-1$
+ }
+ else
+ {
+ dialog.setElements(getAllActivities(projectNameText.getText()).toArray());
+
+ int buttonId = dialog.open();
+ if (buttonId == IDialogConstants.OK_ID)
+ {
+ String activity = (String) dialog.getFirstResult();
+ activityNameText.setText(activity);
+
+ }
+ }
+ }
+ }
+
+ protected static final String ACTIVITY_SELECTION_DIALOG_HELPID =
+ "com.motorola.studio.android.launch.activitySelectionDialog"; //$NON-NLS-1$
+ });
+
+ final Button noActivityButton = new Button(activityGroup, SWT.RADIO);
+ noActivityButton.setText(LaunchNLS.LaunchConfigurationTab_DoNothingButton);
+ gridData = new GridData(GridData.FILL_HORIZONTAL);
+ gridData.horizontalSpan = 3;
+ noActivityButton.setLayoutData(gridData);
+
+ defaultActivityButton.addSelectionListener(new SelectionAdapter()
+ {
+ @Override
+ public void widgetSelected(SelectionEvent e)
+ {
+ if (e.data == UPDATE_WIDGETS_EVENT)
+ {
+ defaultActivityButton.setSelection(!activitySpecified && runDefaultActivity);
+ activityNameText.setEnabled(activitySpecified);
+ activityNameBrowseButton.setEnabled(activitySpecified);
+ }
+ else
+ {
+ // handle variables
+ handleActivityLauncherTypeVariables(defaultActivityButton,
+ specificActivityButton, activityNameText, activityNameBrowseButton);
+ }
+ }
+ });
+
+ specificActivityButton.addSelectionListener(new SelectionAdapter()
+ {
+ @Override
+ public void widgetSelected(SelectionEvent e)
+ {
+ if (e.data == UPDATE_WIDGETS_EVENT)
+ {
+ specificActivityButton.setSelection(activitySpecified && !runDefaultActivity);
+ activityNameText.setEnabled(activitySpecified);
+ activityNameBrowseButton.setEnabled(activitySpecified);
+ }
+ else
+ {
+ // handle variables
+ handleActivityLauncherTypeVariables(defaultActivityButton,
+ specificActivityButton, activityNameText, activityNameBrowseButton);
+ }
+ }
+ });
+
+ noActivityButton.addSelectionListener(new SelectionAdapter()
+ {
+ @Override
+ public void widgetSelected(SelectionEvent e)
+ {
+ if (e.data == UPDATE_WIDGETS_EVENT)
+ {
+ noActivityButton.setSelection(!activitySpecified && !runDefaultActivity);
+ activityNameText.setEnabled(activitySpecified);
+ activityNameBrowseButton.setEnabled(activitySpecified);
+ }
+ else
+ {
+ // handle variables
+ handleActivityLauncherTypeVariables(defaultActivityButton,
+ specificActivityButton, activityNameText, activityNameBrowseButton);
+ }
+ }
+ });
+
+ // Device Name Label
+ Label deviceNameLabel = new Label(destinationGroup, SWT.NONE);
+ deviceNameLabel.setText(LaunchNLS.UI_LaunchComposite_DeviceNameLabel);
+ GridData deviceGridData = new GridData(SWT.LEFT, SWT.CENTER, false, false);
+ deviceNameLabel.setLayoutData(deviceGridData);
+
+ // Device Name Text
+ final Text deviceNameText = new Text(destinationGroup, SWT.SINGLE | SWT.BORDER);
+ deviceNameText.setText(deviceName);
+ deviceGridData = new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1);
+ deviceNameText.setLayoutData(deviceGridData);
+ deviceNameText.addModifyListener(new ModifyListener()
+ {
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.swt.events.ModifyListener#modifyText(org.eclipse.swt.events.ModifyEvent)
+ */
+ public void modifyText(ModifyEvent e)
+ {
+ if (e.data == UPDATE_WIDGETS_EVENT)
+ {
+ deviceNameText.setText(deviceName);
+ }
+ else
+ {
+ deviceName = deviceNameText.getText();
+ updateLaunchConfigurationDialog();
+ }
+ }
+ });
+
+ // Device Name Browse Button
+ deviceNameBrowseButton = new Button(destinationGroup, SWT.PUSH);
+ deviceGridData = new GridData(SWT.FILL, SWT.CENTER, false, false, 1, 1);
+ deviceNameBrowseButton.setLayoutData(deviceGridData);
+ deviceNameBrowseButton.setText(LaunchNLS.UI_LaunchComposite_BrowseButton);
+ deviceNameBrowseButton.addSelectionListener(new SelectionAdapter()
+ {
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.swt.events.SelectionAdapter#widgetSelected(org.eclipse.swt.events.SelectionEvent)
+ */
+ @Override
+ public void widgetSelected(SelectionEvent e)
+ {
+ IProject selectedProject = LaunchUtils.getProject(projectNameText.getText());
+ DeviceSelectionDialog dialog =
+ new DeviceSelectionDialog(getShell(),
+ LaunchNLS.UI_LaunchComposite_SelectDeviceScreenMessage,
+ selectedProject);
+ dialog.setTitle(LaunchNLS.UI_LaunchComposite_SelectDeviceScreenTitle);
+ dialog.setMultipleSelection(false);
+ dialog.setValidator(new ISelectionStatusValidator()
+ {
+
+ public IStatus validate(Object[] selection)
+ {
+ IStatus status = new Status(IStatus.OK, LaunchPlugin.PLUGIN_ID, ""); //$NON-NLS-1$
+ if (selection.length == 0)
+ {
+ status =
+ new Status(IStatus.ERROR, LaunchPlugin.PLUGIN_ID,
+ "No selected instance"); //$NON-NLS-1$
+ }
+ return status;
+ }
+ });
+ int res = dialog.open();
+ if (res == IDialogConstants.OK_ID)
+ {
+ ISerialNumbered serialNumbered = (ISerialNumbered) dialog.getFirstResult();
+ String selectedDevice = ((IInstance) serialNumbered).getName();
+ deviceNameText.setText(selectedDevice);
+ }
+ }
+
+ });
+
+ InstanceEventManager.getInstance().addInstanceListener(instanceListener);
+
+ Link createNewAvdLink = new Link(destinationGroup, SWT.NONE);
+ deviceGridData = new GridData(SWT.RIGHT, SWT.CENTER, true, false, 3, 1);
+ createNewAvdLink.setLayoutData(deviceGridData);
+ createNewAvdLink.setText(LaunchNLS.LaunchConfigurationTab_CreateNewAVDLink);
+ createNewAvdLink.addSelectionListener(new SelectionAdapter()
+ {
+ @Override
+ public void widgetSelected(SelectionEvent e)
+ {
+ OpenNewDeviceWizardHandler handler = new OpenNewDeviceWizardHandler();
+ try
+ {
+ handler.execute(new ExecutionEvent());
+ }
+ catch (ExecutionException exception)
+ {
+ //do nothing
+ }
+ }
+ });
+
+ mainComposite.addListener(SWT.Modify, new Listener()
+ {
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.swt.widgets.Listener#handleEvent(org.eclipse.swt.widgets.Event)
+ */
+ public void handleEvent(Event e)
+ {
+ projectNameText.notifyListeners(SWT.Modify, e);
+ activityNameText.notifyListeners(SWT.Modify, e);
+ deviceNameText.notifyListeners(SWT.Modify, e);
+ defaultActivityButton.notifyListeners(SWT.Selection, e);
+ specificActivityButton.notifyListeners(SWT.Selection, e);
+ noActivityButton.notifyListeners(SWT.Selection, e);
+
+ if (defaultLauncherButton != null)
+ {
+ defaultLauncherButton.notifyListeners(SWT.Selection, e);
+ }
+ if (vdlLauncherButton != null)
+ {
+ vdlLauncherButton.notifyListeners(SWT.Selection, e);
+ }
+ }
+ });
+
+ PlatformUI.getWorkbench().getHelpSystem().setHelp(mainComposite, LAUNCH_DIALOG_HELP); //$NON-NLS-1$
+ }
+
+ /**
+ * Handle the variables regarding Activity Launcher options.
+ *
+ * @param defaultActivityButton {@link Button} for Default Activity.
+ * @param specificActivityButton {@link Button} for Specific Activity.
+ * @param activityNameText {@link Text} holding the Activity to be launched name.
+ * @param activityNameBrowseButton Activity browser {@link Button}.
+ */
+ private void handleActivityLauncherTypeVariables(final Button defaultActivityButton,
+ final Button specificActivityButton, final Text activityNameText,
+ final Button activityNameBrowseButton)
+ {
+ activitySpecified = specificActivityButton.getSelection();
+ runDefaultActivity = defaultActivityButton.getSelection();
+ activityNameText.setEnabled(activitySpecified);
+ activityNameBrowseButton.setEnabled(activitySpecified);
+ updateLaunchConfigurationDialog();
+ }
+
+ /**
+ * @see org.eclipse.debug.ui.ILaunchConfigurationTab#getName()
+ */
+ public String getName()
+ {
+ return LaunchConfigurationTab.NAME;
+ }
+
+ /**
+ * @see org.eclipse.debug.ui.ILaunchConfigurationTab#getImage()
+ */
+ @Override
+ public Image getImage()
+ {
+ return AbstractUIPlugin.imageDescriptorFromPlugin(LaunchPlugin.PLUGIN_ID,
+ ILaunchConfigurationConstants.MOTODEV_APP_ICO).createImage();
+ }
+
+ @Override
+ public void dispose()
+ {
+ InstanceEventManager.getInstance().removeInstanceListener(instanceListener);
+ super.dispose();
+ }
+
+ /**
+ * @see org.eclipse.debug.ui.ILaunchConfigurationTab#initializeFrom(org.eclipse.debug.core.ILaunchConfiguration)
+ */
+ public void initializeFrom(ILaunchConfiguration configuration)
+ {
+ // Assure that when loading the configuration, the TmL devices are in sync with the
+ // AVD available at the SDK
+ InstancesListRefresh.refresh();
+
+ try
+ {
+ projectName =
+ configuration.getAttribute(ILaunchConfigurationConstants.ATTR_PROJECT_NAME,
+ ILaunchConfigurationConstants.DEFAULT_VALUE);
+
+ activityName =
+ configuration.getAttribute(ILaunchConfigurationConstants.ATTR_ACTIVITY,
+ ILaunchConfigurationConstants.DEFAULT_VALUE);
+
+ activitySpecified =
+ (configuration.getAttribute(ILaunchConfigurationConstants.ATTR_LAUNCH_ACTION,
+ ILaunchConfigurationConstants.ATTR_LAUNCH_ACTION_ACTIVITY)) == ILaunchConfigurationConstants.ATTR_LAUNCH_ACTION_ACTIVITY;
+
+ runDefaultActivity =
+ (configuration.getAttribute(ILaunchConfigurationConstants.ATTR_LAUNCH_ACTION,
+ ILaunchConfigurationConstants.ATTR_LAUNCH_ACTION_ACTIVITY)) == ILaunchConfigurationConstants.ATTR_LAUNCH_ACTION_DEFAULT;
+
+ deviceName =
+ configuration.getAttribute(
+ ILaunchConfigurationConstants.ATTR_DEVICE_INSTANCE_NAME,
+ ILaunchConfigurationConstants.DEFAULT_VALUE);
+
+ Event e = new Event();
+ e.type = SWT.Modify;
+ e.data = UPDATE_WIDGETS_EVENT;
+ mainComposite.notifyListeners(SWT.Modify, e);
+ }
+ catch (CoreException e)
+ {
+ // Do nothing for now
+ }
+ }
+
+ /**
+ * @see org.eclipse.debug.ui.ILaunchConfigurationTab#performApply(org.eclipse.debug.core.ILaunchConfigurationWorkingCopy)
+ */
+ public void performApply(ILaunchConfigurationWorkingCopy configuration)
+ {
+ configuration.setAttribute(ILaunchConfigurationConstants.ATTR_PROJECT_NAME, projectName);
+ configuration.setAttribute(ILaunchConfigurationConstants.ATTR_ACTIVITY, activityName);
+
+ // For now we are not preventing the device chooser dialog to appear if the user choose a
+ // handset in the device field. However, if the user chooses an AVD, we set the preferred
+ // AVD field so that we force the launch to happen in the selected AVD without asking the
+ // user.
+ Collection<String> validAvds = SdkUtils.getAllValidVmNames();
+ if (validAvds.contains(deviceName))
+ {
+ configuration.setAttribute(ILaunchConfigurationConstants.ATTR_DEVICE_INSTANCE_NAME,
+ deviceName);
+ configuration.setAttribute(ILaunchConfigurationConstants.ATTR_ADT_DEVICE_INSTANCE_NAME,
+ deviceName);
+ }
+ else
+ {
+ configuration.setAttribute(ILaunchConfigurationConstants.ATTR_DEVICE_INSTANCE_NAME,
+ deviceName);
+ configuration
+ .removeAttribute(ILaunchConfigurationConstants.ATTR_ADT_DEVICE_INSTANCE_NAME);
+ }
+
+ if (activitySpecified)
+ {
+ configuration.setAttribute(ILaunchConfigurationConstants.ATTR_LAUNCH_ACTION,
+ ILaunchConfigurationConstants.ATTR_LAUNCH_ACTION_ACTIVITY);
+ }
+ else if (runDefaultActivity)
+ {
+ configuration.setAttribute(ILaunchConfigurationConstants.ATTR_LAUNCH_ACTION,
+ ILaunchConfigurationConstants.ATTR_LAUNCH_ACTION_DEFAULT);
+ }
+ else
+ {
+ configuration.setAttribute(ILaunchConfigurationConstants.ATTR_LAUNCH_ACTION,
+ ILaunchConfigurationConstants.ATTR_LAUNCH_ACTION_DO_NOTHING);
+ }
+
+ LaunchUtils.updateLaunchConfigurationDefaults(configuration);
+
+ IProject project = LaunchUtils.getProject(projectName);
+ IResource[] mappedResources = null;
+ if (project != null)
+ {
+ mappedResources = new IResource[]
+ {
+ project
+ };
+ }
+
+ configuration.setMappedResources(mappedResources);
+ }
+
+ /**
+ * @see org.eclipse.debug.ui.ILaunchConfigurationTab#setDefaults(org.eclipse.debug.core.ILaunchConfigurationWorkingCopy)
+ */
+ public void setDefaults(ILaunchConfigurationWorkingCopy configuration)
+ {
+ configuration.setAttribute(ILaunchConfigurationConstants.ATTR_PROJECT_NAME,
+ ILaunchConfigurationConstants.DEFAULT_VALUE);
+ configuration.setAttribute(ILaunchConfigurationConstants.ATTR_ACTIVITY,
+ ILaunchConfigurationConstants.DEFAULT_VALUE);
+ configuration.setAttribute(ILaunchConfigurationConstants.ATTR_LAUNCH_ACTION,
+ ILaunchConfigurationConstants.ATTR_LAUNCH_ACTION_DEFAULT);
+ // It is default not to exist Preferred AVD attribute, so we just set the Studio's
+ // device instance name attribute here
+ configuration.setAttribute(ILaunchConfigurationConstants.ATTR_DEVICE_INSTANCE_NAME,
+ ILaunchConfigurationConstants.DEFAULT_VALUE);
+
+ LaunchUtils.setADTLaunchConfigurationDefaults(configuration);
+
+ projectName = ILaunchConfigurationConstants.DEFAULT_VALUE; //$NON-NLS-1$
+ activityName = ILaunchConfigurationConstants.DEFAULT_VALUE; //$NON-NLS-1$
+ deviceName = ILaunchConfigurationConstants.DEFAULT_VALUE; //$NON-NLS-1$
+ activitySpecified = ILaunchConfigurationConstants.DEFAULT_BOOL_VALUE;
+ runDefaultActivity = !ILaunchConfigurationConstants.DEFAULT_BOOL_VALUE;
+
+ if (mainComposite != null)
+ {
+ Event e = new Event();
+ e.type = SWT.Modify;
+ e.data = UPDATE_WIDGETS_EVENT;
+ mainComposite.notifyListeners(SWT.Modify, e);
+ }
+ }
+
+ /**
+ * @see ILaunchConfigurationTab#isValid(ILaunchConfiguration)
+ */
+ @Override
+ public boolean isValid(ILaunchConfiguration launchConfig)
+ {
+ boolean isValid = true;
+ boolean hasWarning = false;
+
+ String projectName = ""; //$NON-NLS-1$
+ String instanceName = ""; //$NON-NLS-1$
+ String activityName = ""; //$NON-NLS-1$
+
+ try
+ {
+ projectName =
+ launchConfig.getAttribute(ILaunchConfigurationConstants.ATTR_PROJECT_NAME,
+ ILaunchConfigurationConstants.DEFAULT_VALUE);
+ instanceName =
+ launchConfig.getAttribute(
+ ILaunchConfigurationConstants.ATTR_DEVICE_INSTANCE_NAME, (String) null);
+ activityName =
+ launchConfig.getAttribute(ILaunchConfigurationConstants.ATTR_ACTIVITY,
+ ILaunchConfigurationConstants.DEFAULT_VALUE);
+ }
+ catch (CoreException e)
+ {
+ StudioLogger.error(LaunchConfigurationTab.class,
+ "Error validating launch configuration " + launchConfig.getName(), e); //$NON-NLS-1$
+ }
+
+ /* Validate current project */
+
+ IProject project = null;
+
+ if (isValid && (projectName.length() > 0))
+ {
+ Path projectPath = new Path(projectName);
+ if (!projectPath.isValidSegment(projectName))
+ {
+ isValid = false;
+ setErrorMessage(LaunchNLS.UI_LaunchConfigurationTab_ERR_PROJECT_NOT_EXIST);
+ }
+ project = ResourcesPlugin.getWorkspace().getRoot().getProject(projectName);
+ if ((project != null) && !project.exists())
+ {
+ isValid = false;
+ setErrorMessage(LaunchNLS.UI_LaunchConfigurationTab_ERR_PROJECT_NOT_EXIST);
+ }
+ else if ((project != null) && SdkUtils.isLibraryProject(project))
+ {
+ isValid = false;
+ setErrorMessage(LaunchNLS.UI_LaunchConfigurationTab_ERR_PROJECT_IS_LIBRARY);
+ }
+ else if (project == null)
+ {
+ isValid = false;
+ setErrorMessage(LaunchNLS.UI_LaunchConfigurationTab_ERR_PROJECT_NOT_EXIST);
+ }
+ }
+ else if (isValid && (projectName.length() == 0))
+ {
+ setErrorMessage(null);
+ }
+
+ // if we have a chosen project, enable/disable the device selection
+ if (project != null)
+ {
+ updateDeviceChooserButton();
+ }
+
+ /* Validate current device instance */
+ if (isValid && (instanceName != null) && (instanceName.length() > 0))
+ {
+ IStatus compatible = LaunchUtils.isCompatible(project, instanceName);
+ if (compatible == null)
+ {
+ setErrorMessage(LaunchNLS.UI_LaunchConfigurationTab_ERR_DEVICE_INEXISTENT);
+ isValid = false;
+ }
+ else if (compatible.getSeverity() == IStatus.ERROR)
+ {
+ setErrorMessage(compatible.getMessage());
+ isValid = false;
+ }
+ else if (compatible.getSeverity() == IStatus.WARNING)
+ {
+ setMessage(compatible.getMessage());
+ hasWarning = true;
+ }
+ }
+ else if (isValid && (instanceName != null) && (instanceName.length() == 0))
+ {
+ setErrorMessage(null);
+ }
+
+ /* Validate current activity */
+ if (isValid && (activityName.length() > 0) && activitySpecified)
+ {
+ /*
+ * Check if the activity is valid in the current METAINF project
+ * file
+ */
+
+ Activity[] currentActivities = null;
+ boolean activityValid = false;
+
+ ManifestData manifestParser = null;
+ try
+ {
+ manifestParser = AndroidManifestParser.parse(new IFolderWrapper(project));
+ }
+ catch (Exception e)
+ {
+ StudioLogger.error(LaunchUtils.class,
+ "An error occurred trying to parse AndroidManifest", e); //$NON-NLS-1$
+ }
+ if (manifestParser != null)
+ {
+ currentActivities = manifestParser.getActivities();
+ }
+ else
+ {
+ // There's a problem with the manifest file / parser. Invalidate
+ // current settings.
+ isValid = false;
+ setErrorMessage(LaunchNLS.UI_LaunchConfigurationTab_ERR_INVALID_ACTIVITY);
+ }
+
+ /* See if the chosen activity is there */
+
+ for (Activity s : currentActivities)
+ {
+ if (s.getName().equals(activityName))
+ {
+ activityValid = true;
+ }
+ }
+
+ if (!activityValid)
+ {
+ isValid = false;
+ setErrorMessage(LaunchNLS.UI_LaunchConfigurationTab_ERR_ACTIVITY_NOT_EXIST);
+ }
+
+ }
+ else if (isValid && ((activityName.length() == 0) && activitySpecified))
+ {
+ setErrorMessage(null);
+ }
+
+ /* Wrap up validation */
+ if (isValid
+ && ((projectName.length() == 0)
+ || ((activitySpecified) && (activityName.length() == 0)) || (instanceName
+ .length() == 0)))
+ {
+ isValid = false;
+
+ if (projectName.length() == 0)
+ {
+ setMessage(LaunchNLS.UI_LaunchConfigurationTab_InfoSelectProject);
+ }
+ else if (instanceName.length() == 0)
+ {
+ setMessage(LaunchNLS.UI_LaunchConfigurationTab_InfoSelectInstance);
+ }
+ else if (activityName.length() == 0)
+ {
+ setMessage(LaunchNLS.UI_LaunchConfigurationTab_InfoSelectActivity);
+ }
+
+ }
+
+ if (isValid)
+ {
+ setErrorMessage(null);
+ if (!hasWarning)
+ {
+ setMessage(null);
+ }
+ }
+
+ return isValid;
+ }
+}
diff --git a/src/plugins/launch/src/com/motorola/studio/android/launch/ui/LaunchConfigurationTabGroup.java b/src/plugins/launch/src/com/motorola/studio/android/launch/ui/LaunchConfigurationTabGroup.java
new file mode 100644
index 0000000..4799e0e
--- /dev/null
+++ b/src/plugins/launch/src/com/motorola/studio/android/launch/ui/LaunchConfigurationTabGroup.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.motorola.studio.android.launch.ui;
+
+import org.eclipse.debug.ui.AbstractLaunchConfigurationTabGroup;
+import org.eclipse.debug.ui.CommonTab;
+import org.eclipse.debug.ui.ILaunchConfigurationDialog;
+import org.eclipse.debug.ui.ILaunchConfigurationTab;
+
+/**
+ * DESCRIPTION:
+ * This class builds the configuration tabs that are displayed when the user is
+ * editing the configuration to run MOTODEV Studio for Android applications.
+ *
+ * RESPONSIBILITY:
+ * Build the configuration tab of the "Run As" features.
+ *
+ * COLABORATORS:
+ * << class relationship>
+ *
+ * USAGE:
+ * Used only by the extension definition.
+ */
+public class LaunchConfigurationTabGroup extends AbstractLaunchConfigurationTabGroup
+{
+ /**
+ * Creates the tabs
+ *
+ * @param dialog dialog
+ * @param mode the launch mode
+ */
+ public void createTabs(ILaunchConfigurationDialog dialog, String mode)
+ {
+ ILaunchConfigurationTab mainLaunchTab = new LaunchConfigurationTab();
+ setTabs(new ILaunchConfigurationTab[]
+ {
+ mainLaunchTab, new CommonTab()
+ });
+ }
+
+}
diff --git a/src/plugins/launch/src/com/motorola/studio/android/launch/ui/StartedInstancesDialog.java b/src/plugins/launch/src/com/motorola/studio/android/launch/ui/StartedInstancesDialog.java
new file mode 100644
index 0000000..f7c03dd
--- /dev/null
+++ b/src/plugins/launch/src/com/motorola/studio/android/launch/ui/StartedInstancesDialog.java
@@ -0,0 +1,386 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.motorola.studio.android.launch.ui;
+
+import java.util.Collection;
+
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.debug.core.ILaunchConfiguration;
+import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
+import org.eclipse.jface.dialogs.IDialogConstants;
+import org.eclipse.jface.dialogs.TitleAreaDialog;
+import org.eclipse.jface.viewers.ArrayContentProvider;
+import org.eclipse.jface.viewers.ColumnLabelProvider;
+import org.eclipse.jface.viewers.ColumnViewerToolTipSupport;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.jface.viewers.TableViewerColumn;
+import org.eclipse.jface.window.ToolTip;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.MouseEvent;
+import org.eclipse.swt.events.MouseListener;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.ui.ISharedImages;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.plugin.AbstractUIPlugin;
+
+import com.motorola.studio.android.AndroidPlugin;
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.emulator.core.devfrm.DeviceFrameworkManager;
+import com.motorola.studio.android.emulator.core.model.IAndroidEmulatorInstance;
+import com.motorola.studio.android.launch.ILaunchConfigurationConstants;
+import com.motorola.studio.android.launch.LaunchPlugin;
+import com.motorola.studio.android.launch.LaunchUtils;
+import com.motorola.studio.android.launch.i18n.LaunchNLS;
+
+/**
+ * This class shows a dialog to the user with online AVDs compatibles with prefferedAvd.
+ * The user can choose an online AVD and click Ok, ignore the dialog or abort the ongoing action.
+ */
+public class StartedInstancesDialog extends TitleAreaDialog
+{
+
+ Collection<IAndroidEmulatorInstance> compatibleStartedInstances = null;
+
+ private TableViewer viewer;
+
+ private IAndroidEmulatorInstance selectedInstance;
+
+ private Button okButton;
+
+ private Button ignoreButton;
+
+ private Button abortButton;
+
+ private final IAndroidEmulatorInstance preferredAvd;
+
+ private IProject project = null;
+
+ private ILaunchConfiguration configuration = null;
+
+ private boolean isUpdateConfigurationSelected = false;
+
+ private static String DIALOG_IMAGE = "icons/choose_compatible_avd_instance.png";
+
+ private static final String STARTED_INSTANCES_HELP_ID = AndroidPlugin.PLUGIN_ID
+ + ".started_instances_selection_dialog";
+
+ /**
+ * @param parentShell
+ * @throws CoreException
+ */
+ public StartedInstancesDialog(Shell parentShell,
+ Collection<IAndroidEmulatorInstance> compatibleStartedInstances,
+ ILaunchConfiguration configuration, IProject project) throws CoreException
+ {
+ super(parentShell);
+
+ this.configuration = configuration;
+ this.compatibleStartedInstances = compatibleStartedInstances;
+ this.project = project;
+
+ final String instanceName =
+ configuration.getAttribute(ILaunchConfigurationConstants.ATTR_DEVICE_INSTANCE_NAME,
+ (String) null);
+
+ this.preferredAvd = DeviceFrameworkManager.getInstance().getInstanceByName(instanceName);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.dialogs.TitleAreaDialog#createContents(org.eclipse.swt.widgets.Composite)
+ */
+ @Override
+ protected Control createContents(Composite parent)
+ {
+ Control control = super.createContents(parent);
+
+ getShell().setText(LaunchNLS.UI_StartedInstancesDialog_WindowTitle);
+
+ setTitle(NLS.bind(LaunchNLS.UI_StartedInstancesDialog_Title, preferredAvd.getName()));
+ setMessage(LaunchNLS.UI_StartedInstancesDialog_Message);
+ setTitleImage(AbstractUIPlugin.imageDescriptorFromPlugin(LaunchPlugin.PLUGIN_ID,
+ DIALOG_IMAGE).createImage());
+
+ enableOkButton();
+
+ PlatformUI.getWorkbench().getHelpSystem().setHelp(parent, STARTED_INSTANCES_HELP_ID);
+ PlatformUI.getWorkbench().getHelpSystem().setHelp(control, STARTED_INSTANCES_HELP_ID);
+
+ return control;
+
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.dialogs.Dialog#createButtonsForButtonBar(org.eclipse.swt.widgets.Composite)
+ */
+ @Override
+ protected void createButtonsForButtonBar(Composite parent)
+ {
+ // create OK, Ignore and Abort buttons
+ okButton = createButton(parent, IDialogConstants.OK_ID, IDialogConstants.OK_LABEL, true);
+ ignoreButton =
+ createButton(parent, IDialogConstants.IGNORE_ID, IDialogConstants.IGNORE_LABEL,
+ false);
+ abortButton =
+ createButton(parent, IDialogConstants.ABORT_ID, IDialogConstants.ABORT_LABEL, false);
+
+ ignoreButton.addMouseListener(new MouseListener()
+ {
+
+ public void mouseUp(MouseEvent e)
+ {
+ setReturnCode(IDialogConstants.IGNORE_ID);
+ close();
+ }
+
+ public void mouseDown(MouseEvent e)
+ {
+ //do nothing
+ }
+
+ public void mouseDoubleClick(MouseEvent e)
+ {
+ //do nothing
+ }
+ });
+
+ abortButton.addMouseListener(new MouseListener()
+ {
+
+ public void mouseUp(MouseEvent e)
+ {
+ setReturnCode(IDialogConstants.ABORT_ID);
+ close();
+ }
+
+ public void mouseDown(MouseEvent e)
+ {
+ //do nothing
+ }
+
+ public void mouseDoubleClick(MouseEvent e)
+ {
+ //do nothing
+ }
+ });
+ }
+
+ /**
+ * Handles the enablement of the OK button.
+ */
+ private void enableOkButton()
+ {
+ if (viewer.getTable().getSelectionCount() > 0)
+ {
+ okButton.setEnabled(true);
+ }
+ else
+ {
+ okButton.setEnabled(false);
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.dialogs.TitleAreaDialog#createDialogArea(org.eclipse.swt.widgets.Composite)
+ */
+ @Override
+ protected Control createDialogArea(Composite parent)
+ {
+ GridLayout layout = new GridLayout(1, false);
+ parent.setLayout(layout);
+
+ createTableArea(parent);
+
+ return parent;
+ }
+
+ /**
+ * @param parent
+ */
+ private void createTableArea(Composite parent)
+ {
+ viewer =
+ new TableViewer(parent, SWT.SINGLE | SWT.H_SCROLL | SWT.V_SCROLL
+ | SWT.FULL_SELECTION | SWT.BORDER);
+ viewer.getControl().setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1));
+ viewer.getTable().setHeaderVisible(true);
+ viewer.getTable().setLinesVisible(false);
+
+ TableViewerColumn avds = new TableViewerColumn(viewer, SWT.NONE);
+ avds.getColumn().setText(LaunchNLS.UI_StartedInstancesDialog_CompatibleAvdsColumnName);
+ avds.getColumn().setResizable(true);
+ avds.getColumn().setWidth(480);
+
+ viewer.addSelectionChangedListener(new ISelectionChangedListener()
+ {
+
+ public void selectionChanged(SelectionChangedEvent event)
+ {
+ enableOkButton();
+ }
+ });
+
+ avds.setLabelProvider(new ColumnLabelProvider()
+ {
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.viewers.ColumnLabelProvider#getText(java.lang.Object)
+ */
+ @Override
+ public String getText(Object element)
+ {
+ IAndroidEmulatorInstance instance = (IAndroidEmulatorInstance) element;
+ return instance.getName() + " (" + instance.getTarget() + ", "
+ + LaunchNLS.UI_StartedInstancesDialog_ApiLevel + " "
+ + instance.getAPILevel() + ")";
+ }
+
+ @Override
+ public Image getImage(Object element)
+ {
+
+ Image img = null;
+
+ IAndroidEmulatorInstance instance = (IAndroidEmulatorInstance) element;
+ IStatus compatible = LaunchUtils.isCompatible(project, instance.getName());
+
+ // notify the warning state
+ if (compatible.getSeverity() == IStatus.WARNING)
+ {
+ img =
+ PlatformUI.getWorkbench().getSharedImages()
+ .getImage(ISharedImages.IMG_OBJS_WARN_TSK);
+ }
+
+ return img;
+ }
+
+ @Override
+ public String getToolTipText(Object element)
+ {
+ String toolTip = null;
+
+ IAndroidEmulatorInstance instance = (IAndroidEmulatorInstance) element;
+ IStatus compatible = LaunchUtils.isCompatible(project, instance.getName());
+
+ if (compatible.getSeverity() == IStatus.WARNING)
+ {
+ toolTip = LaunchNLS.UI_StartedInstancesDialog_Tooltip;
+
+ }
+
+ return toolTip;
+ }
+
+ @Override
+ public int getToolTipDisplayDelayTime(Object object)
+ {
+ return 500;
+ }
+
+ @Override
+ public int getToolTipTimeDisplayed(Object object)
+ {
+ return 5000;
+ }
+
+ });
+
+ ColumnViewerToolTipSupport.enableFor(viewer, ToolTip.NO_RECREATE);
+
+ ArrayContentProvider provider = new ArrayContentProvider();
+ viewer.setContentProvider(provider);
+ viewer.setInput(compatibleStartedInstances);
+
+ Button checkBox = new Button(parent, SWT.CHECK);
+ GridData gridData = new GridData(SWT.LEFT, SWT.CENTER, false, false);
+ checkBox.setLayoutData(gridData);
+ checkBox.setText(LaunchNLS.UI_StartedInstancesDialog_UpdateRunConfigurarion);
+ checkBox.addSelectionListener(new SelectionAdapter()
+ {
+
+ @Override
+ public void widgetSelected(SelectionEvent e)
+ {
+ if (isUpdateConfigurationSelected)
+ {
+ isUpdateConfigurationSelected = false;
+ }
+ else
+ {
+ isUpdateConfigurationSelected = true;
+ }
+ }
+ });
+ }
+
+ public IAndroidEmulatorInstance getSelectedInstance()
+ {
+ return selectedInstance;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.dialogs.Dialog#okPressed()
+ */
+ @Override
+ protected void okPressed()
+ {
+ selectedInstance = null;
+
+ if (viewer.getTable().getSelectionCount() > 0)
+ {
+ selectedInstance =
+ (IAndroidEmulatorInstance) viewer.getTable().getSelection()[0].getData();
+ }
+
+ if (isUpdateConfigurationSelected)
+ {
+ try
+ {
+ updateRunConfiguration();
+ }
+ catch (CoreException e)
+ {
+ StudioLogger.error(StartedInstancesDialog.class,
+ "It was not possible to update the current run configuration");
+ }
+ }
+
+ super.okPressed();
+ }
+
+ private void updateRunConfiguration() throws CoreException
+ {
+ ILaunchConfigurationWorkingCopy workingCopy = configuration.getWorkingCopy();
+ workingCopy.setAttribute(ILaunchConfigurationConstants.ATTR_DEVICE_INSTANCE_NAME,
+ selectedInstance.getName());
+ workingCopy.setAttribute(ILaunchConfigurationConstants.ATTR_ADT_DEVICE_INSTANCE_NAME,
+ selectedInstance.getName());
+ workingCopy.doSave();
+ }
+}
diff --git a/src/plugins/logger.collector/.classpath b/src/plugins/logger.collector/.classpath
new file mode 100644
index 0000000..8a8f166
--- /dev/null
+++ b/src/plugins/logger.collector/.classpath
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>
+ <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/src/plugins/logger.collector/.project b/src/plugins/logger.collector/.project
new file mode 100644
index 0000000..42cd0d2
--- /dev/null
+++ b/src/plugins/logger.collector/.project
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>com.motorolamobility.studio.android.logger.collector</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.ManifestBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.SchemaBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.pde.PluginNature</nature>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+</projectDescription>
diff --git a/src/plugins/logger.collector/META-INF/MANIFEST.MF b/src/plugins/logger.collector/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..8c22952
--- /dev/null
+++ b/src/plugins/logger.collector/META-INF/MANIFEST.MF
@@ -0,0 +1,14 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: MOTODEV Studio Android Log Collector
+Bundle-SymbolicName: com.motorolamobility.studio.android.logger.collector;singleton:=true
+Bundle-Version: 5.0.0.qualifier
+Require-Bundle: org.eclipse.ui,
+ org.eclipse.core.runtime
+Bundle-ActivationPolicy: lazy
+Bundle-Vendor: Motorola Mobility, Inc.
+Bundle-Localization: plugin
+Export-Package: com.motorola.studio.android.logger.collector.core
+Bundle-RequiredExecutionEnvironment: JavaSE-1.6
+Import-Package: com.motorola.studio.android.logger,
+ com.motorola.studio.android.common.utilities
diff --git a/src/plugins/logger.collector/build.properties b/src/plugins/logger.collector/build.properties
new file mode 100644
index 0000000..9f69fe3
--- /dev/null
+++ b/src/plugins/logger.collector/build.properties
@@ -0,0 +1,7 @@
+source.. = src/
+output.. = bin/
+bin.includes = META-INF/,\
+ .,\
+ plugin.properties,\
+ plugin.xml,\
+ schema/
diff --git a/src/plugins/logger.collector/plugin.properties b/src/plugins/logger.collector/plugin.properties
new file mode 100644
index 0000000..0d5d401
--- /dev/null
+++ b/src/plugins/logger.collector/plugin.properties
@@ -0,0 +1 @@
+collect_log_files_command=Collect Log Files... \ No newline at end of file
diff --git a/src/plugins/logger.collector/plugin.xml b/src/plugins/logger.collector/plugin.xml
new file mode 100644
index 0000000..107403d
--- /dev/null
+++ b/src/plugins/logger.collector/plugin.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?eclipse version="3.2"?>
+<plugin>
+ <extension-point id="com.motorola.studio.android.logger.collector.log" name="Logger Collector Log File" schema="schema/log.exsd"/>
+ <extension
+ point="org.eclipse.ui.menus">
+ <menuContribution
+ locationURI="menu:help?before=about">
+ <command
+ commandId="com.motorola.studio.android.logger.collector.commands.collectorCommand"
+ id="com.motorola.studio.android.logger.collector.commands.logger.menus.collectorCommand"
+ mnemonic="L">
+ </command>
+ <separator
+ name="com.motorola.studio.android.logger.collector.separator"
+ visible="true">
+ </separator>
+ </menuContribution>
+ </extension>
+ <extension
+ point="org.eclipse.ui.commands">
+ <command
+ defaultHandler="com.motorola.studio.android.logger.collector.ui.handler.LoggerCollectorHandler"
+ id="com.motorola.studio.android.logger.collector.commands.collectorCommand"
+ name="%collect_log_files_command">
+ </command>
+ </extension>
+ <extension
+ point="com.motorola.studio.android.logger.collector.log">
+ <logContribution
+ logFileImpl="com.motorola.studio.android.logger.collector.core.internal.EclipseLogFile">
+ </logContribution>
+ <logContribution
+ logFileImpl="com.motorola.studio.android.logger.collector.core.internal.StudioLogFile">
+ </logContribution>
+ <logContribution
+ logFileImpl="com.motorola.studio.android.logger.collector.core.internal.EnvironmentLogFile">
+ </logContribution>
+ </extension>
+
+
+</plugin>
diff --git a/src/plugins/logger.collector/schema/log.exsd b/src/plugins/logger.collector/schema/log.exsd
new file mode 100644
index 0000000..fb39b6d
--- /dev/null
+++ b/src/plugins/logger.collector/schema/log.exsd
@@ -0,0 +1,102 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<!-- Schema file written by PDE -->
+<schema targetNamespace="com.motorolamobility.studio.android.logger.collector" xmlns="http://www.w3.org/2001/XMLSchema">
+<annotation>
+ <appInfo>
+ <meta.schema plugin="com.motorolamobility.studio.android.logger.collector" id="com.motorola.studio.android.logger.collector.log" name="Logger Collector Log File"/>
+ </appInfo>
+ <documentation>
+ This extension point allow developer to contribute to the logs that will be packed within Collect Log files feature
+ </documentation>
+ </annotation>
+
+ <element name="extension">
+ <annotation>
+ <appInfo>
+ <meta.element />
+ </appInfo>
+ </annotation>
+ <complexType>
+ <sequence>
+ <element ref="logContribution" minOccurs="1" maxOccurs="unbounded"/>
+ </sequence>
+ <attribute name="point" type="string">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ </annotation>
+ </attribute>
+ <attribute name="id" type="string">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ </annotation>
+ </attribute>
+ <attribute name="name" type="string">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ <appInfo>
+ <meta.attribute translatable="true"/>
+ </appInfo>
+ </annotation>
+ </attribute>
+ </complexType>
+ </element>
+
+ <element name="logContribution">
+ <complexType>
+ <attribute name="logFileImpl" type="string" use="required">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ <appInfo>
+ <meta.attribute kind="java"/>
+ </appInfo>
+ </annotation>
+ </attribute>
+ </complexType>
+ </element>
+
+ <annotation>
+ <appInfo>
+ <meta.section type="since"/>
+ </appInfo>
+ <documentation>
+ [Enter the first release in which this extension point appears.]
+ </documentation>
+ </annotation>
+
+ <annotation>
+ <appInfo>
+ <meta.section type="examples"/>
+ </appInfo>
+ <documentation>
+ [Enter extension point usage example here.]
+ </documentation>
+ </annotation>
+
+ <annotation>
+ <appInfo>
+ <meta.section type="apiinfo"/>
+ </appInfo>
+ <documentation>
+ [Enter API information here.]
+ </documentation>
+ </annotation>
+
+ <annotation>
+ <appInfo>
+ <meta.section type="implementation"/>
+ </appInfo>
+ <documentation>
+ [Enter information about supplied implementation of this extension point.]
+ </documentation>
+ </annotation>
+
+
+</schema>
diff --git a/src/plugins/logger.collector/src/com/motorola/studio/android/logger/collector/core/ILogFile.java b/src/plugins/logger.collector/src/com/motorola/studio/android/logger/collector/core/ILogFile.java
new file mode 100644
index 0000000..17063ac
--- /dev/null
+++ b/src/plugins/logger.collector/src/com/motorola/studio/android/logger/collector/core/ILogFile.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.motorola.studio.android.logger.collector.core;
+
+import java.util.List;
+
+import org.eclipse.core.runtime.IPath;
+
+/**
+ * This interface is used by any features that wants to
+ * have a log file contribution within collect log files feature
+ */
+public interface ILogFile
+{
+ /**
+ * Get the name of
+ * @return the name that will be displayed by the user
+ */
+ public String getLogName();
+
+ /**
+ * Get the full path of the log file
+ * @return
+ */
+ public List<IPath> getLogFilePath();
+
+ /**
+ * Tell to the log collector where the log should be placed within
+ * log package
+ * @return the sub dir to put all declared log files
+ */
+ public String getOutputSubfolderName();
+
+}
diff --git a/src/plugins/logger.collector/src/com/motorola/studio/android/logger/collector/core/internal/CollectLogFile.java b/src/plugins/logger.collector/src/com/motorola/studio/android/logger/collector/core/internal/CollectLogFile.java
new file mode 100644
index 0000000..96ac55e
--- /dev/null
+++ b/src/plugins/logger.collector/src/com/motorola/studio/android/logger/collector/core/internal/CollectLogFile.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.logger.collector.core.internal;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.swt.widgets.TableItem;
+
+import com.motorola.studio.android.common.utilities.FileUtil;
+import com.motorola.studio.android.logger.collector.core.ILogFile;
+import com.motorola.studio.android.logger.collector.util.LogCollectorExtensionLoader;
+import com.motorola.studio.android.logger.collector.util.LoggerCollectorConstants;
+import com.motorola.studio.android.logger.collector.util.LoggerCollectorMessages;
+import com.motorola.studio.android.logger.collector.util.PlatformException;
+import com.motorola.studio.android.logger.collector.util.ZipUtil;
+
+/**
+ * This class is responsible to manage all collecting log files requirements.
+ */
+public class CollectLogFile
+{
+
+ private final ArrayList<ILogFile> logs = LogCollectorExtensionLoader.getLogFiles();
+
+ /**
+ * This method is responsible to retrieve files from informed path.
+ *
+ * @param path The path to be retrieved.
+ * @return The log files
+ */
+ public ArrayList<ILogFile> getLogFileList()
+ {
+ return logs;
+ }
+
+ /**
+ * This method is responsible to compact all log files selected by end-user.
+ *
+ * @param fileName The output compacted file name
+ * @param checkedItems The selected items
+ * @return if the selected files are compacted successfully
+ * @throws PlatformException
+ */
+ public boolean zipLogFiles(String fileName, List<TableItem> checkedItems)
+ throws PlatformException
+ {
+ boolean toReturn = true;
+ String nomalizedDirectory =
+ FileUtil.normalizePath(new Path(fileName).removeLastSegments(1).toOSString());
+ // Temporary folder path to store the log files while they are not
+ // compressed.
+ final String tempFolderPath = FileUtil.normalizePath(nomalizedDirectory) + "temp" //$NON-NLS-1$
+ + Double.toString((Math.random())).replaceAll("\\.", "0") + //$NON-NLS-1$ //$NON-NLS-2$
+ File.separator;
+ final IPath tempFolder = new Path(tempFolderPath);
+
+ try
+ {
+ // Creating the temporary folder.
+ FileUtil.mkdir(tempFolderPath);
+ // Copying the log files.
+ for (TableItem item : checkedItems)
+ {
+ ILogFile logFile = (ILogFile) item.getData();
+ IPath outputFolder = tempFolder.append(logFile.getOutputSubfolderName());
+ FileUtil.mkdir(outputFolder.toOSString());
+ for (IPath path : logFile.getLogFilePath())
+ {
+ FileUtil.copy(new File(path.toOSString()),
+ new File(outputFolder.append(path.lastSegment()).toOSString()));
+ }
+
+ }
+ // Compact all log files from temporary folder
+ new ZipUtil(fileName, tempFolderPath).zip();
+ }
+ catch (IOException e)
+ {
+ toReturn = false;
+ throw new PlatformException(new Status(IStatus.CANCEL,
+ LoggerCollectorConstants.PLUGIN_ID, LoggerCollectorMessages.getInstance()
+ .getString("error.logger.collector.zip"))); //$NON-NLS-1$
+ }
+ finally
+ {
+ // Deleting temporary folder an all files within it.
+ FileUtil.delete(tempFolderPath);
+ }
+ return toReturn;
+ }
+
+}
diff --git a/src/plugins/logger.collector/src/com/motorola/studio/android/logger/collector/core/internal/EclipseLogFile.java b/src/plugins/logger.collector/src/com/motorola/studio/android/logger/collector/core/internal/EclipseLogFile.java
new file mode 100644
index 0000000..d5c9822
--- /dev/null
+++ b/src/plugins/logger.collector/src/com/motorola/studio/android/logger/collector/core/internal/EclipseLogFile.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.logger.collector.core.internal;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.Platform;
+
+import com.motorola.studio.android.logger.collector.core.ILogFile;
+import com.motorola.studio.android.logger.collector.util.LoggerCollectorConstants;
+
+/**
+ * This class provides the Eclipse log file to the
+ * Log Files Collector feature
+ */
+public class EclipseLogFile implements ILogFile
+{
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.platform.logger.collector.core.ILogFile#getLogFilePath()
+ */
+ @Override
+ public List<IPath> getLogFilePath()
+ {
+ ArrayList<IPath> logs = new ArrayList<IPath>();
+ logs.add(Platform.getLogFileLocation());
+ return logs;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.platform.logger.collector.core.ILogFile#getLogName()
+ */
+ @Override
+ public String getLogName()
+ {
+ return "Eclipse Log File";
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.platform.logger.collector.core.ILogFile#getOutputSubfolderName()
+ */
+ @Override
+ public String getOutputSubfolderName()
+ {
+ return LoggerCollectorConstants.PLATFORM_LOG_OUTPUT_FOLDER;
+ }
+}
diff --git a/src/plugins/logger.collector/src/com/motorola/studio/android/logger/collector/core/internal/EnvironmentLogFile.java b/src/plugins/logger.collector/src/com/motorola/studio/android/logger/collector/core/internal/EnvironmentLogFile.java
new file mode 100644
index 0000000..000d703
--- /dev/null
+++ b/src/plugins/logger.collector/src/com/motorola/studio/android/logger/collector/core/internal/EnvironmentLogFile.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.logger.collector.core.internal;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.Path;
+
+import com.motorola.studio.android.logger.collector.core.ILogFile;
+import com.motorola.studio.android.logger.collector.util.LoggerCollectorConstants;
+
+/**
+ * This class provides the environment log file
+ * to the log files collector feature
+ */
+public class EnvironmentLogFile implements ILogFile
+{
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.platform.logger.collector.core.ILogFile#getLogFilePath()
+ */
+ @Override
+ public List<IPath> getLogFilePath()
+ {
+ ArrayList<IPath> logs = new ArrayList<IPath>();
+ logs.add(new Path(LoggerCollectorConstants.LOG_PATH).append("environment.log"));
+ return logs;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.platform.logger.collector.core.ILogFile#getLogName()
+ */
+ @Override
+ public String getLogName()
+ {
+ return "Environment Log File";
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.platform.logger.collector.core.ILogFile#getOutputSubfolderName()
+ */
+ @Override
+ public String getOutputSubfolderName()
+ {
+ return LoggerCollectorConstants.PLATFORM_LOG_OUTPUT_FOLDER;
+ }
+
+}
diff --git a/src/plugins/logger.collector/src/com/motorola/studio/android/logger/collector/core/internal/StudioLogFile.java b/src/plugins/logger.collector/src/com/motorola/studio/android/logger/collector/core/internal/StudioLogFile.java
new file mode 100644
index 0000000..934455c
--- /dev/null
+++ b/src/plugins/logger.collector/src/com/motorola/studio/android/logger/collector/core/internal/StudioLogFile.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.logger.collector.core.internal;
+
+import java.io.File;
+import java.io.FileFilter;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.Path;
+
+import com.motorola.studio.android.logger.collector.core.ILogFile;
+import com.motorola.studio.android.logger.collector.util.LoggerCollectorConstants;
+
+/**
+ * This class provides the studio log files
+ * to the collect log files feature
+ */
+public class StudioLogFile implements ILogFile
+{
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.platform.logger.collector.core.ILogFile#getLogFilePath()
+ */
+ @Override
+ public List<IPath> getLogFilePath()
+ {
+ ArrayList<IPath> logs = new ArrayList<IPath>();
+ File rootLogDir = new File(LoggerCollectorConstants.LOG_PATH);
+ if (rootLogDir.exists() && rootLogDir.isDirectory())
+ {
+ File[] logFiles = rootLogDir.listFiles(new FileFilter()
+ {
+
+ @Override
+ public boolean accept(File pathname)
+ {
+ return pathname.getName().startsWith("studio");
+ }
+ });
+ for (File logFile : logFiles)
+ {
+ logs.add(new Path(logFile.getAbsolutePath()));
+ }
+ }
+
+ return logs;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.platform.logger.collector.core.ILogFile#getLogName()
+ */
+ @Override
+ public String getLogName()
+ {
+ return "Studio Log File";
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.platform.logger.collector.core.ILogFile#getOutputSubfolderName()
+ */
+ @Override
+ public String getOutputSubfolderName()
+ {
+ return LoggerCollectorConstants.PLATFORM_LOG_OUTPUT_FOLDER;
+ }
+}
diff --git a/src/plugins/logger.collector/src/com/motorola/studio/android/logger/collector/ui/LogFileColumn.java b/src/plugins/logger.collector/src/com/motorola/studio/android/logger/collector/ui/LogFileColumn.java
new file mode 100644
index 0000000..4541f32
--- /dev/null
+++ b/src/plugins/logger.collector/src/com/motorola/studio/android/logger/collector/ui/LogFileColumn.java
@@ -0,0 +1,217 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.logger.collector.ui;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.swt.SWT;
+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.Listener;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.swt.widgets.TableColumn;
+import org.eclipse.swt.widgets.TableItem;
+
+import com.motorola.studio.android.logger.collector.core.ILogFile;
+import com.motorola.studio.android.logger.collector.core.internal.CollectLogFile;
+import com.motorola.studio.android.logger.collector.util.LoggerCollectorMessages;
+import com.motorola.studio.android.logger.collector.util.PlatformException;
+import com.motorola.studio.android.logger.collector.util.WidgetsUtil;
+
+/**
+ * This class visually represents a log file Table View.
+ */
+public class LogFileColumn extends Composite
+{
+
+ /**
+ * The table of log files.
+ */
+ private Table tableLogFile = null;
+
+ /**
+ *
+ */
+ CollectLogFile collectLogFile = null;
+
+ /**
+ * Public Constructor.
+ *
+ * @param parent Parent this Composite.
+ * @param style Composite Style.
+ */
+ public LogFileColumn(Composite parent, int style)
+ {
+ super(parent, style);
+ collectLogFile = new CollectLogFile();
+ initialize();
+ }
+
+ /**
+ * This method contains property of this composite.
+ */
+ private void initialize()
+ {
+ GridLayout gridLayoutTableView = new GridLayout();
+ gridLayoutTableView.horizontalSpacing = 0;
+ gridLayoutTableView.marginWidth = 0;
+ gridLayoutTableView.marginHeight = 0;
+ gridLayoutTableView.verticalSpacing = 0;
+ GridData gridDatagridLayout = new GridData(GridData.FILL_VERTICAL | SWT.TOP);
+ this.setBackgroundMode(SWT.NONE);
+ this.setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_WHITE));
+ this.setLayoutData(gridDatagridLayout);
+ this.setLayout(gridLayoutTableView);
+
+ GridData gridData = new GridData(GridData.VERTICAL_ALIGN_BEGINNING | GridData.FILL_BOTH);
+ gridData.widthHint = 230;
+ gridData.heightHint = 300;
+ this.setLayoutData(gridData);
+
+ refresh();
+ }
+
+ /**
+ * This is a callback that will allow us to create the viewer and initialize
+ * it.
+ */
+ public void refresh()
+ {
+ GridData gridDataTableView =
+ new GridData(GridData.VERTICAL_ALIGN_BEGINNING | GridData.FILL_BOTH);
+ gridDataTableView.widthHint = 236;
+ gridDataTableView.heightHint = 300;
+ if (tableLogFile == null)
+ {
+ tableLogFile = new Table(this, SWT.CHECK | SWT.BORDER);
+ tableLogFile.setLayoutData(gridDataTableView);
+ }
+ tableLogFile.removeAll();
+ try
+ {
+ for (ILogFile log : collectLogFile.getLogFileList())
+ {
+ TableItem tableItem = new TableItem(tableLogFile, SWT.NONE);
+ tableItem.setText(log.getLogName());
+ tableItem.setData(log);
+ }
+ }
+ catch (Exception e)
+ {
+ MessageDialog.openError(
+ getShell(),
+ LoggerCollectorMessages.getInstance().getString(
+ "logger.collector.wizard.page.title"), //$NON-NLS-1$
+ LoggerCollectorMessages.getInstance().getString(
+ "error.logger.collector.mount.tableview")); //$NON-NLS-1$
+ }
+ packTableColumns();
+ checkAll(true);
+ }
+
+ /**
+ * This method is responsible to pack tree columns
+ */
+ private void packTableColumns()
+ {
+ // Pack the columns
+ TableColumn[] columns = tableLogFile.getColumns();
+ for (int i = 0, n = columns.length; i < n; i++)
+ {
+ columns[i].pack();
+ }
+ }
+
+ /**
+ * This method collects and zips selected log files.
+ *
+ * @param directory The output directory
+ * @param filename The output file name
+ * @return if collects successfully
+ * @throws PlatformException
+ */
+ public boolean collect(String filename) throws PlatformException
+ {
+ return new CollectLogFile().zipLogFiles(filename,
+ WidgetsUtil.getCheckedLeafItems(this.tableLogFile));
+ }
+
+ /**
+ * This method tests if the table view has nodes and if there is selected
+ * nodes.
+ *
+ * @return true if the table view has nodes and if there is selected nodes.
+ */
+ public boolean hasNodeSelected()
+ {
+ return WidgetsUtil.getCheckedLeafItems(this.tableLogFile).size() > 0;
+ }
+
+ /**
+ * This method collects and compacts selected log files.
+ *
+ * @param directory The output directory
+ * @param filename The output file name
+ * @return if collects successfully
+ */
+ public ArrayList<String> selectedLogFilesExist()
+ {
+ List<TableItem> list = WidgetsUtil.getCheckedLeafItems(this.tableLogFile);
+ ArrayList<String> notFoundItems = new ArrayList<String>();
+ for (TableItem tableItem : list)
+ {
+ Object data = tableItem.getData();
+ if (data instanceof ILogFile)
+ {
+ ILogFile logFile = (ILogFile) data;
+ for (IPath path : logFile.getLogFilePath())
+ {
+ if (!WidgetsUtil.fileExist(path.toOSString()))
+ {
+ notFoundItems.add(logFile.getLogName());
+ }
+
+ }
+ }
+ }
+ return notFoundItems;
+ }
+
+ /**
+ * This method adds a TableListener
+ *
+ * @param eventType Type of event
+ * @param listener Listener
+ */
+ public void addTableListener(int eventType, Listener listener)
+ {
+ this.tableLogFile.addListener(eventType, listener);
+ }
+
+ public void checkAll(boolean selectionValue)
+ {
+ for (TableItem item : tableLogFile.getItems())
+ {
+ item.setChecked(selectionValue);
+ }
+
+ }
+}
diff --git a/src/plugins/logger.collector/src/com/motorola/studio/android/logger/collector/ui/handler/LoggerCollectorHandler.java b/src/plugins/logger.collector/src/com/motorola/studio/android/logger/collector/ui/handler/LoggerCollectorHandler.java
new file mode 100644
index 0000000..2532582
--- /dev/null
+++ b/src/plugins/logger.collector/src/com/motorola/studio/android/logger/collector/ui/handler/LoggerCollectorHandler.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.logger.collector.ui.handler;
+
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+
+import com.motorola.studio.android.logger.collector.ui.wizard.LoggerCollectorWizard;
+import com.motorola.studio.android.logger.collector.util.WidgetsUtil;
+
+/**
+ * This class is responsible to handling action in menu collect log file item.
+ */
+public class LoggerCollectorHandler extends AbstractHandler
+{
+
+ /**
+ * This method is responsible to execute handler action
+ *
+ * @param event execute event
+ * @return handler action
+ */
+ @Override
+ public Object execute(ExecutionEvent event) throws ExecutionException
+ {
+ return WidgetsUtil.runWizard(new LoggerCollectorWizard());
+ }
+}
diff --git a/src/plugins/logger.collector/src/com/motorola/studio/android/logger/collector/ui/wizard/LoggerCollectorWizard.java b/src/plugins/logger.collector/src/com/motorola/studio/android/logger/collector/ui/wizard/LoggerCollectorWizard.java
new file mode 100644
index 0000000..bdeb955
--- /dev/null
+++ b/src/plugins/logger.collector/src/com/motorola/studio/android/logger/collector/ui/wizard/LoggerCollectorWizard.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.motorola.studio.android.logger.collector.ui.wizard;
+
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.jface.wizard.Wizard;
+
+import com.motorola.studio.android.logger.collector.util.LoggerCollectorConstants;
+import com.motorola.studio.android.logger.collector.util.LoggerCollectorMessages;
+import com.motorola.studio.android.logger.collector.util.PlatformException;
+
+/**
+ * This class represents the logger collector wizard.
+ */
+public class LoggerCollectorWizard extends Wizard
+{
+
+ /**
+ * Initializing instance of wizard page
+ */
+ private final LoggerCollectorWizardPage loggerCollectorWizardPage =
+ new LoggerCollectorWizardPage("wizardPage"); //$NON-NLS-1$
+
+ /**
+ * The Constructor
+ */
+ public LoggerCollectorWizard()
+ {
+ setWindowTitle(LoggerCollectorMessages.getInstance().getString(
+ "logger.collector.wizard.page.title")); //$NON-NLS-1$
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.jface.wizard.Wizard#canFinish()
+ */
+ @Override
+ public boolean canFinish()
+ {
+ return loggerCollectorWizardPage.isPageComplete();
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.jface.wizard.Wizard#addPages()
+ */
+ @Override
+ public void addPages()
+ {
+ super.addPages();
+ addPage(loggerCollectorWizardPage);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.jface.wizard.Wizard#performFinish()
+ */
+ @Override
+ public boolean performFinish()
+ {
+ if (loggerCollectorWizardPage.getLogFileColumn() != null)
+ {
+ try
+ {
+
+ IPath filename = new Path(loggerCollectorWizardPage.getFilename());
+ if (filename.getFileExtension() == null
+ || !filename.getFileExtension().equalsIgnoreCase(
+ LoggerCollectorConstants.ZIP_FILE_EXTENSION))
+ {
+ filename =
+ filename.addFileExtension(LoggerCollectorConstants.ZIP_FILE_EXTENSION);
+ }
+ if (loggerCollectorWizardPage.getLogFileColumn().collect(filename.toOSString()))
+ {
+ MessageDialog.openInformation(
+ getShell(),
+ LoggerCollectorMessages.getInstance().getString(
+ "logger.collector.wizard.page.title"), //$NON-NLS-1$
+ LoggerCollectorMessages.getInstance().getString(
+ "logger.collector.wizard.success")); //$NON-NLS-1$
+ }
+ return true;
+ }
+ catch (PlatformException e)
+ {
+ MessageDialog.openError(getShell(), LoggerCollectorMessages.getInstance()
+ .getString("logger.collector.wizard.page.title"), //$NON-NLS-1$
+ e.getMessage());
+ return false;
+ }
+ }
+ return false;
+ }
+}
diff --git a/src/plugins/logger.collector/src/com/motorola/studio/android/logger/collector/ui/wizard/LoggerCollectorWizardPage.java b/src/plugins/logger.collector/src/com/motorola/studio/android/logger/collector/ui/wizard/LoggerCollectorWizardPage.java
new file mode 100644
index 0000000..fc845b2
--- /dev/null
+++ b/src/plugins/logger.collector/src/com/motorola/studio/android/logger/collector/ui/wizard/LoggerCollectorWizardPage.java
@@ -0,0 +1,311 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.logger.collector.ui.wizard;
+
+import java.util.ArrayList;
+import java.util.Calendar;
+
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.jface.preference.DirectoryFieldEditor;
+import org.eclipse.jface.wizard.WizardPage;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.layout.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.Event;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.ui.PlatformUI;
+
+import com.motorola.studio.android.logger.collector.ui.LogFileColumn;
+import com.motorola.studio.android.logger.collector.util.LoggerCollectorConstants;
+import com.motorola.studio.android.logger.collector.util.LoggerCollectorMessages;
+import com.motorola.studio.android.logger.collector.util.WidgetsFactory;
+import com.motorola.studio.android.logger.collector.util.WidgetsUtil;
+
+/**
+ * This class contains the design of collect log files wizard page.
+ */
+public class LoggerCollectorWizardPage extends WizardPage
+{
+
+ /**
+ * Directory field to output compacted file
+ */
+ private DirectoryFieldEditor logDirecotryField = null;
+
+ private Text filenameText = null;
+
+ public static final String LOGGER_COLLECTOR_HELP_ID =
+ "com.motorola.studio.platform.logger.collector.collectlogs";
+
+ /**
+ * Composite used to show the Log File TableColumn.
+ */
+ private LogFileColumn logFileColumn = null;
+
+ private Button selectAll = null;
+
+ private Button unselectAll = null;
+
+ /**
+ * Wizard composite
+ */
+ private Composite composite;
+
+ /**
+ * Field to check if user has changed the wizard
+ */
+ private boolean userChangedWizard = false;
+
+ /**
+ * The default constructor
+ *
+ * @param pageName The page name
+ */
+ protected LoggerCollectorWizardPage(String pageName)
+ {
+ super(pageName);
+ setTitle(LoggerCollectorMessages.getInstance().getString(
+ "logger.collector.wizard.page.title")); //$NON-NLS-1$
+ setDescription(LoggerCollectorMessages.getInstance().getString(
+ "logger.collector.wizard.page.description")); //$NON-NLS-1$
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see
+ * org.eclipse.jface.dialogs.IDialogPage#createControl(org.eclipse.swt.widgets
+ * .Composite)
+ */
+ @Override
+ public void createControl(Composite parent)
+ {
+ composite = getComposite(parent);
+ composite.setLayout(new GridLayout(1, false));
+ composite.setLayoutData(new GridData(GridData.FILL_VERTICAL | SWT.TOP));
+
+ Composite compositeTop = WidgetsFactory.createComposite(composite, 3);
+ logDirecotryField =
+ new DirectoryFieldEditor("logFileNameField", //$NON-NLS-1$
+ LoggerCollectorMessages.getInstance().getString(
+ "logger.collector.wizard.page.directory") + ":", //$NON-NLS-1$ //$NON-NLS-2$
+ compositeTop);
+
+ logDirecotryField.getTextControl(compositeTop).addListener(SWT.Modify, listener);
+ logDirecotryField.getTextControl(compositeTop).setTextLimit(200);
+
+ Label filenameLabel = new Label(compositeTop, SWT.NONE);
+ filenameLabel.setText(LoggerCollectorMessages.getInstance().getString(
+ "logger.collector.wizard.page.file"));//$NON-NLS-1$
+ GridData layoutData = new GridData(SWT.LEFT, SWT.CENTER, false, false);
+ filenameLabel.setLayoutData(layoutData);
+
+ filenameText = new Text(compositeTop, SWT.BORDER);
+
+ layoutData = new GridData(SWT.FILL, SWT.CENTER, true, false, 2, 1);
+ filenameText.setLayoutData(layoutData);
+
+ Composite compositeBottom = WidgetsFactory.createComposite(composite, 2);
+
+ Label l = new Label(compositeBottom, SWT.NONE);
+ l.setText(LoggerCollectorMessages.getInstance()
+ .getString("logger.collector.tableview.root"));//$NON-NLS-1$
+ GridData d = new GridData(SWT.LEFT, SWT.NONE, true, false, 2, 1);
+ l.setLayoutData(d);
+ logFileColumn = new LogFileColumn(compositeBottom, SWT.NONE);
+ logFileColumn.addTableListener(SWT.Selection, listener);
+
+ Composite buttons = new Composite(compositeBottom, SWT.NONE);
+ d = new GridData(SWT.NONE, SWT.TOP, false, true, 1, 1);
+ buttons.setLayoutData(d);
+ FillLayout layout = new FillLayout(SWT.VERTICAL);
+ layout.spacing = 2;
+ buttons.setLayout(layout);
+
+ selectAll = new Button(buttons, SWT.PUSH);
+ selectAll.setText(LoggerCollectorMessages.getInstance().getString(
+ "logger.collector.wizard.page.selectAll"));
+ selectAll.addSelectionListener(new ButtonSelectionListener(true));
+ selectAll.addListener(SWT.Selection, listener);
+ unselectAll = new Button(buttons, SWT.PUSH);
+ unselectAll.setText(LoggerCollectorMessages.getInstance().getString(
+ "logger.collector.wizard.page.unselectAll"));
+ unselectAll.addSelectionListener(new ButtonSelectionListener(false));
+ unselectAll.addListener(SWT.Selection, listener);
+ setControl(composite);
+ PlatformUI.getWorkbench().getHelpSystem().setHelp(getControl(), LOGGER_COLLECTOR_HELP_ID);
+
+ logDirecotryField.setStringValue(System.getProperty("user.home"));
+
+ Calendar c = Calendar.getInstance();
+ filenameText.setText("studio_andr_" + c.get(Calendar.YEAR) + c.get(Calendar.MONTH)
+ + c.get(Calendar.DAY_OF_MONTH) + c.get(Calendar.HOUR) + c.get(Calendar.MINUTE)
+ + "." + LoggerCollectorConstants.ZIP_FILE_EXTENSION);
+
+ setErrorMessage(null);
+ }
+
+ /**
+ * Listener of collect log files finish button
+ */
+ private final Listener listener = new Listener()
+ {
+
+ @Override
+ public void handleEvent(Event event)
+ {
+ userChangedWizard = true;
+ setPageComplete(isPageComplete());
+ setErrorMessage(getErrorMessage());
+ }
+ };
+
+ /**
+ * Return the composite used in the wizard.
+ *
+ * @param parent The parent content composite.
+ * @return The composite used in the wizard.
+ */
+ protected Composite getComposite(Composite parent)
+ {
+ if (this.composite == null)
+ {
+ this.composite = WidgetsFactory.createComposite(parent);
+ }
+ return this.composite;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.jface.wizard.WizardPage#isPageComplete()
+ */
+ @Override
+ public boolean isPageComplete()
+ {
+ return (getErrorMessage() == null) && !WidgetsUtil.isNullOrEmpty(this.logDirecotryField)
+ && logFileColumn.hasNodeSelected();
+ }
+
+ /**
+ * Returns the error message, null if there is no error.
+ *
+ * @return an error message
+ */
+ @Override
+ public String getErrorMessage()
+ {
+ String message = null;
+ String outputDir = Path.fromOSString(this.logDirecotryField.getStringValue()).toOSString();
+ ArrayList<String> notFoundLogs = null;
+
+ if (userChangedWizard)
+ {
+ if (WidgetsUtil.isNullOrEmpty(this.logDirecotryField))
+ {
+ message =
+ LoggerCollectorMessages.getInstance().getString(
+ "error.logger.collector.directory.empty"); //$NON-NLS-1$
+ }
+ else if (!WidgetsUtil.fileExist(outputDir))
+ {
+ message =
+ LoggerCollectorMessages.getInstance().getString(
+ "error.logger.collector.directory.not.found"); //$NON-NLS-1$
+ }
+ else if ((logFileColumn != null) && !logFileColumn.hasNodeSelected())
+ {
+ message =
+ LoggerCollectorMessages.getInstance().getString(
+ "error.logger.collector.log.not.selected"); //$NON-NLS-1$
+ }
+ else if ((logFileColumn != null)
+ && ((notFoundLogs = logFileColumn.selectedLogFilesExist()).size() > 0))
+ {
+ StringBuilder messageBuilder =
+ new StringBuilder(LoggerCollectorMessages.getInstance().getString(
+ "error.logger.collector.log.not.found"));
+ messageBuilder.append(": ");
+ for (String log : notFoundLogs)
+ {
+ messageBuilder.append(log);
+ messageBuilder.append(",");
+ }
+ messageBuilder.deleteCharAt(messageBuilder.length() - 1);
+
+ message = messageBuilder.toString();
+ }
+ }
+ return message;
+ }
+
+ /**
+ * Gets the log file column (table view)
+ *
+ * @return log file column
+ */
+ public LogFileColumn getLogFileColumn()
+ {
+ return this.logFileColumn;
+ }
+
+ /**
+ * Gets the file name text
+ *
+ * @return the file name text
+ */
+ public String getFilename()
+ {
+ IPath fileName = new Path(filenameText.getText());
+ if ((fileName.getFileExtension() == null)
+ || !fileName.getFileExtension().equalsIgnoreCase(
+ LoggerCollectorConstants.ZIP_FILE_EXTENSION))
+ {
+ fileName.addFileExtension(LoggerCollectorConstants.ZIP_FILE_EXTENSION);
+ }
+
+ return new Path(this.logDirecotryField.getStringValue()).append(fileName).toOSString();
+ }
+
+ final class ButtonSelectionListener implements SelectionListener
+ {
+ private final boolean selectionValue;
+
+ public ButtonSelectionListener(boolean selectionValue)
+ {
+ this.selectionValue = selectionValue;
+ }
+
+ @Override
+ public void widgetDefaultSelected(SelectionEvent e)
+ {
+ //do nothing
+ }
+
+ @Override
+ public void widgetSelected(SelectionEvent e)
+ {
+ logFileColumn.checkAll(selectionValue);
+ }
+
+ };
+}
diff --git a/src/plugins/logger.collector/src/com/motorola/studio/android/logger/collector/util/LogCollectorExtensionLoader.java b/src/plugins/logger.collector/src/com/motorola/studio/android/logger/collector/util/LogCollectorExtensionLoader.java
new file mode 100644
index 0000000..2bef177
--- /dev/null
+++ b/src/plugins/logger.collector/src/com/motorola/studio/android/logger/collector/util/LogCollectorExtensionLoader.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.logger.collector.util;
+
+import java.util.ArrayList;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IConfigurationElement;
+import org.eclipse.core.runtime.IExtension;
+import org.eclipse.core.runtime.IExtensionPoint;
+import org.eclipse.core.runtime.Platform;
+
+import com.motorola.studio.android.logger.collector.core.ILogFile;
+
+/**
+ * This class is responsible to load the log collector contributor
+ * extension point and read all needed information
+ */
+public class LogCollectorExtensionLoader
+{
+ private static final String LOGGER_EXTENSION_POINT_ID =
+ "com.motorola.studio.android.logger.collector.log";
+
+ private static final String LOG_FILE_ELEMENT = "logContribution";
+
+ private static final String LOG_FILE_ATTRIBUTE = "logFileImpl";
+
+ public static ArrayList<ILogFile> getLogFiles()
+ {
+ ArrayList<ILogFile> logs = new ArrayList<ILogFile>();
+ IExtensionPoint point =
+ Platform.getExtensionRegistry().getExtensionPoint(LOGGER_EXTENSION_POINT_ID);
+ if (point != null)
+ {
+ IExtension[] extensions = point.getExtensions();
+
+ for (IExtension ext : extensions)
+ {
+ for (IConfigurationElement element : ext.getConfigurationElements())
+ {
+ if (element.getName().equals(LOG_FILE_ELEMENT))
+ {
+ try
+ {
+ Object o = element.createExecutableExtension(LOG_FILE_ATTRIBUTE);
+ if (o instanceof ILogFile)
+ {
+ logs.add((ILogFile) o);
+ }
+ }
+ catch (CoreException e)
+ {
+ //do nothing
+ }
+ }
+ }
+ }
+
+ }
+
+ return logs;
+ }
+}
diff --git a/src/plugins/logger.collector/src/com/motorola/studio/android/logger/collector/util/LoggerCollectorConstants.java b/src/plugins/logger.collector/src/com/motorola/studio/android/logger/collector/util/LoggerCollectorConstants.java
new file mode 100644
index 0000000..6415596
--- /dev/null
+++ b/src/plugins/logger.collector/src/com/motorola/studio/android/logger/collector/util/LoggerCollectorConstants.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.logger.collector.util;
+
+import java.io.File;
+
+import org.eclipse.core.runtime.Platform;
+
+/**
+ * Constant definitions for logger collector plug-in preferences
+ */
+public class LoggerCollectorConstants
+{
+
+ /**
+ * The constant contains a string representation of the regular expression
+ * to validate the file name format. The file name must follow the formats
+ * above: - Only alphanumeric characters and point must be supported
+ * ([A-Za-z0-9_.]). - The max length must be 60 characters;
+ */
+ public static final String FILE_NAME_REGEX = "[\\w.]{1,60}"; //$NON-NLS-1$
+
+ /**
+ * The plug-in ID
+ */
+ public static final String PLUGIN_ID = "com.motorolamobility.studio.android.logger.collector"; //$NON-NLS-1$
+
+ // The plugin path location
+ private static final String PLUGIN_LOCATION = File.separator + ".metadata" //$NON-NLS-1$
+ + File.separator + ".plugins" + File.separator //$NON-NLS-1$
+ + "com.motorolamobility.studio.android.logger"; //$NON-NLS-1$
+
+ // The absolute logger files path
+ public static final String LOG_PATH = Platform.getLocation() + PLUGIN_LOCATION;
+
+ public static final String PLATFORM_LOG_OUTPUT_FOLDER = "platform";
+
+ public static final String ZIP_FILE_EXTENSION = "zip";
+}
diff --git a/src/plugins/logger.collector/src/com/motorola/studio/android/logger/collector/util/LoggerCollectorMessages.java b/src/plugins/logger.collector/src/com/motorola/studio/android/logger/collector/util/LoggerCollectorMessages.java
new file mode 100644
index 0000000..9311858
--- /dev/null
+++ b/src/plugins/logger.collector/src/com/motorola/studio/android/logger/collector/util/LoggerCollectorMessages.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.logger.collector.util;
+
+import java.text.MessageFormat;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+
+/**
+ * Class to handle the messages. By default, all keys has the class name in its
+ * prefix, plus a sequential number.
+ */
+public class LoggerCollectorMessages
+{
+
+ /**
+ * The shared instance.
+ */
+ private static LoggerCollectorMessages instance = null;
+
+ /**
+ * Bundle to get messages from module (properties file).
+ */
+ private final ResourceBundle bundle;
+
+ /**
+ * Class used to get the message.
+ */
+ private Class<? extends Object> clazz;
+
+ /**
+ * Default constructor.
+ */
+ private LoggerCollectorMessages()
+ {
+ this.bundle = ResourceBundle.getBundle("loggerCollector"); //$NON-NLS-1$
+ }
+
+ /**
+ * Returns the single instance.
+ *
+ * @param clazz Class used to get the messages.
+ * @return The singleton instance.
+ */
+ public static synchronized LoggerCollectorMessages getInstance(Class<? extends Object> clazz)
+ {
+ if (instance == null)
+ {
+ instance = new LoggerCollectorMessages();
+ }
+ instance.clazz = clazz;
+ return instance;
+ }
+
+ /**
+ * Returns the single instance.
+ *
+ * @return The singleton instance.
+ */
+ public static synchronized LoggerCollectorMessages getInstance()
+ {
+ if (instance == null)
+ {
+ instance = new LoggerCollectorMessages();
+ }
+ instance.clazz = null;
+ return instance;
+ }
+
+ /**
+ * Returns the message of the given key.
+ *
+ * @param key The message key
+ * @return The message of the given key.
+ */
+ public String getString(String key)
+ {
+ try
+ {
+ return bundle
+ .getString(((this.clazz == null) ? "" : (this.clazz.getName() + ".")) + key); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+ catch (MissingResourceException e)
+ {
+ return "!" + key + "!"; //$NON-NLS-1$ //$NON-NLS-2$
+ }
+ }
+
+ /**
+ * Returns the message of the given key.
+ *
+ * @param key The message key
+ * @param arguments Arguments to replace the pattern in the corresponding
+ * message of the key.
+ * @return The message of the given key.
+ */
+ public String getString(String key, Object... arguments)
+ {
+ try
+ {
+ String message =
+ bundle.getString(((this.clazz == null) ? "" : (this.clazz.getName() + ".")) + key); //$NON-NLS-1$ //$NON-NLS-2$
+ return MessageFormat.format(message, arguments);
+ }
+ catch (MissingResourceException e)
+ {
+ return "!" + key + "!"; //$NON-NLS-1$ //$NON-NLS-2$
+ }
+ }
+}
diff --git a/src/plugins/logger.collector/src/com/motorola/studio/android/logger/collector/util/PlatformException.java b/src/plugins/logger.collector/src/com/motorola/studio/android/logger/collector/util/PlatformException.java
new file mode 100644
index 0000000..b30ccba
--- /dev/null
+++ b/src/plugins/logger.collector/src/com/motorola/studio/android/logger/collector/util/PlatformException.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.motorola.studio.android.logger.collector.util;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IStatus;
+
+/**
+ * This class represents a basic platform exception that all Studios should
+ * extend and implement other functions. The error is logged when the status
+ * severity is <code>IStatus.CANCEL</code>.
+ *
+ * @see org.eclipse.core.runtime.CoreException
+ */
+
+public class PlatformException extends CoreException
+{
+
+ /**
+ * Universal version identifier for a Serializable object.
+ */
+ private static final long serialVersionUID = 5165818604668097411L;
+
+ /**
+ * Construct a platform exception with the given status.
+ *
+ * @param status The IStatus.
+ */
+ public PlatformException(final IStatus status)
+ {
+ super(status);
+ if (status.getSeverity() == IStatus.CANCEL)
+ {
+ PlatformLogger.getInstance(PlatformException.class).error(this.getMessage(), this);
+ }
+ }
+}
diff --git a/src/plugins/logger.collector/src/com/motorola/studio/android/logger/collector/util/PlatformLogger.java b/src/plugins/logger.collector/src/com/motorola/studio/android/logger/collector/util/PlatformLogger.java
new file mode 100644
index 0000000..efdac2b
--- /dev/null
+++ b/src/plugins/logger.collector/src/com/motorola/studio/android/logger/collector/util/PlatformLogger.java
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.motorola.studio.android.logger.collector.util;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import com.motorola.studio.android.logger.Logger;
+
+/**
+ * Class with useful methods to log errors.
+ */
+public class PlatformLogger
+{
+
+ /**
+ * Logger for this class.
+ */
+ private final Logger logger;
+
+ /**
+ * Shared instances by Class
+ */
+ private static Map<Class<? extends Object>, PlatformLogger> instances =
+ new HashMap<Class<? extends Object>, PlatformLogger>();
+
+ /**
+ * Default constructor
+ *
+ * @param clazz Class to be logged.
+ */
+ private PlatformLogger(Class<? extends Object> clazz)
+ {
+ this.logger = Logger.getLogger(clazz.getName());
+ }
+
+ /**
+ * Returns an instance by a given class object.
+ *
+ * @param clazz The given class.
+ * @return an instance by a given class object.
+ */
+ public static synchronized PlatformLogger getInstance(Class<? extends Object> clazz)
+ {
+ PlatformLogger instance = instances.get(clazz);
+ if (instance == null)
+ {
+ instance = new PlatformLogger(clazz);
+ instances.put(clazz, instance);
+ }
+ return instance;
+ }
+
+ /**
+ * Log a message object with the DEBUG level.
+ *
+ * @param message The message to log.
+ */
+ public void debug(String message)
+ {
+ debug(message, null);
+ }
+
+ /**
+ * Log a message object with the DEBUG level.
+ *
+ * @param message The message to log.
+ * @param exception Exception whose stack will be logged.
+ */
+ public void debug(String message, Throwable exception)
+ {
+ if (null == exception)
+ {
+ logger.debug(message);
+ }
+ else
+ {
+ logger.debug(message, exception);
+ }
+ }
+
+ /**
+ * Log a message object with the WARN level.
+ *
+ * @param message The message to log.
+ */
+ public void warn(String message)
+ {
+ warn(message, null);
+ }
+
+ /**
+ * Log a message object with the WARN level.
+ *
+ * @param message The message to log.
+ * @param exception Exception whose stack will be logged.
+ */
+ public void warn(String message, Throwable exception)
+ {
+ if (null == exception)
+ {
+ logger.warn(message);
+ }
+ else
+ {
+ logger.warn(message, exception);
+ }
+ }
+
+ /**
+ * Log a message object with the FATAL level.
+ *
+ * @param message The message to log.
+ */
+ public void fatal(String message)
+ {
+ fatal(message, null);
+ }
+
+ /**
+ * Log a message object with the FATAL level.
+ *
+ * @param message The message to log.
+ * @param exception Exception whose stack will be logged.
+ */
+ public void fatal(String message, Throwable exception)
+ {
+ if (null == exception)
+ {
+ logger.fatal(message);
+ }
+ else
+ {
+ logger.fatal(message, exception);
+ }
+ }
+
+ /**
+ * Log a message object with the INFO level.
+ *
+ * @param message The message to log.
+ */
+ public void info(String message)
+ {
+ info(message, null);
+ }
+
+ /**
+ * Log a message object with the INFO level.
+ *
+ * @param message The message to log.
+ * @param exception Exception whose stack will be logged.
+ */
+ public void info(String message, Throwable exception)
+ {
+ if (null == exception)
+ {
+ logger.info(message);
+ }
+ else
+ {
+ logger.info(message, exception);
+ }
+ }
+
+ /**
+ * Log a message object with the ERROR level.
+ *
+ * @param message The message to log.
+ */
+ public void error(String message)
+ {
+ this.logger.error(message);
+ }
+
+ /**
+ * Log a message object with the ERROR level.
+ *
+ * @param message The message to log.
+ * @param exception Exception whose stack will be logged.
+ */
+ public void error(String message, Throwable exception)
+ {
+ this.logger.error(message, exception);
+ }
+
+}
diff --git a/src/plugins/logger.collector/src/com/motorola/studio/android/logger/collector/util/WidgetsFactory.java b/src/plugins/logger.collector/src/com/motorola/studio/android/logger/collector/util/WidgetsFactory.java
new file mode 100644
index 0000000..40c679c
--- /dev/null
+++ b/src/plugins/logger.collector/src/com/motorola/studio/android/logger/collector/util/WidgetsFactory.java
@@ -0,0 +1,445 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.logger.collector.util;
+
+import org.eclipse.jface.dialogs.IDialogConstants;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Combo;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Group;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.List;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.swt.widgets.TableColumn;
+import org.eclipse.swt.widgets.TableItem;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.swt.widgets.ToolBar;
+import org.eclipse.swt.widgets.ToolItem;
+import org.eclipse.swt.widgets.TreeItem;
+
+/**
+ * Class factory to create widgets.
+ */
+public class WidgetsFactory
+{
+
+ /**
+ * Create a new composite with two columns.
+ *
+ * @param parent The parent composite.
+ * @return A composite with two columns.
+ */
+ public static Composite createComposite(Composite parent)
+ {
+ Composite toReturn = new Composite(parent, SWT.NULL);
+ toReturn.setLayout(createGridLayout());
+ toReturn.setLayoutData(new GridData(GridData.GRAB_HORIZONTAL | GridData.FILL_HORIZONTAL));
+
+ return toReturn;
+ }
+
+ /**
+ * Create a new composite with the given column count.
+ *
+ * @param parent The parent composite.
+ * @param numColumns The column count for the new composite.
+ * @return A composite with the given column count.
+ */
+ public static Composite createComposite(Composite parent, int numColumns)
+ {
+ Composite toReturn = createComposite(parent);
+ ((GridLayout) toReturn.getLayout()).numColumns = numColumns;
+
+ return toReturn;
+ }
+
+ /**
+ * Creates a new line.
+ *
+ * @param parent The parent composite.
+ * @return A label with a line.
+ */
+ public static Label createLine(Composite parent)
+ {
+ Label toReturn = new Label(parent, SWT.SEPARATOR | SWT.HORIZONTAL | SWT.BOLD);
+ GridData gridData = new GridData(GridData.FILL_HORIZONTAL);
+ gridData.horizontalSpan = ((GridLayout) parent.getLayout()).numColumns;
+ toReturn.setLayoutData(gridData);
+
+ return toReturn;
+ }
+
+ /**
+ * Creates a new GridLayout with two columns.
+ *
+ * @return A new GridLayout with two columns.
+ */
+ public static GridLayout createGridLayout()
+ {
+ GridLayout toReturn = new GridLayout();
+ toReturn.numColumns = 2;
+ toReturn.makeColumnsEqualWidth = false;
+ toReturn.marginWidth = 0;
+ toReturn.marginHeight = 0;
+
+ return toReturn;
+ }
+
+ /**
+ * Creates a new GridLayout with the given number of columns.
+ *
+ * @return A new GridLayout with the given number of columns.
+ */
+ public static GridLayout createGridLayout(int numColumns)
+ {
+ GridLayout toReturn = new GridLayout();
+ toReturn.numColumns = numColumns;
+ toReturn.makeColumnsEqualWidth = false;
+ toReturn.marginWidth = 0;
+ toReturn.marginHeight = 0;
+
+ return toReturn;
+ }
+
+ /**
+ * Creates a new GridLayout with the given number of columns for the given
+ * composite.
+ *
+ * @return A new GridLayout with the given number of columns for the given
+ * composite.
+ */
+ public static GridLayout createGridLayout(int numColumns, Composite composite)
+ {
+ GridLayout toReturn = new GridLayout();
+ toReturn.numColumns = numColumns;
+ toReturn.makeColumnsEqualWidth = false;
+ toReturn.marginWidth = 0;
+ toReturn.marginHeight = 0;
+
+ composite.setLayout(toReturn);
+ return toReturn;
+ }
+
+ /**
+ * Creates a new label.
+ *
+ * @param parent The parent composite.
+ * @param text Text used in label.
+ * @return A new label
+ */
+ public static Label createLabel(Composite parent, String text)
+ {
+ return createLabel(parent, text, SWT.NONE);
+ }
+
+ /**
+ * Creates a new label.
+ *
+ * @param parent The parent composite.
+ * @param text Text used in label.
+ * @param style The style used in label.
+ * @return A new label
+ */
+ public static Label createLabel(Composite parent, String text, int style)
+ {
+ Label toReturn = new Label(parent, style);
+ if (text != null)
+ {
+ toReturn.setText(text);
+ }
+ toReturn.setFont(parent.getFont());
+
+ return toReturn;
+ }
+
+ /**
+ * Creates a new combo.
+ *
+ * @param parent The parent composite.
+ * @return The new combo
+ */
+ public static Combo createCombo(Composite parent)
+ {
+ Combo toReturn = new Combo(parent, SWT.READ_ONLY);
+ GridData data = new GridData(GridData.FILL_HORIZONTAL);
+ data.widthHint = IDialogConstants.ENTRY_FIELD_WIDTH;
+ data.horizontalSpan = 1;
+ toReturn.setLayoutData(data);
+
+ return toReturn;
+ }
+
+ /**
+ * Creates two labels used to show a read only value in screens.
+ *
+ * @param parent The parent composite.
+ * @param text The text used in both labels.
+ * @return The label that will show a read only value.
+ */
+ public static Label createValueLabel(Composite parent, String text)
+ {
+ createLabel(parent, text);
+
+ return createLabel(parent, null);
+ }
+
+ /**
+ * Creates a new button.
+ *
+ * @param parent The parent composite.
+ * @param text The text of the button.
+ * @return A new button
+ */
+ public static Button createButton(Composite parent, String text)
+ {
+ Button toReturn = new Button(parent, SWT.PUSH);
+ toReturn.setFont(parent.getFont());
+ toReturn.setText(text);
+ toReturn.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+
+ return toReturn;
+ }
+
+ /**
+ * Creates a new table widget.
+ *
+ * @param parent The parent composite.
+ * @return The new table
+ */
+ public static Table createTable(Composite parent)
+ {
+ Table toReturn =
+ new Table(parent, SWT.H_SCROLL | SWT.V_SCROLL | SWT.MULTI | SWT.FULL_SELECTION
+ | SWT.BORDER);
+ GridData data = new GridData(GridData.FILL_BOTH);
+ data.widthHint = IDialogConstants.ENTRY_FIELD_WIDTH;
+ data.heightHint = toReturn.getItemHeight();
+ data.horizontalSpan = 1;
+ toReturn.setLayoutData(data);
+
+ return toReturn;
+ }
+
+ /**
+ * Creates a new table widget.
+ *
+ * @param parent The parent composite.
+ * @return The new table
+ */
+ public static Table createTableMultiSelection(Composite parent)
+ {
+ Table toReturn =
+ new Table(parent, SWT.H_SCROLL | SWT.V_SCROLL | SWT.FULL_SELECTION | SWT.BORDER
+ | SWT.MULTI);
+ GridData data = new GridData(GridData.FILL_BOTH);
+ data.widthHint = IDialogConstants.ENTRY_FIELD_WIDTH;
+ data.heightHint = toReturn.getItemHeight();
+ data.horizontalSpan = 1;
+ toReturn.setLayoutData(data);
+
+ return toReturn;
+ }
+
+ /**
+ * Creates a new table column.
+ *
+ * @param table The table of the column.
+ * @param text The column text.
+ * @return A new table column
+ */
+ public static TableColumn createTableColumn(Table table, String text)
+ {
+ TableColumn toReturn = new TableColumn(table, SWT.NONE);
+ toReturn.setText(text);
+
+ return toReturn;
+ }
+
+ /**
+ * Creates a new table item.
+ *
+ * @param table The table of the table item.
+ * @param image The image of the item.
+ * @param s The text of the item.
+ * @return The new table item.
+ */
+ public static TableItem createTableItem(Table table, Image image, String s)
+ {
+ TableItem toReturn = new TableItem(table, SWT.NONE);
+ toReturn.setText(s);
+ if (image != null)
+ {
+ toReturn.setImage(image);
+ }
+
+ return toReturn;
+ }
+
+ /**
+ * Creates a collection of table items.
+ *
+ * @param table The table of the table item.
+ * @param image The image of the item.
+ * @param s The text of the item.
+ * @return The new table item.
+ */
+ public static TableItem createTableItem(Table table, Image image, String... s)
+ {
+ TableItem toReturn = new TableItem(table, SWT.NONE);
+ toReturn.setText(s);
+ if (image != null)
+ {
+ toReturn.setImage(image);
+ }
+
+ return toReturn;
+ }
+
+ /**
+ * Creates a new text.
+ *
+ * @param parent The parent composite.
+ * @return The new text.
+ */
+ public static Text createText(Composite parent)
+ {
+ Text toReturn = new Text(parent, SWT.BORDER);
+ GridData data = new GridData(GridData.FILL_HORIZONTAL);
+ data.widthHint = IDialogConstants.ENTRY_FIELD_WIDTH;
+ toReturn.setLayoutData(data);
+
+ return toReturn;
+ }
+
+ /**
+ * Creates a new tree item. If the text parameter is null, it will appear is
+ * after the field parameter.
+ *
+ * @param itemParent The parent tree item
+ * @param image The image of the tree item.
+ * @param field The field.
+ * @param text The text of the tree item.
+ * @return The new tree item.
+ */
+ public static TreeItem createTreeItem(TreeItem itemParent, Image image, String field,
+ String text)
+ {
+ TreeItem toReturn = new TreeItem(itemParent, 0);
+ toReturn.setText((text == null) ? field : field + " (" + text + ")"); //$NON-NLS-1$ //$NON-NLS-2$
+ toReturn.setImage(image);
+ toReturn.setExpanded(true);
+
+ return toReturn;
+ }
+
+ /**
+ * Creates a new horizontal tool bar.
+ *
+ * @param parent The parent tool bar item.
+ * @return A new tool bar item.
+ */
+ public static ToolBar createHorizontalToolBar(Composite parent)
+ {
+ return new ToolBar(parent, SWT.HORIZONTAL);
+ }
+
+ /**
+ * Creates a new tool item like a push button
+ *
+ * @param toolBar The parent tool bar item.
+ * @return A new tool item.
+ */
+ public static ToolItem createPushToolItem(ToolBar toolBar)
+ {
+ return new ToolItem(toolBar, SWT.PUSH);
+ }
+
+ /**
+ * Creates a titled group.
+ *
+ * @param parent The parent composite
+ * @param title The desired title.
+ * @return The titled group
+ */
+ public static Group createTitledGroup(Composite parent, String title)
+ {
+ Group toReturn = new Group(parent, SWT.SHADOW_NONE);
+ toReturn.setText(title);
+ toReturn.setLayout(new GridLayout());
+ toReturn.setLayoutData(new GridData(GridData.FILL_BOTH));
+ return toReturn;
+ }
+
+ /**
+ * Creates a titled group with the given number of columns.
+ *
+ * @param parent The parent composite.
+ * @param title The group title.
+ * @param numColumns The number of columns.
+ * @return The created group.
+ */
+ public static Group createTitledGroup(Composite parent, String title, int numColumns)
+ {
+ Group toReturn = new Group(parent, SWT.SHADOW_NONE);
+ toReturn.setText(title);
+ GridLayout layout = createGridLayout(numColumns);
+ toReturn.setLayout(layout);
+ return toReturn;
+ }
+
+ /**
+ * Creates a group
+ *
+ * @param parent The parent composite
+ * @return The group
+ */
+ public static Group createGroup(Composite parent)
+ {
+ Group toReturn = new Group(parent, SWT.SHADOW_NONE);
+ return toReturn;
+ }
+
+ /**
+ * Creates a radio button
+ *
+ * @param parent The parent composite
+ * @param text The given text.
+ * @return The radio button
+ */
+ public static Button createRadioButton(Composite parent, String text)
+ {
+ Button toReturn = new Button(parent, SWT.RADIO);
+ toReturn.setText(text);
+ toReturn.setLayoutData(new GridData(GridData.FILL_BOTH));
+ return toReturn;
+ }
+
+ /**
+ * Creates a list.
+ *
+ * @param parent The parent composite.
+ * @return The list.
+ */
+ public static List createList(Composite parent)
+ {
+ return new List(parent, SWT.BORDER | SWT.SINGLE);
+ }
+
+}
diff --git a/src/plugins/logger.collector/src/com/motorola/studio/android/logger/collector/util/WidgetsUtil.java b/src/plugins/logger.collector/src/com/motorola/studio/android/logger/collector/util/WidgetsUtil.java
new file mode 100644
index 0000000..caa7f54
--- /dev/null
+++ b/src/plugins/logger.collector/src/com/motorola/studio/android/logger/collector/util/WidgetsUtil.java
@@ -0,0 +1,392 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.motorola.studio.android.logger.collector.util;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.jface.dialogs.Dialog;
+import org.eclipse.jface.preference.FileFieldEditor;
+import org.eclipse.jface.preference.PreferenceDialog;
+import org.eclipse.jface.preference.PreferenceManager;
+import org.eclipse.jface.preference.StringFieldEditor;
+import org.eclipse.jface.wizard.IWizard;
+import org.eclipse.jface.wizard.WizardDialog;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.Combo;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Monitor;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.swt.widgets.TableItem;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.swt.widgets.Tree;
+import org.eclipse.swt.widgets.TreeItem;
+import org.eclipse.ui.PlatformUI;
+
+/**
+ * Class with useful methods for widgets.
+ */
+public class WidgetsUtil
+{
+
+ /**
+ * This method test if a given String is null or empty.
+ *
+ * @param s The String
+ * @return <code>true</code> if the String is null or empty,
+ * <code>false</code> otherwise.
+ */
+ private static boolean isNullOrEmpty(String s)
+ {
+ return ((s != null) && s.trim().equals("")); //$NON-NLS-1$
+ }
+
+ /**
+ * The method verify if the file exist.
+ *
+ * @param fileName The full path for file.
+ * @return <code>true</code> if the file exist, <code>false</code>
+ * otherwise.
+ */
+ public static boolean fileExist(String fileName)
+ {
+ return !isNullOrEmpty(fileName) && new File(fileName).exists();
+ }
+
+ /**
+ * This method test if some StringFieldEditor value of the given collection
+ * is null or empty.
+ *
+ * @param editors
+ * @return <code>true</code> if some StringFieldEditor value is null or
+ * empty, <code>false</code> otherwise.
+ */
+ public static boolean isNullOrEmpty(StringFieldEditor... editors)
+ {
+ for (StringFieldEditor editor : editors)
+ {
+ if (isNullOrEmpty(editor))
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * This method test if a StringFieldEditor value is null or empty.
+ *
+ * @param editor The StringFieldEditor
+ * @return <code>true</code> if the StringFieldEditor value is null or
+ * empty, <code>false</code> otherwise.
+ */
+ public static boolean isNullOrEmpty(StringFieldEditor editor)
+ {
+ return ((editor != null) && isNullOrEmpty(editor.getStringValue()));
+ }
+
+ /**
+ * This method test if a StringFieldEditor value contains a invalid
+ * character.
+ *
+ * @param editor The StringFieldEditor
+ * @return <code>true</code> if the StringFieldEditor value contains invalid
+ * character, <code>false</code> otherwise.
+ */
+ public static boolean checkExistInvalidCharacter(StringFieldEditor editor, String invalidChars)
+ {
+ for (int i = 0; i < invalidChars.length(); i++)
+ {
+ String invalidChar = invalidChars.substring(i, i + 1);
+ if (editor.getStringValue().contains(invalidChar))
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * This method test if a Text value is null or empty.
+ *
+ * @param text The Text
+ * @return <code>true</code> if the Text value is null or empty,
+ * <code>false</code> otherwise.
+ */
+ public static boolean isNullOrEmpty(Text text)
+ {
+ return ((text != null) && isNullOrEmpty(text.getText()));
+ }
+
+ /**
+ * This method test if a FileFieldEditor value is null or empty.
+ *
+ * @param editor The FileFieldEditor
+ * @return <code>true</code> if the FileFieldEditor value is null or empty,
+ * <code>false</code> otherwise.
+ */
+ public static boolean isEmpty(FileFieldEditor editor)
+ {
+ return isNullOrEmpty(editor.getStringValue());
+ }
+
+ /**
+ * This method test if a Combo value is null or empty.
+ *
+ * @param combo The Combo
+ * @return <code>true</code> if the Combo value is null or not selected,
+ * <code>false</code> otherwise.
+ */
+ public static boolean isNullOrDeselected(Combo combo)
+ {
+ return ((combo != null) && (combo.getSelectionIndex() == -1));
+ }
+
+ /**
+ * Returns the size of file.
+ *
+ * @param fileName The file name.
+ * @return The size of file.
+ */
+ public static long fileSize(String fileName)
+ {
+ return new File(fileName).length();
+ }
+
+ /**
+ * This method test if a Table has one or more lines.
+ *
+ * @param table The table
+ * @return <code>true</code> if the Table has one or more lines,
+ * <code>false</code> otherwise.
+ */
+ public static boolean isNullOrEmpty(Table table)
+ {
+ return table.getItemCount() > 0;
+ }
+
+ /**
+ * Executes a wizard.
+ *
+ * @param wizard The wizard.
+ * @return <code>true</code> if the Wizard dialog has constant OK,
+ * <code>false</code> otherwise .
+ */
+ public static boolean runWizard(IWizard wizard)
+ {
+ Shell activeShell = Display.getCurrent().getActiveShell();
+ WizardDialog dialog = new WizardDialog(activeShell, wizard);
+
+ try
+ {
+ dialog.create();
+ }
+ catch (Throwable e)
+ {
+ e.printStackTrace();
+ }
+ centerDialog(dialog);
+ return dialog.open() == WizardDialog.OK;
+ }
+
+ /**
+ * Opens the Eclipse preferences dialog and selects the page of the given
+ * id.
+ *
+ * @param shell The shell.
+ * @param selectedNode The preferences page to selec.
+ * @return <code>true</code> if the Wizard dialog has constant OK,
+ * <code>false</code> otherwise .
+ */
+ public static boolean runPreferencePage(Shell shell, String selectedNode)
+ {
+ PreferenceManager manager = PlatformUI.getWorkbench().getPreferenceManager();
+ PreferenceDialog dialog = new PreferenceDialog(shell, manager);
+ dialog.setSelectedNode(selectedNode);
+ WidgetsUtil.centerDialog(shell);
+ return dialog.open() == PreferenceDialog.OK;
+ }
+
+ /**
+ * Center the dialog.
+ *
+ * @param shell The shell.
+ */
+ public static void centerDialog(Shell shell)
+ {
+ Monitor primary = shell.getDisplay().getPrimaryMonitor();
+ Rectangle bounds = primary.getBounds();
+ Rectangle rect = shell.getBounds();
+ int x = bounds.x + (bounds.width - rect.width) / 2;
+ int y = bounds.y + (bounds.height - rect.height) / 2;
+ shell.setLocation(x, y);
+ }
+
+ /**
+ * Center the dialog.
+ *
+ * @param dialog The dialog.
+ */
+ public static void centerDialog(Dialog dialog)
+ {
+ centerDialog(dialog.getShell());
+ }
+
+ /**
+ * Check the leaf items of the given tree.
+ *
+ * @param tree The tree.
+ * @return A collection containing the leaf tree items.
+ */
+ public static List<TreeItem> getCheckedLeafItems(Tree tree)
+ {
+ List<TreeItem> toReturn = new ArrayList<TreeItem>();
+ selectCheckedLeafItems(tree.getItems(), toReturn);
+ return toReturn;
+ }
+
+ /**
+ * Returns a list of the leaf nodes that are checked.
+ *
+ * @param items The parent items.
+ * @param list A list of the leaf nodes that are checked.
+ */
+ private static void selectCheckedLeafItems(TreeItem[] items, List<TreeItem> list)
+ {
+ int len = items.length;
+ for (int i = 0; i < len; i++)
+ {
+ if (items[i].getItemCount() > 0)
+ {
+ selectCheckedLeafItems(items[i].getItems(), list);
+ }
+ else if (items[i].getChecked())
+ {
+ list.add(items[i]);
+ }
+ }
+ }
+
+ /**
+ * Expand all the given tree items.
+ *
+ * @param items The tree items.
+ */
+ public static void expandAll(TreeItem[] items)
+ {
+ for (int i = 0; i < items.length; i++)
+ {
+ if (items[i].getItems().length > 0)
+ {
+ items[i].setExpanded(true);
+ expandAll(items[i].getItems());
+ }
+ }
+ }
+
+ /**
+ * Returns the full path of a given tree item.
+ *
+ * @param item The tree item.
+ * @return The full path of a given tree item.
+ */
+ public static String getFullPathTreeItem(TreeItem item)
+ {
+ String toReturn = item.getText();
+ if (item != null)
+ {
+ if (item.getParentItem() != null)
+ {
+ toReturn = getFullPathTreeItem(item.getParentItem()) + "." + toReturn; //$NON-NLS-1$
+ }
+ }
+ return toReturn;
+ }
+
+ /**
+ * This method verifies if a given file can be read.
+ *
+ * @param fileName the full file path.
+ * @return <code>true</code> if read permission is granted,
+ * <code>false</code> otherwise.
+ */
+ public static boolean canRead(String fileName)
+ {
+ return !isNullOrEmpty(fileName) && new File(fileName).canRead();
+ }
+
+ /**
+ * This method verifies if a given file has the read and write permissions
+ * granted.
+ *
+ * @param fileName The file
+ * @return <code>true</code> if permissions are granted, <code>false</code>
+ * otherwise.
+ */
+ public static boolean canReadWrite(String fileName)
+ {
+ File file = new File(fileName);
+ return file.canRead() && file.canWrite();
+ }
+
+ /**
+ * This method simulates a refresh in a Composite object.
+ *
+ * @param composite A composite object.
+ */
+ public static void refreshComposite(Composite composite)
+ {
+ for (Composite parent = composite.getParent(); parent != null; parent = parent.getParent())
+ {
+ parent.layout();
+ }
+ }
+
+ /**
+ * Check the leaf items of the given table.
+ *
+ * @param table The table.
+ * @return A collection containing the leaf table items.
+ */
+ public static List<TableItem> getCheckedLeafItems(Table table)
+ {
+ List<TableItem> toReturn = new ArrayList<TableItem>();
+ selectCheckedLeafItems(table.getItems(), toReturn);
+ return toReturn;
+ }
+
+ /**
+ * Returns a list of the leaf nodes that are checked.
+ *
+ * @param items The parent items.
+ * @param list A list of the leaf nodes that are checked.
+ */
+ private static void selectCheckedLeafItems(TableItem[] items, List<TableItem> list)
+ {
+ int len = items.length;
+ for (int i = 0; i < len; i++)
+ {
+ if (items[i].getChecked())
+ {
+ list.add(items[i]);
+ }
+ }
+ }
+}
diff --git a/src/plugins/logger.collector/src/com/motorola/studio/android/logger/collector/util/ZipUtil.java b/src/plugins/logger.collector/src/com/motorola/studio/android/logger/collector/util/ZipUtil.java
new file mode 100644
index 0000000..f2c943c
--- /dev/null
+++ b/src/plugins/logger.collector/src/com/motorola/studio/android/logger/collector/util/ZipUtil.java
@@ -0,0 +1,260 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.logger.collector.util;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.Enumeration;
+import java.util.zip.CRC32;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+import java.util.zip.ZipOutputStream;
+
+/**
+ * Class with useful method to compact (zip) files and directories.
+ */
+public class ZipUtil
+{
+
+ /**
+ * The default buffer.
+ */
+ private static final int BUFFER = 2048;
+
+ /**
+ * The output file.
+ */
+ private File outputFile = null;
+
+ /**
+ * This file represents the current directory.
+ */
+ private File directory = null;
+
+ /**
+ * A ZipOutputStream object.
+ */
+ private ZipOutputStream zos = null;
+
+ /**
+ * The path of current directory.
+ */
+ private final String currentDirectory;
+
+ /**
+ * Class constructor.
+ *
+ * @param outputFile The output to create the zip file.
+ * @param directory The directory that will be compacted.
+ * @throws IOException
+ */
+ public ZipUtil(String outputFile, String directory) throws IOException
+ {
+ this(new File(outputFile), new File(directory));
+ }
+
+ /**
+ * Class constructor.
+ *
+ * @param outputFile The file where the zip file will be created.
+ * @param directory The directory that will be compacted.
+ * @throws IOException
+ */
+ public ZipUtil(File outputFile, File directory) throws IOException
+ {
+ this.outputFile = outputFile;
+ this.directory = directory;
+ this.currentDirectory = directory.getAbsolutePath();
+ }
+
+ /**
+ * Compact the content.
+ *
+ * @throws IOException
+ */
+ public final void zip() throws IOException
+ {
+ FileOutputStream fos = null;
+ try
+ {
+ fos = new FileOutputStream(outputFile);
+ zos = new ZipOutputStream(fos);
+ zipDir(directory);
+ zos.flush();
+ }
+ finally
+ {
+ try
+ {
+ zos.close();
+ fos.close();
+ }
+ catch (IOException io)
+ {
+ io.printStackTrace();
+ }
+ }
+ }
+
+ /**
+ * Compact directory the content.
+ *
+ * @param dir The directory
+ * @throws IOException
+ */
+ private final void zipDir(File dir) throws IOException
+ {
+ if (!dir.getPath().equals(currentDirectory))
+ {
+ String entryName = dir.getPath().substring(currentDirectory.length() + 1);
+ entryName = entryName.replace('\\', '/'); //$NON-NLS-1$ //$NON-NLS-2$
+ ZipEntry ze = new ZipEntry(entryName + "/"); //$NON-NLS-1$
+ if ((dir != null) && dir.exists())
+ {
+ ze.setTime(dir.lastModified());
+ }
+ else
+ {
+ ze.setTime(System.currentTimeMillis());
+ }
+ ze.setSize(0);
+ ze.setMethod(ZipEntry.STORED);
+ ze.setCrc(new CRC32().getValue());
+ zos.putNextEntry(ze);
+ }
+
+ if (dir.exists() && dir.isDirectory())
+ {
+ File[] fileList = dir.listFiles();
+ for (int i = 0; i < fileList.length; i++)
+ {
+ if (fileList[i].isDirectory())
+ {
+ zipDir(fileList[i]);
+ }
+ if (fileList[i].isFile())
+ {
+ zipFile(fileList[i]);
+ }
+ }
+ }
+ }
+
+ /**
+ * Compact the file content.
+ *
+ * @param file The file
+ * @throws IOException
+ */
+ private void zipFile(File file) throws IOException
+ {
+ if (!file.equals(this.outputFile))
+ {
+ BufferedInputStream bis = null;
+ try
+ {
+ bis = new BufferedInputStream(new FileInputStream(file), BUFFER);
+
+ String entryName = file.getPath().substring(currentDirectory.length() + 1);
+ entryName = entryName.replace('\\', '/'); //$NON-NLS-1$ //$NON-NLS-2$
+ ZipEntry fileEntry = new ZipEntry(entryName);
+ zos.putNextEntry(fileEntry);
+
+ byte[] data = new byte[BUFFER];
+ int byteCount;
+ while ((byteCount = bis.read(data, 0, BUFFER)) != -1)
+ {
+ zos.write(data, 0, byteCount);
+ }
+
+ }
+ finally
+ {
+ bis.close();
+ }
+ }
+ }
+
+ /**
+ * Unpacks a zip file to the target directory.
+ *
+ * @param zipFilePath The zip file.
+ * @param destDirPath The destination directory.
+ * @throws IOException
+ */
+ public static void unzip(String zipFilePath, String destDirPath) throws IOException
+ {
+ try
+ {
+ File zipFile = new File(zipFilePath);
+ File folder = new File(destDirPath);
+ ZipFile zip = new ZipFile(zipFile);
+ Enumeration<? extends ZipEntry> entries = zip.entries();
+ while (entries.hasMoreElements())
+ {
+ ZipEntry entry = entries.nextElement();
+ unzipEntry(zip, entry, folder);
+ }
+ }
+ catch (Exception e)
+ {
+ throw new RuntimeException("Error while trying to unzip jar file"); //$NON-NLS-1$
+ }
+ }
+
+ /**
+ * Validate and write the stream of file.
+ *
+ * @param zipFile The zip file
+ * @param zipEntry The zip entry
+ * @param folder The folder
+ * @throws IOException
+ */
+ private static void unzipEntry(ZipFile zipFile, ZipEntry zipEntry, File folder)
+ throws IOException
+ {
+ if (zipEntry.isDirectory())
+ {
+ com.motorola.studio.android.common.utilities.FileUtil.mkdir(new File(folder, zipEntry
+ .getName()).getPath());
+ }
+ else
+ {
+ File outputFile = new File(folder, zipEntry.getName());
+ if (!outputFile.getParentFile().exists())
+ {
+ com.motorola.studio.android.common.utilities.FileUtil.mkdir(outputFile
+ .getParentFile().getPath());
+ }
+ BufferedInputStream is = new BufferedInputStream(zipFile.getInputStream(zipEntry));
+ BufferedOutputStream os = new BufferedOutputStream(new FileOutputStream(outputFile));
+ try
+ {
+ com.motorola.studio.android.common.utilities.FileUtil.copy(
+ new File(zipFile.getName()), outputFile);
+ }
+ finally
+ {
+ os.close();
+ is.close();
+ }
+ }
+ }
+}
diff --git a/src/plugins/logger.collector/src/loggerCollector.properties b/src/plugins/logger.collector/src/loggerCollector.properties
new file mode 100644
index 0000000..5f4a739
--- /dev/null
+++ b/src/plugins/logger.collector/src/loggerCollector.properties
@@ -0,0 +1,31 @@
+#---------------------------------------------------------------------------------
+#com.motorola.studio.platform.logger.collector.ui.wizard.LoggerCollectorWizardPage
+#---------------------------------------------------------------------------------
+logger.collector.wizard.page.title=Collect Log Files
+logger.collector.wizard.page.description=Collect log files for the MOTODEV Studio platform.
+logger.collector.wizard.page.directory=Output directory
+logger.collector.wizard.page.file=Output file
+logger.collector.wizard.page.selectAll=Select All
+logger.collector.wizard.page.unselectAll = Unselect All
+
+error.logger.collector.directory.empty=Output folder is empty
+error.logger.collector.directory.not.found=Output directory does not exist.
+error.logger.collector.directory.access.denied=Directory write permission denied.
+error.logger.collector.log.not.selected=No log file selected.
+error.logger.collector.log.not.found=A log file does not exist
+
+#---------------------------------------------------------------------------------
+#com.motorola.studio.platform.logger.collector.ui.wizard.LoggerCollectorWizard
+#---------------------------------------------------------------------------------
+logger.collector.wizard.success=File(s) successfully compacted.
+
+#---------------------------------------------------------------------------------
+#com.motorola.studio.platform.logger.collector.ui.LogFileColumn
+#---------------------------------------------------------------------------------
+logger.collector.tableview.root=Log files
+error.logger.collector.mount.tableview=Log files could not be retrieved.
+
+#---------------------------------------------------------------------------------
+#com.motorola.studio.platform.logger.collector.core.internal.CollectLogFile
+#---------------------------------------------------------------------------------
+error.logger.collector.zip=Cannot create zip file. \ No newline at end of file
diff --git a/src/plugins/logger/.classpath b/src/plugins/logger/.classpath
new file mode 100644
index 0000000..00572a6
--- /dev/null
+++ b/src/plugins/logger/.classpath
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>
+ <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry exported="true" kind="lib" path="lib/log4j-1.2.14.jar" sourcepath="D:/logging-log4j-1.2.14/src"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/src/plugins/logger/.project b/src/plugins/logger/.project
new file mode 100644
index 0000000..45e1ff6
--- /dev/null
+++ b/src/plugins/logger/.project
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>com.motorolamobility.studio.android.logger</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.ManifestBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.SchemaBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.pde.PluginNature</nature>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+</projectDescription>
diff --git a/src/plugins/logger/META-INF/MANIFEST.MF b/src/plugins/logger/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..114cf4f
--- /dev/null
+++ b/src/plugins/logger/META-INF/MANIFEST.MF
@@ -0,0 +1,13 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: MOTODEV Studio Android Logger
+Bundle-SymbolicName: com.motorolamobility.studio.android.logger;singleton:=true
+Bundle-Version: 5.0.0.qualifier
+Bundle-Activator: com.motorola.studio.android.logger.internal.Activator
+Bundle-Vendor: Motorola Mobility, Inc.
+Export-Package: com.motorola.studio.android.logger
+Bundle-ClassPath: lib/log4j-1.2.14.jar,
+ .
+Require-Bundle: org.eclipse.core.runtime
+Bundle-RequiredExecutionEnvironment: JavaSE-1.6
+Bundle-ActivationPolicy: lazy
diff --git a/src/plugins/logger/build.properties b/src/plugins/logger/build.properties
new file mode 100644
index 0000000..16e4140
--- /dev/null
+++ b/src/plugins/logger/build.properties
@@ -0,0 +1,9 @@
+source.. = src/
+output.. = bin/
+bin.includes = META-INF/,\
+ lib/,\
+ .,\
+ logger.properties,\
+ plugin.xml,\
+ schema/
+
diff --git a/src/plugins/logger/lib/log4j-1.2.14.jar b/src/plugins/logger/lib/log4j-1.2.14.jar
new file mode 100644
index 0000000..6251307
--- /dev/null
+++ b/src/plugins/logger/lib/log4j-1.2.14.jar
Binary files differ
diff --git a/src/plugins/logger/logger.properties b/src/plugins/logger/logger.properties
new file mode 100644
index 0000000..826a53f
--- /dev/null
+++ b/src/plugins/logger/logger.properties
@@ -0,0 +1,22 @@
+# SETS THE PLATFORM DEFAULT LOGGER CONFIGURATION
+log4j.rootLogger=ALL, default
+# LOGGING ON A FILE AT log4j.appender.default.File
+log4j.appender.default=org.apache.log4j.RollingFileAppender
+log4j.appender.default.File=studio.log
+# MAXIMUM SIZE OF THE LOG FILE
+log4j.appender.default.MaxFileSize=2048KB
+# LAYOUT BEING USED BY THE LOGGER
+log4j.appender.default.layout=org.apache.log4j.PatternLayout
+log4j.appender.default.layout.ConversionPattern=%d [%t] %-5p %c - %m%n
+
+# SETS THE PLATFORM ENVIRONMENT LOGGER CONFIGURATION
+log4j.logger.com.motorola.studio.environment=INFO, envconf
+log4j.additivity.com.motorola.studio.environment=false
+# LOGGING ON A FILE AT log4j.appender.default.File
+log4j.appender.envconf=org.apache.log4j.RollingFileAppender
+log4j.appender.envconf.File=environment.log
+# MAXIMUM SIZE OF THE LOG FILE
+log4j.appender.envconf.MaxFileSize=2048KB
+# LAYOUT BEING USED BY THE LOGGER
+log4j.appender.envconf.layout=org.apache.log4j.PatternLayout
+log4j.appender.envconf.layout.ConversionPattern=%d - %m%n \ No newline at end of file
diff --git a/src/plugins/logger/plugin.xml b/src/plugins/logger/plugin.xml
new file mode 100644
index 0000000..b44ce50
--- /dev/null
+++ b/src/plugins/logger/plugin.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?eclipse version="3.2"?>
+<plugin>
+ <extension-point id="configuration" name="Logger Configuration" schema="schema/configuration.exsd"/>
+
+</plugin>
diff --git a/src/plugins/logger/schema/configuration.exsd b/src/plugins/logger/schema/configuration.exsd
new file mode 100644
index 0000000..93c288f
--- /dev/null
+++ b/src/plugins/logger/schema/configuration.exsd
@@ -0,0 +1,102 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<!-- Schema file written by PDE -->
+<schema targetNamespace="com.motorolamobility.studio.android.logger" xmlns="http://www.w3.org/2001/XMLSchema">
+<annotation>
+ <appInfo>
+ <meta.schema plugin="com.motorolamobility.studio.android.logger" id="configuration" name="Logger Configuration"/>
+ </appInfo>
+ <documentation>
+ &lt;P&gt;
+This extension point is used to create a custom logger configuration. Components may wish to have custom configuration for it`s logger, by extending the Logger extension point the developer can customize it`s logger in a properties file so the logger can have the desired behaviour.
+&lt;P&gt;
+ </documentation>
+ </annotation>
+
+ <element name="extension">
+ <annotation>
+ <appInfo>
+ <meta.element />
+ </appInfo>
+ </annotation>
+ <complexType>
+ <sequence>
+ <element ref="configuration"/>
+ </sequence>
+ <attribute name="point" type="string" use="required">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ </annotation>
+ </attribute>
+ <attribute name="id" type="string">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ </annotation>
+ </attribute>
+ <attribute name="name" type="string">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ <appInfo>
+ <meta.attribute translatable="true"/>
+ </appInfo>
+ </annotation>
+ </attribute>
+ </complexType>
+ </element>
+
+ <element name="configuration">
+ <complexType>
+ <attribute name="file" type="string" use="required">
+ <annotation>
+ <documentation>
+ The logger configuration properties file path, relative to the plugins jar file or folder.
+ </documentation>
+ </annotation>
+ </attribute>
+ </complexType>
+ </element>
+
+ <annotation>
+ <appInfo>
+ <meta.section type="since"/>
+ </appInfo>
+ <documentation>
+ 1.0.0
+ </documentation>
+ </annotation>
+
+ <annotation>
+ <appInfo>
+ <meta.section type="examples"/>
+ </appInfo>
+ <documentation>
+ The following is an example
+of the Logger configuration extension point usage:
+&lt;p&gt;
+&lt;pre&gt;
+ &lt;extension
+ point=&quot;com.motorola.studio.android.logger.configuration&quot;&gt;
+ &lt;configuration file=&quot;customConfiguration.properties&quot;/&gt;
+ &lt;/extension&gt;
+&lt;/pre&gt;
+&lt;/p&gt;
+ </documentation>
+ </annotation>
+
+
+
+ <annotation>
+ <appInfo>
+ <meta.section type="copyright"/>
+ </appInfo>
+ <documentation>
+ Copyright (C) 2012 The Android Open Source Project
+ </documentation>
+ </annotation>
+
+</schema>
diff --git a/src/plugins/logger/src/com/motorola/studio/android/logger/Level.java b/src/plugins/logger/src/com/motorola/studio/android/logger/Level.java
new file mode 100644
index 0000000..20efd56
--- /dev/null
+++ b/src/plugins/logger/src/com/motorola/studio/android/logger/Level.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.logger;
+
+/**
+ * Level defines standard logging levels.</p> The standard levels are <b> DEBUG
+ * < INFO < WARN < ERROR < FATAL </b>.
+ * <p>
+ * <b>Note:</b><br>
+ * A log operation of level x in a logger with level y, is enabled if and only
+ * if x >= y.
+ * <p>
+ * <p>
+ * <b>Example:</b><br>
+ * If the level is set to <b>ERROR</b> only messages with level of <b>ERROR</b>
+ * and <b>FATAL</b> will be logged.
+ */
+public final class Level
+{
+
+ // Constants ---------------------------------------
+ /**
+ * Disables all logging levels from being logged. After setting Level to
+ * OFF, no messages will be recorded in log file.
+ */
+ public static final int OFF = Integer.MAX_VALUE;
+
+ /**
+ * The FATAL level is used for severe error events. In case of FATAL, the
+ * application could be aborted.
+ */
+ public static final int FATAL = org.apache.log4j.Level.FATAL_INT;
+
+ /**
+ * The ERROR level is used by errors events. Less severe than FATAL, used
+ * for situations of error that will not crash the application.
+ */
+ public static final int ERROR = org.apache.log4j.Level.ERROR_INT;
+
+ /**
+ * The WARN level is used for potentially harmful situations. Used for
+ * situations that can generate an error.
+ */
+ public static final int WARN = org.apache.log4j.Level.WARN_INT;
+
+ /**
+ * The INFO level is used for informational messages. Informational messages
+ * are used to notify the progress of the application or relevant messages
+ * to be analyzed, like the tracing of the application execution.
+ */
+ public static final int INFO = org.apache.log4j.Level.INFO_INT;
+
+ /**
+ * The DEBUG level is used for relevant informations on an application, like
+ * variable values.
+ */
+ public static final int DEBUG = org.apache.log4j.Level.DEBUG_INT;
+
+ /**
+ * Enables all logging levels. After setting Level to ALL, all the messages
+ * will be recorded in log file.
+ */
+ public static final int ALL = Integer.MIN_VALUE;
+}
diff --git a/src/plugins/logger/src/com/motorola/studio/android/logger/Logger.java b/src/plugins/logger/src/com/motorola/studio/android/logger/Logger.java
new file mode 100644
index 0000000..3b07641
--- /dev/null
+++ b/src/plugins/logger/src/com/motorola/studio/android/logger/Logger.java
@@ -0,0 +1,289 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.logger;
+
+import java.util.HashMap;
+
+import org.apache.log4j.Level;
+
+import com.motorola.studio.android.logger.internal.EclipseEnvironmentManager;
+import com.motorola.studio.android.logger.internal.EnvironmentManager;
+
+/**
+ * Logger provides a logging facility to the <b>MOTODEV Studio</b> based
+ * applications such as Studios and RCP applications. </p> <b>Note:</b> The logs
+ * will be saved on the plug-in state area for this plug-in.
+ */
+public class Logger
+{
+
+ // Class Constants --------------------------------------------------------
+
+ /**
+ * This static block loads the log4j configuration file and resolves the
+ * path to save the logger log file.
+ */
+ static
+ {
+ /* Logging Environment Information */
+ EnvironmentManager envManager = new EclipseEnvironmentManager();
+ envManager.logEnvironment();
+ }
+
+ // Private Variables
+ // --------------------------------------------------------
+
+ /*
+ * This Map pools the Logger instances.
+ */
+ private static HashMap<String, Logger> pool;
+
+ /*
+ * Log4j logger instance being wrapped.
+ */
+ private final org.apache.log4j.Logger logger;
+
+ /**
+ * Builds a logger associated to the specified name.
+ *
+ * @param name The name of the logger.
+ */
+ private Logger(String name)
+ {
+ logger = org.apache.log4j.Logger.getLogger(name);
+ }
+
+ /**
+ * Gets a logger named according to the value of the name parameter. If the
+ * named logger already exists, then the existing instance will be returned.
+ * Otherwise, a new instance is created.
+ *
+ * @param name The name of the logger.
+ * @return the logger associated to the specified name.
+ */
+ public static synchronized Logger getLogger(String name)
+ {
+ Logger logger = null;
+ if (pool == null)
+ {
+ pool = new HashMap<String, Logger>();
+ }
+
+ if (pool.containsKey(name))
+ {
+ logger = pool.get(name);
+ }
+ else
+ {
+ logger = new Logger(name);
+ pool.put(name, logger);
+ }
+ return logger;
+ }
+
+ /**
+ * Logs the specified message with the specified level.
+ *
+ * @param level The log level.
+ * @param message The message to log.
+ */
+ public void log(int level, String message)
+ {
+ boolean key = true;
+ if ((level == com.motorola.studio.android.logger.Level.OFF)
+ || (level == com.motorola.studio.android.logger.Level.ALL))
+ {
+ key = false;
+ }
+
+ if (key)
+ {
+ logger.log(Level.toLevel(level), message);
+ }
+ }
+
+ /**
+ * Logs the specified message and an exception stack trace.
+ *
+ * @param level The log level.
+ * @param message The message to log.
+ * @param exception Exception whose stack will be logged.
+ */
+ public void log(int level, String message, Throwable exception)
+ {
+ boolean key = true;
+ if ((level == com.motorola.studio.android.logger.Level.OFF)
+ || (level == com.motorola.studio.android.logger.Level.ALL))
+ {
+ key = false;
+ }
+ if (key)
+ {
+ logger.log(Level.toLevel(level), message, exception);
+ }
+ }
+
+ /**
+ * Log a message object with the DEBUG level.
+ *
+ * @param message The message to log.
+ */
+ public void debug(String message)
+ {
+ debug(message, null);
+ }
+
+ /**
+ * Log a message object with the DEBUG level.
+ *
+ * @param message The message to log.
+ * @param exception Exception whose stack will be logged.
+ */
+ public void debug(String message, Throwable exception)
+ {
+ if (null == exception)
+ {
+ logger.debug(message);
+ }
+ else
+ {
+ logger.debug(message, exception);
+ }
+ }
+
+ /**
+ * Log a message object with the INFO level.
+ *
+ * @param message The message to log.
+ */
+ public void info(String message)
+ {
+ info(message, null);
+ }
+
+ /**
+ * Log a message object with the INFO level.
+ *
+ * @param message The message to log.
+ * @param exception Exception whose stack will be logged.
+ */
+ public void info(String message, Throwable exception)
+ {
+ if (null == exception)
+ {
+ logger.info(message);
+ }
+ else
+ {
+ logger.info(message, exception);
+ }
+ }
+
+ /**
+ * Log a message object with the WARN level.
+ *
+ * @param message The message to log.
+ */
+ public void warn(String message)
+ {
+ warn(message, null);
+ }
+
+ /**
+ * Log a message object with the WARN level.
+ *
+ * @param message The message to log.
+ * @param exception Exception whose stack will be logged.
+ */
+ public void warn(String message, Throwable exception)
+ {
+ if (null == exception)
+ {
+ logger.warn(message);
+ }
+ else
+ {
+ logger.warn(message, exception);
+ }
+ }
+
+ /**
+ * Log a message object with the ERROR level.
+ *
+ * @param message The message to log.
+ */
+ public void error(String message)
+ {
+ error(message, null);
+ }
+
+ /**
+ * Log a message object with the ERROR level.
+ *
+ * @param message The message to log.
+ * @param exception Exception whose stack will be logged.
+ */
+ public void error(String message, Throwable exception)
+ {
+ if (null == exception)
+ {
+ logger.error(message);
+ }
+ else
+ {
+ logger.error(message, exception);
+ }
+ }
+
+ /**
+ * Log a message object with the FATAL level.
+ *
+ * @param message The message to log.
+ */
+ public void fatal(String message)
+ {
+ fatal(message, null);
+ }
+
+ /**
+ * Log a message object with the FATAL level.
+ *
+ * @param message The message to log.
+ * @param exception Exception whose stack will be logged.
+ */
+ public void fatal(String message, Throwable exception)
+ {
+ if (null == exception)
+ {
+ logger.fatal(message);
+ }
+ else
+ {
+ logger.fatal(message, exception);
+ }
+ }
+
+ /**
+ * Sets the Logger level to the specified value.
+ *
+ * @param level One of the com.motorola.studio.platform.logger.Level
+ * constants.
+ */
+ public void setLevel(int level)
+ {
+ logger.setLevel(Level.toLevel(level));
+ }
+}
diff --git a/src/plugins/logger/src/com/motorola/studio/android/logger/internal/Activator.java b/src/plugins/logger/src/com/motorola/studio/android/logger/internal/Activator.java
new file mode 100644
index 0000000..6daa641
--- /dev/null
+++ b/src/plugins/logger/src/com/motorola/studio/android/logger/internal/Activator.java
@@ -0,0 +1,64 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.logger.internal;
+
+import org.eclipse.core.runtime.Plugin;
+import org.osgi.framework.BundleContext;
+
+/**
+ * The activator class controls the plug-in life cycle
+ */
+public class Activator extends Plugin
+{
+
+ // The plug-in ID
+ public static final String PLUGIN_ID = "com.motorolamobility.studio.android.logger"; //$NON-NLS-1$
+
+ // The shared instance
+ private static Activator plugin;
+
+ /**
+ * The constructor
+ */
+ public Activator()
+ {
+ plugin = this;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see
+ * org.eclipse.ui.plugin.AbstractUIPlugin#stop(org.osgi.framework.BundleContext
+ * )
+ */
+ @Override
+ public void stop(BundleContext context) throws Exception
+ {
+ plugin = null;
+ super.stop(context);
+ }
+
+ /**
+ * Returns the shared instance
+ *
+ * @return the shared instance
+ */
+ public static Activator getDefault()
+ {
+ return plugin;
+ }
+
+}
diff --git a/src/plugins/logger/src/com/motorola/studio/android/logger/internal/EclipseEnvironmentManager.java b/src/plugins/logger/src/com/motorola/studio/android/logger/internal/EclipseEnvironmentManager.java
new file mode 100644
index 0000000..c8f72d2
--- /dev/null
+++ b/src/plugins/logger/src/com/motorola/studio/android/logger/internal/EclipseEnvironmentManager.java
@@ -0,0 +1,328 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.logger.internal;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.Dictionary;
+import java.util.Enumeration;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+
+import org.apache.log4j.PropertyConfigurator;
+import org.eclipse.core.runtime.FileLocator;
+import org.eclipse.core.runtime.IBundleGroup;
+import org.eclipse.core.runtime.IBundleGroupProvider;
+import org.eclipse.core.runtime.IConfigurationElement;
+import org.eclipse.core.runtime.IExtension;
+import org.eclipse.core.runtime.IExtensionPoint;
+import org.eclipse.core.runtime.IExtensionRegistry;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.core.runtime.Status;
+import org.osgi.framework.Bundle;
+
+import com.motorola.studio.android.logger.Logger;
+
+/**
+ * EclipseEnvironmentManager logs the Eclipse environment information such as
+ * all plug-ins available on runtime. It logs the Virtual Machine information as
+ * well by calling the VMEnvironmentManager class method.
+ */
+public class EclipseEnvironmentManager implements EnvironmentManager
+{
+
+ /**
+ * The configuration element
+ */
+ private static final String CONFIGURATION = "configuration"; //$NON-NLS-1$
+
+ /**
+ * Log file name.
+ */
+ public static final String PROPERTY = "logger.properties"; //$NON-NLS-1$
+
+ /**
+ * Platform Logger appender.
+ */
+ public static final String DEFLOG_FILE = "log4j.appender.default.File"; //$NON-NLS-1$
+
+ /**
+ * Platform Environment Logger appender.
+ */
+ public static final String ENVLOG_FILE = "log4j.appender.envconf.File"; //$NON-NLS-1$
+
+ /**
+ * Logs the environment
+ */
+ public void logEnvironment()
+ {
+ IBundleGroup[] bundleGroup;
+ Bundle[] bundles;
+ Bundle bundle;
+
+ /* Map with all extensions configurations. */
+ Map<String, String> log4jPropertiesMap = new LinkedHashMap<String, String>();
+ Properties log4jProperties = new Properties();
+
+ try
+ {
+ /* Reads configuration extension point extensions */
+ this.getExtensionsConfiguration(log4jPropertiesMap);
+ this.getPlatformConfiguration(log4jPropertiesMap);
+ }
+ catch (IOException e)
+ {
+ throw new RuntimeException(e);
+ }
+
+ /* Builds a properties with all configurations */
+ Set<String> keys = log4jPropertiesMap.keySet();
+ for (String key : keys)
+ {
+ log4jProperties.setProperty(key, log4jPropertiesMap.get(key));
+ }
+ PropertyConfigurator.configure(log4jProperties);
+
+ /* Logs the Environment */
+ new VMEnvironmentManager().logEnvironment();
+
+ Logger envLogger = Logger.getLogger("com.motorola.studio.environment"); //$NON-NLS-1$
+ IBundleGroupProvider[] registry = Platform.getBundleGroupProviders();
+ envLogger.info("--------------------------------------"); //$NON-NLS-1$
+ envLogger.info("## Eclipse Plug-ins Log Information ##"); //$NON-NLS-1$
+ envLogger.info("--------------------------------------"); //$NON-NLS-1$
+ for (int i = 0; i < registry.length; i++)
+ {
+ bundleGroup = registry[i].getBundleGroups();
+ for (int j = 0; j < bundleGroup.length; j++)
+ {
+ bundles = bundleGroup[j].getBundles();
+ for (int k = 0; k < bundles.length; k++)
+ {
+ bundle = bundles[k];
+ Dictionary<String, String> values = bundle.getHeaders();
+ envLogger.info(bundle.getSymbolicName() + " - " + //$NON-NLS-1$
+ values.get("Bundle-Version")); //$NON-NLS-1$
+ }
+ }
+ }
+ }
+
+ /**
+ * Getter of the workspace path
+ *
+ * @return The workspace path
+ */
+ private File getLogsFolder()
+ {
+ IPath workspacePath = Activator.getDefault().getStateLocation();
+ File file = null;
+ if (workspacePath != null)
+ {
+ file = workspacePath.toFile();
+ }
+ return file;
+ }
+
+ /**
+ * Loads the Platform configuration.
+ *
+ * @param log4jPropertiesMap Map to append properties keys and values.
+ * @throws IOException if any IO error occurs.
+ */
+ private void getPlatformConfiguration(Map<String, String> log4jPropertiesMap)
+ throws IOException
+ {
+ /* Reads the default configuration. */
+ Activator activator = Activator.getDefault();
+ if (activator != null)
+ {
+ URL bundleUrl = activator.getBundle().getEntry(PROPERTY);
+ if (bundleUrl != null)
+ {
+ Properties props = new Properties();
+ props.load(bundleUrl.openStream());
+
+ File logs = this.getLogsFolder();
+ if (!logs.exists())
+ {
+ throw new RuntimeException("State folder does not exist."); //$NON-NLS-1$
+ }
+
+ String logFileName = props.getProperty(DEFLOG_FILE);
+ String envFileName = props.getProperty(ENVLOG_FILE);
+ if ((logFileName != null) && (envFileName != null))
+ {
+ File logFile = new File(logs.getAbsolutePath() + File.separator + logFileName);
+ File envFile = new File(logs.getAbsolutePath() + File.separator + envFileName);
+ props.setProperty(DEFLOG_FILE, logFile.getAbsolutePath());
+ props.setProperty(ENVLOG_FILE, envFile.getAbsolutePath());
+
+ Enumeration<Object> keys = props.keys();
+ while (keys.hasMoreElements())
+ {
+ String key = (String) keys.nextElement();
+ String value = props.getProperty(key);
+ if (value != null)
+ {
+ log4jPropertiesMap.put(key, value);
+ }
+ }
+ }
+ else
+ {
+ throw new RuntimeException("Logger property file is corrupted."); //$NON-NLS-1$
+ }
+ }
+ else
+ {
+ throw new RuntimeException("Could not get logger.properties URL."); //$NON-NLS-1$
+ }
+ }
+ else
+ {
+ throw new RuntimeException(
+ "Could not get com.motorola.studio.platform.logger activator."); //$NON-NLS-1$
+ }
+ }
+
+ /**
+ * Loads all configuration extension point extensions to read properties
+ * files.
+ *
+ * @param log4jPropertiesMap Map to append properties keys and values.
+ */
+ private void getExtensionsConfiguration(Map<String, String> log4jPropertiesMap)
+ {
+ IExtensionRegistry registry = Platform.getExtensionRegistry();
+ IExtensionPoint extPoint = registry.getExtensionPoint(Activator.PLUGIN_ID, CONFIGURATION);
+ IExtension[] exts = extPoint.getExtensions();
+
+ for (int i = 0; i < exts.length; i++)
+ {
+ IExtension ext = exts[i];
+ if (ext != null)
+ {
+ IConfigurationElement[] configuration = ext.getConfigurationElements();
+ for (int j = 0; j < configuration.length; j++)
+ {
+ IConfigurationElement config = configuration[j];
+ if ((config != null) && config.getName().equals(CONFIGURATION))
+ {
+ String file = config.getAttribute("file"); //$NON-NLS-1$
+ if ((file != null) && (file.length() > 0))
+ {
+ Bundle plugin = Platform.getBundle(ext.getNamespaceIdentifier());
+ if (plugin != null)
+ {
+ URL url = plugin.getEntry(file);
+ if (url != null)
+ {
+ InputStream stream = null;
+ Properties props = null;
+ try
+ {
+ stream = FileLocator.toFileURL(url).openStream();
+ props = new Properties();
+ props.load(stream);
+ }
+ catch (IOException e)
+ {
+ /*
+ * In case the custom configuration file
+ * can not be read the default will be
+ * used.
+ */
+ Activator
+ .getDefault()
+ .getLog()
+ .log(new Status(
+ IStatus.WARNING,
+ ext.getNamespaceIdentifier(),
+ 0,
+ ext.getNamespaceIdentifier()
+ + " can not be loaded, using default configuration.", e)); //$NON-NLS-1$
+ }
+ finally
+ {
+ try
+ {
+ stream.close();
+ }
+ catch (IOException e)
+ {
+ e.printStackTrace();
+ }
+ }
+
+ if (props != null)
+ {
+ Enumeration<Object> keys = props.keys();
+ while (keys.hasMoreElements())
+ {
+ String key = (String) keys.nextElement();
+ String value = props.getProperty(key);
+ if (key.startsWith("log4j.appender.") && key.endsWith(".File")) { //$NON-NLS-1$ //$NON-NLS-2$
+ File logs = this.getLogsFolder();
+ if (logs != null)
+ {
+ value =
+ logs.getAbsolutePath() + File.separator
+ + value;
+ File customLogFile = new File(value);
+ if (customLogFile.exists())
+ {
+ customLogFile.delete();
+ }
+ }
+ }
+ if (value != null)
+ {
+ log4jPropertiesMap.put(key, value);
+ }
+ }
+ }
+
+ }
+ else
+ {
+ Activator
+ .getDefault()
+ .getLog()
+ .log(new Status(
+ IStatus.WARNING,
+ ext.getNamespaceIdentifier(),
+ 0,
+ "Could not load " + //$NON-NLS-1$
+ file
+ + " from " + ext.getNamespaceIdentifier() + //$NON-NLS-1$
+ " plugin.", //$NON-NLS-1$
+ null));
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/src/plugins/logger/src/com/motorola/studio/android/logger/internal/EnvironmentManager.java b/src/plugins/logger/src/com/motorola/studio/android/logger/internal/EnvironmentManager.java
new file mode 100644
index 0000000..cff447e
--- /dev/null
+++ b/src/plugins/logger/src/com/motorola/studio/android/logger/internal/EnvironmentManager.java
@@ -0,0 +1,28 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.logger.internal;
+
+/**
+ * This interface provides a way to log the machine environment.
+ */
+public interface EnvironmentManager
+{
+
+ /**
+ * Logs the environment.
+ */
+ public void logEnvironment();
+}
diff --git a/src/plugins/logger/src/com/motorola/studio/android/logger/internal/VMEnvironmentManager.java b/src/plugins/logger/src/com/motorola/studio/android/logger/internal/VMEnvironmentManager.java
new file mode 100644
index 0000000..41da3dc
--- /dev/null
+++ b/src/plugins/logger/src/com/motorola/studio/android/logger/internal/VMEnvironmentManager.java
@@ -0,0 +1,60 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.logger.internal;
+
+import com.motorola.studio.android.logger.Logger;
+
+/**
+ * This class logs the Environment according to VM information.
+ */
+public class VMEnvironmentManager implements EnvironmentManager
+{
+
+ // Constants --------------------------------------------
+
+ private static final String[] property =
+ {
+ "os.name", //$NON-NLS-1$
+ "os.arch", //$NON-NLS-1$
+ "os.version", //$NON-NLS-1$
+ "java.version", //$NON-NLS-1$
+ "java.vendor", //$NON-NLS-1$
+ "java.vendor.url", //$NON-NLS-1$
+ "java.home", //$NON-NLS-1$
+ "java.vm.specification.name", //$NON-NLS-1$
+ "java.vm.specification.vendor", //$NON-NLS-1$
+ "java.vm.specification.version", //$NON-NLS-1$
+ "java.class.path", //$NON-NLS-1$
+ "java.library.path" //$NON-NLS-1$
+ };
+
+ /**
+ * Logs the environment based on the System VM properties.
+ */
+ public void logEnvironment()
+ {
+ Logger envLogger = Logger.getLogger("com.motorola.studio.environment"); //$NON-NLS-1$
+ /*Navigates looking for the neccessary information.*/
+ for (int i = 0; i < property.length; i++)
+ {
+ String value = System.getProperty(property[i]);
+ if (value != null)
+ {
+ envLogger.info(property[i] + " - " + value); //$NON-NLS-1$
+ }
+ }
+ }
+}
diff --git a/src/plugins/mat/.classpath b/src/plugins/mat/.classpath
new file mode 100644
index 0000000..8a8f166
--- /dev/null
+++ b/src/plugins/mat/.classpath
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>
+ <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/src/plugins/mat/.project b/src/plugins/mat/.project
new file mode 100644
index 0000000..ec2f24f
--- /dev/null
+++ b/src/plugins/mat/.project
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>com.motorola.studio.android.mat</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.ManifestBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.SchemaBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.pde.PluginNature</nature>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+</projectDescription>
diff --git a/src/plugins/mat/META-INF/MANIFEST.MF b/src/plugins/mat/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..784fd11
--- /dev/null
+++ b/src/plugins/mat/META-INF/MANIFEST.MF
@@ -0,0 +1,21 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: %pluginName
+Bundle-SymbolicName: com.motorola.studio.android.mat;singleton:=true
+Bundle-Version: 5.0.0.qualifier
+Bundle-Activator: com.motorola.studio.android.mat.Activator
+Bundle-Vendor: %providerName
+Require-Bundle: org.eclipse.ui,
+ org.eclipse.core.runtime,
+ org.eclipse.mat.ui,
+ org.eclipse.sequoyah.device.framework,
+ org.eclipse.sequoyah.device.common.utilities,
+ org.eclipse.core.resources,
+ com.motorola.studio.android,
+ com.motorola.studio.android.emulator
+Bundle-ActivationPolicy: lazy
+Bundle-RequiredExecutionEnvironment: JavaSE-1.6
+Bundle-Localization: plugin
+Export-Package: com.motorola.studio.android.mat.i18n
+Import-Package: com.motorola.studio.android.common.log
+
diff --git a/src/plugins/mat/build.properties b/src/plugins/mat/build.properties
new file mode 100644
index 0000000..55edf95
--- /dev/null
+++ b/src/plugins/mat/build.properties
@@ -0,0 +1,7 @@
+source.. = src/
+output.. = bin/
+bin.includes = META-INF/,\
+ .,\
+ plugin.xml,\
+ icons/,\
+ plugin.properties
diff --git a/src/plugins/mat/icons/android_oql.png b/src/plugins/mat/icons/android_oql.png
new file mode 100644
index 0000000..d21b7d2
--- /dev/null
+++ b/src/plugins/mat/icons/android_oql.png
Binary files differ
diff --git a/src/plugins/mat/icons/motodev_pane.png b/src/plugins/mat/icons/motodev_pane.png
new file mode 100644
index 0000000..49b95f8
--- /dev/null
+++ b/src/plugins/mat/icons/motodev_pane.png
Binary files differ
diff --git a/src/plugins/mat/plugin.properties b/src/plugins/mat/plugin.properties
new file mode 100644
index 0000000..c6a5696
--- /dev/null
+++ b/src/plugins/mat/plugin.properties
@@ -0,0 +1,13 @@
+#################################################################################
+#
+# MOTODEV Studio for Android MAT Plug-in properties
+#
+#################################################################################
+
+pluginName=MOTODEV Studio for Android MAT Contribution
+providerName=Motorola Mobility, Inc.
+copyright=Copyright (C) 2012 The Android Open Source Project
+
+service_dumpHPROF_description=Generate an HPROF file based on the selected Android process
+service_dumpHPROF_name=Analyze Memory with MAT
+command_dumpHPROF_name=Analyze Memory with MAT \ No newline at end of file
diff --git a/src/plugins/mat/plugin.xml b/src/plugins/mat/plugin.xml
new file mode 100644
index 0000000..ab6e130
--- /dev/null
+++ b/src/plugins/mat/plugin.xml
@@ -0,0 +1,99 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?eclipse version="3.4"?>
+<plugin>
+ <extension
+ point="org.eclipse.mat.ui.editorPanes">
+ <pane
+ class="com.motorola.studio.android.mat.panes.MotodevPane"
+ id="com.motorola.studio.android.mat.MotodevPane">
+ </pane>
+ </extension>
+ <extension
+ point="org.eclipse.mat.ui.editorContributions">
+ <contribution
+ class="com.motorola.studio.android.mat.actions.HeapEditorContributions"
+ editorClass="org.eclipse.mat.ui.snapshot.editor.HeapEditor"
+ sequenceNr="2">
+ </contribution>
+ </extension>
+ <extension
+ id="dumpHprofService"
+ name="Dump HPROF"
+ point="org.eclipse.sequoyah.device.framework.service">
+ <service
+ copyright="%copyright"
+ description="%service_dumpHPROF_description"
+ handler="com.motorola.studio.android.mat.services.DumpHPROFHandler"
+ icon="icons/motodev_pane.png"
+ id="com.motorola.studio.android.mat.dumpHprofService"
+ name="%service_dumpHPROF_name"
+ provider="%providerName"
+ version="0.1.0"
+ visible="true">
+ </service>
+ </extension>
+ <extension
+ id="com.motorola.studio.android.emulator.androidDevice"
+ name="Dump HPROF file"
+ point="org.eclipse.sequoyah.device.framework.serviceDefinition">
+ <service
+ id="com.motorola.studio.android.mat.dumpHprofService">
+ <status
+ endId="com.motorola.studio.android.emulator.status.online"
+ haltId="com.motorola.studio.android.emulator.status.online"
+ startId="com.motorola.studio.android.emulator.status.online">
+ </status>
+ </service>
+ </extension>
+ <extension
+ id="com.motorola.studio.android.handset.androidHandset"
+ name="Dump HPROF file"
+ point="org.eclipse.sequoyah.device.framework.serviceDefinition">
+ <service
+ id="com.motorola.studio.android.mat.dumpHprofService">
+ <status
+ endId="com.motorola.studio.android.handset.status.handsetonline"
+ haltId="com.motorola.studio.android.handset.status.handsetonline"
+ startId="com.motorola.studio.android.handset.status.handsetonline">
+ </status>
+ </service>
+ </extension>
+ <extension
+ id="com.motorola.studio.android.remote.androidRemoteDevice"
+ name="Dump HPROF file"
+ point="org.eclipse.sequoyah.device.framework.serviceDefinition">
+ <service
+ id="com.motorola.studio.android.mat.dumpHprofService">
+ <status
+ endId="com.motorola.studio.android.remote.status.connected"
+ haltId="com.motorola.studio.android.remote.status.connected"
+ startId="com.motorola.studio.android.remote.status.connected">
+ </status>
+ </service>
+ </extension>
+ <extension
+ id="commandDumpHprof"
+ name="Dump HPROF command"
+ point="org.eclipse.ui.commands">
+ <command
+ defaultHandler="com.motorola.studio.android.mat.commands.DumpHPROFCommand"
+ id="com.motorola.studio.android.mat.commandDumpHprof"
+ name="%command_dumpHPROF_name">
+ </command>
+ </extension>
+ <extension
+ point="org.eclipse.ui.menus">
+ <menuContribution
+ locationURI="popup:com.motorola.studio.android.emulator.view.popup">
+ <command
+ commandId="com.motorola.studio.android.mat.commandDumpHprof"
+ icon="icons/motodev_pane.png"
+ id="dumpHPROF.command"
+ label="%command_dumpHPROF_name"
+ style="push">
+ </command>
+ </menuContribution>
+ </extension>
+
+
+</plugin>
diff --git a/src/plugins/mat/src/com/motorola/studio/android/mat/Activator.java b/src/plugins/mat/src/com/motorola/studio/android/mat/Activator.java
new file mode 100644
index 0000000..77c9183
--- /dev/null
+++ b/src/plugins/mat/src/com/motorola/studio/android/mat/Activator.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.mat;
+
+import java.util.List;
+
+import org.eclipse.sequoyah.device.framework.factory.DeviceTypeRegistry;
+import org.eclipse.sequoyah.device.framework.model.IDeviceType;
+import org.eclipse.sequoyah.device.framework.model.IService;
+import org.eclipse.sequoyah.device.framework.model.handler.IServiceHandler;
+import org.eclipse.sequoyah.device.framework.model.handler.ServiceHandler;
+import org.eclipse.ui.plugin.AbstractUIPlugin;
+import org.osgi.framework.BundleContext;
+
+import com.motorola.studio.android.emulator.EmulatorPlugin;
+
+/**
+ * The activator class controls the plug-in life cycle
+ */
+public class Activator extends AbstractUIPlugin
+{
+
+ // The plug-in ID
+ public static final String PLUGIN_ID = "com.motorola.studio.android.mat";
+
+ // The shared instance
+ private static Activator plugin;
+
+ // Dump HPROF command handler
+ private static ServiceHandler dumpHPROFHandler = null;
+
+ // Dump HPRFO service ID
+ private static final String SERVICE_DUMP_HPROF_ID = PLUGIN_ID + ".dumpHprofService";
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.ui.plugin.AbstractUIPlugin#start(org.osgi.framework.BundleContext)
+ */
+ @Override
+ public void start(BundleContext context) throws Exception
+ {
+ super.start(context);
+ plugin = this;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.ui.plugin.AbstractUIPlugin#stop(org.osgi.framework.BundleContext)
+ */
+ @Override
+ public void stop(BundleContext context) throws Exception
+ {
+ plugin = null;
+ super.stop(context);
+ }
+
+ /**
+ * Returns the shared instance
+ *
+ * @return the shared instance
+ */
+ public static Activator getDefault()
+ {
+ return plugin;
+ }
+
+ /**
+ * Retrieves the deploy service handler.
+ *
+ * @return The currently registered stop service handler, or <null> if no handler is registered.
+ */
+ public static ServiceHandler getDumpHPROFHandler()
+ {
+ if (dumpHPROFHandler == null)
+ {
+ // find the appropriate service handler
+ IDeviceType device =
+ DeviceTypeRegistry.getInstance().getDeviceTypeById(EmulatorPlugin.DEVICE_ID);
+ List<IService> services = device.getServices();
+ for (IService service : services)
+ {
+ IServiceHandler handler = service.getHandler();
+ if (handler.getService().getId().equals(SERVICE_DUMP_HPROF_ID))
+ {
+ dumpHPROFHandler = (ServiceHandler) handler;
+ break;
+ }
+ }
+ }
+
+ return dumpHPROFHandler;
+ }
+
+}
diff --git a/src/plugins/mat/src/com/motorola/studio/android/mat/actions/HeapEditorContributions.java b/src/plugins/mat/src/com/motorola/studio/android/mat/actions/HeapEditorContributions.java
new file mode 100644
index 0000000..7b7da31
--- /dev/null
+++ b/src/plugins/mat/src/com/motorola/studio/android/mat/actions/HeapEditorContributions.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.motorola.studio.android.mat.actions;
+
+import org.eclipse.jface.action.Action;
+import org.eclipse.jface.action.IToolBarManager;
+import org.eclipse.jface.action.Separator;
+import org.eclipse.mat.ui.editor.IMultiPaneEditorContributor;
+import org.eclipse.mat.ui.editor.MultiPaneEditor;
+
+@SuppressWarnings("restriction")
+public class HeapEditorContributions implements IMultiPaneEditorContributor
+{
+
+ private Action openMotodevPane;
+
+ public void contributeToToolbar(IToolBarManager manager)
+ {
+ manager.add(new Separator());
+
+ manager.add(openMotodevPane);
+
+ }
+
+ public void init(MultiPaneEditor editor)
+ {
+ openMotodevPane = new OpenMotodevPaneAction();
+
+ }
+
+ public void dispose()
+ {
+ // do nothing
+ }
+
+}
diff --git a/src/plugins/mat/src/com/motorola/studio/android/mat/actions/OpenMotodevPaneAction.java b/src/plugins/mat/src/com/motorola/studio/android/mat/actions/OpenMotodevPaneAction.java
new file mode 100644
index 0000000..79d1206
--- /dev/null
+++ b/src/plugins/mat/src/com/motorola/studio/android/mat/actions/OpenMotodevPaneAction.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.motorola.studio.android.mat.actions;
+
+import org.eclipse.jface.action.Action;
+import org.eclipse.mat.ui.editor.MultiPaneEditor;
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.plugin.AbstractUIPlugin;
+
+import com.motorola.studio.android.mat.Activator;
+import com.motorola.studio.android.mat.i18n.MatNLS;
+import com.motorola.studio.android.mat.panes.MotodevPane;
+
+@SuppressWarnings("restriction")
+public class OpenMotodevPaneAction extends Action
+{
+
+ // Icon image patch
+ private static final String ACTION_IMAGE_PATH = "icons/android_oql.png";
+
+
+ public OpenMotodevPaneAction()
+ {
+ super(MatNLS.Action_Open_Motodev_Pane, AbstractUIPlugin.imageDescriptorFromPlugin(Activator.PLUGIN_ID, ACTION_IMAGE_PATH) );
+ }
+
+
+ @Override
+ public void run()
+ {
+ IWorkbenchPage page = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage();
+ IEditorPart part = page == null ? null : page.getActiveEditor();
+
+ if (part instanceof MultiPaneEditor)
+ {
+ ((MultiPaneEditor) part).addNewPage(MotodevPane.MOTODEV_PANE_ID, null);//$NON-NLS-1$
+ }
+ }
+}
diff --git a/src/plugins/mat/src/com/motorola/studio/android/mat/commands/DumpHPROFCommand.java b/src/plugins/mat/src/com/motorola/studio/android/mat/commands/DumpHPROFCommand.java
new file mode 100644
index 0000000..ea108f8
--- /dev/null
+++ b/src/plugins/mat/src/com/motorola/studio/android/mat/commands/DumpHPROFCommand.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.mat.commands;
+
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.core.commands.IHandler;
+import org.eclipse.sequoyah.device.common.utilities.exception.SequoyahException;
+import org.eclipse.sequoyah.device.framework.model.IInstance;
+
+import com.motorola.studio.android.emulator.core.model.IAndroidEmulatorInstance;
+import com.motorola.studio.android.emulator.ui.view.AbstractAndroidView;
+import com.motorola.studio.android.mat.Activator;
+
+
+public class DumpHPROFCommand extends AbstractHandler implements IHandler
+{
+
+ public Object execute(ExecutionEvent event) throws ExecutionException
+ {
+ IAndroidEmulatorInstance instance = AbstractAndroidView.getActiveInstance();
+ if (instance instanceof IInstance)
+ {
+ try
+ {
+ Activator.getDumpHPROFHandler().run((IInstance)instance);
+ }
+ catch (SequoyahException e)
+ {
+ //do nothing
+ }
+ }
+ return null;
+ }
+
+}
diff --git a/src/plugins/mat/src/com/motorola/studio/android/mat/i18n/MatNLS.java b/src/plugins/mat/src/com/motorola/studio/android/mat/i18n/MatNLS.java
new file mode 100644
index 0000000..eec92aa
--- /dev/null
+++ b/src/plugins/mat/src/com/motorola/studio/android/mat/i18n/MatNLS.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.mat.i18n;
+
+import org.eclipse.osgi.util.NLS;
+
+/**
+ * Class that contains the localized messages to be used through the
+ * MAT Plugin
+ *
+ */
+public class MatNLS extends NLS
+{
+ static
+ {
+ NLS.initializeMessages("com.motorola.studio.android.mat.i18n.matNLS", MatNLS.class);
+ }
+
+ /*
+ * UI strings
+ */
+
+ public static String Motodev_Pane_Title;
+
+ public static String Action_Open_Motodev_Pane;
+
+ public static String DumpHPROFHandler_DEVICE_NOT_READY;
+
+ public static String DumpHPROFHandler_UNSUPPORTED_DEVICE;
+
+ public static String JOB_Name_Dump_Hprof;
+
+}
diff --git a/src/plugins/mat/src/com/motorola/studio/android/mat/i18n/matNLS.properties b/src/plugins/mat/src/com/motorola/studio/android/mat/i18n/matNLS.properties
new file mode 100644
index 0000000..e43ab62
--- /dev/null
+++ b/src/plugins/mat/src/com/motorola/studio/android/mat/i18n/matNLS.properties
@@ -0,0 +1,7 @@
+Motodev_Pane_Title=Android Application Memory Analysis
+
+Action_Open_Motodev_Pane=Open Android Application Memory Analysis
+DumpHPROFHandler_DEVICE_NOT_READY=Device is not ready. Try again in a few seconds.
+DumpHPROFHandler_UNSUPPORTED_DEVICE=This device does not support the dumping of HProf files to the SD card.
+
+JOB_Name_Dump_Hprof=Analyze Memory using MAT
diff --git a/src/plugins/mat/src/com/motorola/studio/android/mat/panes/MotodevPane.java b/src/plugins/mat/src/com/motorola/studio/android/mat/panes/MotodevPane.java
new file mode 100644
index 0000000..f036cfe
--- /dev/null
+++ b/src/plugins/mat/src/com/motorola/studio/android/mat/panes/MotodevPane.java
@@ -0,0 +1,322 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.mat.panes;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.jface.action.Action;
+import org.eclipse.mat.SnapshotException;
+import org.eclipse.mat.query.registry.ArgumentSet;
+import org.eclipse.mat.query.registry.QueryDescriptor;
+import org.eclipse.mat.query.registry.QueryRegistry;
+import org.eclipse.mat.query.registry.QueryResult;
+import org.eclipse.mat.snapshot.IOQLQuery;
+import org.eclipse.mat.snapshot.OQLParseException;
+import org.eclipse.mat.snapshot.SnapshotFactory;
+import org.eclipse.mat.ui.Messages;
+import org.eclipse.mat.ui.editor.AbstractEditorPane;
+import org.eclipse.mat.ui.editor.AbstractPaneJob;
+import org.eclipse.mat.ui.editor.CompositeHeapEditorPane;
+import org.eclipse.mat.ui.editor.EditorPaneRegistry;
+import org.eclipse.mat.ui.util.ErrorHelper;
+import org.eclipse.mat.ui.util.PaneState;
+import org.eclipse.mat.ui.util.PaneState.PaneType;
+import org.eclipse.mat.ui.util.ProgressMonitorWrapper;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.ui.PartInitException;
+import org.eclipse.ui.PlatformUI;
+
+import com.motorola.studio.android.AndroidPlugin;
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.mat.i18n.MatNLS;
+
+@SuppressWarnings("restriction")
+public class MotodevPane extends CompositeHeapEditorPane
+{
+
+ // Select statement
+ private String queryFirstPart = "select * from \"";
+
+ private String querySecondPart = ".*\"";
+
+ // Query to be executed
+ private String queryString;
+
+ // Pane title
+ private static final String PANE_TITLE = MatNLS.Motodev_Pane_Title;
+
+ // Pane ID
+ public final static String MOTODEV_PANE_ID = "com.motorola.studio.android.mat.MotodevPane";
+
+ private Action executeAction;
+
+ // Action to be executed
+ public void createPartControl(Composite parent)
+ {
+ createContainer(parent);
+
+ // Retrive the selected app by the user and construct the query
+ String selectedApp =
+ getEditorInput().getName().substring(
+ getEditorInput().getName().lastIndexOf(File.separator) + 1);
+
+ queryString =
+ queryFirstPart
+ + AndroidPlugin.getDefault().getPreferenceStore().getString(selectedApp)
+ + querySecondPart;
+
+ makeActions();
+
+ }
+
+ private void makeActions()
+ {
+ executeAction = new ExecuteQueryAction();
+
+ executeAction.run();
+ }
+
+ public String getTitle()
+ {
+ return PANE_TITLE;
+ }
+
+ @Override
+ public void initWithArgument(final Object param)
+ {
+ if (param instanceof String)
+ {
+ queryString = (String) param;
+ executeAction.run();
+ }
+ else if (param instanceof QueryResult)
+ {
+ QueryResult queryResult = (QueryResult) param;
+ initQueryResult(queryResult, null);
+ }
+ else if (param instanceof PaneState)
+ {
+ queryString = ((PaneState) param).getIdentifier();
+ new ExecuteQueryAction((PaneState) param).run();
+ }
+ }
+
+ private void initQueryResult(QueryResult queryResult, PaneState state)
+ {
+ IOQLQuery.Result subject = (IOQLQuery.Result) queryResult.getSubject();
+ queryString = subject.getOQLQuery();
+
+ AbstractEditorPane pane =
+ EditorPaneRegistry.instance().createNewPane(subject, this.getClass());
+
+ if (state == null)
+ {
+ for (PaneState child : getPaneState().getChildren())
+ {
+ if (queryString.equals(child.getIdentifier()))
+ {
+ state = child;
+ break;
+ }
+ }
+
+ if (state == null)
+ {
+ state = new PaneState(PaneType.COMPOSITE_CHILD, getPaneState(), queryString, true);
+ state.setImage(getTitleImage());
+ }
+ }
+
+ pane.setPaneState(state);
+
+ createResultPane(pane, queryResult);
+ }
+
+ // //////////////////////////////////////////////////////////////
+ // job to execute query
+ // //////////////////////////////////////////////////////////////
+
+ class OQLJob extends AbstractPaneJob
+ {
+ String queryString;
+
+ PaneState state;
+
+ public OQLJob(AbstractEditorPane pane, String queryString, PaneState state)
+ {
+ super(queryString.toString(), pane);
+ this.queryString = queryString;
+ this.state = state;
+ this.setUser(true);
+ }
+
+ @Override
+ protected IStatus doRun(IProgressMonitor monitor)
+ {
+ try
+ {
+ QueryDescriptor descriptor = QueryRegistry.instance().getQuery("oql");//$NON-NLS-1$
+ ArgumentSet argumentSet =
+ descriptor.createNewArgumentSet(getEditor().getQueryContext());
+ argumentSet.setArgumentValue("queryString", queryString);//$NON-NLS-1$
+ final QueryResult result = argumentSet.execute(new ProgressMonitorWrapper(monitor));
+
+ PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable()
+ {
+ public void run()
+ {
+ initQueryResult(result, state);
+ }
+ });
+ }
+ catch (final Exception e)
+ {
+ PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable()
+ {
+ public void run()
+ {
+ try
+ {
+ createExceptionPane(e, queryString);
+ }
+ catch (PartInitException pie)
+ {
+ ErrorHelper.logThrowable(pie);
+ }
+ }
+ });
+ }
+
+ return Status.OK_STATUS;
+ }
+ }
+
+ public void createExceptionPane(Exception cause, String queryString) throws PartInitException
+ {
+ StringBuilder buf = new StringBuilder(256);
+ buf.append(Messages.OQLPane_ExecutedQuery);
+ buf.append(queryString);
+
+ Throwable t = null;
+ if (cause instanceof SnapshotException)
+ {
+ buf.append(Messages.OQLPane_ProblemReported);
+ buf.append(cause.getMessage());
+ t = cause.getCause();
+ }
+ else
+ {
+ t = cause;
+ }
+
+ if (t != null)
+ {
+ StringWriter w = null;
+ PrintWriter o = null;
+ try
+ {
+ buf.append("\n\n");//$NON-NLS-1$
+ w = new StringWriter();
+ o = new PrintWriter(w);
+ t.printStackTrace(o);
+ o.flush();
+
+ buf.append(w.toString());
+ }
+ finally
+ {
+ try
+ {
+ w.close();
+ o.close();
+ }
+ catch (IOException e)
+ {
+ StudioLogger.error(e.getMessage());
+ }
+ }
+
+ }
+
+ try
+ {
+ AbstractEditorPane pane = EditorPaneRegistry.instance().createNewPane("TextViewPane");//$NON-NLS-1$
+ if (pane == null)
+ {
+ throw new PartInitException(Messages.OQLPane_PaneNotFound);
+ }
+
+ // no pane state -> do not include in navigation history
+ createResultPane(pane, buf.toString());
+ }
+ catch (CoreException e)
+ {
+ throw new PartInitException(ErrorHelper.createErrorStatus(e));
+ }
+ }
+
+ private class ExecuteQueryAction extends Action
+ {
+ private PaneState state;
+
+ public ExecuteQueryAction()
+ {
+ this(null);
+ }
+
+ public ExecuteQueryAction(PaneState state)
+ {
+ this.state = state;
+ }
+
+ @Override
+ public void run()
+ {
+ try
+ {
+ String query = queryString;
+
+ try
+ {
+ // force parsing of OQL query
+ SnapshotFactory.createQuery(query);
+ new OQLJob(MotodevPane.this, query, state).schedule();
+ }
+ catch (final OQLParseException e)
+ {
+ createExceptionPane(e, query);
+ }
+ catch (Exception e)
+ {
+ createExceptionPane(e, query);
+ }
+ }
+ catch (PartInitException e1)
+ {
+ ErrorHelper.logThrowableAndShowMessage(e1, Messages.OQLPane_ErrorExecutingQuery);
+ }
+ }
+
+ }
+
+}
diff --git a/src/plugins/mat/src/com/motorola/studio/android/mat/services/DumpHPROFHandler.java b/src/plugins/mat/src/com/motorola/studio/android/mat/services/DumpHPROFHandler.java
new file mode 100644
index 0000000..250b547
--- /dev/null
+++ b/src/plugins/mat/src/com/motorola/studio/android/mat/services/DumpHPROFHandler.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.mat.services;
+
+import java.util.Map;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.sequoyah.device.framework.model.IInstance;
+import org.eclipse.sequoyah.device.framework.model.handler.IServiceHandler;
+import org.eclipse.sequoyah.device.framework.model.handler.ServiceHandler;
+
+import com.motorola.studio.android.adt.DDMSUtils;
+import com.motorola.studio.android.adt.ISerialNumbered;
+import com.motorola.studio.android.mat.Activator;
+import com.motorola.studio.android.mat.i18n.MatNLS;
+
+public class DumpHPROFHandler extends ServiceHandler
+{
+
+ @Override
+ public IServiceHandler newInstance()
+ {
+ return new DumpHPROFHandler();
+ }
+
+ @Override
+ public IStatus runService(IInstance instance, Map<Object, Object> arg1, IProgressMonitor monitor)
+ {
+ IStatus status = Status.OK_STATUS;
+ if (instance instanceof ISerialNumbered)
+ {
+ ISerialNumbered serialNumbered = (ISerialNumbered) instance;
+
+ final String serialNumber = serialNumbered.getSerialNumber();
+ int deviceApiVersion = DDMSUtils.getDeviceApiVersion(serialNumber);
+
+ if (deviceApiVersion > 0)
+ {
+ if (deviceApiVersion > 2)
+ {
+
+ Job job = new Job(MatNLS.JOB_Name_Dump_Hprof)
+ {
+ @Override
+ protected IStatus run(IProgressMonitor monitor)
+ {
+ return DDMSUtils.dumpHPROF(serialNumber, monitor);
+ }
+
+ };
+ job.setUser(true);
+ job.schedule();
+ }
+ else
+ {
+ status =
+ new Status(IStatus.ERROR, Activator.PLUGIN_ID,
+ MatNLS.DumpHPROFHandler_UNSUPPORTED_DEVICE);
+ }
+ }
+ else
+ {
+ status =
+ new Status(IStatus.ERROR, Activator.PLUGIN_ID,
+ MatNLS.DumpHPROFHandler_DEVICE_NOT_READY);
+ }
+
+ }
+ return status;
+ }
+
+ @Override
+ public IStatus updatingService(IInstance arg0, IProgressMonitor arg1)
+ {
+ return Status.OK_STATUS;
+ }
+
+}
diff --git a/src/plugins/packaging.ui/.classpath b/src/plugins/packaging.ui/.classpath
new file mode 100644
index 0000000..8a8f166
--- /dev/null
+++ b/src/plugins/packaging.ui/.classpath
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>
+ <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/src/plugins/packaging.ui/.project b/src/plugins/packaging.ui/.project
new file mode 100644
index 0000000..5b96cdd
--- /dev/null
+++ b/src/plugins/packaging.ui/.project
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>com.motorola.studio.android.packaging.ui</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.ManifestBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.SchemaBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.pde.PluginNature</nature>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+</projectDescription>
diff --git a/src/plugins/packaging.ui/META-INF/MANIFEST.MF b/src/plugins/packaging.ui/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..95ce1be
--- /dev/null
+++ b/src/plugins/packaging.ui/META-INF/MANIFEST.MF
@@ -0,0 +1,22 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: %Bundle-Name.0
+Bundle-SymbolicName: com.motorola.studio.android.packaging.ui;singleton:=true
+Bundle-Version: 5.0.0.qualifier
+Bundle-Activator: com.motorola.studio.android.packaging.ui.PackagingUIPlugin
+Bundle-Vendor: %Bundle-Vendor.0
+Require-Bundle: org.eclipse.core.resources,
+ org.eclipse.core.runtime,
+ org.eclipse.ui,
+ org.eclipse.ui.forms,
+ org.eclipse.ui.ide,
+ org.eclipse.ui.views,
+ org.eclipse.jdt.core,
+ org.eclipse.jdt.ui,
+ com.motorola.studio.android.common,
+ com.motorola.studio.android,
+ com.motorolamobility.studio.android.certmanager
+Bundle-Localization: plugin
+Bundle-RequiredExecutionEnvironment: JavaSE-1.6
+Bundle-ActivationPolicy: lazy
+Export-Package: com.motorola.studio.android.packaging.ui.i18n
diff --git a/src/plugins/packaging.ui/build.properties b/src/plugins/packaging.ui/build.properties
new file mode 100644
index 0000000..d24082b
--- /dev/null
+++ b/src/plugins/packaging.ui/build.properties
@@ -0,0 +1,7 @@
+source.. = src/
+output.. = bin/
+bin.includes = META-INF/,\
+ .,\
+ plugin.properties,\
+ plugin.xml,\
+ icons/
diff --git a/src/plugins/packaging.ui/icons/export_android_package.png b/src/plugins/packaging.ui/icons/export_android_package.png
new file mode 100644
index 0000000..9451fb9
--- /dev/null
+++ b/src/plugins/packaging.ui/icons/export_android_package.png
Binary files differ
diff --git a/src/plugins/packaging.ui/icons/quick_fix_add.PNG b/src/plugins/packaging.ui/icons/quick_fix_add.PNG
new file mode 100644
index 0000000..53ffb7f
--- /dev/null
+++ b/src/plugins/packaging.ui/icons/quick_fix_add.PNG
Binary files differ
diff --git a/src/plugins/packaging.ui/icons/quick_fix_add_16x16.PNG b/src/plugins/packaging.ui/icons/quick_fix_add_16x16.PNG
new file mode 100644
index 0000000..aa665b3
--- /dev/null
+++ b/src/plugins/packaging.ui/icons/quick_fix_add_16x16.PNG
Binary files differ
diff --git a/src/plugins/packaging.ui/icons/sign_package.png b/src/plugins/packaging.ui/icons/sign_package.png
new file mode 100644
index 0000000..84efb17
--- /dev/null
+++ b/src/plugins/packaging.ui/icons/sign_package.png
Binary files differ
diff --git a/src/plugins/packaging.ui/icons/unsign_package.png b/src/plugins/packaging.ui/icons/unsign_package.png
new file mode 100644
index 0000000..759118b
--- /dev/null
+++ b/src/plugins/packaging.ui/icons/unsign_package.png
Binary files differ
diff --git a/src/plugins/packaging.ui/icons/wizban/export_android_package.png b/src/plugins/packaging.ui/icons/wizban/export_android_package.png
new file mode 100644
index 0000000..188738e
--- /dev/null
+++ b/src/plugins/packaging.ui/icons/wizban/export_android_package.png
Binary files differ
diff --git a/src/plugins/packaging.ui/plugin.properties b/src/plugins/packaging.ui/plugin.properties
new file mode 100644
index 0000000..78426b9
--- /dev/null
+++ b/src/plugins/packaging.ui/plugin.properties
@@ -0,0 +1,12 @@
+#Properties file for com.motorola.studio.android.packaging.ui
+Bundle-Vendor.0=Motorola Mobility, Inc.
+Bundle-Name.0=MOTODEV Studio for Android Packaging UI Plug-in
+signingToolView=Keystore Manager View
+signExternalPackagesAction=Sign Android Package
+removeExternalPackagesSignatureAction=Remove Android Package Signature
+exportWizardShortName=Export Android Application
+exportWizardName=Export Android Application using MOTODEV Studio for Android
+exportWizardDescription=Export signed or unsigned Android packages
+signCommandLabel=Sign Android Package...
+unsignCommandLabel=Remove Android Package Signature...
+signingLabel=Signing \ No newline at end of file
diff --git a/src/plugins/packaging.ui/plugin.xml b/src/plugins/packaging.ui/plugin.xml
new file mode 100644
index 0000000..dfda88c
--- /dev/null
+++ b/src/plugins/packaging.ui/plugin.xml
@@ -0,0 +1,105 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?eclipse version="3.4"?>
+<plugin>
+ <!--extension
+ point="com.motorola.studio.platform.tools.sign.ui.supportBoundCertificate">
+ <SupportBoundCertificate
+ support="false">
+ </SupportBoundCertificate>
+ </extension>
+ <extension
+ id="id1"
+ name="name"
+ point="com.motorola.studio.platform.tools.sign.ui.signViewButtonAction">
+ <SignViewButtonAction
+ className="com.motorolamobility.studio.android.certmanager.ui.action.SignCreatedPackageAction"
+ label="%signExternalPackagesAction"
+ type="Sign Application">
+ </SignViewButtonAction>
+ <SignViewButtonAction
+ className="com.motorolamobility.studio.android.certmanager.ui.action.RemoveSignatureAction"
+ label="%removeExternalPackagesSignatureAction"
+ type="Remove Signature">
+ </SignViewButtonAction>
+ </extension-->
+ <extension
+ point="org.eclipse.ui.exportWizards">
+ <wizard
+ category="com.android.ide.eclipse.wizards.category"
+ class="com.motorola.studio.android.packaging.ui.export.PackageExportWizard"
+ icon="icons/export_android_package.png"
+ id="com.motorola.studio.android.packaging.ui.wizard1"
+ name="%exportWizardName">
+ <description>
+ %exportWizardDescription
+ </description>
+ </wizard>
+ </extension>
+ <extension
+ point="org.eclipse.ui.commands">
+ <command
+ defaultHandler="com.motorolamobility.studio.android.certmanager.command.SignExternalPackagesHandler"
+ id="com.motorola.studio.android.packaging.ui.sign"
+ name="%signCommandLabel">
+ </command>
+ <command
+ defaultHandler="com.motorolamobility.studio.android.certmanager.command.UnsignExternalPackagesHandler"
+ id="com.motorola.studio.android.packaging.ui.unsign"
+ name="%unsignCommandLabel">
+ </command>
+ <command
+ defaultHandler="com.motorola.studio.android.packaging.ui.handlers.ExportWizardHandler"
+ id="com.motorola.studio.android.packaging.ui.export"
+ name="%exportWizardShortName">
+ </command>
+ </extension>
+ <extension
+ point="org.eclipse.ui.menus">
+ <menuContribution
+ locationURI="popup:studioAndroidPopupMenu">
+ <command
+ commandId="com.motorola.studio.android.packaging.ui.sign"
+ icon="icons/sign_package.png"
+ label="%signCommandLabel"
+ style="push">
+ </command>
+ <command
+ commandId="com.motorola.studio.android.packaging.ui.unsign"
+ icon="icons/unsign_package.png"
+ label="%unsignCommandLabel"
+ style="push">
+ </command>
+ </menuContribution>
+ <menuContribution
+ locationURI="menu:motorolaMenu?after=signingSeparator">
+ <menu
+ id="studioSigningMenu"
+ label="%signingLabel">
+ <command
+ commandId="com.motorola.studio.android.packaging.ui.sign"
+ icon="icons/sign_package.png"
+ label="%signCommandLabel"
+ style="push">
+ </command>
+ <command
+ commandId="com.motorola.studio.android.packaging.ui.unsign"
+ icon="icons/unsign_package.png"
+ label="%unsignCommandLabel"
+ style="push">
+ </command>
+ </menu>
+ </menuContribution>
+ <menuContribution
+ allPopups="false"
+ locationURI="menu:motorolaMenu?after=externalToolsSeparator">
+ <command
+ commandId="com.motorola.studio.android.packaging.ui.export"
+ icon="icons/export_android_package.png"
+ label="%exportWizardShortName"
+ style="push"
+ tooltip="%exportWizardDescription">
+ </command>
+ </menuContribution>
+ </extension>
+
+</plugin>
diff --git a/src/plugins/packaging.ui/src/com/motorola/studio/android/packaging/ui/PackagingUIPlugin.java b/src/plugins/packaging.ui/src/com/motorola/studio/android/packaging/ui/PackagingUIPlugin.java
new file mode 100644
index 0000000..a1e1503
--- /dev/null
+++ b/src/plugins/packaging.ui/src/com/motorola/studio/android/packaging/ui/PackagingUIPlugin.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.motorola.studio.android.packaging.ui;
+
+import org.eclipse.ui.plugin.AbstractUIPlugin;
+import org.osgi.framework.BundleContext;
+
+import com.motorola.studio.android.common.log.StudioLogger;
+
+/**
+ * The activator class controls the plug-in life cycle
+ */
+public class PackagingUIPlugin extends AbstractUIPlugin
+{
+
+ // The plug-in ID
+ public static final String PLUGIN_ID = "com.motorola.studio.android.packaging.ui";
+
+ public static final String EXPORT_WIZARD_ICON = "icons/wizban/export_android_package.png";
+
+ public static final String PACKAGING_WIZARD_CONTEXT_HELP_ID = PLUGIN_ID + ".packaging_help";
+
+ public static final String SIGN_EXTERNAL_PKG_WIZARD_CONTEXT_HELP_ID =
+ PackagingUIPlugin.PLUGIN_ID + ".sign_external_pkg_wiz";
+
+ public static final String UNSIGN_EXTERNAL_PKG_WIZARD_CONTEXT_HELP_ID =
+ PackagingUIPlugin.PLUGIN_ID + ".unsign_external_pkg_wiz";
+
+ public static final int PROGRESS_MONITOR_MULTIPLIER = 100;
+
+ // The shared instance
+ private static PackagingUIPlugin plugin;
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.ui.plugin.AbstractUIPlugin#start(org.osgi.framework.BundleContext)
+ */
+ @Override
+ public void start(BundleContext context) throws Exception
+ {
+ StudioLogger.debug(PackagingUIPlugin.class,
+ "Starting MOTODEV Android Packaging UI Plugin...");
+
+ super.start(context);
+ plugin = this;
+
+ StudioLogger.debug(PackagingUIPlugin.class, "MOTODEV Android Packaging UI Plugin started.");
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.ui.plugin.AbstractUIPlugin#stop(org.osgi.framework.BundleContext)
+ */
+ @Override
+ public void stop(BundleContext context) throws Exception
+ {
+ plugin = null;
+ super.stop(context);
+ }
+
+ /**
+ * Returns the shared instance
+ *
+ * @return the shared instance
+ */
+ public static PackagingUIPlugin getDefault()
+ {
+ return plugin;
+ }
+
+}
diff --git a/src/plugins/packaging.ui/src/com/motorola/studio/android/packaging/ui/export/PackageExportWizard.java b/src/plugins/packaging.ui/src/com/motorola/studio/android/packaging/ui/export/PackageExportWizard.java
new file mode 100644
index 0000000..5679ec4
--- /dev/null
+++ b/src/plugins/packaging.ui/src/com/motorola/studio/android/packaging/ui/export/PackageExportWizard.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.packaging.ui.export;
+
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.wizard.Wizard;
+import org.eclipse.ui.IExportWizard;
+import org.eclipse.ui.IWorkbench;
+
+import com.motorola.studio.android.packaging.ui.i18n.Messages;
+
+/**
+ * Export Applications Wizard
+ */
+public class PackageExportWizard extends Wizard implements IExportWizard
+{
+ private PackageExportWizardPage mainPage;
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.jface.wizard.Wizard#performFinish()
+ */
+ @Override
+ public boolean performFinish()
+ {
+ return this.mainPage.finish();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.ui.IWorkbenchWizard#init(org.eclipse.ui.IWorkbench,
+ * org.eclipse.jface.viewers.IStructuredSelection)
+ */
+ public void init(IWorkbench workbench, IStructuredSelection selection)
+ {
+ setWindowTitle(Messages.EXPORT_WIZARD_TILE); // NON-NLS-1
+ setNeedsProgressMonitor(true);
+ this.mainPage = new PackageExportWizardPage(Messages.EXPORT_WIZARD_TILE, selection, true); // NON-NLS-1}
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.jface.wizard.Wizard#addPages()
+ */
+ @Override
+ public void addPages()
+ {
+ super.addPages();
+ addPage(this.mainPage);
+ }
+}
diff --git a/src/plugins/packaging.ui/src/com/motorola/studio/android/packaging/ui/export/PackageExportWizardArea.java b/src/plugins/packaging.ui/src/com/motorola/studio/android/packaging/ui/export/PackageExportWizardArea.java
new file mode 100644
index 0000000..e9e17ae
--- /dev/null
+++ b/src/plugins/packaging.ui/src/com/motorola/studio/android/packaging/ui/export/PackageExportWizardArea.java
@@ -0,0 +1,1897 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.motorola.studio.android.packaging.ui.export;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
+import java.security.UnrecoverableKeyException;
+import java.util.ArrayList;
+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.jar.JarFile;
+
+import org.eclipse.core.resources.IMarker;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IAdaptable;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.MultiStatus;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.SubMonitor;
+import org.eclipse.core.runtime.jobs.IJobChangeEvent;
+import org.eclipse.core.runtime.jobs.IJobChangeListener;
+import org.eclipse.core.runtime.jobs.JobChangeAdapter;
+import org.eclipse.jdt.internal.core.JavaProject;
+import org.eclipse.jdt.internal.ui.JavaPluginImages;
+import org.eclipse.jface.dialogs.ErrorDialog;
+import org.eclipse.jface.dialogs.IMessageProvider;
+import org.eclipse.jface.dialogs.ProgressMonitorDialog;
+import org.eclipse.jface.operation.IRunnableWithProgress;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.jface.viewers.DecorationOverlayIcon;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.StructuredSelection;
+import org.eclipse.jface.window.Window;
+import org.eclipse.jface.wizard.IWizardPage;
+import org.eclipse.jface.wizard.WizardDialog;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Combo;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.DirectoryDialog;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Group;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.MessageBox;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.swt.widgets.Tree;
+import org.eclipse.swt.widgets.TreeItem;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.plugin.AbstractUIPlugin;
+
+import com.motorola.studio.android.AndroidPlugin;
+import com.motorola.studio.android.adt.AdtUtils;
+import com.motorola.studio.android.adt.SdkUtils;
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.common.utilities.EclipseUtils;
+import com.motorola.studio.android.packaging.ui.PackagingUIPlugin;
+import com.motorola.studio.android.packaging.ui.i18n.Messages;
+import com.motorolamobility.studio.android.certmanager.CertificateManagerActivator;
+import com.motorolamobility.studio.android.certmanager.core.KeyStoreManager;
+import com.motorolamobility.studio.android.certmanager.exception.InvalidPasswordException;
+import com.motorolamobility.studio.android.certmanager.exception.KeyStoreManagerException;
+import com.motorolamobility.studio.android.certmanager.job.CreateKeyJob;
+import com.motorolamobility.studio.android.certmanager.packaging.PackageFile;
+import com.motorolamobility.studio.android.certmanager.packaging.sign.PackageFileSigner;
+import com.motorolamobility.studio.android.certmanager.packaging.sign.SignException;
+import com.motorolamobility.studio.android.certmanager.ui.model.IKeyStore;
+import com.motorolamobility.studio.android.certmanager.ui.model.ITreeNode;
+import com.motorolamobility.studio.android.certmanager.ui.wizards.CreateKeyWizard;
+import com.motorolamobility.studio.android.certmanager.ui.wizards.CreateKeystoreWizard;
+import com.motorolamobility.studio.android.certmanager.ui.wizards.SelectExistentKeystoreWizard;
+
+/**
+ *
+ * This Class is an area implementation for Package Export Wizards It contains
+ * all UI and finish logics
+ *
+ */
+@SuppressWarnings("restriction")
+public class PackageExportWizardArea
+{
+ /**
+ * It holds the selection
+ */
+ private final IStructuredSelection selection;
+
+ /**
+ * The destination Folder selected by the user
+ */
+ private Text destinationText;
+
+ private Button selectAllButton;
+
+ private Button deselectAllButton;
+
+ private Button packageDestinationBrowseButton;
+
+ private Button defaultDestination;
+
+ private Button signCheckBox;
+
+ private final Composite parentComposite;
+
+ private final boolean signingEnabled;
+
+ private final HashMap<IProject, Integer> projectSeverity;
+
+ /**
+ * The tree which contains all descriptor files
+ */
+ private Tree tree;
+
+ private String message;
+
+ private int severity;
+
+ private boolean treeSelectionChanged;
+
+ private final Image icon_ok, icon_nok;
+
+ private Combo keystores;
+
+ private Combo keysCombo;
+
+ private Group signingGroup;
+
+ private Button buttonAddKey;
+
+ // Used in parallel with keystore combo. Use it with the selection index.
+ private static ArrayList<IKeyStore> keystoreList = new ArrayList<IKeyStore>();
+
+ //maps keystore->password
+ private final Map<IKeyStore, String> keystorePasswords = new HashMap<IKeyStore, String>();
+
+ private IKeyStore previousSelectedKeystore;
+
+ private String previousSelectedKey;
+
+ private Button buttonExisting;
+
+ private Button buttonAddNew;
+
+ /**
+ * Creates a new Export Area.
+ *
+ * @param selection
+ * The current Selection
+ */
+ public PackageExportWizardArea(IStructuredSelection selection, Composite parent,
+ boolean signingEnabled)
+ {
+ this.selection = normalizeSelection(selection);
+ this.parentComposite = parent;
+ this.signingEnabled = signingEnabled;
+ this.projectSeverity = new HashMap<IProject, Integer>();
+ validateProjects();
+ ImageDescriptor adtProjectImageDescriptor =
+ AbstractUIPlugin.imageDescriptorFromPlugin("com.android.ide.eclipse.adt", //$NON-NLS-1$
+ "icons/android_project.png"); //$NON-NLS-1$
+ ImageDescriptor errorImageDescriptor = JavaPluginImages.DESC_OVR_ERROR;
+ ImageDescriptor projectImage =
+ PlatformUI.getWorkbench().getSharedImages()
+ .getImageDescriptor(org.eclipse.ui.ide.IDE.SharedImages.IMG_OBJ_PROJECT);
+ // (IDecoration.TOP_LEFT, IDecoration.TOP_RIGHT,
+ // IDecoration.BOTTOM_LEFT, IDecoration.BOTTOM_RIGHT and
+ // IDecoration.UNDERLAY).
+ ImageDescriptor[] overlays = new ImageDescriptor[5];
+ overlays[1] = adtProjectImageDescriptor;
+ icon_ok = new DecorationOverlayIcon(projectImage.createImage(), overlays).createImage();
+ overlays[2] = errorImageDescriptor;
+ icon_nok = new DecorationOverlayIcon(projectImage.createImage(), overlays).createImage();
+ createControl(parent);
+ this.treeSelectionChanged = false;
+ }
+
+ /**
+ * It opens the Directory Selection Dialog that allows the user enter the
+ * destination folder
+ *
+ * @param originalPath
+ * The Folder to show first
+ *
+ * @return The entire path of the user choice
+ */
+ private String directoryDialog(String originalPath)
+ {
+ DirectoryDialog directoryDialog;
+
+ directoryDialog = new DirectoryDialog(parentComposite.getShell());
+
+ File directory = new File(originalPath);
+
+ if (directory.exists())
+ {
+ directoryDialog.setFilterPath(directory.getPath());
+ }
+
+ String returnedPath = directoryDialog.open();
+
+ return returnedPath;
+ }
+
+ /**
+ * This method normalize the selection only to contain projects and
+ * descriptors
+ *
+ * @return
+ * @throws CoreException
+ */
+ @SuppressWarnings("unchecked")
+ private IStructuredSelection normalizeSelection(IStructuredSelection selection)
+ {
+ ArrayList<Object> normalized = new ArrayList<Object>();
+ Iterator<Object> iterator = selection.iterator();
+ while (iterator.hasNext())
+ {
+ Object item = iterator.next();
+ IResource resource = null;
+ if (item instanceof IResource)
+ {
+ resource = (IResource) item;
+ }
+ else if (item instanceof IAdaptable)
+ {
+ try
+ {
+ resource = (IResource) ((IAdaptable) item).getAdapter(IResource.class);
+ }
+ catch (Exception e)
+ {
+ StudioLogger.warn("Error retrieving projects.");
+ }
+ }
+ if (resource != null)
+ {
+ IProject project = resource.getProject();
+ if (!normalized.contains(project))
+ {
+ normalized.add(project);
+ }
+ }
+
+ }
+ return new StructuredSelection(normalized);
+ }
+
+ /**
+ * This method is responsible to add all the existent descriptor files of
+ * the current workspace in the tree shown in the wizard.
+ */
+ private void populateTree()
+ {
+ tree.removeAll();
+ IProject[] projects = ResourcesPlugin.getWorkspace().getRoot().getProjects();
+ try
+ {
+ for (IProject project : projects)
+ {
+ if (project.isOpen() && (project.getNature(AndroidPlugin.Android_Nature) != null)
+ && !SdkUtils.isLibraryProject(project))
+ {
+ TreeItem item = new TreeItem(tree, SWT.NONE);
+ item.setData(project);
+ item.setText(project.getName());
+ item.setImage(projectSeverity.get(project) == IMessageProvider.ERROR ? icon_nok
+ : icon_ok);
+ if (selection.toList().contains(project))
+ {
+ item.setChecked(true);
+ }
+ }
+ }
+ }
+ catch (CoreException e)
+ {
+ StudioLogger.error(PackageExportWizardArea.class,
+ "Error populating tree: " + e.getMessage()); //$NON-NLS-1$
+ }
+
+ }
+
+ /**
+ * Create the destination selection group
+ *
+ * @param mainComposite
+ * : the parent composite
+ */
+ private void createDestinationGroup(Composite mainComposite)
+ {
+
+ // create destination group
+ Group destinationGroup = new Group(mainComposite, SWT.SHADOW_ETCHED_OUT);
+ GridLayout layout = new GridLayout(3, false);
+ destinationGroup.setLayout(layout);
+ GridData defaultDestGridData = new GridData(SWT.FILL, SWT.CENTER, true, false, 3, 1);
+ destinationGroup.setLayoutData(defaultDestGridData);
+ destinationGroup.setText(Messages.PACKAGE_EXPORT_WIZARD_AREA_DESTINATION_LABEL);
+
+ // Default Destination
+ this.defaultDestination = new Button(destinationGroup, SWT.CHECK);
+ defaultDestGridData = new GridData(SWT.FILL, SWT.FILL, true, false, 3, 1);
+ this.defaultDestination.setLayoutData(defaultDestGridData);
+ this.defaultDestination.addListener(SWT.Selection, new DefaultDestinationListener());
+ this.defaultDestination.setText(Messages.PACKAGE_EXPORT_WIZARD_USE_DEFAULT_DESTINATION);
+ this.defaultDestination.setSelection(true);
+
+ // Package Destination Label
+ Label packageDestinationLabel = new Label(destinationGroup, SWT.NONE);
+ packageDestinationLabel.setText(Messages.PACKAGE_EXPORT_WIZARD_PACKAGE_DESTINATION_LABEL);
+ GridData folderGridData = new GridData(SWT.LEFT, SWT.CENTER, false, false);
+ packageDestinationLabel.setLayoutData(folderGridData);
+
+ // Package Destination
+ this.destinationText = new Text(destinationGroup, SWT.SINGLE | SWT.BORDER);
+ folderGridData = new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1);
+ this.destinationText.setLayoutData(folderGridData);
+ this.destinationText.setEnabled(!this.defaultDestination.getSelection());
+ this.destinationText.addListener(SWT.Modify, new DestinationTextListener());
+
+ // Browse Button
+ this.packageDestinationBrowseButton = new Button(destinationGroup, SWT.PUSH);
+ folderGridData = new GridData(SWT.FILL, SWT.CENTER, false, false, 1, 1);
+ this.packageDestinationBrowseButton.setLayoutData(folderGridData);
+ this.packageDestinationBrowseButton
+ .setText(Messages.PACKAGE_EXPORT_WIZARD_AREA_BROWSE_BUTTON_LABEL);
+ this.packageDestinationBrowseButton.setEnabled(!this.defaultDestination.getSelection());
+ this.packageDestinationBrowseButton.addListener(SWT.Selection,
+ new PackageDestinationButtonListener());
+
+ }
+
+ public final String[] getKeys(IKeyStore iKeyStore) throws KeyStoreManagerException,
+ InvalidPasswordException
+ {
+ List<String> aliases = new ArrayList<String>();
+
+ aliases = iKeyStore.getAliases(getKeyStorePassword(iKeyStore));
+
+ return aliases.toArray(new String[0]);
+ }
+
+ public String openNewKeyWizard(IKeyStore keyStore, IJobChangeListener createKeyJobListener)
+ {
+
+ CreateKeyWizard wizard =
+ new CreateKeyWizard(keyStore, getKeyStorePassword(getSelectedKeyStore()),
+ createKeyJobListener);
+
+ WizardDialog dialog =
+ new WizardDialog(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(),
+ wizard);
+ dialog.open();
+
+ return wizard.getAlias();
+ }
+
+ private void selectKeystore(IKeyStore newKeystore)
+ {
+ if (newKeystore != null)
+ {
+ int index = keystoreList.indexOf(newKeystore);
+
+ if (keystores.getItemCount() > index)
+ {
+ keystores.select(index);
+
+ loadKeys(newKeystore);
+
+ setEnablement(true);
+ }
+ }
+ else
+ {
+ keystores.deselectAll();
+ }
+
+ previousSelectedKeystore = getSelectedKeyStore();
+ }
+
+ private void selectKeystoreWithoutLoadingKeys(IKeyStore newKeystore)
+ {
+ if (newKeystore != null)
+ {
+ int index = keystoreList.indexOf(newKeystore);
+ keysCombo.removeAll();
+
+ if (keystores.getItemCount() > index)
+ {
+ keystores.select(index);
+
+ setEnablement(true);
+ }
+ }
+ else
+ {
+ keystores.deselectAll();
+ }
+
+ previousSelectedKeystore = getSelectedKeyStore();
+ }
+
+ private boolean loadKeys(IKeyStore newKeystore)
+ {
+ boolean successfullyLoaded = true;
+ keysCombo.removeAll();
+
+ try
+ {
+ String[] keys = getKeys(newKeystore);
+ if (keys != null)
+ {
+ keysCombo.setItems(keys);
+ }
+ }
+ catch (Exception e)
+ {
+ successfullyLoaded = false;
+ StudioLogger.info(PackageExportWizardArea.class,
+ NLS.bind("Could not load keys for keystore: {0}", newKeystore.getFile() //$NON-NLS-1$
+ .getAbsolutePath()));
+ }
+
+ selectKey(0);
+
+ return successfullyLoaded;
+ }
+
+ private void selectKey(String key)
+ {
+
+ String[] keys = keysCombo.getItems();
+
+ int index = -1;
+ int i = 0;
+
+ for (String k : keys)
+ {
+
+ if (k.equals(key))
+ {
+ index = i;
+ break;
+ }
+ i++;
+ }
+
+ selectKey(index);
+ }
+
+ private void selectKey(int index)
+ {
+ if ((index >= 0) && (index < keysCombo.getItemCount()))
+ {
+ keysCombo.select(index);
+ setEnablement(true);
+ }
+ else
+ {
+ keysCombo.deselectAll();
+ }
+
+ }
+
+ private void restorePreviousSelections()
+ {
+ selectKeystore(previousSelectedKeystore);
+ selectKey(previousSelectedKey);
+ }
+
+ private String getSelectedKey()
+ {
+ String result = null;
+ if (keysCombo.getSelectionIndex() >= 0)
+ {
+ result = keysCombo.getText();
+ }
+ return result;
+ }
+
+ protected IKeyStore getSelectedKeyStore()
+ {
+ IKeyStore result = null;
+ if (keystores.getSelectionIndex() >= 0)
+ {
+ result = keystoreList.get(keystores.getSelectionIndex());
+ }
+ return result;
+ }
+
+ /**
+ * Create the sign selection group
+ *
+ * @param mainComposite: the parent composite
+ */
+ private void createSignGroup(Composite mainComposite)
+ {
+ // Create signing group
+ signingGroup = new Group(mainComposite, SWT.SHADOW_ETCHED_OUT);
+ GridLayout layout = new GridLayout(4, false);
+ GridData layoutData = new GridData(SWT.FILL, SWT.CENTER, true, false, 4, 1);
+ signingGroup.setLayout(layout);
+ signingGroup.setLayoutData(layoutData);
+ signingGroup.setText(Messages.PACKAGE_EXPORT_WIZARD_AREA_SIGNING_TAB_TEXT);
+
+ // Sign button/Check box
+ this.signCheckBox = new Button(signingGroup, SWT.CHECK);
+ layoutData = new GridData(SWT.LEFT, SWT.CENTER, true, false, 4, 1);
+ this.signCheckBox.setLayoutData(layoutData);
+ this.signCheckBox.setText(Messages.PACKAGE_EXPORT_WIZARD_AREA_SIGN_CHECK_LABEL);
+ this.signCheckBox.addListener(SWT.Selection, new SignButtonListener());
+ this.signCheckBox.setSelection(true);
+
+ //--------------
+
+ // Keystore label
+ Label keystoreLabel = new Label(signingGroup, SWT.NONE);
+ keystoreLabel.setText(Messages.PACKAGE_EXPORT_WIZARD_AREA_SIGN_KEYSTORE_LABEL);
+ GridData gridData = new GridData(SWT.BEGINNING, SWT.CENTER, false, false, 1, 1);
+ keystoreLabel.setLayoutData(gridData);
+
+ // Keystore combo
+ this.keystores = new Combo(signingGroup, SWT.DROP_DOWN | SWT.READ_ONLY);
+ gridData = new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1);
+ gridData.widthHint = 250;
+ this.keystores.setLayoutData(gridData);
+
+ //populate mapped keystores from view
+ populateKeystoresFromView();
+
+ keystores.addSelectionListener(new SelectionAdapter()
+ {
+ @Override
+ public void widgetSelected(SelectionEvent e)
+ {
+ IKeyStore selectedKeystore = keystoreList.get(keystores.getSelectionIndex());
+ boolean keysLoaded = loadKeys(selectedKeystore);
+
+ if (!keysLoaded)
+ {
+ ITreeNode keystoreNode = (ITreeNode) selectedKeystore;
+
+ if (keystoreNode.getNodeStatus().getCode() == IKeyStore.WRONG_KEYSTORE_TYPE_ERROR_CODE)
+ {
+ EclipseUtils.showInformationDialog(
+ Messages.PackageExportWizardArea_WrongKeystoreTypeDialogTitle,
+ NLS.bind(
+ Messages.PackageExportWizardArea_WrongKeystoreTypeDialogMessage,
+ keystoreNode.getName()));
+ }
+
+ restorePreviousSelections();
+ }
+ else
+ {
+ previousSelectedKeystore = getSelectedKeyStore();
+ previousSelectedKey = getSelectedKey();
+ }
+ setEnablement(true);
+ }
+ });
+
+ if (keystores.getItemCount() <= 0)
+ {
+ signCheckBox.setSelection(false);
+ }
+
+ // Add keystore buttons
+ buttonExisting = new Button(signingGroup, SWT.NONE);
+ buttonAddNew = new Button(signingGroup, SWT.NONE);
+
+ gridData = new GridData(SWT.FILL, SWT.CENTER, false, false, 1, 1);
+ buttonExisting.setLayoutData(gridData);
+ gridData = new GridData(SWT.FILL, SWT.CENTER, false, false, 1, 1);
+ buttonAddNew.setLayoutData(gridData);
+
+ buttonExisting.setText(Messages.PackageExportWizardArea_MenuItem_UseExistent);
+ buttonExisting.addSelectionListener(new SelectionAdapter()
+ {
+ @Override
+ public void widgetSelected(SelectionEvent e)
+ {
+ openSelectKeystoreWizard();
+ }
+ });
+
+ buttonAddNew.setText(Messages.PackageExportWizardArea_MenuItem_AddNew);
+ buttonAddNew.addSelectionListener(new SelectionAdapter()
+ {
+ @Override
+ public void widgetSelected(SelectionEvent e)
+ {
+ CreateKeystoreWizard createKeystoreWizard =
+ new CreateKeystoreWizard(new CreateKeystoreJobListener());
+
+ WizardDialog dialog =
+ new WizardDialog(parentComposite.getShell(), createKeystoreWizard);
+
+ //open the wizard to create keystores
+ dialog.create();
+ if (dialog.open() == Window.OK)
+ {
+ //user really created keystore
+ String keystorePassword = createKeystoreWizard.getCreatedKeystorePassword();
+ addKeystore(createKeystoreWizard.getCreatedKeystoreNode(), true,
+ keystorePassword);
+ //required for case when just keystore is created, but no key is created
+ //DO NOT call selectKeystore here because it has loadKeys, that may conflict with createKey (if a key is created), for this case the CreateKeystoreJobListener will solve the problem.
+ selectKeystoreWithoutLoadingKeys(createKeystoreWizard.getCreatedKeystoreNode());
+ }
+ }
+ });
+
+ // Key label
+ Label keyLabel = new Label(signingGroup, SWT.NONE);
+ keyLabel.setText(Messages.PACKAGE_EXPORT_WIZARD_AREA_SIGN_KEY_LABEL);
+ gridData = new GridData(SWT.BEGINNING, SWT.CENTER, false, false, 1, 1);
+ keyLabel.setLayoutData(gridData);
+
+ // Key Combo
+ this.keysCombo = new Combo(signingGroup, SWT.DROP_DOWN | SWT.READ_ONLY);
+ gridData = new GridData(SWT.FILL, SWT.CENTER, true, false, 2, 1);
+ this.keysCombo.setLayoutData(gridData);
+
+ if (keysCombo.getItemCount() <= 0)
+ {
+ signCheckBox.setSelection(false);
+ }
+
+ keysCombo.addSelectionListener(new SelectionAdapter()
+ {
+ @Override
+ public void widgetSelected(SelectionEvent e)
+ {
+ super.widgetSelected(e);
+
+ previousSelectedKey = getSelectedKey();
+
+ parentComposite.notifyListeners(SWT.Modify, new Event());
+ }
+ });
+
+ // Add keystore button
+ buttonAddKey = new Button(signingGroup, SWT.PUSH);
+ buttonAddKey.setText(Messages.PackageExportWizardArea_AddKeyButton_Text);
+ gridData = new GridData(SWT.FILL, SWT.CENTER, false, false, 1, 1);
+ buttonAddKey.setLayoutData(gridData);
+
+ buttonAddKey.addSelectionListener(new SelectionAdapter()
+ {
+
+ @Override
+ public void widgetSelected(SelectionEvent e)
+ {
+ IKeyStore keyStore = null;
+ if (keystores.getSelectionIndex() >= 0)
+ {
+ keyStore =
+ PackageExportWizardArea.keystoreList.get(keystores.getSelectionIndex());
+ }
+
+ if (keyStore != null)
+ {
+ openNewKeyWizard(keyStore, new CreateKeyJobListener());
+ }
+
+ setEnablement(true);
+
+ }
+ });
+
+ setEnablement(false);
+
+ }
+
+ /**
+ * This class is required because when creating a new key during export, the threads are not synchronized (createKey and loadKeys).
+ * Otherwise, wizard page could not finish accordingly.
+ */
+ private class CreateKeyJobListener extends JobChangeAdapter
+ {
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.runtime.jobs.JobChangeAdapter#done(org.eclipse.core.runtime.jobs.IJobChangeEvent)
+ */
+ @Override
+ public void done(final IJobChangeEvent event)
+ {
+ PlatformUI.getWorkbench().getDisplay().syncExec(new Runnable()
+ {
+ @Override
+ public void run()
+ {
+
+ IKeyStore keyStore = ((CreateKeyJob) event.getJob()).getKeyStore();
+ String key = ((CreateKeyJob) event.getJob()).getCreatedKeyAlias();
+
+ loadKeys(keyStore);
+ selectKey(key);
+ }
+ });
+ }
+ }
+
+ /**
+ * This class is required because when creating a new key and new keystore during export, the threads are not synchronized (createKey and loadKeys).
+ * Otherwise, wizard page could not finish accordingly.
+ */
+ private class CreateKeystoreJobListener extends JobChangeAdapter
+ {
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.runtime.jobs.JobChangeAdapter#done(org.eclipse.core.runtime.jobs.IJobChangeEvent)
+ */
+ @Override
+ public void done(final IJobChangeEvent event)
+ {
+ PlatformUI.getWorkbench().getDisplay().syncExec(new Runnable()
+ {
+ @Override
+ public void run()
+ {
+ IKeyStore keyStore = ((CreateKeyJob) event.getJob()).getKeyStore();
+ String key = ((CreateKeyJob) event.getJob()).getCreatedKeyAlias();
+
+ loadKeys(keyStore);
+ selectKeystore(keyStore);
+ selectKey(key);
+ }
+ });
+ }
+ }
+
+ /**
+ * Inserts keystores mapped into keystore combo
+ */
+ protected void populateKeystoresFromView()
+ {
+ try
+ {
+ //when reopening wizard, we need to clear the list
+ if (!keystoreList.isEmpty())
+ {
+ keystoreList.clear();
+ keystores.removeAll();
+ }
+ if ((KeyStoreManager.getInstance() != null)
+ && (KeyStoreManager.getInstance().getKeyStores() != null))
+ {
+ List<IKeyStore> keyStores = KeyStoreManager.getInstance().getKeyStores();
+ if (keyStores != null)
+ {
+ for (IKeyStore keyStore : keyStores)
+ {
+ insertKeystoreIntoCombo(keyStore);
+ }
+ }
+ }
+ }
+ catch (KeyStoreManagerException e)
+ {
+ StudioLogger.error(PackageExportWizardArea.class, "Error retrieving keystore list", //$NON-NLS-1$
+ e);
+ }
+ }
+
+ protected void insertKeystoreIntoCombo(IKeyStore iKeyStore)
+ {
+ File ksFile = iKeyStore.getFile();
+ keystores.add(ksFile.getName() + " - ( " + ksFile.getPath() + " )"); //$NON-NLS-1$ //$NON-NLS-2$
+ keystoreList.add(iKeyStore);
+ }
+
+ /**
+ * Adds keystore to keystores combo box and model from GUI
+ * @param iKeyStore
+ * @param canSavePassword true if create/select and need to import into view, false if selecting and does NOT need to import into the view
+ * @param password to retrieve keys
+ */
+ protected void addKeystore(IKeyStore iKeyStore, boolean canSavePassword, String password)
+ {
+ keystorePasswords.put(iKeyStore, password);
+ insertKeystoreIntoCombo(iKeyStore);
+ }
+
+ /**
+ * Update the Default Destination The behavior is: if only one project is
+ * selected, then default destination text is set to the folder of this
+ * project if have more than one project selected or none than default
+ * destination text is set to workspace root
+ */
+ private void updateDefaultDestination()
+ {
+ ArrayList<IProject> selectedProjects = getSelectedProjects();
+ if (this.defaultDestination.getSelection())
+ {
+ if ((selectedProjects != null) && (selectedProjects.size() == 1))
+ {
+ this.destinationText.setText(selectedProjects.get(0).getLocation().toOSString());
+ }
+ else
+ {
+ this.destinationText.setText(ResourcesPlugin.getWorkspace().getRoot().getLocation()
+ .toOSString());
+ }
+ }
+ }
+
+ /**
+ * Get selected projects from the tree
+ *
+ * @return
+ */
+ private ArrayList<IProject> getSelectedProjects()
+ {
+ ArrayList<IProject> projects = new ArrayList<IProject>();
+ for (TreeItem item : tree.getItems())
+ {
+ if (item.getChecked())
+ {
+ projects.add((IProject) item.getData());
+ }
+ }
+ return projects;
+ }
+
+ /**
+ * Get selected items from the tree
+ *
+ * @return
+ */
+ private ArrayList<TreeItem> getSelectedItems()
+ {
+ ArrayList<TreeItem> items = new ArrayList<TreeItem>();
+ for (TreeItem item : tree.getItems())
+ {
+ if (item.getChecked())
+ {
+ items.add(item);
+ }
+ }
+ return items;
+ }
+
+ /**
+ * This method creates the entire structure of the wizard page. Also, it
+ * stores the user input.
+ *
+ * @param parent
+ * Composite for all the elements.
+ */
+ public void createControl(Composite parent)
+ {
+ Composite mainComposite = new Composite(parent, SWT.NONE);
+ GridLayout gridLayout = new GridLayout(3, false);
+ GridData gridData = new GridData(SWT.FILL, SWT.FILL, true, true, 3, 1);
+ mainComposite.setLayout(gridLayout);
+ mainComposite.setLayoutData(gridData);
+
+ // Tree structure
+ this.tree = new Tree(mainComposite, SWT.CHECK | SWT.MULTI | SWT.V_SCROLL | SWT.BORDER);
+ gridData = new GridData(SWT.FILL, SWT.FILL, true, true, 2, 2);
+ gridData.heightHint = 150;
+ this.tree.setLayoutData(gridData);
+ this.tree.addListener(SWT.Selection, new TreeListener());
+
+ // Add all the descriptor files to the tree
+ populateTree();
+
+ // Select All Button
+ this.selectAllButton = new Button(mainComposite, SWT.PUSH);
+ gridData = new GridData(SWT.FILL, SWT.UP, false, false, 1, 1);
+ this.selectAllButton.setLayoutData(gridData);
+ this.selectAllButton.setText(Messages.PACKAGE_EXPORT_WIZARD_AREA_SELECT_ALL_BUTTON);
+ this.selectAllButton.addListener(SWT.Selection, new TreeSelectionButtonListener(true));
+
+ // Deselect All button
+ this.deselectAllButton = new Button(mainComposite, SWT.PUSH);
+ gridData = new GridData(SWT.FILL, SWT.UP, false, false, 1, 1);
+ this.deselectAllButton.setLayoutData(gridData);
+ this.deselectAllButton.setText(Messages.PACKAGE_EXPORT_WIZARD_AREA_DESELECT_ALL_BUTTON);
+ this.deselectAllButton.addListener(SWT.Selection, new TreeSelectionButtonListener(false));
+
+ createDestinationGroup(mainComposite);
+
+ if (this.signingEnabled)
+ {
+ createSignGroup(mainComposite);
+ }
+
+ String path = ResourcesPlugin.getWorkspace().getRoot().getLocation().toOSString();
+ Object prj = this.selection.getFirstElement();
+ if ((prj != null) && (this.selection.size() == 1))
+ {
+ if (prj instanceof IProject)
+ {
+ String realPath = ((IProject) prj).getLocation().toOSString();
+ this.destinationText.setText(realPath);
+ }
+ else if (prj instanceof IResource)
+ {
+ String realPath = path + ((IResource) prj).getProject().getFullPath().toOSString();
+ this.destinationText.setText(realPath);
+ }
+ }
+ else
+ {
+ this.destinationText.setText(path);
+ }
+ /**
+ * Force the focus to parent. This action make help ok
+ */
+ if (!parent.isFocusControl())
+ {
+ parent.forceFocus();
+ }
+ }
+
+ /**
+ * Get all projects severities to avoid user selects erroneous projects
+ */
+ private void validateProjects()
+ {
+ try
+ {
+ for (IProject project : ResourcesPlugin.getWorkspace().getRoot().getProjects())
+ {
+ if (project.isOpen())
+ {
+ int sev = project.findMaxProblemSeverity(null, true, IResource.DEPTH_INFINITE);
+ int projectSev;
+ switch (sev)
+ {
+ case IMarker.SEVERITY_ERROR:
+ projectSev = IMessageProvider.ERROR;
+ break;
+ case IMarker.SEVERITY_INFO:
+ projectSev = IMessageProvider.INFORMATION;
+ break;
+ case IMarker.SEVERITY_WARNING:
+ projectSev = IMessageProvider.WARNING;
+ break;
+ default:
+ projectSev = IMessageProvider.NONE;
+ break;
+ }
+ projectSeverity.put(project, new Integer(projectSev));
+ }
+ }
+ }
+ catch (CoreException e)
+ {
+ StudioLogger.error(PackageExportWizardArea.class, "Impossible to get project severity"); //$NON-NLS-1$
+ }
+ }
+
+ /**
+ * Can finish used in {@link IWizardPage} This method validate the page and
+ * change the severity/message.
+ *
+ * @return true if can finish this wizard, false otherwise
+ */
+ public boolean canFinish()
+ {
+ String messageAux = null;
+ int severity_aux = IMessageProvider.NONE;
+
+ /*
+ * Check is has selected items
+ */
+ if (!hasItemChecked())
+ {
+ messageAux = Messages.SELECTOR_MESSAGE_NO_SELECTION;
+ if (treeSelectionChanged)
+ {
+ severity_aux = IMessageProvider.ERROR;
+ }
+ else
+ {
+ severity_aux = IMessageProvider.INFORMATION;
+ }
+ }
+
+ // validate if some selected project has errors
+ if (messageAux == null)
+ {
+ Iterator<IProject> iterator = getSelectedProjects().iterator();
+ while (iterator.hasNext() && (severity_aux != IMessageProvider.ERROR))
+ {
+ severity_aux = projectSeverity.get(iterator.next());
+ }
+ if (severity_aux == IMessageProvider.ERROR)
+ {
+ messageAux = Messages.PACKAGE_EXPORT_WIZARD_AREA_PROJECTS_WITH_ERRORS_SELECTED;
+
+ }
+ }
+
+ /*
+ * Check if the selected location is valid, even if non existent.
+ */
+ IPath path = new Path(this.destinationText.getText());
+
+ if (!this.defaultDestination.getSelection() && (messageAux == null))
+ {
+ // Test if path is blank, to warn user instead of show an error
+ // message
+ if (this.destinationText.getText().equals("")) //$NON-NLS-1$
+ {
+ messageAux = Messages.SELECTOR_MESSAGE_LOCATION_ERROR_INVALID;
+ severity_aux = IMessageProvider.INFORMATION;
+ }
+
+ /*
+ * Do Win32 Validation
+ */
+ if ((messageAux == null) && Platform.getOS().equalsIgnoreCase(Platform.OS_WIN32))
+ {
+ // test path size
+ if (path.toString().length() > 255)
+ {
+ messageAux = Messages.SELECTOR_MESSAGE_LOCATION_ERROR_PATH_TOO_LONG;
+ severity_aux = IMessageProvider.ERROR;
+ }
+ String device = path.getDevice();
+ File deviceFile = null;
+ if (device != null)
+ {
+ deviceFile = new File(path.getDevice());
+ }
+
+ if ((device != null) && !deviceFile.exists())
+ {
+ messageAux =
+ Messages.SELECTOR_MESSAGE_LOCATION_ERROR_INVALID_DEVICE + " [" + device //$NON-NLS-1$
+ + "]"; //$NON-NLS-1$
+ severity_aux = IMessageProvider.ERROR;
+ }
+
+ }
+ // test if path is absolute
+ if (messageAux == null)
+ {
+ if (!path.isAbsolute())
+ {
+ messageAux = Messages.SELECTOR_MESSAGE_LOCATION_ERROR_INVALID;
+ severity_aux = IMessageProvider.ERROR;
+ }
+ }
+
+ if (messageAux == null)
+ {
+ for (String folderName : path.segments())
+ {
+ if (!ResourcesPlugin.getWorkspace().validateName(folderName, IResource.FOLDER)
+ .isOK())
+ {
+ messageAux = Messages.SELECTOR_MESSAGE_LOCATION_ERROR_INVALID;
+ severity_aux = IMessageProvider.ERROR;
+
+ }
+ }
+ }
+
+ if ((messageAux == null) && path.toFile().exists() && !path.toFile().isDirectory())
+ {
+ messageAux = Messages.SELECTOR_MESSAGE_LOCATION_ERROR_NOT_DIRECTORY;
+ severity_aux = IMessageProvider.ERROR;
+ }
+ }
+
+ /*
+ * Check if there are available certificates and if selection isn't null
+ */
+ if (messageAux == null)
+ {
+
+ if (this.signingEnabled && (this.signCheckBox != null)
+ && this.signCheckBox.getSelection()
+ && !((this.keystores != null) && (this.keystores.getItemCount() > 0)))
+ {
+ messageAux = Messages.PACKAGE_EXPORT_WIZARD_AREA_SIGN_NO_KEYSTORE_AVAILABLE;
+ severity_aux = IMessageProvider.ERROR;
+ }
+
+ else if (this.signCheckBox.getSelection()
+ && !((this.keysCombo != null) && (this.keysCombo.getItemCount() > 0)
+ && (this.keysCombo.getSelectionIndex() >= 0)
+ && (this.keysCombo.getItem(this.keysCombo.getSelectionIndex()) != null) && !this.keysCombo
+ .getItem(this.keysCombo.getSelectionIndex()).equals(""))) //$NON-NLS-1$
+ {
+ messageAux = Messages.PACKAGE_EXPORT_WIZARD_AREA_SIGN_NO_KEYSTORE_OR_KEY_SELECTED;
+ severity_aux = IMessageProvider.ERROR;
+ }
+
+ }
+
+ if (messageAux == null)
+ {
+ if (!this.signCheckBox.getSelection())
+ {
+ messageAux = Messages.PACKAGE_EXPORT_WIZARD_AREA_UNSIGNEDPACKAGE_WARNING;
+ severity_aux = IMessageProvider.WARNING;
+ }
+ }
+
+ /*
+ * Setting message
+ */
+ if (messageAux == null)
+ {
+ messageAux = Messages.PACKAGE_EXPORT_WIZARD_AREA_DESCRIPTION;
+ severity_aux = IMessageProvider.NONE;
+ }
+ this.message = messageAux;
+ this.severity = severity_aux;
+
+ boolean result;
+ switch (severity_aux)
+ {
+ case IMessageProvider.ERROR:
+ // ERROR. can't finish wizard
+ result = false;
+ break;
+
+ case IMessageProvider.WARNING:
+ // WARNING. ok to finish the wizard
+ result = true;
+ break;
+
+ case IMessageProvider.INFORMATION:
+ // INFORMATION. Path is empty, so it's NOT OK to finish the wizard
+ result = false;
+ break;
+
+ default:
+ // by default, canFinish returns true
+ result = true;
+ break;
+
+ }
+
+ return result;
+ }
+
+ /**
+ * Check if we have at least one item checked
+ *
+ * @param items
+ * @return true if some of tree item is checked, false otherwise
+ */
+ private boolean hasItemChecked()
+ {
+ boolean checked = false;
+ TreeItem[] items = tree.getItems();
+ int i = 0;
+ while (!checked && (i < items.length))
+ {
+ if (items[i].getChecked())
+ {
+ checked = true;
+ }
+ i++;
+ }
+ return checked;
+ }
+
+ /**
+ * Check if the destination folder is valid during finish action If the
+ * package don't exist, ask to create
+ *
+ * @return true if the destination is valid
+ * @throws CoreException
+ * when errors with directory creation occurs
+ */
+ private boolean checkDestination() throws CoreException
+ {
+ boolean destinationOK = true;
+ if (!defaultDestination.getSelection())
+ {
+ File destination = new File(destinationText.getText());
+ if (!destination.exists())
+ {
+ destinationOK = createDestinationFolderDialog();
+ if (destinationOK)
+ {
+ if (!destination.mkdirs())
+ {
+ throw new CoreException(new Status(IStatus.ERROR,
+ PackagingUIPlugin.PLUGIN_ID,
+ Messages.PACKAGE_EXPORT_WIZARD_AREA_ERROR_DESTINATION_CHECK));
+ }
+ }
+ }
+ }
+ else
+ {
+ for (TreeItem item : getSelectedItems())
+ {
+ IProject project = (IProject) item.getData();
+ IPath dist =
+ project.getLocation().append(
+ CertificateManagerActivator.PACKAGE_PROJECT_DESTINATION);
+ File file = dist.toFile();
+ if (file.exists() && !file.isDirectory())
+ {
+ if (!file.delete())
+ {
+ throw new CoreException(new Status(IStatus.ERROR,
+ PackagingUIPlugin.PLUGIN_ID,
+ Messages.PACKAGE_EXPORT_WIZARD_AREA_ERROR_DESTINATION_CHECK));
+ }
+ }
+ if (!file.exists())
+ {
+ if (!file.mkdir())
+ {
+ throw new CoreException(new Status(IStatus.ERROR,
+ PackagingUIPlugin.PLUGIN_ID,
+ Messages.PACKAGE_EXPORT_WIZARD_AREA_ERROR_DESTINATION_CHECK));
+ }
+ }
+ project.refreshLocal(IResource.DEPTH_ONE, new NullProgressMonitor());
+ }
+ }
+ return destinationOK;
+ }
+
+ /**
+ * Create a destination folder, asking user's permission
+ *
+ * @return
+ */
+ private boolean createDestinationFolderDialog()
+ {
+ MessageBox box =
+ new MessageBox(parentComposite.getShell(), SWT.ICON_QUESTION | SWT.YES | SWT.NO);
+ box.setMessage(Messages.PACKAGE_EXPORT_WIZARD_AREA_CREATE_DIRECTORIES_BOX_MESSAGE);
+ box.setText(Messages.PACKAGE_EXPORT_WIZARD_AREA_CREATE_DIRECTORIES_BOX_TITLE);
+ return box.open() == SWT.YES;
+ }
+
+ /**
+ * do the finish: Create the package for each selected descriptor
+ */
+ public boolean performFinish()
+ {
+ final boolean[] finished =
+ {
+ false
+ };
+ boolean destOK = false;
+ final MultiStatus status =
+ new MultiStatus(PackagingUIPlugin.PLUGIN_ID, IStatus.OK, "", null); //$NON-NLS-1$
+ ProgressMonitorDialog monitorDialog = null;
+ String DESCRIPTION_TO_LOG = StudioLogger.DESCRIPTION_DEFAULT;
+
+ try
+ {
+ destOK = checkDestination();
+ }
+ catch (CoreException e)
+ {
+ status.add(e.getStatus());
+ }
+
+ if (destOK)
+ {
+ monitorDialog = new ProgressMonitorDialog(parentComposite.getShell());
+ try
+ {
+ monitorDialog.run(false, false, new IRunnableWithProgress()
+ {
+
+ @Override
+ public void run(IProgressMonitor aMonitor) throws InvocationTargetException,
+ InterruptedException
+ {
+ int finishSize =
+ getSelectedItems().size()
+ * PackagingUIPlugin.PROGRESS_MONITOR_MULTIPLIER;
+ SubMonitor monitor = SubMonitor.convert(aMonitor);
+ monitor.beginTask(Messages.PACKAGE_EXPORT_WIZARD_AREA_FINISH_ACTION_LABEL,
+ finishSize);
+ IPath exportDestinationFolder = new Path(destinationText.getText());
+ IPath exportDestinationFile = null;
+
+ for (TreeItem item : getSelectedItems())
+ {
+ // get the eclipse project as a java project to get
+ // the project
+ // destination
+ IProject eclipseProject = (IProject) item.getData();
+ try
+ {
+ monitor.worked(PackagingUIPlugin.PROGRESS_MONITOR_MULTIPLIER / 4);
+
+ JavaProject javaProject = new JavaProject();
+ javaProject.setProject(eclipseProject);
+
+ // find all packages built by Android builder
+ Map<String, String> apkConfigurations =
+ SdkUtils.getAPKConfigurationsForProject(eclipseProject);
+
+ Set<String> apkConfNames = new HashSet<String>();
+ if (apkConfigurations != null)
+ {
+ apkConfNames.addAll(apkConfigurations.keySet());
+ }
+ apkConfNames.add(""); // the default package //$NON-NLS-1$
+
+ SubMonitor submonitor =
+ monitor.newChild(PackagingUIPlugin.PROGRESS_MONITOR_MULTIPLIER / 4);
+
+ submonitor.beginTask(
+ Messages.PACKAGE_EXPORT_WIZARD_AREA_EXPORTING_ACTION_LABEL,
+ 3 * PackagingUIPlugin.PROGRESS_MONITOR_MULTIPLIER
+ * apkConfNames.size());
+
+ for (String apkConfName : apkConfNames)
+ {
+
+ String apkName =
+ eclipseProject.getName()
+ + (apkConfName.isEmpty() ? apkConfName : "-" //$NON-NLS-1$ //$NON-NLS-2$
+ + apkConfName);
+ if (defaultDestination.getSelection())
+ {
+ exportDestinationFolder =
+ eclipseProject
+ .getLocation()
+ .append(CertificateManagerActivator.PACKAGE_PROJECT_DESTINATION);
+ }
+ exportDestinationFile =
+ exportDestinationFolder
+ .append(apkName)
+ .addFileExtension(
+ CertificateManagerActivator.PACKAGE_EXTENSION);
+ File file = exportDestinationFile.toFile();
+ submonitor
+ .worked(PackagingUIPlugin.PROGRESS_MONITOR_MULTIPLIER);
+
+ //always export unsigned package
+ AdtUtils.exportUnsignedReleaseApk(javaProject.getProject(),
+ file, submonitor);
+
+ submonitor
+ .worked(PackagingUIPlugin.PROGRESS_MONITOR_MULTIPLIER);
+
+ if (signCheckBox.getSelection())
+ {
+ //sign the package if required
+ IStatus signStatus = signPackage(eclipseProject, file);
+ status.add(signStatus);
+ }
+
+ //zipalign the file and we are done exporting the package
+ PackageFile.zipAlign(file);
+
+ submonitor
+ .worked(PackagingUIPlugin.PROGRESS_MONITOR_MULTIPLIER);
+ }
+ submonitor.done();
+ }
+ catch (CoreException e)
+ {
+ StudioLogger.error(PackageExportWizardArea.class,
+ "Error while building project or getting project output folder" //$NON-NLS-1$
+ + eclipseProject.getName(), e);
+ status.add(new Status(IStatus.ERROR, PackagingUIPlugin.PLUGIN_ID,
+ Messages.PACKAGE_EXPORT_WIZARD_AREA_ERROR_PROJECT_BUILD
+ + " " + eclipseProject.getName())); //$NON-NLS-1$
+ }
+ finally
+ {
+ try
+ {
+ eclipseProject.refreshLocal(
+ IResource.DEPTH_INFINITE,
+ monitor.newChild(PackagingUIPlugin.PROGRESS_MONITOR_MULTIPLIER / 4));
+ }
+ catch (CoreException e)
+ {
+ // do nothing
+ }
+ }
+ monitor.worked(PackagingUIPlugin.PROGRESS_MONITOR_MULTIPLIER / 4);
+ }
+ finished[0] = true;
+
+ }
+ });
+ }
+ catch (Exception e)
+ {
+ StudioLogger.warn("Error finishing package export.");
+ }
+ }
+
+ if (!status.isOK())
+ {
+ status.getMessage();
+ DESCRIPTION_TO_LOG = Messages.PACKAGE_EXPORT_WIZARD_AREA_READONLY_TITLE;
+ ErrorDialog.openError(parentComposite.getShell(),
+ Messages.PACKAGE_EXPORT_WIZARD_AREA_READONLY_TITLE,
+ Messages.PACKAGE_EXPORT_WIZARD_AREA_READONLY_MESSAGE, status);
+ }
+
+ // Saving usage data
+ try
+ {
+ StudioLogger.collectUsageData(StudioLogger.WHAT_APP_MANAGEMENT_PACKAGE,
+ StudioLogger.KIND_APP_MANAGEMENT, DESCRIPTION_TO_LOG,
+ PackagingUIPlugin.PLUGIN_ID, PackagingUIPlugin.getDefault().getBundle()
+ .getVersion().toString());
+ }
+ catch (Throwable e)
+ {
+ // Do nothing, but error on the log should never prevent app from
+ // working
+ }
+
+ return finished[0];
+ }
+
+ /**
+ *
+ * @return key entry password
+ */
+ public String getKeyEntryPassword()
+ {
+ String keyEntryPassword = new String();
+ try
+ {
+ keyEntryPassword =
+ getSelectedKeyStore().getPasswordProvider().getPassword(
+ this.keysCombo.getItem(this.keysCombo.getSelectionIndex()), true);
+ }
+ catch (KeyStoreManagerException e)
+ {
+ StudioLogger.error(this.getClass(), "Error retrieving keys entry password", e); //$NON-NLS-1$
+ }
+ return keyEntryPassword;
+ }
+
+ /**
+ * @param eclipseProject The project being exported.
+ * @param exportedPackage The package to be signed
+ * @throws InvalidPasswordException
+ * @throws KeyStoreManagerException
+ */
+ private IStatus signPackage(IProject eclipseProject, File exportedPackage)
+ {
+ IStatus status = Status.OK_STATUS;
+
+ String keyAlias = keysCombo.getItem(keysCombo.getSelectionIndex());
+ String keystorePassword = getKeyStorePassword(getSelectedKeyStore());
+ String keyPassword = getKeyEntryPassword();
+
+ JarFile jar = null;
+ try
+ {
+ PackageFile pack = null;
+ boolean keepTrying;
+ if (keyPassword != null)
+ {
+ keepTrying = true;
+ }
+ else
+ {
+ keepTrying = false;
+
+ }
+ while (keepTrying)
+ {
+ try
+ {
+ // Open package and remove signature
+ jar = new JarFile(exportedPackage);
+ pack = new PackageFile(jar);
+ pack.removeMetaEntryFiles();
+
+ // Sign the new package
+ PackageFileSigner.signPackage(pack,
+ getSelectedKeyStore().getEntry(keyAlias, keystorePassword),
+ keyPassword, PackageFileSigner.MOTODEV_STUDIO);
+ keepTrying = false;
+ }
+ catch (UnrecoverableKeyException sE)
+ {
+ try
+ {
+ keyPassword =
+ getSelectedKeyStore().getPasswordProvider().getPassword(keyAlias,
+ true, false);
+ }
+ catch (KeyStoreManagerException e)
+ {
+ status =
+ new Status(Status.ERROR, CertificateManagerActivator.PLUGIN_ID,
+ e.getMessage());
+ StudioLogger.error(PackageExportWizardArea.this.getClass(),
+ "Could not retrieve key password on export: " + e.getMessage()); //$NON-NLS-1$
+ }
+ if (keyPassword == null)
+ {
+ keepTrying = false;
+ status = Status.CANCEL_STATUS;
+ }
+ else
+ {
+ keepTrying = true;
+ }
+ }
+ catch (InvalidPasswordException e)
+ {
+ // Should never happen as the entry alias is only available if the keystore password
+ // was typed correctly.
+ // Unless the user changed the keystore password outside the tool while exporting the package.
+ status =
+ new Status(Status.ERROR, CertificateManagerActivator.PLUGIN_ID,
+ e.getMessage());
+ }
+ catch (KeyStoreManagerException e)
+ {
+ // Should never happen as the entry alias is only available if the keystore password
+ // was typed correctly.
+ // Unless the user changed the keystore password outside the tool while exporting the package.
+ status =
+ new Status(Status.ERROR, CertificateManagerActivator.PLUGIN_ID,
+ e.getMessage());
+ }
+ }
+
+ if (status.isOK())
+ {
+ FileOutputStream fileToWrite = null;
+ try
+ {
+ // Write the new package file
+ fileToWrite = new FileOutputStream(exportedPackage);
+ pack.write(fileToWrite);
+ }
+ finally
+ {
+
+ fileToWrite.close();
+ }
+ }
+ else
+ {
+ EclipseUtils.showErrorDialog("Package Signing", "Could not sign the package.");
+ }
+ }
+ catch (IOException e)
+ {
+ StudioLogger.error(PackageExportWizardArea.this.getClass(),
+ "Could not sign the package: " + e.getMessage()); //$NON-NLS-1$
+
+ status =
+ new Status(IStatus.ERROR, PackagingUIPlugin.PLUGIN_ID,
+ Messages.PackageExportWizardArea_ErrorWritingSignedPackageFile + " " //$NON-NLS-1$
+ + eclipseProject.getName());
+ }
+ catch (SignException e)
+ {
+ StudioLogger.error(PackageExportWizardArea.this.getClass(),
+ "Could not sign the package: " + e.getMessage()); //$NON-NLS-1$
+
+ status =
+ new Status(IStatus.ERROR, PackagingUIPlugin.PLUGIN_ID,
+ Messages.PackageExportWizardArea_ErrorSigningPackage
+ + " " + eclipseProject.getName()); //$NON-NLS-1$
+ }
+ finally
+ {
+ try
+ {
+ jar.close();
+ }
+ catch (IOException e)
+ {
+ StudioLogger.error(PackageExportWizardArea.this.getClass(),
+ "Could not sign the package: " + e.getMessage()); //$NON-NLS-1$
+ }
+ }
+ return status;
+ }
+
+ /**
+ * Get the area message
+ *
+ * @return the message for the wizard
+ */
+ public String getMessage()
+ {
+ return this.message;
+ }
+
+ /**
+ * Get the area error severity
+ *
+ * @return
+ */
+ public int getSeverity()
+ {
+ return this.severity;
+ }
+
+ /**
+ * "Browser..." button for package destination.
+ *
+ */
+ private class PackageDestinationButtonListener implements Listener
+ {
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * org.eclipse.swt.widgets.Listener#handleEvent(org.eclipse.swt.widgets
+ * .Event)
+ */
+ @Override
+ public void handleEvent(Event event)
+ {
+ String path = destinationText.getText();
+
+ if (path.equals("")) //$NON-NLS-1$
+ {
+ path =
+ ResourcesPlugin.getWorkspace().getRoot().getLocation().toOSString()
+ .subSequence(0, 2).toString();
+ }
+
+ String executablePath = directoryDialog(path);
+
+ if ((executablePath != null) && !executablePath.equals("")) //$NON-NLS-1$
+ {
+ destinationText.setText(executablePath);
+ }
+ parentComposite.notifyListeners(SWT.Modify, new Event());
+ }
+ }
+
+ /**
+ * Update the default destination status
+ *
+ */
+ private class DestinationTextListener implements Listener
+ {
+
+ @Override
+ public void handleEvent(Event event)
+ {
+ if (!defaultDestination.getSelection())
+ {
+ parentComposite.notifyListeners(SWT.Modify, new Event());
+ }
+ }
+ }
+
+ /**
+ * A Listener for the Tree.
+ *
+ */
+ private class TreeListener implements Listener
+ {
+ @Override
+ public void handleEvent(Event event)
+ {
+ updateDefaultDestination();
+ parentComposite.notifyListeners(SWT.Modify, new Event());
+ treeSelectionChanged = true;
+ }
+ }
+
+ /**
+ * Enable Destination Text.
+ *
+ */
+ private class DefaultDestinationListener implements Listener
+ {
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * org.eclipse.swt.widgets.Listener#handleEvent(org.eclipse.swt.widgets
+ * .Event)
+ */
+ @Override
+ public void handleEvent(Event event)
+ {
+ Button defaultDestination = (Button) event.widget;
+ destinationText.setEnabled(!defaultDestination.getSelection());
+ packageDestinationBrowseButton.setEnabled(!defaultDestination.getSelection());
+ if (defaultDestination.getSelection())
+ {
+ updateDefaultDestination();
+ }
+ else
+ {
+ destinationText.setText(""); //$NON-NLS-1$
+ }
+ destinationText.notifyListeners(SWT.Modify, new Event());
+ parentComposite.notifyListeners(SWT.Modify, new Event());
+
+ }
+
+ }
+
+ /**
+ *
+ * @param notify Notifies listener that validates if Finish can be enabled
+ */
+ private void setEnablement(boolean notify)
+ {
+
+ boolean signEnabled = signCheckBox.getSelection();
+
+ keysCombo.setEnabled(signEnabled);
+ keystores.setEnabled(signEnabled);
+ buttonAddKey.setEnabled(signEnabled);
+ //toolbar.setEnabled(signEnabled);
+ buttonExisting.setEnabled(signEnabled);
+ buttonAddNew.setEnabled(signEnabled);
+
+ if (signEnabled)
+ {
+
+ if (keystores.getSelectionIndex() < 0)
+ {
+ //no keystore selected, clear keys combo
+ buttonAddKey.setEnabled(false);
+ keysCombo.setEnabled(false);
+ keysCombo.removeAll();
+ }
+
+ if (keystores.getItemCount() <= 0)
+ {
+ keysCombo.setEnabled(false);
+ keystores.setEnabled(false);
+ buttonAddKey.setEnabled(false);
+ //toolbar.setEnabled(true);
+ buttonExisting.setEnabled(signEnabled);
+ buttonAddNew.setEnabled(signEnabled);
+ }
+ }
+
+ if (notify)
+ {
+ parentComposite.notifyListeners(SWT.Modify, new Event());
+ }
+
+ }
+
+ protected void openSelectKeystoreWizard()
+ {
+ SelectExistentKeystoreWizard selectExistentKeystoreWizard =
+ new SelectExistentKeystoreWizard();
+
+ WizardDialog dialog =
+ new WizardDialog(parentComposite.getShell(), selectExistentKeystoreWizard);
+
+ //open the wizard to select keystores
+ dialog.create();
+ if (dialog.open() == Window.OK)
+ {
+ //keystore was really selected : adding keystore to the list
+ IKeyStore iKeyStore = selectExistentKeystoreWizard.getSelectedKeystore();
+ boolean canSavePassword = selectExistentKeystoreWizard.canSavePassword();
+ String password = selectExistentKeystoreWizard.getPassword();
+ addKeystore(iKeyStore, canSavePassword, password);
+ selectKeystore(iKeyStore);
+ }
+ }
+
+ /**
+ * This method must be used to retrieve the passwod of any keystore in the context of this wizard.
+ * It is purpose is to cache the passwords of the keystores, mainly of the ones that do not have its password saved,
+ * so the password will be asked only once for each keystore, during the lifetime of this wizard.
+ * */
+ protected String getKeyStorePassword(IKeyStore keystore)
+ {
+ String password = keystorePasswords.get(keystore);
+
+ //check if password is already cached
+ if (password == null)
+ {
+ password = keystore.getKeyStorePassword(true);
+ }
+ if (password != null)
+ {
+ keystorePasswords.put(keystore, password);
+ }
+
+ return password;
+ }
+
+ /**
+ * Sign button listener to enable/disable combos, buttons and finish/next
+ *
+ */
+ private class SignButtonListener implements Listener
+ {
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * org.eclipse.swt.widgets.Listener#handleEvent(org.eclipse.swt.widgets
+ * .Event)
+ */
+ @Override
+ public void handleEvent(Event event)
+ {
+ if (event.widget == signCheckBox)
+ {
+ setEnablement(true);
+ }
+
+ }
+
+ }
+
+ /**
+ * This class will handle the (Des)Select All buttons
+ *
+ */
+ private class TreeSelectionButtonListener implements Listener
+ {
+ private final boolean checked;
+
+ /**
+ * Create a new instance of the listener with the desired check state
+ *
+ * @param selectItems
+ */
+ public TreeSelectionButtonListener(boolean selectItems)
+ {
+ this.checked = selectItems;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * org.eclipse.swt.widgets.Listener#handleEvent(org.eclipse.swt.widgets
+ * .Event)
+ */
+ @Override
+ public void handleEvent(Event event)
+ {
+ for (TreeItem item : tree.getItems())
+ {
+ item.setChecked(checked);
+ }
+ updateDefaultDestination();
+ parentComposite.notifyListeners(SWT.Modify, new Event());
+ treeSelectionChanged = true;
+ }
+
+ }
+} \ No newline at end of file
diff --git a/src/plugins/packaging.ui/src/com/motorola/studio/android/packaging/ui/export/PackageExportWizardPage.java b/src/plugins/packaging.ui/src/com/motorola/studio/android/packaging/ui/export/PackageExportWizardPage.java
new file mode 100644
index 0000000..ec73988
--- /dev/null
+++ b/src/plugins/packaging.ui/src/com/motorola/studio/android/packaging/ui/export/PackageExportWizardPage.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.packaging.ui.export;
+
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.wizard.WizardPage;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.plugin.AbstractUIPlugin;
+
+import com.motorola.studio.android.packaging.ui.PackagingUIPlugin;
+import com.motorola.studio.android.packaging.ui.i18n.Messages;
+
+/**
+ * Package Export Wizard Page
+ *
+ */
+public class PackageExportWizardPage extends WizardPage implements Listener
+{
+ private PackageExportWizardArea dialogArea;
+
+ private final IStructuredSelection structuredSelection;
+
+ private final boolean signEnabled;
+
+ /**
+ * Wizard page constructor.
+ *
+ * @param pageName
+ * @param selection
+ */
+ public PackageExportWizardPage(String pageName, IStructuredSelection selection,
+ boolean signEnabled)
+ {
+ super(pageName);
+ this.structuredSelection = selection;
+ this.signEnabled = signEnabled;
+ setTitle(pageName); // NON-NLS-1
+
+ ImageDescriptor imageDescriptor =
+ AbstractUIPlugin.imageDescriptorFromPlugin(PackagingUIPlugin.PLUGIN_ID,
+ PackagingUIPlugin.EXPORT_WIZARD_ICON);
+
+ if (imageDescriptor != null)
+ {
+ setImageDescriptor(imageDescriptor);
+ }
+
+ setDescription(Messages.EXPORT_WIZARD_DESCRIPTION);
+ }
+
+ /**
+ * Create the Wizard page control
+ *
+ * @param parent
+ * the parent composite
+ */
+ public void createControl(Composite parent)
+ {
+ Composite mainComposite = new Composite(parent, SWT.NONE);
+ mainComposite.setLayout(new GridLayout());
+ mainComposite.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+ mainComposite.addListener(SWT.Modify, this);
+
+ this.dialogArea =
+ new PackageExportWizardArea(structuredSelection, mainComposite, this.signEnabled);
+ PlatformUI.getWorkbench().getHelpSystem().setHelp(mainComposite,
+ PackagingUIPlugin.PACKAGING_WIZARD_CONTEXT_HELP_ID);
+ this.setControl(mainComposite);
+
+ }
+
+ /**
+ * Override the method isPageComplete
+ */
+ @Override
+ public boolean isPageComplete()
+ {
+ boolean pageComplete = this.dialogArea.canFinish();
+ setMessage(this.dialogArea.getMessage(), this.dialogArea.getSeverity());
+ return pageComplete;
+ }
+
+ /**
+ * Delegates the finish operation to Dialog Implementation.
+ */
+ public boolean finish()
+ {
+ return this.dialogArea.performFinish();
+ }
+
+ /**
+ * Event Handler
+ *
+ * @param event
+ */
+ public void handleEvent(Event event)
+ {
+ this.getContainer().updateButtons();
+ setMessage(this.dialogArea.getMessage(), this.dialogArea.getSeverity());
+ }
+}
diff --git a/src/plugins/packaging.ui/src/com/motorola/studio/android/packaging/ui/handlers/ExportWizardHandler.java b/src/plugins/packaging.ui/src/com/motorola/studio/android/packaging/ui/handlers/ExportWizardHandler.java
new file mode 100644
index 0000000..b7d2741
--- /dev/null
+++ b/src/plugins/packaging.ui/src/com/motorola/studio/android/packaging/ui/handlers/ExportWizardHandler.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.packaging.ui.handlers;
+
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.StructuredSelection;
+import org.eclipse.jface.wizard.WizardDialog;
+import org.eclipse.ui.PlatformUI;
+
+import com.motorola.studio.android.packaging.ui.export.PackageExportWizard;
+
+public class ExportWizardHandler extends AbstractHandler
+{
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.core.commands.AbstractHandler#execute(org.eclipse.core.commands.ExecutionEvent)
+ */
+ public Object execute(ExecutionEvent event) throws ExecutionException
+ {
+ ISelection selection =
+ PlatformUI.getWorkbench().getActiveWorkbenchWindow().getSelectionService()
+ .getSelection();
+ IStructuredSelection structuredSel = new StructuredSelection();
+ if (selection instanceof IStructuredSelection)
+ {
+ structuredSel = (IStructuredSelection) selection;
+ }
+
+ PackageExportWizard export = new PackageExportWizard();
+ export.init(PlatformUI.getWorkbench(), structuredSel);
+
+ WizardDialog dialog =
+ new WizardDialog(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(),
+ export);
+ dialog.open();
+
+ return null;
+ }
+}
diff --git a/src/plugins/packaging.ui/src/com/motorola/studio/android/packaging/ui/i18n/Messages.java b/src/plugins/packaging.ui/src/com/motorola/studio/android/packaging/ui/i18n/Messages.java
new file mode 100644
index 0000000..1c9c405
--- /dev/null
+++ b/src/plugins/packaging.ui/src/com/motorola/studio/android/packaging/ui/i18n/Messages.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.packaging.ui.i18n;
+
+import org.eclipse.osgi.util.NLS;
+
+public class Messages extends NLS
+{
+ private static final String BUNDLE_NAME =
+ "com.motorola.studio.android.packaging.ui.i18n.messages"; //$NON-NLS-1$
+
+ public static String PACKAGE_EXPORT_WIZARD_AREA_UNSIGNEDPACKAGE_WARNING;
+
+ static
+ {
+ // initialize resource bundle
+ NLS.initializeMessages(BUNDLE_NAME, Messages.class);
+ }
+
+ private Messages()
+ {
+ }
+
+ public static String EXPORT_WIZARD_TILE;
+
+ public static String EXPORT_WIZARD_DESCRIPTION;
+
+ public static String PACKAGE_EXPORT_WIZARD_AREA_DESCRIPTION;
+
+ public static String PACKAGE_EXPORT_WIZARD_AREA_SELECT_ALL_BUTTON;
+
+ public static String PACKAGE_EXPORT_WIZARD_AREA_DESELECT_ALL_BUTTON;
+
+ public static String PACKAGE_EXPORT_WIZARD_AREA_SIGNING_TAB_TEXT;
+
+ public static String PACKAGE_EXPORT_WIZARD_AREA_SIGN_CHECK_LABEL;
+
+ public static String PACKAGE_EXPORT_WIZARD_AREA_DESTINATION_LABEL;
+
+ public static String PACKAGE_EXPORT_WIZARD_PACKAGE_DESTINATION_LABEL;
+
+ public static String PACKAGE_EXPORT_WIZARD_AREA_BROWSE_BUTTON_LABEL;
+
+ public static String PACKAGE_EXPORT_WIZARD_AREA_SIGN_KEYSTORE_LABEL;
+
+ public static String PACKAGE_EXPORT_WIZARD_AREA_SIGN_KEY_LABEL;
+
+ public static String PACKAGE_EXPORT_WIZARD_USE_DEFAULT_DESTINATION;
+
+ public static String PACKAGE_EXPORT_WIZARD_AREA_READONLY_MESSAGE;
+
+ public static String PACKAGE_EXPORT_WIZARD_AREA_READONLY_TITLE;
+
+ public static String PACKAGE_EXPORT_WIZARD_AREA_SIGN_NO_KEYSTORE_AVAILABLE;
+
+ public static String PACKAGE_EXPORT_WIZARD_AREA_SIGN_NO_KEYSTORE_OR_KEY_SELECTED;
+
+ public static String PACKAGE_EXPORT_WIZARD_AREA_SIGN_BUTTON_OTHER_LABEL;
+
+ public static String PACKAGE_EXPORT_WIZARD_AREA_CREATE_DIRECTORIES_BOX_TITLE;
+
+ public static String PACKAGE_EXPORT_WIZARD_AREA_CREATE_DIRECTORIES_BOX_MESSAGE;
+
+ public static String PACKAGE_EXPORT_WIZARD_AREA_PROJECTS_WITH_ERRORS_SELECTED;
+
+ public static String PACKAGE_EXPORT_WIZARD_AREA_FINISH_ACTION_LABEL;
+
+ public static String PACKAGE_EXPORT_WIZARD_AREA_EXPORTING_ACTION_LABEL;
+
+ public static String PACKAGE_EXPORT_WIZARD_AREA_ERROR_PROJECT_BUILD;
+
+ public static String PACKAGE_EXPORT_WIZARD_AREA_ERROR_DESTINATION_CHECK;
+
+ public static String SELECTOR_MESSAGE_NO_SELECTION;
+
+ public static String SELECTOR_MESSAGE_LOCATION_ERROR_INVALID;
+
+ public static String SELECTOR_MESSAGE_LOCATION_ERROR_NOT_DIRECTORY;
+
+ public static String SELECTOR_MESSAGE_LOCATION_ERROR_INVALID_DEVICE;
+
+ public static String SELECTOR_MESSAGE_LOCATION_ERROR_PATH_TOO_LONG;
+
+ public static String PackageExportWizardArea_AddKeyButton_Text;
+
+ public static String PackageExportWizardArea_ErrorSigningPackage;
+
+ public static String PackageExportWizardArea_ErrorWritingSignedPackageFile;
+
+ public static String PackageExportWizardArea_MenuItem_AddNew;
+
+ public static String PackageExportWizardArea_MenuItem_UseExistent;
+
+ public static String PackageExportWizardArea_WrongKeystoreTypeDialogMessage;
+
+ public static String PackageExportWizardArea_WrongKeystoreTypeDialogTitle;
+}
diff --git a/src/plugins/packaging.ui/src/com/motorola/studio/android/packaging/ui/i18n/messages.properties b/src/plugins/packaging.ui/src/com/motorola/studio/android/packaging/ui/i18n/messages.properties
new file mode 100644
index 0000000..767c3d1
--- /dev/null
+++ b/src/plugins/packaging.ui/src/com/motorola/studio/android/packaging/ui/i18n/messages.properties
@@ -0,0 +1,47 @@
+#################################################################################
+#
+# Mpkg UI Plug-in Messages
+#
+#################################################################################
+
+EXPORT_WIZARD_TILE = Export Android Package Files
+EXPORT_WIZARD_DESCRIPTION = Export an application using the Android package format
+PACKAGE_EXPORT_WIZARD_AREA_DESCRIPTION = Specify which packages should be created by selecting their projects.\nThe default destination is the "dist" folder for each project.
+PACKAGE_EXPORT_WIZARD_AREA_SELECT_ALL_BUTTON = Select All
+PACKAGE_EXPORT_WIZARD_AREA_DESELECT_ALL_BUTTON = Deselect All
+PACKAGE_EXPORT_WIZARD_AREA_SIGNING_TAB_TEXT = Signing
+PACKAGE_EXPORT_WIZARD_AREA_SIGN_CHECK_LABEL = Sign the package
+PACKAGE_EXPORT_WIZARD_AREA_DESTINATION_LABEL = Package Destination
+PACKAGE_EXPORT_WIZARD_PACKAGE_DESTINATION_LABEL = Destination:
+PACKAGE_EXPORT_WIZARD_AREA_BROWSE_BUTTON_LABEL = Browse...
+PACKAGE_EXPORT_WIZARD_AREA_SIGN_KEYSTORE_LABEL = Keystore:
+PACKAGE_EXPORT_WIZARD_AREA_SIGN_KEY_LABEL = Key:
+PACKAGE_EXPORT_WIZARD_USE_DEFAULT_DESTINATION = Use the default destination
+PACKAGE_EXPORT_WIZARD_AREA_READONLY_MESSAGE = One or more packages could not be exported.\nMake sure you have permission to write to the destination directory.\nMake sure the files inside the destination directory are writable.
+PACKAGE_EXPORT_WIZARD_AREA_READONLY_TITLE = Error exporting packages
+PACKAGE_EXPORT_WIZARD_AREA_SIGN_NO_KEYSTORE_AVAILABLE=No keystore available. Either obtain a key or export packages as unsigned.
+PACKAGE_EXPORT_WIZARD_AREA_SIGN_NO_KEYSTORE_OR_KEY_SELECTED=No keystore or key selected. Select a key or export packages as unsigned.
+PACKAGE_EXPORT_WIZARD_AREA_SIGN_BUTTON_OTHER_LABEL=Other...
+PACKAGE_EXPORT_WIZARD_AREA_CREATE_DIRECTORIES_BOX_TITLE = Create Directories
+PACKAGE_EXPORT_WIZARD_AREA_CREATE_DIRECTORIES_BOX_MESSAGE = Target directory does not exist. Would you like to create it?
+PACKAGE_EXPORT_WIZARD_AREA_PROJECTS_WITH_ERRORS_SELECTED = Some selected projects contain errors. Fix these errors or deselect the affected projects before proceeding.
+PACKAGE_EXPORT_WIZARD_AREA_FINISH_ACTION_LABEL = Generating Packages
+PACKAGE_EXPORT_WIZARD_AREA_EXPORTING_ACTION_LABEL = Exporting Package:
+PACKAGE_EXPORT_WIZARD_AREA_ERROR_PROJECT_BUILD = Error building or getting output folder for project
+PACKAGE_EXPORT_WIZARD_AREA_ERROR_DESTINATION_CHECK = Error checking destination folder. Make sure you have the right permissions and enough disk space.
+PACKAGE_EXPORT_WIZARD_AREA_UNSIGNEDPACKAGE_WARNING=Unsigned packages cannot be published to Google Play.
+
+SELECTOR_MESSAGE_NO_SELECTION = Specify which projects should be exported.
+SELECTOR_MESSAGE_LOCATION_ERROR_INVALID = Destination directory is invalid
+SELECTOR_MESSAGE_LOCATION_ERROR_NOT_DIRECTORY = Destination is not a directory
+SELECTOR_MESSAGE_LOCATION_ERROR_INVALID_DEVICE = The device you are trying to use is invalid
+SELECTOR_MESSAGE_LOCATION_ERROR_PATH_TOO_LONG = The path is too long
+
+PackageExportWizardArea_AddKeyButton_Text=Add...
+PackageExportWizardArea_ErrorSigningPackage=Error signing the package for project:
+PackageExportWizardArea_ErrorWritingSignedPackageFile=Error writing the signed package file for project
+PackageExportWizardArea_MenuItem_AddNew=Add new...
+PackageExportWizardArea_MenuItem_UseExistent=Use existing...
+PackageExportWizardArea_WrongKeystoreTypeDialogMessage=Keystore {0} was imported with the wrong type. Re-import it with the correct type.
+PackageExportWizardArea_WrongKeystoreTypeDialogTitle=Wrong Keystore Type
+
diff --git a/src/plugins/preflighting.checkers.ui/.classpath b/src/plugins/preflighting.checkers.ui/.classpath
new file mode 100644
index 0000000..8a8f166
--- /dev/null
+++ b/src/plugins/preflighting.checkers.ui/.classpath
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>
+ <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/src/plugins/preflighting.checkers.ui/.project b/src/plugins/preflighting.checkers.ui/.project
new file mode 100644
index 0000000..3c973b6
--- /dev/null
+++ b/src/plugins/preflighting.checkers.ui/.project
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>com.motorolamobility.preflighting.checkers.ui</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.ManifestBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.SchemaBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.pde.PluginNature</nature>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+</projectDescription>
diff --git a/src/plugins/preflighting.checkers.ui/META-INF/MANIFEST.MF b/src/plugins/preflighting.checkers.ui/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..f861229
--- /dev/null
+++ b/src/plugins/preflighting.checkers.ui/META-INF/MANIFEST.MF
@@ -0,0 +1,20 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: %Bundle-Name
+Bundle-SymbolicName: com.motorolamobility.preflighting.checkers.ui;singleton:=true
+Bundle-Version: 2.0.0.qualifier
+Bundle-ActivationPolicy: lazy
+Bundle-Vendor: %Bundle-Vendor
+Bundle-RequiredExecutionEnvironment: JavaSE-1.6
+Bundle-Localization: plugin
+Require-Bundle: org.eclipse.core.resources,
+ org.eclipse.core.runtime,
+ org.eclipse.ui.ide,
+ org.eclipse.swt,
+ com.motorolamobility.preflighting.core,
+ com.motorola.studio.android.common,
+ org.eclipse.jdt.core,
+ org.eclipse.jdt.ui,
+ org.eclipse.jface.text,
+ org.eclipse.ui.workbench.texteditor
+Import-Package: org.eclipse.core.commands.common
diff --git a/src/plugins/preflighting.checkers.ui/build.properties b/src/plugins/preflighting.checkers.ui/build.properties
new file mode 100644
index 0000000..f97b875
--- /dev/null
+++ b/src/plugins/preflighting.checkers.ui/build.properties
@@ -0,0 +1,23 @@
+
+#
+# Copyright (C) 2012 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+source.. = src/
+output.. = bin/
+bin.includes = META-INF/,\
+ .,\
+ plugin.xml,\
+ plugin.properties
diff --git a/src/plugins/preflighting.checkers.ui/plugin.properties b/src/plugins/preflighting.checkers.ui/plugin.properties
new file mode 100644
index 0000000..6e7c9d6
--- /dev/null
+++ b/src/plugins/preflighting.checkers.ui/plugin.properties
@@ -0,0 +1,21 @@
+#
+# Copyright (C) 2012 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+#Properties file for com.motorolamobility.preflighting.ui
+Bundle-Vendor = Motorola Mobility, Inc.
+Bundle-Name = MOTODEV Studio App Validator Checkers UI
+
+appValidatorMarkerName=App Validator Problem
diff --git a/src/plugins/preflighting.checkers.ui/plugin.xml b/src/plugins/preflighting.checkers.ui/plugin.xml
new file mode 100644
index 0000000..1340959
--- /dev/null
+++ b/src/plugins/preflighting.checkers.ui/plugin.xml
@@ -0,0 +1,107 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?eclipse version="3.4"?>
+
+<!--
+ Copyright (C) 2012 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<plugin>
+
+
+ <extension
+ id="appValidatorMarker"
+ name="%appValidatorMarkerName"
+ point="org.eclipse.core.resources.markers">
+ <super
+ type="org.eclipse.core.resources.problemmarker">
+ </super>
+ </extension>
+ <extension
+ id="missingPermissionsMarker"
+ name="%appValidatorMarkerName"
+ point="org.eclipse.core.resources.markers">
+ <super
+ type="com.motorolamobility.preflighting.checkers.ui.appValidatorMarker">
+ </super>
+ </extension>
+ <extension
+ id="unneededPermissionsMarker"
+ name="%appValidatorMarkerName"
+ point="org.eclipse.core.resources.markers">
+ <super
+ type="com.motorolamobility.preflighting.checkers.ui.appValidatorMarker">
+ </super>
+ </extension>
+ <extension
+ id="deviceCompatibilityUnsupportedFeaturesMarker"
+ name="%appValidatorMarkerName"
+ point="org.eclipse.core.resources.markers">
+ <super
+ type="com.motorolamobility.preflighting.checkers.ui.appValidatorMarker">
+ </super>
+ </extension>
+ <extension
+ id="googlePlayFiltersMissingMinSDK"
+ name="%appValidatorMarkerName"
+ point="org.eclipse.core.resources.markers">
+ <super
+ type="com.motorolamobility.preflighting.checkers.ui.appValidatorMarker">
+ </super>
+ </extension>
+ <extension
+ id="googlePlayFiltersUneededMaxSDK"
+ name="%appValidatorMarkerName"
+ point="org.eclipse.core.resources.markers">
+ <super
+ type="com.motorolamobility.preflighting.checkers.ui.appValidatorMarker">
+ </super>
+ </extension>
+ <extension
+ id="impliedFeaturesMarker"
+ name="%appValidatorMarkerName"
+ point="org.eclipse.core.resources.markers">
+ <super
+ type="com.motorolamobility.preflighting.checkers.ui.appValidatorMarker">
+ </super>
+ </extension>
+ <extension
+ point="org.eclipse.ui.ide.markerResolution">
+ <markerResolutionGenerator
+ class="com.motorolamobility.preflighting.checkers.ui.QuickFixGenerator"
+ markerType="com.motorolamobility.preflighting.checkers.ui.unneededPermissionsMarker">
+ </markerResolutionGenerator>
+ <markerResolutionGenerator
+ class="com.motorolamobility.preflighting.checkers.ui.QuickFixGenerator"
+ markerType="com.motorolamobility.preflighting.checkers.ui.deviceCompatibilityUnsupportedFeaturesMarker">
+ </markerResolutionGenerator>
+ <markerResolutionGenerator
+ class="com.motorolamobility.preflighting.checkers.ui.QuickFixGenerator"
+ markerType="com.motorolamobility.preflighting.checkers.ui.googlePlayFiltersMissingMinSDK">
+ </markerResolutionGenerator>
+ <markerResolutionGenerator
+ class="com.motorolamobility.preflighting.checkers.ui.ImpliedFeaturesGenerator"
+ markerType="com.motorolamobility.preflighting.checkers.ui.impliedFeaturesMarker">
+ </markerResolutionGenerator>
+ <markerResolutionGenerator
+ class="com.motorolamobility.preflighting.checkers.ui.QuickFixGenerator"
+ markerType="com.motorolamobility.preflighting.checkers.ui.googlePlayFiltersUneededMaxSDK">
+ </markerResolutionGenerator>
+ <markerResolutionGenerator
+ class="com.motorolamobility.preflighting.checkers.ui.QuickFixGenerator"
+ markerType="com.motorolamobility.preflighting.checkers.ui.missingPermissionsMarker">
+ </markerResolutionGenerator>
+ </extension>
+
+</plugin>
diff --git a/src/plugins/preflighting.checkers.ui/src/com/motorolamobility/preflighting/checkers/ui/DeviceCompatibilityUnsupportedFeaturesQuickFix.java b/src/plugins/preflighting.checkers.ui/src/com/motorolamobility/preflighting/checkers/ui/DeviceCompatibilityUnsupportedFeaturesQuickFix.java
new file mode 100644
index 0000000..d53b53b
--- /dev/null
+++ b/src/plugins/preflighting.checkers.ui/src/com/motorolamobility/preflighting/checkers/ui/DeviceCompatibilityUnsupportedFeaturesQuickFix.java
@@ -0,0 +1,113 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+package com.motorolamobility.preflighting.checkers.ui;
+
+import java.util.List;
+
+import org.eclipse.core.resources.IMarker;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.ui.IMarkerResolution2;
+
+import com.motorola.studio.android.common.utilities.EclipseUtils;
+import com.motorola.studio.android.manifest.AndroidProjectManifestFile;
+import com.motorola.studio.android.model.manifest.AndroidManifestFile;
+import com.motorola.studio.android.model.manifest.dom.ManifestNode;
+import com.motorola.studio.android.model.manifest.dom.UsesFeatureNode;
+import com.motorolamobility.preflighting.checkers.ui.i18n.CheckersUiNLS;
+
+/**
+ * This class implements the fix for device compatibility - unsupported features condition.
+ * The fix consists in adding &lt;uses-feature android:name="featureName" android:required="false"/&gt; to AndroidManifest.xml.
+ */
+public class DeviceCompatibilityUnsupportedFeaturesQuickFix implements IMarkerResolution2
+{
+
+ private static final String FALSE = "false"; //$NON-NLS-1$
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.IMarkerResolution#getLabel()
+ */
+ public String getLabel()
+ {
+ return CheckersUiNLS.DeviceCompatibilityUnsupportedFeaturesQuickFix_Label;
+ }
+
+ /**
+ * Adds android:required="false" for the feature AndroidManifest.xml.
+ *
+ * @param marker contains the feature that must be made NOT REQUIRED in AndroidManifest.xml.
+ *
+ * @see org.eclipse.ui.IMarkerResolution#run(org.eclipse.core.resources.IMarker)
+ */
+ @SuppressWarnings("unchecked")
+ public void run(IMarker marker)
+ {
+ try
+ {
+ //get the AndroidManifest file
+ IProject project = marker.getResource().getProject();
+ AndroidManifestFile manifestFile = AndroidProjectManifestFile.getFromProject(project);
+ ManifestNode manifestNode = manifestFile.getManifestNode();
+
+ //get the feature that must be made NOT REQUIRED, generated from app validator
+ List<Object> attributes =
+ (List<Object>) marker.getAttribute(QuickFixGenerator.QUICK_FIX_ID);
+
+ for (Object featureId : attributes)
+ {
+ UsesFeatureNode usesFeatureNode =
+ manifestNode.getUsesFeatureNode((String) featureId);
+ if (usesFeatureNode == null)
+ {
+ //uses-feature element does not exist - add the element uses feature node
+ usesFeatureNode = new UsesFeatureNode((String) featureId);
+ manifestNode.addUsesFeatureNode(usesFeatureNode);
+ }
+ //in both cases (uses-feature element exists or not) - add/update the required attribute
+ usesFeatureNode.setRequired(Boolean.parseBoolean(FALSE));
+ }
+
+ //save the project with the new/updated uses-feature
+ AndroidProjectManifestFile.saveToProject(project, manifestFile, true);
+
+ marker.delete();
+ }
+ catch (Exception e)
+ {
+ EclipseUtils.showErrorDialog(CheckersUiNLS.QuickFix_MarkerResolutionFailed,
+ CheckersUiNLS.QuickFix_CouldNotFixTheProblem + e.getMessage());
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.IMarkerResolution2#getDescription()
+ */
+ public String getDescription()
+ {
+ return CheckersUiNLS.DeviceCompatibilityUnsupportedFeaturesQuickFix_Description;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.IMarkerResolution2#getImage()
+ */
+ public Image getImage()
+ {
+ return null;
+ }
+
+} \ No newline at end of file
diff --git a/src/plugins/preflighting.checkers.ui/src/com/motorolamobility/preflighting/checkers/ui/ImpliedFeaturesGenerator.java b/src/plugins/preflighting.checkers.ui/src/com/motorolamobility/preflighting/checkers/ui/ImpliedFeaturesGenerator.java
new file mode 100644
index 0000000..4951ec5
--- /dev/null
+++ b/src/plugins/preflighting.checkers.ui/src/com/motorolamobility/preflighting/checkers/ui/ImpliedFeaturesGenerator.java
@@ -0,0 +1,71 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorolamobility.preflighting.checkers.ui;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.core.resources.IMarker;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.ui.IMarkerResolution;
+import org.eclipse.ui.IMarkerResolution2;
+import org.eclipse.ui.IMarkerResolutionGenerator2;
+import com.motorola.studio.android.common.log.StudioLogger;
+
+public class ImpliedFeaturesGenerator implements IMarkerResolutionGenerator2
+{
+
+ public IMarkerResolution[] getResolutions(IMarker marker)
+ {
+ List<IMarkerResolution2> resolutions = new ArrayList<IMarkerResolution2>();
+
+ try
+ {
+ if (marker.getType().equals(
+ "com.motorolamobility.preflighting.checkers.ui.impliedFeaturesMarker"))
+ {
+ resolutions.add(new ImpliedFeaturesMarkerResolution());
+ }
+ }
+ catch (CoreException e) {
+ StudioLogger.error("Requested marker does not exist: ", e.getMessage());
+ }
+ return resolutions.toArray(new IMarkerResolution2[resolutions.size()]);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.IMarkerResolutionGenerator2#hasResolutions(org.eclipse.core.resources.IMarker)
+ */
+ public boolean hasResolutions(IMarker marker)
+ {
+ boolean hasResolutions = false;
+ try
+ {
+ if (marker.getType().equals(
+ "com.motorolamobility.preflighting.checkers.ui.impliedFeaturesMarker"))
+ {
+ hasResolutions = true;
+ }
+ }
+ catch (CoreException e)
+ {
+ StudioLogger.error("Requested marker does not exist: ", e.getMessage());
+ }
+
+ return hasResolutions;
+ }
+
+}
diff --git a/src/plugins/preflighting.checkers.ui/src/com/motorolamobility/preflighting/checkers/ui/ImpliedFeaturesMarkerResolution.java b/src/plugins/preflighting.checkers.ui/src/com/motorolamobility/preflighting/checkers/ui/ImpliedFeaturesMarkerResolution.java
new file mode 100644
index 0000000..b823d41
--- /dev/null
+++ b/src/plugins/preflighting.checkers.ui/src/com/motorolamobility/preflighting/checkers/ui/ImpliedFeaturesMarkerResolution.java
@@ -0,0 +1,100 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorolamobility.preflighting.checkers.ui;
+
+import java.util.List;
+
+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.swt.graphics.Image;
+import org.eclipse.ui.IMarkerResolution2;
+
+import com.motorola.studio.android.common.exception.AndroidException;
+import com.motorola.studio.android.common.utilities.EclipseUtils;
+import com.motorola.studio.android.manifest.AndroidProjectManifestFile;
+import com.motorola.studio.android.model.manifest.AndroidManifestFile;
+import com.motorola.studio.android.model.manifest.dom.ManifestNode;
+import com.motorola.studio.android.model.manifest.dom.UsesFeatureNode;
+import com.motorolamobility.preflighting.checkers.ui.i18n.CheckersUiNLS;
+
+/**
+ * MarkerResolution responsible for adding the implied feature by permission as uses-feature manifest node.
+ */
+public class ImpliedFeaturesMarkerResolution implements IMarkerResolution2
+{
+
+ public String getLabel()
+ {
+ return CheckersUiNLS.ImpliedFeaturesMarkerResolution_Label;
+ }
+
+ @SuppressWarnings("unchecked")
+ public void run(IMarker marker)
+ {
+ IResource resource = marker.getResource();
+ AndroidManifestFile manifestFile;
+ try
+ {
+ IProject project = resource.getProject();
+ manifestFile = AndroidProjectManifestFile.getFromProject(project);
+ ManifestNode manifestNode = manifestFile.getManifestNode();
+
+ List<Object> attributes =
+ (List<Object>) marker.getAttribute(QuickFixGenerator.QUICK_FIX_ID);
+ for(Object attribute : attributes)
+ {
+ List<String> impliedFeatures = (List<String>) attribute;
+ for(String impliedFeature : impliedFeatures)
+ {
+ UsesFeatureNode usesFeatureNode = manifestNode.getUsesFeatureNode(impliedFeature);
+ if(usesFeatureNode == null) //User has not yet added it!
+ {
+ usesFeatureNode = new UsesFeatureNode(impliedFeature);
+ manifestNode.addUsesFeatureNode(usesFeatureNode);
+ }
+ }
+ }
+ AndroidProjectManifestFile.saveToProject(project, manifestFile, true);
+
+ marker.delete();
+ }
+ catch (AndroidException e)
+ {
+ EclipseUtils.showErrorDialog(CheckersUiNLS.ImpliedFeaturesMarkerResolution_Fail_Msg_Dlg_Title, CheckersUiNLS.ImpliedFeaturesMarkerResolution_Fail_Msg_Save_Manifest);
+ }
+ catch (CoreException e)
+ {
+ EclipseUtils.showErrorDialog(CheckersUiNLS.ImpliedFeaturesMarkerResolution_Fail_Msg_Dlg_Title, CheckersUiNLS.ImpliedFeaturesMarkerResolution_Fail_Msg_Manipulate_Manifest);
+ }
+
+ }
+
+ public Image getImage()
+ {
+ return null;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.IMarkerResolution2#getDescription()
+ */
+ public String getDescription()
+ {
+ return null; //There's no additional description.
+ }
+
+}
diff --git a/src/plugins/preflighting.checkers.ui/src/com/motorolamobility/preflighting/checkers/ui/MissingMinSdkQuickFix.java b/src/plugins/preflighting.checkers.ui/src/com/motorolamobility/preflighting/checkers/ui/MissingMinSdkQuickFix.java
new file mode 100644
index 0000000..7ae3d1b
--- /dev/null
+++ b/src/plugins/preflighting.checkers.ui/src/com/motorolamobility/preflighting/checkers/ui/MissingMinSdkQuickFix.java
@@ -0,0 +1,110 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorolamobility.preflighting.checkers.ui;
+
+import java.io.File;
+
+import org.eclipse.core.resources.IMarker;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.ui.IMarkerResolution2;
+
+import com.motorola.studio.android.common.utilities.AndroidUtils;
+import com.motorola.studio.android.common.utilities.EclipseUtils;
+import com.motorola.studio.android.manifest.AndroidProjectManifestFile;
+import com.motorola.studio.android.model.manifest.AndroidManifestFile;
+import com.motorola.studio.android.model.manifest.dom.ManifestNode;
+import com.motorola.studio.android.model.manifest.dom.UsesSDKNode;
+import com.motorolamobility.preflighting.checkers.ui.i18n.CheckersUiNLS;
+
+/**
+ * This class implements the fix for Android Market filter - Missing minSdk condition.
+ * The fix consists in adding &lt;uses-sdk android:minSdkVersion="targetDeclaredOnproject.properties"/&gt; to AndroidManifest.xml.
+ */
+public class MissingMinSdkQuickFix implements IMarkerResolution2
+{
+ private static final String ANDROID_PREFIX = "android-";
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.IMarkerResolution#getLabel()
+ */
+ public String getLabel()
+ {
+ return CheckersUiNLS.MissingMinSdkQuickFix_Label;
+ }
+
+ /**
+ * Adds android:minSdkVersion="targetDeclaredOnproject.properties" for the uses-sdk in AndroidManifest.xml.
+ *
+ * @param marker
+ *
+ * @see org.eclipse.ui.IMarkerResolution#run(org.eclipse.core.resources.IMarker)
+ */
+ public void run(IMarker marker)
+ {
+ try
+ {
+ //get the AndroidManifest file
+ IProject project = marker.getResource().getProject();
+ AndroidManifestFile manifestFile = AndroidProjectManifestFile.getFromProject(project);
+ ManifestNode manifestNode = manifestFile.getManifestNode();
+
+ File projectAPITargetFile =
+ AndroidUtils.getAndroidTargetPathForProject(project.getLocation().toFile());
+ String projectTargetAPI = projectAPITargetFile.getName();
+
+ UsesSDKNode usesSDKNode = manifestNode.getUsesSdkNode();
+ if (usesSDKNode == null)
+ {
+ //element uses-sdk does not exist - add the entire element with minSdkVersion attribute
+ usesSDKNode = new UsesSDKNode();
+ manifestNode.addUsesSdkNode(usesSDKNode);
+ }
+ //in both cases (if element uses-sdk exists or not) - add minSdkVersion attribute
+ if (projectTargetAPI.contains(ANDROID_PREFIX))
+ {
+ projectTargetAPI = projectTargetAPI.replace(ANDROID_PREFIX, "");
+ }
+ usesSDKNode.setMinSdkVersion(projectTargetAPI);
+
+ //save the project with the new/updated uses-sdk element
+ AndroidProjectManifestFile.saveToProject(project, manifestFile, true);
+
+ marker.delete();
+ }
+ catch (Exception e)
+ {
+ EclipseUtils.showErrorDialog(CheckersUiNLS.QuickFix_MarkerResolutionFailed,
+ CheckersUiNLS.QuickFix_CouldNotFixTheProblem + e.getMessage());
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.IMarkerResolution2#getDescription()
+ */
+ public String getDescription()
+ {
+ return CheckersUiNLS.MissingMinSdkQuickFix_Description;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.IMarkerResolution2#getImage()
+ */
+ public Image getImage()
+ {
+ return null;
+ }
+}
diff --git a/src/plugins/preflighting.checkers.ui/src/com/motorolamobility/preflighting/checkers/ui/MissingPermissionsQuickFix.java b/src/plugins/preflighting.checkers.ui/src/com/motorolamobility/preflighting/checkers/ui/MissingPermissionsQuickFix.java
new file mode 100644
index 0000000..927026d
--- /dev/null
+++ b/src/plugins/preflighting.checkers.ui/src/com/motorolamobility/preflighting/checkers/ui/MissingPermissionsQuickFix.java
@@ -0,0 +1,104 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+package com.motorolamobility.preflighting.checkers.ui;
+
+import java.util.List;
+
+import org.eclipse.core.resources.IMarker;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.ui.IMarkerResolution2;
+
+import com.motorola.studio.android.common.utilities.EclipseUtils;
+import com.motorola.studio.android.manifest.AndroidProjectManifestFile;
+import com.motorola.studio.android.model.manifest.AndroidManifestFile;
+import com.motorola.studio.android.model.manifest.dom.ManifestNode;
+import com.motorola.studio.android.model.manifest.dom.UsesPermissionNode;
+import com.motorolamobility.preflighting.checkers.ui.i18n.CheckersUiNLS;
+
+/**
+ * This class implements the fix for missing permissions condition.
+ * The fix consists in adding all missing permissions to AndroidManifest.xml.
+ */
+public class MissingPermissionsQuickFix implements IMarkerResolution2
+{
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.IMarkerResolution#getLabel()
+ */
+ public String getLabel()
+ {
+ return CheckersUiNLS.MissingPermissionsQuickFix_Label;
+ }
+
+ /**
+ * Adds all missing permissions to AndroidManifest.xml.
+ *
+ * @param marker contains the list of missing permissions that must be added to AndroidManifest.xml.
+ *
+ * @see org.eclipse.ui.IMarkerResolution#run(org.eclipse.core.resources.IMarker)
+ */
+ @SuppressWarnings("unchecked")
+ public void run(IMarker marker)
+ {
+ try
+ {
+ //get the AndroidManifest file
+ IProject project = marker.getResource().getProject();
+ AndroidManifestFile manifestFile = AndroidProjectManifestFile.getFromProject(project);
+ ManifestNode manifestNode = manifestFile.getManifestNode();
+
+ //get the list of missing permissions, generated from app validator
+ List<Object> attributes =
+ (List<Object>) marker.getAttribute(QuickFixGenerator.QUICK_FIX_ID);
+
+ //iterate over the list of missing permissions, adding them to AndroidManifest file
+ for (Object missingPermission : attributes)
+ {
+ manifestNode.addUsesPermissionNode(new UsesPermissionNode(
+ (String) missingPermission));
+ }
+
+ //save the project with the new permissions
+ AndroidProjectManifestFile.saveToProject(project, manifestFile, true);
+
+ marker.delete();
+ }
+ catch (Exception e)
+ {
+ EclipseUtils.showErrorDialog("Marker resolution fail.",
+ "Missing permissions quick fix could not fix the problem: " + e.getMessage());
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.IMarkerResolution2#getDescription()
+ */
+ public String getDescription()
+ {
+ return CheckersUiNLS.MissingPermissionsQuickFix_Description;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.IMarkerResolution2#getImage()
+ */
+ public Image getImage()
+ {
+ return null;
+ }
+
+} \ No newline at end of file
diff --git a/src/plugins/preflighting.checkers.ui/src/com/motorolamobility/preflighting/checkers/ui/QuickFixGenerator.java b/src/plugins/preflighting.checkers.ui/src/com/motorolamobility/preflighting/checkers/ui/QuickFixGenerator.java
new file mode 100644
index 0000000..399c0c1
--- /dev/null
+++ b/src/plugins/preflighting.checkers.ui/src/com/motorolamobility/preflighting/checkers/ui/QuickFixGenerator.java
@@ -0,0 +1,133 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+package com.motorolamobility.preflighting.checkers.ui;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.core.resources.IMarker;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.ui.IMarkerResolution2;
+import org.eclipse.ui.IMarkerResolutionGenerator2;
+
+import com.motorolamobility.preflighting.core.logging.PreflightingLogger;
+
+/**
+ * This class is responsible for checking which markers has quick fixes, and in the cases that a quick fix
+ * is available, it returns the class that implements the fix.
+ * */
+
+public class QuickFixGenerator implements IMarkerResolutionGenerator2
+{
+ /**
+ * AppValidator quick fix identifier.
+ * */
+ public static final String QUICK_FIX_ID = "QuickFix";
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.IMarkerResolutionGenerator#getResolutions(org.eclipse.core.resources.IMarker)
+ */
+ public IMarkerResolution2[] getResolutions(IMarker marker)
+ {
+ //list of possible resolutions for the given marker
+ List<IMarkerResolution2> resolutions = new ArrayList<IMarkerResolution2>();
+
+ try
+ {
+ //check the marker type to associate the proper marker resolution class(es).
+ if (marker.getType().equals(
+ "com.motorolamobility.preflighting.checkers.ui.missingPermissionsMarker"))
+ {
+ resolutions.add(new MissingPermissionsQuickFix());
+ }
+ else if (marker.getType().equals(
+ "com.motorolamobility.preflighting.checkers.ui.unneededPermissionsMarker"))
+ {
+ resolutions.add(new UnneededPermissionsQuickFix());
+ }
+ else if (marker
+ .getType()
+ .equals("com.motorolamobility.preflighting.checkers.ui.deviceCompatibilityUnsupportedFeaturesMarker"))
+ {
+ resolutions.add(new DeviceCompatibilityUnsupportedFeaturesQuickFix());
+ }
+ else if (marker.getType().equals(
+ "com.motorolamobility.preflighting.checkers.ui.googlePlayFiltersMissingMinSDK"))
+ {
+ resolutions.add(new MissingMinSdkQuickFix());
+ }
+ else if (marker.getType().equals(
+ "com.motorolamobility.preflighting.checkers.ui.googlePlayFiltersUneededMaxSDK"))
+ {
+ resolutions.add(new UneededMaxSdkQuickFix());
+ }
+ }
+ catch (CoreException e)
+ {
+ PreflightingLogger.info(QuickFixGenerator.class,
+ "Problem retrieving marker resolutions: " + e.getMessage());
+ }
+
+ return resolutions.toArray(new IMarkerResolution2[resolutions.size()]);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.IMarkerResolutionGenerator2#hasResolutions(org.eclipse.core.resources.IMarker)
+ */
+ public boolean hasResolutions(IMarker marker)
+ {
+ try
+ {
+ //check if the marker type has at least one resolution available.
+ if (marker.getType().equals(
+ "com.motorolamobility.preflighting.checkers.ui.missingPermissionsMarker"))
+ {
+ return true;
+ }
+ else if (marker.getType().equals(
+ "com.motorolamobility.preflighting.checkers.ui.unneededPermissionsMarker"))
+ {
+ return true;
+ }
+ else if (marker
+ .getType()
+ .equals("com.motorolamobility.preflighting.checkers.ui.deviceCompatibilityUnsupportedFeaturesMarker"))
+ {
+ return true;
+ }
+ else if (marker.getType().equals(
+ "com.motorolamobility.preflighting.checkers.ui.googlePlayFiltersMissingMinSDK"))
+ {
+ return true;
+ }
+ else if (marker.getType().equals(
+ "com.motorolamobility.preflighting.checkers.ui.googlePlayFiltersUneededMaxSDK"))
+ {
+ return true;
+ }
+ }
+ catch (CoreException e)
+ {
+ PreflightingLogger.info(QuickFixGenerator.class,
+ "Problem checking if marker has resolutions: " + e.getMessage());
+ }
+
+ //no resolution found for this marker
+ return false;
+ }
+
+} \ No newline at end of file
diff --git a/src/plugins/preflighting.checkers.ui/src/com/motorolamobility/preflighting/checkers/ui/UneededMaxSdkQuickFix.java b/src/plugins/preflighting.checkers.ui/src/com/motorolamobility/preflighting/checkers/ui/UneededMaxSdkQuickFix.java
new file mode 100644
index 0000000..56b72be
--- /dev/null
+++ b/src/plugins/preflighting.checkers.ui/src/com/motorolamobility/preflighting/checkers/ui/UneededMaxSdkQuickFix.java
@@ -0,0 +1,96 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorolamobility.preflighting.checkers.ui;
+
+import org.eclipse.core.resources.IMarker;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.ui.IMarkerResolution2;
+
+import com.motorola.studio.android.common.utilities.EclipseUtils;
+import com.motorola.studio.android.manifest.AndroidProjectManifestFile;
+import com.motorola.studio.android.model.manifest.AndroidManifestFile;
+import com.motorola.studio.android.model.manifest.dom.ManifestNode;
+import com.motorola.studio.android.model.manifest.dom.UsesSDKNode;
+import com.motorolamobility.preflighting.checkers.ui.i18n.CheckersUiNLS;
+
+/**
+ * Removes unneeded maxSdkVersion from AndroidManifest.xml.
+ */
+public class UneededMaxSdkQuickFix implements IMarkerResolution2
+{
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.IMarkerResolution#getLabel()
+ */
+ public String getLabel()
+ {
+ return CheckersUiNLS.UneededMaxSdkQuickFix_Label;
+ }
+
+ /**
+ * Removes maxSdkVersion from AndroidManifest.xml.
+ *
+ * @param marker
+ *
+ * @see org.eclipse.ui.IMarkerResolution#run(org.eclipse.core.resources.IMarker)
+ */
+ public void run(IMarker marker)
+ {
+ try
+ {
+ //get the AndroidManifest file
+ IProject project = marker.getResource().getProject();
+ AndroidManifestFile manifestFile = AndroidProjectManifestFile.getFromProject(project);
+ ManifestNode manifestNode = manifestFile.getManifestNode();
+
+ UsesSDKNode usesSDKNode = manifestNode.getUsesSdkNode();
+ if (usesSDKNode != null)
+ {
+ if (usesSDKNode.getPropMaxSdkVersion() != null)
+ {
+ usesSDKNode.setPropMaxSdkVersion(null);
+
+ //save the project with the new permissions
+ AndroidProjectManifestFile.saveToProject(project, manifestFile, true);
+ }
+ }
+ marker.delete();
+ }
+ catch (Exception e)
+ {
+ EclipseUtils.showErrorDialog(CheckersUiNLS.QuickFix_MarkerResolutionFailed,
+ CheckersUiNLS.QuickFix_CouldNotFixTheProblem + e.getMessage());
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.IMarkerResolution2#getDescription()
+ */
+ public String getDescription()
+ {
+ return CheckersUiNLS.UneededMaxSdkQuickFix_Description;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.IMarkerResolution2#getImage()
+ */
+ public Image getImage()
+ {
+ return null;
+ }
+
+}
diff --git a/src/plugins/preflighting.checkers.ui/src/com/motorolamobility/preflighting/checkers/ui/UnneededPermissionsQuickFix.java b/src/plugins/preflighting.checkers.ui/src/com/motorolamobility/preflighting/checkers/ui/UnneededPermissionsQuickFix.java
new file mode 100644
index 0000000..dca1eed
--- /dev/null
+++ b/src/plugins/preflighting.checkers.ui/src/com/motorolamobility/preflighting/checkers/ui/UnneededPermissionsQuickFix.java
@@ -0,0 +1,101 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+package com.motorolamobility.preflighting.checkers.ui;
+
+import java.util.List;
+
+import org.eclipse.core.resources.IMarker;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.ui.IMarkerResolution2;
+
+import com.motorola.studio.android.common.utilities.EclipseUtils;
+import com.motorola.studio.android.manifest.AndroidProjectManifestFile;
+import com.motorola.studio.android.model.manifest.AndroidManifestFile;
+import com.motorola.studio.android.model.manifest.dom.ManifestNode;
+import com.motorolamobility.preflighting.checkers.ui.i18n.CheckersUiNLS;
+
+/**
+ * This class implements the fix for unneeded permissions condition.
+ * The fix consists in removing all permissions that are not needed.
+ *
+ */
+public class UnneededPermissionsQuickFix implements IMarkerResolution2
+{
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.IMarkerResolution#getLabel()
+ */
+ public String getLabel()
+ {
+ return CheckersUiNLS.UnneededPermissionsQuickFix_Label;
+ }
+
+ /**
+ * Removes all unneeded permissions from AndroidManifest.xml.
+ *
+ * @param marker contains the list of unneeded permissions that must be removed from AndroidManifest.xml.
+ * @see org.eclipse.ui.IMarkerResolution#run(org.eclipse.core.resources.IMarker)
+ */
+ @SuppressWarnings("unchecked")
+ public void run(IMarker marker)
+ {
+ try
+ {
+ //get the AndroidManifest file
+ IProject project = marker.getResource().getProject();
+ AndroidManifestFile manifestFile = AndroidProjectManifestFile.getFromProject(project);
+ ManifestNode manifestNode = manifestFile.getManifestNode();
+
+ //get the list of unneeded permissions to be removed from AndroidManifest
+ List<Object> attributes =
+ (List<Object>) marker.getAttribute(QuickFixGenerator.QUICK_FIX_ID);
+
+ //iterate over the list of unneeded permissions removing them
+ for (Object unneedPermission : attributes)
+ {
+ manifestNode.removeUsesPermissionNode((String) unneedPermission);
+ }
+
+ //save the project with the unneeded permissions removed
+ AndroidProjectManifestFile.saveToProject(project, manifestFile, true);
+
+ marker.delete();
+ }
+ catch (Exception e)
+ {
+ EclipseUtils.showErrorDialog("Marker resolution fail.", "Unneeded permissions quick fix could not fix the problem: " + e.getMessage());
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.IMarkerResolution2#getDescription()
+ */
+ public String getDescription()
+ {
+ return CheckersUiNLS.UnneededPermissionsQuickFix_Description;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.IMarkerResolution2#getImage()
+ */
+ public Image getImage()
+ {
+ return null;
+ }
+
+}
diff --git a/src/plugins/preflighting.checkers.ui/src/com/motorolamobility/preflighting/checkers/ui/i18n/CheckersUiNLS.java b/src/plugins/preflighting.checkers.ui/src/com/motorolamobility/preflighting/checkers/ui/i18n/CheckersUiNLS.java
new file mode 100644
index 0000000..7e2e489
--- /dev/null
+++ b/src/plugins/preflighting.checkers.ui/src/com/motorolamobility/preflighting/checkers/ui/i18n/CheckersUiNLS.java
@@ -0,0 +1,65 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorolamobility.preflighting.checkers.ui.i18n;
+
+import org.eclipse.osgi.util.NLS;
+
+public class CheckersUiNLS extends NLS
+{
+ private static final String BUNDLE_NAME = "com.motorolamobility.preflighting.checkers.ui.i18n.messages"; //$NON-NLS-1$
+
+ public static String MissingPermissionsQuickFix_Description;
+
+ public static String MissingPermissionsQuickFix_Label;
+
+ public static String UnneededPermissionsQuickFix_Description;
+
+ public static String UnneededPermissionsQuickFix_Label;
+
+ public static String DeviceCompatibilityUnsupportedFeaturesQuickFix_Description;
+
+ public static String QuickFix_CouldNotFixTheProblem;
+
+ public static String DeviceCompatibilityUnsupportedFeaturesQuickFix_Label;
+
+ public static String QuickFix_MarkerResolutionFailed;
+
+ public static String MissingMinSdkQuickFix_Description;
+
+ public static String MissingMinSdkQuickFix_Label;
+
+ public static String UneededMaxSdkQuickFix_Description;
+
+ public static String UneededMaxSdkQuickFix_Label;
+
+ public static String ImpliedFeaturesMarkerResolution_Fail_Msg_Dlg_Title;
+
+ public static String ImpliedFeaturesMarkerResolution_Label;
+
+ public static String ImpliedFeaturesMarkerResolution_Fail_Msg_Manipulate_Manifest;
+
+ public static String ImpliedFeaturesMarkerResolution_Fail_Msg_Save_Manifest;
+
+ static
+ {
+ // initialize resource bundle
+ NLS.initializeMessages(BUNDLE_NAME, CheckersUiNLS.class);
+ }
+
+ private CheckersUiNLS()
+ {
+ }
+}
diff --git a/src/plugins/preflighting.checkers.ui/src/com/motorolamobility/preflighting/checkers/ui/i18n/messages.properties b/src/plugins/preflighting.checkers.ui/src/com/motorolamobility/preflighting/checkers/ui/i18n/messages.properties
new file mode 100644
index 0000000..436d397
--- /dev/null
+++ b/src/plugins/preflighting.checkers.ui/src/com/motorolamobility/preflighting/checkers/ui/i18n/messages.properties
@@ -0,0 +1,37 @@
+#
+# Copyright (C) 2012 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+MissingPermissionsQuickFix_Description=Add required permissions to AndroidManifest.xml
+MissingPermissionsQuickFix_Label=Add required permissions
+
+UnneededPermissionsQuickFix_Description=Remove unneeded permissions from AndroidManifest.xml
+UnneededPermissionsQuickFix_Label=Remove unneeded permissions
+
+DeviceCompatibilityUnsupportedFeaturesQuickFix_Description=Add the android:required="false" attribute to the uses-feature element in AndroidManifest.xml
+QuickFix_CouldNotFixTheProblem=Quick fix could not fix the problem:
+DeviceCompatibilityUnsupportedFeaturesQuickFix_Label=Add android:required="false" to the uses-feature element
+QuickFix_MarkerResolutionFailed=Marker resolution failure.
+
+MissingMinSdkQuickFix_Description=Add android:minSdkVersion to AndroidManifest.xml using the version declared in project.properties
+MissingMinSdkQuickFix_Label=Add missing android:minSdkVersion
+
+UneededMaxSdkQuickFix_Description=Remove the unnecessary android:maxSdkVersion attribute from AndroidManifest.xml
+UneededMaxSdkQuickFix_Label=Remove android:maxSdkVersion from AndroidManifest.xml
+
+ImpliedFeaturesMarkerResolution_Fail_Msg_Dlg_Title=Marker resolution failure.
+ImpliedFeaturesMarkerResolution_Fail_Msg_Manipulate_Manifest=Unable to access the AndroidManifest.xml file.
+ImpliedFeaturesMarkerResolution_Fail_Msg_Save_Manifest=Unable to read AndroidManifest.xml or an error ocurred while trying to save the file.
+ImpliedFeaturesMarkerResolution_Label=Add the implied feature to Androidmanifest.xml \ No newline at end of file
diff --git a/src/plugins/preflighting.checkers/.classpath b/src/plugins/preflighting.checkers/.classpath
new file mode 100644
index 0000000..8a8f166
--- /dev/null
+++ b/src/plugins/preflighting.checkers/.classpath
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>
+ <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/src/plugins/preflighting.checkers/.project b/src/plugins/preflighting.checkers/.project
new file mode 100644
index 0000000..4807787
--- /dev/null
+++ b/src/plugins/preflighting.checkers/.project
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>com.motorolamobility.preflighting.checkers</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.ManifestBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.SchemaBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.pde.PluginNature</nature>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+</projectDescription>
diff --git a/src/plugins/preflighting.checkers/META-INF/MANIFEST.MF b/src/plugins/preflighting.checkers/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..7a5b1db
--- /dev/null
+++ b/src/plugins/preflighting.checkers/META-INF/MANIFEST.MF
@@ -0,0 +1,13 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: %pluginName
+Bundle-SymbolicName: com.motorolamobility.preflighting.checkers;singleton:=true
+Bundle-Version: 2.0.0.qualifier
+Bundle-Activator: com.motorolamobility.preflighting.checkers.CheckerPlugin
+Bundle-Vendor: %providerName
+Require-Bundle: org.eclipse.core.runtime,
+ com.motorolamobility.preflighting.core
+Bundle-RequiredExecutionEnvironment: JavaSE-1.6
+Bundle-Localization: plugin
+Bundle-ActivationPolicy: lazy
+Import-Package: org.eclipse.core.resources
diff --git a/src/plugins/preflighting.checkers/build.properties b/src/plugins/preflighting.checkers/build.properties
new file mode 100644
index 0000000..3c507c0
--- /dev/null
+++ b/src/plugins/preflighting.checkers/build.properties
@@ -0,0 +1,22 @@
+#
+# Copyright (C) 2012 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+source.. = src/
+output.. = bin/
+bin.includes = META-INF/,\
+ .,\
+ plugin.xml,\
+ plugin.properties
diff --git a/src/plugins/preflighting.checkers/plugin.properties b/src/plugins/preflighting.checkers/plugin.properties
new file mode 100644
index 0000000..43f46b2
--- /dev/null
+++ b/src/plugins/preflighting.checkers/plugin.properties
@@ -0,0 +1,134 @@
+#
+# Copyright (C) 2012 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+#################################################################################
+#
+# Android Main Activity checker properties
+#
+#################################################################################
+
+pluginName=MOTODEV Studio App Validator Checkers
+providerName=Motorola Mobility, Inc.
+
+mainactivitydescription=Checks the number of activities set as main.
+mainactivityname=Single Main Activity Checker
+mainActivity_singleMainActivity_ConditionName=One Main Activity
+mainActivity_singleMainActivity_ConditionDescription=Presence of exactly one activity set as main
+
+missingDrawableResourcesDescription=Checks your application's drawable resources, looking to see if your app has properly-sized images for every supported screen size and density.\nNote that the android:anyDensity attribute in the Android manifest file can be updated during the packaging process, which may affect validation.
+missingDrawableResourcesName=Missing Drawable Resources Checker
+
+localizationStringDescription=attempts to verify that the application is correctly localized, specifically by checking if all values are translated to all languages.\nNote that this checker only validates applications that have at least one language-specific resource.
+localizationStringName=Localization Strings Checker
+localizationString_missingDefaultLanguageKey_name=Keys missing in default language
+localizationString_missingDefaultLanguageKey_description=Keys are present in other languages that are missing in the default language.
+localizationStrings_missingLanguageKey_name=Keys missing in alternate language
+localizationStrings_missingLanguageKey_description=Keys are present in the default language that are missing in non-default languages.
+localizationStrings_missingValue_name=Keys without value
+localizationStrings_missingValue_description=One or more keys exist without a defined value either in the default language or in other languages.
+
+androidMarketFiltersCheckerDescription=Helps prevent your application from being rejected by Google Play, and from not appearing in Google Play on some devices.
+androidMarketFiltersCheckerName=Google Play Filters Checker
+androidMarketFilters_logCallsConditionName=Log Calls
+androidMarketFilters_logCallsConditionDescription=Search for log calls within code.
+androidMarketFilters_isDebuggableConditionName=Debuggable
+androidMarketFilters_isDebuggableConditionDescription=Verifies if the android:debuggable flag is set to true.
+androidMarketFilters_certificateExpiredConditionName=Certificate Period Expired
+androidMarketFilters_certificateExpiredConditionDescription=Verify that the certificate period is valid as required by Google Play.
+androidMarketFilters_missingManifestIconOrLabelConditionName=Missing Icon or Label
+androidMarketFilters_missingManifestIconOrLabelConditionDescription=Verify the existence of android:icon and android:label in the Android manifest.
+androidMarketFilters_missingVersionCodeOrNameConditionName=Missing Version Code or Name
+androidMarketFilters_missingVersionCodeOrNameConditionDescription=Verify the existence of android:versionCode and android:versionName in the Android manifest.
+androidMarketFilters_missingMinSdkVersionConditionName=Missing minSdkVersion
+androidMarketFilters_missingMinSdkVersionConditionDescription=Check if the uses-sdk android:minSdkVersion property is missing. This may lead to an application crash if your app uses an API that is not supported by an older Android OS version.
+androidMarketFilters_declaredMaxSdkVersionConditionName=Declared maxSdkVersion
+androidMarketFilters_declaredMaxSdkVersionConditionDescription=Check if the uses-sdk android:maxSdkVersion property is set.
+androidMarketFilters_minSdkFilterSmallScreenDevicesConditionName=Availability for Small Screen Devices
+androidMarketFilters_minSdkFilterSmallScreenDevicesConditionDescription=See if the uses-sdk android:minSdkVersion property may prevent applications from appearing in Google Play on small-screen devices.
+androidMarketFilters_permissionToImpliedFeaturesConditionName=Features implied by permissions
+androidMarketFilters_permissionToImpliedFeaturesConditionDescription=Checks if an application declares permissions without declaring required implied features.
+androidMarketFilters_minSdkIsPreviewConditionName=Preview minSdkVersion
+androidMarketFilters_minSdkIsPreviewConditionDescription=Checks if the SDK specified in the android:minSdkVersion attribute within the Android manifest file is a preview SDK.
+androidMarketFilters_targetSdkIsPreviewConditionName=Preview targetSdkVersion
+androidMarketFilters_targetSdkIsPreviewConditionDescription=Checks if an application declares an android:targetSdkVersion within its Android manifest file that is for an SDK preview release.
+androidMarketFilters_zipalignedConditionName=Zipaligned
+androidMarketFilters_zipalignedConditionDescription=Checks if the APK file is optimized using zipalign.
+
+deviceCompatibilityCheckerDescription=Looks for properties within your Android application that are not suitable for a particular device.
+deviceCompatibilityCheckerName=Device Compatibility Checker
+DeviceCompatibilityChecker_SmallScreens_Support_Condition_Name=Small Screens Support
+DeviceCompatibilityChecker_SmallScreens_Support_Condition_Description=Support for devices with small screens.
+DeviceCompatibilityChecker_Unsupported_Features_Condition_Name=Unsupported Features
+DeviceCompatibilityChecker_Unsupported_Features_Condition_Description=Checks the application manifest for permission or feature declarations that are not supported for a particular device, which might prevent the app from being installed on that device.
+
+DeviceCompatibilityChecker_XLargeScreens_Support_Condition_Name=Extra Large Screens Support
+DeviceCompatibilityChecker_XLargeScreens_Support_Condition_Description=Support for devices with extra large screens.
+
+
+permissionsCheckerDescription=Examines the permissions required by the app and the APIs used by the app, looking for possible problems.
+permissionsCheckerName=Permissions Checker
+
+buildingBlocksDeclarationCheckerDescription=Looks for inconsistencies within the Android building blocks declared in your AndroidManifest.xml file.
+buildingBlocksDeclarationCheckerName=Building Blocks Declaration Checker
+
+buildingBlocksDeclarationCheckerDescription_Inheritance = Check whether all building blocks declared in AndroidManifest.xml extend the classes they are meant to inherit from.
+buildingBlocksDeclarationCheckerName_Inheritance = Building Blocks Inheritance
+
+layoutCheckerDescription=Examine the application layout files, looking for problems in their definitions and inconsistencies among the layouts for different configurations.
+layoutCheckerName=Layout Checker
+layoutChecker_MissingIDConditionName=Missing View ID
+layoutChecker_MissingIDConditionDescription=Check if all layouts are using the same set of IDs for their components.
+layoutChecker_repeatedIdConditionName=Repeated View ID
+layoutChecker_repeatedIdConditionDescription=Reads all layout files looking for repeated IDs.
+layoutChecker_viewTypeIdsConditionName=View Type ID
+layoutChecker_viewTypeIdsConditionDescription=Checks if an ID is always used by the same kind of view on different layout configurations.
+layoutChecker_XlargeConfigConditionName = XLarge Layouts
+layoutChecker_XlargeConfigConditionDescription=Checks to see if the project contains layouts under xlarge folders.
+
+permissionsChecker_MissingPermissionName=Missing Permissions
+permissionsChecker_MissingPermissionDescription=Check for required permissions that are not declared in the AndroidManifest.xml file.
+
+permissionsChecker_BlockedPermissionName=Blocked Permissions
+permissionsChecker_BlockedPermissionDescription=Verify if a restricted permission is required by the application. Restricted permissions can only be used by applications signed with the same certificates as those in the system image (in other words, mostly by vendor applications, not third-party applications).
+
+widgetPreviewCheckerDescription=Verify if a widget project supports the preview feature
+widgetPreviewCheckerName=Widget Preview Checker
+
+missingWidgetPreviewConditionName=Missing Widget Preview
+missingWidgetPreviewConditionDescription=Verify that there is a declaration for the widgetPreview tag
+
+UnneededPermissions.description = Looks for any unneeded permissions declared in the manifest file
+UnneededPermissions.Name = Unneeded Permissions
+
+codeChecker_Description=Looks for possible issues in your Java code
+codeChecker_Name = Code Checker
+openedCursorsCondition.description=Looks for methods with non-closed cursors
+openedCursorsCondition.name = Opened Cursors
+
+orphanedItemsCheckerName=Orphaned Items Checker
+orphanedItemsCheckerDescription=Looks for declared items that are not referenced in Java or XML files
+
+orphanedStringsConditionName=Orphaned Strings
+orphanedStringsConditionDescription=\tLooks for declared strings that are not referenced in Java or XML files
+
+MissingDrawableChecker_conditionMissingDrawableFolders_Name=Missing Drawable Folders
+MissingDrawableChecker_conditionMissingDrawableFolders_Description=Existence of all density-specific drawable folders.
+
+MissingDrawableChecker_conditionMissingDrawableResources_Name=Missing Drawable Resources
+MissingDrawableChecker_conditionMissingDrawableResources_Description=Existence of all density-specific versions of drawable resources (images).
+
+MissingDrawableChecker_conditionUnsupportedDensity_Name=All densities support
+MissingDrawableChecker_conditionUnsupportedDensity_Description=Verify that the application's API level supports all referenced density values.
diff --git a/src/plugins/preflighting.checkers/plugin.xml b/src/plugins/preflighting.checkers/plugin.xml
new file mode 100644
index 0000000..8ae55bd
--- /dev/null
+++ b/src/plugins/preflighting.checkers/plugin.xml
@@ -0,0 +1,326 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?eclipse version="3.4"?>
+
+<!--
+ Copyright (C) 2012 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<plugin>
+ <extension
+ point="com.motorolamobility.preflighting.core.checker">
+ <checker
+ class="com.motorolamobility.preflighting.core.checker.Checker"
+ description="%mainactivitydescription"
+ id="singleMainActivity"
+ name="%mainactivityname">
+ <condition
+ class="com.motorolamobility.preflighting.checkers.mainactivity.SingleMainActivityCondition"
+ defaultSeverityLevel="WARNING"
+ description="%mainActivity_singleMainActivity_ConditionDescription"
+ id="singleMainActivity"
+ name="%mainActivity_singleMainActivity_ConditionName">
+ </condition>
+ </checker>
+ <checker
+ class="com.motorolamobility.preflighting.checkers.missingdrawable.MissingDrawableChecker"
+ description="%missingDrawableResourcesDescription"
+ id="missingDrawableResources"
+ name="%missingDrawableResourcesName">
+ <condition
+ class="com.motorolamobility.preflighting.checkers.missingdrawable.MissingDrawableFoldersCondition"
+ defaultSeverityLevel="WARNING"
+ description="%MissingDrawableChecker_conditionMissingDrawableFolders_Description"
+ id="missingDrawableFolders"
+ name="%MissingDrawableChecker_conditionMissingDrawableFolders_Name">
+ </condition>
+ <condition
+ class="com.motorolamobility.preflighting.checkers.missingdrawable.MissingDrawableResourcesCondition"
+ defaultSeverityLevel="WARNING"
+ description="%MissingDrawableChecker_conditionMissingDrawableResources_Description"
+ id="missingDrawables"
+ name="%MissingDrawableChecker_conditionMissingDrawableResources_Name">
+ </condition>
+ <condition
+ class="com.motorolamobility.preflighting.checkers.missingdrawable.AllDensitiesSupportCondition"
+ defaultSeverityLevel="WARNING"
+ description="%MissingDrawableChecker_conditionUnsupportedDensity_Description"
+ id="unsupportedDensity"
+ name="%MissingDrawableChecker_conditionUnsupportedDensity_Name">
+ </condition>
+ </checker>
+ <checker
+ class="com.motorolamobility.preflighting.checkers.localizationStrings.LocalizationStringsChecker"
+ description="%localizationStringDescription"
+ id="localizationStrings"
+ name="%localizationStringName">
+ <condition
+ class="com.motorolamobility.preflighting.checkers.localizationStrings.MissingDefaultLanguageKeyCondition"
+ defaultSeverityLevel="ERROR"
+ description="%localizationString_missingDefaultLanguageKey_description"
+ id="missingDefaultLanguageKey"
+ name="%localizationString_missingDefaultLanguageKey_name">
+ </condition>
+ <condition
+ class="com.motorolamobility.preflighting.checkers.localizationStrings.MissingLanguageKeyCondition"
+ defaultSeverityLevel="WARNING"
+ description="%localizationStrings_missingLanguageKey_description"
+ id="missingLanguageKey"
+ name="%localizationStrings_missingLanguageKey_name">
+ </condition>
+ <condition
+ class="com.motorolamobility.preflighting.checkers.localizationStrings.MissingValueCondition"
+ defaultSeverityLevel="WARNING"
+ description="%localizationStrings_missingValue_description"
+ id="missingValue"
+ name="%localizationStrings_missingValue_name">
+ </condition>
+ </checker>
+ <checker
+ description="%androidMarketFiltersCheckerDescription"
+ id="googlePlayFilters"
+ name="%androidMarketFiltersCheckerName">
+ <condition
+ class="com.motorolamobility.preflighting.checkers.androidmarketfilters.LogCallsCondition"
+ defaultSeverityLevel="WARNING"
+ description="%androidMarketFilters_logCallsConditionDescription"
+ id="logCalls"
+ name="%androidMarketFilters_logCallsConditionName">
+ </condition>
+ <condition
+ class="com.motorolamobility.preflighting.checkers.androidmarketfilters.IsDebuggableCondition"
+ defaultSeverityLevel="WARNING"
+ description="%androidMarketFilters_isDebuggableConditionDescription"
+ id="isDebuggable"
+ name="%androidMarketFilters_isDebuggableConditionName">
+ </condition>
+
+
+
+
+
+
+
+ <condition
+ class="com.motorolamobility.preflighting.checkers.androidmarketfilters.CertificateExpiredCondition"
+ defaultSeverityLevel="ERROR"
+ description="%androidMarketFilters_certificateExpiredConditionDescription"
+ id="certificatePeriodExpired"
+ name="%androidMarketFilters_certificateExpiredConditionName">
+ </condition>
+ <condition
+ class="com.motorolamobility.preflighting.checkers.androidmarketfilters.MissingIconLabelCondition"
+ defaultSeverityLevel="ERROR"
+ description="%androidMarketFilters_missingManifestIconOrLabelConditionDescription"
+ id="missingManifestIconOrLabel"
+ name="%androidMarketFilters_missingManifestIconOrLabelConditionName">
+ </condition>
+ <condition
+ class="com.motorolamobility.preflighting.checkers.androidmarketfilters.MissingVersionOrNameCondition"
+ defaultSeverityLevel="ERROR"
+ description="%androidMarketFilters_missingVersionCodeOrNameConditionDescription"
+ id="missingVersionCodeOrName"
+ name="%androidMarketFilters_missingVersionCodeOrNameConditionName">
+ </condition>
+ <condition
+ class="com.motorolamobility.preflighting.checkers.androidmarketfilters.MissingMinSdkCondition"
+ defaultSeverityLevel="ERROR"
+ description="%androidMarketFilters_missingMinSdkVersionConditionDescription"
+ id="missingMinSdkVersion"
+ markerType="com.motorolamobility.preflighting.checkers.ui.googlePlayFiltersMissingMinSDK"
+ name="%androidMarketFilters_missingMinSdkVersionConditionName">
+ </condition>
+ <condition
+ class="com.motorolamobility.preflighting.checkers.androidmarketfilters.DeclaredMaxSdkCondition"
+ defaultSeverityLevel="WARNING"
+ description="%androidMarketFilters_declaredMaxSdkVersionConditionDescription"
+ id="declaredMaxSdkVersion"
+ markerType="com.motorolamobility.preflighting.checkers.ui.googlePlayFiltersUneededMaxSDK"
+ name="%androidMarketFilters_declaredMaxSdkVersionConditionName">
+ </condition>
+ <condition
+ class="com.motorolamobility.preflighting.checkers.androidmarketfilters.PermissionImpliedFeaturesCondition"
+ defaultSeverityLevel="WARNING"
+ description="%androidMarketFilters_permissionToImpliedFeaturesConditionDescription"
+ id="permissionToImpliedFeatures"
+ markerType="com.motorolamobility.preflighting.checkers.ui.impliedFeaturesMarker"
+ name="%androidMarketFilters_permissionToImpliedFeaturesConditionName">
+ </condition>
+ <condition
+ class="com.motorolamobility.preflighting.checkers.androidmarketfilters.MinSdkIsPreviewCondition"
+ defaultSeverityLevel="WARNING"
+ description="%androidMarketFilters_minSdkIsPreviewConditionDescription"
+ id="minSdkIsPreview"
+ name="%androidMarketFilters_minSdkIsPreviewConditionName">
+ </condition>
+ <condition
+ class="com.motorolamobility.preflighting.checkers.androidmarketfilters.PreviewTargetSdkCondition"
+ defaultSeverityLevel="WARNING"
+ description="%androidMarketFilters_targetSdkIsPreviewConditionDescription"
+ id="targetSdkIsPreview"
+ name="%androidMarketFilters_targetSdkIsPreviewConditionName">
+ </condition>
+ <condition
+ class="com.motorolamobility.preflighting.checkers.androidmarketfilters.ZipalignedCondition"
+ defaultSeverityLevel="WARNING"
+ description="%androidMarketFilters_zipalignedConditionDescription"
+ id="zipaligned"
+ name="%androidMarketFilters_zipalignedConditionName">
+ </condition>
+ </checker>
+ <checker
+ class="com.motorolamobility.preflighting.core.checker.Checker"
+ description="%deviceCompatibilityCheckerDescription"
+ id="deviceCompatibility"
+ name="%deviceCompatibilityCheckerName">
+ <condition
+ class="com.motorolamobility.preflighting.checkers.deviceCompatibility.UnsupportedFeaturesConditions"
+ defaultSeverityLevel="WARNING"
+ description="%DeviceCompatibilityChecker_Unsupported_Features_Condition_Description"
+ id="unsupportedFeatures"
+ markerType="com.motorolamobility.preflighting.checkers.ui.deviceCompatibilityUnsupportedFeaturesMarker"
+ name="%DeviceCompatibilityChecker_Unsupported_Features_Condition_Name">
+ </condition>
+ <condition
+ class="com.motorolamobility.preflighting.checkers.deviceCompatibility.SmallScreensSupportCondition"
+ defaultSeverityLevel="ERROR"
+ description="%DeviceCompatibilityChecker_SmallScreens_Support_Condition_Description"
+ id="smallScreenSupport"
+ name="%DeviceCompatibilityChecker_SmallScreens_Support_Condition_Name">
+ </condition>
+ <condition
+ class="com.motorolamobility.preflighting.checkers.deviceCompatibility.XLargeScreensSupportCondition"
+ defaultSeverityLevel="WARNING"
+ description="%DeviceCompatibilityChecker_XLargeScreens_Support_Condition_Description"
+ id="xlargeScreenSupport"
+ name="%DeviceCompatibilityChecker_XLargeScreens_Support_Condition_Name">
+ </condition>
+ </checker>
+ <checker
+ class="com.motorolamobility.preflighting.core.checker.Checker"
+ description="%buildingBlocksDeclarationCheckerDescription"
+ id="buildingBlocksDeclaration"
+ name="%buildingBlocksDeclarationCheckerName">
+ <condition
+ class="com.motorolamobility.preflighting.checkers.buildingblocksdeclaration.BuildingBlocksInheritanceCondition"
+ defaultSeverityLevel="WARNING"
+ description="%buildingBlocksDeclarationCheckerDescription_Inheritance"
+ id="buildingBlockMissDeclaration"
+ name="%buildingBlocksDeclarationCheckerName_Inheritance">
+ </condition>
+ </checker>
+ <checker
+ class="com.motorolamobility.preflighting.checkers.permissions.PermissionsChecker"
+ description="%permissionsCheckerDescription"
+ id="permissions"
+ name="%permissionsCheckerName">
+ <condition
+ class="com.motorolamobility.preflighting.checkers.permissions.MissingPermissionsCondition"
+ defaultSeverityLevel="WARNING"
+ description="%permissionsChecker_MissingPermissionDescription"
+ id="missingPermission"
+ markerType="com.motorolamobility.preflighting.checkers.ui.missingPermissionsMarker"
+ name="%permissionsChecker_MissingPermissionName">
+ </condition>
+ <condition
+ class="com.motorolamobility.preflighting.checkers.permissions.BlockedPermissionCondition"
+ defaultSeverityLevel="WARNING"
+ description="%permissionsChecker_BlockedPermissionDescription"
+ id="blockedPermission"
+ name="%permissionsChecker_BlockedPermissionName">
+ </condition>
+ <condition
+ class="com.motorolamobility.preflighting.checkers.permissions.UnneededPermissionsCondition"
+ defaultSeverityLevel="WARNING"
+ description="%UnneededPermissions.description"
+ id="unneededPermission"
+ markerType="com.motorolamobility.preflighting.checkers.ui.unneededPermissionsMarker"
+ name="%UnneededPermissions.Name">
+ </condition>
+ </checker>
+ <checker
+ class="com.motorolamobility.preflighting.core.checker.Checker"
+ description="%layoutCheckerDescription"
+ id="layout"
+ name="%layoutCheckerName">
+ <condition
+ class="com.motorolamobility.preflighting.checkers.layout.MissingIdCondition"
+ defaultSeverityLevel="WARNING"
+ description="%layoutChecker_MissingIDConditionDescription"
+ id="missingId"
+ name="%layoutChecker_MissingIDConditionName">
+ </condition>
+ <condition
+ class="com.motorolamobility.preflighting.checkers.layout.RepeatedIdCondition"
+ defaultSeverityLevel="WARNING"
+ description="%layoutChecker_repeatedIdConditionDescription"
+ id="repeatedId"
+ name="%layoutChecker_repeatedIdConditionName">
+ </condition>
+ <condition
+ class="com.motorolamobility.preflighting.checkers.layout.ViewTypeIdsCondition"
+ defaultSeverityLevel="WARNING"
+ description="%layoutChecker_viewTypeIdsConditionDescription"
+ id="viewTypeIds"
+ name="%layoutChecker_viewTypeIdsConditionName">
+ </condition>
+ <condition
+ class="com.motorolamobility.preflighting.checkers.layout.XlargeConfigCondition"
+ defaultSeverityLevel="WARNING"
+ description="%layoutChecker_XlargeConfigConditionDescription"
+ id="xlargeLayouts"
+ name="%layoutChecker_XlargeConfigConditionName">
+ </condition>
+ </checker>
+ <checker
+ class="com.motorolamobility.preflighting.core.checker.Checker"
+ description="%widgetPreviewCheckerDescription"
+ id="widgetPreview"
+ name="%widgetPreviewCheckerName">
+ <condition
+ class="com.motorolamobility.preflighting.checkers.widgetPreview.MissingWidgetPreviewTagCondition"
+ defaultSeverityLevel="WARNING"
+ description="%missingWidgetPreviewConditionDescription"
+ id="missingWidgetPreview"
+ name="%missingWidgetPreviewConditionName">
+ </condition>
+ </checker>
+ <checker
+ class="com.motorolamobility.preflighting.core.checker.Checker"
+ description="%codeChecker_Description"
+ id="codeChecker"
+ name="Code Checker">
+ <condition
+ class="com.motorolamobility.preflighting.checkers.logic.OpenedCursorsCondition"
+ defaultSeverityLevel="WARNING"
+ description="%openedCursorsCondition.description"
+ id="openedCursors"
+ name="%openedCursorsCondition.name">
+ </condition>
+ </checker>
+ <checker
+ class="com.motorolamobility.preflighting.core.checker.Checker"
+ description="%orphanedItemsCheckerDescription"
+ id="orphanedItems"
+ name="%orphanedItemsCheckerName">
+ <condition
+ class="com.motorolamobility.preflighting.checkers.orphanedstrings.OrphanedStringsCondition"
+ defaultSeverityLevel="WARNING"
+ description="%orphanedStringsConditionDescription"
+ id="orphanedStrings"
+ name="%orphanedStringsConditionName">
+ </condition>
+ </checker>
+ </extension>
+</plugin>
diff --git a/src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/CheckerPlugin.java b/src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/CheckerPlugin.java
new file mode 100644
index 0000000..746b42b
--- /dev/null
+++ b/src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/CheckerPlugin.java
@@ -0,0 +1,58 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+package com.motorolamobility.preflighting.checkers;
+
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+
+import com.motorolamobility.preflighting.core.logging.PreflightingLogger;
+
+public class CheckerPlugin implements BundleActivator
+{
+
+ private static BundleContext context;
+
+ public static final String PLUGIN_ID = "com.motorolamobility.preflighting.checkers";
+
+ static BundleContext getContext()
+ {
+ return context;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.osgi.framework.BundleActivator#start(org.osgi.framework.BundleContext)
+ */
+ public void start(BundleContext bundleContext) throws Exception
+ {
+ PreflightingLogger.debug(CheckerPlugin.class, "Starting Preflighting Checker Plugin...");
+
+ CheckerPlugin.context = bundleContext;
+
+ PreflightingLogger.debug(CheckerPlugin.class, "Preflighting Checker Plugin started...");
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.osgi.framework.BundleActivator#stop(org.osgi.framework.BundleContext)
+ */
+ public void stop(BundleContext bundleContext) throws Exception
+ {
+ CheckerPlugin.context = null;
+ }
+
+}
diff --git a/src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/androidmarketfilters/CertificateExpiredCondition.java b/src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/androidmarketfilters/CertificateExpiredCondition.java
new file mode 100644
index 0000000..fd6085d
--- /dev/null
+++ b/src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/androidmarketfilters/CertificateExpiredCondition.java
@@ -0,0 +1,129 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorolamobility.preflighting.checkers.androidmarketfilters;
+
+import java.security.cert.Certificate;
+import java.security.cert.CertificateExpiredException;
+import java.security.cert.CertificateNotYetValidException;
+import java.security.cert.X509Certificate;
+import java.util.Calendar;
+import java.util.GregorianCalendar;
+import java.util.List;
+
+import org.eclipse.core.runtime.IStatus;
+
+import com.motorolamobility.preflighting.checkers.i18n.CheckerNLS;
+import com.motorolamobility.preflighting.core.PreflightingCorePlugin;
+import com.motorolamobility.preflighting.core.applicationdata.ApplicationData;
+import com.motorolamobility.preflighting.core.checker.condition.CanExecuteConditionStatus;
+import com.motorolamobility.preflighting.core.checker.condition.Condition;
+import com.motorolamobility.preflighting.core.devicespecification.DeviceSpecification;
+import com.motorolamobility.preflighting.core.exception.PreflightingCheckerException;
+import com.motorolamobility.preflighting.core.internal.cond.utils.ConditionUtils;
+import com.motorolamobility.preflighting.core.logging.PreflightingLogger;
+import com.motorolamobility.preflighting.core.validation.ValidationManagerConfiguration;
+import com.motorolamobility.preflighting.core.validation.ValidationResult;
+import com.motorolamobility.preflighting.core.validation.ValidationResultData;
+
+public class CertificateExpiredCondition extends Condition
+{
+
+ private ValidationManagerConfiguration valManagerConfig;
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorolamobility.preflighting.core.checker.condition.Condition#execute(com.motorolamobility.preflighting.core.applicationdata.ApplicationData, java.util.List, com.motorolamobility.preflighting.core.validation.ValidationManagerConfiguration, com.motorolamobility.preflighting.core.validation.ValidationResult)
+ */
+ @Override
+ public void execute(ApplicationData data, List<DeviceSpecification> deviceSpecs,
+ ValidationManagerConfiguration valManagerConfig, ValidationResult results)
+ throws PreflightingCheckerException
+ {
+ this.valManagerConfig = valManagerConfig;
+ Calendar date = GregorianCalendar.getInstance();
+ date.clear();
+ date.set(2033, Calendar.OCTOBER, 22);
+ List<Certificate> certList = data.getCertificateChain();
+ if (certList != null)
+ {
+ for (Certificate cert : certList)
+ {
+ if (cert instanceof X509Certificate)
+ {
+ X509Certificate x509Cert = (X509Certificate) cert;
+ try
+ {
+ x509Cert.checkValidity(date.getTime());
+ }
+ catch (CertificateExpiredException ce)
+ {
+ // exception means certificate expired
+ addValidationResult(
+ results,
+ getId(),
+ CheckerNLS
+ .bind(CheckerNLS.AndroidMarketFiltersChecker_certificatePeriodExpired_Issue,
+ x509Cert.getNotAfter()),
+ CheckerNLS.AndroidMarketFiltersChecker_certificatePeriodExpired_Suggestion);
+ }
+ catch (CertificateNotYetValidException e)
+ {
+ //certificate did not expire yet, but it is not valid until 22 October 2033
+ addValidationResult(
+ results,
+ getId(),
+ CheckerNLS
+ .bind(CheckerNLS.AndroidMarketFiltersChecker_certificatePeriodNotYeatValid_Issue,
+ x509Cert.getNotBefore()),
+ CheckerNLS.AndroidMarketFiltersChecker_certificatePeriodExpired_Suggestion);
+ }
+ }
+ else
+ {
+ PreflightingLogger.error(CertificateExpiredCondition.class,
+ "Unrecognized certificate type"); //$NON-NLS-1$
+ }
+ }
+ }
+ }
+
+ private void addValidationResult(ValidationResult results, String id, String issueDescription,
+ String quickFixSuggestion)
+ {
+ ValidationResultData resultData = new ValidationResultData();
+ resultData.setSeverity(getSeverityLevel());
+ resultData.setConditionID(getId());
+ resultData.setIssueDescription(issueDescription);
+ resultData.setQuickFixSuggestion(quickFixSuggestion);
+ resultData.setInfoURL(ConditionUtils.getDescriptionLink(getChecker().getId(), getId(),
+ valManagerConfig));
+ results.addValidationResult(resultData);
+
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorolamobility.preflighting.core.checker.condition.Condition#canExecute(com.motorolamobility.preflighting.core.applicationdata.ApplicationData, java.util.List)
+ */
+ @Override
+ public CanExecuteConditionStatus canExecute(ApplicationData data,
+ List<DeviceSpecification> deviceSpecs) throws PreflightingCheckerException
+ {
+ return new CanExecuteConditionStatus(IStatus.OK, PreflightingCorePlugin.PLUGIN_ID, "",
+ getId());
+ }
+
+}
diff --git a/src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/androidmarketfilters/DeclaredMaxSdkCondition.java b/src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/androidmarketfilters/DeclaredMaxSdkCondition.java
new file mode 100644
index 0000000..36a8d9c
--- /dev/null
+++ b/src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/androidmarketfilters/DeclaredMaxSdkCondition.java
@@ -0,0 +1,116 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorolamobility.preflighting.checkers.androidmarketfilters;
+
+import java.util.List;
+
+import org.w3c.dom.DOMException;
+import org.w3c.dom.Document;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import com.motorolamobility.preflighting.checkers.i18n.CheckerNLS;
+import com.motorolamobility.preflighting.core.applicationdata.ApplicationData;
+import com.motorolamobility.preflighting.core.applicationdata.XMLElement;
+import com.motorolamobility.preflighting.core.checker.condition.CanExecuteConditionStatus;
+import com.motorolamobility.preflighting.core.checker.condition.Condition;
+import com.motorolamobility.preflighting.core.checker.condition.ICondition;
+import com.motorolamobility.preflighting.core.devicespecification.DeviceSpecification;
+import com.motorolamobility.preflighting.core.exception.PreflightingCheckerException;
+import com.motorolamobility.preflighting.core.internal.cond.utils.ConditionUtils;
+import com.motorolamobility.preflighting.core.utils.CheckerUtils;
+import com.motorolamobility.preflighting.core.utils.ManifestConstants;
+import com.motorolamobility.preflighting.core.utils.XmlUtils;
+import com.motorolamobility.preflighting.core.validation.ValidationManagerConfiguration;
+import com.motorolamobility.preflighting.core.validation.ValidationResult;
+import com.motorolamobility.preflighting.core.validation.ValidationResultData;
+
+public class DeclaredMaxSdkCondition extends Condition implements ICondition
+{
+
+ private XMLElement manifestElement;
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorolamobility.preflighting.core.checker.condition.Condition#execute(com.motorolamobility.preflighting.core.applicationdata.ApplicationData, java.util.List, com.motorolamobility.preflighting.core.validation.ValidationManagerConfiguration, com.motorolamobility.preflighting.core.validation.ValidationResult)
+ */
+ @Override
+ public void execute(ApplicationData data, List<DeviceSpecification> deviceSpecs,
+ ValidationManagerConfiguration valManagerConfig, ValidationResult results)
+ throws PreflightingCheckerException
+ {
+ manifestElement = data.getManifestElement();
+ Document manifestDoc = manifestElement.getDocument();
+ NodeList usesSdkList = manifestDoc.getElementsByTagName(ManifestConstants.USES_SDK_TAG);
+
+ if (usesSdkList.getLength() > 0)
+ {
+ // Check if attribute exists.
+ for (int i = 0; i < usesSdkList.getLength(); i++)
+ {
+ Node usesSdkNode = usesSdkList.item(i);
+ NamedNodeMap map = usesSdkNode.getAttributes();
+
+ int currentIssuedLine;
+ Node atr = map.getNamedItem(ManifestConstants.ANDROID_MAX_SDK_VERSION_ATTRIBUTE);
+ try
+ {
+ if ((atr != null) && (atr.getNodeValue().length() > 0)) //$NON-NLS-1$
+ {
+ // item exist but it is not recommended
+ currentIssuedLine = manifestElement.getNodeLineNumber(usesSdkNode);
+ ValidationResultData resultData =
+ new ValidationResultData(
+ CheckerUtils.createFileToIssuesMap(
+ manifestElement.getFile(), currentIssuedLine),
+ getSeverityLevel(),
+ CheckerNLS.AndroidMarketFiltersChecker_declaredMaxSdkVersion_Issue,
+ CheckerNLS.AndroidMarketFiltersChecker_declaredMaxSdkVersion_Suggestion,
+ getId());
+ resultData.setInfoURL(ConditionUtils.getDescriptionLink(getChecker()
+ .getId(), getId(), valManagerConfig));
+ resultData.setPreview(XmlUtils.getXMLNodeAsString(usesSdkNode, false));
+ resultData.setMarkerType(getMarkerType());
+ results.addValidationResult(resultData);
+ }
+
+ }
+ catch (DOMException e)
+ {
+ // Error retrieving attribute
+ throw new PreflightingCheckerException(
+ CheckerNLS.bind(
+ CheckerNLS.AndroidMarketFiltersChecker_Exception_Getting_Manifest_Attribute,
+ ManifestConstants.ANDROID_MAX_SDK_VERSION_ATTRIBUTE), e);
+ }
+ }
+ }
+
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorolamobility.preflighting.core.checker.condition.Condition#canExecute(com.motorolamobility.preflighting.core.applicationdata.ApplicationData, java.util.List)
+ */
+ @Override
+ public CanExecuteConditionStatus canExecute(ApplicationData data,
+ List<DeviceSpecification> deviceSpecs) throws PreflightingCheckerException
+ {
+ return CheckerUtils.isAndroidManifestFileExistent(data, getId());
+ }
+
+}
diff --git a/src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/androidmarketfilters/IsDebuggableCondition.java b/src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/androidmarketfilters/IsDebuggableCondition.java
new file mode 100644
index 0000000..57422d7
--- /dev/null
+++ b/src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/androidmarketfilters/IsDebuggableCondition.java
@@ -0,0 +1,125 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorolamobility.preflighting.checkers.androidmarketfilters;
+
+import java.io.File;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.w3c.dom.Attr;
+import org.w3c.dom.Document;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import com.motorolamobility.preflighting.checkers.i18n.CheckerNLS;
+import com.motorolamobility.preflighting.core.applicationdata.ApplicationData;
+import com.motorolamobility.preflighting.core.applicationdata.XMLElement;
+import com.motorolamobility.preflighting.core.checker.condition.CanExecuteConditionStatus;
+import com.motorolamobility.preflighting.core.checker.condition.Condition;
+import com.motorolamobility.preflighting.core.checker.condition.ICondition;
+import com.motorolamobility.preflighting.core.devicespecification.DeviceSpecification;
+import com.motorolamobility.preflighting.core.exception.PreflightingCheckerException;
+import com.motorolamobility.preflighting.core.utils.CheckerUtils;
+import com.motorolamobility.preflighting.core.utils.ManifestConstants;
+import com.motorolamobility.preflighting.core.utils.XmlUtils;
+import com.motorolamobility.preflighting.core.validation.ValidationManagerConfiguration;
+import com.motorolamobility.preflighting.core.validation.ValidationResult;
+import com.motorolamobility.preflighting.core.validation.ValidationResultData;
+
+/**
+ * Condition that verifies if the android:debuggable flag is set to true.
+ * It is recommended to set this flag to false before publishing the app.
+ * A warning message is added to the results if the flag is true.
+ */
+public class IsDebuggableCondition extends Condition implements ICondition
+{
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorolamobility.preflighting.core.checker.condition.Condition#canExecute(com.motorolamobility.preflighting.core.applicationdata.ApplicationData, java.util.List)
+ */
+ @Override
+ public CanExecuteConditionStatus canExecute(ApplicationData data,
+ List<DeviceSpecification> deviceSpecs) throws PreflightingCheckerException
+ {
+ return CheckerUtils.isAndroidManifestFileExistent(data, getId());
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorolamobility.preflighting.core.checker.condition.Condition#execute(com.motorolamobility.preflighting.core.applicationdata.ApplicationData, java.util.List, com.motorolamobility.preflighting.core.devicespecification.PlatformRules, com.motorolamobility.preflighting.core.validation.ValidationManagerConfiguration, com.motorolamobility.preflighting.core.validation.ValidationResult)
+ */
+ @Override
+ public void execute(ApplicationData data, List<DeviceSpecification> deviceSpecs,
+ ValidationManagerConfiguration valManagerConfig, ValidationResult results)
+ throws PreflightingCheckerException
+ {
+ XMLElement manifestElement = data.getManifestElement();
+ if (manifestElement != null)
+ {
+ Document manifestDoc = manifestElement.getDocument();
+ NodeList applicationNodes =
+ manifestDoc.getElementsByTagName(ManifestConstants.APPLICATION_TAG);
+
+ if (applicationNodes != null)
+ {
+ for (int i = 0; i < applicationNodes.getLength(); i++)
+ {
+ Node applicationNode = applicationNodes.item(i);
+ analyzeApplicationNode(results, manifestElement, applicationNode);
+ }
+ }
+ }
+
+ }
+
+ /**
+ * Looks for the android:debuggable attribute. If found add a warning resultData if the value is true.
+ * @param results
+ * @param manifestElement
+ * @param applicationNode
+ */
+ private void analyzeApplicationNode(ValidationResult results, XMLElement manifestElement,
+ Node applicationNode)
+ {
+ NamedNodeMap attributes = applicationNode.getAttributes();
+ Attr debuggableAttr =
+ (Attr) attributes.getNamedItem(ManifestConstants.ANDROID_DEBUGGABLE_ATTRIBUTE);
+ if (debuggableAttr != null)
+ {
+ String value = debuggableAttr.getValue();
+ if (value.equalsIgnoreCase("true")) ////$NON-NLS-1$
+ {
+ Map<File, List<Integer>> fileToIssueLines = new HashMap<File, List<Integer>>(1);
+ int lineNumber = manifestElement.getNodeLineNumber(applicationNode);
+ if (lineNumber > 0)
+ {
+ fileToIssueLines.put(manifestElement.getFile(), Arrays.asList(lineNumber));
+ }
+ ValidationResultData resultData =
+ new ValidationResultData(fileToIssueLines, getSeverityLevel(),
+ CheckerNLS.IsDebuggableCondition_AttrFound_Message,
+ CheckerNLS.IsDebuggableCondition_AttrFound_QuickFix, getId());
+ resultData.setPreview(XmlUtils.getXMLNodeAsString(applicationNode, false));
+ results.addValidationResult(resultData);
+ }
+ }
+ }
+
+}
diff --git a/src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/androidmarketfilters/LogCallsCondition.java b/src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/androidmarketfilters/LogCallsCondition.java
new file mode 100644
index 0000000..3c3479b
--- /dev/null
+++ b/src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/androidmarketfilters/LogCallsCondition.java
@@ -0,0 +1,118 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorolamobility.preflighting.checkers.androidmarketfilters;
+
+import java.io.File;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import com.motorolamobility.preflighting.checkers.i18n.CheckerNLS;
+import com.motorolamobility.preflighting.core.applicationdata.ApplicationData;
+import com.motorolamobility.preflighting.core.applicationdata.SourceFolderElement;
+import com.motorolamobility.preflighting.core.checker.condition.CanExecuteConditionStatus;
+import com.motorolamobility.preflighting.core.checker.condition.Condition;
+import com.motorolamobility.preflighting.core.devicespecification.DeviceSpecification;
+import com.motorolamobility.preflighting.core.exception.PreflightingCheckerException;
+import com.motorolamobility.preflighting.core.source.model.Invoke;
+import com.motorolamobility.preflighting.core.utils.CheckerUtils;
+import com.motorolamobility.preflighting.core.validation.ValidationManagerConfiguration;
+import com.motorolamobility.preflighting.core.validation.ValidationResult;
+import com.motorolamobility.preflighting.core.validation.ValidationResultData;
+
+/**
+ * Condition that verifies if there's any call to Android logcat within the java source code.
+ * If any is found a warning message is added to the results
+ */
+public class LogCallsCondition extends Condition
+{
+
+ /**
+ * Full-qualified name of android Log class.
+ */
+ public static final String ANDROID_UTIL_LOG = "android.util.Log";
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorolamobility.preflighting.core.checker.condition.Condition#canExecute(com.motorolamobility.preflighting.core.applicationdata.ApplicationData, java.util.List)
+ */
+ @Override
+ public CanExecuteConditionStatus canExecute(ApplicationData data,
+ List<DeviceSpecification> deviceSpecs) throws PreflightingCheckerException
+ {
+ return CheckerUtils.isJavaModelComplete(data, getId());
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorolamobility.preflighting.core.checker.condition.Condition#execute(com.motorolamobility.preflighting.core.applicationdata.ApplicationData, java.util.List, com.motorolamobility.preflighting.core.devicespecification.PlatformRules, com.motorolamobility.preflighting.core.validation.ValidationManagerConfiguration, com.motorolamobility.preflighting.core.validation.ValidationResult)
+ */
+ @Override
+ public void execute(ApplicationData data, List<DeviceSpecification> deviceSpecs,
+ ValidationManagerConfiguration valManagerConfig, ValidationResult results)
+ throws PreflightingCheckerException
+ {
+ List<SourceFolderElement> sourceFolderElements = data.getJavaModel();
+
+ for (SourceFolderElement sourceFolder : sourceFolderElements)
+ {
+ List<Invoke> calledMethods = sourceFolder.getInvokedMethods();
+ for (Invoke calledMethod : calledMethods)
+ {
+ analyzeCalledMethod(results, calledMethod);
+ }
+ }
+
+ }
+
+ /**
+ * Verify if the called method is a log call.
+ * If it is, add a warning result to results.
+ * @param results
+ * @param calledMethod
+ */
+ private void analyzeCalledMethod(ValidationResult results, Invoke calledMethod)
+ {
+ String classCalled = calledMethod.getClassCalled();
+
+ if ((classCalled != null) && (classCalled.equals(ANDROID_UTIL_LOG)))
+ {
+ if (calledMethod.getMethodName().equals("w") //$NON-NLS-1$
+ || calledMethod.getMethodName().equals("i") //$NON-NLS-1$
+ || calledMethod.getMethodName().equals("v") //$NON-NLS-1$
+ || calledMethod.getMethodName().equals("d") //$NON-NLS-1$
+ || calledMethod.getMethodName().equals("e") //$NON-NLS-1$
+ || calledMethod.getMethodName().equals("wtf")) //$NON-NLS-1$
+ {
+ Map<File, List<Integer>> fileToIssueLines = new HashMap<File, List<Integer>>(1);
+ int line = calledMethod.getLine();
+ if (line > 0)
+ {
+ fileToIssueLines.put(new File(calledMethod.getSourceFileFullPath()),
+ Arrays.asList(line));
+ }
+
+ ValidationResultData resultData =
+ new ValidationResultData(fileToIssueLines, getSeverityLevel(),
+ CheckerNLS.LogCallsCondition_CallFound_Message,
+ CheckerNLS.LogCallsCondition_CallFound_QuickFix, getId());
+ resultData.setPreview(calledMethod.getQualifiedName());
+ results.addValidationResult(resultData);
+ }
+ }
+ }
+}
diff --git a/src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/androidmarketfilters/MinSdkIsPreviewCondition.java b/src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/androidmarketfilters/MinSdkIsPreviewCondition.java
new file mode 100644
index 0000000..552ad37
--- /dev/null
+++ b/src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/androidmarketfilters/MinSdkIsPreviewCondition.java
@@ -0,0 +1,102 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorolamobility.preflighting.checkers.androidmarketfilters;
+
+import java.util.List;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import com.motorolamobility.preflighting.checkers.i18n.CheckerNLS;
+import com.motorolamobility.preflighting.core.applicationdata.ApplicationData;
+import com.motorolamobility.preflighting.core.applicationdata.XMLElement;
+import com.motorolamobility.preflighting.core.checker.condition.CanExecuteConditionStatus;
+import com.motorolamobility.preflighting.core.checker.condition.Condition;
+import com.motorolamobility.preflighting.core.checker.condition.ICondition;
+import com.motorolamobility.preflighting.core.devicespecification.DeviceSpecification;
+import com.motorolamobility.preflighting.core.exception.PreflightingCheckerException;
+import com.motorolamobility.preflighting.core.internal.cond.utils.ConditionUtils;
+import com.motorolamobility.preflighting.core.utils.CheckerUtils;
+import com.motorolamobility.preflighting.core.utils.ManifestConstants;
+import com.motorolamobility.preflighting.core.utils.XmlUtils;
+import com.motorolamobility.preflighting.core.validation.ValidationManagerConfiguration;
+import com.motorolamobility.preflighting.core.validation.ValidationResult;
+import com.motorolamobility.preflighting.core.validation.ValidationResultData;
+
+public class MinSdkIsPreviewCondition extends Condition implements ICondition
+{
+ /*
+ * (non-Javadoc)
+ * @see com.motorolamobility.preflighting.core.checker.condition.Condition#execute(com.motorolamobility.preflighting.core.applicationdata.ApplicationData, java.util.List, com.motorolamobility.preflighting.core.validation.ValidationManagerConfiguration, com.motorolamobility.preflighting.core.validation.ValidationResult)
+ */
+ @Override
+ public void execute(ApplicationData data, List<DeviceSpecification> deviceSpecs,
+ ValidationManagerConfiguration valManagerConfig, ValidationResult results)
+ throws PreflightingCheckerException
+ {
+ XMLElement manifestElement = data.getManifestElement();
+ Document manifestDoc = manifestElement.getDocument();
+ NodeList usesSdkList = manifestDoc.getElementsByTagName(ManifestConstants.USES_SDK_TAG);
+ if (usesSdkList.getLength() > 0)
+ {
+ // Check if attribute exists.
+ for (int i = 0; i < usesSdkList.getLength(); i++)
+ {
+ Node usesSdkNode = usesSdkList.item(i);
+ NamedNodeMap map = usesSdkNode.getAttributes();
+ Node minSdkAtr = map.getNamedItem(ManifestConstants.MIN_SDK_VERSION_ATTRIBUTE); //$NON-NLS-1$
+ try
+ {
+ if (minSdkAtr != null)
+ {
+ String minSdkStr = minSdkAtr.getNodeValue().trim();
+ Integer.valueOf(minSdkStr); //Try to get the int value. It the exception is thrown issue the warning.
+ }
+ }
+ catch (NumberFormatException e)
+ {
+ int currentIssuedLine = manifestElement.getNodeLineNumber(usesSdkNode);
+ ValidationResultData resultData =
+ new ValidationResultData(
+ CheckerUtils.createFileToIssuesMap(manifestElement.getFile(),
+ currentIssuedLine),
+ getSeverityLevel(),
+ CheckerNLS.AndroidMarketFiltersChecker_minSdkIsPreview_Issue,
+ CheckerNLS.AndroidMarketFiltersChecker_minSdkIsPreview_Suggestion,
+ getId());
+ resultData.setInfoURL(ConditionUtils.getDescriptionLink(getChecker().getId(),
+ getId(), valManagerConfig));
+ resultData.setPreview(XmlUtils.getXMLNodeAsString(usesSdkNode, false));
+ results.addValidationResult(resultData);
+ }
+ }
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorolamobility.preflighting.core.checker.condition.Condition#canExecute(com.motorolamobility.preflighting.core.applicationdata.ApplicationData, java.util.List)
+ */
+ @Override
+ public CanExecuteConditionStatus canExecute(ApplicationData data,
+ List<DeviceSpecification> deviceSpecs) throws PreflightingCheckerException
+ {
+ return CheckerUtils.isAndroidManifestFileExistent(data, getId());
+ }
+
+}
diff --git a/src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/androidmarketfilters/MissingIconLabelCondition.java b/src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/androidmarketfilters/MissingIconLabelCondition.java
new file mode 100644
index 0000000..9ff3eed
--- /dev/null
+++ b/src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/androidmarketfilters/MissingIconLabelCondition.java
@@ -0,0 +1,139 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorolamobility.preflighting.checkers.androidmarketfilters;
+
+import java.util.List;
+
+import org.w3c.dom.DOMException;
+import org.w3c.dom.Document;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import com.motorolamobility.preflighting.checkers.i18n.CheckerNLS;
+import com.motorolamobility.preflighting.core.applicationdata.ApplicationData;
+import com.motorolamobility.preflighting.core.applicationdata.XMLElement;
+import com.motorolamobility.preflighting.core.checker.condition.CanExecuteConditionStatus;
+import com.motorolamobility.preflighting.core.checker.condition.Condition;
+import com.motorolamobility.preflighting.core.devicespecification.DeviceSpecification;
+import com.motorolamobility.preflighting.core.exception.PreflightingCheckerException;
+import com.motorolamobility.preflighting.core.internal.cond.utils.ConditionUtils;
+import com.motorolamobility.preflighting.core.utils.CheckerUtils;
+import com.motorolamobility.preflighting.core.utils.ManifestConstants;
+import com.motorolamobility.preflighting.core.utils.XmlUtils;
+import com.motorolamobility.preflighting.core.validation.ValidationManagerConfiguration;
+import com.motorolamobility.preflighting.core.validation.ValidationResult;
+import com.motorolamobility.preflighting.core.validation.ValidationResultData;
+
+public class MissingIconLabelCondition extends Condition
+{
+
+ private XMLElement manifestElement;
+
+ private ValidationManagerConfiguration valManagerConfig;
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorolamobility.preflighting.core.checker.condition.Condition#execute(com.motorolamobility.preflighting.core.applicationdata.ApplicationData, java.util.List, com.motorolamobility.preflighting.core.validation.ValidationManagerConfiguration, com.motorolamobility.preflighting.core.validation.ValidationResult)
+ */
+ @Override
+ public void execute(ApplicationData data, List<DeviceSpecification> deviceSpecs,
+ ValidationManagerConfiguration valManagerConfig, ValidationResult results)
+ throws PreflightingCheckerException
+ {
+ this.valManagerConfig = valManagerConfig;
+ manifestElement = data.getManifestElement();
+ Document manifestDoc = manifestElement.getDocument();
+ NodeList appLst = manifestDoc.getElementsByTagName(ManifestConstants.APPLICATION_TAG); //$NON-NLS-1$
+
+ for (int i = 0; i < appLst.getLength(); i++)
+ {
+ Node applicationNode = appLst.item(i);
+ NamedNodeMap map = applicationNode.getAttributes();
+
+ checkIfAtributeIsMissing(
+ results,
+ applicationNode,
+ map,
+ ManifestConstants.ANDROID_ICON_ATTRIBUTE);
+
+
+ checkIfAtributeIsMissing(
+ results,
+ applicationNode,
+ map,
+ ManifestConstants.ANDROID_LABEL_ATTRIBUTE);
+
+ }
+
+ }
+
+ /**
+ * Checks if the attribute name in the XML is not existent (null) or value
+ * empty
+ *
+ * @param map
+ * list of attribute from one XML element
+ * @throws PreflightingCheckerException
+ */
+ private void checkIfAtributeIsMissing(ValidationResult results, Node manifestNode,
+ NamedNodeMap map, String attributeName) throws PreflightingCheckerException
+ {
+ Node atr = map.getNamedItem(attributeName); //$NON-NLS-1$
+ try
+ {
+ if ((atr == null) || atr.getNodeValue().trim().length() == 0)
+ {
+ int currentIssuedLine = manifestElement.getNodeLineNumber(manifestNode);
+ ValidationResultData resultData =
+ new ValidationResultData(
+ CheckerUtils.createFileToIssuesMap(manifestElement.getFile(),
+ currentIssuedLine),
+ getSeverityLevel(),
+ CheckerNLS
+ .bind(CheckerNLS.AndroidMarketFiltersChecker_missingManifestIconOrLabel_Issue,
+ attributeName),
+ CheckerNLS
+ .bind(CheckerNLS.AndroidMarketFiltersChecker_missingManifestIconOrLabel_Suggestion,
+ attributeName), getId());
+ resultData.setInfoURL(ConditionUtils.getDescriptionLink(getChecker().getId(),
+ getId(), valManagerConfig));
+ resultData.setPreview(XmlUtils.getXMLNodeAsString(manifestNode, false));
+ results.addValidationResult(resultData);
+ }
+
+ }
+ catch (DOMException e)
+ {
+ // Error retrieving attribute
+ throw new PreflightingCheckerException(CheckerNLS.bind(
+ CheckerNLS.AndroidMarketFiltersChecker_Exception_Getting_Manifest_Attribute,
+ attributeName), e);
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorolamobility.preflighting.core.checker.condition.Condition#canExecute(com.motorolamobility.preflighting.core.applicationdata.ApplicationData, java.util.List)
+ */
+ @Override
+ public CanExecuteConditionStatus canExecute(ApplicationData data,
+ List<DeviceSpecification> deviceSpecs) throws PreflightingCheckerException
+ {
+ return CheckerUtils.isAndroidManifestFileExistent(data, getId());
+ }
+
+}
diff --git a/src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/androidmarketfilters/MissingMinSdkCondition.java b/src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/androidmarketfilters/MissingMinSdkCondition.java
new file mode 100644
index 0000000..7a3e807
--- /dev/null
+++ b/src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/androidmarketfilters/MissingMinSdkCondition.java
@@ -0,0 +1,142 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorolamobility.preflighting.checkers.androidmarketfilters;
+
+import java.util.List;
+
+import org.w3c.dom.DOMException;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import com.motorolamobility.preflighting.checkers.i18n.CheckerNLS;
+import com.motorolamobility.preflighting.core.applicationdata.ApplicationData;
+import com.motorolamobility.preflighting.core.applicationdata.XMLElement;
+import com.motorolamobility.preflighting.core.checker.condition.CanExecuteConditionStatus;
+import com.motorolamobility.preflighting.core.checker.condition.Condition;
+import com.motorolamobility.preflighting.core.checker.condition.ICondition;
+import com.motorolamobility.preflighting.core.devicespecification.DeviceSpecification;
+import com.motorolamobility.preflighting.core.exception.PreflightingCheckerException;
+import com.motorolamobility.preflighting.core.internal.cond.utils.ConditionUtils;
+import com.motorolamobility.preflighting.core.utils.CheckerUtils;
+import com.motorolamobility.preflighting.core.utils.ManifestConstants;
+import com.motorolamobility.preflighting.core.utils.XmlUtils;
+import com.motorolamobility.preflighting.core.validation.ValidationManagerConfiguration;
+import com.motorolamobility.preflighting.core.validation.ValidationResult;
+import com.motorolamobility.preflighting.core.validation.ValidationResultData;
+
+public class MissingMinSdkCondition extends Condition implements ICondition
+{
+ private XMLElement manifestElement;
+
+ private ValidationManagerConfiguration valManagerConfig;
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorolamobility.preflighting.core.checker.condition.Condition#execute(com.motorolamobility.preflighting.core.applicationdata.ApplicationData, java.util.List, com.motorolamobility.preflighting.core.validation.ValidationManagerConfiguration, com.motorolamobility.preflighting.core.validation.ValidationResult)
+ */
+ @Override
+ public void execute(ApplicationData data, List<DeviceSpecification> deviceSpecs,
+ ValidationManagerConfiguration valManagerConfig, ValidationResult results)
+ throws PreflightingCheckerException
+ {
+ this.valManagerConfig = valManagerConfig;
+ manifestElement = data.getManifestElement();
+
+ NodeList usesSdkList =
+ manifestElement.getDocument().getElementsByTagName(ManifestConstants.USES_SDK_TAG);
+
+ if (usesSdkList.getLength() > 0)
+ {
+ // Check if attribute exists.
+ for (int i = 0; i < usesSdkList.getLength(); i++)
+ {
+ Node usesSdkNode = usesSdkList.item(i);
+ NamedNodeMap map = usesSdkNode.getAttributes();
+
+ checkIfAtributeIsMissing(results, usesSdkNode, map,
+ ManifestConstants.MIN_SDK_VERSION_ATTRIBUTE);
+ }
+ }
+ else
+ {
+ ValidationResultData resultData =
+ new ValidationResultData(CheckerUtils.createFileToIssuesMap(
+ manifestElement.getFile(), 0), getSeverityLevel(),
+ CheckerNLS.AndroidMarketFiltersChecker_missingMinSdkVersion_Issue,
+ CheckerNLS.AndroidMarketFiltersChecker_missingMinSdkVersion_Suggestion,
+ getId());
+ resultData.setInfoURL(ConditionUtils.getDescriptionLink(getChecker().getId(), getId(),
+ valManagerConfig));
+ resultData.setMarkerType(getMarkerType());
+ results.addValidationResult(resultData);
+ }
+
+ }
+
+ /**
+ * Checks if the attribute name in the XML is not existent (null) or value
+ * empty
+ *
+ * @param map
+ * list of attribute from one XML element
+ * @throws PreflightingCheckerException
+ */
+ private void checkIfAtributeIsMissing(ValidationResult results, Node manifestNode,
+ NamedNodeMap map, String attributeName) throws PreflightingCheckerException
+ {
+ Node atr = map.getNamedItem(attributeName);
+ try
+ {
+ if ((atr == null) || (atr.getNodeValue().trim().length() == 0))
+ {
+ int currentIssuedLine = manifestElement.getNodeLineNumber(manifestNode);
+ ValidationResultData resultData =
+ new ValidationResultData(
+ CheckerUtils.createFileToIssuesMap(manifestElement.getFile(),
+ currentIssuedLine),
+ getSeverityLevel(),
+ CheckerNLS.AndroidMarketFiltersChecker_missingMinSdkVersion_Issue,
+ CheckerNLS.AndroidMarketFiltersChecker_missingMinSdkVersion_Suggestion,
+ getId());
+ resultData.setInfoURL(ConditionUtils.getDescriptionLink(getChecker().getId(),
+ getId(), valManagerConfig));
+ resultData.setPreview(XmlUtils.getXMLNodeAsString(manifestNode, false));
+ resultData.setMarkerType(getMarkerType());
+ results.addValidationResult(resultData);
+ }
+ }
+ catch (DOMException e)
+ {
+ // Error retrieving attribute
+ throw new PreflightingCheckerException(CheckerNLS.bind(
+ CheckerNLS.AndroidMarketFiltersChecker_Exception_Getting_Manifest_Attribute,
+ attributeName), e);
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorolamobility.preflighting.core.checker.condition.Condition#canExecute(com.motorolamobility.preflighting.core.applicationdata.ApplicationData, java.util.List)
+ */
+ @Override
+ public CanExecuteConditionStatus canExecute(ApplicationData data,
+ List<DeviceSpecification> deviceSpecs) throws PreflightingCheckerException
+ {
+ return CheckerUtils.isAndroidManifestFileExistent(data, getId());
+ }
+
+}
diff --git a/src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/androidmarketfilters/MissingVersionOrNameCondition.java b/src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/androidmarketfilters/MissingVersionOrNameCondition.java
new file mode 100644
index 0000000..9d98ca0
--- /dev/null
+++ b/src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/androidmarketfilters/MissingVersionOrNameCondition.java
@@ -0,0 +1,136 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorolamobility.preflighting.checkers.androidmarketfilters;
+
+import java.util.List;
+
+import org.w3c.dom.DOMException;
+import org.w3c.dom.Document;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import com.motorolamobility.preflighting.checkers.i18n.CheckerNLS;
+import com.motorolamobility.preflighting.core.applicationdata.ApplicationData;
+import com.motorolamobility.preflighting.core.applicationdata.XMLElement;
+import com.motorolamobility.preflighting.core.checker.condition.CanExecuteConditionStatus;
+import com.motorolamobility.preflighting.core.checker.condition.Condition;
+import com.motorolamobility.preflighting.core.checker.condition.ICondition;
+import com.motorolamobility.preflighting.core.devicespecification.DeviceSpecification;
+import com.motorolamobility.preflighting.core.exception.PreflightingCheckerException;
+import com.motorolamobility.preflighting.core.internal.cond.utils.ConditionUtils;
+import com.motorolamobility.preflighting.core.utils.CheckerUtils;
+import com.motorolamobility.preflighting.core.utils.ManifestConstants;
+import com.motorolamobility.preflighting.core.utils.XmlUtils;
+import com.motorolamobility.preflighting.core.validation.ValidationManagerConfiguration;
+import com.motorolamobility.preflighting.core.validation.ValidationResult;
+import com.motorolamobility.preflighting.core.validation.ValidationResultData;
+
+public class MissingVersionOrNameCondition extends Condition implements ICondition
+{
+
+ private XMLElement manifestElement;
+
+ private ValidationManagerConfiguration valManagerConfig;
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorolamobility.preflighting.core.checker.condition.Condition#execute(com.motorolamobility.preflighting.core.applicationdata.ApplicationData, java.util.List, com.motorolamobility.preflighting.core.validation.ValidationManagerConfiguration, com.motorolamobility.preflighting.core.validation.ValidationResult)
+ */
+ @Override
+ public void execute(ApplicationData data, List<DeviceSpecification> deviceSpecs,
+ ValidationManagerConfiguration valManagerConfig, ValidationResult results)
+ throws PreflightingCheckerException
+ {
+ this.valManagerConfig = valManagerConfig;
+ manifestElement = data.getManifestElement();
+ Document manifestDoc = manifestElement.getDocument();
+ NodeList manifestLst = manifestDoc.getElementsByTagName(ManifestConstants.MANIFEST_TAG);
+ for (int i = 0; i < manifestLst.getLength(); i++)
+ {
+ Node manifestNode = manifestLst.item(i);
+ NamedNodeMap map = manifestNode.getAttributes();
+
+ checkIfAtributeIsMissing(
+ results,
+ manifestNode,
+ map,
+ ManifestConstants.ANDROID_VERSION_CODE_ATTRUIBUTE);
+
+ checkIfAtributeIsMissing(
+ results,
+ manifestNode,
+ map,
+ ManifestConstants.ANDROID_VERSION_NAME_ATTRIBUTE);
+ }
+ }
+
+ /**
+ * Checks if the attribute name in the XML is not existent (null) or value
+ * empty
+ *
+ * @param map
+ * list of attribute from one XML element
+ * @throws PreflightingCheckerException
+ */
+ private void checkIfAtributeIsMissing(ValidationResult results, Node manifestNode,
+ NamedNodeMap map, String attributeName) throws PreflightingCheckerException
+ {
+ Node atr = map.getNamedItem(attributeName); //$NON-NLS-1$
+ try
+ {
+ if ((atr == null) || atr.getNodeValue().trim().length() == 0)
+ {
+ int currentIssuedLine = manifestElement.getNodeLineNumber(manifestNode);
+ ValidationResultData resultData =
+ new ValidationResultData(
+ CheckerUtils.createFileToIssuesMap(manifestElement.getFile(),
+ currentIssuedLine),
+ getSeverityLevel(),
+ CheckerNLS.bind(
+ CheckerNLS.AndroidMarketFiltersChecker_missingVersionCodeOrName_Issue,
+ attributeName),
+ CheckerNLS
+ .bind(CheckerNLS.AndroidMarketFiltersChecker_missingVersionCodeOrName_Suggestion,
+ attributeName), getId());
+ resultData.setInfoURL(ConditionUtils.getDescriptionLink(getChecker().getId(),
+ getId(), valManagerConfig));
+ resultData.setPreview(XmlUtils.getXMLNodeAsString(manifestNode, false));
+ results.addValidationResult(resultData);
+ }
+
+ }
+ catch (DOMException e)
+ {
+ // Error retrieving attribute
+ throw new PreflightingCheckerException(CheckerNLS.bind(
+ CheckerNLS.AndroidMarketFiltersChecker_Exception_Getting_Manifest_Attribute,
+ attributeName), e);
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorolamobility.preflighting.core.checker.condition.Condition#canExecute(com.motorolamobility.preflighting.core.applicationdata.ApplicationData, java.util.List)
+ */
+ @Override
+ public CanExecuteConditionStatus canExecute(ApplicationData data,
+ List<DeviceSpecification> deviceSpecs) throws PreflightingCheckerException
+ {
+ return CheckerUtils.isAndroidManifestFileExistent(data, getId());
+ }
+
+}
diff --git a/src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/androidmarketfilters/PermissionImpliedFeaturesCondition.java b/src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/androidmarketfilters/PermissionImpliedFeaturesCondition.java
new file mode 100644
index 0000000..0570bc8
--- /dev/null
+++ b/src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/androidmarketfilters/PermissionImpliedFeaturesCondition.java
@@ -0,0 +1,159 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorolamobility.preflighting.checkers.androidmarketfilters;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.w3c.dom.DOMException;
+import org.w3c.dom.Document;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import com.motorolamobility.preflighting.checkers.i18n.CheckerNLS;
+import com.motorolamobility.preflighting.core.applicationdata.ApplicationData;
+import com.motorolamobility.preflighting.core.applicationdata.XMLElement;
+import com.motorolamobility.preflighting.core.checker.condition.CanExecuteConditionStatus;
+import com.motorolamobility.preflighting.core.checker.condition.Condition;
+import com.motorolamobility.preflighting.core.checker.condition.ICondition;
+import com.motorolamobility.preflighting.core.devicespecification.DeviceSpecification;
+import com.motorolamobility.preflighting.core.devicespecification.internal.PlatformRules;
+import com.motorolamobility.preflighting.core.exception.PreflightingCheckerException;
+import com.motorolamobility.preflighting.core.internal.cond.utils.ConditionUtils;
+import com.motorolamobility.preflighting.core.permissionfeature.Feature;
+import com.motorolamobility.preflighting.core.utils.CheckerUtils;
+import com.motorolamobility.preflighting.core.utils.ManifestConstants;
+import com.motorolamobility.preflighting.core.utils.XmlUtils;
+import com.motorolamobility.preflighting.core.validation.ValidationManagerConfiguration;
+import com.motorolamobility.preflighting.core.validation.ValidationResult;
+import com.motorolamobility.preflighting.core.validation.ValidationResultData;
+
+public class PermissionImpliedFeaturesCondition extends Condition implements ICondition
+{
+
+ /**
+ * Checks each uses-permission declared into XML if there are all the implied uses-feature declared
+ * @param platformRules rules to get permission to feature mapping
+ * @param result
+ * @param manifestDoc
+ * @throws PreflightingCheckerException
+ */
+ @Override
+ public void execute(ApplicationData data, List<DeviceSpecification> deviceSpecs,
+ ValidationManagerConfiguration valManagerConfig, ValidationResult results)
+ throws PreflightingCheckerException
+ {
+ XMLElement manifestElement = data.getManifestElement();
+ Document manifestDoc = manifestElement.getDocument();
+ PlatformRules platformRules = PlatformRules.getInstance();
+
+ //collecting set of declared features
+ Set<Feature> featuresDeclared = new HashSet<Feature>();
+ NodeList featureLst = manifestDoc.getElementsByTagName(ManifestConstants.USES_FEATURE_TAG);
+ for (int j = 0; j < featureLst.getLength(); j++)
+ {
+ Node featureNode = featureLst.item(j);
+ NamedNodeMap featureMap = featureNode.getAttributes();
+ Node featureAtr = featureMap.getNamedItem(ManifestConstants.ANDROID_NAME_ATTRIBUTE); //$NON-NLS-1$
+ if ((featureAtr != null) && featureAtr.getNodeValue().trim().length() != 0)
+ {
+ Feature featDecl = new Feature(featureAtr.getNodeValue());
+ featuresDeclared.add(featDecl);
+ }
+ }
+
+ //comparing with features required
+ NodeList permissionsLst =
+ manifestDoc.getElementsByTagName(ManifestConstants.USES_PERMISSION_ATTRIBUTE);
+ for (int i = 0; i < permissionsLst.getLength(); i++)
+ {
+ Node permissionNode = permissionsLst.item(i);
+ NamedNodeMap permissionMap = permissionNode.getAttributes();
+ Node permissionAtr =
+ permissionMap.getNamedItem(ManifestConstants.ANDROID_NAME_ATTRIBUTE); //$NON-NLS-1$
+ try
+ {
+ if ((permissionAtr != null) && !permissionAtr.getNodeValue().trim().equals("")) //$NON-NLS-1$
+ {
+ String permissionId = permissionAtr.getNodeValue();
+ Set<Feature> featuresRequired =
+ platformRules.getImpliedFeaturesSet(permissionId);
+ //check if these required features were declared using <uses-feature>
+ if ((featuresRequired != null)
+ && !featuresDeclared.containsAll(featuresRequired))
+ {
+ //there are missing features required
+ int currentIssuedLine = manifestElement.getNodeLineNumber(permissionNode);
+ //missing set = features required - features declared
+ Set<Feature> missingDeclaredFeatures =
+ new HashSet<Feature>(featuresRequired);
+ missingDeclaredFeatures.removeAll(featuresDeclared);
+ String issueDescription =
+ CheckerNLS
+ .bind(CheckerNLS.AndroidMarketFiltersChecker_permissionToImpliedFeatures_Issue,
+ new Object[]
+ {
+ missingDeclaredFeatures, permissionId
+ });
+ ValidationResultData resultData =
+ new ValidationResultData(
+ CheckerUtils.createFileToIssuesMap(
+ manifestElement.getFile(), currentIssuedLine),
+ getSeverityLevel(),
+ issueDescription,
+ CheckerNLS.AndroidMarketFiltersChecker_permissionToImpliedFeatures_Suggestion,
+ getId());
+ resultData.setInfoURL(ConditionUtils.getDescriptionLink(getChecker()
+ .getId(), getId(), valManagerConfig));
+ resultData.setPreview(XmlUtils.getXMLNodeAsString(permissionNode, false));
+
+ resultData.setMarkerType(getMarkerType());
+ List<String> missingFeaturesIds = new ArrayList<String>(missingDeclaredFeatures.size());
+ for(Feature missingFeature : missingDeclaredFeatures)
+ {
+ missingFeaturesIds.add(missingFeature.toString());
+ }
+ resultData.appendExtra(missingFeaturesIds);
+ results.addValidationResult(resultData);
+ }
+ }
+ }
+ catch (DOMException e)
+ {
+ // Error retrieving attribute
+ throw new PreflightingCheckerException(
+ CheckerNLS.bind(
+ CheckerNLS.AndroidMarketFiltersChecker_Exception_Getting_Manifest_Attribute,
+ ManifestConstants.ANDROID_NAME_ATTRIBUTE), e);
+ }
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorolamobility.preflighting.core.checker.condition.Condition#canExecute(com.motorolamobility.preflighting.core.applicationdata.ApplicationData, java.util.List)
+ */
+ @Override
+ public CanExecuteConditionStatus canExecute(ApplicationData data,
+ List<DeviceSpecification> deviceSpecs) throws PreflightingCheckerException
+ {
+ return CheckerUtils.isAndroidManifestFileExistent(data, getId());
+ }
+
+}
diff --git a/src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/androidmarketfilters/PreviewTargetSdkCondition.java b/src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/androidmarketfilters/PreviewTargetSdkCondition.java
new file mode 100644
index 0000000..2b980b1
--- /dev/null
+++ b/src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/androidmarketfilters/PreviewTargetSdkCondition.java
@@ -0,0 +1,104 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorolamobility.preflighting.checkers.androidmarketfilters;
+
+import java.util.List;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import com.motorolamobility.preflighting.checkers.i18n.CheckerNLS;
+import com.motorolamobility.preflighting.core.applicationdata.ApplicationData;
+import com.motorolamobility.preflighting.core.applicationdata.XMLElement;
+import com.motorolamobility.preflighting.core.checker.condition.CanExecuteConditionStatus;
+import com.motorolamobility.preflighting.core.checker.condition.Condition;
+import com.motorolamobility.preflighting.core.checker.condition.ICondition;
+import com.motorolamobility.preflighting.core.devicespecification.DeviceSpecification;
+import com.motorolamobility.preflighting.core.exception.PreflightingCheckerException;
+import com.motorolamobility.preflighting.core.internal.cond.utils.ConditionUtils;
+import com.motorolamobility.preflighting.core.utils.CheckerUtils;
+import com.motorolamobility.preflighting.core.utils.ManifestConstants;
+import com.motorolamobility.preflighting.core.utils.XmlUtils;
+import com.motorolamobility.preflighting.core.validation.ValidationManagerConfiguration;
+import com.motorolamobility.preflighting.core.validation.ValidationResult;
+import com.motorolamobility.preflighting.core.validation.ValidationResultData;
+
+public class PreviewTargetSdkCondition extends Condition implements ICondition
+{
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorolamobility.preflighting.core.checker.condition.Condition#execute(com.motorolamobility.preflighting.core.applicationdata.ApplicationData, java.util.List, com.motorolamobility.preflighting.core.validation.ValidationManagerConfiguration, com.motorolamobility.preflighting.core.validation.ValidationResult)
+ */
+ @Override
+ public void execute(ApplicationData data, List<DeviceSpecification> deviceSpecs,
+ ValidationManagerConfiguration valManagerConfig, ValidationResult results)
+ throws PreflightingCheckerException
+ {
+ XMLElement manifestElement = data.getManifestElement();
+ Document manifestDoc = manifestElement.getDocument();
+ NodeList usesSdkList = manifestDoc.getElementsByTagName(ManifestConstants.USES_SDK_TAG);
+ if (usesSdkList.getLength() > 0)
+ {
+ // Check if attribute exists.
+ for (int i = 0; i < usesSdkList.getLength(); i++)
+ {
+ Node usesSdkNode = usesSdkList.item(i);
+ NamedNodeMap map = usesSdkNode.getAttributes();
+ Node targetSdkNode =
+ map.getNamedItem(ManifestConstants.TARGET_SDK_VERSION_ATTRIBUTE); //$NON-NLS-1$
+ try
+ {
+ if (targetSdkNode != null)
+ {
+ String targetSdkStr = targetSdkNode.getNodeValue().trim();
+ Integer.valueOf(targetSdkStr); //Try to get the int value. It the exception is thrown issue the warning.
+ }
+ }
+ catch (NumberFormatException e)
+ {
+ int currentIssuedLine = manifestElement.getNodeLineNumber(usesSdkNode);
+ ValidationResultData resultData =
+ new ValidationResultData(
+ CheckerUtils.createFileToIssuesMap(manifestElement.getFile(),
+ currentIssuedLine),
+ getSeverityLevel(),
+ CheckerNLS.AndroidMarketFiltersChecker_targetSdkIsPreview_Issue,
+ CheckerNLS.AndroidMarketFiltersChecker_targetSdkIsPreview_Suggestion,
+ getId());
+ resultData.setInfoURL(ConditionUtils.getDescriptionLink(getChecker().getId(),
+ getId(), valManagerConfig));
+ resultData.setPreview(XmlUtils.getXMLNodeAsString(usesSdkNode, false));
+ results.addValidationResult(resultData);
+ }
+ }
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorolamobility.preflighting.core.checker.condition.Condition#canExecute(com.motorolamobility.preflighting.core.applicationdata.ApplicationData, java.util.List)
+ */
+ @Override
+ public CanExecuteConditionStatus canExecute(ApplicationData data,
+ List<DeviceSpecification> deviceSpecs) throws PreflightingCheckerException
+ {
+ return CheckerUtils.isAndroidManifestFileExistent(data, getId());
+ }
+
+}
diff --git a/src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/androidmarketfilters/ZipalignedCondition.java b/src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/androidmarketfilters/ZipalignedCondition.java
new file mode 100644
index 0000000..4517f95
--- /dev/null
+++ b/src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/androidmarketfilters/ZipalignedCondition.java
@@ -0,0 +1,242 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorolamobility.preflighting.checkers.androidmarketfilters;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.List;
+
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.osgi.util.NLS;
+
+import com.motorolamobility.preflighting.checkers.CheckerPlugin;
+import com.motorolamobility.preflighting.checkers.i18n.CheckerNLS;
+import com.motorolamobility.preflighting.core.applicationdata.ApplicationData;
+import com.motorolamobility.preflighting.core.checker.Checker;
+import com.motorolamobility.preflighting.core.checker.condition.CanExecuteConditionStatus;
+import com.motorolamobility.preflighting.core.checker.condition.Condition;
+import com.motorolamobility.preflighting.core.checker.condition.ICondition;
+import com.motorolamobility.preflighting.core.devicespecification.DeviceSpecification;
+import com.motorolamobility.preflighting.core.exception.PreflightingCheckerException;
+import com.motorolamobility.preflighting.core.internal.cond.utils.ConditionUtils;
+import com.motorolamobility.preflighting.core.internal.utils.ProjectUtils;
+import com.motorolamobility.preflighting.core.logging.PreflightingLogger;
+import com.motorolamobility.preflighting.core.utils.CheckerUtils;
+import com.motorolamobility.preflighting.core.validation.Parameter;
+import com.motorolamobility.preflighting.core.validation.ValidationManager;
+import com.motorolamobility.preflighting.core.validation.ValidationManagerConfiguration;
+import com.motorolamobility.preflighting.core.validation.ValidationResult;
+import com.motorolamobility.preflighting.core.validation.ValidationResultData;
+import com.motorolamobility.preflighting.core.verbose.DebugVerboseOutputter;
+import com.motorolamobility.preflighting.core.verbose.DebugVerboseOutputter.VerboseLevel;
+
+public class ZipalignedCondition extends Condition implements ICondition
+{
+
+ private final String ZIPALIGN_FAILED_STRING = "Verification FAILED";
+
+ private final String ZIPALIGN_SUCCESS_STRING = "Verification succesful";
+
+ private final String OPTIONS_FOR_ZIPALIGN = "-c -v 4";
+
+ private final String ZIPALIGN_EXEC = Platform.getOS().equals(Platform.OS_WIN32)
+ ? "zipalign.exe" : "zipalign"; //$NON-NLS-1$ //$NON-NLS-2$
+
+ private static String TOOLS = "tools";
+
+ private String zipalignPath;
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorolamobility.preflighting.core.checker.condition.Condition#execute(com.motorolamobility.preflighting.core.applicationdata.ApplicationData, java.util.List, com.motorolamobility.preflighting.core.validation.ValidationManagerConfiguration, com.motorolamobility.preflighting.core.validation.ValidationResult)
+ */
+ @Override
+ public void execute(ApplicationData data, List<DeviceSpecification> deviceSpecs,
+ ValidationManagerConfiguration valManagerConfig, ValidationResult results)
+ throws PreflightingCheckerException
+ {
+ if (!data.isProject() && (zipalignPath != null))
+ {
+
+ String applicationPath = data.getApplicationPath();
+ String[] zipalignCmdLine = new String[OPTIONS_FOR_ZIPALIGN.split(" ").length + 2];
+ String[] splitOptions = OPTIONS_FOR_ZIPALIGN.split(" ");
+ zipalignCmdLine[0] = zipalignPath;
+ zipalignCmdLine[OPTIONS_FOR_ZIPALIGN.split(" ").length + 1] = applicationPath;
+
+ for (int i = 1; i <= splitOptions.length; i++)
+ {
+ zipalignCmdLine[i] = splitOptions[i - 1].trim();
+ }
+
+ BufferedReader reader = null;
+ InputStreamReader inputStreamReader = null;
+ try
+ {
+
+ Process p = Runtime.getRuntime().exec(zipalignCmdLine);
+ inputStreamReader = new InputStreamReader(p.getInputStream());
+ reader = new BufferedReader(inputStreamReader);
+
+ String line = null;
+ Boolean zipaligned = null;
+
+ //look for the phrase "Verification FAILED" in the zipalign output.
+ while ((line = reader.readLine()) != null)
+ {
+ if (line.contains(ZIPALIGN_FAILED_STRING))
+ {
+ zipaligned = false;
+ break;
+ }
+ else if (line.contains(ZIPALIGN_SUCCESS_STRING))
+ {
+ zipaligned = true;
+ break;
+ }
+ }
+
+ if (zipaligned == null)
+ {
+ PreflightingLogger.error(CertificateExpiredCondition.class,
+ "It was not possible to identify if file is zipaligned"); //$NON-NLS-1$
+ }
+ else
+ {
+ if (!zipaligned)
+ {
+ ValidationResultData resultData =
+ new ValidationResultData(
+ CheckerUtils.createFileToIssuesMap(null, 0),
+ getSeverityLevel(),
+ NLS.bind(
+ CheckerNLS.AndroidMarketFiltersChecker_zipaligned_Issue,
+ applicationPath),
+ CheckerNLS.AndroidMarketFiltersChecker_zipaligned_Suggestion,
+ getId());
+ resultData.setInfoURL(ConditionUtils.getDescriptionLink(getChecker()
+ .getId(), getId(), valManagerConfig));
+ results.addValidationResult(resultData);
+ }
+ }
+ }
+ catch (IOException e)
+ {
+ PreflightingLogger.error(CertificateExpiredCondition.class,
+ "It was not possible to execute/read zipalign command"); //$NON-NLS-1$
+ }
+ finally
+ {
+ if (reader != null)
+ {
+ try
+ {
+ reader.close();
+ }
+ catch (IOException e)
+ {
+ //Do Nothing.
+ }
+ }
+ if (inputStreamReader != null)
+ {
+ try
+ {
+ inputStreamReader.close();
+ }
+ catch (IOException e)
+ {
+ //Do Nothing.
+ }
+ }
+ }
+
+ }
+ else
+ {
+ if (data.isProject())
+ {
+ DebugVerboseOutputter
+ .printVerboseMessage(
+ CheckerNLS
+ .bind(CheckerNLS.AndroidMarketFiltersChecker_zipaligned_ConditionNotExecutedForProject,
+ getId()), VerboseLevel.v2);
+ }
+ }
+
+ }
+
+ private String getZipalignPath(String sdk)
+ {
+
+ String zipAlignPath = null;
+ File sdkFolder = new File(sdk);
+
+ if (sdkFolder.isDirectory())
+ {
+ File toolsFolder = new File(sdkFolder, TOOLS);
+
+ if (toolsFolder.exists())
+ {
+ File zipalignToolFile = new File(toolsFolder, ZIPALIGN_EXEC);
+ if (zipalignToolFile.exists())
+ {
+ zipAlignPath = zipalignToolFile.getAbsolutePath();
+ }
+ }
+ }
+
+ return zipAlignPath;
+ }
+
+ /**
+ * This checker can be executed if the zipalign is available in the SDK
+ */
+ @Override
+ public CanExecuteConditionStatus canExecute(ApplicationData data,
+ List<DeviceSpecification> deviceSpecs) throws PreflightingCheckerException
+ {
+ CanExecuteConditionStatus status = null;
+ String sdkPath = null;
+
+ for (Parameter parameter : ((Checker) getChecker()).getGlobalParams())
+ {
+ if (parameter.getParameterType().equals(
+ ValidationManager.InputParameter.SDK_PATH.getAlias()))
+ {
+ sdkPath = ProjectUtils.getSdkPath(parameter);
+ }
+ }
+
+ if (sdkPath != null)
+ {
+ zipalignPath = getZipalignPath(sdkPath);
+ status =
+ new CanExecuteConditionStatus(IStatus.OK, CheckerPlugin.PLUGIN_ID, "", getId());
+ }
+ else
+ {
+ status =
+ new CanExecuteConditionStatus(IStatus.ERROR, CheckerPlugin.PLUGIN_ID,
+ "Sdk path could not be retrieved from global paremeters", getId());
+ }
+ return status;
+ }
+
+}
diff --git a/src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/buildingblocksdeclaration/BuildingBlocksInheritanceCondition.java b/src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/buildingblocksdeclaration/BuildingBlocksInheritanceCondition.java
new file mode 100644
index 0000000..d7a45a6
--- /dev/null
+++ b/src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/buildingblocksdeclaration/BuildingBlocksInheritanceCondition.java
@@ -0,0 +1,405 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorolamobility.preflighting.checkers.buildingblocksdeclaration;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import com.motorolamobility.preflighting.checkers.i18n.CheckerNLS;
+import com.motorolamobility.preflighting.core.applicationdata.ApplicationData;
+import com.motorolamobility.preflighting.core.applicationdata.SourceFolderElement;
+import com.motorolamobility.preflighting.core.applicationdata.XMLElement;
+import com.motorolamobility.preflighting.core.checker.condition.CanExecuteConditionStatus;
+import com.motorolamobility.preflighting.core.checker.condition.Condition;
+import com.motorolamobility.preflighting.core.checker.condition.ICondition;
+import com.motorolamobility.preflighting.core.devicespecification.DeviceSpecification;
+import com.motorolamobility.preflighting.core.exception.PreflightingCheckerException;
+import com.motorolamobility.preflighting.core.internal.cond.utils.ConditionUtils;
+import com.motorolamobility.preflighting.core.source.model.SourceFileElement;
+import com.motorolamobility.preflighting.core.utils.CheckerUtils;
+import com.motorolamobility.preflighting.core.validation.ValidationManagerConfiguration;
+import com.motorolamobility.preflighting.core.validation.ValidationResult;
+import com.motorolamobility.preflighting.core.validation.ValidationResultData;
+
+/**
+ * This condition verifies whether a building block inherits
+ * from the class it is supposed to.
+ *
+ */
+public class BuildingBlocksInheritanceCondition extends Condition implements ICondition
+{
+ /**
+ * Enumerator holds all building block types.
+ */
+ private enum BuildingBlockType
+ {
+ ACTIVITY, SERVICE, CONTENT_PROVIDER, BROADCAST_RECEIVER
+ }
+
+ private static final String ANDROID_NAME_NODE_ATTRIBUTE = "android:name"; //$NON-NLS-1$
+
+ private static final String ACTIVITY_NODE_NAME = "activity"; //$NON-NLS-1$
+
+ private static final String PROVIDER_NODE_NAME = "provider"; //$NON-NLS-1$
+
+ private static final String RECEIVER_NODE_NAME = "receiver"; //$NON-NLS-1$
+
+ private static final String SERVICE_NODE_NAME = "service"; //$NON-NLS-1$
+
+ private static final String MANIFEST_NODE_NAME = "manifest"; //$NON-NLS-1$
+
+ private static final String PACKAGE_NAME_NODE_ATTRIBUTE = "package"; //$NON-NLS-1$
+
+ private static final String ACTIVITY_CLASS_NAME = "android.app.Activity"; //$NON-NLS-1$
+
+ private static final String CONTENT_PROVIDER_CLASS_NAME = "android.content.ContentProvider"; //$NON-NLS-1$
+
+ private static final String BROADCAST_RECEIVER_CLASS_NAME = "android.content.BroadcastReceiver"; //$NON-NLS-1$
+
+ private static final String SERVICE_CLASS_NAME = "android.app.Service"; //$NON-NLS-1$
+
+ private static String DEFAULT_PACKAGE_REPRESENTATION_REGULAR_EXPRESSION = "\\."; //$NON-NLS-1$
+
+ private static String DEFAULT_PACKAGE_REPRESENTATION = "."; //$NON-NLS-1$
+
+ private static char PACKAGE_SEPARATOR = '.'; //$NON-NLS-1$
+
+ private static char PATH_SEPARATOR = '/'; //$NON-NLS-1$
+
+ private static String COLUMN_STRING = ";"; //$NON-NLS-1$
+
+ private static int ZERO_INDEX = 0;
+
+ private static int FIRST_INDEX = 1;
+
+ private static String[] KNOWN_ACITIVITES_IMPLEMENTATIONS = new String[]
+ {
+ "android.app.Activity", "android.accounts.AccountAuthenticatorActivity",
+ "android.app.ActivityGroup", "android.app.AliasActivity",
+ "android.app.ExpandableListActivity", "android.app.ListActivity",
+ "android.app.NativeActivity", "android.app.LauncherActivity",
+ "android.preference.PreferenceActivity", "android.app.TabActivity"
+ };
+
+ private static String[] KNOWN_CONTENT_PROVIDERS_IMPLEMENTATIONS = new String[]
+ {
+ "android.content.ContentProvider", "android.test.mock.MockContentProvider",
+ "android.content.SearchRecentSuggestionsProvider"
+ };
+
+ private static String[] KNOWN_BROADCAST_RECEIVERS_IMPLEMENTATIONS = new String[]
+ {
+ "android.content.BroadcastReceiver", "android.appwidget.AppWidgetProvider",
+ "android.app.admin.DeviceAdminReceiver"
+ };
+
+ private static String[] KNOWN_SERVICES_IMPLEMENTATIONS = new String[]
+ {
+ "android.app.Service", "android.inputmethodservice.AbstractInputMethodService",
+ "android.accessibilityservice.AccessibilityService", "android.app.IntentService",
+ "android.speech.RecognitionService", "android.widget.RemoteViewsService",
+ "android.service.wallpaper.WallpaperService",
+ "android.inputmethodservice.InputMethodService"
+ };
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorolamobility.preflighting.core.checker.condition.Condition#canExecute(com.motorolamobility.preflighting.core.applicationdata.ApplicationData, java.util.List)
+ */
+ @Override
+ public CanExecuteConditionStatus canExecute(ApplicationData data,
+ List<DeviceSpecification> deviceSpecs) throws PreflightingCheckerException
+ {
+ return (CanExecuteConditionStatus) CheckerUtils
+ .isAndroidManifestFileExistent(data, getId());
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorolamobility.preflighting.core.checker.condition.Condition#execute(com.motorolamobility.preflighting.core.applicationdata.ApplicationData, java.util.List, com.motorolamobility.preflighting.core.devicespecification.PlatformRules, com.motorolamobility.preflighting.core.validation.ValidationManagerConfiguration)
+ */
+ @Override
+ public void execute(ApplicationData data, List<DeviceSpecification> deviceSpecs,
+ ValidationManagerConfiguration valManagerConfig, ValidationResult results)
+ throws PreflightingCheckerException
+ {
+ XMLElement document = data.getManifestElement();
+ Document manifestDoc = document.getDocument();
+
+ List<SourceFolderElement> folders = data.getJavaModel();
+
+ // get all missdeclared classes from all building blocks
+ List<String> missDeclaredActivities =
+ getMissDeclaredBuildingBlocks(manifestDoc, folders, BuildingBlockType.ACTIVITY);
+
+ List<String> missDeclaredContentProviders =
+ getMissDeclaredBuildingBlocks(manifestDoc, folders,
+ BuildingBlockType.CONTENT_PROVIDER);
+
+ List<String> missDeclaredBroadcastReceivers =
+ getMissDeclaredBuildingBlocks(manifestDoc, folders,
+ BuildingBlockType.BROADCAST_RECEIVER);
+
+ List<String> missDelcaredServices =
+ getMissDeclaredBuildingBlocks(manifestDoc, folders, BuildingBlockType.SERVICE);
+
+ // having all missdeclared building blocks, add their appropriated error messages
+ addValidationResults(results, missDeclaredActivities, BuildingBlockType.ACTIVITY,
+ valManagerConfig);
+
+ addValidationResults(results, missDeclaredBroadcastReceivers,
+ BuildingBlockType.BROADCAST_RECEIVER, valManagerConfig);
+
+ addValidationResults(results, missDeclaredContentProviders,
+ BuildingBlockType.CONTENT_PROVIDER, valManagerConfig);
+
+ addValidationResults(results, missDelcaredServices, BuildingBlockType.SERVICE,
+ valManagerConfig);
+ }
+
+ /**
+ * Get the default package name from the AndroidManifest.xml file.
+ *
+ * @param manifestDoc {@link Document} representing the AndroidManifest.xml file.
+ *
+ * @return Returns the default package name.
+ */
+ private String getDefaultPackageName(Document manifestDoc)
+ {
+ NodeList nodeList = manifestDoc.getElementsByTagName(MANIFEST_NODE_NAME);
+ return nodeList.item(ZERO_INDEX).getAttributes().getNamedItem(PACKAGE_NAME_NODE_ATTRIBUTE)
+ .getNodeValue();
+ }
+
+ /**
+ * Add validation results to the results list.
+ *
+ * @param results {@link ValidationResult} list where
+ * the {@link ValidationResultData} is added.
+ * @param missDelcaredBuildingBlocks The list of missdeclared building blocks
+ * to be managed.
+ * @param buildingBlockType {@link BuildingBlockType}.
+ * @param valManagerConfig Validation manager result
+ */
+ private void addValidationResults(ValidationResult results,
+ List<String> missDelcaredBuildingBlocks, BuildingBlockType buildingBlockType,
+ ValidationManagerConfiguration valManagerConfig)
+ {
+ String buildingBlockClassName = null;
+ String buildingBockText = null;
+
+ switch (buildingBlockType)
+ {
+ case ACTIVITY:
+ buildingBlockClassName = ACTIVITY_CLASS_NAME;
+ buildingBockText = CheckerNLS.BuildingBlocksInheritanceCondition_Activity;
+ break;
+ case BROADCAST_RECEIVER:
+ buildingBlockClassName = BROADCAST_RECEIVER_CLASS_NAME;
+ buildingBockText = CheckerNLS.BuildingBlocksInheritanceCondition_BroadcastReceiver;
+ break;
+ case CONTENT_PROVIDER:
+ buildingBlockClassName = CONTENT_PROVIDER_CLASS_NAME;
+ buildingBockText = CheckerNLS.BuildingBlocksInheritanceCondition_ContentProvider;
+ break;
+ case SERVICE:
+ buildingBlockClassName = SERVICE_CLASS_NAME;
+ buildingBockText = CheckerNLS.BuildingBlocksInheritanceCondition_Service;
+ break;
+ }
+
+ if ((missDelcaredBuildingBlocks != null) && (missDelcaredBuildingBlocks.size() > 0))
+ {
+ ValidationResultData validationResult = null;
+ for (String missDelcaredBuildingBlock : missDelcaredBuildingBlocks)
+ {
+ validationResult = new ValidationResultData();
+ validationResult.setConditionID(getId());
+ validationResult
+ .setIssueDescription(CheckerNLS
+ .bind(CheckerNLS.BuildingBlockDeclarationCondition_TheClassXShouldExtendBuildingBlockY,
+ new String[]
+ {
+ missDelcaredBuildingBlock, buildingBockText
+ }));
+ validationResult
+ .setQuickFixSuggestion(CheckerNLS
+ .bind(CheckerNLS.BuildingBlocksInheritanceCondition_TheBuildingBlockXShouldExtendClassY,
+ new String[]
+ {
+ missDelcaredBuildingBlock, buildingBlockClassName
+ }));
+ validationResult.setInfoURL(ConditionUtils.getDescriptionLink(getChecker().getId(),
+ getId(), valManagerConfig));
+ validationResult.setSeverity(getSeverityLevel());
+ results.addValidationResult(validationResult);
+ }
+ }
+ }
+
+ /**
+ * Retrieve the list of missdeclared building blocks of
+ * a certain {@link BuildingBlockType}.
+ *
+ * @param manifestDoc AndroidManifest.xml {@link Document} representation.
+ * @param folders The {@link SourceFolderElement} list which represents
+ * the Android Project.
+ * @param buildingBlockType The building block type to be analyzed.
+ *
+ * @return Returns the list of missdeclared building blocks for a certain
+ * {@link BuildingBlockType}.
+ */
+ private List<String> getMissDeclaredBuildingBlocks(Document manifestDoc,
+ List<SourceFolderElement> folders, BuildingBlockType buildingBlockType)
+ {
+ String selectedNodeName = null;
+ String[] selectedBuildingBlockClassNameList = null;
+
+ // determined the nodes and classes to search accoding to the building block
+ switch (buildingBlockType)
+ {
+ case ACTIVITY:
+ selectedNodeName = ACTIVITY_NODE_NAME;
+ selectedBuildingBlockClassNameList = KNOWN_ACITIVITES_IMPLEMENTATIONS;
+ break;
+ case CONTENT_PROVIDER:
+ selectedNodeName = PROVIDER_NODE_NAME;
+ selectedBuildingBlockClassNameList = KNOWN_CONTENT_PROVIDERS_IMPLEMENTATIONS;
+ break;
+ case BROADCAST_RECEIVER:
+ selectedNodeName = RECEIVER_NODE_NAME;
+ selectedBuildingBlockClassNameList = KNOWN_BROADCAST_RECEIVERS_IMPLEMENTATIONS;
+ break;
+ case SERVICE:
+ selectedNodeName = SERVICE_NODE_NAME;
+ selectedBuildingBlockClassNameList = KNOWN_SERVICES_IMPLEMENTATIONS;
+ break;
+
+ }
+
+ List<String> missDeclaredBuldingBlocks = new ArrayList<String>();
+ Node buildingBlockNode = null;
+ Node nodeName = null;
+ String buildingBlockName = null;
+ String sourceFileName = null;
+ String superClassName = null;
+ List<SourceFileElement> files = null;
+
+ // get all building block nodes
+ NodeList applicationNodes = manifestDoc.getElementsByTagName(selectedNodeName);
+ // For each building block node, check whether its superclass is
+ // supposed parent
+ if ((applicationNodes != null) && (applicationNodes.getLength() > 0))
+ {
+ for (int index = 0; index < applicationNodes.getLength(); index++)
+ {
+ // get the building block name
+ buildingBlockNode = applicationNodes.item(index);
+ nodeName =
+ buildingBlockNode.getAttributes().getNamedItem(ANDROID_NAME_NODE_ATTRIBUTE);
+ if (nodeName != null)
+ {
+ buildingBlockName = nodeName.getNodeValue();
+ // get the full package name of the building block
+ buildingBlockName = adjustFullPackageName(buildingBlockName, manifestDoc);
+ // Find the equivalent source of the building block and check
+ // its superclass.
+ for (SourceFolderElement folder : folders)
+ {
+ files = folder.getSourceFileElements();
+ if ((files != null) && (files.size() > 0))
+ {
+ for (SourceFileElement file : files)
+ {
+ // for executing the checker, for now it cannot be an inner class
+ // TODO : this verification can be removed after the correct class name is retrieved
+ if (!file.isInnerClass())
+ {
+ sourceFileName = file.getClassFullPath();
+ sourceFileName = getFullPackagePathFromFullPath(sourceFileName);
+ if ((sourceFileName != null)
+ && sourceFileName.equals(buildingBlockName))
+ {
+ superClassName = file.getSuperclassName();
+ superClassName =
+ getFullPackagePathFromFullPath(superClassName);
+ if (!Arrays.asList(selectedBuildingBlockClassNameList)
+ .contains(superClassName))
+ {
+ // The superclass is not the expected one!
+ missDeclaredBuldingBlocks.add(sourceFileName);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ // retrieve the list of miss declared building blocks
+ return missDeclaredBuldingBlocks;
+ }
+
+ /**
+ * Given the full path to a Java file, its full packaged-name representation
+ * is returned.
+ *
+ * @param fullPath The full path of a Java file.
+ *
+ * @return Returns the full package-name of a Java file.
+ */
+ public String getFullPackagePathFromFullPath(String fullPath)
+ {
+ String fullPackageAndClassName = null;
+ fullPackageAndClassName = fullPath.replace(PATH_SEPARATOR, PACKAGE_SEPARATOR);
+ if (fullPackageAndClassName.endsWith(COLUMN_STRING))
+ {
+ fullPackageAndClassName = fullPackageAndClassName.split(COLUMN_STRING)[ZERO_INDEX];
+ }
+
+ return fullPackageAndClassName;
+ }
+
+ /**
+ * Set the full package name for the building block.
+ *
+ * @param buildingBlockName Building block name.
+ * @param manifestDoc AndroidManifest.xml {@link Document}.
+ *
+ * @return Returns the building block with its full package name.
+ */
+ private String adjustFullPackageName(String buildingBlockName, Document manifestDoc)
+ {
+ String defaultPackageName = getDefaultPackageName(manifestDoc);
+ if (!buildingBlockName.contains(defaultPackageName))
+ {
+ if (buildingBlockName.contains(DEFAULT_PACKAGE_REPRESENTATION))
+ {
+ buildingBlockName =
+ buildingBlockName.split(DEFAULT_PACKAGE_REPRESENTATION_REGULAR_EXPRESSION)[FIRST_INDEX];
+ }
+ buildingBlockName = defaultPackageName + PACKAGE_SEPARATOR + buildingBlockName;
+ }
+
+ return buildingBlockName;
+ }
+}
diff --git a/src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/deviceCompatibility/SmallScreensSupportCondition.java b/src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/deviceCompatibility/SmallScreensSupportCondition.java
new file mode 100644
index 0000000..2d1619f
--- /dev/null
+++ b/src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/deviceCompatibility/SmallScreensSupportCondition.java
@@ -0,0 +1,252 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorolamobility.preflighting.checkers.deviceCompatibility;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.core.runtime.IStatus;
+import org.w3c.dom.DOMException;
+import org.w3c.dom.Document;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import com.motorolamobility.preflighting.checkers.CheckerPlugin;
+import com.motorolamobility.preflighting.checkers.i18n.CheckerNLS;
+import com.motorolamobility.preflighting.core.applicationdata.ApplicationData;
+import com.motorolamobility.preflighting.core.applicationdata.XMLElement;
+import com.motorolamobility.preflighting.core.checker.condition.CanExecuteConditionStatus;
+import com.motorolamobility.preflighting.core.checker.condition.Condition;
+import com.motorolamobility.preflighting.core.checker.condition.ICondition;
+import com.motorolamobility.preflighting.core.devicelayoutspecification.ParametersType;
+import com.motorolamobility.preflighting.core.devicespecification.DeviceSpecification;
+import com.motorolamobility.preflighting.core.exception.PreflightingCheckerException;
+import com.motorolamobility.preflighting.core.internal.cond.utils.ConditionUtils;
+import com.motorolamobility.preflighting.core.utils.ManifestConstants;
+import com.motorolamobility.preflighting.core.utils.XmlUtils;
+import com.motorolamobility.preflighting.core.validation.ValidationManagerConfiguration;
+import com.motorolamobility.preflighting.core.validation.ValidationResult;
+import com.motorolamobility.preflighting.core.validation.ValidationResultData;
+
+/**
+ * Check if device has small screen but AndroidManifest set smallScreens=false.
+ */
+public class SmallScreensSupportCondition extends Condition implements ICondition
+{
+
+ /*
+ * AndroidManifest.xml constants
+ */
+
+ /**
+ * Elements to validate
+ */
+ private XMLElement manifestElement;
+
+ private final String SMALL_SCREEN = "small"; //$NON-NLS-1$
+
+ //small screen are supported since API level 4
+ private final int SMALL_SCREEN_THRESHOLD = 4;
+
+ @Override
+ public CanExecuteConditionStatus canExecute(ApplicationData data,
+ List<DeviceSpecification> deviceSpecs) throws PreflightingCheckerException
+ {
+ CanExecuteConditionStatus status =
+ new CanExecuteConditionStatus(IStatus.OK, CheckerPlugin.PLUGIN_ID, "");
+
+ manifestElement = data.getManifestElement();
+ if (manifestElement == null)
+ {
+ status =
+ new CanExecuteConditionStatus(IStatus.ERROR, CheckerPlugin.PLUGIN_ID,
+ CheckerNLS.Invalid_ManifestFile);
+ }
+ else
+ {
+ Document manifestDoc = manifestElement.getDocument();
+
+ if (manifestDoc == null)
+ {
+ status =
+ new CanExecuteConditionStatus(IStatus.ERROR, CheckerPlugin.PLUGIN_ID,
+ CheckerNLS.Invalid_ManifestFile);
+ }
+ }
+
+ status.setConditionId(getId());
+ return status;
+ }
+
+ @Override
+ public void execute(ApplicationData data, List<DeviceSpecification> deviceSpecs,
+ ValidationManagerConfiguration valManagerConfig, ValidationResult results)
+ throws PreflightingCheckerException
+ {
+ checkSmallScreenCondition(deviceSpecs, valManagerConfig, results);
+ }
+
+ private void checkSmallScreenCondition(List<DeviceSpecification> deviceSpecs,
+ ValidationManagerConfiguration valManagerConfig, ValidationResult results)
+ throws PreflightingCheckerException
+ {
+ boolean userDeclaredUnsupported = false;
+ boolean supportSmallScreen = true;
+ Document manifestDoc = manifestElement.getDocument();
+ Node usesSdkNode = null;
+
+ // get supports-screen node
+ NodeList supportsScreenLst =
+ manifestDoc.getElementsByTagName(ManifestConstants.SUPPORTS_SCREEN_TAG);
+ if (supportsScreenLst.getLength() > 0)
+ {
+ Node supportsScreenNode = supportsScreenLst.item(0); //Get the first occurrence.
+ NamedNodeMap map = supportsScreenNode.getAttributes();
+ //small-screen attribute
+ Node smallScreenNode = map.getNamedItem(ManifestConstants.SMALL_SCREENS_ATTRIBUTE);
+ if (smallScreenNode != null)
+ {
+ String smallScreenNodeValue = smallScreenNode.getNodeValue();
+ try
+ {
+ userDeclaredUnsupported = !Boolean.parseBoolean(smallScreenNodeValue);
+ }
+ catch (Exception e)
+ {
+ //Do Nothing, the value will be assumed to be false.
+ }
+ }
+ }
+
+ if (!userDeclaredUnsupported)
+ {
+ NodeList usesSdkList = manifestDoc.getElementsByTagName(ManifestConstants.USES_SDK_TAG);
+ if (usesSdkList.getLength() > 0)
+ {
+ usesSdkNode = usesSdkList.item(0);
+ supportSmallScreen = checkIfMinSdkTooLowFilteringSmallDevices(usesSdkNode);
+ }
+ }
+
+ //small screens are not supported by user application
+ if (!supportSmallScreen)
+ {
+ ArrayList<String> smallDevicesList = new ArrayList<String>();
+ //check if is there any device with small screen on the list
+ for (DeviceSpecification currentSpec : deviceSpecs)
+ {
+ ParametersType params = currentSpec.getDeviceInfo().getDefault();
+ String deviceScreenSize = params.getScreenSize();
+ if (deviceScreenSize.equals(SMALL_SCREEN))
+ {
+ smallDevicesList.add(currentSpec.getDeviceInfo().getName());
+ }
+ }
+
+ int currentIssuedLine = manifestElement.getNodeLineNumber(usesSdkNode);
+ String preview = XmlUtils.getXMLNodeAsString(usesSdkNode, false);
+ //list of small screen devices
+ for (String currentDevice : smallDevicesList)
+ {
+ ValidationResultData resultData = new ValidationResultData();
+ resultData.setSeverity(getSeverityLevel());
+ resultData.setConditionID(getId());
+ resultData
+ .setIssueDescription(CheckerNLS
+ .bind(CheckerNLS.DeviceCompatibilityChecker_SMALL_SCREEN_SUPPORT_ISSUE_DESCRIPTION,
+ currentDevice));
+
+ String fixSuggestionMessage =
+ CheckerNLS.DeviceCompatibilityChecker_SMALL_SCREEN_SUPPORT_FIX_SUGGESTION;
+
+ resultData.setQuickFixSuggestion(fixSuggestionMessage);
+
+ ArrayList<Integer> lines = new ArrayList<Integer>();
+ lines.add(currentIssuedLine);
+ resultData.addFileToIssueLines(manifestElement.getFile(), lines);
+ resultData.setInfoURL(ConditionUtils.getDescriptionLink(getChecker().getId(),
+ getId(), valManagerConfig));
+ resultData.setPreview(preview);
+ results.addValidationResult(resultData);
+ }
+ }
+ }
+
+ //Verify if minSdk and targetSdk values are less than 4 (the SMALL_SCREEN_THRESHOLD)
+ private boolean checkIfMinSdkTooLowFilteringSmallDevices(Node usesSdkNode)
+ throws PreflightingCheckerException
+ {
+ boolean supportsSmallScreens = true;
+ Node minSdkAtr =
+ usesSdkNode.getAttributes().getNamedItem(
+ ManifestConstants.MIN_SDK_VERSION_ATTRIBUTE);
+ try
+ {
+ if (minSdkAtr != null)
+ {
+ String minSdkStr = minSdkAtr.getNodeValue().trim();
+ try
+ {
+ Integer minSdk = Integer.valueOf(minSdkStr);
+ if (minSdk < SMALL_SCREEN_THRESHOLD) //$NON-NLS-1$
+ {
+ // min sdk too low => check target version
+ Node targetSdkVersionAtr =
+ usesSdkNode.getAttributes().getNamedItem(
+ ManifestConstants.ANDROID_TARGET_SDK_VERSION_ATTRIBUTE);
+ if ((targetSdkVersionAtr == null)
+ || (targetSdkVersionAtr.getNodeValue().trim().length() == 0))
+ {
+ supportsSmallScreens = false;
+ }
+ else
+ {
+ // target sdk declared => check if value is ok
+ String targetSdkVersionStr = targetSdkVersionAtr.getNodeValue().trim();
+ try
+ {
+ Integer targetSdkVersion = Integer.valueOf(targetSdkVersionStr);
+ if (targetSdkVersion < SMALL_SCREEN_THRESHOLD)
+ {
+ supportsSmallScreens = false;
+ }
+ }
+ catch (NumberFormatException nfe)
+ {
+ // Do nothing, it will be handled on the target sdk is a preview condition.
+ }
+ }
+
+ }
+ }
+ catch (NumberFormatException nfe)
+ {
+ // Do nothing, it will be handled on the min sdk is a preview condition.
+ }
+ }
+ }
+ catch (DOMException e)
+ {
+ // Error retrieving attribute
+ throw new PreflightingCheckerException(CheckerNLS.bind(
+ CheckerNLS.AndroidMarketFiltersChecker_Exception_Getting_Manifest_Attribute,
+ ManifestConstants.ANDROID_TARGET_SDK_VERSION_ATTRIBUTE), e);
+ }
+ return supportsSmallScreens;
+ }
+
+}
diff --git a/src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/deviceCompatibility/UnsupportedFeaturesConditions.java b/src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/deviceCompatibility/UnsupportedFeaturesConditions.java
new file mode 100644
index 0000000..8f5d53c
--- /dev/null
+++ b/src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/deviceCompatibility/UnsupportedFeaturesConditions.java
@@ -0,0 +1,315 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorolamobility.preflighting.checkers.deviceCompatibility;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.eclipse.core.runtime.IStatus;
+import org.w3c.dom.DOMException;
+import org.w3c.dom.Document;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import com.motorolamobility.preflighting.checkers.CheckerPlugin;
+import com.motorolamobility.preflighting.checkers.i18n.CheckerNLS;
+import com.motorolamobility.preflighting.core.applicationdata.ApplicationData;
+import com.motorolamobility.preflighting.core.applicationdata.XMLElement;
+import com.motorolamobility.preflighting.core.checker.condition.CanExecuteConditionStatus;
+import com.motorolamobility.preflighting.core.checker.condition.Condition;
+import com.motorolamobility.preflighting.core.checker.condition.ICondition;
+import com.motorolamobility.preflighting.core.devicelayoutspecification.Device;
+import com.motorolamobility.preflighting.core.devicespecification.DeviceSpecification;
+import com.motorolamobility.preflighting.core.devicespecification.internal.PlatformRules;
+import com.motorolamobility.preflighting.core.exception.PreflightingCheckerException;
+import com.motorolamobility.preflighting.core.internal.cond.utils.ConditionUtils;
+import com.motorolamobility.preflighting.core.permissionfeature.Feature;
+import com.motorolamobility.preflighting.core.utils.XmlUtils;
+import com.motorolamobility.preflighting.core.validation.ValidationManagerConfiguration;
+import com.motorolamobility.preflighting.core.validation.ValidationResult;
+import com.motorolamobility.preflighting.core.validation.ValidationResultData;
+
+/**
+ * Check if application requires any feature that is not available on a given device.
+ */
+public class UnsupportedFeaturesConditions extends Condition implements ICondition
+{
+
+ private static final String USES_FEATURE = "uses-feature"; //$NON-NLS-1$
+
+ private static final String USES_PERMISSION = "uses-permission"; //$NON-NLS-1$
+
+ private static final String ANDROID_NAME = "android:name"; //$NON-NLS-1$
+
+ private static final String ANDROID_REQUIRED = "android:required"; //$NON-NLS-1$
+
+ /**
+ * Elements to validate
+ */
+ private XMLElement manifestElement;
+
+ @Override
+ public CanExecuteConditionStatus canExecute(ApplicationData data,
+ List<DeviceSpecification> deviceSpecs) throws PreflightingCheckerException
+ {
+ CanExecuteConditionStatus status =
+ new CanExecuteConditionStatus(IStatus.OK, CheckerPlugin.PLUGIN_ID, "");
+
+ manifestElement = data.getManifestElement();
+ if (manifestElement == null)
+ {
+ status =
+ new CanExecuteConditionStatus(IStatus.ERROR, CheckerPlugin.PLUGIN_ID,
+ CheckerNLS.Invalid_ManifestFile);
+ }
+ else
+ {
+ Document manifestDoc = manifestElement.getDocument();
+
+ if (manifestDoc == null)
+ {
+ status =
+ new CanExecuteConditionStatus(IStatus.ERROR, CheckerPlugin.PLUGIN_ID,
+ CheckerNLS.Invalid_ManifestFile);
+ }
+ }
+
+ status.setConditionId(getId());
+ return status;
+ }
+
+ @Override
+ public void execute(ApplicationData data, List<DeviceSpecification> deviceSpecs,
+ ValidationManagerConfiguration valManagerConfig, ValidationResult results)
+ throws PreflightingCheckerException
+ {
+ checkUnsupportedFeatures(PlatformRules.getInstance(), deviceSpecs, valManagerConfig,
+ results);
+ }
+
+ /**
+ * Check if there is features (declared or implied) that are unsupported by a device
+ * @param platformRules
+ * @param deviceSpecs
+ * @param valManagerConfig
+ * @param resultList
+ * @throws PreflightingCheckerException
+ */
+ private void checkUnsupportedFeatures(PlatformRules platformRules,
+ List<DeviceSpecification> deviceSpecs, ValidationManagerConfiguration valManagerConfig,
+ ValidationResult results) throws PreflightingCheckerException
+ {
+ Document manifestDoc = manifestElement.getDocument();
+ //collecting set of declared features
+ Map<String, Feature> featuresDeclared = new HashMap<String, Feature>();
+ for (DeviceSpecification deviceSpec : deviceSpecs)
+ {
+ Device device = deviceSpec.getDeviceInfo();
+ checkIssuesForDeclaredFeatures(valManagerConfig, results, manifestDoc,
+ featuresDeclared, device);
+
+ checkIssuesForFeaturesImplied(platformRules, valManagerConfig, results, manifestDoc,
+ featuresDeclared, device);
+ }
+
+ }
+
+ /**
+ * Checks unsupported features for a device that were declared
+ * @param platformRules
+ * @param valManagerConfig
+ * @param resultList
+ * @param manifestDoc
+ * @param featuresDeclared
+ * @param device
+ * @throws PreflightingCheckerException
+ */
+ private void checkIssuesForFeaturesImplied(PlatformRules platformRules,
+ ValidationManagerConfiguration valManagerConfig, ValidationResult results,
+ Document manifestDoc, Map<String, Feature> featuresDeclared, Device device)
+ throws PreflightingCheckerException
+ {
+ int currentIssuedLine;
+ String preview;
+ //collecting set of declared features derived from permissions
+ NodeList permissionsLst = manifestDoc.getElementsByTagName(USES_PERMISSION);
+ for (int i = 0; i < permissionsLst.getLength(); i++)
+ {
+ Node permissionNode = permissionsLst.item(i);
+ NamedNodeMap permissionMap = permissionNode.getAttributes();
+ Node permissionAtr = permissionMap.getNamedItem(ANDROID_NAME); //$NON-NLS-1$
+ Node permissionRequiredAtr = permissionMap.getNamedItem(ANDROID_REQUIRED);//$NON-NLS-1$
+ try
+ {
+ boolean required = true;
+ if (permissionRequiredAtr != null)
+ {
+ required = permissionRequiredAtr.getNodeValue().trim().equals("true"); //$NON-NLS-1$
+ }
+ if (required && (permissionAtr != null)
+ && !permissionAtr.getNodeValue().trim().equals("")) //$NON-NLS-1$
+ {
+ String permissionId = permissionAtr.getNodeValue();
+ Set<Feature> featuresImplied =
+ platformRules.getImpliedFeaturesSet(permissionId);
+
+ for (Feature impliedFeature : featuresImplied)
+ {
+ if (!device.getSupportedFeatures().isEmpty()
+ && !device.getSupportedFeatures().contains(impliedFeature))
+ {
+ //if device has features supported declared (old devices.xml won't have this declaration)
+ //and feature was not found
+ Feature decl = featuresDeclared.get(impliedFeature.getId());
+ if ((decl == null) || decl.isRequired())
+ {
+ //permission => implied feature unsupported : raise issue
+ //because it is not marked as not required
+ currentIssuedLine =
+ manifestElement.getNodeLineNumber(permissionNode);
+ preview = XmlUtils.getXMLNodeAsString(permissionNode, false);
+
+ //feature declared but unsupported => raise warning
+ ValidationResultData resultData = new ValidationResultData();
+ resultData.setSeverity(getSeverityLevel());
+ resultData.setConditionID(getId());
+ resultData
+ .setIssueDescription(CheckerNLS
+ .bind(CheckerNLS.DeviceCompatibilityChecker_CONDITION_UNSUPPORTED_FEATURE_IMPLIED_ISSUE_DESCRIPTION,
+ new String[]
+ {
+ permissionId,
+ impliedFeature.getId(),
+ device.getName()
+ }));
+ String fixSuggestionMessage =
+ (CheckerNLS
+ .bind(CheckerNLS.DeviceCompatibilityChecker_CONDITION_UNSUPPORTED_FEATURE_FIX_SUGGESTION,
+ new String[]
+ {
+ impliedFeature.getId()
+ }));
+ resultData.setQuickFixSuggestion(fixSuggestionMessage);
+ resultData.setMarkerType(getMarkerType());
+ resultData.appendExtra(impliedFeature.getId());
+
+ ArrayList<Integer> lines = new ArrayList<Integer>();
+ lines.add(currentIssuedLine);
+ resultData.addFileToIssueLines(manifestElement.getFile(), lines);
+ resultData.setInfoURL(ConditionUtils.getDescriptionLink(
+ getChecker().getId(), getId(), valManagerConfig));
+ resultData.setPreview(preview);
+ results.addValidationResult(resultData);
+
+ }
+
+ }
+ }
+ }
+ }
+ catch (DOMException e)
+ {
+ // Error retrieving attribute
+ throw new PreflightingCheckerException(
+ CheckerNLS.bind(
+ CheckerNLS.AndroidMarketFiltersChecker_Exception_Getting_Manifest_Attribute,
+ ANDROID_NAME), e);
+ }
+ }
+ }
+
+ /**
+ * Checks unsupported features for a device (permissions that imply in features)
+ * @param valManagerConfig
+ * @param resultList
+ * @param manifestDoc
+ * @param featuresDeclared
+ * @param device
+ */
+ private void checkIssuesForDeclaredFeatures(ValidationManagerConfiguration valManagerConfig,
+ ValidationResult results, Document manifestDoc, Map<String, Feature> featuresDeclared,
+ Device device)
+ {
+ int currentIssuedLine;
+ String preview;
+ NodeList featureLst = manifestDoc.getElementsByTagName(USES_FEATURE);
+ for (int j = 0; j < featureLst.getLength(); j++)
+ {
+ Node featureNode = featureLst.item(j);
+ NamedNodeMap featureMap = featureNode.getAttributes();
+ Node featureAtr = featureMap.getNamedItem(ANDROID_NAME);
+ Node featureRequiredAtr = featureMap.getNamedItem(ANDROID_REQUIRED);//$NON-NLS-1$
+ if ((featureAtr != null) && !featureAtr.getNodeValue().trim().equals("")) //$NON-NLS-1$
+ {
+ Feature featDecl = new Feature(featureAtr.getNodeValue());
+ boolean required = true;
+ if (featureRequiredAtr != null)
+ {
+ required = featureRequiredAtr.getNodeValue().trim().equals("true"); //$NON-NLS-1$
+ featDecl.setRequired(required);
+ }
+ featuresDeclared.put(featDecl.getId(), featDecl);
+ if (required)
+ {
+ if (!device.getSupportedFeatures().isEmpty()
+ && !device.getSupportedFeatures().contains(featDecl))
+ {
+ //if device has features supported declared (old devices.xml won't have this declaration)
+ //and feature was not found
+
+ //feature declared but unsupported => raise issue
+ currentIssuedLine = manifestElement.getNodeLineNumber(featureNode);
+ preview = XmlUtils.getXMLNodeAsString(featureNode, false);
+
+ ValidationResultData resultData = new ValidationResultData();
+ resultData.setSeverity(getSeverityLevel());
+ resultData.setConditionID(getId());
+ resultData
+ .setIssueDescription(CheckerNLS
+ .bind(CheckerNLS.DeviceCompatibilityChecker_CONDITION_UNSUPPORTED_FEATURE_DECLARED_ISSUE_DESCRIPTION,
+ new String[]
+ {
+ featDecl.getId(), device.getName()
+ }));
+ String fixSuggestionMessage =
+ (CheckerNLS
+ .bind(CheckerNLS.DeviceCompatibilityChecker_CONDITION_UNSUPPORTED_FEATURE_FIX_SUGGESTION,
+ new String[]
+ {
+ featDecl.getId()
+ }));
+ resultData.setQuickFixSuggestion(fixSuggestionMessage);
+ resultData.setMarkerType(getMarkerType());
+ resultData.appendExtra(featDecl.getId());
+
+ ArrayList<Integer> lines = new ArrayList<Integer>();
+ lines.add(currentIssuedLine);
+ resultData.addFileToIssueLines(manifestElement.getFile(), lines);
+ resultData.setInfoURL(ConditionUtils.getDescriptionLink(getChecker()
+ .getId(), getId(), valManagerConfig));
+ resultData.setPreview(preview);
+ results.addValidationResult(resultData);
+ }
+ }
+ }
+ }
+ }
+
+}
diff --git a/src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/deviceCompatibility/XLargeScreensSupportCondition.java b/src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/deviceCompatibility/XLargeScreensSupportCondition.java
new file mode 100644
index 0000000..9bb67fd
--- /dev/null
+++ b/src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/deviceCompatibility/XLargeScreensSupportCondition.java
@@ -0,0 +1,269 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorolamobility.preflighting.checkers.deviceCompatibility;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.core.runtime.IStatus;
+import org.w3c.dom.Document;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import com.motorolamobility.preflighting.checkers.CheckerPlugin;
+import com.motorolamobility.preflighting.checkers.i18n.CheckerNLS;
+import com.motorolamobility.preflighting.core.applicationdata.ApplicationData;
+import com.motorolamobility.preflighting.core.applicationdata.XMLElement;
+import com.motorolamobility.preflighting.core.checker.condition.CanExecuteConditionStatus;
+import com.motorolamobility.preflighting.core.checker.condition.Condition;
+import com.motorolamobility.preflighting.core.checker.condition.ICondition;
+import com.motorolamobility.preflighting.core.devicelayoutspecification.ParametersType;
+import com.motorolamobility.preflighting.core.devicespecification.DeviceSpecification;
+import com.motorolamobility.preflighting.core.exception.PreflightingCheckerException;
+import com.motorolamobility.preflighting.core.internal.cond.utils.ConditionUtils;
+import com.motorolamobility.preflighting.core.utils.CheckerUtils;
+import com.motorolamobility.preflighting.core.utils.ManifestConstants;
+import com.motorolamobility.preflighting.core.utils.XmlUtils;
+import com.motorolamobility.preflighting.core.validation.ValidationManagerConfiguration;
+import com.motorolamobility.preflighting.core.validation.ValidationResult;
+import com.motorolamobility.preflighting.core.validation.ValidationResultData;
+
+/**
+ * Check if device has small screen but AndroidManifest set smallScreens=false.
+ */
+public class XLargeScreensSupportCondition extends Condition implements ICondition
+{
+
+ /*
+ * AndroidManifest.xml constants
+ */
+
+ /**
+ * Elements to validate
+ */
+ private XMLElement manifestElement;
+
+ private final String XLARGE_SCREEN = "xlarge"; //$NON-NLS-1$
+
+ //xlarge screens are supported since API level 4
+ private final int XLARGE_SCREEN_THRESHOLD = 4;
+
+ @Override
+ public CanExecuteConditionStatus canExecute(ApplicationData data,
+ List<DeviceSpecification> deviceSpecs) throws PreflightingCheckerException
+ {
+ CanExecuteConditionStatus status =
+ new CanExecuteConditionStatus(IStatus.OK, CheckerPlugin.PLUGIN_ID, "");
+
+ manifestElement = data.getManifestElement();
+ if (manifestElement == null)
+ {
+ status =
+ new CanExecuteConditionStatus(IStatus.ERROR, CheckerPlugin.PLUGIN_ID,
+ CheckerNLS.Invalid_ManifestFile);
+ }
+ else
+ {
+ Document manifestDoc = manifestElement.getDocument();
+
+ if (manifestDoc == null)
+ {
+ status =
+ new CanExecuteConditionStatus(IStatus.ERROR, CheckerPlugin.PLUGIN_ID,
+ CheckerNLS.Invalid_ManifestFile);
+ }
+ }
+
+ status.setConditionId(getId());
+ return status;
+ }
+
+ @Override
+ public void execute(ApplicationData data, List<DeviceSpecification> deviceSpecs,
+ ValidationManagerConfiguration valManagerConfig, ValidationResult results)
+ throws PreflightingCheckerException
+ {
+ checkXLargeScreenCondition(deviceSpecs, valManagerConfig, results);
+ }
+
+ private void checkXLargeScreenCondition(List<DeviceSpecification> deviceSpecs,
+ ValidationManagerConfiguration valManagerConfig, ValidationResult results)
+ {
+ boolean supportXLargeScreen = true;
+ int currentIssuedLine = -1;
+ String preview = null;
+ Document manifestDoc = manifestElement.getDocument();
+
+ // get supports-screen node
+ NodeList supportsScreenLst =
+ manifestDoc.getElementsByTagName(ManifestConstants.SUPPORTS_SCREEN_TAG);
+ if (supportsScreenLst.getLength() > 0)
+ {
+ Node supportsScreenNode = supportsScreenLst.item(0); //Get the first occurrence.
+ NamedNodeMap map = supportsScreenNode.getAttributes();
+ //xlarge-screen attribute
+ Node xlargeScreenNode = map.getNamedItem(ManifestConstants.XLARGE_SCREENS_ATTRIBUTE);
+ if (xlargeScreenNode != null)
+ {
+ String xlargeScreenNodeValue = xlargeScreenNode.getNodeValue();
+ //tag line
+ currentIssuedLine = manifestElement.getNodeLineNumber(supportsScreenNode);
+ preview = XmlUtils.getXMLNodeAsString(supportsScreenNode, false);
+ try
+ {
+ supportXLargeScreen = Boolean.parseBoolean(xlargeScreenNodeValue);
+ }
+ catch (Exception e)
+ {
+ //Do Nothing. Value will assumed to be true.
+ }
+ }
+ }
+
+ //Verify if Compatible screens list the xlarge size
+ if (supportXLargeScreen)
+ {
+ boolean compatibleXLargeScreens = true;
+ NodeList compatibleScreensNodes =
+ manifestDoc.getElementsByTagName("compatible-screens");
+ if (compatibleScreensNodes.getLength() > 0)
+ {
+ compatibleXLargeScreens = false;
+ supportXLargeScreen = false; //Change it to false until we find a xlarge screen size under compatible-screens
+ Node compatibleScreensNode = compatibleScreensNodes.item(0);
+ NodeList screenNodes = compatibleScreensNode.getChildNodes();
+ for (int i = 0; i < screenNodes.getLength(); i++)
+ {
+ Node screenNode = screenNodes.item(i);
+ NamedNodeMap screenAttributes = screenNode.getAttributes();
+ if (screenAttributes != null)
+ {
+ Node screenSizeNode = screenAttributes.getNamedItem("android:screenSize");
+ String screenSizeValue = screenSizeNode.getNodeValue();
+ if (screenSizeValue.equals("xlarge"))
+ {
+ compatibleXLargeScreens = true; //User declared that the xlarge is supported.
+ break;
+ }
+ }
+ }
+ }
+ // If the user has not declared that supports xlarge screen = false and has not restricted slarge compatibility by using the compatible-screens tag. We consider that this app should support it and then we can validate
+ if (supportXLargeScreen && compatibleXLargeScreens)
+ {
+
+ NodeList usesSdkList =
+ manifestDoc.getElementsByTagName(ManifestConstants.USES_SDK_TAG);
+ if (usesSdkList.getLength() > 0)
+ {
+ Node usesSdkNode = usesSdkList.item(0);
+ currentIssuedLine = manifestElement.getNodeLineNumber(usesSdkNode);
+ preview = XmlUtils.getXMLNodeAsString(usesSdkNode, false);
+
+ String targetSdkStr = CheckerUtils.getTargetSdk(manifestDoc);
+ int targetSdk = -1;
+ try
+ {
+ targetSdk = Integer.parseInt(targetSdkStr);
+ }
+ catch (NumberFormatException e)
+ {
+ targetSdk = -1; //Target Sdk is a String, it's a preview SDK, we'll not be able to handle this.
+ }
+
+ if ((targetSdk > -1) && (targetSdk < XLARGE_SCREEN_THRESHOLD))
+ {
+ supportXLargeScreen = false;
+ }
+
+ // If targetSdk >= XLARGE_SCREEN_THRESHOLD, we have to test with minSdkVersion
+ if (supportXLargeScreen)
+ {
+ int minSdk = -1;
+ try
+ {
+ minSdk = Integer.parseInt(targetSdkStr);
+ }
+ catch (NumberFormatException e)
+ {
+ minSdk = -1; //Target Sdk is a String, it's a preview SDK, we'll not be able to handle this.
+ }
+ if ((minSdk > -1) && (minSdk < XLARGE_SCREEN_THRESHOLD))
+ {
+ supportXLargeScreen = false;
+ }
+ }
+ }
+ }
+
+ //xlarge screens are not supported by user application
+ if (!supportXLargeScreen && compatibleXLargeScreens)
+ {
+ ArrayList<String> xlargeDevicesList = new ArrayList<String>();
+ //check if is there any device with xlarge screen on the list
+ for (DeviceSpecification currentSpec : deviceSpecs)
+ {
+ ParametersType params = currentSpec.getDeviceInfo().getDefault();
+ String deviceScreenSize = params.getScreenSize();
+ if (deviceScreenSize.equals(XLARGE_SCREEN))
+ {
+ xlargeDevicesList.add(currentSpec.getDeviceInfo().getName());
+ }
+ }
+
+ if (xlargeDevicesList.size() > 0)
+ {
+ String devices = "";
+ //list of xlarge screen devices
+ for (String currentDevice : xlargeDevicesList)
+ {
+ if (devices.equals(""))
+ {
+ devices += "[" + currentDevice + "]";
+ }
+ else
+ {
+ devices += ", [" + currentDevice + "]";
+ }
+ }
+
+ ValidationResultData resultData = new ValidationResultData();
+ resultData.setSeverity(getSeverityLevel());
+ resultData.setConditionID(getId());
+ resultData
+ .setIssueDescription(CheckerNLS
+ .bind(CheckerNLS.DeviceCompatibilityChecker_XLARGE_SCREEN_SUPPORT_ISSUE_DESCRIPTION,
+ devices));
+
+ String fixSuggestionMessage =
+ CheckerNLS.DeviceCompatibilityChecker_XLARGE_SCREEN_SUPPORT_FIX_SUGGESTION;
+
+ resultData.setQuickFixSuggestion(fixSuggestionMessage);
+
+ ArrayList<Integer> lines = new ArrayList<Integer>();
+ lines.add(currentIssuedLine);
+ resultData.addFileToIssueLines(manifestElement.getFile(), lines);
+ resultData.setInfoURL(ConditionUtils.getDescriptionLink(getChecker().getId(),
+ getId(), valManagerConfig));
+ resultData.setPreview(preview);
+ results.addValidationResult(resultData);
+
+ }
+ }
+ }
+ }
+}
diff --git a/src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/i18n/CheckerNLS.java b/src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/i18n/CheckerNLS.java
new file mode 100644
index 0000000..6b42b62
--- /dev/null
+++ b/src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/i18n/CheckerNLS.java
@@ -0,0 +1,236 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+package com.motorolamobility.preflighting.checkers.i18n;
+
+import org.eclipse.osgi.util.NLS;
+
+public class CheckerNLS extends NLS
+{
+ private static final String BUNDLE_NAME =
+ "com.motorolamobility.preflighting.checkers.i18n.CheckerNLS"; //$NON-NLS-1$
+
+ public static String Missing_res_folder;
+
+ public static String MainActivityChecker_Exception_Get_Action_Intent_Value;
+
+ public static String MainActivityChecker_Exception_Get_Category_Intent_Value;
+
+ public static String Invalid_ManifestFile;
+
+ public static String IsDebuggableCondition_AttrFound_Message;
+
+ public static String IsDebuggableCondition_AttrFound_QuickFix;
+
+ public static String MainActivityChecker_manyMainActivity;
+
+ public static String MainActivityChecker_MoreThanOneMainActivityFixSuggestion;
+
+ public static String MainActivityChecker_noMainActivity;
+
+ public static String MainActivityChecker_NoMainActivityFixSuggestion;
+
+ public static String MissingDrawableChecker_AddDrawableToFolder;
+
+ public static String MissingDrawableChecker_CreateFolderOnResFolder;
+
+ public static String MissingDrawableChecker_CreateFoldersOnResFolder;
+
+ public static String MissingDrawableChecker_UselessXhdpiResources;
+
+ public static String MissingDrawableChecker_UselessXhdpiResourcesSugestion;
+
+ public static String MissingDrawableChecker_FolderNotFound;
+
+ public static String MissingDrawableChecker_ldpiFolder;
+
+ public static String MissingDrawableChecker_mdpiFolder;
+
+ public static String MissingDrawableChecker_hdpiFolder;
+
+ public static String MissingDrawableChecker_xhdpiFolder;
+
+ public static String MissingDrawableChecker_missingDrawableDesc;
+
+ public static String MissingDrawableChecker_noDensitySpecificDrawableFolders;
+
+ public static String MissingDrawableChecker_noDrawableFolders;
+
+ public static String MissingWidgetPreviewTagCondition_quickFix;
+
+ public static String MissingWidgetPreviewTagCondition_WarningMessage;
+
+ public static String LocalizationStringsChecker_Exception_MissingDefaultKeys;
+
+ public static String LocalizationStringsChecker_Exception_MissingLocaleKeys;
+
+ public static String LocalizationStringsChecker_Exception_EmptyValuesDefault;
+
+ public static String LocalizationStringsChecker_Exception_EmptyValuesLocale;
+
+ public static String LocalizationStringsChecker_defaultStringNotFoundSimple;
+
+ public static String LocalizationStringsChecker_localeStringNotFoundSimple;
+
+ public static String LocalizationStringsChecker_addStringToLocalizationResourceDetailed;
+
+ public static String LocalizationStringsChecker_invalidParameters;
+
+ public static String LocalizationStringsChecker_invalidDefaultLocaleParameter;
+
+ public static String LocalizationStringsChecker_helpParameterLocaleValueFormatDescription;
+
+ public static String LocalizationStringsChecker_helpParameterLocaleDescription;
+
+ public static String LocalizationStringsChecker_helpParameterLocaleValueDescription;
+
+ public static String LocalizationStringsChecker_detailedLocaleResourceNotFound;
+
+ public static String LocalizationStringsChecker_localeStringEmptyValue;
+
+ public static String LocalizationStringsChecker_defaultStringEmptyValue;
+
+ public static String LocalizationStringsChecker_Missing_stringsXml_File;
+
+ public static String LocalizationStringsChecker_stringEmptyValueQuickFix;
+
+ public static String LocalizationStringsChecker_conditionMissingValue_CouldNotBeRun_EmptyLocalizationFiles;
+
+ public static String LocalizationStringsChecker_conditionMissingKey_CouldNotBeRun_NoDefault;
+
+ public static String LocalizationStringsChecker_conditionMissingKey_CouldNotBeRun_NoLocale;
+
+ public static String LocalizationStringsChecker_UnknownError;
+
+ public static String LogCallsCondition_CallFound_Message;
+
+ public static String LogCallsCondition_CallFound_QuickFix;
+
+ public static String AndroidMarketFiltersChecker_declaredMaxSdkVersion_Suggestion;
+
+ public static String AndroidMarketFiltersChecker_Exception_Getting_Manifest_Attribute;
+
+ public static String AndroidMarketFiltersChecker_missingManifestIconOrLabel_Suggestion;
+
+ public static String AndroidMarketFiltersChecker_missingManifestIconOrLabel_Issue;
+
+ public static String AndroidMarketFiltersChecker_certificatePeriodExpired_Suggestion;
+
+ public static String AndroidMarketFiltersChecker_certificatePeriodExpired_Issue;
+
+ public static String AndroidMarketFiltersChecker_declaredMaxSdkVersion_Issue;
+
+ public static String AndroidMarketFiltersChecker_minSdkIsPreview_Issue;
+
+ public static String AndroidMarketFiltersChecker_minSdkIsPreview_Suggestion;
+
+ public static String AndroidMarketFiltersChecker_targetSdkIsPreview_Issue;
+
+ public static String AndroidMarketFiltersChecker_targetSdkIsPreview_Suggestion;
+
+ public static String AndroidMarketFiltersChecker_permissionToImpliedFeatures_Suggestion;
+
+ public static String AndroidMarketFiltersChecker_permissionToImpliedFeatures_Issue;
+
+ //version
+ public static String AndroidMarketFiltersChecker_missingVersionCodeOrName_Suggestion;
+
+ public static String AndroidMarketFiltersChecker_missingVersionCodeOrName_Issue;
+
+ public static String AndroidMarketFiltersChecker_certificatePeriodNotYeatValid_Issue;
+
+ static
+ {
+ // initialize resource bundle
+ NLS.initializeMessages(BUNDLE_NAME, CheckerNLS.class);
+ }
+
+ public static String AndroidMarketFiltersChecker_missingMinSdkVersion_Issue;
+
+ public static String AndroidMarketFiltersChecker_missingMinSdkVersion_Suggestion;
+
+ public static String BuildingBlockDeclarationCondition_TheClassXShouldExtendBuildingBlockY;
+
+ public static String AndroidMarketFiltersChecker_zipaligned_Suggestion;
+
+ public static String AndroidMarketFiltersChecker_zipaligned_Issue;
+
+ public static String AndroidMarketFiltersChecker_zipaligned_ConditionNotExecutedForProject;
+
+ public static String BuildingBlocksInheritanceCondition_Activity;
+
+ public static String BuildingBlocksInheritanceCondition_BroadcastReceiver;
+
+ public static String BuildingBlocksInheritanceCondition_ContentProvider;
+
+ public static String BuildingBlocksInheritanceCondition_Service;
+
+ public static String BuildingBlocksInheritanceCondition_TheBuildingBlockXShouldExtendClassY;
+
+ public static String DeviceCompatibilityChecker_SMALL_SCREEN_SUPPORT_FIX_SUGGESTION;
+
+ public static String DeviceCompatibilityChecker_SMALL_SCREEN_SUPPORT_ISSUE_DESCRIPTION;
+
+ public static String DeviceCompatibilityChecker_XLARGE_SCREEN_SUPPORT_FIX_SUGGESTION;
+
+ public static String DeviceCompatibilityChecker_XLARGE_SCREEN_SUPPORT_ISSUE_DESCRIPTION;
+
+ public static String PermissionsChecker_MissingPermission_Message;
+
+ public static String PermissionsChecker_MissingPermission_QuickFix;
+
+ public static String UnneededPermissionsCondition_UneededPermissionMessage;
+
+ public static String UnneededPermissionsCondition_UneededPermissionQuickFix;
+
+ public static String UnsecurePermissionsChecker_conditionForbiddenPermission_description;
+
+ public static String UnsecurePermissionsChecker_conditionForbiddenPermission_suggestion;
+
+ public static String DeviceCompatibilityChecker_CONDITION_UNSUPPORTED_FEATURE_FIX_SUGGESTION;
+
+ public static String DeviceCompatibilityChecker_CONDITION_UNSUPPORTED_FEATURE_DECLARED_ISSUE_DESCRIPTION;
+
+ public static String DeviceCompatibilityChecker_CONDITION_UNSUPPORTED_FEATURE_IMPLIED_ISSUE_DESCRIPTION;
+
+ public static String LayoutChecker_MissingKeyWarningMessage;
+
+ public static String LayoutChecker_MissingKeyFixSuggestion;
+
+ public static String OpenedCursorsCondition_Result_Message;
+
+ public static String OpenedCursorsCondition_Result_QuickFix;
+
+ public static String RepeatedIdCondition_Result_Description;
+
+ public static String RepeatedIdCondition_Result_QuickFix;
+
+ public static String ViewTypeIdsCondition_Results_Description;
+
+ public static String ViewTypeIdsCondition_Results_QuickFix;
+
+ public static String XlargeConfigCondition_Result_Description;
+
+ public static String XlargeConfigCondition_Result_QuickFix;
+
+ public static String OrphanedStringsCondition_Result_Description;
+
+ public static String OrphanedStringsCondition_Result_QuickFix;
+
+ private CheckerNLS()
+ {
+ }
+}
diff --git a/src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/i18n/CheckerNLS.properties b/src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/i18n/CheckerNLS.properties
new file mode 100644
index 0000000..bcc92b8
--- /dev/null
+++ b/src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/i18n/CheckerNLS.properties
@@ -0,0 +1,116 @@
+#
+# Copyright (C) 2012 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+Missing_res_folder=Could not find the 'res' folder.
+MainActivityChecker_Exception_Get_Action_Intent_Value=An error occurred while retrieving the "android:name" value of an "action" intent filter.
+MainActivityChecker_Exception_Get_Category_Intent_Value=An error occurred while retrieving the "android:name" value of a "category" intent filter.
+Invalid_ManifestFile=Android manifest is invalid. Verify the file.
+IsDebuggableCondition_AttrFound_Message=Attribute android:debuggable is set to true. You should set the android:debuggable attribute to false before publishing the application.
+IsDebuggableCondition_AttrFound_QuickFix=Set the android:debuggable flag to false
+MainActivityChecker_manyMainActivity=More than one main activity found in the Android manifest file.
+MainActivityChecker_MoreThanOneMainActivityFixSuggestion=Make sure that your app has only one activity with an "android.intent.action.MAIN" action in its Android manifest file.
+MainActivityChecker_noMainActivity=No main activity found in the Android manifest file.
+MainActivityChecker_NoMainActivityFixSuggestion=Add the "android.intent.action.MAIN" action and the "android.intent.category.LAUNCHER" category to the same intent-filter on one of the activities declared in the Android manifest file.
+MissingDrawableChecker_AddDrawableToFolder=Add the appropriate "{0}" drawable to the "{1}" folder.
+MissingDrawableChecker_CreateFolderOnResFolder=Create the "{0}" folder inside the "res" folder and add the appropriate drawables to it.
+MissingDrawableChecker_CreateFoldersOnResFolder=Create the "{0}", "{1}" and "{2}" folders inside the "res" folder and add the appropriate drawables to them.
+MissingDrawableChecker_UselessXhdpiResources=The application's API level does not support the xhdpi screen density.
+MissingDrawableChecker_UselessXhdpiResourcesSugestion=Delete the "drawable-xhdpi" folder from the application resources or change the application's API level (in AndroidManifest.xml) to include level 9 or above.
+MissingDrawableChecker_FolderNotFound=Folder "{0}" does not exist.
+MissingDrawableChecker_ldpiFolder=drawable-ldpi
+MissingDrawableChecker_mdpiFolder=drawable-mdpi
+MissingDrawableChecker_hdpiFolder=drawable-hdpi
+MissingDrawableChecker_xhdpiFolder=drawable-xhdpi
+MissingDrawableChecker_missingDrawableDesc=Drawable "{0}" does not exist in the "{1}" folder.
+MissingDrawableChecker_noDensitySpecificDrawableFolders=Density-specific drawable folders were not found for the application. You should create them with density-specific versions of your drawables.
+MissingDrawableChecker_noDrawableFolders=The application has no drawable folders.
+MissingWidgetPreviewTagCondition_quickFix=Add a widget preview to your resources file by inserting android:previewImage="@drawable/yourpreview" to your <appwidget-provider> declaration.
+MissingWidgetPreviewTagCondition_WarningMessage=This widget project does not have a registered preview image in its resources file.
+LocalizationStringsChecker_Exception_MissingDefaultKeys=An error occurred while checking for default keys missing in other localization files.
+LocalizationStringsChecker_Exception_MissingLocaleKeys=An error occurred while checking for non-default keys missing in the default localization files.
+LocalizationStringsChecker_Exception_EmptyValuesDefault= An error occurred while checking for empty values in the default localization files.
+LocalizationStringsChecker_Exception_EmptyValuesLocale= An error occurred while checking for empty values in the non-default localization files.
+LocalizationStringsChecker_defaultStringNotFoundSimple=The "{0}" key from the default localization files was not found in the "{1}" localization files.
+LocalizationStringsChecker_localeStringNotFoundSimple=The "{0}" key from the "{1}" localization files was not found in the default localization files.
+LocalizationStringsChecker_addStringToLocalizationResourceDetailed=Add the key to the proper localization file.
+LocalizationStringsChecker_invalidParameters=The "{0}" parameter is not valid for the Localization Strings Checker.
+LocalizationStringsChecker_invalidDefaultLocaleParameter=The "defaultLocale" parameter value is invalid. Values must follow the "ll" or "ll_CC" format, where "ll" is a language code and "CC" is a country code.
+LocalizationStringsChecker_helpParameterLocaleValueFormatDescription=Values must follow the "ll" or "ll_CC" format, where "ll" is a language code and "CC" is a country code.
+LocalizationStringsChecker_helpParameterLocaleDescription=Set the default locale to be used following the "ll" or "ll_CC" format, where ll is the language and CC is the country. The default "values" folder in your application will be ignored.
+LocalizationStringsChecker_helpParameterLocaleValueDescription=LOCALE
+LocalizationStringsChecker_detailedLocaleResourceNotFound=The locale specified by the "defaultLocale" parameter was not found.
+LocalizationStringsChecker_localeStringEmptyValue=The "{0}" key from the "{1}" localization files does not have a value defined.
+LocalizationStringsChecker_defaultStringEmptyValue=The "{0}" key from the default localization files does not have a value defined.
+LocalizationStringsChecker_Missing_stringsXml_File=Could not find a valid 'strings.xml' file in the required folders.
+LocalizationStringsChecker_stringEmptyValueQuickFix=Define a value for the key.
+LocalizationStringsChecker_conditionMissingValue_CouldNotBeRun_EmptyLocalizationFiles=It was not possible to run condition {0} because the checker could not find any string keys.
+LocalizationStringsChecker_conditionMissingKey_CouldNotBeRun_NoDefault=It was not possible to run condition {0} because the checker could not find the default localization file.
+LocalizationStringsChecker_conditionMissingKey_CouldNotBeRun_NoLocale=It was not possible to run condition {0} because the checker could not find any non-default localization files.
+LocalizationStringsChecker_UnknownError=It was not possible to run condition {0}. Unknown error.
+LogCallsCondition_CallFound_Message=You should remove all log calls before publishing the app.
+LogCallsCondition_CallFound_QuickFix=Remove the log call.
+AndroidMarketFiltersChecker_Exception_Getting_Manifest_Attribute = An error occurred while retrieving the attribute {0}.
+AndroidMarketFiltersChecker_missingManifestIconOrLabel_Suggestion=Add the {0} attribute in the application element or supply a value for it.
+AndroidMarketFiltersChecker_missingManifestIconOrLabel_Issue=The {0} attribute is not defined or is empty in the application element.
+AndroidMarketFiltersChecker_certificatePeriodExpired_Suggestion=Sign your application using a certificate that expires after October 22, 2033.
+AndroidMarketFiltersChecker_certificatePeriodExpired_Issue=Certificate expires on {0}, but Google Play requires it to expire after October 22, 2033.
+AndroidMarketFiltersChecker_missingMinSdkVersion_Issue=The android:minSdkVersion attribute is not defined or is empty in the uses-sdk element. Although this allows the app to be installed on any Android device, it may crash if, for example, the application uses an API not supported by the Android OS version running on that device.
+AndroidMarketFiltersChecker_missingMinSdkVersion_Suggestion=Add the android:minSdkVersion attribute to the uses-sdk element or supply a valid value for it.
+AndroidMarketFiltersChecker_declaredMaxSdkVersion_Issue=The android:maxSdkVersion attribute is defined in the uses-sdk element. This is not recommended as it limits which devices can run your application.
+AndroidMarketFiltersChecker_declaredMaxSdkVersion_Suggestion=Check if the uses-sdk android:maxSdkVersion property is set. If so, remove it from your manifest.
+AndroidMarketFiltersChecker_permissionToImpliedFeatures_Suggestion=Add the implied feature to the Android manifest file. If your app does not require the implied feature, put "android:required="false" in the feature declaration you are adding.
+AndroidMarketFiltersChecker_permissionToImpliedFeatures_Issue=Google Play will assume that the following features are required: {0} as implied by the following permission: {1}.
+AndroidMarketFiltersChecker_missingVersionCodeOrName_Suggestion=Add the {0} attribute to the manifest element or supply a value for it.
+AndroidMarketFiltersChecker_missingVersionCodeOrName_Issue=The {0} attribute is not defined or is empty in the manifest element.
+AndroidMarketFiltersChecker_certificatePeriodNotYeatValid_Issue=Certificate is not valid during the required period. It is only valid after {0}.
+DeviceCompatibilityChecker_SMALL_SCREEN_SUPPORT_FIX_SUGGESTION=In the AndroidManifest.xml file, change your application target API level to 4 or above and then set android:smallScreens to true in the supports-screens element.
+DeviceCompatibilityChecker_SMALL_SCREEN_SUPPORT_ISSUE_DESCRIPTION=Small screen devices are not supported by this application. It will not run on the following device: {0}.
+DeviceCompatibilityChecker_XLARGE_SCREEN_SUPPORT_FIX_SUGGESTION=In the AndroidManifest.xml file, change your application target API level to 4 or above.
+DeviceCompatibilityChecker_XLARGE_SCREEN_SUPPORT_ISSUE_DESCRIPTION=Extra large screen devices are not fully supported by this application. If you want to prevent this app from running on XLarge screen devices, add <compatible-screens> information to the AndroidManifest.xml file. Otherwise it will work under compatibility mode on devices such as: {0}.
+PermissionsChecker_MissingPermission_Message=Called method, {0}, requires the following permission(s): {1}.
+PermissionsChecker_MissingPermission_QuickFix=Verify and add the required permission(s) to the AndroidManifest.xml file
+UnneededPermissionsCondition_UneededPermissionMessage=Declared permission "{0}" does not appear to be needed by any called method. Consider removing this permission.
+UnneededPermissionsCondition_UneededPermissionQuickFix=Remove this permission if it is really not needed by the application.
+UnsecurePermissionsChecker_conditionForbiddenPermission_description=The following permission cannot be used by most applications: {0}.
+UnsecurePermissionsChecker_conditionForbiddenPermission_suggestion=Verify that your application can actually use this permission. If not, remove it from the manifest.
+DeviceCompatibilityChecker_CONDITION_UNSUPPORTED_FEATURE_FIX_SUGGESTION=Verify that the permission or feature is actually required by the application. Consider declaring <uses-feature android:name="{0}" android:required="false"/> in your Android manifest, and then use the feature only on devices that support it (use 'PackageManager.hasSystemFeature(String featureName)' to check for the presence of the feature at run time).
+DeviceCompatibilityChecker_CONDITION_UNSUPPORTED_FEATURE_DECLARED_ISSUE_DESCRIPTION=The application declared the unsupported feature {0} which might prevent the application from being installed on the following device: {1}.
+DeviceCompatibilityChecker_CONDITION_UNSUPPORTED_FEATURE_IMPLIED_ISSUE_DESCRIPTION=The application declares the permission {0} which implies the unsupported feature {1}. This might prevent the application from being installed on the following device: {2}.
+LayoutChecker_MissingKeyWarningMessage=The following ID was not defined in your layout file: "{0}".
+LayoutChecker_MissingKeyFixSuggestion=Add the missing ID to the layout file.
+AndroidMarketFiltersChecker_minSdkIsPreview_Issue=The android:minSdkVersion declared in the Android manifest file is a string instead of a number. It is probably a preview SDK, rather than an official release.
+AndroidMarketFiltersChecker_minSdkIsPreview_Suggestion=Verify that android:minSDKVersion is for an official release before submitting the app to Google Play.
+AndroidMarketFiltersChecker_targetSdkIsPreview_Issue=The android:targetSdkVersion is specified in the Android manifest using a string rather than a number. It is probably a preview SDK instead of an official release.
+AndroidMarketFiltersChecker_targetSdkIsPreview_Suggestion=Verify that the android:targetSDKVersion is for an official SDK release before submitting the application to Google Play.
+BuildingBlockDeclarationCondition_TheClassXShouldExtendBuildingBlockY=The building block represented by class "{0}" does not directly extend "{1}". Your app could crash if "{1}" is not within the hierarchy of "{0}".
+AndroidMarketFiltersChecker_zipaligned_Suggestion=Use the zipalign tool provided with the Android SDK to optimize your APK file.
+AndroidMarketFiltersChecker_zipaligned_Issue=File {0} is not optimized with zipalign.
+AndroidMarketFiltersChecker_zipaligned_ConditionNotExecutedForProject=Condition {0} will not be executed because it is valid only for APK files.
+BuildingBlocksInheritanceCondition_Activity=Activity
+BuildingBlocksInheritanceCondition_BroadcastReceiver=Broadcast Receiver
+BuildingBlocksInheritanceCondition_ContentProvider=Content Provider
+BuildingBlocksInheritanceCondition_Service=Service
+BuildingBlocksInheritanceCondition_TheBuildingBlockXShouldExtendClassY=Verify that "{0}" has "{1}" within its hierarchy.
+OpenedCursorsCondition_Result_Message=The cursor "{0}" at "{1}" does not seem to be closed. {2} instance(s) using this variable were not closed.
+OpenedCursorsCondition_Result_QuickFix=Make sure that all cursors are closed to avoid problems during app execution.
+RepeatedIdCondition_Result_Description=The following ID is already defined in the layout file {0}: "{1}".
+RepeatedIdCondition_Result_QuickFix=Make sure that IDs are not repeated.
+ViewTypeIdsCondition_Results_Description=Element ID {0}, a {1}, has already been declared as a {2}.
+ViewTypeIdsCondition_Results_QuickFix=Use different IDs for different view types.
+XlargeConfigCondition_Result_Description=Default layout file {0} does not have an xlarge-specific version. This app may not have the look and feel expected on devices with extra large screens.
+XlargeConfigCondition_Result_QuickFix=Define an xlarge layout for {0} or add <supports-screens android:xlargeScreens="false"> to the manifest file.
+OrphanedStringsCondition_Result_Description=Application declares the string ID: {0} but it is not used in source code or resource files.
+OrphanedStringsCondition_Result_QuickFix=Check your code and resources to see if the string should be used. If the string really is unnecessary, consider removing it.
diff --git a/src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/layout/GlobalLayoutId.java b/src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/layout/GlobalLayoutId.java
new file mode 100644
index 0000000..8498110
--- /dev/null
+++ b/src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/layout/GlobalLayoutId.java
@@ -0,0 +1,60 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorolamobility.preflighting.checkers.layout;
+
+import java.util.HashMap;
+import java.util.Set;
+
+import org.w3c.dom.Node;
+
+/**
+ * This class represents the IDs of a given layout,
+ * compiles ids from all configurations of a given layout.
+ * It saves the node where the first occurrence was found.
+ */
+class GlobalLayoutId
+{
+ private HashMap<String, Node> idsMap;
+
+ public GlobalLayoutId()
+ {
+ this.idsMap = new HashMap<String, Node>();
+ }
+
+ public void addID(String strID, Node node)
+ {
+ if (!idsMap.containsKey(strID))
+ {
+ idsMap.put(strID, node);
+ }
+ }
+
+ /*
+ * Returns the node where this ID was first defined.
+ */
+ public Node getNode(String strID)
+ {
+ return idsMap.get(strID);
+ }
+
+ /*
+ * Returns the list of IDs defined in this layout.
+ */
+ public Set<String> getIdsList()
+ {
+ return idsMap.keySet();
+ }
+} \ No newline at end of file
diff --git a/src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/layout/LayoutFileId.java b/src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/layout/LayoutFileId.java
new file mode 100644
index 0000000..db9e50d
--- /dev/null
+++ b/src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/layout/LayoutFileId.java
@@ -0,0 +1,85 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorolamobility.preflighting.checkers.layout;
+
+import java.io.File;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+
+import org.w3c.dom.Node;
+
+/**
+ * Bean class representing all ids in a given layout found.
+ * This class can contain a list of Ids and/or a Map<Node,String> where
+ * the key is the XML Node declaring the id and the value is the id itself.
+ */
+class LayoutFileId
+{
+ private File file;
+
+ private HashSet<String> idsList;
+
+ private Map<Node, String> nodeIdMap;
+
+ public LayoutFileId(File file)
+ {
+ this.file = file;
+ this.idsList = new HashSet<String>();
+ this.nodeIdMap = new HashMap<Node, String>();
+ }
+
+ public LayoutFileId(File file, HashSet<String> idsList)
+ {
+ this.file = file;
+ this.idsList = idsList;
+ this.nodeIdMap = new HashMap<Node, String>();
+ }
+
+ public LayoutFileId(File file, Map<Node, String> nodeMap)
+ {
+ this.file = file;
+ this.idsList = (HashSet<String>) nodeMap.values();
+ this.nodeIdMap = nodeMap;
+ }
+
+ public File getLayoutFile()
+ {
+ return file;
+ }
+
+ public HashSet<String> getIdsList()
+ {
+ return idsList;
+ }
+
+ public Map<Node, String> getNodeIdMap()
+ {
+ return nodeIdMap;
+ }
+
+ public void addId(Node node, String id)
+ {
+ addId(id);
+ nodeIdMap.put(node, id);
+ }
+
+ public void addId(String id)
+ {
+ idsList.add(id);
+ }
+
+} \ No newline at end of file
diff --git a/src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/layout/MissingIdCondition.java b/src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/layout/MissingIdCondition.java
new file mode 100644
index 0000000..1c8b27e
--- /dev/null
+++ b/src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/layout/MissingIdCondition.java
@@ -0,0 +1,231 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorolamobility.preflighting.checkers.layout;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.eclipse.core.runtime.IStatus;
+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 com.motorolamobility.preflighting.checkers.CheckerPlugin;
+import com.motorolamobility.preflighting.checkers.i18n.CheckerNLS;
+import com.motorolamobility.preflighting.core.applicationdata.ApplicationData;
+import com.motorolamobility.preflighting.core.applicationdata.XMLElement;
+import com.motorolamobility.preflighting.core.checker.condition.CanExecuteConditionStatus;
+import com.motorolamobility.preflighting.core.checker.condition.Condition;
+import com.motorolamobility.preflighting.core.checker.condition.ICondition;
+import com.motorolamobility.preflighting.core.devicespecification.DeviceSpecification;
+import com.motorolamobility.preflighting.core.exception.PreflightingCheckerException;
+import com.motorolamobility.preflighting.core.internal.cond.utils.ConditionUtils;
+import com.motorolamobility.preflighting.core.utils.CheckerUtils;
+import com.motorolamobility.preflighting.core.utils.LayoutConstants;
+import com.motorolamobility.preflighting.core.utils.XmlUtils;
+import com.motorolamobility.preflighting.core.validation.ValidationManagerConfiguration;
+import com.motorolamobility.preflighting.core.validation.ValidationResult;
+import com.motorolamobility.preflighting.core.validation.ValidationResultData;
+
+/**
+ * Layout checker condition that verifies if any id declared in a given layout configuration
+ * is missing on another configurations for the declaring layout
+ */
+public class MissingIdCondition extends Condition implements ICondition
+{
+
+ /*
+ * This map keeps all the keys for a given layout e.g. main.xml.
+ * It is used to look for any missing ID.
+ */
+ private HashMap<String, GlobalLayoutId> globalMap;
+
+ private ValidationManagerConfiguration valManagerConfig;
+
+ @Override
+ public CanExecuteConditionStatus canExecute(ApplicationData data,
+ List<DeviceSpecification> deviceSpecs) throws PreflightingCheckerException
+ {
+ CanExecuteConditionStatus status =
+ new CanExecuteConditionStatus(IStatus.OK, CheckerPlugin.PLUGIN_ID, "");
+ status.setConditionId(getId());
+ return status;
+ }
+
+ @Override
+ public void execute(ApplicationData data, List<DeviceSpecification> deviceSpecs,
+ ValidationManagerConfiguration valManagerConfig, ValidationResult results)
+ throws PreflightingCheckerException
+ {
+ List<XMLElement> layoutList = data.getLayoutElements();
+ if (layoutList != null)
+ {
+ this.valManagerConfig = valManagerConfig;
+
+ globalMap = new HashMap<String, GlobalLayoutId>();
+
+ //This map stores the IDs of each layout file.
+ //An instance of LayoutFileIDs is created for each file
+ //and the map associates it to the given layout.
+ //Hence, two files of different configurations but with the same name e.g. main.xml
+ //will be associated under the key "main.xml" on the map.
+ HashMap<String, List<LayoutFileId>> mainMap = new HashMap<String, List<LayoutFileId>>();
+
+ for (XMLElement element : layoutList)
+ {
+ String layoutName = element.getFile().getName();
+
+ //initialize globalMap
+ if (!globalMap.containsKey(layoutName))
+ {
+ globalMap.put(layoutName, new GlobalLayoutId());
+ }
+
+ //search for IDs and saves it in the map
+ HashSet<String> idsList = retriveLayoutIDs(layoutName, element.getDocument());
+
+ if (mainMap.keySet().contains(layoutName))
+ {
+ mainMap.get(layoutName).add(new LayoutFileId(element.getFile(), idsList));
+ }
+ else
+ {
+ List<LayoutFileId> layoutArray = new ArrayList<LayoutFileId>();
+ layoutArray.add(new LayoutFileId(element.getFile(), idsList));
+ mainMap.put(layoutName, layoutArray);
+ }
+ }
+
+ //create the results
+ checkForMissingLayoutIDs(mainMap, results);
+ }
+ }
+
+ /*
+ * Analyze the generated lists and create the results.
+ */
+ private void checkForMissingLayoutIDs(HashMap<String, List<LayoutFileId>> mainMap,
+ ValidationResult results)
+ {
+ //for each layout e.g. main.xml
+ for (String key : mainMap.keySet())
+ {
+ Set<String> currentCompleteIDList = new HashSet<String>();
+ //this is the list of all IDs found for the given layout
+ final Set<String> completeIDList = globalMap.get(key).getIdsList();
+
+ //for each file e.g. any main.xml found inside the layout's directories
+ for (LayoutFileId layout : mainMap.get(key))
+ {
+ currentCompleteIDList.addAll(completeIDList);
+
+ //check if there are missing keys
+ if (!layout.getIdsList().containsAll(currentCompleteIDList))
+ {
+ currentCompleteIDList.removeAll(layout.getIdsList());
+
+ //create the result for each key
+ for (String missingKey : currentCompleteIDList)
+ {
+ ValidationResultData data = new ValidationResultData();
+ data.addFileToIssueLines(layout.getLayoutFile(), new ArrayList<Integer>());
+ data.setConditionID(getId());
+ data.setSeverity(getSeverityLevel());
+ data.setQuickFixSuggestion(CheckerNLS.LayoutChecker_MissingKeyFixSuggestion);
+ data.setPreview(XmlUtils.getXMLNodeAsString(
+ globalMap.get(key).getNode(missingKey), false));
+ data.setIssueDescription(CheckerNLS.bind(
+ CheckerNLS.LayoutChecker_MissingKeyWarningMessage, missingKey));
+ data.setInfoURL(ConditionUtils.getDescriptionLink(getChecker().getId(),
+ getId(), valManagerConfig));
+ results.addValidationResult(data);
+ }
+ }
+ currentCompleteIDList.clear();
+ }
+ }
+ }
+
+ /*
+ * start the visit by the root node
+ */
+ private HashSet<String> retriveLayoutIDs(String layoutName, Document document)
+ {
+ //this is the current layout ID list
+ HashSet<String> idsList = new HashSet<String>();
+ Element rootElem = document.getDocumentElement();
+
+ String rootId = rootElem.getAttribute(LayoutConstants.ANDROID_ID_ATTRIBUTE);
+ rootId = CheckerUtils.getIdValue(rootId.trim());
+ if (rootId.length() > 0)
+ {
+ //add the ID to local and global list
+ idsList.add(rootId);
+ globalMap.get(layoutName).addID(rootId, rootElem);
+ }
+
+ //visit children
+ NodeList nodeList = rootElem.getChildNodes();
+ for (int i = 0; i < nodeList.getLength(); i++)
+ {
+ Node node = nodeList.item(i);
+ if (node.getNodeType() == Node.ELEMENT_NODE)
+ {
+ visitNode(layoutName, node, idsList);
+ }
+ }
+
+ return idsList;
+ }
+
+ /**
+ * Visit nodes recursively, retrieving its IDs.
+ */
+ private void visitNode(String layoutName, Node node, HashSet<String> idsList)
+ {
+ NamedNodeMap map = node.getAttributes();
+ Node attribute = map.getNamedItem(LayoutConstants.ANDROID_ID_ATTRIBUTE);
+
+ if (attribute != null)
+ {
+ String id = attribute.getTextContent();
+ id = CheckerUtils.getIdValue(id.trim());
+ if (id.length() > 0)
+ {
+ //add the ID to local and global list
+ idsList.add(id);
+ globalMap.get(layoutName).addID(id, node);
+ }
+ }
+
+ //visit children
+ NodeList nodeList = node.getChildNodes();
+ for (int i = 0; i < nodeList.getLength(); i++)
+ {
+ Node childNode = nodeList.item(i);
+ if (childNode.getNodeType() == Node.ELEMENT_NODE)
+ {
+ visitNode(layoutName, childNode, idsList);
+ }
+ }
+ }
+
+}
diff --git a/src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/layout/RepeatedIdCondition.java b/src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/layout/RepeatedIdCondition.java
new file mode 100644
index 0000000..5ae29ea
--- /dev/null
+++ b/src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/layout/RepeatedIdCondition.java
@@ -0,0 +1,152 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorolamobility.preflighting.checkers.layout;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.osgi.util.NLS;
+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 com.motorolamobility.preflighting.checkers.CheckerPlugin;
+import com.motorolamobility.preflighting.checkers.i18n.CheckerNLS;
+import com.motorolamobility.preflighting.core.applicationdata.ApplicationData;
+import com.motorolamobility.preflighting.core.applicationdata.XMLElement;
+import com.motorolamobility.preflighting.core.checker.condition.CanExecuteConditionStatus;
+import com.motorolamobility.preflighting.core.checker.condition.Condition;
+import com.motorolamobility.preflighting.core.checker.condition.ICondition;
+import com.motorolamobility.preflighting.core.devicespecification.DeviceSpecification;
+import com.motorolamobility.preflighting.core.exception.PreflightingCheckerException;
+import com.motorolamobility.preflighting.core.internal.cond.utils.ConditionUtils;
+import com.motorolamobility.preflighting.core.utils.CheckerUtils;
+import com.motorolamobility.preflighting.core.utils.LayoutConstants;
+import com.motorolamobility.preflighting.core.utils.XmlUtils;
+import com.motorolamobility.preflighting.core.validation.ValidationManagerConfiguration;
+import com.motorolamobility.preflighting.core.validation.ValidationResult;
+import com.motorolamobility.preflighting.core.validation.ValidationResultData;
+
+/**
+ * Layout Checker condition that verifies if an id is declared in more than one component on a given layout file.
+ */
+public class RepeatedIdCondition extends Condition implements ICondition
+{
+
+ private ValidationManagerConfiguration valManagerConfig;
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorolamobility.preflighting.core.checker.condition.Condition#canExecute(com.motorolamobility.preflighting.core.applicationdata.ApplicationData, java.util.List)
+ */
+ @Override
+ public CanExecuteConditionStatus canExecute(ApplicationData data,
+ List<DeviceSpecification> deviceSpecs) throws PreflightingCheckerException
+ {
+ //All situations already handled by Checker
+ CanExecuteConditionStatus status =
+ new CanExecuteConditionStatus(IStatus.OK, CheckerPlugin.PLUGIN_ID, ""); //$NON-NLS-1$
+ status.setConditionId(getId());
+ return status;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorolamobility.preflighting.core.checker.condition.Condition#execute(com.motorolamobility.preflighting.core.applicationdata.ApplicationData, java.util.List, com.motorolamobility.preflighting.core.devicespecification.PlatformRules, com.motorolamobility.preflighting.core.validation.ValidationManagerConfiguration, com.motorolamobility.preflighting.core.validation.ValidationResult)
+ */
+ @Override
+ public void execute(ApplicationData data, List<DeviceSpecification> deviceSpecs,
+ ValidationManagerConfiguration valManagerConfig, ValidationResult results)
+ throws PreflightingCheckerException
+ {
+ List<XMLElement> layoutList = data.getLayoutElements();
+ if (layoutList != null)
+ {
+ this.valManagerConfig = valManagerConfig;
+
+ //Iterate on all layout elements
+ for (XMLElement layoutElement : layoutList)
+ {
+ HashSet<String> idsList = new HashSet<String>();
+ Document layoutDocument = layoutElement.getDocument();
+ Element docElement = layoutDocument.getDocumentElement();
+
+ searchRepeatedIds(results, layoutElement, idsList, docElement);
+ }
+ }
+ }
+
+ /*
+ * This method takes an XML element as input and recursively tries to locate repeated ids
+ * In case an repeated id is detected the results are updated with a issue.
+ */
+ private void searchRepeatedIds(ValidationResult results, XMLElement xmlElement,
+ HashSet<String> idsList, Node elementNode)
+ {
+ //Check if the node has an ID, if yes, verifies if it was not previously found (already present on idList)
+ NamedNodeMap map = elementNode.getAttributes();
+ Node attribute = map.getNamedItem(LayoutConstants.ANDROID_ID_ATTRIBUTE);
+ if (attribute != null)
+ {
+ String id = attribute.getTextContent();
+ id = CheckerUtils.getIdValue(id.trim());
+ if (id.length() > 0)
+ {
+ if (idsList.contains(id))
+ {
+ File layoutFile = xmlElement.getFile();
+ String layoutName = layoutFile.getName();
+ String descriptionMsg =
+ NLS.bind(CheckerNLS.RepeatedIdCondition_Result_Description, layoutName,
+ id);
+ String quickFixSuggestion =
+ NLS.bind(CheckerNLS.RepeatedIdCondition_Result_QuickFix, id, layoutName);
+ ValidationResultData resultData =
+ new ValidationResultData(null, getSeverityLevel(), descriptionMsg,
+ quickFixSuggestion, getId());
+ int lineNumber = xmlElement.getNodeLineNumber(elementNode);
+ resultData.addFileToIssueLines(layoutFile,
+ lineNumber >= 0 ? Arrays.asList(lineNumber) : new ArrayList<Integer>());
+ resultData.setPreview(XmlUtils.getXMLNodeAsString(elementNode, false));
+ resultData.setInfoURL(ConditionUtils.getDescriptionLink(getChecker().getId(),
+ getId(), valManagerConfig));
+ results.addValidationResult(resultData);
+ }
+ else
+ {
+ idsList.add(id);
+ }
+ }
+ }
+
+ //visit children
+ NodeList nodeList = elementNode.getChildNodes();
+ for (int i = 0; i < nodeList.getLength(); i++)
+ {
+ Node node = nodeList.item(i);
+ if (node.getNodeType() == Node.ELEMENT_NODE)
+ {
+ searchRepeatedIds(results, xmlElement, idsList, node);
+ }
+ }
+ }
+}
diff --git a/src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/layout/ViewTypeIdsCondition.java b/src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/layout/ViewTypeIdsCondition.java
new file mode 100644
index 0000000..b8c2df5
--- /dev/null
+++ b/src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/layout/ViewTypeIdsCondition.java
@@ -0,0 +1,208 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorolamobility.preflighting.checkers.layout;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.osgi.util.NLS;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import com.motorolamobility.preflighting.checkers.CheckerPlugin;
+import com.motorolamobility.preflighting.checkers.i18n.CheckerNLS;
+import com.motorolamobility.preflighting.core.applicationdata.ApplicationData;
+import com.motorolamobility.preflighting.core.applicationdata.XMLElement;
+import com.motorolamobility.preflighting.core.checker.condition.CanExecuteConditionStatus;
+import com.motorolamobility.preflighting.core.checker.condition.Condition;
+import com.motorolamobility.preflighting.core.checker.condition.ICondition;
+import com.motorolamobility.preflighting.core.devicespecification.DeviceSpecification;
+import com.motorolamobility.preflighting.core.exception.PreflightingCheckerException;
+import com.motorolamobility.preflighting.core.internal.cond.utils.ConditionUtils;
+import com.motorolamobility.preflighting.core.utils.CheckerUtils;
+import com.motorolamobility.preflighting.core.utils.LayoutConstants;
+import com.motorolamobility.preflighting.core.utils.XmlUtils;
+import com.motorolamobility.preflighting.core.validation.ValidationManagerConfiguration;
+import com.motorolamobility.preflighting.core.validation.ValidationResult;
+import com.motorolamobility.preflighting.core.validation.ValidationResultData;
+
+/**
+ * Layout checker condition that verifies if an ID is used for the same kind of view on all configurations of a given layout
+ */
+public class ViewTypeIdsCondition extends Condition implements ICondition
+{
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorolamobility.preflighting.core.checker.condition.Condition#canExecute(com.motorolamobility.preflighting.core.applicationdata.ApplicationData, java.util.List)
+ */
+ @Override
+ public CanExecuteConditionStatus canExecute(ApplicationData data,
+ List<DeviceSpecification> deviceSpecs) throws PreflightingCheckerException
+ {
+ //All situations already handled by Checker
+ CanExecuteConditionStatus status =
+ new CanExecuteConditionStatus(IStatus.OK, CheckerPlugin.PLUGIN_ID, ""); //$NON-NLS-1$
+ status.setConditionId(getId());
+ return status;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorolamobility.preflighting.core.checker.condition.Condition#execute(com.motorolamobility.preflighting.core.applicationdata.ApplicationData, java.util.List, com.motorolamobility.preflighting.core.devicespecification.PlatformRules, com.motorolamobility.preflighting.core.validation.ValidationManagerConfiguration, com.motorolamobility.preflighting.core.validation.ValidationResult)
+ *
+ */
+ @Override
+ public void execute(ApplicationData data, List<DeviceSpecification> deviceSpecs,
+ ValidationManagerConfiguration valManagerConfig, ValidationResult results)
+ throws PreflightingCheckerException
+ {
+
+ List<XMLElement> layoutList = data.getLayoutElements();
+
+ //Map that lists all LayoutFilesIds for a given layout. Key is a layoutfile name, values is a list containing all layoutfiles that represents all
+ //configurations for that layout
+ Map<String, List<LayoutFileId>> allIdsMap =
+ new HashMap<String, List<LayoutFileId>>(layoutList.size());
+ Map<File, XMLElement> xmlElementMap = new HashMap<File, XMLElement>(layoutList.size());
+
+ //Mount allIdsMap
+ collectNeededData(layoutList, allIdsMap, xmlElementMap);
+
+ //Iterate over all layouts, analyzing all configurations
+ Set<String> layouts = allIdsMap.keySet();
+ for (String layout : layouts)
+ {
+ Map<String, String> idViewTypeMap = new HashMap<String, String>(); //Key=id, value=View type of the first occurrence
+ List<LayoutFileId> layoutFileIds = allIdsMap.get(layout);
+ for (LayoutFileId layoutFileId : layoutFileIds)
+ {
+ analyzeLayoutFileId(valManagerConfig, results, xmlElementMap, idViewTypeMap,
+ layoutFileId);
+ }
+ }
+
+ }
+
+ private void analyzeLayoutFileId(ValidationManagerConfiguration valManagerConfig,
+ ValidationResult results, Map<File, XMLElement> xmlElementMap,
+ Map<String, String> idViewTypeMap, LayoutFileId layoutFileId)
+ {
+ Map<Node, String> nodeMap = layoutFileId.getNodeIdMap();
+ for (Node viewNode : nodeMap.keySet())
+ {
+ String nodeId = nodeMap.get(viewNode);
+ String viewType = viewNode.getNodeName();
+ if (idViewTypeMap.containsKey(nodeId)) //Verify if this id was previouslly found.
+ {
+ String mapViewType = idViewTypeMap.get(nodeId);
+ if (!mapViewType.equals(viewType)) //Different types found for the same id, issue found!
+ {
+ ValidationResultData resultData =
+ getIssueResultData(valManagerConfig, xmlElementMap, layoutFileId,
+ viewNode, nodeId, viewType, mapViewType);
+ results.addValidationResult(resultData);
+ }
+ }
+ else
+ //First time for this ID, put this on the map!
+ {
+ idViewTypeMap.put(nodeId, viewType);
+ }
+ }
+ }
+
+ private ValidationResultData getIssueResultData(
+ ValidationManagerConfiguration valManagerConfig, Map<File, XMLElement> xmlElementMap,
+ LayoutFileId layoutFileId, Node viewNode, String nodeId, String viewType,
+ String mapViewType)
+ {
+ ValidationResultData resultData =
+ new ValidationResultData(null, getSeverityLevel(), NLS.bind(
+ CheckerNLS.ViewTypeIdsCondition_Results_Description, new String[]
+ {
+ nodeId, viewType, mapViewType
+ }), CheckerNLS.ViewTypeIdsCondition_Results_QuickFix, getId());
+ File layoutFile = layoutFileId.getLayoutFile();
+ int lineNumber = xmlElementMap.get(layoutFile).getNodeLineNumber(viewNode);
+ resultData.addFileToIssueLines(layoutFile, lineNumber >= 0 ? Arrays.asList(lineNumber)
+ : new ArrayList<Integer>());
+ resultData.setPreview(XmlUtils.getXMLNodeAsString(viewNode, false));
+ resultData.setInfoURL(ConditionUtils.getDescriptionLink(getChecker().getId(), getId(),
+ valManagerConfig));
+ return resultData;
+ }
+
+ /*
+ * Fill all needed information in order to do the verification.
+ * Retrieves all ids from all layout files as well as its xml declaring nodes.
+ */
+ private void collectNeededData(List<XMLElement> layoutList,
+ Map<String, List<LayoutFileId>> allIdsMap, Map<File, XMLElement> xmlElementMap)
+ {
+ for (XMLElement element : layoutList)
+ {
+ File layoutFile = element.getFile();
+ xmlElementMap.put(layoutFile, element);
+ LayoutFileId layoutFileId = new LayoutFileId(layoutFile);
+ getLayoutIds(element, element.getDocument().getDocumentElement(), layoutFileId);
+ List<LayoutFileId> layoutFileList;
+ if (allIdsMap.containsKey(layoutFile.getName()))
+ {
+ layoutFileList = allIdsMap.get(layoutFile.getName());
+ }
+ else
+ {
+ layoutFileList = new ArrayList<LayoutFileId>();
+ }
+ layoutFileList.add(layoutFileId);
+ allIdsMap.put(layoutFile.getName(), layoutFileList);
+ }
+ }
+
+ private void getLayoutIds(XMLElement xmlElement, Node elementNode, LayoutFileId layoutFileId)
+ {
+ NamedNodeMap map = elementNode.getAttributes();
+ Node attribute = map.getNamedItem(LayoutConstants.ANDROID_ID_ATTRIBUTE);
+ if (attribute != null)
+ {
+ String id = attribute.getTextContent();
+ id = CheckerUtils.getIdValue(id.trim());
+ if (id.length() > 0)
+ {
+ layoutFileId.addId(elementNode, id);
+ }
+ }
+
+ //visit children
+ NodeList nodeList = elementNode.getChildNodes();
+ for (int i = 0; i < nodeList.getLength(); i++)
+ {
+ Node node = nodeList.item(i);
+ if (node.getNodeType() == Node.ELEMENT_NODE)
+ {
+ getLayoutIds(xmlElement, node, layoutFileId);
+ }
+ }
+ }
+}
diff --git a/src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/layout/XlargeConfigCondition.java b/src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/layout/XlargeConfigCondition.java
new file mode 100644
index 0000000..6cfa0be
--- /dev/null
+++ b/src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/layout/XlargeConfigCondition.java
@@ -0,0 +1,235 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorolamobility.preflighting.checkers.layout;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.osgi.util.NLS;
+import org.w3c.dom.Document;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import com.motorolamobility.preflighting.checkers.CheckerPlugin;
+import com.motorolamobility.preflighting.checkers.i18n.CheckerNLS;
+import com.motorolamobility.preflighting.core.applicationdata.ApplicationData;
+import com.motorolamobility.preflighting.core.applicationdata.XMLElement;
+import com.motorolamobility.preflighting.core.checker.condition.CanExecuteConditionStatus;
+import com.motorolamobility.preflighting.core.checker.condition.Condition;
+import com.motorolamobility.preflighting.core.checker.condition.ICondition;
+import com.motorolamobility.preflighting.core.devicespecification.DeviceSpecification;
+import com.motorolamobility.preflighting.core.exception.PreflightingCheckerException;
+import com.motorolamobility.preflighting.core.internal.cond.utils.ConditionUtils;
+import com.motorolamobility.preflighting.core.utils.CheckerUtils;
+import com.motorolamobility.preflighting.core.utils.ManifestConstants;
+import com.motorolamobility.preflighting.core.validation.ValidationManagerConfiguration;
+import com.motorolamobility.preflighting.core.validation.ValidationResult;
+import com.motorolamobility.preflighting.core.validation.ValidationResultData;
+
+/**
+ * This Checker verifies if a there's any layout defined without an xlarge screen support
+ */
+public class XlargeConfigCondition extends Condition implements ICondition
+{
+
+ private static final int MIN_SDK_VERSION = 4;
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorolamobility.preflighting.core.checker.condition.Condition#canExecute(com.motorolamobility.preflighting.core.applicationdata.ApplicationData, java.util.List)
+ */
+ @Override
+ public CanExecuteConditionStatus canExecute(ApplicationData data,
+ List<DeviceSpecification> deviceSpecs) throws PreflightingCheckerException
+ {
+
+ CanExecuteConditionStatus status =
+ new CanExecuteConditionStatus(IStatus.OK, CheckerPlugin.PLUGIN_ID, ""); //$NON-NLS-1$
+
+ XMLElement manElem = data.getManifestElement();
+ if (manElem == null)
+ {
+ status =
+ new CanExecuteConditionStatus(IStatus.ERROR, CheckerPlugin.PLUGIN_ID,
+ CheckerNLS.Invalid_ManifestFile);
+ }
+ else
+ {
+ Document manifestDoc = manElem.getDocument();
+
+ if (manifestDoc == null)
+ {
+ status =
+ new CanExecuteConditionStatus(IStatus.ERROR, CheckerPlugin.PLUGIN_ID,
+ CheckerNLS.Invalid_ManifestFile);
+ }
+ }
+
+ status.setConditionId(getId());
+ return status;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorolamobility.preflighting.core.checker.condition.Condition#execute(com.motorolamobility.preflighting.core.applicationdata.ApplicationData, java.util.List, com.motorolamobility.preflighting.core.devicespecification.PlatformRules, com.motorolamobility.preflighting.core.validation.ValidationManagerConfiguration, com.motorolamobility.preflighting.core.validation.ValidationResult)
+ */
+ @Override
+ public void execute(ApplicationData data, List<DeviceSpecification> deviceSpecs,
+ ValidationManagerConfiguration valManagerConfig, ValidationResult results)
+ throws PreflightingCheckerException
+ {
+
+ if (supportsXLargeScreens(data))
+ {
+ List<XMLElement> layoutElements = data.getLayoutElements();
+ List<File> defaultLayoutFiles = new ArrayList<File>(layoutElements.size());
+ List<String> xLargeLayoutFileNames = new ArrayList<String>(layoutElements.size());
+ List<File> missingXLargeList = new ArrayList<File>();
+
+ for (XMLElement xmlElement : layoutElements)
+ {
+ File layoutFile = xmlElement.getFile();
+ String parentFolder = layoutFile.getParentFile().getName();
+ if (isDefaultLayout(parentFolder))
+ {
+ defaultLayoutFiles.add(layoutFile);
+ }
+ else if (isXlarge(parentFolder))
+ {
+ xLargeLayoutFileNames.add(layoutFile.getName());
+ }
+ }
+
+ //Retrieve the Layouts that does not have xlarge configuration
+ for (File defaultLayout : defaultLayoutFiles)
+ {
+ if (!xLargeLayoutFileNames.contains(defaultLayout.getName()))
+ {
+ missingXLargeList.add(defaultLayout);
+ }
+ }
+
+ fillResults(valManagerConfig, missingXLargeList, results);
+ }
+ }
+
+ /*
+ * Verifies if the app supports xlarge screens, based on the manifest file data
+ */
+ private boolean supportsXLargeScreens(ApplicationData data)
+ {
+ XMLElement manifestElement = data.getManifestElement();
+ Document manifestDoc = manifestElement.getDocument();
+ NodeList supportsScreenNodes =
+ manifestDoc.getElementsByTagName(ManifestConstants.SUPPORTS_SCREEN_TAG);
+
+ boolean supportsXlarge = true;
+ if (supportsScreenNodes.getLength() > 0)
+ {
+ Node supportsScreenNode = supportsScreenNodes.item(0); //Get the first occurrence.
+ NamedNodeMap map = supportsScreenNode.getAttributes();
+ Node xLargeNode = map.getNamedItem(ManifestConstants.XLARGE_SCREENS_ATTRIBUTE);
+ if (xLargeNode != null)
+ {
+ String xLargeScreenNodeValue = xLargeNode.getNodeValue();
+ try
+ {
+ supportsXlarge = Boolean.parseBoolean(xLargeScreenNodeValue);
+ }
+ catch (Exception e)
+ {
+ //Do Nothing. Value will assumed to be true.
+ }
+ }
+ }
+
+ try
+ {
+ String minSdkStr = CheckerUtils.getMinSdk(manifestDoc);
+ int minSdkVersion = -1;
+ try
+ {
+ minSdkVersion = Integer.parseInt(minSdkStr);
+ }
+ catch (NumberFormatException e)
+ {
+ minSdkVersion = -1; //Min Sdk is a String, it's a preview SDK, we'll not be able to handle this.
+ }
+
+ String targetSdkStr = CheckerUtils.getTargetSdk(manifestDoc);
+ int targetSdk = -1;
+ try
+ {
+ targetSdk = Integer.parseInt(targetSdkStr);
+ }
+ catch (NumberFormatException e)
+ {
+ targetSdk = -1; //Target Sdk is a String, it's a preview SDK, we'll not be able to handle this.
+ }
+
+ if (((minSdkVersion > 0) && (minSdkVersion < MIN_SDK_VERSION))
+ || ((targetSdk > 0) && (targetSdk < MIN_SDK_VERSION)))
+ {
+ supportsXlarge = false;
+ }
+ }
+ catch (NumberFormatException e)
+ {
+ supportsXlarge = true; //Let's assume that all preview SDKs supports xlargeScreens.
+ }
+ return supportsXlarge;
+ }
+
+ /*
+ * Fill a result data for every layout file that is in the missingXLargeList
+ */
+ private void fillResults(ValidationManagerConfiguration valManagerConfig,
+ List<File> missingXLargeList, ValidationResult results)
+ {
+ for (File xlargeMissingFile : missingXLargeList)
+ {
+ ValidationResultData resultData =
+ new ValidationResultData(null, getSeverityLevel(), NLS.bind(
+ CheckerNLS.XlargeConfigCondition_Result_Description,
+ xlargeMissingFile.getName()), NLS.bind(
+ CheckerNLS.XlargeConfigCondition_Result_QuickFix,
+ xlargeMissingFile.getName()), getId());
+ resultData.setInfoURL(ConditionUtils.getDescriptionLink(getChecker().getId(), getId(),
+ valManagerConfig));
+ resultData.addFileToIssueLines(xlargeMissingFile, new ArrayList<Integer>(0));
+ results.addValidationResult(resultData);
+ }
+ }
+
+ /*
+ * Checks if a folder contains the xlarge suffix or not
+ */
+ private boolean isXlarge(String parentFolder)
+ {
+ return parentFolder.contains("xlarge"); //$NON-NLS-1$
+ }
+
+ /*
+ * Checks if a folder is the default layout folder
+ */
+ private boolean isDefaultLayout(String parentFolder)
+ {
+ return parentFolder.equalsIgnoreCase("layout"); //$NON-NLS-1$
+ }
+}
diff --git a/src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/localizationStrings/LocalizationStringsChecker.java b/src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/localizationStrings/LocalizationStringsChecker.java
new file mode 100644
index 0000000..8a2d5ee
--- /dev/null
+++ b/src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/localizationStrings/LocalizationStringsChecker.java
@@ -0,0 +1,333 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+package com.motorolamobility.preflighting.checkers.localizationStrings;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+
+import com.motorolamobility.preflighting.checkers.CheckerPlugin;
+import com.motorolamobility.preflighting.checkers.i18n.CheckerNLS;
+import com.motorolamobility.preflighting.core.applicationdata.ApplicationData;
+import com.motorolamobility.preflighting.core.applicationdata.Element;
+import com.motorolamobility.preflighting.core.applicationdata.Element.Type;
+import com.motorolamobility.preflighting.core.applicationdata.ElementUtils;
+import com.motorolamobility.preflighting.core.applicationdata.ResourcesFolderElement;
+import com.motorolamobility.preflighting.core.applicationdata.StringsElement;
+import com.motorolamobility.preflighting.core.checker.Checker;
+import com.motorolamobility.preflighting.core.checker.IChecker;
+import com.motorolamobility.preflighting.core.devicespecification.DeviceSpecification;
+import com.motorolamobility.preflighting.core.exception.PreflightingCheckerException;
+import com.motorolamobility.preflighting.core.validation.Parameter;
+import com.motorolamobility.preflighting.core.validation.ParameterDescription;
+import com.motorolamobility.preflighting.core.validation.ParameterType;
+import com.motorolamobility.preflighting.core.validation.Value;
+
+/**
+ * Localization Strings Checker implementation.
+ * This checker will look for missing strings in a given language strings.xml file that exist in other language files.
+ *
+ */
+public class LocalizationStringsChecker extends Checker implements IChecker
+{
+ // Parameter constants
+ public final String PARAMETER_DEFAULT_LOCALE = "defaultLocale"; //$NON-NLS-1$
+
+ private final String PARAMETER_DEFAULT_LOCALE_PATTERN = "[a-z]{2}(_[A-Z]{2})?"; //$NON-NLS-1$
+
+ /**
+ * Default locale parameter value
+ */
+ private String parameterDefaultLocaleValue = null;
+
+ /**
+ * Resources folder
+ */
+ private ResourcesFolderElement resFolder;
+
+ /**
+ * The string element for the default locale
+ */
+ private StringsElement stringsKeysDefault;
+
+ private Locale defaultLocale;
+
+ /**
+ * Check if resources and values folder exists, set the default locale and call its conditions canExecute method.
+ * @see com.motorola.preflighting.core.checker.IChecker#canExecute(com.motorola.preflighting.core.applicationdata.ApplicationData, java.util.List)
+ */
+ @Override
+ public IStatus canExecute(ApplicationData data, List<DeviceSpecification> deviceSpecs)
+ throws PreflightingCheckerException
+ {
+ //status of the checker
+ IStatus status = Status.OK_STATUS;
+
+ if (status.isOK())
+ {
+ status = setResourcesFolder(data);
+ }
+ if (status.isOK())
+ {
+ status = valuesFolderExists();
+ }
+ if (status.isOK())
+ {
+ status = setDefaultLocale();
+ }
+ if (status.isOK())
+ {
+ //the super implementation check its conditions status
+ status = super.canExecute(data, deviceSpecs);
+ }
+
+ return status;
+ }
+
+ /**
+ * Sets the default locale.
+ * @return IStatus.Error if default locale is empty or does not exist.
+ */
+ private IStatus setDefaultLocale()
+ {
+ IStatus status = Status.OK_STATUS;
+
+ defaultLocale = null;
+ // Check if we should use the default language or the user specified one as the default
+ if (parameterDefaultLocaleValue != null)
+ {
+ String[] splittedLocale = parameterDefaultLocaleValue.split("_"); //$NON-NLS-1$
+
+ if (splittedLocale.length > 1)
+ {
+ defaultLocale = new Locale(splittedLocale[0], splittedLocale[1]);
+ stringsKeysDefault = resFolder.getValuesElement(defaultLocale);
+ }
+ else
+ {
+ defaultLocale = new Locale(splittedLocale[0]);
+ stringsKeysDefault = resFolder.getValuesElement(defaultLocale);
+ }
+
+ // Check if the locale provided was found. If not, the test cannot execute
+ if ((stringsKeysDefault == null) || (stringsKeysDefault.getKeyList().size() < 1))
+ {
+ // Status
+ status =
+ new Status(
+ IStatus.ERROR,
+ CheckerPlugin.PLUGIN_ID,
+ CheckerNLS.LocalizationStringsChecker_detailedLocaleResourceNotFound);
+
+ }
+
+ }
+ else
+ {
+ // Retrieve the default language string and string array keys
+ stringsKeysDefault = resFolder.getDefaultValuesElement();
+ }
+ return status;
+ }
+
+ /**
+ * @param checkerStatus
+ * @return
+ */
+ private IStatus valuesFolderExists()
+ {
+ IStatus checkerStatus = Status.OK_STATUS;
+
+ // Check if at least one "values" folder exist with a appropriate "strings.xml" file
+ int numberOfFoundValuesResources = 0;
+ for (Element e : resFolder.getChildren())
+ {
+ if (e.getType() == Element.Type.FOLDER_VALUES)
+ {
+ for (Element children : e.getChildren())
+ {
+ if (children.getType() == Element.Type.FILE_STRINGS)
+ {
+ numberOfFoundValuesResources++;
+ }
+ }
+ }
+ }
+
+ if (numberOfFoundValuesResources == 0)
+ {
+ checkerStatus =
+ new Status(IStatus.ERROR, CheckerPlugin.PLUGIN_ID,
+ CheckerNLS.LocalizationStringsChecker_Missing_stringsXml_File);
+ }
+ return checkerStatus;
+ }
+
+ /**
+ * @param data
+ * @param checkerStatus
+ * @return
+ */
+ private IStatus setResourcesFolder(ApplicationData data)
+ {
+ IStatus status = Status.OK_STATUS;
+
+ // Check for the existence of a \res folder
+ List<Element> folderResElements =
+ ElementUtils.getElementByType(data.getRootElement(), Type.FOLDER_RES);
+
+ this.resFolder =
+ folderResElements.size() > 0 ? (ResourcesFolderElement) folderResElements.get(0)
+ : null;
+
+ if (resFolder == null)
+ {
+ status =
+ new Status(IStatus.ERROR, CheckerPlugin.PLUGIN_ID,
+ CheckerNLS.Missing_res_folder);
+ }
+ return status;
+ }
+
+ /**
+ * Check if default locale was changed via parameter and properly sets it.
+ * @see com.motorola.preflighting.core.checker.AbstractChecker#validateInputParams(java.util.List)
+ */
+ @Override
+ public IStatus validateInputParams(List<Parameter> parameters)
+ {
+ IStatus status = Status.OK_STATUS;
+ parameterDefaultLocaleValue = null;
+
+ // Iterate through the list of parameters and check for problems
+ if (parameters.size() > 0)
+ {
+
+ for (Parameter p : parameters)
+ {
+
+ if (p.getParameterType().equals(PARAMETER_DEFAULT_LOCALE))
+ {
+ // Check if parameter is valid
+ if (p.getValue().matches(PARAMETER_DEFAULT_LOCALE_PATTERN))
+ {
+ parameterDefaultLocaleValue = p.getValue();
+ }
+ else
+ {
+ status =
+ new Status(
+ Status.ERROR,
+ CheckerPlugin.PLUGIN_ID,
+ CheckerNLS.LocalizationStringsChecker_invalidDefaultLocaleParameter);
+
+ break;
+ }
+ }
+ else
+ {
+ // Invalid parameters
+ status =
+ new Status(Status.ERROR, CheckerPlugin.PLUGIN_ID, CheckerNLS.bind(
+ CheckerNLS.LocalizationStringsChecker_invalidParameters,
+ p.getParameterType()));
+
+ break;
+ }
+ }
+ }
+
+ return status;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorolamobility.preflighting.core.checker.AbstractChecker#getParams()
+ */
+ @Override
+ public List<ParameterDescription> getParameterDescriptions()
+ {
+ // Return this cheker parameters
+
+ List<ParameterDescription> returnList = new ArrayList<ParameterDescription>();
+
+ /*
+ * "defaultLocale" parameter
+ */
+ Value valueLocale = new Value();
+ valueLocale.setValue("ll_CC"); //$NON-NLS-1$
+ valueLocale
+ .setDescription(CheckerNLS.LocalizationStringsChecker_helpParameterLocaleValueFormatDescription);
+
+ List<Value> allowedValuesLocale = new ArrayList<Value>();
+ allowedValuesLocale.add(valueLocale);
+
+ ParameterDescription descriptionLocale = new ParameterDescription();
+ descriptionLocale.setName(PARAMETER_DEFAULT_LOCALE);
+ descriptionLocale.setType(ParameterType.STRING);
+ descriptionLocale.setDefaultValue(valueLocale);
+ descriptionLocale.setAllowedValues(allowedValuesLocale);
+ descriptionLocale.setValueRequired(true);
+ descriptionLocale
+ .setDescription(CheckerNLS.LocalizationStringsChecker_helpParameterLocaleDescription);
+ descriptionLocale
+ .setValueDescription(CheckerNLS.LocalizationStringsChecker_helpParameterLocaleValueDescription);
+
+ returnList.add(descriptionLocale);
+
+ return returnList;
+ }
+
+ @Override
+ public void clean()
+ {
+
+ resFolder = null;
+ if (stringsKeysDefault != null)
+ {
+ stringsKeysDefault.clean();
+ }
+ stringsKeysDefault = null;
+ defaultLocale = null;
+ }
+
+ /**
+ * @return the resFolder
+ */
+ public ResourcesFolderElement getResourcesFolder()
+ {
+ return resFolder;
+ }
+
+ /**
+ * @return the stringsKeysDefault
+ */
+ public StringsElement getStringsKeysDefault()
+ {
+ return stringsKeysDefault;
+ }
+
+ /**
+ * @return the defaultLocale
+ */
+ public Locale getDefaultLocale()
+ {
+ return defaultLocale;
+ }
+}
diff --git a/src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/localizationStrings/MissingDefaultLanguageKeyCondition.java b/src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/localizationStrings/MissingDefaultLanguageKeyCondition.java
new file mode 100644
index 0000000..d35ac2b
--- /dev/null
+++ b/src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/localizationStrings/MissingDefaultLanguageKeyCondition.java
@@ -0,0 +1,219 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+package com.motorolamobility.preflighting.checkers.localizationStrings;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Locale;
+
+import org.eclipse.core.runtime.IStatus;
+
+import com.motorolamobility.preflighting.checkers.CheckerPlugin;
+import com.motorolamobility.preflighting.checkers.i18n.CheckerNLS;
+import com.motorolamobility.preflighting.core.applicationdata.ApplicationData;
+import com.motorolamobility.preflighting.core.applicationdata.ResourcesFolderElement;
+import com.motorolamobility.preflighting.core.applicationdata.StringsElement;
+import com.motorolamobility.preflighting.core.checker.condition.CanExecuteConditionStatus;
+import com.motorolamobility.preflighting.core.checker.condition.Condition;
+import com.motorolamobility.preflighting.core.checker.condition.ICondition;
+import com.motorolamobility.preflighting.core.devicespecification.DeviceSpecification;
+import com.motorolamobility.preflighting.core.exception.PreflightingCheckerException;
+import com.motorolamobility.preflighting.core.internal.cond.utils.ConditionUtils;
+import com.motorolamobility.preflighting.core.validation.ValidationManagerConfiguration;
+import com.motorolamobility.preflighting.core.validation.ValidationResult;
+import com.motorolamobility.preflighting.core.validation.ValidationResultData;
+
+/**
+ * Check if keys present in other languages and missing in the default language.
+ */
+public class MissingDefaultLanguageKeyCondition extends Condition implements ICondition
+{
+ private ValidationManagerConfiguration valManagerConfig;
+
+ private LocalizationStringsChecker checker;
+
+ private ResourcesFolderElement resFolder;
+
+ /**
+ * The string element for the default locale
+ */
+ private StringsElement stringsKeysDefault;
+
+ private Locale defaultLocale;
+
+ /**
+ * Check if there are default and alternate locales.
+ * @see com.motorolamobility.preflighting.core.checker.condition.Condition#canExecute(com.motorolamobility.preflighting.core.applicationdata.ApplicationData, java.util.List)
+ */
+ @Override
+ public CanExecuteConditionStatus canExecute(ApplicationData data,
+ List<DeviceSpecification> deviceSpecs) throws PreflightingCheckerException
+ {
+ CanExecuteConditionStatus status;
+
+ this.checker = (LocalizationStringsChecker) getChecker();
+ this.resFolder = checker.getResourcesFolder();
+ this.stringsKeysDefault = checker.getStringsKeysDefault();
+
+ List<Locale> availableLocales = resFolder.getAvailableLocales();
+
+ //check if default or non-default locales exist
+ if ((stringsKeysDefault != null) && (stringsKeysDefault.getKeyList() != null)
+ && (availableLocales != null) && (availableLocales.size() > 0))
+ {
+ //there is both default and non-default locales
+ status = new CanExecuteConditionStatus(IStatus.OK, CheckerPlugin.PLUGIN_ID, ""); //$NON-NLS-1$
+ status.setConditionId(getId());
+ }
+ else if (((stringsKeysDefault == null) || (stringsKeysDefault.getKeyList() == null))
+ && (availableLocales != null) && (availableLocales.size() > 0))
+ {
+ //there is no default locale set
+ status =
+ new CanExecuteConditionStatus(
+ IStatus.ERROR,
+ CheckerPlugin.PLUGIN_ID,
+ CheckerNLS
+ .bind(CheckerNLS.LocalizationStringsChecker_conditionMissingKey_CouldNotBeRun_NoDefault,
+ getId()));
+ status.setConditionId(getId());
+ }
+ else if (((stringsKeysDefault != null) && (stringsKeysDefault.getKeyList() != null) && (stringsKeysDefault
+ .getKeyList().size() > 0))
+ && ((availableLocales == null) || (availableLocales.size() < 1)))
+ {
+ //there is no non-default locales
+ status =
+ new CanExecuteConditionStatus(
+ IStatus.ERROR,
+ CheckerPlugin.PLUGIN_ID,
+ CheckerNLS
+ .bind(CheckerNLS.LocalizationStringsChecker_conditionMissingKey_CouldNotBeRun_NoLocale,
+ getId()));
+ status.setConditionId(getId());
+ }
+ else
+ {
+ //unknown error
+ status =
+ new CanExecuteConditionStatus(IStatus.ERROR, CheckerPlugin.PLUGIN_ID,
+ CheckerNLS.bind(CheckerNLS.LocalizationStringsChecker_UnknownError,
+ getId()));
+ status.setConditionId(getId());
+ }
+
+ return status;
+ }
+
+ /**
+ * Check if alternate locales define keys that are not defined in the default locale.
+ * @see com.motorolamobility.preflighting.core.checker.condition.Condition#execute(com.motorolamobility.preflighting.core.applicationdata.ApplicationData, java.util.List, com.motorolamobility.preflighting.core.validation.ValidationManagerConfiguration, com.motorolamobility.preflighting.core.validation.ValidationResult)
+ */
+ @Override
+ public void execute(ApplicationData data, List<DeviceSpecification> deviceSpecs,
+ ValidationManagerConfiguration valManagerConfig, ValidationResult results)
+ throws PreflightingCheckerException
+ {
+ this.valManagerConfig = valManagerConfig;
+ this.defaultLocale = checker.getDefaultLocale();
+
+ /*
+ * Check for problems related to the default localization files
+ */
+
+ // Check for missing keys
+ for (Locale locale : resFolder.getAvailableLocales())
+ {
+ if (!locale.equals(defaultLocale))
+ {
+ // Check for keys present in the locales that are missing in the default language
+ checkForMissingDefaultKeys(locale, results);
+ }
+ }
+
+ }
+
+ /**
+ * Auxiliary method to check for missing locale keys in the default localization resource
+ * @param locale - The locale to be validated.
+ * @return A validation result
+ * @throws PreflightingCheckerException
+ */
+ private void checkForMissingDefaultKeys(Locale locale, ValidationResult results)
+ throws PreflightingCheckerException
+ {
+
+ try
+ {
+ StringsElement localeStringsElement = resFolder.getValuesElement(locale);
+
+ if (localeStringsElement != null)
+ {
+ // Builders to construct the description and quickfix
+ String resultDescription;
+
+ for (String s : localeStringsElement.getKeyList())
+ {
+ if (!stringsKeysDefault.containsKey(s))
+ {
+ ValidationResultData result = new ValidationResultData();
+
+ result.setSeverity(getSeverityLevel());
+ result.setConditionID(getId());
+
+ String localeDisplayName = null;
+ if ((locale.getCountry() != null) && (locale.getCountry().length() > 0))
+ {
+ localeDisplayName = locale.getLanguage() + "_" + locale.getCountry(); //$NON-NLS-1$
+ }
+ else
+ {
+ localeDisplayName = locale.getLanguage();
+ }
+
+ // Construct description
+ resultDescription =
+ CheckerNLS
+ .bind(CheckerNLS.LocalizationStringsChecker_localeStringNotFoundSimple,
+ s, localeDisplayName);
+
+ result.setIssueDescription(resultDescription.toString());
+
+ //Associate the res folder to the issue
+ result.addFileToIssueLines(resFolder.getFile(),
+ Collections.<Integer> emptyList());
+
+ // Set fix suggestion
+ result.setQuickFixSuggestion(CheckerNLS.LocalizationStringsChecker_addStringToLocalizationResourceDetailed);
+ result.setInfoURL(ConditionUtils.getDescriptionLink(checker.getId(),
+ getId(), valManagerConfig));
+
+ //Add the result to the list
+ results.addValidationResult(result);
+ }
+ }
+
+ }
+
+ }
+ catch (Exception e)
+ {
+ throw new PreflightingCheckerException(
+ CheckerNLS.LocalizationStringsChecker_Exception_MissingLocaleKeys, e);
+ }
+ }
+}
diff --git a/src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/localizationStrings/MissingLanguageKeyCondition.java b/src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/localizationStrings/MissingLanguageKeyCondition.java
new file mode 100644
index 0000000..6baaa46
--- /dev/null
+++ b/src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/localizationStrings/MissingLanguageKeyCondition.java
@@ -0,0 +1,224 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+package com.motorolamobility.preflighting.checkers.localizationStrings;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Locale;
+
+import org.eclipse.core.runtime.IStatus;
+
+import com.motorolamobility.preflighting.checkers.CheckerPlugin;
+import com.motorolamobility.preflighting.checkers.i18n.CheckerNLS;
+import com.motorolamobility.preflighting.core.applicationdata.ApplicationData;
+import com.motorolamobility.preflighting.core.applicationdata.ResourcesFolderElement;
+import com.motorolamobility.preflighting.core.applicationdata.StringsElement;
+import com.motorolamobility.preflighting.core.checker.condition.CanExecuteConditionStatus;
+import com.motorolamobility.preflighting.core.checker.condition.Condition;
+import com.motorolamobility.preflighting.core.checker.condition.ICondition;
+import com.motorolamobility.preflighting.core.devicespecification.DeviceSpecification;
+import com.motorolamobility.preflighting.core.exception.PreflightingCheckerException;
+import com.motorolamobility.preflighting.core.internal.cond.utils.ConditionUtils;
+import com.motorolamobility.preflighting.core.validation.ValidationManagerConfiguration;
+import com.motorolamobility.preflighting.core.validation.ValidationResult;
+import com.motorolamobility.preflighting.core.validation.ValidationResultData;
+
+/**
+ * Check if keys present in the default language are missing in non-default languages.
+ *
+ */
+public class MissingLanguageKeyCondition extends Condition implements ICondition
+{
+ private LocalizationStringsChecker checker;
+
+ private ValidationManagerConfiguration valManagerConfig;
+
+ private ResourcesFolderElement resFolder;
+
+ private Locale defaultLocale;
+
+ /**
+ * The string element for the default locale
+ */
+ StringsElement stringsKeysDefault;
+
+ /* (non-Javadoc)
+ * @see com.motorolamobility.preflighting.core.checker.condition.Condition#canExecute(com.motorolamobility.preflighting.core.applicationdata.ApplicationData, java.util.List)
+ */
+ @Override
+ public CanExecuteConditionStatus canExecute(ApplicationData data,
+ List<DeviceSpecification> deviceSpecs) throws PreflightingCheckerException
+ {
+ CanExecuteConditionStatus status;
+
+ this.checker = (LocalizationStringsChecker) getChecker();
+ this.resFolder = checker.getResourcesFolder();
+ this.stringsKeysDefault = checker.getStringsKeysDefault();
+
+ List<Locale> availableLocales = resFolder.getAvailableLocales();
+
+ //check if default or non-default locales exist
+ if ((stringsKeysDefault != null) && (stringsKeysDefault.getKeyList() != null)
+ && (availableLocales != null) && (availableLocales.size() > 0))
+ {
+ //there is both default and non-default locales
+ status = new CanExecuteConditionStatus(IStatus.OK, CheckerPlugin.PLUGIN_ID, ""); //$NON-NLS-1$
+ status.setConditionId(getId());
+ }
+ else if (((stringsKeysDefault == null) || (stringsKeysDefault.getKeyList() == null))
+ && (availableLocales != null) && (availableLocales.size() > 0))
+ {
+ //there is no default locale set
+ status =
+ new CanExecuteConditionStatus(
+ IStatus.ERROR,
+ CheckerPlugin.PLUGIN_ID,
+ CheckerNLS
+ .bind(CheckerNLS.LocalizationStringsChecker_conditionMissingKey_CouldNotBeRun_NoDefault,
+ getId()));
+ status.setConditionId(getId());
+ }
+ else if (((stringsKeysDefault != null) && (stringsKeysDefault.getKeyList() != null) && (stringsKeysDefault
+ .getKeyList().size() > 0))
+ && ((availableLocales == null) || (availableLocales.size() < 1)))
+ {
+ //there is no non-default locales
+ status =
+ new CanExecuteConditionStatus(
+ IStatus.ERROR,
+ CheckerPlugin.PLUGIN_ID,
+ CheckerNLS
+ .bind(CheckerNLS.LocalizationStringsChecker_conditionMissingKey_CouldNotBeRun_NoLocale,
+ getId()));
+ status.setConditionId(getId());
+ }
+ else
+ {
+ //unknow error
+ status =
+ new CanExecuteConditionStatus(IStatus.ERROR, CheckerPlugin.PLUGIN_ID,
+ CheckerNLS.bind(CheckerNLS.LocalizationStringsChecker_UnknownError,
+ getId()));
+ status.setConditionId(getId());
+ }
+
+ return status;
+ }
+
+ /**
+ * Check if there are keys defined in the default locale that are not defined in alternate locales.
+ * @see com.motorolamobility.preflighting.core.checker.condition.Condition#execute(com.motorolamobility.preflighting.core.applicationdata.ApplicationData, java.util.List, com.motorolamobility.preflighting.core.validation.ValidationManagerConfiguration, com.motorolamobility.preflighting.core.validation.ValidationResult)
+ */
+ @Override
+ public void execute(ApplicationData data, List<DeviceSpecification> deviceSpecs,
+ ValidationManagerConfiguration valManagerConfig, ValidationResult results)
+ throws PreflightingCheckerException
+ {
+ this.valManagerConfig = valManagerConfig;
+ this.defaultLocale = checker.getDefaultLocale();
+
+ /*
+ * Check for problems related to the non-default localization files
+ */
+ for (Locale l : resFolder.getAvailableLocales())
+ {
+ if (!l.equals(defaultLocale))
+ {
+ // Check for keys present in the default language that are missing from locales
+ checkForMissingLocaleKeys(l, results);
+ }
+ }
+ }
+
+ /**
+ * Auxiliary method to check for missing default keys for a given locale
+ * @param locale - The locale to be validated.
+ * @param results
+ * @return A validation result.
+ * @throws PreflightingCheckerException
+ */
+ private void checkForMissingLocaleKeys(Locale locale, ValidationResult results)
+ throws PreflightingCheckerException
+ {
+
+ try
+ {
+ StringsElement localeStringsElement = resFolder.getValuesElement(locale);
+
+ if (localeStringsElement != null)
+ {
+ // Construct a list of missing keys
+ List<String> missingKeys = new ArrayList<String>();
+
+ for (String s : stringsKeysDefault.getKeyList())
+ {
+ if (!localeStringsElement.containsKey(s))
+ {
+ missingKeys.add(s);
+ }
+ }
+
+ // If there are missing keys, issue an warning listing them all
+ if (missingKeys.size() > 0)
+ {
+ ValidationResultData result;
+
+ for (String key : missingKeys)
+ {
+ result = new ValidationResultData();
+ result.setSeverity(getSeverityLevel());
+
+ result.setConditionID(getId());
+
+ //Associate the result to the res folder
+ result.addFileToIssueLines(resFolder.getFile(),
+ Collections.<Integer> emptyList());
+
+ String localeDisplayName = null;
+ if ((locale.getCountry() != null) && (locale.getCountry().length() > 0))
+ {
+ localeDisplayName = locale.getLanguage() + "_" + locale.getCountry(); //$NON-NLS-1$
+ }
+ else
+ {
+ localeDisplayName = locale.getLanguage();
+ }
+
+ // Set description
+ result.setIssueDescription(CheckerNLS.bind(
+ CheckerNLS.LocalizationStringsChecker_defaultStringNotFoundSimple,
+ key, localeDisplayName));
+
+ // Set quickfix
+ result.setQuickFixSuggestion(CheckerNLS.LocalizationStringsChecker_addStringToLocalizationResourceDetailed);
+ result.setInfoURL(ConditionUtils.getDescriptionLink(checker.getId(),
+ getId(), valManagerConfig));
+ //Add to result list
+ results.addValidationResult(result);
+ }
+ }
+ }
+
+ }
+ catch (Exception e)
+ {
+ throw new PreflightingCheckerException(
+ CheckerNLS.LocalizationStringsChecker_Exception_MissingDefaultKeys, e);
+ }
+ }
+}
diff --git a/src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/localizationStrings/MissingValueCondition.java b/src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/localizationStrings/MissingValueCondition.java
new file mode 100644
index 0000000..380373b
--- /dev/null
+++ b/src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/localizationStrings/MissingValueCondition.java
@@ -0,0 +1,314 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+package com.motorolamobility.preflighting.checkers.localizationStrings;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Locale;
+
+import org.eclipse.core.runtime.IStatus;
+
+import com.motorolamobility.preflighting.checkers.CheckerPlugin;
+import com.motorolamobility.preflighting.checkers.i18n.CheckerNLS;
+import com.motorolamobility.preflighting.core.applicationdata.ApplicationData;
+import com.motorolamobility.preflighting.core.applicationdata.ResourcesFolderElement;
+import com.motorolamobility.preflighting.core.applicationdata.StringsElement;
+import com.motorolamobility.preflighting.core.checker.condition.CanExecuteConditionStatus;
+import com.motorolamobility.preflighting.core.checker.condition.Condition;
+import com.motorolamobility.preflighting.core.checker.condition.ICondition;
+import com.motorolamobility.preflighting.core.devicespecification.DeviceSpecification;
+import com.motorolamobility.preflighting.core.exception.PreflightingCheckerException;
+import com.motorolamobility.preflighting.core.internal.cond.utils.ConditionUtils;
+import com.motorolamobility.preflighting.core.validation.ValidationManagerConfiguration;
+import com.motorolamobility.preflighting.core.validation.ValidationResult;
+import com.motorolamobility.preflighting.core.validation.ValidationResultData;
+
+/**
+ * Check if there are keys with empty values.
+ *
+ */
+public class MissingValueCondition extends Condition implements ICondition
+{
+ private LocalizationStringsChecker checker;
+
+ private ValidationManagerConfiguration valManagerConfig;
+
+ private ResourcesFolderElement resFolder;
+
+ private Locale defaultLocale;
+
+ /**
+ * The string element for the default locale
+ */
+ StringsElement stringsKeysDefault;
+
+ /**
+ * Check if there is at least one string key defined in default or alternate locales.
+ * @see com.motorolamobility.preflighting.core.checker.condition.Condition#canExecute(com.motorolamobility.preflighting.core.applicationdata.ApplicationData, java.util.List)
+ */
+ @Override
+ public CanExecuteConditionStatus canExecute(ApplicationData data,
+ List<DeviceSpecification> deviceSpecs) throws PreflightingCheckerException
+ {
+ CanExecuteConditionStatus status;
+
+ this.checker = (LocalizationStringsChecker) getChecker();
+ this.resFolder = checker.getResourcesFolder();
+ this.stringsKeysDefault = checker.getStringsKeysDefault();
+
+ List<Locale> availableLocales = resFolder.getAvailableLocales();
+
+ //check if there is at least one key in default or non-default locales
+ if ((stringsKeysDefault != null) && (stringsKeysDefault.getKeyList() != null)
+ && (stringsKeysDefault.getKeyList().size() > 0))
+ {
+ //at least one string key found at stringsKeyDefault
+ status = new CanExecuteConditionStatus(IStatus.OK, CheckerPlugin.PLUGIN_ID, ""); //$NON-NLS-1$
+ status.setConditionId(getId());
+ }
+ else if ((availableLocales != null) && (availableLocales.size() > 0))
+ {
+ //default locale has no string key defined
+ //look for string keys in non-default languages
+ boolean nonDefaultLanguageStringKeyFound = false;
+
+ for (Locale locale : availableLocales)
+ {
+ if (resFolder.getValuesElement(locale).getKeyList().size() > 0)
+ {
+ nonDefaultLanguageStringKeyFound = true;
+ break;
+ }
+ }
+
+ if (nonDefaultLanguageStringKeyFound)
+ {
+ //at least one string key found in non default languages (availableLocales)
+ status = new CanExecuteConditionStatus(IStatus.OK, CheckerPlugin.PLUGIN_ID, ""); //$NON-NLS-1$
+ status.setConditionId(getId());
+ }
+ else
+ {
+ //both default and non-default localization files are empty
+ status =
+ new CanExecuteConditionStatus(
+ IStatus.ERROR,
+ CheckerPlugin.PLUGIN_ID,
+ CheckerNLS
+ .bind(CheckerNLS.LocalizationStringsChecker_conditionMissingValue_CouldNotBeRun_EmptyLocalizationFiles,
+ getId()));
+ status.setConditionId(getId());
+ }
+ }
+ else
+ {
+ //default localization file is empty and there is no non-default locale set
+ status =
+ new CanExecuteConditionStatus(
+ IStatus.ERROR,
+ CheckerPlugin.PLUGIN_ID,
+ CheckerNLS
+ .bind(CheckerNLS.LocalizationStringsChecker_conditionMissingValue_CouldNotBeRun_EmptyLocalizationFiles,
+ getId()));
+ status.setConditionId(getId());
+ }
+
+ return status;
+ }
+
+ /**
+ * Check if there are keys with empty values in default and alternate locales.
+ * @see com.motorolamobility.preflighting.core.checker.condition.Condition#execute(com.motorolamobility.preflighting.core.applicationdata.ApplicationData, java.util.List, com.motorolamobility.preflighting.core.validation.ValidationManagerConfiguration, com.motorolamobility.preflighting.core.validation.ValidationResult)
+ */
+ @Override
+ public void execute(ApplicationData data, List<DeviceSpecification> deviceSpecs,
+ ValidationManagerConfiguration valManagerConfig, ValidationResult results)
+ throws PreflightingCheckerException
+ {
+ this.valManagerConfig = valManagerConfig;
+ this.defaultLocale = checker.getDefaultLocale();
+
+ // Check for keys with empty values - First the default localization and then the locales
+ checkForEmptyValues(null, results);
+
+ /*
+ * Check for problems related to the non-default localization files
+ */
+ for (Locale l : resFolder.getAvailableLocales())
+ {
+ if (!l.equals(defaultLocale))
+ {
+ // Check for keys with empty values
+ checkForEmptyValues(l, results);
+ }
+ }
+ }
+
+ /**
+ * Auxiliary method to check for keys with empty values.
+ * @param locale - The locale to be validated. If null, the method will validate the default localization resource
+ * @param results
+ * @return A validation result
+ * @throws PreflightingCheckerException
+ */
+ private void checkForEmptyValues(Locale locale, ValidationResult results)
+ throws PreflightingCheckerException
+ {
+ // Strings element to be validated. Can either be from the default localization resource or a locale one.
+ StringsElement localeStringsElement;
+
+ // Construct a list of missing keys
+ List<String> keysWithEmptyValues = new ArrayList<String>();
+
+ if (locale != null)
+ {
+ localeStringsElement = resFolder.getValuesElement(locale);
+ }
+ else
+ {
+ // Validate default localization resource
+ localeStringsElement = stringsKeysDefault;
+ }
+
+ try
+ {
+ if (localeStringsElement != null)
+ {
+ // Find keys with empty values
+ keysWithEmptyValues = findKeysWithMissingValues(localeStringsElement);
+
+ ValidationResultData result;
+ for (String key : keysWithEmptyValues)
+ {
+ result = new ValidationResultData();
+ // Builders to construct the description and quickfix
+ String resultDescription;
+
+ // Create a result and return it
+ result.setSeverity(getSeverityLevel());
+
+ result.setConditionID(getId());
+
+ //Associate the result to the resFolder
+ result.addFileToIssueLines(resFolder.getFile(),
+ Collections.<Integer> emptyList());
+
+ // The result is different depending if we are validating the default localization files or not
+ if (locale != null)
+ {
+ String localeDisplayName = null;
+ if ((locale.getCountry() != null) && (locale.getCountry().length() > 0))
+ {
+ localeDisplayName = locale.getLanguage() + "_" + locale.getCountry(); //$NON-NLS-1$
+ }
+ else
+ {
+ localeDisplayName = locale.getLanguage();
+ }
+
+ // Construct description
+ resultDescription =
+ CheckerNLS
+ .bind(CheckerNLS.LocalizationStringsChecker_localeStringEmptyValue,
+ key, localeDisplayName);
+
+ result.setIssueDescription(resultDescription.toString());
+ }
+ else
+ {
+ //Set description
+ result.setIssueDescription(CheckerNLS.bind(
+ CheckerNLS.LocalizationStringsChecker_defaultStringEmptyValue, key));
+ }
+
+ // Set quickfix
+ result.setQuickFixSuggestion(CheckerNLS.LocalizationStringsChecker_stringEmptyValueQuickFix);
+ result.setInfoURL(ConditionUtils.getDescriptionLink(checker.getId(), getId(),
+ valManagerConfig));
+
+ // Add result to the result list
+ results.addValidationResult(result);
+ }
+ }
+
+ }
+ catch (Exception e)
+ {
+ String exceptionMessage;
+ if (locale == null)
+ {
+ exceptionMessage =
+ CheckerNLS.LocalizationStringsChecker_Exception_EmptyValuesDefault;
+ }
+ else
+ {
+ exceptionMessage =
+ CheckerNLS.LocalizationStringsChecker_Exception_EmptyValuesLocale;
+ }
+ throw new PreflightingCheckerException(exceptionMessage, e);
+ }
+ }
+
+ /**
+ * Auxiliary method to look for empty values in a StringsElement
+ * @param localeStringsElement - A strings element representing a localization resource.
+ * @return A list containing keys with empty values.
+ */
+ private List<String> findKeysWithMissingValues(StringsElement localeStringsElement)
+ {
+
+ List<String> missingValues = new ArrayList<String>();
+
+ if (localeStringsElement != null)
+ {
+ for (String key : localeStringsElement.getKeyList())
+ {
+ // Check value associated with the key
+ if (localeStringsElement.getValue(key) != null)
+ {
+ Object value = localeStringsElement.getValue(key);
+
+ // The value can either be a String or a List<String>.
+ if (value instanceof String)
+ {
+ if (((String) value).length() < 1)
+ {
+ // Empty value found!
+ missingValues.add(key);
+ }
+ }
+ else if (value instanceof List<?>)
+ {
+ if (((List<?>) value).size() < 1)
+ {
+ // Empty value found!
+ missingValues.add(key);
+ }
+ }
+ }
+ else
+ {
+ missingValues.add(key);
+ }
+
+ }
+ }
+
+ return missingValues;
+ }
+}
diff --git a/src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/logic/OpenedCursorsCondition.java b/src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/logic/OpenedCursorsCondition.java
new file mode 100644
index 0000000..b600068
--- /dev/null
+++ b/src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/logic/OpenedCursorsCondition.java
@@ -0,0 +1,291 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorolamobility.preflighting.checkers.logic;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import com.motorolamobility.preflighting.checkers.i18n.CheckerNLS;
+import com.motorolamobility.preflighting.core.applicationdata.ApplicationData;
+import com.motorolamobility.preflighting.core.applicationdata.SourceFolderElement;
+import com.motorolamobility.preflighting.core.checker.condition.CanExecuteConditionStatus;
+import com.motorolamobility.preflighting.core.checker.condition.Condition;
+import com.motorolamobility.preflighting.core.checker.condition.ICondition;
+import com.motorolamobility.preflighting.core.devicespecification.DeviceSpecification;
+import com.motorolamobility.preflighting.core.exception.PreflightingCheckerException;
+import com.motorolamobility.preflighting.core.internal.cond.utils.ConditionUtils;
+import com.motorolamobility.preflighting.core.logging.PreflightingLogger;
+import com.motorolamobility.preflighting.core.source.model.Instruction;
+import com.motorolamobility.preflighting.core.source.model.Invoke;
+import com.motorolamobility.preflighting.core.source.model.Method;
+import com.motorolamobility.preflighting.core.source.model.SourceFileElement;
+import com.motorolamobility.preflighting.core.source.model.Variable;
+import com.motorolamobility.preflighting.core.utils.CheckerUtils;
+import com.motorolamobility.preflighting.core.validation.ValidationManagerConfiguration;
+import com.motorolamobility.preflighting.core.validation.ValidationResult;
+import com.motorolamobility.preflighting.core.validation.ValidationResultData;
+
+/**
+ * This condition verifies if all opened db cursors are closed within a method
+ */
+public class OpenedCursorsCondition extends Condition implements ICondition
+{
+
+ private static final String DATABASE_CURSOR_QUALIFIED_NAME = "android.database.Cursor"; //$NON-NLS-1$
+
+ private boolean isProject;
+
+ /*
+ * Exclude managed cursors
+ */
+ private static final String ACTIVITY_QUALIFIED_NAME = "android.app.Activity";
+
+ private static final String START_MANAGING_CURSOR_METHOD_NAME = ".startManagingCursor";
+
+ private static final String MANAGED_QUERY_METHOD_NAME = ".managedQuery";
+
+ // close method
+ private static final String CLOSE_METHOD_NAME = ".close"; //$NON-NLS-1$
+
+ @Override
+ public CanExecuteConditionStatus canExecute(ApplicationData data,
+ List<DeviceSpecification> deviceSpecs) throws PreflightingCheckerException
+ {
+ return CheckerUtils.isJavaModelComplete(data, getId());
+ }
+
+ @Override
+ public void execute(ApplicationData data, List<DeviceSpecification> deviceSpecs,
+ ValidationManagerConfiguration valManagerConfig, ValidationResult results)
+ throws PreflightingCheckerException
+ {
+ this.isProject = data.isProject();
+ List<SourceFolderElement> sourceFolderElements = data.getJavaModel();
+ for (SourceFolderElement sourceFolder : sourceFolderElements)
+ {
+ List<SourceFileElement> sourceFiles = sourceFolder.getSourceFileElements();
+ for (SourceFileElement sourceFile : sourceFiles)
+ {
+ //Look on virtual methods
+ List<Method> methods = sourceFile.getVirtualMethods();
+ analizeMethods(valManagerConfig, results, sourceFile, methods);
+
+ //Look on direct methods
+ methods = sourceFile.getDirectMethods();
+ analizeMethods(valManagerConfig, results, sourceFile, methods);
+ }
+ }
+ }
+
+ private void analizeMethods(ValidationManagerConfiguration valManagerConfig,
+ ValidationResult results, SourceFileElement sourceFile, List<Method> methods)
+ {
+ for (Method method : methods)
+ {
+ List<Instruction> instructions = method.getInstructions();
+ //Get opened cursors map, variable x num. times it was assigned a new cursor without being closed
+ Map<Variable, Integer> openedCursors =
+ getOpenedCursors(method, sourceFile, instructions);
+
+ //For each opened cursor fill a validation result
+ for (Variable variable : openedCursors.keySet())
+ {
+ int openedCursor = openedCursors.get(variable);
+ if (openedCursor > 0)
+ {
+ addValidationResult(results, valManagerConfig, sourceFile, method, variable,
+ openedCursors);
+ }
+ }
+ }
+ }
+
+ private Map<Variable, Integer> getOpenedCursors(Method method, SourceFileElement sourceFile,
+ List<Instruction> instructions)
+ {
+ Map<Variable, Integer> openedCursors = new HashMap<Variable, Integer>();
+ for (Instruction instruction : instructions)
+ {
+ if (instruction instanceof Invoke)
+ {
+ Invoke calledMethod = (Invoke) instruction;
+
+ if ((calledMethod.getReturnType() != null)
+ && (calledMethod.getQualifiedName() != null))
+ {
+ /*
+ * NOTE: There is a difference between the Java Model for Projects and APKs
+ *
+ * For Projects, when an inherit method is called, it's qualified name contains the
+ * class which actually contains the method.
+ *
+ * For APKs, this do not occur, and the qualified name contains the name of the
+ * class itself.
+ *
+ * Ex: calling android.app.Activity.startManagingCursor
+ * - For Projects: qualified name = android.app.Activity.startManagingCursor
+ * - For APKs: qualified name = <the user activity>.startManagingCursor
+ */
+
+ /*
+ * Opening cursors
+ */
+ if ((calledMethod.getReturnType() != null)
+ && (calledMethod.getReturnType().equals(DATABASE_CURSOR_QUALIFIED_NAME))
+ && (!calledMethod.getQualifiedName().endsWith(
+ ((isProject) ? ACTIVITY_QUALIFIED_NAME : sourceFile
+ .getClassFullPath()) + MANAGED_QUERY_METHOD_NAME)))
+ {
+ // Opened cursor found, increment counter
+ Variable variable = getAssignedVariable(method, calledMethod);
+ Integer counter = null;
+ if (variable != null)
+ {
+ if (openedCursors.containsKey(variable))
+ {
+ counter = openedCursors.get(variable);
+ counter++;
+ }
+ else
+ {
+ counter = 1;
+ }
+ openedCursors.put(variable, counter);
+ }
+ }
+
+ /*
+ * Closing cursors
+ */
+ String objectName = null;
+ //close() found
+ if (calledMethod.getQualifiedName().equals(
+ DATABASE_CURSOR_QUALIFIED_NAME + CLOSE_METHOD_NAME))
+ {
+
+ objectName = calledMethod.getObjectName();
+ }
+ // startManagingCursos(cursor) found - disregard this cursor
+ else if (calledMethod.getQualifiedName().endsWith(
+ ((isProject) ? ACTIVITY_QUALIFIED_NAME : sourceFile.getClassFullPath())
+ + START_MANAGING_CURSOR_METHOD_NAME))
+ {
+ List<String> paramNames = calledMethod.getParameterNames();
+ if ((paramNames != null) && (paramNames.size() > 0))
+ {
+ objectName = paramNames.get(0);
+ }
+ }
+
+ // decrement the counter
+ if (objectName != null)
+ {
+ Variable key = getKey(openedCursors, objectName);
+ Integer counter = null;
+ if (openedCursors.containsKey(key))
+ {
+ counter = openedCursors.get(key);
+ counter--;
+ openedCursors.put(key, counter);
+ }
+ else
+ {
+ //Don't do anything. for some reason a close was called on a non local variable.
+ //In the future add support for parameters and fields
+ }
+ }
+ }
+ else
+ {
+ PreflightingLogger
+ .error("Could not retrieve qualified name or return type for method: "
+ + calledMethod);
+ }
+ }
+
+ }
+ return openedCursors;
+ }
+
+ /*
+ * Search within openedCursors keySet for a variable with name that is equal to objectName
+ */
+ private Variable getKey(Map<Variable, Integer> openedCursors, String objectName)
+ {
+ Variable key = null;
+ Iterator<Variable> it = openedCursors.keySet().iterator();
+
+ while ((key == null) && it.hasNext())
+ {
+ Variable variable = it.next();
+ if (variable.getName().equals(objectName))
+ {
+ key = variable;
+ }
+ }
+
+ return key;
+ }
+
+ /*
+ * Retrieve the Variable assigned with the return of calledMethod, if any.
+ */
+ private Variable getAssignedVariable(Method method, Invoke calledMethod)
+ {
+ String assignedVariable = calledMethod.getAssignedVariable();
+ List<Variable> variables = method.getVariables();
+ Variable variable = null;
+ Iterator<Variable> it = variables.iterator();
+ while ((variable == null) && it.hasNext())
+ {
+ Variable listVar = it.next();
+ if (listVar.getName().equals(assignedVariable))
+ {
+ variable = listVar;
+ }
+ }
+
+ return variable;
+ }
+
+ private void addValidationResult(ValidationResult results,
+ ValidationManagerConfiguration valManagerConfig, SourceFileElement sourceFile,
+ Method method, Variable variable, Map<Variable, Integer> openedCursors)
+ {
+ ValidationResultData resultData = new ValidationResultData();
+ resultData.setConditionID(getId());
+
+ resultData.addFileToIssueLines(sourceFile.getFile(), Arrays.asList(new Integer[]
+ {
+ variable.getLineNumber()
+ }));
+
+ resultData.setIssueDescription(CheckerNLS.bind(
+ CheckerNLS.OpenedCursorsCondition_Result_Message, new String[]
+ {
+ variable.getName(), sourceFile.getClassFullPath() + "." //$NON-NLS-1$
+ + method.getMethodName(), openedCursors.get(variable).toString()
+ }));
+ resultData.setQuickFixSuggestion(CheckerNLS.OpenedCursorsCondition_Result_QuickFix);
+ resultData.setSeverity(getSeverityLevel());
+ resultData.setInfoURL(ConditionUtils.getDescriptionLink(getChecker().getId(), getId(),
+ valManagerConfig));
+ results.addValidationResult(resultData);
+ }
+}
diff --git a/src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/mainactivity/SingleMainActivityCondition.java b/src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/mainactivity/SingleMainActivityCondition.java
new file mode 100644
index 0000000..12357d0
--- /dev/null
+++ b/src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/mainactivity/SingleMainActivityCondition.java
@@ -0,0 +1,270 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+package com.motorolamobility.preflighting.checkers.mainactivity;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import org.eclipse.core.runtime.IStatus;
+import org.w3c.dom.DOMException;
+import org.w3c.dom.Document;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import com.motorolamobility.preflighting.checkers.CheckerPlugin;
+import com.motorolamobility.preflighting.checkers.i18n.CheckerNLS;
+import com.motorolamobility.preflighting.core.applicationdata.ApplicationData;
+import com.motorolamobility.preflighting.core.applicationdata.XMLElement;
+import com.motorolamobility.preflighting.core.checker.condition.CanExecuteConditionStatus;
+import com.motorolamobility.preflighting.core.checker.condition.Condition;
+import com.motorolamobility.preflighting.core.checker.condition.ICondition;
+import com.motorolamobility.preflighting.core.devicespecification.DeviceSpecification;
+import com.motorolamobility.preflighting.core.exception.PreflightingCheckerException;
+import com.motorolamobility.preflighting.core.internal.cond.utils.ConditionUtils;
+import com.motorolamobility.preflighting.core.validation.ValidationManagerConfiguration;
+import com.motorolamobility.preflighting.core.validation.ValidationResult;
+import com.motorolamobility.preflighting.core.validation.ValidationResultData;
+
+/**
+ * Condition to check if the AndroidManifest has exactly one main activity declared.
+ *
+ */
+public class SingleMainActivityCondition extends Condition implements ICondition
+{
+ /**
+ * Identifier of category LAUNCHER.
+ */
+ private static final String ANDROID_INTENT_CATEGORY_LAUNCHER =
+ "android.intent.category.LAUNCHER";
+
+ /**
+ * Identifier of CATEGORY element.
+ */
+ private static final String CATEGORY_TAG = "category";
+
+ /**
+ * Identifier of action MAIN.
+ */
+ private static final String ANDROID_INTENT_ACTION_MAIN = "android.intent.action.MAIN";
+
+ /**
+ * Identifier of the android:name attribute.
+ */
+ private static final String ANDROID_NAME_ATTR = "android:name";
+
+ /**
+ * Identifier of the ACTION element.
+ */
+ private static final String ACTION_TAG = "action";
+
+ /**
+ * Identifier of the INTENT_FILTER element.
+ */
+ private static final String INTENT_FILTER_TAG = "intent-filter";
+
+ /**
+ * Identifier of the ACTIVITY element.
+ */
+ private static final String ACTIVITY_TAG = "activity";
+
+ private ValidationManagerConfiguration valManagerConfig;
+
+ private List<Integer> issuedLinesList = null;
+
+ private XMLElement manElement;
+
+ private static final String MAIN_ACTION_NODE =
+ "<action android:name=\"android.intent.action.MAIN\"/>";
+
+ /**
+ * Check that AndroidManifet exists and has a manifest node.
+ * @see com.motorolamobility.preflighting.core.checker.condition.Condition#canExecute(com.motorolamobility.preflighting.core.applicationdata.ApplicationData, java.util.List)
+ */
+ @Override
+ public CanExecuteConditionStatus canExecute(ApplicationData data,
+ List<DeviceSpecification> deviceSpecs) throws PreflightingCheckerException
+ {
+ CanExecuteConditionStatus status =
+ new CanExecuteConditionStatus(IStatus.OK, CheckerPlugin.PLUGIN_ID, "");
+ status.setConditionId(getId());
+
+ XMLElement manElem = data.getManifestElement();
+ if (manElem == null)
+ {
+ status =
+ new CanExecuteConditionStatus(IStatus.ERROR, CheckerPlugin.PLUGIN_ID,
+ CheckerNLS.Invalid_ManifestFile);
+ }
+ else
+ {
+ Document manifestDoc = manElem.getDocument();
+
+ if (manifestDoc == null)
+ {
+ status =
+ new CanExecuteConditionStatus(IStatus.ERROR, CheckerPlugin.PLUGIN_ID,
+ CheckerNLS.Invalid_ManifestFile);
+ }
+ }
+ return status;
+ }
+
+ /**
+ * Check if there is exactly one main activity declared on AndroidManifest.
+ * @see com.motorolamobility.preflighting.core.checker.condition.Condition#execute(com.motorolamobility.preflighting.core.applicationdata.ApplicationData, java.util.List, com.motorolamobility.preflighting.core.validation.ValidationManagerConfiguration, com.motorolamobility.preflighting.core.validation.ValidationResult)
+ */
+ @Override
+ public void execute(ApplicationData data, List<DeviceSpecification> deviceSpecs,
+ ValidationManagerConfiguration valManagerConfig, ValidationResult results)
+ throws PreflightingCheckerException
+ {
+ int mainActivityCount = 0;
+
+ this.valManagerConfig = valManagerConfig;
+ issuedLinesList = new ArrayList<Integer>();
+ manElement = data.getManifestElement();
+ Document manifestDoc = manElement.getDocument();
+
+ // get activity nodes
+ NodeList actLst = manifestDoc.getElementsByTagName(ACTIVITY_TAG); //$NON-NLS-1$
+
+ for (int i = 0; i < actLst.getLength(); i++)
+ {
+ NodeList intentFilterLst = actLst.item(i).getChildNodes();
+ for (int j = 0; j < intentFilterLst.getLength(); j++)
+ {
+ int currentIssuedLine = 0;
+
+ boolean actionFound = false;
+ boolean categoryFound = false;
+
+ Node intentFilterNode = intentFilterLst.item(j);
+ // get intent-filter nodes
+ if (intentFilterNode.getNodeName().equals(INTENT_FILTER_TAG)) //$NON-NLS-1$
+ {
+ NodeList actionLst = intentFilterNode.getChildNodes();
+ for (int k = 0; k < actionLst.getLength(); k++)
+ {
+ Node actionNode = actionLst.item(k);
+ // get action nodes
+ if (actionNode.getNodeName().equals(ACTION_TAG)) //$NON-NLS-1$
+ {
+ NamedNodeMap map = actionNode.getAttributes();
+ // name attribute must be set to
+ // android.intent.action.MAIN
+ Node nameAtr = map.getNamedItem(ANDROID_NAME_ATTR); //$NON-NLS-1$
+
+ try
+ {
+ if ((nameAtr != null)
+ && nameAtr.getNodeValue()
+ .equals(ANDROID_INTENT_ACTION_MAIN)) //$NON-NLS-1$
+ {
+ actionFound = true;
+ // NOTE: the action node position is issued
+ currentIssuedLine = manElement.getNodeLineNumber(actionNode);
+ }
+
+ }
+ catch (DOMException e)
+ {
+ // Error retrieving value of the action intent
+ throw new PreflightingCheckerException(
+ CheckerNLS.MainActivityChecker_Exception_Get_Action_Intent_Value,
+ e);
+ }
+ }
+ // get category nodes
+ else if (actionNode.getNodeName().equals(CATEGORY_TAG)) //$NON-NLS-1$
+ {
+ NamedNodeMap map = actionNode.getAttributes();
+ // name attribute must be set to
+ // android.intent.category.LAUNCHER
+ Node nameAtr = map.getNamedItem(ANDROID_NAME_ATTR); //$NON-NLS-1$
+ try
+ {
+ if ((nameAtr != null)
+ && nameAtr.getNodeValue().equals(
+ ANDROID_INTENT_CATEGORY_LAUNCHER)) //$NON-NLS-1$
+ {
+ categoryFound = true;
+ }
+
+ }
+ catch (DOMException e)
+ {
+ // Error retrieving value of the category intent
+ throw new PreflightingCheckerException(
+ CheckerNLS.MainActivityChecker_Exception_Get_Category_Intent_Value,
+ e);
+ }
+ }
+ }
+ // a main activity must have both category and action nodes
+ if (categoryFound && actionFound)
+ {
+ mainActivityCount++;
+ issuedLinesList.add(currentIssuedLine);
+ }
+ }
+ }
+ }
+
+ createValidationResult(mainActivityCount, results);
+ }
+
+ private void createValidationResult(int mainActivityCount, ValidationResult results)
+ {
+ ValidationResultData resultData = new ValidationResultData();
+
+ // one main activity - OK
+ if (mainActivityCount == 1)
+ {
+ resultData.setSeverity(ValidationResultData.SEVERITY.OK);
+ }
+ else
+ {
+ resultData.setSeverity(getSeverityLevel());
+ resultData.setConditionID(getId());
+ resultData.setInfoURL(ConditionUtils.getDescriptionLink(getChecker().getId(), getId(),
+ valManagerConfig));
+
+ // none main activity - WARNING
+ if (mainActivityCount == 0)
+ {
+ resultData.setIssueDescription(CheckerNLS.MainActivityChecker_noMainActivity);
+ resultData.addFileToIssueLines(manElement.getFile(),
+ Collections.<Integer> emptyList());
+ resultData
+ .setQuickFixSuggestion(CheckerNLS.MainActivityChecker_NoMainActivityFixSuggestion);
+ }
+ // more than one main activity - WARNING
+ else
+ {
+ resultData.setIssueDescription(CheckerNLS.MainActivityChecker_manyMainActivity);
+ resultData
+ .setQuickFixSuggestion(CheckerNLS.MainActivityChecker_MoreThanOneMainActivityFixSuggestion);
+ resultData.addFileToIssueLines(manElement.getFile(), issuedLinesList);
+ resultData.setPreview(MAIN_ACTION_NODE);
+ }
+ }
+ results.addValidationResult(resultData);
+ }
+
+}
diff --git a/src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/missingdrawable/AllDensitiesSupportCondition.java b/src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/missingdrawable/AllDensitiesSupportCondition.java
new file mode 100644
index 0000000..09495d7
--- /dev/null
+++ b/src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/missingdrawable/AllDensitiesSupportCondition.java
@@ -0,0 +1,101 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorolamobility.preflighting.checkers.missingdrawable;
+
+import java.util.Collections;
+import java.util.List;
+
+import org.eclipse.core.runtime.IStatus;
+
+import com.motorolamobility.preflighting.checkers.i18n.CheckerNLS;
+import com.motorolamobility.preflighting.core.PreflightingCorePlugin;
+import com.motorolamobility.preflighting.core.applicationdata.ApplicationData;
+import com.motorolamobility.preflighting.core.applicationdata.FolderElement;
+import com.motorolamobility.preflighting.core.checker.condition.CanExecuteConditionStatus;
+import com.motorolamobility.preflighting.core.checker.condition.Condition;
+import com.motorolamobility.preflighting.core.checker.condition.ICondition;
+import com.motorolamobility.preflighting.core.devicespecification.DeviceSpecification;
+import com.motorolamobility.preflighting.core.exception.PreflightingCheckerException;
+import com.motorolamobility.preflighting.core.internal.cond.utils.ConditionUtils;
+import com.motorolamobility.preflighting.core.validation.ValidationManagerConfiguration;
+import com.motorolamobility.preflighting.core.validation.ValidationResult;
+import com.motorolamobility.preflighting.core.validation.ValidationResultData;
+
+/**
+ * Verifies that the application's API level supports all referenced density values.
+ */
+public class AllDensitiesSupportCondition extends Condition implements ICondition
+{
+ private MissingDrawableData missingDrawableData;
+
+ /**
+ * @see com.motorolamobility.preflighting.core.checker.condition.Condition#execute(com.motorolamobility.preflighting.core.applicationdata.ApplicationData, java.util.List, com.motorolamobility.preflighting.core.validation.ValidationManagerConfiguration, com.motorolamobility.preflighting.core.validation.ValidationResult)
+ */
+ @Override
+ public void execute(ApplicationData data, List<DeviceSpecification> deviceSpecs,
+ ValidationManagerConfiguration valManagerConfig, ValidationResult results)
+ throws PreflightingCheckerException
+ {
+ // First we need to check if this test is valid at all.
+ // If the minSdkVersion is 3 (or lower) or the <supports-screen> tag does not exit, return an OK result.
+ MissingDrawableChecker checker;
+ if (getChecker() instanceof MissingDrawableChecker)
+ {
+ checker = (MissingDrawableChecker) getChecker();
+ missingDrawableData = checker.getMissingDrawableData();
+ if (missingDrawableData.isTestApplicable())
+ {
+ generateResults(results, valManagerConfig);
+ }
+ }
+ }
+
+ /**
+ * @see com.motorolamobility.preflighting.core.checker.condition.Condition#canExecute(com.motorolamobility.preflighting.core.applicationdata.ApplicationData, java.util.List)
+ */
+ @Override
+ public CanExecuteConditionStatus canExecute(ApplicationData data,
+ List<DeviceSpecification> deviceSpecs) throws PreflightingCheckerException
+ {
+ CanExecuteConditionStatus status =
+ new CanExecuteConditionStatus(IStatus.OK, PreflightingCorePlugin.PLUGIN_ID,
+ "", getId()); //$NON-NLS-1$
+ return status;
+ }
+
+ /**
+ * Auxiliary method to execute validation and generate the results
+ * @return A list of results
+ */
+ private void generateResults(ValidationResult results,
+ ValidationManagerConfiguration valManagerConfig)
+ {
+ if (missingDrawableData.isXhdpiFolderExists() && !missingDrawableData.isXhdpiApplicable())
+ {
+ FolderElement xhdpiFolder = missingDrawableData.getResFolder().getXhdpiDrawableFolder();
+ //creates a result
+ ValidationResultData result = new ValidationResultData();
+ result.setSeverity(getSeverityLevel());
+ result.setIssueDescription(CheckerNLS.MissingDrawableChecker_UselessXhdpiResources);
+ result.setQuickFixSuggestion(CheckerNLS.MissingDrawableChecker_UselessXhdpiResourcesSugestion);
+ result.addFileToIssueLines(xhdpiFolder.getFile(), Collections.<Integer> emptyList());
+ result.setConditionID(getId());
+ result.setInfoURL(ConditionUtils.getDescriptionLink(getChecker().getId(), getId(),
+ valManagerConfig));
+ results.addValidationResult(result);
+ }
+ }
+}
diff --git a/src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/missingdrawable/MissingDrawableChecker.java b/src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/missingdrawable/MissingDrawableChecker.java
new file mode 100644
index 0000000..9b72760
--- /dev/null
+++ b/src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/missingdrawable/MissingDrawableChecker.java
@@ -0,0 +1,127 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+package com.motorolamobility.preflighting.checkers.missingdrawable;
+
+import java.util.List;
+
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.w3c.dom.Document;
+
+import com.motorolamobility.preflighting.checkers.CheckerPlugin;
+import com.motorolamobility.preflighting.checkers.i18n.CheckerNLS;
+import com.motorolamobility.preflighting.core.applicationdata.ApplicationData;
+import com.motorolamobility.preflighting.core.applicationdata.Element;
+import com.motorolamobility.preflighting.core.applicationdata.Element.Type;
+import com.motorolamobility.preflighting.core.applicationdata.ElementUtils;
+import com.motorolamobility.preflighting.core.applicationdata.ResourcesFolderElement;
+import com.motorolamobility.preflighting.core.applicationdata.XMLElement;
+import com.motorolamobility.preflighting.core.checker.Checker;
+import com.motorolamobility.preflighting.core.devicespecification.DeviceSpecification;
+import com.motorolamobility.preflighting.core.exception.PreflightingCheckerException;
+import com.motorolamobility.preflighting.core.validation.ValidationManagerConfiguration;
+import com.motorolamobility.preflighting.core.validation.ValidationResult;
+
+/**
+ * Missing Drawable Checker class implementation
+ */
+public class MissingDrawableChecker extends Checker
+{
+ /**
+ * Information about the drawable folders into application
+ */
+ private MissingDrawableData missingDrawableData;
+
+ /*
+ * Output related constants
+ */
+ /* (non-Javadoc)
+ * @see com.motorolamobility.preflighting.core.checker.IChecker#validateApplication(com.motorolamobility.preflighting.core.applicationdata.ApplicationData, java.util.List)
+ */
+ @Override
+ public void validateApplication(ApplicationData data, List<DeviceSpecification> deviceSpecs,
+ ValidationManagerConfiguration valManagerConfig, ValidationResult results)
+ throws PreflightingCheckerException
+ {
+ this.missingDrawableData = new MissingDrawableData(data);
+ //run enabled conditions which belongs to the checker
+ super.validateApplication(data, deviceSpecs, valManagerConfig, results);
+ }
+
+ /**
+ * @return the missingDrawableData
+ */
+ protected MissingDrawableData getMissingDrawableData()
+ {
+ return missingDrawableData;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorolamobility.preflighting.core.checker.IChecker#canExecute(com.motorolamobility.preflighting.core.applicationdata.ApplicationData, java.util.List)
+ */
+ @Override
+ public IStatus canExecute(ApplicationData data, List<DeviceSpecification> deviceSpecs)
+ throws PreflightingCheckerException
+ {
+
+ IStatus status = Status.OK_STATUS;
+
+ //collect status from child condition
+ status = super.canExecute(data, deviceSpecs);
+
+ if (status.isOK())
+ {
+ // Check for a non-null document
+ XMLElement manElem = data.getManifestElement();
+
+ if (manElem == null)
+ {
+ status =
+ new Status(IStatus.ERROR, CheckerPlugin.PLUGIN_ID,
+ CheckerNLS.Invalid_ManifestFile);
+ }
+ else
+ {
+ Document manifestDoc = manElem.getDocument();
+ if (manifestDoc == null)
+ {
+ status =
+ new Status(IStatus.ERROR, CheckerPlugin.PLUGIN_ID,
+ CheckerNLS.Invalid_ManifestFile);
+ }
+
+ }
+
+ // Check for the existence of a \res folder
+ List<Element> folderResElements =
+ ElementUtils.getElementByType(data.getRootElement(), Type.FOLDER_RES);
+
+ ResourcesFolderElement resFolder =
+ folderResElements.size() > 0 ? (ResourcesFolderElement) folderResElements
+ .get(0) : null;
+
+ if (resFolder == null)
+ {
+ status =
+ new Status(IStatus.ERROR, CheckerPlugin.PLUGIN_ID,
+ CheckerNLS.Missing_res_folder);
+ }
+ }
+
+ return status;
+ }
+}
diff --git a/src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/missingdrawable/MissingDrawableData.java b/src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/missingdrawable/MissingDrawableData.java
new file mode 100644
index 0000000..6b75362
--- /dev/null
+++ b/src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/missingdrawable/MissingDrawableData.java
@@ -0,0 +1,304 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorolamobility.preflighting.checkers.missingdrawable;
+
+import java.util.HashSet;
+import java.util.List;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import com.motorolamobility.preflighting.core.applicationdata.ApplicationData;
+import com.motorolamobility.preflighting.core.applicationdata.Element;
+import com.motorolamobility.preflighting.core.applicationdata.Element.Type;
+import com.motorolamobility.preflighting.core.applicationdata.ElementUtils;
+import com.motorolamobility.preflighting.core.applicationdata.FolderElement;
+import com.motorolamobility.preflighting.core.applicationdata.ResourcesFolderElement;
+import com.motorolamobility.preflighting.core.applicationdata.XMLElement;
+
+/**
+ * Keeps information about the drawable folders into application
+ */
+public class MissingDrawableData
+{
+ /*
+ * Android manifest related constants
+ */
+ private final int MIN_SDK_THRESHOLD = 3;
+
+ private final int MAX_XHDPI_SDK_THRESHOLD = 9;
+
+ private final String TAG_NAME_USES_SDK = "uses-sdk"; //$NON-NLS-1$
+
+ private final String TAG_NAME_SUPPORTS_SCREENS = "supports-screens"; //$NON-NLS-1$
+
+ private final String ATTRIBUTE_ANY_DENSITY = "android:anyDensity"; //$NON-NLS-1$
+
+ private final String ATTRIBUTE_MIN_SDK_VERSION = "android:minSdkVersion"; //$NON-NLS-1$
+
+ private final String ATTRIBUTE_MAX_SDK_VERSION = "android:maxSdkVersion"; //$NON-NLS-1$
+
+ private boolean standardFolderExists = false;
+
+ private boolean ldpiFolderExists = false;
+
+ private boolean mdpiFolderExists = false;
+
+ private boolean hdpiFolderExists = false;
+
+ private boolean xhdpiFolderExists = false;
+
+ /**
+ * The application manifest
+ */
+ private XMLElement manifestElement;
+
+ /**
+ * xhdpi resolution, available for api level 9 or above
+ */
+ private boolean isXhdpiApplicable = true;
+
+ private ResourcesFolderElement resFolder;
+
+ //A list of all existing drawable elements names
+ private HashSet<String> drawableElements = new HashSet<String>();
+
+ private boolean testApplicable;
+
+ public MissingDrawableData(ApplicationData appData)
+ {
+ this.manifestElement = appData.getManifestElement();
+
+ // Retrieve all images declared in all drawable folders
+ List<Element> folderResElements =
+ ElementUtils.getElementByType(appData.getRootElement(), Type.FOLDER_RES);
+ resFolder =
+ folderResElements.size() > 0 ? (ResourcesFolderElement) folderResElements.get(0)
+ : null;
+ // Check if all 4 resolution folders and the standard folder exist
+ if (resFolder.getDrawableFolder() != null)
+ {
+ standardFolderExists = true;
+ }
+
+ // ldpi folder
+ if (resFolder.getLdpiDrawableFolder() != null)
+ {
+ ldpiFolderExists = true;
+ }
+
+ // mdpi folder
+ if (resFolder.getMdpiDrawableFolder() != null)
+ {
+ mdpiFolderExists = true;
+ }
+
+ // hdpi folder
+ if (resFolder.getHdpiDrawableFolder() != null)
+ {
+ hdpiFolderExists = true;
+ }
+
+ // hdpi folder
+ if (resFolder.getXhdpiDrawableFolder() != null)
+ {
+ xhdpiFolderExists = true;
+ }
+
+ // Construct a list of unique drawable elements
+ List<FolderElement> drawableFolders = resFolder.getDrawableFolders();
+ for (FolderElement f : drawableFolders)
+ {
+ for (Element e : ElementUtils.getElementByType(f, Type.FILE_DRAWABLE))
+ {
+ drawableElements.add(e.getName());
+ }
+ }
+
+ checkTestApplicability();
+ }
+
+ /**
+ * @return the resFolder
+ */
+ protected ResourcesFolderElement getResFolder()
+ {
+ return resFolder;
+ }
+
+ /**
+ * @return the standardFolderExists
+ */
+ protected boolean isStandardFolderExists()
+ {
+ return standardFolderExists;
+ }
+
+ /**
+ * @return the ldpiFolderExists
+ */
+ protected boolean isLdpiFolderExists()
+ {
+ return ldpiFolderExists;
+ }
+
+ /**
+ * @return the mdpiFolderExists
+ */
+ protected boolean isMdpiFolderExists()
+ {
+ return mdpiFolderExists;
+ }
+
+ /**
+ * @return the hdpiFolderExists
+ */
+ protected boolean isHdpiFolderExists()
+ {
+ return hdpiFolderExists;
+ }
+
+ /**
+ * @return the xhdpiFolderExists
+ */
+ protected boolean isXhdpiFolderExists()
+ {
+ return xhdpiFolderExists;
+ }
+
+ /**
+ * @return the isXhdpiApplicable
+ */
+ protected boolean isXhdpiApplicable()
+ {
+ return isXhdpiApplicable;
+ }
+
+ /**
+ * @return the drawableElements
+ */
+ protected HashSet<String> getDrawableElements()
+ {
+ return drawableElements;
+ }
+
+ /**
+ * Auxiliary method to determine is this test is applicable at all to the given application. If not, the checker will return an OK status.
+ * If the minSdkVersion is 3 (or lower) or the <supports-screen> tag does not exit, return an OK result.
+ * @param data The application data.
+ * @return A boolean stating if the the test is applicable
+ */
+ private boolean checkTestApplicability()
+ {
+ testApplicable = true;
+
+ Document manifestDoc = manifestElement.getDocument();
+ isXhdpiApplicable = true;
+ // Validate <uses-sdk> node.
+ NodeList usesSdkList = manifestDoc.getElementsByTagName(TAG_NAME_USES_SDK);
+ if (usesSdkList.getLength() > 0)
+ {
+ for (int i = 0; i < usesSdkList.getLength(); i++)
+ {
+ Node usesSdkNode = usesSdkList.item(i);
+ Node minSdkAttribute =
+ usesSdkNode.getAttributes().getNamedItem(ATTRIBUTE_MIN_SDK_VERSION);
+ if (minSdkAttribute != null)
+ {
+ int minSdk = Integer.parseInt(minSdkAttribute.getNodeValue());
+ if (minSdk <= MIN_SDK_THRESHOLD)
+ {
+ testApplicable = false;
+ }
+ }
+ else
+ {
+ testApplicable = false;
+ }
+ Node maxSdkAttribute =
+ usesSdkNode.getAttributes().getNamedItem(ATTRIBUTE_MAX_SDK_VERSION);
+
+ if (maxSdkAttribute != null)
+ {
+ int maxSdk = Integer.parseInt(maxSdkAttribute.getNodeValue());
+ if (maxSdk < MAX_XHDPI_SDK_THRESHOLD)
+ {
+ isXhdpiApplicable = false;
+ }
+ }
+ }
+ }
+ else
+ {
+ testApplicable = false;
+ }
+
+ if (testApplicable)
+ {
+ // Validate <supports-screen> node.
+ NodeList supportsScreensList =
+ manifestDoc.getElementsByTagName(TAG_NAME_SUPPORTS_SCREENS);
+ if (supportsScreensList.getLength() > 0)
+ {
+ for (int i = 0; i < supportsScreensList.getLength(); i++)
+ {
+ Node supportsScreensNode = supportsScreensList.item(i);
+ Node anyDensityAttribute =
+ supportsScreensNode.getAttributes().getNamedItem(ATTRIBUTE_ANY_DENSITY);
+ if (anyDensityAttribute != null)
+ {
+ boolean anyDensity =
+ Boolean.parseBoolean(anyDensityAttribute.getNodeValue());
+ if (!anyDensity)
+ {
+ testApplicable = false;
+ }
+ }
+
+ }
+ }
+ }
+
+ return testApplicable;
+ }
+
+ /**
+ * Keep the result of {@link MissingDrawableData#checkTestApplicability() } to avoid running it for all conditions
+ * Warning: Guarantee that {@link MissingDrawableData#checkTestApplicability() } is run once before calling this method.
+ * @return A boolean stating if the the test is applicable
+ */
+ protected boolean isTestApplicable()
+ {
+ return testApplicable;
+ }
+
+ /**
+ * @return true if at least one folder (ldpi, mdpi, hdpi or xhdpi exists), false otherwise.
+ */
+ public boolean atLeastOneDrawableFolderExist()
+ {
+ return (isLdpiFolderExists() || isMdpiFolderExists() || isHdpiFolderExists() || (isXhdpiFolderExists() && isXhdpiApplicable()));
+ }
+
+ /**
+ * @return true if at least one folder (ldpi, mdpi, hdpi or xhdpi is missing), false otherwise.
+ */
+ public boolean isMissingAtLeastOneDrawableFolder()
+ {
+ return (!isLdpiFolderExists() || !isMdpiFolderExists() || !isHdpiFolderExists() || (!isXhdpiFolderExists() && isXhdpiApplicable()));
+ }
+}
diff --git a/src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/missingdrawable/MissingDrawableFoldersCondition.java b/src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/missingdrawable/MissingDrawableFoldersCondition.java
new file mode 100644
index 0000000..51ade28
--- /dev/null
+++ b/src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/missingdrawable/MissingDrawableFoldersCondition.java
@@ -0,0 +1,168 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorolamobility.preflighting.checkers.missingdrawable;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import org.eclipse.core.runtime.IStatus;
+
+import com.motorolamobility.preflighting.checkers.i18n.CheckerNLS;
+import com.motorolamobility.preflighting.core.PreflightingCorePlugin;
+import com.motorolamobility.preflighting.core.applicationdata.ApplicationData;
+import com.motorolamobility.preflighting.core.checker.condition.CanExecuteConditionStatus;
+import com.motorolamobility.preflighting.core.checker.condition.Condition;
+import com.motorolamobility.preflighting.core.checker.condition.ICondition;
+import com.motorolamobility.preflighting.core.devicespecification.DeviceSpecification;
+import com.motorolamobility.preflighting.core.exception.PreflightingCheckerException;
+import com.motorolamobility.preflighting.core.internal.cond.utils.ConditionUtils;
+import com.motorolamobility.preflighting.core.validation.ValidationManagerConfiguration;
+import com.motorolamobility.preflighting.core.validation.ValidationResult;
+import com.motorolamobility.preflighting.core.validation.ValidationResultData;
+
+/**
+ * Verifies existence of all density-specific drawable folders.
+ * Raise issue if one or more drawable folders do not exist.
+ */
+public class MissingDrawableFoldersCondition extends Condition implements ICondition
+{
+ private MissingDrawableData missingDrawableData;
+
+ /**
+ * @see com.motorolamobility.preflighting.core.checker.condition.Condition#execute(com.motorolamobility.preflighting.core.applicationdata.ApplicationData, java.util.List, com.motorolamobility.preflighting.core.validation.ValidationManagerConfiguration, com.motorolamobility.preflighting.core.validation.ValidationResult)
+ */
+ @Override
+ public void execute(ApplicationData data, List<DeviceSpecification> deviceSpecs,
+ ValidationManagerConfiguration valManagerConfig, ValidationResult results)
+ throws PreflightingCheckerException
+ {
+ // First we need to check if this test is valid at all.
+ // If the minSdkVersion is 3 (or lower) or the <supports-screen> tag does not exit, return an OK result.
+ MissingDrawableChecker checker;
+ if (getChecker() instanceof MissingDrawableChecker)
+ {
+ checker = (MissingDrawableChecker) getChecker();
+ missingDrawableData = checker.getMissingDrawableData();
+ if (missingDrawableData.isTestApplicable())
+ {
+ generateResults(results, valManagerConfig);
+ }
+ }
+ }
+
+ /**
+ * @see com.motorolamobility.preflighting.core.checker.condition.Condition#canExecute(com.motorolamobility.preflighting.core.applicationdata.ApplicationData, java.util.List)
+ */
+ @Override
+ public CanExecuteConditionStatus canExecute(ApplicationData data,
+ List<DeviceSpecification> deviceSpecs) throws PreflightingCheckerException
+ {
+ CanExecuteConditionStatus status =
+ new CanExecuteConditionStatus(IStatus.OK, PreflightingCorePlugin.PLUGIN_ID,
+ "", getId()); //$NON-NLS-1$
+ return status;
+ }
+
+ /**
+ * Auxiliary method to execute validation and generate the results
+ * @return A list of results
+ */
+ private void generateResults(ValidationResult results,
+ ValidationManagerConfiguration valManagerConfig)
+ {
+ if (missingDrawableData.atLeastOneDrawableFolderExist())
+ {
+
+ if (missingDrawableData.isMissingAtLeastOneDrawableFolder())
+ {
+ // That's an error
+ ArrayList<String> strsToBind = new ArrayList<String>();
+
+ if (!missingDrawableData.isLdpiFolderExists())
+ {
+ strsToBind.add(CheckerNLS.MissingDrawableChecker_ldpiFolder);
+ }
+ if (!missingDrawableData.isMdpiFolderExists())
+ {
+ strsToBind.add(CheckerNLS.MissingDrawableChecker_mdpiFolder);
+ }
+ if (!missingDrawableData.isHdpiFolderExists())
+ {
+ strsToBind.add(CheckerNLS.MissingDrawableChecker_hdpiFolder);
+ }
+ if (!missingDrawableData.isXhdpiFolderExists()
+ && missingDrawableData.isXhdpiApplicable())
+ {
+ strsToBind.add(CheckerNLS.MissingDrawableChecker_xhdpiFolder);
+ }
+
+ for (String currentStr : strsToBind)
+ {
+ ValidationResultData result = new ValidationResultData();
+ result.setSeverity(getSeverityLevel());
+ result.setIssueDescription(CheckerNLS.bind(
+ CheckerNLS.MissingDrawableChecker_FolderNotFound, currentStr));
+ result.setQuickFixSuggestion(CheckerNLS.bind(
+ CheckerNLS.MissingDrawableChecker_CreateFolderOnResFolder, currentStr));
+ result.addFileToIssueLines(missingDrawableData.getResFolder().getFile(),
+ Collections.<Integer> emptyList());
+ result.setConditionID(getId());
+ result.setInfoURL(ConditionUtils.getDescriptionLink(getChecker().getId(),
+ getId(), valManagerConfig));
+ results.addValidationResult(result);
+
+ }
+ }
+ }
+ else
+ {
+ // No drawable resolution folders exist
+ if (missingDrawableData.isStandardFolderExists())
+ {
+ ValidationResultData result = new ValidationResultData();
+ result.setConditionID(getId());
+ result.setSeverity(getSeverityLevel());
+ result.setIssueDescription(CheckerNLS.MissingDrawableChecker_noDensitySpecificDrawableFolders);
+ result.setQuickFixSuggestion(CheckerNLS.bind(
+ CheckerNLS.MissingDrawableChecker_CreateFoldersOnResFolder, new Object[]
+ {
+ CheckerNLS.MissingDrawableChecker_ldpiFolder,
+ CheckerNLS.MissingDrawableChecker_mdpiFolder,
+ CheckerNLS.MissingDrawableChecker_hdpiFolder
+ }));
+ result.addFileToIssueLines(missingDrawableData.getResFolder().getFile(),
+ Collections.<Integer> emptyList());
+ result.setInfoURL(ConditionUtils.getDescriptionLink(getChecker().getId(), getId(),
+ valManagerConfig));
+ results.addValidationResult(result);
+ }
+ else
+ {
+ ValidationResultData result = new ValidationResultData();
+ result.setSeverity(getSeverityLevel());
+ result.setIssueDescription(CheckerNLS.MissingDrawableChecker_noDrawableFolders);
+ result.addFileToIssueLines(missingDrawableData.getResFolder().getFile(),
+ Collections.<Integer> emptyList());
+ result.setConditionID(getId());
+ result.setInfoURL(ConditionUtils.getDescriptionLink(getChecker().getId(), getId(),
+ valManagerConfig));
+ results.addValidationResult(result);
+ }
+ }
+ }
+
+}
diff --git a/src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/missingdrawable/MissingDrawableResourcesCondition.java b/src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/missingdrawable/MissingDrawableResourcesCondition.java
new file mode 100644
index 0000000..8451f26
--- /dev/null
+++ b/src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/missingdrawable/MissingDrawableResourcesCondition.java
@@ -0,0 +1,171 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorolamobility.preflighting.checkers.missingdrawable;
+
+import java.util.Collections;
+import java.util.List;
+
+import org.eclipse.core.runtime.IStatus;
+
+import com.motorolamobility.preflighting.checkers.i18n.CheckerNLS;
+import com.motorolamobility.preflighting.core.PreflightingCorePlugin;
+import com.motorolamobility.preflighting.core.applicationdata.ApplicationData;
+import com.motorolamobility.preflighting.core.applicationdata.FolderElement;
+import com.motorolamobility.preflighting.core.checker.condition.CanExecuteConditionStatus;
+import com.motorolamobility.preflighting.core.checker.condition.Condition;
+import com.motorolamobility.preflighting.core.checker.condition.ICondition;
+import com.motorolamobility.preflighting.core.devicespecification.DeviceSpecification;
+import com.motorolamobility.preflighting.core.exception.PreflightingCheckerException;
+import com.motorolamobility.preflighting.core.internal.cond.utils.ConditionUtils;
+import com.motorolamobility.preflighting.core.validation.ValidationManagerConfiguration;
+import com.motorolamobility.preflighting.core.validation.ValidationResult;
+import com.motorolamobility.preflighting.core.validation.ValidationResultData;
+
+/**
+ * Checks existence of all density-specific versions of drawable resources (images).
+ */
+public class MissingDrawableResourcesCondition extends Condition implements ICondition
+{
+
+ private MissingDrawableData missingDrawableData;
+
+ /**
+ * @see com.motorolamobility.preflighting.core.checker.condition.Condition#execute(com.motorolamobility.preflighting.core.applicationdata.ApplicationData, java.util.List, com.motorolamobility.preflighting.core.validation.ValidationManagerConfiguration, com.motorolamobility.preflighting.core.validation.ValidationResult)
+ */
+ @Override
+ public void execute(ApplicationData data, List<DeviceSpecification> deviceSpecs,
+ ValidationManagerConfiguration valManagerConfig, ValidationResult results)
+ throws PreflightingCheckerException
+ {
+ // First we need to check if this test is valid at all.
+ // If the minSdkVersion is 3 (or lower) or the <supports-screen> tag does not exit, return an OK result.
+ MissingDrawableChecker checker;
+ if (getChecker() instanceof MissingDrawableChecker)
+ {
+ checker = (MissingDrawableChecker) getChecker();
+ missingDrawableData = checker.getMissingDrawableData();
+ if (missingDrawableData.isTestApplicable())
+ {
+ generateResults(results, valManagerConfig);
+ }
+ }
+ }
+
+ /**
+ * @see com.motorolamobility.preflighting.core.checker.condition.Condition#canExecute(com.motorolamobility.preflighting.core.applicationdata.ApplicationData, java.util.List)
+ */
+ @Override
+ public CanExecuteConditionStatus canExecute(ApplicationData data,
+ List<DeviceSpecification> deviceSpecs) throws PreflightingCheckerException
+ {
+ CanExecuteConditionStatus status =
+ new CanExecuteConditionStatus(IStatus.OK, PreflightingCorePlugin.PLUGIN_ID,
+ "", getId()); //$NON-NLS-1$
+ return status;
+ }
+
+ /**
+ * Auxiliary method to execute validation and generate the results
+ * @return A list of results
+ */
+ private void generateResults(ValidationResult results,
+ ValidationManagerConfiguration valManagerConfig)
+ {
+ if (missingDrawableData.atLeastOneDrawableFolderExist())
+ {
+ // For each drawable element, check if it exists in all 4 expected drawable folder: ldpi, mdpi, hdpi, xhdpi
+ for (String s : missingDrawableData.getDrawableElements())
+ {
+ if (missingDrawableData.isLdpiFolderExists())
+ {
+ FolderElement ldpiFolder =
+ missingDrawableData.getResFolder().getLdpiDrawableFolder();
+ if (!ldpiFolder.containsFile(s))
+ {
+ ValidationResultData result =
+ createMissingDrawableResult(s, ldpiFolder,
+ CheckerNLS.MissingDrawableChecker_ldpiFolder,
+ valManagerConfig);
+ results.addValidationResult(result);
+ }
+ }
+
+ if (missingDrawableData.isMdpiFolderExists())
+ {
+ FolderElement mdpiFolder =
+ missingDrawableData.getResFolder().getMdpiDrawableFolder();
+ if (!mdpiFolder.containsFile(s))
+ {
+ ValidationResultData result =
+ createMissingDrawableResult(s, mdpiFolder,
+ CheckerNLS.MissingDrawableChecker_mdpiFolder,
+ valManagerConfig);
+ results.addValidationResult(result);
+ }
+ }
+
+ if (missingDrawableData.isHdpiFolderExists())
+ {
+ FolderElement hdpiFolder =
+ missingDrawableData.getResFolder().getHdpiDrawableFolder();
+ if (!hdpiFolder.containsFile(s))
+ {
+ ValidationResultData result =
+ createMissingDrawableResult(s, hdpiFolder,
+ CheckerNLS.MissingDrawableChecker_hdpiFolder,
+ valManagerConfig);
+ results.addValidationResult(result);
+ }
+ }
+
+ if (missingDrawableData.isXhdpiFolderExists()
+ && missingDrawableData.isXhdpiApplicable())
+ {
+ FolderElement xhdpiFolder =
+ missingDrawableData.getResFolder().getXhdpiDrawableFolder();
+ if (!xhdpiFolder.containsFile(s))
+ {
+ ValidationResultData result =
+ createMissingDrawableResult(s, xhdpiFolder,
+ CheckerNLS.MissingDrawableChecker_xhdpiFolder,
+ valManagerConfig);
+ results.addValidationResult(result);
+ }
+ }
+ }
+
+ }
+ }
+
+ private ValidationResultData createMissingDrawableResult(String s, FolderElement folder,
+ String msg, ValidationManagerConfiguration valManagerConfig)
+ {
+
+ // Create a result
+ ValidationResultData result = new ValidationResultData();
+ result.setSeverity(getSeverityLevel());
+ result.setIssueDescription(CheckerNLS.bind(
+ CheckerNLS.MissingDrawableChecker_missingDrawableDesc, s, msg));
+ result.setQuickFixSuggestion(CheckerNLS.bind(
+ CheckerNLS.MissingDrawableChecker_AddDrawableToFolder, s, msg));
+ result.addFileToIssueLines(folder.getFile(), Collections.<Integer> emptyList());
+ result.setConditionID(getId());
+ result.setInfoURL(ConditionUtils.getDescriptionLink(getChecker().getId(), getId(),
+ valManagerConfig));
+
+ return result;
+ }
+}
diff --git a/src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/orphanedstrings/OrphanedStringsCondition.java b/src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/orphanedstrings/OrphanedStringsCondition.java
new file mode 100644
index 0000000..8ad1121
--- /dev/null
+++ b/src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/orphanedstrings/OrphanedStringsCondition.java
@@ -0,0 +1,162 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorolamobility.preflighting.checkers.orphanedstrings;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+
+import org.eclipse.core.runtime.IStatus;
+
+import com.motorolamobility.preflighting.checkers.CheckerPlugin;
+import com.motorolamobility.preflighting.checkers.i18n.CheckerNLS;
+import com.motorolamobility.preflighting.core.PreflightingCorePlugin;
+import com.motorolamobility.preflighting.core.applicationdata.ApplicationData;
+import com.motorolamobility.preflighting.core.applicationdata.Element;
+import com.motorolamobility.preflighting.core.applicationdata.Element.Type;
+import com.motorolamobility.preflighting.core.applicationdata.ElementUtils;
+import com.motorolamobility.preflighting.core.applicationdata.ResourcesFolderElement;
+import com.motorolamobility.preflighting.core.checker.condition.CanExecuteConditionStatus;
+import com.motorolamobility.preflighting.core.checker.condition.Condition;
+import com.motorolamobility.preflighting.core.checker.condition.ICondition;
+import com.motorolamobility.preflighting.core.devicespecification.DeviceSpecification;
+import com.motorolamobility.preflighting.core.exception.PreflightingCheckerException;
+import com.motorolamobility.preflighting.core.internal.cond.utils.ConditionUtils;
+import com.motorolamobility.preflighting.core.logging.PreflightingLogger;
+import com.motorolamobility.preflighting.core.validation.ValidationManagerConfiguration;
+import com.motorolamobility.preflighting.core.validation.ValidationResult;
+import com.motorolamobility.preflighting.core.validation.ValidationResultData;
+
+/**
+ * Condition that looks for declared strings that are not used in the code (Java or resources XML)
+ */
+public class OrphanedStringsCondition extends Condition implements ICondition
+{
+
+ private ResourcesFolderElement resFolder;
+
+ /**
+ * @see com.motorolamobility.preflighting.core.checker.condition.Condition#execute(com.motorolamobility.preflighting.core.applicationdata.ApplicationData, java.util.List, com.motorolamobility.preflighting.core.devicespecification.internal.PlatformRules, com.motorolamobility.preflighting.core.validation.ValidationManagerConfiguration, com.motorolamobility.preflighting.core.validation.ValidationResult)
+ */
+ @Override
+ public void execute(ApplicationData data, List<DeviceSpecification> deviceSpecs,
+ ValidationManagerConfiguration valManagerConfig, ValidationResult results)
+ throws PreflightingCheckerException
+ {
+ Set<String> usedStringsSet = data.getUsedStringsInApplication();
+
+ PreflightingLogger.debug("Used strings: " + usedStringsSet);
+
+ //notUsedStringsSet - initially we have the entire set of declared strings
+ Set<String> notUsedStringsSet = data.getDeclaredStringsInResourceFiles();
+
+ PreflightingLogger.debug("Declared strings: " + notUsedStringsSet);
+
+ //removing all used strings (notUsedStringSet = declaredStringsSet - usedStringsSet)
+ notUsedStringsSet.removeAll(usedStringsSet);
+
+ PreflightingLogger.debug("Not used strings: " + notUsedStringsSet);
+
+ if ((notUsedStringsSet != null) && !notUsedStringsSet.isEmpty())
+ {
+ //we found strings not used
+ addResults(results, notUsedStringsSet, valManagerConfig);
+ }
+ }
+
+ private void addResults(ValidationResult results, Set<String> notUsedStringsSet,
+ ValidationManagerConfiguration valManagerConfig)
+ {
+ for (String notUsedString : notUsedStringsSet)
+ {
+ //adding string result (reached max number of chars)
+ ValidationResultData result = new ValidationResultData();
+
+ // Create a result and return it
+ result.setSeverity(getSeverityLevel());
+ result.setConditionID(getId());
+
+ //Associate the result to the resFolder
+ result.addFileToIssueLines(resFolder.getFile(), Collections.<Integer> emptyList());
+
+ // Construct description
+ String resultDescription =
+ CheckerNLS.bind(CheckerNLS.OrphanedStringsCondition_Result_Description,
+ notUsedString);
+ result.setIssueDescription(resultDescription.toString());
+
+ // Set quickfix
+ result.setQuickFixSuggestion(CheckerNLS.OrphanedStringsCondition_Result_QuickFix);
+ result.setInfoURL(ConditionUtils.getDescriptionLink(getChecker().getId(), getId(),
+ valManagerConfig));
+
+ // Add result to the result list
+ results.addValidationResult(result);
+ }
+ }
+
+ /**
+ * @see com.motorolamobility.preflighting.core.checker.condition.Condition#canExecute(com.motorolamobility.preflighting.core.applicationdata.ApplicationData, java.util.List)
+ */
+ @Override
+ public CanExecuteConditionStatus canExecute(ApplicationData data,
+ List<DeviceSpecification> deviceSpecs) throws PreflightingCheckerException
+ {
+ CanExecuteConditionStatus status =
+ new CanExecuteConditionStatus(IStatus.OK, PreflightingCorePlugin.PLUGIN_ID, "");
+ //Verify if model contains res folder
+ List<Element> folderResElements =
+ ElementUtils.getElementByType(data.getRootElement(), Type.FOLDER_RES);
+
+ resFolder =
+ folderResElements.size() > 0 ? (ResourcesFolderElement) folderResElements.get(0)
+ : null;
+
+ if (resFolder == null)
+ {
+ status =
+ new CanExecuteConditionStatus(IStatus.ERROR, CheckerPlugin.PLUGIN_ID,
+ CheckerNLS.Missing_res_folder);
+ }
+ if (status.isOK())
+ {
+ // Check if at least one "values" folder exist with a appropriate "strings.xml" file
+ int numberOfFoundValuesResources = 0;
+ for (Element e : resFolder.getChildren())
+ {
+ if (e.getType() == Element.Type.FOLDER_VALUES)
+ {
+ for (Element children : e.getChildren())
+ {
+ if (children.getType() == Element.Type.FILE_STRINGS)
+ {
+ numberOfFoundValuesResources++;
+ }
+ }
+ }
+ }
+
+ if (numberOfFoundValuesResources == 0)
+ {
+ status =
+ new CanExecuteConditionStatus(IStatus.ERROR, CheckerPlugin.PLUGIN_ID,
+ CheckerNLS.LocalizationStringsChecker_Missing_stringsXml_File);
+ }
+ }
+ status.setConditionId(getId());
+ return status;
+ }
+}
diff --git a/src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/permissions/BlockedPermissionCondition.java b/src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/permissions/BlockedPermissionCondition.java
new file mode 100644
index 0000000..e773b33
--- /dev/null
+++ b/src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/permissions/BlockedPermissionCondition.java
@@ -0,0 +1,169 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorolamobility.preflighting.checkers.permissions;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.core.runtime.IStatus;
+import org.w3c.dom.Document;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import com.motorolamobility.preflighting.checkers.CheckerPlugin;
+import com.motorolamobility.preflighting.checkers.i18n.CheckerNLS;
+import com.motorolamobility.preflighting.core.applicationdata.ApplicationData;
+import com.motorolamobility.preflighting.core.applicationdata.XMLElement;
+import com.motorolamobility.preflighting.core.checker.condition.CanExecuteConditionStatus;
+import com.motorolamobility.preflighting.core.checker.condition.Condition;
+import com.motorolamobility.preflighting.core.checker.condition.ICondition;
+import com.motorolamobility.preflighting.core.devicespecification.DeviceSpecification;
+import com.motorolamobility.preflighting.core.devicespecification.internal.PlatformRules;
+import com.motorolamobility.preflighting.core.exception.PreflightingCheckerException;
+import com.motorolamobility.preflighting.core.internal.cond.utils.ConditionUtils;
+import com.motorolamobility.preflighting.core.utils.XmlUtils;
+import com.motorolamobility.preflighting.core.validation.ValidationManagerConfiguration;
+import com.motorolamobility.preflighting.core.validation.ValidationResult;
+import com.motorolamobility.preflighting.core.validation.ValidationResultData;
+
+public class BlockedPermissionCondition extends Condition implements ICondition
+{
+
+ private static final String USES_PERMISSION = "uses-permission"; //$NON-NLS-1$
+
+ private static final String ANDROID_NAME = "android:name"; //$NON-NLS-1$
+
+ /**
+ * Elements to validate
+ */
+ private XMLElement manifestElement;
+
+ private Document manifestDoc;
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorolamobility.preflighting.core.checker.condition.Condition#canExecute(com.motorolamobility.preflighting.core.applicationdata.ApplicationData, java.util.List)
+ */
+ @Override
+ public CanExecuteConditionStatus canExecute(ApplicationData data,
+ List<DeviceSpecification> deviceSpecs) throws PreflightingCheckerException
+ {
+ CanExecuteConditionStatus status =
+ new CanExecuteConditionStatus(IStatus.OK, CheckerPlugin.PLUGIN_ID, "");
+
+ manifestElement = data.getManifestElement();
+ if (manifestElement == null)
+ {
+ status =
+ new CanExecuteConditionStatus(IStatus.ERROR, CheckerPlugin.PLUGIN_ID,
+ CheckerNLS.Invalid_ManifestFile);
+ }
+ else if ((manifestDoc = manifestElement.getDocument()) == null)
+ {
+ status =
+ new CanExecuteConditionStatus(IStatus.ERROR, CheckerPlugin.PLUGIN_ID,
+ CheckerNLS.Invalid_ManifestFile);
+ }
+
+ status.setConditionId(getId());
+ return status;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorolamobility.preflighting.core.checker.condition.Condition#execute(com.motorolamobility.preflighting.core.applicationdata.ApplicationData, java.util.List, com.motorolamobility.preflighting.core.devicespecification.PlatformRules, com.motorolamobility.preflighting.core.validation.ValidationManagerConfiguration)
+ */
+ @Override
+ public void execute(ApplicationData data, List<DeviceSpecification> deviceSpecs,
+ ValidationManagerConfiguration valManagerConfig, ValidationResult results)
+ throws PreflightingCheckerException
+ {
+ NodeList permissionsNodeLst = manifestDoc.getElementsByTagName(USES_PERMISSION);
+
+ //Extract permissions from manifest
+ List<String> manifestPermissionsList =
+ new ArrayList<String>(permissionsNodeLst.getLength());
+ for (int i = 0; i < permissionsNodeLst.getLength(); i++)
+ {
+ Node permissionNode = permissionsNodeLst.item(i);
+ NamedNodeMap permissionMap = permissionNode.getAttributes();
+ Node permissionAtr = permissionMap.getNamedItem(ANDROID_NAME);
+
+ if ((permissionAtr != null))
+ {
+ String permissionId = permissionAtr.getNodeValue().trim();
+ if (!permissionId.equals("")) //$NON-NLS-1$
+ {
+ manifestPermissionsList.add(permissionId);
+ }
+ }
+ }
+
+ //analyze permissions node
+ verifyBlockedPermissions(PlatformRules.getInstance(), valManagerConfig, results,
+ permissionsNodeLst);
+ }
+
+ private void verifyBlockedPermissions(PlatformRules platformRules,
+ ValidationManagerConfiguration valManagerConfig, ValidationResult result,
+ NodeList permissionsLst)
+ {
+ for (int i = 0; i < permissionsLst.getLength(); i++)
+ {
+ Node permissionNode = permissionsLst.item(i);
+ NamedNodeMap permissionMap = permissionNode.getAttributes();
+ Node permissionAtr = permissionMap.getNamedItem(ANDROID_NAME);
+
+ if ((permissionAtr != null) && !permissionAtr.getNodeValue().trim().equals("")) //$NON-NLS-1$
+ {
+ String permissionId = permissionAtr.getNodeValue();
+ //if blocked permission, create a result data
+ if (platformRules.isPermissionBlocked(permissionId))
+ {
+ result.addValidationResult(createBlockedPermissionsValidationResult(
+ permissionNode, permissionId, valManagerConfig));
+ }
+ }
+ }
+ }
+
+ private ValidationResultData createBlockedPermissionsValidationResult(Node permissionNode,
+ String permissionId, ValidationManagerConfiguration valManagerConfig)
+ {
+ ValidationResultData resultData = new ValidationResultData();
+ resultData.setConditionID(getId());
+
+ ArrayList<Integer> lines = new ArrayList<Integer>();
+ int issuedLine = manifestElement.getNodeLineNumber(permissionNode);
+ if (issuedLine != -1)
+ {
+ lines.add(issuedLine);
+ }
+ resultData.addFileToIssueLines(manifestElement.getFile(), lines);
+ resultData.setIssueDescription(CheckerNLS.bind(
+ CheckerNLS.UnsecurePermissionsChecker_conditionForbiddenPermission_description,
+ permissionId));
+ resultData
+ .setQuickFixSuggestion(CheckerNLS.UnsecurePermissionsChecker_conditionForbiddenPermission_suggestion);
+ resultData.setSeverity(getSeverityLevel());
+ resultData.setInfoURL(ConditionUtils.getDescriptionLink(getChecker().getId(), getId(),
+ valManagerConfig));
+ resultData.setPreview(XmlUtils.getXMLNodeAsString(permissionNode, false));
+
+ return resultData;
+ }
+}
diff --git a/src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/permissions/MissingPermissionsCondition.java b/src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/permissions/MissingPermissionsCondition.java
new file mode 100644
index 0000000..5bca8c3
--- /dev/null
+++ b/src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/permissions/MissingPermissionsCondition.java
@@ -0,0 +1,172 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorolamobility.preflighting.checkers.permissions;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.w3c.dom.Node;
+
+import com.motorolamobility.preflighting.checkers.i18n.CheckerNLS;
+import com.motorolamobility.preflighting.core.applicationdata.ApplicationData;
+import com.motorolamobility.preflighting.core.checker.condition.CanExecuteConditionStatus;
+import com.motorolamobility.preflighting.core.checker.condition.Condition;
+import com.motorolamobility.preflighting.core.checker.condition.ICondition;
+import com.motorolamobility.preflighting.core.devicespecification.DeviceSpecification;
+import com.motorolamobility.preflighting.core.devicespecification.internal.PlatformRules;
+import com.motorolamobility.preflighting.core.exception.PreflightingCheckerException;
+import com.motorolamobility.preflighting.core.internal.cond.utils.ConditionUtils;
+import com.motorolamobility.preflighting.core.source.model.Invoke;
+import com.motorolamobility.preflighting.core.utils.CheckerUtils;
+import com.motorolamobility.preflighting.core.validation.ValidationManagerConfiguration;
+import com.motorolamobility.preflighting.core.validation.ValidationResult;
+import com.motorolamobility.preflighting.core.validation.ValidationResultData;
+
+public class MissingPermissionsCondition extends Condition implements ICondition
+{
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorolamobility.preflighting.core.checker.condition.Condition#canExecute(com.motorolamobility.preflighting.core.applicationdata.ApplicationData, java.util.List)
+ */
+ @Override
+ public CanExecuteConditionStatus canExecute(ApplicationData data,
+ List<DeviceSpecification> deviceSpecs) throws PreflightingCheckerException
+ {
+ CanExecuteConditionStatus status =
+ CheckerUtils.isAndroidManifestFileExistent(data, getId());
+ if (status.isOK())
+ {
+ status = CheckerUtils.isJavaModelComplete(data, getId());
+ }
+
+ return status;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorolamobility.preflighting.core.checker.condition.Condition#execute(com.motorolamobility.preflighting.core.applicationdata.ApplicationData, java.util.List, com.motorolamobility.preflighting.core.devicespecification.PlatformRules, com.motorolamobility.preflighting.core.validation.ValidationManagerConfiguration)
+ */
+ @Override
+ public void execute(ApplicationData data, List<DeviceSpecification> deviceSpecs,
+ ValidationManagerConfiguration valManagerConfig, ValidationResult results)
+ throws PreflightingCheckerException
+ {
+ Map<String, Node> manifestPermissions =
+ CheckerUtils.getPermissions(data.getManifestElement().getDocument());
+
+ analyzeMissingPermissions(data, PlatformRules.getInstance(), valManagerConfig, results,
+ manifestPermissions.keySet());
+
+ }
+
+ private void analyzeMissingPermissions(ApplicationData data, PlatformRules platformRules,
+ ValidationManagerConfiguration valManagerConfig, ValidationResult result,
+ Set<String> manifestPermissions)
+ {
+ NeededAppPermissions neededAppPermissions =
+ ((PermissionsChecker) getChecker()).getNeededAppPermissions();
+
+ Map<Invoke, List<String>> requiredPermissionsMap =
+ neededAppPermissions.getRequiredPermissionsMap();
+ Map<Invoke, List<String>> optionalPermissionsMap =
+ neededAppPermissions.getOptionalPermissionsMap();
+
+ //Check required permissions
+ for (Invoke invoked : requiredPermissionsMap.keySet())
+ {
+ for (String requiredPermission : requiredPermissionsMap.get(invoked))
+ {
+ if (!manifestPermissions.contains(requiredPermission))
+ {
+ ValidationResultData validationResult =
+ createMissingPermissionResult(valManagerConfig, invoked, new String[]
+ {
+ requiredPermission
+ });
+ result.addValidationResult(validationResult);
+ }
+ }
+ }
+
+ //Check optional permissions
+ for (Invoke invoked : optionalPermissionsMap.keySet())
+ {
+ boolean optionalPermissionFound = false;
+ List<String> optionalPermissionsList = optionalPermissionsMap.get(invoked);
+ for (String optionalPermission : optionalPermissionsList)
+ {
+ if (manifestPermissions.contains(optionalPermission))
+ {
+ optionalPermissionFound = true;
+ break;
+ }
+ }
+
+ //If none of the optional permission was declared then setup an error on result.
+ if (!optionalPermissionsList.isEmpty() && !optionalPermissionFound)
+ {
+ ValidationResultData validationResult =
+ createMissingPermissionResult(valManagerConfig, invoked,
+ optionalPermissionsList.toArray(new String[optionalPermissionsList
+ .size()]));
+ result.addValidationResult(validationResult);
+ }
+ }
+
+ }
+
+ private ValidationResultData createMissingPermissionResult(
+ ValidationManagerConfiguration valManagerConfig, Invoke invokedMethod,
+ String[] permissions)
+ {
+ //Fill ValidationData, missing required permission found!
+ ValidationResultData resultData = new ValidationResultData();
+ resultData.setConditionID(getId());
+ resultData.setMarkerType(getMarkerType());
+
+ ArrayList<Integer> lines = new ArrayList<Integer>();
+ int issuedLine = invokedMethod.getLine();
+ if (issuedLine != -1)
+ {
+ lines.add(issuedLine);
+ }
+ File javaFile = new File(invokedMethod.getSourceFileFullPath());
+ resultData.addFileToIssueLines(javaFile, lines);
+
+ StringBuffer missingPermissionsStrBuffer = new StringBuffer();
+ for (String missingPermission : permissions)
+ {
+ missingPermissionsStrBuffer.append(missingPermission + ",");
+ resultData.appendExtra(missingPermission);
+ }
+ missingPermissionsStrBuffer.deleteCharAt(missingPermissionsStrBuffer.length() - 1);
+ String permissionsString = missingPermissionsStrBuffer.toString();
+
+ resultData.setIssueDescription(CheckerNLS.bind(
+ CheckerNLS.PermissionsChecker_MissingPermission_Message,
+ invokedMethod.getQualifiedName(), permissionsString)); //Format the permissions array so that the displayed result will be nice.
+ resultData.setQuickFixSuggestion(CheckerNLS.PermissionsChecker_MissingPermission_QuickFix);
+ resultData.setSeverity(getSeverityLevel());
+ resultData.setInfoURL(ConditionUtils.getDescriptionLink(getChecker().getId(), getId(),
+ valManagerConfig));
+ //We don't have the preview for java at this time.
+ return resultData;
+ }
+}
diff --git a/src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/permissions/NeededAppPermissions.java b/src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/permissions/NeededAppPermissions.java
new file mode 100644
index 0000000..50042c4
--- /dev/null
+++ b/src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/permissions/NeededAppPermissions.java
@@ -0,0 +1,91 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorolamobility.preflighting.checkers.permissions;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import com.motorolamobility.preflighting.core.applicationdata.SourceFolderElement;
+import com.motorolamobility.preflighting.core.devicespecification.internal.PlatformRules;
+import com.motorolamobility.preflighting.core.source.model.Invoke;
+import com.motorolamobility.preflighting.core.source.model.PermissionGroups;
+
+/**
+ * This class is intended to retrieve and store required permissions based on sourceFolderElements.
+ * This will be used by PermissionsChecker conditions.
+ */
+class NeededAppPermissions
+{
+ private Map<Invoke, List<String>> requiredPermissionsMap;
+
+ private Map<Invoke, List<String>> optionalPermissionsMap;
+
+ public NeededAppPermissions(PlatformRules platformRules,
+ List<SourceFolderElement> sourceFolderElements)
+ {
+ requiredPermissionsMap = new HashMap<Invoke, List<String>>(200);
+ optionalPermissionsMap = new HashMap<Invoke, List<String>>(200);
+
+ mountPermissionsMap(platformRules, sourceFolderElements);
+ }
+
+ private void mountPermissionsMap(PlatformRules platformRules,
+ List<SourceFolderElement> sourceFolderElements)
+ {
+ for (SourceFolderElement sourceFolderElement : sourceFolderElements)
+ {
+ List<Invoke> invokedMethods = sourceFolderElement.getInvokedMethods();
+ if (invokedMethods != null)
+ {
+ for (Invoke invoked : invokedMethods)
+ {
+ String signature = invoked.getQualifiedName();
+ PermissionGroups permissionsForMethod =
+ platformRules.getPermissionsForMethod(signature);
+
+ if (permissionsForMethod != null)
+ {
+ List<String> requiredPermissions =
+ permissionsForMethod.getRequiredPermissions();
+ List<String> optionalPermissions =
+ permissionsForMethod.getOptionalPermissions();
+
+ requiredPermissionsMap.put(invoked, requiredPermissions);
+ optionalPermissionsMap.put(invoked, optionalPermissions);
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * @return the requiredPermissionsMap
+ */
+ public Map<Invoke, List<String>> getRequiredPermissionsMap()
+ {
+ return requiredPermissionsMap;
+ }
+
+ /**
+ * @return the optionalPermissionsMap
+ */
+ public Map<Invoke, List<String>> getOptionalPermissionsMap()
+ {
+ return optionalPermissionsMap;
+ }
+
+}
diff --git a/src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/permissions/PermissionsChecker.java b/src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/permissions/PermissionsChecker.java
new file mode 100644
index 0000000..6b19b08
--- /dev/null
+++ b/src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/permissions/PermissionsChecker.java
@@ -0,0 +1,73 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorolamobility.preflighting.checkers.permissions;
+
+import java.util.List;
+
+import org.eclipse.core.runtime.IStatus;
+
+import com.motorolamobility.preflighting.core.applicationdata.ApplicationData;
+import com.motorolamobility.preflighting.core.applicationdata.SourceFolderElement;
+import com.motorolamobility.preflighting.core.checker.Checker;
+import com.motorolamobility.preflighting.core.devicespecification.DeviceSpecification;
+import com.motorolamobility.preflighting.core.devicespecification.internal.PlatformRules;
+import com.motorolamobility.preflighting.core.exception.PreflightingCheckerException;
+import com.motorolamobility.preflighting.core.utils.CheckerUtils;
+import com.motorolamobility.preflighting.core.validation.ValidationManagerConfiguration;
+import com.motorolamobility.preflighting.core.validation.ValidationResult;
+
+public class PermissionsChecker extends Checker
+{
+ NeededAppPermissions neededAppPermissions;
+
+ /*
+ * This checker won't run the java model is not preset or incomplete.
+ * (non-Javadoc)
+ * @see com.motorolamobility.preflighting.core.checker.Checker#canExecute(com.motorolamobility.preflighting.core.applicationdata.ApplicationData, java.util.List)
+ */
+ @Override
+ public IStatus canExecute(ApplicationData data, List<DeviceSpecification> deviceSpecs)
+ throws PreflightingCheckerException
+ {
+ IStatus status = CheckerUtils.isJavaModelComplete(data, null);
+
+ if (status.isOK())
+ {
+ status = super.canExecute(data, deviceSpecs);
+ }
+
+ return status;
+ }
+
+ @Override
+ public void validateApplication(ApplicationData data, List<DeviceSpecification> deviceSpecs,
+ ValidationManagerConfiguration valManagerConfig, ValidationResult results)
+ throws PreflightingCheckerException
+ {
+ List<SourceFolderElement> sourceFolderElements = data.getJavaModel();
+ neededAppPermissions =
+ new NeededAppPermissions(PlatformRules.getInstance(), sourceFolderElements);
+ super.validateApplication(data, deviceSpecs, valManagerConfig, results);
+ }
+
+ /**
+ * @return the neededAppPermissions
+ */
+ public NeededAppPermissions getNeededAppPermissions()
+ {
+ return neededAppPermissions;
+ }
+}
diff --git a/src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/permissions/UnneededPermissionsCondition.java b/src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/permissions/UnneededPermissionsCondition.java
new file mode 100644
index 0000000..d873a6c
--- /dev/null
+++ b/src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/permissions/UnneededPermissionsCondition.java
@@ -0,0 +1,150 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorolamobility.preflighting.checkers.permissions;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+import org.w3c.dom.Node;
+
+import com.motorolamobility.preflighting.checkers.i18n.CheckerNLS;
+import com.motorolamobility.preflighting.core.applicationdata.ApplicationData;
+import com.motorolamobility.preflighting.core.applicationdata.XMLElement;
+import com.motorolamobility.preflighting.core.checker.condition.CanExecuteConditionStatus;
+import com.motorolamobility.preflighting.core.checker.condition.Condition;
+import com.motorolamobility.preflighting.core.checker.condition.ICondition;
+import com.motorolamobility.preflighting.core.devicespecification.DeviceSpecification;
+import com.motorolamobility.preflighting.core.exception.PreflightingCheckerException;
+import com.motorolamobility.preflighting.core.internal.cond.utils.ConditionUtils;
+import com.motorolamobility.preflighting.core.source.model.Invoke;
+import com.motorolamobility.preflighting.core.utils.CheckerUtils;
+import com.motorolamobility.preflighting.core.utils.XmlUtils;
+import com.motorolamobility.preflighting.core.validation.ValidationManagerConfiguration;
+import com.motorolamobility.preflighting.core.validation.ValidationResult;
+import com.motorolamobility.preflighting.core.validation.ValidationResultData;
+
+public class UnneededPermissionsCondition extends Condition implements ICondition
+{
+
+ private XMLElement manifestElement;
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorolamobility.preflighting.core.checker.condition.Condition#canExecute(com.motorolamobility.preflighting.core.applicationdata.ApplicationData, java.util.List)
+ */
+ @Override
+ public CanExecuteConditionStatus canExecute(ApplicationData data,
+ List<DeviceSpecification> deviceSpecs) throws PreflightingCheckerException
+ {
+ CanExecuteConditionStatus status =
+ CheckerUtils.isAndroidManifestFileExistent(data, getId());
+ if (status.isOK())
+ {
+ status = CheckerUtils.isJavaModelComplete(data, getId());
+ }
+
+ return status;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorolamobility.preflighting.core.checker.condition.Condition#execute(com.motorolamobility.preflighting.core.applicationdata.ApplicationData, java.util.List, com.motorolamobility.preflighting.core.devicespecification.PlatformRules, com.motorolamobility.preflighting.core.validation.ValidationManagerConfiguration, com.motorolamobility.preflighting.core.validation.ValidationResult)
+ */
+ @Override
+ public void execute(ApplicationData data, List<DeviceSpecification> deviceSpecs,
+ ValidationManagerConfiguration valManagerConfig, ValidationResult results)
+ throws PreflightingCheckerException
+ {
+ manifestElement = data.getManifestElement();
+ Map<String, Node> manifestPermissions =
+ CheckerUtils.getPermissions(manifestElement.getDocument());
+
+ List<String> allCodePermissions = getAllCodePermissions();
+
+ for (String permission : manifestPermissions.keySet())
+ {
+ if (!allCodePermissions.contains(permission)) //Verify if the permission declared on Manifest File is on the code needed permissions list.
+ {
+ //If not needed fill a result
+ addValidationResult(valManagerConfig, results, manifestPermissions, permission);
+ }
+ }
+ }
+
+ /*
+ * Add a ValidationResultData to the results list, with the given data
+ */
+ private void addValidationResult(ValidationManagerConfiguration valManagerConfig,
+ ValidationResult results, Map<String, Node> manifestPermissions, String permission)
+ {
+ ValidationResultData resultData = new ValidationResultData();
+ resultData.setConditionID(getId());
+ resultData.setMarkerType(getMarkerType());
+ resultData.appendExtra(permission);
+
+ ArrayList<Integer> lines = new ArrayList<Integer>(1);
+ Node permissionNode = manifestPermissions.get(permission);
+ int issuedLine = manifestElement.getNodeLineNumber(permissionNode);
+ if (issuedLine >= 0)
+ {
+ lines.add(issuedLine);
+ }
+
+ File manifestFile = manifestElement.getFile();
+ resultData.addFileToIssueLines(manifestFile, lines);
+
+ resultData.setIssueDescription(CheckerNLS.bind(
+ CheckerNLS.UnneededPermissionsCondition_UneededPermissionMessage, permission));
+ resultData
+ .setQuickFixSuggestion(CheckerNLS.UnneededPermissionsCondition_UneededPermissionQuickFix);
+ resultData.setSeverity(getSeverityLevel());
+ resultData.setInfoURL(ConditionUtils.getDescriptionLink(getChecker().getId(), getId(),
+ valManagerConfig));
+ resultData.setPreview(XmlUtils.getXMLNodeAsString(permissionNode, false));
+ results.addValidationResult(resultData);
+ }
+
+ /*
+ * Compile all needed permissions found by code analysis into one single list.
+ */
+ private List<String> getAllCodePermissions()
+ {
+ NeededAppPermissions neededAppPermissions =
+ ((PermissionsChecker) getChecker()).getNeededAppPermissions();
+
+ Map<Invoke, List<String>> requiredPermissionsMap =
+ neededAppPermissions.getRequiredPermissionsMap();
+ Map<Invoke, List<String>> optionalPermissionsMap =
+ neededAppPermissions.getOptionalPermissionsMap();
+
+ List<String> allCodePermissions = new ArrayList<String>();
+ Collection<List<String>> requiredPermissions = requiredPermissionsMap.values();
+ for (List<String> methodPermissions : requiredPermissions)
+ {
+ allCodePermissions.addAll(methodPermissions);
+ }
+
+ Collection<List<String>> optionalPermissions = optionalPermissionsMap.values();
+ for (List<String> methodPermissions : optionalPermissions)
+ {
+ allCodePermissions.addAll(methodPermissions);
+ }
+ return allCodePermissions;
+ }
+}
diff --git a/src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/widgetPreview/MissingWidgetPreviewTagCondition.java b/src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/widgetPreview/MissingWidgetPreviewTagCondition.java
new file mode 100644
index 0000000..81251df
--- /dev/null
+++ b/src/plugins/preflighting.checkers/src/com/motorolamobility/preflighting/checkers/widgetPreview/MissingWidgetPreviewTagCondition.java
@@ -0,0 +1,386 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+package com.motorolamobility.preflighting.checkers.widgetPreview;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import org.eclipse.core.runtime.IStatus;
+import org.w3c.dom.DOMException;
+import org.w3c.dom.Document;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import com.motorolamobility.preflighting.checkers.CheckerPlugin;
+import com.motorolamobility.preflighting.checkers.i18n.CheckerNLS;
+import com.motorolamobility.preflighting.core.applicationdata.ApplicationData;
+import com.motorolamobility.preflighting.core.applicationdata.Element;
+import com.motorolamobility.preflighting.core.applicationdata.Element.Type;
+import com.motorolamobility.preflighting.core.applicationdata.ElementUtils;
+import com.motorolamobility.preflighting.core.applicationdata.FolderElement;
+import com.motorolamobility.preflighting.core.applicationdata.ResourcesFolderElement;
+import com.motorolamobility.preflighting.core.applicationdata.XMLElement;
+import com.motorolamobility.preflighting.core.checker.condition.CanExecuteConditionStatus;
+import com.motorolamobility.preflighting.core.checker.condition.Condition;
+import com.motorolamobility.preflighting.core.checker.condition.ICondition;
+import com.motorolamobility.preflighting.core.devicespecification.DeviceSpecification;
+import com.motorolamobility.preflighting.core.exception.PreflightingCheckerException;
+import com.motorolamobility.preflighting.core.internal.cond.utils.ConditionUtils;
+import com.motorolamobility.preflighting.core.utils.CheckerUtils;
+import com.motorolamobility.preflighting.core.validation.ValidationManagerConfiguration;
+import com.motorolamobility.preflighting.core.validation.ValidationResult;
+import com.motorolamobility.preflighting.core.validation.ValidationResultData;
+
+public class MissingWidgetPreviewTagCondition extends Condition implements ICondition
+{
+
+ /**
+ *
+ */
+ private static final int MIN_TABLET_SDK_VERSION = 11;
+
+ @Override
+ public void execute(ApplicationData data, List<DeviceSpecification> deviceSpecs,
+ ValidationManagerConfiguration valManagerConfig, ValidationResult results)
+ throws PreflightingCheckerException
+ {
+ ArrayList<ValidationResultData> resultDataList = new ArrayList<ValidationResultData>();
+
+ boolean hasTag = true;
+
+ FolderElement xmlFolder = null;
+ XMLElement xmlFileElement = data.getManifestElement();
+
+ //Getting the sdk version, because only from android 3.0 on the previewImage is supported
+ String minSdkStr = CheckerUtils.getMinSdk(xmlFileElement.getDocument());
+ int minSdkVersion = -1;
+ try
+ {
+ minSdkVersion = Integer.parseInt(minSdkStr);
+ }
+ catch (NumberFormatException e)
+ {
+ minSdkVersion = -1;
+ }
+
+ //it only makes sense to check for previewImage in widgets applications
+ boolean isWidget = isWidgetAplication(data);
+
+ //is a widget and is for android 3.0 and above version
+ if ((minSdkVersion >= MIN_TABLET_SDK_VERSION) && (isWidget))
+ {
+
+ xmlFolder = getXMLFolder(data);
+ //Get the xml filename responsible for the widget resources
+ ArrayList<String> xmlFilename = getWidgetResourceFilename(data);
+
+ for (String resourceFile : xmlFilename)
+ {
+ ValidationResultData resultData = new ValidationResultData();
+ //get the actual xml file element
+ xmlFileElement = getXMLFile(xmlFolder, resourceFile);
+
+ //check for the android:previewImage tag
+ hasTag = checkForPreviewTag(xmlFileElement);
+
+ if (hasTag)
+ {
+ //it is not a widget OR is not android 3.0+ version OR everything went fine
+ resultData.setSeverity(ValidationResultData.SEVERITY.OK);
+
+ }
+ else
+ {
+ //start build the result
+ resultData.setSeverity(getSeverityLevel());
+ resultData.setConditionID(getId());
+ resultData.setInfoURL(ConditionUtils.getDescriptionLink(getChecker().getId(),
+ getId(), valManagerConfig));
+ resultData
+ .setQuickFixSuggestion(CheckerNLS.MissingWidgetPreviewTagCondition_quickFix);
+ resultData
+ .setIssueDescription(CheckerNLS.MissingWidgetPreviewTagCondition_WarningMessage);
+
+ resultData.addFileToIssueLines(xmlFileElement.getFile(),
+ Collections.<Integer> emptyList());
+
+ }
+ resultDataList.add(resultData);
+
+ }
+ }
+ results.addAll(resultDataList);
+
+ }
+
+ @Override
+ public CanExecuteConditionStatus canExecute(ApplicationData data,
+ List<DeviceSpecification> deviceSpecs) throws PreflightingCheckerException
+ {
+ CanExecuteConditionStatus status =
+ new CanExecuteConditionStatus(IStatus.OK, CheckerPlugin.PLUGIN_ID, null);
+ status.setConditionId(getId());
+
+ XMLElement manElem = data.getManifestElement();
+ if (manElem == null)
+ {
+ status =
+ new CanExecuteConditionStatus(IStatus.ERROR, CheckerPlugin.PLUGIN_ID,
+ CheckerNLS.Invalid_ManifestFile);
+ status.setConditionId(getId());
+ }
+ else
+ {
+ Document manifestDoc = manElem.getDocument();
+
+ if (manifestDoc == null)
+ {
+ status =
+ new CanExecuteConditionStatus(IStatus.ERROR, CheckerPlugin.PLUGIN_ID,
+ CheckerNLS.Invalid_ManifestFile);
+ status.setConditionId(getId());
+ }
+ }
+
+ return status;
+ }
+
+ /**
+ * This Method check for the widget related intent in order to determine if the application
+ * is a widget
+ *
+ * @param data
+ * @return whether the application is a widget project or not
+ * @throws PreflightingCheckerException
+ */
+
+ private boolean isWidgetAplication(ApplicationData data) throws PreflightingCheckerException
+ {
+ Boolean actionFound = false;
+ XMLElement manElem = data.getManifestElement();
+ if (manElem != null)
+ {
+ Document manifestDoc = manElem.getDocument();
+ if (manifestDoc != null)
+ {
+ NodeList rcvLst = manifestDoc.getElementsByTagName("receiver"); //$NON-NLS-1$
+
+ for (int receiverIndex = 0; receiverIndex < rcvLst.getLength(); receiverIndex++)
+ {
+
+ NodeList intentFilterLst = rcvLst.item(receiverIndex).getChildNodes();
+ for (int intentFilterIndex = 0; intentFilterIndex < intentFilterLst.getLength(); intentFilterIndex++)
+ {
+ Node intentFilterNode = intentFilterLst.item(intentFilterIndex);
+ // get intent-filter nodes
+ if (intentFilterNode.getNodeName().equals("intent-filter")) //$NON-NLS-1$
+ {
+ NodeList actionLst = intentFilterNode.getChildNodes();
+ for (int actionListIndex = 0; actionListIndex < actionLst.getLength(); actionListIndex++)
+ {
+ Node actionNode = actionLst.item(actionListIndex);
+ // get action nodes
+ if (actionNode.getNodeName().equals("action")) //$NON-NLS-1$
+ {
+ NamedNodeMap map = actionNode.getAttributes();
+ // name attribute must be set to
+ // android.appwidget.action.APPWIDGET_UPDATE
+ Node nameAtr = map.getNamedItem("android:name"); //$NON-NLS-1$
+
+ try
+ {
+ if ((nameAtr != null)
+ && nameAtr
+ .getNodeValue()
+ .equals("android.appwidget.action.APPWIDGET_UPDATE")) //$NON-NLS-1$
+ {
+ actionFound = true;
+
+ }
+
+ }
+ catch (DOMException e)
+ {
+ // Error retrieving value of the action intent
+ throw new PreflightingCheckerException(
+ CheckerNLS.MainActivityChecker_Exception_Get_Action_Intent_Value,
+ e);
+ }
+ }
+
+ }
+
+ }
+
+ }
+ }
+ }
+ }
+ return actionFound;
+ }
+
+ /**
+ * This method retrieves the filename of the widget resouces XML pointed in the AndroidManifest
+ * @param data
+ * @return the filename
+ */
+
+ private ArrayList<String> getWidgetResourceFilename(ApplicationData data)
+ {
+
+ ArrayList<String> xmlFilename = new ArrayList<String>();
+
+ XMLElement manElem = data.getManifestElement();
+ if (manElem != null)
+ {
+ Document manifestDoc = manElem.getDocument();
+ if (manifestDoc != null)
+ {
+ NodeList actLst = manifestDoc.getElementsByTagName("receiver"); //$NON-NLS-1$
+
+ for (int k = 0; k < actLst.getLength(); k++)
+ {
+
+ NodeList intentFilterLst = actLst.item(k).getChildNodes();
+ for (int j = 0; j < intentFilterLst.getLength(); j++)
+ {
+ Node metaDataNode = intentFilterLst.item(j);
+ // get meta-data
+ if (metaDataNode.getNodeName().equals("meta-data")) //$NON-NLS-1$
+ {
+
+ NamedNodeMap map = metaDataNode.getAttributes();
+ // name attribute must be set to
+ // android.appwidget.provider
+ Node nameAtr = map.getNamedItem("android:name"); //$NON-NLS-1$
+
+ try
+ {
+ if ((nameAtr != null)
+ && nameAtr.getNodeValue().equals(
+ "android.appwidget.provider")) //$NON-NLS-1$
+ {
+ Node resourceAtr = map.getNamedItem("android:resource"); //$NON-NLS-1$
+ if (resourceAtr != null)
+ {
+
+ xmlFilename.add(resourceAtr.getNodeValue()
+ .substring(
+ xmlFilename.indexOf("@xml/")
+ + "@xml/".length() + 1));
+
+ }
+
+ }
+
+ }
+ catch (DOMException e)
+ {
+ // DO Nothing
+ }
+ }
+
+ }
+
+ }
+
+ }
+ }
+ return xmlFilename; //$NON-NLS-1$ //$NON-NLS-2$
+ }
+
+ /**
+ * This method gets the xml folder of the project
+ *
+ * @param data
+ * @return the xml folder
+ */
+
+ private FolderElement getXMLFolder(ApplicationData data)
+ {
+ List<Element> folderResElements =
+ ElementUtils.getElementByType(data.getRootElement(), Type.FOLDER_RES);
+ FolderElement xmlFolder = null;
+ ResourcesFolderElement resFolder =
+ folderResElements.size() > 0 ? (ResourcesFolderElement) folderResElements.get(0)
+ : null;
+
+ if (resFolder != null)
+ {
+ for (Element element : resFolder.getChildren())
+ {
+ if ((element instanceof FolderElement) && (element.getName().equals("xml"))) //$NON-NLS-1$
+ {
+ xmlFolder = (FolderElement) element;
+ }
+ }
+ }
+ return xmlFolder;
+ }
+
+ private XMLElement getXMLFile(FolderElement xmlFolder, String xmlFilename)
+ {
+ XMLElement xmlFileElement = null;
+ if (xmlFolder instanceof FolderElement)
+ {
+ for (Element element : xmlFolder.getChildren())
+ {
+ if (element.getName().equals(xmlFilename + ".xml") //$NON-NLS-1$
+ && (element instanceof XMLElement))
+ {
+ xmlFileElement = (XMLElement) element;
+ }
+
+ }
+ }
+ return xmlFileElement;
+ }
+
+ /**
+ * @param xmlFileElement
+ * @return
+ */
+ private boolean checkForPreviewTag(XMLElement xmlFileElement)
+ {
+ boolean hasPreviewImage = false;
+ if (xmlFileElement != null)
+ {
+ Document resourceDoc = xmlFileElement.getDocument();
+ if (resourceDoc != null)
+ {
+ NodeList provLst = resourceDoc.getElementsByTagName("appwidget-provider"); //$NON-NLS-1$
+
+ for (int k = 0; k < provLst.getLength(); k++)
+ {
+
+ NamedNodeMap atrMap = provLst.item(k).getAttributes();
+ Node previewImageNode = atrMap.getNamedItem("android:previewImage"); //$NON-NLS-1$
+
+ if (previewImageNode != null)
+ {
+ hasPreviewImage = true;
+ }
+ }
+ }
+ }
+ return hasPreviewImage;
+
+ }
+
+}
diff --git a/src/plugins/preflighting.core/.classpath b/src/plugins/preflighting.core/.classpath
new file mode 100644
index 0000000..736d14c
--- /dev/null
+++ b/src/plugins/preflighting.core/.classpath
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.5"/>
+ <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry kind="src" path="etc"/>
+ <classpathentry kind="lib" path="lib/log4j-1.2.14.jar"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/src/plugins/preflighting.core/.project b/src/plugins/preflighting.core/.project
new file mode 100644
index 0000000..e07f314
--- /dev/null
+++ b/src/plugins/preflighting.core/.project
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>com.motorolamobility.preflighting.core</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.ManifestBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.SchemaBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.pde.PluginNature</nature>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+</projectDescription>
diff --git a/src/plugins/preflighting.core/META-INF/MANIFEST.MF b/src/plugins/preflighting.core/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..ee1a5c1
--- /dev/null
+++ b/src/plugins/preflighting.core/META-INF/MANIFEST.MF
@@ -0,0 +1,45 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: %Bundle-Name
+Bundle-SymbolicName: com.motorolamobility.preflighting.core;singleton:=true
+Bundle-Version: 2.0.0.qualifier
+Bundle-Activator: com.motorolamobility.preflighting.core.PreflightingCorePlugin
+Bundle-Vendor: %Bundle-Vendor
+Require-Bundle: org.eclipse.core.runtime,
+ org.eclipse.jdt.core,
+ org.apache.xerces,
+ org.eclipse.ui.ide
+Bundle-RequiredExecutionEnvironment: JavaSE-1.6
+Bundle-ActivationPolicy: lazy
+Export-Package: com.motorolamobility.preflighting.core;uses:="org.eclipse.core.runtime,org.osgi.framework",
+ com.motorolamobility.preflighting.core.applicationdata;uses:="org.w3c.dom",
+ com.motorolamobility.preflighting.core.checker;
+ uses:="org.eclipse.core.runtime,
+ com.motorolamobility.preflighting.core,
+ com.motorolamobility.preflighting.core.validation,
+ com.motorolamobility.preflighting.core.devicespecification,
+ com.motorolamobility.preflighting.core.applicationdata",
+ com.motorolamobility.preflighting.core.checker.condition;
+ uses:="com.motorolamobility.preflighting.core.checker,
+ org.eclipse.core.runtime,
+ com.motorolamobility.preflighting.core.validation,
+ com.motorolamobility.preflighting.core.devicespecification,
+ com.motorolamobility.preflighting.core.applicationdata",
+ com.motorolamobility.preflighting.core.checker.parameter,
+ com.motorolamobility.preflighting.core.devicelayoutspecification;uses:="com.motorolamobility.preflighting.core.internal.devicelayoutspecification",
+ com.motorolamobility.preflighting.core.devicespecification;uses:="com.motorolamobility.preflighting.core.internal.devicespecification,com.motorolamobility.preflighting.core.source.model",
+ com.motorolamobility.preflighting.core.devicespecification.internal;x-friends:="com.motorolamobility.preflighting.checkers",
+ com.motorolamobility.preflighting.core.exception,
+ com.motorolamobility.preflighting.core.internal.cond.utils;x-friends:="com.motorolamobility.preflighting.checkers",
+ com.motorolamobility.preflighting.core.internal.conditions;x-friends:="com.motorolamobility.preflighting.checkers",
+ com.motorolamobility.preflighting.core.internal.utils,
+ com.motorolamobility.preflighting.core.logging,
+ com.motorolamobility.preflighting.core.permissionfeature,
+ com.motorolamobility.preflighting.core.source.model;uses:="org.eclipse.jdt.core.dom,com.motorolamobility.preflighting.core.applicationdata",
+ com.motorolamobility.preflighting.core.utils;uses:="com.motorolamobility.preflighting.core.internal.devicelayoutspecification,org.w3c.dom",
+ com.motorolamobility.preflighting.core.validation;uses:="com.motorolamobility.preflighting.core.internal.checker,com.motorolamobility.preflighting.core.devicespecification",
+ com.motorolamobility.preflighting.core.verbose
+Bundle-Localization: plugin
+Bundle-ClassPath: lib/log4j-1.2.14.jar,
+ .
+
diff --git a/src/plugins/preflighting.core/about.ini b/src/plugins/preflighting.core/about.ini
new file mode 100644
index 0000000..aedf9a9
--- /dev/null
+++ b/src/plugins/preflighting.core/about.ini
@@ -0,0 +1,24 @@
+# about.ini
+# contains information about a feature
+# java.io.Properties file (ISO 8859-1 with "\" escapes)
+# "%key" are externalized strings defined in about.properties
+# This file does not need to be translated.
+
+# Property "aboutText" contains blurb for "About" dialog (translated)
+aboutText=%blurb
+
+# Property "windowImage" contains path to window icon (16x16)
+# needed for primary features only
+
+# Property "featureImage" contains path to feature image (32x32)
+featureImage=resources/plate32.png
+
+# Property "aboutImage" contains path to product image (500x330 or 115x164)
+# needed for primary features only
+
+# Property "appName" contains name of the application (translated)
+# needed for primary features only
+
+# Property "welcomePerspective" contains the id of the perspective in which the
+# welcome page is to be opened.
+# optional \ No newline at end of file
diff --git a/src/plugins/preflighting.core/about.properties b/src/plugins/preflighting.core/about.properties
new file mode 100644
index 0000000..0725bb7
--- /dev/null
+++ b/src/plugins/preflighting.core/about.properties
@@ -0,0 +1,7 @@
+blurb=MOTODEV Studio App Validator Tool\n\
+\n\
+Version: {featureVersion}\n\
+\n\
+Build id: {0}\n\
+\n\
+Copyright (C) 2012 The Android Open Source Project \ No newline at end of file
diff --git a/src/plugins/preflighting.core/apktool/apktool.jar b/src/plugins/preflighting.core/apktool/apktool.jar
new file mode 100644
index 0000000..aa5d5b2
--- /dev/null
+++ b/src/plugins/preflighting.core/apktool/apktool.jar
Binary files differ
diff --git a/src/plugins/preflighting.core/build.properties b/src/plugins/preflighting.core/build.properties
new file mode 100644
index 0000000..1e32051
--- /dev/null
+++ b/src/plugins/preflighting.core/build.properties
@@ -0,0 +1,16 @@
+source.. = src/
+output.. = bin/
+bin.includes = META-INF/,\
+ .,\
+ plugin.xml,\
+ plugin.properties,\
+ files/,\
+ etc/,\
+ about.ini,\
+ about.properties,\
+ resources/,\
+ devices/,\
+ about.mappings,\
+ lib/,\
+ apktool/,\
+ schema/
diff --git a/src/plugins/preflighting.core/configuration_files/appvalidator.cfg b/src/plugins/preflighting.core/configuration_files/appvalidator.cfg
new file mode 100644
index 0000000..361a677
--- /dev/null
+++ b/src/plugins/preflighting.core/configuration_files/appvalidator.cfg
@@ -0,0 +1,4 @@
+base_url=http://developer.motorola.com/docstools/library/motodev-app-validator
+info_url_query=/##PARAM_1#-#PARAM_2#
+#apktool or aapt
+apk_extraction_mode=apktool
diff --git a/src/plugins/preflighting.core/configuration_files/macosx_x86/appvalidator.cfg b/src/plugins/preflighting.core/configuration_files/macosx_x86/appvalidator.cfg
new file mode 100644
index 0000000..258ee70
--- /dev/null
+++ b/src/plugins/preflighting.core/configuration_files/macosx_x86/appvalidator.cfg
@@ -0,0 +1,4 @@
+base_url=http://developer.motorola.com/docstools/library/motodev-app-validator
+info_url_query=/##PARAM_1#-#PARAM_2#
+#apktool or aapt
+apk_extraction_mode=aapt
diff --git a/src/plugins/preflighting.core/devices/a1260.xml b/src/plugins/preflighting.core/devices/a1260.xml
new file mode 100644
index 0000000..a7392e8
--- /dev/null
+++ b/src/plugins/preflighting.core/devices/a1260.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<d:layout-devices xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:d="http://schemas.android.com/sdk/android/layout-devices/1">
+
+ <d:device id="a1260" name="MOTO A1260" provider="Motorola Mobility, Inc.">
+
+ <!--Product specifications: http://developer.motorola.com/products/a1260/-->
+
+ <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>ldpi</d:pixel-density>
+ <d:touch-type>stylus</d:touch-type>
+ <d:text-input-method>nokeys</d:text-input-method>
+ <d:keyboard-state>keyssoft</d:keyboard-state>
+ <d:nav-method>nonav</d:nav-method>
+ <d:screen-dimension>
+ <d:size>240</d:size>
+ <d:size>400</d:size>
+ </d:screen-dimension>
+ <d:xdpi>120</d:xdpi>
+ <d:ydpi>120</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:layout-devices> \ No newline at end of file
diff --git a/src/plugins/preflighting.core/devices/a1680.xml b/src/plugins/preflighting.core/devices/a1680.xml
new file mode 100644
index 0000000..d60ef6d
--- /dev/null
+++ b/src/plugins/preflighting.core/devices/a1680.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<d:layout-devices xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:d="http://schemas.android.com/sdk/android/layout-devices/1">
+
+ <d:device id="a1680" name="MOTO A1680" provider="Motorola Mobility, Inc.">
+
+ <!--Product specifications: http://developer.motorola.com/products/a1680/-->
+
+ <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>stylus</d:touch-type>
+ <d:text-input-method>nokeys</d:text-input-method>
+ <d:keyboard-state>keyssoft</d:keyboard-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>240</d:xdpi>
+ <d:ydpi>240</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:layout-devices> \ No newline at end of file
diff --git a/src/plugins/preflighting.core/devices/admiral-xt603.xml b/src/plugins/preflighting.core/devices/admiral-xt603.xml
new file mode 100644
index 0000000..abd5374
--- /dev/null
+++ b/src/plugins/preflighting.core/devices/admiral-xt603.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<d:layout-devices xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:d="http://schemas.android.com/sdk/android/layout-devices/1">
+
+ <d:device id="admiral-xt603" name="ADMIRAL XT603" provider="Motorola Mobility, Inc.">
+
+ <!--Product specifications: http://developer.motorola.com/products/admiral-xt603/-->
+
+ <d:default>
+ <d:screen-size>small</d:screen-size>
+ <d:screen-ratio>notlong</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:text-input-method>qwerty</d:text-input-method>
+ <d:keyboard-state>keysexposed</d:keyboard-state>
+ <d:nav-method>nonav</d:nav-method>
+ <d:screen-dimension>
+ <d:size>480</d:size>
+ <d:size>640</d:size>
+ </d:screen-dimension>
+ <d:xdpi>240</d:xdpi>
+ <d:ydpi>240</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:keyboard-state>keyssoft</d:keyboard-state>
+ </d:config>
+ <d:supported-features>
+ <d:feature>android.hardware.bluetooth</d:feature>
+ <d:feature>android.hardware.camera</d:feature>
+ <d:feature>android.hardware.camera.autofocus</d:feature>
+ <d:feature>android.hardware.camera.flash</d:feature>
+ <d:feature>android.hardware.location</d:feature>
+ <d:feature>android.hardware.location.gps</d:feature>
+ <d:feature>android.hardware.location.network</d:feature>
+ <d:feature>android.hardware.microphone</d:feature>
+ <d:feature>android.hardware.sensor.accelerometer</d:feature>
+ <d:feature>android.hardware.sensor.compass</d:feature>
+ <d:feature>android.hardware.sensor.light</d:feature>
+ <d:feature>android.hardware.sensor.proximity</d:feature>
+ <d:feature>android.hardware.telephony</d:feature>
+ <d:feature>android.hardware.telephony.cdma</d:feature>
+ <d:feature>android.hardware.touchscreen</d:feature>
+ <d:feature>android.hardware.touchscreen.multitouch</d:feature>
+ <d:feature>android.hardware.touchscreen.multitouch.distinct</d:feature>
+ <d:feature>android.software.live_wallpaper</d:feature>
+ <d:feature>android.software.sip</d:feature>
+ <d:feature>android.software.sip.voip</d:feature>
+ </d:supported-features>
+
+ </d:device>
+
+</d:layout-devices> \ No newline at end of file
diff --git a/src/plugins/preflighting.core/devices/atrix-2-mb865.xml b/src/plugins/preflighting.core/devices/atrix-2-mb865.xml
new file mode 100644
index 0000000..b34fb9e
--- /dev/null
+++ b/src/plugins/preflighting.core/devices/atrix-2-mb865.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<d:layout-devices xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:d="http://schemas.android.com/sdk/android/layout-devices/1">
+
+ <d:device id="atrix-2-mb865" name="ATRIX 2 MB865" provider="Motorola Mobility, Inc.">
+
+ <!--Product specifications: http://developer.motorola.com/products/atrix-2-mb865/-->
+
+ <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:text-input-method>nokeys</d:text-input-method>
+ <d:keyboard-state>keyssoft</d:keyboard-state>
+ <d:nav-method>nonav</d:nav-method>
+ <d:screen-dimension>
+ <d:size>540</d:size>
+ <d:size>960</d:size>
+ </d:screen-dimension>
+ <d:xdpi>240</d:xdpi>
+ <d:ydpi>240</d:ydpi>
+ </d:default>
+ <d:supported-features>
+ <d:feature>android.hardware.bluetooth</d:feature>
+ <d:feature>android.hardware.camera</d:feature>
+ <d:feature>android.hardware.camera.autofocus</d:feature>
+ <d:feature>android.hardware.camera.flash</d:feature>
+ <d:feature>android.hardware.camera.front</d:feature>
+ <d:feature>android.hardware.location</d:feature>
+ <d:feature>android.hardware.location.gps</d:feature>
+ <d:feature>android.hardware.location.network</d:feature>
+ <d:feature>android.hardware.microphone</d:feature>
+ <d:feature>android.hardware.sensor.accelerometer</d:feature>
+ <d:feature>android.hardware.sensor.compass</d:feature>
+ <d:feature>android.hardware.sensor.gyroscope</d:feature>
+ <d:feature>android.hardware.sensor.light</d:feature>
+ <d:feature>android.hardware.sensor.proximity</d:feature>
+ <d:feature>android.hardware.telephony</d:feature>
+ <d:feature>android.hardware.telephony.gsm</d:feature>
+ <d:feature>android.hardware.touchscreen</d:feature>
+ <d:feature>android.hardware.touchscreen.multitouch</d:feature>
+ <d:feature>android.hardware.touchscreen.multitouch.distinct</d:feature>
+ <d:feature>android.hardware.wifi</d:feature>
+ <d:feature>android.software.live_wallpaper</d:feature>
+ </d:supported-features>
+
+ </d:device>
+
+</d:layout-devices> \ No newline at end of file
diff --git a/src/plugins/preflighting.core/devices/atrix-2-me865.xml b/src/plugins/preflighting.core/devices/atrix-2-me865.xml
new file mode 100644
index 0000000..b3a4cbe
--- /dev/null
+++ b/src/plugins/preflighting.core/devices/atrix-2-me865.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<d:layout-devices xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:d="http://schemas.android.com/sdk/android/layout-devices/1">
+
+ <d:device id="atrix-2-me865" name="ATRIX 2 ME865" provider="Motorola Mobility, Inc.">
+
+ <!--Product specifications: http://developer.motorola.com/products/atrix-2-me865/-->
+
+ <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:text-input-method>nokeys</d:text-input-method>
+ <d:keyboard-state>keyssoft</d:keyboard-state>
+ <d:nav-method>nonav</d:nav-method>
+ <d:screen-dimension>
+ <d:size>540</d:size>
+ <d:size>960</d:size>
+ </d:screen-dimension>
+ <d:xdpi>240</d:xdpi>
+ <d:ydpi>240</d:ydpi>
+ </d:default>
+ <d:supported-features>
+ <d:feature>android.hardware.bluetooth</d:feature>
+ <d:feature>android.hardware.camera</d:feature>
+ <d:feature>android.hardware.camera.autofocus</d:feature>
+ <d:feature>android.hardware.camera.flash</d:feature>
+ <d:feature>android.hardware.camera.front</d:feature>
+ <d:feature>android.hardware.location</d:feature>
+ <d:feature>android.hardware.location.gps</d:feature>
+ <d:feature>android.hardware.location.network</d:feature>
+ <d:feature>android.hardware.microphone</d:feature>
+ <d:feature>android.hardware.sensor.accelerometer</d:feature>
+ <d:feature>android.hardware.sensor.compass</d:feature>
+ <d:feature>android.hardware.sensor.gyroscope</d:feature>
+ <d:feature>android.hardware.sensor.light</d:feature>
+ <d:feature>android.hardware.sensor.proximity</d:feature>
+ <d:feature>android.hardware.telephony</d:feature>
+ <d:feature>android.hardware.telephony.gsm</d:feature>
+ <d:feature>android.hardware.touchscreen</d:feature>
+ <d:feature>android.hardware.touchscreen.multitouch</d:feature>
+ <d:feature>android.hardware.touchscreen.multitouch.distinct</d:feature>
+ <d:feature>android.hardware.wifi</d:feature>
+ <d:feature>android.software.live_wallpaper</d:feature>
+ </d:supported-features>
+
+ </d:device>
+
+</d:layout-devices> \ No newline at end of file
diff --git a/src/plugins/preflighting.core/devices/atrix-mb860.xml b/src/plugins/preflighting.core/devices/atrix-mb860.xml
new file mode 100644
index 0000000..8151edc
--- /dev/null
+++ b/src/plugins/preflighting.core/devices/atrix-mb860.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<d:layout-devices xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:d="http://schemas.android.com/sdk/android/layout-devices/1">
+
+ <d:device id="atrix-mb860" name="ATRIX 4G MB860" provider="Motorola Mobility, Inc.">
+
+ <!--Product specifications: http://developer.motorola.com/products/atrix-mb860/-->
+
+ <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:text-input-method>nokeys</d:text-input-method>
+ <d:keyboard-state>keyssoft</d:keyboard-state>
+ <d:nav-method>nonav</d:nav-method>
+ <d:screen-dimension>
+ <d:size>540</d:size>
+ <d:size>960</d:size>
+ </d:screen-dimension>
+ <d:xdpi>240</d:xdpi>
+ <d:ydpi>240</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:supported-features>
+ <d:feature>android.hardware.bluetooth</d:feature>
+ <d:feature>android.hardware.camera</d:feature>
+ <d:feature>android.hardware.camera.autofocus</d:feature>
+ <d:feature>android.hardware.camera.flash</d:feature>
+ <d:feature>android.hardware.camera.front</d:feature>
+ <d:feature>android.hardware.location</d:feature>
+ <d:feature>android.hardware.location.gps</d:feature>
+ <d:feature>android.hardware.location.network</d:feature>
+ <d:feature>android.hardware.microphone</d:feature>
+ <d:feature>android.hardware.sensor.accelerometer</d:feature>
+ <d:feature>android.hardware.sensor.compass</d:feature>
+ <d:feature>android.hardware.sensor.light</d:feature>
+ <d:feature>android.hardware.sensor.proximity</d:feature>
+ <d:feature>android.hardware.telephony</d:feature>
+ <d:feature>android.hardware.telephony.gsm</d:feature>
+ <d:feature>android.hardware.touchscreen</d:feature>
+ <d:feature>android.hardware.touchscreen.multitouch</d:feature>
+ <d:feature>android.hardware.touchscreen.multitouch.distinct</d:feature>
+ <d:feature>android.hardware.wifi</d:feature>
+ <d:feature>android.software.live_wallpaper</d:feature>
+ <d:feature>com.motorola.hardware.fingerprint</d:feature>
+ </d:supported-features>
+
+ </d:device>
+
+</d:layout-devices> \ No newline at end of file
diff --git a/src/plugins/preflighting.core/devices/atrix-me860.xml b/src/plugins/preflighting.core/devices/atrix-me860.xml
new file mode 100644
index 0000000..f8c7962
--- /dev/null
+++ b/src/plugins/preflighting.core/devices/atrix-me860.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<d:layout-devices xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:d="http://schemas.android.com/sdk/android/layout-devices/1">
+
+ <d:device id="atrix-me860" name="ATRIX ME860, MB861" provider="Motorola Mobility, Inc.">
+
+ <!--Product specifications: http://developer.motorola.com/products/atrix-me860/-->
+
+ <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:text-input-method>nokeys</d:text-input-method>
+ <d:keyboard-state>keyssoft</d:keyboard-state>
+ <d:nav-method>nonav</d:nav-method>
+ <d:screen-dimension>
+ <d:size>540</d:size>
+ <d:size>960</d:size>
+ </d:screen-dimension>
+ <d:xdpi>240</d:xdpi>
+ <d:ydpi>240</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:supported-features>
+ <d:feature>android.hardware.bluetooth</d:feature>
+ <d:feature>android.hardware.camera</d:feature>
+ <d:feature>android.hardware.camera.autofocus</d:feature>
+ <d:feature>android.hardware.camera.flash</d:feature>
+ <d:feature>android.hardware.camera.front</d:feature>
+ <d:feature>android.hardware.location</d:feature>
+ <d:feature>android.hardware.location.gps</d:feature>
+ <d:feature>android.hardware.location.network</d:feature>
+ <d:feature>android.hardware.microphone</d:feature>
+ <d:feature>android.hardware.sensor.accelerometer</d:feature>
+ <d:feature>android.hardware.sensor.compass</d:feature>
+ <d:feature>android.hardware.sensor.light</d:feature>
+ <d:feature>android.hardware.sensor.proximity</d:feature>
+ <d:feature>android.hardware.telephony</d:feature>
+ <d:feature>android.hardware.telephony.gsm</d:feature>
+ <d:feature>android.hardware.touchscreen</d:feature>
+ <d:feature>android.hardware.touchscreen.multitouch</d:feature>
+ <d:feature>android.hardware.touchscreen.multitouch.distinct</d:feature>
+ <d:feature>android.hardware.wifi</d:feature>
+ <d:feature>android.software.live_wallpaper</d:feature>
+ <d:feature>com.motorola.hardware.fingerprint</d:feature>
+ </d:supported-features>
+
+ </d:device>
+
+</d:layout-devices> \ No newline at end of file
diff --git a/src/plugins/preflighting.core/devices/backflip-mb300.xml b/src/plugins/preflighting.core/devices/backflip-mb300.xml
new file mode 100644
index 0000000..71d2cf3
--- /dev/null
+++ b/src/plugins/preflighting.core/devices/backflip-mb300.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<d:layout-devices xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:d="http://schemas.android.com/sdk/android/layout-devices/1">
+
+ <d:device id="backflip-mb300" name="BACKFLIP MB300" provider="Motorola Mobility, Inc.">
+
+ <!--Product specifications: http://developer.motorola.com/products/backflip-mb300/-->
+
+ <d:default>
+ <d:screen-size>normal</d:screen-size>
+ <d:screen-ratio>notlong</d:screen-ratio>
+ <d:screen-orientation>port</d:screen-orientation>
+ <d:pixel-density>mdpi</d:pixel-density>
+ <d:touch-type>finger</d:touch-type>
+ <d:text-input-method>qwerty</d:text-input-method>
+ <d:nav-method>trackball</d:nav-method>
+ <d:screen-dimension>
+ <d:size>320</d:size>
+ <d:size>480</d:size>
+ </d:screen-dimension>
+ <d:xdpi>160</d:xdpi>
+ <d:ydpi>160</d:ydpi>
+ </d:default>
+ <d:config name="Portrait, closed">
+ <d:keyboard-state>keyssoft</d:keyboard-state>
+ <d:screen-orientation>port</d:screen-orientation>
+ </d:config>
+ <d:config name="Landscape, closed">
+ <d:keyboard-state>keyssoft</d:keyboard-state>
+ <d:screen-orientation>land</d:screen-orientation>
+ </d:config>
+ <d:config name="Landscape, open">
+ <d:keyboard-state>keysexposed</d:keyboard-state>
+ <d:screen-orientation>land</d:screen-orientation>
+ <d:nav-state>navexposed</d:nav-state>
+ </d:config>
+
+ </d:device>
+
+</d:layout-devices> \ No newline at end of file
diff --git a/src/plugins/preflighting.core/devices/backflip-me600.xml b/src/plugins/preflighting.core/devices/backflip-me600.xml
new file mode 100644
index 0000000..28e4d9f
--- /dev/null
+++ b/src/plugins/preflighting.core/devices/backflip-me600.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<d:layout-devices xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:d="http://schemas.android.com/sdk/android/layout-devices/1">
+
+ <d:device id="backflip-me600" name="BACKFLIP ME600" provider="Motorola Mobility, Inc.">
+
+ <!--Product specifications: http://developer.motorola.com/products/backflip-me600/-->
+
+ <d:default>
+ <d:screen-size>normal</d:screen-size>
+ <d:screen-ratio>notlong</d:screen-ratio>
+ <d:screen-orientation>port</d:screen-orientation>
+ <d:pixel-density>mdpi</d:pixel-density>
+ <d:touch-type>finger</d:touch-type>
+ <d:text-input-method>qwerty</d:text-input-method>
+ <d:nav-method>trackball</d:nav-method>
+ <d:screen-dimension>
+ <d:size>320</d:size>
+ <d:size>480</d:size>
+ </d:screen-dimension>
+ <d:xdpi>160</d:xdpi>
+ <d:ydpi>160</d:ydpi>
+ </d:default>
+ <d:config name="Portrait, closed">
+ <d:keyboard-state>keyssoft</d:keyboard-state>
+ <d:screen-orientation>port</d:screen-orientation>
+ </d:config>
+ <d:config name="Landscape, closed">
+ <d:keyboard-state>keyssoft</d:keyboard-state>
+ <d:screen-orientation>land</d:screen-orientation>
+ </d:config>
+ <d:config name="Landscape, open">
+ <d:keyboard-state>keysexposed</d:keyboard-state>
+ <d:screen-orientation>land</d:screen-orientation>
+ <d:nav-state>navexposed</d:nav-state>
+ </d:config>
+
+ </d:device>
+
+</d:layout-devices> \ No newline at end of file
diff --git a/src/plugins/preflighting.core/devices/bravo.xml b/src/plugins/preflighting.core/devices/bravo.xml
new file mode 100644
index 0000000..a87314e
--- /dev/null
+++ b/src/plugins/preflighting.core/devices/bravo.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<d:layout-devices xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:d="http://schemas.android.com/sdk/android/layout-devices/1">
+
+ <d:device id="bravo" name="BRAVO MB520" provider="Motorola Mobility, Inc.">
+
+ <!--Product specifications: http://developer.motorola.com/products/bravo/-->
+
+ <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:text-input-method>nokeys</d:text-input-method>
+ <d:keyboard-state>keyssoft</d:keyboard-state>
+ <d:nav-method>nonav</d:nav-method>
+ <d:screen-dimension>
+ <d:size>480</d:size>
+ <d:size>854</d:size>
+ </d:screen-dimension>
+ <d:xdpi>240</d:xdpi>
+ <d:ydpi>240</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:supported-features>
+ <d:feature>android.hardware.bluetooth</d:feature>
+ <d:feature>android.hardware.camera</d:feature>
+ <d:feature>android.hardware.camera.autofocus</d:feature>
+ <d:feature>android.hardware.location</d:feature>
+ <d:feature>android.hardware.location.gps</d:feature>
+ <d:feature>android.hardware.location.network</d:feature>
+ <d:feature>android.hardware.microphone</d:feature>
+ <d:feature>android.hardware.sensor.accelerometer</d:feature>
+ <d:feature>android.hardware.sensor.compass</d:feature>
+ <d:feature>android.hardware.sensor.light</d:feature>
+ <d:feature>android.hardware.sensor.proximity</d:feature>
+ <d:feature>android.hardware.telephony</d:feature>
+ <d:feature>android.hardware.telephony.gsm</d:feature>
+ <d:feature>android.hardware.touchscreen</d:feature>
+ <d:feature>android.hardware.touchscreen.multitouch</d:feature>
+ <d:feature>android.hardware.touchscreen.multitouch.distinct</d:feature>
+ <d:feature>android.hardware.wifi</d:feature>
+ <d:feature>android.software.live_wallpaper</d:feature>
+ </d:supported-features>
+
+ </d:device>
+
+</d:layout-devices> \ No newline at end of file
diff --git a/src/plugins/preflighting.core/devices/charm-mb502.xml b/src/plugins/preflighting.core/devices/charm-mb502.xml
new file mode 100644
index 0000000..0d59fe3
--- /dev/null
+++ b/src/plugins/preflighting.core/devices/charm-mb502.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<d:layout-devices xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:d="http://schemas.android.com/sdk/android/layout-devices/1">
+
+ <d:device id="charm-mb502" name="CHARM MB502" provider="Motorola Mobility, Inc.">
+
+ <!--Product specifications: http://developer.motorola.com/products/charm-mb502/-->
+
+ <d:default>
+ <d:screen-size>small</d:screen-size>
+ <d:screen-ratio>notlong</d:screen-ratio>
+ <d:screen-orientation>land</d:screen-orientation>
+ <d:pixel-density>ldpi</d:pixel-density>
+ <d:touch-type>finger</d:touch-type>
+ <d:text-input-method>qwerty</d:text-input-method>
+ <d:keyboard-state>keysexposed</d:keyboard-state>
+ <d:nav-method>trackball</d:nav-method>
+ <d:screen-dimension>
+ <d:size>320</d:size>
+ <d:size>240</d:size>
+ </d:screen-dimension>
+ <d:xdpi>120</d:xdpi>
+ <d:ydpi>120</d:ydpi>
+ </d:default>
+ <d:config name="Portrait">
+ <d:screen-orientation>port</d:screen-orientation>
+ <d:keyboard-state>keyssoft</d:keyboard-state>
+ </d:config>
+ <d:config name="Landscape">
+ <d:screen-orientation>land</d:screen-orientation>
+ </d:config>
+
+ </d:device>
+
+</d:layout-devices> \ No newline at end of file
diff --git a/src/plugins/preflighting.core/devices/charm-me502.xml b/src/plugins/preflighting.core/devices/charm-me502.xml
new file mode 100644
index 0000000..5bb173a
--- /dev/null
+++ b/src/plugins/preflighting.core/devices/charm-me502.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<d:layout-devices xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:d="http://schemas.android.com/sdk/android/layout-devices/1">
+
+ <d:device id="charm-me502" name="CHARM ME502" provider="Motorola Mobility, Inc.">
+
+ <!--Product specifications: http://developer.motorola.com/products/charm-me502/-->
+
+ <d:default>
+ <d:screen-size>small</d:screen-size>
+ <d:screen-ratio>notlong</d:screen-ratio>
+ <d:screen-orientation>land</d:screen-orientation>
+ <d:pixel-density>ldpi</d:pixel-density>
+ <d:touch-type>finger</d:touch-type>
+ <d:text-input-method>qwerty</d:text-input-method>
+ <d:keyboard-state>keysexposed</d:keyboard-state>
+ <d:nav-method>trackball</d:nav-method>
+ <d:screen-dimension>
+ <d:size>320</d:size>
+ <d:size>240</d:size>
+ </d:screen-dimension>
+ <d:xdpi>120</d:xdpi>
+ <d:ydpi>120</d:ydpi>
+ </d:default>
+ <d:config name="Portrait">
+ <d:screen-orientation>port</d:screen-orientation>
+ <d:keyboard-state>keyssoft</d:keyboard-state>
+ </d:config>
+ <d:config name="Landscape">
+ <d:screen-orientation>land</d:screen-orientation>
+ </d:config>
+
+ </d:device>
+
+</d:layout-devices> \ No newline at end of file
diff --git a/src/plugins/preflighting.core/devices/citrus.xml b/src/plugins/preflighting.core/devices/citrus.xml
new file mode 100644
index 0000000..6d99c3d
--- /dev/null
+++ b/src/plugins/preflighting.core/devices/citrus.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<d:layout-devices xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:d="http://schemas.android.com/sdk/android/layout-devices/1">
+
+ <d:device id="citrus" name="CITRUS WX445" provider="Motorola Mobility, Inc.">
+
+ <!--Product specifications: http://developer.motorola.com/products/citrus/-->
+
+ <d:default>
+ <d:screen-size>small</d:screen-size>
+ <d:screen-ratio>notlong</d:screen-ratio>
+ <d:screen-orientation>port</d:screen-orientation>
+ <d:pixel-density>ldpi</d:pixel-density>
+ <d:touch-type>finger</d:touch-type>
+ <d:text-input-method>nokeys</d:text-input-method>
+ <d:keyboard-state>keyssoft</d:keyboard-state>
+ <d:nav-method>trackball</d:nav-method>
+ <d:screen-dimension>
+ <d:size>240</d:size>
+ <d:size>320</d:size>
+ </d:screen-dimension>
+ <d:xdpi>120</d:xdpi>
+ <d:ydpi>120</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:layout-devices> \ No newline at end of file
diff --git a/src/plugins/preflighting.core/devices/cliq.xml b/src/plugins/preflighting.core/devices/cliq.xml
new file mode 100644
index 0000000..87221c9
--- /dev/null
+++ b/src/plugins/preflighting.core/devices/cliq.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<d:layout-devices xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:d="http://schemas.android.com/sdk/android/layout-devices/1">
+
+ <d:device id="cliq" name="CLIQ MB200" provider="Motorola Mobility, Inc.">
+
+ <!--Product specifications: http://developer.motorola.com/products/cliq/-->
+
+ <d:default>
+ <d:screen-size>normal</d:screen-size>
+ <d:screen-ratio>notlong</d:screen-ratio>
+ <d:screen-orientation>port</d:screen-orientation>
+ <d:pixel-density>mdpi</d:pixel-density>
+ <d:touch-type>finger</d:touch-type>
+ <d:text-input-method>qwerty</d:text-input-method>
+ <d:nav-method>dpad</d:nav-method>
+ <d:screen-dimension>
+ <d:size>320</d:size>
+ <d:size>480</d:size>
+ </d:screen-dimension>
+ <d:xdpi>160</d:xdpi>
+ <d:ydpi>160</d:ydpi>
+ </d:default>
+ <d:config name="Portrait, closed">
+ <d:keyboard-state>keyssoft</d:keyboard-state>
+ <d:screen-orientation>port</d:screen-orientation>
+ </d:config>
+ <d:config name="Landscape, closed">
+ <d:keyboard-state>keyssoft</d:keyboard-state>
+ <d:screen-orientation>land</d:screen-orientation>
+ </d:config>
+ <d:config name="Landscape, open">
+ <d:keyboard-state>keysexposed</d:keyboard-state>
+ <d:screen-orientation>land</d:screen-orientation>
+ <d:nav-state>navexposed</d:nav-state>
+ </d:config>
+
+ </d:device>
+
+</d:layout-devices> \ No newline at end of file
diff --git a/src/plugins/preflighting.core/devices/cliq2.xml b/src/plugins/preflighting.core/devices/cliq2.xml
new file mode 100644
index 0000000..a90ab85
--- /dev/null
+++ b/src/plugins/preflighting.core/devices/cliq2.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<d:layout-devices xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:d="http://schemas.android.com/sdk/android/layout-devices/1">
+
+ <d:device id="cliq2" name="CLIQ 2 MB611" provider="Motorola Mobility, Inc.">
+
+ <!--Product specifications: http://developer.motorola.com/products/cliq2/-->
+
+ <d:default>
+ <d:screen-size>large</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:text-input-method>qwerty</d:text-input-method>
+ <d:nav-method>dpad</d:nav-method>
+ <d:screen-dimension>
+ <d:size>480</d:size>
+ <d:size>854</d:size>
+ </d:screen-dimension>
+ <d:xdpi>240</d:xdpi>
+ <d:ydpi>240</d:ydpi>
+ </d:default>
+ <d:config name="Portrait, closed">
+ <d:keyboard-state>keyssoft</d:keyboard-state>
+ <d:screen-orientation>port</d:screen-orientation>
+ </d:config>
+ <d:config name="Landscape, closed">
+ <d:keyboard-state>keyssoft</d:keyboard-state>
+ <d:screen-orientation>land</d:screen-orientation>
+ </d:config>
+ <d:config name="Landscape, open">
+ <d:keyboard-state>keysexposed</d:keyboard-state>
+ <d:screen-orientation>land</d:screen-orientation>
+ <d:nav-state>navexposed</d:nav-state>
+ </d:config>
+
+ </d:device>
+
+</d:layout-devices> \ No newline at end of file
diff --git a/src/plugins/preflighting.core/devices/cliqxt.xml b/src/plugins/preflighting.core/devices/cliqxt.xml
new file mode 100644
index 0000000..21c5c24
--- /dev/null
+++ b/src/plugins/preflighting.core/devices/cliqxt.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<d:layout-devices xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:d="http://schemas.android.com/sdk/android/layout-devices/1">
+
+ <d:device id="cliqxt" name="CLIQ XT MB501" provider="Motorola Mobility, Inc.">
+
+ <!--Product specifications: http://developer.motorola.com/products/cliqxt/-->
+
+ <d:default>
+ <d:screen-size>normal</d:screen-size>
+ <d:screen-ratio>notlong</d:screen-ratio>
+ <d:screen-orientation>port</d:screen-orientation>
+ <d:pixel-density>mdpi</d:pixel-density>
+ <d:touch-type>finger</d:touch-type>
+ <d:text-input-method>nokeys</d:text-input-method>
+ <d:keyboard-state>keyssoft</d:keyboard-state>
+ <d:nav-method>trackball</d:nav-method>
+ <d:screen-dimension>
+ <d:size>320</d:size>
+ <d:size>480</d:size>
+ </d:screen-dimension>
+ <d:xdpi>160</d:xdpi>
+ <d:ydpi>160</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:layout-devices> \ No newline at end of file
diff --git a/src/plugins/preflighting.core/devices/defy.xml b/src/plugins/preflighting.core/devices/defy.xml
new file mode 100644
index 0000000..5d213c4
--- /dev/null
+++ b/src/plugins/preflighting.core/devices/defy.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<d:layout-devices xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:d="http://schemas.android.com/sdk/android/layout-devices/1">
+
+ <d:device id="defy" name="DEFY MB525" provider="Motorola Mobility, Inc.">
+
+ <!--Product specifications: http://developer.motorola.com/products/defy/-->
+
+ <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:text-input-method>nokeys</d:text-input-method>
+ <d:keyboard-state>keyssoft</d:keyboard-state>
+ <d:nav-method>nonav</d:nav-method>
+ <d:screen-dimension>
+ <d:size>480</d:size>
+ <d:size>854</d:size>
+ </d:screen-dimension>
+ <d:xdpi>240</d:xdpi>
+ <d:ydpi>240</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:layout-devices> \ No newline at end of file
diff --git a/src/plugins/preflighting.core/devices/defyplus-mb526.xml b/src/plugins/preflighting.core/devices/defyplus-mb526.xml
new file mode 100644
index 0000000..21ba9ed
--- /dev/null
+++ b/src/plugins/preflighting.core/devices/defyplus-mb526.xml
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<d:layout-devices xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:d="http://schemas.android.com/sdk/android/layout-devices/1">
+
+ <d:device id="defyplus-mb526" name="DEFY+ MB526" provider="Motorola Mobility, Inc.">
+
+ <!--Product specifications: http://developer.motorola.com/products/defyplus-mb526/-->
+
+ <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:text-input-method>nokeys</d:text-input-method>
+ <d:keyboard-state>keyssoft</d:keyboard-state>
+ <d:nav-method>nonav</d:nav-method>
+ <d:screen-dimension>
+ <d:size>480</d:size>
+ <d:size>854</d:size>
+ </d:screen-dimension>
+ <d:xdpi>240</d:xdpi>
+ <d:ydpi>240</d:ydpi>
+ </d:default>
+ <d:supported-features>
+ <d:feature>android.hardware.bluetooth</d:feature>
+ <d:feature>android.hardware.camera</d:feature>
+ <d:feature>android.hardware.camera.autofocus</d:feature>
+ <d:feature>android.hardware.camera.flash</d:feature>
+ <d:feature>android.hardware.location</d:feature>
+ <d:feature>android.hardware.location.gps</d:feature>
+ <d:feature>android.hardware.location.network</d:feature>
+ <d:feature>android.hardware.microphone</d:feature>
+ <d:feature>android.hardware.sensor.accelerometer</d:feature>
+ <d:feature>android.hardware.sensor.compass</d:feature>
+ <d:feature>android.hardware.sensor.light</d:feature>
+ <d:feature>android.hardware.sensor.proximity</d:feature>
+ <d:feature>android.hardware.telephony</d:feature>
+ <d:feature>android.hardware.telephony.gsm</d:feature>
+ <d:feature>android.hardware.touchscreen</d:feature>
+ <d:feature>android.hardware.touchscreen.multitouch</d:feature>
+ <d:feature>android.hardware.touchscreen.multitouch.distinct</d:feature>
+ <d:feature>android.hardware.wifi</d:feature>
+ <d:feature>android.software.live_wallpaper</d:feature>
+ </d:supported-features>
+
+ </d:device>
+
+</d:layout-devices> \ No newline at end of file
diff --git a/src/plugins/preflighting.core/devices/devour.xml b/src/plugins/preflighting.core/devices/devour.xml
new file mode 100644
index 0000000..0352ad7
--- /dev/null
+++ b/src/plugins/preflighting.core/devices/devour.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<d:layout-devices xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:d="http://schemas.android.com/sdk/android/layout-devices/1">
+
+ <d:device id="devour" name="DEVOUR A555" provider="Motorola Mobility, Inc.">
+
+ <!--Product specifications: http://developer.motorola.com/products/devour/-->
+
+ <d:default>
+ <d:screen-size>normal</d:screen-size>
+ <d:screen-ratio>notlong</d:screen-ratio>
+ <d:screen-orientation>port</d:screen-orientation>
+ <d:pixel-density>mdpi</d:pixel-density>
+ <d:touch-type>finger</d:touch-type>
+ <d:text-input-method>qwerty</d:text-input-method>
+ <d:nav-method>trackball</d:nav-method>
+ <d:screen-dimension>
+ <d:size>320</d:size>
+ <d:size>480</d:size>
+ </d:screen-dimension>
+ <d:xdpi>160</d:xdpi>
+ <d:ydpi>160</d:ydpi>
+ </d:default>
+ <d:config name="Portrait, closed">
+ <d:keyboard-state>keyssoft</d:keyboard-state>
+ <d:screen-orientation>port</d:screen-orientation>
+ </d:config>
+ <d:config name="Landscape, closed">
+ <d:keyboard-state>keyssoft</d:keyboard-state>
+ <d:screen-orientation>land</d:screen-orientation>
+ </d:config>
+ <d:config name="Landscape, open">
+ <d:keyboard-state>keysexposed</d:keyboard-state>
+ <d:screen-orientation>land</d:screen-orientation>
+ </d:config>
+
+ </d:device>
+
+</d:layout-devices> \ No newline at end of file
diff --git a/src/plugins/preflighting.core/devices/dext-mb200.xml b/src/plugins/preflighting.core/devices/dext-mb200.xml
new file mode 100644
index 0000000..826d3d9
--- /dev/null
+++ b/src/plugins/preflighting.core/devices/dext-mb200.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<d:layout-devices xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:d="http://schemas.android.com/sdk/android/layout-devices/1">
+
+ <d:device id="dext-mb200" name="DEXT MB200" provider="Motorola Mobility, Inc.">
+
+ <!--Product specifications: http://developer.motorola.com/products/dext-mb200/-->
+
+ <d:default>
+ <d:screen-size>normal</d:screen-size>
+ <d:screen-ratio>notlong</d:screen-ratio>
+ <d:screen-orientation>port</d:screen-orientation>
+ <d:pixel-density>mdpi</d:pixel-density>
+ <d:touch-type>finger</d:touch-type>
+ <d:text-input-method>qwerty</d:text-input-method>
+ <d:nav-method>dpad</d:nav-method>
+ <d:screen-dimension>
+ <d:size>320</d:size>
+ <d:size>480</d:size>
+ </d:screen-dimension>
+ <d:xdpi>160</d:xdpi>
+ <d:ydpi>160</d:ydpi>
+ </d:default>
+ <d:config name="Portrait, closed">
+ <d:keyboard-state>keyssoft</d:keyboard-state>
+ <d:screen-orientation>port</d:screen-orientation>
+ </d:config>
+ <d:config name="Landscape, closed">
+ <d:keyboard-state>keyssoft</d:keyboard-state>
+ <d:screen-orientation>land</d:screen-orientation>
+ </d:config>
+ <d:config name="Landscape, open">
+ <d:keyboard-state>keysexposed</d:keyboard-state>
+ <d:screen-orientation>land</d:screen-orientation>
+ <d:nav-state>navexposed</d:nav-state>
+ </d:config>
+
+ </d:device>
+
+</d:layout-devices> \ No newline at end of file
diff --git a/src/plugins/preflighting.core/devices/droid-3-xt862.xml b/src/plugins/preflighting.core/devices/droid-3-xt862.xml
new file mode 100644
index 0000000..b4da0fb
--- /dev/null
+++ b/src/plugins/preflighting.core/devices/droid-3-xt862.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<d:layout-devices xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:d="http://schemas.android.com/sdk/android/layout-devices/1">
+
+ <d:device id="droid-3-xt862" name="DROID 3 by Motorola, XT862" provider="Motorola Mobility, Inc.">
+
+ <!--Product specifications: http://developer.motorola.com/products/droid-3-xt862/-->
+
+ <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:text-input-method>qwerty</d:text-input-method>
+ <d:nav-method>dpad</d:nav-method>
+ <d:screen-dimension>
+ <d:size>540</d:size>
+ <d:size>960</d:size>
+ </d:screen-dimension>
+ <d:xdpi>240</d:xdpi>
+ <d:ydpi>240</d:ydpi>
+ </d:default>
+ <d:config name="Portrait, closed">
+ <d:keyboard-state>keyssoft</d:keyboard-state>
+ <d:screen-orientation>port</d:screen-orientation>
+ </d:config>
+ <d:config name="Landscape, closed">
+ <d:keyboard-state>keyssoft</d:keyboard-state>
+ <d:screen-orientation>land</d:screen-orientation>
+ </d:config>
+ <d:config name="Landscape, open">
+ <d:keyboard-state>keysexposed</d:keyboard-state>
+ <d:screen-orientation>land</d:screen-orientation>
+ <d:nav-state>navexposed</d:nav-state>
+ </d:config>
+ <d:supported-features>
+ <d:feature>android.hardware.bluetooth</d:feature>
+ <d:feature>android.hardware.camera</d:feature>
+ <d:feature>android.hardware.camera.autofocus</d:feature>
+ <d:feature>android.hardware.camera.flash</d:feature>
+ <d:feature>android.hardware.camera.front</d:feature>
+ <d:feature>android.hardware.location</d:feature>
+ <d:feature>android.hardware.location.gps</d:feature>
+ <d:feature>android.hardware.location.network</d:feature>
+ <d:feature>android.hardware.microphone</d:feature>
+ <d:feature>android.hardware.sensor.accelerometer</d:feature>
+ <d:feature>android.hardware.sensor.compass</d:feature>
+ <d:feature>android.hardware.sensor.light</d:feature>
+ <d:feature>android.hardware.sensor.proximity</d:feature>
+ <d:feature>android.hardware.telephony</d:feature>
+ <d:feature>android.hardware.telephony.cdma</d:feature>
+ <d:feature>android.hardware.telephony.gsm</d:feature>
+ <d:feature>android.hardware.touchscreen</d:feature>
+ <d:feature>android.hardware.touchscreen.multitouch</d:feature>
+ <d:feature>android.hardware.touchscreen.multitouch.distinct</d:feature>
+ <d:feature>android.hardware.wifi</d:feature>
+ <d:feature>android.software.live_wallpaper</d:feature>
+ </d:supported-features>
+
+ </d:device>
+
+</d:layout-devices> \ No newline at end of file
diff --git a/src/plugins/preflighting.core/devices/droid-4-xt894.xml b/src/plugins/preflighting.core/devices/droid-4-xt894.xml
new file mode 100644
index 0000000..8cd426f
--- /dev/null
+++ b/src/plugins/preflighting.core/devices/droid-4-xt894.xml
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<d:layout-devices xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:d="http://schemas.android.com/sdk/android/layout-devices/1">
+
+ <d:device id="droid-4-xt894" name="DROID 4 by Motorola, XT894" provider="Motorola Mobility, Inc.">
+
+ <!--Product specifications: http://developer.motorola.com/products/droid-4-xt894/-->
+
+ <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:text-input-method>qwerty</d:text-input-method>
+ <d:nav-method>dpad</d:nav-method>
+ <d:screen-dimension>
+ <d:size>540</d:size>
+ <d:size>960</d:size>
+ </d:screen-dimension>
+ <d:xdpi>240</d:xdpi>
+ <d:ydpi>240</d:ydpi>
+ </d:default>
+ <d:config name="Portrait, closed">
+ <d:keyboard-state>keyssoft</d:keyboard-state>
+ <d:screen-orientation>port</d:screen-orientation>
+ </d:config>
+ <d:config name="Landscape, closed">
+ <d:keyboard-state>keyssoft</d:keyboard-state>
+ <d:screen-orientation>land</d:screen-orientation>
+ </d:config>
+ <d:config name="Landscape, open">
+ <d:keyboard-state>keysexposed</d:keyboard-state>
+ <d:screen-orientation>land</d:screen-orientation>
+ <d:nav-state>navexposed</d:nav-state>
+ </d:config>
+ <d:supported-features>
+ <d:feature>android.hardware.bluetooth</d:feature>
+ <d:feature>android.hardware.camera</d:feature>
+ <d:feature>android.hardware.camera.autofocus</d:feature>
+ <d:feature>android.hardware.camera.flash</d:feature>
+ <d:feature>android.hardware.camera.front</d:feature>
+ <d:feature>android.hardware.location</d:feature>
+ <d:feature>android.hardware.location.gps</d:feature>
+ <d:feature>android.hardware.location.network</d:feature>
+ <d:feature>android.hardware.microphone</d:feature>
+ <d:feature>android.hardware.sensor.accelerometer</d:feature>
+ <d:feature>android.hardware.sensor.compass</d:feature>
+ <d:feature>android.hardware.sensor.light</d:feature>
+ <d:feature>android.hardware.sensor.proximity</d:feature>
+ <d:feature>android.hardware.telephony</d:feature>
+ <d:feature>android.hardware.telephony.cdma</d:feature>
+ <d:feature>android.hardware.telephony.gsm</d:feature>
+ <d:feature>android.hardware.touchscreen</d:feature>
+ <d:feature>android.hardware.touchscreen.multitouch</d:feature>
+ <d:feature>android.hardware.touchscreen.multitouch.distinct</d:feature>
+ <d:feature>android.hardware.wifi</d:feature>
+ <d:feature>android.software.live_wallpaper</d:feature>
+ <d:feature>com.verizon.hardware.telephony.ehrpd</d:feature>
+ <d:feature>com.verizon.hardware.telephony.lte</d:feature>
+ <d:feature>vzw.hardware.hdmi</d:feature>
+ </d:supported-features>
+
+ </d:device>
+
+</d:layout-devices> \ No newline at end of file
diff --git a/src/plugins/preflighting.core/devices/droid-bionic-xt875.xml b/src/plugins/preflighting.core/devices/droid-bionic-xt875.xml
new file mode 100644
index 0000000..1917ba7
--- /dev/null
+++ b/src/plugins/preflighting.core/devices/droid-bionic-xt875.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<d:layout-devices xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:d="http://schemas.android.com/sdk/android/layout-devices/1">
+
+ <d:device id="droid-bionic-xt875" name="DROID BIONIC XT875" provider="Motorola Mobility, Inc.">
+
+ <!--Product specifications: http://developer.motorola.com/products/droid-bionic-xt875/-->
+
+ <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:text-input-method>nokeys</d:text-input-method>
+ <d:keyboard-state>keyssoft</d:keyboard-state>
+ <d:nav-method>nonav</d:nav-method>
+ <d:screen-dimension>
+ <d:size>540</d:size>
+ <d:size>960</d:size>
+ </d:screen-dimension>
+ <d:xdpi>240</d:xdpi>
+ <d:ydpi>240</d:ydpi>
+ </d:default>
+ <d:supported-features>
+ <d:feature>android.hardware.bluetooth</d:feature>
+ <d:feature>android.hardware.camera</d:feature>
+ <d:feature>android.hardware.camera.autofocus</d:feature>
+ <d:feature>android.hardware.camera.flash</d:feature>
+ <d:feature>android.hardware.camera.front</d:feature>
+ <d:feature>android.hardware.location</d:feature>
+ <d:feature>android.hardware.location.gps</d:feature>
+ <d:feature>android.hardware.location.network</d:feature>
+ <d:feature>android.hardware.microphone</d:feature>
+ <d:feature>android.hardware.sensor.accelerometer</d:feature>
+ <d:feature>android.hardware.sensor.compass</d:feature>
+ <d:feature>android.hardware.sensor.light</d:feature>
+ <d:feature>android.hardware.sensor.proximity</d:feature>
+ <d:feature>android.hardware.telephony</d:feature>
+ <d:feature>android.hardware.telephony.cdma</d:feature>
+ <d:feature>android.hardware.telephony.gsm</d:feature>
+ <d:feature>android.hardware.touchscreen</d:feature>
+ <d:feature>android.hardware.touchscreen.multitouch</d:feature>
+ <d:feature>android.hardware.touchscreen.multitouch.distinct</d:feature>
+ <d:feature>android.hardware.wifi</d:feature>
+ <d:feature>android.software.live_wallpaper</d:feature>
+ <d:feature>com.verizon.hardware.telephony.ehrpd</d:feature>
+ <d:feature>com.verizon.hardware.telephony.lte</d:feature>
+ </d:supported-features>
+
+ </d:device>
+
+</d:layout-devices> \ No newline at end of file
diff --git a/src/plugins/preflighting.core/devices/droid-razr-maxx-xt912.xml b/src/plugins/preflighting.core/devices/droid-razr-maxx-xt912.xml
new file mode 100644
index 0000000..ab4a4e5
--- /dev/null
+++ b/src/plugins/preflighting.core/devices/droid-razr-maxx-xt912.xml
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<d:layout-devices xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:d="http://schemas.android.com/sdk/android/layout-devices/1">
+
+ <d:device id="droid-razr-maxx-xt912" name="DROID RAZR MAXX by Motorola, XT912" provider="Motorola Mobility, Inc.">
+
+ <!--Product specifications: http://developer.motorola.com/products/droid-razr-maxx-xt912/-->
+
+ <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:text-input-method>nokeys</d:text-input-method>
+ <d:keyboard-state>keyssoft</d:keyboard-state>
+ <d:nav-method>nonav</d:nav-method>
+ <d:screen-dimension>
+ <d:size>540</d:size>
+ <d:size>960</d:size>
+ </d:screen-dimension>
+ <d:xdpi>240</d:xdpi>
+ <d:ydpi>240</d:ydpi>
+ </d:default>
+ <d:supported-features>
+ <d:feature>android.hardware.bluetooth</d:feature>
+ <d:feature>android.hardware.camera</d:feature>
+ <d:feature>android.hardware.camera.autofocus</d:feature>
+ <d:feature>android.hardware.camera.flash</d:feature>
+ <d:feature>android.hardware.location</d:feature>
+ <d:feature>android.hardware.location.gps</d:feature>
+ <d:feature>android.hardware.location.network</d:feature>
+ <d:feature>android.hardware.microphone</d:feature>
+ <d:feature>android.hardware.sensor.accelerometer</d:feature>
+ <d:feature>android.hardware.sensor.compass</d:feature>
+ <d:feature>android.hardware.sensor.light</d:feature>
+ <d:feature>android.hardware.sensor.proximity</d:feature>
+ <d:feature>android.hardware.telephony</d:feature>
+ <d:feature>android.hardware.telephony.cdma</d:feature>
+ <d:feature>android.hardware.telephony.gsm</d:feature>
+ <d:feature>android.hardware.touchscreen</d:feature>
+ <d:feature>android.hardware.touchscreen.multitouch</d:feature>
+ <d:feature>android.hardware.touchscreen.multitouch.distinct</d:feature>
+ <d:feature>android.hardware.wifi</d:feature>
+ <d:feature>android.software.live_wallpaper</d:feature>
+ </d:supported-features>
+
+ </d:device>
+
+</d:layout-devices> \ No newline at end of file
diff --git a/src/plugins/preflighting.core/devices/droid-razr-xt912.xml b/src/plugins/preflighting.core/devices/droid-razr-xt912.xml
new file mode 100644
index 0000000..9fb2708
--- /dev/null
+++ b/src/plugins/preflighting.core/devices/droid-razr-xt912.xml
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<d:layout-devices xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:d="http://schemas.android.com/sdk/android/layout-devices/1">
+
+ <d:device id="droid-razr-xt912" name="DROID RAZR by Motorola, XT912" provider="Motorola Mobility, Inc.">
+
+ <!--Product specifications: http://developer.motorola.com/products/droid-razr-xt912/-->
+
+ <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:text-input-method>nokeys</d:text-input-method>
+ <d:keyboard-state>keyssoft</d:keyboard-state>
+ <d:nav-method>nonav</d:nav-method>
+ <d:screen-dimension>
+ <d:size>540</d:size>
+ <d:size>960</d:size>
+ </d:screen-dimension>
+ <d:xdpi>240</d:xdpi>
+ <d:ydpi>240</d:ydpi>
+ </d:default>
+ <d:supported-features>
+ <d:feature>android.hardware.bluetooth</d:feature>
+ <d:feature>android.hardware.camera</d:feature>
+ <d:feature>android.hardware.camera.autofocus</d:feature>
+ <d:feature>android.hardware.camera.flash</d:feature>
+ <d:feature>android.hardware.location</d:feature>
+ <d:feature>android.hardware.location.gps</d:feature>
+ <d:feature>android.hardware.location.network</d:feature>
+ <d:feature>android.hardware.microphone</d:feature>
+ <d:feature>android.hardware.sensor.accelerometer</d:feature>
+ <d:feature>android.hardware.sensor.compass</d:feature>
+ <d:feature>android.hardware.sensor.light</d:feature>
+ <d:feature>android.hardware.sensor.proximity</d:feature>
+ <d:feature>android.hardware.telephony</d:feature>
+ <d:feature>android.hardware.telephony.cdma</d:feature>
+ <d:feature>android.hardware.telephony.gsm</d:feature>
+ <d:feature>android.hardware.touchscreen</d:feature>
+ <d:feature>android.hardware.touchscreen.multitouch</d:feature>
+ <d:feature>android.hardware.touchscreen.multitouch.distinct</d:feature>
+ <d:feature>android.hardware.wifi</d:feature>
+ <d:feature>android.software.live_wallpaper</d:feature>
+ </d:supported-features>
+
+ </d:device>
+
+</d:layout-devices> \ No newline at end of file
diff --git a/src/plugins/preflighting.core/devices/droid-x2-mb870.xml b/src/plugins/preflighting.core/devices/droid-x2-mb870.xml
new file mode 100644
index 0000000..3cfcf1d
--- /dev/null
+++ b/src/plugins/preflighting.core/devices/droid-x2-mb870.xml
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<d:layout-devices xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:d="http://schemas.android.com/sdk/android/layout-devices/1">
+
+ <d:device id="droid-x2-mb870" name="DROID X2 by Motorola, MB870" provider="Motorola Mobility, Inc.">
+
+ <!--Product specifications: http://developer.motorola.com/products/droid-x2-mb870/-->
+
+ <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:text-input-method>nokeys</d:text-input-method>
+ <d:keyboard-state>keyssoft</d:keyboard-state>
+ <d:nav-method>nonav</d:nav-method>
+ <d:screen-dimension>
+ <d:size>540</d:size>
+ <d:size>960</d:size>
+ </d:screen-dimension>
+ <d:xdpi>240</d:xdpi>
+ <d:ydpi>240</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:supported-features>
+ <d:feature>android.hardware.bluetooth</d:feature>
+ <d:feature>android.hardware.camera</d:feature>
+ <d:feature>android.hardware.camera.autofocus</d:feature>
+ <d:feature>android.hardware.camera.flash</d:feature>
+ <d:feature>android.hardware.location</d:feature>
+ <d:feature>android.hardware.location.gps</d:feature>
+ <d:feature>android.hardware.location.network</d:feature>
+ <d:feature>android.hardware.microphone</d:feature>
+ <d:feature>android.hardware.sensor.accelerometer</d:feature>
+ <d:feature>android.hardware.sensor.compass</d:feature>
+ <d:feature>android.hardware.sensor.light</d:feature>
+ <d:feature>android.hardware.sensor.proximity</d:feature>
+ <d:feature>android.hardware.telephony</d:feature>
+ <d:feature>android.hardware.telephony.cdma</d:feature>
+ <d:feature>android.hardware.touchscreen</d:feature>
+ <d:feature>android.hardware.touchscreen.multitouch</d:feature>
+ <d:feature>android.hardware.touchscreen.multitouch.distinct</d:feature>
+ <d:feature>android.hardware.wifi</d:feature>
+ <d:feature>android.software.live_wallpaper</d:feature>
+ </d:supported-features>
+
+ </d:device>
+
+</d:layout-devices> \ No newline at end of file
diff --git a/src/plugins/preflighting.core/devices/droid-xyboard-10-1-mz617.xml b/src/plugins/preflighting.core/devices/droid-xyboard-10-1-mz617.xml
new file mode 100644
index 0000000..a1462a6
--- /dev/null
+++ b/src/plugins/preflighting.core/devices/droid-xyboard-10-1-mz617.xml
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<d:layout-devices xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:d="http://schemas.android.com/sdk/android/layout-devices/1">
+
+ <d:device id="droid-xyboard-10-1-mz617" name="DROID XYBOARD 10.1 by Motorola, MZ617" provider="Motorola Mobility, Inc.">
+
+ <!--Product specifications: http://developer.motorola.com/products/droid-xyboard-10-1-mz617/-->
+
+ <d:default>
+ <d:screen-size>xlarge</d:screen-size>
+ <d:screen-ratio>long</d:screen-ratio>
+ <d:screen-orientation>land</d:screen-orientation>
+ <d:pixel-density>mdpi</d:pixel-density>
+ <d:touch-type>finger</d:touch-type>
+ <d:text-input-method>nokeys</d:text-input-method>
+ <d:keyboard-state>keyssoft</d:keyboard-state>
+ <d:nav-method>nonav</d:nav-method>
+ <d:screen-dimension>
+ <d:size>800</d:size>
+ <d:size>1280</d:size>
+ </d:screen-dimension>
+ <d:xdpi>160</d:xdpi>
+ <d:ydpi>160</d:ydpi>
+ </d:default>
+ <d:supported-features>
+ <d:feature>android.hardware.bluetooth</d:feature>
+ <d:feature>android.hardware.camera</d:feature>
+ <d:feature>android.hardware.camera.autofocus</d:feature>
+ <d:feature>android.hardware.camera.flash</d:feature>
+ <d:feature>android.hardware.camera.front</d:feature>
+ <d:feature>android.hardware.faketouch</d:feature>
+ <d:feature>android.hardware.location</d:feature>
+ <d:feature>android.hardware.location.gps</d:feature>
+ <d:feature>android.hardware.location.network</d:feature>
+ <d:feature>android.hardware.microphone</d:feature>
+ <d:feature>android.hardware.screen.landscape</d:feature>
+ <d:feature>android.hardware.screen.portrait</d:feature>
+ <d:feature>android.hardware.sensor.accelerometer</d:feature>
+ <d:feature>android.hardware.sensor.barometer</d:feature>
+ <d:feature>android.hardware.sensor.compass</d:feature>
+ <d:feature>android.hardware.sensor.gyroscope</d:feature>
+ <d:feature>android.hardware.sensor.light</d:feature>
+ <d:feature>android.hardware.touchscreen</d:feature>
+ <d:feature>android.hardware.touchscreen.multitouch</d:feature>
+ <d:feature>android.hardware.touchscreen.multitouch.distinct</d:feature>
+ <d:feature>android.hardware.touchscreen.multitouch.jazzhand</d:feature>
+ <d:feature>android.hardware.usb.accessory</d:feature>
+ <d:feature>android.hardware.usb.host</d:feature>
+ <d:feature>android.hardware.wifi</d:feature>
+ <d:feature>android.software.live_wallpaper</d:feature>
+ <d:feature>android.software.sip</d:feature>
+ <d:feature>android.software.sip.voip</d:feature>
+ <d:feature>com.motorola.hardware.CIR</d:feature>
+ <d:feature>com.motorola.hardware.CIR.UEI</d:feature>
+ <d:feature>com.verizon.hardware.telephony.ehrpd</d:feature>
+ <d:feature>com.verizon.hardware.telephony.lte</d:feature>
+ </d:supported-features>
+
+ </d:device>
+
+</d:layout-devices> \ No newline at end of file
diff --git a/src/plugins/preflighting.core/devices/droid-xyboard-8-2-mz609.xml b/src/plugins/preflighting.core/devices/droid-xyboard-8-2-mz609.xml
new file mode 100644
index 0000000..dcac38b
--- /dev/null
+++ b/src/plugins/preflighting.core/devices/droid-xyboard-8-2-mz609.xml
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<d:layout-devices xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:d="http://schemas.android.com/sdk/android/layout-devices/1">
+
+ <d:device id="droid-xyboard-8-2-mz609" name="DROID XYBOARD 8.2 by Motorola, MZ609" provider="Motorola Mobility, Inc.">
+
+ <!--Product specifications: http://developer.motorola.com/products/droid-xyboard-8-2-mz609/-->
+
+ <d:default>
+ <d:screen-size>xlarge</d:screen-size>
+ <d:screen-ratio>long</d:screen-ratio>
+ <d:screen-orientation>port</d:screen-orientation>
+ <d:pixel-density>mdpi</d:pixel-density>
+ <d:touch-type>finger</d:touch-type>
+ <d:text-input-method>nokeys</d:text-input-method>
+ <d:keyboard-state>keyssoft</d:keyboard-state>
+ <d:nav-method>nonav</d:nav-method>
+ <d:screen-dimension>
+ <d:size>1280</d:size>
+ <d:size>800</d:size>
+ </d:screen-dimension>
+ <d:xdpi>160</d:xdpi>
+ <d:ydpi>160</d:ydpi>
+ </d:default>
+ <d:supported-features>
+ <d:feature>android.hardware.bluetooth</d:feature>
+ <d:feature>android.hardware.camera</d:feature>
+ <d:feature>android.hardware.camera.autofocus</d:feature>
+ <d:feature>android.hardware.camera.flash</d:feature>
+ <d:feature>android.hardware.camera.front</d:feature>
+ <d:feature>android.hardware.faketouch</d:feature>
+ <d:feature>android.hardware.location</d:feature>
+ <d:feature>android.hardware.location.gps</d:feature>
+ <d:feature>android.hardware.location.network</d:feature>
+ <d:feature>android.hardware.microphone</d:feature>
+ <d:feature>android.hardware.screen.landscape</d:feature>
+ <d:feature>android.hardware.screen.portrait</d:feature>
+ <d:feature>android.hardware.sensor.accelerometer</d:feature>
+ <d:feature>android.hardware.sensor.barometer</d:feature>
+ <d:feature>android.hardware.sensor.compass</d:feature>
+ <d:feature>android.hardware.sensor.gyroscope</d:feature>
+ <d:feature>android.hardware.sensor.light</d:feature>
+ <d:feature>android.hardware.touchscreen</d:feature>
+ <d:feature>android.hardware.touchscreen.multitouch</d:feature>
+ <d:feature>android.hardware.touchscreen.multitouch.distinct</d:feature>
+ <d:feature>android.hardware.touchscreen.multitouch.jazzhand</d:feature>
+ <d:feature>android.hardware.usb.accessory</d:feature>
+ <d:feature>android.hardware.usb.host</d:feature>
+ <d:feature>android.hardware.wifi</d:feature>
+ <d:feature>android.software.live_wallpaper</d:feature>
+ <d:feature>android.software.sip</d:feature>
+ <d:feature>android.software.sip.voip</d:feature>
+ <d:feature>com.motorola.hardware.CIR</d:feature>
+ <d:feature>com.motorola.hardware.CIR.UEI</d:feature>
+ <d:feature>com.verizon.hardware.telephony.ehrpd</d:feature>
+ <d:feature>com.verizon.hardware.telephony.lte</d:feature>
+ </d:supported-features>
+
+ </d:device>
+
+</d:layout-devices> \ No newline at end of file
diff --git a/src/plugins/preflighting.core/devices/droid.xml b/src/plugins/preflighting.core/devices/droid.xml
new file mode 100644
index 0000000..568ef6d
--- /dev/null
+++ b/src/plugins/preflighting.core/devices/droid.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<d:layout-devices xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:d="http://schemas.android.com/sdk/android/layout-devices/1">
+
+ <d:device id="droid" name="DROID by Motorola, A855" provider="Motorola Mobility, Inc.">
+
+ <!--Product specifications: http://developer.motorola.com/products/droid/-->
+
+ <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:text-input-method>qwerty</d:text-input-method>
+ <d:nav-method>dpad</d:nav-method>
+ <d:screen-dimension>
+ <d:size>480</d:size>
+ <d:size>854</d:size>
+ </d:screen-dimension>
+ <d:xdpi>240</d:xdpi>
+ <d:ydpi>240</d:ydpi>
+ </d:default>
+ <d:config name="Portrait, closed">
+ <d:keyboard-state>keyssoft</d:keyboard-state>
+ <d:screen-orientation>port</d:screen-orientation>
+ </d:config>
+ <d:config name="Landscape, closed">
+ <d:keyboard-state>keyssoft</d:keyboard-state>
+ <d:screen-orientation>land</d:screen-orientation>
+ </d:config>
+ <d:config name="Landscape, open">
+ <d:keyboard-state>keysexposed</d:keyboard-state>
+ <d:screen-orientation>land</d:screen-orientation>
+ <d:nav-state>navexposed</d:nav-state>
+ </d:config>
+
+ </d:device>
+
+</d:layout-devices> \ No newline at end of file
diff --git a/src/plugins/preflighting.core/devices/droid2.xml b/src/plugins/preflighting.core/devices/droid2.xml
new file mode 100644
index 0000000..3db0ab7
--- /dev/null
+++ b/src/plugins/preflighting.core/devices/droid2.xml
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<d:layout-devices xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:d="http://schemas.android.com/sdk/android/layout-devices/1">
+
+ <d:device id="droid2" name="DROID 2 by Motorola, A955" provider="Motorola Mobility, Inc.">
+
+ <!--Product specifications: http://developer.motorola.com/products/droid2/-->
+
+ <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:text-input-method>qwerty</d:text-input-method>
+ <d:nav-method>dpad</d:nav-method>
+ <d:screen-dimension>
+ <d:size>480</d:size>
+ <d:size>854</d:size>
+ </d:screen-dimension>
+ <d:xdpi>240</d:xdpi>
+ <d:ydpi>240</d:ydpi>
+ </d:default>
+ <d:config name="Portrait, closed">
+ <d:keyboard-state>keyssoft</d:keyboard-state>
+ <d:screen-orientation>port</d:screen-orientation>
+ </d:config>
+ <d:config name="Landscape, closed">
+ <d:keyboard-state>keyssoft</d:keyboard-state>
+ <d:screen-orientation>land</d:screen-orientation>
+ </d:config>
+ <d:config name="Landscape, open">
+ <d:keyboard-state>keysexposed</d:keyboard-state>
+ <d:screen-orientation>land</d:screen-orientation>
+ <d:nav-state>navexposed</d:nav-state>
+ </d:config>
+ <d:supported-features>
+ <d:feature>android.hardware.bluetooth</d:feature>
+ <d:feature>android.hardware.camera</d:feature>
+ <d:feature>android.hardware.camera.autofocus</d:feature>
+ <d:feature>android.hardware.camera.flash</d:feature>
+ <d:feature>android.hardware.location</d:feature>
+ <d:feature>android.hardware.location.gps</d:feature>
+ <d:feature>android.hardware.location.network</d:feature>
+ <d:feature>android.hardware.microphone</d:feature>
+ <d:feature>android.hardware.sensor.accelerometer</d:feature>
+ <d:feature>android.hardware.sensor.compass</d:feature>
+ <d:feature>android.hardware.sensor.light</d:feature>
+ <d:feature>android.hardware.sensor.proximity</d:feature>
+ <d:feature>android.hardware.telephony</d:feature>
+ <d:feature>android.hardware.telephony.cdma</d:feature>
+ <d:feature>android.hardware.touchscreen</d:feature>
+ <d:feature>android.hardware.touchscreen.multitouch</d:feature>
+ <d:feature>android.hardware.touchscreen.multitouch.distinct</d:feature>
+ <d:feature>android.hardware.wifi</d:feature>
+ <d:feature>android.software.live_wallpaper</d:feature>
+ </d:supported-features>
+
+ </d:device>
+
+</d:layout-devices> \ No newline at end of file
diff --git a/src/plugins/preflighting.core/devices/droid2global.xml b/src/plugins/preflighting.core/devices/droid2global.xml
new file mode 100644
index 0000000..642a0e1
--- /dev/null
+++ b/src/plugins/preflighting.core/devices/droid2global.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<d:layout-devices xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:d="http://schemas.android.com/sdk/android/layout-devices/1">
+
+ <d:device id="droid2global" name="DROID 2 Global by Motorola, A956" provider="Motorola Mobility, Inc.">
+
+ <!--Product specifications: http://developer.motorola.com/products/droid2global/-->
+
+ <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:text-input-method>qwerty</d:text-input-method>
+ <d:nav-method>dpad</d:nav-method>
+ <d:screen-dimension>
+ <d:size>480</d:size>
+ <d:size>854</d:size>
+ </d:screen-dimension>
+ <d:xdpi>240</d:xdpi>
+ <d:ydpi>240</d:ydpi>
+ </d:default>
+ <d:config name="Portrait, closed">
+ <d:keyboard-state>keyssoft</d:keyboard-state>
+ <d:screen-orientation>port</d:screen-orientation>
+ </d:config>
+ <d:config name="Landscape, closed">
+ <d:keyboard-state>keyssoft</d:keyboard-state>
+ <d:screen-orientation>land</d:screen-orientation>
+ </d:config>
+ <d:config name="Landscape, open">
+ <d:keyboard-state>keysexposed</d:keyboard-state>
+ <d:screen-orientation>land</d:screen-orientation>
+ <d:nav-state>navexposed</d:nav-state>
+ </d:config>
+ <d:supported-features>
+ <d:feature>android.hardware.bluetooth</d:feature>
+ <d:feature>android.hardware.camera</d:feature>
+ <d:feature>android.hardware.camera.autofocus</d:feature>
+ <d:feature>android.hardware.camera.flash</d:feature>
+ <d:feature>android.hardware.location</d:feature>
+ <d:feature>android.hardware.location.gps</d:feature>
+ <d:feature>android.hardware.location.network</d:feature>
+ <d:feature>android.hardware.microphone</d:feature>
+ <d:feature>android.hardware.sensor.accelerometer</d:feature>
+ <d:feature>android.hardware.sensor.compass</d:feature>
+ <d:feature>android.hardware.sensor.light</d:feature>
+ <d:feature>android.hardware.sensor.proximity</d:feature>
+ <d:feature>android.hardware.telephony</d:feature>
+ <d:feature>android.hardware.telephony.cdma</d:feature>
+ <d:feature>android.hardware.telephony.gsm</d:feature>
+ <d:feature>android.hardware.touchscreen</d:feature>
+ <d:feature>android.hardware.touchscreen.multitouch</d:feature>
+ <d:feature>android.hardware.touchscreen.multitouch.distinct</d:feature>
+ <d:feature>android.hardware.wifi</d:feature>
+ <d:feature>android.software.live_wallpaper</d:feature>
+ </d:supported-features>
+
+ </d:device>
+
+</d:layout-devices> \ No newline at end of file
diff --git a/src/plugins/preflighting.core/devices/droidpro.xml b/src/plugins/preflighting.core/devices/droidpro.xml
new file mode 100644
index 0000000..ac9f513
--- /dev/null
+++ b/src/plugins/preflighting.core/devices/droidpro.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<d:layout-devices xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:d="http://schemas.android.com/sdk/android/layout-devices/1">
+
+ <d:device id="droidpro" name="DROID Pro by Motorola, XT610" provider="Motorola Mobility, Inc.">
+
+ <!--Product specifications: http://developer.motorola.com/products/droidpro/-->
+
+ <d:default>
+ <d:screen-size>normal</d:screen-size>
+ <d:screen-ratio>notlong</d:screen-ratio>
+ <d:screen-orientation>port</d:screen-orientation>
+ <d:pixel-density>mdpi</d:pixel-density>
+ <d:touch-type>finger</d:touch-type>
+ <d:text-input-method>qwerty</d:text-input-method>
+ <d:keyboard-state>keysexposed</d:keyboard-state>
+ <d:nav-method>nonav</d:nav-method>
+ <d:screen-dimension>
+ <d:size>320</d:size>
+ <d:size>480</d:size>
+ </d:screen-dimension>
+ <d:xdpi>160</d:xdpi>
+ <d:ydpi>160</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:keyboard-state>keyssoft</d:keyboard-state>
+ </d:config>
+ <d:supported-features>
+ <d:feature>android.hardware.bluetooth</d:feature>
+ <d:feature>android.hardware.camera</d:feature>
+ <d:feature>android.hardware.camera.autofocus</d:feature>
+ <d:feature>android.hardware.camera.flash</d:feature>
+ <d:feature>android.hardware.location</d:feature>
+ <d:feature>android.hardware.location.gps</d:feature>
+ <d:feature>android.hardware.location.network</d:feature>
+ <d:feature>android.hardware.microphone</d:feature>
+ <d:feature>android.hardware.sensor.accelerometer</d:feature>
+ <d:feature>android.hardware.sensor.compass</d:feature>
+ <d:feature>android.hardware.sensor.light</d:feature>
+ <d:feature>android.hardware.sensor.proximity</d:feature>
+ <d:feature>android.hardware.telephony</d:feature>
+ <d:feature>android.hardware.telephony.cdma</d:feature>
+ <d:feature>android.hardware.telephony.gsm</d:feature>
+ <d:feature>android.hardware.touchscreen</d:feature>
+ <d:feature>android.hardware.touchscreen.multitouch</d:feature>
+ <d:feature>android.hardware.touchscreen.multitouch.distinct</d:feature>
+ <d:feature>android.hardware.wifi</d:feature>
+ <d:feature>android.software.live_wallpaper</d:feature>
+ </d:supported-features>
+
+ </d:device>
+
+</d:layout-devices> \ No newline at end of file
diff --git a/src/plugins/preflighting.core/devices/droidx.xml b/src/plugins/preflighting.core/devices/droidx.xml
new file mode 100644
index 0000000..121b4d3
--- /dev/null
+++ b/src/plugins/preflighting.core/devices/droidx.xml
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<d:layout-devices xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:d="http://schemas.android.com/sdk/android/layout-devices/1">
+
+ <d:device id="droidx" name="DROID X by Motorola, MB811" provider="Motorola Mobility, Inc.">
+
+ <!--Product specifications: http://developer.motorola.com/products/droidx/-->
+
+ <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:text-input-method>nokeys</d:text-input-method>
+ <d:keyboard-state>keyssoft</d:keyboard-state>
+ <d:nav-method>nonav</d:nav-method>
+ <d:screen-dimension>
+ <d:size>480</d:size>
+ <d:size>854</d:size>
+ </d:screen-dimension>
+ <d:xdpi>240</d:xdpi>
+ <d:ydpi>240</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:supported-features>
+ <d:feature>android.hardware.bluetooth</d:feature>
+ <d:feature>android.hardware.camera</d:feature>
+ <d:feature>android.hardware.camera.autofocus</d:feature>
+ <d:feature>android.hardware.camera.flash</d:feature>
+ <d:feature>android.hardware.location</d:feature>
+ <d:feature>android.hardware.location.gps</d:feature>
+ <d:feature>android.hardware.location.network</d:feature>
+ <d:feature>android.hardware.microphone</d:feature>
+ <d:feature>android.hardware.sensor.accelerometer</d:feature>
+ <d:feature>android.hardware.sensor.compass</d:feature>
+ <d:feature>android.hardware.sensor.light</d:feature>
+ <d:feature>android.hardware.sensor.proximity</d:feature>
+ <d:feature>android.hardware.telephony</d:feature>
+ <d:feature>android.hardware.telephony.cdma</d:feature>
+ <d:feature>android.hardware.touchscreen</d:feature>
+ <d:feature>android.hardware.touchscreen.multitouch</d:feature>
+ <d:feature>android.hardware.touchscreen.multitouch.distinct</d:feature>
+ <d:feature>android.hardware.wifi</d:feature>
+ <d:feature>android.software.live_wallpaper</d:feature>
+ </d:supported-features>
+
+ </d:device>
+
+</d:layout-devices> \ No newline at end of file
diff --git a/src/plugins/preflighting.core/devices/electrify.xml b/src/plugins/preflighting.core/devices/electrify.xml
new file mode 100644
index 0000000..5d62d0b
--- /dev/null
+++ b/src/plugins/preflighting.core/devices/electrify.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<d:layout-devices xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:d="http://schemas.android.com/sdk/android/layout-devices/1">
+
+ <d:device id="electrify" name="ELECTRIFY" provider="Motorola Mobility, Inc.">
+
+ <!--Product specifications: http://developer.motorola.com/products/electrify/-->
+
+ <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:text-input-method>nokeys</d:text-input-method>
+ <d:keyboard-state>keyssoft</d:keyboard-state>
+ <d:nav-method>nonav</d:nav-method>
+ <d:screen-dimension>
+ <d:size>540</d:size>
+ <d:size>960</d:size>
+ </d:screen-dimension>
+ <d:xdpi>240</d:xdpi>
+ <d:ydpi>240</d:ydpi>
+ </d:default>
+ <d:supported-features>
+ <d:feature>android.hardware.bluetooth</d:feature>
+ <d:feature>android.hardware.camera</d:feature>
+ <d:feature>android.hardware.camera.autofocus</d:feature>
+ <d:feature>android.hardware.camera.flash</d:feature>
+ <d:feature>android.hardware.camera.front</d:feature>
+ <d:feature>android.hardware.location</d:feature>
+ <d:feature>android.hardware.location.gps</d:feature>
+ <d:feature>android.hardware.location.network</d:feature>
+ <d:feature>android.hardware.microphone</d:feature>
+ <d:feature>android.hardware.sensor.accelerometer</d:feature>
+ <d:feature>android.hardware.sensor.compass</d:feature>
+ <d:feature>android.hardware.sensor.light</d:feature>
+ <d:feature>android.hardware.sensor.proximity</d:feature>
+ <d:feature>android.hardware.telephony</d:feature>
+ <d:feature>android.hardware.telephony.cdma</d:feature>
+ <d:feature>android.hardware.telephony.gsm</d:feature>
+ <d:feature>android.hardware.touchscreen</d:feature>
+ <d:feature>android.hardware.touchscreen.multitouch</d:feature>
+ <d:feature>android.hardware.touchscreen.multitouch.distinct</d:feature>
+ <d:feature>android.hardware.wifi</d:feature>
+ <d:feature>android.software.live_wallpaper</d:feature>
+ </d:supported-features>
+
+ </d:device>
+
+</d:layout-devices> \ No newline at end of file
diff --git a/src/plugins/preflighting.core/devices/fire-xt-xt530.xml b/src/plugins/preflighting.core/devices/fire-xt-xt530.xml
new file mode 100644
index 0000000..0c1f613
--- /dev/null
+++ b/src/plugins/preflighting.core/devices/fire-xt-xt530.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<d:layout-devices xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:d="http://schemas.android.com/sdk/android/layout-devices/1">
+
+ <d:device id="fire-xt-xt530" name="FIRE XT XT530" provider="Motorola Mobility, Inc.">
+
+ <!--Product specifications: http://developer.motorola.com/products/fire-xt-xt530/-->
+
+ <d:default>
+ <d:screen-size>normal</d:screen-size>
+ <d:screen-ratio>notlong</d:screen-ratio>
+ <d:screen-orientation>port</d:screen-orientation>
+ <d:pixel-density>mdpi</d:pixel-density>
+ <d:touch-type>finger</d:touch-type>
+ <d:text-input-method>nokeys</d:text-input-method>
+ <d:keyboard-state>keyssoft</d:keyboard-state>
+ <d:nav-method>nonav</d:nav-method>
+ <d:screen-dimension>
+ <d:size>320</d:size>
+ <d:size>480</d:size>
+ </d:screen-dimension>
+ <d:xdpi>160</d:xdpi>
+ <d:ydpi>160</d:ydpi>
+ </d:default>
+ <d:supported-features>
+ <d:feature>android.hardware.bluetooth</d:feature>
+ <d:feature>android.hardware.camera</d:feature>
+ <d:feature>android.hardware.camera.autofocus</d:feature>
+ <d:feature>android.hardware.camera.flash</d:feature>
+ <d:feature>android.hardware.camera.front</d:feature>
+ <d:feature>android.hardware.location</d:feature>
+ <d:feature>android.hardware.location.gps</d:feature>
+ <d:feature>android.hardware.location.network</d:feature>
+ <d:feature>android.hardware.microphone</d:feature>
+ <d:feature>android.hardware.sensor.accelerometer</d:feature>
+ <d:feature>android.hardware.sensor.compass</d:feature>
+ <d:feature>android.hardware.sensor.light</d:feature>
+ <d:feature>android.hardware.sensor.proximity</d:feature>
+ <d:feature>android.hardware.telephony</d:feature>
+ <d:feature>android.hardware.telephony.gsm</d:feature>
+ <d:feature>android.hardware.touchscreen</d:feature>
+ <d:feature>android.hardware.touchscreen.multitouch</d:feature>
+ <d:feature>android.hardware.wifi</d:feature>
+ <d:feature>android.software.live_wallpaper</d:feature>
+ <d:feature>android.software.sip</d:feature>
+ <d:feature>android.software.sip.voip</d:feature>
+ </d:supported-features>
+
+ </d:device>
+
+</d:layout-devices> \ No newline at end of file
diff --git a/src/plugins/preflighting.core/devices/fire-xt311.xml b/src/plugins/preflighting.core/devices/fire-xt311.xml
new file mode 100644
index 0000000..df9d034
--- /dev/null
+++ b/src/plugins/preflighting.core/devices/fire-xt311.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<d:layout-devices xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:d="http://schemas.android.com/sdk/android/layout-devices/1">
+
+ <d:device id="fire-xt311" name="FIRE XT311" provider="Motorola Mobility, Inc.">
+
+ <!--Product specifications: http://developer.motorola.com/products/fire-xt311/-->
+
+ <d:default>
+ <d:screen-size>small</d:screen-size>
+ <d:screen-ratio>notlong</d:screen-ratio>
+ <d:screen-orientation>port</d:screen-orientation>
+ <d:pixel-density>ldpi</d:pixel-density>
+ <d:touch-type>finger</d:touch-type>
+ <d:text-input-method>qwerty</d:text-input-method>
+ <d:keyboard-state>keysexposed</d:keyboard-state>
+ <d:nav-method>nonav</d:nav-method>
+ <d:screen-dimension>
+ <d:size>240</d:size>
+ <d:size>320</d:size>
+ </d:screen-dimension>
+ <d:xdpi>120</d:xdpi>
+ <d:ydpi>120</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:keyboard-state>keyssoft</d:keyboard-state>
+ </d:config>
+
+ </d:device>
+
+</d:layout-devices> \ No newline at end of file
diff --git a/src/plugins/preflighting.core/devices/fire-xt317.xml b/src/plugins/preflighting.core/devices/fire-xt317.xml
new file mode 100644
index 0000000..230c32d
--- /dev/null
+++ b/src/plugins/preflighting.core/devices/fire-xt317.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<d:layout-devices xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:d="http://schemas.android.com/sdk/android/layout-devices/1">
+
+ <d:device id="fire-xt317" name="FIRE XT317" provider="Motorola Mobility, Inc.">
+
+ <!--Product specifications: http://developer.motorola.com/products/fire-xt317/-->
+
+ <d:default>
+ <d:screen-size>small</d:screen-size>
+ <d:screen-ratio>notlong</d:screen-ratio>
+ <d:screen-orientation>port</d:screen-orientation>
+ <d:pixel-density>ldpi</d:pixel-density>
+ <d:touch-type>finger</d:touch-type>
+ <d:text-input-method>qwerty</d:text-input-method>
+ <d:keyboard-state>keysexposed</d:keyboard-state>
+ <d:nav-method>nonav</d:nav-method>
+ <d:screen-dimension>
+ <d:size>240</d:size>
+ <d:size>320</d:size>
+ </d:screen-dimension>
+ <d:xdpi>120</d:xdpi>
+ <d:ydpi>120</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:keyboard-state>keyssoft</d:keyboard-state>
+ </d:config>
+
+ </d:device>
+
+</d:layout-devices> \ No newline at end of file
diff --git a/src/plugins/preflighting.core/devices/flipout-mb511.xml b/src/plugins/preflighting.core/devices/flipout-mb511.xml
new file mode 100644
index 0000000..b588617
--- /dev/null
+++ b/src/plugins/preflighting.core/devices/flipout-mb511.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<d:layout-devices xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:d="http://schemas.android.com/sdk/android/layout-devices/1">
+
+ <d:device id="flipout-mb511" name="FLIPOUT MB511" provider="Motorola Mobility, Inc.">
+
+ <!--Product specifications: http://developer.motorola.com/products/flipout-mb511/-->
+
+ <d:default>
+ <d:screen-size>small</d:screen-size>
+ <d:screen-ratio>notlong</d:screen-ratio>
+ <d:screen-orientation>land</d:screen-orientation>
+ <d:pixel-density>ldpi</d:pixel-density>
+ <d:touch-type>finger</d:touch-type>
+ <d:text-input-method>qwerty</d:text-input-method>
+ <d:nav-method>dpad</d:nav-method>
+ <d:screen-dimension>
+ <d:size>320</d:size>
+ <d:size>240</d:size>
+ </d:screen-dimension>
+ <d:xdpi>120</d:xdpi>
+ <d:ydpi>120</d:ydpi>
+ </d:default>
+ <d:config name="Portrait, closed">
+ <d:keyboard-state>keyssoft</d:keyboard-state>
+ <d:screen-orientation>port</d:screen-orientation>
+ </d:config>
+ <d:config name="Landscape, closed">
+ <d:keyboard-state>keyssoft</d:keyboard-state>
+ <d:screen-orientation>land</d:screen-orientation>
+ </d:config>
+ <d:config name="Landscape, open">
+ <d:keyboard-state>keysexposed</d:keyboard-state>
+ <d:screen-orientation>land</d:screen-orientation>
+ <d:nav-state>navexposed</d:nav-state>
+ </d:config>
+
+ </d:device>
+
+</d:layout-devices> \ No newline at end of file
diff --git a/src/plugins/preflighting.core/devices/flipout-me511.xml b/src/plugins/preflighting.core/devices/flipout-me511.xml
new file mode 100644
index 0000000..cc27091
--- /dev/null
+++ b/src/plugins/preflighting.core/devices/flipout-me511.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<d:layout-devices xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:d="http://schemas.android.com/sdk/android/layout-devices/1">
+
+ <d:device id="flipout-me511" name="FLIPOUT ME511" provider="Motorola Mobility, Inc.">
+
+ <!--Product specifications: http://developer.motorola.com/products/flipout-me511/-->
+
+ <d:default>
+ <d:screen-size>small</d:screen-size>
+ <d:screen-ratio>notlong</d:screen-ratio>
+ <d:screen-orientation>land</d:screen-orientation>
+ <d:pixel-density>ldpi</d:pixel-density>
+ <d:touch-type>finger</d:touch-type>
+ <d:text-input-method>qwerty</d:text-input-method>
+ <d:nav-method>dpad</d:nav-method>
+ <d:screen-dimension>
+ <d:size>320</d:size>
+ <d:size>240</d:size>
+ </d:screen-dimension>
+ <d:xdpi>120</d:xdpi>
+ <d:ydpi>120</d:ydpi>
+ </d:default>
+ <d:config name="Portrait, closed">
+ <d:keyboard-state>keyssoft</d:keyboard-state>
+ <d:screen-orientation>port</d:screen-orientation>
+ </d:config>
+ <d:config name="Landscape, closed">
+ <d:keyboard-state>keyssoft</d:keyboard-state>
+ <d:screen-orientation>land</d:screen-orientation>
+ </d:config>
+ <d:config name="Landscape, open">
+ <d:keyboard-state>keysexposed</d:keyboard-state>
+ <d:screen-orientation>land</d:screen-orientation>
+ <d:nav-state>navexposed</d:nav-state>
+ </d:config>
+
+ </d:device>
+
+</d:layout-devices> \ No newline at end of file
diff --git a/src/plugins/preflighting.core/devices/flipside.xml b/src/plugins/preflighting.core/devices/flipside.xml
new file mode 100644
index 0000000..7c92f7b
--- /dev/null
+++ b/src/plugins/preflighting.core/devices/flipside.xml
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<d:layout-devices xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:d="http://schemas.android.com/sdk/android/layout-devices/1">
+
+ <d:device id="flipside" name="FLIPSIDE MB508" provider="Motorola Mobility, Inc.">
+
+ <!--Product specifications: http://developer.motorola.com/products/flipside/-->
+
+ <d:default>
+ <d:screen-size>normal</d:screen-size>
+ <d:screen-ratio>notlong</d:screen-ratio>
+ <d:screen-orientation>port</d:screen-orientation>
+ <d:pixel-density>mdpi</d:pixel-density>
+ <d:touch-type>finger</d:touch-type>
+ <d:text-input-method>qwerty</d:text-input-method>
+ <d:nav-method>trackball</d:nav-method>
+ <d:screen-dimension>
+ <d:size>320</d:size>
+ <d:size>480</d:size>
+ </d:screen-dimension>
+ <d:xdpi>160</d:xdpi>
+ <d:ydpi>160</d:ydpi>
+ </d:default>
+ <d:config name="Portrait, closed">
+ <d:keyboard-state>keyssoft</d:keyboard-state>
+ <d:screen-orientation>port</d:screen-orientation>
+ </d:config>
+ <d:config name="Landscape, closed">
+ <d:keyboard-state>keyssoft</d:keyboard-state>
+ <d:screen-orientation>land</d:screen-orientation>
+ </d:config>
+ <d:config name="Landscape, open">
+ <d:keyboard-state>keysexposed</d:keyboard-state>
+ <d:screen-orientation>land</d:screen-orientation>
+ </d:config>
+ <d:supported-features>
+ <d:feature>android.hardware.bluetooth</d:feature>
+ <d:feature>android.hardware.camera</d:feature>
+ <d:feature>android.hardware.location</d:feature>
+ <d:feature>android.hardware.location.gps</d:feature>
+ <d:feature>android.hardware.location.network</d:feature>
+ <d:feature>android.hardware.microphone</d:feature>
+ <d:feature>android.hardware.sensor.accelerometer</d:feature>
+ <d:feature>android.hardware.sensor.compass</d:feature>
+ <d:feature>android.hardware.sensor.light</d:feature>
+ <d:feature>android.hardware.sensor.proximity</d:feature>
+ <d:feature>android.hardware.telephony</d:feature>
+ <d:feature>android.hardware.telephony.gsm</d:feature>
+ <d:feature>android.hardware.touchscreen</d:feature>
+ <d:feature>android.hardware.touchscreen.multitouch</d:feature>
+ <d:feature>android.hardware.touchscreen.multitouch.distinct</d:feature>
+ <d:feature>android.hardware.wifi</d:feature>
+ <d:feature>android.software.live_wallpaper</d:feature>
+ </d:supported-features>
+
+ </d:device>
+
+</d:layout-devices> \ No newline at end of file
diff --git a/src/plugins/preflighting.core/devices/i1.xml b/src/plugins/preflighting.core/devices/i1.xml
new file mode 100644
index 0000000..7197af4
--- /dev/null
+++ b/src/plugins/preflighting.core/devices/i1.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<d:layout-devices xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:d="http://schemas.android.com/sdk/android/layout-devices/1">
+
+ <d:device id="i1" name="i1" provider="Motorola Mobility, Inc.">
+
+ <!--Product specifications: http://developer.motorola.com/products/i1/-->
+
+ <d:default>
+ <d:screen-size>normal</d:screen-size>
+ <d:screen-ratio>notlong</d:screen-ratio>
+ <d:screen-orientation>port</d:screen-orientation>
+ <d:pixel-density>mdpi</d:pixel-density>
+ <d:touch-type>finger</d:touch-type>
+ <d:text-input-method>nokeys</d:text-input-method>
+ <d:keyboard-state>keyssoft</d:keyboard-state>
+ <d:nav-method>dpad</d:nav-method>
+ <d:screen-dimension>
+ <d:size>320</d:size>
+ <d:size>480</d:size>
+ </d:screen-dimension>
+ <d:xdpi>160</d:xdpi>
+ <d:ydpi>160</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:layout-devices> \ No newline at end of file
diff --git a/src/plugins/preflighting.core/devices/i940.xml b/src/plugins/preflighting.core/devices/i940.xml
new file mode 100644
index 0000000..e5284ce
--- /dev/null
+++ b/src/plugins/preflighting.core/devices/i940.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<d:layout-devices xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:d="http://schemas.android.com/sdk/android/layout-devices/1">
+
+ <d:device id="i940" name="i940" provider="Motorola Mobility, Inc.">
+
+ <!--Product specifications: http://developer.motorola.com/products/i940/-->
+
+ <d:default>
+ <d:screen-size>normal</d:screen-size>
+ <d:screen-ratio>notlong</d:screen-ratio>
+ <d:screen-orientation>port</d:screen-orientation>
+ <d:pixel-density>mdpi</d:pixel-density>
+ <d:touch-type>finger</d:touch-type>
+ <d:text-input-method>nokeys</d:text-input-method>
+ <d:keyboard-state>keyssoft</d:keyboard-state>
+ <d:nav-method>nonav</d:nav-method>
+ <d:screen-dimension>
+ <d:size>320</d:size>
+ <d:size>480</d:size>
+ </d:screen-dimension>
+ <d:xdpi>160</d:xdpi>
+ <d:ydpi>160</d:ydpi>
+ </d:default>
+ <d:supported-features>
+ <d:feature>android.hardware.bluetooth</d:feature>
+ <d:feature>android.hardware.camera</d:feature>
+ <d:feature>android.hardware.camera.autofocus</d:feature>
+ <d:feature>android.hardware.camera.flash</d:feature>
+ <d:feature>android.hardware.location</d:feature>
+ <d:feature>android.hardware.location.gps</d:feature>
+ <d:feature>android.hardware.location.network</d:feature>
+ <d:feature>android.hardware.sensor.accelerometer</d:feature>
+ <d:feature>android.hardware.sensor.compass</d:feature>
+ <d:feature>android.hardware.sensor.light</d:feature>
+ <d:feature>android.hardware.sensor.proximity</d:feature>
+ <d:feature>android.hardware.telephony</d:feature>
+ <d:feature>android.hardware.telephony.gsm</d:feature>
+ <d:feature>android.hardware.touchscreen</d:feature>
+ <d:feature>android.hardware.touchscreen.multitouch</d:feature>
+ <d:feature>android.hardware.wifi</d:feature>
+ </d:supported-features>
+
+ </d:device>
+
+</d:layout-devices> \ No newline at end of file
diff --git a/src/plugins/preflighting.core/devices/mb501.xml b/src/plugins/preflighting.core/devices/mb501.xml
new file mode 100644
index 0000000..f02938f
--- /dev/null
+++ b/src/plugins/preflighting.core/devices/mb501.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<d:layout-devices xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:d="http://schemas.android.com/sdk/android/layout-devices/1">
+
+ <d:device id="mb501" name="QUENCH MB501" provider="Motorola Mobility, Inc.">
+
+ <!--Product specifications: http://developer.motorola.com/products/mb501/-->
+
+ <d:default>
+ <d:screen-size>normal</d:screen-size>
+ <d:screen-ratio>notlong</d:screen-ratio>
+ <d:screen-orientation>port</d:screen-orientation>
+ <d:pixel-density>mdpi</d:pixel-density>
+ <d:touch-type>finger</d:touch-type>
+ <d:text-input-method>nokeys</d:text-input-method>
+ <d:keyboard-state>keyssoft</d:keyboard-state>
+ <d:nav-method>trackball</d:nav-method>
+ <d:screen-dimension>
+ <d:size>320</d:size>
+ <d:size>480</d:size>
+ </d:screen-dimension>
+ <d:xdpi>160</d:xdpi>
+ <d:ydpi>160</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:layout-devices> \ No newline at end of file
diff --git a/src/plugins/preflighting.core/devices/me501.xml b/src/plugins/preflighting.core/devices/me501.xml
new file mode 100644
index 0000000..5873904
--- /dev/null
+++ b/src/plugins/preflighting.core/devices/me501.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<d:layout-devices xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:d="http://schemas.android.com/sdk/android/layout-devices/1">
+
+ <d:device id="me501" name="QUENCH ME501" provider="Motorola Mobility, Inc.">
+
+ <!--Product specifications: http://developer.motorola.com/products/me501/-->
+
+ <d:default>
+ <d:screen-size>normal</d:screen-size>
+ <d:screen-ratio>notlong</d:screen-ratio>
+ <d:screen-orientation>port</d:screen-orientation>
+ <d:pixel-density>mdpi</d:pixel-density>
+ <d:touch-type>finger</d:touch-type>
+ <d:text-input-method>nokeys</d:text-input-method>
+ <d:keyboard-state>keyssoft</d:keyboard-state>
+ <d:nav-method>trackball</d:nav-method>
+ <d:screen-dimension>
+ <d:size>320</d:size>
+ <d:size>480</d:size>
+ </d:screen-dimension>
+ <d:xdpi>160</d:xdpi>
+ <d:ydpi>160</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:layout-devices> \ No newline at end of file
diff --git a/src/plugins/preflighting.core/devices/me525-plus.xml b/src/plugins/preflighting.core/devices/me525-plus.xml
new file mode 100644
index 0000000..0a108ba
--- /dev/null
+++ b/src/plugins/preflighting.core/devices/me525-plus.xml
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<d:layout-devices xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:d="http://schemas.android.com/sdk/android/layout-devices/1">
+
+ <d:device id="me525-plus" name="MOTO ME525+" provider="Motorola Mobility, Inc.">
+
+ <!--Product specifications: http://developer.motorola.com/products/me525-plus/-->
+
+ <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:text-input-method>nokeys</d:text-input-method>
+ <d:keyboard-state>keyssoft</d:keyboard-state>
+ <d:nav-method>nonav</d:nav-method>
+ <d:screen-dimension>
+ <d:size>480</d:size>
+ <d:size>854</d:size>
+ </d:screen-dimension>
+ <d:xdpi>240</d:xdpi>
+ <d:ydpi>240</d:ydpi>
+ </d:default>
+ <d:supported-features>
+ <d:feature>android.hardware.bluetooth</d:feature>
+ <d:feature>android.hardware.camera</d:feature>
+ <d:feature>android.hardware.camera.autofocus</d:feature>
+ <d:feature>android.hardware.camera.flash</d:feature>
+ <d:feature>android.hardware.location</d:feature>
+ <d:feature>android.hardware.location.gps</d:feature>
+ <d:feature>android.hardware.location.network</d:feature>
+ <d:feature>android.hardware.microphone</d:feature>
+ <d:feature>android.hardware.sensor.accelerometer</d:feature>
+ <d:feature>android.hardware.sensor.compass</d:feature>
+ <d:feature>android.hardware.sensor.light</d:feature>
+ <d:feature>android.hardware.sensor.proximity</d:feature>
+ <d:feature>android.hardware.telephony</d:feature>
+ <d:feature>android.hardware.telephony.gsm</d:feature>
+ <d:feature>android.hardware.touchscreen</d:feature>
+ <d:feature>android.hardware.touchscreen.multitouch</d:feature>
+ <d:feature>android.hardware.touchscreen.multitouch.distinct</d:feature>
+ <d:feature>android.hardware.wifi</d:feature>
+ <d:feature>android.software.live_wallpaper</d:feature>
+ </d:supported-features>
+
+ </d:device>
+
+</d:layout-devices> \ No newline at end of file
diff --git a/src/plugins/preflighting.core/devices/me525.xml b/src/plugins/preflighting.core/devices/me525.xml
new file mode 100644
index 0000000..f9f6d2e
--- /dev/null
+++ b/src/plugins/preflighting.core/devices/me525.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<d:layout-devices xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:d="http://schemas.android.com/sdk/android/layout-devices/1">
+
+ <d:device id="me525" name="MOTO ME525" provider="Motorola Mobility, Inc.">
+
+ <!--Product specifications: http://developer.motorola.com/products/me525/-->
+
+ <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:text-input-method>nokeys</d:text-input-method>
+ <d:keyboard-state>keyssoft</d:keyboard-state>
+ <d:nav-method>nonav</d:nav-method>
+ <d:screen-dimension>
+ <d:size>480</d:size>
+ <d:size>854</d:size>
+ </d:screen-dimension>
+ <d:xdpi>240</d:xdpi>
+ <d:ydpi>240</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:layout-devices> \ No newline at end of file
diff --git a/src/plugins/preflighting.core/devices/me811.xml b/src/plugins/preflighting.core/devices/me811.xml
new file mode 100644
index 0000000..cc3c60c
--- /dev/null
+++ b/src/plugins/preflighting.core/devices/me811.xml
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<d:layout-devices xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:d="http://schemas.android.com/sdk/android/layout-devices/1">
+
+ <d:device id="me811" name="MOTO ME811" provider="Motorola Mobility, Inc.">
+
+ <!--Product specifications: http://developer.motorola.com/products/me811/-->
+
+ <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:text-input-method>nokeys</d:text-input-method>
+ <d:keyboard-state>keyssoft</d:keyboard-state>
+ <d:nav-method>nonav</d:nav-method>
+ <d:screen-dimension>
+ <d:size>480</d:size>
+ <d:size>854</d:size>
+ </d:screen-dimension>
+ <d:xdpi>240</d:xdpi>
+ <d:ydpi>240</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:supported-features>
+ <d:feature>android.hardware.bluetooth</d:feature>
+ <d:feature>android.hardware.camera</d:feature>
+ <d:feature>android.hardware.camera.autofocus</d:feature>
+ <d:feature>android.hardware.camera.flash</d:feature>
+ <d:feature>android.hardware.location</d:feature>
+ <d:feature>android.hardware.location.gps</d:feature>
+ <d:feature>android.hardware.location.network</d:feature>
+ <d:feature>android.hardware.microphone</d:feature>
+ <d:feature>android.hardware.sensor.accelerometer</d:feature>
+ <d:feature>android.hardware.sensor.compass</d:feature>
+ <d:feature>android.hardware.sensor.light</d:feature>
+ <d:feature>android.hardware.sensor.proximity</d:feature>
+ <d:feature>android.hardware.telephony</d:feature>
+ <d:feature>android.hardware.telephony.cdma</d:feature>
+ <d:feature>android.hardware.touchscreen</d:feature>
+ <d:feature>android.hardware.touchscreen.multitouch</d:feature>
+ <d:feature>android.hardware.touchscreen.multitouch.distinct</d:feature>
+ <d:feature>android.hardware.wifi</d:feature>
+ <d:feature>android.software.live_wallpaper</d:feature>
+ </d:supported-features>
+
+ </d:device>
+
+</d:layout-devices> \ No newline at end of file
diff --git a/src/plugins/preflighting.core/devices/milestone-3-xt860.xml b/src/plugins/preflighting.core/devices/milestone-3-xt860.xml
new file mode 100644
index 0000000..41902dc
--- /dev/null
+++ b/src/plugins/preflighting.core/devices/milestone-3-xt860.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<d:layout-devices xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:d="http://schemas.android.com/sdk/android/layout-devices/1">
+
+ <d:device id="milestone-3-xt860" name="MILESTONE 3 XT860, ME863" provider="Motorola Mobility, Inc.">
+
+ <!--Product specifications: http://developer.motorola.com/products/milestone-3-xt860/-->
+
+ <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:text-input-method>qwerty</d:text-input-method>
+ <d:nav-method>dpad</d:nav-method>
+ <d:screen-dimension>
+ <d:size>540</d:size>
+ <d:size>960</d:size>
+ </d:screen-dimension>
+ <d:xdpi>240</d:xdpi>
+ <d:ydpi>240</d:ydpi>
+ </d:default>
+ <d:config name="Portrait, closed">
+ <d:keyboard-state>keyssoft</d:keyboard-state>
+ <d:screen-orientation>port</d:screen-orientation>
+ </d:config>
+ <d:config name="Landscape, closed">
+ <d:keyboard-state>keyssoft</d:keyboard-state>
+ <d:screen-orientation>land</d:screen-orientation>
+ </d:config>
+ <d:config name="Landscape, open">
+ <d:keyboard-state>keysexposed</d:keyboard-state>
+ <d:screen-orientation>land</d:screen-orientation>
+ <d:nav-state>navexposed</d:nav-state>
+ </d:config>
+ <d:supported-features>
+ <d:feature>android.hardware.bluetooth</d:feature>
+ <d:feature>android.hardware.camera</d:feature>
+ <d:feature>android.hardware.camera.autofocus</d:feature>
+ <d:feature>android.hardware.camera.flash</d:feature>
+ <d:feature>android.hardware.camera.front</d:feature>
+ <d:feature>android.hardware.location</d:feature>
+ <d:feature>android.hardware.location.gps</d:feature>
+ <d:feature>android.hardware.location.network</d:feature>
+ <d:feature>android.hardware.microphone</d:feature>
+ <d:feature>android.hardware.sensor.accelerometer</d:feature>
+ <d:feature>android.hardware.sensor.compass</d:feature>
+ <d:feature>android.hardware.sensor.light</d:feature>
+ <d:feature>android.hardware.sensor.proximity</d:feature>
+ <d:feature>android.hardware.telephony</d:feature>
+ <d:feature>android.hardware.telephony.gsm</d:feature>
+ <d:feature>android.hardware.touchscreen</d:feature>
+ <d:feature>android.hardware.touchscreen.multitouch</d:feature>
+ <d:feature>android.hardware.touchscreen.multitouch.distinct</d:feature>
+ <d:feature>android.hardware.wifi</d:feature>
+ <d:feature>android.software.live_wallpaper</d:feature>
+ </d:supported-features>
+
+ </d:device>
+
+</d:layout-devices> \ No newline at end of file
diff --git a/src/plugins/preflighting.core/devices/milestone-a853.xml b/src/plugins/preflighting.core/devices/milestone-a853.xml
new file mode 100644
index 0000000..f59b264
--- /dev/null
+++ b/src/plugins/preflighting.core/devices/milestone-a853.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<d:layout-devices xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:d="http://schemas.android.com/sdk/android/layout-devices/1">
+
+ <d:device id="milestone-a853" name="MILESTONE A853" provider="Motorola Mobility, Inc.">
+
+ <!--Product specifications: http://developer.motorola.com/products/milestone-a853/-->
+
+ <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:text-input-method>qwerty</d:text-input-method>
+ <d:nav-method>dpad</d:nav-method>
+ <d:screen-dimension>
+ <d:size>480</d:size>
+ <d:size>854</d:size>
+ </d:screen-dimension>
+ <d:xdpi>240</d:xdpi>
+ <d:ydpi>240</d:ydpi>
+ </d:default>
+ <d:config name="Portrait, closed">
+ <d:keyboard-state>keyssoft</d:keyboard-state>
+ <d:screen-orientation>port</d:screen-orientation>
+ </d:config>
+ <d:config name="Landscape, closed">
+ <d:keyboard-state>keyssoft</d:keyboard-state>
+ <d:screen-orientation>land</d:screen-orientation>
+ </d:config>
+ <d:config name="Landscape, open">
+ <d:keyboard-state>keysexposed</d:keyboard-state>
+ <d:screen-orientation>land</d:screen-orientation>
+ <d:nav-state>navexposed</d:nav-state>
+ </d:config>
+
+ </d:device>
+
+</d:layout-devices> \ No newline at end of file
diff --git a/src/plugins/preflighting.core/devices/milestone-qrty-a853.xml b/src/plugins/preflighting.core/devices/milestone-qrty-a853.xml
new file mode 100644
index 0000000..b7d0969
--- /dev/null
+++ b/src/plugins/preflighting.core/devices/milestone-qrty-a853.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<d:layout-devices xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:d="http://schemas.android.com/sdk/android/layout-devices/1">
+
+ <d:device id="milestone-qrty-a853" name="MOTO QRTYâ„¢ A853" provider="Motorola Mobility, Inc.">
+
+ <!--Product specifications: http://developer.motorola.com/products/milestone-qrty-a853/-->
+
+ <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:text-input-method>qwerty</d:text-input-method>
+ <d:nav-method>dpad</d:nav-method>
+ <d:screen-dimension>
+ <d:size>480</d:size>
+ <d:size>854</d:size>
+ </d:screen-dimension>
+ <d:xdpi>240</d:xdpi>
+ <d:ydpi>240</d:ydpi>
+ </d:default>
+ <d:config name="Portrait, closed">
+ <d:keyboard-state>keyssoft</d:keyboard-state>
+ <d:screen-orientation>port</d:screen-orientation>
+ </d:config>
+ <d:config name="Landscape, closed">
+ <d:keyboard-state>keyssoft</d:keyboard-state>
+ <d:screen-orientation>land</d:screen-orientation>
+ </d:config>
+ <d:config name="Landscape, open">
+ <d:keyboard-state>keysexposed</d:keyboard-state>
+ <d:screen-orientation>land</d:screen-orientation>
+ <d:nav-state>navexposed</d:nav-state>
+ </d:config>
+
+ </d:device>
+
+</d:layout-devices> \ No newline at end of file
diff --git a/src/plugins/preflighting.core/devices/milestone-xt720.xml b/src/plugins/preflighting.core/devices/milestone-xt720.xml
new file mode 100644
index 0000000..20a92b9
--- /dev/null
+++ b/src/plugins/preflighting.core/devices/milestone-xt720.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<d:layout-devices xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:d="http://schemas.android.com/sdk/android/layout-devices/1">
+
+ <d:device id="milestone-xt720" name="MILESTONE XT720" provider="Motorola Mobility, Inc.">
+
+ <!--Product specifications: http://developer.motorola.com/products/milestone-xt720/-->
+
+ <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:text-input-method>nokeys</d:text-input-method>
+ <d:keyboard-state>keyssoft</d:keyboard-state>
+ <d:nav-method>nonav</d:nav-method>
+ <d:screen-dimension>
+ <d:size>480</d:size>
+ <d:size>854</d:size>
+ </d:screen-dimension>
+ <d:xdpi>240</d:xdpi>
+ <d:ydpi>240</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:layout-devices> \ No newline at end of file
diff --git a/src/plugins/preflighting.core/devices/milestone2-me722.xml b/src/plugins/preflighting.core/devices/milestone2-me722.xml
new file mode 100644
index 0000000..4c93df4
--- /dev/null
+++ b/src/plugins/preflighting.core/devices/milestone2-me722.xml
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<d:layout-devices xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:d="http://schemas.android.com/sdk/android/layout-devices/1">
+
+ <d:device id="milestone2-me722" name="MILESTONE 2 ME722" provider="Motorola Mobility, Inc.">
+
+ <!--Product specifications: http://developer.motorola.com/products/milestone2-me722/-->
+
+ <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:text-input-method>qwerty</d:text-input-method>
+ <d:nav-method>dpad</d:nav-method>
+ <d:screen-dimension>
+ <d:size>480</d:size>
+ <d:size>854</d:size>
+ </d:screen-dimension>
+ <d:xdpi>240</d:xdpi>
+ <d:ydpi>240</d:ydpi>
+ </d:default>
+ <d:config name="Portrait, closed">
+ <d:keyboard-state>keyssoft</d:keyboard-state>
+ <d:screen-orientation>port</d:screen-orientation>
+ </d:config>
+ <d:config name="Landscape, closed">
+ <d:keyboard-state>keyssoft</d:keyboard-state>
+ <d:screen-orientation>land</d:screen-orientation>
+ </d:config>
+ <d:config name="Landscape, open">
+ <d:keyboard-state>keysexposed</d:keyboard-state>
+ <d:screen-orientation>land</d:screen-orientation>
+ <d:nav-state>navexposed</d:nav-state>
+ </d:config>
+ <d:supported-features>
+ <d:feature>android.hardware.bluetooth</d:feature>
+ <d:feature>android.hardware.camera</d:feature>
+ <d:feature>android.hardware.camera.autofocus</d:feature>
+ <d:feature>android.hardware.camera.flash</d:feature>
+ <d:feature>android.hardware.location</d:feature>
+ <d:feature>android.hardware.location.gps</d:feature>
+ <d:feature>android.hardware.location.network</d:feature>
+ <d:feature>android.hardware.microphone</d:feature>
+ <d:feature>android.hardware.sensor.accelerometer</d:feature>
+ <d:feature>android.hardware.sensor.compass</d:feature>
+ <d:feature>android.hardware.sensor.light</d:feature>
+ <d:feature>android.hardware.sensor.proximity</d:feature>
+ <d:feature>android.hardware.telephony</d:feature>
+ <d:feature>android.hardware.telephony.gsm</d:feature>
+ <d:feature>android.hardware.touchscreen</d:feature>
+ <d:feature>android.hardware.touchscreen.multitouch</d:feature>
+ <d:feature>android.hardware.touchscreen.multitouch.distinct</d:feature>
+ <d:feature>android.hardware.wifi</d:feature>
+ <d:feature>android.software.live_wallpaper</d:feature>
+ </d:supported-features>
+
+ </d:device>
+
+</d:layout-devices> \ No newline at end of file
diff --git a/src/plugins/preflighting.core/devices/milestone2.xml b/src/plugins/preflighting.core/devices/milestone2.xml
new file mode 100644
index 0000000..91afa5a
--- /dev/null
+++ b/src/plugins/preflighting.core/devices/milestone2.xml
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<d:layout-devices xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:d="http://schemas.android.com/sdk/android/layout-devices/1">
+
+ <d:device id="milestone2" name="MILESTONE 2 A953" provider="Motorola Mobility, Inc.">
+
+ <!--Product specifications: http://developer.motorola.com/products/milestone2/-->
+
+ <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:text-input-method>qwerty</d:text-input-method>
+ <d:nav-method>dpad</d:nav-method>
+ <d:screen-dimension>
+ <d:size>480</d:size>
+ <d:size>854</d:size>
+ </d:screen-dimension>
+ <d:xdpi>240</d:xdpi>
+ <d:ydpi>240</d:ydpi>
+ </d:default>
+ <d:config name="Portrait, closed">
+ <d:keyboard-state>keyssoft</d:keyboard-state>
+ <d:screen-orientation>port</d:screen-orientation>
+ </d:config>
+ <d:config name="Landscape, closed">
+ <d:keyboard-state>keyssoft</d:keyboard-state>
+ <d:screen-orientation>land</d:screen-orientation>
+ </d:config>
+ <d:config name="Landscape, open">
+ <d:keyboard-state>keysexposed</d:keyboard-state>
+ <d:screen-orientation>land</d:screen-orientation>
+ <d:nav-state>navexposed</d:nav-state>
+ </d:config>
+ <d:supported-features>
+ <d:feature>android.hardware.bluetooth</d:feature>
+ <d:feature>android.hardware.camera</d:feature>
+ <d:feature>android.hardware.camera.autofocus</d:feature>
+ <d:feature>android.hardware.camera.flash</d:feature>
+ <d:feature>android.hardware.location</d:feature>
+ <d:feature>android.hardware.location.gps</d:feature>
+ <d:feature>android.hardware.location.network</d:feature>
+ <d:feature>android.hardware.microphone</d:feature>
+ <d:feature>android.hardware.sensor.accelerometer</d:feature>
+ <d:feature>android.hardware.sensor.compass</d:feature>
+ <d:feature>android.hardware.sensor.light</d:feature>
+ <d:feature>android.hardware.sensor.proximity</d:feature>
+ <d:feature>android.hardware.telephony</d:feature>
+ <d:feature>android.hardware.telephony.gsm</d:feature>
+ <d:feature>android.hardware.touchscreen</d:feature>
+ <d:feature>android.hardware.touchscreen.multitouch</d:feature>
+ <d:feature>android.hardware.touchscreen.multitouch.distinct</d:feature>
+ <d:feature>android.hardware.wifi</d:feature>
+ <d:feature>android.software.live_wallpaper</d:feature>
+ </d:supported-features>
+
+ </d:device>
+
+</d:layout-devices> \ No newline at end of file
diff --git a/src/plugins/preflighting.core/devices/motoluxe-xt615.xml b/src/plugins/preflighting.core/devices/motoluxe-xt615.xml
new file mode 100644
index 0000000..5640700
--- /dev/null
+++ b/src/plugins/preflighting.core/devices/motoluxe-xt615.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<d:layout-devices xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:d="http://schemas.android.com/sdk/android/layout-devices/1">
+
+ <d:device id="motoluxe-xt615" name="MOTOLUXE XT615" provider="Motorola Mobility, Inc.">
+
+ <!--Product specifications: http://developer.motorola.com/products/motoluxe-xt615/-->
+
+ <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:text-input-method>nokeys</d:text-input-method>
+ <d:keyboard-state>keyssoft</d:keyboard-state>
+ <d:nav-method>nonav</d:nav-method>
+ <d:screen-dimension>
+ <d:size>480</d:size>
+ <d:size>854</d:size>
+ </d:screen-dimension>
+ <d:xdpi>240</d:xdpi>
+ <d:ydpi>240</d:ydpi>
+ </d:default>
+ <d:supported-features>
+ <d:feature>android.hardware.bluetooth</d:feature>
+ <d:feature>android.hardware.camera</d:feature>
+ <d:feature>android.hardware.camera.autofocus</d:feature>
+ <d:feature>android.hardware.camera.flash</d:feature>
+ <d:feature>android.hardware.camera.front</d:feature>
+ <d:feature>android.hardware.location</d:feature>
+ <d:feature>android.hardware.location.gps</d:feature>
+ <d:feature>android.hardware.location.network</d:feature>
+ <d:feature>android.hardware.microphone</d:feature>
+ <d:feature>android.hardware.sensor.accelerometer</d:feature>
+ <d:feature>android.hardware.sensor.compass</d:feature>
+ <d:feature>android.hardware.sensor.light</d:feature>
+ <d:feature>android.hardware.sensor.proximity</d:feature>
+ <d:feature>android.hardware.telephony</d:feature>
+ <d:feature>android.hardware.telephony.cdma</d:feature>
+ <d:feature>android.hardware.telephony.gsm</d:feature>
+ <d:feature>android.hardware.touchscreen</d:feature>
+ <d:feature>android.hardware.touchscreen.multitouch</d:feature>
+ <d:feature>android.hardware.wifi</d:feature>
+ <d:feature>android.software.live_wallpaper</d:feature>
+ <d:feature>android.software.sip</d:feature>
+ <d:feature>android.software.sip.voip</d:feature>
+ </d:supported-features>
+
+ </d:device>
+
+</d:layout-devices> \ No newline at end of file
diff --git a/src/plugins/preflighting.core/devices/mt620.xml b/src/plugins/preflighting.core/devices/mt620.xml
new file mode 100644
index 0000000..8514e2d
--- /dev/null
+++ b/src/plugins/preflighting.core/devices/mt620.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<d:layout-devices xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:d="http://schemas.android.com/sdk/android/layout-devices/1">
+
+ <d:device id="mt620" name="MOTO MT620" provider="Motorola Mobility, Inc.">
+
+ <!--Product specifications: http://developer.motorola.com/products/mt620/-->
+
+ <d:default>
+ <d:screen-size>normal</d:screen-size>
+ <d:screen-ratio>notlong</d:screen-ratio>
+ <d:screen-orientation>port</d:screen-orientation>
+ <d:pixel-density>mdpi</d:pixel-density>
+ <d:touch-type>finger</d:touch-type>
+ <d:text-input-method>qwerty</d:text-input-method>
+ <d:keyboard-state>keysexposed</d:keyboard-state>
+ <d:nav-method>nonav</d:nav-method>
+ <d:screen-dimension>
+ <d:size>320</d:size>
+ <d:size>480</d:size>
+ </d:screen-dimension>
+ <d:xdpi>160</d:xdpi>
+ <d:ydpi>160</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:keyboard-state>keyssoft</d:keyboard-state>
+ </d:config>
+
+ </d:device>
+
+</d:layout-devices> \ No newline at end of file
diff --git a/src/plugins/preflighting.core/devices/mt710.xml b/src/plugins/preflighting.core/devices/mt710.xml
new file mode 100644
index 0000000..6cf5c89
--- /dev/null
+++ b/src/plugins/preflighting.core/devices/mt710.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<d:layout-devices xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:d="http://schemas.android.com/sdk/android/layout-devices/1">
+
+ <d:device id="mt710" name="MOTO MT710" provider="Motorola Mobility, Inc.">
+
+ <!--Product specifications: http://developer.motorola.com/products/mt710/-->
+
+ <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>stylus</d:touch-type>
+ <d:text-input-method>nokeys</d:text-input-method>
+ <d:keyboard-state>keyssoft</d:keyboard-state>
+ <d:nav-method>nonav</d:nav-method>
+ <d:screen-dimension>
+ <d:size>480</d:size>
+ <d:size>854</d:size>
+ </d:screen-dimension>
+ <d:xdpi>240</d:xdpi>
+ <d:ydpi>240</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:layout-devices> \ No newline at end of file
diff --git a/src/plugins/preflighting.core/devices/mt716.xml b/src/plugins/preflighting.core/devices/mt716.xml
new file mode 100644
index 0000000..3df8c3f
--- /dev/null
+++ b/src/plugins/preflighting.core/devices/mt716.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<d:layout-devices xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:d="http://schemas.android.com/sdk/android/layout-devices/1">
+
+ <d:device id="mt716" name="MOTO MT716" provider="Motorola Mobility, Inc.">
+
+ <!--Product specifications: http://developer.motorola.com/products/mt716/-->
+
+ <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:text-input-method>qwerty</d:text-input-method>
+ <d:nav-method>dpad</d:nav-method>
+ <d:screen-dimension>
+ <d:size>480</d:size>
+ <d:size>854</d:size>
+ </d:screen-dimension>
+ <d:xdpi>240</d:xdpi>
+ <d:ydpi>240</d:ydpi>
+ </d:default>
+ <d:config name="Portrait, closed">
+ <d:keyboard-state>keyssoft</d:keyboard-state>
+ <d:screen-orientation>port</d:screen-orientation>
+ </d:config>
+ <d:config name="Landscape, closed">
+ <d:keyboard-state>keyssoft</d:keyboard-state>
+ <d:screen-orientation>land</d:screen-orientation>
+ </d:config>
+ <d:config name="Portrait, open">
+ <d:keyboard-state>keysexposed</d:keyboard-state>
+ <d:screen-orientation>port</d:screen-orientation>
+ <d:nav-state>navexposed</d:nav-state>
+ </d:config>
+
+ </d:device>
+
+</d:layout-devices> \ No newline at end of file
diff --git a/src/plugins/preflighting.core/devices/mt720.xml b/src/plugins/preflighting.core/devices/mt720.xml
new file mode 100644
index 0000000..e5d44e8
--- /dev/null
+++ b/src/plugins/preflighting.core/devices/mt720.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<d:layout-devices xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:d="http://schemas.android.com/sdk/android/layout-devices/1">
+
+ <d:device id="mt720" name="MOTO MT720" provider="Motorola Mobility, Inc.">
+
+ <!--Product specifications: http://developer.motorola.com/products/mt720/-->
+
+ <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>stylus</d:touch-type>
+ <d:text-input-method>nokeys</d:text-input-method>
+ <d:keyboard-state>keyssoft</d:keyboard-state>
+ <d:nav-method>nonav</d:nav-method>
+ <d:screen-dimension>
+ <d:size>480</d:size>
+ <d:size>854</d:size>
+ </d:screen-dimension>
+ <d:xdpi>240</d:xdpi>
+ <d:ydpi>240</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:layout-devices> \ No newline at end of file
diff --git a/src/plugins/preflighting.core/devices/mt810.xml b/src/plugins/preflighting.core/devices/mt810.xml
new file mode 100644
index 0000000..e8f2543
--- /dev/null
+++ b/src/plugins/preflighting.core/devices/mt810.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<d:layout-devices xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:d="http://schemas.android.com/sdk/android/layout-devices/1">
+
+ <d:device id="mt810" name="MOTO MT810" provider="Motorola Mobility, Inc.">
+
+ <!--Product specifications: http://developer.motorola.com/products/mt810/-->
+
+ <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:text-input-method>nokeys</d:text-input-method>
+ <d:keyboard-state>keyssoft</d:keyboard-state>
+ <d:nav-method>nonav</d:nav-method>
+ <d:screen-dimension>
+ <d:size>480</d:size>
+ <d:size>854</d:size>
+ </d:screen-dimension>
+ <d:xdpi>240</d:xdpi>
+ <d:ydpi>240</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:layout-devices> \ No newline at end of file
diff --git a/src/plugins/preflighting.core/devices/mt810lx.xml b/src/plugins/preflighting.core/devices/mt810lx.xml
new file mode 100644
index 0000000..96ecd35
--- /dev/null
+++ b/src/plugins/preflighting.core/devices/mt810lx.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<d:layout-devices xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:d="http://schemas.android.com/sdk/android/layout-devices/1">
+
+ <d:device id="mt810lx" name="MOTO MT810lx" provider="Motorola Mobility, Inc.">
+
+ <!--Product specifications: http://developer.motorola.com/products/mt810lx/-->
+
+ <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:text-input-method>nokeys</d:text-input-method>
+ <d:keyboard-state>keyssoft</d:keyboard-state>
+ <d:nav-method>nonav</d:nav-method>
+ <d:screen-dimension>
+ <d:size>480</d:size>
+ <d:size>854</d:size>
+ </d:screen-dimension>
+ <d:xdpi>240</d:xdpi>
+ <d:ydpi>240</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:layout-devices> \ No newline at end of file
diff --git a/src/plugins/preflighting.core/devices/mt870.xml b/src/plugins/preflighting.core/devices/mt870.xml
new file mode 100644
index 0000000..e6d2120
--- /dev/null
+++ b/src/plugins/preflighting.core/devices/mt870.xml
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<d:layout-devices xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:d="http://schemas.android.com/sdk/android/layout-devices/1">
+
+ <d:device id="mt870" name="MOTO MT870" provider="Motorola Mobility, Inc.">
+
+ <!--Product specifications: http://developer.motorola.com/products/mt870/-->
+
+ <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:text-input-method>nokeys</d:text-input-method>
+ <d:keyboard-state>keyssoft</d:keyboard-state>
+ <d:nav-method>nonav</d:nav-method>
+ <d:screen-dimension>
+ <d:size>540</d:size>
+ <d:size>960</d:size>
+ </d:screen-dimension>
+ <d:xdpi>240</d:xdpi>
+ <d:ydpi>240</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:supported-features>
+ <d:feature>android.hardware.bluetooth</d:feature>
+ <d:feature>android.hardware.camera</d:feature>
+ <d:feature>android.hardware.camera.autofocus</d:feature>
+ <d:feature>android.hardware.camera.flash</d:feature>
+ <d:feature>android.hardware.camera.front</d:feature>
+ <d:feature>android.hardware.location</d:feature>
+ <d:feature>android.hardware.location.gps</d:feature>
+ <d:feature>android.hardware.location.network</d:feature>
+ <d:feature>android.hardware.microphone</d:feature>
+ <d:feature>android.hardware.sensor.accelerometer</d:feature>
+ <d:feature>android.hardware.sensor.compass</d:feature>
+ <d:feature>android.hardware.sensor.light</d:feature>
+ <d:feature>android.hardware.sensor.proximity</d:feature>
+ <d:feature>android.hardware.telephony</d:feature>
+ <d:feature>android.hardware.telephony.gsm</d:feature>
+ <d:feature>android.hardware.touchscreen</d:feature>
+ <d:feature>android.hardware.touchscreen.multitouch</d:feature>
+ <d:feature>android.hardware.touchscreen.multitouch.distinct</d:feature>
+ <d:feature>android.hardware.wifi</d:feature>
+ <d:feature>android.software.live_wallpaper</d:feature>
+ </d:supported-features>
+
+ </d:device>
+
+</d:layout-devices> \ No newline at end of file
diff --git a/src/plugins/preflighting.core/devices/mt917.xml b/src/plugins/preflighting.core/devices/mt917.xml
new file mode 100644
index 0000000..f52d848
--- /dev/null
+++ b/src/plugins/preflighting.core/devices/mt917.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<d:layout-devices xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:d="http://schemas.android.com/sdk/android/layout-devices/1">
+
+ <d:device id="mt917" name="MOTO MT917" provider="Motorola Mobility, Inc.">
+
+ <!--Product specifications: http://developer.motorola.com/products/mt917/-->
+
+ <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:text-input-method>nokeys</d:text-input-method>
+ <d:keyboard-state>keyssoft</d:keyboard-state>
+ <d:nav-method>nonav</d:nav-method>
+ <d:screen-dimension>
+ <d:size>1280</d:size>
+ <d:size>720</d:size>
+ </d:screen-dimension>
+ <d:xdpi>240</d:xdpi>
+ <d:ydpi>240</d:ydpi>
+ </d:default>
+ <d:supported-features>
+ <d:feature>android.hardware.bluetooth</d:feature>
+ <d:feature>android.hardware.camera</d:feature>
+ <d:feature>android.hardware.camera.autofocus</d:feature>
+ <d:feature>android.hardware.camera.flash</d:feature>
+ <d:feature>android.hardware.camera.front</d:feature>
+ <d:feature>android.hardware.location</d:feature>
+ <d:feature>android.hardware.location.gps</d:feature>
+ <d:feature>android.hardware.location.network</d:feature>
+ <d:feature>android.hardware.microphone</d:feature>
+ <d:feature>android.hardware.sensor.accelerometer</d:feature>
+ <d:feature>android.hardware.sensor.compass</d:feature>
+ <d:feature>android.hardware.sensor.gyroscope</d:feature>
+ <d:feature>android.hardware.sensor.light</d:feature>
+ <d:feature>android.hardware.sensor.proximity</d:feature>
+ <d:feature>android.hardware.telephony</d:feature>
+ <d:feature>android.hardware.telephony.gsm</d:feature>
+ <d:feature>android.hardware.touchscreen</d:feature>
+ <d:feature>android.hardware.touchscreen.multitouch</d:feature>
+ <d:feature>android.hardware.touchscreen.multitouch.distinct</d:feature>
+ <d:feature>android.hardware.wifi</d:feature>
+ <d:feature>android.software.live_wallpaper</d:feature>
+ </d:supported-features>
+
+ </d:device>
+
+</d:layout-devices> \ No newline at end of file
diff --git a/src/plugins/preflighting.core/devices/photon-mb855.xml b/src/plugins/preflighting.core/devices/photon-mb855.xml
new file mode 100644
index 0000000..ed8fe4e
--- /dev/null
+++ b/src/plugins/preflighting.core/devices/photon-mb855.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<d:layout-devices xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:d="http://schemas.android.com/sdk/android/layout-devices/1">
+
+ <d:device id="photon-mb855" name="PHOTON 4G MB855" provider="Motorola Mobility, Inc.">
+
+ <!--Product specifications: http://developer.motorola.com/products/photon-mb855/-->
+
+ <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:text-input-method>nokeys</d:text-input-method>
+ <d:keyboard-state>keyssoft</d:keyboard-state>
+ <d:nav-method>nonav</d:nav-method>
+ <d:screen-dimension>
+ <d:size>540</d:size>
+ <d:size>960</d:size>
+ </d:screen-dimension>
+ <d:xdpi>240</d:xdpi>
+ <d:ydpi>240</d:ydpi>
+ </d:default>
+ <d:supported-features>
+ <d:feature>android.hardware.bluetooth</d:feature>
+ <d:feature>android.hardware.camera</d:feature>
+ <d:feature>android.hardware.camera.autofocus</d:feature>
+ <d:feature>android.hardware.camera.flash</d:feature>
+ <d:feature>android.hardware.camera.front</d:feature>
+ <d:feature>android.hardware.location</d:feature>
+ <d:feature>android.hardware.location.gps</d:feature>
+ <d:feature>android.hardware.location.network</d:feature>
+ <d:feature>android.hardware.microphone</d:feature>
+ <d:feature>android.hardware.sensor.accelerometer</d:feature>
+ <d:feature>android.hardware.sensor.compass</d:feature>
+ <d:feature>android.hardware.sensor.light</d:feature>
+ <d:feature>android.hardware.sensor.proximity</d:feature>
+ <d:feature>android.hardware.telephony</d:feature>
+ <d:feature>android.hardware.telephony.cdma</d:feature>
+ <d:feature>android.hardware.telephony.gsm</d:feature>
+ <d:feature>android.hardware.touchscreen</d:feature>
+ <d:feature>android.hardware.touchscreen.multitouch</d:feature>
+ <d:feature>android.hardware.touchscreen.multitouch.distinct</d:feature>
+ <d:feature>android.hardware.wifi</d:feature>
+ <d:feature>android.software.live_wallpaper</d:feature>
+ </d:supported-features>
+
+ </d:device>
+
+</d:layout-devices> \ No newline at end of file
diff --git a/src/plugins/preflighting.core/devices/pro-xt610.xml b/src/plugins/preflighting.core/devices/pro-xt610.xml
new file mode 100644
index 0000000..c62c9b1
--- /dev/null
+++ b/src/plugins/preflighting.core/devices/pro-xt610.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<d:layout-devices xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:d="http://schemas.android.com/sdk/android/layout-devices/1">
+
+ <d:device id="pro-xt610" name="PRO XT610" provider="Motorola Mobility, Inc.">
+
+ <!--Product specifications: http://developer.motorola.com/products/pro-xt610/-->
+
+ <d:default>
+ <d:screen-size>normal</d:screen-size>
+ <d:screen-ratio>notlong</d:screen-ratio>
+ <d:screen-orientation>port</d:screen-orientation>
+ <d:pixel-density>mdpi</d:pixel-density>
+ <d:touch-type>finger</d:touch-type>
+ <d:text-input-method>qwerty</d:text-input-method>
+ <d:keyboard-state>keysexposed</d:keyboard-state>
+ <d:nav-method>nonav</d:nav-method>
+ <d:screen-dimension>
+ <d:size>320</d:size>
+ <d:size>480</d:size>
+ </d:screen-dimension>
+ <d:xdpi>160</d:xdpi>
+ <d:ydpi>160</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:keyboard-state>keyssoft</d:keyboard-state>
+ </d:config>
+
+ </d:device>
+
+</d:layout-devices> \ No newline at end of file
diff --git a/src/plugins/preflighting.core/devices/quenchxt3.xml b/src/plugins/preflighting.core/devices/quenchxt3.xml
new file mode 100644
index 0000000..962986e
--- /dev/null
+++ b/src/plugins/preflighting.core/devices/quenchxt3.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<d:layout-devices xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:d="http://schemas.android.com/sdk/android/layout-devices/1">
+
+ <d:device id="quenchxt3" name="QUENCH XT3 XT502" provider="Motorola Mobility, Inc.">
+
+ <!--Product specifications: http://developer.motorola.com/products/quenchxt3/-->
+
+ <d:default>
+ <d:screen-size>normal</d:screen-size>
+ <d:screen-ratio>notlong</d:screen-ratio>
+ <d:screen-orientation>port</d:screen-orientation>
+ <d:pixel-density>mdpi</d:pixel-density>
+ <d:touch-type>finger</d:touch-type>
+ <d:text-input-method>nokeys</d:text-input-method>
+ <d:keyboard-state>keyssoft</d:keyboard-state>
+ <d:nav-method>trackball</d:nav-method>
+ <d:screen-dimension>
+ <d:size>320</d:size>
+ <d:size>480</d:size>
+ </d:screen-dimension>
+ <d:xdpi>160</d:xdpi>
+ <d:ydpi>160</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:layout-devices> \ No newline at end of file
diff --git a/src/plugins/preflighting.core/devices/quenchxt5.xml b/src/plugins/preflighting.core/devices/quenchxt5.xml
new file mode 100644
index 0000000..be9c57d
--- /dev/null
+++ b/src/plugins/preflighting.core/devices/quenchxt5.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<d:layout-devices xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:d="http://schemas.android.com/sdk/android/layout-devices/1">
+
+ <d:device id="quenchxt5" name="QUENCH XT5 XT502" provider="Motorola Mobility, Inc.">
+
+ <!--Product specifications: http://developer.motorola.com/products/quenchxt5/-->
+
+ <d:default>
+ <d:screen-size>normal</d:screen-size>
+ <d:screen-ratio>notlong</d:screen-ratio>
+ <d:screen-orientation>port</d:screen-orientation>
+ <d:pixel-density>mdpi</d:pixel-density>
+ <d:touch-type>finger</d:touch-type>
+ <d:text-input-method>nokeys</d:text-input-method>
+ <d:keyboard-state>keyssoft</d:keyboard-state>
+ <d:nav-method>trackball</d:nav-method>
+ <d:screen-dimension>
+ <d:size>320</d:size>
+ <d:size>480</d:size>
+ </d:screen-dimension>
+ <d:xdpi>160</d:xdpi>
+ <d:ydpi>160</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:layout-devices> \ No newline at end of file
diff --git a/src/plugins/preflighting.core/devices/razr-xt910.xml b/src/plugins/preflighting.core/devices/razr-xt910.xml
new file mode 100644
index 0000000..07c840a
--- /dev/null
+++ b/src/plugins/preflighting.core/devices/razr-xt910.xml
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<d:layout-devices xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:d="http://schemas.android.com/sdk/android/layout-devices/1">
+
+ <d:device id="razr-xt910" name="RAZR XT910" provider="Motorola Mobility, Inc.">
+
+ <!--Product specifications: http://developer.motorola.com/products/razr-xt910/-->
+
+ <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:text-input-method>nokeys</d:text-input-method>
+ <d:keyboard-state>keyssoft</d:keyboard-state>
+ <d:nav-method>nonav</d:nav-method>
+ <d:screen-dimension>
+ <d:size>540</d:size>
+ <d:size>960</d:size>
+ </d:screen-dimension>
+ <d:xdpi>240</d:xdpi>
+ <d:ydpi>240</d:ydpi>
+ </d:default>
+ <d:supported-features>
+ <d:feature>android.hardware.bluetooth</d:feature>
+ <d:feature>android.hardware.camera</d:feature>
+ <d:feature>android.hardware.camera.autofocus</d:feature>
+ <d:feature>android.hardware.camera.flash</d:feature>
+ <d:feature>android.hardware.location</d:feature>
+ <d:feature>android.hardware.location.gps</d:feature>
+ <d:feature>android.hardware.location.network</d:feature>
+ <d:feature>android.hardware.microphone</d:feature>
+ <d:feature>android.hardware.sensor.accelerometer</d:feature>
+ <d:feature>android.hardware.sensor.compass</d:feature>
+ <d:feature>android.hardware.sensor.light</d:feature>
+ <d:feature>android.hardware.sensor.proximity</d:feature>
+ <d:feature>android.hardware.telephony</d:feature>
+ <d:feature>android.hardware.telephony.cdma</d:feature>
+ <d:feature>android.hardware.telephony.gsm</d:feature>
+ <d:feature>android.hardware.touchscreen</d:feature>
+ <d:feature>android.hardware.touchscreen.multitouch</d:feature>
+ <d:feature>android.hardware.touchscreen.multitouch.distinct</d:feature>
+ <d:feature>android.hardware.wifi</d:feature>
+ <d:feature>android.software.live_wallpaper</d:feature>
+ </d:supported-features>
+
+ </d:device>
+
+</d:layout-devices> \ No newline at end of file
diff --git a/src/plugins/preflighting.core/devices/spice-key-xt316.xml b/src/plugins/preflighting.core/devices/spice-key-xt316.xml
new file mode 100644
index 0000000..f291cf7
--- /dev/null
+++ b/src/plugins/preflighting.core/devices/spice-key-xt316.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<d:layout-devices xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:d="http://schemas.android.com/sdk/android/layout-devices/1">
+
+ <d:device id="spice-key-xt316" name="SPICE Key XT316" provider="Motorola Mobility, Inc.">
+
+ <!--Product specifications: http://developer.motorola.com/products/spice-key-xt316/-->
+
+ <d:default>
+ <d:screen-size>small</d:screen-size>
+ <d:screen-ratio>notlong</d:screen-ratio>
+ <d:screen-orientation>port</d:screen-orientation>
+ <d:pixel-density>ldpi</d:pixel-density>
+ <d:touch-type>finger</d:touch-type>
+ <d:text-input-method>qwerty</d:text-input-method>
+ <d:keyboard-state>keysexposed</d:keyboard-state>
+ <d:nav-method>nonav</d:nav-method>
+ <d:screen-dimension>
+ <d:size>240</d:size>
+ <d:size>320</d:size>
+ </d:screen-dimension>
+ <d:xdpi>120</d:xdpi>
+ <d:ydpi>120</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:keyboard-state>keyssoft</d:keyboard-state>
+ </d:config>
+
+ </d:device>
+
+</d:layout-devices> \ No newline at end of file
diff --git a/src/plugins/preflighting.core/devices/spice-xt-xt531.xml b/src/plugins/preflighting.core/devices/spice-xt-xt531.xml
new file mode 100644
index 0000000..0818ec8
--- /dev/null
+++ b/src/plugins/preflighting.core/devices/spice-xt-xt531.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<d:layout-devices xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:d="http://schemas.android.com/sdk/android/layout-devices/1">
+
+ <d:device id="spice-xt-xt531" name="SPICE XT XT531" provider="Motorola Mobility, Inc.">
+
+ <!--Product specifications: http://developer.motorola.com/products/spice-xt-xt531/-->
+
+ <d:default>
+ <d:screen-size>normal</d:screen-size>
+ <d:screen-ratio>notlong</d:screen-ratio>
+ <d:screen-orientation>port</d:screen-orientation>
+ <d:pixel-density>mdpi</d:pixel-density>
+ <d:touch-type>finger</d:touch-type>
+ <d:text-input-method>nokeys</d:text-input-method>
+ <d:keyboard-state>keyssoft</d:keyboard-state>
+ <d:nav-method>nonav</d:nav-method>
+ <d:screen-dimension>
+ <d:size>320</d:size>
+ <d:size>480</d:size>
+ </d:screen-dimension>
+ <d:xdpi>160</d:xdpi>
+ <d:ydpi>160</d:ydpi>
+ </d:default>
+ <d:supported-features>
+ <d:feature>android.hardware.bluetooth</d:feature>
+ <d:feature>android.hardware.camera</d:feature>
+ <d:feature>android.hardware.camera.autofocus</d:feature>
+ <d:feature>android.hardware.camera.flash</d:feature>
+ <d:feature>android.hardware.camera.front</d:feature>
+ <d:feature>android.hardware.location</d:feature>
+ <d:feature>android.hardware.location.gps</d:feature>
+ <d:feature>android.hardware.location.network</d:feature>
+ <d:feature>android.hardware.microphone</d:feature>
+ <d:feature>android.hardware.sensor.accelerometer</d:feature>
+ <d:feature>android.hardware.sensor.compass</d:feature>
+ <d:feature>android.hardware.sensor.light</d:feature>
+ <d:feature>android.hardware.sensor.proximity</d:feature>
+ <d:feature>android.hardware.telephony</d:feature>
+ <d:feature>android.hardware.telephony.gsm</d:feature>
+ <d:feature>android.hardware.touchscreen</d:feature>
+ <d:feature>android.hardware.touchscreen.multitouch</d:feature>
+ <d:feature>android.hardware.wifi</d:feature>
+ <d:feature>android.software.live_wallpaper</d:feature>
+ <d:feature>android.software.sip</d:feature>
+ <d:feature>android.software.sip.voip</d:feature>
+ </d:supported-features>
+
+ </d:device>
+
+</d:layout-devices> \ No newline at end of file
diff --git a/src/plugins/preflighting.core/devices/spice.xml b/src/plugins/preflighting.core/devices/spice.xml
new file mode 100644
index 0000000..f910767
--- /dev/null
+++ b/src/plugins/preflighting.core/devices/spice.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<d:layout-devices xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:d="http://schemas.android.com/sdk/android/layout-devices/1">
+
+ <d:device id="spice" name="SPICE XT300" provider="Motorola Mobility, Inc.">
+
+ <!--Product specifications: http://developer.motorola.com/products/spice/-->
+
+ <d:default>
+ <d:screen-size>small</d:screen-size>
+ <d:screen-ratio>notlong</d:screen-ratio>
+ <d:screen-orientation>port</d:screen-orientation>
+ <d:pixel-density>ldpi</d:pixel-density>
+ <d:touch-type>finger</d:touch-type>
+ <d:text-input-method>qwerty</d:text-input-method>
+ <d:nav-method>trackball</d:nav-method>
+ <d:screen-dimension>
+ <d:size>240</d:size>
+ <d:size>320</d:size>
+ </d:screen-dimension>
+ <d:xdpi>120</d:xdpi>
+ <d:ydpi>120</d:ydpi>
+ </d:default>
+ <d:config name="Portrait, closed">
+ <d:keyboard-state>keyssoft</d:keyboard-state>
+ <d:screen-orientation>port</d:screen-orientation>
+ </d:config>
+ <d:config name="Landscape, closed">
+ <d:keyboard-state>keyssoft</d:keyboard-state>
+ <d:screen-orientation>land</d:screen-orientation>
+ </d:config>
+ <d:config name="Portrait, open">
+ <d:keyboard-state>keysexposed</d:keyboard-state>
+ <d:screen-orientation>port</d:screen-orientation>
+ </d:config>
+
+ </d:device>
+
+</d:layout-devices> \ No newline at end of file
diff --git a/src/plugins/preflighting.core/devices/titanium.xml b/src/plugins/preflighting.core/devices/titanium.xml
new file mode 100644
index 0000000..305ee16
--- /dev/null
+++ b/src/plugins/preflighting.core/devices/titanium.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<d:layout-devices xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:d="http://schemas.android.com/sdk/android/layout-devices/1">
+
+ <d:device id="titanium" name="TITANIUM" provider="Motorola Mobility, Inc.">
+
+ <!--Product specifications: http://developer.motorola.com/products/titanium/-->
+
+ <d:default>
+ <d:screen-size>normal</d:screen-size>
+ <d:screen-ratio>notlong</d:screen-ratio>
+ <d:screen-orientation>port</d:screen-orientation>
+ <d:pixel-density>mdpi</d:pixel-density>
+ <d:touch-type>finger</d:touch-type>
+ <d:text-input-method>qwerty</d:text-input-method>
+ <d:keyboard-state>keysexposed</d:keyboard-state>
+ <d:nav-method>nonav</d:nav-method>
+ <d:screen-dimension>
+ <d:size>320</d:size>
+ <d:size>480</d:size>
+ </d:screen-dimension>
+ <d:xdpi>160</d:xdpi>
+ <d:ydpi>160</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:keyboard-state>keyssoft</d:keyboard-state>
+ </d:config>
+ <d:supported-features>
+ <d:feature>android.hardware.bluetooth</d:feature>
+ <d:feature>android.hardware.camera</d:feature>
+ <d:feature>android.hardware.camera.autofocus</d:feature>
+ <d:feature>android.hardware.camera.flash</d:feature>
+ <d:feature>android.hardware.location</d:feature>
+ <d:feature>android.hardware.location.gps</d:feature>
+ <d:feature>android.hardware.location.network</d:feature>
+ <d:feature>android.hardware.sensor.accelerometer</d:feature>
+ <d:feature>android.hardware.sensor.compass</d:feature>
+ <d:feature>android.hardware.sensor.light</d:feature>
+ <d:feature>android.hardware.sensor.proximity</d:feature>
+ <d:feature>android.hardware.telephony</d:feature>
+ <d:feature>android.hardware.telephony.gsm</d:feature>
+ <d:feature>android.hardware.touchscreen</d:feature>
+ <d:feature>android.hardware.touchscreen.multitouch</d:feature>
+ <d:feature>android.hardware.wifi</d:feature>
+ </d:supported-features>
+
+ </d:device>
+
+</d:layout-devices> \ No newline at end of file
diff --git a/src/plugins/preflighting.core/devices/triumph-wx435.xml b/src/plugins/preflighting.core/devices/triumph-wx435.xml
new file mode 100644
index 0000000..dea3997
--- /dev/null
+++ b/src/plugins/preflighting.core/devices/triumph-wx435.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<d:layout-devices xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:d="http://schemas.android.com/sdk/android/layout-devices/1">
+
+ <d:device id="triumph-wx435" name="TRIUMPH WX435" provider="Motorola Mobility, Inc.">
+
+ <!--Product specifications: http://developer.motorola.com/products/triumph-wx435/-->
+
+ <d:default>
+ <d:screen-size>large</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:text-input-method>nokeys</d:text-input-method>
+ <d:keyboard-state>keyssoft</d:keyboard-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>240</d:xdpi>
+ <d:ydpi>240</d:ydpi>
+ </d:default>
+ <d:supported-features>
+ <d:feature>android.hardware.bluetooth</d:feature>
+ <d:feature>android.hardware.camera</d:feature>
+ <d:feature>android.hardware.camera.autofocus</d:feature>
+ <d:feature>android.hardware.camera.flash</d:feature>
+ <d:feature>android.hardware.location</d:feature>
+ <d:feature>android.hardware.location.gps</d:feature>
+ <d:feature>android.hardware.location.network</d:feature>
+ <d:feature>android.hardware.microphone</d:feature>
+ <d:feature>android.hardware.sensor.accelerometer</d:feature>
+ <d:feature>android.hardware.sensor.compass</d:feature>
+ <d:feature>android.hardware.sensor.light</d:feature>
+ <d:feature>android.hardware.sensor.proximity</d:feature>
+ <d:feature>android.hardware.telephony</d:feature>
+ <d:feature>android.hardware.telephony.cdma</d:feature>
+ <d:feature>android.hardware.touchscreen</d:feature>
+ <d:feature>android.hardware.touchscreen.multitouch</d:feature>
+ <d:feature>android.hardware.wifi</d:feature>
+ <d:feature>android.software.live_wallpaper</d:feature>
+ </d:supported-features>
+
+ </d:device>
+
+</d:layout-devices> \ No newline at end of file
diff --git a/src/plugins/preflighting.core/devices/xoom-2-3g-mz616.xml b/src/plugins/preflighting.core/devices/xoom-2-3g-mz616.xml
new file mode 100644
index 0000000..b0b6bee
--- /dev/null
+++ b/src/plugins/preflighting.core/devices/xoom-2-3g-mz616.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<d:layout-devices xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:d="http://schemas.android.com/sdk/android/layout-devices/1">
+
+ <d:device id="xoom-2-3g-mz616" name="XOOM 2 3G MZ616" provider="Motorola Mobility, Inc.">
+
+ <!--Product specifications: http://developer.motorola.com/products/xoom-2-3g-mz616/-->
+
+ <d:default>
+ <d:screen-size>xlarge</d:screen-size>
+ <d:screen-ratio>long</d:screen-ratio>
+ <d:screen-orientation>land</d:screen-orientation>
+ <d:pixel-density>mdpi</d:pixel-density>
+ <d:touch-type>finger</d:touch-type>
+ <d:text-input-method>nokeys</d:text-input-method>
+ <d:keyboard-state>keyssoft</d:keyboard-state>
+ <d:nav-method>nonav</d:nav-method>
+ <d:screen-dimension>
+ <d:size>800</d:size>
+ <d:size>1280</d:size>
+ </d:screen-dimension>
+ <d:xdpi>160</d:xdpi>
+ <d:ydpi>160</d:ydpi>
+ </d:default>
+ <d:supported-features>
+ <d:feature>android.hardware.bluetooth</d:feature>
+ <d:feature>android.hardware.camera</d:feature>
+ <d:feature>android.hardware.camera.autofocus</d:feature>
+ <d:feature>android.hardware.camera.flash</d:feature>
+ <d:feature>android.hardware.camera.front</d:feature>
+ <d:feature>android.hardware.faketouch</d:feature>
+ <d:feature>android.hardware.location</d:feature>
+ <d:feature>android.hardware.location.gps</d:feature>
+ <d:feature>android.hardware.location.network</d:feature>
+ <d:feature>android.hardware.microphone</d:feature>
+ <d:feature>android.hardware.screen.landscape</d:feature>
+ <d:feature>android.hardware.screen.portrait</d:feature>
+ <d:feature>android.hardware.sensor.accelerometer</d:feature>
+ <d:feature>android.hardware.sensor.barometer</d:feature>
+ <d:feature>android.hardware.sensor.compass</d:feature>
+ <d:feature>android.hardware.sensor.gyroscope</d:feature>
+ <d:feature>android.hardware.sensor.light</d:feature>
+ <d:feature>android.hardware.touchscreen</d:feature>
+ <d:feature>android.hardware.touchscreen.multitouch</d:feature>
+ <d:feature>android.hardware.touchscreen.multitouch.distinct</d:feature>
+ <d:feature>android.hardware.touchscreen.multitouch.jazzhand</d:feature>
+ <d:feature>android.hardware.usb.accessory</d:feature>
+ <d:feature>android.hardware.usb.host</d:feature>
+ <d:feature>android.hardware.wifi</d:feature>
+ <d:feature>android.software.live_wallpaper</d:feature>
+ <d:feature>android.software.sip</d:feature>
+ <d:feature>android.software.sip.voip</d:feature>
+ </d:supported-features>
+
+ </d:device>
+
+</d:layout-devices> \ No newline at end of file
diff --git a/src/plugins/preflighting.core/devices/xoom-2-me-3g-mz608.xml b/src/plugins/preflighting.core/devices/xoom-2-me-3g-mz608.xml
new file mode 100644
index 0000000..747a47d
--- /dev/null
+++ b/src/plugins/preflighting.core/devices/xoom-2-me-3g-mz608.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<d:layout-devices xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:d="http://schemas.android.com/sdk/android/layout-devices/1">
+
+ <d:device id="xoom-2-me-3g-mz608" name="XOOM 2 Media Edition 3G MZ608" provider="Motorola Mobility, Inc.">
+
+ <!--Product specifications: http://developer.motorola.com/products/xoom-2-me-3g-mz608/-->
+
+ <d:default>
+ <d:screen-size>xlarge</d:screen-size>
+ <d:screen-ratio>long</d:screen-ratio>
+ <d:screen-orientation>port</d:screen-orientation>
+ <d:pixel-density>mdpi</d:pixel-density>
+ <d:touch-type>finger</d:touch-type>
+ <d:text-input-method>nokeys</d:text-input-method>
+ <d:keyboard-state>keyssoft</d:keyboard-state>
+ <d:nav-method>nonav</d:nav-method>
+ <d:screen-dimension>
+ <d:size>1280</d:size>
+ <d:size>800</d:size>
+ </d:screen-dimension>
+ <d:xdpi>160</d:xdpi>
+ <d:ydpi>160</d:ydpi>
+ </d:default>
+ <d:supported-features>
+ <d:feature>android.hardware.bluetooth</d:feature>
+ <d:feature>android.hardware.camera</d:feature>
+ <d:feature>android.hardware.camera.autofocus</d:feature>
+ <d:feature>android.hardware.camera.flash</d:feature>
+ <d:feature>android.hardware.camera.front</d:feature>
+ <d:feature>android.hardware.faketouch</d:feature>
+ <d:feature>android.hardware.location</d:feature>
+ <d:feature>android.hardware.location.gps</d:feature>
+ <d:feature>android.hardware.location.network</d:feature>
+ <d:feature>android.hardware.microphone</d:feature>
+ <d:feature>android.hardware.screen.landscape</d:feature>
+ <d:feature>android.hardware.screen.portrait</d:feature>
+ <d:feature>android.hardware.sensor.accelerometer</d:feature>
+ <d:feature>android.hardware.sensor.barometer</d:feature>
+ <d:feature>android.hardware.sensor.compass</d:feature>
+ <d:feature>android.hardware.sensor.gyroscope</d:feature>
+ <d:feature>android.hardware.sensor.light</d:feature>
+ <d:feature>android.hardware.touchscreen</d:feature>
+ <d:feature>android.hardware.touchscreen.multitouch</d:feature>
+ <d:feature>android.hardware.touchscreen.multitouch.distinct</d:feature>
+ <d:feature>android.hardware.touchscreen.multitouch.jazzhand</d:feature>
+ <d:feature>android.hardware.usb.accessory</d:feature>
+ <d:feature>android.hardware.usb.host</d:feature>
+ <d:feature>android.hardware.wifi</d:feature>
+ <d:feature>android.software.live_wallpaper</d:feature>
+ <d:feature>android.software.sip</d:feature>
+ <d:feature>android.software.sip.voip</d:feature>
+ </d:supported-features>
+
+ </d:device>
+
+</d:layout-devices> \ No newline at end of file
diff --git a/src/plugins/preflighting.core/devices/xoom-2-me-mz607.xml b/src/plugins/preflighting.core/devices/xoom-2-me-mz607.xml
new file mode 100644
index 0000000..0418a3a
--- /dev/null
+++ b/src/plugins/preflighting.core/devices/xoom-2-me-mz607.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<d:layout-devices xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:d="http://schemas.android.com/sdk/android/layout-devices/1">
+
+ <d:device id="xoom-2-me-mz607" name="XOOM 2 Media Edition with WiFi MZ607" provider="Motorola Mobility, Inc.">
+
+ <!--Product specifications: http://developer.motorola.com/products/xoom-2-me-mz607/-->
+
+ <d:default>
+ <d:screen-size>xlarge</d:screen-size>
+ <d:screen-ratio>long</d:screen-ratio>
+ <d:screen-orientation>port</d:screen-orientation>
+ <d:pixel-density>mdpi</d:pixel-density>
+ <d:touch-type>finger</d:touch-type>
+ <d:text-input-method>nokeys</d:text-input-method>
+ <d:keyboard-state>keyssoft</d:keyboard-state>
+ <d:nav-method>nonav</d:nav-method>
+ <d:screen-dimension>
+ <d:size>1280</d:size>
+ <d:size>800</d:size>
+ </d:screen-dimension>
+ <d:xdpi>160</d:xdpi>
+ <d:ydpi>160</d:ydpi>
+ </d:default>
+ <d:supported-features>
+ <d:feature>android.hardware.bluetooth</d:feature>
+ <d:feature>android.hardware.camera</d:feature>
+ <d:feature>android.hardware.camera.autofocus</d:feature>
+ <d:feature>android.hardware.camera.flash</d:feature>
+ <d:feature>android.hardware.camera.front</d:feature>
+ <d:feature>android.hardware.faketouch</d:feature>
+ <d:feature>android.hardware.location</d:feature>
+ <d:feature>android.hardware.location.gps</d:feature>
+ <d:feature>android.hardware.location.network</d:feature>
+ <d:feature>android.hardware.microphone</d:feature>
+ <d:feature>android.hardware.screen.landscape</d:feature>
+ <d:feature>android.hardware.screen.portrait</d:feature>
+ <d:feature>android.hardware.sensor.accelerometer</d:feature>
+ <d:feature>android.hardware.sensor.barometer</d:feature>
+ <d:feature>android.hardware.sensor.compass</d:feature>
+ <d:feature>android.hardware.sensor.gyroscope</d:feature>
+ <d:feature>android.hardware.sensor.light</d:feature>
+ <d:feature>android.hardware.touchscreen</d:feature>
+ <d:feature>android.hardware.touchscreen.multitouch</d:feature>
+ <d:feature>android.hardware.touchscreen.multitouch.distinct</d:feature>
+ <d:feature>android.hardware.touchscreen.multitouch.jazzhand</d:feature>
+ <d:feature>android.hardware.usb.accessory</d:feature>
+ <d:feature>android.hardware.usb.host</d:feature>
+ <d:feature>android.hardware.wifi</d:feature>
+ <d:feature>android.software.live_wallpaper</d:feature>
+ <d:feature>android.software.sip</d:feature>
+ <d:feature>android.software.sip.voip</d:feature>
+ </d:supported-features>
+
+ </d:device>
+
+</d:layout-devices> \ No newline at end of file
diff --git a/src/plugins/preflighting.core/devices/xoom-2-mz615.xml b/src/plugins/preflighting.core/devices/xoom-2-mz615.xml
new file mode 100644
index 0000000..0e8ec7d
--- /dev/null
+++ b/src/plugins/preflighting.core/devices/xoom-2-mz615.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<d:layout-devices xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:d="http://schemas.android.com/sdk/android/layout-devices/1">
+
+ <d:device id="xoom-2-mz615" name="XOOM 2 with WiFi MZ615" provider="Motorola Mobility, Inc.">
+
+ <!--Product specifications: http://developer.motorola.com/products/xoom-2-mz615/-->
+
+ <d:default>
+ <d:screen-size>xlarge</d:screen-size>
+ <d:screen-ratio>long</d:screen-ratio>
+ <d:screen-orientation>land</d:screen-orientation>
+ <d:pixel-density>mdpi</d:pixel-density>
+ <d:touch-type>finger</d:touch-type>
+ <d:text-input-method>nokeys</d:text-input-method>
+ <d:keyboard-state>keyssoft</d:keyboard-state>
+ <d:nav-method>nonav</d:nav-method>
+ <d:screen-dimension>
+ <d:size>800</d:size>
+ <d:size>1280</d:size>
+ </d:screen-dimension>
+ <d:xdpi>160</d:xdpi>
+ <d:ydpi>160</d:ydpi>
+ </d:default>
+ <d:supported-features>
+ <d:feature>android.hardware.bluetooth</d:feature>
+ <d:feature>android.hardware.camera</d:feature>
+ <d:feature>android.hardware.camera.autofocus</d:feature>
+ <d:feature>android.hardware.camera.flash</d:feature>
+ <d:feature>android.hardware.camera.front</d:feature>
+ <d:feature>android.hardware.faketouch</d:feature>
+ <d:feature>android.hardware.location</d:feature>
+ <d:feature>android.hardware.location.gps</d:feature>
+ <d:feature>android.hardware.location.network</d:feature>
+ <d:feature>android.hardware.microphone</d:feature>
+ <d:feature>android.hardware.screen.landscape</d:feature>
+ <d:feature>android.hardware.screen.portrait</d:feature>
+ <d:feature>android.hardware.sensor.accelerometer</d:feature>
+ <d:feature>android.hardware.sensor.barometer</d:feature>
+ <d:feature>android.hardware.sensor.compass</d:feature>
+ <d:feature>android.hardware.sensor.gyroscope</d:feature>
+ <d:feature>android.hardware.sensor.light</d:feature>
+ <d:feature>android.hardware.touchscreen</d:feature>
+ <d:feature>android.hardware.touchscreen.multitouch</d:feature>
+ <d:feature>android.hardware.touchscreen.multitouch.distinct</d:feature>
+ <d:feature>android.hardware.touchscreen.multitouch.jazzhand</d:feature>
+ <d:feature>android.hardware.usb.accessory</d:feature>
+ <d:feature>android.hardware.usb.host</d:feature>
+ <d:feature>android.hardware.wifi</d:feature>
+ <d:feature>android.software.live_wallpaper</d:feature>
+ <d:feature>android.software.sip</d:feature>
+ <d:feature>android.software.sip.voip</d:feature>
+ </d:supported-features>
+
+ </d:device>
+
+</d:layout-devices> \ No newline at end of file
diff --git a/src/plugins/preflighting.core/devices/xoom-mz505.xml b/src/plugins/preflighting.core/devices/xoom-mz505.xml
new file mode 100644
index 0000000..60727b9
--- /dev/null
+++ b/src/plugins/preflighting.core/devices/xoom-mz505.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<d:layout-devices xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:d="http://schemas.android.com/sdk/android/layout-devices/1">
+
+ <d:device id="xoom-mz505" name="XOOM Family Edition MZ505" provider="Motorola Mobility, Inc.">
+
+ <!--Product specifications: http://developer.motorola.com/products/xoom-mz505/-->
+
+ <d:default>
+ <d:screen-size>xlarge</d:screen-size>
+ <d:screen-ratio>long</d:screen-ratio>
+ <d:screen-orientation>land</d:screen-orientation>
+ <d:pixel-density>mdpi</d:pixel-density>
+ <d:touch-type>finger</d:touch-type>
+ <d:text-input-method>nokeys</d:text-input-method>
+ <d:keyboard-state>keyssoft</d:keyboard-state>
+ <d:nav-method>nonav</d:nav-method>
+ <d:screen-dimension>
+ <d:size>800</d:size>
+ <d:size>1280</d:size>
+ </d:screen-dimension>
+ <d:xdpi>160</d:xdpi>
+ <d:ydpi>160</d:ydpi>
+ </d:default>
+ <d:supported-features>
+ <d:feature>android.hardware.bluetooth</d:feature>
+ <d:feature>android.hardware.camera</d:feature>
+ <d:feature>android.hardware.camera.autofocus</d:feature>
+ <d:feature>android.hardware.camera.flash</d:feature>
+ <d:feature>android.hardware.camera.front</d:feature>
+ <d:feature>android.hardware.faketouch</d:feature>
+ <d:feature>android.hardware.location</d:feature>
+ <d:feature>android.hardware.location.gps</d:feature>
+ <d:feature>android.hardware.location.network</d:feature>
+ <d:feature>android.hardware.microphone</d:feature>
+ <d:feature>android.hardware.sensor.accelerometer</d:feature>
+ <d:feature>android.hardware.sensor.compass</d:feature>
+ <d:feature>android.hardware.sensor.gyroscope</d:feature>
+ <d:feature>android.hardware.sensor.light</d:feature>
+ <d:feature>android.hardware.touchscreen</d:feature>
+ <d:feature>android.hardware.touchscreen.multitouch</d:feature>
+ <d:feature>android.hardware.touchscreen.multitouch.distinct</d:feature>
+ <d:feature>android.hardware.touchscreen.multitouch.jazzhand</d:feature>
+ <d:feature>android.hardware.usb.accessory</d:feature>
+ <d:feature>android.hardware.usb.host</d:feature>
+ <d:feature>android.hardware.wifi</d:feature>
+ <d:feature>android.software.live_wallpaper</d:feature>
+ </d:supported-features>
+
+ </d:device>
+
+</d:layout-devices> \ No newline at end of file
diff --git a/src/plugins/preflighting.core/devices/xoom-mz601.xml b/src/plugins/preflighting.core/devices/xoom-mz601.xml
new file mode 100644
index 0000000..a71da6d
--- /dev/null
+++ b/src/plugins/preflighting.core/devices/xoom-mz601.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<d:layout-devices xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:d="http://schemas.android.com/sdk/android/layout-devices/1">
+
+ <d:device id="xoom-mz601" name="XOOM 3G MZ601, MZ603" provider="Motorola Mobility, Inc.">
+
+ <!--Product specifications: http://developer.motorola.com/products/xoom-mz601/-->
+
+ <d:default>
+ <d:screen-size>xlarge</d:screen-size>
+ <d:screen-ratio>long</d:screen-ratio>
+ <d:screen-orientation>land</d:screen-orientation>
+ <d:pixel-density>mdpi</d:pixel-density>
+ <d:touch-type>finger</d:touch-type>
+ <d:text-input-method>nokeys</d:text-input-method>
+ <d:keyboard-state>keyssoft</d:keyboard-state>
+ <d:nav-method>nonav</d:nav-method>
+ <d:screen-dimension>
+ <d:size>800</d:size>
+ <d:size>1280</d:size>
+ </d:screen-dimension>
+ <d:xdpi>160</d:xdpi>
+ <d:ydpi>160</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:supported-features>
+ <d:feature>android.hardware.bluetooth</d:feature>
+ <d:feature>android.hardware.camera</d:feature>
+ <d:feature>android.hardware.camera.autofocus</d:feature>
+ <d:feature>android.hardware.camera.flash</d:feature>
+ <d:feature>android.hardware.camera.front</d:feature>
+ <d:feature>android.hardware.faketouch</d:feature>
+ <d:feature>android.hardware.location</d:feature>
+ <d:feature>android.hardware.location.gps</d:feature>
+ <d:feature>android.hardware.location.network</d:feature>
+ <d:feature>android.hardware.microphone</d:feature>
+ <d:feature>android.hardware.screen.landscape</d:feature>
+ <d:feature>android.hardware.screen.portrait</d:feature>
+ <d:feature>android.hardware.sensor.accelerometer</d:feature>
+ <d:feature>android.hardware.sensor.barometer</d:feature>
+ <d:feature>android.hardware.sensor.compass</d:feature>
+ <d:feature>android.hardware.sensor.gyroscope</d:feature>
+ <d:feature>android.hardware.sensor.light</d:feature>
+ <d:feature>android.hardware.touchscreen</d:feature>
+ <d:feature>android.hardware.touchscreen.multitouch</d:feature>
+ <d:feature>android.hardware.touchscreen.multitouch.distinct</d:feature>
+ <d:feature>android.hardware.touchscreen.multitouch.jazzhand</d:feature>
+ <d:feature>android.hardware.usb.accessory</d:feature>
+ <d:feature>android.hardware.usb.host</d:feature>
+ <d:feature>android.hardware.wifi</d:feature>
+ <d:feature>android.software.live_wallpaper</d:feature>
+ <d:feature>android.software.sip</d:feature>
+ <d:feature>android.software.sip.voip</d:feature>
+ </d:supported-features>
+
+ </d:device>
+
+</d:layout-devices> \ No newline at end of file
diff --git a/src/plugins/preflighting.core/devices/xoom-mz604.xml b/src/plugins/preflighting.core/devices/xoom-mz604.xml
new file mode 100644
index 0000000..99dfadf
--- /dev/null
+++ b/src/plugins/preflighting.core/devices/xoom-mz604.xml
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<d:layout-devices xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:d="http://schemas.android.com/sdk/android/layout-devices/1">
+
+ <d:device id="xoom-mz604" name="XOOM with Wi-Fi MZ604, MZ606" provider="Motorola Mobility, Inc.">
+
+ <!--Product specifications: http://developer.motorola.com/products/xoom-mz604/-->
+
+ <d:default>
+ <d:screen-size>xlarge</d:screen-size>
+ <d:screen-ratio>long</d:screen-ratio>
+ <d:screen-orientation>land</d:screen-orientation>
+ <d:pixel-density>mdpi</d:pixel-density>
+ <d:touch-type>finger</d:touch-type>
+ <d:text-input-method>nokeys</d:text-input-method>
+ <d:keyboard-state>keyssoft</d:keyboard-state>
+ <d:nav-method>nonav</d:nav-method>
+ <d:screen-dimension>
+ <d:size>800</d:size>
+ <d:size>1280</d:size>
+ </d:screen-dimension>
+ <d:xdpi>160</d:xdpi>
+ <d:ydpi>160</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:supported-features>
+ <d:feature>android.hardware.bluetooth</d:feature>
+ <d:feature>android.hardware.camera</d:feature>
+ <d:feature>android.hardware.camera.autofocus</d:feature>
+ <d:feature>android.hardware.camera.flash</d:feature>
+ <d:feature>android.hardware.camera.front</d:feature>
+ <d:feature>android.hardware.faketouch</d:feature>
+ <d:feature>android.hardware.location</d:feature>
+ <d:feature>android.hardware.location.gps</d:feature>
+ <d:feature>android.hardware.location.network</d:feature>
+ <d:feature>android.hardware.microphone</d:feature>
+ <d:feature>android.hardware.screen.landscape</d:feature>
+ <d:feature>android.hardware.screen.portrait</d:feature>
+ <d:feature>android.hardware.sensor.accelerometer</d:feature>
+ <d:feature>android.hardware.sensor.barometer</d:feature>
+ <d:feature>android.hardware.sensor.compass</d:feature>
+ <d:feature>android.hardware.sensor.gyroscope</d:feature>
+ <d:feature>android.hardware.sensor.light</d:feature>
+ <d:feature>android.hardware.touchscreen</d:feature>
+ <d:feature>android.hardware.touchscreen.multitouch</d:feature>
+ <d:feature>android.hardware.touchscreen.multitouch.distinct</d:feature>
+ <d:feature>android.hardware.touchscreen.multitouch.jazzhand</d:feature>
+ <d:feature>android.hardware.usb.accessory</d:feature>
+ <d:feature>android.hardware.usb.host</d:feature>
+ <d:feature>android.hardware.wifi</d:feature>
+ <d:feature>android.software.live_wallpaper</d:feature>
+ <d:feature>android.software.sip</d:feature>
+ <d:feature>android.software.sip.voip</d:feature>
+ <d:feature>com.google.android.feature.GOOGLE_BUILD</d:feature>
+ </d:supported-features>
+
+ </d:device>
+
+</d:layout-devices> \ No newline at end of file
diff --git a/src/plugins/preflighting.core/devices/xoom-mz605.xml b/src/plugins/preflighting.core/devices/xoom-mz605.xml
new file mode 100644
index 0000000..de19f2e
--- /dev/null
+++ b/src/plugins/preflighting.core/devices/xoom-mz605.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<d:layout-devices xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:d="http://schemas.android.com/sdk/android/layout-devices/1">
+
+ <d:device id="xoom-mz605" name="XOOM 3G MZ605" provider="Motorola Mobility, Inc.">
+
+ <!--Product specifications: http://developer.motorola.com/products/xoom-mz605/-->
+
+ <d:default>
+ <d:screen-size>xlarge</d:screen-size>
+ <d:screen-ratio>long</d:screen-ratio>
+ <d:screen-orientation>land</d:screen-orientation>
+ <d:pixel-density>mdpi</d:pixel-density>
+ <d:touch-type>finger</d:touch-type>
+ <d:text-input-method>nokeys</d:text-input-method>
+ <d:keyboard-state>keyssoft</d:keyboard-state>
+ <d:nav-method>nonav</d:nav-method>
+ <d:screen-dimension>
+ <d:size>800</d:size>
+ <d:size>1280</d:size>
+ </d:screen-dimension>
+ <d:xdpi>160</d:xdpi>
+ <d:ydpi>160</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:supported-features>
+ <d:feature>android.hardware.bluetooth</d:feature>
+ <d:feature>android.hardware.camera</d:feature>
+ <d:feature>android.hardware.camera.autofocus</d:feature>
+ <d:feature>android.hardware.camera.flash</d:feature>
+ <d:feature>android.hardware.camera.front</d:feature>
+ <d:feature>android.hardware.faketouch</d:feature>
+ <d:feature>android.hardware.location</d:feature>
+ <d:feature>android.hardware.location.gps</d:feature>
+ <d:feature>android.hardware.location.network</d:feature>
+ <d:feature>android.hardware.microphone</d:feature>
+ <d:feature>android.hardware.screen.landscape</d:feature>
+ <d:feature>android.hardware.screen.portrait</d:feature>
+ <d:feature>android.hardware.sensor.accelerometer</d:feature>
+ <d:feature>android.hardware.sensor.barometer</d:feature>
+ <d:feature>android.hardware.sensor.compass</d:feature>
+ <d:feature>android.hardware.sensor.gyroscope</d:feature>
+ <d:feature>android.hardware.sensor.light</d:feature>
+ <d:feature>android.hardware.touchscreen</d:feature>
+ <d:feature>android.hardware.touchscreen.multitouch</d:feature>
+ <d:feature>android.hardware.touchscreen.multitouch.distinct</d:feature>
+ <d:feature>android.hardware.touchscreen.multitouch.jazzhand</d:feature>
+ <d:feature>android.hardware.usb.accessory</d:feature>
+ <d:feature>android.hardware.usb.host</d:feature>
+ <d:feature>android.hardware.wifi</d:feature>
+ <d:feature>android.software.live_wallpaper</d:feature>
+ <d:feature>android.software.sip</d:feature>
+ <d:feature>android.software.sip.voip</d:feature>
+ </d:supported-features>
+
+ </d:device>
+
+</d:layout-devices> \ No newline at end of file
diff --git a/src/plugins/preflighting.core/devices/xoom.xml b/src/plugins/preflighting.core/devices/xoom.xml
new file mode 100644
index 0000000..7b7009f
--- /dev/null
+++ b/src/plugins/preflighting.core/devices/xoom.xml
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<d:layout-devices xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:d="http://schemas.android.com/sdk/android/layout-devices/1">
+
+ <d:device id="xoom" name="XOOM 4G LTE MZ600, MZ602" provider="Motorola Mobility, Inc.">
+
+ <!--Product specifications: http://developer.motorola.com/products/xoom/-->
+
+ <d:default>
+ <d:screen-size>xlarge</d:screen-size>
+ <d:screen-ratio>long</d:screen-ratio>
+ <d:screen-orientation>land</d:screen-orientation>
+ <d:pixel-density>mdpi</d:pixel-density>
+ <d:touch-type>finger</d:touch-type>
+ <d:text-input-method>nokeys</d:text-input-method>
+ <d:keyboard-state>keyssoft</d:keyboard-state>
+ <d:nav-method>nonav</d:nav-method>
+ <d:screen-dimension>
+ <d:size>800</d:size>
+ <d:size>1280</d:size>
+ </d:screen-dimension>
+ <d:xdpi>160</d:xdpi>
+ <d:ydpi>160</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:supported-features>
+ <d:feature>android.hardware.bluetooth</d:feature>
+ <d:feature>android.hardware.camera</d:feature>
+ <d:feature>android.hardware.camera.autofocus</d:feature>
+ <d:feature>android.hardware.camera.flash</d:feature>
+ <d:feature>android.hardware.camera.front</d:feature>
+ <d:feature>android.hardware.faketouch</d:feature>
+ <d:feature>android.hardware.location</d:feature>
+ <d:feature>android.hardware.location.gps</d:feature>
+ <d:feature>android.hardware.location.network</d:feature>
+ <d:feature>android.hardware.microphone</d:feature>
+ <d:feature>android.hardware.screen.landscape</d:feature>
+ <d:feature>android.hardware.screen.portrait</d:feature>
+ <d:feature>android.hardware.sensor.accelerometer</d:feature>
+ <d:feature>android.hardware.sensor.barometer</d:feature>
+ <d:feature>android.hardware.sensor.compass</d:feature>
+ <d:feature>android.hardware.sensor.gyroscope</d:feature>
+ <d:feature>android.hardware.sensor.light</d:feature>
+ <d:feature>android.hardware.touchscreen</d:feature>
+ <d:feature>android.hardware.touchscreen.multitouch</d:feature>
+ <d:feature>android.hardware.touchscreen.multitouch.distinct</d:feature>
+ <d:feature>android.hardware.touchscreen.multitouch.jazzhand</d:feature>
+ <d:feature>android.hardware.usb.accessory</d:feature>
+ <d:feature>android.hardware.usb.host</d:feature>
+ <d:feature>android.hardware.wifi</d:feature>
+ <d:feature>android.software.live_wallpaper</d:feature>
+ <d:feature>android.software.sip</d:feature>
+ <d:feature>android.software.sip.voip</d:feature>
+ <d:feature>com.google.android.feature.GOOGLE_BUILD</d:feature>
+ <d:feature>com.vzw.hardware.ehrpd</d:feature>
+ <d:feature>com.vzw.hardware.lte</d:feature>
+ </d:supported-features>
+
+ </d:device>
+
+</d:layout-devices> \ No newline at end of file
diff --git a/src/plugins/preflighting.core/devices/xprt-mb612.xml b/src/plugins/preflighting.core/devices/xprt-mb612.xml
new file mode 100644
index 0000000..f3d32ed
--- /dev/null
+++ b/src/plugins/preflighting.core/devices/xprt-mb612.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<d:layout-devices xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:d="http://schemas.android.com/sdk/android/layout-devices/1">
+
+ <d:device id="xprt-mb612" name="XPRT MB612" provider="Motorola Mobility, Inc.">
+
+ <!--Product specifications: http://developer.motorola.com/products/xprt-mb612/-->
+
+ <d:default>
+ <d:screen-size>normal</d:screen-size>
+ <d:screen-ratio>notlong</d:screen-ratio>
+ <d:screen-orientation>port</d:screen-orientation>
+ <d:pixel-density>mdpi</d:pixel-density>
+ <d:touch-type>finger</d:touch-type>
+ <d:text-input-method>qwerty</d:text-input-method>
+ <d:keyboard-state>keysexposed</d:keyboard-state>
+ <d:nav-method>nonav</d:nav-method>
+ <d:screen-dimension>
+ <d:size>320</d:size>
+ <d:size>480</d:size>
+ </d:screen-dimension>
+ <d:xdpi>160</d:xdpi>
+ <d:ydpi>160</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:keyboard-state>keyssoft</d:keyboard-state>
+ </d:config>
+ <d:supported-features>
+ <d:feature>android.hardware.bluetooth</d:feature>
+ <d:feature>android.hardware.camera</d:feature>
+ <d:feature>android.hardware.camera.autofocus</d:feature>
+ <d:feature>android.hardware.camera.flash</d:feature>
+ <d:feature>android.hardware.location</d:feature>
+ <d:feature>android.hardware.location.gps</d:feature>
+ <d:feature>android.hardware.location.network</d:feature>
+ <d:feature>android.hardware.microphone</d:feature>
+ <d:feature>android.hardware.sensor.accelerometer</d:feature>
+ <d:feature>android.hardware.sensor.compass</d:feature>
+ <d:feature>android.hardware.sensor.light</d:feature>
+ <d:feature>android.hardware.sensor.proximity</d:feature>
+ <d:feature>android.hardware.telephony</d:feature>
+ <d:feature>android.hardware.telephony.cdma</d:feature>
+ <d:feature>android.hardware.telephony.gsm</d:feature>
+ <d:feature>android.hardware.touchscreen</d:feature>
+ <d:feature>android.hardware.touchscreen.multitouch</d:feature>
+ <d:feature>android.hardware.touchscreen.multitouch.distinct</d:feature>
+ <d:feature>android.hardware.wifi</d:feature>
+ <d:feature>android.software.live_wallpaper</d:feature>
+ </d:supported-features>
+
+ </d:device>
+
+</d:layout-devices> \ No newline at end of file
diff --git a/src/plugins/preflighting.core/devices/xt301.xml b/src/plugins/preflighting.core/devices/xt301.xml
new file mode 100644
index 0000000..995e970
--- /dev/null
+++ b/src/plugins/preflighting.core/devices/xt301.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<d:layout-devices xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:d="http://schemas.android.com/sdk/android/layout-devices/1">
+
+ <d:device id="xt301" name="MOTO XT301" provider="Motorola Mobility, Inc.">
+
+ <!--Product specifications: http://developer.motorola.com/products/xt301/-->
+
+ <d:default>
+ <d:screen-size>small</d:screen-size>
+ <d:screen-ratio>notlong</d:screen-ratio>
+ <d:screen-orientation>port</d:screen-orientation>
+ <d:pixel-density>ldpi</d:pixel-density>
+ <d:touch-type>finger</d:touch-type>
+ <d:text-input-method>nokeys</d:text-input-method>
+ <d:keyboard-state>keyssoft</d:keyboard-state>
+ <d:nav-method>nonav</d:nav-method>
+ <d:screen-dimension>
+ <d:size>240</d:size>
+ <d:size>320</d:size>
+ </d:screen-dimension>
+ <d:xdpi>120</d:xdpi>
+ <d:ydpi>120</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:layout-devices> \ No newline at end of file
diff --git a/src/plugins/preflighting.core/devices/xt316.xml b/src/plugins/preflighting.core/devices/xt316.xml
new file mode 100644
index 0000000..3294587
--- /dev/null
+++ b/src/plugins/preflighting.core/devices/xt316.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<d:layout-devices xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:d="http://schemas.android.com/sdk/android/layout-devices/1">
+
+ <d:device id="xt316" name="MOTO XT316" provider="Motorola Mobility, Inc.">
+
+ <!--Product specifications: http://developer.motorola.com/products/xt316/-->
+
+ <d:default>
+ <d:screen-size>small</d:screen-size>
+ <d:screen-ratio>notlong</d:screen-ratio>
+ <d:screen-orientation>port</d:screen-orientation>
+ <d:pixel-density>ldpi</d:pixel-density>
+ <d:touch-type>finger</d:touch-type>
+ <d:text-input-method>qwerty</d:text-input-method>
+ <d:keyboard-state>keysexposed</d:keyboard-state>
+ <d:nav-method>nonav</d:nav-method>
+ <d:screen-dimension>
+ <d:size>240</d:size>
+ <d:size>320</d:size>
+ </d:screen-dimension>
+ <d:xdpi>120</d:xdpi>
+ <d:ydpi>120</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:keyboard-state>keyssoft</d:keyboard-state>
+ </d:config>
+
+ </d:device>
+
+</d:layout-devices> \ No newline at end of file
diff --git a/src/plugins/preflighting.core/devices/xt319.xml b/src/plugins/preflighting.core/devices/xt319.xml
new file mode 100644
index 0000000..88abeca
--- /dev/null
+++ b/src/plugins/preflighting.core/devices/xt319.xml
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<d:layout-devices xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:d="http://schemas.android.com/sdk/android/layout-devices/1">
+
+ <d:device id="xt319" name="MOTO XT319" provider="Motorola Mobility, Inc.">
+
+ <!--Product specifications: http://developer.motorola.com/products/xt319/-->
+
+ <d:default>
+ <d:screen-size>normal</d:screen-size>
+ <d:screen-ratio>notlong</d:screen-ratio>
+ <d:screen-orientation>port</d:screen-orientation>
+ <d:pixel-density>mdpi</d:pixel-density>
+ <d:touch-type>finger</d:touch-type>
+ <d:text-input-method>nokeys</d:text-input-method>
+ <d:keyboard-state>keyssoft</d:keyboard-state>
+ <d:nav-method>nonav</d:nav-method>
+ <d:screen-dimension>
+ <d:size>320</d:size>
+ <d:size>480</d:size>
+ </d:screen-dimension>
+ <d:xdpi>160</d:xdpi>
+ <d:ydpi>160</d:ydpi>
+ </d:default>
+ <d:supported-features>
+ <d:feature>android.hardware.bluetooth</d:feature>
+ <d:feature>android.hardware.camera</d:feature>
+ <d:feature>android.hardware.camera.front</d:feature>
+ <d:feature>android.hardware.location</d:feature>
+ <d:feature>android.hardware.location.gps</d:feature>
+ <d:feature>android.hardware.location.network</d:feature>
+ <d:feature>android.hardware.microphone</d:feature>
+ <d:feature>android.hardware.sensor.accelerometer</d:feature>
+ <d:feature>android.hardware.sensor.compass</d:feature>
+ <d:feature>android.hardware.sensor.light</d:feature>
+ <d:feature>android.hardware.sensor.proximity</d:feature>
+ <d:feature>android.hardware.telephony</d:feature>
+ <d:feature>android.hardware.telephony.gsm</d:feature>
+ <d:feature>android.hardware.touchscreen</d:feature>
+ <d:feature>android.hardware.touchscreen.multitouch</d:feature>
+ <d:feature>android.hardware.wifi</d:feature>
+ <d:feature>android.software.live_wallpaper</d:feature>
+ <d:feature>android.software.sip</d:feature>
+ <d:feature>android.software.sip.voip</d:feature>
+ </d:supported-features>
+
+ </d:device>
+
+</d:layout-devices> \ No newline at end of file
diff --git a/src/plugins/preflighting.core/devices/xt531.xml b/src/plugins/preflighting.core/devices/xt531.xml
new file mode 100644
index 0000000..52af44b
--- /dev/null
+++ b/src/plugins/preflighting.core/devices/xt531.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<d:layout-devices xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:d="http://schemas.android.com/sdk/android/layout-devices/1">
+
+ <d:device id="xt531" name="MOTO XT531" provider="Motorola Mobility, Inc.">
+
+ <!--Product specifications: http://developer.motorola.com/products/xt531/-->
+
+ <d:default>
+ <d:screen-size>normal</d:screen-size>
+ <d:screen-ratio>notlong</d:screen-ratio>
+ <d:screen-orientation>port</d:screen-orientation>
+ <d:pixel-density>mdpi</d:pixel-density>
+ <d:touch-type>finger</d:touch-type>
+ <d:text-input-method>nokeys</d:text-input-method>
+ <d:keyboard-state>keyssoft</d:keyboard-state>
+ <d:nav-method>nonav</d:nav-method>
+ <d:screen-dimension>
+ <d:size>320</d:size>
+ <d:size>480</d:size>
+ </d:screen-dimension>
+ <d:xdpi>160</d:xdpi>
+ <d:ydpi>160</d:ydpi>
+ </d:default>
+ <d:supported-features>
+ <d:feature>android.hardware.bluetooth</d:feature>
+ <d:feature>android.hardware.camera</d:feature>
+ <d:feature>android.hardware.camera.autofocus</d:feature>
+ <d:feature>android.hardware.camera.flash</d:feature>
+ <d:feature>android.hardware.camera.front</d:feature>
+ <d:feature>android.hardware.location</d:feature>
+ <d:feature>android.hardware.location.gps</d:feature>
+ <d:feature>android.hardware.location.network</d:feature>
+ <d:feature>android.hardware.microphone</d:feature>
+ <d:feature>android.hardware.sensor.accelerometer</d:feature>
+ <d:feature>android.hardware.sensor.compass</d:feature>
+ <d:feature>android.hardware.sensor.light</d:feature>
+ <d:feature>android.hardware.sensor.proximity</d:feature>
+ <d:feature>android.hardware.telephony</d:feature>
+ <d:feature>android.hardware.telephony.gsm</d:feature>
+ <d:feature>android.hardware.touchscreen</d:feature>
+ <d:feature>android.hardware.touchscreen.multitouch</d:feature>
+ <d:feature>android.hardware.wifi</d:feature>
+ <d:feature>android.software.live_wallpaper</d:feature>
+ <d:feature>android.software.sip</d:feature>
+ <d:feature>android.software.sip.voip</d:feature>
+ </d:supported-features>
+
+ </d:device>
+
+</d:layout-devices> \ No newline at end of file
diff --git a/src/plugins/preflighting.core/devices/xt532.xml b/src/plugins/preflighting.core/devices/xt532.xml
new file mode 100644
index 0000000..dc3bb35
--- /dev/null
+++ b/src/plugins/preflighting.core/devices/xt532.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<d:layout-devices xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:d="http://schemas.android.com/sdk/android/layout-devices/1">
+
+ <d:device id="xt532" name="MOTO XT532" provider="Motorola Mobility, Inc.">
+
+ <!--Product specifications: http://developer.motorola.com/products/xt532/-->
+
+ <d:default>
+ <d:screen-size>normal</d:screen-size>
+ <d:screen-ratio>notlong</d:screen-ratio>
+ <d:screen-orientation>port</d:screen-orientation>
+ <d:pixel-density>mdpi</d:pixel-density>
+ <d:touch-type>finger</d:touch-type>
+ <d:text-input-method>nokeys</d:text-input-method>
+ <d:keyboard-state>keyssoft</d:keyboard-state>
+ <d:nav-method>nonav</d:nav-method>
+ <d:screen-dimension>
+ <d:size>320</d:size>
+ <d:size>480</d:size>
+ </d:screen-dimension>
+ <d:xdpi>160</d:xdpi>
+ <d:ydpi>160</d:ydpi>
+ </d:default>
+ <d:supported-features>
+ <d:feature>android.hardware.bluetooth</d:feature>
+ <d:feature>android.hardware.camera</d:feature>
+ <d:feature>android.hardware.camera.autofocus</d:feature>
+ <d:feature>android.hardware.camera.flash</d:feature>
+ <d:feature>android.hardware.camera.front</d:feature>
+ <d:feature>android.hardware.location</d:feature>
+ <d:feature>android.hardware.location.gps</d:feature>
+ <d:feature>android.hardware.location.network</d:feature>
+ <d:feature>android.hardware.microphone</d:feature>
+ <d:feature>android.hardware.sensor.accelerometer</d:feature>
+ <d:feature>android.hardware.sensor.compass</d:feature>
+ <d:feature>android.hardware.sensor.light</d:feature>
+ <d:feature>android.hardware.sensor.proximity</d:feature>
+ <d:feature>android.hardware.telephony</d:feature>
+ <d:feature>android.hardware.telephony.gsm</d:feature>
+ <d:feature>android.hardware.touchscreen</d:feature>
+ <d:feature>android.hardware.touchscreen.multitouch</d:feature>
+ <d:feature>android.hardware.wifi</d:feature>
+ <d:feature>android.software.live_wallpaper</d:feature>
+ <d:feature>android.software.sip</d:feature>
+ <d:feature>android.software.sip.voip</d:feature>
+ </d:supported-features>
+
+ </d:device>
+
+</d:layout-devices> \ No newline at end of file
diff --git a/src/plugins/preflighting.core/devices/xt615.xml b/src/plugins/preflighting.core/devices/xt615.xml
new file mode 100644
index 0000000..2cb6e3c
--- /dev/null
+++ b/src/plugins/preflighting.core/devices/xt615.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<d:layout-devices xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:d="http://schemas.android.com/sdk/android/layout-devices/1">
+
+ <d:device id="xt615" name="MOTO XT615" provider="Motorola Mobility, Inc.">
+
+ <!--Product specifications: http://developer.motorola.com/products/xt615/-->
+
+ <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:text-input-method>nokeys</d:text-input-method>
+ <d:keyboard-state>keyssoft</d:keyboard-state>
+ <d:nav-method>nonav</d:nav-method>
+ <d:screen-dimension>
+ <d:size>480</d:size>
+ <d:size>854</d:size>
+ </d:screen-dimension>
+ <d:xdpi>240</d:xdpi>
+ <d:ydpi>240</d:ydpi>
+ </d:default>
+ <d:supported-features>
+ <d:feature>android.hardware.bluetooth</d:feature>
+ <d:feature>android.hardware.camera</d:feature>
+ <d:feature>android.hardware.camera.autofocus</d:feature>
+ <d:feature>android.hardware.camera.flash</d:feature>
+ <d:feature>android.hardware.camera.front</d:feature>
+ <d:feature>android.hardware.location</d:feature>
+ <d:feature>android.hardware.location.gps</d:feature>
+ <d:feature>android.hardware.location.network</d:feature>
+ <d:feature>android.hardware.microphone</d:feature>
+ <d:feature>android.hardware.sensor.accelerometer</d:feature>
+ <d:feature>android.hardware.sensor.compass</d:feature>
+ <d:feature>android.hardware.sensor.light</d:feature>
+ <d:feature>android.hardware.sensor.proximity</d:feature>
+ <d:feature>android.hardware.telephony</d:feature>
+ <d:feature>android.hardware.telephony.cdma</d:feature>
+ <d:feature>android.hardware.telephony.gsm</d:feature>
+ <d:feature>android.hardware.touchscreen</d:feature>
+ <d:feature>android.hardware.touchscreen.multitouch</d:feature>
+ <d:feature>android.hardware.wifi</d:feature>
+ <d:feature>android.software.live_wallpaper</d:feature>
+ <d:feature>android.software.sip</d:feature>
+ <d:feature>android.software.sip.voip</d:feature>
+ </d:supported-features>
+
+ </d:device>
+
+</d:layout-devices> \ No newline at end of file
diff --git a/src/plugins/preflighting.core/devices/xt701.xml b/src/plugins/preflighting.core/devices/xt701.xml
new file mode 100644
index 0000000..142ba62
--- /dev/null
+++ b/src/plugins/preflighting.core/devices/xt701.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<d:layout-devices xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:d="http://schemas.android.com/sdk/android/layout-devices/1">
+
+ <d:device id="xt701" name="MOTO XT701" provider="Motorola Mobility, Inc.">
+
+ <!--Product specifications: http://developer.motorola.com/products/xt701/-->
+
+ <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:text-input-method>nokeys</d:text-input-method>
+ <d:keyboard-state>keyssoft</d:keyboard-state>
+ <d:nav-method>nonav</d:nav-method>
+ <d:screen-dimension>
+ <d:size>480</d:size>
+ <d:size>854</d:size>
+ </d:screen-dimension>
+ <d:xdpi>240</d:xdpi>
+ <d:ydpi>240</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:layout-devices> \ No newline at end of file
diff --git a/src/plugins/preflighting.core/devices/xt702.xml b/src/plugins/preflighting.core/devices/xt702.xml
new file mode 100644
index 0000000..f5d6b6d
--- /dev/null
+++ b/src/plugins/preflighting.core/devices/xt702.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<d:layout-devices xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:d="http://schemas.android.com/sdk/android/layout-devices/1">
+
+ <d:device id="xt702" name="MOTO XT702" provider="Motorola Mobility, Inc.">
+
+ <!--Product specifications: http://developer.motorola.com/products/xt702/-->
+
+ <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:text-input-method>qwerty</d:text-input-method>
+ <d:nav-method>dpad</d:nav-method>
+ <d:screen-dimension>
+ <d:size>480</d:size>
+ <d:size>854</d:size>
+ </d:screen-dimension>
+ <d:xdpi>240</d:xdpi>
+ <d:ydpi>240</d:ydpi>
+ </d:default>
+ <d:config name="Portrait, closed">
+ <d:keyboard-state>keyssoft</d:keyboard-state>
+ <d:screen-orientation>port</d:screen-orientation>
+ </d:config>
+ <d:config name="Landscape, closed">
+ <d:keyboard-state>keyssoft</d:keyboard-state>
+ <d:screen-orientation>land</d:screen-orientation>
+ </d:config>
+ <d:config name="Landscape, open">
+ <d:keyboard-state>keysexposed</d:keyboard-state>
+ <d:screen-orientation>land</d:screen-orientation>
+ <d:nav-state>navexposed</d:nav-state>
+ </d:config>
+
+ </d:device>
+
+</d:layout-devices> \ No newline at end of file
diff --git a/src/plugins/preflighting.core/devices/xt711.xml b/src/plugins/preflighting.core/devices/xt711.xml
new file mode 100644
index 0000000..2367bc7
--- /dev/null
+++ b/src/plugins/preflighting.core/devices/xt711.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<d:layout-devices xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:d="http://schemas.android.com/sdk/android/layout-devices/1">
+
+ <d:device id="xt711" name="MOTO XT711" provider="Motorola Mobility, Inc.">
+
+ <!--Product specifications: http://developer.motorola.com/products/xt711/-->
+
+ <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:text-input-method>nokeys</d:text-input-method>
+ <d:keyboard-state>keyssoft</d:keyboard-state>
+ <d:nav-method>nonav</d:nav-method>
+ <d:screen-dimension>
+ <d:size>480</d:size>
+ <d:size>854</d:size>
+ </d:screen-dimension>
+ <d:xdpi>240</d:xdpi>
+ <d:ydpi>240</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:layout-devices> \ No newline at end of file
diff --git a/src/plugins/preflighting.core/devices/xt720.xml b/src/plugins/preflighting.core/devices/xt720.xml
new file mode 100644
index 0000000..3fd0be4
--- /dev/null
+++ b/src/plugins/preflighting.core/devices/xt720.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<d:layout-devices xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:d="http://schemas.android.com/sdk/android/layout-devices/1">
+
+ <d:device id="xt720" name="MOTOROI XT720" provider="Motorola Mobility, Inc.">
+
+ <!--Product specifications: http://developer.motorola.com/products/xt720/-->
+
+ <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:text-input-method>nokeys</d:text-input-method>
+ <d:keyboard-state>keyssoft</d:keyboard-state>
+ <d:nav-method>nonav</d:nav-method>
+ <d:screen-dimension>
+ <d:size>480</d:size>
+ <d:size>854</d:size>
+ </d:screen-dimension>
+ <d:xdpi>240</d:xdpi>
+ <d:ydpi>240</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:layout-devices> \ No newline at end of file
diff --git a/src/plugins/preflighting.core/devices/xt800.xml b/src/plugins/preflighting.core/devices/xt800.xml
new file mode 100644
index 0000000..1e7f95d
--- /dev/null
+++ b/src/plugins/preflighting.core/devices/xt800.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<d:layout-devices xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:d="http://schemas.android.com/sdk/android/layout-devices/1">
+
+ <d:device id="xt800" name="MOTO XT800" provider="Motorola Mobility, Inc.">
+
+ <!--Product specifications: http://developer.motorola.com/products/xt800/-->
+
+ <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:text-input-method>nokeys</d:text-input-method>
+ <d:keyboard-state>keyssoft</d:keyboard-state>
+ <d:nav-method>nonav</d:nav-method>
+ <d:screen-dimension>
+ <d:size>480</d:size>
+ <d:size>854</d:size>
+ </d:screen-dimension>
+ <d:xdpi>240</d:xdpi>
+ <d:ydpi>240</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:layout-devices> \ No newline at end of file
diff --git a/src/plugins/preflighting.core/devices/xt800plus.xml b/src/plugins/preflighting.core/devices/xt800plus.xml
new file mode 100644
index 0000000..c2f4d09
--- /dev/null
+++ b/src/plugins/preflighting.core/devices/xt800plus.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<d:layout-devices xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:d="http://schemas.android.com/sdk/android/layout-devices/1">
+
+ <d:device id="xt800plus" name="MOTO XT800+" provider="Motorola Mobility, Inc.">
+
+ <!--Product specifications: http://developer.motorola.com/products/xt800plus/-->
+
+ <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:text-input-method>nokeys</d:text-input-method>
+ <d:keyboard-state>keyssoft</d:keyboard-state>
+ <d:nav-method>nonav</d:nav-method>
+ <d:screen-dimension>
+ <d:size>480</d:size>
+ <d:size>854</d:size>
+ </d:screen-dimension>
+ <d:xdpi>240</d:xdpi>
+ <d:ydpi>240</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:layout-devices> \ No newline at end of file
diff --git a/src/plugins/preflighting.core/devices/xt800w.xml b/src/plugins/preflighting.core/devices/xt800w.xml
new file mode 100644
index 0000000..511c6b4
--- /dev/null
+++ b/src/plugins/preflighting.core/devices/xt800w.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<d:layout-devices xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:d="http://schemas.android.com/sdk/android/layout-devices/1">
+
+ <d:device id="xt800w" name="MOTO GLAM XT800w" provider="Motorola Mobility, Inc.">
+
+ <!--Product specifications: http://developer.motorola.com/products/xt800w/-->
+
+ <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:text-input-method>nokeys</d:text-input-method>
+ <d:keyboard-state>keyssoft</d:keyboard-state>
+ <d:nav-method>nonav</d:nav-method>
+ <d:screen-dimension>
+ <d:size>480</d:size>
+ <d:size>854</d:size>
+ </d:screen-dimension>
+ <d:xdpi>240</d:xdpi>
+ <d:ydpi>240</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:layout-devices> \ No newline at end of file
diff --git a/src/plugins/preflighting.core/devices/xt806.xml b/src/plugins/preflighting.core/devices/xt806.xml
new file mode 100644
index 0000000..2c49b0a
--- /dev/null
+++ b/src/plugins/preflighting.core/devices/xt806.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<d:layout-devices xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:d="http://schemas.android.com/sdk/android/layout-devices/1">
+
+ <d:device id="xt806" name="MOTO XT806" provider="Motorola Mobility, Inc.">
+
+ <!--Product specifications: http://developer.motorola.com/products/xt806/-->
+
+ <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>stylus</d:touch-type>
+ <d:text-input-method>nokeys</d:text-input-method>
+ <d:keyboard-state>keyssoft</d:keyboard-state>
+ <d:nav-method>nonav</d:nav-method>
+ <d:screen-dimension>
+ <d:size>480</d:size>
+ <d:size>854</d:size>
+ </d:screen-dimension>
+ <d:xdpi>240</d:xdpi>
+ <d:ydpi>240</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:layout-devices> \ No newline at end of file
diff --git a/src/plugins/preflighting.core/devices/xt860-4g.xml b/src/plugins/preflighting.core/devices/xt860-4g.xml
new file mode 100644
index 0000000..c99578e
--- /dev/null
+++ b/src/plugins/preflighting.core/devices/xt860-4g.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<d:layout-devices xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:d="http://schemas.android.com/sdk/android/layout-devices/1">
+
+ <d:device id="xt860-4g" name="XT860 4G" provider="Motorola Mobility, Inc.">
+
+ <!--Product specifications: http://developer.motorola.com/products/xt860-4g/-->
+
+ <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:text-input-method>qwerty</d:text-input-method>
+ <d:nav-method>dpad</d:nav-method>
+ <d:screen-dimension>
+ <d:size>540</d:size>
+ <d:size>960</d:size>
+ </d:screen-dimension>
+ <d:xdpi>240</d:xdpi>
+ <d:ydpi>240</d:ydpi>
+ </d:default>
+ <d:config name="Portrait, closed">
+ <d:keyboard-state>keyssoft</d:keyboard-state>
+ <d:screen-orientation>port</d:screen-orientation>
+ </d:config>
+ <d:config name="Landscape, closed">
+ <d:keyboard-state>keyssoft</d:keyboard-state>
+ <d:screen-orientation>land</d:screen-orientation>
+ </d:config>
+ <d:config name="Landscape, open">
+ <d:keyboard-state>keysexposed</d:keyboard-state>
+ <d:screen-orientation>land</d:screen-orientation>
+ <d:nav-state>navexposed</d:nav-state>
+ </d:config>
+ <d:supported-features>
+ <d:feature>android.hardware.bluetooth</d:feature>
+ <d:feature>android.hardware.camera</d:feature>
+ <d:feature>android.hardware.camera.autofocus</d:feature>
+ <d:feature>android.hardware.camera.flash</d:feature>
+ <d:feature>android.hardware.camera.front</d:feature>
+ <d:feature>android.hardware.location</d:feature>
+ <d:feature>android.hardware.location.gps</d:feature>
+ <d:feature>android.hardware.location.network</d:feature>
+ <d:feature>android.hardware.microphone</d:feature>
+ <d:feature>android.hardware.sensor.accelerometer</d:feature>
+ <d:feature>android.hardware.sensor.compass</d:feature>
+ <d:feature>android.hardware.sensor.light</d:feature>
+ <d:feature>android.hardware.sensor.proximity</d:feature>
+ <d:feature>android.hardware.telephony</d:feature>
+ <d:feature>android.hardware.telephony.gsm</d:feature>
+ <d:feature>android.hardware.touchscreen</d:feature>
+ <d:feature>android.hardware.touchscreen.multitouch</d:feature>
+ <d:feature>android.hardware.touchscreen.multitouch.distinct</d:feature>
+ <d:feature>android.hardware.wifi</d:feature>
+ <d:feature>android.software.live_wallpaper</d:feature>
+ </d:supported-features>
+
+ </d:device>
+
+</d:layout-devices> \ No newline at end of file
diff --git a/src/plugins/preflighting.core/devices/xt882.xml b/src/plugins/preflighting.core/devices/xt882.xml
new file mode 100644
index 0000000..282096b
--- /dev/null
+++ b/src/plugins/preflighting.core/devices/xt882.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<d:layout-devices xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:d="http://schemas.android.com/sdk/android/layout-devices/1">
+
+ <d:device id="xt882" name="MOTO XT882" provider="Motorola Mobility, Inc.">
+
+ <!--Product specifications: http://developer.motorola.com/products/xt882/-->
+
+ <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:text-input-method>nokeys</d:text-input-method>
+ <d:keyboard-state>keyssoft</d:keyboard-state>
+ <d:nav-method>nonav</d:nav-method>
+ <d:screen-dimension>
+ <d:size>540</d:size>
+ <d:size>960</d:size>
+ </d:screen-dimension>
+ <d:xdpi>240</d:xdpi>
+ <d:ydpi>240</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:supported-features>
+ <d:feature>android.hardware.bluetooth</d:feature>
+ <d:feature>android.hardware.camera</d:feature>
+ <d:feature>android.hardware.camera.autofocus</d:feature>
+ <d:feature>android.hardware.camera.flash</d:feature>
+ <d:feature>android.hardware.camera.front</d:feature>
+ <d:feature>android.hardware.location</d:feature>
+ <d:feature>android.hardware.location.gps</d:feature>
+ <d:feature>android.hardware.location.network</d:feature>
+ <d:feature>android.hardware.microphone</d:feature>
+ <d:feature>android.hardware.sensor.accelerometer</d:feature>
+ <d:feature>android.hardware.sensor.compass</d:feature>
+ <d:feature>android.hardware.sensor.light</d:feature>
+ <d:feature>android.hardware.sensor.proximity</d:feature>
+ <d:feature>android.hardware.telephony</d:feature>
+ <d:feature>android.hardware.telephony.cdma</d:feature>
+ <d:feature>android.hardware.telephony.gsm</d:feature>
+ <d:feature>android.hardware.touchscreen</d:feature>
+ <d:feature>android.hardware.touchscreen.multitouch</d:feature>
+ <d:feature>android.hardware.touchscreen.multitouch.distinct</d:feature>
+ <d:feature>android.hardware.wifi</d:feature>
+ <d:feature>android.software.live_wallpaper</d:feature>
+ </d:supported-features>
+
+ </d:device>
+
+</d:layout-devices> \ No newline at end of file
diff --git a/src/plugins/preflighting.core/devices/xt883.xml b/src/plugins/preflighting.core/devices/xt883.xml
new file mode 100644
index 0000000..bf6968c
--- /dev/null
+++ b/src/plugins/preflighting.core/devices/xt883.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<d:layout-devices xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:d="http://schemas.android.com/sdk/android/layout-devices/1">
+
+ <d:device id="xt883" name="MILESTONE 3 XT883" provider="Motorola Mobility, Inc.">
+
+ <!--Product specifications: http://developer.motorola.com/products/xt883/-->
+
+ <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:text-input-method>qwerty</d:text-input-method>
+ <d:nav-method>dpad</d:nav-method>
+ <d:screen-dimension>
+ <d:size>540</d:size>
+ <d:size>960</d:size>
+ </d:screen-dimension>
+ <d:xdpi>240</d:xdpi>
+ <d:ydpi>240</d:ydpi>
+ </d:default>
+ <d:config name="Portrait, closed">
+ <d:keyboard-state>keyssoft</d:keyboard-state>
+ <d:screen-orientation>port</d:screen-orientation>
+ </d:config>
+ <d:config name="Landscape, closed">
+ <d:keyboard-state>keyssoft</d:keyboard-state>
+ <d:screen-orientation>land</d:screen-orientation>
+ </d:config>
+ <d:config name="Landscape, open">
+ <d:keyboard-state>keysexposed</d:keyboard-state>
+ <d:screen-orientation>land</d:screen-orientation>
+ <d:nav-state>navexposed</d:nav-state>
+ </d:config>
+ <d:supported-features>
+ <d:feature>android.hardware.bluetooth</d:feature>
+ <d:feature>android.hardware.camera</d:feature>
+ <d:feature>android.hardware.camera.autofocus</d:feature>
+ <d:feature>android.hardware.camera.flash</d:feature>
+ <d:feature>android.hardware.camera.front</d:feature>
+ <d:feature>android.hardware.location</d:feature>
+ <d:feature>android.hardware.location.gps</d:feature>
+ <d:feature>android.hardware.location.network</d:feature>
+ <d:feature>android.hardware.microphone</d:feature>
+ <d:feature>android.hardware.sensor.accelerometer</d:feature>
+ <d:feature>android.hardware.sensor.compass</d:feature>
+ <d:feature>android.hardware.sensor.light</d:feature>
+ <d:feature>android.hardware.sensor.proximity</d:feature>
+ <d:feature>android.hardware.telephony</d:feature>
+ <d:feature>android.hardware.telephony.cdma</d:feature>
+ <d:feature>android.hardware.telephony.gsm</d:feature>
+ <d:feature>android.hardware.touchscreen</d:feature>
+ <d:feature>android.hardware.touchscreen.multitouch</d:feature>
+ <d:feature>android.hardware.touchscreen.multitouch.distinct</d:feature>
+ <d:feature>android.hardware.wifi</d:feature>
+ <d:feature>android.software.live_wallpaper</d:feature>
+ </d:supported-features>
+
+ </d:device>
+
+</d:layout-devices> \ No newline at end of file
diff --git a/src/plugins/preflighting.core/devices/xt928.xml b/src/plugins/preflighting.core/devices/xt928.xml
new file mode 100644
index 0000000..aa50afc
--- /dev/null
+++ b/src/plugins/preflighting.core/devices/xt928.xml
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<d:layout-devices xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:d="http://schemas.android.com/sdk/android/layout-devices/1">
+
+ <d:device id="xt928" name="MOTO XT928" provider="Motorola Mobility, Inc.">
+
+ <!--Product specifications: http://developer.motorola.com/products/xt928/-->
+
+ <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:text-input-method>nokeys</d:text-input-method>
+ <d:keyboard-state>keyssoft</d:keyboard-state>
+ <d:nav-method>nonav</d:nav-method>
+ <d:screen-dimension>
+ <d:size>1280</d:size>
+ <d:size>720</d:size>
+ </d:screen-dimension>
+ <d:xdpi>240</d:xdpi>
+ <d:ydpi>240</d:ydpi>
+ </d:default>
+ <d:supported-features>
+ <d:feature>android.hardware.bluetooth</d:feature>
+ <d:feature>android.hardware.camera</d:feature>
+ <d:feature>android.hardware.camera.autofocus</d:feature>
+ <d:feature>android.hardware.camera.flash</d:feature>
+ <d:feature>android.hardware.camera.front</d:feature>
+ <d:feature>android.hardware.location</d:feature>
+ <d:feature>android.hardware.location.gps</d:feature>
+ <d:feature>android.hardware.location.network</d:feature>
+ <d:feature>android.hardware.microphone</d:feature>
+ <d:feature>android.hardware.sensor.accelerometer</d:feature>
+ <d:feature>android.hardware.sensor.compass</d:feature>
+ <d:feature>android.hardware.sensor.gyroscope</d:feature>
+ <d:feature>android.hardware.sensor.light</d:feature>
+ <d:feature>android.hardware.sensor.proximity</d:feature>
+ <d:feature>android.hardware.telephony</d:feature>
+ <d:feature>android.hardware.telephony.cdma</d:feature>
+ <d:feature>android.hardware.touchscreen</d:feature>
+ <d:feature>android.hardware.touchscreen.multitouch</d:feature>
+ <d:feature>android.hardware.touchscreen.multitouch.distinct</d:feature>
+ <d:feature>android.hardware.wifi</d:feature>
+ <d:feature>android.software.live_wallpaper</d:feature>
+ <d:feature>android.software.sip</d:feature>
+ <d:feature>android.software.sip.voip</d:feature>
+ <d:feature>com.motorola.vpn.cisco</d:feature>
+ <d:feature>com.motorola.vpn.juniper</d:feature>
+ </d:supported-features>
+
+ </d:device>
+
+</d:layout-devices> \ No newline at end of file
diff --git a/src/plugins/preflighting.core/devices/xyboard-10-1-mz615.xml b/src/plugins/preflighting.core/devices/xyboard-10-1-mz615.xml
new file mode 100644
index 0000000..1b91f09
--- /dev/null
+++ b/src/plugins/preflighting.core/devices/xyboard-10-1-mz615.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<d:layout-devices xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:d="http://schemas.android.com/sdk/android/layout-devices/1">
+
+ <d:device id="xyboard-10-1-mz615" name="XYBOARD 10.1 with WiFi MZ615" provider="Motorola Mobility, Inc.">
+
+ <!--Product specifications: http://developer.motorola.com/products/xyboard-10-1-mz615/-->
+
+ <d:default>
+ <d:screen-size>xlarge</d:screen-size>
+ <d:screen-ratio>long</d:screen-ratio>
+ <d:screen-orientation>land</d:screen-orientation>
+ <d:pixel-density>mdpi</d:pixel-density>
+ <d:touch-type>finger</d:touch-type>
+ <d:text-input-method>nokeys</d:text-input-method>
+ <d:keyboard-state>keyssoft</d:keyboard-state>
+ <d:nav-method>nonav</d:nav-method>
+ <d:screen-dimension>
+ <d:size>800</d:size>
+ <d:size>1280</d:size>
+ </d:screen-dimension>
+ <d:xdpi>160</d:xdpi>
+ <d:ydpi>160</d:ydpi>
+ </d:default>
+ <d:supported-features>
+ <d:feature>android.hardware.bluetooth</d:feature>
+ <d:feature>android.hardware.camera</d:feature>
+ <d:feature>android.hardware.camera.autofocus</d:feature>
+ <d:feature>android.hardware.camera.flash</d:feature>
+ <d:feature>android.hardware.camera.front</d:feature>
+ <d:feature>android.hardware.faketouch</d:feature>
+ <d:feature>android.hardware.location</d:feature>
+ <d:feature>android.hardware.location.gps</d:feature>
+ <d:feature>android.hardware.location.network</d:feature>
+ <d:feature>android.hardware.microphone</d:feature>
+ <d:feature>android.hardware.screen.landscape</d:feature>
+ <d:feature>android.hardware.screen.portrait</d:feature>
+ <d:feature>android.hardware.sensor.accelerometer</d:feature>
+ <d:feature>android.hardware.sensor.barometer</d:feature>
+ <d:feature>android.hardware.sensor.compass</d:feature>
+ <d:feature>android.hardware.sensor.gyroscope</d:feature>
+ <d:feature>android.hardware.sensor.light</d:feature>
+ <d:feature>android.hardware.touchscreen</d:feature>
+ <d:feature>android.hardware.touchscreen.multitouch</d:feature>
+ <d:feature>android.hardware.touchscreen.multitouch.distinct</d:feature>
+ <d:feature>android.hardware.touchscreen.multitouch.jazzhand</d:feature>
+ <d:feature>android.hardware.usb.accessory</d:feature>
+ <d:feature>android.hardware.usb.host</d:feature>
+ <d:feature>android.hardware.wifi</d:feature>
+ <d:feature>android.software.live_wallpaper</d:feature>
+ <d:feature>android.software.sip</d:feature>
+ <d:feature>android.software.sip.voip</d:feature>
+ </d:supported-features>
+
+ </d:device>
+
+</d:layout-devices> \ No newline at end of file
diff --git a/src/plugins/preflighting.core/devices/xyboard-8-2-mz607.xml b/src/plugins/preflighting.core/devices/xyboard-8-2-mz607.xml
new file mode 100644
index 0000000..9194b49
--- /dev/null
+++ b/src/plugins/preflighting.core/devices/xyboard-8-2-mz607.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<d:layout-devices xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:d="http://schemas.android.com/sdk/android/layout-devices/1">
+
+ <d:device id="xyboard-8-2-mz607" name="XYBOARD 8.2 with WiFi MZ607" provider="Motorola Mobility, Inc.">
+
+ <!--Product specifications: http://developer.motorola.com/products/xyboard-8-2-mz607/-->
+
+ <d:default>
+ <d:screen-size>xlarge</d:screen-size>
+ <d:screen-ratio>long</d:screen-ratio>
+ <d:screen-orientation>port</d:screen-orientation>
+ <d:pixel-density>mdpi</d:pixel-density>
+ <d:touch-type>finger</d:touch-type>
+ <d:text-input-method>nokeys</d:text-input-method>
+ <d:keyboard-state>keyssoft</d:keyboard-state>
+ <d:nav-method>nonav</d:nav-method>
+ <d:screen-dimension>
+ <d:size>1280</d:size>
+ <d:size>800</d:size>
+ </d:screen-dimension>
+ <d:xdpi>160</d:xdpi>
+ <d:ydpi>160</d:ydpi>
+ </d:default>
+ <d:supported-features>
+ <d:feature>android.hardware.bluetooth</d:feature>
+ <d:feature>android.hardware.camera</d:feature>
+ <d:feature>android.hardware.camera.autofocus</d:feature>
+ <d:feature>android.hardware.camera.flash</d:feature>
+ <d:feature>android.hardware.camera.front</d:feature>
+ <d:feature>android.hardware.faketouch</d:feature>
+ <d:feature>android.hardware.location</d:feature>
+ <d:feature>android.hardware.location.gps</d:feature>
+ <d:feature>android.hardware.location.network</d:feature>
+ <d:feature>android.hardware.microphone</d:feature>
+ <d:feature>android.hardware.screen.landscape</d:feature>
+ <d:feature>android.hardware.screen.portrait</d:feature>
+ <d:feature>android.hardware.sensor.accelerometer</d:feature>
+ <d:feature>android.hardware.sensor.barometer</d:feature>
+ <d:feature>android.hardware.sensor.compass</d:feature>
+ <d:feature>android.hardware.sensor.gyroscope</d:feature>
+ <d:feature>android.hardware.sensor.light</d:feature>
+ <d:feature>android.hardware.touchscreen</d:feature>
+ <d:feature>android.hardware.touchscreen.multitouch</d:feature>
+ <d:feature>android.hardware.touchscreen.multitouch.distinct</d:feature>
+ <d:feature>android.hardware.touchscreen.multitouch.jazzhand</d:feature>
+ <d:feature>android.hardware.usb.accessory</d:feature>
+ <d:feature>android.hardware.usb.host</d:feature>
+ <d:feature>android.hardware.wifi</d:feature>
+ <d:feature>android.software.live_wallpaper</d:feature>
+ <d:feature>android.software.sip</d:feature>
+ <d:feature>android.software.sip.voip</d:feature>
+ </d:supported-features>
+
+ </d:device>
+
+</d:layout-devices> \ No newline at end of file
diff --git a/src/plugins/preflighting.core/etc/log4j.properties b/src/plugins/preflighting.core/etc/log4j.properties
new file mode 100644
index 0000000..28edd58
--- /dev/null
+++ b/src/plugins/preflighting.core/etc/log4j.properties
@@ -0,0 +1,11 @@
+# SETS THE PLATFORM DEFAULT LOGGER CONFIGURATION
+log4j.rootLogger=ALL, default
+# LOGGING ON A FILE AT log4j.appender.default.File
+log4j.appender.default=org.apache.log4j.RollingFileAppender
+# Log will be placed at user home
+log4j.appender.default.File=${user.home}/appValidator.log
+# MAXIMUM SIZE OF THE LOG FILE
+log4j.appender.default.MaxFileSize=2048KB
+# LAYOUT BEING USED BY THE LOGGER
+log4j.appender.default.layout=org.apache.log4j.PatternLayout
+log4j.appender.default.layout.ConversionPattern=%d [%t] %-5p %c - %m%n
diff --git a/src/plugins/preflighting.core/files/apilevel-permission-map.xml b/src/plugins/preflighting.core/files/apilevel-permission-map.xml
new file mode 100644
index 0000000..ad77f5b
--- /dev/null
+++ b/src/plugins/preflighting.core/files/apilevel-permission-map.xml
@@ -0,0 +1,4228 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<api_permissions>
+
+ <!-- API level 1, Android platform 1.0 -->
+ <api_level number="1">
+ <permission>
+ <name>ACCESS_CHECKIN_PROPERTIES</name>
+ <description>Allows read/write access to the "properties" table in the checkin database, to change values that get uploaded.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>ACCESS_COARSE_LOCATION</name>
+ <description>Allows an application to access coarse (e.g., Cell-ID, WiFi) location</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>ACCESS_FINE_LOCATION</name>
+ <description>Allows an application to access fine (e.g., GPS) location</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>ACCESS_LOCATION_EXTRA_COMMANDS</name>
+ <description>Allows an application to access extra location provider commands</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>ACCESS_MOCK_LOCATION</name>
+ <description>Allows an application to create mock location providers for testing</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>ACCESS_NETWORK_STATE</name>
+ <description>Allows applications to access information about networks</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>ACCESS_SURFACE_FLINGER</name>
+ <description>Allows an application to use SurfaceFlinger's low level features</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>ACCESS_WIFI_STATE</name>
+ <description>Allows applications to access information about Wi-Fi networks</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>BATTERY_STATS</name>
+ <description>Allows an application to collect battery statistics</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>BLUETOOTH</name>
+ <description>Allows applications to connect to paired bluetooth devices</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>BLUETOOTH_ADMIN</name>
+ <description>Allows applications to discover and pair bluetooth devices</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>BRICK</name>
+ <description>Required to be able to disable the device (very dangerous!).</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>BROADCAST_PACKAGE_REMOVED</name>
+ <description>Allows an application to broadcast a notification that an application package has been removed.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>BROADCAST_STICKY</name>
+ <description>Allows an application to broadcast sticky intents.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>CALL_PHONE</name>
+ <description>Allows an application to initiate a phone call without going through the Dialer user interface for the user to confirm the call being placed.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>CALL_PRIVILEGED</name>
+ <description>Allows an application to call any phone number, including emergency numbers, without going through the Dialer user interface for the user to confirm the call being placed.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>CAMERA</name>
+ <description>Required to be able to access the camera device.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>CHANGE_COMPONENT_ENABLED_STATE</name>
+ <description>Allows an application to change whether an application component (other than its own) is enabled or not.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>CHANGE_CONFIGURATION</name>
+ <description>Allows an application to modify the current configuration, such as locale.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>CHANGE_NETWORK_STATE</name>
+ <description>Allows applications to change network connectivity state</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>CHANGE_WIFI_STATE</name>
+ <description>Allows applications to change Wi-Fi connectivity state</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>CLEAR_APP_CACHE</name>
+ <description>Allows an application to clear the caches of all installed applications on the device.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>CLEAR_APP_USER_DATA</name>
+ <description>Allows an application to clear user data</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>CONTROL_LOCATION_UPDATES</name>
+ <description>Allows enabling/disabling location update notifications from the radio.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>DELETE_CACHE_FILES</name>
+ <description>Allows an application to delete cache files.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>DELETE_PACKAGES</name>
+ <description>Allows an application to delete packages.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>DEVICE_POWER</name>
+ <description>Allows low-level access to power management</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>DIAGNOSTIC</name>
+ <description>Allows applications to RW to diagnostic resources.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>DISABLE_KEYGUARD</name>
+ <description>Allows applications to disable the keyguard</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>DUMP</name>
+ <description>Allows an application to retrieve state dump information from system services.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>EXPAND_STATUS_BAR</name>
+ <description>Allows an application to expand or collapse the status bar.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>FACTORY_TEST</name>
+ <description>Run as a manufacturer test application, running as the root user.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>FLASHLIGHT</name>
+ <description>Allows access to the flashlight</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>FORCE_BACK</name>
+ <description>Allows an application to force a BACK operation on whatever is the top activity.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>GET_ACCOUNTS</name>
+ <description>Allows access to the list of accounts in the Accounts Service</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>GET_PACKAGE_SIZE</name>
+ <description>Allows an application to find out the space used by any package.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>GET_TASKS</name>
+ <description>Allows an application to get information about the currently or recently running tasks: a thumbnail representation of the tasks, what activities are running in it, etc.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>HARDWARE_TEST</name>
+ <description>Allows access to hardware peripherals.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>INJECT_EVENTS</name>
+ <description>Allows an application to inject user events (keys, touch, trackball) into the event stream and deliver them to ANY window.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>INSTALL_PACKAGES</name>
+ <description>Allows an application to install packages.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>INTERNAL_SYSTEM_WINDOW</name>
+ <description>Allows an application to open windows that are for use by parts of the system user interface.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>INTERNET</name>
+ <description>Allows applications to open network sockets.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>MANAGE_APP_TOKENS</name>
+ <description>Allows an application to manage (create, destroy, Z-order) application tokens in the window manager.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>MASTER_CLEAR</name>
+ <description></description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>MODIFY_AUDIO_SETTINGS</name>
+ <description>Allows an application to modify global audio settings</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>MODIFY_PHONE_STATE</name>
+ <description>Allows modification of the telephony state - power on, mmi, etc.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>MOUNT_UNMOUNT_FILESYSTEMS</name>
+ <description>Allows mounting and unmounting file systems for removable storage.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>PERSISTENT_ACTIVITY</name>
+ <description>Allow an application to make its activities persistent.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>PROCESS_OUTGOING_CALLS</name>
+ <description>Allows an application to monitor, modify, or abort outgoing calls.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>READ_CALENDAR</name>
+ <description>Allows an application to read the user's calendar data.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>READ_CONTACTS</name>
+ <description>Allows an application to read the user's contacts data.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>READ_FRAME_BUFFER</name>
+ <description>Allows an application to take screen shots and more generally get access to the frame buffer data</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>READ_INPUT_STATE</name>
+ <description>Allows an application to retrieve the current state of keys and switches.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>READ_LOGS</name>
+ <description>Allows an application to read the low-level system log files.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>READ_OWNER_DATA</name>
+ <description>Allows an application to read the owner's data.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>READ_PHONE_STATE</name>
+ <description>Allows read only access to phone state.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>READ_SMS</name>
+ <description>Allows an application to read SMS messages.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>READ_SYNC_SETTINGS</name>
+ <description>Allows applications to read the sync settings</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>READ_SYNC_STATS</name>
+ <description>Allows applications to read the sync stats</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>REBOOT</name>
+ <description>Required to be able to reboot the device.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>RECEIVE_BOOT_COMPLETED</name>
+ <description>Allows an application to receive the ACTION_BOOT_COMPLETED that is broadcast after the system finishes booting.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>RECEIVE_MMS</name>
+ <description>Allows an application to monitor incoming MMS messages, to record or perform processing on them.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>RECEIVE_SMS</name>
+ <description>Allows an application to monitor incoming SMS messages, to record or perform processing on them.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>RECEIVE_WAP_PUSH</name>
+ <description>Allows an application to monitor incoming WAP push messages.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>RECORD_AUDIO</name>
+ <description>Allows an application to record audio</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>REORDER_TASKS</name>
+ <description>Allows an application to change the Z-order of tasks</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>RESTART_PACKAGES</name>
+ <description> This constant is deprecated. The restartPackage(String) API is no longer supported.</description>
+ <deprecated>true</deprecated>
+ </permission>
+ <permission>
+ <name>SEND_SMS</name>
+ <description>Allows an application to send SMS messages.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>SET_ACTIVITY_WATCHER</name>
+ <description>Allows an application to watch and control how activities are started globally in the system.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>SET_ALWAYS_FINISH</name>
+ <description>Allows an application to control whether activities are immediately finished when put in the background.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>SET_ANIMATION_SCALE</name>
+ <description>Modify the global animation scaling factor.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>SET_DEBUG_APP</name>
+ <description>Configure an application for debugging.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>SET_ORIENTATION</name>
+ <description>Allows low-level access to setting the orientation (actually rotation) of the screen.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>SET_PREFERRED_APPLICATIONS</name>
+ <description> This constant is deprecated. No longer useful, see addPackageToPreferred(String) for details.</description>
+ <deprecated>true</deprecated>
+ </permission>
+ <permission>
+ <name>SET_PROCESS_LIMIT</name>
+ <description>Allows an application to set the maximum number of (not needed) application processes that can be running.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>SET_TIME_ZONE</name>
+ <description>Allows applications to set the system time zone</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>SET_WALLPAPER</name>
+ <description>Allows applications to set the wallpaper</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>SET_WALLPAPER_HINTS</name>
+ <description>Allows applications to set the wallpaper hints</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>SIGNAL_PERSISTENT_PROCESSES</name>
+ <description>Allow an application to request that a signal be sent to all persistent processes</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>STATUS_BAR</name>
+ <description>Allows an application to open, close, or disable the status bar and its icons.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>SUBSCRIBED_FEEDS_READ</name>
+ <description>Allows an application to allow access the subscribed feeds ContentProvider.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>SUBSCRIBED_FEEDS_WRITE</name>
+ <description></description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>SYSTEM_ALERT_WINDOW</name>
+ <description>Allows an application to open windows using the type TYPE_SYSTEM_ALERT, shown on top of all other applications.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>VIBRATE</name>
+ <description>Allows access to the vibrator</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>WAKE_LOCK</name>
+ <description>Allows using PowerManager WakeLocks to keep processor from sleeping or screen from dimming</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>WRITE_APN_SETTINGS</name>
+ <description>Allows applications to write the apn settings</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>WRITE_CALENDAR</name>
+ <description>Allows an application to write (but not read) the user's calendar data.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>WRITE_CONTACTS</name>
+ <description>Allows an application to write (but not read) the user's contacts data.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>WRITE_GSERVICES</name>
+ <description>Allows an application to modify the Google service map.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>WRITE_OWNER_DATA</name>
+ <description>Allows an application to write (but not read) the owner's data.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>WRITE_SETTINGS</name>
+ <description>Allows an application to read or write the system settings.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>WRITE_SMS</name>
+ <description>Allows an application to write SMS messages.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>WRITE_SYNC_SETTINGS</name>
+ <description>Allows applications to write the sync settings</description>
+ <deprecated>false</deprecated>
+ </permission>
+ </api_level>
+
+ <!-- API level 2, Android platform 1.1 (Petit Four) -->
+ <api_level number="2">
+ <permission>
+ <name>ACCESS_CHECKIN_PROPERTIES</name>
+ <description>Allows read/write access to the "properties" table in the checkin database, to change values that get uploaded.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>ACCESS_COARSE_LOCATION</name>
+ <description>Allows an application to access coarse (e.g., Cell-ID, WiFi) location</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>ACCESS_FINE_LOCATION</name>
+ <description>Allows an application to access fine (e.g., GPS) location</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>ACCESS_LOCATION_EXTRA_COMMANDS</name>
+ <description>Allows an application to access extra location provider commands</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>ACCESS_MOCK_LOCATION</name>
+ <description>Allows an application to create mock location providers for testing</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>ACCESS_NETWORK_STATE</name>
+ <description>Allows applications to access information about networks</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>ACCESS_SURFACE_FLINGER</name>
+ <description>Allows an application to use SurfaceFlinger's low level features</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>ACCESS_WIFI_STATE</name>
+ <description>Allows applications to access information about Wi-Fi networks</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>BATTERY_STATS</name>
+ <description>Allows an application to collect battery statistics</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>BLUETOOTH</name>
+ <description>Allows applications to connect to paired bluetooth devices</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>BLUETOOTH_ADMIN</name>
+ <description>Allows applications to discover and pair bluetooth devices</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>BRICK</name>
+ <description>Required to be able to disable the device (very dangerous!).</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>BROADCAST_PACKAGE_REMOVED</name>
+ <description>Allows an application to broadcast a notification that an application package has been removed.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>BROADCAST_SMS</name>
+ <description>Allows an application to broadcast an SMS receipt notification</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>BROADCAST_STICKY</name>
+ <description>Allows an application to broadcast sticky intents.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>BROADCAST_WAP_PUSH</name>
+ <description>Allows an application to broadcast a WAP PUSH receipt notification</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>CALL_PHONE</name>
+ <description>Allows an application to initiate a phone call without going through the Dialer user interface for the user to confirm the call being placed.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>CALL_PRIVILEGED</name>
+ <description>Allows an application to call any phone number, including emergency numbers, without going through the Dialer user interface for the user to confirm the call being placed.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>CAMERA</name>
+ <description>Required to be able to access the camera device.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>CHANGE_COMPONENT_ENABLED_STATE</name>
+ <description>Allows an application to change whether an application component (other than its own) is enabled or not.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>CHANGE_CONFIGURATION</name>
+ <description>Allows an application to modify the current configuration, such as locale.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>CHANGE_NETWORK_STATE</name>
+ <description>Allows applications to change network connectivity state</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>CHANGE_WIFI_STATE</name>
+ <description>Allows applications to change Wi-Fi connectivity state</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>CLEAR_APP_CACHE</name>
+ <description>Allows an application to clear the caches of all installed applications on the device.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>CLEAR_APP_USER_DATA</name>
+ <description>Allows an application to clear user data</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>CONTROL_LOCATION_UPDATES</name>
+ <description>Allows enabling/disabling location update notifications from the radio.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>DELETE_CACHE_FILES</name>
+ <description>Allows an application to delete cache files.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>DELETE_PACKAGES</name>
+ <description>Allows an application to delete packages.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>DEVICE_POWER</name>
+ <description>Allows low-level access to power management</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>DIAGNOSTIC</name>
+ <description>Allows applications to RW to diagnostic resources.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>DISABLE_KEYGUARD</name>
+ <description>Allows applications to disable the keyguard</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>DUMP</name>
+ <description>Allows an application to retrieve state dump information from system services.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>EXPAND_STATUS_BAR</name>
+ <description>Allows an application to expand or collapse the status bar.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>FACTORY_TEST</name>
+ <description>Run as a manufacturer test application, running as the root user.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>FLASHLIGHT</name>
+ <description>Allows access to the flashlight</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>FORCE_BACK</name>
+ <description>Allows an application to force a BACK operation on whatever is the top activity.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>GET_ACCOUNTS</name>
+ <description>Allows access to the list of accounts in the Accounts Service</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>GET_PACKAGE_SIZE</name>
+ <description>Allows an application to find out the space used by any package.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>GET_TASKS</name>
+ <description>Allows an application to get information about the currently or recently running tasks: a thumbnail representation of the tasks, what activities are running in it, etc.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>HARDWARE_TEST</name>
+ <description>Allows access to hardware peripherals.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>INJECT_EVENTS</name>
+ <description>Allows an application to inject user events (keys, touch, trackball) into the event stream and deliver them to ANY window.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>INSTALL_PACKAGES</name>
+ <description>Allows an application to install packages.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>INTERNAL_SYSTEM_WINDOW</name>
+ <description>Allows an application to open windows that are for use by parts of the system user interface.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>INTERNET</name>
+ <description>Allows applications to open network sockets.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>MANAGE_APP_TOKENS</name>
+ <description>Allows an application to manage (create, destroy, Z-order) application tokens in the window manager.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>MASTER_CLEAR</name>
+ <description></description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>MODIFY_AUDIO_SETTINGS</name>
+ <description>Allows an application to modify global audio settings</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>MODIFY_PHONE_STATE</name>
+ <description>Allows modification of the telephony state - power on, mmi, etc.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>MOUNT_UNMOUNT_FILESYSTEMS</name>
+ <description>Allows mounting and unmounting file systems for removable storage.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>PERSISTENT_ACTIVITY</name>
+ <description>Allow an application to make its activities persistent.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>PROCESS_OUTGOING_CALLS</name>
+ <description>Allows an application to monitor, modify, or abort outgoing calls.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>READ_CALENDAR</name>
+ <description>Allows an application to read the user's calendar data.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>READ_CONTACTS</name>
+ <description>Allows an application to read the user's contacts data.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>READ_FRAME_BUFFER</name>
+ <description>Allows an application to take screen shots and more generally get access to the frame buffer data</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>READ_INPUT_STATE</name>
+ <description>Allows an application to retrieve the current state of keys and switches.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>READ_LOGS</name>
+ <description>Allows an application to read the low-level system log files.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>READ_OWNER_DATA</name>
+ <description>Allows an application to read the owner's data.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>READ_PHONE_STATE</name>
+ <description>Allows read only access to phone state.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>READ_SMS</name>
+ <description>Allows an application to read SMS messages.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>READ_SYNC_SETTINGS</name>
+ <description>Allows applications to read the sync settings</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>READ_SYNC_STATS</name>
+ <description>Allows applications to read the sync stats</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>REBOOT</name>
+ <description>Required to be able to reboot the device.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>RECEIVE_BOOT_COMPLETED</name>
+ <description>Allows an application to receive the ACTION_BOOT_COMPLETED that is broadcast after the system finishes booting.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>RECEIVE_MMS</name>
+ <description>Allows an application to monitor incoming MMS messages, to record or perform processing on them.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>RECEIVE_SMS</name>
+ <description>Allows an application to monitor incoming SMS messages, to record or perform processing on them.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>RECEIVE_WAP_PUSH</name>
+ <description>Allows an application to monitor incoming WAP push messages.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>RECORD_AUDIO</name>
+ <description>Allows an application to record audio</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>REORDER_TASKS</name>
+ <description>Allows an application to change the Z-order of tasks</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>RESTART_PACKAGES</name>
+ <description> This constant is deprecated. The restartPackage(String) API is no longer supported.</description>
+ <deprecated>true</deprecated>
+ </permission>
+ <permission>
+ <name>SEND_SMS</name>
+ <description>Allows an application to send SMS messages.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>SET_ACTIVITY_WATCHER</name>
+ <description>Allows an application to watch and control how activities are started globally in the system.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>SET_ALWAYS_FINISH</name>
+ <description>Allows an application to control whether activities are immediately finished when put in the background.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>SET_ANIMATION_SCALE</name>
+ <description>Modify the global animation scaling factor.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>SET_DEBUG_APP</name>
+ <description>Configure an application for debugging.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>SET_ORIENTATION</name>
+ <description>Allows low-level access to setting the orientation (actually rotation) of the screen.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>SET_PREFERRED_APPLICATIONS</name>
+ <description> This constant is deprecated. No longer useful, see addPackageToPreferred(String) for details.</description>
+ <deprecated>true</deprecated>
+ </permission>
+ <permission>
+ <name>SET_PROCESS_LIMIT</name>
+ <description>Allows an application to set the maximum number of (not needed) application processes that can be running.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>SET_TIME_ZONE</name>
+ <description>Allows applications to set the system time zone</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>SET_WALLPAPER</name>
+ <description>Allows applications to set the wallpaper</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>SET_WALLPAPER_HINTS</name>
+ <description>Allows applications to set the wallpaper hints</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>SIGNAL_PERSISTENT_PROCESSES</name>
+ <description>Allow an application to request that a signal be sent to all persistent processes</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>STATUS_BAR</name>
+ <description>Allows an application to open, close, or disable the status bar and its icons.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>SUBSCRIBED_FEEDS_READ</name>
+ <description>Allows an application to allow access the subscribed feeds ContentProvider.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>SUBSCRIBED_FEEDS_WRITE</name>
+ <description></description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>SYSTEM_ALERT_WINDOW</name>
+ <description>Allows an application to open windows using the type TYPE_SYSTEM_ALERT, shown on top of all other applications.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>VIBRATE</name>
+ <description>Allows access to the vibrator</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>WAKE_LOCK</name>
+ <description>Allows using PowerManager WakeLocks to keep processor from sleeping or screen from dimming</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>WRITE_APN_SETTINGS</name>
+ <description>Allows applications to write the apn settings</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>WRITE_CALENDAR</name>
+ <description>Allows an application to write (but not read) the user's calendar data.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>WRITE_CONTACTS</name>
+ <description>Allows an application to write (but not read) the user's contacts data.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>WRITE_GSERVICES</name>
+ <description>Allows an application to modify the Google service map.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>WRITE_OWNER_DATA</name>
+ <description>Allows an application to write (but not read) the owner's data.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>WRITE_SETTINGS</name>
+ <description>Allows an application to read or write the system settings.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>WRITE_SMS</name>
+ <description>Allows an application to write SMS messages.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>WRITE_SYNC_SETTINGS</name>
+ <description>Allows applications to write the sync settings</description>
+ <deprecated>false</deprecated>
+ </permission>
+ </api_level>
+
+ <!-- API level 3, Android platform 1.5 (Cupcake) - Based on Linux Kernel 2.6.27 -->
+ <api_level number="3">
+ <permission>
+ <name>ACCESS_CHECKIN_PROPERTIES</name>
+ <description>Allows read/write access to the "properties" table in the checkin database, to change values that get uploaded.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>ACCESS_COARSE_LOCATION</name>
+ <description>Allows an application to access coarse (e.g., Cell-ID, WiFi) location</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>ACCESS_FINE_LOCATION</name>
+ <description>Allows an application to access fine (e.g., GPS) location</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>ACCESS_LOCATION_EXTRA_COMMANDS</name>
+ <description>Allows an application to access extra location provider commands</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>ACCESS_MOCK_LOCATION</name>
+ <description>Allows an application to create mock location providers for testing</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>ACCESS_NETWORK_STATE</name>
+ <description>Allows applications to access information about networks</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>ACCESS_SURFACE_FLINGER</name>
+ <description>Allows an application to use SurfaceFlinger's low level features</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>ACCESS_WIFI_STATE</name>
+ <description>Allows applications to access information about Wi-Fi networks</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>BATTERY_STATS</name>
+ <description>Allows an application to collect battery statistics</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>BIND_APPWIDGET</name>
+ <description>Allows an application to tell the AppWidget service which application can access AppWidget's data.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>BIND_INPUT_METHOD</name>
+ <description>Must be required by an InputMethodService, to ensure that only the system can bind to it.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>BLUETOOTH</name>
+ <description>Allows applications to connect to paired bluetooth devices</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>BLUETOOTH_ADMIN</name>
+ <description>Allows applications to discover and pair bluetooth devices</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>BRICK</name>
+ <description>Required to be able to disable the device (very dangerous!).</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>BROADCAST_PACKAGE_REMOVED</name>
+ <description>Allows an application to broadcast a notification that an application package has been removed.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>BROADCAST_SMS</name>
+ <description>Allows an application to broadcast an SMS receipt notification</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>BROADCAST_STICKY</name>
+ <description>Allows an application to broadcast sticky intents.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>BROADCAST_WAP_PUSH</name>
+ <description>Allows an application to broadcast a WAP PUSH receipt notification</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>CALL_PHONE</name>
+ <description>Allows an application to initiate a phone call without going through the Dialer user interface for the user to confirm the call being placed.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>CALL_PRIVILEGED</name>
+ <description>Allows an application to call any phone number, including emergency numbers, without going through the Dialer user interface for the user to confirm the call being placed.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>CAMERA</name>
+ <description>Required to be able to access the camera device.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>CHANGE_COMPONENT_ENABLED_STATE</name>
+ <description>Allows an application to change whether an application component (other than its own) is enabled or not.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>CHANGE_CONFIGURATION</name>
+ <description>Allows an application to modify the current configuration, such as locale.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>CHANGE_NETWORK_STATE</name>
+ <description>Allows applications to change network connectivity state</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>CHANGE_WIFI_STATE</name>
+ <description>Allows applications to change Wi-Fi connectivity state</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>CLEAR_APP_CACHE</name>
+ <description>Allows an application to clear the caches of all installed applications on the device.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>CLEAR_APP_USER_DATA</name>
+ <description>Allows an application to clear user data</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>CONTROL_LOCATION_UPDATES</name>
+ <description>Allows enabling/disabling location update notifications from the radio.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>DELETE_CACHE_FILES</name>
+ <description>Allows an application to delete cache files.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>DELETE_PACKAGES</name>
+ <description>Allows an application to delete packages.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>DEVICE_POWER</name>
+ <description>Allows low-level access to power management</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>DIAGNOSTIC</name>
+ <description>Allows applications to RW to diagnostic resources.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>DISABLE_KEYGUARD</name>
+ <description>Allows applications to disable the keyguard</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>DUMP</name>
+ <description>Allows an application to retrieve state dump information from system services.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>EXPAND_STATUS_BAR</name>
+ <description>Allows an application to expand or collapse the status bar.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>FACTORY_TEST</name>
+ <description>Run as a manufacturer test application, running as the root user.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>FLASHLIGHT</name>
+ <description>Allows access to the flashlight</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>FORCE_BACK</name>
+ <description>Allows an application to force a BACK operation on whatever is the top activity.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>GET_ACCOUNTS</name>
+ <description>Allows access to the list of accounts in the Accounts Service</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>GET_PACKAGE_SIZE</name>
+ <description>Allows an application to find out the space used by any package.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>GET_TASKS</name>
+ <description>Allows an application to get information about the currently or recently running tasks: a thumbnail representation of the tasks, what activities are running in it, etc.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>HARDWARE_TEST</name>
+ <description>Allows access to hardware peripherals.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>INJECT_EVENTS</name>
+ <description>Allows an application to inject user events (keys, touch, trackball) into the event stream and deliver them to ANY window.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>INSTALL_PACKAGES</name>
+ <description>Allows an application to install packages.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>INTERNAL_SYSTEM_WINDOW</name>
+ <description>Allows an application to open windows that are for use by parts of the system user interface.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>INTERNET</name>
+ <description>Allows applications to open network sockets.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>MANAGE_APP_TOKENS</name>
+ <description>Allows an application to manage (create, destroy, Z-order) application tokens in the window manager.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>MASTER_CLEAR</name>
+ <description></description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>MODIFY_AUDIO_SETTINGS</name>
+ <description>Allows an application to modify global audio settings</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>MODIFY_PHONE_STATE</name>
+ <description>Allows modification of the telephony state - power on, mmi, etc.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>MOUNT_FORMAT_FILESYSTEMS</name>
+ <description>Allows formatting file systems for removable storage.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>MOUNT_UNMOUNT_FILESYSTEMS</name>
+ <description>Allows mounting and unmounting file systems for removable storage.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>PERSISTENT_ACTIVITY</name>
+ <description>Allow an application to make its activities persistent.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>PROCESS_OUTGOING_CALLS</name>
+ <description>Allows an application to monitor, modify, or abort outgoing calls.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>READ_CALENDAR</name>
+ <description>Allows an application to read the user's calendar data.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>READ_CONTACTS</name>
+ <description>Allows an application to read the user's contacts data.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>READ_FRAME_BUFFER</name>
+ <description>Allows an application to take screen shots and more generally get access to the frame buffer data</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>READ_INPUT_STATE</name>
+ <description>Allows an application to retrieve the current state of keys and switches.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>READ_LOGS</name>
+ <description>Allows an application to read the low-level system log files.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>READ_OWNER_DATA</name>
+ <description>Allows an application to read the owner's data.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>READ_PHONE_STATE</name>
+ <description>Allows read only access to phone state.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>READ_SMS</name>
+ <description>Allows an application to read SMS messages.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>READ_SYNC_SETTINGS</name>
+ <description>Allows applications to read the sync settings</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>READ_SYNC_STATS</name>
+ <description>Allows applications to read the sync stats</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>REBOOT</name>
+ <description>Required to be able to reboot the device.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>RECEIVE_BOOT_COMPLETED</name>
+ <description>Allows an application to receive the ACTION_BOOT_COMPLETED that is broadcast after the system finishes booting.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>RECEIVE_MMS</name>
+ <description>Allows an application to monitor incoming MMS messages, to record or perform processing on them.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>RECEIVE_SMS</name>
+ <description>Allows an application to monitor incoming SMS messages, to record or perform processing on them.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>RECEIVE_WAP_PUSH</name>
+ <description>Allows an application to monitor incoming WAP push messages.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>RECORD_AUDIO</name>
+ <description>Allows an application to record audio</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>REORDER_TASKS</name>
+ <description>Allows an application to change the Z-order of tasks</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>RESTART_PACKAGES</name>
+ <description> This constant is deprecated. The restartPackage(String) API is no longer supported.</description>
+ <deprecated>true</deprecated>
+ </permission>
+ <permission>
+ <name>SEND_SMS</name>
+ <description>Allows an application to send SMS messages.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>SET_ACTIVITY_WATCHER</name>
+ <description>Allows an application to watch and control how activities are started globally in the system.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>SET_ALWAYS_FINISH</name>
+ <description>Allows an application to control whether activities are immediately finished when put in the background.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>SET_ANIMATION_SCALE</name>
+ <description>Modify the global animation scaling factor.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>SET_DEBUG_APP</name>
+ <description>Configure an application for debugging.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>SET_ORIENTATION</name>
+ <description>Allows low-level access to setting the orientation (actually rotation) of the screen.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>SET_PREFERRED_APPLICATIONS</name>
+ <description> This constant is deprecated. No longer useful, see addPackageToPreferred(String) for details.</description>
+ <deprecated>true</deprecated>
+ </permission>
+ <permission>
+ <name>SET_PROCESS_LIMIT</name>
+ <description>Allows an application to set the maximum number of (not needed) application processes that can be running.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>SET_TIME_ZONE</name>
+ <description>Allows applications to set the system time zone</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>SET_WALLPAPER</name>
+ <description>Allows applications to set the wallpaper</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>SET_WALLPAPER_HINTS</name>
+ <description>Allows applications to set the wallpaper hints</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>SIGNAL_PERSISTENT_PROCESSES</name>
+ <description>Allow an application to request that a signal be sent to all persistent processes</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>STATUS_BAR</name>
+ <description>Allows an application to open, close, or disable the status bar and its icons.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>SUBSCRIBED_FEEDS_READ</name>
+ <description>Allows an application to allow access the subscribed feeds ContentProvider.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>SUBSCRIBED_FEEDS_WRITE</name>
+ <description></description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>SYSTEM_ALERT_WINDOW</name>
+ <description>Allows an application to open windows using the type TYPE_SYSTEM_ALERT, shown on top of all other applications.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>UPDATE_DEVICE_STATS</name>
+ <description>Allows an application to update device statistics.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>VIBRATE</name>
+ <description>Allows access to the vibrator</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>WAKE_LOCK</name>
+ <description>Allows using PowerManager WakeLocks to keep processor from sleeping or screen from dimming</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>WRITE_APN_SETTINGS</name>
+ <description>Allows applications to write the apn settings</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>WRITE_CALENDAR</name>
+ <description>Allows an application to write (but not read) the user's calendar data.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>WRITE_CONTACTS</name>
+ <description>Allows an application to write (but not read) the user's contacts data.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>WRITE_GSERVICES</name>
+ <description>Allows an application to modify the Google service map.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>WRITE_OWNER_DATA</name>
+ <description>Allows an application to write (but not read) the owner's data.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>WRITE_SECURE_SETTINGS</name>
+ <description>Allows an application to read or write the secure system settings.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>WRITE_SETTINGS</name>
+ <description>Allows an application to read or write the system settings.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>WRITE_SMS</name>
+ <description>Allows an application to write SMS messages.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>WRITE_SYNC_SETTINGS</name>
+ <description>Allows applications to write the sync settings</description>
+ <deprecated>false</deprecated>
+ </permission>
+ </api_level>
+
+ <!-- API level 4, Android platform 1.6 (Donut) - Based on Linux Kernel 2.6.29 -->
+ <api_level number="4">
+ <permission>
+ <name>ACCESS_CHECKIN_PROPERTIES</name>
+ <description>Allows read/write access to the "properties" table in the checkin database, to change values that get uploaded.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>ACCESS_COARSE_LOCATION</name>
+ <description>Allows an application to access coarse (e.g., Cell-ID, WiFi) location</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>ACCESS_FINE_LOCATION</name>
+ <description>Allows an application to access fine (e.g., GPS) location</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>ACCESS_LOCATION_EXTRA_COMMANDS</name>
+ <description>Allows an application to access extra location provider commands</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>ACCESS_MOCK_LOCATION</name>
+ <description>Allows an application to create mock location providers for testing</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>ACCESS_NETWORK_STATE</name>
+ <description>Allows applications to access information about networks</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>ACCESS_SURFACE_FLINGER</name>
+ <description>Allows an application to use SurfaceFlinger's low level features</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>ACCESS_WIFI_STATE</name>
+ <description>Allows applications to access information about Wi-Fi networks</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>BATTERY_STATS</name>
+ <description>Allows an application to collect battery statistics</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>BIND_APPWIDGET</name>
+ <description>Allows an application to tell the AppWidget service which application can access AppWidget's data.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>BIND_INPUT_METHOD</name>
+ <description>Must be required by an InputMethodService, to ensure that only the system can bind to it.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>BLUETOOTH</name>
+ <description>Allows applications to connect to paired bluetooth devices</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>BLUETOOTH_ADMIN</name>
+ <description>Allows applications to discover and pair bluetooth devices</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>BRICK</name>
+ <description>Required to be able to disable the device (very dangerous!).</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>BROADCAST_PACKAGE_REMOVED</name>
+ <description>Allows an application to broadcast a notification that an application package has been removed.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>BROADCAST_SMS</name>
+ <description>Allows an application to broadcast an SMS receipt notification</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>BROADCAST_STICKY</name>
+ <description>Allows an application to broadcast sticky intents.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>BROADCAST_WAP_PUSH</name>
+ <description>Allows an application to broadcast a WAP PUSH receipt notification</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>CALL_PHONE</name>
+ <description>Allows an application to initiate a phone call without going through the Dialer user interface for the user to confirm the call being placed.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>CALL_PRIVILEGED</name>
+ <description>Allows an application to call any phone number, including emergency numbers, without going through the Dialer user interface for the user to confirm the call being placed.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>CAMERA</name>
+ <description>Required to be able to access the camera device.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>CHANGE_COMPONENT_ENABLED_STATE</name>
+ <description>Allows an application to change whether an application component (other than its own) is enabled or not.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>CHANGE_CONFIGURATION</name>
+ <description>Allows an application to modify the current configuration, such as locale.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>CHANGE_NETWORK_STATE</name>
+ <description>Allows applications to change network connectivity state</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>CHANGE_WIFI_MULTICAST_STATE</name>
+ <description>Allows applications to enter Wi-Fi Multicast mode</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>CHANGE_WIFI_STATE</name>
+ <description>Allows applications to change Wi-Fi connectivity state</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>CLEAR_APP_CACHE</name>
+ <description>Allows an application to clear the caches of all installed applications on the device.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>CLEAR_APP_USER_DATA</name>
+ <description>Allows an application to clear user data</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>CONTROL_LOCATION_UPDATES</name>
+ <description>Allows enabling/disabling location update notifications from the radio.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>DELETE_CACHE_FILES</name>
+ <description>Allows an application to delete cache files.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>DELETE_PACKAGES</name>
+ <description>Allows an application to delete packages.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>DEVICE_POWER</name>
+ <description>Allows low-level access to power management</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>DIAGNOSTIC</name>
+ <description>Allows applications to RW to diagnostic resources.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>DISABLE_KEYGUARD</name>
+ <description>Allows applications to disable the keyguard</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>DUMP</name>
+ <description>Allows an application to retrieve state dump information from system services.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>EXPAND_STATUS_BAR</name>
+ <description>Allows an application to expand or collapse the status bar.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>FACTORY_TEST</name>
+ <description>Run as a manufacturer test application, running as the root user.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>FLASHLIGHT</name>
+ <description>Allows access to the flashlight</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>FORCE_BACK</name>
+ <description>Allows an application to force a BACK operation on whatever is the top activity.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>GET_ACCOUNTS</name>
+ <description>Allows access to the list of accounts in the Accounts Service</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>GET_PACKAGE_SIZE</name>
+ <description>Allows an application to find out the space used by any package.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>GET_TASKS</name>
+ <description>Allows an application to get information about the currently or recently running tasks: a thumbnail representation of the tasks, what activities are running in it, etc.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>GLOBAL_SEARCH</name>
+ <description>This permission can be used on content providers to allow the global search system to access their data.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>HARDWARE_TEST</name>
+ <description>Allows access to hardware peripherals.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>INJECT_EVENTS</name>
+ <description>Allows an application to inject user events (keys, touch, trackball) into the event stream and deliver them to ANY window.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>INSTALL_LOCATION_PROVIDER</name>
+ <description>Allows an application to install a location provider into the Location Manager</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>INSTALL_PACKAGES</name>
+ <description>Allows an application to install packages.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>INTERNAL_SYSTEM_WINDOW</name>
+ <description>Allows an application to open windows that are for use by parts of the system user interface.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>INTERNET</name>
+ <description>Allows applications to open network sockets.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>MANAGE_APP_TOKENS</name>
+ <description>Allows an application to manage (create, destroy, Z-order) application tokens in the window manager.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>MASTER_CLEAR</name>
+ <description></description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>MODIFY_AUDIO_SETTINGS</name>
+ <description>Allows an application to modify global audio settings</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>MODIFY_PHONE_STATE</name>
+ <description>Allows modification of the telephony state - power on, mmi, etc.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>MOUNT_FORMAT_FILESYSTEMS</name>
+ <description>Allows formatting file systems for removable storage.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>MOUNT_UNMOUNT_FILESYSTEMS</name>
+ <description>Allows mounting and unmounting file systems for removable storage.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>PERSISTENT_ACTIVITY</name>
+ <description>Allow an application to make its activities persistent.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>PROCESS_OUTGOING_CALLS</name>
+ <description>Allows an application to monitor, modify, or abort outgoing calls.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>READ_CALENDAR</name>
+ <description>Allows an application to read the user's calendar data.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>READ_CONTACTS</name>
+ <description>Allows an application to read the user's contacts data.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>READ_FRAME_BUFFER</name>
+ <description>Allows an application to take screen shots and more generally get access to the frame buffer data</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>READ_HISTORY_BOOKMARKS</name>
+ <description>Allows an application to read (but not write) the user's browsing history and bookmarks.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>READ_INPUT_STATE</name>
+ <description>Allows an application to retrieve the current state of keys and switches.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>READ_LOGS</name>
+ <description>Allows an application to read the low-level system log files.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>READ_OWNER_DATA</name>
+ <description>Allows an application to read the owner's data.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>READ_PHONE_STATE</name>
+ <description>Allows read only access to phone state.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>READ_SMS</name>
+ <description>Allows an application to read SMS messages.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>READ_SYNC_SETTINGS</name>
+ <description>Allows applications to read the sync settings</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>READ_SYNC_STATS</name>
+ <description>Allows applications to read the sync stats</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>REBOOT</name>
+ <description>Required to be able to reboot the device.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>RECEIVE_BOOT_COMPLETED</name>
+ <description>Allows an application to receive the ACTION_BOOT_COMPLETED that is broadcast after the system finishes booting.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>RECEIVE_MMS</name>
+ <description>Allows an application to monitor incoming MMS messages, to record or perform processing on them.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>RECEIVE_SMS</name>
+ <description>Allows an application to monitor incoming SMS messages, to record or perform processing on them.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>RECEIVE_WAP_PUSH</name>
+ <description>Allows an application to monitor incoming WAP push messages.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>RECORD_AUDIO</name>
+ <description>Allows an application to record audio</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>REORDER_TASKS</name>
+ <description>Allows an application to change the Z-order of tasks</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>RESTART_PACKAGES</name>
+ <description> This constant is deprecated. The restartPackage(String) API is no longer supported.</description>
+ <deprecated>true</deprecated>
+ </permission>
+ <permission>
+ <name>SEND_SMS</name>
+ <description>Allows an application to send SMS messages.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>SET_ACTIVITY_WATCHER</name>
+ <description>Allows an application to watch and control how activities are started globally in the system.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>SET_ALWAYS_FINISH</name>
+ <description>Allows an application to control whether activities are immediately finished when put in the background.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>SET_ANIMATION_SCALE</name>
+ <description>Modify the global animation scaling factor.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>SET_DEBUG_APP</name>
+ <description>Configure an application for debugging.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>SET_ORIENTATION</name>
+ <description>Allows low-level access to setting the orientation (actually rotation) of the screen.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>SET_PREFERRED_APPLICATIONS</name>
+ <description> This constant is deprecated. No longer useful, see addPackageToPreferred(String) for details.</description>
+ <deprecated>true</deprecated>
+ </permission>
+ <permission>
+ <name>SET_PROCESS_LIMIT</name>
+ <description>Allows an application to set the maximum number of (not needed) application processes that can be running.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>SET_TIME_ZONE</name>
+ <description>Allows applications to set the system time zone</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>SET_WALLPAPER</name>
+ <description>Allows applications to set the wallpaper</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>SET_WALLPAPER_HINTS</name>
+ <description>Allows applications to set the wallpaper hints</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>SIGNAL_PERSISTENT_PROCESSES</name>
+ <description>Allow an application to request that a signal be sent to all persistent processes</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>STATUS_BAR</name>
+ <description>Allows an application to open, close, or disable the status bar and its icons.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>SUBSCRIBED_FEEDS_READ</name>
+ <description>Allows an application to allow access the subscribed feeds ContentProvider.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>SUBSCRIBED_FEEDS_WRITE</name>
+ <description></description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>SYSTEM_ALERT_WINDOW</name>
+ <description>Allows an application to open windows using the type TYPE_SYSTEM_ALERT, shown on top of all other applications.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>UPDATE_DEVICE_STATS</name>
+ <description>Allows an application to update device statistics.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>VIBRATE</name>
+ <description>Allows access to the vibrator</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>WAKE_LOCK</name>
+ <description>Allows using PowerManager WakeLocks to keep processor from sleeping or screen from dimming</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>WRITE_APN_SETTINGS</name>
+ <description>Allows applications to write the apn settings</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>WRITE_CALENDAR</name>
+ <description>Allows an application to write (but not read) the user's calendar data.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>WRITE_CONTACTS</name>
+ <description>Allows an application to write (but not read) the user's contacts data.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>WRITE_EXTERNAL_STORAGE</name>
+ <description>Allows an application to write to external storage</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>WRITE_GSERVICES</name>
+ <description>Allows an application to modify the Google service map.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>WRITE_HISTORY_BOOKMARKS</name>
+ <description>Allows an application to write (but not read) the user's browsing history and bookmarks.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>WRITE_OWNER_DATA</name>
+ <description>Allows an application to write (but not read) the owner's data.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>WRITE_SECURE_SETTINGS</name>
+ <description>Allows an application to read or write the secure system settings.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>WRITE_SETTINGS</name>
+ <description>Allows an application to read or write the system settings.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>WRITE_SMS</name>
+ <description>Allows an application to write SMS messages.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>WRITE_SYNC_SETTINGS</name>
+ <description>Allows applications to write the sync settings</description>
+ <deprecated>false</deprecated>
+ </permission>
+ </api_level>
+
+ <!-- API level 5, Android platform 2.0 (Eclair) - Based on Linux Kernel 2.6.29 -->
+ <api_level number="5">
+ <permission>
+ <name>ACCESS_CHECKIN_PROPERTIES</name>
+ <description>Allows read/write access to the "properties" table in the checkin database, to change values that get uploaded.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>ACCESS_COARSE_LOCATION</name>
+ <description>Allows an application to access coarse (e.g., Cell-ID, WiFi) location</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>ACCESS_FINE_LOCATION</name>
+ <description>Allows an application to access fine (e.g., GPS) location</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>ACCESS_LOCATION_EXTRA_COMMANDS</name>
+ <description>Allows an application to access extra location provider commands</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>ACCESS_MOCK_LOCATION</name>
+ <description>Allows an application to create mock location providers for testing</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>ACCESS_NETWORK_STATE</name>
+ <description>Allows applications to access information about networks</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>ACCESS_SURFACE_FLINGER</name>
+ <description>Allows an application to use SurfaceFlinger's low level features</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>ACCESS_WIFI_STATE</name>
+ <description>Allows applications to access information about Wi-Fi networks</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>ACCOUNT_MANAGER</name>
+ <description>Allows applications to call into AccountAuthenticators.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>AUTHENTICATE_ACCOUNTS</name>
+ <description>Allows an application to act as an AccountAuthenticator for the AccountManager</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>BATTERY_STATS</name>
+ <description>Allows an application to collect battery statistics</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>BIND_APPWIDGET</name>
+ <description>Allows an application to tell the AppWidget service which application can access AppWidget's data.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>BIND_INPUT_METHOD</name>
+ <description>Must be required by an InputMethodService, to ensure that only the system can bind to it.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>BLUETOOTH</name>
+ <description>Allows applications to connect to paired bluetooth devices</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>BLUETOOTH_ADMIN</name>
+ <description>Allows applications to discover and pair bluetooth devices</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>BRICK</name>
+ <description>Required to be able to disable the device (very dangerous!).</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>BROADCAST_PACKAGE_REMOVED</name>
+ <description>Allows an application to broadcast a notification that an application package has been removed.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>BROADCAST_SMS</name>
+ <description>Allows an application to broadcast an SMS receipt notification</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>BROADCAST_STICKY</name>
+ <description>Allows an application to broadcast sticky intents.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>BROADCAST_WAP_PUSH</name>
+ <description>Allows an application to broadcast a WAP PUSH receipt notification</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>CALL_PHONE</name>
+ <description>Allows an application to initiate a phone call without going through the Dialer user interface for the user to confirm the call being placed.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>CALL_PRIVILEGED</name>
+ <description>Allows an application to call any phone number, including emergency numbers, without going through the Dialer user interface for the user to confirm the call being placed.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>CAMERA</name>
+ <description>Required to be able to access the camera device.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>CHANGE_COMPONENT_ENABLED_STATE</name>
+ <description>Allows an application to change whether an application component (other than its own) is enabled or not.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>CHANGE_CONFIGURATION</name>
+ <description>Allows an application to modify the current configuration, such as locale.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>CHANGE_NETWORK_STATE</name>
+ <description>Allows applications to change network connectivity state</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>CHANGE_WIFI_MULTICAST_STATE</name>
+ <description>Allows applications to enter Wi-Fi Multicast mode</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>CHANGE_WIFI_STATE</name>
+ <description>Allows applications to change Wi-Fi connectivity state</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>CLEAR_APP_CACHE</name>
+ <description>Allows an application to clear the caches of all installed applications on the device.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>CLEAR_APP_USER_DATA</name>
+ <description>Allows an application to clear user data</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>CONTROL_LOCATION_UPDATES</name>
+ <description>Allows enabling/disabling location update notifications from the radio.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>DELETE_CACHE_FILES</name>
+ <description>Allows an application to delete cache files.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>DELETE_PACKAGES</name>
+ <description>Allows an application to delete packages.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>DEVICE_POWER</name>
+ <description>Allows low-level access to power management</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>DIAGNOSTIC</name>
+ <description>Allows applications to RW to diagnostic resources.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>DISABLE_KEYGUARD</name>
+ <description>Allows applications to disable the keyguard</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>DUMP</name>
+ <description>Allows an application to retrieve state dump information from system services.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>EXPAND_STATUS_BAR</name>
+ <description>Allows an application to expand or collapse the status bar.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>FACTORY_TEST</name>
+ <description>Run as a manufacturer test application, running as the root user.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>FLASHLIGHT</name>
+ <description>Allows access to the flashlight</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>FORCE_BACK</name>
+ <description>Allows an application to force a BACK operation on whatever is the top activity.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>GET_ACCOUNTS</name>
+ <description>Allows access to the list of accounts in the Accounts Service</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>GET_PACKAGE_SIZE</name>
+ <description>Allows an application to find out the space used by any package.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>GET_TASKS</name>
+ <description>Allows an application to get information about the currently or recently running tasks: a thumbnail representation of the tasks, what activities are running in it, etc.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>GLOBAL_SEARCH</name>
+ <description>This permission can be used on content providers to allow the global search system to access their data.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>HARDWARE_TEST</name>
+ <description>Allows access to hardware peripherals.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>INJECT_EVENTS</name>
+ <description>Allows an application to inject user events (keys, touch, trackball) into the event stream and deliver them to ANY window.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>INSTALL_LOCATION_PROVIDER</name>
+ <description>Allows an application to install a location provider into the Location Manager</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>INSTALL_PACKAGES</name>
+ <description>Allows an application to install packages.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>INTERNAL_SYSTEM_WINDOW</name>
+ <description>Allows an application to open windows that are for use by parts of the system user interface.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>INTERNET</name>
+ <description>Allows applications to open network sockets.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>MANAGE_ACCOUNTS</name>
+ <description>Allows an application to manage the list of accounts in the AccountManager</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>MANAGE_APP_TOKENS</name>
+ <description>Allows an application to manage (create, destroy, Z-order) application tokens in the window manager.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>MASTER_CLEAR</name>
+ <description></description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>MODIFY_AUDIO_SETTINGS</name>
+ <description>Allows an application to modify global audio settings</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>MODIFY_PHONE_STATE</name>
+ <description>Allows modification of the telephony state - power on, mmi, etc.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>MOUNT_FORMAT_FILESYSTEMS</name>
+ <description>Allows formatting file systems for removable storage.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>MOUNT_UNMOUNT_FILESYSTEMS</name>
+ <description>Allows mounting and unmounting file systems for removable storage.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>PERSISTENT_ACTIVITY</name>
+ <description>Allow an application to make its activities persistent.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>PROCESS_OUTGOING_CALLS</name>
+ <description>Allows an application to monitor, modify, or abort outgoing calls.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>READ_CALENDAR</name>
+ <description>Allows an application to read the user's calendar data.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>READ_CONTACTS</name>
+ <description>Allows an application to read the user's contacts data.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>READ_FRAME_BUFFER</name>
+ <description>Allows an application to take screen shots and more generally get access to the frame buffer data</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>READ_HISTORY_BOOKMARKS</name>
+ <description>Allows an application to read (but not write) the user's browsing history and bookmarks.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>READ_INPUT_STATE</name>
+ <description>Allows an application to retrieve the current state of keys and switches.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>READ_LOGS</name>
+ <description>Allows an application to read the low-level system log files.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>READ_OWNER_DATA</name>
+ <description>Allows an application to read the owner's data.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>READ_PHONE_STATE</name>
+ <description>Allows read only access to phone state.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>READ_SMS</name>
+ <description>Allows an application to read SMS messages.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>READ_SYNC_SETTINGS</name>
+ <description>Allows applications to read the sync settings</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>READ_SYNC_STATS</name>
+ <description>Allows applications to read the sync stats</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>REBOOT</name>
+ <description>Required to be able to reboot the device.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>RECEIVE_BOOT_COMPLETED</name>
+ <description>Allows an application to receive the ACTION_BOOT_COMPLETED that is broadcast after the system finishes booting.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>RECEIVE_MMS</name>
+ <description>Allows an application to monitor incoming MMS messages, to record or perform processing on them.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>RECEIVE_SMS</name>
+ <description>Allows an application to monitor incoming SMS messages, to record or perform processing on them.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>RECEIVE_WAP_PUSH</name>
+ <description>Allows an application to monitor incoming WAP push messages.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>RECORD_AUDIO</name>
+ <description>Allows an application to record audio</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>REORDER_TASKS</name>
+ <description>Allows an application to change the Z-order of tasks</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>RESTART_PACKAGES</name>
+ <description> This constant is deprecated. The restartPackage(String) API is no longer supported.</description>
+ <deprecated>true</deprecated>
+ </permission>
+ <permission>
+ <name>SEND_SMS</name>
+ <description>Allows an application to send SMS messages.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>SET_ACTIVITY_WATCHER</name>
+ <description>Allows an application to watch and control how activities are started globally in the system.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>SET_ALWAYS_FINISH</name>
+ <description>Allows an application to control whether activities are immediately finished when put in the background.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>SET_ANIMATION_SCALE</name>
+ <description>Modify the global animation scaling factor.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>SET_DEBUG_APP</name>
+ <description>Configure an application for debugging.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>SET_ORIENTATION</name>
+ <description>Allows low-level access to setting the orientation (actually rotation) of the screen.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>SET_PREFERRED_APPLICATIONS</name>
+ <description> This constant is deprecated. No longer useful, see addPackageToPreferred(String) for details.</description>
+ <deprecated>true</deprecated>
+ </permission>
+ <permission>
+ <name>SET_PROCESS_LIMIT</name>
+ <description>Allows an application to set the maximum number of (not needed) application processes that can be running.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>SET_TIME_ZONE</name>
+ <description>Allows applications to set the system time zone</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>SET_WALLPAPER</name>
+ <description>Allows applications to set the wallpaper</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>SET_WALLPAPER_HINTS</name>
+ <description>Allows applications to set the wallpaper hints</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>SIGNAL_PERSISTENT_PROCESSES</name>
+ <description>Allow an application to request that a signal be sent to all persistent processes</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>STATUS_BAR</name>
+ <description>Allows an application to open, close, or disable the status bar and its icons.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>SUBSCRIBED_FEEDS_READ</name>
+ <description>Allows an application to allow access the subscribed feeds ContentProvider.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>SUBSCRIBED_FEEDS_WRITE</name>
+ <description></description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>SYSTEM_ALERT_WINDOW</name>
+ <description>Allows an application to open windows using the type TYPE_SYSTEM_ALERT, shown on top of all other applications.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>UPDATE_DEVICE_STATS</name>
+ <description>Allows an application to update device statistics.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>USE_CREDENTIALS</name>
+ <description>Allows an application to request authtokens from the AccountManager</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>VIBRATE</name>
+ <description>Allows access to the vibrator</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>WAKE_LOCK</name>
+ <description>Allows using PowerManager WakeLocks to keep processor from sleeping or screen from dimming</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>WRITE_APN_SETTINGS</name>
+ <description>Allows applications to write the apn settings</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>WRITE_CALENDAR</name>
+ <description>Allows an application to write (but not read) the user's calendar data.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>WRITE_CONTACTS</name>
+ <description>Allows an application to write (but not read) the user's contacts data.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>WRITE_EXTERNAL_STORAGE</name>
+ <description>Allows an application to write to external storage</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>WRITE_GSERVICES</name>
+ <description>Allows an application to modify the Google service map.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>WRITE_HISTORY_BOOKMARKS</name>
+ <description>Allows an application to write (but not read) the user's browsing history and bookmarks.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>WRITE_OWNER_DATA</name>
+ <description>Allows an application to write (but not read) the owner's data.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>WRITE_SECURE_SETTINGS</name>
+ <description>Allows an application to read or write the secure system settings.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>WRITE_SETTINGS</name>
+ <description>Allows an application to read or write the system settings.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>WRITE_SMS</name>
+ <description>Allows an application to write SMS messages.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>WRITE_SYNC_SETTINGS</name>
+ <description>Allows applications to write the sync settings</description>
+ <deprecated>false</deprecated>
+ </permission>
+ </api_level>
+
+ <!-- API level 6, Android platform 2.0.1 (Eclair) - Based on Linux Kernel 2.6.29 -->
+ <api_level number="6">
+ <permission>
+ <name>ACCESS_CHECKIN_PROPERTIES</name>
+ <description>Allows read/write access to the "properties" table in the checkin database, to change values that get uploaded.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>ACCESS_COARSE_LOCATION</name>
+ <description>Allows an application to access coarse (e.g., Cell-ID, WiFi) location</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>ACCESS_FINE_LOCATION</name>
+ <description>Allows an application to access fine (e.g., GPS) location</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>ACCESS_LOCATION_EXTRA_COMMANDS</name>
+ <description>Allows an application to access extra location provider commands</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>ACCESS_MOCK_LOCATION</name>
+ <description>Allows an application to create mock location providers for testing</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>ACCESS_NETWORK_STATE</name>
+ <description>Allows applications to access information about networks</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>ACCESS_SURFACE_FLINGER</name>
+ <description>Allows an application to use SurfaceFlinger's low level features</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>ACCESS_WIFI_STATE</name>
+ <description>Allows applications to access information about Wi-Fi networks</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>ACCOUNT_MANAGER</name>
+ <description>Allows applications to call into AccountAuthenticators.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>AUTHENTICATE_ACCOUNTS</name>
+ <description>Allows an application to act as an AccountAuthenticator for the AccountManager</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>BATTERY_STATS</name>
+ <description>Allows an application to collect battery statistics</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>BIND_APPWIDGET</name>
+ <description>Allows an application to tell the AppWidget service which application can access AppWidget's data.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>BIND_INPUT_METHOD</name>
+ <description>Must be required by an InputMethodService, to ensure that only the system can bind to it.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>BLUETOOTH</name>
+ <description>Allows applications to connect to paired bluetooth devices</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>BLUETOOTH_ADMIN</name>
+ <description>Allows applications to discover and pair bluetooth devices</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>BRICK</name>
+ <description>Required to be able to disable the device (very dangerous!).</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>BROADCAST_PACKAGE_REMOVED</name>
+ <description>Allows an application to broadcast a notification that an application package has been removed.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>BROADCAST_SMS</name>
+ <description>Allows an application to broadcast an SMS receipt notification</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>BROADCAST_STICKY</name>
+ <description>Allows an application to broadcast sticky intents.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>BROADCAST_WAP_PUSH</name>
+ <description>Allows an application to broadcast a WAP PUSH receipt notification</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>CALL_PHONE</name>
+ <description>Allows an application to initiate a phone call without going through the Dialer user interface for the user to confirm the call being placed.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>CALL_PRIVILEGED</name>
+ <description>Allows an application to call any phone number, including emergency numbers, without going through the Dialer user interface for the user to confirm the call being placed.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>CAMERA</name>
+ <description>Required to be able to access the camera device.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>CHANGE_COMPONENT_ENABLED_STATE</name>
+ <description>Allows an application to change whether an application component (other than its own) is enabled or not.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>CHANGE_CONFIGURATION</name>
+ <description>Allows an application to modify the current configuration, such as locale.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>CHANGE_NETWORK_STATE</name>
+ <description>Allows applications to change network connectivity state</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>CHANGE_WIFI_MULTICAST_STATE</name>
+ <description>Allows applications to enter Wi-Fi Multicast mode</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>CHANGE_WIFI_STATE</name>
+ <description>Allows applications to change Wi-Fi connectivity state</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>CLEAR_APP_CACHE</name>
+ <description>Allows an application to clear the caches of all installed applications on the device.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>CLEAR_APP_USER_DATA</name>
+ <description>Allows an application to clear user data</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>CONTROL_LOCATION_UPDATES</name>
+ <description>Allows enabling/disabling location update notifications from the radio.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>DELETE_CACHE_FILES</name>
+ <description>Allows an application to delete cache files.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>DELETE_PACKAGES</name>
+ <description>Allows an application to delete packages.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>DEVICE_POWER</name>
+ <description>Allows low-level access to power management</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>DIAGNOSTIC</name>
+ <description>Allows applications to RW to diagnostic resources.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>DISABLE_KEYGUARD</name>
+ <description>Allows applications to disable the keyguard</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>DUMP</name>
+ <description>Allows an application to retrieve state dump information from system services.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>EXPAND_STATUS_BAR</name>
+ <description>Allows an application to expand or collapse the status bar.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>FACTORY_TEST</name>
+ <description>Run as a manufacturer test application, running as the root user.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>FLASHLIGHT</name>
+ <description>Allows access to the flashlight</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>FORCE_BACK</name>
+ <description>Allows an application to force a BACK operation on whatever is the top activity.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>GET_ACCOUNTS</name>
+ <description>Allows access to the list of accounts in the Accounts Service</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>GET_PACKAGE_SIZE</name>
+ <description>Allows an application to find out the space used by any package.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>GET_TASKS</name>
+ <description>Allows an application to get information about the currently or recently running tasks: a thumbnail representation of the tasks, what activities are running in it, etc.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>GLOBAL_SEARCH</name>
+ <description>This permission can be used on content providers to allow the global search system to access their data.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>HARDWARE_TEST</name>
+ <description>Allows access to hardware peripherals.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>INJECT_EVENTS</name>
+ <description>Allows an application to inject user events (keys, touch, trackball) into the event stream and deliver them to ANY window.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>INSTALL_LOCATION_PROVIDER</name>
+ <description>Allows an application to install a location provider into the Location Manager</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>INSTALL_PACKAGES</name>
+ <description>Allows an application to install packages.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>INTERNAL_SYSTEM_WINDOW</name>
+ <description>Allows an application to open windows that are for use by parts of the system user interface.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>INTERNET</name>
+ <description>Allows applications to open network sockets.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>MANAGE_ACCOUNTS</name>
+ <description>Allows an application to manage the list of accounts in the AccountManager</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>MANAGE_APP_TOKENS</name>
+ <description>Allows an application to manage (create, destroy, Z-order) application tokens in the window manager.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>MASTER_CLEAR</name>
+ <description></description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>MODIFY_AUDIO_SETTINGS</name>
+ <description>Allows an application to modify global audio settings</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>MODIFY_PHONE_STATE</name>
+ <description>Allows modification of the telephony state - power on, mmi, etc.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>MOUNT_FORMAT_FILESYSTEMS</name>
+ <description>Allows formatting file systems for removable storage.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>MOUNT_UNMOUNT_FILESYSTEMS</name>
+ <description>Allows mounting and unmounting file systems for removable storage.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>PERSISTENT_ACTIVITY</name>
+ <description>Allow an application to make its activities persistent.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>PROCESS_OUTGOING_CALLS</name>
+ <description>Allows an application to monitor, modify, or abort outgoing calls.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>READ_CALENDAR</name>
+ <description>Allows an application to read the user's calendar data.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>READ_CONTACTS</name>
+ <description>Allows an application to read the user's contacts data.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>READ_FRAME_BUFFER</name>
+ <description>Allows an application to take screen shots and more generally get access to the frame buffer data</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>READ_HISTORY_BOOKMARKS</name>
+ <description>Allows an application to read (but not write) the user's browsing history and bookmarks.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>READ_INPUT_STATE</name>
+ <description>Allows an application to retrieve the current state of keys and switches.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>READ_LOGS</name>
+ <description>Allows an application to read the low-level system log files.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>READ_OWNER_DATA</name>
+ <description>Allows an application to read the owner's data.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>READ_PHONE_STATE</name>
+ <description>Allows read only access to phone state.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>READ_SMS</name>
+ <description>Allows an application to read SMS messages.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>READ_SYNC_SETTINGS</name>
+ <description>Allows applications to read the sync settings</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>READ_SYNC_STATS</name>
+ <description>Allows applications to read the sync stats</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>REBOOT</name>
+ <description>Required to be able to reboot the device.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>RECEIVE_BOOT_COMPLETED</name>
+ <description>Allows an application to receive the ACTION_BOOT_COMPLETED that is broadcast after the system finishes booting.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>RECEIVE_MMS</name>
+ <description>Allows an application to monitor incoming MMS messages, to record or perform processing on them.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>RECEIVE_SMS</name>
+ <description>Allows an application to monitor incoming SMS messages, to record or perform processing on them.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>RECEIVE_WAP_PUSH</name>
+ <description>Allows an application to monitor incoming WAP push messages.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>RECORD_AUDIO</name>
+ <description>Allows an application to record audio</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>REORDER_TASKS</name>
+ <description>Allows an application to change the Z-order of tasks</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>RESTART_PACKAGES</name>
+ <description> This constant is deprecated. The restartPackage(String) API is no longer supported.</description>
+ <deprecated>true</deprecated>
+ </permission>
+ <permission>
+ <name>SEND_SMS</name>
+ <description>Allows an application to send SMS messages.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>SET_ACTIVITY_WATCHER</name>
+ <description>Allows an application to watch and control how activities are started globally in the system.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>SET_ALWAYS_FINISH</name>
+ <description>Allows an application to control whether activities are immediately finished when put in the background.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>SET_ANIMATION_SCALE</name>
+ <description>Modify the global animation scaling factor.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>SET_DEBUG_APP</name>
+ <description>Configure an application for debugging.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>SET_ORIENTATION</name>
+ <description>Allows low-level access to setting the orientation (actually rotation) of the screen.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>SET_PREFERRED_APPLICATIONS</name>
+ <description> This constant is deprecated. No longer useful, see addPackageToPreferred(String) for details.</description>
+ <deprecated>true</deprecated>
+ </permission>
+ <permission>
+ <name>SET_PROCESS_LIMIT</name>
+ <description>Allows an application to set the maximum number of (not needed) application processes that can be running.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>SET_TIME_ZONE</name>
+ <description>Allows applications to set the system time zone</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>SET_WALLPAPER</name>
+ <description>Allows applications to set the wallpaper</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>SET_WALLPAPER_HINTS</name>
+ <description>Allows applications to set the wallpaper hints</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>SIGNAL_PERSISTENT_PROCESSES</name>
+ <description>Allow an application to request that a signal be sent to all persistent processes</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>STATUS_BAR</name>
+ <description>Allows an application to open, close, or disable the status bar and its icons.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>SUBSCRIBED_FEEDS_READ</name>
+ <description>Allows an application to allow access the subscribed feeds ContentProvider.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>SUBSCRIBED_FEEDS_WRITE</name>
+ <description></description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>SYSTEM_ALERT_WINDOW</name>
+ <description>Allows an application to open windows using the type TYPE_SYSTEM_ALERT, shown on top of all other applications.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>UPDATE_DEVICE_STATS</name>
+ <description>Allows an application to update device statistics.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>USE_CREDENTIALS</name>
+ <description>Allows an application to request authtokens from the AccountManager</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>VIBRATE</name>
+ <description>Allows access to the vibrator</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>WAKE_LOCK</name>
+ <description>Allows using PowerManager WakeLocks to keep processor from sleeping or screen from dimming</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>WRITE_APN_SETTINGS</name>
+ <description>Allows applications to write the apn settings</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>WRITE_CALENDAR</name>
+ <description>Allows an application to write (but not read) the user's calendar data.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>WRITE_CONTACTS</name>
+ <description>Allows an application to write (but not read) the user's contacts data.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>WRITE_EXTERNAL_STORAGE</name>
+ <description>Allows an application to write to external storage</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>WRITE_GSERVICES</name>
+ <description>Allows an application to modify the Google service map.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>WRITE_HISTORY_BOOKMARKS</name>
+ <description>Allows an application to write (but not read) the user's browsing history and bookmarks.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>WRITE_OWNER_DATA</name>
+ <description>Allows an application to write (but not read) the owner's data.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>WRITE_SECURE_SETTINGS</name>
+ <description>Allows an application to read or write the secure system settings.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>WRITE_SETTINGS</name>
+ <description>Allows an application to read or write the system settings.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>WRITE_SMS</name>
+ <description>Allows an application to write SMS messages.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>WRITE_SYNC_SETTINGS</name>
+ <description>Allows applications to write the sync settings</description>
+ <deprecated>false</deprecated>
+ </permission>
+ </api_level>
+
+ <!-- API level 7, Android platform 2.1 (Eclair) - Based on Linux Kernel 2.6.29 -->
+ <api_level number="7">
+ <permission>
+ <name>ACCESS_CHECKIN_PROPERTIES</name>
+ <description>Allows read/write access to the "properties" table in the checkin database, to change values that get uploaded.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>ACCESS_COARSE_LOCATION</name>
+ <description>Allows an application to access coarse (e.g., Cell-ID, WiFi) location</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>ACCESS_FINE_LOCATION</name>
+ <description>Allows an application to access fine (e.g., GPS) location</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>ACCESS_LOCATION_EXTRA_COMMANDS</name>
+ <description>Allows an application to access extra location provider commands</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>ACCESS_MOCK_LOCATION</name>
+ <description>Allows an application to create mock location providers for testing</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>ACCESS_NETWORK_STATE</name>
+ <description>Allows applications to access information about networks</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>ACCESS_SURFACE_FLINGER</name>
+ <description>Allows an application to use SurfaceFlinger's low level features</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>ACCESS_WIFI_STATE</name>
+ <description>Allows applications to access information about Wi-Fi networks</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>ACCOUNT_MANAGER</name>
+ <description>Allows applications to call into AccountAuthenticators.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>AUTHENTICATE_ACCOUNTS</name>
+ <description>Allows an application to act as an AccountAuthenticator for the AccountManager</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>BATTERY_STATS</name>
+ <description>Allows an application to collect battery statistics</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>BIND_APPWIDGET</name>
+ <description>Allows an application to tell the AppWidget service which application can access AppWidget's data.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>BIND_INPUT_METHOD</name>
+ <description>Must be required by an InputMethodService, to ensure that only the system can bind to it.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>BLUETOOTH</name>
+ <description>Allows applications to connect to paired bluetooth devices</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>BLUETOOTH_ADMIN</name>
+ <description>Allows applications to discover and pair bluetooth devices</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>BRICK</name>
+ <description>Required to be able to disable the device (very dangerous!).</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>BROADCAST_PACKAGE_REMOVED</name>
+ <description>Allows an application to broadcast a notification that an application package has been removed.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>BROADCAST_SMS</name>
+ <description>Allows an application to broadcast an SMS receipt notification</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>BROADCAST_STICKY</name>
+ <description>Allows an application to broadcast sticky intents.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>BROADCAST_WAP_PUSH</name>
+ <description>Allows an application to broadcast a WAP PUSH receipt notification</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>CALL_PHONE</name>
+ <description>Allows an application to initiate a phone call without going through the Dialer user interface for the user to confirm the call being placed.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>CALL_PRIVILEGED</name>
+ <description>Allows an application to call any phone number, including emergency numbers, without going through the Dialer user interface for the user to confirm the call being placed.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>CAMERA</name>
+ <description>Required to be able to access the camera device.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>CHANGE_COMPONENT_ENABLED_STATE</name>
+ <description>Allows an application to change whether an application component (other than its own) is enabled or not.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>CHANGE_CONFIGURATION</name>
+ <description>Allows an application to modify the current configuration, such as locale.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>CHANGE_NETWORK_STATE</name>
+ <description>Allows applications to change network connectivity state</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>CHANGE_WIFI_MULTICAST_STATE</name>
+ <description>Allows applications to enter Wi-Fi Multicast mode</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>CHANGE_WIFI_STATE</name>
+ <description>Allows applications to change Wi-Fi connectivity state</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>CLEAR_APP_CACHE</name>
+ <description>Allows an application to clear the caches of all installed applications on the device.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>CLEAR_APP_USER_DATA</name>
+ <description>Allows an application to clear user data</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>CONTROL_LOCATION_UPDATES</name>
+ <description>Allows enabling/disabling location update notifications from the radio.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>DELETE_CACHE_FILES</name>
+ <description>Allows an application to delete cache files.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>DELETE_PACKAGES</name>
+ <description>Allows an application to delete packages.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>DEVICE_POWER</name>
+ <description>Allows low-level access to power management</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>DIAGNOSTIC</name>
+ <description>Allows applications to RW to diagnostic resources.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>DISABLE_KEYGUARD</name>
+ <description>Allows applications to disable the keyguard</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>DUMP</name>
+ <description>Allows an application to retrieve state dump information from system services.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>EXPAND_STATUS_BAR</name>
+ <description>Allows an application to expand or collapse the status bar.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>FACTORY_TEST</name>
+ <description>Run as a manufacturer test application, running as the root user.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>FLASHLIGHT</name>
+ <description>Allows access to the flashlight</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>FORCE_BACK</name>
+ <description>Allows an application to force a BACK operation on whatever is the top activity.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>GET_ACCOUNTS</name>
+ <description>Allows access to the list of accounts in the Accounts Service</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>GET_PACKAGE_SIZE</name>
+ <description>Allows an application to find out the space used by any package.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>GET_TASKS</name>
+ <description>Allows an application to get information about the currently or recently running tasks: a thumbnail representation of the tasks, what activities are running in it, etc.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>GLOBAL_SEARCH</name>
+ <description>This permission can be used on content providers to allow the global search system to access their data.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>HARDWARE_TEST</name>
+ <description>Allows access to hardware peripherals.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>INJECT_EVENTS</name>
+ <description>Allows an application to inject user events (keys, touch, trackball) into the event stream and deliver them to ANY window.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>INSTALL_LOCATION_PROVIDER</name>
+ <description>Allows an application to install a location provider into the Location Manager</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>INSTALL_PACKAGES</name>
+ <description>Allows an application to install packages.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>INTERNAL_SYSTEM_WINDOW</name>
+ <description>Allows an application to open windows that are for use by parts of the system user interface.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>INTERNET</name>
+ <description>Allows applications to open network sockets.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>MANAGE_ACCOUNTS</name>
+ <description>Allows an application to manage the list of accounts in the AccountManager</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>MANAGE_APP_TOKENS</name>
+ <description>Allows an application to manage (create, destroy, Z-order) application tokens in the window manager.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>MASTER_CLEAR</name>
+ <description></description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>MODIFY_AUDIO_SETTINGS</name>
+ <description>Allows an application to modify global audio settings</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>MODIFY_PHONE_STATE</name>
+ <description>Allows modification of the telephony state - power on, mmi, etc.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>MOUNT_FORMAT_FILESYSTEMS</name>
+ <description>Allows formatting file systems for removable storage.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>MOUNT_UNMOUNT_FILESYSTEMS</name>
+ <description>Allows mounting and unmounting file systems for removable storage.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>PERSISTENT_ACTIVITY</name>
+ <description>Allow an application to make its activities persistent.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>PROCESS_OUTGOING_CALLS</name>
+ <description>Allows an application to monitor, modify, or abort outgoing calls.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>READ_CALENDAR</name>
+ <description>Allows an application to read the user's calendar data.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>READ_CONTACTS</name>
+ <description>Allows an application to read the user's contacts data.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>READ_FRAME_BUFFER</name>
+ <description>Allows an application to take screen shots and more generally get access to the frame buffer data</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>READ_HISTORY_BOOKMARKS</name>
+ <description>Allows an application to read (but not write) the user's browsing history and bookmarks.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>READ_INPUT_STATE</name>
+ <description>Allows an application to retrieve the current state of keys and switches.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>READ_LOGS</name>
+ <description>Allows an application to read the low-level system log files.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>READ_OWNER_DATA</name>
+ <description>Allows an application to read the owner's data.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>READ_PHONE_STATE</name>
+ <description>Allows read only access to phone state.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>READ_SMS</name>
+ <description>Allows an application to read SMS messages.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>READ_SYNC_SETTINGS</name>
+ <description>Allows applications to read the sync settings</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>READ_SYNC_STATS</name>
+ <description>Allows applications to read the sync stats</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>REBOOT</name>
+ <description>Required to be able to reboot the device.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>RECEIVE_BOOT_COMPLETED</name>
+ <description>Allows an application to receive the ACTION_BOOT_COMPLETED that is broadcast after the system finishes booting.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>RECEIVE_MMS</name>
+ <description>Allows an application to monitor incoming MMS messages, to record or perform processing on them.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>RECEIVE_SMS</name>
+ <description>Allows an application to monitor incoming SMS messages, to record or perform processing on them.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>RECEIVE_WAP_PUSH</name>
+ <description>Allows an application to monitor incoming WAP push messages.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>RECORD_AUDIO</name>
+ <description>Allows an application to record audio</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>REORDER_TASKS</name>
+ <description>Allows an application to change the Z-order of tasks</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>RESTART_PACKAGES</name>
+ <description> This constant is deprecated. The restartPackage(String) API is no longer supported.</description>
+ <deprecated>true</deprecated>
+ </permission>
+ <permission>
+ <name>SEND_SMS</name>
+ <description>Allows an application to send SMS messages.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>SET_ACTIVITY_WATCHER</name>
+ <description>Allows an application to watch and control how activities are started globally in the system.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>SET_ALWAYS_FINISH</name>
+ <description>Allows an application to control whether activities are immediately finished when put in the background.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>SET_ANIMATION_SCALE</name>
+ <description>Modify the global animation scaling factor.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>SET_DEBUG_APP</name>
+ <description>Configure an application for debugging.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>SET_ORIENTATION</name>
+ <description>Allows low-level access to setting the orientation (actually rotation) of the screen.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>SET_PREFERRED_APPLICATIONS</name>
+ <description> This constant is deprecated. No longer useful, see addPackageToPreferred(String) for details.</description>
+ <deprecated>true</deprecated>
+ </permission>
+ <permission>
+ <name>SET_PROCESS_LIMIT</name>
+ <description>Allows an application to set the maximum number of (not needed) application processes that can be running.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>SET_TIME_ZONE</name>
+ <description>Allows applications to set the system time zone</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>SET_WALLPAPER</name>
+ <description>Allows applications to set the wallpaper</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>SET_WALLPAPER_HINTS</name>
+ <description>Allows applications to set the wallpaper hints</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>SIGNAL_PERSISTENT_PROCESSES</name>
+ <description>Allow an application to request that a signal be sent to all persistent processes</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>STATUS_BAR</name>
+ <description>Allows an application to open, close, or disable the status bar and its icons.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>SUBSCRIBED_FEEDS_READ</name>
+ <description>Allows an application to allow access the subscribed feeds ContentProvider.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>SUBSCRIBED_FEEDS_WRITE</name>
+ <description></description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>SYSTEM_ALERT_WINDOW</name>
+ <description>Allows an application to open windows using the type TYPE_SYSTEM_ALERT, shown on top of all other applications.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>UPDATE_DEVICE_STATS</name>
+ <description>Allows an application to update device statistics.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>USE_CREDENTIALS</name>
+ <description>Allows an application to request authtokens from the AccountManager</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>VIBRATE</name>
+ <description>Allows access to the vibrator</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>WAKE_LOCK</name>
+ <description>Allows using PowerManager WakeLocks to keep processor from sleeping or screen from dimming</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>WRITE_APN_SETTINGS</name>
+ <description>Allows applications to write the apn settings</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>WRITE_CALENDAR</name>
+ <description>Allows an application to write (but not read) the user's calendar data.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>WRITE_CONTACTS</name>
+ <description>Allows an application to write (but not read) the user's contacts data.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>WRITE_EXTERNAL_STORAGE</name>
+ <description>Allows an application to write to external storage</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>WRITE_GSERVICES</name>
+ <description>Allows an application to modify the Google service map.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>WRITE_HISTORY_BOOKMARKS</name>
+ <description>Allows an application to write (but not read) the user's browsing history and bookmarks.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>WRITE_OWNER_DATA</name>
+ <description>Allows an application to write (but not read) the owner's data.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>WRITE_SECURE_SETTINGS</name>
+ <description>Allows an application to read or write the secure system settings.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>WRITE_SETTINGS</name>
+ <description>Allows an application to read or write the system settings.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>WRITE_SMS</name>
+ <description>Allows an application to write SMS messages.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>WRITE_SYNC_SETTINGS</name>
+ <description>Allows applications to write the sync settings</description>
+ <deprecated>false</deprecated>
+ </permission>
+ </api_level>
+
+ <!-- API level 8, Android platform 2.2 (Froyo) - Based on Linux Kernel 2.6.32 -->
+ <api_level number="8">
+ <permission>
+ <name>ACCESS_CHECKIN_PROPERTIES</name>
+ <description>Allows read/write access to the "properties" table in the checkin database, to change values that get uploaded.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>ACCESS_COARSE_LOCATION</name>
+ <description>Allows an application to access coarse (e.g., Cell-ID, WiFi) location</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>ACCESS_FINE_LOCATION</name>
+ <description>Allows an application to access fine (e.g., GPS) location</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>ACCESS_LOCATION_EXTRA_COMMANDS</name>
+ <description>Allows an application to access extra location provider commands</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>ACCESS_MOCK_LOCATION</name>
+ <description>Allows an application to create mock location providers for testing</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>ACCESS_NETWORK_STATE</name>
+ <description>Allows applications to access information about networks</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>ACCESS_SURFACE_FLINGER</name>
+ <description>Allows an application to use SurfaceFlinger's low level features</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>ACCESS_WIFI_STATE</name>
+ <description>Allows applications to access information about Wi-Fi networks</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>ACCOUNT_MANAGER</name>
+ <description>Allows applications to call into AccountAuthenticators.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>AUTHENTICATE_ACCOUNTS</name>
+ <description>Allows an application to act as an AccountAuthenticator for the AccountManager</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>BATTERY_STATS</name>
+ <description>Allows an application to collect battery statistics</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>BIND_APPWIDGET</name>
+ <description>Allows an application to tell the AppWidget service which application can access AppWidget's data.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>BIND_DEVICE_ADMIN</name>
+ <description>Must be required by device administration receiver, to ensure that only the system can interact with it.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>BIND_INPUT_METHOD</name>
+ <description>Must be required by an InputMethodService, to ensure that only the system can bind to it.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>BIND_WALLPAPER</name>
+ <description>Must be required by a WallpaperService, to ensure that only the system can bind to it.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>BLUETOOTH</name>
+ <description>Allows applications to connect to paired bluetooth devices</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>BLUETOOTH_ADMIN</name>
+ <description>Allows applications to discover and pair bluetooth devices</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>BRICK</name>
+ <description>Required to be able to disable the device (very dangerous!).</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>BROADCAST_PACKAGE_REMOVED</name>
+ <description>Allows an application to broadcast a notification that an application package has been removed.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>BROADCAST_SMS</name>
+ <description>Allows an application to broadcast an SMS receipt notification</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>BROADCAST_STICKY</name>
+ <description>Allows an application to broadcast sticky intents.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>BROADCAST_WAP_PUSH</name>
+ <description>Allows an application to broadcast a WAP PUSH receipt notification</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>CALL_PHONE</name>
+ <description>Allows an application to initiate a phone call without going through the Dialer user interface for the user to confirm the call being placed.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>CALL_PRIVILEGED</name>
+ <description>Allows an application to call any phone number, including emergency numbers, without going through the Dialer user interface for the user to confirm the call being placed.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>CAMERA</name>
+ <description>Required to be able to access the camera device.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>CHANGE_COMPONENT_ENABLED_STATE</name>
+ <description>Allows an application to change whether an application component (other than its own) is enabled or not.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>CHANGE_CONFIGURATION</name>
+ <description>Allows an application to modify the current configuration, such as locale.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>CHANGE_NETWORK_STATE</name>
+ <description>Allows applications to change network connectivity state</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>CHANGE_WIFI_MULTICAST_STATE</name>
+ <description>Allows applications to enter Wi-Fi Multicast mode</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>CHANGE_WIFI_STATE</name>
+ <description>Allows applications to change Wi-Fi connectivity state</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>CLEAR_APP_CACHE</name>
+ <description>Allows an application to clear the caches of all installed applications on the device.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>CLEAR_APP_USER_DATA</name>
+ <description>Allows an application to clear user data</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>CONTROL_LOCATION_UPDATES</name>
+ <description>Allows enabling/disabling location update notifications from the radio.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>DELETE_CACHE_FILES</name>
+ <description>Allows an application to delete cache files.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>DELETE_PACKAGES</name>
+ <description>Allows an application to delete packages.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>DEVICE_POWER</name>
+ <description>Allows low-level access to power management</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>DIAGNOSTIC</name>
+ <description>Allows applications to RW to diagnostic resources.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>DISABLE_KEYGUARD</name>
+ <description>Allows applications to disable the keyguard</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>DUMP</name>
+ <description>Allows an application to retrieve state dump information from system services.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>EXPAND_STATUS_BAR</name>
+ <description>Allows an application to expand or collapse the status bar.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>FACTORY_TEST</name>
+ <description>Run as a manufacturer test application, running as the root user.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>FLASHLIGHT</name>
+ <description>Allows access to the flashlight</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>FORCE_BACK</name>
+ <description>Allows an application to force a BACK operation on whatever is the top activity.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>GET_ACCOUNTS</name>
+ <description>Allows access to the list of accounts in the Accounts Service</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>GET_PACKAGE_SIZE</name>
+ <description>Allows an application to find out the space used by any package.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>GET_TASKS</name>
+ <description>Allows an application to get information about the currently or recently running tasks: a thumbnail representation of the tasks, what activities are running in it, etc.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>GLOBAL_SEARCH</name>
+ <description>This permission can be used on content providers to allow the global search system to access their data.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>HARDWARE_TEST</name>
+ <description>Allows access to hardware peripherals.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>INJECT_EVENTS</name>
+ <description>Allows an application to inject user events (keys, touch, trackball) into the event stream and deliver them to ANY window.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>INSTALL_LOCATION_PROVIDER</name>
+ <description>Allows an application to install a location provider into the Location Manager</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>INSTALL_PACKAGES</name>
+ <description>Allows an application to install packages.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>INTERNAL_SYSTEM_WINDOW</name>
+ <description>Allows an application to open windows that are for use by parts of the system user interface.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>INTERNET</name>
+ <description>Allows applications to open network sockets.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>KILL_BACKGROUND_PROCESSES</name>
+ <description>Allows an application to call killBackgroundProcesses(String).</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>MANAGE_ACCOUNTS</name>
+ <description>Allows an application to manage the list of accounts in the AccountManager</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>MANAGE_APP_TOKENS</name>
+ <description>Allows an application to manage (create, destroy, Z-order) application tokens in the window manager.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>MASTER_CLEAR</name>
+ <description></description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>MODIFY_AUDIO_SETTINGS</name>
+ <description>Allows an application to modify global audio settings</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>MODIFY_PHONE_STATE</name>
+ <description>Allows modification of the telephony state - power on, mmi, etc.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>MOUNT_FORMAT_FILESYSTEMS</name>
+ <description>Allows formatting file systems for removable storage.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>MOUNT_UNMOUNT_FILESYSTEMS</name>
+ <description>Allows mounting and unmounting file systems for removable storage.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>PERSISTENT_ACTIVITY</name>
+ <description>Allow an application to make its activities persistent.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>PROCESS_OUTGOING_CALLS</name>
+ <description>Allows an application to monitor, modify, or abort outgoing calls.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>READ_CALENDAR</name>
+ <description>Allows an application to read the user's calendar data.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>READ_CONTACTS</name>
+ <description>Allows an application to read the user's contacts data.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>READ_FRAME_BUFFER</name>
+ <description>Allows an application to take screen shots and more generally get access to the frame buffer data</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>READ_HISTORY_BOOKMARKS</name>
+ <description>Allows an application to read (but not write) the user's browsing history and bookmarks.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>READ_INPUT_STATE</name>
+ <description>Allows an application to retrieve the current state of keys and switches.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>READ_LOGS</name>
+ <description>Allows an application to read the low-level system log files.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>READ_OWNER_DATA</name>
+ <description>Allows an application to read the owner's data.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>READ_PHONE_STATE</name>
+ <description>Allows read only access to phone state.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>READ_SMS</name>
+ <description>Allows an application to read SMS messages.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>READ_SYNC_SETTINGS</name>
+ <description>Allows applications to read the sync settings</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>READ_SYNC_STATS</name>
+ <description>Allows applications to read the sync stats</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>REBOOT</name>
+ <description>Required to be able to reboot the device.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>RECEIVE_BOOT_COMPLETED</name>
+ <description>Allows an application to receive the ACTION_BOOT_COMPLETED that is broadcast after the system finishes booting.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>RECEIVE_MMS</name>
+ <description>Allows an application to monitor incoming MMS messages, to record or perform processing on them.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>RECEIVE_SMS</name>
+ <description>Allows an application to monitor incoming SMS messages, to record or perform processing on them.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>RECEIVE_WAP_PUSH</name>
+ <description>Allows an application to monitor incoming WAP push messages.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>RECORD_AUDIO</name>
+ <description>Allows an application to record audio</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>REORDER_TASKS</name>
+ <description>Allows an application to change the Z-order of tasks</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>RESTART_PACKAGES</name>
+ <description> This constant is deprecated. The restartPackage(String) API is no longer supported.</description>
+ <deprecated>true</deprecated>
+ </permission>
+ <permission>
+ <name>SEND_SMS</name>
+ <description>Allows an application to send SMS messages.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>SET_ACTIVITY_WATCHER</name>
+ <description>Allows an application to watch and control how activities are started globally in the system.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>SET_ALWAYS_FINISH</name>
+ <description>Allows an application to control whether activities are immediately finished when put in the background.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>SET_ANIMATION_SCALE</name>
+ <description>Modify the global animation scaling factor.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>SET_DEBUG_APP</name>
+ <description>Configure an application for debugging.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>SET_ORIENTATION</name>
+ <description>Allows low-level access to setting the orientation (actually rotation) of the screen.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>SET_PREFERRED_APPLICATIONS</name>
+ <description> This constant is deprecated. No longer useful, see addPackageToPreferred(String) for details.</description>
+ <deprecated>true</deprecated>
+ </permission>
+ <permission>
+ <name>SET_PROCESS_LIMIT</name>
+ <description>Allows an application to set the maximum number of (not needed) application processes that can be running.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>SET_TIME</name>
+ <description>Allows applications to set the system time</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>SET_TIME_ZONE</name>
+ <description>Allows applications to set the system time zone</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>SET_WALLPAPER</name>
+ <description>Allows applications to set the wallpaper</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>SET_WALLPAPER_HINTS</name>
+ <description>Allows applications to set the wallpaper hints</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>SIGNAL_PERSISTENT_PROCESSES</name>
+ <description>Allow an application to request that a signal be sent to all persistent processes</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>STATUS_BAR</name>
+ <description>Allows an application to open, close, or disable the status bar and its icons.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>SUBSCRIBED_FEEDS_READ</name>
+ <description>Allows an application to allow access the subscribed feeds ContentProvider.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>SUBSCRIBED_FEEDS_WRITE</name>
+ <description></description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>SYSTEM_ALERT_WINDOW</name>
+ <description>Allows an application to open windows using the type TYPE_SYSTEM_ALERT, shown on top of all other applications.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>UPDATE_DEVICE_STATS</name>
+ <description>Allows an application to update device statistics.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>USE_CREDENTIALS</name>
+ <description>Allows an application to request authtokens from the AccountManager</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>VIBRATE</name>
+ <description>Allows access to the vibrator</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>WAKE_LOCK</name>
+ <description>Allows using PowerManager WakeLocks to keep processor from sleeping or screen from dimming</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>WRITE_APN_SETTINGS</name>
+ <description>Allows applications to write the apn settings</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>WRITE_CALENDAR</name>
+ <description>Allows an application to write (but not read) the user's calendar data.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>WRITE_CONTACTS</name>
+ <description>Allows an application to write (but not read) the user's contacts data.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>WRITE_EXTERNAL_STORAGE</name>
+ <description>Allows an application to write to external storage</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>WRITE_GSERVICES</name>
+ <description>Allows an application to modify the Google service map.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>WRITE_HISTORY_BOOKMARKS</name>
+ <description>Allows an application to write (but not read) the user's browsing history and bookmarks.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>WRITE_OWNER_DATA</name>
+ <description>Allows an application to write (but not read) the owner's data.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>WRITE_SECURE_SETTINGS</name>
+ <description>Allows an application to read or write the secure system settings.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>WRITE_SETTINGS</name>
+ <description>Allows an application to read or write the system settings.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>WRITE_SMS</name>
+ <description>Allows an application to write SMS messages.</description>
+ <deprecated>false</deprecated>
+ </permission>
+ <permission>
+ <name>WRITE_SYNC_SETTINGS</name>
+ <description>Allows applications to write the sync settings</description>
+ <deprecated>false</deprecated>
+ </permission>
+ </api_level>
+
+ <!-- API level n, Android platform x.x (Gingerbread) - Based on Linux Kernel x.x.xx -->
+
+</api_permissions> \ No newline at end of file
diff --git a/src/plugins/preflighting.core/files/blocked_permissions.txt b/src/plugins/preflighting.core/files/blocked_permissions.txt
new file mode 100644
index 0000000..0cc62de
--- /dev/null
+++ b/src/plugins/preflighting.core/files/blocked_permissions.txt
@@ -0,0 +1,64 @@
+android.permission.ACCESS_CACHE_FILESYSTEM
+android.permission.ACCESS_CHECKIN_PROPERTIES
+android.permission.ACCESS_DOWNLOAD_MANAGER
+android.permission.ACCESS_DOWNLOAD_MANAGER_ADVANCED
+android.permission.BACKUP
+android.permission.BIND_WALLPAPER
+android.permission.CALL_PRIVILEGED
+android.permission.CONTROL_LOCATION_UPDATES
+android.permission.DELETE_CACHE_FILES
+android.permission.DELETE_PACKAGES
+android.permission.INSTALL_PACKAGES
+android.permission.MASTER_CLEAR
+android.permission.MOVE_PACKAGE
+android.permission.PERFORM_CDMA_PROVISIONING
+android.permission.SET_TIME
+android.permission.SET_WALLPAPER_COMPONENT
+android.permission.STATUS_BAR
+android.permission.WRITE_GSERVICES
+android.permission.ACCESS_BLUETOOTH_SHARE
+android.permission.ACCESS_SURFACE_FLINGER
+android.permission.ACCESS_DRM
+android.permission.BIND_DEVICE_ADMIN
+android.permission.BIND_INPUT_METHOD
+android.permission.BRICK
+android.permission.BROADCAST_PACKAGE_REMOVED
+android.permission.BROADCAST_SMS
+android.permission.CHANGE_COMPONENT_ENABLED_STATE
+android.permission.CLEAR_APP_USER_DATA
+android.permission.COPY_PROTECTED_DATA
+android.permission.DELETE_CACHE_FILES
+android.permission.DEVICE_POWER
+android.permission.FACTORY_TEST
+android.permission.FORCE_BACK
+android.permission.FORCE_STOP_PACKAGES
+android.permission.INJECT_EVENTS
+android.permission.INTERNAL_SYSTEM_WINDOW
+android.permission.MANAGE_APP_TOKENS
+android.permission.PACKAGE_USAGE_STATS
+android.permission.READ_FRAME_BUFFER
+android.permission.READ_INPUT_STATE
+android.permission.REBOOT
+android.permission.SEND_DOWNLOAD_COMPLETED_INTENTS
+android.permission.SET_ACTIVITY_WATCHER
+android.permission.SET_ORIENTATION
+android.permission.SHUTDOWN
+android.permission.STOP_APP_SWITCHES
+android.permission.UPDATE_DEVICE_STATS
+android.permission.GLOBAL_SEARCH
+android.permission.WRITE_SECURE_SETTINGS
+android.permission.ASEC_ACCESS
+android.permission.ASEC_CREATE
+android.permission.ASEC_DESTROY
+android.permission.ASEC_MOUNT_UNMOUNT
+android.permission.ASEC_RENAME
+android.permission.CHANGE_BACKGROUND_DATA_SETTING
+android.permission.DIAGNOSTIC
+android.permission.GLOBAL_SEARCH_CONTROL
+android.permission.SET_PREFERRED_APPLICATIONS
+android.permission.WRITE_GSERVICES
+android.permission.HARDWARE_TEST
+android.permission.INSTALL_LOCATION_PROVIDER
+android.permission.BROADCAST_WAP_PUSH
+android.permission.BIND_APPWIDGET
+android.permission.ACCOUNT_MANAGER
diff --git a/src/plugins/preflighting.core/files/devices/droidx/hardware.ini b/src/plugins/preflighting.core/files/devices/droidx/hardware.ini
new file mode 100644
index 0000000..34a907b
--- /dev/null
+++ b/src/plugins/preflighting.core/files/devices/droidx/hardware.ini
@@ -0,0 +1,73 @@
+# This file describes the properties of a given Motorola Mobility handset
+#
+# In the sections below, each possible hardware parameter is defined and the default value listed.
+# If the default value is not correct, change the value to the correct one, and remove the leading
+# hash sign to enable the parameter to override the default.
+#
+# Copyright 2009-2010 Motorola Mobility, Inc.
+#
+
+# An integer specifying the amount of physical RAM on the device in megabytes. [96]
+hw.ramSize = 512M
+
+# Does the device have a touch screen? Boolean with values of yes or no. [yes]
+touchScreen = yes
+
+# Does the device have a trackball? Boolean with values of yes or no. [yes]
+hw.trackBall = no
+
+# Does the device have a physical keyboard? (qwerty or similar)
+# Boolean with values of yes or no. [yes]
+hw.keyboard = no
+
+# Does the device have a Dpad? Boolean with values of yes or no. [yes]
+#hw.dPad = yes
+
+# Does the device support a GSM modem? Boolean with values of yes or no. [yes]
+gsmModem = no
+
+# Does the device have a camera? Boolean with values of yes or no. [no]
+hw.camera = yes
+
+# If the device has a camera, how many horizontal pixels does it have? Integer [640]
+hw.camera.maxHorizontalPixels = 3265
+
+# If the device has a camera, how many vertical pixels does it have? Integer [480
+hw.camera.maxVerticalPixels = 2488
+
+# Does the device have GPS? Boolean with values of yes or no. [yes]
+hw.gps = yes
+
+# Is the device battery powered? Boolean with values of yes or no. [yes]
+hw.battery = yes
+
+# Does the device have an accelerometer.? Boolean with values of yes or no. [yes]
+# Accelerometer (used for auto-rotation)
+hw.accelerometer = yes
+
+# Does the device support audio recording? Boolean with values of yes or no. [yes]
+hw.audioInput = yes
+
+# Does the device support audio playback? Boolean with values of yes or no. [yes]
+hw.audioOutput = yes
+
+# Does the device support removal of an SD Card? Boolean with values of yes or no. [yes]
+hw.sdCard = yes
+
+# Integer specifying the size in megabytes of the system partition. [72]
+disk.systemPartition.size = 156M
+
+# Integer specifying the size in megabytes of the data partition. [66]
+disk.dataPartition.size = 66M
+
+# Does the device have a cache partition? Boolean with values of yes or no. [yes]
+disk.cachePartition = yes
+
+# Integer specifying the size in megabytes of the cache partition if present. [66]
+disk.cachePartition.size = 66M
+
+# Approximate LCD pixel density in pixels per inch. Allowed values are 120, 160, or 240. [160]
+hw.lcd.density = 240
+
+# Integer specifying in megabytes the maximum Virtual Memory heap size. [16]
+vm.heapSize = 32M
diff --git a/src/plugins/preflighting.core/files/devices/droidx/manifest.ini b/src/plugins/preflighting.core/files/devices/droidx/manifest.ini
new file mode 100644
index 0000000..9e92721
--- /dev/null
+++ b/src/plugins/preflighting.core/files/devices/droidx/manifest.ini
@@ -0,0 +1,13 @@
+# SDK Add-on Manifest
+name=DroidX
+vendor=Motorola Mobility, Inc.
+description=WVGA Android 2.1 phone without physical keyboard
+
+# default skin
+skin=DroidX
+
+# version of the Android platform on which this add-on is built.
+api=7
+
+# revision of the add-on
+revision=1 \ No newline at end of file
diff --git a/src/plugins/preflighting.core/files/feature-apilevel-map.xml b/src/plugins/preflighting.core/files/feature-apilevel-map.xml
new file mode 100644
index 0000000..8d48bde
--- /dev/null
+++ b/src/plugins/preflighting.core/files/feature-apilevel-map.xml
@@ -0,0 +1,83 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<feature_api>
+ <feature>
+ <id>android.hardware.bluetooth</id>
+ <since_apilevel>5</since_apilevel>
+ </feature>
+ <feature>
+ <id>android.hardware.camera</id>
+ <since_apilevel>2</since_apilevel>
+ </feature>
+ <feature>
+ <id>android.hardware.camera.autofocus</id>
+ <since_apilevel>4</since_apilevel>
+ </feature>
+ <feature>
+ <id>android.hardware.camera.flash</id>
+ <since_apilevel>5</since_apilevel>
+ </feature>
+ <feature>
+ <id>android.hardware.location</id>
+ <since_apilevel>3</since_apilevel>
+ </feature>
+ <feature>
+ <id>android.hardware.location.gps</id>
+ <since_apilevel>3</since_apilevel>
+ </feature>
+ <feature>
+ <id>android.hardware.location.network</id>
+ <since_apilevel>3</since_apilevel>
+ </feature>
+ <feature>
+ <id>android.hardware.microphone</id>
+ <since_apilevel>2</since_apilevel>
+ </feature>
+ <feature>
+ <id>android.hardware.sensor.accelerometer</id>
+ <since_apilevel>2</since_apilevel>
+ </feature>
+ <feature>
+ <id>android.hardware.sensor.compass</id>
+ <since_apilevel>2</since_apilevel>
+ </feature>
+ <feature>
+ <id>android.hardware.sensor.light</id>
+ <since_apilevel>2</since_apilevel>
+ </feature>
+ <feature>
+ <id>android.hardware.sensor.proximity</id>
+ <since_apilevel>2</since_apilevel>
+ </feature>
+ <feature>
+ <id>android.hardware.telephony</id>
+ <since_apilevel>2</since_apilevel>
+ </feature>
+ <feature>
+ <id>android.hardware.telephony.cdma</id>
+ <since_apilevel>4</since_apilevel>
+ </feature>
+ <feature>
+ <id>android.hardware.telephony.gsm</id>
+ <since_apilevel>2</since_apilevel>
+ </feature>
+ <feature>
+ <id>android.hardware.touchscreen</id>
+ <since_apilevel>2</since_apilevel>
+ </feature>
+ <feature>
+ <id>android.hardware.touchscreen.multitouch</id>
+ <since_apilevel>5</since_apilevel>
+ </feature>
+ <feature>
+ <id>android.hardware.touchscreen.multitouch.distinct</id>
+ <since_apilevel>5</since_apilevel>
+ </feature>
+ <feature>
+ <id>android.hardware.wifi</id>
+ <since_apilevel>1</since_apilevel>
+ </feature>
+ <feature>
+ <id>android.software.live_wallpaper</id>
+ <since_apilevel>7</since_apilevel>
+ </feature>
+</feature_api>
diff --git a/src/plugins/preflighting.core/files/method_permission_list_2.3.csv b/src/plugins/preflighting.core/files/method_permission_list_2.3.csv
new file mode 100644
index 0000000..75f7de9
--- /dev/null
+++ b/src/plugins/preflighting.core/files/method_permission_list_2.3.csv
@@ -0,0 +1,159 @@
+android.accounts.AccountManager.getPassword,AUTHENTICATE_ACCOUNTS
+android.accounts.AccountManager.getUserData,AUTHENTICATE_ACCOUNTS
+android.accounts.AccountManager.getAccounts,GET_ACCOUNTS
+android.accounts.AccountManager.getAccountsByType,GET_ACCOUNTS
+android.accounts.AccountManager.hasFeatures,GET_ACCOUNTS
+android.accounts.AccountManager.getAccountsByTypeAndFeatures,GET_ACCOUNTS
+android.accounts.AccountManager.addAccountExplicitly,AUTHENTICATE_ACCOUNTS
+android.accounts.AccountManager.removeAccount,MANAGE_ACCOUNTS
+android.accounts.AccountManager.invalidateAuthToken,USE_CREDENTIALS||MANAGE_ACCOUNTS
+android.accounts.AccountManager.peekAuthToken,AUTHENTICATE_ACCOUNTS
+android.accounts.AccountManager.setPassword,AUTHENTICATE_ACCOUNTS
+android.accounts.AccountManager.clearPassword,MANAGE_ACCOUNTS
+android.accounts.AccountManager.setUserData,AUTHENTICATE_ACCOUNTS
+android.accounts.AccountManager.setAuthToken,AUTHENTICATE_ACCOUNTS
+android.accounts.AccountManager.blockingGetAuthToken,USE_CREDENTIALS
+android.accounts.AccountManager.getAuthToken,USE_CREDENTIALS
+android.accounts.AccountManager.addAccount,MANAGE_ACCOUNTS
+android.accounts.AccountManager.confirmCredentials,MANAGE_ACCOUNTS
+android.accounts.AccountManager.updateCredentials,MANAGE_ACCOUNTS
+android.accounts.AccountManager.editProperties,MANAGE_ACCOUNTS
+android.accounts.AccountManager.getAuthTokenByFeatures,MANAGE_ACCOUNTS
+android.app.ActivityManager.getRecentTasks,GET_TASKS
+android.app.ActivityManager.getRunningTasks,GET_TASKS
+android.app.ActivityManager.getRunningTasks,GET_TASKS
+android.app.ActivityManager.killBackgroundProcesses,KILL_BACKGROUND_PROCESSES
+android.app.AlarmManager.setTime,SET_TIME
+android.app.AlarmManager.setTimeZone,SET_TIME_ZONE
+android.app.DownloadManager.Request.setDestinationUri,WRITE_EXTERNAL_STORAGE
+android.bluetooth.BluetoothAdapter.isEnabled,BLUETOOTH
+android.bluetooth.BluetoothAdapter.getState,BLUETOOTH
+android.bluetooth.BluetoothAdapter.enable,BLUETOOTH_ADMIN
+android.bluetooth.BluetoothAdapter.disable,BLUETOOTH_ADMIN
+android.bluetooth.BluetoothAdapter.getAddress,BLUETOOTH
+android.bluetooth.BluetoothAdapter.getName,BLUETOOTH
+android.bluetooth.BluetoothAdapter.setName,BLUETOOTH_ADMIN
+android.bluetooth.BluetoothAdapter.getScanMode,BLUETOOTH
+android.bluetooth.BluetoothAdapter.setScanMode,WRITE_SECURE_SETTINGS
+android.bluetooth.BluetoothAdapter.startDiscovery,BLUETOOTH_ADMIN
+android.bluetooth.BluetoothAdapter.cancelDiscovery,BLUETOOTH_ADMIN
+android.bluetooth.BluetoothAdapter.isDiscovering,BLUETOOTH
+android.bluetooth.BluetoothAdapter.getBondedDevices,BLUETOOTH
+android.bluetooth.BluetoothAdapter.listenUsingRfcommOn,BLUETOOTH_ADMIN
+android.bluetooth.BluetoothAdapter.listenUsingRfcommWithServiceRecord,BLUETOOTH
+android.bluetooth.BluetoothAdapter.listenUsingInsecureRfcommWithServiceRecord,BLUETOOTH
+android.bluetooth.BluetoothAdapter.readOutOfBandData,BLUETOOTH
+android.bluetooth.BluetoothDevice.getName,BLUETOOTH
+android.bluetooth.BluetoothDevice.createBond,BLUETOOTH_ADMIN
+android.bluetooth.BluetoothDevice.createBondOutOfBand,BLUETOOTH_ADMIN
+android.bluetooth.BluetoothDevice.setDeviceOutOfBandData,BLUETOOTH_ADMIN
+android.bluetooth.BluetoothDevice.cancelBondProcess,BLUETOOTH_ADMIN
+android.bluetooth.BluetoothDevice.removeBond,BLUETOOTH_ADMIN
+android.bluetooth.BluetoothDevice.getBondState,BLUETOOTH
+android.bluetooth.BluetoothDevice.getBluetoothClass,BLUETOOTH
+android.bluetooth.BluetoothDevice.getTrustState,BLUETOOTH
+android.bluetooth.BluetoothDevice.setTrust,BLUETOOTH_ADMIN
+android.bluetooth.BluetoothDevice.createRfcommSocket,BLUETOOTH
+android.bluetooth.BluetoothDevice.createRfcommSocketToServiceRecord,BLUETOOTH
+android.bluetooth.BluetoothDevice.createInsecureRfcommSocketToServiceRecord,BLUETOOTH
+android.bluetooth.BluetoothDevice.createInsecureRfcommSocket,BLUETOOTH_ADMIN
+android.bluetooth.BluetoothDevice.createScoSocket,BLUETOOTH_ADMIN
+android.content.Context.sendStickyBroadcast,BROADCAST_STICKY
+android.content.Context.removeStickyBroadcast,BROADCAST_STICKY
+android.content.pm.PackageManager.installPackage,INSTALL_PACKAGES
+android.content.pm.PackageManager.deletePackage,DELETE_PACKAGES
+android.content.pm.PackageManager.deleteApplicationCacheFiles,DELETE_CACHE_FILES
+android.content.pm.PackageManager.getPackageSizeInfo,GET_PACKAGE_SIZE
+android.net.Proxy.getPreferredHttpHost,ACCESS_NETWORK_STATE
+android.nfc.NfcAdapter.enableForegroundDispatch,NFC
+android.nfc.NfcAdapter.disableForegroundDispatch,NFC
+android.nfc.NfcAdapter.enableForegroundNdefPush,NFC
+android.nfc.NfcAdapter.disableForegroundNdefPush,NFC
+android.nfc.NfcAdapter.setLocalNdefMessage,NFC
+android.nfc.NfcAdapter.getLocalNdefMessage,NFC
+android.nfc.tech.IsoDep.setTimeout,NFC
+android.nfc.tech.IsoDep.transceive,NFC
+android.nfc.tech.MifareClassic.authenticateSectorWithKeyA,NFC
+android.nfc.tech.MifareClassic.authenticateSectorWithKeyB,NFC
+android.nfc.tech.MifareClassic.readBlock,NFC
+android.nfc.tech.MifareClassic.writeBlock,NFC
+android.nfc.tech.MifareClassic.increment,NFC
+android.nfc.tech.MifareClassic.decrement,NFC
+android.nfc.tech.MifareClassic.transfer,NFC
+android.nfc.tech.MifareClassic.restore,NFC
+android.nfc.tech.MifareClassic.transceive,NFC
+android.nfc.tech.MifareUltralight.readPages,NFC
+android.nfc.tech.MifareUltralight.writePage,NFC
+android.nfc.tech.MifareUltralight.transceive,NFC
+android.nfc.tech.Ndef.isWritable,NFC
+android.nfc.tech.Ndef.writeNdefMessage,NFC
+android.nfc.tech.Ndef.makeReadOnly,NFC
+android.nfc.tech.NdefFormatable.format,NFC
+android.nfc.tech.NdefFormatable.formatReadOnly,NFC
+android.nfc.tech.NfcA.transceive,NFC
+android.nfc.tech.NfcB.transceive,NFC
+android.nfc.tech.NfcF.transceive,NFC
+android.nfc.tech.NfcV.transceive,NFC
+android.nfc.tech.TagTechnology.connect,NFC
+android.nfc.tech.TagTechnology.reconnect,NFC
+android.nfc.tech.TagTechnology.close,NFC
+android.os.PowerManager.reboot,REBOOT
+android.os.RecoverySystem.installPackage,REBOOT
+android.os.RecoverySystem.rebootWipeUserData,REBOOT
+android.provider.Browser.getAllBookmarks,READ_HISTORY_BOOKMARKS
+android.provider.Browser.getAllVisitedUrls,READ_HISTORY_BOOKMARKS
+android.provider.Browser.updateVisitedHistory,WRITE_HISTORY_BOOKMARKS
+android.provider.Browser.updateVisitedHistory,READ_HISTORY_BOOKMARKS
+android.provider.Browser.getVisitedHistory,READ_HISTORY_BOOKMARKS
+android.provider.Browser.truncateHistory,WRITE_HISTORY_BOOKMARKS
+android.provider.Browser.truncateHistory,READ_HISTORY_BOOKMARKS
+android.provider.Browser.canClearHistory,READ_HISTORY_BOOKMARKS
+android.provider.Browser.clearHistory,WRITE_HISTORY_BOOKMARKS
+android.provider.Browser.deleteHistoryWhere,WRITE_HISTORY_BOOKMARKS
+android.provider.Browser.deleteHistoryWhere,READ_HISTORY_BOOKMARKS
+android.provider.Browser.deleteHistoryTimeFrame,WRITE_HISTORY_BOOKMARKS
+android.provider.Browser.deleteFromHistory,WRITE_HISTORY_BOOKMARKS
+android.provider.Browser.addSearchUrl,WRITE_HISTORY_BOOKMARKS
+android.provider.Browser.addSearchUrl,READ_HISTORY_BOOKMARKS
+android.provider.Browser.clearSearches,WRITE_HISTORY_BOOKMARKS
+android.provider.Browser.requestAllIcons,READ_HISTORY_BOOKMARKS
+android.app.DownloadManagerBaseTest.setWiFiStateOn,CHANGE_WIFI_STATE
+android.app.DownloadManagerBaseTest.setWiFiStateOn,ACCESS_WIFI_STATE
+android.app.DownloadManagerBaseTest.setAirplaneModeOn,WRITE_SETTINGS
+android.location.LocationManager.addTestProvider,ACCESS_MOCK_LOCATION
+android.location.LocationManager.removeTestProvider,ACCESS_MOCK_LOCATION
+android.location.LocationManager.setTestProviderLocation,ACCESS_MOCK_LOCATION
+android.location.LocationManager.clearTestProviderLocation,ACCESS_MOCK_LOCATION
+android.location.LocationManager.setTestProviderEnabled,ACCESS_MOCK_LOCATION
+android.location.LocationManager.clearTestProviderEnabled,ACCESS_MOCK_LOCATION
+android.location.LocationManager.setTestProviderStatus,ACCESS_MOCK_LOCATION
+android.location.LocationManager.clearTestProviderStatus,ACCESS_MOCK_LOCATION
+android.location.LocationManager.addGpsStatusListener,ACCESS_FINE_LOCATION
+android.location.LocationManager.addNmeaListener,ACCESS_FINE_LOCATION
+android.media.AsyncPlayer.setUsesWakeLock,WAKE_LOCK
+android.media.AudioManager.startBluetoothSco,MODIFY_AUDIO_SETTINGS
+android.media.AudioManager.stopBluetoothSco,MODIFY_AUDIO_SETTINGS
+android.media.MediaPlayer.setWakeMode,WAKE_LOCK
+android.telephony.PhoneNumberUtils.isVoiceMailNumber,READ_PHONE_STATE
+android.telephony.TelephonyManager.getDeviceSoftwareVersion,READ_PHONE_STATE
+android.telephony.TelephonyManager.getDeviceId,READ_PHONE_STATE
+android.telephony.TelephonyManager.getCellLocation,ACCESS_COARSE_LOCATION||ACCESS_FINE_LOCATION
+android.telephony.TelephonyManager.enableLocationUpdates,CONTROL_LOCATION_UPDATES
+android.telephony.TelephonyManager.disableLocationUpdates,CONTROL_LOCATION_UPDATES
+android.telephony.TelephonyManager.getSimSerialNumber,READ_PHONE_STATE
+android.telephony.TelephonyManager.getSubscriberId,READ_PHONE_STATE
+android.telephony.TelephonyManager.getLine1Number,READ_PHONE_STATE
+android.telephony.TelephonyManager.getLine1AlphaTag,READ_PHONE_STATE
+android.telephony.TelephonyManager.getVoiceMailNumber,READ_PHONE_STATE
+android.telephony.TelephonyManager.getCompleteVoiceMailNumber,CALL_PRIVILEGED
+android.telephony.TelephonyManager.getVoiceMessageCount,READ_PHONE_STATE
+android.telephony.TelephonyManager.getVoiceMailAlphaTag,READ_PHONE_STATE
+android.net.sip.SipAudioCall.setSpeakerMode,MODIFY_AUDIO_SETTINGS
+android.net.sip.SipAudioCall.startAudio,WAKE_LOCK
+android.net.sip.SipAudioCall.startAudio,ACCESS_WIFI_STATE
+android.net.sip.SipAudioCall.startAudio,RECORD_AUDIO
+android.net.wifi.WifiNative.setBluetoothCoexistenceModeCommand,BLUETOOTH_COEXISTENCE_MODE_ENABLED
+android.net.wifi.WifiNative.setBluetoothCoexistenceModeCommand,BLUETOOTH_COEXISTENCE_MODE_SENSE
+android.net.wifi.WifiStateTracker.setBluetoothCoexistenceMode,BLUETOOTH_COEXISTENCE_MODE_ENABLED
+android.net.wifi.WifiStateTracker.setBluetoothCoexistenceMode,BLUETOOTH_COEXISTENCE_MODE_DESABLED
+android.net.wifi.WifiStateTracker.setBluetoothCoexistenceMode,BLUETOOTH_COEXISTENCE_MODE_SENSE
diff --git a/src/plugins/preflighting.core/files/method_permission_list_4.0.csv b/src/plugins/preflighting.core/files/method_permission_list_4.0.csv
new file mode 100644
index 0000000..ded3af2
--- /dev/null
+++ b/src/plugins/preflighting.core/files/method_permission_list_4.0.csv
@@ -0,0 +1,208 @@
+android.accounts.AccountManager.getPassword,AUTHENTICATE_ACCOUNTS,C:\Studio\PermScript\frameworks\base\core\java\android\accounts\AccountManager.java:304
+android.accounts.AccountManager.getUserData,AUTHENTICATE_ACCOUNTS,C:\Studio\PermScript\frameworks\base\core\java\android\accounts\AccountManager.java:329
+android.accounts.AccountManager.getAccounts,GET_ACCOUNTS,C:\Studio\PermScript\frameworks\base\core\java\android\accounts\AccountManager.java:372
+android.accounts.AccountManager.getAccountsByType,GET_ACCOUNTS,C:\Studio\PermScript\frameworks\base\core\java\android\accounts\AccountManager.java:397
+android.accounts.AccountManager.hasFeatures,GET_ACCOUNTS,C:\Studio\PermScript\frameworks\base\core\java\android\accounts\AccountManager.java:429
+android.accounts.AccountManager.getAccountsByTypeAndFeatures,GET_ACCOUNTS,C:\Studio\PermScript\frameworks\base\core\java\android\accounts\AccountManager.java:474
+android.accounts.AccountManager.addAccountExplicitly,AUTHENTICATE_ACCOUNTS,C:\Studio\PermScript\frameworks\base\core\java\android\accounts\AccountManager.java:512
+android.accounts.AccountManager.removeAccount,MANAGE_ACCOUNTS,C:\Studio\PermScript\frameworks\base\core\java\android\accounts\AccountManager.java:543
+android.accounts.AccountManager.invalidateAuthToken,USE_CREDENTIALS,C:\Studio\PermScript\frameworks\base\core\java\android\accounts\AccountManager.java:575
+android.accounts.AccountManager.invalidateAuthToken,MANAGE_ACCOUNTS,C:\Studio\PermScript\frameworks\base\core\java\android\accounts\AccountManager.java:575
+android.accounts.AccountManager.peekAuthToken,AUTHENTICATE_ACCOUNTS,C:\Studio\PermScript\frameworks\base\core\java\android\accounts\AccountManager.java:604
+android.accounts.AccountManager.setPassword,AUTHENTICATE_ACCOUNTS,C:\Studio\PermScript\frameworks\base\core\java\android\accounts\AccountManager.java:630
+android.accounts.AccountManager.clearPassword,MANAGE_ACCOUNTS,C:\Studio\PermScript\frameworks\base\core\java\android\accounts\AccountManager.java:654
+android.accounts.AccountManager.setUserData,AUTHENTICATE_ACCOUNTS,C:\Studio\PermScript\frameworks\base\core\java\android\accounts\AccountManager.java:679
+android.accounts.AccountManager.setAuthToken,AUTHENTICATE_ACCOUNTS,C:\Studio\PermScript\frameworks\base\core\java\android\accounts\AccountManager.java:706
+android.accounts.AccountManager.blockingGetAuthToken,USE_CREDENTIALS,C:\Studio\PermScript\frameworks\base\core\java\android\accounts\AccountManager.java:740
+android.accounts.AccountManager.getAuthToken,USE_CREDENTIALS,C:\Studio\PermScript\frameworks\base\core\java\android\accounts\AccountManager.java:814
+android.accounts.AccountManager.getAuthToken,USE_CREDENTIALS,C:\Studio\PermScript\frameworks\base\core\java\android\accounts\AccountManager.java:904
+android.accounts.AccountManager.getAuthToken,USE_CREDENTIALS,C:\Studio\PermScript\frameworks\base\core\java\android\accounts\AccountManager.java:982
+android.accounts.AccountManager.addAccount,MANAGE_ACCOUNTS,C:\Studio\PermScript\frameworks\base\core\java\android\accounts\AccountManager.java:1050
+android.accounts.AccountManager.confirmCredentials,MANAGE_ACCOUNTS,C:\Studio\PermScript\frameworks\base\core\java\android\accounts\AccountManager.java:1123
+android.accounts.AccountManager.updateCredentials,MANAGE_ACCOUNTS,C:\Studio\PermScript\frameworks\base\core\java\android\accounts\AccountManager.java:1183
+android.accounts.AccountManager.editProperties,MANAGE_ACCOUNTS,C:\Studio\PermScript\frameworks\base\core\java\android\accounts\AccountManager.java:1234
+android.accounts.AccountManager.getAuthTokenByFeatures,MANAGE_ACCOUNTS,C:\Studio\PermScript\frameworks\base\core\java\android\accounts\AccountManager.java:1759
+android.app.ActivityManager.getRecentTasks,GET_TASKS,C:\Studio\PermScript\frameworks\base\core\java\android\app\ActivityManager.java:367
+android.app.ActivityManager.getRunningTasks,GET_TASKS,C:\Studio\PermScript\frameworks\base\core\java\android\app\ActivityManager.java:503
+android.app.ActivityManager.getRunningTasks,GET_TASKS,C:\Studio\PermScript\frameworks\base\core\java\android\app\ActivityManager.java:531
+android.app.ActivityManager.moveTaskToFront,REORDER_TASKS,C:\Studio\PermScript\frameworks\base\core\java\android\app\ActivityManager.java:679
+android.app.ActivityManager.killBackgroundProcesses,KILL_BACKGROUND_PROCESSES,C:\Studio\PermScript\frameworks\base\core\java\android\app\ActivityManager.java:1392
+android.app.AlarmManager.setTime,SET_TIME,C:\Studio\PermScript\frameworks\base\core\java\android\app\AlarmManager.java:286
+android.app.AlarmManager.setTimeZone,SET_TIME_ZONE,C:\Studio\PermScript\frameworks\base\core\java\android\app\AlarmManager.java:299
+android.bluetooth.BluetoothA2dp.connect,BLUETOOTH_ADMIN,C:\Studio\PermScript\frameworks\base\core\java\android\bluetooth\BluetoothA2dp.java:153
+android.bluetooth.BluetoothA2dp.connect,BLUETOOTH,C:\Studio\PermScript\frameworks\base\core\java\android\bluetooth\BluetoothA2dp.java:153
+android.bluetooth.BluetoothA2dp.disconnect,BLUETOOTH_ADMIN,C:\Studio\PermScript\frameworks\base\core\java\android\bluetooth\BluetoothA2dp.java:194
+android.bluetooth.BluetoothA2dp.setPriority,BLUETOOTH_ADMIN,C:\Studio\PermScript\frameworks\base\core\java\android\bluetooth\BluetoothA2dp.java:276
+android.bluetooth.BluetoothA2dp.getPriority,BLUETOOTH,C:\Studio\PermScript\frameworks\base\core\java\android\bluetooth\BluetoothA2dp.java:308
+android.bluetooth.BluetoothA2dp.isA2dpPlaying,BLUETOOTH,C:\Studio\PermScript\frameworks\base\core\java\android\bluetooth\BluetoothA2dp.java:330
+android.bluetooth.BluetoothA2dp.suspendSink,BLUETOOTH_ADMIN,C:\Studio\PermScript\frameworks\base\core\java\android\bluetooth\BluetoothA2dp.java:361
+android.bluetooth.BluetoothA2dp.resumeSink,BLUETOOTH_ADMIN,C:\Studio\PermScript\frameworks\base\core\java\android\bluetooth\BluetoothA2dp.java:391
+android.bluetooth.BluetoothAdapter.isEnabled,BLUETOOTH_ADMIN,C:\Studio\PermScript\frameworks\base\core\java\android\bluetooth\BluetoothAdapter.java:408
+android.bluetooth.BluetoothAdapter.isEnabled,BLUETOOTH,C:\Studio\PermScript\frameworks\base\core\java\android\bluetooth\BluetoothAdapter.java:408
+android.bluetooth.BluetoothAdapter.getState,BLUETOOTH,C:\Studio\PermScript\frameworks\base\core\java\android\bluetooth\BluetoothAdapter.java:426
+android.bluetooth.BluetoothAdapter.enable,BLUETOOTH_ADMIN,C:\Studio\PermScript\frameworks\base\core\java\android\bluetooth\BluetoothAdapter.java:460
+android.bluetooth.BluetoothAdapter.disable,BLUETOOTH_ADMIN,C:\Studio\PermScript\frameworks\base\core\java\android\bluetooth\BluetoothAdapter.java:491
+android.bluetooth.BluetoothAdapter.getAddress,BLUETOOTH,C:\Studio\PermScript\frameworks\base\core\java\android\bluetooth\BluetoothAdapter.java:505
+android.bluetooth.BluetoothAdapter.getName,BLUETOOTH,C:\Studio\PermScript\frameworks\base\core\java\android\bluetooth\BluetoothAdapter.java:519
+android.bluetooth.BluetoothAdapter.getUuids,BLUETOOTH,C:\Studio\PermScript\frameworks\base\core\java\android\bluetooth\BluetoothAdapter.java:534
+android.bluetooth.BluetoothAdapter.setName,BLUETOOTH_ADMIN,C:\Studio\PermScript\frameworks\base\core\java\android\bluetooth\BluetoothAdapter.java:556
+android.bluetooth.BluetoothAdapter.getScanMode,BLUETOOTH,C:\Studio\PermScript\frameworks\base\core\java\android\bluetooth\BluetoothAdapter.java:580
+android.bluetooth.BluetoothAdapter.setScanMode,WRITE_SECURE_SETTINGS,C:\Studio\PermScript\frameworks\base\core\java\android\bluetooth\BluetoothAdapter.java:616
+android.bluetooth.BluetoothAdapter.startDiscovery,BLUETOOTH_ADMIN,C:\Studio\PermScript\frameworks\base\core\java\android\bluetooth\BluetoothAdapter.java:677
+android.bluetooth.BluetoothAdapter.cancelDiscovery,BLUETOOTH_ADMIN,C:\Studio\PermScript\frameworks\base\core\java\android\bluetooth\BluetoothAdapter.java:702
+android.bluetooth.BluetoothAdapter.isDiscovering,BLUETOOTH,C:\Studio\PermScript\frameworks\base\core\java\android\bluetooth\BluetoothAdapter.java:729
+android.bluetooth.BluetoothAdapter.getBondedDevices,BLUETOOTH,C:\Studio\PermScript\frameworks\base\core\java\android\bluetooth\BluetoothAdapter.java:748
+android.bluetooth.BluetoothAdapter.getProfileConnectionState,BLUETOOTH,C:\Studio\PermScript\frameworks\base\core\java\android\bluetooth\BluetoothAdapter.java:794
+android.bluetooth.BluetoothAdapter.listenUsingRfcommOn,BLUETOOTH_ADMIN,C:\Studio\PermScript\frameworks\base\core\java\android\bluetooth\BluetoothAdapter.java:863
+android.bluetooth.BluetoothAdapter.listenUsingRfcommWithServiceRecord,BLUETOOTH,C:\Studio\PermScript\frameworks\base\core\java\android\bluetooth\BluetoothAdapter.java:898
+android.bluetooth.BluetoothAdapter.listenUsingInsecureRfcommWithServiceRecord,BLUETOOTH,C:\Studio\PermScript\frameworks\base\core\java\android\bluetooth\BluetoothAdapter.java:929
+android.bluetooth.BluetoothAdapter.listenUsingEncryptedRfcommWithServiceRecord,BLUETOOTH,C:\Studio\PermScript\frameworks\base\core\java\android\bluetooth\BluetoothAdapter.java:967
+android.bluetooth.BluetoothAdapter.readOutOfBandData,BLUETOOTH,C:\Studio\PermScript\frameworks\base\core\java\android\bluetooth\BluetoothAdapter.java:1108
+android.bluetooth.BluetoothAdapter.changeApplicationBluetoothState,BLUETOOTH,C:\Studio\PermScript\frameworks\base\core\java\android\bluetooth\BluetoothAdapter.java:1218
+android.bluetooth.BluetoothDevice.available,BLUETOOTH,C:\Studio\PermScript\frameworks\base\core\java\android\bluetooth\BluetoothDevice.java:79
+android.bluetooth.BluetoothDevice.getName,BLUETOOTH,C:\Studio\PermScript\frameworks\base\core\java\android\bluetooth\BluetoothDevice.java:560
+android.bluetooth.BluetoothDevice.createBond,BLUETOOTH_ADMIN,C:\Studio\PermScript\frameworks\base\core\java\android\bluetooth\BluetoothDevice.java:627
+android.bluetooth.BluetoothDevice.createBondOutOfBand,BLUETOOTH_ADMIN,C:\Studio\PermScript\frameworks\base\core\java\android\bluetooth\BluetoothDevice.java:653
+android.bluetooth.BluetoothDevice.setDeviceOutOfBandData,BLUETOOTH_ADMIN,C:\Studio\PermScript\frameworks\base\core\java\android\bluetooth\BluetoothDevice.java:673
+android.bluetooth.BluetoothDevice.cancelBondProcess,BLUETOOTH_ADMIN,C:\Studio\PermScript\frameworks\base\core\java\android\bluetooth\BluetoothDevice.java:687
+android.bluetooth.BluetoothDevice.removeBond,BLUETOOTH_ADMIN,C:\Studio\PermScript\frameworks\base\core\java\android\bluetooth\BluetoothDevice.java:704
+android.bluetooth.BluetoothDevice.getBondState,BLUETOOTH,C:\Studio\PermScript\frameworks\base\core\java\android\bluetooth\BluetoothDevice.java:721
+android.bluetooth.BluetoothDevice.getBluetoothClass,BLUETOOTH,C:\Studio\PermScript\frameworks\base\core\java\android\bluetooth\BluetoothDevice.java:734
+android.bluetooth.BluetoothDevice.getTrustState,BLUETOOTH,C:\Studio\PermScript\frameworks\base\core\java\android\bluetooth\BluetoothDevice.java:748
+android.bluetooth.BluetoothDevice.setTrust,BLUETOOTH_ADMIN,C:\Studio\PermScript\frameworks\base\core\java\android\bluetooth\BluetoothDevice.java:763
+android.bluetooth.BluetoothDevice.createRfcommSocket,BLUETOOTH,C:\Studio\PermScript\frameworks\base\core\java\android\bluetooth\BluetoothDevice.java:882
+android.bluetooth.BluetoothDevice.createRfcommSocketToServiceRecord,BLUETOOTH,C:\Studio\PermScript\frameworks\base\core\java\android\bluetooth\BluetoothDevice.java:918
+android.bluetooth.BluetoothDevice.createInsecureRfcommSocketToServiceRecord,BLUETOOTH,C:\Studio\PermScript\frameworks\base\core\java\android\bluetooth\BluetoothDevice.java:951
+android.bluetooth.BluetoothDevice.createInsecureRfcommSocket,BLUETOOTH_ADMIN,C:\Studio\PermScript\frameworks\base\core\java\android\bluetooth\BluetoothDevice.java:970
+android.bluetooth.BluetoothDevice.createScoSocket,BLUETOOTH_ADMIN,C:\Studio\PermScript\frameworks\base\core\java\android\bluetooth\BluetoothDevice.java:985
+android.bluetooth.BluetoothHeadset.connect,BLUETOOTH_ADMIN,C:\Studio\PermScript\frameworks\base\core\java\android\bluetooth\BluetoothHeadset.java:271
+android.bluetooth.BluetoothHeadset.connect,BLUETOOTH,C:\Studio\PermScript\frameworks\base\core\java\android\bluetooth\BluetoothHeadset.java:271
+android.bluetooth.BluetoothHeadset.disconnect,BLUETOOTH_ADMIN,C:\Studio\PermScript\frameworks\base\core\java\android\bluetooth\BluetoothHeadset.java:312
+android.bluetooth.BluetoothHeadset.setPriority,BLUETOOTH_ADMIN,C:\Studio\PermScript\frameworks\base\core\java\android\bluetooth\BluetoothHeadset.java:394
+android.bluetooth.BluetoothHeadset.getPriority,BLUETOOTH,C:\Studio\PermScript\frameworks\base\core\java\android\bluetooth\BluetoothHeadset.java:426
+android.bluetooth.BluetoothHeadset.startVoiceRecognition,BLUETOOTH,C:\Studio\PermScript\frameworks\base\core\java\android\bluetooth\BluetoothHeadset.java:462
+android.bluetooth.BluetoothHeadset.stopVoiceRecognition,BLUETOOTH,C:\Studio\PermScript\frameworks\base\core\java\android\bluetooth\BluetoothHeadset.java:486
+android.bluetooth.BluetoothHeadset.isAudioConnected,BLUETOOTH,C:\Studio\PermScript\frameworks\base\core\java\android\bluetooth\BluetoothHeadset.java:509
+android.bluetooth.BluetoothHealth.registerSinkAppConfiguration,BLUETOOTH,C:\Studio\PermScript\frameworks\base\core\java\android\bluetooth\BluetoothHealth.java:112
+android.bluetooth.BluetoothHealth.registerAppConfiguration,BLUETOOTH,C:\Studio\PermScript\frameworks\base\core\java\android\bluetooth\BluetoothHealth.java:137
+android.bluetooth.BluetoothHealth.unregisterAppConfiguration,BLUETOOTH,C:\Studio\PermScript\frameworks\base\core\java\android\bluetooth\BluetoothHealth.java:169
+android.bluetooth.BluetoothHealth.connectChannelToSource,BLUETOOTH,C:\Studio\PermScript\frameworks\base\core\java\android\bluetooth\BluetoothHealth.java:197
+android.bluetooth.BluetoothHealth.connectChannelToSink,BLUETOOTH,C:\Studio\PermScript\frameworks\base\core\java\android\bluetooth\BluetoothHealth.java:226
+android.bluetooth.BluetoothHealth.disconnectChannel,BLUETOOTH,C:\Studio\PermScript\frameworks\base\core\java\android\bluetooth\BluetoothHealth.java:255
+android.bluetooth.BluetoothHealth.getMainChannelFd,BLUETOOTH,C:\Studio\PermScript\frameworks\base\core\java\android\bluetooth\BluetoothHealth.java:284
+android.bluetooth.BluetoothHealth.getConnectionState,BLUETOOTH,C:\Studio\PermScript\frameworks\base\core\java\android\bluetooth\BluetoothHealth.java:316
+android.bluetooth.BluetoothHealth.getConnectedDevices,BLUETOOTH,C:\Studio\PermScript\frameworks\base\core\java\android\bluetooth\BluetoothHealth.java:344
+android.bluetooth.BluetoothHealth.getDevicesMatchingConnectionStates,BLUETOOTH,C:\Studio\PermScript\frameworks\base\core\java\android\bluetooth\BluetoothHealth.java:376
+android.bluetooth.BluetoothInputDevice.connect,BLUETOOTH_ADMIN,C:\Studio\PermScript\frameworks\base\core\java\android\bluetooth\BluetoothInputDevice.java:140
+android.bluetooth.BluetoothInputDevice.connect,BLUETOOTH,C:\Studio\PermScript\frameworks\base\core\java\android\bluetooth\BluetoothInputDevice.java:140
+android.bluetooth.BluetoothInputDevice.disconnect,BLUETOOTH_ADMIN,C:\Studio\PermScript\frameworks\base\core\java\android\bluetooth\BluetoothInputDevice.java:181
+android.bluetooth.BluetoothInputDevice.setPriority,BLUETOOTH_ADMIN,C:\Studio\PermScript\frameworks\base\core\java\android\bluetooth\BluetoothInputDevice.java:263
+android.bluetooth.BluetoothInputDevice.getPriority,BLUETOOTH,C:\Studio\PermScript\frameworks\base\core\java\android\bluetooth\BluetoothInputDevice.java:295
+android.bluetooth.BluetoothPan.connect,BLUETOOTH_ADMIN,C:\Studio\PermScript\frameworks\base\core\java\android\bluetooth\BluetoothPan.java:159
+android.bluetooth.BluetoothPan.connect,BLUETOOTH,C:\Studio\PermScript\frameworks\base\core\java\android\bluetooth\BluetoothPan.java:159
+android.bluetooth.BluetoothPan.disconnect,BLUETOOTH_ADMIN,C:\Studio\PermScript\frameworks\base\core\java\android\bluetooth\BluetoothPan.java:200
+android.bluetooth.BluetoothProfile.getConnectedDevices,BLUETOOTH,C:\Studio\PermScript\frameworks\base\core\java\android\bluetooth\BluetoothProfile.java:125
+android.bluetooth.BluetoothProfile.getDevicesMatchingConnectionStates,BLUETOOTH,C:\Studio\PermScript\frameworks\base\core\java\android\bluetooth\BluetoothProfile.java:141
+android.bluetooth.BluetoothProfile.getConnectionState,BLUETOOTH,C:\Studio\PermScript\frameworks\base\core\java\android\bluetooth\BluetoothProfile.java:153
+android.content.Context.sendStickyBroadcast,BROADCAST_STICKY,C:\Studio\PermScript\frameworks\base\core\java\android\content\Context.java:1015
+android.content.Context.removeStickyBroadcast,BROADCAST_STICKY,C:\Studio\PermScript\frameworks\base\core\java\android\content\Context.java:1075
+android.net.Proxy.getPreferredHttpHost,ACCESS_NETWORK_STATE,C:\Studio\PermScript\frameworks\base\core\java\android\net\Proxy.java:215
+android.net.TrafficStats.setThreadStatsUid,UPDATE_DEVICE_STATS,C:\Studio\PermScript\frameworks\base\core\java\android\net\TrafficStats.java:135
+android.nfc.NfcAdapter.setNdefPushMessage,NFC,C:\Studio\PermScript\frameworks\base\core\java\android\nfc\NfcAdapter.java:529
+android.nfc.NfcAdapter.setNdefPushMessageCallback,NFC,C:\Studio\PermScript\frameworks\base\core\java\android\nfc\NfcAdapter.java:568
+android.nfc.NfcAdapter.setOnNdefPushCompleteCallback,NFC,C:\Studio\PermScript\frameworks\base\core\java\android\nfc\NfcAdapter.java:597
+android.nfc.NfcAdapter.enableForegroundDispatch,NFC,C:\Studio\PermScript\frameworks\base\core\java\android\nfc\NfcAdapter.java:644
+android.nfc.NfcAdapter.disableForegroundDispatch,NFC,C:\Studio\PermScript\frameworks\base\core\java\android\nfc\NfcAdapter.java:680
+android.nfc.NfcAdapter.enableForegroundNdefPush,NFC,C:\Studio\PermScript\frameworks\base\core\java\android\nfc\NfcAdapter.java:732
+android.nfc.NfcAdapter.disableForegroundNdefPush,NFC,C:\Studio\PermScript\frameworks\base\core\java\android\nfc\NfcAdapter.java:759
+android.nfc.tech.IsoDep.setTimeout,NFC,C:\Studio\PermScript\frameworks\base\core\java\android\nfc\tech\IsoDep.java:91
+android.nfc.tech.IsoDep.getTimeout,NFC,C:\Studio\PermScript\frameworks\base\core\java\android\nfc\tech\IsoDep.java:109
+android.nfc.tech.IsoDep.transceive,NFC,C:\Studio\PermScript\frameworks\base\core\java\android\nfc\tech\IsoDep.java:170
+android.nfc.tech.MifareClassic.authenticateSectorWithKeyA,NFC,C:\Studio\PermScript\frameworks\base\core\java\android\nfc\tech\MifareClassic.java:336
+android.nfc.tech.MifareClassic.authenticateSectorWithKeyB,NFC,C:\Studio\PermScript\frameworks\base\core\java\android\nfc\tech\MifareClassic.java:363
+android.nfc.tech.MifareClassic.readBlock,NFC,C:\Studio\PermScript\frameworks\base\core\java\android\nfc\tech\MifareClassic.java:418
+android.nfc.tech.MifareClassic.writeBlock,NFC,C:\Studio\PermScript\frameworks\base\core\java\android\nfc\tech\MifareClassic.java:440
+android.nfc.tech.MifareClassic.increment,NFC,C:\Studio\PermScript\frameworks\base\core\java\android\nfc\tech\MifareClassic.java:469
+android.nfc.tech.MifareClassic.decrement,NFC,C:\Studio\PermScript\frameworks\base\core\java\android\nfc\tech\MifareClassic.java:497
+android.nfc.tech.MifareClassic.transfer,NFC,C:\Studio\PermScript\frameworks\base\core\java\android\nfc\tech\MifareClassic.java:524
+android.nfc.tech.MifareClassic.restore,NFC,C:\Studio\PermScript\frameworks\base\core\java\android\nfc\tech\MifareClassic.java:546
+android.nfc.tech.MifareClassic.transceive,NFC,C:\Studio\PermScript\frameworks\base\core\java\android\nfc\tech\MifareClassic.java:573
+android.nfc.tech.MifareClassic.setTimeout,NFC,C:\Studio\PermScript\frameworks\base\core\java\android\nfc\tech\MifareClassic.java:599
+android.nfc.tech.MifareClassic.getTimeout,NFC,C:\Studio\PermScript\frameworks\base\core\java\android\nfc\tech\MifareClassic.java:617
+android.nfc.tech.MifareUltralight.readPages,NFC,C:\Studio\PermScript\frameworks\base\core\java\android\nfc\tech\MifareUltralight.java:158
+android.nfc.tech.MifareUltralight.writePage,NFC,C:\Studio\PermScript\frameworks\base\core\java\android\nfc\tech\MifareUltralight.java:183
+android.nfc.tech.MifareUltralight.transceive,NFC,C:\Studio\PermScript\frameworks\base\core\java\android\nfc\tech\MifareUltralight.java:213
+android.nfc.tech.MifareUltralight.setTimeout,NFC,C:\Studio\PermScript\frameworks\base\core\java\android\nfc\tech\MifareUltralight.java:239
+android.nfc.tech.MifareUltralight.getTimeout,NFC,C:\Studio\PermScript\frameworks\base\core\java\android\nfc\tech\MifareUltralight.java:258
+android.nfc.tech.Ndef.isWritable,NFC,C:\Studio\PermScript\frameworks\base\core\java\android\nfc\tech\Ndef.java:238
+android.nfc.tech.Ndef.writeNdefMessage,NFC,C:\Studio\PermScript\frameworks\base\core\java\android\nfc\tech\Ndef.java:300
+android.nfc.tech.Ndef.makeReadOnly,NFC,C:\Studio\PermScript\frameworks\base\core\java\android\nfc\tech\Ndef.java:363
+android.nfc.tech.NdefFormatable.format,NFC,C:\Studio\PermScript\frameworks\base\core\java\android\nfc\tech\NdefFormatable.java:93
+android.nfc.tech.NdefFormatable.formatReadOnly,NFC,C:\Studio\PermScript\frameworks\base\core\java\android\nfc\tech\NdefFormatable.java:115
+android.nfc.tech.NfcA.transceive,NFC,C:\Studio\PermScript\frameworks\base\core\java\android\nfc\tech\NfcA.java:118
+android.nfc.tech.NfcA.setTimeout,NFC,C:\Studio\PermScript\frameworks\base\core\java\android\nfc\tech\NfcA.java:144
+android.nfc.tech.NfcA.getTimeout,NFC,C:\Studio\PermScript\frameworks\base\core\java\android\nfc\tech\NfcA.java:162
+android.nfc.tech.NfcB.transceive,NFC,C:\Studio\PermScript\frameworks\base\core\java\android\nfc\tech\NfcB.java:113
+android.nfc.tech.NfcF.transceive,NFC,C:\Studio\PermScript\frameworks\base\core\java\android\nfc\tech\NfcF.java:117
+android.nfc.tech.NfcF.setTimeout,NFC,C:\Studio\PermScript\frameworks\base\core\java\android\nfc\tech\NfcF.java:143
+android.nfc.tech.NfcF.getTimeout,NFC,C:\Studio\PermScript\frameworks\base\core\java\android\nfc\tech\NfcF.java:161
+android.nfc.tech.NfcV.transceive,NFC,C:\Studio\PermScript\frameworks\base\core\java\android\nfc\tech\NfcV.java:113
+android.nfc.tech.TagTechnology.connect,NFC,C:\Studio\PermScript\frameworks\base\core\java\android\nfc\tech\TagTechnology.java:169
+android.nfc.tech.TagTechnology.reconnect,NFC,C:\Studio\PermScript\frameworks\base\core\java\android\nfc\tech\TagTechnology.java:187
+android.nfc.tech.TagTechnology.close,NFC,C:\Studio\PermScript\frameworks\base\core\java\android\nfc\tech\TagTechnology.java:198
+android.os.PowerManager.Runnable,WAKE_LOCK,C:\Studio\PermScript\frameworks\base\core\java\android\os\PowerManager.java:212
+android.os.PowerManager.reboot,REBOOT,C:\Studio\PermScript\frameworks\base\core\java\android\os\PowerManager.java:529
+android.os.RecoverySystem.installPackage,REBOOT,C:\Studio\PermScript\frameworks\base\core\java\android\os\RecoverySystem.java:320
+android.os.RecoverySystem.rebootWipeUserData,REBOOT,C:\Studio\PermScript\frameworks\base\core\java\android\os\RecoverySystem.java:340
+android.provider.Browser.Uri.parse,WRITE_HISTORY_BOOKMARKS,C:\Studio\PermScript\frameworks\base\core\java\android\provider\Browser.java:43
+android.provider.Browser.Uri.parse,READ_HISTORY_BOOKMARKS,C:\Studio\PermScript\frameworks\base\core\java\android\provider\Browser.java:43
+android.provider.Browser.Uri.parse,WRITE_HISTORY_BOOKMARKS,C:\Studio\PermScript\frameworks\base\core\java\android\provider\Browser.java:124
+android.provider.Browser.Uri.parse,READ_HISTORY_BOOKMARKS,C:\Studio\PermScript\frameworks\base\core\java\android\provider\Browser.java:124
+android.provider.Browser.getAllBookmarks,READ_HISTORY_BOOKMARKS,C:\Studio\PermScript\frameworks\base\core\java\android\provider\Browser.java:235
+android.provider.Browser.getAllVisitedUrls,READ_HISTORY_BOOKMARKS,C:\Studio\PermScript\frameworks\base\core\java\android\provider\Browser.java:250
+android.provider.Browser.updateVisitedHistory,WRITE_HISTORY_BOOKMARKS,C:\Studio\PermScript\frameworks\base\core\java\android\provider\Browser.java:310
+android.provider.Browser.updateVisitedHistory,READ_HISTORY_BOOKMARKS,C:\Studio\PermScript\frameworks\base\core\java\android\provider\Browser.java:310
+android.provider.Browser.getVisitedHistory,READ_HISTORY_BOOKMARKS,C:\Studio\PermScript\frameworks\base\core\java\android\provider\Browser.java:360
+android.provider.Browser.truncateHistory,WRITE_HISTORY_BOOKMARKS,C:\Studio\PermScript\frameworks\base\core\java\android\provider\Browser.java:395
+android.provider.Browser.truncateHistory,READ_HISTORY_BOOKMARKS,C:\Studio\PermScript\frameworks\base\core\java\android\provider\Browser.java:395
+android.provider.Browser.canClearHistory,READ_HISTORY_BOOKMARKS,C:\Studio\PermScript\frameworks\base\core\java\android\provider\Browser.java:428
+android.provider.Browser.clearHistory,WRITE_HISTORY_BOOKMARKS,C:\Studio\PermScript\frameworks\base\core\java\android\provider\Browser.java:450
+android.provider.Browser.deleteHistoryWhere,WRITE_HISTORY_BOOKMARKS,C:\Studio\PermScript\frameworks\base\core\java\android\provider\Browser.java:465
+android.provider.Browser.deleteHistoryWhere,READ_HISTORY_BOOKMARKS,C:\Studio\PermScript\frameworks\base\core\java\android\provider\Browser.java:465
+android.provider.Browser.deleteHistoryTimeFrame,WRITE_HISTORY_BOOKMARKS,C:\Studio\PermScript\frameworks\base\core\java\android\provider\Browser.java:497
+android.provider.Browser.deleteFromHistory,WRITE_HISTORY_BOOKMARKS,C:\Studio\PermScript\frameworks\base\core\java\android\provider\Browser.java:522
+android.provider.Browser.addSearchUrl,WRITE_HISTORY_BOOKMARKS,C:\Studio\PermScript\frameworks\base\core\java\android\provider\Browser.java:534
+android.provider.Browser.addSearchUrl,READ_HISTORY_BOOKMARKS,C:\Studio\PermScript\frameworks\base\core\java\android\provider\Browser.java:534
+android.provider.Browser.clearSearches,WRITE_HISTORY_BOOKMARKS,C:\Studio\PermScript\frameworks\base\core\java\android\provider\Browser.java:547
+android.provider.Browser.requestAllIcons,READ_HISTORY_BOOKMARKS,C:\Studio\PermScript\frameworks\base\core\java\android\provider\Browser.java:568
+android.view.inputmethod.InputMethodManager.Object,BIND_INPUT_METHOD,C:\Studio\PermScript\frameworks\base\core\java\android\view\inputmethod\InputMethodManager.java:198
+android.webkit.WebView.onGlobalLayout,INTERNET,C:\Studio\PermScript\frameworks\base\core\java\android\webkit\WebView.java:326
+android.app.DownloadManagerBaseTest.setWiFiStateOn,ACCESS_WIFI_STATE,C:\Studio\PermScript\frameworks\base\core\tests\coretests\src\android\app\DownloadManagerBaseTest.java:513
+android.app.DownloadManagerBaseTest.setWiFiStateOn,CHANGE_WIFI_STATE,C:\Studio\PermScript\frameworks\base\core\tests\coretests\src\android\app\DownloadManagerBaseTest.java:513
+android.app.DownloadManagerBaseTest.setAirplaneModeOn,WRITE_SETTINGS,C:\Studio\PermScript\frameworks\base\core\tests\coretests\src\android\app\DownloadManagerBaseTest.java:557
+android.os.PowerManagerTest.doTestSetBacklightBrightness,DEVICE_POWER,C:\Studio\PermScript\frameworks\base\core\tests\coretests\src\android\os\PowerManagerTest.java:131
+android.security.KeyChain.choosePrivateKeyAlias,USE_CREDENTIALS,C:\Studio\PermScript\frameworks\base\keystore\java\android\security\KeyChain.java:230
+android.security.KeyChain.getPrivateKey,USE_CREDENTIALS,C:\Studio\PermScript\frameworks\base\keystore\java\android\security\KeyChain.java:287
+android.security.KeyChain.getCertificateChain,USE_CREDENTIALS,C:\Studio\PermScript\frameworks\base\keystore\java\android\security\KeyChain.java:318
+android.media.audiofx.Visualizer.System.loadLibrary,MODIFY_AUDIO_SETTINGS,C:\Studio\PermScript\frameworks\base\media\java\android\media\audiofx\Visualizer.java:64
+android.media.audiofx.Visualizer.System.loadLibrary,RECORD_AUDIO,C:\Studio\PermScript\frameworks\base\media\java\android\media\audiofx\Visualizer.java:64
+android.media.AudioManager.startBluetoothSco,MODIFY_AUDIO_SETTINGS,C:\Studio\PermScript\frameworks\base\media\java\android\media\AudioManager.java:971
+android.media.AudioManager.stopBluetoothSco,MODIFY_AUDIO_SETTINGS,C:\Studio\PermScript\frameworks\base\media\java\android\media\AudioManager.java:989
+android.media.MediaPlayer.setWakeMode,INTERNET,C:\Studio\PermScript\frameworks\base\media\java\android\media\MediaPlayer.java:957
+android.media.MediaPlayer.setWakeMode,WAKE_LOCK,C:\Studio\PermScript\frameworks\base\media\java\android\media\MediaPlayer.java:957
+android.telephony.TelephonyManager.enableLocationUpdates,CONTROL_LOCATION_UPDATES,C:\Studio\PermScript\frameworks\base\telephony\java\android\telephony\TelephonyManager.java:234
+android.telephony.TelephonyManager.disableLocationUpdates,CONTROL_LOCATION_UPDATES,C:\Studio\PermScript\frameworks\base\telephony\java\android\telephony\TelephonyManager.java:251
+android.net.sip.SipAudioCall.SipAudioCall.class.getSimpleName,INTERNET,C:\Studio\PermScript\frameworks\base\voip\java\android\net\sip\SipAudioCall.java:55
+android.net.sip.SipAudioCall.SipAudioCall.class.getSimpleName,MODIFY_AUDIO_SETTINGS,C:\Studio\PermScript\frameworks\base\voip\java\android\net\sip\SipAudioCall.java:55
+android.net.sip.SipAudioCall.SipAudioCall.class.getSimpleName,ACCESS_WIFI_STATE,C:\Studio\PermScript\frameworks\base\voip\java\android\net\sip\SipAudioCall.java:55
+android.net.sip.SipAudioCall.SipAudioCall.class.getSimpleName,USE_SIP,C:\Studio\PermScript\frameworks\base\voip\java\android\net\sip\SipAudioCall.java:55
+android.net.sip.SipAudioCall.SipAudioCall.class.getSimpleName,RECORD_AUDIO,C:\Studio\PermScript\frameworks\base\voip\java\android\net\sip\SipAudioCall.java:55
+android.net.sip.SipAudioCall.SipAudioCall.class.getSimpleName,WAKE_LOCK,C:\Studio\PermScript\frameworks\base\voip\java\android\net\sip\SipAudioCall.java:55
+android.net.sip.SipAudioCall.setSpeakerMode,MODIFY_AUDIO_SETTINGS,C:\Studio\PermScript\frameworks\base\voip\java\android\net\sip\SipAudioCall.java:866
+android.net.sip.SipAudioCall.startAudio,ACCESS_WIFI_STATE,C:\Studio\PermScript\frameworks\base\voip\java\android\net\sip\SipAudioCall.java:981
+android.net.sip.SipAudioCall.startAudio,RECORD_AUDIO,C:\Studio\PermScript\frameworks\base\voip\java\android\net\sip\SipAudioCall.java:981
+android.net.sip.SipAudioCall.startAudio,WAKE_LOCK,C:\Studio\PermScript\frameworks\base\voip\java\android\net\sip\SipAudioCall.java:981
+android.net.wifi.WifiManager.WifiLock,WAKE_LOCK,C:\Studio\PermScript\frameworks\base\wifi\java\android\net\wifi\WifiManager.java:1241
+android.net.ConnectivityManager.setBackgroundDataSetting,CHANGE_BACKGROUND_DATA_SETTING
+android.net.ConnectivityManager.getActiveNetworkInfo,ACCESS_NETWORK_STATE
+android.telephony.SmsManager.sendTextMessage,SEND_SMS
diff --git a/src/plugins/preflighting.core/files/permission-feature-map.xml b/src/plugins/preflighting.core/files/permission-feature-map.xml
new file mode 100644
index 0000000..6d3c133
--- /dev/null
+++ b/src/plugins/preflighting.core/files/permission-feature-map.xml
@@ -0,0 +1,111 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<permission_to_feature_map>
+ <category name="Bluetooth">
+ <permission id="android.permission.BLUETOOTH">
+ <!-- implied features required -->
+ <feature id="android.hardware.bluetooth"></feature>
+ </permission>
+ <permission id="android.permission.BLUETOOTH_ADMIN">
+ <!-- implied features required -->
+ <feature id="android.hardware.bluetooth"></feature>
+ </permission>
+ </category>
+ <category name="Camera">
+ <permission id="android.permission.CAMERA">
+ <!-- implied features required -->
+ <feature id="android.hardware.camera"></feature>
+ <feature id="android.hardware.camera.autofocus"></feature>
+ <feature id="android.hardware.camera.flash"></feature>
+ </permission>
+ </category>
+ <category name="Location">
+ <permission id="android.permission.ACCESS_MOCK_LOCATION">
+ <!-- implied features required -->
+ <feature id="android.hardware.location"></feature>
+ </permission>
+ <permission id="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS">
+ <!-- implied features required -->
+ <feature id="android.hardware.location"></feature>
+ </permission>
+ <permission id="android.permission.INSTALL_LOCATION_PROVIDER">
+ <!-- implied features required -->
+ <feature id="android.hardware.location"></feature>
+ </permission>
+ <permission id="android.permission.ACCESS_COARSE_LOCATION">
+ <!-- implied features required -->
+ <feature id="android.hardware.location.network"></feature>
+ <feature id="android.hardware.location"></feature>
+ </permission>
+ <permission id="android.permission.ACCESS_FINE_LOCATION">
+ <!-- implied features required -->
+ <feature id="android.hardware.location.gps"></feature>
+ <feature id="android.hardware.location"></feature>
+ </permission>
+ </category>
+ <category name="Microphone">
+ <permission id="android.permission.RECORD_AUDIO">
+ <!-- implied features required -->
+ <feature id="android.hardware.microphone"></feature>
+ </permission>
+ </category>
+ <category name="Telephony">
+ <permission id="android.permission.CALL_PHONE">
+ <!-- implied features required -->
+ <feature id="android.hardware.telephony"></feature>
+ </permission>
+ <permission id="android.permission.CALL_PRIVILEGED">
+ <!-- implied features required -->
+ <feature id="android.hardware.telephony"></feature>
+ </permission>
+ <permission id="android.permission.MODIFY_PHONE_STATE">
+ <!-- implied features required -->
+ <feature id="android.hardware.telephony"></feature>
+ </permission>
+ <permission id="android.permission.PROCESS_OUTGOING_CALLS">
+ <!-- implied features required -->
+ <feature id="android.hardware.telephony"></feature>
+ </permission>
+ <permission id="android.permission.READ_SMS">
+ <!-- implied features required -->
+ <feature id="android.hardware.telephony"></feature>
+ </permission>
+ <permission id="android.permission.RECEIVE_SMS">
+ <!-- implied features required -->
+ <feature id="android.hardware.telephony"></feature>
+ </permission>
+ <permission id="android.permission.RECEIVE_MMS">
+ <!-- implied features required -->
+ <feature id="android.hardware.telephony"></feature>
+ </permission>
+ <permission id="android.permission.RECEIVE_WAP_PUSH">
+ <!-- implied features required -->
+ <feature id="android.hardware.telephony"></feature>
+ </permission>
+ <permission id="android.permission.SEND_SMS">
+ <!-- implied features required -->
+ <feature id="android.hardware.telephony"></feature>
+ </permission>
+ <permission id="android.permission.WRITE_APN_SETTINGS">
+ <!-- implied features required -->
+ <feature id="android.hardware.telephony"></feature>
+ </permission>
+ <permission id="android.permission.WRITE_SMS">
+ <!-- implied features required -->
+ <feature id="android.hardware.telephony"></feature>
+ </permission>
+ </category>
+ <category name="Wifi">
+ <permission id="android.permission.ACCESS_WIFI_STATE">
+ <!-- implied features required -->
+ <feature id="android.hardware.wifi"></feature>
+ </permission>
+ <permission id="android.permission.CHANGE_WIFI_STATE">
+ <!-- implied features required -->
+ <feature id="android.hardware.wifi"></feature>
+ </permission>
+ <permission id="android.permission.CHANGE_WIFI_MULTICAST_STATE">
+ <!-- implied features required -->
+ <feature id="android.hardware.wifi"></feature>
+ </permission>
+ </category>
+</permission_to_feature_map>
diff --git a/src/plugins/preflighting.core/files/signature-permission-map.xml b/src/plugins/preflighting.core/files/signature-permission-map.xml
new file mode 100644
index 0000000..43a8022
--- /dev/null
+++ b/src/plugins/preflighting.core/files/signature-permission-map.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+
+ <!-- For future/next sprints -->
+ <!-- For future/next sprints -->
+ <!-- For future/next sprints -->
+
+<signature_permission>
+
+ <signature>
+
+ <package>android.gesture</package>
+ <class>
+ <name>Gesture</name>
+ <since_apilevel>4</since_apilevel>
+ <deprecated>false</deprecated>
+ </class>
+ <class_name>Gesture</class_name>
+ <class_name>Gesture</class_name>
+ <class_name>Gesture</class_name>
+
+
+
+ <since_apilevel>4</since_apilevel>
+ <deprecated>false</deprecated>
+ </signature>
+
+ <signature>
+ <name>android.speech.RecognizerIntent</name>
+ <since_apilevel>3</since_apilevel>
+ <deprecated>false</deprecated>
+ </signature>
+
+ <signature>
+ <name>android.speech.action.GET_LANGUAGE_DETAILS</name>
+ <since_apilevel>3</since_apilevel>
+ <deprecated>false</deprecated>
+ </signature>
+
+
+android.speech.action.GET_LANGUAGE_DETAILS
+
+
+ <!-- ... -->
+ <!-- ... -->
+
+</signature_apilevel> \ No newline at end of file
diff --git a/src/plugins/preflighting.core/lib/log4j-1.2.14.jar b/src/plugins/preflighting.core/lib/log4j-1.2.14.jar
new file mode 100644
index 0000000..6251307
--- /dev/null
+++ b/src/plugins/preflighting.core/lib/log4j-1.2.14.jar
Binary files differ
diff --git a/src/plugins/preflighting.core/plugin.properties b/src/plugins/preflighting.core/plugin.properties
new file mode 100644
index 0000000..86ca457
--- /dev/null
+++ b/src/plugins/preflighting.core/plugin.properties
@@ -0,0 +1,7 @@
+#Properties file for com.motorolamobility.preflighting.core
+Bundle-Vendor = Motorola Mobility, Inc.
+Bundle-Name = MOTODEV Studio App Validator Core
+
+DroidXdeviceSpecification = Droid X Device Specification
+DroidXdeviceSpecificationDesc = Device specification for the Droid X
+
diff --git a/src/plugins/preflighting.core/plugin.xml b/src/plugins/preflighting.core/plugin.xml
new file mode 100644
index 0000000..0846e4b
--- /dev/null
+++ b/src/plugins/preflighting.core/plugin.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?eclipse version="3.4"?>
+<plugin>
+ <extension-point id="com.motorolamobility.preflighting.core.checker" name="Checker" schema="schema/checker.exsd"/>
+
+</plugin>
diff --git a/src/plugins/preflighting.core/resources/plate32.png b/src/plugins/preflighting.core/resources/plate32.png
new file mode 100644
index 0000000..4bff905
--- /dev/null
+++ b/src/plugins/preflighting.core/resources/plate32.png
Binary files differ
diff --git a/src/plugins/preflighting.core/schema/checker.exsd b/src/plugins/preflighting.core/schema/checker.exsd
new file mode 100644
index 0000000..90ee6ab
--- /dev/null
+++ b/src/plugins/preflighting.core/schema/checker.exsd
@@ -0,0 +1,304 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<!-- Schema file written by PDE -->
+<schema targetNamespace="com.motorolamobility.preflighting.core" xmlns="http://www.w3.org/2001/XMLSchema">
+<annotation>
+ <appinfo>
+ <meta.schema plugin="com.motorolamobility.preflighting.core" id="checker" name="Checker"/>
+ </appinfo>
+ <documentation>
+ This extension point provides means to create a checker for the App Validator. Checkers allow you to make verifications in either your Android Project or Android Application Packages (APKs).
+ </documentation>
+ </annotation>
+
+ <element name="extension">
+ <annotation>
+ <appinfo>
+ <meta.element />
+ </appinfo>
+ </annotation>
+ <complexType>
+ <sequence minOccurs="1" maxOccurs="unbounded">
+ <element ref="checker"/>
+ </sequence>
+ <attribute name="point" type="string" use="required">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ </annotation>
+ </attribute>
+ <attribute name="id" type="string">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ </annotation>
+ </attribute>
+ <attribute name="name" type="string">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ <appinfo>
+ <meta.attribute translatable="true"/>
+ </appinfo>
+ </annotation>
+ </attribute>
+ </complexType>
+ </element>
+
+ <element name="checker">
+ <annotation>
+ <documentation>
+ This extension point provides the developer with all the basic structures needed for creating a Checker.
+The checker architecture supposes a hierarchy of conditions lying below every checker, giving meaningful results divided by the conditions class.
+ </documentation>
+ </annotation>
+ <complexType>
+ <sequence minOccurs="0" maxOccurs="unbounded">
+ <element ref="condition"/>
+ <element ref="parameter"/>
+ </sequence>
+ <attribute name="id" type="string" use="required">
+ <annotation>
+ <documentation>
+ This is the Checker Id.
+Don&apos;t use &quot;.&quot; as part of the id since it is used to specify conditions in the command line (checker.condition).
+ </documentation>
+ </annotation>
+ </attribute>
+ <attribute name="name" type="string" use="required">
+ <annotation>
+ <documentation>
+ This is the checker name. It would preferably be a translatable value.
+ </documentation>
+ <appinfo>
+ <meta.attribute translatable="true"/>
+ </appinfo>
+ </annotation>
+ </attribute>
+ <attribute name="description" type="string" use="required">
+ <annotation>
+ <documentation>
+ A short description of your checker.
+ </documentation>
+ <appinfo>
+ <meta.attribute translatable="true"/>
+ </appinfo>
+ </annotation>
+ </attribute>
+ <attribute name="class" type="string">
+ <annotation>
+ <documentation>
+ The class where the checker is implemented. If none is provided, the default class will be used, which is com.motorolamobility.preflighting.core.checker.Checker.
+ In case one wishes to customize a checker, simply implement a new com.motorolamobility.preflighting.core.checker.IChecker
+ as required by this extension-point, or extend the default class. You can implement your own checker class if you need to compute something that is common to more than one condition, for example.
+ </documentation>
+ <appinfo>
+ <meta.attribute kind="java" basedOn="com.motorolamobility.preflighting.core.checker.Checker:com.motorolamobility.preflighting.core.checker.IChecker"/>
+ </appinfo>
+ </annotation>
+ </attribute>
+ </complexType>
+ </element>
+
+ <element name="condition">
+ <annotation>
+ <documentation>
+ This is the extension point which provides all the necessary interfaces to the developer in order to create a checker condition, a prerequisite for the checker archtecture.
+ </documentation>
+ </annotation>
+ <complexType>
+ <attribute name="id" type="string" use="required">
+ <annotation>
+ <documentation>
+ The condition ID.
+ </documentation>
+ </annotation>
+ </attribute>
+ <attribute name="name" type="string" use="required">
+ <annotation>
+ <documentation>
+ The condition name.
+ </documentation>
+ <appinfo>
+ <meta.attribute translatable="true"/>
+ </appinfo>
+ </annotation>
+ </attribute>
+ <attribute name="description" type="string" use="required">
+ <annotation>
+ <documentation>
+ A short description about the condition.
+ </documentation>
+ <appinfo>
+ <meta.attribute translatable="true"/>
+ </appinfo>
+ </annotation>
+ </attribute>
+ <attribute name="defaultSeverityLevel" use="required">
+ <annotation>
+ <documentation>
+ The default severity level of your condition. There are 4 valid values for this field:
+
+- WARNING: Will rise a warning;
+- ERROR: Wil rise a non-critical error;
+- FATAL: will rise a fatal error, usually employed to denote a critical failure.
+ </documentation>
+ </annotation>
+ <simpleType>
+ <restriction base="string">
+ <enumeration value="WARNING">
+ </enumeration>
+ <enumeration value="ERROR">
+ </enumeration>
+ <enumeration value="FATAL">
+ </enumeration>
+ </restriction>
+ </simpleType>
+ </attribute>
+ <attribute name="class" type="string" use="required">
+ <annotation>
+ <documentation>
+ The class where the condition is implemented. The actual application verification should be done here, and not in the Checker classes.
+ There is a default implementation of the Condition at com.motorolamobility.preflighting.core.checker.condition.Condition. It does some
+ basic tasks such as holding elements of the Condition (Id, Description and so on...). So, when defining the class which implements this extention-point,
+ one could also make this class extend com.motorolamobility.preflighting.core.checker.condition.Condition. Thus, one only has to implement the abstract methods
+ of the default class com.motorolamobility.preflighting.core.checker.condition.Condition which actually do the work.
+ </documentation>
+ <appinfo>
+ <meta.attribute kind="java" basedOn="com.motorolamobility.preflighting.core.checker.condition.Condition:com.motorolamobility.preflighting.core.checker.condition.ICondition"/>
+ </appinfo>
+ </annotation>
+ </attribute>
+ <attribute name="markerType" type="string">
+ <annotation>
+ <documentation>
+ The marker type is used by problems view and editor to distinguish the markers. If the condition being defined provides a quick fix, then it should set its own markerType. Otherwise, use the default value.
+ </documentation>
+ </annotation>
+ </attribute>
+ </complexType>
+ </element>
+
+ <element name="parameter">
+ <annotation>
+ <documentation>
+ This is the extension point which provides a parameter to a checker
+ </documentation>
+ </annotation>
+ <complexType>
+ <attribute name="id" type="string" use="required">
+ <annotation>
+ <documentation>
+ The checker parameter ID.
+ </documentation>
+ </annotation>
+ </attribute>
+ <attribute name="name" type="string" use="required">
+ <annotation>
+ <documentation>
+ The checker parameter name.
+ </documentation>
+ <appinfo>
+ <meta.attribute translatable="true"/>
+ </appinfo>
+ </annotation>
+ </attribute>
+ <attribute name="description" type="string" use="required">
+ <annotation>
+ <documentation>
+ A short description about the chekcer parameter.
+ </documentation>
+ <appinfo>
+ <meta.attribute translatable="true"/>
+ </appinfo>
+ </annotation>
+ </attribute>
+ <attribute name="valueDescription" type="string" use="required">
+ <annotation>
+ <documentation>
+ A short value-description about the chekcer parameter.
+ </documentation>
+ <appinfo>
+ <meta.attribute translatable="true"/>
+ </appinfo>
+ </annotation>
+ </attribute>
+ <attribute name="isMandatory" type="boolean" use="required">
+ <annotation>
+ <documentation>
+ This flag determines whether this checker parameter is mandatory.
+ </documentation>
+ </annotation>
+ </attribute>
+ <attribute name="type" use="required">
+ <annotation>
+ <documentation>
+ This fields defines the types of this parameter.
+ </documentation>
+ </annotation>
+ <simpleType>
+ <restriction base="string">
+ <enumeration value="STRING">
+ </enumeration>
+ <enumeration value="BOOLEAN">
+ </enumeration>
+ <enumeration value="INTEGER">
+ </enumeration>
+ </restriction>
+ </simpleType>
+ </attribute>
+ </complexType>
+ </element>
+
+ <annotation>
+ <appinfo>
+ <meta.section type="since"/>
+ </appinfo>
+ <documentation>
+ 1.0.0
+ </documentation>
+ </annotation>
+
+ <annotation>
+ <appinfo>
+ <meta.section type="examples"/>
+ </appinfo>
+ <documentation>
+ &lt;p&gt;The plugin &lt;code&gt;preflighting.samplechecker.androidlabel&lt;/code&gt; distributed in the App Validator SDK contains an example on how to implement a Condition that accepts a parameter.&lt;/p&gt;
+
+&lt;p&gt;The plugin &lt;code&gt;preflighting.samplecheckers.findviewbyid&lt;/code&gt; shows how to implement a Condition available only for projects (not for APKs). It also serves as an example about how to analyse Android (Java code) by using App Validator SDK framework.&lt;/p&gt;
+ </documentation>
+ </annotation>
+
+ <annotation>
+ <appinfo>
+ <meta.section type="apiinfo"/>
+ </appinfo>
+ <documentation>
+ You can access App Validator SDK API, by browsing the javadoc of the most important classes in the framework &lt;code&gt;Checker&lt;/code&gt;, &lt;code&gt;Condition&lt;/code&gt;, &lt;code&gt;ApplicationData&lt;/code&gt;, &lt;code&gt; CanExecuteConditionStatus&lt;/code&gt;, &lt;code&gt;PlatformRules&lt;/code&gt;, &lt;/code&gt;ValidationResult&lt;/code&gt; .
+ </documentation>
+ </annotation>
+
+ <annotation>
+ <appinfo>
+ <meta.section type="implementation"/>
+ </appinfo>
+ <documentation>
+ &lt;p&gt;&lt;code&gt;Checker&lt;/code&gt; is an utility class available in the App Validator SDK that implements &lt;code&gt;IChecker&lt;/code&gt;. If checker does not contain any special parameter validation, developers may indicate com.motorolamobility.preflighting.core.checker in the extension point.&lt;/p&gt;
+
+&lt;p&gt;&lt;code&gt;Condition&lt;/code&gt; is an utility class available in the App Validator SDK that implements &lt;code&gt;ICondition&lt;/code&gt;. Developers may use &lt;code&gt;Condition&lt;/code&gt; as a base class to inherit the most common operations for code analysis.&lt;/p&gt;
+ </documentation>
+ </annotation>
+
+ <annotation>
+ <appinfo>
+ <meta.section type="copyright"/>
+ </appinfo>
+ <documentation>
+ Copyright (C) 2012 The Android Open Source Project
+ </documentation>
+ </annotation>
+
+</schema>
diff --git a/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/IParameterProcessor.java b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/IParameterProcessor.java
new file mode 100644
index 0000000..1861baa
--- /dev/null
+++ b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/IParameterProcessor.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.preflighting.core;
+
+import java.util.List;
+
+import org.eclipse.core.runtime.IStatus;
+
+import com.motorolamobility.preflighting.core.validation.Parameter;
+import com.motorolamobility.preflighting.core.validation.ParameterDescription;
+
+/**
+ * Interface for parameter processors. Implementers of this interface should validate parameters passed to App Validator.
+ */
+public interface IParameterProcessor
+{
+
+ /**
+ * Returns the definitions for checker input parameters.
+ * List of flagName, type (bool, string, integer,etc) and default value
+ */
+ public List<ParameterDescription> getParameterDescriptions();
+
+ /**
+ * Validates input parameters. In the case of a Checker also keep these parameters on an internal structure.
+ */
+ public IStatus validateInputParams(List<Parameter> parameters);
+
+} \ No newline at end of file
diff --git a/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/PreflightingCorePlugin.java b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/PreflightingCorePlugin.java
new file mode 100644
index 0000000..2ad40c6
--- /dev/null
+++ b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/PreflightingCorePlugin.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.preflighting.core;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+
+import com.motorolamobility.preflighting.core.logging.PreflightingLogger;
+
+/**
+ * This is the PLugin's Activator. It is responsible for initializing
+ * this plug-in.
+ */
+public class PreflightingCorePlugin implements BundleActivator
+{
+ /**
+ * This is the unique identifier of this plugin.
+ */
+ public static final String PLUGIN_ID = "com.motorolamobility.preflighting.core"; //$NON-NLS-1$
+
+ private static BundleContext context;
+
+ private static PreflightingCorePlugin plugin;
+
+ private static Set<String> availableMarkers;
+
+ /**
+ * Default constructor.
+ */
+ public PreflightingCorePlugin()
+ {
+ plugin = this;
+ availableMarkers = new HashSet<String>();
+ }
+
+ /**
+ * Retrieve the {@link BundleContext} of this plug-in.
+ *
+ * @return Returns the {@link BundleContext} of this plug-in.
+ */
+ public static BundleContext getContext()
+ {
+ return context;
+ }
+
+ /**
+ * Retrieves an instance of this plug-in.
+ *
+ * @return Returns an instance of this plug-in.
+ */
+ public static PreflightingCorePlugin getInstance()
+ {
+ return plugin;
+ }
+
+ /**
+ * Start this plug-in.
+ *
+ * @param bundleContext {@link BundleContext} to be used with
+ * this plug-in.
+ *
+ * @throws Exception Exception thrown when there is an error
+ * starting this plug-in.
+ *
+ * @see org.osgi.framework.BundleActivator#start(org.osgi.framework.BundleContext)
+ */
+ public void start(BundleContext bundleContext) throws Exception
+ {
+ PreflightingCorePlugin.context = bundleContext;
+ PreflightingLogger.debug(PreflightingCorePlugin.class,
+ "Starting Preflighting Core Plugin...");
+
+ PreflightingLogger.debug(PreflightingCorePlugin.class,
+ "Preflighting Core Plugin started...");
+ }
+
+ /**
+ * Stop this plug-in.
+ *
+ * @param bundleContext {@link BundleContext} used with this plug-in.
+ *
+ * @throws Exception Exception thrown when there is an error stopping this plug-in.
+ *
+ * @see org.osgi.framework.BundleActivator#stop(org.osgi.framework.BundleContext)
+ */
+ public void stop(BundleContext bundleContext) throws Exception
+ {
+ PreflightingCorePlugin.context = null;
+ }
+
+ public static boolean addAvailableMarker(String markerType)
+ {
+ return PreflightingCorePlugin.availableMarkers.add(markerType);
+ }
+
+ public static Set<String> getAvailableMarkers()
+ {
+ return PreflightingCorePlugin.availableMarkers;
+ }
+} \ No newline at end of file
diff --git a/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/applicationdata/ApplicationData.java b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/applicationdata/ApplicationData.java
new file mode 100644
index 0000000..7e3095f
--- /dev/null
+++ b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/applicationdata/ApplicationData.java
@@ -0,0 +1,472 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.preflighting.core.applicationdata;
+
+import java.security.cert.Certificate;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Locale;
+import java.util.Set;
+
+import org.eclipse.jdt.core.dom.CompilationUnit;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import com.motorolamobility.preflighting.core.applicationdata.Element.Type;
+import com.motorolamobility.preflighting.core.exception.PreflightingToolException;
+import com.motorolamobility.preflighting.core.internal.utils.ProjectUtils;
+import com.motorolamobility.preflighting.core.internal.utils.StringUsageIdentifier;
+import com.motorolamobility.preflighting.core.source.model.SourceFileElement;
+import com.motorolamobility.preflighting.core.validation.Parameter;
+
+/**
+ * This Class represents the Application either in project or APK format that
+ * will be analyzed by the tool.
+ *
+ * It has a root node attribute that points to the root Element of the
+ * Application. From that node is possible to navigate through the Application
+ * tree.
+ *
+ */
+public class ApplicationData
+{
+ private Element rootElement;
+
+ private String rootElementPath;
+
+ private int appVer;
+
+ private String appName;
+
+ private String applicationPath;
+
+ private XMLElement manifestElement;
+
+ private List<XMLElement> layoutElements = new java.util.ArrayList<XMLElement>();
+
+ private List<XMLElement> stringElements = new java.util.ArrayList<XMLElement>();
+
+ private List<XMLElement> xmlElements = new java.util.ArrayList<XMLElement>();
+
+ private boolean isProject = false;
+
+ private List<SourceFolderElement> sourceFolderElements =
+ new java.util.ArrayList<SourceFolderElement>();
+
+ private List<Certificate> certificateChain;
+
+ /**
+ * Construct a new ApplicationData with the given parameters.
+ *
+ * @param globalParams the list of global parameters.
+ * @throws PreflightingToolException
+ * In case the applcationPath does not point to a APK or valid
+ * Android Project.
+ */
+ public ApplicationData(List<Parameter> globalParams) throws PreflightingToolException
+ {
+ ProjectUtils.populateAplicationData(globalParams, this);
+ initialize();
+ }
+
+ /***
+ * Initialize the class properties by reading the Application tree
+ *
+ */
+ public void initialize()
+ {
+ // Manifest Element
+ List<Element> manifestList =
+ ElementUtils.getElementByType(rootElement, Element.Type.FILE_MANIFEST);
+ if (manifestList.size() > 0)
+ {
+ if (manifestList.get(0) instanceof XMLElement)
+ {
+ manifestElement = (XMLElement) manifestList.get(0);
+ NodeList manifestByTag =
+ manifestElement.getDocument().getElementsByTagName("manifest");
+
+ Node manifest =
+ (manifestByTag != null) && (manifestByTag.getLength() > 0) ? manifestByTag
+ .item(0) : null;
+ Node versionCode = null;
+ if (manifest != null)
+ {
+ versionCode = manifest.getAttributes().getNamedItem("android:versionCode");
+ }
+
+ try
+ {
+ appVer = Integer.parseInt(versionCode.getTextContent());
+ }
+ catch (Exception e)
+ {
+ appVer = 0;
+ }
+ }
+ }
+
+ // Layout Elements
+ List<Element> layoutsList =
+ ElementUtils.getElementByType(rootElement, Element.Type.FILE_LAYOUT);
+
+ if (layoutsList != null)
+ {
+ for (Element element : layoutsList)
+ {
+ if (element instanceof XMLElement)
+ {
+ layoutElements.add((XMLElement) element);
+ }
+ }
+ }
+
+ // String Elements
+ List<Element> stringsList =
+ ElementUtils.getElementByType(rootElement, Element.Type.FILE_STRINGS);
+ if (stringsList != null)
+ {
+ for (Element element : stringsList)
+ {
+ if (element instanceof XMLElement)
+ {
+ stringElements.add((XMLElement) element);
+ }
+ }
+ }
+
+ //Source Element
+ List<Element> srcFolders =
+ ElementUtils.getElementByType(rootElement, Element.Type.FOLDER_SRC);
+ for (Element srcFolder : srcFolders)
+ {
+ if (srcFolder instanceof SourceFolderElement)
+ {
+ sourceFolderElements.add((SourceFolderElement) srcFolder);
+ }
+ }
+
+ xmlElements = ElementUtils.getXMLElements(rootElement);
+ }
+
+ /**
+ * Return the Application root element.
+ * @return The Element which represents the Application root element.
+ */
+ public Element getRootElement()
+ {
+ return rootElement;
+ }
+
+ /**
+ * Set the ApplicationData to a given Element.
+ * @param rootElement The Element.
+ */
+ public void setRootElement(Element rootElement)
+ {
+ this.rootElement = rootElement;
+ }
+
+ /**
+ * Return the Application root element path.
+ * @return A string with the Application root element path.
+ */
+ public String getRootElementPath()
+ {
+ return rootElementPath;
+ }
+
+ /**
+ * Set the Application root element path.
+ * @param rootElementPath The element path.
+ */
+ public void setRootElementPath(String rootElementPath)
+ {
+ this.rootElementPath = rootElementPath;
+ }
+
+ /**
+ * Return the Element which corresponds to the Manifest file.
+ * @return A XML Element of the Manifest file.
+ */
+ public XMLElement getManifestElement()
+ {
+ return manifestElement;
+ }
+
+ /**
+ * Return the Element list which corresponds to the Layout files.
+ * @return A list of Elements of the layout files.
+ */
+ public List<XMLElement> getLayoutElements()
+ {
+ return layoutElements;
+ }
+
+ /**
+ * Return the Element list which corresponds to the Localization String files.
+ * @return A list of Elements of the Localization String files.
+ */
+ public List<XMLElement> getStringElements()
+ {
+ return stringElements;
+ }
+
+ public List<XMLElement> getXMLElements()
+ {
+ return xmlElements;
+ }
+
+ /**
+ * Return wheter the ApplicationData belongs to a project.
+ * @return A Boolean stating whether it is a project or not.
+ */
+ public boolean isProject()
+ {
+ return isProject;
+ }
+
+ /**
+ * Set whether the ApplicationData belongs to a project or not.
+ * @param isProject A Boolean stating whether it is a project or not.
+ */
+ public void setIsProject(boolean isProject)
+ {
+ this.isProject = isProject;
+ }
+
+ /**
+ * Return the list of certificates.
+ * @return A list of certificates, empty if the application (project or APK) does not have any certificates.
+ */
+ public List<Certificate> getCertificateChain()
+ {
+ return certificateChain;
+ }
+
+ /**
+ * Sets the Certificate Chain of the Application.
+ * @param certificateChain a list of certificates.
+ */
+ public void setCertificateChain(List<Certificate> certificateChain)
+ {
+ this.certificateChain = certificateChain;
+ }
+
+ /**
+ * Cleans all the data from the ApplicationData.
+ */
+ public void clean()
+ {
+ if (this.rootElement != null)
+ {
+ this.rootElement.clean();
+ }
+ this.rootElement = null;
+ if (this.manifestElement != null)
+ {
+ this.manifestElement.clean();
+ }
+ this.manifestElement = null;
+ if (this.layoutElements != null)
+ {
+ for (XMLElement elem : this.layoutElements)
+ {
+ elem.clean();
+ }
+ this.layoutElements.clear();
+ }
+ this.layoutElements = null;
+ if (this.stringElements != null)
+ {
+ for (XMLElement elem : this.stringElements)
+ {
+ elem.clean();
+ }
+ this.stringElements.clear();
+ }
+ this.stringElements = null;
+ if (this.certificateChain != null)
+ {
+ this.certificateChain.clear();
+ }
+ this.certificateChain = null;
+ if (this.sourceFolderElements != null)
+ {
+ this.sourceFolderElements.clear();
+ }
+ }
+
+ /**
+ * Return the Application Version.
+ * @return An Integer representing the application version.
+ */
+ public int getVersion()
+ {
+ return appVer;
+ }
+
+ /**
+ * Set the Application name.
+ * @param appName A string with the application name.
+ */
+ public void setName(String appName)
+ {
+ this.appName = appName;
+ }
+
+ /**
+ * Return the Application name.
+ * @return A string with the application name.
+ */
+ public String getName()
+ {
+ return appName;
+ }
+
+ /**
+ * Set the Application path.
+ * @param applicationPath A string with the application path.
+ */
+ public void setApplicationPath(String applicationPath)
+ {
+ this.applicationPath = applicationPath;
+ }
+
+ /**
+ * Return the Application path.
+ * @return A string with the application path.
+ */
+ public String getApplicationPath()
+ {
+ return applicationPath;
+ }
+
+ /**
+ * Return a {@link SourceFolderElement} list which corresponds to the Java Model.
+ * @return A {@link SourceFolderElement} list which corresponds to the Java Model.
+ */
+ public List<SourceFolderElement> getJavaModel()
+ {
+ return sourceFolderElements;
+ }
+
+ /**
+ *
+ * Set a {@link SourceFolderElement} list which corresponds to the Java Model.
+ * @param javaModel A {@link SourceFolderElement} list which corresponds to the Java Model.
+ */
+ public void setJavaModel(List<SourceFolderElement> javaModel)
+ {
+ this.sourceFolderElements = javaModel;
+ }
+
+ /**
+ * Returns project list of {@link CompilationUnit} to enable checkers/conditions that are project specific (not available for APK verifications).
+ * @return The list of {@link CompilationUnit} objects.
+ */
+ public List<CompilationUnit> getProjectCompilationUnits()
+ {
+ List<CompilationUnit> projectCompilationUnits = new ArrayList<CompilationUnit>();
+ if (this.sourceFolderElements != null)
+ {
+ for (SourceFolderElement folder : this.sourceFolderElements)
+ {
+ for (SourceFileElement file : folder.getSourceFileElements())
+ {
+ projectCompilationUnits.add(file.getCompilationUnit());
+ }
+ }
+ }
+ return projectCompilationUnits;
+ }
+
+ /**
+ * Gets the set of strings used by the app (identified by string ID, not including "R.string")
+ * @return set with the used string in the XML files
+ */
+ private Set<String> getUsedStringsInXMLFiles()
+ {
+ return StringUsageIdentifier.identifyStringsUsed(xmlElements);
+ }
+
+ /**
+ * Gets the set of strings used by the app (identified by string ID, not including "R.string")
+ * @return set with the used strings in the Java files
+ */
+ private Set<String> getUsedStringsInJavaFiles()
+ {
+ Set<String> usedStringsInJava = new HashSet<String>();
+ if (this.sourceFolderElements != null)
+ {
+ for (SourceFolderElement folder : this.sourceFolderElements)
+ {
+ usedStringsInJava.addAll(folder.getUsedStringConstants());
+ }
+ }
+ return usedStringsInJava;
+ }
+
+ /**
+ * Gets the set of used strings (they contain only the id of the string, that is, it does not include R.string)
+ * @return the complete set of strings used throughout the application (either in Java or XML files)
+ */
+ public Set<String> getUsedStringsInApplication()
+ {
+ Set<String> usedStringsInApplication = new HashSet<String>();
+ usedStringsInApplication.addAll(getUsedStringsInXMLFiles());
+ usedStringsInApplication.addAll(getUsedStringsInJavaFiles());
+ return usedStringsInApplication;
+ }
+
+ /**
+ * Gets the set of declared strings (based on default locale string keys merged with other locale string keys)
+ * WARNING: Before calling this method, use {@link Condition#canExecute(ApplicationData, List)} to verify if there is a res folder in the application
+ * @return the set of strings declared in the app's resource XML files
+ */
+ public Set<String> getDeclaredStringsInResourceFiles()
+ {
+ Set<String> declaredStrings = new HashSet<String>();
+ List<Element> folderResElements =
+ ElementUtils.getElementByType(this.getRootElement(), Type.FOLDER_RES);
+
+ ResourcesFolderElement resFolder =
+ folderResElements.size() > 0 ? (ResourcesFolderElement) folderResElements.get(0)
+ : null;
+
+ if (resFolder != null)
+ {
+ StringsElement stringsKeysDefault = resFolder.getDefaultValuesElement();
+
+ if ((stringsKeysDefault != null) && (stringsKeysDefault.getKeyList() != null))
+ {
+ //adding keys from default locale
+ declaredStrings.addAll(stringsKeysDefault.getKeyList());
+ }
+ for (Locale l : resFolder.getAvailableLocales())
+ {
+ StringsElement stringsKeysLocale = resFolder.getValuesElement(l);
+ if ((stringsKeysLocale != null) && (stringsKeysLocale.getKeyList() != null))
+ {
+ //adding keys from non-default locales
+ declaredStrings.addAll(stringsKeysLocale.getKeyList());
+ }
+ }
+ }
+ return declaredStrings;
+ }
+}
diff --git a/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/applicationdata/Element.java b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/applicationdata/Element.java
new file mode 100644
index 0000000..496818c
--- /dev/null
+++ b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/applicationdata/Element.java
@@ -0,0 +1,188 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.preflighting.core.applicationdata;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+/***
+ * Base element of the Application Data tree. This class represents a node of the tree.
+ * <br>
+ * Note: For specialized element types see subclasses of this class.
+ */
+public class Element
+{
+ /**
+ * Enumerator for available elements inside Android projects.
+ * <br><br>
+ * The project tree starts at {@link Element.Type#ROOT}
+ * <br>
+ * Inside root, there are folders e.g.: {@link Element.Type#FOLDER_RES} or {@link Element.Type#FOLDER_DRAWABLE}.
+ * <br>
+ * Inside folders there are files e.g.: {@link Element.Type#FILE_JAVA} or {@link Element.Type#FILE_LAYOUT}
+ *
+ */
+ public enum Type
+ {
+ ROOT, FOLDER_SRC, FOLDER_RES, FOLDER_LAYOUT, FOLDER_VALUES, FOLDER_DRAWABLE, FOLDER_LIB,
+ FOLDER_UNKNOWN, FILE_DRAWABLE, FILE_LAYOUT, FILE_XML, FILE_STRINGS, FILE_MANIFEST,
+ FILE_UNKNOWN, FILE_JAVA
+ };
+
+ // Class Properties
+ private String name;
+
+ private Element parent;
+
+ private Type type;
+
+ private File file;
+
+ private List<Element> children;
+
+ /***
+ * The Constructor.
+ *
+ * @param name
+ * @param parent
+ * @param type
+ */
+ public Element(String name, Element parent, Type type)
+ {
+ this.name = name;
+ this.parent = parent;
+ this.type = type;
+ children = new ArrayList<Element>();
+ }
+
+ /**
+ * Adds a child to the Element.
+ * @param childElement
+ */
+ public void addChild(Element childElement)
+ {
+ children.add(childElement);
+ }
+
+ /**
+ * Removes a child from the Element.
+ * @param childElement
+ */
+ public void removeChild(Element childElement)
+ {
+ children.remove(childElement);
+ }
+
+ /**
+ * Returns the file which corresponds to the Element.
+ * @return file which corresponds to the Element.
+ */
+ public File getFile()
+ {
+ return file;
+ }
+
+ /**
+ * Returns the type which corresponds to this Element.
+ * @return Type which corresponds to the Element.
+ */
+ public Type getType()
+ {
+ return type;
+ }
+
+ /**
+ * Sets the type which corresponds to this Element.
+ * @return Type which corresponds to the Element.
+ */
+ public void setType(Type type)
+ {
+ this.type = type;
+ }
+
+ public List<Element> getChildren()
+ {
+ return children;
+ }
+
+ public void setChildren(List<Element> children)
+ {
+ this.children = children;
+ }
+
+ /**
+ * Returns the name of the Element.
+ * @return String with the name of the Element.
+ */
+ public String getName()
+ {
+ return name;
+ }
+
+ /**
+ * Sets the name of the Element.
+ * @param name String with the name.
+ */
+ public void setName(String name)
+ {
+ this.name = name;
+ }
+
+ /**
+ * Returns the parent element of this Element.
+ * @return an Element which corresponds to the parent of this Element.
+ */
+ public Element getParent()
+ {
+ return parent;
+ }
+
+ /**
+ * Sets a parent Element to this Element.
+ * @param parent The element which will be the parent of this Element.
+ */
+ public void setParent(Element parent)
+ {
+ this.parent = parent;
+ }
+
+ /**
+ * Sets the file of the Element.
+ * @param file the File of the Element.
+ */
+ public void setFile(File file)
+ {
+ this.file = file;
+ }
+
+ /**
+ * Cleans this Element removing all of its children.
+ * @param file the File of the Element.
+ */
+ public void clean()
+ {
+ if (this.children != null)
+ {
+ for (Element child : this.children)
+ {
+ child.clean();
+ }
+ }
+ this.children = null;
+ }
+
+}
diff --git a/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/applicationdata/ElementUtils.java b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/applicationdata/ElementUtils.java
new file mode 100644
index 0000000..739a8df
--- /dev/null
+++ b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/applicationdata/ElementUtils.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.preflighting.core.applicationdata;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Utility class to search for {@link Element} inside an Android project.
+ */
+public class ElementUtils
+{
+ /***
+ * Returns a list of application elements for a specific type.
+ *
+ * @param rootElement the root node of the Android project tree.
+ * @param type used to filter the Elements that will be returned. (e.g.: {@link Element.Type#FILE_JAVA}, {@link Element.Type#FILE_LAYOUT} or {@link Element.Type#FILE_DRAWABLE}
+ * @return the list of {@link Element} inside Android project that have the type specified.
+ */
+ public static List<Element> getElementByType(Element rootElement, Element.Type type)
+ {
+ List<Element> resultList = new ArrayList<Element>();
+ for (Element element : rootElement.getChildren())
+ {
+ if (element.getType() == type)
+ {
+ resultList.add(element);
+ }
+ else if (element instanceof FolderElement)
+ {
+ resultList.addAll(getElementByType(element, type));
+ }
+ }
+ return resultList;
+ }
+
+ /**
+ * Gets all XML elements that are children (either directly or indirectly) of the root element
+ * @param rootElement
+ * @return list of elements that are XML files (ending with .xml)
+ */
+ public static List<XMLElement> getXMLElements(Element rootElement)
+ {
+ List<XMLElement> resultList = new ArrayList<XMLElement>();
+ if (rootElement != null)
+ {
+ for (Element element : rootElement.getChildren())
+ {
+ if ((element instanceof XMLElement) && (element.getFile() != null)
+ && element.getFile().getName().endsWith(".xml"))
+ {
+ resultList.add((XMLElement) element);
+ }
+ else if (element instanceof FolderElement)
+ {
+ resultList.addAll(getXMLElements(element));
+ }
+ }
+ }
+ return resultList;
+ }
+
+ /**
+ * Cleans the given root Element recursively cleaning and removing all of its children.
+ * @param rootElement the root node to be clean.
+ */
+ public static void clean(Element rootElement)
+ {
+ rootElement.clean();
+ }
+}
diff --git a/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/applicationdata/FolderElement.java b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/applicationdata/FolderElement.java
new file mode 100644
index 0000000..08616db
--- /dev/null
+++ b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/applicationdata/FolderElement.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.preflighting.core.applicationdata;
+
+import java.io.File;
+
+/***
+ * Represents a folder of an application.
+ */
+public class FolderElement extends Element
+{
+ /***
+ * Class constructor
+ *
+ * @param folder File representing the folder location.
+ * @param parent Parent Element.
+ * @param type The type of the folder.
+ */
+ public FolderElement(File folder, Element parent, Type type)
+ {
+ super(folder.getName(), parent, type);
+ setFile(folder);
+ }
+
+ /***
+ * Verifies if the folder represented by this class contains a specified file.
+ */
+ public boolean containsFile(String filename)
+ {
+ boolean result = false;
+ for (Element element : this.getChildren())
+ {
+ if (element.getName().equals(filename))
+ {
+ result = true;
+ break;
+ }
+ }
+ return result;
+ }
+
+ @Override
+ public void clean()
+ {
+ super.clean();
+ }
+}
diff --git a/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/applicationdata/ResourcesFolderElement.java b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/applicationdata/ResourcesFolderElement.java
new file mode 100644
index 0000000..4e7dc9c
--- /dev/null
+++ b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/applicationdata/ResourcesFolderElement.java
@@ -0,0 +1,377 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.preflighting.core.applicationdata;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+
+/***
+ * This class is intended to be used to represent the resources folder (\res) of an application
+ */
+public class ResourcesFolderElement extends FolderElement
+{
+ private static final String VALUES = "values";
+
+ private static final String STRINGS_XML = "strings.xml";
+
+ /**
+ * Represents the Android's Project "drawable" folder.
+ */
+ public static final String DRAWABLE_FOLDER = "drawable";
+
+ /**
+ * Represents the Android's Project "drawable-ldpi" folder.
+ */
+ public static final String LDPI_DRAWABLE_FOLDER = "drawable-ldpi";
+
+ /**
+ * Represents the Android's Project "drawable-mdpi" folder.
+ */
+ public static final String MDPI_DRAWABLE_FOLDER = "drawable-mdpi";
+
+ /**
+ * Represents the Android's Project "drawable-hdpi" folder.
+ */
+ public static final String HDPI_DRAWABLE_FOLDER = "drawable-hdpi";
+
+ /**
+ * Represents the Android's Project "drawable-xhdpi" folder.
+ */
+ public static final String XHDPI_DRAWABLE_FOLDER = "drawable-xhdpi";
+
+ private List<Locale> localeList = null;
+
+ /***
+ * Class constructor that always call superclass constructor using type {@link Element.Type#FOLDER_RES}.
+ *
+ * @param name Folder File representing the folder.
+ * @param parent Parent element.
+ */
+ public ResourcesFolderElement(File folder, Element parent)
+ {
+ super(folder, parent, Type.FOLDER_RES);
+ }
+
+ /* Drawable methods */
+ /***
+ * Gets all drawable folders found in a a application.
+ *
+ * @return A list of drawable folders.
+ */
+ public List<FolderElement> getDrawableFolders()
+ {
+ List<FolderElement> list = new ArrayList<FolderElement>();
+ for (Element element : this.getChildren())
+ {
+ if (element.getType() == Element.Type.FOLDER_DRAWABLE)
+ {
+ if (element instanceof FolderElement)
+ {
+ list.add((FolderElement) element);
+ }
+ }
+ }
+ return list;
+ }
+
+ /**
+ * Gets the standard drawable folder
+ *
+ * @return Retruns the folder element that represents the standard drawable folder.
+ */
+ public FolderElement getDrawableFolder()
+ {
+ return getDrawableFolder(ResourcesFolderElement.DRAWABLE_FOLDER);
+ }
+
+ /***
+ * Gets the low DPI drawable folder.
+ *
+ * @return Retruns the folder element that represents the low DPI drawable folder. Can be null.
+ */
+ public FolderElement getLdpiDrawableFolder()
+ {
+ return getDrawableFolder(ResourcesFolderElement.LDPI_DRAWABLE_FOLDER);
+ }
+
+ /***
+ * Gets the medium DPI drawable folder.
+ *
+ * @return Returns the folder element that represents the medium DPI drawable folder. Can be null.
+ */
+ public FolderElement getMdpiDrawableFolder()
+ {
+ return getDrawableFolder(ResourcesFolderElement.MDPI_DRAWABLE_FOLDER);
+ }
+
+ /***
+ * Gets the high DPI drawable folder.
+ *
+ * @return Returns the folder element that represents the high DPI drawable folder. Can be null.
+ */
+ public FolderElement getHdpiDrawableFolder()
+ {
+ return getDrawableFolder(ResourcesFolderElement.HDPI_DRAWABLE_FOLDER);
+ }
+
+ /***
+ * Gets the extra high DPI drawable folder.
+ *
+ * @return Returns the folder element that represents the extra high DPI drawable folder. Can be null.
+ */
+ public FolderElement getXhdpiDrawableFolder()
+ {
+ return getDrawableFolder(ResourcesFolderElement.XHDPI_DRAWABLE_FOLDER);
+ }
+
+ /***
+ * Gets an drawable folder.
+ *
+ * @param folderName Foler name which represents a drawable one.
+ *
+ * @return Returns the folder element.
+ */
+ private FolderElement getDrawableFolder(String folderName)
+ {
+ FolderElement result = null;
+ for (FolderElement element : getDrawableFolders())
+ {
+ if (element.getName().equals(folderName))
+ {
+ result = element;
+ break;
+ }
+ }
+ return result;
+ }
+
+ /*Strings methods*/
+ /**
+ * Gets all values folders elements.
+ *
+ * @return Returns the folder element list.
+ */
+ public List<FolderElement> getValuesFolders()
+ {
+ List<FolderElement> result = new ArrayList<FolderElement>();
+ for (Element element : getChildren())
+ {
+ if (element.getType().equals(Element.Type.FOLDER_VALUES))
+ {
+ if (element instanceof FolderElement)
+ {
+ result.add((FolderElement) element);
+ }
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Gets all available Locales.
+ *
+ * @return Returns all available locales.
+ */
+ public List<Locale> getAvailableLocales()
+ {
+ if (localeList == null)
+ {
+ localeList = new ArrayList<Locale>();
+ Locale locale;
+ for (FolderElement folder : getValuesFolders())
+ {
+ locale = getLocaleFromFolderName(folder.getName());
+ if ((locale != null) && !localeList.contains(locale))
+ {
+ localeList.add(locale);
+ }
+ }
+ }
+ return localeList;
+ }
+
+ /**
+ * Merge a list of string elements in a single one.
+ *
+ * @return Returns the merged String Elements.
+ */
+ private StringsElement mergeStringsElements(List<StringsElement> list)
+ {
+ StringsElement stringsUnion = null;
+ if (list.size() > 0)
+ {
+ stringsUnion = new StringsElement(STRINGS_XML, null);
+
+ for (StringsElement element : list)
+ {
+ for (String key : element.getKeyList())
+ {
+ Object value = element.getValue(key);
+
+ boolean valid = value != null;
+ if (valid && (value instanceof String))
+ {
+ valid = ((String) value).length() > 0;
+ }
+ else if (valid && (value instanceof List))
+ {
+ valid = ((List) value).size() > 0;
+ }
+
+ if (!stringsUnion.containsKey(key) || (stringsUnion.containsKey(key) && !valid))
+ {
+ stringsUnion.addEntry(key, element.getValue(key));
+ }
+ }
+ }
+ }
+ return stringsUnion;
+ }
+
+ /**
+ * Gets the Strings Element for a a specific Locale.
+ *
+ * @param locale Specific {@link Locale}.
+ *
+ * @return Returns the specific Strings element.
+ */
+ public StringsElement getValuesElement(Locale locale)
+ {
+ List<StringsElement> stringsElementsToMerge = new ArrayList<StringsElement>();
+
+ Locale localefromFolder;
+ for (FolderElement folder : getValuesFolders())
+ {
+ localefromFolder = getLocaleFromFolderName(folder.getName());
+
+ //Searching for default values
+ if (locale == null)
+ {
+ //Found default values
+ if (localefromFolder == null)
+ {
+ for (Element stringsElement : folder.getChildren())
+ {
+ if ((stringsElement.getType() == Element.Type.FILE_STRINGS))
+ {
+ stringsElementsToMerge.add((StringsElement) stringsElement);
+ }
+ }
+ }
+ }
+ //Searching for specific language
+ else
+ {
+ //Ignoring default values
+ if (localefromFolder != null)
+ {
+ if (localefromFolder.getLanguage().equals(locale.getLanguage())
+ && localefromFolder.getCountry().equals(locale.getCountry()))
+ {
+ for (Element stringsElement : folder.getChildren())
+ {
+ if ((stringsElement.getType() == Element.Type.FILE_STRINGS))
+ {
+ stringsElementsToMerge.add((StringsElement) stringsElement);
+ }
+ }
+ }
+ }
+ }
+ }
+ return mergeStringsElements(stringsElementsToMerge);
+ }
+
+ /**
+ * Gets the default values element.
+ *
+ * @return Returns the default Strings set.
+ */
+ public StringsElement getDefaultValuesElement()
+ {
+ return getValuesElement(null);
+ }
+
+ /***
+ * Extract a locale based on a folder name.
+ *
+ * @param folderName Folder name which the {@link Locale} will be based on.
+ *
+ * @return Returns the extracted {@link Locale}. Can be null.
+ */
+ private static Locale getLocaleFromFolderName(String folderName)
+ {
+ Locale result = null;
+ folderName = folderName.replace(VALUES, "");
+ String[] segments = folderName.split("-");
+
+ String language = null;
+ String country = null;
+
+ List<String> isoLanguages = new ArrayList<String>();
+ for (String lang : Locale.getISOLanguages())
+ {
+ isoLanguages.add(lang);
+ }
+
+ List<String> isoContries = new ArrayList<String>();
+ for (String lang : Locale.getISOCountries())
+ {
+ isoContries.add(lang);
+ }
+
+ for (int i = 1; i < segments.length; i++)
+ {
+ if ((segments[i].length() == 2) && (language == null)
+ && (isoLanguages.contains(segments[i])))
+ {
+ language = segments[i];
+ }
+ else if (segments[i].matches("r[A-Z]{2}") && (country == null)
+ && (isoContries.contains(segments[i].substring(1))))
+ {
+ // Ignore 'r' character
+ country = segments[i].substring(1);
+ }
+ }
+
+ if (language != null)
+ {
+ if (country != null)
+ {
+ result = new Locale(language, country);
+ }
+ else
+ {
+ result = new Locale(language);
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Clean the {@link Locale} list.
+ */
+ @Override
+ public void clean()
+ {
+ super.clean();
+ this.localeList = null;
+ }
+}
diff --git a/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/applicationdata/SourceFolderElement.java b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/applicationdata/SourceFolderElement.java
new file mode 100644
index 0000000..971fb63
--- /dev/null
+++ b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/applicationdata/SourceFolderElement.java
@@ -0,0 +1,426 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.motorolamobility.preflighting.core.applicationdata;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import com.motorolamobility.preflighting.core.source.model.Constant;
+import com.motorolamobility.preflighting.core.source.model.Field;
+import com.motorolamobility.preflighting.core.source.model.Instruction;
+import com.motorolamobility.preflighting.core.source.model.Invoke;
+import com.motorolamobility.preflighting.core.source.model.Method;
+import com.motorolamobility.preflighting.core.source.model.SourceFileElement;
+
+/**
+ * Folder that contains source code inside the Android application project.
+ */
+public class SourceFolderElement extends FolderElement
+{
+ private static final String R_JAVA = "R.java";
+
+ private static final String R$ID = "R$id";
+
+ private static final String R$LAYOUT = "R$layout";
+
+ private static final String R$STRING = "R$string";
+
+ private List<SourceFileElement> sourceFileElements = new ArrayList<SourceFileElement>();
+
+ private final boolean isApk;
+
+ /**
+ * Constructor which loads the minimum necessary data.
+ *
+ * @param folder {@link File} which represents the folder.
+ * @param parent {@link Element} that is the parent in the tree of the Android project representation.
+ * @param isApk <code>true</code> if dealing with APK, <code>false</code> if dealing with Project.
+ */
+ public SourceFolderElement(File folder, Element parent, boolean isApk)
+ {
+ super(folder, parent, Element.Type.FOLDER_SRC);
+ this.isApk = isApk;
+ }
+
+ /**
+ * Gets the list of {@link SourceFileElement} objects inside this folder.
+ *
+ * @return Returns the list of {@link SourceFileElement} inside the folder.
+ */
+ public List<SourceFileElement> getSourceFileElements()
+ {
+ return sourceFileElements;
+ }
+
+ /**
+ * Get the list of {@link Invoke} which is inside each {@link SourceFileElement}
+ * of this folder.
+ *
+ * @return returns the list of invoked methods inside each {@link SourceFileElement}.
+ */
+ public List<Invoke> getInvokedMethods()
+ {
+ List<Invoke> invokedMethods = new ArrayList<Invoke>();
+ for (SourceFileElement smali : sourceFileElements)
+ {
+ List<Method> virtualMethods = smali.getVirtualMethods();
+ List<Method> directMethods = smali.getDirectMethods();
+ extractMethodsInvoked(invokedMethods, directMethods);
+ extractMethodsInvoked(invokedMethods, virtualMethods);
+ }
+ return invokedMethods;
+ }
+
+ /**
+ * Lists all methods invoked by a APK, given a list of methods.
+ *
+ * @param invokedMethodsInsideProject List of {@link Invoke} methods inside
+ * the project.
+ * @param methods List of methods where the search will be done.
+ */
+ private void extractMethodsInvoked(List<Invoke> invokedMethodsInsideProject,
+ List<Method> methods)
+ {
+ for (Method m : methods)
+ {
+ List<Instruction> instructions = m.getInstructions();
+ for (Instruction instr : instructions)
+ {
+ if (instr instanceof Invoke)
+ {
+ Invoke invoke = (Invoke) instr;
+ invokedMethodsInsideProject.add(invoke);
+ }
+ }
+ }
+ }
+
+ /**
+ * Gets all Ids declared in R.java.
+ *
+ * @return Returns the {@link List} of Ids within R.Java, represented by {@link Field} objects.
+ */
+ public List<Field> getIds()
+ {
+ List<Field> ids = new ArrayList<Field>();
+ for (SourceFileElement smali : sourceFileElements)
+ {
+ if (R_JAVA.equals(smali.getSourceName()))
+ {
+ List<Field> staticFields = smali.getStaticFields();
+ if (smali.getClassFullPath().contains(R$ID))
+ {
+ ids.addAll(smali.getStaticFields());
+ break;
+ }
+ else
+ {
+ for (Field field : staticFields)
+ {
+ if (field.getName().startsWith("id"))
+ {
+ ids.add(field);
+ }
+ }
+ }
+ }
+ }
+ return ids;
+ }
+
+ /**
+ * Get layouts declared in R.java.
+ *
+ * @return Returns the list of {@link Field} representing the
+ * layouts declared in R.java.
+ */
+ public List<Field> getLayouts()
+ {
+ List<Field> layouts = new ArrayList<Field>();
+ if (isApk)
+ {
+ if (sourceFileElements != null)
+ {
+ for (SourceFileElement smali : sourceFileElements)
+ {
+ if (R_JAVA.equals(smali.getSourceName()))
+ {
+ List<Field> staticFields = smali.getStaticFields();
+ if (smali.getClassFullPath().contains(R$LAYOUT))
+ {
+ layouts.addAll(staticFields);
+ break;
+ }
+ else
+ {
+ for (Field field : staticFields)
+ {
+ if (field.getName().startsWith("layout"))
+ {
+ layouts.add(field);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ return layouts;
+ }
+
+ /**
+ * Get strings declared in R.java.
+ *
+ * @return the list of {@link Field} objects representing the strings declared in R.java.
+ */
+ public List<Field> getStrings()
+ {
+ List<Field> strings = new ArrayList<Field>();
+ if (isApk)
+ {
+ if (sourceFileElements != null)
+ {
+ for (SourceFileElement smali : sourceFileElements)
+ {
+ if (R_JAVA.equals(smali.getSourceName()))
+ {
+ List<Field> staticFields = smali.getStaticFields();
+ if (smali.getClassFullPath().contains(R$STRING))
+ {
+ strings.addAll(staticFields);
+ break;
+ }
+ else
+ {
+ for (Field field : staticFields)
+ {
+ if (field.getName().startsWith("string"))
+ {
+ strings.add(field);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ return strings;
+ }
+
+ /**
+ * Gets the layouts that are used in the code.
+ * (it makes the relationship among const command
+ * and the reference inside R.java)
+ *
+ * @return Returns the list of layouts used in the code.
+ */
+ private List<Field> getLayoutsUsedInCode()
+ {
+ List<Field> layoutsUsed = new ArrayList<Field>();
+ List<Field> layoutsDeclared = getLayouts();
+ if (isApk)
+ {
+ if (sourceFileElements != null)
+ {
+ for (SourceFileElement smali : sourceFileElements)
+ {
+ List<Method> virtualMethods = smali.getVirtualMethods();
+ List<Method> directMethods = smali.getDirectMethods();
+ extractLayoutsUsed(layoutsUsed, layoutsDeclared, directMethods);
+ extractLayoutsUsed(layoutsUsed, layoutsDeclared, virtualMethods);
+ }
+ }
+ }
+ return layoutsUsed;
+
+ }
+
+ private void extractLayoutsUsed(List<Field> layoutsUsed, List<Field> layoutsDeclared,
+ List<Method> methods)
+ {
+ for (Method m : methods)
+ {
+ List<Instruction> instructions = m.getInstructions();
+ for (Instruction instr : instructions)
+ {
+ if (instr instanceof Constant)
+ {
+ Constant constant = (Constant) instr;
+ String value = constant.getValue();
+ for (Field f : layoutsDeclared)
+ {
+ //find layout being used - unfortunately it may be false positive
+ if ((value != null) && value.equals(f.getValue()))
+ {
+ layoutsUsed.add(f);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Gets the strings that are used in the code.
+ * (it makes the relationship among const command
+ * and the reference inside R.java)
+ *
+ * @return Returns the list of layouts used in the code.
+ */
+ private List<Field> getStringsUsedInCode()
+ {
+ List<Field> stringsUsed = new ArrayList<Field>();
+ List<Field> stringsDeclared = getStrings();
+ if (isApk)
+ {
+ if (sourceFileElements != null)
+ {
+ for (SourceFileElement smali : sourceFileElements)
+ {
+ List<Method> virtualMethods = smali.getVirtualMethods();
+ List<Method> directMethods = smali.getDirectMethods();
+ extractStringsUsed(stringsUsed, stringsDeclared, directMethods);
+ extractStringsUsed(stringsUsed, stringsDeclared, virtualMethods);
+ }
+ }
+ }
+ return stringsUsed;
+
+ }
+
+ private void extractStringsUsed(List<Field> stringsUsed, List<Field> stringsDeclared,
+ List<Method> methods)
+ {
+ for (Method m : methods)
+ {
+ List<Instruction> instructions = m.getInstructions();
+ for (Instruction instr : instructions)
+ {
+ if (instr instanceof Constant)
+ {
+ Constant constant = (Constant) instr;
+ String value = constant.getValue();
+ for (Field f : stringsDeclared)
+ {
+ //find layout being used - unfortunately it may be false positive
+ if ((value != null) && value.equals(f.getValue()))
+ {
+ stringsUsed.add(f);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Gets all the strings used in Java files inside the source folder
+ * @return the set of strings used in the Java files (identified by string ID, not including "R.string")
+ */
+ public Set<String> getUsedStringConstants()
+ {
+ Set<String> usedStrings = new HashSet<String>();
+ if (sourceFileElements != null)
+ {
+ for (SourceFileElement source : sourceFileElements)
+ {
+ if (!isApk)
+ {
+ //project
+ if (source.getUsedStringConstants() != null)
+ {
+ usedStrings.addAll(source.getUsedStringConstants());
+ }
+ }
+ else
+ {
+ //APK
+ List<Field> fields = getStringsUsedInCode();
+ if (fields != null)
+ {
+ for (Field f : fields)
+ {
+ String aux = f.getName().replace("string.", "");
+ usedStrings.add(aux);
+ }
+ }
+ }
+ }
+ }
+ return usedStrings;
+ }
+
+ /**
+ * Gets all layouts used in Java files inside the source folder
+ * @return the set of layouts used in the Java files (identified by layout ID, not including "R.layout")
+ */
+ public Set<String> getUsedLayoutConstants()
+ {
+ Set<String> usedLayouts = new HashSet<String>();
+ for (SourceFileElement source : sourceFileElements)
+ {
+ if (!isApk)
+ {
+ if (source.getUsedLayoutConstants() != null)
+ {
+ usedLayouts.addAll(source.getUsedLayoutConstants());
+ }
+ }
+ else
+ {
+ List<Field> fields = getLayoutsUsedInCode();
+ if (fields != null)
+ {
+ for (Field f : fields)
+ {
+ String aux = f.getName().replace("layout.", "");
+ usedLayouts.add(f.getName());
+ }
+ }
+ }
+ }
+ return usedLayouts;
+ }
+
+ /**
+ * Clear Source File elements.
+ */
+ @Override
+ public void clean()
+ {
+ super.clean();
+ sourceFileElements.clear();
+ sourceFileElements = null;
+ }
+
+ /**
+ * This implementation provides a human-readable text of this
+ * {@link SourceFileElement}.
+ *
+ * @return Returns a human-readable text of this {@link SourceFileElement}.
+ *
+ * @see Object#toString()
+ */
+ @Override
+ public String toString()
+ {
+ return "ProjectJavaModel [sourceFileElements=" + sourceFileElements + "]";
+ }
+}
diff --git a/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/applicationdata/StringsElement.java b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/applicationdata/StringsElement.java
new file mode 100644
index 0000000..ebb4712
--- /dev/null
+++ b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/applicationdata/StringsElement.java
@@ -0,0 +1,325 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.preflighting.core.applicationdata;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+/***
+ * This class is intended to be used to represent strings.xml files under values folders in a Android Application
+ */
+public class StringsElement extends XMLElement
+{
+ private static final String RESOURCES_ELEMENT = "resources";
+
+ private static final String STRING_ELEMENT = "string";
+
+ private static final String STRING_ARRAY_ELEMENT = "string-array";
+
+ private static final String STRING_NAME_ATTRIBUTE = "name";
+
+ private Map<String, LocalizationValue> map;
+
+ /**
+ * Constructor which sets the minimum basic data.
+ *
+ * @param name Name of the element.
+ * @param parent String element's Parent.
+ */
+ public StringsElement(String name, Element parent)
+ {
+ super(name, parent, Element.Type.FILE_STRINGS);
+ map = new HashMap<String, LocalizationValue>();
+ }
+
+ /**
+ * Set the {@link Document} and load the Map.
+ */
+ @Override
+ public void setDocument(Document document)
+ {
+ super.setDocument(document);
+ loadMap();
+ }
+
+ /***
+ * Loads the document nodes in a map
+ */
+ private void loadMap()
+ {
+ Document doc = getDocument();
+ if (doc != null)
+ {
+ NodeList list = doc.getElementsByTagName(RESOURCES_ELEMENT);
+ if (list.getLength() > 0)
+ {
+ //Get first resource element
+ Node rootNode = list.item(0);
+
+ //Get strings entries
+ NodeList nodes = rootNode.getChildNodes();
+ Node node;
+
+ if (nodes.getLength() > 0)
+ {
+ int length = nodes.getLength();
+ int i;
+
+ String key;
+ LocalizationValue value;
+ List<String> valuesList;
+
+ //For each entry of strings.xml, here represented by the document, create an entry in the map
+ for (i = 0; i < length; i++)
+ {
+ node = nodes.item(i);
+ if (node instanceof org.w3c.dom.Element)
+ {
+ //Single Value
+ if (node.getNodeName().equals(STRING_ELEMENT))
+ {
+ key =
+ node.getAttributes().getNamedItem(STRING_NAME_ATTRIBUTE)
+ .getNodeValue();
+ value =
+ new LocalizationValue(ValueType.SINGLE,
+ node.getTextContent(), null);
+
+ this.addEntry(key, value);
+ }
+ //String-array
+ else if (node.getNodeName().equals(STRING_ARRAY_ELEMENT))
+ {
+ key =
+ node.getAttributes().getNamedItem(STRING_NAME_ATTRIBUTE)
+ .getNodeValue();
+
+ valuesList = new ArrayList<String>();
+
+ //Read Array items
+ NodeList itemNodes = node.getChildNodes();
+ if (itemNodes.getLength() > 0)
+ {
+ int j;
+ for (j = 0; j < length; j++)
+ {
+ if (itemNodes.item(j) instanceof org.w3c.dom.Element)
+ {
+ if (itemNodes.item(j).getTextContent().length() > 0)
+ {
+ valuesList.add(itemNodes.item(j).getTextContent());
+ }
+ }
+ }
+ }
+
+ value = new LocalizationValue(ValueType.ARRAY, null, valuesList);
+ this.addEntry(key, value);
+ }
+
+ }
+
+ }
+ }
+ }
+ }
+ }
+
+ /***
+ * Gets a list of keys from strings.xml represented by this element.
+ *
+ * @return Returns a list of keys.
+ */
+ public List<String> getKeyList()
+ {
+ List<String> result = new ArrayList<String>();
+ result.addAll(map.keySet());
+ return result;
+ }
+
+ /**
+ * Adds an entry to the map.
+ * Intend to be used for creation of elements that does not represent a existing file.
+ * (e.g. Union of many strings.xml of the same language).
+ *
+ * @param key The string that represents the key.
+ * @param value A single string value or a List of strings in case the value is an array
+ */
+ @SuppressWarnings("unchecked")
+ public void addEntry(String key, Object value)
+ {
+ if (value instanceof String)
+ {
+ map.put(key, new LocalizationValue(ValueType.SINGLE, (String) value, null));
+ }
+ else if (value instanceof List)
+ {
+ map.put(key, new LocalizationValue(ValueType.ARRAY, null, (List<String>) value));
+ }
+ else if (value instanceof LocalizationValue)
+ {
+ map.put(key, (LocalizationValue) value);
+ }
+ }
+
+ /***
+ * Check if the element contains a specific key.
+ *
+ * @param key The key to be checked.
+ * @return Returns <code>true</code> if yes, <code>false</code> otherwise.
+ */
+ public boolean containsKey(String key)
+ {
+ return map.containsKey(key);
+ }
+
+ /***
+ * Check if the element contains values for a specific key.
+ *
+ * @param key Key to be checked.
+ * @return Returns <code>true</code> if yes, <code>false</code> otherwise.
+ */
+ public boolean containsValue(String key)
+ {
+ return (getValue(key) != null);
+ }
+
+ /**
+ * Given a certain key, its value is returned.
+ *
+ * @param key Key which is related to the value to be retrieved.
+ *
+ * @return Returns a certain value based on a key.
+ */
+ public Object getValue(String key)
+ {
+ Object result = null;
+ if (map != null)
+ {
+ LocalizationValue value = map.get(key);
+ if (value != null)
+ {
+ if (value.getType() == ValueType.SINGLE)
+ {
+ result = value.getValue();
+ }
+ else if (value.getType() == ValueType.ARRAY)
+ {
+ result = value.getValues();
+ }
+ }
+ }
+ return result;
+ }
+
+ //Localization value
+
+ /***
+ * Enumeration that represents the possible types of a value.
+ */
+ public enum ValueType
+ {
+ /**
+ * The value is a single element.
+ */
+ SINGLE,
+ /**
+ * The value is an array of values.
+ */
+ ARRAY,
+ /**
+ * The value is a plural representation of value.
+ */
+ PLURAL
+ };
+
+ /**
+ * Class that represents a localization value. Can be a single value, an array or a plural
+ */
+ class LocalizationValue
+ {
+ /**
+ * Constructor which sets the minimum necessary data.
+ *
+ * @param type The type of Location value.
+ * @param value The value.
+ * @param values List of values.
+ */
+ public LocalizationValue(ValueType type, String value, List<String> values)
+ {
+ this.type = type;
+ this.value = value;
+ this.values = values;
+ }
+
+ private final ValueType type;
+
+ private final String value;
+
+ private List<String> values = new ArrayList<String>();
+
+ /**
+ * Gets the type.
+ *
+ * @return Returns the {@link ValueType}.
+ */
+ public ValueType getType()
+ {
+ return type;
+ }
+
+ /**
+ * Gets the value.
+ *
+ * @return Returns the value.
+ */
+ public String getValue()
+ {
+ return value;
+ }
+
+ /**
+ * Gets the list of values.
+ *
+ * @return Returns the list of values.
+ */
+ public List<String> getValues()
+ {
+ return values;
+ }
+ }
+
+ /**
+ * Clean the create Map.
+ */
+ @Override
+ public void clean()
+ {
+ super.clean();
+ if (map != null)
+ {
+ map.clear();
+ }
+ this.map = null;
+ }
+
+}
diff --git a/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/applicationdata/XMLElement.java b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/applicationdata/XMLElement.java
new file mode 100644
index 0000000..842739d
--- /dev/null
+++ b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/applicationdata/XMLElement.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.preflighting.core.applicationdata;
+
+import java.io.File;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+
+import com.motorolamobility.preflighting.core.internal.utils.ProjectUtils;
+
+/***
+ * Specialization of Element for XML files.
+ * It has specific methods for XML files.
+ */
+public class XMLElement extends Element
+{
+ private Document document;
+
+ private File xmlFile;
+
+ /**
+ * Construct an XMLElement object with the given parameters.
+ *
+ * @param name the XML element name
+ * @param parent the XML element parent
+ * @param type the XML element type
+ */
+ public XMLElement(String name, Element parent, Type type)
+ {
+ super(name, parent, type);
+ }
+
+ /**
+ * Returns the document of this {@link XMLElement}
+ * @return the document of this XMLElement
+ */
+ public Document getDocument()
+ {
+ return document;
+ }
+
+ /**
+ * Sets the document for this XML element
+ * @param document the document for this XMLElement
+ */
+ public void setDocument(Document document)
+ {
+ this.document = document;
+ }
+
+ /**
+ * Return the line number of a node at its document or -1 if not possible to retrieve it.
+ * @param node Node to be located.
+ * @return line Line number.
+ */
+ public int getNodeLineNumber(Node node)
+ {
+ Integer line = -1;
+
+ if ((node.getUserData(ProjectUtils.LINE_NUMBER_KEY) != null)
+ && (node.getUserData(ProjectUtils.LINE_NUMBER_KEY) instanceof Integer))
+ {
+ line = (Integer) node.getUserData(ProjectUtils.LINE_NUMBER_KEY);
+ }
+
+ return line;
+ }
+
+ @Override
+ /**
+ * Clean the object references.
+ */
+ public void clean()
+ {
+ super.clean();
+ this.document = null;
+ this.xmlFile = null;
+ }
+}
diff --git a/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/checker/Checker.java b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/checker/Checker.java
new file mode 100644
index 0000000..a0bd3ad
--- /dev/null
+++ b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/checker/Checker.java
@@ -0,0 +1,339 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.preflighting.core.checker;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.MultiStatus;
+import org.eclipse.osgi.util.NLS;
+
+import com.motorolamobility.preflighting.core.PreflightingCorePlugin;
+import com.motorolamobility.preflighting.core.applicationdata.ApplicationData;
+import com.motorolamobility.preflighting.core.checker.condition.CanExecuteConditionStatus;
+import com.motorolamobility.preflighting.core.checker.condition.ICondition;
+import com.motorolamobility.preflighting.core.checker.parameter.ICheckerParameter;
+import com.motorolamobility.preflighting.core.devicespecification.DeviceSpecification;
+import com.motorolamobility.preflighting.core.exception.PreflightingCheckerException;
+import com.motorolamobility.preflighting.core.i18n.PreflightingCoreNLS;
+import com.motorolamobility.preflighting.core.validation.Parameter;
+import com.motorolamobility.preflighting.core.validation.ParameterDescription;
+import com.motorolamobility.preflighting.core.validation.ValidationManagerConfiguration;
+import com.motorolamobility.preflighting.core.validation.ValidationResult;
+
+/**
+ * This is the base Checker implementation.
+ * <br>
+ * Usually new checkers do not need to have an implementation of this class.
+ * Default implementation of canExecute and execute methods will iterate over all checker conditions calling
+ * conditions canExecute and validateApplication methods.
+ */
+public class Checker implements IChecker
+{
+
+ private MultiStatus checkerStatus;
+
+ /**
+ * The unique identifier for this checker.
+ */
+ private String id;
+
+ /**
+ * A map for the conditions this checker has.
+ */
+ private Map<String, ICondition> conditions;
+
+ /**
+ * The parameters for this {@link Checker}.
+ */
+ private Map<String, ICheckerParameter> checkerParameters;
+
+ /**
+ * The global parameters that were passed on command line
+ */
+ private List<Parameter> globalParams = new ArrayList<Parameter>();
+
+ /**
+ * True if condition is enabled to run.
+ */
+ protected boolean enabled = true;
+
+ /**
+ * The default implementation of this method returns a list of {@link ParameterDescription} objects.
+ * which is derived from the list of entered parameters of the Checker Extension-point.
+ *
+ * @return List of {@link ParameterDescription} where each object holds a detailed description
+ * of each parameter.
+ *
+ * @see com.motorolamobility.preflighting.core.IParameterProcessor#getParameterDescriptions()
+ */
+ public List<ParameterDescription> getParameterDescriptions()
+ {
+ List<ParameterDescription> parameters = new ArrayList<ParameterDescription>();
+
+ if ((checkerParameters != null) && (checkerParameters.size() > 0))
+ {
+ ParameterDescription parameterDescription = null;
+ for (ICheckerParameter conditionParameter : checkerParameters.values())
+ {
+ parameterDescription = new ParameterDescription();
+ parameterDescription.setName(conditionParameter.getName());
+ parameterDescription.setDescription(conditionParameter.getDescription());
+ parameterDescription.setValueDescription(conditionParameter.getValueDescription());
+ parameterDescription.setValueRequired(conditionParameter.isMandatory());
+ parameterDescription.setType(conditionParameter.getType());
+ parameters.add(parameterDescription);
+ }
+ }
+
+ return parameters;
+ }
+
+ /**
+ * The default implementation of this method verifies whether all {@link ICheckerParameter} objects
+ * which are mandatory have values. One may override this method in order to validate each {@link ICheckerParameter}
+ * value. Also, when overriding this method, remember to call its <code>super</code> implementation
+ * so the validation here is also performed.
+ * <br>
+ * The entered parameters are the ones inputed by the user and they are represented by a
+ * list of {@link Parameter}.
+ * <br>
+ * This method returns an {@link IStatus} which represents the status of this method execution. However, for this to work properly, one has
+ * to return its implementation represented by {@link MultiStatus}.
+ *
+ * @param parameters The parameters which are entered by the user. When implementing this method, one
+ * may use them to perform the necessary validations. One also can use the {@link ICheckerParameter} object values
+ * to validate the entered parameters since they are the same.
+ *
+ * @return Return the status of this method execution. Although its return value is an {@link IStatus},
+ * one must return {@link MultiStatus}, which is its implementation for this framework.
+ *
+ * @see com.motorolamobility.preflighting.core.IParameterProcessor#validateInputParams(java.util.List)
+ */
+ public IStatus validateInputParams(List<Parameter> parameters)
+ {
+ CanExecuteConditionStatus status =
+ new CanExecuteConditionStatus(CanExecuteConditionStatus.OK,
+ PreflightingCorePlugin.PLUGIN_ID, ""); //$NON-NLS-1$
+
+ // check whether all mandatory parameters were entered
+ if ((checkerParameters != null) && (checkerParameters.size() > 0))
+ {
+ for (ICheckerParameter checkerParameter : checkerParameters.values())
+ {
+ if (checkerParameter.isMandatory() && (checkerParameter.getValue() == null))
+ {
+ status =
+ new CanExecuteConditionStatus(
+ CanExecuteConditionStatus.ERROR,
+ PreflightingCorePlugin.PLUGIN_ID,
+ NLS.bind(
+ PreflightingCoreNLS.Checker_MandatoryParam_EmptyValueWarn,
+ getId(), checkerParameter.getId()),
+ checkerParameter.getId());
+ break;
+ }
+ }
+ }
+
+ return status;
+ }
+
+ /**
+ * Set the Checker Identifier. This value identifies
+ * the {@link Checker} uniquely.
+ *
+ * @param id Represent the Checker Identifier.
+ */
+ public void setId(String id)
+ {
+ this.id = id;
+ }
+
+ /**
+ * Implementation of the generic {@link IChecker#canExecute(ApplicationData, List)} Method.
+ * This method verifies if all checker conditions can be executed.
+ * <br>
+ * Note: For backward support, old checkers must override this method with their own version.
+ * <br>
+ * If you override this method be aware that you either might have to manually call the canExecute method for your conditions or call super.canExecute(),
+ * in order to verify make sure that canExecute method from conditions are being called.
+ *
+ * @param data Represents the data structure of the APK or Android Project.
+ * @param deviceSpecs When the checker is applied to specific devices, the list of
+ * {@link DeviceSpecification} holds the ones to be validated.
+ *
+ * @return Return the status of the execution. Although {@link IStatus} is the returned
+ * interface, one must return {@link MultiStatus}, which is a collection of {@link CanExecuteConditionStatus}
+ *
+ *
+ * @throws PreflightingCheckerException Exception thrown if there are any problems executing this validation.
+ *
+ */
+ public IStatus canExecute(ApplicationData data, List<DeviceSpecification> deviceSpecs)
+ throws PreflightingCheckerException
+ {
+
+ checkerStatus = new MultiStatus(PreflightingCorePlugin.PLUGIN_ID, IStatus.OK, null, null);
+
+ if ((conditions != null) && (!conditions.isEmpty()))
+ {
+
+ for (ICondition condition : conditions.values())
+ {
+ if (condition.isEnabled())
+ {
+ IStatus conditionStatus = condition.canExecute(data, deviceSpecs);
+ checkerStatus.add(conditionStatus);
+ }
+ }
+ }
+
+ return checkerStatus;
+
+ }
+
+ /**
+ * The default implementation executes all conditions that returned an {@link IStatus#OK} status on {@link IChecker#canExecute(ApplicationData, List)} method.
+ *
+ * @see com.motorolamobility.preflighting.core.checker.IChecker#validateApplication(com.motorolamobility.preflighting.core.applicationdata.ApplicationData, java.util.List, com.motorolamobility.preflighting.core.devicespecification.internal.PlatformRules, com.motorolamobility.preflighting.core.validation.ValidationManagerConfiguration, com.motorolamobility.preflighting.core.validation.ValidationResult)
+ */
+ public void validateApplication(ApplicationData data, List<DeviceSpecification> deviceSpecs,
+ ValidationManagerConfiguration valManagerConfig, ValidationResult results)
+ throws PreflightingCheckerException
+ {
+ if ((conditions != null) && (!conditions.isEmpty()))
+ {
+ for (ICondition condition : conditions.values())
+ {
+ if (condition.isEnabled())
+ {
+ for (IStatus conditionStatus : checkerStatus.getChildren())
+ {
+ if (conditionStatus instanceof CanExecuteConditionStatus)
+ {
+ if ((((CanExecuteConditionStatus) conditionStatus).getConditionId()
+ .equals(condition.getId()))
+ && (conditionStatus.getSeverity() == IStatus.OK))
+ {
+ condition.execute(data, deviceSpecs, valManagerConfig, results);
+
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Free memory allocated
+ */
+ public void clean()
+ {
+ //Override if necessary
+ }
+
+ public String getId()
+ {
+ return id;
+ }
+
+ /**
+ *
+ * @see com.motorolamobility.preflighting.core.checker.IChecker#getConditions()
+ */
+ public Map<String, ICondition> getConditions()
+ {
+ return conditions != null ? conditions : new HashMap<String, ICondition>(0);
+ }
+
+ /**
+ *
+ * @see com.motorolamobility.preflighting.core.checker.IChecker#setConditions(java.util.HashMap)
+ */
+ public void setConditions(HashMap<String, ICondition> conditions)
+ {
+ this.conditions = conditions;
+ }
+
+ /**
+ * Gets the list of parameters for this checker.
+ *
+ * @return the list of parameters for this checker.
+ */
+ public Map<String, ICheckerParameter> getParameters()
+ {
+ return this.checkerParameters;
+ }
+
+ /**
+ * Sets the list of Parameters for this Condition.
+ *
+ * @param conditionParameters This list of Parameters for this Condition.
+ */
+ public void setParameters(Map<String, ICheckerParameter> conditionParameters)
+ {
+ this.checkerParameters = conditionParameters;
+ }
+
+ /**
+ * @return the globalParams
+ */
+ public final List<Parameter> getGlobalParams()
+ {
+ return globalParams;
+ }
+
+ /**
+ * @param globalParams the globalParams to set
+ */
+ public final void setGlobalParams(List<Parameter> globalParams)
+ {
+ this.globalParams = globalParams;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorolamobility.preflighting.core.checker.IChecker#isEnabled()
+ */
+ public boolean isEnabled()
+ {
+ return enabled;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorolamobility.preflighting.core.checker.IChecker#setEnabled(boolean)
+ */
+ public void setEnabled(boolean enabled)
+ {
+ this.enabled = enabled;
+ }
+
+ /* (non-Javadoc)
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString()
+ {
+ return "Checker [checkerStatus=" + checkerStatus + ", id=" + id + ", enabled=" + enabled
+ + "]";
+ }
+}
diff --git a/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/checker/CheckerDescription.java b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/checker/CheckerDescription.java
new file mode 100644
index 0000000..a8c3131
--- /dev/null
+++ b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/checker/CheckerDescription.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.preflighting.core.checker;
+
+/***
+ * This class describes a checker and is intended to be used by UI classes
+ * that have to show information about checkers.
+ *
+ */
+public final class CheckerDescription
+{
+ private String id;
+
+ private String name;
+
+ private String description;
+
+ /**
+ *
+ * @return The checker id.
+ */
+ public String getId()
+ {
+ return id;
+ }
+
+ /**
+ * Sets the checker id.
+ * @param id the checker id.
+ */
+ public void setId(String id)
+ {
+ this.id = id;
+ }
+
+ /**
+ * Returns the checker name.
+ * @return the checker name.
+ */
+ public String getName()
+ {
+ return name;
+ }
+
+ /**
+ * Sets the checker name.
+ * @param name the checker name.
+ */
+ public void setName(String name)
+ {
+ this.name = name;
+ }
+
+ /**
+ * Returns the checker description.
+ * @return the checker description.
+ */
+ public String getDescription()
+ {
+ return description;
+ }
+
+ /**
+ * Sets the checker description.
+ * @param decription the checker description.
+ */
+ public void setDescription(String decription)
+ {
+ this.description = decription;
+ }
+
+}
diff --git a/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/checker/CheckerExtension.java b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/checker/CheckerExtension.java
new file mode 100644
index 0000000..b13215c
--- /dev/null
+++ b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/checker/CheckerExtension.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.preflighting.core.checker;
+
+import com.motorolamobility.preflighting.core.PreflightingCorePlugin;
+import com.motorolamobility.preflighting.core.internal.checkerparameter.CheckerParameterElement;
+import com.motorolamobility.preflighting.core.internal.conditions.ConditionElement;
+
+/**
+ * Bean class representing a checker extension.
+ */
+public final class CheckerExtension
+{
+
+ public static final String CHECKER_EXTENSION_POINT_ID = PreflightingCorePlugin.PLUGIN_ID
+ + ".checker"; //$NON-NLS-1$
+
+ public static final String CHECKER_EXTENSION_POINT_ELEMENT_CHECKER = "checker"; //$NON-NLS-1$
+
+ public static final String CHECKER_EXTENSION_POINT_ATTRIBUTE_ID = "id"; //$NON-NLS-1$
+
+ public static final String CHECKER_EXTENSION_POINT_ATTRIBUTE_NAME = "name"; //$NON-NLS-1$
+
+ public static final String CHECKER_EXTENSION_POINT_ATTRIBUTE_DESCRIPTION = "description"; //$NON-NLS-1$
+
+ public static final String CHECKER_EXTENSION_POINT_ATTRIBUTE_CLASS = "class"; //$NON-NLS-1$
+
+ private final String id;
+
+ private final String name;
+
+ private final String description;
+
+ private final IChecker checker;
+
+ private ConditionElement[] conditions;
+
+ private CheckerParameterElement[] conditionParameters;
+
+ /**
+ * Creates a new checker extension object.
+ *
+ * @param id Checker id.
+ * @param name Checker name.
+ * @param description Checker description.
+ * @param checker Checker object.
+ */
+ public CheckerExtension(String id, String name, String description, IChecker checker)
+ {
+ this.id = id;
+ this.name = name;
+ this.description = description;
+ this.checker = checker;
+ }
+
+ /**
+ * @return the checker id.
+ */
+ public String getId()
+ {
+ return id;
+ }
+
+ /**
+ * @return the checker name.
+ */
+ public String getName()
+ {
+ return name;
+ }
+
+ /**
+ * @return the checker description.
+ */
+ public String getDescription()
+ {
+ return description;
+ }
+
+ /**
+ * @return the checker object.
+ */
+ public IChecker getChecker()
+ {
+ return checker;
+ }
+
+ /**
+ * @return the checker conditions. If the checker has no conditions declared an empty array is returned.
+ */
+ public ConditionElement[] getConditions()
+ {
+ return conditions != null ? conditions : new ConditionElement[0];
+ }
+
+ /**
+ * @param conditions Checker conditions to set.
+ */
+ public void setConditions(ConditionElement[] conditions)
+ {
+ this.conditions = conditions;
+ }
+
+ /**
+ * Gets the list of Parameters for this Condition.
+ *
+ * @return Returns the list of Parameters for this Condition.
+ */
+ public CheckerParameterElement[] getConditionParameters()
+ {
+ return this.conditionParameters;
+ }
+
+ /**
+ * Sets the list of Parameters for this Condition.
+ *
+ * @param conditionParameters This list of Parameters for this Condition.
+ */
+ public void setConditionParameters(CheckerParameterElement[] conditionParameters)
+ {
+ this.conditionParameters = conditionParameters;
+ }
+
+ /* (non-Javadoc)
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString()
+ {
+ return "CheckerExtension [id=" + id + ", name=" + name + ", description=" + description
+ + ", checker=" + checker + "]";
+ }
+}
diff --git a/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/checker/IChecker.java b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/checker/IChecker.java
new file mode 100644
index 0000000..cedbb82
--- /dev/null
+++ b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/checker/IChecker.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.preflighting.core.checker;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.MultiStatus;
+
+import com.motorolamobility.preflighting.core.IParameterProcessor;
+import com.motorolamobility.preflighting.core.applicationdata.ApplicationData;
+import com.motorolamobility.preflighting.core.checker.condition.ICondition;
+import com.motorolamobility.preflighting.core.checker.parameter.ICheckerParameter;
+import com.motorolamobility.preflighting.core.devicespecification.DeviceSpecification;
+import com.motorolamobility.preflighting.core.exception.PreflightingCheckerException;
+import com.motorolamobility.preflighting.core.validation.ValidationManagerConfiguration;
+import com.motorolamobility.preflighting.core.validation.ValidationResult;
+import com.motorolamobility.preflighting.core.validation.ValidationResultData;
+
+/**
+ * This is the basic interface for Checkers. All Checkers must define an implementation of this Interface.
+ * It can be used to keep common data among conditions and run common verifications before executing the conditions.
+ * It is recommended to use the {@link Checker} class and override just the necessary methods instead of implementing this Interface.
+ */
+public interface IChecker extends IParameterProcessor
+{
+
+ /**
+ * Performs the validation. This method is called once for each {@link IChecker} that is configured to execute if
+ * {@link IChecker#canExecute(ApplicationData, List)} method returns a {@link IStatus#OK}.
+ *
+ * @param data The {@link ApplicationData} containing all available information for the application being tested.
+ * @param deviceSpecs The {@link List} containing all {@link DeviceSpecification} available to AppValidator
+ * @param valManagerConfig {@link ValidationManagerConfiguration} containing the configuration for this validation.
+ * @param checkerResults {@link ValidationResult} At the end of validation, {@link ValidationResultData} must be added to checkerResults.
+ * @throws PreflightingCheckerException Exception thrown if there are any problems executing this validation.
+ */
+ public void validateApplication(ApplicationData data, List<DeviceSpecification> deviceSpecs,
+ ValidationManagerConfiguration valManagerConfig, ValidationResult checkerResults)
+ throws PreflightingCheckerException;
+
+ /**
+ * Verifies is this Checker can be executed or not.
+ * The idea here is to only return a non OK value if there's some invalid information on {@link ApplicationData}.
+ *
+ * @param data The {@link ApplicationData} containing all available information for the application being tested.
+ * @param deviceSpecs The {@link List} containing all {@link DeviceSpecification} available to AppValidator
+ *
+ * @return Returns the status indicating whether the {@link IChecker} can be executed. Although
+ * {@link IStatus} is returned, one must return its implementation: {@link MultiStatus}.
+ *
+ * @throws PreflightingCheckerException Exception thrown in case there is any problem executing
+ * this validation.
+ */
+ public IStatus canExecute(ApplicationData data, List<DeviceSpecification> deviceSpecs)
+ throws PreflightingCheckerException;
+
+ /**
+ * Returns the {@link IChecker} unique identifier.
+ *
+ * @return The checker unique identifier.
+ */
+ public String getId();
+
+ /**
+ * Sets the {@link IChecker} unique identifier.
+ *
+ * @param id Returns the checker unique identifier.
+ */
+ public void setId(String id);
+
+ /**
+ * Returns the conditions {@link ICondition} defined by the {@link IChecker}.
+ * <br>
+ * If the {@link IChecker} has no conditions, an empty {@link Map} must be returned.
+ *
+ * @return A {@link Map} that contains the condition IDs as keys and the condition objects associated with them.
+ */
+ public Map<String, ICondition> getConditions();
+
+ /**
+ * Set the conditions defined by the checker. Conditions are not mandatory, so setting conditions is optional.
+ *
+ * @param checkerConditions {@link Map} holding the conditions. Each condition is identified
+ * by a {@link String}.
+ */
+ public void setConditions(HashMap<String, ICondition> checkerConditions);
+
+ /**
+ * Gets the list of Parameters for this Condition.
+ *
+ * @return Returns the list of Parameters for this Condition.
+ */
+ public Map<String, ICheckerParameter> getParameters();
+
+ /**
+ * Sets the list of parameters for this condition.
+ *
+ * @param conditionParameters This list of Parameters for this Condition.
+ */
+ public void setParameters(Map<String, ICheckerParameter> conditionParameters);
+
+ /**
+ * This method will be called right after execution.
+ * All data used for this verification, which is stored in this class for some reason, must be cleared in order to reduce memory consumption.
+ */
+ public void clean();
+
+ /**
+ * @return true if this checker is set as enabled to run, false otherwise.
+ */
+ public boolean isEnabled();
+
+ /**
+ * Enables or disables the checker.
+ * @param enabled true if enabled, false otherwise.
+ */
+ public void setEnabled(boolean enabled);
+
+}
diff --git a/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/checker/condition/CanExecuteConditionStatus.java b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/checker/condition/CanExecuteConditionStatus.java
new file mode 100644
index 0000000..e34beda
--- /dev/null
+++ b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/checker/condition/CanExecuteConditionStatus.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.preflighting.core.checker.condition;
+
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+
+import com.motorolamobility.preflighting.core.checker.Checker;
+
+/**
+ * Status that should be used throughout this framework. Every time an {@link IStatus} is
+ * required, use {@link CanExecuteConditionStatus} instead.
+ */
+public class CanExecuteConditionStatus extends Status
+{
+
+ private String conditionId;
+
+ /**
+ * Returns a Condition identifier which this {@link CanExecuteConditionStatus}
+ * instance is linked.
+ *
+ * @return Returns the {@link Condition} identifier which this object
+ * is attached with.
+ */
+ public String getConditionId()
+ {
+ return conditionId;
+ }
+
+ /**
+ * Sets the {@link Condition} identifier for this {@link CanExecuteConditionStatus}. This status
+ * then is attached to a certain {@link Condition}.
+ *
+ * @param conditionId The condition identifier which attaches a certain Condition to
+ * a Status.
+ */
+ public void setConditionId(String conditionId)
+ {
+ this.conditionId = conditionId;
+ }
+
+ /**
+ * Constructor which holds minimum info to instantiate a meaningful {@link CanExecuteConditionStatus}. Note
+ * that if you wish to provide an {@link IStatus#ERROR} or {@link IStatus#WARNING}, the condition identifier
+ * must be provided using the method {@link CanExecuteConditionStatus#setConditionId(String)}.
+ *
+ * @param severity The severity of the status. One may find them as constants for the
+ * {@link Status} class.
+ * @param pluginId The plug-in identifier where the {@link Checker} is implemented.
+ * @param message Message to be displayed by this status.
+ */
+ public CanExecuteConditionStatus(int severity, String pluginId, String message)
+ {
+ super(severity, pluginId, message);
+ }
+
+ /**
+ * Constructor which holds minimum info to instantiate a meaningful {@link CanExecuteConditionStatus}. Note
+ * that if you wish to provide an {@link IStatus#ERROR} or {@link IStatus#WARNING}, the condition identifier
+ * must be provided using the method {@link CanExecuteConditionStatus#setConditionId(String)}.
+ *
+ * @param severity The severity of the status. One may find them as constants for the
+ * {@link CanExecuteConditionStatus} class.
+ * @param pluginId The plug-in identifier where the {@link Checker} is implemented.
+ * @param message Message to be displayed by this status.
+ * @param exception Exception which raised this {@link CanExecuteConditionStatus}.
+ */
+ public CanExecuteConditionStatus(int severity, String pluginId, String message,
+ Throwable exception)
+ {
+ super(severity, pluginId, message, exception);
+
+ }
+
+ /**
+ * Constructor which holds minimum info to instantiate a meaningful {@link CanExecuteConditionStatus}. Note
+ * that if you wish to provide an {@link IStatus#ERROR} or {@link IStatus#WARNING}, the condition identifier
+ * must be provided using the method {@link CanExecuteConditionStatus#setConditionId(String)}.
+ *
+ * @param severity The severity of the status. One may find them as constants for the
+ * {@link CanExecuteConditionStatus} class.
+ * @param pluginId The plug-in identifier where the {@link Checker} is implemented.
+ * @param code The plug-in specific {@link CanExecuteConditionStatus} status code.
+ * @param message Message to be displayed by this status.
+ * @param exception Exception which raised this {@link CanExecuteConditionStatus}.
+ */
+ public CanExecuteConditionStatus(int severity, String pluginId, int code, String message,
+ Throwable exception)
+ {
+ super(severity, pluginId, code, message, exception);
+
+ }
+
+ /**
+ * Constructor which holds minimum info to instantiate a meaningful {@link CanExecuteConditionStatus}.
+ *
+ * @param severity The severity of the status. One may find them as constants for the
+ * {@link CanExecuteConditionStatus} class.
+ * @param pluginId The plug-in identifier where the {@link Checker} is implemented.
+ * @param message Message to be displayed by this status.
+ * @param conditionId The identifier of the condition
+ */
+ public CanExecuteConditionStatus(int severity, String pluginId, String message,
+ String conditionId)
+ {
+ super(severity, pluginId, message);
+ this.conditionId = conditionId;
+
+ }
+
+ /**
+ * Constructor which holds minimum info to instantiate a meaningful {@link CanExecuteConditionStatus}.
+ *
+ * @param severity The severity of the status. One may find them as constants for the
+ * {@link CanExecuteConditionStatus} class.
+ * @param pluginId The plug-in identifier where the {@link Checker} is implemented.
+ * @param message Message to be displayed by this status.
+ * @param exception Exception which raised this {@link CanExecuteConditionStatus}.
+ * @param conditionId The identifier of the condition
+ */
+ public CanExecuteConditionStatus(int severity, String pluginId, String message,
+ Throwable exception, String conditionId)
+ {
+ super(severity, pluginId, message, exception);
+ this.conditionId = conditionId;
+
+ }
+
+ /**
+ * Constructor which holds minimum info to instantiate a meaningful {@link CanExecuteConditionStatus}.
+ *
+ * @param severity The severity of the status. One may find them as constants for the
+ * {@link CanExecuteConditionStatus} class.
+ * @param pluginId The plug-in identifier where the {@link Checker} is implemented.
+ * @param code The plug-in specific {@link CanExecuteConditionStatus} status code.
+ * @param message Message to be displayed by this status.
+ * @param exception Exception which raised this {@link CanExecuteConditionStatus}.
+ * @param conditionId The identifier of the condition
+ */
+ public CanExecuteConditionStatus(int severity, String pluginId, int code, String message,
+ Throwable exception, String conditionId)
+ {
+ super(severity, pluginId, code, message, exception);
+ this.conditionId = conditionId;
+
+ }
+
+ public void setStatusSeverity(int severity)
+ {
+ setSeverity(severity);
+ }
+
+}
diff --git a/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/checker/condition/Condition.java b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/checker/condition/Condition.java
new file mode 100644
index 0000000..d00ce91
--- /dev/null
+++ b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/checker/condition/Condition.java
@@ -0,0 +1,254 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.motorolamobility.preflighting.core.checker.condition;
+
+import java.util.List;
+
+import com.motorolamobility.preflighting.core.applicationdata.ApplicationData;
+import com.motorolamobility.preflighting.core.checker.IChecker;
+import com.motorolamobility.preflighting.core.devicespecification.DeviceSpecification;
+import com.motorolamobility.preflighting.core.exception.PreflightingCheckerException;
+import com.motorolamobility.preflighting.core.validation.ValidationManagerConfiguration;
+import com.motorolamobility.preflighting.core.validation.ValidationResult;
+import com.motorolamobility.preflighting.core.validation.ValidationResultData.SEVERITY;
+
+/**
+ * Default implementation for a Checker Condition.
+ *
+ * It is recommended that conditions extend this class, overriding methods canExecute and execute.
+ * Note that the attributes id, name, description, defaultSeverityLevel and checker are all filled
+ * by the AppValidator core with information provided on the extension from plugin.xml file.
+ *
+ */
+public abstract class Condition implements ICondition
+{
+
+ /**
+ * The condition ID.
+ */
+ protected String id;
+
+ /**
+ * The condition name.
+ */
+ protected String name;
+
+ /**
+ * Description of the condition.
+ */
+ protected String description;
+
+ /**
+ * Which severity this condition represents by default.
+ */
+ protected SEVERITY defaultSeverityLevel;
+
+ /**
+ * Checker that is the owner of this Condition.
+ */
+ protected IChecker checker;
+
+ /**
+ * True if condition is enabled to run.
+ */
+ protected boolean enabled = true;
+
+ /**
+ * Specific marker type for this Condition.
+ * Default value is a default AppValidator marker type.
+ * Subclasses should define their own marker type and properly set this attribute.
+ */
+ protected String markerType;
+
+ /**
+ * Default constructor.
+ *
+ */
+ public Condition()
+ {
+ }
+
+ /**
+ * Construct a new Condition with the given parameters.
+ *
+ * @param id Condition id.
+ * @param name Condition name.
+ * @param description Condition description.
+ * @param defaultLevel Condition default level.
+ */
+ public Condition(String id, String name, String description, SEVERITY defaultLevel)
+ {
+ this.id = id;
+ this.name = name;
+ this.description = description;
+ this.defaultSeverityLevel = defaultLevel;
+ }
+
+ /**
+ * @return Condition id.
+ */
+ public String getId()
+ {
+ return id;
+ }
+
+ /**
+ * @param id Condition id to set.
+ */
+ public void setId(String id)
+ {
+ this.id = id;
+ }
+
+ /**
+ * @return Condition name.
+ */
+ public String getName()
+ {
+ return name;
+ }
+
+ /**
+ * @param name Condition name to set.
+ */
+ public void setName(String name)
+ {
+ this.name = name;
+ }
+
+ /**
+ * @return Condition description.
+ */
+ public String getDescription()
+ {
+ return description;
+ }
+
+ /**
+ * @param description Condition description to set.
+ */
+ public void setDescription(String description)
+ {
+ this.description = description;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorolamobility.preflighting.core.checker.condition.ICondition#getSeverityLevel()
+ */
+ public SEVERITY getSeverityLevel()
+ {
+ return defaultSeverityLevel;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorolamobility.preflighting.core.checker.condition.ICondition#setSeverityLevel(com.motorolamobility.preflighting.core.validation.ValidationResultData.SEVERITY)
+ */
+ public void setSeverityLevel(SEVERITY defaultSeverityLevel)
+ {
+ this.defaultSeverityLevel = defaultSeverityLevel;
+ }
+
+ /**
+ * @return Checker associated with the condition.
+ */
+ public IChecker getChecker()
+ {
+ return checker;
+ }
+
+ /**
+ * @param checker Checker to set.
+ */
+ public void setChecker(IChecker checker)
+ {
+ this.checker = checker;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorolamobility.preflighting.core.checker.condition.ICondition#setMarkerType(String)
+ */
+ public void setMarkerType(String markerType)
+ {
+ this.markerType = markerType;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorolamobility.preflighting.core.checker.condition.ICondition#getMarkerType()
+ */
+ public String getMarkerType()
+ {
+ return this.markerType;
+ }
+
+ /**
+ * <b>Must always be implemented.</b>
+ *
+ * @param data the {@link ApplicationData}
+ * @param deviceSpecs the list of {@link DeviceSpecification}
+ * @param valManagerConfig the {@link ValidationManagerConfiguration}
+ * @param results
+ *
+ * @throws PreflightingCheckerException
+ */
+ public abstract void execute(ApplicationData data, List<DeviceSpecification> deviceSpecs,
+ ValidationManagerConfiguration valManagerConfig, ValidationResult results)
+ throws PreflightingCheckerException;
+
+ /**
+ * <b>Must always be implemented.</b>
+ *
+ * @param data the {@link ApplicationData}
+ * @param deviceSpecs the list of {@link DeviceSpecification}
+ * @return the status, that is if this condition can be executed. See {@link CanExecuteConditionStatus} for more information.
+ * @throws PreflightingCheckerException
+ */
+ public abstract CanExecuteConditionStatus canExecute(ApplicationData data,
+ List<DeviceSpecification> deviceSpecs) throws PreflightingCheckerException;
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorolamobility.preflighting.core.checker.condition.ICondition#isEnabled()
+ */
+ public boolean isEnabled()
+ {
+ return enabled;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorolamobility.preflighting.core.checker.condition.ICondition#setEnabled(boolean)
+ */
+ public void setEnabled(boolean enabled)
+ {
+ this.enabled = enabled;
+ }
+
+ /* (non-Javadoc)
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString()
+ {
+ return "Condition [id=" + id + ", name=" + name + ", description=" + description
+ + ", defaultSeverityLevel=" + defaultSeverityLevel + ", checker=" + checker
+ + ", enabled=" + enabled + "]";
+ }
+}
diff --git a/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/checker/condition/ICondition.java b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/checker/condition/ICondition.java
new file mode 100644
index 0000000..930002e
--- /dev/null
+++ b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/checker/condition/ICondition.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.preflighting.core.checker.condition;
+
+import java.util.List;
+
+import com.motorolamobility.preflighting.core.applicationdata.ApplicationData;
+import com.motorolamobility.preflighting.core.checker.IChecker;
+import com.motorolamobility.preflighting.core.devicespecification.DeviceSpecification;
+import com.motorolamobility.preflighting.core.exception.PreflightingCheckerException;
+import com.motorolamobility.preflighting.core.validation.ValidationManagerConfiguration;
+import com.motorolamobility.preflighting.core.validation.ValidationResult;
+import com.motorolamobility.preflighting.core.validation.ValidationResultData.SEVERITY;
+
+/**
+ * A Checker Condition.
+ * This class is responsible to verify one specific condition for a given Checker.
+ * All Checkers must supply a Condition, even if there is only one condition in a Checker.
+ * If you want you can extend the default {@link Condition} implementation and only override
+ * execute and canExecute methods as needed.
+ */
+public interface ICondition
+{
+ /**
+ *
+ * This method is called in order to perform the validation for this condition.
+ *
+ * @param data general information about the app being validated.
+ * @param deviceSpecs device specifications to be used during validations.
+ * @param valManagerConfig a bean that represents the configuration of a given validation.
+ * @param results the object that will receive results for this validation. Use the add methods of it in order to include new results to the current validation.
+ * @return A ValidationResult for problems or success.
+ * Can also return null if no problems are found.
+ * @throws PreflightingCheckerException Exception thrown when there are problems executing
+ * the checker.
+ */
+ public void execute(ApplicationData data, List<DeviceSpecification> deviceSpecs,
+ ValidationManagerConfiguration valManagerConfig, ValidationResult results)
+ throws PreflightingCheckerException;
+
+ /**
+ * This method verifies if this condition can be executed or if there is something wrong with the {@link ApplicationData}
+ * that prevents this checker from executing properly.
+ * @param data The {@link ApplicationData} available for this validation.
+ * @param deviceSpecs {@link List} of {@link DeviceSpecification}
+ * @return
+ * @throws PreflightingCheckerException Exception thrown when there are problems validating
+ * the checker.
+ */
+ public CanExecuteConditionStatus canExecute(ApplicationData data,
+ List<DeviceSpecification> deviceSpecs) throws PreflightingCheckerException;
+
+ /**
+ * @return the condition ID.
+ */
+ public String getId();
+
+ /**
+ * @param id the condition id to set.
+ */
+ public void setId(String conditionId);
+
+ /**
+ * @return the condition name.
+ */
+ public String getName();
+
+ /**
+ * @param name the condition name to set.
+ */
+ public void setName(String name);
+
+ /**
+ * @return the condition description.
+ */
+ public String getDescription();
+
+ /**
+ * @param description the description to set.
+ */
+ public void setDescription(String description);
+
+ /**
+ * Get the default severity level for this condition.
+ * @return the default severity level of this condition.
+ */
+ public SEVERITY getSeverityLevel();
+
+ /**
+ * Sets the default severity level for this condition.
+ * @param defaultSeverityLevel the severity level to set.
+ */
+ public void setSeverityLevel(SEVERITY defaultSeverityLevel);
+
+ /**
+ * @param checker the checker that is the owner of this condition.
+ */
+ public void setChecker(IChecker checker);
+
+ /**
+ * @return the checker, owner of this condition.
+ */
+ public IChecker getChecker();
+
+ /**
+ * Returns true if this condition is set as enabled to run, false otherwise.
+ * @return
+ */
+ public boolean isEnabled();
+
+ /**
+ * Enables or disables the condition.
+ * @param enabled true if enabled, false otherwise.
+ */
+ public void setEnabled(boolean enabled);
+
+ /**
+ * Sets string the identifies the marker type of this condition.
+ * The marker type is used to implement quick fix.
+ * @param the type of the marker.
+ */
+ public void setMarkerType(String markerType);
+
+ /**
+ * Returns the string that identifies the marker type for this condition.
+ */
+ public String getMarkerType();
+}
diff --git a/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/checker/parameter/CheckerParameter.java b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/checker/parameter/CheckerParameter.java
new file mode 100644
index 0000000..2308197
--- /dev/null
+++ b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/checker/parameter/CheckerParameter.java
@@ -0,0 +1,213 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.motorolamobility.preflighting.core.checker.parameter;
+
+import com.motorolamobility.preflighting.core.validation.ParameterType;
+
+/**
+ * Bean class representing a Checker Parameter extension element.
+ */
+public class CheckerParameter implements ICheckerParameter
+{
+ private String id;
+
+ private String name;
+
+ private String value = null;
+
+ private Boolean booleanValue = null;
+
+ private Integer intValue = null;
+
+ private String description;
+
+ private String valueDescription;
+
+ private boolean isMandatory;
+
+ private ParameterType type;
+
+ /**
+ * This constructors populates the beam with all properties.
+ *
+ * @param id Checker Parameter Id.
+ * @param name Checker Parameter Name.
+ * @param description Checker Parameter Description.
+ * @param valueDescription Value-specific description.
+ * @param type The type of this parameter.
+ * @param isMandatory Flag which determines whether this Checker
+ * Parameter is mandatory.
+ */
+ public CheckerParameter(String id, String name, String description, String valueDescription,
+ ParameterType type, boolean isMandatory)
+ {
+ this.id = id;
+ this.name = name;
+ this.description = description;
+ this.valueDescription = valueDescription;
+ this.type = type;
+ this.isMandatory = isMandatory;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorolamobility.preflighting.core.checker.parameter.ICheckerParameter#getId()
+ */
+ public String getId()
+ {
+ return id;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorolamobility.preflighting.core.checker.parameter.ICheckerParameter#setId(java.lang.String)
+ */
+ public void setId(String id)
+ {
+ this.id = id;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorolamobility.preflighting.core.checker.parameter.ICheckerParameter#getName()
+ */
+ public String getName()
+ {
+ return name;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorolamobility.preflighting.core.checker.parameter.ICheckerParameter#setName(java.lang.String)
+ */
+ public void setName(String name)
+ {
+ this.name = name;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorolamobility.preflighting.core.checker.parameter.ICheckerParameter#getValue()
+ */
+ public String getValue()
+ {
+ return value;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorolamobility.preflighting.core.checker.parameter.ICheckerParameter#setValue(java.lang.String)
+ */
+ public void setValue(String value)
+ {
+ this.value = value;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorolamobility.preflighting.core.checker.parameter.ICheckerParameter#setValue(boolean)
+ */
+ public void setBooleanValue(Boolean value)
+ {
+ this.booleanValue = value;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorolamobility.preflighting.core.checker.parameter.ICheckerParameter#isValueTrue()
+ */
+ public Boolean getBooleanValue()
+ {
+ return booleanValue;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorolamobility.preflighting.core.checker.parameter.ICheckerParameter#getIntValue()
+ */
+ public Integer getIntValue()
+ {
+ return intValue;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorolamobility.preflighting.core.checker.parameter.ICheckerParameter#setValue(int)
+ */
+ public void setIntValue(Integer value)
+ {
+ this.intValue = value;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorolamobility.preflighting.core.checker.parameter.ICheckerParameter#getDescription()
+ */
+ public String getDescription()
+ {
+ return description;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorolamobility.preflighting.core.checker.parameter.ICheckerParameter#setDescription(java.lang.String)
+ */
+ public void setDescription(String description)
+ {
+ this.description = description;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorolamobility.preflighting.core.checker.parameter.ICheckerParameter#getValueDescription()
+ */
+ public String getValueDescription()
+ {
+ return valueDescription;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorolamobility.preflighting.core.checker.parameter.ICheckerParameter#setValueDescription(java.lang.String)
+ */
+ public void setValueDescription(String valueDescription)
+ {
+ this.valueDescription = valueDescription;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorolamobility.preflighting.core.checker.parameter.ICheckerParameter#isMandatory()
+ */
+ public boolean isMandatory()
+ {
+ return isMandatory;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorolamobility.preflighting.core.checker.parameter.ICheckerParameter#setMandatory(boolean)
+ */
+ public void setMandatory(boolean isMandatory)
+ {
+ this.isMandatory = isMandatory;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorolamobility.preflighting.core.checker.parameter.ICheckerParameter#getType()
+ */
+ public ParameterType getType()
+ {
+ return type;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorolamobility.preflighting.core.checker.parameter.ICheckerParameter#setType(java.lang.String)
+ */
+ public void setType(ParameterType type)
+ {
+ this.type = type;
+ }
+}
diff --git a/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/checker/parameter/ICheckerParameter.java b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/checker/parameter/ICheckerParameter.java
new file mode 100644
index 0000000..42e91c2
--- /dev/null
+++ b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/checker/parameter/ICheckerParameter.java
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.motorolamobility.preflighting.core.checker.parameter;
+
+import com.motorolamobility.preflighting.core.validation.ParameterType;
+
+/**
+ * Interface representing a checker parameter
+ */
+public interface ICheckerParameter
+{
+
+ /**
+ * Gets the Checker Parameter Id.
+ *
+ * @return Returns the Checker Parameter Id.
+ */
+ public String getId();
+
+ /**
+ * Sets the checker parameter's ID.
+ *
+ * @param id The Checker Parameter Id to be set.
+ */
+ public void setId(String id);
+
+ /**
+ * Gets Checker Parameter Name.
+ *
+ * @return Returns the Checker Parameter Name.
+ */
+ public String getName();
+
+ /**
+ * Sets the checker parameter's name.
+ *
+ * @param name The Checker Parameter Name to be set.
+ */
+ public void setName(String name);
+
+ /**
+ * Gets the value entered for this parameter. This value is
+ * entered by the user on the command line.
+ * If not set, default value is null.
+ *
+ * @return Returns the value set by the user.
+ */
+ public String getValue();
+
+ /**
+ * Gets the boolean value entered for this parameter if its type is boolean. This value is
+ * entered by the user on the command line.
+ *
+ * If not set, default value is null.
+ *
+ * @return Returns the value, as boolean, set by the user.
+ */
+ public Boolean getBooleanValue();
+
+ /**
+ * Gets the int value entered for this parameter if its type is integer. This value is
+ * entered by the user on the command line.
+ *
+ * If not set, default value is null.
+ *
+ * @return Returns the value, as int, set by the user.
+ */
+ public Integer getIntValue();
+
+ /**
+ * Sets the parameter value. This value is
+ * entered by the user on the command line.
+ *
+ * @param value The value set by set by the user.
+ */
+ public void setValue(String value);
+
+ /**
+ * Sets the parameter value. This value is
+ * entered by the user on the command line.
+ *
+ * @param value The value set by the user.
+ */
+ public void setIntValue(Integer intValue);
+
+ /**
+ * Sets the parameter value. This value is
+ * entered by the user on the command line.
+ *
+ * @param value The value set by the user.
+ */
+ public void setBooleanValue(Boolean booleanValue);
+
+ /**
+ * Gets the Checker Parameter Description.
+ *
+ * @return Returns the Checker Parameter Description.
+ */
+ public String getDescription();
+
+ /**
+ * Sets the checker parameter's description.
+ *
+ * @param description The Checker Parameter to be set.
+ */
+ public void setDescription(String description);
+
+ /**
+ * Gets the value-specific description.
+ *
+ * @return Returns the value-specific description.
+ */
+ public String getValueDescription();
+
+ /**
+ * Sets the value-specific description.
+ *
+ * @param valueDescription The value-specific description to set.
+ */
+ public void setValueDescription(String valueDescription);
+
+ /**
+ * Gets a flag which determines whether this Checker Parameter
+ * is mandatory.
+ *
+ * @return Returns a flag which determines whether this Checker Parameter
+ * is mandatory.
+ */
+ public boolean isMandatory();
+
+ /**
+ * Specifies whether this checker parameter is mandatory.
+ *
+ * @param isMandatory The flag which determines whether this Checker Parameter
+ * is mandatory to be set.
+ */
+ public void setMandatory(boolean isMandatory);
+
+ /**
+ * Gets the Parameter´s type. This type represents the variable type
+ * of the parameter: String, Integer, Boolean and so on. They are
+ * defined in the parameter´s type extension-point.
+ *
+ * @return Returns the type.
+ */
+ public ParameterType getType();
+
+ /**
+ * Sets the parameter's type. This type represents the variable type
+ * of the parameter: String, Integer, Boolean and so on. They are
+ * defined in the parameter´s type extension-point.
+ *
+ * @param type The Parameter´s type to set.
+ */
+ public void setType(ParameterType type);
+} \ No newline at end of file
diff --git a/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/devicelayoutspecification/Device.java b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/devicelayoutspecification/Device.java
new file mode 100644
index 0000000..fa3318b
--- /dev/null
+++ b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/devicelayoutspecification/Device.java
@@ -0,0 +1,228 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.preflighting.core.devicelayoutspecification;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.motorolamobility.preflighting.core.i18n.PreflightingCoreNLS;
+import com.motorolamobility.preflighting.core.internal.devicelayoutspecification.ConfigType;
+import com.motorolamobility.preflighting.core.permissionfeature.Feature;
+
+/**
+ * <p>This class is a bean for device specification in XML format.
+ * <p>The following schema fragment specifies the expected content contained within this class.
+ *
+ * <pre>
+ * &lt;complexType>
+ * &lt;complexContent>
+ * &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+ * &lt;sequence>
+ * &lt;element name="default" type="{http://schemas.android.com/sdk/android/layout-devices/1}parametersType" minOccurs="0"/>
+ * &lt;element name="config" type="{http://schemas.android.com/sdk/android/layout-devices/1}configType" maxOccurs="unbounded"/>
+ * &lt;/sequence>
+ * &lt;attribute name="name" use="required" type="{http://www.w3.org/2001/XMLSchema}normalizedString" />
+ * &lt;/restriction>
+ * &lt;/complexContent>
+ * &lt;/complexType>
+ * </pre>
+ *
+ *
+ */
+public class Device
+{
+
+ /**
+ * Line separator
+ */
+ private final static String NEWLINE = System.getProperty("line.separator"); //$NON-NLS-1$
+
+ /**
+ * Tab character
+ */
+ private final static String TAB = "\t"; //$NON-NLS-1$
+
+ protected ParametersType _default;
+
+ /**
+ * The list of specifications.
+ */
+ protected List<ConfigType> config = new ArrayList<ConfigType>();
+
+ /**
+ * The list of features this device supports.
+ */
+ protected List<Feature> supportedFeatures = new ArrayList<Feature>();
+
+ /**
+ * The device name.
+ */
+ protected String name;
+
+ /**
+ * The device id.
+ */
+ protected String id;
+
+ /**
+ * The device provider/manufacturer.
+ */
+ protected String provider;
+
+ /**
+ * Gets the value of the default property.
+ *
+ * @return
+ * possible object is
+ * {@link ParametersType }
+ *
+ */
+ public ParametersType getDefault()
+ {
+ return _default;
+ }
+
+ /**
+ * Sets the value of the default property.
+ *
+ * @param value
+ * allowed object is
+ * {@link ParametersType }
+ *
+ */
+ public void setDefault(ParametersType value)
+ {
+ this._default = value;
+ }
+
+ /**
+ * Gets the value of the config property.
+ *
+ * <p>
+ * This accessor method returns a reference to the live list,
+ * not a snapshot. Therefore any modification you make to the
+ * returned list will be present inside the JAXB object.
+ * This is why there is not a <CODE>set</CODE> method for the config property.
+ *
+ * <p>
+ * For example, to add a new item, do as follows:
+ * <pre>
+ * getConfig().add(newItem);
+ * </pre>
+ *
+ *
+ * <p>
+ * Objects of the following type(s) are allowed in the list:
+ * {@link ConfigType}
+ *
+ *
+ */
+ public List<ConfigType> getConfig()
+ {
+ return this.config;
+ }
+
+ /**
+ * Adds a configuration/specification to the list of specifications.
+ * @param type the configuration to be added.
+ */
+ public void addConfig(ConfigType type)
+ {
+ config.add(type);
+ }
+
+ /**
+ * Gets the value of the name property.
+ *
+ * @return the name of this device.
+ *
+ */
+ public String getName()
+ {
+ return name;
+ }
+
+ /**
+ * Sets the value of the name property.
+ *
+ * @param value the name of this device.
+ *
+ */
+ public void setName(String value)
+ {
+ this.name = value;
+ }
+
+ @Override
+ public String toString()
+ {
+ StringBuilder builder = new StringBuilder();
+ String provider = getProvider();
+ provider = (provider != null) && (!provider.equals("")) ? " - " + provider : "";
+ builder.append(PreflightingCoreNLS.Device_Device + TAB + name + provider + NEWLINE);
+
+ builder.append("Id: " + getId() + NEWLINE);
+
+ builder.append(NEWLINE);
+ if (_default != null)
+ {
+ builder.append(_default);
+ }
+ if ((config != null) && (config.size() > 0))
+ {
+ for (ConfigType conf : config)
+ {
+ builder.append(conf);
+ }
+ }
+ builder.append(NEWLINE);
+ if ((supportedFeatures != null) && (supportedFeatures.size() > 0))
+ {
+ builder.append(PreflightingCoreNLS.Device_SupportedFeatures + NEWLINE);
+ for (Feature nonSupportedFeat : supportedFeatures)
+ {
+ builder.append(TAB + nonSupportedFeat.getId() + NEWLINE);
+ }
+ }
+ return builder.toString();
+ }
+
+ public List<Feature> getSupportedFeatures()
+ {
+ return supportedFeatures;
+ }
+
+ public String getId()
+ {
+ return id;
+ }
+
+ public void setId(String id)
+ {
+ this.id = id;
+ }
+
+ public String getProvider()
+ {
+ return provider;
+ }
+
+ public void setProvider(String provider)
+ {
+ this.provider = provider;
+ }
+
+}
diff --git a/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/devicelayoutspecification/LayoutDevicesReader.java b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/devicelayoutspecification/LayoutDevicesReader.java
new file mode 100644
index 0000000..2fe2793
--- /dev/null
+++ b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/devicelayoutspecification/LayoutDevicesReader.java
@@ -0,0 +1,247 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.motorolamobility.preflighting.core.devicelayoutspecification;
+
+import java.io.IOException;
+
+import javax.xml.parsers.ParserConfigurationException;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.xml.sax.SAXException;
+
+import com.motorolamobility.preflighting.core.internal.devicelayoutspecification.ConfigType;
+import com.motorolamobility.preflighting.core.internal.devicelayoutspecification.LayoutDevicesType;
+import com.motorolamobility.preflighting.core.internal.devicelayoutspecification.ObjectFactory;
+import com.motorolamobility.preflighting.core.internal.devicelayoutspecification.ScreenDimension;
+import com.motorolamobility.preflighting.core.permissionfeature.Feature;
+
+/**
+ * Reads the XML with device specifications and creates a {@link LayoutDevicesType} model.
+ */
+public final class LayoutDevicesReader
+{
+
+ private static final String D_DEVICE = "d:device";
+
+ private static final String D_SUPPORTED_FEATURES = "d:supported-features";
+
+ private static final String D_DEFAULT = "d:default";
+
+ private final Document document;
+
+ /**
+ * @param document XML that contains the properties of a device.
+ */
+ public LayoutDevicesReader(Document document)
+ {
+ this.document = document;
+ }
+
+ /**
+ * Reads XML that contains the specifications of a given device.
+ * @return {@link LayoutDevicesType} representing the device read.
+ * @throws ParserConfigurationException
+ * @throws SAXException
+ * @throws IOException
+ */
+ public LayoutDevicesType read() throws ParserConfigurationException, SAXException, IOException
+ {
+ LayoutDevicesType layoutDevicesType = ObjectFactory.getInstance().createLayoutDevicesType();
+
+ NodeList deviceList = document.getElementsByTagName(D_DEVICE);
+ for (int i = 0; i < deviceList.getLength(); i++)
+ {
+ Node deviceNode = deviceList.item(i);
+ NamedNodeMap deviceNodeAttrs = deviceNode.getAttributes();
+
+ Node deviceIdAtr = deviceNodeAttrs.getNamedItem("id");
+ if ((deviceIdAtr != null) && !deviceIdAtr.getNodeValue().trim().equals("")) //$NON-NLS-1$
+ {
+ // add device
+ Device dev = new Device();
+ dev.setId(deviceIdAtr.getNodeValue());
+
+ dev.setName(extractValueFromAttributes(deviceNodeAttrs, "name"));
+ dev.setProvider(extractValueFromAttributes(deviceNodeAttrs, "provider"));
+
+ NodeList defaultOrConfigList = deviceNode.getChildNodes();
+ for (int j = 0; j < defaultOrConfigList.getLength(); j++)
+ {
+ Node defaultOrConfigNode = defaultOrConfigList.item(j);
+ if ((defaultOrConfigNode != null)
+ && (defaultOrConfigNode.getNodeType() == Node.ELEMENT_NODE))
+ {
+ if (defaultOrConfigNode.getNodeName()
+ .equalsIgnoreCase(D_SUPPORTED_FEATURES))
+ {
+ NodeList paramsList = defaultOrConfigNode.getChildNodes();
+ for (int z = 0; z < paramsList.getLength(); z++)
+ {
+ Node supportedFeatureNode = paramsList.item(z);
+ if ((supportedFeatureNode != null)
+ && (supportedFeatureNode.getNodeType() == Node.ELEMENT_NODE))
+ {
+ Node valueNode = supportedFeatureNode.getFirstChild();
+ String supportedFeatureValue = valueNode.getNodeValue();
+ if ((supportedFeatureValue != null)
+ && !supportedFeatureValue.equals(""))
+ {
+ dev.getSupportedFeatures().add(
+ new Feature(supportedFeatureValue));
+ }
+ }
+ }
+ }
+ else
+ {
+ boolean isDefault =
+ defaultOrConfigNode.getNodeName().equalsIgnoreCase(D_DEFAULT);
+ ParametersType paramTypes =
+ extractParamTypes(defaultOrConfigNode, isDefault);
+ if (!(paramTypes instanceof ConfigType))
+ {
+ //default
+ dev.setDefault(paramTypes);
+ }
+ else
+ {
+ //config
+ NamedNodeMap configAttrs = defaultOrConfigNode.getAttributes();
+ Node configAtr = configAttrs.getNamedItem("name");
+ if ((configAtr != null)
+ && !configAtr.getNodeValue().trim().equals(""))
+ {
+ ConfigType type = (ConfigType) paramTypes;
+ type.setName(configAtr.getNodeValue());
+ dev.addConfig(type);
+ }
+ }
+ }
+ }
+ }
+ layoutDevicesType.getDevices().add(dev);
+ }
+ }
+ return layoutDevicesType;
+ }
+
+ private String extractValueFromAttributes(NamedNodeMap deviceNodeAttrs, String attribute)
+ {
+
+ String value = "";
+
+ Node item = deviceNodeAttrs.getNamedItem(attribute);
+ value = (item != null) ? item.getNodeValue() : "";
+
+ value = (value != null) ? value : "";
+
+ return value;
+ }
+
+ private ParametersType extractParamTypes(Node node, boolean isDefaultNode)
+ {
+ //add parameters
+ ParametersType paramTypes =
+ isDefaultNode ? ObjectFactory.getInstance().createParametersType() : ObjectFactory
+ .getInstance().createConfigType();
+ NodeList paramsList = node.getChildNodes();
+ for (int z = 0; z < paramsList.getLength(); z++)
+ {
+ Node paramNode = paramsList.item(z);
+ if ((paramNode != null) && (paramNode.getNodeType() == Node.ELEMENT_NODE))
+ {
+ String paramName = paramNode.getNodeName();
+ Node valueNode = paramNode.getFirstChild();
+ String paramValue = valueNode.getNodeValue();
+ if (paramName.equalsIgnoreCase("d:country-code"))
+ {
+ paramTypes.setCountryCode(Float.parseFloat(paramValue));
+ }
+ else if (paramName.equalsIgnoreCase("d:network-code"))
+ {
+ paramTypes.setNetworkCode(Float.parseFloat(paramValue));
+ }
+ else if (paramName.equalsIgnoreCase("d:screen-size"))
+ {
+ paramTypes.setScreenSize(paramValue);
+ }
+ else if (paramName.equalsIgnoreCase("d:screen-ratio"))
+ {
+ paramTypes.setScreenRatio(paramValue);
+ }
+ else if (paramName.equalsIgnoreCase("d:screen-orientation"))
+ {
+ paramTypes.setScreenOrientation(paramValue);
+ }
+ else if (paramName.equalsIgnoreCase("d:pixel-density"))
+ {
+ paramTypes.setPixelDensity(paramValue);
+ }
+ else if (paramName.equalsIgnoreCase("d:touch-type"))
+ {
+ paramTypes.setTouchType(paramValue);
+ }
+ else if (paramName.equalsIgnoreCase("d:keyboard-state"))
+ {
+ paramTypes.setKeyboardState(paramValue);
+ }
+ else if (paramName.equalsIgnoreCase("d:text-input-method"))
+ {
+ paramTypes.setTextInputMethod(paramValue);
+ }
+ else if (paramName.equalsIgnoreCase("d:nav-state"))
+ {
+ paramTypes.setNavState(paramValue);
+ }
+ else if (paramName.equalsIgnoreCase("d:nav-method"))
+ {
+ paramTypes.setNavMethod(paramValue);
+ }
+ else if (paramName.equalsIgnoreCase("d:screen-dimension"))
+ {
+ ScreenDimension dim =
+ ObjectFactory.getInstance().createParametersTypeScreenDimension();
+ NodeList dimensionList = paramNode.getChildNodes();
+ for (int w = 0; w < dimensionList.getLength(); w++)
+ {
+ Node dimensionNode = dimensionList.item(w);
+ if ((dimensionNode != null)
+ && (dimensionNode.getNodeType() == Node.ELEMENT_NODE))
+ {
+ Node sizeNode = dimensionNode.getFirstChild();
+ String dimValue = sizeNode.getNodeValue();
+ dim.addSize(Integer.parseInt(dimValue));
+ }
+ }
+ paramTypes.setScreenDimension(dim);
+ }
+ else if (paramName.equalsIgnoreCase("d:xdpi"))
+ {
+ paramTypes.setXdpi(Float.parseFloat(paramValue));
+ }
+ else if (paramName.equalsIgnoreCase("d:ydpi"))
+ {
+ paramTypes.setYdpi(Float.parseFloat(paramValue));
+ }
+ }
+ }
+ return paramTypes;
+ }
+}
diff --git a/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/devicelayoutspecification/ParametersType.java b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/devicelayoutspecification/ParametersType.java
new file mode 100644
index 0000000..848916d
--- /dev/null
+++ b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/devicelayoutspecification/ParametersType.java
@@ -0,0 +1,587 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.preflighting.core.devicelayoutspecification;
+
+import com.motorolamobility.preflighting.core.internal.devicelayoutspecification.ScreenDimension;
+
+/**
+ *
+ * The parametersType define all the parameters that can happen either in a
+ * "default" element or in a named "config" element.
+ * Each parameter element can appear once at most.
+ *
+ * Parameters here are the same as those used to specify alternate Android
+ * resources, as documented by
+ * http://d.android.com/guide/topics/resources/resources-i18n.html#AlternateResources
+ *
+ *
+ * <p>Java class for parametersType complex type.
+ *
+ * <p>The following schema fragment specifies the expected content contained within this class.
+ *
+ * <pre>
+ * &lt;complexType name="parametersType">
+ * &lt;complexContent>
+ * &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+ * &lt;all>
+ * &lt;element name="country-code" minOccurs="0">
+ * &lt;simpleType>
+ * &lt;restriction base="{http://www.w3.org/2001/XMLSchema}float">
+ * &lt;minInclusive value="100"/>
+ * &lt;maxInclusive value="999"/>
+ * &lt;/restriction>
+ * &lt;/simpleType>
+ * &lt;/element>
+ * &lt;element name="network-code" minOccurs="0">
+ * &lt;simpleType>
+ * &lt;restriction base="{http://www.w3.org/2001/XMLSchema}float">
+ * &lt;minExclusive value="0"/>
+ * &lt;maxExclusive value="1000"/>
+ * &lt;/restriction>
+ * &lt;/simpleType>
+ * &lt;/element>
+ * &lt;element name="screen-size" minOccurs="0">
+ * &lt;simpleType>
+ * &lt;restriction base="{http://www.w3.org/2001/XMLSchema}token">
+ * &lt;enumeration value="small"/>
+ * &lt;enumeration value="normal"/>
+ * &lt;enumeration value="large"/>
+ * &lt;enumeration value="xlarge"/>
+ * &lt;/restriction>
+ * &lt;/simpleType>
+ * &lt;/element>
+ * &lt;element name="screen-ratio" minOccurs="0">
+ * &lt;simpleType>
+ * &lt;restriction base="{http://www.w3.org/2001/XMLSchema}token">
+ * &lt;enumeration value="long"/>
+ * &lt;enumeration value="notlong"/>
+ * &lt;/restriction>
+ * &lt;/simpleType>
+ * &lt;/element>
+ * &lt;element name="screen-orientation" minOccurs="0">
+ * &lt;simpleType>
+ * &lt;restriction base="{http://www.w3.org/2001/XMLSchema}token">
+ * &lt;enumeration value="port"/>
+ * &lt;enumeration value="land"/>
+ * &lt;enumeration value="square"/>
+ * &lt;/restriction>
+ * &lt;/simpleType>
+ * &lt;/element>
+ * &lt;element name="pixel-density" minOccurs="0">
+ * &lt;simpleType>
+ * &lt;restriction base="{http://www.w3.org/2001/XMLSchema}token">
+ * &lt;enumeration value="ldpi"/>
+ * &lt;enumeration value="mdpi"/>
+ * &lt;enumeration value="hdpi"/>
+ * &lt;enumeration value="xhdpi"/>
+ * &lt;/restriction>
+ * &lt;/simpleType>
+ * &lt;/element>
+ * &lt;element name="touch-type" minOccurs="0">
+ * &lt;simpleType>
+ * &lt;restriction base="{http://www.w3.org/2001/XMLSchema}token">
+ * &lt;enumeration value="notouch"/>
+ * &lt;enumeration value="stylus"/>
+ * &lt;enumeration value="finger"/>
+ * &lt;/restriction>
+ * &lt;/simpleType>
+ * &lt;/element>
+ * &lt;element name="keyboard-state" minOccurs="0">
+ * &lt;simpleType>
+ * &lt;restriction base="{http://www.w3.org/2001/XMLSchema}token">
+ * &lt;enumeration value="keysexposed"/>
+ * &lt;enumeration value="keyshidden"/>
+ * &lt;enumeration value="keyssoft"/>
+ * &lt;/restriction>
+ * &lt;/simpleType>
+ * &lt;/element>
+ * &lt;element name="text-input-method" minOccurs="0">
+ * &lt;simpleType>
+ * &lt;restriction base="{http://www.w3.org/2001/XMLSchema}token">
+ * &lt;enumeration value="nokeys"/>
+ * &lt;enumeration value="qwerty"/>
+ * &lt;enumeration value="12key"/>
+ * &lt;/restriction>
+ * &lt;/simpleType>
+ * &lt;/element>
+ * &lt;element name="nav-state" minOccurs="0">
+ * &lt;simpleType>
+ * &lt;restriction base="{http://www.w3.org/2001/XMLSchema}token">
+ * &lt;enumeration value="navexposed"/>
+ * &lt;enumeration value="navhidden"/>
+ * &lt;/restriction>
+ * &lt;/simpleType>
+ * &lt;/element>
+ * &lt;element name="nav-method" minOccurs="0">
+ * &lt;simpleType>
+ * &lt;restriction base="{http://www.w3.org/2001/XMLSchema}token">
+ * &lt;enumeration value="dpad"/>
+ * &lt;enumeration value="trackball"/>
+ * &lt;enumeration value="wheel"/>
+ * &lt;enumeration value="nonav"/>
+ * &lt;/restriction>
+ * &lt;/simpleType>
+ * &lt;/element>
+ * &lt;element name="screen-dimension" minOccurs="0">
+ * &lt;complexType>
+ * &lt;complexContent>
+ * &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+ * &lt;sequence maxOccurs="2" minOccurs="2">
+ * &lt;element name="size">
+ * &lt;simpleType>
+ * &lt;restriction base="{http://www.w3.org/2001/XMLSchema}positiveInteger">
+ * &lt;/restriction>
+ * &lt;/simpleType>
+ * &lt;/element>
+ * &lt;/sequence>
+ * &lt;/restriction>
+ * &lt;/complexContent>
+ * &lt;/complexType>
+ * &lt;/element>
+ * &lt;element name="xdpi" minOccurs="0">
+ * &lt;simpleType>
+ * &lt;restriction base="{http://www.w3.org/2001/XMLSchema}float">
+ * &lt;minExclusive value="0"/>
+ * &lt;/restriction>
+ * &lt;/simpleType>
+ * &lt;/element>
+ * &lt;element name="ydpi" minOccurs="0">
+ * &lt;simpleType>
+ * &lt;restriction base="{http://www.w3.org/2001/XMLSchema}float">
+ * &lt;minExclusive value="0"/>
+ * &lt;/restriction>
+ * &lt;/simpleType>
+ * &lt;/element>
+ * &lt;/all>
+ * &lt;/restriction>
+ * &lt;/complexContent>
+ * &lt;/complexType>
+ * </pre>
+ *
+ *
+ */
+public class ParametersType
+{
+ /**
+ * Line separator
+ */
+ protected final static String NEWLINE = System.getProperty("line.separator"); //$NON-NLS-1$
+
+ /**
+ * Tab character
+ */
+ protected final static String TAB = "\t"; //$NON-NLS-1$
+
+ private Float countryCode;
+
+ private Float networkCode;
+
+ private String screenSize;
+
+ private String screenRatio;
+
+ private String screenOrientation;
+
+ private String pixelDensity;
+
+ private String touchType;
+
+ private String keyboardState;
+
+ private String textInputMethod;
+
+ private String navState;
+
+ private String navMethod;
+
+ private ScreenDimension screenDimension;
+
+ private Float xdpi;
+
+ private Float ydpi;
+
+ /**
+ * Gets the value for the countryCode property.
+ *
+ * @return the country code.
+ *
+ */
+ public Float getCountryCode()
+ {
+ return countryCode;
+ }
+
+ /**
+ * Sets the value for the countryCode property.
+ *
+ * @param value the country code.
+ */
+ protected void setCountryCode(Float value)
+ {
+ this.countryCode = value;
+ }
+
+ /**
+ * Gets the value for the networkCode property.
+ *
+ * @return the network code.
+ *
+ */
+ public Float getNetworkCode()
+ {
+ return networkCode;
+ }
+
+ /**
+ * Sets the value for the networkCode property.
+ *
+ * @param value the network code.
+ */
+ protected void setNetworkCode(Float value)
+ {
+ this.networkCode = value;
+ }
+
+ /**
+ * Gets the value for the screenSize property.
+ *
+ * @return the screen size property.
+ */
+ public String getScreenSize()
+ {
+ return screenSize;
+ }
+
+ /**
+ * Sets the value for the screenSize property.
+ *
+ * @param value the value for the screenSize property.
+ */
+ protected void setScreenSize(String value)
+ {
+ this.screenSize = value;
+ }
+
+ /**
+ * Gets the value for the screenRatio property.
+ *
+ * @return the value for the screenRatio property.
+ *
+ */
+ public String getScreenRatio()
+ {
+ return screenRatio;
+ }
+
+ /**
+ * Sets the value for the screenRatio property.
+ *
+ * @param value the value for the screenRatio property.
+ */
+ protected void setScreenRatio(String value)
+ {
+ this.screenRatio = value;
+ }
+
+ /**
+ * Gets the value for the screenOrientation property.
+ *
+ * @return the value for the screenOrientation property.
+ */
+ public String getScreenOrientation()
+ {
+ return screenOrientation;
+ }
+
+ /**
+ * Sets the value for the screenOrientation property.
+ *
+ * @param value the value for the screenOrientation property.
+ */
+ protected void setScreenOrientation(String value)
+ {
+ this.screenOrientation = value;
+ }
+
+ /**
+ * Gets the value for the pixelDensity property.
+ *
+ * @return the value for the pixelDensity property.
+ */
+ public String getPixelDensity()
+ {
+ return pixelDensity;
+ }
+
+ /**
+ * Sets the value for the pixelDensity property.
+ *
+ * @param value the value for the pixelDensity property.
+ */
+ protected void setPixelDensity(String value)
+ {
+ this.pixelDensity = value;
+ }
+
+ /**
+ * Gets the value for the touchType property.
+ *
+ * @return the value for the touchType property.
+ */
+ public String getTouchType()
+ {
+ return touchType;
+ }
+
+ /**
+ * Sets the value for the touchType property.
+ *
+ * @param value the value for the touchType property.
+ * allowed object is
+ * {@link String }
+ *
+ */
+ protected void setTouchType(String value)
+ {
+ this.touchType = value;
+ }
+
+ /**
+ * Gets the value for the keyboardState property.
+ *
+ * @return
+ * possible object is
+ * {@link String }
+ *
+ */
+ public String getKeyboardState()
+ {
+ return keyboardState;
+ }
+
+ /**
+ * Sets the value for the keyboardState property.
+ *
+ * @param value the value for the keyboardState property.
+ */
+ protected void setKeyboardState(String value)
+ {
+ this.keyboardState = value;
+ }
+
+ /**
+ * Gets the value for the textInputMethod property.
+ *
+ * @return the value for the textInputMethod property.
+ */
+ public String getTextInputMethod()
+ {
+ return textInputMethod;
+ }
+
+ /**
+ * Sets the value for the textInputMethod property.
+ *
+ * @param value the value for the textInputMethod property.
+ */
+ protected void setTextInputMethod(String value)
+ {
+ this.textInputMethod = value;
+ }
+
+ /**
+ * Gets the value for the navState property.
+ *
+ * @return the value for the navState property.
+ */
+ public String getNavState()
+ {
+ return navState;
+ }
+
+ /**
+ * Sets the value for the navState property.
+ *
+ * @param value the value for the navState property.
+ */
+ protected void setNavState(String value)
+ {
+ this.navState = value;
+ }
+
+ /**
+ * Gets the value for the navMethod property.
+ *
+ * @return the value for the navMethod property.
+ */
+ public String getNavMethod()
+ {
+ return navMethod;
+ }
+
+ /**
+ * Sets the value for the navMethod property.
+ *
+ * @param value the value for the navMethod property.
+ */
+ protected void setNavMethod(String value)
+ {
+ this.navMethod = value;
+ }
+
+ /**
+ * Gets the value for the screenDimension property.
+ *
+ * @return the value for the screenDimension property.
+ */
+ public ScreenDimension getScreenDimension()
+ {
+ return screenDimension;
+ }
+
+ /**
+ * Sets the value for the screenDimension property.
+ *
+ * @param value the value for the screenDimension property.
+ */
+ protected void setScreenDimension(ScreenDimension value)
+ {
+ this.screenDimension = value;
+ }
+
+ /**
+ * Gets the value for the xdpi property.
+ *
+ * @return the value for the xdpi property.
+ */
+ public Float getXdpi()
+ {
+ return xdpi;
+ }
+
+ /**
+ * Sets the value for the xdpi property.
+ *
+ * @param value the value for the xdpi property.
+ */
+ protected void setXdpi(Float value)
+ {
+ this.xdpi = value;
+ }
+
+ /**
+ * Gets the value for the ydpi property.
+ *
+ * @return the value for the ydpi property.
+ */
+ public Float getYdpi()
+ {
+ return ydpi;
+ }
+
+ /**
+ * Sets the value for the ydpi property.
+ *
+ * @param value the value for the ydpi property.
+ */
+ protected void setYdpi(Float value)
+ {
+ this.ydpi = value;
+ }
+
+ /**
+ * Prints out the Header.
+ *
+ * @return The text of the Header.
+ */
+ protected String toStringHeader()
+ {
+ return "Default parameters:" + "\n";
+ }
+
+ /**
+ * This implementation provides a human-readable text of this
+ * {@link ParametersType}.
+ *
+ * @return Returns a human-readable text of this {@link ParametersType}.
+ *
+ * @see Object#toString()
+ */
+ @Override
+ public String toString()
+ {
+ StringBuilder builder = new StringBuilder();
+ builder.append(toStringHeader());
+ if (countryCode != null)
+ {
+ builder.append(TAB + "countryCode: " + countryCode + NEWLINE);
+ }
+ if (networkCode != null)
+ {
+ builder.append(TAB + "networkCode: " + networkCode + NEWLINE);
+ }
+ if (screenSize != null)
+ {
+ builder.append(TAB + "screenSize: " + screenSize + NEWLINE);
+ }
+ if (screenRatio != null)
+ {
+ builder.append(TAB + "screenRatio: " + screenRatio + NEWLINE);
+ }
+ if (screenOrientation != null)
+ {
+ builder.append(TAB + "screenOrientation: " + screenOrientation + NEWLINE);
+ }
+ if (pixelDensity != null)
+ {
+ builder.append(TAB + "pixelDensity: " + pixelDensity + NEWLINE);
+ }
+ if (touchType != null)
+ {
+ builder.append(TAB + "touchType: " + touchType + NEWLINE);
+ }
+ if (keyboardState != null)
+ {
+ builder.append(TAB + "keyboardState: " + keyboardState + NEWLINE);
+ }
+ if (textInputMethod != null)
+ {
+ builder.append(TAB + "textInputMethod: " + textInputMethod + NEWLINE);
+ }
+ if (navState != null)
+ {
+ builder.append(TAB + "navState: " + navState + NEWLINE);
+ }
+ if (navMethod != null)
+ {
+ builder.append(TAB + "navMethod: " + navMethod + NEWLINE);
+ }
+ if (screenDimension != null)
+ {
+ builder.append(TAB + "screenDimension: " + screenDimension + NEWLINE);
+ }
+ if (xdpi != null)
+ {
+ builder.append(TAB + "xdpi: " + xdpi + NEWLINE);
+ }
+ if (ydpi != null)
+ {
+ builder.append(TAB + "ydpi: " + ydpi + NEWLINE);
+ }
+ builder.append(NEWLINE);
+ return builder.toString();
+ }
+}
diff --git a/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/devicespecification/DeviceSpecification.java b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/devicespecification/DeviceSpecification.java
new file mode 100644
index 0000000..7038a61
--- /dev/null
+++ b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/devicespecification/DeviceSpecification.java
@@ -0,0 +1,243 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.preflighting.core.devicespecification;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+
+import com.motorolamobility.preflighting.core.devicelayoutspecification.Device;
+import com.motorolamobility.preflighting.core.devicespecification.internal.PlatformRules;
+import com.motorolamobility.preflighting.core.logging.PreflightingLogger;
+
+/**
+ * Representation of the configuration of a device
+ */
+public final class DeviceSpecification
+{
+ //Some (but not all) of the keys to access the properties
+ public static String PROPERTY_TOUCH_SCREEN = "touchScreen"; //$NON-NLS-1$
+
+ public static String PROPERTY_TRACKBALL = "hw.trackBall"; //$NON-NLS-1$
+
+ public static String PROPERTY_KEYBOARD = "hw.keyboard"; //$NON-NLS-1$
+
+ public static String PROPERTY_CAMERA = "hw.camera"; //$NON-NLS-1$
+
+ public static String PROPERTY_GPS = "hw.gps"; //$NON-NLS-1$
+
+ public static String PROPERTY_ACCELEROMETER = "hw.accelerometer"; //$NON-NLS-1$
+
+ public static String PROPERTY_SDCARD = "hw.sdCard"; //$NON-NLS-1$
+
+ public static String PROPERTY_NAME = "name"; //$NON-NLS-1$
+
+ public static String PROPERTY_API_LEVEL = "api"; //$NON-NLS-1$
+
+ private int deviceApiLevel = -1;
+
+ private PlatformRules platformRules;
+
+ //(merge hw.ini + manifest.ini)
+ private Map<String, String> propertyNameToValue;
+
+ /**
+ * Data based on layout-devices.xsd
+ */
+ private Device deviceInfo;
+
+ private static String COMMENT_SIGN = "#"; //$NON-NLS-1$
+
+ private static String EQUALS_SIGN = "="; //$NON-NLS-1$
+
+ /**
+ * Reads an inputstream as a ini file and convert it to a Key-Value Map
+ * @param input
+ * @return a map that represents the file that was read.
+ * @throws IOException
+ */
+ private static Map<String, String> iniFileToMap(InputStream input) throws IOException
+ {
+ Map<String, String> result = new HashMap<String, String>();
+ String line;
+ BufferedReader reader = null;
+ try
+ {
+ reader = new BufferedReader(new InputStreamReader(input, "UTF-8")); //$NON-NLS-1$
+ while ((line = reader.readLine()) != null)
+ {
+ // parse the line, ignoring comments (#) and empty lines, mount the map
+ if ((!(line == null) && !line.trim().startsWith(COMMENT_SIGN))
+ && !line.trim().equals("")) //$NON-NLS-1$
+ {
+ int equals = line.indexOf(EQUALS_SIGN);
+ if (equals > -1)
+ {
+ String key = line.substring(0, equals).trim();
+ String value = line.substring(equals + 1, line.length()).trim();
+
+ if (result.containsKey(key) && (!result.get(key).equals(value)))
+ {
+ PreflightingLogger.warn(DeviceSpecification.class,
+ "Wrong device specification file. Duplicated key: " + key); //$NON-NLS-1$
+ }
+ result.put(key, value);
+ }
+ else
+ {
+ PreflightingLogger.warn(DeviceSpecification.class,
+ "Wrong device specification file. Line does not contain key-values:" //$NON-NLS-1$
+ + line);
+ }
+ }
+ }
+ }
+ finally
+ {
+ if (reader != null)
+ {
+ try
+ {
+ reader.close();
+ }
+ catch (Exception e)
+ {
+ //Do Nothing
+ }
+ }
+ input.close();
+ }
+
+ return result;
+ }
+
+ /**
+ * @param apiLevel the API level from the device being represented
+ * @param deviceInfo {@link Device}
+ */
+ public DeviceSpecification(int apiLevel, Device deviceInfo)
+ {
+ this.deviceInfo = deviceInfo;
+ this.deviceApiLevel = apiLevel;
+ platformRules = PlatformRules.getInstance();
+ if (!platformRules.isApiLevelSupported(deviceApiLevel))
+ {
+ PreflightingLogger.warn(DeviceSpecification.class, "API Level for device " + getName() //$NON-NLS-1$
+ + " not supported: " + deviceApiLevel); //$NON-NLS-1$
+ }
+ }
+
+ /**
+ * Creates a new Device Specification object
+ *
+ * @param hardwareIni Stream containing the hardware.ini file for this device
+ * @param manifestIni Stream containing the manifest.ini file for this device
+ */
+ public DeviceSpecification(InputStream hardwareIni, InputStream manifestIni)
+ {
+
+ platformRules = PlatformRules.getInstance();
+ propertyNameToValue = new HashMap<String, String>();
+
+ if ((hardwareIni == null) || (manifestIni == null))
+ {
+ PreflightingLogger.warn(DeviceSpecification.class,
+ "Wrong call to the constructor: null input stream"); //$NON-NLS-1$
+ return;
+ }
+
+ try
+ {
+ propertyNameToValue.putAll(iniFileToMap(hardwareIni));
+ propertyNameToValue.putAll(iniFileToMap(manifestIni));
+ }
+ catch (IOException e)
+ {
+ PreflightingLogger.error(DeviceSpecification.class,
+ "Error loading properties for device.", e); //$NON-NLS-1$
+ }
+
+ this.deviceApiLevel = Integer.parseInt(propertyNameToValue.get(PROPERTY_API_LEVEL));
+
+ if (!platformRules.isApiLevelSupported(deviceApiLevel))
+ {
+ PreflightingLogger.warn(DeviceSpecification.class, "API Level for device " + getName() //$NON-NLS-1$
+ + " not supported: " + deviceApiLevel); //$NON-NLS-1$
+ }
+ }
+
+ /**
+ * Gets the API Level for this device.
+ * @return the API level for this device.
+ */
+ public int getAPILevel()
+ {
+ return deviceApiLevel;
+ }
+
+ /**
+ * Gets the property "name" for this device
+ * @return the property "name" for this device.
+ */
+ public String getName()
+ {
+ return (deviceInfo != null) ? deviceInfo.getName() : "";
+ }
+
+ /**
+ * Gets a Properties object containing
+ * the specification for the device
+ * @return
+ */
+ public Properties getSpecs()
+ {
+ Properties p = new Properties();
+ p.putAll(propertyNameToValue);
+ return p;
+ }
+
+ /**
+ * Returns the device representation.
+ * @return the device representation.
+ */
+ public Device getDeviceInfo()
+ {
+ return deviceInfo;
+ }
+
+ /**
+ * The device id.
+ * @return the device id.
+ */
+ public String getId()
+ {
+
+ return (deviceInfo != null) ? deviceInfo.getId() : "";
+ }
+
+ /**
+ * The device provider.
+ * @return the device provider.
+ */
+ public String getProvider()
+ {
+ return (deviceInfo != null) ? deviceInfo.getProvider() : "";
+ }
+}
diff --git a/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/devicespecification/DevicesSpecsContainer.java b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/devicespecification/DevicesSpecsContainer.java
new file mode 100644
index 0000000..f649c09
--- /dev/null
+++ b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/devicespecification/DevicesSpecsContainer.java
@@ -0,0 +1,403 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.preflighting.core.devicespecification;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import com.motorolamobility.preflighting.core.devicelayoutspecification.Device;
+import com.motorolamobility.preflighting.core.devicelayoutspecification.ParametersType;
+
+/**
+ * This class is responsible to keep Device Specifications.
+ * When a new Device Specification is added it is also added to a internal map that tracks specs vs devices
+ * The map uses SpecKey value as key, representing all specs, Each entry has a list of {@link DeviceSpecification}
+ * containing all devices with that spec.
+ */
+public class DevicesSpecsContainer
+{
+
+ /* @formatter:off */
+ /**
+ * Enumerator for the specifications and their variations.
+ */
+ public static enum SpecKey
+ {
+ screenSize_Small("screenSize_Small"), screenSize_Normal("screenSize_Normal"),
+ screenSize_Large("screenSize_Large"), screenSize_XLarge("screenSize_XLarge"),
+
+ screenRatio_NotLong("screenRatio_NotLong"), screenRatio_Long("screenRatio_Long"),
+
+ screenOrientation_Square("screenOrientation_Square"), screenOrientation_Port(
+ "screenOrientation_Port"), screenOrientation_Land("screenOrientation_Land"),
+
+ pixelDensity_Low("pixelDensity_Low"), pixelDensity_Medium("pixelDensity_Medium"),
+ pixelDensity_High("pixelDensity_High"), pixelDensity_XHigh("pixelDensity_XHigh"),
+
+ touchType_NoTouch("touchType_NoTouch"), touchType_Stylus("touchType_Stylus"),
+ touchType_Finger("touchType_Finger"),
+
+ textIME_NoKeys("textIME_NoKeys"), textIME_Qwerty("textIME_Qwerty"), textIME_TwelveKey(
+ "textIME_TwelveKey"),
+
+ KbState_KeysSoft("KbState_KeysSoft"), KbState_KeysExposed("KbState_KeysExposed"),
+ KbState_KeysHidden("KbState_KeysHidden"),
+
+ navMethod_NoNav("navMethod_NoNav"), navMethod_DPad("navMethod_DPad"), navMethod_TrackBall(
+ "navMethod_TrackBall"), navMethod_Wheel("navMethod_Wheel");
+
+ private String id;
+
+ private SpecKey(String id)
+ {
+ this.id = id;
+ }
+
+ /**
+ * @return the alias
+ */
+ public String getId()
+ {
+ return id;
+ }
+
+ /**
+ * @param alias
+ * @return true if the value of alias is recognized as a valid
+ * InputParameter. Return false if alias is null.
+ */
+
+ public static boolean contains(String id)
+ {
+ boolean contains = false;
+
+ if (id != null)
+ {
+ for (SpecKey key : SpecKey.values())
+ {
+ if (key.getId().equals(id))
+ {
+ contains = true;
+ break;
+ }
+ }
+ }
+
+ return contains;
+ }
+ }
+
+ /* @formatter:on */
+
+ private final List<DeviceSpecification> deviceSpecifications;
+
+ private final Map<SpecKey, List<DeviceSpecification>> specDevFilterMap;
+
+ private static DevicesSpecsContainer instance;
+
+ private DevicesSpecsContainer()
+ {
+ specDevFilterMap = new HashMap<SpecKey, List<DeviceSpecification>>(SpecKey.values().length);
+ deviceSpecifications = new ArrayList<DeviceSpecification>(50);
+ }
+
+ /**
+ * An instance of this class. This class implements the singleton pattern.
+ * @return the instance.
+ */
+ public static DevicesSpecsContainer getInstance()
+ {
+ if (instance == null)
+ {
+ instance = new DevicesSpecsContainer();
+ }
+ return instance;
+ }
+
+ /**
+ * Clear internal structures that are cached.
+ */
+ public void clear()
+ {
+ specDevFilterMap.clear();
+ deviceSpecifications.clear();
+ }
+
+ /**
+ * @return the specDeviceMap
+ */
+ public Map<SpecKey, List<DeviceSpecification>> getSpecDevFilterMap()
+ {
+ return specDevFilterMap;
+ }
+
+ /**
+ * Returns the value for a given specification.
+ * @param specKey the key that represents a specification.
+ * @return the value for that specification.
+ */
+ public List<DeviceSpecification> getDeviceSpecifications(SpecKey specKey)
+ {
+ return specDevFilterMap.get(specKey);
+ }
+
+ /**
+ * Returns the list of device specifications.
+ * @return the list of device specifications.
+ */
+ public List<DeviceSpecification> getDeviceSpecifications()
+ {
+ return deviceSpecifications;
+ }
+
+ /**
+ * Adds device specifications.
+ * @param deviceSpec the specifications to be added.
+ */
+ public void addDeviceSpecification(DeviceSpecification deviceSpec)
+ {
+ if (!deviceSpecifications.contains(deviceSpec))
+ {
+ deviceSpecifications.add(deviceSpec);
+ }
+
+ Device deviceInfo = deviceSpec.getDeviceInfo();
+ ParametersType defaultSpecs = deviceInfo.getDefault();
+ List<SpecKey> keysToAdd = new ArrayList<SpecKey>();
+
+ keysToAdd.add(getScreenSizeKey(defaultSpecs.getScreenSize(), deviceSpec));
+ keysToAdd.add(getScreenRatioKey(defaultSpecs.getScreenRatio(), deviceSpec));
+ keysToAdd.add(getScreenOrientationKey(defaultSpecs.getScreenOrientation(), deviceSpec));
+ keysToAdd.add(getPixelDensityKey(defaultSpecs.getPixelDensity(), deviceSpec));
+ keysToAdd.add(getTouchTypeKey(defaultSpecs.getTouchType(), deviceSpec));
+ keysToAdd.add(getTextInputMethodKey(defaultSpecs.getTextInputMethod(), deviceSpec));
+ keysToAdd.add(getKeyboardStateKey(defaultSpecs.getKeyboardState(), deviceSpec));
+ keysToAdd.add(getNavMethodKey(defaultSpecs.getNavMethod(), deviceSpec));
+
+ for (SpecKey key : keysToAdd)
+ {
+ addToMap(key, deviceSpec);
+ }
+
+ }
+
+ private void addToMap(SpecKey key, DeviceSpecification deviceSpec)
+ {
+ if (key != null)
+ {
+ List<DeviceSpecification> devicesSpecs = specDevFilterMap.get(key);
+ if (devicesSpecs == null)
+ {
+ devicesSpecs = new ArrayList<DeviceSpecification>();
+ }
+ devicesSpecs.add(deviceSpec);
+ specDevFilterMap.put(key, devicesSpecs);
+ }
+ }
+
+ private SpecKey getScreenSizeKey(String screenSize, DeviceSpecification deviceSpec)
+ {
+ SpecKey key = null;
+ if (screenSize != null)
+ {
+ if (screenSize.equalsIgnoreCase("small"))
+ {
+ key = SpecKey.screenSize_Small;
+ }
+ else if (screenSize.equalsIgnoreCase("normal"))
+ {
+ key = SpecKey.screenSize_Normal;
+ }
+ else if (screenSize.equalsIgnoreCase("large"))
+ {
+ key = SpecKey.screenSize_Large;
+ }
+ else if (screenSize.equalsIgnoreCase("xlarge"))
+ {
+ key = SpecKey.screenSize_XLarge;
+ }
+ }
+
+ return key;
+ }
+
+ private SpecKey getScreenRatioKey(String screenRatio, DeviceSpecification deviceSpec)
+ {
+ SpecKey key = null;
+
+ if (screenRatio != null)
+ {
+ if (screenRatio.equalsIgnoreCase("notlong"))
+ {
+ key = SpecKey.screenRatio_NotLong;
+ }
+ else if (screenRatio.equalsIgnoreCase("long"))
+ {
+ key = SpecKey.screenRatio_Long;
+ }
+ }
+
+ return key;
+ }
+
+ private SpecKey getScreenOrientationKey(String screenOrientation, DeviceSpecification deviceSpec)
+ {
+ SpecKey key = null;
+
+ if (screenOrientation != null)
+ {
+ if (screenOrientation.equalsIgnoreCase("square"))
+ {
+ key = SpecKey.screenOrientation_Square;
+ }
+ else if (screenOrientation.equalsIgnoreCase("port"))
+ {
+ key = SpecKey.screenOrientation_Port;
+ }
+ else if (screenOrientation.equalsIgnoreCase("land"))
+ {
+ key = SpecKey.screenOrientation_Land;
+ }
+ }
+
+ return key;
+ }
+
+ private SpecKey getPixelDensityKey(String pixelDensity, DeviceSpecification deviceSpec)
+ {
+ SpecKey key = null;
+
+ if (pixelDensity != null)
+ {
+ if (pixelDensity.equalsIgnoreCase("ldpi"))
+ {
+ key = SpecKey.pixelDensity_Low;
+ }
+ else if (pixelDensity.equalsIgnoreCase("mdpi"))
+ {
+ key = SpecKey.pixelDensity_Medium;
+ }
+ else if (pixelDensity.equalsIgnoreCase("hdpi"))
+ {
+ key = SpecKey.pixelDensity_High;
+ }
+ else if (pixelDensity.equalsIgnoreCase("xhdpi"))
+ {
+ key = SpecKey.pixelDensity_XHigh;
+ }
+ }
+
+ return key;
+ }
+
+ private SpecKey getTouchTypeKey(String touchType, DeviceSpecification deviceSpec)
+ {
+ SpecKey key = null;
+
+ if (touchType != null)
+ {
+ if (touchType.equalsIgnoreCase("notouch"))
+ {
+ key = SpecKey.touchType_NoTouch;
+ }
+ else if (touchType.equalsIgnoreCase("stylus"))
+ {
+ key = SpecKey.touchType_Stylus;
+ }
+ else if (touchType.equalsIgnoreCase("finger"))
+ {
+ key = SpecKey.touchType_Finger;
+ }
+ }
+
+ return key;
+ }
+
+ private SpecKey getTextInputMethodKey(String textInputMethod, DeviceSpecification deviceSpec)
+ {
+ SpecKey key = null;
+
+ if (textInputMethod != null)
+ {
+ if (textInputMethod.equalsIgnoreCase("nokeys"))
+ {
+ key = SpecKey.textIME_NoKeys;
+ }
+ else if (textInputMethod.equalsIgnoreCase("qwerty"))
+ {
+ key = SpecKey.textIME_Qwerty;
+ }
+ else if (textInputMethod.equalsIgnoreCase("twelvekey"))
+ {
+ key = SpecKey.textIME_TwelveKey;
+ }
+ }
+
+ return key;
+ }
+
+ private SpecKey getKeyboardStateKey(String keyboardState, DeviceSpecification deviceSpec)
+ {
+ SpecKey key = null;
+
+ if (keyboardState != null)
+ {
+ if (keyboardState.equalsIgnoreCase("keyssoft"))
+ {
+ key = SpecKey.KbState_KeysSoft;
+ }
+ else if (keyboardState.equalsIgnoreCase("keysexposed"))
+ {
+ key = SpecKey.KbState_KeysExposed;
+ }
+ else if (keyboardState.equalsIgnoreCase("keyshidden"))
+ {
+ key = SpecKey.KbState_KeysHidden;
+ }
+ }
+
+ return key;
+ }
+
+ private SpecKey getNavMethodKey(String navMethod, DeviceSpecification deviceSpec)
+ {
+ SpecKey key = null;
+
+ if (navMethod != null)
+ {
+ if (navMethod.equalsIgnoreCase("nonav"))
+ {
+ key = SpecKey.navMethod_NoNav;
+ }
+ else if (navMethod.equalsIgnoreCase("dpad"))
+ {
+ key = SpecKey.navMethod_DPad;
+ }
+ else if (navMethod.equalsIgnoreCase("trackball"))
+ {
+ key = SpecKey.navMethod_TrackBall;
+ }
+ else if (navMethod.equalsIgnoreCase("wheel"))
+ {
+ key = SpecKey.navMethod_TrackBall;
+ }
+ }
+
+ return key;
+ }
+}
diff --git a/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/devicespecification/internal/PlatformRules.java b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/devicespecification/internal/PlatformRules.java
new file mode 100644
index 0000000..938894d
--- /dev/null
+++ b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/devicespecification/internal/PlatformRules.java
@@ -0,0 +1,808 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.preflighting.core.devicespecification.internal;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+
+import org.w3c.dom.DOMException;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.xml.sax.SAXException;
+
+import com.motorolamobility.preflighting.core.PreflightingCorePlugin;
+import com.motorolamobility.preflighting.core.applicationdata.SourceFolderElement;
+import com.motorolamobility.preflighting.core.internal.permissionfeature.PermissionToFeatureMapReader;
+import com.motorolamobility.preflighting.core.internal.permissionfeature.PermissionToFeatureMapping;
+import com.motorolamobility.preflighting.core.internal.utils.MethodPermissionCSVReader;
+import com.motorolamobility.preflighting.core.logging.PreflightingLogger;
+import com.motorolamobility.preflighting.core.source.model.Invoke;
+import com.motorolamobility.preflighting.core.source.model.PermissionGroups;
+
+/**
+ * Singleton that contains rules that are specific from the Android Platform
+ */
+public class PlatformRules
+{
+ /**
+ * XML from API level to permission
+ */
+ private static final String API_LEVEL_PERMISSION_MAP = "files/apilevel-permission-map.xml"; //$NON-NLS-1$
+
+ /**
+ * XML from feature to API level
+ */
+ private static final String FEATURE_API_LEVEL_MAP = "files/feature-apilevel-map.xml"; //$NON-NLS-1$
+
+ /**
+ * XML from permission to implied features
+ */
+ private static final String PERMISSION_FEATURE_MAP = "files/permission-feature-map.xml"; //$NON-NLS-1$
+
+ /**
+ * Text file from blocked permissions
+ */
+ private static final String BLOCKED_PERMISSIONS_LIST = "files/blocked_permissions.txt"; //$NON-NLS-1$
+
+ /**
+ * Collection of method signature to permission required
+ */
+ private static final String METHOD_PERMISSION_LIST = "files/method_permission_list_4.0.csv"; //$NON-NLS-1$
+
+ private static PlatformRules uniqInstance;
+
+ /**
+ * Map from api level (key) to {@link Permission}
+ */
+ private HashMap<Integer, ArrayList<Permission>> permissionsList = null;
+
+ // private HashMap<Integer, ArrayList<Feature>> featuresList = null;
+
+ /**
+ * Map from api level (key) to {@link Feature}
+ */
+ private HashMap<Integer, Feature> featuresList = null;
+
+ /**
+ * Map from method signature (key) to permissions required
+ */
+ private Map<String, PermissionGroups> methodToPermissionGroup =
+ new HashMap<String, PermissionGroups>();
+
+ //private final ArrayList<String> permissionsBySignatureList = new ArrayList<String>();
+
+ private PermissionToFeatureMapping permissionToFeatureMapping = null;
+
+ /**
+ * List of blocked permissions
+ */
+ private final ArrayList<String> blockedPermissionsList = new ArrayList<String>();
+
+ /**
+ * <li>API level 1 <li>Android platform 1.0
+ */
+ public static final int API_LEVEL_1 = 1;
+
+ /**
+ * <li>API level 2 <li>Android platform 1.1
+ */
+ public static final int API_LEVEL_2 = 2;
+
+ /**
+ * <li>API level 3 <li>Android platform 1.5 (Cupcake) <li>Based on Linux
+ * Kernel 2.6.27
+ */
+ public static final int API_LEVEL_3 = 3;
+
+ /**
+ * <li>API level 4 <li>Android platform 1.6 (Donut) <li>Based on Linux
+ * Kernel 2.6.29
+ */
+ public static final int API_LEVEL_4 = 4;
+
+ /**
+ * <li>API level 5 <li>Android platform 2.0 (Eclair) <li>Based on Linux
+ * Kernel 2.6.29
+ */
+ public static final int API_LEVEL_5 = 5;
+
+ /**
+ * <li>API level 6 <li>Android platform 2.0.1 (Eclair) <li>Based on Linux
+ * Kernel 2.6.29
+ */
+ public static final int API_LEVEL_6 = 6;
+
+ /**
+ * <li>API level 7 <li>Android platform 2.1 (Eclair) <li>Based on Linux
+ * Kernel 2.6.29
+ */
+ public static final int API_LEVEL_7 = 7;
+
+ /**
+ * <li>API level 8 <li>Android platform 2.2 (Froyo) <li>Based on Linux
+ * Kernel 2.6.32
+ */
+ public static final int API_LEVEL_8 = 8;
+
+ /**
+ * Singleton instance.
+ */
+ private PlatformRules()
+ {
+ loadAllPermissions();
+ loadAllFeatures();
+ loadPermissionToRequiredImpliedFeatures();
+ loadBlockedPermissions();
+ loadMethodToPermissionList();
+ }
+
+ /**
+ * Returns the unique instance of PlatformRules
+ *
+ * @return The unique instance of PlatformRules
+ */
+ public static synchronized PlatformRules getInstance()
+ {
+ if (uniqInstance == null)
+ {
+ uniqInstance = new PlatformRules();
+ }
+ return uniqInstance;
+ }
+
+ /**
+ * Returns a list of permissions related to a given Android API level.
+ *
+ * @param apiLevel The API Level. These values are constants of this {@link PlatformRules}
+ * class. You have {@link PlatformRules#API_LEVEL_1}, {@link PlatformRules#API_LEVEL_2} and so on.
+ *
+ * @return An ArrayList of Strings containing all permission of a given
+ * Android API level -OR- returns a null ArrayList if there are no
+ * permissions or if the API level number is not valid/supported.
+ */
+ public ArrayList<String> getPermissions(int apiLevel)
+ {
+ ArrayList<String> permissions = null;
+ if ((permissionsList != null) && (isApiLevelSupported(apiLevel))
+ && (permissionsList.containsKey(apiLevel)))
+ {
+ ArrayList<Permission> permissionsForThisLevel = null;
+ Set<Entry<Integer, ArrayList<Permission>>> entries = permissionsList.entrySet();
+ for (Entry<Integer, ArrayList<Permission>> entry : entries)
+ {
+ if (apiLevel == entry.getKey().intValue())
+ {
+ permissionsForThisLevel = entry.getValue();
+ }
+ }
+ if ((permissionsForThisLevel != null) && (!permissionsForThisLevel.isEmpty()))
+ {
+ permissions = new ArrayList<String>();
+ for (Permission permission : permissionsForThisLevel)
+ {
+ permissions.add(permission.getPermissionName());
+ }
+ }
+ }
+ return permissions;
+ }
+
+ /**
+ * Returns all features related to a given Android API level.
+ *
+ * @param apiLevel The API Level. These values are constants of this {@link PlatformRules}
+ * class. You have {@link PlatformRules#API_LEVEL_1}, {@link PlatformRules#API_LEVEL_2} and so on.
+ *
+ * @return An ArrayList of Strings containing all features of a given
+ * Android API level -OR- returns a null ArrayList if there are no
+ * features or if the API level number is not valid/supported.
+ */
+ public ArrayList<String> getFeatures(int apiLevel)
+ {
+ ArrayList<String> features = null;
+ if ((featuresList != null) && (isApiLevelSupported(apiLevel))
+ && (featuresList.containsKey(apiLevel)))
+ {
+ Set<Entry<Integer, Feature>> entries = featuresList.entrySet();
+ features = new ArrayList<String>();
+ for (Entry<Integer, Feature> entry : entries)
+ {
+ if (apiLevel == entry.getKey().intValue())
+ {
+ features.add(entry.getValue().getFeatureID());
+ }
+ }
+ }
+ return features;
+ }
+
+ /**
+ * Returns all permissions related to a given method signature.
+ *
+ * @param signature The method's signature. In order to better understand how these
+ * signatures are created, see {@link Invoke#getQualifiedName()}, {@link SourceFolderElement#getInvokedMethods()} documentation.
+ * The {@link Invoke#getQualifiedName()} method returns this signature.
+ *
+ * @return Given a Method signature, its {@link PermissionGroups}
+ * are returned.
+ *
+ * @see {@link Invoke#getQualifiedName()}, {@link SourceFolderElement#getInvokedMethods()}
+ */
+ public PermissionGroups getPermissionsForMethod(String signature)
+ {
+ return methodToPermissionGroup.get(signature);
+ }
+
+ /**
+ * Verifies if the API level number is valid/supported.
+ *
+ * @param apiLevel The API Level. These values are constants of this {@link PlatformRules}
+ * class. You have {@link PlatformRules#API_LEVEL_1}, {@link PlatformRules#API_LEVEL_2} and so on.
+ *
+ * @return Returns <code>true</code> in case the API Level is supported,
+ * <code>false</code> otherwise.
+ */
+ public boolean isApiLevelSupported(int apiLevel)
+ {
+ boolean supported = false;
+ if ((apiLevel >= API_LEVEL_1) && (apiLevel <= API_LEVEL_8))
+ {
+ supported = true;
+ }
+ return supported;
+ }
+
+ /**
+ * Load all permission.
+ */
+ private void loadAllPermissions()
+ {
+ ArrayList<Permission> permissions = null;
+ DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
+ DocumentBuilder documentBuilder = null;
+ NodeList apiLevelNodes = loadXmlFiles(builderFactory, documentBuilder, "api_level", //$NON-NLS-1$
+ // "C:\\tmp\\apilevel-permission-map.xml"); // for JUnit purposes. //$NON-NLS-1$
+ API_LEVEL_PERMISSION_MAP);
+ int apiLevel = 0;
+
+ if ((apiLevelNodes != null) && (apiLevelNodes.getLength() > 0))
+ {
+ permissions = new ArrayList<Permission>();
+ for (int i = 0; i < apiLevelNodes.getLength(); i++)
+ {
+ Element apiLevelTag = (Element) apiLevelNodes.item(i);
+ apiLevel = Integer.parseInt(apiLevelTag.getAttribute("number")); //$NON-NLS-1$
+ NodeList permissionNodes = apiLevelTag.getElementsByTagName("permission"); //$NON-NLS-1$
+ for (int j = 0; j < permissionNodes.getLength(); j++)
+ {
+ Element permissionTag = (Element) permissionNodes.item(j);
+ if (permissionTag != null)
+ {
+ String name = null;
+ String description = null;
+ boolean deprecated = false;
+ try
+ {
+ name = getChildNodes(permissionTag.getElementsByTagName("name")); //$NON-NLS-1$
+ description =
+ getChildNodes(permissionTag.getElementsByTagName("description")); //$NON-NLS-1$
+ deprecated =
+ Boolean.parseBoolean(getChildNodes(permissionTag
+ .getElementsByTagName("deprecated"))); //$NON-NLS-1$
+ }
+ catch (Exception e)
+ {
+
+ e.printStackTrace();
+ }
+ if (name != null)
+ {
+ Permission permission =
+ new Permission(apiLevel, name, description, deprecated);
+ permissions.add(permission);
+ }
+ }
+ }
+ }
+ if ((!permissions.isEmpty()) && (apiLevel != 0))
+ {
+ permissionsList = new HashMap<Integer, ArrayList<Permission>>();
+ permissionsList.put(apiLevel, permissions);
+ }
+ }
+ }
+
+ /**
+ * Load all features.
+ */
+ private void loadAllFeatures()
+ {
+ DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
+ DocumentBuilder documentBuilder = null;
+ NodeList featureApiNodes = loadXmlFiles(builderFactory, documentBuilder, "feature", //$NON-NLS-1$
+ // "C:\\tmp\\feature-apilevel-map.xml"); // for JUnit purposes. //$NON-NLS-1$
+ FEATURE_API_LEVEL_MAP);
+ int apiLevel = 0;
+
+ if ((featureApiNodes != null) && (featureApiNodes.getLength() > 0))
+ {
+ featuresList = new HashMap<Integer, Feature>();
+ for (int i = 0; i < featureApiNodes.getLength(); i++)
+ {
+ Element featureTag = (Element) featureApiNodes.item(i);
+ if (featureTag != null)
+ {
+ String featureID = null;
+ int sinceApiLevel = 0;
+ try
+ {
+ featureID = getChildNodes(featureTag.getElementsByTagName("id")); //$NON-NLS-1$
+ sinceApiLevel =
+ Integer.parseInt(getChildNodes(featureTag
+ .getElementsByTagName("since_apilevel"))); //$NON-NLS-1$
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ }
+ if ((featureID != null) && (isApiLevelSupported(sinceApiLevel)))
+ {
+ Feature feature = new Feature(featureID, sinceApiLevel);
+ featuresList.put(apiLevel, feature);
+ }
+ }
+ }
+ }
+
+ }
+
+ /**
+ * Load XML file into a {@link NodeList} structure. The basis for
+ * the {@link NodeList} is the tag paramter.
+ *
+ * @param builderFactory Builder factory object.
+ * @param documentBuilder Builder object.
+ * @param tag Tag which will serve as the basis for bringing
+ * the {@link NodeList}.
+ * @param fileName The XML file Name.
+ *
+ * @return Returns a {@link NodeList} from a XML file based on
+ * the tag parameter.
+ */
+ private NodeList loadXmlFiles(DocumentBuilderFactory builderFactory,
+ DocumentBuilder documentBuilder, String tag, String fileName)
+ {
+ try
+ {
+ documentBuilder = builderFactory.newDocumentBuilder();
+ }
+ catch (ParserConfigurationException e1)
+ {
+
+ e1.printStackTrace();
+ }
+ Document doc = null;
+ try
+ {
+ // For JUnit tests purpose
+ // doc = documentBuilder.parse(new File(fileName));
+ // -------------------------
+ doc =
+ documentBuilder.parse(PreflightingCorePlugin.getContext().getBundle()
+ .getEntry(fileName).openStream());
+ }
+ catch (SAXException e1)
+ {
+ // TODO Auto-generated catch block
+ e1.printStackTrace();
+ }
+ catch (IOException e1)
+ {
+ // TODO Auto-generated catch block
+ e1.printStackTrace();
+ }
+ return doc.getDocumentElement().getElementsByTagName(tag);
+ }
+
+ /**
+ * Given a {@link NodeList} consider only the first element. Return
+ * its first child´s value.
+ *
+ * @param node {@link NodeList} where only the first child will
+ * be considered.
+ *
+ * @return Returns the value of the {@link NodeList}´s first element´s
+ * first child.
+ *
+ * @throws DOMException Exception thrown in case there are problems
+ * handling the {@link NodeList}.
+ */
+ private String getChildNodes(NodeList node) throws DOMException
+ {
+ if ((node != null) && (node.getLength() > 0))
+ {
+ Element nameElement = (Element) node.item(0);
+ if ((nameElement != null) && (nameElement.hasChildNodes()))
+ {
+ Node childTag = nameElement.getFirstChild();
+ if (childTag != null)
+ {
+ return childTag.getNodeValue();
+ }
+ }
+ }
+ return null;
+ }
+
+ private void loadMethodToPermissionList()
+ {
+ InputStream csvStream = null;
+ InputStreamReader streamReader = null;
+ try
+ {
+ csvStream =
+ PreflightingCorePlugin.getContext().getBundle()
+ .getEntry(METHOD_PERMISSION_LIST).openStream();
+ streamReader = new InputStreamReader(csvStream);
+ methodToPermissionGroup =
+ MethodPermissionCSVReader.readMapMethodToPermission(streamReader);
+ }
+ catch (IOException e)
+ {
+ PreflightingLogger
+ .error("Problem reading " + METHOD_PERMISSION_LIST + " exception: " + e.getLocalizedMessage()); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+ finally
+ {
+ if (streamReader != null)
+ {
+ try
+ {
+ streamReader.close();
+ }
+ catch (IOException e)
+ {
+ //Do Nothing.
+ }
+ }
+ try
+ {
+ if (csvStream != null)
+ {
+ csvStream.close();
+ }
+ }
+ catch (IOException e)
+ {
+ //do nothing
+ }
+ }
+ }
+
+ private void loadBlockedPermissions()
+ {
+ InputStream txtStream = null;
+ BufferedReader br = null;
+ try
+ {
+ txtStream =
+ PreflightingCorePlugin.getContext().getBundle()
+ .getEntry(BLOCKED_PERMISSIONS_LIST).openStream();
+ br = new BufferedReader(new InputStreamReader(txtStream));
+
+ String line = br.readLine();
+ while (line != null)
+ {
+ blockedPermissionsList.add(line.trim());
+ line = br.readLine();
+ }
+ }
+ catch (IOException e)
+ {
+ PreflightingLogger.error("Problem reading " + BLOCKED_PERMISSIONS_LIST + " exception: "
+ + e.getLocalizedMessage()); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+ finally
+ {
+ if (br != null)
+ {
+ try
+ {
+ br.close();
+ }
+ catch (IOException e)
+ {
+ //Do Nothing.
+ }
+ }
+ try
+ {
+ if (txtStream != null)
+ {
+ txtStream.close();
+ }
+ }
+ catch (IOException e)
+ {
+ //do nothing
+ }
+ }
+ }
+
+ /**
+ * Checks whether permission is blocked.
+ *
+ * @param permission Identifier of the permission (as specified in <code>android:name</code> tag inside AndroidManifest.xml)
+ *
+ * @return Returns <code>true</code> if blocked (need special system certificate), false if not
+ */
+ public boolean isPermissionBlocked(String permission)
+ {
+ return blockedPermissionsList.contains(permission);
+ }
+
+ private void loadPermissionToRequiredImpliedFeatures()
+ {
+ InputStream xmlStream;
+ try
+ {
+ xmlStream =
+ PreflightingCorePlugin.getContext().getBundle()
+ .getEntry(PERMISSION_FEATURE_MAP).openStream();
+ PermissionToFeatureMapReader permissionToFeatureReader =
+ new PermissionToFeatureMapReader(xmlStream);
+ permissionToFeatureMapping = permissionToFeatureReader.read();
+ }
+ catch (Exception e)
+ {
+ PreflightingLogger.error("Problem reading " + PERMISSION_FEATURE_MAP + " exception: "
+ + e.getLocalizedMessage());
+ }
+ }
+
+ /**
+ * Gets the list of required features for a certain permission. In case none
+ * is required, <code>null</code> is returned.
+ *
+ * @param permissionId Permision identifier which the required featured will be
+ * fetched.
+ *
+ * @return Returns the list of required features for a certain permission. In case none
+ * is required, <code>null</code> is returned.
+ */
+ public Set<com.motorolamobility.preflighting.core.permissionfeature.Feature> getImpliedFeaturesSet(
+ String permissionId)
+ {
+ Set<com.motorolamobility.preflighting.core.permissionfeature.Feature> features =
+ new HashSet<com.motorolamobility.preflighting.core.permissionfeature.Feature>();
+ if (permissionToFeatureMapping.getRequiredImpliedFeaturesForPermission(permissionId) != null)
+ {
+ features.addAll(permissionToFeatureMapping
+ .getRequiredImpliedFeaturesForPermission(permissionId));
+ }
+ return features;
+ }
+
+ /**
+ * Given a certain category, all permissions related to it are retrieved.
+ *
+ * @param categoryName Category name which the related permissions will be fetched.
+ *
+ * @return Returns the list of permissions for a certain category.
+ */
+ public Set<com.motorolamobility.preflighting.core.permissionfeature.Permission> getPermissionsForCategory(
+ String categoryName)
+ {
+ Set<com.motorolamobility.preflighting.core.permissionfeature.Permission> permissions =
+ new HashSet<com.motorolamobility.preflighting.core.permissionfeature.Permission>();
+ if (permissionToFeatureMapping.getPermissionsForCategory(categoryName) != null)
+ {
+ permissions.addAll(permissionToFeatureMapping.getPermissionsForCategory(categoryName));
+ }
+ return permissions;
+ }
+
+ /**
+ * An inner class to represent a set of permissions for a given API level.
+ */
+ class Permission
+ {
+ private int apLevelNumber = 0;
+
+ private String permissionName = null;
+
+ private String permissionDescription = null;
+
+ private boolean deprecated = false;
+
+ /**
+ * Constructor which load a set of parameters.
+ *
+ * @param apiLevel API level.
+ * @param name Permission name.
+ * @param description Permission description.
+ * @param deprecated A flag indicating whether the permission is deprecated.
+ */
+ Permission(int apiLevel, String name, String description, boolean deprecated)
+ {
+ apLevelNumber = apiLevel;
+ permissionName = name;
+ permissionDescription = (description != null) ? description : ""; //$NON-NLS-1$
+ this.deprecated = deprecated;
+ }
+
+ /**
+ * Gets the permission´s name.
+ *
+ * @return Returns the permission name.
+ */
+ private String getPermissionName()
+ {
+ return permissionName;
+ }
+
+ /**
+ * Sets the permission´s name.
+ *
+ * @param permissionName
+ * The permission name to set
+ */
+ @SuppressWarnings("unused")
+ private void setPermissionName(String permissionName)
+ {
+ this.permissionName = permissionName;
+ }
+
+ /**
+ * Gets the permission´s description.
+ *
+ * @return Returns the permission description
+ */
+ @SuppressWarnings("unused")
+ private String getPermissionDescription()
+ {
+ return permissionDescription;
+ }
+
+ /**
+ * Sets the permission´s description.
+ *
+ * @param permissionDescription
+ * the permission description to set.
+ */
+ @SuppressWarnings("unused")
+ private void setPermissionDescription(String permissionDescription)
+ {
+ this.permissionDescription = permissionDescription;
+ }
+
+ /**
+ * The a flag indicating whether the permission is deprecated.
+ *
+ * @return the Returns the flag indicating whether a permission
+ * is deprecated.
+ */
+ @SuppressWarnings("unused")
+ private boolean isDeprecated()
+ {
+ return deprecated;
+ }
+
+ /**
+ * Set a flag indicating whether the permission is deprecated.
+ *
+ * @param permissionDescription
+ * The flag for deprecation to be set.
+ */
+ @SuppressWarnings("unused")
+ private void setDeprecated(boolean deprecated)
+ {
+ this.deprecated = deprecated;
+ }
+
+ /**
+ * Get the API Level related to this permission.
+ *
+ * @return Returns the API Level related to this permission.
+ */
+ @SuppressWarnings("unused")
+ private int getApLevelNumber()
+ {
+ return apLevelNumber;
+ }
+ }
+
+ /**
+ * An inner class to represent a set of features and their first API level.
+ */
+ class Feature
+ {
+ private String featureID = null;
+
+ private int sinceApiLevel = 0;
+
+ /**
+ * Constructor which sets necessary parameters.
+ *
+ * @param featureID Feature identifier.
+ * @param sinceApiLevel Since-when API Level.
+ */
+ Feature(String featureID, int sinceApiLevel)
+ {
+ this.featureID = featureID;
+ this.sinceApiLevel = sinceApiLevel;
+ }
+
+ /**
+ * Gets the feature Id.
+ *
+ * @return Returns the feature Id.
+ */
+ private String getFeatureID()
+ {
+ return featureID;
+ }
+
+ /**
+ * Sets the feature Id.
+ *
+ * @param featureID
+ * The feature Id to be set.
+ */
+ @SuppressWarnings("unused")
+ private void setFeatureID(String featureID)
+ {
+ this.featureID = featureID;
+ }
+
+ /**
+ * Get the Since-when API level.
+ *
+ * @return Returns the Since-when API Level.
+ */
+ @SuppressWarnings("unused")
+ private int getSinceApiLevel()
+ {
+ return sinceApiLevel;
+ }
+
+ /**
+ * Sets the Since-When API level.
+ *
+ * @param sinceApiLevel
+ * Sets the Since-When API Level.
+ */
+ @SuppressWarnings("unused")
+ private void setSinceApiLevel(int sinceApiLevel)
+ {
+ this.sinceApiLevel = sinceApiLevel;
+ }
+ }
+}
diff --git a/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/exception/PreflightingCheckerException.java b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/exception/PreflightingCheckerException.java
new file mode 100644
index 0000000..49aa8cd
--- /dev/null
+++ b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/exception/PreflightingCheckerException.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.preflighting.core.exception;
+
+/**
+ * Exception class representing an exceptional error on a checker's execution.
+ */
+@SuppressWarnings("serial")
+public class PreflightingCheckerException extends Exception
+{
+
+ /**
+ * Instantiates the {@link PreflightingCheckerException} passing the error message.
+ *
+ * @param message Message which originated this exception.
+ */
+ public PreflightingCheckerException(String message)
+ {
+ super(message);
+ }
+
+ /**
+ * Instantiates the {@link PreflightingCheckerException} passing the message and the {@link Throwable}
+ * which causes this error.
+ *
+ * @param message Message which originated the error.
+ * @param cause {@link Throwable} which originated the error.
+ */
+ public PreflightingCheckerException(String message, Throwable cause)
+ {
+ super(message, cause);
+ }
+}
diff --git a/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/exception/PreflightingExtensionPointException.java b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/exception/PreflightingExtensionPointException.java
new file mode 100644
index 0000000..6431e3c
--- /dev/null
+++ b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/exception/PreflightingExtensionPointException.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.preflighting.core.exception;
+
+/**
+ * Exception class representing an error on extension point reading.
+ */
+@SuppressWarnings("serial")
+public class PreflightingExtensionPointException extends PreflightingToolException
+{
+
+ /**
+ * Instantiates the {@link PreflightingExtensionPointException} providing
+ * the error message which originated this exception.
+ *
+ * @param message Error message which originated this exception.
+ */
+ public PreflightingExtensionPointException(String message)
+ {
+ super(message);
+ }
+
+ /**
+ * Instantiates the {@link PreflightingExtensionPointException} providing the
+ * error message and {@link Throwable} which originated this exception.
+ *
+ * @param message Error message for this exception.
+ * @param cause {@link Throwable} which originated this exception.
+ */
+ public PreflightingExtensionPointException(String message, Throwable cause)
+ {
+ super(message, cause);
+ }
+}
diff --git a/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/exception/PreflightingParameterException.java b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/exception/PreflightingParameterException.java
new file mode 100644
index 0000000..701a02e
--- /dev/null
+++ b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/exception/PreflightingParameterException.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.preflighting.core.exception;
+
+/**
+ * Exception representing problem with interpreting input parameters.
+ */
+@SuppressWarnings("serial")
+public class PreflightingParameterException extends PreflightingToolException
+{
+ /**
+ * Instantiates the {@link PreflightingParameterException} providing
+ * the error message which originated this exception.
+ *
+ * @param message Error message which originated this exception.
+ */
+ public PreflightingParameterException(String message)
+ {
+ super(message);
+ }
+}
diff --git a/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/exception/PreflightingToolException.java b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/exception/PreflightingToolException.java
new file mode 100644
index 0000000..d0e150f
--- /dev/null
+++ b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/exception/PreflightingToolException.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.preflighting.core.exception;
+
+/**
+ * Exception class representing the failure to execute the preflighting tool.
+ */
+@SuppressWarnings("serial")
+public class PreflightingToolException extends Exception
+{
+
+ /**
+ * Instantiates the {@link PreflightingToolException} providing
+ * the error message which originated this exception.
+ *
+ * @param message Error message which originated this exception.
+ */
+ public PreflightingToolException(String message)
+ {
+ super(message);
+ }
+
+ /**
+ * Instantiates the {@link PreflightingToolException} providing the
+ * error message and {@link Throwable} which originated this exception.
+ *
+ * @param message Error message for this exception.
+ * @param cause {@link Throwable} which originated this exception.
+ */
+ public PreflightingToolException(String message, Throwable cause)
+ {
+ super(message, cause);
+ }
+}
diff --git a/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/exception/ValidationLimitException.java b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/exception/ValidationLimitException.java
new file mode 100644
index 0000000..b5fb5b9
--- /dev/null
+++ b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/exception/ValidationLimitException.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.preflighting.core.exception;
+
+/**
+ * This Exception must be used every time the validation limit is exceeded.
+ */
+public class ValidationLimitException extends ArrayIndexOutOfBoundsException
+{
+ private static final long serialVersionUID = -4575525459322334535L;
+
+ /**
+ * Constructs a <code>ValidationLimitException</code> with no
+ * detail message.
+ */
+ public ValidationLimitException()
+ {
+ }
+
+ /**
+ * Constructs a <code>ValidationLimitException</code> class
+ * with the specified detail message.
+ *
+ * @param s The detail message.
+ */
+ public ValidationLimitException(String s)
+ {
+ super(s);
+ }
+
+}
diff --git a/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/i18n/PreflightingCoreNLS.java b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/i18n/PreflightingCoreNLS.java
new file mode 100644
index 0000000..c3039ea
--- /dev/null
+++ b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/i18n/PreflightingCoreNLS.java
@@ -0,0 +1,233 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.preflighting.core.i18n;
+
+import org.eclipse.osgi.util.NLS;
+
+/**
+ * I18n
+ */
+public class PreflightingCoreNLS extends NLS
+{
+ private static final String BUNDLE_NAME =
+ "com.motorolamobility.preflighting.core.i18n.messages"; //$NON-NLS-1$
+
+ public static String Checker_MandatoryParam_EmptyValueWarn;
+
+ public static String CheckerExtensionReader_CheckerExtensionPointNotFound;
+
+ public static String CheckerExtensionReader_UnexpectedErrorCheckerExtensionPoint;
+
+ public static String GlobalInputParamsValidator_NonExistentApplicationPathMessage;
+
+ public static String GlobalInputParamsValidator_NonExistentSdkPathMessage;
+
+ public static String GlobalInputParamsValidator_SdkPathNotFolderMessage;
+
+ public static String GlobalInputParamsValidator_SdkPathNotValidSdkMessage;
+
+ public static String GlobalInputParamsValidator_UnknownCheckersForWarningLevelAdjustmentMessage;
+
+ public static String GlobalInputParamsValidator_UnknownParameterMessage;
+
+ public static String GlobalInputParamsValidator_AppPathParameterMissing;
+
+ public static String GlobalInputParamsValidator_RepeatedParameterErrorMessage;
+
+ public static String GlobalInputParamsValidator_SdkPathParameterMissing;
+
+ public static String GlobalInputParamsValidator_VerboseMessage_CheckingSystemPathForSDK;
+
+ public static String GlobalInputParamsValidator_VerboseMessage_UsingParamSDKPath;
+
+ public static String GlobalInputParamsValidator_VerboseMessage_UsingSystemSDKPath;
+
+ public static String GlobalInputParamsValidator_VerboseMessage_SDKPathNotFoundOnSystemPath;
+
+ public static String GlobalInputParamsValidator_WarningLevelAdjustmentAllCheckers;
+
+ public static String GlobalInputParamsValidator_WarningLevelAdjustmentFollowingCheckers;
+
+ public static String GlobalInputParamsValidator_WarningLevelAdjustmentParameterFoundMessage;
+
+ public static String GlobalInputParamsValidator_LimitParam;
+
+ public static String ProjectUtils_Error_Parsing_Manifest_DEBUG;
+
+ public static String ProjectUtils_Error_Parsing_Manifest_INFO;
+
+ public static String ProjectUtils_InvalidPathErrorMessage;
+
+ public static String ValidationManager_ErrorRetrievingApplicationData;
+
+ public static String ValidationManager_CheckerDescriptionMessage;
+
+ public static String ValidationManager_DeviceDescriptionMessage;
+
+ public static String ValidationManager_Errors_NoValidationApplyOrError;
+
+ public static String ValidationManager_ErrorToWarningDescriptionMessage;
+
+ public static String ValidationManager_OutputDescriptionMessage;
+
+ public static String ValidationManager_OutputSintaxMessage;
+
+ public static String ValidationManager_LimitDescription;
+
+ public static String ValidationManager_InputParametersProblemMessage;
+
+ public static String ValidationManager_SdkPathDescriptionMessage;
+
+ public static String ValidationManager_UnableToExecuteCheckerMessage;
+
+ public static String ValidationManager_UnableToExecuteCheckerMessage_Detailed;
+
+ public static String ValidationManager_NoConditionsReason;
+
+ public static String ValidationManager_UnableToExecuteCondition;
+
+ public static String ValidationManager_UnableToExecuteCondition_Detailed;
+
+ public static String ValidationManager_UnexpectedExceptiononChecker;
+
+ public static String ValidationManager_UnexpectedExceptiononChecker_V2;
+
+ public static String ValidationManager_UnknownCheckerMessage;
+
+ public static String ValidationManager_UnknownCheckerOrConditionMessage;
+
+ public static String ValidationManager_IncorrectSyntax;
+
+ public static String ValidationManager_InvalidParamType_Bool;
+
+ public static String ValidationManager_InvalidParamType_Int;
+
+ public static String ValidationManager_UnknownParameterMessage;
+
+ public static String ValidationManager_UnknownDeviceMessage;
+
+ public static String ValidationManager_ValidationLimitReached;
+
+ public static String ValidationManager_MandatoryParameterMissing;
+
+ public static String ValidationManager_CheckerWasDisabled;
+
+ public static String ValidationManager_ValidationManager_VerboseMessage_Skipping_Device_Verifications;
+
+ public static String ValidationManager_VerboseMessage_AllCheckersRun;
+
+ public static String ValidationManager_VerboseMessage_CheckerFinished;
+
+ public static String ValidationManager_VerboseMessage_ProblemsCheckerParameters;
+
+ public static String ValidationManager_VerboseMessage_ProblemsGlobalParameters;
+
+ public static String ValidationManager_VerboseMessage_RunningChecker;
+
+ public static String ValidationManager_VerboseMessage_UnknownParametersFound;
+
+ public static String ValidationManager_VerboseMessage_ValidatingGlobalParameters;
+
+ public static String ValidationManager_VerboseMessage_VerifyingCheckerRun;
+
+ public static String ValidationManager_WarningToErrorDescriptionMessage;
+
+ public static String ValidationResultData_ErrorSeverityMessage;
+
+ public static String ValidationResultData_FatalSeverityMessage;
+
+ public static String ValidationResultData_OkSeverityMessage;
+
+ public static String ValidationResultData_WarningSeverityMessage;
+
+ public static String VerboseOutputter_DebugVerboseLeveString;
+
+ public static String VerboseOutputter_InfoVerboseLevelString;
+
+ public static String ApkUtils_AaptExecutionProblemMessage;
+
+ public static String ApkUtils_AaptResultReadProblemMessage;
+
+ public static String ApkUtils_DomInstanceProblemMessage;
+
+ public static String ApkUtils_ImpossibleExtractAndroidPackageMessage;
+
+ public static String ApkUtils_ZipExtraction;
+
+ public static String ApkUtils_ZipExtractionFile;
+
+ public static String Device_Device;
+
+ public static String Device_SupportedFeatures;
+
+ public static String WarningLevelFilter_ErrorsCountMessage;
+
+ public static String WarningLevelFilter_ErrorsMessage;
+
+ public static String WarningLevelFilter_FatalErrorsCountMessage;
+
+ public static String WarningLevelFilter_FatalErrorsMessage;
+
+ public static String WarningLevelFilter_NoFatalErrorsMessage;
+
+ public static String WarningLevelFilter_NoFatalErrorsNorWarningsMessage;
+
+ public static String WarningLevelFilter_NoFatalNorErrorsMessage;
+
+ public static String WarningLevelFilter_NoProblemsMessage;
+
+ public static String WarningLevelFilter_TotalMessage;
+
+ public static String WarningLevelFilter_VerboseMessage_FilterningResult;
+
+ public static String WarningLevelFilter_VerboseMessage_ResultFiltered;
+
+ public static String WarningLevelFilter_WarningsCountMessage;
+
+ public static String ProjectUtils_ErrorReadingCertificate;
+
+ public static String ProjectUtils_ErrorExecutingApkTool;
+
+ public static String ProjectUtils_ErrorReadingJavaModel;
+
+ public static String ProjectUtils_ErrorReadingSourceFile;
+
+ public static String ProjectUtils_ErrorReadingDefaultPropertiesFile;
+
+ public static String ProjectUtils_ErrorReadingClasspathFile;
+
+ public static String Invalid_ManifestFile;
+
+ public static String JavaModelNotFound_Err;
+
+ public static String EmptyInvokedMethods_Err;
+
+ public static String NoSourceFilesFound_Err;
+
+ public static String ValidationManager_DisableCheckerDescriptionMessage;
+
+ public static String ApkToolUtils_MalformedAPK;
+
+ static
+ {
+ // initialize resource bundle
+ NLS.initializeMessages(BUNDLE_NAME, PreflightingCoreNLS.class);
+ }
+
+ private PreflightingCoreNLS()
+ {
+ }
+}
diff --git a/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/i18n/messages.properties b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/i18n/messages.properties
new file mode 100644
index 0000000..0b118ff
--- /dev/null
+++ b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/i18n/messages.properties
@@ -0,0 +1,101 @@
+ApkUtils_AaptExecutionProblemMessage=Problems executing aapt command.
+ApkUtils_AaptResultReadProblemMessage=Problems reading aapt command execution result.
+ApkUtils_DomInstanceProblemMessage=Problems creating DOM instance.
+ApkUtils_ImpossibleExtractAndroidPackageMessage=It was not possible to read the Android package.
+ApkUtils_ZipExtraction=Problem extracting zip file.
+ApkUtils_ZipExtractionFile=Problem extracting file {0}.
+Checker_MandatoryParam_EmptyValueWarn=Checker {0} will not be run. There must be a value for the mandatory parameter ID "{1}".
+CheckerExtensionReader_CheckerExtensionPointNotFound=Checker extension point not found
+CheckerExtensionReader_UnexpectedErrorCheckerExtensionPoint=Unexpected error with the checker extension point
+Device_Device=Device
+Device_SupportedFeatures=Supported Features:
+GlobalInputParamsValidator_NonExistentApplicationPathMessage=Application path does not exist:
+GlobalInputParamsValidator_NonExistentSdkPathMessage=SDK path does not exist:
+GlobalInputParamsValidator_SdkPathNotFolderMessage=SDK path is not a folder:
+GlobalInputParamsValidator_SdkPathNotValidSdkMessage=SDK path does not contain a valid SDK:
+GlobalInputParamsValidator_UnknownCheckersForWarningLevelAdjustmentMessage=The following checkers or conditions specified for warning level adjustment are unknown: {0}
+GlobalInputParamsValidator_UnknownParameterMessage=Unknown parameter:
+GlobalInputParamsValidator_AppPathParameterMissing=Application path parameter is missing. Specify the -help option for usage.
+GlobalInputParamsValidator_RepeatedParameterErrorMessage=Each warning level adjustment parameter should be passed only once; please remove the repeated {0} parameter.
+GlobalInputParamsValidator_SdkPathParameterMissing=Android SDK path parameter is missing. Specify the -help option for usage.
+GlobalInputParamsValidator_VerboseMessage_CheckingSystemPathForSDK=SDK path not found as an input parameter. Checking if it is available on the system path...
+GlobalInputParamsValidator_VerboseMessage_SDKPathNotFoundOnSystemPath=SDK path not found on the system path
+GlobalInputParamsValidator_VerboseMessage_UsingParamSDKPath=Using SDK path passed as parameter
+GlobalInputParamsValidator_VerboseMessage_UsingSystemSDKPath=Using system SDK path
+GlobalInputParamsValidator_WarningLevelAdjustmentAllCheckers=All checker conditions will have validation result warning levels adjusted.
+GlobalInputParamsValidator_WarningLevelAdjustmentFollowingCheckers=The following checkers and/or conditions will have validation result warning levels adjusted: {0}
+GlobalInputParamsValidator_WarningLevelAdjustmentParameterFoundMessage=Warning level adjustment parameter detected; verifying which checkers and/or conditions will have validation result warning levels adjusted.
+GlobalInputParamsValidator_LimitParam=Value for the "limit" parameter is invalid.
+ProjectUtils_Error_Parsing_Manifest_DEBUG=Error parsing {0} at line {1} - {2}
+ProjectUtils_Error_Parsing_Manifest_INFO=Could not parse {0}. Some checkers may fail to execute.
+ProjectUtils_InvalidPathErrorMessage=The path does not point to either a valid Android project or a valid Android package.
+
+ValidationManager_ErrorRetrievingApplicationData=An error occurred while retrieving data from the analyzed Android application.
+ValidationManager_CheckerDescriptionMessage=Specify a checker to be run by supplying its ID (CHK) and specific checker parameters (PRM).
+ValidationManager_DisableCheckerDescriptionMessage=Specify a checker (CHK) or condition (CHK.CND) that should not run by supplying its ID. The other checkers will run normally.
+ValidationManager_DeviceDescriptionMessage=Specify a device [DEV] that will be checked against your application for incompatibilities. Use 'none' to skip device verification.
+ValidationManager_Errors_NoValidationApplyOrError=No validation was performed. None of the selected checkers apply for this application.
+ValidationManager_ErrorToWarningDescriptionMessage=Decreases the warning level for all or for the particular set of checkers or conditions specified by CHK and CND IDs
+ValidationManager_OutputDescriptionMessage=Format output to the specified type. TYPE can be [text (default) | csv | xml].
+ValidationManager_OutputSintaxMessage=[TYPE]
+ValidationManager_LimitDescription=Limit the number of output entries.
+ValidationManager_InputParametersProblemMessage=Problems found with input parameters.
+ValidationManager_SdkPathDescriptionMessage=Specify an Android SDK to be used during validation instead of the default one
+ValidationManager_UnableToExecuteCheckerMessage=Checker {0} could not execute. For details, increase the verbosity level.
+ValidationManager_NoConditionsReason=There are no eligible conditions to run.
+ValidationManager_UnableToExecuteCheckerMessage_Detailed=Checker {0} could not execute. Reason: {1}
+ValidationManager_UnableToExecuteCondition=Condition {0} from checker {1} could not execute. For details increase the verbosity level.
+ValidationManager_UnableToExecuteCondition_Detailed=Condition {0} from checker {1} could not execute. Reason: {2}
+ValidationManager_UnexpectedExceptiononChecker=An exception occurred while executing the checker {0}
+ValidationManager_UnexpectedExceptiononChecker_V2=Exception {0} occurred during execution of checker {1}
+ValidationManager_UnknownCheckerMessage=Unknown checker "{0}"
+ValidationManager_UnknownCheckerOrConditionMessage=Unknown checker or condition "{0}"
+ValidationManager_IncorrectSyntax=Incorrect syntax: -{0}\nCheck usage the with -help switch
+ValidationManager_InvalidParamType_Bool=The parameter value "{0}" for checker parameter ID "{1}" is not a valid boolean value.
+ValidationManager_InvalidParamType_Int=The parameter value "{0}" for checker parameter ID "{1}" is not a valid integer value.
+ValidationManager_UnknownParameterMessage=Unknown parameter:
+ValidationManager_UnknownDeviceMessage=Unknown device {0}\n
+ValidationManager_ValidationLimitReached=Validation limit reached, {0}\!
+ValidationManager_MandatoryParameterMissing=A mandatory parameter is missing.
+ValidationManager_CheckerWasDisabled=Checker was disabled.
+ValidationManager_ValidationManager_VerboseMessage_Skipping_Device_Verifications=Parameter '-d none' was specified. Skipping device verifications.
+ValidationManager_VerboseMessage_AllCheckersRun=No specific checker specified; all checkers will be run
+ValidationManager_VerboseMessage_CheckerFinished=Checker {0} finished running
+ValidationManager_VerboseMessage_ProblemsCheckerParameters=Problems found with checker parameters
+ValidationManager_VerboseMessage_ProblemsGlobalParameters=Problems found with global parameters
+ValidationManager_VerboseMessage_RunningChecker=Running checker {0}...
+ValidationManager_VerboseMessage_UnknownParametersFound=Unknown parameters found
+ValidationManager_VerboseMessage_ValidatingGlobalParameters=Validating global parameters...
+ValidationManager_VerboseMessage_VerifyingCheckerRun=Verifying if checker {0} can be run...
+ValidationManager_WarningToErrorDescriptionMessage=Increases the warning level for all or for a particular set of checkers or conditions specified by CHK and CND IDs
+ValidationResultData_ErrorSeverityMessage=ERROR
+ValidationResultData_FatalSeverityMessage=FATAL ERROR
+ValidationResultData_OkSeverityMessage=OK
+ValidationResultData_WarningSeverityMessage=WARNING
+VerboseOutputter_DebugVerboseLeveString=DEBUG\t-\
+VerboseOutputter_InfoVerboseLevelString=INFO\t-\
+WarningLevelFilter_ErrorsCountMessage={0} Error(s)
+WarningLevelFilter_ErrorsMessage=Errors were found in the specified Android application.
+WarningLevelFilter_FatalErrorsCountMessage={0} Fatal error(s)
+WarningLevelFilter_FatalErrorsMessage=Fatal errors were found in the specified Android application.
+WarningLevelFilter_NoFatalErrorsMessage=No fatal errors were found in the specified Android application.
+WarningLevelFilter_NoFatalErrorsNorWarningsMessage=No fatal errors, errors, or warnings were found in the specified Android application.
+WarningLevelFilter_NoFatalNorErrorsMessage=No fatal errors or errors were found in the specified Android application.
+WarningLevelFilter_NoProblemsMessage=No problems were found in the specified Android application.
+WarningLevelFilter_TotalMessage=TOTAL:\
+WarningLevelFilter_VerboseMessage_FilterningResult=Filtering validation result according to the set warning level...
+WarningLevelFilter_VerboseMessage_ResultFiltered=Finished filtering validation result according to the set warning level
+WarningLevelFilter_WarningsCountMessage={0} Warning(s)
+
+ProjectUtils_ErrorReadingCertificate=Error while reading certificate
+ProjectUtils_ErrorExecutingApkTool=Error executing ApkTool
+ProjectUtils_ErrorReadingJavaModel = Problem loading Java model
+ProjectUtils_ErrorReadingSourceFile=Error reading source files:
+ProjectUtils_ErrorReadingDefaultPropertiesFile=Some problems were found with your project's default.properties.
+ProjectUtils_ErrorReadingClasspathFile="Some problems were found with your classpath for the project.
+
+Invalid_ManifestFile=Android manifest is invalid. Verify it.
+EmptyInvokedMethods_Err=Invoked methods model is empty.
+JavaModelNotFound_Err=Java model not found.
+NoSourceFilesFound_Err=No source files found.
+ApkToolUtils_MalformedAPK=Error while unpacking due to malformed APK file.
diff --git a/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/internal/checker/CheckerExtensionReader.java b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/internal/checker/CheckerExtensionReader.java
new file mode 100644
index 0000000..2c77930
--- /dev/null
+++ b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/internal/checker/CheckerExtensionReader.java
@@ -0,0 +1,281 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.preflighting.core.internal.checker;
+
+import static com.motorolamobility.preflighting.core.logging.PreflightingLogger.error;
+import static com.motorolamobility.preflighting.core.logging.PreflightingLogger.warn;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IConfigurationElement;
+import org.eclipse.core.runtime.IExtension;
+import org.eclipse.core.runtime.IExtensionPoint;
+import org.eclipse.core.runtime.IExtensionRegistry;
+import org.eclipse.core.runtime.InvalidRegistryObjectException;
+import org.eclipse.core.runtime.Platform;
+
+import com.motorolamobility.preflighting.core.PreflightingCorePlugin;
+import com.motorolamobility.preflighting.core.checker.Checker;
+import com.motorolamobility.preflighting.core.checker.CheckerExtension;
+import com.motorolamobility.preflighting.core.checker.IChecker;
+import com.motorolamobility.preflighting.core.checker.condition.ICondition;
+import com.motorolamobility.preflighting.core.checker.parameter.CheckerParameter;
+import com.motorolamobility.preflighting.core.checker.parameter.ICheckerParameter;
+import com.motorolamobility.preflighting.core.exception.PreflightingExtensionPointException;
+import com.motorolamobility.preflighting.core.i18n.PreflightingCoreNLS;
+import com.motorolamobility.preflighting.core.internal.checkerparameter.CheckerParameterElement;
+import com.motorolamobility.preflighting.core.internal.conditions.ConditionElement;
+import com.motorolamobility.preflighting.core.validation.ParameterType;
+import com.motorolamobility.preflighting.core.validation.ValidationResultData.SEVERITY;
+
+public abstract class CheckerExtensionReader
+{
+ /**
+ * Load existing checkers, mapped by checker id (value used to call the checker
+ * on command line).
+ * The map is sorted alphabetically by checker id.
+ *
+ * @param checkersMap The map of checkers to be populated
+ *
+ * @throws PreflightingExtensionPointException If an error with this extension point is found,
+ * the exception is thrown to warn the framework that this functionality is broken
+ */
+ public static void loadCheckers(TreeMap<String, CheckerExtension> checkersMap)
+ throws PreflightingExtensionPointException
+ {
+ checkersMap.clear();
+
+ IExtensionRegistry extReg = Platform.getExtensionRegistry();
+ IExtensionPoint extPoint =
+ extReg.getExtensionPoint(CheckerExtension.CHECKER_EXTENSION_POINT_ID);
+ // it should not be null, but check to prevent errors
+ if (extPoint != null)
+ {
+ try
+ {
+ IExtension[] extensions = extPoint.getExtensions();
+ for (IExtension aExtension : extensions)
+ {
+ IConfigurationElement[] configElements = aExtension.getConfigurationElements();
+ for (IConfigurationElement aConfig : configElements)
+ {
+ if (aConfig.getName().equals(
+ CheckerExtension.CHECKER_EXTENSION_POINT_ELEMENT_CHECKER))
+ {
+ try
+ {
+ String id =
+ aConfig.getAttribute(CheckerExtension.CHECKER_EXTENSION_POINT_ATTRIBUTE_ID);
+ String name =
+ aConfig.getAttribute(CheckerExtension.CHECKER_EXTENSION_POINT_ATTRIBUTE_NAME);
+ String description =
+ aConfig.getAttribute(CheckerExtension.CHECKER_EXTENSION_POINT_ATTRIBUTE_DESCRIPTION);
+
+ String checkerClassName =
+ aConfig.getAttribute(CheckerExtension.CHECKER_EXTENSION_POINT_ATTRIBUTE_CLASS);
+ IChecker checker = null;
+ if (checkerClassName != null)
+ {
+ checker =
+ (IChecker) aConfig
+ .createExecutableExtension(CheckerExtension.CHECKER_EXTENSION_POINT_ATTRIBUTE_CLASS);
+ }
+ else
+ {
+ checker = new Checker();
+ }
+ checker.setId(id);
+ checker.setEnabled(true);
+ CheckerExtension checkerExtension =
+ new CheckerExtension(id, name, description, checker);
+
+ loadConditions(checkerExtension, aConfig);
+
+ loadParameters(checkerExtension, aConfig);
+
+ checkersMap.put(id, checkerExtension);
+ }
+ catch (CoreException ce)
+ {
+ warn(CheckerExtensionReader.class,
+ "Error reading checker extension of id " //$NON-NLS-1$
+ + aExtension.getUniqueIdentifier()
+ + ": invalid checker or condition class", ce);
+ }
+ catch (Exception e)
+ {
+ warn(CheckerExtensionReader.class,
+ "Error reading checker extension of id " //$NON-NLS-1$
+ + aExtension.getUniqueIdentifier(), e);
+ }
+ }
+ }
+ }
+ }
+ catch (InvalidRegistryObjectException e)
+ {
+ error(CheckerExtensionReader.class,
+ "Unexpected error with the checker extension point", e); //$NON-NLS-1$
+ throw new PreflightingExtensionPointException(
+ PreflightingCoreNLS.CheckerExtensionReader_UnexpectedErrorCheckerExtensionPoint,
+ e);
+ }
+ }
+ else
+ {
+ error(CheckerExtensionReader.class, "Checker extension point not found"); //$NON-NLS-1$
+ throw new PreflightingExtensionPointException(
+ PreflightingCoreNLS.CheckerExtensionReader_CheckerExtensionPointNotFound);
+ }
+ }
+
+ /*
+ * Load all children elements from a given checker element.
+ * Currently only conditions are supported.
+ */
+ private static void loadConditions(CheckerExtension checkerExtension,
+ IConfigurationElement checkerElement) throws CoreException
+ {
+ IConfigurationElement[] childrenElements = checkerElement.getChildren();
+ if (childrenElements.length > 0)
+ {
+ List<ConditionElement> conditionsList =
+ new ArrayList<ConditionElement>(childrenElements.length);
+ HashMap<String, ICondition> checkerConditions =
+ new HashMap<String, ICondition>(childrenElements.length);
+ for (IConfigurationElement childElement : childrenElements)
+ {
+ if (childElement.getName().equals(ConditionElement.CHECKER_CONDITION_ELEMENT_NAME)) //Condition found, load it!
+ {
+ String conditionId =
+ childElement
+ .getAttribute(ConditionElement.CHECKER_CONDITION_ATTRIBUTE_ID);
+ String conditionName =
+ childElement
+ .getAttribute(ConditionElement.CHECKER_CONDITION_ATTRIBUTE_NAME);
+ String conditionDesc =
+ childElement
+ .getAttribute(ConditionElement.CHECKER_CONDITION_ATTRIBUTE_DESCRIPTION);
+ String defaultSeverityLevel =
+ childElement
+ .getAttribute(ConditionElement.CHECKER_CONDITION_ATTRIBUTE_DEFAULT_SEVERITY);
+ String markerType =
+ childElement
+ .getAttribute(ConditionElement.CHECKER_CONDITION_ATTRIBUTE_MARKER_TYPE);
+ ICondition condition =
+ (ICondition) childElement
+ .createExecutableExtension(ConditionElement.CHECKER_CONDITION_ATTRIBUTE_CLASS);
+ condition.setId(conditionId);
+ condition.setName(conditionName);
+ condition.setDescription(conditionDesc);
+ condition.setSeverityLevel(SEVERITY.valueOf(defaultSeverityLevel));
+ condition.setChecker(checkerExtension.getChecker());
+ condition.setMarkerType(markerType);
+ PreflightingCorePlugin.addAvailableMarker(markerType);
+
+ ConditionElement conditionElement =
+ new ConditionElement(conditionId, conditionName, conditionDesc,
+ defaultSeverityLevel, condition);
+ conditionsList.add(conditionElement);
+ checkerConditions.put(conditionId, condition);
+ }
+ }
+ checkerExtension.getChecker().setConditions(checkerConditions);
+ checkerExtension.setConditions(conditionsList
+ .toArray(new ConditionElement[conditionsList.size()]));
+ }
+ }
+
+ /**
+ * Load the list of {@link CheckerParameterElement} objects into the {@link ConditionElement} parameter,
+ * This list comes from the defined extension-points.
+ *
+ * @param checkerExtension {@link CheckerExtension} where the {@link CheckerParameterElement} objects
+ * will be added.
+ * @param conditionConfigurationElement Configuration element. This element is responsible to fetch the
+ * data from the defined extension-points.
+ */
+ private static void loadParameters(CheckerExtension checkerExtension,
+ IConfigurationElement conditionConfigurationElement)
+ {
+ IConfigurationElement[] childrenElements = conditionConfigurationElement.getChildren();
+ if (childrenElements.length > 0)
+ {
+ List<CheckerParameterElement> parameterElementList =
+ new ArrayList<CheckerParameterElement>(childrenElements.length);
+ Map<String, ICheckerParameter> parameters = new HashMap<String, ICheckerParameter>();
+ for (IConfigurationElement childElement : childrenElements)
+ {
+ if (childElement.getName().equals(
+ CheckerParameterElement.CHECKER_PARAMETER_ELEMENT_NAME)) //Condition found, load it!
+ {
+ String parameterId =
+ childElement
+ .getAttribute(CheckerParameterElement.CHECKER_PARAMETER_ATTRIBUTE_ID);
+ String parameterName =
+ childElement
+ .getAttribute(CheckerParameterElement.CHECKER_PARAMETER_ATTRIBUTE_NAME);
+ String parameterDescription =
+ childElement
+ .getAttribute(CheckerParameterElement.CHECKER_PARAMETER_ATTRIBUTE_DESCRIPTION);
+ String parameterValueDescription =
+ childElement
+ .getAttribute(CheckerParameterElement.CHECKER_PARAMETER_ATTRIBUTE_VALUE_DESCRIPTION);
+ String parameterTypeString =
+ childElement
+ .getAttribute(CheckerParameterElement.CHECKER_PARAMETER_ATTRIBUTE_TYPE);
+ boolean parameterIsMandatory =
+ Boolean.parseBoolean(childElement
+ .getAttribute(CheckerParameterElement.CHECKER_PARAMETER_ATTRIBUTE_IS_MANDATORY));
+
+ ParameterType parameterType = null;
+ if (parameterTypeString.equals("BOOLEAN"))
+ {
+ parameterType = ParameterType.BOOLEAN;
+ }
+ else if (parameterTypeString.equals("INTEGER"))
+ {
+ parameterType = ParameterType.INTEGER;
+ }
+ else
+ {
+ parameterType = ParameterType.STRING;
+ }
+
+ ICheckerParameter parameter =
+ new CheckerParameter(parameterId, parameterName, parameterDescription,
+ parameterValueDescription, parameterType, parameterIsMandatory);
+
+ CheckerParameterElement parameterElement =
+ new CheckerParameterElement(parameterId, parameterName,
+ parameterDescription, parameterValueDescription,
+ parameterTypeString, parameterIsMandatory);
+
+ parameters.put(parameterId, parameter);
+ parameterElementList.add(parameterElement);
+ }
+ }
+ checkerExtension.getChecker().setParameters(parameters);
+ checkerExtension.setConditionParameters(parameterElementList
+ .toArray(new CheckerParameterElement[parameterElementList.size()]));
+ }
+ }
+}
diff --git a/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/internal/checkerparameter/CheckerParameterElement.java b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/internal/checkerparameter/CheckerParameterElement.java
new file mode 100644
index 0000000..3a78ab4
--- /dev/null
+++ b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/internal/checkerparameter/CheckerParameterElement.java
@@ -0,0 +1,228 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.motorolamobility.preflighting.core.internal.checkerparameter;
+
+import com.motorolamobility.preflighting.core.checker.IChecker;
+
+/**
+ * Bean class representing a Checker Parameter extension element.
+ */
+public final class CheckerParameterElement
+{
+ public static final String CHECKER_PARAMETER_ELEMENT_NAME = "parameter"; //$NON-NLS-1$
+
+ public static final String CHECKER_PARAMETER_ATTRIBUTE_ID = "id"; //$NON-NLS-1$
+
+ public static final String CHECKER_PARAMETER_ATTRIBUTE_NAME = "name"; //$NON-NLS-1$
+
+ public static final String CHECKER_PARAMETER_ATTRIBUTE_DESCRIPTION = "description"; //$NON-NLS-1$
+
+ public static final String CHECKER_PARAMETER_ATTRIBUTE_VALUE_DESCRIPTION = "valueDescription"; //$NON-NLS-1$
+
+ public static final String CHECKER_PARAMETER_ATTRIBUTE_IS_MANDATORY = "isMandatory"; //$NON-NLS-1$
+
+ public static final String CHECKER_PARAMETER_ATTRIBUTE_TYPE = "type"; //$NON-NLS-1$
+
+ private String id;
+
+ private String name;
+
+ private String value;
+
+ private String description;
+
+ private String valueDescription;
+
+ private boolean isMandatory;
+
+ private String type;
+
+ /**
+ * This constructors populates the beam with all properties.
+ *
+ * @param id Checker Parameter Id.
+ * @param name Checker Parameter Name.
+ * @param description Checker Parameter Description.
+ * @param valueDescription Value-specific description.
+ * @param type The type of this parameter.
+ * @param isMandatory Flag which determines whether this Checker
+ * Parameter is mandatory.
+ */
+ public CheckerParameterElement(String id, String name, String description,
+ String valueDescription, String type, boolean isMandatory)
+ {
+ this.id = id;
+ this.name = name;
+ this.description = description;
+ this.valueDescription = valueDescription;
+ this.type = type;
+ this.isMandatory = isMandatory;
+ }
+
+ /**
+ * Gets the Checker Parameter Id.
+ *
+ * @return Returns the Checker Parameter Id.
+ */
+ public String getId()
+ {
+ return id;
+ }
+
+ /**
+ * Sets the Checker Parameter Id.
+ *
+ * @param id The Checker Parameter Id to be set.
+ */
+ public void setId(String id)
+ {
+ this.id = id;
+ }
+
+ /**
+ * Gets Checker Parameter Name.
+ *
+ * @return Returns the Checker Parameter Name.
+ */
+ public String getName()
+ {
+ return name;
+ }
+
+ /**
+ * Sets the Checker Parameter Name.
+ *
+ * @param name The Checker Parameter Name to be set.
+ */
+ public void setName(String name)
+ {
+ this.name = name;
+ }
+
+ /**
+ * Gets the value entered for this parameter. This value is
+ * entered by the one who uses the {@link IChecker} which utilizes
+ * this parameter.
+ *
+ * @return Returns the value set by the {@link IChecker}´s user.
+ */
+ public String getValue()
+ {
+ return value;
+ }
+
+ /**
+ * Sets the value entered for this parameter. This value is
+ * entered by the one who uses the {@link IChecker} which utilizes
+ * this parameter.
+ *
+ * @param value The value set by the {@link IChecker}´s user to be set.
+ */
+ public void setValue(String value)
+ {
+ this.value = value;
+ }
+
+ /**
+ * Gets the Checker Parameter Description.
+ *
+ * @return Returns the Checker Parameter Description.
+ */
+ public String getDescription()
+ {
+ return description;
+ }
+
+ /**
+ * Sets the Checker Parameter Description.
+ *
+ * @param description The Checker Parameter to be set.
+ */
+ public void setDescription(String description)
+ {
+ this.description = description;
+ }
+
+ /**
+ * Gets the value-specific description.
+ *
+ * @return Returns the value-specific description.
+ */
+ public String getValueDescription()
+ {
+ return valueDescription;
+ }
+
+ /**
+ * Sets the value-specific description.
+ *
+ * @param valueDescription The value-specific description to set.
+ */
+ public void setValueDescription(String valueDescription)
+ {
+ this.valueDescription = valueDescription;
+ }
+
+ /**
+ * Gets a flag which determines whether this Checker Parameter
+ * is mandatory.
+ *
+ * @return Returns a flag which determines whether this Checker Parameter
+ * is mandatory.
+ */
+ public boolean isMandatory()
+ {
+ return isMandatory;
+ }
+
+ /**
+ * Sets a flag which determines whether this Checker Parameter
+ * is mandatory.
+ *
+ * @param isMandatory The flag which determines whether this Checker Parameter
+ * is mandatory to be set.
+ */
+ public void setMandatory(boolean isMandatory)
+ {
+ this.isMandatory = isMandatory;
+ }
+
+ /**
+ * Gets the Parameter´s type. This type represents the variable type
+ * of the parameter: String, Integer, Boolean and so on. They are
+ * defined in the parameter´s type extension-point.
+ *
+ * @return Returns the type.
+ */
+ public String getType()
+ {
+ return type;
+ }
+
+ /**
+ * Sets the Parameter´s type. This type represents the variable type
+ * of the parameter: String, Integer, Boolean and so on. They are
+ * defined in the parameter´s type extension-point.
+ *
+ * @param type The Parameter´s type to set.
+ */
+ public void setType(String type)
+ {
+ this.type = type;
+ }
+
+}
diff --git a/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/internal/cond/utils/ConditionUtils.java b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/internal/cond/utils/ConditionUtils.java
new file mode 100644
index 0000000..ad63e0e
--- /dev/null
+++ b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/internal/cond/utils/ConditionUtils.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.preflighting.core.internal.cond.utils;
+
+import com.motorolamobility.preflighting.core.validation.ValidationManagerConfiguration;
+
+/**
+ * Utility class for conditions
+ */
+public class ConditionUtils
+{
+ /**
+ * Computes the link with the checker and condition detailed description.
+ * This method returns the expected format for AppValidator Web.
+ * @param checkerId
+ * @param conditionId
+ * @return the URL for the given checker/condition description
+ */
+ public static String getDescriptionLink(String checkerId, String conditionId,
+ ValidationManagerConfiguration valManagerConfig)
+ {
+ String link =
+ valManagerConfig
+ .getProperty(ValidationManagerConfiguration.ConfigProperties.BASE_URL_PROPERTY
+ .getName());
+ if (link != null)
+ {
+ link +=
+ valManagerConfig
+ .getProperty(ValidationManagerConfiguration.ConfigProperties.URL_QUERY_PROPERTY
+ .getName());
+ link = link.replaceFirst("#PARAM_1#", checkerId).replaceFirst("#PARAM_2#", conditionId);
+ }
+
+ return link;
+ }
+}
diff --git a/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/internal/conditions/ConditionElement.java b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/internal/conditions/ConditionElement.java
new file mode 100644
index 0000000..8f74f28
--- /dev/null
+++ b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/internal/conditions/ConditionElement.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.preflighting.core.internal.conditions;
+
+import com.motorolamobility.preflighting.core.checker.condition.ICondition;
+
+/**
+ * Bean class representing a Condition extension element.
+ */
+public final class ConditionElement
+{
+ public static final String CHECKER_CONDITION_ELEMENT_NAME = "condition"; //$NON-NLS-1$
+
+ public static final String CHECKER_CONDITION_ATTRIBUTE_ID = "id"; //$NON-NLS-1$
+
+ public static final String CHECKER_CONDITION_ATTRIBUTE_NAME = "name"; //$NON-NLS-1$
+
+ public static final String CHECKER_CONDITION_ATTRIBUTE_DESCRIPTION = "description"; //$NON-NLS-1$
+
+ public static final String CHECKER_CONDITION_ATTRIBUTE_DEFAULT_SEVERITY =
+ "defaultSeverityLevel"; //$NON-NLS-1$
+
+ public static final String CHECKER_CONDITION_ATTRIBUTE_CLASS = "class"; //$NON-NLS-1$
+
+ public static final String CHECKER_CONDITION_ATTRIBUTE_MARKER_TYPE = "markerType"; //$NON-NLS-1$
+
+ private final String id;
+
+ private final String name;
+
+ private final String description;
+
+ private final String defaultSeverityLevel;
+
+ private final ICondition condition;
+
+ /**
+ * Creates a new Condition Element
+ * @param id
+ * @param name
+ * @param description
+ * @param defaultSeverityLevel
+ * @param checker
+ * @param condition
+ */
+ public ConditionElement(String id, String name, String description,
+ String defaultSeverityLevel, ICondition condition)
+ {
+ this.id = id;
+ this.name = name;
+ this.description = description;
+ this.defaultSeverityLevel = defaultSeverityLevel;
+ this.condition = condition;
+ }
+
+ /**
+ * @return the id
+ */
+ public String getId()
+ {
+ return id;
+ }
+
+ /**
+ * @return the name
+ */
+ public String getName()
+ {
+ return name;
+ }
+
+ /**
+ * @return the description
+ */
+ public String getDescription()
+ {
+ return description;
+ }
+
+ /**
+ * @return the defaultSeverityLevel
+ */
+ public String getDefaultSeverityLevel()
+ {
+ return defaultSeverityLevel;
+ }
+
+ /**
+ * @return the condition
+ */
+ public ICondition getChecker()
+ {
+ return condition;
+ }
+} \ No newline at end of file
diff --git a/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/internal/devicelayoutspecification/ConfigType.java b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/internal/devicelayoutspecification/ConfigType.java
new file mode 100644
index 0000000..eeadeda
--- /dev/null
+++ b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/internal/devicelayoutspecification/ConfigType.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.preflighting.core.internal.devicelayoutspecification;
+
+import com.motorolamobility.preflighting.core.devicelayoutspecification.ParametersType;
+
+/**
+ *
+ * The configType defines the content of a "config" element in a "device" element.
+ *
+ * A "config" element can have all the parameters elements defined by
+ * "parameterType". It also has a required "name" attribute that indicates the
+ * user-interface name for this configuration.
+ *
+ *
+ * <p>Java class for configType complex type.
+ *
+ * <p>The following schema fragment specifies the expected content contained within this class.
+ *
+ * <pre>
+ * &lt;complexType name="configType">
+ * &lt;complexContent>
+ * &lt;extension base="{http://schemas.android.com/sdk/android/layout-devices/1}parametersType">
+ * &lt;attribute name="name" use="required" type="{http://www.w3.org/2001/XMLSchema}normalizedString" />
+ * &lt;/extension>
+ * &lt;/complexContent>
+ * &lt;/complexType>
+ * </pre>
+ *
+ *
+ */
+public final class ConfigType extends ParametersType
+{
+ protected String name;
+
+ /**
+ * Gets the value of the name property.
+ *
+ * @return
+ * possible object is
+ * {@link String }
+ *
+ */
+ public String getName()
+ {
+ return name;
+ }
+
+ /**
+ * Sets the value of the name property.
+ *
+ * @param value
+ * allowed object is
+ * {@link String }
+ *
+ */
+ public void setName(String value)
+ {
+ this.name = value;
+ }
+
+ @Override
+ protected String toStringHeader()
+ {
+ return "Configuration: " + name + NEWLINE;
+ }
+}
diff --git a/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/internal/devicelayoutspecification/LayoutDevicesType.java b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/internal/devicelayoutspecification/LayoutDevicesType.java
new file mode 100644
index 0000000..461376f
--- /dev/null
+++ b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/internal/devicelayoutspecification/LayoutDevicesType.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.preflighting.core.internal.devicelayoutspecification;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.motorolamobility.preflighting.core.devicelayoutspecification.Device;
+
+/**
+ *
+ * The "layout-devices" element is the root element of this schema.
+ *
+ * It must contain zero or more "device" elements that each define the configurations
+ * available for a given device.
+ *
+ * These definitions are used in the Graphical Layout Editor in the
+ * Android Development Tools (ADT) plugin for Eclipse.
+ *
+ *
+ * <p>Java class for layoutDevicesType complex type.
+ *
+ * <p>The following schema fragment specifies the expected content contained within this class.
+ *
+ * <pre>
+ * &lt;complexType name="layoutDevicesType">
+ * &lt;complexContent>
+ * &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+ * &lt;sequence>
+ * &lt;element name="device" maxOccurs="unbounded" minOccurs="0">
+ * &lt;complexType>
+ * &lt;complexContent>
+ * &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+ * &lt;sequence>
+ * &lt;element name="default" type="{http://schemas.android.com/sdk/android/layout-devices/1}parametersType" minOccurs="0"/>
+ * &lt;element name="config" type="{http://schemas.android.com/sdk/android/layout-devices/1}configType" maxOccurs="unbounded"/>
+ * &lt;/sequence>
+ * &lt;attribute name="name" use="required" type="{http://www.w3.org/2001/XMLSchema}normalizedString" />
+ * &lt;/restriction>
+ * &lt;/complexContent>
+ * &lt;/complexType>
+ * &lt;/element>
+ * &lt;/sequence>
+ * &lt;/restriction>
+ * &lt;/complexContent>
+ * &lt;/complexType>
+ * </pre>
+ *
+ *
+ */
+public final class LayoutDevicesType
+{
+
+ protected List<Device> devices = new ArrayList<Device>();
+
+ /**
+ * Gets the value of the device property.
+ *
+ * <p>
+ * This accessor method returns a reference to the live list,
+ * not a snapshot. Therefore any modification you make to the
+ * returned list will be present inside the JAXB object.
+ * This is why there is not a <CODE>set</CODE> method for the device property.
+ *
+ * <p>
+ * For example, to add a new item, do as follows:
+ * <pre>
+ * getDevice().add(newItem);
+ * </pre>
+ *
+ *
+ * <p>
+ * Objects of the following type(s) are allowed in the list
+ * {@link LayoutDevicesType.Device }
+ *
+ *
+ */
+ public List<Device> getDevices()
+ {
+ return this.devices;
+ }
+
+ @Override
+ public String toString()
+ {
+ return "LayoutDevicesType [device=" + devices + "]";
+ }
+}
diff --git a/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/internal/devicelayoutspecification/ObjectFactory.java b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/internal/devicelayoutspecification/ObjectFactory.java
new file mode 100644
index 0000000..7544e7b
--- /dev/null
+++ b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/internal/devicelayoutspecification/ObjectFactory.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.preflighting.core.internal.devicelayoutspecification;
+
+import com.motorolamobility.preflighting.core.devicelayoutspecification.Device;
+import com.motorolamobility.preflighting.core.devicelayoutspecification.ParametersType;
+
+/**
+ * This object contains factory methods for each
+ * Java content interface and Java element interface
+ * generated in the layout_devices package.
+ * <p>An ObjectFactory allows you to programatically
+ * construct new instances of the Java representation
+ * for XML content. The Java representation of XML
+ * content can consist of schema derived interfaces
+ * and classes representing the binding of schema
+ * type definitions, element declarations and model
+ * groups. Factory methods for each of these are
+ * provided in this class.
+ *
+ */
+public final class ObjectFactory
+{
+ /**
+ * Create a new ObjectFactory that can be used to create new instances of schema derived classes for package: layout_devices
+ *
+ */
+ private ObjectFactory()
+ {
+ }
+
+ /**
+ * Singleton
+ */
+ private final static ObjectFactory instance = new ObjectFactory();
+
+ public static synchronized ObjectFactory getInstance()
+ {
+ return instance;
+ }
+
+ /**
+ * Create an instance of {@link ParametersType.ScreenDimension }
+ *
+ */
+ public ScreenDimension createParametersTypeScreenDimension()
+ {
+ return new ScreenDimension();
+ }
+
+ /**
+ * Create an instance of {@link ConfigType }
+ *
+ */
+ public ConfigType createConfigType()
+ {
+ return new ConfigType();
+ }
+
+ /**
+ * Create an instance of {@link ParametersType }
+ *
+ */
+ public ParametersType createParametersType()
+ {
+ return new ParametersType();
+ }
+
+ /**
+ * Create an instance of {@link LayoutDevicesType.Device }
+ *
+ */
+ public Device createLayoutDevicesTypeDevice()
+ {
+ return new Device();
+ }
+
+ /**
+ * Create an instance of {@link LayoutDevicesType }
+ *
+ */
+ public LayoutDevicesType createLayoutDevicesType()
+ {
+ return new LayoutDevicesType();
+ }
+}
diff --git a/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/internal/devicelayoutspecification/ScreenDimension.java b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/internal/devicelayoutspecification/ScreenDimension.java
new file mode 100644
index 0000000..918d7aa
--- /dev/null
+++ b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/internal/devicelayoutspecification/ScreenDimension.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.preflighting.core.internal.devicelayoutspecification;
+
+import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * <p>Java class for anonymous complex type.
+ *
+ * <p>The following schema fragment specifies the expected content contained within this class.
+ *
+ * <pre>
+ * &lt;complexType>
+ * &lt;complexContent>
+ * &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+ * &lt;sequence maxOccurs="2" minOccurs="2">
+ * &lt;element name="size">
+ * &lt;simpleType>
+ * &lt;restriction base="{http://www.w3.org/2001/XMLSchema}positiveInteger">
+ * &lt;/restriction>
+ * &lt;/simpleType>
+ * &lt;/element>
+ * &lt;/sequence>
+ * &lt;/restriction>
+ * &lt;/complexContent>
+ * &lt;/complexType>
+ * </pre>
+ *
+ *
+ */
+public final class ScreenDimension
+{
+ protected List<Integer> size = new ArrayList<Integer>();
+
+ /**
+ * Gets the value of the size property.
+ *
+ * <p>
+ * This accessor method returns a reference to the live list,
+ * not a snapshot. Therefore any modification you make to the
+ * returned list will be present inside the JAXB object.
+ * This is why there is not a <CODE>set</CODE> method for the size property.
+ *
+ * <p>
+ * For example, to add a new item, do as follows:
+ * <pre>
+ * getSize().add(newItem);
+ * </pre>
+ *
+ *
+ * <p>
+ * Objects of the following type(s) are allowed in the list
+ * {@link BigInteger }
+ *
+ *
+ */
+ public List<Integer> getSize()
+ {
+ return this.size;
+ }
+
+ public void addSize(Integer integer)
+ {
+ size.add(integer);
+ }
+
+ @Override
+ public String toString()
+ {
+ return "" + size;
+ }
+}
diff --git a/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/internal/logging/Logger.java b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/internal/logging/Logger.java
new file mode 100644
index 0000000..731f62c
--- /dev/null
+++ b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/internal/logging/Logger.java
@@ -0,0 +1,295 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.preflighting.core.internal.logging;
+
+import java.io.IOException;
+import java.net.URL;
+import java.util.HashMap;
+import java.util.Properties;
+
+import org.apache.log4j.Level;
+import org.apache.log4j.PropertyConfigurator;
+
+import com.motorolamobility.preflighting.core.PreflightingCorePlugin;
+
+/**
+ * Logger provides a logging facility to the <b>MOTODEV Studio</b> based
+ * applications such as Studios and RCP applications. </p> <b>Note:</b> The logs
+ * will be saved on the plug-in state area for this plug-in.
+ */
+public final class Logger
+{
+
+ /**
+ * Configure Log4J
+ */
+ static
+ {
+ URL log4jProperties =
+ PreflightingCorePlugin.getContext().getBundle().getResource("etc/log4j.properties");
+ Properties p = new Properties();
+ try
+ {
+ p.load(log4jProperties.openStream());
+ PropertyConfigurator.configure(p);
+ }
+ catch (IOException e)
+ {
+ //Do Nothing.
+ }
+ }
+
+ /*
+ * This Map pools the Logger instances.
+ */
+ private static HashMap<String, Logger> pool;
+
+ /*
+ * Log4j logger instance being wrapped.
+ */
+ private final org.apache.log4j.Logger logger;
+
+ /**
+ * Builds a logger associated to the specified name.
+ *
+ * @param name The name of the logger.
+ */
+ private Logger(String name)
+ {
+ logger = org.apache.log4j.Logger.getLogger(name);
+ }
+
+ /**
+ * Gets a logger named according to the value of the name parameter. If the
+ * named logger already exists, then the existing instance will be returned.
+ * Otherwise, a new instance is created.
+ *
+ * @param name The name of the logger.
+ * @return the logger associated to the specified name.
+ */
+ public static synchronized Logger getLogger(String name)
+ {
+ Logger logger = null;
+ if (pool == null)
+ {
+ pool = new HashMap<String, Logger>();
+ }
+
+ if (pool.containsKey(name))
+ {
+ logger = pool.get(name);
+ }
+ else
+ {
+ logger = new Logger(name);
+ pool.put(name, logger);
+ }
+ return logger;
+ }
+
+ /**
+ * Logs the specified message with the specified level.
+ *
+ * @param level The log level.
+ * @param message The message to log.
+ */
+ public void log(int level, String message)
+ {
+ boolean key = true;
+ if ((level == com.motorolamobility.preflighting.core.logging.Level.OFF)
+ || (level == com.motorolamobility.preflighting.core.logging.Level.ALL))
+ {
+ key = false;
+ }
+
+ if (key)
+ {
+ logger.log(Level.toLevel(level), message);
+ }
+ }
+
+ /**
+ * Logs the specified message and an exception stack trace.
+ *
+ * @param level The log level.
+ * @param message The message to log.
+ * @param exception Exception whose stack will be logged.
+ */
+ public void log(int level, String message, Throwable exception)
+ {
+ boolean key = true;
+ if ((level == com.motorolamobility.preflighting.core.logging.Level.OFF)
+ || (level == com.motorolamobility.preflighting.core.logging.Level.ALL))
+ {
+ key = false;
+ }
+ if (key)
+ {
+ logger.log(Level.toLevel(level), message, exception);
+ }
+ }
+
+ /**
+ * Log a message object with the DEBUG level.
+ *
+ * @param message The message to log.
+ */
+ public void debug(String message)
+ {
+ debug(message, null);
+ }
+
+ /**
+ * Log a message object with the DEBUG level.
+ *
+ * @param message The message to log.
+ * @param exception Exception whose stack will be logged.
+ */
+ public void debug(String message, Throwable exception)
+ {
+ if (null == exception)
+ {
+ logger.debug(message);
+ }
+ else
+ {
+ logger.debug(message, exception);
+ }
+ }
+
+ /**
+ * Log a message object with the INFO level.
+ *
+ * @param message The message to log.
+ */
+ public void info(String message)
+ {
+ info(message, null);
+ }
+
+ /**
+ * Log a message object with the INFO level.
+ *
+ * @param message The message to log.
+ * @param exception Exception whose stack will be logged.
+ */
+ public void info(String message, Throwable exception)
+ {
+ if (null == exception)
+ {
+ logger.info(message);
+ }
+ else
+ {
+ logger.info(message, exception);
+ }
+ }
+
+ /**
+ * Log a message object with the WARN level.
+ *
+ * @param message The message to log.
+ */
+ public void warn(String message)
+ {
+ warn(message, null);
+ }
+
+ /**
+ * Log a message object with the WARN level.
+ *
+ * @param message The message to log.
+ * @param exception Exception whose stack will be logged.
+ */
+ public void warn(String message, Throwable exception)
+ {
+ if (null == exception)
+ {
+ logger.warn(message);
+ }
+ else
+ {
+ logger.warn(message, exception);
+ }
+ }
+
+ /**
+ * Log a message object with the ERROR level.
+ *
+ * @param message The message to log.
+ */
+ public void error(String message)
+ {
+ error(message, null);
+ }
+
+ /**
+ * Log a message object with the ERROR level.
+ *
+ * @param message The message to log.
+ * @param exception Exception whose stack will be logged.
+ */
+ public void error(String message, Throwable exception)
+ {
+ if (null == exception)
+ {
+ logger.error(message);
+ }
+ else
+ {
+ logger.error(message, exception);
+ }
+ }
+
+ /**
+ * Log a message object with the FATAL level.
+ *
+ * @param message The message to log.
+ */
+ public void fatal(String message)
+ {
+ fatal(message, null);
+ }
+
+ /**
+ * Log a message object with the FATAL level.
+ *
+ * @param message The message to log.
+ * @param exception Exception whose stack will be logged.
+ */
+ public void fatal(String message, Throwable exception)
+ {
+ if (null == exception)
+ {
+ logger.fatal(message);
+ }
+ else
+ {
+ logger.fatal(message, exception);
+ }
+ }
+
+ /**
+ * Sets the Logger level to the specified value.
+ *
+ * @param level One of the {@link com.motorolamobility.preflighting.core.logging.Level}
+ * constants.
+ */
+ public void setLevel(int level)
+ {
+ logger.setLevel(Level.toLevel(level));
+ }
+}
diff --git a/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/internal/permissionfeature/PermissionToFeatureMapReader.java b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/internal/permissionfeature/PermissionToFeatureMapReader.java
new file mode 100644
index 0000000..2f64a3f
--- /dev/null
+++ b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/internal/permissionfeature/PermissionToFeatureMapReader.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.preflighting.core.internal.permissionfeature;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.xml.sax.SAXException;
+
+import com.motorolamobility.preflighting.core.permissionfeature.Feature;
+import com.motorolamobility.preflighting.core.permissionfeature.Permission;
+
+/**
+ * Reads and populates permission_to_feature_map object
+ */
+public final class PermissionToFeatureMapReader
+{
+ /**
+ * File to read
+ */
+ private final InputStream xmlStream;
+
+ /**
+ * @param xmlStream input stream from which to read the permission to implied required feature mapping
+ */
+ public PermissionToFeatureMapReader(InputStream xmlStream)
+ {
+ this.xmlStream = xmlStream;
+ }
+
+ /**
+ * Reads XML set in the {@link PermissionToFeatureMapReader#xmlStream} variable
+ * @return
+ * @throws ParserConfigurationException
+ * @throws SAXException
+ * @throws IOException
+ */
+ public PermissionToFeatureMapping read() throws ParserConfigurationException, SAXException,
+ IOException
+ {
+ PermissionToFeatureMapping permissionToFeatureMapping = new PermissionToFeatureMapping();
+ DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance();
+ DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder();
+ Document doc = docBuilder.parse(xmlStream);
+
+ NodeList categoriesList = doc.getElementsByTagName("category");
+ for (int i = 0; i < categoriesList.getLength(); i++)
+ {
+ Node categoryNode = categoriesList.item(i);
+ NamedNodeMap categoryNodeMap = categoryNode.getAttributes();
+ Node categoryAtr = categoryNodeMap.getNamedItem("name");
+ if ((categoryAtr != null) && !categoryAtr.getNodeValue().trim().equals("")) //$NON-NLS-1$
+ {
+ // add category
+ String categoryName = categoryAtr.getNodeValue();
+ List<Permission> permissions = new ArrayList<Permission>();
+ NodeList permissionsList = categoryNode.getChildNodes();
+ for (int j = 0; j < permissionsList.getLength(); j++)
+ {
+ Node permissionNode = permissionsList.item(j);
+ if ((permissionNode != null)
+ && (permissionNode.getNodeType() == Node.ELEMENT_NODE))
+ {
+ NamedNodeMap permissionMap = permissionNode.getAttributes();
+ Node permissionAtr = permissionMap.getNamedItem("id");
+ if ((permissionAtr != null)
+ && !permissionAtr.getNodeValue().trim().equals("")) //$NON-NLS-1$
+ {
+ // add permission into category
+ String permId = permissionAtr.getNodeValue();
+ Permission permission = new Permission(permId);
+ permissions.add(permission);
+
+ // find implied required features to add
+ List<Feature> features = new ArrayList<Feature>();
+ NodeList featureList = permissionNode.getChildNodes();
+ for (int z = 0; z < featureList.getLength(); z++)
+ {
+ Node featureNode = featureList.item(z);
+ if ((featureNode != null)
+ && (featureNode.getNodeType() == Node.ELEMENT_NODE))
+ {
+ NamedNodeMap featureMap = featureNode.getAttributes();
+ Node featureAtr = featureMap.getNamedItem("id");
+ if ((featureAtr != null)
+ && !featureAtr.getNodeValue().trim().equals("")) //$NON-NLS-1$
+ {
+ Feature feature = new Feature(featureAtr.getNodeValue());
+ features.add(feature);
+ }
+ }
+ }
+ permissionToFeatureMapping.putFeatures(permId, features);
+ }
+ }
+ }
+ permissionToFeatureMapping.putPermissions(categoryName, permissions);
+ }
+ }
+ return permissionToFeatureMapping;
+ }
+
+}
diff --git a/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/internal/permissionfeature/PermissionToFeatureMapping.java b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/internal/permissionfeature/PermissionToFeatureMapping.java
new file mode 100644
index 0000000..96720a5
--- /dev/null
+++ b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/internal/permissionfeature/PermissionToFeatureMapping.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.preflighting.core.internal.permissionfeature;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import com.motorolamobility.preflighting.core.permissionfeature.Feature;
+import com.motorolamobility.preflighting.core.permissionfeature.Permission;
+
+/**
+ * Container class to keep permissions to implied required feature mapping
+ */
+public final class PermissionToFeatureMapping
+{
+ /**
+ * Category name to list of permission IDs
+ */
+ private final Map<String, List<Permission>> categoryNameToPermissions =
+ new HashMap<String, List<Permission>>();
+
+ /**
+ * Permission id to list of implied required features
+ */
+ private final Map<String, List<Feature>> permissionIdToImpliedRequiredFeatures =
+ new HashMap<String, List<Feature>>();
+
+ /**
+ *
+ * @param categoryName
+ * @return list of permissions for the given category, null if not found
+ */
+ public List<Permission> getPermissionsForCategory(String categoryName)
+ {
+ return categoryNameToPermissions.get(categoryName);
+ }
+
+ /**
+ *
+ * @param permissionId
+ * @return list of required features for the given permission, null if not
+ * found
+ */
+ public List<Feature> getRequiredImpliedFeaturesForPermission(String permissionId)
+ {
+ return permissionIdToImpliedRequiredFeatures.get(permissionId);
+ }
+
+ public List<Permission> putPermissions(String key, List<Permission> value)
+ {
+ return categoryNameToPermissions.put(key, value);
+ }
+
+ public List<Feature> putFeatures(String key, List<Feature> value)
+ {
+ return permissionIdToImpliedRequiredFeatures.put(key, value);
+ }
+
+}
diff --git a/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/internal/utils/AaptUtils.java b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/internal/utils/AaptUtils.java
new file mode 100644
index 0000000..1ddfc68
--- /dev/null
+++ b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/internal/utils/AaptUtils.java
@@ -0,0 +1,1528 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.preflighting.core.internal.utils;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.StringWriter;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipException;
+import java.util.zip.ZipInputStream;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.transform.OutputKeys;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
+
+import org.eclipse.core.runtime.Path;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import com.motorolamobility.preflighting.core.exception.PreflightingToolException;
+import com.motorolamobility.preflighting.core.i18n.PreflightingCoreNLS;
+import com.motorolamobility.preflighting.core.logging.PreflightingLogger;
+
+public final class AaptUtils
+{
+
+ private static final String ANDROID_SMALL_SCREENS = "android:smallScreens";
+
+ public static final String APP_VALIDATOR_TEMP_DIR = "MotodevAppValidator";
+
+ private static final String JAVA_TEMP_DIR_PROPERTY = "java.io.tmpdir";
+
+ private static final String TEMP_DIR_PATH = System.getProperty(JAVA_TEMP_DIR_PROPERTY);
+
+ // Temp folder used for APK extracting
+ public static final File tmpAppValidatorFolder =
+ new File(TEMP_DIR_PATH, APP_VALIDATOR_TEMP_DIR);
+
+ private static final String CLASSES_DEX = "classes.dex"; //$NON-NLS-1$
+
+ private static final String RESOURCES_ARSC = "resources.arsc"; //$NON-NLS-1$
+
+ private static final String XML_FILE = "xml"; //$NON-NLS-1$
+
+ private static final String ELEMENT_NODE = "E:"; //$NON-NLS-1$
+
+ private static final String ATTRIBUTE_NODE = "A:"; //$NON-NLS-1$
+
+ private static final String NAMESPACE_XMLNS = "N:"; //$NON-NLS-1$
+
+ private static HashMap<String, HashMap<String, String>> resourceValues =
+ new HashMap<String, HashMap<String, String>>();
+
+ private static Map<String, String> navigationMap;
+
+ private static Map<String, String> nightMap;
+
+ private static Map<String, String> keyboardMap;
+
+ private static Map<String, String> touchMap;
+
+ private static Map<String, String> densityMap;
+
+ private static Map<String, String> sizeMap;
+
+ private static Map<String, String> orientationMap;
+
+ private static Map<String, String> longMap;
+
+ private static Map<String, String> navHiddenMap;
+
+ private static Map<String, String> keyHiddenMap;
+
+ private static Map<String, String> typeMap;
+
+ public static final String APK_EXTENSION = ".apk";
+
+ public static final String ZIP_EXTENSION = ".zip";
+
+ private static Map<Pattern, Map<String, String>> localizationAttributesMap2 =
+ new HashMap<Pattern, Map<String, String>>();
+
+ private static Map<Pattern, String> localizationAttributesMap1 = new HashMap<Pattern, String>();
+
+ private static Pattern[] patternArray = new Pattern[18];
+
+ static
+ {
+ // Initialize specific maps
+ navigationMap = new HashMap<String, String>();
+ navigationMap.put("1", "nonav");
+ navigationMap.put("2", "dpad");
+ navigationMap.put("3", "trackball");
+ navigationMap.put("4", "wheel");
+
+ nightMap = new HashMap<String, String>();
+ nightMap.put("16", "notnight");
+ nightMap.put("32", "night");
+
+ keyboardMap = new HashMap<String, String>();
+ keyboardMap.put("1", "nokeys");
+ keyboardMap.put("2", "qwerty");
+ keyboardMap.put("3", "12key");
+
+ touchMap = new HashMap<String, String>();
+ touchMap.put("1", "notouch");
+ touchMap.put("2", "stylus");
+ touchMap.put("3", "finger");
+
+ densityMap = new HashMap<String, String>();
+ densityMap.put("no", "nodpi");
+ densityMap.put("120", "ldpi");
+ densityMap.put("160", "mdpi");
+ densityMap.put("240", "hdpi");
+
+ sizeMap = new HashMap<String, String>();
+ sizeMap.put("1", "small");
+ sizeMap.put("2", "normal");
+ sizeMap.put("3", "large");
+
+ orientationMap = new HashMap<String, String>();
+ orientationMap.put("1", "port");
+ orientationMap.put("2", "land");
+ orientationMap.put("3", "square");
+
+ longMap = new HashMap<String, String>();
+ longMap.put("16", "notlong");
+ longMap.put("32", "long");
+
+ navHiddenMap = new HashMap<String, String>();
+ navHiddenMap.put("8", "navhidden");
+ navHiddenMap.put("4", "navexposed");
+
+ keyHiddenMap = new HashMap<String, String>();
+ keyHiddenMap.put("1", "keyexposed");
+ keyHiddenMap.put("2", "keyhidden");
+
+ typeMap = new HashMap<String, String>();
+ typeMap.put("3", "car");
+
+ // initialize localization folder attributes
+ // the order of patternArray elements are extremely important, do not
+ // modify it
+ localizationAttributesMap1.put(patternArray[0] = Pattern.compile("mcc=[0-9]+"), "mcc");
+ localizationAttributesMap1.put(patternArray[1] = Pattern.compile("mnc=[0-9]+"), "mnc");
+ localizationAttributesMap1.put(patternArray[2] = Pattern.compile("lang=[a-z]+"), "");
+ localizationAttributesMap1.put(patternArray[3] = Pattern.compile("cnt=[A-Z]+"), "r");
+ localizationAttributesMap2.put(patternArray[4] = Pattern.compile("sz=[0-9]"), sizeMap);
+ localizationAttributesMap2.put(patternArray[5] = Pattern.compile("lng=[0-9]+"), longMap);
+ localizationAttributesMap2.put(patternArray[6] = Pattern.compile("orient=[0-9]"),
+ orientationMap);
+ localizationAttributesMap2.put(patternArray[7] = Pattern.compile("type=[0-9]"), typeMap);
+ localizationAttributesMap2.put(patternArray[8] = Pattern.compile("night=[0-9]+"), nightMap);
+ localizationAttributesMap2.put(patternArray[9] = Pattern.compile("density=[0-9]+"),
+ densityMap);
+ localizationAttributesMap2.put(patternArray[10] = Pattern.compile("touch=[0-9]"), touchMap);
+ localizationAttributesMap2.put(patternArray[11] = Pattern.compile("keyhid=[0-9]"),
+ keyHiddenMap);
+ localizationAttributesMap2
+ .put(patternArray[12] = Pattern.compile("kbd=[0-9]"), keyboardMap);
+ localizationAttributesMap2.put(patternArray[13] = Pattern.compile("navhid=[0-9]"),
+ navHiddenMap);
+ localizationAttributesMap2.put(patternArray[14] = Pattern.compile("nav=[0-9]"),
+ navigationMap);
+ localizationAttributesMap1.put(patternArray[15] = Pattern.compile("\\sw=[0-9]+"), "");
+ localizationAttributesMap1.put(patternArray[16] = Pattern.compile("\\sh=[0-9]+"), "x");
+ localizationAttributesMap1.put(patternArray[17] = Pattern.compile("sdk=[0-9]+"), "v");
+
+ }
+
+ /**
+ * Cleans resources maps among executions for applications
+ */
+ public static void cleanApplicationResourceValues()
+ {
+ resourceValues.clear();
+ }
+
+ public static void extractFilesFromAPK(File apkFile, String sdkPath, File tmpProjectFile)
+ throws PreflightingToolException
+ {
+ if ((tmpProjectFile != null) && tmpProjectFile.exists() && tmpProjectFile.canWrite())
+ {
+ ZipInputStream apkInputStream = null;
+ FileOutputStream apkOutputStream = null;
+ try
+ {
+ // create the buffer and the the zip stream
+ byte[] buf = new byte[1024];
+ apkInputStream = new ZipInputStream(new FileInputStream(apkFile.getAbsolutePath()));
+
+ ZipEntry apkZipEntry = null;
+ try
+ {
+ apkZipEntry = apkInputStream.getNextEntry();
+ }
+ catch (Exception e)
+ {
+ PreflightingLogger.error(ApkUtils.class,
+ "It was not possible to read the android package.", e); //$NON-NLS-1$
+ }
+
+ if (apkZipEntry == null)
+ {
+ throw new IOException("Invalid APK file.");
+ }
+
+ String folders = null;
+ File fileToCreate = null;
+
+ // create res folder
+ fileToCreate = new File(tmpProjectFile, "res");
+ if (!fileToCreate.exists())
+ {
+ fileToCreate.mkdirs();
+ }
+
+ // create the resources file
+ Map<File, Document> languageMap =
+ retrieveLocalizationStringsMapFromAPK(sdkPath, apkFile.getAbsolutePath(),
+ "ProjectResourcesValues.xml");
+ createLocalizationFilesFromMap(languageMap, fileToCreate);
+
+ // iterates through each entry to be extracted of the android
+ // package
+ while (apkZipEntry != null)
+ {
+ try
+ {
+ String apkEntryName = apkZipEntry.getName();
+
+ if (apkEntryName.indexOf(Path.SEPARATOR) != -1)
+ {
+ // creates the directory structure
+ folders =
+ apkEntryName.substring(0,
+ apkEntryName.lastIndexOf(Path.SEPARATOR));
+ fileToCreate = new File(tmpProjectFile, folders);
+ if (!fileToCreate.exists())
+ {
+ fileToCreate.mkdirs();
+ }
+ }
+
+ if (apkEntryName.endsWith(XML_FILE))
+ {
+ // Gets XML from the parser
+ fileToCreate = new File(tmpProjectFile, apkEntryName);
+
+ createXMLFile(sdkPath, apkFile.getAbsolutePath(), apkEntryName,
+ fileToCreate);
+ }
+ // filter files which is desired to create
+ else if (!apkEntryName.endsWith(RESOURCES_ARSC)
+ && !apkEntryName.endsWith(CLASSES_DEX))
+ {
+ // write the file
+ try
+ {
+ apkOutputStream =
+ new FileOutputStream(tmpProjectFile.getAbsolutePath()
+ + Path.SEPARATOR + apkEntryName);
+
+ int length = 0;
+ while ((length = apkInputStream.read(buf, 0, 1024)) > -1)
+ {
+ apkOutputStream.write(buf, 0, length);
+ }
+ }
+ finally
+ {
+ if (apkOutputStream != null)
+ {
+ try
+ {
+ apkOutputStream.close();
+ }
+ catch (IOException e)
+ {
+ //Do Nothing.
+ }
+ }
+ }
+ }
+ }
+ catch (ZipException zipException)
+ {
+ // throw exception because the apk is probably corrupt
+ PreflightingLogger
+ .error(ApkUtils.class,
+ "It was not possible to read the android package; it is probably corrupt.", zipException); //$NON-NLS-1$
+ throw new PreflightingToolException(
+ PreflightingCoreNLS.ApkUtils_ImpossibleExtractAndroidPackageMessage,
+ zipException);
+ }
+ catch (IOException ioException)
+ {
+ // log the error but do not thrown an exception because
+ // it will be attempted to create all files
+ PreflightingLogger.error(ApkUtils.class,
+ "It was not possible to extract the android package.", ioException); //$NON-NLS-1$
+ }
+ finally
+ {
+ apkInputStream.closeEntry();
+ apkZipEntry = apkInputStream.getNextEntry();
+ }
+ }
+ }
+ catch (IOException ioException)
+ {
+ PreflightingLogger.error(ApkUtils.class,
+ "It was not possible to read the android package.", ioException); //$NON-NLS-1$
+ throw new PreflightingToolException(
+ PreflightingCoreNLS.ApkUtils_ImpossibleExtractAndroidPackageMessage,
+ ioException);
+ }
+ finally
+ {
+ try
+ {
+ if (apkInputStream != null)
+ {
+ apkInputStream.close();
+ }
+ if (apkOutputStream != null)
+ {
+ apkOutputStream.close();
+ }
+ }
+ catch (IOException ioException)
+ {
+ // Do Nothing.
+ }
+ }
+
+ }
+ else
+ {
+ PreflightingLogger.error(ApkUtils.class,
+ "It was not possible to read the android package."); //$NON-NLS-1$
+ throw new PreflightingToolException(
+ PreflightingCoreNLS.ApkUtils_ImpossibleExtractAndroidPackageMessage);
+ }
+ }
+
+ /**
+ * Given an APK file, all folders and DOMs for creating the directory
+ * structure with the localization files are returned in a {@link Map}. <br>
+ * The {@link Map} returned holds the following info: [{@link File},
+ * {@link Document}] in which the {@link File} represents the folder path in
+ * which the {@link Document} is to be created.
+ *
+ * @param aaptPath
+ * AAP tool path.
+ * @param apkPath
+ * APK file which the strings of translation are retrieved.
+ * @param xmlFileName
+ * XML file name generated by the AAP tool.
+ *
+ * @return The {@link Map} structure holding the {@link File}s and
+ * {@link Document}s necessary to create the directory tree for
+ * translation.
+ *
+ * @throws PreflightingToolException
+ * Exception thrown in case anything goes wrong extracting data.
+ */
+ public static Map<File, Document> retrieveLocalizationStringsMapFromAPK(String aaptPath,
+ String apkPath, String xmlFileName) throws PreflightingToolException
+ {
+ BufferedReader bReader = null;
+ InputStreamReader reader = null;
+ Map<File, Document> map = new HashMap<File, Document>();
+ try
+ {
+ Process aapt =
+ runAAPTCommandForExtractingResourcesAndValues(aaptPath, apkPath, xmlFileName);
+
+ // read output and store it in a buffer
+ reader = new InputStreamReader(aapt.getInputStream());
+ bReader = new BufferedReader(reader);
+
+ // patterns used to retrieve lines for language, key and values of
+ // string translations
+ Pattern languagePattern = Pattern.compile("config\\s[0-9]+");
+ Pattern stringKeyPattern =
+ Pattern.compile("[\\s]{2,}resource.+:string/[a-zA-Z0-9\\._$]+:");
+ Pattern stringArrayKeyPattern =
+ Pattern.compile("[\\s]{2,}resource.+:array/[a-zA-Z0-9\\._$]+:");
+ Pattern stringArrayCountPattern = Pattern.compile("Count=[0-9]+");
+ Pattern stringValuePattern = Pattern.compile("\".*\"");
+
+ Matcher matcher = null;
+ Document document = null;
+ Element resourceElement = null;
+ Element stringElement = null;
+ File languageDirectory = null;
+
+ int stringArraySize = 0;
+
+ String folderName = null;
+ String stringArraySizeText = null;
+ String key = null;
+ String value = null;
+ String[] arrayValue = null;
+
+ String infoLine = "";
+ while ((infoLine = bReader.readLine()) != null)
+ {
+ // try to match with language
+ matcher = languagePattern.matcher(infoLine);
+ if (matcher.find())
+ {
+ // in case there are a document and file, add it to the map
+ if ((document != null) && (languageDirectory != null)
+ && (resourceElement != null)
+ && (resourceElement.getChildNodes() != null)
+ && (resourceElement.getChildNodes().getLength() > 0))
+ {
+ map.put(languageDirectory, document);
+ // reset them
+ languageDirectory = null;
+ document = null;
+ }
+
+ // get the folder name based on the language
+ folderName = createResourcesSubfolders(infoLine, "values");
+ languageDirectory = new File(folderName);
+
+ // try to find an existent directory
+ document = findDocumentByLanguageDirectory(map, languageDirectory);
+
+ // the DOM was not found - initialize variables
+ if (document == null)
+ {
+ document = createNewDocument();
+ resourceElement = document.createElement("resources");
+ document.appendChild(resourceElement);
+ }
+ // the DOM was found - get the resources element (root
+ // element)
+ else
+ {
+ resourceElement = document.getDocumentElement();
+ }
+ }
+
+ // try to match with single string keys
+ matcher = stringKeyPattern.matcher(infoLine);
+ if (matcher.find())
+ {
+ key = matcher.group();
+ key = key.split(":string/")[1].split(":")[0];
+
+ infoLine = "";
+ do
+ {
+ // go the the next line in order to read the value
+ infoLine += bReader.readLine();
+ }
+ // do not delete the bReader.ready() statement because this avoids infinitive loops in case the regular expression fails
+ while (!infoLine.matches(".*\".*\".*") && bReader.ready());
+ matcher = stringValuePattern.matcher(infoLine);
+ if (matcher.find())
+ {
+ value = matcher.group();
+ value = value.substring(1, value.length() - 1);
+
+ // create element to be appended to the resource element
+ appendNewElementToNode("string", "name", key, value, resourceElement,
+ document);
+ }
+ }
+
+ // try to match with array string keys
+ matcher = stringArrayKeyPattern.matcher(infoLine);
+ if (matcher.find())
+ {
+ key = matcher.group();
+ key = key.split(":array/")[1].split(":")[0];
+ // go the the next line in order to get the number of
+ // elements in the array
+ infoLine = bReader.readLine();
+ matcher = stringArrayCountPattern.matcher(infoLine);
+ if (matcher.find())
+ {
+ stringArraySizeText = matcher.group();
+ stringArraySize = Integer.parseInt(stringArraySizeText.split("=")[1]);
+ // get each string of the array
+ arrayValue = new String[stringArraySize];
+ for (int arrayStringIndex = 0; arrayStringIndex < stringArraySize; arrayStringIndex++)
+ {
+ try
+ {
+ // go the the next line in order to read the
+ // value
+ infoLine = bReader.readLine();
+ matcher = stringValuePattern.matcher(infoLine);
+ matcher.find();
+ value = matcher.group();
+ value = value.substring(1, value.length() - 1);
+ }
+ catch (Exception e)
+ {
+ // TODO fix this (for now, just keep going, but
+ // this value may be necessary in the future)
+ value = "(reference)";
+ }
+ arrayValue[arrayStringIndex] = value;
+ }
+
+ // append array-string element
+ stringElement =
+ appendNewElementToNode("string-array", "name", key, null,
+ resourceElement, document);
+
+ // create and append the array of strings
+ for (int arrayStringIndex = 0; arrayStringIndex < stringArraySize; arrayStringIndex++)
+ {
+ value = arrayValue[arrayStringIndex];
+ appendNewElementToNode("item", null, null, value, stringElement,
+ document);
+ }
+ }
+ }
+ }
+ // in case there are a document and file, add it to the map
+ if ((document != null) && (languageDirectory != null) && (resourceElement != null)
+ && (resourceElement.getChildNodes() != null)
+ && (resourceElement.getChildNodes().getLength() > 0))
+ {
+ map.put(languageDirectory, document);
+ // reset them
+ languageDirectory = null;
+ document = null;
+ }
+ }
+ catch (IOException ioException)
+ {
+ PreflightingLogger.error(ApkUtils.class, ioException.getMessage());
+ throw new PreflightingToolException(ioException.getMessage(), ioException);
+ }
+ finally
+ {
+ // close resources
+ if (reader != null)
+ {
+ try
+ {
+ reader.close();
+ }
+ catch (IOException e)
+ {
+ //Do Nothing.
+ }
+ }
+ if (bReader != null)
+ {
+ try
+ {
+ bReader.close();
+ }
+ catch (IOException e)
+ {
+ //Do Nothing.
+ }
+ }
+ }
+
+ return map;
+ }
+
+ /**
+ * Execute the AAPT command: aapt d --values resources [ApkFile].apk >
+ * [XMLFileName].xml.
+ *
+ * @param aaptPath
+ * AAPT path.
+ * @param apkPath
+ * Target APK File path.
+ * @param xmlFileName
+ * XML file name which will be generated.
+ *
+ * @return The {@link Process} created the the execution of the AAPT
+ * command.
+ *
+ * @throws IOException
+ * Exception thrown in case the command execution fails.
+ */
+ private static Process runAAPTCommandForExtractingResourcesAndValues(String aaptPath,
+ String apkPath, String xmlFileName) throws IOException
+ {
+ // execute command: aapt.exe d --values resources <name>.apk <name>.xml
+ String[] aaptCommand = new String[]
+ {
+ aaptPath, "d", "--values", "resources", apkPath, xmlFileName //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ };
+
+ return Runtime.getRuntime().exec(aaptCommand);
+ }
+
+ /**
+ * Put data from {@link Map}, created in method
+ * {@link #retrieveLocalizationStringsMapFromAPK(String, String, String)}
+ * into the /res directory using the given parameter {@link File}.
+ *
+ * @param map
+ * {@link Map} which data will be extracted.
+ * @param resFile
+ * {@link File} structure which will hold the tree model holding
+ * directories and translation files.
+ * @throws PreflightingToolException
+ */
+ private static void createLocalizationFilesFromMap(Map<File, Document> map, File resFile)
+ throws PreflightingToolException
+ {
+ Set<File> fileSet = map.keySet();
+ File stringFolder = null;
+
+ // iterate through all directories
+ for (File key : fileSet)
+ {
+ // create temporary directories
+ stringFolder = new File(resFile, key.getPath());
+ if (!stringFolder.exists())
+ {
+ stringFolder.mkdirs();
+ }
+ // create XML file
+ createXmlFromDom(map.get(key), new File(resFile.getAbsolutePath() + Path.SEPARATOR
+ + key.getPath() + File.separator + "strings.xml"));
+ }
+ }
+
+ /**
+ * Create a XML File based on an AAPT output from a APK embedded file.
+ *
+ * @param aaptPath
+ * AAPT path.
+ * @param apkPath
+ * APK path.
+ * @param xmlFileName
+ * the XML file name which is embedded in the APT and is to be
+ * created as an XML file.
+ * @param fileToCreate
+ * XML file to be created.
+ *
+ * @throws PreflightingToolException
+ * Exception thrown when there are problems creating the XML
+ * file. The exception message describe in details the problem.
+ */
+ public static void createXMLFile(String aaptPath, String apkPath, String xmlFileName,
+ File fileToCreate) throws PreflightingToolException
+ {
+ // command for AAPT tool which gets the XML-to-be file to be worked on
+ String[] aaptCommand = new String[]
+ {
+ aaptPath, "dump", "xmltree", apkPath, xmlFileName //$NON-NLS-1$ //$NON-NLS-2$
+ };
+
+ // execute AAPT command
+ Process aapt = null;
+ try
+ {
+ aapt = Runtime.getRuntime().exec(aaptCommand);
+ }
+ catch (IOException ioException)
+ {
+ PreflightingLogger.error(ApkUtils.class, "Problems executing AAPT command.", //$NON-NLS-1$
+ ioException);
+ throw new PreflightingToolException(
+ PreflightingCoreNLS.ApkUtils_AaptExecutionProblemMessage, ioException);
+ }
+
+ Map<String, String> namespaceMap = new HashMap<String, String>();
+ Map<Integer, LineElement> map =
+ readToMap(aapt, aaptPath, apkPath, fileToCreate.getAbsolutePath(), namespaceMap);
+
+ if (!map.isEmpty())
+ {
+ Integer outerRow = map.keySet().size();
+ Integer innerRow;
+ while (outerRow > 0)
+ {
+ LineElement childElement = map.get(outerRow);
+ innerRow = outerRow - 1;
+ while (innerRow > 0)
+ {
+ LineElement parentElement = map.get(innerRow);
+ if (parentElement.getDepth() < childElement.getDepth())
+ {
+ parentElement.addChildLine(outerRow);
+ break;
+ }
+ innerRow--;
+ }
+ outerRow--;
+ }
+
+ // create new DOM
+ Document document = createNewDocument();
+ // populate it
+ addNodes(document, map, map.get(1), null);
+ // add schema
+ for (String namespace : namespaceMap.keySet())
+ {
+ document.getDocumentElement().setAttribute("xmlns:" + namespace,
+ namespaceMap.get(namespace));
+ }
+
+ // create XML file
+ createXmlFromDom(document, fileToCreate);
+ }
+ }
+
+ /**
+ * generate folder names according to configurations
+ *
+ * @param lineRead
+ * line read from aapt output
+ * @param folderPrefix
+ * The first name of all folders.
+ * @return the directory name
+ * @throws PreflightingToolException
+ * Exception thrown when the entered line has a bad format.
+ */
+ private static String createResourcesSubfolders(String lineRead, String folderPrefix)
+ throws PreflightingToolException
+ {
+ Pattern configPattern = Pattern.compile("config\\s[0-9]");
+
+ Matcher matcher = null;
+ StringBuffer strBuf = new StringBuffer(lineRead);
+ // try to match with type
+ matcher = configPattern.matcher(strBuf);
+ if (matcher.find())
+ {
+ for (int i = 0; i < 18; i++)
+ {
+ matcher = patternArray[i].matcher(strBuf);
+ if (matcher.find())
+ {
+ String result = matcher.group();
+ String value = result.split("=")[1];
+ // special treatment
+ if (localizationAttributesMap2.containsKey(patternArray[i]))
+ {
+ String nameSegment =
+ localizationAttributesMap2.get(patternArray[i]).get(value);
+ if (nameSegment != null)
+ {
+ folderPrefix += "-" + nameSegment;
+ }
+ }
+ else
+ {
+ String nameSegment =
+ localizationAttributesMap1.get(patternArray[i]) + value;
+ // treat the specific case of height, whose value is
+ // preceded by x egg. 1024x864
+ if (i != 16)
+ {
+ folderPrefix += "-" + nameSegment;
+ }
+ else
+ {
+ folderPrefix += nameSegment;
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ PreflightingLogger.error("The entered line has a bad format.");
+ throw new PreflightingToolException("The entered line has a bad format.");
+ }
+
+ return folderPrefix;
+ }
+
+ /**
+ * Create a XML file from a {@link Document}.
+ *
+ * @param document
+ * Document to be turned into a XML File.
+ * @param xmlFile
+ * XML file which will receive the {@link Document} stream.
+ *
+ * @throws PreflightingToolException
+ * Exception thrown when there are problems creating the XML
+ * file.
+ */
+ private static void createXmlFromDom(Document document, File xmlFile)
+ throws PreflightingToolException
+ {
+
+ StreamResult result = null;
+ DOMSource source = null;
+ Transformer transformer = null;
+ FileOutputStream fo = null;
+ StringWriter sw = null;
+
+ try
+ {
+ // get factory
+ TransformerFactory transformerFactory = TransformerFactory.newInstance();
+
+ // get transformer and configure it
+ transformer = transformerFactory.newTransformer();
+ transformer.setOutputProperty(OutputKeys.ENCODING, "utf-8"); //$NON-NLS-1$
+ transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "no"); //$NON-NLS-1$
+ transformer.setOutputProperty(OutputKeys.INDENT, "yes"); //$NON-NLS-1$
+ transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4"); //$NON-NLS-1$ //$NON-NLS-2$
+ // create the XML file
+ source = new DOMSource(document);
+ sw = new StringWriter();
+ result = new StreamResult(sw);
+ transformer.transform(source, result);
+ fo = new FileOutputStream(xmlFile);
+ fo.write(sw.toString().getBytes("utf-8"));
+
+ }
+ catch (Exception ex)
+ {
+ //log error, but try to continue validation without the XML file with problem
+ PreflightingLogger.error(ApkUtils.class, "Problems creating the XML file.", ex); //$NON-NLS-1$
+ }
+ finally
+ {
+ try
+ {
+ // Close streams and stuff
+ if (fo != null)
+ {
+ fo.close();
+ }
+
+ if (result.getWriter() != null)
+ {
+ result.getWriter().close();
+ }
+
+ if (sw != null)
+ {
+ sw.close();
+ }
+
+ }
+ catch (IOException e)
+ {
+ // do nothing
+ }
+ }
+ }
+
+ /**
+ * Create a new {@link Document}.
+ *
+ * @return A newly-created {@link Document} object.
+ *
+ * @throws PreflightingToolException
+ * Exception thrown when there are problems creating a new
+ * {@link Document}.
+ */
+ private static Document createNewDocument() throws PreflightingToolException
+ {
+ DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
+ DocumentBuilder documentBuilder = null;
+ try
+ {
+ documentBuilder = documentBuilderFactory.newDocumentBuilder();
+ }
+ catch (ParserConfigurationException pcException)
+ {
+ PreflightingLogger
+ .error(ApkUtils.class, "Problems creating DOM isntance.", pcException); //$NON-NLS-1$
+ throw new PreflightingToolException(
+ PreflightingCoreNLS.ApkUtils_DomInstanceProblemMessage, pcException);
+ }
+ // create DOM
+ Document document = documentBuilder.newDocument();
+ return document;
+ }
+
+ /**
+ * Create a Map holding the AAPT XML output info.
+ *
+ * @param aaptProccess
+ * AAPT execution process - its data will be processed here
+ * @param aaptPath
+ * APPT path
+ * @param apkPath
+ * APK path
+ * @param xmlFileName
+ * XML file name
+ *
+ * @return The Map holding APPT XML output info.
+ *
+ * @throws PreflightingToolException
+ * Exception thrown in case there are problems reading the APPT
+ * XML output info from the process.
+ */
+ public static Map<Integer, LineElement> readToMap(Process aaptProccess, String aaptPath,
+ String apkPath, String xmlFileName, Map<String, String> namespaceMap)
+ throws PreflightingToolException
+ {
+ InputStreamReader reader = new InputStreamReader(aaptProccess.getInputStream());
+ BufferedReader bReader = new BufferedReader(reader);
+
+ // list for the map
+ List<LineElement> lineList = new ArrayList<LineElement>();
+ LineElement lineElement;
+
+ String infoLine;
+ try
+ {
+ while ((infoLine = bReader.readLine()) != null)
+ {
+ if (infoLine.length() > 0)
+ {
+ lineElement = new LineElement();
+ if (infoLine.contains(ELEMENT_NODE) || infoLine.contains(ELEMENT_NODE))
+ {
+ lineElement.setType(LineElement.LineType.ELEMENT);
+ lineElement.setDepth(infoLine.split(ELEMENT_NODE)[0].length());
+ lineElement.setName(getElementLineName(infoLine));
+ lineList.add(lineElement);
+ }
+ else if (infoLine.contains(ATTRIBUTE_NODE))
+ {
+ lineElement.setType(LineElement.LineType.ATTRIBUTE);
+ lineElement.setDepth(infoLine.split(ATTRIBUTE_NODE)[0].length());
+ lineElement.setName(getElementLineName(infoLine));
+ lineElement.setValue(getElementLineValue(aaptPath, apkPath, xmlFileName,
+ infoLine));
+ lineList.add(lineElement);
+ }
+ else if (infoLine.contains(NAMESPACE_XMLNS))
+ {
+ String namespace = infoLine.split(NAMESPACE_XMLNS)[1].trim();
+ if (namespace.indexOf("=") != -1)
+ {
+ String id = namespace.substring(0, namespace.indexOf("="));
+ String url = namespace.substring(namespace.indexOf("=") + 1);
+ namespaceMap.put(id, url);
+ }
+ }
+ }
+ }
+ }
+ catch (IOException ioException)
+ {
+ PreflightingLogger.error(ApkUtils.class,
+ "Problems reading AAPT command execution result.", ioException); //$NON-NLS-1$
+ throw new PreflightingToolException(
+ PreflightingCoreNLS.ApkUtils_AaptResultReadProblemMessage, ioException);
+ }
+ finally
+ {
+ // close resources
+ try
+ {
+ if (bReader != null)
+ {
+ bReader.close();
+ }
+ if (reader != null)
+ {
+ reader.close();
+ }
+ }
+ catch (IOException ioException)
+ {
+ PreflightingLogger.error(ApkUtils.class,
+ "Problems reading AAPT command execution result.", ioException); //$NON-NLS-1$
+ throw new PreflightingToolException(
+ PreflightingCoreNLS.ApkUtils_AaptResultReadProblemMessage, ioException);
+ }
+ }
+
+ Map<Integer, LineElement> map = new HashMap<Integer, LineElement>();
+ Integer counter = 0;
+ for (LineElement elem : lineList)
+ {
+ ++counter;
+ map.put(counter, elem);
+ }
+ return map;
+
+ }
+
+ /**
+ * Get the Element Line?s Name from a text line. It could either be an
+ * Element or an Attribute.
+ *
+ * @param lineText
+ * Text Line where the Name will be retrieved.
+ *
+ * @return Returns the Name.
+ */
+ private static String getElementLineName(String lineText)
+ {
+ Matcher matcher = null;
+ Pattern pattern = null;
+ String matchText = null;
+ String name = null;
+
+ // try to match the element pattern
+ pattern = Pattern.compile("(E: .+ ){1}"); //$NON-NLS-1$
+ matcher = pattern.matcher(lineText);
+ // in case there is a match, populate the Line Element object
+ if (matcher.find())
+ {
+ matchText = matcher.group();
+ name = matchText.split(ELEMENT_NODE)[1].trim();
+ }
+ else
+ {
+ // try to match the attribute pattern
+ pattern = Pattern.compile("(A:){1}"); //$NON-NLS-1$
+ matcher = pattern.matcher(lineText);
+ if (matcher.find())
+ {
+ // since there is an element pattern, get its name
+ pattern = Pattern.compile("^ *A:\\s*[\\w:\\w]*"); //$NON-NLS-1$
+ matcher = pattern.matcher(lineText);
+ if (matcher.find())
+ {
+ matchText = matcher.group();
+ // get the name
+ name = matchText.split(ATTRIBUTE_NODE)[1];
+ // adjust it
+ name = name.trim();
+ }
+ }
+ }
+
+ return name;
+ }
+
+ /**
+ * Try to find the {@link Document} associated with a certain language
+ * directory path. In case nothing is found, null is returned.
+ *
+ * @param map
+ * {@link Map} in which the search will be made.
+ * @param languageDirectory
+ * Directory holding the path to be compared, in order to find
+ * the {@link Document} in the given {@link Map}. This Object is
+ * updated with a reference to the object in the {@link Map}.
+ *
+ * @return {@link Document} element associated, in case a match is
+ * successful.
+ */
+ private static Document findDocumentByLanguageDirectory(Map<File, Document> map,
+ File languageDirectory)
+ {
+ Document document = null;
+ Set<File> languageFolders = map.keySet();
+ if (languageFolders != null)
+ {
+ for (File languageFolder : languageFolders)
+ {
+ if (languageFolder.getPath().equals(languageDirectory.getPath()))
+ {
+ document = map.get(languageFolder);
+ languageDirectory = languageFolder;
+ break;
+ }
+ }
+ }
+ return document;
+ }
+
+ /**
+ * Given a certain parent {@link Element} and {@link Document}, append a
+ * child {@link Element} with Tag Name (which cannot be null), Node
+ * Attribute Name, Node Attribute Value (which both are null at the same
+ * time or none is null at all), Node Value (which can be null).
+ *
+ * @param nodeTagName
+ * Node Tag Name.
+ * @param nodeAttributeName
+ * Node Attribute Name.
+ * @param nodeAttributeValue
+ * Node Attribute Value.
+ * @param nodeValue
+ * Node Value.
+ * @param elementToBeApppendedTo
+ * Parent {@link Element} which the new created {@link Element}
+ * will be appended to.
+ * @param document
+ * {@link Document} which everything belongs to.
+ *
+ * @return Returns the created {@link Element}.
+ */
+ private static Element appendNewElementToNode(String nodeTagName, String nodeAttributeName,
+ String nodeAttributeValue, String nodeValue, Element elementToBeApppendedTo,
+ Document document)
+ {
+ // create element and append it
+ Element element = document.createElement(nodeTagName);
+ if ((nodeAttributeName != null) && (nodeAttributeValue != null))
+ {
+ element.setAttribute(nodeAttributeName, nodeAttributeValue);
+ }
+ if (nodeValue != null)
+ {
+ element.setTextContent(nodeValue);
+ }
+ elementToBeApppendedTo.appendChild(element);
+
+ return element;
+ }
+
+ /**
+ * Add all attributes and children in a {@link Document}, given a
+ * {@link LineElement}.
+ *
+ * @param document
+ * DOM where elements are added.
+ * @param map
+ * Map holding all {@link LineElement}s.
+ * @param elem
+ * Element to be added to the DOM.
+ * @param rootElement
+ * Root element.
+ */
+ private static void addNodes(Document document, Map<Integer, LineElement> map,
+ LineElement elem, Element rootElement)
+ {
+ if (elem.getType() == LineElement.LineType.ELEMENT)
+ {
+ // add element or root
+ Element element = document.createElement(elem.getName());
+ if (rootElement == null)
+ {
+ document.appendChild(element);
+ }
+ else
+ {
+ rootElement.appendChild(element);
+ }
+
+ // add children
+ for (Integer childElementMapIndex : elem.getChildLines())
+ {
+ LineElement childElement = map.get(childElementMapIndex);
+ // add all attributes from this node
+ if (childElement.getType() == LineElement.LineType.ATTRIBUTE)
+ {
+ element.setAttribute(childElement.getName(), childElement.getValue());
+ }
+ // add a child element
+ else
+ {
+ addNodes(document, map, childElement, element);
+ }
+
+ }
+ }
+ }
+
+ /**
+ * Retrieve the Value of an Text line.
+ *
+ * @param lineText
+ * Text line where the value will be retrieved from.
+ *
+ * @return Value retrieved.
+ */
+ private static String getElementLineValue(String aaptPath, String apkPath, String xmlFileName,
+ String lineText)
+ {
+ Matcher matcher = null;
+ Pattern pattern = null;
+ String matchText = null;
+ String name = null;
+
+ // Get the values, depending on their pattern
+
+ // start with Raw values
+ pattern = Pattern.compile("(\\(Raw: \".*\"\\)){1}"); //$NON-NLS-1$
+ matcher = pattern.matcher(lineText);
+ if (matcher.find())
+ {
+ matchText = matcher.group();
+ // get the element within ""
+ pattern = Pattern.compile("(\".*\"){1}"); //$NON-NLS-1$
+ matcher = pattern.matcher(matchText);
+ if (matcher.find())
+ {
+ matchText = matcher.group();
+ name = matchText.replaceAll("\"", "").trim(); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+ }
+ else
+ {
+ // get values after @
+ pattern = Pattern.compile("(\\)=@.*){1}"); //$NON-NLS-1$
+ matcher = pattern.matcher(lineText);
+ if (matcher.find())
+ {
+ matchText = matcher.group();
+ name = matchText.replaceAll("\\)=@", "").trim(); //$NON-NLS-1$ //$NON-NLS-2$
+ name = getResourceMatch(aaptPath, apkPath, xmlFileName, name);
+ }
+ else
+ {
+ // get values with type
+ pattern = Pattern.compile("(\\(type .*\\).*){1}"); //$NON-NLS-1$
+ matcher = pattern.matcher(lineText);
+ if (matcher.find())
+ {
+ matchText = matcher.group();
+ name = matchText.replaceAll("(\\(type .*\\))", ""); //$NON-NLS-1$ //$NON-NLS-2$
+ name = name.replace("0x", ""); //$NON-NLS-1$ //$NON-NLS-2$
+ try
+ {
+ long longValue = Long.parseLong(name, 16);
+ name = Long.toHexString(longValue).trim();
+
+ // TODO: correctly handle types instead of doing this kind of verification
+ if (lineText.contains(ANDROID_SMALL_SCREENS)
+ || lineText.contains("android:normalScreens")
+ || lineText.contains("android:largeScreens")
+ || lineText.contains("android:xlargeScreens")
+ || lineText.contains("android:anyDensity")
+ || lineText.contains("android:resizeable"))
+
+ {
+ name = longValue == 0 ? "false" : "true";
+ }
+
+ }
+ catch (NumberFormatException ex)
+ {
+ /*
+ * Do nothing because the number could not be converted
+ * to an integer. Leave it as it is to put in the XML
+ * file.
+ */
+ }
+ }
+ }
+ }
+
+ return name;
+ }
+
+ /**
+ * Get the Resource reference from a @x value in the AAPT XML output.
+ *
+ * @param aaptPath
+ * AAPT Path.
+ * @param apkPath
+ * APK Path.
+ * @param xmlFileName
+ * XML file Name
+ * @param resourceId
+ * Resource Id which the value will be retrieved.
+ *
+ * @return Value referenced by a resource Id.
+ */
+ private static String getResourceMatch(String aaptPath, String apkPath, String xmlFileName,
+ String resourceId)
+ {
+ xmlFileName = xmlFileName.substring(xmlFileName.indexOf(".tmp") + 5);
+
+ // we parse a xml file only once, so we check if its values are already
+ // stored
+ if (resourceValues.get(xmlFileName) == null)
+ {
+ HashMap<String, String> currentMap = new HashMap<String, String>();
+
+ BufferedReader bReader = null;
+ InputStreamReader reader = null;
+ try
+ {
+ Process aapt =
+ runAAPTCommandForExtractingResourcesAndValues(aaptPath, apkPath,
+ xmlFileName);
+
+ // read output and store it in a buffer
+ reader = new InputStreamReader(aapt.getInputStream());
+ bReader = new BufferedReader(reader);
+
+ String infoLine = ""; //$NON-NLS-1$
+ StringBuffer strBuf = new StringBuffer();
+ while ((infoLine = bReader.readLine()) != null)
+ {
+ strBuf.append(infoLine);
+ strBuf.append("\n"); //$NON-NLS-1$
+ }
+
+ // apply pattern to retrieve resource id and its value
+ Pattern pattern =
+ Pattern.compile("resource\\s[0-9a-fxA-FX]+\\s[a-zA-Z_0-9.]+:[a-z0-9./_]+:"); //$NON-NLS-1$
+ Matcher matcher = pattern.matcher(strBuf);
+
+ Pattern keyPattern = Pattern.compile("\\s[0-9a-fxA-FX]+\\s"); //$NON-NLS-1$
+ Pattern valuePattern = Pattern.compile(":[a-z0-9./_]+:"); //$NON-NLS-1$
+
+ while (matcher.find())
+ {
+ String match = matcher.group();
+ // key matcher
+ Matcher keyMatcher = keyPattern.matcher(match);
+ keyMatcher.find();
+ String key = keyMatcher.group();
+ key = key.trim();
+
+ // aapt output has a resource reference for each
+ // configuration
+ // e.g. a drawable resource can present three densities:
+ // hpdi, mpdi, lpdi
+ if (!currentMap.containsKey(key))
+ {
+ // value matcher
+ Matcher valueMatcher = valuePattern.matcher(match);
+ valueMatcher.find();
+ String value = valueMatcher.group();
+ value = "@" + value.substring(1, value.length() - 1); //$NON-NLS-1$
+
+ currentMap.put(key, value);
+ }
+ }
+ // store in global variable
+ resourceValues.put(xmlFileName, currentMap);
+ }
+ catch (Exception e)
+ {
+ PreflightingLogger.error(ApkUtils.class, e.getMessage());
+ }
+ finally
+ {
+ if (reader != null)
+ {
+ try
+ {
+ reader.close();
+ }
+ catch (IOException e)
+ {
+ //Do nothing.
+ }
+ }
+ if (bReader != null)
+ {
+ try
+ {
+ bReader.close();
+ }
+ catch (IOException e)
+ {
+ //Do nothing.
+ }
+ }
+ }
+ }
+
+ return resourceValues.get(xmlFileName).get(resourceId);
+ }
+}
+
+/**
+ * Class which holds each line information from the AAPT XML output.
+ */
+class LineElement
+{
+
+ /**
+ * Enumerator which determines which type of the emement it is to be put in
+ * the XML file.
+ */
+ public enum LineType
+ {
+ ELEMENT, ATTRIBUTE
+ }
+
+ private final List<Integer> childLines = new ArrayList<Integer>();
+
+ /**
+ * Get the list of children indexes.
+ *
+ * @return List of children indexes.
+ */
+ public List<Integer> getChildLines()
+ {
+ Collections.sort(childLines);
+ return childLines;
+ }
+
+ /**
+ * Add a child index representation.
+ *
+ * @param index
+ * Child inex representation.
+ */
+ public void addChildLine(Integer index)
+ {
+ childLines.add(index);
+ }
+
+ private LineType type;
+
+ /**
+ * Get the {@link LineType}.
+ *
+ * @return The {@link LineType}.
+ */
+ public LineType getType()
+ {
+ return type;
+ }
+
+ /**
+ * Set the {@link LineType}.
+ *
+ * @param type
+ * The {@link LineType}.
+ */
+ public void setType(LineType type)
+ {
+ this.type = type;
+ }
+
+ /**
+ * Get the Name.
+ *
+ * @return The name.
+ */
+ public String getName()
+ {
+ return name;
+ }
+
+ /**
+ * Set the Name.
+ *
+ * @param name
+ * The name.
+ */
+ public void setName(String name)
+ {
+ this.name = name;
+ }
+
+ /**
+ * Get the node depth.
+ *
+ * @return The node depth.
+ */
+ public int getDepth()
+ {
+ return depth;
+ }
+
+ /**
+ * Set the node depth.
+ *
+ * @param depth
+ * The node depth.
+ */
+ public void setDepth(int depth)
+ {
+ this.depth = depth;
+ }
+
+ private String name;
+
+ private String value;
+
+ /**
+ * Get the Node value.
+ *
+ * @return The node value.
+ */
+ public String getValue()
+ {
+ return value;
+ }
+
+ /**
+ * Set the node value.
+ *
+ * @param value
+ * The node value.
+ */
+ public void setValue(String value)
+ {
+ this.value = value;
+ }
+
+ private int depth;
+
+}
diff --git a/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/internal/utils/ApkUtils.java b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/internal/utils/ApkUtils.java
new file mode 100644
index 0000000..6382ce0
--- /dev/null
+++ b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/internal/utils/ApkUtils.java
@@ -0,0 +1,318 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.preflighting.core.internal.utils;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.List;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+import java.util.zip.CRC32;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
+
+import org.eclipse.core.runtime.Path;
+
+import com.motorolamobility.preflighting.core.exception.PreflightingToolException;
+import com.motorolamobility.preflighting.core.i18n.PreflightingCoreNLS;
+import com.motorolamobility.preflighting.core.logging.PreflightingLogger;
+import com.motorolamobility.preflighting.core.validation.ValidationManagerConfiguration;
+
+/**
+ * This class holds methods which deal with APK package.
+ */
+public final class ApkUtils
+{
+ public static final String APP_VALIDATOR_TEMP_DIR = "MotodevAppValidator";
+
+ private static final String JAVA_TEMP_DIR_PROPERTY = "java.io.tmpdir";
+
+ private static final String TEMP_DIR_PATH = System.getProperty(JAVA_TEMP_DIR_PROPERTY);
+
+ // Temp folder used for APK extracting
+ public static final File tmpAppValidatorFolder =
+ new File(TEMP_DIR_PATH, APP_VALIDATOR_TEMP_DIR);
+
+ private static final String RSA = ".rsa";
+
+ private static final String DSA = ".dsa";
+
+ public static final String APK_EXTENSION = ".apk";
+
+ public static final String ZIP_EXTENSION = ".zip";
+
+ private static final int BUFFER_SIZE = 1024;
+
+ /**
+ * Give an APK file, a tree directories and files are extracted,
+ * representing partially the project which generated the APK.
+ * <p>
+ * The files are created in a temporary directory.
+ *
+ * @param apkFile
+ * APK file which the project tree will be extracted from.
+ * @param sdkPath
+ * SDK path where the tools for extracting and interpreting the
+ * APK information will be used.
+ *
+ * @return A file object holding a tree of directories and files which
+ * represent partially the project which generated the APK.
+ *
+ * @throws PreflightingToolException
+ * Exception thrown when there are problems creating the files
+ * and directories structure.
+ */
+ public static File extractProjectFromAPK(File apkFile, String sdkPath)
+ throws PreflightingToolException
+ {
+ String apkName = apkFile.getName();
+
+ // Create a temp directory to contain all extracted packages, if needed
+ if (!tmpAppValidatorFolder.exists())
+ {
+
+ try
+ {
+ tmpAppValidatorFolder.mkdir();
+ }
+ catch (SecurityException se)
+ {
+ PreflightingLogger.error(ApkUtils.class,
+ "It was not possible to extract the android package.", se); //$NON-NLS-1$
+ throw new PreflightingToolException(
+ PreflightingCoreNLS.ApkUtils_ImpossibleExtractAndroidPackageMessage, se);
+ }
+ }
+
+ File tmpProjectFile;
+ try
+ {
+ tmpProjectFile = File.createTempFile(apkName, null, tmpAppValidatorFolder);
+ tmpProjectFile.delete();
+ tmpProjectFile.mkdir();
+ }
+ catch (IOException ioException)
+ {
+ PreflightingLogger.error(ApkUtils.class,
+ "It was not possible to extract the android package.", ioException); //$NON-NLS-1$
+ throw new PreflightingToolException(
+ PreflightingCoreNLS.ApkUtils_ImpossibleExtractAndroidPackageMessage,
+ ioException);
+ }
+
+ String extractionMode =
+ ValidationManagerConfiguration.getInstance().getProperty(
+ ValidationManagerConfiguration.ConfigProperties.APK_EXTRACTION_MODE
+ .getName());
+ if (extractionMode.equals(ValidationManagerConfiguration.ExtractionModes.APKTOOL_MODE
+ .getMode()))
+ {
+ ApktoolUtils.extractFilesFromApk(apkFile, tmpProjectFile);
+ }
+ else
+ {
+ AaptUtils.extractFilesFromAPK(apkFile, sdkPath, tmpProjectFile);
+ }
+
+ return tmpProjectFile;
+ }
+
+ /**
+ * Returns a handler to the temp directory used for extracting APKs.
+ *
+ * @return A handler to the temp directory.
+ */
+ public static File getAppValidatorTempApkFolder()
+ {
+ return tmpAppValidatorFolder;
+ }
+
+ /**
+ * Iterates over APK (jar entries) to populate
+ *
+ * @param projectFile
+ * @return
+ * @throws IOException
+ * @throws CertificateException
+ */
+ public static List<Certificate> populateCertificate(File projectFile) throws IOException,
+ CertificateException
+ {
+ List<Certificate> certList = new ArrayList<Certificate>();
+ JarFile jar = new JarFile(projectFile);
+ Enumeration<JarEntry> jarEntries = jar.entries();
+ while (jarEntries.hasMoreElements())
+ {
+ JarEntry entry = jarEntries.nextElement();
+ if (entry.getName().toLowerCase().contains(DSA)
+ || entry.getName().toLowerCase().contains(RSA))
+ {
+ certList.addAll(extractCertificate(jar, entry));
+ }
+ }
+ return certList;
+ }
+
+ /**
+ * Extracts certificate from APK
+ *
+ * @param jar
+ * @param entry
+ * rsa or dsa jar item
+ * @return
+ * @throws IOException
+ * I/O problem to read jar
+ * @throws CertificateException
+ * certificate has problems
+ */
+ private static List<Certificate> extractCertificate(JarFile jar, JarEntry entry)
+ throws IOException, CertificateException
+ {
+ List<Certificate> certList = new ArrayList<Certificate>();
+ InputStream inStream = null;
+ try
+ {
+ CertificateFactory cf = CertificateFactory.getInstance("X.509");
+ inStream = jar.getInputStream(entry);
+ Collection<? extends Certificate> c = cf.generateCertificates(inStream);
+ Iterator<? extends Certificate> i = c.iterator();
+ while (i.hasNext())
+ {
+ Certificate cert = i.next();
+ certList.add(cert);
+ }
+ }
+ finally
+ {
+ if (inStream != null)
+ {
+ inStream.close();
+ }
+ }
+ return certList;
+ }
+
+ /**
+ * Unzip zipFile and returns the directory created with its contents
+ * @param zipFile
+ * @return
+ * @throws PreflightingToolException
+ */
+ public static File unzip(File zipFile) throws PreflightingToolException
+ {
+ File tempExtractionDir = null;
+ ZipInputStream apkInputStream = null;
+ FileOutputStream apkOutputStream = null;
+
+ try
+ {
+ //crate MOTODEV temp folder
+ if (!tmpAppValidatorFolder.exists())
+ {
+ tmpAppValidatorFolder.mkdir();
+ }
+ //create extraction folder
+ tempExtractionDir = File.createTempFile(zipFile.getName(), null, tmpAppValidatorFolder);
+ tempExtractionDir.delete();
+ tempExtractionDir.mkdir();
+
+ //open zipFile stream
+ apkInputStream = new ZipInputStream(new FileInputStream(zipFile.getAbsolutePath()));
+ ZipEntry apkZipEntry = apkInputStream.getNextEntry();
+
+ byte[] buf = new byte[BUFFER_SIZE];
+ CRC32 crc = new CRC32();
+
+ //while there are apks inside zip file
+ while (apkZipEntry != null)
+ {
+ crc.reset();
+ if (apkZipEntry.getName().endsWith(APK_EXTENSION))
+ {
+ //file to be created (apk)
+ try
+ {
+ apkOutputStream =
+ new FileOutputStream(tempExtractionDir.getAbsolutePath()
+ + Path.SEPARATOR + apkZipEntry.getName());
+
+ int length = 0;
+ //creates apk and updates CRC during the process
+ while ((length = apkInputStream.read(buf, 0, BUFFER_SIZE)) > -1)
+ {
+ apkOutputStream.write(buf, 0, length);
+ crc.update(buf, 0, length);
+ }
+
+ //test if extraction went fine
+ if (crc.getValue() != apkZipEntry.getCrc())
+ {
+ throw new PreflightingToolException(PreflightingCoreNLS.bind(
+ PreflightingCoreNLS.ApkUtils_ZipExtractionFile,
+ apkZipEntry.getName()));
+ }
+ }
+ finally
+ {
+ if (apkOutputStream != null)
+ {
+ try
+ {
+ apkOutputStream.close();
+ }
+ catch (IOException e)
+ {
+ // do nothing
+ }
+ }
+ }
+ }
+ apkZipEntry = apkInputStream.getNextEntry();
+ }
+ }
+ catch (IOException ioe)
+ {
+ //error during extraction, abort validation
+ throw new PreflightingToolException(PreflightingCoreNLS.ApkUtils_ZipExtraction, ioe);
+ }
+ finally
+ {
+ try
+ {
+ if (apkInputStream != null)
+ {
+ apkInputStream.close();
+ }
+ }
+ catch (IOException e)
+ {
+ // do nothing
+ }
+ }
+
+ return tempExtractionDir;
+ }
+} \ No newline at end of file
diff --git a/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/internal/utils/ApktoolUtils.java b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/internal/utils/ApktoolUtils.java
new file mode 100644
index 0000000..2f059e0
--- /dev/null
+++ b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/internal/utils/ApktoolUtils.java
@@ -0,0 +1,982 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.preflighting.core.internal.utils;
+
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Scanner;
+import java.util.Set;
+import java.util.StringTokenizer;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+
+import org.eclipse.core.runtime.FileLocator;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.jdt.core.dom.PrimitiveType;
+import org.eclipse.osgi.util.NLS;
+import org.osgi.framework.Bundle;
+
+import com.motorolamobility.preflighting.core.PreflightingCorePlugin;
+import com.motorolamobility.preflighting.core.applicationdata.Element;
+import com.motorolamobility.preflighting.core.applicationdata.SourceFolderElement;
+import com.motorolamobility.preflighting.core.exception.PreflightingToolException;
+import com.motorolamobility.preflighting.core.i18n.PreflightingCoreNLS;
+import com.motorolamobility.preflighting.core.logging.PreflightingLogger;
+import com.motorolamobility.preflighting.core.source.model.Constant;
+import com.motorolamobility.preflighting.core.source.model.Field;
+import com.motorolamobility.preflighting.core.source.model.Instruction;
+import com.motorolamobility.preflighting.core.source.model.Invoke;
+import com.motorolamobility.preflighting.core.source.model.Method;
+import com.motorolamobility.preflighting.core.source.model.SourceFileElement;
+import com.motorolamobility.preflighting.core.source.model.Variable;
+
+/**
+ * Extracts smali representation for the files inside an apk
+ */
+public final class ApktoolUtils
+{
+ /**
+ *
+ */
+ private static final String PARAM_SEPARATOR = ";";
+
+ private static final String INNER_CLASS = "InnerClass";
+
+ private static final String SEMICOLON = PARAM_SEPARATOR;
+
+ private static final String RPAREN = ")";
+
+ private static final String LPAREN = "(";
+
+ private static final String COLON = ":";
+
+ private static final String TYPE_PREFIX_L = "L";
+
+ private static final String ARRAYTYPE_PREFIX = "[";
+
+ private static final String PARAMETERS_SEPARATOR = SEMICOLON;
+
+ private static final String CONST = "const";
+
+ private static final String METHODITEMS_SEPARATOR2 = "->";
+
+ private static final String METHODITEMS_SEPARATOR1 = ";->";
+
+ private static final String INVOKE = "invoke";
+
+ private static final String END_METHOD = ".end method";
+
+ private static final String CONSTRUCTOR = "constructor";
+
+ private static final String ANNOTATION = ".annotation";
+
+ private static final String METHOD = ".method";
+
+ private static final String ASSIGNMENT_OPERATOR = "=";
+
+ private static final String FINAL = "final";
+
+ private static final String STATIC = "static";
+
+ private static final String FIELD = ".field";
+
+ private static final String SOURCE = ".source";
+
+ private static final String SUPER = ".super";
+
+ private static final String CLASS = ".class";
+
+ private static final String LINE = ".line";
+
+ private static final String VIRTUAL_METHODS = "# virtual methods";
+
+ private static final String DIRECT_METHODS = "# direct methods";
+
+ private static final String INSTANCE_FIELDS = "# instance fields";
+
+ private static final String STATIC_FIELDS = "# static fields";
+
+ private static final String SMALI = ".smali";
+
+ private static final String APK = ".apk";
+
+ private static final String APKTOOL_JAR_PATH = "/apktool/apktool.jar";
+
+ private static Map<String, String> smaliTypeMap = new HashMap<String, String>(9);
+
+ static
+ {
+ smaliTypeMap.put("V", PrimitiveType.VOID.toString());
+ smaliTypeMap.put("Z", PrimitiveType.BOOLEAN.toString());
+ smaliTypeMap.put("B", PrimitiveType.BYTE.toString());
+ smaliTypeMap.put("S", PrimitiveType.SHORT.toString());
+ smaliTypeMap.put("C", PrimitiveType.CHAR.toString());
+ smaliTypeMap.put("I", PrimitiveType.INT.toString());
+ smaliTypeMap.put("J", PrimitiveType.LONG.toString());
+ smaliTypeMap.put("F", PrimitiveType.FLOAT.toString());
+ smaliTypeMap.put("D", PrimitiveType.DOUBLE.toString());
+ }
+
+ /**
+ * Extracts smali representation for the files inside an apk
+ * @param apkFolder
+ * @param appName apk filename
+ * @return
+ * @throws InterruptedException
+ * @throws IOException
+ * @throws AndrolibException
+ */
+ public static SourceFolderElement extractJavaModel(File apkFolder, Element parent, File smaliDir)
+ throws IOException, InterruptedException
+ {
+ SourceFolderElement model = new SourceFolderElement(apkFolder, parent, true);
+ Set<File> smaliFiles = visitFolderToIdentifySmalis(smaliDir);
+ for (File smaliFile : smaliFiles)
+ {
+ SourceFileElement m = readFromSmali(smaliFile, model);
+ model.getSourceFileElements().add(m);
+ }
+ return model;
+ }
+
+ /**
+ * Extract information from methods, fields, constants, invocations, etc
+ * from a smali file
+ * @param smali file to read
+ * @param parent
+ * @return smali model
+ * @throws FileNotFoundException
+ */
+ private static SourceFileElement readFromSmali(File smali, Element parent)
+ throws FileNotFoundException
+ {
+ SourceFileElement model = new SourceFileElement(smali, parent);
+ FileInputStream fileInputStream = new FileInputStream(smali);
+ Scanner scanner = new Scanner(fileInputStream);
+ try
+ {
+ boolean readingStaticFields = false;
+ boolean readingInstanceFields = false;
+ boolean readingDirectMethods = false;
+ boolean readingVirtualMethods = false;
+ int lineInfo = -1;
+ while (scanner.hasNextLine())
+ {
+ String line = scanner.nextLine();
+ if ((line != null) && !line.equals(""))
+ {
+ if (line.trim().startsWith(STATIC_FIELDS))
+ {
+ readingStaticFields = true;
+ readingInstanceFields =
+ readingDirectMethods = readingVirtualMethods = false;
+ }
+ else if (line.trim().startsWith(INSTANCE_FIELDS))
+ {
+ readingInstanceFields = true;
+ readingStaticFields = readingDirectMethods = readingVirtualMethods = false;
+ }
+ else if (line.trim().startsWith(DIRECT_METHODS))
+ {
+ readingDirectMethods = true;
+ readingStaticFields = readingInstanceFields = readingVirtualMethods = false;
+ }
+ else if (line.trim().startsWith(VIRTUAL_METHODS))
+ {
+ readingVirtualMethods = true;
+ readingStaticFields = readingInstanceFields = readingDirectMethods = false;
+ }
+ else if (line.trim().startsWith(LINE))
+ {
+ StringTokenizer token = new StringTokenizer(line);
+ if (token.hasMoreTokens())
+ {
+ token.nextToken();
+ }
+ if (token.hasMoreTokens())
+ {
+ try
+ {
+ lineInfo = Integer.parseInt(token.nextToken());
+ }
+ catch (NumberFormatException e)
+ {
+ PreflightingLogger.error("Could not get line Number for line "
+ + line);
+ }
+ }
+ }
+ else if (line.trim().startsWith(CLASS))
+ {
+ String[] tokens = line.trim().split(" ");
+ for (int i = 0; i < tokens.length; i++)
+ {
+ if ((tokens[i] != null) && tokens[i].startsWith(TYPE_PREFIX_L))
+ {
+ model.setClassFullPath(getType(tokens[i]));
+ break;
+ }
+ }
+ }
+ else if (line.trim().startsWith(SUPER))
+ {
+ String[] tokens = line.trim().split(" ");
+ if (tokens.length == 2)
+ {
+ model.setSuperclassName(tokens[1].substring(1)); //remove first L
+ }
+ }
+ else if (line.trim().startsWith(SOURCE))
+ {
+ String[] tokens = line.trim().split(" ");
+ if (tokens.length == 2)
+ {
+ String sourceName = tokens[1];
+ sourceName = sourceName.substring(1, sourceName.length() - 1); //Remove quotes
+ model.setSourceName(sourceName);
+ String pkg = getPkg(model.getClassFullPath());
+ model.setFile(new File(pkg.replace('.', '/') + "/" + sourceName)); //Smali file makes no sense for checkers because it's temporary.
+ }
+ }
+ else if (line.trim().startsWith(FIELD))
+ {
+ Field field = new Field();
+ if (line.contains(STATIC))
+ {
+ field.setStatic(true);
+ }
+ if (line.contains(FINAL))
+ {
+ field.setFinal(true);
+ }
+ String[] tokens = line.trim().split(" ");
+ String visibility = tokens[1];
+
+ field.setVisibility(visibility);
+ for (int i = 0; i < tokens.length; i++)
+ {
+ //find attribute name and type (separated by colon)
+ if (tokens[i].trim().contains(COLON))
+ {
+ String[] aux = tokens[i].trim().split(COLON);
+ String name = model.getName();
+ if (name.contains("$"))
+ {
+ name =
+ name.substring(name.indexOf("$") + 1,
+ name.lastIndexOf("."));
+ field.setName(name + "." + aux[0]);
+ }
+ if (aux.length > 1)
+ {
+ String typeKey = aux[1];
+ String type = "";
+ type = getType(typeKey);
+
+ field.setType(type);
+ }
+ break;
+ }
+ }
+ if (ASSIGNMENT_OPERATOR.equals(tokens[tokens.length - 2]))
+ {
+ //there is assignment = <value>
+ field.setValue(tokens[tokens.length - 1]);
+ }
+ if (readingInstanceFields)
+ {
+ model.getInstanceFields().add(field);
+ }
+ else if (readingStaticFields)
+ {
+ model.getStaticFields().add(field);
+ }
+ }
+ else if (line.trim().startsWith(METHOD))
+ {
+ //Map representing the Smali variable index(vX) -> variable name
+ Map<String, String> varMap = new HashMap<String, String>();
+ Method method = new Method();
+ if (line.contains(STATIC))
+ {
+ method.setStatic(true);
+ }
+ if (line.contains(CONSTRUCTOR))
+ {
+ method.setConstructor(true);
+ }
+ String[] tokens = line.trim().split(" ");
+ for (int i = 0; i < tokens.length; i++)
+ {
+ if (tokens[i].trim().contains(LPAREN))
+ {
+ int lP = tokens[i].trim().indexOf(LPAREN);
+ if (lP >= 0)
+ {
+ String temp = tokens[i].trim().substring(0, lP);
+ method.setMethodName(temp);
+ int rP = tokens[i].trim().indexOf(RPAREN);
+ String params = null;
+ if (rP >= 0)
+ {
+ params = tokens[i].trim().substring(lP + 1, rP);
+ if (!params.equals(""))
+ {
+ String[] methodParams = params.split(SEMICOLON);
+ for (String param : methodParams)
+ {
+ //Get all params (it gets only one in cases like IIL<type>; that results integer, integer, type, integer
+ List<String> paramTypes =
+ getParamTypes(param + PARAMETERS_SEPARATOR);
+ method.getParameterTypes().addAll(paramTypes);
+ }
+ }
+ String returnType = tokens[i].trim().substring(rP + 1);
+ method.setReturnType(getType(returnType));
+ }
+ break;
+ }
+
+ }
+ }
+ line = scanner.nextLine();
+ Invoke invoke = null;
+ while (!line.trim().startsWith(END_METHOD))
+ {
+ //read instructions
+ if (line.trim().startsWith(INVOKE))
+ {
+ invoke = new Invoke();
+ if (line.contains("-" + Method.DIRECT))
+ {
+ invoke.setType(Method.DIRECT);
+ }
+ else if (line.contains("-" + Method.VIRTUAL))
+ {
+ invoke.setType(Method.VIRTUAL);
+ }
+ int objectIdxStart = line.indexOf('{') + 1;
+ int objectIdxEnd = line.indexOf('}');
+ String paramsIds = line.substring(objectIdxStart, objectIdxEnd);
+ String objIdx = null;
+ List<String> paramNames = new ArrayList<String>();
+ if (paramsIds.contains(","))
+ {
+ String[] paramsSplit = paramsIds.split(", ");
+ // the returned object is always the first item
+ objIdx = paramsSplit[0];
+ // the other items are parameters
+ for (int paramsSplitIndex = 1; paramsSplitIndex < paramsSplit.length; paramsSplitIndex++)
+ {
+ if (varMap.containsKey(paramsSplit[paramsSplitIndex]))
+ {
+ paramNames.add(varMap
+ .get(paramsSplit[paramsSplitIndex]));
+ }
+ else
+ {
+ paramNames.add(paramsSplit[paramsSplitIndex]);
+ }
+ }
+ }
+ else
+ {
+ // doesn't have parameters
+ objIdx = !paramsIds.equals("p0") ? paramsIds : null;
+ }
+
+ String objectName = null;
+ if (varMap.containsKey(objIdx))
+ {
+ objectName = varMap.get(objIdx);
+ }
+ else
+ {
+ objectName = objIdx;
+ }
+ invoke.setObjectName(objectName);
+ invoke.setParameterNames(paramNames);
+
+ int commaInd = objectIdxEnd;
+ String callmethod;
+ if (commaInd >= 0)
+ {
+ callmethod = line.substring(commaInd + 2, line.length()).trim();
+ String[] methodItems = callmethod.split(METHODITEMS_SEPARATOR1);
+ invoke.setClassCalled(getType(methodItems[0]));
+ if (methodItems.length <= 1)
+ {
+ //try to use -> only
+ methodItems = callmethod.split(METHODITEMS_SEPARATOR2);
+ }
+ extractInvoke(invoke, methodItems);
+ invoke.setLine(lineInfo);
+ invoke.setSourceFileFullPath(model.getSourceFileFullPath());
+ }
+
+ method.getInstructions().add(invoke);
+ }
+ else if (line.trim().startsWith(CONST))
+ {
+ String[] split = line.trim().split(" ");
+ Constant c = new Constant();
+ c.setType(split[0]);
+ if (split.length > 2)
+ {
+ c.setValue(split[2]);
+ }
+ method.getInstructions().add(c);
+ c.setSourceFileFullPath(model.getSourceFileFullPath());
+ }
+ else if (line.trim().startsWith(LINE))
+ {
+ StringTokenizer token = new StringTokenizer(line);
+ if (token.hasMoreTokens())
+ {
+ token.nextToken();
+ }
+ if (token.hasMoreTokens())
+ {
+ try
+ {
+ lineInfo = Integer.parseInt(token.nextToken());
+ }
+ catch (NumberFormatException e)
+ {
+ PreflightingLogger
+ .error("Could not get line Number for line " + line);
+ }
+ }
+ }
+ else if (line.trim().startsWith(".local ")) //Variable declaration
+ {
+ String[] split = line.trim().split(", ");
+ String varMapIdx = split[0];
+ varMapIdx = varMapIdx.replace(".local ", "");
+
+ String[] varNameType = split[1].split(COLON);
+ String varName = varNameType[0].trim();
+ String varType = varNameType[1];
+ varType = getType(varType);
+
+ Variable variable = new Variable();
+ variable.setName(varName);
+ variable.setType(varType);
+ variable.setLineNumber(lineInfo);
+ varMap.put(varMapIdx, varName);
+ method.addVariable(variable);
+ List<Instruction> instructions = method.getInstructions();
+ for (Instruction instruction : instructions)
+ {
+ if (instruction instanceof Invoke)
+ {
+ Invoke inv = (Invoke) instruction;
+ String invokeReturnVar = inv.getAssignedVariable();
+ if ((invokeReturnVar != null)
+ && invokeReturnVar.equals(varMapIdx))
+ {
+ inv.setAssignedVariable(varName);
+ }
+ String objectName = inv.getObjectName();
+ if ((objectName != null) && objectName.equals(varMapIdx))
+ {
+ inv.setObjectName(varName);
+ }
+ }
+ }
+ }
+ else if (line.trim().startsWith("move-result-object"))
+ {
+ String resultVarIdx =
+ line.trim().substring(line.trim().lastIndexOf(" ") + 1);
+ String varName =
+ varMap.containsKey(resultVarIdx) ? varMap.get(resultVarIdx)
+ : resultVarIdx;
+ if (invoke != null)
+ {
+ invoke.setAssignedVariable(varName);
+ }
+ }
+
+ line = scanner.nextLine();
+ }
+
+ if (readingDirectMethods)
+ {
+ model.getDirectMethods().add(method);
+ }
+ else if (readingVirtualMethods)
+ {
+ model.getVirtualMethods().add(method);
+ }
+ }
+ // TODO : Here one must fetch the correct inner class name
+ else if (line.startsWith(ANNOTATION) && line.contains(INNER_CLASS))
+ {
+ model.setInnerClass(true);
+ }
+ }
+ }
+ }
+ finally
+ {
+ if (fileInputStream != null)
+ {
+ try
+ {
+ fileInputStream.close();
+ }
+ catch (IOException e)
+ {
+ //DO nothing.
+ }
+ }
+ scanner.close();
+ }
+ return model;
+ }
+
+ private static String getPkg(String classFullPath)
+ {
+ int dotLastIndex = classFullPath.lastIndexOf('.');
+ String pkg = "";
+ if (dotLastIndex > 0)
+ {
+ pkg = classFullPath.substring(0, dotLastIndex);
+ }
+ return pkg;
+ }
+
+ /**
+ * @param params list of parameters
+ * @return list of param types
+ */
+ private static List<String> getParamTypes(String params)
+ {
+ StringBuilder replacementStr = new StringBuilder();
+ List<String> paramTypes = new ArrayList<String>();
+ for (int i = 0; i < params.length(); i++)
+ {
+ String c = "" + params.charAt(i);
+ if (c.equals(ARRAYTYPE_PREFIX))
+ {
+ replacementStr.append(c);
+ }
+ else if (c.equals(TYPE_PREFIX_L))
+ {
+ replacementStr.append(c);
+ do
+ {
+ i++;
+ c = "" + params.charAt(i);
+ replacementStr.append(c);
+ }
+ while (!c.equals(PARAM_SEPARATOR));
+ replacementStr.append(PARAM_SEPARATOR);
+ }
+ else if (smaliTypeMap.containsKey(c))
+ {
+ //it is a simple type
+ replacementStr.append(c + PARAM_SEPARATOR);
+ }
+ //PARAM_SEPARATOR chars are not expected to be logged
+ else if (!c.equals(PARAM_SEPARATOR))
+ {
+ PreflightingLogger
+ .error("Chars not recognized. " + c + ". Check params: " + params);
+ }
+ }
+ StringTokenizer tokenizer = new StringTokenizer(replacementStr.toString(), PARAM_SEPARATOR);
+ while (tokenizer.hasMoreTokens())
+ {
+ String type = tokenizer.nextToken();
+ paramTypes.add(getType(type));
+ }
+ return paramTypes;
+ }
+
+ /**
+ * @param typeKey
+ * @return if it is an array appends to the type [], otherwise returns the type given for the source code
+ */
+ private static String getType(String typeKey)
+ {
+ String type;
+ boolean isArray = false;
+ if (typeKey.startsWith(ARRAYTYPE_PREFIX))
+ {
+ isArray = true;
+ typeKey = typeKey.substring(1);
+ }
+ if (typeKey.startsWith(TYPE_PREFIX_L))
+ {
+ type = typeKey.replaceAll("/", ".");
+ type = type.replaceAll(PARAM_SEPARATOR, "");
+ type =
+ type.contains("$") ? type.substring(1, type.lastIndexOf('$')) : type
+ .substring(1);
+ }
+ else
+ {
+ type = smaliTypeMap.get(typeKey);
+ }
+ if (isArray)
+ {
+ type += "[]";
+ }
+ if (type == null)
+ {
+ PreflightingLogger.error("Type not recognized. Check statement: " + typeKey);
+ }
+ return type;
+ }
+
+ /**
+ * Extract information about invocation (the part after -> or ;->)
+ * @param invoke
+ * @param methodItems
+ */
+ private static void extractInvoke(Invoke invoke, String[] methodItems)
+ {
+ int lparenInd = methodItems[1].indexOf(LPAREN);
+ int rparenInd = methodItems[1].indexOf(RPAREN);
+ String metName = methodItems[1].substring(0, lparenInd);
+ invoke.setMethodName(metName);
+ String param = methodItems[1].substring(lparenInd + 1, rparenInd);
+ if (!param.equals(""))
+ {
+ String[] params = param.split(PARAMETERS_SEPARATOR);
+ invoke.setParameterTypes(Arrays.asList(params));
+ }
+ String retType = methodItems[1].substring(rparenInd + 1);
+ invoke.setReturnType(getType(retType));
+ }
+
+ /**
+ * Runs apktool to extract java and resource files
+ * @param apk
+ * @param outputDir
+ * @throws PreflightingToolException
+ */
+ public static void extractFilesFromApk(File apk, File tempProjectFolder)
+ throws PreflightingToolException
+ {
+ Process proc = null;
+ try
+ {
+ Bundle bundle = PreflightingCorePlugin.getContext().getBundle();
+ URL apktoolURL = bundle.getEntry(APKTOOL_JAR_PATH);
+ apktoolURL = FileLocator.toFileURL(apktoolURL);
+ String path = apktoolURL.getPath();
+
+ if (Platform.getOS().equals(Platform.OS_WIN32) && path.startsWith("/"))
+ {
+ path = path.substring(1);
+ }
+ String[] args =
+ new String[]
+ {
+ "java", "-Xmx512m", "-jar", path, "d", "-f", apk.getAbsolutePath(),
+ tempProjectFolder.getAbsolutePath()
+ };
+
+ proc = Runtime.getRuntime().exec(args);
+ }
+ catch (Exception e)
+ {
+ PreflightingLogger.error(ProjectUtils.class,
+ PreflightingCoreNLS.ProjectUtils_ErrorExecutingApkTool, e); //$NON-NLS-1$
+ throw new PreflightingToolException(
+ PreflightingCoreNLS.ProjectUtils_ErrorExecutingApkTool);
+ }
+
+ try
+ {
+ //verify if APK is not empty (otherwise command will not return)
+ if (isApkValid(apk))
+ {
+ //trying to extract jar - if fail, jar is probably corrupted
+ File aux = File.createTempFile("apktemp", "folder");
+ aux.getParentFile().mkdirs();
+ File temp = new File(aux.getParentFile(), "apks");
+ temp.mkdir();
+ boolean fileOk = unpackZipFile(apk, temp.getAbsolutePath());
+ if (fileOk && (proc != null) && (proc.waitFor() != 0))
+ {
+ throw new PreflightingToolException(
+ PreflightingCoreNLS.ProjectUtils_ErrorExecutingApkTool);
+ }
+ else if (!fileOk)
+ {
+ //apk corrupted
+ throw new PreflightingToolException(NLS.bind(
+ PreflightingCoreNLS.ApkUtils_ZipExtractionFile, apk.getAbsolutePath()));
+ }
+ deleteDirRecursively(temp);
+ }
+ else
+ {
+ //invalid apk
+ throw new PreflightingToolException(NLS.bind(
+ PreflightingCoreNLS.ApkUtils_ZipExtractionFile, apk.getAbsolutePath()));
+ }
+ }
+ catch (InterruptedException e)
+ {
+ throw new PreflightingToolException(
+ PreflightingCoreNLS.ProjectUtils_ErrorExecutingApkTool, e);
+ }
+ catch (IOException e)
+ {
+ throw new PreflightingToolException(NLS.bind(
+ PreflightingCoreNLS.ApkUtils_ZipExtractionFile, apk.getAbsolutePath()));
+ }
+ }
+
+ private static boolean isApkValid(File apk)
+ {
+ return (apk != null)
+ && (apk.length() > 0)
+ && (apk.getName().toLowerCase().endsWith("apk") || apk.getName().toLowerCase()
+ .endsWith("zip"));
+ }
+
+ private static void visitFolderToIdentifySmalis(File basefile, Set<File> classesFiles)
+ {
+ if ((basefile != null) && basefile.isDirectory())
+ {
+ File[] subfolders = basefile.listFiles();
+ for (File file : subfolders)
+ {
+ visitFolderToIdentifySmalis(file, classesFiles);
+ }
+ }
+ else if ((basefile != null) && basefile.isFile())
+ {
+ if (basefile.getName().endsWith(SMALI))
+ {
+ classesFiles.add(basefile);
+ }
+ }
+ }
+
+ private static Set<File> visitFolderToIdentifySmalis(File rootFolder)
+ {
+ Set<File> classesFiles = new HashSet<File>();
+ visitFolderToIdentifySmalis(rootFolder, classesFiles);
+ return classesFiles;
+ }
+
+ private static void visitFolderToIdentifyApks(File basefile, Set<File> apkFiles)
+ {
+ if ((basefile != null) && basefile.isDirectory())
+ {
+ File[] subfolders = basefile.listFiles();
+ for (File file : subfolders)
+ {
+ visitFolderToIdentifyApks(file, apkFiles);
+ }
+ }
+ else if ((basefile != null) && basefile.isFile())
+ {
+ if (basefile.getName().endsWith(APK))
+ {
+ apkFiles.add(basefile);
+ }
+ }
+ }
+
+ private static Set<File> visitFolderToIdentifyApks(File rootFolder)
+ {
+ Set<File> classesFiles = new HashSet<File>();
+ visitFolderToIdentifyApks(rootFolder, classesFiles);
+ return classesFiles;
+ }
+
+ /**
+ * Unpack a zip file.
+ *
+ * @param file the file
+ * @param destination the destination path or null to unpack at the same directory of file
+ * @return true if unpacked, false otherwise
+ */
+ private static boolean unpackZipFile(File file, String destination)
+ throws PreflightingToolException
+ {
+ ZipFile zipFile = null;
+ String extractDestination = destination != null ? destination : file.getParent();
+ if (!extractDestination.endsWith(File.separator))
+ {
+ extractDestination += File.separator;
+ }
+
+ boolean unziped = true;
+ try
+ {
+ zipFile = new ZipFile(file);
+ }
+ catch (Throwable e)
+ {
+ unziped = false;
+ }
+ if (zipFile != null)
+ {
+ Enumeration<? extends ZipEntry> entries = zipFile.entries();
+ InputStream input = null;
+ OutputStream output = null;
+ while (entries.hasMoreElements())
+ {
+ try
+ {
+ ZipEntry entry = entries.nextElement();
+ String name = entry.getName();
+
+ if (name.startsWith(".."))
+ {
+ throw new PreflightingToolException(
+ PreflightingCoreNLS.ApkToolUtils_MalformedAPK);
+ }
+
+ File newFile = new File(extractDestination + name);
+ if (entry.isDirectory())
+ {
+ newFile.mkdirs();
+ }
+ else
+ {
+ newFile.getParentFile().mkdirs();
+ if (newFile.createNewFile())
+ {
+ input = zipFile.getInputStream(entry);
+ output = new BufferedOutputStream(new FileOutputStream(newFile));
+ copyStreams(input, output);
+ }
+ }
+ }
+ catch (PreflightingToolException pe)
+ {
+ unziped = false;
+ throw pe;
+ }
+ catch (Throwable t)
+ {
+ unziped = false;
+ }
+ finally
+ {
+ try
+ {
+ if (input != null)
+ {
+ input.close();
+ }
+ if (output != null)
+ {
+ output.close();
+ }
+ }
+ catch (Throwable t)
+ {
+ //do nothing
+ }
+ }
+ }
+ }
+ return unziped;
+ }
+
+ /**
+ * Copy the input stream to the output stream
+ * @param inputStream
+ * @param outputStream
+ * @throws IOException
+ */
+ private static void copyStreams(InputStream inputStream, OutputStream outputStream)
+ throws IOException
+ {
+ byte[] buffer = new byte[1024];
+ int length;
+
+ while ((length = inputStream.read(buffer)) >= 0)
+ {
+ outputStream.write(buffer, 0, length);
+ }
+ }
+
+ /**
+ * This method deletes the directory, all files and all subdirectories under
+ * it. If a deletion fails, the method stops attempting to delete and
+ * returns false.
+ *
+ * @param directory
+ * The directory to be deleted
+ * @return Returns true if all deletions were successful. If the directory
+ * doesn't exist returns false.
+ * @throws IOException
+ * When the parameter isn't a directory
+ */
+ private static boolean deleteDirRecursively(File directory) throws IOException
+ {
+ String dirName = "";
+ boolean success = true;
+ if (directory.exists())
+ {
+ if (directory.isDirectory())
+ {
+ dirName = directory.getName();
+ File[] children = directory.listFiles();
+ for (File element : children)
+ {
+ if (element.isFile())
+ {
+ success = success && element.delete();
+ }
+ else
+ {
+ success = success && deleteDirRecursively(element);
+ }
+ }
+ success = success && directory.delete();
+ }
+ else
+ {
+ String errorMessage = directory.getName() + " is not a diretory.";
+ throw new IOException(errorMessage);
+ }
+ }
+ else
+ {
+ String errorMessage = "The directory does not exist.";
+ success = false;
+ throw new IOException(errorMessage);
+ }
+ return success;
+ }
+}
diff --git a/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/internal/utils/MethodPermissionCSVReader.java b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/internal/utils/MethodPermissionCSVReader.java
new file mode 100644
index 0000000..dc760ee
--- /dev/null
+++ b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/internal/utils/MethodPermissionCSVReader.java
@@ -0,0 +1,208 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.preflighting.core.internal.utils;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+import com.motorolamobility.preflighting.core.source.model.PermissionGroups;
+
+public final class MethodPermissionCSVReader
+{
+
+ private static final String ANDROID_PERMISSION_PREFIX = "android.permission.";
+
+ /**
+ * For tests
+ * @param args
+ */
+ public static void main(String[] args)
+ {
+ File f =
+ new File(
+ "C:\\motodev\\motodev\\android\\src\\plugins\\preflighting.core\\files\\method_permission_list_4.0.csv");
+ try
+ {
+ readMapMethodToPermission(new InputStreamReader(new FileInputStream(f)));
+ }
+ catch (IOException e)
+ {
+ e.printStackTrace();
+ }
+
+ }
+
+ /**
+ * Reads files\method_permission_list.csv and creates a map with
+ * key - classPackagePath.methodName
+ * value - permission name (without android.permission) (it can use || to indicate conditions that are optional, that is interchangeable)
+ * Case there is more than one line for a permission it indicates that it is a && condition
+ *
+ * E.g.: If the file have the following declarations
+ * android.accounts.AccountManager.method1,PERMISSION1
+ * android.accounts.AccountManager.method1,PERMISSION2||PERMISSION3
+ *
+ * It will return the following:
+ * PERMISSION1 = required
+ * PERMISSION2, PERMISSION3 = optional (one of them is required)
+ *
+ * @param csv
+ * @return
+ * @throws IOException
+ */
+ public static Map<String, PermissionGroups> readMapMethodToPermission(
+ InputStreamReader csvStream) throws IOException
+ {
+ Map<String, PermissionGroups> methodToPermission = new HashMap<String, PermissionGroups>();
+ BufferedReader reader = null;
+ try
+ {
+ reader = new BufferedReader(csvStream);
+ String line = reader.readLine();
+ while (line != null)
+ {
+ String[] splitAux = line.split(",");
+ if (splitAux.length >= 2)
+ {
+ String classAndMethod = splitAux[0];
+ String permissionGroupAux = splitAux[1];
+ PermissionGroups permissionGroups = methodToPermission.get(classAndMethod);
+ if (permissionGroups == null)
+ {
+ //method not included yet - add new entry on map
+ permissionGroups = new PermissionGroups();
+ methodToPermission.put(classAndMethod, permissionGroups);
+ }
+ if ((permissionGroupAux != null) && permissionGroupAux.contains("||"))
+ {
+ //optional permissions - permissions should not contain spaces
+ String[] optionalPermissions = permissionGroupAux.split("\\|\\|");
+
+ for (int i = 0; i < optionalPermissions.length; i++)
+ {
+ String optionalPermission = optionalPermissions[i];
+ optionalPermissions[i] = ANDROID_PERMISSION_PREFIX + optionalPermission;
+ }
+
+ permissionGroups.getOptionalPermissions().addAll(
+ Arrays.asList(optionalPermissions));
+ }
+ else
+ {
+ //required permission
+ permissionGroups.getRequiredPermissions().add(
+ ANDROID_PERMISSION_PREFIX + permissionGroupAux);
+ }
+
+ }
+ line = reader.readLine();
+ }
+ }
+ finally
+ {
+ if (reader != null)
+ {
+ try
+ {
+ reader.close();
+ }
+ catch (Exception e)
+ {
+ //Do Nothing.
+ }
+ }
+ }
+
+ return methodToPermission;
+ }
+
+ /**
+ * Method that prints the mapping into a file.
+ * Used to check if the mapping is being properly constructed.
+ * @param methodToPermission
+ */
+ @SuppressWarnings("unused")
+ private static void saveToFile(Map<String, PermissionGroups> methodToPermission)
+ {
+ File f = new File("C:\\methodToPermission.csv");
+ OutputStreamWriter writer = null;
+
+ try
+ {
+ writer = new OutputStreamWriter(new FileOutputStream(f));
+
+ Set<String> classes = methodToPermission.keySet();
+
+ for (String cls : classes)
+ {
+ PermissionGroups permissions = methodToPermission.get(cls);
+
+ for (String permission : permissions.getRequiredPermissions())
+ {
+ writer.append(cls + "," + permission.substring(permission.lastIndexOf('.') + 1)
+ + "\n");
+ }
+
+ if (!permissions.getOptionalPermissions().isEmpty())
+ {
+ writer.append(cls + ",");
+ boolean first = true;
+ for (String permission : permissions.getOptionalPermissions())
+ {
+ writer.append(first ? permission.substring(permission.lastIndexOf('.') + 1)
+ : "||" + permission.substring(permission.lastIndexOf('.') + 1));
+ first = false;
+ }
+ writer.append('\n');
+ }
+ }
+ writer.flush();
+
+ }
+ catch (FileNotFoundException e)
+ {
+ e.printStackTrace();
+ }
+ catch (IOException e)
+ {
+ e.printStackTrace();
+ }
+ finally
+ {
+ if (writer != null)
+ {
+ try
+ {
+ writer.close();
+ }
+ catch (IOException e)
+ {
+ //Do Nothing.
+ }
+ }
+ }
+ }
+}
diff --git a/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/internal/utils/ProjectUtils.java b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/internal/utils/ProjectUtils.java
new file mode 100644
index 0000000..13adb84
--- /dev/null
+++ b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/internal/utils/ProjectUtils.java
@@ -0,0 +1,1680 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.preflighting.core.internal.utils;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileReader;
+import java.io.FilenameFilter;
+import java.io.IOException;
+import java.lang.reflect.Modifier;
+import java.net.FileNameMap;
+import java.net.URLConnection;
+import java.nio.CharBuffer;
+import java.security.cert.Certificate;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.StringTokenizer;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.jdt.core.JavaCore;
+import org.eclipse.jdt.core.dom.AST;
+import org.eclipse.jdt.core.dom.ASTNode;
+import org.eclipse.jdt.core.dom.ASTParser;
+import org.eclipse.jdt.core.dom.ASTVisitor;
+import org.eclipse.jdt.core.dom.Assignment;
+import org.eclipse.jdt.core.dom.Block;
+import org.eclipse.jdt.core.dom.CompilationUnit;
+import org.eclipse.jdt.core.dom.Expression;
+import org.eclipse.jdt.core.dom.FieldDeclaration;
+import org.eclipse.jdt.core.dom.IMethodBinding;
+import org.eclipse.jdt.core.dom.ITypeBinding;
+import org.eclipse.jdt.core.dom.IVariableBinding;
+import org.eclipse.jdt.core.dom.MethodDeclaration;
+import org.eclipse.jdt.core.dom.MethodInvocation;
+import org.eclipse.jdt.core.dom.Modifier.ModifierKeyword;
+import org.eclipse.jdt.core.dom.NumberLiteral;
+import org.eclipse.jdt.core.dom.QualifiedName;
+import org.eclipse.jdt.core.dom.SimpleName;
+import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
+import org.eclipse.jdt.core.dom.TypeDeclaration;
+import org.eclipse.jdt.core.dom.VariableDeclarationExpression;
+import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
+import org.eclipse.osgi.util.NLS;
+import org.w3c.dom.Document;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.w3c.dom.UserDataHandler;
+import org.xml.sax.Attributes;
+import org.xml.sax.ErrorHandler;
+import org.xml.sax.Locator;
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXParseException;
+import org.xml.sax.helpers.DefaultHandler;
+
+import com.motorolamobility.preflighting.core.applicationdata.ApplicationData;
+import com.motorolamobility.preflighting.core.applicationdata.Element;
+import com.motorolamobility.preflighting.core.applicationdata.Element.Type;
+import com.motorolamobility.preflighting.core.applicationdata.FolderElement;
+import com.motorolamobility.preflighting.core.applicationdata.ResourcesFolderElement;
+import com.motorolamobility.preflighting.core.applicationdata.SourceFolderElement;
+import com.motorolamobility.preflighting.core.applicationdata.StringsElement;
+import com.motorolamobility.preflighting.core.applicationdata.XMLElement;
+import com.motorolamobility.preflighting.core.exception.PreflightingToolException;
+import com.motorolamobility.preflighting.core.i18n.PreflightingCoreNLS;
+import com.motorolamobility.preflighting.core.logging.PreflightingLogger;
+import com.motorolamobility.preflighting.core.sdk.SdkUtils;
+import com.motorolamobility.preflighting.core.source.model.Constant;
+import com.motorolamobility.preflighting.core.source.model.Field;
+import com.motorolamobility.preflighting.core.source.model.Invoke;
+import com.motorolamobility.preflighting.core.source.model.Method;
+import com.motorolamobility.preflighting.core.source.model.SourceFileElement;
+import com.motorolamobility.preflighting.core.source.model.Variable;
+import com.motorolamobility.preflighting.core.validation.Parameter;
+import com.motorolamobility.preflighting.core.validation.ValidationManager;
+import com.motorolamobility.preflighting.core.verbose.DebugVerboseOutputter;
+import com.motorolamobility.preflighting.core.verbose.DebugVerboseOutputter.VerboseLevel;
+
+public final class ProjectUtils
+{
+ //Constants
+ private static final String ANDROID_VERSION_API_LEVEL = "AndroidVersion.ApiLevel";
+
+ private static final String SOURCE_PROPERTIES = "source.properties";
+
+ private static final String ANDROID_JAR = "android.jar";
+
+ private static final String PLATFORMS = "platforms";
+
+ private static final String ANDROID = "android-";
+
+ private static final String TARGET = "target";
+
+ private static final String DEFAULT_PROPERTIES = "default.properties";
+
+ private static final String PROJECT_PROPERTIES = "project.properties";
+
+ private static final String R_LAYOUT = "R.layout"; //$NON-NLS-1$
+
+ private static final String R_STRING = "R.string"; //$NON-NLS-1$
+
+ // Constants for validate the android project structure
+ private static final String ANDROID_APK_NAMESPACE_URI =
+ "http://schemas.android.com/apk/res/android"; //$NON-NLS-1$
+
+ private static final String ANDROID_SCHEME_ATT = "xmlns:android"; //$NON-NLS-1$
+
+ private static final String TAG_RESOURCES = "resources"; //$NON-NLS-1$
+
+ private static final String TAG_MANIFEST = "manifest"; //$NON-NLS-1$
+
+ private static final String ANDROID_MANIFEST_NAME = "AndroidManifest.xml"; //$NON-NLS-1$
+
+ private static final String XML_FILE_EXTENSION = "xml"; //$NON-NLS-1$
+
+ private static final String FOLDER_DRAWABLE = "drawable"; //$NON-NLS-1$
+
+ private static final String FOLDER_VALUES = "values"; //$NON-NLS-1$
+
+ private static final String FOLDER_LAYOUT = "layout"; //$NON-NLS-1$
+
+ private static final String FOLDER_DIST = "dist"; //$NON-NLS-1$
+
+ private static final String FOLDER_SRC = "src"; //$NON-NLS-1$
+
+ private static final String FOLDER_SMALI = "smali"; //$NON-NLS-1$
+
+ private static final String FOLDER_GEN = "gen"; //$NON-NLS-1$
+
+ private static final String FOLDER_RES = "res"; //$NON-NLS-1$
+
+ private static final String FOLDER_LIB = "lib"; //$NON-NLS-1$
+
+ public static final String LINE_NUMBER_KEY = "line_number"; //$NON-NLS-1$
+
+ public static final String JAVA_FILE_PROPERTY = "java_file";
+
+ public static final FileNameMap fileNameMap = URLConnection.getFileNameMap();
+
+ // mapping names of the android folders structure and theirs element types
+ private static HashMap<String, Type> foldersName = new HashMap<String, Type>();
+
+ /**
+ * Returns in appData the tree of elements of a given project or package
+ *
+ * @param project
+ * @param appData
+ * @throws IOException
+ */
+ public static void populateAplicationData(List<Parameter> globalParameters,
+ ApplicationData appData) throws PreflightingToolException
+ {
+
+ Parameter applicationPathPrm = null;
+
+ for (Parameter param : globalParameters)
+ {
+ if (ValidationManager.InputParameter.APPLICATION_PATH.getAlias().equals(
+ param.getParameterType()))
+ {
+ applicationPathPrm = param;
+ break;
+ }
+ }
+
+ String applicationPath = applicationPathPrm.getValue();
+ appData.setApplicationPath(applicationPath);
+
+ File file = null;
+ if (applicationPath != null)
+ {
+
+ // mapping android project folders structure
+ foldersName.put(FOLDER_SRC, Element.Type.FOLDER_SRC);
+ foldersName.put(FOLDER_SMALI, Element.Type.FOLDER_SRC);
+ foldersName.put(FOLDER_GEN, Element.Type.FOLDER_SRC);
+ foldersName.put(FOLDER_RES, Element.Type.FOLDER_RES);
+ foldersName.put(FOLDER_LIB, Element.Type.FOLDER_LIB);
+ foldersName.put(FOLDER_DRAWABLE, Element.Type.FOLDER_DRAWABLE);
+ foldersName.put(FOLDER_VALUES, Element.Type.FOLDER_VALUES);
+ foldersName.put(FOLDER_LAYOUT, Element.Type.FOLDER_LAYOUT);
+
+ file = new File(applicationPath);
+ if ((file != null) && file.canRead())
+ {
+ if (file.isDirectory())
+ {
+ Element element = new Element(file.getName(), null, Element.Type.ROOT);
+ boolean isFolderAProject = validateProjectFolder(file);
+
+ if (!isFolderAProject)
+ {
+ throw new PreflightingToolException(
+ PreflightingCoreNLS.ProjectUtils_InvalidPathErrorMessage);
+ }
+
+ boolean isProject = true;
+ appData.setRootElement(element);
+ appData.setRootElementPath(file.getAbsolutePath());
+ appData.setIsProject(isProject);
+ appData.setName(file.getName());
+
+ populateApplicationDataRecursively(file, appData.getRootElement(), isProject,
+ globalParameters);
+
+ }
+ else
+ // it is a file, could be an android package
+ {
+ Parameter sdkPathPrm = null;
+
+ for (Parameter param : globalParameters)
+ {
+ if (ValidationManager.InputParameter.SDK_PATH.getAlias().equals(
+ param.getParameterType()))
+ {
+ sdkPathPrm = param;
+ break;
+ }
+ }
+
+ String sdkPath = sdkPathPrm.getValue();
+ String tempSdkPath = SdkUtils.getLatestAAPTToolPath(sdkPath);
+
+ if (tempSdkPath != null)
+ {
+ sdkPath = tempSdkPath;
+ }
+ // the apk should be converted to an android project
+ // structure and
+ // this project will be converted to a tree
+ // apktool or aapt may be used
+
+ File projectFile = ApkUtils.extractProjectFromAPK(file, sdkPath);
+
+ if ((projectFile != null) && projectFile.canRead())
+ {
+ Element element =
+ new Element(projectFile.getName(), null, Element.Type.ROOT);
+ appData.setRootElement(element);
+ appData.setRootElementPath(projectFile.getAbsolutePath());
+ IPath path = new Path(file.getName());
+ appData.setName(path.removeFileExtension().toString());
+
+ // extract certificate info from APK
+ try
+ {
+ List<Certificate> certificateChain = ApkUtils.populateCertificate(file);
+ appData.setCertificateChain(certificateChain);
+ }
+ catch (Exception e)
+ {
+ PreflightingLogger.error(ProjectUtils.class,
+ PreflightingCoreNLS.ProjectUtils_ErrorReadingCertificate, e); //$NON-NLS-1$
+ throw new PreflightingToolException(
+ PreflightingCoreNLS.ProjectUtils_ErrorReadingCertificate);
+ }
+
+ populateApplicationDataRecursively(projectFile, appData.getRootElement(),
+ false, globalParameters);
+ }
+ }
+ }
+ else
+ {
+ throw new PreflightingToolException(
+ PreflightingCoreNLS.ProjectUtils_InvalidPathErrorMessage);
+ }
+ }
+ }
+
+ /**
+ * Verifies if the given path contains an Android project. It just checks if
+ * there's an AndroidManifest.xml on the root dir.
+ *
+ * @param dir
+ * the path
+ * @return true if the folder contains a project, false otherwise.
+ * @throws PreflightingToolException
+ */
+ public static boolean validateProjectFolder(File dir)
+ {
+ File[] androidManifest = dir.listFiles(new FilenameFilter()
+ {
+ public boolean accept(File directory, String fileName)
+ {
+
+ return fileName.equalsIgnoreCase(ANDROID_MANIFEST_NAME);
+ }
+ });
+
+ return androidManifest.length > 0;
+ }
+
+ /**
+ * Build the tree of elements by categorizing each element, creating the
+ * proper element object and adding the element on the proper node of the
+ * tree.
+ *
+ * @param project
+ * @param appElement
+ */
+ private static void populateApplicationDataRecursively(File project, Element appElement,
+ boolean isProject, List<Parameter> globalParameters)
+ {
+ Element element = null;
+ File[] files = project.listFiles();
+ // the XML Document fulfilled by checkElementType method when the
+ // element is a XML file
+ Document[] xmlDoc = new Document[1];
+
+ for (int i = 0; i < files.length; i++)
+ {
+ File file = files[i];
+ Type elementType = checkElementType(file, appElement, xmlDoc);
+ if (file.isFile())
+ {
+ if (xmlDoc[0] != null)
+ {
+ if (elementType == Type.FILE_STRINGS)
+ {
+ element = new StringsElement(file.getName(), appElement);
+ }
+ else if (elementType == Type.FILE_LAYOUT)
+ {
+ element = new XMLElement(file.getName(), appElement, elementType);
+ }
+ else
+ {
+ element = new XMLElement(file.getName(), appElement, elementType);
+ }
+
+ // fill each node of xmlDoc with line number information
+ if (file.getName().equals(ANDROID_MANIFEST_NAME))
+ {
+ populateLineNumber(file, xmlDoc);
+ }
+
+ ((XMLElement) element).setDocument(xmlDoc[0]);
+ }
+ else
+ {
+ element = new Element(file.getName(), appElement, elementType);
+ }
+ element.setFile(file);
+ appElement.addChild(element);
+ }
+ else
+ {
+ // dist folder is not scanned
+ if (file.getName().equals(FOLDER_DIST))
+ {
+ continue;
+ }
+ else if (elementType.equals(Element.Type.FOLDER_RES))
+ {
+ if (!file.getAbsolutePath().endsWith(
+ File.separator + "bin" + File.separator + "res"))
+ {
+ // WARNING: the if above is necessary since ADT R14
+ // do not consider /bin/res (otherwise it will break
+ // several conditions)
+ element = new ResourcesFolderElement(file, appElement);
+ }
+ }
+ else if (elementType.equals(Element.Type.FOLDER_SRC))
+ {
+ try
+ {
+ if (!isProject)
+ {
+ element = ApktoolUtils.extractJavaModel(file, appElement, file);
+ }
+ else
+ {
+ element =
+ ProjectUtils.createCompilationUnits(appElement, file, project,
+ globalParameters);
+ }
+ }
+ catch (Exception e)
+ {
+ element = new FolderElement(file, appElement, elementType);
+ PreflightingLogger.error(ProjectUtils.class,
+ PreflightingCoreNLS.ProjectUtils_ErrorReadingJavaModel, e); //$NON-NLS-1$
+ }
+ }
+ else
+ {
+ element = new FolderElement(file, appElement, elementType);
+ }
+ if (element != null)
+ {
+ appElement.addChild(element);
+ populateApplicationDataRecursively(file, element, isProject, globalParameters);
+ }
+ }
+ }
+ }
+
+ /*
+ * Sets each DOM with line number information
+ *
+ * @param xmlFile
+ */
+ private static void populateLineNumber(File xmlFile, Document[] xmlDoc)
+ {
+ ArrayList<Node> nodesSequencialList = new ArrayList<Node>();
+ xmlDoc[0].getDocumentElement().normalize();
+ populateSequencialNodesList(xmlDoc[0], nodesSequencialList, xmlDoc);
+
+ SAXParserFactory saxfac = SAXParserFactory.newInstance();
+ SAXParser saxParser;
+ try
+ {
+ saxParser = saxfac.newSAXParser();
+ saxParser.parse(xmlFile, new XmlLineHandler(nodesSequencialList));
+ }
+ catch (Exception e)
+ {
+ PreflightingLogger.warn(ProjectUtils.class,
+ "Line number information will not be added to " + xmlFile.getName(), e); //$NON-NLS-1$
+ // do nothing - line number information will not be added
+ }
+ nodesSequencialList.clear();
+ }
+
+ /**
+ * Find the proper Element.Type for the given fileElement.
+ *
+ * @param fileElement
+ * @param appElement
+ * @return elementType
+ */
+ private static Type checkElementType(final File fileElement, Element appElement,
+ Document[] xmlDoc)
+ {
+ Type elementType = null;
+ if (fileElement.isDirectory())
+ {
+ // try to find the current folder on the android project default
+ // structure
+ elementType = foldersName.get(fileElement.getName().toLowerCase());
+
+ if (elementType == null)
+ {
+ // this other folders are part of the android project default
+ // structure but can have minor variations
+ // such as: drawable-hdpi, drawable-ldpi, values-pt
+ if (fileElement.getName().toLowerCase().startsWith(FOLDER_DRAWABLE))
+ {
+ elementType = foldersName.get(FOLDER_DRAWABLE);
+ }
+ else if (fileElement.getName().toLowerCase().startsWith(FOLDER_VALUES))
+ {
+ elementType = foldersName.get(FOLDER_VALUES);
+ }
+ else if (fileElement.getName().toLowerCase().startsWith(FOLDER_LAYOUT))
+ {
+ elementType = foldersName.get(FOLDER_LAYOUT);
+ }
+ else
+ {
+ elementType = Element.Type.FOLDER_UNKNOWN;
+ }
+ }
+ }
+ else
+ // element is a file
+ {
+ // it probably is a XML file
+ if (fileElement.getName().endsWith(XML_FILE_EXTENSION))
+ {
+ // try to convert the possible xml file into a Document
+ DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
+ DocumentBuilder db = null;
+ try
+ {
+ db = dbf.newDocumentBuilder();
+ db.setErrorHandler(new ErrorHandler()
+ {
+ public void warning(SAXParseException saxException) throws SAXException
+ {
+ DebugVerboseOutputter.printVerboseMessage(NLS.bind(
+ PreflightingCoreNLS.ProjectUtils_Error_Parsing_Manifest_INFO,
+ fileElement.getName()), VerboseLevel.v1);
+
+ DebugVerboseOutputter.printVerboseMessage(NLS.bind(
+ PreflightingCoreNLS.ProjectUtils_Error_Parsing_Manifest_DEBUG,
+ new String[]
+ {
+ fileElement.getName(),
+ Integer.toString(saxException.getLineNumber()),
+ saxException.getLocalizedMessage()
+ }), VerboseLevel.v2);
+
+ PreflightingLogger.warn(
+ ProjectUtils.class,
+ "Could not parse " //$NON-NLS-1$
+ + fileElement.getName()
+ + ":" + saxException.getLineNumber() + " - " + saxException.getMessage()); //$NON-NLS-1$ //$NON-NLS-2$ $NON-NLS-2$
+ }
+
+ public void fatalError(SAXParseException saxException) throws SAXException
+ {
+ DebugVerboseOutputter.printVerboseMessage(NLS.bind(
+ PreflightingCoreNLS.ProjectUtils_Error_Parsing_Manifest_INFO,
+ fileElement.getName()), VerboseLevel.v1);
+
+ DebugVerboseOutputter.printVerboseMessage(NLS.bind(
+ PreflightingCoreNLS.ProjectUtils_Error_Parsing_Manifest_DEBUG,
+ new String[]
+ {
+ fileElement.getName(),
+ Integer.toString(saxException.getLineNumber()),
+ saxException.getLocalizedMessage()
+ }), VerboseLevel.v2);
+
+ PreflightingLogger.fatal(
+ ProjectUtils.class,
+ "Could not parse " //$NON-NLS-1$
+ + fileElement.getName()
+ + ":" + saxException.getLineNumber() + " - " + saxException.getMessage()); //$NON-NLS-1$ //$NON-NLS-2$ $NON-NLS-2$
+ }
+
+ public void error(SAXParseException saxException) throws SAXException
+ {
+ DebugVerboseOutputter.printVerboseMessage(NLS.bind(
+ PreflightingCoreNLS.ProjectUtils_Error_Parsing_Manifest_INFO,
+ fileElement.getName()), VerboseLevel.v1);
+
+ DebugVerboseOutputter.printVerboseMessage(NLS.bind(
+ PreflightingCoreNLS.ProjectUtils_Error_Parsing_Manifest_DEBUG,
+ new String[]
+ {
+ fileElement.getName(),
+ Integer.toString(saxException.getLineNumber()),
+ saxException.getLocalizedMessage()
+ }), VerboseLevel.v2);
+
+ PreflightingLogger.error(
+ ProjectUtils.class,
+ "Could not parse " //$NON-NLS-1$
+ + fileElement.getName()
+ + ":" + saxException.getLineNumber() + " - " + saxException.getMessage()); //$NON-NLS-1$ //$NON-NLS-2$ $NON-NLS-2$
+ }
+ });
+
+ xmlDoc[0] = db.parse(fileElement);
+ }
+ catch (Exception e)
+ {
+ PreflightingLogger.warn(ProjectUtils.class, "Could not read the " //$NON-NLS-1$
+ + fileElement.getName() + " file.", e); //$NON-NLS-1$
+ }
+ if (xmlDoc[0] != null) // it really is a valid XML file
+ {
+ if (appElement.getType().equals(Element.Type.ROOT)
+ && fileElement.getName().equals(ANDROID_MANIFEST_NAME))
+ {
+ // the AndroidManifest file should be under the project
+ // root
+ // Validating the attribute
+ // xmlns:android="http://schemas.android.com/apk/res/android
+ // to check if the file really is an AndroidManifest
+ if (xmlDoc[0].getDocumentElement().getAttribute(ANDROID_SCHEME_ATT)
+ .equals(ANDROID_APK_NAMESPACE_URI)
+ && xmlDoc[0].getDocumentElement().getNodeName()
+ .equals(TAG_MANIFEST))
+ {
+ elementType = Element.Type.FILE_MANIFEST;
+ }
+ }
+ else if (appElement.getType().equals(Element.Type.FOLDER_LAYOUT)) // the parent is the
+ // layout folder
+ {
+
+ // Checking if the xml file is an android layout file by
+ // validating the attribute
+ // xmlns:android="http://schemas.android.com/apk/res/android"
+
+ if (xmlDoc[0].getDocumentElement().getAttribute(ANDROID_SCHEME_ATT)
+ .equals(ANDROID_APK_NAMESPACE_URI))
+ {
+ elementType = Element.Type.FILE_LAYOUT;
+ }
+
+ }
+ else if (appElement.getType().equals(Element.Type.FOLDER_VALUES)) // the parent is the
+ // values folder
+ {
+ // Check the tag indicating if it really is a strings
+ // file
+ if (xmlDoc[0].getDocumentElement().getNodeName().equals(TAG_RESOURCES))
+ {
+ elementType = Element.Type.FILE_STRINGS;
+ }
+ }
+ else
+ {
+ elementType = Element.Type.FILE_XML;
+ }
+ }
+ }
+ else if (appElement.getType().equals(Element.Type.FOLDER_DRAWABLE)) // the parent is the
+ // drawable folder
+ {
+ // check if the file is a valid image
+ String mimeType = fileNameMap.getContentTypeFor(fileElement.getAbsolutePath());
+
+ if ((mimeType != null) && mimeType.startsWith("image/"))
+ {
+ elementType = Element.Type.FILE_DRAWABLE;
+ }
+ }
+ if (elementType == null)
+ {
+ elementType = Element.Type.FILE_UNKNOWN;
+ }
+ }
+ return elementType;
+ }
+
+ public static SourceFolderElement createCompilationUnits(Element parent, File srcDir,
+ File projectDir, List<Parameter> globalParameters) throws IOException,
+ PreflightingToolException
+ {
+ SourceFolderElement model = new SourceFolderElement(srcDir, parent, false);
+ IPath projectPath = Path.fromOSString(srcDir.getParent());
+ List<CompilationUnit> list = new ArrayList<CompilationUnit>();
+ List<File> classPathFiles = readLibPathsFromClasspathEntries(projectDir);
+ File androidTarget = getAndroidTargetPathForProject(projectDir, globalParameters);
+ classPathFiles.add(androidTarget);
+ visitFolderToIdentifyClasses(srcDir, list, projectPath, classPathFiles);
+
+ for (CompilationUnit compilationUnit : list)
+ {
+ SourceFileElement sourceFileElement = ProjectUtils.readFromJava(compilationUnit, model);
+ sourceFileElement.setCompilationUnit(compilationUnit);
+ model.addChild(sourceFileElement);
+ model.getSourceFileElements().add(sourceFileElement);
+ }
+
+ return model;
+ }
+
+ /*
+ * Read all .java files inside src folder and create its ASTs objects.
+ *
+ * @param sourceFile
+ *
+ * @param list List of ASTs which is updated at each recursive call
+ *
+ * @param projectPath
+ *
+ * @throws PreflightingToolException
+ */
+ private static void visitFolderToIdentifyClasses(File sourceFile, List<CompilationUnit> list,
+ IPath projectPath, List<File> classPathFiles) throws PreflightingToolException
+ {
+
+ try
+ {
+ if (sourceFile.isFile())
+ {
+ // is a java file
+ if (sourceFile.getName().endsWith(".java"))
+ {
+ FileReader reader = null;
+ CharBuffer cb = null;
+
+ try
+ {
+ reader = new FileReader(sourceFile);
+ cb = CharBuffer.allocate((int) sourceFile.length());
+
+ int count = reader.read(cb);
+ cb.flip();
+
+ // verify if all bytes were read
+ if (count == sourceFile.length())
+ {
+ ASTParser parser = ASTParser.newParser(AST.JLS3);
+ parser.setSource(cb.array());
+
+ Map options = JavaCore.getOptions();
+ JavaCore.setComplianceOptions(JavaCore.VERSION_1_5, options);
+ parser.setCompilerOptions(options);
+
+ List<String> classPathList =
+ new ArrayList<String>(classPathFiles.size());
+ for (File file : classPathFiles)
+ {
+ classPathList.add(file.getAbsolutePath());
+ }
+ String[] classpathEntries =
+ classPathList.toArray(new String[classPathFiles.size()]);
+ File srcFolder = new File(projectPath.toFile(), "src");
+ File genFolder = new File(projectPath.toFile(), "gen");
+ int sourcepathEntriesSize = 0;
+ if (srcFolder.exists())
+ {
+ sourcepathEntriesSize++;
+ }
+ if (genFolder.exists())
+ {
+ sourcepathEntriesSize++;
+ }
+
+ String[] sourcepathEntries = new String[sourcepathEntriesSize];
+
+ if (sourcepathEntriesSize == 1)
+ {
+ sourcepathEntries[0] = srcFolder.getAbsolutePath();
+ }
+ if (sourcepathEntriesSize == 2)
+ {
+ sourcepathEntries[0] = srcFolder.getAbsolutePath();
+ sourcepathEntries[1] = genFolder.getAbsolutePath();
+ }
+
+ parser.setEnvironment(classpathEntries, sourcepathEntries, null, true);
+ parser.setUnitName(computeRelativePath(projectPath,
+ sourceFile.getAbsolutePath()));
+ parser.setResolveBindings(true);
+
+ ASTNode nodes = parser.createAST(null);
+
+ if (nodes.getNodeType() == ASTNode.COMPILATION_UNIT)
+ {
+ CompilationUnit cu = (CompilationUnit) nodes;
+ cu.setProperty(JAVA_FILE_PROPERTY, sourceFile);
+ list.add(cu);
+ }
+ }
+ else
+ {
+ DebugVerboseOutputter.printVerboseMessage(
+ PreflightingCoreNLS.ProjectUtils_ErrorReadingSourceFile
+ + sourceFile.getName(), VerboseLevel.v1);
+ }
+ }
+ // syntax error
+ catch (Exception syntaxException)
+ {
+ DebugVerboseOutputter.printVerboseMessage(
+ PreflightingCoreNLS.ProjectUtils_ErrorReadingSourceFile
+ + sourceFile.getName(), VerboseLevel.v1);
+ }
+ finally
+ {
+ if (cb != null)
+ {
+ cb.clear();
+ }
+ if (reader != null)
+ {
+ reader.close();
+ }
+ }
+ }
+ }
+ else if (sourceFile.isDirectory())
+ {
+ File[] subDirs = sourceFile.listFiles();
+
+ for (int i = 0; i < subDirs.length; i++)
+ {
+ visitFolderToIdentifyClasses(subDirs[i], list, projectPath, classPathFiles);
+ }
+ }
+ }
+ catch (Exception e)
+ {
+ throw new PreflightingToolException(
+ PreflightingCoreNLS.ProjectUtils_ErrorReadingSourceFile, e);
+ }
+ }
+
+ /**
+ * Returns a relative path e.g. /Project/src/package/name.java
+ *
+ * @param projectPath
+ * @param sourcePath
+ * @return
+ */
+ private static String computeRelativePath(IPath projectPath, String sourcePath)
+ {
+ IPath relativePath = Path.fromOSString(sourcePath).makeRelativeTo(projectPath);
+ return File.separator + projectPath.lastSegment() + File.separator
+ + relativePath.toOSString();
+ }
+
+ /**
+ * Reads lib paths from project .classpath file
+ *
+ * @param projectDir
+ * @return list of files for libraries used inside Android project
+ * @throws PreflightingToolException
+ * problem to read .classpath file
+ */
+ private static List<File> readLibPathsFromClasspathEntries(File projectDir)
+ throws PreflightingToolException
+ {
+ List<File> libPaths = new ArrayList<File>();
+ DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
+ DocumentBuilder db = null;
+
+ try
+ {
+ db = dbf.newDocumentBuilder();
+ File classPathFile = new File(projectDir, ".classpath");
+ if (classPathFile.exists())
+ {
+ Document doc = db.parse(classPathFile);
+ doc.getDocumentElement().normalize();
+ NodeList nodeLst = doc.getElementsByTagName("classpathentry");
+
+ for (int s = 0; s < nodeLst.getLength(); s++)
+ {
+ Node node = nodeLst.item(s);
+ if (node.getNodeType() == Node.ELEMENT_NODE)
+ {
+ NamedNodeMap attrs = node.getAttributes();
+ Node kindNode = attrs.getNamedItem("kind");
+ Node pathNode = attrs.getNamedItem("path");
+ if ((kindNode != null) && (pathNode != null)
+ && (kindNode.getNodeValue() != null)
+ && (pathNode.getNodeValue() != null))
+ {
+ if (kindNode.getNodeValue().equals("lib"))
+ {
+ String pathValue = pathNode.getNodeValue();
+ File f = new File(pathValue);
+ if (f.exists())
+ {
+ libPaths.add(f);
+ }
+ else
+ {
+ PreflightingLogger.debug(ProjectUtils.class,
+ "Could not find lib path: " //$NON-NLS-1$
+ + f.getAbsolutePath());
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ catch (Exception e)
+ {
+ throw new PreflightingToolException(
+ PreflightingCoreNLS.ProjectUtils_ErrorReadingClasspathFile, e);
+ }
+ return libPaths;
+ }
+
+ /**
+ * Retrieves the path to android.jar file inside platform/$target$, where
+ * $target$ is defined inside default.properties file
+ *
+ * @param projectDir
+ * @param globalParameters
+ * parameters where SDK_PATH is included
+ * @return file with path to android.jar
+ * @throws PreflightingToolException
+ * problem to read default.properties
+ */
+ private static File getAndroidTargetPathForProject(File projectDir,
+ List<Parameter> globalParameters) throws PreflightingToolException
+ {
+ File androidJarTarget = null;
+ Properties properties = new Properties();
+ // changed from default.properties to project.properties after R14
+ File defaultPropertiesFile = new File(projectDir, PROJECT_PROPERTIES);
+ if (!defaultPropertiesFile.exists())
+ {
+ // WARNING: do not remove statement below assigning
+ // default.properties file to keep compatibility with projects
+ // created with ADTs before R14
+ defaultPropertiesFile = new File(projectDir, DEFAULT_PROPERTIES);
+ }
+ if (defaultPropertiesFile.exists())
+ {
+ try
+ {
+ FileInputStream fileInputStream = null;
+ try
+ {
+ fileInputStream = new FileInputStream(defaultPropertiesFile);
+ properties.load(fileInputStream);
+ }
+ finally
+ {
+ try
+ {
+ fileInputStream.close();
+ }
+ catch (Exception e)
+ {
+ //Do Nothing.
+ }
+ }
+ if (properties.containsKey(TARGET))
+ {
+ String targetValue = properties.getProperty(TARGET);
+ if (targetValue != null)
+ {
+ if (!targetValue.startsWith(ANDROID))
+ {
+ try
+ {
+ // add-on => <name>:<model>:<version>
+ int colonIndex = targetValue.lastIndexOf(":");
+ if (colonIndex >= 0)
+ {
+ targetValue = ANDROID + targetValue.substring(colonIndex + 1);
+ }
+ }
+ catch (Exception e)
+ {
+ PreflightingLogger.debug("Unable to set target value.");
+ }
+ }
+ }
+ Parameter sdkPathPrm = null;
+ for (Parameter param : globalParameters)
+ {
+ if (ValidationManager.InputParameter.SDK_PATH.getAlias().equals(
+ param.getParameterType()))
+ {
+ sdkPathPrm = param;
+ break;
+ }
+ }
+ String sdkPath = getSdkPath(sdkPathPrm);
+
+ if (sdkPath != null)
+ {
+ // found sdk path
+ androidJarTarget =
+ new File(sdkPath + File.separator + PLATFORMS + File.separator
+ + targetValue + File.separator + ANDROID_JAR);
+ if (!androidJarTarget.exists())
+ {
+ //if not found the exact version, then look for one version of android that is greater than target value (and retrieve android.jar)
+ File baseFolder = new File(sdkPath + File.separator + PLATFORMS);
+ File[] androidPlatforms = baseFolder.listFiles();
+ boolean foundJar = false;
+ if (androidPlatforms.length > 0)
+ {
+ for (File androidPlatform : androidPlatforms)
+ {
+ File sourcePropsFile =
+ new File(androidPlatform, SOURCE_PROPERTIES);
+ File jar = new File(androidPlatform, ANDROID_JAR);
+ if (sourcePropsFile.exists() && jar.exists())
+ {
+ Properties sourceProperties = new Properties();
+ try
+ {
+ fileInputStream = new FileInputStream(sourcePropsFile);
+ sourceProperties.load(fileInputStream);
+ }
+ finally
+ {
+ if (fileInputStream != null)
+ {
+ fileInputStream.close();
+ }
+ }
+ //platform api level
+ String apiLevel =
+ sourceProperties
+ .getProperty(ANDROID_VERSION_API_LEVEL);
+ int index = targetValue.indexOf("-");
+ if ((index >= 0) && (apiLevel != null))
+ {
+ //project target declared
+ String versionName = targetValue.substring(index + 1);
+ try
+ {
+ Integer version = Integer.valueOf(versionName);
+ Integer apiLevelVersion = Integer.valueOf(apiLevel);
+ if (apiLevelVersion >= version)
+ {
+ //found a compatible platform
+ foundJar = true;
+ androidJarTarget = jar;
+ break;
+ }
+ }
+ catch (NumberFormatException nfe)
+ {
+ //ignore this folder (add-on or preview)
+ }
+ }
+ }
+ }
+ if (!foundJar)
+ {
+ throw new PreflightingToolException(
+ androidJarTarget.getAbsolutePath()
+ + "not found, check your sdk or your application target platform.");
+ }
+ }
+ }
+ }
+ else
+ {
+ throw new PreflightingToolException("Sdk path not found.");
+ }
+ }
+ }
+ catch (IOException e)
+ {
+ throw new PreflightingToolException(
+ PreflightingCoreNLS.ProjectUtils_ErrorReadingDefaultPropertiesFile, e);
+ }
+ }
+ else
+ {
+ throw new PreflightingToolException("default.properties file not found.");
+ }
+ return androidJarTarget;
+ }
+
+ /**
+ * Returns the path for Android SDK
+ *
+ * @param sdkPathPrm
+ * parameter with the path or "aapt" (if it is taking the value
+ * from PATH environment variable)
+ * @return resolved path to Android SDK
+ */
+ public static String getSdkPath(Parameter sdkPathPrm)
+ {
+ String sdkPath = null;
+ if (sdkPathPrm.getValue().equals("aapt"))
+ {
+ // sdk was not defined - take from environment variable
+ String pathVariable = System.getenv("PATH");
+ String pathSeparator = System.getProperty("path.separator");
+ String subPath = null;
+ File checkedPath = null;
+ String[] folderList = null;
+
+ StringTokenizer token = new StringTokenizer(pathVariable, pathSeparator);
+ while (token.hasMoreTokens())
+ {
+ subPath = token.nextToken();
+ checkedPath = new File(subPath);
+ if (checkedPath.isDirectory())
+ {
+ folderList = checkedPath.list();
+ for (String s : folderList)
+ {
+ if (s.equals("emulator") || s.equals("emulator.exe") || s.equals("adb")
+ || s.equals("adb.exe"))
+ {
+ File root = checkedPath.getParentFile();
+ sdkPath = root.getAbsolutePath();
+ break;
+ }
+ }
+ }
+ }
+
+ }
+ else
+ {
+ // sdk was defined on execution
+ sdkPath = sdkPathPrm.getValue();
+ }
+ return sdkPath;
+ }
+
+ /*
+ * Returns a list of nodes at document order.
+ */
+ private static void populateSequencialNodesList(Node node, ArrayList<Node> nodesSequencialList,
+ Document[] xmlDoc)
+ {
+ if (node instanceof Document)
+ {
+ xmlDoc[0] = (Document) node;
+ }
+ else if (node instanceof org.w3c.dom.Element)
+ {
+ nodesSequencialList.add(node);
+ }
+
+ NodeList children = node.getChildNodes();
+ for (int i = 0; i < children.getLength(); i++)
+ {
+ populateSequencialNodesList(children.item(i), nodesSequencialList, xmlDoc);
+ }
+ }
+
+ /**
+ * This method deletes the directory, all files and all subdirectories under
+ * it. If a deletion fails, the method stops attempting to delete and
+ * returns false.
+ *
+ * @param directory
+ * The directory to be deleted
+ * @return Returns true if all deletions were successful. If the directory
+ * doesn't exist returns false.
+ * @throws IOException
+ * When the parameter isn't a directory
+ */
+ public static boolean deleteDirRecursively(File directory) throws IOException
+ {
+ String dirName = ""; //$NON-NLS-1$
+
+ boolean success = true;
+
+ if (directory.exists())
+ {
+ if (directory.isDirectory())
+ {
+ dirName = directory.getName();
+ File[] children = directory.listFiles();
+
+ for (File element : children)
+ {
+ if (element.isFile())
+ {
+ element.deleteOnExit();
+ success = success && element.delete();
+ }
+ else
+ {
+ success = success && deleteDirRecursively(element);
+ }
+ }
+
+ directory.deleteOnExit();
+ success = success && directory.delete();
+ }
+ else
+ {
+ String errorMessage = directory.getName() + " is not a diretory."; //$NON-NLS-1$
+ PreflightingLogger.error(errorMessage);
+ throw new IOException(errorMessage);
+ }
+ }
+ else
+ {
+ String errorMessage = "The directory does not exist."; //$NON-NLS-1$
+ PreflightingLogger.error(errorMessage);
+ success = false;
+ throw new IOException(errorMessage);
+ }
+
+ if (success && !dirName.equals("")) //$NON-NLS-1$
+ {
+ PreflightingLogger.info("The directory " + dirName + "was successfully deleted."); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+
+ return success;
+ }
+
+ /**
+ * Get the application version
+ *
+ * @param appData
+ * @return an integer representing the version
+ */
+ public static Integer getApplicationVersionCode(XMLElement manifest)
+ {
+ Integer versionCode = 0;
+ if (manifest != null)
+ {
+ Document doc = manifest.getDocument();
+ if (doc != null)
+ {
+ NodeList manifestList = doc.getElementsByTagName("manifest"); //$NON-NLS-1$
+ org.w3c.dom.Element manifestElement = (org.w3c.dom.Element) manifestList.item(0);
+ if (manifestElement != null)
+ {
+ String strVersion = manifestElement.getAttribute("android:versionCode"); //$NON-NLS-1$
+ try
+ {
+ versionCode = Integer.parseInt(strVersion);
+ }
+ catch (NumberFormatException nfe)
+ {
+ // do nothing
+ }
+ }
+ }
+ }
+
+ return versionCode;
+ }
+
+ /**
+ * Extract all needed information from a CompilationUnit and fill the
+ * internal source file model (SourceFileElement). Fill all invoked Methods
+ * and resource constants
+ *
+ * @param javaCompilationUnit
+ * JDT model of a .java source file.
+ * @param parent
+ * SourceFolderElement, the source folder model containing this
+ * compilation unit.
+ * @return
+ */
+ public static SourceFileElement readFromJava(final CompilationUnit javaCompilationUnit,
+ Element parent)
+ {
+ File file = (File) javaCompilationUnit.getProperty(JAVA_FILE_PROPERTY);
+ final SourceFileElement sourceFileElement = new SourceFileElement(file, parent);
+
+ /* Fill type information */
+ Object type = javaCompilationUnit.types().get(0); /*
+ * Grab the class
+ * declaration
+ */
+ if (type instanceof TypeDeclaration)
+ {
+ TypeDeclaration typeDeclaration = (TypeDeclaration) type;
+ ITypeBinding superClassType = typeDeclaration.resolveBinding().getSuperclass();
+ String superClass = superClassType != null ? superClassType.getQualifiedName() : null;
+ sourceFileElement.setSuperclassName(superClass);
+ String typeFullName = typeDeclaration.resolveBinding().getQualifiedName();
+ sourceFileElement.setClassFullPath(typeFullName);
+ }
+
+ javaCompilationUnit.accept(new ASTVisitor()
+ {
+
+ /*
+ * Visit method declaration, searching for instructions.
+ */
+ @Override
+ public boolean visit(MethodDeclaration node)
+ {
+ // Fill Method information
+ Method method = new Method();
+ SimpleName name = node.getName();
+ method.setMethodName(name.getFullyQualifiedName());
+ int modifiers = node.getModifiers();
+ boolean methodType = isMethodVirtual(modifiers);
+ method.setStatic(ProjectUtils.isStatic(modifiers));
+
+ /*
+ * Extract information regarding method parameters
+ */
+ List<SingleVariableDeclaration> parameters = node.parameters();
+ for (SingleVariableDeclaration param : parameters)
+ {
+ ITypeBinding typeBinding = param.getType().resolveBinding();
+ if (typeBinding != null)
+ {
+ String paramTypeName = typeBinding.getName();
+ method.getParameterTypes().add(paramTypeName);
+ }
+
+ }
+
+ method.setConstructor(node.isConstructor());
+ IMethodBinding binding = node.resolveBinding();
+ if (binding != null)
+ {
+ String returnTypeStr = binding.getReturnType().getName();
+ method.setReturnType(returnTypeStr);
+ }
+
+ Block body = node.getBody();
+ int lineNumber =
+ body != null ? javaCompilationUnit.getLineNumber(body.getStartPosition())
+ : javaCompilationUnit.getLineNumber(node.getStartPosition());
+ method.setLineNumber(lineNumber);
+
+ // Navigate through statements...
+ if (body != null)
+ {
+ analizeBody(javaCompilationUnit, method, body);
+ }
+
+ sourceFileElement.addMethod(getMethodTypeString(methodType), method);
+ return super.visit(node);
+ }
+
+ /*
+ * Visit field declaration, only for R.java file. Extracting
+ * declared constants.
+ */
+ @Override
+ public boolean visit(FieldDeclaration node)
+ {
+ if (sourceFileElement.getName().equals("R.java"))
+ {
+ String typeName = node.getType().resolveBinding().getName();
+ int modifiers = node.getModifiers();
+ boolean isStatic = isStatic(modifiers);
+ Field field = new Field();
+ field.setType(typeName);
+ field.setStatic(isStatic);
+ field.setVisibility(getVisibility(modifiers));
+ field.setFinal(isFinal(modifiers));
+ if (isStatic)
+ {
+ List<VariableDeclarationFragment> fragments = node.fragments(); // TODO Verify what to do when
+ // there's more than one
+ // fragment... enum?
+ for (VariableDeclarationFragment fragment : fragments)
+ {
+ IVariableBinding binding = fragment.resolveBinding();
+ String name = binding.getName();
+ String declaringClassName = binding.getDeclaringClass().getName();
+ field.setName(declaringClassName + "." + name);
+ Expression initializer = fragment.getInitializer();
+ if (initializer != null)
+ {
+ if (initializer instanceof NumberLiteral)
+ {
+ NumberLiteral numberInitializer = (NumberLiteral) initializer;
+ String value = numberInitializer.getToken();
+ field.setValue(value);
+ }
+ }
+ }
+ sourceFileElement.getStaticFields().add(field);
+ }
+ }
+ return super.visit(node);
+ }
+
+ @Override
+ public boolean visit(QualifiedName node)
+ {
+ //visit to recognize R.string or R.layout usage
+ if ((node.getQualifier() != null) && node.getQualifier().isQualifiedName())
+ {
+ if (node.getQualifier().toString().equals(R_STRING))
+ {
+ sourceFileElement.getUsedStringConstants().add(node.getName().toString());
+ }
+ else if (node.getQualifier().toString().equals(R_LAYOUT))
+ {
+ sourceFileElement.getUsedLayoutConstants().add(node.getName().toString());
+ }
+ }
+
+ return super.visit(node);
+ }
+ });
+
+ return sourceFileElement;
+ }
+
+ /*
+ * All methods are virtual on Java, except those that are either private or
+ * final.
+ */
+ private static boolean isMethodVirtual(int methodModifiers)
+ {
+ boolean isVirtual = (methodModifiers & (Modifier.PRIVATE | Modifier.FINAL)) == 0;
+ return isVirtual;
+ }
+
+ private static String getMethodTypeString(boolean isVirtual)
+ {
+ return isVirtual ? Method.VIRTUAL : Method.DIRECT;
+ }
+
+ /*
+ * Verify if modifiers flags contains the 'Static' bit on
+ */
+ private static boolean isStatic(int modifiers)
+ {
+ return (modifiers & Modifier.STATIC) != 0;
+ }
+
+ /*
+ * Verify if modifiers flags contains the 'Final' bit on
+ */
+ private static boolean isFinal(int modifiers)
+ {
+ return (modifiers & Modifier.FINAL) != 0;
+ }
+
+ /*
+ * Search on modifiers flags and retrieve the visibility keyword
+ * corresponding to the flag bit
+ */
+ private static String getVisibility(int modifiers)
+ {
+ boolean isPublic = (modifiers & Modifier.PUBLIC) != 0;
+ boolean isPrivate = (modifiers & Modifier.PRIVATE) != 0;
+ boolean isProtected = (modifiers & Modifier.PROTECTED) != 0;
+
+ if (isPublic)
+ {
+ return ModifierKeyword.PUBLIC_KEYWORD.toString();
+ }
+ if (isPrivate)
+ {
+ return ModifierKeyword.PRIVATE_KEYWORD.toString();
+ }
+ if (isProtected)
+ {
+ return ModifierKeyword.PROTECTED_KEYWORD.toString();
+ }
+
+ return "";
+
+ }
+
+ /*
+ * Navigate in a Block and extract called methods and all R constants used
+ * as Method parameters.
+ */
+ private static void analizeBody(final CompilationUnit javaCompilationUnit, final Method method,
+ Block body)
+ {
+ body.accept(new ASTVisitor()
+ {
+
+ @Override
+ public boolean visit(VariableDeclarationFragment node)
+ {
+ String varName = node.getName().getIdentifier();
+ ITypeBinding typeBinding = node.resolveBinding().getType();
+ String typeQualifiedName = typeBinding.getQualifiedName();
+ int modifiers = typeBinding.getModifiers();
+ boolean isFinal = isFinal(modifiers);
+ boolean isStatic = isStatic(modifiers);
+ String value = null;
+ Expression initializer = node.getInitializer();
+ if (initializer != null)
+ {
+ value = initializer.toString();
+ }
+ int lineNumber = javaCompilationUnit.getLineNumber(node.getStartPosition());
+
+ Variable variable = new Variable();
+ variable.setName(varName);
+ variable.setType(typeQualifiedName);
+ variable.setFinal(isFinal);
+ variable.setStatic(isStatic);
+ variable.setValue(value);
+ variable.setLineNumber(lineNumber);
+
+ method.addVariable(variable);
+
+ return super.visit(node);
+ }
+
+ @Override
+ public boolean visit(MethodInvocation node)
+ {
+ // Fill invoked method model.
+ MethodInvocation invoked = node;
+ IMethodBinding methodBinding = invoked.resolveMethodBinding();
+ if (methodBinding != null)
+ {
+ IMethodBinding methodDeclaration = methodBinding.getMethodDeclaration();
+ ITypeBinding declaringClass = methodDeclaration.getDeclaringClass();
+ String declaringClassName = "";
+ if (declaringClass != null)
+ {
+ declaringClassName = declaringClass.getQualifiedName();
+ }
+ String methodSimpleName = methodBinding.getName();
+ int lineNumber = javaCompilationUnit.getLineNumber(invoked.getStartPosition());
+ String returnType = methodBinding.getReturnType().getQualifiedName();
+ int methodModifiers = methodBinding.getModifiers();
+ boolean isVirtual = isMethodVirtual(methodModifiers);
+ String sourceFileFullPath =
+ ((File) javaCompilationUnit.getProperty(JAVA_FILE_PROPERTY))
+ .getAbsolutePath();
+
+ // Retrieve parameter types and look for R constants used
+ // within method arguments
+ List arguments = invoked.arguments();
+ List<String> parameterTypes = new ArrayList<String>(arguments.size());
+ List<String> parameterNames = new ArrayList<String>(arguments.size());
+ for (Object argument : arguments)
+ {
+ Expression argumentExpression = (Expression) argument;
+ ITypeBinding typeBinding = argumentExpression.resolveTypeBinding();
+ String parameterType = "";
+ String parameterName = "";
+ if (typeBinding != null)
+ {
+ parameterType = typeBinding.getName();
+ parameterName = argumentExpression.toString();
+ }
+ else
+ {
+ continue;
+ }
+
+ parameterTypes.add(parameterType);
+ parameterNames.add(parameterName);
+ if (argumentExpression instanceof QualifiedName) /*
+ * Can
+ * be a
+ * constant
+ * access
+ */
+ {
+ QualifiedName qualifiedName = (QualifiedName) argumentExpression;
+ String fullQualifiedName =
+ qualifiedName.getQualifier().getFullyQualifiedName();
+ if (fullQualifiedName.startsWith("R.")) /*
+ * Accessing
+ * a R
+ * constant
+ */
+ {
+ Constant constant = new Constant();
+ constant.setSourceFileFullPath(sourceFileFullPath);
+ constant.setLine(lineNumber);
+ constant.setType(parameterType);
+ Object constantExpressionValue =
+ qualifiedName.resolveConstantExpressionValue();
+ if (constantExpressionValue != null)
+ {
+ String constantValueHex = constantExpressionValue.toString();
+ if (constantExpressionValue instanceof Integer)
+ {
+ Integer integerValue = (Integer) constantExpressionValue;
+ constantValueHex = Integer.toHexString(integerValue);
+ }
+ constant.setValue(constantValueHex);
+ method.getInstructions().add(constant);
+ }
+ }
+
+ }
+ }
+
+ // Get the name of the object who owns the method being
+ // called.
+ Expression expression = invoked.getExpression();
+ String objectName = null;
+ if ((expression != null) && (expression instanceof SimpleName))
+ {
+ SimpleName simpleName = (SimpleName) expression;
+ objectName = simpleName.getIdentifier();
+ }
+
+ // Get the variable, if any, that received the method
+ // returned value
+ ASTNode parent = invoked.getParent();
+ String assignedVariable = null;
+ if (parent instanceof VariableDeclarationFragment)
+ {
+ VariableDeclarationFragment variableDeclarationFragment =
+ (VariableDeclarationFragment) parent;
+ assignedVariable = variableDeclarationFragment.getName().getIdentifier();
+ }
+ else if (parent instanceof Assignment)
+ {
+ Assignment assignment = (Assignment) parent;
+ Expression leftHandSide = assignment.getLeftHandSide();
+ if (leftHandSide instanceof SimpleName)
+ {
+ SimpleName name = (SimpleName) leftHandSide;
+ assignedVariable = name.getIdentifier();
+ }
+ }
+
+ // Fill Invoke object and add to the method model.
+ Invoke invoke = new Invoke();
+ invoke.setLine(lineNumber);
+ invoke.setMethodName(methodSimpleName);
+ invoke.setObjectName(objectName);
+ invoke.setType(getMethodTypeString(isVirtual));
+ invoke.setReturnType(returnType);
+ invoke.setClassCalled(declaringClassName);
+ invoke.setParameterTypes(parameterTypes);
+ invoke.setParameterNames(parameterNames);
+ invoke.setSourceFileFullPath(sourceFileFullPath);
+ invoke.setAssignedVariable(assignedVariable);
+
+ method.getInstructions().add(invoke);
+ }
+ return super.visit(node);
+ }
+
+ @Override
+ public boolean visit(VariableDeclarationExpression node)
+ {
+
+ return super.visit(node);
+ }
+
+ @Override
+ public boolean visit(Assignment node)
+ {
+ Expression lhs = node.getLeftHandSide();
+ String name = "";
+ if (lhs instanceof SimpleName)
+ {
+ SimpleName simpleName = (SimpleName) lhs;
+ name = simpleName.getIdentifier();
+ }
+ ITypeBinding typeBinding = lhs.resolveTypeBinding();
+ String type = typeBinding.getName();
+ // method.addAssigment(assignment);
+
+ // TODO Auto-generated method stub
+ return super.visit(node);
+ }
+ });
+ }
+}
+
+class XmlLineHandler extends DefaultHandler
+{
+ private int index = 0;
+
+ private ArrayList<Node> nodesSequencialList = null;
+
+ private Locator locator;
+
+ public XmlLineHandler(ArrayList<Node> nodesSequencialList)
+ {
+ this.nodesSequencialList = nodesSequencialList;
+ }
+
+ @Override
+ public void startElement(String uri, String localName, String qName, Attributes attributes)
+ throws SAXException
+ {
+ setCurrentNodeLineNumber(nodesSequencialList.get(index), locator.getLineNumber());
+ index++;
+ }
+
+ @Override
+ public void setDocumentLocator(Locator locator)
+ {
+ this.locator = locator;
+ }
+
+ /*
+ * adds line number info to DOM node.
+ */
+ private void setCurrentNodeLineNumber(Node node, Integer lineNumber)
+ {
+ node.setUserData(ProjectUtils.LINE_NUMBER_KEY, lineNumber, new EmptyDataHandler());
+ }
+}
+
+class EmptyDataHandler implements UserDataHandler
+{
+ public void handle(short operation, String key, Object data, Node src, Node dst)
+ {
+ // Do nothing
+ }
+}
diff --git a/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/internal/utils/StringUsageIdentifier.java b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/internal/utils/StringUsageIdentifier.java
new file mode 100644
index 0000000..d610b39
--- /dev/null
+++ b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/internal/utils/StringUsageIdentifier.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.preflighting.core.internal.utils;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import com.motorolamobility.preflighting.core.applicationdata.XMLElement;
+
+/**
+ * Utility class to help identifying strings that are used inside XML files
+ */
+public class StringUsageIdentifier
+{
+ private static final String STRING_USAGE_PATTERN = "@string/";
+
+ /**
+ * Identify strings used inside XML files
+ * @param xmlElements representation of the XML in the App validator
+ * @return set with strings used
+ */
+ public static Set<String> identifyStringsUsed(List<XMLElement> xmlElements)
+ {
+ Set<String> stringsUsed = new HashSet<String>();
+ if (xmlElements != null)
+ {
+ for (XMLElement xml : xmlElements)
+ {
+ Document document = xml.getDocument();
+ for (Node node = document.getFirstChild(); node != null; node =
+ node.getNextSibling())
+ {
+ visitNode(stringsUsed, node);
+ }
+ }
+ }
+ return stringsUsed;
+ }
+
+ /**
+ * Visit the node
+ * @param stringsUsed set to keep the strings used
+ * @param node xml element to visit
+ */
+ private static void visitNode(Set<String> stringsUsed, Node node)
+ {
+ checkStringsUsedInNode(stringsUsed, node);
+ if (node.hasChildNodes())
+ {
+ NodeList list = node.getChildNodes();
+ for (int i = 0; i < list.getLength(); i++)
+ {
+ Node subnode = list.item(i);
+ visitNode(stringsUsed, subnode);
+ }
+ }
+ }
+
+ /**
+ * Visits attributes from the node
+ * @param stringsUsed set to keep the strings used
+ * @param node xml element to visit
+ */
+ private static void checkStringsUsedInNode(Set<String> stringsUsed, Node node)
+ {
+ if (node.getNodeType() != Node.COMMENT_NODE)
+ {
+ NamedNodeMap map = node.getAttributes();
+ if (map != null)
+ {
+ for (int index = 0; index < map.getLength(); index++)
+ {
+ Node atr = map.item(index);
+ if ((atr != null) && (atr.getNodeValue() != null)
+ && !atr.getNodeValue().trim().equals("")) //$NON-NLS-1$
+ {
+ String value = atr.getNodeValue();
+ readStringId(stringsUsed, value);
+ }
+ }
+ }
+ }
+
+ }
+
+ /**
+ * Check if attribute contains {@link StringUsageIdentifier#STRING_USAGE_PATTERN} and if so, get the string id
+ * @param stringsUsed set to keep the strings used
+ * @param value attribute node value
+ */
+ private static void readStringId(Set<String> stringsUsed, String value)
+ {
+ int i = value.indexOf(STRING_USAGE_PATTERN);
+ if (i >= 0)
+ {
+ //cut text after "@string/
+ String stringId = value.substring(i + STRING_USAGE_PATTERN.length());
+ stringsUsed.add(stringId);
+ }
+ }
+}
diff --git a/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/logging/Level.java b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/logging/Level.java
new file mode 100644
index 0000000..94d2944
--- /dev/null
+++ b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/logging/Level.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.motorolamobility.preflighting.core.logging;
+
+/**
+ * This class defines standard logging levels.</p> The standard levels are <b> DEBUG
+ * < INFO < WARN < ERROR < FATAL </b>.
+ * <p>
+ * <b>Note:</b><br>
+ * A log operation of level x in a logger with level y, is enabled if and only
+ * if x >= y.
+ * <p>
+ * <p>
+ * <b>Example:</b><br>
+ * If the level is set to <b>ERROR</b> only messages with level of <b>ERROR</b>
+ * and <b>FATAL</b> will be logged.
+ */
+public final class Level
+{
+
+ // Constants ---------------------------------------
+ /**
+ * Disables all logging levels from being logged. After setting Level to
+ * OFF, no messages will be recorded in log file.
+ */
+ public static final int OFF = Integer.MAX_VALUE;
+
+ /**
+ * The FATAL level is used for severe error events. In case of FATAL, the
+ * application could be aborted.
+ */
+ public static final int FATAL = org.apache.log4j.Level.FATAL_INT;
+
+ /**
+ * The ERROR level is used by errors events. Less severe than FATAL, used
+ * for situations of error that will not crash the application.
+ */
+ public static final int ERROR = org.apache.log4j.Level.ERROR_INT;
+
+ /**
+ * The WARN level is used for potentially harmful situations. Used for
+ * situations that can generate an error.
+ */
+ public static final int WARN = org.apache.log4j.Level.WARN_INT;
+
+ /**
+ * The INFO level is used for informational messages. Informational messages
+ * are used to notify the progress of the application or relevant messages
+ * to be analyzed, like the tracing of the application execution.
+ */
+ public static final int INFO = org.apache.log4j.Level.INFO_INT;
+
+ /**
+ * The DEBUG level is used for relevant informations on an application, like
+ * variable values.
+ */
+ public static final int DEBUG = org.apache.log4j.Level.DEBUG_INT;
+
+ /**
+ * Enables all logging levels. After setting Level to ALL, all the messages
+ * will be recorded in log file.
+ */
+ public static final int ALL = Integer.MIN_VALUE;
+}
diff --git a/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/logging/PreflightingLogger.java b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/logging/PreflightingLogger.java
new file mode 100644
index 0000000..0054920
--- /dev/null
+++ b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/logging/PreflightingLogger.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.preflighting.core.logging;
+
+import com.motorolamobility.preflighting.core.internal.logging.Logger;
+
+/**
+ * Logger class for App Validator tool.
+ * All methods must be accessed statically.
+ */
+public abstract class PreflightingLogger
+{
+ /**
+ * Print log messages in {@link com.motorolamobility.preflighting.core.logging.Level#DEBUG} level.
+ *
+ * @param message Message to be logged.
+ */
+ public static void debug(String message)
+ {
+ Logger.getLogger(PreflightingLogger.class.getName()).debug(message);
+ }
+
+ /**
+ * Print log messages in {@link com.motorolamobility.preflighting.core.logging.Level#INFO} level.
+ *
+ * @param message Message to be logged.
+ */
+ public static void info(String message)
+ {
+ Logger.getLogger(PreflightingLogger.class.getName()).info(message);
+ }
+
+ /**
+ * Print log messages in {@link com.motorolamobility.preflighting.core.logging.Level#WARN} level.
+ *
+ * @param message Message to be logged.
+ */
+ public static void warn(String message)
+ {
+ Logger.getLogger(PreflightingLogger.class.getName()).warn(message);
+ }
+
+ /**
+ * Print log messages in {@link com.motorolamobility.preflighting.core.logging.Level#ERROR} level.
+ *
+ * @param message Message to be logged.
+ */
+ public static void error(String message)
+ {
+ Logger.getLogger(PreflightingLogger.class.getName()).error(message);
+ }
+
+ /**
+ * Print log messages in {@link com.motorolamobility.preflighting.core.logging.Level#FATAL} level.
+ *
+ * @param message Message to be logged.
+ */
+ public static void fatal(String message)
+ {
+ Logger.getLogger(PreflightingLogger.class.getName()).fatal(message);
+ }
+
+ /**
+ * Print log messages in {@link com.motorolamobility.preflighting.core.logging.Level#DEBUG} level.
+ *
+ * @param obj Object where the error occurred. Can be <code>null</code>.
+ * @param message Message to be logged.
+ */
+ public static void debug(Object obj, String message)
+ {
+ Logger.getLogger(PreflightingLogger.class.getName()).debug(message);
+
+ }
+
+ /**
+ * Print log messages in {@link com.motorolamobility.preflighting.core.logging.Level#INFO} level.
+ *
+ * @param message Message to be logged.
+ * @param aClass Class to identify in the log.
+ */
+ public static void info(Class<?> aClass, String message)
+ {
+ Logger.getLogger(aClass.toString()).info(message);
+ }
+
+ /**
+ * Print log messages in {@link com.motorolamobility.preflighting.core.logging.Level#WARN} level.
+ *
+ * @param message Message to be logged.
+ * @param aClass Class to identify in the log.
+ */
+ public static void warn(Class<?> aClass, String message)
+ {
+ Logger.getLogger(aClass.toString()).warn(message);
+ }
+
+ /**
+ * Print log messages in {@link com.motorolamobility.preflighting.core.logging.Level#WARN} level.
+ *
+ * @param aClass Class to identify in the log.
+ * @param message Message to be logged.
+ * @param Throwable that raised the warning.
+ */
+ public static void warn(Class<?> aClass, String message, Throwable throwable)
+ {
+ Logger.getLogger(aClass.toString()).warn(message, throwable);
+ }
+
+ /**
+ * Print log messages in {@link com.motorolamobility.preflighting.core.logging.Level#ERROR} level.
+ *
+ * @param aClass Class to identify in the log.
+ * @param message Message to be logged.
+ */
+ public static void error(Class<?> aClass, String message)
+ {
+ Logger.getLogger(aClass.toString()).error(message);
+ }
+
+ /**
+ * Print log messages in {@link com.motorolamobility.preflighting.core.logging.Level#ERROR} level.
+ *
+ * @param aClass Class to identify in the log.
+ * @param message Message to be logged.
+ * @param Throwable that raised the warning.
+ */
+ public static void error(Class<?> aClass, String message, Throwable error)
+ {
+ Logger.getLogger(aClass.toString()).error(message, error);
+ }
+
+ /**
+ * Print log messages in {@link com.motorolamobility.preflighting.core.logging.Level#FATAL} level.
+ *
+ * @param aClass Class to identify in the log.
+ * @param message Message to be logged.
+ */
+ public static void fatal(Class<?> aClass, String message)
+ {
+ Logger.getLogger(aClass.toString()).fatal(message);
+ }
+
+ /**
+ * Print log messages in {@link com.motorolamobility.preflighting.core.logging.Level#FATAL} level.
+ *
+ * @param aClass Class to identify in the log.
+ * @param message Message to be logged.
+ * @param Throwable that raised the warning.
+ */
+ public static void fatal(Class<?> aClass, String message, Throwable error)
+ {
+ Logger.getLogger(aClass.toString()).fatal(message, error);
+ }
+}
diff --git a/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/permissionfeature/Feature.java b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/permissionfeature/Feature.java
new file mode 100644
index 0000000..60e2046
--- /dev/null
+++ b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/permissionfeature/Feature.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.preflighting.core.permissionfeature;
+
+/**
+ * This class is a Bean object that represents the data inside of the <code>&lt;uses-feature&gt;</code> node from AndroidManifest.xml.
+ */
+public class Feature
+{
+ private String id;
+
+ private boolean required = true;
+
+ /**
+ * @param id the value inside <code>&lt;uses-feature android:name=""&gt;</code>.
+ */
+ public Feature(String id)
+ {
+ this.id = id;
+ }
+
+ /**
+ * @return The id corresponds to the value inside <code>&lt;uses-feature android:name=""&gt;</code>.
+ */
+ public String getId()
+ {
+ return id;
+ }
+
+ /**
+ * @param id set the value which corresponds to <code>&lt;uses-feature android:name=""&gt;</code>.
+ */
+ public void setId(String id)
+ {
+ this.id = id;
+ }
+
+ /**
+ * @return Returns true if this feature is set as required inside <code>&lt;uses-feature android:required=""&gt;</code>.
+ */
+ public boolean isRequired()
+ {
+ return required;
+ }
+
+ /**
+ * @param required set true if this feature is required, as declared in <code>&lt;uses-feature android:required=""&gt;</code>.
+ */
+ public void setRequired(boolean required)
+ {
+ this.required = required;
+ }
+
+ @Override
+ public String toString()
+ {
+ return id;
+ }
+
+ @Override
+ public int hashCode()
+ {
+ final int prime = 31;
+ int result = 1;
+ result = (prime * result) + ((id == null) ? 0 : id.hashCode());
+ return result;
+ }
+
+ @Override
+ /**
+ * Equals if id attribute is the same
+ */
+ public boolean equals(Object obj)
+ {
+ if (this == obj)
+ {
+ return true;
+ }
+ if (obj == null)
+ {
+ return false;
+ }
+ if (getClass() != obj.getClass())
+ {
+ return false;
+ }
+ Feature other = (Feature) obj;
+ if (id == null)
+ {
+ if (other.id != null)
+ {
+ return false;
+ }
+ }
+ else if (!id.equals(other.id))
+ {
+ return false;
+ }
+ return true;
+ }
+
+}
diff --git a/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/permissionfeature/Permission.java b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/permissionfeature/Permission.java
new file mode 100644
index 0000000..69ed187
--- /dev/null
+++ b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/permissionfeature/Permission.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.preflighting.core.permissionfeature;
+
+/**
+ * Bean object representing the data inside <code>&lt;uses-permission&gt;</code> node from AndroidManifest.xml
+ */
+public class Permission
+{
+ private String id;
+
+ /**
+ * Constructor which assigns this {@link Permission} identifier.
+ *
+ * @param id The value inside <code>&lt;uses-permission android:name=""&gt;</code>
+ */
+ public Permission(String id)
+ {
+ this.id = id;
+ }
+
+ /**
+ * Gets the id as in the node <code>&lt;uses-permission android:name=""&gt;</code>.
+ *
+ * @return Returns the id as in the node <code>&lt;uses-permission android:name=""&gt;</code>.
+ */
+ public String getId()
+ {
+ return id;
+ }
+
+ /**
+ *
+ * Set the id as in the node <code>&lt;uses-permission android:name=""&gt;</code>.
+ *
+ * @param id The id as in the node <code>&lt;uses-permission android:name=""&gt;</code>.
+ */
+ public void setId(String id)
+ {
+ this.id = id;
+ }
+
+ /**
+ * Prints out the Id.
+ *
+ * @return Returns the Id.
+ *
+ * @see Object#toString()
+ */
+ @Override
+ public String toString()
+ {
+ return id;
+ }
+
+ /**
+ * Creates a hash-code number based on this object´s Id.
+ *
+ * @return Returns the hash-code based on this object´s Id.
+ */
+ @Override
+ public int hashCode()
+ {
+ final int prime = 31;
+ int result = 1;
+ result = (prime * result) + ((id == null) ? 0 : id.hashCode());
+ return result;
+ }
+
+ /**
+ * Returns true whether the compared object is a {@link Permission}
+ * and the Ids are equal.
+ *
+ * @param obj Object to be compared. it must be an instance of {@link Permission}.
+ *
+ * @return Returns <code>true</code> if the {@link Permission} are equal,
+ * <code>false</code> otherwise.
+ *
+ * @see Object#equals(Object)
+ */
+ @Override
+ public boolean equals(Object obj)
+ {
+ if (this == obj)
+ {
+ return true;
+ }
+ if (obj == null)
+ {
+ return false;
+ }
+ if (getClass() != obj.getClass())
+ {
+ return false;
+ }
+ Permission other = (Permission) obj;
+ if (id == null)
+ {
+ if (other.id != null)
+ {
+ return false;
+ }
+ }
+ else if (!id.equals(other.id))
+ {
+ return false;
+ }
+ return true;
+ }
+}
diff --git a/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/sdk/SdkUtils.java b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/sdk/SdkUtils.java
new file mode 100644
index 0000000..5b46f6a
--- /dev/null
+++ b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/sdk/SdkUtils.java
@@ -0,0 +1,227 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.preflighting.core.sdk;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.Properties;
+
+import org.eclipse.core.runtime.Platform;
+
+/**
+ * This class has methods which aids the use of the Android SDK.
+ */
+public class SdkUtils
+{
+ private static final boolean isWin32 = Platform.getOS().equals(Platform.OS_WIN32);
+
+ /**
+ * Folder "platforms" in the Android SDK
+ */
+ public static final String FD_PLATFORMS = "platforms"; //$NON-NLS-1$
+
+ /**
+ * Folder "tools" in the Android SDK
+ */
+ public static final String FD_TOOLS = "tools"; //$NON-NLS-1$
+
+ private static String FD_PLATFORM_TOOLS = "platform-tools";;
+
+ /**
+ * AAPT executable command.
+ */
+ public static String AAPT_EXE = isWin32 ? "aapt.exe" : "aapt"; //$NON-NLS-1$ //$NON-NLS-2$
+
+ private static final String SOURCE_PROPERTY_FILE = "source.properties"; //$NON-NLS-1$
+
+ private static final String API_LEVEL_KEY = "ro.build.version.sdk"; //$NON-NLS-1$
+
+ private static final String BUILD_PROP_FILE = "build.prop"; //$NON-NLS-1$
+
+ /**
+ * Android version, API Level tag in AndroidManifext.xml file.
+ */
+ public final static String APILEVEL = "AndroidVersion.ApiLevel"; //$NON-NLS-1$
+
+ /**
+ * Get the AAPT path of the higher API Level available given the sdk path.
+ *
+ * @param sdkFolder SDK folder.
+ *
+ * @return Returns the AAPT. absolute path, null if path not found.
+ */
+ public static String getLatestAAPTToolPath(String sdkFolder)
+ {
+ Integer maxApiLevel = 0;
+ File newestAaptTool = null;
+
+ File platformFolder = new File(sdkFolder, FD_PLATFORMS);
+ File platformToolsFolder = new File(sdkFolder, FD_PLATFORM_TOOLS);
+
+ //Verify if it's a tools_r8 sdk (appt tool will be located under the platform-tools folder).
+ if (platformToolsFolder.exists() && platformToolsFolder.isDirectory())
+ {
+ File aaptToolFile = new File(platformToolsFolder, AAPT_EXE);
+ if (aaptToolFile.exists())
+ {
+ newestAaptTool = aaptToolFile;
+ }
+ }
+ else
+ // it's an older SDK, try to find the aapt tool inside the platform folders.
+ {
+ //platform folder
+ if (platformFolder.exists() && platformFolder.isDirectory())
+ {
+ File[] targets = platformFolder.listFiles();
+
+ if (targets != null)
+ {
+ for (File target : targets)
+ {
+ if (target.isDirectory())
+ {
+ //look forward source.properties file
+ File propertiesFile = new File(target, SOURCE_PROPERTY_FILE);
+
+ if (propertiesFile.exists())
+ {
+ //load properties
+ Properties properties = getPropertiesFromTarget(propertiesFile);
+
+ String apilevel = properties.getProperty(APILEVEL, "0"); //$NON-NLS-1$
+
+ try
+ {
+ //get api level and store aapt tool path if latest
+ Integer intApiLevel = Integer.parseInt(apilevel);
+ if (intApiLevel > maxApiLevel)
+ {
+ File aaptToolFile =
+ new File(target, FD_TOOLS
+ + System.getProperty("file.separator")
+ + AAPT_EXE);
+ if (aaptToolFile.exists())
+ {
+ newestAaptTool = aaptToolFile;
+ maxApiLevel = intApiLevel;
+ }
+ }
+ }
+ catch (NumberFormatException e)
+ {
+ // Do nothing
+ }
+ }
+ else
+ {
+ //if source.properties does not exist (jil and ophone cases) get build.prop file
+ propertiesFile = new File(target, BUILD_PROP_FILE);
+
+ if (propertiesFile.exists())
+ {
+ //load properties
+ Properties properties = getPropertiesFromTarget(propertiesFile);
+
+ if ((properties != null)
+ && properties.containsKey(API_LEVEL_KEY))
+ {
+ String apilevel = properties.getProperty(API_LEVEL_KEY);
+
+ try
+ {
+ //store latest aapt tool path
+ Integer intApiLevel = Integer.parseInt(apilevel);
+ if (intApiLevel > maxApiLevel)
+ {
+ File aaptToolFile =
+ new File(
+ target,
+ FD_TOOLS
+ + System.getProperty("file.separator")
+ + AAPT_EXE);
+ if (aaptToolFile.exists())
+ {
+ newestAaptTool = aaptToolFile;
+ maxApiLevel = intApiLevel;
+ }
+ }
+ }
+ catch (NumberFormatException e)
+ {
+ // Do nothing
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return newestAaptTool != null ? newestAaptTool.getAbsolutePath() : null;
+ }
+
+ /**
+ * Retrieves the properties from a SDK target.
+ *
+ * @param propertiesFile The properties file.
+ *
+ * @return Returns a {@link Properties} object containing the properties from a SDK or null
+ * if the file could not be read.
+ */
+ private static Properties getPropertiesFromTarget(File propertiesFile)
+ {
+ Properties properties = new Properties();
+
+ FileInputStream fis = null;
+
+ try
+ {
+ fis = new FileInputStream(propertiesFile);
+ properties.load(fis);
+ }
+ catch (FileNotFoundException e)
+ {
+ // Do nothing. If the file has problems, there is no way to detect
+ // the target version
+ }
+ catch (IOException e)
+ {
+ // Do nothing. If the file has problems, there is no way to detect
+ // the target version
+ }
+ finally
+ {
+ if (fis != null)
+ {
+ try
+ {
+ fis.close();
+ }
+ catch (IOException e)
+ {
+ // Do nothing
+ }
+ }
+ }
+
+ return properties;
+ }
+}
diff --git a/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/source/model/Assignment.java b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/source/model/Assignment.java
new file mode 100644
index 0000000..4974d18
--- /dev/null
+++ b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/source/model/Assignment.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.preflighting.core.source.model;
+
+/**
+ * Represents a variable assignment in the code being verified.
+ */
+public class Assignment extends Instruction
+{
+ Variable variable;
+
+ Instruction value;
+
+}
diff --git a/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/source/model/Constant.java b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/source/model/Constant.java
new file mode 100644
index 0000000..fe83a31
--- /dev/null
+++ b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/source/model/Constant.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.preflighting.core.source.model;
+
+/**
+ * Represents a constant being accessed in the application data model.
+ */
+public class Constant extends Instruction
+{
+
+ private String type; //string, class
+
+ private String value;
+
+ /**
+ * Retrieve the constant type.
+ *
+ * @return Constant type.
+ */
+ public String getType()
+ {
+ return type;
+ }
+
+ /**
+ * Set the type of the constant (e.g.: string, class).
+ *
+ * @param type Constant type.
+ */
+ public void setType(String type)
+ {
+ this.type = type;
+ }
+
+ /**
+ * Retrieve the constant value.
+ *
+ * @return The value of the constant.
+ */
+ public String getValue()
+ {
+ return value;
+ }
+
+ /**
+ * Set the value of the constant.
+ *
+ * @param value Constant value.
+ */
+ public void setValue(String value)
+ {
+ this.value = value;
+ }
+
+ @Override
+ public String toString()
+ {
+ return "Constant [type=" + type + ", value=" + value + "]";
+ }
+}
diff --git a/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/source/model/Field.java b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/source/model/Field.java
new file mode 100644
index 0000000..3b76628
--- /dev/null
+++ b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/source/model/Field.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.motorolamobility.preflighting.core.source.model;
+
+/**
+ * This class represents a field (static or instance) declared.
+ */
+public class Field extends Variable
+{
+ @Override
+ public String toString()
+ {
+ return "Field [isStatic=" + isStatic + ", isFinal=" + isFinal + ", visibility="
+ + visibility + ", type=" + type + ", attributeName=" + name + ", value=" + value
+ + "]";
+ }
+}
diff --git a/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/source/model/Instruction.java b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/source/model/Instruction.java
new file mode 100644
index 0000000..de9cae3
--- /dev/null
+++ b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/source/model/Instruction.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.motorolamobility.preflighting.core.source.model;
+
+/**
+ * Represents items inside of a method (invocations or constants).
+ */
+public abstract class Instruction
+{
+ private int line;
+
+ private String sourceFileFullPath;
+
+ /**
+ * @return Returns the line of the instruction if possible, 0 if not found.
+ */
+ public int getLine()
+ {
+ return line;
+ }
+
+ /**
+ * Sets the line of the instruction.
+ * @param line the line of the instruction.
+ */
+ public void setLine(int line)
+ {
+ this.line = line;
+ }
+
+ /**
+ * @return the full path of the source file.
+ */
+ public String getSourceFileFullPath()
+ {
+ return sourceFileFullPath;
+ }
+
+ /**
+ * @param sourceFileFullPath the full path of the source file to be set.
+ */
+ public void setSourceFileFullPath(String sourceFileFullPath)
+ {
+ this.sourceFileFullPath = sourceFileFullPath;
+ }
+
+ @Override
+ public String toString()
+ {
+ return "Instruction [line=" + line + "]";
+ }
+}
diff --git a/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/source/model/Invoke.java b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/source/model/Invoke.java
new file mode 100644
index 0000000..cbb8423
--- /dev/null
+++ b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/source/model/Invoke.java
@@ -0,0 +1,217 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.motorolamobility.preflighting.core.source.model;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Represents a method (direct or virtual) being invoked.
+ */
+public class Invoke extends Instruction
+{
+
+ private String type; //direct or virtual
+
+ private String classCalled;
+
+ private String methodName;
+
+ private String objectName;
+
+ private String assignedVariable;
+
+ private List<String> parameterTypes = new ArrayList<String>();
+
+ private List<String> parameterNames = new ArrayList<String>();
+
+ private String returnType;
+
+ /**
+ * Returns the type of the Invoke (direct or virtual).
+ * @return the type of the Invoke.
+ */
+ public String getType()
+ {
+ return type;
+ }
+
+ /**
+ * Sets the type of the Invoke (direct or virtual).
+ * @param type the Invoke type.
+ */
+ public void setType(String type)
+ {
+ this.type = type;
+ }
+
+ /**
+ * Returns the name of the class that owns the method being invoked.
+ * @return name of the class that owns the method being invoked.
+ */
+ public String getClassCalled()
+ {
+ return classCalled;
+ }
+
+ /**
+ * Sets the class that owns the method being invoked.
+ * @param classCalled the class that owns the method being invoked.
+ */
+ public void setClassCalled(String classCalled)
+ {
+ this.classCalled = classCalled;
+ }
+
+ /**
+ * Returns the name of the method being invoked.
+ * @return name of the method being invoked.
+ */
+ public String getMethodName()
+ {
+ return methodName;
+ }
+
+ /**
+ * Sets the name of the method being invoked.
+ * @param methodName the name of the method being invoked.
+ */
+ public void setMethodName(String methodName)
+ {
+ this.methodName = methodName;
+ }
+
+ /**
+ * Returns the name of the object reference name being invoked.
+ * @return name of the object reference name being invoked.
+ */
+ public String getObjectName()
+ {
+ return objectName;
+ }
+
+ /**
+ * Sets object reference name being invoked.
+ * @param objectName the object name.
+ */
+ public void setObjectName(String objectName)
+ {
+ this.objectName = objectName;
+ }
+
+ /**
+ * Returns the name of the variable being assigned in the return of the Invoke.
+ * @return name of the variable being assigned in the return of the Invoke.
+ */
+ public String getAssignedVariable()
+ {
+ return assignedVariable;
+ }
+
+ /**
+ * Sets the name of the variable being assigned in the return of the Invoke.
+ * @param assignedVariable the name of the variable being assigned in the return of the Invoke.
+ */
+ public void setAssignedVariable(String assignedVariable)
+ {
+ this.assignedVariable = assignedVariable;
+ }
+
+ /**
+ * Returns the return type of the method invocation.
+ * @return the return type of the method invocation.
+ */
+ public String getReturnType()
+ {
+ return returnType;
+ }
+
+ /**
+ * Sets the return type of the method invocation.
+ * @param returnType the return type of the method invocation.
+ */
+ public void setReturnType(String returnType)
+ {
+ this.returnType = returnType;
+ }
+
+ /**
+ * Returns the list of parameters types that the invocation needs to match signature.
+ * @return the list of parameters types that the invocation needs to match signature.
+ */
+ public List<String> getParameterTypes()
+ {
+ return parameterTypes;
+ }
+
+ /**
+ * Sets the list of parameters types that the invocation needs to match signature.
+ * @param parameterTypes the list of parameters types that the invocation needs to match signature.
+ */
+ public void setParameterTypes(List<String> parameterTypes)
+ {
+ this.parameterTypes = parameterTypes;
+ }
+
+ /**
+ * Returns the list of parameter names (variable references) that the invocation uses.
+ * @return list of parameter names (variable references) that the invocation uses.
+ */
+ public List<String> getParameterNames()
+ {
+ return parameterNames;
+ }
+
+ /**
+ * Set list of parameter names (variable references) that the invocation uses.
+ * @param parameterNames
+ */
+ public void setParameterNames(List<String> parameterNames)
+ {
+ this.parameterNames = parameterNames;
+ }
+
+ @Override
+ public String toString()
+ {
+ return "Invoke [type=" + type + ", classCalled=" + classCalled + ", methodName="
+ + methodName + ", parameterTypes=" + parameterTypes + ", returnType=" + returnType
+ + "]";
+ }
+
+ /**
+ * Gets complete signature of the Invoke.
+ * <br>
+ * @return string in the format &lt;classCalled&gt;.&lt;methodName&gt;
+ */
+ public String getQualifiedName()
+ {
+ String methodName = getMethodName();
+ String classCalled = getClassCalled();
+ String signature = "";
+ if (classCalled != null)
+ {
+ signature = classCalled.replace('/', '.') + "." + methodName; //$NON-NLS-1$
+ }
+ else
+ {
+ signature = methodName;
+ }
+ return signature;
+ }
+
+}
diff --git a/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/source/model/Method.java b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/source/model/Method.java
new file mode 100644
index 0000000..eef91cb
--- /dev/null
+++ b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/source/model/Method.java
@@ -0,0 +1,207 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.preflighting.core.source.model;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Represents a method declaration for a given class.
+ */
+public class Method
+{
+
+ /**
+ * Constant for direct methods.
+ */
+ public static final String DIRECT = "direct";
+
+ /**
+ * Constant for virtual methods.
+ */
+ public static final String VIRTUAL = "virtual";
+
+ private boolean isStatic = false;
+
+ private boolean isConstructor = false;
+
+ private String visibility;
+
+ private String returnType;
+
+ private String methodName;
+
+ private int lineNumber;
+
+ private final List<String> parameterTypes = new ArrayList<String>();
+
+ private final List<Instruction> instructions = new ArrayList<Instruction>();
+
+ private final List<Variable> variables = new ArrayList<Variable>();
+
+ /**
+ * Returns the visibility of this method.
+ * @return visibility of this method.
+ */
+ public String getVisibility()
+ {
+ return visibility;
+ }
+
+ /**
+ * Sets the visibility of this method.
+ * @param visibility the visibility of this method.
+ */
+ public void setVisibility(String visibility)
+ {
+ this.visibility = visibility;
+ }
+
+ /**
+ * Returns the return type of this method.
+ * @return return type of this method.
+ */
+ public String getReturnType()
+ {
+ return returnType;
+ }
+
+ /**
+ * Sets the return type of this method.
+ * @param returnType the return type of this method.
+ */
+ public void setReturnType(String returnType)
+ {
+ this.returnType = returnType;
+ }
+
+ /**
+ * Returns true if this method is static, false otherwise.
+ * @return <code>true</code> if static, <code>false</code> otherwise.
+ */
+ public boolean isStatic()
+ {
+ return isStatic;
+ }
+
+ /**
+ * Returns true if this method is a constructor, false otherwise.
+ * @return <code>true</code> if constructor, <code>false</code> otherwise.
+ */
+ public boolean isConstructor()
+ {
+ return isConstructor;
+ }
+
+ /**
+ * Returns the list of parameters types in the method declaration.
+ * @return list of parameters types in the method declaration.
+ */
+ public List<String> getParameterTypes()
+ {
+ return parameterTypes;
+ }
+
+ /**
+ * Returns the list of {@link Instruction} objects that are part of the method declaration.
+ * @return the list of {@link Instruction} objects that are part of the method declaration.
+ */
+ public List<Instruction> getInstructions()
+ {
+ return instructions;
+ }
+
+ /**
+ * Set true if the method is static.
+ * @param isStatic the boolean that indicates whether this method is static.
+ */
+ public void setStatic(boolean isStatic)
+ {
+ this.isStatic = isStatic;
+ }
+
+ /**
+ * Set true if the method is a constructor.
+ * @param isConstructor the boolean that indicates whether this method is a constructor.
+ */
+ public void setConstructor(boolean isConstructor)
+ {
+ this.isConstructor = isConstructor;
+ }
+
+ /**
+ * Returns the method name.
+ * @return the method name.
+ */
+ public String getMethodName()
+ {
+ return methodName;
+ }
+
+ /**
+ * Sets the method name.
+ * @param methodName the method name.
+ */
+ public void setMethodName(String methodName)
+ {
+ this.methodName = methodName;
+ }
+
+ /**
+ * Returns the line number.
+ * @return the line number (the first line of this method).
+ */
+ public int getLineNumber()
+ {
+ return lineNumber;
+ }
+
+ /**
+ * Sets the line number of the declared method (the first line).
+ * @param lineNumber the line number of the declared method (the first line).
+ */
+ public void setLineNumber(int lineNumber)
+ {
+ this.lineNumber = lineNumber;
+ }
+
+ /**
+ * Returns the list of {@link Variable} objects.
+ * @return the list of {@link Variable} objects.
+ */
+ public List<Variable> getVariables()
+ {
+ return variables;
+ }
+
+ /**
+ * Adds a new {@link Variable} declared inside of the method declaration.
+ * @param variable the variable to be added.
+ */
+ public void addVariable(Variable variable)
+ {
+ this.variables.add(variable);
+ }
+
+ @Override
+ public String toString()
+ {
+ return "Method [isStatic=" + isStatic + ", isConstructor=" + isConstructor
+ + ", visibility=" + visibility + ", returnType=" + returnType + ", methodName="
+ + methodName + ", parameterTypes=" + parameterTypes + ", instructions="
+ + instructions + "]";
+ }
+}
diff --git a/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/source/model/PermissionGroups.java b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/source/model/PermissionGroups.java
new file mode 100644
index 0000000..c05a594
--- /dev/null
+++ b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/source/model/PermissionGroups.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.motorolamobility.preflighting.core.source.model;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Class that contains a list of required and optional permissions that the application needs (or should declare)
+ * based on the methods invoked inside the application. These permissions are
+ * the ones declared in the AndroidManifest.xml in the tag &lt;uses-permission>.
+ */
+public class PermissionGroups
+{
+ /**
+ * The permission must be declared
+ */
+ private final List<String> requiredPermissions = new ArrayList<String>();
+
+ /**
+ * At least one of this permissions must be declared
+ */
+ private final List<String> optionalPermissions = new ArrayList<String>();
+
+ /**
+ * Get the {@link List} of required permissions. These are the ones in the
+ * AndroidManifest.xml represented within the tag &lt;uses-permission>.
+ *
+ * @return Returns the list of required permissions.
+ */
+ public List<String> getRequiredPermissions()
+ {
+ return requiredPermissions;
+ }
+
+ /**
+ * Get the list of optional permissions. These are the ones in the
+ * AndroidManifest.xml represented within the tag &lt;uses-permission>.
+ *
+ * @return Returns the list of optional permissions.
+ */
+ public List<String> getOptionalPermissions()
+ {
+ return optionalPermissions;
+ }
+
+ /**
+ * This implementation provides a human-readable text of this
+ * {@link PermissionGroups}.
+ *
+ * @return Returns a human-readable text of this {@link PermissionGroups}.
+ *
+ * @see Object#toString()
+ */
+ @Override
+ public String toString()
+ {
+ return "PermissionGroups [requiredPermissions=" + requiredPermissions
+ + ", optionalPermissions=" + optionalPermissions + "]";
+ }
+}
diff --git a/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/source/model/SourceFileElement.java b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/source/model/SourceFileElement.java
new file mode 100644
index 0000000..e12690f
--- /dev/null
+++ b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/source/model/SourceFileElement.java
@@ -0,0 +1,283 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.motorolamobility.preflighting.core.source.model;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.eclipse.jdt.core.dom.CompilationUnit;
+
+import com.motorolamobility.preflighting.core.applicationdata.Element;
+
+/**
+ * Represent a source file.
+ */
+public class SourceFileElement extends Element
+{
+ private String classFullPath;
+
+ private String superclassName;
+
+ private String sourceName;
+
+ private boolean isInnerClass;
+
+ private CompilationUnit compilationUnit;
+
+ private final List<Field> staticFields = new ArrayList<Field>();
+
+ private final List<Field> instanceFields = new ArrayList<Field>();
+
+ private final List<Method> directMethods = new ArrayList<Method>();
+
+ private final List<Method> virtualMethods = new ArrayList<Method>();
+
+ private final Set<String> usedStringConstants = new HashSet<String>();
+
+ private final Set<String> usedLayoutConstants = new HashSet<String>();
+
+ /**
+ * Instantiates a source file based on a {@link File} and
+ * a {@link Element} as its parent.
+ *
+ * @param file {@link File} object for creating the source file.
+ * @param parent Parent element represented by an {@link Element}.
+ */
+ public SourceFileElement(File file, Element parent)
+ {
+ super(file.getName(), parent, Element.Type.FILE_JAVA);
+ setFile(file);
+ }
+
+ /**
+ * Returns <code>true</code> in case this element is an inner class,
+ * <code>false</code> otherwise.
+ *
+ * @return Returns <code>true</code> in case this element is an inner class,
+ * <code>false</code> otherwise.
+ */
+ public boolean isInnerClass()
+ {
+ return isInnerClass;
+ }
+
+ /**
+ * Set <code>true</code> for declaring this File as an inner
+ * class, <code>false</code> for anything else.
+ *
+ * @param isInnerClass Value which determines whether this
+ * element is an inner class.
+ */
+ public void setInnerClass(boolean isInnerClass)
+ {
+ this.isInnerClass = isInnerClass;
+ }
+
+ /**
+ * Gets the full path of the class inside source file.
+ */
+ public String getClassFullPath()
+ {
+ return classFullPath;
+ }
+
+ /**
+ * Set the full path of the class inside source file.
+ *
+ * @param classFullPath The class which originates the full path
+ * to be set.
+ */
+ public void setClassFullPath(String classFullPath)
+ {
+ this.classFullPath = classFullPath;
+ }
+
+ /**
+ * Gets the superclass name of this {@link SourceFileElement}.
+ *
+ * @return Returns the name of the superclass of the class inside this source file.
+ */
+ public String getSuperclassName()
+ {
+ return superclassName;
+ }
+
+ /**
+ * Sets the superclass name of this {@link SourceFileElement}.
+ *
+ * @param superclassName Superclass name to be set.
+ */
+ public void setSuperclassName(String superclassName)
+ {
+ this.superclassName = superclassName;
+ }
+
+ /**
+ * Get the source name of this {@link SourceFileElement}.
+ *
+ * @return Returns the source name.
+ */
+ public String getSourceName()
+ {
+ return sourceName;
+ }
+
+ /**
+ * Sets the name of the source name.
+ *
+ * @param sourceName source name to be set.
+ */
+ public void setSourceName(String sourceName)
+ {
+ this.sourceName = sourceName;
+ }
+
+ /**
+ * Gets the list of static fields.
+ *
+ * @return Return the list of {@link Field} declared as static in the source file.
+ */
+ public List<Field> getStaticFields()
+ {
+ return staticFields;
+ }
+
+ /**
+ * Gets the list of instance fields.
+ *
+ * @return Returns the list of {@link Field} declared as instance (non-static) in the source file.
+ */
+ public List<Field> getInstanceFields()
+ {
+ return instanceFields;
+ }
+
+ /**
+ * Gets the list of direct methods.
+ *
+ * @return Returns the list of {@link Method} directly declared in this source file.
+ */
+ public List<Method> getDirectMethods()
+ {
+ return directMethods;
+ }
+
+ /**
+ * Gets the list of virtual methods.
+ *
+ * @return Returns the list of {@link Method} inherited from other classes (or declared as abstract).
+ */
+ public List<Method> getVirtualMethods()
+ {
+ return virtualMethods;
+ }
+
+ /**
+ * Gets the set of string constants used in the code (entries don't include "R.string").
+ *
+ * @return the set of {@link String} constants used in the source file.
+ */
+ public Set<String> getUsedStringConstants()
+ {
+ return usedStringConstants;
+ }
+
+ /**
+ * Gets the set of layout constants used in the code (entries don't include "R.layout").
+ *
+ * @return the set of {@link String} constants representing layouts used in the source file.
+ */
+ public Set<String> getUsedLayoutConstants()
+ {
+ return usedLayoutConstants;
+ }
+
+ /**
+ * Adds a method to this {@link SourceFileElement}.
+ *
+ * @param type The types are: ("direct" or "virtual").
+ * @param method The {@link Method} to be added.
+ */
+ public void addMethod(String type, Method method)
+ {
+ if (Method.DIRECT.equals(type))
+ {
+ getDirectMethods().add(method);
+ }
+ else
+ {
+ getVirtualMethods().add(method);
+ }
+ }
+
+ /**
+ * Gets the source file full path.
+ *
+ * @return Returns the full path to source file.
+ */
+ public String getSourceFileFullPath()
+ {
+ String classFullPath = getClassFullPath();
+ String pack = classFullPath;
+ if (classFullPath.contains("/"))
+ {
+ pack = classFullPath.substring(0, classFullPath.lastIndexOf('/'));
+ }
+ String sourceFile = pack + "/" + getSourceName();
+ return sourceFile;
+ }
+
+ /**
+ * Get the {@link CompilationUnit} associated with this {@link SourceFileElement}.
+ *
+ * @return Returns the compilationUnit (if associated with an Android project, but not for APK).
+ */
+ public CompilationUnit getCompilationUnit()
+ {
+ return compilationUnit;
+ }
+
+ /**
+ * Set the compilationUnit (if associated with an Android project, but not for APK).
+ *
+ * @param compilationUnit The {@link CompilationUnit} to be set.
+ */
+ public void setCompilationUnit(CompilationUnit compilationUnit)
+ {
+ this.compilationUnit = compilationUnit;
+ }
+
+ /**
+ * This implementation provides a human-readable text of this
+ * {@link SourceFileElement}.
+ *
+ * @return Returns a human-readable text of this {@link SourceFileElement}.
+ *
+ * @see Object#toString()
+ */
+ @Override
+ public String toString()
+ {
+ return "SourceFileElement [classFullPath=" + classFullPath + ", superclassName="
+ + superclassName + ", sourceName=" + sourceName + ", staticFields=" + staticFields
+ + ", instanceFields=" + instanceFields + ", directMethods=" + directMethods
+ + ", virtualMethods=" + virtualMethods + "]";
+ }
+}
diff --git a/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/source/model/Variable.java b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/source/model/Variable.java
new file mode 100644
index 0000000..872afdb
--- /dev/null
+++ b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/source/model/Variable.java
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.motorolamobility.preflighting.core.source.model;
+
+/**
+ * Represents a variable declared in the code being verified.
+ */
+public class Variable
+{
+
+ protected boolean isStatic = false;
+
+ protected boolean isFinal = false;
+
+ protected String visibility;
+
+ protected String type;
+
+ protected String name;
+
+ protected String value;
+
+ protected int lineNumber;
+
+ /**
+ * Returns the type of the variable.
+ * @return Type of the variable.
+ */
+ public String getType()
+ {
+ return type;
+ }
+
+ /**
+ * Sets the type of the variable.
+ *
+ * @param type the type of the variable.
+ */
+ public void setType(String type)
+ {
+ this.type = type;
+ }
+
+ /**
+ * Returns the name of the variable.
+ * @return Name of the variable.
+ */
+ public String getName()
+ {
+ return name;
+ }
+
+ /**
+ * Set the name of the variable.
+ *
+ * @param name the name of the variable.
+ */
+ public void setName(String name)
+ {
+ this.name = name;
+ }
+
+ /**
+ * Returns a <code>boolean</code> stating whether this variable is static or not.
+ * @return <code>true</code> if variable is static, <code>false</code> if non-static (instance variable).
+ */
+ public boolean isStatic()
+ {
+ return isStatic;
+ }
+
+ /**
+ * Returns a <code>boolean</code> stating whether this variable is final or not.
+ * @return <code>true</code> if variable is final, <code>false</code> if it is not.
+ */
+ public boolean isFinal()
+ {
+ return isFinal;
+ }
+
+ /**
+ * Returns the visibility of the variable.
+ * @return The visibility (public, protected, package, private) of the variable.
+ */
+ public String getVisibility()
+ {
+ return visibility;
+ }
+
+ /**
+ * Set the visibility (public, protected, package, private) of the variable.
+ *
+ * @param visibility
+ */
+ public void setVisibility(String visibility)
+ {
+ this.visibility = visibility;
+ }
+
+ /**
+ * Set if the variable is static.
+ *
+ * @param isStatic <code>true</code> if variable is static, <code>false</code> if non-static (instance variable).
+ */
+ public void setStatic(boolean isStatic)
+ {
+ this.isStatic = isStatic;
+ }
+
+ /**
+ * Set if the variable is final.
+ *
+ * @param isFinal <code>true</code> if variable is final, <code>false</code> if not.
+ */
+ public void setFinal(boolean isFinal)
+ {
+ this.isFinal = isFinal;
+ }
+
+ /**
+ * Returns a <code> String </code> representing the value of the variable.
+ * @return The value assigned to the variable.
+ */
+ public String getValue()
+ {
+ return value;
+ }
+
+ /**
+ * Set a value to the variable.
+ *
+ * @param value
+ */
+ public void setValue(String value)
+ {
+ this.value = value;
+ }
+
+ /**
+ * Return the line number where this variable appears in the code.
+ * @return The line of the variable if possible, 0 if not found.
+ */
+ public int getLineNumber()
+ {
+ return lineNumber;
+ }
+
+ /**
+ * Set the line of the variable.
+ *
+ * @param lineNumber
+ */
+ public void setLineNumber(int lineNumber)
+ {
+ this.lineNumber = lineNumber;
+ }
+
+ @Override
+ public String toString()
+ {
+ return "Variable [isStatic=" + isStatic + ", isFinal=" + isFinal + ", visibility="
+ + visibility + ", type=" + type + ", name=" + name + ", value=" + value + "]";
+ }
+
+}
diff --git a/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/utils/CheckerUtils.java b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/utils/CheckerUtils.java
new file mode 100644
index 0000000..d0e11ab
--- /dev/null
+++ b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/utils/CheckerUtils.java
@@ -0,0 +1,304 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.preflighting.core.utils;
+
+import java.io.File;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.jdt.core.dom.CompilationUnit;
+import org.w3c.dom.Document;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import com.motorolamobility.preflighting.core.PreflightingCorePlugin;
+import com.motorolamobility.preflighting.core.applicationdata.ApplicationData;
+import com.motorolamobility.preflighting.core.applicationdata.SourceFolderElement;
+import com.motorolamobility.preflighting.core.applicationdata.XMLElement;
+import com.motorolamobility.preflighting.core.checker.condition.CanExecuteConditionStatus;
+import com.motorolamobility.preflighting.core.i18n.PreflightingCoreNLS;
+import com.motorolamobility.preflighting.core.source.model.Invoke;
+import com.motorolamobility.preflighting.core.source.model.SourceFileElement;
+
+/**
+ * Utility class that can be used by any checker and its conditions.
+ */
+public class CheckerUtils
+{
+ /**
+ * Defines the key for retrieving files of a map for a given {@link CompilationUnit}.
+ */
+ public static final String JAVA_FILE_PROPERTY = "java_file";
+
+ /**
+ * Parse the attribute value for the given id: @+id/object_id from the Android manifest file.
+ * @param id the id whose value will be returnd.
+ * @return The object_id portion of the string.
+ */
+ public static String getIdValue(String id)
+ {
+ int index = id.lastIndexOf("/"); //$NON-NLS-1$
+ if ((index != -1) && ((index + 1) < id.length()))
+ {
+ id = id.substring(index + 1);
+ }
+ return id;
+ }
+
+ /**
+ * Retrieve the app target from a given Manifest document.
+ * @param manifestDoc XML representing AndroidManifest.xml.
+ * @return The value of android:targetSdkVersion.
+ * @throws NumberFormatException in case of preview sdk (target sdk being not a number string).
+ */
+ public static String getTargetSdk(Document manifestDoc) throws NumberFormatException
+ {
+ String targetSdk = "";
+ NodeList usesSdkList = manifestDoc.getElementsByTagName(ManifestConstants.USES_SDK_TAG);
+ if (usesSdkList.getLength() > 0)
+ {
+ Node usesSdkNode = usesSdkList.item(0);
+ Node targetSdkAttribute =
+ usesSdkNode.getAttributes().getNamedItem(
+ ManifestConstants.TARGET_SDK_VERSION_ATTRIBUTE);
+
+ if (targetSdkAttribute != null)
+ {
+ targetSdk = targetSdkAttribute.getNodeValue();
+ }
+ }
+ return targetSdk;
+ }
+
+ /**
+ * Retrieve the app minSdk version from a given Manifest document.
+ * @param manifestDoc XML representing AndroidManifest.xml.
+ * @return The value of android:minSdkVersion.
+ * @throws NumberFormatException in case of preview sdk.
+ */
+ public static String getMinSdk(Document manifestDoc) throws NumberFormatException
+ {
+ String minSdk = "";
+ NodeList usesSdkList = manifestDoc.getElementsByTagName(ManifestConstants.USES_SDK_TAG);
+ if (usesSdkList.getLength() > 0)
+ {
+ Node usesSdkNode = usesSdkList.item(0);
+ Node minSdkAttribute =
+ usesSdkNode.getAttributes().getNamedItem(
+ ManifestConstants.MIN_SDK_VERSION_ATTRIBUTE);
+
+ if (minSdkAttribute != null)
+ {
+ minSdk = minSdkAttribute.getNodeValue();
+ }
+ }
+ return minSdk;
+ }
+
+ /**
+ * Retrieve all permissions declared on the manifest file.
+ * @param manifestDoc The Manifest xml Document.
+ * @return Map containing <permissionId, xmlNode> where xmlNode is the entire uses-permission for that permission.
+ */
+ public static Map<String, Node> getPermissions(Document manifestDoc)
+ {
+ NodeList permissionsNodeLst =
+ manifestDoc.getElementsByTagName(ManifestConstants.USES_PERMISSION_ATTRIBUTE);
+
+ //Extract permissions from manifest
+ Map<String, Node> manifestPermissions =
+ new HashMap<String, Node>(permissionsNodeLst.getLength());
+ for (int i = 0; i < permissionsNodeLst.getLength(); i++)
+ {
+ Node permissionNode = permissionsNodeLst.item(i);
+ NamedNodeMap permissionMap = permissionNode.getAttributes();
+ Node permissionAtr =
+ permissionMap.getNamedItem(ManifestConstants.ANDROID_NAME_ATTRIBUTE);
+
+ if ((permissionAtr != null))
+ {
+ String permissionId = permissionAtr.getNodeValue().trim();
+ if (permissionId.length() > 0)
+ {
+ manifestPermissions.put(permissionId, permissionNode);
+ }
+ }
+ }
+ return manifestPermissions;
+ }
+
+ /**
+ * Given an {@link ApplicationData} file structure, it is verified whether
+ * the AndroidManifest.xml file exists. The result is returned as an
+ * {@link IStatus}.
+ *
+ * @param data {@link ApplicationData} file structure.
+ * @param conditionId The condition Id, can be null.
+ *
+ * @return Return the {@link IStatus} of the AndroidManifest.xml file
+ * existence. The {@link IStatus} returned actually is an extension of it,
+ * called {@link CanExecuteConditionStatus}.
+ */
+ public static CanExecuteConditionStatus isAndroidManifestFileExistent(ApplicationData data,
+ String conditionId)
+ {
+ CanExecuteConditionStatus status =
+ new CanExecuteConditionStatus(IStatus.OK, PreflightingCorePlugin.PLUGIN_ID, ""); //$NON-NLS-1$
+
+ //Look for Manifest file
+ XMLElement manifestElement = data.getManifestElement();
+ if (manifestElement == null)
+ {
+ status =
+ new CanExecuteConditionStatus(IStatus.ERROR, PreflightingCorePlugin.PLUGIN_ID,
+ PreflightingCoreNLS.Invalid_ManifestFile);
+ }
+ else if ((manifestElement.getDocument()) == null)
+ {
+ status =
+ new CanExecuteConditionStatus(IStatus.ERROR, PreflightingCorePlugin.PLUGIN_ID,
+ PreflightingCoreNLS.Invalid_ManifestFile);
+ }
+
+ if (conditionId != null)
+ {
+ status.setConditionId(conditionId);
+ }
+
+ return status;
+ }
+
+ /**
+ * Given an {@link ApplicationData} structure, it is verified whether
+ * the javaModel is available. The result is returned as an
+ * {@link IStatus}.
+ *
+ * @param data {@link ApplicationData} file structure.
+ * @param conditionId The condition Id, can be null.
+ *
+ * @return Return the {@link IStatus} of the AndroidManifest.xml file
+ * existence. The {@link IStatus} returned actually is an extension of it,
+ * called {@link CanExecuteConditionStatus}.
+ */
+ public static CanExecuteConditionStatus isJavaModelAvailable(ApplicationData data,
+ String conditionId)
+ {
+ CanExecuteConditionStatus status =
+ new CanExecuteConditionStatus(IStatus.OK, PreflightingCorePlugin.PLUGIN_ID, ""); //$NON-NLS-1$;
+ List<SourceFolderElement> sourceFolderElements = data.getJavaModel();
+ if ((sourceFolderElements == null) || sourceFolderElements.isEmpty())
+ {
+ status =
+ new CanExecuteConditionStatus(IStatus.ERROR, PreflightingCorePlugin.PLUGIN_ID,
+ PreflightingCoreNLS.JavaModelNotFound_Err);
+ }
+
+ if (conditionId != null)
+ {
+ status.setConditionId(conditionId);
+ }
+
+ return status;
+ }
+
+ /**
+ * Given an {@link ApplicationData} structure, it is verified whether
+ * the javaModel is complete, i.e. exists and invokeMethods are available. The result is returned as an
+ * {@link IStatus}.
+ *
+ * @param data {@link ApplicationData} file structure.
+ * @param conditionId The condition Id, can be null.
+ *
+ * @return Return the {@link IStatus} of the AndroidManifest.xml file
+ * existence. The {@link IStatus} returned actually is an extension of it,
+ * called {@link CanExecuteConditionStatus}.
+ */
+ public static CanExecuteConditionStatus isJavaModelComplete(ApplicationData data,
+ String conditionId)
+ {
+ CanExecuteConditionStatus status = isJavaModelAvailable(data, conditionId); //$NON-NLS-1$;
+
+ List<SourceFolderElement> sourceFolderElements = data.getJavaModel();
+ if (status.isOK())
+ {
+ boolean found = false;
+ for (SourceFolderElement sourceFolderElement : sourceFolderElements)
+ {
+ List<Invoke> invokedMethods = sourceFolderElement.getInvokedMethods();
+ if ((invokedMethods != null) && !invokedMethods.isEmpty())
+ {
+ found = true;
+ break;
+ }
+ }
+ if (!found)
+ {
+ status =
+ new CanExecuteConditionStatus(IStatus.ERROR,
+ PreflightingCorePlugin.PLUGIN_ID,
+ PreflightingCoreNLS.EmptyInvokedMethods_Err);
+ }
+ }
+ if (status.isOK())
+ {
+ boolean found = false;
+ for (SourceFolderElement sourceFolderElement : sourceFolderElements)
+ {
+ List<SourceFileElement> sourceFiles = sourceFolderElement.getSourceFileElements();
+ if ((sourceFiles != null) && !sourceFiles.isEmpty())
+ {
+ found = true;
+ break;
+ }
+ }
+ if (!found)
+ {
+ status =
+ new CanExecuteConditionStatus(IStatus.ERROR,
+ PreflightingCorePlugin.PLUGIN_ID,
+ PreflightingCoreNLS.NoSourceFilesFound_Err);
+ }
+ }
+
+ if (conditionId != null)
+ {
+ status.setConditionId(conditionId);
+ }
+
+ return status;
+ }
+
+ /**
+ * Create a fileToIssues map, containing only 1 line.
+ * @param file
+ * @param currentIssuedLine
+ * @return
+ */
+ public static Map<File, List<Integer>> createFileToIssuesMap(File file, int currentIssuedLine)
+ {
+ Map<File, List<Integer>> fileToIssueLines = new HashMap<File, List<Integer>>(1);
+ if ((file != null) && (currentIssuedLine > 0))
+ {
+ fileToIssueLines.put(file, Arrays.asList(currentIssuedLine));
+ }
+ return fileToIssueLines;
+ }
+
+}
diff --git a/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/utils/LayoutConstants.java b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/utils/LayoutConstants.java
new file mode 100644
index 0000000..199b322
--- /dev/null
+++ b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/utils/LayoutConstants.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.preflighting.core.utils;
+
+/**
+ * This Interface intends to hold Android Layout XML constants.
+ * It is not mandatory to use these constants, they are here to avoid
+ * mistypes and minimize impacts due to changes.
+ */
+public interface LayoutConstants
+{
+ /**
+ * The 'android:id' tag.
+ */
+ String ANDROID_ID_ATTRIBUTE = "android:id"; //$NON-NLS-1$
+}
diff --git a/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/utils/LimitedList.java b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/utils/LimitedList.java
new file mode 100644
index 0000000..3f7fb46
--- /dev/null
+++ b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/utils/LimitedList.java
@@ -0,0 +1,208 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.preflighting.core.utils;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+
+import com.motorolamobility.preflighting.core.exception.ValidationLimitException;
+
+/**
+ * An implementation of ArrayList that will throw an {@link ValidationLimitException} if its size is bigger than maxSize after an item addition.
+ * If UNLIMITED is passed as maxSize during list construction {@link LimitedList} will behave exactly like {@link ArrayList}.
+ * For more information regarding the behavior of the list @see ArrayList.
+ */
+public class LimitedList<T> extends ArrayList<T>
+{
+ private static final long serialVersionUID = 7066195136542105428L;
+
+ /**
+ * Constant that determines if this List is unlimited, when used as MaxSize.
+ */
+ public static final int UNLIMITED = -1;
+
+ /**
+ * Flag indicating that this is an unlimited list.
+ */
+ private boolean unlimited = false;
+
+ /**
+ * The max size of this list.
+ */
+ private int maxSize = 0;
+
+ /**
+ * Constructs an empty list with an initial capacity of ten elements.
+ * In order to construct an unlimited list use UNLIMITED as maxSize.
+ * @param maxSize this list maxSize, must be greater than zero or UNLIMITED.
+ * @exception IllegalArgumentException if the specified maxSize
+ * is not greater than zero.
+ */
+ public LimitedList(int maxSize)
+ {
+ this(10, maxSize);
+ }
+
+ /**
+ * Constructs an empty list with the specified initial capacity.
+ * In order to construct an unlimited list use UNLIMITED as maxSize.
+ * @param initialCapacity the initial capacity of the list.
+ * @param maxSize this list maxSize, must be greater than zero or UNLIMITED.
+ * @exception IllegalArgumentException if the specified maxSize or initialCapacity
+ * is not greater than zero.
+ */
+ public LimitedList(int initialCapacity, int maxSize)
+ {
+ super(initialCapacity);
+ if ((maxSize < 0) && (maxSize != UNLIMITED))
+ {
+ throw new IllegalArgumentException(
+ "Max size must be a positive integer or LimitedList.UNLIMITED");
+ }
+ this.maxSize = maxSize;
+ unlimited = maxSize == UNLIMITED;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see java.util.ArrayList#add(java.lang.Object)
+ */
+ @Override
+ public boolean add(T element)
+ {
+ if (!exceedsMaxSize(1))
+ {
+ return super.add(element);
+ }
+ else
+ {
+ throw new ValidationLimitException();
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see java.util.ArrayList#add(int, java.lang.Object)
+ */
+ @Override
+ public void add(int index, T element)
+ {
+ if (!exceedsMaxSize(1))
+ {
+ super.add(index, element);
+ }
+ else
+ {
+ throw new ValidationLimitException();
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see java.util.ArrayList#addAll(java.util.Collection)
+ */
+ @Override
+ public boolean addAll(Collection<? extends T> c)
+ {
+ if (!exceedsMaxSize(c.size()))
+ {
+ return super.addAll(c);
+ }
+ else
+ {
+ List<T> allowedSubList = getAllowedList(c);
+ super.addAll(allowedSubList);
+ throw new ValidationLimitException();
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see java.util.ArrayList#addAll(int, java.util.Collection)
+ */
+ @Override
+ public boolean addAll(int index, Collection<? extends T> c)
+ {
+ if (!exceedsMaxSize(c.size()))
+ {
+ return super.addAll(index, c);
+ }
+ else
+ {
+ List<T> allowedSubList = getAllowedList(c);
+ super.addAll(index, allowedSubList);
+ throw new ValidationLimitException();
+ }
+ }
+
+ /**
+ * Retrieves a subset of c containing all elements that can still be added into this list.
+ */
+ private List<T> getAllowedList(Collection<? extends T> c)
+ {
+ int allowed = maxSize - size();
+ @SuppressWarnings("unchecked")
+ T[] toBeAdded = (T[]) new Object[maxSize];
+ System.arraycopy(c.toArray(), 0, toBeAdded, 0, allowed);
+ List<T> allowedSubList = Arrays.asList(toBeAdded);
+ return allowedSubList;
+ }
+
+ /**
+ * Verifies if it's possible to add a given number of elements into
+ * the list, without exceeding its maxSize.
+ * @param newElementsCount number of elements to be added
+ * @return true if newElementsCount would exceed maxSize if added
+ */
+ private boolean exceedsMaxSize(int newElementsCount)
+ {
+ boolean exceedsMaxSize = false;
+ if (!unlimited)
+ {
+ if ((size() + newElementsCount) > maxSize)
+ {
+ exceedsMaxSize = true;
+ }
+ }
+ return exceedsMaxSize;
+ }
+
+ /**
+ * @return the maxSize
+ */
+ public int getMaxSize()
+ {
+ return maxSize;
+ }
+
+ /**
+ * @param maxSize the maxSize to set
+ */
+ public void setMaxSize(int maxSize)
+ {
+ this.maxSize = maxSize;
+ }
+
+ /**
+ * @return the unlimited
+ */
+ public boolean isUnlimited()
+ {
+ return unlimited;
+ }
+}
diff --git a/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/utils/ManifestConstants.java b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/utils/ManifestConstants.java
new file mode 100644
index 0000000..6b8ebdd
--- /dev/null
+++ b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/utils/ManifestConstants.java
@@ -0,0 +1,117 @@
+/*
+ /*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.preflighting.core.utils;
+
+/**
+ * This Interface intends to hold Android Manifest XML constants.
+ * It's not mandatory to use these constants, they are here to avoid
+ * mistypes and minimize impacts due to changes.
+ */
+public class ManifestConstants
+{
+
+ /**
+ * The 'manifest' tag
+ */
+ public static final String MANIFEST_TAG = "manifest"; //$NON-NLS-1$
+
+ /**
+ * The 'supports-screens' tag
+ */
+ public static final String SUPPORTS_SCREEN_TAG = "supports-screens"; //$NON-NLS-1$
+
+ /**
+ * The 'android:smallScreens' tag
+ */
+ public static final String SMALL_SCREENS_ATTRIBUTE = "android:smallScreens"; //$NON-NLS-1$
+
+ /**
+ * The 'android:xlargeScreens' tag
+ */
+ public static final String XLARGE_SCREENS_ATTRIBUTE = "android:xlargeScreens"; //$NON-NLS-1$
+
+ /**
+ * The 'android:targetSdkVersion' tag
+ */
+ public static final String TARGET_SDK_VERSION_ATTRIBUTE = "android:targetSdkVersion"; //$NON-NLS-1$
+
+ /**
+ * The 'android:minSdkVersion' tag
+ */
+ public static final String MIN_SDK_VERSION_ATTRIBUTE = "android:minSdkVersion"; //$NON-NLS-1$
+
+ /**
+ * The 'uses-sdk' tag
+ */
+ public static final String USES_SDK_TAG = "uses-sdk"; //$NON-NLS-1$
+
+ /**
+ * The 'uses-feature' tag
+ */
+ public static final String USES_FEATURE_TAG = "uses-feature"; //$NON-NLS-1$
+
+ /**
+ * The 'uses-permission' tag
+ */
+ public final static String USES_PERMISSION_ATTRIBUTE = "uses-permission"; //$NON-NLS-1$
+
+ /**
+ * The 'android:name' tag
+ */
+ public final static String ANDROID_NAME_ATTRIBUTE = "android:name"; //$NON-NLS-1$
+
+ /**
+ * The application node
+ */
+ public static final String APPLICATION_TAG = "application"; //$NON-NLS-1$
+
+ /**
+ * The 'android:debuggable' attribute
+ */
+ public static final String ANDROID_DEBUGGABLE_ATTRIBUTE = "android:debuggable"; //$NON-NLS-1$
+
+ /**
+ * The 'android:icon' attribute
+ */
+ public static final String ANDROID_ICON_ATTRIBUTE = "android:icon"; //$NON-NLS-1$
+
+ /**
+ * The 'android:label' attribute
+ */
+ public static final String ANDROID_LABEL_ATTRIBUTE = "android:label"; //$NON-NLS-1$
+
+ /**
+ * The 'android:versionName' attribute
+ */
+ public static final String ANDROID_VERSION_NAME_ATTRIBUTE = "android:versionName"; //$NON-NLS-1$
+
+ /**
+ * The 'android:versionCode' attribute
+ */
+ public static final String ANDROID_VERSION_CODE_ATTRUIBUTE = "android:versionCode"; //$NON-NLS-1$
+
+ /**
+ * The 'android:maxSdkVersion' attribute
+ */
+ public static final String ANDROID_MAX_SDK_VERSION_ATTRIBUTE = "android:maxSdkVersion"; //$NON-NLS-1$
+
+ /**
+ * The 'android:targetSdkVersion' attribute
+ */
+ public static final String ANDROID_TARGET_SDK_VERSION_ATTRIBUTE = "android:targetSdkVersion"; //$NON-NLS-1$
+
+}
diff --git a/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/utils/XmlUtils.java b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/utils/XmlUtils.java
new file mode 100644
index 0000000..e448943
--- /dev/null
+++ b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/utils/XmlUtils.java
@@ -0,0 +1,209 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.preflighting.core.utils;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FilenameFilter;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.io.UnsupportedEncodingException;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.transform.OutputKeys;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
+
+import org.eclipse.core.runtime.Platform;
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+import org.w3c.dom.ls.DOMImplementationLS;
+import org.w3c.dom.ls.LSOutput;
+import org.w3c.dom.ls.LSSerializer;
+
+import com.motorolamobility.preflighting.core.devicelayoutspecification.LayoutDevicesReader;
+import com.motorolamobility.preflighting.core.internal.devicelayoutspecification.LayoutDevicesType;
+import com.motorolamobility.preflighting.core.internal.devicelayoutspecification.ObjectFactory;
+import com.motorolamobility.preflighting.core.logging.PreflightingLogger;
+
+/**
+ * Utility class to manipulate XML documents, such as:
+ * <ul>
+ * <li>Print formatted XML in output streams;</li>
+ * <li>Get a specified node as a string;</li>
+ * <li>Parse a &lt;device&gt;.xml available in the folder devices to get representation.</li>
+ * </ul>
+ */
+public class XmlUtils
+{
+ public static final String DEVICE_CONFIGURATION_FOLDER = "devices";
+
+ /**
+ * Print the indented document.
+ *
+ * @param stream
+ * @param document
+ *
+ * @throws UnsupportedEncodingException
+ * @throws TransformerExceptions
+ */
+ public static void printXMLFormat(Document document) throws UnsupportedEncodingException,
+ TransformerException
+ {
+ TransformerFactory transformerFactory = TransformerFactory.newInstance();
+ transformerFactory.setAttribute("indent-number", 4); //$NON-NLS-1$
+ Transformer transformer = transformerFactory.newTransformer();
+ transformer.setOutputProperty(OutputKeys.METHOD, "xml"); //$NON-NLS-1$
+ transformer.setOutputProperty(OutputKeys.INDENT, "yes"); //$NON-NLS-1$
+
+ StreamResult streamResult = new StreamResult(); //$NON-NLS-1$
+
+ DOMSource source = new DOMSource(document);
+ transformer.transform(source, streamResult);
+ }
+
+ /**
+ * Get a string representing a XML node.
+ *
+ * @param node XML node to be printed.
+ * @param deep If true recursively prints the node and all of its children. If false prints only the node itself.
+ *
+ * @return string Representation of the document.
+ */
+ public static String getXMLNodeAsString(Node node, boolean deep)
+ {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ PrintStream stream = new PrintStream(baos);
+ String outputString = null;
+ try
+ {
+ DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
+ dbf.setNamespaceAware(true);
+ DocumentBuilder builder = dbf.newDocumentBuilder();
+
+ DOMImplementationLS ls = (DOMImplementationLS) builder.getDOMImplementation();
+ LSSerializer lss = ls.createLSSerializer();
+ LSOutput lso = ls.createLSOutput();
+ lso.setByteStream(stream);
+ lss.write(node.cloneNode(deep), lso);
+ stream.flush();
+ outputString = baos.toString();
+ outputString =
+ outputString.substring(outputString.indexOf(">") + 1, outputString.length());
+ }
+ catch (ParserConfigurationException e)
+ {
+ PreflightingLogger.debug("Unable to get Xml Node as String");
+ }
+ finally
+ {
+ if (baos != null)
+ {
+ try
+ {
+ baos.close();
+ }
+ catch (IOException e)
+ {
+ //Do nothing.
+ }
+ }
+ try
+ {
+ if (stream != null)
+ {
+ stream.flush();
+ stream.close();
+ }
+ }
+ catch (Exception ex)
+ {
+ //do nothing
+ }
+ }
+
+ return outputString;
+ }
+
+ /**
+ * Parse the data about device folder that can be used in the validation.
+ *
+ * @return {@link LayoutDevicesType} representing device (e.g.: screen layout).
+ */
+ public static LayoutDevicesType parseDevicesXmlFiles()
+ {
+ LayoutDevicesType layoutDevicesType = ObjectFactory.getInstance().createLayoutDevicesType();
+
+ String path = Platform.getInstallLocation().getURL().getPath();
+ if (!path.endsWith(File.separator))
+ {
+ path += File.separator;
+ }
+ path += DEVICE_CONFIGURATION_FOLDER;
+ File xmlFilesFolder = new File(path);
+
+ if (xmlFilesFolder.exists() && xmlFilesFolder.isDirectory())
+ {
+ String[] deviceXmlFiles = xmlFilesFolder.list(new FilenameFilter()
+ {
+ public boolean accept(File arg0, String arg1)
+ {
+ if (arg1.endsWith(".xml"))
+ {
+ return true;
+ }
+ return false;
+ }
+ });
+
+ for (String currentFile : deviceXmlFiles)
+ {
+ layoutDevicesType.getDevices().addAll(
+ readDeviceFile(new File(xmlFilesFolder, currentFile)).getDevices());
+ }
+ }
+
+ return layoutDevicesType;
+ }
+
+ private static LayoutDevicesType readDeviceFile(File f)
+ {
+ LayoutDevicesType layoutDevicesType = null;
+ DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+ DocumentBuilder builder;
+ try
+ {
+ builder = factory.newDocumentBuilder();
+ Document document = builder.parse(f);
+ LayoutDevicesReader reader = new LayoutDevicesReader(document);
+ layoutDevicesType = reader.read();
+ }
+ catch (ParserConfigurationException e)
+ {
+ PreflightingLogger.debug("Unable to read file " + f.getAbsolutePath());
+ }
+ catch (Exception e)
+ {
+ PreflightingLogger.debug("Unable to parse file " + f.getAbsolutePath());
+ }
+ return layoutDevicesType;
+ }
+}
diff --git a/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/validation/ApplicationValidationResult.java b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/validation/ApplicationValidationResult.java
new file mode 100644
index 0000000..13ad70c
--- /dev/null
+++ b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/validation/ApplicationValidationResult.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.preflighting.core.validation;
+
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.core.runtime.IStatus;
+import org.w3c.dom.Document;
+
+/**
+ * This class encapsulates a single execution of validation for an application.
+ */
+public final class ApplicationValidationResult
+{
+ private final List<ValidationResult> results;
+
+ private final Map<String, IStatus> executionStatus;
+
+ private final String application;
+
+ private final int version;
+
+ private final String applicationPath;
+
+ private Document resultXMLDocument;
+
+ /**
+ * Construct a new ApplicationValidationResult with the given parameters.
+ *
+ * @param application the name of the Application.
+ * @param version the version of the Application.
+ * @param applicationPath the path of the Application.
+ */
+ public ApplicationValidationResult(String application, int version, String applicationPath)
+ {
+ this.results = new ArrayList<ValidationResult>();
+ this.executionStatus = new LinkedHashMap<String, IStatus>();
+ this.application = application;
+ this.version = version;
+ this.applicationPath = applicationPath;
+ }
+
+ /**
+ * Add a {@link ValidationResult} representing the results collected from a specific condition from a checker.
+ * @param result The result collected from a specific condition from a checker.
+ */
+ public void addResult(ValidationResult result)
+ {
+ results.add(result);
+ }
+
+ /**
+ * Add a list of {@link ValidationResult} as the result of the Application.
+ * @param result A list of ValidationResult.
+ */
+ public void addResult(List<ValidationResult> result)
+ {
+ results.addAll(result);
+ }
+
+ /**
+ * Gets the results (list of {@link ValidationResult}) for the Application.
+ * @return The list of ValidationResult for the Application.
+ */
+ public List<ValidationResult> getResults()
+ {
+ return results;
+ }
+
+ /**
+ * Adds a status (successful or failure) to the given checker.
+ * @param checkerID The checker id.
+ * @param status Status of the checker.
+ */
+ public void addStatus(String checkerID, IStatus status)
+ {
+ executionStatus.put(checkerID, status);
+ }
+
+ /**
+ * Gets the execution status (the key for the map is the checkerId).
+ * @return map with the execution status for each checkerId.
+ */
+ public Map<String, IStatus> getExecutionStatus()
+ {
+ return executionStatus;
+ }
+
+ /**
+ * Gets the Application name.
+ * @return The Application name.
+ */
+ public String getApplication()
+ {
+ return application;
+ }
+
+ /**
+ * Gets the Application version.
+ * @return The Application version.
+ */
+ public int getVersion()
+ {
+ return version;
+ }
+
+ /**
+ * Gets the Application path.
+ * @return The Application path.
+ */
+ public String getApplicationPath()
+ {
+ return applicationPath;
+ }
+
+ /**
+ * Gets the XML result document.
+ * @return The XML result document.
+ */
+ public Document getXmlResultDocument()
+ {
+ return resultXMLDocument;
+ }
+
+ /**
+ * Sets the XML result document.
+ * @param document The XML result document.
+ */
+ public void setXmlResultDocument(Document document)
+ {
+ this.resultXMLDocument = document;
+ }
+
+}
diff --git a/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/validation/ComplexParameter.java b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/validation/ComplexParameter.java
new file mode 100644
index 0000000..5d37087
--- /dev/null
+++ b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/validation/ComplexParameter.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.preflighting.core.validation;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A complex parameter is a parameter that is composed of a list of {@link Parameter}.
+ */
+public class ComplexParameter extends Parameter
+{
+
+ private List<Parameter> parameters;
+
+ @Override
+ public String toString()
+ {
+ String retValue =
+ "Complex Parameter\n name: " + getParameterType() + "\n value: " + getValue(); //$NON-NLS-1$ //$NON-NLS-2$
+
+ for (Parameter param : parameters)
+ {
+ retValue += "\n " + param.toString(); //$NON-NLS-1$
+ }
+
+ return retValue;
+ }
+
+ /**
+ * Constructs a new ComplexParameter with the given parameters.
+ *
+ * @param parameterType Parameter type.
+ * @param value Parameter value.
+ */
+ public ComplexParameter(String parameterType, String value)
+ {
+ super(parameterType, value);
+ }
+
+ /**
+ * Default constructor.
+ *
+ */
+ public ComplexParameter()
+ {
+ }
+
+ /**
+ * Adds a new parameter to the complex parameter.
+ *
+ * @param key Name of the parameter attribute.
+ * @param value Value for the parameter attribute.
+ */
+ public void addParameter(String key, String value)
+ {
+ if (parameters == null)
+ {
+ parameters = new ArrayList<Parameter>();
+ }
+ parameters.add(new Parameter(key, value));
+ }
+
+ /**
+ * Gets all {@link Parameter} that compose {@link ComplexParameter}.
+ *
+ * @return The list of parameters.
+ */
+ public List<Parameter> getParameters()
+ {
+ return parameters;
+ }
+}
diff --git a/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/validation/GlobalInputParamsValidator.java b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/validation/GlobalInputParamsValidator.java
new file mode 100644
index 0000000..c04227a
--- /dev/null
+++ b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/validation/GlobalInputParamsValidator.java
@@ -0,0 +1,461 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.preflighting.core.validation;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import com.motorolamobility.preflighting.core.checker.CheckerExtension;
+import com.motorolamobility.preflighting.core.checker.condition.ICondition;
+import com.motorolamobility.preflighting.core.devicespecification.DeviceSpecification;
+import com.motorolamobility.preflighting.core.exception.PreflightingExtensionPointException;
+import com.motorolamobility.preflighting.core.i18n.PreflightingCoreNLS;
+import com.motorolamobility.preflighting.core.sdk.SdkUtils;
+import com.motorolamobility.preflighting.core.utils.LimitedList;
+import com.motorolamobility.preflighting.core.validation.ValidationManager.InputParameter;
+import com.motorolamobility.preflighting.core.validation.ValidationManager.WarningLevelAdjustmentType;
+import com.motorolamobility.preflighting.core.validation.ValidationResultData.SEVERITY;
+import com.motorolamobility.preflighting.core.verbose.DebugVerboseOutputter;
+import com.motorolamobility.preflighting.core.verbose.DebugVerboseOutputter.VerboseLevel;
+
+/**
+ * Utility class (static) which validates global parameters passed to App Validator.
+ */
+public final class GlobalInputParamsValidator
+{
+ /**
+ * Constant for the variable that holds the system path.
+ */
+ public static final String PATH_ENVIRONMENT_VARIABLE = "path"; //$NON-NLS-1$
+
+ private static final String WARNING_LEVEL_ADJUSTMENT_CHECKER_SEPARATOR = " "; //$NON-NLS-1$
+
+ private static final String WARNING_LEVEL_ADJUSTMENT_CHECKER_MESSAGE_SEPARATOR = ", "; //$NON-NLS-1$
+
+ /**
+ * Validates all global parameters passed to the application.
+ * Returns the ValidationResult object containing only ValidationResultData
+ * corresponding to errors on given parameters.
+ *
+ * @param params the global parameters passed to App Validator.
+ * @param adjustedWarningLevelInfo The object to hold information about warning level configuration.
+ * @return ValidationResult object containing the validation errors, if any.
+ */
+ public static ValidationResult validateGlobalParams(List<Parameter> params,
+ Map<WarningLevelAdjustmentType, Set<String>> adjustedWarningLevelInfo,
+ List<DeviceSpecification> deviceSpecifications, ValidationManager validationManager)
+ {
+ ValidationResult globalResult = new ValidationResult(null, LimitedList.UNLIMITED);
+ ValidationResultData resultData = null;
+ boolean sdkParamFound = false;
+ boolean appPathParamFound = false;
+
+ for (Parameter param : params)
+ {
+ resultData = new ValidationResultData();
+ String inputParam = param.getParameterType();
+ String inputParamValue = param.getValue();
+
+ if (InputParameter.SDK_PATH.getAlias().equals(inputParam))
+ {
+ validateSdkParam(resultData, inputParamValue);
+ sdkParamFound = true;
+
+ // say that sdk path passed as parameter is being used
+ DebugVerboseOutputter
+ .printVerboseMessage(
+ PreflightingCoreNLS.GlobalInputParamsValidator_VerboseMessage_UsingParamSDKPath,
+ VerboseLevel.v1);
+ }
+ else if (InputParameter.APPLICATION_PATH.getAlias().equals(inputParam))
+ {
+ validateApplicationPathParam(resultData, inputParamValue);
+ appPathParamFound = true;
+
+ }
+ else if (InputParameter.DEVICE_DESCRIPTION.getAlias().equals(inputParam))
+ {
+ validateDescribeDeviceParam(resultData, inputParamValue, deviceSpecifications);
+ }
+ else if (InputParameter.WARNING_TO_ERROR.getAlias().equals(inputParam)
+ || (InputParameter.ERROR_TO_WARNING.getAlias().equals(inputParam)))
+ {
+ validateWarningAdjustmentParameter(resultData, inputParamValue, inputParam,
+ adjustedWarningLevelInfo, validationManager);
+ }
+ else if (InputParameter.LIMIT.getAlias().equals(inputParam))
+ {
+ validateLimitParam(resultData, inputParamValue);
+ }
+ else
+ {
+ // unknown parameter passed
+ resultData.setSeverity(SEVERITY.ERROR);
+ resultData
+ .setIssueDescription(PreflightingCoreNLS.GlobalInputParamsValidator_UnknownParameterMessage
+ + param.getParameterType());
+ }
+
+ if (!resultData.getSeverity().equals(SEVERITY.OK))
+ {
+ globalResult.addValidationResult(resultData);
+ }
+ }
+
+ resultData = new ValidationResultData();
+ if (!appPathParamFound)
+ {
+ resultData.setSeverity(SEVERITY.ERROR);
+ resultData
+ .setIssueDescription(PreflightingCoreNLS.GlobalInputParamsValidator_AppPathParameterMissing);
+ globalResult.addValidationResult(resultData);
+ }
+ else if (!sdkParamFound)
+ {
+ DebugVerboseOutputter
+ .printVerboseMessage(
+ PreflightingCoreNLS.GlobalInputParamsValidator_VerboseMessage_CheckingSystemPathForSDK,
+ VerboseLevel.v2);
+
+ boolean aaptFound = true;
+
+ String[] aaptCommand = new String[]
+ {
+ "aapt", "help" //$NON-NLS-1$ //$NON-NLS-2$
+ };
+
+ // test AAPT command
+ try
+ {
+ Runtime.getRuntime().exec(aaptCommand);
+ }
+ catch (IOException ioException)
+ {
+ //aapt not found
+ aaptFound = false;
+ DebugVerboseOutputter
+ .printVerboseMessage(
+ PreflightingCoreNLS.GlobalInputParamsValidator_VerboseMessage_SDKPathNotFoundOnSystemPath,
+ VerboseLevel.v2);
+ }
+
+ if (!aaptFound)
+ {
+ resultData.setSeverity(SEVERITY.ERROR);
+ resultData
+ .setIssueDescription(PreflightingCoreNLS.GlobalInputParamsValidator_SdkPathParameterMissing);
+ }
+ else
+ {
+ params.add(new Parameter(ValidationManager.InputParameter.SDK_PATH.getAlias(),
+ "aapt")); //$NON-NLS-1$
+
+ // say that system sdk path is being used
+ DebugVerboseOutputter
+ .printVerboseMessage(
+ PreflightingCoreNLS.GlobalInputParamsValidator_VerboseMessage_UsingSystemSDKPath,
+ VerboseLevel.v1);
+
+ }
+ if ((resultData.getSeverity() != null) && !resultData.getSeverity().equals(SEVERITY.OK))
+ {
+ globalResult.addValidationResult(resultData);
+ }
+ }
+
+ return globalResult;
+ }
+
+ private static void validateApplicationPathParam(ValidationResultData resultData,
+ String inputParamValue)
+ {
+ File appPath = new File(inputParamValue);
+
+ if (!appPath.exists())
+ {
+ resultData.setSeverity(SEVERITY.ERROR);
+ resultData
+ .setIssueDescription(PreflightingCoreNLS.GlobalInputParamsValidator_NonExistentApplicationPathMessage
+ + inputParamValue);
+ }
+ else
+ {
+ resultData.setSeverity(SEVERITY.OK);
+ }
+ }
+
+ private static void validateLimitParam(ValidationResultData resultData, String paramValue)
+ {
+ resultData.setSeverity(SEVERITY.OK);
+
+ if (paramValue != null)
+ {
+ try
+ {
+ int limit = Integer.parseInt(paramValue);
+ if (limit >= 0)
+ {
+ return;
+ }
+ }
+ catch (NumberFormatException nfe)
+ {
+ resultData.setSeverity(SEVERITY.ERROR);
+ resultData.setIssueDescription(PreflightingCoreNLS.bind(
+ PreflightingCoreNLS.GlobalInputParamsValidator_LimitParam, paramValue));
+ }
+ }
+
+ resultData.setSeverity(SEVERITY.ERROR);
+ resultData.setIssueDescription(PreflightingCoreNLS.bind(
+ PreflightingCoreNLS.GlobalInputParamsValidator_LimitParam, paramValue));
+ }
+
+ /**
+ * Validates if SDK parameter is valid (if SDK folder exists, is a directory and have the binaries required to run App Validator).
+ * @param resultData result that is filled if there is any problem with SDK parameter.
+ * @param inputParamValue the global input value passed to run App Validator.
+ */
+ public static void validateSdkParam(ValidationResultData resultData, String inputParamValue)
+ {
+ File sdkFolder = new File(inputParamValue);
+
+ if (!sdkFolder.exists())
+ {
+ resultData.setSeverity(SEVERITY.ERROR);
+ resultData
+ .setIssueDescription(PreflightingCoreNLS.GlobalInputParamsValidator_NonExistentSdkPathMessage
+ + inputParamValue);
+ }
+ else if (!sdkFolder.isDirectory())
+ {
+ resultData.setSeverity(SEVERITY.ERROR);
+ resultData
+ .setIssueDescription(PreflightingCoreNLS.GlobalInputParamsValidator_SdkPathNotFolderMessage
+ + inputParamValue);
+ }
+ // check if a particular tool is inside the folder (in this case, aapt), and
+ // if it isn't, assume it is not a valid sdk folder
+ else if (SdkUtils.getLatestAAPTToolPath(inputParamValue) == null)
+ {
+ resultData.setSeverity(SEVERITY.ERROR);
+ resultData
+ .setIssueDescription(PreflightingCoreNLS.GlobalInputParamsValidator_SdkPathNotValidSdkMessage
+ + inputParamValue);
+ }
+ else
+ {
+ resultData.setSeverity(SEVERITY.OK);
+ }
+ }
+
+ private static boolean validateDescribeDeviceParam(ValidationResultData resultData,
+ String describeDeviceParamValue, List<DeviceSpecification> deviceSpecifications)
+ {
+ boolean deviceIdfound = false;
+ if (deviceSpecifications != null)
+ {
+ for (DeviceSpecification spec : deviceSpecifications)
+ {
+ if ((spec.getId() != null) && spec.getId().equals(describeDeviceParamValue))
+ {
+ deviceIdfound = true;
+ break;
+ }
+ }
+ }
+ return deviceIdfound;
+ }
+
+ private static void validateWarningAdjustmentParameter(ValidationResultData resultData,
+ String inputParamValue, String inputParam,
+ Map<WarningLevelAdjustmentType, Set<String>> adjustedWarningLevelInfo,
+ ValidationManager validationManager)
+ {
+ DebugVerboseOutputter
+ .printVerboseMessage(
+ PreflightingCoreNLS.GlobalInputParamsValidator_WarningLevelAdjustmentParameterFoundMessage,
+ VerboseLevel.v1);
+
+ WarningLevelAdjustmentType adjustmentType;
+ if (InputParameter.WARNING_TO_ERROR.getAlias().equals(inputParam))
+ {
+ adjustmentType = WarningLevelAdjustmentType.INCREASE;
+ }
+ else
+ {
+ adjustmentType = WarningLevelAdjustmentType.DECREASE;
+ }
+
+ // trim parameter value for guaranteeing there is something on it other than white spaces
+ if (inputParamValue != null)
+ {
+ inputParamValue = inputParamValue.trim();
+ }
+
+ Set<String> checkerIdsToAdjust = adjustedWarningLevelInfo.get(adjustmentType);
+
+ // if the parameter was not previously passed, create the list of checkers for it
+ if (checkerIdsToAdjust == null)
+ {
+ checkerIdsToAdjust = new HashSet<String>();
+ adjustedWarningLevelInfo.put(adjustmentType, checkerIdsToAdjust);
+ }
+ // if the parameter was previously passed, this is not allowed (return immediately to avoid complicated code)
+ else
+ {
+ resultData.setSeverity(SEVERITY.ERROR);
+ resultData.setIssueDescription(PreflightingCoreNLS.bind(
+ PreflightingCoreNLS.GlobalInputParamsValidator_RepeatedParameterErrorMessage,
+ inputParam));
+ return;
+ }
+
+ // no checker id passed; all checkers will have their warning levels adjusted;
+ // validation result is ok
+ if ((inputParamValue == null) || (inputParamValue.length() == 0))
+ {
+ DebugVerboseOutputter
+ .printVerboseMessage(
+ PreflightingCoreNLS.GlobalInputParamsValidator_WarningLevelAdjustmentAllCheckers,
+ VerboseLevel.v1);
+ resultData.setSeverity(SEVERITY.OK);
+ }
+ else
+ {
+ // retrieve passed checker ids, validate they exist, and add
+ // them to a list of checker ids to be adjusted for warning level
+ String[] completeIds =
+ inputParamValue.split(WARNING_LEVEL_ADJUSTMENT_CHECKER_SEPARATOR);
+ Map<String, CheckerExtension> knownCheckers = null;
+ StringBuilder unknownCheckerIdsMessage = new StringBuilder();
+ StringBuilder knownCheckerIdsMessage = new StringBuilder();
+ boolean unknownCheckerFound = false;
+ try
+ {
+ knownCheckers = ValidationManager.loadCheckers();
+ }
+ catch (PreflightingExtensionPointException e)
+ {
+ // do nothing; the list of checkers will fail to be validated
+ // and a correct message with unknown checkers will be used
+ }
+
+ for (String completeId : completeIds)
+ {
+ completeId = completeId.trim();
+ if (completeId.length() > 0)
+ {
+ //Grab the conditionID if exists, so we can verify it later.
+ String[] checkerCondition = completeId.split("\\.");
+ String checkerId = null;
+ String conditionId = null;
+ switch (checkerCondition.length)
+ {
+ case 0:
+ //Do nothing, checkerId is already correct
+ break;
+ case 1:
+ checkerId = checkerCondition[0];
+ break;
+ case 2:
+ checkerId = checkerCondition[0];
+ conditionId = checkerCondition[1];
+ break;
+ default:
+ unknownCheckerIdsMessage.append(completeId
+ + WARNING_LEVEL_ADJUSTMENT_CHECKER_MESSAGE_SEPARATOR);
+ unknownCheckerFound = true;
+ }
+
+ //Verify if checker is valid
+ if ((knownCheckers == null) || !knownCheckers.containsKey(checkerId))
+ {
+ unknownCheckerIdsMessage.append(completeId
+ + WARNING_LEVEL_ADJUSTMENT_CHECKER_MESSAGE_SEPARATOR);
+ unknownCheckerFound = true;
+ }
+ else
+ {
+ //Checker is valid now verify if the condition is valid
+ boolean conditionValid = true;
+ if (conditionId != null)
+ {
+ Map<String, ICondition> checkerConditions =
+ knownCheckers.get(checkerId).getChecker().getConditions();
+ if (checkerConditions != null)
+ {
+ conditionValid = checkerConditions.containsKey(conditionId);
+ }
+ else
+ {
+ conditionValid = false;
+ }
+ }
+
+ if (conditionValid)
+ {
+ checkerIdsToAdjust.add(completeId);
+ knownCheckerIdsMessage.append(completeId
+ + WARNING_LEVEL_ADJUSTMENT_CHECKER_MESSAGE_SEPARATOR);
+ }
+ else
+ {
+ unknownCheckerIdsMessage.append(completeId
+ + WARNING_LEVEL_ADJUSTMENT_CHECKER_MESSAGE_SEPARATOR);
+ unknownCheckerFound = true;
+ }
+ }
+ }
+ }
+
+ if (unknownCheckerFound)
+ {
+ // remove last comma added
+ unknownCheckerIdsMessage.delete(unknownCheckerIdsMessage
+ .lastIndexOf(WARNING_LEVEL_ADJUSTMENT_CHECKER_MESSAGE_SEPARATOR),
+ unknownCheckerIdsMessage.length());
+ String unknownCheckerIdsMessageStr = unknownCheckerIdsMessage.toString();
+
+ resultData.setSeverity(SEVERITY.ERROR);
+ resultData
+ .setIssueDescription(PreflightingCoreNLS
+ .bind(PreflightingCoreNLS.GlobalInputParamsValidator_UnknownCheckersForWarningLevelAdjustmentMessage,
+ unknownCheckerIdsMessageStr));
+ }
+ else
+ {
+ // remove last comma added
+ knownCheckerIdsMessage.delete(knownCheckerIdsMessage
+ .lastIndexOf(WARNING_LEVEL_ADJUSTMENT_CHECKER_MESSAGE_SEPARATOR),
+ knownCheckerIdsMessage.length());
+ String knownCheckerIdsMessageStr = knownCheckerIdsMessage.toString();
+
+ DebugVerboseOutputter
+ .printVerboseMessage(
+ PreflightingCoreNLS
+ .bind(PreflightingCoreNLS.GlobalInputParamsValidator_WarningLevelAdjustmentFollowingCheckers,
+ knownCheckerIdsMessageStr), VerboseLevel.v1);
+
+ resultData.setSeverity(SEVERITY.OK);
+ }
+ }
+ }
+}
diff --git a/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/validation/Parameter.java b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/validation/Parameter.java
new file mode 100644
index 0000000..fc8279f
--- /dev/null
+++ b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/validation/Parameter.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.preflighting.core.validation;
+
+/**
+ * Represents a parameter that can be received by App Validator. It can be either a global parameter or a specific parameter from a checker.
+ */
+public class Parameter
+{
+ private String parameterType;
+
+ private String value;
+
+ /**
+ * Print a human-readable Text of this {@link Parameter}.
+ *
+ * @return Returns a human-readable Text.
+ *
+ * @see Object#toString()
+ */
+ @Override
+ public String toString()
+ {
+ return "Parameter\n name: " + parameterType + "\n value: " + value; //$NON-NLS-1$ //$NON-NLS-2$
+ }
+
+ /**
+ * Constructs a {@link Parameter} that can be passed to App Validator
+ *
+ * @param parameterType Indicates of of the types of the instantiated {@link Parameter}.
+ * @param value The value to be set into the {@link Parameter}.
+ */
+ public Parameter(String parameterType, String value)
+ {
+ this.parameterType = parameterType;
+ this.value = value;
+ }
+
+ /**
+ * Constructs a {@link Parameter} with no values.
+ */
+ public Parameter()
+ {
+ }
+
+ /**
+ * Gets the parameter type. It holds the following values:
+ * <ui>
+ * <li>{@link ValidationManager#DEVICE_PARAMETER}</li>
+ * <li>{@link ValidationManager#CHECKER_PARAMETER}</li><li></li>
+ * <li>{@link ValidationManager#DISABLE_CHECKER_PARAMETER}</li><li></li>
+ * </ui>
+ *
+ * @return Returns the parameter type.
+ */
+ public String getParameterType()
+ {
+ return parameterType;
+ }
+
+ /**
+ * Gets the value of the parameter
+ *
+ * @return
+ */
+ public String getValue()
+ {
+ return value;
+ }
+
+ /**
+ * Sets parameter type. It holds the following values:
+ * <ui>
+ * <li>{@link ValidationManager#DEVICE_PARAMETER}</li>
+ * <li>{@link ValidationManager#CHECKER_PARAMETER}</li>
+ * <li>{@link ValidationManager#DISABLE_CHECKER_PARAMETER}</li><li></li>
+ * </ui>
+ *
+ * @param parameterType The type of the parameter.
+ */
+ public void setParameterType(String parameterType)
+ {
+ this.parameterType = parameterType;
+ }
+
+ /**
+ * Sets parameter value. This represents the content of the {@link Parameter}.
+ *
+ * @param value Parameter value to be set.
+ */
+ public void setValue(String value)
+ {
+ this.value = value;
+ }
+
+ /**
+ * Verifies if a given object is a {@link Parameter} and whether
+ * it is equal to this instance. In case two {@link Parameter} objects
+ * have the same {@link Parameter#getParameterType()}, they are considered
+ * equal.
+ *
+ * @param obj Object to be compared. Note that it must be an instance
+ * of {@link Parameter}.
+ */
+ @Override
+ public boolean equals(Object obj)
+ {
+ boolean equals = false;
+ if (obj instanceof Parameter)
+ {
+ equals = true;
+ Parameter objParam = (Parameter) obj;
+ String objParamType = objParam.getParameterType();
+
+ if (objParamType == null)
+ {
+ equals = (this.parameterType == null);
+ }
+
+ if (equals && (objParamType != null))
+ {
+ equals = objParamType.equals(this.parameterType);
+ }
+ }
+ return equals;
+ }
+}
diff --git a/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/validation/ParameterDescription.java b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/validation/ParameterDescription.java
new file mode 100644
index 0000000..75b0228
--- /dev/null
+++ b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/validation/ParameterDescription.java
@@ -0,0 +1,208 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.preflighting.core.validation;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This class contains the description for a given parameter.
+ */
+public class ParameterDescription
+{
+ private String name = null;
+
+ private String description = null;
+
+ private String valueDescription = null;
+
+ private boolean valueRequired = false;
+
+ /**
+ * The type of value held by the parameter
+ */
+ private ParameterType Type;
+
+ /**
+ * Default value
+ */
+ private Value defaultValue = null;
+
+ /**
+ * List of values allowed from this parameter
+ */
+ private List<Value> allowedValues = new ArrayList<Value>();
+
+ /**
+ * Gets the list of {@link Value} objects which are allowed.
+ *
+ * @return Returns a {@link List} of values which are allowed as parameters.
+ */
+ public List<Value> getAllowedValues()
+ {
+ return allowedValues;
+ }
+
+ /**
+ * Sets the {@link List} of {@link Value} objects which are allowed.
+ *
+ * @param allowedValues {@link List} of allowed {@link Value} objects to be set.
+ */
+ public void setAllowedValues(List<Value> allowedValues)
+ {
+ this.allowedValues = allowedValues;
+ }
+
+ /**
+ * Gets the description of a Parameter (e.g. used in help)
+ *
+ * @return Return the description of a Parameter.
+ */
+ public String getDescription()
+ {
+ return description;
+ }
+
+ /**
+ * Sets the description of a Parameter.
+ *
+ * @param description The description of a Parameter to be set.
+ */
+ public void setDescription(String description)
+ {
+ this.description = description;
+ }
+
+ /**
+ * Gets the default value of a Parameter
+ *
+ * @return Returns the default value of a Parameter.
+ */
+ public Value getDefaultValue()
+ {
+ return defaultValue;
+ }
+
+ /**
+ * Sets the default value of a Parameter.
+ *
+ * @param defaultValue The default value of a Parameter to be set.
+ */
+ public void setDefaultValue(Value defaultValue)
+ {
+ this.defaultValue = defaultValue;
+ }
+
+ /**
+ * Gets the Parameter object name.
+ *
+ * @return Returns the Parameter object name.
+ */
+ public String getName()
+ {
+ return name;
+ }
+
+ /**
+ * Sets the Parameter object´s name.
+ *
+ * @param name Parameter´s name to be set.
+ */
+ public void setName(String name)
+ {
+ this.name = name;
+ }
+
+ /**
+ * Gets the {@link ParameterType}. This represents the several
+ * object which a {@link ParameterDescription} can have.
+ *
+ * @return Returns the {@link ParameterType} of this instance.
+ */
+ public ParameterType getType()
+ {
+ return Type;
+ }
+
+ /**
+ * Set the {@link ParameterType} of this instance. This represents the several
+ * object which a {@link ParameterDescription} can have.
+ *
+ * @param type The {@link ParameterType} to be set.
+ */
+ public void setType(ParameterType type)
+ {
+ Type = type;
+ }
+
+ /**
+ * Gets a description of the value being assigned to a parameter (used in help)
+ *
+ * @return Returns the value description.
+ */
+ public String getValueDescription()
+ {
+ return valueDescription;
+ }
+
+ /**
+ * Sets the description of the value being assigned to a parameter (used in help).
+ *
+ * @param description The value description to be set.
+ */
+ public void setValueDescription(String description)
+ {
+ this.valueDescription = description;
+ }
+
+ /**
+ * Returns <code>true</code> if this parameter is required,
+ * <code>false</code> otherwise.
+ *
+ * @return <code>true</code> if required, <code>false</code> otherwise
+ */
+ public boolean isValueRequired()
+ {
+ return valueRequired;
+ }
+
+ /**
+ * Sets if the value is required to run the checker or condition.
+ *
+ * @param valueRequired Set <code>true</code> in case this {@link ParameterDescription}
+ * is required, <code>false</code> otherwise.
+ */
+ public void setValueRequired(boolean valueRequired)
+ {
+ this.valueRequired = valueRequired;
+ }
+
+ /**
+ * This implementation provides a human-readable text of this
+ * {@link ParameterDescription}.
+ *
+ * @return Returns a human-readable text of this {@link ParameterDescription}.
+ *
+ * @see Object#toString()
+ */
+ @Override
+ public String toString()
+ {
+ return "ParameterDescription [name=" + name + ", description=" + description //$NON-NLS-1$ //$NON-NLS-2$
+ + ", defaulfValue=" + defaultValue + ", allowedValues=" + allowedValues + "]"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+
+}
diff --git a/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/validation/ParameterType.java b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/validation/ParameterType.java
new file mode 100644
index 0000000..97d8f7e
--- /dev/null
+++ b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/validation/ParameterType.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.motorolamobility.preflighting.core.validation;
+
+/**
+ * Gives some possible parameter types for the App Validator
+ */
+public enum ParameterType
+{
+ /**
+ * The parameter type if a {@link String}.
+ */
+ STRING,
+ /**
+ * The parameter type is a {@link Boolean}.
+ */
+ BOOLEAN,
+ /**
+ * The parameter type is an {@link Integer}.
+ */
+ INTEGER
+}
diff --git a/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/validation/ValidationManager.java b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/validation/ValidationManager.java
new file mode 100644
index 0000000..5c11c63
--- /dev/null
+++ b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/validation/ValidationManager.java
@@ -0,0 +1,1975 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.preflighting.core.validation;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.osgi.util.NLS;
+
+import com.motorolamobility.preflighting.core.PreflightingCorePlugin;
+import com.motorolamobility.preflighting.core.applicationdata.ApplicationData;
+import com.motorolamobility.preflighting.core.applicationdata.XMLElement;
+import com.motorolamobility.preflighting.core.checker.Checker;
+import com.motorolamobility.preflighting.core.checker.CheckerDescription;
+import com.motorolamobility.preflighting.core.checker.CheckerExtension;
+import com.motorolamobility.preflighting.core.checker.IChecker;
+import com.motorolamobility.preflighting.core.checker.condition.CanExecuteConditionStatus;
+import com.motorolamobility.preflighting.core.checker.condition.Condition;
+import com.motorolamobility.preflighting.core.checker.condition.ICondition;
+import com.motorolamobility.preflighting.core.checker.parameter.ICheckerParameter;
+import com.motorolamobility.preflighting.core.devicelayoutspecification.Device;
+import com.motorolamobility.preflighting.core.devicespecification.DeviceSpecification;
+import com.motorolamobility.preflighting.core.devicespecification.DevicesSpecsContainer;
+import com.motorolamobility.preflighting.core.devicespecification.internal.PlatformRules;
+import com.motorolamobility.preflighting.core.exception.PreflightingCheckerException;
+import com.motorolamobility.preflighting.core.exception.PreflightingExtensionPointException;
+import com.motorolamobility.preflighting.core.exception.PreflightingParameterException;
+import com.motorolamobility.preflighting.core.exception.PreflightingToolException;
+import com.motorolamobility.preflighting.core.exception.ValidationLimitException;
+import com.motorolamobility.preflighting.core.i18n.PreflightingCoreNLS;
+import com.motorolamobility.preflighting.core.internal.checker.CheckerExtensionReader;
+import com.motorolamobility.preflighting.core.internal.devicelayoutspecification.LayoutDevicesType;
+import com.motorolamobility.preflighting.core.internal.utils.AaptUtils;
+import com.motorolamobility.preflighting.core.internal.utils.ApkUtils;
+import com.motorolamobility.preflighting.core.internal.utils.ProjectUtils;
+import com.motorolamobility.preflighting.core.logging.PreflightingLogger;
+import com.motorolamobility.preflighting.core.utils.LimitedList;
+import com.motorolamobility.preflighting.core.utils.XmlUtils;
+import com.motorolamobility.preflighting.core.validation.ValidationResultData.SEVERITY;
+import com.motorolamobility.preflighting.core.verbose.DebugVerboseOutputter;
+import com.motorolamobility.preflighting.core.verbose.DebugVerboseOutputter.VerboseLevel;
+import com.motorolamobility.preflighting.core.verbose.WarningLevelFilter;
+
+/**
+ * This class is responsible for accessing App Validator framework components (checkers,
+ * device specifications, etc).
+ */
+public class ValidationManager
+{
+ /**
+ * Keeps properties that control App Validator (such as the base URL for the checker/condition help).
+ */
+ private ValidationManagerConfiguration valManagerConfig = null;
+
+ /**
+ * Map of checkers, ordered by checker id (command line value used to call the checker).
+ */
+ private final static TreeMap<String, CheckerExtension> checkers =
+ new TreeMap<String, CheckerExtension>();
+
+ /**
+ * Maps of global parameter descriptions (the key is {@link ParameterDescription#getName()}).
+ */
+ private final Map<String, ParameterDescription> globalParametersDescriptions =
+ new LinkedHashMap<String, ParameterDescription>();
+
+ /**
+ * List of {@link Parameter} (the global parameters for App Validator).
+ */
+ private List<Parameter> globalParams;
+
+ /**
+ * Parameter to specify a checker.
+ */
+ public static final String CHECKER_PARAMETER = "c"; //$NON-NLS-1$
+
+ /**
+ * Parameter to disable a checker.
+ */
+ public static final String DISABLE_CHECKER_PARAMETER = "dc"; //$NON-NLS-1$
+
+ /**
+ * Parameter to specify a device.
+ */
+ public static final String DEVICE_PARAMETER = "d"; //$NON-NLS-1$
+
+ private static final String DEVICE_PARAMETER_NONE_VALUE = "none"; //$NON-NLS-1$
+
+ public static final String APP_VALIDATOR_RESULT_ID = "appValidatorResult"; //$NON-NLS-1$
+
+ // Flag used to decided wether or not we will delete the temp folder used to
+ // extract APK packages.
+ // Only set to false when the user passes a "hidden" "-keepTempFiles"
+ // parameter
+ private boolean deleteApkTempFolder = true;
+
+ private final ArrayList<String> tempResourcesToDelete;
+
+ //Flag used to determine whether device verifications will be made or not
+ // if 'true' then user specified the "-d none" parameter and no device verification will be executed.
+ private boolean noneDeviceSpecified = false;
+
+ /**
+ * Container {@link DevicesSpecsContainer} responsible to keep the list of {@link DeviceSpecification}.
+ */
+ private final DevicesSpecsContainer devicesSpecsContainer;
+
+ /**
+ * Input parameters for the App Validator.
+ */
+ public static enum InputParameter
+ {
+
+ SDK_PATH("sdk"), //$NON-NLS-1$
+
+ // app path: is omitted in command line
+ APPLICATION_PATH("input"), //$NON-NLS-1$
+
+ WARNING_TO_ERROR("wx"), //$NON-NLS-1$
+
+ ERROR_TO_WARNING("xw"), //$NON-NLS-1$
+
+ DEVICE_DESCRIPTION("describe-device"), //$NON-NLS-1$
+
+ OUTPUT("output"), //$NON-NLS-1$
+
+ LIMIT("limit"); //$NON-NLS-1$
+
+ private String alias;
+
+ private InputParameter(String id)
+ {
+ this.alias = id;
+ }
+
+ /**
+ * Gets the alias.
+ *
+ * @return Return the alias.
+ */
+ public String getAlias()
+ {
+ return alias;
+ }
+
+ /**
+ * Verifies whether a certain value is recognized as a valid
+ * alias for a {@link InputParameter}.
+ *
+ * @param alias Alias to which the comparation will be made.
+ *
+ * @return True if the value of alias is recognized as a valid
+ * {@link InputParameter}. Return false if alias is null.
+ */
+ public static boolean contains(String alias)
+ {
+
+ boolean contains = false;
+
+ if (alias != null)
+ {
+ for (InputParameter input : InputParameter.values())
+ {
+ if (input.getAlias().equals(alias))
+ {
+ contains = true;
+ break;
+ }
+ }
+ }
+
+ return contains;
+ }
+ }
+
+ /**
+ * Constructor which instantiates the {@link ValidationManager} with
+ * default parameters.
+ */
+ public ValidationManager()
+ {
+ tempResourcesToDelete = new ArrayList<String>();
+
+ devicesSpecsContainer = DevicesSpecsContainer.getInstance();
+ if (devicesSpecsContainer.getDeviceSpecifications().isEmpty())
+ {
+ loadDeviceSpecifications();
+ }
+ }
+
+ /**
+ * Load Device specifications.
+ */
+ private void loadDeviceSpecifications()
+ {
+ LayoutDevicesType layoutDevicesType = XmlUtils.parseDevicesXmlFiles();
+
+ for (Device deviceInfo : layoutDevicesType.getDevices())
+ {
+ DeviceSpecification devSpec =
+ new DeviceSpecification(PlatformRules.API_LEVEL_3, deviceInfo);
+ devicesSpecsContainer.addDeviceSpecification(devSpec);
+ }
+ }
+
+ /**
+ * Load existing checkers, mapped by checker id (value used to call the
+ * checker on command line). The map is sorted alphabetically by checker id.
+ *
+ * @return Checkers map.
+ */
+ public static Map<String, CheckerExtension> loadCheckers()
+ throws PreflightingExtensionPointException
+ {
+ if (checkers.isEmpty())
+ {
+ CheckerExtensionReader.loadCheckers(checkers);
+ }
+ return checkers;
+ }
+
+ /**
+ * Filter the device specifications based on the parameters.
+ * @param deviceParams Parameters associated with devices.
+ * @return List of {@link DeviceSpecification}.
+ */
+ public List<DeviceSpecification> filterDeviceSpecifications(List<Parameter> deviceParams)
+ {
+ List<DeviceSpecification> filteredListDeviceSpecifications =
+ new ArrayList<DeviceSpecification>();
+
+ List<DeviceSpecification> deviceSpecifications =
+ devicesSpecsContainer.getDeviceSpecifications();
+ if (deviceParams.size() > 0)
+ {
+ //some devices specified => only fill specifications for the devices that match the id
+ for (Parameter param : deviceParams)
+ {
+ String deviceId = param.getValue();
+ //TODO Check how to get apiLevel from Device
+ if (deviceSpecifications != null)
+ {
+ for (DeviceSpecification deviceSpecification : deviceSpecifications)
+ {
+ if (deviceSpecification.getId().equalsIgnoreCase(deviceId))
+ {
+ //found id
+ DeviceSpecification devSpec =
+ new DeviceSpecification(PlatformRules.API_LEVEL_1,
+ deviceSpecification.getDeviceInfo());
+ filteredListDeviceSpecifications.add(devSpec);
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ //no device specified => use all devices
+ filteredListDeviceSpecifications.addAll(deviceSpecifications);
+ }
+
+ return filteredListDeviceSpecifications;
+ }
+
+ /**
+ * Returns the checker based on extension points declared.
+ * @param checkerId
+ * @return
+ */
+ public static CheckerExtension getCheckerExtension(String checkerId)
+ {
+ CheckerExtension result = null;
+ try
+ {
+ loadCheckers();
+ result = checkers.get(checkerId);
+ }
+ catch (PreflightingExtensionPointException e)
+ {
+ PreflightingLogger.debug("Unable to read checker extension " + checkerId);
+ }
+ return result;
+ }
+
+ /**
+ * Validate if all devices passed as parameters exists.
+ *
+ * @param deviceParams devices passed as parameters
+ * @return
+ */
+ private List<ValidationResult> validateDeviceParams(List<Parameter> deviceParams)
+ {
+ List<ValidationResult> resultsList = new ArrayList<ValidationResult>();
+
+ for (Parameter param : deviceParams)
+ {
+ boolean deviceExists = false;
+ String deviceId = param.getValue();
+ if (deviceId == null)
+ {
+ ValidationResult globalResult = new ValidationResult(null, LimitedList.UNLIMITED);
+ ValidationResultData resultData = new ValidationResultData();
+ resultData.setSeverity(SEVERITY.ERROR);
+ resultData.setIssueDescription(PreflightingCoreNLS.bind(
+ PreflightingCoreNLS.ValidationManager_IncorrectSyntax,
+ param.getParameterType()));
+ globalResult.addValidationResult(resultData);
+ resultsList.add(globalResult);
+ }
+ else
+ {
+ for (DeviceSpecification currentDevice : devicesSpecsContainer
+ .getDeviceSpecifications())
+ {
+ if (currentDevice.getId().equals(deviceId))
+ {
+ deviceExists = true;
+ break;
+ }
+ }
+ if (!deviceExists)
+ {
+ if (!param.getValue().equals(DEVICE_PARAMETER_NONE_VALUE))
+ {
+ ValidationResult globalResult =
+ new ValidationResult(null, LimitedList.UNLIMITED);
+ ValidationResultData resultData = new ValidationResultData();
+ resultData.setSeverity(SEVERITY.ERROR);
+ resultData.setIssueDescription(PreflightingCoreNLS.bind(
+ PreflightingCoreNLS.ValidationManager_UnknownDeviceMessage,
+ " '" + deviceId + "'")); //$NON-NLS-1$ //$NON-NLS-2$
+ globalResult.addValidationResult(resultData);
+ resultsList.add(globalResult);
+ }
+ else
+ {
+ noneDeviceSpecified = true;
+ }
+ }
+ }
+ }
+
+ return resultsList;
+ }
+
+ /**
+ * Validate two things: 1- all checkers passed are known on the framework,
+ * and 2- all parameters passed for the checkers are valid
+ *
+ * @param params
+ * The checkers parameters
+ * @param knownCheckers
+ * The map of known checkers
+ *
+ * @return A list of ValidationResult. Each ValidationResult of its checker.
+ * Return an empty list if no problems are found.
+ */
+ private List<ValidationResult> validateCheckerParams(List<Parameter> params,
+ Map<String, CheckerExtension> knownCheckers)
+ {
+ List<ValidationResult> resultsList = new ArrayList<ValidationResult>();
+
+ //clear the parameter list inside each Checker
+ initializeParams(knownCheckers);
+
+ if ((knownCheckers != null) && !knownCheckers.isEmpty())
+ {
+ if (!params.isEmpty())
+ {
+ for (Parameter param : params)
+ {
+ String checkerId = param.getValue();
+ ValidationResultData resultData = new ValidationResultData();
+ if (checkerId == null)
+ {
+ resultData.setSeverity(SEVERITY.ERROR);
+ resultData.setIssueDescription(NLS.bind(
+ PreflightingCoreNLS.ValidationManager_IncorrectSyntax,
+ param.getParameterType()));
+ }
+ else if (!knownCheckers.keySet().contains(checkerId))
+ {
+ resultData.setSeverity(SEVERITY.ERROR);
+ resultData.setIssueDescription(NLS.bind(
+ PreflightingCoreNLS.ValidationManager_UnknownCheckerMessage,
+ checkerId));
+ }
+ else
+ {
+ List<Parameter> subParameters = null;
+ IChecker checker = knownCheckers.get(checkerId).getChecker();
+
+ if (param instanceof ComplexParameter)
+ {
+ ComplexParameter complexParam = (ComplexParameter) param;
+ subParameters = complexParam.getParameters();
+ }
+ else
+ {
+ subParameters = new ArrayList<Parameter>();
+ }
+ IStatus validationStatus = setParameters(checker, subParameters);
+ if (validationStatus.isOK())
+ {
+ resultData.setSeverity(SEVERITY.OK);
+ validationStatus = checker.validateInputParams(subParameters);
+ if (validationStatus.isOK())
+ {
+ resultData.setSeverity(SEVERITY.OK);
+ }
+ else
+ {
+ if (validationStatus.getSeverity() == Status.INFO)
+ {
+ resultData.setSeverity(SEVERITY.WARNING);
+ }
+ else
+ {
+ resultData.setSeverity(SEVERITY.ERROR);
+ }
+
+ resultData.setIssueDescription(validationStatus.getMessage());
+ }
+ }
+ else
+ {
+ resultData.setSeverity(SEVERITY.ERROR);
+ resultData.setIssueDescription(validationStatus.getMessage());
+ }
+
+ }
+
+ ValidationResult result =
+ new ValidationResult(checkerId, LimitedList.UNLIMITED);
+ if (!resultData.getSeverity().equals(SEVERITY.OK))
+ {
+ result.addValidationResult(resultData);
+ resultsList.add(result);
+ }
+ }
+ }
+ else
+ //Validate the empty list, because some checkers can have mandatory parameters
+ {
+ validateMandatoryParams(params, knownCheckers, resultsList);
+ }
+ }
+
+ return resultsList;
+ }
+
+ private void validateMandatoryParams(List<Parameter> params,
+ Map<String, CheckerExtension> knownCheckers, List<ValidationResult> resultsList)
+ {
+ for (CheckerExtension checkerExtension : knownCheckers.values())
+ {
+ IChecker checker = checkerExtension.getChecker();
+
+ // validate only for those that are enabled
+ if (checker.isEnabled())
+ {
+ IStatus validationStatus = checker.validateInputParams(params);
+ ValidationResultData resultData = new ValidationResultData();
+ if (validationStatus.isOK())
+ {
+ resultData.setSeverity(SEVERITY.OK);
+ }
+ else
+ {
+ if (validationStatus.getSeverity() == Status.INFO)
+ {
+ resultData.setSeverity(SEVERITY.WARNING);
+ }
+ else
+ {
+ resultData.setSeverity(SEVERITY.ERROR);
+ }
+ resultData.setIssueDescription(validationStatus.getMessage());
+ }
+ ValidationResult result =
+ new ValidationResult(checker.getId(), LimitedList.UNLIMITED);
+ if (!resultData.getSeverity().equals(SEVERITY.OK))
+ {
+ result.addValidationResult(resultData);
+ resultsList.add(result);
+ }
+ }
+ }
+ }
+
+ /**
+ * Initialize the list of parameters of each checker.
+ * @param knownCheckers
+ */
+ private void initializeParams(Map<String, CheckerExtension> knownCheckers)
+ {
+ for (CheckerExtension checkerExtension : knownCheckers.values())
+ {
+ IChecker checker = checkerExtension.getChecker();
+
+ Map<String, ICheckerParameter> parameters = checker.getParameters();
+ if (parameters != null)
+ {
+ for (ICheckerParameter checkerParam : parameters.values())
+ {
+ ParameterType type = checkerParam.getType();
+ switch (type)
+ {
+ case BOOLEAN:
+ checkerParam.setBooleanValue(null);
+ break;
+ case STRING:
+ checkerParam.setValue(null);
+ break;
+ case INTEGER:
+ checkerParam.setIntValue(null);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * All parameters inputed by a user (stored in the {@link List} of {@link Parameter} objects)
+ * are set into the {@link IChecker} map of {@link ICheckerParameter} objects.
+ *
+ * @param checker {@link IChecker} where the parameter values will be set.
+ * @param params The {@link List} of {@link Parameter} which values will be set
+ * into a {@link IChecker}.
+ */
+ private IStatus setParameters(IChecker checker, List<Parameter> params)
+ {
+ IStatus status = Status.OK_STATUS;
+
+ // set values entered by the user into the Checker Parameters
+ if ((params != null) && (params.size() > 0))
+ {
+ Map<String, ICheckerParameter> checkerParameters = checker.getParameters();
+
+ ICheckerParameter checkerParameter = null;
+ for (Parameter enteredParameter : params)
+ {
+ if ((checkerParameters != null) && (checkerParameters.size() > 0))
+ {
+ checkerParameter = checkerParameters.get(enteredParameter.getParameterType());
+ if (checkerParameter != null)
+ {
+ String value = enteredParameter.getValue();
+ ParameterType type = checkerParameter.getType();
+ if (type == ParameterType.INTEGER)
+ {
+ try
+ {
+ int intValue = Integer.parseInt(value);
+ checkerParameter.setIntValue(intValue);
+ }
+ catch (NumberFormatException e)
+ {
+ return new Status(
+ IStatus.ERROR,
+ PreflightingCorePlugin.PLUGIN_ID,
+ NLS.bind(
+ PreflightingCoreNLS.ValidationManager_InvalidParamType_Int,
+ value, checkerParameter.getId()));
+ }
+
+ }
+ else if (type == ParameterType.BOOLEAN)
+ {
+ if (isBoolean(value))
+ {
+ boolean booleanValue = Boolean.parseBoolean(value);
+ checkerParameter.setBooleanValue(booleanValue);
+ }
+ else
+ {
+ return new Status(
+ IStatus.ERROR,
+ PreflightingCorePlugin.PLUGIN_ID,
+ NLS.bind(
+ PreflightingCoreNLS.ValidationManager_InvalidParamType_Bool,
+ value, checkerParameter.getId()));
+ }
+ }
+
+ checkerParameter.setValue(value);
+ }
+ }
+ }
+ }
+ return status;
+ }
+
+ private boolean isBoolean(String value)
+ {
+ return Boolean.TRUE.toString().equalsIgnoreCase(value)
+ || Boolean.FALSE.toString().equalsIgnoreCase(value);
+ }
+
+ private ValidationResult validateGlobalParams(List<Parameter> params,
+ Map<WarningLevelAdjustmentType, Set<String>> adjustedWarningLevelInfo,
+ List<DeviceSpecification> deviceSpecifications)
+ {
+ return GlobalInputParamsValidator.validateGlobalParams(params, adjustedWarningLevelInfo,
+ deviceSpecifications, this);
+
+ }
+
+ /**
+ * Delegates all parameters validation and sets global parameters during the process
+ *
+ * @param knownCheckers checkers available
+ * @param commandLineParams parsed command line parameters
+ * @param checkerParams -c parameter
+ * @param disabledCheckers -dc parameter (represents checkers that will not run)
+ * @param deviceParams -d parameter
+ * @param adjustedWarningLevelInfo warning level adjustments -wx -xw
+ * @return List of found issues. Should be empty it everything goes fine.
+ */
+ private List<ValidationResult> startParamsValidation(
+ Map<String, CheckerExtension> knownCheckers, List<Parameter> commandLineParams,
+ List<Parameter> checkerParams, List<Parameter> disabledCheckers,
+ List<Parameter> deviceParams,
+ Map<WarningLevelAdjustmentType, Set<String>> adjustedWarningLevelInfo)
+ {
+ List<Parameter> globalParams = new ArrayList<Parameter>();
+ List<Parameter> errorParams = new ArrayList<Parameter>();
+
+ for (Parameter param : commandLineParams)
+ {
+ if (InputParameter.contains(param.getParameterType()))
+ {
+ globalParams.add(param);
+ }
+ else if (CHECKER_PARAMETER.equals(param.getParameterType()))
+ {
+ checkerParams.add(param);
+ }
+ else if (DISABLE_CHECKER_PARAMETER.equals(param.getParameterType()))
+ {
+ disabledCheckers.add(param);
+ }
+ else if (DEVICE_PARAMETER.equals(param.getParameterType()))
+ {
+ deviceParams.add(param);
+ }
+ else
+ {
+ errorParams.add(param);
+ }
+ }
+
+ setGlobalParameters(globalParams);
+
+ for (CheckerExtension checkerExtension : knownCheckers.values())
+ {
+ if ((checkerExtension != null) && (checkerExtension.getChecker() instanceof Checker))
+ {
+ Checker checker = (Checker) checkerExtension.getChecker();
+ checker.setGlobalParams(globalParams);
+ }
+
+ }
+
+ List<ValidationResult> mergedResultList = new ArrayList<ValidationResult>();
+
+ DebugVerboseOutputter.printVerboseMessage("", VerboseLevel.v0); //$NON-NLS-1$
+
+ DebugVerboseOutputter.printVerboseMessage(
+ PreflightingCoreNLS.ValidationManager_VerboseMessage_ValidatingGlobalParameters,
+ VerboseLevel.v2);
+ // Map<WarningLevelAdjustmentType, Set<String>> adjustedWarningLevelInfo =
+ // new LinkedHashMap<ValidationManager.WarningLevelAdjustmentType, Set<String>>(2);
+ ValidationResult globalValidationResult =
+ validateGlobalParams(globalParams, adjustedWarningLevelInfo,
+ devicesSpecsContainer.getDeviceSpecifications());
+ List<ValidationResult> deviceValidationResultList = validateDeviceParams(deviceParams);
+
+ if (errorParams.size() > 0)
+ {
+ DebugVerboseOutputter.printVerboseMessage(
+ PreflightingCoreNLS.ValidationManager_VerboseMessage_UnknownParametersFound,
+ VerboseLevel.v2);
+ mergedResultList.add(getValidationResultFromErrorParams(errorParams));
+ }
+ if (!globalValidationResult.getValidationResult().isEmpty())
+ {
+ DebugVerboseOutputter.printVerboseMessage(
+ PreflightingCoreNLS.ValidationManager_VerboseMessage_ProblemsGlobalParameters,
+ VerboseLevel.v2);
+ mergedResultList.add(globalValidationResult);
+ }
+ if (!deviceValidationResultList.isEmpty())
+ {
+ mergedResultList.addAll(deviceValidationResultList);
+ }
+
+ return mergedResultList;
+ }
+
+ /***
+ * Run the validation tool itself. The input parameters passed are first
+ * validated and if they are valid, the checkers are run. If they are not
+ * valid, the validation problems are printed.
+ *
+ * @param commandLineParams
+ * The input parameters from command line.
+ *
+ * @return The validation result from the checkers.
+ *
+ * @throws PreflightingParameterException
+ * In case of problems with input parameters.
+ * @throws PreflightingToolException
+ * In case of any other problems (not related to input
+ * parameters).
+ */
+ public synchronized List<ApplicationValidationResult> run(List<Parameter> commandLineParams)
+ throws PreflightingParameterException, PreflightingToolException
+ {
+ List<Parameter> checkerParams = new ArrayList<Parameter>();
+ List<Parameter> disabledCheckers = new ArrayList<Parameter>();
+ List<Parameter> deviceParams = new ArrayList<Parameter>();
+ List<ApplicationValidationResult> results = new ArrayList<ApplicationValidationResult>();
+
+ Map<String, CheckerExtension> knownCheckers = loadCheckers();
+
+ //validate parameters
+ Map<WarningLevelAdjustmentType, Set<String>> adjustedWarningLevelInfo =
+ new LinkedHashMap<ValidationManager.WarningLevelAdjustmentType, Set<String>>(2);
+ List<ValidationResult> mergedResultList =
+ startParamsValidation(knownCheckers, commandLineParams, checkerParams,
+ disabledCheckers, deviceParams, adjustedWarningLevelInfo);
+
+ //no problems regarding the parameters
+ if (mergedResultList.isEmpty())
+ {
+ //by default all checkers and conditions are enabled
+ setAllCheckersConditionsAsEnabled(knownCheckers);
+
+ // Retrieve the list of disabled checkers and disabled conditions
+ // Implementing the -dc switch behavior
+ // If no checker was selected, populate the list with all known checkers
+ disableCheckersConditions(disabledCheckers, knownCheckers);
+
+ List<ValidationResult> checkerValidationResultList =
+ validateCheckerParams(checkerParams, knownCheckers);
+
+ List<String> invalidParamsCheckers =
+ new ArrayList<String>(checkerValidationResultList.size());
+ if (!checkerValidationResultList.isEmpty())
+ {
+ for (ValidationResult checkerValidationResult : checkerValidationResultList)
+ {
+ invalidParamsCheckers.add(checkerValidationResult.getCheckerId());
+ }
+ DebugVerboseOutputter
+ .printVerboseMessage(
+ PreflightingCoreNLS.ValidationManager_VerboseMessage_ProblemsCheckerParameters,
+ VerboseLevel.v2);
+ DebugVerboseOutputter.printVerboseMessage("", VerboseLevel.v2); //$NON-NLS-1$
+ printParameterErrors(checkerValidationResultList);
+ //new line before results
+ DebugVerboseOutputter.printVerboseMessage("", VerboseLevel.v0);
+ }
+
+ enableCheckers(checkerParams, knownCheckers, invalidParamsCheckers);
+
+ if (checkerParams.isEmpty()
+ || (!checkerParams.isEmpty() && (enabledCheckers(knownCheckers) > 0)))
+ {
+ //list of apks or single project
+ ArrayList<String> applications = getResourcesToValidate();
+
+ valManagerConfig = ValidationManagerConfiguration.getInstance();
+
+ //for each application
+
+ if ((applications != null) && (applications.size() > 0))
+ {
+ for (String currentApplication : applications)
+ {
+ boolean isApk = currentApplication.endsWith(".apk"); //$NON-NLS-1$
+ Parameter appParam =
+ new Parameter(
+ ValidationManager.InputParameter.APPLICATION_PATH
+ .getAlias(),
+ currentApplication);
+ //add path to the current app
+ globalParams.add(appParam);
+
+ ApplicationValidationResult applicationValidationResult =
+ validateApplication(knownCheckers, deviceParams, isApk,
+ adjustedWarningLevelInfo, invalidParamsCheckers);
+
+ results.add(applicationValidationResult);
+
+ //remove path for next iteration
+ globalParams.remove(appParam);
+ }
+ }
+ else
+ {
+ PreflightingToolException e =
+ new PreflightingToolException("No application(s) found");
+
+ throw e;
+ }
+
+ if (deleteApkTempFolder)
+ {
+ deleteTempResources();
+ }
+ }
+ }
+ else
+ {
+ // print empty line to separate the messages for parameter errors
+ DebugVerboseOutputter.printVerboseMessage("", VerboseLevel.v2); //$NON-NLS-1$
+ if (!mergedResultList.isEmpty())
+ {
+ printParameterErrors(mergedResultList);
+ }
+ throw new PreflightingParameterException(
+ PreflightingCoreNLS.ValidationManager_InputParametersProblemMessage);
+ }
+ return results;
+ }
+
+ /**
+ * Enable checkers according to -c parameters (-dc has higher precedence)
+ * @param checkerParams
+ * @param knownCheckers
+ * @param invalidParamsCheckers
+ */
+ private void enableCheckers(List<Parameter> checkerParams,
+ Map<String, CheckerExtension> knownCheckers, List<String> invalidParamsCheckers)
+ {
+
+ Set<String> enabled = new HashSet<String>();
+
+ for (Parameter checkerParam : checkerParams)
+ {
+ enabled.add(checkerParam.getValue());
+ }
+
+ //implementing -c behavior
+ //case 2: (identify items with -c) if there is parameter, all checkers will be disabled (except the ones that are marked with -c)
+ for (CheckerExtension checkerExt : knownCheckers.values())
+ {
+ IChecker checker = checkerExt.getChecker();
+
+ // -c and contains or invalid
+ if (((enabled.size() > 0) && (!enabled.contains(checker.getId())))
+ || invalidParamsCheckers.contains(checker.getId()))
+ {
+ checker.setEnabled(false);
+ }
+
+ }
+
+ }
+
+ /**
+ * @param checkersToRun
+ * @return
+ */
+ public List<CheckerExtension> findCheckersToRun(Collection<CheckerExtension> knownCheckers)
+ {
+ List<CheckerExtension> checkersToRun = new ArrayList<CheckerExtension>();
+ for (CheckerExtension checkerExt : knownCheckers)
+ {
+ if (checkerExt.getChecker().isEnabled())
+ {
+ checkersToRun.add(checkerExt);
+ }
+ }
+ return checkersToRun;
+ }
+
+ private ICondition extractCondition(Parameter disabledChecker,
+ Map<String, CheckerExtension> knownCheckers)
+ {
+
+ ICondition condition = null;
+ String str = disabledChecker.getValue();
+ int pos = str.indexOf('.', 0);
+
+ if (pos > 0)
+ {
+ String conditionStr = str.substring(pos + 1);
+ String checkerStr = str.substring(0, pos);
+
+ CheckerExtension checker = knownCheckers.get(checkerStr);
+
+ if (checker != null)
+ {
+ Map<String, ICondition> conditionsMap = checker.getChecker().getConditions();
+
+ Boolean runCheckerExecution = false;
+ Collection<ICondition> conditions = conditionsMap.values();
+ if (conditions != null)
+ {
+ for (ICondition c : conditions)
+ {
+ if (c.getId().equals(conditionStr))
+ {
+ //sets if condition with the given id is disabled
+ condition = c;
+ condition.setEnabled(false);
+ }
+ //sets if checker should run (because at least one condition is enabled)
+ runCheckerExecution |= c.isEnabled();
+ }
+ }
+
+ checker.getChecker().setEnabled(runCheckerExecution);
+ }
+ }
+
+ return condition;
+ }
+
+ /**
+ * WARNING: This method is required because the algorithm relies that all checkers/conditions are enabled
+ * before applying -dc (disable checker or condition) and -c (enable checker).
+ *
+ * Otherwise CheckerExtension objects, which are instantiate only once (on plug-in loading) will be with enabled in an inconsistent state.
+ */
+ private void setAllCheckersConditionsAsEnabled(Map<String, CheckerExtension> knownCheckers)
+ {
+ if ((knownCheckers != null) && !knownCheckers.isEmpty())
+ {
+ for (CheckerExtension chkExt : knownCheckers.values())
+ {
+ chkExt.getChecker().setEnabled(true);
+
+ Map<String, ICondition> conditionsMap = chkExt.getChecker().getConditions();
+ Collection<ICondition> conditions = conditionsMap.values();
+ if (conditions != null)
+ {
+ for (ICondition c : conditions)
+ {
+ c.setEnabled(true);
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Disable checkers that apply. Retrieve disabledCheckers and disabledConditions
+ */
+ private void disableCheckersConditions(List<Parameter> disabledCheckers,
+ Map<String, CheckerExtension> knownCheckers)
+ {
+
+ //try to remove the checker described in disabledChecker
+ for (Parameter disabledChecker : disabledCheckers)
+ {
+ ICondition condition = extractCondition(disabledChecker, knownCheckers);
+
+ // if it is not a condition it might be a checker
+ if (condition == null)
+ {
+ CheckerExtension checkerExtension = knownCheckers.get(disabledChecker.getValue());
+ if (checkerExtension != null)
+ {
+ //implementing -dc for checker
+ checkerExtension.getChecker().setEnabled(false);
+ }
+ // at this point, if neither a condition nor a checker were retrieved the parameter is incorrect
+ else
+ {
+
+ ValidationResultData resultData = new ValidationResultData();
+ resultData.setSeverity(SEVERITY.ERROR);
+ resultData.setIssueDescription(NLS.bind(
+ PreflightingCoreNLS.ValidationManager_UnknownCheckerOrConditionMessage,
+ disabledChecker.getValue()));
+
+ ValidationResult result = new ValidationResult(null, LimitedList.UNLIMITED);
+ result.addValidationResult(resultData);
+ ArrayList<ValidationResult> results = new ArrayList<ValidationResult>();
+ results.add(result);
+ printParameterErrors(results);
+ }
+ }
+ }
+ }
+
+ /*
+ * Extract zip file if any or simply returns the apk or project
+ */
+ private ArrayList<String> getResourcesToValidate() throws PreflightingToolException
+ {
+ String path = null;
+ Parameter pathParam = null;
+ for (Parameter param : globalParams)
+ {
+ //keep application path for later evaluation
+ if (param.getParameterType().equals(InputParameter.APPLICATION_PATH.getAlias()))
+ {
+ path = param.getValue();
+ pathParam = param;
+ }
+ }
+ globalParams.remove(pathParam);
+
+ ArrayList<String> applications = new ArrayList<String>();
+ //path to a .zip file with .apks inside it
+ if (path.endsWith(ApkUtils.ZIP_EXTENSION))
+ {
+ //extract zip to temp folder and store its apks paths
+ File zipFolder = ApkUtils.unzip(new File(path));
+ tempResourcesToDelete.add(zipFolder.getAbsolutePath());
+
+ File[] apks = zipFolder.listFiles();
+ for (int i = 0; i < apks.length; i++)
+ {
+ applications.add(apks[i].getAbsolutePath());
+ }
+ DebugVerboseOutputter.setCurrentVerboseLevel(VerboseLevel.v0);
+ }
+ else
+ //project or .apk
+ {
+ applications.add(path);
+ }
+
+ return applications;
+ }
+
+ /**
+ * Applies a change in the level for the list of {@link ValidationResult} provided.
+ * @param knownCheckers Map of checkers.
+ * @param adjustedWarningLevelInfo The map of levels to adjust.
+ * @param validationResult The results to change the levels.
+ */
+ private void applyWarningLevelAdjustment(Map<String, CheckerExtension> knownCheckers,
+ Map<WarningLevelAdjustmentType, Set<String>> adjustedWarningLevelInfo,
+ List<ValidationResult> validationResult)
+ {
+ Map<String, List<String>> exceptionsMap = null;
+ Map<WarningLevelAdjustmentType, List<String>> checkersIdsMap =
+ new HashMap<WarningLevelAdjustmentType, List<String>>(2);
+ Map<WarningLevelAdjustmentType, Map<String, List<String>>> conditionsIdsMap =
+ new HashMap<WarningLevelAdjustmentType, Map<String, List<String>>>(2);
+
+ // Extract what will be adjusted, whole checkers and separated
+ // conditions
+ for (WarningLevelAdjustmentType type : adjustedWarningLevelInfo.keySet())
+ {
+ Set<String> checkersSet = adjustedWarningLevelInfo.get(type);
+ List<String> checkerIdsToAdjust = null;
+ Map<String, List<String>> conditionIdsToAdjust = null;
+
+ if ((checkersSet != null) && !checkersSet.isEmpty())
+ {
+ checkerIdsToAdjust = new ArrayList<String>(checkersSet.size());
+ conditionIdsToAdjust = new HashMap<String, List<String>>(checkersSet.size());
+ if (exceptionsMap == null)
+ {
+ exceptionsMap = new HashMap<String, List<String>>(2 * checkersSet.size());
+ }
+
+ for (String completeId : checkersSet)
+ {
+ String[] split = completeId.split("\\."); //$NON-NLS-1$
+ if (split.length > 1)
+ {
+ String checkerId = split[0];
+ String conditionId = split[1];
+
+ // update conditions to adjust map
+ List<String> checkerConditions = null;
+ if (conditionIdsToAdjust.containsKey(checkerId))
+ {
+ checkerConditions = conditionIdsToAdjust.get(checkerId);
+ }
+ else
+ {
+ checkerConditions = new ArrayList<String>(5);
+ }
+ checkerConditions.add(conditionId);
+ conditionIdsToAdjust.put(checkerId, checkerConditions);
+
+ // update exceptions map, it contains every condition
+ // processed, doesn't matter the condition
+ if (exceptionsMap.containsKey(checkerId))
+ {
+ checkerConditions = exceptionsMap.get(checkerId);
+ }
+ else
+ {
+ checkerConditions = new ArrayList<String>(5);
+ }
+ checkerConditions.add(conditionId);
+ exceptionsMap.put(checkerId, checkerConditions);
+ }
+ else
+ {
+ checkerIdsToAdjust.add(completeId);
+ }
+ }
+ }
+ else
+ {
+ checkerIdsToAdjust = new ArrayList<String>(knownCheckers.keySet());
+ }
+ checkersIdsMap.put(type, checkerIdsToAdjust);
+ conditionsIdsMap.put(type, conditionIdsToAdjust);
+ }
+
+ // Adjust conditions first, it is higher priority.
+ for (WarningLevelAdjustmentType type : conditionsIdsMap.keySet())
+ {
+ if (conditionsIdsMap != null)
+ {
+ Map<String, List<String>> conditionsToAjust = conditionsIdsMap.get(type);
+ if ((conditionsToAjust != null) && !conditionsToAjust.isEmpty())
+ {
+ WarningLevelFilter.adjustWarningLevels(validationResult,
+ type == WarningLevelAdjustmentType.INCREASE, null, conditionsToAjust,
+ null);
+ }
+ }
+
+ }
+
+ // Adjust checkers, set the conditionsMap as the ignore map, so it won't
+ // be processed again.
+ for (WarningLevelAdjustmentType type : checkersIdsMap.keySet())
+ {
+ if (checkersIdsMap != null)
+ {
+ List<String> checkersIdsToAdjust = checkersIdsMap.get(type);
+ if ((checkersIdsToAdjust != null) && !checkersIdsToAdjust.isEmpty())
+ {
+ WarningLevelFilter.adjustWarningLevels(validationResult,
+ type == WarningLevelAdjustmentType.INCREASE, checkersIdsToAdjust, null,
+ exceptionsMap);
+ }
+ }
+ }
+ }
+
+ /**
+ * Prints the validation result for input parameters.
+ *
+ * @param parametersValidationResults
+ * The results of the input parameters validation.
+ */
+ private void printParameterErrors(List<ValidationResult> parametersValidationResults)
+ {
+ for (ValidationResult parameterResult : parametersValidationResults)
+ {
+ for (ValidationResultData parameterResultData : parameterResult.getValidationResult())
+ {
+ StringBuilder stringBuilder = new StringBuilder();
+ stringBuilder.append(parameterResultData.getSeverity().toString());
+ if (parameterResultData.getIssueDescription() != null)
+ {
+ stringBuilder.append(": "); //$NON-NLS-1$
+ stringBuilder.append(parameterResultData.getIssueDescription());
+ }
+ DebugVerboseOutputter
+ .printVerboseMessage(stringBuilder.toString(), VerboseLevel.v0);
+ }
+ }
+ }
+
+ private ValidationResult getValidationResultFromErrorParams(List<Parameter> errorParams)
+ {
+
+ ValidationResult validationResult = new ValidationResult(null, LimitedList.UNLIMITED);
+
+ for (Parameter param : errorParams)
+ {
+ ValidationResultData resultData = new ValidationResultData();
+ resultData.setSeverity(SEVERITY.ERROR);
+ resultData
+ .setIssueDescription(PreflightingCoreNLS.ValidationManager_UnknownParameterMessage
+ + param.getParameterType());
+ validationResult.addValidationResult(resultData);
+ }
+
+ return validationResult;
+ }
+
+ private int enabledCheckers(Map<String, CheckerExtension> knownCheckers)
+ {
+
+ int counter = 0;
+
+ if ((knownCheckers != null) && (knownCheckers.values() != null))
+ {
+ for (CheckerExtension c : knownCheckers.values())
+ {
+ counter += c.getChecker().isEnabled() ? 1 : 0;
+ }
+ }
+
+ return counter;
+ }
+
+ /***
+ * Run the checkers passed. If the list is empty or <code>null</code>, run
+ * all checkers.
+ *
+ * @param checkersList
+ * The list of checkers to be run, or an empty list (or
+ * <code>null</code> list) for running all checkers
+ * @param knownCheckers
+ * The list of all known checkers, which will be run in case
+ * there is no specific checkers passed on checkersToRun
+ * @param currentApplication
+ * @param adjustedWarningLevelInfo
+ *
+ * @return The list of validation results
+ *
+ * @throws PreflightingToolException
+ *
+ */
+ private ApplicationValidationResult validateApplication(
+ Map<String, CheckerExtension> knownCheckers, List<Parameter> deviceParams,
+ boolean isApk, Map<WarningLevelAdjustmentType, Set<String>> adjustedWarningLevelInfo,
+ List<String> invalidParamsCheckers) throws PreflightingToolException
+ {
+ ApplicationValidationResult applicationResult = null;
+
+ List<ValidationResult> results = new ArrayList<ValidationResult>();
+
+ int checkersCounter = enabledCheckers(knownCheckers);
+
+ if ((knownCheckers != null) && (checkersCounter == knownCheckers.size()))
+ {
+ DebugVerboseOutputter.printVerboseMessage(
+ PreflightingCoreNLS.ValidationManager_VerboseMessage_AllCheckersRun,
+ VerboseLevel.v1);
+
+ }
+
+ // Will be used to print a message in case no checker was executed
+ boolean wasAnyCheckerApplicable = false;
+
+ // Will be used to print a message in case a checker failed to execute
+ // due to an exception
+ List<String> failedCheckers = new ArrayList<String>();
+
+ if ((knownCheckers != null) && (checkersCounter > 0))
+ {
+ // run the necessary checkers
+
+ // Try to create application data
+ ApplicationData applicationData = null;
+
+ try
+ {
+ applicationData = new ApplicationData(getGlobalParameters());
+ if (isApk)
+ {
+ tempResourcesToDelete.add(applicationData.getRootElementPath());
+ }
+ }
+ catch (PreflightingToolException e)
+ {
+ DebugVerboseOutputter.printVerboseMessage(
+ PreflightingCoreNLS.ValidationManager_ErrorRetrievingApplicationData,
+ VerboseLevel.v1);
+ DebugVerboseOutputter.printVerboseMessage(e.getMessage(), VerboseLevel.v2);
+ throw new PreflightingToolException(
+ PreflightingCoreNLS.ValidationManager_ErrorRetrievingApplicationData, e);
+ }
+
+ if (applicationData != null)
+ {
+ applicationResult =
+ new ApplicationValidationResult(applicationData.getName(),
+ applicationData.getVersion(), applicationData.getApplicationPath());
+ XMLElement manifestElement = applicationData.getManifestElement();
+ if (manifestElement != null)
+ {
+ applicationResult.setXmlResultDocument(manifestElement.getDocument());
+ }
+
+ List<DeviceSpecification> deviceSpecs = null;
+
+ if (!noneDeviceSpecified)
+ {
+ deviceSpecs = filterDeviceSpecifications(deviceParams);
+ }
+ else
+ {
+ deviceSpecs = Collections.emptyList();
+ DebugVerboseOutputter
+ .printVerboseMessage(
+ PreflightingCoreNLS.ValidationManager_ValidationManager_VerboseMessage_Skipping_Device_Verifications,
+ VerboseLevel.v1);
+ }
+
+ boolean limitReached = false;
+ int limit = getLimit();
+ for (CheckerExtension checkerExt : knownCheckers.values())
+ {
+ if (checkerExt.getChecker().isEnabled())
+ {
+ IChecker checker = checkerExt.getChecker();
+
+ DebugVerboseOutputter
+ .printVerboseMessage(
+ PreflightingCoreNLS
+ .bind(PreflightingCoreNLS.ValidationManager_VerboseMessage_VerifyingCheckerRun,
+ checkerExt.getId()), VerboseLevel.v2);
+ /*
+ * Checkers will return a IStatus in the method canExecute
+ * to explain any problems preventing the execution. They
+ * also should throw exceptions
+ * (PreflightingCheckerException) whenever a problem occurs
+ * during execution.
+ */
+ try
+ {
+
+ //The returned status can be either a single status or
+ //a MultiStatus with the statuses from every condition
+ //ran for this specific checker
+
+ IStatus canExecuteChecker =
+ checker.canExecute(applicationData, deviceSpecs);
+ if (canExecuteChecker.isMultiStatus())
+ {
+ if (!canExecuteChecker.isOK()) //There are at least one condition that can't be executed
+ {
+ IStatus canProceed = null;
+ for (IStatus conditionStatus : canExecuteChecker.getChildren())
+ {
+ if (!conditionStatus.isOK())
+ {
+ CanExecuteConditionStatus canExecuteConditionStatus =
+ (CanExecuteConditionStatus) conditionStatus;
+ DebugVerboseOutputter
+ .printVerboseMessage(
+ PreflightingCoreNLS
+ .bind(PreflightingCoreNLS.ValidationManager_UnableToExecuteCondition,
+ canExecuteConditionStatus
+ .getConditionId(),
+ checkerExt.getId()),
+ VerboseLevel.v0);
+
+ DebugVerboseOutputter
+ .printVerboseMessage(
+ PreflightingCoreNLS
+ .bind(PreflightingCoreNLS.ValidationManager_UnableToExecuteCondition_Detailed,
+ new String[]
+ {
+ canExecuteConditionStatus
+ .getConditionId(),
+ checkerExt
+ .getId(),
+ canExecuteConditionStatus
+ .getMessage()
+ }), VerboseLevel.v1);
+
+ }
+ else
+ {
+ canProceed = conditionStatus;
+ }
+ }
+ canExecuteChecker = canProceed;
+ }
+ }
+
+ // that is none of the conditions were able to run
+ if (canExecuteChecker == null)
+ {
+ canExecuteChecker =
+ new Status(
+ IStatus.CANCEL,
+ PreflightingCorePlugin.PLUGIN_ID,
+ PreflightingCoreNLS.ValidationManager_NoConditionsReason);
+ }
+
+ if (!canExecuteChecker.isOK())
+ {
+ DebugVerboseOutputter
+ .printVerboseMessage(
+ PreflightingCoreNLS
+ .bind(PreflightingCoreNLS.ValidationManager_UnableToExecuteCheckerMessage,
+ checkerExt.getId()),
+ VerboseLevel.v0);
+
+ DebugVerboseOutputter
+ .printVerboseMessage(
+ PreflightingCoreNLS
+ .bind(PreflightingCoreNLS.ValidationManager_UnableToExecuteCheckerMessage_Detailed,
+ checkerExt.getId(),
+ canExecuteChecker.getMessage()),
+ VerboseLevel.v1);
+
+ applicationResult
+ .addStatus(
+ checkerExt.getId(),
+ new Status(
+ IStatus.CANCEL,
+ PreflightingCorePlugin.PLUGIN_ID,
+ PreflightingCoreNLS
+ .bind(PreflightingCoreNLS.ValidationManager_UnableToExecuteCheckerMessage_Detailed,
+ checkerExt.getId(),
+ canExecuteChecker
+ .getMessage())));
+ }
+ else
+ {
+ wasAnyCheckerApplicable = true;
+ DebugVerboseOutputter
+ .printVerboseMessage(
+ PreflightingCoreNLS
+ .bind(PreflightingCoreNLS.ValidationManager_VerboseMessage_RunningChecker,
+ checkerExt.getId()),
+ VerboseLevel.v2);
+ ValidationResult checkerResults = null;
+ try
+ {
+ checkerResults = new ValidationResult(checker.getId(), limit);
+ checker.validateApplication(applicationData, deviceSpecs,
+ valManagerConfig, checkerResults);
+ applicationResult.addStatus(checkerExt.getId(),
+ Status.OK_STATUS);
+ if (checkerResults != null)
+ {
+ results.add(checkerResults);
+
+ int resultsSize =
+ checkerResults.getValidationResult().size();
+ if (limit != LimitedList.UNLIMITED)
+ {
+ limit -= resultsSize;
+ if (limit <= 0)
+ {
+ limitReached = true;
+ }
+ }
+ }
+ }
+ catch (PreflightingCheckerException e)
+ {
+ failedCheckers.add(checkerExt.getId());
+
+ applicationResult.addStatus(
+ checkerExt.getId(),
+ new Status(IStatus.ERROR,
+ PreflightingCorePlugin.PLUGIN_ID, e
+ .getMessage()));
+
+ PreflightingLogger.error(this.getClass(),
+ "Unexpected exception while running checker " //$NON-NLS-1$
+ + checkerExt.getId(), e);
+
+ DebugVerboseOutputter
+ .printVerboseMessage(
+ PreflightingCoreNLS
+ .bind(PreflightingCoreNLS.ValidationManager_UnexpectedExceptiononChecker,
+ checkerExt.getId()),
+ VerboseLevel.v1);
+
+ DebugVerboseOutputter.printVerboseMessage(e.getMessage(),
+ VerboseLevel.v2);
+
+ }
+ catch (ValidationLimitException e)
+ {
+ if (checkerResults != null)
+ {
+ results.add(checkerResults);
+ }
+ limitReached = true;
+ }
+ catch (Exception e)
+ {
+ failedCheckers.add(checkerExt.getId());
+
+ PreflightingLogger.error(this.getClass(),
+ "Unexpected exception while running checker " //$NON-NLS-1$
+ + checkerExt.getName(), e);
+
+ applicationResult
+ .addStatus(
+ checkerExt.getId(),
+ new Status(
+ IStatus.ERROR,
+ PreflightingCorePlugin.PLUGIN_ID,
+ PreflightingCoreNLS
+ .bind(PreflightingCoreNLS.ValidationManager_UnexpectedExceptiononChecker_V2,
+ e.getLocalizedMessage(),
+ checkerExt.getId())));
+
+ DebugVerboseOutputter
+ .printVerboseMessage(
+ PreflightingCoreNLS
+ .bind(PreflightingCoreNLS.ValidationManager_UnexpectedExceptiononChecker,
+ checkerExt.getId()),
+ VerboseLevel.v1);
+
+ DebugVerboseOutputter
+ .printVerboseMessage(
+ PreflightingCoreNLS
+ .bind(PreflightingCoreNLS.ValidationManager_UnexpectedExceptiononChecker_V2,
+ e.getLocalizedMessage(),
+ checkerExt.getId()),
+ VerboseLevel.v2);
+ }
+
+ DebugVerboseOutputter
+ .printVerboseMessage(
+ PreflightingCoreNLS
+ .bind(PreflightingCoreNLS.ValidationManager_VerboseMessage_CheckerFinished,
+ checkerExt.getId()),
+ VerboseLevel.v2);
+
+ }
+ }
+ catch (Exception e)
+ {
+ failedCheckers.add(checkerExt.getId());
+
+ PreflightingLogger.error(this.getClass(),
+ "Unexpected exception while running checker " //$NON-NLS-1$
+ + checkerExt.getName(), e);
+
+ applicationResult
+ .addStatus(
+ checkerExt.getId(),
+ new Status(
+ IStatus.ERROR,
+ PreflightingCorePlugin.PLUGIN_ID,
+ PreflightingCoreNLS
+ .bind(PreflightingCoreNLS.ValidationManager_UnexpectedExceptiononChecker_V2,
+ e.getLocalizedMessage(),
+ checkerExt.getId())));
+
+ DebugVerboseOutputter
+ .printVerboseMessage(
+ PreflightingCoreNLS
+ .bind(PreflightingCoreNLS.ValidationManager_UnexpectedExceptiononChecker,
+ checkerExt.getId()), VerboseLevel.v1);
+
+ DebugVerboseOutputter
+ .printVerboseMessage(
+ PreflightingCoreNLS
+ .bind(PreflightingCoreNLS.ValidationManager_UnexpectedExceptiononChecker_V2,
+ e.getLocalizedMessage(),
+ checkerExt.getId()), VerboseLevel.v2);
+ }
+
+ if (limitReached)
+ {
+ DebugVerboseOutputter.printVerboseMessage(PreflightingCoreNLS.bind(
+ PreflightingCoreNLS.ValidationManager_ValidationLimitReached,
+ getLimit()), VerboseLevel.v2);
+ break;
+ }
+
+ //clean checker data
+ checker.clean();
+ //call Garbage Collector to free memory
+ Runtime.getRuntime().gc();
+ }
+ else
+ {
+ Status status = null;
+ IChecker checker = checkerExt.getChecker();
+
+ //if checker was not disabled but a mandatory parameter is missing, the checker will
+ //be disabled.
+ if ((invalidParamsCheckers != null)
+ && (invalidParamsCheckers.contains(checker.getId())))
+ {
+ status =
+ new Status(
+ IStatus.ERROR,
+ PreflightingCorePlugin.PLUGIN_ID,
+ PreflightingCoreNLS.ValidationManager_MandatoryParameterMissing);
+ }
+ //if checker is not in the list of invalid parameters, it was manually disabled by the user.
+ else
+ {
+ status =
+ new Status(
+ IStatus.INFO,
+ PreflightingCorePlugin.PLUGIN_ID,
+ PreflightingCoreNLS.ValidationManager_CheckerWasDisabled);
+ }
+
+ applicationResult.addStatus(checkerExt.getChecker().getId(), status);
+ }
+
+ }
+
+ //clean up application data private instance objects
+ applicationData.clean();
+ //clean data from APK
+ AaptUtils.cleanApplicationResourceValues();
+ //after all checkers run - call Garbage Collector to free memory
+ Runtime.getRuntime().gc();
+ }
+
+ }
+
+ if (!wasAnyCheckerApplicable)
+ {
+ // No checker was executed, warns user about that
+ DebugVerboseOutputter.printVerboseMessage(
+ PreflightingCoreNLS.ValidationManager_Errors_NoValidationApplyOrError,
+ VerboseLevel.v0);
+ }
+
+ /*
+ *
+ * REMOVED BLOCK
+ * Execution Status will be printed after execution
+ *
+ *
+ *
+ if (!failedCheckers.isEmpty())
+ {
+ // At least one checker failed to execute due to an exception.
+ StringBuffer formattedFailedCheckers = new StringBuffer();
+ for (String checkerId : failedCheckers)
+ {
+ formattedFailedCheckers.append("\n\t"); //$NON-NLS-1$
+ formattedFailedCheckers.append(checkerId);
+ }
+ VerboseOutputter.printVerboseMessage(NLS.bind(
+ PreflightingCoreNLS.ValidationManager_FailedCheckers,
+ formattedFailedCheckers.toString()), VerboseLevel.v0);
+
+ if ((VerboseOutputter.getCurrentVerboseLevel() == VerboseLevel.v0)
+ || (VerboseOutputter.getCurrentVerboseLevel() == VerboseLevel.v1))
+ {
+ VerboseOutputter.printVerboseMessage(
+ PreflightingCoreNLS.ValidationManager_FailedCheckers_IncreaseVerbosity,
+ VerboseLevel.v0);
+ }
+
+ }*/
+
+ // if the warning level for the validation result needs to be
+ // adjusted, do it
+ if (!adjustedWarningLevelInfo.isEmpty())
+ {
+ applyWarningLevelAdjustment(knownCheckers, adjustedWarningLevelInfo, results);
+ }
+
+ // filter results according to warning level before printing
+ results = WarningLevelFilter.filterValidationResultsForCurrentWarningLevel(results);
+ applicationResult.addResult(results);
+ // return full list of results
+ return applicationResult;
+ }
+
+ /**
+ * @param checkersList
+ * @param knownCheckers
+ * @return
+ */
+
+ private int getLimit()
+ {
+ int limit = LimitedList.UNLIMITED;
+ for (Parameter param : globalParams)
+ {
+ if (InputParameter.LIMIT.getAlias().equals(param.getParameterType()))
+ {
+ try
+ {
+ limit = Integer.parseInt(param.getValue());
+ }
+ catch (NumberFormatException nfe)
+ {
+ //do nothing
+ }
+ }
+ }
+ return limit;
+ }
+
+ /**
+ * Sets the global parameters for the validation
+ * @param globalParams
+ */
+ private void setGlobalParameters(List<Parameter> globalParams)
+ {
+ this.globalParams = globalParams;
+ }
+
+ /**
+ * Get the list of global arguments and their values.
+ *
+ * @return The list of global Parameters.
+ */
+ public List<Parameter> getGlobalParameters()
+ {
+ return globalParams;
+ }
+
+ /**
+ * Get the description for the parameters of the given checker.
+ * @param checkerId
+ * @return List of parameter descriptions.
+ */
+ public List<ParameterDescription> getParametersDescription(String checkerId)
+ {
+ if (checkerId == null)
+ {
+ return new ArrayList<ParameterDescription>(getParametersDescriptionAsMap().values());
+ }
+ else
+ {
+ CheckerExtension checkerExt = getCheckerExtension(checkerId);
+ if (checkerExt != null)
+ {
+ return checkerExt.getChecker().getParameterDescriptions();
+ }
+ else
+ {
+ return null;
+ }
+ }
+ }
+
+ /**
+ * @return Map from {@link ParameterDescription#getName()} to {@link ParameterDescription}
+ */
+ public Map<String, ParameterDescription> getParametersDescriptionAsMap()
+ {
+ if (globalParametersDescriptions.isEmpty())
+ {
+ populateParametersDescriptionList();
+ }
+
+ return new LinkedHashMap<String, ParameterDescription>(globalParametersDescriptions);
+ }
+
+ private void populateParametersDescriptionList()
+ {
+ ParameterDescription desc = new ParameterDescription();
+ desc.setName(InputParameter.SDK_PATH.getAlias());
+ desc.setDescription(PreflightingCoreNLS.ValidationManager_SdkPathDescriptionMessage);
+ desc.setValueDescription("SDKPATH"); //$NON-NLS-1$
+ desc.setType(ParameterType.STRING);
+ globalParametersDescriptions.put(desc.getName(), desc);
+
+ desc = new ParameterDescription();
+ desc.setName(CHECKER_PARAMETER);
+ desc.setDescription(PreflightingCoreNLS.ValidationManager_CheckerDescriptionMessage);
+ desc.setValueDescription("CHK [PRM]..."); //$NON-NLS-1$
+ desc.setType(ParameterType.STRING);
+ globalParametersDescriptions.put(desc.getName(), desc);
+
+ desc = new ParameterDescription();
+ desc.setName(DISABLE_CHECKER_PARAMETER);
+ desc.setDescription(PreflightingCoreNLS.ValidationManager_DisableCheckerDescriptionMessage);
+ desc.setValueDescription("CHK[.CND]"); //$NON-NLS-1$
+ desc.setType(ParameterType.STRING);
+ globalParametersDescriptions.put(desc.getName(), desc);
+
+ desc = new ParameterDescription();
+ desc.setName(DEVICE_PARAMETER);
+ desc.setDescription(PreflightingCoreNLS.ValidationManager_DeviceDescriptionMessage);
+ desc.setValueDescription("[DEV]"); //$NON-NLS-1$
+ desc.setType(ParameterType.STRING);
+ globalParametersDescriptions.put(desc.getName(), desc);
+
+ desc = new ParameterDescription();
+ desc.setName(InputParameter.WARNING_TO_ERROR.getAlias());
+ desc.setValueDescription("[CHK[.CND]]..."); //$NON-NLS-1$
+ desc.setDescription(PreflightingCoreNLS.ValidationManager_WarningToErrorDescriptionMessage);
+ globalParametersDescriptions.put(desc.getName(), desc);
+
+ desc = new ParameterDescription();
+ desc.setName(InputParameter.ERROR_TO_WARNING.getAlias());
+ desc.setValueDescription("[CHK[.CND]]..."); //$NON-NLS-1$
+ desc.setDescription(PreflightingCoreNLS.ValidationManager_ErrorToWarningDescriptionMessage);
+ globalParametersDescriptions.put(desc.getName(), desc);
+
+ desc = new ParameterDescription();
+ desc.setName(InputParameter.OUTPUT.getAlias());
+ desc.setValueDescription(PreflightingCoreNLS.ValidationManager_OutputSintaxMessage);
+ desc.setDescription(PreflightingCoreNLS.ValidationManager_OutputDescriptionMessage);
+ globalParametersDescriptions.put(desc.getName(), desc);
+
+ desc = new ParameterDescription();
+ desc.setName(InputParameter.LIMIT.getAlias());
+ desc.setValueDescription("[COUNT]"); //$NON-NLS-1$
+ desc.setDescription(PreflightingCoreNLS.ValidationManager_LimitDescription);
+ globalParametersDescriptions.put(desc.getName(), desc);
+ }
+
+ /**
+ * Get a list of checkers (with id and description).
+ *
+ * @return
+ */
+ public List<CheckerDescription> getCheckersDescription()
+ {
+ List<CheckerDescription> list = new ArrayList<CheckerDescription>();
+ try
+ {
+ loadCheckers();
+ for (String checkerId : checkers.keySet())
+ {
+ CheckerDescription chkDesc = new CheckerDescription();
+ CheckerExtension checkerExt = checkers.get(checkerId);
+
+ chkDesc.setId(checkerId);
+ chkDesc.setName(checkerExt.getName());
+ chkDesc.setDescription(checkerExt.getDescription());
+
+ list.add(chkDesc);
+ }
+ }
+ catch (PreflightingExtensionPointException e)
+ {
+ // Do nothing
+ }
+ return list;
+ }
+
+ /**
+ * Get all conditions for the checker passed as parameter.
+ * The information is retrieved from the extension point.
+ *
+ * @param checkerId The checker id to have its conditions retrieved.
+ * @return All conditions of the checker passed as parameter.
+ */
+ public List<Condition> getCheckerConditions(String checkerId)
+ {
+ List<Condition> conditionList = null;
+
+ try
+ {
+ loadCheckers();
+
+ CheckerExtension checkerExt = checkers.get(checkerId);
+
+ conditionList = new ArrayList(checkerExt.getChecker().getConditions().values());
+
+ }
+ catch (PreflightingExtensionPointException e)
+ {
+ // Do nothing
+ }
+
+ return conditionList;
+
+ }
+
+ /**
+ * Get a list of devices available (with name and description).
+ *
+ * @return
+ */
+ public List<Value> getDevicesInfoList()
+ {
+ ArrayList<Value> list = new ArrayList<Value>();
+ for (DeviceSpecification currentDevice : devicesSpecsContainer.getDeviceSpecifications())
+ {
+ Value v = new Value();
+ v.setValue(currentDevice.getName() + " - [" + currentDevice.getId() + "]"); //$NON-NLS-1$ $NON-NLS-2$
+ v.setDescription(""); //$NON-NLS-1$
+ list.add(v);
+ }
+
+ return list;
+ }
+
+ /**
+ * Get a device description.
+ *
+ * @return
+ */
+ public String getDeviceDescription(String deviceId)
+ {
+ String deviceDescr = null;
+ for (DeviceSpecification currentDevice : devicesSpecsContainer.getDeviceSpecifications())
+ {
+ if ((currentDevice.getId() != null) && currentDevice.getId().equals(deviceId))
+ {
+ deviceDescr = currentDevice.getDeviceInfo().toString();
+ break;
+ }
+ }
+ return deviceDescr;
+ }
+
+ /**
+ * Types of warning level available adjustment.
+ */
+ public enum WarningLevelAdjustmentType
+ {
+ INCREASE, DECREASE;
+ }
+
+ /**
+ * @param deleteApkTempFolder
+ * The deleteApkTempFolder to set
+ */
+ public void setDeleteApkTempFolder(boolean deleteApkTempFolder)
+ {
+ this.deleteApkTempFolder = deleteApkTempFolder;
+ }
+
+ private void deleteTempResources()
+ {
+ for (String currentPath : tempResourcesToDelete)
+ {
+ File currentFile = new File(currentPath);
+ if ((currentFile != null) && currentFile.exists())
+ {
+ try
+ {
+ // Try to delete the APK temp folder
+ if (!ProjectUtils.deleteDirRecursively(currentFile))
+ {
+ currentFile.deleteOnExit();
+ }
+ }
+ catch (IOException e)
+ {
+ // If the attempt above fails, try to schedule a
+ // deletion when JVM execution is finished
+ currentFile.deleteOnExit();
+ }
+ }
+ }
+ }
+
+ /**
+ * @return The {@link DevicesSpecsContainer} for the validator.
+ */
+ public DevicesSpecsContainer getDevicesSpecsContainer()
+ {
+ return devicesSpecsContainer;
+ }
+}
diff --git a/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/validation/ValidationManagerConfiguration.java b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/validation/ValidationManagerConfiguration.java
new file mode 100644
index 0000000..76d519f
--- /dev/null
+++ b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/validation/ValidationManagerConfiguration.java
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.preflighting.core.validation;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.util.Properties;
+
+import org.eclipse.core.runtime.Platform;
+
+/**
+ * Singleton that encapsulates the data in a file that configures App Validator execution.
+ */
+public class ValidationManagerConfiguration
+{
+
+ private final static String APP_VALIDATOR_CONFIG_FILE_NAME = "appvalidator.cfg"; //$NON-NLS-1$
+
+ private static ValidationManagerConfiguration instance;
+
+ /**
+ * @return singleton instance of {@link ValidationManagerConfiguration}
+ */
+ public static ValidationManagerConfiguration getInstance()
+ {
+ if (instance == null)
+ {
+ instance = new ValidationManagerConfiguration();
+ }
+ return instance;
+ }
+
+ /**
+ * Properties that are available into {@link ValidationManagerConfiguration#APP_VALIDATOR_CONFIG_FILE_NAME}.
+ */
+ public static enum ConfigProperties
+ {
+ BASE_URL_PROPERTY("base_url"), //$NON-NLS-1$
+
+ URL_QUERY_PROPERTY("info_url_query"), //$NON-NLS-1$
+
+ APK_EXTRACTION_MODE("apk_extraction_mode"); //$NON-NLS-1$
+
+ private String name;
+
+ private ConfigProperties(String name)
+ {
+ this.name = name;
+ }
+
+ /**
+ * @return string with the name of the configuration property
+ */
+ public String getName()
+ {
+ return name;
+ }
+ }
+
+ /**
+ * Property that exposes the modes to analyze APK.
+ */
+ public static enum ExtractionModes
+ {
+ APKTOOL_MODE("apktool"), //$NON-NLS-1$
+
+ AAPT_MODE("aapt"); //$NON-NLS-1$
+
+ private String mode;
+
+ private ExtractionModes(String mode)
+ {
+ this.mode = mode;
+ }
+
+ /**
+ * @return mode the chosen tool (mode to analyze APK)
+ */
+ public String getMode()
+ {
+ return mode;
+ }
+
+ /**
+ * Checks if a mode is not contained in the list of {@link ExtractionModes} available
+ * @param mode the string containing the mode (tool to analyze APK)
+ * @return <code>true</code> if found, <code>false</code> otherwise
+ */
+ public static boolean contains(String mode)
+ {
+ boolean contains = false;
+ if (mode != null)
+ {
+ for (ExtractionModes input : ExtractionModes.values())
+ {
+ if (input.getMode().equals(mode))
+ {
+ contains = true;
+ break;
+ }
+ }
+ }
+
+ return contains;
+ }
+ }
+
+ private final static String DEFAULT_URL = "http://developer.motorola.com/"; //$NON-NLS-1$
+
+ private final Properties p = new Properties();
+
+ /**
+ * Initializes map with app validator startup configuration
+ */
+ private ValidationManagerConfiguration()
+ {
+ String path = Platform.getInstallLocation().getURL().getPath();
+ if (!path.endsWith(File.separator))
+ {
+ path += File.separator;
+ }
+ path += APP_VALIDATOR_CONFIG_FILE_NAME;
+
+ File f = new File(path);
+ FileInputStream fis = null;
+ try
+ {
+ if (f.exists() && f.isFile())
+ {
+ fis = new FileInputStream(f);
+ p.load(fis);
+ }
+ if (getProperty(ConfigProperties.BASE_URL_PROPERTY.getName()) == null)
+ {
+ p.put(ConfigProperties.BASE_URL_PROPERTY.getName(), DEFAULT_URL);
+ }
+ if (getProperty(ConfigProperties.URL_QUERY_PROPERTY.getName()) == null)
+ {
+ p.put(ConfigProperties.URL_QUERY_PROPERTY.getName(), "");
+ }
+ if (!ExtractionModes.contains(getProperty(ConfigProperties.APK_EXTRACTION_MODE
+ .getName())))
+ {
+ p.put(ConfigProperties.APK_EXTRACTION_MODE.getName(),
+ ExtractionModes.APKTOOL_MODE.getMode());
+ }
+ }
+ catch (IOException e)
+ {
+ //populate with default values
+ p.put(ConfigProperties.BASE_URL_PROPERTY.getName(), DEFAULT_URL);
+ p.put(ConfigProperties.URL_QUERY_PROPERTY.getName(), "");
+ p.put(ConfigProperties.APK_EXTRACTION_MODE.getName(),
+ ExtractionModes.APKTOOL_MODE.getMode());
+ }
+ finally
+ {
+ if (fis != null)
+ {
+ try
+ {
+ fis.close();
+ }
+ catch (IOException e)
+ {
+ // do nothing
+ }
+ }
+ }
+ }
+
+ /**
+ * Get the selected property.
+ * @param property string with one of the items available in {@link ConfigProperties}
+ * @return the selected property or null if its not found.
+ */
+ public String getProperty(String property)
+ {
+ return p.getProperty(property);
+ }
+}
diff --git a/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/validation/ValidationResult.java b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/validation/ValidationResult.java
new file mode 100644
index 0000000..95ef92d
--- /dev/null
+++ b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/validation/ValidationResult.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.preflighting.core.validation;
+
+import java.util.List;
+
+import com.motorolamobility.preflighting.core.utils.LimitedList;
+
+/**
+ * It contains a {@link LimitedList} of {@link ValidationResultData}, that is,
+ * a collection of results raised by a checker or condition.
+ */
+public class ValidationResult
+{
+
+ private LimitedList<ValidationResultData> data = null;
+
+ private String checkerId;
+
+ /**
+ * Constructor
+ * @param limit maximum number of results that can be returned (if the list of results exceed, only the first items will be displayed)
+ * @param checkerId The checkerId for the checker being validated. The value is null for global validations.
+ */
+ public ValidationResult(String checkerId, int limit)
+ {
+ this.setCheckerId(checkerId);
+ data = new LimitedList<ValidationResultData>(limit);
+ }
+
+ /**
+ * Returns the list of results raised by a checker or condition
+ * @return list of {@link ValidationResultData}
+ */
+ public List<ValidationResultData> getValidationResult()
+ {
+ return data;
+ }
+
+ /**
+ * Add one ValidationResultData to the ValidationResult object.
+ * @param dataRow a new single issue found by a checker/condition
+ */
+ public void addValidationResult(ValidationResultData dataRow)
+ {
+ data.add(dataRow);
+ }
+
+ /**
+ * @param checkerId The checkerId to set.
+ */
+ private void setCheckerId(String checkerId)
+ {
+ this.checkerId = checkerId;
+ }
+
+ /**
+ * Returns the unique identifier for the checker (as declared in the <code>com.motorolamobility.preflighting.core.checker</code> extension point)
+ * @return The checkerId or null if not informed.
+ */
+ public String getCheckerId()
+ {
+ return checkerId;
+ }
+
+ /**
+ * Clear the validation result list.
+ */
+ public void clear()
+ {
+ data.clear();
+ }
+
+ /**
+ * Add all items from list of {@link ValidationResultData} to the validation result.
+ *
+ * @param list of items to add into the {@link LimitedList} of results raised by condition/checker.
+ */
+ public void addAll(List<ValidationResultData> list)
+ {
+ data.addAll(list);
+ }
+}
diff --git a/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/validation/ValidationResultData.java b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/validation/ValidationResultData.java
new file mode 100644
index 0000000..1a91fd9
--- /dev/null
+++ b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/validation/ValidationResultData.java
@@ -0,0 +1,350 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.preflighting.core.validation;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import com.motorolamobility.preflighting.core.checker.condition.Condition;
+import com.motorolamobility.preflighting.core.i18n.PreflightingCoreNLS;
+
+/**
+ * Corresponds to one validation issue found by a checker/condition.
+ */
+public class ValidationResultData
+{
+ /**
+ * Severity level of the result.
+ */
+ public enum SEVERITY
+ {
+ /**
+ * The FATAL level is used for severe error events. In case of FATAL, the
+ * application could be aborted.
+ */
+ FATAL
+ {
+ @Override
+ public String toString()
+ {
+ return PreflightingCoreNLS.ValidationResultData_FatalSeverityMessage;
+ }
+ },
+ /**
+ * The ERROR level is used by errors events. Less severe than FATAL, used
+ * for situations of error that will not crash the application.
+ */
+ ERROR
+ {
+ @Override
+ public String toString()
+ {
+ return PreflightingCoreNLS.ValidationResultData_ErrorSeverityMessage;
+ }
+ },
+ /**
+ * The WARNING level is used for potentially harmful situations. Used for
+ * situations that can generate an error.
+ */
+ WARNING
+ {
+ @Override
+ public String toString()
+ {
+ return PreflightingCoreNLS.ValidationResultData_WarningSeverityMessage;
+ }
+ },
+ /**
+ * The OK level is used if no problem was identified by this checker / condition.
+ */
+ OK
+ {
+ @Override
+ public String toString()
+ {
+ return PreflightingCoreNLS.ValidationResultData_OkSeverityMessage;
+ }
+ };
+
+ }
+
+ private Map<File, List<Integer>> fileToIssueLines;
+
+ // initialize to avoid invalid value (null)
+ private SEVERITY severity = SEVERITY.OK;
+
+ private String issueDescription;
+
+ private String quickFixSuggestion;
+
+ /**
+ * The condition associated with this result data.
+ */
+ private String conditionID;
+
+ /**
+ * The preview associated with this data (like the line with problems)
+ */
+ private String preview = null;
+
+ /**
+ * Link with more information about validation/checker
+ */
+ private String infoURL = null;
+
+ private String markerType;
+
+ private final List<Object> extra;
+
+ /**
+ * Default constructor.
+ */
+ public ValidationResultData()
+ {
+ fileToIssueLines = new HashMap<File, List<Integer>>();
+ extra = new ArrayList<Object>();
+ }
+
+ /**
+ * Constructor that fills the most used items when an issue is raised.
+ * @param fileToIssueLines List of files and the lines where the problem occurred
+ * @param severity {@link SEVERITY} of the issue.
+ * @param issueDescription Description of the issue.
+ * @param quickFixSuggestion Quick fix suggested.
+ * @param conditionID {@link Condition#getId()} that identified the issue.
+ */
+ public ValidationResultData(Map<File, List<Integer>> fileToIssueLines, SEVERITY severity,
+ String issueDescription, String quickFixSuggestion, String conditionID)
+ {
+ this.fileToIssueLines =
+ fileToIssueLines != null ? fileToIssueLines : new HashMap<File, List<Integer>>();
+ this.severity = severity;
+ this.issueDescription = issueDescription;
+ this.quickFixSuggestion = quickFixSuggestion;
+ this.conditionID = conditionID;
+ this.extra = new ArrayList<Object>();
+ }
+
+ /**
+ * Constructor that fills the most used items when an issue is raised.
+ * @param fileToIssueLines List of files and the lines where the problem occurred
+ * @param severity {@link SEVERITY} of the issue.
+ * @param issueDescription Description of the issue.
+ * @param quickFixSuggestion Quick fix suggested.
+ * @param conditionID {@link Condition#getId()} that identified the issue.
+ * @param markerType the type of the problem that will be reported by App Validator.
+ */
+ public ValidationResultData(Map<File, List<Integer>> fileToIssueLines, SEVERITY severity,
+ String issueDescription, String quickFixSuggestion, String conditionID,
+ String markerType)
+ {
+ this.fileToIssueLines =
+ fileToIssueLines != null ? fileToIssueLines : new HashMap<File, List<Integer>>();
+ this.severity = severity;
+ this.issueDescription = issueDescription;
+ this.quickFixSuggestion = quickFixSuggestion;
+ this.conditionID = conditionID;
+ this.markerType = markerType;
+ this.extra = new ArrayList<Object>();
+ }
+
+ /**
+ * Returns a {@link Map} containing the {@link File} and the corresponding lines with issues of this
+ * {@link ValidationResultData}.
+ * @return a {@link Map} with the {@link File} and its issue lines.
+ */
+ public Map<File, List<Integer>> getFileToIssueLines()
+ {
+ return fileToIssueLines;
+ }
+
+ /**
+ * Adds to a File, usually the problematic one, to the issue lines of the {@link ValidationResultData}.
+ * @param file the file descriptor.
+ * @param lines the lines in which the issue appears. */
+
+ public void addFileToIssueLines(File file, List<Integer> lines)
+ {
+ if (fileToIssueLines == null)
+ {
+ fileToIssueLines = new HashMap<File, List<Integer>>();
+ }
+ fileToIssueLines.put(file, lines);
+ }
+
+ /**
+ * Returns the issue {@link SEVERITY}.
+ * @return the issue {@link SEVERITY}.
+ */
+ public SEVERITY getSeverity()
+ {
+ return severity;
+ }
+
+ /**
+ * Sets the {@link ValidationResultData} issue severity.
+ * @param severity a {@link SEVERITY}.
+ */
+ public void setSeverity(SEVERITY severity)
+ {
+ // protect from invalid value (null)
+ if (severity != null)
+ {
+ this.severity = severity;
+ }
+ else
+ {
+ this.severity = SEVERITY.OK;
+ }
+ }
+
+ /**
+ * Returns the issue description for this {@link ValidationResultData}.
+ * @return the issue description.
+ */
+ public String getIssueDescription()
+ {
+ return issueDescription;
+ }
+
+ /**
+ * Sets the issue description for this {@link ValidationResultData}.
+ * @param issueDescription the issue description.
+ */
+ public void setIssueDescription(String issueDescription)
+ {
+ this.issueDescription = issueDescription;
+ }
+
+ /**
+ * Return the quick fix suggestion for this issue, or <code>null</code>
+ * if there's no suggestion.
+ *
+ * @return The quick fix suggestion.
+ */
+ public String getQuickFixSuggestion()
+ {
+ return quickFixSuggestion;
+ }
+
+ /**
+ * Set the quick fix suggestion for this issue. If an empty string, or <code>null</code>
+ * is passed, the quick fix suggestion is set to <code>null</code>, indicating
+ * there is no suggestion for the issue.
+ *
+ * @param quickFixSuggestion The quick fix suggestion for the issue.
+ */
+ public void setQuickFixSuggestion(String quickFixSuggestion)
+ {
+ if ((quickFixSuggestion == null) || (quickFixSuggestion.length() == 0))
+ {
+ this.quickFixSuggestion = null;
+ }
+ else
+ {
+ this.quickFixSuggestion = quickFixSuggestion;
+ }
+ }
+
+ /**
+ * Returns the condition ID from this {@link ValidationResultData}.
+ * @return The condition ID.
+ */
+ public String getConditionID()
+ {
+ return conditionID;
+ }
+
+ /**
+ * Sets the Condition Id of this {@link ValidationResultData}.
+ * @param conditionID The condition ID to set.
+ */
+ public void setConditionID(String conditionID)
+ {
+ this.conditionID = conditionID;
+ }
+
+ /**
+ * Get line preview of validation result.
+ * @return The preview text or null if no preview is available.
+ */
+ public String getPreview()
+ {
+ return preview;
+ }
+
+ /**
+ * Set the validation preview that will be seen by user.
+ * @param preview A preview text.
+ */
+ public void setPreview(String preview)
+ {
+ this.preview = preview;
+ }
+
+ /**
+ * Get infoURL with more information about error.
+ * @return The infoURL.
+ */
+ public String getInfoURL()
+ {
+ return infoURL;
+ }
+
+ /**
+ * Set infoURL with more information.
+ * @param infoURL the url with the info
+ */
+ public void setInfoURL(String url)
+ {
+ this.infoURL = url;
+ }
+
+ /**
+ * @return the markerType
+ */
+ public String getMarkerType()
+ {
+ return markerType;
+ }
+
+ /**
+ * @param markerType the markerType to set
+ */
+ public void setMarkerType(String markerType)
+ {
+ this.markerType = markerType;
+ }
+
+ /**
+ * @return the list of extra values.
+ */
+ public List<Object> getExtra()
+ {
+ return this.extra;
+ }
+
+ /**
+ * Appends {@code value} to the list of extras.
+ */
+ public void appendExtra(Object value)
+ {
+ extra.add(value);
+ }
+} \ No newline at end of file
diff --git a/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/validation/Value.java b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/validation/Value.java
new file mode 100644
index 0000000..fc1c9c6
--- /dev/null
+++ b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/validation/Value.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.preflighting.core.validation;
+
+/**
+ * Encapsulates 2 strings: one for the value, and another for a description.
+ */
+public class Value
+{
+ private String value;
+
+ private String description = null;
+
+ /**
+ * Returns the value
+ * @return The value.
+ */
+ public String getValue()
+ {
+ return value;
+ }
+
+ /**
+ * Sets the value
+ * @param value The value to set.
+ */
+ public void setValue(String value)
+ {
+ this.value = value;
+ }
+
+ /**
+ * Retrieve the description of the value.
+ *
+ * @return The description of the value.
+ */
+ public String getDescription()
+ {
+ return description;
+ }
+
+ /**
+ * Set the description of the value.
+ *
+ * @param description The description to set on the value.
+ */
+ public void setDescription(String description)
+ {
+ this.description = description;
+ }
+}
diff --git a/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/verbose/DebugVerboseOutputter.java b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/verbose/DebugVerboseOutputter.java
new file mode 100644
index 0000000..6a48e95
--- /dev/null
+++ b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/verbose/DebugVerboseOutputter.java
@@ -0,0 +1,193 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.preflighting.core.verbose;
+
+import java.io.PrintStream;
+
+import com.motorolamobility.preflighting.core.i18n.PreflightingCoreNLS;
+
+/**
+ * Abstract class responsible for defining verbosity levels, setting the current verbosity level,
+ * and printing verbose messages according to verbosity level set.
+ * The verbosity level will be passed as parameter to the application, and if not passed,
+ * {@link DebugVerboseOutputter#DEFAULT_VERBOSE_LEVEL} will be assumed (default verbosity level).
+ * The default stream used by this outputter is system.err.
+ */
+public abstract class DebugVerboseOutputter
+{
+ public enum VerboseLevel
+ {
+ /**
+ * Default verbosity level (no added verbosity).
+ */
+ v0
+ {
+ /* (non-Javadoc)
+ * @see java.lang.Enum#toString()
+ */
+ @Override
+ public String toString()
+ {
+ return ""; //$NON-NLS-1$
+ }
+ },
+
+ /**
+ * Indicate the application flow as individual resources or conditions are checked.
+ */
+ v1
+ {
+ /* (non-Javadoc)
+ * @see java.lang.Enum#toString()
+ */
+ @Override
+ public String toString()
+ {
+ return PreflightingCoreNLS.VerboseOutputter_InfoVerboseLevelString;
+ }
+ },
+
+ /**
+ * Debugging mode indicates a great deal of information about every aspect of the execution
+ * flow. This could be as functions are entered or exited.
+ */
+ v2
+ {
+ /* (non-Javadoc)
+ * @see java.lang.Enum#toString()
+ */
+ @Override
+ public String toString()
+ {
+ return PreflightingCoreNLS.VerboseOutputter_DebugVerboseLeveString;
+ }
+ };
+ }
+
+ private static final String NEWLINE = System.getProperty("line.separator"); //$NON-NLS-1$
+
+ private static PrintStream myStream = System.out;
+
+ /**
+ * The default verbosity level ({@link VerboseLevel#v0}).
+ */
+ public static final VerboseLevel DEFAULT_VERBOSE_LEVEL = VerboseLevel.v0;
+
+ /**
+ * The current verbosity level used by the application.
+ */
+ private static VerboseLevel currentVerboseLevel = DEFAULT_VERBOSE_LEVEL;
+
+ /**
+ * Retrieve the current verbosity level used by the application.
+ *
+ * @return Current verbosity level used by the application.
+ */
+ public static VerboseLevel getCurrentVerboseLevel()
+ {
+ return currentVerboseLevel;
+ }
+
+ /**
+ * Set the current verbosity level to the given value.
+ * If <code>null</code> is passed, the default verbosity level
+ * ({@link DebugVerboseOutputter#DEFAULT_VERBOSE_LEVEL}) will be set.
+ *
+ * @param level The verbosity level to be set.
+ */
+ public static void setCurrentVerboseLevel(VerboseLevel level)
+ {
+ if (level != null)
+ {
+ currentVerboseLevel = level;
+ }
+ else
+ {
+ currentVerboseLevel = DEFAULT_VERBOSE_LEVEL;
+ }
+ }
+
+ /**
+ * Print the message with the given verbosity level, if and only
+ * if the current verbosity level allows it to be printed (current
+ * level is greater or equal to the passed level).
+ *
+ * @param message Message to be printed.
+ * @param level The verbosity level in which the message should be printed.
+ */
+ public static void printVerboseMessage(String message, VerboseLevel level)
+ {
+ if (currentVerboseLevel.compareTo(level) >= 0)
+ {
+ if ((message != null) && (message.length() > 0))
+ {
+ getPrintStream().println(level + message);
+ }
+ else
+ {
+ getPrintStream().println();
+ }
+ getPrintStream().flush();
+ }
+ }
+
+ /**
+ * Return a string with the message with the given verbosity level, if and only
+ * if the current verbosity level allows it to be printed (current
+ * level is greater or equal to the passed level).
+ *
+ * @param message Message to be printed.
+ * @param level The verbosity level in which the message should be printed.
+ * @return a string with the message with the given verbosity level.
+ */
+ public static String printVerboseMessageToString(String message, VerboseLevel level)
+ {
+ String strMsg = "";
+
+ if (currentVerboseLevel.compareTo(level) >= 0)
+ {
+ if ((message != null) && (message.length() > 0))
+ {
+ strMsg = level + message;
+ }
+ else
+ {
+ strMsg = NEWLINE;
+ }
+ }
+ return strMsg;
+ }
+
+ /**
+ * Set the stream for printing the verbose output.
+ *
+ * @param printStream The stream output.
+ */
+ public static void setStream(PrintStream printStream)
+ {
+ myStream = printStream;
+ }
+
+ /**
+ * Retrieve the print stream to be used for printing messages.
+ *
+ * @return The print stream.
+ */
+ private static PrintStream getPrintStream()
+ {
+ return myStream;
+ }
+}
diff --git a/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/verbose/WarningLevelFilter.java b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/verbose/WarningLevelFilter.java
new file mode 100644
index 0000000..e63f20b
--- /dev/null
+++ b/src/plugins/preflighting.core/src/com/motorolamobility/preflighting/core/verbose/WarningLevelFilter.java
@@ -0,0 +1,502 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.preflighting.core.verbose;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import com.motorolamobility.preflighting.core.i18n.PreflightingCoreNLS;
+import com.motorolamobility.preflighting.core.utils.LimitedList;
+import com.motorolamobility.preflighting.core.validation.ValidationResult;
+import com.motorolamobility.preflighting.core.validation.ValidationResultData;
+import com.motorolamobility.preflighting.core.validation.ValidationResultData.SEVERITY;
+import com.motorolamobility.preflighting.core.verbose.DebugVerboseOutputter.VerboseLevel;
+
+/**
+ * Abstract class responsible for defining warning levels, setting the current warning level,
+ * and filtering results and returning appropriate messages based on current warning
+ * level set.
+ * The warning level will be passed as a parameter to the application, if not,
+ * {@link WarningLevelFilter#DEFAULT_WARNING_LEVEL} will be assumed (default warning level).
+ */
+public abstract class WarningLevelFilter
+{
+ /**
+ * The terminator string for the total message.
+ */
+ private static final String TOTAL_MESSAGE_TERMINATOR = "."; //$NON-NLS-1$
+
+ /**
+ * The level separator string for the total message.
+ */
+ private static final String TOTAL_MESSAGE_LEVEL_SEPARATOR = ", "; //$NON-NLS-1$
+
+ /**
+ * Enumeration representing the various warning levels available to the application.
+ */
+ public enum WarningLevel
+ {
+ /**
+ * Suppress all messages, just return success or failure.
+ */
+ w0,
+ /**
+ * Only indicate extremely severe conditions that could cause immediate failure of
+ * the application on launch (fatal errors).
+ */
+ w1,
+ /**
+ * Indicate improper conditions that will lead to failure or possible problems on
+ * different handsets (fatal errors + errors). This is the default warning level.
+ */
+ w2,
+ /**
+ * Indicate all improper conditions (fatal errors + errors + warnings).
+ */
+ w3,
+ /**
+ * Indicate all improper conditions (fatal errors + errors + warnings) and their
+ * potential fixes, if known.
+ */
+ w4;
+ }
+
+ /**
+ * The default warning level ({@link WarningLevel#w2}).
+ */
+ public static WarningLevel DEFAULT_WARNING_LEVEL = WarningLevel.w2;
+
+ /**
+ * The current warning level being used.
+ */
+ private static WarningLevel currentWarningLevel = DEFAULT_WARNING_LEVEL;
+
+ /**
+ * Retrieve the current warning level for the application.
+ *
+ * @return Current warning level.
+ */
+ public static WarningLevel getCurrentWarningLevel()
+ {
+ return currentWarningLevel;
+ }
+
+ /**
+ * Set the current warning level to the given one. If <code>null</code> is
+ * passed, the default warning level is used ({@link WarningLevelFilter#DEFAULT_WARNING_LEVEL}).
+ *
+ * @param warningLevel Warning level to be set.
+ */
+ public static void setCurrentWarningLevel(WarningLevel warningLevel)
+ {
+ if (warningLevel != null)
+ {
+ currentWarningLevel = warningLevel;
+ }
+ else
+ {
+ currentWarningLevel = DEFAULT_WARNING_LEVEL;
+ }
+ }
+
+ /**
+ * Return whether quick fix suggestions should be printed
+ * (warning level equals {@link WarningLevel#w4}) or not.
+ *
+ * @return <code>true</code> if quick fix suggestions should be printed,
+ * <code>false</code> otherwise.
+ */
+ public static boolean printQuickFixSuggestions()
+ {
+ return currentWarningLevel.equals(WarningLevel.w4);
+ }
+
+ /**
+ * Return whether the severities should be printed or not
+ * (severities are not printed if warning level equals {@link WarningLevel#w0}).
+ *
+ * @return <code>true</code> if severities should be printed,
+ * <code>false</code> otherwise.
+ */
+ public static boolean printSeverity()
+ {
+ return !currentWarningLevel.equals(WarningLevel.w0);
+ }
+
+ /**
+ * Filters the given list of validation results so that the returning list
+ * of validation results contain only the appropriate issues, according to
+ * warning level set.
+ *
+ * @param validationResultList The list of validation results found by the application
+ *
+ * @return The filtered validation results list, according to warning level set
+ */
+ public static List<ValidationResult> filterValidationResultsForCurrentWarningLevel(
+ List<ValidationResult> validationResultList)
+ {
+ if (validationResultList == null)
+ {
+ throw new IllegalArgumentException("List<ValidationResult> cannot be null"); //$NON-NLS-1$
+ }
+
+ List<ValidationResult> filteredValidationResult = new ArrayList<ValidationResult>();
+
+ if (validationResultList.size() > 0)
+ {
+
+ DebugVerboseOutputter.printVerboseMessage(
+ PreflightingCoreNLS.WarningLevelFilter_VerboseMessage_FilterningResult,
+ VerboseLevel.v2);
+
+ if (currentWarningLevel.equals(WarningLevel.w0))
+ {
+ boolean fatalErrorsDetected = false, potentialErrorsDetected = false;
+
+ for (ValidationResult result : validationResultList)
+ {
+ for (ValidationResultData resultData : result.getValidationResult())
+ {
+ if (resultData.getSeverity().equals(SEVERITY.FATAL))
+ {
+ fatalErrorsDetected = true;
+ break;
+ }
+ else if (resultData.getSeverity().equals(SEVERITY.ERROR))
+ {
+ potentialErrorsDetected = true;
+ }
+ }
+ }
+
+ ValidationResult result = null;
+ if (fatalErrorsDetected)
+ {
+ result =
+ createValidationResultObject(
+ PreflightingCoreNLS.WarningLevelFilter_FatalErrorsMessage,
+ SEVERITY.FATAL);
+ }
+ else if (potentialErrorsDetected)
+ {
+ result =
+ createValidationResultObject(
+ PreflightingCoreNLS.WarningLevelFilter_ErrorsMessage,
+ SEVERITY.ERROR);
+ }
+ else
+ {
+ result =
+ createValidationResultObject(
+ PreflightingCoreNLS.WarningLevelFilter_NoProblemsMessage,
+ SEVERITY.OK);
+ }
+ filteredValidationResult.add(result);
+ }
+ else if (currentWarningLevel.equals(WarningLevel.w1)) // fatal errors only
+ {
+ boolean fatalErrorsDetected =
+ filterValidationResultForSeverity(validationResultList,
+ filteredValidationResult, SEVERITY.FATAL);
+ if (!fatalErrorsDetected)
+ {
+ ValidationResult result =
+ createValidationResultObject(
+ PreflightingCoreNLS.WarningLevelFilter_NoFatalErrorsMessage,
+ SEVERITY.OK);
+ filteredValidationResult.add(result);
+ }
+ }
+ else if (currentWarningLevel.equals(WarningLevel.w2)) // fatal errors and errors only
+ {
+ boolean errorsOrFatalErrorsDetected =
+ filterValidationResultForSeverity(validationResultList,
+ filteredValidationResult, SEVERITY.ERROR);
+ if (!errorsOrFatalErrorsDetected)
+ {
+ ValidationResult result =
+ createValidationResultObject(
+ PreflightingCoreNLS.WarningLevelFilter_NoFatalNorErrorsMessage,
+ SEVERITY.OK);
+ filteredValidationResult.add(result);
+ }
+ }
+ else if (currentWarningLevel.compareTo(WarningLevel.w3) >= 0) // all levels
+ {
+ boolean warningsErrorsOrFatalErrorsDetected =
+ filterValidationResultForSeverity(validationResultList,
+ filteredValidationResult, SEVERITY.WARNING);
+ if (!warningsErrorsOrFatalErrorsDetected)
+ {
+ ValidationResult result =
+ createValidationResultObject(
+ PreflightingCoreNLS.WarningLevelFilter_NoFatalErrorsNorWarningsMessage,
+ SEVERITY.OK);
+ filteredValidationResult.add(result);
+ }
+ }
+
+ DebugVerboseOutputter.printVerboseMessage(
+ PreflightingCoreNLS.WarningLevelFilter_VerboseMessage_ResultFiltered,
+ VerboseLevel.v2);
+ }
+
+ return filteredValidationResult;
+ }
+
+ private static ValidationResult createValidationResultObject(String message, SEVERITY severity)
+ {
+ ValidationResult result = new ValidationResult(null, LimitedList.UNLIMITED);
+ ValidationResultData resultData = new ValidationResultData();
+ result.addValidationResult(resultData);
+ resultData.setSeverity(severity);
+ resultData.setIssueDescription(message);
+ return result;
+ }
+
+ /**
+ * Filter the validation results of a particular checker, given the limit severity
+ * (which is defined according to warning level set). The result is added to the filtered
+ * list also passed.
+ * Returns a flag indicating if any issues were found of at least the given severity.
+ *
+ * @param validationResultList The original list of validation results
+ * @param filteredValidationResult The filtered list; entries are added to it by this method
+ * @param limitSeverity The limit severity for issues entering the filtered list or not
+ *
+ * @return <code>true</code> if any issues were found with at least the severity passed
+ * (or more severe), <code>false</code> otherwise
+ */
+ private static boolean filterValidationResultForSeverity(
+ List<ValidationResult> validationResultList,
+ List<ValidationResult> filteredValidationResult, SEVERITY limitSeverity)
+ {
+ boolean expectedLevelDetected = false;
+
+ for (ValidationResult result : validationResultList)
+ {
+ ValidationResult filteredResult = null;
+ for (ValidationResultData resultData : result.getValidationResult())
+ {
+ if (resultData.getSeverity().compareTo(limitSeverity) <= 0)
+ {
+ if (filteredResult == null)
+ {
+ filteredResult =
+ new ValidationResult(result.getCheckerId(), LimitedList.UNLIMITED);
+ }
+ filteredResult.addValidationResult(resultData);
+ expectedLevelDetected = true;
+ }
+ }
+ if (filteredResult != null)
+ {
+ filteredValidationResult.add(filteredResult);
+ }
+ }
+
+ return expectedLevelDetected;
+ }
+
+ /**
+ * Retrieves the total message summing up fatal errors, errors and warnings, depending
+ * on current warning level.
+ *
+ * @param validationResultList The list of validation results already filtered (as it
+ * was outputted to used)
+ *
+ * @return The total message string, or <code>null</code> if no message applies
+ */
+ public static String getValidationResultTotalMessage(List<ValidationResult> validationResultList)
+ {
+ String totalMessage = null;
+ if (validationResultList != null)
+ {
+ if (currentWarningLevel.compareTo(WarningLevel.w0) > 0)
+ {
+ int fatalErrorsCount = 0, errorsCount = 0, warningsCount = 0;
+
+ for (ValidationResult result : validationResultList)
+ {
+ for (ValidationResultData resultData : result.getValidationResult())
+ {
+ if (resultData.getSeverity().equals(SEVERITY.FATAL))
+ {
+ fatalErrorsCount++;
+ }
+ else if (resultData.getSeverity().equals(SEVERITY.ERROR))
+ {
+ errorsCount++;
+ }
+ else if (resultData.getSeverity().equals(SEVERITY.WARNING))
+ {
+ warningsCount++;
+ }
+ }
+ }
+
+ StringBuilder stringBuilder = new StringBuilder();
+
+ // (some code repetition was used to avoid unnecessary repeated testing that would
+ // slow performance)
+ if (currentWarningLevel.compareTo(WarningLevel.w3) >= 0)
+ {
+ if ((fatalErrorsCount > 0) || (errorsCount > 0) || (warningsCount > 0))
+ {
+ stringBuilder.append(PreflightingCoreNLS.WarningLevelFilter_TotalMessage);
+ stringBuilder.append(PreflightingCoreNLS.bind(
+ PreflightingCoreNLS.WarningLevelFilter_FatalErrorsCountMessage,
+ fatalErrorsCount)
+ + TOTAL_MESSAGE_LEVEL_SEPARATOR);
+ stringBuilder.append(PreflightingCoreNLS.bind(
+ PreflightingCoreNLS.WarningLevelFilter_ErrorsCountMessage,
+ errorsCount)
+ + TOTAL_MESSAGE_LEVEL_SEPARATOR);
+ stringBuilder.append(PreflightingCoreNLS.bind(
+ PreflightingCoreNLS.WarningLevelFilter_WarningsCountMessage,
+ warningsCount)
+ + TOTAL_MESSAGE_TERMINATOR);
+ }
+ }
+ else if (currentWarningLevel.equals(WarningLevel.w2))
+ {
+ if ((fatalErrorsCount > 0) || (errorsCount > 0))
+ {
+ stringBuilder.append(PreflightingCoreNLS.WarningLevelFilter_TotalMessage);
+ stringBuilder.append(PreflightingCoreNLS.bind(
+ PreflightingCoreNLS.WarningLevelFilter_FatalErrorsCountMessage,
+ fatalErrorsCount)
+ + TOTAL_MESSAGE_LEVEL_SEPARATOR);
+ stringBuilder.append(PreflightingCoreNLS.bind(
+ PreflightingCoreNLS.WarningLevelFilter_ErrorsCountMessage,
+ errorsCount)
+ + TOTAL_MESSAGE_TERMINATOR);
+ }
+ }
+ else if (currentWarningLevel.equals(WarningLevel.w1))
+ {
+ if (fatalErrorsCount > 0)
+ {
+ stringBuilder.append(PreflightingCoreNLS.WarningLevelFilter_TotalMessage);
+ stringBuilder.append(PreflightingCoreNLS.bind(
+ PreflightingCoreNLS.WarningLevelFilter_FatalErrorsCountMessage,
+ fatalErrorsCount)
+ + TOTAL_MESSAGE_TERMINATOR);
+ }
+ }
+
+ if (stringBuilder.length() > 0)
+ {
+ totalMessage = stringBuilder.toString();
+ }
+ }
+ }
+ return totalMessage;
+ }
+
+ /**
+ * Adjusts the warning level on the given validation result list.
+ * The level can be increased or decreased.
+ * Only the validation results from checkers or conditions passed on the are adjusted.
+ *
+ * @param validationResultList The list of validation results
+ * @param raiseWarningLevels Whether the warning level should be increased (<code>true</code>
+ * is passed) or decreased (<code>false</code> is passed)
+ * @param checkerIdsToAdjustWarningLevel The list of checkers whose validation results should
+ * have the warning level adjusted
+ * @param conditionIdsMap specific checkers conditions to adjust, instead of a whole checker.
+ * @param excludedConditionsIds
+ */
+ public static void adjustWarningLevels(List<ValidationResult> validationResultList,
+ boolean raiseWarningLevels, List<String> checkerIdsToAdjustWarningLevel,
+ Map<String, List<String>> conditionIdsMap, Map<String, List<String>> exclusionMap)
+ {
+ if (validationResultList == null)
+ {
+ throw new IllegalArgumentException("List<ValidationResult> cannot be null"); //$NON-NLS-1$
+ }
+ if (checkerIdsToAdjustWarningLevel == null)
+ {
+ checkerIdsToAdjustWarningLevel = Collections.emptyList();
+ }
+ if (conditionIdsMap == null)
+ {
+ conditionIdsMap = new HashMap<String, List<String>>(0);
+ }
+
+ SEVERITY[] severities = SEVERITY.values();
+ for (ValidationResult result : validationResultList)
+ {
+ String checkerId = result.getCheckerId();
+ if (checkerId != null)
+ {
+ //Verify if there's anything to be changed
+ if (checkerIdsToAdjustWarningLevel.contains(checkerId)
+ || (conditionIdsMap.containsKey(checkerId)))
+ {
+ List<String> conditionsToAdjust = conditionIdsMap.get(checkerId);
+
+ //If something has to be changed, process the whole set, applying the changes.
+ for (ValidationResultData resultData : result.getValidationResult())
+ {
+ boolean mustAdjust = true;
+ //User specified only some conditions
+ if (!checkerIdsToAdjustWarningLevel.contains(checkerId)
+ && (conditionsToAdjust != null))
+ {
+ mustAdjust = conditionsToAdjust.contains(resultData.getConditionID());
+ }
+ else if (exclusionMap != null)
+ {
+ List<String> conditionsToIgnore = exclusionMap.get(checkerId);
+ if (conditionsToIgnore != null)
+ {
+ mustAdjust =
+ !conditionsToIgnore.contains(resultData.getConditionID());
+ }
+ }
+ if (mustAdjust)
+ {
+ SEVERITY resultSeverity = resultData.getSeverity();
+ if (raiseWarningLevels)
+ {
+ // last level is not raised, and above warning is not raised as well
+ if ((SEVERITY.FATAL.compareTo(resultSeverity) < 0)
+ && !SEVERITY.OK.equals(resultSeverity))
+ {
+ resultData
+ .setSeverity(severities[resultSeverity.ordinal() - 1]);
+ }
+ }
+ else
+ {
+ // level is decreased for warning at least (nothing goes deeper than that)
+ if (SEVERITY.WARNING.compareTo(resultSeverity) > 0)
+ {
+ resultData
+ .setSeverity(severities[resultSeverity.ordinal() + 1]);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/src/plugins/preflighting.core/xsd/layout-devices.xsd b/src/plugins/preflighting.core/xsd/layout-devices.xsd
new file mode 100644
index 0000000..953cd4f
--- /dev/null
+++ b/src/plugins/preflighting.core/xsd/layout-devices.xsd
@@ -0,0 +1,344 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Eclipse Public License, Version 1.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.eclipse.org/org/documents/epl-v10.php
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+-->
+<xsd:schema
+ targetNamespace="http://schemas.android.com/sdk/android/layout-devices/1"
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+ xmlns:c="http://schemas.android.com/sdk/android/layout-devices/1"
+ elementFormDefault="qualified"
+ attributeFormDefault="unqualified"
+ version="1">
+
+ <!-- The root element layout-devices defines a sequence of 0..n device elements. -->
+
+ <xsd:element name="layout-devices" type="c:layoutDevicesType" />
+
+ <xsd:complexType name="layoutDevicesType">
+ <xsd:annotation>
+ <xsd:documentation xml:lang="en">
+ The "layout-devices" element is the root element of this schema.
+
+ It must contain zero or more "device" elements that each define the configurations
+ available for a given device.
+
+ These definitions are used in the Graphical Layout Editor in the
+ Android Development Tools (ADT) plugin for Eclipse.
+ </xsd:documentation>
+ </xsd:annotation>
+
+ <xsd:sequence>
+ <!-- layout-devices defines a sequence of 0..n device elements. -->
+ <xsd:element name="device" minOccurs="0" maxOccurs="unbounded">
+
+ <xsd:annotation>
+ <xsd:documentation xml:lang="en">
+ A device element must contain at most one "default" element
+ followed by one or more "config" elements.
+
+ The "default" element defines all the default parameters
+ inherited by the following "config" elements.
+ Each "config" element can override the default values, if any.
+
+ A "device" element also has a required "name" attribute that
+ represents the user-interface name of this device.
+ </xsd:documentation>
+ </xsd:annotation>
+
+ <xsd:complexType>
+ <!-- device defines a choice of 0..1 default element
+ and 1..n config elements. -->
+
+ <xsd:sequence>
+ <xsd:element name="default" type="c:parametersType"
+ minOccurs="0" maxOccurs="1" />
+ <xsd:element name="config" type="c:configType"
+ minOccurs="1" maxOccurs="unbounded" />
+ </xsd:sequence>
+
+ <xsd:attribute name="name" type="xsd:normalizedString" use="required" />
+ </xsd:complexType>
+
+ </xsd:element>
+ </xsd:sequence>
+ </xsd:complexType>
+
+ <!-- The type of a device>default element.
+ This is overridden by configType below for the device>config element.
+ -->
+ <xsd:complexType name="parametersType">
+ <xsd:annotation>
+ <xsd:documentation xml:lang="en">
+ The parametersType define all the parameters that can happen either in a
+ "default" element or in a named "config" element.
+ Each parameter element can appear once at most.
+
+ Parameters here are the same as those used to specify alternate Android
+ resources, as documented by
+ http://d.android.com/guide/topics/resources/resources-i18n.html#AlternateResources
+ </xsd:documentation>
+ </xsd:annotation>
+
+ <xsd:all>
+ <!-- parametersType says that 0..1 of each of these elements must be declared. -->
+
+ <xsd:element name="country-code" minOccurs="0">
+ <xsd:annotation>
+ <xsd:documentation xml:lang="en">
+ Specifies the configuration is for a particular Mobile Country Code.
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:simpleType>
+ <xsd:restriction base="xsd:float">
+ <xsd:minInclusive value="100" />
+ <xsd:maxInclusive value="999" />
+ </xsd:restriction>
+ </xsd:simpleType>
+ </xsd:element>
+
+ <xsd:element name="network-code" minOccurs="0">
+ <xsd:annotation>
+ <xsd:documentation xml:lang="en">
+ Specifies the configuration is for a particular Mobile Network Code.
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:simpleType>
+ <xsd:restriction base="xsd:float">
+ <xsd:minExclusive value="0" />
+ <xsd:maxExclusive value="1000" />
+ </xsd:restriction>
+ </xsd:simpleType>
+ </xsd:element>
+
+ <xsd:element name="screen-size" minOccurs="0">
+ <xsd:annotation>
+ <xsd:documentation xml:lang="en">
+ Specifies that the configuration is for a particular class of screen.
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:simpleType>
+ <xsd:restriction base="xsd:token">
+ <xsd:enumeration value="small" />
+ <xsd:enumeration value="normal" />
+ <xsd:enumeration value="large" />
+ <xsd:enumeration value="xlarge" />
+ </xsd:restriction>
+ </xsd:simpleType>
+ </xsd:element>
+
+ <xsd:element name="screen-ratio" minOccurs="0">
+ <xsd:annotation>
+ <xsd:documentation xml:lang="en">
+ Specifies that the configuration is for a taller/wider than traditional
+ screen. This is based purely on the aspect ration of the screen: QVGA,
+ HVGA, and VGA are notlong; WQVGA, WVGA, FWVGA are long. Note that long
+ may mean either wide or tall, depending on the current orientation.
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:simpleType>
+ <xsd:restriction base="xsd:token">
+ <xsd:enumeration value="long" />
+ <xsd:enumeration value="notlong" />
+ </xsd:restriction>
+ </xsd:simpleType>
+ </xsd:element>
+
+ <xsd:element name="screen-orientation" minOccurs="0">
+ <xsd:annotation>
+ <xsd:documentation xml:lang="en">
+ Specifies that the configuration is for a screen that is tall (port) or
+ wide (land).
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:simpleType>
+ <xsd:restriction base="xsd:token">
+ <xsd:enumeration value="port" />
+ <xsd:enumeration value="land" />
+ <xsd:enumeration value="square" />
+ </xsd:restriction>
+ </xsd:simpleType>
+ </xsd:element>
+
+ <xsd:element name="pixel-density" minOccurs="0">
+ <xsd:annotation>
+ <xsd:documentation xml:lang="en">
+ Specifies the screen density the configuration is defined for. The medium
+ density of traditional HVGA screens (mdpi) is defined to be approximately
+ 160dpi; low density (ldpi) is 120, and high density (hdpi) is 240. There
+ is thus a 4:3 scaling factor between each density, so a 9x9 bitmap in ldpi
+ would be 12x12 is mdpi and 16x16 in hdpi.
+ The special nodpi density that can be used in resource qualifiers is not
+ a valid keyword here.
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:simpleType>
+ <xsd:restriction base="xsd:token">
+ <xsd:enumeration value="ldpi" />
+ <xsd:enumeration value="mdpi" />
+ <xsd:enumeration value="hdpi" />
+ <xsd:enumeration value="xhdpi" />
+ </xsd:restriction>
+ </xsd:simpleType>
+ </xsd:element>
+
+ <xsd:element name="touch-type" minOccurs="0">
+ <xsd:annotation>
+ <xsd:documentation xml:lang="en">
+ Specifies the touch type the configuration is defined for.
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:simpleType>
+ <xsd:restriction base="xsd:token">
+ <xsd:enumeration value="notouch" />
+ <xsd:enumeration value="stylus" />
+ <xsd:enumeration value="finger" />
+ </xsd:restriction>
+ </xsd:simpleType>
+ </xsd:element>
+
+ <xsd:element name="keyboard-state" minOccurs="0">
+ <xsd:annotation>
+ <xsd:documentation xml:lang="en">
+ If your configuration uses a soft keyboard, use the keyssoft value.
+ If it doesn't and has a real keyboard, use keysexposed or keyshidden.
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:simpleType>
+ <xsd:restriction base="xsd:token">
+ <xsd:enumeration value="keysexposed" />
+ <xsd:enumeration value="keyshidden" />
+ <xsd:enumeration value="keyssoft" />
+ </xsd:restriction>
+ </xsd:simpleType>
+ </xsd:element>
+
+ <xsd:element name="text-input-method" minOccurs="0">
+ <xsd:annotation>
+ <xsd:documentation xml:lang="en">
+ Specifies the primary text input method the configuration is designed for.
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:simpleType>
+ <xsd:restriction base="xsd:token">
+ <xsd:enumeration value="nokeys" />
+ <xsd:enumeration value="qwerty" />
+ <xsd:enumeration value="12key" />
+ </xsd:restriction>
+ </xsd:simpleType>
+ </xsd:element>
+
+ <xsd:element name="nav-state" minOccurs="0">
+ <xsd:annotation>
+ <xsd:documentation xml:lang="en">
+ Specifies whether the primary non-touchscreen navigation control is
+ exposed or hidden.
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:simpleType>
+ <xsd:restriction base="xsd:token">
+ <xsd:enumeration value="navexposed" />
+ <xsd:enumeration value="navhidden" />
+ </xsd:restriction>
+ </xsd:simpleType>
+ </xsd:element>
+
+ <xsd:element name="nav-method" minOccurs="0">
+ <xsd:annotation>
+ <xsd:documentation xml:lang="en">
+ Specifies the primary non-touchscreen navigation method the configuration
+ is designed for.
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:simpleType>
+ <xsd:restriction base="xsd:token">
+ <xsd:enumeration value="dpad" />
+ <xsd:enumeration value="trackball" />
+ <xsd:enumeration value="wheel" />
+ <xsd:enumeration value="nonav" />
+ </xsd:restriction>
+ </xsd:simpleType>
+ </xsd:element>
+
+ <xsd:element name="screen-dimension" minOccurs="0">
+ <xsd:annotation>
+ <xsd:documentation xml:lang="en">
+ Specifies the device screen resolution, in pixels.
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:complexType>
+ <xsd:sequence minOccurs="2" maxOccurs="2">
+
+ <xsd:element name="size">
+ <xsd:simpleType>
+ <xsd:restriction base="xsd:positiveInteger" />
+ </xsd:simpleType>
+ </xsd:element>
+
+ </xsd:sequence>
+ </xsd:complexType>
+ </xsd:element>
+
+ <xsd:element name="xdpi" minOccurs="0">
+ <xsd:annotation>
+ <xsd:documentation xml:lang="en">
+ Specifies the actual density in X of the device screen.
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:simpleType>
+ <xsd:restriction base="xsd:float">
+ <xsd:minExclusive value="0" />
+ </xsd:restriction>
+ </xsd:simpleType>
+ </xsd:element>
+
+ <xsd:element name="ydpi" minOccurs="0">
+ <xsd:annotation>
+ <xsd:documentation xml:lang="en">
+ Specifies the actual density in Y of the device screen.
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:simpleType>
+ <xsd:restriction base="xsd:float">
+ <xsd:minExclusive value="0" />
+ </xsd:restriction>
+ </xsd:simpleType>
+ </xsd:element>
+
+ </xsd:all>
+ </xsd:complexType>
+
+ <!-- The type definition of a device>config element.
+ This type is basically all the element defined by parametersType and an extra
+ required "name" attribute for the user-interface configuration name.
+ -->
+ <xsd:complexType name="configType">
+ <xsd:annotation>
+ <xsd:documentation>
+ The configType defines the content of a "config" element in a "device" element.
+
+ A "config" element can have all the parameters elements defined by
+ "parameterType". It also has a required "name" attribute that indicates the
+ user-interface name for this configuration.
+ </xsd:documentation>
+ </xsd:annotation>
+
+ <xsd:complexContent>
+ <xsd:extension base="c:parametersType">
+ <xsd:attribute name="name" type="xsd:normalizedString" use="required" />
+ </xsd:extension>
+ </xsd:complexContent>
+ </xsd:complexType>
+
+</xsd:schema>
diff --git a/src/plugins/preflighting.samplechecker.androidlabel/.classpath b/src/plugins/preflighting.samplechecker.androidlabel/.classpath
new file mode 100644
index 0000000..8a8f166
--- /dev/null
+++ b/src/plugins/preflighting.samplechecker.androidlabel/.classpath
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>
+ <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/src/plugins/preflighting.samplechecker.androidlabel/.project b/src/plugins/preflighting.samplechecker.androidlabel/.project
new file mode 100644
index 0000000..60c6c3f
--- /dev/null
+++ b/src/plugins/preflighting.samplechecker.androidlabel/.project
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>com.motorolamobility.preflighting.samplechecker.androidlabel</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.ManifestBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.SchemaBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.pde.PluginNature</nature>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+</projectDescription>
diff --git a/src/plugins/preflighting.samplechecker.androidlabel/META-INF/MANIFEST.MF b/src/plugins/preflighting.samplechecker.androidlabel/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..9ff998a
--- /dev/null
+++ b/src/plugins/preflighting.samplechecker.androidlabel/META-INF/MANIFEST.MF
@@ -0,0 +1,16 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: %pluginName
+Bundle-SymbolicName: com.motorolamobility.preflighting.samplechecker.androidlabel;singleton:=true
+Bundle-Version: 2.0.0.qualifier
+Bundle-Activator: com.motorolamobility.preflighting.samplechecker.androidlabel.AndroidLabelActivator
+Bundle-Vendor: %providerName
+Require-Bundle: org.eclipse.core.runtime,
+ com.motorolamobility.preflighting.core,
+ org.apache.xerces
+Bundle-RequiredExecutionEnvironment: JavaSE-1.6
+Bundle-ActivationPolicy: lazy
+Bundle-Localization: plugin
+comment: Copyright (C) 2012 The Android Open Source Project
+
+
diff --git a/src/plugins/preflighting.samplechecker.androidlabel/build.properties b/src/plugins/preflighting.samplechecker.androidlabel/build.properties
new file mode 100644
index 0000000..8739fb3
--- /dev/null
+++ b/src/plugins/preflighting.samplechecker.androidlabel/build.properties
@@ -0,0 +1,23 @@
+#******************************************************************************
+# Copyright (C) 2012 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#******************************************************************************
+
+source.. = src/
+output.. = bin/
+bin.includes = META-INF/,\
+ .,\
+ src/,\
+ plugin.properties,\
+ plugin.xml
diff --git a/src/plugins/preflighting.samplechecker.androidlabel/plugin.properties b/src/plugins/preflighting.samplechecker.androidlabel/plugin.properties
new file mode 100644
index 0000000..b98f7e4
--- /dev/null
+++ b/src/plugins/preflighting.samplechecker.androidlabel/plugin.properties
@@ -0,0 +1,24 @@
+#******************************************************************************
+# Copyright (C) 2012 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#******************************************************************************
+
+pluginName=MOTODEV Studio App Validator Android Label Sample Checker
+providerName=Motorola Mobility, Inc.
+
+androidLabelCheckerDescription=Check whether the Android label is properly named.
+androidLabelChecker=Android Label Checker
+
+correctTextInLabelConditionDescription=\tCheck whether the Android label contains the specified text parameter.
+correctTextInLabelCondition=Correct text of Android label \ No newline at end of file
diff --git a/src/plugins/preflighting.samplechecker.androidlabel/plugin.xml b/src/plugins/preflighting.samplechecker.androidlabel/plugin.xml
new file mode 100644
index 0000000..48ab4a7
--- /dev/null
+++ b/src/plugins/preflighting.samplechecker.androidlabel/plugin.xml
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?eclipse version="3.4"?>
+
+<!--
+******************************************************************************
+ Copyright (C) 2012 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+******************************************************************************
+-->
+
+<plugin>
+ <extension
+ point="com.motorolamobility.preflighting.core.checker">
+ <checker
+ class="com.motorolamobility.preflighting.samplechecker.androidlabel.implementation.AndroidLabelChecker"
+ description="%androidLabelCheckerDescription"
+ id="androidLabel"
+ name="%androidLabelChecker">
+ <condition
+ class="com.motorolamobility.preflighting.samplechecker.androidlabel.implementation.CorrectTextInLabelCondition"
+ defaultSeverityLevel="WARNING"
+ description="%correctTextInLabelConditionDescription"
+ id="findTextInLabel"
+ name="%correctTextInLabelCondition">
+ </condition>
+ <parameter
+ description="Enter the text which will be matched against the Android Application label, in all locales."
+ id="labelText"
+ isMandatory="true"
+ name="labelText"
+ type="STRING"
+ valueDescription="LABEL_TEXT">
+ </parameter>
+ </checker>
+ </extension>
+
+</plugin>
diff --git a/src/plugins/preflighting.samplechecker.androidlabel/src/com/motorolamobility/preflighting/samplechecker/androidlabel/AndroidLabelActivator.java b/src/plugins/preflighting.samplechecker.androidlabel/src/com/motorolamobility/preflighting/samplechecker/androidlabel/AndroidLabelActivator.java
new file mode 100644
index 0000000..73cd9a7
--- /dev/null
+++ b/src/plugins/preflighting.samplechecker.androidlabel/src/com/motorolamobility/preflighting/samplechecker/androidlabel/AndroidLabelActivator.java
@@ -0,0 +1,73 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+package com.motorolamobility.preflighting.samplechecker.androidlabel;
+
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+
+import com.motorolamobility.preflighting.core.logging.PreflightingLogger;
+
+public class AndroidLabelActivator implements BundleActivator
+{
+
+ public static final String PLUGIN_ID =
+ "com.motorolamobility.preflighting.samplechecker.androidLabel"; //$NON-NLS-1$
+
+ private static BundleContext context;
+
+ private static AndroidLabelActivator plugin;
+
+ public AndroidLabelActivator()
+ {
+ plugin = this;
+ }
+
+ static BundleContext getContext()
+ {
+ return context;
+ }
+
+ public static AndroidLabelActivator getInstance()
+ {
+ return plugin;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.osgi.framework.BundleActivator#start(org.osgi.framework.BundleContext)
+ */
+ public void start(BundleContext bundleContext) throws Exception
+ {
+ PreflightingLogger.debug(AndroidLabelActivator.class,
+ "Starting MOTODEV Studio App Validator Android Label Sample Checker Plugin...");
+
+ AndroidLabelActivator.context = bundleContext;
+
+ PreflightingLogger.debug(AndroidLabelActivator.class,
+ "MOTODEV Studio App Validator Android Label Sample Checker Plugin started.");
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.osgi.framework.BundleActivator#stop(org.osgi.framework.BundleContext)
+ */
+ public void stop(BundleContext bundleContext) throws Exception
+ {
+ AndroidLabelActivator.context = null;
+ }
+
+}
diff --git a/src/plugins/preflighting.samplechecker.androidlabel/src/com/motorolamobility/preflighting/samplechecker/androidlabel/i18n/AndroidLabelCheckerNLS.java b/src/plugins/preflighting.samplechecker.androidlabel/src/com/motorolamobility/preflighting/samplechecker/androidlabel/i18n/AndroidLabelCheckerNLS.java
new file mode 100644
index 0000000..cce20db
--- /dev/null
+++ b/src/plugins/preflighting.samplechecker.androidlabel/src/com/motorolamobility/preflighting/samplechecker/androidlabel/i18n/AndroidLabelCheckerNLS.java
@@ -0,0 +1,52 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+package com.motorolamobility.preflighting.samplechecker.androidlabel.i18n;
+
+import org.eclipse.osgi.util.NLS;
+
+public class AndroidLabelCheckerNLS extends NLS
+{
+ private static final String BUNDLE_NAME =
+ "com.motorolamobility.preflighting.samplechecker.androidlabel.i18n.AndroidLabelCheckerNLS"; //$NON-NLS-1$
+
+ public static String CorrectTextInLabelCondition_AddLabelAndroidXMLLocale;
+
+ public static String CorrectTextInLabelCondition_AddTextInLabel;
+
+ public static String CorrectTextInLabelCondition_AndroidXMlMustHaveLabelRunChecker;
+
+ public static String CorrectTextInLabelCondition_ExecuteCheckerEnterLabelText;
+
+ public static String CorrectTextInLabelCondition_LabelNotContainedAndroidXML;
+
+ public static String CorrectTextInLabelCondition_LabelReferedAndroidXML;
+
+ public static String CorrectTextInLabelCondition_LabelReferedAndroidXMLDefaultLocale;
+
+ public static String CorrectTextInLabelCondition_LabelReferedAndroidXMLLocale;
+
+ public static String CorrectTextInLabelCondition_NoEnteredParamWarn;
+ static
+ {
+ // initialize resource bundle
+ NLS.initializeMessages(BUNDLE_NAME, AndroidLabelCheckerNLS.class);
+ }
+
+ private AndroidLabelCheckerNLS()
+ {
+ }
+}
diff --git a/src/plugins/preflighting.samplechecker.androidlabel/src/com/motorolamobility/preflighting/samplechecker/androidlabel/i18n/AndroidLabelCheckerNLS.properties b/src/plugins/preflighting.samplechecker.androidlabel/src/com/motorolamobility/preflighting/samplechecker/androidlabel/i18n/AndroidLabelCheckerNLS.properties
new file mode 100644
index 0000000..224210c
--- /dev/null
+++ b/src/plugins/preflighting.samplechecker.androidlabel/src/com/motorolamobility/preflighting/samplechecker/androidlabel/i18n/AndroidLabelCheckerNLS.properties
@@ -0,0 +1,25 @@
+#******************************************************************************
+# Copyright (C) 2012 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#******************************************************************************
+
+CorrectTextInLabelCondition_AddLabelAndroidXMLLocale=Verify whether the text {0} is part of the "android:label" tag within the manifest file for the "{1}" locale.
+CorrectTextInLabelCondition_AddTextInLabel=Verify whether the text {0} is part of the "android:label" tag within AndroidManifest.xml file.
+CorrectTextInLabelCondition_AndroidXMlMustHaveLabelRunChecker=The Android application must have a label in order to run this checker.
+CorrectTextInLabelCondition_ExecuteCheckerEnterLabelText=To execute this checker you must supply the parameter labelText.
+CorrectTextInLabelCondition_LabelNotContainedAndroidXML=The text {0} is not part of the "android:label" tag within the app's manifest file.
+CorrectTextInLabelCondition_LabelReferedAndroidXML=The text {0} is not part of the "android:label" tag within the manifest file for the default locale.
+CorrectTextInLabelCondition_LabelReferedAndroidXMLDefaultLocale=Verify whether the text {0} is part of the "android:label" tag in the manifest file for the default locale.
+CorrectTextInLabelCondition_LabelReferedAndroidXMLLocale=The text {0} is not part of the "android:label" tag within the manifest file for the "{1}" locale.
+CorrectTextInLabelCondition_NoEnteredParamWarn=This checker requires parameters. Please check the help for more details.
diff --git a/src/plugins/preflighting.samplechecker.androidlabel/src/com/motorolamobility/preflighting/samplechecker/androidlabel/implementation/AndroidLabelChecker.java b/src/plugins/preflighting.samplechecker.androidlabel/src/com/motorolamobility/preflighting/samplechecker/androidlabel/implementation/AndroidLabelChecker.java
new file mode 100644
index 0000000..3998a32
--- /dev/null
+++ b/src/plugins/preflighting.samplechecker.androidlabel/src/com/motorolamobility/preflighting/samplechecker/androidlabel/implementation/AndroidLabelChecker.java
@@ -0,0 +1,59 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+package com.motorolamobility.preflighting.samplechecker.androidlabel.implementation;
+
+import java.util.List;
+
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+
+import com.motorolamobility.preflighting.core.checker.Checker;
+import com.motorolamobility.preflighting.core.checker.IChecker;
+import com.motorolamobility.preflighting.core.checker.condition.CanExecuteConditionStatus;
+import com.motorolamobility.preflighting.core.validation.Parameter;
+
+/**
+ * This Checker is responsible for verify Android Label issues. In
+ * order to see more details, see its Conditions.
+ * <br><br>
+ * This checker is intended to be used as an sample so App Validator
+ * users can create their own checkers, using this as a reference.
+ *
+ */
+public class AndroidLabelChecker extends Checker implements IChecker
+{
+ /**
+ * Defines the command line parameter
+ */
+ public static final String PARAMETER_LABEL_TEXT = "labelText"; //$NON-NLS-1$
+
+ /* (non-Javadoc)
+ * @see com.motorolamobility.preflighting.core.checker.Checker#validateInputParams(java.util.List)
+ */
+ @Override
+ public IStatus validateInputParams(List<Parameter> parameters)
+ {
+ CanExecuteConditionStatus status =
+ (CanExecuteConditionStatus) super.validateInputParams(parameters);
+
+ if (status.getSeverity() == Status.ERROR)
+ {
+ status.setStatusSeverity(Status.INFO);
+ }
+ return status;
+ }
+}
diff --git a/src/plugins/preflighting.samplechecker.androidlabel/src/com/motorolamobility/preflighting/samplechecker/androidlabel/implementation/CorrectTextInLabelCondition.java b/src/plugins/preflighting.samplechecker.androidlabel/src/com/motorolamobility/preflighting/samplechecker/androidlabel/implementation/CorrectTextInLabelCondition.java
new file mode 100644
index 0000000..bf1c759
--- /dev/null
+++ b/src/plugins/preflighting.samplechecker.androidlabel/src/com/motorolamobility/preflighting/samplechecker/androidlabel/implementation/CorrectTextInLabelCondition.java
@@ -0,0 +1,377 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+package com.motorolamobility.preflighting.samplechecker.androidlabel.implementation;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+
+import org.eclipse.core.runtime.IStatus;
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import com.motorolamobility.preflighting.core.applicationdata.ApplicationData;
+import com.motorolamobility.preflighting.core.applicationdata.Element;
+import com.motorolamobility.preflighting.core.applicationdata.Element.Type;
+import com.motorolamobility.preflighting.core.applicationdata.ElementUtils;
+import com.motorolamobility.preflighting.core.applicationdata.ResourcesFolderElement;
+import com.motorolamobility.preflighting.core.applicationdata.StringsElement;
+import com.motorolamobility.preflighting.core.applicationdata.XMLElement;
+import com.motorolamobility.preflighting.core.checker.condition.CanExecuteConditionStatus;
+import com.motorolamobility.preflighting.core.checker.condition.Condition;
+import com.motorolamobility.preflighting.core.checker.condition.ICondition;
+import com.motorolamobility.preflighting.core.devicespecification.DeviceSpecification;
+import com.motorolamobility.preflighting.core.exception.PreflightingCheckerException;
+import com.motorolamobility.preflighting.core.logging.PreflightingLogger;
+import com.motorolamobility.preflighting.core.utils.CheckerUtils;
+import com.motorolamobility.preflighting.core.validation.ValidationManagerConfiguration;
+import com.motorolamobility.preflighting.core.validation.ValidationResult;
+import com.motorolamobility.preflighting.core.validation.ValidationResultData;
+import com.motorolamobility.preflighting.samplechecker.androidlabel.AndroidLabelActivator;
+import com.motorolamobility.preflighting.samplechecker.androidlabel.i18n.AndroidLabelCheckerNLS;
+
+/**
+ * This Condition verifies whether a given text is a substring of the Android Application Label.
+ * <br><br>
+ * For this condition, a text is entered using the parameter labelText. In order to
+ * have no warnings reported, all resources which the Android Application
+ * Label points out, must have this parameter as a part of its name.
+ */
+public class CorrectTextInLabelCondition extends Condition implements ICondition
+{
+ /**
+ * Represents the AndroidManifest.xml application node.
+ */
+ private static final String MANIFEST_TAG_APPLICATION = "application"; //$NON-NLS-1$
+
+ /**
+ * Represents the AndroidManifest.xml label property name.
+ */
+ private static final String MANIFEST_TAG_LABEL = "android:label"; //$NON-NLS-1$
+
+ /**
+ * Represents the prefix for String resources on the AndroidManifest.xml
+ */
+ private static final String ANDROID_STRING_IDENTIFIER = "@string/"; //$NON-NLS-1$
+
+ private String parameterText;
+
+ /**
+ * Executes the {@link AndroidLabelChecker} validations, which are:
+ * <ul>
+ * <li>The entered label must be contained in the default resource.</li>
+ * <li>The entered label must be contained in all alternative resources.</li>
+ * <li>In case the Application Label is declared inside AndroidManifest.xml, the entered label is contained in it.</li>
+ * </ul>
+ *
+ * @param data Data Structure of the Android Project. It serves for APKs and Android Projects.
+ * @param deviceSpecs Device specifications for phones.
+ * @param platformRules Rules and standards for the Android APi being used.
+ * @param valManagerConfig App Validator Manager configuration.
+ * @param results The results which will be returned from the validation performed in this method.
+ *
+ * @throws PreflightingCheckerException Exception thrown when there are unexpected problems validating
+ * the Android Application.
+ */
+ @Override
+ public void execute(ApplicationData data, List<DeviceSpecification> deviceSpecs,
+ ValidationManagerConfiguration valManagerConfig, ValidationResult results)
+ throws PreflightingCheckerException
+ {
+ // get the label from AndroidManifest.xml
+ XMLElement document = data.getManifestElement();
+ Document manifestDoc = document.getDocument();
+ Node labelNode = getLabelNode(manifestDoc);
+ String androidLabelText = labelNode.getNodeValue();
+
+ // get entered parameter
+ AndroidLabelChecker checker = (AndroidLabelChecker) getChecker();
+ parameterText =
+ checker.getParameters().get(AndroidLabelChecker.PARAMETER_LABEL_TEXT).getValue();
+
+ if (parameterText == null)
+ {
+ PreflightingLogger
+ .debug("Variable parameterText is null. Check if parameter \"labelText\" of checker androidLabel is being set.");
+ }
+
+ // handle case where the label is a resource identifier
+ if (androidLabelText.startsWith(ANDROID_STRING_IDENTIFIER))
+ {
+ analyzeLocalizedLabel(data, valManagerConfig, results, document, androidLabelText);
+ }
+ else
+ {
+ // the label is a hard coded text, check the string itself
+ analyzeHardcodedLabel(valManagerConfig, results, document, labelNode, androidLabelText);
+ }
+ }
+
+ /**
+ * Verify if the label value contains the parameterText.
+ */
+ private void analyzeHardcodedLabel(ValidationManagerConfiguration valManagerConfig,
+ ValidationResult results, XMLElement document, Node labelNode, String androidLabelText)
+ {
+ if ((parameterText != null)
+ && !androidLabelText.toLowerCase().contains(parameterText.toLowerCase()))
+ {
+ List<Integer> lineList = new ArrayList<Integer>();
+ int lineNumber = document.getNodeLineNumber(labelNode);
+ if (lineNumber > 0) //Verify if line number is available.
+ {
+ lineList.add(lineNumber);
+ }
+
+ // create validation result - error structure
+ ValidationResultData result =
+ createValidationResult(
+ AndroidLabelCheckerNLS.bind(
+ AndroidLabelCheckerNLS.CorrectTextInLabelCondition_LabelNotContainedAndroidXML,
+ parameterText),
+ AndroidLabelCheckerNLS
+ .bind(AndroidLabelCheckerNLS.CorrectTextInLabelCondition_AddTextInLabel,
+ parameterText), valManagerConfig, document,
+ parameterText, lineList);
+
+ // add created result to the results list
+ results.addValidationResult(result);
+ }
+ }
+
+ /*
+ * Verify if all string resources (from all locales) referred by the label resource identifier
+ * contains the parameterText
+ */
+ private void analyzeLocalizedLabel(ApplicationData data,
+ ValidationManagerConfiguration valManagerConfig, ValidationResult results,
+ XMLElement document, String androidLabelText)
+ {
+ // get resource identifier
+ String resId = androidLabelText.replace(ANDROID_STRING_IDENTIFIER, ""); //$NON-NLS-1$
+
+ // get resource folder
+ List<Element> folderResElements =
+ ElementUtils.getElementByType(data.getRootElement(), Type.FOLDER_RES);
+ ResourcesFolderElement resFolder =
+ folderResElements.size() > 0 ? (ResourcesFolderElement) folderResElements.get(0)
+ : null;
+
+ // check default locale
+ StringsElement defaultElements = resFolder.getDefaultValuesElement();
+ if (defaultElements != null)
+ {
+ Object value = defaultElements.getValue(resId);
+ androidLabelText = (value != null) ? (String) value : ""; //$NON-NLS-1$
+
+ // execute the checker condition - the entered text must be within the label
+ if ((parameterText != null)
+ && !androidLabelText.toLowerCase().contains(parameterText.toLowerCase()))
+ {
+ // create validation result - error structure
+ ValidationResultData result =
+ createValidationResult(
+ AndroidLabelCheckerNLS.bind(
+ AndroidLabelCheckerNLS.CorrectTextInLabelCondition_LabelReferedAndroidXML,
+ parameterText),
+ AndroidLabelCheckerNLS
+ .bind(AndroidLabelCheckerNLS.CorrectTextInLabelCondition_LabelReferedAndroidXMLDefaultLocale,
+ parameterText), valManagerConfig, document,
+ parameterText, new ArrayList<Integer>());
+
+ // add created result to the results list
+ results.addValidationResult(result);
+ }
+ }
+
+ // check non-default locales
+ if ((resFolder != null) && (resFolder.getAvailableLocales() != null)
+ && (resFolder.getAvailableLocales().size() > 0))
+ {
+ for (Locale locale : resFolder.getAvailableLocales())
+ {
+ String localeText =
+ locale.getLanguage()
+ + ((locale.getCountry() != null)
+ && (locale.getCountry().length() > 0)
+ ? "_" + locale.getCountry() : ""); //$NON-NLS-1$ //$NON-NLS-2$
+
+ // get the android label for each locale
+ StringsElement stringsElement = resFolder.getValuesElement(locale);
+
+ Object value = stringsElement.getValue(resId);
+ androidLabelText = (value != null) ? (String) value : ""; //$NON-NLS-1$
+
+ // execute the checker condition - the entered text must be within the label
+ if (!androidLabelText.toLowerCase().contains(parameterText.toLowerCase()))
+ {
+ // create validation result - error structure
+ ValidationResultData result =
+ createValidationResult(
+ AndroidLabelCheckerNLS.bind(
+ AndroidLabelCheckerNLS.CorrectTextInLabelCondition_LabelReferedAndroidXMLLocale,
+ parameterText, localeText),
+ AndroidLabelCheckerNLS
+ .bind(AndroidLabelCheckerNLS.CorrectTextInLabelCondition_AddLabelAndroidXMLLocale,
+ parameterText, localeText), valManagerConfig,
+ document, parameterText, new ArrayList<Integer>());
+
+ // add created result to the results list
+ results.addValidationResult(result);
+ }
+ }
+ }
+ }
+
+ /**
+ * In order to execute the checker, first several conditions must be verified.
+ * <ul>
+ * <li>There must be an AndroidManifest.xml file in the Android Project.</li>
+ * <li>There must be a label in the AndroidManifest.xml file.</li>
+ * <li>There must be a value for the parameter labelText.</li>
+ * </ul>
+ *
+ * @param data Data structure holding all files, classes and resources of the
+ * APK or Project.
+ * @param deviceSpecs List of device specifications.
+ *
+ * @return Returns the {@link IStatus} which states whether the Checker
+ * can be run.
+ *
+ * @throws PreflightingCheckerException Exception thrown in case there is any problem
+ * verifying the conditions.
+ */
+ @Override
+ public CanExecuteConditionStatus canExecute(ApplicationData data,
+ List<DeviceSpecification> deviceSpecs) throws PreflightingCheckerException
+ {
+ // first check the manifest file status
+ CanExecuteConditionStatus status =
+ CheckerUtils.isAndroidManifestFileExistent(data, getId());
+
+ // there must be parameters AndroidLabelChecker.PARAMETER_LABEL_TEXT set
+ String labelParameterValue =
+ getChecker().getParameters().get(AndroidLabelChecker.PARAMETER_LABEL_TEXT)
+ .getValue();
+ if (labelParameterValue == null)
+ {
+ status =
+ new CanExecuteConditionStatus(IStatus.INFO, AndroidLabelActivator.PLUGIN_ID,
+ AndroidLabelCheckerNLS.CorrectTextInLabelCondition_NoEnteredParamWarn);
+ }
+
+ if (status.getSeverity() != IStatus.ERROR)
+ {
+ // there must be a parameter set
+ AndroidLabelChecker checker = (AndroidLabelChecker) getChecker();
+ if ((checker.getParameters() != null)
+ && checker.getParameters()
+ .containsKey(AndroidLabelChecker.PARAMETER_LABEL_TEXT))
+ {
+
+ XMLElement document = data.getManifestElement();
+ Document manifestDoc = document.getDocument();
+
+ // there must be a label in order to allow the checker execution
+ Node labelNode = getLabelNode(manifestDoc);
+ if ((labelNode != null) && (labelNode.getNodeValue() != null))
+ {
+ status = new CanExecuteConditionStatus(IStatus.OK, getChecker().getId(), ""); //$NON-NLS-1$
+ }
+ else
+ {
+ status =
+ new CanExecuteConditionStatus(
+ IStatus.ERROR,
+ AndroidLabelActivator.PLUGIN_ID,
+ AndroidLabelCheckerNLS.CorrectTextInLabelCondition_AndroidXMlMustHaveLabelRunChecker);
+ }
+ }
+ else
+ {
+ status =
+ new CanExecuteConditionStatus(
+ IStatus.ERROR,
+ AndroidLabelActivator.PLUGIN_ID,
+ AndroidLabelCheckerNLS.CorrectTextInLabelCondition_ExecuteCheckerEnterLabelText);
+ }
+ }
+
+ status.setConditionId(getId());
+
+ return status;
+ }
+
+ /**
+ * Create the {@link ValidationResultData} which represents the error that
+ * has occurred during the validation. It will be reported to the user.
+ *
+ * @param issueDescription The description of the error.
+ * @param quickFixSuggestion The quick-fix-suggestion for the error.
+ * @param valManagerConfig App Validator Manager configuration.
+ * @param document AndroidManifest.xml in a {@link Document} object.
+ * @param parameterText The text entered by the user as a parameter.
+ * @param lineList The list of lines where the error has occurred.
+ *
+ * @return Returns the {@link ValidationResultData} which holds the error
+ * to be displayed by the App Validator.
+ */
+ private ValidationResultData createValidationResult(String issueDescription,
+ String quickFixSuggestion, ValidationManagerConfiguration valManagerConfig,
+ XMLElement document, String parameterText, List<Integer> lineList)
+ {
+ ValidationResultData result = new ValidationResultData();
+ result.setConditionID(getId());
+ result.addFileToIssueLines(document.getFile(), lineList);
+ result.setIssueDescription(issueDescription);
+ result.setQuickFixSuggestion(quickFixSuggestion);
+ result.setInfoURL("http://developer.motorola.com/docstools/library/motodev-app-validator/#androidLabel-findTextInLabel"); //$NON-NLS-1$
+ result.setSeverity(getSeverityLevel());
+ return result;
+ }
+
+ /**
+ * Get the label {@link Node} from the AndroidManifext.xml file
+ * represented as a {@link Document}.
+ * <br>
+ * In case nothing is found, <code>null</code> is returned.
+ *
+ * @param manifestDoc AdnroidManifest.xml file as a {@link Document} where
+ * the label will be sought.
+ *
+ * @return Returns the AndroidManifest.xml label {@link Node}.
+ */
+ private Node getLabelNode(Document manifestDoc)
+ {
+ final int APPLICATION_NODE_INDEX = 0;
+
+ Node labelNode = null;
+
+ NodeList applicationNodes = manifestDoc.getElementsByTagName(MANIFEST_TAG_APPLICATION);
+
+ // there must be one application note, get it
+ Node applicationNode = applicationNodes.item(APPLICATION_NODE_INDEX);
+ if (applicationNode != null)
+ {
+ // get label node
+ labelNode = applicationNode.getAttributes().getNamedItem(MANIFEST_TAG_LABEL);
+ }
+
+ return labelNode;
+ }
+}
diff --git a/src/plugins/preflighting.samplecheckers.findviewbyid.ui/.classpath b/src/plugins/preflighting.samplecheckers.findviewbyid.ui/.classpath
new file mode 100644
index 0000000..2d1a430
--- /dev/null
+++ b/src/plugins/preflighting.samplecheckers.findviewbyid.ui/.classpath
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.5"/>
+ <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/src/plugins/preflighting.samplecheckers.findviewbyid.ui/.project b/src/plugins/preflighting.samplecheckers.findviewbyid.ui/.project
new file mode 100644
index 0000000..45b41e6
--- /dev/null
+++ b/src/plugins/preflighting.samplecheckers.findviewbyid.ui/.project
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>com.motorolamobility.preflighting.samplechecker.findviewbyid.ui</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.ManifestBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.SchemaBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.pde.PluginNature</nature>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+</projectDescription>
diff --git a/src/plugins/preflighting.samplecheckers.findviewbyid.ui/META-INF/MANIFEST.MF b/src/plugins/preflighting.samplecheckers.findviewbyid.ui/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..425b08c
--- /dev/null
+++ b/src/plugins/preflighting.samplecheckers.findviewbyid.ui/META-INF/MANIFEST.MF
@@ -0,0 +1,20 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: %pluginName
+Bundle-SymbolicName: com.motorolamobility.preflighting.samplechecker.findviewbyid.ui;singleton:=true
+Bundle-Version: 2.0.0.qualifier
+Bundle-Vendor: %providerName
+Bundle-RequiredExecutionEnvironment: JavaSE-1.6
+Require-Bundle: org.eclipse.ui.ide,
+ org.eclipse.core.runtime,
+ org.eclipse.jdt.core,
+ org.eclipse.ui.workbench.texteditor,
+ org.eclipse.core.resources,
+ org.eclipse.jdt.ui,
+ org.eclipse.jface.text,
+ org.eclipse.swt,
+ com.motorola.studio.android.common,
+ org.eclipse.core.commands
+Bundle-Localization: plugin
+comment: Copyright (C) 2012 The Android Open Source Project
+Bundle-ActivationPolicy: lazy
diff --git a/src/plugins/preflighting.samplecheckers.findviewbyid.ui/build.properties b/src/plugins/preflighting.samplecheckers.findviewbyid.ui/build.properties
new file mode 100644
index 0000000..329287d
--- /dev/null
+++ b/src/plugins/preflighting.samplecheckers.findviewbyid.ui/build.properties
@@ -0,0 +1,11 @@
+source.. = src/
+output.. = bin/
+bin.includes = META-INF/,\
+ .,\
+ plugin.xml,\
+ plugin.properties,\
+ src/
+src.includes = src/,\
+ META-INF/,\
+ plugin.properties,\
+ plugin.xml
diff --git a/src/plugins/preflighting.samplecheckers.findviewbyid.ui/plugin.properties b/src/plugins/preflighting.samplecheckers.findviewbyid.ui/plugin.properties
new file mode 100644
index 0000000..d1a5cdd
--- /dev/null
+++ b/src/plugins/preflighting.samplecheckers.findviewbyid.ui/plugin.properties
@@ -0,0 +1,3 @@
+pluginName=MOTODEV Studio App Validator SDK Unnecessary findViewById Sample Checker QuickFix Support
+providerName=Motorola Mobility, Inc.
+markerName=App Validator Problem \ No newline at end of file
diff --git a/src/plugins/preflighting.samplecheckers.findviewbyid.ui/plugin.xml b/src/plugins/preflighting.samplecheckers.findviewbyid.ui/plugin.xml
new file mode 100644
index 0000000..f72056b
--- /dev/null
+++ b/src/plugins/preflighting.samplecheckers.findviewbyid.ui/plugin.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?eclipse version="3.4"?>
+<plugin>
+ <extension
+ id="findViewByIdPermissionsMarker"
+ name="markerName"
+ point="org.eclipse.core.resources.markers">
+ <!-- if you don't use the 'name' attribute with the value 'App Validator Problem', your marker will stand out
+ from other App Validator markers, which is not indicated -->
+ <super
+ type="com.motorolamobility.preflighting.checkers.ui.appValidatorMarker">
+ </super>
+ </extension>
+ <extension
+ point="org.eclipse.ui.ide.markerResolution">
+ <markerResolutionGenerator
+ class="com.motorolamobility.preflighting.samplechecker.findviewbyid.quickfix.FindViewByIdMarkerGenerator"
+ markerType="com.motorolamobility.preflighting.samplechecker.findviewbyid.ui.findViewByIdPermissionsMarker">
+ </markerResolutionGenerator>
+ </extension>
+</plugin>
diff --git a/src/plugins/preflighting.samplecheckers.findviewbyid.ui/src/com/motorolamobility/preflighting/samplechecker/findviewbyid/quickfix/FindViewByIdMarkerGenerator.java b/src/plugins/preflighting.samplecheckers.findviewbyid.ui/src/com/motorolamobility/preflighting/samplechecker/findviewbyid/quickfix/FindViewByIdMarkerGenerator.java
new file mode 100644
index 0000000..adf78bc
--- /dev/null
+++ b/src/plugins/preflighting.samplecheckers.findviewbyid.ui/src/com/motorolamobility/preflighting/samplechecker/findviewbyid/quickfix/FindViewByIdMarkerGenerator.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.preflighting.samplechecker.findviewbyid.quickfix;
+
+import static com.motorola.studio.android.common.log.StudioLogger.error;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.core.resources.IMarker;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.ui.IMarkerResolution2;
+import org.eclipse.ui.IMarkerResolutionGenerator2;
+
+public class FindViewByIdMarkerGenerator implements IMarkerResolutionGenerator2
+{
+
+ private static final String MARKER_ID =
+ "com.motorolamobility.preflighting.samplechecker.findviewbyid.ui.findViewByIdPermissionsMarker";
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.IMarkerResolutionGenerator#getResolutions(org.eclipse.core.resources.IMarker)
+ */
+ public IMarkerResolution2[] getResolutions(IMarker marker)
+ {
+ List<IMarkerResolution2> resolutions = new ArrayList<IMarkerResolution2>();
+
+ try
+ {
+ if (marker.getType().equals(MARKER_ID))
+ {
+ resolutions.add(new FindViewByIdMarkerResolution());
+ }
+ }
+ catch (CoreException e)
+ {
+ error(getClass(), "Could not get marker type", e); //$NON-NLS-1$
+ }
+ return resolutions.toArray(new IMarkerResolution2[resolutions.size()]);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.ui.IMarkerResolutionGenerator2#hasResolutions(org.eclipse.core.resources.IMarker)
+ */
+ public boolean hasResolutions(IMarker marker)
+ {
+ boolean hasResolutions = false;
+ try
+ {
+ if (marker.getType().equals(MARKER_ID))
+ {
+ hasResolutions = true;
+ }
+ }
+ catch (CoreException e)
+ {
+ error(getClass(), "Could not get marker type", e); //$NON-NLS-1$
+ }
+
+ return hasResolutions;
+ }
+}
diff --git a/src/plugins/preflighting.samplecheckers.findviewbyid.ui/src/com/motorolamobility/preflighting/samplechecker/findviewbyid/quickfix/FindViewByIdMarkerResolution.java b/src/plugins/preflighting.samplecheckers.findviewbyid.ui/src/com/motorolamobility/preflighting/samplechecker/findviewbyid/quickfix/FindViewByIdMarkerResolution.java
new file mode 100644
index 0000000..f29138d
--- /dev/null
+++ b/src/plugins/preflighting.samplecheckers.findviewbyid.ui/src/com/motorolamobility/preflighting/samplechecker/findviewbyid/quickfix/FindViewByIdMarkerResolution.java
@@ -0,0 +1,315 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.preflighting.samplechecker.findviewbyid.quickfix;
+
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IMarker;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.jdt.core.ICompilationUnit;
+import org.eclipse.jdt.core.JavaCore;
+import org.eclipse.jdt.core.JavaModelException;
+import org.eclipse.jdt.core.dom.AST;
+import org.eclipse.jdt.core.dom.ASTNode;
+import org.eclipse.jdt.core.dom.ASTParser;
+import org.eclipse.jdt.core.dom.ASTVisitor;
+import org.eclipse.jdt.core.dom.Assignment;
+import org.eclipse.jdt.core.dom.Block;
+import org.eclipse.jdt.core.dom.CompilationUnit;
+import org.eclipse.jdt.core.dom.DoStatement;
+import org.eclipse.jdt.core.dom.EnhancedForStatement;
+import org.eclipse.jdt.core.dom.Expression;
+import org.eclipse.jdt.core.dom.ExpressionStatement;
+import org.eclipse.jdt.core.dom.ForStatement;
+import org.eclipse.jdt.core.dom.MethodInvocation;
+import org.eclipse.jdt.core.dom.SimpleName;
+import org.eclipse.jdt.core.dom.Statement;
+import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
+import org.eclipse.jdt.core.dom.VariableDeclarationStatement;
+import org.eclipse.jdt.core.dom.WhileStatement;
+import org.eclipse.jdt.ui.JavaUI;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.text.edits.TextEdit;
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.IMarkerResolution2;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.texteditor.AbstractTextEditor;
+
+import com.motorola.studio.android.common.utilities.EclipseUtils;
+import com.motorolamobility.preflighting.samplechecker.findviewbyid.quickfix.i18n.MessagesNLS;
+
+/**
+ * MarkerResolution responsible for moving the findViewByID call outside the loop.
+ */
+public class FindViewByIdMarkerResolution implements IMarkerResolution2
+{
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.ui.IMarkerResolution#getLabel()
+ */
+ public String getLabel()
+ {
+ return MessagesNLS.FindViewByIdMarkerResolution_Label;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.ui.IMarkerResolution#run(org.eclipse.core.resources.IMarker)
+ */
+ public void run(IMarker marker)
+ {
+ IResource resource = marker.getResource();
+ final ICompilationUnit iCompilationUnit =
+ JavaCore.createCompilationUnitFrom((IFile) resource);
+
+ ASTParser parser = ASTParser.newParser(AST.JLS3);
+ parser.setProject(JavaCore.create(resource.getProject()));
+ parser.setResolveBindings(true);
+ parser.setSource(iCompilationUnit);
+ parser.setStatementsRecovery(true);
+ parser.setBindingsRecovery(true);
+ final CompilationUnit compUnit = (CompilationUnit) parser.createAST(null);
+
+ try
+ {
+ final int lineNumber = marker.getAttribute(IMarker.LINE_NUMBER, -1);
+ MethodInvocation invokedMethod = null;
+
+ //Look for the invokedMethod that shall be moved
+ invokedMethod = getMethodInvocation(compUnit, lineNumber, invokedMethod);
+
+ IEditorPart editor = openEditor(iCompilationUnit);
+
+ ASTNode loopNode = getLoopNode(invokedMethod);
+
+ //Retrieve block parent for the loop statement.
+ Block targetBlock = (Block) loopNode.getParent();
+ List<Statement> statements = targetBlock.statements();
+ int i = getLoopStatementIndex(loopNode, statements);
+
+ //Add the node before the loop.
+ compUnit.recordModifications();
+ ASTNode invokedMethodStatement = getInvokedStatement(invokedMethod);
+ final VariableDeclarationStatement varDeclarationStatement[] =
+ new VariableDeclarationStatement[1];
+
+ //Verify if the invoke statement contains a variable attribution
+ if (invokedMethodStatement instanceof ExpressionStatement)
+ {
+ ExpressionStatement expressionStatement =
+ (ExpressionStatement) invokedMethodStatement;
+ Expression expression = expressionStatement.getExpression();
+ if (expression instanceof Assignment)
+ {
+ Expression leftHandSide = ((Assignment) expression).getLeftHandSide();
+ if (leftHandSide instanceof SimpleName) //Search for the variable declaration
+ {
+ SimpleName simpleName = (SimpleName) leftHandSide;
+ final String varName = simpleName.getIdentifier();
+
+ loopNode.accept(new ASTVisitor()
+ {
+ @Override
+ public boolean visit(VariableDeclarationStatement node) //Visit all variable declarations inside the loop looking for the variable which receives the findViewById result
+ {
+ List<VariableDeclarationFragment> fragments = node.fragments();
+ for (VariableDeclarationFragment fragment : fragments)
+ {
+ if (fragment.getName().getIdentifier().equals(varName))
+ {
+ varDeclarationStatement[0] = node;
+ break;
+ }
+ }
+ return super.visit(node);
+ }
+ });
+ }
+ }
+ }
+
+ //Variable is declared inside the loop, now let's move the variable declaration if needed
+ if (varDeclarationStatement[0] != null)
+ {
+ ASTNode varDeclarationSubTree =
+ ASTNode.copySubtree(targetBlock.getAST(), varDeclarationStatement[0]);
+ statements.add(i, (Statement) varDeclarationSubTree.getRoot());
+
+ //Delete the node inside loop.
+ varDeclarationStatement[0].delete();
+ i++;
+ }
+ ASTNode copySubtree = ASTNode.copySubtree(targetBlock.getAST(), invokedMethodStatement);
+ statements.add(i, (Statement) copySubtree.getRoot());
+
+ //Delete the node inside loop.
+ invokedMethodStatement.delete();
+
+ // apply changes to file
+ final Map<?, ?> mapOptions = JavaCore.create(resource.getProject()).getOptions(true);
+ final IDocument document =
+ ((AbstractTextEditor) editor).getDocumentProvider().getDocument(
+ editor.getEditorInput());
+ PlatformUI.getWorkbench().getDisplay().syncExec(new Runnable()
+ {
+
+ public void run()
+ {
+ try
+ {
+ TextEdit edit = compUnit.rewrite(document, mapOptions);
+ iCompilationUnit.applyTextEdit(edit, new NullProgressMonitor());
+ }
+ catch (JavaModelException e)
+ {
+ EclipseUtils.showErrorDialog(
+ MessagesNLS.FindViewByIdMarkerResolution_Error_Msg_Title,
+ MessagesNLS.FindViewByIdMarkerResolution_Error_Aplying_Changes
+ + e.getMessage());
+ }
+
+ }
+ });
+
+ marker.delete();
+
+ }
+ catch (Exception e)
+ {
+ EclipseUtils.showErrorDialog(MessagesNLS.FindViewByIdMarkerResolution_Error_Msg_Title,
+ MessagesNLS.FindViewByIdMarkerResolution_Error_Could_Not_Fix_Code);
+ }
+ }
+
+ private IEditorPart openEditor(final ICompilationUnit iCompilationUnit)
+ {
+ IEditorPart editor = null;
+ try
+ {
+ editor = JavaUI.openInEditor(iCompilationUnit);
+ }
+ catch (Exception e)
+ {
+ EclipseUtils.showErrorDialog(MessagesNLS.FindViewByIdMarkerResolution_Error_Msg_Title,
+ MessagesNLS.FindViewByIdMarkerResolution_Error_Unable_To_Open_Editor);
+ }
+ return editor;
+ }
+
+ private MethodInvocation getMethodInvocation(final CompilationUnit compUnit,
+ final int lineNumber, MethodInvocation invokedMethod)
+ {
+ final MethodInvocation[] tempMethodInvocation = new MethodInvocation[1];
+ compUnit.accept(new ASTVisitor()
+ {
+ @Override
+ public boolean visit(MethodInvocation node)
+ {
+ if (compUnit.getLineNumber(node.getStartPosition()) == lineNumber)
+ {
+ tempMethodInvocation[0] = node;
+ }
+ return super.visit(node);
+ };
+ });
+ if (tempMethodInvocation[0] != null)
+ {
+ invokedMethod = tempMethodInvocation[0];
+ }
+ return invokedMethod;
+ }
+
+ /*
+ * Look for Statement containing the invokedMethod
+ */
+ private ASTNode getInvokedStatement(MethodInvocation invokedMethod)
+ {
+ boolean found = false;
+ ASTNode parent = invokedMethod.getParent();
+ do
+ {
+ if ((parent instanceof Statement))
+ {
+ found = true;
+ }
+ else
+ {
+ parent = parent.getParent();
+ }
+ }
+ while (!found);
+ return parent;
+ }
+
+ /*
+ * Resturns the index of the loop statement, inside a list of statements
+ */
+ private int getLoopStatementIndex(ASTNode loopNode, List<Statement> statements)
+ {
+ int i = 0;
+ for (i = 0; i < statements.size(); i++)
+ {
+ Statement statement = statements.get(i);
+ if (statement.equals(loopNode))
+ {
+ break;
+ }
+ }
+ return i;
+ }
+
+ /*
+ * Find the loop node that contains the called method
+ */
+ private ASTNode getLoopNode(MethodInvocation invokedMethod)
+ {
+ boolean found = false;
+ ASTNode parent = invokedMethod.getParent();
+ do
+ {
+ parent = parent.getParent();
+ if ((parent instanceof ForStatement) || (parent instanceof DoStatement)
+ || (parent instanceof WhileStatement)
+ || (parent instanceof EnhancedForStatement))
+ {
+ found = true;
+ }
+ }
+ while (!found);
+ return parent;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.ui.IMarkerResolution2#getDescription()
+ */
+ public String getDescription()
+ {
+ return MessagesNLS.FindViewByIdMarkerResolution_Description;
+ }
+
+ public Image getImage()
+ {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+}
diff --git a/src/plugins/preflighting.samplecheckers.findviewbyid.ui/src/com/motorolamobility/preflighting/samplechecker/findviewbyid/quickfix/i18n/MessagesNLS.java b/src/plugins/preflighting.samplecheckers.findviewbyid.ui/src/com/motorolamobility/preflighting/samplechecker/findviewbyid/quickfix/i18n/MessagesNLS.java
new file mode 100644
index 0000000..8fa29c1
--- /dev/null
+++ b/src/plugins/preflighting.samplecheckers.findviewbyid.ui/src/com/motorolamobility/preflighting/samplechecker/findviewbyid/quickfix/i18n/MessagesNLS.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.preflighting.samplechecker.findviewbyid.quickfix.i18n;
+
+import org.eclipse.osgi.util.NLS;
+
+public class MessagesNLS extends NLS
+{
+ private static final String BUNDLE_NAME =
+ "com.motorolamobility.preflighting.samplechecker.findviewbyid.quickfix.i18n.messages"; //$NON-NLS-1$
+
+ public static String FindViewByIdMarkerResolution_Description;
+
+ public static String FindViewByIdMarkerResolution_Error_Aplying_Changes;
+
+ public static String FindViewByIdMarkerResolution_Error_Could_Not_Fix_Code;
+
+ public static String FindViewByIdMarkerResolution_Error_Msg_Title;
+
+ public static String FindViewByIdMarkerResolution_Error_Unable_To_Open_Editor;
+
+ public static String FindViewByIdMarkerResolution_Label;
+ static
+ {
+ // initialize resource bundle
+ NLS.initializeMessages(BUNDLE_NAME, MessagesNLS.class);
+ }
+
+ private MessagesNLS()
+ {
+ }
+}
diff --git a/src/plugins/preflighting.samplecheckers.findviewbyid.ui/src/com/motorolamobility/preflighting/samplechecker/findviewbyid/quickfix/i18n/messages.properties b/src/plugins/preflighting.samplecheckers.findviewbyid.ui/src/com/motorolamobility/preflighting/samplechecker/findviewbyid/quickfix/i18n/messages.properties
new file mode 100644
index 0000000..09ab88a
--- /dev/null
+++ b/src/plugins/preflighting.samplecheckers.findviewbyid.ui/src/com/motorolamobility/preflighting/samplechecker/findviewbyid/quickfix/i18n/messages.properties
@@ -0,0 +1,6 @@
+FindViewByIdMarkerResolution_Description=findViewById should be called outside loop codes for performance improvement.
+FindViewByIdMarkerResolution_Error_Aplying_Changes=Error applying changes:
+FindViewByIdMarkerResolution_Error_Could_Not_Fix_Code=An error has occurred while trying to fix the problem. Try Running the appValidator again and don't made any changes to the code.
+FindViewByIdMarkerResolution_Error_Msg_Title=Marker resolution fail.
+FindViewByIdMarkerResolution_Error_Unable_To_Open_Editor=Unable to open editor or bring it to front for Java file while trying to generate code from layout xml file
+FindViewByIdMarkerResolution_Label=Move findViewById call outside the loop block
diff --git a/src/plugins/preflighting.samplecheckers.findviewbyid/.classpath b/src/plugins/preflighting.samplecheckers.findviewbyid/.classpath
new file mode 100644
index 0000000..2d1a430
--- /dev/null
+++ b/src/plugins/preflighting.samplecheckers.findviewbyid/.classpath
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.5"/>
+ <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/src/plugins/preflighting.samplecheckers.findviewbyid/.project b/src/plugins/preflighting.samplecheckers.findviewbyid/.project
new file mode 100644
index 0000000..6603fc5
--- /dev/null
+++ b/src/plugins/preflighting.samplecheckers.findviewbyid/.project
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>com.motorolamobility.preflighting.samplechecker.findviewbyid</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.ManifestBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.SchemaBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.pde.PluginNature</nature>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+</projectDescription>
diff --git a/src/plugins/preflighting.samplecheckers.findviewbyid/META-INF/MANIFEST.MF b/src/plugins/preflighting.samplecheckers.findviewbyid/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..768677f
--- /dev/null
+++ b/src/plugins/preflighting.samplecheckers.findviewbyid/META-INF/MANIFEST.MF
@@ -0,0 +1,23 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: %pluginName
+Bundle-SymbolicName: com.motorolamobility.preflighting.samplechecker.findviewbyid;singleton:=true
+Bundle-Version: 2.0.0.qualifier
+Bundle-Activator: com.motorolamobility.preflighting.samplechecker.findviewbyid.SampleCheckersActivator
+Bundle-Vendor: %providerName
+Require-Bundle: org.eclipse.core.runtime,
+ com.motorolamobility.preflighting.core,
+ org.eclipse.jdt.core
+Bundle-RequiredExecutionEnvironment: JavaSE-1.6
+Bundle-ActivationPolicy: lazy
+Bundle-Localization: plugin
+comment: Copyright (C) 2012 The Android Open Source Project
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+ http://www.apache.org/licenses/LICENSE-2.0
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/src/plugins/preflighting.samplecheckers.findviewbyid/build.properties b/src/plugins/preflighting.samplecheckers.findviewbyid/build.properties
new file mode 100644
index 0000000..6f09706
--- /dev/null
+++ b/src/plugins/preflighting.samplecheckers.findviewbyid/build.properties
@@ -0,0 +1,23 @@
+#******************************************************************************
+# Copyright (C) 2012 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#******************************************************************************
+
+source.. = src/
+output.. = bin/
+bin.includes = META-INF/,\
+ .,\
+ plugin.xml,\
+ plugin.properties,\
+ src/
diff --git a/src/plugins/preflighting.samplecheckers.findviewbyid/plugin.properties b/src/plugins/preflighting.samplecheckers.findviewbyid/plugin.properties
new file mode 100644
index 0000000..2ba209d
--- /dev/null
+++ b/src/plugins/preflighting.samplecheckers.findviewbyid/plugin.properties
@@ -0,0 +1,8 @@
+pluginName=MOTODEV Studio App Validator SDK Unnecessary findViewById Sample Checker
+providerName=Motorola Mobility, Inc.
+
+unnecessaryFindViewByIdCheckerDescription=Looks for findViewById statements that can be moved to reduce CPU processing.
+unnecessaryFindViewByIdChecker=Unnecessary findViewById Checker
+
+FindViewByIdInsideLoopsConditionDescription=Checks for findViewById statements that are inside a loop block (for, foreach, while, do-while) that could possibly be moved outside the loop, to reduce CPU processing.
+FindViewByIdInsideLoopsCondition=Unnecessary findViewById inside loops condition \ No newline at end of file
diff --git a/src/plugins/preflighting.samplecheckers.findviewbyid/plugin.xml b/src/plugins/preflighting.samplecheckers.findviewbyid/plugin.xml
new file mode 100644
index 0000000..f1b9199
--- /dev/null
+++ b/src/plugins/preflighting.samplecheckers.findviewbyid/plugin.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?eclipse version="3.4"?>
+
+<!--
+ Copyright (C) 2012 The Android Open Source Project
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+ http://www.apache.org/licenses/LICENSE-2.0
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<plugin>
+ <extension
+ point="com.motorolamobility.preflighting.core.checker">
+ <checker
+ class="com.motorolamobility.preflighting.core.checker.Checker"
+ description="%unnecessaryFindViewByIdCheckerDescription"
+ id="unnecessaryFindViewById"
+ name="%unnecessaryFindViewByIdChecker">
+ <condition
+ class="com.motorolamobility.preflighting.samplechecker.findviewbyid.implementation.FindViewByIdInLoop"
+ defaultSeverityLevel="WARNING"
+ description="%FindViewByIdInsideLoopsConditionDescription"
+ id="findViewByIdInsideLoops"
+ markerType="com.motorolamobility.preflighting.samplechecker.findviewbyid.ui.findViewByIdPermissionsMarker"
+ name="%FindViewByIdInsideLoopsCondition">
+ </condition>
+ </checker>
+ </extension>
+
+</plugin>
diff --git a/src/plugins/preflighting.samplecheckers.findviewbyid/src/com/motorolamobility/preflighting/samplechecker/findviewbyid/SampleCheckersActivator.java b/src/plugins/preflighting.samplecheckers.findviewbyid/src/com/motorolamobility/preflighting/samplechecker/findviewbyid/SampleCheckersActivator.java
new file mode 100644
index 0000000..74a7bdb
--- /dev/null
+++ b/src/plugins/preflighting.samplecheckers.findviewbyid/src/com/motorolamobility/preflighting/samplechecker/findviewbyid/SampleCheckersActivator.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.motorolamobility.preflighting.samplechecker.findviewbyid;
+
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+
+import com.motorolamobility.preflighting.core.logging.PreflightingLogger;
+
+/**
+ * The activator class controls the plug-in life cycle
+ */
+public class SampleCheckersActivator implements BundleActivator
+{
+
+ // The plug-in ID
+ public static final String PLUGIN_ID =
+ "com.motorolamobility.preflighting.samplechecker.findviewbyid"; //$NON-NLS-1$
+
+ private static BundleContext context;
+
+ // The shared instance
+ private static SampleCheckersActivator plugin;
+
+ /**
+ * The constructor
+ */
+ public SampleCheckersActivator()
+ {
+ plugin = this;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.osgi.framework.BundleActivator#start(org.osgi.framework.BundleContext)
+ */
+ public void start(BundleContext context) throws Exception
+ {
+ PreflightingLogger
+ .debug(SampleCheckersActivator.class,
+ "Starting MOTODEV Studio App Validator SDK Unnecessary findViewById Sample Checker...");
+
+ SampleCheckersActivator.context = context;
+
+ PreflightingLogger
+ .debug(SampleCheckersActivator.class,
+ "MOTODEV Studio App Validator SDK Unnecessary findViewById Sample Checker started...");
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.osgi.framework.BundleActivator#start(org.osgi.framework.BundleContext)
+ */
+ public void stop(BundleContext context) throws Exception
+ {
+ SampleCheckersActivator.context = null;
+ }
+
+ static BundleContext getContext()
+ {
+ return context;
+ }
+
+ /**
+ * Returns the shared instance
+ *
+ * @return the shared instance
+ */
+ public static SampleCheckersActivator getDefault()
+ {
+ return plugin;
+ }
+
+}
diff --git a/src/plugins/preflighting.samplecheckers.findviewbyid/src/com/motorolamobility/preflighting/samplechecker/findviewbyid/i18n/Messages.java b/src/plugins/preflighting.samplecheckers.findviewbyid/src/com/motorolamobility/preflighting/samplechecker/findviewbyid/i18n/Messages.java
new file mode 100644
index 0000000..69aeb4a
--- /dev/null
+++ b/src/plugins/preflighting.samplecheckers.findviewbyid/src/com/motorolamobility/preflighting/samplechecker/findviewbyid/i18n/Messages.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.motorolamobility.preflighting.samplechecker.findviewbyid.i18n;
+
+import org.eclipse.osgi.util.NLS;
+
+/**
+ * I18n for the checkers
+ */
+public class Messages extends NLS
+{
+ private static final String BUNDLE_NAME =
+ "com.motorolamobility.preflighting.samplechecker.findviewbyid.i18n.messages"; //$NON-NLS-1$
+
+ public static String FindViewByIdInsideLoopCondition_IssueDescription;
+
+ public static String FindViewByIdInsideLoopCondition_QuickFixSuggestion;
+ static
+ {
+ // initialize resource bundle
+ NLS.initializeMessages(BUNDLE_NAME, Messages.class);
+ }
+
+ private Messages()
+ {
+ }
+}
diff --git a/src/plugins/preflighting.samplecheckers.findviewbyid/src/com/motorolamobility/preflighting/samplechecker/findviewbyid/i18n/messages.properties b/src/plugins/preflighting.samplecheckers.findviewbyid/src/com/motorolamobility/preflighting/samplechecker/findviewbyid/i18n/messages.properties
new file mode 100644
index 0000000..33be510
--- /dev/null
+++ b/src/plugins/preflighting.samplecheckers.findviewbyid/src/com/motorolamobility/preflighting/samplechecker/findviewbyid/i18n/messages.properties
@@ -0,0 +1,18 @@
+###############################################################################
+# Copyright (C) 2012 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+###############################################################################
+
+FindViewByIdInsideLoopCondition_IssueDescription=Method findViewById is being called inside a loop.
+FindViewByIdInsideLoopCondition_QuickFixSuggestion=Determine whether the findViewById call can be placed outside the loop to improve performance.
diff --git a/src/plugins/preflighting.samplecheckers.findviewbyid/src/com/motorolamobility/preflighting/samplechecker/findviewbyid/implementation/FindViewByIdInLoop.java b/src/plugins/preflighting.samplecheckers.findviewbyid/src/com/motorolamobility/preflighting/samplechecker/findviewbyid/implementation/FindViewByIdInLoop.java
new file mode 100644
index 0000000..09b874d
--- /dev/null
+++ b/src/plugins/preflighting.samplecheckers.findviewbyid/src/com/motorolamobility/preflighting/samplechecker/findviewbyid/implementation/FindViewByIdInLoop.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.motorolamobility.preflighting.samplechecker.findviewbyid.implementation;
+
+import java.util.List;
+
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.jdt.core.dom.CompilationUnit;
+
+import com.motorolamobility.preflighting.core.applicationdata.ApplicationData;
+import com.motorolamobility.preflighting.core.checker.condition.CanExecuteConditionStatus;
+import com.motorolamobility.preflighting.core.checker.condition.Condition;
+import com.motorolamobility.preflighting.core.checker.condition.ICondition;
+import com.motorolamobility.preflighting.core.devicespecification.DeviceSpecification;
+import com.motorolamobility.preflighting.core.exception.PreflightingCheckerException;
+import com.motorolamobility.preflighting.core.logging.PreflightingLogger;
+import com.motorolamobility.preflighting.core.utils.CheckerUtils;
+import com.motorolamobility.preflighting.core.validation.ValidationManagerConfiguration;
+import com.motorolamobility.preflighting.core.validation.ValidationResult;
+import com.motorolamobility.preflighting.samplechecker.findviewbyid.SampleCheckersActivator;
+
+/**
+ * This condition shows how to use utility class {@link CheckerUtils} to identify if java is available before checker execution.
+ * <br>
+ * The example illustrates how to check Android code through App Validator:
+ * <ul>
+ * <li>the access of CompilationUnits from project,</li>
+ * <li>the ASTVisitor to get information about the code</li>
+ * <li>how to report results through a ValidationResultData</li>
+ * </ul>
+ *
+ * The checker searches for <code>findViewById</code> statements which are inside a loop block (<code>for, extended for, while, do-while</code>)
+ * that could be possibly placed outside the loop to reduce CPU processing.
+ */
+public class FindViewByIdInLoop extends Condition implements ICondition
+{
+
+ /**
+ * @see com.motorolamobility.preflighting.core.checker.condition.Condition#canExecute(com.motorolamobility.preflighting.core.applicationdata.ApplicationData, java.util.List)
+ */
+ @Override
+ public CanExecuteConditionStatus canExecute(ApplicationData data,
+ List<DeviceSpecification> deviceSpecs) throws PreflightingCheckerException
+ {
+ CanExecuteConditionStatus status = null;
+ //Check if we are analysing an Android source code Project or an APK
+ if (data.isProject())
+ {
+ //Verify if model is complete to allow checker analysis in the code
+ status = CheckerUtils.isJavaModelComplete(data, getId());
+ }
+ else
+ {
+ //this checker is devoted only for Android projects (not for APK)
+ status =
+ new CanExecuteConditionStatus(
+ IStatus.ERROR,
+ SampleCheckersActivator.PLUGIN_ID,
+ "This condition runs only for Android Project (not for APK). Please check the help for more details.");
+ }
+ status.setConditionId(getId());
+ return status;
+ }
+
+ /**
+ * @see com.motorolamobility.preflighting.core.checker.condition.Condition#execute(com.motorolamobility.preflighting.core.applicationdata.ApplicationData, java.util.List, com.motorolamobility.preflighting.core.validation.ValidationManagerConfiguration)
+ */
+ @Override
+ public void execute(ApplicationData data, List<DeviceSpecification> deviceSpecs,
+ ValidationManagerConfiguration valManagerConfig, ValidationResult results)
+ throws PreflightingCheckerException
+ {
+ //get source compilation units to visit the code
+ List<CompilationUnit> compilationUnits = data.getProjectCompilationUnits();
+ if (compilationUnits != null)
+ {
+ for (CompilationUnit compilationUnit : compilationUnits)
+ {
+ //visit each source file to find issues
+ if (compilationUnit != null)
+ {
+ compilationUnit.accept(new FindViewByIdVisitor(getId(), getSeverityLevel(),
+ getMarkerType(), results, compilationUnit));
+ }
+ }
+ }
+ else
+ {
+ //print in the console (if info LEVEL set for verbosity of App Validator output)
+ PreflightingLogger.info("No compilation unit found to visit the code");
+ }
+ }
+}
diff --git a/src/plugins/preflighting.samplecheckers.findviewbyid/src/com/motorolamobility/preflighting/samplechecker/findviewbyid/implementation/FindViewByIdVisitor.java b/src/plugins/preflighting.samplecheckers.findviewbyid/src/com/motorolamobility/preflighting/samplechecker/findviewbyid/implementation/FindViewByIdVisitor.java
new file mode 100644
index 0000000..d31ad09
--- /dev/null
+++ b/src/plugins/preflighting.samplecheckers.findviewbyid/src/com/motorolamobility/preflighting/samplechecker/findviewbyid/implementation/FindViewByIdVisitor.java
@@ -0,0 +1,203 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.preflighting.samplechecker.findviewbyid.implementation;
+
+import java.io.File;
+import java.util.ArrayList;
+
+import org.eclipse.jdt.core.dom.ASTNode;
+import org.eclipse.jdt.core.dom.ASTVisitor;
+import org.eclipse.jdt.core.dom.CompilationUnit;
+import org.eclipse.jdt.core.dom.DoStatement;
+import org.eclipse.jdt.core.dom.EnhancedForStatement;
+import org.eclipse.jdt.core.dom.ForStatement;
+import org.eclipse.jdt.core.dom.IMethodBinding;
+import org.eclipse.jdt.core.dom.MethodDeclaration;
+import org.eclipse.jdt.core.dom.MethodInvocation;
+import org.eclipse.jdt.core.dom.WhileStatement;
+
+import com.motorolamobility.preflighting.core.logging.PreflightingLogger;
+import com.motorolamobility.preflighting.core.utils.CheckerUtils;
+import com.motorolamobility.preflighting.core.validation.ValidationResult;
+import com.motorolamobility.preflighting.core.validation.ValidationResultData;
+import com.motorolamobility.preflighting.core.validation.ValidationResultData.SEVERITY;
+import com.motorolamobility.preflighting.samplechecker.findviewbyid.i18n.Messages;
+
+/**
+ * Visitor specialized to identify <code>findViewById</code> statements inside loops.
+ */
+public class FindViewByIdVisitor extends ASTVisitor
+{
+
+ private static final String R_CONSTANT = "R."; //$NON-NLS-1$
+
+ private static final String FIND_VIEW_BY_ID_METHOD_BINDING =
+ "public android.view.View findViewById(int)"; //$NON-NLS-1$
+
+ private final ValidationResult results;
+
+ private final CompilationUnit compilationUnit;
+
+ private final String id;
+
+ private final SEVERITY severityLevel;
+
+ private String markerType;
+
+ /**
+ * Construct a new FindViewByIdVisitor with the given parameters.
+ *
+ * @param id the condition id
+ * @param severityLevel the condition default severity level
+ * @param markerType object to keep the checker results
+ * @param results
+ * @param valManagerConfig manager responsible to format output of App Validator
+ * @param compilationUnit object representing the source code to be analyzed
+ */
+ public FindViewByIdVisitor(String id, SEVERITY severityLevel, String markerType,
+ ValidationResult results, CompilationUnit compilationUnit)
+ {
+ this.id = id;
+ this.severityLevel = severityLevel;
+ this.markerType = markerType;
+ this.results = results;
+ this.compilationUnit = compilationUnit;
+ }
+
+ /**
+ * Visit method invocations to find invocation of <code>findViewById</code>.
+ *
+ * @param invoked method that is being called and will be analyzed to check if it is a <code>findViewById</code> call.
+ */
+ @Override
+ public boolean visit(MethodInvocation invoked)
+ {
+ //find the signature of the method that is being called
+ IMethodBinding methodBinding = invoked.resolveMethodBinding();
+ if (methodBinding != null)
+ {
+ if (methodBinding.toString().trim().equalsIgnoreCase(FIND_VIEW_BY_ID_METHOD_BINDING))
+ {
+ //according to the signature, findViewById was called
+ ASTNode parentNode = invoked.getParent();
+ Object firstElement = invoked.arguments().get(0);
+ if (firstElement != null && firstElement.toString() != null
+ && firstElement.toString().startsWith(R_CONSTANT))
+ {
+ //argument has a constant R. (indicating access that could be possibly done outside the loop)
+ if (hasLoopStatementAsParent(parentNode))
+ {
+ //print in the console (if DEBUG level set for verbosity of App Validator output)
+ PreflightingLogger
+ .debug("Found findViewById invocation inside loop statement");
+
+ //call is inside a loop statement - raise issue
+ ValidationResultData validationResult = createResult(invoked);
+ results.addValidationResult(validationResult);
+ }
+ }
+ }
+ }
+ return super.visit(invoked);
+ }
+
+ /**
+ * Recursively check if <code>node</code> has a loop statement as parent.
+ * @param node to check if parent is a loop statement
+ * @return <code>true</code> if finds (<code>for, extended for, while, do-while</code>) as parent of <code>node</code>, <code>false</code> otherwise
+ */
+ private boolean hasLoopStatementAsParent(ASTNode node)
+ {
+ if (node == null)
+ {
+ return false;
+ }
+ else
+ {
+ ASTNode parentNode = node.getParent();
+ if (parentNode == null || parentNode instanceof MethodDeclaration)
+ {
+ //base case of recursion: reached top level (method declaration or class declaration) without finding a loop statement
+ return false;
+ }
+ else if (isLoopStatement(parentNode))
+ {
+ //base case of recursion: reached loop statement
+ return true;
+ }
+ else
+ {
+ //continue search : go to the parent node
+ return hasLoopStatementAsParent(parentNode);
+ }
+ }
+ }
+
+ /**
+ * Check if the {@link ASTNode} is a loop block (<code>for, extended for, while, do-while</code>).
+ *
+ * @param statement node to verify
+ * @return <code>true</code> if (<code>for, extended for, while, do-while</code>), <code>false</code> otherwise
+ *
+ * @see org.eclipse.jdt.core.dom.ForStatement
+ * @see org.eclipse.jdt.core.dom.DoStatement
+ * @see org.eclipse.jdt.core.dom.WhileStatement
+ * @see org.eclipse.jdt.core.dom.EnhancedForStatement
+ */
+ private boolean isLoopStatement(ASTNode statement)
+ {
+ return statement instanceof ForStatement || statement instanceof DoStatement
+ || statement instanceof WhileStatement || statement instanceof EnhancedForStatement;
+ }
+
+ /**
+ * Create the App Validator issues found for the checker.
+ *
+ * @param invoked method where the problem occurs
+ * @return data containing the issue
+ */
+ private ValidationResultData createResult(MethodInvocation invoked)
+ {
+ ValidationResultData resultData = new ValidationResultData();
+
+ //set the condition related to the problem
+ resultData.setConditionID(id);
+ resultData.setMarkerType(markerType);
+
+ //set the lines where the problem occurred
+ ArrayList<Integer> lines = new ArrayList<Integer>();
+ int issuedLine = compilationUnit.getLineNumber(invoked.getStartPosition());
+ if (issuedLine != -1)
+ {
+ lines.add(issuedLine);
+ }
+
+ //set the source file associated with the issue
+ File javaFile = (File) compilationUnit.getProperty(CheckerUtils.JAVA_FILE_PROPERTY);
+ resultData.addFileToIssueLines(javaFile, lines);
+
+ //set description, quick fix, and severity level
+ resultData.setIssueDescription(Messages.FindViewByIdInsideLoopCondition_IssueDescription);
+ resultData
+ .setQuickFixSuggestion(Messages.FindViewByIdInsideLoopCondition_QuickFixSuggestion);
+ resultData.setSeverity(severityLevel);
+
+ //set the URL with the help associated with developer page regarding this checker
+ resultData
+ .setInfoURL("http://developer.motorola.com/docstools/library/motodev-app-validator/#unnecessaryFindViewById-unnecessaryFindViewByIdInsideLoops");
+ return resultData;
+ }
+} \ No newline at end of file
diff --git a/src/plugins/preflighting.sdk/.project b/src/plugins/preflighting.sdk/.project
new file mode 100644
index 0000000..c528b27
--- /dev/null
+++ b/src/plugins/preflighting.sdk/.project
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>com.motorolamobility.preflighting.sdk</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.pde.ManifestBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.SchemaBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.pde.PluginNature</nature>
+ </natures>
+</projectDescription>
diff --git a/src/plugins/preflighting.sdk/META-INF/MANIFEST.MF b/src/plugins/preflighting.sdk/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..47e6078
--- /dev/null
+++ b/src/plugins/preflighting.sdk/META-INF/MANIFEST.MF
@@ -0,0 +1,8 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: %Bundle-Name
+Bundle-SymbolicName: com.motorolamobility.preflighting.sdk;singleton:=true
+Bundle-Version: 2.0.0.qualifier
+Bundle-Vendor: %Bundle-Vendor
+Bundle-Localization: plugin
+Require-Bundle: org.eclipse.pde.core
diff --git a/src/plugins/preflighting.sdk/about.ini b/src/plugins/preflighting.sdk/about.ini
new file mode 100644
index 0000000..aedf9a9
--- /dev/null
+++ b/src/plugins/preflighting.sdk/about.ini
@@ -0,0 +1,24 @@
+# about.ini
+# contains information about a feature
+# java.io.Properties file (ISO 8859-1 with "\" escapes)
+# "%key" are externalized strings defined in about.properties
+# This file does not need to be translated.
+
+# Property "aboutText" contains blurb for "About" dialog (translated)
+aboutText=%blurb
+
+# Property "windowImage" contains path to window icon (16x16)
+# needed for primary features only
+
+# Property "featureImage" contains path to feature image (32x32)
+featureImage=resources/plate32.png
+
+# Property "aboutImage" contains path to product image (500x330 or 115x164)
+# needed for primary features only
+
+# Property "appName" contains name of the application (translated)
+# needed for primary features only
+
+# Property "welcomePerspective" contains the id of the perspective in which the
+# welcome page is to be opened.
+# optional \ No newline at end of file
diff --git a/src/plugins/preflighting.sdk/about.properties b/src/plugins/preflighting.sdk/about.properties
new file mode 100644
index 0000000..f821fd2
--- /dev/null
+++ b/src/plugins/preflighting.sdk/about.properties
@@ -0,0 +1,7 @@
+blurb=MOTODEV Studio for Android SDK\n\
+\n\
+Version: {featureVersion}\n\
+\n\
+Build id: {0}\n\
+\n\
+Copyright (C) 2012 The Android Open Source Project \ No newline at end of file
diff --git a/src/plugins/preflighting.sdk/build.properties b/src/plugins/preflighting.sdk/build.properties
new file mode 100644
index 0000000..1157433
--- /dev/null
+++ b/src/plugins/preflighting.sdk/build.properties
@@ -0,0 +1,3 @@
+bin.includes = META-INF/,\
+ plugin.properties,\
+ plugin.xml
diff --git a/src/plugins/preflighting.sdk/plugin.properties b/src/plugins/preflighting.sdk/plugin.properties
new file mode 100644
index 0000000..7ea1198
--- /dev/null
+++ b/src/plugins/preflighting.sdk/plugin.properties
@@ -0,0 +1,3 @@
+#Properties file for com.motorolamobility.preflighting.sdk
+Bundle-Vendor = Motorola Mobility, Inc.
+Bundle-Name = MOTODEV Studio App Validator SDK \ No newline at end of file
diff --git a/src/plugins/preflighting.sdk/plugin.xml b/src/plugins/preflighting.sdk/plugin.xml
new file mode 100644
index 0000000..867dbac
--- /dev/null
+++ b/src/plugins/preflighting.sdk/plugin.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?eclipse version="3.4"?>
+<plugin>
+ <extension
+ point="org.eclipse.pde.core.javadoc">
+ <javadoc
+ archive="false"
+ path="doc">
+ <plugin
+ id="com.motorolamobility.preflighting.core">
+ </plugin>
+ <plugin
+ id="com.motorolamobility.preflighting">
+ </plugin>
+ </javadoc>
+ </extension>
+
+</plugin>
diff --git a/src/plugins/preflighting.sdk/resources/plate32.png b/src/plugins/preflighting.sdk/resources/plate32.png
new file mode 100644
index 0000000..4bff905
--- /dev/null
+++ b/src/plugins/preflighting.sdk/resources/plate32.png
Binary files differ
diff --git a/src/plugins/preflighting.ui/.classpath b/src/plugins/preflighting.ui/.classpath
new file mode 100644
index 0000000..8a8f166
--- /dev/null
+++ b/src/plugins/preflighting.ui/.classpath
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>
+ <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/src/plugins/preflighting.ui/.project b/src/plugins/preflighting.ui/.project
new file mode 100644
index 0000000..1f714e8
--- /dev/null
+++ b/src/plugins/preflighting.ui/.project
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>com.motorolamobility.preflighting.ui</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.ManifestBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.SchemaBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.pde.PluginNature</nature>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+</projectDescription>
diff --git a/src/plugins/preflighting.ui/META-INF/MANIFEST.MF b/src/plugins/preflighting.ui/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..d63bf2b
--- /dev/null
+++ b/src/plugins/preflighting.ui/META-INF/MANIFEST.MF
@@ -0,0 +1,26 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: %Bundle-Name
+Bundle-SymbolicName: com.motorolamobility.preflighting.ui;singleton:=true
+Bundle-Version: 2.0.0.qualifier
+Bundle-Vendor: %Bundle-Vendor
+Bundle-RequiredExecutionEnvironment: JavaSE-1.6
+Require-Bundle: com.motorolamobility.preflighting,
+ com.motorolamobility.preflighting.core,
+ org.eclipse.core.runtime,
+ org.eclipse.core.resources,
+ org.eclipse.ui,
+ org.eclipse.ui.console,
+ org.eclipse.ui.ide,
+ com.motorolamobility.preflighting.checkers,
+ org.eclipse.jdt.core,
+ org.eclipse.text,
+ org.eclipse.jdt.ui,
+ org.eclipse.ui.workbench.texteditor,
+ com.motorola.studio.android.common
+Bundle-Localization: plugin
+Bundle-Activator: com.motorolamobility.preflighting.ui.PreflightingUIPlugin
+Bundle-ActivationPolicy: lazy
+Export-Package: com.motorolamobility.preflighting.ui
+Import-Package: org.eclipse.ui.model,
+ org.eclipse.ui.views.navigator
diff --git a/src/plugins/preflighting.ui/about.ini b/src/plugins/preflighting.ui/about.ini
new file mode 100644
index 0000000..d020e1e
--- /dev/null
+++ b/src/plugins/preflighting.ui/about.ini
@@ -0,0 +1,40 @@
+#
+# Copyright (C) 2012 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+# about.ini
+# contains information about a feature
+# java.io.Properties file (ISO 8859-1 with "\" escapes)
+# "%key" are externalized strings defined in about.properties
+# This file does not need to be translated.
+
+# Property "aboutText" contains blurb for "About" dialog (translated)
+aboutText=%blurb
+
+# Property "windowImage" contains path to window icon (16x16)
+# needed for primary features only
+
+# Property "featureImage" contains path to feature image (32x32)
+featureImage=resources/plate32.png
+
+# Property "aboutImage" contains path to product image (500x330 or 115x164)
+# needed for primary features only
+
+# Property "appName" contains name of the application (translated)
+# needed for primary features only
+
+# Property "welcomePerspective" contains the id of the perspective in which the
+# welcome page is to be opened.
+# optional \ No newline at end of file
diff --git a/src/plugins/preflighting.ui/about.properties b/src/plugins/preflighting.ui/about.properties
new file mode 100644
index 0000000..29463dc
--- /dev/null
+++ b/src/plugins/preflighting.ui/about.properties
@@ -0,0 +1,23 @@
+#
+# Copyright (C) 2012 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+blurb=MOTODEV Studio App Validator Tool UI Integration\n\
+\n\
+Version: {featureVersion}\n\
+\n\
+Build id: {0}\n\
+\n\
+Copyright (C) 2012 The Android Open Source Project \ No newline at end of file
diff --git a/src/plugins/preflighting.ui/build.properties b/src/plugins/preflighting.ui/build.properties
new file mode 100644
index 0000000..3940c58
--- /dev/null
+++ b/src/plugins/preflighting.ui/build.properties
@@ -0,0 +1,26 @@
+#
+# Copyright (C) 2012 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+source.. = src/
+output.. = bin/
+bin.includes = META-INF/,\
+ .,\
+ plugin.xml,\
+ plugin.properties,\
+ icons/,\
+ resources/,\
+ about.ini,\
+ about.properties
diff --git a/src/plugins/preflighting.ui/icons/MOTODEVAppValidator_128x128.png b/src/plugins/preflighting.ui/icons/MOTODEVAppValidator_128x128.png
new file mode 100644
index 0000000..384d502
--- /dev/null
+++ b/src/plugins/preflighting.ui/icons/MOTODEVAppValidator_128x128.png
Binary files differ
diff --git a/src/plugins/preflighting.ui/icons/MOTODEVAppValidator_16x16.png b/src/plugins/preflighting.ui/icons/MOTODEVAppValidator_16x16.png
new file mode 100644
index 0000000..104dc76
--- /dev/null
+++ b/src/plugins/preflighting.ui/icons/MOTODEVAppValidator_16x16.png
Binary files differ
diff --git a/src/plugins/preflighting.ui/icons/MOTODEVAppValidator_16x16_clear.png b/src/plugins/preflighting.ui/icons/MOTODEVAppValidator_16x16_clear.png
new file mode 100644
index 0000000..5343479
--- /dev/null
+++ b/src/plugins/preflighting.ui/icons/MOTODEVAppValidator_16x16_clear.png
Binary files differ
diff --git a/src/plugins/preflighting.ui/icons/MOTODEVAppValidator_24x24.png b/src/plugins/preflighting.ui/icons/MOTODEVAppValidator_24x24.png
new file mode 100644
index 0000000..71b9fd4
--- /dev/null
+++ b/src/plugins/preflighting.ui/icons/MOTODEVAppValidator_24x24.png
Binary files differ
diff --git a/src/plugins/preflighting.ui/icons/MOTODEVAppValidator_256x256.png b/src/plugins/preflighting.ui/icons/MOTODEVAppValidator_256x256.png
new file mode 100644
index 0000000..029b9cd
--- /dev/null
+++ b/src/plugins/preflighting.ui/icons/MOTODEVAppValidator_256x256.png
Binary files differ
diff --git a/src/plugins/preflighting.ui/icons/MOTODEVAppValidator_32x32.png b/src/plugins/preflighting.ui/icons/MOTODEVAppValidator_32x32.png
new file mode 100644
index 0000000..2b15f8b
--- /dev/null
+++ b/src/plugins/preflighting.ui/icons/MOTODEVAppValidator_32x32.png
Binary files differ
diff --git a/src/plugins/preflighting.ui/icons/MOTODEVAppValidator_48x48.png b/src/plugins/preflighting.ui/icons/MOTODEVAppValidator_48x48.png
new file mode 100644
index 0000000..e2e725a
--- /dev/null
+++ b/src/plugins/preflighting.ui/icons/MOTODEVAppValidator_48x48.png
Binary files differ
diff --git a/src/plugins/preflighting.ui/icons/MOTODEVAppValidator_512x512.png b/src/plugins/preflighting.ui/icons/MOTODEVAppValidator_512x512.png
new file mode 100644
index 0000000..f587d11
--- /dev/null
+++ b/src/plugins/preflighting.ui/icons/MOTODEVAppValidator_512x512.png
Binary files differ
diff --git a/src/plugins/preflighting.ui/icons/MOTODEVAppValidator_64x64.png b/src/plugins/preflighting.ui/icons/MOTODEVAppValidator_64x64.png
new file mode 100644
index 0000000..25d479e
--- /dev/null
+++ b/src/plugins/preflighting.ui/icons/MOTODEVAppValidator_64x64.png
Binary files differ
diff --git a/src/plugins/preflighting.ui/icons/MOTODEVAppValidator_75x66.png b/src/plugins/preflighting.ui/icons/MOTODEVAppValidator_75x66.png
new file mode 100644
index 0000000..30fe19e
--- /dev/null
+++ b/src/plugins/preflighting.ui/icons/MOTODEVAppValidator_75x66.png
Binary files differ
diff --git a/src/plugins/preflighting.ui/icons/command_validate_apps_16x16.png b/src/plugins/preflighting.ui/icons/command_validate_apps_16x16.png
new file mode 100644
index 0000000..8a6b268
--- /dev/null
+++ b/src/plugins/preflighting.ui/icons/command_validate_apps_16x16.png
Binary files differ
diff --git a/src/plugins/preflighting.ui/icons/command_validate_apps_32x32.png b/src/plugins/preflighting.ui/icons/command_validate_apps_32x32.png
new file mode 100644
index 0000000..52861d0
--- /dev/null
+++ b/src/plugins/preflighting.ui/icons/command_validate_apps_32x32.png
Binary files differ
diff --git a/src/plugins/preflighting.ui/icons/command_validate_projs_16x16.png b/src/plugins/preflighting.ui/icons/command_validate_projs_16x16.png
new file mode 100644
index 0000000..8291ca4
--- /dev/null
+++ b/src/plugins/preflighting.ui/icons/command_validate_projs_16x16.png
Binary files differ
diff --git a/src/plugins/preflighting.ui/icons/command_validate_projs_32x32.png b/src/plugins/preflighting.ui/icons/command_validate_projs_32x32.png
new file mode 100644
index 0000000..338a3cd
--- /dev/null
+++ b/src/plugins/preflighting.ui/icons/command_validate_projs_32x32.png
Binary files differ
diff --git a/src/plugins/preflighting.ui/plugin.properties b/src/plugins/preflighting.ui/plugin.properties
new file mode 100644
index 0000000..1b667a2
--- /dev/null
+++ b/src/plugins/preflighting.ui/plugin.properties
@@ -0,0 +1,33 @@
+#
+# Copyright (C) 2012 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+#Properties file for com.motorolamobility.preflighting.ui
+Bundle-Vendor = Motorola Mobility, Inc.
+Bundle-Name = MOTODEV Studio App Validator UI
+
+analyze_apk_command.name = Validate Android Application
+analyze_apk_command.description = Validate an Android application
+command.line.page.name = Android App Validator
+
+appValidatorSubMenuLabel=App Validator
+
+motodevmenu.appvalidator.apk=Validate Android Apps...
+motodevmenu.appvalidator.apk.tooltip=Validates one or more Android packages (.apks)
+toolbar.appvalidator.apk=Validate Android Apps
+
+motodevmenu.appvalidator.project=Validate Android Project(s)...
+motodevmenu.appvalidator.project.tooltip=Validates one or more Android projects
+toolbar.appvalidator.project=Validate Android Project(s) \ No newline at end of file
diff --git a/src/plugins/preflighting.ui/plugin.xml b/src/plugins/preflighting.ui/plugin.xml
new file mode 100644
index 0000000..afa4f60
--- /dev/null
+++ b/src/plugins/preflighting.ui/plugin.xml
@@ -0,0 +1,197 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?eclipse version="3.4"?>
+<!--
+ Copyright (C) 2012 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<plugin>
+ <extension
+ point="org.eclipse.ui.menus">
+ <menuContribution
+ allPopups="true"
+ locationURI="popup:org.eclipse.ui.popup.any">
+ <command
+ commandId="com.motorolamobility.preflighting.validateapp"
+ icon="icons/MOTODEVAppValidator_16x16.png"
+ id="com.motorolamobility.preflighting.command.validateapp"
+ label="%analyze_apk_command.name"
+ style="push">
+ <visibleWhen
+ checkEnabled="false">
+ <and>
+ <count
+ value="1">
+ </count>
+ <or>
+ <iterate
+ ifEmpty="false"
+ operator="and">
+ <adapt
+ type="org.eclipse.core.resources.IResource">
+ <test
+ forcePluginActivation="true"
+ property="org.eclipse.core.resources.extension"
+ value="apk">
+ </test>
+ </adapt>
+ </iterate>
+ <iterate
+ ifEmpty="false"
+ operator="and">
+ <adapt
+ type="org.eclipse.core.resources.IProject">
+ <test
+ property="org.eclipse.core.resources.open">
+ </test>
+ <test
+ forcePluginActivation="true"
+ property="org.eclipse.core.resources.projectNature"
+ value="com.android.ide.eclipse.adt.AndroidNature">
+ </test>
+ </adapt>
+ </iterate>
+ </or>
+ </and>
+ </visibleWhen>
+ </command>
+ </menuContribution>
+ <menuContribution
+ allPopups="false"
+ locationURI="menu:motorolaMenu?after=appValidatorSeparator">
+ <menu
+ id="studioAndroidAppValidatorMenu"
+ label="%appValidatorSubMenuLabel">
+ <command
+ commandId="com.motorolamobility.preflighting.validateapkdialog"
+ icon="icons/command_validate_apps_16x16.png"
+ label="%motodevmenu.appvalidator.apk"
+ style="push"
+ tooltip="%motodevmenu.appvalidator.apk.tooltip">
+ </command>
+ <command
+ commandId="com.motorolamobility.preflighting.validateprojectdialog"
+ icon="icons/command_validate_projs_16x16.png"
+ label="%motodevmenu.appvalidator.project"
+ style="push"
+ tooltip="%motodevmenu.appvalidator.project.tooltip">
+ </command>
+ </menu>
+ </menuContribution>
+ <menuContribution
+ allPopups="false"
+ locationURI="popup:org.eclipse.core.runtime.xml.source.RulerContext">
+ <command
+ commandId="org.eclipse.jdt.ui.edit.text.java.correction.assist.proposals"
+ style="push">
+ </command>
+ </menuContribution>
+ <menuContribution
+ allPopups="false"
+ locationURI="toolbar:org.eclipse.ui.main.toolbar">
+ <toolbar
+ id="appValidatorToolbar">
+ <visibleWhen
+ checkEnabled="false">
+ <with
+ variable="activeWorkbenchWindow.activePerspective">
+ <or>
+ <equals
+ value="com.motorola.studio.android.perspective">
+ </equals>
+ <equals
+ value="org.eclipse.jdt.ui.JavaPerspective">
+ </equals>
+ </or>
+ </with>
+ </visibleWhen>
+ </toolbar>
+ </menuContribution>
+ <menuContribution
+ allPopups="false"
+ locationURI="toolbar:appValidatorToolbar">
+ <command
+ commandId="com.motorolamobility.preflighting.validateapkdialog"
+ icon="icons/command_validate_apps_16x16.png"
+ label="%toolbar.appvalidator.apk"
+ style="push"
+ tooltip="%toolbar.appvalidator.apk">
+ </command>
+ <command
+ commandId="com.motorolamobility.preflighting.validateprojectdialog"
+ icon="icons/command_validate_projs_16x16.png"
+ label="%toolbar.appvalidator.project"
+ style="push"
+ tooltip="%toolbar.appvalidator.project">
+ </command>
+ </menuContribution>
+ <menuContribution
+ allPopups="false"
+ locationURI="menu:project">
+ <separator
+ name="projectMenuAppValidatorSeparator"
+ visible="true">
+ </separator>
+ <menu
+ id="projectAppValidatorMenu"
+ label="%appValidatorSubMenuLabel">
+ <command
+ commandId="com.motorolamobility.preflighting.validateapkdialog"
+ icon="icons/command_validate_apps_16x16.png"
+ label="%motodevmenu.appvalidator.apk"
+ style="push"
+ tooltip="%motodevmenu.appvalidator.apk.tooltip">
+ </command>
+ <command
+ commandId="com.motorolamobility.preflighting.validateprojectdialog"
+ icon="icons/command_validate_projs_16x16.png"
+ label="%motodevmenu.appvalidator.project"
+ style="push"
+ tooltip="%motodevmenu.appvalidator.project.tooltip">
+ </command>
+ </menu>
+ </menuContribution>
+
+ </extension>
+ <extension
+ point="org.eclipse.ui.commands">
+ <command
+ defaultHandler="com.motorolamobility.preflighting.ui.handlers.AnalyzeApkHandler"
+ description="%analyze_apk_command.description"
+ id="com.motorolamobility.preflighting.validateapp"
+ name="%analyze_apk_command.name">
+ </command>
+ <command
+ defaultHandler="com.motorolamobility.preflighting.ui.handlers.OpenProjectDialogHandler"
+ description="%motodevmenu.appvalidator.project.tooltip"
+ id="com.motorolamobility.preflighting.validateprojectdialog"
+ name="%motodevmenu.appvalidator.project">
+ </command>
+ <command
+ defaultHandler="com.motorolamobility.preflighting.ui.handlers.OpenApkDialogHandler"
+ description="%motodevmenu.appvalidator.apk.tooltip"
+ id="com.motorolamobility.preflighting.validateapkdialog"
+ name="%motodevmenu.appvalidator.apk">
+ </command>
+ </extension>
+ <extension
+ point="org.eclipse.ui.preferencePages">
+ <page
+ category="com.motorola.studio.platform.ui.preference"
+ class="com.motorolamobility.preflighting.ui.CommandLinePreferencePage"
+ id="com.motorolamobility.preflighting.ui.commandLinePreferencePage"
+ name="%command.line.page.name">
+ </page>
+ </extension>
+</plugin>
diff --git a/src/plugins/preflighting.ui/resources/plate32.png b/src/plugins/preflighting.ui/resources/plate32.png
new file mode 100644
index 0000000..4bff905
--- /dev/null
+++ b/src/plugins/preflighting.ui/resources/plate32.png
Binary files differ
diff --git a/src/plugins/preflighting.ui/src/com/motorolamobility/preflighting/ui/AppValidatorClearActionDelegate.java b/src/plugins/preflighting.ui/src/com/motorolamobility/preflighting/ui/AppValidatorClearActionDelegate.java
new file mode 100644
index 0000000..fef0c4e
--- /dev/null
+++ b/src/plugins/preflighting.ui/src/com/motorolamobility/preflighting/ui/AppValidatorClearActionDelegate.java
@@ -0,0 +1,96 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+package com.motorolamobility.preflighting.ui;
+
+import java.net.URL;
+
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.jface.action.Action;
+import org.eclipse.jface.action.IAction;
+import org.eclipse.jface.action.IToolBarManager;
+import org.eclipse.jface.action.Separator;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.ui.IActionBars;
+import org.eclipse.ui.IViewActionDelegate;
+import org.eclipse.ui.IViewPart;
+
+import com.motorolamobility.preflighting.core.logging.PreflightingLogger;
+import com.motorolamobility.preflighting.ui.handlers.AnalyzeApkHandler;
+import com.motorolamobility.preflighting.ui.i18n.PreflightingUiNLS;
+
+public class AppValidatorClearActionDelegate implements IViewActionDelegate
+{
+ public void run(IAction action)
+ {
+ //do nothing
+ }
+
+ public void selectionChanged(IAction action, ISelection selection)
+ {
+ //do nothing
+ }
+
+ public void init(IViewPart view)
+ {
+ IActionBars actionBar = view.getViewSite().getActionBars();
+ IToolBarManager toolBar = actionBar.getToolBarManager();
+ //defines action
+ Action action = new Action()
+ {
+ @Override
+ public void run()
+ {
+ try
+ {
+ IProject[] projects = ResourcesPlugin.getWorkspace().getRoot().getProjects();
+ //clear app validator markers for every opened project and its subfiles
+ for (int i = 0; projects != null && i < projects.length; i++)
+ {
+ if (projects[i].isOpen())
+ {
+ projects[i].deleteMarkers(AnalyzeApkHandler.DEFAULT_APP_VALIDATOR_MARKER_TYPE,
+ true, IResource.DEPTH_INFINITE);
+ }
+ }
+ }
+ catch (CoreException e)
+ {
+ PreflightingLogger.error("Error removing markers from projects.");
+ }
+
+ super.run();
+ }
+ };
+ //set icon
+ URL imageUrl =
+ PreflightingUIPlugin.getDefault().getBundle()
+ .getEntry("icons/MOTODEVAppValidator_16x16_clear.png");
+ ImageDescriptor imageDesc = ImageDescriptor.createFromURL(imageUrl);
+ action.setImageDescriptor(imageDesc);
+ action.setEnabled(true);
+ action.setToolTipText(PreflightingUiNLS.ProblemsView_ClearAppValidatorMarkers);
+ //add to tool bar and update it
+ toolBar.add(action);
+
+ toolBar.add(new Separator());
+ actionBar.updateActionBars();
+ }
+}
diff --git a/src/plugins/preflighting.ui/src/com/motorolamobility/preflighting/ui/CommandLinePreferencePage.java b/src/plugins/preflighting.ui/src/com/motorolamobility/preflighting/ui/CommandLinePreferencePage.java
new file mode 100644
index 0000000..af54d1d
--- /dev/null
+++ b/src/plugins/preflighting.ui/src/com/motorolamobility/preflighting/ui/CommandLinePreferencePage.java
@@ -0,0 +1,216 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+package com.motorolamobility.preflighting.ui;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.jface.dialogs.IMessageProvider;
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.jface.preference.PreferencePage;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.TabFolder;
+import org.eclipse.swt.widgets.TabItem;
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.IWorkbenchPreferencePage;
+import org.eclipse.ui.PlatformUI;
+
+import com.motorolamobility.preflighting.core.logging.PreflightingLogger;
+import com.motorolamobility.preflighting.ui.i18n.PreflightingUiNLS;
+import com.motorolamobility.preflighting.ui.tabs.AbstractAppValidatorTabComposite;
+import com.motorolamobility.preflighting.ui.tabs.CheckersTabComposite;
+import com.motorolamobility.preflighting.ui.tabs.DevicesTabComposite;
+import com.motorolamobility.preflighting.ui.tabs.GeneralSettingsComposite;
+import com.motorolamobility.preflighting.ui.tabs.UIChangedListener;
+
+public class CommandLinePreferencePage extends PreferencePage implements IWorkbenchPreferencePage
+{
+ private final String PREFERENCE_PAGE_HELP = PreflightingUIPlugin.PREFLIGHTING_UI_PLUGIN_ID
+ + ".preference-appvalidator-commandline"; //$NON-NLS-1$
+
+ private List<AbstractAppValidatorTabComposite> pagesComposite;
+
+ public CommandLinePreferencePage()
+ {
+ setPreferenceStore(PreflightingUIPlugin.getDefault().getPreferenceStore());
+ }
+
+ public void init(IWorkbench workbench)
+ {
+ //do nothing
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.preference.PreferencePage#performOk()
+ */
+ @Override
+ public boolean performOk()
+ {
+ /*
+ * Workaround to make it work on MacOS
+ *
+ * On MacOS, the TableViewer cell doesn't lose the focus when the
+ * user clicks "Apply" or "OK". This way, the editor's setValue method
+ * is not called, and the cell value is not updated in the model.
+ * Given that, the last modification would not be persisted.
+ *
+ * This forces the focus change and consequently updates the model
+ * before continuing.
+ */
+ getShell().setFocus();
+ getControl().setFocus();
+
+ StringBuilder commandLine = new StringBuilder();
+ for (AbstractAppValidatorTabComposite composite : pagesComposite)
+ {
+ composite.performOk(getPreferenceStore());
+ commandLine.append(composite.commandLineBuilder() + " "); //$NON-NLS-1$
+
+ }
+ getPreferenceStore().setValue(PreflightingUIPlugin.COMMAND_LINE_PREFERENCE_KEY,
+ commandLine.toString().trim());
+ getPreferenceStore().setValue(PreflightingUIPlugin.ERRORS_TO_WARNINGS_PREFERENCE_KEY,
+ Boolean.getBoolean(PreflightingUIPlugin.ECLIPSE_PROBLEM_TO_WARNING_VALUE));
+
+ PreflightingLogger.debug("App Validator command line: " + commandLine);
+
+ return super.performOk();
+ }
+
+ /**
+ *
+ */
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.preference.PreferencePage#performDefaults()
+ */
+ @Override
+ protected void performDefaults()
+ {
+ for (AbstractAppValidatorTabComposite composite : pagesComposite)
+ {
+ composite.performDefaults();
+ }
+ super.performDefaults();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.preference.PreferencePage#isValid()
+ */
+
+ @Override
+ protected Control createContents(Composite parent)
+ {
+ PlatformUI.getWorkbench().getHelpSystem().setHelp(parent, PREFERENCE_PAGE_HELP);
+
+ pagesComposite = new ArrayList<AbstractAppValidatorTabComposite>();
+
+ IPreferenceStore prefStore = PreflightingUIPlugin.getDefault().getPreferenceStore();
+ if ((!prefStore.contains(PreflightingUIPlugin.OUTPUT_LIMIT_VALUE))
+ && prefStore.contains(PreflightingUIPlugin.COMMAND_LINE_PREFERENCE_KEY)
+ && (!(prefStore.getString(PreflightingUIPlugin.COMMAND_LINE_PREFERENCE_KEY))
+ .equals(PreflightingUIPlugin.DEFAULT_BACKWARD_COMMANDLINE)))
+ {
+
+ Label backLabel = new Label(parent, SWT.WRAP);
+ GridData layoutData = new GridData(SWT.FILL, SWT.TOP, true, false);
+ layoutData.widthHint = 450;
+ backLabel.setLayoutData(layoutData);
+ backLabel.setText("You have previously set the following App Validator parameters:\n"
+ + prefStore.getString(PreflightingUIPlugin.COMMAND_LINE_PREFERENCE_KEY));
+
+ }
+
+ Composite mainComposite = new Composite(parent, SWT.NONE);
+ mainComposite.setLayout(new GridLayout(1, false));
+ GridData mainData = new GridData(SWT.FILL, SWT.TOP, true, false);
+ mainComposite.setLayoutData(mainData);
+
+ TabFolder tabFolder = new TabFolder(mainComposite, SWT.TOP);
+
+ tabFolder.setLayoutData(new GridData(GridData.FILL_BOTH));
+
+ TabItem generalSettingsTab = new TabItem(tabFolder, SWT.NONE);
+ generalSettingsTab
+ .setText(PreflightingUiNLS.CommandLinePreferencePage_GeneralSettingTabName);
+ AbstractAppValidatorTabComposite genSettingsComposite =
+ new GeneralSettingsComposite(tabFolder, SWT.NONE);
+ generalSettingsTab.setControl(genSettingsComposite);
+ genSettingsComposite.addUIChangedListener(new UIChangedListener()
+ {
+
+ public void uiChanged(AbstractAppValidatorTabComposite composite)
+ {
+ validateUI(composite);
+ }
+ });
+ pagesComposite.add(genSettingsComposite);
+
+ TabItem checkersSettingsTab = new TabItem(tabFolder, SWT.NONE);
+ checkersSettingsTab.setText(PreflightingUiNLS.CommandLinePreferencePage_Checkers_Tab);
+ AbstractAppValidatorTabComposite checkersTabComposite =
+ new CheckersTabComposite(tabFolder, SWT.NONE, getPreferenceStore());
+ checkersSettingsTab.setControl(checkersTabComposite);
+ checkersTabComposite.addUIChangedListener(new UIChangedListener()
+ {
+
+ public void uiChanged(AbstractAppValidatorTabComposite composite)
+ {
+ validateUI(composite);
+ }
+ });
+ pagesComposite.add(checkersTabComposite);
+
+ TabItem devicesSettingTab = new TabItem(tabFolder, SWT.NONE);
+ devicesSettingTab.setText(PreflightingUiNLS.CommandLinePreferencePage_Devices_Tab);
+ AbstractAppValidatorTabComposite devicesTabComposite =
+ new DevicesTabComposite(tabFolder, SWT.NONE, getPreferenceStore());
+ devicesSettingTab.setControl(devicesTabComposite);
+ pagesComposite.add(devicesTabComposite);
+
+ setValid(((GeneralSettingsComposite) genSettingsComposite).canFinish());
+
+ return mainComposite;
+ }
+
+ public void validateUI(AbstractAppValidatorTabComposite composite)
+ {
+ IStatus status = composite.isValid();
+
+ if (status.getSeverity() == IStatus.ERROR)
+ {
+ setValid(false);
+ setMessage(status.getMessage(), IMessageProvider.ERROR);
+ }
+ else if (status.getSeverity() == IStatus.WARNING)
+ {
+ setMessage(status.getMessage(), IMessageProvider.WARNING);
+ }
+ else
+ {
+ setValid(true);
+ setMessage(null);
+ }
+
+ }
+}
diff --git a/src/plugins/preflighting.ui/src/com/motorolamobility/preflighting/ui/PreflightingUIPlugin.java b/src/plugins/preflighting.ui/src/com/motorolamobility/preflighting/ui/PreflightingUIPlugin.java
new file mode 100644
index 0000000..e33907f
--- /dev/null
+++ b/src/plugins/preflighting.ui/src/com/motorolamobility/preflighting/ui/PreflightingUIPlugin.java
@@ -0,0 +1,140 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+package com.motorolamobility.preflighting.ui;
+
+import org.eclipse.ui.plugin.AbstractUIPlugin;
+import org.osgi.framework.BundleContext;
+
+public class PreflightingUIPlugin extends AbstractUIPlugin
+{
+
+ /**
+ * The Preflighting UI plugin id
+ */
+ public static final String PREFLIGHTING_UI_PLUGIN_ID = "com.motorolamobility.preflighting.ui"; //$NON-NLS-1$
+
+ /**
+ * The preference key for preflighting command line arguments
+ */
+ public static final String COMMAND_LINE_PREFERENCE_KEY = PREFLIGHTING_UI_PLUGIN_ID
+ + ".commandLinePreference"; //$NON-NLS-1$
+
+ public static final String OUTPUT_LIMIT_VALUE = PREFLIGHTING_UI_PLUGIN_ID + ".outputLimit";
+
+ public static final String OUTPUT_LIMIT_DEFAULT_VALUE = "1000";
+
+ public static final String OUTPUT_TYPE_VALUE = PREFLIGHTING_UI_PLUGIN_ID + ".outputType";
+
+ public static final String OUTPUT_TYPE_DEFAULT_VALUE = "0";
+
+ public static final String WARNING_LEVEL_VALUE = PREFLIGHTING_UI_PLUGIN_ID + ".warningLevel";
+
+ public static final String WARNING_LEVEL_DEFAULT_VALUE = "4";
+
+ public static final String VERBOSITY_LEVEL_VALUE = PREFLIGHTING_UI_PLUGIN_ID
+ + ".verbosityLevel";
+
+ public static final String VERBOSITY_LEVEL_DEFAULT_VALUE = "0";
+
+ /**
+ * The preference key for preflighting problems view markers policy
+ */
+ public static final String ERRORS_TO_WARNINGS_PREFERENCE_KEY = PREFLIGHTING_UI_PLUGIN_ID
+ + ".errorsToWarningPreference"; //$NON-NLS-1$
+
+ public static final String ECLIPSE_PROBLEM_TO_WARNING_VALUE = PREFLIGHTING_UI_PLUGIN_ID
+ + ".eclipseErrorToWarning";
+
+ public static final String ECLIPSE_PROBLEM_TO_WARNING_DEFAULT_VALUE = "true";
+
+ /**
+ * The preference key for devices to be checked against
+ */
+ public static final String DEVICES_PREFERENCE_KEY = PREFLIGHTING_UI_PLUGIN_ID
+ + ".devicesPreference"; //$NON-NLS-1$
+
+ public static final String USE_ALL_DEVICES_PREFERENCE_KEY = PREFLIGHTING_UI_PLUGIN_ID
+ + ".useAllDevicesPreference"; //$NON-NLS-1$;
+
+ /**
+ * The preference keys for checkers
+ */
+ public static final String CHECKERS_PREFERENCE_KEY = PREFLIGHTING_UI_PLUGIN_ID
+ + ".checkersPreference"; //$NON-NLS-1$
+
+ public static final String USE_ALL_CHECKERS_PREFERENCE_KEY = PREFLIGHTING_UI_PLUGIN_ID
+ + ".useAllCheckersPreference"; //$NON-NLS-1$
+
+ public static final String CHECKERS_PARAMS_PREFERENCE_KEY = PREFLIGHTING_UI_PLUGIN_ID
+ + ".checkersParamsPreference"; //$NON-NLS-1$;
+
+ public static final String CHECKERS_WARNING_LEVELS_PREFERENCE_KEY = PREFLIGHTING_UI_PLUGIN_ID
+ + ".checkersWarningLevelsPreference"; //$NON-NLS-1$;
+
+ public static final String CHECKERS_CONDITIONS_WARNING_LEVELS_PREFERENCE_KEY =
+ PREFLIGHTING_UI_PLUGIN_ID + ".checkersConditionsWarningLevelsPreference"; //$NON-NLS-1$;
+
+ public static final String CHECKERS_CONDITIONS_PREFERENCE_KEY = PREFLIGHTING_UI_PLUGIN_ID
+ + ".checkersConditionsPreference"; //$NON-NLS-1$;
+
+ public static final String DEFAULT_BACKWARD_COMMANDLINE = "-w4 -v";
+
+ public static final String DEFAULT_COMMANDLINE = "-output text -w4 -v0";
+
+ public static final String SHOW_BACKWARD_DIALOG = PREFLIGHTING_UI_PLUGIN_ID
+ + ".showBackwardDialog";
+
+ public final static String TOGGLE_DIALOG = ".toggle.dialog";
+
+ public static final String COMMAND_LINE_PREFERENCE_PAGE =
+ "com.motorolamobility.preflighting.ui.commandLinePreferencePage";
+
+ // The shared instance
+ private static PreflightingUIPlugin plugin;
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.plugin.AbstractUIPlugin#start(org.osgi.framework.BundleContext)
+ */
+ @Override
+ public void start(BundleContext context) throws Exception
+ {
+ super.start(context);
+ plugin = this;
+
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.ui.plugin.AbstractUIPlugin#stop(org.osgi.framework.BundleContext)
+ */
+ @Override
+ public void stop(BundleContext context) throws Exception
+ {
+ plugin = null;
+ super.stop(context);
+ }
+
+ /**
+ * Returns the shared instance
+ *
+ * @return the shared instance
+ */
+ public static PreflightingUIPlugin getDefault()
+ {
+ return plugin;
+ }
+}
diff --git a/src/plugins/preflighting.ui/src/com/motorolamobility/preflighting/ui/handlers/AnalyzeApkHandler.java b/src/plugins/preflighting.ui/src/com/motorolamobility/preflighting/ui/handlers/AnalyzeApkHandler.java
new file mode 100644
index 0000000..976313d
--- /dev/null
+++ b/src/plugins/preflighting.ui/src/com/motorolamobility/preflighting/ui/handlers/AnalyzeApkHandler.java
@@ -0,0 +1,996 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+package com.motorolamobility.preflighting.ui.handlers;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IMarker;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.IWorkspace;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IAdaptable;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.jface.dialogs.IDialogConstants;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.jface.dialogs.MessageDialogWithToggle;
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.StructuredSelection;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.PartInitException;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.console.ConsolePlugin;
+import org.eclipse.ui.console.IConsole;
+import org.eclipse.ui.console.IOConsole;
+import org.eclipse.ui.console.IOConsoleOutputStream;
+
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.common.utilities.AndroidUtils;
+import com.motorolamobility.preflighting.core.exception.PreflightingParameterException;
+import com.motorolamobility.preflighting.core.exception.PreflightingToolException;
+import com.motorolamobility.preflighting.core.logging.PreflightingLogger;
+import com.motorolamobility.preflighting.core.validation.ApplicationValidationResult;
+import com.motorolamobility.preflighting.core.validation.Parameter;
+import com.motorolamobility.preflighting.core.validation.ValidationManager;
+import com.motorolamobility.preflighting.core.validation.ValidationManager.InputParameter;
+import com.motorolamobility.preflighting.core.validation.ValidationResult;
+import com.motorolamobility.preflighting.core.validation.ValidationResultData;
+import com.motorolamobility.preflighting.core.validation.ValidationResultData.SEVERITY;
+import com.motorolamobility.preflighting.core.verbose.DebugVerboseOutputter;
+import com.motorolamobility.preflighting.core.verbose.DebugVerboseOutputter.VerboseLevel;
+import com.motorolamobility.preflighting.core.verbose.WarningLevelFilter;
+import com.motorolamobility.preflighting.internal.commandinput.ApplicationParameterInterpreter;
+import com.motorolamobility.preflighting.internal.commandinput.CommandLineInputProcessor;
+import com.motorolamobility.preflighting.internal.commandinput.exception.ParameterParseException;
+import com.motorolamobility.preflighting.internal.commandoutput.OutputterFactory;
+import com.motorolamobility.preflighting.output.AbstractOutputter;
+import com.motorolamobility.preflighting.ui.PreflightingUIPlugin;
+import com.motorolamobility.preflighting.ui.i18n.PreflightingUiNLS;
+import com.motorolamobility.preflighting.ui.utilities.EclipseUtils;
+
+public class AnalyzeApkHandler extends AbstractHandler
+{
+
+ /**
+ * Newline character
+ */
+ private static final String NEWLINE = System.getProperty("line.separator"); //$NON-NLS-1$
+
+ /**
+ * Identifier for AppValidator default marker
+ */
+ public final static String DEFAULT_APP_VALIDATOR_MARKER_TYPE =
+ "com.motorolamobility.preflighting.checkers.ui.appValidatorMarker"; //$NON-NLS-1$
+
+ /**
+ * Problems view ID
+ */
+ private final static String PROBLEMS_VIEW_ID = "org.eclipse.ui.views.ProblemView"; //$NON-NLS-1$
+
+ /**
+ * App validator exit code
+ */
+ private final int EXIT_CODE_OK = 0;
+
+ private final int EXIT_CODE_TOOL_CONTEXT_ERROR = 1;
+
+ private final int EXIT_APPLICATION_CONTEXT_ERROR = 2;
+
+ /**
+ * The resource being analyzed (project or APK).
+ */
+ private StructuredSelection initialSelection = null;
+
+ /**
+ * True if file is inside workspace, false otherwise (because markers can not be inserted)
+ */
+ private boolean enableMarkers = true;
+
+ private boolean downgradeErrors = true;
+
+ private final IProgressMonitor monitor = null;
+
+ public AnalyzeApkHandler()
+ {
+ }
+
+ public AnalyzeApkHandler(StructuredSelection selection)
+ {
+ //initialize items (applications) to be validated
+ this.initialSelection = selection;
+ }
+
+ /**
+ * Jobs that runs one app validation
+ */
+ private class PreFlightJob extends Job
+ {
+ private final String path;
+
+ private final String sdkPath;
+
+ private String strOutput = ""; //$NON-NLS-1$
+
+ private final IResource analyzedResource;
+
+ private int exitCode = EXIT_CODE_OK;
+
+ /**
+ * @param name
+ * @param stream
+ * @param sdkPath
+ */
+ private PreFlightJob(String path, String sdkPath, IResource analyzedResource)
+ {
+ super(PreflightingUiNLS.AnalyzeApkHandler_AppValidatorJobName);
+ this.path = path;
+ this.sdkPath = sdkPath;
+ this.analyzedResource = analyzedResource;
+ }
+
+ @Override
+ protected IStatus run(IProgressMonitor monitor)
+ {
+ StringBuilder strbValidationOutput = new StringBuilder(""); //$NON-NLS-1$
+
+ exitCode =
+ executePreFlightingTool(path, sdkPath, analyzedResource, strbValidationOutput);
+ strOutput = strbValidationOutput.toString();
+
+ return Status.OK_STATUS;
+ }
+
+ public String getOutput()
+ {
+ return strOutput;
+ }
+
+ public int getExitCode()
+ {
+ return exitCode;
+ }
+ }
+
+ /**
+ * Job that runs several app validations (one PreFlightJob each time)
+ *
+ */
+ private class ParentJob extends Job
+ {
+ ArrayList<PreFlightJob> jobList;
+
+ public ParentJob(String name, ArrayList<PreFlightJob> jobList)
+ {
+ super(name);
+ this.jobList = jobList;
+ }
+
+ @Override
+ protected IStatus run(IProgressMonitor monitor)
+ {
+ OutputStream console = getActiveConsole();
+ PrintStream stream = null;
+
+ try
+ {
+ stream = new PrintStream(console);
+
+ monitor.beginTask(PreflightingUiNLS.ApplicationValidation_monitorTaskName,
+ jobList.size() + 1);
+ monitor.worked(1);
+ for (PreFlightJob job : jobList)
+ {
+ try
+ {
+ job.schedule();
+ job.join();
+ }
+ catch (InterruptedException e)
+ {
+ //Do nothing
+ }
+ monitor.worked(1);
+ String currentJobOutput = job.getOutput();
+ stream.println(currentJobOutput);
+
+ //there is no need to print error messages several times
+ if (job.getExitCode() == EXIT_CODE_TOOL_CONTEXT_ERROR)
+ {
+ break;
+ }
+ }
+ }
+ finally
+ {
+ stream.flush();
+ stream.close();
+ monitor.done();
+ }
+
+ return Status.OK_STATUS;
+ }
+ }
+
+ public static final String CONSOLE_ID = "analyze_apk_console"; //$NON-NLS-1$
+
+ private static final String QUICK_FIX_ID = "QuickFix";
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.core.commands.AbstractHandler#execute(org.eclipse.core.commands.ExecutionEvent)
+ */
+ public Object execute(ExecutionEvent event) throws ExecutionException
+ {
+
+ IWorkbench workbench = PlatformUI.getWorkbench();
+ if ((workbench != null) && !workbench.isClosing())
+ {
+ IWorkbenchWindow window = workbench.getActiveWorkbenchWindow();
+ if (window != null)
+ {
+ ISelection selection = null;
+ if (initialSelection != null)
+ {
+ selection = initialSelection;
+ }
+ else
+ {
+ selection = window.getSelectionService().getSelection();
+ }
+
+ if (selection instanceof IStructuredSelection)
+ {
+ IStructuredSelection sselection = (IStructuredSelection) selection;
+ Iterator<?> it = sselection.iterator();
+ String sdkPath = AndroidUtils.getSDKPathByPreference();
+ if (monitor != null)
+ {
+ monitor.setTaskName(PreflightingUiNLS.ApplicationValidation_monitorTaskName);
+ monitor.beginTask(PreflightingUiNLS.ApplicationValidation_monitorTaskName,
+ sselection.size() + 1);
+ monitor.worked(1);
+ }
+
+ ArrayList<PreFlightJob> jobList = new ArrayList<PreFlightJob>();
+ boolean isHelpExecution = false;
+
+ IPreferenceStore preferenceStore =
+ PreflightingUIPlugin.getDefault().getPreferenceStore();
+
+ boolean showMessageDialog = true;
+ if (preferenceStore.contains(PreflightingUIPlugin.SHOW_BACKWARD_DIALOG
+ + PreflightingUIPlugin.TOGGLE_DIALOG))
+ {
+ showMessageDialog =
+ MessageDialogWithToggle.ALWAYS.equals(preferenceStore
+ .getString(PreflightingUIPlugin.SHOW_BACKWARD_DIALOG
+ + PreflightingUIPlugin.TOGGLE_DIALOG));
+ }
+
+ if (showMessageDialog
+ && (!preferenceStore.contains(PreflightingUIPlugin.OUTPUT_LIMIT_VALUE))
+ && preferenceStore
+ .contains(PreflightingUIPlugin.COMMAND_LINE_PREFERENCE_KEY)
+ && (!(preferenceStore
+ .getString(PreflightingUIPlugin.COMMAND_LINE_PREFERENCE_KEY))
+ .equals(PreflightingUIPlugin.DEFAULT_BACKWARD_COMMANDLINE)))
+ {
+ String commandLine =
+ PreflightingUIPlugin
+ .getDefault()
+ .getPreferenceStore()
+ .getString(PreflightingUIPlugin.COMMAND_LINE_PREFERENCE_KEY);
+ MessageDialogWithToggle dialog =
+ MessageDialogWithToggle
+ .openYesNoQuestion(
+ PlatformUI.getWorkbench()
+ .getActiveWorkbenchWindow().getShell(),
+ PreflightingUiNLS.AnalyzeApkHandler_BackwardMsg_Title,
+ NLS.bind(
+ PreflightingUiNLS.AnalyzeApkHandler_BackwardMsg_Message,
+ commandLine),
+ PreflightingUiNLS.AnalyzeApkHandler_Do_Not_Show_Again,
+ false, preferenceStore,
+ PreflightingUIPlugin.SHOW_BACKWARD_DIALOG
+ + PreflightingUIPlugin.TOGGLE_DIALOG);
+
+ int returnCode = dialog.getReturnCode();
+ if (returnCode == IDialogConstants.YES_ID)
+ {
+ EclipseUtils.openPreference(PlatformUI.getWorkbench()
+ .getActiveWorkbenchWindow().getShell(),
+ PreflightingUIPlugin.COMMAND_LINE_PREFERENCE_PAGE);
+ }
+ }
+
+ String userParamsStr =
+ preferenceStore
+ .getString(PreflightingUIPlugin.COMMAND_LINE_PREFERENCE_KEY);
+
+ downgradeErrors =
+ preferenceStore
+ .contains(PreflightingUIPlugin.ECLIPSE_PROBLEM_TO_WARNING_VALUE)
+ ? preferenceStore
+ .getBoolean(PreflightingUIPlugin.ECLIPSE_PROBLEM_TO_WARNING_VALUE)
+ : true;
+
+ //we look for a help parameter: -help or -list-checkers
+ //in such case we execute app validator only once,
+ //since all executions will have the same output
+ if (userParamsStr.length() > 0)
+ {
+ String regex = "((?!(\\s+" + "-" + ")).)*"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ Pattern pat = Pattern.compile(regex);
+ Matcher matcher = pat.matcher(userParamsStr);
+ while (matcher.find())
+ {
+ String parameterValues =
+ userParamsStr.substring(matcher.start(), matcher.end());
+ if (parameterValues.equals("-" //$NON-NLS-1$
+ + ApplicationParameterInterpreter.PARAM_HELP)
+ || parameterValues.equals("-" //$NON-NLS-1$
+ + ApplicationParameterInterpreter.PARAM_LIST_CHECKERS))
+ {
+ isHelpExecution = true;
+ }
+ }
+ }
+ while (it.hasNext())
+ {
+ IResource analyzedResource = null;
+ Object resource = it.next();
+ String path = null;
+ if (resource instanceof IFile)
+ {
+ IFile apkfile = (IFile) resource;
+ analyzedResource = apkfile;
+ if (apkfile.getFileExtension().equals("apk") && apkfile.exists() //$NON-NLS-1$
+ && apkfile.getLocation().toFile().canRead())
+ {
+ /*
+ * For each apk, execute all verifications passaing the two needed parameters
+ */
+ path = apkfile.getLocation().toOSString();
+ }
+ else
+ {
+ MessageDialog.openError(window.getShell(),
+ PreflightingUiNLS.AnalyzeApkHandler_ErrorTitle,
+ PreflightingUiNLS.AnalyzeApkHandler_ApkFileErrorMessage);
+ }
+ }
+ else if (resource instanceof File)
+ {
+ File apkfile = (File) resource;
+
+ if (apkfile.getName().endsWith(".apk") && apkfile.exists() //$NON-NLS-1$
+ && apkfile.canRead())
+ {
+ /*
+ * For each apk, execute all verifications passaing the two needed parameters
+ */
+ path = apkfile.getAbsolutePath();
+ }
+ else
+ {
+ MessageDialog.openError(window.getShell(),
+ PreflightingUiNLS.AnalyzeApkHandler_ErrorTitle,
+ PreflightingUiNLS.AnalyzeApkHandler_ApkFileErrorMessage);
+ }
+ enableMarkers = false;
+ }
+ else if (resource instanceof IProject)
+ {
+ IProject project = (IProject) resource;
+ analyzedResource = project;
+ path = project.getLocation().toOSString();
+ }
+ else if (resource instanceof IAdaptable)
+ {
+ IAdaptable adaptable = (IAdaptable) resource;
+ IProject project = (IProject) adaptable.getAdapter(IProject.class);
+ analyzedResource = project;
+
+ if (project != null)
+ {
+ path = project.getLocation().toOSString();
+ }
+ }
+
+ if (path != null)
+ {
+ PreFlightJob job = new PreFlightJob(path, sdkPath, analyzedResource);
+ jobList.add(job);
+ if (isHelpExecution)
+ {
+ //app validator is executed only once for help commands
+ break;
+ }
+ }
+ }
+
+ if (enableMarkers)
+ {
+ // Show and activate problems view
+ Runnable showProblemsView = new Runnable()
+ {
+ public void run()
+ {
+ try
+ {
+ PlatformUI.getWorkbench().getActiveWorkbenchWindow()
+ .getActivePage().showView(PROBLEMS_VIEW_ID);
+ }
+ catch (PartInitException e)
+ {
+ PreflightingLogger.error("Error showing problems view."); //$NON-NLS-1$
+ }
+
+ }
+ };
+
+ Display.getDefault().asyncExec(showProblemsView);
+ }
+ //show console for external apks
+ else
+ {
+ showApkConsole();
+ }
+
+ ParentJob parentJob =
+ new ParentJob(
+ PreflightingUiNLS.AnalyzeApkHandler_PreflightingToolNameMessage,
+ jobList);
+ parentJob.setUser(true);
+ parentJob.schedule();
+
+ try
+ {
+ if (monitor != null)
+ {
+ monitor.done();
+ }
+ }
+ catch (Exception e)
+ {
+ //Do nothing
+ }
+ }
+ }
+ }
+
+ return null;
+ }
+
+ private int executePreFlightingTool(String path, String sdkPath, IResource analyzedResource,
+ StringBuilder strbValidationOutput)
+ {
+ int returnValue = EXIT_CODE_OK;
+ List<Parameter> parameters = null;
+
+ //Remove app validator markers every time.
+ if (enableMarkers)
+ {
+ cleanMarkers(analyzedResource);
+ }
+
+ // set default warning and verbosity levels
+ ByteArrayOutputStream verboseOutputStream = new ByteArrayOutputStream();
+ PrintStream prtStream = new PrintStream(verboseOutputStream);
+ DebugVerboseOutputter.setStream(prtStream);
+
+ DebugVerboseOutputter.setCurrentVerboseLevel(DebugVerboseOutputter.DEFAULT_VERBOSE_LEVEL);
+ WarningLevelFilter.setCurrentWarningLevel(WarningLevelFilter.DEFAULT_WARNING_LEVEL);
+
+ DebugVerboseOutputter.printVerboseMessage("", VerboseLevel.v0); //$NON-NLS-1$
+ DebugVerboseOutputter.printVerboseMessage("", VerboseLevel.v0); //$NON-NLS-1$
+ DebugVerboseOutputter.printVerboseMessage(PreflightingUiNLS.AnalyzeApkHandler_Header,
+ VerboseLevel.v0);
+ DebugVerboseOutputter.printVerboseMessage("", VerboseLevel.v0); //$NON-NLS-1$
+
+ IPreferenceStore preferenceStore = PreflightingUIPlugin.getDefault().getPreferenceStore();
+ String userParamsStr = null;
+ if (preferenceStore.contains(PreflightingUIPlugin.COMMAND_LINE_PREFERENCE_KEY))
+ {
+ userParamsStr =
+ preferenceStore.getString(PreflightingUIPlugin.COMMAND_LINE_PREFERENCE_KEY);
+ }
+ else
+ {
+ userParamsStr = PreflightingUIPlugin.DEFAULT_COMMANDLINE;
+ }
+
+ try
+ {
+ if (userParamsStr.length() > 0)
+ {
+ CommandLineInputProcessor commandLineInputProcessor =
+ new CommandLineInputProcessor();
+ parameters = commandLineInputProcessor.processCommandLine(userParamsStr);
+ }
+ else
+ {
+ parameters = new ArrayList<Parameter>(5);
+ }
+
+ parameters.add(new Parameter(ValidationManager.InputParameter.APPLICATION_PATH
+ .getAlias(), path));
+
+ //If user selected a different SDK, let's use their definition, otherwise we'll pick the SDK currently set
+ Parameter sdkParam =
+ new Parameter(ValidationManager.InputParameter.SDK_PATH.getAlias(), null);
+ if (!parameters.contains(sdkParam))
+ {
+ parameters.add(new Parameter(ValidationManager.InputParameter.SDK_PATH.getAlias(),
+ sdkPath));
+ }
+
+ //If user selected the help, list-checker, list-devices or describe-device parameter, let's clear and use only those parameters
+ Parameter helpParam = new Parameter(ApplicationParameterInterpreter.PARAM_HELP, null);
+ boolean hasHelpParam = parameters.contains(helpParam);
+ Parameter listCheckersParam =
+ new Parameter(ApplicationParameterInterpreter.PARAM_LIST_CHECKERS, null);
+ boolean hasListCheckersParam = parameters.contains(listCheckersParam);
+ Parameter describeDeviceParam =
+ new Parameter(ApplicationParameterInterpreter.PARAM_DESC_DEVICE, null);
+ boolean hasDescribeDeviceParam = parameters.contains(describeDeviceParam);
+ Parameter listDevicesParam =
+ new Parameter(ApplicationParameterInterpreter.PARAM_LIST_DEVICES, null);
+ boolean hasListDevicesParam = parameters.contains(listDevicesParam);
+
+ if (hasHelpParam || hasListCheckersParam || hasDescribeDeviceParam
+ || hasListDevicesParam)
+ {
+ int neededParamIdx =
+ hasHelpParam ? parameters.indexOf(helpParam) : hasListCheckersParam
+ ? parameters.indexOf(listCheckersParam) : hasDescribeDeviceParam
+ ? parameters.indexOf(describeDeviceParam) : parameters
+ .indexOf(listDevicesParam);
+ Parameter parameter = parameters.get(neededParamIdx);
+ parameters.clear();
+ parameters.add(parameter);
+ // Show console
+ showApkConsole();
+ }
+
+ List<Parameter> parametersCopy = new ArrayList<Parameter>(parameters);
+
+ AbstractOutputter outputter = null;
+ for (Parameter param : parametersCopy)
+ {
+ if (InputParameter.OUTPUT.getAlias().equals(param.getParameterType()))
+ {
+ ApplicationParameterInterpreter.validateOutputParam(param.getValue());
+ outputter = OutputterFactory.getInstance().createOutputter(parameters);
+ parameters.remove(param);
+ break;
+ }
+ }
+
+ if (outputter == null)
+ {
+ outputter = OutputterFactory.getInstance().createOutputter(null);
+ }
+
+ ValidationManager validationManager = new ValidationManager();
+
+ if (userParamsStr.length() > 0)
+ {
+ ApplicationParameterInterpreter.checkApplicationParameters(parameters,
+ validationManager, prtStream);
+ }
+
+ if (!hasHelpParam && !hasListCheckersParam && !hasDescribeDeviceParam
+ && !hasListDevicesParam)
+ {
+ List<ApplicationValidationResult> results = validationManager.run(parameters);
+ ApplicationValidationResult result = null;
+
+ //inside studio, there won't be any support zip files, thus the map will have only one result
+ for (ApplicationValidationResult aResult : results)
+ {
+ result = aResult;
+ }
+
+ strbValidationOutput.append(verboseOutputStream.toString());
+ if (enableMarkers && (analyzedResource != null) && (result != null))
+ {
+ // Create problem markers
+ createProblemMarkers(result.getResults(), analyzedResource);
+ }
+
+ if (result != null)
+ {
+ ByteArrayOutputStream baos = null;
+ try
+ {
+ baos = new ByteArrayOutputStream();
+ outputter.print(result, baos, parameters);
+ strbValidationOutput.append(baos.toString());
+ }
+ finally
+ {
+ try
+ {
+ baos.close();
+ }
+ catch (IOException e)
+ {
+ StudioLogger.error("Could not close stream: ", e.getMessage());
+ }
+ }
+
+ if ((result.getResults().size() > 0))
+ {
+ //result already used
+ //clean it to help Garbage Collector to work freeing memory
+ result.getResults().clear();
+ result = null;
+ Runtime.getRuntime().gc();
+ }
+
+ }
+
+ }
+ else
+ {
+ strbValidationOutput.append(verboseOutputStream.toString());
+ }
+
+ try
+ {
+ verboseOutputStream.flush();
+ }
+ catch (IOException e)
+ {
+ StudioLogger.error("Could not flush stream: ", e.getMessage());
+ }
+ }
+ catch (PreflightingParameterException pe)
+ {
+ // only log, do not print message (ValidationManager.run() method already prints
+ // each problem individually)
+ PreflightingLogger.error(AnalyzeApkHandler.class,
+ "Parameter problems executing MOTODEV Studio Application Validator", pe); //$NON-NLS-1$
+ strbValidationOutput.append(verboseOutputStream.toString());
+ // Show console
+ showApkConsole();
+ returnValue = EXIT_CODE_TOOL_CONTEXT_ERROR;
+ }
+ catch (PreflightingToolException e)
+ {
+ PreflightingLogger.error(AnalyzeApkHandler.class,
+ "An error ocurred trying to execute MOTODEV Studio Application Validator", e); //$NON-NLS-1$
+
+ strbValidationOutput.append(verboseOutputStream.toString());
+ strbValidationOutput
+ .append(PreflightingUiNLS.AnalyzeApkHandler_PreflightingApplicationExecutionErrorMessage);
+ strbValidationOutput.append(e.getMessage());
+ strbValidationOutput.append(NEWLINE);
+ returnValue = EXIT_APPLICATION_CONTEXT_ERROR;
+ // Show console
+ showApkConsole();
+
+ }
+ catch (ParameterParseException e)
+ {
+ PreflightingLogger.error(AnalyzeApkHandler.class,
+ "An error ocurred trying to execute MOTODEV Studio Application Validator", e); //$NON-NLS-1$
+
+ strbValidationOutput.append(verboseOutputStream.toString());
+ strbValidationOutput
+ .append(PreflightingUiNLS.AnalyzeApkHandler_PreflightingApplicationExecutionErrorMessage);
+ strbValidationOutput.append(e.getMessage());
+ strbValidationOutput.append(NEWLINE);
+
+ // Show console
+ showApkConsole();
+ returnValue = EXIT_CODE_TOOL_CONTEXT_ERROR;
+ }
+ finally
+ {
+ try
+ {
+ verboseOutputStream.close();
+ prtStream.close();
+ }
+ catch (IOException e)
+ {
+ StudioLogger.error("Could not close stream: ", e.getMessage());
+ }
+ }
+
+ return returnValue;
+ }
+
+ /**
+ * Get the output stream of existent apk analysis console. Create one if needed.
+ * @return the output stream
+ */
+ private OutputStream getActiveConsole()
+ {
+ IConsole outputConsole = null;
+ for (IConsole console : ConsolePlugin.getDefault().getConsoleManager().getConsoles())
+ {
+ if (CONSOLE_ID.equals(console.getType()))
+ {
+ outputConsole = console;
+ }
+ }
+
+ if (outputConsole == null)
+ {
+ outputConsole =
+ new IOConsole(PreflightingUiNLS.AnalyzeApkHandler_PreflightingToolNameMessage,
+ CONSOLE_ID, null);
+ ConsolePlugin.getDefault().getConsoleManager().addConsoles(new IConsole[]
+ {
+ outputConsole
+ });
+ }
+
+ IOConsoleOutputStream stream = ((IOConsole) outputConsole).newOutputStream();
+
+ return stream;
+ }
+
+ /**
+ * Bring the Apk console to the front. Create it if necessary.
+ */
+ private void showApkConsole()
+ {
+ IConsole outputConsole = null;
+ for (IConsole console : ConsolePlugin.getDefault().getConsoleManager().getConsoles())
+ {
+ if (CONSOLE_ID.equals(console.getType()))
+ {
+ outputConsole = console;
+ }
+ }
+
+ if (outputConsole == null)
+ {
+ outputConsole =
+ new IOConsole(PreflightingUiNLS.AnalyzeApkHandler_PreflightingToolNameMessage,
+ CONSOLE_ID, null);
+ ConsolePlugin.getDefault().getConsoleManager().addConsoles(new IConsole[]
+ {
+ outputConsole
+ });
+ }
+
+ // Show console
+ ConsolePlugin.getDefault().getConsoleManager().showConsoleView(outputConsole);
+
+ }
+
+ /**
+ * This method will create the markers that will be shown by the "Problems"
+ * view based on the results of the AppValidator.
+ */
+ private void createProblemMarkers(List<ValidationResult> resultList, IResource analyzedResource)
+ {
+
+ cleanMarkers(analyzedResource);
+
+ // Check if resource analyzed is a APK or not
+ boolean isResourceAPK = false;
+
+ if (analyzedResource instanceof IFile)
+ {
+ if (((IFile) analyzedResource).getFileExtension().equals("apk")) //$NON-NLS-1$
+ {
+ isResourceAPK = true;
+ }
+ }
+
+ // Iterate through the results
+ for (ValidationResult result : resultList)
+ {
+ for (ValidationResultData data : result.getValidationResult())
+ {
+ createMarkerBasedOnResultData(data, isResourceAPK, analyzedResource);
+ }
+ }
+ }
+
+ /**
+ * Remove all MARKER_APP_VALIDATOR_TYPE markers
+ */
+ private void cleanMarkers(IResource analyzedResource)
+ {
+ try
+ {
+ if (analyzedResource != null)
+ {
+ analyzedResource.deleteMarkers(DEFAULT_APP_VALIDATOR_MARKER_TYPE, true,
+ IResource.DEPTH_INFINITE);
+ }
+ }
+ catch (CoreException e)
+ {
+ // An error ocurred while cleaning the markers
+ PreflightingLogger.error(AnalyzeApkHandler.class,
+ "Error cleaning problem markers for the analyzed resource: " //$NON-NLS-1$
+ + analyzedResource.getFullPath().toOSString());
+ }
+ }
+
+ /**
+ * Auxiliary method to create markers based on a validation result data.
+ * @param data The validation result data
+ * @param isResourceAPK boolean determining if the resource being analyzed is an APK or not
+ */
+ private void createMarkerBasedOnResultData(ValidationResultData data, boolean isResourceAPK,
+ IResource analyzedResource)
+ {
+ // Create a marker for each result data (only errors and warnings)
+ if ((data.getSeverity() == SEVERITY.FATAL) || (data.getSeverity() == SEVERITY.ERROR)
+ || (data.getSeverity() == SEVERITY.WARNING))
+ {
+
+ /*
+ * Now comes the tricky part. The result data can either have files and lines associated with it or not.
+ * If the resource is an apk, ignore that.
+ */
+ if ((!isResourceAPK) && (data.getFileToIssueLines() != null)
+ && (data.getFileToIssueLines().size() > 0))
+ {
+ // Create a marker for each line in a file
+ for (File f : data.getFileToIssueLines().keySet())
+ {
+
+ IWorkspace workspace = ResourcesPlugin.getWorkspace();
+ IPath location = Path.fromOSString(f.getAbsolutePath());
+
+ IResource markerResource = workspace.getRoot().getFileForLocation(location);
+
+ // Convert to IFolder type if necessary
+ if (f.isDirectory())
+ {
+ markerResource =
+ workspace.getRoot().getFolder(markerResource.getFullPath());
+ }
+
+ if ((markerResource != null) && markerResource.exists())
+ {
+ // Iterate through the lines and create markers. If no lines are associated with the file, don't provide a line value
+ List<Integer> lineList = data.getFileToIssueLines().get(f);
+
+ if ((lineList != null) && (lineList.size() > 0))
+ {
+ // Create a marker for each line
+ for (Integer l : lineList)
+ {
+ createMarker(markerResource, data, l);
+ }
+ }
+ else
+ {
+ createMarker(markerResource, data, -1);
+ }
+
+ }
+
+ }
+ }
+ else
+ {
+ /*
+ * No files are associated with the result data. It's either an apk or a problem with no related file.
+ * In any case, mark the resource being analyzed by the App validator tool
+ */
+ createMarker(analyzedResource, data, -1);
+ }
+
+ }
+ }
+
+ /**
+ * Auxiliary method to create markers. If lineNumber is less than 0, ignore it.
+ * @param resource The resource to be marked.
+ * @param data The set of information used to create the marker.
+ * @param lineNumber The line number.
+ */
+ @SuppressWarnings("incomplete-switch")
+ private void createMarker(IResource resource, ValidationResultData data, int lineNumber)
+ {
+ // Get problem message: error description + quickfix suggestion
+ String markerMessage = data.getIssueDescription() + " " + data.getQuickFixSuggestion(); //$NON-NLS-1$
+
+ // Determine the severity
+ int markerSeverity = 0;
+
+ if (downgradeErrors)
+ {
+ //every error is going to be addressed as an warning
+ markerSeverity = IMarker.SEVERITY_WARNING;
+ }
+ else
+ {
+ switch (data.getSeverity())
+ {
+ case FATAL:
+ markerSeverity = IMarker.SEVERITY_ERROR;
+ break;
+ case ERROR:
+ markerSeverity = IMarker.SEVERITY_ERROR;
+ break;
+ case WARNING:
+ markerSeverity = IMarker.SEVERITY_WARNING;
+ break;
+ }
+ }
+
+ try
+ {
+ String markerType =
+ data.getMarkerType() == null
+ ? AnalyzeApkHandler.DEFAULT_APP_VALIDATOR_MARKER_TYPE : data
+ .getMarkerType();
+
+ // Create marker
+ IMarker marker = resource.createMarker(markerType);
+
+ // Set description
+ marker.setAttribute(IMarker.MESSAGE, markerMessage);
+
+ // Set severity
+ marker.setAttribute(IMarker.SEVERITY, markerSeverity);
+
+ // Set extra data used to fix the problem
+ marker.setAttribute(QUICK_FIX_ID, data.getExtra());
+
+ if (lineNumber >= 0)
+ {
+ // Set line number
+ marker.setAttribute(IMarker.LINE_NUMBER, lineNumber);
+ }
+ }
+ catch (CoreException e)
+ {
+ // An error occurred while creating the markers
+ PreflightingLogger.error(AnalyzeApkHandler.class,
+ "Error creating marker for the following resource: " + resource.getName()); //$NON-NLS-1$
+ }
+
+ }
+} \ No newline at end of file
diff --git a/src/plugins/preflighting.ui/src/com/motorolamobility/preflighting/ui/handlers/OpenApkDialogHandler.java b/src/plugins/preflighting.ui/src/com/motorolamobility/preflighting/ui/handlers/OpenApkDialogHandler.java
new file mode 100644
index 0000000..96c12a8
--- /dev/null
+++ b/src/plugins/preflighting.ui/src/com/motorolamobility/preflighting/ui/handlers/OpenApkDialogHandler.java
@@ -0,0 +1,67 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+package com.motorolamobility.preflighting.ui.handlers;
+
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.StructuredSelection;
+import org.eclipse.jface.wizard.WizardDialog;
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.PlatformUI;
+
+import com.motorolamobility.preflighting.ui.wizards.ApkValidationWizard;
+
+
+public class OpenApkDialogHandler extends AbstractHandler{
+
+ public Object execute(ExecutionEvent event) throws ExecutionException {
+
+ IWorkbench workbench = PlatformUI.getWorkbench();
+
+ if ((workbench != null) && !workbench.isClosing())
+ {
+
+ IWorkbenchWindow window = workbench.getActiveWorkbenchWindow();
+
+ if (window != null)
+ {
+ ISelection selection = window.getSelectionService().getSelection();
+ IStructuredSelection structureSelection = null;
+ if (selection instanceof IStructuredSelection)
+ {
+ structureSelection = (IStructuredSelection) selection;
+ }
+ else
+ {
+ structureSelection = new StructuredSelection();
+ }
+ WizardDialog dialog =
+ new WizardDialog(window.getShell(), new ApkValidationWizard(
+ structureSelection, event));
+
+ dialog.open();
+ }
+ }
+
+ return null;
+ }
+
+}
diff --git a/src/plugins/preflighting.ui/src/com/motorolamobility/preflighting/ui/handlers/OpenProjectDialogHandler.java b/src/plugins/preflighting.ui/src/com/motorolamobility/preflighting/ui/handlers/OpenProjectDialogHandler.java
new file mode 100644
index 0000000..7b1b445
--- /dev/null
+++ b/src/plugins/preflighting.ui/src/com/motorolamobility/preflighting/ui/handlers/OpenProjectDialogHandler.java
@@ -0,0 +1,197 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+package com.motorolamobility.preflighting.ui.handlers;
+
+import java.net.URL;
+
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+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.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.jface.dialogs.IDialogConstants;
+import org.eclipse.jface.viewers.StructuredSelection;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.jface.viewers.ViewerFilter;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.dialogs.ElementTreeSelectionDialog;
+import org.eclipse.ui.dialogs.ISelectionStatusValidator;
+import org.eclipse.ui.model.WorkbenchContentProvider;
+import org.eclipse.ui.model.WorkbenchLabelProvider;
+import org.eclipse.ui.plugin.AbstractUIPlugin;
+import org.eclipse.ui.views.navigator.ResourceComparator;
+import org.osgi.framework.Bundle;
+
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorolamobility.preflighting.ui.PreflightingUIPlugin;
+import com.motorolamobility.preflighting.ui.i18n.PreflightingUiNLS;
+
+public class OpenProjectDialogHandler extends AbstractHandler
+{
+
+ /*
+ * Opens dialog to choose a project
+ */
+ private IProject[] openProjectSelectionDialog()
+ {
+ IProject[] selectedProjects = null;
+
+ // get shell
+ Shell shell = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell();
+ // crate package dialog
+ final ElementTreeSelectionDialog packageDialog =
+ new ElementTreeSelectionDialog(shell, new WorkbenchLabelProvider(),
+ new WorkbenchContentProvider());
+
+ // set title and message
+ packageDialog.setTitle(PreflightingUiNLS.OpenProjectDialogHandler_dialogTitle);
+ packageDialog.setMessage(PreflightingUiNLS.OpenProjectDialogHandler_dialogDescription);
+
+ Bundle bundle = PreflightingUIPlugin.getDefault().getBundle();
+ URL url = bundle.getEntry((new StringBuilder("/")).append( //$NON-NLS-1$
+ "icons" + IPath.SEPARATOR + "MOTODEVAppValidator_16x16.png") //$NON-NLS-1$ //$NON-NLS-2$
+ .toString());
+
+ packageDialog.setImage(AbstractUIPlugin.imageDescriptorFromPlugin(
+ PreflightingUIPlugin.PREFLIGHTING_UI_PLUGIN_ID, url.getPath()).createImage());
+
+ IProject[] projects = ResourcesPlugin.getWorkspace().getRoot().getProjects();
+
+ for (int j = 0; j < projects.length; j++)
+ {
+ try
+ {
+ if (projects[j].getNature("com.android.ide.eclipse.adt.AndroidNature") != null
+ && projects[j].isOpen())
+ {
+ packageDialog.setInitialSelection(projects[j]);
+ break;
+ }
+ }
+ catch (CoreException e)
+ {
+ StudioLogger.error("There was a problem with the project: ", e.getMessage());
+ }
+ }
+
+ // set workspace as root
+ packageDialog.setInput(ResourcesPlugin.getWorkspace().getRoot());
+ // set comparator
+ packageDialog.setComparator(new ResourceComparator(ResourceComparator.NAME));
+
+ //filter extensions
+ packageDialog.addFilter(new ViewerFilter()
+ {
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.jface.viewers.ViewerFilter#select(org.eclipse.jface.viewers.Viewer, java.lang.Object, java.lang.Object)
+ */
+ @Override
+ public boolean select(Viewer viewer, Object parentElement, Object element)
+ {
+ boolean returnValue = false;
+ if (element instanceof IProject)
+ {
+ IProject proj = (IProject) element;
+ try
+ {
+ if (proj.getNature("com.android.ide.eclipse.adt.AndroidNature") != null) //$NON-NLS-1$
+ {
+ returnValue = true;
+ }
+ }
+ catch (Exception e)
+ {
+ //object is skipped
+ }
+ }
+
+ // the element must be a project
+ return returnValue;
+ }
+ });
+
+ packageDialog.setValidator(new ISelectionStatusValidator()
+ {
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.ui.dialogs.ISelectionStatusValidator#validate(java.lang.Object[])
+ */
+ public IStatus validate(Object[] selection)
+ {
+ // by default the status is OK
+ IStatus valid = null;
+ if (selection.length == 0)
+ {
+ valid =
+ new Status(IStatus.ERROR,
+ PreflightingUIPlugin.PREFLIGHTING_UI_PLUGIN_ID,
+ PreflightingUiNLS.OpenProjectDialogHandler_OneProjectSelected);
+ }
+ else
+ {
+ valid =
+ new Status(IStatus.OK, PreflightingUIPlugin.PREFLIGHTING_UI_PLUGIN_ID,
+ ""); //$NON-NLS-1$
+ }
+ // return validation
+ return valid;
+ }
+ });
+ // open dialog
+ if (packageDialog.open() == IDialogConstants.OK_ID)
+ {
+ // get the result
+ Object[] resources = packageDialog.getResult();
+ selectedProjects = new IProject[resources.length];
+
+ for (int i = 0; i < resources.length; i++)
+ {
+ selectedProjects[i] = (IProject) resources[i];
+ }
+ }
+ // return the selected projects
+ return selectedProjects;
+ }
+
+ public Object execute(final ExecutionEvent event) throws ExecutionException
+ {
+
+ final IProject[] selectedProjects = openProjectSelectionDialog();
+ if (selectedProjects != null)
+ {
+ StructuredSelection selection = new StructuredSelection(selectedProjects);
+ AnalyzeApkHandler apkHandler = new AnalyzeApkHandler(selection);
+ try
+ {
+ apkHandler.execute(event);
+ }
+ catch (ExecutionException e)
+ {
+ //do nothing
+ }
+ }
+
+ return null;
+ }
+}
diff --git a/src/plugins/preflighting.ui/src/com/motorolamobility/preflighting/ui/i18n/PreflightingUiNLS.java b/src/plugins/preflighting.ui/src/com/motorolamobility/preflighting/ui/i18n/PreflightingUiNLS.java
new file mode 100644
index 0000000..8dfbd71
--- /dev/null
+++ b/src/plugins/preflighting.ui/src/com/motorolamobility/preflighting/ui/i18n/PreflightingUiNLS.java
@@ -0,0 +1,179 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorolamobility.preflighting.ui.i18n;
+
+import org.eclipse.osgi.util.NLS;
+
+public class PreflightingUiNLS extends NLS
+{
+ private static final String BUNDLE_NAME = "com.motorolamobility.preflighting.ui.i18n.messages"; //$NON-NLS-1$
+
+ public static String AnalyzeApkHandler_ApkFileErrorMessage;
+
+ public static String AnalyzeApkHandler_AppValidatorJobName;
+
+ public static String AnalyzeApkHandler_BackwardMsg_Message;
+
+ public static String AnalyzeApkHandler_BackwardMsg_Title;
+
+ public static String AnalyzeApkHandler_Do_Not_Show_Again;
+
+ public static String AnalyzeApkHandler_ErrorTitle;
+
+ public static String AnalyzeApkHandler_Header;
+
+ public static String AnalyzeApkHandler_PreflightingApplicationExecutionErrorMessage;
+
+ public static String AnalyzeApkHandler_PreflightingToolNameMessage;
+
+ public static String CheckersTabComposite_Id_Column;
+
+ public static String CheckersTabComposite_Name_Column;
+
+ public static String CheckersTabComposite_WarningLevel_Column;
+
+ public static String CheckersTabComposite_ChangeWarningLevel_Column;
+
+ public static String CheckersTabComposite_Checker_Params_Column;
+
+ public static String CheckersTabComposite_Checkers_Group;
+
+ public static String CheckersTabComposite_Conditions_Group;
+
+ public static String CheckersTabComposite_Checkers_SelectAll_Check;
+
+ public static String CommandLinePreferencePage_Checkers_Tab;
+
+ public static String CommandLinePreferencePage_Devices_Tab;
+
+ public static String CommandLinePreferencePage_GeneralSettingTabName;
+
+ public static String ApkValidationWizard_wizardTitle;
+
+ public static String ApkValidationWizardPage_browseLabel;
+
+ public static String ApkValidationWizardPage_description;
+
+ public static String ApkValidationWizardPage_deseletAllLabel;
+
+ public static String ApkValidationWizardPage_emptyFolderMsg;
+
+ public static String ApkValidationWizardPage_emptyListMsg;
+
+ public static String ApkValidationWizardPage_folderLabel;
+
+ public static String ApkValidationWizardPage_invalidDeviceMsg;
+
+ public static String ApkValidationWizardPage_invalidFolderMsg;
+
+ public static String ApkValidationWizardPage_invalidSourceDirectoryMsg;
+
+ public static String ApkValidationWizardPage_onePackageMsg;
+
+ public static String ApkValidationWizardPage_packagesLabel;
+
+ public static String ApkValidationWizardPage_selectAllLabel;
+
+ public static String ApkValidationWizardPage_title;
+
+ public static String ApkValidationWizardPage_tooLongMsg;
+
+ public static String ApkValidationWizardPage_validateMsg;
+
+ public static String OpenProjectDialogHandler_dialogDescription;
+
+ public static String OpenProjectDialogHandler_dialogTitle;
+
+ public static String OpenProjectDialogHandler_OneProjectSelected;
+
+ public static String ApplicationValidation_monitorTaskName;
+
+ public static String DevicesTabComposite_Devices_Group;
+
+ public static String DevicesTabComposite_Name_Column;
+
+ public static String DevicesTabComposite_pixelDensity_Column;
+
+ public static String DevicesTabComposite_ScreenSize_Column;
+
+ public static String DevicesTabComposite_SelectAll_Check;
+
+ public static String GeneralSettingsComposite_24;
+
+ public static String GeneralSettingsComposite_CSVOutputCombo;
+
+ public static String GeneralSettingsComposite_LimitLabel;
+
+ public static String GeneralSettingsComposite_OutputLimit;
+
+ public static String GeneralSettingsComposite_OutputLimitNaNValidationMessage;
+
+ public static String GeneralSettingsComposite_OutputSettingLabel;
+
+ public static String GeneralSettingsComposite_OutputTypeLabel;
+
+ public static String GeneralSettingsComposite_PlainTextRadioButton;
+
+ public static String GeneralSettingsComposite_ShowErrosProblemsButton;
+
+ public static String GeneralSettingsComposite_VerbosityLevel0;
+
+ public static String GeneralSettingsComposite_VerbosityLevel1;
+
+ public static String GeneralSettingsComposite_VerbosityLevel2;
+
+ public static String GeneralSettingsComposite_VerbosityLevelLabel;
+
+ public static String GeneralSettingsComposite_VerbositySettingLabel;
+
+ public static String GeneralSettingsComposite_WarningLevel0;
+
+ public static String GeneralSettingsComposite_WarningLevel1;
+
+ public static String GeneralSettingsComposite_WarningLevel2;
+
+ public static String GeneralSettingsComposite_WarningLevel3;
+
+ public static String GeneralSettingsComposite_WarningLevel4;
+
+ public static String GeneralSettingsComposite_WarningLevelSettings;
+
+ public static String GeneralSettingsComposite_XMLOutputCombo;
+
+ public static String ProblemsView_ClearAppValidatorMarkers;
+
+ public static String CheckersTabComposite_WarningLevel_Operation_Increase;
+
+ public static String CheckersTabComposite_WarningLevel_Operation_Kepp;
+
+ public static String CheckersTabComposite_WarningLevel_Operation_Decrease;
+
+ public static String CheckersTabComposite_Validation_Error_No_Checker;
+
+ public static String CheckersTabComposite_Validation_Warning_Param_Problem;
+
+
+
+ static
+ {
+ // initialize resource bundle
+ NLS.initializeMessages(BUNDLE_NAME, PreflightingUiNLS.class);
+ }
+
+ private PreflightingUiNLS()
+ {
+ }
+}
diff --git a/src/plugins/preflighting.ui/src/com/motorolamobility/preflighting/ui/i18n/messages.properties b/src/plugins/preflighting.ui/src/com/motorolamobility/preflighting/ui/i18n/messages.properties
new file mode 100644
index 0000000..234bfaa
--- /dev/null
+++ b/src/plugins/preflighting.ui/src/com/motorolamobility/preflighting/ui/i18n/messages.properties
@@ -0,0 +1,92 @@
+#
+# Copyright (C) 2012 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+AnalyzeApkHandler_ApkFileErrorMessage=The selected file isn't readable or isn't a valid APK file. Check the file and try again.
+AnalyzeApkHandler_AppValidatorJobName=App. Validator Job
+AnalyzeApkHandler_BackwardMsg_Message=You have previously used the App Validator with the following parameters:\n\n{0}\n\nThese settings will be kept, but in order to better configure the App Validator we recommend that you set these parameters through the new UI.\n\nDo you want to configure the parameters now?
+AnalyzeApkHandler_BackwardMsg_Title=MOTODEV App Validator Backward Compatibility
+AnalyzeApkHandler_Do_Not_Show_Again=Do not show me again
+AnalyzeApkHandler_ErrorTitle=Error
+AnalyzeApkHandler_Header=****APP VALIDATOR****
+AnalyzeApkHandler_PreflightingApplicationExecutionErrorMessage=Error executing MOTODEV Studio App Validator:\n
+AnalyzeApkHandler_PreflightingToolNameMessage=MOTODEV Studio App Validator
+
+CheckersTabComposite_Id_Column=ID
+CheckersTabComposite_Name_Column=Name
+CheckersTabComposite_WarningLevel_Column=Warning Level
+CheckersTabComposite_ChangeWarningLevel_Column=Change Warning Level
+CheckersTabComposite_Checker_Params_Column=Parameters
+CheckersTabComposite_Checkers_Group=Checkers
+CheckersTabComposite_Conditions_Group=Conditions
+CheckersTabComposite_Checkers_SelectAll_Check=Select/deselect all
+CheckersTabComposite_WarningLevel_Operation_Increase=Increase
+CheckersTabComposite_WarningLevel_Operation_Kepp=(No Change)
+CheckersTabComposite_WarningLevel_Operation_Decrease=Decrease
+CheckersTabComposite_Validation_Error_No_Checker=Select at least one checker.
+CheckersTabComposite_Validation_Warning_Param_Problem=Parameters can be passed to a checker only when all of the checker's conditions are selected. Otherwise, the parameters are ignored.
+
+CommandLinePreferencePage_Checkers_Tab=Checkers
+CommandLinePreferencePage_Devices_Tab=Devices
+CommandLinePreferencePage_GeneralSettingTabName=General Settings
+
+ApplicationValidation_monitorTaskName=Validating Applications...
+ApkValidationWizard_wizardTitle=Package Validation
+ApkValidationWizardPage_browseLabel=Browse...
+ApkValidationWizardPage_description=Select a folder containing Android packages.
+ApkValidationWizardPage_deseletAllLabel=Deselect All
+ApkValidationWizardPage_emptyFolderMsg=This folder does not contain any Android packages
+ApkValidationWizardPage_emptyListMsg=Select a folder containing Android packages.
+ApkValidationWizardPage_folderLabel=Folder:
+ApkValidationWizardPage_invalidDeviceMsg=The device you are trying to use is invalid
+ApkValidationWizardPage_invalidFolderMsg=The chosen folder is invalid or does not exist
+ApkValidationWizardPage_invalidSourceDirectoryMsg=The chosen source directory is not a directory
+ApkValidationWizardPage_onePackageMsg=Select at least one package to validate
+ApkValidationWizardPage_packagesLabel=Select the packages:
+ApkValidationWizardPage_selectAllLabel=Select All
+ApkValidationWizardPage_title=Application Package Validation
+ApkValidationWizardPage_tooLongMsg=The path is too long
+ApkValidationWizardPage_validateMsg=Validate selected packages
+DevicesTabComposite_Devices_Group=Devices
+DevicesTabComposite_Name_Column=Name
+DevicesTabComposite_pixelDensity_Column=Pixel Density
+DevicesTabComposite_ScreenSize_Column=Screen Size
+DevicesTabComposite_SelectAll_Check=Select/deselect all
+GeneralSettingsComposite_24=
+GeneralSettingsComposite_CSVOutputCombo=CSV
+GeneralSettingsComposite_LimitLabel=(0 for unlimited)
+GeneralSettingsComposite_OutputLimit=Output limit:
+GeneralSettingsComposite_OutputLimitNaNValidationMessage=The output limit must be a positive number.
+GeneralSettingsComposite_OutputSettingLabel=Output Settings
+GeneralSettingsComposite_OutputTypeLabel=Output type:
+GeneralSettingsComposite_PlainTextRadioButton=Plain text
+GeneralSettingsComposite_ShowErrosProblemsButton=Show errors as warnings in the Problems view
+GeneralSettingsComposite_VerbosityLevel0=0: Only validation results
+GeneralSettingsComposite_VerbosityLevel1=1: Execution info
+GeneralSettingsComposite_VerbosityLevel2=2: Debug and execution Info
+GeneralSettingsComposite_VerbosityLevelLabel=Verbosity levels
+GeneralSettingsComposite_VerbositySettingLabel=Verbosity Settings
+GeneralSettingsComposite_WarningLevel0=0: No messages
+GeneralSettingsComposite_WarningLevel1=1: Fatal errors
+GeneralSettingsComposite_WarningLevel2=2: Fatal errors and errors
+GeneralSettingsComposite_WarningLevel3=3: All errors and warnings
+GeneralSettingsComposite_WarningLevel4=4: Errors, warnings, and fix suggestions
+GeneralSettingsComposite_WarningLevelSettings=Warning levels
+GeneralSettingsComposite_XMLOutputCombo=XML
+OpenProjectDialogHandler_dialogDescription=Select a project to be validated.
+OpenProjectDialogHandler_dialogTitle=Project Selection
+OpenProjectDialogHandler_OneProjectSelected=At least one project must be selected.
+
+ProblemsView_ClearAppValidatorMarkers=Clear App Validator Markers
diff --git a/src/plugins/preflighting.ui/src/com/motorolamobility/preflighting/ui/tabs/AbstractAppValidatorTabComposite.java b/src/plugins/preflighting.ui/src/com/motorolamobility/preflighting/ui/tabs/AbstractAppValidatorTabComposite.java
new file mode 100644
index 0000000..689c8bb
--- /dev/null
+++ b/src/plugins/preflighting.ui/src/com/motorolamobility/preflighting/ui/tabs/AbstractAppValidatorTabComposite.java
@@ -0,0 +1,68 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorolamobility.preflighting.ui.tabs;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.swt.widgets.Composite;
+
+public abstract class AbstractAppValidatorTabComposite extends Composite
+{
+
+ private final Set<UIChangedListener> listeners;
+
+ /**
+ * @param parent
+ * @param style
+ */
+ public AbstractAppValidatorTabComposite(Composite parent, int style)
+ {
+ super(parent, style);
+ listeners = new HashSet<UIChangedListener>();
+
+ }
+
+ public void removeUIChangedListener(UIChangedListener listener)
+ {
+ listeners.remove(listener);
+ }
+
+ public void notifyListener()
+ {
+ for (UIChangedListener listener : listeners)
+ {
+ listener.uiChanged(this);
+ }
+ }
+
+ abstract public IStatus isValid();
+
+ abstract public void performDefaults();
+
+ abstract public void performOk(IPreferenceStore preferenceStore);
+
+ abstract public String commandLineBuilder();
+
+ public void addUIChangedListener(UIChangedListener listener)
+ {
+ listeners.add(listener);
+
+ }
+
+}
diff --git a/src/plugins/preflighting.ui/src/com/motorolamobility/preflighting/ui/tabs/CheckersTabComposite.java b/src/plugins/preflighting.ui/src/com/motorolamobility/preflighting/ui/tabs/CheckersTabComposite.java
new file mode 100644
index 0000000..ff5e5b1
--- /dev/null
+++ b/src/plugins/preflighting.ui/src/com/motorolamobility/preflighting/ui/tabs/CheckersTabComposite.java
@@ -0,0 +1,1755 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorolamobility.preflighting.ui.tabs;
+
+import java.util.ArrayList;
+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.StringTokenizer;
+
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.jface.viewers.CellEditor;
+import org.eclipse.jface.viewers.CellLabelProvider;
+import org.eclipse.jface.viewers.CheckStateChangedEvent;
+import org.eclipse.jface.viewers.CheckboxTableViewer;
+import org.eclipse.jface.viewers.ColumnViewer;
+import org.eclipse.jface.viewers.ColumnViewerToolTipSupport;
+import org.eclipse.jface.viewers.ComboBoxCellEditor;
+import org.eclipse.jface.viewers.EditingSupport;
+import org.eclipse.jface.viewers.ICheckStateListener;
+import org.eclipse.jface.viewers.IStructuredContentProvider;
+import org.eclipse.jface.viewers.StructuredSelection;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.jface.viewers.TableViewerColumn;
+import org.eclipse.jface.viewers.TextCellEditor;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.jface.viewers.ViewerCell;
+import org.eclipse.jface.viewers.ViewerComparator;
+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.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Group;
+import org.eclipse.swt.widgets.Layout;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.swt.widgets.TableColumn;
+import org.eclipse.swt.widgets.TableItem;
+
+import com.motorolamobility.preflighting.core.checker.CheckerDescription;
+import com.motorolamobility.preflighting.core.checker.condition.Condition;
+import com.motorolamobility.preflighting.core.validation.ValidationManager;
+import com.motorolamobility.preflighting.ui.PreflightingUIPlugin;
+import com.motorolamobility.preflighting.ui.i18n.PreflightingUiNLS;
+
+/**
+ * This class represents the Checkers Tab on the App Validador preference page.
+ */
+public class CheckersTabComposite extends AbstractAppValidatorTabComposite
+{
+
+ private final class CheckersColumnSelectionAdapter extends SelectionAdapter
+ {
+ private int columnIndex;
+
+ public CheckersColumnSelectionAdapter(int columnIndex)
+ {
+ this.columnIndex = columnIndex;
+ }
+
+ @Override
+ public void widgetSelected(SelectionEvent e)
+ {
+ checkersTableComparator.setColumn(columnIndex);
+ checkersTableViewer.getTable().setSortColumn((TableColumn) e.getSource());
+ checkersTableViewer.getTable().setSortDirection(
+ checkersTableComparator.getSwtDirection());
+ checkersTableViewer.refresh();
+ super.widgetSelected(e);
+ }
+ }
+
+ private final class ConditionsColumnSelectionAdapter extends SelectionAdapter
+ {
+ private int columnIndex;
+
+ public ConditionsColumnSelectionAdapter(int columnIndex)
+ {
+ this.columnIndex = columnIndex;
+ }
+
+ @Override
+ public void widgetSelected(SelectionEvent e)
+ {
+ conditionsTableComparator.setColumn(columnIndex);
+ conditionsTableViewer.getTable().setSortColumn((TableColumn) e.getSource());
+ conditionsTableViewer.getTable().setSortDirection(
+ conditionsTableComparator.getSwtDirection());
+ conditionsTableViewer.refresh();
+ super.widgetSelected(e);
+ }
+ }
+
+ private static final String NO_CHECKERS_SELECTED = "none"; //$NON-NLS-1$
+
+ /**
+ * Checkers table label provider, provides Cell Text and Tooltip
+ */
+ public class CheckersLabelProvider extends CellLabelProvider
+ {
+
+ /*
+ * Display time in seconds
+ */
+ private static final int TOOLTIP_DISPLAYTIME = 10;
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.jface.viewers.CellLabelProvider#update(org.eclipse.jface.viewers.ViewerCell)
+ */
+ @Override
+ public void update(ViewerCell cell)
+ {
+ String text = "";
+ int columnIndex = cell.getColumnIndex();
+ CheckerDescription checkerDescription = (CheckerDescription) cell.getElement();
+ switch (columnIndex)
+ {
+ case NAME_COLUMN_INDEX:
+ text = checkerDescription.getName();
+ break;
+ case ID_COLUMN_INDEX:
+ text = checkerDescription.getId();
+ break;
+ case CHECKER_PARAMS_COLUMN_INDEX:
+ String checkerId = checkerDescription.getId();
+ text = checkerParams.containsKey(checkerId) ? checkerParams.get(checkerId) : "";
+ break;
+ case CHECKER_CHANGE_WARNING_LEVEL_COLUMN_INDEX:
+ text =
+ getWarningLevelText(customCheckersWarningLevels.get(checkerDescription
+ .getId()));
+ break;
+ default:
+ break;
+ }
+ cell.setText(text);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.jface.viewers.CellLabelProvider#getToolTipDisplayDelayTime(java.lang.Object)
+ */
+ @Override
+ public int getToolTipDisplayDelayTime(Object object)
+ {
+ return 200;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.jface.viewers.CellLabelProvider#getToolTipTimeDisplayed(java.lang.Object)
+ */
+ @Override
+ public int getToolTipTimeDisplayed(Object object)
+ {
+ return TOOLTIP_DISPLAYTIME * 1000;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.jface.viewers.CellLabelProvider#getToolTipText(java.lang.Object)
+ */
+ @Override
+ public String getToolTipText(Object element)
+ {
+ CheckerDescription checkerDescription = (CheckerDescription) element;
+ return checkerDescription.getDescription();
+ }
+
+ }
+
+ /**
+ * Conditions table label provider, provides Cell Text and Tooltip
+ */
+ public class ConditionsLabelProvider extends CellLabelProvider
+ {
+
+ /*
+ * Display time in seconds
+ */
+ private static final int TOOLTIP_DISPLAYTIME = 10;
+
+ @Override
+ public void update(ViewerCell cell)
+ {
+ String text = "";
+ int columnIndex = cell.getColumnIndex();
+ Condition condition = (Condition) cell.getElement();
+ switch (columnIndex)
+ {
+ case NAME_COLUMN_INDEX:
+ text = condition.getName();
+ break;
+ case ID_COLUMN_INDEX:
+ text = condition.getId();
+ break;
+ case CONDITION_WARNING_LEVEL_COLUMN_INDEX:
+ text = condition.getSeverityLevel().toString();
+ break;
+ case CONDITION_CHANGE_WARNING_LEVEL_COLUMN_INDEX:
+ text =
+ getWarningLevelText(customConditionsWarningLevels
+ .get(condition.getId()));
+ break;
+ default:
+ break;
+ }
+ cell.setText(text);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.jface.viewers.CellLabelProvider#getToolTipDisplayDelayTime(java.lang.Object)
+ */
+ @Override
+ public int getToolTipDisplayDelayTime(Object object)
+ {
+ return 200;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.jface.viewers.CellLabelProvider#getToolTipTimeDisplayed(java.lang.Object)
+ */
+ @Override
+ public int getToolTipTimeDisplayed(Object object)
+ {
+ return TOOLTIP_DISPLAYTIME * 1000;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.jface.viewers.CellLabelProvider#getToolTipText(java.lang.Object)
+ */
+ @Override
+ public String getToolTipText(Object element)
+ {
+ Condition condition = (Condition) element;
+ return condition.getDescription();
+ }
+
+ }
+
+ /**
+ * Checkers Table content provider
+ */
+ public class CheckersContentProvider implements IStructuredContentProvider
+ {
+
+ @SuppressWarnings("unchecked")
+ public Object[] getElements(Object inputElement)
+ {
+ List<CheckerDescription> checkersDescription = (List<CheckerDescription>) inputElement;
+ return checkersDescription.toArray();
+ }
+
+ public void dispose()
+ {
+ //do nothing
+ }
+
+ public void inputChanged(Viewer viewer, Object oldInput, Object newInput)
+ {
+ //do nothing
+ }
+ }
+
+ /**
+ * Conditions Table content provider
+ */
+ public class ConditionsContentProvider implements IStructuredContentProvider
+ {
+
+ @SuppressWarnings("unchecked")
+ public Object[] getElements(Object inputElement)
+ {
+ List<Condition> conditions = (List<Condition>) inputElement;
+ return conditions.toArray();
+ }
+
+ public void dispose()
+ {
+ //do nothing
+ }
+
+ public void inputChanged(Viewer viewer, Object oldInput, Object newInput)
+ {
+ //do nothing
+ }
+ }
+
+ /**
+ * Table comparator
+ * Add sort functionality for checkers
+ */
+ private class CheckersTableComparator extends ViewerComparator
+ {
+ private final int ORDER_ASC = 1;
+
+ private final int ORDER_DESC = -1;
+
+ /**
+ * Column that must be used to sort elements
+ */
+ private int column = -1;
+
+ private int direction = ORDER_ASC;
+
+ public void setColumn(int column)
+ {
+ if (this.column == column)
+ {
+ direction = direction == ORDER_ASC ? ORDER_DESC : ORDER_ASC;
+ }
+ else
+ {
+ this.column = column;
+ direction = ORDER_ASC;
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.jface.viewers.ViewerComparator#compare(org.eclipse.jface.viewers.Viewer, java.lang.Object, java.lang.Object)
+ */
+ @Override
+ public int compare(Viewer viewer, Object e1, Object e2)
+ {
+ CheckerDescription checkerLeft = (CheckerDescription) e1;
+ CheckerDescription checkerRight = (CheckerDescription) e2;
+
+ String left = ""; //$NON-NLS-1$
+ String right = ""; //$NON-NLS-1$
+ switch (column)
+ {
+ case NAME_COLUMN_INDEX:
+ left = checkerLeft.getName();
+ right = checkerRight.getName();
+ break;
+ case ID_COLUMN_INDEX:
+ left = checkerLeft.getId();
+ right = checkerRight.getId();
+ break;
+ case CHECKER_PARAMS_COLUMN_INDEX:
+ String leftParams = checkerParams.get(checkerLeft.getId());
+ String rightParams = checkerParams.get(checkerRight.getId());
+ left = ((leftParams != null) ? leftParams : "");
+ right = ((rightParams != null) ? rightParams : "");
+ break;
+ case CHECKER_CHANGE_WARNING_LEVEL_COLUMN_INDEX:
+ Boolean leftWarningLevel = customCheckersWarningLevels.get(checkerLeft.getId());
+ Boolean rightWarningLevel =
+ customCheckersWarningLevels.get(checkerRight.getId());
+ left = ((leftWarningLevel != null) ? leftWarningLevel.toString() : "");
+ right = ((rightWarningLevel != null) ? rightWarningLevel.toString() : "");
+ break;
+ default:
+ break;
+ }
+
+ return left.compareTo(right) * direction;
+ }
+
+ /**
+ * Returns the SWT constant which represents the direction
+ * @return
+ */
+ public int getSwtDirection()
+ {
+ return direction == ORDER_ASC ? SWT.UP : SWT.DOWN;
+ }
+ }
+
+ /**
+ * Conditions comparator
+ * Add sort functionality for checkers
+ */
+ private class ConditionsTableComparator extends ViewerComparator
+ {
+ private final int ORDER_ASC = 1;
+
+ private final int ORDER_DESC = -1;
+
+ /**
+ * Column that must be used to sort elements
+ */
+ private int column = -1;
+
+ private int direction = ORDER_ASC;
+
+ public void setColumn(int column)
+ {
+ if (this.column == column)
+ {
+ direction = direction == ORDER_ASC ? ORDER_DESC : ORDER_ASC;
+ }
+ else
+ {
+ this.column = column;
+ direction = ORDER_ASC;
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.jface.viewers.ViewerComparator#compare(org.eclipse.jface.viewers.Viewer, java.lang.Object, java.lang.Object)
+ */
+ @Override
+ public int compare(Viewer viewer, Object e1, Object e2)
+ {
+ Condition conditionLeft = (Condition) e1;
+ Condition conditionRight = (Condition) e2;
+
+ String left = ""; //$NON-NLS-1$
+ String right = ""; //$NON-NLS-1$
+ switch (column)
+ {
+ case NAME_COLUMN_INDEX:
+ left = conditionLeft.getName();
+ right = conditionRight.getName();
+ break;
+ case ID_COLUMN_INDEX:
+ left = conditionLeft.getId();
+ right = conditionRight.getId();
+ break;
+ case CHECKER_PARAMS_COLUMN_INDEX:
+ left = conditionLeft.getSeverityLevel().toString();
+ right = conditionRight.getSeverityLevel().toString();
+ break;
+ case CHECKER_CHANGE_WARNING_LEVEL_COLUMN_INDEX:
+ Boolean leftWarningLevel =
+ customConditionsWarningLevels.get(conditionLeft.getId());
+ Boolean rightWarningLevel =
+ customConditionsWarningLevels.get(conditionRight.getId());
+ left = ((leftWarningLevel != null) ? leftWarningLevel.toString() : "");
+ right = ((rightWarningLevel != null) ? rightWarningLevel.toString() : "");
+ break;
+ default:
+ break;
+ }
+
+ return left.compareTo(right) * direction;
+ }
+
+ /**
+ * Returns the SWT constant which represents the direction
+ * @return
+ */
+ public int getSwtDirection()
+ {
+ return direction == ORDER_ASC ? SWT.UP : SWT.DOWN;
+ }
+ }
+
+ /*
+ * Table columns
+ */
+ private static final int NAME_COLUMN_INDEX = 0;
+
+ private static final int ID_COLUMN_INDEX = 1;
+
+ private static final int CONDITION_WARNING_LEVEL_COLUMN_INDEX = 2;
+
+ private static final int CONDITION_CHANGE_WARNING_LEVEL_COLUMN_INDEX = 3;
+
+ private static final int CHECKER_PARAMS_COLUMN_INDEX = 2;
+
+ private static final int CHECKER_CHANGE_WARNING_LEVEL_COLUMN_INDEX = 3;
+
+ /**
+ * The Checkers TableViewer
+ */
+ private CheckboxTableViewer checkersTableViewer;
+
+ /**
+ * The Conditions TableViewer
+ */
+ private CheckboxTableViewer conditionsTableViewer;
+
+ /**
+ * The Select All checkbox for checkers
+ */
+ private Button selectAllCheckersCheck;
+
+ /**
+ * The Select All checkbox for conditions
+ */
+ private Button selectAllConditionsCheck;
+
+ /**
+ * A list with all CheckerDescription
+ */
+ private List<CheckerDescription> checkersDescriptions;
+
+ /**
+ * The model which keeps checker params, Key is checkerId and value is the Parameters String
+ */
+ private Map<String, String> checkerParams;
+
+ /**
+ * The model which keeps the custom warning levels, Key is checkerId and value is a Boolean that indicates:
+ * true -> increases warning level
+ * false -> decreases warning level
+ * null and/or inexistent -> no change
+ */
+ private Map<String, Boolean> customCheckersWarningLevels = new HashMap<String, Boolean>();
+
+ private Map<String, Boolean> customConditionsWarningLevels = new HashMap<String, Boolean>();
+
+ /**
+ * The model which keeps all conditions, Key is checkerId and value is a list of its conditions
+ */
+ private Map<String, List<Condition>> allConditionsMap = new HashMap<String, List<Condition>>();
+
+ /**
+ * The model which keeps only the selected conditions, Key is checkerId and value is a list of its selected conditions
+ */
+ private Map<String, List<Condition>> selectedConditionsMap =
+ new HashMap<String, List<Condition>>();
+
+ /**
+ * Warning Level Operations (increase, keep, decrease)
+ * Currently, it's not possible to set the warning level, you can only increase or decrease it
+ */
+ private String[] WarningLevelOperations =
+ {
+ PreflightingUiNLS.CheckersTabComposite_WarningLevel_Operation_Increase,
+ PreflightingUiNLS.CheckersTabComposite_WarningLevel_Operation_Kepp,
+ PreflightingUiNLS.CheckersTabComposite_WarningLevel_Operation_Decrease
+ };
+
+ /**
+ * Checkers table comparator, used for sort functionality
+ */
+ private CheckersTableComparator checkersTableComparator;
+
+ /**
+ * Conditions table comparator, used for sort functionality
+ */
+ private ConditionsTableComparator conditionsTableComparator;
+
+ /**
+ * Return the text that represents the warning level change, which is
+ * saved as a boolean
+ *
+ * true -> increases warning level
+ * false -> decreases warning level
+ * null -> no change
+ *
+ * @param action the value stored
+ * @return the corresponding warning level operation
+ */
+ public String getWarningLevelText(Boolean action)
+ {
+ String result = PreflightingUiNLS.CheckersTabComposite_WarningLevel_Operation_Kepp;
+
+ if (action != null)
+ {
+ if (action)
+ {
+ result = PreflightingUiNLS.CheckersTabComposite_WarningLevel_Operation_Increase;
+ }
+ else
+ {
+ result = PreflightingUiNLS.CheckersTabComposite_WarningLevel_Operation_Decrease;
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Initialize all UI items
+ *
+ * @param parent the parent composite
+ * @param style the SWT styles
+ * @param preferenceStore the preference store
+ */
+ public CheckersTabComposite(Composite parent, int style, IPreferenceStore preferenceStore)
+ {
+ super(parent, style);
+
+ Layout layout = new GridLayout(1, false);
+ this.setLayout(layout);
+
+ createCheckersComposite();
+
+ init(preferenceStore);
+ }
+
+ /**
+ * Creates the composite which will have the checkers and also
+ * the conditions selection
+ */
+ private void createCheckersComposite()
+ {
+ Layout layout;
+ Composite composite = new Composite(this, SWT.NONE);
+ layout = new GridLayout(1, true);
+ composite.setLayout(layout);
+
+ GridData gd = new GridData(SWT.FILL, SWT.FILL, true, true);
+ composite.setLayoutData(gd);
+
+ /*
+ * Create checkers group
+ */
+ createCheckersGroup(composite);
+
+ /*
+ * Include the conditions area in the checker group.
+ * This are is updated according to the checker selection
+ */
+ createConditionsGroup(composite);
+
+ }
+
+ /**
+ * Create and return the Checkers group.
+ * It contains the table with all checkers listed. When a checker is
+ * selected, the corresponding conditions are updated in the conditions
+ * table.
+ *
+ * @param topComposite the parent composite
+ */
+ private void createCheckersGroup(Composite topComposite)
+ {
+
+ Group checkersGroup = new Group(topComposite, SWT.NONE);
+ checkersGroup.setLayout(new GridLayout(1, false));
+ checkersGroup.setText(PreflightingUiNLS.CheckersTabComposite_Checkers_Group);
+ GridData gd = new GridData(SWT.FILL, SWT.FILL, true, true);
+ checkersGroup.setLayoutData(gd);
+
+ checkersTableViewer =
+ CheckboxTableViewer.newCheckList(checkersGroup, SWT.BORDER | SWT.SINGLE
+ | SWT.FULL_SELECTION);
+ Control checkersTableControl = checkersTableViewer.getTable();
+ gd = new GridData(SWT.FILL, SWT.FILL, true, true);
+ checkersTableControl.setLayoutData(gd);
+
+ checkersTableComparator = new CheckersTableComparator();
+ checkersTableViewer.setComparator(checkersTableComparator);
+
+ /*
+ * Create Columns
+ */
+ TableViewerColumn column = new TableViewerColumn(checkersTableViewer, SWT.NONE); // Name
+ column.getColumn().setText(PreflightingUiNLS.CheckersTabComposite_Name_Column);
+ column.getColumn().setResizable(true);
+ column.getColumn().setWidth(230);
+ column.setLabelProvider(new CheckersLabelProvider());
+ column.getColumn().addSelectionListener(
+ new CheckersColumnSelectionAdapter(NAME_COLUMN_INDEX));
+
+ column = new TableViewerColumn(checkersTableViewer, SWT.NONE); // ID
+ column.getColumn().setText(PreflightingUiNLS.CheckersTabComposite_Id_Column);
+ column.getColumn().setResizable(true);
+ column.getColumn().setWidth(100);
+ column.setLabelProvider(new CheckersLabelProvider());
+ column.getColumn()
+ .addSelectionListener(new CheckersColumnSelectionAdapter(ID_COLUMN_INDEX));
+
+ column = new TableViewerColumn(checkersTableViewer, SWT.NONE); // Parameters
+ column.getColumn().setText(PreflightingUiNLS.CheckersTabComposite_Checker_Params_Column);
+ column.getColumn().setResizable(true);
+ column.getColumn().setWidth(100);
+ column.setLabelProvider(new CheckersLabelProvider());
+ column.getColumn().addSelectionListener(
+ new CheckersColumnSelectionAdapter(CHECKER_PARAMS_COLUMN_INDEX));
+ // set a custom editor for this column
+ column.setEditingSupport(new ParameterEditingSupport(checkersTableViewer));
+
+ column = new TableViewerColumn(checkersTableViewer, SWT.NONE); // Change Warning Level
+ column.getColumn()
+ .setText(PreflightingUiNLS.CheckersTabComposite_ChangeWarningLevel_Column);
+ column.getColumn().setResizable(true);
+ column.getColumn().setWidth(100);
+ column.setLabelProvider(new CheckersLabelProvider());
+ column.getColumn().addSelectionListener(
+ new CheckersColumnSelectionAdapter(CHECKER_CHANGE_WARNING_LEVEL_COLUMN_INDEX));
+ // set a custom editor for this column
+ column.setEditingSupport(new WarningLevelEditingSupport(checkersTableViewer));
+
+ checkersTableViewer.getTable().setHeaderVisible(true);
+
+ checkersTableViewer.setContentProvider(new CheckersContentProvider());
+ ColumnViewerToolTipSupport.enableFor(checkersTableViewer);
+ // Selection Change Listener handles needed UI updates on the paramsText according to checkers selected
+ // It also updates the conditions table
+ checkersTableViewer.getTable().addSelectionListener(new SelectionAdapter()
+ {
+ @Override
+ public void widgetSelected(SelectionEvent e)
+ {
+ CheckerDescription checkerDescription = (CheckerDescription) e.item.getData();
+ TableItem tableItem = (TableItem) e.item;
+ updateConditionTableBasedOnChecker(tableItem.getChecked());
+ populateConditionsTable(checkerDescription.getId());
+ }
+ });
+
+ // CheckStateListener handles the selectAll check box behavior and also selects the clicked item on the table.
+ checkersTableViewer.addCheckStateListener(new ICheckStateListener()
+ {
+
+ public void checkStateChanged(CheckStateChangedEvent event)
+ {
+ boolean isAllSelected = areAllItemsChecked(checkersTableViewer);
+ selectAllCheckersCheck.setSelection(isAllSelected);
+ checkersTableViewer.setSelection(new StructuredSelection(event.getElement()), true);
+ updateConditionTableBasedOnChecker(event.getChecked());
+ notifyListener();
+ }
+ });
+
+ ValidationManager validationManager = new ValidationManager();
+ checkersDescriptions = validationManager.getCheckersDescription();
+ checkerParams = new HashMap<String, String>(checkersDescriptions.size());
+ checkersTableViewer.setInput(checkersDescriptions);
+
+ selectAllCheckersCheck = new Button(checkersGroup, SWT.CHECK);
+ selectAllCheckersCheck
+ .setText(PreflightingUiNLS.CheckersTabComposite_Checkers_SelectAll_Check);
+ gd = new GridData(SWT.LEFT, SWT.BOTTOM, false, false);
+ selectAllCheckersCheck.setLayoutData(gd);
+ selectAllCheckersCheck.setSelection(true);
+ selectAllCheckersCheck.addSelectionListener(new SelectionAdapter()
+ {
+ @Override
+ public void widgetSelected(SelectionEvent e)
+ {
+ checkersTableViewer.setAllChecked(selectAllCheckersCheck.getSelection());
+ updateConditionTableBasedOnChecker(selectAllCheckersCheck.getSelection());
+ super.widgetSelected(e);
+ notifyListener();
+ }
+ });
+
+ checkersTableComparator.setColumn(0);
+
+ }
+
+ /**
+ * @param event
+ */
+ public void updateConditionTableBasedOnChecker(boolean checkerEnabled)
+ {
+ if (!checkerEnabled)
+ {
+ //disabling checker => disable all conditions as well (and disable the table)
+ conditionsTableViewer.getTable().setEnabled(false);
+ selectAllConditionsCheck.setEnabled(false);
+ }
+ else
+ {
+ //enabling checker => enables all conditions as well (and enable the table)
+ conditionsTableViewer.getTable().setEnabled(true);
+ selectAllConditionsCheck.setEnabled(true);
+ }
+ }
+
+ /**
+ * Create the conditions are, which contains a table with all
+ * conditions of the selected checker. Its content is updated
+ * dynamically based on the checker selection
+ *
+ * @param topComposite the parent composite
+ */
+ /**
+ * @param topComposite
+ */
+ private void createConditionsGroup(Composite topComposite)
+ {
+
+ Group conditionsGroup = new Group(topComposite, SWT.NONE);
+ conditionsGroup.setLayout(new GridLayout(1, false));
+ conditionsGroup.setText(PreflightingUiNLS.CheckersTabComposite_Conditions_Group);
+ GridData gd = new GridData(SWT.FILL, SWT.FILL, true, true);
+ gd.heightHint = 200;
+ conditionsGroup.setLayoutData(gd);
+
+ conditionsTableViewer =
+ CheckboxTableViewer.newCheckList(conditionsGroup, SWT.BORDER | SWT.MULTI
+ | SWT.FULL_SELECTION);
+ Control conditionsTableControl = conditionsTableViewer.getTable();
+ gd.heightHint = 200;
+ conditionsTableControl.setLayoutData(gd);
+
+ conditionsTableViewer.setContentProvider(new ConditionsContentProvider());
+ ColumnViewerToolTipSupport.enableFor(conditionsTableViewer);
+
+ conditionsTableComparator = new ConditionsTableComparator();
+ conditionsTableViewer.setComparator(conditionsTableComparator);
+
+ // Create Columns
+ TableViewerColumn column = new TableViewerColumn(conditionsTableViewer, SWT.NONE); // Name
+ column.getColumn().setText(PreflightingUiNLS.CheckersTabComposite_Name_Column);
+ column.getColumn().setResizable(true);
+ column.getColumn().setWidth(230);
+ column.setLabelProvider(new ConditionsLabelProvider());
+ column.getColumn().addSelectionListener(
+ new ConditionsColumnSelectionAdapter(NAME_COLUMN_INDEX));
+
+ column = new TableViewerColumn(conditionsTableViewer, SWT.NONE); // ID
+ column.getColumn().setText(PreflightingUiNLS.CheckersTabComposite_Id_Column);
+ column.getColumn().setResizable(true);
+ column.getColumn().setWidth(100);
+ column.setLabelProvider(new ConditionsLabelProvider());
+ column.getColumn().addSelectionListener(
+ new ConditionsColumnSelectionAdapter(ID_COLUMN_INDEX));
+
+ column = new TableViewerColumn(conditionsTableViewer, SWT.NONE); // Warning Level
+ column.getColumn().setText(PreflightingUiNLS.CheckersTabComposite_WarningLevel_Column);
+ column.getColumn().setResizable(true);
+ column.getColumn().setWidth(100);
+ column.setLabelProvider(new ConditionsLabelProvider());
+ column.getColumn().addSelectionListener(
+ new ConditionsColumnSelectionAdapter(CONDITION_WARNING_LEVEL_COLUMN_INDEX));
+
+ column = new TableViewerColumn(conditionsTableViewer, SWT.NONE); // Change Warning Level
+ column.getColumn()
+ .setText(PreflightingUiNLS.CheckersTabComposite_ChangeWarningLevel_Column);
+ column.getColumn().setResizable(true);
+ column.getColumn().setWidth(100);
+ column.setLabelProvider(new ConditionsLabelProvider());
+ column.getColumn().addSelectionListener(
+ new ConditionsColumnSelectionAdapter(CONDITION_CHANGE_WARNING_LEVEL_COLUMN_INDEX));
+ // set a custom editor for this column
+ column.setEditingSupport(new WarningLevelEditingSupport(conditionsTableViewer));
+
+ conditionsTableViewer.getTable().setHeaderVisible(true);
+
+ conditionsTableViewer.addCheckStateListener(new ICheckStateListener()
+ {
+
+ public void checkStateChanged(CheckStateChangedEvent event)
+ {
+ // update "select all" control
+ boolean isAllSelected = areAllItemsChecked(conditionsTableViewer);
+ selectAllConditionsCheck.setSelection(isAllSelected);
+ // move the selection to the checked item
+ conditionsTableViewer.setSelection(new StructuredSelection(event.getElement()),
+ true);
+
+ // get the condition and the checker that are selected
+ Condition condition = (Condition) event.getElement();
+ CheckerDescription selectedChecker =
+ (CheckerDescription) ((StructuredSelection) checkersTableViewer
+ .getSelection()).getFirstElement();
+ // update the selected conditions maps accordingly, adding the specified condition
+ // if the checkbox is checked or removing it otherwise
+ List<Condition> conditions = selectedConditionsMap.get(selectedChecker.getId());
+ if (event.getChecked())
+ {
+ conditions.add(condition);
+ }
+ else
+ {
+ conditions.remove(condition);
+ }
+ notifyListener();
+ }
+ });
+
+ selectAllConditionsCheck = new Button(conditionsGroup, SWT.CHECK);
+ selectAllConditionsCheck
+ .setText(PreflightingUiNLS.CheckersTabComposite_Checkers_SelectAll_Check);
+ gd = new GridData(SWT.LEFT, SWT.BOTTOM, false, false);
+ selectAllConditionsCheck.setLayoutData(gd);
+ selectAllConditionsCheck.setSelection(true);
+ selectAllConditionsCheck.addSelectionListener(new SelectionAdapter()
+ {
+ @Override
+ public void widgetSelected(SelectionEvent e)
+ {
+ // update UI
+ conditionsTableViewer.setAllChecked(selectAllConditionsCheck.getSelection());
+
+ // update model
+ CheckerDescription selectedChecker =
+ (CheckerDescription) ((StructuredSelection) checkersTableViewer
+ .getSelection()).getFirstElement();
+ // get all conditions of the selected checker and add them to the selected conditions list
+ if (selectAllConditionsCheck.getSelection())
+ {
+ selectAllConditions(selectedChecker.getId());
+ }
+ // clear the selected conditions list
+ else
+ {
+ selectedConditionsMap.put(selectedChecker.getId(), new ArrayList<Condition>());
+ }
+ super.widgetSelected(e);
+ notifyListener();
+ }
+ });
+
+ selectAllConditionsCheck.setEnabled(conditionsTableViewer.getTable().isEnabled());
+
+ conditionsTableComparator.setColumn(0);
+ }
+
+ /**
+ * Select all conditions in the model, by copying the list with all conditions
+ *
+ * @param checkerId the checker id to have all its conditions selected
+ */
+ private void selectAllConditions(String checkerId)
+ {
+ List<Condition> selectedConditions = new ArrayList<Condition>();
+ for (Condition condition : allConditionsMap.get(checkerId))
+ {
+ selectedConditions.add(condition);
+ }
+ selectedConditionsMap.put(checkerId, selectedConditions);
+ }
+
+ /**
+ * Create a custom cell editor which will contain a text editor so that
+ * users can modify the parameters for a particular checker
+ */
+ public class ParameterEditingSupport extends EditingSupport
+ {
+
+ public ParameterEditingSupport(ColumnViewer viewer)
+ {
+ super(viewer);
+ }
+
+ @Override
+ protected void setValue(Object element, Object value)
+ {
+ checkerParams.put(((CheckerDescription) element).getId(), (String) value);
+ getViewer().update(element, null);
+ notifyListener();
+ }
+
+ @Override
+ protected Object getValue(Object element)
+ {
+ CheckerDescription checkerDescription = (CheckerDescription) element;
+ String params = ""; //$NON-NLS-1$
+ if (checkerParams.containsKey(checkerDescription.getId()))
+ {
+ params = checkerParams.get(checkerDescription.getId());
+ }
+ return params;
+ }
+
+ @Override
+ protected CellEditor getCellEditor(Object element)
+ {
+ return new TextCellEditor(((TableViewer) getViewer()).getTable(), SWT.NONE);
+ }
+
+ @Override
+ protected boolean canEdit(Object element)
+ {
+ return true;
+ }
+
+ }
+
+ /**
+ * Create a custom cell editor which will contain a combobox so that
+ * users can modify (increase / decrease) the warning level for a particular
+ * condition or checker
+ */
+ public class WarningLevelEditingSupport extends EditingSupport
+ {
+
+ public WarningLevelEditingSupport(TableViewer viewer)
+ {
+ super(viewer);
+ }
+
+ @Override
+ protected CellEditor getCellEditor(Object element)
+ {
+ CellEditor comboEditor =
+ new ComboBoxCellEditor(((TableViewer) getViewer()).getTable(),
+ WarningLevelOperations, SWT.READ_ONLY | SWT.FULL_SELECTION
+ | SWT.DROP_DOWN);
+ return comboEditor;
+
+ }
+
+ @Override
+ protected boolean canEdit(Object element)
+ {
+ return true;
+ }
+
+ @Override
+ protected Object getValue(Object element)
+ {
+ // Get value from model
+ Boolean customWarningLevel = null;
+ if (element instanceof Condition)
+ {
+ customWarningLevel =
+ customConditionsWarningLevels.get(((Condition) element).getId());
+ }
+ else
+ {
+ customWarningLevel =
+ customCheckersWarningLevels.get(((CheckerDescription) element).getId());
+ }
+
+ // select the appropriate item in the combobox
+ int value = 1; // keep
+ if (customWarningLevel != null)
+ {
+ value = ((customWarningLevel) ? 0 : 2); // increase, decrease
+ }
+ return new Integer(value);
+ }
+
+ @Override
+ protected void setValue(Object element, Object value)
+ {
+ int intValue = ((Integer) value).intValue();
+ Boolean booleanValue = null; // 1 = default (keep warning level)
+ if (intValue != 1)
+ {
+ booleanValue = ((intValue == 0) ? new Boolean(true) : new Boolean(false));
+ }
+
+ // Set value in the model
+ if (element instanceof Condition)
+ {
+ customConditionsWarningLevels.put(((Condition) element).getId(), booleanValue);
+ }
+ else
+ {
+ customCheckersWarningLevels.put(((CheckerDescription) element).getId(),
+ booleanValue);
+ }
+
+ // Update the UI. This generate a call to the label provider
+ getViewer().update(element, null);
+ getViewer().refresh();
+ }
+ }
+
+ /**
+ * Update the conditions table based on the checker that is selected
+ *
+ * @param checkerId the checker selected in the UI
+ */
+ private void populateConditionsTable(String checkerId)
+ {
+
+ // display appropriate elements
+ if (checkerId != null)
+ {
+ conditionsTableViewer.setInput(allConditionsMap.get(checkerId));
+ }
+ else
+ {
+ conditionsTableViewer.setInput(new ArrayList<Condition>());
+ }
+
+ // update selection in the table and also the "select all" button state
+ conditionsTableViewer.setCheckedElements(selectedConditionsMap.get(checkerId).toArray());
+ selectAllConditionsCheck.setEnabled(conditionsTableViewer.getTable().isEnabled());
+ selectAllConditionsCheck.setSelection(areAllItemsChecked(conditionsTableViewer));
+
+ }
+
+ /*
+ * Verifies if all items on a given CheckboxTableViewer are selected
+ */
+ private boolean areAllItemsChecked(CheckboxTableViewer tableViewer)
+ {
+ Table table = tableViewer.getTable();
+ TableItem[] items = table.getItems();
+ boolean allChecked = true;
+ int i = 0;
+ while ((i < items.length) && allChecked)
+ {
+ TableItem tableItem = items[i];
+ if (!tableItem.getChecked())
+ {
+ allChecked = false;
+ }
+ i++;
+ }
+ return allChecked;
+ }
+
+ /*
+ * Load data from the preference store and configure the UI accordingly.
+ */
+ private void init(IPreferenceStore preferenceStore)
+ {
+ /*
+ * Populate condition maps
+ */
+ ValidationManager validationManager = new ValidationManager();
+ for (CheckerDescription checkerDescription : checkersDescriptions)
+ {
+ allConditionsMap.put(checkerDescription.getId(),
+ validationManager.getCheckerConditions(checkerDescription.getId()));
+ }
+
+ String checkersPreference =
+ preferenceStore.getString(PreflightingUIPlugin.CHECKERS_PREFERENCE_KEY);
+ // Preference is empty means first time. Perform defaults
+ if (checkersPreference.length() == 0)
+ {
+ performDefaults();
+ }
+ else
+ {
+ /*
+ * Checkers information
+ */
+ if (!checkersPreference.equals(NO_CHECKERS_SELECTED)) //There's checkers to load and check on the table
+ {
+ StringTokenizer tokenizer = new StringTokenizer(checkersPreference, ","); //$NON-NLS-1$
+ List<CheckerDescription> selectedCheckerDescriptions =
+ new ArrayList<CheckerDescription>(tokenizer.countTokens());
+ while (tokenizer.hasMoreTokens())
+ {
+ String checkerId = tokenizer.nextToken();
+ CheckerDescription checkerDescription = getCheckerDescription(checkerId);
+ if (checkerDescription != null)
+ {
+ selectedCheckerDescriptions.add(checkerDescription);
+ }
+ }
+ checkersTableViewer.setCheckedElements(selectedCheckerDescriptions.toArray());
+ }
+ selectAllCheckersCheck.setSelection(areAllItemsChecked(checkersTableViewer));
+
+ String checkersConditionsPreference =
+ preferenceStore
+ .getString(PreflightingUIPlugin.CHECKERS_CONDITIONS_PREFERENCE_KEY);
+ if (checkersConditionsPreference.length() > 0)
+ {
+ StringTokenizer tokenizer = new StringTokenizer(checkersConditionsPreference, ";");
+ while (tokenizer.hasMoreElements())
+ {
+ String checkerConditionStr = tokenizer.nextToken();
+ String[] split = checkerConditionStr.split(":");
+ if (split.length == 2)
+ {
+ String checkerId = split[0];
+ String[] selectedConditionsIds = split[1].split(",");
+
+ // define which are the selected conditions
+ Set<String> selectedConditionsIdsSet = new HashSet<String>();
+ for (String selectedConditionId : selectedConditionsIds)
+ {
+ selectedConditionsIdsSet.add(selectedConditionId);
+ }
+ // get the Condition objects and add them to the selected conditions map
+ List<Condition> allConditionsList = allConditionsMap.get(checkerId);
+ List<Condition> selectedConditionsList = new ArrayList<Condition>();
+ if (allConditionsList != null)
+ {
+ for (Condition condition : allConditionsList)
+ {
+ if (selectedConditionsIdsSet.contains(condition.getId()))
+ {
+ selectedConditionsList.add(condition);
+ }
+ }
+ }
+ selectedConditionsMap.put(checkerId, selectedConditionsList);
+ }
+ }
+ }
+ // create an empty list for the checkers that had no preferences set (new checkers, for example)
+ for (CheckerDescription checkerDescription : checkersDescriptions)
+ {
+ if (selectedConditionsMap.get(checkerDescription.getId()) == null)
+ {
+ selectedConditionsMap.put(checkerDescription.getId(),
+ new ArrayList<Condition>());
+ }
+ }
+
+ /*
+ * Get Extended Properties
+ */
+ // Checker parameters
+ loadExtendedProperty(checkerParams, preferenceStore,
+ PreflightingUIPlugin.CHECKERS_PARAMS_PREFERENCE_KEY, String.class);
+
+ // Custom checker warning levels
+ loadExtendedProperty(customCheckersWarningLevels, preferenceStore,
+ PreflightingUIPlugin.CHECKERS_WARNING_LEVELS_PREFERENCE_KEY, Boolean.class);
+
+ // Custom conditions warning levels
+ loadExtendedProperty(customConditionsWarningLevels, preferenceStore,
+ PreflightingUIPlugin.CHECKERS_CONDITIONS_WARNING_LEVELS_PREFERENCE_KEY,
+ Boolean.class);
+
+ checkersTableViewer.refresh();
+
+ }
+ }
+
+ /**
+ * Load saved extend property. The extended properties are related to
+ * a checker or condition, and are saved in the form:
+ * <id>,<extended_property>;<id>,<extended_property>...
+ *
+ * These properties are loaded in a map <id> -> <extended_property>
+ *
+ * @param map the map where the information will be loaded
+ * @param preferenceStore the preference store
+ * @param preferenceName the preference key used to store
+ */
+ @SuppressWarnings(
+ {
+ "rawtypes", "unchecked"
+ })
+ private void loadExtendedProperty(Map map, IPreferenceStore preferenceStore,
+ String preferenceName, Class valueType)
+ {
+
+ String preference = preferenceStore.getString(preferenceName);
+ if (preference.length() > 0)
+ {
+ StringTokenizer tokenizer = new StringTokenizer(preference, ";");
+ while (tokenizer.hasMoreElements())
+ {
+ String extendedProperty = tokenizer.nextToken();
+ String[] split = extendedProperty.split(",");
+ if (split.length == 2)
+ {
+ String id = split[0];
+ String params = split[1];
+ // string (checker param)
+ if (valueType.getName().equals("java.lang.String"))
+ {
+ map.put(id, params);
+ }
+ // boolean (warning levels)
+ else
+ {
+ map.put(id, new Boolean(params));
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Saved extended properties. The extended properties are related to
+ * a checker or condition, and are saved in the form:
+ * <id>,<extended_property>;<id>,<extended_property>...
+ *
+ * The properties come from a map <id> -> <extended_property>
+ *
+ * @param map a map with the extended properties
+ * @param preferenceStore the preference store
+ * @param preferenceName the preference key used to store
+ */
+ private void saveExtendedProperty(Map<String, ?> map, IPreferenceStore preferenceStore,
+ String preferenceName)
+ {
+ StringBuilder stringBuilder = new StringBuilder();
+ if (!map.isEmpty())
+ {
+ for (String id : map.keySet())
+ {
+ String params = ((map.get(id) != null) ? map.get(id).toString() : null);
+ if ((params != null) && (params.length() > 0))
+ {
+ stringBuilder.append(id);
+ stringBuilder.append(","); //$NON-NLS-1$
+ stringBuilder.append(params);
+ stringBuilder.append(";"); //$NON-NLS-1$
+ }
+ }
+ deleteLastChar(stringBuilder);
+ }
+ preferenceStore.setValue(preferenceName, stringBuilder.toString());
+ }
+
+ /**
+ * Get the CheckerDescription object for a fiven checkerId passed as parameter
+ *
+ * @param checkerId the checker id of the object to be retrieved
+ * @return the CheckerDescription object that represents the checker with the id passed as parameter
+ */
+ private CheckerDescription getCheckerDescription(String checkerId)
+ {
+ CheckerDescription checkerDescriptionFound = null;
+ Iterator<CheckerDescription> it = checkersDescriptions.iterator();
+ while ((checkerDescriptionFound == null) && it.hasNext())
+ {
+ CheckerDescription checkerDescription = it.next();
+ if (checkerDescription.getId().equals(checkerId))
+ {
+ checkerDescriptionFound = checkerDescription;
+ }
+ }
+ return checkerDescriptionFound;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorolamobility.preflighting.ui.tabs.AbstractAppValidatorTabComposite#isValid()
+ */
+ @Override
+ public IStatus isValid()
+ {
+ IStatus status = Status.OK_STATUS;
+ boolean isValid = true;
+ boolean hasWarning = false;
+ String msg = "";
+
+ // check if no checker is selected
+ if (checkersTableViewer.getCheckedElements().length == 0)
+ {
+ isValid = false;
+ msg = PreflightingUiNLS.CheckersTabComposite_Validation_Error_No_Checker;
+ }
+
+ // check if there are parameters for checkers that have custom conditions selection
+ Object[] checkerDescriptions = checkersTableViewer.getCheckedElements();
+ if (checkerDescriptions.length > 0)
+ {
+ for (Object checkerDescObj : checkerDescriptions)
+ {
+ String checkerId = ((CheckerDescription) checkerDescObj).getId();
+ if (((checkerParams.get(checkerId) != null) && !checkerParams.get(checkerId)
+ .equals("")))
+ {
+ if (hasCustomConditionsSelection(checkerId))
+ {
+ hasWarning = true;
+ msg =
+ PreflightingUiNLS.CheckersTabComposite_Validation_Warning_Param_Problem;
+ }
+ }
+ }
+ }
+
+ if (!isValid)
+ {
+ status = new Status(IStatus.ERROR, PreflightingUIPlugin.PREFLIGHTING_UI_PLUGIN_ID, msg);
+ }
+ else if (hasWarning)
+ {
+ status =
+ new Status(IStatus.WARNING, PreflightingUIPlugin.PREFLIGHTING_UI_PLUGIN_ID, msg);
+ }
+
+ return status;
+ }
+
+ /**
+ * Check if there are unselected conditions for the checker passed as parameter
+ *
+ * @param checkerId the checker to have the conditions selection analysed
+ * @return true if there is custom selection for the conditions (i.e. unselected conditions), false otherwise
+ */
+ private boolean hasCustomConditionsSelection(String checkerId)
+ {
+ boolean result = false;
+ List<Condition> selectedConditions = selectedConditionsMap.get(checkerId);
+
+ if ((selectedConditions != null)
+ && (selectedConditionsMap.get(checkerId).size() != allConditionsMap.get(checkerId)
+ .size()))
+ {
+ result = true;
+ }
+
+ return result;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorolamobility.preflighting.ui.tabs.AbstractAppValidatorTabComposite#performDefaults()
+ */
+ @Override
+ public void performDefaults()
+ {
+
+ /*
+ * Checkers - select all
+ */
+ // model / UI
+ checkersTableViewer.setAllChecked(true);
+ selectAllCheckersCheck.setSelection(true);
+
+ // model
+ for (CheckerDescription checkerDescription : checkersDescriptions)
+ {
+ selectAllConditions(checkerDescription.getId());
+ }
+ // UI
+ conditionsTableViewer.setAllChecked(true);
+ selectAllConditionsCheck.setSelection(true);
+
+ /*
+ * Extended Properties
+ */
+ // model
+ checkerParams.clear();
+ customCheckersWarningLevels.clear();
+ customConditionsWarningLevels.clear();
+
+ /*
+ * Update UIs
+ */
+ checkersTableViewer.refresh();
+ conditionsTableViewer.refresh();
+
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorolamobility.preflighting.ui.tabs.AbstractAppValidatorTabComposite#performOk(org.eclipse.jface.preference.IPreferenceStore)
+ */
+ @Override
+ public void performOk(IPreferenceStore preferenceStore)
+ {
+ StringBuilder stringBuilder;
+
+ /*
+ * Checkers information
+ */
+ Object[] checkerDescriptions = checkersTableViewer.getCheckedElements();
+
+ stringBuilder = new StringBuilder();
+ if (checkerDescriptions.length > 0)
+ {
+ for (Object checkerDescObj : checkerDescriptions)
+ {
+ CheckerDescription checkerDescription = (CheckerDescription) checkerDescObj;
+ stringBuilder.append(checkerDescription.getId());
+ stringBuilder.append(","); //$NON-NLS-1$
+ }
+ deleteLastChar(stringBuilder);
+ preferenceStore.putValue(PreflightingUIPlugin.CHECKERS_PREFERENCE_KEY,
+ stringBuilder.toString());
+ }
+ else
+ {
+ preferenceStore.putValue(PreflightingUIPlugin.CHECKERS_PREFERENCE_KEY,
+ NO_CHECKERS_SELECTED);
+ }
+
+ stringBuilder = new StringBuilder();
+ if (!selectedConditionsMap.isEmpty())
+ {
+ for (String checkerId : selectedConditionsMap.keySet())
+ {
+ stringBuilder.append(checkerId);
+ stringBuilder.append(":");
+ for (Condition condition : selectedConditionsMap.get(checkerId))
+ {
+ stringBuilder.append(condition.getId());
+ stringBuilder.append(","); //$NON-NLS-1$
+ }
+ deleteLastChar(stringBuilder);
+ stringBuilder.append(";");
+ }
+ deleteLastChar(stringBuilder);
+ }
+ preferenceStore.setValue(PreflightingUIPlugin.CHECKERS_CONDITIONS_PREFERENCE_KEY,
+ stringBuilder.toString());
+
+ /*
+ * Extended Properties
+ */
+ // Checker parameters
+ saveExtendedProperty(checkerParams, preferenceStore,
+ PreflightingUIPlugin.CHECKERS_PARAMS_PREFERENCE_KEY);
+ // Custom checker warning levels
+ saveExtendedProperty(customCheckersWarningLevels, preferenceStore,
+ PreflightingUIPlugin.CHECKERS_WARNING_LEVELS_PREFERENCE_KEY);
+ // Custom conditions warning levels
+ saveExtendedProperty(customConditionsWarningLevels, preferenceStore,
+ PreflightingUIPlugin.CHECKERS_CONDITIONS_WARNING_LEVELS_PREFERENCE_KEY);
+
+ }
+
+ /**
+ * Delete the last char of a string builder
+ *
+ * @param stringBuilder the string builder to have the last char deleted
+ */
+ private void deleteLastChar(StringBuilder stringBuilder)
+ {
+ if (stringBuilder.length() > 0)
+ {
+ stringBuilder.deleteCharAt(stringBuilder.length() - 1);
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorolamobility.preflighting.ui.tabs.AbstractAppValidatorTabComposite#commandLineBuilder()
+ */
+ @Override
+ public String commandLineBuilder()
+ {
+ String commandline = "";
+
+ boolean hasCustomCheckers = !areAllItemsChecked(checkersTableViewer);
+ boolean hasCustomParameters = hasParameters();
+ boolean hasCustomConditions = hasCustomConditions();
+ boolean hasCustomWarningLevels = hasCustomWarningLevels();
+
+ // check if there are custom configurations
+ if (hasCustomCheckers || hasCustomParameters || hasCustomConditions
+ || hasCustomWarningLevels)
+ {
+ Object[] checkedElements = checkersTableViewer.getCheckedElements();
+ StringBuilder stringBuilder = new StringBuilder();
+
+ Boolean customWarningLevel = null;
+ List<String> elementsToIncreaseWarningLevel = new ArrayList<String>();
+ List<String> elementsToDecreaseWarningLevel = new ArrayList<String>();
+
+ /*
+ * Iterate in the selected checkers
+ */
+ for (Object checkedObj : checkedElements)
+ {
+ CheckerDescription checkerDescription = (CheckerDescription) checkedObj;
+ String checkerId = checkerDescription.getId();
+ List<Condition> selectedConditions =
+ ((selectedConditionsMap.get(checkerId) != null) ? selectedConditionsMap
+ .get(checkerId) : new ArrayList<Condition>());
+
+ // Have custom parameters or there are checkers disabled
+ // just create the command line block to
+ // select the checker and to pass the appropriate parameters, if any
+ if (hasCustomParameters
+ || (checkedElements.length < checkersTableViewer.getTable().getItems().length))
+ {
+ stringBuilder.append("-c ");
+ stringBuilder.append(checkerId);
+ stringBuilder.append(" ");
+
+ // params only apply if there are no unselected/custom conditions
+ String parameters = checkerParams.get(checkerId);
+ if ((parameters != null) && (parameters.length() > 0))
+ {
+ stringBuilder.append(parameters);
+ stringBuilder.append(" ");
+ }
+ }
+ // Given that there are conditions disabled, create the command line block
+ // to enable only the selected ones. Additionally, define the custom warning level
+ // for that condition, if any
+ List<Condition> allConditions = allConditionsMap.get(checkerId);
+ if (allConditions != null)
+ {
+ for (Condition condition : allConditions)
+ {
+ if ((selectedConditions != null) && !selectedConditions.contains(condition))
+ {
+ //Condition is NOT selected => add it to -dc (disable condition) command
+ stringBuilder.append("-dc ");
+ stringBuilder.append(checkerId);
+ stringBuilder.append(".");
+ stringBuilder.append(condition.getId());
+ stringBuilder.append(" ");
+ }
+ }
+ }
+
+ /*
+ * Build the list of items that must have their warning levels changed
+ */
+ // checker
+ customWarningLevel = customCheckersWarningLevels.get(checkerId);
+ if (customWarningLevel != null)
+ {
+ if (customWarningLevel)
+ {
+ elementsToIncreaseWarningLevel.add(checkerId);
+ }
+ else
+ {
+ elementsToDecreaseWarningLevel.add(checkerId);
+ }
+ }
+ // conditions
+ for (Condition condition : ((selectedConditions != null) ? selectedConditions
+ : allConditionsMap.get(checkerId)))
+ {
+ customWarningLevel = customConditionsWarningLevels.get(condition.getId());
+ if (customWarningLevel != null)
+ {
+ if (customWarningLevel)
+ {
+ elementsToIncreaseWarningLevel.add(checkerId + "." + condition.getId());
+ }
+ else
+ {
+ elementsToDecreaseWarningLevel.add(checkerId + "." + condition.getId());
+ }
+ }
+
+ }
+
+ }
+
+ /*
+ * Custom warning levels
+ */
+ stringBuilder.append(createCustomWarningLevelCommandLine(true,
+ elementsToIncreaseWarningLevel));
+ stringBuilder.append(createCustomWarningLevelCommandLine(false,
+ elementsToDecreaseWarningLevel));
+
+ commandline = stringBuilder.toString().trim();
+
+ }
+
+ return commandline;
+
+ }
+
+ /**
+ * Create the command line piece which increases or decreases the warning level
+ * for certain element (checker or condition)
+ *
+ * @param increase true if the warning level must be increased, false if it must be decreased, null if the warning level must not change
+ * @param element the element to have its warning level changed
+ * @return the command line piece
+ */
+ public String createCustomWarningLevelCommandLine(Boolean increase, List<String> elements)
+ {
+ StringBuilder result = new StringBuilder();
+
+ if ((increase != null) && (elements.size() > 0))
+ {
+ if (increase)
+ {
+ result.append("-wx"); // increase
+ }
+ else
+ {
+ result.append("-xw"); // decrease
+ }
+ result.append(" ");
+ for (String element : elements)
+ {
+ result.append(element);
+ result.append(" ");
+ }
+ }
+
+ return result.toString();
+ }
+
+ /**
+ * Check if there are parameters set for any checker
+ *
+ * @return true if there are parameters set, false otherwise
+ */
+ private boolean hasParameters()
+ {
+ boolean paramsFound = false;
+
+ Iterator<String> it = checkerParams.values().iterator();
+ while (it.hasNext() && !paramsFound)
+ {
+ String checkerParam = it.next();
+ if (checkerParam.length() > 0)
+ {
+ paramsFound = true;
+ }
+ }
+
+ return paramsFound;
+ }
+
+ /**
+ * Check if there are unselected/custom conditions.
+ * This is used to decide if the App Validator command line must be created or not
+ *
+ * @return true if there are custom unselected/custom conditions, false otherwise
+ */
+ private boolean hasCustomConditions()
+ {
+ boolean result = false;
+
+ for (String checkerId : allConditionsMap.keySet())
+ {
+ List<Condition> selectedConditions =
+ ((selectedConditionsMap.get(checkerId) != null) ? selectedConditionsMap
+ .get(checkerId) : new ArrayList<Condition>());
+ if (allConditionsMap.get(checkerId).size() != selectedConditions.size())
+ {
+ result = true;
+ break;
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Check if there are custom warning levels
+ * This is used to decide if the App Validator command line must be created or not
+ *
+ * @return true if there are custom warning levels, false otherwise
+ */
+ private boolean hasCustomWarningLevels()
+ {
+
+ boolean result = false;
+
+ List<Map<String, Boolean>> maps = new ArrayList<Map<String, Boolean>>();
+ maps.add(customCheckersWarningLevels);
+ maps.add(customConditionsWarningLevels);
+
+ for (Map<String, Boolean> map : maps)
+ {
+ for (Boolean customWarningLevel : map.values())
+ {
+ if (customWarningLevel != null)
+ {
+ result = true;
+ break;
+ }
+ }
+ }
+
+ return result;
+ }
+}
diff --git a/src/plugins/preflighting.ui/src/com/motorolamobility/preflighting/ui/tabs/DevicesTabComposite.java b/src/plugins/preflighting.ui/src/com/motorolamobility/preflighting/ui/tabs/DevicesTabComposite.java
new file mode 100644
index 0000000..992bafd
--- /dev/null
+++ b/src/plugins/preflighting.ui/src/com/motorolamobility/preflighting/ui/tabs/DevicesTabComposite.java
@@ -0,0 +1,512 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorolamobility.preflighting.ui.tabs;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.StringTokenizer;
+
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.jface.viewers.CheckboxTableViewer;
+import org.eclipse.jface.viewers.ILabelProviderListener;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.IStructuredContentProvider;
+import org.eclipse.jface.viewers.ITableLabelProvider;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+import org.eclipse.jface.viewers.TableViewerColumn;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.jface.viewers.ViewerComparator;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Group;
+import org.eclipse.swt.widgets.Layout;
+import org.eclipse.swt.widgets.TableColumn;
+import org.eclipse.swt.widgets.TableItem;
+
+import com.motorolamobility.preflighting.core.devicespecification.DeviceSpecification;
+import com.motorolamobility.preflighting.core.validation.ValidationManager;
+import com.motorolamobility.preflighting.ui.PreflightingUIPlugin;
+import com.motorolamobility.preflighting.ui.i18n.PreflightingUiNLS;
+
+/**
+ * This class represents the Devices Tab on the app validador preference page.
+ */
+public class DevicesTabComposite extends AbstractAppValidatorTabComposite
+{
+ private final class ColumnSelectionAdapter extends SelectionAdapter
+ {
+ private int columnIndex;
+
+ public ColumnSelectionAdapter(int columnIndex)
+ {
+ this.columnIndex = columnIndex;
+ }
+
+ @Override
+ public void widgetSelected(SelectionEvent e)
+ {
+ devicesTableComparator.setColumn(columnIndex);
+ devicesTableViewer.getTable().setSortColumn((TableColumn) e.getSource());
+ devicesTableViewer.getTable()
+ .setSortDirection(devicesTableComparator.getSwtDirection());
+ devicesTableViewer.refresh();
+ super.widgetSelected(e);
+ }
+ }
+
+ /*
+ * Table content provider
+ */
+ public class DevicesContentProvider implements IStructuredContentProvider
+ {
+ @SuppressWarnings("unchecked")
+ public Object[] getElements(Object inputElement)
+ {
+ return ((List<DeviceSpecification>) inputElement).toArray();
+ }
+
+ public void dispose()
+ {
+ //do nothing
+ }
+
+ public void inputChanged(Viewer viewer, Object oldInput, Object newInput)
+ {
+ //do nothing
+ }
+
+ }
+
+ /*
+ * Table label provider
+ */
+ public class DevicesLabelProvider implements ITableLabelProvider
+ {
+
+ public void addListener(ILabelProviderListener listener)
+ {
+ //do nothing
+ }
+
+ public void dispose()
+ {
+ //do nothing
+ }
+
+ public boolean isLabelProperty(Object element, String property)
+ {
+ return false;
+ }
+
+ public void removeListener(ILabelProviderListener listener)
+ {
+ //do nothing
+ }
+
+ public Image getColumnImage(Object element, int columnIndex)
+ {
+ return null;
+ }
+
+ public String getColumnText(Object element, int columnIndex)
+ {
+ String text = ""; //$NON-NLS-1$
+ DeviceSpecification deviceSpec = (DeviceSpecification) element;
+ switch (columnIndex)
+ {
+ case NAME_COLUMN_INDEX:
+ text = deviceSpec.getName();
+ break;
+ case SCREENSIZE_COLUMN_INDEX:
+ text = deviceSpec.getDeviceInfo().getDefault().getScreenSize();
+ break;
+ case PIXELDENSITY_COLUMN_INDEX:
+ text = deviceSpec.getDeviceInfo().getDefault().getPixelDensity();
+ break;
+ default:
+ break;
+ }
+ return text;
+ }
+
+ }
+
+ /*
+ * Table comparator
+ * Add sort functionality
+ */
+ private class DevicesTableComparator extends ViewerComparator
+ {
+ private final int ORDER_ASC = 1;
+
+ private final int ORDER_DESC = -1;
+
+ /**
+ * Column that must be used to sort elements
+ */
+ private int column = -1;
+
+ private int direction = ORDER_ASC;
+
+ public void setColumn(int column)
+ {
+ if (this.column == column)
+ {
+ direction = direction == ORDER_ASC ? ORDER_DESC : ORDER_ASC;
+ }
+ else
+ {
+ this.column = column;
+ direction = ORDER_ASC;
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.jface.viewers.ViewerComparator#compare(org.eclipse.jface.viewers.Viewer, java.lang.Object, java.lang.Object)
+ */
+ @Override
+ public int compare(Viewer viewer, Object e1, Object e2)
+ {
+ DeviceSpecification deviceLeft = (DeviceSpecification) e1;
+ DeviceSpecification deviceRight = (DeviceSpecification) e2;
+
+ String left = ""; //$NON-NLS-1$
+ String right = ""; //$NON-NLS-1$
+ switch (column)
+ {
+ case NAME_COLUMN_INDEX:
+ left = deviceLeft.getName();
+ right = deviceRight.getName();
+ break;
+ case SCREENSIZE_COLUMN_INDEX:
+ left = deviceLeft.getDeviceInfo().getDefault().getScreenSize();
+ right = deviceRight.getDeviceInfo().getDefault().getScreenSize();
+ break;
+ case PIXELDENSITY_COLUMN_INDEX:
+ left = deviceLeft.getDeviceInfo().getDefault().getPixelDensity();
+ right = deviceRight.getDeviceInfo().getDefault().getPixelDensity();
+ break;
+ default:
+ break;
+ }
+
+ return left.compareTo(right) * direction;
+ }
+
+ /**
+ * Returns the SWT constant which represents the direction
+ * @return
+ */
+ public int getSwtDirection()
+ {
+ return direction == ORDER_ASC ? SWT.UP : SWT.DOWN;
+ }
+ }
+
+ private static final String NO_DEVICE_SELECTED = "none"; //$NON-NLS-1$
+
+ /**
+ * Index of device name column
+ */
+ private static final int NAME_COLUMN_INDEX = 0;
+
+ /**
+ * Index of screen size column
+ */
+ private static final int SCREENSIZE_COLUMN_INDEX = 1;
+
+ /**
+ * Index of pixel density column
+ */
+ private static final int PIXELDENSITY_COLUMN_INDEX = 2;
+
+ private Button selectAllCheck;
+
+ private CheckboxTableViewer devicesTableViewer;
+
+ private DevicesTableComparator devicesTableComparator;
+
+ /**
+ * Construct the GUI for the Devices Tab.
+ * @param parent
+ * @param style
+ * @param preferenceStore
+ */
+ public DevicesTabComposite(Composite parent, int style, IPreferenceStore preferenceStore)
+ {
+ super(parent, style);
+
+ //Create main layout
+ this.setLayout(new GridLayout(1, false));
+
+ Group deviceListGroup = new Group(this, SWT.NONE);
+ Layout layout = new GridLayout(1, false);
+ deviceListGroup.setLayout(layout);
+ deviceListGroup.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+ deviceListGroup.setText(PreflightingUiNLS.DevicesTabComposite_Devices_Group);
+
+ devicesTableViewer =
+ CheckboxTableViewer.newCheckList(deviceListGroup, SWT.BORDER | SWT.MULTI
+ | SWT.FULL_SELECTION);
+
+ Control devicesTableControl = devicesTableViewer.getTable();
+ GridData gd = new GridData(GridData.FILL_BOTH);
+ gd.heightHint = parent.getSize().y;
+ devicesTableControl.setLayoutData(gd);
+
+ devicesTableComparator = new DevicesTableComparator();
+ devicesTableViewer.setComparator(devicesTableComparator);
+
+ //Create Columns
+ TableViewerColumn column = new TableViewerColumn(devicesTableViewer, SWT.NONE);
+ column.getColumn().setText(PreflightingUiNLS.DevicesTabComposite_Name_Column);
+ column.getColumn().setResizable(true);
+ column.getColumn().setWidth(250);
+ column.getColumn().addSelectionListener(new ColumnSelectionAdapter(NAME_COLUMN_INDEX));
+
+ column = new TableViewerColumn(devicesTableViewer, SWT.NONE);
+ column.getColumn().setText(PreflightingUiNLS.DevicesTabComposite_ScreenSize_Column);
+ column.getColumn().setResizable(true);
+ column.getColumn().setWidth(80);
+ column.getColumn()
+ .addSelectionListener(new ColumnSelectionAdapter(SCREENSIZE_COLUMN_INDEX));
+
+ column = new TableViewerColumn(devicesTableViewer, SWT.NONE);
+ column.getColumn().setText(PreflightingUiNLS.DevicesTabComposite_pixelDensity_Column);
+ column.getColumn().setResizable(true);
+ column.getColumn().setWidth(80);
+ column.getColumn().addSelectionListener(
+ new ColumnSelectionAdapter(PIXELDENSITY_COLUMN_INDEX));
+
+ //Configure Table
+ devicesTableViewer.getTable().setHeaderVisible(true);
+
+ devicesTableViewer.setContentProvider(new DevicesContentProvider());
+ devicesTableViewer.setLabelProvider(new DevicesLabelProvider());
+
+ ValidationManager validationManager = new ValidationManager();
+ Collection<DeviceSpecification> deviceSpecifications =
+ validationManager.getDevicesSpecsContainer().getDeviceSpecifications();
+ devicesTableViewer.setInput(deviceSpecifications);
+
+ devicesTableViewer.addSelectionChangedListener(new ISelectionChangedListener()
+ {
+ public void selectionChanged(SelectionChangedEvent event)
+ {
+ boolean isAllSelected = isAllItemsChecked();
+ selectAllCheck.setSelection(isAllSelected);
+ }
+ });
+
+ //Create Select all section
+ Composite bottomComposite = new Composite(deviceListGroup, SWT.NONE);
+ gd = new GridData(SWT.FILL, SWT.BOTTOM, true, false);
+ bottomComposite.setLayoutData(gd);
+ layout = new GridLayout(2, true);
+ bottomComposite.setLayout(layout);
+ gd = new GridData(SWT.END, SWT.CENTER, false, true);
+
+ selectAllCheck = new Button(bottomComposite, SWT.CHECK);
+ selectAllCheck.setText(PreflightingUiNLS.DevicesTabComposite_SelectAll_Check);
+ selectAllCheck.setLayoutData(gd);
+ selectAllCheck.setSelection(true);
+ selectAllCheck.addSelectionListener(new SelectionAdapter()
+ {
+ @Override
+ public void widgetSelected(SelectionEvent e)
+ {
+ modifyCheckBoxes(selectAllCheck.getSelection());
+ super.widgetSelected(e);
+ }
+ });
+
+ init(preferenceStore);
+ }
+
+ /*
+ * Load data from preference store, reflecting in the GUI.
+ */
+ private void init(IPreferenceStore preferenceStore)
+ {
+ String prefKey = preferenceStore.getString(PreflightingUIPlugin.DEVICES_PREFERENCE_KEY);
+ if (prefKey.length() > 0) //Found devices, check them!
+ {
+ if (!prefKey.equals(NO_DEVICE_SELECTED))
+ {
+ modifyCheckBoxes(false);
+ StringTokenizer tokenizer = new StringTokenizer(prefKey, ","); //$NON-NLS-1$
+ while (tokenizer.hasMoreTokens())
+ {
+ String deviceIdPref = tokenizer.nextToken();
+ checkTableItem(deviceIdPref);
+ }
+ }
+ }
+ else
+ {
+ performDefaults();
+ }
+
+ selectAllCheck.setSelection(isAllItemsChecked());
+ }
+
+ private void checkTableItem(String deviceIdPref)
+ {
+ TableItem[] tableItems = devicesTableViewer.getTable().getItems();
+ boolean found = false;
+ int i = 0;
+ while (!found && (i < tableItems.length))
+ {
+ TableItem tableItem = tableItems[i];
+ DeviceSpecification deviceSpec = (DeviceSpecification) tableItem.getData();
+ String deviceIdTable = deviceSpec.getId();
+ if (deviceIdTable.equalsIgnoreCase(deviceIdPref))
+ {
+ tableItem.setChecked(true);
+ found = true;
+ }
+ else
+ {
+ i++;
+ }
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorolamobility.preflighting.ui.tabs.AbstractAppValidatorTabComposite#performDefaults()
+ */
+ @Override
+ public void performDefaults()
+ {
+ modifyCheckBoxes(true);
+ selectAllCheck.setSelection(true);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorolamobility.preflighting.ui.tabs.AbstractAppValidatorTabComposite#performOk(org.eclipse.jface.preference.IPreferenceStore)
+ */
+ @Override
+ public void performOk(IPreferenceStore preferenceStore)
+ {
+ Object[] elements = devicesTableViewer.getCheckedElements();
+ //Build the comma separated list with all checked devices ids
+ StringBuilder stringBuilder = new StringBuilder();
+ for (Object element : elements)
+ {
+ DeviceSpecification deviceSpec = (DeviceSpecification) element;
+ stringBuilder.append(deviceSpec.getId());
+ stringBuilder.append(","); //$NON-NLS-1$
+ }
+ //Remove the last comma.
+ if (stringBuilder.length() > 0)
+ {
+ stringBuilder.deleteCharAt(stringBuilder.length() - 1);
+ }
+ else if (stringBuilder.length() == 0)
+ {
+ stringBuilder.append(NO_DEVICE_SELECTED);
+ }
+
+ preferenceStore.setValue(PreflightingUIPlugin.DEVICES_PREFERENCE_KEY,
+ stringBuilder.toString());
+ preferenceStore.setValue(PreflightingUIPlugin.USE_ALL_DEVICES_PREFERENCE_KEY,
+ selectAllCheck.getSelection());
+
+ }
+
+ /*
+ * Update all checkboxes with the given value
+ */
+ private void modifyCheckBoxes(boolean check)
+ {
+ TableItem[] items = devicesTableViewer.getTable().getItems();
+ for (TableItem tableItem : items)
+ {
+ tableItem.setChecked(check);
+ }
+ }
+
+ /*
+ * Verifies if all items are checked
+ */
+ private boolean isAllItemsChecked()
+ {
+ TableItem[] items = devicesTableViewer.getTable().getItems();
+ boolean allChecked = true;
+ int i = 0;
+ while ((i < items.length) && allChecked)
+ {
+ TableItem tableItem = items[i];
+ if (!tableItem.getChecked())
+ {
+ allChecked = false;
+ }
+ i++;
+ }
+ return allChecked;
+ }
+
+ @Override
+ public IStatus isValid()
+ {
+ return Status.OK_STATUS;
+ }
+
+ @Override
+ public String commandLineBuilder()
+ {
+
+ String commandline = null;
+ if (!isAllItemsChecked())
+ {
+ Object[] checkedElements = devicesTableViewer.getCheckedElements();
+ StringBuilder stringBuilder = new StringBuilder(150);
+ if (checkedElements.length > 0)
+ {
+ for (Object checkedObj : checkedElements)
+ {
+ DeviceSpecification deviceSpec = (DeviceSpecification) checkedObj;
+ stringBuilder.append("-d ");
+ stringBuilder.append(deviceSpec.getId());
+ stringBuilder.append(" ");
+ }
+ }
+ else
+ {
+ stringBuilder.append("-d none");
+ }
+ commandline = stringBuilder.toString().trim();
+ }
+ else
+ {
+ commandline = "";
+ }
+ return commandline;
+
+ }
+}
diff --git a/src/plugins/preflighting.ui/src/com/motorolamobility/preflighting/ui/tabs/GeneralSettingsComposite.java b/src/plugins/preflighting.ui/src/com/motorolamobility/preflighting/ui/tabs/GeneralSettingsComposite.java
new file mode 100644
index 0000000..cf98ce2
--- /dev/null
+++ b/src/plugins/preflighting.ui/src/com/motorolamobility/preflighting/ui/tabs/GeneralSettingsComposite.java
@@ -0,0 +1,407 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorolamobility.preflighting.ui.tabs;
+
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.ModifyEvent;
+import org.eclipse.swt.events.ModifyListener;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.graphics.FontData;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Combo;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Group;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Layout;
+import org.eclipse.swt.widgets.Text;
+
+import com.motorolamobility.preflighting.ui.PreflightingUIPlugin;
+import com.motorolamobility.preflighting.ui.i18n.PreflightingUiNLS;
+
+public class GeneralSettingsComposite extends AbstractAppValidatorTabComposite
+{
+
+ /**
+ *
+ */
+ public static enum outputTypes
+ {
+ TEXT, XML, CSV
+ };
+
+ private int outputTypeSelection;
+
+ private Text limitText;
+
+ private Button outputTypePlainText;
+
+ private Button outputTypeXML;
+
+ private Button outputTypeCSV;
+
+ private Combo warningCombo;
+
+ private Combo verbosityCombo;
+
+ private Button eclipseProblemToWarningButton;
+
+ private IPreferenceStore prefStore;
+
+ private String errorMessage;
+
+ /**
+ * @return the canFinish
+ */
+ public boolean canFinish()
+ {
+
+ return canFinish;
+ }
+
+ /**
+ * @param canFinish the canFinish to set
+ */
+ public void setCanFinish(boolean canFinish)
+ {
+
+ this.canFinish = canFinish;
+ }
+
+ private boolean canFinish = true;
+
+ /**
+ * @param parent
+ * @param style
+ */
+ public GeneralSettingsComposite(Composite parent, int style)
+ {
+ super(parent, style);
+
+ prefStore = PreflightingUIPlugin.getDefault().getPreferenceStore();
+
+ outputTypeSelection =
+ Integer.parseInt(getPrefStoreValue(PreflightingUIPlugin.OUTPUT_TYPE_VALUE, "0"));
+
+ Layout layout = new GridLayout(1, true);
+ this.setLayout(layout);
+ this.setLayoutData(new GridData(GridData.FILL_BOTH));
+
+ Group limitGroup = new Group(this, SWT.NONE);
+ layout = new GridLayout(1, false);
+ limitGroup.setLayout(layout);
+ limitGroup.setText(PreflightingUiNLS.GeneralSettingsComposite_OutputSettingLabel);
+ limitGroup.setLayoutData(new GridData(SWT.FILL, SWT.NONE, true, false));
+
+ Composite limitsComposite = new Composite(limitGroup, SWT.NONE);
+ limitsComposite.setLayout(new GridLayout(3, false));
+ limitsComposite.setLayoutData(new GridData(SWT.LEFT, SWT.NONE, true, false));
+
+ Label limitLabel = new Label(limitsComposite, SWT.NONE);
+ limitLabel.setText(PreflightingUiNLS.GeneralSettingsComposite_OutputLimit);
+ GridData labelData = new GridData(SWT.LEFT, SWT.NONE, false, false);
+ limitLabel.setLayoutData(labelData);
+
+ limitText = new Text(limitsComposite, SWT.BORDER);
+ GridData textLayoutData = new GridData(SWT.LEFT, SWT.NONE, false, false);
+ textLayoutData.widthHint = 70;
+ limitText.setLayoutData(textLayoutData);
+
+ limitText.setText(getPrefStoreValue(PreflightingUIPlugin.OUTPUT_LIMIT_VALUE,
+ (PreflightingUIPlugin.OUTPUT_LIMIT_DEFAULT_VALUE)));
+
+ limitText.addModifyListener(new ModifyListener()
+ {
+
+ public void modifyText(ModifyEvent e)
+ {
+ int value = 0;
+ canFinish = true;
+ try
+ {
+ value = Integer.parseInt(limitText.getText());
+ if (value < 0)
+ {
+ canFinish = false;
+ errorMessage =
+ PreflightingUiNLS.GeneralSettingsComposite_OutputLimitNaNValidationMessage;
+
+ }
+ }
+ catch (NumberFormatException exc)
+ {
+
+ canFinish = false;
+ errorMessage =
+ PreflightingUiNLS.GeneralSettingsComposite_OutputLimitNaNValidationMessage;
+
+ }
+
+ notifyListener();
+ }
+ });
+
+ Label limitDefaultLabel = new Label(limitsComposite, SWT.NONE);
+ limitDefaultLabel.setText(PreflightingUiNLS.GeneralSettingsComposite_LimitLabel);
+
+ FontData[] oldFontData = limitDefaultLabel.getFont().getFontData();
+
+ for (FontData f : oldFontData)
+ {
+
+ f.setStyle(f.getStyle() | SWT.ITALIC);
+
+ }
+
+ Font newItFont = new Font(getDisplay(), oldFontData);
+ limitDefaultLabel.setFont(newItFont);
+ limitDefaultLabel.setLayoutData(new GridData(SWT.NONE, SWT.NONE, true, false));
+
+ Composite outputTypeComposite = new Composite(limitGroup, SWT.NONE);
+ layout = new GridLayout(1, false);
+ outputTypeComposite.setLayout(layout);
+ outputTypeComposite.setLayoutData(new GridData(SWT.NONE, SWT.NONE, true, false));
+
+ Label outputTypeLabel = new Label(outputTypeComposite, SWT.LEFT);
+ outputTypeLabel.setText(PreflightingUiNLS.GeneralSettingsComposite_OutputTypeLabel);
+
+ String outputType =
+ getPrefStoreValue(PreflightingUIPlugin.OUTPUT_TYPE_VALUE,
+ PreflightingUIPlugin.OUTPUT_TYPE_DEFAULT_VALUE);
+
+ outputTypePlainText = new Button(outputTypeComposite, SWT.RADIO);
+ outputTypePlainText
+ .setText(PreflightingUiNLS.GeneralSettingsComposite_PlainTextRadioButton);
+ outputTypePlainText.setSelection(outputType.equals(String.valueOf(outputTypes.TEXT
+ .ordinal())));
+ outputTypePlainText.addSelectionListener(new SelectionListener()
+ {
+
+ public void widgetSelected(SelectionEvent e)
+ {
+
+ if (outputTypePlainText.getSelection())
+ {
+ outputTypeSelection = outputTypes.TEXT.ordinal();
+ }
+
+ }
+
+ public void widgetDefaultSelected(SelectionEvent e)
+ {
+ //do nothing
+ }
+ });
+
+ outputTypeXML = new Button(outputTypeComposite, SWT.RADIO);
+ outputTypeXML.setText(PreflightingUiNLS.GeneralSettingsComposite_XMLOutputCombo);
+ outputTypeXML.setSelection(outputType.equals(String.valueOf(outputTypes.XML.ordinal()))); //$NON-NLS-1$
+ outputTypeXML.addSelectionListener(new SelectionListener()
+ {
+
+ public void widgetSelected(SelectionEvent e)
+ {
+ if (outputTypeXML.getSelection())
+ {
+ outputTypeSelection = outputTypes.XML.ordinal();
+ }
+ }
+
+ public void widgetDefaultSelected(SelectionEvent e)
+ {
+ //do nothing
+ }
+ });
+
+ outputTypeCSV = new Button(outputTypeComposite, SWT.RADIO);
+ outputTypeCSV.setText(PreflightingUiNLS.GeneralSettingsComposite_CSVOutputCombo);
+ outputTypeCSV.setSelection(outputType.equals(String.valueOf(outputTypes.CSV.ordinal()))); //$NON-NLS-1$
+
+ outputTypeCSV.addSelectionListener(new SelectionListener()
+ {
+
+ public void widgetSelected(SelectionEvent e)
+ {
+ if (outputTypeCSV.getSelection())
+ {
+ outputTypeSelection = outputTypes.CSV.ordinal();
+ }
+ }
+
+ public void widgetDefaultSelected(SelectionEvent e)
+ {
+ //do nothing
+ }
+ });
+
+ Group verbosityGroup = new Group(this, SWT.NONE);
+ layout = new GridLayout(1, false);
+ verbosityGroup.setLayout(layout);
+ verbosityGroup.setText(PreflightingUiNLS.GeneralSettingsComposite_VerbositySettingLabel);
+ verbosityGroup.setLayoutData(new GridData(SWT.FILL, SWT.NONE, true, false));
+
+ Label warningLevelsLabel = new Label(verbosityGroup, SWT.NONE);
+ warningLevelsLabel.setText(PreflightingUiNLS.GeneralSettingsComposite_WarningLevelSettings);
+
+ warningCombo = new Combo(verbosityGroup, SWT.BORDER | SWT.READ_ONLY);
+ GridData comboLayoutData = new GridData(SWT.NONE, SWT.NONE, false, true);
+ comboLayoutData.widthHint = 400;
+ warningCombo.setLayoutData(comboLayoutData);
+
+ warningCombo.add(PreflightingUiNLS.GeneralSettingsComposite_WarningLevel0);
+ warningCombo.add(PreflightingUiNLS.GeneralSettingsComposite_WarningLevel1);
+ warningCombo.add(PreflightingUiNLS.GeneralSettingsComposite_WarningLevel2);
+ warningCombo.add(PreflightingUiNLS.GeneralSettingsComposite_WarningLevel3);
+ warningCombo.add(PreflightingUiNLS.GeneralSettingsComposite_WarningLevel4);
+ warningCombo.select(Integer.parseInt(getPrefStoreValue(
+ PreflightingUIPlugin.WARNING_LEVEL_VALUE,
+ PreflightingUIPlugin.WARNING_LEVEL_DEFAULT_VALUE)));
+
+ Label verbosityLevelsLabel = new Label(verbosityGroup, SWT.NONE);
+ verbosityLevelsLabel
+ .setText(PreflightingUiNLS.GeneralSettingsComposite_VerbosityLevelLabel);
+
+ verbosityCombo = new Combo(verbosityGroup, SWT.BORDER | SWT.READ_ONLY);
+ verbosityCombo.setLayoutData(comboLayoutData);
+ verbosityCombo.add(PreflightingUiNLS.GeneralSettingsComposite_VerbosityLevel0);
+ verbosityCombo.add(PreflightingUiNLS.GeneralSettingsComposite_VerbosityLevel1);
+ verbosityCombo.add(PreflightingUiNLS.GeneralSettingsComposite_VerbosityLevel2);
+
+ verbosityCombo.select(Integer.parseInt(getPrefStoreValue(
+ PreflightingUIPlugin.VERBOSITY_LEVEL_VALUE,
+ PreflightingUIPlugin.VERBOSITY_LEVEL_DEFAULT_VALUE)));
+
+ eclipseProblemToWarningButton = new Button(this, SWT.CHECK);
+ eclipseProblemToWarningButton
+ .setText(PreflightingUiNLS.GeneralSettingsComposite_ShowErrosProblemsButton);
+ eclipseProblemToWarningButton.setSelection(Boolean.parseBoolean(getPrefStoreValue(
+ PreflightingUIPlugin.ECLIPSE_PROBLEM_TO_WARNING_VALUE,
+ PreflightingUIPlugin.ECLIPSE_PROBLEM_TO_WARNING_DEFAULT_VALUE)));
+
+ }
+
+ @Override
+ public void performDefaults()
+ {
+
+ limitText.setText(PreflightingUIPlugin.OUTPUT_LIMIT_DEFAULT_VALUE); //$NON-NLS-1$
+
+ outputTypePlainText.setSelection(true);
+
+ outputTypeSelection = 0;
+
+ outputTypeXML.setSelection(false);
+
+ outputTypeCSV.setSelection(false);
+
+ warningCombo.select(Integer.valueOf(PreflightingUIPlugin.WARNING_LEVEL_DEFAULT_VALUE));
+
+ verbosityCombo.select(0);
+
+ eclipseProblemToWarningButton.setSelection(true);
+
+ }
+
+ private String getPrefStoreValue(String prefKey, String defaultValue)
+ {
+
+ String returnValue = null;
+ if (!prefStore.isDefault(prefKey))
+ {
+ returnValue = prefStore.getString(prefKey);
+
+ }
+ else
+ {
+ returnValue = defaultValue;
+ }
+
+ return returnValue;
+
+ }
+
+ @Override
+ public IStatus isValid()
+ {
+ IStatus status =
+ new Status(IStatus.OK, PreflightingUIPlugin.PREFLIGHTING_UI_PLUGIN_ID,
+ PreflightingUiNLS.GeneralSettingsComposite_24);
+
+ if (!canFinish())
+ {
+ status =
+ new Status(IStatus.ERROR, PreflightingUIPlugin.PREFLIGHTING_UI_PLUGIN_ID,
+ errorMessage);
+ }
+
+ return status;
+ }
+
+ @Override
+ public void performOk(IPreferenceStore preferenceStore)
+ {
+ preferenceStore.setValue(PreflightingUIPlugin.OUTPUT_LIMIT_VALUE, limitText.getText());
+
+ preferenceStore.setValue(PreflightingUIPlugin.OUTPUT_TYPE_VALUE,
+ String.valueOf(outputTypeSelection));
+
+ preferenceStore.setValue(PreflightingUIPlugin.WARNING_LEVEL_VALUE,
+ String.valueOf(warningCombo.getSelectionIndex()));
+
+ preferenceStore.setValue(PreflightingUIPlugin.VERBOSITY_LEVEL_VALUE,
+ String.valueOf(verbosityCombo.getSelectionIndex()));
+
+ preferenceStore.setValue(PreflightingUIPlugin.ECLIPSE_PROBLEM_TO_WARNING_VALUE,
+ Boolean.toString(eclipseProblemToWarningButton.getSelection()));
+
+ }
+
+ @Override
+ public String commandLineBuilder()
+ {
+
+ StringBuilder commandLine = new StringBuilder();
+
+ //First part: output limit
+ String limit = limitText.getText();
+
+ commandLine.append(!limit.equals("0") ? "-limit " + limit + " " : "");
+
+ //Second, output type
+
+ commandLine.append("-output "
+ + GeneralSettingsComposite.outputTypes.values()[outputTypeSelection].toString()
+ .toLowerCase() + " ");
+
+ //warning levels
+
+ commandLine.append("-w" + String.valueOf(warningCombo.getSelectionIndex()) + " ");
+
+ //verbosity levels
+
+ commandLine.append("-v" + String.valueOf(verbosityCombo.getSelectionIndex()) + " ");
+
+ return commandLine.toString().trim();
+
+ }
+
+}
diff --git a/src/plugins/preflighting.ui/src/com/motorolamobility/preflighting/ui/tabs/UIChangedListener.java b/src/plugins/preflighting.ui/src/com/motorolamobility/preflighting/ui/tabs/UIChangedListener.java
new file mode 100644
index 0000000..0449623
--- /dev/null
+++ b/src/plugins/preflighting.ui/src/com/motorolamobility/preflighting/ui/tabs/UIChangedListener.java
@@ -0,0 +1,24 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorolamobility.preflighting.ui.tabs;
+
+
+public interface UIChangedListener
+{
+
+ public void uiChanged(AbstractAppValidatorTabComposite composite);
+
+}
diff --git a/src/plugins/preflighting.ui/src/com/motorolamobility/preflighting/ui/utilities/EclipseUtils.java b/src/plugins/preflighting.ui/src/com/motorolamobility/preflighting/ui/utilities/EclipseUtils.java
new file mode 100644
index 0000000..7e6674a
--- /dev/null
+++ b/src/plugins/preflighting.ui/src/com/motorolamobility/preflighting/ui/utilities/EclipseUtils.java
@@ -0,0 +1,56 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorolamobility.preflighting.ui.utilities;
+
+import java.util.List;
+
+import org.eclipse.jface.preference.IPreferenceNode;
+import org.eclipse.jface.preference.PreferenceDialog;
+import org.eclipse.jface.preference.PreferenceManager;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.internal.dialogs.WorkbenchPreferenceDialog;
+
+
+@SuppressWarnings("restriction")
+public class EclipseUtils
+{
+ @SuppressWarnings("unchecked")
+ public static void openPreference(Shell shell, String nodeID)
+ {
+ // Makes the network preferences dialog manager
+ PreferenceManager manager = PlatformUI.getWorkbench().getPreferenceManager();
+ IPreferenceNode networkNode = null;
+ for (IPreferenceNode node : (List<IPreferenceNode>) manager
+ .getElements(PreferenceManager.PRE_ORDER))
+ {
+ if (node.getId().equals(nodeID))
+ {
+ networkNode = node;
+ break;
+ }
+ }
+ PreferenceManager prefMan = new PreferenceManager();
+ if (networkNode != null)
+ {
+ prefMan.addToRoot(networkNode);
+ }
+ PreferenceDialog preferencesDialog = new WorkbenchPreferenceDialog(shell, prefMan);
+ preferencesDialog.create();
+ preferencesDialog.open();
+ }
+
+}
diff --git a/src/plugins/preflighting.ui/src/com/motorolamobility/preflighting/ui/wizards/ApkValidationWizard.java b/src/plugins/preflighting.ui/src/com/motorolamobility/preflighting/ui/wizards/ApkValidationWizard.java
new file mode 100644
index 0000000..5e5f1bb
--- /dev/null
+++ b/src/plugins/preflighting.ui/src/com/motorolamobility/preflighting/ui/wizards/ApkValidationWizard.java
@@ -0,0 +1,96 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+package com.motorolamobility.preflighting.ui.wizards;
+
+import java.io.File;
+import java.net.URL;
+import java.util.List;
+
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.StructuredSelection;
+import org.eclipse.jface.wizard.Wizard;
+import org.eclipse.ui.plugin.AbstractUIPlugin;
+import org.osgi.framework.Bundle;
+
+import com.motorolamobility.preflighting.ui.PreflightingUIPlugin;
+import com.motorolamobility.preflighting.ui.handlers.AnalyzeApkHandler;
+import com.motorolamobility.preflighting.ui.i18n.PreflightingUiNLS;
+
+/**
+ * This Wizard selects the packages to validate
+ */
+public class ApkValidationWizard extends Wizard
+{
+ private ApkValidationWizardPage page = null;
+
+ private final ExecutionEvent event;
+
+ public ApkValidationWizard(IStructuredSelection selection, ExecutionEvent event)
+ {
+ setWindowTitle(PreflightingUiNLS.ApkValidationWizard_wizardTitle);
+ setNeedsProgressMonitor(true);
+ setHelpAvailable(false);
+ this.page = new ApkValidationWizardPage("apkWizardPage", selection); //$NON-NLS-1$
+ this.event = event;
+
+ Bundle bundle = PreflightingUIPlugin.getDefault().getBundle();
+ URL url =
+ bundle.getEntry((new StringBuilder("/")).append( //$NON-NLS-1$
+ "icons" + IPath.SEPARATOR + "MOTODEVAppValidator_64x64.png") //$NON-NLS-1$ //$NON-NLS-2$
+ .toString());
+
+ setDefaultPageImageDescriptor(AbstractUIPlugin.imageDescriptorFromPlugin(
+ PreflightingUIPlugin.PREFLIGHTING_UI_PLUGIN_ID, url.getPath()));
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.jface.wizard.Wizard#addPages()
+ */
+ @Override
+ public void addPages()
+ {
+ addPage(this.page);
+ }
+
+ /**
+ * Finishes this wizard, validating the selected packages
+ */
+ @Override
+ public boolean performFinish()
+ {
+ List<File> selectedFiles = ApkValidationWizard.this.page.getSelectedPackages();
+
+ StructuredSelection selection = new StructuredSelection(selectedFiles);
+ AnalyzeApkHandler apkHandler = new AnalyzeApkHandler(selection);
+ try
+ {
+ apkHandler.execute(event);
+ }
+ catch (ExecutionException e)
+ {
+ //do nothing
+ }
+
+ return true;
+ }
+
+}
diff --git a/src/plugins/preflighting.ui/src/com/motorolamobility/preflighting/ui/wizards/ApkValidationWizardPage.java b/src/plugins/preflighting.ui/src/com/motorolamobility/preflighting/ui/wizards/ApkValidationWizardPage.java
new file mode 100644
index 0000000..d9fba62
--- /dev/null
+++ b/src/plugins/preflighting.ui/src/com/motorolamobility/preflighting/ui/wizards/ApkValidationWizardPage.java
@@ -0,0 +1,435 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+package com.motorolamobility.preflighting.ui.wizards;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.jface.dialogs.IMessageProvider;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.wizard.WizardPage;
+import org.eclipse.swt.SWT;
+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.DirectoryDialog;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.swt.widgets.Tree;
+import org.eclipse.swt.widgets.TreeItem;
+
+import com.motorolamobility.preflighting.ui.i18n.PreflightingUiNLS;
+
+public class ApkValidationWizardPage extends WizardPage
+{
+ private Text sourceDirText = null;
+
+ private Button browseDirButton = null;
+
+ private Tree packagesTree = null;
+
+ private Button selectAllButton = null;
+
+ private Button deselectAllButton = null;
+
+ //private WizardSelection selection = null;
+
+ protected Composite mainComposite = null;
+
+ /**
+ * Create a new wizard page based on selection
+ *
+ * @param pageName
+ * the page name
+ * @param selection
+ * the selection
+ */
+ public ApkValidationWizardPage(String pageName, IStructuredSelection selection)
+ {
+ super(pageName);
+ setDescription(PreflightingUiNLS.ApkValidationWizardPage_description);
+ setTitle(PreflightingUiNLS.ApkValidationWizardPage_title);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * org.eclipse.jface.dialogs.IDialogPage#createControl(org.eclipse.swt.widgets
+ * .Composite)
+ */
+ public void createControl(Composite parent)
+ {
+ this.mainComposite = new Composite(parent, SWT.NULL);
+ // create new layout with 3 columns of different sizes
+ GridLayout layout = new GridLayout(3, false);
+ this.mainComposite.setLayout(layout);
+
+ Label sourceDirLabel = new Label(this.mainComposite, SWT.NONE);
+ sourceDirLabel.setText(PreflightingUiNLS.ApkValidationWizardPage_folderLabel);
+
+ GridData layoutData = new GridData(SWT.LEFT, SWT.CENTER, false, false);
+ sourceDirLabel.setLayoutData(layoutData);
+
+ this.sourceDirText = new Text(this.mainComposite, SWT.BORDER);
+ layoutData = new GridData(SWT.FILL, SWT.CENTER, true, false);
+ this.sourceDirText.setLayoutData(layoutData);
+ this.sourceDirText.addListener(SWT.Modify, new SourceDirectoryTextListener());
+
+ this.browseDirButton = new Button(this.mainComposite, SWT.PUSH);
+ this.browseDirButton.setText(PreflightingUiNLS.ApkValidationWizardPage_browseLabel);
+ layoutData = new GridData(SWT.FILL, SWT.CENTER, false, false);
+ this.browseDirButton.setLayoutData(layoutData);
+ this.browseDirButton.addListener(SWT.Selection, new BrowseButtonListener());
+
+ Label packagesLabel = new Label(this.mainComposite, SWT.NONE);
+ packagesLabel.setText(PreflightingUiNLS.ApkValidationWizardPage_packagesLabel);
+ packagesLabel.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false, 3, 1));
+
+ this.packagesTree = new Tree(this.mainComposite, SWT.BORDER | SWT.CHECK | SWT.V_SCROLL);
+ layoutData = new GridData(SWT.FILL, SWT.FILL, true, true, 2, 2);
+ layoutData.heightHint = 150;
+ this.packagesTree.setLayoutData(layoutData);
+ this.packagesTree.addListener(SWT.Selection, new TreeSelectionListener());
+
+ Composite selectionButtons = new Composite(this.mainComposite, SWT.FILL);
+ layoutData = new GridData(SWT.FILL, SWT.TOP, false, true, 1, 2);
+ selectionButtons.setLayoutData(layoutData);
+ FillLayout row = new FillLayout(SWT.VERTICAL);
+ row.spacing = 3;
+ selectionButtons.setLayout(row);
+
+ this.selectAllButton = new Button(selectionButtons, SWT.PUSH);
+ this.selectAllButton.setText(PreflightingUiNLS.ApkValidationWizardPage_selectAllLabel);
+ SelectionButtonsListener selectionButtonsListener = new SelectionButtonsListener();
+ this.selectAllButton.addListener(SWT.Selection, selectionButtonsListener);
+
+ this.deselectAllButton = new Button(selectionButtons, SWT.PUSH);
+ this.deselectAllButton.setText(PreflightingUiNLS.ApkValidationWizardPage_deseletAllLabel);
+ this.deselectAllButton.addListener(SWT.Selection, selectionButtonsListener);
+
+ updatePageComplete();
+ setControl(this.mainComposite);
+ }
+
+ /**
+ * Populates the tree with the packages of base dir Requires a valid folder
+ * set as source dir
+ */
+ private void populateTree(List<String> selection)
+ {
+ File sourceDir = getSourcePath().toFile();
+ this.packagesTree.removeAll();
+ if (sourceDir.isDirectory() && sourceDir.canWrite())
+ {
+ File[] list = sourceDir.listFiles();
+ for (File file : list)
+ {
+ if (file.canRead() && file.isFile() && file.getName().endsWith(".apk")) //$NON-NLS-1$
+ {
+ TreeItem fileItem = new TreeItem(this.packagesTree, SWT.NONE);
+ String text = file.getName();
+
+ fileItem.setText(text);
+ fileItem.setData(file);
+ if ((selection != null) && selection.contains(file.getName())
+ && file.canWrite())
+ {
+ fileItem.setChecked(true);
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Validates if the source directory is valid one
+ *
+ * @return true if the source dir text is valid, false otherwise
+ */
+ private boolean isSourceDirValid()
+ {
+
+ String messageAux = null;
+ int severity = IMessageProvider.NONE;
+
+ /*
+ * Check if the selected location is valid, even if non existent.
+ */
+ IPath path = new Path(this.sourceDirText.getText());
+
+ // Test if path is blank, to warn user instead of show an error message
+ if (this.sourceDirText.getText().equals("")) //$NON-NLS-1$
+ {
+ messageAux = PreflightingUiNLS.ApkValidationWizardPage_emptyListMsg;
+ severity = IMessageProvider.INFORMATION;
+ }
+
+ /*
+ * Do Win32 Validation
+ */
+ if ((messageAux == null) && Platform.getOS().equalsIgnoreCase(Platform.OS_WIN32))
+ {
+ // test path size
+ if (path.toString().length() > 255)
+ {
+ messageAux = PreflightingUiNLS.ApkValidationWizardPage_tooLongMsg;
+ severity = IMessageProvider.WARNING;
+ }
+ String device = path.getDevice();
+ File deviceFile = null;
+ if (device != null)
+ {
+ deviceFile = new File(path.getDevice());
+ }
+
+ if ((device != null) && !deviceFile.exists())
+ {
+ messageAux =
+ PreflightingUiNLS.ApkValidationWizardPage_invalidDeviceMsg
+ + " [" + device + "]"; //$NON-NLS-2$ //$NON-NLS-3$
+ severity = IMessageProvider.ERROR;
+ }
+
+ }
+ // test if path is absolute
+ if (messageAux == null)
+ {
+ if (!path.isAbsolute() || !path.toFile().exists())
+ {
+ messageAux = PreflightingUiNLS.ApkValidationWizardPage_invalidFolderMsg;
+ severity = IMessageProvider.ERROR;
+ }
+ }
+
+ if (messageAux == null)
+ {
+ for (String folderName : path.segments())
+ {
+ if (!ResourcesPlugin.getWorkspace().validateName(folderName, IResource.FOLDER)
+ .isOK())
+ {
+ messageAux = PreflightingUiNLS.ApkValidationWizardPage_invalidFolderMsg;
+ severity = IMessageProvider.ERROR;
+ }
+ }
+ }
+
+ if ((messageAux == null) && ((path.toFile().exists() && !path.toFile().isDirectory())))
+ {
+ messageAux = PreflightingUiNLS.ApkValidationWizardPage_invalidSourceDirectoryMsg;
+ severity = IMessageProvider.ERROR;
+ }
+
+ /*
+ * Setting message
+ */
+ if (messageAux == null)
+ {
+ messageAux = PreflightingUiNLS.ApkValidationWizardPage_validateMsg;
+ severity = IMessageProvider.NONE;
+ }
+ setMessage(messageAux, severity);
+ return severity == IMessageProvider.NONE;
+ }
+
+ /**
+ * @return the path of base dir where packages are located
+ */
+ public IPath getSourcePath()
+ {
+ return new Path(this.sourceDirText.getText());
+ }
+
+ /**
+ *
+ * @return the list with selected packages
+ */
+ public List<File> getSelectedPackages()
+ {
+ ArrayList<File> selected = new ArrayList<File>();
+ for (TreeItem item : this.packagesTree.getItems())
+ {
+ if (item.getChecked())
+ {
+ selected.add((File) item.getData());
+ }
+ }
+
+ return selected;
+ }
+
+ /**
+ * Update the page status, validating each field of this page Subclasses
+ */
+ public void updatePageComplete()
+ {
+ String messageAux = null;
+ int severity = IMessageProvider.NONE;
+
+ if (isSourceDirValid())
+ {
+ if (this.packagesTree.getItemCount() == 0)
+ {
+ messageAux = PreflightingUiNLS.ApkValidationWizardPage_emptyFolderMsg;
+ severity = IMessageProvider.ERROR;
+ }
+ }
+ else
+ {
+ messageAux = getMessage();
+ severity = getMessageType();
+ }
+
+ if ((messageAux == null) && (getSelectedPackages().size() == 0))
+ {
+ messageAux = PreflightingUiNLS.ApkValidationWizardPage_onePackageMsg;
+ severity = IMessageProvider.INFORMATION;
+ }
+
+ if (messageAux == null)
+ {
+ messageAux = PreflightingUiNLS.ApkValidationWizardPage_validateMsg;
+ severity = IMessageProvider.NONE;
+ }
+
+ setMessage(messageAux, severity);
+ setPageComplete(severity == IMessageProvider.NONE);
+ }
+
+ /**
+ * This class implements the listener of browse button, opening the browse
+ * window and updating the dir text
+ */
+ class BrowseButtonListener implements Listener
+ {
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * org.eclipse.swt.widgets.Listener#handleEvent(org.eclipse.swt.widgets
+ * .Event)
+ */
+ public void handleEvent(Event event)
+ {
+ DirectoryDialog dialog =
+ new DirectoryDialog(ApkValidationWizardPage.this.mainComposite.getShell());
+ dialog.setFilterPath(!ApkValidationWizardPage.this.sourceDirText.getText().trim()
+ .equals("") ? ApkValidationWizardPage.this.sourceDirText.getText() : null); //$NON-NLS-1$
+ String path = dialog.open();
+ if (path != null)
+ {
+ ApkValidationWizardPage.this.sourceDirText.setText(path);
+ populateTree(null);
+ updatePageComplete();
+ }
+ }
+ }
+
+ /*
+ * Listener to validate any SourceDirectory text Change
+ */
+ class SourceDirectoryTextListener implements Listener
+ {
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * org.eclipse.swt.widgets.Listener#handleEvent(org.eclipse.swt.widgets
+ * .Event)
+ */
+ public void handleEvent(Event event)
+ {
+ ApkValidationWizardPage.this.packagesTree.removeAll();
+ if (isSourceDirValid())
+ {
+ populateTree(null);
+ }
+ updatePageComplete();
+ }
+ }
+
+ /*
+ * This class handles clicks on select all and deselect all buttons
+ */
+ class SelectionButtonsListener implements Listener
+ {
+
+ /**
+ * Check/Uncheck all items
+ *
+ * @param check
+ * : true for check, false for unckeck
+ */
+ private void setCheckedAll(boolean check)
+ {
+ for (TreeItem item : ApkValidationWizardPage.this.packagesTree.getItems())
+ {
+ item.setChecked(check);
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * org.eclipse.swt.widgets.Listener#handleEvent(org.eclipse.swt.widgets
+ * .Event)
+ */
+ public void handleEvent(Event event)
+ {
+ if (event.widget == ApkValidationWizardPage.this.selectAllButton)
+ {
+ setCheckedAll(true);
+ }
+ else if (event.widget == ApkValidationWizardPage.this.deselectAllButton)
+ {
+ setCheckedAll(false);
+ }
+ updatePageComplete();
+ }
+ }
+
+ /**
+ * Listener to update wizard status according tree selection
+ *
+ */
+ class TreeSelectionListener implements Listener
+ {
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * org.eclipse.swt.widgets.Listener#handleEvent(org.eclipse.swt.widgets
+ * .Event)
+ */
+ public void handleEvent(Event event)
+ {
+ updatePageComplete();
+ }
+ }
+}
diff --git a/src/plugins/preflighting/.classpath b/src/plugins/preflighting/.classpath
new file mode 100644
index 0000000..8875508
--- /dev/null
+++ b/src/plugins/preflighting/.classpath
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.5"/>
+ <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/src/plugins/preflighting/.project b/src/plugins/preflighting/.project
new file mode 100644
index 0000000..34a1e20
--- /dev/null
+++ b/src/plugins/preflighting/.project
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>com.motorolamobility.preflighting</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.ManifestBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.SchemaBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.pde.PluginNature</nature>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+</projectDescription>
diff --git a/src/plugins/preflighting/META-INF/MANIFEST.MF b/src/plugins/preflighting/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..b0c6a54
--- /dev/null
+++ b/src/plugins/preflighting/META-INF/MANIFEST.MF
@@ -0,0 +1,19 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: %pluginName
+Bundle-SymbolicName: com.motorolamobility.preflighting;singleton:=true
+Bundle-Version: 2.0.0.qualifier
+Bundle-Vendor: %providerName
+Require-Bundle: org.eclipse.core.runtime,
+ com.motorolamobility.preflighting.core,
+ org.junit
+Bundle-RequiredExecutionEnvironment: JavaSE-1.6
+Bundle-Localization: plugin
+Bundle-ActivationPolicy: lazy
+Export-Package: com.motorolamobility.preflighting.internal,
+ com.motorolamobility.preflighting.internal.commandinput,
+ com.motorolamobility.preflighting.internal.commandinput.exception,
+ com.motorolamobility.preflighting.internal.commandoutput,
+ com.motorolamobility.preflighting.output
+Import-Package: org.apache.xerces.jaxp
+Bundle-Activator: com.motorolamobility.preflighting.internal.PreflightingPlugin
diff --git a/src/plugins/preflighting/build.properties b/src/plugins/preflighting/build.properties
new file mode 100644
index 0000000..80ecad3
--- /dev/null
+++ b/src/plugins/preflighting/build.properties
@@ -0,0 +1,7 @@
+source.. = src/
+output.. = bin/
+bin.includes = plugin.xml,\
+ META-INF/,\
+ .,\
+ plugin.properties,\
+ schema/
diff --git a/src/plugins/preflighting/icons/AppValidator.ico b/src/plugins/preflighting/icons/AppValidator.ico
new file mode 100644
index 0000000..91d7ee6
--- /dev/null
+++ b/src/plugins/preflighting/icons/AppValidator.ico
Binary files differ
diff --git a/src/plugins/preflighting/icons/AppValidator.xpm b/src/plugins/preflighting/icons/AppValidator.xpm
new file mode 100644
index 0000000..67b4827
--- /dev/null
+++ b/src/plugins/preflighting/icons/AppValidator.xpm
@@ -0,0 +1,1220 @@
+/* XPM */
+static char * AppValidator_xpm[] = {
+"512 512 705 2",
+" c None",
+". c #A2C538",
+"+ c #A4C438",
+"@ c #A0C536",
+"# c #A3C437",
+"$ c #A3C336",
+"% c #A3C438",
+"& c #A1C338",
+"* c #A4C538",
+"= c #A3C538",
+"- c #A3C537",
+"; c #A4C437",
+"> c #A2C438",
+", c #A3C436",
+"' c #A0C237",
+") c #A2C13E",
+"! c #A2C436",
+"~ c #A2C439",
+"{ c #A2C435",
+"] c #A1C335",
+"^ c #A3C337",
+"/ c #A2C539",
+"( c #A3C536",
+"_ c #A1C436",
+": c #A4C537",
+"< c #A2C335",
+"[ c #A1C435",
+"} c #A3C435",
+"| c #A2C437",
+"1 c #A2C537",
+"2 c #A4C439",
+"3 c #A1C334",
+"4 c #A2C334",
+"5 c #A4C539",
+"6 c #A4C536",
+"7 c #A2C336",
+"8 c #A2C337",
+"9 c #A4C337",
+"0 c #A3C539",
+"a c #A1C537",
+"b c #A1C437",
+"c c #A2C434",
+"d c #A3C638",
+"e c #A4C436",
+"f c #A4C338",
+"g c #A3C338",
+"h c #A3C439",
+"i c #A1C333",
+"j c #A7C43B",
+"k c #A1C434",
+"l c #A5C439",
+"m c #A7C740",
+"n c #A5C43A",
+"o c #A5C539",
+"p c #A7C53A",
+"q c #A8C53C",
+"r c #A4C53A",
+"s c #A2C338",
+"t c #A0C332",
+"u c #A0C239",
+"v c #A2C536",
+"w c #A3C335",
+"x c #A4C435",
+"y c #A1C438",
+"z c #9FBE3D",
+"A c #A5C63D",
+"B c #A4C336",
+"C c #A6C53E",
+"D c #A6C53C",
+"E c #ABCA4A",
+"F c #A4C639",
+"G c #A6C73F",
+"H c #A4C638",
+"I c #A0C435",
+"J c #A5C53A",
+"K c #A6C73E",
+"L c #A3C535",
+"M c #A3C53A",
+"N c #A6C63C",
+"O c #A2C637",
+"P c #A2C238",
+"Q c #A6C63E",
+"R c #A6C63D",
+"S c #A2C535",
+"T c #000000",
+"U c #020300",
+"V c #A1C336",
+"W c #0B0B0B",
+"X c #3F3F3F",
+"Y c #3D3D3D",
+"Z c #222222",
+"` c #919190",
+" . c #FFFFFF",
+".. c #E5E5E4",
+"+. c #C5C5C5",
+"@. c #050505",
+"#. c #0D1004",
+"$. c #050504",
+"%. c #3A3A3A",
+"&. c #FCFCFB",
+"*. c #5C5C5B",
+"=. c #0E0D0E",
+"-. c #0C0C0B",
+";. c #E9E9E9",
+">. c #FEFEFE",
+",. c #FBFBFB",
+"'. c #F9F9F9",
+"). c #FCFCFC",
+"!. c #393939",
+"~. c #0A0A0A",
+"{. c #030303",
+"]. c #C2C2C2",
+"^. c #020201",
+"/. c #252525",
+"(. c #ABABAB",
+"_. c #F3F3F3",
+":. c #040404",
+"<. c #616161",
+"[. c #040503",
+"}. c #2B2B2B",
+"|. c #C9C9C9",
+"1. c #FDFDFD",
+"2. c #B8B8B7",
+"3. c #040403",
+"4. c #010101",
+"5. c #A3C434",
+"6. c #858584",
+"7. c #A3C639",
+"8. c #A4C53B",
+"9. c #121312",
+"0. c #F6F6F6",
+"a. c #2F2F2F",
+"b. c #E0E0E0",
+"c. c #060606",
+"d. c #D3D3D3",
+"e. c #2A2A2A",
+"f. c #E2E2E2",
+"g. c #ADD03B",
+"h. c #363636",
+"i. c #CACACA",
+"j. c #A3C237",
+"k. c #A2C236",
+"l. c #FFFFFE",
+"m. c #B7B7B7",
+"n. c #6F6F6F",
+"o. c #767676",
+"p. c #EFEFEF",
+"q. c #090909",
+"r. c #B2B2B2",
+"s. c #666666",
+"t. c #A4C535",
+"u. c #F0F0F0",
+"v. c #E3E3E3",
+"w. c #7A7A7A",
+"x. c #020202",
+"y. c #505050",
+"z. c #E8E8E8",
+"A. c #A1C238",
+"B. c #A0C03D",
+"C. c #070707",
+"D. c #DBDBDB",
+"E. c #212121",
+"F. c #080808",
+"G. c #EAEAEA",
+"H. c #282828",
+"I. c #242424",
+"J. c #010201",
+"K. c #A2C235",
+"L. c #A1C235",
+"M. c #010200",
+"N. c #010100",
+"O. c #0F1205",
+"P. c #A4C335",
+"Q. c #647921",
+"R. c #2D360F",
+"S. c #070802",
+"T. c #030301",
+"U. c #849F2B",
+"V. c #52631B",
+"W. c #20270B",
+"X. c #050601",
+"Y. c #9DBD33",
+"Z. c #789127",
+"`. c #495818",
+" + c #1C2209",
+".+ c #97B631",
+"++ c #748B26",
+"@+ c #3C4914",
+"#+ c #131606",
+"$+ c #8CA930",
+"%+ c #637521",
+"&+ c #313A10",
+"*+ c #A1C036",
+"=+ c #859F2B",
+"-+ c #55661C",
+";+ c #9EBF35",
+">+ c #A4C637",
+",+ c #A2C237",
+"'+ c #A1C136",
+")+ c #A6C738",
+"!+ c #3AC500",
+"~+ c #3CC501",
+"{+ c #3EC600",
+"]+ c #30C500",
+"^+ c #2DC400",
+"/+ c #3CC502",
+"(+ c #35C500",
+"_+ c #36C500",
+":+ c #26C400",
+"<+ c #38C502",
+"[+ c #46C504",
+"}+ c #44C504",
+"|+ c #27C400",
+"1+ c #36C501",
+"2+ c #3FC501",
+"3+ c #38C500",
+"4+ c #3BC503",
+"5+ c #37C500",
+"6+ c #3EC502",
+"7+ c #40C600",
+"8+ c #34C500",
+"9+ c #35C501",
+"0+ c #3AC502",
+"a+ c #40C503",
+"b+ c #37C600",
+"c+ c #37C503",
+"d+ c #22C400",
+"e+ c #3FC502",
+"f+ c #3FC601",
+"g+ c #32C500",
+"h+ c #33C400",
+"i+ c #31C400",
+"j+ c #39C502",
+"k+ c #31C500",
+"l+ c #31C501",
+"m+ c #42C503",
+"n+ c #A4C236",
+"o+ c #41C504",
+"p+ c #A5C738",
+"q+ c #2AC400",
+"r+ c #35C401",
+"s+ c #3AC501",
+"t+ c #6D8325",
+"u+ c #A0C036",
+"v+ c #A5C637",
+"w+ c #37C501",
+"x+ c #32C501",
+"y+ c #383F26",
+"z+ c #98AE50",
+"A+ c #A0C035",
+"B+ c #A1C139",
+"C+ c #39C501",
+"D+ c #3AC600",
+"E+ c #030401",
+"F+ c #000002",
+"G+ c #1C2109",
+"H+ c #4F5F1B",
+"I+ c #39C500",
+"J+ c #040501",
+"K+ c #303913",
+"L+ c #28300E",
+"M+ c #6C8225",
+"N+ c #87A22D",
+"O+ c #47C504",
+"P+ c #131706",
+"Q+ c #5B6B2C",
+"R+ c #9EBA42",
+"S+ c #1D230B",
+"T+ c #56681D",
+"U+ c #98B733",
+"V+ c #30C401",
+"W+ c #2BC400",
+"X+ c #2EC400",
+"Y+ c #3DC501",
+"Z+ c #3DC502",
+"`+ c #35C502",
+" @ c #46C604",
+".@ c #3EC503",
+"+@ c #29C400",
+"@@ c #9FC232",
+"#@ c #34C402",
+"$@ c #41C503",
+"%@ c #3FC503",
+"&@ c #AAC949",
+"*@ c #A6C53F",
+"=@ c #31C401",
+"-@ c #28C400",
+";@ c #37C505",
+">@ c #20C300",
+",@ c #FFD686",
+"'@ c #8BCC37",
+")@ c #38C600",
+"!@ c #2DC500",
+"~@ c #FFD694",
+"{@ c #7ACA2E",
+"]@ c #3AC50B",
+"^@ c #30C400",
+"/@ c #2EC500",
+"(@ c #FFD580",
+"_@ c #FFD57F",
+":@ c #ECD370",
+"<@ c #98CD3E",
+"[@ c #ABCE3A",
+"}@ c #1BC300",
+"|@ c #32C400",
+"1@ c #47C60E",
+"2@ c #F0D478",
+"3@ c #37C400",
+"4@ c #19C300",
+"5@ c #46C505",
+"6@ c #40C501",
+"7@ c #1FC400",
+"8@ c #54C614",
+"9@ c #FCD478",
+"0@ c #FBD477",
+"a@ c #FFD582",
+"b@ c #FFD99A",
+"c@ c #B4D052",
+"d@ c #0AC200",
+"e@ c #38C501",
+"f@ c #1DC300",
+"g@ c #81CB34",
+"h@ c #FFD689",
+"i@ c #FFD47E",
+"j@ c #FED57A",
+"k@ c #FED579",
+"l@ c #FED47A",
+"m@ c #FDD57A",
+"n@ c #FFD579",
+"o@ c #FED57B",
+"p@ c #FED67A",
+"q@ c #FDD477",
+"r@ c #FBD47C",
+"s@ c #FFD68D",
+"t@ c #FFD589",
+"u@ c #17C300",
+"v@ c #2EC401",
+"w@ c #000100",
+"x@ c #FCD47B",
+"y@ c #FFD78C",
+"z@ c #FED783",
+"A@ c #7BCA32",
+"B@ c #22C300",
+"C@ c #14C200",
+"D@ c #C3D05C",
+"E@ c #FFD792",
+"F@ c #FFD67F",
+"G@ c #FFD57C",
+"H@ c #FCD479",
+"I@ c #FAD47B",
+"J@ c #FFD688",
+"K@ c #FFD692",
+"L@ c #FFD78B",
+"M@ c #FAD476",
+"N@ c #FDD479",
+"O@ c #FDD47A",
+"P@ c #FFD57A",
+"Q@ c #FFD679",
+"R@ c #FED679",
+"S@ c #FFD47A",
+"T@ c #FFD67A",
+"U@ c #FDD579",
+"V@ c #FED67B",
+"W@ c #FCD477",
+"X@ c #FBD47A",
+"Y@ c #F8D479",
+"Z@ c #FFD796",
+"`@ c #F9D68C",
+" # c #76CA2D",
+".# c #29C300",
+"+# c #1AC200",
+"@# c #FDD57E",
+"## c #FFD78E",
+"$# c #F3D47A",
+"%# c #84CB36",
+"&# c #21C300",
+"*# c #43C610",
+"=# c #F3D582",
+"-# c #CCD164",
+";# c #CED15D",
+"># c #FFD687",
+",# c #FDD68B",
+"'# c #99CD43",
+")# c #FFD793",
+"!# c #FBD476",
+"~# c #FDD67A",
+"{# c #FBD479",
+"]# c #5DC822",
+"^# c #3CC503",
+"/# c #43C604",
+"(# c #28C300",
+"_# c #56C818",
+":# c #9DCE46",
+"<# c #FED584",
+"[# c #E2D473",
+"}# c #34C40B",
+"|# c #1EC300",
+"1# c #6BC921",
+"2# c #59C817",
+"3# c #4DC710",
+"4# c #FFD789",
+"5# c #C5D05D",
+"6# c #64C81D",
+"7# c #ADCF4E",
+"8# c #FCD685",
+"9# c #FED479",
+"0# c #FBD478",
+"a# c #23C400",
+"b# c #2FC500",
+"c# c #2CC400",
+"d# c #C9D163",
+"e# c #FFD684",
+"f# c #FFD785",
+"g# c #CFD167",
+"h# c #5FC71A",
+"i# c #3AC509",
+"j# c #38C507",
+"k# c #95CD44",
+"l# c #21C400",
+"m# c #FDD578",
+"n# c #FCD577",
+"o# c #FDD57B",
+"p# c #FFD68B",
+"q# c #FFD68F",
+"r# c #2FC400",
+"s# c #1CC300",
+"t# c #84CB32",
+"u# c #FFD68A",
+"v# c #FFD685",
+"w# c #FED67C",
+"x# c #FFD682",
+"y# c #F6D47B",
+"z# c #A6CE46",
+"A# c #1FC300",
+"B# c #10C200",
+"C# c #2BC500",
+"D# c #47C505",
+"E# c #AFCF4E",
+"F# c #FFD797",
+"G# c #33C501",
+"H# c #43C503",
+"I# c #40C504",
+"J# c #8FCD3A",
+"K# c #FFD897",
+"L# c #FFD586",
+"M# c #FCD57E",
+"N# c #53C714",
+"O# c #24C300",
+"P# c #4FC70B",
+"Q# c #DFD36F",
+"R# c #FFD791",
+"S# c #FFD581",
+"T# c #FDD679",
+"U# c #FFD58B",
+"V# c #83CB39",
+"W# c #42C504",
+"X# c #50C711",
+"Y# c #F5D57D",
+"Z# c #FFD790",
+"`# c #FED585",
+" $ c #FBD579",
+".$ c #BED055",
+"+$ c #23C300",
+"@$ c #5CC81E",
+"#$ c #EFD57A",
+"$$ c #FCD578",
+"%$ c #FCD47A",
+"&$ c #34C400",
+"*$ c #37C502",
+"=$ c #CED268",
+"-$ c #FFD795",
+";$ c #FFD584",
+">$ c #FED685",
+",$ c #FDD47C",
+"'$ c #FFD57E",
+")$ c #3FC504",
+"!$ c #26C300",
+"~$ c #62C81A",
+"{$ c #F2D57B",
+"]$ c #FFD683",
+"^$ c #F2D57E",
+"/$ c #FAD478",
+"($ c #F5D483",
+"_$ c #3CC602",
+":$ c #63C91D",
+"<$ c #0CC100",
+"[$ c #33C503",
+"}$ c #C0D058",
+"|$ c #FFD585",
+"1$ c #E4D36E",
+"2$ c #37C509",
+"3$ c #71CA29",
+"4$ c #11C200",
+"5$ c #7FCB2D",
+"6$ c #FFD787",
+"7$ c #FAD475",
+"8$ c #5BC820",
+"9$ c #34C501",
+"0$ c #DDD36C",
+"a$ c #FFD78F",
+"b$ c #FDD580",
+"c$ c #94CD3E",
+"d$ c #16C200",
+"e$ c #89CC3D",
+"f$ c #82CB37",
+"g$ c #FFD57B",
+"h$ c #ACCE4E",
+"i$ c #FFD794",
+"j$ c #FFD583",
+"k$ c #FBD67F",
+"l$ c #5BC720",
+"m$ c #08C100",
+"n$ c #6FC923",
+"o$ c #25C400",
+"p$ c #FED678",
+"q$ c #ACCF4F",
+"r$ c #FED583",
+"s$ c #FED684",
+"t$ c #FED485",
+"u$ c #EBD374",
+"v$ c #62C823",
+"w$ c #FFD68E",
+"x$ c #FFD484",
+"y$ c #FCD579",
+"z$ c #FED680",
+"A$ c #4FC610",
+"B$ c #FFD479",
+"C$ c #7ECB35",
+"D$ c #FFD690",
+"E$ c #FED586",
+"F$ c #FED57D",
+"G$ c #FBD47B",
+"H$ c #FCD585",
+"I$ c #A4CD44",
+"J$ c #48C604",
+"K$ c #24C400",
+"L$ c #37C504",
+"M$ c #FED47D",
+"N$ c #E8D481",
+"O$ c #4BC60F",
+"P$ c #C9D262",
+"Q$ c #FDD582",
+"R$ c #C1D059",
+"S$ c #FED57C",
+"T$ c #FDD589",
+"U$ c #D8D270",
+"V$ c #79C92B",
+"W$ c #F7D58E",
+"X$ c #FED581",
+"Y$ c #FED57F",
+"Z$ c #FED582",
+"`$ c #FED587",
+" % c #FDD587",
+".% c #FFD486",
+"+% c #FDD67C",
+"@% c #9ACD43",
+"#% c #39C506",
+"$% c #CFD165",
+"%% c #9FCD45",
+"&% c #19C200",
+"*% c #ECD47E",
+"=% c #FFD798",
+"-% c #FED47F",
+";% c #FED682",
+">% c #FED683",
+",% c #FED486",
+"'% c #68C827",
+")% c #1DC400",
+"!% c #2AC300",
+"~% c #50C715",
+"{% c #F4D57B",
+"]% c #FBD577",
+"^% c #86CB34",
+"/% c #80CB2F",
+"(% c #FBD686",
+"_% c #E0D26F",
+":% c #B2CF52",
+"<% c #FAD47D",
+"[% c #FFD899",
+"}% c #AACF4D",
+"|% c #3EC500",
+"1% c #CFD26A",
+"2% c #FCD57C",
+"3% c #E0D376",
+"4% c #4DC81A",
+"5% c #33C500",
+"6% c #43C611",
+"7% c #1EC400",
+"8% c #F6D587",
+"9% c #3EC60D",
+"0% c #50C714",
+"a% c #45C603",
+"b% c #8BCB37",
+"c% c #FFD587",
+"d% c #FAD587",
+"e% c #70CA26",
+"f% c #47C503",
+"g% c #B1CE4D",
+"h% c #5CC820",
+"i% c #FED47B",
+"j% c #52C606",
+"k% c #20C400",
+"l% c #36C504",
+"m% c #D5D26F",
+"n% c #40C502",
+"o% c #52C715",
+"p% c #C8D161",
+"q% c #3DC506",
+"r% c #CFD268",
+"s% c #FDD480",
+"t% c #F8D68D",
+"u% c #3FC608",
+"v% c #CBD166",
+"w% c #CAD263",
+"x% c #FFD898",
+"y% c #A8CE4D",
+"z% c #FFD896",
+"A% c #54C716",
+"B% c #FED57E",
+"C% c #F8D683",
+"D% c #FED484",
+"E% c #92CC38",
+"F% c #31C405",
+"G% c #CAD160",
+"H% c #25C300",
+"I% c #FCD57F",
+"J% c #0DC200",
+"K% c #FFD47D",
+"L% c #53C717",
+"M% c #FFD68C",
+"N% c #FFD79A",
+"O% c #B1CE4E",
+"P% c #60C819",
+"Q% c #97CD3D",
+"R% c #E8D473",
+"S% c #3AC504",
+"T% c #7DCB2F",
+"U% c #91CC3A",
+"V% c #FDD581",
+"W% c #FED689",
+"X% c #FED478",
+"Y% c #FED580",
+"Z% c #FDD67F",
+"`% c #FDD67B",
+" & c #EED578",
+".& c #3DC508",
+"+& c #FDD57D",
+"@& c #FFD78A",
+"#& c #87CC34",
+"$& c #FDD481",
+"%& c #74CA2B",
+"&& c #FDD585",
+"*& c #FFD481",
+"=& c #FED686",
+"-& c #2BC401",
+";& c #45C604",
+">& c #FDD583",
+",& c #FDD584",
+"'& c #FED47C",
+")& c #FDD57F",
+"!& c #FFD47C",
+"~& c #EED47A",
+"{& c #DDD36A",
+"]& c #9BCD3F",
+"^& c #41C502",
+"/& c #F6D57B",
+"(& c #49C613",
+"_& c #15C300",
+":& c #26C401",
+"<& c #BBD056",
+"[& c #1CC200",
+"}& c #14C300",
+"|& c #2CC401",
+"1& c #FDD484",
+"2& c #7FCB32",
+"3& c #A3C637",
+"4& c #FED480",
+"5& c #FFD681",
+"6& c #FED47E",
+"7& c #FCD47D",
+"8& c #D0D160",
+"9& c #E8D371",
+"0& c #45C60F",
+"a& c #ACCF4C",
+"b& c #13C200",
+"c& c #FDD478",
+"d& c #FCD580",
+"e& c #3DC503",
+"f& c #18C200",
+"g& c #89CC39",
+"h& c #FED681",
+"i& c #8DCB35",
+"j& c #63C91F",
+"k& c #FBD685",
+"l& c #4AC70E",
+"m& c #FDD57C",
+"n& c #3BC502",
+"o& c #FFD893",
+"p& c #44C503",
+"q& c #43C70D",
+"r& c #B8D052",
+"s& c #8BCB38",
+"t& c #ACCE4B",
+"u& c #EAD373",
+"v& c #FFD47F",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" . + @ # $ # ",
+" % & # . * % # = # ",
+" = = - ; = # = > $ ",
+" = % = , # % * = ; ' ) ",
+" = # * * * % * - # , , ! ",
+" ~ - # . # , , { = = % % - # - # * * - * { ] ",
+" # ^ , # # = % % ^ # , > / # = = # ; = # ( # = - % _ . % = - # # ; : # ; : - - # # < [ } ",
+" # # | . # , ; % ; = # : + : ; = = | * ; : = - # = * = - , = % * % = 1 # = 1 1 % 2 % 3 1 _ % # - , : ; : # = - # = # ; # = ",
+" # 4 * # = - | _ # # - - + # * * # - : = # # # ; - = : # # # - # - # # # - - * : = ; - - # = # , # # $ . # | = = . | - : : - # # * * + % = : - 5 ",
+" : # , # + # # = ; % - : % # * ; * - # - - # # % # : * * - % ; : # # : # # % # : 6 # ; * * = ; : # : - - % ^ # # = 1 , | # , - = - # # - - ; + * = - # # ",
+" : : : , # - # ; - # ; * : : * # # # # ( # # # - # - : - - % * * # # : + : : + # # # : # * = = # - # # | # # : - + # - , 7 8 = # # ; , % ; # # - # - # # # ! - # ",
+" / : : : ; # - # # # ; # = * # * * : + - # ; # ; : ; * 9 + # * ; : - * # - : : % - - # # - = = % % = # - 6 # # - # # # ; - ( % , , : : : : : : * : # - # # # # - * ( + ; ",
+" : : : 1 # = # : # # ; # - ; : ; : * ; % ; # # # - ; - ; ; : ; : # # # # # = : + # # - # - * : - 7 # * # # ^ % 0 # : ; ; ; : ; * % = ; % = = ; = 1 : : : : : : : # # - # * * : : % = = ^ % ",
+" % 2 1 a 7 - , + = # - * : : # - # # : - # # * * # - > # - - - ; ; # # : % ; ; : % = % + : : ^ 9 + # : ; : = - = = * # , : : : # # = * ; # # # , # | . b : : : : : : : % ; # = = 6 = / c < = , ",
+" 1 # # ! # > = % # # ; ; ; : : : : : % = : ; # * * : % # * ; : % % # # - # - # * * : ; : * = # : ; - - ; % # # # , # * * : = = * # ; % ; # * : # # - # - # # - # # * = # # # : : : : : : : : * : # = * # * > - d = ",
+" = # ! % # # ; ! = d % * - - # # - ; : - # : = : # - % * * % * ; e 6 - ; * * % = # * ; e ; = = % : # # - - ; ; ; f + : : : : # * ; # ; * * : # * - - # # * : # # - # : ; = - # # : : : : : : : : : # # # - # - # - 5 5 | ",
+" # ! # , , # : : # ; - = 0 - - ( # # # # # - # ; : : # * ; # # ; - : 6 ; : 6 - # : % * ; e # ; * - + ; - # # # : # - e , # : : - - # ; ; % , ; # # # # - = * ; # - # , ; ; - # # # ; = 1 } e : : : : : : : : ; % # : + # = = # ",
+" = 4 % = > # - # # - # 0 ; - : * : + ; ; # # - ; g # 9 * + + ; : : : % # % : 6 : - # # # # # - * * - - - # - : ; : * : : : # # # - - # ; - - # ; * : - ; ; : * ; = : : : # : % = + # # # , e $ # # % : : : : : : : : : - : * # # = d c # ",
+" + | = # , # - - , , # = = : # ; - # * = # # * * # * h % # ( # = # - - * : + % - # - - # # ; = * : * = # ; : # = * # - ; # # # ; # - # # * = * ; ; # ^ 9 + - - # , # # - ; # ; # # # # ; - # = : # # % ^ # : : : : : : : : : * ; ; # = | # { { > ",
+" . | # + - # * * - # $ = = : : : : * : : : # * * * = = % * : + * : - - # * : - # - # - # = = ; # + * # # ; = * # # * = # ; ; # ; % # - # - * ; : ; - - # - # ; : : ; % = # # : : - # # = - # # - ! = < = # . : : : : : : : : : : % * # # = # # { . ",
+" = . % # # ; ^ # : - % * * + ; - - ; # # - : ; - - ; + = % = % # * # ^ # : : * + + * : : # - ; + + # ; e # # = = ; # + * = # ; # ; ; * * % ; : : # # # # * # - - * : : - : : ; 6 ; # # # : = * * + * * # # # = | | i : : : : : : : - - ; % ; + + * ; - # 5 ",
+" . = ! . % % ; # # # * # * ; # - # # # ; # - ; ; # % # - - = # : : * : ; ; * # , # # = % = - # - : ; ; - , : # + - + # ; e # ; ; = : # - # ; - - - ; - : ; - ; * # = - - # : # ; - - ; : % - ; * . + * % # % = * # # j = : : : : : : : : - - ; ; - * # # = * : ",
+" = % * % % ; * : ; - # # ; # : ; : ; ; : : # : # ; ^ # # - - # # : - # # ; ; * # ; - # : ; * : - # - ; + * * + - - - , : + : # # # : : # # - # # - - ; ; 6 6 * ; # - - : # ( 6 # - = # # : - # - * * ; # ; # - : = # . = : : : : : : : : + * = = # , ; # : % = ; ",
+" . # = # - - = - # - + % - # : + : : : * : ; : : * : ; : * : ; # # # : # # ( # , # , , , 6 , # # ; # # - # - % % = # # ; - - 6 ; * 9 + : ; # = # - # ; # : - # ; # = % # # - ; - = # * * # = # # - % ; ; ^ + # # # 5 | = : : : : : : : : # = % ; # = ; * # = | # ",
+" @ # : = - - = * # % ; - - % * # - - # : : ; : - ; # # - , : ; = # 0 = # # ; - - - - # : : : - # # # - # ; ; 0 = : ; * * : - , = % ; # : # ; 0 = - - ; e # : e 6 6 ; - # # ; : - + % - ; # # # % = * = = = = * - [ 0 * | : : : : : : : : , ; = ; # ; : ; - # = = ",
+" = = | # # # ; : ; # = * : % % * * # - - # - # # # e 6 # - ; # # * * * - * : ; = * * ; : : - - # # - # ; : * ; % : - - ; - f : * ; = # # ( e # % * - - - ( ( : - : ; ; ; = % : : ; : - - * * ; # # * = 0 0 = = = = % # | . # : : : : : : : : : : ; : - - # - ! # ^ - ",
+" . # , : : # - ; * 6 ; + # # % 0 # # + * % # - ; * = : 6 # # * % : - # - * ; - # : # # - # # # # - # # : : , ; # # : - - ; : ; ^ ^ # - , 6 : - % * ; : : # # - : # ; : : # : ; : : - # - * , ; # % = 0 % = = % h = - % = ! = : : : : : : : : - # - ; ; = = # > k ^ 8 ",
+" = # % - ; # # - - - # # # - + h * ; ; # # * - # - - # ; # ; + % - + : : % # - - - # - - # # ; # ; ; # : : : - - % # - # # - # % # - - 6 : # # * ; : + # - - - : : # # # - # - # - # - # : : : # * = = 0 * # 0 % ; { 5 : : : : : : : : : # # - # ; = - % ! = % ",
+" = % # # # # , ; - - # = ; ; ; = % # * % # ; # # e : 6 # = * : : : : - - ; % + = # # # - # : ; : * + : - # ; : ; : : * : : ; : + ; # # , 6 6 , : % # : # # # # - # = 0 = . # : - : = * # # - # : : : ; + * * = * * ; - = : : : : : : : : : # % # = > = % = * # # 5 ",
+" = # * - - : : * * : - > # ; - - > # , - # : # # 6 e : ; # : - # - # - - ; : : : : : = # - # - - # ; # : + ; > # ; % - : # # ( # # ; - : : 6 ; ; # ( e ; : ; ; - - = = 0 = ; # * # % + ; * ; : - # * + * # # # - - # % 0 : : : : : : : : : * * = * = - = # { 0 . ",
+" = ; * = # : # # - - # # % # : * - % # ; - - : * * - # # - # # # ( ; : # # : - ; - ; # # ; - # - - # - * # - % % # : * * e 6 6 e : : : , # - # - # 6 : - ; = + # # # - + * * , : = - # # : 6 # # # - # # - ; # # # - - = % : : : : : : : : : # - # ; # # = = = { l . ",
+" ~ m , 7 = = + # # : - ; - : * : - - ; : ; : * * - - % : ; ; # # : e ( e ( , # - # ; # ; # + : * - % ; : : % - - = * : - - % - # # : 6 ( # e # : + # ; # ; - # ; # - = * # * # # - * # ; : % : : ; # ; : # # - # # - - 0 - # : : : : : : : : # - # : - * # = = e , n ",
+" = 1 = ; # = % : # # : ; : * * ^ 9 + # 6 e 6 - - % = # * - # - * : # , 6 # e ; ; + : : ; : ; # - # ; - - - - # - # : ^ 9 + # * : # * # - ; # # * # - : # # ; # # # - # % + # = - # : # ; - - * : - - : : ; : = = : 6 : - - * % : : : : : : : : : : : ; ; ; # - % - k - ",
+" % # * * # % % * # # = - - - % * # # # : : 6 ; * + * ; # - - * # # # ; 6 6 - ( - - - ; ; ; - ; ; # , ; - - # - ; : # # - # ; # # : : ; * ; ; ; : ; ; : : : - * * = = # - ; = * : - - * ; - - # # - # : ; - ; = * - # # # 5 5 = : : : : : : : : : : # - # - - # % , } o p ",
+" . : * # | - = = = # = : = > # * ; ; = * # # # 6 : - # - # # ; : : % # : - # 6 , # ; # : - - ; : * : : : # # : : e e : - , # ; e # : e 6 ( 6 ; # - - ; - ; # : % # : = , 6 # # ; 6 , # * * * : # # # * * : * - : # # # = = # : : : : : : : ; # ; - # - # - ; # # # q ",
+" = = = | # * = % % . - # = % % # : * * ; # # # # 6 e ; ( ; : # - - - * : - = : e : * # - - # # - # # ; # # # - ; - - ; e 6 # e ( ( : - : ; ; # 6 ( ; : ; # - ( 6 - - # e 6 e ( 6 # - e ; % - # - # # % = - - # ; # : # % - c # : : : : : - # , # ; # - # # % * = 0 o # ",
+" . | = 8 - # # # % % % % * # 0 = : - - % * : 6 : e - ; e ( e ( , - : ; ^ 9 + ; # - # e ( : # , ; : ; # ; # , ; - # ; # ; 6 e ( 6 # - : # ; : * : ; ; 6 = # # - ; # - # - - e - e - # , # # > # - ; : ; ; % ; : - # # - % = { ; - : : : : : # - : ; = ; : - = # ; c % r ",
+" % . # % - # + * # # * = 0 = * * 9 + # * ; ( ( 6 e : - , 6 # e ; - : : - # # # * = # # # 6 , ; 6 e 6 - * : : : ; * = + - e - e - # # : # ; - ; # ; ; : = * # ; ; ; - % + : 6 ( ; # # # : : % # % - - ; ; % # - - - e - # = | - # : : : : = + * # , # # % . 0 : : : s = ",
+" t = # # # : # ; # # + * = % ; # # # # ; # - - - 6 ; # ( ( 6 6 - ( e # ; - - * 9 ; = = # # : , , : : 6 * % # # ( - = + # : 6 ( ; # # # : : # ; : ; # - # : # # # # # : ; : ; , e - * 9 ; - ; % * # - - - - # ; ; - # - : - # = # : : : : # # % # ; ; # - = : : : : ",
+" . = | . = % # 8 : # # # : : # ; : # # , # : % = # % + - * - # = - ; - # 6 , : e , ( : > # # = # # - ; - : : - # # ; - - , ; * ; ; ; , e - * 9 ; - # : : ; = * = , 6 , # # # - # # - ; # , : % # : - % : : : # # = % ; # # : - # ; = = = = : : : : : % = ; # # : - [ : : : : ",
+" = & = 1 . # , # = * ; ; # - , 6 e 6 6 e : ; : ; 6 : : : # ; : * + = = : e : ; : - # ; # # = = * * ; 6 - - - , # # : * : e 6 ( 6 ; # - , : % # : # ( : : - # ; ; e - ; ; - # ; ; # # ; # ( ; # ( e # ( - # - , ; = ; , ; # # # # # d + # = : : : : * # # + < | % e : : : . u ",
+" = e ! * % % # = : # # ; e : 6 - - # : - * # ; # - # - # - # : ; ; # ; # - e 6 = : # 6 ; # = = * ; # 6 ( ; : # e # - : - : ; ; # 6 ( ; : ; # ( e : ; % # : * ; - = # - 6 # - # : * = - - ; , 6 : - # - - # ; : : * = # * + * # : # # # % : : : : : % * * ; $ { : : : : : = ; # $ # , ",
+" % = # = % * # ; * # # # : 6 e : : * + * : # # : # - # ; : - # - # # # # # # : 6 ; # # - - - % ; * ; # ; ; 6 e ( 6 # - : # ; : : : ; ; 6 e 6 6 : - # ; ; # # # : * * # e ( ; # # = * = # : : 6 : # - + # ; - # ( - = + * * * % # - * # - - , ; # = = % ; : : : : : : = = _ . | $ : * - # ",
+" = = # # . # # e e # ; # , e - # # , : * % ; ; + * : : % # - - : - # * : ; ; - - ; - ; # # - # - - # - # * : ; : 6 - # # : # # # - - : ; : : 6 ; # # - ; : ; ; # % - * - , 6 - 6 * = * # # 6 ( e e 6 6 ; ; e # 6 , ; : # = # , # * * , # - = , : = ; : # : : : : : : : = % % = = # * ; # # # # - [ ",
+" # = ; = | : # # e } e ; : 6 - , # # ; # # ; - - ; : - = % - ; ; # - # # - - # # * # ; # * * - ; : , # # ; * ; * : # # # - : : * ; - - e 6 - # # 6 , # - * # # % * + * = ( ( 6 : * : = - # # - ; , : e e - = ; ; , , , 6 ; = # - - ; + * : : . % : = : : : : : | - # % : ; + + # # : = # ^ v , { ",
+" - - % % = ; - + + , w # : , : - # * ; ; # % # - # # # = ; - - # - # - # - # # # - + % # * ; - # - ; : : ; : # # # - # % * # # - = * : : 6 ; ; # 6 ; ; - + ; ; = - # = % : ; - - % = - # - ; * ; ; e : # - > # ; - - : # # # # - # - # # - % , . : : : : : = : : # - - ; # * ; : : ^ - , ; # # ] # ",
+" = - # , | # % - - - ; 9 # : , 6 ( # - # # 6 - # - * # ; : ; = - # : % ; # ; * + * + * * % * ; # - # : # % ; : ; # + * * : ; 6 % ; * # # # # 6 - : , : ( ; # # : ; ; ; = ; : = = # : = + - - 6 ( 6 ; : : # - % # : * * - * = - * # # - # - = # = = # # # # ; * : # # ; # # - - * # # ; - , ^ ",
+" = # # ; | , # # , # ; * = - - ; , 6 , , : , : - % + ; * ; : ; ; : : * : ; : ; * % # # ; # # ; # - # e ( : = # : ; e # - # : : * : # : : # # ( 6 6 ( e ( e : # - # # : * : = # : # = - * : # ; ; # ; = : : # * : - - % * ; ; : * ; : : # % % , = $ $ - # # # - - ; # # = * + * * ; : * * $ $ # # ",
+" = - | # : # # * * ; ; - * + + # # ; : : e # e - ; * ; : 6 - # - - - # # # - # # # ; # # ; # # : # - + # ( , ; - % # 6 ( # - % # - # 6 - ; - - - 6 6 : e # ; # - ; : - # : - ; : * : # * - - - ; - = = + - # # ^ 9 + # * ; ; % - # - - ; - # - % , e ; # > * : # 6 # # - # * * # # - # ; * - # * + ; - # , $ ",
+" # = # : = - # * ; % + * - - , ; ; # - - e - 6 e 6 ( ; : - ; # # - - ; + ; - - # # * + * = * ; # : # # + : 6 e ( , # 6 : e # # - # : , : - ; ; - ; ( 6 e # - - # # - - # # ; ; - ; # ; : : - - ; % # # # % # * - - - ; # # - # % # - - ; : % ; # ; ! ; % : / = . % # - : ; # - # * ; % # % , : % # # - # # # = = # - , ",
+" | * * 9 - | % = # * : # # # ; - : ( , ( 6 # ; = + ; 6 # # : # - # # - - - - + + + : : - - # # ; ; : : # - # 6 e 6 6 ( 6 e # # : ; # e - ; # , e - - 6 ; : % ; # # # # # ; # ; - : # ; # % + : # * * 9 % * * * : , # ; : = - # * : - : # - * : ; # - = = ; $ ; x % # % = - # # 6 ( ; : ; % * ; * * - - ; # , ; - : ; - - , = # | ",
+" - ; - % # - % * * : * * = , 6 # : e : 6 - * = ; * = * - - # - - # # - - ; : ; : # # # # : ; # - - # # # - # 6 , ( : # ( # # * # e - 6 e : # ; : * * ; ; : * : ; * % % # # ; : : : # e ( : = = * - % # * ; e ; # ; ; - * + * * # 9 + # ; - # - # % - - > # * # - # 7 | # - % # = * : ; ; 6 e 6 # # # # # : # # - - : - # - # # # , # . 7 ",
+" * = + d + = # # = - - % # e - - 7 ( 6 6 # # # # ; % % # # # : # # ; : : ; ; : - - - ; : : : = # - # ; # : : ; - # , 6 - - ; # ( 6 # # : : % ; # - - # = # # - # - ^ % 0 # 6 - # : : : ( , ; ; # ; # ; , ( 6 # ; # # , # # ; + % # % # % * * ; # - * = # # # - % # | ( { # . u # = * - ; - ; = ; # ; ; : : 6 - % # # , : * 9 : # # - # ; * + # - # % | = ",
+" 8 # = ; # % % % = = # # ; # % * - # % = * * - ; : - ; # % = # | # - # # - ; - # # # ; - # # : : : ; ; , : 6 : - # : ; = - ; # - ; ; ; : * 6 ( ; : + * * ; - - ; # = = # 6 ; # - - e - 6 e 6 : # : e : 6 - % % * * ; ; : ; ; * : ; * : # - ; # - # # * # - # ; # # * - - $ : % & = v # = * + # * = : ; ; % # ; # # - * - # : # - * * ; : + : ; * % + d # = # # ",
+" % # ^ : % + # # * * - = * % * * * % # # * * ; * # # # # , , - # # # # # * * # - # - ( - ; # ; - # e 6 - # * # ; : 6 ; : ; # - 6 # + : ; ; 6 e e # # ; : : # * = # ; : : ( , ( 6 # # : : ( 6 # ( 6 # : + % - # - - - ; ; - # - # ; : * : # - ; # # , - # # # - = = ^ + ; # > % # # > # # + * = = # - - # # - * ; ; * ; # ; ; # # # # # - - # # ; : * + # ^ # , $ ",
+" ^ % = - - : * % = * ; % * # % * : # # # ; # # ; ; - - , : ; # # # # : - # - + * : # - - 6 , , : , : : ; : = : : ; - # ^ e e ; # - - ; : : 6 # - - - ; # * = , 6 # : e : 6 - ; ; ; # ; # - # # # - # - # - ; ; : - - # - # - ; - ; : : # - # # # - # - ; ; | # # $ ! ^ # # + + % # # # = % ; ; # # % # % * - # # ; # , # ; # # # # - 6 ; * = * # : # # # + # ! $ ",
+" ; , # % ; % - - - ; # - - % * * : ; # # % : - # ; * * % # - # * * - # ; : : ; * = - # = : : e # e - , # * * # ; : * ; ; # # : 6 ; - - # 6 , # # - - ; - . # e - 6 # ( 6 6 # - 6 # ; : # - ; : ; : % ; # ; # ; # - # ; : + = # ; % - # ; : ; ; # * = % ; - - # - - # * * - ; % * * # ; = ; ; = : * * * : 6 - # 6 # # ; # # * * : ; # ( - = + # # ^ * # # ^ % # # ",
+" : : - ^ ; # % | : # + # * ; : * # * * % # e e 6 - % # # # , # # - # : ; ; # - ; : # : - - e - 6 e ( - * : # ; - ; # # * - # : % ; # # - # - - # # # = = # : # ; - # # : ; e ( 6 # : : # ; # : : * : * * : : # # : : ; : : : : # # # - ; : , # % : : # # - # # # # # # : * + - # # # # ; # # - # # # : - ; : : # - : # # * ; ; * ; , ; : ; ; # # : ; : e - , # y z ",
+" % : : ! _ e % = - + * ! # = = + # # * ; f % # : 6 ; + : ; ; ; # # # # - - : # # # # - # , ( 6 # # - ; : : * : - * ; # # * : ; # 6 : ( , : - # # - # # ; : , - # # - # * + - ( # : - : # - - # # - # # = - - # : - - : - # - ; # % # # : # # - - # - ; ; # , : ; + # # - - ; ; # * * + * : : ; : # ^ # # - - - ; + : - # % * ; # # : , , 6 ; + # # - # # # 6 ; # = 8 8 ",
+" # * - > * % # % % - + * * * : # % * ; # % # # : # - - ; ; ; * * * # - - # - ; ^ # # ; : 6 - ; # # ; # - - # : * # ; % * ; 6 - # : e : # # - # - ; # ; + * - # ; : ; ; ; ; - - ; - # - # # % # * * - # - - # - + + ; : + + * # * * * - # - - - # - # , ; # : ; # - # # # ; ; # * ; # - - # - # - # - # # - - ; * - - # # e 6 - - ; - : ; # # - ; - - # ; : : - % # = ",
+" - # # % % ; # - : ^ $ # % # # * d % = * - # ; * # # : ; # # - - - # # ; - # : + # # # : ; - 6 6 e - 6 # # ; * # ( - # - - # # : 6 # - # ( 6 ; - # - - = : : - ; # ; : ; : , : - ; ; - ; 6 - # * * # * ; # # - # - # - - - # # = : ; # # # ; # - # # ; : # # * = ; = : ; # - # - - % * ; % ; % % = # # , - # - # # - % ; # = , : 6 ; ; - # : ; % # # - # # , 6 # + # - # = ",
+" | # # # + % ^ ; # # * : ^ % + # # % # # * * = # + ; = * % = # # # : * % # - - ; - # - # : : 6 6 ( ; # ( e : ; # # ; - # e # # - - # # # # # % # ; # # # # - * : : - # e - ; # , e - ( e - # # % * ; = * # # - # - # - # - # ; - - - # ; # : ; : - - + * = = # : - ; # - = # # % # : * : = = ; 6 : : ; # = # # : * : e # e - ; # , e * # , * : : * # # e - - # + * * # ",
+" % * # = = * # ; # # ; ; # # ^ # f + * # * % ; ; # # * * % = # - # % > # ; * # # : | - # - e # - 6 , 6 : - # ; # , ; # # # - = + # ; ; - * * * * : ; + - - # ; ; # ; - 6 e 6 ( 6 ; : ; # # # # # ; ; % + ; * * + + : + : + : : ; ; ; # : # # - # # - # = % ; # # # : # = # # * * * # # # = % - - # - # : * * * # - - e - 6 e 6 ( 6 ; * # ; # # e : # # - # - # e # # # = ",
+" ! % * - - # ; : : * - : + % 6 % # % + = - : = # % # # ; - + * * % - : % # : ; # - : = + * - = # . # 6 : # # - ; : : : ; # = = : - # # ; # - # # # : # - # # # - - # # # # : : ( ; 6 e 6 - - * - - - - # # : 6 - # # # # ; # - ; # - - # - * # # # ; ; ; = ; # ^ # ; : : : : - # # # % # # % % ; # # ; : # # # : ( , ( 6 # # : : ( 6 6 ; # - # # # : ; * * * * : + # # # - * | ",
+" ! # # # : # 9 ; # = = - # % ! - ( # ^ % = ; ; - # = = : - - # + * ; * : - , : % = # # - - = = # ; 6 , 6 # - # - - # ; # , ; - : ; ; # ; , # # ; ; # # - ; : - # - - # - # ; ; ; : : 6 ; ; * ; # - # - # # : : : ; : ; - : ; = + # # - # # * : % ; # ; # - # # : : ; - - # : + ; : = * : : : : ; ; 6 # # - # : e : 6 - ; ; ; # ; ; # 6 ( ; : ; ( e : 6 # = = # - # # = g = = ",
+" # 6 - # ; # # # # # # ; # # # e ; # * + e # ^ # . = - ; * ; % % ; e # # ^ 9 + ; ; - - # - : + * = , 6 - : ; ; ; ; - - ; - : : : ; # # = * : : * = - ; # # # ; % : + # # - # - # # - # - # : # # ; # - + = # ; % # # - # ; : # # ( # + * : : - # : * : * * - : + # : : - # # * # - - # # : - # # # ; - + : ; ; : # ( 6 6 # - 6 # : + : : ; ; 6 e 6 : = * : ; , # * * + = * g . 0 = ",
+" % : # # * # # = # # # ; # : = # - # ; ; h = A % * * * # ; = # : # : # # - # % # : * * - = # # # # e # - # # - # : ; ; - ; 6 - - # % # % : - # % ; # # * * ; - # # - # ; : % ; # # = * # # * # # : # - : : : : # ; # - , 6 e 6 6 e : e # ; # : + + * # # # : : * # # - # # = * % - - ; ; - = = ; # , ; - - ; ; : - - - - # e ( ; # - - ; # : # : 6 = = # * * - - ; + * * ; B 1 = = ",
+" | + * * = * = % - # - = # # + ; # - # : = 0 % - % , ; * * = # - # * = ; * # # * : - - % # - + ; # # * + = # + * + - # , e - ( e : * * * : ; ; - # : # ; ; ; # # % # : % : : * : # - % + ; : ; : ; : : = - - ; # - - e : 6 - - # # : 6 ( # # : # # = % % # - # # - # - ; # - = % ; + + * = * % ; = : : : ; # - - ; ; ; # # ( e ; 6 # - - ; * # # : ; % # * : # - # - # # * ; # % > % ",
+" # , # , # # ; # # + ; + ; ; : : # # % # : * = % # = s # # % # + - % # - # * - % ; ^ 9 + # * * * = # * * = - - = ; # - # e 6 ; : ; - # # # - - - # 6 # : : ; # ; # * * * : * - # # # - # # : : - # # # - - : * # ; # * * % # - # # # * # - ; ; * # # = - # ; : * * + * ; # ; # # - % ; ; * = = # ; # # - # ; ; # + ; ; # # % # - e ; * * # # * ; # # ; % # - - # # # # = - # : # # # ^ 6 ",
+" - 6 ( # # = ; % # # : # # ^ + # - * * : - - * = - # - $ # = > % * - : = - ; - * - - # # ; = % = = - * ; ; - , # , e , e ( 6 e 6 e - # # - - # : # - - # - = = : : # # - - - # # | # - # * # - # # - # # - # # : : ; - % * # ; ; # : : ; * ; # * # - # : ; - - ; + - - # # # * : : - , ; - % ; : ; # # ; : : : # # # # * * * * e : # # + # * * ; # - ; : - - # - # ; * * : - - * # % # - $ ",
+" # 6 6 - # % = # # # % * # # : = # = # ^ 9 + # * = # # # # # # % % # + - # = ; # % = + % # , # : = = * ; : - # + # - : 9 + # - # - ; : : : * = - - % # # - # = # - ; : : # - : # # # - # # # + * * ; : : : - : : ; ; ; : = - ; * = # e 6 ( 6 ; # - + : - # - # - # # # * * ; * = - - : : ; = - - # - - # - # - - ; # # : # # = ; , e * # : # # : # - - - + # * # * : 6 ; : ; ; ; * # - # ( = ",
+" # 6 ( # # # # # # # # 9 # + ; # * # # ; # : = - - ; % # # - ; # # # > % ; | # # # % = - - - ; $ # : = * ; : # + : : # # + # # : , , # : ; ; # # ; ; ; ; # - - - ; - - - - ; ; * ; # = * # - - # # : - # - # , e ; : * ; e : ; ; : : % + : ; ; # 6 ( ; : ; ; - # ; - - # # * ; # - ; ; * # - # e - + - # ; # - - - # , ; * # , : # # % * : # 6 : * : * : : : - # # + * : - # - # # ; # ; : ; = = ",
+" - 6 ( - # % ; ; # # # # # ^ % % ; - # = = ; * : # # # # % # # ; # % : # f = ; ; + = = % - = # # # = = * ; > # f + ; : = = = # ; - - , ; # # % # # ; # # - % # : # # # - - ; # # ; # - # ; ; : : - # # # # # - ; ; # # # 6 ; - - - - # # : ; * : ; ; 6 e - # ; * = * : ; % * ; ; # - ; % * # : 6 * - - ; ; * * : * # ; # * # ; - ; * * = - # 6 - ; : ; - # - : ; * # - - - - ( # # % # ; ; ; . ; # ",
+" # , , - # # - : # # # # # ; * 9 # # % - ; # % * ; # # # g + # # # - : # # $ g % ^ * = % = - # = = = # * : % # : * * = % | ; # : * * - # # - * * : : # - = - = # # # - # # - # # : # - - # # # - - # ; * + * * + = - # # 6 e , # % = % # - , * - # - # : # ( - = + # # # % # - # # # ; # # # # # ; ; : ; ; ; : # - * : * ; - : - , # # - * - # ( : : : % # - - ; # ; # # : * : + * = # ; # # # # # ",
+" ! # # , : # ; # # , # - ; ; % # # # * - # # * = # # - - - # ; * - # # # # = + . [ # # ; % + : # - - # % ^ 9 * : - - % * * # # * * - % * - - * ; # : # - - ; : = # # - - # % ; ; # - - : : : : - # # : : ; * % # # * * + # # - : + ; = ; # - ; ; : ; # 6 e 6 6 e : * * = * * * # # ; : : ; # # - # - , # - - - # - - ; , - : * * * ; # , : # ; : : * # - * * - # - # ; # ; # # - # = = , # - ; : : # ",
+" - : * # 6 , # ; # # # % # # % % # * # 0 # - # - = , 6 # # - # : * - - # - $ ; = * # % + # % ; # # = + # - # ^ 9 + # * ; : - # * ; # * ; - = % # , e # # e : : ; * ; ^ # ; * : # # # : : # ; # : + ; # - * = - # - ; + * * * * # - * : : + # # 6 e 6 6 - - # # : # * ; # # # = # : : ; : - # ; - - ( # # # - - - # # - # # - - * = # # ; - # % - # : # - % ; # # = % % # # + ; ; ; = # # # # - # # # ",
+" - 6 # # # # # # # # : : # , - - # 0 = * * + ; # # e - # ; : % ; # # : e 6 ; # - , } # # $ = - g ; = - f : - - - # # : * * : - > # ; = % # > # ; - - , , ; , ( , e : ; ; : : # # ; # ( , : * - # # - # # : ; # ; : # - # # * ; - # = - - # ; # ; : : 6 : - : # * # * * ; # + * * : : : - : * # # * * : + + * * ; # # , - # ; - - # # # # : = * * ; : : : # % % ; 9 e + % : # # - # # ; # * * % + : * + ; ",
+" - - : , : ; : # # , # - - * % # # # > % % # , # * # ; - - - : * : # - - # : : ( , w ^ # # : : e + g % - - = - * - # - - # - % * * % # : * - = % # : * * - # $ # - # = * ; # , ; ; ; * - : # - # # % # - e ( 6 e ; # - # # * % * ; # - # ; ( 6 = # # - # # ; ; # : : ; * ; : ; # # - # # # ; e : # # # # * ; * % # # : * 6 ; * = * : - # - # * * + # , # - - # + % - % # * = - # ; : ; ; = # * ; ; : 6 + * = + ",
+" , 6 # # , e e ; ; - - - # ; # # ; , # - # # - - # # ; - # - # # # # # : - # : e : ; # : , - % , - $ * - ; % + - % + * = + : ; : * : - - - > # ; - - % * * + : : ; ; * - # # ; , ; # , : - | # * ; * ; ; : e ; : - # = ; + ; * * : # # : 6 - * : : * # # ; e # : e 6 ( 6 - # % - - # ; ; ; , ; ; : : * : # - # # # ; ; ( - = + # # ; ; - - ; + * # ; # # - # # ; # # = = ; # : : ; : # : % * ; # - - # # = # ",
+" # 6 e ; = # - 6 # # - # # $ * - - - - % * % ; - - - # - # - # # # ; 6 - # # : # ( 6 6 # : : | # $ + * $ e # # - ! - : # % % ; - ^ 9 + # # % # : * + : ; : - * - , B e ; # ; ; : : ; # # : = # # 6 ( 6 ; # - - # ; = * # ; : - # ; + * * - > # # e 6 # e ( ( : - : ; : ; # : * + + - - = * : : ; - # # # = * ; ; % * ; , ; : ; ; # ; # - # - # ; # # - % * % % % ; ; ; ; # # : : - # # : # ; - - ; * + + * * v ",
+" = , : | # # # - : ; - # # # : : ; 9 + # * : * : - # - # # : - # # : e e : - , ; - # # : : - , : * # . # , , 1 # = = - = ; ; = ; + - # # % * * : - - % * = : # % = * ; # , , ; # , - # * * # ; * ; ; ; # 6 ( ; : # ; ; : ; # : # , : # 6 # : % # : 6 e ( 6 # - : # # # ; - # # # - - # - - # = - ; # ; ; = - - # ; # - : , , 6 ; + + = * # # - = - # # - * e + % % ; # + * = - # - # # # : : - * # - ; # * # : - ",
+" , - = = # : - - : e : ; ; # ; # # # # % - # - : - # # % # # - # - ; e ; 6 # - # : + * + : * ; ; - * * ; $ # , $ % = + * $ ; # : - - - e # ^ 9 + # * ; % # * * ; # - * : e - - , , # : - , e : ; : * : ; ; 6 e ( 6 6 e 6 # * - - : , = * * * : - e - e - # # : # # # - # # # - # # - # # - ; * * + - = ; # ; - ; - - ; - : # - - # % + ; * # # ; = % + # # : * - - ; e # # , e - * 9 ; - # = - ; - # % * % - # ",
+" # ; # # ; - # * = % ; 6 # # # # # # # % # : # * ; * : : * * % ; # # - 6 ( ; ; = * ; - # - # - > # ; - ; : # # - # ; $ - ; = - % = + * * * * - # % # : ; * # - % # # , - # + # : * * - 6 6 B e # = * ; - ; ; # : : : : ( : 6 6 e ( 6 6 6 ( ; # : ; , - 6 ( ; # # # : ; * + % # - # - ( : : % ; : - # # # # - - * : : - ; ; - ; 6 - - ; ; - # : - # - # : ; : # # % # - # # : - + # , : % # : # ( ; * = * ; % % = = # - ",
+" # , : # # ; # - * * # * : - - # # : + : * : - : # - - # e # # * : # - ; , : # e # ; , - 6 # + * % # : * * - > # * * % # ; ( # % 9 % + # = # % # - * : - - % = * # - > # ; % # # - - % * : : ; , ; ; ; : * ; = * ; # ; ; - 6 - # , : # 6 6 , , # ( , 6 ; , e - * 9 ; - - # - - - - # # - ; : * : - - # - # # % * + - # ; # , e - ( e ; : * - - # ^ + - - # # : * : * : : > # 6 # # + # ; # ( e : ; - = + # # - - # # - # ",
+" , ( : # # # ; # # # # % # # = * : ; - - ^ 9 - # # % : % ; = = = # - - : - , e e ( , 6 ( ; 6 - # * : - - % * % # : * # * # : ; # + + # % f + ; # # ^ 9 + # = = : = - % # : * * - + # * ; : - * - , B , # # ; # - # ; ; , - 6 # - ( ; e e , , : , : - ; ; # , : % # : # - # - # : # # # : # # ; # - + : ; # ; : # - : ; 6 ( 6 ; : ; - # # - # - # # : * : + : : : ; # ; ; % # : = ; e , , 6 : # : ; # % + - # % % = % + % * ",
+" - ( , # - - , , # * - - - # + # : ; : # ; * # - # * : : * : # # ; # # | : - ; ; # : - ; # 6 - - > ^ 9 + % # ; % # : * + : % = * + # # = - ^ 9 % # % - - # # : = = = > # * : - - % * # # # = : # % = * ; # , , ; # - ; , 6 6 ( ; 6 6 6 6 ; ; : # : 6 ( - # # : ( ; # ( e : : % ; : : * = : : * = - : ; - - ; : : ; : * * - : ( 6 e 6 e - # # = ; : ; ; - # * # : 6 e 6 - # = * : - > # ; - - , , # - ; : ; : ; * + * : ; : ( ",
+" # - - - - # , ; : - : : - - # # # ; # # - - = * ; ; - # # # - # - - # = - e : : # ; - # # : - # * - # # * : - * : - - % * * * - , ( % ; . . # # ; * + + * = # = % % % # ^ ; - # * + f + ; + = * * ; # - * : e , , e # # : ; # 6 : : # 6 e ; : # 6 : - - # ( ; : , 6 : - % : * : ; # # ; ; # # # : - - # # - - : # : = # ; : ; # - # - ; : : * : : - : : * : # , : : 6 # - : ^ 9 + ; # : * * - # $ - # # # # : ; * % # # ; # ",
+" - # # # ; # ; , # : # # : - ; : - % % * * - : - # # - - # # - ; ; # - + 0 # , ( , e , 6 , , 6 * : - # : # % 9 + ^ 9 + # * ; : : * # $ ! # ; ] + ; = % = = % # % % % * : - : * * - - e ( , - * * + : - # + # - - # $ , e e ( # ( : : # 6 , : : # 6 ; # * * # ( e 6 : # # # # # - # ^ # - - ; - # . # - # # - - * # * # # ; = + # - # # # : : * ; - - # - # # : : : ; # = = # - # = ; : - - % * * + ; , # - # # % # ; # - : : ",
+" # ; # # # - ; , 6 # # - - # - - # : ; : ; # : : # # - = % # - # # - # ; % # # - : : : ; : : = - 9 + # : > # # # # # % : = = : - - % * # # = = ^ C ^ = # ^ % + % * # ^ * : - - % * # e e ; : * = # # + : : # % * * + ; ; , 6 , 6 , : , - : , : , - , 6 : - # : : 6 6 , # # % # - # ; ; ; : : * * = # : ; ; * : : ; - # - * ; # - - # # - - # # ; # - ; : # : # # , ; # : : : * # # - 9 + # * ; : - * - # . - * # - ; = , - # | ",
+" # # ; , | - ( ( , e # - # # # - - - # # : # # - - + # # : ; + ; 6 : : ; = # - ; ; # - # # - - % * = - # = % # - - ; # - % # + 9 + # * ; # * = * = # 2 D + g + # : ; # ^ 9 + # * ; # - : , * * # ; # # + * # * ; : - e ( : : : : , 6 ( ; : , 6 ( ; 6 : # # # # # : 6 ; ; : ; * * % # - - - ; % # # - ; : - # - - - ; : : % ; # - ; % : : * # # : # - # - # , - # # * = # ( e : # ; - - # # = = : # % = * = # = % + - - ; * # % ",
+" # # - # | - 6 : - ; : - # 9 * * ; % # # : - - ; : - - - # # - - - # - # ; # # - = # # = = % - - # 9 + = % * : - , , : + # - = # % % % % - ^ 9 + # = ^ # % # 9 9 % % - * # # # = : # # # % * % # # * : - * - # * * - # # 6 - - # = ; , 6 ( - ; , ( 6 6 , # - - - - , : 6 e 6 - - - ; # - - ; * = % + - # ; # . . # # - ; - * * - ; : # e - - # : : # ; * + % ; # * = * + : - ; * = * : * * = # * * * ; # - * = ; : ; - # - # - - ",
+" # : : # - # # 6 # - # * : - : 6 - ; # # - # # # # ; % % # # # - - - # ; - : # # - # # # ; % - # ; : # # # - ^ 9 + # ; - - # % * - # = % # - > # # ; # = * , + - # B = % * # # % - * : = : : # # # # ^ 9 + ; ; # * ; - % = ; # # - # : : - 6 : # ; : : 6 ; ; - - - # # ; ( : 6 ; * - * = = # ; * : ; ; % # : * = # ; * * : : # # # - # # : * - # - # : # + * * : # = ; : : # ( - = + # # * ; ; - # = + : - # + # # = = - # - % - 6 ",
+" > - , ^ # , # : ; # - : : = : - # - - = * ; + - : * * * * * : - # # - # # - - + : : ; # * # - ; e , , : , - * : - : # - , : = # = % = # - # # ; # : * - > # ; - = # - # % % - + * : # * > # ; - % # # - # ; ; , % * : * * ; # # + ; ; # . . # 6 ; # - - e - - # * : ; : , # # # ; : * = = ; 6 - - # : + * : - - = , 6 # - - ; * % * % # % # * v * : : : - # # = ; # . . # 6 ; , ; : # ( % * ; : * * - # # + : : # # : + # % * ; % % = ",
+" | = - , ; # # # # # # - - : * # : + - = . - # : - # # ; # # : - # # # - # - ; - * : # + ; : ; * ; ; : - # # : ^ 9 + = * * ; ; # # # + * * # * * ; : - - ; % # : * * = ; , > # + ^ # ; : # # : * * : - % = # ; - = % - - ; - - # : + # * = # ; : : ( , ( 6 # # : # # # 6 : , # # e - = = # : # # ; * # # ^ 9 + > # e - , - - : ; : # # % = = 0 c = # * # # , - # # * = # ; : - - , # - # # # # - - % * # $ f + ; + * # - # - # # + % - ",
+" : % # , e : ; # # # = - - - % : # - # 0 % ; - # : + # : ^ # : - # # - - = - - * : + ; - : e 6 ( 6 ; # - - - ; - # # # + * : # * * = # # # # - - # 9 % # : * : - - % * % # # # # f , g % * - * - - ^ 9 + + * * : * * - % # : * * - , # # * = , 6 # : e : 6 - ; : ; : : # 6 , 6 : : 6 ( ; * : : * : - * # % ; * = % # : e 6 # # - e , % # + = . > = # = + % * - - # + = = , 6 # * * - # ; ; # - * + # * = : e # ; - * % # : - # # - - - % # ",
+" # ; # # ; e # # - - # # # ; # - # # = = 5 # # - * # = # : = ; - + : ; # ; * : ; ; - ; # # - : ; ; # 6 ( ; : : - ; ; - - - ^ 9 # * ; # # ; % # % * # # * : - ^ 9 + # * ; % # * { # $ 7 % = , * - * # - # ; e # # - - % * * : - - % ; - > # ; ; e - 6 # ( 6 6 # - # : , : , - : ; , : , e - % ; - - # : : ; ; - * + * : - # ( # # ; : # = % ^ > | E # = = # * : + + * : e - - - % * - # # - # # - # - = - ; # # * - * : - - # - * # - # * ",
+" | - * # # # ; # # - # # # # * = : * * = = * # - # # - # - # # # * - - ; ; ; ; # - - - # * * # ; : * : ; ; 6 e - ; # , e - - ; : % * ; # = = * * * ; * - ^ 9 + - # - = - = : - ; $ # + , ^ # % # + % + : ! * : # # + % * ; ^ 9 + % # : * > # ; - : * ; - : : - # ; ( : , 6 ( ; - # e 6 # , : % - : # ( 6 : : ; * - - # 9 + 6 , + * ; : # = k F = # ^ # # = % % - = % # # # * ; - # - - # ; - ; # - - - # # + # ^ 9 + ; : : % # ; * - - ",
+" # # - - , # # # # ; # # # # * = # - # # = = # ; : % ; = % + + + * ; # # - - # - = # # # # * # % ; - ; # ; ; : 6 e 6 ( 6 ; : ; : ; # - ( - + + : - > # ; - * : - - % * = - , % = * # e $ + $ ! # # # ^ % + - % + + # : * ; : - # % * : - - % # : * * * ; : - # # : + ; ( - ; , 6 , , : , 6 ( ; # ( e : ; # e - # # - # - # # : , e # - = : { % % % s % = % * # % # # # - - % ; - # = # ; - ; - # # # - # * = - # - - - ; % ; # % # - ",
+" # : # # - x - ( e # # ; # # = % ; ; - - % ; : : : * # : ; + # = # - # * # # - # # - # * % + # # : ; : ; = # # # : : ( 6 e 6 e # # # - # ( ^ > # ; % # : - > 9 + # * ; # ; - - - ; # ; - = ! # 2 D ^ ^ 9 = % * , * * - - # = ; - # ^ 9 + # * : - - % # # # # - # # # - 6 : # ; : : e # e 6 ( e 6 : - ; = : 6 * + + : ; # : : # = : # - ! k : = e 9 % + + = - # ; # * : # # = + * * - - # # # + * = # - # # - - ; : : : # ; = ",
+" % : : - = # , e 6 ( e , # # ; = # # ; : ; = - - - # # - # - ; ; : # - # - # ; : ; * - - ; # # : * : - 6 # = ; ; ; ; # ; # - # - # % # - # - # % # : * : - - % # : * # % # : * * - % # : * , - # # # # = - # ^ + # % + # # = = ; - ; ; - * % % # ; - = % # # : + : : = . # 6 ; # - - e - 6 6 - - # # : + # - # : ; * - # : 6 - # % # # : = o p e # # = ; + - - # # ; : : ; - - % * : + + * # = % # - # - # # = - # = # , # ; ",
+" # * ; # * % # # # | - e , e ; # # , 6 e = # ; - - = # # ; # - - : : = # = : : ; : % , - ; ; # # - # : # # : , - 6 # ; + # # # - * * + # # - + * : - ^ 9 + # * - # * = * : - - % * * : - - * * * - = # # $ ; % # ^ # % - - % * * = # * % % % # : * * # * * # - * # % = # : : ( , ( 6 # # : - : # * # # ; - - ; - # ; # - * * + % * * # = = * s = % + # # % # : ; ; 9 + # * ; : # # # ; = ; - - - - - - # # * ; # # ( ",
+" # # # # # , # - : = # # # # # ; , : 6 - - # * = * : ; 6 : : # - + ; # # - # : : - # ; # # ; = : # : + ; - - ; - e ( ; # # - # - % # = - # - - + ; # - - # # - % * - % ; ^ # + # : * ^ ; + # * ; : % # # # n G , = ^ ; # = # # * = = # = = = * : - - # # * ; - - # ; # = # # : e : 6 - ; ; ; ; # : : ; # * : : # ; # : # - ; + * = * ; * = = | * - + - * * * # - # # # ; # = : * ; # ; : e - # # - - # , ; # # * * : ",
+" # # # , + % $ e ( # * : - # 6 ( # 6 e : - : # % + # ; - - # : : # - - * = : - - # - # # ; : # : - * # - - ; ; - ; ; - - , - # ; # - # # # = + # ^ # ; : * = ; # * ; : # : # $ * : - - % # # ; # = : # : # = , ; # - # $ 7 # % # + * = - * * # - # ^ 9 + # : % * ; + % # : * * * 6 # ( 6 6 # - 6 # e # : e 6 : - = - ; = * : - # - # # % * * H } 1 I . % = - # # # # ; : ; ; - * ; f * * # * * ; 6 * + + : : : : : ; # = 0 , ",
+" # * ; ; = * , # # , ; ; ( 1 ; , e - # - # # : : ; # - % # # - ; - # , * = # # ; # ; : # - - - # # - # - - # , e # : * * - # ; # ; - # # ; : - : = : 6 # # - # # # = : # # * e ^ 9 + # = # : e 9 + * : - # , # # = # , , # $ ^ # # - = + # = % % % # # = % # # # # # * : - - # # ; - # # # 6 # ( 6 ( : - : ; ; * ; : - # : - # % # ; # # # # = J K @ % = # - # # # : : ; : - # # - # # - # * ; # # : ; * # ; - # - # * * > ! ",
+" , % * # , , ; # # * # : ; , : # ; : , , # ; # : e ; ; * * # * = + : # , ; * # # - ; , - = # # # # ; : ; - # # : * : - - % * - = * : : + + # - : : : ( ( e : * : ; ; ; ^ ; - ; - - # # ; # : # : # # ^ 9 + : # % # : # * # : # $ 7 = % = % + * % = L # * ; # # # = * # ^ 9 + ; # # % ; # = # ; - # # - : # ; : : ; ; # - - # : + * * : - ; # g > = = - ^ = # + * # # : : - # : + # - ; # - % * ; ^ # ; - # # # # - - # * ; ; # ",
+" $ # # # : # 9 ; # = = - # # 6 # , - - ; ; - # - : # : # + * ; % ; * + + = - ; - - ; - # ; - - # : : ; : # # - : ^ 9 + # * ; # # : - # - - - - - ; # # , 6 e = ^ # = # # % * ; - # # : : % 0 * ; # # - # ^ 9 + * : - # ; * * # > . : # % . - # * # % # % % # % % % 0 - # # # * * * : + * * + * * - # : # ; - ; # # # # - # # - # ; ; ; # * h % = , # = t % , = - # ; - ; ; * # - - # ; : ; # # # ; - - # ; # - # # - % * ; - [ ",
+" # 6 - # ; # # # ^ # # ; # # ; 6 # ( , ( 6 : : ; # - ; # ; : ; ; * ; # , # * * + # ; : ; * = * : ; - ; - # # - # # - # ; - # # # - # # # # - # - # ; # ; : 6 e # * 0 = # # # # : * * * # = % * = = = % % = - # % ^ 9 + * = # # # # % | ^ C > < ^ % + * * ^ * * = = % 0 ; * ; # * ; : - , # # # # # - # : : - * ; # # ; : : # - # ; ; = # # * = % = * , $ = % | # - : : - * ; ; % # - # # # ^ ; # ; # # - # ; = * : : ; * # # # ; % $ ",
+" + : ; - * , # = # - # ; # , 6 # ; % 6 6 - ; ; ; : * * - - - ; ; # ; # - - ; + * # - ( - = + # ; ; # # # - # : ; % ; # ; = # # ; : : - : ; # # ; : : ; # - # - # - * * + ; # - # - % * % % % % * # % # % : : # % - # # % # - # ! # ! * # : # M ^ ^ ^ + * # ; # ; = * % * ; - # % * ; * * - % # # # # # # - # : ; - , 6 # # - # ; ; = = ; = % * = % # # ; # # N # - * # # | - ; ; # # - # # - ; : ; ; : # # * * ; ; # - - - # # # # ; : 6 : , ",
+" # + * # = * - # - ( - = ( # : e # # ; : 6 # - 6 # # # # - # # - - # : # - # - ; # # 6 , ; : ; ; # ; # - # ; - - # * ; - - * ; : : ; : : : : : ; ; : ; - - - # - - : # # , # * * % # * ; # # = # = # # # # % 6 ; # - - # = * # # + # # = * % % > ; ; # # + % + ; - = % # # + : * : - - % * = * * * * % # , # : ; : 6 # % # % + - ; = ; : ; # + # * ; - # # # , - # * * % * # : # + * # # % # % * + # : - # ; : * ; = * # # - # # # ; # ; - e ( ",
+" ( , # , # # ; # # + ; + ; , # % # ( : - ; = ; e ( # ( ; 6 : ; ; - : % : : # 6 e 6 - - : , , 6 ; % = - : : = % # : # ; : % + - ; ; ; : : # - - - ; ; ; : ; ; - # # # : ; # - - ; + * : - = * % * * # # # * * # - : ( , ( 6 # % = ; # + # # - * # # ; - x # ^ g = - = = = # # f + ; 9 + # * ; # * ; * ; # - # # = - ; * * * * : # # ; , - e 6 - # ; : = = * # * - r / # # . # ; # * : : - * % * * * - # # e # # : : # # # % % ; * * % * * # # : - # 6 ",
+" , 6 e # # = ; - # 9 : # # 7 : # , - # ; ; : - ( , 6 , - ( ; e e , # : * = # : : 6 ; * * % : = - # # ; # } - ; * - # ; - ; : : - - - - - # : - # - - - - - - # : ; # # - # # # - # # ; # # - # = % * # # * ; # % : e : 6 - * * ; $ # , - = = # : | * # : : e % = - = ; % = + # % # : - - : ; % * ; * ; # * # - # . # # # # # : e ( , 6 ( : 6 # : # ; 7 - # * - - + . % M 8 O = | % - # # # # ; - # + # # # # : + # # % # : * * * - # - : 6 - # - # ; # - % - : ",
+" # 6 6 # # # # # ; # # * - # - - # % # # ; - - - # # : , 6 6 6 ; ; : # * * # # ; - # 6 : # * ; ; ; # = % : : - : * : : - * : ; ; - - # - - = - * # : # - # , - # : ; # # # - # ; - # % : : # - # % = = # % * ; * : # ( 6 6 - = + : # % # # # # # : - - ; # % : - ) # % 9 % + * - % # % % * * # # * * # - # - # = * * = % + # # - ; # : - ; # 6 - * * + ; ; # ; - d # # = # = - 1 # . | * # * * * : - # ; : : # # + * # - - - # - - # # # : # ( ; - ; # ; : # # : ; : ; ",
+" , 6 , ; # # # # # - # 9 , e # # # - : : - - - # - - ; : - - # e ; : # 6 : - e , 6 , 6 e ; # = * # # ; - % # - ; - - # * + + * * : : : # - : : # # : - # : : : - - # * + + : - : * ; + - ; - - # = = . # * # # # : # # # * * e e # # # # ; % # # = ; % ; # # ; + , $ ^ ^ # # # % | - # - ; : # * = - > # ; % % # * * = - = - % ; # # ; - # # ( ( ; # = = + # , - # : % * # + : ; ; # : = 6 # # # # + : * # # : = = * = - - * # - : # - # - ( 6 6 # - # ; # # # = - : , ",
+" , 6 ( # # # # # # # - # # $ # + - # ; - # : # ; - ; + - - % * + # : : # 6 , 6 : : # : 6 e : 6 : ; - # - ; # - # : # : ; # # = # # - # # # # - # # * # : + - # - # # - : ; * - # # ; e # ; : # : ; - # # # # ; # , * * + - : , # # * - - 1 = # ^ ^ ; # * ; # ; = * - : - e + = * ; # $ = - # % = - % # : % 0 + * = = # , * * : # , , 6 , , 6 6 e ; # - - 6 - ; ; # * ; * * : : = = # ; # ; # # $ f + % % = > # # = % ; # 6 # % : : # = - - 6 - - # ; 6 - # ; : - % ( ",
+" , , e , # # # - # # # # # # # ; # , 6 # - , e , 6 = = ; ; : # , 6 , : , - : ; , 6 - ( ( 6 # 6 e 6 - # : ; : : * # - - # - + : # # # : - # , # # ; - - # - # - # # + - ; - # % - , # - # ; # - - # , - # ; # * % # ; + * - % , 7 # : ; # # ; # = # - , # # + * # - % = % # B ; - = = d * - = % # = * : - - - # = % # ; # + # ; - - , ( : : : # e # ; , - 6 # - ( ; # = - % - # # - ; ; * # * * e # # # % = # : # - ; - 6 - ; : 6 # # e : * ; # # # : 6 ; : : ; : ; ( ",
+" , # # , , , , , # # # - # # * # # ; e - - # # : - # # # # # # # # : , 6 ( ; - # e 6 6 - - 6 # : : 6 ; * # - # - # ; - # # - # - # * - # : + ( - * = * : - # - - : * ; * # - * * * ; - # , ; ; # - # - - # # ; * * ; - # = : ; ; - - , ; ; # # + : * # - # # ; * % # # # * : ; * P 8 # * * + = : ; # ^ 9 + # * ; = % # : * % # : * * - 6 - - , e e ( , 6 ( ; 6 6 6 6 ; + # * * # - - ; # - # * ; : ; * * = = , 6 : ; , - 6 # - ( ( e 6 6 # # - * + * e # 6 * * - # # e ",
+" 6 : * - 6 , # ; e # # % = # $ # # # : = - - 6 : = : : - - # # : * # 6 - ; , 6 , , : # # : * ; : # # # 6 : ; # ; : : - ; % ; : % ; - # : ; # # ; : = + # , # = + # - % # - ; : # # # * = + 6 : ; - - # ; + : ; # ; : ; # : - - $ 9 + | , # ; ; # # , , { ; # ^ # ; # % = # - # = ; + , e = - g + % * % # = % * ; - * : - - * : - - % * : # # # ; - : - ; # 6 : - : : # # ; ; # - # ; : ; : % * ; - # * ; = # e - 6 , 6 ( ; 6 6 e e 6 e e ; # ; ; * = , ; # + # ; * ; * ",
+" # 6 ; # # # # # e ; : : ; , # # # # = + * # - # ; # # # - ; * # # : 6 : # ; : : e # : ; # # # - # # # 6 e 6 : - # # - # ; : : * - # 6 e 6 - # : ; : # # - ; : - - - , ; # , # # # 6 # # # : - # # # # - # - , e 6 e 6 # 6 e ( | # ; # ( # # # ; ; # , , | # e # ^ # | , ; - ; - * * - 7 w # ^ # % : - # - # # : ^ 9 + # ^ 9 + # * ; # ; - # : * * - # # ( : ; 6 6 # - # : = - % # - - - # # # ; % * ; # # - # : - ; # 6 : : e # : e ; : 6 % - . # # * = : = # * ; - ",
+" - - : , # - ; # # , ( - - # * * : : - # - # - - # # # - # : - ; # . . # 6 ; # - - e - e 6 - - , - # - - : - - # # : = - # ; - ; # ; : : : 6 ; : , , # - ; : # - : ; : : : ; - : + ; # # # ; # - # # * % : # - # ; : : 6 ( 6 , - ^ - : ( - - # # e ; # ( 6 - # ( # # - # # # * * = % - # # # , , $ % = % = # = # # 9 * : # # : # # ; # = # : * : - - % 6 , , 6 , ( , # - - # - * : * % * ; : ; # # # # % % # # - # ; - # # 6 - ; ; : # - - # : * * + + * = # ; : # - # e ",
+" , 6 ; # , e e e # - - - # # * # # - : ; - ^ # : + : ; : : : * ; * = # ; : : ( , ( 6 # : 6 ; - : ; ; : : - # : + ; - * : : - * ; # # - # # - ; ; # - # - # - # ; : - # * - # - # , # * * % % : - # # + % - - # * - 8 # # ( 6 , , e - , # # # ( ( ( # # , e , # # ; : * * : , , # * - - % # # + # # w $ . 8 % = # - # % ^ 9 ; * # , : # : % % % ^ 9 + # * ; : : : - - # ; ; : * % * ; - - - - - # % * ; * * * : ; ; e , 6 , - 6 # - ( ; ( ; : ; # - # , # * + - > # ; = * , ",
+" - 6 e , # # ; 6 6 ( - # # ^ % - # # - # # * - * # - - ; ; - # , ; % = , 6 # : e : 6 - ; ; # 6 * # e e , - : # ( - * ; : # # : ; : : - ( ( # = : + ; ; : # ; - : : : : % ; ( # # # - # * ; + % - ; : ; : - # # # # # - . - # + # - - # # # ; , , ( - : # # , , # + : ( ( 7 9 + # # e ( > $ # # # # # % # $ , { 1 = % - ; # # # # * # ; - = = * * = % # : * * # - - # # : ; # , # ; ; # = # % * : - # # # # # # # - ; = - - , 6 ( ; 6 6 6 6 ; 6 e 6 - - # - - + = = % # : * * ( ",
+" Q ( : , , e , ; : # - # # # : * # ; # ; : * : : ; - # # - - # : - - # # e - 6 # ( 6 # . - 6 ( ; : 6 ; ; : # : 6 , : % # : # ( 6 e e ( 6 ; e : - - - - - # % - : # # # - * : # + ; * * % * ; - = * : - ; % : - - # # % : * % # ^ ^ - ! # % * 6 , : # - 6 ; ^ # , , ; 6 , , : # - ; e - ( | # - : ; % # ; # , # # $ 7 = = % # # % # % # # : - , # # * + * : - - % * * = # # e , e # # ; ; ; * * * % # * + ; % ; # # ; # - - # : - ; # 6 : - : : e ; : 6 ; - # % # : # + * : - - # 6 ",
+" , - = # # : - - : e : ; ; # = # # : # # - # - # : # ; ; - : - ; - # % # - # ; - # # * = e ( ; 6 e # e ; : # 6 : # ; # ( e : ; # : 6 ; , ( ( , # - - - # - - - - # # # - # ; - # : 6 - - * # ; : # # # - - ; # % % = - = l # , # % = # - * * ; : # # , e e : # # e ( , , e # # # e ^ ; ( e # 8 9 + # # , , # - ; e # # = ^ ^ 0 % = * - - ; ; ; # # - - ^ 9 + # * ; * = # # - : , , e # # # # # # ^ - - % + * : # # - ; : : # ; - # # ( : ; 6 ; , : : # # = * * : - - - ^ 9 + # ; : ",
+" # ; # # ; # # * = % # 6 # # e # # # : : # # - # e # % ; = * # * : # : * * - # 6 : # # * = # # - : : # , : : # 6 , e e 6 : - # # # # ( e ; : ; 6 # - # - # # = % ; : : : : + ; : ; + ; : : % - # : - - # - = # + % : = . = % 8 # - * * - | # e e ( - # : : , # - ; ; # e e } ! , # # # ! , # 8 # # # # # - : ^ # ^ # g ; + - = # # # * * # # - # # # # e # # , > # ; - - , : e : # # # ; - ; ; # # : # # : 6 ( , e , 6 , , 6 6 6 e 6 : , : , ( * ; ^ 9 # : - - # # , - : ",
+" , : # | ; # | * * # * : - - * # # # # # # # # # : ; * : + # ; # # - # - - # # ( ; - # # # e = % ; - # : , : , - : ; # # - : : : : : 6 - : ; ( : # - # : - - # # - - ; # - # # # # - - - ; % % - - # - # k c % : 1 ^ # # = * # : : ; ; , , ; # : : # # - # , 6 : , 7 7 8 ; # # ; - , , ( ( # # % # ; # , ^ + % - * = # = # * ; # : # # * * # : # # # % # : * * - $ , B e : * : ; : - # , ; # + ; , - * : e # : : : - 6 ; * : , 6 ( ; : - - # # = = * % * * # , ",
+" ( ( : # | # ; # # # # % # # - % ; + - - # - = % - : - # ; ; ; % : : * # : 6 , , 6 e , # # # % % ; # # - - , 6 ( ; = # # * + + # # - - ; : ; # - ; : # - : # # # * - - : # ; : ; ; # # ; - ; ; - , - ; = # v G E # + = # - - : ; # # # , # # # # # - , # # ; , # , # ; ; ; # } ( ( - - # # # , : # ; , $ = % % - | % * ; # ; * # * ; + # # % # * : - - % * # ; ; , - # - - # # # : : ; # + # # + # - ; # # # * ; ; : ( - ; , 6 - # # - # # ; - : # # ; e ",
+" , , # - - , ; # * # - - # # ; ; # # % = # - = % - # # # # : # % - ; = * : # : : : : : 6 6 : : : : ; # . . # ; ; % ; , : # # = + * - - ; ; # # : * : - # = # # - % ; # ; ; # # # # * * : # - - ; + : = # # R # # - : ; # # # # | # ; : , - # , # ; - , # ; ; # # , x S , , ^ % % # % # ; - # ; x g % - * = * % = # # % * ; # = % # , ^ 9 + # * - > # ; - - , , : * # - - # 6 ; ; : : : # # * = # - # # - - 6 : # ; : : ; # ; : # # - ; % # # , ",
+" # - - , # # , ; : - : : - # + # # = * : : : # : ; - # ; : # - # - # ; ; ; # : - - - # # # # + - - ; # * = # : # : * : e # : * - # = % # - ; : : : ; # - # = = * # # e - # # = % * = # # % * % # * # # * h 1 # ( 6 : # : , # , ; # * # # , | # # , ( # , , ; ; , , , ! | , + ^ # - , , # # > . [ = - # + * # # % # # # # # - = ; - # # ^ 9 + % # : * * - # * # 7 # # # ; # ; = + # = : : ; # ; ; # . . # 6 ; # - # - - # - ; # ; : - * % # ",
+" , # ( # ; ; , - : # # : : ; : # ; % : # - # - # # # : : ; : ; # # - - - - # # # - : : - - - # - - ; # * = , 6 ; # - - e - # # # # = # % * : ; ; # , = + * * + + ; : 6 * + ; = ; ; # # # . ; % # ; ^ = l % 6 - , , ( # ; ; , ; # $ # 8 # , , , - , # , e # ( , # # , ( ( | # # , - e ! 7 > ; # ; # = - + * - # + ; * - > # ; % # - # - * : - - % * = = # = % # # # = = ; # + : # # - # # # * = # ; : : ( * ; # # # - = - : ; : = # , ",
+" # ; # # # # - , 6 # # - - - # - # # : - ; - = * # - # : : - - - # ; - - # - # # # - - # - ; ; # # # : * = # ; : : ( , ( 6 # # : + + * * * * + * # # ; # # # = % + : - # : ; * : - # # # * # = = ; . , : , , ( : : # # : # | , % = , , # , , ( 6 ; , , e , # # , ( ( ^ ; , , # - # ! * + # , % = * * # % ; : % # % # : * * - - - > # + # * ; : - - - - e } - + + # - 6 6 , # - ; % # * = , 6 # : e - = * ; : * : + * * : * [ . ",
+" , # ; # ! - ( ( , e # - - # # ; % # - ; ; - ; 6 # # # - - # # 6 ; * = * : : : = - = + # ; : # # # ; : - * = , 6 # : e : 6 - ; ; ; : # # # - % # # ; # ; - + + * * - - # - ; - # - ; ; * + + - = = = % # : - ; ; ; - # , - % # # ; , , ( - : ; , , , # ; , , , { # : * # ; , # | = = # # , + = - # % * # # * : - - % * # : % # : * * - : - , # * * - ( ^ # ; 6 : 6 ; ; - # # : . # e - 6 # ( : - # ; : ; : # # # * % / . ",
+" # # : # | # 6 ; # # : - # ^ - - # # ; # , e - ( e # - : ; : # ( - = + # # # * * : : - # ; : ; * : # - : ; # e - 6 # ( 6 6 # - 6 # ; + : - # - - - : : # - # # # # + * : : # - : ; # : ; * % = = | n 2 - ; ; # ^ - , ! # # * 6 e : , - 6 ; , , , , ; 6 , , , , ( , ^ ( , ; # # e * ; # 2 D , ^ # ; - # ^ 9 + # * ; # : * * - - % * * # = % # # ; : * , % ; : , : ( ; : , = = , 6 # ; - # # - , : - - ; ; % : - ; > . ",
+" # : : # # # # 6 ; # ; * - # ; # # % # - # : - ; ; % # * # # % 6 , ; : ; ; - - : * * - : ; : + 7 | - - - # , : # - 6 ( ; ; : ; e ( ; # # # - # - - # # : : ; : ; # # ; * = - # * # # # = = = + - % = ; - # # - - ! # * * # ; # # , e e : # # e , , , e , , # ! # # > : # | - , # = # # - < ^ % + + - = % # * : - - % * # * ; : - ; # % # ; = + # # # e # e ( e ( ; ; # e # e # : + : # - # # - ; ; - * % : - = T T ",
+" # - , # , , - : ; # - : # # # # ; * * : ; # # ; : : : ; * * * : , , 6 : e # - # = % * ; - # # # # # # : # - # # # ; # - - # - ( ; 6 - # - # - # # : ; : # # ( # : # - - ; ; : ; # = * - # : ; : = # # - - # # - * * - , ; e e ( , # : : 6 , , , ; # , e $ 8 # - ; # - , # - , * : , $ 0 = % = # - # + 9 + # * ; : - = : # # # ; + = ; # + * e e ( , 6 # e 6 ( , # , : : ; - # # - - # ; : : - : - * H = T T T T ",
+" | = - , ; # # # # , # - - ; # = - = # # # - # # - - # - # = # # ; - : # - : : - # # * ; e , ( # # : - # # 6 # * : # # - # * - * - # # ; # % % - # - , 6 e 6 6 e # * = : - # - # - - = - # # | , | % = > , $ - # # * e e + * | # : # ^ # # * # : : ; ; , , # # : : , , ; # , 6 ; # # # 8 , , > # ^ , e , , ; # # A 5 # + * + + # # : * * : # % * * * > # + + # ; e # ; # , ( 6 6 - ( e # , # - - e , # , % # | - # * = # : : : = U T T T T T T T T T T T T T ",
+" - % # , , - ; # # # = - , % - ; - * + # ; : # ; - # # ; # - # # - ; 6 - - # # | ; # = = - * : % : : - = # # ; * - # $ - # + # ; % * * = = # * ; : : 6 - - # # : - % + ; - # % # % % ; ; - # h 1 / = % # | # ; ; 0 # , e , - - # # # # ; # # : : , - - : ; , , , # # # # # , # , # = ; # - ( , , # , - # , , ; , % % ^ $ 7 = = + + % : - - - % * * * ; ; ; % , # # ; + ; # : # , e - - 6 , : e : ; : ; : ; * = = % : - # # , : : : : # T T T T T T T T T T T T T T T T T T T T T ",
+" e ; , , # ; # ; # - # # # ; # - - # # , ; ( , e , 6 # - # % = # # # - ( e : # # # = * % - - # : # * # # * % # ; # # * + + : : # # * ; ; # = # # = : e : - : # * # # - : ( ; : # * * * + ; : = > | . = * # 8 : # - + % > , ; - - # ; ; , ; # # - : # # , - : ; , ( # # # # ; : # - - - # ; * - , , , ( ( 9 6 ( # , , - $ # # $ # % = % + * ; % * ; : - - > # ; - = % % # % - # 6 ( ; # - * : e - - # : ; # - - - # - # - ; # # : : : : V T T T T T T T T T T T T T T T T T T T T T ",
+" # # - - ^ # # e # # , # # # # * # ; # # - - # : # : - # ; : : : ; 6 : : * % - # # # # ; - # # : : : # # * + ; # - : = # # # * + ; : # : 6 # # % = + + # # ; ; # : : ; * ; e ( e ( e # # # # # - # % > ! = * 8 , : ; ; = - + % , : * - ! # ; ; ; # ( # # # # # ! , : # : , # # ; # # - # # # # # # - ( e e , , $ B ; , , ; - # ^ ; # # # # % 9 % = % # # = - * = % # : * * % + * # # # , e - - # + # ( - - - # # ; - - # # # : - ; ; : : : : : T T T T T T T T W X Y X Z T W X T T T T T T T ",
+" # : - # # , , , , , , # # , , # # # # * ; : + % % = : : ; - # ; - - # # # - ; - # # # # - # # ; # # # # + % ^ # - # * : - # % = % * # 6 : , * # : ^ ^ , # ; e # : e 6 ( 6 , 6 # e - - : # ; 0 { = . $ < ! $ - # # ; - - # ; : - - # - - # , # # # ; ; # ( ( # ; , , e # , , ( # # # # ( , , # # - # , , ( e - - e { w # , # ; ( - , # - # , e # % - # % % + * : # * : - - % = # # - ; = # , : ; - : # # - ; : ; * = * : ; % = # - ( : : : : : : T T T T T T T T T ` . . . ...Y . .+.@.#.T T T T T ",
+" % : : - = # , ; ( e ; # ; # , ; : ; # - # ; : # ^ # ; # - - # # - # - - # ; # # - ; - # # , # ; | % * = : = * # # # # ; ; # = * ; # % # 6 , # : # - # = ; e ( ( : - : ; ; # ( 6 6 # # * # # # - d = < ! , - - - # : - , - > # , ^ ^ + 6 6 - - # # , # # ( e - - - # ; : , , ( : : # , ( # , , # = # , , , # ; : - e B x , ! v - , , ^ - $ $ # # $ = - # % + # : # ^ 9 + # * > # # # # # # ( ; : * * - # - # # - = + # # - : ; 6 * : : : : : : : $.T T T T T T T T %. . . . . . . . . .&.*.T T T T T T T ",
+" > * ; # * % # e , ! e e # # , # , ( # - , : - ; ; + * # ; : + ; + : * : + : + : + : ; ; - ( , # # % * ; # # ; # ; * - : + % # ; : - : , - : # * * * * : ( 6 # - : # ; : * : * - : % : ; : : = 5 5 | | | # % # - - - : * # : % # # # # ; , # # ^ , ( , ; # , e , - # - ; * # # - - - ; ; , # ; : , # # 6 6 e # # # : ; e e e , , , , ; ; : - # | # > . [ # ^ ^ 9 % % - # # # # - % # # # # ; # # # + - % * = # - - - # # ( - # ; - - + : : : : : : ( =.T T T T T T T T -.;. . .>. . . .,.'.).)...!.T T T T T T T ",
+" ; # # ; # # # - : = # # , , , # # , : : ; - # - # % # # # # - - - # , ; ; # # - : # - # # # - ; ( , # # # : # 9 ; - = = - - # ; - # # 6 ( ; - # e 6 * ; # e - # # : # ; - ; # # # # * - - - ; = # 1 > = # % = # - + : ( # : - * : # % # ; + # ; , ( ( - # ; # , , # + # , # # * % # # ; ; # ^ # ; , , , 6 6 ; # - # , # 6 e e , , ! # # % ^ ^ # ; # ! 7 : # # = e ^ # = # # = % # # ; ; # , - , # ; e # * ; : > = % : ; ; , # # ; + ; : : : : : : : # ~.{.T T T T T T T W ]. . . . . . . .>. .>. . .+.@.T T T T T T ^. ",
+" ; # # + % $ , , # * : - # # # ; , ; : ; - - # # % # % # # # - - - # ; ; ; # # - # # - # # ^ - ^ # 6 - # ; # # # # - # ; # # # 6 * * # ; , e 6 , : , : ; # # % % # : : - * : % ; # # = % - - # = ; | ! , # # - # - - - = # 9 + ^ ^ + # ! : # * 6 e : - - 6 ; # # , e ; 6 , , , # # # | - ; : # - - - # , , # ( ; ; - # # # , ; ; e 6 , , , # # # | # # # # - = : # - # = = + ; # - - = * ; # % # + : # # # : % # - % # ; - ; = = ; : : : : : : : : # T T T T T T T T /.(. . .>.>.>.>.>. . .>. . . . .*.Z T T T T T T ",
+" | * : : = * , e , , ; ; - | # # # , # - # - + : ; ; * ; ; : % ; # # * = ; - ; ; # - - - + # ; : g ; : ; : * # # = # - # ; # # ; # | % : # ; : 6 e # - - ; - * % # : - # : ; : * : # - = = # : = : # < ! , % % # % - # : # * * # ^ # # + + # # * * - - ^ ^ e e e : # - , , , , e # # # # - - # - - - - : # , , - e 6 : # # ( ( ; # : : e e # # # # ; # ; : # # % = * : , , % ; # % 0 # # # # * + + = + ; * # : = : = % # : * * * * # : : : : : : : : 0 I T T T T T T T T T _. . .>.>. . . . . .>.>. .>.'.). ...!.T T T T T T ",
+" # % * ; , , ; 6 # * ( : ; , - : # # = % + : ; - - ; ; ; : : : * : # - # ; - ; - # + * * * # , # 6 # + * # = * - # - # - = # # - ; # # , , # # - # ; # : : # = - # - = * : : : ; ; # # # + * # : 1 # : [ - ; # 9 = * * + ; * * + # ; e - # # # = ^ # - * * - # # e e ( # - : : , # # # ; # e , ( # , ( - - : : # # # ; 6 6 # # , ( e ; : # ; 6 e , # 1 # , , 8 9 + $ * - # : ; ' g % - * = + # # e # ; - ; # + * # * - % % * : - - % - % : : : : : : : : : 0 :.T T T T T T T T <. . .>. . . . .>.>.>.>. . . .,.'. . .+.@.T T T T T [. ",
+" , # ; # : , 9 ; , = = - : , ( , ; # * # - - ; # # - - - # # # # # - - - : 6 : ( ( # ; ; # e # # , , # - # # ; # # + # + ; ^ e # # * : - - ; ; ; : : # e 6 e , ; ; : * = ; : : - - # : - - # # : # : # + = - # - # : # # + : 6 - - # : 6 e : e 6 = # ^ # % * # : : ; ; , , # ; : : # # : # , 6 : # # # 6 ( - # - - ! # : 6 6 , # - 6 ( ( # # ; ; # , e # - ; , , # , # ; # , # - # ; : - # # # ; # ; : # # : # ; e # = ; - = # ^ 9 + # = % * : : : : : : : : : - T T T T T T T T T T }.|. .1.>. .>. .>. . . .>.>. .>. .>.). .&.*.T T T T T T ",
+" ; 6 - - ; # # # # # # ; # # # 6 ( e ( , # - , - # ; - - # - # - # # # | # # e 6 ; e : # : ; , # # # 6 ( , # = ; % % ; : # # 7 e , # ; ; ( | - 6 # ; * - : ; : - # * * * # # - # ; # - - # # % # = 1 = % - % - - # # ; : : : - : : , e - # ; ; , # - = # % # # - - : ; # # # - - - # # # # e # = ; # # - # # # ^ - - # - : ; ; # , e ( # # # # # # , , , - - : ; 9 7 , 6 # : - ! ; : * + * ; $ ^ # # d = % + = - : + : - - * * # # ; = + % * % : : : : : : : : : : | T T T T T T T T T -.;. .>.>. .>.>.>. . . .>. . . . .>. . .). .2.3.T T T 4.T ",
+" # : - # * , # = ^ # # ; # , ^ # # ( e 6 6 e 6 ; * = * : # - # - # # # # # - 6 , ( 6 , e - ; - # ; 6 6 : ; # - # # + # * , # , - # | : # # ; , # ( - = ; ; : = : : - # - # ; : * = % + # * - + % % = * > = = : ; : ; - - : * ; - # 6 - , # ; e # # # # % - : # # # - : ; # - # # # # ; : # : - # ; ; * * - , ; : - # # , ; - - , ; # - # % % # # # ( , ( - ; e e # , ( - | # - - , % ; : + # w 5. ^ = * - - - # = - % - # + # * - = ; # + * : : : : : : * : : : + T T T T T T T T T T T ` . .>.>.>. . . .>. . . . . . . . .>. .1. . .T T T T T T ",
+" # + * ; = * # # - # - = - ( # , # # 6 , ( : # ( - = ; # # # # # - - # ; - # ; # - : - # : # ; # # # 6 ( # # # # # # # # 9 , 9 ; # = = - : # - , 6 , ; : - # - ; # # # # - : : ; : + ; : # ; : - - = = - > | $ # % ; : : ; , # - # # ; e 6 # e ( ( : | # ; # # : - ; # ( , : # : , # # ; ; * * # # # # # + - ( e , # ; # $ e ; # - # ; # # = = = # # - ( , e , , : : ^ ; # | b # ; , ; # # : ; # # ( ^ ^ # f - * * * - = # : : - * = > # ; e - : : : : : - # , = 0 = T T T T T T T T T T T T 6. . . . . . . . . .>.>. .>. .>. . . . .>. . . .T T T T T T ",
+" , # # , # ; - # + e + ; e 6 , e # : ; - # , 6 , ; e 6 - # - # - + - - # # # # # # % ; : # # # * : 6 ( # # ; # # , # # # # ^ , # - # ; # # # ! , : , , ( # - - ; # ; + # # : : - ; # e - # ; ; % % = e = > ; # = # # # - # , ; : - # ; 6 e ( 6 # - : # - # % % # * ; $ - 6 : , , ( # ; # # # ; # # # - # # ! ( e - - ( # e e ; - - # ; * # - - - - , , ( # e , ( ( 6 e ; # - # # # # % , # ; # # # * - ! # ; # + + * * - # # ; # % = % # : * # : : : : % * + # { 7.% T T T T T T T T T T /.(. . .,.>. . .>. . . . .>. .>. .>. . . .>. . . . .T T T T T T ",
+" - 6 e e # = ; - # ; : # # { # ; # , : 6 : # # : , , : 6 ; * : + : * # # , # # # # + * % # ; + # # # , , , - - , # # # # # # # = ^ ( # ; # - = # # ; - : # # * : : ; # # = # , , ; # # # : + ; ; % 0 ; 6 # # = * # # # # ; : : : ; : + - e - e : - # : # # % * % # # - = ; - # , , ( : : # # # # - ; - = # # , , ; ; : : e $ ; ; ^ # # # # = - = - # ; 6 , # + : ( 6 , e # ; - # # # , # e - # : # - ; # # # , ; # # + 7 # - = = % # * : - - * : : : * : # - 7 * | T T T T T T T T T T _. . .>. .>. . . . . . .>.>. .>. .>.>. . .>. . . . .T T T T T T ",
+" # 6 6 ; ; # - # # # ^ * - # # - # , e 6 - ; - - ; - : # # 6 : # ; ; : ; ; : + * # ; e # * - ; ; ^ # # # , 6 # ; # # - # - ; # # # # - = - : # ; # # e # , - , * % ; - - # * = ; # * ; # - # - # # = * : - , = = : : % # - # # # : - - ; : 6 ( ; # # # : : - # # # # # , ; : = # # # ; ; - ; ; ; - # ; - # # 6 6 e # # - : ; , , ; # # % % # ; = % % , , , ^ # ^ e 6 ( 6 , # - : # # = # 7 # ! - # # # = # # : ; 2 # g * + ; ! - # % - 9 + # - : : : # : % # 1 l . T T T T T T T T T T T <. . .>. . .>. .>. .>. . .>. . . . . .>. .>. . . . . .T T T T T T ",
+" # 6 ( ; # # # # # # # 9 # ; # # # % : , : - ; ; - ; 6 - # 6 e # - ( ; e e , e # > # ; # , # e # # : * - 6 , # ; , # # % # # # # # # + ; # # + # ; - : = - # # ; * : ; ; # = * # * # # # : * + : ; * * + K 5 = # # # # # - # # - - ; : , ; , e - * 9 ; # # # - # ; = + ^ # # e : # # # # # ; ; # # - ; ( # ^ 6 6 ; ; # - # - 6 e e , % = | # - , # - : ( , # # # # 6 , # 6 : # # ; # # - # $ e , # # # , # , , * $ # ; ^ # 8 ; % # + * # = # 9 : : : : e - 1 8.r T T T T T T T T T T -.;.>. .>. .>.>. .>. .>. .>. .>.>.>. . .>. .>. . . . .T T T T T T ",
+" ( 6 ( # : # ; # # # # # # $ , + - # ; # e - ; # , e - ( e : , : 6 6 , 6 ; ; : : # # # : # # e e # # 6 # ^ B ; # # e e : : = e # # # : # # 7 + # ^ e ( # * : - - # # # - # # - # # # * * : : - - - ; # ; # - = # # - = * ; - - # * # # - # ; # , : % # : # # # ; : = ; # + * : : ; : : # - - ; # - - - - # # # # # ; ; - # # - ( ; ; e # , - - - 6 e # # - # - ; # # # ( , # - : : % , , = = - ; $ | # ^ : : - ! , ; : , % ; $ 7 # % % + * * + - # ^ 9 + # % ; # # = T T T T T T T T T W ]. .>. .>. .>. . .>. .>.>.>. .>. .>. . .>. . .>. . . .T T T T T T ",
+" $ , e # # ; ; # # # # # # , # ; ; # : * 6 e # : # - ; ; - # # ; : : - # 6 ; : ; ; * # , : ; + , # - : 6 : : : # # , - - # % , # + ^ * : # % = # # e # , ; ; - | # # # - ; # . # - # * ; # = # % + ; # # 0 = 0 % # > # # # * ; : ; : = - : : : : : : : : : : : : : : : : : : ; # # # # ! * ; # - - | - - : # # - : , 6 : # # , , # # : : , e # : ; ( ( # # , ( - : - - ( e # ; 6 ; % # , , , : : # # , # , L | - ! $ # , , = % ^ # 8 # # % * * = - # # - = 1 8.n T T T T T T T T /.(. . .>. .>. .>. . .>. .>. .>. .>.>. .>. . .>.>. . . . .T T T T T T ",
+" ^ # # e , # # , # - # - # # , # # # : - - # : : # - - # # - # # ; # # - # ( : : # # * # ; - - - ; e 6 ; ; , e e ; e - - - # # # # # # 9 ; : # # ; e 6 * ( : # # : # - , ; # * % ; : : % # + * * - - # * * % = # | % % # # : # : , # * : : : # # e 6 6 : = : - ; * * # - # ; # : : : : - # # 9 # - - # # : : # # # ; e 6 : # - ( e ; : : # 6 e # # - - - # , e ( - # - - ( ; , e 6 , % * , , , # = ; ; # , , ( - # ! , # e - # ; # - { = - ^ * # + # - % # = { r T T T T T T T -._. . .>. .>. .>. .>. .>. .>. .>. . .>. . . .>.>. . . . . .T T T T T T ",
+" : * - 6 , # ; ( - # % = # - # # # ^ 9 ; ; ; ; : : + * ( : * : : : * ; % , : # : , * ; # 6 ( , # ; 6 e : * # : 6 6 ( - # # ^ ^ ^ # ( # # ^ # ; # 9 ; # = = - # : ; # # # * # : - - - ; % # # # # # * # * ; 0 = , * # * * : - - , : : : : # # # : ; : 6 - - # : # - ; # # - - ; = * ; : : 8 # - # - # * - - - # # : 6 e , # - 6 ( ( # # ^ 9 ; , e # # ; - : : 6 - # , # - - , , , ( , , # # 6 , # - , # # , ( 6 , ! , - % e , , | # * # , % { ^ = = = 7.0 # ; # % U T T T T T T T W ]. . .>. . .>. .>. .>. .>. .>. .>. . . . .>.>.>.>. . . . .T T T T T T T ",
+" 6 6 ; , # # # # e ; : : * , $ # # # - = = # * # - - - # - ; ; ; ; # ; ; * # : # 6 ( ; # , e * ; e ( : # # ; # : : , # # # # : # # ^ # # # - ; ^ # # # # ^ ; # # # ( # * * # # - # - - ; ; ; # # # # * % * * * K 5 % = - - 9 9 + # : - - ; - * * * # - # - : % * # - ; ; # # - - # : # # * : ; # # ; : # ^ 9 9 : - # - : ; ; # , e ( # ; # # | # ; - - - - , ; # # # ; - # # # ; - # - , , # # # # ; ; , 1 | # ^ - ( , , - 7 e ; # e e - ; ^ # = - # $ 7 , = 9 f | - H = = T 9.T T T T T T T /.(. . .1.>. .>. .>. .>.>. .>. .>. . .>. .>.>. .>.>. . . . . .T T T T T T T ",
+" - - : 6 # # # # # , - - # - # # e # : * * # - # # = ; - # : # # - # - # 6 ; # : # ; , 6 ( 6 ; ; # , - = * # : - - : e : ; # # # # # # - ; ; # # # # = # # # ; # # - # * # # = % + % - # - # = # - ; - # # * ; - - = % + # - # # : # $ # - - # # # ; : % ; : - # - # - - # ; % * # # % # + ; : + = : - - - # # - # - ; ; ; - - , ; # - - % % # # # : : - - ; $ ; % # % # # % 6 6 : # , e # # ; # # ; ; , . . ; ; , , ( : # ; } } ; # 1 1 - ; # ; , * + , # * % + # = @.T T T T T T T T -.;. . .>. .>. .>. .>. .>.>. .>. .>. .>. .>. .>. . . . . . . . .T T T T T T T ",
+" ( 6 # ^ , e e # # - - - # % - # ; , - - # ; : ; + * * * : : : - ( ( # # 6 e , 6 : # : : ( ( # # ; ; # # ; - : * = % # 6 , # ; : # e % % - ; = # - # - # - = - # # # } # ; ; - # : : ; * + * # : : ; - - + * ; # = - = = # + * : # - # ; - # # # # : : * : ; : % ; ; # % # % : # # * * * # - + : = - # : # - : ^ : - # 9 ; # - # # # - = = % = - - ( , e , # : : # # # - - # 6 ; # , , ; - # # # # ; : - ( : ; ( # # ; # - ( 6 , - : # + - # # , # # # : : : < > ^ T T T T T T T T W ]. . .>. .>. .>. .>.>. . .>. .>. .>. .>. . . .>. .>. . . . . .T T T T T T T ",
+" , 6 e ; # # # 6 e # - # # ^ # # # # ; + : : : ; : # # ; - # : , 6 ; e : : , - 6 ; ; ; ; # e , - , 6 : # # ; # - * * # * : ; = # # # : : # ( # ^ ; % # + ; + ; ; - = # # ; # - # # - ; ; ; * % # = * # - # # # ; - - ! # * # ; e : , , # - : ; * # ; ; - - # - : : * : ; * * * : # # ; % # # # # # # : : - # * # ; ^ 9 + # # # # - # ; * # # - = # ; , ( # e 6 ( ( , e ; # # ^ | # | # , # ; # # # # # ; : , ( - , ; # ; # # = * , 6 : , | # 8 - # # , , # + : : : : T T T T T T T T /.(. .>. . . .>. .>. .>. . .>. . .>.>. . .>.>. . .>.>. . . .0. .T T T T T T T T ",
+" ( : , , , # # : 6 - # # # ; * # # = - # : : : - ; # , ; # ( 6 ; , ( ( , , : e , : - 6 # ; ; # - , ( : ; # # ; # # ; , % # : # # , # - ( , # # = ; # % # : # # 7 % ; # # e # | # ; # - - # # ; # % + ; * # + # g * 1 e - % = # : : , : # 1 # - ; : , # ; : # # = # # # # # # # : ; : - - # # - : : : : : * * # e 6 - # # # # * ; # = : - = - = = # ; 6 , % + : , , ( ( ; ; - # ; # , , # | ; ; # # % % # ; ; ( , , , ( # # # # # 6 6 ! - # # , # - > , # , ; % ; ! : : T T T T T T T T _. . . . . .>.>. .>.>. .>.>. . .>. . . .>. . . .>. . .>. . . . .T T T T T T T T ",
+" 6 - = - , : - - : e : ; ; # - # # # ; ; ; # : - # # * * : ; : ( e ; : ; 6 # : , - 6 6 ( ; # - - ( , , # - - , ; # * ; - - , e ; ; - - - ; - # ^ - ^ ^ + # * # # # + # : - : = # # % ; - ; ; # * # ; ; : 6 : - * * % * % = . % = % # : # # # # # # # # - ; , e # # * * * # # - # % : # # - # + ; # : * - # : # ; : : 6 ; % # # # ; ; # ^ 9 % # ; = % % - , ; 9 # # e 6 ( , # ; - : ; # # $ # # ( ; # # # - # - - # e e ( , # # ; # # # e 6 : # # } $ ; # # 6 , , # g ( : : : : T T T T T T T T <. . .>. . .>.>. .>. .>. . . . . . .>. .>.>. .>. . . . . .>. . .a.T T T T T T T T ",
+" ; # # ; # : * = % # 6 ; # # ; # : * + # : - # . # # * ; # - - 6 - : ; : : , # e - # 6 6 e - - - # - - , , # , ; : - : : # - 6 # # - # , $ ; # # # # # # # 9 # # + # ^ ; # - * : - - # ; ; % + * * 7 # : ; ; % # # - * = = * ; # ; : : # , # ; # # # , - - - # # # # - ; * * + % - # - - # # - - # # ( # : - - - # - ; = % = : ; ; : % # 8 , , e # - : - # ; # # # 6 , # ; ; - - ; # - # # ^ # # g # # - # : : ; # # : : ; # - : ; ; ; e , : % $ , , 6 : ( < # # ^ - - : : : : T T T T T T T b. . .>. . .>.>. .>. . . . .>. .>. . . . . . .>. . . . . .>. . .c.T T T T T T T T ",
+" } : # # ; # # * * # * : # # , # # # # - - # : * = # % * ; ( , ( 6 : # - % ; 6 - 6 e : ( ( , ; : - # # - # # # , 6 : # # : # : : - = # # , # # # : , # # | # # ; ; % : # # # # ; ; # | # - - # # - # # * * = ; # % # % * = # ; + # e # : - , ; ; ; # # e ( v # # ^ # ; ; : 6 + * # = % : : % # % : + ; : : 6 : : ; # - % : + * : # # - % : - - # , # - - # - : # # # ( , # - : * % # # = = - ; # { - # , # # ; : - # # # ; * % % : - # , e , - % # , 6 6 e , | % e % g , | : : : : : T T T T T T T T d.>. . .>. . .>. .>.>.>. .>. .>. . . .>. .>. . . . . . .1. . .%.T T T T T T T T ",
+" 6 ( : ; # # ; # # # # % # - # # - # : # % + * # * = , 6 # : e : 6 - 6 # - # # : e - - 6 - # , - # # ; # # ; : : , 6 # # - : - - : , : ; , , - # , # e # # # # # # # ^ ; # : * - : ; # # - # % % # : - - ; % + : * * * : # # ; = % - > ; : : , # # # ; ; - ( ( , # # * # # ( , # # # : ; - ; * * : # - - - # ; 6 : 6 - # # - # # # ; - | ^ 9 + # * ( ( - # , ( - : # - ( e ; ; 6 ; % % # , , : : # , , - # # ^ # ; # # - # # : : # # = - - , e , # # , , ( ( 6 # , 7 e # # 6 - > : : : : : : T T T T T T T e.f.>. .>.>. .>. . . . .>. .>. .>. .>.>.>. . . . .>. . . .1. . .T T T T T T T T ",
+" , , # # # , # # * # - - # : ; ; ; : ; # # # * - . # e - 6 # ( 6 6 # e ( , # ( ( : = - e : : ; - , # ; # v # ( ( , e # - - : * = % ; 6 # # , - # ; # # - # - # 9 ; # # 9 ; - = = - - 6 , # ; * * : * : # - # # - - # # : # : # : : : = : 6 # - # # , # # ( ( # - , * ; - , # # # - # # - # # # : # # # - # # : : 6 ; * * = ; # # ; = % # # # # ^ 9 + ^ ; e ( - # - - ( ; ^ ; 6 , % * 6 , , # = ; ; # , , , $ ^ # : # # - - : : : ; # = - # , e : , , - # , 6 6 ( , # $ , > # ^ # ( - - , ; : : T T T T T T T T . . . .>. .>.>. . .>. .>. .>. .>. .>. . . .>.>. .>. .>. . . .T T T T T T T T T ",
+" ! - - - # # , ; : - : : - # ; # # # + : # # , : = = # : # ; - # # : ; e ( : : : e - : 6 6 # # , : # # * # | - 6 6 - # : - # # * * % * : * # 6 , # ; ; * 9 % - - # ; # # # % % % ; # , : 6 ; # # # - # ^ # # % # % * + # * - + g. : : : : : # # # , ( ( # # , e , - % + ; % * - e , # : * ; # # , : # - # ; ; # . . : 6 : # - - # = = ; # - # # ; ; # 8 ; # - - # # # - - # - # ( , # # : 6 # % # # # # , , e , , # # ; - : - - - % # # # : 6 , # # # - - # # , 6 # # ^ $ , ; ; # 8 # # # $ # = T T T T T T T T . . . .>. .>. . .>.>. .>. .>.>.>. . . . . .>. .>.>. . . . .h.T T T T T T T T ",
+" ! # e # # # , # : # # : - - - - # # = ; 6 : : ; ; : , # e # : + * + - ( ; 6 6 ( - e ( : 6 % = ; # : : % # # # 6 ( : ; * # # # + # # % , # # ; # = # # # : : : ; # - # # = # # # ; # % - # - % # - % * ; : = * * : # - # * - # | B : : : : # ; , ( ( - # # # , , # % # ( ( ! * : ; # , , - * - # : : # % # # * = + ; : : ( , ( ; : # # % ; # # - - # - # 1 % = ^ ; ; ; - # - , , ; ; : ; # ; # | | # # - ( , , # $ e - ! ; % - # # : 6 # # - # # # ; # # % # # # - + # , , # , - - ( ( | # 1 0 T T T T T T T T . . . .>. .>. .>.>.>. . .>. .>. . .>. .>. .>. . .>. . .d.T T T T T T T T T ",
+" , ; # , , # # , 6 # # - # ; = = # # # # % = # - # ; : # , : : ; - # - # # - ; : - - ; - 6 - - > # # - , # , , : : ; # - : , ; # - : - - # 6 # ; - ^ # , , - # * * = * # % - # - = # : - # # # # ; : ; 0 - # : # # : # - % * - - % # $ : : : : ( , : # - 6 ; # # , e ; 6 , , : + + * % , ( ; + ; - - - ; * * = : - - - - : e : 6 : : # * * : # - # # % # # : # = * - ( # ; ^ , e ; # - # : # # # . . ; ; , , ( : ; ; ( } # # | : # : : ; - - # # ; # # # # % ; # - - : ( # # # # % % 1 8 : - % T T T T T T T T b. .>.>. . . .>. .>. .>.>. .>. . .>.>. .>. .>.>.>. . .0.a.T T T T T T T T ",
+" # ; # | - ( ( , e # - - # % 0 = # + + * : ; 6 ; - # : ; : - - e , # = = , : # e # ; , - e , ; # # = - , ; : # # # # # - # , ; : - : : ( # , , , e , # - - - # # # # ; # = + # + ; # : : ; ; * : : ; + % # - # # , : - # # # # - - ; # $ : : : # : # # , e e : # - ( 6 e , e # # # # * : - ( # 1 - , = # # # ; # - ; - - ; : 6 6 # : , # ; # - - - * * * # , # # # ; # , : # , , ; - # - # # ; : - ( : , # - # # # - # ( e > , # + : : : : # - - ; # # # # ^ % = # # ; # # % + # # - # # ^ 8 = - T T T T T T T T d. . .>. . .>.>. .>. . .>. . . . .>.>. .>. .>. .>.>. .i.c.T T T T T T T ",
+" , # # # | # 6 # # ; : - - # | * - ; : * % ; - - # # - ; : : ; ; : : * + - - , e e ( , 6 ( # : # : : % # , 6 # # # # # = - # , : : # # : e : # # - 6 e # - # , B ; # = ; # # # : # # < - # - - - : : - = + % # - - # : ; # # ; , # - = # , [ # : : : : # ; e e ( # - : : , # # # ; # , , $ j.+ % # # , , = # : - # * * = * - ; # : # # 6 ( ; = - # # # # # # - # - # # # = * % # # # # # # # # # ; : 6 ( - # ; # , , , = * , e , ! b + - - : : : - - : ; # # # # # # ; * * # # # # ; # # # # # k.% T T T T T T T <. . .>.>. .>. . . . .>. . . . . .>.>. . .>. .>. . . .l.%.T T T T T T T ",
+" # : : - - # # 6 ; # # * ; # # % - # # - # # - # # # # # ; # ; - - # # # # ; - ; ; # : - ; # ( : # ^ ; # # # # , # # # # # : : , ( # # - : # # ; # : : : - # # , : # ; = # # % % * : ^ 9 # # - - % # : * - - - # - % = ( , ; ; = : : * = # - 1 # : : : : : ; ; , , - # : : # # - # , 6 # # # # % * : % ; - - - : * : = + # # - * ; : # : ; # * ; } e ; # # ; : : ; ; # # - # | % - | # # # # % % # ; - , , , , ( , , # # # 6 6 , # # , } : : : : : : : : : % # # # ; ; - = * ; ; : = # # % * # # $ | T T T T T T T m. . .>. .>.>. . . .>.>. .>. .>. .>.>. . . . .>.>.1. .n.T T T T T T T ",
+" | - , # # , # : ; # - : - - % - - # ; : ; ; : ; # - + : : ; # # # ; # % # - e : : # ; - # ; ; - - - * # # * ; # # # # # # , ( , e ; - - = * # : - - : e : ; e , # # # # # # - # 9 # - # # * * ; * : - + % % % % # # # : ; , # - # e : % # = = - : # - - : ; # # # - - - # # # - ( # = ; # = # # : # # # # , : # # : ; ; : : % , : * # , : + + , w # + + * ; : # # * = : : ; % * - ( # | # # - # - # # e e ( , # # ; e # # # 6 : ; 6 { < : : : : : : : : % # # # ; ; # # # # # - # # % = : : : , # T T T T T T T T m. . .>. . .>. .>.>. . . .>. .>. . .>. .>. . .>. . .d.T T T T T T T T ",
+" | = - , ; 6 ; # # # # - - # # # # # * ; ; : # # # # : * : ; - # # # - * * * ; ; ( , e , 6 , $ ; # # - % # # # , # # # # # # 6 : # ; : - # ^ ; - : * = % # 6 e , # # # : ; # # # # # ^ ; # # - + = ^ 9 + - # % 0 # = - * = + # - , # ; + : : # , # { | # # - : ; # - # # - ; ; : ; # % % # ; * # # : $ # - ^ , 6 , , # # - - ; % ; - : # ; - - - ; 9 + # # # # ; - - : : * # # - # ; # # g # # # - : : ; ; : : : ; # - : e ; # # , : % , , B 7 : : : : : : : : : # # % ; ; - # # # ; : = ; % # - - $ T T T T T T T T o. . .>. . .>. . . . . . .>. .>. . .>. . .>. . .p.p.h.T T T T T T T ",
+" - % , , , ( # , # # = - - # # # - % ; - - # # : # # # ; ; : ; # # - ; ; # % # # - # : - # ; : + * ; # + * e # ( e ; # ; # # 6 : ; ( * : # # ; # # * * # * : # , - # : # - # # # # # * : ^ * # * * - # = = # % % % = * # # # + * # e ( # : * # ; - # $ # ( 6 : # : e # # ; ; : : # # # # # % - ( e e - 6 ; # : ; , , - - - : ; # # , : : 6 : # ; * = # # * - : ; : + ; : # % = , # # # * : , # # # : - # # # - * # ; : - # # e 6 # % # , e # e 6 : : : : : : : : # ; ; # - # g # : # ^ # = % - | T T T T T T T T q.m. . .>. . . . .>.>.>.>. .>. . .>.>. . . . .r.s.T T T T T T T T ",
+" # ; , # # , - # - # # # # - # - # - ; # # - # # - # ; e 6 - # ; # - : ; ; ; ; ; : ; : = : ; - - # # ; # # : 6 6 6 ( e e # , : : # # - : : ; % # ; # # # $ % # # 6 : # # # # - # - # ^ # # # ; - - - - + % = # % + % % ; % # # # - ( # = - # # # # # * : - - 6 : , , ( # ; ; # : : # # # - # # ( ( e - - e t., # * + ; ( # # - # * # # : 6 - ( ; - * + * + % : # # ( # # - : : ; 6 : ; ; ^ ^ + # # ; # # - # # : : ; ; = - , , e e : # , , ( , 6 # , 7 : : : : : : : : ; ; # % - - : # ; : = % | T T T T T T T T T o.u. . . . .>. .>. . . . .>. . .>.1. . .v.s.T T T T T T T T T ",
+" . - * , | # # # # # # # , , , e ; - ; , ; ; - # ; : % ; : 6 - # # ; : - - - # # # : : # ; # # # ^ # ; e e # # # # - - - e ( , ; # # # - , # : - , ; # * # - - # # 6 , # ; - % ; % # # # # # : * * # ; : - - # - * - + % # + * * : % * : - : + ; - : - # # ; = , , ( : : # # : - - ; % = # # ( , e ; : ; , B ; , # - # , ( B 6 6 , , : , : - # * - - # # * ; 6 6 e : # # # : - - # - # ; # e , $ ^ | # # : : : - # # = # # , e : , , # , ( 6 6 ( , ! 8 : : : : : : : - = - = # ; ; - 7 # T T T T T T T T T q.e. . . . .>. . . . .>. .>. . .>.1. .w.T T T T T T T T T T T ",
+" - - - # # , , # # , # # ; ( # # # : ; # ; # * : : : * ; # , 6 # ; : 6 , # - # # # - # ; : - # # # # ; , ( ! # # # # # # # , # # , % = - - - - ; , ; : - : : ( # # # # - # ; # : : # e : # ; - - % * # ; % - % - : : - - ; e # # : : ^ 9 + - - * : # : * - 1 > = : : - ; ; ; - : : - # # 6 6 , ; e e : ; , , , , # ; , # , 6 : : e : e - ; # - # # # # # - # # : # - # # # - # # ; : = * ; - # # # : # : # # - % # ; ; : 6 , # # # # - ; # , 6 # # f ^ } : : : : : : ; % - # # = : # # x.T T T T T T T T T T y.1. . . . .>. . . . . .>. . .z.d.T T T T T T T T T T ",
+" = ; # # * e 6 ( e , e ; ; , ( : ; , ; - # # # ; - - # e 6 - : # - # = - e ; - : : - - # : - : # ; ; : 6 ( e ( e ( e # - : # : # # ( # # # : * # # , : : # # : : 6 ; - - # # , # - # : : ; : 9 + # * ; : ; # : ; : ; ; % % + : # # : ; # ^ 9 + # : ; % + % ; - e $ A.B. - - ; ; # # - : - # # 6 6 ; ; # # # # 6 e , , # ( : * : # - # e : : e 6 ; : % 9 + ; - : # * # - : % # # * % = # , : # : : # , # e % % - - ^ # : 6 : # - , # # ; # ; # # # # - + ; , , , , : : : : - # # * # # T T T T T T T T T T T C.D. .>. .>. . . . .1. . . .e.E.T T T T T T 4.T T ",
+" > # ; ; # : 6 6 6 ( e e # # ( , , , e , 6 , # # - - : - # 6 ; 6 - - * % ; # - - # - % ; : : ; # ; # : ; , ( e 6 # - ( # : 6 e # # - # # # # ; ; : : , 6 # # - 6 # # , , e ; ; - - - % % : - - # # ; = ; # , : # # # # % * * * # ; * # , : + # # ^ 9 + # - ; - - % * = | - - ; : - - - - - # # - : # ; - # # # 6 ; ; e : ( , ( : ( , v e # : : : 6 - - % # ; ; # : : ; * ; = : * ; | % ; # - - # : # ( , ( # # # | # # - : ; - , ( # ; # # : # % # # | # : ( , # # # e x : : : : # ; = | T T T T T T T T T T T T T F.m. . . . . . . . . .v.s.T T T T T T T T T T ",
+" # ; e , , # , # - # - e ( e , # , , : - # # - # - % # : * # - ( # : # > # # ; - - # - - - , * * # ; ; # , ! # # # - : - : , # , , - # # ; # 1 # ( ( , e # - ( e : # # ; 6 # # - # , 9 % - % + = - : # - 6 ; - - - ; = + # # % # + * # ; - - # # ; # ; ; # : * * - * = , # $ - - - - - : # # - : e 6 : # # , e ; ; : : , , # : e : 6 # ; : - : 6 ; + , # ; e # : e 6 ( # : - # # ( # # - ; : ; # , - - ; - , , # | # ; # # # - - ; ; # # ; # % = # # # # ! % + ; ; # , w : : : : # - T T T T T T T T T T T T T T o.u.z.G.u.z.G.z.G.w.T T T T T T T T T T T ",
+" # ; e , , , , ; # # # # # # * - ; ; = : : ; : ; : * * # : # # ; # # : % # # : # - * * - - # * ; e # - # , : # # # # # # e - ( , , : ; # # = . # 6 : - ; : - - ^ # # # # # | = # # # # - # # ; : * # # . # : - , # # * = # = = : - # # : - , # ; ; # = * : - # : * * - - : [ h - - : : # # - : 6 6 : # # ( e , # # : 6 e ; # ( e 6 : : - ; # # 6 6 # e ( ( ; - : ; # # - # - # # # # - 6 e 6 - ( ; * ; - # # # # ; # ; # # # - : ; # # # # # # ; * * 6 e , ; ; # # # , ! 7 : : : T T T T T T T T T T T T T T T q.e.H.I.T q.e.H.I.T T T T T T T T ",
+" # : 6 e e ( e ( e # - : # # ^ # , # # - # # - # - - # # - - - - : # # - * : - # # : - # - # % * # - - # # e + % 9 ; # : # e 6 6 ( e e # # = = # # 6 : : : * - - * # - - - # e : # ; # : # # 7 # % + ; * * + * * ; ; - * + * * ; 9 + # # + ; - - - * : - ^ 9 * : - - % * : ; $ # 8 | } - : : # # : 6 6 e # - 6 ( ( # # # # : , e : : : : e ; : ; # # e e ( 6 # - # - ; = : # # ; : ; - - # ; : : 6 6 e 6 ( 6 , , ( # # , , B ^ # # # : : % # # # # ; - = * ; e ; # # # % * 6 e $ 7 T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T ",
+" : : ; , ( e 6 # # ( ( : , # # % * # - - ( # # - # - - # # - , * : : % ; # ^ 9 + # - # # - , e ; # # ; e # # , # # ; , - - # , , , , - e ( , % # , : : ; # - : # # ; # ; * - # : ( # # , e # # # # # # : 6 : e # # , # * - - + + : # # = = # - ; - 6 ^ 9 # # # ^ 9 + # * ; : , : , = = , , 7 ; : - # - : ; ; # ( : : : : : : : : : : ; - - # , e $ 6 # B e - , - # : : : - # # - : : ; : # # - # # - - 6 : # ; : : e # ; % ; ( # # e # | # # % # # - # # - # | # # - # - # # : : : ; $ : , T T T T T T T T T T T T T T T T T T T T T 4.T T T T T T T T T ",
+" # ; # , ! # # # # : - : e - # # # # : * : + # ; + - - # # : + # # : * : - - # : : - # ; : # ; : # ; ; e : # ( e 6 6 e ( 6 # # ; # # # # # , ; ( ; ; # # # - - # # ; # # * % : : - = ! # # = - # # - , # # - ; - - : ; : - , # # : * * : ( - > - ; - # , = = : - # # ; : : : # : 6 # - , % | _ - - ; ; ; - : : ; ; - - % % # # # ( - - - ; # # # , , , 6 ( # # # , ; # # - ; : : : - # # - # ; # . . # 6 ; # - - e : 6 : # # # # | # ! # - - # # # % # ; # # # # ; : = # % - - # ; ; : e , # T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T ",
+" e # - , , : # # , # ; # ; # > # e # - - # * # # + * + + : * # # # # # - - - # # # - # : : e - ; # , e ( - : # # # ; : : # ( e ( e # - : % # , , 6 ; # # % = - - # # # ; # # # * % # * % # # # | # # ; # # ; : : * * # , : * ; + - - - % : - % % # : * * - # ; # - = e : : # % : 6 : ; e # - # $ 7 : : ; : : : # ; ; - = = + # # - ( , e , # : : # ; # ( ! e - # ; ; = - - # - # # : # # ; : ; # * = # ; : : ( , ( 6 6 6 : # # # # # # # # # - # # ^ # ; ; # # # 9 , : # ^ # = # # ; - | ( e # # # T T T T T T T T T T T T T T T T T T T T T T T T T T T T ",
+" # - - # e + # ^ # # : # # # % # : * * - % ; # # # # - - % = * + = # ; - ; : + ; # - # : - , # - ( : ; - # - % # # 9 - - , , # # ( # : e # # : ; : : - - # # # # # # , # # : # # - # ; : - : = # # % : # # ; ; - ; % ; ; # # # # : # : # e # % * : - - % * * + - = % , ( , e - % # # , # ; : , e ; # # : : : : - # ; * - - - - - - , ( # e , ( , , e ; # # # ! : - # , % ; # ; - ; : * * = # - ; # * = , 6 # : e : 6 - . , 6 ; : ; - ^ # ^ # # # ; # ^ # : ; ; # - - ( : # # : = # # ; # # 1 , e # # ; T T T T T T T T T T T T T T T T T 4.T T T T T T ",
+" - ; e ; # , # # # - - - # # ^ ; # - - % * * : # ; # # = * # : ; * = , 6 # # ( # # ; * * # , + # # , : # , # # + ; # # % - # # # # : - : e # % : ; # ; - # # # # # - # , ; # # # # + % ^ ; - = * : - # * - # - = = - - - ( - * - : 6 6 6 e e ( , ^ 9 + # * ; : - * ( # ; ; # # * : # # , # # ! , ; ; ; ; # * : : ; # - : : = - = - # ; 6 , # + : e 6 6 , # ; - # , e e # # ; % ; ; # ; - ; # # ; # - - - # : : 6 # ( 6 6 = ^ ; : : ( # : + + # # - | # # | ^ ^ ; ; - = - = ; # , - , # , # # # # - - - - - : # @.T T T T T T T T T T T T T T T T T T T T T ",
+" ; ; 6 : # ( , 6 6 # ( 6 : # ; # ^ + # * ; : % ; = * ; # # - # # = # # : 6 6 e : : 6 # - # ; * - , , ; ; # # # * # - # # , ; # , , # # # - - # , # # ; - # # # : % , # , 8 # * # : = * # # # # ; ; # | # : + # ; # - # # - ; * ; # - # # ; # : - - # # ; # = : # % : ; # = * * ^ 9 + - | * # , ( # , # ; ; # , e f % + - # - : = % % # , # g % ; e 6 , 6 ; % - : ; # = % 8 ; # ; # # - : : # : : - : % ; - % # : - # # : ; , 6 # : e - # # # % # # # , ( # - 8 9 # # ; % - - # = : # # # % # # # # # - # - ; ; # # J.T T T T T T T T T T T T T T T ",
+" , e , # : # - # ; : : # 6 : ; - , - # # % # : * : # # - ( , - # + * * : - # # : 6 ( ( e 6 e * # , e ; ; ; , : # = : - * : ; # ^ ; - : # # % * e : ( e ; ; ; ; # ; ; e : , , % * - - # ; # # * # : # # = - # # # ; ; : % # : # # % * : : : ; # ; - # # ( : ; 9 # : ; # , : # * ; - # , # > - e ( - - # # , # # ( , , e ^ ^ g = - # # - : : # ; ; # # 6 , # # : ; # ; # - = % # ; = * # # # # ; : : - : * : # - # : : + * + - ( - 6 # ( - + : ; # - . # e # # # - # # # ^ # - # # * ; ; $ = # ; - # - ( , , , # : : ^ # T T T T T T T T T T T T T T T ",
+" ( : ; , # - % - # 9 # - # # # - , : : + + : ( e # # - # - # 6 - - # # ^ 9 + # * # - ; , 6 e e ; # e e e , e # # - = = # = # , # ; , - - # # ; ; e 6 6 ( e e # # # 1 # ( ( } , # # # : # 9 ; # = = - # # * : - - - ; ; : * : : - # - - - # - ; - , 6 , , 6 e 6 ; # # , # ; - = % ; - # # # # | , # # # , ( 6 # # , e , - % ^ . : , # # # - # - : : # # ( , # - : # # # # = = : # # # # # # ; - : ; # # # # # # : ; # # - # - # : # ; - # ; - - ; : = = # : ; # # # # # ; # - - # ; = # # - - # % = : ( ( # e , ( ! , e ; ; # T T T T T T T T T T T T T T T ",
+" , : # , # # + - # # % - - - # # : : - - * : : - # - # % ; , ; # ; : % : ; : : : ; * ; ; : e ; # e ; - | , 6 ( , # # : * % ( e 6 6 ; ( 6 e , # # # ; - - - e , : = | ^ 6 ^ # 6 - - ; # # # # = + ; # # # e ; # # ; : - # # # # : + ; # # ; # - : - # : : : : 6 6 - % ; - - - * # - * ; # ; # > # # ; , , ( - # # # , , # + , } # = 0 | , - # , ( - : # - , e ; ; 6 ; # # # , , : : # # ; - % - 1 # ( $ # , ; : * # * # # * * - ; # - # : + # # # - - + * * # # - # # , ^ ^ 9 * % ^ # # # 8 = - > = # ; 6 , % + : ( e , 6 ; ; - # , T T T T T T T T T T T T T T T T ",
+" , , ; , # # # * # # - # , e - - # ; # # - # # # # - - : * : : : 6 - , # ; : # : e 6 ( 6 ; ; ; : e ( : - # # , 6 e e , ; * # $ # # - : # e e e , # # ; ; # # # # = = , # 6 ; # : * # * # # = # # # ; # * - - # # - - # - # # # - ; - - ; ; : ; : = : : - - # # : 6 ; ; # - , # ^ 9 + # $ , , ( % # * 6 e : - - 6 ; # # , w ; 6 ! } , # # % * 8 , 6 , - # - - ( ; e - 6 , % * e , , # = ; ; , # # ; + - ! ( ; 6 * ; - # : * * : : 6 : ( # : ; - : # ; - # # # # * : * e e e # # ; ; ; - # # % % , # = % % : , # g # # e 6 , ( ; + - : ; , # J.T T T T T T T T T T T T T T T ",
+" , ; $ # ; - # = : - * : , = % : : : ; # : + ; : ; + # # # ; ; ; e 6 # e ( ( : - : ; # : ; - : ; : : : - , # # 6 6 ; e # # # - # 9 # - # , ( e ( e ( e # - : - , % # , - 6 # + * : = * # # - - - = # - = # # - - - # # - - - # - # # - # # : : # e # # - # - # # - - 6 - * ; ; - # # # # : # # # * * - # # # $ e e : # # ( , , , e , $ # 8 % + : - # # # - - # - - ( , # # : 6 # % - - # # # - , , ! - - ^ ( ( ( * * + # # - : : ( , % % # ( , e , 6 , , 6 , : # - # # , | - # # # # ! ^ + $ # # # # - : : ; # ; # # 6 , # ; # = # ; # # . , T T T T T T T T T T T T T T T T ",
+" e e e , ; # # # = = # = - , # : , # - - # # - : ; # - # # - # # ; 6 e ( 6 # - : # ; * # # , : # - # - , , - ; , , - ( ( - # + ; # # % - ; ( ( , 6 # - ( - : ( - , ; e ; ; , , # - ^ # ; - # + # + ; # # ; ; # # * 6 : ; # # # - - # # - # ; - - # # - % + : ; ; # . . # 6 ; # # # = - , # : - ^ ^ - * * - # ; e e ( - # : : 6 - # ; ; # $ 6 ^ > + = # # ; - - # - ( , # # : # # ; # | 1 # # - ( , , # B # % - } , # * = # + - % # , + * # # - # : - # : : : - ; , V K.; - # - # # # # - % * - * : ; # # - # - ; e # # ( , # - : - # # ! = = - , [ T T T T T T T T T T T T T T ",
+" ; - # ( 6 ( , # # ; * % % ; * # , , - : : - - - - - # # # - # % - e - e - ; # = # = = # : # 6 $ , e ; # e e e e , ; , - ; # * # # # # , , , , # # # - : - : , # , e e ; # # 6 ( - # = ; # # ; : # # 7 - # # , # - - # ; * + * * + * : # - : # : # ; : ; : + # # * = # ; : : ( , # - ( ( 6 # # ^ $ # # * # : : ; ; , , - - : : # # # # , 6 : , , | ^ ; % - - - ; # , e # # # - # # ; # . . # # - ! ( : ^ ^ 6 : * ; ; 1 - ; * # : % - - e # , : ; : = : : - # # # - # 7 L.7 * # = # e - # # # # * - # ! , # # , ( - : # # ( e # ; 6 ; % % # ! , : : | $ T T T T T T T T T T T T T T ",
+" , : - - # # 6 e # # ; * ^ B * # # # - # - # + # - - - : : % ; # ; : 6 ( ; , ( + * * = = # - * - 6 : 6 ( , # - , 6 6 ; ; : - # = # - * : ( # , : # # # : # # ; # # - e : : # 6 6 # , # % # # % % * # # - - # # # # # # - : ; * % # # , ; # # # - # - # # : ; + % # * = , 6 # : e : , ( - - 6 ^ - + : # : - # - - : ; # # # # # - # # - # , - = ; $ # # ; ^ g # # % # , e ; - # - # # ; : - ( : ; # # # # # - , e # # , * - - # # # : * * - # $ : : # ; + * % * # # # - # 7 ] # - # : 6 * + - # ; # - # # # # ; 6 ( - # - - , ; ; # 6 , % * , , , % = ; ; , , T T T T T T T T T T T T T T ",
+" : : : ! # # # # 6 ; ; # - - # * # - # - # ; ; # - : - , * : * : ; - - , e - ; : # # # # = # # # # , - ( ( 6 e ; e e e : ; # # # - - # = - ( - e # # 9 ; : : # ; # % : ; # ; , 6 , # , , # # # # # 9 # + ; # ; ; * : ; # - # # # ; : : : : ; # # = % * % % = # ; # - . # e - 6 # ( 6 : e : * ; # # | = ^ 9 + # # # - : ; # # # # # 9 ; : : # - % # ; * # ! # % g # , ; # # # # ^ ^ ; : 6 ( - # ; ; # # , = * , , # # 1 # # * # # ( - % * * + ; , ; # * : : # - ; # . = # + % + : - # : ; : * ; * : - # % % * 6 # | # # - - # - - ( , # ; : 6 # % = # # # | ; ( T T T T T T T T T T T T T T T ",
+" - - # , - - : # - ( ( - % = - # ; : ; ; : - # # - # : + # # # # : * * # , : % # : - - # # # # : # * + ; # , 6 , # - # : e , , , # * * % , - ; , # # $ - - - - - - # # # # ; 6 ( # # # 6 , , # - # # ^ ; ; # # ! + # # : ; # # ; # ; ; # - ; = * : ; * : : ; 6 : : = = # : # ; - # - 6 6 # # - - | # # # # - | ^ # ( 6 : # : , # # ; # # * # # # # # # - ( , $ # % # # # # # % % # ; # ! , , , ( # # ; # # 6 6 , # # , # # - > , # ; ; : - * - , ; # : ; # # # * = = , e ; : # # - ; - # # - - % : # # - - - = ^ # # : - # - , , ; ; # # : ; # | | # ; - ( , , , T T T T T T T T T T T T T T T ",
+" , e ; # # # e e # ; # - e ; - # : : ; : ; = ; # - = * # : # ; ; # . - % * : : # ( , , # # , # - # # - ; # # # , , # # # # # x e # # ; * ; $ : # ( , 6 6 ; ( 6 # + * , : ( , # , e # # 6 6 ; # # # # # # # ; # # | # # , # # # * - : : # # - # : # # # ; # ; - - - # ; : , # e # - # : # : ; : : + - * - # % # # # $ , , # , , ( # # # # : ; # # # # # # ( ( e - , } # % , ; ; % - - # - # , e e ( , # # ; , # # # 6 : # # , 7 + % # 6 ; ; : # % = * ; # # , : ; % - = # ; e } ( # # - - ( # , ; # # - * # e # # # # : $ $ ; # , e ; # # # - # # # . . # % - , ( : # $ T T T T T T T T T T T T T ",
+" - 6 , , # # # 6 6 ; ; : e , : # : : - # - - - = + # - # # - # # * = # * ; ; : 6 : 6 # # : 6 ; - # # ; # # % # , # - # # # # ( 6 # # # ; ( # : # : e ; : : # e ; + : ( 6 , , # # , # # e , # # # - # $ , # ^ # # # # ; - - # * ; ; # # ; * + ; # # , # , # - # # - # ; : # # : : + : : * + # ; # - ; ^ 9 % % # # | # % # # # , ( : : # # # - - # # = # # , , ; ; : , } j. ^ ; f * # # | : : ; # - : : ; # - : , # # , ( : % , # # * : - , # ; + * ; # - # # ; - - # # - * * * - ( # ; : : : ; 6 : 6 * ; , # e 6 ; # # # # ( : , e , ; - # - # # ; : - ( : ; ; # - $ # - ( x e T T T T T T T T T T T T T ",
+" - - ( , 6 e # ; ; 6 : ; ; - ; - : + # # ; - - , : - # : : # - # - ; % % ; ; ; ; e ; : e # # : - , , # # , # - # ( # # : : : ; # # ( ( - # ; # - % # # 9 % - # , e : , # ; - : * , 6 , # ; # - # % # # # - # # ; - ; : - - - ( * ; - - # * # # : : : : * ; # # # : + : : : ; - - # ( ; - # - % ; * , ( ; * % = = # # ; * : - # # # # - ; ; # # # # # # # 6 6 , ; # , : ; w w 7 ^ % # , # # - : - # # , ( * # # : - # # , , - % # , , , ^ + # * e ; f 6 - # + # : - , # ; : * = # ; : ; , - # : ; ; - # : : : * : : : ; ; # # , # # e # # # # # # # ; : 6 ( - ; ; # # # # = * ( , v T T M.N.T T T T T T T T T T ",
+" * + # # # 6 , - # # : # , # : - # 6 ; * = * : ; - : : # - - # - * - # # - - # ; : # 6 6 , , # # # 6 : # = # ; , e # e : ; e e # # $ - : # ; # # + # # # % - ; 6 6 , # # # ; 6 # # , , # # # # : : # , # # # # ; # : : - = # # ; + + # # ; : - % # ; # ; # + * # - # - # # # - - # # e , # - ; ; * ; : ; # = = - - ^ # # , % % ; % - # ; ; # # # - - # # 6 6 ; # - - # # 6 e w { # % v # # # - # # : - ; ; = - , , e , ( # , , ( ( , , , 7 : ; # , # # : # # + ; ; f * * # # ; = + # - # w w e : # # # # - - - # # - ; - # # ; # , ; e # # % % # # # ( , , , ( # # # # ; 6 6 { | , T T T N.T T T T T T T ",
+" # - # , # # , # # # # # # x # # # # ( - = + # ; : : % ; # % ; : : % # % # % # : - * # : : e # # # # e 6 # # # # - 6 , # - ( # 6 6 , ; : ; # : # # * ; = - # , e 6 , , e ( , - : 6 , # # # # , # - # # # # - # 7 # # * : % * % # ; # | ; # = : % ; ; = * # ; e # # - - = # * * : ; * : : : : 6 : ; # - # # # % # # # - # , ; e , # : | # # ; # - - - # ^ # # # ; ; - # # # , ; ; e , } 1 | = | - 6 , : : - e # = # , , e : , ( ( # , 6 6 ( , # ^ , , $ ^ % # : = = - # , , - # = = ; # + * = , w # ; # # # % ; : : - # ; * e # # # : 6 } ; # % # - # - - ^ e e ( , # # ; # # # # 6 : # , O.T T T U U N.T T T ",
+" # # ; # # # - # # ( , # - : ; * # , 6 , ; : # - - - ; % % + - - - ; % % * * * * # : : ; # # # # 6 , # ( , # # e , - ( # # e - # # e : ; ; # # ; : # = : - * : e 6 ( e 6 # - ( 6 ; e , e e # e - - - # - # # # - # : # # # % # : - : = - % # ; : : 6 # - # # ; ; ; # : * : # * ; # - ; # # # # e ( # : - # - # # - # # - * - # ; # # ! # # - # - # - - : # # # # , 6 : # # ( , # # : : $ P.7 , , * : # ; # # - % # ; # : 6 , , # # # # # , , 6 # # ^ $ , e # - s : # | ; : ; : : # + + # ; e # # : ; ; # ^ * : * : # - : : ; # # , # , , # # ; 9 * - # - : : ; # # : : ; # - : # # + ; ( : % # Q.R.S.T T N.T.U N. ",
+" ( # # # # # = # # - : : , : 6 ; # # : , , # - # - - % ; # # # - - - ; % # # # # # ; e 6 - $ # # # 6 ; - , # # # : # # - ( ( ( ( : # : e , e ; # # , = = # = # , , ( , # # - 6 e : # # 6 6 # # - # # 9 ; - # : 6 , # # # + % ^ # - % * : - - : 6 ( ( e 6 6 : : * ; # - # * % * ; 6 ; : # # # # ; ( e - * + : ; * * * + ; * ; # - - - ^ # # + % - # # # : # # # - 6 6 : # - ( e # - # # 6 e ; ^ , # - * * # # # , # : 6 # - - , # # ; ; # # , # # - + # , , ! | # * e e , , e 6 - # # ^ # ; ; : # # : ; # # : - # # - - # # - ; : : : : 6 ( , # # e % , - - # : - # # # # * # ; : - # # , 6 - % # w U.V.W.X.T T U M. ",
+" # 6 : # = # ; ; ; # ; : # e 6 # # - - ; - : ; - # # ; * : 6 : - # - - ; ; : ; # : + : : 6 , ; + $ , , # , # : ; # # ; + : # 7 # # # # # # , ( , , ( # # # * % - # , ; * # , 6 : # # # # : : # - # # # , - , # # , # * + : = * ^ ; # # ; ; # = # - ; , 6 e e , - - = * : * : ; # ; ( ; : ; # - * : ; : , ( - # - % % # , # # ; # : ; # ^ # , # # + * - - # # # : 6 ( , | # 6 ( ( ; # ; # - ( e # # $ # - - # : # # ; , : ; - - - # ; ; # , 6 % # # # % : ( # # # ; # # # : : : 6 - # - # - # ; = ; * # , : # # : + # # # - # # - - - # ; e 6 # e , , % # # ; : - - - # # : % # * = - # e e e # - , , ( Y.Z.`. +M.T ",
+" | e , # # # # # # # # - , : , : - ; ; - ; 6 # - ; ; - - ; - ; # ; # - - - - ; # # - # , , e : : ; 6 , # , # # ; # # # ; # # # # - , # - # # - # 6 e ; ; ; * # B : , # % 9 e - = # # : - - : e : ; # # - # , ; # , % * : # # ; ; # * - : # # : ; * ; ; : e ; : # % ; # # - # ; # : ; 6 e 6 % ; # ; # - ; : ; ; * + | # - # # : : : : ; = : - ; # , # = # # # # - : ; ; # , 6 ( # # # # # # : - - - - , $ : , , - : ; # # # # - - # # # - ( # % = # # # # # % + # ; - - - # - * - : % ; # . = = # # * # ; = 0 = # # = # # * # ; - - # # ; 6 e ( 6 # # # : - ; ^ ^ # : ; : : # # % = : # e e : , ( - # ( 6 6 .+++@+#+ ",
+" , # ( ! # ; # # # # # # ; e # , # ; ( , 6 ( ; 6 # : - ; ; - ; 6 - : # - # - - # ; ; : ; ; # : , # # ; , 8 # # # ; # # # , , - - # # : # ; # # # # , 6 - : # ; e # : , # # ; ; # # ; - 6 * = % # 6 e # - - # , ; } , # ; # : # 9 ; # = = - - : e 6 ( 6 ; # - - # * * : # # - # - - ; ; : : : * : # ; # ; ; ; : # - # ; : - : % : : : : # # ; ; # # # - - # # # # # # ; - - ( # # - = % % # # # , # # - ; # # % # 7 - : ; # # # # - : ; - - - # # , ; * * # e ; : ; # # # , , # ; # # * : * = ; ; # # # + - = = 0 = % : * : ; * = * : * + - e - e - ; # # | # # - # # : ; : - - % # # # : 6 e # # # - # ; k.w 6 # $+%+&+ ",
+" 6 ; , - = # # ; - % - - - * : 6 # # - - ; # e : e - ; # , e - 6 ; : : # - # # : : ; , # - - e # # # # , 6 : ( , , # - e , # e | # | : # $ # # - # # # ( ( - # 6 : , , 6 L 6 : , , ; , - * * # * : : , # # ^ , ; , 6 - # ; # # # # - # ; ^ # # 6 ; ; # 6 ( ; : ; # # # ( 6 : - - > # # , # ; , - # = * ; # # # # ; ; # - # # # - ; , - - # ; = * * = , 6 # # 6 - # ; : - - # ; ; # = = ; # # - ( , , , # : : # , , , : # # , # # : : % # # # # # ; = * ; , e # # # % * , e $ 8 % # # - * = # # * * ; # : ; + * * # * ; % - = + # = - ; : 6 ( ; # # * * # ; - ; 6 # # % = # # # : 6 - # # # # # ; ; - ^ ; # # - + *+=+-+ ",
+" , , , | - # # # # # + : , | + # # # : : ; - # # # 6 e 6 ( 6 e : # - - # # - # # : : - e - , $ , # ; , # 6 , : # , , , ( e $ # # # , # # # e ; , e , # ; | - e e # # # : ; e ( : # # , ; # # # # % # : e : , , # e # : - # * # # = # - # ; # # # # : * : ; ; 6 e 6 - - - # ; 6 , - # - # - # ; ; # % # - # * * * + # # * * # ; : : * # # - - # : - # # e ( e , , e : ^ 9 ; # # ; * # - - # - : ( ( # e , , , , e ; # v 6 : , , ( ^ # ; # % # # # # # # # , # # - - # # ; : : : e # # ; # - # = # # * ; = # # # * # - % * ; - ; : : ; * ; ; ; , e - * 9 ; - # , e - ( , - * % # # - - : ; - # - # ; ; ; - - % % # - * : ( ;+ ",
+" : 6 6 # # # # ; , , # ; e # = # # * * + % ; - # ; : - : : ( : - # # - - # # ; : ; ; ; , # e , } , ; e # + , ( 6 , # ( , , # # # , # ; # ; , } # # # ( ( # ; : ; - # , % = # e , # # # , # # * # - - # % ; 6 , # 6 # + * : = * # # - # - = # # % # # - ; # ; ; : : 6 ; * * # # - ; # ; : % ; # * * * * - # # * ; , ; % # * ; , 6 # : e # # # # e # : + e : 6 - # # e e e # ^ # > # ; = - = - # ; 6 , % + : ( ( ( e # ; - # # $ , , v : : # # # # # # % % # # # , # ; : = # ^ # - - # ^ ( , ^ : + * * % * ; + * = # = # : + # - - - : e 6 ( 6 ; : 6 - : % # : ; - ( ; : ; - # , , ^ 9 ; # ; # # - - # # # # ; # % = # 7 * >+V % ,+ ",
+" % ; e # ; % # ; e - # - ( 6 6 # # * ; - - ; 6 ; : + + * # ; # ; # - # # - # # ; ; - , , , 6 ( e ( # , , ; ; - , , | ; , , ^ , { , # ; ; # # 6 x v , # , # ; ; # # # # + - # - - - ( e , ; : - : : - - % # , - , , , # ( , , ; - # + # + ; # * * ; - * ; ; # - # # # 6 : # - | # - - ; : * : - # * ; # # # % * ; * ; - % * ; e - 6 # ( # - - # # - ; # ( 6 6 # , # ; e ; # # % % # # = * = % % # ( ; 9 # ; e 6 ( ( # + - : # ^ # , # - ; ; # # # ^ # ; ; e ( # ^ # : # ^ # = # , # # - # ; # # # # # # - # = % + * - # > # ; - # : : ; ; # 6 ( 6 : * # ( # , : # ( 6 e - # # - - # # ; # # # - - : ; # # # # # ; ; * * : j.'+# ; # % % ",
+" - # # e 6 : - - # # # : - # # # ; % * ; - # - - - # - - + - ; % - # # - # # - # - - - , # ; w ( ( - - # , , e - , , # # ( | ; , } , # # # ; ; # ( , ! - , : $ # , e , # * - # # # # e , , ; : # # : # # # # , e # - 6 ( - # = ; - # ; : # # 7 * ; # : ; : ; - ( ( # # 6 e , : # ; - ; # # # # - # # # - # # * # 6 ( - # # % # : - ; - # ; ; : # # : # # # # : : : * : 6 : e # , * : | - # ; # - : - # ; ; # # 6 , # # # # + ; # # # )+ , # ; # # ^ ^ # : ; ; ( # # # : # # : = # , # # # - - ; # # # % ; ; ; = ; # # # ; % # : * * # # : * : # : - # # - ; # * - # - # - ; ; # - # # , ; + # # - # : : % # # # # ; # = * ; ^ : * # # % * - # ",
+" - , , ( # - # # , , ( : ; + * # 9 e # 6 - - - - - # # # - : - - + : ; ; - # ; ; # - # # : ; # # # # , ( ( 6 e , e , # , , 7 ; , , # - # # , # # ( , # # , # ; e , # # # # ; ; # ; e 6 , , 6 # # - # * : - - , : # 6 6 : ; ; # # ; ; # * ; # : * # ( 6 e e ( 6 ; e : : , - 6 - ; : # # e ; = = % * + * * * * = : ; # # % # * : - ; : - * : : ; : : : * = : : : # - # # e ( , : - ^ 9 + # : , # # - # - ; ; # # ( , # - : # % # # = = : # # : | - - - - # # ; - = # = # # - - # # $ # , # # ; : : - ; ; - # # - # ; # # - * : - - % : : - ; - * = : : # # - + * : : * # # ; - : : : # ; : # - - % - ; % % # # # # ; # = # # # - % * % * : : : ; ",
+" | e # 8 , # # - # - 6 , - # - # - # # - 6 + + : + : % % * - # : ; - - ; : # # # # - # ^ - : , ; # : e e ( - : e , # , , # # # , , # # , , ( , # # , e , # # , e ( e # , , # # ; # 1 , ( ( , e ; - # : - : : - , - # 6 ( # # # # # # , # 9 ; # % # # ; # : 6 ; , ( ( , , : e , : - : # - - 6 # # = = ; # # # - ; ; : + + * : ^ 9 + = ; ; ; ; # : ; ; # # ; ; - # # - , # ; ( e ; # # : - # ; v | # # , ( - : : - ( e # ; 6 ; # # # , , : : , $ | # - - : 8 ^ - - ; % - # # = : # # : # , , # - - ; - ^ # ; : : - # * + * # ^ 9 + # - % - # ; - # % # # : # - - # # e : # ; # # * = # ; - # # # | e 6 6 > # # # # % ; # - # # # ; : = # # # - : ",
+" ( 6 # # # # ; # # ; , , # e , , - ; ; 6 - - - # - + + % - ; * # # # # - - ; : # - ; # : : ! - # * # ( ; ( ( 6 e ; # , , # : , , # # ; , , ( - # # # , , # ; - , , } ( , , # , - % 1 # 6 : - ; : - - ; # # # - - e 6 ( ( # # - # # # - # # B ( ; - # ; # # ( e ; : ; 6 # : , ; 6 6 6 ( # - : ; # = = # ; # # % ; - - - # # % - # = = ; # , ; # - # # ^ # ; # - # # + : * : ; - - # + 9 # # - * % ^ e 6 ( - # - # , ; ; : 6 , % * 6 e , % = ; ; $ | # ; : # # # ; e , - # # * ; ; $ # # e e # # - ; * ; * # # # : : : # = : = = # ; = # ; # : * * * : - # % # : % # # # # * + * = = * # = * # # # ( e e % # # # ^ # ; ; # - = ^ # : # ^ ; = - : : ",
+" 6 - # , , e : # , # # : # ; : # : 6 : ( : - - # - # # - - - ; : ; - # ; - - # ; # - : # ; * ^ # - * * # , , e e e : , - $ , , ! , # * , e : - - 6 ; # # , e ; 6 ( , , e e # : ( - # $ # 6 ; ; : * , ( # # - : = # # , e - # ; : ; # # # # # , ( $ ; - ; : : 6 - : ; : : , + : ; # 6 6 : e e # : - % # - # * * : # # - * + # ; # # * + + : : : ; - # # ; ; = * = % # - - - - # ; : ; ; # # ; - # % : - ( # # # # - - # - - ( , # - - 6 # % # # # # # , x # # # : e , , # # ; = - | ; ; # # , e # # ; ; : 6 # - ; # # ; % : ; : + : ; # # e - - - % ^ 9 + # * : - # # # - # - # = % ; # # % * * + ^ : e # * : # # ^ ; : ; ; - > # # : , ; : = % : : ",
+" - ; # , # 6 e 6 ( e # : ( ( - # , # e ( , : # - ; : % ; % - # ; - * ; * = * : * : # - # - # * # ^ # ; * - # , ; e ( # - # # , ( # , * * , , , , , e e : # # ( ( , , e # # , , - , % , , # : ; # - : , e ; - # + , # # # 6 # # , # , , # - ; $ ; # , e ; # ; # - : % ; % # 6 - # - - ( ( 6 e ; 6 , ; : % ; # * ; ; # - * # , # # : ; , - - - # - , 6 , , 6 # : # # # ( ( # ( # # 6 ; e ( : = - - # ^ ^ % # * ^ ; ; : - # - , e # # : # : ; # . | # $ - ( , , # ^ # : ; ; # # , ( # | ; # | ; # ; e - - # # ; # : + , - # # # # # # - = % : 6 * ; # * - # # ; ^ 9 + # - - - - # # - * - # * # - # # - # - - ^ ^ + # : ; # # ; - = # = ; ; - - # : : : ",
+" + ; , , # # e e ; # : - , # : : # # ; ( e # - - : : * ; * # # - * - - = + # - - : : # ; - * # + # # ; # # : : : ; , , # # # : ^ ^ , - * * - , # e e ( - # : : , # # # ; # , ( ( 7 ; # # # , ! # - - # # : - - % - : * ( 6 , # ; 6 ; ; % # - e e , , 6 ; ; = - * : * : # # : # ; - - - - 6 ; e 6 e - : * : % * ; : : : ; e - : ; = + # - # # % = - # * = * - # # : 6 6 : ; ; : ; , # , ( , # - # # # # # # # # # , ( # # , e ; # , : : # # # . . ; ; # ( ( : , $ # # ; - - , # # # - # % # # # : # ; - ; # ; # # - # # - - - # * ; ; : ; - # : ; # # ; ; - # - # # % = # # , # * # % = - ; # # # - - - # # # # # # * = > # # - ; % - # # = : ; : : : ",
+" # # - , ( ( - - # : ( - , , ^ # ; ; * : ; * - # # : # # = + : - - # , ; : ; ; : - - # , ; # % # # * = , $ , # - - ; # # # # - , , ^ ^ # # * # : : ; ; , $ - , : : # # e , , 6 # # , , # ; # # # - - - # - - # # | ; 6 ; # e ; # # ; e : : # 6 ; # ; # : # - * * ; # # # # 6 ; * = * : : * ; ; # ; ; - # # # # # # # # # # # - # # ; # + * # * * - + * = + ; # : ; # : ( 6 e e , , : , 6 : - ; , : ; - , e ; # # # # e * # e , ; - # , # # ; : - ( : # : - # ; # - | e # # - # | ^ ^ - - # # # - ( , , , # : : # ; # # | - ( , # ; - - = - # - ; - - - , # ; # # : : : ; 6 : : : ; : * # # ; # # = # # - + # - # # # # % * # # ; ^ ^ - # # * = ; : : : ",
+" # ; # # , 6 : # # : - # # # ; ; ; - # : : ; : + ; : : : # # # - - : , , 6 - ; ; = : : : : ; * # # # e e # # , , - : ; # , , # , , ; - , , # # - - : ; # # # ( v , # # # , e # = ; # # - , # # # # : e , - ( # : # - : e # ( # # # , , - - # 6 , ; : # 6 # : : % # # # - # ( - = + # : * ; * ; - # # # # # # # % # ; ; ; : ; + : ; ; ; e # # # - - # = % % : : e 6 - * - 6 ; ; : # : 6 # # # ; - = + # 6 ; * ; ; # = - % # ; # # # # # # $ ; : ( ( - ; ; - # # # = * ( } e # $ ; * % - - # - # , ( # e , ( ( ( e ; # # # 1 : = : 6 # ; ; * : : # - - # ( - : ; # ; # # - - # - # # * ; : ; ; # - % - # # # # # - ; ; # # # : - # # # # + ; = = # : : # ",
+" 1 ; ; ; : ( e e ; # : # # ^ - # # # # - - - # # - : - - ; # - # - - ; - : # # - - ; , - # - # # ; = # # , # ; ^ # , , : # , e , # # # - - # - # # # - : ; # , , # , , ; : # - = # # ; * # , , $ # # ^ , , , , : - ( 6 ; # , e e # ; - - - ; , - : : : # 6 - - ; % # - # ; 6 , : # ; ; # # # # - - # - * 9 e ( : * * # # : ; ; - - ; ; ; : # # : # # ; = ; - % # ; : ; : * ; ; ; : # 6 : , # # : * + * # ( - = * ; ; ; # # - - # # # # # % % # , ; ( , , , ( # # # # # 6 6 , 1 - # = - = # | ; 6 , % + : e , , 6 ; ; - # # # ( # - : % ; ; = - ; = : - ; : ; - # # - # : # ; : ; # # # - ; ; # - = # : * = - - # # - * # # # ^ # % # # + # # # : : : , - ",
+" # , , , - , 6 6 # # # # # ; # - : ; : # # : # - # # - ; - : ; ; ; - , - # ; - - # , * - : ; # = # : # # # , # # , < ( , , ( , # ! # ; e # # | ^ # , ( : # : , , # ; e # # # # # # # # - ( , , # , # % # , $ B ; 6 e : = # : 6 - # - # , e ( # - , : , - - - ; * - # - # : - # * % = * - ; = % ; : : % # , ( , # - , # # # # # # - - # % ; * # , : - # - # ; 6 : : # : - - - # : # 6 , 6 : * ; # - , 6 , ; : = * # , ; ; # # # - # ; # # - # - , e e e ( , # # ; ; # # # 6 : e # % = # = % % - 6 # ^ # ; e 6 ( , , % - : ; 9 # # | , # # # # - # ; ; : : # * : - # ; ; = * : # ; : - # # - - # - # - # # ; # # # ; - * ; - # # - : * % - # - # : : : , : ",
+" = = < , # - # : - * * : + + # , e ; : : : ; ; # - # ; : : - # , # , e 6 ; * = * : : ; ; - e 6 # ; : : : # # # # # , , , , ! ( - : # 8 ^ , , # # - $ , , ( , , ( , , , , # e , # # , # , , ( e - # , ( $ # : ; , ( - # # # # # : : # # # # ; , ; : , 6 ( ; ; : ; # - # - - ; + * + + = # - - ; - - - ; % + ; : 6 e ( , # - , - # # : # - # # * # ; - # # : # 6 : e : # = + # # , : , - : ; , : 6 : # # : , , 6 * * # - # % # ^ ; ; # ^ * - - # : : ; e : : : ; # - : # + # ; , : % 7 $ # , # - : # ; # # # # 6 , # ; - - : ; # - # # ^ ; - : : # ; ; * % - # * : : + - # : - - # ; : - # ; # - # - * # ^ # ; # # , # % * * + % | # # # ; - | ^ : : : : ( , ",
+" - : : : # , , # + * * - # - - # ; # # # # - ; : : # : : ; : : # - = : # ( - = + # # ; ; % : : 6 e , - ; # - ! , ; ; e , ( , , # # # ; ; , ( , # # | # , ( ( , , ( : : $ $ # # , , - = # , ( , e ; : - , $ , , , e - - - ^ : - - : e : ; e # # # # ( - ; , 6 6 e 6 - # ; : ; ; : # # * = , 6 # # # - - ; = - - - # 6 e , 6 e 6 # : - # % ; % # % # - - e ( : # 6 , ; - + : - # # : 6 ( ; - # e 6 - ; - - ; - : # # = = - ; ; * ( # e # : ^ $ # # - : - # # , # * # ; : - # # , 6 # % # , , , , # - # - ; - # # ( , # - : # # # , = = # # # | # : - ; # ; ; * # - - # # - - # # - # # # % % % : - % + # # ; : = * * * * : ; : - * # # : = % 9 - : * : : # 6 , ",
+" = : : : # : ; ^ # - # # - - # # # # % # # # - - # ; : : - # # - - % , 6 , ; : ; + # # # # ; # # : - - ; e : 6 - , , , , , , e ; , # ; e # , , , , , # 6 6 , # # # : - ; ; e , e # , # # 6 6 , e , e : ; , , e , ; # # ; , ; : = % # 6 # # 6 ; # - 6 : # ; 6 6 : 6 ; : : ; : # - # - . # e - 6 # # - - = * # . . # 6 : - # # # : # : + * : + - # ; : ; # 6 : , - : : ; - - - # - : ; , 6 , , : , : - ; ; - ; + : ; : ; # ; - # e 6 # ; # # # # # # - - - # # : # # % = - - e e , # # e , ( , , e 1 ^ , ( - : : # ( e # ; 6 ; # # # , , : : # ; ; # # - 1 # } ( # # - # ; : : # ; : ; , # # + % - ; ; : : ; # , - # # * ; # - # # # - * = % + # # : = : : : = ",
+" % : : : : , # - # 7 # # ^ # # % : * ; * % # ; - - : : ; ; ; ; ; e : # # : , , 6 # ; ; # # ; ; : : = : : : 6 e : * : 7 : # ! , ; ; ; # ; , ( ! - , $ $ , e 6 ; # - # # - e ; # , , , # # , 6 6 ; # , # - , 6 e , , , ! # ; # # - # # * : # # : # ( , ( 6 ; # - ( 6 , # : : - ; - # # # = # : # ; # - # # % + * = # ; # - # % # % - = # - # # : ; : : ; : # 6 6 ( ; - - - # # - # # # ; ; : : e # e - ; # , e # - - ; e 6 - 6 , , : , : - ; ; - - * : 1 # # - : : : ; % = : ; , e : , ( , # ( 6 6 , { | e , - # - # ( ; ; : 6 , % * , , , # = ; ; # # # # , # , , ; ( # : : - # : : ; : : * : # ; * e # - ; , ; # ; : : * - # # # # ; ; * ; # - * : : # : : # e ",
+" # : : : : - # # : % # # + * # # - # ; + * * = * % # : - - : - ; # ; - - ; - : # - = * # ; e - - # ; , # ; - # : e , # : e } , # # # ; ; , , ( v - , # ^ , , , e - # ! ( e ; , - - - , , , # # ; ; - , , , , ; ; e : , , , ; # # # # # # , 6 # : e : 6 - : ( , ( : : - # ( , # ; # - ; : , # e ; # # # - # = * = , 6 # # * * * ; - > # ; # * # : : : % - , - ; , 6 , , - ; ; - - # # - # # - e - 6 e 6 ( 6 , % ; # - : 6 ; : ; ; # e - ; # , e # ^ 9 + # - ; # - - % # # : : 6 e # # # , - # # , 6 # | f | # # - - # # # ( , , 6 - 6 # % # # # # # ; 6 6 ( # ; ^ ( ( ( : : ; : : : : # - - # - # ; - ; + # * = # - - # # ; # - - # # # ; # - ; : * : : : , , : !+~+ ",
+" : : : : : % + # : * : - - - # # # - # - - - + # * : - - * # ; - : ; ; ; - ; 6 - # ; = % + : # # : - ( - = = : # , , # e t., , - # ^ , # # ( , ( - ( , # , , , , , ! # ( 6 # # - - - : # , # - e 6 : # # ( 6 # , : : , , , # # * - # # # e - 6 # ( 6 6 # : e : 6 - - # : - # * : # # - # # 6 : # * * # # + + % # e # : - # # ; ; e % # : - ; # ; ; - - - 6 : # ; : : e # - = * : 6 * * # - ( 6 # # : : ( 6 ; * : # ; # # 6 : - : - 6 , , 6 : # ^ - # # - # # % - # # : 6 # # # ; # # ; # # % # # ; - + # e , ; e - # - ( e # ; : # # ; # | | # ; - ( ( , - $ : # - , ( # - ; - - - ; % - # # % # : * * = * = = # # - # - # - # : # e - ; # ; # : ; : # : = ( , {+]+^+/+ ",
+" : : : : : ; - , # = ^ 9 + - # - - - - # = + # ; * ^ 9 + # + # ; : # # ( , e - ( - * # : ; : ; ; * # , ; : - - : - # # - , , # # $ ! ( , e # , e , # # # # , e 6 x , # # e ; # , , ( e : # # - : e 6 # # # ( e # - - - 6 e , # - # : : - - ; # # - # # : , , 6 , 6 # # # # : + # - ; ; : ; ; 6 , - * ; + * - - # # % - # : + > # ; : # # # ; ; ; # ; % . . # 6 ; # - - e - - # : # # ; : - 6 ; - ; ; ; # ; # 6 ( ; # ; # # 6 e , - # # : : : - - # : - # # - ; e 8 # : : : ; - - - # ; # # # : % # # - # : ( # % - | , e # # ; # # ; # # . . # # # , ( : ; ; : 6 # # - | # , # - - ; ; * * = * : - - % * = % ; # ; : % = # # ; - ; : ; = * : # : : * * } | 6 , !+!+^+(+_+ ",
+" : : : : - - ; ; # - % # = - # # # - # ; : - # - # ; # # ; : - , e 6 6 e : : : ; , * = # # - - # * # ; : # - - # 6 # * # ; # # ; , , ( - ( , # , , # # # , ( { , , # # ( ( # # , ; - 6 ; , # : 6 e , # , 6 ( ( # # # # ; , e # - # # : - , # # # ; + : : : : : : # ; ; - : # - # : - : ; : : # # ; : ; * % # # % # = # * # - ; # : * ; : + ; ; = + # * = # - : : ( , ( 6 # - # % # + ; : * ; : # - 6 # ; + : ; ; 6 : : : : , 6 # ; : : - - # # : - # ; - # # , , # # : ; # # # - - # # # - - # % = # # - # # % + + ; # , e ; - - - # # ; : - ( : # ; - # # # - # 6 : % ; # ; - # # # # - # - > # 9 + # * ; ; # # ; ; ; : ; 6 : # ; - > # # : - ; : - - # } { J {+]+:+<+[+_+ ",
+" : : : : # % + * * = : * * : ; * + * * + # - # # ; # * % # : * * - - # # : 6 ( ( e 6 # ; - # : : ; # - ( e : # - - ^ ; , ! # # * , , : # - 6 ; # ^ , $ ; 6 , ( e ; # , , L ( ; ; # # # $ , , # - : ; ; # , 6 ( # ; # # # # # , 6 - - - # # ^ ; - ; ; - - # # - # # # # ; : # # ( + 6 6 6 ( # # - - # - # # ; - * * * ; ; # # # # : - - % # - - - # # # * * # - ; : e : 6 - ; ; ; = = = # # - - ; # # - ; # # ; ; ; ; , : 6 ( : 6 ; # ; # # # - - # : + , : # # - - ; % * ; ; # # | - : ; # - - # # ; ; * * # ; # # ; # % % e # # # # # # # ; : 6 ( - # ; - # # # = * 6 6 : - | : * * # ; e - * : % # : # # # # # ^ # - - - ; - - # - - : % # : * # - : * : - J , m _+!+}+|+1+2+3+ ",
+" : : : : = = # : * = # - - # : ; * % # - - # # ; ; ; - * * - - % * * # * # - ; , 6 e e , 6 , , e 6 - - : - # ; # # # # : # # * * # ; # # e e e : # # 6 , , , e , # # L # ; - ( # | # # , , - - # # ; - - , ; ^ - # % % # # , , e # - ; ; e # # = % # , - - ; # : + - , 6 e 6 6 e e # 6 6 : * + : : % ; # # - - # # * # # * * = * 9 + # * ; - - # - # # - - : : # # ( 6 6 # # # * # : # % # % * : : : , - # # - - ( # e 6 6 : 6 - 6 ( ; : ; # # - * # ; # ; + = + # # , , # # ^ # # # : : % # # # # # % = * ; ; - = ^ # % * : ; # ^ # # % % # # - , , , , ( # # ; ; % 6 6 , # # - - % # - , : , % * : - = - # # ; ; - : - # # ; : ; # # : * : - - - : : - - = % # _+:+<+4+<+[+5+ ",
+" : : : : - = + # # : % ; # % - # # # - ; - # , : : : ; * = + # * ; : : : ; * ; ; : e : - # : : : 6 ; - # ; - ; : # # - ; # # - * * - , , e e ( ( ( : : , # , e ; # , ( , ^ , = = # , , # ^ ( 6 ^ ; ; # - # ; # # = = # # - - ( , e , # : : , , % = = - : ! e # - 6 6 6 - - # # : 6 ( ( ( 6 6 : # * * : # - - ; # # - - # * ; ; ; # # - # = * : ; * # # # - - * + - # # : % # # # # = * * * : # - - - ; ; # # ; # e 6 : ( ( 6 : * ; ; 6 e 6 - # # ; : : : : ; : - # e } e ; # # : + - - - % # # - # # - # # # # - = % # # : : : ; # % # - # - ( # e e ( , # # ; ; # ; # 6 : : - # % : : # : , ; ^ 9 + % ; * * ; ; - - : # - - - : ; # - ^ 9 + # % : # % ( = * k ~+|+6+|+1+2+3+ ",
+" : : : : : | % : # # : * : # 0 = # % # % + + : : * # - # * # # ; = + # : e 6 ( 6 ; # - - : : - - ; - ; * ; * - # - # ; : - ^ ^ # # * # : : ; ; , , # : : : , , , - , 6 , # # # | - - # # # , e e , # : : # # ; * - # - # # 6 , ( # e , ( , e e ; # # # - * # 6 L # e 6 : ; : # * # - ; - - 6 ; e ( : - # # # # - # - # # % * ; # - * * - # = # # - - # # - # # % # : ; ; % # % # * * # - # ; - # - # # - # # ; # e - - 6 - - 6 ; # ; * : # : - ; # ; - ; * # - - * + + , w # # # 1 ; 9 + # # # # # % # # - # # # ; : = # ; = - # , # # : : ; # # : : ; # - : # # % # 6 : % # # 9 ; - . B # # ^ + - - - # - - - - : : # - - # # # - # # # : + : * : 1 S o { 7+8+9+^+0+a+_+b+ ",
+" : : : : : : : - # # # * = % ; - * * * * : - - # # # # - 6 - # = = ; # + - : ; ; # 6 ( ; : ; # # - - e 6 ( 6 ; # ; - ; ; # # # , # e # # - - : ; # # # - # # # # , # ( , = ; # = - - # # 9 , ( , , e ( ( , , # # - = - = # # ; 6 , # + : ( e ( e ; ; - ( , e , ; - 6 , ( e 6 # : : ; * ; : ; , # , ( , ; - # : - # - - # , ; % * ; - - ; : ; ; # # : # # - : ; * * * # , 6 - * * % * ; = # # : % # - - # - # # : ( : = - e : * ; # ; - # * * + - = * : * % + ; - # - # ; : 9 + # # # - # # = : # # ^ # ; ; ; # # f # : # ^ % = - - # : : - # # # : * % ; : - # # e , * % # e , : % = # ( , ^ ^ , - # ; - - # - ; ; # - # - # ; : ; - - # # : # 9 = # j + 5+_+c+d+6+e+e+f+ ",
+" : : : : : : : # # # # - + : ; # ; ; = = # % # - - # # - : 6 - # - # ; - ; ; : * : ; ; 6 e 6 - = + # : ; ; # 6 ( ; : ; # e , ( 6 e 6 : , # # - : ; # - # # # ^ ; : ; , # - # ; * * - - $ ; 6 B , e e e e 6 6 , ; # # # = % % # , # ^ # # e 6 ( , ; # - : e # # , 7 ; - - # , # : e 6 ( e , , : , : : 6 e ( , # : + ; : : : # * ; : : : : ; : ; : # # - # = # # # # * # # - # # : = - ; - * = ; - : ; : + = ; : : e - : 6 6 # # - ; ; : * * # ; # # : - # # # # : - * # ; = * = # : # , - # ; ^ 9 ^ ^ # : ; ; - = = : : # # : = # - # # - # # : * # * = - - e e , # # , , ( , 6 ; , w 6 6 , ( # % = * : ; ; : ; * # - # : : ; - # : + : # # # = | > _+g+h+i+4+0+j+(+ ",
+" : : : : : : : : * = * : ; - # : + + * % # ; # ; - - # - * ; e : ; # ; # # - # - - - ; ; : : 6 ; : - # ; : * : ( ; 6 e 6 % # # , ; ; 6 # # ^ , , , : # : , $ , ; , : # , # # # ^ # - ( , , # , , B # , e e ( 6 ; % | - , ; # - : # # # # # # 6 , # : # : # ; # - - # # # ; : # # # ; # ; ; : # : 6 # # # 6 e 6 6 # - - - # # % * ; - - : : - # # ; - # + + * * ; # # % # # # % # ; : ; # ; * * # : # # ( # ; 6 6 ( - e ( : 6 # # - - ; - # * ; : # - - * - * = - # : + = = ; * % : : * ; % * # | # , # # # # ; - = # = ; ; : - # # , : # : : - # % = - - , e : , ( - - ( 6 6 , , ! , : # ( # % # # # ; * # ; : - - - ; : # ; * # - : * * + = - 7+k+l+<+m+5+e+a+_+b+ ",
+" : : : : : : : : ; : * * : * = - # = # ; % ; % ; # ; : ; # ; 6 e 6 - : + ; # - # # - ; - # # # 6 : # # ; - ; : e : 6 : 6 # ( , 6 - # # # : # , , , # , , ( # e , , 6 , , # # , , , , ( e - - 6 } n+# # # e , # # # # : e e # # - # - - # # # ( , # - : + # # # = = # # , , e 6 # - - # e ; : # 6 : , # # 6 , ( : # # - : = - # # # # - - - # # # * : - - # * ; # * * * * * 0 * * 6 e 6 - - # # ; e 6 6 e : e ; : - - ; - 6 - ; ; : ; - * ; # * - * * = : % # ; ; * # - ; # ; # * * + : ( # - # # # - * 6 7 ^ # # ; % - # # = : # # ; # # - % % # # : 6 , ^ # # # - # # , 6 # % ^ $ } , ; : ^ ; - - * ; ; : # # # % : : ; - # - : ; + * = # 7+k+l+5+o+:+<+0+j+3+ ",
+" : : : : : : : : - 6 # ; - . # # - % # : # - - : - - - * : - e : 6 ; - # * % = # # # - ( ( # # 6 , # # ; : 6 # ( 6 6 # # - : : : # * % # # - % * : ; , , ( : : , , e , , e - = # # , ( # ; : # , $ , , # ( , , ( ; % # , ! - - # , ( - : , - , e ; ; 6 ; # # # , , : : , # # # # - | # { ( : # 6 , 6 : : ; - # , - - # % ; ; ; % ; ; : ; % % # # * - # : ; * + # * ; # * ; # # # : : 6 ; : ; # - - # # : 6 , # e # ; , - 6 # - ( ; e 6 ; # : ; , * = # * - - : # # - ; # # # * * = # ; : ; : % # # # j.9 6 - # ; # # - # # * ; , p+: % : 6 # - - # # # ; # # + ; # # - + # , , ! ! 6 + ; ( # # : * * - # * # # - ; : # - : - 0 # . # 5+q+r+4+c+1+s+1+(+(+ ",
+" : : : : : : : : * ; : : # % = % + * : - ; - - # % = * # - * ; 6 # ; * ; * + : + + * # ; e : : , 6 : * : ; ; - # # : # ; - - - % # * ; : # # # # # # # # , ( - ; ; e # - - , # # 6 6 , ; # # : ; , e , , ( e # ; e # # # # # # e e , - # - # , ; ; ; 6 , % * , , , # = ; ; # # # : # - ( , ; , - : ; , : 6 : # # # ; # - % # * * * * ; : * * + * = = * # : ; * % * ; % * ; # # , ; : : : ; : : ; : # * # - ; e e ( , 6 ( ; 6 6 6 6 ; # : - # # ; e # - % * * # # ; # # * # # * ; % # ; = : : * , # ; ; # # % % ; # # = # ; = # ! t+u+v+: ; - - - # ; # # ; : % # # # ; : ( # # # # = * % + : - - % * # * # # ; - - * # : = % = = - 5+_+:+d+6+h+w+4+}+x+_+ ",
+" : : : : : : : : # : : ; - # : ; # ^ 9 + # % : ; * ; # - * ; ; ; e 6 ( 6 ; # # - % * ; ( ( , , : ; , : : - - : + : : * = * ; # - % * ; # = ^ # ; , # ; # # , ( e ; # # - # , , , 6 6 ; ; - , , - 6 e , , ! , # ; 6 , # # e 7 , : 6 # # # # - - | ( # ( , # ; - 6 # # # # # # # ; , , ( + , e ( e ( # e 6 - ; - - ; # # - : - # # # : - # # # # = % ; : # - # # # # - - - # * # # : # : ; - # - ; ; : : ; * ; ; # : - ; # 6 : - : = ; ; # - # ; + : # # : $ * # # - - - ; ; % * ; = # = ; - # # ( ; ( # # # # # * = # # # - - , T y+z+A+B+# - - # # # # - # % = # # = : # % + + # - - # - # ; # * ; : = = 0 = : - # : : # % # d # _+g+9+k+C+o+:+6+e+[+5+D+ ",
+" : : : : : : : - # - e , 6 - # # # - # # # - - ; e 6 # # + # * * * ; ; # 6 ( ; : ; # ; : ; 6 # - # e 6 - % - - # # # = + # - - # # # # + * * * ! # ; e ; : v , e # - - - - , # , ( , # ; - # # # , ; ; e - , , , ( , , ( , , , | # ; ^ # # # - # - v , # # # ; ; ; # | | , # - ( ( , # , - ; , t., , : , : - ; ; - 6 # ; # : ; - : : = - , # ; = ; * 6 # - ; # # - - ; # # - # # : : : # # - # - - - e 6 ( : : # ; - # # ( # + - > # ; # ; ; ; # % ; * # , : % # - # - ; # ; # # : = = # : + e # - , , # - # # # # = + # # # w E+T T F+G+H+# # - : ; - # - # # ; ; * * # # ; * ; # # # # | 7 , # * : = = = = + ; : + # : = : | q . I+s+o+h+i+1+s+0+j+5+D+ ",
+" : : : : : : : : = ( : - # ; # # ; ; - # # - # ; 6 e ( # # # ; = # : * : ; ; 6 e 6 - : ; : - ; - - : , : - ; ; # * ; : ; ; : # % # = # # # # * ; # , , , ! - # ( ( - # - - : # # , ( , 6 : # # , , # ; : : , , , , # # - ^ # # # # - # # , ( : # , e ; ; : # - # # # . . ; ; - , ( : # e 6 , ; : : , | e - ; # , e - ; # , # ; - # + * * * = - + : = - : ; ; # ; - - # # ; : = * # # # ; ; : ; ; # - : ; ; ( , e , 6 , , 6 $ f ; % # : * # * = - # * * # ; * * * # # - ; ; = + # ; = # e # # e - ; : 6 ( # # 1 # # # # : | ^ T.X.T.T T J+K+- >+: : % # # # # ; - = * ; # : ; # # % * ; ; # # # : # * = ; : - - # - : : = = = 1 ~+(+k+0+m+s+s+|+1+2+3+ ",
+" : : : : : : : : : # ; # : : # * * % # ; : : * # - e - e - : ; # # ; - ; # # ; : : 6 : # - = : : ; e # e - ; # , e # = # # # , * * + * ; ; # # * * * * 7 - # # $ e e # # , , : # # # # ( 6 e # # ( e ; ; # ; 6 e e # # ; # # # # , | | # ; - L , , , , ; - , , , # ; : - ( : # ; # # # # - ( e # - - e - 6 e , ( 6 ; : ; # # # , e - # # # # * ; - - ; : # # * = = * : ; : : ; % + ; * * * % * ; ; ; # ; : : : # : - # : : : e # ^ * : - : * = * # # = % ; : - % * # # % : ; : - , ; # # # # : 6 ; ; 6 6 # # ; = , # + # + - T T T N.E+X.M.T T L+M+N+8 % # # # # ; = = # # # - # ; ; # : : : ; ^ # # # . # # # % % % # : = % = > !+O+I+5+o+:+k+0+m+a+_+b+ ",
+" : : : : : : : : : % # ; - # # * ; * : ; - ; : # ; 6 ( ; # # # : : - * # # : - # # # 6 # - ; # ; - e - 6 e 6 ( # = - # : : : ; % # # # # # ; : - - e # 6 * 7 ( # # 6 ( , # : ; , # : 6 6 e # - 6 ( ( ; ; ; e , e e # ( , # # ; e # > # , # # , ( , , # e # # # # # ; : 6 ( - # ; # : # # = * 6 ( , v ( # # : # , 6 6 e 6 - # ( 6 ; : ; # # - ; # ; # - - # ; = = + - - - # - # # ; : 6 - - # # # % ; # ; - # # * = : : - - # # = - ^ 9 + = = % - ; - = ; * : * * ; ; * + # # - : : : ; # | # - # ; ; 6 ( # # # # # # 1 # ^ $ T T T T T M.E+J+T T T P+Q+R+7 # # % # # # # # # ; : = ; # # - # - # # # # * * % # - = = # : % { = . {+]+c+w+(+k+0+s+o+:+1+e+/+3+ ",
+" : : : : : : : : : - > # ; - % * ; # ; - * ; ; # - # e - * 9 ; - # : ; : ; ; ( ( # # 6 6 - : ( , ( 6 # # : : ( # # # - ( # # # - # ; # # # # - # - ; , # # % * # - ( , 6 e # ; # , - : ; ; , ( , ( - # # # , , e e # - - # # , # : 6 , : # # # ! ; # | , # # # % % # ; : , , , , ( # # , ; # 6 6 ( ( - - # ; # ; , : e - ; : ( 6 e 6 # * * ; # # * % - - # = % = # # : # # - # 7 # : - # : + # # # = : ; : + ; : # # - # # # = % - - # # ; = ; : ; - > # ; : * = # - - - - - - # - - # % # # - # - 6 ( - - % ; ; # # - # | T T T T T T T T U J+E+T T T S+T+U+^ # ; ; # # # g # : # ^ ; = % # - * # - - # # * - = * ; ; : : , = 5+!+V+i+W+5+o+:+1+s+o+}+(+(+ ",
+" : : : : : : : : : = % # : * * - # * : : : # - # ( ( # : % # : # ( 6 6 e 6 6 ; e : 6 : e : : e : 6 - # ; ; # - - ; : * : + - ; : ; # * * * - - ; ; - # # , - # * + # $ , 6 e # # # ; e ; - - , # ; - - % % # # # # # # - ; # , # # ( ( # , ( - ; # # = , e ; # # - # - # # e e ( , # # ; # , # , 6 : ; - # 8 ; + = ( , , # # ; # - - # * ; ; : ; - % + # * # - # : # - , ; # - # * * + * # - * 9 + ; # : # - - - # ; : # # ; ; # . . # 6 ; # # - # # % # : * * # # - # ( # # # ; - - % # - * # # # , e - - # # # # # # # # T T T T T T T T T T N.T.J+T.T $ # : ; ; # = % # : ; ; : = % # - { # | # # # * ! - = : : : : 5 2 7+5+_+:+d+6+X+Y+o+h+i+(+k+1+x+_+ ",
+" : : : : : : : : : % * : - - % * - # # ; e ( # 6 ; e : ; # ( e : ; # : : 6 , ( ( ( ( 6 e 6 6 ( 6 ; : : - * * ; ; ; ; # * ; : : ; : # * ; # - - ; ; # # # - # # - # # # # , , $ # # ^ e e # - # # ; # = = # # # - ( , e , # : : # # e , ! , , ! , , $ ; : 6 $ # - - # : : ; # # : : ; # - : ; # # ; , : % # # # # , : 9 # # 9 + # - # % * ; # ; : : ; : - : % + : # ; : : : ; * + ; # # * # - % # : ; - # # # # - : : # * * # # * = # ; : : ( , - # = * : - ; - # = = * : + + * = * : * - - + $ # # # e - # # # ! - # - # T T T T T T T T T T T T T N.E+ ^ # ; - = - = # # # - # # , | # | # , # v < | % : : : : 9 5+q+g+9+k+^+Z+W+`+s+o+h+i+<+[+5+D+ ",
+" : : : : : : : : = ; ^ 9 + # * ; : ; , # , ( # 6 ( ( , , 6 : - # ; # # # e ; : ; - - 6 ; ; : - - # - # : % # - * * # * ; ; : : - # % * ; # - # + = * : - # # # ; # # - - , , , , # , e 6 : # # ; * - - - - # # , ( # e e , , ( e ; # , , - , # # e , # # , 6 # ( # # # : - # # , 6 * # ; : - # # , 6 : % # e 6 - ; # - # e e ^ ( - , # # # - - # # # # ; - * : ; ; : ; # # - # # - ; : ; ; - : * # , 6 , - * # # : : - * ; % # * = , 6 # : e : 6 - = ^ 9 + ( ( + + # : - - # = + ; : = - = 0 : : * # 6 , # ; , * # % = T T T T T T T T T T T T T T T ^ # # ; % - # # = : ; # % # # # % # = { 1 + : : : : 5+_+:+d+<+w+ @.@s+o+h+k+s+m+|+1+2+3+ ",
+" : : : : : : : : : ; ; # # ; ; e , , : , : : e ; : ; 6 # : # # - ; : : : # : ; : : * ; # ; e # ; : - ( # ; # - + + * = - - - # # # - % * : ; ; # # # : - , , # ; # # - % , # ; : ; # : ( $ , # : # = - = - # ; 6 , # + : ( , , , ; ; - # ; ; e , # - # # ; # , : ; # # , - - # # : # # # = - # , e , - - , , ( 6 ( # ( 7 6 : , , # , - # # ( : # # * # - , * = ; + # - # # : , , e , 6 # ( * - ; : - # - * ; ; - # # # - ; # - . # e - 6 # ( 6 6 # = - # # # - # # # - - : ; * + : ; - ; + # : 6 # # ; ; % # # , : : = T T T T T T T T T T T T T T T T T | # # # - # # * ; e , = # ; = % ; ; = = : : : : : {+]+g+9+k++@O+I+s+(+k+o+h+i+<+4+a+_+b+ ",
+" : : : : : : : : : : : ( * ; 6 ; : # : 6 # # ; : ; : : # 6 , # - # - , : , ; ; : ; # - = e e ( , e : ; - * - ; - # = % # - ; - - + # * ; : ; : ; , , - : # 6 : - = # ; ; # # # : # # # # # ; e ; # # : = % % 6 6 e 9 # e e 6 ( , e # - : # ; = # ^ : : # # , , 6 # # # , ( # - : : # # # = - - e e : , ( - - ( 6 6 e , | , , 6 : # , # : : e - * * ; # ; ; ; # - % # - - - ; - : - - # - # - - : + ; : : % # - # ; : ; ; : : = = # : # ; - # # ; : * % + ; ; * * # - # # # : ; * - # , * - # - : , # : - # # , ! - # T T T T T T T T T T T T T T T T T T T # # * % ; = # # # : # # : : : : : : : : !+!+}+<+w+[+^+w+(+k+0+k+0+m+|+1+/+e+f+ ",
+" : : : : : : : : : # : 6 e ( : , : # 6 : , # - - - # # - 6 ; ; - # # , 6 ( ; - - ; # = = ; # : - - # ; # # * # # ; = ; - : - ; ; - ; # # # # - # : * : , , e , # # # # # # # # - # - # , - 6 | , $ , ; ; # - : ; ; ; # , # 6 , # - - - # ; # - # # # - - # , , : - # e 6 : e e # - - % ^ # # : 6 e # # # # # # ; e 6 # # ^ e , , : ; , 6 ( - e : - 6 # - 6 # * * # % - ; ; - ; 6 : : : # # # * # - - - ; % + : - : ; 6 * - # ; - # # # # : + * * ; : ; - : ; # # : # ; # ; # : # * ; * # : 6 6 # # , e e # , - - - % T T T T T T T T T T T T T T T T T T @@% + # # # 8 % % # * : % % = 5 {+]+^+<+#@`+c+w+O+I+5+o+:+o+:+1+[+_+b+ ",
+" : : : : : : : : : 6 # - - 6 : # : # 6 , 6 # # - * * - # , : ( ; : # - ; , 6 , # - - % ; : # ; - # - ; : # - - : ; : : , - ; # , 6 6 : # - - - # : - x , # ( , , : # # # # # # , # : - , ( , , : ; , } , # - - # - - , , , ( , # - : : # # # = = # # # | e e : , , ( , ( 6 e ; ( , # % : 6 ( # # ; # # ; ; - # # # : - + ; , , , , ( ; 6 ( - # - 6 : : e ( ; ; ; : # ; # , e - ( ; # + ; + ; : % ; - - ; * * - - : - - # - # ; # = ; ; - ; - # ; - # ; : # # ( # # * # * * : # * ; 6 ; ^ # 6 e # # # - 6 - ! - # # :.T T T T T T T T T T T T T T T T T 4. # # - % % , # % ^ 9 % = = = _+!+}+^+9+$@/++@5+w+(+k+0+h+1+s+e+e+f+ ",
+" : : : : : : : : : ; # . . # 6 ; # , - ( ; ; - # ; ; - # # : # 6 e ( : : ; : : ; - : # - , e , 6 , - # - # , : # - % ; 6 ( , ( : ( ( 6 e ; % * # * : 6 6 ; # # # : - - - # - # - # : e # # , , : # ; # ! # - # , ( - : , - , e # ; 6 ; # # # , , : : , ; e , # # ! # v ( ( ( e , 8 ^ # ; : ; - # - # ; e , 6 , % ; # # # : ( # # , ; # e # ; , - 6 # - 6 ; e ( e 6 : , ( 6 ; 6 , ; # # ; # # : * : # : : # # ; # # # - # ; : : - > # ; - - , e , 6 , 6 e 6 6 e : : # # * ; % ; # # - # , e : # # # # # ; : # # # T T T T T T T T T T T T T T T T T T T 8 ^ j.# 7 # # - = = : . :+<+4+O+I+%@`+c+O++@(+k+0+m+5+h+j+I+ ",
+" : : : : : : : : : # * = # ; : : ( , , : # e # - # # - = # 6 : 6 , ( 6 e 6 - - : : , * * # : - # ; - # - # # 6 # - # # : e - - 6 - - 6 ; ; # # # - , , , # # # # e , # # + : ( ! ( # , - , e , ; - - # # * ^ , e , - # - , , ; , , 6 , % * , , , # = ; ; # , # - # - v $ ; , ( , # ^ , # ; # # - - # ; - - # # % = # # ; : # % + # , , , ( , 6 ( ; 6 6 6 - , 6 # 6 , e - 6 e - : : ; ; - # # # # # - ; : ; - # # ; % ; # # - # # % # : * * - : - # : - - # ; : - # * % * ; # # , ; - # , : - - # 6 - - : , # ; e T T T T T T T T T T T T T T T T T T T ^ &@*@ . ~+d+6+=@w+s+o+h+i+x+^+5+o+:+1+`+c+[+g+ ",
+" : : : : : : : : : # - , 6 # : e : - , e e % ; # # * = # 6 , : # ; - : 6 : - - ; # - % # = : : ( ; : # # % - 6 - = # ( : = - e : * # - - , # # - - 6 6 - # , # ; , , ; ; # , - # # : : e , ; # # # - # > : - : - , # # - - # - - ( , , e , 6 , # - # # # # # , e ! # # ^ , ( ( ; ; , , 8 # # # - : ; # - # # # # ; * * - # e : ; # # # # 1 ^ # 6 : ( ( 6 ( 6 6 - ( : # ; : # ; - # # + : ; # # - # : ; - - - , : * : # - ( # ; * : - - % * = : : : ; ; - - ; # # ; # # % % # , = # # : : - = # # ; = % e , $ T T T T T T T T T T T T T T T T T T T !+8+k+^+O+I+(+k+0+m+5+ @.@8+1+s+o+0+1+2+3+ ",
+" : : : : : : : ( e 6 e - 6 # ( ; - ; ; # * : ; # . # e - : ; , : , 6 - ; - - ; - # * ; # ; e ( e ( , # . # e : : : e - : 6 6 # # ; # - - # ; : ; ; , ; # # # ; # # # - # ( # ( , ( B e e e , # # ; # - - | = = , ; e , - # - ( , ; , , : - ; # | | # # - ( , , # $ - % + , , # # , # % ^ ! # # : : % # # # # ; : = * ; , 6 # # # % * ; # ^ 8 ( : - - 6 ; = # 6 , : e , , 6 # : + * - - ; ; : - # # : : * * - # - # : * : + - ^ 9 + # * ; # ; # ; : - ; ; : # : ; ; # : + - , - - # * # # * % # ; # | * : # T T T T T T T T T T T T T T T T T T T :+d+6+h+w+(+k+o+:+1+<+w+(+k+k+^+4+a+_+b+ ",
+" : : : : : : : # : : - # ; - ; 6 e : : # ; : 6 - = # : ; - # e 6 - # : - ; ; - ; ; # - - - , 6 # e ; - - # ; 6 6 ( - e ( : 6 # # - # - # : - # # # e 6 : - # # # : : , , ; # ; ( | e ; - - , 6 ( , # # # : # # # # , ( , , , e ; ; - # ; # # ^ . . # # - , ( : # # , # = # # 1 | - # # % # B # # # % # # - , ; # - , # # - - ; % # : : : , $ 6 , # * ; * ; 9 , 6 , : : 6 6 : ; - = = # - - - ; # # = - - % * * ; ; - # * # # - # # 6 6 ( : ; # e - ; # - - # > # ; * # # # - - e # # # # # ; - : = # % 7 T T T T T T T T T T T T T T T T T T T -@s+k+k+0+m+^+5+o+1+s+s+o+h+5+w+ @.@e+e+f+ ",
+" : : : : : : : : - # : + # : > # ; - % - 6 e 6 ; : , # 6 , , : , : # - ; # , e - ( e : # ; ( 6 6 - ( e e 6 ( ; : - - ; - 6 - ; ; : ; ( # : - - , e 6 ( 6 ( # , , ( : , # ; # ; # , , : , - ( # 6 e ; ; ; : # $ # # # ( # # , , ; - , , , , ; : - ( : , - - ; # # - , , ; ; * * : , # # # | # % % # # # # # % ; e , # , , ; : = # # : - # : ; : ; ; - - * ; # : : # : ; ( ( 6 e , = * * # - - ; - # - + # * ; # # , : * : # ; ; ; ; 6 6 : e 6 - 6 e 6 : - = # % # : * - , # # # , # # + % $ ; # = * : - # T T T T T T T T T T T T T T T T T T T ;@>@k+0+o+:+1+^+0+s+o+h+k+0+m+<+k+^+0+j+(+ ",
+" : : : : : : : # * # - # # % # : * * - : : # 6 : # ; : : e # e - 6 e 6 ( 6 ; : ; - # # : # - # 6 , : e , : # e # ; , - 6 # - ( ; , , - ! # e # # , # ; , # # 6 , # , : # - # ; 6 : : ( # , # 6 6 - - # * : ; ; # - ; : # e ; # # # # # # ; : 6 ( - , ; - - # # = * e , # 1 - ; e # # # , # % # # 8 # # ^ # ; ; ; - - ^ # : # ^ % = - - - - 1 # ; % = # # 6 , ; # 6 6 - - 6 : : ; # # - # - * * * # # - # # # * : - - # : # - - - - ( 6 : 6 # # : - # # ; % * : - - , # : 8 # * - # = * , ; # # ; ; - T T T T T T T T T T T T T T T T T T T % ,@'@>@o+:+1++@Z+.@Z+I+0+m+o+:+1+w+ @.@I+g+)@ ",
+" > : : : : : : : : : ; - > # ; - - - % * * - . # 6 ; # - - e - 6 # # : : ( 6 e 6 e - # # : ; : : e : e : - , e e ( , 6 ( ; 6 6 6 6 # # - , ( 6 # | # # # e ( ; , , # # 6 , - - # # - # - ( - - , # ( ( - # : - - # ; : ! ; # | ; # # # % % # # # ( , , , ( # # # # # 6 6 , - # # # , - ; $ : e # # ^ # ^ 9 # : ; ; - # # - : # # : = # - # - # 1 # # - % # : : : ; : 6 6 : e e # : # - % = - ; + + ; * * # * * ; - - # - # # # : * - - 6 + = # # - # # ; # - ^ 9 + - , * # # % * - # # ; , ( * # : ; # T T T T T T T T T T T T T T T T T T % !@ ~@{@]@1+s+(+^+9+I+s+(+k+0+m+s+e+w+C+^@/@ ",
+" = : : : : : : : : : : % # : * * # * ; : = # ; : : ( , ( 6 # # # , ; # ; # - # - ; : : : : : : : ( e : - ; ; # : - ; # 6 : - : e B - , 6 - # , , e : , , # # : # , , , - # $ , e ; # # e e e ; ; # - # # : # % - , - : - - ; ( # # # # - # - ; # e e ( , # # ; # # # # 6 : # , , ^ = : # , , e ; | * ; ^ 6 # # ; - = # = ; # # - # # # * # # # ; # - * - - - - : ; ( ( 6 e ; 6 , 6 , | % # - # # : 6 ; # * ; # # - # - - # ; = # % * ; : : % - - * - : - - - # # # # : e , # # ; : # 9 ; ( = = - : T T T T T T T T T T T T T T T (@_@:@<@[@}@:+|@1@2@ 3@4@W+`+c+O+I+(+k+0+o+:+1+5@6@m+7@8@ ",
+" > ! : : : : : : : : : * : - - % * * # # # ; # # : e : 6 - ; ; : : : * * : - + ; : ; ; # # - - - ; # - e : : # ; - # # ( : % : ; 6 ( # ; # # , 6 e 6 ( e , : ( , # 6 , , # : ; 6 ( , e 6 # 6 6 # ; : ; % * # - ; + # # ( , # # : f # - # # : : ; ; # : : ; # - : ^ ; ; # e : % ^ # ; # 9 ; # # # ^ # # | # , 9 # - ; % - # # = : ; # - # # # ; = * % ; ; - ; # ; 6 - - 6 ; e 6 e : # ( ; # - # ; : - ; % * ; # ; : ; # # ; # % # * - ; - ; % + # * ; # : + + * ; ^ # ; # 6 - # ; # # , # # # ; # T T T T T T T T T T T T T T | 9@0@a@b@c@d@g+e@f@g@h@i@j@k@l@j@j@k@k@l@j@m@j@k@j@j@k@m@j@j@m@j@j@j@j@m@j@m@j@j@j@j@k@l@k@m@j@j@k@j@j@k@j@j@j@j@j@j@k@k@k@j@k@j@j@k@j@n@k@m@k@m@j@j@j@j@j@j@j@j@j@j@k@m@j@j@k@j@k@k@l@j@k@k@j@k@j@j@j@j@k@k@k@k@j@j@m@j@j@j@j@k@j@j@m@n@k@k@o@j@m@m@p@k@m@j@j@j@j@j@q@r@s@t@{@u@W+C+o+h+i+W+(+k+o+:+1+s+v@/+^+i+ ",
+" ^ : : : : : : : : : : 9 + # * ; % % : * # * * # ( 6 6 # - 6 # - - # # - # - # # # # ^ - ( - - ; # # , ( , e , 6 , , 6 - - % # # ; # ; e 6 , ( e e ; ; : - , # # , , ; 6 ( - ( , 6 e 6 # ; ( : ; # # * ; : ; - # : ; ; - # ; # - - , # - # : - # # , ( * # # : - # # e , # % # e 6 , e + = ( , ^ ^ ! # ; : : ( , , , - # # * # ; ^ % # ; : # % * - , , e : = - e : * ; ; : : 6 ( # + : : # : : ; : - - # # : : ; : : ; * * * - * # * = ; : - # # ; # - # = # , - - 9 # : ; - * # # = # # # ; # w@T T T T T T T T T T T 4. j@p@k@j@x@y@z@A@B@_+q+C@D@E@F@G@H@I@J@K@L@i@M@N@O@k@j@j@P@j@p@P@n@P@k@j@k@P@p@p@Q@j@n@j@Q@j@p@p@p@p@p@n@p@m@p@Q@k@n@P@k@k@P@p@p@k@P@j@p@Q@P@R@n@k@P@R@P@R@m@p@Q@p@p@p@S@p@T@j@p@p@Q@j@R@p@p@p@p@k@n@p@n@n@m@U@j@p@p@Q@j@k@n@P@R@p@j@m@k@j@j@m@m@p@j@T@k@p@p@l@m@V@j@W@X@Y@Z@`@ #.#Z++@k+0+m+5+h+5+o+:+i+W+j+e@W++@+# ",
+" : : : : : : : : : : : # * h % + % # ; # * ; - ; - - , e ( - # : ; # ; ; ; # - - # ; * : # # # : * - - # : - # : : : # - # , # ; # - , ( ( - # , : ( - , , , , e ( ( # * + # # - 6 ( # ; - : # # # ; # # ; # - - ; ; ; # # # # # - 6 - , ( # - # # : # # - = - # , e , - # , , ( , 6 - , < } : # , # # # : e ; , - ; # ; = - | + = , 6 # : # % = * : e - : 6 6 # # - - # # , ; 6 : # - ; : : - # - - ( # : : - # % - - # # : % * = = + - # : # : # - ; % # , % 6 # + * # = * # # - - - = - w@T T T T T T T T T m@j@j@j@j@j@N@@###$#%#u@3+&#*#=#-#;#>#Z@,#,#'#)#_@!#N@j@j@j@S@n@P@n@P@k@j@j@j@j@n@n@P@P@n@S@P@P@n@P@Q@j@j@n@P@P@j@j@j@n@P@R@j@o@j@j@p@j@n@S@P@j@n@k@j@j@n@j@n@k@j@n@k@j@j@P@P@k@j@j@j@P@R@P@p@k@j@j@j@j@j@p@p@j@k@j@j@j@P@j@p@P@j@j@n@P@j@p@m@m@~#j@j@p@P@n@k@j@j@U@U@!#Y@{#Z@,#]#u@g+(+5+o+:+1+5@^#8+1+s+5+h+/#(#_#:#= ",
+" : : : : : : : : : : = % - - # ; # # ; # : * * - # # # * - # # - 6 # - : ; ; - # * # # # ; ; 9 ; : = : : - - # # - # * - # ; , # , 6 # # # 6 - # , e , , , , ; # - # ; , # , # # # # # # # * # # # : # - # - - = * # # # # : # # e 1 ( # ( : : # ^ # = # # $ e : , , - - , 6 6 , , | , , # + # # # # , e ( # # ; - # % # : # , # ; = * * ; # - - # ( : 6 # # - - # - - 6 ; e ( : - - = # # : * : * * - - # 9 + # $ , ; % = * ; # % # ; % : : ; ; ; , # - # , # , # # ; # - + # + ; ; j@j@j@p@j@P@j@k@m@<#_@[#}#|#k+}@1#2#3#4#5#6#<+B@7#8#_@!#P@j@j@n@P@n@P@k@j@j@P@j@j@p@p@Q@9#P@P@P@T@k@P@j@j@j@j@n@n@j@n@k@j@P@j@j@k@k@k@j@j@n@j@p@n@S@P@R@S@p@n@S@P@j@n@k@j@j@j@j@P@j@j@j@j@k@j@j@j@n@S@P@j@n@j@j@P@j@j@P@j@j@j@P@j@j@j@j@m@p@m@j@m@m@j@p@m@n@P@U@j@m@k@0#{#Z@,#5#6#a#b#C+h+W+1+c+s+s+W+5+o+1+5@6@c#+@d#e# ",
+" : : : : : : : : : : % # # # = # # : - - % * + : ; ; # # ; e ( ; # # - , : * : # # ; ; + * # # ; - # - # ; ; - - | # ; ; ; : ( e e e # : # , ^ e # , ( - , # ; , # - - # , ( , # - : : # ; # % : : # ; - - % + ; * : : ; # # : # ; - ; # - - % ^ # # : 6 , # # # - - # # 6 6 # # ^ $ } : # ( ^ # # ( e 6 6 - # $ g + , % : - # * * = : # # : , - 6 - ; ; : ; ; : ; , # , ( , ; % % ; * - # * # # - + : ; # * + ; , ; ; * % * * : - ; - - - - # , - # # 6 ( , # = ; # # # : # # m@p@k@k@n@P@p@j@k@k@j@N@f#g#h#i#}@+@c#j#k#(#B@c#l#3+7#8#m#n#j@~#p@k@p@n@S@P@P@p@k@j@j@j@P@n@P@p@j@P@n@R@n@S@j@j@Q@P@n@j@j@j@l@R@j@o@p@S@o@T@k@n@j@j@j@j@n@k@j@j@j@p@n@S@P@j@j@n@j@n@n@j@j@P@P@j@o@p@n@j@p@n@S@n@k@j@k@k@j@n@P@P@p@k@P@k@j@m@~#o@m@o#p@m@m@9#m@P@p@U@k@o#p#q#5#6#(#B@w+W+0+m+o+h+i+W+j+o+h+1+Z+/+r#s#t#u#v# ",
+" * : : : : : : : : : : # % = , 9 + # * ; - - ; ; % * - ( ; 6 , # ( : - # - ; - - - # # - # # * * : ; # # - + : # e , , , - # 6 6 ( # # # : ( # e e ( , # # # # # # ( # - : : , # : # % # # - # * = * : # ; : 6 - # - ; # - e # : + = - # # : 6 # # | # # # ; , ; ; ; # # - + # e , , , : # , ! ( , , : # v ^ ^ # ( # # : # * : - # ; - - ( - 6 # - ( ; e e , , : , : : 6 e ( , # , ; * : = % + - - ; ; : - * - , B # # # # ; : # # - # - # * : ; 6 6 # , # # # # # | * - # k@P@n@k@j@j@j@k@j@k@N@w#x#y#z#A#!+3+&#B#C#D#r#a#l#3+E#>#P@k@o@j@k@j@n@j@j@j@j@P@k@j@j@n@k@j@j@P@n@S@n@k@k@n@j@j@j@Q@P@j@j@j@j@j@j@n@o@j@n@n@k@j@j@j@n@S@P@k@j@k@j@j@j@P@j@n@k@j@j@P@n@P@P@j@n@k@j@p@n@j@j@n@S@P@R@9#S@n@k@k@j@j@j@n@S@P@j@j@j@m@j@m@~#j@m@j@U@U@U@H@q#F#k#(#B@G#w+s+o+:+k+0+m+5+h+/#0+H#(+I#5+|#J#K#L#(@ ",
+" # # : : : : : : : : : = * # # # * = : # - # : * * - # : ; ; - - # - # # - # # % # % * + # # - # # # ; - # : : $ # # - # : , * * - # ; , , # - e 6 : # = # ; # ( # ; : ; # : ; # # ; ; - # * # - # 7 # : - : ; ; e - # # - # - * | # ; ; : ; - # # # ; # ; # ; % # # # : : ( # # , , 6 : : ; - # , e , ; ; - ; : * # # ^ 9 + # : * * # - ; 6 # # , ; ; : # : 6 # # # 6 e 6 6 e , # * : ; : - # - = : # % = * ; ; ; # # : ; # * * ; - - - # # 6 , # # # # # # - # 9 ; k@p@P@n@k@j@o@p@k@j@j@j@j@j@,@M#N#|#^+B#O#h+9+Z+W+c+!+P#Q#R#S#U@l@n@m@l@T#P@j@p@n@S@P@p@k@R@j@j@j@p@n@S@P@j@k@j@k@j@p@j@j@j@P@j@j@j@j@j@n@n@P@R@j@j@j@j@P@j@j@P@j@k@n@P@p@n@P@k@n@P@p@k@n@l@n@k@j@k@j@j@j@n@j@j@j@P@P@j@j@P@j@P@j@p@j@j@P@j@P@j@o@j@m@j@m@p@m@k@N@N@X@U#V#B#O#1++@(+k+0+m+o+:+1+5@c+o+:+.@m+W#A#X#Y#Z#`# $ ",
+" ; # + : : : : : : : = ; = # - # * - - # : = - % = ; : , , e ; : - # % ; : * * * : # # e # ; - - # : # # e : 1 # e , # + * * # # # : , # % # , e , , , , , , 6 # # - # : , : , , : ( ; * * = # : ; * * ; ; # : 6 * : : - e , , : # - - ; # # - - # , - # # # % = # , # , - % + ; ; # , : 6 : # # # , , ( # : : # : - - # * : - - % * # 6 : # # ; # ; : # 6 : , # # 6 , ( : # ( # - # , # - # # # - * * ; # - * : ; # ; # # * : : # = : - 6 ( # # # # # # # # # | j@j@Q@j@n@P@R@j@k@P@j@j@k@l@k@j@9#u#.$X+-@9+1+O+I+s+o+h+V++$@$#$>#$$H@q#L@%$U@j@o@k@n@j@j@P@R@j@o@p@j@j@j@j@j@R@j@j@P@j@j@k@n@P@p@k@k@j@P@j@p@p@Q@9#j@n@n@P@p@k@P@p@P@n@k@j@j@j@P@n@k@j@P@R@k@Q@k@S@j@j@k@P@o@P@R@j@o@T@k@n@P@p@P@p@k@j@n@P@p@j@k@j@m@m@j@m@m@U@j@j@o#x@Z@`@4@^+W+0+c+5+o+:+1+1+s+Z+h+i+5+A#+$&$-@*$=$-$`#;$>$ ",
+" # ^ # # # : : : : % # # - - # ; ; * # # % # - > # ; - - , , # ; ; - - - # # # - # - ; ; # ; : : : * - # , # ( e , , , - # , 6 # # # # # , # ( , : - # - - - # # , # : , e # # e ( # # # # # # 6 % - e , e - # - % ; ; : # # : : - - ^ # , , - : ; ! - # , , # ; * * # ; e # ; # # # , 1 ^ - - ; - - # - - # % - , , # ^ 9 + # * , # ( * # # + - : : # 6 , 6 : : ; - # , 6 , - - # = * : : : * : + + : - # + # - ; : ; % * : : , - # # , , # # # # # # | # # # U@l@j@j@j@j@P@j@n@P@p@j@j@j@j@j@j@,$'$,@'@>@)$k+w+(+k+0+m+5+I+!$~${$]$X@U#^$s@s@/$U@j@n@S@l@n@j@j@k@k@j@R@j@j@j@k@n@P@p@k@P@n@k@j@j@j@j@P@p@P@j@j@P@n@P@n@k@j@j@k@j@n@n@P@R@j@j@n@n@P@R@j@P@k@j@j@P@o@j@n@j@j@p@n@j@j@j@P@n@k@j@k@j@j@k@P@k@j@j@j@j@k@j@j@k@m@R@j@U@!#Y@K@($]#u@_$o+h+i+W+1+s+h+i+k+0+^#`+-@:$%#<$[$}$L@`#|$;$L# ",
+" # - + ; ; + * - * # > # # * ; * * - + * % # : * * - # $ , # # - - # - # - , ; # ; # : ; ; # # # - ; - , # , - ! - # # : # # - - ; 6 ; ; # = : - - - # - - # e 6 - ; : - , 6 % - # + + : * # # - : # # - : * : - # - - # # # : - ^ # # - : : % # # # ; ; - = * ; e : # # # % * , ; # ^ ; - ; 6 - # - * * * = - - - # - ; # * - 6 : # # - ; , : , - : ; , : 6 : # # : , # # - # ; # : - ; # ^ # # + : : # # - - : # # : # - $ # # # , : , ; , , # # - # k@p@p@p@j@j@j@p@n@k@j@j@o@k@j@p@n@k@j@k@,@1$2$W+}+C#5+o+:+1+5@1+~+q+3$e#Z@`@4$5$6$;$7$j@k@j@j@n@j@j@n@j@j@n@T@n@j@n@k@j@j@j@n@P@R@j@o@n@P@p@P@p@k@j@P@j@n@P@R@j@o@p@n@S@n@P@R@j@j@p@p@Q@9#P@P@j@n@P@p@j@n@n@S@P@j@j@k@j@j@n@P@R@P@R@j@j@j@j@R@j@j@j@j@k@n@l@j@9#j@k@!#{#Z@,#8$4@5+k+0+%@[+^+9+(+m+9+o+W+9$+$`+0$[#z@z#a$b$;$|$;$L# ",
+" ^ ^ # - e # # # % % # ; # # # # # # # * : - - % * * + ; # - # : ; # # - : : * : : - ; # ^ # ; # # : ; - % - , ( # + * # # # , , , # # - ; # # # # + : # 1 : ( : # , ( ( 6 # + : : # # # = # # + - # : + # # - ; * e ( # = # - * : ^ # - # % # # # # % % % # # # - - # ; ; : : : ; ^ , e # ( - # = # - # + # # - # e ; # % . # 6 ; , e - , 6 ( ; - # e 6 - ; - - ; - : # # ; = * * ; ; ; # # $ f + - # # - ; ; * = # # - # : * # 6 , # ; 6 - e % # # U@j@j@j@p@n@S@j@n@P@R@j@o@n@k@j@j@m@m@k@k@ $h@c$f@I+$@8+1+0+k+0+l+x+1+d$e$Z#]#d+^+f$,@m@n#o@m@j@j@S@P@j@j@n@j@P@n@P@P@R@j@P@k@P@P@j@j@n@k@j@j@j@j@n@P@p@k@P@n@j@j@k@j@j@j@j@n@k@j@j@j@j@P@n@P@k@n@k@j@j@j@p@p@P@j@k@j@P@k@k@j@j@P@j@j@j@j@g$k@k@P@P@j@k@j@P@j@j@k@k@o#p#q#5#6#a#^@w+o+:+_$w+O+I+Z+W+`+s+o+c#B@h$i$f###a@j$]$<#L#;$L# ",
+" , ^ 8 + # # : # # - ; ; - % # : , : ^ 9 + # * ; : - * - - # - = * = * # # # # # # # # ; # - ; : ; - , - # # # # # - # # # - 6 6 # # # # ; # # # ; ; # . . # # ; # : - # $ f + ; ; # # # : = = = * # ; # # ; + # , ( , ; # # # ! e , 6 , , , # # % % # # # # # ; : = # ; - - e 6 , - ; ; ; - - ; # - ; e , - , e e # * = # ; : % ; e , - ; , 6 , , : , : - ; ; - ; 6 - # = % % # # # - % - e # % - % # % ; ; % # ; - ^ # 6 e , # # # # # # : : - k@V@j@k@j@j@j@j@k@j@k@j@j@n@S@P@R@S@P@j@j@j@$$(@k$l$m$c#s+o+h+i+W+x++@l+B@r#n$4@|+o$h+E#>#n#W@o@j@j@j@j@k@k@j@P@j@k@P@j@j@P@j@j@P@j@n@n@P@R@j@o@p@n@S@j@j@j@n@P@j@j@n@k@j@p@n@S@k@j@j@j@P@j@k@k@k@P@R@j@k@j@j@n@T@j@n@k@P@P@k@n@P@p@l@n@P@j@p@n@P@n@P@n@n@j@p@j@j@p$_@q#F#k#(#B@G#9+Z+W+`+c+s+O+I+s+o+h+C+r#/@q$)#|$r$;$j$j$s$|$|$|$t$ ",
+" % : ^ e * # # - + - * = # # # ; - - # # ; % # : # % = ( ; : ; = % ; - = # # # % % # ; # # - - e , # - , # # - 7 - # ^ * # ; e # ; # # ; ; ; # - # * = # ; # * = # : ; # # % - ; # # # ; - | # - ; # * = - - , : : 6 e ( , # # - - # : : # # ^ # ; ; # # # g ; : # $ ; = # - : ( , - : 6 - # ; e : : ; : - ; ; # # * = , 6 # - % : # : # ; : : e # e - ; # , e - # ; * + - # # # ; : ; : ; ; # * * * * * # # , # : # - : ( , , # # # , | - # # k@p@9#j@n@k@j@j@j@j@j@n@k@j@n@j@j@j@k@j@P@j@k@j@p@6$u$v$q+k+0+m+5+h+[+^+9+Z+|@r#c+c#w+!+P##$>#U@U@j@~#m@j@k@j@j@P@k@j@k@n@P@p@k@n@l@n@k@j@P@j@j@k@j@j@j@j@o@p@T@R@n@j@n@n@j@j@P@j@j@P@j@j@j@n@j@k@P@Q@j@j@j@o@p@j@k@n@S@P@j@p@n@k@j@k@n@k@j@j@k@j@j@j@P@p@p@k@m@k@i@j$-$w$V#B#O#`+c+I+s+o+h+i+W+w+c+s+w+.@a+|#J#)#x#r$;$<#]$r$;$|$x$|$`# ",
+" * % 9 # # ^ 8 = # - % * * # : - , - > # ; : - * * ; - 6 e 6 = ; 9 + # ; * * * * * * * # ; ; : : : 6 : # # = # # # # # # # , 6 : - - # # # : - # * # # - * = = * # # * * * - : : - * * - + # - - # * = # : 6 # # # 6 e 6 # ^ = : - - - # ^ 9 ; : ; ; # # # : : ; e : = # # # ; # | - 6 # ; : 6 e + # : # : : # ; - # e - # * # + # 6 ; # - - e - 6 e 6 ( 6 6 : # * ; * # : ; : : ; : ; : # , # # # * ; # # # % # e 6 e , , e e , e - - - % j@p@g$j@n@j@P@j@j@j@R@j@j@P@j@j@n@P@j@j@o@P@p@P@n@R@j@y$_@z$<$A$2$W+m+h+w+O+I+s+o+h+Z+W+m+V+!$~${$s@/$U@o@j@R@j@j@p@Q@B$P@n@k@j@j@j@j@Q@n@S@j@l@j@j@n@k@j@j@j@k@k@j@n@k@P@T@k@n@P@p@k@P@p@k@k@j@n@j@n@P@j@j@j@j@k@P@P@P@k@j@j@k@j@P@R@j@n@P@R@j@j@j@j@j@j@l@j@P@j@m@p$_@S#4#C$4@^+o+h+i+(+k+0+m+s+o+h+i+W+w+.@f@X#D$p#E$;$s$;$`#;$;$`#`#L#L# ",
+" # # ; % % + + * ; ; # : * - ; % # : * * # # # # * : - # - # # # # 6 # # # - : % # e ; # # # e ( e # * , - - # # , , ( - # # # , , ( : ; + * # ; # = % ; # # , : : # # - # # # ; : - - # # % # : # 6 : , # # 6 , 6 # # ; # # # | - + ; : # # ; - = > = # e 6 - # , $ ; # # 6 ( ; 6 , , - # - 6 : # ( , e , 6 , , # # # = # ; : : ( , ( 6 # # : : ( 6 - : : ; # ; - - ; ; : - - - # # # # : + * : : e * ; : 6 e 6 - , 6 6 # ( - # , j@j@m@j@j@j@P@Q@j@j@n@k@p@n@P@k@9#P@R@n@S@k@j@j@n@P@R@m@k@U@u#.$h@c$f@I+)$k+w+(+k+0+m+s+o+h+1+~+q+3$6$;$F$O@m@k@j@k@l@j@n@n@P@R@j@j@P@j@j@j@j@j@k@j@j@P@k@j@j@j@j@p@n@S@P@n@P@n@k@j@j@k@j@P@k@j@R@k@9#P@R@n@S@j@j@j@S@S@P@P@k@j@P@j@j@j@n@n@P@j@j@P@j@Q@k@k@9#j@k@j@j@G$-$H$I$A#8+J$9+Z+W+5+o+k+0+k+0+m+5+h+ @K$L$=$R#j$|$r$r$`#L#L#|$;$;$`#`# ",
+" ^ % 9 % = = * ; # # - - # * : - = = ; ; # # ^ 9 + # : * * - - # - # - - ; % % # : # , # ; ( e - , , - ! - e # # , - ; - # - 6 , - # : ; # # * + % # : : : # # # - # % = # , ; : ( : * * * : # 6 , 6 : : ; - 6 ; % # # # # # # , = % | ; - - ; % - # # = : e , ; # # # ; # 6 - # ; - # - : ; # - # : - # : ; ; - # = , 6 # : e : 6 - ; ; ; # # ; # # > # ; - - # - - # - * + : ; # # - - # # , # = , 6 : # , , # # : - # # # , M$k@j@9#j@k@j@n@P@j@j@j@P@R@j@p@n@S@P@j@S@P@j@n@j@o@j@P@j@P@m@U@'$>#(@k$1$2$W+}+C#5+o+:+1+k+0+m+(+c#1+d$f$,@m@n#U@l@j@j@j@j@n@j@P@j@n@P@p@k@j@j@j@n@n@P@j@n@j@P@j@k@j@j@j@j@n@P@R@P@n@S@j@j@k@k@n@P@n@S@P@j@n@k@j@k@j@k@l@P@j@n@S@P@p@k@P@j@p@p@Q@9#P@p@k@n@k@j@m@j@k@k@$$v#N$I$A#C#9+O+I+s+o+h+1+o+:+o+:+1+5@%@>@O$P$F#Q$r$;$s$j$`#E$E$E$;$;$;$`# ",
+" s , # ^ = = # * * % + : # # # = = # + * # - # * : - - % + # # ; : - - ; ; # * ; : : * : : 6 * ; + : e ( 6 # # ; # # ; # # , , # e e ( - # : - - # # # # # # # - : : ; 6 : : - - ; # # # : , - : ; , : 6 * ; e 6 # # : : # ; ; ; : - # - # # # - # # * - , B 6 # # # # # : # # : : e e # : - % : = : : # # = * - # e - 6 # ( 6 6 6 : 6 # # : : ; * % # : * - * * + ( ; : ; # ; : : * # - e # * - e - = # , : - - : e : ; # U@j@j@o@p@T@j@P@R@n@S@j@j@j@k@j@n@j@p@n@j@p@n@S@P@k@m@j@j@n@j@k@j@y$9@6$u$A@f@5+(+k+0+m++@o+:+1+Z+W+l+B@h+R$E@S$!#O@j@k@j@n@S@j@j@l@k@j@j@j@n@9#P@p@p@Q@n@S@P@p@k@n@k@j@p@n@P@T@j@n@k@j@k@j@k@l@P@n@k@j@p@n@S@P@R@S@P@j@m@p@j@n@k@j@j@j@n@j@n@j@P@n@P@j@O@k@m@k@p@m@p$r$S#T$U$3@4@m+=@1+(+k+0+m+8+1+W+`+c+Z+/+g+V$W$-$X$Y$Z$j$;$L#,@|$`$ %`#|$j$.% ",
+" ; ^ + * * * # = - * = % + # ; e # * * : ^ 9 + : : - # - # - # # # # , ; - # # - - - # ; B * 6 6 - # , , e : # , # # : - ; , ; 6 ; * # # ; : # - # - : ; : , 6 - - # - - : # # ; - - , , - # e 6 - ; - - 6 ; * # e 6 ( e ; # # - = # # # ; # ; = # | - , , , ; : + % ; * # 6 e ; 6 , ; = - ; # # ; * * 6 ( ; : ; ; - : ; - # 6 ( ; # : : - = * : - - # % * * - 6 e 6 - - - ; : # # , * = | ; # # ; , ; * = % # 6 # # k@V@j@j@k@k@j@j@j@n@k@j@j@P@j@j@j@j@k@j@j@k@j@j@j@j@P@R@j@j@j@j@n@S$F$p@+%(@$#@%:+I+H#:+1+c+Z+W+`+s+o+x++@^+#%$%##U@j@U@l@k@k@p@k@k@k@R@j@P@n@S@P@j@n@j@P@j@j@j@j@j@P@k@j@j@k@P@Q@n@S@P@R@S@P@j@m@n@P@R@j@j@j@j@j@j@k@j@j@k@j@n@P@R@j@o@p@T@n@S@P@j@k@n@j@j@j@k@m@k@N@G$J@H$%%<+&%b#Z+W+`+5+o+:+1+s+o+h+i+W+j+e@4$*%=%Z$-%;%;$>%;$`#|$`#E$,%t$E$E$t$ ",
+" # % # + # ; : ; - = % : # # : # # , - - # * # - : - # # - # # % + ; ; e # ; - - # - ; - # : ; # , # 6 e 6 ( e ; : 6 e + ; , # 6 # * - # # > - ; 6 ; : 6 : : # # - # # - * = : * * - # $ , : , : - ; ; - # * # : ; ; # 6 # # # ^ : # # ; # - # % # # # # , , - - - # 6 , ; 6 e 6 6 : # + : % # - # * # ; 6 e 6 - * = = = : e e 6 : - - - = ^ 9 + # : * = # ; : : 6 ; - : # ; # , , + # , : , # ; , # * * | * : # j@j@j@j@j@P@j@j@p@n@S@P@n@P@p@k@p@k@j@n@k@j@j@j@j@j@j@P@j@P@j@j@j@j@j@j@k@j@m#_@4#'%m$)%d+6+I+s+o+h+k+0+[+^+9+!%~%{%j$]%j@j@B$k@T@P@l@k@j@P@p@n@j@p@n@S@j@n@j@j@j@o@p@n@S@P@n@j@k@j@j@j@j@j@k@j@P@k@j@P@j@j@k@P@k@j@p@j@p@n@S@j@n@n@P@j@k@k@j@j@j@m@j@j@n@T#j@n@m@k@k@$$|$N$^%]@c#w+I+s+o+h+(+1+k+0+k+0+m+5+h+/#A#/%(%J@@#Z$r$r$;$|$E$E$;$;$E$|$`$ %`# ",
+" ^ % % % * ; 1 # # : : = # # # # % # : * * : = # # % # # * : + - - 6 * = # ; # - # - # # ; , 6 : - e e ; # : - , # = # - 6 ( ( e 6 + * % # : - # - - # - # # * # - # # ; : - % * * + ; , e - ; # , e : # # * : * ; ; 9 + # # - - - # # - - # # , - ; - : # - , ; : , , : : : 6 # = # : * * - # # # # : : 6 : # = = # ; - : ; 9 + # - - - # # f * * # # ; # # # - = - ; : ; # # : , ( : , # # ; , ; e # % , , m@j@k@k@n@P@p@k@k@j@j@j@n@k@j@j@j@j@j@j@P@R@k@n@P@p@j@j@P@P@p@k@P@j@n@P@p@j@P@m@m@v#_%v$>@o+h+i+k+0+m+5+:+w+O+c#w+W+:%L@U@k@j@k@l@j@R@P@j@k@j@j@n@k@j@j@j@P@n@j@j@P@j@j@n@j@n@n@j@n@k@j@j@j@j@n@j@P@j@j@k@j@P@P@n@j@j@j@k@j@j@j@j@p@p@Q@9#n@j@j@m@o#p@m@T#T#m@O@m@m@j@0#<%[%}%f@|%W+`+(+k+0+m+5+5+o+5+o+:+1+5@6@]++@1%-$2%;$<#j$j$<#`#|$`#;$;$L#|$E$,%L# ",
+" # ; % v - # # # = # % # % : ; * : - - % * - : ; * * + + + * - - - = * ; = * # % # # # - , ( ( , # # : ( - # , ; - : # - ; , 6 e # # * : # # # 9 + # ; : * # # ; - ; # # - * ; : - * - , # 6 ( 6 ; ; - - - # : * ; # # + - - , # ; * | ^ ; ( , # - ; 6 ( - ; : ; - - # : # , # # # : # ; : ; # - - # # - # - % ; : : : # # # # - # # % = ( # # - # ; # # - - ; ; : - # ( # # 1 , , , : - , ; # * : - - , O@n@k@k@n@k@j@j@j@P@j@o@j@P@R@j@o@p@k@S@n@P@j@P@k@j@P@j@p@Q@j@j@P@j@n@k@j@j@j@j@j@j@m#>#c$2$0+m+h+o+:+1+s+s+Z+w+W+`+.#1@2@u#W@j@l@j@j@~#l@j@R@j@o@k@j@P@j@P@T@k@n@P@p@k@P@n@j@n@S@j@P@R@j@o@j@k@k@j@j@j@j@j@j@j@k@k@P@P@j@n@k@j@j@j@j@j@P@n@S@m@m@p@j@~#j@m@p@j@U@k@k@N@;$-$3%4%5%s+o+h+5+o+:+1+9+Z+ @`+1+s+Z+j+e@s#t#u#Y$r$j$s$|$>$`#t$E$E$`#`#`#|$E$,%E$ ",
+" | ^ # + % * - = = * : : : ^ 9 - - # ; e ; : % # # # = ; # - - ; # - * + ; * - # ; # # , 6 e # # # - # # # - ; + ; * ; ; : e ; : ^ 9 + # , # # # # - ; ; * : * * ; # * # - : # % = * # : ( 6 e # # - - # # # # - # - # ; : - % g ; e - # # , e - ( 6 6 6 ( # ; : ; # - ; # * * - : : ; % % - # : % ; : * * - # # - # , - - : = , { 8 # , # ( # # * * # - # - - ; # # * - , - - # # # , ; : - : : | P@p@j@j@P@n@P@R@j@o@P@p@k@P@n@n@j@j@k@k@j@9#P@R@n@k@n@P@p@k@l@k@n@P@p@k@P@R@j@o@R@j@j@p@k@ $h@l$f@c#8+1+s+k+0+m+I+s+o+h+e@f@g@L@i@W@O@~#k@k@j@k@k@k@j@R@P@p@k@j@P@n@k@j@j@j@n@P@n@9#P@R@n@S@j@k@n@k@S@j@j@j@j@j@P@k@j@R@j@j@j@P@n@n@j@j@P@j@j@n@j@j@m@~#o@k@T#p@m@V@k@k@0#S#T$U$6%7%6+k+0+m+[+^+9+Z+W+`+w+h+i+W+j+/#(#/%8%J@Z$;%]$r$<#L#L#|$`$ %|$;$;$|$;$|$L# ",
+" # # % * % = % * ; # # # * * : * # # ! # : * - # = + # # # ; : : # ; - # - ; ; ; : ( e e # # : # # ^ - # ; e 6 ( 6 ; # - - # : : 6 # # # # ; # ; ; # - - - % : ; # : # ; : * ; : ; # ; # - # - # ; ; # ; : : % ; > # 9 + # # , , , 6 ( 6 ; ; : # 6 6 : e e # : ; e : # * ; - : : - ; - - # - * - - * ; : * ; ; # - - # ; * = , ^ g % , e # # * ; # # ; - - # , # = , , # ; ; # # , - : # # : - j@k@j@j@n@j@P@j@j@k@j@j@j@j@Q@P@n@l@n@k@o@P@j@n@P@n@k@j@j@j@P@n@k@j@j@j@P@j@j@k@k@p@9#j@j@x@6$k$v$q+4+Z+5+o+:+<+o+k+0+m+5+q+C@'#)#{#j@o@m@j@T@P@l@k@n@k@j@j@j@j@n@P@R@j@o@p@T@n@S@P@j@n@k@j@j@n@k@P@R@n@S@j@j@k@k@n@P@R@j@j@j@p@T@k@n@P@p@k@P@n@m@j@m@j@k@9#m@U@j@U@U@k@>#H$%%9%a#5+s+o+h+i+w+O+I+s+o+h+`+m+5+h+/#]++@1%-$2%j$|$;$j$<#L#L#|$E$`#`#|$`#L#`#`#`# ",
+" % # % * * # ; # # # * * : - # # ; - - % * = ; = # * = - # : # ; : # # e , , , - # 6 6 # # # # : : # # : ; ; # 6 ( ; : ; # # # ( 6 : * * * * * # # : # * # # # : # ; - ; * # , e # : = - - - # # * : ; ; * : % # : ; , # , # : : ( 6 , 6 : : ( ( 6 e ; 6 , : ; : % * ; : : 6 - # : ; # # # - % * ; # ; - # ; : ; ; ; % + : : ; # ^ - = % * ; * # : : : # , : , , ; # # ; ; # , 6 | # - , j@p@j@j@j@k@n@P@p@k@j@P@j@j@P@j@j@j@j@Q@k@n@j@p@n@S@n@P@R@j@j@j@n@P@R@j@j@n@n@j@j@n@T@k@k@j@n@V@@###z$0%f@a%8+1+|+8+5+o+:+1+w++@+@B@{%j$0@n#l@l@j@p@j@j@p@Q@9#j@n@k@j@P@j@j@k@k@j@j@j@p@n@S@P@p@n@S@P@j@n@k@j@j@j@n@l@P@n@j@j@P@j@j@P@n@k@j@j@j@n@P@j@m@j@m@~#m@j@U@U@!#Y@J@H$b%]@|++@(+k+0+Z+W+k+0+(+k+0+o+h+i+s+6@]+s#t#u#L#j$;$j$;$`#`#L#L#`#E$;$t$E$E$ %`#;$c% ",
+" # % + : : - - # - > # % * # : + # * ; : : # + = = # # - , 6 e 6 6 6 < # - - # : ; * * # - - # % + : ; : : ; ; 6 e 6 - - - # ; # # # # # * # # # - # # - ; : : - * # # ; # # : = = 0 % ; - # * ; - ; # # ; e , , : , e ; : # # : e - - 6 - - 6 ; e : # : - - # # # % # - - : : : # # # - # # # : # ; : - # # ; - # # - - # ; ; : ; # # - - - # - # # # - , # ; # | # ( ( , e # - - : m@P@j@n@j@P@n@k@j@j@P@P@p@k@P@j@j@k@j@j@j@j@j@k@j@j@j@j@P@j@j@n@k@n@P@j@k@P@k@k@P@j@j@k@B$P@n@j@k@m@,$4#c@;@-@O+4+s+c+8+1+Z+W+`+c+Z+W+:%L@U@n#U@j@k@k@j@j@j@n@l@n@k@j@j@l@j@j@j@P@j@j@j@j@j@j@k@j@j@j@p@n@S@P@j@j@j@j@m@p@k@n@P@p@k@j@n@P@R@j@o@p@T@n@S@j@T#j@o@n@U@k@W@j@E@d%b%]@|+-@^+9+Z+W+s+o+h+i+5+o+k+0+m+^+.@g+s#e%8%E$|$;$<#;$L#L#L#`#E$,@|$`#|$`$ %,%|$;$`$ ",
+" # ^ ; % ; : % : % # : % # - ; e = - # # : % # : * * - - - - # , 6 ; ; , , # + * * # # - - # : - - ; ; : ; ; : : 6 ; * : # # - - ; # # * : ; ; # = * ; : - # : # , , ; # # = = = 5 # , - * * ; # - - # : ; : # : 6 , : : , # ( : = - e : * ; ; ; : # : - , # ; * = # : * ; , - # # % + # , ; # - - # - # ; : = % # % * + # : : ; # # = # - # # , ( # # , # ; # | # 6 # - # : - - m@o@p@k@j@j@n@P@R@j@j@n@j@j@j@P@k@P@R@j@o@j@j@j@P@n@k@p@n@S@P@j@p@j@j@j@j@j@k@j@Q@j@j@P@P@k@j@P@j@j@p@m@'$>#'@>@)$(+f%Z+O+I+s+o+h+i+s+j+1@2@u#S$!#O@k@l@m@m@j@j@Q@k@S@o@k@B$j@n@P@p@k@k@j@j@k@j@n@k@j@k@j@j@P@p@j@j@j@j@j@P@n@k@j@j@P@j@j@P@j@j@k@k@n@S@P@j@m@m@j@U@m@Y@J@H$g%h%3@4@G#^+9+I+s+o+k+0+m+5+C#5+o+:+ @H#}@L$=$R#|$|$r$;$|$|$`#L#L#,@|$<#|$;$|$E$,%E$`#L#E$ ",
+" # # ^ % + ; % * : - - - # * : # = * = # * : - - % * : + : # * , : ( ; : # # - # # - # # = - # # - - # # - # # # 6 : # - ; # # % * * - # - # = . - - # # ( 6 e - 6 - 6 # # = = * # - # # # # # - # : ; ; : # 6 : : , : ; # e - : 6 6 # # # # - # # ; 6 ; ; - * + - # - # % + : ; ; * : : : ; - # - # : : ; * * * : # # ; * # * # # % % ; : ; # # - , : : # - , , 6 - # - * # - U@p@j@j@n@j@j@j@P@j@j@k@j@j@o@p@n@S@P@j@j@P@j@j@j@n@P@R@j@j@j@j@j@P@k@k@P@j@j@n@P@j@j@j@n@P@j@i%P@j@j@~#j@n#j$1$2$W+}+$@k+w+(+k+0+m+(+k+0+f@g@L@i@M@j@j@n@j@m@~#j@n@n@o@j@j@n@k@k@j@j@R@S@P@j@n@k@n@Q@j@P@n@k@j@j@j@j@j@j@j@n@P@k@n@P@p@k@n@S@j@j@n@j@j@j@k@S@9#m@k@X${#Z@h@b%{@&%W+<+j%O+I+(+k+0+o+:+1+5@$@8+1+s+/@k%l%m%F#Q$r$;$<#j$L#L#L#`#E$<#;$;$|$;$;$|$`#>$<#`#L# ",
+" e 8 # * ; : # + # = = ; ; : * * # # ^ 9 + # * ; % - # ; # - e , , ( # , ( # ^ - # - - | # e - # # - : # ^ # 6 e , : # # * - * ; % = ; # 0 % # # , # # - # ( # - : ; # = = # ; : # # # - ; - - * # # # # 6 e 6 , , 6 ( - e ( : 6 # # # * * - - 6 ; e ( * : - # ; - : ; - - ; ; # - # - # - # : % : : - # # # : % * : : # - ; * % - # # # # # , # - , # , , 6 : ; # - : ; k@j@j@n@n@j@j@P@j@k@j@j@j@j@P@j@j@P@j@n@j@n@P@k@n@k@j@j@R@n@S@j@j@k@k@j@n@P@k@9#P@R@n@S@j@p@n@p@j@n@j@j@j@k@j@y$h@c$l$m$W+}+C#5+o+:+1+5@n%+@q+C@'#)#_@]%j@j@k@j@j@j@p@p@j@p@n@S@P@P@j@j@n@j@p@n@S@n@k@p@n@S@P@R@j@o@j@R@n@S@j@n@P@n@k@j@j@j@j@j@j@n@S@j@j@j@m@j@k@k@o#p#q#5#o%3@4@w++@s+s+w+(+k+0+:+1+s+Z+/+9+Z+_+C+X+9+p%-$X$Y$|$;$|$|$`#E$L#,@|$;$;$<#L#|$|$;$`#|$<#;$c% ",
+" # # # % ; : 7 # % , # - - % * * ; - % - # # * : # - # ; ; 6 # , ; # ; , ( 6 # # # % : 6 * + + : # * : : , - 6 - # - # : * ; * = = = 5 # * : : * = # 6 : e e # ; - % ; : : : ; # - # # - - + : % ; * e : - : : : - - ; - 6 - ; ; : ; ; : ; , # , ( , ; ; - # - # # # # - - - # = = # , ; : * : # : # # , - # ; # : ; : ; ; * * % # # ; - - = - , ; # # # # # # - # U@j@9#P@T@k@n@P@p@9#P@T@k@n@P@p@k@j@p@n@S@j@p@n@S@n@S@P@j@n@k@j@k@j@k@j@j@p@n@S@P@j@n@k@j@j@j@n@k@j@j@j@j@P@j@R@j@(@k$u$c$f@I+$@8+1+s+o+:+`+c+e++@B@:%L@U@k@l@l@j@P@j@j@j@j@p@j@j@n@P@k@j@j@k@j@j@n@P@R@j@n@j@P@j@j@k@j@n@k@j@n@S@n@P@R@j@o@p@n@S@j@j@j@P@l@j@j@j@p$N@q#F#k#(#&%W+W+`+c+s+o+h+5+o+:+1+5@%@[+^+9+s+ @K$q%r%F#L#s%r$<#`#|$L#,@|$|$<#L#;$;$;$`#e#j$;$L#E$`#;$`$ ",
+" , % # = + - % = # = = # * * ; : * * > # ; % * * # > # ; - - - ( e e 6 ( ; # ; + # - # : ; * ; * # , , : e , : - # * # - # # # = = * * # # # - ; ( ( 6 e ; : ; ; = - # # : # - - : # - , : : * : ; # - # : # e # ; , - 6 # - ( ; e e , , : , : : 6 e ( , # - , - # ; - - ; # % ; : : : # # ; ; * * + : ; # ; # ; ; # ; : 6 = % # # : ( # % # , , # # # # # = - # j@k@j@k@j@P@n@k@j@j@k@j@P@n@k@j@j@j@k@j@j@P@j@j@n@9#n@j@p@n@S@P@R@S@P@j@m@j@j@n@j@p@n@S@P@k@j@P@n@n@j@j@j@P@j@j@j@j@N@m#(@k$l$m$c#H#[+^+9+o+h+i+g+G#.#1@2@u#W@W@O@l@j@j@j@j@j@j@j@j@p@n@S@P@j@j@j@P@n@P@j@j@n@P@p@k@j@p@n@S@n@S@P@j@p@n@S@j@k@n@k@j@k@j@j@j@m@p@j@N@Y@i$t%V#B#O#w++@o+h+i+k+0+m+8+1+s+Z+h++@<+o+^#k+(#u%v%Z@Q$X$Z$r$r$j$<#|$<#;$`#`#`#;$;$;$j$j$;$L#L#t$|$|$E$ ",
+" % v - - % - = - % % * : - - % % # : * # - # % # : * : : , : e , : # e # * * * ; ; - # * ; ; 6 # : , ( 6 6 6 - # - % # # = = # ; : # # - - - - 6 ; 6 e 6 : # # - - + + # - # : ; - # # # * ; - # ; , e e ( , 6 ( ; 6 6 6 6 ; ; : # : 6 # # # 6 e 6 6 e 6 ; * = * : - : ; : - # - # # ; ; * # # # - ; = + - = * : : - : = - * $ e ; # # # ; e e - # # # # j@j@j@j@P@n@P@R@j@o@n@j@n@P@R@j@o@p@k@n@P@p@k@l@S@P@j@k@j@j@j@j@k@n@j@P@k@S@k@j@k@j@j@j@j@j@n@k@P@P@k@n@P@p@j@o@j@j@l@j@P@6$u$v$q+.@w+W+k+s+O+I+<+j%e@f@g@L@i@{#j@~#k@S@p@k@j@j@P@j@j@P@j@j@k@n@P@p@j@p@k@n@k@j@j@P@j@j@j@j@j@j@P@j@P@n@j@p@n@S@P@R@S@j@j@j@m@U@k@k@Z@,#8$4@^+C+(+k+0+m+5+o+:+1+w+(+Z+W+`+c+s+s+v@+@|@w%x%b$Y$;%<#;$<#;$|$;$;$;$|$;$;$;$|$j$r$;$|$`#E$|$`$ %`# ",
+" 1 # + * * * ; = + = # 9 + # 9 * : - - % * # % # # - + ; # e : - , e e # - % # ; # - % ; # : , , # : ; # 6 : * = * * # - % ; # : : : ; - # : * ; ; : : 6 ( # + : : # # # : + ; - - # # # # - - - : ; ; # : - ; # 6 : - : : e ; : # 6 : , # # 6 , ( : # ( - = + # , * ; ; # : # - # # : 6 # # - ; ; ; - % # # - ; , e ; ; # # - - * , # # # # e ( # # # # m@p@k@n@P@p@k@P@j@j@k@j@j@j@P@j@j@j@P@n@k@j@j@j@n@j@p@n@n@k@j@j@P@n@k@j@P@j@P@R@j@o@j@j@j@p@n@S@P@P@j@n@k@j@j@j@k@n@k@j@j@~#k@>#z$0%f@m+o+h+k+0+(+s+s+Z+(+C@'#s@0@U@j@k@j@T@j@n@P@p@k@P@p@k@P@n@k@j@j@j@j@j@P@j@n@P@p@k@k@j@j@j@j@j@j@n@P@k@j@j@j@j@j@k@n@k@j@m@j@H@q#5#6#a#8+J$C#5+o+:+1+8+1+s+Z+C#5+s+o+h+i+W+ @e@d+y%z%Z$;$`#r$j$;$<#<#L#|$;$<#|$;$;$;$j$;$`#`#L#,@|$|$E$,%`# ",
+" $ % + # % : * = * # ; - # ^ 9 + # * * * * = ; # % - # # = - ; ; : # : ; # : # - * : # % - # - # # ; ( ( # - % % % - = % # # : * ( ; : # # - - - # , ; 6 : # % + ; ; - # # % ; : ; ; # ; : # ; # # # ; - # # ( : ; 6 6 , : : # 6 , 6 : : ; - # , 6 , ; : ; ; # ; - # : : ; : ; # # # ( 6 : - - - # : - # * # # # # $ ^ ; - - - , # , # # B ( # # # m@P@P@n@k@j@j@P@j@j@j@j@j@P@j@l@j@j@j@n@P@R@j@j@j@j@k@j@j@P@j@j@P@n@P@R@j@P@k@9#P@j@p@S@j@k@j@j@j@n@l@n@P@R@j@j@j@n@P@R@j@j@j@U@P@4#c@+$k+0+m+h+i+i+9+9$]+`++@A%^$(@B%U@l@l@T#P@k@j@j@k@j@j@j@n@P@R@j@j@j@P@P@p@k@k@j@j@j@P@j@P@j@j@n@n@k@p@n@k@j@j@j@k@j@n@R@m@j@r$p#C%(#B@G#+@/#j%8+1+Z+Z+/+c+s+o+h+Z+W+`+c+5+h+x+(#/%F#h@<#r$|$r$D%`#]$;$`#e#j$<#L#|$|$|$;$`#L#L#|$<#;$|$E$;$`# ",
+" % % - # # + * # # = % - # # ; - # # # > # ; = + # # = # , ; , # 6 6 * : : # # # * * ; # # # ; * # - # - + % * * = * : - # e ( e ( , # - - # - - 6 ; e ( : = = # # + * - - - ; ; ; : ; ; - : ; e , 6 , , 6 , ; ; - : , : , - : ; , : 6 : # # : , , 6 ; - * - 6 % - - , e 6 - - - # ; 6 , # # - # : + # * = = # + * # ; # # * e 6 ( e # - ; # | j@P@p@n@P@k@n@P@p@j@j@k@n@P@p@k@9#P@R@n@P@l@n@j@j@P@j@n@k@j@k@n@P@p@k@P@j@p@n@S@P@j@n@k@j@j@j@j@j@j@j@Q@k@n@P@R@n@S@j@k@j@j@j@P@k@U@'$>#A@;@-@1+m+w+ @I+s+o+h+(+|#E%a$S$n#O@l@j@P@R@j@j@R@j@o@j@n@P@T@k@n@P@p@j@j@j@R@j@o@p@n@P@p@k@P@n@S@P@j@P@k@j@j@j@k@j@m@p@j@7$q#~@C$O#1+`+c+s+w+W+`+s+o+h+i+k+`+I+s+o+h+i+W+s+%@+@1%-$@#r$;$e#<#;$j$j$;$j$j$;$|$<#;$e#`#<#|$L#L#|$;$;$;$`#e#L# ",
+" e # # g + - # # - % % # : # # # : , : = ; # + * # = = # : # 6 # # # # % # : = # , # # : + * : * = : - ; # # # - # : + , 6 # e ; ; : ; ; : ; , # , ( , ; e : ; e # # - # - - - : * # # - # : - # : : : # * ; # # , 6 ( ; - # e 6 - = = - ; - : # # # + % # : * * - # $ ; + * + + - # ; # * * # - + * # , # * # # ; # # : 6 6 6 ( e e # # j@j@j@j@j@P@n@k@j@j@j@P@n@k@j@j@j@P@j@n@k@j@Q@k@n@P@p@k@P@R@P@n@k@j@j@j@P@j@j@n@j@p@n@S@P@j@j@P@j@j@k@k@j@n@P@j@n@k@j@R@n@S@j@n@P@j@p@m@'$J@'@>@:+k+w+(+(+k+0+m+5++$F%G%##{#j@j@j@P@j@R@P@j@j@p@n@S@j@P@n@k@j@j@j@o@p@T@j@k@n@k@j@j@j@n@j@j@n@j@n@k@P@j@P@j@m@R@m@k@'$w$V#A#8+I+h+i+h+s+o+h+k+0+m+s+o+h+(+k+0+m+5+w+]+H%t#u#I%j$a@]$<#j$;$<#;$r$r$;$<#;$;$;$|$;$;$|$`#E$`#|$;$;$j$j$,@ ",
+" e ^ + : - - # # # % * * * ; ; - - # ; e # # # ; - ; : ; # # - * * ; e ; ; ; ; * # # - - # = # # : # - # - * # - ( 6 6 - ( e ; e e , , : , : : 6 e ( # ; : # - ; # - # ; : # # # ; : = : : - - # % * ; # - + ; , 6 , , : ; + : # - # # : 6 = ; * * : - - % * * + ; ; + ; # + : : ; # # # # # # ; , - ^ # ; e , # # # # # - - e ( ( j@m@k@j@o@j@n@P@R@j@j@P@n@P@R@j@o@p@p@n@S@P@j@P@n@k@j@j@j@j@k@n@P@p@k@n@P@p@k@P@P@k@j@j@j@k@n@P@p@k@P@n@j@n@j@p@n@S@P@j@n@k@j@n@k@j@k@j@U@m#,@1$2$W+}+C#5+I+o+:+1+Z+W+J%~%{%j$]%j@k@n@j@k@l@j@j@P@j@j@j@n@P@R@j@o@p@j@j@j@P@n@P@R@j@o@j@p@j@n@k@j@j@o@j@j@k@k@m@j@7$K%4#C$4@C#w+m+k+0+m+k+0+m+W+:+(+k+0+m+5+o+:+1+w+H#}@L%*%J@r$r$;$s$r$j$<#<#L#|$|$|$;$|$;$;$|$;$;$`#L#L#L#e#;$<#`#;$|$ ",
+" s % = = * - % * # % * ; : = # ; # : # # # : * * - ; % - # # # : , : ( : # * # ; # # # = # % : ; - # : : - # : ( ; 6 , : 6 ; ; : # : 6 # # # 6 # = = ; - ; # # - # # # # ; # # # # # # # # - # # - # # - # ; : : * 6 : e : % ; - - ; = = # * ^ 9 + # = ; : - * - # : ; # # - : * * - # ; = # # - , # ; e , , # # # # , # # # U@j@j@k@j@k@k@j@P@j@n@P@p@j@P@j@j@k@k@j@j@j@j@j@n@P@R@j@o@j@P@n@k@j@j@j@n@j@j@j@j@P@j@j@o@P@n@k@j@j@j@n@n@j@j@k@j@j@j@p@n@S@P@n@S@P@k@j@l@j@U@ $h@c$f@I+$@8+(+k+0+I+s+o+:+W+:%L@F$k@l@j@m@k@P@P@P@P@n@k@n@P@j@n@j@k@k@j@j@j@j@k@j@j@j@P@j@p@n@S@P@R@j@k@k@j@S@9#j@U@'$M%I$A#w++@W+]+o+:+1+o+:+1+o+^+9+o+:+`+c+1+s+w+/@k%l%m%N%2%Z$`#|$<#r$`#]$;$`#e#;$|$;$|$;$<#L#|$|$|$L#L#`#`#j$<#;$;$J@ ",
+" | # # * * * = % # # - - % = - , # ; : - - % : * # # - # # # ( e ( e : ; # : + + + * * # # # # - - ; : ; # # 6 6 : e ( e ; : # 6 : , # # 6 # * * # . # = * # # % # # # * * ; # # # # # ; ; # . . % + : # - - # ( , : : - - ; ; - ; ; ; ; - $ # = * # ; : % = * - - > # : - - % * = = # , ; : ; : 6 , e ( e ( e # - : # U@j@j@n@P@n@n@j@j@j@P@j@j@j@j@l@j@j@n@j@j@j@j@j@k@j@k@j@j@k@j@n@P@R@j@o@j@p@j@o@n@P@p@k@j@j@n@P@R@j@o@p@p@n@S@j@P@n@k@k@j@j@P@j@j@j@j@R@j@o@p@j@j@(@k$l$m$c#H#5+o+:+(+k+0+m+.#1@2@]$U@k@j@j@j@k@k@l@j@P@j@j@p@n@S@j@n@j@P@j@j@j@T@k@n@P@p@k@j@j@j@j@k@k@p@n@l@O@m@U@_@D$O%^@C#`+(+k+0+m+s+h+1+h+k+0+m+I+s+s+h+i+W+j+/#c#+@}$-$Y$;%r$|$|$e#j$j$j$;$j$j$<#L#|$|$;$|$<#e#j$;$`#E$L#L#<#>$|$|$c% ",
+" = - ; : - : % + = - > # ; - - - - - # * * - = # # # # # . . # : - - # # - ( # # # * * ; : : = - 6 e 6 - # : ; ; e , , : : # 6 , 6 : : ; : : # * = # % + ; * * * : # * ; = * * # ; + # # * = # ; : : ( , ( 6 , ; : - ; ; # , e - # , # ; # * : # # : - - % - - * % # 9 + # * = = = ; # : # : ; ( ( e 6 # - ( - : ( 1 N@m@j@j@p@n@k@S@o@n@P@p@k@j@o@k@k@j@9#j@j@k@P@o@j@k@P@n@j@n@T@k@j@P@j@j@k@j@j@j@n@k@j@j@j@j@j@n@P@j@j@k@k@p@n@j@j@n@P@R@j@j@P@p@n@S@j@j@j@j@k@k@P@j@9@6$u$v$f@.@8+1+x+5+o+:+1+e@f@3$e#u#W@j@j@m@m@j@j@j@P@k@j@j@j@j@k@n@P@p@k@P@j@P@n@k@j@j@P@k@j@j@j@n@T@k@P@j@k@k@_@'$t@P%}@m+h+5+o+:+k+0+k+0+m+o+:+1+(+k+0+m+5+h+/#]+|+Q%R#L#;$r$;$D%`#j$;$<#;$r$r$;$;$`#e#j$j$;$;$j$;$`#L#L#L#L#`#|$`#L#L# ",
+" 9 - # : : * * # # # % # : * * - - - % ^ 9 + # % # # % : = # ; ; ; + : * * : + ; # # ; # - # ; : : : 6 , , # + # : ; : , : , - : ; , : 6 # - # * = , 6 6 : 6 - ^ 9 % * : ; 6 : - # : % # * = , 6 # : e : 6 : , , ; - - 6 ( 6 ; : , ( - # # % ; # ; 9 + # * * = % * # ; - # + + = , # ; e , ; , , , # # ! # - - : , j@p@k@T@j@n@j@n@o@j@n@S@k@j@j@j@n@P@R@n@j@P@j@p@P@n@n@P@j@j@j@k@B$P@P@j@j@P@j@j@n@P@R@j@o@p@p@n@j@P@k@j@n@j@p@n@S@P@j@P@j@P@P@j@n@k@j@k@j@k@j@n@j@j@P@j@9#>#R%S%h+H#%@[+^+9+Z+W+`+1+d$g@L@i@M@k@j@~#k@k@k@P@k@R@j@j@j@n@k@j@j@j@n@j@n@P@R@j@o@k@k@j@P@j@j@k@B$k@j@N@p$_@D$O%^@k+0+m+g+x+5+o+:+o+:+k+0+s+(+k+o+:+1+5@6@]+A#T%a$I%X$<#;$<#;$j$D%>$s$r$r$;$;$;$j$j$;$s$j$j$;$<#|$L#L#`#E$`#`#`#`#E$ ",
+" = # = - - % = * f * : - - % * * ; ^ - ^ - % # # # # = = , 6 # - - ; - # * # # * * ; - - - ( 6 , # # : : : : * # , : , 6 ( ; - # e 6 - ; - - # # e # : : ; # - # # - ; - - # + * # # - . # e - 6 # - 6 - ; - ; - ; : ; ( 6 e 6 , ; e ( : * : # * # # ; # = * = ; # : - , # = % , * : e # - # , : # # , # # # # , U@j@m@j@n@n@P@j@j@j@n@n@P@j@j@j@n@n@P@P@p@P@p@j@j@j@Q@P@R@n@S@S@P@k@j@P@k@P@j@n@k@j@P@k@j@k@k@j@j@p@k@n@P@p@k@j@j@j@j@n@j@n@j@p@n@S@P@R@S@P@j@m@j@j@j@j@~#U@G@h@U%q+.@9+w+O+I+s+o+h+l+B@C@'#)#_@k@U@o@j@k@j@n@j@j@P@j@j@P@R@j@o@p@T@k@j@P@j@j@k@j@j@n@P@Q@j@P@k@m@k@k@V%-$t@P%}@m+:+1+0+[+8+1+s+1+5+o+:+1+5+o+k+0+m+/+o+-@:$W%I%X$Z$<#j$<#s$j$j$`#|$;$|$;$;$;$r$;$r$j$|$>$|$`#|$`#E$,@|$|$;$;$;$L# ",
+" = - - + # * # , e ^ 9 + # * - : ; # > # ; - # ; # % # # e = ; ; : - # = : # # * ; # # # 6 - , : ; # - - # , - e ; ( - ; , 6 , , : , : - : ; * + * # # ; - # = * % * ; , ; K.; * # # = = # e # ; , - 6 # - ; , e e , , : , : : 6 e ( ( , # # : : ; * ; * * = = # 9 % * ; : * # # # ! , - # # e + # ^ # ; : # # 1 k@V@j@P@j@p@p@Q@9#P@j@p@p@Q@9#j@j@p@p@Q@9#k@j@j@j@k@j@j@j@n@k@j@P@S@P@j@n@k@n@R@n@S@k@P@R@j@o@j@o@j@P@n@k@j@j@j@j@j@P@n@n@j@j@k@j@j@j@j@j@j@j@P@k@j@j@j@j@j@k@U@(@z$0%f@)$k+w+(+k+0+s+o+h++@B@7#8#]$n#U@l@k@j@P@n@P@p@Q@9#P@j@j@k@k@j@j@j@p@k@n@S@P@j@n@P@j@j@O@m@R@X%H@q#4#^%^+b#[+^+9+Z+W+`+I+s+o+h+1+^+0+o+h+o+:+1+C++$`+0$J@2%Z$`#|$|$<#;$|$|$`#E$,@|$;$<#>%X$Y%j$s$<#`#L#,@L#,@|$<#;$|$;$;$L#`# ",
+" + # : % # + # - - - # # # 6 ; * * * % # : * * = # ; % # - > # ; # : # = * # % * ; , # # ( : : e : : # # - # # - - 6 : # ; : : e # e - ; - : ; * % # # # - , ; = = - # ; 7 - # * - # ; # e e ( , 6 ( ; 6 6 6 6 ; ; : # : 6 # # # 6 e 6 : # # : e 6 ( # # = % ; # ; # # - # , e # - # ; e # # , # # # # - - # | j@j@j@P@p@P@j@j@P@P@p@j@j@j@n@S@k@j@j@j@P@T@k@j@o@j@k@j@j@n@S@P@j@n@j@p@n@S@P@j@n@k@j@P@k@j@j@k@n@j@n@n@P@R@P@j@j@n@P@p@Q@P@n@j@n@j@j@j@j@j@j@j@P@k@k@j@j@j@P@j@k@9@4#c@+$W+}+C#5+o+(+k+0+m+D#l#3+3$e#(@!#U@j@j@j@j@j@j@P@P@P@n@j@n@j@j@j@P@j@j@n@j@j@9#P@R@n@S@k@j@N@j@>#w$V#B#|%c+w+O+I+s+o+h+(+k+0+m+5+Z+k+0+m+5+C+.@r#|+h$i$2%Y%r$;$D%`#]$j$L#L#,@|$<#L#|$Y%F$Z%Y%X$j$>%;$|$<#;$t$E$;$;$`#|$`#`#L# ",
+" # = + * - : : # - - # - : , # 6 : # - * : - - % * * : * * : % # * : - = % * * ; # 6 # , , 6 , ( e - - # - # ; # . . # 6 ; # - - e - 6 e 6 # - # # ; # # # - # * = - ; # - # , ; # : # ; : ; # : - ; # 6 : - : : e ; : # 6 : , # # 6 , ( % ; * - : ; - # ; : ; # # : # - # # # * = ; ; ; , : # ( , 6 6 # ( 6 | m@m@k@k@j@P@p@P@j@n@k@j@P@j@n@S@P@j@n@k@j@j@k@B$j@j@P@j@n@k@j@P@P@n@j@k@j@j@j@p@n@S@P@p@n@S@P@j@n@k@j@p@p@n@P@p@k@n@k@j@j@j@j@Q@j@j@j@n@j@j@n@P@p@Q@n@S@P@j@j@j@j@j@j@'$>#%#u@W+%@[+^+5+o+:+1+5@6@1+d$E%a$S$n#O@l@m@j@j@o@j@j@n@P@R@j@o@p@P@p@k@n@Q@R@9#P@j@n@k@j@m@k@k@K%4#C$4@^+I+s+o+h+(+k+0+m+5+o+:+1+I+s+o+:+1+5@6@a+|#J#K#|$;$r$r$<#;$j$j$`#`#|$<#;$;$`#X$i%j@j@m@_@a@j$j$;$;$L#`#`#`#|$;$|$;$`#E$ ",
+" ; - + + # # : * * : # - # # 6 e - - ^ 9 + # * ; : % ^ ^ 9 * : ^ 9 + # # # # # 6 e ( : : : - * * # - - # , ; * = # ; : : ( , ( 6 # # : : # - # # * % ; * + * = = = : e , , : ; # % : : : : # ; - # # ( : * # 6 , : : # 6 , 6 : : ; - # , # * # ; : * ; : ; : % # : : # - ( - = | , e , , : # - , ; : : # , j@p@m@j@j@k@j@m@j@p@n@n@k@P@p@n@n@j@p@n@S@P@j@j@T@k@n@P@p@k@k@p@j@P@k@k@j@n@k@k@k@j@j@j@j@j@n@j@p@n@S@P@j@j@j@k@j@j@n@P@R@j@o@k@j@j@P@j@j@R@k@n@k@j@l@j@n@j@p@j@j@j@l@j@l@n#j$[#c+h+5@w+O+I+1+o+h+i+W+l+B@F%G%##P@j@j@j@T#j@k@n@k@j@P@j@j@k@k@j@P@n@n@P@n@k@j@n@S@P@j@k@U@'$M%I$A#8+e+(+k+0+m+5+o+:+1+8+1+s+Z+(+k+0+m+Z+%@(+f@X#Y#J@<#;$;$<#j$j$<#<#r$;$;$;$;$;$r$b$V@p@F$j@`%X$;$<#;$`#`#|$;$;$|$;$|$;$;$J@ ",
+" = : = * * : - - % * ; - # # , : # e - # # : * * : # + - # ^ # e # # : ; # # ( ; 6 # - - # ; - % * - + : : : ; = , 6 # : e : 6 - ; ; ; ; * * : : * + ; - # = % ; + * * - # : * - - - # ( , e , 6 , , 6 , # % # : , : , - : ; , : 6 : # # % , # ; : # - # : # * : - # - - , = % , : ; e , # % - | 9 # - # # j@j@j@k@p@k@P@j@j@j@n@k@j@R@j@j@j@j@k@j@j@j@j@n@j@P@n@k@j@j@j@k@j@j@j@l@j@j@P@j@n@j@j@j@j@j@j@j@k@j@j@j@j@j@j@j@R@k@j@P@P@j@j@k@k@k@P@p@k@j@n@T@P@R@j@j@P@j@k@j@P@j@j@j@j@j@j@y$f#u$v$q+|@/#(+k+0+m+5+h+x++@J%~%{%j$n#m#k@l@j@n@k@j@j@l@j@n@P@j@k@j@Q@P@n@P@k@n@S@P@j@j@j@;$D$O%^@C#9+c+5+o+:+G#8+1+k+0+m+o+h+s+5+o+:+1+s+%@>@O$P$R#M#;$r$r$;$<#<#j$<#;$;$;$;$j$X$X$F$j@i%l@k@p@Y%j$;$;$;$;$|$;$`#L#|$t$E$<#c% ",
+" = % e # ^ 9 - # * ; , 6 , , : 6 - - 6 * : - - % * * # # # : ; = # ^ ; # * * : + ; ; # # - # # * ; : - # # - # # e - 6 # ( 6 6 # - 6 # # - # ; # # # ; - # * ; - - - % * * ; : # # # # - # : - # : : : % * - # : , 6 ( ; - # e 6 - ; - - ; - # * e : ; * # - ^ 9 + # * e 6 - , , : # # # # + - # # % - # j@j@k@k@j@j@p@n@S@n@P@R@j@o@j@j@m@j@n@k@j@j@j@P@k@9#P@R@n@S@j@j@j@j@j@P@n@P@p@n@S@P@j@j@j@j@j@P@n@k@j@j@j@P@j@j@P@j@j@j@k@j@j@n@j@j@j@j@P@j@j@k@B$j@n@P@p@k@n@P@p@p@p@Q@j@j@R@m@ $># &.&c#0+5+o+:+1+:+1+[+^++@W+:%L@U@$$O@l@P@k@S@o@k@n@k@j@n@k@j@P@j@j@Q@j@l@j@j@S$U@N@G$-$t@P%}@|%g+#@+@1+s+s+^+5+o+:+k+0+m+k+0+m+5+I+]+9+V$W$-$Q$Z$`#;$<#;$|$<#<#L#|$|$;$j$r$+&+%k@l@k@T#m@B%Y%>%j$j$;$|$L#,@|$L#L#|$`$<#`$ ",
+" = ^ # = - , = # # : - # : : - e : * ; ^ 9 + # * ; : * * * ; : % % > # # * ; - # - - # # ; ; ; # = : # ; , - # # : # ; - # # : ; e ( # # ; ; # # - ; = * : ; # 9 + # * ; : * * - ( , : # - = : : - : : ; * # # ( - ; , 6 , , : , : - ; ; - ; # : - + * = % + : # : ; # , : , , , ; # # # # * , | # # , k@j@j@j@j@o@k@j@k@P@j@P@j@j@k@R@j@j@n@n@P@j@j@p@n@S@P@j@n@k@j@j@P@j@j@j@n@k@j@j@j@k@P@k@j@P@j@j@n@P@R@j@n@P@p@n@P@p@k@o@k@k@j@k@P@j@P@n@j@p@n@S@P@j@n@k@j@j@j@j@P@j@j@j@p@n@n@j@j@$$P@@&#&d$0+8+1++@Z+Z+Z+w+O+^+.#1@2@u#{#j@j@k@k@o@j@n@P@P@k@j@j@P@p@R@j@n@P@P@j@p@m@k@U@|$N$^%^+5%f%e+[+^+9+(+s+s+o+1+s+o+:+1+o+:+1+ @K$q%L%*%=%$&r$r$|$r$r$j$`#]$;$`#e#e#X$X$F$Z%+%U@m@p@j@k@F$Y%s$>%j$;$,@|$<#`#`#`#|$<#|$E$ ",
+" * : = = * : ; ; : = : : - - - 6 # - # # # - - # = - + # ; - + # # % # % * ; - : - , # : # # : ; + = ; = : * + ; # ; # ; # * + - ( ; : # # # = # + ; + # ( ; # # # ; # = * = # ; : ; , - # ; # # # ; # # # # - 6 : # ; : : e # e - ; # , e # - # # # # : ; : - # = ; # e + e e ; # , , e # = : - * : ( j@j@j@k@j@k@k@j@j@j@j@n@S@P@j@j@j@j@j@p@n@P@k@j@n@S@j@p@n@S@P@n@P@p@k@j@n@P@R@j@o@p@p@n@S@P@j@n@k@j@P@j@n@k@j@j@k@j@j@o@j@n@P@R@j@j@k@n@n@k@j@j@j@p@n@P@R@j@o@n@P@p@j@j@j@j@k@k@j@j@j@9@,@%&4$a%%@[+^+9+g+o+h+i+(+k+f@3$L@i@]%N@j@j@j@j@Q@n@n@j@j@P@j@j@n@k@P@k@n@j@U@j@p$|$D$}%f@|%9+/#n%5+k+0+s+(+k+0+(+k+s+o+h+i+_+9+(#u%v%m%N%|$Z$r$;$D%<#]$;$j$j$;$j$j$j$X$X$F$Z%j@m@o@i%o@i%j@F$-%j$>%;$<#;$;$;$;$;$|$]$<#L# ",
+" # # = % : = - # - # ; # # ; : 6 # - # - : - , # ; : % # : * * % * * : - # # , + * * : - # * # # # - = # # - : = - = * : : # - # # - - # # ; ; : - + % # - ; # : + # ; * * # # ; = + # % # ; - # - # ; ; # . . # 6 ; # - - e - 6 e 6 ( 6 ; ; - # ; # - # # - - - ; 6 e # # , e e , # , e ( = = # = # U@m@j@n@S@n@P@R@j@j@j@j@j@j@j@j@P@j@j@n@j@p@n@S@P@j@j@j@j@j@j@R@j@j@j@P@k@j@P@j@j@k@R@p@n@j@p@n@S@P@j@k@j@P@R@j@j@j@j@j@j@n@n@P@j@j@9#j@Q@P@n@k@j@k@j@j@j@j@j@j@k@j@j@j@P@j@P@n@k@P@k@m@m@'$,@.&-@9+w+<+w+ @/#m+5+r#c#1+d$'#)#_@/$P@l@n@k@j@T@k@n@P@p@j@j@n@n@P@l@j@k@j@N@;$-$t@P%5%f%=@9+s+o+o+:+k+0+o+:+5+(+k+0+m+5+ @K$q%r%x%b$&&]$r$;$<#;$j$j$;$<#;$r$r$;$r$Y%i%j@o@k@B$m@o@i%V@j@j@o@Y%*&;$;$;$;$|$|$;$;$<#|$`# ",
+" . ^ % % * + # ; - # # % ; : ; + ; # - - : * ; ; - * * : - - % : # ^ 9 + # # ; : # ^ 9 + ; * * = # # : * ; ; ; - # # % # : = = ; # - * + ; - : , - - # % ; # - # - # : # - # = = ; # + * : # = % = + # # * = # ; : : ( , ( 6 # # : : ( 6 e 6 - : : # - ; # = # # * - : : , ; - , , 6 ( , , # # * % - R@k@k@j@j@j@n@n@j@j@P@j@j@j@k@j@n@j@j@P@j@j@n@j@k@j@j@j@j@j@j@j@j@k@j@P@j@j@l@j@j@j@j@j@j@k@j@j@j@j@n@k@j@j@j@j@j@j@j@P@j@p@p@Q@9#P@n@j@j@j@j@k@j@j@P@j@n@k@S@k@j@j@o@p@p@k@n@P@p@n@S@n@V@G@@&#&>@1+1+e+(+=@#@1+5@Z+W+`+B@B@7#8#;$n#l@k@k@j@n@n@k@j@j@S@l@n@j@j@j@j@N@k@$$|$N$^%^+b#.@O+I+k+0+m+s+o+:+9+Z+W+5+o+:+j+h+(#u%v%Z@Z$<#>$r$;$<#j$;$j$r$r$j$<#;$<#;$X$Y$p@j@p@k@k@j@j@F$l@k@j@k@-%S#s$;$;$j$<#L#|$|$x$`#.% ",
+" = * * ; = # # # - : : * : : # ; # # ; - e # # # * - ^ 9 + # = # # - # : % * # - : + # : # # # = # % # - # - # : * * * : - = * # ; - # # ; : # , ; = # * : * % # - # + # ( - + + # ; e # ; ; : ; % + % # * = , 6 # : e : 6 # ; ; ; # ; # : 6 - : : : # + + * + * - - - , , : - , ( , 6 e ; ; ; * $ j@j@k@R@9#P@T@k@n@P@p@k@j@j@k@k@P@n@P@p@k@n@k@p@P@k@j@j@j@j@j@j@j@n@k@P@k@n@n@S@j@j@n@k@j@n@k@n@p@n@S@P@n@S@p@k@k@n@P@p@j@j@j@P@n@P@k@j@n@k@j@k@n@P@p@k@P@R@j@j@P@n@k@j@j@m@S@j@j@n@j@j@j@U@,@1$2$-@g+G#%@m+$@Z+I+s+o+h+i+l#3+A%,@(@{#m@m@l@n@k@B$j@m@P@j@U@U@j@m@G$/$/$<%[%}%f@|%W+`+O+I+o+:+1+8+9+O+I+s+o+h+1+h+9$+$|@w%F#L#r$=&E$;$<#j$;$<#j$X$X$Y%a@s$>%Y%Y%j@j@l@~#k@p@o@i%j@j@m@o@i%o@Y%x#D%s$j$|$;$e#j$|$;$t$ ",
+" = # = 0 0 % - = # - # # , ; # ; ; : : ; : : # # - : - # # ; # # % # # : * # * ; # % * : % # # # # # # : # - # * ; # ^ 9 + ; # # ; # - , 6 e 6 ; : : : # # = % % - = - , e ( ^ # ; + : # # - ; ; ; # # - . # e - 6 # ( 6 e ( , - # ; + ; - - # - - # - - # - # = # ; : # : : # , , # - 6 , - # # # j@k@k@n@k@j@P@n@k@j@j@j@j@j@j@j@j@k@j@P@j@P@R@j@P@j@j@j@n@P@j@l@P@P@j@n@B$n@k@j@k@j@k@l@P@P@n@k@j@j@j@j@j@j@j@P@n@k@j@j@j@j@j@j@k@n@j@j@j@j@P@n@k@j@j@j@n@P@R@j@n@P@R@j@o@p@p@k@P@n@k@9#j@k@ $e#'@a%%@[+n%g+%@w+(+k+0+m+5+-&5+|#E%a$j$m#j@l@k@~#j@k@9#k@U@j@O@m@j@x@<%[%-$3%4%I+s+o+h+i+c+m+W+`+O+I+s+(+k+0+m+;&^#c#B@h$z%>&X$,&`#|$r$e#j$>%j$r$Y%`%o@'$S#X$Y%B%o@p@j@j@p@~#j@+%l@V@k@F$Z%B%'&j$s$;$|$>$j$j$;$|$;$`# ",
+" % - % = = % = # # ; : : : : : ; : : : - # # - - - # - - # , : + # % : - = - # - : : = = * * * * # # 6 # % ; # * ; ; - # # 6 - # ; e : 6 - - - - # e - - % % + - # # # ^ # - # , 9 % % ; * # - - : : : = = # : # ; : : : # : - , ; # # ; : # : # - - - # ; : ; # ; # - - - ; # - ( e , , ( ( - # ~#R@k@k@n@k@j@P@P@R@j@o@j@j@j@j@P@k@j@R@p@n@S@j@j@n@P@j@n@n@P@k@9#P@o@p@p@n@S@P@n@S@P@j@m@p@n@P@R@j@o@p@n@k@j@o@n@P@R@9#j@j@j@j@j@j@k@P@Q@j@j@n@P@R@j@o@p@T@j@j@j@p@j@n@j@k@k@j@k@n@P@R@j@j@j@'$v#;@-@9+w+O+I+[+^+9+o+:+`+c+(+k+0+.#1@>#$$P@j@m@j@k@j@m@j@j@m@k@k@I@;$-$3%4%5%f%k+0+m+m+h+i+s+o+h+i+(+k+0+o+:+1+C+r#|+q$)#|$B%S#r$x$<#r$r$;$j$r$j$Y$j@o@F$Z%)&Y%o@i%j@k@j@k@l@j@l@o@o@i%j@'$(@a@a@]$<#L#E$E$;$L#`#<#L# ",
+" * % # * # 0 = # - # - - # - # # # # 6 6 # # = * : : : * + ; - - = = # % # * = # ; # # + - % * = % : : * * % % # # ; - = - 6 # # : 6 e - * 9 ; : # # - : : # # # * : : ; # : ; * - # # # * # 6 ; # - - # : , # ; e ^ 9 + # ; - ; # ; ( 6 6 # : : # : : : : ; : - % $ , e ; , , , e e 6 e | - # j@j@n@P@n@P@j@P@j@j@k@S@j@j@k@k@n@P@R@j@j@j@j@j@n@p@j@p@p@n@S@P@j@n@k@j@j@j@j@j@j@j@P@k@k@j@P@j@j@k@j@k@l@j@k@n@j@n@k@j@P@S@n@j@j@n@P@j@j@j@j@P@j@j@k@k@P@p@j@j@k@k@P@P@n@R@j@n@n@k@j@j@j@~#'$,@'@>@)$k+w+9+w+O+I+s+o+h+i+|+o+:+!$f@g@L@R#S$k@m@j@m@p@j@o@U@U@!#S#T$U$6%7%c#5+o+:+1+1+Z+W+^+c+m+5+5+o+:+1+s+.@a+|#J#K#L#V%E$`#E$r$e#;$;$r$<#r$j$X$V@j@j@k@k@k@l@j@l@j@k@j@j@k@j@j@i%k@j@'&Y%]$j$s$;$`#|$`#L#L#L#`#t$ ",
+" % % = = 0 * * # - # - ; - - - # ( # - : ; # = = # - - - # : - , # ; * # % # % # : * * : # # # - - % - - % * * + # # : + e ( e - e - # : % # : # # # : # # % # # : : # : : e ; % + : - - ; ; # 6 ( ; : ; # + # # : * # # e , 6 ; : : ; - # 6 , # - - # # + * - ; % # : 6 , 7 ; : # 6 6 # ; : - j@k@j@n@k@j@P@p@k@j@k@j@k@j@k@l@P@n@P@j@l@n@k@j@j@j@j@j@j@n@j@p@n@S@P@j@j@j@k@j@j@j@P@j@j@j@P@P@R@S@P@j@m@p@n@j@P@P@j@j@P@k@P@k@9#P@R@n@S@j@j@k@k@j@n@k@j@j@R@j@j@j@j@k@k@P@j@n@P@R@j@R@j@'&Y%,@1$2$W+H#C#w+9+w+(+k+0+Z+W+`+1+s+}+q+C@@$#$R#n#U@o@O@U@j@U@0#Y@J@H$%%9%a#5+W+`+1+s+Z+I+s+o+o+:+1+Z++@1+s+Z+5%.@f@X#Y#J@r$E$ %E$|$;$r$j$|$L#`#j$r$Y%`%k@j@j@V@j@j@V@j@p@o@i%k@p@j@j@k@p@F$i@j$;$]$r$D%t$E$E$E$E$;$;$`# ",
+" ; # = + * * * ; - # = # + * : 6 : e e # : - % ; : # # ; # : * ; ; - * * = % * : - - % * # # # # % # ; - * ; : - : : - # = : e 6 - e - % # ( e ; : : : ; - * * * : : : # ; * - # # - * - - * : ; ; 6 e 6 e # : ; # # = - : - # - # : = = : e : # - # # ; : # # ; ; - - ( ( 6 e e , # e : ; - ~#j@k@k@P@R@j@j@j@j@S@P@R@S@P@j@m@p@T@n@j@P@k@S@j@n@p@n@S@P@j@k@j@j@j@j@j@j@k@R@j@o@j@P@k@j@p@R@n@P@j@j@P@k@j@j@j@P@j@n@k@p@p@n@S@P@j@n@k@j@k@j@k@l@P@P@R@p@n@S@j@o@P@k@j@R@j@j@n@n@S@j@n@P@o@F$!&h@c$f@x+0+$@+@V+5+O+I+s+o+h+i+W+c#e+V++$@$~&s@$$U@j@k@k@j@{#Z@h@b%]@|+^@9+o+h+i+W+w+(+k+0+m+Z+W+`+c+s+g+%@(+f@L$=$R#M#<#>$E$|$`#;$;$;$;$`#|$j$X$Y$p@k@j@k@j@k@j@U@k@j@l@j@j@k@j@m@o@i%Y$Y%;$;$s$j$L#|$`$ %`$ %;$`#L# ",
+" % ; % - # : # e : + : ; : - # ( 6 e ; 6 , ; = - # # : : ; * ; # # * - - * * ^ 9 + # # ; = = % % # % : - , = * + > # ; - # = ; e 6 : = - e : - : ; ; # # * - # - , - # 6 # : - % # % ; ; - ; # ; ; : : 6 # # ; ; # # # # - : : - - # # ; # - : - - : : : : # * : # * + ; # : 6 ( - # # : # # ~#R@n@n@P@Q@R@j@o@p@j@j@j@o@P@n@k@j@j@j@P@p@n@o@j@n@j@j@j@n@P@k@j@o@j@j@P@k@j@R@j@j@j@p@S@P@j@n@k@j@j@j@P@j@o@p@n@l@n@k@j@j@j@n@j@p@n@S@P@R@S@P@j@m@p@T@j@j@j@j@P@j@k@n@P@P@n@j@p@p@j@p@n@P@j@j@k@(@{&0%f@k%^+-@w+8+0+(+k+0+m+5+Z+W+`+9+I+4$5$6$;$%$N@0@S#T$U$g%o%3@4@]+O+I+s+m+5+i+C#5+o+:+`+s+o+h+i+W+s+%@>@O$P$-$Q$X$>$E$L#`#|$;$;$r$;$;$|$j$X$+%o@i%j@k@j@j@k@j@j@j@k@V@j@p@j@j@p@k@a@e#;$j$<#`#`#`#E$,%E$,%x$j$`# ",
+" + = % ; : : * : ; - # # # # - - 6 ; e e 6 : # # - - # # - # # # ; # # - # # - # - > # ; - # # = * g % * ; # # - % # : * * - ; 6 e - : 6 6 # # - # # ^ * # : # - ; # * * 6 , ; * * * : - * ; # - 6 # ; : % # - # * * # ; : # # # # - # - # ; # : * # - - , # # 9 # - ; ; # # , # # # # # # ~#k@P@n@P@P@j@j@k@P@k@j@j@k@n@P@R@j@o@k@j@j@j@j@n@n@P@k@j@p@n@S@P@k@j@k@k@n@P@R@k@k@j@n@j@p@n@S@j@j@P@j@P@j@k@l@j@Q@k@S@P@j@j@j@k@j@j@P@j@j@k@j@P@k@j@j@P@j@n@P@p@k@l@P@P@j@n@R@j@P@j@j@n@m@j@p@U@'$,@]&f@o$^@9+5+o+(+k+o+:+1+5@s+o+k+0+^&d+^+f$,@R#!#J@H$%%9%<+&%W+C+G#w+(+:+1+ @$@h+I+s+o+h+k+0+m+5+w+]+H%V$W$-$X$Y$r$<#L#`#;$|$;$;$<#;$<#L#r$r$j$o@j@R@p@j@k@k@p@m@o@i%j@k@k@l@j@k@l@Y%x#r$;$|$;$|$;$;$|$E$|$<#;$L# ",
+" = = % ; ; - # - e , % # # ; : * ; # - # , , : : : - # # - # ; ; = * * - # : * * - % # : * - ; - # # % + # : ; % % # - - % * * e - e ( : 6 , # - ; # ; : ; ; - # ; # * ; ( # : # # - # : ; * ; e ( , 6 # - % - # * ; : : ; - - ; # ; ; % # : * # - * : - ( # ; # # ; # # = - | ! ! 7 # - ~#k@n@p@T@Q@9#P@j@n@S@P@j@n@k@j@j@j@k@R@j@o@j@j@p@p@j@P@j@j@n@j@j@j@j@p@l@P@n@P@R@j@j@j@j@j@j@k@n@P@p@p@Q@k@j@P@j@j@n@j@j@P@j@T@k@n@j@p@k@n@P@j@P@j@k@P@p@n@k@j@j@j@m@p@p@n@S@P@j@j@j@j@P@j@j@j@j@j@a@/&(&_&:&Y+I+w+(+o+:+s+Z+Z+C#0+o+:+1+Z+o$h+@$#$p#h@b%]@g+c#w++@ @9+Z+W+`+c+Y+$@w+(+k+0+c+o+:+1+w+H#}@L%*%=%$&s%;%`#;$`#e#<#L#|$|$j$j$;$;$<#r$r$Y%m@o@i%p@p@l@k@l@k@j@k@k@j@j@p@l@j@_@a@j$;$|$;$;$;$;$`#E$E$|$<#`# ",
+" = * % - # = % + : : * * : : ; : ; - # # : ; - # % + : 6 : : : ; * ; - : # ; - # # * : - ! ; # : # % # = # % # ; = + + # * ; : 6 ( ; - 6 - ; ; : ; ; # ; , # e : : % * ; ( # ( # + : : 6 > # - ( ; e - : - # + % * ; : : # = - : ; # # * # - - % - - - # - % # 6 # # # # # = # # , : : : m@j@j@j@S@P@P@P@j@n@j@p@n@S@P@R@S@j@n@j@j@k@k@j@j@j@P@p@k@P@j@j@j@P@j@j@m@p@T@P@j@j@n@S@k@j@P@n@k@j@p@n@S@n@P@p@k@P@S@j@n@P@n@P@n@k@j@P@n@k@j@j@P@j@k@j@T@k@n@P@p@P@k@j@j@j@j@p@k@P@n@j@j@j@j@9#k@j@9@y@<&[&}&s+m+k+0+o+c+s+I+s+o+h+w+c+I+s+|&I+!$~$g%o%3@5@]+-@`+c+O+I+s+o+h+X+Y+(+C#5+o+:+i+W+s+w+/@k%l%m%N%1&r$X$j$j$;$j$j$;$`#e#j$r$<#;$;$e#j$r$Y$j@j@m@m@o@k@j@+%k@p@k@j@l@n@V@l@F$'&X$;$<#L#|$|$;$`#`#|$`#|$;$.% ",
+" - % - * # : ; : # - # # ; ; - # # # - # # - - 6 : = - # # : : - # # - , e , 6 , # : + : ! # - + = : : # # * = = ; # + * * = # , e , - 6 # - ( ; e e , , : , 6 # # # # # ; , ; 6 : # e # ; % # , ( 6 # # + : : - - ; ; ; # = = ; - - # = ; # # ; # : , # = * # 6 : - = # ; - - # : : ; m@p@p@j@j@j@j@n@j@j@k@j@j@j@j@j@k@j@P@k@j@n@j@j@n@k@j@j@j@P@k@n@P@p@k@P@k@j@j@P@j@j@n@P@j@j@n@P@R@j@P@j@j@k@j@j@j@n@j@j@p@n@k@n@P@R@j@n@P@R@j@o@p@n@k@j@P@n@k@j@j@P@j@j@j@j@P@j@n@P@T@j@k@j@j@n@j@j@U@'$@&2&4$o$5+o+:+1+i+W+(+k+0+m+k+w+W+j++@s+~+n$<+&%W+C+G#o+h+i+w+(+k+0+m+h+i+w+(+k+0+m+5+h+_+C+X+9+p%-$&&Z$`#|$`#;$r$r$;$L#j$j$;$<#>%;$j$>%r$X$Y$k@p@j@j@k@l@j@$$j@j@p@j@P@m@o@i%'$(@X$;$;$`#e#j$;$|$t$E$E$`$j$`# ",
+" # 7 # = - # # : # - # # - e , # : # ; ; # . . # , ; - # # - # : # ; : # : - # : : ; = # 0 < * * # > = = % : = # # ; e # > # ; - , 6 ( ; 6 6 6 6 ; ; : # : : 6 # # - , # - - 6 ; e - : ; ; - e ; : ; ; $ f + ; : - - - - - % ; : ; ; * - # * * ; ; # 6 # - # # e , # # # # 6 # - # - ~#j@j@k@j@p@n@S@P@j@n@k@j@j@j@k@k@j@P@j@n@P@k@j@P@R@j@o@p@P@n@k@j@j@j@n@k@n@P@p@k@P@P@j@n@k@j@P@j@j@j@j@j@R@j@p@n@S@n@P@n@P@R@j@P@p@n@l@n@k@j@k@j@j@j@j@n@P@R@j@o@p@k@P@n@P@p@n@S@j@n@n@P@j@k@n@j@j@m@U@v#R%.&c# @g+x++@5+o+5+o+:+1+5@C#5+[+^+9+Z+r#c#w++@<+j%^#m+5+(+k+o+:+0+o+h+i+5+o+:+1+5@w+ @K$q%r%F#L#r$r$|$`#]$;$|$|$`#E$r$>%;$X$Y%Y%X$Y%S#F$k@l@m@o@i%j@j@j@p@k@j@~#k@k@j@k@'&Y%X$j$;$;$j$j$;$`#L#|$`$ %E$r$t$ ",
+" # * # # # # % : : # - - ; : : - ; # # # * = # ; 6 e ( , # - , - # ; ; : = : : - # # ; = 3&; 9 - * = # * ; = = ; , : : # % # : * - ; # 6 : - : : e ; : # 6 6 - ; ; : ; ; : ; , # , ( # : 6 6 - : ; e - 6 # # - # - - # # ; = - - - - = % # # # - # # : = , , # ( , ; * ; - # v # # : ~#k@n@n@k@j@j@j@j@j@P@k@j@j@j@k@j@j@P@j@n@n@j@j@P@j@j@k@j@n@P@R@j@o@p@P@n@k@j@j@j@n@l@n@k@j@j@j@n@P@j@j@j@j@k@j@P@n@k@j@j@P@k@j@k@m@j@Q@k@S@o@n@k@j@j@j@j@P@j@j@k@j@P@n@k@j@j@j@j@j@p@p@Q@P@n@k@j@R@m@m@ $y@#&d$a%%@[+^+9+Z+Z+W+`+c+s+$@8+w+O+I+s+o+h+`+c+s+s+:+c+s+5+o+k+o+:+0+m+5+(+h+i+W+I#/@k%u%v%Z@>&X$r$;$;$r$j$j$|$L#,@|$j$j$Y%X$F$)&b$+%+%k@m@o@i%F$F$F$Z%p@m@l@Z%o@i%p@p@k@i@X$;%]$>%;$|$;$<#`#`#|$E$,%E$E$`# ",
+" = % . # # , * # - # = * : # # # - - # # * = , # # 6 e 6 6 e 6 ; * = - # ; - # # * + % # | | # # # * : ! = = = # ; - - # * : - - - # # ( : ; : e ( : : # 6 , # - ( ; e e , , : , : : # 6 : - : # - # e ( ; ; - - # - | # e - # # | # = % % # = = # , = : # 6 ; : - # : # - | % - - m@j@n@k@n@k@j@P@k@9#P@R@n@S@j@n@j@P@R@P@T@k@n@P@p@k@j@P@k@P@P@j@j@k@j@n@P@R@j@o@p@j@Q@k@S@o@j@j@k@j@j@j@j@P@P@j@n@P@R@j@j@n@S@P@j@n@n@j@j@o@j@n@P@k@j@j@P@l@j@P@j@j@n@P@R@j@o@p@T@j@j@j@P@n@P@R@j@j@j@B$k@,@{&;@-@9+w+O+I+I+s+o+h+i+W+C+G#d+I+(+k+0+m+h+i+W+o+h+i+W+8+1+o+:+Z+Z+9+Z+5+o+5+h+C+X+9+w%x%b$4&Y%x$<#<#<#;$r$;$|$<#r$;$j$X$b$F$F$Z%+%+%m@m@o@i%o@i%j@m@o@i%j@p@+%+%~#k@l@e#>%j$s$j$;$|$;$|$;$;$|$E$E$`#|$L# ",
+" % % - # # - = % = * + ; : ; # ; - - # # . # e # # 6 , ( : # ( - = = # # # - - ; + * - - - - 9 = % + + ; - - # * * # ^ 9 + - 6 , , 6 , ( 6 e ; , : , - , 6 6 6 6 ; ; : # : 6 # ; # 6 e # # # - # ( ; , : * * : = % : 6 * # - 6 # ; 0 = # = * ; ; : , , , # # # ; # ! # # + : - m@j@k@k@P@k@p@n@S@P@j@n@k@j@k@j@j@j@n@j@P@n@k@j@j@j@j@p@p@n@S@P@j@n@j@P@j@j@j@k@k@P@j@j@o@j@n@P@j@j@P@j@P@j@n@k@j@P@j@j@j@k@j@p@n@S@P@P@k@j@n@n@P@k@k@j@P@j@P@j@n@k@j@P@j@j@k@n@j@j@j@j@j@j@j@P@j@j@o@j@-%a@,@'@>@)$k+w+(+(+k+0+m+w++@<+j%^#C#5+o+:+0+m+(+k+0+m+$@W+`+c+1+s+s+O+I+C+8+h+ @ @K$q%r%F#Y$Y%Z$r$;$;$e#j$<#;$<#;$;$j$r$r$Y%+%k@j@o@o@j@j@j@o@j@j@j@j@j@k@l@V@o@l@k@F$Z%F$S#j$>%j$r$<#L#;$|$;$;$`#`#|$L#`#|$ ",
+" * . % = * = * * ; # - * + = = = # # % # # , 6 : : ; - # , 6 , # = = * # # - # - # # % # = = ^ # 9 * = % = # # % * * # # - , # 7 # # - 6 ; ; # : * * - # # # : e ; : # 6 : , # # 6 , ( 6 6 : # , # : - - % * # # - # : ; * : - # * # ; : # # # : - 6 6 # # # # ; # # # ; ; ~#R@n@n@n@k@j@n@j@p@n@S@P@R@S@k@j@n@S@j@n@P@R@j@o@p@j@j@j@n@j@p@n@j@P@p@k@j@j@n@P@R@n@S@j@n@n@P@n@P@p@n@l@n@k@j@j@j@j@j@n@P@k@j@j@j@p@n@S@j@p@p@Q@9#S@j@n@n@l@n@k@j@j@l@j@p@n@S@j@P@j@j@j@k@j@j@j@j@m@~#o@Y%,@1$2$W+%@[+^+5+o+:+1+`+c+k+0+m+9+o+1+o+:+1+5+o+:+1+Z+W+h+i+(+k+9+Z+W+`+s+s+h+>@u%v%Z@5&Y%X$X$Z$Z$j$>%r$a@s$>%;$>%r$X$F$+&+%j@Z%k@k@j@F$Z%o@P@j@n@j@j@j@F$Z%m@o@i%o@i%i@Y$Z$;%]$r$D%`#<#L#|$|$;$;$|$E$|$`# ",
+" # % % = 0 0 * + : - # + # # = % : * * * - : ; , : 6 : # # : # * - = 0 0 * # # - = - # % . 0 = - ; # + : - * ; # + # : # ; - = % > # # : * - - % * * # , 6 , : : # 6 , 6 : : ; - # , 6 e 6 : : 9 + # * ; : # # - ; - # # - : * # # : # - - # # ; , # # # # ; ; # # - - j@j@n@P@R@j@j@k@j@j@j@j@j@j@j@j@j@j@j@j@n@k@j@k@j@j@P@j@j@k@j@n@P@p@j@j@j@j@P@j@n@k@j@j@p@p@n@k@j@j@j@Q@k@S@o@k@k@j@n@n@P@j@j@P@j@j@n@j@j@j@j@P@n@j@j@p@p@Q@k@S@o@9#k@j@j@p@j@n@j@n@n@j@j@n@P@j@j@j@j@m@~# $h@c$f@9+w+O+I+1+s+o+h+5+o+:+O+I+s+k+s+o+I+8+1+s+I+s+o+h+i+5+O+I+s+o+h+i+v@+@|@w%x%Y$+%b$X$X$Y%Y%X$Y%X$X$S#j$X$Y%S#Y%F$Z%j@j@o@m@o@i%j@o@o@P@j@m@m@j@i%j@k@j@V@k@n@F$Z%_@a@j$s$j$j$;$;$`#e#|$;$;$|$`#|$`# ",
+" % = % % = = # # # + : : # # % % # # # # ; - # e 6 - ; - - ; * = % = = = % ; * * ; ; ; = - = % # # % ; - = = - : - * # * : - % # : - - * # * ; : - # ; - , : , - : ; , : 6 : # # : : 6 # - # # ; # = : + * * ; # - # * # : # # - - - 1 # # # e 6 : - ! # # : : - j@p@p@P@j@j@P@n@k@j@j@j@n@n@S@j@j@j@j@n@S@P@k@j@j@P@p@k@j@P@n@k@j@j@j@o@j@n@l@n@S@P@k@j@j@n@P@R@j@j@j@P@n@o@j@n@P@j@p@p@Q@n@P@p@k@P@n@j@p@n@j@j@j@j@j@j@j@P@P@o@j@n@P@p@R@n@P@k@9#P@P@k@9#P@R@n@n@S@j@l@j@$$(@'@>@)$k+w+(+k+k+0+m+8+1+9+Z+(+5+o+k+0+(+k+0+m+o+k+0+m+W+`+w+(+k+0+m+j+e@d+y%z%5&F$F$o@'$X$F$)&)&Y%S#X$F$)&)&S#Y%S$l@+%F$Z%k@j@o@F$Z%o@P@p@k@j@m@m@o@T#p@k@k@9#i%j@j@-%*&r$e#<#;$j$;$j$j$L#|$|$;$`#L#.% ",
+" = # % 0 * # : , $ f + - ; * - # - # # , 6 , , : , : - ; ; - = % ; % # # 0 : 6 - # # # , = [ = $ # % - + # - - * + ; ^ 9 * * - 9 + # ; # # g % # e , 6 , 6 ( ; - # e 6 - ; - - ; ; : # - - e + ; + = * % # # # + : : ; * # # - % % - # , , e - - - # , ! ( : ~#j@k@j@P@j@j@n@P@R@j@o@p@T@k@o@k@S@n@j@j@j@P@j@j@P@j@j@j@n@k@P@R@j@o@j@P@n@n@k@j@j@j@j@n@P@P@j@n@j@n@P@p@j@j@n@n@P@j@j@P@n@k@j@j@j@n@P@k@j@j@j@j@j@P@j@p@n@p@p@j@n@n@P@P@j@p@n@S@P@j@n@S@P@j@n@k@j@j@P@j@j@j@k@1$N#W+}+C#5+O+I+:+1+[+^+9+s+4+`+5+o+:+5+o+:+1+5+o+:+<+j%h+k+0+o+:+1+/#(#/%F#h@+%k@l@`%X$b$B%o@6&Y%S$b$B%o@F$Z%)&j@j@+%j@+%k@j@i%j@o@j@j@j@n@T@k@n@j@j@~#k@k@k@j@j@p@j@Y%x#e#s$<#;$j$r$;$<#|$`#|$;$|$t$ ",
+" # # = 0 = = # # 6 # : ; e # ; ; = % % ; : : e # e - ; # , e = ; # # - ; # : - : * * # ; = * . # % ; + * * - - - - # - % * * + - # * * # # : - > # # ; , 6 , , : , : - ; ; : # e # ; , - e : # ; # - : : ; # # # - # - ( : - ! # e # # $ , - 1 # - 6 , - ~#R@k@n@j@n@k@j@P@j@j@k@k@S@k@j@P@k@P@k@j@P@R@n@P@p@k@p@n@S@P@P@j@j@p@n@j@Q@n@j@j@P@j@n@k@j@p@n@S@P@k@j@j@j@j@p@p@n@j@j@n@P@R@j@o@p@T@p@j@j@S@n@n@P@p@k@j@k@j@j@j@p@p@j@P@j@j@n@j@p@n@P@j@p@n@S@P@n@P@p@j@j@j@j@h@.$X+5+$@8+w+(+k+0+w+O+I+6+o+h+9+Z+W+`+1+s+Z+W+`+s+k+0+m+o+k+0+m+6@]++@1%-$@#Y%U@o@p@Y%S$i%j@o@F$V@j@o@i%j@o@m@o@o@+%j@j@j@o@j@T#k@P@p@P@j@P@B$k@j@V@j@j@R@p@j@m@m@o@S#S#;$D%e#r$;$<#|$|$`#`#|$j$`#`# ",
+" # % % 0 % = = : : ; % - : ; # = # + % # - - e - 6 e 6 ( 6 # # # # g % # ; : ; # # # # = = = * = - d : ; ; - % % - * ; : = > - * ; - : = = % # , , ; : - e # e - ; # ; , e e ( , 6 ( ; : # * % * : ; - # - # - % + # # ; , ( 6 # # # # , ; - ; , , o#U@m@n@P@Q@j@j@n@P@j@n@j@P@R@j@p@p@n@S@P@j@n@k@j@j@k@j@j@j@j@j@P@k@j@R@j@T@k@n@P@p@k@n@j@k@j@j@P@p@R@j@o@p@j@j@j@n@j@P@n@k@j@j@k@k@j@j@j@P@k@P@k@j@j@j@k@j@j@j@j@n@j@P@p@9#P@T@k@j@j@p@k@j@j@j@n@k@j@j@j@P@j@j@(@L@@%:+W+}+[+^+o+:+W+`+(+k+0+m+I+s+o+h+O+I+s+o+h+i+o+:+1+5+o+:+Z+W+(#t#u#I%Z$X$Y%X$F$)&m@j@T#j@j@k@k@o@k@k@l@Y$Z%o@m@o@F$Z%o@j@l@j@n@P@p@k@P@P@j@m@m@o@j@j@~#j@k@B$S$F$X$Z$e#]$<#;$|$|$`#|$;$;$L#;$L# ",
+" # | % * * * > % # ; ; * ; ; + * * * : ( , ( 6 # # : : ( 6 ; # : * h % : : ; : ; # * * = = = * ; + * = = + + % = ; = = - > % * ; % - - > # ; - - , * # # - 6 e 6 # # : - # : - ; # 6 : % * * + + * ; - + : : ; ; 9 + : ( - # , , e : # , # # : - j@j@k@n@P@P@k@9#P@R@n@S@j@P@j@j@j@j@n@j@p@n@S@P@j@o@R@n@S@j@j@k@k@n@P@R@j@P@n@k@j@j@n@k@j@P@n@k@j@j@j@j@k@k@R@j@j@p@j@n@P@R@j@j@p@R@j@j@j@p@p@n@S@P@j@n@k@j@j@p@n@S@P@j@n@k@j@P@n@k@j@j@j@j@j@j@P@R@k@j@j@k@k@j@y$_@4#U%&%9+w+O+I+s+o+h+5+o+:+1+(+k+0+m++@(+k+0+m+k+^+0+O+I+s+s+j+e@d+y%J@7&Y%Y%Y%b$B%o@B$j@+%j@j@j@Z%j@k@j@l@F$Z%o@o@i%j@o@j@m@m@n@k@j@j@j@p@n@j@j@B$j@j@k@j@j@m@o@i%U@@#X$j$j$r$;$|$L#,@|$;$`#`#|$L# ",
+" # = # # # + % # : e 6 ( 6 # # # # # : e : 6 - - ; * # ; # # = : = = % : : - # # * * ; * = = e ^ # g * % * % % * = = # % # # # - # # % # : * * - * * * # # : : 6 # ; - # ; - # # ( : ; # # # - # # - - - # # # - * = ; # # ; 6 e 6 ( e ; : 6 ~#R@n@p@T@n@S@P@j@n@k@j@n@S@P@j@j@j@j@k@j@j@j@j@j@j@n@k@j@k@j@k@l@P@n@j@j@P@P@R@p@n@S@P@n@n@P@R@j@o@j@j@j@P@j@j@j@j@k@j@j@Q@j@j@j@P@j@n@j@j@j@n@j@p@n@S@P@k@P@o@n@j@p@n@S@j@j@n@P@R@j@o@j@P@j@P@j@n@S@P@k@j@S@R@m@i@v#8&-@o$k+w+(+k+0+m+8+1+s+Z+5+o+:+`+c+5+o+:+1+Z+W+`+m+(+f%h+/#(#/%F#E$Y%Y%S#Y%S$S$m@m@o@o@i%m@j@o@m@m@o@i%j@F$Z%o@k@j@o@j@j@j@P@j@j@j@P@j@j@j@P@j@m@m@m@m@o@i%j@j@m@`%X$r$;$`#s$;$|$<#;$|$;$;$;$`# ",
+" # * - % > + # - - : ; ; # , ; - - e # ( 6 6 # ; ; - - * - - ; ; = = % ; # # # # * ; : % = # ; ^ % * - # # # : * : - - % * * * : - - % * * = # ; : ; # : e , 6 , , 6 , , 6 e 6 - # ; - - # - # # # - # # # % ; , , : # e e ; ; : - j@j@j@j@n@j@p@n@S@P@R@j@j@j@j@j@j@n@n@k@j@j@j@p@n@S@P@R@n@S@j@m@p@k@n@P@p@k@k@j@j@j@j@n@R@P@j@p@j@j@j@j@j@j@j@n@j@j@j@P@j@j@j@n@l@n@k@j@j@j@j@k@j@j@j@j@P@p@n@S@k@j@j@n@k@n@k@j@j@j@n@P@p@j@p@n@S@j@k@n@P@R@j@j@-%(@9&0&_&$@8+5+o+:+1+s+o+h+i+8+s+o+h+i+I+1+s+h+s+o+h+i+5+o+w+/@+@1%-$E$;$r$e#j$o@j@j@B$m@m@o@i%j@m@o@i%m@o@i%j@o@o@i%m@k@k@n@P@p@k@n@P@p@k@l@j@j@j@j@j@j@p@k@p@p@k@o@_@Y%j$D%|$<#;$;$|$;$;$;$|$E$L# ",
+" = = - - % - # ; # ; : * : : : + : ; - # # : ; * - # ; * ; # - # # # = # # # # % * ; # = J , # % + - - - - ^ 9 + # * # # ^ 9 + # * ; : # # ; = ; # # : - # : : : - : : : 6 ; * = * ; # - + * ; ; * # ; # - , ( ( - ! ; : ( - ; j@k@j@j@j@k@j@j@j@j@j@j@l@P@k@p@n@S@P@j@j@P@j@j@j@j@j@n@k@j@P@k@P@n@k@j@j@j@n@k@j@k@P@k@9#P@R@n@S@j@j@j@j@p@n@P@j@n@S@R@n@S@j@j@Q@k@S@o@P@k@j@n@k@j@P@k@j@j@j@j@n@k@n@P@R@j@R@j@o@j@P@j@n@k@j@j@j@j@l@P@n@j@j@j@V@ $y@a&b&c#H#8+1+s+(+k+0+m+5+9+k+0+m+5+h+k+0+m+k+0+m+5+9+_+C+s#t#u#I%r$x$r$r$j$X$j@j@j@j@m@j@j@T#B$j@j@B$j@j@T#k@j@F$Z%o@n@k@j@j@j@k@j@j@P@n@j@9#j@P@9#j@j@j@k@k@l@k@m@_@a@e#<#;$;$|$`#|$;$;$|$E$.% ",
+" = - = % + # # * = : - ; ; # - # - # : + * + - ( # % # # ; # - # # # = # * + * : + # ; * K ( # % + * : = - # # % # # - - # # - # = : # = = # # * * = : : - - # # - # # ( - = + ; : # - * % # : ; - # ; # # , 6 , ( # : - # ~#R@n@k@n@j@n@k@j@j@j@j@j@j@P@j@p@n@j@p@n@P@p@n@k@j@p@n@S@P@j@P@j@n@P@R@j@o@p@n@R@j@j@n@S@P@j@n@k@j@k@j@P@j@j@P@j@n@k@j@n@k@j@k@j@j@n@o@j@n@S@n@S@P@p@n@S@P@k@j@n@P@R@j@P@j@j@j@j@k@P@p@n@S@P@R@n@P@j@m@p@k@n@P@j@j@c&w#J@v$q+.@H#%@[+^+o+:+1+5@I+o+:+1+5@5+o+:+1+o+:+1+O+I+ @K$q%r%J@|$r$j$e#j$r$Y%j@j@m@m@o@i%j@n@P@j@P@m@Z%o@m@o@i%j@o@n@P@R@j@o@p@P@j@j@n@P@k@j@j@j@j@k@j@j@j@k@j@j@k@'&*&e#<#;$<#`#;$|$;$;$|$E$`# ",
+" = = - # : + * * * - : ; : - # ; - : ; - # - # # : * * ; ; # - # # ^ # # ; * % # - : # ; ^ # # ^ % ; * % = - g + * > # ; - % * e = * ; + : # * ; ; # # # # - # # - 6 , ; : - : : : # # ; # - | , ; ; ; : ( e e # : : : # o#j@j@n@k@p@n@k@j@j@j@P@j@j@P@k@k@j@k@j@k@j@j@P@k@k@j@j@j@j@j@P@k@k@j@j@j@k@n@S@S@P@j@P@j@p@n@S@P@R@n@P@Q@j@j@p@n@S@P@n@S@P@R@S@P@j@j@j@n@n@j@j@P@j@j@P@j@j@k@n@P@p@j@j@k@k@j@P@n@k@j@j@j@j@j@j@k@j@P@k@P@n@k@j@o@j@m@j$,@z$0%|#G#9+w+8+1+s+Z+h+i+1+s+Z++@8+1+s+c+1+c+s+v@h+(#u%v%Z@2%<#r$j$<#r$j$Y$U@P@Z%B$j@j@T#S@n@P@p@k@j@j@B$j@j@T#k@k@j@j@j@k@P@j@n@k@j@P@j@j@k@j@P@j@P@j@p@j@k@l@k@'$Y%;$<#;$<#E$<#L#|$|$;$D%`# ",
+" % - # w - # = - % * + * - # # - : - e , # - ; ; 6 # # e , : # % 7 = : ; # # ; , : : # : = # # % % + ^ # + # - % # : * * ; : - * # ^ 9 % * ; # # = - = * % # % : , , : + - # - - > # % ; # - # , , , - ! 6 6 : : : : m@j@P@n@k@j@j@k@S@j@P@p@k@j@n@k@j@P@P@k@R@j@o@n@k@j@n@k@j@n@p@n@S@P@j@n@k@j@j@n@k@n@P@p@j@j@j@P@j@n@k@k@j@j@j@j@j@j@j@j@j@P@k@j@k@k@p@j@p@p@n@P@p@n@P@p@k@j@n@k@k@S@o@j@n@j@j@9#j@R@j@o@j@j@j@j@j@j@P@j@n@P@R@j@o@j@l@j@P@u#.$X+5+)$k+w+(+k+0+m+[++@9+Z+^+9+Z+h+i+W+i+W+v@+@/@w%x%d&Z$e#r$;$e#r$r$Y%i%j@l@k@j@9#j@n@k@j@P@j@j@P@S@P@j@j@P@j@j@P@n@k@l@n@k@j@j@j@j@j@j@j@P@k@j@j@m@k@j@j@'&Y%X$j$;$;$|$`#;$`#e#j$s$`#L# ",
+" # = , ^ ; ; = # * ; # - # % # % - ; : : : 6 : 6 6 ; * ; # # # ; = ; - ; # # : + - # ; # # * # ; + : = * # * : - - % * : = : + - # # # # - - # % * = * * : ; - * # # ; ; - ; % # = : : - # _ : : : : : : : : m@k@j@p@j@P@j@p@n@j@j@j@p@n@S@P@j@n@P@k@j@j@k@j@p@n@S@P@n@k@j@j@j@j@n@S@P@p@n@S@n@k@j@S@P@n@j@p@n@S@P@R@j@j@j@P@j@p@n@S@P@n@j@j@n@T@k@j@j@j@k@j@j@k@j@j@p@n@S@S@n@o@j@n@S@P@j@n@P@j@j@P@n@S@P@j@j@j@P@j@j@P@j@j@k@k@j@j@k@'$L@@%:+e&}+C#5+o+:+1+`+c+I+s+O+I+0+m+h+i+j+j+^@s#c$)#x#-%X$X$Z$;$e#j$r$Y$U@m@o@i%k@j@j@j@R@P@p@j@k@j@j@j@n@j@P@k@p@Q@B$R@Q@k@S@o@k@k@n@P@j@p@Q@B$R@j@j@j@p@F$i@X$;%]$;$|$|$;$;$j$j$;$r$D%t$ ",
+" # | = # # = * * * = : * * * * : : - # # # e : ( ( 6 ( 6 ; ; - - > # ; - * * # - - = # - = # # % % % # : ^ 9 + # * ; : - * = % # # # ; + : + - # # # - # # ; - # # ; # # : * * = # 6 e : : : : : : : : : : ~#k@k@n@n@k@k@j@j@P@j@k@j@P@j@j@j@p@n@S@P@j@n@j@j@j@j@Q@P@R@k@j@j@j@j@j@j@P@j@j@j@j@j@j@j@j@k@j@j@j@j@n@k@n@P@p@k@j@j@j@k@k@P@j@j@k@B$j@j@j@R@j@o@R@j@o@p@j@j@j@j@j@n@n@j@p@n@S@j@P@j@j@n@j@j@j@j@p@P@k@j@k@n@j@n@P@j@j@j@c&x#4#'%f&)%}+8+1+(+k+h+i+(+k+0+(+k+0+m+5+v@5%q+g&D$p#@#h&X$Y%Z$j$>%r$X$Y$o@o@o@j@R@j@j@P@p@j@k@k@n@k@j@j@p@p@Q@9#j@j@P@j@j@n@o@j@n@P@k@j@k@l@P@n@j@m@m@o@i%j@_@a@j$s$e#j$|$;$r$r$;$<#j$j$`# ",
+" # = - - = ; - - = - ; # # # ; # # - # , # - 6 - - ; ; # # : * * % # : * * = , 6 = - = : * - # 8 # * * % # # - = : # % = # * * : : * : * h % % # - - ; - - + : : ; # # - % * * # # : : : : : : : : : ~#j@k@P@S@P@n@n@P@T@k@n@P@p@k@j@j@n@P@j@p@n@S@P@j@k@j@n@k@j@j@n@j@j@k@j@j@P@n@j@j@P@j@n@P@j@n@k@j@p@n@S@n@k@j@j@j@j@P@k@j@R@j@P@P@P@R@j@P@k@j@j@k@k@j@k@n@S@j@k@j@j@p@j@k@j@j@j@P@p@k@P@n@P@j@j@k@n@n@S@P@n@k@k@j@n@P@k@j@k@$$v#_%i&f@I+$@C#5+o+Z+W+5+o+:+5+o+:+1+5@5+A#T%a$p#@#b$Y%X$Y%Y%X$Y%Y%S#Y%Z%o@m@l@j@j@j@j@j@j@k@n@n@j@j@P@j@j@P@n@j@p@k@k@j@j@j@n@n@P@R@j@j@j@n@P@k@j@m@o@p@o#@#*&r$j$j$;$|$;$;$|$`#<#|$>$L# ",
+" % # 0 % = # # # - # ; ; - * : ; # * : : = - e : * : * : : - - % * : - - . # e - = : * - - - # % + : = = # * - = % % # * ; # - + + * = % * * ; # - - : * : ; | = # # * ; ; : : : : : : : : : : : ~#p@j@j@j@j@n@k@j@P@n@k@j@j@j@j@j@p@p@k@j@j@j@j@j@k@k@P@R@p@n@S@k@P@j@j@p@T@k@n@P@p@k@j@p@n@S@P@k@j@j@j@R@n@S@j@j@k@k@n@P@R@j@S@S@P@j@i%P@j@j@j@n@j@j@n@k@j@j@j@j@j@j@j@j@P@j@k@j@j@j@n@P@j@j@P@n@k@o@j@n@P@R@j@j@p@k@n@l@j@$$U@a@k$c$m$I+$@8+O+s+o+h+Z+W+`+1+s+Z+/+-@:$W%M%b$Y$Y%Y%X$F$)&)&Y%Y%Y$j@o@P@F$Z%P@P@j@j@j@j@n@j@k@n@P@p@n@j@P@P@k@j@k@k@n@P@j@p@p@Q@R@j@o@p@T@n@P@j@n@j@~#j@`%Y%j$r$;$<#L#|$|$;$`#`#`#`#L# ",
+" = = # 0 = # = * # # : : : % ; # ; % # - - 6 6 # # - ; # 9 + # * ^ 9 + ; # # - , * # ; # | # # ; + - : = ; - ; - % % * ; = % - : * = # # , # ; : : : : : # # - # = - # : : : : : : : : : : : m@j@k@k@j@j@P@R@j@n@P@R@j@o@p@S@j@P@j@n@P@Q@j@j@j@j@j@P@k@j@j@j@k@j@j@k@j@P@n@k@j@j@n@R@j@j@j@R@n@k@j@j@n@k@j@k@j@k@l@P@n@P@k@k@j@P@p@j@n@S@j@j@P@j@n@S@P@j@j@p@n@S@j@j@P@p@k@R@j@o@p@T@k@n@P@n@P@R@j@n@n@P@j@k@j@P@n@k@j@j@P@o@6&`%k$l$m$c#k+(+k+0+I+s+o+h+i+W+9$r#`+0$a$)&h&X$S#Y%b$B%o@'&S$S$m@m@o@i%j@j@k@j@p@n@P@n@n@P@n@k@j@j@k@P@P@n@j@j@P@n@k@j@j@j@j@P@n@j@j@P@j@j@j@k@j@j@j@p@Y$_@a@j$s$;$`#e#j$`#L#L#L#|$L# ",
+" = - h % * # % + ; * - - ; % - - ; # # * - : 6 - * : ; # ; # - ^ # # * * = # # ; - # : # - % > % % * = # * % # : = : # # - % * - = # : ; # , # : - # # * = - # = # : : : : : : : : : : : ~#R@n@k@j@j@j@j@j@k@j@j@j@k@k@j@P@p@j@n@P@j@j@j@j@j@P@k@n@k@j@j@R@j@o@j@j@n@P@R@j@j@P@j@j@j@j@n@k@S@p@n@S@P@R@S@P@j@m@p@T@n@j@j@P@n@k@j@j@p@n@j@j@n@T@k@P@Q@j@j@j@j@n@j@j@j@n@S@j@k@n@P@n@k@j@j@P@j@j@p@p@Q@9#j@j@P@P@R@j@o@j@o@k@9@6$u$v$q+w+5+o+:+(+k+0+m+5+h+8+A#Q%i$(@b$Y%X$S#Y%S$S$k@i%j@j@j@B$j@j@T#R@S@P@n@k@j@Q@P@n@P@R@j@o@j@j@k@k@P@j@n@P@R@j@o@p@j@j@j@n@P@p@k@j@j@j@j@P@j@m@p@'&*&r$j$;$j$j$<#|$L#L#L#|$L# ",
+" $ - 0 = > # = # : 6 - - ; ; - = ; # # = % ; # # + # = * : ; % - # ; * ; * # ; e ; * ; ; * # $ % = , # # + # = = * # = # # - * * # , : # : - - # - # * ; % % : : : : : : : : : : : : m@j@P@j@j@j@k@P@k@j@Q@j@n@S@P@k@j@k@9#P@R@n@S@j@j@k@k@n@P@j@j@P@j@j@k@k@n@j@P@j@j@k@k@j@j@P@n@S@P@P@j@j@j@j@j@k@j@P@k@j@j@n@n@P@j@n@n@j@j@j@j@j@j@j@k@B$P@j@j@j@P@j@j@P@j@j@n@j@j@n@k@P@n@R@j@j@j@P@j@j@j@P@k@n@P@p@n@k@j@k@k@j@j@j@m#>#z$0%f@m+1+s+<+o+:+1+h+9$B@j&k&|$;$<#r$j$X$)&o@B$j@j@o@o@P@S@P@j@j@j@k@j@P@k@j@j@j@Q@j@j@j@k@P@k@j@R@j@P@P@P@j@j@k@k@j@j@j@k@j@j@U@j@j@j@j@j@j@j@F$B%Y%;%;$r$r$;$|$|$`#E$E$;$E$ ",
+" # + # : % # : * : : # - # = - > # ; ; = ; # # % : # # # # : * % * % * ; * * * 6 ; ; - ; - * # ^ * ; # : * * = - # + # # ; - # # - # , ; # # # : : : : : : : : : : : : : : m@k@n@n@j@k@n@k@n@P@n@j@j@P@j@j@n@S@P@j@n@k@j@k@j@k@l@P@Q@9#j@j@j@p@P@n@k@j@j@j@j@j@j@n@P@p@j@j@j@j@k@j@j@j@k@k@j@P@j@j@k@j@k@P@j@P@T@j@j@P@j@j@j@j@P@k@j@n@n@P@p@k@P@p@k@P@S@P@j@P@R@j@j@k@n@n@P@p@k@j@j@j@n@k@j@n@P@R@j@n@j@j@j@p@0@P@4#c@+$h+3+g+e+1+s+Z++@r#l&(%w$|$r$j$j$r$Y%`%j@k@P@R@T#j@j@j@P@P@j@k@k@j@P@j@j@k@j@n@S@j@j@k@k@n@P@R@j@S@S@P@j@j@n@j@j@p@P@R@j@o@R@j@P@j@j@j@P@m@m&Y$a@j$j$|$|$;$;$L#,@|$|$<#L# ",
+" = * # : * ; - - > ; : = = = * % # : * * - # = # # # * * = # : : # # ; # # # - # - - - # ; ^ A ; = # % ; : # * % : % # : * ( ; : : : | : : : : : : : : : : : : : : : : o#j@j@n@P@n@k@l@P@T@k@n@P@p@j@k@n@j@p@n@S@P@R@S@P@j@m@p@P@n@n@P@j@j@n@P@R@j@o@j@j@P@j@j@j@P@j@P@k@j@p@n@j@j@k@j@j@j@j@P@p@n@n@j@k@P@Q@n@P@p@k@j@j@n@k@j@P@n@k@j@j@j@j@j@p@n@j@P@j@n@S@P@j@n@n@k@j@j@j@j@j@n@P@R@k@j@P@j@j@j@R@j@j@j@k@m@'$>#U%&%w+|@n&m+6@e@d+A$D$o&<#]$j$>%r$X$Y$U@j@S@j@n@k@k@n@P@P@p@j@j@j@j@P@P@j@k@n@k@j@k@j@k@l@P@n@P@k@k@P@P@p@j@j@n@n@j@j@P@j@k@n@P@p@j@p@n@j@j@o@'$S#j$>%;$|$;$;$|$<#;$L#<#`# ",
+" * # * - # : * * % # - # = % : # = - - % * * : : = # , ; : # 6 : % # # # # # - - # # # - = = e # g % + # # = # = * : : : : : : : : : : : : : : : : : : : : : : : : m@p@k@p@n@S@P@j@j@P@n@k@j@j@j@k@j@k@j@j@j@j@j@n@j@P@k@j@j@k@n@j@n@j@P@P@j@j@k@n@P@p@n@S@P@p@k@k@P@j@j@P@j@P@j@j@j@P@j@p@n@j@j@j@j@P@n@k@j@j@j@l@P@P@j@j@n@P@R@j@o@j@n@P@j@j@j@p@n@n@j@p@n@S@P@R@j@o@p@T@j@j@j@j@n@j@j@P@j@j@n@T@j@j@j@k@n#j$8&-@p&^@^+i+q&s#t#a$a@r$Y%Y%X$Y%S#Y%F$o@j@P@j@j@l@n@k@j@j@j@P@j@j@p@Q@j@p@n@S@P@R@S@P@j@m@p@T@n@l@j@Q@n@k@9#P@T@k@n@P@j@n@k@k@j@j@k@l@j@j@P@~#Y$Y$Z$j$<#L#|$|$;$|$;$`#`#L# ",
+" = * * + * # - % * : # # % % + = # - + * ; - - + * = , : : # 6 , # ; # ^ # - ; - # # # : = , $ ^ % % + # 7 : : : : : : : : : : : : : : : : : : : : : : : : ~#j@j@j@n@j@p@n@S@n@P@R@j@P@P@j@k@j@j@j@j@j@j@j@j@n@n@j@j@j@k@P@Q@j@P@j@n@k@n@k@j@n@k@j@j@j@j@l@P@n@P@p@p@j@j@n@P@p@k@P@n@j@j@j@j@n@P@R@j@o@p@j@P@j@n@k@j@P@j@j@k@9#P@R@n@S@k@j@j@j@k@j@j@j@P@j@j@k@k@S@P@k@9#P@k@n@P@p@k@j@j@k@B$P@P@j@j@y$9&0&&$-@+@+#r&e%8%J@X$Z$F$)&)&Y%Y$S$j@n@P@p@j@P@j@P@R@j@o@j@j@j@k@l@j@k@j@j@j@j@j@k@j@P@k@j@j@n@n@k@j@n@n@k@j@P@n@k@p@n@S@P@R@j@o@p@j@n@j@P@o@6&Y%Y%j$;$`#e#j$;$E$`#|$|$`# ",
+" + ; : : # * = + * - # * % = * = # - - # - # ; e # # : , : , - : : : ; # = - # ; : + * # = 0 ^ # + % = : : : : : : : : : : : : : : : : : : : : m@j@k@j@j@k@j@j@j@j@P@j@j@n@P@k@j@j@P@j@j@j@P@j@j@Q@P@k@j@j@n@P@j@n@l@n@k@n@P@R@j@S@P@R@n@S@j@m@n@k@j@j@j@j@p@k@j@j@j@n@R@n@S@j@n@n@P@j@j@k@k@n@l@n@k@j@j@l@j@n@S@P@j@n@k@j@p@k@j@j@n@k@j@j@j@P@j@j@k@j@n@P@P@j@n@k@j@j@P@P@P@P@k@j@P@j@R@k@y@A@<$A$:#s&a@-$Z#2%Y%b$B%o@'&S$`%k@P@k@p@n@P@p@k@j@j@j@k@j@P@k@j@P@j@n@k@j@j@j@k@k@j@P@j@j@n@P@P@R@j@P@P@n@j@n@P@R@j@n@k@j@k@j@k@j@k@9#P@R@P@o@F$-%a@;$j$j$;$`#L#L#L#L#=& ",
+" * : ^ 9 + # - - ; ; - # = = # ^ # # : # # : * : : # : , 6 ( ; - # - # # # # - # # - . # [ - ^ # % % : : : : : : : : : : : : : : : : : ~#R@n@n@j@n@k@j@j@k@j@j@j@p@n@S@n@P@p@k@n@P@p@j@j@j@j@n@k@9#P@R@n@S@Q@k@S@o@j@n@P@p@j@n@k@j@k@j@j@j@R@j@o@j@j@P@j@k@P@j@n@k@j@j@n@P@k@j@j@P@n@k@Q@k@S@o@j@j@p@n@j@p@n@S@P@R@j@j@j@j@P@j@k@n@P@p@R@n@P@R@S@j@p@n@P@R@j@j@n@S@S@P@j@i%P@k@m@m@j$$#t&u&R#_@L#Y$S$j@S$S$S$k@j@P@j@j@P@k@j@n@j@j@j@n@k@j@n@P@j@j@j@p@k@P@k@j@P@j@k@j@j@P@k@n@k@k@n@j@k@P@n@P@R@j@o@p@n@S@P@R@S@P@p@n@S@P@j@n@P@m@Y$o@;$r$r$;$<#|$L#L#`#`#E$ ",
+" # # - # # : - > # ; - : * # - * : + ; # # , % ; ; ; ( - ; , 6 , , : , : + ; # ; * + * % = # , ^ % % : : : : : : : : : : : : : : m@j@k@P@j@P@k@P@n@j@j@P@j@j@n@n@k@j@P@n@k@j@j@j@j@k@j@n@S@P@j@n@k@k@j@j@o@P@n@k@j@j@n@S@P@R@S@P@j@j@j@j@k@n@P@p@k@j@p@n@S@P@j@j@P@Q@S@P@j@n@P@R@n@n@o@j@n@k@j@j@k@j@j@j@j@k@k@j@k@j@P@k@n@k@P@j@n@k@j@j@k@j@P@j@j@j@j@n@S@P@n@P@k@j@P@P@j@B$y$_@@&e#_@S$F$`%k@j@p@j@j@U@j@j@n@p@j@P@j@j@j@j@k@k@P@n@k@j@j@j@j@j@j@j@k@n@P@k@k@j@j@Q@B$P@R@n@P@k@j@n@S@P@j@j@k@j@j@j@j@P@k@P@n@k@j@p@n@S@j@m@6&'$<#;$<#;$|$|$`#E$`#;$=& ",
+" = * % % - # # % # : * * - % * : ; # # * # - : - - - 6 : # ; : : e # e - % + : : ; * = % % # + % s : : : : : : : : : : : : ~#R@n@j@j@n@P@T@k@n@P@p@k@P@n@P@R@j@n@P@R@j@o@P@j@n@j@n@j@p@n@S@P@n@k@j@j@n@P@R@j@j@j@j@j@j@k@j@P@k@j@P@n@k@j@j@j@k@j@P@P@p@k@j@P@j@j@j@j@k@P@j@j@j@j@n@n@n@P@n@n@k@j@n@P@n@k@j@n@P@R@n@P@j@p@n@S@P@k@k@k@j@P@j@j@j@j@j@j@j@p@n@S@P@p@j@P@P@j@o@Y%o@F$Z%o@k@m@o@o@j@P@j@p@n@P@k@P@j@j@j@j@P@k@j@n@P@R@j@o@9#j@j@k@j@j@j@n@j@k@k@j@j@n@k@P@j@j@k@j@n@j@j@j@j@n@P@j@j@j@j@P@n@P@k@n@P@P@j@p@~#'&Y%X$j$j$j$|$L#,@|$|$;$E$ ",
+" # % % # : : : * : - - % * * ; : # # - - ; : * # . . # 6 ; # - - e - 6 ; ; # # - # * # * ( { j@j@k@P@p@k@P@n@k@j@j@j@n@j@P@j@n@k@P@j@j@n@l@n@j@j@P@P@j@j@j@j@j@k@j@j@j@P@j@j@k@j@j@j@k@k@j@P@j@j@n@P@R@j@o@k@n@P@p@j@j@j@P@R@n@S@j@j@n@n@j@j@P@j@p@p@p@Q@n@T@k@9#P@R@n@R@j@P@j@n@k@P@j@n@k@j@9#j@p@j@j@P@k@j@P@j@j@P@j@j@n@k@j@j@j@R@n@S@j@B$j@j@o@j@k@j@m@n@P@p@j@P@j@j@n@j@P@P@n@j@j@j@j@j@P@j@j@j@j@P@j@j@j@g$k@k@R@S@P@j@m@k@k@P@k@P@j@j@j@n@S@P@n@k@j@n@P@o@j@k@j@P@n@k@n@P@j@p@o@i@X$;%]$r$;$;$|$<#;$`#L#|$ ",
+" # . # : : ; # ^ 9 + # - = % * | # - ; - % - # * = # ; : : ( , ( 6 # # - ; : # - % + # * * = j@k@k@j@j@j@n@P@R@j@o@p@T@j@j@n@S@k@j@P@j@j@Q@k@n@P@p@p@k@j@j@P@j@k@j@P@j@l@P@j@n@j@j@P@R@j@j@P@n@k@j@P@j@j@k@n@k@j@j@j@o@p@j@n@k@k@n@P@T@k@n@P@p@j@j@j@j@j@j@k@S@P@j@n@k@j@j@p@n@S@P@p@n@S@P@R@S@j@j@j@p@Q@p@k@P@n@P@p@k@P@P@R@j@o@j@n@k@j@k@j@j@T#j@P@j@j@p@R@j@n@P@p@p@n@S@j@j@k@k@P@P@k@n@P@p@k@k@n@P@p@k@j@j@p@P@k@j@j@j@P@k@k@k@n@P@P@j@P@j@n@j@n@P@R@j@j@j@j@P@j@j@n@P@n@k@j@j@k@j@_@a@j$;$|$;$;$;$;$;$<#`#`# ",
+" * = = # - # - - # # # - # * ; # # - # * - - = * = , 6 # : e : 6 - # ; : * : : ; : ; % * 0 7. ~#R@n@j@j@o@o@n@j@j@P@k@k@j@j@P@j@p@n@S@P@j@j@P@n@k@j@j@j@k@n@P@p@k@j@j@P@k@j@j@j@P@j@S@P@j@j@j@n@n@j@j@P@j@j@n@P@R@j@o@j@k@k@n@S@P@n@k@j@P@n@k@j@j@j@j@j@p@j@j@P@j@p@n@S@P@j@k@j@j@j@k@j@j@j@j@j@j@j@j@k@l@j@j@P@n@k@j@j@n@T@k@j@j@k@n@S@P@R@S@j@k@n@P@p@n@P@j@P@n@k@j@k@j@j@j@P@k@j@R@j@P@n@k@j@j@P@n@k@j@j@j@P@j@P@j@k@j@j@j@P@j@P@p@p@k@n@P@p@k@n@k@j@P@j@j@j@j@P@p@n@k@j@j@P@R@j@p@m@o@'&*&;%<#L#|$|$;$;$|$`#;$c% ",
+" % # # # ; - # # - # # : # ; # = * # # ; - - # # # e - 6 # ( 6 6 ; : : ; : - # ; - ; # # # - ~#k@n@k@j@k@T@k@n@P@p@k@k@n@P@p@k@j@j@j@P@j@j@n@P@R@j@j@P@n@k@j@j@j@n@p@Q@B$j@R@k@k@k@P@l@j@n@P@T@k@n@P@p@k@P@n@S@j@j@k@j@n@j@j@j@n@P@R@j@n@P@R@j@o@j@j@k@j@P@j@j@k@j@j@j@j@P@n@k@j@j@R@j@o@j@j@k@k@k@j@j@P@j@j@n@P@R@j@j@j@k@B$k@j@j@j@j@j@j@k@P@n@k@j@j@P@j@j@n@P@R@j@n@k@j@k@k@n@P@R@j@n@P@R@j@o@n@j@P@j@j@k@n@P@p@k@k@n@P@j@P@k@j@j@P@n@k@j@j@j@n@j@j@j@P@j@j@P@j@j@k@j@P@j@n@S@j@p@j@m@_@a@j$;$`#e#j$;$`#|$;$;$`$ ",
+" = = # # # - # ; : ; : : : : * + + ; - # : - # # : : # ; - # # : : : : - # # - : ; - # - ; ; o#j@P@n@k@P@P@n@k@j@j@P@n@k@j@j@j@j@j@P@j@n@k@j@j@j@j@k@n@P@R@j@o@p@T@n@S@P@j@n@j@k@S@o@n@j@k@j@P@n@k@j@j@j@n@k@j@k@j@k@l@P@j@j@j@P@j@n@k@j@P@j@j@k@j@k@n@P@p@k@P@n@P@j@j@j@n@P@R@j@o@j@j@j@j@j@p@n@S@j@j@j@k@P@k@k@j@j@k@P@P@k@j@P@k@j@j@j@k@j@n@P@R@j@o@j@n@k@j@P@j@j@P@k@j@k@l@P@n@n@k@j@P@j@j@k@n@P@p@k@P@n@k@j@j@P@n@k@j@p@Q@B$P@P@n@P@R@n@P@T@k@n@n@P@p@n@P@p@k@j@j@P@j@R@j@9#k@k@k@k@B%Y%r$;$j$j$;$`#L#L#|$|$E$ ",
+" . * # : # # : : ; : # - % * ; # : ; : ; # # - * ; # e # : + * ; : # - - # ; : ; : : - # # + m@k@k@n@T@k@n@P@R@j@j@n@P@R@j@o@p@n@n@l@n@k@R@j@o@p@j@P@j@P@j@j@k@k@p@n@j@p@n@S@P@n@o@j@j@P@R@j@n@P@R@j@o@p@S@P@R@S@P@j@m@p@j@p@j@l@n@k@j@P@j@j@j@j@P@n@k@j@j@j@n@k@k@j@n@k@n@P@j@j@k@k@j@j@P@j@j@n@j@S@n@j@j@n@P@j@j@p@n@j@p@n@i%P@R@j@P@j@j@j@j@P@j@j@k@n@k@j@n@l@j@j@n@j@P@j@m@p@n@k@j@j@l@j@P@n@k@j@j@j@n@P@R@j@j@n@j@R@n@n@k@j@S@n@j@j@P@k@j@P@n@k@k@j@R@k@j@j@k@n@P@p@R@j@m@m@j@B%Y%'$B%j$j$r$r$;$<#|$L#E$E$E$`# ",
+" ; % = # * : : : - # - # # # # * ; 6 e 6 - - : % # , : : ; - ; ; : % ; # : : ; : - - * + : + ~#R@k@p@P@n@k@j@j@n@k@j@P@j@j@k@j@j@j@Q@k@S@j@j@k@n@P@p@P@p@k@j@j@P@j@j@k@j@j@j@m@j@j@n@n@j@k@k@j@P@j@j@k@k@j@j@P@j@j@P@k@j@k@l@k@n@k@S@o@Q@j@j@j@j@n@P@R@j@o@p@P@j@j@j@j@j@p@n@j@j@n@n@n@P@p@k@P@n@P@k@P@k@9#P@R@n@S@j@j@j@j@j@j@n@S@P@j@j@j@j@p@n@S@j@j@S@S@o@j@k@j@k@P@j@j@P@k@Q@k@S@o@k@k@j@n@P@R@j@o@p@T@P@j@j@k@k@j@j@n@P@R@j@R@k@n@P@p@R@j@n@P@R@j@j@n@R@j@P@n@k@n@n@j@m@m@j@'$(@a@]$j$Y%>%;$r$;$|$|$`#E$`#|$.% ",
+" : . = # # - , # # - - * * - * ; ; - : 6 ; : ; % # # - - e , # - + * : # : : - # - - # - , * ~#R@n@j@n@P@R@j@n@k@j@j@j@j@j@n@k@j@j@j@n@o@j@P@n@k@j@j@j@j@R@n@P@p@k@k@S@P@j@k@j@j@j@p@p@p@k@j@P@j@j@j@n@j@j@j@P@k@j@P@j@j@j@P@n@k@j@j@P@j@P@j@n@k@j@p@n@j@k@j@P@j@j@j@P@j@j@n@j@k@n@n@k@j@j@j@n@P@p@p@n@S@P@j@n@k@j@j@j@j@R@j@j@n@j@p@n@S@k@j@n@k@j@n@k@j@o@j@n@P@R@j@j@k@j@P@j@j@n@o@j@n@P@R@j@j@j@j@k@k@j@l@j@j@n@j@P@R@j@P@j@j@n@n@k@j@j@j@j@p@n@j@j@P@P@j@j@n@P@R@n@k@j@p@~#'&Y%X$j$j$j$a@]$<#;$;$|$L#,@|$|$|$t$ ",
+" * = % % : : ( - = + # * ; : ; # # - # # # - ; ; : : : ; : : % # : % # - - - * ; ; % # # - % j@j@k@j@P@j@Q@k@S@o@j@9#j@j@j@k@k@P@j@j@j@n@P@n@k@j@j@j@j@n@k@j@k@P@n@j@p@n@S@P@k@j@j@j@k@S@j@j@j@P@j@p@k@j@j@j@j@j@P@S@P@j@n@P@R@j@j@R@n@l@n@k@j@k@j@j@j@n@k@j@j@j@j@p@k@n@P@S@n@n@S@R@j@P@n@k@n@j@j@n@j@p@n@S@P@R@j@o@j@j@P@j@j@k@j@j@j@p@n@S@P@n@P@R@j@j@n@n@n@j@j@P@j@j@P@k@m@j@j@n@n@P@R@j@j@P@j@S@o@k@k@j@k@P@j@P@P@n@j@p@n@S@P@R@j@j@P@j@j@n@j@j@P@j@n@k@S@P@j@n@R@j@j@o@i@X$;%]$r$<#j$s$|$;$;$|$`#`#;$|$<#`# ",
+" # $ % + = # * : ; - * * ; : # * * # * * + # - - - # ; - - # # e : # - : - # + # # - # # - e j@k@n@S@P@j@j@n@o@j@n@P@j@k@n@j@k@j@P@j@j@p@n@P@j@j@o@p@n@S@P@R@S@P@j@k@j@j@j@j@9#j@j@p@n@o@j@j@P@p@k@j@j@j@P@j@j@p@Q@9#j@j@P@j@S@P@j@n@k@j@k@S@j@n@k@j@k@P@k@j@j@i%n@j@n@n@P@j@n@k@S@j@j@n@P@R@S@P@j@n@k@j@j@j@P@j@j@k@n@P@p@j@j@p@P@j@k@j@j@j@R@n@S@P@j@j@p@T@k@n@P@p@k@p@Q@B$k@n@P@p@j@o@k@n@P@p@k@o@j@n@P@R@j@j@k@P@n@j@k@j@j@j@P@j@n@P@p@k@P@n@j@n@l@n@k@n@j@p@n@S@R@~#j@j@_@X$;%]$r$;%]$r$L#|$|$;$|$;$;$E$E$L# ",
+" = : = * * * + + : : % # # * # * ; # * ; ; - - # - - # ; # , # ; ( e # # + : : # ; : ; ; = , ~#R@n@n@n@k@j@j@j@j@n@n@P@j@n@k@P@k@n@P@k@j@j@j@p@k@j@k@j@j@j@j@j@k@j@P@k@j@o@P@n@k@p@R@j@j@P@p@k@j@j@j@o@n@P@p@k@j@j@P@n@S@P@p@k@j@p@n@S@P@R@S@o@j@n@P@R@p@n@j@j@j@n@P@j@p@p@Q@n@S@P@k@n@P@p@P@j@j@p@n@S@P@k@j@j@j@P@j@n@k@j@j@P@j@P@p@k@n@S@P@j@n@k@j@n@k@j@j@P@n@k@j@j@k@l@j@P@n@k@j@j@j@k@n@k@j@j@j@j@n@n@P@j@j@k@j@n@k@n@j@j@j@j@j@n@k@j@j@j@n@P@k@j@Q@k@S@o@k@j@j@j@j@m@m@o@_@a@j$>%<#j$s$;$`#e#j$;$|$;$;$|$L#J@ ",
+" : - = 0 * * # - - ; % + # + % * ; % * ; ; # # # + * : ; : : * : ; : , $ f + # : = ; : # > - m@j@j@P@n@P@p@P@n@j@p@p@Q@9#P@R@j@j@p@n@S@P@j@j@j@j@j@n@k@j@j@j@k@k@j@P@j@j@j@n@P@k@j@n@k@P@j@j@R@j@o@P@n@k@j@j@j@n@j@n@k@k@j@j@n@k@j@j@j@k@j@j@j@n@n@P@j@j@n@P@k@9#P@R@n@S@j@j@j@j@P@n@k@j@P@k@k@j@j@j@j@j@j@j@P@P@p@k@P@k@n@P@p@k@P@n@j@n@j@p@n@S@P@j@j@j@j@j@n@P@R@j@j@j@j@j@n@P@R@j@o@p@T@P@R@j@o@j@j@p@p@Q@9#P@j@j@P@n@n@S@j@j@k@j@R@j@j@j@P@T@k@S@o@n@n@o@j@n@S@j@S@R@m@o@i%'&*&r$j$r$r$j$;$j$j$;$<#|$L#L#|$`#c% ",
+" % % * = 0 + # - - ; : ; * : e - - , # # # * * ; # # # - # # # - - - # e # # ; * : - - # ! - m@j@k@j@n@P@k@n@k@j@j@j@P@k@j@P@n@k@j@n@j@j@j@j@j@j@k@P@Q@j@j@j@k@j@j@P@j@j@k@j@n@S@P@n@k@j@j@P@j@j@j@n@P@R@j@o@p@p@n@S@P@R@p@n@S@n@k@j@P@n@k@j@j@p@p@Q@j@j@p@n@S@P@j@n@k@j@P@j@j@j@n@P@R@j@n@P@R@j@o@P@j@j@n@P@p@k@j@n@P@n@k@j@j@j@n@P@k@j@k@j@j@j@j@j@i%j@j@P@j@P@j@j@j@j@j@j@P@p@j@j@k@k@j@P@j@j@k@k@j@j@j@P@n@P@j@O@n@n@k@j@k@P@R@j@o@T@n@P@p@R@9#o@j@n@j@j@n@k@j@k@j@j@m@m@F$B%Y%j$j$s$s$;$r$r$;$;$|$|$`#E$|$|$`$ ",
+" % = % % = * - # ; # # ; * = # % * # g % # * ; # - - # ; : ; ; - # - # - # # : ; ; ; - # # # ~#R@n@k@p@Q@B$P@R@j@j@j@j@j@j@n@P@R@j@P@k@j@S@n@j@j@n@P@j@j@j@P@j@j@j@p@p@n@S@P@n@j@P@j@R@j@P@p@k@P@n@k@j@j@j@k@k@j@j@j@j@j@n@k@j@P@k@j@n@P@R@j@j@j@j@j@P@j@j@n@k@p@n@S@P@j@p@n@n@k@j@P@j@j@n@P@k@j@j@p@n@n@k@j@j@p@n@S@n@P@R@j@o@p@T@n@S@j@n@k@j@j@P@j@n@n@P@p@k@j@j@j@j@n@j@j@j@j@P@P@n@j@n@j@j@j@k@j@j@j@j@j@k@n@j@j@n@S@P@R@S@j@j@p@k@n@k@j@j@n@k@j@n@n@P@j@p@P@R@S@P@j@j@m@o@i@X$;%]$r$r$s$j$j$|$;$;$L#,@|$L#|$E$ ",
+" . = % * = * * + * # # * % % ; : : * h % = * % : # # * : ; = # # = ; % ; # : . % % # 6 # # # j@j@k@j@n@k@n@P@j@j@j@j@j@k@j@P@j@j@n@S@P@k@P@k@9#P@R@n@S@P@p@k@k@j@j@j@n@j@p@n@n@l@n@j@j@j@j@j@n@P@R@j@o@p@n@j@j@j@j@p@n@S@P@P@R@j@p@n@n@j@j@P@j@n@P@p@k@n@P@R@l@j@j@j@k@j@j@j@j@R@j@n@k@p@Q@B$P@j@j@n@P@R@j@k@j@j@j@j@P@j@j@k@k@j@j@j@j@P@j@j@n@j@P@n@k@j@j@j@j@P@j@j@n@S@j@j@k@k@n@k@k@k@P@j@j@S@o@k@j@j@k@j@k@P@Q@j@j@j@j@j@P@j@j@n@P@R@j@P@j@k@j@p@p@Q@j@j@j@j@k@j@P@m@m@j@_@a@j$<#s$j$j$|$<#L#|$|$|$<#`#`#|$L# ",
+" = = % # - ; + : # * # = % # - - : = = % # : # - # - : : - ; : # % % * # # = = % - # # ( ; = j@j@k@j@P@n@k@j@k@S@n@j@j@n@P@j@j@j@n@j@p@p@n@S@P@j@n@k@j@j@j@j@n@k@j@j@j@k@j@j@j@Q@k@n@P@j@o@p@T@P@j@j@k@k@j@j@j@P@j@j@j@j@P@j@S@P@j@T@k@n@P@p@n@k@j@j@j@P@j@j@j@j@j@j@o@P@k@9#P@R@n@S@R@j@n@k@p@k@j@n@k@j@j@n@k@j@j@j@n@j@j@j@j@j@n@S@j@P@j@n@k@j@n@P@R@j@o@n@P@p@k@n@k@j@k@j@k@l@P@P@j@R@j@j@P@j@j@n@Q@n@j@j@n@P@j@k@9#P@n@P@p@k@j@n@P@k@j@l@n@j@j@j@j@R@n@S@P@j@n@j@m@m@o@i%'&*&r$>%j$|$>$j$;$`#e#j$;$|$;$;$`#L# ",
+" = - = # # - # = * + ; = # ; - - = = = # - # # * # - # - ; * # * % = = # = - = * # = - # : - m@k@R@n@j@n@P@R@j@P@k@P@k@9#P@R@n@S@j@j@k@j@j@n@j@p@n@S@P@R@k@n@j@P@k@j@j@j@n@k@j@j@P@n@k@j@j@k@k@n@j@p@n@S@P@R@S@P@p@j@P@j@j@P@n@j@p@j@P@n@k@j@n@P@R@j@o@n@l@n@j@j@P@j@P@p@n@S@P@j@n@k@j@k@9#j@j@n@l@n@k@j@j@l@n@j@n@j@j@n@j@j@P@j@j@n@j@j@P@n@P@R@k@n@k@j@j@k@k@j@j@n@S@P@R@n@P@p@m@p@p@j@k@n@P@p@j@n@n@k@P@k@9#P@R@n@S@P@j@n@S@j@j@j@p@n@S@P@Q@k@n@P@P@j@n@k@j@p@n@S@j@R@B$k@F$B%Y%j$j$>%<#`#<#;$j$j$;$;$|$;$|$|$`# ",
+" : : : % # - = ; : - > : * = % = * # # # # % : # ; * * # # * # - = = . - # - # - # ; ; 6 , # ~#U@k@j@k@P@P@n@j@p@p@n@S@P@j@n@k@j@k@j@n@k@n@j@k@j@j@j@j@P@n@k@j@n@k@j@j@j@P@j@j@P@j@P@R@j@j@P@j@j@k@j@j@j@j@j@k@j@j@P@j@n@P@p@j@k@j@j@n@P@R@j@o@P@k@j@9#P@R@n@n@P@p@k@k@n@k@j@p@n@S@P@R@S@P@j@n@j@Q@k@S@o@k@k@j@k@P@j@T@k@n@P@p@k@j@n@j@p@Q@j@P@j@P@P@j@k@j@P@R@j@j@j@j@j@n@k@j@j@j@j@j@P@n@k@j@j@j@p@p@p@n@S@P@j@n@k@j@p@n@S@j@j@P@j@j@n@j@j@P@n@j@P@j@n@S@P@k@j@j@j@j@j@m@m@o@Y$Y%Y%Z$j$j$;$;$r$r$;$<#|$L#E$E$<#E$ ",
+" : : : # * - ; : * * % = % * # # : ; # ; # : ; : 6 # # # # * : # = % : + * # # - ; ; e 6 e % U@l@j@k@j@k@k@j@k@j@j@j@p@n@S@P@R@S@j@P@k@k@P@j@k@j@j@j@n@P@R@j@k@j@j@j@P@k@n@P@p@k@j@P@n@P@p@k@j@n@k@j@j@j@k@k@j@j@p@n@S@j@j@j@n@k@k@j@P@j@j@k@p@n@S@P@j@n@k@j@j@j@k@l@P@j@k@j@j@j@P@j@j@p@n@n@P@j@n@o@j@n@P@R@j@P@j@P@n@k@j@j@k@n@S@j@l@j@j@n@j@P@j@n@S@P@P@j@j@k@j@j@n@P@R@j@o@p@j@j@n@P@R@P@j@j@j@j@P@n@j@p@n@S@P@n@S@P@j@n@P@p@k@P@n@j@j@n@n@P@p@k@j@p@n@S@P@j@k@n@R@j@m@m@o@Y$Y%Y%j$r$;$r$D%<#;$|$|$`#E$`#>$L# ",
+" : : : : = # # ; - % * * # : ; * # , * # * : # - ( e 6 , # - # - # ; # - + ; * * ; ; : : , ^ ~#R@n@n@B$j@j@k@j@j@k@n@P@p@j@j@j@n@k@P@k@9#P@R@n@S@j@n@k@j@P@j@n@j@j@n@P@p@n@k@j@j@j@j@n@k@j@j@j@P@n@Q@j@j@j@j@j@P@j@j@j@R@j@j@P@P@j@j@n@p@k@j@n@j@n@j@p@n@S@P@R@S@P@j@m@p@P@n@k@n@P@p@k@n@j@P@j@j@m@j@j@n@n@P@j@j@P@j@n@P@R@j@j@n@k@k@k@j@j@j@j@n@l@n@k@j@j@j@j@k@S@o@j@j@P@j@j@k@k@j@p@j@n@P@p@k@P@k@j@j@j@j@j@j@k@j@j@j@p@n@k@j@j@j@n@P@T@P@j@k@j@j@j@k@j@j@j@j@P@p@k@B$P@j@j@m@p@j@-%a@e#>%j$j$;$j$|$L#,@|$;$|$,@ ",
+" : : : : * : : - # * ; : + + * * # ; : # * # , ; , 6 - 6 ; = % + % ; # - # : 6 - - - - # # # j@j@P@p@k@n@j@j@P@n@k@j@j@j@j@j@p@p@n@S@P@j@n@k@l@n@k@j@j@p@n@k@P@j@k@j@j@P@R@j@k@n@k@P@n@n@j@j@n@P@j@j@j@k@n@P@p@k@k@j@j@P@j@P@j@n@k@j@j@j@n@R@n@j@j@P@j@j@j@j@k@j@P@k@j@j@P@n@k@j@j@n@k@j@P@k@k@j@j@j@p@p@Q@9#P@T@n@S@P@j@p@n@S@P@R@j@j@P@j@j@j@Q@k@S@o@k@k@j@n@o@j@n@P@p@k@P@n@j@j@k@n@k@j@j@j@j@P@j@P@P@l@j@P@k@n@k@j@j@P@R@j@o@p@T@n@n@l@n@k@j@o@j@n@j@j@j@j@j@j@P@k@j@S@R@j@j@j@o@Y%x#j$>%r$;$>%;$|$<#`#<#L#`# ",
+" : : : : : # = = ; # = : : # = - = ; # : : ; * ; ; 6 : ; : ; ; : : : # - * : - : * - # - # ; j@k@j@j@T@k@n@P@n@P@R@j@j@P@k@j@j@j@n@j@p@n@S@P@Q@k@S@o@k@j@j@R@j@P@P@P@k@j@j@j@P@n@k@P@k@P@k@9#P@R@n@S@P@n@k@j@j@j@p@n@P@p@k@l@n@k@j@j@o@9#P@R@k@n@P@p@n@j@j@k@k@j@P@j@j@j@n@P@R@p@n@S@P@p@Q@j@n@S@k@j@j@j@P@k@j@P@j@j@j@k@j@j@p@T@k@n@P@p@k@k@j@p@n@o@j@n@P@R@j@j@n@k@j@j@j@n@P@k@S@n@P@R@j@o@p@P@p@k@j@j@j@p@Q@n@P@R@j@P@j@j@j@k@k@j@j@j@Q@k@S@j@k@n@S@j@j@P@j@k@n@P@k@j@j@j@m@m@o@i%Y$S#j$j$<#X$j$j$>%;$;$|$`#`# ",
+" : : : : : , # : - % + ; ; % = # + = ( : e 6 ( 6 : - , 6 e 6 ; - - # : # , ; : - - # ; : ; % ~#R@n@j@j@P@n@k@k@k@P@j@j@j@j@j@j@j@j@j@k@j@j@j@j@j@n@o@j@n@S@P@R@n@j@S@P@j@i%P@j@n@P@j@p@p@n@S@P@j@n@k@k@n@S@j@j@P@j@n@k@j@j@j@Q@k@S@o@j@j@P@j@n@n@k@j@P@j@o@p@j@j@j@P@j@j@P@j@j@k@j@j@j@k@j@p@n@S@j@n@P@j@j@P@j@j@n@P@j@j@n@k@j@j@P@n@k@j@j@n@P@k@j@j@j@n@n@P@j@j@j@P@R@j@j@j@T@n@n@o@j@P@j@j@k@k@j@j@p@j@j@k@l@j@j@P@j@P@j@j@j@j@j@P@j@n@j@j@n@o@j@n@R@k@n@P@p@j@j@p@n@S@P@n@P@j@j@j@j@S$X$Z$r$X$Y%Y%Z$j$j$;$j$;$L# ",
+" : : : : : # - + # # ; : 6 - ; 6 : : : - : ; ; # : * ; : : 6 6 , , : # : : : = # , - # : - = m@j@n@k@j@n@P@R@j@n@P@j@j@j@j@j@P@j@k@j@n@k@j@j@j@j@j@j@n@n@P@j@n@k@P@j@P@p@j@n@R@n@S@j@j@j@n@j@p@n@S@P@k@T@k@n@P@p@k@P@R@j@o@n@S@n@o@j@n@P@p@n@S@k@n@P@p@k@k@k@9#P@T@k@n@P@p@k@P@n@k@j@j@n@k@j@j@j@n@n@P@n@P@p@k@P@n@n@j@j@P@j@j@o@n@n@k@k@j@n@k@S@j@j@j@p@p@Q@j@P@j@j@n@n@S@P@j@m@j@j@n@n@p@j@n@j@j@n@P@j@j@k@k@j@P@n@k@j@j@P@j@n@P@p@k@P@k@j@P@j@n@j@P@n@k@j@j@P@j@j@n@j@j@V@R@j@p@j@V@j@@#r$X$X$F$Y%Y%j$r$;$;$;$t$ ",
+" : : : : : : = * - 7 # : - : - - # # : # ; : * : ( ^ # - # ; : : e # # : - # ; # - # # : * | m@p@P@k@k@j@j@j@9#P@R@n@S@j@j@k@k@n@j@j@P@k@j@j@j@S@j@j@p@p@p@n@S@P@p@k@n@k@j@j@n@k@j@P@l@j@j@k@j@j@j@k@j@P@n@k@j@j@j@n@n@j@j@P@j@j@j@j@k@j@j@j@P@n@k@j@j@j@j@n@k@j@P@n@k@j@j@j@n@j@j@P@j@n@P@j@n@k@p@p@n@k@j@j@j@n@P@k@n@P@p@k@j@k@n@S@P@R@j@p@j@j@o@k@j@j@j@n@P@p@k@P@n@j@P@j@P@k@j@j@p@j@j@j@j@j@9#P@R@n@S@j@j@j@n@P@k@n@P@p@k@k@j@P@T@k@n@P@p@j@p@k@n@P@R@j@P@p@k@P@j@P@j@o@m@o@m@o@m@o@`%X$Y%b$B%a@-%a@e#;$j$<#t$ ",
+" : : : : : : = 0 # # * * - - - # # # : = - # ; # # # # - ; # - - e - - # - ; ; - # : - # = | j@o@j@k@j@j@n@P@j@n@k@j@k@j@k@l@P@n@j@n@n@P@p@j@j@k@j@j@k@j@j@j@j@p@n@S@P@p@n@S@P@k@j@j@k@k@n@k@j@k@n@j@n@P@R@j@o@P@T@k@n@P@p@k@k@j@j@P@j@o@j@n@P@R@j@o@n@Q@P@j@j@n@P@R@j@o@p@T@n@P@p@n@S@p@n@S@P@j@n@k@R@k@j@p@j@R@n@k@j@j@j@n@k@j@j@P@j@j@j@j@j@k@k@j@P@n@k@j@j@j@n@n@P@p@k@P@j@j@j@j@j@P@j@n@S@P@j@n@k@j@k@j@j@j@P@n@k@j@j@j@n@k@j@P@n@k@j@j@j@P@n@k@j@k@j@j@j@j@p@n@S@R@F$Z%o@B$j@p@j@m&Y$b$S$j@F$Y%a@x#r$;$E$|$ ",
+" : : : : : : % + # 6 # - - # # - # = * = + + + = : # - : ( , ( 6 # # % + * : - - # - - { # p@j@j@k@P@j@j@p@n@S@P@R@S@P@j@m@p@j@j@j@j@j@j@P@j@j@j@j@n@k@j@R@k@j@j@j@k@j@j@j@j@R@j@P@j@j@k@k@j@P@P@k@j@P@j@j@k@j@P@n@k@j@j@j@j@n@P@p@k@k@j@j@j@j@j@k@k@n@9#n@k@j@P@j@j@k@k@j@k@j@j@j@j@j@n@j@p@n@S@P@j@S@P@j@j@n@k@R@j@o@n@S@P@R@P@k@j@P@j@j@j@n@j@j@n@P@R@j@o@P@n@k@j@j@j@P@i%j@j@j@j@j@j@j@j@j@n@S@P@R@S@P@j@j@n@P@R@j@j@P@P@R@j@j@P@P@P@j@j@n@n@R@P@j@j@p@j@j@n@j@j@j@j@o@j@j@P@j@k@m@p@m@o@i%j@V@'$S#j$j$X$ ",
+" : : : : : : % - ; # - # : # ; : # - : # # # * = : : # : e : 6 - * : : : ; * * h = % | % ~#j@k@k@j@j@k@j@j@j@j@j@j@k@j@P@k@j@P@j@j@j@n@P@p@j@j@o@j@j@P@n@P@j@k@j@j@n@k@j@o@j@i%P@p@k@j@j@k@P@P@j@j@i%P@j@j@j@P@j@j@j@k@j@j@n@P@j@j@j@S@k@m@j@k@j@n@n@Q@j@j@o@k@k@p@j@n@j@j@R@j@P@j@k@g$j@k@j@j@j@P@j@j@j@p@n@P@R@j@j@k@k@j@j@k@j@n@P@p@k@j@k@P@j@j@j@P@j@j@k@k@P@R@j@o@p@k@m@j@m@m@p@m@j@j@j@j@k@k@j@j@P@j@n@k@j@P@P@j@j@j@j@j@m@m@o@i%m@o@i%j@j@j@Z%o@k@i%P@j@j@n@j@j@T#j@j@'$Y$i%j@m@m@m@o@i%j@j@k@S$F$Z%j@ ",
+" : : : : : : : ; # # % - - : ; : # # # + = = * # - - # ( 6 6 # # : : : # * ; % % { % 0 ~#R@n@n@j@j@k@j@P@j@j@j@P@T#o@j@o@P@P@j@m@m@k@j@T@j@m@j@g$n@P@o@n@j@k@S@o@k@k@j@k@Q@j@j@B$j@k@m@o@j@k@p@p@j@n@j@o@j@j@k@T#k@o@k@j@P@j@j@o@j@j@j@o@o@o@j@n@P@R@j@j@j@j@o@j@j@S@j@j@j@j@j@k@o@k@U@j@j@j@j@j@P@j@j@j@k@j@j@j@j@n@j@j@P@p@p@k@j@Q@j@n@j@j@j@j@j@l@j@j@n@j@j@j@j@k@k@o@k@m@j@o@m@k@o@Q@j@j@k@j@m@m@j@n@j@j@j@P@p@j@n@S@n@j@j@o@j@j@k@m@m@o@i%j@o@j@j@j@j@j@k@m@U@m@o@~#'&Y%Z%o@j@m@m@o@i%j@T#j@o@i%j@o@U@ ",
+" : : : : : : : + # - - # : - # ; # # * % ; * * - - , # : + : : : : : # * ; > - k = j@k@!&S$X$F$j@@#F$S$S$@#Y$X$F$S$F$S$@#Y$j@j@U@k@k@j@@#Y$j@S$@#Y$v&Y$Y$j@S$k@j@v&Y$j@k@k@j@v&Y$j@v&Y$j@j@k@!&S$k@j@v&Y$B%Z$X$j@!&S$k@j@X$X$F$S$@#Y$X$S$Y%v&Y$Y$j@k@v&Y$Y$v&Y$v&X$F$S$@#Y$S$@#Y$X$F$S$@#@#@#Y$k@j@v&Y$!&S$P@P@j@j@o@U@j@j@j@p@j@Q@P@j@n@Q@k@o@j@P@j@B%P@j@j@Q@j@P@j@F$S$@#Y$X$Z$j@j@k@j@j@j@k@o@l@o@j@o@k@p@k@S@j@P@k@o@j@S$o@n@P@Q@n@j@o@S$@#Y$X$F$S$@#S$@#S$@#S$@#Y$X$F$S$@#Y$X$k@j@G@o@n@j@k@k@N@ ",
+" : : : : : : : : = - - ; # # # , # # ; - - % * : : : : : : : : : : % * ; = # % ",
+" : : : : : : : : : : : * : : : : - ; : % * - : : : : : : : : : % 0 + # k # ",
+" = : : : : : : : : : : : : # - # : : : : : : : : : : : : # # * - + ; ! ",
+" : : : : : : : : : : : : : : : : : : : : : : : - - % # = - # % % # ",
+" : : : : : : : : : : : : : : : : : : : : : # % * + : g + - % - ",
+" : : : : : : : # = : : : : : : : ; - - - > # + % # > # ^ + % ",
+" : : : : : : : % + # : ; # + = = # % # - - - = # = e ",
+" : : : : : # = # + * - # = % % # - > # = = = # ",
+" : : : : + # # = * * = h % # = - - : # ",
+" ^ * * - : = % = % # * # ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" "};
diff --git a/src/plugins/preflighting/icons/Appvalidator.icns b/src/plugins/preflighting/icons/Appvalidator.icns
new file mode 100644
index 0000000..970e568
--- /dev/null
+++ b/src/plugins/preflighting/icons/Appvalidator.icns
Binary files differ
diff --git a/src/plugins/preflighting/plugin.properties b/src/plugins/preflighting/plugin.properties
new file mode 100644
index 0000000..881cc33
--- /dev/null
+++ b/src/plugins/preflighting/plugin.properties
@@ -0,0 +1,3 @@
+#Properties file for com.motorolamobility.preflighting
+providerName=Motorola Mobility, Inc.
+pluginName=MOTODEV Studio App Validator \ No newline at end of file
diff --git a/src/plugins/preflighting/plugin.xml b/src/plugins/preflighting/plugin.xml
new file mode 100644
index 0000000..02d087e
--- /dev/null
+++ b/src/plugins/preflighting/plugin.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?eclipse version="3.4"?>
+<plugin>
+ <extension-point id="com.motorolamobility.preflighting.outputter" name="App Validator Outputter" schema="schema/com.motorolamobility.preflighting.outputter.exsd"/>
+
+ <extension
+ id="MOTODEVApplicationValidator"
+ point="org.eclipse.core.runtime.applications">
+ <application>
+ <run
+ class="com.motorolamobility.preflighting.internal.PreflightingApplication">
+ </run>
+ </application>
+ </extension>
+ <extension
+ id="appvalidator"
+ point="org.eclipse.core.runtime.products">
+ <product
+ application="com.motorolamobility.preflighting.MOTODEVApplicationValidator"
+ name="MOTODEV Studio Application Validator">
+ <property
+ name="appName"
+ value="MOTODEV Studio Application Validator">
+ </property>
+ </product>
+ </extension>
+
+</plugin>
diff --git a/src/plugins/preflighting/schema/com.motorolamobility.preflighting.outputter.exsd b/src/plugins/preflighting/schema/com.motorolamobility.preflighting.outputter.exsd
new file mode 100644
index 0000000..1494e0b
--- /dev/null
+++ b/src/plugins/preflighting/schema/com.motorolamobility.preflighting.outputter.exsd
@@ -0,0 +1,109 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<!-- Schema file written by PDE -->
+<schema targetNamespace="com.motorolamobility.preflighting" xmlns="http://www.w3.org/2001/XMLSchema">
+<annotation>
+ <appinfo>
+ <meta.schema plugin="com.motorolamobility.preflighting" id="outputter" name="App Validator Outputter"/>
+ </appinfo>
+ <documentation>
+ This extension point provides the facilities needed for the development of a Application Validator outputter.
+ </documentation>
+ </annotation>
+
+ <element name="extension">
+ <annotation>
+ <appinfo>
+ <meta.element />
+ </appinfo>
+ </annotation>
+ <complexType>
+ <sequence minOccurs="1" maxOccurs="unbounded">
+ <element ref="outputter" minOccurs="1" maxOccurs="unbounded"/>
+ </sequence>
+ <attribute name="point" type="string" use="required">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ </annotation>
+ </attribute>
+ <attribute name="id" type="string">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ </annotation>
+ </attribute>
+ <attribute name="name" type="string">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ <appinfo>
+ <meta.attribute translatable="true"/>
+ </appinfo>
+ </annotation>
+ </attribute>
+ </complexType>
+ </element>
+
+ <element name="outputter">
+ <complexType>
+ <attribute name="id" type="string" use="required">
+ <annotation>
+ <documentation>
+ The id to be used together with -output parameter.
+ </documentation>
+ </annotation>
+ </attribute>
+ <attribute name="class" type="string" use="required">
+ <annotation>
+ <documentation>
+ The class containing the actual implementation of the outputter.
+ </documentation>
+ <appinfo>
+ <meta.attribute kind="java" basedOn="com.motorolamobility.preflighting.output.AbstractOutputter:"/>
+ </appinfo>
+ </annotation>
+ </attribute>
+ </complexType>
+ </element>
+
+ <annotation>
+ <appinfo>
+ <meta.section type="since"/>
+ </appinfo>
+ <documentation>
+ 1.0.0
+ </documentation>
+ </annotation>
+
+
+ <annotation>
+ <appinfo>
+ <meta.section type="apiinfo"/>
+ </appinfo>
+ <documentation>
+ You can access App Validator SDK API, by browsing the javadoc of the most important class for outputter functionality &lt;code&gt;com.motorolamobility.preflighting.output.AbstractOutputter&lt;/code&gt;.
+ </documentation>
+ </annotation>
+
+ <annotation>
+ <appinfo>
+ <meta.section type="implementation"/>
+ </appinfo>
+ <documentation>
+ Developers may use &lt;code&gt;AbstractOutputter&lt;/code&gt; as a base class to inherit the most common operations for a new outputter.
+ </documentation>
+ </annotation>
+
+ <annotation>
+ <appinfo>
+ <meta.section type="copyright"/>
+ </appinfo>
+ <documentation>
+ Copyright (C) 2012 The Android Open Source Project
+ </documentation>
+ </annotation>
+
+</schema>
diff --git a/src/plugins/preflighting/src/com/motorolamobility/preflighting/i18n/PreflightingNLS.java b/src/plugins/preflighting/src/com/motorolamobility/preflighting/i18n/PreflightingNLS.java
new file mode 100644
index 0000000..63d82c2
--- /dev/null
+++ b/src/plugins/preflighting/src/com/motorolamobility/preflighting/i18n/PreflightingNLS.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.preflighting.i18n;
+
+import org.eclipse.osgi.util.NLS;
+
+public class PreflightingNLS extends NLS
+{
+ private static final String BUNDLE_NAME = "com.motorolamobility.preflighting.i18n.messages"; //$NON-NLS-1$
+
+ public static String ApplicationParameterInterpreter_CheckerNotFound;
+
+ public static String ApplicationParameterInterpreter_ListAvailableCheckersMessage;
+
+ public static String ApplicationParameterInterpreter_HelpMessage;
+
+ public static String ApplicationParameterInterpreter_InvalidVerbosityLevel;
+
+ public static String ApplicationParameterInterpreter_InvalidWarningLevel;
+
+ public static String ApplicationParameterInterpreter_ParameterVerbosityDescription;
+
+ public static String ApplicationParameterInterpreter_ParameterWarningDescription;
+
+ public static String ApplicationParameterInterpreter_V_W_PARAMETER_ERROR;
+
+ public static String ApplicationParameterInterpreter_InvalidPort;
+
+ public static String ApplicationParameterInterpreter_UsingDefaultPort;
+
+ public static String CommandLineInputProcessor_IncorrectSyntax;
+
+ public static String HelpPrinter_Device_Id;
+
+ public static String HelpPrinter_Checker_Description;
+
+ public static String HelpPrinter_Checker_Id;
+
+ public static String HelpPrinter_Checker_Name;
+
+ public static String HelpPrinter_Checker_NotAvailable;
+
+ public static String HelpPrinter_DefaultMessage;
+
+ public static String HelpPrinter_IOExceptionMessage;
+
+ public static String HelpPrinter_OptionsMessage;
+
+ public static String HelpPrinter_ProgramDescritpion;
+
+ public static String HelpPrinter_ProgramName;
+
+ public static String HelpPrinter_Usage;
+
+ public static String HelperPrinter_CheckerId;
+
+ public static String HelperPrinter_CheckerDescription;
+
+ public static String HelperPrinter_CheckerUsesParameters;
+
+ public static String HelperPrinter_CheckerDoesNotUseParameters;
+
+ public static String HelperPrinter_CheckerHasConditions;
+
+ public static String PreflightingApplication_VerboseMessage_ForwardingResultsMessage;
+
+ public static String PreflightingApplication_VerboseMessage_StartingProcessMessage;
+
+ public static String ApplicationParameterInterpreter_ListAvailableDevicesMessage;
+
+ public static String ApplicationParameterInterpreter_DescribeDeviceMessage;
+
+ public static String GlobalInputParamsValidator_NonExistentDeviceIdMessage;
+
+ public static String Daemon_LinsteningMessage;
+
+ public static String Daemon_StartingErrorMessage;
+
+ public static String Daemon_Stopped;
+
+ public static String Daemon_StartingStatusMessage;
+
+ public static String Daemon_ValidationError;
+
+ public static String Daemon_TestDaemonSucceedTry;
+
+ public static String Daemon_TestDaemonFailedTry;
+
+ public static String Daemon_TestDaemonStatusMessage;
+
+ public static String OutputterFactory_OutputParametersValidMessage;
+
+ public static String OutputterFactory_OutputParametersInvalidMessage;
+
+ public static String OutputterFactory_TextOutputFormatMessage;
+
+ public static String OutputterFactory_ValidationOutputModeMessage;
+
+ public static String OutputterFactory_XmlOutputFormatNotImplementedMessage;
+
+ public static String TextOutputter_File_Prefix;
+
+ public static String TextOutputter_FixSuggestionMessage;
+
+ public static String TextOutputter_Folder_Prefix;
+
+ public static String TextOutputter_IOExceptionMessage;
+
+ public static String TextOutputter_LineMessage;
+
+ public static String TextOutputter_LinesMessage;
+
+ public static String TextOutputter_PrintResultsErrorMessage;
+
+ public static String TextOutputter_ResultsMessage;
+
+ public static String TextOutputter_ApplicationMessage;
+
+ public static String TextOutputter_ExecutionReportExecutedMsg;
+
+ public static String TextOutputter_ExecutionReportSeparator;
+
+ public static String TextOutputter_ExecutionReportTitle;
+
+ public static String TextOutputter_OneOccurrenceMessage;
+
+ public static String XMLOutputter_PrintResultsErrorMessage;
+
+ public static String TextOutputter_MoreThanOneOccurrenceMessage;
+
+ public static String ApplicationParameterInterpreter_OutputParam;
+
+ static
+ {
+ // initialize resource bundle
+ NLS.initializeMessages(BUNDLE_NAME, PreflightingNLS.class);
+ }
+
+ private PreflightingNLS()
+ {
+ }
+}
diff --git a/src/plugins/preflighting/src/com/motorolamobility/preflighting/i18n/messages.properties b/src/plugins/preflighting/src/com/motorolamobility/preflighting/i18n/messages.properties
new file mode 100644
index 0000000..e3a643b
--- /dev/null
+++ b/src/plugins/preflighting/src/com/motorolamobility/preflighting/i18n/messages.properties
@@ -0,0 +1,61 @@
+ApplicationParameterInterpreter_CheckerNotFound=Checker not found:
+ApplicationParameterInterpreter_ListAvailableCheckersMessage=List available checkers
+ApplicationParameterInterpreter_HelpMessage=See information about general usage or about a checker by specifying its ID (CHK)
+ApplicationParameterInterpreter_InvalidVerbosityLevel=The specified verbosity level is invalid.
+ApplicationParameterInterpreter_InvalidWarningLevel=The specified warning level is invalid.
+ApplicationParameterInterpreter_ParameterVerbosityDescription=Specify verbosity level, where N is one of: 0 (default, most succinct), 1, 2 (most verbose)
+ApplicationParameterInterpreter_ParameterWarningDescription=Specify warning level, where N is one of: 0 (most severe warnings only), 1, 2 (default), 3, 4 (all warnings)
+ApplicationParameterInterpreter_V_W_PARAMETER_ERROR=Parameter {0} does not expect a value.
+ApplicationParameterInterpreter_InvalidPort=Invalid port value: {0}
+ApplicationParameterInterpreter_UsingDefaultPort=No port value defined. Using default port [{0}]
+CommandLineInputProcessor_IncorrectSyntax=Incorrect syntax: -{0}\nCheck usage with the -help switch
+HelpPrinter_Device_Id=Available Devices:
+HelpPrinter_Checker_Description=Description:
+HelpPrinter_Checker_Id=ID:
+HelpPrinter_Checker_Name=Name:
+HelpPrinter_Checker_NotAvailable=Not available
+HelpPrinter_DefaultMessage=Default:
+HelpPrinter_IOExceptionMessage=An I/O exception occurred.
+HelpPrinter_OptionsMessage=Options:
+HelpPrinter_ProgramDescritpion=Analyze the Android APK or Android project specified by the FILE parameter. \n(The Android SDK tools directory set in the PATH environment variable is used by default)
+HelpPrinter_ProgramName=APPVALIDATOR
+HelpPrinter_Usage=Usage:
+HelperPrinter_CheckerId=Checker ID:
+HelperPrinter_CheckerDescription=Checker Description:
+HelperPrinter_CheckerUsesParameters=This checker accepts the following parameters:
+HelperPrinter_CheckerDoesNotUseParameters=Checker does not accept parameters
+HelperPrinter_CheckerHasConditions=This checker checks the following conditions and default severity levels:
+PreflightingApplication_VerboseMessage_ForwardingResultsMessage=Forwarding results...
+PreflightingApplication_VerboseMessage_StartingProcessMessage=Starting validation process...
+ApplicationParameterInterpreter_ListAvailableDevicesMessage=List available devices
+ApplicationParameterInterpreter_DescribeDeviceMessage=Show information about a specific device
+ApplicationParameterInterpreter_OutputParam=Unknown output type "{0}".
+GlobalInputParamsValidator_NonExistentDeviceIdMessage=Device ID does not exist: {0}
+Daemon_LinsteningMessage=Listening on port {0}.
+Daemon_StartingErrorMessage=Problem starting the daemon.
+Daemon_StartingStatusMessage=Starting daemon...
+Daemon_Stopped=Daemon stopped
+Daemon_ValidationError=Validation aborted:
+Daemon_TestDaemonFailedTry=Try {0}: failed
+Daemon_TestDaemonSucceedTry=Try {0}: succeed
+Daemon_TestDaemonStatusMessage=Testing port {0}... press Ctrl + c to abort
+OutputterFactory_TextOutputFormatMessage=Validation output will be provided in plain text format.
+OutputterFactory_OutputParametersInvalidMessage=Value for "output" parameter is invalid.
+OutputterFactory_OutputParametersValidMessage=Output parameters are valid.
+OutputterFactory_ValidationOutputModeMessage=Set validation output mode.
+OutputterFactory_XmlOutputFormatNotImplementedMessage=Validation output will be provided in XML format (NOT YET IMPLEMENTED).
+TextOutputter_File_Prefix=File:\
+TextOutputter_FixSuggestionMessage=Fix suggestion:\
+TextOutputter_Folder_Prefix=Folder:\
+TextOutputter_IOExceptionMessage=An I/O exception occurred.
+TextOutputter_LineMessage=Line\
+TextOutputter_LinesMessage=Lines\
+TextOutputter_PrintResultsErrorMessage=An error occurred while printing the results.
+TextOutputter_ResultsMessage=***** RESULTS *****
+TextOutputter_ApplicationMessage=Application:
+TextOutputter_ExecutionReportExecutedMsg=Executed
+TextOutputter_ExecutionReportSeparator=:
+TextOutputter_ExecutionReportTitle=Execution Report:
+TextOutputter_OneOccurrenceMessage=\ (1 occurrence)
+TextOutputter_MoreThanOneOccurrenceMessage=\ ({0} occurrences)
+XMLOutputter_PrintResultsErrorMessage=Error formatting XML output. \ No newline at end of file
diff --git a/src/plugins/preflighting/src/com/motorolamobility/preflighting/internal/PreflightingApplication.java b/src/plugins/preflighting/src/com/motorolamobility/preflighting/internal/PreflightingApplication.java
new file mode 100644
index 0000000..e62633b
--- /dev/null
+++ b/src/plugins/preflighting/src/com/motorolamobility/preflighting/internal/PreflightingApplication.java
@@ -0,0 +1,239 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.preflighting.internal;
+
+import java.io.PrintStream;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.equinox.app.IApplication;
+import org.eclipse.equinox.app.IApplicationContext;
+
+import com.motorolamobility.preflighting.core.exception.PreflightingParameterException;
+import com.motorolamobility.preflighting.core.exception.PreflightingToolException;
+import com.motorolamobility.preflighting.core.validation.ApplicationValidationResult;
+import com.motorolamobility.preflighting.core.validation.Parameter;
+import com.motorolamobility.preflighting.core.validation.ValidationManager;
+import com.motorolamobility.preflighting.core.validation.ValidationManager.InputParameter;
+import com.motorolamobility.preflighting.core.validation.ValidationResult;
+import com.motorolamobility.preflighting.core.validation.ValidationResultData;
+import com.motorolamobility.preflighting.core.verbose.DebugVerboseOutputter;
+import com.motorolamobility.preflighting.core.verbose.DebugVerboseOutputter.VerboseLevel;
+import com.motorolamobility.preflighting.i18n.PreflightingNLS;
+import com.motorolamobility.preflighting.internal.commandinput.ApplicationParameterInterpreter;
+import com.motorolamobility.preflighting.internal.commandinput.CommandLineInputProcessor;
+import com.motorolamobility.preflighting.internal.commandinput.exception.ParameterParseException;
+import com.motorolamobility.preflighting.internal.commandoutput.OutputterFactory;
+import com.motorolamobility.preflighting.output.AbstractOutputter;
+
+/**
+ * This class controls all aspects of the application's execution
+ */
+public class PreflightingApplication implements IApplication
+{
+
+ /* (non-Javadoc)
+ * @see org.eclipse.equinox.app.IApplication#start(org.eclipse.equinox.app.IApplicationContext)
+ */
+ public Object start(IApplicationContext context)
+ {
+ String commandLine = getCommandLineParameters(context.getArguments());
+ Integer exitCode = validate(commandLine, System.out, System.err);
+ return exitCode;
+ }
+
+ public static Integer validate(String commandLine, PrintStream out, PrintStream err)
+ {
+ Integer exitCode = IApplication.EXIT_OK;
+
+ CommandLineInputProcessor commandLineInputProcessor = new CommandLineInputProcessor();
+
+ AbstractOutputter outputter = null;
+ DebugVerboseOutputter.setStream(err);
+
+ // Get the parameter list
+ List<Parameter> parameterList;
+
+ try
+ {
+ parameterList = commandLineInputProcessor.processCommandLine(commandLine);
+ ValidationManager validationManager = new ValidationManager();
+ outputter = OutputterFactory.getInstance().createOutputter(parameterList);
+
+ List<Parameter> parametersCopy = new ArrayList<Parameter>(parameterList);
+
+ for (Parameter param : parametersCopy)
+ {
+ if (InputParameter.OUTPUT.getAlias().equals(param.getParameterType()))
+ {
+ ApplicationParameterInterpreter.validateOutputParam(param.getValue());
+ outputter = OutputterFactory.getInstance().createOutputter(parameterList);
+ parameterList.remove(param);
+ break;
+ }
+ }
+
+ if (!ApplicationParameterInterpreter.checkApplicationParameters(parameterList,
+ validationManager, out))
+ {
+ try
+ {
+ //path of application or project or apk
+ Parameter pathParam = null;
+ for (Parameter param : parameterList)
+ {
+ if (param.getParameterType().equals(
+ InputParameter.APPLICATION_PATH.getAlias()))
+ {
+ pathParam = param;
+ }
+ }
+
+ DebugVerboseOutputter
+ .printVerboseMessage(
+ PreflightingNLS.PreflightingApplication_VerboseMessage_StartingProcessMessage,
+ VerboseLevel.v2);
+ List<ApplicationValidationResult> validationResults =
+ validationManager.run(parameterList);
+
+ if (validationResults.size() > 0)
+ {
+ DebugVerboseOutputter
+ .printVerboseMessage(
+ PreflightingNLS.PreflightingApplication_VerboseMessage_ForwardingResultsMessage,
+ VerboseLevel.v2);
+ // print empty line to separate
+ DebugVerboseOutputter.printVerboseMessage("", VerboseLevel.v2);
+
+ exitCode = getExitCode(exitCode, validationResults);
+ }
+
+ // Print validation results
+ for (ApplicationValidationResult result : validationResults)
+ {
+ parameterList.remove(pathParam);
+ Parameter appParam =
+ new Parameter(
+ ValidationManager.InputParameter.APPLICATION_PATH
+ .getAlias(),
+ result.getApplicationPath());
+ parameterList.add(appParam);
+ outputter.print(result, out, parameterList);
+ }
+ }
+ catch (PreflightingParameterException pe)
+ {
+ // parameter problem message should already be printed; only set exit
+ // code (do not show message because it is not necessary)
+ exitCode = new Integer(1);
+
+ if (outputter != null)
+ {
+ outputter.printError(pe, out);
+ }
+ }
+ }
+ }
+ catch (ParameterParseException e)
+ {
+ // Command line parameter problem. Show message.
+ DebugVerboseOutputter.printVerboseMessage(e.getMessage(), VerboseLevel.v0);
+ exitCode = new Integer(1);
+ outputter = OutputterFactory.getInstance().createOutputter(null);
+ outputter.printError(e, out);
+
+ }
+ catch (PreflightingToolException e)
+ {
+ exitCode = new Integer(1);
+ DebugVerboseOutputter.printVerboseMessage(e.getMessage(), VerboseLevel.v0);
+ outputter = OutputterFactory.getInstance().createOutputter(null);
+ outputter.printError(e, out);
+ }
+ return exitCode;
+ }
+
+ /**
+ * Verifies if any checker returned an error or a fatal error, and then return the
+ * required exit code for a given situation.
+ * @param exitCode
+ * @param result
+ * @return
+ */
+ private static Integer getExitCode(Integer exitCode, List<ApplicationValidationResult> results)
+ {
+ for (ApplicationValidationResult appValidationResult : results)
+ {
+ List<ValidationResult> result = appValidationResult.getResults();
+ Iterator<ValidationResult> it = result.iterator();
+ while (it.hasNext() && (exitCode < 1))
+ {
+ ValidationResult validationResult = it.next();
+ List<ValidationResultData> resultData = validationResult.getValidationResult();
+ Iterator<ValidationResultData> itData = resultData.iterator();
+ while (itData.hasNext() && (exitCode < 1))
+ {
+ ValidationResultData data = itData.next();
+ if (data.getSeverity() == ValidationResultData.SEVERITY.ERROR)
+ {
+ exitCode = new Integer(2);
+ return exitCode;
+ }
+ }
+ }
+ }
+ return exitCode;
+ }
+
+ /**
+ * Gets parameters from parameter map provided from IApplicationContext.
+ * <br>
+ * Application context parameters are placed in the map using String keys and String[] values
+ *
+ * @param appContextParameters Map<String, String[]> containing parameters
+ * @return String containing all parameters passed to the application
+ */
+ @SuppressWarnings("rawtypes")
+ private String getCommandLineParameters(Map appContextParameters)
+ {
+
+ StringBuffer commandLine = new StringBuffer();
+
+ Iterator iterator = appContextParameters.values().iterator();
+
+ while (iterator.hasNext())
+ {
+ String[] args = (String[]) iterator.next();
+
+ for (int i = 0; i < args.length; i++)
+ {
+ commandLine.append(args[i] + " "); //$NON-NLS-1$
+ }
+ }
+
+ return commandLine.toString();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.equinox.app.IApplication#stop()
+ */
+ public void stop()
+ {
+ // nothing to do
+ }
+}
diff --git a/src/plugins/preflighting/src/com/motorolamobility/preflighting/internal/PreflightingPlugin.java b/src/plugins/preflighting/src/com/motorolamobility/preflighting/internal/PreflightingPlugin.java
new file mode 100644
index 0000000..411535c
--- /dev/null
+++ b/src/plugins/preflighting/src/com/motorolamobility/preflighting/internal/PreflightingPlugin.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.preflighting.internal;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import org.eclipse.core.runtime.IBundleGroup;
+import org.eclipse.core.runtime.IBundleGroupProvider;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.core.runtime.Plugin;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Version;
+
+import com.motorolamobility.preflighting.core.PreflightingCorePlugin;
+import com.motorolamobility.preflighting.core.logging.PreflightingLogger;
+
+public class PreflightingPlugin extends Plugin implements BundleActivator
+{
+ private static PreflightingPlugin instance;
+
+ private String appValidatorVersion = null;
+
+ private static final String COM_MOTOROLAMOBILITY_PREFLIGHTING_FEATURE =
+ "com.motorolamobility.preflighting.feature";
+
+ public PreflightingPlugin()
+ {
+ instance = this;
+ }
+
+ @Override
+ public void start(BundleContext context) throws Exception
+ {
+ PreflightingLogger.debug(PreflightingPlugin.class, "Starting Preflighting Plugin...");
+
+ super.start(context);
+
+ PreflightingLogger.debug(PreflightingPlugin.class, "Preflighting Plugin started...");
+ }
+
+ public static PreflightingPlugin getInstance()
+ {
+ return instance;
+ }
+
+ private void readAppValidatorVersion()
+ {
+ IBundleGroupProvider[] providers = Platform.getBundleGroupProviders();
+ List<IBundleGroup> groups = new ArrayList<IBundleGroup>();
+ if (providers != null)
+ {
+ for (int i = 0; i < providers.length; ++i)
+ {
+ IBundleGroup[] bundleGroups = providers[i].getBundleGroups();
+ groups.addAll(Arrays.asList(bundleGroups));
+ }
+ }
+ for (IBundleGroup group : groups)
+ {
+ if (group.getIdentifier().equals(COM_MOTOROLAMOBILITY_PREFLIGHTING_FEATURE))
+ {
+ appValidatorVersion = group.getVersion();
+ break;
+ }
+ }
+
+ /*
+ * WORKAROUND for commandline product
+ */
+ if (appValidatorVersion == null)
+ {
+ List<Bundle> appValidatorBundles = new ArrayList<Bundle>();
+ appValidatorBundles.add(this.getBundle());
+ appValidatorBundles.add(Platform
+ .getBundle("com.motorolamobility.preflighting.checkers"));
+ appValidatorBundles.add(PreflightingCorePlugin.getContext().getBundle());
+
+ Version v = null;
+ for (Bundle b : appValidatorBundles)
+ {
+ if ((v == null) && (b != null))
+ {
+ v = b.getVersion();
+ }
+ if ((b != null) && (v != null) && (b.getVersion().compareTo(v) > 0))
+ {
+ v = b.getVersion();
+ }
+ }
+
+ appValidatorVersion = v.toString();
+ }
+
+ /*
+ * End of WORKAROUND for commandline product
+ */
+ }
+
+ public String getAppValidatorVersion()
+ {
+ if (appValidatorVersion == null)
+ {
+ readAppValidatorVersion();
+ }
+ return appValidatorVersion;
+ }
+
+}
diff --git a/src/plugins/preflighting/src/com/motorolamobility/preflighting/internal/commandinput/ApplicationParameterInterpreter.java b/src/plugins/preflighting/src/com/motorolamobility/preflighting/internal/commandinput/ApplicationParameterInterpreter.java
new file mode 100644
index 0000000..4be8908
--- /dev/null
+++ b/src/plugins/preflighting/src/com/motorolamobility/preflighting/internal/commandinput/ApplicationParameterInterpreter.java
@@ -0,0 +1,416 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.preflighting.internal.commandinput;
+
+import java.io.PrintStream;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.eclipse.osgi.util.NLS;
+
+import com.motorolamobility.preflighting.core.checker.CheckerDescription;
+import com.motorolamobility.preflighting.core.checker.CheckerExtension;
+import com.motorolamobility.preflighting.core.exception.PreflightingToolException;
+import com.motorolamobility.preflighting.core.validation.GlobalInputParamsValidator;
+import com.motorolamobility.preflighting.core.validation.Parameter;
+import com.motorolamobility.preflighting.core.validation.ParameterDescription;
+import com.motorolamobility.preflighting.core.validation.ParameterType;
+import com.motorolamobility.preflighting.core.validation.ValidationManager;
+import com.motorolamobility.preflighting.core.validation.ValidationManager.InputParameter;
+import com.motorolamobility.preflighting.core.validation.ValidationResultData;
+import com.motorolamobility.preflighting.core.validation.ValidationResultData.SEVERITY;
+import com.motorolamobility.preflighting.core.validation.Value;
+import com.motorolamobility.preflighting.core.verbose.DebugVerboseOutputter;
+import com.motorolamobility.preflighting.core.verbose.DebugVerboseOutputter.VerboseLevel;
+import com.motorolamobility.preflighting.core.verbose.WarningLevelFilter;
+import com.motorolamobility.preflighting.core.verbose.WarningLevelFilter.WarningLevel;
+import com.motorolamobility.preflighting.i18n.PreflightingNLS;
+import com.motorolamobility.preflighting.internal.commandinput.exception.ParameterParseException;
+import com.motorolamobility.preflighting.internal.commandoutput.OutputterFactory;
+import com.motorolamobility.preflighting.internal.daemon.Daemon;
+import com.motorolamobility.preflighting.internal.help.printer.HelpPrinter;
+
+public class ApplicationParameterInterpreter
+{
+ public static final String PARAMETER_WARNING = "wN"; //$NON-NLS-1$
+
+ public static final String WARNING_FLAG = PARAMETER_WARNING.substring(0, 1);
+
+ public static final String PARAM_VERBOSITY = "vN"; //$NON-NLS-1$
+
+ public static final String VERBOSITY_FLAG = PARAM_VERBOSITY.substring(0, 1);
+
+ /**
+ * Theses parameters are defined by Application. Application interprets
+ * these parameters.
+ */
+ public static final String PARAM_HELP = "help"; //$NON-NLS-1$
+
+ public static final String PARAM_LIST_CHECKERS = "list-checkers"; //$NON-NLS-1$
+
+ public static final String PARAM_LIST_DEVICES = "list-devices"; //$NON-NLS-1$
+
+ // Hidden parameter. For internal use only.
+ public static final String KEEP_TEMP_FILES = "keepTempFiles"; //$NON-NLS-1$
+
+ public static final String PARAM_DESC_DEVICE = "describe-device"; //$NON-NLS-1$
+
+ public static final String PARAM_DAEMON = "daemon"; //$NON-NLS-1$
+
+ private static final String PARAM_DEBUG = "showDebugMessages";
+
+ /**
+ * Check if there are application parameters within the parameter list.
+ * Also, retrieve verbosity and warning levels from command line, if any,
+ * and set the appropriate values.
+ *
+ * @param parameters
+ * all parameters passed to application
+ * @return true if there are application parameters, false otherwise
+ * @throws ParameterParseException
+ */
+ public static boolean checkApplicationParameters(List<Parameter> parameters,
+ ValidationManager validationManager, PrintStream printStream)
+ throws ParameterParseException, PreflightingToolException
+ {
+
+ // retrieve warning level and verbosity level so that everything is
+ // set before the application actually runs
+ List<Parameter> parametersCopy = new ArrayList<Parameter>(parameters);
+
+ // Reset the boolean just to make sure that files are not deleted only
+ // when the user passes the appropriate argument
+ validationManager.setDeleteApkTempFolder(true);
+
+ for (Parameter param : parametersCopy)
+ {
+ String parameterType = param.getParameterType();
+
+ // Check for the hidden keepTempFiles parameter
+ if (parameterType.equals(KEEP_TEMP_FILES))
+ {
+ validationManager.setDeleteApkTempFolder(false);
+ parameters.remove(param);
+ }
+ // ignore "-wx", check only for "-wN" parameters (and "-vN")
+ else if (!InputParameter.WARNING_TO_ERROR.getAlias().equals(parameterType)
+ && (parameterType.startsWith(VERBOSITY_FLAG) || parameterType
+ .startsWith(WARNING_FLAG)))
+ {
+ if (param.getValue() != null)
+ {
+ throw new ParameterParseException(NLS.bind(
+ PreflightingNLS.ApplicationParameterInterpreter_V_W_PARAMETER_ERROR,
+ parameterType));
+ }
+
+ if (parameterType.startsWith(VERBOSITY_FLAG))
+ {
+ VerboseLevel verboseLevel = DebugVerboseOutputter.DEFAULT_VERBOSE_LEVEL;
+ try
+ {
+ // if the value passed is not a valid enum value, an
+ // IllegalArgumentException
+ // will be thrown
+ verboseLevel =
+ VerboseLevel.valueOf(VerboseLevel.class, parameterType.trim());
+
+ DebugVerboseOutputter.setCurrentVerboseLevel(verboseLevel);
+ }
+ catch (Exception e)
+ {
+ throw new ParameterParseException(
+ PreflightingNLS.ApplicationParameterInterpreter_InvalidVerbosityLevel);
+ }
+ }
+ else if (parameterType.startsWith(WARNING_FLAG))
+ {
+ WarningLevel warningLevel = WarningLevelFilter.DEFAULT_WARNING_LEVEL;
+ try
+ {
+ // if the value passed is not a valid enum value, an
+ // IllegalArgumentException
+ // will be thrown
+ warningLevel =
+ WarningLevel.valueOf(WarningLevel.class, parameterType.trim());
+ }
+ catch (Exception e)
+ {
+ throw new ParameterParseException(
+ PreflightingNLS.ApplicationParameterInterpreter_InvalidWarningLevel);
+ }
+ WarningLevelFilter.setCurrentWarningLevel(warningLevel);
+ }
+ // remove from original list so it is not passed along
+ parameters.remove(param);
+ }
+ }
+
+ return checkHelpParameter(parameters, validationManager, printStream)
+ || checkListParameters(parameters, validationManager, printStream)
+ || checkDaemonParameter(parameters);
+ }
+
+ public static void validateOutputParam(String paramValue) throws ParameterParseException
+ {
+ if (paramValue != null)
+ {
+ if (!OutputterFactory.getInstance().isOutputterAvailable(paramValue).isOK())
+ {
+ throw new ParameterParseException(PreflightingNLS.bind(
+ PreflightingNLS.ApplicationParameterInterpreter_OutputParam, paramValue));
+ }
+ }
+ else
+ {
+ throw new ParameterParseException(PreflightingNLS.bind(
+ PreflightingNLS.ApplicationParameterInterpreter_OutputParam, ""));
+ }
+ }
+
+ private static boolean checkDaemonParameter(List<Parameter> parameters)
+ throws ParameterParseException, PreflightingToolException
+ {
+ boolean hasDaemonParameter = false;
+ Parameter pDaemon = getParameter(parameters, PARAM_DAEMON);
+ Parameter pSDK = getParameter(parameters, InputParameter.SDK_PATH.getAlias());
+ Parameter pDebug = getParameter(parameters, PARAM_DEBUG);
+
+ String sdkPath = (pSDK == null ? null : pSDK.getValue());
+
+ if (pDaemon != null)
+ {
+ hasDaemonParameter = true;
+
+ int serverPort = Daemon.DEFAULT_PORT;
+ if (pDaemon.getValue() != null)
+ {
+ try
+ {
+ serverPort = Integer.parseInt(pDaemon.getValue());
+ }
+ catch (NumberFormatException nfe)
+ {
+ throw new ParameterParseException(NLS.bind(
+ PreflightingNLS.ApplicationParameterInterpreter_InvalidPort,
+ pDaemon.getValue()));
+ }
+ }
+ else
+ {
+ DebugVerboseOutputter.printVerboseMessage(NLS.bind(
+ PreflightingNLS.ApplicationParameterInterpreter_UsingDefaultPort,
+ Daemon.DEFAULT_PORT), VerboseLevel.v0);
+
+ }
+
+ if (sdkPath != null)
+ {
+ ValidationResultData resultData = new ValidationResultData();
+ GlobalInputParamsValidator.validateSdkParam(resultData, sdkPath);
+
+ if (resultData.getSeverity() != SEVERITY.OK)
+ {
+ throw new ParameterParseException(resultData.getIssueDescription());
+ }
+
+ }
+
+ try
+ {
+ Daemon daemon = new Daemon(serverPort, sdkPath);
+
+ //The first connection test goes to console instead of nullStream
+ daemon.setDebugOn(true);
+
+ daemon.startDaemon();
+
+ //wait for daemon to bound port
+ Thread.sleep(Daemon.BOUND_TIMEOUT);
+
+ daemon.testDaemon();
+
+ // set debug true if the -debugDaemon is passed
+ daemon.setDebugOn(pDebug != null);
+
+ //never exits
+ daemon.join();
+ }
+ catch (Exception e)
+ {
+ throw new PreflightingToolException(e.getMessage(), e);
+ }
+ }
+
+ return hasDaemonParameter;
+ }
+
+ private static boolean checkListParameters(List<Parameter> parameters,
+ ValidationManager validationManager, PrintStream printStream)
+ throws ParameterParseException
+ {
+ boolean hasListParameter = false;
+ Parameter p = getParameter(parameters, PARAM_LIST_CHECKERS);
+ if (p != null)
+ {
+ hasListParameter = true;
+ List<CheckerDescription> checkers = validationManager.getCheckersDescription();
+ HelpPrinter.printCheckersList(checkers, printStream);
+ }
+
+ p = getParameter(parameters, PARAM_LIST_DEVICES);
+ if (p != null)
+ {
+ hasListParameter = true;
+ List<Value> devices = validationManager.getDevicesInfoList();
+ HelpPrinter.printDevicesList(devices, printStream);
+ }
+
+ p = getParameter(parameters, PARAM_DESC_DEVICE);
+ if (p != null)
+ {
+ hasListParameter = true;
+ String deviceId = p.getValue();
+ if (deviceId == null)
+ {
+ throw new ParameterParseException(PreflightingNLS.bind(
+ PreflightingNLS.CommandLineInputProcessor_IncorrectSyntax,
+ p.getParameterType()));
+ }
+ String deviceDescription = validationManager.getDeviceDescription(deviceId);
+ HelpPrinter.printDevicesDescription(deviceDescription, deviceId, printStream);
+ }
+
+ return hasListParameter;
+ }
+
+ private static boolean checkHelpParameter(List<Parameter> parameters,
+ ValidationManager validationManager, PrintStream printStream)
+ {
+ boolean hasHelpParam = false;
+ Parameter p = getParameter(parameters, PARAM_HELP);
+ if (p != null)
+ {
+ hasHelpParam = true;
+ List<ParameterDescription> paramsDescr = null;
+ if (p.getValue() != null)
+ {
+ CheckerExtension checkerExt = ValidationManager.getCheckerExtension(p.getValue());
+ if (checkerExt == null)
+ {
+ printStream
+ .print(PreflightingNLS.ApplicationParameterInterpreter_CheckerNotFound
+ + p.getValue() + "\n\n"); //$NON-NLS-1$
+ paramsDescr = getAllParams(validationManager);
+ HelpPrinter.printHelp(printStream, paramsDescr, true);
+ }
+ else
+ {
+ // informed CHECKERID, print the description from the
+ // extension point declaration and print the parameters
+ // returned by the checker itself.
+ paramsDescr = validationManager.getParametersDescription(p.getValue());
+ // HelpPrinter.printHelp(paramsDescr, false);
+ HelpPrinter.printHelpChecker(printStream, checkerExt);
+ }
+ }
+ else
+ {
+ paramsDescr = getAllParams(validationManager);
+ HelpPrinter.printHelp(printStream, paramsDescr, true);
+ }
+ }
+ return hasHelpParam;
+ }
+
+ private static List<ParameterDescription> getAllParams(ValidationManager validationManager)
+ {
+ List<ParameterDescription> paramsDescr = new ArrayList<ParameterDescription>();
+ if (validationManager.getParametersDescription(null) != null)
+ {
+ paramsDescr.addAll(validationManager.getParametersDescription(null));
+ }
+ if (OutputterFactory.getInstance().getParameterDescriptions() != null)
+ {
+ paramsDescr.addAll(OutputterFactory.getInstance().getParameterDescriptions());
+ }
+
+ // TODO Confirm if it can be removed and also remove ununsed methods
+ /*
+ * paramsDescr.add(createHelpDescription());
+ * paramsDescr.add(createListDescription());
+ */
+
+ ParameterDescription listCheckersDesc = new ParameterDescription();
+ listCheckersDesc.setDefaultValue(null);
+ listCheckersDesc.setName(PARAM_LIST_CHECKERS);
+ listCheckersDesc
+ .setDescription(PreflightingNLS.ApplicationParameterInterpreter_ListAvailableCheckersMessage);
+ paramsDescr.add(listCheckersDesc);
+
+ ParameterDescription listDevDesc = new ParameterDescription();
+ listDevDesc.setDefaultValue(null);
+ listDevDesc.setName(PARAM_LIST_DEVICES);
+ listDevDesc
+ .setDescription(PreflightingNLS.ApplicationParameterInterpreter_ListAvailableDevicesMessage);
+ paramsDescr.add(listDevDesc);
+
+ ParameterDescription describeDevice = new ParameterDescription();
+ describeDevice.setDefaultValue(null);
+ describeDevice.setName(PARAM_DESC_DEVICE);
+ describeDevice.setValueDescription("[DEV]");//$NON-NLS-1$
+ describeDevice
+ .setDescription(PreflightingNLS.ApplicationParameterInterpreter_DescribeDeviceMessage);
+ paramsDescr.add(describeDevice);
+
+ ParameterDescription helpDesc = new ParameterDescription();
+ helpDesc.setName(PARAM_HELP);
+ helpDesc.setDescription(PreflightingNLS.ApplicationParameterInterpreter_HelpMessage);
+ helpDesc.setValueDescription("[CHK]");//$NON-NLS-1$
+ helpDesc.setType(ParameterType.STRING);
+ paramsDescr.add(helpDesc);
+
+ ParameterDescription verbosityDesc = new ParameterDescription();
+ verbosityDesc.setName(PARAM_VERBOSITY);
+ verbosityDesc
+ .setDescription(PreflightingNLS.ApplicationParameterInterpreter_ParameterVerbosityDescription);
+ paramsDescr.add(verbosityDesc);
+
+ ParameterDescription warningDesc = new ParameterDescription();
+ warningDesc.setName(PARAMETER_WARNING);
+ warningDesc
+ .setDescription(PreflightingNLS.ApplicationParameterInterpreter_ParameterWarningDescription);
+ paramsDescr.add(warningDesc);
+
+ return paramsDescr;
+ }
+
+ public static Parameter getParameter(List<Parameter> parameters, String parameter)
+ {
+ Parameter param = null;
+ Iterator<Parameter> it = parameters.iterator();
+ while (it.hasNext() && (param == null))
+ {
+ Parameter p = it.next();
+ if (p.getParameterType().compareTo(parameter) == 0)
+ {
+ param = p;
+ }
+ }
+ return param;
+ }
+}
diff --git a/src/plugins/preflighting/src/com/motorolamobility/preflighting/internal/commandinput/CommandLineInputProcessor.java b/src/plugins/preflighting/src/com/motorolamobility/preflighting/internal/commandinput/CommandLineInputProcessor.java
new file mode 100644
index 0000000..293bc49
--- /dev/null
+++ b/src/plugins/preflighting/src/com/motorolamobility/preflighting/internal/commandinput/CommandLineInputProcessor.java
@@ -0,0 +1,230 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.preflighting.internal.commandinput;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.eclipse.osgi.util.NLS;
+
+import com.motorolamobility.preflighting.core.validation.ComplexParameter;
+import com.motorolamobility.preflighting.core.validation.Parameter;
+import com.motorolamobility.preflighting.i18n.PreflightingNLS;
+import com.motorolamobility.preflighting.internal.commandinput.exception.ParameterParseException;
+
+/**
+ * Process command line commands below:
+ */
+public class CommandLineInputProcessor
+{
+ /**
+ * White space character
+ */
+ private static final String WHITE_SPACE = " "; //$NON-NLS-1$
+
+ /**
+ * Simple parameter marker character
+ */
+ private static final String PARAMETER_MARKER = "-"; //$NON-NLS-1$
+
+ /**
+ * Simple parameter separator
+ */
+ private static final String COMPLEX_PARAMETER_SEPARATOR = "="; //$NON-NLS-1$
+
+ /**
+ *
+ * @param params
+ * @return
+ * @throws ParameterParseException
+ */
+ public List<Parameter> processCommandLine(String params) throws ParameterParseException
+ {
+ // Parse command line arguments and create parameters
+ List<Parameter> commandLineParameters = new ArrayList<Parameter>();
+
+ // if no parameters were passed, assume help will be printed
+ if (params.length() == 0)
+ {
+ Parameter helpParameter =
+ new Parameter(ApplicationParameterInterpreter.PARAM_HELP, null);
+ commandLineParameters.add(helpParameter);
+ }
+ else
+ {
+ // find the pattern " -", which is expected to separate each parameter
+ String regex = "((?!(\\s+" + PARAMETER_MARKER + ")).)*"; //$NON-NLS-1$ //$NON-NLS-2$
+ Pattern pat = Pattern.compile(regex);
+ Matcher matcher = pat.matcher(params);
+ Parameter commandLineparameter = null;
+
+ // for each parameter part found, process it
+ while (matcher.find())
+ {
+ String parameterValues = params.substring(matcher.start(), matcher.end());
+
+ if (parameterValues.length() > 0)
+ {
+ commandLineparameter = getCommandLineParameter(parameterValues);
+ commandLineParameters.add(commandLineparameter);
+ }
+ }
+ }
+ return commandLineParameters;
+ }
+
+ /**
+ * Gets a parameter from command line. Command line arguments are passed to the
+ * application like this: -arg foo bar -c check1 name1=val1 name2=val2
+ *
+ * The parameter for this example is:
+ * name = arg
+ * values = foo, bar
+ *
+ * @param parameterValues String containing the parameter and its values. Eg: -arg foo bar
+ * @return And instance of object Parameter
+ * @throws ParameterParseException
+ */
+ private Parameter getCommandLineParameter(String parameterValues)
+ throws ParameterParseException
+ {
+
+ Parameter parameter = null;
+
+ if (parameterValues.indexOf(COMPLEX_PARAMETER_SEPARATOR) == -1)
+ {
+
+ // Its a simple parameter, example: -param1 value1 or example: -param2
+ parameter = getParameterFromCommandLine(parameterValues);
+
+ }
+ else
+ {
+ // Its a complex parameter, example: -c checker1 file=C:\arg.txt version=2
+ parameter = getComplexParameterFromCommandLine(parameterValues);
+ }
+
+ return parameter;
+ }
+
+ /**
+ * Get a complex parameter from command line. Example: -c checker1 param1=val1 param2=var2
+ * @param values String[] containing the complex parameter. For example, for command line -c checker1 param1=val1 param2=var2
+ * the contents of values are: [[c] [check1] [param1=val1] [param2=val2]]
+ * @return Instance of Parameter, containing a ComplexParameter
+ * @throws ParameterParseException
+ */
+ private Parameter getComplexParameterFromCommandLine(String parameterValues)
+ throws ParameterParseException
+ {
+ // complex parameter example: -c checker param1=val1 param2=val2
+ ComplexParameter parameter = new ComplexParameter();
+
+ String[] values = parameterValues.split(WHITE_SPACE);
+
+ if (values.length < 3)
+ {
+ throw new ParameterParseException(NLS.bind(
+ PreflightingNLS.CommandLineInputProcessor_IncorrectSyntax, parameterValues));
+ }
+ else
+ {
+
+ String parameterType = values[0];
+
+ if (parameterType.startsWith(PARAMETER_MARKER))
+ {
+ parameterType = parameterType.substring(1);
+ }
+ parameter.setParameterType(parameterType);
+ parameter.setValue(values[1]);
+
+ int lenghtToCut = values[0].length() + values[1].length() + 2;
+ String subParameters = parameterValues.substring(lenghtToCut);
+
+ String[] checkSyntax = subParameters.split("\\s*\\w+\\s*" + COMPLEX_PARAMETER_SEPARATOR //$NON-NLS-1$
+ + "\\s*\\w+\\s*"); //$NON-NLS-1$
+ if (checkSyntax.length > 0)
+ {
+ throw new ParameterParseException(NLS.bind(
+ PreflightingNLS.CommandLineInputProcessor_IncorrectSyntax, parameterValues));
+ }
+
+ Pattern pat = Pattern.compile("\\w+\\s*" + COMPLEX_PARAMETER_SEPARATOR + "\\s*\\w+"); //$NON-NLS-1$ //$NON-NLS-2$
+ Matcher matcher = pat.matcher(subParameters);
+
+ while (matcher.find())
+ {
+ String part = subParameters.substring(matcher.start(), matcher.end());
+ String[] pair = part.split(COMPLEX_PARAMETER_SEPARATOR);
+ parameter.addParameter(pair[0].trim(), pair[1].trim());
+ }
+ }
+ return parameter;
+ }
+
+ /**
+ * Get a parameter from command line (example -param value), passed as a String[].
+ * @param parameterValues parameter values
+ * @return Instance of Parameter
+ * @throws ParameterParseException
+ */
+ private Parameter getParameterFromCommandLine(String parameterValues)
+ throws ParameterParseException
+ {
+ Parameter parameter = new Parameter();
+
+ // find first white space to separate tag from value
+ int indexOfFirstWhiteSpace = parameterValues.indexOf(WHITE_SPACE);
+ String parameterType = null;
+ String parameterValue = null;
+
+ // if there is no whitespace, everything is the parameter value necessary
+ if (indexOfFirstWhiteSpace >= 0)
+ {
+ // substring original string from character 1 (skip "-"), to first
+ // whitespace character
+ parameterType = parameterValues.substring(1, indexOfFirstWhiteSpace);
+ // retrieve the rest of the original string (starting from first whitespace)
+ // as the value for the parameter
+ parameterValue = parameterValues.substring(indexOfFirstWhiteSpace).trim();
+ }
+ else
+ {
+ // skip "-"
+ parameterType = parameterValues.substring(1);
+ }
+
+ // check for unexpected format (both parts of the parameter empty)
+ if (((parameterValue == null) || (parameterValue.length() == 0))
+ && ((parameterType == null) || (parameterType.length() == 0)))
+ {
+ throw new ParameterParseException(NLS.bind(
+ PreflightingNLS.CommandLineInputProcessor_IncorrectSyntax, parameterValues));
+ }
+
+ // store parameter type and value
+ parameter.setParameterType(parameterType);
+ if ((parameterValue != null) && (parameterValue.length() > 0))
+ {
+ parameter.setValue(parameterValue);
+ }
+
+ return parameter;
+ }
+}
diff --git a/src/plugins/preflighting/src/com/motorolamobility/preflighting/internal/commandinput/exception/ParameterParseException.java b/src/plugins/preflighting/src/com/motorolamobility/preflighting/internal/commandinput/exception/ParameterParseException.java
new file mode 100644
index 0000000..aa02d2c
--- /dev/null
+++ b/src/plugins/preflighting/src/com/motorolamobility/preflighting/internal/commandinput/exception/ParameterParseException.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.preflighting.internal.commandinput.exception;
+
+public class ParameterParseException extends Exception
+{
+
+ private static final long serialVersionUID = 4702281264766593937L;
+
+ public ParameterParseException(String msg)
+ {
+ super(msg);
+ }
+
+}
diff --git a/src/plugins/preflighting/src/com/motorolamobility/preflighting/internal/commandoutput/CSVOutputter.java b/src/plugins/preflighting/src/com/motorolamobility/preflighting/internal/commandoutput/CSVOutputter.java
new file mode 100644
index 0000000..3682aa0
--- /dev/null
+++ b/src/plugins/preflighting/src/com/motorolamobility/preflighting/internal/commandoutput/CSVOutputter.java
@@ -0,0 +1,385 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.preflighting.internal.commandoutput;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.PrintStream;
+import java.util.List;
+
+import com.motorolamobility.preflighting.core.exception.PreflightingToolException;
+import com.motorolamobility.preflighting.core.logging.PreflightingLogger;
+import com.motorolamobility.preflighting.core.validation.ApplicationValidationResult;
+import com.motorolamobility.preflighting.core.validation.Parameter;
+import com.motorolamobility.preflighting.core.validation.ValidationResult;
+import com.motorolamobility.preflighting.core.validation.ValidationResultData;
+import com.motorolamobility.preflighting.core.validation.ValidationResultData.SEVERITY;
+import com.motorolamobility.preflighting.core.verbose.DebugVerboseOutputter;
+import com.motorolamobility.preflighting.core.verbose.DebugVerboseOutputter.VerboseLevel;
+import com.motorolamobility.preflighting.core.verbose.WarningLevelFilter;
+import com.motorolamobility.preflighting.i18n.PreflightingNLS;
+import com.motorolamobility.preflighting.output.AbstractOutputter;
+
+public class CSVOutputter extends AbstractOutputter
+{
+ private static final int MAX_PREVIEW_LENGTH = 120;
+
+ /**
+ * Line separator
+ */
+ private final String NEWLINE = System.getProperty("line.separator"); //$NON-NLS-1$
+
+ /**
+ * CSV separator
+ */
+ private final String DELIMITER = ",";
+
+ /**
+ * Quote string
+ */
+ private final String QUOTES = "\"";
+
+ /**
+ * Backslash string
+ */
+ private final String BACKSLASH = "\\";
+
+ /**
+ * I/O Exception message logging.
+ */
+ private final String IO_ERROR = PreflightingNLS.TextOutputter_IOExceptionMessage;
+
+ /**
+ * Exception stating that it was not possible to print the results.
+ */
+ private final String PRINT_ERROR = PreflightingNLS.TextOutputter_PrintResultsErrorMessage;
+
+ /**
+ * Writer used for output
+ */
+ private BufferedWriter writer = null;
+
+ private enum CSV_FIELDS
+ {
+ type
+ {
+ @Override
+ public String toString()
+ {
+ return "type";
+ }
+ },
+
+ checker
+ {
+ @Override
+ public String toString()
+ {
+ return "checker_id";
+ }
+ },
+
+ condition
+ {
+ @Override
+ public String toString()
+ {
+ return "condition";
+ }
+ },
+
+ description
+ {
+ @Override
+ public String toString()
+ {
+ return "description";
+ }
+ },
+
+ file
+ {
+ @Override
+ public String toString()
+ {
+ return "file";
+ }
+ },
+
+ line
+ {
+ @Override
+ public String toString()
+ {
+ return "line";
+ }
+ },
+
+ preview
+ {
+ @Override
+ public String toString()
+ {
+ return "preview";
+ }
+ },
+
+ suggestion
+ {
+ @Override
+ public String toString()
+ {
+ return "suggestion";
+ }
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorolamobility.preflighting.core.commandoutput.IOutputter#print(java.util.Map, java.io.OutputStream, java.util.List)
+ */
+ @Override
+ public void print(ApplicationValidationResult results, OutputStream stream,
+ List<Parameter> parameters) throws PreflightingToolException
+ {
+ initializeParams(parameters);
+
+ // Use a BufferedWriter to write the results
+ writer = new BufferedWriter(new OutputStreamWriter(stream));
+ try
+ {
+ if (results.getResults().size() > 0)
+ {
+ printCSVHeader();
+ for (ValidationResult checker : results.getResults())
+ {
+ printCheckerResultCSV(checker);
+ }
+ }
+
+ /*
+ TextOutputter.printExecutionReport(results.getExecutionStatus(), errorStream);
+ */
+ }
+ catch (IOException e)
+ {
+ PreflightingLogger.error(getClass(), IO_ERROR + e.getMessage());
+ throw new PreflightingToolException(PRINT_ERROR, e);
+ }
+ finally
+ {
+ try
+ {
+ writer.close();
+ //to help Garbage Collector to work
+ writer = null;
+ }
+ catch (IOException e)
+ {
+ PreflightingLogger.error(getClass(), IO_ERROR + e.getMessage());
+ }
+ }
+ }
+
+ /**
+ * Prints the CSV fields.
+ */
+ private void printCSVHeader() throws IOException
+ {
+ StringBuilder stringBuilder = new StringBuilder();
+
+ // Append results message
+ CSV_FIELDS[] fields = CSV_FIELDS.values();
+
+ DebugVerboseOutputter.printVerboseMessage("", VerboseLevel.v1);
+ for (int i = 0; i < fields.length; i++)
+ {
+ if (fields[i].compareTo(CSV_FIELDS.suggestion) == 0)
+ {
+ if (WarningLevelFilter.printQuickFixSuggestions())
+ {
+ stringBuilder.append(QUOTES + fields[i].toString() + QUOTES + DELIMITER);
+ }
+ }
+ else
+ {
+ stringBuilder.append(QUOTES + fields[i].toString() + QUOTES + DELIMITER);
+ }
+ }
+
+ stringBuilder.deleteCharAt(stringBuilder.length() - 1);
+ stringBuilder.append(NEWLINE);
+
+ writer.write(stringBuilder.toString());
+ }
+
+ private void printCheckerResultCSV(ValidationResult validationResult) throws IOException
+ {
+ List<ValidationResultData> data = validationResult.getValidationResult();
+
+ if (data.size() > 0)
+ {
+ StringBuilder stringBuilder = new StringBuilder();
+
+ for (ValidationResultData d : data)
+ {
+ if (getApplicationFile() != null)
+ {
+ //No files were issued
+ if (d.getFileToIssueLines().keySet().isEmpty())
+ {
+ printCSVLine(stringBuilder, d, validationResult, null, null);
+ }
+ else
+ {
+ // Iterate through the list of files
+ for (File resource : d.getFileToIssueLines().keySet())
+ {
+ String fileLocation = computeResourcePath(resource);
+ // Number of occurrences
+ List<Integer> lines = d.getFileToIssueLines().get(resource);
+
+ if (lines.isEmpty())
+ {
+ printCSVLine(stringBuilder, d, validationResult, fileLocation, null);
+ }
+ else
+ {
+ for (Integer currentLine : lines)
+ {
+ printCSVLine(stringBuilder, d, validationResult, fileLocation,
+ currentLine);
+ }
+ }
+ }
+ }
+ }
+
+ writer.write(stringBuilder.toString());
+ writer.flush();
+
+ // Clean string builder
+ stringBuilder.delete(0, stringBuilder.length());
+ }
+ }
+ }
+
+ /**
+ * Prints each line of the CSV output.
+ */
+ private void printCSVLine(StringBuilder stringBuilder, ValidationResultData resultData,
+ ValidationResult validationResult, String fileLocation, Integer currentLine)
+ {
+ if (!resultData.getSeverity().equals(SEVERITY.OK))
+ {
+ // Append severity
+ stringBuilder.append(QUOTES + resultData.getSeverity().toString() + QUOTES + DELIMITER);
+
+ //checker id
+ stringBuilder.append(QUOTES + validationResult.getCheckerId() + QUOTES + DELIMITER);
+ //condition
+ stringBuilder.append(QUOTES + resultData.getConditionID() + QUOTES + DELIMITER);
+
+ // Append result description
+ if (resultData.getIssueDescription() != null)
+ {
+ stringBuilder.append(QUOTES + escapeQuotes(resultData.getIssueDescription())
+ + QUOTES);
+ }
+
+ stringBuilder.append(DELIMITER);
+
+ if (fileLocation != null)
+ {
+ stringBuilder.append(QUOTES + fileLocation + QUOTES);
+ }
+ stringBuilder.append(DELIMITER);
+
+ if (currentLine != null)
+ {
+ stringBuilder.append(QUOTES + currentLine + QUOTES);
+ }
+ stringBuilder.append(DELIMITER);
+
+ String preview = resultData.getPreview();
+ if (preview != null)
+ {
+ stringBuilder.append(QUOTES
+ + escapeQuotes(preview.length() > MAX_PREVIEW_LENGTH ? preview.substring(0,
+ MAX_PREVIEW_LENGTH) : preview) + QUOTES);
+ }
+ //stringBuilder.append(DELIMITER);
+
+ /* Removing infoUrl from non-xml outputters
+ if (resultData.getInfoURL() != null)
+ {
+ stringBuilder.append(QUOTES + escapeQuotes(resultData.getInfoURL()) + QUOTES);
+ }
+ */
+
+ if (WarningLevelFilter.printQuickFixSuggestions())
+ {
+ stringBuilder.append(DELIMITER);
+
+ if (resultData.getQuickFixSuggestion() != null)
+ {
+ stringBuilder.append(QUOTES + escapeQuotes(resultData.getQuickFixSuggestion())
+ + QUOTES);
+ }
+ }
+
+ stringBuilder.append(NEWLINE);
+ }
+ }
+
+ /**
+ * Escape quotes from a given string
+ * @param textToEscape string that may contain quotes to be escaped
+ * @return
+ */
+ private String escapeQuotes(String textToEscape)
+ {
+ StringBuilder strBuilder = new StringBuilder();
+ if (textToEscape.indexOf(QUOTES) != -1)
+ {
+ String[] splitedStr = textToEscape.split(QUOTES);
+ for (int i = 0; i < (splitedStr.length - 1); i++)
+ {
+ strBuilder.append(splitedStr[i] + BACKSLASH + QUOTES);
+ }
+ strBuilder.append(splitedStr[splitedStr.length - 1]);
+
+ //special case where the text ends with a quote
+ if (textToEscape.endsWith(QUOTES))
+ {
+ strBuilder.append(BACKSLASH + QUOTES);
+ }
+
+ textToEscape = strBuilder.toString();
+ }
+
+ return textToEscape;
+ }
+
+ @Override
+ public void printError(Exception exceptionThrown, PrintStream out)
+ {
+ // TODO Auto-generated method stub
+
+ }
+}
diff --git a/src/plugins/preflighting/src/com/motorolamobility/preflighting/internal/commandoutput/DaemonXMLOutputter.java b/src/plugins/preflighting/src/com/motorolamobility/preflighting/internal/commandoutput/DaemonXMLOutputter.java
new file mode 100644
index 0000000..0870c73
--- /dev/null
+++ b/src/plugins/preflighting/src/com/motorolamobility/preflighting/internal/commandoutput/DaemonXMLOutputter.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.preflighting.internal.commandoutput;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.util.List;
+
+import org.w3c.dom.CDATASection;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+
+import com.motorolamobility.preflighting.core.exception.PreflightingToolException;
+import com.motorolamobility.preflighting.core.validation.ApplicationValidationResult;
+import com.motorolamobility.preflighting.core.validation.Parameter;
+
+public class DaemonXMLOutputter extends XmlOutputter
+{
+ private static final String XML_TAG_CSV_OUTPUT = "CSVContent";
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorolamobility.preflighting.commandoutput.XmlOutputter#generateCustomApplicationNodes(org.w3c.dom.Element, com.motorolamobility.preflighting.core.validation.ApplicationValidationResult, java.util.List)
+ */
+ @Override
+ protected void generateCustomApplicationNodes(Element applicationElem,
+ ApplicationValidationResult result, List<Parameter> parameters)
+ {
+ /*
+ * Put the validation result as a CDATA section inside xml output
+ */
+ ByteArrayOutputStream outStream = new ByteArrayOutputStream();
+ PrintStream stream = new PrintStream(outStream);
+ CSVOutputter outputter = new CSVOutputter();
+ try
+ {
+ outputter.print(result, stream, parameters);
+ Element csvout = document.createElement(XML_TAG_CSV_OUTPUT);
+ CDATASection cdata = document.createCDATASection(outStream.toString().trim());
+ csvout.appendChild(cdata);
+ applicationElem.appendChild(csvout);
+ Node manifest =
+ document.importNode(
+ result.getXmlResultDocument().getElementsByTagName("manifest").item(0),
+ true);
+ applicationElem.appendChild(manifest);
+
+ }
+ catch (PreflightingToolException e)
+ {
+ //do nothing
+ }
+ finally
+ {
+ stream.close();
+ try
+ {
+ outStream.close();
+ }
+ catch (IOException e)
+ {
+ //Do nothing.
+ }
+ }
+ }
+}
diff --git a/src/plugins/preflighting/src/com/motorolamobility/preflighting/internal/commandoutput/NullOutputStream.java b/src/plugins/preflighting/src/com/motorolamobility/preflighting/internal/commandoutput/NullOutputStream.java
new file mode 100644
index 0000000..f63edc2
--- /dev/null
+++ b/src/plugins/preflighting/src/com/motorolamobility/preflighting/internal/commandoutput/NullOutputStream.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.preflighting.internal.commandoutput;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+public class NullOutputStream extends OutputStream
+{
+
+ @Override
+ public void write(int arg0) throws IOException
+ {
+ //do nothing since this is a null Stream
+ }
+}
diff --git a/src/plugins/preflighting/src/com/motorolamobility/preflighting/internal/commandoutput/OutputterExtensionReader.java b/src/plugins/preflighting/src/com/motorolamobility/preflighting/internal/commandoutput/OutputterExtensionReader.java
new file mode 100644
index 0000000..e540fb0
--- /dev/null
+++ b/src/plugins/preflighting/src/com/motorolamobility/preflighting/internal/commandoutput/OutputterExtensionReader.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.preflighting.internal.commandoutput;
+
+import static com.motorolamobility.preflighting.core.logging.PreflightingLogger.warn;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IConfigurationElement;
+import org.eclipse.core.runtime.IExtensionRegistry;
+import org.eclipse.core.runtime.Platform;
+
+import com.motorolamobility.preflighting.output.AbstractOutputter;
+
+public class OutputterExtensionReader
+{
+
+ private static final String OUTPUTTER_EXT_POINT_ID =
+ "com.motorolamobility.preflighting.outputter";
+
+ private static final String OUTPUTTER_ATTRIBUTE_ID = "id";
+
+ private static final String OUTPUTTER_ATTRIBUTE_CLASS = "class";
+
+ private static Map<String, AbstractOutputter> outputtersMap = null;
+
+ private static Map<String, AbstractOutputter> loadOutputters()
+ {
+
+ outputtersMap = new HashMap<String, AbstractOutputter>();
+ IExtensionRegistry registry = Platform.getExtensionRegistry();
+ IConfigurationElement[] elements =
+ registry.getConfigurationElementsFor(OUTPUTTER_EXT_POINT_ID);
+
+ for (IConfigurationElement element : elements)
+ {
+ if (element.getName().equals("outputter"))
+ {
+ try
+ {
+ String id = element.getAttribute(OUTPUTTER_ATTRIBUTE_ID).toUpperCase();
+ AbstractOutputter outputter =
+ (AbstractOutputter) element
+ .createExecutableExtension(OUTPUTTER_ATTRIBUTE_CLASS);
+
+ outputtersMap.put(id, outputter);
+
+ }
+ catch (CoreException e)
+ {
+ warn(OutputterExtensionReader.class,
+ "Error reading outputter extension for extension point " //$NON-NLS-1$
+ + OUTPUTTER_EXT_POINT_ID, e);
+ }
+
+ }
+
+ }
+
+ return outputtersMap;
+
+ }
+
+ public static Map<String, AbstractOutputter> getOutputtersMap()
+ {
+
+ return outputtersMap != null ? outputtersMap : loadOutputters();
+ }
+
+}
diff --git a/src/plugins/preflighting/src/com/motorolamobility/preflighting/internal/commandoutput/OutputterFactory.java b/src/plugins/preflighting/src/com/motorolamobility/preflighting/internal/commandoutput/OutputterFactory.java
new file mode 100644
index 0000000..c3bbe5b
--- /dev/null
+++ b/src/plugins/preflighting/src/com/motorolamobility/preflighting/internal/commandoutput/OutputterFactory.java
@@ -0,0 +1,320 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.preflighting.internal.commandoutput;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+
+import com.motorolamobility.preflighting.core.IParameterProcessor;
+import com.motorolamobility.preflighting.core.PreflightingCorePlugin;
+import com.motorolamobility.preflighting.core.validation.Parameter;
+import com.motorolamobility.preflighting.core.validation.ParameterDescription;
+import com.motorolamobility.preflighting.core.validation.ParameterType;
+import com.motorolamobility.preflighting.core.validation.ValidationManager.InputParameter;
+import com.motorolamobility.preflighting.core.validation.Value;
+import com.motorolamobility.preflighting.i18n.PreflightingNLS;
+import com.motorolamobility.preflighting.output.AbstractOutputter;
+
+/**
+ * Factory class for pre-flighting outputters
+ */
+public class OutputterFactory implements IParameterProcessor
+{
+
+ public static enum OutputType
+ {
+ CSV, TEXT, XML, DAEMON;
+
+ @Override
+ public String toString()
+ {
+ return super.toString().toLowerCase();
+ };
+
+ }
+
+ /**
+ * OUTPUT parameter keyword.
+ */
+ public final static String OUTPUT_PARAMETER = "output"; //$NON-NLS-1$
+
+ /**
+ * Maps parameters to possible values
+ */
+ private final Map<String, List<OutputType>> parameterValueMap =
+ new HashMap<String, List<OutputType>>();
+
+ /**
+ * Maps parameters to default values
+ */
+ private final Map<String, Value> parameterDefaultMap = new HashMap<String, Value>();
+
+ /**
+ * Maps parameters to their descriptions
+ */
+ private final Map<String, String> parameterDescriptionMap = new HashMap<String, String>();
+
+ /**
+ * Maps possible values to their descriptions
+ */
+ private final Map<String, String> valueDescriptionMap = new HashMap<String, String>();
+
+ /**
+ * Maps possible values to their types
+ */
+ private final Map<String, ParameterType> parameterTypeMap =
+ new HashMap<String, ParameterType>();
+
+ private static OutputterFactory instance;
+
+ private static OutputType defaultOutputter = OutputType.TEXT;
+
+ /**
+ * Default constructor.
+ */
+ private OutputterFactory()
+ {
+ // Initialize maps
+
+ // List of values
+ List<OutputType> valuesList = new ArrayList<OutputType>();
+ valuesList.add(OutputType.XML); // XML support not present in this version
+ valuesList.add(OutputType.CSV);
+ valuesList.add(OutputType.TEXT);
+
+ parameterValueMap.put(OUTPUT_PARAMETER, valuesList);
+
+ Value defaultOutputValue = new Value();
+ defaultOutputValue.setDescription(PreflightingNLS.OutputterFactory_TextOutputFormatMessage);
+ defaultOutputValue.setValue(OutputType.TEXT.toString());
+ parameterDefaultMap.put(OUTPUT_PARAMETER, defaultOutputValue);
+
+ parameterDescriptionMap.put(OUTPUT_PARAMETER,
+ PreflightingNLS.OutputterFactory_ValidationOutputModeMessage);
+
+ valueDescriptionMap.put(OutputType.TEXT.toString(),
+ PreflightingNLS.OutputterFactory_TextOutputFormatMessage);
+ valueDescriptionMap.put(OutputType.CSV.toString(),
+ PreflightingNLS.OutputterFactory_XmlOutputFormatNotImplementedMessage);
+ valueDescriptionMap.put(OutputType.XML.toString(),
+ PreflightingNLS.OutputterFactory_XmlOutputFormatNotImplementedMessage);
+
+ parameterTypeMap.put(OUTPUT_PARAMETER, ParameterType.STRING);
+ }
+
+ public static OutputterFactory getInstance()
+ {
+ if (instance == null)
+ {
+ instance = new OutputterFactory();
+ }
+ return instance;
+ }
+
+ /**
+ * Gets parameters accepted by the outputter created by the factory
+ * @return
+ */
+ public List<ParameterDescription> getParameterDescriptions()
+ {
+ //TODO Since there is no implemented alternative to TEXT, disable disable parameter
+ /*
+ List<ParameterDescription> parameterList = new ArrayList<ParameterDescription>();
+
+ for (String p : parameterValueMap.keySet())
+ {
+ ParameterDescription paramDesc = new ParameterDescription();
+
+ // Set allowed values
+ List<Value> valueList = new ArrayList<Value>();
+ for (String v : parameterValueMap.get(p))
+ {
+ Value value = new Value();
+
+ value.setDescription(valueDescriptionMap.get(v));
+ value.setValue(v);
+
+ valueList.add(value);
+ }
+ paramDesc.setType(parameterTypeMap.get(p));
+ paramDesc.setAllowedValues(valueList);
+
+ // Set default value
+ paramDesc.setDefaultValue(parameterDefaultMap.get(p));
+
+ // Set parameter description
+ paramDesc.setDescription(parameterDescriptionMap.get(p));
+
+ // Set parameter name
+ paramDesc.setName(p);
+
+ parameterList.add(paramDesc);
+
+ }
+ */
+
+ return null;
+
+ }
+
+ /**
+ * Create a new outputter passing the application parameter list as parameter
+ * @param parameters the application parameters or null to create the default one
+ * @return the desired outputter
+ */
+ public AbstractOutputter createOutputter(List<Parameter> parameters)
+ {
+ AbstractOutputter outputter = null;
+ if (parameters != null)
+ {
+ for (Parameter param : parameters)
+ {
+ String parameterType = param.getParameterType();
+
+ if (parameterType.equals(InputParameter.OUTPUT.getAlias()))
+ {
+ if (param.getValue() != null)
+ {
+ try
+ {
+ outputter = internalCreateOutputter(param.getValue());
+ }
+ catch (IllegalArgumentException e)
+ {
+ //do nothing
+ }
+ }
+ }
+ }
+ }
+ //default outputter
+ if (outputter == null)
+ {
+ outputter = internalCreateOutputter(defaultOutputter.toString());
+ }
+
+ return outputter;
+ }
+
+ private AbstractOutputter internalCreateOutputter(String type)
+ {
+ AbstractOutputter outputter = null;
+ String typeUpper = type.toUpperCase();
+ Map<String, AbstractOutputter> outputters = OutputterExtensionReader.getOutputtersMap();
+ outputter = outputters.get(typeUpper);
+
+ // the outputter can be defined through extension point
+ if (outputter == null)
+ {
+
+ OutputType typeEnum = OutputType.valueOf(typeUpper);
+
+ switch (typeEnum)
+ {
+ case CSV:
+ outputter = new CSVOutputter();
+ break;
+
+ case XML:
+ outputter = new XmlOutputter();
+ break;
+
+ case DAEMON:
+ outputter = new DaemonXMLOutputter();
+ break;
+
+ case TEXT:
+ outputter = new TextOutputter();
+ break;
+ default:
+
+ break;
+ }
+ }
+
+ return outputter;
+ }
+
+ public IStatus isOutputterAvailable(String value)
+ {
+ IStatus validationResult = null;
+
+ if (value != null)
+ {
+ value = value.toUpperCase();
+ }
+
+ // if this output is not defined through extension point, see if it is internal
+ if (!OutputterExtensionReader.getOutputtersMap().containsKey(value))
+ {
+ try
+ {
+ OutputType.valueOf(value);
+ }
+ catch (IllegalArgumentException e)
+ {
+ validationResult =
+ new Status(IStatus.ERROR, PreflightingCorePlugin.PLUGIN_ID,
+ PreflightingNLS.OutputterFactory_OutputParametersInvalidMessage);
+ }
+ }
+
+ // If no problems were found, return a OK status
+ if (validationResult == null)
+ {
+ validationResult =
+ new Status(IStatus.OK, PreflightingCorePlugin.PLUGIN_ID,
+ PreflightingNLS.OutputterFactory_OutputParametersValidMessage);
+ }
+
+ return validationResult;
+ }
+
+ public IStatus validateInputParams(List<Parameter> parameters)
+ {
+ return Status.OK_STATUS;
+ }
+
+ public OutputType getDefaultOutputter()
+ {
+ return defaultOutputter;
+ }
+
+ public void setDefaultOutputter(OutputType outputter)
+ {
+ defaultOutputter = outputter;
+ }
+}
diff --git a/src/plugins/preflighting/src/com/motorolamobility/preflighting/internal/commandoutput/TextOutputter.java b/src/plugins/preflighting/src/com/motorolamobility/preflighting/internal/commandoutput/TextOutputter.java
new file mode 100644
index 0000000..d10d339
--- /dev/null
+++ b/src/plugins/preflighting/src/com/motorolamobility/preflighting/internal/commandoutput/TextOutputter.java
@@ -0,0 +1,352 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.preflighting.internal.commandoutput;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.PrintStream;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.core.runtime.IStatus;
+
+import com.motorolamobility.preflighting.core.exception.PreflightingToolException;
+import com.motorolamobility.preflighting.core.logging.PreflightingLogger;
+import com.motorolamobility.preflighting.core.validation.ApplicationValidationResult;
+import com.motorolamobility.preflighting.core.validation.Parameter;
+import com.motorolamobility.preflighting.core.validation.ValidationManager;
+import com.motorolamobility.preflighting.core.validation.ValidationResult;
+import com.motorolamobility.preflighting.core.validation.ValidationResultData;
+import com.motorolamobility.preflighting.core.validation.ValidationResultData.SEVERITY;
+import com.motorolamobility.preflighting.core.verbose.WarningLevelFilter;
+import com.motorolamobility.preflighting.i18n.PreflightingNLS;
+import com.motorolamobility.preflighting.output.AbstractOutputter;
+
+public class TextOutputter extends AbstractOutputter
+{
+
+ private static final int MAX_PREVIEW_LENGTH = 120;
+
+ /**
+ * Character for separating line numbers on the output
+ */
+ private static final String MESSAGE_LINES_SEPARATOR = ", "; //$NON-NLS-1$
+
+ /**
+ * Line separator
+ */
+ private final String NEWLINE = System.getProperty("line.separator"); //$NON-NLS-1$
+
+ /**
+ * I/O Exception message logging.
+ */
+ private final String IO_ERROR = PreflightingNLS.TextOutputter_IOExceptionMessage;
+
+ /**
+ * Exception stating that it was not possible to print the results.
+ */
+ private final String PRINT_ERROR = PreflightingNLS.TextOutputter_PrintResultsErrorMessage;
+
+ /**
+ * Tab character
+ */
+ private final String TAB = "\t"; //$NON-NLS-1$
+
+ /**
+ * Character for separating line numbers from preview message
+ */
+ private static final String PREVIEW_MESSAGE_SEPARATOR = " - "; //$NON-NLS-1$
+
+ /* (non-Javadoc)
+ * @see com.motorolamobility.preflighting.core.commandoutput.IOutputter#print(java.util.Map, java.io.OutputStream, java.util.List)
+ */
+ @Override
+ public void print(ApplicationValidationResult results, OutputStream stream,
+ List<Parameter> parameters) throws PreflightingToolException
+ {
+ initializeParams(parameters);
+
+ // Use a BufferedWriter to write the results
+ OutputStreamWriter outputStreamWriter = new OutputStreamWriter(stream);
+ BufferedWriter writer = new BufferedWriter(outputStreamWriter);
+ try
+ {
+ if (results.getResults().size() > 0)
+ {
+ StringBuilder stringBuilder = new StringBuilder();
+
+ // Append results message
+ stringBuilder.append(NEWLINE + PreflightingNLS.TextOutputter_ApplicationMessage
+ + getApplicationFile().getAbsolutePath() + NEWLINE);
+ stringBuilder.append(NEWLINE + PreflightingNLS.TextOutputter_ResultsMessage
+ + NEWLINE);
+
+ writer.write(stringBuilder.toString());
+ writer.flush();
+
+ writer.newLine();
+
+ // Iterate trough the results and print them.
+ for (ValidationResult checker : results.getResults())
+ {
+ printCheckerResult(checker, writer);
+ }
+
+ writer.newLine();
+
+ /*
+ //Print the execution Report
+ printExecutionReport(results.getExecutionStatus(), stream);
+ */
+
+ String totalMessage =
+ WarningLevelFilter.getValidationResultTotalMessage(results.getResults());
+ if (totalMessage != null)
+ {
+ writer.newLine();
+ writer.write(totalMessage);
+ writer.flush();
+ writer.newLine();
+ }
+ writer.newLine();
+
+ }
+ }
+ catch (IOException e)
+ {
+
+ PreflightingLogger.error(getClass(), IO_ERROR + e.getMessage());
+ throw new PreflightingToolException(PRINT_ERROR, e);
+ }
+ finally
+ {
+
+ try
+ {
+ outputStreamWriter.close();
+ }
+ catch (IOException e)
+ {
+ //Do Nothing.
+ }
+ try
+ {
+ writer.close();
+ }
+ catch (IOException e)
+ {
+ //Do Nothing.
+ }
+ }
+
+ }
+
+ public static void printExecutionReport(Map<String, IStatus> executionStatus,
+ OutputStream stream) throws IOException
+ {
+ if (executionStatus != null)
+ {
+ OutputStreamWriter outputStreamWriter = null;
+ BufferedWriter bufferedWriter = null;
+ try
+ {
+ outputStreamWriter = new OutputStreamWriter(stream);
+ bufferedWriter = new BufferedWriter(outputStreamWriter);
+ bufferedWriter.newLine();
+ bufferedWriter.newLine();
+ bufferedWriter.write(PreflightingNLS.TextOutputter_ExecutionReportTitle);
+ bufferedWriter.newLine();
+
+ for (String checkerId : executionStatus.keySet())
+ {
+ String checkerName = ValidationManager.getCheckerExtension(checkerId).getName();
+ IStatus checkerStatus = executionStatus.get(checkerId);
+ String statusMessage =
+ checkerStatus.isOK()
+ ? PreflightingNLS.TextOutputter_ExecutionReportExecutedMsg
+ : checkerStatus.getMessage();
+ bufferedWriter.newLine();
+ bufferedWriter.write(checkerName
+ + PreflightingNLS.TextOutputter_ExecutionReportSeparator
+ + statusMessage);
+ }
+ bufferedWriter.newLine();
+ bufferedWriter.flush();
+ }
+ finally
+ {
+ if (bufferedWriter != null)
+ {
+ bufferedWriter.close();
+ }
+ try
+ {
+ outputStreamWriter.close();
+ }
+ catch (Exception e)
+ {
+ //Do Nothing.
+ }
+ }
+ }
+ }
+
+ /**
+ * Auxiliary method to print the results of a single checker
+ *
+ * @param checker - The checker that returned the results
+ * @param validationResult - The validation results.
+ * @param writer
+ * @throws IOException
+ */
+ private void printCheckerResult(ValidationResult validationResult, BufferedWriter writer)
+ throws IOException
+ {
+ List<ValidationResultData> data = validationResult.getValidationResult();
+
+ if (data.size() > 0)
+ {
+ boolean printSeverity = WarningLevelFilter.printSeverity();
+
+ StringBuilder stringBuilder = new StringBuilder();
+
+ for (ValidationResultData d : data)
+ {
+ /*
+ * Result format will be like this:
+ * {Severity} : {Issue Description}
+ * {File} ({Number of occurrences})
+ * Line[s] {Line Number[s] (separated by comma)}
+ */
+
+ if (printSeverity && !d.getSeverity().equals(SEVERITY.OK))
+ {
+ // Append severity
+ stringBuilder.append(d.getSeverity().toString());
+ }
+
+ // Append result description
+ if (d.getIssueDescription() != null)
+ {
+ if (printSeverity && !d.getSeverity().equals(SEVERITY.OK))
+ {
+ stringBuilder.append(": "); //$NON-NLS-1$
+ }
+ stringBuilder.append(d.getIssueDescription() + NEWLINE);
+ }
+
+ if (getApplicationFile() != null)
+ {
+ // Iterate through the list of files
+ for (File resource : d.getFileToIssueLines().keySet())
+ {
+ stringBuilder.append(TAB);
+ if (resource.isFile() || resource.getName().endsWith(".java"))
+ {
+ stringBuilder.append(PreflightingNLS.TextOutputter_File_Prefix);
+ }
+ else
+ {
+ stringBuilder.append(PreflightingNLS.TextOutputter_Folder_Prefix);
+ }
+
+ // Append file location (only relative path)
+ String fileLocation = computeResourcePath(resource);
+ // Append number of occurrences
+ List<Integer> lines = d.getFileToIssueLines().get(resource);
+ String numberOfOccurrences = ""; //$NON-NLS-1$
+ String lineMessage = null;
+ if (lines.size() == 1)
+ {
+ numberOfOccurrences =
+ PreflightingNLS.TextOutputter_OneOccurrenceMessage;
+ lineMessage = PreflightingNLS.TextOutputter_LineMessage;
+ }
+ else if (lines.size() > 1)
+ {
+ numberOfOccurrences =
+ PreflightingNLS
+ .bind(PreflightingNLS.TextOutputter_MoreThanOneOccurrenceMessage,
+ lines.size());
+ lineMessage = PreflightingNLS.TextOutputter_LinesMessage;
+ }
+
+ stringBuilder.append(fileLocation + numberOfOccurrences);
+
+ stringBuilder.append(NEWLINE);
+
+ // Check if it is an apk or a project, and print file lines only for project
+ if ((getApplicationFile() != null) && getApplicationFile().isDirectory())
+ {
+ if (lineMessage != null)
+ {
+ stringBuilder.append(TAB);
+ stringBuilder.append(TAB);
+
+ stringBuilder.append(lineMessage);
+ }
+
+ // For each line found, report a result
+ for (int i = 0; i < lines.size(); i++)
+ {
+ // Append line number
+ stringBuilder.append(lines.get(i).intValue());
+ if (i < (lines.size() - 1))
+ {
+ stringBuilder.append(MESSAGE_LINES_SEPARATOR);
+ }
+ }
+ if (d.getPreview() != null)
+ {
+ stringBuilder.append(PREVIEW_MESSAGE_SEPARATOR);
+ String preview = d.getPreview();
+ stringBuilder.append(preview.length() > MAX_PREVIEW_LENGTH
+ ? preview.substring(0, MAX_PREVIEW_LENGTH) : preview);
+ }
+ stringBuilder.append(NEWLINE);
+ }
+ }
+ }
+
+ // print quick fix only for correct warning level and if a quick fix is available
+ if (WarningLevelFilter.printQuickFixSuggestions()
+ && (d.getQuickFixSuggestion() != null))
+ {
+ stringBuilder.append(TAB);
+ stringBuilder.append(PreflightingNLS.TextOutputter_FixSuggestionMessage);
+ stringBuilder.append(d.getQuickFixSuggestion());
+ stringBuilder.append(NEWLINE);
+ }
+
+ writer.write(stringBuilder.toString());
+ writer.newLine();
+ writer.flush();
+
+ // Clean string builder
+ stringBuilder.delete(0, stringBuilder.length());
+ }
+ }
+ }
+
+ @Override
+ public void printError(Exception exceptionThrown, PrintStream out)
+ {
+ // Not needed.
+ }
+}
diff --git a/src/plugins/preflighting/src/com/motorolamobility/preflighting/internal/commandoutput/XmlOutputter.java b/src/plugins/preflighting/src/com/motorolamobility/preflighting/internal/commandoutput/XmlOutputter.java
new file mode 100644
index 0000000..6b717a4
--- /dev/null
+++ b/src/plugins/preflighting/src/com/motorolamobility/preflighting/internal/commandoutput/XmlOutputter.java
@@ -0,0 +1,367 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.preflighting.internal.commandoutput;
+
+import java.io.File;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.util.List;
+import java.util.Map;
+
+import javax.xml.parsers.ParserConfigurationException;
+
+import org.apache.xerces.jaxp.DocumentBuilderFactoryImpl;
+import org.eclipse.core.runtime.IStatus;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import com.motorolamobility.preflighting.core.exception.PreflightingToolException;
+import com.motorolamobility.preflighting.core.logging.PreflightingLogger;
+import com.motorolamobility.preflighting.core.utils.XmlUtils;
+import com.motorolamobility.preflighting.core.validation.ApplicationValidationResult;
+import com.motorolamobility.preflighting.core.validation.Parameter;
+import com.motorolamobility.preflighting.core.validation.ValidationManagerConfiguration;
+import com.motorolamobility.preflighting.core.validation.ValidationResult;
+import com.motorolamobility.preflighting.core.validation.ValidationResultData;
+import com.motorolamobility.preflighting.core.validation.ValidationResultData.SEVERITY;
+import com.motorolamobility.preflighting.core.verbose.WarningLevelFilter;
+import com.motorolamobility.preflighting.i18n.PreflightingNLS;
+import com.motorolamobility.preflighting.internal.PreflightingPlugin;
+import com.motorolamobility.preflighting.output.AbstractOutputter;
+
+public class XmlOutputter extends AbstractOutputter
+{
+
+ protected Document document = null;
+
+ private static final String XML_TAG_APP_VALIDATOR = "AppValidator";
+
+ private static final String XML_TAG_APPLICATION = "Application";
+
+ private static final String XML_TAG_ERROR = "Error";
+
+ private static final String XML_TAG_FATAL_ERROR = "FatalError";
+
+ private static final String XML_TAG_WARNING = "Warning";
+
+ private static final String XML_TAG_RESOURCE = "Resource";
+
+ private static final String XML_TAG_LINE = "Line";
+
+ private static final String XML_TAG_SUGGESTION = "Suggestion";
+
+ private static final String XML_ATTRIBUTE_CHECKER_ID = "checker_id";
+
+ private static final String XML_ATTRIBUTE_CONDITION_ID = "condition_id";
+
+ private static final String XML_ATTRIBUTE_INFO_URL = "info_url";
+
+ private static final String XML_ATTRIBUTE_PATH = "path";
+
+ private static final String XML_ATTRIBUTE_APP_NAME = "app_name";
+
+ private static final String XML_TAG_DESCRIPTION = "Description";
+
+ private static final String XML_TAG_PREVIEW = "Preview";
+
+ private static final String XML_ATTRIBUTE_APPVALIDATOR_VERSION = "version";
+
+ private static final String XML_ATTRIBUTE_APPLICATION_VERSION = "app_version";
+
+ private static final String XML_ATTRIBUTE_MOTODEV_LINK = "description_url";
+
+ private static final String XML_CHECKER_STATUS_VALUE_FAIL = "Failed";
+
+ private static final String XML_CHECKER_STATUS_VALUE_DISABLED = "Disabled";
+
+ private static final String XML_CHECKER_STATUS_VALUE_OK = "Executed";
+
+ private static final String XML_ATTRIBUTE_MESSAGE = "message";
+
+ private static final String XML_ATTRIBUTE_STATUS = "status";
+
+ private static final String XML_TAG_CHECKER_STATUS = "CheckerStatus";
+
+ private static final String XML_TAG_EXECUTION_REPORT = "ExecutionReport";
+
+ //TODO change tag name
+ private static final String XML_TAG_APP_VALIDATOR_EXECUTION_REPORT = "ExecutionReport";
+
+ private static final String XML_TAG_MESSAGES = "Messages";
+
+ /**
+ * Writes to file xml or text in the following format: <br>
+ * &lt;type (Error/Warning/Info)&gt; &lt;number files involved (Java, XML)&gt; &lt;filePath1 (without OS specific characters)&gt;:&lt;linePath1&gt; ... &lt;filePathN&gt;:&lt;linePathN&gt; &lt;description&gt;
+ * @throws PreflightingToolException
+ */
+ @Override
+ public void print(ApplicationValidationResult result, OutputStream stream,
+ List<Parameter> parameters) throws PreflightingToolException
+ {
+ initializeParams(parameters);
+
+ try
+ {
+ Element appValElem = createRootNode();
+
+ Element applicationElem = document.createElement(XML_TAG_APPLICATION);
+ applicationElem.setAttribute(XML_ATTRIBUTE_APP_NAME, getApplicationFile().getName());
+ applicationElem.setAttribute(XML_ATTRIBUTE_APPLICATION_VERSION,
+ String.valueOf(result.getVersion()));
+
+ generateCustomApplicationNodes(applicationElem, result, parameters);
+ appValElem.appendChild(applicationElem);
+
+ Element messagesElement = document.createElement(XML_TAG_MESSAGES);
+ applicationElem.appendChild(messagesElement);
+
+ //create result nodes and append them to document
+ generateResultNodes(messagesElement, result.getResults());
+
+ generateExecutionReport(applicationElem, result.getExecutionStatus());
+
+ //create XML output
+ XmlUtils.printXMLFormat(document);
+ }
+ catch (Exception e)
+ {
+ PreflightingLogger.error(getClass(), PreflightingNLS.TextOutputter_IOExceptionMessage
+ + e.getMessage());
+ throw new PreflightingToolException(
+ PreflightingNLS.XMLOutputter_PrintResultsErrorMessage, e);
+ }
+ }
+
+ /*
+ * Generates the nodes for the result list
+ */
+ private void generateResultNodes(Element rootElement, List<ValidationResult> result)
+ {
+ for (ValidationResult checker : result)
+ {
+ for (ValidationResultData data : checker.getValidationResult())
+ {
+ if (SEVERITY.OK.compareTo(data.getSeverity()) != 0)
+ {
+ Element issueElement = createIssueNode(data.getSeverity());
+ issueElement.setAttribute(XML_ATTRIBUTE_CHECKER_ID, checker.getCheckerId());
+ issueElement.setAttribute(XML_ATTRIBUTE_CONDITION_ID, data.getConditionID());
+ if (data.getInfoURL() != null)
+ {
+ issueElement.setAttribute(XML_ATTRIBUTE_INFO_URL, data.getInfoURL());
+ }
+ Element descriptionElement = createDescriptionNode(data.getIssueDescription());
+ issueElement.appendChild(descriptionElement);
+
+ for (File currentFile : data.getFileToIssueLines().keySet())
+ {
+ Element resourceElement =
+ createResourceNode(currentFile,
+ data.getFileToIssueLines().get(currentFile));
+ issueElement.appendChild(resourceElement);
+ }
+
+ if (WarningLevelFilter.printQuickFixSuggestions())
+ {
+ Element suggestionElement =
+ createSuggestionNode(data.getQuickFixSuggestion());
+ issueElement.appendChild(suggestionElement);
+ }
+
+ if (data.getPreview() != null)
+ {
+ Element previewElement = createPreviewNode(data.getPreview());
+ issueElement.appendChild(previewElement);
+ }
+
+ rootElement.appendChild(issueElement);
+ }
+ }
+ }
+ }
+
+ /*
+ * Generate the execution report node based on contents from executionStatus map
+ */
+ private void generateExecutionReport(Element appValElem, Map<String, IStatus> executionStatus)
+ {
+ Element executionReportElement = document.createElement(XML_TAG_EXECUTION_REPORT);
+
+ for (String checkerId : executionStatus.keySet())
+ {
+ IStatus checkerStatus = executionStatus.get(checkerId);
+ Element checkerStatusElement = document.createElement(XML_TAG_CHECKER_STATUS);
+
+ checkerStatusElement.setAttribute(XML_ATTRIBUTE_CHECKER_ID, checkerId);
+
+ //checker status equal INFO will be displayed as not executed
+ String status;
+ switch (checkerStatus.getSeverity())
+ {
+ case IStatus.OK:
+ status = XML_CHECKER_STATUS_VALUE_OK;
+ break;
+ case IStatus.INFO:
+ status = XML_CHECKER_STATUS_VALUE_DISABLED;
+ break;
+ default: //failed
+ status = XML_CHECKER_STATUS_VALUE_FAIL;
+ break;
+ }
+
+ checkerStatusElement.setAttribute(XML_ATTRIBUTE_STATUS, status);
+ checkerStatusElement.setAttribute(XML_ATTRIBUTE_MESSAGE, checkerStatus.getMessage());
+
+ executionReportElement.appendChild(checkerStatusElement);
+ }
+
+ appValElem.appendChild(executionReportElement);
+ }
+
+ /*
+ * ERROR, FATAL_ERROR or WARNING nodes
+ */
+ private Element createIssueNode(SEVERITY severity)
+ {
+ Element element = null;
+
+ if (SEVERITY.ERROR.compareTo(severity) == 0)
+ {
+ element = document.createElement(XML_TAG_ERROR);
+ }
+ else if (SEVERITY.WARNING.compareTo(severity) == 0)
+ {
+ element = document.createElement(XML_TAG_WARNING);
+ }
+ else if (SEVERITY.FATAL.compareTo(severity) == 0)
+ {
+ element = document.createElement(XML_TAG_FATAL_ERROR);
+ }
+
+ return element;
+ }
+
+ /*
+ * Issue description node
+ */
+ private Element createDescriptionNode(String description)
+ {
+ Element element = document.createElement(XML_TAG_DESCRIPTION);
+
+ if (description != null)
+ {
+ element.setTextContent(description);
+ }
+
+ return element;
+ }
+
+ /**
+ * Create resource node
+ * @param currentFile (resource)
+ * @param lines with errors in this resource
+ * @return the resource node
+ */
+ private Element createResourceNode(File currentFile, List<Integer> lines)
+ {
+ Element resElement = document.createElement(XML_TAG_RESOURCE);
+ resElement.setAttribute(XML_ATTRIBUTE_PATH, computeResourcePath(currentFile));
+
+ for (Integer currentLine : lines)
+ {
+ Element lineElement = document.createElement(XML_TAG_LINE);
+ lineElement.setTextContent(currentLine.toString());
+ resElement.appendChild(lineElement);
+ }
+
+ return resElement;
+ }
+
+ /**
+ * Create fix sugestion node
+ * @param suggestion text
+ * @return the sugestion node
+ */
+ private Element createSuggestionNode(String suggestion)
+ {
+ Element sugElement = document.createElement(XML_TAG_SUGGESTION);
+ sugElement.setTextContent(suggestion);
+
+ return sugElement;
+ }
+
+ /**
+ * Create a Preview node with Validation preview
+ * @param preview text
+ * @return preview node
+ */
+ private Element createPreviewNode(String preview)
+ {
+ Element previewElement = document.createElement(XML_TAG_PREVIEW);
+ previewElement.setTextContent(preview);
+
+ return previewElement;
+ }
+
+ @Override
+ public void printError(Exception exceptionThrown, PrintStream out)
+ {
+ try
+ {
+ Element appValElem = createRootNode();
+ Element executionReportElement =
+ document.createElement(XML_TAG_APP_VALIDATOR_EXECUTION_REPORT);
+ executionReportElement
+ .setAttribute(XML_ATTRIBUTE_STATUS, XML_CHECKER_STATUS_VALUE_FAIL);
+ executionReportElement
+ .setAttribute(XML_ATTRIBUTE_MESSAGE, exceptionThrown.getMessage());
+ appValElem.appendChild(executionReportElement);
+
+ XmlUtils.printXMLFormat(document);
+ }
+ catch (Exception e)
+ {
+ PreflightingLogger.error(getClass(),
+ PreflightingNLS.XMLOutputter_PrintResultsErrorMessage + e.getMessage());
+ }
+ }
+
+ //creates the document and its root node
+ private Element createRootNode() throws ParserConfigurationException
+ {
+ document = DocumentBuilderFactoryImpl.newInstance().newDocumentBuilder().newDocument();
+
+ Element appValElem = document.createElement(XML_TAG_APP_VALIDATOR);
+ document.appendChild(appValElem);
+ String appValidatorVersion = PreflightingPlugin.getInstance().getAppValidatorVersion();
+ ValidationManagerConfiguration valManagerConfiguration =
+ ValidationManagerConfiguration.getInstance();
+ String motodevLink =
+ valManagerConfiguration
+ .getProperty(ValidationManagerConfiguration.ConfigProperties.BASE_URL_PROPERTY
+ .getName());
+ appValElem.setAttribute(XML_ATTRIBUTE_APPVALIDATOR_VERSION, appValidatorVersion);
+ appValElem.setAttribute(XML_ATTRIBUTE_MOTODEV_LINK, motodevLink);
+
+ return appValElem;
+ }
+
+ protected void generateCustomApplicationNodes(Element applicationElem,
+ ApplicationValidationResult result, List<Parameter> params)
+ {
+ //Do nothing.
+ }
+}
diff --git a/src/plugins/preflighting/src/com/motorolamobility/preflighting/internal/daemon/Daemon.java b/src/plugins/preflighting/src/com/motorolamobility/preflighting/internal/daemon/Daemon.java
new file mode 100644
index 0000000..a704afa
--- /dev/null
+++ b/src/plugins/preflighting/src/com/motorolamobility/preflighting/internal/daemon/Daemon.java
@@ -0,0 +1,422 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.preflighting.internal.daemon;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.net.UnknownHostException;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import com.motorolamobility.preflighting.core.exception.PreflightingToolException;
+import com.motorolamobility.preflighting.core.validation.ValidationManager.InputParameter;
+import com.motorolamobility.preflighting.core.verbose.DebugVerboseOutputter;
+import com.motorolamobility.preflighting.core.verbose.DebugVerboseOutputter.VerboseLevel;
+import com.motorolamobility.preflighting.i18n.PreflightingNLS;
+import com.motorolamobility.preflighting.internal.PreflightingApplication;
+import com.motorolamobility.preflighting.internal.commandoutput.NullOutputStream;
+import com.motorolamobility.preflighting.internal.commandoutput.OutputterFactory;
+import com.motorolamobility.preflighting.internal.commandoutput.OutputterFactory.OutputType;
+import com.motorolamobility.preflighting.internal.help.printer.HelpPrinter;
+
+public class Daemon
+{
+ final public static String STATUS_CONTROL_MESSAGE = "are_you_alive";
+
+ final public static String LISTENING_STATUS_MESSAGE = "i_am_up_and_running";
+
+ final public static Integer DEFAULT_PORT = 50000;
+
+ final public static int BOUND_TIMEOUT = 2000;
+
+ final private static int TEST_TIMEOUT = 1000;
+
+ final private static String LOCALHOST = "127.0.0.1";
+
+ private final int serverPort;
+
+ private final String sdkPath;
+
+ private Thread daemonThread = null;
+
+ private PrintStream debugStream = null;
+
+ public static PrintStream nullStream = new PrintStream(new NullOutputStream());
+
+ public Daemon(int serverPort, String sdkPath)
+ {
+ // Default debug/error stream
+ DebugVerboseOutputter.setStream(nullStream);
+ OutputterFactory.getInstance().setDefaultOutputter(OutputType.XML);
+ this.serverPort = serverPort;
+ this.sdkPath = sdkPath;
+ }
+
+ class DaemonRunnable implements Runnable
+ {
+
+ Socket socket;
+
+ private final Daemon daemon;
+
+ private final String command;
+
+ public DaemonRunnable(Socket socket, String command, Daemon daemon)
+ {
+ this.socket = socket;
+ this.command = command;
+ this.daemon = daemon;
+ }
+
+ /*
+ * delete -sdk parameter from client
+ * throws an exception if -daemon is found
+ * add -sdk parameter that comes from daemon
+ */
+ private String processParameters(String parameters, PrintStream stream)
+ throws PreflightingToolException
+ {
+ String newParams = null;
+ String regex = "((?!(\\s+-)).)*"; //$NON-NLS-1$
+ Pattern pat = Pattern.compile(regex);
+ Matcher matcher = pat.matcher(parameters);
+
+ // for each parameter part found, process it
+ while (matcher.find())
+ {
+ String parameterValues = parameters.substring(matcher.start(), matcher.end());
+
+ // remove -sdk coming from client
+ if (parameterValues.trim().startsWith("-sdk")) //$NON-NLS-1$
+ {
+ newParams = parameters.substring(0, matcher.start());
+ newParams =
+ newParams + parameters.substring(matcher.end(), parameters.length());
+ }
+ if (parameterValues.trim().startsWith("-daemon")) //$NON-NLS-1$
+ {
+ throw new PreflightingToolException("Cannot start a daemon inside another."); //$NON-NLS-1$
+ }
+ }
+ if (newParams == null)
+ {
+ newParams = parameters;
+ }
+
+ //adding the sdk path passed to the daemon
+ if ((newParams != null) && (daemon.getSdkPath() != null))
+ {
+ newParams +=
+ " -" + (InputParameter.SDK_PATH.getAlias()) + " " + daemon.getSdkPath();
+ }
+
+ return newParams;
+ }
+
+ public void run()
+ {
+ PrintStream stream = null;
+ try
+ {
+
+ String parameters = command;
+ stream = new PrintStream(socket.getOutputStream(), true);
+
+ DebugVerboseOutputter.printVerboseMessage("New input: " + parameters, //$NON-NLS-1$
+ VerboseLevel.v0);
+
+ if (parameters.equals(STATUS_CONTROL_MESSAGE))
+ {
+ stream.println(LISTENING_STATUS_MESSAGE);
+ }
+ else if (parameters.equals("-list-checkers"))
+ {
+ HelpPrinter.printXMLCheckerList(stream);
+ }
+ else if (parameters.equals("-list-devices"))
+ {
+ HelpPrinter.printXMLDevicesList(stream);
+ }
+ else if (parameters.equals("-get-checkers-devices-specsMap-xml"))
+ {
+ HelpPrinter.printXMLDevicesCheckersSpecsMap(stream);
+ }
+ else
+ {
+ parameters = processParameters(parameters, stream);
+ PreflightingApplication.validate(parameters, stream, daemon.getDebugStream());
+ }
+ stream.flush();
+ }
+ catch (Exception e)
+ {
+ DebugVerboseOutputter.printVerboseMessage(PreflightingNLS.Daemon_ValidationError
+ + " " + socket.getRemoteSocketAddress() + ". " + e.getMessage(),
+ VerboseLevel.v0);
+ }
+ finally
+ {
+ if (stream != null)
+ {
+ stream.close();
+ }
+ if (socket != null)
+ {
+ try
+ {
+ socket.close();
+ }
+ catch (IOException e)
+ {
+ // do nothing since the socket was closed here
+ }
+ }
+ }
+ }
+ }
+
+ private void fork(Socket socket, String command)
+ {
+ DaemonRunnable runnable = new DaemonRunnable(socket, command, this);
+ Thread t = new Thread(runnable, "AppValidator - " + socket.getLocalPort()); //$NON-NLS-1$
+
+ t.start();
+ }
+
+ public PrintStream getDebugStream()
+ {
+ return debugStream;
+ }
+
+ private void run() throws IOException
+ {
+ ServerSocket socket = null;
+ try
+ {
+ socket = new ServerSocket(serverPort);
+
+ while (true)
+ {
+ try
+ {
+ Socket conn = socket.accept();
+
+ String command = readCommand(conn);
+
+ if (!command.equals("-quit"))
+
+ {
+ fork(conn, command);
+ }
+ else
+ {
+ break;
+ }
+
+ }
+ catch (OutOfMemoryError err)
+ {
+ // log
+ DebugVerboseOutputter.printVerboseMessage(
+ "The application ran out of memory: " + err.getMessage(), //$NON-NLS-1$
+ VerboseLevel.v0);
+ break;
+ }
+ // any other exception occured. continue
+ catch (Exception e)
+ {
+ DebugVerboseOutputter.printVerboseMessage(
+ "The validation instance failed to execute: " + e.getMessage(), //$NON-NLS-1$
+ VerboseLevel.v0);
+ }
+ }
+ }
+ finally
+ {
+ if (socket != null)
+ {
+ try
+ {
+ socket.close();
+ }
+ catch (IOException e)
+ {
+ // do nothing since the socket was closed here
+ }
+ }
+ }
+
+ System.out.println(PreflightingNLS.Daemon_Stopped);
+
+ }
+
+ private String readCommand(Socket socket) throws IOException
+ {
+
+ String command = "";
+
+ InputStream in = socket.getInputStream();
+ BufferedReader reader = null;
+ try
+ {
+ reader = new BufferedReader(new InputStreamReader(in));
+ command = reader.readLine();
+ }
+ finally
+ {
+ if (reader != null)
+ {
+ reader.close();
+ }
+ }
+ return command;
+
+ }
+
+ /**
+ * Starts this daemon in a new thread
+ */
+ public void startDaemon()
+ {
+ try
+ {
+ System.out.println(PreflightingNLS.Daemon_StartingStatusMessage);
+
+ //that's the daemon thread
+ daemonThread = new Thread()
+ {
+ @Override
+ public void run()
+ {
+ try
+ {
+ Daemon.this.run();
+ }
+ catch (Exception e)
+ {
+ DebugVerboseOutputter.printVerboseMessage(
+ "Daemon aborted: " + e.getMessage(), //$NON-NLS-1$
+ VerboseLevel.v0);
+ }
+ }
+ };
+ daemonThread.start();
+ }
+
+ catch (Exception e)
+ {
+ DebugVerboseOutputter.printVerboseMessage(PreflightingNLS.Daemon_StartingErrorMessage
+ + " " + e.getMessage(), VerboseLevel.v0);
+ }
+ }
+
+ /**
+ * Test daemon with a status control message.
+ * @param daemonThread
+ * @param serverPort
+ * @throws IOException
+ * @throws UnknownHostException
+ * @throws InterruptedException
+ * @throws Exception
+ */
+ public boolean testDaemon() throws UnknownHostException, IOException, InterruptedException
+ {
+ boolean returnValue = false;
+
+ if (daemonThread.isAlive())
+ {
+ DebugVerboseOutputter.printVerboseMessage(PreflightingNLS.bind(
+ PreflightingNLS.Daemon_TestDaemonStatusMessage, serverPort), VerboseLevel.v0);
+ int i = 1;
+ while (true)
+ {
+ Socket clientSocket = null;
+ BufferedReader reader = null;
+ try
+ {
+ clientSocket = new Socket(LOCALHOST, serverPort);
+ OutputStream out = clientSocket.getOutputStream();
+ String aux = Daemon.STATUS_CONTROL_MESSAGE + "\n";
+ out.write(aux.getBytes());
+ out.flush();
+
+ InputStream in = clientSocket.getInputStream();
+ reader = new BufferedReader(new InputStreamReader(in));
+
+ String line = reader.readLine();
+ if (line.equals(Daemon.LISTENING_STATUS_MESSAGE))
+ {
+ DebugVerboseOutputter.printVerboseMessage(PreflightingNLS.bind(
+ PreflightingNLS.Daemon_TestDaemonSucceedTry, i), VerboseLevel.v0);
+ DebugVerboseOutputter.printVerboseMessage(PreflightingNLS.bind(
+ PreflightingNLS.Daemon_LinsteningMessage, serverPort),
+ VerboseLevel.v0);
+ returnValue = true;
+ break;
+ }
+
+ DebugVerboseOutputter.printVerboseMessage(
+ PreflightingNLS.bind(PreflightingNLS.Daemon_TestDaemonFailedTry, i++),
+ VerboseLevel.v0);
+ //wait before next try
+ Thread.sleep(TEST_TIMEOUT);
+ }
+ finally
+ {
+ if (reader != null)
+ {
+ reader.close();
+ }
+ if (clientSocket != null)
+ {
+ clientSocket.close();
+ }
+ }
+ }
+
+ }
+ return returnValue;
+ }
+
+ public void join() throws InterruptedException
+ {
+ daemonThread.join();
+ }
+
+ public String getSdkPath()
+ {
+ return sdkPath;
+ }
+
+ /**
+ * Turn on/off debug mode. If debug is true, debug messages will be printed to the system error stream.
+ * If false, no message is printed.
+ *
+ * @param debug true for debug mode on
+ */
+ public void setDebugOn(boolean debug)
+ {
+ debugStream = debug ? System.err : nullStream;
+ DebugVerboseOutputter.setStream(debugStream);
+ }
+
+}
diff --git a/src/plugins/preflighting/src/com/motorolamobility/preflighting/internal/daemon/JUnitClient.java b/src/plugins/preflighting/src/com/motorolamobility/preflighting/internal/daemon/JUnitClient.java
new file mode 100644
index 0000000..f4593c0
--- /dev/null
+++ b/src/plugins/preflighting/src/com/motorolamobility/preflighting/internal/daemon/JUnitClient.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.preflighting.internal.daemon;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.net.Socket;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import org.junit.Test;
+
+public class JUnitClient
+{
+
+ static int serverPort = 2020;
+
+ public static void runDaemon(int port)
+ {
+
+ final int serverPort = port;
+ Thread t = new Thread("App Validator Daemon")
+ {
+
+ @Override
+ public void run()
+ {
+ super.run();
+ /*
+ try
+ {
+ Daemon.run(serverPort);
+ }
+ catch (IOException e)
+ {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }*/
+ }
+ };
+
+ t.setDaemon(true);
+ t.start();
+ }
+
+ public void testClientInternal(String name) throws UnknownHostException, IOException
+ {
+ Socket s = new Socket("10.10.26.208", serverPort);
+
+ OutputStream out = s.getOutputStream();
+ String aux =
+ "c:\\temp\\apks\\" + name
+ + " -sdk C:\\home\\studio\\android-sdk-windows -w4 -v2 -output text\n";
+ out.write(aux.getBytes());
+ out.flush();
+
+ InputStream in = s.getInputStream();
+ BufferedReader reader = null;
+ StringBuilder sb;
+ try
+ {
+ reader = new BufferedReader(new InputStreamReader(in));
+
+ sb = new StringBuilder();
+ String line = reader.readLine();
+
+ while (line != null)
+ {
+ sb.append(line + "\n");
+ line = reader.readLine();
+ }
+ }
+ finally
+ {
+ if (reader != null)
+ {
+ reader.close();
+ }
+ try
+ {
+ s.close();
+ }
+ catch (Exception e)
+ {
+ //Do Nothing.
+ }
+ }
+
+ System.out.println(sb.toString());
+ }
+
+ @Test
+ public void testClient() throws IOException, InterruptedException
+ {
+ List<Thread> threads = Collections.synchronizedList(new ArrayList<Thread>());
+
+ for (int j = 0; j < 1; j++)
+ {
+ for (int i = 1; i <= 1; i++)
+ {
+ final int ii = i;
+ Thread t = new Thread()
+ {
+ @Override
+ public void run()
+ {
+ try
+ {
+ testClientInternal("a" + ii + ".apk");
+ }
+ catch (Exception e)
+ {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+ };
+ t.start();
+ threads.add(t);
+ }
+ }
+
+ for (Thread thread : threads)
+ {
+ thread.join();
+ }
+ }
+
+ @Test
+ public void testDaemon() throws IOException
+ {
+ //Daemon.run(serverPort);
+ }
+}
diff --git a/src/plugins/preflighting/src/com/motorolamobility/preflighting/internal/help/printer/HelpPrinter.java b/src/plugins/preflighting/src/com/motorolamobility/preflighting/internal/help/printer/HelpPrinter.java
new file mode 100644
index 0000000..a85d8e0
--- /dev/null
+++ b/src/plugins/preflighting/src/com/motorolamobility/preflighting/internal/help/printer/HelpPrinter.java
@@ -0,0 +1,808 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.motorolamobility.preflighting.internal.help.printer;
+
+import java.io.BufferedWriter;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.PrintStream;
+import java.io.UnsupportedEncodingException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.transform.TransformerException;
+
+import org.apache.xerces.jaxp.DocumentBuilderFactoryImpl;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import com.motorolamobility.preflighting.core.checker.CheckerDescription;
+import com.motorolamobility.preflighting.core.checker.CheckerExtension;
+import com.motorolamobility.preflighting.core.checker.IChecker;
+import com.motorolamobility.preflighting.core.checker.condition.Condition;
+import com.motorolamobility.preflighting.core.checker.condition.ICondition;
+import com.motorolamobility.preflighting.core.devicespecification.DeviceSpecification;
+import com.motorolamobility.preflighting.core.devicespecification.DevicesSpecsContainer;
+import com.motorolamobility.preflighting.core.devicespecification.DevicesSpecsContainer.SpecKey;
+import com.motorolamobility.preflighting.core.exception.PreflightingExtensionPointException;
+import com.motorolamobility.preflighting.core.logging.PreflightingLogger;
+import com.motorolamobility.preflighting.core.utils.XmlUtils;
+import com.motorolamobility.preflighting.core.validation.ParameterDescription;
+import com.motorolamobility.preflighting.core.validation.ValidationManager;
+import com.motorolamobility.preflighting.core.validation.ValidationManagerConfiguration;
+import com.motorolamobility.preflighting.core.validation.Value;
+import com.motorolamobility.preflighting.i18n.PreflightingNLS;
+import com.motorolamobility.preflighting.internal.PreflightingPlugin;
+
+/**
+ * Class responsible for printing help information to the console / command line.
+ *
+ */
+public class HelpPrinter
+{
+
+ private static final String SEPARATOR = ",";
+
+ private static final String XML_TAG_CHECKERS = "Checkers";
+
+ private static final String XML_TAG_SPECS = "Specs";
+
+ private static final String XML_TAG_SPEC = "Spec";
+
+ private static final String XML_TAG_SPEC_DEVICES = "SpecDevices";
+
+ private static final String XML_TAG_DEVICES = "Devices";
+
+ private static final String ATTRIBUTE_LEVEL = "level";
+
+ private static final String XML_TAG_CONDITION = "Condition";
+
+ private static final String XML_TAG_VALUE_DESCRIPTION = "ValueDescription";
+
+ private static final String ATTRIBUTE_NAME = "name";
+
+ private static final String XML_TAG_PARAMETER = "Parameter";
+
+ private static final String XML_TAG_DESCRIPTION = "Description";
+
+ private static final String ATTRIBUTE_ID = "id";
+
+ public static final String XML_TAG_CHECKER = "Checker";
+
+ public static final String XML_TAG_DEVICE = "Device";
+
+ public static Document devicesCheckersSpecsMapDocument = null;
+
+ /**
+ * Line separator
+ */
+ private final static String NEWLINE = System.getProperty("line.separator"); //$NON-NLS-1$
+
+ /**
+ * Tab character
+ */
+ private final static String TAB = "\t"; //$NON-NLS-1$
+
+ /**
+ * I/O Exception message logging.
+ */
+ private final static String IO_ERROR = PreflightingNLS.HelpPrinter_IOExceptionMessage;
+
+ private static final String XML_TAG_APP_VALIDATOR = "AppValidator";
+
+ private static final String XML_ATTRIBUTE_APPVALIDATOR_VERSION = "version";
+
+ private static final String XML_ATTRIBUTE_MOTODEV_LINK = "description_url";
+
+ private static final HashMap<String, Document> cache = new HashMap<String, Document>();
+
+ /**
+ * Print help data.
+ * @param parameters Help data
+ * @param printSyntax Whether syntax should be printed or not
+ * @param printStream
+ * @param checker checker to get the description and parameters returned
+ */
+ public static void printHelp(PrintStream printStream, List<ParameterDescription> parameters,
+ boolean printSyntax)
+ {
+ if (parameters.size() > 0)
+ {
+ StringBuilder stringBuilder = new StringBuilder();
+
+ if (printSyntax)
+ {
+ stringBuilder.append(PreflightingNLS.HelpPrinter_Usage + " ");
+ stringBuilder.append("\t" + PreflightingNLS.HelpPrinter_ProgramName);
+ stringBuilder
+ .append("[OPTION[PRM]]...\n\t" + PreflightingNLS.HelpPrinter_ProgramName + "[OPTION[PRM]]... -input [FILE] [OPTION[PRM]]...\n");//$NON-NLS-1$
+ stringBuilder.append(PreflightingNLS.HelpPrinter_ProgramDescritpion + NEWLINE
+ + NEWLINE);
+ }
+
+ stringBuilder.append(PreflightingNLS.HelpPrinter_OptionsMessage + NEWLINE);
+ // Iterate through the parameters and print the info
+ for (ParameterDescription p : parameters)
+ {
+ /*
+ * The output format will be something like this:
+ *
+ * Parameter info:
+ * -{Parameter name} [TAB] Default value: {defaultValue} - {Description}
+ * [TAB] {value1} Type: {type} - Description
+ */
+
+ // Append parameter name
+ if (p.getName() != null)
+ {
+ stringBuilder.append("-" + p.getName()); //$NON-NLS-1$
+ int totalLenght = p.getName().length();
+ if (p.getValueDescription() != null)
+ {
+ stringBuilder.append(" " + p.getValueDescription()); //$NON-NLS-1$
+ totalLenght += p.getValueDescription().length() + 1;
+ }
+ if (totalLenght < 25)
+ {
+ int i = 25 - totalLenght;
+ while (i > 0)
+ {
+ stringBuilder.append(" ");
+ i--;
+ }
+ }
+ }
+
+ // Append default value
+ if (p.getDefaultValue() != null)
+ {
+ stringBuilder.append(PreflightingNLS.HelpPrinter_DefaultMessage
+ + p.getDefaultValue().getValue());
+ stringBuilder.append(" "); //$NON-NLS-1$
+ }
+
+ // Append description
+ if (p.getDescription() != null)
+ {
+ stringBuilder.append("- "); //$NON-NLS-1$
+ stringBuilder.append(p.getDescription());
+ stringBuilder.append(NEWLINE);
+ }
+
+ // Append values information
+ for (Value v : p.getAllowedValues())
+ {
+ stringBuilder.append(TAB);
+
+ // Append value
+ if (v.getValue() != null)
+ {
+ stringBuilder.append(v.getValue());
+ stringBuilder.append(" - "); //$NON-NLS-1$
+ }
+
+ // Append description
+ if (v.getDescription() != null)
+ {
+ stringBuilder.append(v.getDescription());
+ stringBuilder.append(NEWLINE);
+ }
+ }
+
+ }
+
+ printStream.print(stringBuilder.toString());
+ printStream.flush();
+ }
+
+ }
+
+ /**
+ * Prints a XML file which contains a list of devices, their id for App Validator and provider
+ * @param stream The stream used to print the XML file
+ * @throws ParserConfigurationException
+ * @throws TransformerException
+ * @throws UnsupportedEncodingException
+ */
+ public static void printXMLDevicesList(PrintStream stream) throws ParserConfigurationException,
+ UnsupportedEncodingException, TransformerException
+ {
+
+ ValidationManager validationManager = new ValidationManager();
+
+ DevicesSpecsContainer devicesSpecsContainer = validationManager.getDevicesSpecsContainer();
+
+ Document document =
+ DocumentBuilderFactoryImpl.newInstance().newDocumentBuilder().newDocument();
+ Element appvalidatorElement = createAppvalidatorNode(document);
+
+ for (DeviceSpecification deviceInfo : devicesSpecsContainer.getDeviceSpecifications())
+ {
+ appvalidatorElement.appendChild(createDeviceNode(deviceInfo, document));
+ }
+
+ XmlUtils.printXMLFormat(document);
+
+ validationManager = null;
+ }
+
+ private static Element createDeviceNode(DeviceSpecification deviceInfo, Document document)
+ {
+ Element deviceNode = document.createElement(XML_TAG_DEVICE);
+ deviceNode.setTextContent(deviceInfo.getName());
+
+ deviceNode.setAttribute("id", deviceInfo.getId());
+ deviceNode.setAttribute("provider", deviceInfo.getProvider());
+
+ return deviceNode;
+ }
+
+ /**
+ * Prints a XML file which contains a list of checkers and all the information regarding each checker
+ * @param stream The stream used to print the XML file
+ */
+ public static void printXMLCheckerList(PrintStream stream)
+ throws PreflightingExtensionPointException, ParserConfigurationException,
+ UnsupportedEncodingException, TransformerException
+ {
+
+ Map<String, CheckerExtension> checkers = ValidationManager.loadCheckers();
+ Collection<CheckerExtension> checkerExtensions = checkers.values();
+
+ Document document =
+ DocumentBuilderFactoryImpl.newInstance().newDocumentBuilder().newDocument();
+ Element appvalidatorElement = null;
+
+ appvalidatorElement = createAppvalidatorNode(document);
+
+ for (CheckerExtension extension : checkerExtensions)
+ {
+ Element node = getXMLCheckerNode(extension, document);
+ appvalidatorElement.appendChild(node);
+ }
+
+ XmlUtils.printXMLFormat(document);
+
+ }
+
+ private static Element createAppvalidatorNode(Document document)
+ {
+
+ Element appValElem = document.createElement(XML_TAG_APP_VALIDATOR);
+ document.appendChild(appValElem);
+ String appValidatorVersion = PreflightingPlugin.getInstance().getAppValidatorVersion();
+ ValidationManagerConfiguration valManagerConfiguration =
+ ValidationManagerConfiguration.getInstance();
+ String motodevLink =
+ valManagerConfiguration
+ .getProperty(ValidationManagerConfiguration.ConfigProperties.BASE_URL_PROPERTY
+ .getName());
+
+ appValElem.setAttribute(XML_ATTRIBUTE_APPVALIDATOR_VERSION, appValidatorVersion);
+ appValElem.setAttribute(XML_ATTRIBUTE_MOTODEV_LINK, motodevLink);
+
+ return appValElem;
+ }
+
+ /**
+ * Generate a XML node for a specific checker
+ */
+ private static Element getXMLCheckerNode(CheckerExtension extension, Document document)
+ {
+
+ Element checkerNode = document.createElement(XML_TAG_CHECKER);
+ checkerNode.setAttribute(ATTRIBUTE_ID, extension.getId());
+
+ Element descriptionNode = document.createElement(XML_TAG_DESCRIPTION);
+ descriptionNode.setTextContent(extension.getDescription());
+
+ checkerNode.appendChild(descriptionNode);
+
+ IChecker checker = extension.getChecker();
+ List<Condition> conditionList = getSortedConditions(checker);
+
+ appendConditions(checkerNode, document, conditionList);
+ appendParameters(checkerNode, document, checker);
+
+ return checkerNode;
+ }
+
+ private static void appendParameters(Element checkerNode, Document document, IChecker checker)
+ {
+ List<ParameterDescription> paremeterDescriptionList = checker.getParameterDescriptions();
+ if ((paremeterDescriptionList != null) && (paremeterDescriptionList.size() > 0))
+ {
+ for (ParameterDescription param : paremeterDescriptionList)
+ {
+ Element parameterNode = document.createElement(XML_TAG_PARAMETER);
+ parameterNode.setAttribute(ATTRIBUTE_NAME, param.getName());
+
+ Element valueDescriptionNode = document.createElement(XML_TAG_VALUE_DESCRIPTION);
+ valueDescriptionNode.setTextContent(param.getValueDescription());
+
+ Element paramDescrNode = document.createElement(XML_TAG_DESCRIPTION);
+ paramDescrNode.setTextContent(param.getDescription());
+
+ parameterNode.appendChild(valueDescriptionNode);
+ parameterNode.appendChild(paramDescrNode);
+ checkerNode.appendChild(parameterNode);
+ }
+ }
+ }
+
+ private static void appendConditions(Element checkerNode, Document document,
+ List<Condition> conditionList)
+ {
+ for (ICondition condition : conditionList)
+ {
+ Element conditionNode = document.createElement(XML_TAG_CONDITION);
+ conditionNode.setAttribute(ATTRIBUTE_ID, condition.getId());
+ conditionNode.setAttribute(ATTRIBUTE_LEVEL, condition.getSeverityLevel().toString());
+
+ Element conditionDescrNode = document.createElement(XML_TAG_DESCRIPTION);
+ conditionDescrNode.setTextContent(condition.getDescription());
+
+ conditionNode.appendChild(conditionDescrNode);
+ checkerNode.appendChild(conditionNode);
+ }
+ }
+
+ /**
+ * Prints the description from the extension point declaration and
+ * print the parameters returned by the checker itself.
+ * @param checkerExtension
+ */
+ public static void printHelpChecker(PrintStream out, CheckerExtension checkerExtension)
+ {
+ StringBuilder stringBuilder = new StringBuilder();
+
+ stringBuilder.append(PreflightingNLS.HelperPrinter_CheckerId + checkerExtension.getId()
+ + NEWLINE);
+ stringBuilder.append(PreflightingNLS.HelperPrinter_CheckerDescription
+ + checkerExtension.getDescription() + NEWLINE);
+
+ IChecker checker = checkerExtension.getChecker();
+
+ // Append condition information
+ List<Condition> conditionList = getSortedConditions(checker);
+
+ // Size < 0 should never happen, but you never can be too sure...
+ if ((conditionList != null) && (conditionList.size() > 0))
+ {
+ // Iterate through the condition and print the descriptions
+ stringBuilder.append(NEWLINE);
+ stringBuilder.append(PreflightingNLS.HelperPrinter_CheckerHasConditions + NEWLINE);
+
+ stringBuilder.append(NEWLINE);
+ stringBuilder.append(TAB);
+ stringBuilder.append("Default level");
+ stringBuilder.append(TAB);
+
+ stringBuilder.append("Condition ID");
+ stringBuilder.append(TAB);
+ stringBuilder.append(TAB);
+ stringBuilder.append(TAB);
+ stringBuilder.append("Description");
+ stringBuilder.append(NEWLINE);
+
+ for (ICondition c : conditionList)
+ {
+ stringBuilder.append(TAB);
+ stringBuilder.append(c.getSeverityLevel().toString());
+ stringBuilder.append(TAB);
+ stringBuilder.append(TAB);
+ stringBuilder.append(c.getId());
+ stringBuilder.append(TAB);
+ if (c.getId().length() < 24)
+ {
+ stringBuilder.append(TAB);
+ }
+ if (c.getId().length() < 15)
+ {
+ stringBuilder.append(TAB);
+ }
+ stringBuilder.append(c.getDescription());
+ stringBuilder.append(NEWLINE);
+ }
+
+ stringBuilder.append(NEWLINE);
+ }
+
+ List<ParameterDescription> paremeterDescriptionList = checker.getParameterDescriptions();
+ if (paremeterDescriptionList != null)
+ {
+
+ if (paremeterDescriptionList.size() > 0)
+ {
+ stringBuilder.append(PreflightingNLS.HelperPrinter_CheckerUsesParameters + NEWLINE);
+ }
+ else
+ {
+ stringBuilder.append(PreflightingNLS.HelperPrinter_CheckerDoesNotUseParameters
+ + NEWLINE);
+ }
+ for (ParameterDescription returnParam : paremeterDescriptionList)
+ {
+ String paramMessage =
+ TAB + returnParam.getName() + " = " + returnParam.getValueDescription();
+ if ((returnParam.getType() != null) && (returnParam.getType().toString() != null))
+ {
+ paramMessage += " [" + returnParam.getType().toString() + "]";
+ }
+ paramMessage += TAB + "- " + returnParam.getDescription() + NEWLINE;
+ stringBuilder.append(paramMessage);
+ }
+ }
+
+ out.print(stringBuilder.toString());
+ }
+
+ /**
+ * Return a sorted list of checker conditions according to it default level
+ *
+ * @param checker the checker what have the conditions
+ * @return the sorted list of conditions
+ */
+ private static List<Condition> getSortedConditions(IChecker checker)
+ {
+ List<Condition> conditionList = new ArrayList(checker.getConditions().values());
+
+ // Sort the conditions list
+ Collections.sort(conditionList, new Comparator<Condition>()
+ {
+ public int compare(Condition o1, Condition o2)
+ {
+ if (o1.getSeverityLevel().ordinal() > o2.getSeverityLevel().ordinal())
+ {
+ return +1;
+ }
+ else
+ {
+ return -1;
+ }
+ }
+ });
+ return conditionList;
+ }
+
+ public static void printDevicesList(List<Value> list, PrintStream printStream)
+ {
+ if (list.size() > 0)
+ {
+ StringBuilder stringBuilder = new StringBuilder();
+
+ stringBuilder.append(PreflightingNLS.HelpPrinter_Device_Id + " name - [id]");
+ stringBuilder.append(NEWLINE);
+ for (Value current : list)
+ {
+ //Append device ID
+ stringBuilder.append(TAB);
+ stringBuilder.append(current.getValue());
+ stringBuilder.append(NEWLINE);
+ }
+ printStream.print(stringBuilder.toString());
+ // Clean string builder
+ stringBuilder.delete(0, stringBuilder.length());
+ }
+ }
+
+ public static void printDevicesDescription(String deviceDescription, String deviceId,
+ PrintStream printStream)
+ {
+ if ((deviceDescription != null) && !deviceDescription.trim().equals(""))
+ {
+ printStream.print(deviceDescription);
+ printStream.println();
+ }
+ else
+ {
+ //device id not found
+ printStream.print(PreflightingNLS
+ .bind(PreflightingNLS.GlobalInputParamsValidator_NonExistentDeviceIdMessage,
+ deviceId));
+ }
+ }
+
+ public static void printCheckersList(List<CheckerDescription> list, PrintStream printStream)
+ {
+ if (list.size() > 0)
+ {
+ StringBuilder stringBuilder = new StringBuilder();
+
+ /*
+ * Header
+ */
+
+ // Iterate through the values and print the info
+ for (CheckerDescription checker : list)
+ {
+ /*
+ * The output format will be something like this:
+ *
+ * {value} {TAB} {Description}
+ */
+
+ //Append ID
+ stringBuilder.append(PreflightingNLS.HelpPrinter_Checker_Id);
+ stringBuilder.append(TAB);
+ stringBuilder.append(TAB);
+ stringBuilder.append(checker.getId());
+ stringBuilder.append(NEWLINE);
+
+ //Append Name
+ stringBuilder.append(PreflightingNLS.HelpPrinter_Checker_Name);
+ stringBuilder.append(TAB);
+ stringBuilder.append(TAB);
+ if ((checker.getName() != null))
+ {
+ stringBuilder.append(checker.getName());
+ }
+ else
+ {
+ stringBuilder.append(PreflightingNLS.HelpPrinter_Checker_NotAvailable);
+ }
+ stringBuilder.append(NEWLINE);
+
+ //Append Description
+ stringBuilder.append(PreflightingNLS.HelpPrinter_Checker_Description);
+ stringBuilder.append(TAB);
+ if ((checker.getName() != null))
+ {
+ stringBuilder.append(checker.getDescription());
+ }
+ else
+ {
+ stringBuilder.append(PreflightingNLS.HelpPrinter_Checker_NotAvailable);
+ }
+ stringBuilder.append(NEWLINE);
+ stringBuilder.append(NEWLINE);
+
+ printStream.print(stringBuilder.toString());
+
+ // Clean string builder
+ stringBuilder.delete(0, stringBuilder.length());
+ }
+ }
+ }
+
+ /**
+ * Print a list of values.
+ * @param values The list of values
+ * @param stream The output stream
+ */
+ public static void printList(List<Value> values, OutputStream stream)
+ {
+ if (values.size() > 0)
+ {
+ /*
+ * Writer used for output
+ */
+ BufferedWriter writer = null;
+ StringBuilder stringBuilder = new StringBuilder();
+
+ writer = new BufferedWriter(new OutputStreamWriter(stream));
+
+ try
+ {
+ // Iterate through the values and print the info
+ for (Value v : values)
+ {
+ /*
+ * The output format will be something like this:
+ *
+ * {value} {TAB} {Description}
+ */
+
+ stringBuilder.append(v.getValue());
+ stringBuilder.append(TAB);
+ if (v.getDescription() != null)
+ {
+ stringBuilder.append(v.getDescription());
+ }
+
+ writer.write(stringBuilder.toString());
+ writer.flush();
+
+ // Clean string builder
+ stringBuilder.delete(0, stringBuilder.length());
+
+ }
+ }
+ catch (IOException e)
+ {
+
+ PreflightingLogger.error(HelpPrinter.class, IO_ERROR + e.getMessage());
+
+ }
+ finally
+ {
+ try
+ {
+ writer.close();
+ }
+ catch (IOException e)
+ {
+ PreflightingLogger.error(HelpPrinter.class, IO_ERROR + e.getMessage());
+ }
+ }
+ }
+
+ }
+
+ private static Element createSpecDevicesMapNode(ValidationManager validationManager,
+ Document document, String categories)
+ {
+ Element specDevicesNode = document.createElement(XML_TAG_SPEC_DEVICES);
+ Map<SpecKey, List<DeviceSpecification>> specDevsMap =
+ validationManager.getDevicesSpecsContainer().getSpecDevFilterMap();
+
+ //Set<SpecKey> specs = specDevsMap.keySet();
+ String[] specs = categories.split(SEPARATOR);
+
+ for (String specStr : specs)
+ {
+ SpecKey spec = SpecKey.valueOf(specStr);
+ Element specNode = document.createElement(XML_TAG_SPEC);
+ specNode.setAttribute(ATTRIBUTE_ID, spec.getId());
+
+ List<DeviceSpecification> devs = specDevsMap.get(spec);
+
+ if (devs != null)
+ {
+ for (DeviceSpecification dev : devs)
+ {
+ Element devNode = document.createElement(XML_TAG_DEVICE);
+ devNode.setAttribute(ATTRIBUTE_ID, dev.getId());
+ specNode.appendChild(devNode);
+ }
+ }
+
+ specDevicesNode.appendChild(specNode);
+ }
+
+ return specDevicesNode;
+ }
+
+ private static Element creatSpecsNode(ValidationManager validationManager, Document document,
+ String categories)
+ {
+
+ Element specsNode = document.createElement(XML_TAG_SPECS);
+ String[] specList = categories.split(SEPARATOR);
+
+ //SpecKey[] specs = SpecKey.values();
+ //for (SpecKey spec : specs)
+ for (String spec : specList)
+ {
+ Element specNode = document.createElement(XML_TAG_SPEC);
+ specNode.setAttribute(ATTRIBUTE_ID, spec);//spec.getId());
+ specNode.setAttribute(ATTRIBUTE_NAME, spec);//spec.getId());
+
+ specsNode.appendChild(specNode);
+ }
+
+ return specsNode;
+ }
+
+ private static Element createCheckersNode(ValidationManager validationManager, Document document)
+ {
+
+ Element checkersNode = document.createElement(XML_TAG_CHECKERS);
+
+ List<CheckerDescription> checkers = validationManager.getCheckersDescription();
+
+ for (CheckerDescription checker : checkers)
+ {
+ Element checkerNode = document.createElement(XML_TAG_CHECKER);
+ checkerNode.setAttribute(ATTRIBUTE_ID, checker.getId());
+ checkerNode.setAttribute(ATTRIBUTE_NAME, checker.getName());
+
+ checkersNode.appendChild(checkerNode);
+ }
+
+ return checkersNode;
+ }
+
+ private static Element createDevicesNode(ValidationManager validationManager, Document document)
+ {
+
+ Element devicesNode = document.createElement(XML_TAG_DEVICES);
+ DevicesSpecsContainer devicesSpecsContainer = validationManager.getDevicesSpecsContainer();
+ List<DeviceSpecification> devices = devicesSpecsContainer.getDeviceSpecifications();
+
+ // order by device name
+ Collections.sort(devices, new Comparator<DeviceSpecification>()
+ {
+ public int compare(DeviceSpecification d1, DeviceSpecification d2)
+ {
+ return d1.getName().compareToIgnoreCase(d2.getName());
+ }
+ });
+
+ for (DeviceSpecification devSpec : devices)
+ {
+
+ Element deviceNode = document.createElement(XML_TAG_DEVICE);
+
+ deviceNode.setAttribute(ATTRIBUTE_ID, devSpec.getId());
+ deviceNode.setAttribute(ATTRIBUTE_NAME, devSpec.getName().replace("™", "&#8482;"));
+
+ devicesNode.appendChild(deviceNode);
+ }
+
+ return devicesNode;
+ }
+
+ /**
+ * Print an XML file with: list of checkers, list of devices, map of specifications vs devices
+ * @param stream The output stream
+ * @throws ParserConfigurationException
+ * @throws TransformerException
+ * @throws UnsupportedEncodingException
+ */
+ public static void printXMLDevicesCheckersSpecsMap(PrintStream stream)
+ throws ParserConfigurationException, UnsupportedEncodingException, TransformerException
+ {
+
+ // TODO: implement a configuration file or another way to keep this list
+ String categories =
+ SpecKey.screenSize_Small.getId() + SEPARATOR + SpecKey.screenSize_XLarge.getId()
+ + SEPARATOR + SpecKey.screenOrientation_Port.getId() + SEPARATOR
+ + SpecKey.screenOrientation_Land.getId() + SEPARATOR
+ + SpecKey.pixelDensity_High.getId() + SEPARATOR
+ + SpecKey.pixelDensity_Low.getId() + SEPARATOR
+ + SpecKey.pixelDensity_Medium.getId() + SEPARATOR
+ + SpecKey.pixelDensity_XHigh.getId();
+
+ if (cache.get(categories) == null)
+ {
+ Document document =
+ DocumentBuilderFactoryImpl.newInstance().newDocumentBuilder().newDocument();
+ Element appvalidatorElement = createAppvalidatorNode(document);
+
+ ValidationManager validationManager = new ValidationManager();
+
+ Element checkersNode = createCheckersNode(validationManager, document);
+ appvalidatorElement.appendChild(checkersNode);
+
+ Element devicesNode = createDevicesNode(validationManager, document);
+ appvalidatorElement.appendChild(devicesNode);
+
+ Element specsNode = creatSpecsNode(validationManager, document, categories);
+ appvalidatorElement.appendChild(specsNode);
+
+ Element specDevicesMapNode =
+ createSpecDevicesMapNode(validationManager, document, categories);
+ appvalidatorElement.appendChild(specDevicesMapNode);
+
+ cache.put(categories, document);
+ }
+
+ XmlUtils.printXMLFormat(cache.get(categories));
+
+ XmlUtils.printXMLFormat(cache.get(categories));
+
+ }
+
+}
diff --git a/src/plugins/preflighting/src/com/motorolamobility/preflighting/output/AbstractOutputter.java b/src/plugins/preflighting/src/com/motorolamobility/preflighting/output/AbstractOutputter.java
new file mode 100644
index 0000000..39fb9ec
--- /dev/null
+++ b/src/plugins/preflighting/src/com/motorolamobility/preflighting/output/AbstractOutputter.java
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorolamobility.preflighting.output;
+
+import java.io.File;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.util.List;
+
+import com.motorolamobility.preflighting.core.exception.PreflightingToolException;
+import com.motorolamobility.preflighting.core.validation.ApplicationValidationResult;
+import com.motorolamobility.preflighting.core.validation.Parameter;
+import com.motorolamobility.preflighting.core.validation.ValidationManager.InputParameter;
+
+/**
+ * Abstract class for printing results
+ */
+public abstract class AbstractOutputter
+{
+ private File applicationFile;
+
+ private int limit = -1;
+
+ /**
+ * Returns the path for the application being validated.
+ * <br><br>
+ * Note: call initializeParams before calling this method.
+ * @return the path for the application being validated.
+ */
+ public File getApplicationFile()
+ {
+ return applicationFile;
+ }
+
+ /**
+ * Sets the path for the application being validated.
+ * @param applicationFile the file path.
+ */
+ public void setApplicationFile(File applicationFile)
+ {
+ this.applicationFile = applicationFile;
+ }
+
+ /**
+ * The maximum number of results that will be displayed.
+ * <br><br>
+ * Note: call initializeParams before calling this method.
+ * @return an integer representing the limit.
+ */
+ public int getLimit()
+ {
+ return limit;
+ }
+
+ /**
+ * Sets the maximum number of results that will be displayed.
+ * @param limit the integer representing the limit.
+ */
+ public void setLimit(int limit)
+ {
+ this.limit = limit;
+ }
+
+ /**
+ * Initialize limit and applicationFile variables
+ * @param parameters the parameters to the outputter
+ */
+ public void initializeParams(List<Parameter> parameters)
+ {
+ for (Parameter inputParameter : parameters)
+ {
+ if (InputParameter.APPLICATION_PATH.getAlias()
+ .equals(inputParameter.getParameterType()))
+ {
+ String applicationPath = inputParameter.getValue();
+ applicationFile = new File(applicationPath);
+ }
+ else if (InputParameter.LIMIT.getAlias().equals(inputParameter.getParameterType()))
+ {
+ try
+ {
+ limit = Integer.parseInt(inputParameter.getValue());
+ }
+ catch (NumberFormatException nfe)
+ {
+ //do nothing
+ }
+ }
+ }
+ }
+
+ /**
+ * Print errors to a stream.
+ *
+ * @param exceptionThrown
+ * @param out the stream used to print errors.
+ */
+ public abstract void printError(Exception exceptionThrown, PrintStream errorStream);
+
+ /**
+ * Prints the results to a stream.
+ * <br>
+ * Note: If you need parameter information inside this method, call initializeParams.
+ *
+ * @param result the result set to be printed.
+ * @param stream the stream used to print validation results.
+ * @param parameters the parameters from the command line
+ * @throws PreflightingToolException
+ */
+ public abstract void print(ApplicationValidationResult results, OutputStream stream,
+ List<Parameter> parameters) throws PreflightingToolException;
+
+ /**
+ * Compute the relative path of project and APK resources
+ * @param resouce the file representing the resource whose path is desired
+ * @return the resource path
+ */
+ protected String computeResourcePath(File resource)
+ {
+ // Append file location (only relative path)
+ String fileLocation = resource.getAbsolutePath();
+ // project passed
+ if (fileLocation.startsWith(applicationFile.getAbsolutePath()))
+ {
+ fileLocation = fileLocation.substring(applicationFile.getAbsolutePath().length() + 1);
+ }
+ // probably apk passed, check
+ else
+ {
+ String apkName = applicationFile.getName();
+ int apkNameIndex = fileLocation.indexOf(apkName);
+ // if this test fails, the file with problem is possibly somewhere
+ // unknown and the relative path cannot be guessed and it will be used as is
+ if (apkNameIndex != -1)
+ {
+ int relativePathStartIndex = fileLocation.indexOf(File.separator, apkNameIndex);
+ fileLocation = fileLocation.substring(relativePathStartIndex + 1);
+ }
+ else
+ {
+ fileLocation = resource.getPath();
+ }
+
+ }
+ return fileLocation;
+ }
+}
diff --git a/src/plugins/remote.device/.classpath b/src/plugins/remote.device/.classpath
new file mode 100644
index 0000000..8a8f166
--- /dev/null
+++ b/src/plugins/remote.device/.classpath
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>
+ <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/src/plugins/remote.device/.project b/src/plugins/remote.device/.project
new file mode 100644
index 0000000..c5812d3
--- /dev/null
+++ b/src/plugins/remote.device/.project
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>com.motorola.studio.android.remote</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.ManifestBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.SchemaBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.pde.PluginNature</nature>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+</projectDescription>
diff --git a/src/plugins/remote.device/META-INF/MANIFEST.MF b/src/plugins/remote.device/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..b5861a4
--- /dev/null
+++ b/src/plugins/remote.device/META-INF/MANIFEST.MF
@@ -0,0 +1,20 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: %pluginName
+Bundle-SymbolicName: com.motorola.studio.android.remote;singleton:=true
+Bundle-Version: 5.0.0.qualifier
+Bundle-Activator: com.motorola.studio.android.remote.RemoteDevicePlugin
+Bundle-Vendor: %providerName
+Require-Bundle: org.eclipse.ui,
+ org.eclipse.core.runtime,
+ org.eclipse.sequoyah.device.framework,
+ org.eclipse.sequoyah.device.framework.ui,
+ org.eclipse.sequoyah.device.common.utilities,
+ com.motorola.studio.android.common,
+ com.motorola.studio.android
+Bundle-RequiredExecutionEnvironment: JavaSE-1.6
+Bundle-Localization: plugin
+Bundle-ActivationPolicy: lazy
+Export-Package: com.motorola.studio.android.remote,
+ com.motorola.studio.android.remote.i18n,
+ com.motorola.studio.android.remote.instance
diff --git a/src/plugins/remote.device/build.properties b/src/plugins/remote.device/build.properties
new file mode 100644
index 0000000..6b4afb7
--- /dev/null
+++ b/src/plugins/remote.device/build.properties
@@ -0,0 +1,7 @@
+source.. = src/
+output.. = bin/
+bin.includes = META-INF/,\
+ .,\
+ plugin.xml,\
+ icons/,\
+ plugin.properties
diff --git a/src/plugins/remote.device/icons/plate16.gif b/src/plugins/remote.device/icons/plate16.gif
new file mode 100644
index 0000000..ddf36d3
--- /dev/null
+++ b/src/plugins/remote.device/icons/plate16.gif
Binary files differ
diff --git a/src/plugins/remote.device/icons/remote_device.png b/src/plugins/remote.device/icons/remote_device.png
new file mode 100644
index 0000000..82fa84a
--- /dev/null
+++ b/src/plugins/remote.device/icons/remote_device.png
Binary files differ
diff --git a/src/plugins/remote.device/icons/start.png b/src/plugins/remote.device/icons/start.png
new file mode 100644
index 0000000..c7fa5a2
--- /dev/null
+++ b/src/plugins/remote.device/icons/start.png
Binary files differ
diff --git a/src/plugins/remote.device/icons/started-icon-16x16.png b/src/plugins/remote.device/icons/started-icon-16x16.png
new file mode 100644
index 0000000..38f4f9e
--- /dev/null
+++ b/src/plugins/remote.device/icons/started-icon-16x16.png
Binary files differ
diff --git a/src/plugins/remote.device/icons/stop.png b/src/plugins/remote.device/icons/stop.png
new file mode 100644
index 0000000..5e0df42
--- /dev/null
+++ b/src/plugins/remote.device/icons/stop.png
Binary files differ
diff --git a/src/plugins/remote.device/icons/stopped-icon-16x16.png b/src/plugins/remote.device/icons/stopped-icon-16x16.png
new file mode 100644
index 0000000..4c77aee
--- /dev/null
+++ b/src/plugins/remote.device/icons/stopped-icon-16x16.png
Binary files differ
diff --git a/src/plugins/remote.device/icons/switch2usb_cable.png b/src/plugins/remote.device/icons/switch2usb_cable.png
new file mode 100644
index 0000000..6503782
--- /dev/null
+++ b/src/plugins/remote.device/icons/switch2usb_cable.png
Binary files differ
diff --git a/src/plugins/remote.device/icons/switch2usb_cable2.png b/src/plugins/remote.device/icons/switch2usb_cable2.png
new file mode 100644
index 0000000..e5200ae
--- /dev/null
+++ b/src/plugins/remote.device/icons/switch2usb_cable2.png
Binary files differ
diff --git a/src/plugins/remote.device/icons/switch2usb_symbol.png b/src/plugins/remote.device/icons/switch2usb_symbol.png
new file mode 100644
index 0000000..43d05b9
--- /dev/null
+++ b/src/plugins/remote.device/icons/switch2usb_symbol.png
Binary files differ
diff --git a/src/plugins/remote.device/icons/wireless-16x16.png b/src/plugins/remote.device/icons/wireless-16x16.png
new file mode 100644
index 0000000..cf5546e
--- /dev/null
+++ b/src/plugins/remote.device/icons/wireless-16x16.png
Binary files differ
diff --git a/src/plugins/remote.device/icons/wireless_wizard-icon-64x64.png b/src/plugins/remote.device/icons/wireless_wizard-icon-64x64.png
new file mode 100644
index 0000000..58ee256
--- /dev/null
+++ b/src/plugins/remote.device/icons/wireless_wizard-icon-64x64.png
Binary files differ
diff --git a/src/plugins/remote.device/plugin.properties b/src/plugins/remote.device/plugin.properties
new file mode 100644
index 0000000..784c7c9
--- /dev/null
+++ b/src/plugins/remote.device/plugin.properties
@@ -0,0 +1,42 @@
+#
+# Copyright (C) 2012 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+pluginName=MOTODEV Studio for Android Remote Device Plug-in
+providerName=Motorola Mobility, Inc.
+copyright=Copyright (C) 2012 The Android Open Source Project
+
+connectServiceName=Connect
+connectServiceDescription=Service to connect to a remote device
+
+disconnectServiceName=Disconnect
+disconnectServiceDescription=Service to disconnect from a remote device
+
+initServiceName=Init Remote Device Service
+initServiceDescription=Service to initialize the remote device
+
+connectedStatus=Connected
+disconnectedStatus=Disconnected
+
+remoteDeviceType=Android Remote Device
+
+remoteDevicePropertyPage=Remote Device
+
+newRemoteDeviceWizardName=New Android Remote Device...
+
+wirelessServiceName=Switch to Wireless Connection Mode
+usbModeServiceName=Switch to USB Connection Mode
+wirelessDescription=Service to switch the connection mode from USB cable to TCP/IP
+usbModeDescription=Service to switch the connection mode from TCP/IP to USB cable \ No newline at end of file
diff --git a/src/plugins/remote.device/plugin.xml b/src/plugins/remote.device/plugin.xml
new file mode 100644
index 0000000..7d66136
--- /dev/null
+++ b/src/plugins/remote.device/plugin.xml
@@ -0,0 +1,238 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?eclipse version="3.4"?>
+
+<!--
+ Copyright (C) 2012 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<plugin>
+ <extension
+ id="androidRemoteDevice"
+ name="androidRemoteDevice"
+ point="org.eclipse.sequoyah.device.framework.deviceTypes">
+ <deviceType
+ dropSupportHandler="com.motorola.studio.android.remote.RemoteDeviceDropSupportHandler"
+ handler="com.motorola.studio.android.remote.handlers.RemoteDeviceHandler"
+ icon="icons/plate16.gif"
+ id="com.motorola.studio.android.remote.androidRemoteDevice"
+ isAbstract="false"
+ isPersistent="true"
+ label="%remoteDeviceType"
+ name="%remoteDeviceType">
+ </deviceType>
+ </extension>
+ <extension
+ id="status.connected"
+ point="org.eclipse.sequoyah.device.framework.status">
+ <status
+ canDeleteInstance="false"
+ canEditProperties="false"
+ id="com.motorola.studio.android.remote.status.connected"
+ image="icons/started-icon-16x16.png"
+ name="%connectedStatus">
+ </status>
+ </extension>
+ <extension
+ id="status.disconnected"
+ point="org.eclipse.sequoyah.device.framework.status">
+ <status
+ canDeleteInstance="true"
+ canEditProperties="true"
+ id="com.motorola.studio.android.remote.status.disconnected"
+ image="icons/stopped-icon-16x16.png"
+ name="%disconnectedStatus">
+ </status>
+ </extension>
+ <extension
+ id="connectRemoteService"
+ name="connectRemoteService"
+ point="org.eclipse.sequoyah.device.framework.service">
+ <service
+ copyright="%copyright"
+ description="%connectServiceDescription"
+ handler="com.motorola.studio.android.remote.handlers.ConnectToRemoteHandler"
+ icon="icons/start.png"
+ id="com.motorola.studio.android.remote.connectRemoteService"
+ name="%connectServiceName"
+ provider="%providerName"
+ version="1.0.0"
+ visible="true">
+ </service>
+ </extension>
+ <extension
+ id="disconnectRemoteService"
+ name="disconnectRemoteService"
+ point="org.eclipse.sequoyah.device.framework.service">
+ <service
+ copyright="%copyright"
+ description="%disconnectServiceDescription"
+ handler="com.motorola.studio.android.remote.handlers.DisconnectFromRemoteHandler"
+ icon="icons/stop.png"
+ id="com.motorola.studio.android.remote.disconnectRemoteService"
+ name="%disconnectServiceName"
+ provider="%providerName"
+ version="1.0.0"
+ visible="true">
+ </service>
+ </extension>
+ <extension
+ id="initRemoteService"
+ name="initRemoteService"
+ point="org.eclipse.sequoyah.device.framework.service">
+ <service
+ copyright="%copyright"
+ description="%initServiceDescription"
+ handler="com.motorola.studio.android.remote.handlers.InitRemoteHandler"
+ icon="icons/start.png"
+ id="com.motorola.studio.android.remote.initRemoteService"
+ name="%initServiceName"
+ provider="%providerName"
+ version="1.0.0"
+ visible="false">
+ </service>
+ </extension>
+ <extension
+ id="com.motorola.studio.android.remote.androidRemoteDevice"
+ point="org.eclipse.sequoyah.device.framework.serviceDefinition">
+ <service
+ id="com.motorola.studio.android.remote.connectRemoteService">
+ <status
+ endId="com.motorola.studio.android.remote.status.connected"
+ haltId="com.motorola.studio.android.remote.status.disconnected"
+ startId="com.motorola.studio.android.remote.status.disconnected">
+ </status></service>
+ </extension>
+ <extension
+ id="com.motorola.studio.android.remote.androidRemoteDevice"
+ point="org.eclipse.sequoyah.device.framework.serviceDefinition">
+ <service
+ id="com.motorola.studio.android.remote.disconnectRemoteService">
+ <status
+ endId="com.motorola.studio.android.remote.status.disconnected"
+ haltId="com.motorola.studio.android.remote.status.connected"
+ startId="com.motorola.studio.android.remote.status.connected">
+ </status></service>
+ </extension>
+ <extension
+ id="com.motorola.studio.android.remote.androidRemoteDevice"
+ point="org.eclipse.sequoyah.device.framework.serviceDefinition">
+ <service
+ id="com.motorola.studio.android.remote.initRemoteService">
+ <status
+ endId="com.motorola.studio.android.remote.status.disconnected"
+ haltId="com.motorola.studio.android.remote.status.disconnected"
+ startId="OFF">
+ </status></service>
+ </extension>
+ <extension
+ point="org.eclipse.sequoyah.device.framework.ui.newDeviceWizardPages">
+ <wizardPage
+ id="com.motorola.studio.android.remote.ipPortPage"
+ pageClass="com.motorola.studio.android.remote.ui.RemoteDeviceWizardPage">
+ <deviceType
+ deviceTypeId="com.motorola.studio.android.remote.androidRemoteDevice">
+ </deviceType>
+ </wizardPage>
+ </extension>
+ <extension
+ point="org.eclipse.ui.propertyPages">
+ <page
+ class="com.motorola.studio.android.remote.ui.RemoteDevicePropertiesPage"
+ id="com.motorola.studio.android.remote.remoteDevice"
+ name="%remoteDevicePropertyPage">
+ <enabledWhen>
+ <instanceof
+ value="com.motorola.studio.android.remote.instance.RemoteDeviceInstance">
+ </instanceof>
+ </enabledWhen>
+ </page>
+ </extension>
+ <extension point="org.eclipse.ui.menus">
+ <menuContribution locationURI="menu:motorolaMenu?after=otherNewWizardsSeparator">
+ <command commandId="com.motorola.studio.android.device.new.remote.device.wizard" icon="icons/remote_device.png" label="%newRemoteDeviceWizardName" style="push">
+ </command>
+ </menuContribution>
+ </extension>
+ <extension point="org.eclipse.ui.commands">
+ <command defaultHandler="com.motorola.studio.android.remote.handlers.OpenNewRemoteDeviceWizardHandler" id="com.motorola.studio.android.device.new.remote.device.wizard" name="%newRemoteDeviceWizardName">
+ </command>
+ </extension>
+ <!--START WiFi service -->
+ <extension
+ id="wireless"
+ name="%wirelessServiceName"
+ point="org.eclipse.sequoyah.device.framework.service">
+ <service
+ copyright="%copyright"
+ description="%wirelessDescription"
+ handler="com.motorola.studio.android.remote.handlers.WirelessServiceHandler"
+ icon="icons/wireless-16x16.png"
+ id="com.motorola.studio.android.remote.wifi"
+ name="%wirelessServiceName"
+ parallelized="false"
+ provider="%providerName"
+ version="0.1.0"
+ visible="true">
+ </service>
+ </extension>
+ <extension
+ id="com.motorola.studio.android.handset.androidHandset"
+ name="switchtowireless"
+ point="org.eclipse.sequoyah.device.framework.serviceDefinition">
+ <service
+ id="com.motorola.studio.android.remote.wifi">
+ <status
+ endId="com.motorola.studio.android.handset.status.handsetonline"
+ haltId="com.motorola.studio.android.handset.status.handsetonline"
+ startId="com.motorola.studio.android.handset.status.handsetonline">
+ </status>
+ </service>
+ </extension>
+ <!--END WiFi services END-->
+
+ <!--START back to USB service -->
+ <extension
+ id="usbmode"
+ name="%usbModeServiceName"
+ point="org.eclipse.sequoyah.device.framework.service">
+ <service
+ copyright="%copyright"
+ description="%usbModeDescription"
+ handler="com.motorola.studio.android.remote.handlers.USBModeServiceHandler"
+ icon="icons/switch2usb_cable.png"
+ id="com.motorola.studio.android.remote.usbmode"
+ name="%usbModeServiceName"
+ parallelized="false"
+ provider="%providerName"
+ version="0.1.0"
+ visible="true">
+ </service>
+ </extension>
+ <extension
+ id="com.motorola.studio.android.remote.androidRemoteDevice"
+ name="switchtousbmode"
+ point="org.eclipse.sequoyah.device.framework.serviceDefinition">
+ <service
+ id="com.motorola.studio.android.remote.usbmode">
+ <status
+ endId="com.motorola.studio.android.remote.status.disconnected"
+ haltId="com.motorola.studio.android.remote.status.disconnected"
+ startId="com.motorola.studio.android.remote.status.connected">
+ </status>
+ </service>
+ </extension>
+ <!--END back to USB services END-->
+
+</plugin>
diff --git a/src/plugins/remote.device/src/com/motorola/studio/android/remote/RemoteDeviceConstants.java b/src/plugins/remote.device/src/com/motorola/studio/android/remote/RemoteDeviceConstants.java
new file mode 100644
index 0000000..c70b200
--- /dev/null
+++ b/src/plugins/remote.device/src/com/motorola/studio/android/remote/RemoteDeviceConstants.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.remote;
+
+/**
+ * Constants used by Android Remote Device Plug-in.
+ */
+public class RemoteDeviceConstants
+{
+
+ /**
+ * The ID of the device declared by this plug-in
+ */
+ public static final String DEVICE_ID = RemoteDevicePlugin.PLUGIN_ID + ".androidRemoteDevice";
+
+ public static final String SERVICE_INIT_ID = RemoteDevicePlugin.PLUGIN_ID
+ + ".initRemoteService";
+
+ public static final String SERVICE_CONNECT_ID = RemoteDevicePlugin.PLUGIN_ID
+ + ".connectRemoteService";
+
+ public static final String SERVICE_DISCONNECT_ID = RemoteDevicePlugin.PLUGIN_ID
+ + ".disconnectRemoteService";
+
+ public static final String HELP_ID = RemoteDevicePlugin.PLUGIN_ID + ".remoteDeviceProperties";
+
+ public static final String WIRELESS_HELP_ID = RemoteDevicePlugin.PLUGIN_ID
+ + ".wirelessRemoteDeviceProperties";
+
+ public static final String DUMMY_TRANSITION = "dummy";
+
+ public static final int DEFAULT_TIMEOUT = 30;
+
+ public static final int DEFAULT_PORT = 10000;
+
+ public static final String DEFAULT_WIRELESS_SUFIX = "_wireless";
+
+ /**
+ * Preference key of the Question Dialog about disconnection all remote devices in shutdown
+ */
+ public static final String DISCONNECT_ALL_REMOTE_DEVICES_IN_SHUTDOWN_KEY_PREFERENCE =
+ "disconnect.all.remote.devices.in.shutdown";
+
+ public static final String CONNECT_PARAMETER = "connect";
+
+ /**
+ * The minimum port number allowed to create a remote device.
+ */
+ public static final int MINIMUM_PORT_NUMBER = 1024;
+
+ /**
+ * The maximum port number allowed for TCP/IP addresses.
+ */
+ public static final int MAXIMUM_PORT_NUMBER = 65535;
+}
diff --git a/src/plugins/remote.device/src/com/motorola/studio/android/remote/RemoteDeviceDropSupportHandler.java b/src/plugins/remote.device/src/com/motorola/studio/android/remote/RemoteDeviceDropSupportHandler.java
new file mode 100644
index 0000000..417e2bd
--- /dev/null
+++ b/src/plugins/remote.device/src/com/motorola/studio/android/remote/RemoteDeviceDropSupportHandler.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.remote;
+
+import org.eclipse.sequoyah.device.framework.model.IInstance;
+import org.eclipse.swt.dnd.DropTargetEvent;
+import org.eclipse.swt.dnd.TransferData;
+
+import com.motorola.studio.android.devices.AbstractDeviceDropSupportHandler;
+
+public class RemoteDeviceDropSupportHandler extends AbstractDeviceDropSupportHandler
+{
+
+ /* (non-Javadoc)
+ * @see org.eclipse.sequoyah.device.framework.model.IDeviceTypeDropSupport#canDrop(org.eclipse.sequoyah.device.framework.model.IInstance, org.eclipse.swt.dnd.TransferData, org.eclipse.swt.dnd.DropTargetEvent)
+ */
+ @Override
+ public boolean canDrop(IInstance instance, TransferData data, DropTargetEvent event)
+ {
+ return super.canDrop(instance, data, event)
+ && RemoteDevicePlugin.STATUS_ONLINE_ID.equals(instance.getStatus());
+ }
+}
diff --git a/src/plugins/remote.device/src/com/motorola/studio/android/remote/RemoteDeviceInstanceBuilder.java b/src/plugins/remote.device/src/com/motorola/studio/android/remote/RemoteDeviceInstanceBuilder.java
new file mode 100644
index 0000000..dae7687
--- /dev/null
+++ b/src/plugins/remote.device/src/com/motorola/studio/android/remote/RemoteDeviceInstanceBuilder.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.remote;
+
+import java.util.Properties;
+
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.sequoyah.device.framework.model.IInstanceBuilder;
+
+/**
+ * This class knows how to build TmL instances of remote devices.
+ * It's intended to be used by TmL only.
+ */
+public class RemoteDeviceInstanceBuilder implements IInstanceBuilder
+{
+ private final Properties properties;
+
+ private final String name;
+
+ /**
+ * Creates a new Instance Builder with the given information.
+ *
+ * @param instanceName the name of the instance to be created using this builder
+ * @param properties the properties of the instance to be created using this builder
+ */
+ public RemoteDeviceInstanceBuilder(String instanceName, Properties properties)
+ {
+ this.properties = properties;
+ this.name = instanceName;
+ }
+
+ /**
+ * Always returns <code>null</code> since this information does
+ * not make sense for Android Remote Devices Instances.
+ */
+ public IPath getLocationPath()
+ {
+ return null;
+ }
+
+ /**
+ * Retrieves the name of the instance to be created using this builder.
+ *
+ * @return the name of the instance to be created using this builder.
+ */
+ public String getProjectName()
+ {
+ return name;
+ }
+
+ /**
+ * Retrieves the properties of the instance to be created using this builder.
+ *
+ * @return the properties of the instance to be created using this builder.
+ */
+ public Properties getProperties()
+ {
+ return properties;
+ }
+
+ /**
+ * Retrieves the value of the give property key.
+ *
+ * @param key the key of the property.
+ *
+ * @return the value for the property for the instance to be created using this builder.
+ */
+ public String getProperty(String key)
+ {
+ return properties.getProperty(key);
+ }
+}
diff --git a/src/plugins/remote.device/src/com/motorola/studio/android/remote/RemoteDevicePlugin.java b/src/plugins/remote.device/src/com/motorola/studio/android/remote/RemoteDevicePlugin.java
new file mode 100644
index 0000000..b3afbf6
--- /dev/null
+++ b/src/plugins/remote.device/src/com/motorola/studio/android/remote/RemoteDevicePlugin.java
@@ -0,0 +1,280 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.remote;
+
+import static com.motorola.studio.android.common.log.StudioLogger.warn;
+
+import java.util.Collection;
+import java.util.List;
+
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.sequoyah.device.common.utilities.exception.SequoyahException;
+import org.eclipse.sequoyah.device.framework.DeviceUtils;
+import org.eclipse.sequoyah.device.framework.events.IInstanceListener;
+import org.eclipse.sequoyah.device.framework.events.InstanceAdapter;
+import org.eclipse.sequoyah.device.framework.events.InstanceEvent;
+import org.eclipse.sequoyah.device.framework.events.InstanceEventManager;
+import org.eclipse.sequoyah.device.framework.factory.DeviceTypeRegistry;
+import org.eclipse.sequoyah.device.framework.model.IDeviceType;
+import org.eclipse.sequoyah.device.framework.model.IInstance;
+import org.eclipse.sequoyah.device.framework.model.IService;
+import org.eclipse.sequoyah.device.framework.model.handler.IServiceHandler;
+import org.eclipse.sequoyah.device.framework.model.handler.ServiceHandler;
+import org.eclipse.ui.IWorkbenchListener;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.plugin.AbstractUIPlugin;
+import org.osgi.framework.BundleContext;
+
+import com.motorola.studio.android.AndroidPlugin;
+import com.motorola.studio.android.adt.DDMSFacade;
+import com.motorola.studio.android.adt.DdmsRunnable;
+import com.motorola.studio.android.adt.StudioAndroidEventManager;
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.remote.instance.RemoteDeviceInstance;
+
+/**
+ * The activator class controls the plug-in life cycle.
+ */
+public class RemoteDevicePlugin extends AbstractUIPlugin
+{
+
+ public static final String PLUGIN_ID = "com.motorola.studio.android.remote";
+
+ /**
+ * The ID of the device declared by this plug-in
+ */
+ public static final String DEVICE_ID = PLUGIN_ID + ".androidRemoteDevice";
+
+ public static final String STATUS_ONLINE_ID = PLUGIN_ID + ".status.connected";
+
+ public static final String WIRELESS_PAGE_CONTEXT_HELP_ID = PLUGIN_ID + ".langPage";
+
+ /**
+ * The shared instance.
+ */
+ private static RemoteDevicePlugin plugin;
+
+ /**
+ * The service that connects the remote device.
+ */
+ private static ServiceHandler connectServiceHandler = null;
+
+ /**
+ * The service that disconnects the remote device.
+ */
+ private static ServiceHandler disconnectServiceHandler = null;
+
+ // sync Studio device status for already connected remote devices
+ private static final Runnable sdkLoaderListener = new Runnable()
+ {
+ @Override
+ public void run()
+ {
+ Collection<String> serialNumbers = DDMSFacade.getConnectedSerialNumbers();
+ for (String serial : serialNumbers)
+ {
+ RemoteDeviceUtils.connectDevice(serial);
+ }
+ }
+ };
+
+ /*
+ * Listener called when a new device is connected
+ */
+ private static DdmsRunnable connectedListener = new DdmsRunnable()
+ {
+
+ @Override
+ public void run(String serialNumber)
+ {
+ RemoteDeviceUtils.connectDevice(serialNumber);
+ }
+ };
+
+ /*
+ * Listener called when a device is disconnected
+ */
+ private static DdmsRunnable disconnectedListener = new DdmsRunnable()
+ {
+
+ @Override
+ public void run(String serialNumber)
+ {
+ RemoteDeviceUtils.disconnectDevice(serialNumber);
+ }
+ };
+
+ /*
+ * Listener responsible for initializing the Remote Device instances
+ * right after they are loaded by TmL
+ */
+ private static final IInstanceListener tmlListener = new InstanceAdapter()
+ {
+ @Override
+ public void instanceLoaded(InstanceEvent e)
+ {
+ IInstance instance = e.getInstance();
+ if (instance instanceof RemoteDeviceInstance)
+ {
+ IDeviceType device =
+ DeviceTypeRegistry.getInstance().getDeviceTypeById(
+ instance.getDeviceTypeId());
+ IService service =
+ DeviceUtils.getServiceById(device, RemoteDeviceConstants.SERVICE_INIT_ID);
+ IServiceHandler handler = service.getHandler();
+ try
+ {
+ handler.run(instance);
+ }
+ catch (SequoyahException e1)
+ {
+ warn("Remote Device: the instance " + instance.getName()
+ + " is in an incorrect state (" + e1.getMessage() + ").");
+ }
+ }
+ }
+ };
+
+ // Listener that will be used to ask the user if he wants to disconnect the remote devices when the Studio is being closed
+ private static final IWorkbenchListener workbenchListener = new RemoteDeviceWorkbenchListener();
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.ui.plugin.AbstractUIPlugin#start(org.osgi.framework.BundleContext)
+ */
+ @Override
+ public void start(BundleContext context) throws Exception
+ {
+ StudioLogger.debug(RemoteDevicePlugin.class,
+ "Starting MOTODEV Android Remote Device Plugin...");
+
+ super.start(context);
+ plugin = this;
+ AndroidPlugin.getDefault().addSDKLoaderListener(sdkLoaderListener);
+ StudioAndroidEventManager.asyncAddDeviceChangeListeners(connectedListener,
+ disconnectedListener);
+ InstanceEventManager.getInstance().addInstanceListener(tmlListener);
+ PlatformUI.getWorkbench().addWorkbenchListener(workbenchListener);
+
+ StudioLogger.debug(RemoteDevicePlugin.class,
+ "Starting MOTODEV Android Remote Device Plugin started.");
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.ui.plugin.AbstractUIPlugin#stop(org.osgi.framework.BundleContext)
+ */
+ @Override
+ public void stop(BundleContext context) throws Exception
+ {
+ AndroidPlugin.getDefault().removeSDKLoaderListener(sdkLoaderListener);
+ StudioAndroidEventManager.asyncRemoveDeviceChangeListeners(connectedListener,
+ disconnectedListener);
+ InstanceEventManager.getInstance().removeInstanceListener(tmlListener);
+ PlatformUI.getWorkbench().removeWorkbenchListener(workbenchListener);
+ plugin = null;
+ super.stop(context);
+ }
+
+ /**
+ * Returns the shared instance
+ *
+ * @return the shared instance
+ */
+ public static RemoteDevicePlugin getDefault()
+ {
+ return plugin;
+ }
+
+ /**
+ * Retrieves the connect service handler.
+ *
+ * @return The currently registered connect service handler, or <null> if no handler is registered.
+ */
+ public static ServiceHandler getConnectServiceHandler()
+ {
+ if (connectServiceHandler == null)
+ {
+ IDeviceType device =
+ DeviceTypeRegistry.getInstance().getDeviceTypeById(
+ RemoteDeviceConstants.DEVICE_ID);
+ List<IService> services = device.getServices();
+ for (IService service : services)
+ {
+ IServiceHandler handler = service.getHandler();
+ if (handler.getService().getId().equals(RemoteDeviceConstants.SERVICE_CONNECT_ID))
+ {
+ connectServiceHandler = (ServiceHandler) handler;
+ break;
+ }
+ }
+ }
+
+ return connectServiceHandler;
+ }
+
+ /**
+ * Retrieves the disconnect service handler.
+ *
+ * @return The currently registered disconnect service handler, or <null> if no handler is registered.
+ */
+ public static ServiceHandler getDisconnectServiceHandler()
+ {
+ if (disconnectServiceHandler == null)
+ {
+ IDeviceType device =
+ DeviceTypeRegistry.getInstance().getDeviceTypeById(
+ RemoteDeviceConstants.DEVICE_ID);
+ List<IService> services = device.getServices();
+ for (IService service : services)
+ {
+ IServiceHandler handler = service.getHandler();
+ if (handler.getService().getId()
+ .equals(RemoteDeviceConstants.SERVICE_DISCONNECT_ID))
+ {
+ disconnectServiceHandler = (ServiceHandler) handler;
+ break;
+ }
+ }
+ }
+
+ return disconnectServiceHandler;
+ }
+
+ /**
+ * Creates and returns a new image descriptor for an image file in this plug-in.
+ * @param path the relative path of the image file, relative to the root of the plug-in; the path must be legal
+ * @return an image descriptor, or null if no image could be found
+ */
+ public static ImageDescriptor getImageDescriptor(String path)
+ {
+ return imageDescriptorFromPlugin(PLUGIN_ID, path);
+ }
+
+ public static boolean isWifiServiceEnabled()
+ {
+ Boolean enabled = null;
+ try
+ {
+ enabled = Boolean.parseBoolean(System.getProperty("enableWifiService"));
+ }
+ catch (Exception e)
+ {
+ enabled = Boolean.FALSE;
+ }
+ return enabled;
+ }
+}
diff --git a/src/plugins/remote.device/src/com/motorola/studio/android/remote/RemoteDeviceUtils.java b/src/plugins/remote.device/src/com/motorola/studio/android/remote/RemoteDeviceUtils.java
new file mode 100644
index 0000000..6970af8
--- /dev/null
+++ b/src/plugins/remote.device/src/com/motorola/studio/android/remote/RemoteDeviceUtils.java
@@ -0,0 +1,359 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.remote;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+import java.util.concurrent.TimeoutException;
+
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.sequoyah.device.common.utilities.exception.SequoyahException;
+import org.eclipse.sequoyah.device.framework.model.AbstractMobileInstance;
+import org.eclipse.sequoyah.device.framework.model.IInstance;
+import org.eclipse.ui.PlatformUI;
+
+import com.motorola.studio.android.adt.DDMSFacade;
+import com.motorola.studio.android.adt.ISerialNumbered;
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.devices.DevicesManager;
+import com.motorola.studio.android.remote.instance.RemoteDeviceInstance;
+
+/**
+ * Class that contains business methods and utilities.
+ */
+public class RemoteDeviceUtils
+{
+
+ /**
+ * Handle Remote Device connection.
+ *
+ * @param serialNumber the serial number of the connected device
+ */
+ public static void connectDevice(final String serialNumber)
+ {
+ PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable()
+ {
+ public void run()
+ {
+ /*
+ * Check if it's a remote device
+ */
+ if (DDMSFacade.isRemote(serialNumber))
+ {
+
+ ISerialNumbered instance =
+ DevicesManager.getInstance().getDeviceBySerialNumber(serialNumber);
+
+ boolean isTransitioning =
+ ((instance != null) ? ((AbstractMobileInstance) instance)
+ .getStateMachineHandler().isTransitioning() : false);
+
+ StudioLogger.debug("Handle remote device connected event. Serial Number: "
+ + serialNumber + " Instance: " + instance + " Transitioning: "
+ + isTransitioning);
+
+ /*
+ * If the instance exists and is transitioning, so skip this method, the
+ * connect handler will change the instance status
+ */
+ if ((instance == null) || ((instance != null) && (!isTransitioning)))
+ {
+
+ /*
+ * This method is necessary because sometimes (for example when the connection is refuses)
+ * the device appears in the adb devices list but it's not in the "online" state
+ */
+ boolean onlineDevice = waitForDeviceToBeOnline(serialNumber, instance);
+
+ if (onlineDevice)
+ {
+ /*
+ * If the device instance already exists
+ */
+ if (instance == null)
+ {
+
+ try
+ {
+
+ StudioLogger
+ .debug("Connecting Remote Device: device doesn't exist, create a new instance");
+
+ DevicesManager.getInstance().createInstanceForDevice(
+ serialNumber, RemoteDeviceConstants.DEVICE_ID,
+ getInstanceBuilder(serialNumber),
+ RemoteDeviceConstants.SERVICE_INIT_ID);
+ }
+ catch (SequoyahException e)
+ {
+ StudioLogger
+ .error("Connecting Remote Device: error while creating device instance "
+ + e.getMessage());
+ }
+ }
+
+ try
+ {
+ instance =
+ DevicesManager.getInstance().getDeviceBySerialNumber(
+ serialNumber);
+
+ StudioLogger
+ .debug("Connecting Remote Device: the TmL service will be called");
+
+ Map<Object, Object> arguments = new HashMap<Object, Object>();
+ arguments.put(RemoteDeviceConstants.DUMMY_TRANSITION, true);
+ RemoteDevicePlugin.getConnectServiceHandler().run(
+ (IInstance) instance, arguments);
+ }
+ catch (Exception e)
+ {
+ StudioLogger.error("Error when running TmL connect service: "
+ + e.getMessage());
+ }
+ }
+ }
+ }
+ }
+ });
+ }
+
+ /**
+ * Handle Remote Device disconnection
+ *
+ * @param serialNumber the serial number of the disconnected device
+ */
+ public static void disconnectDevice(String serialNumber)
+ {
+ if (DDMSFacade.isRemote(serialNumber))
+ {
+
+ ISerialNumbered instance =
+ DevicesManager.getInstance().getDeviceBySerialNumber(serialNumber);
+
+ StudioLogger.debug("Handle remote device disconnected event. Serial Number: "
+ + serialNumber + " Instance: " + instance);
+
+ if (instance != null)
+ {
+ Object volatileProperty =
+ ((RemoteDeviceInstance) instance).getProperties().get(
+ RemoteDeviceInstance.PROPERTY_VOLATILE);
+ boolean isVolatile =
+ ((volatileProperty != null) ? ((Boolean) volatileProperty).booleanValue()
+ : false);
+
+ if (!isVolatile)
+ {
+ try
+ {
+ StudioLogger
+ .debug("Disconnecting Remote Device: the device is NOT volatile, the TmL service will be called");
+
+ Map<Object, Object> arguments = new HashMap<Object, Object>();
+ arguments.put(RemoteDeviceConstants.DUMMY_TRANSITION, true);
+ RemoteDevicePlugin.getDisconnectServiceHandler().run((IInstance) instance,
+ arguments);
+ }
+ catch (Exception e)
+ {
+ StudioLogger.error("Error when running TmL disconnect service: "
+ + e.getMessage());
+ }
+ }
+ else
+ {
+ StudioLogger
+ .debug("Disconnecting Remote Device: the device is volatile, it will be deleted");
+ DevicesManager.getInstance().deleteInstanceOfDevice(serialNumber);
+ }
+
+ }
+
+ }
+
+ }
+
+ /*
+ * Wait until the device status becomes online
+ *
+ * @param serialNumber device serial number
+ * @param instance TmL instance, if it exists
+ * @return true if the device became online, false otherwise
+ */
+ private static boolean waitForDeviceToBeOnline(String serialNumber, ISerialNumbered instance)
+ {
+ StudioLogger.debug("Wait device to be online: " + serialNumber);
+
+ boolean instanceOnline = false;
+ long timeoutLimit = 0;
+
+ if (instance != null)
+ {
+ Properties prop = ((IInstance) instance).getProperties();
+ String timeout = prop.getProperty(RemoteDeviceInstance.PROPERTY_TIMEOUT);
+ timeoutLimit = System.currentTimeMillis() + (Integer.parseInt(timeout) * 1000);
+ }
+ else
+ {
+ timeoutLimit =
+ System.currentTimeMillis() + (RemoteDeviceConstants.DEFAULT_TIMEOUT * 1000);
+
+ }
+
+ while ((instanceOnline = DDMSFacade.isDeviceOnline(serialNumber)) == false)
+ {
+ try
+ {
+ Thread.sleep(1000);
+ }
+ catch (InterruptedException e)
+ {
+ StudioLogger.error("Wait for device to be online: thread has been interrupted");
+ }
+
+ try
+ {
+ testTimeout(timeoutLimit);
+ }
+ catch (TimeoutException e)
+ {
+ StudioLogger.warn("Timeout reached wile wating device to be online: "
+ + serialNumber);
+ break;
+ }
+
+ }
+ return instanceOnline;
+
+ }
+
+ /*
+ * Get the instance builder needed by TmL in order to create a new Remote Device instance
+ *
+ * @param serialNumber serial number of the Remote Device that shall be added
+ * @return the instance builder needed by TmL to create a new Remote Device instance
+ */
+ private static RemoteDeviceInstanceBuilder getInstanceBuilder(String serialNumber)
+ {
+
+ RemoteDeviceInstanceBuilder instanceBuilder = null;
+
+ String[] serialNumberParts = serialNumber.split(":");
+ String host = serialNumberParts[0];
+ String port = serialNumberParts[1];
+
+ Properties props = new Properties();
+ props.put(RemoteDeviceInstance.PROPERTY_HOST, host);
+ props.put(RemoteDeviceInstance.PROPERTY_PORT, port);
+ props.put(RemoteDeviceInstance.PROPERTY_TIMEOUT,
+ String.valueOf(RemoteDeviceConstants.DEFAULT_TIMEOUT));
+
+ // mark this instance as volatile
+ props.put(RemoteDeviceInstance.PROPERTY_VOLATILE, true);
+
+ instanceBuilder = new RemoteDeviceInstanceBuilder(serialNumber, props);
+
+ return instanceBuilder;
+ }
+
+ /*
+ * Compare the device instance with a pair host:port to check if the device
+ * has the same host:port
+ *
+ * @param device the device to be analyzed
+ * @param host host IP or name
+ * @param port port number
+ * @return true if the the device has the same host:port, false otherwise
+ */
+ public static boolean hasSameHostAndPort(ISerialNumbered device, String host, int port)
+ {
+ boolean returnValue = false;
+
+ String deviceHost =
+ ((RemoteDeviceInstance) device).getProperties().getProperty(
+ RemoteDeviceInstance.PROPERTY_HOST);
+ String devicePort =
+ ((RemoteDeviceInstance) device).getProperties().getProperty(
+ RemoteDeviceInstance.PROPERTY_PORT);
+
+ if ((host.equals(deviceHost)) && (String.valueOf(port).equals(devicePort)))
+ {
+ returnValue = true;
+ }
+
+ return returnValue;
+
+ }
+
+ /**
+ * Execute a command.
+ *
+ * @param cmd Array of strings holding the command to
+ * be executed.
+ *
+ * @return The {@link IStatus} of the command execution.
+ *
+ * @throws IOException Exception thrown in case there are problems
+ * executing the command.
+ */
+ public static IStatus executeCommand(String[] cmd) throws IOException
+ {
+ IStatus status = Status.OK_STATUS;
+
+ Runtime runtime = Runtime.getRuntime();
+ Process process = runtime.exec(cmd);
+
+ try
+ {
+ // wait for the command to finish its execution
+ process.waitFor();
+ }
+ catch (InterruptedException e)
+ {
+ StudioLogger.error(RemoteDeviceUtils.class, "Problems executing the command");
+ status =
+ new Status(IStatus.ERROR, RemoteDevicePlugin.PLUGIN_ID,
+ "Problems executing the command", e);
+ }
+ // in case the is a problem with the command execution, create an error status
+ if (process.exitValue() != 0)
+ {
+ StudioLogger.error(RemoteDeviceUtils.class, "The IP was not found");
+ status =
+ new Status(IStatus.ERROR, RemoteDevicePlugin.PLUGIN_ID, "The IP was not found");
+ }
+
+ return status;
+ }
+
+ /*
+ * Checks if the timeout limit has reached
+ *
+ * @param timeoutLimit The system time limit that cannot be overtaken, in milliseconds
+ * @throws StartTimeoutException When the system time limit is overtaken
+ */
+ private static void testTimeout(long timeoutLimit) throws TimeoutException
+ {
+ if (System.currentTimeMillis() > timeoutLimit)
+ {
+ throw new TimeoutException();
+ }
+ }
+}
diff --git a/src/plugins/remote.device/src/com/motorola/studio/android/remote/RemoteDeviceWorkbenchListener.java b/src/plugins/remote.device/src/com/motorola/studio/android/remote/RemoteDeviceWorkbenchListener.java
new file mode 100644
index 0000000..739da6a
--- /dev/null
+++ b/src/plugins/remote.device/src/com/motorola/studio/android/remote/RemoteDeviceWorkbenchListener.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.remote;
+
+import java.util.Collection;
+
+import org.eclipse.sequoyah.device.common.utilities.exception.SequoyahException;
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.IWorkbenchListener;
+
+import com.motorola.studio.android.adt.ISerialNumbered;
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.common.preferences.DialogWithToggleUtils;
+import com.motorola.studio.android.devices.DevicesManager;
+import com.motorola.studio.android.remote.i18n.RemoteDeviceNLS;
+import com.motorola.studio.android.remote.instance.RemoteDeviceInstance;
+
+/**
+ * Class to implement the IWorkbenchListener that will check if
+ * there are connected Remote Devices when the uses tries to close Studio.
+ * The user will be prompted whether he wants to disconnect them or not.
+ */
+public class RemoteDeviceWorkbenchListener implements IWorkbenchListener
+{
+
+ public void postShutdown(IWorkbench workbench)
+ {
+ // Nothing to do.
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.IWorkbenchListener#preShutdown(org.eclipse.ui.IWorkbench, boolean)
+ */
+ public boolean preShutdown(IWorkbench workbench, boolean forced)
+ {
+
+ Collection<ISerialNumbered> connectedDevices =
+ DevicesManager.getInstance().getOnlineDevicesByType(RemoteDeviceInstance.class);
+
+ if (connectedDevices.size() > 0)
+ {
+
+ boolean disconnectRemoteInstances =
+ DialogWithToggleUtils
+ .showQuestion(
+ RemoteDeviceConstants.DISCONNECT_ALL_REMOTE_DEVICES_IN_SHUTDOWN_KEY_PREFERENCE,
+ RemoteDeviceNLS.QUESTION_ConnectedRemoteDevicesOnClose_Title,
+ RemoteDeviceNLS.QUESTION_ConnectedRemoteDevicesOnClose_Text);
+
+ if (disconnectRemoteInstances)
+ {
+ for (ISerialNumbered device : connectedDevices)
+ {
+ try
+ {
+ RemoteDevicePlugin.getDisconnectServiceHandler().run(
+ (RemoteDeviceInstance) device);
+ }
+ catch (SequoyahException e)
+ {
+ StudioLogger
+ .error("Error when trying to disconnect Remote Devices on Studio shutdown: "
+ + e.getMessage());
+ }
+ }
+ }
+
+ }
+
+ return true;
+ }
+}
diff --git a/src/plugins/remote.device/src/com/motorola/studio/android/remote/handlers/ConnectToRemoteHandler.java b/src/plugins/remote.device/src/com/motorola/studio/android/remote/handlers/ConnectToRemoteHandler.java
new file mode 100644
index 0000000..0d5cdc9
--- /dev/null
+++ b/src/plugins/remote.device/src/com/motorola/studio/android/remote/handlers/ConnectToRemoteHandler.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.remote.handlers;
+
+import java.io.IOException;
+import java.util.Map;
+import java.util.Properties;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.sequoyah.device.framework.events.InstanceEvent;
+import org.eclipse.sequoyah.device.framework.events.InstanceEvent.InstanceEventType;
+import org.eclipse.sequoyah.device.framework.events.InstanceEventManager;
+import org.eclipse.sequoyah.device.framework.model.IInstance;
+import org.eclipse.sequoyah.device.framework.model.handler.IServiceHandler;
+import org.eclipse.sequoyah.device.framework.model.handler.ServiceHandler;
+
+import com.motorola.studio.android.adt.DDMSFacade;
+import com.motorola.studio.android.adt.ISerialNumbered;
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.remote.RemoteDeviceConstants;
+import com.motorola.studio.android.remote.RemoteDevicePlugin;
+import com.motorola.studio.android.remote.i18n.RemoteDeviceNLS;
+import com.motorola.studio.android.remote.instance.RemoteDeviceInstance;
+
+/**
+ * Service handler responsible for connecting to a remote device
+ */
+public class ConnectToRemoteHandler extends ServiceHandler
+{
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.sequoyah.device.framework.model.handler.ServiceHandler#newInstance()
+ */
+ @Override
+ public IServiceHandler newInstance()
+ {
+ return new ConnectToRemoteHandler();
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.sequoyah.device.framework.model.handler.ServiceHandler#runService(org.eclipse.sequoyah.device.framework.model.IInstance, java.util.Map, org.eclipse.core.runtime.IProgressMonitor)
+ */
+ @Override
+ public IStatus runService(IInstance instance, Map<Object, Object> arguments,
+ IProgressMonitor monitor)
+ {
+
+ StudioLogger
+ .debug("TmL Connect to Remote Device Service: start connecting to remote device: "
+ + instance);
+
+ if (arguments != null)
+ {
+ if (((Boolean) arguments.get(RemoteDeviceConstants.DUMMY_TRANSITION)).booleanValue())
+ {
+ StudioLogger.debug("TmL Connect to Remote Device Service: dummy transition");
+ setSuffix(instance);
+ return Status.OK_STATUS;
+ }
+ }
+
+ IStatus status = Status.OK_STATUS;
+
+ /*
+ * Call ADB connect
+ */
+ Properties prop = instance.getProperties();
+ String host = prop.getProperty(RemoteDeviceInstance.PROPERTY_HOST);
+ String port = prop.getProperty(RemoteDeviceInstance.PROPERTY_PORT);
+ String timeout = prop.getProperty(RemoteDeviceInstance.PROPERTY_TIMEOUT);
+
+ try
+ {
+ status =
+ DDMSFacade.connectTcpIp((ISerialNumbered) instance, host, port,
+ Integer.parseInt(timeout), monitor);
+ }
+ catch (IOException e)
+ {
+ return new Status(IStatus.ERROR, RemoteDevicePlugin.PLUGIN_ID,
+ RemoteDeviceNLS.ERR_ConnectToRemote_AdbStart);
+ }
+
+ /* ------------------------------------------------------------ */
+
+ if (status.getSeverity() == IStatus.OK)
+ {
+ setSuffix(instance);
+ }
+ else
+ {
+ instance.setNameSuffix(null);
+ InstanceEventManager.getInstance().notifyListeners(
+ new InstanceEvent(InstanceEventType.INSTANCE_UPDATED, instance));
+ }
+
+ StudioLogger
+ .debug("TmL Connect to Remote Device Service: finish connecting to remote device. status: "
+ + status.getSeverity());
+
+ return status;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.sequoyah.device.framework.model.handler.ServiceHandler#updatingService(org.eclipse.sequoyah.device.framework.model.IInstance, org.eclipse.core.runtime.IProgressMonitor)
+ */
+ @Override
+ public IStatus updatingService(IInstance instance, IProgressMonitor monitor)
+ {
+ return Status.OK_STATUS;
+ }
+
+ /*
+ * Set the instance suffix (its serial number)
+ *
+ * @param instance the instance to which the suffix will be added
+ */
+ private void setSuffix(IInstance instance)
+ {
+
+ if (instance != null)
+ {
+ StudioLogger.debug("TmL Connect to Remote Device Service: setting suffix to instance "
+ + instance.getName());
+
+ String suffix = ((ISerialNumbered) instance).getSerialNumber();
+ if (!instance.getName().equals(suffix))
+ {
+ instance.setNameSuffix(suffix);
+ InstanceEventManager.getInstance().notifyListeners(
+ new InstanceEvent(InstanceEventType.INSTANCE_UPDATED, instance));
+ }
+ }
+ }
+}
diff --git a/src/plugins/remote.device/src/com/motorola/studio/android/remote/handlers/DisconnectFromRemoteHandler.java b/src/plugins/remote.device/src/com/motorola/studio/android/remote/handlers/DisconnectFromRemoteHandler.java
new file mode 100644
index 0000000..fa516df
--- /dev/null
+++ b/src/plugins/remote.device/src/com/motorola/studio/android/remote/handlers/DisconnectFromRemoteHandler.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.remote.handlers;
+
+import java.io.IOException;
+import java.util.Map;
+import java.util.Properties;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.sequoyah.device.framework.events.InstanceEvent;
+import org.eclipse.sequoyah.device.framework.events.InstanceEvent.InstanceEventType;
+import org.eclipse.sequoyah.device.framework.events.InstanceEventManager;
+import org.eclipse.sequoyah.device.framework.model.IInstance;
+import org.eclipse.sequoyah.device.framework.model.handler.IServiceHandler;
+import org.eclipse.sequoyah.device.framework.model.handler.ServiceHandler;
+
+import com.motorola.studio.android.adt.DDMSFacade;
+import com.motorola.studio.android.adt.ISerialNumbered;
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.remote.RemoteDeviceConstants;
+import com.motorola.studio.android.remote.RemoteDevicePlugin;
+import com.motorola.studio.android.remote.i18n.RemoteDeviceNLS;
+import com.motorola.studio.android.remote.instance.RemoteDeviceInstance;
+
+/**
+ * Service handler responsible for disconnecting from a remote device.
+ */
+public class DisconnectFromRemoteHandler extends ServiceHandler
+{
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.sequoyah.device.framework.model.handler.ServiceHandler#newInstance()
+ */
+ @Override
+ public IServiceHandler newInstance()
+ {
+ return new DisconnectFromRemoteHandler();
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.sequoyah.device.framework.model.handler.ServiceHandler#runService(org.eclipse.sequoyah.device.framework.model.IInstance, java.util.Map, org.eclipse.core.runtime.IProgressMonitor)
+ */
+ @Override
+ public IStatus runService(IInstance instance, Map<Object, Object> arguments,
+ IProgressMonitor monitor)
+ {
+ StudioLogger
+ .debug("TmL Disconnect from Remote Device Service: start disconnecting from remote device: "
+ + instance);
+
+ if (arguments != null)
+ {
+ if (((Boolean) arguments.get(RemoteDeviceConstants.DUMMY_TRANSITION)).booleanValue())
+ {
+ StudioLogger.debug("TmL Disconnect from Remote Device Service: dummy transition");
+ setSuffix(instance);
+ return Status.OK_STATUS;
+ }
+ }
+
+ IStatus status = Status.OK_STATUS;
+
+ /*
+ * Call ADB disconnect
+ */
+ Properties prop = instance.getProperties();
+ String host = prop.getProperty(RemoteDeviceInstance.PROPERTY_HOST);
+ String port = prop.getProperty(RemoteDeviceInstance.PROPERTY_PORT);
+ String timeout = prop.getProperty(RemoteDeviceInstance.PROPERTY_TIMEOUT);
+
+ try
+ {
+ status =
+ DDMSFacade.disconnectTcpIp((ISerialNumbered) instance, host, port,
+ Integer.parseInt(timeout), monitor);
+ }
+ catch (IOException e)
+ {
+ return new Status(IStatus.ERROR, RemoteDevicePlugin.PLUGIN_ID,
+ RemoteDeviceNLS.ERR_DisconnectToRemote_AdbStart);
+ }
+
+ /* ------------------------------------------------------------ */
+
+ if (status.getSeverity() == IStatus.OK)
+ {
+ setSuffix(instance);
+ }
+
+ StudioLogger
+ .debug("TmL Disconnect from Remote Device Service: finish disconnecting from remote device. status: "
+ + status.getSeverity());
+
+ return status;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.sequoyah.device.framework.model.handler.ServiceHandler#updatingService(org.eclipse.sequoyah.device.framework.model.IInstance, org.eclipse.core.runtime.IProgressMonitor)
+ */
+ @Override
+ public IStatus updatingService(IInstance instance, IProgressMonitor monitor)
+ {
+ return Status.OK_STATUS;
+ }
+
+ /*
+ * Set the suffix to null - i.e. no suffix
+ *
+ * @param instance the instance from which the suffix will be removed
+ */
+ private void setSuffix(IInstance instance)
+ {
+ if (instance != null)
+ {
+ StudioLogger
+ .debug("TmL Disconnect from Remote Device Service: removing suffix from instance "
+ + instance.getName());
+ instance.setNameSuffix(null);
+ InstanceEventManager.getInstance().notifyListeners(
+ new InstanceEvent(InstanceEventType.INSTANCE_UPDATED, instance));
+ }
+ }
+
+}
diff --git a/src/plugins/remote.device/src/com/motorola/studio/android/remote/handlers/InitRemoteHandler.java b/src/plugins/remote.device/src/com/motorola/studio/android/remote/handlers/InitRemoteHandler.java
new file mode 100644
index 0000000..a46f17b
--- /dev/null
+++ b/src/plugins/remote.device/src/com/motorola/studio/android/remote/handlers/InitRemoteHandler.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.remote.handlers;
+
+import java.util.Map;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.sequoyah.device.framework.model.IInstance;
+import org.eclipse.sequoyah.device.framework.model.handler.IServiceHandler;
+import org.eclipse.sequoyah.device.framework.model.handler.ServiceHandler;
+
+/**
+ * Service handler responsible for initializing a remote device.
+ * Is simply change to status from OFF to the initial state (Disconnected).
+ */
+public class InitRemoteHandler extends ServiceHandler
+{
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.sequoyah.device.framework.model.handler.ServiceHandler#newInstance()
+ */
+ @Override
+ public IServiceHandler newInstance()
+ {
+ return new InitRemoteHandler();
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.sequoyah.device.framework.model.handler.ServiceHandler#runService(org.eclipse.sequoyah.device.framework.model.IInstance, java.util.Map, org.eclipse.core.runtime.IProgressMonitor)
+ */
+ @Override
+ public IStatus runService(IInstance instance, Map<Object, Object> arguments,
+ IProgressMonitor monitor)
+ {
+ // just let the status be changed
+ return Status.OK_STATUS;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.sequoyah.device.framework.model.handler.ServiceHandler#updatingService(org.eclipse.sequoyah.device.framework.model.IInstance, org.eclipse.core.runtime.IProgressMonitor)
+ */
+ @Override
+ public IStatus updatingService(IInstance instance, IProgressMonitor monitor)
+ {
+ return Status.OK_STATUS;
+ }
+}
diff --git a/src/plugins/remote.device/src/com/motorola/studio/android/remote/handlers/OpenNewRemoteDeviceWizardHandler.java b/src/plugins/remote.device/src/com/motorola/studio/android/remote/handlers/OpenNewRemoteDeviceWizardHandler.java
new file mode 100644
index 0000000..7521cd0
--- /dev/null
+++ b/src/plugins/remote.device/src/com/motorola/studio/android/remote/handlers/OpenNewRemoteDeviceWizardHandler.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.motorola.studio.android.remote.handlers;
+
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.jface.wizard.WizardDialog;
+import org.eclipse.sequoyah.device.framework.ui.wizard.NewDeviceMenuWizard;
+import org.eclipse.ui.PlatformUI;
+
+import com.motorola.studio.android.remote.RemoteDevicePlugin;
+
+public class OpenNewRemoteDeviceWizardHandler extends AbstractHandler
+{
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.core.commands.AbstractHandler#execute(org.eclipse.core.commands.ExecutionEvent)
+ */
+ public Object execute(ExecutionEvent event) throws ExecutionException
+ {
+ PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable()
+ {
+
+ public void run()
+ {
+ NewDeviceMenuWizard wizard = new NewDeviceMenuWizard();
+ wizard.setCurrentDeviceTypeId(RemoteDevicePlugin.DEVICE_ID);
+ WizardDialog dialog =
+ new WizardDialog(PlatformUI.getWorkbench().getActiveWorkbenchWindow()
+ .getShell(), wizard);
+ dialog.open();
+ }
+ });
+
+ return null;
+ }
+}
diff --git a/src/plugins/remote.device/src/com/motorola/studio/android/remote/handlers/RemoteDeviceHandler.java b/src/plugins/remote.device/src/com/motorola/studio/android/remote/handlers/RemoteDeviceHandler.java
new file mode 100644
index 0000000..2e8edbd
--- /dev/null
+++ b/src/plugins/remote.device/src/com/motorola/studio/android/remote/handlers/RemoteDeviceHandler.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.remote.handlers;
+
+import org.eclipse.sequoyah.device.framework.model.IDeviceLauncher;
+import org.eclipse.sequoyah.device.framework.model.IInstance;
+import org.eclipse.sequoyah.device.framework.model.handler.IDeviceHandler;
+
+import com.motorola.studio.android.remote.instance.RemoteDeviceInstance;
+
+/**
+ * This class represents a TmL IDeviceHandler for Android Remote Device Instances
+ */
+public class RemoteDeviceHandler implements IDeviceHandler
+{
+ /**
+ * Creates an Android Remote Device Instance with the given id
+ *
+ * @param id the instance id
+ */
+ public IInstance createDeviceInstance(String id)
+ {
+ IInstance instance = new RemoteDeviceInstance();
+ instance.setId(id);
+ return instance;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.sequoyah.device.framework.model.handler.IDeviceHandler#createDeviceLauncher(org.eclipse.sequoyah.device.framework.model.IInstance)
+ */
+ public IDeviceLauncher createDeviceLauncher(IInstance instance)
+ {
+ return null;
+ }
+}
diff --git a/src/plugins/remote.device/src/com/motorola/studio/android/remote/handlers/USBModeServiceHandler.java b/src/plugins/remote.device/src/com/motorola/studio/android/remote/handlers/USBModeServiceHandler.java
new file mode 100644
index 0000000..61bab7d
--- /dev/null
+++ b/src/plugins/remote.device/src/com/motorola/studio/android/remote/handlers/USBModeServiceHandler.java
@@ -0,0 +1,189 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.remote.handlers;
+
+import java.io.IOException;
+import java.util.Map;
+import java.util.Properties;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.SubMonitor;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.sequoyah.device.framework.model.IInstance;
+import org.eclipse.sequoyah.device.framework.model.IService;
+import org.eclipse.sequoyah.device.framework.model.handler.IServiceHandler;
+import org.eclipse.sequoyah.device.framework.model.handler.ServiceHandler;
+
+import com.motorola.studio.android.adt.DDMSFacade;
+import com.motorola.studio.android.adt.ISerialNumbered;
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.common.utilities.EclipseUtils;
+import com.motorola.studio.android.remote.RemoteDevicePlugin;
+import com.motorola.studio.android.remote.i18n.RemoteDeviceNLS;
+import com.motorola.studio.android.remote.instance.RemoteDeviceInstance;
+
+/**
+ * Handler which switches back a remote device from TCP/IP to USB mode.
+ */
+public class USBModeServiceHandler extends ServiceHandler
+{
+ // Min SDK version that supports tcpip connection mode
+ private static final int MIN_SDK_VERSION = 6;
+
+ /**
+ * Get the wireless service handler.
+ */
+ @Override
+ public IServiceHandler newInstance()
+ {
+ return new USBModeServiceHandler();
+ }
+
+ /**
+ * Get the phone IP, validate it and launch the Wireless wizard.
+ */
+ @Override
+ public IStatus runService(final IInstance instance, Map<Object, Object> arguments,
+ final IProgressMonitor monitor)
+ {
+
+ final SubMonitor subMonitor = SubMonitor.convert(monitor, 1000);
+ subMonitor.beginTask(
+ RemoteDeviceNLS.USBModeServiceHandler_MsgStartingProcessOfSwitchingToUSBMode, 1000);
+
+ final ISerialNumbered device = (ISerialNumbered) instance;
+
+ int deviceSdkVersion = -1;
+ try
+ {
+ deviceSdkVersion =
+ Integer.parseInt(DDMSFacade.getDeviceProperty(device.getSerialNumber(),
+ "ro.build.version.sdk")); //$NON-NLS-1$
+
+ subMonitor.worked(100);
+ }
+ catch (Exception e)
+ {
+ StudioLogger.error(USBModeServiceHandler.class,
+ RemoteDeviceNLS.USBModeServiceHandler_2, e);
+ }
+
+ // if it was not possible to retrieve the sdk version
+ // try to execute the service anyway
+ if ((!subMonitor.isCanceled()) && (deviceSdkVersion < MIN_SDK_VERSION)
+ && (deviceSdkVersion != -1))
+ {
+ EclipseUtils.showErrorDialog(RemoteDeviceNLS.Title_ReturningToUSBConnectionDialog,
+ RemoteDeviceNLS.USBModeServiceHandler_MsgUnableToSwitchToUSBDueToSDKVersion);
+ }
+ else
+ {
+
+ if (!subMonitor.isCanceled())
+ {
+
+ subMonitor
+ .setTaskName(RemoteDeviceNLS.WirelessServiceHandler_MsgRetrievingDeviceIPNumber);
+
+ // retrieve the IP, Port and timeout and validate it
+ Properties properties = instance.getProperties();
+ String host = properties.getProperty(RemoteDeviceInstance.PROPERTY_HOST);
+ String port = properties.getProperty(RemoteDeviceInstance.PROPERTY_PORT);
+ String timeout = properties.getProperty(RemoteDeviceInstance.PROPERTY_TIMEOUT);
+
+ subMonitor.worked(600);
+ if (host == null)
+ {
+ EclipseUtils.showErrorDialog(
+ RemoteDeviceNLS.Title_ReturningToUSBConnectionDialog,
+ RemoteDeviceNLS.ERR_WirelessWizard_No_IP);
+ }
+
+ else
+ {
+ if (!subMonitor.isCanceled())
+ {
+ subMonitor
+ .setTaskName(RemoteDeviceNLS.USBModeServiceHandler_MsgSwithcingTCPToUSB);
+ // switch the device from connection mode from TCP/IP to USB
+ try
+ {
+ IStatus status =
+ DDMSFacade.switchFromTCPConnectionModeToUSBConnectionMode(
+ device, host, port, Integer.parseInt(timeout),
+ subMonitor.newChild(300));
+
+ // in case the status is not OK, show an error message
+ if ((status != null) && (status.getSeverity() == IStatus.ERROR))
+ {
+ EclipseUtils
+ .showErrorDialog(
+ RemoteDeviceNLS.Title_ReturningToUSBConnectionDialog,
+ NLS.bind(
+ RemoteDeviceNLS.USBModeServiceHandler_MsgItWasNotPossibleToSwitchDeviceToUSBMode,
+ host), status);
+ }
+ else
+ {
+ // show a success message
+ EclipseUtils.showInformationDialog(
+ RemoteDeviceNLS.Title_ReturningToUSBConnectionDialog,
+ NLS.bind(RemoteDeviceNLS.USBModeServiceHandler_MsgSuccess,
+ host));
+ }
+ }
+ catch (IOException e)
+ {
+ StudioLogger
+ .error(this.getClass(),
+ NLS.bind(
+ "It was not possible to switch the android device {0} connection mode to USB.", //$NON-NLS-1$
+ device.getDeviceName()), e);
+ EclipseUtils
+ .showErrorDialog(
+ RemoteDeviceNLS.Title_ReturningToUSBConnectionDialog,
+ NLS.bind(
+ RemoteDeviceNLS.USBModeServiceHandler_MsgItWasNotPossibleToSwitchToUSBMode,
+ device.getDeviceName()));
+ }
+ }
+ }
+ }
+ }
+ return Status.OK_STATUS;
+ }
+
+ /**
+ * Simply Return an OK Status.
+ */
+ @Override
+ public IStatus updatingService(IInstance arg0, IProgressMonitor arg1)
+ {
+ return Status.OK_STATUS;
+ }
+
+ @Override
+ public void setService(IService service)
+ {
+ super.setService(service);
+ if (service != null)
+ {
+ service.setVisible(RemoteDevicePlugin.isWifiServiceEnabled());
+ }
+ }
+}
diff --git a/src/plugins/remote.device/src/com/motorola/studio/android/remote/handlers/WirelessServiceHandler.java b/src/plugins/remote.device/src/com/motorola/studio/android/remote/handlers/WirelessServiceHandler.java
new file mode 100644
index 0000000..294353d
--- /dev/null
+++ b/src/plugins/remote.device/src/com/motorola/studio/android/remote/handlers/WirelessServiceHandler.java
@@ -0,0 +1,206 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.remote.handlers;
+
+import java.net.InetAddress;
+import java.util.Map;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.SubMonitor;
+import org.eclipse.jface.wizard.WizardDialog;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.sequoyah.device.framework.model.IInstance;
+import org.eclipse.sequoyah.device.framework.model.IService;
+import org.eclipse.sequoyah.device.framework.model.handler.IServiceHandler;
+import org.eclipse.sequoyah.device.framework.model.handler.ServiceHandler;
+import org.eclipse.ui.PlatformUI;
+
+import com.motorola.studio.android.adt.DDMSFacade;
+import com.motorola.studio.android.adt.ISerialNumbered;
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.common.utilities.EclipseUtils;
+import com.motorola.studio.android.remote.RemoteDevicePlugin;
+import com.motorola.studio.android.remote.i18n.RemoteDeviceNLS;
+import com.motorola.studio.android.remote.ui.wireless.WirelessWizard;
+
+/**
+ * Handler which launches the Over the Air wizard.
+ */
+public class WirelessServiceHandler extends ServiceHandler
+{
+ private static final int TIMEOUT_REACH_IP = 30000;
+
+ // Min SDK version that supports tcpip connection mode
+ private static final int MIN_SDK_VERSION = 6;
+
+ /**
+ * Get the wireless service handler.
+ */
+ @Override
+ public IServiceHandler newInstance()
+ {
+ return new WirelessServiceHandler();
+ }
+
+ /**
+ * Get the phone IP, validate it and launch the Wireless wizard.
+ */
+ @Override
+ public IStatus runService(final IInstance instance, Map<Object, Object> arguments,
+ final IProgressMonitor monitor)
+ {
+
+ final SubMonitor subMonitor = SubMonitor.convert(monitor, 1000);
+ subMonitor.beginTask(RemoteDeviceNLS.WirelessServiceHandler_MsgLaunchingWirelessConnection,
+ 1000);
+
+ final ISerialNumbered device = (ISerialNumbered) instance;
+
+ int deviceSdkVersion = -1;
+ try
+ {
+ deviceSdkVersion =
+ Integer.parseInt(DDMSFacade.getDeviceProperty(device.getSerialNumber(),
+ "ro.build.version.sdk"));
+
+ subMonitor.worked(100);
+ }
+ catch (Exception e)
+ {
+ StudioLogger.error(WirelessServiceHandler.class,
+ "Problems trying to retrieve handset's sdk version.", e);
+ }
+
+ // if it was not possible to retrieve the sdk version
+ // try to execute the service anyway
+ if ((!subMonitor.isCanceled()) && (deviceSdkVersion < MIN_SDK_VERSION)
+ && (deviceSdkVersion != -1))
+ {
+ EclipseUtils.showErrorDialog(
+ RemoteDeviceNLS.WirelessWizard_TitleWirelessConnectionModeWizard,
+ RemoteDeviceNLS.ERR_WirelessWizard_NOT_VALID_SDK);
+ }
+ else
+ {
+
+ if (!subMonitor.isCanceled())
+ {
+
+ subMonitor
+ .setTaskName(RemoteDeviceNLS.WirelessServiceHandler_MsgRetrievingDeviceIPNumber);
+
+ // retrieve the IP and validate it
+ final String host =
+ DDMSFacade.getWirelessIPfromHandset(device.getSerialNumber(), monitor);
+
+ subMonitor.worked(300);
+ if (host == null)
+ {
+ EclipseUtils.showErrorDialog(
+ RemoteDeviceNLS.WirelessWizard_TitleWirelessConnectionModeWizard,
+ RemoteDeviceNLS.ERR_WirelessWizard_No_IP);
+ }
+ else
+ {
+ if (!subMonitor.isCanceled())
+ {
+ // check whether the IP can be reached
+ subMonitor
+ .setTaskName(RemoteDeviceNLS.WirelessServiceHandler_MsgPingingIPAddress);
+ InetAddress ipAddress = null;
+ boolean canReachIPAddress = true;
+ if (!subMonitor.isCanceled())
+ {
+ try
+ {
+ ipAddress = InetAddress.getByName(host);
+ canReachIPAddress =
+ (ipAddress != null)
+ && ipAddress.isReachable(TIMEOUT_REACH_IP);
+ subMonitor.worked(200);
+ }
+ catch (Exception e)
+ {
+ canReachIPAddress = false;
+ StudioLogger
+ .error(this.getClass(), NLS.bind(
+ RemoteDeviceNLS.ERR_WirelessWizard_Reach_IP, host),
+ e);
+ }
+
+ if (!canReachIPAddress)
+ {
+ EclipseUtils
+ .showErrorDialog(
+ RemoteDeviceNLS.WirelessWizard_TitleWirelessConnectionModeWizard,
+ NLS.bind(
+ RemoteDeviceNLS.ERR_WirelessWizard_Reach_IP,
+ host));
+ }
+ else
+ {
+ if (!subMonitor.isCanceled())
+ {
+ // launch the wireless wizard
+ PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable()
+ {
+ public void run()
+ {
+ subMonitor.worked(400);
+ WirelessWizard wizard = new WirelessWizard();
+ wizard.setInstance(device);
+ //wizard.setIp("192.168.16.2");
+ wizard.setIp(host);
+ wizard.setProgressMonitor(monitor);
+ WizardDialog dialog =
+ new WizardDialog(PlatformUI.getWorkbench()
+ .getActiveWorkbenchWindow().getShell(),
+ wizard);
+ dialog.open();
+ }
+ });
+ }
+
+ }
+ }
+ }
+ }
+ }
+ }
+ return Status.OK_STATUS;
+ }
+
+ /**
+ * Simply Return an OK Status.
+ */
+ @Override
+ public IStatus updatingService(IInstance arg0, IProgressMonitor arg1)
+ {
+ return Status.OK_STATUS;
+ }
+
+ @Override
+ public void setService(IService service)
+ {
+ super.setService(service);
+ if (service != null)
+ {
+ service.setVisible(RemoteDevicePlugin.isWifiServiceEnabled());
+ }
+ }
+}
diff --git a/src/plugins/remote.device/src/com/motorola/studio/android/remote/i18n/RemoteDeviceNLS.java b/src/plugins/remote.device/src/com/motorola/studio/android/remote/i18n/RemoteDeviceNLS.java
new file mode 100644
index 0000000..999d9d6
--- /dev/null
+++ b/src/plugins/remote.device/src/com/motorola/studio/android/remote/i18n/RemoteDeviceNLS.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.remote.i18n;
+
+import org.eclipse.osgi.util.NLS;
+
+/**
+ * This class is the NLS component for this plug-in.
+ */
+public class RemoteDeviceNLS extends NLS
+{
+ /**
+ * The bundle location.
+ * It refers to messages.properties file inside this package
+ */
+
+ static
+ {
+ NLS.initializeMessages("com.motorola.studio.android.remote.i18n.remoteDeviceNLS",
+ RemoteDeviceNLS.class);
+ }
+
+ public static String UI_Host;
+
+ public static String UI_Name;
+
+ public static String UI_Timeout;
+
+ public static String UI_RemoteDeviceWizardPage_WizardName;
+
+ public static String UI_RemoteDeviceWizardPage_Title;
+
+ public static String UI_RemoteDeviceWizardPage_Description;
+
+ public static String UI_WirelessWizard_Name;
+
+ public static String UI_WirelessInformationPage_Title;
+
+ public static String UI_WirelessInformationPage_Description;
+
+ public static String ERR_WirelessDeviceWizardPage_Name;
+
+ public static String ERR_RemoteDeviceWizardPage_IP;
+
+ public static String ERR_RemoteDeviceWizardPage_Port;
+
+ public static String ERR_RemoteDeviceWizardPage_Timeout;
+
+ public static String ERR_RemoteDeviceWizardPage_Duplicated;
+
+ public static String QUESTION_ConnectedRemoteDevicesOnClose_Title;
+
+ public static String QUESTION_ConnectedRemoteDevicesOnClose_Text;
+
+ public static String ERR_ConnectToRemote_AdbStart;
+
+ public static String ERR_DisconnectToRemote_AdbStart;
+
+ public static String SwitchFromUSBAndConnectToWirelessRunnable_CreatingRemoteDeviceInstance;
+
+ public static String SwitchFromUSBAndConnectToWirelessRunnable_MsgCreatingWirelessRemoteDevice;
+
+ public static String SwitchFromUSBAndConnectToWirelessRunnable_MsgNotPossibleToConvertUSBToTCPIP;
+
+ public static String SwitchFromUSBAndConnectToWirelessRunnable_ConnectingToWifiDevice;
+
+ public static String WirelessPropertiesComposite_MsgPortNumberEqualOrHigherThan;
+
+ public static String WirelessServiceHandler_MsgLaunchingWirelessConnection;
+
+ public static String WirelessServiceHandler_MsgPingingIPAddress;
+
+ public static String WirelessServiceHandler_MsgRetrievingDeviceIPNumber;
+
+ public static String WirelessWizard_MsgErrorProblemsSwitchingDeviceToTCPIP;
+
+ public static String WirelessWizard_TitleWirelessConnectionModeWizard;
+
+ public static String WirelessWizard_WirelessDeviceCreatedSuccessfully;
+
+ public static String ERR_WirelessWizard_Reach_IP;
+
+ public static String ERR_WirelessWizard_No_IP;
+
+ public static String ERR_WirelessWizard_NOT_VALID_SDK;
+
+ public static String UI_Port;
+
+ public static String USBModeServiceHandler_2;
+
+ public static String USBModeServiceHandler_MsgItWasNotPossibleToSwitchDeviceToUSBMode;
+
+ public static String USBModeServiceHandler_MsgItWasNotPossibleToSwitchToUSBMode;
+
+ public static String USBModeServiceHandler_MsgStartingProcessOfSwitchingToUSBMode;
+
+ public static String USBModeServiceHandler_MsgSuccess;
+
+ public static String USBModeServiceHandler_MsgSwithcingTCPToUSB;
+
+ public static String USBModeServiceHandler_MsgUnableToSwitchToUSBDueToSDKVersion;
+
+ public static String ERR_RemoteDeviceWizardPage_WirelessDuplicated;
+
+ public static String Title_ReturningToUSBConnectionDialog;
+}
diff --git a/src/plugins/remote.device/src/com/motorola/studio/android/remote/i18n/remoteDeviceNLS.properties b/src/plugins/remote.device/src/com/motorola/studio/android/remote/i18n/remoteDeviceNLS.properties
new file mode 100644
index 0000000..dff808f
--- /dev/null
+++ b/src/plugins/remote.device/src/com/motorola/studio/android/remote/i18n/remoteDeviceNLS.properties
@@ -0,0 +1,62 @@
+#
+# Copyright (C) 2012 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+UI_Name=Wireless Device Name:
+UI_Host=Host:
+UI_Port=Port:
+UI_Timeout=Timeout (sec):
+
+UI_RemoteDeviceWizardPage_WizardName=New Android Remote Device Wizard
+UI_RemoteDeviceWizardPage_Title=New Android Remote Device
+UI_RemoteDeviceWizardPage_Description=Enter Android remote device instance information
+
+UI_WirelessWizard_Name=Switch Connection Mode Wizard
+UI_WirelessInformationPage_Title=Switch to TCP/IP connection mode
+UI_WirelessInformationPage_Description=Check the information provided below
+USBModeServiceHandler_2=Problems trying to retrieve handset's OS version.
+USBModeServiceHandler_MsgItWasNotPossibleToSwitchDeviceToUSBMode=It was not possible to switch the device {0} connection mode to USB.
+USBModeServiceHandler_MsgItWasNotPossibleToSwitchToUSBMode=It was not possible to switch the android device {0} connection mode to USB.
+USBModeServiceHandler_MsgStartingProcessOfSwitchingToUSBMode=Starting process of switching to USB connection mode...
+USBModeServiceHandler_MsgSuccess=The remote device {0} had its connection mode switched to USB. Plug the device in to access it.
+USBModeServiceHandler_MsgSwithcingTCPToUSB=Switching remote device connection mode from TCP/IP to USB...
+USBModeServiceHandler_MsgUnableToSwitchToUSBDueToSDKVersion=Unable to switch to USB connection mode on handsets with Android OS version lower than 2.1.
+
+ERR_WirelessDeviceWizardPage_Name=Invalid device name. The name cannot be empty.
+ERR_RemoteDeviceWizardPage_IP=Invalid IP. Enter an IP address in the format XXX.XXX.XXX.XXX.
+ERR_RemoteDeviceWizardPage_Port=Invalid port number. Enter a positive integer.
+ERR_RemoteDeviceWizardPage_Timeout=Invalid timeout value. Enter a positive integer.
+ERR_RemoteDeviceWizardPage_Duplicated=Duplicate entry. The "{0}" Android remote device has the same host and port.
+ERR_RemoteDeviceWizardPage_WirelessDuplicated=The "{0}" remote device has the same host and port as those entered here. If you click Finish this remote device will be used for the wireless connection.
+QUESTION_ConnectedRemoteDevicesOnClose_Title=Connected Android Virtual Devices
+QUESTION_ConnectedRemoteDevicesOnClose_Text=There are connected Android remote devices. Do you want to disconnect them?
+
+ERR_ConnectToRemote_AdbStart=Could not start ADB connect command
+ERR_DisconnectToRemote_AdbStart=Could not start ADB disconnect command
+SwitchFromUSBAndConnectToWirelessRunnable_ConnectingToWifiDevice=Trying to connect to the device via wireless.
+SwitchFromUSBAndConnectToWirelessRunnable_CreatingRemoteDeviceInstance=Creating remote device instance...
+SwitchFromUSBAndConnectToWirelessRunnable_MsgCreatingWirelessRemoteDevice=Creating wireless remote device...
+SwitchFromUSBAndConnectToWirelessRunnable_MsgNotPossibleToConvertUSBToTCPIP=It was not possible to switch the device connection mode from USB to TCP/IP.
+WirelessPropertiesComposite_MsgPortNumberEqualOrHigherThan=The port number must be equal to or greater than {0} and equal to or lesser than {1}.
+WirelessServiceHandler_MsgLaunchingWirelessConnection=Launching Wireless Connection Mode wizard...
+WirelessServiceHandler_MsgPingingIPAddress=Trying to reach IP address...
+WirelessServiceHandler_MsgRetrievingDeviceIPNumber=Retrieving wireless IP address from the device...
+WirelessWizard_MsgErrorProblemsSwitchingDeviceToTCPIP=Problems switching connection mode to TCP/IP. This may have happened due to the following reasons:\n\n - The wireless connection in your computer or device may be offline.\n - You probably need root permissions to execute this operation on your device.\n - The configured port may already be in use.\n - Your network may be slow: increase the timeout value.
+WirelessWizard_TitleWirelessConnectionModeWizard=Wireless Connection Mode Wizard
+WirelessWizard_WirelessDeviceCreatedSuccessfully=The device connection mode was successfully switched to TCP/IP. Unplug the device from the USB port and use the newly created remote device.
+ERR_WirelessWizard_Reach_IP=Unable to reach the IP address {0}.
+ERR_WirelessWizard_No_IP=Unable to retrieve the wireless IP address from your device. Check its wireless configurations.
+ERR_WirelessWizard_NOT_VALID_SDK=It is not possible to switch to TCP/IP connection mode on handsets with OS version lower than 2.1.
+Title_ReturningToUSBConnectionDialog=USB Connection mode \ No newline at end of file
diff --git a/src/plugins/remote.device/src/com/motorola/studio/android/remote/instance/RemoteDeviceInstance.java b/src/plugins/remote.device/src/com/motorola/studio/android/remote/instance/RemoteDeviceInstance.java
new file mode 100644
index 0000000..320df65
--- /dev/null
+++ b/src/plugins/remote.device/src/com/motorola/studio/android/remote/instance/RemoteDeviceInstance.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.remote.instance;
+
+import java.util.Collection;
+import java.util.Properties;
+
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.sequoyah.device.framework.model.AbstractMobileInstance;
+import org.eclipse.ui.model.IWorkbenchAdapter;
+
+import com.motorola.studio.android.adt.DDMSFacade;
+import com.motorola.studio.android.adt.ISerialNumbered;
+import com.motorola.studio.android.remote.RemoteDevicePlugin;
+
+/**
+ * This class represents a Android Remote Device instance
+ */
+public class RemoteDeviceInstance extends AbstractMobileInstance implements ISerialNumbered,
+ IWorkbenchAdapter
+{
+
+ public static final String PROPERTY_HOST = RemoteDevicePlugin.PLUGIN_ID + ".hostProperty";
+
+ public static final String PROPERTY_PORT = RemoteDevicePlugin.PLUGIN_ID + ".portProperty";
+
+ public static final String PROPERTY_TIMEOUT = RemoteDevicePlugin.PLUGIN_ID + ".timeoutProperty";
+
+ /**
+ * Property used to mark if the device shall be removed from the list when it gets disconnected.
+ */
+ public static final String PROPERTY_VOLATILE = RemoteDevicePlugin.PLUGIN_ID
+ + ".volatileProperty";
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.adt.ISerialNumbered#getSerialNumber()
+ */
+ public String getSerialNumber()
+ {
+ String serialNumber = null;
+ Properties prop = getProperties();
+ if (prop != null)
+ {
+ String host = prop.getProperty(PROPERTY_HOST);
+ String port = prop.getProperty(PROPERTY_PORT);
+ String candidateSerial = host + ":" + port;
+
+ Collection<String> allSerialNumbers = DDMSFacade.getConnectedSerialNumbers();
+ if (allSerialNumbers.contains(candidateSerial))
+ {
+ serialNumber = candidateSerial;
+ }
+ }
+ return serialNumber;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.adt.ISerialNumbered#getFullName()
+ */
+ public String getFullName()
+ {
+ if (getNameSuffix() != null)
+ {
+ return getName() + " (" + getNameSuffix() + ")";
+ }
+ else
+ {
+ return getName();
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.adt.ISerialNumbered#getDeviceName()
+ */
+ public String getDeviceName()
+ {
+ return getName();
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.sequoyah.device.framework.model.AbstractMobileInstance#toString()
+ */
+ @Override
+ public String toString()
+ {
+ return getName();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.model.IWorkbenchAdapter#getChildren(java.lang.Object)
+ */
+ public Object[] getChildren(Object o)
+ {
+ return new Object[0];
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.model.IWorkbenchAdapter#getImageDescriptor(java.lang.Object)
+ */
+ public ImageDescriptor getImageDescriptor(Object object)
+ {
+ return null;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.model.IWorkbenchAdapter#getParent(java.lang.Object)
+ */
+ public Object getParent(Object o)
+ {
+ return null;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.model.IWorkbenchAdapter#getLabel(java.lang.Object)
+ */
+ public String getLabel(Object o)
+ {
+ return getName();
+ }
+
+}
diff --git a/src/plugins/remote.device/src/com/motorola/studio/android/remote/ui/RemoteDevicePropertiesPage.java b/src/plugins/remote.device/src/com/motorola/studio/android/remote/ui/RemoteDevicePropertiesPage.java
new file mode 100644
index 0000000..eb60704
--- /dev/null
+++ b/src/plugins/remote.device/src/com/motorola/studio/android/remote/ui/RemoteDevicePropertiesPage.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.remote.ui;
+
+import java.util.Properties;
+
+import org.eclipse.core.runtime.IAdaptable;
+import org.eclipse.sequoyah.device.framework.model.IInstance;
+import org.eclipse.swt.events.DisposeEvent;
+import org.eclipse.swt.events.DisposeListener;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.ui.IWorkbenchPropertyPage;
+import org.eclipse.ui.dialogs.PropertyPage;
+
+import com.motorola.studio.android.adt.ISerialNumbered;
+import com.motorola.studio.android.remote.instance.RemoteDeviceInstance;
+import com.motorola.studio.android.remote.ui.RemotePropertiesComposite.RemotePropertiesChangedListener;
+
+/**
+ * Property page for Android Remote Devices.
+ */
+public class RemoteDevicePropertiesPage extends PropertyPage implements
+ RemotePropertiesChangedListener, IWorkbenchPropertyPage
+{
+
+ private RemotePropertiesComposite composite;
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.preference.PreferencePage#createContents(org.eclipse.swt.widgets.Composite)
+ */
+ @Override
+ protected Control createContents(Composite parent)
+ {
+ String host = "";
+ String port = "";
+ String timeout = "";
+
+ IInstance instance = null;
+
+ IAdaptable adaptable = getElement();
+ if (adaptable instanceof IInstance)
+ {
+ instance = (IInstance) adaptable;
+ Properties prop = instance.getProperties();
+ String propHost = prop.getProperty(RemoteDeviceInstance.PROPERTY_HOST);
+ String propPort = prop.getProperty(RemoteDeviceInstance.PROPERTY_PORT);
+ String propTimeout = prop.getProperty(RemoteDeviceInstance.PROPERTY_TIMEOUT);
+ host = (propHost != null) ? propHost : "";
+ port = (propPort != null) ? propPort : "";
+ timeout = (propTimeout != null) ? propTimeout : "";
+ }
+
+ composite =
+ new RemotePropertiesComposite(parent, host, port, timeout,
+ (ISerialNumbered) instance);
+ composite.addPropertyChangeListener(this);
+ composite.addDisposeListener(new DisposeListener()
+ {
+ public void widgetDisposed(DisposeEvent e)
+ {
+ composite.removePropertyChangeListener(RemoteDevicePropertiesPage.this);
+ }
+ });
+
+ setErrorMessage(composite.getErrorMessage());
+ noDefaultAndApplyButton();
+
+ return composite;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.remote.ui.RemotePropertiesComposite.RemotePropertiesChangedListener#propertiesChanged()
+ */
+ public void propertiesChanged()
+ {
+ setErrorMessage(composite.getErrorMessage());
+ setValid(isValid());
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.preference.PreferencePage#performOk()
+ */
+ @Override
+ public boolean performOk()
+ {
+ String host = composite.getHost();
+ int port = composite.getPort();
+ int timeout = composite.getTimeout();
+
+ IAdaptable adaptable = getElement();
+ if (adaptable instanceof IInstance)
+ {
+ IInstance instance = (IInstance) adaptable;
+ Properties prop = instance.getProperties();
+ prop.setProperty(RemoteDeviceInstance.PROPERTY_HOST, host);
+ prop.setProperty(RemoteDeviceInstance.PROPERTY_PORT, Integer.toString(port));
+ prop.setProperty(RemoteDeviceInstance.PROPERTY_TIMEOUT, Integer.toString(timeout));
+ }
+
+ return super.performOk();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.preference.PreferencePage#isValid()
+ */
+ @Override
+ public boolean isValid()
+ {
+ return (composite.getErrorMessage() == null);
+ }
+
+}
diff --git a/src/plugins/remote.device/src/com/motorola/studio/android/remote/ui/RemoteDeviceWizardPage.java b/src/plugins/remote.device/src/com/motorola/studio/android/remote/ui/RemoteDeviceWizardPage.java
new file mode 100644
index 0000000..b60b27c
--- /dev/null
+++ b/src/plugins/remote.device/src/com/motorola/studio/android/remote/ui/RemoteDeviceWizardPage.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.remote.ui;
+
+import java.util.Properties;
+
+import org.eclipse.jface.wizard.WizardPage;
+import org.eclipse.sequoyah.device.framework.ui.wizard.IInstanceProperties;
+import org.eclipse.swt.events.DisposeEvent;
+import org.eclipse.swt.events.DisposeListener;
+import org.eclipse.swt.widgets.Composite;
+
+import com.motorola.studio.android.remote.i18n.RemoteDeviceNLS;
+import com.motorola.studio.android.remote.instance.RemoteDeviceInstance;
+import com.motorola.studio.android.remote.ui.RemotePropertiesComposite.RemotePropertiesChangedListener;
+
+/**
+ * Wizard Page to be used by TmL to create a new Device Remove Instance.
+ */
+public class RemoteDeviceWizardPage extends WizardPage implements IInstanceProperties,
+ RemotePropertiesChangedListener
+{
+ private RemotePropertiesComposite composite;
+
+ /**
+ * Creates a RemoteDeviceWizardPage object.
+ */
+ public RemoteDeviceWizardPage()
+ {
+ super(RemoteDeviceNLS.UI_RemoteDeviceWizardPage_WizardName);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.sequoyah.device.framework.ui.wizard.IInstanceProperties#getProperties()
+ */
+ public Properties getProperties()
+ {
+ Properties props = new Properties();
+ props.put(RemoteDeviceInstance.PROPERTY_HOST, composite.getHost());
+ props.put(RemoteDeviceInstance.PROPERTY_PORT, Integer.toString(composite.getPort()));
+ props.put(RemoteDeviceInstance.PROPERTY_TIMEOUT, Integer.toString(composite.getTimeout()));
+ return props;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.dialogs.IDialogPage#createControl(org.eclipse.swt.widgets.Composite)
+ */
+ public void createControl(Composite parent)
+ {
+ setTitle(RemoteDeviceNLS.UI_RemoteDeviceWizardPage_Title);
+ setMessage(RemoteDeviceNLS.UI_RemoteDeviceWizardPage_Description);
+
+ composite = new RemotePropertiesComposite(parent);
+ composite.addPropertyChangeListener(this);
+ composite.addDisposeListener(new DisposeListener()
+ {
+ public void widgetDisposed(DisposeEvent e)
+ {
+ composite.removePropertyChangeListener(RemoteDeviceWizardPage.this);
+ composite = null;
+ RemoteDeviceWizardPage.this.setControl(null);
+ }
+ });
+
+ setPageComplete(composite.getErrorMessage() == null);
+ setControl(composite);
+
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.wizard.WizardPage#isPageComplete()
+ */
+ @Override
+ public boolean isPageComplete()
+ {
+ return (composite != null) && (composite.getErrorMessage() == null);
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.remote.ui.RemotePropertiesComposite.RemotePropertiesChangedListener#propertiesChanged()
+ */
+ public void propertiesChanged()
+ {
+ setErrorMessage(composite.getErrorMessage());
+ setPageComplete(isPageComplete());
+ }
+}
diff --git a/src/plugins/remote.device/src/com/motorola/studio/android/remote/ui/RemotePropertiesComposite.java b/src/plugins/remote.device/src/com/motorola/studio/android/remote/ui/RemotePropertiesComposite.java
new file mode 100644
index 0000000..f6d285f
--- /dev/null
+++ b/src/plugins/remote.device/src/com/motorola/studio/android/remote/ui/RemotePropertiesComposite.java
@@ -0,0 +1,340 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.remote.ui;
+
+import java.util.Collection;
+import java.util.LinkedHashSet;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.ModifyEvent;
+import org.eclipse.swt.events.ModifyListener;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.ui.PlatformUI;
+
+import com.motorola.studio.android.adt.ISerialNumbered;
+import com.motorola.studio.android.devices.DevicesManager;
+import com.motorola.studio.android.remote.RemoteDeviceConstants;
+import com.motorola.studio.android.remote.RemoteDeviceUtils;
+import com.motorola.studio.android.remote.i18n.RemoteDeviceNLS;
+import com.motorola.studio.android.remote.instance.RemoteDeviceInstance;
+
+/**
+ * Composite containing the Android Remote Device properties for edition
+ */
+public class RemotePropertiesComposite extends Composite
+{
+
+ private String host;
+
+ private int port;
+
+ private int timeout;
+
+ // DEVICE - if it already exist
+ ISerialNumbered device;
+
+ private final Collection<RemotePropertiesChangedListener> listeners =
+ new LinkedHashSet<RemotePropertiesChangedListener>();
+
+ // IP validation
+ private static final String ZERO_TO_255_PATTERN =
+ "((\\d)|(\\d\\d)|([0-1]\\d\\d)|(2[0-4]\\d)|(25[0-5]))";
+
+ private static final String IP_PATTERN = ZERO_TO_255_PATTERN + "\\." + ZERO_TO_255_PATTERN
+ + "\\." + ZERO_TO_255_PATTERN + "\\." + ZERO_TO_255_PATTERN;
+
+ // Error messages
+ private static final String HOST_ERR_MESSAGE = RemoteDeviceNLS.ERR_RemoteDeviceWizardPage_IP;
+
+ private static final String PORT_ERR_MESSAGE = RemoteDeviceNLS.ERR_RemoteDeviceWizardPage_Port;
+
+ private static final String TIMEOUT_ERR_MESSAGE =
+ RemoteDeviceNLS.ERR_RemoteDeviceWizardPage_Timeout;
+
+ /**
+ * Listener that must be implemented by others who want to monitor changes
+ * in this composite
+ */
+ public interface RemotePropertiesChangedListener
+ {
+ public void propertiesChanged();
+ }
+
+ /**
+ * Constructor
+ *
+ * @param parent the parent composite
+ */
+ public RemotePropertiesComposite(Composite parent)
+ {
+ this(parent, "", "", "", null);
+ }
+
+ /**
+ * Create contents of the composite
+ *
+ * @param parent the parent composite
+ * @param initialHost initial value for host
+ * @param initialPort initial value for port number
+ * @param initialTiomeout initial value for timeout
+ */
+ public RemotePropertiesComposite(Composite parent, String initialHost, String initialPort,
+ String initialTimeout, ISerialNumbered device)
+ {
+ super(parent, SWT.NONE);
+
+ this.device = device;
+ this.host = (((initialHost != null) && (!initialHost.equals(""))) ? initialHost : null);
+ this.port =
+ (((initialPort != null) && (!initialPort.equals(""))) ? Integer
+ .parseInt(initialPort) : -1);
+ this.timeout =
+ (((initialTimeout != null) && (!initialTimeout.equals(""))) ? Integer
+ .parseInt(initialTimeout) : -1);
+
+ // Set Help
+ PlatformUI.getWorkbench().getHelpSystem().setHelp(parent, RemoteDeviceConstants.HELP_ID);
+
+ setLayout(new GridLayout(2, false));
+
+ Label hostLabel = new Label(this, SWT.NONE);
+ hostLabel.setText(RemoteDeviceNLS.UI_Host);
+ GridData data = new GridData(SWT.FILL, SWT.FILL, false, false);
+ hostLabel.setLayoutData(data);
+
+ final Text hostText = new Text(this, SWT.BORDER);
+ data = new GridData(SWT.FILL, SWT.FILL, true, false);
+ hostText.setLayoutData(data);
+ hostText.addModifyListener(new ModifyListener()
+ {
+ private final Pattern p = Pattern.compile(IP_PATTERN);
+
+ public void modifyText(ModifyEvent e)
+ {
+ String candidateHost = hostText.getText();
+ if (candidateHost != null)
+ {
+ Matcher m = p.matcher(candidateHost);
+ if (m.matches())
+ {
+ host = candidateHost;
+ }
+ else
+ {
+ host = "";
+ }
+ notifyListeners();
+ }
+ }
+ });
+ hostText.setText(initialHost);
+ hostText.setFocus();
+
+ Label portLabel = new Label(this, SWT.NONE);
+ portLabel.setText(RemoteDeviceNLS.UI_Port);
+ data = new GridData(SWT.FILL, SWT.FILL, false, false);
+ portLabel.setLayoutData(data);
+
+ final Text portText = new Text(this, SWT.BORDER);
+ data = new GridData(SWT.FILL, SWT.FILL, true, false);
+ portText.setLayoutData(data);
+ portText.addModifyListener(new ModifyListener()
+ {
+ public void modifyText(ModifyEvent e)
+ {
+ String portStr = portText.getText();
+ try
+ {
+ port = Integer.parseInt(portStr);
+ }
+ catch (NumberFormatException e1)
+ {
+ port = -1;
+ }
+ finally
+ {
+ notifyListeners();
+ }
+ }
+ });
+ portText.setText(initialPort);
+
+ Label timeoutLabel = new Label(this, SWT.NONE);
+ timeoutLabel.setText(RemoteDeviceNLS.UI_Timeout);
+ data = new GridData(SWT.FILL, SWT.FILL, false, false);
+ timeoutLabel.setLayoutData(data);
+
+ final Text timeoutText = new Text(this, SWT.BORDER);
+ data = new GridData(SWT.FILL, SWT.FILL, true, false);
+ timeoutText.setLayoutData(data);
+ timeoutText.addModifyListener(new ModifyListener()
+ {
+ public void modifyText(ModifyEvent e)
+ {
+ String timeoutStr = timeoutText.getText();
+ try
+ {
+ timeout = Integer.parseInt(timeoutStr);
+ }
+ catch (NumberFormatException e1)
+ {
+ timeout = -1;
+ }
+ finally
+ {
+ notifyListeners();
+ }
+ }
+ });
+ timeoutText.setText((!initialTimeout.equals("")) ? initialTimeout : String
+ .valueOf(RemoteDeviceConstants.DEFAULT_TIMEOUT));
+
+ }
+
+ /**
+ * Add a listener which will be notified when there is a change in the composite
+ *
+ * @param listener a listener which will be notified when there is a change in the composite
+ */
+ public void addPropertyChangeListener(RemotePropertiesChangedListener listener)
+ {
+ synchronized (listeners)
+ {
+ listeners.add(listener);
+ }
+ }
+
+ /**
+ * Remove a listener from the list
+ *
+ * @param listener the listener to be removed
+ */
+ public void removePropertyChangeListener(RemotePropertiesChangedListener listener)
+ {
+ synchronized (listeners)
+ {
+ listeners.remove(listener);
+ }
+ }
+
+ /**
+ * Get the configured host
+ *
+ * @return the configured host
+ */
+ public String getHost()
+ {
+ return host;
+ }
+
+ /**
+ * Get the configured timeout
+ *
+ * @return the configured timeout
+ */
+ public int getTimeout()
+ {
+ return timeout;
+ }
+
+ /**
+ * Get the configured port number
+ *
+ * @return the configured port number
+ */
+ public int getPort()
+ {
+ return port;
+ }
+
+ /**
+ * Get the error message associated with the current state, if any
+ *
+ * @return the error message associated with the current state, if any
+ */
+ public String getErrorMessage()
+ {
+ String errorMsg = null;
+
+ if (timeout < 0)
+ {
+ errorMsg = TIMEOUT_ERR_MESSAGE;
+ }
+ if (port < 0)
+ {
+ errorMsg = PORT_ERR_MESSAGE;
+ }
+ else if ((port < RemoteDeviceConstants.MINIMUM_PORT_NUMBER)
+ || (port > RemoteDeviceConstants.MAXIMUM_PORT_NUMBER))
+ {
+ errorMsg =
+ NLS.bind(
+ RemoteDeviceNLS.WirelessPropertiesComposite_MsgPortNumberEqualOrHigherThan,
+ RemoteDeviceConstants.MINIMUM_PORT_NUMBER,
+ RemoteDeviceConstants.MAXIMUM_PORT_NUMBER);
+ }
+ if (host != null)
+ {
+ if (host.equals(""))
+ {
+ errorMsg = HOST_ERR_MESSAGE;
+ }
+ }
+
+ // check if host:port already exist
+ Collection<ISerialNumbered> existentRemoteDeviceInstances =
+ DevicesManager.getInstance().getInstancesByType(RemoteDeviceInstance.class);
+ for (ISerialNumbered device : existentRemoteDeviceInstances)
+ {
+ if (RemoteDeviceUtils.hasSameHostAndPort(device, host, port))
+ {
+ if ((this.device == null)
+ || ((this.device != null) && (!this.device.getDeviceName().equals(
+ device.getDeviceName()))))
+ {
+ errorMsg =
+ NLS.bind(RemoteDeviceNLS.ERR_RemoteDeviceWizardPage_Duplicated,
+ device.getDeviceName());
+
+ break;
+ }
+ }
+ }
+
+ return errorMsg;
+ }
+
+ /*
+ * Notify change listeners that there was a change in the values
+ */
+ private void notifyListeners()
+ {
+ synchronized (listeners)
+ {
+ for (RemotePropertiesChangedListener listener : listeners)
+ {
+ listener.propertiesChanged();
+ }
+ }
+ }
+}
diff --git a/src/plugins/remote.device/src/com/motorola/studio/android/remote/ui/wireless/WirelessDeviceWizardPage.java b/src/plugins/remote.device/src/com/motorola/studio/android/remote/ui/wireless/WirelessDeviceWizardPage.java
new file mode 100644
index 0000000..540942b
--- /dev/null
+++ b/src/plugins/remote.device/src/com/motorola/studio/android/remote/ui/wireless/WirelessDeviceWizardPage.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.remote.ui.wireless;
+
+import java.util.Properties;
+
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.jface.dialogs.IMessageProvider;
+import org.eclipse.jface.wizard.WizardPage;
+import org.eclipse.sequoyah.device.framework.ui.wizard.IInstanceProperties;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.DisposeEvent;
+import org.eclipse.swt.events.DisposeListener;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Shell;
+
+import com.motorola.studio.android.remote.i18n.RemoteDeviceNLS;
+import com.motorola.studio.android.remote.instance.RemoteDeviceInstance;
+import com.motorola.studio.android.remote.ui.wireless.WirelessPropertiesComposite.WirelessPropertiesChangedListener;
+
+/**
+ * Wizard Page to be used by TmL to create a new Wireless Device Remove Instance
+ */
+public class WirelessDeviceWizardPage extends WizardPage implements IInstanceProperties,
+ WirelessPropertiesChangedListener
+{
+ private WirelessPropertiesComposite composite;
+
+ /**
+ * Creates a WirelessDeviceWizardPage object.
+ */
+ public WirelessDeviceWizardPage()
+ {
+ super(RemoteDeviceNLS.UI_WirelessWizard_Name);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.sequoyah.device.framework.ui.wizard.IInstanceProperties#getProperties()
+ */
+ public Properties getProperties()
+ {
+ Properties props = new Properties();
+ props.put(RemoteDeviceInstance.PROPERTY_HOST, composite.getHost());
+ props.put(RemoteDeviceInstance.PROPERTY_PORT, Integer.toString(composite.getPort()));
+ props.put(RemoteDeviceInstance.PROPERTY_TIMEOUT, Integer.toString(composite.getTimeout()));
+ return props;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.dialogs.IDialogPage#createControl(org.eclipse.swt.widgets.Composite)
+ */
+ public void createControl(Composite parent)
+ {
+ setTitle(RemoteDeviceNLS.UI_WirelessInformationPage_Title);
+ setMessage(RemoteDeviceNLS.UI_WirelessInformationPage_Description);
+
+ composite =
+ new WirelessPropertiesComposite(parent, ((WirelessWizard) getWizard()).getIp(),
+ ((WirelessWizard) getWizard()).getInstance());
+ composite.addPropertyChangeListener(this);
+ composite.addDisposeListener(new DisposeListener()
+ {
+ public void widgetDisposed(DisposeEvent e)
+ {
+ composite.removePropertyChangeListener(WirelessDeviceWizardPage.this);
+ composite = null;
+ WirelessDeviceWizardPage.this.setControl(null);
+ }
+ });
+ setControl(composite);
+ setStatusMessage();
+
+ // adjust the wizard page size - one could also use: this.getWizard().getContainer().getShell().computeSize(500, 500);
+ this.getWizardShell().setSize(this.getWizardShell().computeSize(750, SWT.DEFAULT));
+ }
+
+ private Shell getWizardShell()
+ {
+ return this.getWizard().getContainer().getShell();
+ }
+
+ /*
+ * Set the {@link IStatus} message for this wizard.
+ */
+ private void setStatusMessage()
+ {
+ IStatus status = composite.getStatus();
+ switch (status.getSeverity())
+ {
+ case IStatus.ERROR:
+ setErrorMessage(status.getMessage());
+ setMessage(null);
+ break;
+ case IStatus.WARNING:
+ setErrorMessage(null);
+ setMessage(status.getMessage(), IMessageProvider.WARNING);
+ break;
+ case IStatus.OK:
+ setErrorMessage(null);
+ setMessage(status.getMessage(), IMessageProvider.INFORMATION);
+ break;
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.wizard.WizardPage#isPageComplete()
+ */
+ @Override
+ public boolean isPageComplete()
+ {
+ return (composite != null) && (composite.getStatus() != null)
+ && (composite.getStatus().getSeverity() != IStatus.ERROR);
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.remote.ui.RemotePropertiesComposite.RemotePropertiesChangedListener#propertiesChanged()
+ */
+ public void propertiesChanged()
+ {
+ setStatusMessage();
+ setPageComplete(isPageComplete());
+ }
+
+ /**
+ * Get the device name.
+ *
+ * @return The device name.
+ */
+ public String getDeviceName()
+ {
+ return composite != null ? composite.getDeviceName() : "";
+ }
+}
diff --git a/src/plugins/remote.device/src/com/motorola/studio/android/remote/ui/wireless/WirelessPropertiesComposite.java b/src/plugins/remote.device/src/com/motorola/studio/android/remote/ui/wireless/WirelessPropertiesComposite.java
new file mode 100644
index 0000000..3718dcc
--- /dev/null
+++ b/src/plugins/remote.device/src/com/motorola/studio/android/remote/ui/wireless/WirelessPropertiesComposite.java
@@ -0,0 +1,502 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.remote.ui.wireless;
+
+import java.util.Collection;
+import java.util.LinkedHashSet;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.sequoyah.device.framework.factory.InstanceRegistry;
+import org.eclipse.sequoyah.device.framework.model.AbstractMobileInstance;
+import org.eclipse.sequoyah.device.framework.ui.DeviceUIResources;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.ModifyEvent;
+import org.eclipse.swt.events.ModifyListener;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.ui.PlatformUI;
+
+import com.motorola.studio.android.adt.ISerialNumbered;
+import com.motorola.studio.android.devices.DevicesManager;
+import com.motorola.studio.android.remote.RemoteDeviceConstants;
+import com.motorola.studio.android.remote.RemoteDevicePlugin;
+import com.motorola.studio.android.remote.RemoteDeviceUtils;
+import com.motorola.studio.android.remote.i18n.RemoteDeviceNLS;
+import com.motorola.studio.android.remote.instance.RemoteDeviceInstance;
+
+/**
+ * Composite containing the Android Wireless Remote Device properties for edition.
+ */
+public class WirelessPropertiesComposite extends Composite
+{
+ /**
+ * Empty String
+ */
+ private static final String EMPTY_STRING = "";
+
+ /**
+ * Default invalid integer
+ */
+ private static final int DEFAULT_INVALID_INTEGER = -1;
+
+ private String name;
+
+ private final String host;
+
+ private int port;
+
+ private int timeout;
+
+ // DEVICE - if it already exist
+ ISerialNumbered device;
+
+ private IProgressMonitor monitor;
+
+ private final Collection<WirelessPropertiesChangedListener> listeners =
+ new LinkedHashSet<WirelessPropertiesChangedListener>();
+
+ // Error messages
+ private static final String NAME_ERR_MESSAGE =
+ RemoteDeviceNLS.ERR_WirelessDeviceWizardPage_Name;
+
+ private static final String HOST_ERR_MESSAGE = RemoteDeviceNLS.ERR_RemoteDeviceWizardPage_IP;
+
+ private static final String PORT_ERR_MESSAGE = RemoteDeviceNLS.ERR_RemoteDeviceWizardPage_Port;
+
+ private static final String TIMEOUT_ERR_MESSAGE =
+ RemoteDeviceNLS.ERR_RemoteDeviceWizardPage_Timeout;
+
+ private final Text nameText;
+
+ private final Text portText;
+
+ private final Text timeoutText;
+
+ /*
+ * Listener used for handle Timeout text change.
+ */
+ private final class TimeoutModifyListener implements ModifyListener
+ {
+ public void modifyText(ModifyEvent e)
+ {
+ String timeoutStr = timeoutText.getText();
+ try
+ {
+ timeout = Integer.parseInt(timeoutStr);
+ }
+ catch (NumberFormatException e1)
+ {
+ timeout = DEFAULT_INVALID_INTEGER;
+ }
+ finally
+ {
+ notifyListeners();
+ }
+ }
+ }
+
+ /*
+ * Listener used for handle Port text change.
+ */
+ private final class PortModifyListener implements ModifyListener
+ {
+ public void modifyText(ModifyEvent e)
+ {
+ String portStr = portText.getText();
+ try
+ {
+ port = Integer.parseInt(portStr);
+ // manage the case where the entered IP/Port matches of the an existing device
+ manageSameIPAndPortOfRemoteDevice();
+ }
+ catch (NumberFormatException e1)
+ {
+ port = DEFAULT_INVALID_INTEGER;
+ }
+ finally
+ {
+ notifyListeners();
+ }
+ }
+ }
+
+ /*
+ * Listener used for handle Name text change.
+ */
+ private final class NameModifyListener implements ModifyListener
+ {
+ public void modifyText(ModifyEvent e)
+ {
+ name = nameText.getText();
+ notifyListeners();
+ }
+ }
+
+ /**
+ * Listener that must be implemented by others who want to monitor changes
+ * in this composite
+ */
+ public interface WirelessPropertiesChangedListener
+ {
+ public void propertiesChanged();
+ }
+
+ /**
+ * Constructor
+ *
+ * @param parent the parent composite
+ */
+ public WirelessPropertiesComposite(Composite parent, String host, ISerialNumbered device)
+ {
+ this(parent, host, EMPTY_STRING, EMPTY_STRING, device);
+ }
+
+ /**
+ * Create contents of the composite
+ *
+ * @param parent the parent composite
+ * @param initialHost initial value for host
+ * @param initialPort initial value for port number
+ * @param initialTiomeout initial value for timeout
+ */
+ public WirelessPropertiesComposite(Composite parent, String initialHost, String initialPort,
+ String initialTimeout, ISerialNumbered device)
+ {
+ super(parent, SWT.NONE);
+ this.name = device.getDeviceName() + RemoteDeviceConstants.DEFAULT_WIRELESS_SUFIX;
+ this.host = initialHost;
+ this.port = RemoteDeviceConstants.DEFAULT_PORT;
+ this.timeout = RemoteDeviceConstants.DEFAULT_TIMEOUT;
+
+ // Set Help
+ PlatformUI.getWorkbench().getHelpSystem()
+ .setHelp(parent, RemoteDeviceConstants.WIRELESS_HELP_ID);
+ setLayout(new GridLayout(2, false));
+
+ // add device name
+ Label nameLabel = new Label(this, SWT.NONE);
+ nameLabel.setText(RemoteDeviceNLS.UI_Name);
+ nameLabel.setLayoutData(new GridData(SWT.RIGHT, SWT.FILL, false, false));
+
+ // add device text
+ nameText = new Text(this, SWT.BORDER);
+ nameText.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
+ nameText.addModifyListener(new NameModifyListener());
+ nameText.setText(name);
+
+ // add IP name
+ Label hostLabel = new Label(this, SWT.NONE);
+ hostLabel.setText(RemoteDeviceNLS.UI_Host);
+ hostLabel.setLayoutData(new GridData(SWT.RIGHT, SWT.FILL, false, false));
+
+ // add IP text
+ Text hostText = new Text(this, SWT.BORDER);
+ hostText.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
+ hostText.setEnabled(false);
+ hostText.setText(host);
+
+ // add port name
+ Label portLabel = new Label(this, SWT.NONE);
+ portLabel.setText(RemoteDeviceNLS.UI_Port);
+ portLabel.setLayoutData(new GridData(SWT.RIGHT, SWT.FILL, false, false));
+
+ // add port text
+ portText = new Text(this, SWT.BORDER);
+ portText.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
+ portText.setText(String.valueOf(port));
+ portText.addModifyListener(new PortModifyListener());
+ portText.setText(String.valueOf(port));
+
+ // add timeout label
+ Label timeoutLabel = new Label(this, SWT.NONE);
+ timeoutLabel.setText(RemoteDeviceNLS.UI_Timeout);
+ timeoutLabel.setLayoutData(new GridData(SWT.RIGHT, SWT.FILL, false, false));
+
+ // add timeout text
+ timeoutText = new Text(this, SWT.BORDER);
+ timeoutText.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
+ timeoutText.addModifyListener(new TimeoutModifyListener());
+ timeoutText.setText((!initialTimeout.equals(EMPTY_STRING)) ? initialTimeout : String
+ .valueOf(RemoteDeviceConstants.DEFAULT_TIMEOUT));
+
+ // manage the case where the entered IP/Port matches of the an existing device
+ manageSameIPAndPortOfRemoteDevice();
+ }
+
+ /**
+ * Add a listener which will be notified when there is a change in the composite
+ *
+ * @param wirelessDeviceWizardPage a listener which will be notified when there is a change in the composite
+ */
+ public void addPropertyChangeListener(WirelessPropertiesChangedListener listener)
+ {
+ synchronized (listeners)
+ {
+ listeners.add(listener);
+ }
+ }
+
+ /**
+ * Remove a listener from the list
+ *
+ * @param wirelessDeviceWizardPage the listener to be removed
+ */
+ public void removePropertyChangeListener(WirelessPropertiesChangedListener listener)
+ {
+ synchronized (listeners)
+ {
+ listeners.remove(listener);
+ }
+ }
+
+ /**
+ * Get the configured host
+ *
+ * @return the configured host
+ */
+ public String getHost()
+ {
+ return host;
+ }
+
+ /**
+ * Get the configured timeout
+ *
+ * @return the configured timeout
+ */
+ public int getTimeout()
+ {
+ return timeout;
+ }
+
+ /**
+ * Get the configured port number
+ *
+ * @return the configured port number
+ */
+ public int getPort()
+ {
+ return port;
+ }
+
+ /**
+ * Get the status associated with the current state.
+ *
+ * @return The {@link IStatus}.
+ */
+ public IStatus getStatus()
+ {
+ IStatus status =
+ new Status(IStatus.OK, RemoteDevicePlugin.PLUGIN_ID,
+ RemoteDeviceNLS.UI_WirelessInformationPage_Description);
+ String errorMessage = null;
+ boolean isValidationOK = true;
+
+ if ((name == null) || name.equals(""))
+ {
+ errorMessage = NAME_ERR_MESSAGE;
+ status = new Status(IStatus.ERROR, RemoteDevicePlugin.PLUGIN_ID, errorMessage);
+ isValidationOK = false;
+ }
+
+ if (isValidationOK && (port < 0))
+ {
+ errorMessage = PORT_ERR_MESSAGE;
+ status = new Status(IStatus.ERROR, RemoteDevicePlugin.PLUGIN_ID, errorMessage);
+ isValidationOK = false;
+ }
+ if ((isValidationOK && (port < RemoteDeviceConstants.MINIMUM_PORT_NUMBER))
+ || (port > RemoteDeviceConstants.MAXIMUM_PORT_NUMBER))
+ {
+ errorMessage =
+ NLS.bind(
+ RemoteDeviceNLS.WirelessPropertiesComposite_MsgPortNumberEqualOrHigherThan,
+ RemoteDeviceConstants.MINIMUM_PORT_NUMBER,
+ RemoteDeviceConstants.MAXIMUM_PORT_NUMBER);
+ status = new Status(IStatus.ERROR, RemoteDevicePlugin.PLUGIN_ID, errorMessage);
+ isValidationOK = false;
+ }
+ if (isValidationOK && (timeout < 0))
+ {
+ errorMessage = TIMEOUT_ERR_MESSAGE;
+ status = new Status(IStatus.ERROR, RemoteDevicePlugin.PLUGIN_ID, errorMessage);
+ isValidationOK = false;
+ }
+ if (isValidationOK && (host != null))
+ {
+ if (host.equals(EMPTY_STRING))
+ {
+ errorMessage = HOST_ERR_MESSAGE;
+ status = new Status(IStatus.ERROR, RemoteDevicePlugin.PLUGIN_ID, errorMessage);
+ isValidationOK = false;
+ }
+ }
+
+ if (isValidationOK)
+ {
+ // check if host:port already exist
+ Collection<ISerialNumbered> existentRemoteDeviceInstances =
+ DevicesManager.getInstance().getInstancesByType(RemoteDeviceInstance.class);
+ for (ISerialNumbered device : existentRemoteDeviceInstances)
+ {
+ if (RemoteDeviceUtils.hasSameHostAndPort(device, host, port))
+ {
+ if ((this.device == null)
+ || ((this.device != null) && (!this.device.getDeviceName().equals(
+ device.getDeviceName()))))
+ {
+ errorMessage =
+ NLS.bind(
+ RemoteDeviceNLS.ERR_RemoteDeviceWizardPage_WirelessDuplicated,
+ device.getDeviceName());
+ status =
+ new Status(IStatus.WARNING, RemoteDevicePlugin.PLUGIN_ID,
+ errorMessage);
+ isValidationOK = false;
+ break;
+ }
+ }
+ }
+ }
+
+ if (isValidationOK && (name != null))
+ {
+ // this verification is only applied if the IP/Port does not coincide with another application
+ if (getRemoteDeviceWithSameIPAndPort() == null)
+ {
+ InstanceRegistry registry = InstanceRegistry.getInstance();
+ if (!name.equals(EMPTY_STRING))
+ { //$NON-NLS-1$
+ if (!(registry.getInstancesByName(name).size() == 0))
+ {
+ errorMessage =
+ DeviceUIResources.SEQUOYAH_Emulator_Wizard_Project_Description_Duplicated_Error;
+ status =
+ new Status(IStatus.ERROR, RemoteDevicePlugin.PLUGIN_ID,
+ errorMessage);
+ isValidationOK = false;
+ }
+ else if (!AbstractMobileInstance.validName(name))
+ {
+ errorMessage = DeviceUIResources.SEQUOYAH_Instance_Name_Invalid_Error;
+ status =
+ new Status(IStatus.ERROR, RemoteDevicePlugin.PLUGIN_ID,
+ errorMessage);
+ isValidationOK = false;
+ }
+ }
+ }
+ }
+
+ return status;
+ }
+
+ /*
+ * Notify change listeners that there was a change in the values
+ */
+ private void notifyListeners()
+ {
+ synchronized (listeners)
+ {
+ for (WirelessPropertiesChangedListener listener : listeners)
+ {
+ listener.propertiesChanged();
+ }
+ }
+ }
+
+ /*
+ * Manage the case in which was inputed the same IP and port of
+ * an existing remote device.
+ */
+ private void manageSameIPAndPortOfRemoteDevice()
+ {
+ ISerialNumbered matchedDevice = getRemoteDeviceWithSameIPAndPort();
+ // if there is a device which matches the IP/Port of this wizard, set its Name and disable the field
+ if (matchedDevice != null)
+ {
+ nameText.setText(matchedDevice.getDeviceName());
+ nameText.setEnabled(false);
+ }
+ // since there is no device, enable the name field
+ else
+ {
+ nameText.setEnabled(true);
+ }
+ }
+
+ /*
+ * Returns a device which matches the IP/Port of this wizard,
+ * in cas there is any.
+ *
+ * @return get the {@link ISerialNumbered} device which matches
+ * the IP/Port of this wizard.
+ */
+ private ISerialNumbered getRemoteDeviceWithSameIPAndPort()
+ {
+ ISerialNumbered matchedDevice = null;
+ Collection<ISerialNumbered> existentRemoteDeviceInstances =
+ DevicesManager.getInstance().getInstancesByType(RemoteDeviceInstance.class);
+ for (ISerialNumbered device : existentRemoteDeviceInstances)
+ {
+ if (RemoteDeviceUtils.hasSameHostAndPort(device, host, port))
+ {
+ if ((this.device == null)
+ || ((this.device != null) && (!this.device.getDeviceName().equals(
+ device.getDeviceName()))))
+ {
+ matchedDevice = device;
+ break;
+ }
+ }
+ }
+ return matchedDevice;
+ }
+
+ /**
+ * Get the {@link IProgressMonitor}
+ *
+ * @return Return the monitor
+ */
+ public IProgressMonitor getProgressMonitor()
+ {
+ return monitor;
+ }
+
+ /**
+ * @param monitor the monitor to set
+ */
+ public void setProgressMonitor(IProgressMonitor monitor)
+ {
+ this.monitor = monitor;
+ }
+
+ /**
+ * Get the device name.
+ *
+ * @return Device name
+ */
+ public String getDeviceName()
+ {
+ return this.name;
+ }
+}
diff --git a/src/plugins/remote.device/src/com/motorola/studio/android/remote/ui/wireless/WirelessWizard.java b/src/plugins/remote.device/src/com/motorola/studio/android/remote/ui/wireless/WirelessWizard.java
new file mode 100644
index 0000000..453b6c5
--- /dev/null
+++ b/src/plugins/remote.device/src/com/motorola/studio/android/remote/ui/wireless/WirelessWizard.java
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.motorola.studio.android.remote.ui.wireless;
+
+import java.lang.reflect.InvocationTargetException;
+import java.util.Properties;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.jface.wizard.Wizard;
+
+import com.motorola.studio.android.AndroidPlugin;
+import com.motorola.studio.android.adt.ISerialNumbered;
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.common.utilities.EclipseUtils;
+import com.motorola.studio.android.remote.RemoteDevicePlugin;
+import com.motorola.studio.android.remote.i18n.RemoteDeviceNLS;
+import com.motorola.studio.android.remote.ui.wireless.runnables.SwitchFromUSBAndConnectToWirelessRunnable;
+
+/**
+ * Switch to Wireless Connection Mode.
+ */
+public class WirelessWizard extends Wizard
+{
+ // Wizard icon
+ private final String WIRELESS_WIZARD_IMAGE_PATH = "icons/wireless_wizard-icon-64x64.png"; //$NON-NLS-1$
+
+ WirelessDeviceWizardPage informationPage;
+
+ private ISerialNumbered instance;
+
+ private String host;
+
+ private IProgressMonitor monitor;
+
+ /**
+ * Default constructor.
+ */
+ public WirelessWizard()
+ {
+ super.setDefaultPageImageDescriptor(RemoteDevicePlugin
+ .getImageDescriptor(WIRELESS_WIZARD_IMAGE_PATH));
+ this.setWindowTitle(RemoteDeviceNLS.UI_WirelessWizard_Name);
+ setNeedsProgressMonitor(true);
+ }
+
+ @Override
+ public void addPages()
+ {
+ informationPage = new WirelessDeviceWizardPage();
+ addPage(informationPage);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.wizard.Wizard#performFinish()
+ */
+ @Override
+ public boolean performFinish()
+ {
+ boolean isProcessOK = true;
+ try
+ {
+ // execute connection switch and show success message in case everything went fine
+ getContainer().run(true, true, new SwitchFromUSBAndConnectToWirelessRunnable(this));
+ EclipseUtils.showInformationDialog(
+ RemoteDeviceNLS.WirelessWizard_TitleWirelessConnectionModeWizard,
+ RemoteDeviceNLS.WirelessWizard_WirelessDeviceCreatedSuccessfully);
+ StudioLogger.collectUsageData(StudioLogger.WHAT_REMOTE_WIRELESS,
+ StudioLogger.KIND_REMOTE_DEVICE, StudioLogger.DESCRIPTION_DEFAULT,
+ RemoteDevicePlugin.PLUGIN_ID, RemoteDevicePlugin.getDefault().getBundle()
+ .getVersion().toString());
+ }
+ catch (InvocationTargetException ite)
+ {
+ // treat case where something went wrong - log, show an error message and set the wizard flag
+ StudioLogger.error(this.getClass(), "Problems switching device to TCP/IP.", ite); //$NON-NLS-1$
+ IStatus status =
+ new Status(IStatus.ERROR, AndroidPlugin.PLUGIN_ID,
+ ite.getTargetException() != null ? ite.getTargetException()
+ .getMessage() : ite.getMessage());
+ EclipseUtils.showErrorDialog(
+ RemoteDeviceNLS.WirelessWizard_TitleWirelessConnectionModeWizard,
+ RemoteDeviceNLS.WirelessWizard_MsgErrorProblemsSwitchingDeviceToTCPIP, status);
+ isProcessOK = false;
+ }
+ catch (InterruptedException ie)
+ {
+ // action was canceled by the user, therefore do not close the wizard
+ isProcessOK = false;
+ }
+
+ return isProcessOK;
+ }
+
+ public void setInstance(ISerialNumbered instance)
+ {
+ this.instance = instance;
+ }
+
+ public ISerialNumbered getInstance()
+ {
+ return instance;
+ }
+
+ /**
+ * @return the monitor
+ */
+ public IProgressMonitor getProgressMonitor()
+ {
+ return monitor;
+ }
+
+ /**
+ * @param monitor the monitor to set
+ */
+ public void setProgressMonitor(IProgressMonitor monitor)
+ {
+ this.monitor = monitor;
+ }
+
+ /**
+ * Get the {@link Properties} associated with this {@link Wizard}.
+ *
+ * @return Return the related {@link Properties}.
+ */
+ public Properties getProperties()
+ {
+ return informationPage != null ? informationPage.getProperties() : null;
+ }
+
+ /**
+ * @param host
+ */
+ public void setIp(String host)
+ {
+ this.host = host;
+ }
+
+ /**
+ * @param host
+ */
+ public String getIp()
+ {
+ return host;
+ }
+
+ public String getDeviceName()
+ {
+ return informationPage.getDeviceName();
+ }
+}
diff --git a/src/plugins/remote.device/src/com/motorola/studio/android/remote/ui/wireless/runnables/SwitchFromUSBAndConnectToWirelessRunnable.java b/src/plugins/remote.device/src/com/motorola/studio/android/remote/ui/wireless/runnables/SwitchFromUSBAndConnectToWirelessRunnable.java
new file mode 100644
index 0000000..7207ce6
--- /dev/null
+++ b/src/plugins/remote.device/src/com/motorola/studio/android/remote/ui/wireless/runnables/SwitchFromUSBAndConnectToWirelessRunnable.java
@@ -0,0 +1,230 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.remote.ui.wireless.runnables;
+
+import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
+import java.util.List;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.SubMonitor;
+import org.eclipse.jface.operation.IRunnableWithProgress;
+import org.eclipse.jface.wizard.Wizard;
+import org.eclipse.sequoyah.device.common.utilities.exception.SequoyahException;
+import org.eclipse.sequoyah.device.framework.DevicePlugin;
+import org.eclipse.sequoyah.device.framework.factory.InstanceRegistry;
+import org.eclipse.sequoyah.device.framework.manager.InstanceManager;
+import org.eclipse.sequoyah.device.framework.model.IInstance;
+
+import com.motorola.studio.android.AndroidPlugin;
+import com.motorola.studio.android.adt.DDMSFacade;
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.remote.i18n.RemoteDeviceNLS;
+import com.motorola.studio.android.remote.instance.RemoteDeviceInstance;
+import com.motorola.studio.android.remote.ui.wireless.WirelessWizard;
+
+/**
+ * Service which switches the device to TCP/IP, add it as a remote
+ * device to the Device Management and connect to it via the wireless
+ * network.
+ */
+public class SwitchFromUSBAndConnectToWirelessRunnable implements IRunnableWithProgress
+{
+
+ private final WirelessWizard wirelessWizard;
+
+ /**
+ * Constructor which passes the {@link Wizard} page.
+ *
+ * @param wirelessWizard Wizard paged
+ */
+ public SwitchFromUSBAndConnectToWirelessRunnable(WirelessWizard wirelessWizard)
+ {
+ this.wirelessWizard = wirelessWizard;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.jface.operation.IRunnableWithProgress#run(org.eclipse.core.runtime.IProgressMonitor)
+ */
+ public void run(IProgressMonitor monitor) throws InvocationTargetException,
+ InterruptedException
+ {
+ boolean isInstanceCreated = false;
+ SubMonitor subMonitor = SubMonitor.convert(monitor, 1000);
+ subMonitor
+ .beginTask(
+ RemoteDeviceNLS.SwitchFromUSBAndConnectToWirelessRunnable_MsgCreatingWirelessRemoteDevice,
+ 1000);
+
+ RemoteDeviceInstance remoteDeviceInstance = null;
+ IStatus status = Status.OK_STATUS;
+
+ // get connection timeout
+ int connectionTimeout =
+ Integer.valueOf(this.wirelessWizard.getProperties().getProperty(
+ RemoteDeviceInstance.PROPERTY_TIMEOUT));
+
+ try
+ {
+ subMonitor.worked(100);
+
+ if (!subMonitor.isCanceled())
+ {
+ // switch device connection from USB to TCP/IP
+ try
+ {
+ status =
+ DDMSFacade.switchUSBtoTcpIp(this.wirelessWizard.getInstance()
+ .getDeviceName(), this.wirelessWizard.getInstance()
+ .getSerialNumber(), this.wirelessWizard.getProperties()
+ .getProperty(RemoteDeviceInstance.PROPERTY_PORT),
+ connectionTimeout, subMonitor.newChild(300));
+ }
+ catch (IOException se)
+ {
+ // log error, adjust the status and throw the exception
+ status = handleStatusAndLogDuringException(se);
+ throw new InvocationTargetException(se, se.getMessage());
+ }
+ }
+
+ remoteDeviceInstance = null;
+ if (!(status.getSeverity() == IStatus.ERROR) && !subMonitor.isCanceled())
+ {
+ subMonitor
+ .setTaskName(RemoteDeviceNLS.SwitchFromUSBAndConnectToWirelessRunnable_CreatingRemoteDeviceInstance);
+
+ // verify if there already is an instance created with the same name
+ List<IInstance> instanceByName =
+ InstanceRegistry.getInstance().getInstancesByName(
+ this.wirelessWizard.getDeviceName());
+
+ if ((instanceByName != null) && (!instanceByName.isEmpty()))
+ {
+ remoteDeviceInstance = (RemoteDeviceInstance) instanceByName.get(0);
+ }
+ else
+ {
+ // create the new remote device instance
+ try
+ {
+ remoteDeviceInstance =
+ (RemoteDeviceInstance) InstanceManager.createInstance(
+ this.wirelessWizard.getDeviceName(), //$NON-NLS-1$
+ "com.motorola.studio.android.remote.androidRemoteDevice", //$NON-NLS-1$
+ DevicePlugin.SEQUOYAH_STATUS_OFF,
+ this.wirelessWizard.getProperties());
+ }
+ catch (SequoyahException se)
+ {
+ // log error, adjust the status and throw the exception
+ status = handleStatusAndLogDuringException(se);
+ throw new InvocationTargetException(se, se.getMessage());
+ }
+
+ if (!subMonitor.isCanceled())
+ {
+ // add instance to the DDMS and set the flag
+ InstanceRegistry.getInstance().addInstance(remoteDeviceInstance);
+ isInstanceCreated = true;
+ }
+ }
+ subMonitor.worked(300);
+ }
+
+ if (!subMonitor.isCanceled())
+ {
+ subMonitor
+ .setTaskName(RemoteDeviceNLS.SwitchFromUSBAndConnectToWirelessRunnable_ConnectingToWifiDevice);
+
+ int timeoutAux = (connectionTimeout > 60) ? connectionTimeout : 60;
+
+ long timeoutLimit = System.currentTimeMillis() + (timeoutAux * 1000);
+
+ // after the adb mode is switched to tcpip the handset takes a while to
+ // be available for connection, that is why this while exists
+ while ((!DDMSFacade.isDeviceOnline(remoteDeviceInstance.getSerialNumber()))
+ && (System.currentTimeMillis() < timeoutLimit))
+ {
+ // connect the remote device via TCP/IP
+ try
+ {
+ status =
+ DDMSFacade.connectTcpIp(
+ remoteDeviceInstance,
+ this.wirelessWizard.getProperties().getProperty(
+ RemoteDeviceInstance.PROPERTY_HOST),
+ this.wirelessWizard.getProperties().getProperty(
+ RemoteDeviceInstance.PROPERTY_PORT),
+ connectionTimeout, subMonitor.newChild(300));
+ }
+ catch (IOException ioe)
+ {
+ status = handleStatusAndLogDuringException(ioe);
+ throw new InvocationTargetException(ioe, ioe.getMessage());
+ }
+ }
+ }
+ // in case the status has errors, throw InvocationTargetException
+ if ((status != null) && (status.getSeverity() == IStatus.ERROR)
+ && (!subMonitor.isCanceled()))
+ {
+ if (status.getException() != null)
+ {
+ throw new InvocationTargetException(status.getException());
+ }
+ else
+ {
+ throw new InvocationTargetException(
+ new Exception(
+ RemoteDeviceNLS.SwitchFromUSBAndConnectToWirelessRunnable_MsgNotPossibleToConvertUSBToTCPIP));
+ }
+ }
+
+ // treat the case where the monitor is canceled - throw InterruptedException as stated in this method
+ if (monitor.isCanceled())
+ {
+ throw new InterruptedException(
+ "The switching to TCP/IP connection mode was canceled by the user.");
+ }
+ }
+ finally
+ {
+ // remove the device in case it exists and was added to the Device Management View
+ if ((remoteDeviceInstance != null) && (isInstanceCreated)
+ && ((status.getSeverity() == IStatus.ERROR) || (subMonitor.isCanceled())))
+ {
+ InstanceRegistry.getInstance().removeInstance(remoteDeviceInstance);
+ }
+ }
+ }
+
+ /*
+ * Log the exception and get the error status.
+ *
+ * @param exception The exception to be treated.
+ *
+ * @return Returns the Error status.
+ */
+ private IStatus handleStatusAndLogDuringException(Exception exception)
+ {
+ StudioLogger.error(this.getClass(), exception.getMessage(), exception);
+ return new Status(IStatus.ERROR, AndroidPlugin.PLUGIN_ID, exception.getMessage());
+ }
+} \ No newline at end of file
diff --git a/src/plugins/snippets/.classpath b/src/plugins/snippets/.classpath
new file mode 100644
index 0000000..8a8f166
--- /dev/null
+++ b/src/plugins/snippets/.classpath
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>
+ <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/src/plugins/snippets/.project b/src/plugins/snippets/.project
new file mode 100644
index 0000000..38fac4e
--- /dev/null
+++ b/src/plugins/snippets/.project
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>com.motorola.studio.android.codesnippets</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.ManifestBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.SchemaBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.pde.PluginNature</nature>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+</projectDescription>
diff --git a/src/plugins/snippets/META-INF/MANIFEST.MF b/src/plugins/snippets/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..c52e86d
--- /dev/null
+++ b/src/plugins/snippets/META-INF/MANIFEST.MF
@@ -0,0 +1,19 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: %pluginName
+Bundle-SymbolicName: com.motorola.studio.android.codesnippets;singleton:=true
+Bundle-Version: 5.0.0.qualifier
+Bundle-Localization: plugin
+Require-Bundle: org.eclipse.core.runtime,
+ org.eclipse.core.resources,
+ org.eclipse.ui,
+ org.eclipse.ui.ide,
+ org.eclipse.ui.editors,
+ org.eclipse.gef,
+ org.eclipse.jface.text,
+ org.eclipse.jdt.ui,
+ org.eclipse.wst.common.snippets,
+ com.motorola.studio.android.common
+Bundle-Vendor: %providerName
+Bundle-RequiredExecutionEnvironment: JavaSE-1.6
+Export-Package: com.motorola.studio.android.codesnippets.i18n
diff --git a/src/plugins/snippets/build.properties b/src/plugins/snippets/build.properties
new file mode 100644
index 0000000..0dc34f7
--- /dev/null
+++ b/src/plugins/snippets/build.properties
@@ -0,0 +1,6 @@
+source.. = src/
+output.. = bin/
+bin.includes = META-INF/,\
+ .,\
+ plugin.xml,\
+ plugin.properties
diff --git a/src/plugins/snippets/plugin.properties b/src/plugins/snippets/plugin.properties
new file mode 100644
index 0000000..221659b
--- /dev/null
+++ b/src/plugins/snippets/plugin.properties
@@ -0,0 +1,532 @@
+#################################################################################
+#
+# Code Snippets properties
+#
+#################################################################################
+
+pluginName=MOTODEV Studio for Android Code Snippets Plug-in
+providerName= Motorola Mobility, Inc.
+
+# -------------------------
+# System Services
+# -------------------------
+
+category.service=System Services
+category.service.description=Obtain handles for system-level services
+
+snippet.service.systemService00=Activity Manager
+snippet.service.systemService01=Alarm Manager
+snippet.service.systemService02=Audio Manager
+snippet.service.systemService03=Clipboard Manager
+snippet.service.systemService04=Connectivity Manager
+snippet.service.systemService05=Input Method Manager
+snippet.service.systemService06=Keyguard Manager
+snippet.service.systemService07=Layout Inflater Manager
+snippet.service.systemService08=Location Manager
+snippet.service.systemService09=Notification Manager
+snippet.service.systemService10=Power Manager
+snippet.service.systemService11=Search Manager
+snippet.service.systemService12=Sensor Manager
+snippet.service.systemService13=Telephony Manager
+snippet.service.systemService14=Vibrator
+snippet.service.systemService15=Wallpaper
+snippet.service.systemService16=Wi-Fi Manager
+snippet.service.systemService17=Window Manager
+
+snippet.service.systemService00.description=Obtain a handle to the manager responsible for interacting with running activities
+snippet.service.systemService01.description=Obtain a handle to the manager responsible for providing access to the system alarm services
+snippet.service.systemService02.description=Obtain a handle to the manager responsible for handling volume, ringer modes, audio routing and so on
+snippet.service.systemService03.description=Obtain a handle to the manager responsible for accessing and modifying the contents of the global clipboard
+snippet.service.systemService04.description=Obtain a handle to the manager responsible for answering queries about the state of network connectivity
+snippet.service.systemService05.description=Obtain a handle to the manager responsible for handling input methods
+snippet.service.systemService06.description=Obtain a handle to the manager responsible for locking and unlocking the keyboard
+snippet.service.systemService07.description=Obtain a handle to the manager responsible for instantiating layout XML files into their corresponding View objects
+snippet.service.systemService08.description=Obtain a handle to the manager responsible for providing access to the system location services (geographical location)
+snippet.service.systemService09.description=Obtain a handle to the manager responsible for informing the user about events that happen in the background
+snippet.service.systemService10.description=Obtain a handle to the manager responsible for controlling power management, including "wake locks"
+snippet.service.systemService11.description=Obtain a handle to the manager responsible for providing access to the system search services
+snippet.service.systemService12.description=Obtain a handle to the manager responsible for accessing sensors, such as those that provide information about device orientation and inclination
+snippet.service.systemService13.description=Obtain a handle to the manager responsible for accessing information about the telephony services on the device
+snippet.service.systemService14.description=Obtain a handle to the manager responsible for interacting with the vibration hardware
+snippet.service.systemService15.description=Obtain a handle to the manager responsible for accessing wallpapers
+snippet.service.systemService16.description=Obtain a handle to the manager responsible for managing all aspects of Wi-Fi connectivity
+snippet.service.systemService17.description=Obtain a handle to the manager responsible for granting apps access to the system window manager
+
+
+# -------------------------
+# General
+# -------------------------
+
+category.general=General
+category.general.description=General snippets to use while building your application
+
+snippet.general.sendSMS=Send an SMS
+snippet.general.toast=Display a Toast
+snippet.general.statusBar=Status Bar Notification
+snippet.general.vibrateTime=Vibrate the phone for a given time
+snippet.general.vibratePattern=Vibrate the phone following an on/off pattern
+snippet.general.strictMode=Turn on Strict Mode
+
+
+snippet.general.sendSMS.description=Construct and send an SMS message
+snippet.general.toast.description=Display a "toast" message to the user
+snippet.general.statusBar.description=Display a message in the status bar
+snippet.general.vibrateTime.description=Trigger the vibration hardware for a limited time
+snippet.general.vibratePattern.description=Trigger the vibration hardware to follow an on/off pattern
+snippet.general.strictMode.description=Detect operations executed in the UI that are not recommended and bring them to the user's attention
+
+
+# -------------------------
+# Preferences
+# -------------------------
+
+category.preferences=Preferences
+category.preferences.description=Snippets used to store and retrieve application preferences as key-value pairs.
+
+snippet.preferences.loadSharedPreference=Retrieve shared preference - named preferences file
+snippet.preferences.loadSharedPreference.description=Retrieve a preference value from a named preferences file.
+
+snippet.preferences.loadSharedPreferenceFromPreferenceManager=Retrieve shared preference - single preference file
+snippet.preferences.loadSharedPreferenceFromPreferenceManager.description=Retrieve a shared preference from Preference Manager
+
+snippet.preferences.writeSharedPreference=Write shared preference - named preferences file
+snippet.preferences.writeSharedPreference.description=Write a preference value to a named preferences file.
+
+snippet.preferences.writeSharedPreferenceFromPreferenceManager=Write shared preference - single preference file
+snippet.preferences.writeSharedPreferenceFromPreferenceManager.description=Write a preference value in the one preferences file used by your Activity.
+
+# -------------------------
+# Database
+# -------------------------
+
+category.database=Database
+category.database.description=Database related snippets which use SQLite
+
+snippet.database.createOrOpenDb=Database - Create/Open
+snippet.database.deleteDb=Database - Delete
+snippet.database.createTbl=Table - Create
+snippet.database.deleteTbl=Table - Delete
+snippet.database.insertEntry=Entry - Insert
+snippet.database.updateEntry=Entry - Update
+snippet.database.deleteEntry=Entry - Delete
+snippet.database.runQuery=Run SQL Query
+
+snippet.database.createOrOpenDb.description=Open or create a new SQLite database
+snippet.database.deleteDb.description=Delete a SQLite database
+snippet.database.createTbl.description=Create a new table within a database
+snippet.database.deleteTbl.description=Delete a table from a database
+snippet.database.insertEntry.description=Insert a table row
+snippet.database.updateEntry.description=Update a table row
+snippet.database.deleteEntry.description=Delete a table row
+snippet.database.runQuery.description=Execute a SQL query
+
+
+# -------------------------
+# Menus and Action Bar
+# -------------------------
+
+category.menus=Menus and Action Bar
+category.menus.description=Snippets for working with menu and action bar
+
+snippet.menus.handleItems=Construct and handle an options menu
+snippet.menus.disableItem=Disable a menu item
+snippet.menus.subItem=Add a sub-menu
+snippet.menus.declareMenuXML=XML menu definition
+snippet.menus.inflateMenu=Inflate menu from XML
+snippet.menus.showActionBar=Show action bar
+snippet.menus.hideActionBar=Hide action bar
+snippet.menus.manage.actionbar.label=Action bar - navigation tab mode
+snippet.menus.toggle.actionbar.label=Toggle action bar visibility
+
+snippet.menus.handleItems.description=Example of how to add items to an options menu and handle menu item selection. Add this to your activity.
+snippet.menus.disableItem.description=Disable a menu item, making it unusable
+snippet.menus.subItem.description=Add a sub-menu to a menu item
+snippet.menus.declareMenuXML.description=A menu definition, in XML. Save this in res/menu/ and name it <nameYouWantToReferToThisMenu>.xml
+snippet.menus.inflateMenu.description=Inflate an XML menu definition for use in an activity.
+snippet.menus.showActionBar.description=Programmatically show the action bar
+snippet.menus.hideActionBar.description=Programmatically hide the action bar
+snippet.menus.manage.actionbar.description=Put the action bar in tab mode for navigation
+snippet.menus.toggle.actionbar.description=Show the action bar if it is hidden, and hide it otherwise.
+
+
+# -------------------------
+# Screen
+# -------------------------
+
+category.screen=Screen
+category.screen.description=Snippets related to screen properties
+
+snippet.screen.fullscreen=Make an activity full-screen
+snippet.screen.size=Get the screen size
+snippet.screen.orientation=Get the screen orientation
+
+snippet.screen.fullscreen.description=Expand the current activity so it fits the device's screen
+snippet.screen.size.description=Retrieve the size of the device's screen
+snippet.screen.orientation.description=Retrieve the current screen orientation
+
+
+
+# -------------------------
+# Log
+# -------------------------
+
+category.log=Log
+category.log.description=Snippets for logging messages from an Android application
+
+snippet.log.error=Log ERROR message
+snippet.log.warning=Log WARNING message
+snippet.log.info=Log INFO message
+snippet.log.debug=Log DEBUG message
+
+snippet.log.error.description=Log an ERROR message
+snippet.log.warning.description=Log a WARNING message
+snippet.log.info.description=Log an INFO message
+snippet.log.debug.description=Log a DEBUG message
+
+
+# -------------------------
+# GPS
+# -------------------------
+
+category.gps=GPS
+category.gps.description=Snippets related to GPS location and status
+
+snippet.gps.currentCoordinates=Get current GPS coordinates
+snippet.gps.lastCoordinates=Get last known GPS coordinates
+snippet.gps.distance=Distance between GPS coordinates
+snippet.gps.listenerChanges=Register for GPS status changes
+snippet.gps.listenerProximity=Register for a proximity alert
+
+snippet.gps.currentCoordinates.description=Get the current location of the device
+snippet.gps.lastCoordinates.description=Get the last known location of the device
+snippet.gps.distance.description=Calculate the distance between two locations
+snippet.gps.listenerChanges.description=Register a listener for changes in GPS status
+snippet.gps.listenerProximity.description=Register a listener to be notified when the device is near a given point
+
+
+# -------------------------
+# Media
+# -------------------------
+
+category.media=Media
+category.media.description=Snippets to play/record media files
+
+snippet.media.playAudioVideoRaw=Play audio or video from a file within the application
+snippet.media.playAudioVideoFile=Play audio or video given a path to a file or a URL
+snippet.media.startRecordingAudio=Start recording audio
+snippet.media.stopRecording=Stop recording audio
+
+snippet.media.playAudioVideoRaw.description=Play a media file that is located in a folder inside the application
+snippet.media.playAudioVideoFile.description=Play a media file that is located in the local file system or on the Internet
+snippet.media.startRecordingAudio.description=Record audio and save to a file
+snippet.media.stopRecording.description=Stop the audio recording
+
+
+# -------------------------
+# MOTOROLA API
+# -------------------------
+
+category.motorolaapi=Motorola APIs
+category.motorolaapi.description=Snippets for Motorola APIs
+
+snippet.motorolaapi.ffcamera=Use Front-Facing Camera
+snippet.motorolaapi.hdmi=Retrieve HDMI Status
+
+snippet.motorolaapi.ffcamera.description=Example of how to obtain the camera object that represents the front-facing camera
+snippet.motorolaapi.hdmi.description=Receive notification of HDMI status changes
+
+
+# -------------------------
+# Resources and Assets
+# -------------------------
+
+category.resources=Resources and Assets
+category.resources.description=Snippets to access resources and assets
+
+snippet.resources.listAssets=List your application's assets
+snippet.resources.readFile=Open an asset as a byte stream
+snippet.resources.readXmlFile=Open an asset for parsing as XML
+
+snippet.resources.listAssets.description=Create a list of assets within your application's package
+snippet.resources.readFile.description=Retrieve an InputStream for a named asset
+snippet.resources.readXmlFile.description=Retrieve an XmlResourceParser for a compiled XML file
+
+
+# -------------------------
+# Dialogs
+# -------------------------
+
+category.dialogs=Dialogs
+category.dialogs.description=Snippets to display dialogs
+
+snippet.dialogs.alert=Alert Dialog
+snippet.dialogs.progressDialog=Progress Dialog
+snippet.dialogs.progressBar=Progress Bar Dialog
+snippet.dialogs.datePicker=Date Picker Dialog
+snippet.dialogs.timePicker=Time Picker Dialog
+snippet.dialogs.custom=Custom Dialog
+snippet.dialogs.customAlert=Custom Alert Dialog
+
+snippet.dialogs.alert.description=Display a simple alert dialog with a Yes/No question
+snippet.dialogs.progressDialog.description=Display a simple progress dialog
+snippet.dialogs.progressBar.description=Display a progress bar
+snippet.dialogs.datePicker.description=Construct and display a dialog that allows the user to select a date
+snippet.dialogs.timePicker.description=Construct and display a dialog that allows the user to select a time
+snippet.dialogs.custom.description=Display a dialog with custom content. This dialog must have a title.
+snippet.dialogs.customAlert.description=Display an alert dialog with custom content. This dialog doesn't have to have a title.
+
+
+# -------------------------
+# Web and WebServices
+
+# -------------------------
+
+category.web=Web and Web Services
+
+category.web.description=Samples of how to access web servers from your Android applications
+
+
+
+snippet.web.post=Executing a POST request
+
+snippet.web.get=Executing a GET request
+
+snippet.web.json=Retrieve JSON from a server response
+
+snippet.web.rest=Query for a REST response
+
+snippet.web.email=Send an email
+
+snippet.web.urlconnection=Retrieve data from a URL
+
+snippet.web.soap= SOAP example
+
+
+
+snippet.web.post.description=Executes a post request using the Apache HTTPClient
+
+snippet.web.get.description=Executes a GET request using the Apache HTTPClient
+
+snippet.web.json.description=Retrieve JSON objects from a RESTful web server response. See the "Query for a REST response" snippet to get the response.
+
+snippet.web.email.description=Send an email message where the subject, body, and destination are all text strings
+
+snippet.web.urlconnection.description=Open a connection to a given URL and get the response as a String
+
+snippet.web.soap.description=Example of accessing and parsing SOAP from a web server using the KSoap project
+snippet.web.rest.description=Query for a REST response from a web server using a GET request
+
+
+
+
+
+# -------------------------
+
+# Sensors
+
+# -------------------------
+
+category.sensors=Sensors
+
+category.sensors.description=Use of sensors, such as accelerometer
+
+
+
+snnipet.sensor.detect=Detect a sensor
+
+snnipet.sensor.getorientation=Read data from a sensor
+
+
+
+snnipet.sensor.detect.description=Determine if your device supports a particular sensor
+
+snnipet.sensor.getorientation.description=Read data from an orientation sensor
+
+
+
+
+
+# -------------------------
+
+# SQL
+
+# -------------------------
+
+category.sql=SQL
+
+category.sql.description=Some SQL samples that you can use to mount your queries
+
+
+
+snippet.sql.update=Update a table
+
+snippet.sql.inner=Inner join example
+
+snippet.sql.left=Left join example
+
+snippet.sql.count=Count example
+
+snippet.sql.insert=Insert example
+
+snippet.sql.createfromtable=Create a table with data from another table
+
+snippet.sql.drop=Delete (drop) a table
+
+snippet.sql.delete=Delete rows
+
+snippet.sql.create=Create table
+
+
+
+snippet.sql.update.description=SQL query for updating a table
+
+snippet.sql.inner.description=SQL query for selecting all students from a table students that are also present in the table math_class
+
+snippet.sql.left.description=SQL query for selecting all students from a table students that has grade bigger than 5 in the table math_class
+
+snippet.sql.count.description=How many different cities do our students come from?
+
+snippet.sql.insert.description=Insert a David Bowie album into a database table
+
+snippet.sql.createfromtable.description=Creates a table composed of rock songs from the "songs" table
+
+snippet.sql.drop.description=Deletes a named table
+
+snippet.sql.delete.description=Deletes from a table all songs that have a rating less than 5
+
+snippet.sql.create.description=Creates a table to store songs
+
+
+
+
+
+# -------------------------
+
+# Bluetooth
+
+# -------------------------
+
+category.bluetooth=Bluetooth
+
+category.bluetooth.description=Snippets to access and use the device bluetooth
+
+
+
+snippet.bluetooth.check=Verify that Bluetooth is supported
+
+snippet.bluetooth.enable=Enable Bluetooth
+
+snippet.bluetooth.discoverable=Ensure that this device is discoverable
+
+snippet.bluetooth.getpaired=Get paired devices
+
+snippet.bluetooth.discover=Register for remote device discovery
+
+snippet.bluetooth.incoming=Wait for incoming connections
+
+snippet.bluetooth.connect=Connect to a device
+
+
+
+snippet.bluetooth.check.description=Verify that the device supports Bluetooth by trying to get the Bluetooth adapter
+
+snippet.bluetooth.enable.description=If Bluetooth is supported, enables it such that you receive a callback when it is enabled
+
+snippet.bluetooth.getpaired.description=Gets the set of devices paired with this one
+
+snippet.bluetooth.discover.description=Registers intents for remote device discovery. Also includes a BroadcastReceiver that is notified as each device is found and when the discovery process completes.
+snippet.bluetooth.discoverable.description=If this device is not discoverable, make it so
+
+snippet.bluetooth.incoming.description=Code (likely to run on a thread) that creates a listening, secure RFCOMM Bluetooth socket, retrieves incoming connections, and opens input and output streams connected to the socket.
+
+snippet.bluetooth.connect.description=Connect to a device
+
+
+
+# -------------------------
+
+# Localization
+# -------------------------
+
+category.localization=Localization
+category.localization.description=Snippets to work with localized resources
+
+snippet.localization.string=Get localized string
+snippet.localization.drawable=Get localized drawable
+snippet.localization.curLocale=Get current locale
+snippet.localization.resLocale=Get resource locale
+snippet.localization.dynamicMsg=Create formatted message
+
+snippet.localization.string.description=Get a localized string resource
+snippet.localization.drawable.description=Get a localized drawable resource
+snippet.localization.curLocale.description=Get the current locale from Java
+snippet.localization.resLocale.description=Get the current locale for your application's package
+snippet.localization.dynamicMsg.description=Create a formatted message containing the full language name in the default locale
+
+# -------------------------
+# General UI
+# -------------------------
+category.ui=General UI Utilities
+category.ui.description=Code for handling UIs in an efficient way
+
+snippet.ui.gestures.string=Pixel density independent touch gestures
+snippet.ui.sizes.string=Adjust widget size
+snippet.ui.background.string=Scale bitmap to view size
+snippet.ui.widget.string=Respond to widget interaction
+snippet.ui.moveout.string=Move code out of UI thread
+snippet.ui.backgroundtaskwithnotification.string=Background task with status bar notification
+snippet.ui.hide.statusbar.label=Hide status bar - lights off mode
+snippet.ui.visible.statusbar.label=Make status bar visible
+
+snippet.ui.gestures.description=Make your touch gestures work the same way, independent of the device pixel density.
+snippet.ui.sizes.description=Adjust the size of a widget (a button, in this example) when the enclosing view changes its size.
+snippet.ui.background.description=Resize a bitmap to the size of a view. Useful for creating background images.
+snippet.ui.widget.description=Handle user interaction with a widget (a button, in this example).
+snippet.ui.moveout.description=Use AsyncTask to perform background operations, thus avoiding UI freezes and improving application responsiveness. This helps with Honeycomb (Android 3.0), which enforces the rule "don't do network access in the UI thread" (throws exception).
+snippet.ui.backgroundtaskwithnotification.description=Use AsyncTask to perform a background operation while the user is notified via the status bar.
+snippet.ui.hide.statusbar.description=Hides status bar
+snippet.ui.visible.statusbar.description=Shows status bar (if it was hidden)
+
+# -------------------------
+# Android Intents for basic building blocks
+# -------------------------
+intents.ui=Android Intents
+intents.ui.description=Intent templates to call Activities, Broadcast Receivers, and Services
+
+intents.start.activity.explicitly.label=Start Activity explicitly
+intents.start.activity.explicitly.description=Starts Activity by calling the class (not using id)
+
+intents.start.activity.implicitly.label=Start Activity implicitly
+intents.start.activity.implicitly.description=Starts Activity by using the id, action and category
+
+intents.start.activity.for.result.label=Start Activity for result
+intents.start.activity.for.result.description=Starts Activity and waits for a result
+
+intents.send.broadcast.label=Send Broadcast
+intents.send.broadcast.description=Sends message for broadcast receivers
+
+intents.start.service.label=Start Service
+intents.start.service.description=Binds and starts service
+
+# -------------------------
+# Fragment snippets
+# -------------------------
+
+fragments.ui=Fragment
+fragments.ui.description=Code to manipulate fragments
+
+fragments.replace.transaction.label=Fragment - Replace transaction
+fragments.replace.transaction.description=Replace one fragment by a new one using a animation
+
+fragments.add.transaction.label=Add Fragment programmatically
+fragments.add.transaction.description=Adds one fragment programmatically
+
+# -------------------------
+# Face recognition
+# -------------------------
+category.facerecog=Face recognition
+category.facerecog.description=Basic code for finding faces in a Bitmap
+
+snippet.facerecog.string=Find faces
+
+snippet.facerecog.description=Find faces in Bitmap and how much confidence can you take on them.
diff --git a/src/plugins/snippets/plugin.xml b/src/plugins/snippets/plugin.xml
new file mode 100644
index 0000000..13dd718
--- /dev/null
+++ b/src/plugins/snippets/plugin.xml
@@ -0,0 +1,2086 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?eclipse version="3.2"?>
+<plugin>
+ <extension
+ point="org.eclipse.ui.startup">
+ <startup
+ class="com.motorola.studio.android.codesnippets.AndroidSnippetsStartup"></startup>
+ </extension>
+ <extension point="org.eclipse.wst.common.snippets.SnippetContributions">
+ <category
+ description="%category.service.description"
+ id="android_services"
+ label="%category.service">
+ <item
+ class="com.motorola.studio.android.codesnippets.AndroidPermissionInsertSnippet"
+ description="%snippet.service.systemService00.description"
+ id="com.motorola.studio.android.codesnippets.item3"
+ label="%snippet.service.systemService00">
+ <content>
+ // To use this ActivityManager method, AndroidManifest.xml must have the following permission:
+//&lt;uses-permission android:name=&quot;android.permission.GET_TASKS&quot;/&gt;
+ActivityManager activityManager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
+ </content>
+ </item>
+ <item
+ description="%snippet.service.systemService01.description"
+ id="com.motorola.studio.android.codesnippets.item4"
+ label="%snippet.service.systemService01">
+ <content>
+ AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
+ </content>
+ </item>
+ <item
+ description="%snippet.service.systemService02.description"
+ id="com.motorola.studio.android.codesnippets.item5"
+ label="%snippet.service.systemService02">
+ <content>
+ AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
+ </content>
+ </item>
+ <item
+ description="%snippet.service.systemService03.description"
+ id="com.motorola.studio.android.codesnippets.item6"
+ label="%snippet.service.systemService03">
+ <content>
+ ClipboardManager clipboardManager = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
+ </content>
+ </item>
+ <item
+ class="com.motorola.studio.android.codesnippets.AndroidPermissionInsertSnippet"
+ description="%snippet.service.systemService04.description"
+ id="com.motorola.studio.android.codesnippets.item7"
+ label="%snippet.service.systemService04">
+ <content>
+ // To use this ConectivityManager method, AndroidManifest.xml must have the following permission:
+// &lt;uses-permission android:name=&quot;android.permission.ACCESS_NETWORK_STATE&quot;/&gt;
+ConnectivityManager connectivityManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
+ </content>
+ </item>
+ <item
+ description="%snippet.service.systemService05.description"
+ id="com.motorola.studio.android.codesnippets.item8"
+ label="%snippet.service.systemService05">
+ <content>
+ InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
+ </content>
+ </item>
+ <item
+ description="%snippet.service.systemService06.description"
+ id="com.motorola.studio.android.codesnippets.item9"
+ label="%snippet.service.systemService06">
+ <content>
+ KeyguardManager keyguardManager = (KeyguardManager) getSystemService(Context.KEYGUARD_SERVICE);
+ </content>
+ </item>
+ <item
+ description="%snippet.service.systemService07.description"
+ id="com.motorola.studio.android.codesnippets.item10"
+ label="%snippet.service.systemService07">
+ <content>
+ LayoutInflater layoutInflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ </content>
+ </item>
+ <item
+ description="%snippet.service.systemService08.description"
+ id="com.motorola.studio.android.codesnippets.item11"
+ label="%snippet.service.systemService08">
+ <content>
+ LocationManager locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
+ </content>
+ </item>
+ <item
+ description="%snippet.service.systemService09.description"
+ id="com.motorola.studio.android.codesnippets.item12"
+ label="%snippet.service.systemService09">
+ <content>
+ NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
+ </content>
+ </item>
+ <item
+ class="com.motorola.studio.android.codesnippets.AndroidPermissionInsertSnippet"
+ description="%snippet.service.systemService10.description"
+ id="com.motorola.studio.android.codesnippets.item13"
+ label="%snippet.service.systemService10">
+ <content>
+ // To use this PowerManager method, AndroidManifest.xml must have the following permission:
+// &lt;uses-permission android:name=&quot;android.permission.DEVICE_POWER&quot;/&gt;
+PowerManager powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE);
+ </content>
+ </item>
+ <item
+ description="%snippet.service.systemService11.description"
+ id="com.motorola.studio.android.codesnippets.item14"
+ label="%snippet.service.systemService11">
+ <content>
+ SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE);
+ </content>
+ </item>
+ <item
+ description="%snippet.service.systemService12.description"
+ id="com.motorola.studio.android.codesnippets.item15"
+ label="%snippet.service.systemService12">
+ <content>
+ SensorManager sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
+ </content>
+ </item>
+ <item
+ class="com.motorola.studio.android.codesnippets.AndroidPermissionInsertSnippet"
+ description="%snippet.service.systemService13.description"
+ id="com.motorola.studio.android.codesnippets.item32"
+ label="%snippet.service.systemService13">
+ <content>
+ // To use this TelephonyManager method, AndroidManifest.xml must have the following permission:
+// &lt;uses-permission android:name=&quot;android.permission.READ_PHONE_STATE&quot;/&gt;
+TelephonyManager telephonyManager = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
+ </content>
+ </item>
+ <item
+ class="com.motorola.studio.android.codesnippets.AndroidPermissionInsertSnippet"
+ description="%snippet.service.systemService14.description"
+ id="com.motorola.studio.android.codesnippets.item33"
+ label="%snippet.service.systemService14">
+ <content>
+ // To use this Vibrator method, AndroidManifest.xml must have the following permission:
+// &lt;uses-permission android:name=&quot;android.permission.VIBRATE&quot;/&gt;
+Vibrator vibrator = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);
+ </content>
+ </item>
+ <item
+ class="com.motorola.studio.android.codesnippets.AndroidPermissionInsertSnippet"
+ description="%snippet.service.systemService15.description"
+ id="com.motorola.studio.android.codesnippets.item34"
+ label="%snippet.service.systemService15">
+ <content>
+ // To use this WallpaperService method, AndroidManifest.xml must have the following permission:
+// &lt;uses-permission android:name=&quot;android.permission.SET_WALLPAPER&quot;/&gt;
+WallpaperService wallpaperService = (WallpaperService) getSystemService(Context.WALLPAPER_SERVICE);
+ </content>
+ </item>
+ <item
+ class="com.motorola.studio.android.codesnippets.AndroidPermissionInsertSnippet"
+ description="%snippet.service.systemService16.description"
+ id="com.motorola.studio.android.codesnippets.item35"
+ label="%snippet.service.systemService16">
+ <content>
+ // To use this WifiManager method, AndroidManifest.xml must have the following permission:
+// &lt;uses-permission android:name=&quot;android.permission.ACCESS_WIFI_STATE&quot;/&gt;
+WifiManager wifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE);
+ </content>
+ </item>
+ <item
+ description="%snippet.service.systemService17.description"
+ id="com.motorola.studio.android.codesnippets.item36"
+ label="%snippet.service.systemService17">
+ <content>
+ WindowManager windowManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
+ </content>
+ </item>
+ </category>
+
+
+
+ <category
+ description="%category.general.description"
+ id="android_general"
+ label="%category.general">
+ <item
+ class="com.motorola.studio.android.codesnippets.AndroidPermissionInsertSnippet"
+ description="%snippet.general.sendSMS.description"
+ id="com.motorola.studio.android.codesnippets.item9"
+ label="%snippet.general.sendSMS">
+ <content>
+ // To use these SmsManager methods, AndroidManifest.xml must have the following permission:
+// &lt;uses-permission android:name=&quot;android.permission.SEND_SMS&quot;/&gt;
+SmsManager m = SmsManager.getDefault();
+String destinationNumber =&quot;0123456789&quot;;
+String text = &quot;Hello!&quot;;
+m.sendTextMessage(destinationNumber, null, text, null, null);
+ </content>
+ </item>
+ <item
+ description="%snippet.general.toast.description"
+ id="com.motorola.studio.android.codesnippets.item8"
+ label="%snippet.general.toast">
+ <content>
+ Toast.makeText(this, &quot;Put your message here&quot;, Toast.LENGTH_SHORT).show();
+ </content>
+ </item>
+ <item
+ description="%snippet.general.statusBar.description"
+ id="com.motorola.studio.android.codesnippets.item31"
+ label="%snippet.general.statusBar">
+ <content>
+ int notificationID = 10;
+NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
+// Create the notification
+Notification notification = new Notification(R.drawable.yourIconId, &quot;Put your notification text here&quot;, System.currentTimeMillis());
+// Create the notification's expanded message
+// When the user clicks on it, it opens your activity
+Intent intent = new Intent(this, YourActivityName.class);
+PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, 0);
+notification.setLatestEventInfo(this, &quot;Put your title here&quot;, &quot;Put your text here&quot;, pendingIntent);
+// Show notification
+notificationManager.notify(notificationID, notification);
+ </content>
+ </item>
+ <item
+ class="com.motorola.studio.android.codesnippets.AndroidPermissionInsertSnippet"
+ description="%snippet.general.vibrateTime.description"
+ id="com.motorola.studio.android.codesnippets.item6"
+ label="%snippet.general.vibrateTime">
+ <content>
+ // To use these Vibrator methods, AndroidManifest.xml must have the following permission:
+// &lt;uses-permission android:name=&quot;android.permission.VIBRATE&quot;/&gt;
+// Vibrate for 1 second
+Vibrator vibrator = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);
+vibrator.vibrate(1000);
+ </content>
+ </item>
+ <item
+ class="com.motorola.studio.android.codesnippets.AndroidPermissionInsertSnippet"
+ description="%snippet.general.vibratePattern.description"
+ id="com.motorola.studio.android.codesnippets.item25"
+ label="%snippet.general.vibratePattern">
+ <content>
+// To use these Vibrator methods, AndroidManifest.xml must have the following permission:
+//&lt;uses-permission android:name=&quot;android.permission.VIBRATE&quot;/&gt;
+// Vibrate in a pattern with 0ms off(start immediately), 200ms on, 100ms off, 100ms on, 500ms off, 500ms on,
+// repeating the pattern starting from index 4: 100ms on.
+// Note that you'll have to call vibrator.cancel() in order to stop the vibrator.
+// Change the second parameter to -1 if you want play the pattern only once.
+Vibrator vibrator = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);
+vibrator.vibrate(new long[] {0, 200, 100, 100, 500, 500}, 4);
+ </content>
+ </item>
+ <item
+ class="com.motorola.studio.android.codesnippets.AndroidPermissionInsertSnippet"
+ description="%snippet.general.strictMode.description"
+ id="com.motorola.studio.android.codesnippets.item25"
+ label="%snippet.general.strictMode">
+ <content>
+ // Add or remove any policies and penalties strict mode checking.
+ // This is for application development and testing and should be
+ // removed before your application reaches the market.
+ // For more details of what each method does, check
+ // http://developer.android.com/reference/android/os/StrictMode.ThreadPolicy.Builder.html
+ StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
+ .detectDiskReads()
+ .detectDiskWrites()
+ .detectNetwork() // or .detectAll() for all detectable problems
+ .penaltyDialog()
+ .penaltyLog()
+ .build());
+ StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
+ .detectLeakedSqlLiteObjects()
+ .penaltyLog()
+ .penaltyDeath()
+ .build());
+ // you can also use the penaltyDropBox() as an alternative to displaying a violation occurrence
+ </content>
+ </item>
+ </category>
+
+
+
+
+ <category
+ description="%category.database.description"
+ id="android_database"
+ label="%category.database">
+ <item
+ description="%snippet.database.createOrOpenDb.description"
+ id="com.motorola.studio.android.codesnippets.item29"
+ label="%snippet.database.createOrOpenDb">
+ <content>
+ SQLiteDatabase db =openOrCreateDatabase(&quot;MyDatabaseName&quot;, MODE_PRIVATE, null);
+ </content>
+ </item>
+ <item
+ description="%snippet.database.deleteDb.description"
+ id="com.motorola.studio.android.codesnippets.item5"
+ label="%snippet.database.deleteDb">
+ <content>
+ boolean success = deleteDatabase(&quot;MyDatabaseName&quot;);
+ </content>
+ </item>
+ <item
+ description="%snippet.database.createTbl.description"
+ id="com.motorola.studio.android.codesnippets.item17"
+ label="%snippet.database.createTbl">
+ <content>
+ db.execSQL(&quot;CREATE TABLE MyTableName (_id INTEGER PRIMARY KEY AUTOINCREMENT, YourColumnName TEXT);&quot;);
+ </content>
+ </item>
+ <item
+ description="%snippet.database.deleteTbl.description"
+ id="com.motorola.studio.android.codesnippets.item18"
+ label="%snippet.database.deleteTbl">
+ <content>
+ db.execSQL(&quot;DROP TABLE IF EXISTS MyTableName&quot;);
+ </content>
+ </item>
+ <item
+ description="%snippet.database.insertEntry.description"
+ id="com.motorola.studio.android.codesnippets.item20"
+ label="%snippet.database.insertEntry">
+ <content>
+ // Since SQL doesn&apos;t allow the insertion of a completely empty row, the second parameter of db.insert defines the column that will receive NULL if cv is empty
+ContentValues cv=new ContentValues();
+cv.put(&quot;YourColumnName&quot;, &quot;YourColumnValue&quot;);
+db.insert(&quot;MyTableName&quot;, &quot;YourColumnName&quot;, cv);
+ </content>
+ </item>
+ <item
+ description="%snippet.database.updateEntry.description"
+ id="com.motorola.studio.android.codesnippets.item21"
+ label="%snippet.database.updateEntry">
+ <content>
+ ContentValues cv=new ContentValues();
+cv.put(&quot;YourColumnName&quot;, &quot;YourColumnValue&quot;);
+db.update(&quot;MyTableName&quot;, cv, &quot;_id=?&quot;, new String[]{&quot;1&quot;});
+ </content>
+ </item>
+ <item
+ description="%snippet.database.deleteEntry.description"
+ id="com.motorola.studio.android.codesnippets.item22"
+ label="%snippet.database.deleteEntry">
+ <content>
+ db.delete(&quot;MyTableName&quot;,&quot;_id=?&quot;, new String[]{&quot;1&quot;});
+ </content>
+ </item>
+ <item
+ description="%snippet.database.runQuery.description"
+ id="com.motorola.studio.android.codesnippets.item23"
+ label="%snippet.database.runQuery">
+ <content>
+ Cursor c=db.rawQuery(SQL_COMMAND, null);
+ </content>
+ </item>
+ </category>
+
+
+
+
+
+ <category
+ description="%category.menus.description"
+ id="android_menus"
+ label="%category.menus">
+ <item
+ description="%snippet.menus.handleItems.description"
+ id="com.motorola.studio.android.codesnippets.item3"
+ label="%snippet.menus.handleItems">
+ <content>
+ /*
+* Add this in your Activity
+*/
+private final int MENU_ITEM_0 = 0;
+private final int MENU_ITEM_1 = 1;
+
+
+/**
+ * Add menu items
+ *
+ * @see android.app.Activity#onCreateOptionsMenu(android.view.Menu)
+ */
+public boolean onCreateOptionsMenu(Menu menu) {
+ menu.add(0, MENU_ITEM_0, 0, &quot;Menu Item 0&quot;);
+ menu.add(0, MENU_ITEM_1, 0, &quot;Menu Item 1&quot;);
+ return true;
+}
+
+/**
+ * Define menu action
+ *
+ * @see android.app.Activity#onOptionsItemSelected(android.view.MenuItem)
+ */
+public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case MENU_ITEM_0:
+ // put your code here
+ break;
+ case MENU_ITEM_1:
+ // put your code here
+ break;
+ default:
+ // put your code here
+ }
+ return false;
+}
+ </content>
+ </item>
+ <item
+ description="%snippet.menus.disableItem.description"
+ id="com.motorola.studio.android.codesnippets.item2"
+ label="%snippet.menus.disableItem">
+ <content>
+ menu.findItem(&quot;yourItemId&quot;).setEnabled(false);
+ </content>
+ </item>
+ <item
+ description="%snippet.menus.subItem.description"
+ id="com.motorola.studio.android.codesnippets.item3"
+ label="%snippet.menus.subItem">
+ <content>
+ SubMenu subMenu = menu.addSubMenu(&quot;YourMenu&quot;);
+ subMenu.add(&quot;YourSubMenu1&quot;);
+
+ </content>
+ </item>
+ <item
+ description="%snippet.menus.declareMenuXML.description"
+ id="com.motorola.studio.android.codesnippets.item4"
+ label="%snippet.menus.declareMenuXML">
+ <content>
+ &lt;menu xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;&gt;
+ &lt;item android:id=&quot;@+id/menu_0&quot;
+ android:title=&quot;Menu Item 0&quot; /&gt;
+ &lt;item android:id=&quot;@+id/menu_1&quot;
+ android:title=&quot;Menu Item 1&quot; /&gt;
+&lt;/menu&gt;
+ </content>
+ </item>
+ <item
+ description="%snippet.menus.inflateMenu.description"
+ id="com.motorola.studio.android.codesnippets.item5"
+ label="%snippet.menus.inflateMenu">
+ <content>
+ /**
+* Inflate Menu from XML
+*/
+public boolean onCreateOptionsMenu(Menu menu) {
+ super.onCreateOptionsMenu(menu);
+ MenuInflater inflater = getMenuInflater();
+ inflater.inflate(R.menu.yourXMLName, menu);
+ return true;
+}
+ </content>
+ </item>
+ <item
+ description="%snippet.menus.showActionBar.description"
+ id="com.motorola.studio.android.codesnippets.item6"
+ label="%snippet.menus.showActionBar">
+ <content>
+ //Notes:
+// - The Action Bar was introduced in API level 11.
+// If you set minSDKVersion &lt; 11 or targetSDKVersion &lt; 11 or
+// if you do not set any of these, getActionBar() will return null.
+// - If you removed the Action Bar using a theme,
+// getActionBar() will return null.
+if (getActionBar() != null){
+ getActionBar().show();
+}
+ </content>
+ </item>
+ <item
+ description="%snippet.menus.hideActionBar.description"
+ id="com.motorola.studio.android.codesnippets.item7"
+ label="%snippet.menus.hideActionBar">
+ <content>
+ //Notes:
+// - The Action Bar was introduced in API level 11.
+// If you set minSDKVersion &lt; 11 or targetSDKVersion &lt; 11 or
+// if you do not set any of these, getActionBar() will return null.
+// - If you removed the Action Bar using a theme,
+// getActionBar() will return null.
+if (getActionBar() != null){
+ getActionBar().hide();
+}
+ </content>
+ </item>
+ <item
+ description="%snippet.menus.toggle.actionbar.description"
+ id="com.motorola.studio.android.codesnippets.item9"
+ label="%snippet.menus.toggle.actionbar.label">
+ <content>
+ //Toggles action bar state (If visible, hides it. If hidden, shows it)
+ActionBar bar = getActionBar();
+if (bar != null) {
+ if (bar.isShowing()) {
+ bar.hide();
+ }
+ else {
+ bar.show();
+ }
+}
+ </content>
+ </item>
+ <item
+ description="%snippet.menus.manage.actionbar.description"
+ id="com.motorola.studio.android.codesnippets.item8"
+ label="%snippet.menus.manage.actionbar.label">
+ <content>
+ //Sets the action bar navigation mode
+ActionBar actionBar = getActionBar();
+actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
+ </content>
+ </item>
+ </category>
+ <category
+ description="%category.dialogs.description"
+ id="android_dialogs"
+ label="%category.dialogs">
+ <item
+ description="%snippet.dialogs.alert.description"
+ id="com.motorola.studio.android.codesnippets.item26"
+ label="%snippet.dialogs.alert">
+ <content>
+ AlertDialog.Builder builder = new AlertDialog.Builder(this);
+builder.setMessage(&quot;Put your question here?&quot;)
+ .setCancelable(false)
+ .setPositiveButton(&quot;Yes&quot;, new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int id) {
+ // put your code here
+ }
+ })
+ .setNegativeButton(&quot;No&quot;, new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int id) {
+ // put your code here
+ dialog.cancel();
+ }
+ });
+AlertDialog alertDialog = builder.create();
+alertDialog.show();
+ </content>
+ </item>
+ <item
+ description="%snippet.dialogs.progressDialog"
+ id="com.motorola.studio.android.codesnippets.item27"
+ label="%snippet.dialogs.progressDialog">
+ <content>
+ ProgressDialog dialog = ProgressDialog.show(this, &quot;Your Title&quot;, &quot;Put your message here&quot;, true);
+ </content>
+ </item>
+ <item
+ description="%snippet.dialogs.progressBar.description"
+ id="com.motorola.studio.android.codesnippets.item29"
+ label="%snippet.dialogs.progressBar">
+ <content>
+ ProgressDialog progressDialog = new ProgressDialog(this);
+progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
+progressDialog.setMax(PROGRESS_MAX);
+progressDialog.setMessage(&quot;Put your message here&quot;);
+progressDialog.setCancelable(false);
+progressDialog.incrementProgressBy(PROGRESS_INCREMENT);
+ </content>
+ </item>
+ <item
+ description="%snippet.dialogs.datePicker.description"
+ id="com.motorola.studio.android.codesnippets.item28"
+ label="%snippet.dialogs.datePicker">
+ <content>
+ // Define the date picker dialog listener, which will be called after
+// the user picks a date in the displayed dialog
+DatePickerDialog.OnDateSetListener datePickerDialogListener =
+ new DatePickerDialog.OnDateSetListener() {
+
+ public void onDateSet(DatePicker view, int year,
+ int monthOfYear, int dayOfMonth) {
+ // put your code here
+ // update your model/view with the date selected by the user
+ }
+ };
+
+// Get the current date
+Calendar calendar = Calendar.getInstance();
+int year = calendar.get(Calendar.YEAR);
+int month = calendar.get(Calendar.MONTH);
+int day = calendar.get(Calendar.DAY_OF_MONTH);
+
+// Create a date picker dialog
+DatePickerDialog datePickerDialog = new DatePickerDialog(this,
+ datePickerDialogListener,
+ year, month, day);
+
+// Display the date picker dialog
+datePickerDialog.show();
+
+ </content>
+ </item>
+ <item
+ description="%snippet.dialogs.timePicker.description"
+ id="com.motorola.studio.android.codesnippets.item25"
+ label="%snippet.dialogs.timePicker">
+ <content>
+ // Define the date picker dialog listener, which will be called after
+// the user picks a time in the displayed dialog
+TimePickerDialog.OnTimeSetListener timePickerDialogListener =
+ new TimePickerDialog.OnTimeSetListener() {
+ public void onTimeSet(TimePicker view, int hourOfDay, int minute) {
+ // put your code here
+ // update your model/view with the date selected by the user
+ }
+ };
+
+// Get the current time
+Calendar c = Calendar.getInstance();
+int hour = c.get(Calendar.HOUR_OF_DAY);
+int minute = c.get(Calendar.MINUTE);
+
+// Create a time picker dialog
+TimePickerDialog timerPickerDialog = new TimePickerDialog(this,
+ timePickerDialogListener, hour, minute, false);
+
+// Display the time picker dialog
+timerPickerDialog.show();
+
+ </content>
+ </item>
+ <item
+ description="%snippet.dialogs.custom.description"
+ id="com.motorola.studio.android.codesnippets.item30"
+ label="%snippet.dialogs.custom">
+ <content>
+ Dialog dialog = new Dialog(this);
+dialog.setContentView(R.layout.yourLayoutId);
+dialog.show();
+
+ </content>
+ </item>
+ <item
+ id="com.motorola.studio.android.codesnippets.item9"
+ label="%snippet.dialogs.customAlert">
+ <content>
+ LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+View layout = inflater.inflate(R.layout.yourLayoutId, (ViewGroup) findViewById(R.id.yourLayoutRoot));
+AlertDialog.Builder builder = new AlertDialog.Builder(this)
+ .setView(layout);
+AlertDialog alertDialog = builder.create();
+alertDialog.show();
+
+ </content>
+ </item>
+ </category>
+
+
+
+
+ <category
+ description="%category.screen.description"
+ id="android_screen"
+ label="%category.screen">
+ <item
+ description="%snippet.screen.fullscreen.description"
+ id="com.motorola.studio.android.codesnippets.item5"
+ label="%snippet.screen.fullscreen">
+ <content>
+ getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
+ </content>
+ </item>
+ <item
+ description="%snippet.screen.size.description"
+ id="com.motorola.studio.android.codesnippets.item24"
+ label="%snippet.screen.size">
+ <content>
+ Display display = ((WindowManager) getSystemService(WINDOW_SERVICE)).getDefaultDisplay();
+int width = display.getWidth();
+int height = display.getHeight();
+ </content>
+ </item>
+ <item
+ description="%snippet.screen.orientation.description"
+ id="com.motorola.studio.android.codesnippets.item1"
+ label="%snippet.screen.orientation">
+ <content>
+ Display display = ((WindowManager) getSystemService(WINDOW_SERVICE)).getDefaultDisplay();
+int orientation = display.getOrientation();
+ </content>
+ </item>
+ </category>
+ <category
+ description="%category.gps.description"
+ id="android_gps"
+ label="%category.gps">
+ <item
+ class="com.motorola.studio.android.codesnippets.AndroidPermissionInsertSnippet"
+ description="%snippet.gps.currentCoordinates.description"
+ id="com.motorola.studio.android.codesnippets.item6"
+ label="%snippet.gps.currentCoordinates">
+ <content>
+ // To use these LocationManager methods, AndroidManifest.xml must have the following permission:
+// &lt;uses-permission android:name=&quot;android.permission.ACCESS_FINE_LOCATION&quot;/&gt;
+LocationManager locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
+locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, new LocationListener() {
+ public void onStatusChanged(String provider, int status, Bundle extras) {
+ // called when the location provider status changes. Possible status: OUT_OF_SERVICE, TEMPORARILY_UNAVAILABLE or AVAILABLE.
+ }
+ public void onProviderEnabled(String provider) {
+ // called when the location provider is enabled by the user
+ }
+ public void onProviderDisabled(String provider) {
+ // called when the location provider is disabled by the user. If it is already disabled, it&apos;s called immediately after requestLocationUpdates
+ }
+
+ public void onLocationChanged(Location location) {
+ double latitute = location.getLatitude();
+ double longitude = location.getLongitude();
+ // do whatever you want with the coordinates
+ }
+});
+ </content>
+ </item>
+ <item
+ class="com.motorola.studio.android.codesnippets.AndroidPermissionInsertSnippet"
+ description="%snippet.gps.lastCoordinates.description"
+ id="com.motorola.studio.android.codesnippets.item4"
+ label="%snippet.gps.lastCoordinates">
+ <content>
+ // To use these LocationManager methods, AndroidManifest.xml must have the following permission:
+// &lt;uses-permission android:name=&quot;android.permission.ACCESS_FINE_LOCATION&quot;/&gt;
+LocationManager locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
+Location location = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);
+double latitute, longitude = 0;
+if(location != null){
+ latitute = location.getLatitude();
+ longitude = location.getLongitude();
+}
+
+ </content>
+ </item>
+ <item
+ description="%snippet.gps.distance.description"
+ id="com.motorola.studio.android.codesnippets.item7"
+ label="%snippet.gps.distance">
+ <content>
+ Location originLocation = new Location(&quot;gps&quot;);
+Location destinationLocation = new Location(&quot;gps&quot;);
+originLocation.setLatitude(originLatitude);
+originLocation.setLongitude(originLongitude);
+destinationLocation.setLatitude(originLatitude);
+destinationLocation.setLongitude(originLongitude);
+float distance = originLocation.distanceTo(destinationLocation);
+
+ </content>
+ </item>
+ <item
+ class="com.motorola.studio.android.codesnippets.AndroidPermissionInsertSnippet"
+ description="%snippet.gps.listenerChanges.description"
+ id="com.motorola.studio.android.codesnippets.item1"
+ label="%snippet.gps.listenerChanges">
+ <content>
+ // To use these LocationManager methods, AndroidManifest.xml must have the following permission:
+// &lt;uses-permission android:name=&quot;android.permission.ACCESS_FINE_LOCATION&quot;/&gt;
+LocationManager locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
+locationManager.addGpsStatusListener(new GpsStatus.Listener(){
+
+ public void onGpsStatusChanged(int event) {
+ switch(event){
+ // Event sent when the GPS system has started
+ case GpsStatus.GPS_EVENT_STARTED:
+ // put your code here
+ break;
+
+ // Event sent when the GPS system has stopped
+ case GpsStatus.GPS_EVENT_STOPPED:
+ // put your code here
+ break;
+
+ // Event sent when the GPS system has received its first fix since starting
+ case GpsStatus.GPS_EVENT_FIRST_FIX:
+ // put your code here
+ break;
+
+ // Event sent periodically to report GPS satellite status
+ case GpsStatus.GPS_EVENT_SATELLITE_STATUS:
+ // put your code here
+ break;
+
+ }
+ }
+});
+ </content>
+ </item>
+ <item
+ class="com.motorola.studio.android.codesnippets.AndroidPermissionInsertSnippet"
+ description="%snippet.gps.listenerProximity.description"
+ id="com.motorola.studio.android.codesnippets.item2"
+ label="%snippet.gps.listenerProximity">
+ <content>
+ // To use these LocationManager methods, AndroidManifest.xml must have the following permission:
+// &lt;uses-permission android:name=&quot;android.permission.ACCESS_FINE_LOCATION&quot;/&gt;
+// Use PendingIntent.getActivity(Context, int, Intent, int), PendingIntent.getBroadcast(Context, int, Intent, int) or PendingIntent.getService(Context, int, Intent, int) to create the PendingIntent, which will be used to generate an Intent to fire when the proximity condition is satisfied.
+PendingIntent pendingIntent;
+// latitude the latitude of the central point of the alert region
+// longitude the longitude of the central point of the alert region
+// radius the radius of the alert region, in meters
+LocationManager locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
+locationManager.addProximityAlert(latitude, longitude, radius, -1, pendingIntent);
+ </content>
+ </item>
+ </category>
+ <category
+ description="%category.media.description"
+ id="android_media"
+ label="%category.media">
+ <item
+ description="%snippet.media.playAudioVideoRaw.description"
+ id="com.motorola.studio.android.codesnippets.item17"
+ label="%snippet.media.playAudioVideoRaw">
+ <content>
+ // Put the media file into the res/raw folder of your application
+MediaPlayer mp = MediaPlayer.create(this, R.raw.yourSoundId);
+mp.start();
+ </content>
+ </item>
+ <item
+ description="%snippet.media.playAudioVideoFile.description"
+ id="com.motorola.studio.android.codesnippets.item18"
+ label="%snippet.media.playAudioVideoFile">
+ <content>
+ MediaPlayer mp = new MediaPlayer();
+mp.setDataSource(FILE_PATH_OR_URL);
+mp.prepare();
+mp.start();
+ </content>
+ </item>
+ <item
+ class="com.motorola.studio.android.codesnippets.AndroidPermissionInsertSnippet"
+ description="%snippet.media.startRecordingAudio.description"
+ id="com.motorola.studio.android.codesnippets.item16"
+ label="%snippet.media.startRecordingAudio">
+ <content>
+ // To use these MediaRecorder methods, AndroidManifest.xml must have the following permission:
+// &lt;uses-permission android:name=&quot;android.permission.RECORD_AUDIO&quot;/&gt;
+MediaRecorder recorder = new MediaRecorder();
+recorder.setAudioSource(MediaRecorder.AudioSource.MIC);
+recorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
+recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
+recorder.setOutputFile(PATH_NAME); // The file must already exist
+recorder.prepare();
+recorder.start();
+ </content>
+ </item>
+ <item
+ description="%snippet.media.stopRecording.description"
+ id="com.motorola.studio.android.codesnippets.item21"
+ label="%snippet.media.stopRecording">
+ <content>
+ recorder.stop();
+recorder.release();
+ </content>
+ </item>
+ </category>
+ <category
+ description="%category.motorolaapi.description"
+ id="android_motorolaapi"
+ label="%category.motorolaapi">
+ <item
+ class="com.motorola.studio.android.codesnippets.AndroidPermissionInsertSnippet"
+ description="%snippet.motorolaapi.ffcamera.description"
+ id="com.motorola.studio.android.codesnippets.motorolaapi1"
+ label="%snippet.motorolaapi.ffcamera">
+ <content>
+ // To use these Camera methods, AndroidManifest.xml must have the following permission:
+// &lt;uses-permission android:name=&quot;android.permission.CAMERA&quot;/&gt;
+// Add an import com.motorola.hardware.frontcamera.FrontCamera; statement
+// Add an import android.hardware.Camera; statement
+// Note that you need to add a Motorola library to make this work.
+// See more details at:
+// https://developer.motorola.com/docstools/library/motorola-front-facing-camera-api/
+
+Camera frontCam = FrontCamera.getFrontCamera();
+// the frontCam object represents the front-facing camera
+// treat it like any other Camera object
+frontCam.release(); // always release when done with the Camera object
+ </content>
+ </item>
+ <item description="%snippet.motorolaapi.hdmi.description"
+ id="com.motorola.studio.android.codesnippets.motorolaapi2"
+ label="%snippet.motorolaapi.hdmi">
+ <content>
+// Insert the code below into the onReceive method of a BroadcastReceiver class
+// This broadcast receiver must have the following intent filter:
+// &lt;intent-filter&gt;
+// &lt;action android:name="com.motorola.intent.action.externaldisplaystate"/&gt;
+// &lt;/intent-filter&gt;
+// More details about Motorola HDMI Status API are available at:
+// https://developer.motorola.com/docstools/library/motorola-hdmi-status-api/
+final String EXTDISP_PUBLIC_STATE = "com.motorola.intent.action.externaldisplaystate";
+final String EXTRA_HDCP = "";
+final String EXTRA_HDMI = "";
+
+String action = intent.getAction();
+Bundle extras = (intent != null) ? intent.getExtras() : null;
+
+if (action.equals(EXTDISP_PUBLIC_STATE))
+{
+ if (extras != null)
+ {
+ int hdmi = extras.getInt(EXTRA_HDMI);
+ int hdcp = extras.getInt(EXTRA_HDCP);
+ // Application decides what to do with this information
+ }
+}
+ </content>
+ </item>
+ </category>
+ <category
+ description="%category.preferences.description"
+ id="com.motorola.studio.android.codesnippets.category2"
+ label="%category.preferences">
+ <item
+ description="%snippet.preferences.loadSharedPreference.description"
+ id="com.motorola.studio.android.codesnippets.item5"
+ label="%snippet.preferences.loadSharedPreference">
+ <content>
+// Use MODE_WORLD_READABLE and/or MODE_WORLD_WRITEABLE to grant access to other applications
+SharedPreferences preferences = getSharedPreferences(&quot;YourPreferencesName&quot;, MODE_PRIVATE);
+String preferenceValue = preferences.getString(&quot;yourPreferenceKey&quot;, &quot;defaultValue&quot;);
+ </content>
+ </item>
+ <item
+ description="%snippet.preferences.loadSharedPreferenceFromPreferenceManager.description"
+ id="com.motorola.studio.android.codesnippets.item8"
+ label="%snippet.preferences.loadSharedPreferenceFromPreferenceManager">
+ <content>
+// Use MODE_WORLD_READABLE and/or MODE_WORLD_WRITEABLE to grant access to other applications
+SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
+String preferenceValue = preferences.getString(&quot;yourPreferenceKey&quot;, &quot;defaultValue&quot;);
+ </content>
+ </item>
+ <item
+ description="%snippet.preferences.writeSharedPreference.description"
+ id="com.motorola.studio.android.codesnippets.item7"
+ label="%snippet.preferences.writeSharedPreference">
+ <content>
+// Use MODE_WORLD_READABLE and/or MODE_WORLD_WRITEABLE to grant access to other applications
+SharedPreferences preferences = getSharedPreferences(&quot;YourPreferencesName&quot;, MODE_PRIVATE);
+SharedPreferences.Editor editor = preferences.edit();
+editor.putString(&quot;yourPreferenceKey&quot;, &quot;Your Preference Value&quot;);
+editor.commit();
+ </content>
+ </item>
+ <item
+ description="%snippet.preferences.writeSharedPreferenceFromPreferenceManager.description"
+ id="com.motorola.studio.android.codesnippets.item5"
+ label="%snippet.preferences.writeSharedPreferenceFromPreferenceManager">
+ <content>
+// Use MODE_WORLD_READABLE and/or MODE_WORLD_WRITEABLE to grant access to other applications
+SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
+SharedPreferences.Editor editor = preferences.edit();
+editor.putString(&quot;yourPreferenceKey&quot;, &quot;Your Preference Value&quot;);
+editor.commit();
+ </content>
+ </item>
+ </category>
+ <category
+ description="%category.resources.description"
+ id="android_resources"
+ label="%category.resources">
+ <item
+ description="%snippet.resources.listAssets.description"
+ id="com.motorola.studio.android.codesnippets.item24"
+ label="%snippet.resources.listAssets">
+ <content>
+ String fileArray[] = this.getAssets().list(&quot;&quot;);
+ </content>
+ </item>
+ <item
+ description="%snippet.resources.readFile.description"
+ id="com.motorola.studio.android.codesnippets.item22"
+ label="%snippet.resources.readFile">
+ <content>
+ InputStream is = this.getAssets().open(fileName);
+ </content>
+ </item>
+ <item
+ description="%snippet.resources.readXmlFile.description"
+ id="com.motorola.studio.android.codesnippets.item23"
+ label="%snippet.resources.readXmlFile">
+ <content>
+ XmlResourceParser xmlParser = this.getAssets().openXmlResourceParser(fileName);
+
+ </content>
+ </item>
+ </category>
+
+
+
+ <category
+ description="%category.log.description"
+ id="android_log"
+ label="%category.log">
+ <item
+ description="%snippet.log.error.description"
+ id="com.motorola.studio.android.codesnippets.item16"
+ label="%snippet.log.error">
+ <content>
+ Log.e(&quot;YourIdentifier&quot;,&quot;This is an error message&quot;);
+ </content>
+ </item>
+ <item
+ description="%snippet.log.warning.description"
+ id="com.motorola.studio.android.codesnippets.item26"
+ label="%snippet.log.warning">
+ <content>
+ Log.w(&quot;YourIdentifier&quot;,&quot;This is a warning message&quot;);
+ </content>
+ </item>
+ <item
+ description="%snippet.log.info.description"
+ id="com.motorola.studio.android.codesnippets.item27"
+ label="%snippet.log.info">
+ <content>
+ Log.i(&quot;YourIdentifier&quot;,&quot;This is an information message&quot;);
+ </content>
+ </item>
+ <item
+ description="%snippet.log.debug.description"
+ id="com.motorola.studio.android.codesnippets.item28"
+ label="%snippet.log.debug">
+ <content>
+ Log.d(&quot;YourIdentifier&quot;,&quot;This is a debug message&quot;);
+ </content>
+ </item>
+ </category>
+ <category
+ description="%category.localization.description"
+ id="android_localization"
+ label="%category.localization">
+ <item
+ description="%snippet.localization.string.description"
+ id="com.motorola.studio.android.codesnippets.item1"
+ label="%snippet.localization.string">
+ <content>
+ String text = getResources().getString(R.string.yourStringID);
+ </content>
+ </item>
+ <item
+ description="%snippet.localization.drawable.description"
+ id="com.motorola.studio.android.codesnippets.item2"
+ label="%snippet.localization.drawable">
+ <content>
+ Drawable drawable = getResources().getDrawable(R.drawable.yourDrawableID);
+ </content>
+ </item>
+ <item
+ description="%snippet.localization.curLocale.description"
+ id="com.motorola.studio.android.codesnippets.item4"
+ label="%snippet.localization.curLocale">
+ <content>
+ Locale curLocale = java.util.Locale.getDefault();
+ </content>
+ </item>
+ <item
+ description="%snippet.localization.resLocale.description"
+ id="com.motorola.studio.android.codesnippets.item5"
+ label="%snippet.localization.resLocale">
+ <content>
+ Locale resLocale = getResources().getConfiguration().locale;
+ </content>
+ </item>
+ <item
+ description="%snippet.localization.dynamicMsg.description"
+ id="com.motorola.studio.android.codesnippets.item3"
+ label="%snippet.localization.dynamicMsg">
+ <content>
+ // yourMsgID =&quot;The language is {0}&quot;
+String msg = getResources().getString(R.string.yourMsgID);
+Object[] arguments = {
+ java.util.Locale.getDefault().getDisplayLanguage()
+};
+String fullMsg = java.text.MessageFormat.format(msg, arguments);
+ </content>
+ </item>
+ </category>
+ <category
+ description="%category.bluetooth.description"
+ id="android_bluetooth"
+ label="%category.bluetooth">
+ <item
+ class="com.motorola.studio.android.codesnippets.AndroidPermissionInsertSnippet"
+ description="%snippet.bluetooth.check.description"
+ id="com.motorola.studio.android.codesnippets.bt1"
+ label="%snippet.bluetooth.check">
+ <content>
+ // To use this BluetoothAdapter method, AndroidManifest.xml must have the following permission:
+//&lt;uses-permission android:name=&quot;android.permission.BLUETOOTH&quot;/&gt;
+// Trying to get the adapter
+BluetoothAdapter btAdapter = BluetoothAdapter.getDefaultAdapter();
+
+if (btAdapter == null) {
+ // Bluetooth is not supported, do something here to warn the user
+ return;
+}
+ </content>
+ </item>
+ <item
+ class="com.motorola.studio.android.codesnippets.AndroidPermissionInsertSnippet"
+ description="%snippet.bluetooth.connect.description"
+ id="com.motorola.studio.android.codesnippets.bt7"
+ label="%snippet.bluetooth.connect">
+ <content>
+ // To use these BluetoothAdapter methods, AndroidManifest.xml must have the following permission:
+//&lt;uses-permission android:name=&quot;android.permission.BLUETOOTH&quot;/&gt;
+/*
+ * This code is loose here but you will
+ * likely use it inside a thread
+ *
+ * Make sure you have the &apos;device&apos; variable (BluetoothDevice)
+ * at the point you insert this code
+ */
+
+// UUID for your application
+UUID MY_UUID = UUID.fromString(&quot;yourdata&quot;);
+
+// Get the adapter
+BluetoothAdapter btAdapter = BluetoothAdapter.getDefaultAdapter();
+
+// The socket
+BluetoothSocket socket = null;
+try {
+ // Your app UUID string (is also used by the server)
+ socket = device.createRfcommSocketToServiceRecord(MY_UUID);
+} catch (IOException e) { }
+
+// For performance reasons
+btAdapter.cancelDiscovery();
+
+try {
+ // Be aware that this is a blocking operation. You probably want to use this in a thread
+ socket.connect();
+
+} catch (IOException connectException) {
+ // Unable to connect; close the socket and get out
+ try {
+ socket.close();
+ } catch (IOException closeException) {
+ // Deal with it
+ }
+ return;
+}
+
+// Now manage your connection (in a separate thread)
+myConnectionManager(socket);
+ </content>
+ </item>
+ <item
+ class="com.motorola.studio.android.codesnippets.AndroidPermissionInsertSnippet"
+ description="%snippet.bluetooth.enable.description"
+ id="com.motorola.studio.android.codesnippets.bt2"
+ label="%snippet.bluetooth.enable">
+ <content>
+ // To use these BluetoothAdapter methods, AndroidManifest.xml must have the following permission:
+//&lt;uses-permission android:name=&quot;android.permission.BLUETOOTH&quot;/&gt;
+/* This number is used to identify this request (&quot;Enable Bluetooth&quot;)
+ * when the callback method onActivityResult() is called. Your
+ * interaction with the Bluetooth stack will probably start there.
+ *
+ * You probably want to insert this as a global variable
+ */
+int ENABLE_BLUETOOTH = 1;
+
+// Get the adapter
+BluetoothAdapter btAdapter = BluetoothAdapter.getDefaultAdapter();
+
+if (btAdapter == null) {
+ return;
+}
+
+// If Bluetooth is not yet enabled, enable it
+if (!btAdapter.isEnabled()) {
+ Intent enableBluetooth = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
+ startActivityForResult(enableBluetooth, ENABLE_BLUETOOTH);
+ // Now implement the onActivityResult() and wait for it to be invoked with ENABLE_BLUETOOTH
+}
+ </content>
+ </item>
+ <item
+ class="com.motorola.studio.android.codesnippets.AndroidPermissionInsertSnippet"
+ description="%snippet.bluetooth.getpaired.description"
+ id="com.motorola.studio.android.codesnippets.bt4"
+ label="%snippet.bluetooth.getpaired">
+ <content>
+ // To use these BluetoothAdapter methods, AndroidManifest.xml must have the following permission:
+//&lt;uses-permission android:name=&quot;android.permission.BLUETOOTH&quot;/&gt;
+// Get the adapter
+BluetoothAdapter btAdapter = BluetoothAdapter.getDefaultAdapter();
+
+// Get the paired devices
+Set&lt;BluetoothDevice&gt; devices = btAdapter.getBondedDevices();
+
+// If there are paired devices, do whatever you&apos;re supposed to do
+if (devices.size() &gt; 0) {
+ for (BluetoothDevice pairedDevice : devices) {
+ // do something useful with the device
+ }
+}
+ </content>
+ </item>
+ <item
+ class="com.motorola.studio.android.codesnippets.AndroidPermissionInsertSnippet"
+ description="%snippet.bluetooth.discoverable.description"
+ id="com.motorola.studio.android.codesnippets.bt3"
+ label="%snippet.bluetooth.discoverable">
+ <content>
+ // To use these BluetoothAdapter methods, AndroidManifest.xml must have the following permission:
+//&lt;uses-permission android:name=&quot;android.permission.BLUETOOTH&quot;/&gt;
+//Get the adapter
+BluetoothAdapter btAdapter = BluetoothAdapter.getDefaultAdapter();
+
+if (btAdapter == null) {
+ return;
+}
+
+// If Bluetooth is not discoverable, make it discoverable
+if (btAdapter.getScanMode() != BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE) {
+ Intent makeDiscoverable = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
+ makeDiscoverable.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 500);
+ // In a real situation you would probably use startActivityForResult to get the user&apos;s choice.
+ startActivity(makeDiscoverable);
+}
+ </content>
+ </item>
+ <item
+ class="com.motorola.studio.android.codesnippets.AndroidPermissionInsertSnippet"
+ description="%snippet.bluetooth.incoming.description"
+ id="com.motorola.studio.android.codesnippets.bt6"
+ label="%snippet.bluetooth.incoming">
+ <content>
+ // To use these BluetoothAdapter methods, AndroidManifest.xml must have the following permission:
+//&lt;uses-permission android:name=&quot;android.permission.BLUETOOTH&quot;/&gt;
+// UUID for your application
+UUID MY_UUID = UUID.fromString(&quot;yourdata&quot;);
+
+// SDP record name used when creating the server socket
+String NAME = &quot;BluetoothExample&quot;;
+
+// The server socket
+BluetoothServerSocket btServerSocket = null;
+
+// The adapter
+BluetoothAdapter btAdapter = BluetoothAdapter.getDefaultAdapter();
+
+// The socket
+BluetoothSocket socket = null;
+
+try {
+ btServerSocket = btAdapter.listenUsingRfcommWithServiceRecord(NAME, MY_UUID);
+
+ // This operation is blocking: you will wait until it returns or an error occurs
+ socket = btServerSocket.accept();
+} catch (IOException e) {
+ // Deal with it
+}
+
+if (socket != null) {
+ /* The connection was accepted. Do what you want to do.
+ * For example, get the streams
+ */
+ InputStream inputStream = null;
+ OutputStream outputStream = null;
+ try {
+ inputStream = socket.getInputStream();
+ outputStream = socket.getOutputStream();
+ } catch (IOException e) {
+ // Deal with it
+ }
+}
+ </content>
+ </item>
+ <item
+ class="com.motorola.studio.android.codesnippets.AndroidPermissionInsertSnippet"
+ description="%snippet.bluetooth.discover.description"
+ id="com.motorola.studio.android.codesnippets.bt5"
+ label="%snippet.bluetooth.discover">
+ <content>
+ // To use these BluetoothDevice methods, AndroidManifest.xml must have the following permission:
+//&lt;uses-permission android:name=&quot;android.permission.BLUETOOTH&quot;/&gt;
+
+// BroadcastReceiver that is notified as each device is found and when the discovery process completes
+// Should be an internal class or in a separate .java file
+final BroadcastReceiver bluetoothReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+
+ // Device was discovered
+ if (BluetoothDevice.ACTION_FOUND.equals(action)) {
+ BluetoothDevice device =
+intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
+ if (device.getBondState() != BluetoothDevice.BOND_BONDED) {
+ // device is not already paired. Do something useful here.
+ }
+ // Discovery is finished
+ } else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) {
+ // do something useful here
+ }
+ }
+};
+
+// Register for notification upon discovery of a Bluetooth device
+IntentFilter intentFilter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
+this.registerReceiver(bluetoothReceiver, intentFilter);
+
+// Register for notification upon completion of the device discovery process
+intentFilter = new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
+this.registerReceiver(bluetoothReceiver, intentFilter);
+
+ </content>
+ </item>
+ </category>
+ <category
+ description="%category.sql.description"
+ id="android_sql"
+ label="%category.sql">
+ <item
+ description="%snippet.sql.create.description"
+ id="com.motorola.studio.android.codesnippets.sql1"
+ label="%snippet.sql.create">
+ <content>
+ CREATE TABLE songs(
+ id INTEGER,
+ name TEXT,
+ genre TEXT,
+ rate REAL,
+ some_raw_data BLOB
+);
+ </content>
+ </item>
+ <item
+ description="%snippet.sql.createfromtable.description"
+ id="com.motorola.studio.android.codesnippets.sql2"
+ label="%snippet.sql.createfromtable">
+ <content>
+ SELECT * INTO rockssongs
+FROM songs WHERE genre =&apos;rock&apos;;
+ </content>
+ </item>
+ <item
+ description="%snippet.sql.count.description"
+ id="com.motorola.studio.android.codesnippets.sql3"
+ label="%snippet.sql.count">
+ <content>
+ SELECT COUNT(DISTINCT student_city) AS total
+FROM students;
+ </content>
+ </item>
+ <item
+ description="%snippet.sql.delete.description"
+ id="com.motorola.studio.android.codesnippets.sql4"
+ label="%snippet.sql.delete">
+ <content>
+DELETE FROM songs
+WHERE rating&lt; 5;
+ </content>
+ </item>
+ <item
+ description="%snippet.sql.drop.description"
+ id="com.motorola.studio.android.codesnippets.sql5"
+ label="%snippet.sql.drop">
+ <content>
+ DROP TABLE&quot;bad_songs&quot;;
+ </content>
+ </item>
+ <item
+ description="%snippet.sql.left.description"
+ id="com.motorola.studio.android.codesnippets.sql6"
+ label="%snippet.sql.left">
+ <content>
+ SELECT students.name
+FROM students LEFT JOIN math_class
+ ON students.id = math_class.student_id
+WHERE math_class.grade &gt; 5;
+ </content>
+ </item>
+ <item
+ description="%snippet.sql.inner.description"
+ id="com.motorola.studio.android.codesnippets.sql7"
+ label="%snippet.sql.inner">
+ <content>
+ SELECT DISTINCT students.id, students.name
+FROM students INNER JOIN math_class ON students.id = math_class.student_id;
+ </content>
+ </item>
+ <item
+ description="%snippet.sql.insert.description"
+ id="com.motorola.studio.android.codesnippets.sql8"
+ label="%snippet.sql.insert">
+ <content>
+ INSERT INTO albums(ambum_id, artist_name, album_name)
+VALUES(&apos;12345&apos;, &apos;David Bowie&apos;,&apos;Ziggy Stardust&apos;);
+ </content>
+ </item>
+ <item
+ description="%snippet.sql.update.description"
+ id="com.motorola.studio.android.codesnippets.sql9"
+ label="%snippet.sql.update">
+ <content>
+ UPDATE songs SET already_listened =&quot;yes&quot;
+WHERE genre = &quot;rock&quot;;
+ </content>
+ </item>
+ </category>
+ <category
+ description="%category.web.description"
+ id="android_web"
+ label="%category.web">
+ <item
+ class="com.motorola.studio.android.codesnippets.AndroidPermissionInsertSnippet"
+ description="%snippet.web.get.description"
+ id="com.motorola.studio.android.codesnippets.web1"
+ label="%snippet.web.get">
+ <content>
+ // To use these Internet methods, AndroidManifest.xml must have the following permission:
+//&lt;uses-permission android:name=&quot;android.permission.INTERNET&quot;/&gt;
+URI myURI = null;
+try {
+ myURI = new URI(&quot;www.webserver.org&quot;);
+} catch (URISyntaxException e) {
+ // Deal with it
+}
+HttpClient httpClient = new DefaultHttpClient();
+HttpGet getMethod = new HttpGet(myURI);
+HttpResponse webServerResponse = null;
+try {
+ webServerResponse = httpClient.execute(getMethod);
+} catch (ClientProtocolException e) {
+ // Deal with it
+} catch (IOException e) {
+ // Deal with it
+}
+
+HttpEntity httpEntity = webServerResponse.getEntity();
+
+if (httpEntity != null) {
+ // You have your response; handle it. For instance, with an input
+ // stream
+ // InputStream input = entity.getContent();
+}
+ </content>
+ </item>
+ <item
+ class="com.motorola.studio.android.codesnippets.AndroidPermissionInsertSnippet"
+ description="%snippet.web.post.description"
+ id="com.motorola.studio.android.codesnippets.web2"
+ label="%snippet.web.post">
+ <content>
+ // To use these Internet methods, AndroidManifest.xml must have the following permission:
+//&lt;uses-permission android:name=&quot;android.permission.INTERNET&quot;/&gt;
+// Create the Apache HTTP client and post
+ HttpClient httpclient = new DefaultHttpClient();
+ HttpPost httppost = new HttpPost(&quot;http://www.website.org/service.php&quot;);
+
+ try {
+ // Add data to your post
+ List&lt;NameValuePair&gt; pairs = new ArrayList&lt;NameValuePair&gt;(2);
+ pairs.add(new BasicNameValuePair(&quot;ID&quot;, &quot;VALUE&quot;));
+ pairs.add(new BasicNameValuePair(&quot;string&quot;, &quot;Yeah!&quot;));
+ httppost.setEntity(new UrlEncodedFormEntity(pairs));
+
+ //Finally, execute the request
+ HttpResponse webServerAnswer = httpclient.execute(httppost);
+
+ } catch (ClientProtocolException e) {
+ //Deal with it
+ } catch (IOException e) {
+ //Deal with it
+ }
+ </content>
+ </item>
+ <item
+ class="com.motorola.studio.android.codesnippets.AndroidPermissionInsertSnippet"
+ description="%snippet.web.json.description"
+ id="com.motorola.studio.android.codesnippets.web3"
+ label="%snippet.web.json">
+ <content>
+ // To use these Internet methods, AndroidManifest.xml must have the following permission:
+//&lt;uses-permission android:name=&quot;android.permission.INTERNET&quot;/&gt;
+// The JSON objects
+JSONObject myJSON = null;
+JSONArray names = null;
+JSONArray values = null;
+
+String restWebServerResponse = &quot;TheResponse&quot;;
+try{
+ myJSON = new JSONObject(restWebServerResponse);
+ names = myJSON.names();
+ values = myJSON.toJSONArray(names);
+}
+catch (JSONException e) {
+ // Deal with it
+}
+
+for (int i = 0; i &lt; values.length(); i++) {
+ // Do something with values.getString(i)
+}
+ </content>
+ </item>
+ <item
+ class="com.motorola.studio.android.codesnippets.AndroidPermissionInsertSnippet"
+ description="%snippet.web.rest.description"
+ id="com.motorola.studio.android.codesnippets.web4"
+ label="%snippet.web.rest">
+ <content>
+ // To use these Internet methods, AndroidManifest.xml must have the following permission:
+//&lt;uses-permission android:name=&quot;android.permission.INTERNET&quot;/&gt;
+// This assumes that you have a URL from which to get the answer
+URI myURL = new URI(&quot;www.website.org&quot;);
+
+// The HTTP objects
+HttpClient httpClient = new DefaultHttpClient();
+HttpGet getMethod = new HttpGet(myURL);
+HttpResponse httpResponse;
+
+// The query result
+String result = null;
+
+try {
+ httpResponse = httpClient.execute(getMethod);
+ // You might want to check response.getStatusLine().toString()
+
+ HttpEntity entity = httpResponse.getEntity();
+
+ if (entity != null) {
+ InputStream instream = entity.getContent();
+ BufferedReader reader = new BufferedReader( new InputStreamReader(instream));
+ StringBuilder sb = new StringBuilder();
+ String line = null;
+ try {
+ while ((line = reader.readLine()) != null) {
+ sb.append(line + &quot;\n&quot;);
+ }
+ } catch (IOException e) {
+ // Deal with it
+ } finally {
+ try {
+ instream.close();
+ } catch (IOException e) {
+ // Deal with it
+ }
+ }
+
+ // Handle the result (for instance, get a JSON object
+ // using the &quot;Retrieve JSON from a server response&quot; snippet)
+ handleResult(result);
+ }
+} catch (ClientProtocolException e) {
+ // Deal with it
+} catch (IOException e) {
+ // Deal with it
+}
+ </content>
+ </item>
+ <item
+ class="com.motorola.studio.android.codesnippets.AndroidPermissionInsertSnippet"
+ description="%snippet.web.email.description"
+ id="com.motorola.studio.android.codesnippets.web5"
+ label="%snippet.web.email">
+ <content>
+ // To use these Internet methods, AndroidManifest.xml must have the following permission:
+// &lt;uses-permission android:name=&quot;android.permission.INTERNET&quot;/&gt;
+Intent emailIntent = new Intent(android.content.Intent.ACTION_SEND);
+emailIntent.setType(&quot;plain/text&quot;);
+emailIntent.putExtra(android.content.Intent.EXTRA_EMAIL, new String[]{&quot;to@email.com&quot;});
+emailIntent.putExtra(android.content.Intent.EXTRA_SUBJECT, &quot;Hello!&quot;);
+emailIntent.putExtra(android.content.Intent.EXTRA_TEXT, &quot;Hello!&quot;);
+
+startActivity(Intent.createChooser(emailIntent, &quot;Send mail...&quot;));
+ </content>
+ </item>
+ <item
+ class="com.motorola.studio.android.codesnippets.AndroidPermissionInsertSnippet"
+ description="%snippet.web.urlconnection.description"
+ id="com.motorola.studio.android.codesnippets.web6"
+ label="%snippet.web.urlconnection">
+ <content>
+ // To use these Internet methods, AndroidManifest.xml must have the following permission:
+//&lt;uses-permission android:name=&quot;android.permission.INTERNET&quot;/&gt;
+// The data that is retrieved
+String result = null;
+
+try {
+ // This assumes that you have a URL from which the response will come
+ URL url = new URL(&quot;www.webaddress.org&quot;);
+
+ // Open a connection to the URL and obtain a buffered input stream
+ URLConnection connection = url.openConnection();
+ InputStream inputStream = connection.getInputStream();
+ BufferedInputStream bufferedInput = new BufferedInputStream(inputStream);
+
+ // Read the response into a byte array
+ ByteArrayBuffer byteArray = new ByteArrayBuffer(50);
+ int current = 0;
+ while((current = bufferedInput.read()) != -1){
+ byteArray.append((byte)current);
+ }
+
+ // Construct a String object from the byte array containing the response
+ result = new String(byteArray.toByteArray());
+} catch (Exception e) {
+
+}
+
+// Handle the result
+handleResult(result);
+ </content>
+ </item>
+ <item
+ class="com.motorola.studio.android.codesnippets.AndroidPermissionInsertSnippet"
+ description="%snippet.web.soap.description"
+ id="com.motorola.studio.android.codesnippets.web7"
+ label="%snippet.web.soap">
+ <content>
+ // To use these Internet methods, AndroidManifest.xml must have the following permission:
+//&lt;uses-permission android:name=&quot;android.permission.INTERNET&quot;/&gt;
+/* This example is intended to be used with the kSOAP
+ * project (http://ksoap2.sourceforge.net/), which
+ * provides some objects to deal with SOAP within
+ * mobile development.
+ *
+ * You must download the kSOAP objects and
+ * have something like this in your import list:
+ * import org.ksoap2.SoapEnvelope; // (and other necessary classes)
+ */
+String SOAP_ACTION = &quot;yourMethod&quot;;
+String METHOD_NAME = &quot;yourMethod&quot;;
+String NAMESPACE = &quot;http://namespace.com/&quot;;
+String URL = &quot;http://server.org&quot;;
+
+SoapObject request = new SoapObject(NAMESPACE, METHOD_NAME);
+request.addProperty(&quot;property1&quot;, &quot;property&quot;);
+
+SoapSerializationEnvelope envelope = new SoapSerializationEnvelope(SoapEnvelope.VER11);
+envelope.setOutputSoapObject(request);
+HttpTransportSE androidHttpTransport = new HttpTransportSE(URL);
+androidHttpTransport.call(SOAP_ACTION, envelope);
+
+Object results = envelope.getResponse();
+
+// Handle the results
+handleResults(results);
+ </content>
+ </item>
+ </category>
+ <category
+ description="%category.sensors.description"
+ id="android_sensor"
+ label="%category.sensors">
+ <item
+ description="%snnipet.sensor.detect.description"
+ id="com.motorola.studio.android.codesnippets.sensor1"
+ label="%snnipet.sensor.detect">
+ <content>
+ // This code requires API level 3 (Android 1.5) or higher
+SensorManager manager = (SensorManager) getSystemService(SENSOR_SERVICE);
+boolean isAccelerometerSupported = true;
+if(manager.getSensorList(Sensor.TYPE_ACCELEROMETER).isEmpty()){
+ isAccelerometerSupported = false;
+}
+ </content>
+ </item>
+ <item
+ description="%snnipet.sensor.getorientation.description"
+ id="com.motorola.studio.android.codesnippets.sensor2"
+ label="%snnipet.sensor.getorientation">
+ <content>
+ // This code requires API level 3 (Android 1.5) or higher
+SensorManager manager;
+manager = (SensorManager) getSystemService(SENSOR_SERVICE);
+manager.registerListener(new SensorEventListener(){
+ public void onAccuracyChanged(Sensor sensor, int accuracy){
+ }
+
+ public void onSensorChanged(SensorEvent event){
+ if(event.sensor.getType() == Sensor.TYPE_ORIENTATION){
+ // The contents of the values[] array depends upon the sensor type.
+ // See the description of the values[] array in the SensorEvent object documentation.
+ float azimuth = Math.round(event.values[0]);
+ float pitch = Math.round(event.values[1]);
+ float roll = Math.round(event.values[2]);
+
+ // Do something with the device orientation values
+ }
+ }},
+ manager.getDefaultSensor(Sensor.TYPE_ORIENTATION),
+ SensorManager.SENSOR_DELAY_NORMAL);
+ </content>
+ </item>
+ </category>
+
+ <category
+ description="%category.ui.description"
+ id="ui"
+ label="%category.ui">
+ <item
+ description="%snippet.ui.gestures.description"
+ id="com.motorola.studio.android.codesnippets.ui1"
+ label="%snippet.ui.gestures.string">
+ <content>
+private static final float DISTANCE_DIP = 16.0f;
+private static final float PATH_DIP = 40.0f;
+// convert dip measurements to pixels
+final float scale = getResources().getDisplayMetrics().density;
+int scaledDistance = (int) (DISTANCE_DIP * scale + 0.5f);
+int scaledPath = (int) (PATH_DIP * scale + 0.5f);
+// For more information about touch gestures and screens support, see:
+// http://developer.android.com/resources/articles/gestures.html
+// http://developer.android.com/reference/android/gesture/package-summary.html
+// http://developer.android.com/guide/practices/screens_support.html
+ </content>
+ </item>
+ <item
+ description="%snippet.ui.background.description"
+ id="com.motorola.studio.android.codesnippets.ui3"
+ label="%snippet.ui.background.string">
+ <content>
+ originalImage = Bitmap.createScaledBitmap(
+originalImage, // bitmap to resize
+view.getWidth(), // new width
+view.getHeight(), // new height
+true); // bilinear filtering
+
+ </content>
+ </item>
+ <item
+ description="%snippet.ui.sizes.description"
+ id="com.motorola.studio.android.codesnippets.ui2"
+ label="%snippet.ui.sizes.string">
+ <content>
+ public void onSizeChanged(int w, int h, int oldW,
+int oldH) {
+// Calculate relative sizes at runtime
+// mButton and mButtonBackGround are of type Drawable
+int selfW = mButton.getIntrinsicWidth();
+int selfH = mButton.getIntrinsicHeight();
+int marginX = (w - selfW) / 2;
+int marginY = (h - selfH) / 2;
+mButtonBackground.setBounds(marginX, marginY,
+marginX + selfW, marginY + selfH);
+mButton.setBounds(marginX, marginY,
+marginX + selfW, marginY + selfH);
+// Implement the measureText method to resize text data, if applicable
+measureText();
+}
+ </content>
+ </item>
+ <item
+ description="%snippet.ui.widget.description"
+ id="com.motorola.studio.android.codesnippets.ui4"
+ label="%snippet.ui.widget.string">
+ <content>
+ // Add import android.view.View.OnClickListener; statement
+// Add import android.widget.Button; statement
+// Add import android.view.View; statement
+
+Button b1 = (Button) findViewById(R.id.your_button_id); // Use this method carefully, it consumes lots of system resources
+b1.setOnClickListener(new OnClickListener() {
+ public void onClick(View v) {
+ // Handle the button click here as you wish
+ }
+});
+ </content>
+ </item>
+ <item
+ description="%snippet.ui.moveout.description"
+ id="com.motorola.studio.android.codesnippets.ui5"
+ label="%snippet.ui.moveout.string">
+ <content>
+// You must provide types for the three generic parameters before the code will compile.
+// For more details, see http://developer.android.com/reference/android/os/AsyncTask.html
+ private class MoveOutOfUIthread extends AsyncTask&lt;
+ Params, // one or more values of this type are passed to doInBackground()
+ Progress, // the type of the progress units published during background crunching.
+ Result // the type of the result returned by doInBackground()
+ &gt;
+ {
+
+ protected Integer doInBackground(Params... p1, p2, p3) {
+ // your background task here
+ Result result = new Result();
+ return result;
+ }
+
+ protected void onPostExecute(Result r) {
+ // this gets the object returned by doInBackground, and executes on the UI thread
+ }
+ }
+ new MoveOutOfUIthread().execute(p1, p2, p3);
+ </content>
+ </item>
+ <item
+ description="%snippet.ui.backgroundtaskwithnotification.description"
+ id="com.motorola.studio.android.codesnippets.ui6"
+ label="%snippet.ui.backgroundtaskwithnotification.string">
+ <content>
+ // This code is supposed to be used as an inner class and will start a
+ // background task while it notifies the user via the status bar. The outer class
+ // must implement Context (sub)classes like Activity and Service. Use
+ // new BackgroundTaskWithStatusBarNotification(id, title, text,
+ // context).execute() to start the background task.
+ private class BackgroundTaskWithStatusBarNotification extends
+ AsyncTask&lt;Void, Integer, Void&gt; {
+
+ // A unique identifier for the notification.
+ private int mId;
+
+ // The text that will be displayed when the status bar is expanded.
+ private String mShortDesc;
+
+ // The text that flows by in the status bar when the notification first
+ // activates.
+ private String mContent;
+
+ // The application context where the task was launched.
+ private Context mContext;
+
+ // The notification itself.
+ private Notification mNotification;
+
+ // The notification manager retrieved from the system.
+ private NotificationManager mNotificationManager;
+
+ // An intent that will be launched when user clicks the
+ // notification
+ PendingIntent mPendingIntent;
+
+ public BackgroundTaskWithStatusBarNotification(int id,
+ String shortDesc, String content, Context context) {
+ mId = id;
+ mShortDesc = shortDesc;
+ mContent = content;
+ mContext = context;
+ }
+
+ protected void onPreExecute() {
+ mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
+
+ mNotification = new Notification(
+ android.R.drawable.stat_sys_download, mContent,
+ System.currentTimeMillis());
+
+ // TODO: enter the initial text for your task here, such as:
+ // &quot;0% complete&quot;.
+ String progressText = &quot;your_text_here&quot;;
+
+ // this intent will be launched when user clicks the notification in the
+ // status bar and will start activity YourActivity
+ Intent intent = new Intent(mContext, YourActivity.class);
+ mPendingIntent = PendingIntent.getActivity(mContext, 0, intent, 0);
+
+ // updates the notification
+ mNotification.setLatestEventInfo(mContext, mShortDesc,
+ progressText, mPendingIntent);
+ mNotification.flags = Notification.FLAG_ONGOING_EVENT;
+ mNotificationManager.notify(mId, mNotification);
+ }
+
+ @Override
+ protected Void doInBackground(Void... argList) {
+
+ // TODO: add the content of your background task here. Call
+ // AsyncTask.publishProgress() method to update the
+ // tasks progress (method onProgressUpdate() will handle this
+ // progress update).
+
+ return null;
+ }
+
+ protected void onProgressUpdate(Integer... progress) {
+
+ // TODO: supply the text that must be displayed while the task is in
+ // progress. For instance, &quot;progress[0] + &quot;% complete&quot;.
+ String contentText = &quot;your_text_here&quot;;
+
+ // updates the notification
+ mNotification.setLatestEventInfo(mContext, mShortDesc, contentText,
+ mPendingIntent);
+ mNotificationManager.notify(mId, mNotification);
+ }
+
+ protected void onPostExecute(Void result) {
+ // TODO: supply the final text that must be displayed when the task
+ // is done. For instance, &quot;Done&quot;.
+ String contentText = &quot;your_text_here&quot;;
+
+ // updates the notification
+ mNotification.icon = android.R.drawable.stat_sys_download_done;
+ mNotification.setLatestEventInfo(mContext, mShortDesc, contentText,
+ mPendingIntent);
+ mNotificationManager.notify(mId, mNotification);
+ }
+}
+ </content>
+ </item>
+ <item
+ description="%snippet.ui.hide.statusbar.description"
+ id="com.motorola.studio.android.codesnippets.item6"
+ label="%snippet.ui.hide.statusbar.label">
+ <content>
+ //Hides status bar (if it was showing)
+View v = findViewById(R.id.view_id); //for example, the ID representing the root of a linear layout
+v.setSystemUiVisibility(View.STATUS_BAR_HIDDEN);
+ </content>
+ </item>
+ <item
+ description="%snippet.ui.visible.statusbar.description"
+ id="com.motorola.studio.android.codesnippets.item7"
+ label="%snippet.ui.visible.statusbar.label">
+ <content>
+ //Shows status bar (if it was hidden)
+View v = findViewById(R.id.view_id); //for example, the ID representing the root of a linear layout
+v.setSystemUiVisibility(View.STATUS_BAR_VISIBLE);
+ </content>
+ </item>
+ </category>
+ <category
+ description="%intents.ui.description"
+ id="android_intents"
+ label="%intents.ui">
+ <item
+ description="%intents.start.activity.explicitly.description"
+ id="com.motorola.studio.android.codesnippets.intents1"
+ label="%intents.start.activity.explicitly.label">
+ <content>
+ //Calls another activity, by name, without passing data
+
+Intent iExp = new Intent(this, ActivityToCall.class); //TODO Replace 'ActivityToCall' with the class name of the activity being called
+
+startActivity(iExp);
+ </content>
+ </item>
+ <item
+ description="%intents.start.activity.implicitly.description"
+ id="com.motorola.studio.android.codesnippets.item2"
+ label="%intents.start.activity.implicitly.label">
+ <content>
+ //Calls another activity, by action and category, without passing data
+//refer to AndroidManifest.xml&lt;intent-filter&gt; when determining the action and category of the activity to call
+Intent iImp = new Intent(&quot;actionName&quot;); //TODO Replace 'actionName' as appropriate for your action (for example, Intent.ACTION_EDIT)
+iImp.addCategory(&quot;categoryName&quot;); //TODO Replace 'categoryName' as appropriate for your category (for example, Intent.CATEGORY_DEFAULT)
+startActivity(iImp);
+ </content>
+ </item>
+ <item
+ description="%intents.start.activity.for.result.description"
+ id="com.motorola.studio.android.codesnippets.item3"
+ label="%intents.start.activity.for.result.label">
+ <content>
+ //Calls another activity, identified by action and category, passing data URL and a MIME type
+//The class calling the snippet code must implement the following method:
+//protected void onActivityResult (int requestCode, int resultCode, Intent data) {}
+Intent iImp = new Intent();
+iImp.setAction(&quot;actionName&quot;); //TODO Replace 'actionName' as appropriate for your action (for example, Intent.ACTION_EDIT)
+iImp.addCategory(&quot;categoryName&quot;); //TODO Replace 'categoryName' as appropriate for your category (for example, Intent.CATEGORY_DEFAULT)
+//optional - set data and MIME type for the intent
+iImp.setDataAndType(Uri.parse(&quot;http://com.example.project/folder&quot;), &quot;text/plain&quot;); //TODO Change URL and MIME type as appropriate
+startActivityForResult(iImp, 0); //TODO The second parameter (here, zero) is the request code to be used in onActivityResult(); change this parameter to an appropriate value for your activity
+ </content>
+ </item>
+ <item
+ description="%intents.send.broadcast.description"
+ id="com.motorola.studio.android.codesnippets.item4"
+ label="%intents.send.broadcast.label">
+ <content>
+ //Broadcasts an event notification
+//before sending broadcast, make sure that your application has permission to broadcast the action. Otherwise, a SecurityException: Permission denial will be thrown
+Intent iBroad = new Intent();
+iBroad.setAction(&quot;actionName&quot;); //TODO Replace 'actionName' as appropriate for your action (for example, Intent.ACTION_EDIT)
+iBroad.addCategory(&quot;categoryName&quot;); //TODO Replace 'categoryName' as appropriate for your category (for example, Intent.CATEGORY_DEFAULT)
+this.sendBroadcast(iBroad);
+
+ </content>
+ </item>
+ <item
+ description="%intents.start.service.description"
+ id="com.motorola.studio.android.codesnippets.item5"
+ label="%intents.start.service.label">
+ <content>
+ //Starts a service (task to be accomplished in the background, without UI)
+//The class employing the snippet code must implement ServiceConnection
+Intent iServ = new Intent();
+iServ.setClass(getBaseContext(), ServiceName.class); //TODO Replace 'ServiceName' with the class name for your Service
+bindService(iServ, this, BIND_AUTO_CREATE);
+startService(iServ);
+ </content>
+ </item>
+ </category>
+ <category
+ description="%fragments.ui.description"
+ id="android_fragments"
+ label="%fragments.ui">
+ <item
+ description="%fragments.replace.transaction.description"
+ id="com.motorola.studio.android.codesnippets.item10"
+ label="%fragments.replace.transaction.label">
+ <content>
+ //Replaces a container's fragment(s)
+FragmentTransaction ft = getFragmentManager().beginTransaction();
+MyFragment newFrag =new MyFragment(); //TODO Change to your fragment constructor
+ft.addToBackStack(null); //Allows this transaction to be undone when the back button is pressed
+ft.replace(R.id.containerId, newFrag, &quot;newFragTag&quot;); //TODO Replace 'R.id.containerId' (with the ID of a View) and 'newFragTag'
+ft.commit();
+ </content>
+ </item>
+ <item
+ description="%fragments.add.transaction.description"
+ id="com.motorola.studio.android.codesnippets.item11"
+ label="%fragments.add.transaction.label">
+ <content>
+ //Adds a fragment to a specified layout
+FragmentTransaction ft = getFragmentManager().beginTransaction();
+MyFragment newFrag =new MyFragment(); //TODO Change to your fragment constructor
+ft.addToBackStack(null); //Allows this transaction to be undone when the back button is pressed
+ft.add(R.id.layoutId, newFrag); //TODO Replace 'R.id.layoutId' (with the ID of a layout)
+ft.commit();
+ </content>
+ </item>
+ </category>
+ <category
+ description="%category.facerecog.description"
+ id="face_recognition"
+ label="%category.facerecog">
+ <item
+ description="%snippet.facerecog.description"
+ id="com.motorola.studio.android.codesnippets.facerecog1"
+ label="%snippet.facerecog.string">
+ <content>
+ //Make sure the bitmap file was generated using the RGB_565
+ //configuration, or else it cannot be used for detection
+ //For more information, see http://developer.android.com/reference/android/media/FaceDetector.html#findFaces(android.graphics.Bitmap, android.media.FaceDetector.Face[])
+ BitmapFactory.Options bmpOptions = new BitmapFactory.Options();
+ bmpOptions.inPreferredConfig = Bitmap.Config.RGB_565;
+
+ //Decode the file from Resources and get some of its parameters.
+ //You can also use any of the decode methods from BitmapFactory
+ Bitmap bmp = BitmapFactory.decodeResource(getResources(), /*TODO: Insert your drawable resource ID here*/, bmpOptions);
+ int MaxNumberOfDetectedFaces = 2;
+
+ //Instantiate a vector with the number of faces you want
+ Face[] faces = new FaceDetector.Face[MaxNumberOfDetectedFaces];
+ //Instantiate a FaceDetector object for the bitmap
+ FaceDetector fd = new FaceDetector(bmp.getWidth(), bmp.getHeight(), MaxNumberOfDetectedFaces);
+
+ //Use the findfaces() method to actually process the bitmap
+ if (fd.findFaces(bmp, faces) &gt; 0) {
+ //Do something here. You can see how accurate the detection was
+ //by calling confidence() on any of the found faces.
+ //In this example, it looks only for the first face (faces[0]).
+ Toast.makeText(this, &quot;Confidence: &quot; + faces[0].confidence(), Toast.LENGTH_SHORT).show();
+ }
+ else {
+ //No faces were found in the bitmap
+ Toast.makeText(this, &quot;No face found!&quot;, Toast.LENGTH_SHORT).show();
+ }
+ </content>
+ </item>
+ </category>
+ </extension>
+ <extension
+ point="org.eclipse.ui.perspectiveExtensions">
+ <perspectiveExtension
+ targetID="org.eclipse.jdt.ui.JavaPerspective">
+ <viewShortcut
+ id="org.eclipse.wst.common.snippets.internal.ui.SnippetsView">
+ </viewShortcut>
+ <view
+ id="org.eclipse.wst.common.snippets.internal.ui.SnippetsView"
+ minimized="false"
+ relationship="stack"
+ relative="org.eclipse.ui.views.ContentOutline"
+ visible="true">
+ </view>
+ </perspectiveExtension>
+ </extension>
+ </plugin>
diff --git a/src/plugins/snippets/src/com/motorola/studio/android/codesnippets/AndroidPermissionInsertSnippet.java b/src/plugins/snippets/src/com/motorola/studio/android/codesnippets/AndroidPermissionInsertSnippet.java
new file mode 100644
index 0000000..5743e16
--- /dev/null
+++ b/src/plugins/snippets/src/com/motorola/studio/android/codesnippets/AndroidPermissionInsertSnippet.java
@@ -0,0 +1,274 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+package com.motorola.studio.android.codesnippets;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.StringTokenizer;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.ITextSelection;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.swt.dnd.DragSourceEvent;
+import org.eclipse.ui.IEditorInput;
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.part.FileEditorInput;
+import org.eclipse.ui.texteditor.ITextEditor;
+import org.eclipse.wst.common.snippets.core.ISnippetItem;
+import org.eclipse.wst.common.snippets.core.ISnippetsEntry;
+import org.eclipse.wst.common.snippets.internal.ui.SnippetsView;
+import org.eclipse.wst.common.snippets.ui.DefaultSnippetInsertion;
+
+import com.motorola.studio.android.codesnippets.i18n.AndroidSnippetsNLS;
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.common.log.UsageDataConstants;
+import com.motorola.studio.android.common.utilities.EclipseUtils;
+import com.motorola.studio.android.manifest.AndroidProjectManifestFile;
+import com.motorola.studio.android.model.manifest.AndroidManifestFile;
+import com.motorola.studio.android.model.manifest.dom.ManifestNode;
+import com.motorola.studio.android.model.manifest.dom.UsesPermissionNode;
+
+public class AndroidPermissionInsertSnippet extends DefaultSnippetInsertion
+{
+
+ @Override
+ protected void doInsert(IEditorPart editorPart, ITextEditor textEditor, IDocument document,
+ ITextSelection textSelection) throws BadLocationException
+ {
+ String replacement = getInsertString(editorPart.getEditorSite().getShell());
+ boolean permissionsAdded = addPermissionToManifest(editorPart, replacement);
+
+ // get the Snippets View
+ SnippetsView snippetsView =
+ (SnippetsView) EclipseUtils.getActiveView(AndroidSnippetsStartup.SNIPPETS_VIEW_ID);
+ // get the selected snippet
+ ISnippetsEntry snippetEntry = snippetsView.getSelectedEntry();
+ if (snippetEntry != null)
+ {
+ String snippetLabel = snippetEntry.getLabel();
+
+ if (CodeSnippetsPlugin.getDefault() != null)
+ {
+ StudioLogger
+ .collectUsageData(
+ UsageDataConstants.WHAT_CODESNIPPET,
+ UsageDataConstants.KIND_CODESNIPPET,
+ "Codesnippet '" + snippetLabel + "' used. Permission added: " + permissionsAdded, //$NON-NLS-1$
+ AndroidSnippetsStartup.SNIPPETS_VIEW_ID, CodeSnippetsPlugin
+ .getDefault().getBundle().getVersion().toString());
+ }
+ }
+
+ super.doInsert(editorPart, textEditor, document, textSelection);
+ }
+
+ /**
+ * If the snippetText contains comment to insert uses-permission, then
+ * it adds uses-permission to androidmanifest file
+ * @param editorPart editor
+ * @param snippetText text to drop
+ */
+ private boolean addPermissionToManifest(IEditorPart editorPart, String snippetText)
+ {
+ boolean needToAddPermission =
+ snippetText.contains("AndroidManifest.xml must have the following permission:"); //$NON-NLS-1$
+ boolean shouldAddToManifest = false;
+ if (needToAddPermission)
+ {
+
+ List<String> neededPermissions = getNeededPermissions(snippetText);
+ List<String> permissionsToBeAdded = new ArrayList<String>(neededPermissions.size());
+
+ IEditorInput input = editorPart.getEditorInput();
+ FileEditorInput fileEditorInput = null;
+ ManifestNode manifestNode = null;
+ if (input instanceof FileEditorInput)
+ {
+ fileEditorInput = (FileEditorInput) input;
+ IProject project;
+ IFile file = fileEditorInput.getFile();
+ project = file.getProject();
+ try
+ {
+ AndroidManifestFile androidManifestFile =
+ AndroidProjectManifestFile.getFromProject(project);
+ manifestNode = androidManifestFile.getManifestNode();
+ }
+ catch (Exception e)
+ {
+ // Do nothing, just ask for the permissions.
+ }
+ }
+
+ if (manifestNode != null)
+ {
+ for (String neededPermission : neededPermissions)
+ {
+ if (!permAlreadyExists(manifestNode, neededPermission))
+ {
+ permissionsToBeAdded.add(neededPermission);
+ }
+ }
+ }
+
+ if (!permissionsToBeAdded.isEmpty())
+ {
+
+ StringBuilder permMsgBuilder = new StringBuilder();
+ for (String neededPermission : permissionsToBeAdded)
+ {
+ permMsgBuilder
+ .append(AndroidSnippetsNLS.AndroidPermissionInsertSnippet_PermissionPrefix);
+ permMsgBuilder.append(neededPermission);
+ permMsgBuilder
+ .append(AndroidSnippetsNLS.AndroidPermissionInsertSnippet_PermissionSuffix);
+ }
+
+ //Ask user permission
+ shouldAddToManifest =
+ EclipseUtils
+ .showQuestionDialog(
+ AndroidSnippetsNLS.AndroidPermissionInsertSnippet_Msg_AddToManifest_Title,
+ NLS.bind(
+ AndroidSnippetsNLS.AndroidPermissionInsertSnippet_Msg_AddToManifest_Msg,
+ permMsgBuilder.toString()));
+
+ if (shouldAddToManifest)
+ {
+ AndroidManifestFile androidManifestFile = null;
+ manifestNode = null;
+ if (fileEditorInput != null)
+ {
+ addPermissionToManifest(permissionsToBeAdded, fileEditorInput,
+ androidManifestFile, manifestNode);
+ }
+
+ }
+ }
+ }
+ return shouldAddToManifest;
+ }
+
+ private List<String> getNeededPermissions(String snippetText)
+ {
+ //search each <uses-permission tag
+ StringTokenizer lineToken = new StringTokenizer(snippetText, "\n\r"); //$NON-NLS-1$
+ List<String> neededPermissions = new ArrayList<String>(lineToken.countTokens());
+ while (lineToken.hasMoreTokens())
+ {
+ String line = lineToken.nextToken();
+ if (line.contains("<uses-permission")) //$NON-NLS-1$
+ {
+ String permNameToAdd = null;
+ String androidNameStr = "android:name=\""; //$NON-NLS-1$
+ int beginIndex = line.indexOf(androidNameStr);
+ int endIndex = line.indexOf("\"/>"); //$NON-NLS-1$
+ if ((beginIndex > 0) && (endIndex > 0))
+ {
+ permNameToAdd = line.substring(beginIndex + androidNameStr.length(), endIndex);
+ neededPermissions.add(permNameToAdd);
+ }
+ else
+ {
+ //log malformed permission statement
+ StudioLogger
+ .error(AndroidPermissionInsertSnippet.class,
+ "Permission code snippet was not in the right format to enable insert of uses-permission on androidmanifest.xml" //$NON-NLS-1$
+ + snippetText);
+ }
+ }
+ }
+
+ return neededPermissions;
+ }
+
+ /**
+ * If the snippetText contains comment to insert uses-permission, then
+ * it asks user to add uses-permission to androidmanifest file
+ * @param snippetText text to drop
+ * @param input editor
+ * @param androidManifestFile file to update
+ * @param manifestNode node to update
+ */
+ private void addPermissionToManifest(List<String> neededPermissions, IEditorInput input,
+ AndroidManifestFile androidManifestFile, ManifestNode manifestNode)
+ {
+ IProject project;
+ IFile file = ((FileEditorInput) input).getFile();
+ project = file.getProject();
+ try
+ {
+ androidManifestFile = AndroidProjectManifestFile.getFromProject(project);
+ manifestNode = androidManifestFile.getManifestNode();
+
+ for (String neededPermission : neededPermissions)
+ {
+ if (!permAlreadyExists(manifestNode, neededPermission))
+ {
+ //append permission node
+ UsesPermissionNode usesPermissionNode =
+ new UsesPermissionNode(neededPermission);
+ manifestNode.addChild(usesPermissionNode);
+ }
+ }
+
+ AndroidProjectManifestFile.saveToProject(project, androidManifestFile, true);
+ }
+ catch (Exception e)
+ {
+ StudioLogger.error(AndroidPermissionInsertSnippet.class,
+ "Error adding snippet permissions to androidmanifest.xml.", e); //$NON-NLS-1$
+ }
+ }
+
+ private boolean permAlreadyExists(ManifestNode manifestNode, String neededPermission)
+ {
+ //Check if permissions does not exist yet
+ boolean permAlreadyExists = false;
+ List<UsesPermissionNode> permissionsNode = manifestNode.getUsesPermissionNodes();
+
+ if (permissionsNode != null)
+ {
+ for (UsesPermissionNode existentPermissionNode : permissionsNode)
+ {
+ String permName =
+ existentPermissionNode.getNodeProperties()
+ .get(UsesPermissionNode.PROP_NAME);
+ if ((permName != null) && permName.equals(neededPermission))
+ {
+ permAlreadyExists = true;
+ break;
+ }
+ }
+ }
+ return permAlreadyExists;
+ }
+
+ @Override
+ public void dragSetData(DragSourceEvent event, ISnippetItem item)
+ {
+ IEditorPart part =
+ PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage()
+ .getActiveEditor();
+ addPermissionToManifest(part, item.getContentString());
+ super.dragSetData(event, item);
+ }
+}
diff --git a/src/plugins/snippets/src/com/motorola/studio/android/codesnippets/AndroidSnippetsStartup.java b/src/plugins/snippets/src/com/motorola/studio/android/codesnippets/AndroidSnippetsStartup.java
new file mode 100644
index 0000000..e555654
--- /dev/null
+++ b/src/plugins/snippets/src/com/motorola/studio/android/codesnippets/AndroidSnippetsStartup.java
@@ -0,0 +1,338 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.codesnippets;
+
+import org.eclipse.gef.ui.palette.PaletteViewer;
+import org.eclipse.jface.action.ToolBarManager;
+import org.eclipse.swt.events.DragDetectEvent;
+import org.eclipse.swt.events.DragDetectListener;
+import org.eclipse.swt.events.MouseEvent;
+import org.eclipse.swt.events.MouseListener;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.ui.IPerspectiveDescriptor;
+import org.eclipse.ui.IPerspectiveListener;
+import org.eclipse.ui.IStartup;
+import org.eclipse.ui.IViewReference;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.IWorkbenchPartReference;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.PerspectiveAdapter;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.wst.common.snippets.core.ISnippetItem;
+import org.eclipse.wst.common.snippets.core.ISnippetsEntry;
+import org.eclipse.wst.common.snippets.internal.ui.SnippetsView;
+
+import com.motorola.studio.android.common.utilities.EclipseUtils;
+
+/**
+ * This class register listeners in the Snippets View to monitor changes on its
+ * selection. This way, MOTODEV Studio for Android displays the snippet preview
+ * when appropriate
+ *
+ */
+@SuppressWarnings("restriction")
+public class AndroidSnippetsStartup implements IStartup
+{
+
+ public final static String SNIPPETS_VIEW_ID =
+ "org.eclipse.wst.common.snippets.internal.ui.SnippetsView";
+
+ private static SnippetsViewContributionItem searchContributionItem;
+
+ private static TooltipDisplayConfigContriutionItem tooltipDisplayConfigcontributionItem;
+
+ /*
+ * The tool tip being displayed
+ */
+ private AndroidSnippetsTooltip tooltip = null;
+
+ private static void addSearchBar(final SnippetsView view)
+ {
+ if (searchContributionItem == null)
+ {
+ ToolBarManager tbManager =
+ (ToolBarManager) view.getViewSite().getActionBars().getToolBarManager();
+
+ // the item which searches for words within snippets titles, descriptions and codes
+ searchContributionItem = new SnippetsViewContributionItem(view);
+
+ tbManager.add(searchContributionItem);
+ tbManager.update(true);
+ view.getViewSite().getActionBars().updateActionBars();
+ }
+
+ if (tooltipDisplayConfigcontributionItem == null)
+ {
+ ToolBarManager tbManager =
+ (ToolBarManager) view.getViewSite().getActionBars().getToolBarManager();
+
+ // the item which configures the display of the tool tip
+ tooltipDisplayConfigcontributionItem = new TooltipDisplayConfigContriutionItem();
+
+ tbManager.add(tooltipDisplayConfigcontributionItem);
+ tbManager.update(true);
+ view.getViewSite().getActionBars().updateActionBars();
+ }
+ }
+
+ /**
+ * Add a mouse listener to Snippets View
+ * Open the snippet preview tooltip when the user clicks on a
+ * snippet item.
+ * Close it when he clicks again in the same item
+ *
+ * @see org.eclipse.ui.IStartup#earlyStartup()
+ */
+ public void earlyStartup()
+ {
+
+ /*
+ * This is the listener responsible for monitoring mouse clicks
+ * on snippet items. When the user clicks on an item, it triggers
+ * the action to display the snippet preview
+ */
+ final MouseListener snippetsMouseListener = new MouseListener()
+ {
+ /**
+ * Do nothing
+ *
+ * @see org.eclipse.swt.events.MouseListener#mouseUp(org.eclipse.swt.events.MouseEvent)
+ */
+ public void mouseUp(MouseEvent e)
+ {
+ // nothing
+ }
+
+ /**
+ * Open/Toogle tooltip
+ *
+ * @see org.eclipse.swt.events.MouseListener#mouseDown(org.eclipse.swt.events.MouseEvent)
+ */
+ public void mouseDown(MouseEvent e)
+ {
+
+ // get the Snippets View
+ SnippetsView snippetsView =
+ (SnippetsView) EclipseUtils.getActiveView(SNIPPETS_VIEW_ID);
+
+ // get the selected snippet
+ ISnippetsEntry snippetEntry = snippetsView.getSelectedEntry();
+
+ // check the tooltip is already being displayed
+ // open it if it's not and its config allows so, close it otherwise
+ if ((snippetEntry instanceof ISnippetItem)
+ && ((tooltip == null) || (!tooltip.getItem().equals(snippetEntry)))
+ && tooltipDisplayConfigcontributionItem.isTooltipDisplayed())
+ {
+ Control snippetsControl = snippetsView.getViewer().getControl();
+ tooltip =
+ new AndroidSnippetsTooltip((ISnippetItem) snippetEntry, snippetsControl);
+ tooltip.setPopupDelay(250);
+ tooltip.show(new Point(snippetsControl.getBounds().width, 0));
+
+ }
+ else
+ {
+ if (tooltip != null)
+ {
+ tooltip.hide();
+ }
+ tooltip = null;
+ }
+ }
+
+ /**
+ * Hide tooltip
+ *
+ * @see org.eclipse.swt.events.MouseListener#mouseDoubleClick(org.eclipse.swt.events.MouseEvent)
+ */
+ public void mouseDoubleClick(MouseEvent e)
+ {
+ if (tooltip != null)
+ {
+ tooltip.hide();
+ }
+ tooltip = null;
+ }
+ };
+
+ /**
+ * this listener is called when the mouse is dragged. It simply
+ * hides the tool tip when this action occurs.
+ */
+ final DragDetectListener snippetsMouseDragListener = new DragDetectListener()
+ {
+
+ /**
+ * Hide the tool tip.
+ */
+ public void dragDetected(DragDetectEvent e)
+ {
+ // simply hide the tool tip
+ if (tooltip != null)
+ {
+ tooltip.hide();
+ }
+ }
+ };
+
+ /*
+ * This is the listener that is attached to the workspace and monitor
+ * perspectives activation, as well as changes in the views being displayed
+ * (for example: view opened/closed). It intends to attach the snippetsMouseListener
+ * listener declared above in the Snippets View being used by Eclipse
+ */
+ final IPerspectiveListener perspectiveListener = new PerspectiveAdapter()
+ {
+ // Set if it has already been executed
+ // If so, it doesn't need to be executed again
+ boolean executed = false;
+
+ /**
+ * This action is called when the user goes to another perspective (it's not called for the first perspective
+ * displayed when he opens Eclipse)
+ *
+ * @see org.eclipse.ui.PerspectiveAdapter#perspectiveActivated(org.eclipse.ui.IWorkbenchPage, org.eclipse.ui.IPerspectiveDescriptor)
+ */
+ @Override
+ public void perspectiveActivated(IWorkbenchPage page, IPerspectiveDescriptor perspective)
+ {
+ IViewReference[] viewReferences = page.getViewReferences();
+ for (IViewReference viewReference : viewReferences)
+ {
+ if (SNIPPETS_VIEW_ID.equals(viewReference.getId()))
+ {
+ SnippetsView snippetsView = (SnippetsView) viewReference.getView(true);
+ if (snippetsView != null)
+ {
+ addSearchBar(snippetsView);
+ PaletteViewer palleteViewer = snippetsView.getViewer();
+ if (palleteViewer != null)
+ {
+ Control control = palleteViewer.getControl();
+ if (control != null)
+ {
+ control.removeMouseListener(snippetsMouseListener); // remove mouse listener just to ensure we never have two listeners registered
+ control.removeDragDetectListener(snippetsMouseDragListener); // remove mouse draggable listener just to ensure we never have two listeners registered
+ control.addMouseListener(snippetsMouseListener);
+ control.addDragDetectListener(snippetsMouseDragListener);
+ executed = true;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * This is called when something in the perspective have changed.
+ * For example: when a view is opened or closed
+ *
+ * @see org.eclipse.ui.PerspectiveAdapter#perspectiveChanged(org.eclipse.ui.IWorkbenchPage, org.eclipse.ui.IPerspectiveDescriptor, org.eclipse.ui.IWorkbenchPartReference, java.lang.String)
+ */
+ @Override
+ public void perspectiveChanged(IWorkbenchPage page, IPerspectiveDescriptor perspective,
+ IWorkbenchPartReference partRef, String changeId)
+ {
+ // check if it's the Snippet View
+ if (SNIPPETS_VIEW_ID.equals(partRef.getId()))
+ {
+ // if it's the Snippet View and it's being OPENED
+ if (IWorkbenchPage.CHANGE_VIEW_SHOW.equals(changeId))
+ {
+ if (!executed)
+ {
+ SnippetsView snippetsView = (SnippetsView) partRef.getPart(false);
+ addSearchBar(snippetsView);
+ snippetsView.getViewer().getControl()
+ .removeMouseListener(snippetsMouseListener); // remove mouse listener just to ensure we never have two listeners registered
+ snippetsView.getViewer().getControl()
+ .removeDragDetectListener(snippetsMouseDragListener); // remove mouse draggable listener just to ensure we never have two liseteners registered
+ snippetsView.getViewer().getControl()
+ .addMouseListener(snippetsMouseListener);
+ snippetsView.getViewer().getControl()
+ .addDragDetectListener(snippetsMouseDragListener);
+ // it doesn't need to add the mouse listener to the Snippets View in further opportunities
+ executed = true;
+ }
+ }
+ // if it's the Snippet View and it's being CLOSED
+ else if (IWorkbenchPage.CHANGE_VIEW_HIDE.equals(changeId))
+ {
+ // it must add the mouse listener to the Snippets View again next time the view is opened
+ if (searchContributionItem != null)
+ {
+ searchContributionItem.clean();
+ searchContributionItem.getParent().remove(searchContributionItem);
+ }
+
+ if (tooltipDisplayConfigcontributionItem != null) {
+ tooltipDisplayConfigcontributionItem.getParent().remove(tooltipDisplayConfigcontributionItem);
+ }
+
+ searchContributionItem = null;
+ tooltipDisplayConfigcontributionItem = null;
+
+ executed = false;
+ }
+ }
+
+ }
+
+ };
+
+ /*
+ * Attach the perspectiveListener declared above into the active window
+ * Also try to add the snippetsMouseListener listener to the Snippet View,
+ * if it's already being displayed
+ */
+ PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable()
+ {
+
+ public void run()
+ {
+ /*
+ * Add mouse listener to Snippet View
+ */
+ final IWorkbenchWindow activeWindow =
+ PlatformUI.getWorkbench().getActiveWorkbenchWindow();
+
+ activeWindow.addPerspectiveListener(perspectiveListener);
+
+ IViewReference viewReference =
+ activeWindow.getActivePage().findViewReference(SNIPPETS_VIEW_ID);
+
+ if (viewReference != null)
+ {
+ final SnippetsView snippetsView = (SnippetsView) viewReference.getView(true);
+ if (snippetsView != null)
+ {
+ addSearchBar(snippetsView);
+ snippetsView.getViewer().getControl()
+ .addMouseListener(snippetsMouseListener);
+ snippetsView.getViewer().getControl()
+ .addDragDetectListener(snippetsMouseDragListener);
+ }
+
+ }
+
+ }
+
+ });
+
+ }
+} \ No newline at end of file
diff --git a/src/plugins/snippets/src/com/motorola/studio/android/codesnippets/AndroidSnippetsTooltip.java b/src/plugins/snippets/src/com/motorola/studio/android/codesnippets/AndroidSnippetsTooltip.java
new file mode 100644
index 0000000..a954ba8
--- /dev/null
+++ b/src/plugins/snippets/src/com/motorola/studio/android/codesnippets/AndroidSnippetsTooltip.java
@@ -0,0 +1,147 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.codesnippets;
+
+import org.eclipse.jdt.internal.ui.JavaPlugin;
+import org.eclipse.jdt.internal.ui.javaeditor.JavaSourceViewer;
+import org.eclipse.jdt.ui.PreferenceConstants;
+import org.eclipse.jdt.ui.text.JavaSourceViewerConfiguration;
+import org.eclipse.jdt.ui.text.JavaTextTools;
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.jface.resource.JFaceResources;
+import org.eclipse.jface.text.Document;
+import org.eclipse.jface.text.source.SourceViewer;
+import org.eclipse.jface.window.ToolTip;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.ScrolledComposite;
+import org.eclipse.swt.events.ControlAdapter;
+import org.eclipse.swt.events.ControlEvent;
+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.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.wst.common.snippets.core.ISnippetItem;
+
+import com.motorola.studio.android.codesnippets.i18n.AndroidSnippetsNLS;
+
+/**
+ * Customized tooltip for snippets in Snippet View.
+ * It's intended to display the snippet preview
+ *
+ */
+@SuppressWarnings("restriction")
+public class AndroidSnippetsTooltip extends ToolTip
+{
+
+ /*
+ * The snippet item to be displayed in the tooltip
+ */
+ private final ISnippetItem item;
+
+ /**
+ * Constructor
+ *
+ * @param item the snippet item to be displayed
+ * @param control
+ */
+ public AndroidSnippetsTooltip(ISnippetItem item, Control control)
+ {
+ super(control, NO_RECREATE, true);
+ this.item = item;
+ }
+
+ /**
+ * Display the snippet preview by using a JAVA Source Viewer, which is
+ * used to highlight the code
+ *
+ * @see org.eclipse.jface.window.ToolTip#createToolTipContentArea(org.eclipse.swt.widgets.Event, org.eclipse.swt.widgets.Composite)
+ */
+ @Override
+ protected Composite createToolTipContentArea(Event event, Composite parent)
+ {
+ // the main composite
+ Composite mainComposite = new Composite(parent, SWT.NULL);
+ mainComposite.setLayout(new GridLayout(1, true));
+
+ /*
+ * snippet preview label
+ */
+ Label textElem = new Label(mainComposite, SWT.LEFT);
+ textElem.setText(AndroidSnippetsNLS.UI_Snippet_Preview);
+ textElem.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, true, false, 1, 1));
+
+ /*
+ * JAVA source viewer
+ */
+ final ScrolledComposite scroll =
+ new ScrolledComposite(mainComposite, SWT.H_SCROLL | SWT.V_SCROLL);
+ scroll.setLayout(new FillLayout());
+ // create scroll layout which receives values to limit its area of display
+ GridData scrollLayoutData = new GridData(SWT.FILL, SWT.FILL, false, false, 1, 1);
+ Rectangle visibleArea = parent.getDisplay().getActiveShell().getMonitor().getClientArea();
+ scrollLayoutData.heightHint = visibleArea.height / 3;
+ scrollLayoutData.widthHint = visibleArea.width / 3;
+ scroll.setLayoutData(scrollLayoutData);
+
+ final Composite javaSourceViewerComposite = new Composite(scroll, SWT.NULL);
+ javaSourceViewerComposite.setLayout(new FillLayout());
+
+ int styles = SWT.MULTI | SWT.BORDER | SWT.FULL_SELECTION;
+ Document document = new Document(item.getContentString());
+ IPreferenceStore store = JavaPlugin.getDefault().getCombinedPreferenceStore();
+ JavaTextTools javaTextTools = JavaPlugin.getDefault().getJavaTextTools();
+
+ SourceViewer javaSourceViewer =
+ new JavaSourceViewer(javaSourceViewerComposite, null, null, false, styles, store);
+ javaSourceViewer.configure(new JavaSourceViewerConfiguration(javaTextTools
+ .getColorManager(), store, null, null));
+ javaSourceViewer.getControl().setFont(
+ JFaceResources.getFont(PreferenceConstants.EDITOR_TEXT_FONT));
+ javaTextTools.setupJavaDocumentPartitioner(document);
+ javaSourceViewer.setDocument(document);
+ javaSourceViewer.setEditable(false);
+
+ // set up scroll
+ scroll.setContent(javaSourceViewerComposite);
+ scroll.setExpandHorizontal(true);
+ scroll.setExpandVertical(true);
+ scroll.addControlListener(new ControlAdapter()
+ {
+ @Override
+ public void controlResized(ControlEvent e)
+ {
+ scroll.setMinSize(javaSourceViewerComposite.computeSize(SWT.DEFAULT, SWT.DEFAULT));
+ }
+ });
+
+ return mainComposite;
+ }
+
+ /**
+ * Get the snippet item being displayed
+ *
+ * @return the snippet item being displayed
+ */
+ public ISnippetItem getItem()
+ {
+ return item;
+ }
+
+}
diff --git a/src/plugins/snippets/src/com/motorola/studio/android/codesnippets/CodeSnippetsPlugin.java b/src/plugins/snippets/src/com/motorola/studio/android/codesnippets/CodeSnippetsPlugin.java
new file mode 100644
index 0000000..b3a0ab8
--- /dev/null
+++ b/src/plugins/snippets/src/com/motorola/studio/android/codesnippets/CodeSnippetsPlugin.java
@@ -0,0 +1,69 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.codesnippets;
+
+import org.eclipse.ui.plugin.AbstractUIPlugin;
+import org.osgi.framework.BundleContext;
+
+import com.motorola.studio.android.common.log.StudioLogger;
+
+public class CodeSnippetsPlugin extends AbstractUIPlugin
+{
+
+ // The plug-in ID
+ public static final String PLUGIN_ID = "com.motorola.studio.android.codesnippets"; //$NON-NLS-1$
+
+ // The shared instance
+ private static CodeSnippetsPlugin plugin;
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.ui.plugin.AbstractUIPlugin#start(org.osgi.framework.BundleContext)
+ */
+ @Override
+ public void start(BundleContext context) throws Exception
+ {
+ StudioLogger.debug(CodeSnippetsPlugin.class,
+ "Starting MOTODEV Android Code Snippets Plugin...");
+
+ super.start(context);
+ plugin = this;
+
+ StudioLogger.debug(CodeSnippetsPlugin.class,
+ "MOTODEV Android Code Snippets Plugin started.");
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.ui.plugin.AbstractUIPlugin#stop(org.osgi.framework.BundleContext)
+ */
+ @Override
+ public void stop(BundleContext context) throws Exception
+ {
+ plugin = null;
+ super.stop(context);
+ }
+
+ /**
+ * Returns the shared instance
+ *
+ * @return the shared instance
+ */
+ public static CodeSnippetsPlugin getDefault()
+ {
+ return plugin;
+ }
+}
diff --git a/src/plugins/snippets/src/com/motorola/studio/android/codesnippets/SnippetsViewContributionItem.java b/src/plugins/snippets/src/com/motorola/studio/android/codesnippets/SnippetsViewContributionItem.java
new file mode 100644
index 0000000..d869601
--- /dev/null
+++ b/src/plugins/snippets/src/com/motorola/studio/android/codesnippets/SnippetsViewContributionItem.java
@@ -0,0 +1,227 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+package com.motorola.studio.android.codesnippets;
+
+import java.util.Iterator;
+import java.util.List;
+
+import org.eclipse.jface.action.ControlContribution;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.wst.common.snippets.internal.palette.SnippetPaletteDrawer;
+import org.eclipse.wst.common.snippets.internal.palette.SnippetPaletteItem;
+import org.eclipse.wst.common.snippets.internal.ui.SnippetsView;
+
+import com.motorola.studio.android.codesnippets.i18n.AndroidSnippetsNLS;
+
+@SuppressWarnings("restriction")
+public class SnippetsViewContributionItem extends ControlContribution
+{
+ private final SnippetsView view;
+
+ public SnippetsViewContributionItem(SnippetsView view)
+ {
+ super("com.motorola.studio.android.codesnippets.search");
+ this.view = view;
+ }
+
+ private Text text;
+
+ final String INITIAL_TEXT = AndroidSnippetsNLS.UI_Snippet_SearchLabel;
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.action.ControlContribution#computeWidth(org.eclipse.swt.widgets.Control)
+ */
+ @Override
+ protected int computeWidth(Control control)
+ {
+ return text.computeSize(100, SWT.DEFAULT).x;
+ }
+
+ @Override
+ protected Control createControl(Composite parent)
+ {
+
+ text = new Text(parent, SWT.BORDER | SWT.SEARCH | SWT.ICON_SEARCH);
+
+ text.setToolTipText(INITIAL_TEXT);
+ text.setEnabled(true);
+ text.setEditable(true);
+ text.setMessage(INITIAL_TEXT);
+
+ resetView();
+
+ text.addListener(SWT.Modify, new Listener()
+ {
+ public void handleEvent(Event event)
+ {
+ String typed = text.getText().toLowerCase();
+
+ // variables for the first inner loop
+ Object rootChildObject = null;
+ SnippetPaletteDrawer snippetPaletteDrawer = null;
+ SnippetPaletteItem snippetPalletItem = null;
+
+ // variables for the second inner loop
+ List<?> snippetPalleteDrawerChildren = null;
+ Iterator<?> snippetPalleteDrawerIterator = null;
+ Integer foundItemsCount = null;
+ Integer lastIndex = null;
+ Object snippetPalletObject = null;
+
+ // get text and items to be sought in
+ List<?> rootChildren = view.getRoot().getChildren();
+ Iterator<?> rootChildremIterator = rootChildren.iterator();
+
+ /* Here the idea is to iterate through the snippets labels, text and codes,
+ * and find a match. In case there are results, a number of found items by category
+ * is displayed.
+ */
+ while (rootChildremIterator.hasNext())
+ {
+ rootChildObject = rootChildremIterator.next();
+ if (rootChildObject instanceof SnippetPaletteDrawer)
+ {
+ snippetPaletteDrawer = (SnippetPaletteDrawer) rootChildObject;
+ snippetPalleteDrawerChildren = snippetPaletteDrawer.getChildren();
+ snippetPalleteDrawerIterator = snippetPalleteDrawerChildren.iterator();
+ foundItemsCount = 0;
+ while (snippetPalleteDrawerIterator.hasNext())
+ {
+ snippetPalletObject = snippetPalleteDrawerIterator.next();
+ if (snippetPalletObject instanceof SnippetPaletteItem)
+ {
+ snippetPalletItem = (SnippetPaletteItem) snippetPalletObject;
+
+ // there must be a match for either the label, description or code of the snippet
+ if (snippetPalletItem.getLabel().toLowerCase().contains(typed)
+ || snippetPalletItem.getDescription().toLowerCase()
+ .contains(typed)
+ || snippetPalletItem.getContentString().toLowerCase()
+ .contains(typed))
+ {
+ snippetPalletItem.setVisible(true);
+ foundItemsCount++;
+ }
+ else
+ {
+ // since no match was found for the snippets, try to find for its category label
+ if (snippetPaletteDrawer.getLabel().toLowerCase()
+ .contains(typed))
+ {
+ snippetPalletItem.setVisible(true);
+ foundItemsCount++;
+ }
+ else
+ {
+ snippetPalletItem.setVisible(false);
+ }
+ }
+ }
+ }
+
+ // display the number of found items between parenthesis
+ lastIndex = snippetPaletteDrawer.getLabel().lastIndexOf(")");
+ if (lastIndex == -1)
+ {
+ snippetPaletteDrawer.setLabel(snippetPaletteDrawer.getLabel() + " ("
+ + foundItemsCount + ")");
+ }
+ else
+ {
+ snippetPaletteDrawer.setLabel(snippetPaletteDrawer.getLabel()
+ .replaceFirst("\\(\\d+\\)",
+ "(" + foundItemsCount.toString() + ")"));
+ }
+
+ /*
+ * In case no match is found, hide the pallete and all
+ * its children, otherwise display the number of items
+ * found between parenthesis.
+ */
+ if (foundItemsCount == 0)
+ {
+ snippetPaletteDrawer.setVisible(false);
+ snippetPaletteDrawer.setFilters(new String[]
+ {
+ "!"
+ });
+ }
+ else
+ {
+ // show the item
+ snippetPaletteDrawer.setVisible(true);
+ snippetPaletteDrawer.setFilters(new String[]
+ {
+ "*"
+ });
+ }
+ }
+ }
+ }
+
+ });
+
+ return text;
+ }
+
+ /**
+ * Set all items to be visible.
+ */
+ private void resetView()
+ {
+ // get text and items to be sought in
+ List<?> rootChildren = view.getRoot().getChildren();
+ Iterator<?> rootChildremIterator = rootChildren.iterator();
+
+ /*
+ * Here the idea is to iterate through the snippets labels, text and codes,
+ * and set everything to visible, since the view is saving the state.
+ */
+ while (rootChildremIterator.hasNext())
+ {
+ Object rootChildObject = rootChildremIterator.next();
+ if (rootChildObject instanceof SnippetPaletteDrawer)
+ {
+ SnippetPaletteDrawer snippetPaletteDrawer = (SnippetPaletteDrawer) rootChildObject;
+ List<?> snippetPalleteDrawerChildren = snippetPaletteDrawer.getChildren();
+ Iterator<?> snippetPalleteDrawerIterator = snippetPalleteDrawerChildren.iterator();
+ snippetPaletteDrawer.setVisible(true);
+ while (snippetPalleteDrawerIterator.hasNext())
+ {
+ Object snippetPalletObject = snippetPalleteDrawerIterator.next();
+ if (snippetPalletObject instanceof SnippetPaletteItem)
+ {
+ SnippetPaletteItem snippetPalletItem =
+ (SnippetPaletteItem) snippetPalletObject;
+ snippetPalletItem.setVisible(true);
+ }
+ }
+ }
+
+ }
+ }
+
+ public void clean()
+ {
+ text.setText("");
+ }
+}
diff --git a/src/plugins/snippets/src/com/motorola/studio/android/codesnippets/TooltipDisplayConfigContriutionItem.java b/src/plugins/snippets/src/com/motorola/studio/android/codesnippets/TooltipDisplayConfigContriutionItem.java
new file mode 100644
index 0000000..2a814c2
--- /dev/null
+++ b/src/plugins/snippets/src/com/motorola/studio/android/codesnippets/TooltipDisplayConfigContriutionItem.java
@@ -0,0 +1,164 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.codesnippets;
+
+import org.eclipse.core.runtime.preferences.ConfigurationScope;
+import org.eclipse.core.runtime.preferences.IEclipsePreferences;
+import org.eclipse.jface.action.ControlContribution;
+import org.eclipse.jface.dialogs.IDialogSettings;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.layout.RowLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.osgi.service.prefs.BackingStoreException;
+
+import com.motorola.studio.android.codesnippets.i18n.AndroidSnippetsNLS;
+import com.motorola.studio.android.common.log.StudioLogger;
+
+/**
+ * This {@link ControlContribution} adds the check box button which
+ * shows or hides the tooltip of the snippet.
+ *
+ * @see ControlContribution
+ * @see {@link AndroidSnippetsStartup}
+ *
+ */
+public class TooltipDisplayConfigContriutionItem extends ControlContribution
+{
+ /**
+ * Listener which updates the status of whether or not to display
+ * the tool tip.
+ *
+ * @see SelectionListener
+ *
+ */
+ private final class TooltipSelectionListener implements SelectionListener
+ {
+ /**
+ * Here the state of the tooltip display button is persisted in
+ * the {@link IDialogSettings}.
+ *
+ * @see org.eclipse.swt.events.SelectionListener#widgetSelected(org.eclipse.swt.events.SelectionEvent)
+ */
+ public void widgetSelected(SelectionEvent e)
+ {
+ // action for when the check box is pressed.
+ performButtonSelection();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.swt.events.SelectionListener#widgetDefaultSelected(org.eclipse.swt.events.SelectionEvent)
+ */
+ public void widgetDefaultSelected(SelectionEvent e)
+ {
+ // do nothing
+ }
+ }
+
+ /**
+ * Check box {@link Button} which shows or hides the tooltip.
+ */
+ private Button showToolTipButton = null;
+
+ /**
+ * {@link IDialogSettings} field for whether or not the tooltip
+ * is to be displayed.
+ */
+ private static final String DIALOG_SETTINGS__IS_TOOLTIP_DISPLAYED = "IsTooltipDisplayed";
+
+ /**
+ * Constructor which initiates this {@link ControlContribution}.
+ */
+ public TooltipDisplayConfigContriutionItem()
+ {
+ super("com.motorola.studio.android.codesnippets.tooltipDisplayConfig"); //$NON-NLS-1$
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.action.ControlContribution#createControl(org.eclipse.swt.widgets.Composite)
+ */
+ @Override
+ protected Control createControl(Composite parent)
+ {
+ // adjust layout in order to produce space to the left
+ Composite mainComposite = new Composite(parent, SWT.NONE);
+ RowLayout layout = new RowLayout(SWT.FILL);
+ layout.center = true;
+ layout.marginLeft = 10;
+ mainComposite.setLayout(layout);
+
+ // create the check box button
+ showToolTipButton = new Button(mainComposite, SWT.CHECK);
+ showToolTipButton
+ .setText(AndroidSnippetsNLS.TooltipDisplayConfigContriutionItem_ShowPreview);
+ showToolTipButton.addSelectionListener(new TooltipSelectionListener());
+
+ // set the selection persisted
+ IEclipsePreferences preferences = getEclipsePreferences();
+ boolean isTooltipDisplayed =
+ preferences.getBoolean(DIALOG_SETTINGS__IS_TOOLTIP_DISPLAYED, true);
+ showToolTipButton.setSelection(isTooltipDisplayed);
+ performButtonSelection();
+
+ return mainComposite;
+ }
+
+ /**
+ * Returns <code>true</code> in case the tool tip is to
+ * be displayed, or <code>false</code> should it be hidden.
+ *
+ * @return <code>true</code> in case the tool tip
+ * is to be displayed, <code>false</code> otherwise.
+ */
+ public boolean isTooltipDisplayed()
+ {
+ return showToolTipButton.getSelection();
+ }
+
+ /**
+ * Method called when the check box {@link Button} is called. It
+ * persists the check box selection state in the {@link IDialogSettings}.
+ */
+ private void performButtonSelection()
+ {
+ // persist whether or not to show the tooltip
+ IEclipsePreferences preferences = getEclipsePreferences();
+ preferences.putBoolean(DIALOG_SETTINGS__IS_TOOLTIP_DISPLAYED,
+ showToolTipButton.getSelection());
+ try
+ {
+ preferences.flush();
+ }
+ catch (BackingStoreException bse)
+ {
+ StudioLogger.error(TooltipDisplayConfigContriutionItem.class.toString(),
+ "Preferences for snippets could not be saved.", bse); //$NON-NLS-1$
+ }
+ }
+
+ /**
+ * Get Eclipse´s preferences.
+ *
+ * @return Return Eclipse´s preferences.
+ */
+ private IEclipsePreferences getEclipsePreferences()
+ {
+ return ConfigurationScope.INSTANCE.getNode(AndroidSnippetsStartup.SNIPPETS_VIEW_ID);
+ }
+}
diff --git a/src/plugins/snippets/src/com/motorola/studio/android/codesnippets/i18n/AndroidSnippetsNLS.java b/src/plugins/snippets/src/com/motorola/studio/android/codesnippets/i18n/AndroidSnippetsNLS.java
new file mode 100644
index 0000000..07c77d8
--- /dev/null
+++ b/src/plugins/snippets/src/com/motorola/studio/android/codesnippets/i18n/AndroidSnippetsNLS.java
@@ -0,0 +1,55 @@
+/*
+* Copyright (C) 2012 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.motorola.studio.android.codesnippets.i18n;
+
+import org.eclipse.osgi.util.NLS;
+
+/**
+ * This class is the NLS component for Code Snippets plug-in
+ *
+ */
+public class AndroidSnippetsNLS extends NLS
+{
+ /**
+ * The bundle location.
+ * It refers to messages.properties file inside this package
+ */
+ private static final String BUNDLE_NAME =
+ "com.motorola.studio.android.codesnippets.i18n.androidSnippetsNLS";
+
+ static
+ {
+ NLS.initializeMessages(BUNDLE_NAME, AndroidSnippetsNLS.class);
+ }
+
+ public static String AndroidPermissionInsertSnippet_Msg_AddToManifest_Msg;
+
+ public static String AndroidPermissionInsertSnippet_Msg_AddToManifest_Title;
+
+ public static String AndroidPermissionInsertSnippet_PermissionPrefix;
+
+ public static String AndroidPermissionInsertSnippet_PermissionSuffix;
+
+ public static String TooltipDisplayConfigContriutionItem_ShowPreview;
+
+ /*
+ * UI strings area
+ */
+ public static String UI_Snippet_Preview;
+
+ public static String UI_Snippet_SearchLabel;
+
+}
diff --git a/src/plugins/snippets/src/com/motorola/studio/android/codesnippets/i18n/androidSnippetsNLS.properties b/src/plugins/snippets/src/com/motorola/studio/android/codesnippets/i18n/androidSnippetsNLS.properties
new file mode 100644
index 0000000..b66bfd4
--- /dev/null
+++ b/src/plugins/snippets/src/com/motorola/studio/android/codesnippets/i18n/androidSnippetsNLS.properties
@@ -0,0 +1,7 @@
+AndroidPermissionInsertSnippet_Msg_AddToManifest_Msg=This snippet requires specific permissions in order to work correctly.\nShould the following permissions be added to AndroidManifest.xml?\n{0}
+AndroidPermissionInsertSnippet_Msg_AddToManifest_Title=Snippet Required Permissions
+AndroidPermissionInsertSnippet_PermissionPrefix=-
+AndroidPermissionInsertSnippet_PermissionSuffix=\n
+TooltipDisplayConfigContriutionItem_ShowPreview=Show Preview
+UI_Snippet_Preview=Snippet Preview:
+UI_Snippet_SearchLabel=Search... \ No newline at end of file
diff --git a/src/plugins/translation/.classpath b/src/plugins/translation/.classpath
new file mode 100644
index 0000000..4db0e26
--- /dev/null
+++ b/src/plugins/translation/.classpath
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>
+ <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry exported="true" kind="lib" path="commons-net-1.4.1.jar"/>
+ <classpathentry exported="true" kind="lib" path="jakarta-oro-2.0.8.jar"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/src/plugins/translation/.project b/src/plugins/translation/.project
new file mode 100644
index 0000000..492aab1
--- /dev/null
+++ b/src/plugins/translation/.project
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>com.motorola.studio.android.translation</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.ManifestBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.SchemaBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.pde.PluginNature</nature>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+</projectDescription>
diff --git a/src/plugins/translation/META-INF/MANIFEST.MF b/src/plugins/translation/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..f6d4dd8
--- /dev/null
+++ b/src/plugins/translation/META-INF/MANIFEST.MF
@@ -0,0 +1,26 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: %pluginName
+Bundle-SymbolicName: com.motorola.studio.android.translation;singleton:=true
+Bundle-Version: 5.0.0.qualifier
+Bundle-Vendor: %providerName
+Bundle-RequiredExecutionEnvironment: JavaSE-1.6
+Require-Bundle: org.eclipse.core.net,
+ org.eclipse.core.runtime,
+ org.eclipse.jface,
+ org.eclipse.sequoyah.localization.tools,
+ org.eclipse.ui.workbench,
+ com.motorola.studio.android.common,
+ org.eclipse.ui.net,
+ org.eclipse.sequoyah.localization.android,
+ org.eclipse.sequoyah.localization.editor
+Bundle-ClassPath: .,
+ commons-net-1.4.1.jar,
+ jakarta-oro-2.0.8.jar
+Import-Package: org.apache.commons.httpclient,
+ org.apache.commons.httpclient.auth;version="3.1.0",
+ org.apache.commons.httpclient.methods;version="3.1.0",
+ org.apache.commons.httpclient.params;version="3.1.0"
+Bundle-Activator: com.motorola.studio.android.localization.translators.TranslationPlugin
+Bundle-Localization: plugin
+Bundle-ActivationPolicy: lazy
diff --git a/src/plugins/translation/build.properties b/src/plugins/translation/build.properties
new file mode 100644
index 0000000..dc4162c
--- /dev/null
+++ b/src/plugins/translation/build.properties
@@ -0,0 +1,10 @@
+source.. = src/
+output.. = bin/
+bin.includes = META-INF/,\
+ .,\
+ plugin.xml,\
+ commons-net-1.4.1.jar,\
+ jakarta-oro-2.0.8.jar,\
+ OSGI-INF/l10n/bundle.properties,\
+ plugin.properties,\
+ icons/
diff --git a/src/plugins/translation/commons-net-1.4.1.jar b/src/plugins/translation/commons-net-1.4.1.jar
new file mode 100644
index 0000000..9666a92
--- /dev/null
+++ b/src/plugins/translation/commons-net-1.4.1.jar
Binary files differ
diff --git a/src/plugins/translation/icons/google-branding.png b/src/plugins/translation/icons/google-branding.png
new file mode 100644
index 0000000..ce2fc73
--- /dev/null
+++ b/src/plugins/translation/icons/google-branding.png
Binary files differ
diff --git a/src/plugins/translation/jakarta-oro-2.0.8.jar b/src/plugins/translation/jakarta-oro-2.0.8.jar
new file mode 100644
index 0000000..23488d2
--- /dev/null
+++ b/src/plugins/translation/jakarta-oro-2.0.8.jar
Binary files differ
diff --git a/src/plugins/translation/plugin.properties b/src/plugins/translation/plugin.properties
new file mode 100644
index 0000000..5fd9cad
--- /dev/null
+++ b/src/plugins/translation/plugin.properties
@@ -0,0 +1,6 @@
+pluginName=MOTODEV Studio for Android Automatic Translation Support Plug-in
+providerName=Motorola Mobility, Inc.
+copyright=Copyright (C) 2012 The Android Open Source Project
+
+preferencePageName=Google Translate
+translator.google=Google Translate
diff --git a/src/plugins/translation/plugin.xml b/src/plugins/translation/plugin.xml
new file mode 100644
index 0000000..ff28919
--- /dev/null
+++ b/src/plugins/translation/plugin.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?eclipse version="3.4"?>
+<plugin>
+
+ <extension
+ point="org.eclipse.ui.preferencePages">
+ <page
+ category="com.motorola.studio.platform.ui.preference"
+ class="com.motorola.studio.android.localization.translators.preferences.ui.TranslationPreferencePage"
+ id="com.motorola.studio.android.localization.translators.preferencepage"
+ name="%preferencePageName">
+ </page>
+ </extension>
+
+ <extension
+ id="org.eclipse.sequoyah.localization.tools.googleTranslator"
+ name="%translator.google"
+ point="org.eclipse.sequoyah.localization.tools.translator">
+ <translator
+ brandingImage="icons/google-branding.png"
+ class="com.motorola.studio.android.localization.translators.GoogleTranslator"
+ name="%translator.google">
+ </translator>
+ </extension>
+</plugin>
diff --git a/src/plugins/translation/src/com/motorola/studio/android/json/JSONArray.java b/src/plugins/translation/src/com/motorola/studio/android/json/JSONArray.java
new file mode 100644
index 0000000..91d2491
--- /dev/null
+++ b/src/plugins/translation/src/com/motorola/studio/android/json/JSONArray.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.json;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Stack;
+
+public class JSONArray extends JSONValue
+{
+ private final List<JSONValue> value;
+
+ public JSONArray(List<JSONValue> value)
+ {
+ this.value = value;
+ }
+
+ @Override
+ public List<JSONValue> getValue()
+ {
+ return value;
+ }
+
+ static JSONValue parseValues(List<Character> json)
+ {
+ Stack<Character> stack = new Stack<Character>();
+ List<JSONValue> values = new ArrayList<JSONValue>();
+ boolean parsed = false;
+ while (!parsed)
+ {
+ Character next = json.get(0);
+ if (next == '[')
+ {
+ json.remove(0);
+ stack.push('[');
+ }
+ else if (next == ']')
+ {
+ json.remove(0);
+ if (stack.pop() != '[')
+ {
+ throw new IllegalArgumentException();
+ }
+ else
+ {
+ parsed = true;
+ }
+ }
+ else if (next == ' ' || next == '\r' || next == '\n' || next == ',')
+ {
+ json.remove(0);
+ }
+ else
+ {
+ values.add(JSONValueParser.parse(json));
+ }
+ }
+ return new JSONArray(values);
+ }
+
+ @Override
+ public String toString()
+ {
+ String string = "[";
+ Iterator<JSONValue> objectIterator = value.iterator();
+ while (objectIterator.hasNext())
+ {
+ string += objectIterator.next().toString();
+ if (objectIterator.hasNext())
+ {
+ string += ",";
+ }
+ }
+ string += "]";
+
+ return string;
+ }
+}
diff --git a/src/plugins/translation/src/com/motorola/studio/android/json/JSONBoolean.java b/src/plugins/translation/src/com/motorola/studio/android/json/JSONBoolean.java
new file mode 100644
index 0000000..4332471
--- /dev/null
+++ b/src/plugins/translation/src/com/motorola/studio/android/json/JSONBoolean.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.json;
+
+import java.util.List;
+
+public class JSONBoolean extends JSONValue
+{
+ public final boolean value;
+
+ public JSONBoolean(boolean value)
+ {
+ this.value = value;
+ }
+
+ @Override
+ public Object getValue()
+ {
+ return value;
+ }
+
+ static JSONValue parseValues(List<Character> json)
+ {
+ boolean parsed = false;
+ boolean value = false;
+
+ while (!parsed)
+ {
+ Character next = json.get(0);
+
+ if (next == 't' && json.get(1) == 'r' && json.get(2) == 'u' && json.get(3) == 'e')
+ {
+ for (int i = 0; i < 4; i++)
+ {
+ json.remove(0);
+ }
+ parsed = true;
+ value = true;
+ }
+ else if (next == 'f' && json.get(1) == 'a' && json.get(2) == 'l' && json.get(3) == 's'
+ && json.get(4) == 'e')
+ {
+ for (int i = 0; i < 5; i++)
+ {
+ json.remove(0);
+ }
+ parsed = true;
+ }
+ else
+ {
+ throw new IllegalArgumentException();
+ }
+
+ }
+ return new JSONBoolean(value);
+ }
+
+ @Override
+ public String toString()
+ {
+ return Boolean.toString(value);
+ }
+
+}
diff --git a/src/plugins/translation/src/com/motorola/studio/android/json/JSONNull.java b/src/plugins/translation/src/com/motorola/studio/android/json/JSONNull.java
new file mode 100644
index 0000000..792bce3
--- /dev/null
+++ b/src/plugins/translation/src/com/motorola/studio/android/json/JSONNull.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.json;
+
+import java.util.List;
+
+public class JSONNull extends JSONValue
+{
+ @Override
+ public Object getValue()
+ {
+ return null;
+ }
+
+ static JSONValue parseValues(List<Character> json)
+ {
+ boolean parsed = false;
+
+ while (!parsed)
+ {
+ Character next = json.get(0);
+
+ if (next == 'n' && json.get(1) == 'u' && json.get(2) == 'l' && json.get(3) == 'l')
+ {
+ for (int i = 0; i < 4; i++)
+ {
+ json.remove(0);
+ }
+ parsed = true;
+ }
+ else
+ {
+ throw new IllegalArgumentException();
+ }
+
+ }
+ return new JSONNull();
+ }
+
+ @Override
+ public String toString()
+ {
+ return "null";
+ }
+}
diff --git a/src/plugins/translation/src/com/motorola/studio/android/json/JSONNumber.java b/src/plugins/translation/src/com/motorola/studio/android/json/JSONNumber.java
new file mode 100644
index 0000000..d10c9ca
--- /dev/null
+++ b/src/plugins/translation/src/com/motorola/studio/android/json/JSONNumber.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.json;
+
+import java.util.List;
+
+public class JSONNumber extends JSONValue
+{
+
+ private final int value;
+
+ public JSONNumber(int value)
+ {
+ this.value = value;
+ }
+
+ @Override
+ public Object getValue()
+ {
+ return value;
+ }
+
+ static JSONValue parseValues(List<Character> json)
+ {
+ boolean parsed = false;
+ StringBuilder number = new StringBuilder();
+
+ while (!parsed)
+ {
+ Character next = json.get(0);
+ if (next >= 48 && next <= 57)
+ {
+ json.remove(0);
+ number.append(next);
+ }
+ else
+ {
+ parsed = true;
+ }
+ }
+
+ return new JSONNumber(Integer.parseInt(number.toString()));
+
+ }
+
+ @Override
+ public String toString()
+ {
+ return Integer.toString(value);
+ }
+
+}
diff --git a/src/plugins/translation/src/com/motorola/studio/android/json/JSONObject.java b/src/plugins/translation/src/com/motorola/studio/android/json/JSONObject.java
new file mode 100644
index 0000000..5b64abe
--- /dev/null
+++ b/src/plugins/translation/src/com/motorola/studio/android/json/JSONObject.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.json;
+
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+import java.util.Stack;
+
+public class JSONObject extends JSONValue
+{
+ private final Set<JSONPair> value;
+
+ public JSONObject(Set<JSONPair> value)
+ {
+ this.value = value;
+ }
+
+ @Override
+ public Set<JSONPair> getValue()
+ {
+ return value;
+ }
+
+ static JSONValue parseValues(List<Character> json)
+ {
+ Stack<Character> stack = new Stack<Character>();
+ Set<JSONPair> values = new HashSet<JSONPair>();
+ boolean parsed = false;
+ while (!parsed)
+ {
+ Character next = json.get(0);
+
+ if (next == '{')
+ {
+ json.remove(0);
+ stack.push('{');
+ }
+ else if (next == '}')
+ {
+ json.remove(0);
+ if (stack.pop() != '{')
+ {
+ throw new IllegalArgumentException();
+ }
+ else
+ {
+ parsed = true;
+ }
+ }
+ else if (next == ' ' || next == '\r' || next == '\n' || next == ',')
+ {
+ json.remove(0);
+ }
+ else if (next == '"')
+ {
+ values.add(JSONPair.parse(json));
+ }
+ }
+ return new JSONObject(values);
+ }
+
+ @Override
+ public String toString()
+ {
+ String string = "{";
+ Iterator<JSONPair> objectIterator = value.iterator();
+ while (objectIterator.hasNext())
+ {
+ string += objectIterator.next().toString();
+ if (objectIterator.hasNext())
+ {
+ string += ",";
+ }
+ }
+ string += "}";
+
+ return string;
+ }
+
+}
diff --git a/src/plugins/translation/src/com/motorola/studio/android/json/JSONPair.java b/src/plugins/translation/src/com/motorola/studio/android/json/JSONPair.java
new file mode 100644
index 0000000..2a6424b
--- /dev/null
+++ b/src/plugins/translation/src/com/motorola/studio/android/json/JSONPair.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.json;
+
+import java.util.List;
+
+public class JSONPair
+{
+ private final String name;
+
+ private final JSONValue value;
+
+ public JSONPair(String name, JSONValue value)
+ {
+ this.name = name;
+ this.value = value;
+ }
+
+ public String getName()
+ {
+ return name;
+ }
+
+ public JSONValue getValue()
+ {
+ return value;
+ }
+
+ public static JSONPair parse(List<Character> json)
+ {
+ String name = null;
+ JSONValue value = null;
+ boolean parsed = false;
+ while (!parsed)
+ {
+ Character next = json.get(0);
+ if (next == '"')
+ {
+ name = parseName(json);
+ }
+ else if ((next == ' ') || (next == '\r') || (next == '\n'))
+ {
+ json.remove(0);
+ }
+ else if (next == ':')
+ {
+ json.remove(0);
+ value = JSONValueParser.parse(json);
+ parsed = true;
+ }
+ }
+
+ return new JSONPair(name, value);
+ }
+
+ private static String parseName(List<Character> json)
+ {
+ String name = null;
+ StringBuilder nameBuilder = new StringBuilder();
+ boolean specialChar = false;
+ Character next;
+ json.remove(0);
+ while (name == null)
+ {
+ next = json.remove(0);
+ if ((next == '"'))
+ {
+ if (specialChar)
+ {
+ specialChar = false;
+ }
+ else
+ {
+ name = nameBuilder.toString();
+ }
+ }
+ else if (next == '\\')
+ {
+ specialChar = true;
+ }
+ else
+ {
+ specialChar = false;
+ nameBuilder.append(next);
+ }
+ }
+ return name;
+ }
+
+ @Override
+ public String toString()
+ {
+ return "\"" + name + "\":" + value.toString();
+ }
+
+}
diff --git a/src/plugins/translation/src/com/motorola/studio/android/json/JSONString.java b/src/plugins/translation/src/com/motorola/studio/android/json/JSONString.java
new file mode 100644
index 0000000..c341bba
--- /dev/null
+++ b/src/plugins/translation/src/com/motorola/studio/android/json/JSONString.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.json;
+
+import java.util.List;
+
+public class JSONString extends JSONValue
+{
+ private final String value;
+
+ public JSONString(String value)
+ {
+ this.value = value;
+ }
+
+ @Override
+ public String getValue()
+ {
+ return value;
+ }
+
+ public static JSONValue parseValues(List<Character> json)
+ {
+ String value = null;
+ boolean parsed = false;
+ while (!parsed)
+ {
+ Character next = json.get(0);
+ if (next == '"')
+ {
+
+ value = parseName(json);
+ parsed = true;
+ }
+ }
+
+ return new JSONString(value);
+ }
+
+ private static String parseName(List<Character> json)
+ {
+ String name = null;
+ StringBuilder nameBuilder = new StringBuilder();
+ boolean specialChar = false;
+ Character next;
+ json.remove(0);
+ while (name == null)
+ {
+ next = json.remove(0);
+ if (next == '"')
+ {
+ if (specialChar)
+ {
+ specialChar = false;
+ nameBuilder.append(next);
+ }
+ else
+ {
+ name = nameBuilder.toString();
+ }
+ }
+ else if (next == '\\')
+ {
+ specialChar = true;
+ }
+ else
+ {
+ specialChar = false;
+ nameBuilder.append(next);
+ }
+ }
+ return name;
+ }
+
+ @Override
+ public String toString()
+ {
+ return "\"" + value + "\"";
+ }
+
+}
diff --git a/src/plugins/translation/src/com/motorola/studio/android/json/JSONValue.java b/src/plugins/translation/src/com/motorola/studio/android/json/JSONValue.java
new file mode 100644
index 0000000..4738177
--- /dev/null
+++ b/src/plugins/translation/src/com/motorola/studio/android/json/JSONValue.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.json;
+
+import java.util.List;
+
+public abstract class JSONValue
+{
+ public abstract Object getValue();
+
+ static JSONValue parse(List<Character> json)
+ {
+ return null;
+ }
+}
diff --git a/src/plugins/translation/src/com/motorola/studio/android/json/JSONValueParser.java b/src/plugins/translation/src/com/motorola/studio/android/json/JSONValueParser.java
new file mode 100644
index 0000000..816d90c
--- /dev/null
+++ b/src/plugins/translation/src/com/motorola/studio/android/json/JSONValueParser.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.json;
+
+import java.util.List;
+
+public class JSONValueParser
+{
+ private JSONValueParser()
+ {
+ };
+
+ static JSONValue parse(List<Character> json)
+ {
+ JSONValue value = null;
+
+ boolean parsed = false;
+
+ while (!parsed)
+ {
+ Character next = json.get(0);
+
+ if (next == '{')
+ {
+ value = JSONObject.parse(json);
+ parsed = true;
+ }
+ else if (next == '[')
+ {
+ value = JSONArray.parse(json);
+ parsed = true;
+ }
+ else if (next == '"')
+ {
+ value = JSONString.parse(json);
+ parsed = true;
+ }
+ else if (next == 'n')
+ {
+ value = JSONNull.parse(json);
+ parsed = true;
+ }
+ else if (next == 't' || next == 'f')
+ {
+ value = JSONBoolean.parse(json);
+ parsed = true;
+ }
+ else if (next >= 48 && next <= 57)
+ {
+ value = JSONNumber.parse(json);
+ parsed = true;
+ }
+ else if (next == ' ' || next == '\r' || next == '\n')
+ {
+ json.remove(0);
+ }
+
+ }
+ return value;
+ }
+}
diff --git a/src/plugins/translation/src/com/motorola/studio/android/json/Jason.java b/src/plugins/translation/src/com/motorola/studio/android/json/Jason.java
new file mode 100644
index 0000000..0b0b039
--- /dev/null
+++ b/src/plugins/translation/src/com/motorola/studio/android/json/Jason.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.motorola.studio.android.json;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * This class is responsible to parse a JSON string into objects
+ * Visit {@link http://www.json.org/} for more information
+ *
+ */
+public class Jason
+{
+ private final Set<JSONObject> objects;
+
+ public Jason(String value)
+ {
+ objects = new HashSet<JSONObject>();
+ stip(value);
+ }
+
+ public Set<JSONObject> getJSON()
+ {
+ return objects;
+ }
+
+ private void stip(String value)
+ {
+ List<Character> json = new ArrayList<Character>();
+ for (char c : value.toCharArray())
+ {
+ json.add(c);
+ }
+
+ while (json.size() > 0)
+ {
+ Character next = json.get(0);
+
+ if (next == '{')
+ {
+ JSONObject object = (JSONObject) JSONObject.parse(json);
+ objects.add(object);
+ }
+ else if ((next == ' ') || (next == '\r') || (next == '\n') || (next == ','))
+ {
+ json.remove(0);
+ }
+ }
+ }
+
+ @Override
+ public String toString()
+ {
+ String string = "";
+ Iterator<JSONObject> objectIterator = objects.iterator();
+ while (objectIterator.hasNext())
+ {
+ string += objectIterator.next().toString();
+ if (objectIterator.hasNext())
+ {
+ string += ",";
+ }
+ }
+ return string;
+ }
+}
diff --git a/src/plugins/translation/src/com/motorola/studio/android/localization/translators/GoogleTranslator.java b/src/plugins/translation/src/com/motorola/studio/android/localization/translators/GoogleTranslator.java
new file mode 100644
index 0000000..4e6c0a5
--- /dev/null
+++ b/src/plugins/translation/src/com/motorola/studio/android/localization/translators/GoogleTranslator.java
@@ -0,0 +1,946 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.localization.translators;
+
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.net.Authenticator;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLEncoder;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.commons.httpclient.Credentials;
+import org.apache.commons.httpclient.DefaultHttpMethodRetryHandler;
+import org.apache.commons.httpclient.HttpClient;
+import org.apache.commons.httpclient.HttpException;
+import org.apache.commons.httpclient.HttpStatus;
+import org.apache.commons.httpclient.UsernamePasswordCredentials;
+import org.apache.commons.httpclient.auth.AuthScope;
+import org.apache.commons.httpclient.methods.GetMethod;
+import org.apache.commons.httpclient.params.HttpMethodParams;
+import org.eclipse.core.internal.net.ProxyManager;
+import org.eclipse.core.net.proxy.IProxyData;
+import org.eclipse.core.net.proxy.IProxyService;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.sequoyah.localization.tools.datamodel.node.TranslationResult;
+import org.eclipse.sequoyah.localization.tools.extensions.classes.ITranslator;
+import org.eclipse.sequoyah.localization.tools.extensions.implementation.generic.ITranslateDialog;
+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.Composite;
+import org.eclipse.swt.widgets.Link;
+
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.common.proxy.ProxyAuthenticator;
+import com.motorola.studio.android.common.utilities.EclipseUtils;
+import com.motorola.studio.android.json.JSONArray;
+import com.motorola.studio.android.json.JSONObject;
+import com.motorola.studio.android.json.JSONPair;
+import com.motorola.studio.android.json.JSONString;
+import com.motorola.studio.android.json.JSONValue;
+import com.motorola.studio.android.json.Jason;
+import com.motorola.studio.android.localization.translators.i18n.TranslateNLS;
+
+/**
+ * Uses the Google translator web service (via executing a http request and
+ * parsing the answer) in order to translate a text string.
+ *
+ * Google v2 supports only one source and one destination per request, but support many words.
+ *
+ */
+@SuppressWarnings("restriction")
+public final class GoogleTranslator extends ITranslator implements GoogleTranslatorConstants
+{
+
+ /**
+ * Translate all words from some source language to destination language.
+ * This method handles any needed splits in original request due to API limitations.
+ * @param words the words being translated
+ * @param fromLanguage the origin language
+ * @param toLanguage the destination language
+ * @param monitor progress monitor
+ * @return a list of translation results
+ * @throws Exception
+ */
+ public List<TranslationResult> translate(List<String> words, String fromLanguage,
+ String toLanguage, IProgressMonitor monitor) throws Exception
+ {
+ List<TranslationResult> translationResults = new ArrayList<TranslationResult>();
+ int characterCount = 0;
+ int MAX_REQUEST_SIZE = getMaxQuerySize(fromLanguage, toLanguage);
+
+ int maxRequestSize = MAX_REQUEST_SIZE;
+ List<String> wordsToTranslate = new ArrayList<String>();
+
+ Iterator<String> wordsIterator = words.iterator();
+ int counter = 0;
+
+ while (wordsIterator.hasNext())
+ {
+ maxRequestSize -= STRING_PAR.length();
+ String word = wordsIterator.next();
+ /* try to add some more words to the request.
+ * If there is no more room left to request, execute the translation and continue afterwards
+ */
+ if (characterCount + word.length() < maxRequestSize)
+ {
+ wordsToTranslate.add(word);
+ characterCount += word.length();
+ }
+ else
+ {
+ URL translationURL =
+ createTranslationURL(wordsToTranslate, fromLanguage, toLanguage);
+ String httpRequestResponseBody = executeHttpGetRequest(translationURL);
+
+ List<String> responses = parseTranslationResponse(httpRequestResponseBody);
+
+ for (int i = 0; i < wordsToTranslate.size(); i++)
+ {
+ translationResults.add(new TranslationResult(words.get(counter++), this,
+ responses.get(i), fromLanguage, toLanguage, Calendar.getInstance()
+ .getTime(), true));
+ }
+ characterCount = 0;
+ maxRequestSize = MAX_REQUEST_SIZE;
+ wordsToTranslate.clear();
+ wordsToTranslate.add(word);
+ }
+ }
+
+ /*
+ * execute the request with remaining sentences
+ */
+ if (!wordsToTranslate.isEmpty())
+ {
+ URL translationURL = createTranslationURL(wordsToTranslate, fromLanguage, toLanguage);
+ String httpRequestResponseBody = executeHttpGetRequest(translationURL);
+ List<String> responses = parseTranslationResponse(httpRequestResponseBody);
+
+ for (int i = 0; i < wordsToTranslate.size(); i++)
+ {
+ translationResults.add(new TranslationResult(words.get(counter++), this, responses
+ .get(i), fromLanguage, toLanguage, Calendar.getInstance().getTime(), true));
+ }
+ }
+
+ return translationResults;
+ }
+
+ /**
+ * Translates a string.
+ *
+ * @param text The String to be translated.
+ * @param from Original language.
+ * @param to Target language.
+ * @return The translated String.
+ * @throws Exception on errors.
+ */
+ @Override
+ public TranslationResult translate(final String text, String from, String to) throws Exception
+ {
+ TranslationResult translationResult;
+
+ if (text != null && !text.equals("") && text.length() < getMaxQuerySize(from, to)) //$NON-NLS-1$
+ {
+ String httpResult = ""; //$NON-NLS-1$
+ URL url = null;
+
+ // Creates the URL to be used as request
+ try
+ {
+ List<String> sentences = new ArrayList<String>();
+ sentences.add(text);
+ url = createTranslationURL(sentences, from, to);
+ httpResult = executeHttpGetRequest(url);
+ translationResult =
+ new TranslationResult(text, this, parseTranslationResponse(httpResult).get(
+ 0), from, to, Calendar.getInstance().getTime(), true);
+ }
+ catch (UnsupportedEncodingException e)
+ {
+ throw new HttpException(TranslateNLS.GoogleTranslator_Error_UnsupportedEncoding
+ + ENCODING_TYPE);
+ }
+
+ }
+ else if (text.length() >= getMaxQuerySize(from, to))
+ {
+ throw new Exception(TranslateNLS.GoogleTranslator_Error_QueryTooBig);
+ }
+ else
+ {
+ translationResult = new TranslationResult(text, this, text, from, to, new Date(), true);
+ }
+
+ try
+ {
+ String descriptionToLog =
+ StudioLogger.KEY_TRANSLATION_PROVIDER + StudioLogger.VALUE_GOOGLE
+ + StudioLogger.SEPARATOR + StudioLogger.KEY_TRANSLATION_FROM_LANG
+ + from + StudioLogger.SEPARATOR + StudioLogger.KEY_TRANSLATION_TO_LANG
+ + to;
+ StudioLogger.collectUsageData(StudioLogger.WHAT_LOCALIZATION_AUTOMATICTRANSLATION,
+ StudioLogger.KIND_LOCALIZATION, descriptionToLog, TranslationPlugin.PLUGIN_ID,
+ TranslationPlugin.getDefault().getBundle().getVersion().toString());
+ }
+ catch (Throwable t)
+ {
+ // Do nothing, usage data collection is for statistics and should not prevent tool from work
+ }
+
+ return translationResult;
+
+ }
+
+ /**
+ * Translate a single word from one language to several other
+ * @param sentences sentence being translated
+ * @param fromLanguage source language
+ * @param toLanguages target languages
+ */
+ @Override
+ public List<TranslationResult> translate(String sentence, String fromLanguage,
+ List<String> toLanguages) throws Exception
+ {
+ List<TranslationResult> translationResults = new ArrayList<TranslationResult>();
+
+ // Lets start with some checkings, one can never be too careful
+ if (fromLanguage == null || toLanguages == null || toLanguages.isEmpty())
+ {
+ // We must have a FROM and a TO languages
+ throw new IllegalArgumentException(
+ TranslateNLS.GoogleTranslator_Error_ToAndFromLanguagesAreEmpty);
+ }
+ else if (sentence == null || sentence.equals("")) //$NON-NLS-1$
+ {
+ // We must have something to be translated
+ sentence = ""; //$NON-NLS-1$
+ if (toLanguages.size() == 1)
+ {
+ translationResults.add(new TranslationResult("", this, "", fromLanguage, //$NON-NLS-1$ //$NON-NLS-2$
+ toLanguages.get(0), new Date(), true));
+ }
+ }
+ else if (sentence.length() >= getMaxQuerySize(fromLanguage, toLanguages.get(0)))
+ {
+ throw new Exception(TranslateNLS.GoogleTranslator_Error_QueryTooBig);
+ }
+ /*
+ * Delegate the translation to another method
+ */
+ else
+ {
+ for (String toLanguage : toLanguages)
+ {
+ translationResults.add(translate(sentence, fromLanguage, toLanguage));
+ }
+ }
+
+ try
+ {
+ // Collecting usage data for statistic purposes
+ String descriptionToLog =
+ StudioLogger.KEY_TRANSLATION_PROVIDER + StudioLogger.VALUE_GOOGLE
+ + StudioLogger.SEPARATOR + StudioLogger.KEY_TRANSLATION_FROM_LANG
+ + fromLanguage + StudioLogger.SEPARATOR
+ + StudioLogger.KEY_TRANSLATION_TO_LANG + "several languages"; //$NON-NLS-1$
+
+ StudioLogger.collectUsageData(StudioLogger.WHAT_LOCALIZATION_AUTOMATICTRANSLATION,
+ StudioLogger.KIND_LOCALIZATION, descriptionToLog, TranslationPlugin.PLUGIN_ID,
+ TranslationPlugin.getDefault().getBundle().getVersion().toString());
+ }
+ catch (Throwable t)
+ {
+ // Do nothing, usage data collection is for statistics and should not prevent tool from work
+ }
+
+ return translationResults;
+ }
+
+ /**
+ * Translate a list of sentences from one language to another
+ * @param sentences sentences being translated
+ * @param fromLanguage source language
+ * @param toLanguage target language
+ * @param monitor progress monitor
+ */
+ @Override
+ public List<TranslationResult> translateAll(List<String> sentences, String fromLanguage,
+ String toLanguage, IProgressMonitor monitor) throws Exception
+ {
+ // The result (duhh)
+ List<TranslationResult> translationResults = new ArrayList<TranslationResult>();
+
+ // Lets start with some checkings, one can never be too carefull
+ if (fromLanguage == null || toLanguage == null)
+ {
+ // We must have a FROM and a TO languages
+ throw new IllegalArgumentException(
+ TranslateNLS.GoogleTranslator_Error_ToAndFromLanguagesAreEmpty);
+ }
+ else if (sentences == null || sentences.size() == 0)
+ {
+ // We must have something to be translated
+ throw new IllegalArgumentException(TranslateNLS.GoogleTranslator_Error_NoAvailableData);
+ }
+ else
+ {
+ translationResults.addAll(translate(sentences, fromLanguage, toLanguage, monitor));
+ }
+
+ try
+ {
+ // Collecting usage data for statistic purposes
+ String descriptionToLog =
+ StudioLogger.KEY_TRANSLATION_PROVIDER + StudioLogger.VALUE_GOOGLE
+ + StudioLogger.SEPARATOR + StudioLogger.KEY_TRANSLATION_FROM_LANG
+ + fromLanguage + StudioLogger.SEPARATOR
+ + StudioLogger.KEY_TRANSLATION_TO_LANG + toLanguage;
+
+ StudioLogger.collectUsageData(StudioLogger.WHAT_LOCALIZATION_AUTOMATICTRANSLATION,
+ StudioLogger.KIND_LOCALIZATION, descriptionToLog, TranslationPlugin.PLUGIN_ID,
+ TranslationPlugin.getDefault().getBundle().getVersion().toString());
+ }
+ catch (Throwable t)
+ {
+ // Do nothing, usage data collection is for statistics and should not prevent tool from work
+ }
+
+ return translationResults;
+ }
+
+ /**
+ * Translates a list of strings from a list of given languages to other given languages (given by a list, or course),
+ * using google Ajax API's for that.
+ *
+ * The three lists have the same number of elements.
+ *
+ * This comment feels like the "Three Swatch watch switching witches watched switched Swatch watch witches switch",
+ * but I'll let it here anyway.
+ */
+ @Override
+ public List<TranslationResult> translateAll(List<String> words, List<String> fromLanguage,
+ List<String> toLanguage, IProgressMonitor monitor) throws Exception
+ {
+ // The result (duhh)
+ List<TranslationResult> translationResults = new ArrayList<TranslationResult>();
+
+ // Lets start with some checkings, one can never be too carefull
+ if (fromLanguage == null || toLanguage == null)
+ {
+ // We must have a FROM and a TO languages
+ throw new IllegalArgumentException(
+ TranslateNLS.GoogleTranslator_Error_ToAndFromLanguagesAreEmpty);
+ }
+ else if (words == null || words.size() == 0)
+ {
+ // We must have something to be translated
+ throw new IllegalArgumentException(TranslateNLS.GoogleTranslator_Error_NoAvailableData);
+ }
+ else
+ {
+ translationResults.addAll(groupAndTranslate(words, fromLanguage, toLanguage, monitor));
+ }
+
+ try
+ {
+ // Collecting usage data for statistic purposes
+ String descriptionToLog =
+ StudioLogger.KEY_TRANSLATION_PROVIDER + StudioLogger.VALUE_GOOGLE
+ + StudioLogger.SEPARATOR + StudioLogger.KEY_TRANSLATION_FROM_LANG
+ + fromLanguage + StudioLogger.SEPARATOR
+ + StudioLogger.KEY_TRANSLATION_TO_LANG + toLanguage;
+
+ StudioLogger.collectUsageData(StudioLogger.WHAT_LOCALIZATION_AUTOMATICTRANSLATION,
+ StudioLogger.KIND_LOCALIZATION, descriptionToLog, TranslationPlugin.PLUGIN_ID,
+ TranslationPlugin.getDefault().getBundle().getVersion().toString());
+ }
+ catch (Throwable t)
+ {
+ // Do nothing, usage data collection is for statistics and should not prevent tool from work
+ }
+
+ return translationResults;
+ }
+
+ private List<TranslationResult> groupAndTranslate(List<String> words,
+ List<String> fromLanguage, List<String> toLanguage, IProgressMonitor monitor)
+ throws Exception
+ {
+
+ List<TranslationResult> results = new ArrayList<TranslationResult>();
+ /*
+ * Get all words with same source and same destination and group them to make translation more efficient
+ * Notice that this implementation relies on input condition that all lists have the same size and all elements with same index makes one request
+ */
+
+ class StringItem
+ {
+ private String sentence = "";
+
+ private int orderNumber = -1;
+
+ public StringItem(String sentence, int orderNumber)
+ {
+ this.sentence = sentence;
+ this.orderNumber = orderNumber;
+ }
+
+ public Integer getOrderNumber()
+ {
+ return orderNumber;
+ }
+
+ public String getSentence()
+ {
+ return sentence;
+ }
+
+ @Override
+ public String toString()
+ {
+ return orderNumber + "|" + sentence;
+ }
+ }
+
+ /*
+ * This map holds a list of group of stringitems being translated. These items have the same from and to languages
+ * Using linkedHashMap to keep insertionOrder
+ */
+ Map<String, List<StringItem>> sameSourceDestMap =
+ new LinkedHashMap<String, List<StringItem>>();
+ Map<Integer, TranslationResult> translations = new HashMap<Integer, TranslationResult>();
+
+ // group things with same from and to languages
+ for (int i = 0; i < words.size(); i++)
+ {
+ /*
+ * Check if one of the words are big enough to not be translated
+ */
+ if (words.get(i).length() >= getMaxQuerySize(fromLanguage.get(i), toLanguage.get(i))
+ - STRING_PAR.length() - 1)
+ {
+ throw new Exception(TranslateNLS.GoogleTranslator_Error_QueryTooBig);
+ }
+
+ String key = fromLanguage.get(i) + "|" + toLanguage.get(i);
+ List<StringItem> itemsToTranslate = sameSourceDestMap.get(key);
+ if (itemsToTranslate == null)
+ {
+ itemsToTranslate = new ArrayList<StringItem>();
+ sameSourceDestMap.put(key, itemsToTranslate);
+ }
+ itemsToTranslate.add(new StringItem(words.get(i), i));
+ }
+
+ for (String key : sameSourceDestMap.keySet())
+ {
+ List<StringItem> itemsToTranslate = sameSourceDestMap.get(key);
+ List<String> items = new ArrayList<String>();
+ for (StringItem item : itemsToTranslate)
+ {
+ items.add(item.getSentence());
+ }
+ List<TranslationResult> tempResults =
+ translate(items, key.split("\\|")[0], key.split("\\|")[1], monitor);
+ for (int i = 0; i < itemsToTranslate.size(); i++)
+ {
+ translations.put(itemsToTranslate.get(i).getOrderNumber(), tempResults.get(i));
+ }
+
+ }
+
+ for (int i = 0; i < words.size(); i++)
+ {
+ results.add(translations.get(i));
+ }
+
+ return results;
+ }
+
+ private int getMaxQuerySize(String fromLanguage, String toLanguage)
+ {
+ return MAX_QUERY_SIZE - TRANSLATE_URL_WITHOUT_PARAMS.length() - API_KEY_PARAM.length()
+ - getApiKey().length() - SOURCE_PAR.length() - fromLanguage.length()
+ - TARGET_PARAM.length() - toLanguage.length();
+ }
+
+ /**
+ * Parse the translation response of the http request
+ * @param httpRequestResponseBody the response body
+ * @param sourceLanguage the source language
+ * @param destinationLanguage the destination language
+ * @return a list of String objects for the strings translated for source/destination languages pair
+ */
+ private List<String> parseTranslationResponse(String httpRequestResponseBody)
+ {
+ JSONPair translationSection = getTranslationSection(httpRequestResponseBody);
+
+ return getTranslations(translationSection);
+ }
+
+ /*
+ * {
+ "data": {
+ "translations": [
+ {
+ "translatedText": "bla bla bla"
+ },
+ {
+ "translatedText": "foo bar"
+ }
+ ]
+ }
+ }
+ */
+ private JSONPair getTranslationSection(String httpRequestResponseBody)
+ {
+ Jason ripper = new Jason(httpRequestResponseBody);
+ JSONPair translationsSection = null;
+ Iterator<JSONObject> jsonIterator = ripper.getJSON().iterator();
+ while (translationsSection == null && jsonIterator.hasNext())
+ {
+ translationsSection = findPair(jsonIterator.next(), TRANSLATIONS_SECTION);
+ }
+
+ return translationsSection;
+ }
+
+ private static JSONPair findPair(JSONValue origin, String name)
+ {
+ JSONPair pair = null;
+ if (origin instanceof JSONObject)
+ {
+ JSONObject object = (JSONObject) origin;
+ Iterator<JSONPair> pairs = object.getValue().iterator();
+ while (pair == null && pairs.hasNext())
+ {
+ JSONPair jsonPair = pairs.next();
+ if (jsonPair.getName().equals(name))
+ {
+ pair = jsonPair;
+ }
+ else
+ {
+ pair = findPair(jsonPair.getValue(), name);
+ }
+
+ }
+ }
+
+ return pair;
+ }
+
+ private List<String> getTranslations(JSONPair translationSection)
+ {
+ List<String> translations = new ArrayList<String>();
+ if (translationSection.getValue() instanceof JSONArray)
+ {
+ JSONArray translationsArray = (JSONArray) translationSection.getValue();
+ for (JSONValue translationObject : translationsArray.getValue())
+ {
+ translations.add(getTranslation(translationObject));
+ }
+ }
+
+ return translations;
+ }
+
+ /**
+ * @param translationObject
+ * {
+ * "translatedText": "Hallo Welt"
+ * }
+ * @return pure translation
+ * Hallo Welt
+ */
+ private String getTranslation(JSONValue translationObject)
+ {
+ String translation = null;
+ if (translationObject instanceof JSONObject)
+ {
+ JSONObject jsonObject = (JSONObject) translationObject;
+ translation =
+ ((JSONString) jsonObject.getValue().iterator().next().getValue()).getValue();
+ }
+ return translation != null ? fixHTMLTags(translation) : null;
+ }
+
+ private URL createTranslationURL(List<String> wordsToTranslate, String fromLanguage,
+ String toLanguage) throws UnsupportedEncodingException
+ {
+ URL translationURL = null;
+
+ // We need to unescape the ' (apostrophe) before sending it to translation
+ String regex = "\\\\'"; //$NON-NLS-1$
+ Pattern pattern = Pattern.compile(regex);
+
+ StringBuilder urlBuilder = new StringBuilder(TRANSLATE_URL_WITHOUT_PARAMS);
+ urlBuilder.append(URL_PARAMETERS.replace("#FROM#", fromLanguage)
+ .replace("#TO#", toLanguage).replace("#API_KEY#", getApiKey()));
+
+ for (String word : wordsToTranslate)
+ {
+ String wordToTranslate = pattern.matcher(word).replaceAll("'");
+ urlBuilder.append(STRING_PAR);
+ urlBuilder.append(URLEncoder.encode(wordToTranslate, ENCODING_TYPE));
+ }
+
+ try
+ {
+ translationURL = new URL(urlBuilder.toString());
+ }
+ catch (MalformedURLException e)
+ {
+ StudioLogger.error(getClass(), "Unable to create translation URL", e);
+ }
+
+ return translationURL;
+ }
+
+ /**
+ * The Android localization files text must accept three HTML tags: i, b and u.
+ * Nevertheless, google translator returns the close part of this tags with
+ * a extra-space that makes the sintax wrong. This method will try to fix it.
+ * @param originalText
+ * @return the text with the tags fixed
+ */
+ private static String fixHTMLTags(String originalText)
+ {
+ String result = ""; //$NON-NLS-1$
+ if (originalText != null)
+ {
+ result = originalText;
+ }
+ result = originalText.replaceAll("</ b>", "</b>"); //$NON-NLS-1$ //$NON-NLS-2$
+ result = originalText.replaceAll("</ i>", "</i>"); //$NON-NLS-1$ //$NON-NLS-2$
+ result = originalText.replaceAll("</ u>", "</u>"); //$NON-NLS-1$ //$NON-NLS-2$
+
+ result = originalText.replaceAll("</ B>", "</B>"); //$NON-NLS-1$ //$NON-NLS-2$
+ result = originalText.replaceAll("</ I>", "</I>"); //$NON-NLS-1$ //$NON-NLS-2$
+ result = originalText.replaceAll("</ U>", "</U>"); //$NON-NLS-1$ //$NON-NLS-2$
+
+ result = originalText.replaceAll(" \\\\ n ", " \\\\n "); //$NON-NLS-1$ //$NON-NLS-2$
+
+ return result;
+ }
+
+ /**
+ * Creates an HTTP request with the URL, execute it as a get, and returns
+ * the a string with the result.
+ *
+ * @param url
+ * URL to be executed.
+ * @return String with the URL execution result.
+ * @throws IOException
+ * If an exception occurs on transport
+ * @throws HttpException
+ * If an exception occurs on the protocol
+ * @throws Exception
+ * on error.
+ */
+ protected static String executeHttpGetRequest(final URL url) throws HttpException
+ {
+
+ // Checking query size due to google policies
+ if (url.toString().length() > MAX_QUERY_SIZE)
+ {
+ throw new HttpException(TranslateNLS.GoogleTranslator_Error_QueryTooBig);
+ }
+
+ // Try to retrieve proxy configuration to use if necessary
+ IProxyService proxyService = ProxyManager.getProxyManager();
+ IProxyData proxyData = null;
+ if (proxyService.isProxiesEnabled() || proxyService.isSystemProxiesEnabled())
+ {
+ Authenticator.setDefault(new ProxyAuthenticator());
+ String urlStr = url.toString();
+ if (urlStr.startsWith("https"))
+ {
+ proxyData = proxyService.getProxyData(IProxyData.HTTPS_PROXY_TYPE);
+ StudioLogger.debug(GoogleTranslator.class, "Using https proxy"); //$NON-NLS-1$
+ }
+ else if (urlStr.startsWith("http"))
+ {
+ proxyData = proxyService.getProxyData(IProxyData.HTTP_PROXY_TYPE);
+ StudioLogger.debug(GoogleTranslator.class, "Using http proxy"); //$NON-NLS-1$
+ }
+ else
+ {
+ StudioLogger.debug(GoogleTranslator.class, "Not using any proxy"); //$NON-NLS-1$
+ }
+ }
+
+ // Creates the http client and the method to be executed
+ HttpClient client = null;
+ client = new HttpClient();
+
+ // If there is proxy data, work with it
+ if (proxyData != null)
+ {
+ if (proxyData.getHost() != null)
+ {
+ // Sets proxy host and port, if any
+ client.getHostConfiguration().setProxy(proxyData.getHost(), proxyData.getPort());
+ }
+
+ if (proxyData.getUserId() != null && proxyData.getUserId().trim().length() > 0)
+ {
+ // Sets proxy user and password, if any
+ Credentials cred =
+ new UsernamePasswordCredentials(proxyData.getUserId(),
+ proxyData.getPassword() == null ? "" : proxyData.getPassword()); //$NON-NLS-1$
+ client.getState().setProxyCredentials(AuthScope.ANY, cred);
+ }
+ }
+
+ // Creating the method to be executed, the URL at this point is enough
+ // because it is complete
+ GetMethod method = new GetMethod(url.toString());
+
+ // Set method to be retried three times in case of error
+ method.getParams().setParameter(HttpMethodParams.RETRY_HANDLER,
+ new DefaultHttpMethodRetryHandler(RETRIES, false));
+
+ method.setRequestHeader(REFERER_HEADER, REFERER_SITE);
+
+ // Set the connection timeout
+ client.getHttpConnectionManager().getParams().setConnectionTimeout(new Integer(TIMEOUT));
+
+ String result = ""; //$NON-NLS-1$
+ try
+ {
+ // Execute the method.
+ int statusCode;
+ try
+ {
+ statusCode = client.executeMethod(method);
+ result = method.getResponseBodyAsString(MAX_SIZE);
+ }
+ catch (IOException e)
+ {
+ throw new HttpException(TranslateNLS.GoogleTranslator_Error_CannotConnectToServer
+ + e.getMessage());
+ }
+
+ checkStatusCode(statusCode, result);
+
+ // Unescape any possible unicode char
+ result = unescapeUnicode(result);
+
+ // Unescape any possible HTML sequence
+ result = unescapeHTML(result);
+
+ }
+
+ finally
+ {
+ // Release the connection.
+ method.releaseConnection();
+ }
+
+ return result;
+ }
+
+ private static void checkStatusCode(int statusCode, String response) throws HttpException
+ {
+ switch (statusCode)
+ {
+ case HttpStatus.SC_OK:
+ //do nothing
+ break;
+ case HttpStatus.SC_BAD_REQUEST:
+ throw new HttpException(NLS.bind(
+ TranslateNLS.GoogleTranslator_ErrorMessageExecutingRequest,
+ getErrorMessage(response)));
+
+ case HttpStatus.SC_REQUEST_URI_TOO_LONG:
+ throw new HttpException(TranslateNLS.GoogleTranslator_Error_QueryTooBig);
+
+ case HttpStatus.SC_FORBIDDEN:
+ throw new HttpException(NLS.bind(
+ TranslateNLS.GoogleTranslator_ErrorMessageNoValidTranslationReturned,
+ getErrorMessage(response)));
+
+ default:
+ throw new HttpException(NLS.bind(
+ TranslateNLS.GoogleTranslator_Error_HTTPRequestError, new Object[]
+ {
+ statusCode, getErrorMessage(response)
+ }));
+
+ }
+ }
+
+ /**
+ * According to APIv2, the error message is in the end of the response
+ * {
+ "error": {
+ "errors": [
+ {
+ "domain": "global",
+ "reason": "invalid",
+ "message": "Invalid Value"
+ }
+ ],
+ "code": 400,
+ "message": "Invalid Value"
+ }
+ }
+ * @param response the method response body
+ * @return the error message
+ */
+ private static String getErrorMessage(String response)
+ {
+ Jason ripper = new Jason(response);
+ JSONPair translationsSection = null;
+ Iterator<JSONObject> jsonIterator = ripper.getJSON().iterator();
+ while (translationsSection == null && jsonIterator.hasNext())
+ {
+ translationsSection = findPair(jsonIterator.next(), MESSAGE_TEXT);
+ }
+
+ return translationsSection != null ? ((JSONString) translationsSection.getValue())
+ .getValue() : null;
+ }
+
+ /**
+ * Unescape any HTML sequence that exists inside the string. For example,
+ * the sequence &#39; will be changed to the ' symbol
+ *
+ * @param source
+ * original text
+ * @return the result
+ */
+ private static String unescapeHTML(String source)
+ {
+ Pattern p = Pattern.compile("&#([0-9]+);"); //$NON-NLS-1$
+ String result = source;
+ Matcher m = p.matcher(result);
+ while (m.find())
+ {
+ char c = (char) Integer.parseInt(m.group(1));
+ if (c == "'".charAt(0)) //$NON-NLS-1$
+ {
+ // Apostrophes must be escaped by preceding it with a backslash (\) on the XML file
+ result = result.replaceAll(m.group(0), "\\\\'"); //$NON-NLS-1$
+ }
+ else
+ {
+ result = result.replaceAll(m.group(0), "" + c); //$NON-NLS-1$
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Unescape any Unicode sequence that exists inside the string.
+ *
+ * For example, the sequence \u0000 will be changed to the symbol
+ * correnponded to the 0000 unicode value.
+ *
+ * @param source
+ * original text
+ * @return the result
+ */
+ private static String unescapeUnicode(String source)
+ {
+ int i = 0, len = source.length();
+ char c;
+ StringBuffer buffer = new StringBuffer(len);
+ while (i < len)
+ {
+ c = source.charAt(i++);
+ if (c == '\\')
+ {
+ if (i < len)
+ {
+ c = source.charAt(i++);
+ if (c == 'u')
+ {
+ c = (char) Integer.parseInt(source.substring(i, i + 4), 16);
+ i += 4;
+ }
+ }
+ }
+ buffer.append(c);
+ }
+ return buffer.toString();
+ }
+
+ private static String getApiKey()
+ {
+ String apiKey = GoogleTranslatorConstants.API_KEY;
+ IPreferenceStore prefStore = TranslationPlugin.getDefault().getPreferenceStore();
+ if (!prefStore.isDefault(GoogleTranslatorConstants.API_KEY_VALUE_PREFERENCE))
+ {
+ apiKey = prefStore.getString(GoogleTranslatorConstants.API_KEY_VALUE_PREFERENCE);
+ if (apiKey == null)
+ {
+ apiKey = GoogleTranslatorConstants.API_KEY;
+ }
+ }
+
+ return apiKey;
+ }
+
+ @Override
+ public Composite createCustomArea(Composite parent, final ITranslateDialog dialog)
+ {
+ Composite mainComposite = new Composite(parent, SWT.NONE);
+ mainComposite.setLayout(new GridLayout(1, false));
+
+ final Link prefPageLink = new Link(mainComposite, SWT.NONE);
+ prefPageLink.setText(TranslateNLS.GoogleTranslator_ChangeAPIkeyLabel);
+ prefPageLink.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, true, true, 1, 1));
+ prefPageLink.addSelectionListener(new SelectionAdapter()
+ {
+ @Override
+ public void widgetSelected(SelectionEvent e)
+ {
+ EclipseUtils.openPreference(prefPageLink.getShell(),
+ "com.motorola.studio.android.localization.translators.preferencepage"); //$NON-NLS-1$
+ dialog.validate();
+ }
+ });
+ mainComposite.setLayoutData(new GridData(SWT.RIGHT, SWT.BOTTOM, false, false));
+ return mainComposite;
+ }
+
+ @Override
+ public String canTranslate(String fromLanguage, String[] toLanguages)
+ {
+ return getApiKey() == null || GoogleTranslatorConstants.API_KEY.equals(getApiKey())
+ ? TranslateNLS.GoogleTranslator_ErrorNoAPIkeySet : null;
+ }
+} \ No newline at end of file
diff --git a/src/plugins/translation/src/com/motorola/studio/android/localization/translators/GoogleTranslatorConstants.java b/src/plugins/translation/src/com/motorola/studio/android/localization/translators/GoogleTranslatorConstants.java
new file mode 100644
index 0000000..b6730c9
--- /dev/null
+++ b/src/plugins/translation/src/com/motorola/studio/android/localization/translators/GoogleTranslatorConstants.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.localization.translators;
+
+import org.eclipse.sequoyah.localization.tools.extensions.implementation.generic.TranslatorConstants;
+
+/**
+ * Constants used by the GoogleTranslator class
+ * and its users.
+ */
+public interface GoogleTranslatorConstants extends TranslatorConstants
+{
+
+ //API Key to identify MOTODEV site
+ static final String API_KEY =
+ "ABQIAAAAzLP1ONRdncTVQSc4T1g2MRT7zv61Fj6qXODo5OU8i7eIwTs2GRQjZ2moI_dkzMckcgUZys7M9wAMvQ";
+
+ public static final String API_KEY_VALUE_PREFERENCE = "google.translator.apikey";
+
+ // Encoding type
+ public static final String ENCODING_TYPE = "UTF-8";
+
+ /**
+ * Parameter used to create URL
+ */
+ public static final String SOURCE_PAR = "&source=";
+
+ /**
+ * Parameter used to create URL
+ */
+ public static final String STRING_PAR = "&q=";
+
+ /**
+ * Parameter used to create URL
+ */
+ public static final String TARGET_PARAM = "&target=";
+
+ /**
+ * Parameter used to create URL
+ */
+ public static final String API_KEY_PARAM = "&key=";
+
+ /**
+ * The base URL to access the translation service.
+ * the #FROM# and #TO# parts are replaced on execution time.
+ */
+ public static final String URL_PARAMETERS = API_KEY_PARAM + "#API_KEY#" + SOURCE_PAR + "#FROM#"
+ + TARGET_PARAM + "#TO#";
+
+ /**
+ * The base URL to access the translation service, without the parameters.
+ * The parameters are created on real time when needed.
+ */
+ public static final String TRANSLATE_URL_WITHOUT_PARAMS =
+ "https://www.googleapis.com/language/translate/v2?prettyprint=false";
+
+ /**
+ * The base URL to access the translation service.
+ * the #FROM# and #TO# parts are replaced on execution time.
+ */
+ public static final String BASE_TRANSLATE_URL = TRANSLATE_URL_WITHOUT_PARAMS + URL_PARAMETERS;
+
+ /**
+ * Text that appears just before the translated text begins
+ * on a typical answer from the web server
+ */
+ public static final String TRANSLATED_TEXT_KEY = "translatedText";
+
+ /**
+ * Text defining the translation section of response
+ */
+ public static final String TRANSLATIONS_SECTION = "translations";
+
+ /**
+ * Text defining the error section of response
+ */
+ public static final String ERROR_SECTION = "error";
+
+ /**
+ * Text defining the message section of response
+ */
+ public static final String MESSAGE_TEXT = "message";
+
+ //Max string size
+ public static int MAX_SIZE = 100000;
+
+ //Max string size
+ public static int MAX_QUERY_SIZE = 2000; // google rules!! Do not change without VT
+
+ //Number of retries for the http request when there are connection problems
+ public static final int RETRIES = 0;
+
+ //Timeout in miliseconds for the http queries
+ public static final int TIMEOUT = 4000;
+
+ //HTTP Header constant to set the referer
+ public static final String REFERER_HEADER = "Referer";
+
+ // Site to be used as a referer site
+ public static final String REFERER_SITE = "http://developer.motorola.com";
+
+}
diff --git a/src/plugins/translation/src/com/motorola/studio/android/localization/translators/TranslationPlugin.java b/src/plugins/translation/src/com/motorola/studio/android/localization/translators/TranslationPlugin.java
new file mode 100644
index 0000000..f97b55d
--- /dev/null
+++ b/src/plugins/translation/src/com/motorola/studio/android/localization/translators/TranslationPlugin.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.motorola.studio.android.localization.translators;
+
+import org.eclipse.ui.plugin.AbstractUIPlugin;
+import org.osgi.framework.BundleContext;
+
+import com.motorola.studio.android.common.log.StudioLogger;
+
+public class TranslationPlugin extends AbstractUIPlugin
+{
+
+ /**
+ * The plug-in ID
+ */
+ public static final String PLUGIN_ID = "com.motorola.studio.android.translation";
+
+ // The shared instance
+ private static TranslationPlugin plugin;
+
+ public TranslationPlugin()
+ {
+ plugin = this;
+ }
+
+ /**
+ * Returns the shared instance
+ *
+ * @return the shared instance
+ */
+ public static TranslationPlugin getDefault()
+ {
+ return plugin;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.ui.plugin.AbstractUIPlugin#start(org.osgi.framework.BundleContext)
+ */
+ @Override
+ public void start(BundleContext context) throws Exception
+ {
+ StudioLogger.debug(TranslationPlugin.class,
+ "Starting MOTODEV Studio for Android Translation Plugin...");
+
+ super.start(context);
+
+ StudioLogger.debug(TranslationPlugin.class,
+ "MOTODEV Studio for Android Translation Plugin started.");
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.ui.plugin.AbstractUIPlugin#stop(org.osgi.framework.BundleContext)
+ */
+ @Override
+ public void stop(BundleContext context) throws Exception
+ {
+ plugin = null;
+ super.stop(context);
+ }
+
+}
diff --git a/src/plugins/translation/src/com/motorola/studio/android/localization/translators/i18n/TranslateNLS.java b/src/plugins/translation/src/com/motorola/studio/android/localization/translators/i18n/TranslateNLS.java
new file mode 100644
index 0000000..32dc8fa
--- /dev/null
+++ b/src/plugins/translation/src/com/motorola/studio/android/localization/translators/i18n/TranslateNLS.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.motorola.studio.android.localization.translators.i18n;
+
+import org.eclipse.osgi.util.NLS;
+
+public class TranslateNLS extends NLS
+{
+ static
+ {
+ NLS.initializeMessages(
+ "com.motorola.studio.android.localization.translators.i18n.translateNLS", //$NON-NLS-1$
+ TranslateNLS.class);
+ }
+
+ public static String GoogleTranslator_ChangeAPIkeyLabel;
+
+ public static String GoogleTranslator_Error_CannotConnectToServer;
+
+ public static String GoogleTranslator_Error_HTTPRequestError;
+
+ public static String GoogleTranslator_Error_NoAvailableData;
+
+ public static String GoogleTranslator_Error_QueryTooBig;
+
+ public static String GoogleTranslator_Error_ToAndFromLanguagesAreEmpty;
+
+ public static String GoogleTranslator_Error_UnsupportedEncoding;
+
+ public static String GoogleTranslator_ErrorMessageExecutingRequest;
+
+ public static String GoogleTranslator_ErrorMessageNoValidTranslationReturned;
+
+ public static String GoogleTranslator_ErrorNoAPIkeySet;
+
+ public static String AndroidPreferencePage_googleApiKey_GroupLabel;
+
+ public static String AndroidPreferencePage_googleApiKey_Label;
+
+ public static String AndroidPreferencePage_googleApiKey_Note;
+
+ public static String AndroidPreferencePage_googleApiKey_Tooltip;
+
+}
diff --git a/src/plugins/translation/src/com/motorola/studio/android/localization/translators/i18n/translateNLS.properties b/src/plugins/translation/src/com/motorola/studio/android/localization/translators/i18n/translateNLS.properties
new file mode 100644
index 0000000..5eb661f
--- /dev/null
+++ b/src/plugins/translation/src/com/motorola/studio/android/localization/translators/i18n/translateNLS.properties
@@ -0,0 +1,15 @@
+GoogleTranslator_ChangeAPIkeyLabel=<a>Change your API key</a>
+GoogleTranslator_Error_CannotConnectToServer=Cannot connect to HTTP server:
+GoogleTranslator_Error_HTTPRequestError=Answer for the HTTP request was not OK. Error code: {0}. Error message: {1}.
+GoogleTranslator_Error_NoAvailableData=List of words to be translated cannot be empty
+GoogleTranslator_Error_QueryTooBig=Cannot execute query; query text is too long
+GoogleTranslator_Error_ToAndFromLanguagesAreEmpty='To' and 'From' languages cannot be empty
+GoogleTranslator_Error_UnsupportedEncoding=Unsupported encoding type:
+GoogleTranslator_ErrorMessageExecutingRequest=An error occurred trying to execute your translation request: {0}
+GoogleTranslator_ErrorMessageNoValidTranslationReturned=The translation service did not return a valid translation. The daily limit for accessing the Google Translate API may have been reached or your API key is wrong.\nService response message is: {0}
+GoogleTranslator_ErrorNoAPIkeySet=You must enter your own API key
+
+AndroidPreferencePage_googleApiKey_GroupLabel=Google Translate API
+AndroidPreferencePage_googleApiKey_Label=API Key:
+AndroidPreferencePage_googleApiKey_Note=Note: to obtain your own API key, go to the Google APIs Console at <a>{0}</a>
+AndroidPreferencePage_googleApiKey_Tooltip=Supply the API key to be used to access the Google Translate API \ No newline at end of file
diff --git a/src/plugins/translation/src/com/motorola/studio/android/localization/translators/preferences/ui/TranslationPreferencePage.java b/src/plugins/translation/src/com/motorola/studio/android/localization/translators/preferences/ui/TranslationPreferencePage.java
new file mode 100644
index 0000000..95904c0
--- /dev/null
+++ b/src/plugins/translation/src/com/motorola/studio/android/localization/translators/preferences/ui/TranslationPreferencePage.java
@@ -0,0 +1,196 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.motorola.studio.android.localization.translators.preferences.ui;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+
+import org.eclipse.core.runtime.preferences.IEclipsePreferences;
+import org.eclipse.core.runtime.preferences.InstanceScope;
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.jface.preference.PreferencePage;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.ModifyEvent;
+import org.eclipse.swt.events.ModifyListener;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Group;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Link;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.IWorkbenchPreferencePage;
+import org.eclipse.ui.PartInitException;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.browser.IWebBrowser;
+import org.eclipse.ui.browser.IWorkbenchBrowserSupport;
+import org.osgi.service.prefs.BackingStoreException;
+
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.localization.translators.GoogleTranslatorConstants;
+import com.motorola.studio.android.localization.translators.TranslationPlugin;
+import com.motorola.studio.android.localization.translators.i18n.TranslateNLS;
+
+public class TranslationPreferencePage extends PreferencePage implements IWorkbenchPreferencePage
+{
+ private final String GOOGLE_APIS_CONSOLE_LINK = "http://code.google.com/apis/console/"; //$NON-NLS-1$
+
+ private Text apiKeyText;
+
+ private String apiKeyValue;
+
+ public void init(IWorkbench workbench)
+ {
+ noDefaultAndApplyButton();
+ }
+
+ @Override
+ public boolean performOk()
+ {
+ InstanceScope scope = (InstanceScope) InstanceScope.INSTANCE;
+ IEclipsePreferences prefs = scope.getNode(TranslationPlugin.PLUGIN_ID);
+ if (apiKeyValue.trim().length() == 0)
+ {
+ prefs.remove(GoogleTranslatorConstants.API_KEY_VALUE_PREFERENCE);
+ }
+ else
+ {
+ prefs.put(GoogleTranslatorConstants.API_KEY_VALUE_PREFERENCE, apiKeyValue.trim());
+ }
+
+ try
+ {
+ prefs.flush();
+ }
+ catch (BackingStoreException e)
+ {
+ //do nothing
+ }
+
+ return super.performOk();
+ }
+
+ @Override
+ protected Control createContents(Composite parent)
+ {
+ Composite entryTable = new Composite(parent, SWT.NULL);
+ GridData data = new GridData(SWT.FILL, SWT.TOP, true, false);
+ entryTable.setLayoutData(data);
+
+ GridLayout layout = new GridLayout();
+ entryTable.setLayout(layout);
+
+ layout = new GridLayout(2, false);
+
+ layout = new GridLayout(2, false);
+ Group translatorAPIGroup = new Group(entryTable, SWT.NONE);
+ translatorAPIGroup.setLayout(layout);
+ translatorAPIGroup.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false));
+ translatorAPIGroup.setText(TranslateNLS.AndroidPreferencePage_googleApiKey_GroupLabel);
+
+ Link noteLabel = new Link(translatorAPIGroup, SWT.WRAP);
+ noteLabel.setText(TranslateNLS.bind(TranslateNLS.AndroidPreferencePage_googleApiKey_Note,
+ GOOGLE_APIS_CONSOLE_LINK));
+ data = new GridData(SWT.FILL, SWT.CENTER, true, false, 2, 1);
+ data.widthHint = 450;
+ noteLabel.setLayoutData(data);
+
+ noteLabel.addSelectionListener(new SelectionAdapter()
+ {
+ /* (non-Javadoc)
+ * @see org.eclipse.swt.events.SelectionAdapter#widgetSelected(org.eclipse.swt.events.SelectionEvent)
+ */
+ @Override
+ public void widgetSelected(SelectionEvent e)
+ {
+ IWorkbenchBrowserSupport browserSupport =
+ PlatformUI.getWorkbench().getBrowserSupport();
+
+ /*
+ * open the browser
+ */
+ IWebBrowser browser;
+ try
+ {
+ browser =
+ browserSupport.createBrowser(IWorkbenchBrowserSupport.LOCATION_BAR
+ | IWorkbenchBrowserSupport.NAVIGATION_BAR
+ | IWorkbenchBrowserSupport.AS_EXTERNAL, "MOTODEV", null, null); //$NON-NLS-1$
+
+ browser.openURL(new URL(GOOGLE_APIS_CONSOLE_LINK));
+ }
+ catch (PartInitException ex)
+ {
+ StudioLogger.error("Error opening the Google APIs Console link: " //$NON-NLS-1$
+ + ex.getMessage());
+ }
+ catch (MalformedURLException ex)
+ {
+ StudioLogger.error("Error opening the Google APIs Console link: " //$NON-NLS-1$
+ + ex.getMessage());
+ }
+ }
+ });
+
+ Label apiKeyLabel = new Label(translatorAPIGroup, SWT.NONE);
+ apiKeyLabel.setText(TranslateNLS.AndroidPreferencePage_googleApiKey_Label);
+ data = new GridData(SWT.LEFT, SWT.CENTER, false, false, 1, 1);
+ apiKeyLabel.setLayoutData(data);
+
+ apiKeyText = new Text(translatorAPIGroup, SWT.BORDER);
+ apiKeyValue = getApiKey();
+ apiKeyText.setText(apiKeyValue);
+ apiKeyText.setToolTipText(TranslateNLS.AndroidPreferencePage_googleApiKey_Tooltip);
+ data = new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1);
+ apiKeyText.setLayoutData(data);
+ apiKeyText.addModifyListener(new ModifyListener()
+ {
+ public void modifyText(ModifyEvent e)
+ {
+ apiKeyValue = apiKeyText.getText();
+
+ }
+ });
+
+ return entryTable;
+ }
+
+ /**
+ * get the apikey
+ * @return the apikey or an empty string for the default one
+ */
+ private static String getApiKey()
+ {
+ String apiKey = ""; //$NON-NLS-1$
+ IPreferenceStore prefStore = TranslationPlugin.getDefault().getPreferenceStore();
+ if (!prefStore.isDefault(GoogleTranslatorConstants.API_KEY_VALUE_PREFERENCE))
+ {
+ apiKey = prefStore.getString(GoogleTranslatorConstants.API_KEY_VALUE_PREFERENCE);
+ if (apiKey == null)
+ {
+ apiKey = ""; //$NON-NLS-1$
+ }
+ }
+
+ return apiKey;
+ }
+
+}
diff --git a/src/plugins/videos/.classpath b/src/plugins/videos/.classpath
new file mode 100644
index 0000000..9b3e81e
--- /dev/null
+++ b/src/plugins/videos/.classpath
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="lib" path="libs/gdata/java/deps/google-collect-1.0-rc1.jar"/>
+ <classpathentry kind="lib" path="libs/mail.jar"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.5"/>
+ <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry kind="lib" path="libs/gdata/java/deps/jsr305.jar"/>
+ <classpathentry kind="lib" path="libs/gdata/java/lib/gdata-client-1.0.jar"/>
+ <classpathentry kind="lib" path="libs/gdata/java/lib/gdata-client-meta-1.0.jar"/>
+ <classpathentry kind="lib" path="libs/gdata/java/lib/gdata-core-1.0.jar"/>
+ <classpathentry kind="lib" path="libs/gdata/java/lib/gdata-media-1.0.jar"/>
+ <classpathentry kind="lib" path="libs/gdata/java/lib/gdata-youtube-2.0.jar"/>
+ <classpathentry kind="lib" path="libs/gdata/java/lib/gdata-youtube-meta-2.0.jar"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/src/plugins/videos/.project b/src/plugins/videos/.project
new file mode 100644
index 0000000..1eadb2d
--- /dev/null
+++ b/src/plugins/videos/.project
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>com.motorola.studio.android.videos</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.ManifestBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.SchemaBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.pde.PluginNature</nature>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+</projectDescription>
diff --git a/src/plugins/videos/META-INF/MANIFEST.MF b/src/plugins/videos/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..e554978
--- /dev/null
+++ b/src/plugins/videos/META-INF/MANIFEST.MF
@@ -0,0 +1,26 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: %pluginName
+Bundle-SymbolicName: com.motorola.studio.android.videos; singleton:=true
+Bundle-Version: 5.0.0.qualifier
+Bundle-Activator: com.motorola.studio.android.videos.Activator
+Bundle-Vendor: %providerName
+Require-Bundle: org.eclipse.ui,
+ org.eclipse.core.runtime,
+ org.eclipse.core.net,
+ com.motorola.studio.android,
+ org.eclipse.ui.net,
+ com.motorola.studio.android.common
+Bundle-RequiredExecutionEnvironment: JavaSE-1.6
+Bundle-Localization: plugin
+Bundle-ActivationPolicy: lazy
+Bundle-ClassPath: libs/gdata/java/deps/google-collect-1.0-rc1.jar,
+ libs/gdata/java/deps/jsr305.jar,
+ .,
+ libs/gdata/java/lib/gdata-client-1.0.jar,
+ libs/gdata/java/lib/gdata-client-meta-1.0.jar,
+ libs/gdata/java/lib/gdata-core-1.0.jar,
+ libs/gdata/java/lib/gdata-media-1.0.jar,
+ libs/gdata/java/lib/gdata-youtube-2.0.jar,
+ libs/gdata/java/lib/gdata-youtube-meta-2.0.jar,
+ libs/mail.jar
diff --git a/src/plugins/videos/build.properties b/src/plugins/videos/build.properties
new file mode 100644
index 0000000..de829a0
--- /dev/null
+++ b/src/plugins/videos/build.properties
@@ -0,0 +1,20 @@
+source.. = src/
+output.. = bin/
+bin.includes = plugin.xml,\
+ META-INF/,\
+ .,\
+ icons/,\
+ contexts.xml,\
+ resources/,\
+ libs/,\
+ libs/gdata/java/deps/google-collect-1.0-rc1.jar,\
+ libs/gdata/java/deps/jsr305.jar,\
+ libs/gdata/java/lib/gdata-client-1.0.jar,\
+ libs/gdata/java/lib/gdata-client-meta-1.0.jar,\
+ libs/gdata/java/lib/gdata-core-1.0.jar,\
+ libs/gdata/java/lib/gdata-media-1.0.jar,\
+ libs/gdata/java/lib/gdata-youtube-2.0.jar,\
+ libs/gdata/java/lib/gdata-youtube-meta-2.0.jar,\
+ plugin.properties,\
+ libs/mail.jar
+jars.compile.order = .
diff --git a/src/plugins/videos/contexts.xml b/src/plugins/videos/contexts.xml
new file mode 100644
index 0000000..02e26e4
--- /dev/null
+++ b/src/plugins/videos/contexts.xml
@@ -0,0 +1,12 @@
+<contexts>
+ <context id="viewer" title="Sample View">
+ <description>This is the context help for the sample view with a table viewer. It was generated by a PDE template.</description>
+ <topic href="/PLUGINS_ROOT/org.eclipse.platform.doc.isv/guide/ua_help_context.htm" label="Context-sensitive help">
+ <enablement>
+ <with variable="platform">
+ <test property="org.eclipse.core.runtime.isBundleInstalled" args="org.eclipse.platform.doc.isv"/>
+ </with>
+ </enablement>
+ </topic>
+ </context>
+</contexts>
diff --git a/src/plugins/videos/icons/error_icon.png b/src/plugins/videos/icons/error_icon.png
new file mode 100644
index 0000000..7517adf
--- /dev/null
+++ b/src/plugins/videos/icons/error_icon.png
Binary files differ
diff --git a/src/plugins/videos/icons/loading_icon.gif b/src/plugins/videos/icons/loading_icon.gif
new file mode 100644
index 0000000..802a3eb
--- /dev/null
+++ b/src/plugins/videos/icons/loading_icon.gif
Binary files differ
diff --git a/src/plugins/videos/icons/play_icon.png b/src/plugins/videos/icons/play_icon.png
new file mode 100644
index 0000000..d499599
--- /dev/null
+++ b/src/plugins/videos/icons/play_icon.png
Binary files differ
diff --git a/src/plugins/videos/icons/preview_not_available.png b/src/plugins/videos/icons/preview_not_available.png
new file mode 100644
index 0000000..f208e7c
--- /dev/null
+++ b/src/plugins/videos/icons/preview_not_available.png
Binary files differ
diff --git a/src/plugins/videos/icons/thumbnail_loading.png b/src/plugins/videos/icons/thumbnail_loading.png
new file mode 100644
index 0000000..ba22b89
--- /dev/null
+++ b/src/plugins/videos/icons/thumbnail_loading.png
Binary files differ
diff --git a/src/plugins/videos/icons/video_view16.png b/src/plugins/videos/icons/video_view16.png
new file mode 100644
index 0000000..b20c55f
--- /dev/null
+++ b/src/plugins/videos/icons/video_view16.png
Binary files differ
diff --git a/src/plugins/videos/libs/gdata/java/deps/google-collect-1.0-rc1.jar b/src/plugins/videos/libs/gdata/java/deps/google-collect-1.0-rc1.jar
new file mode 100644
index 0000000..bc499ee
--- /dev/null
+++ b/src/plugins/videos/libs/gdata/java/deps/google-collect-1.0-rc1.jar
Binary files differ
diff --git a/src/plugins/videos/libs/gdata/java/deps/jsr305.jar b/src/plugins/videos/libs/gdata/java/deps/jsr305.jar
new file mode 100644
index 0000000..cf5f561
--- /dev/null
+++ b/src/plugins/videos/libs/gdata/java/deps/jsr305.jar
Binary files differ
diff --git a/src/plugins/videos/libs/gdata/java/lib/gdata-client-1.0.jar b/src/plugins/videos/libs/gdata/java/lib/gdata-client-1.0.jar
new file mode 100644
index 0000000..4ce15f5
--- /dev/null
+++ b/src/plugins/videos/libs/gdata/java/lib/gdata-client-1.0.jar
Binary files differ
diff --git a/src/plugins/videos/libs/gdata/java/lib/gdata-client-meta-1.0.jar b/src/plugins/videos/libs/gdata/java/lib/gdata-client-meta-1.0.jar
new file mode 100644
index 0000000..2583121
--- /dev/null
+++ b/src/plugins/videos/libs/gdata/java/lib/gdata-client-meta-1.0.jar
Binary files differ
diff --git a/src/plugins/videos/libs/gdata/java/lib/gdata-core-1.0.jar b/src/plugins/videos/libs/gdata/java/lib/gdata-core-1.0.jar
new file mode 100644
index 0000000..156df91
--- /dev/null
+++ b/src/plugins/videos/libs/gdata/java/lib/gdata-core-1.0.jar
Binary files differ
diff --git a/src/plugins/videos/libs/gdata/java/lib/gdata-media-1.0.jar b/src/plugins/videos/libs/gdata/java/lib/gdata-media-1.0.jar
new file mode 100644
index 0000000..3628102
--- /dev/null
+++ b/src/plugins/videos/libs/gdata/java/lib/gdata-media-1.0.jar
Binary files differ
diff --git a/src/plugins/videos/libs/gdata/java/lib/gdata-youtube-2.0.jar b/src/plugins/videos/libs/gdata/java/lib/gdata-youtube-2.0.jar
new file mode 100644
index 0000000..e8bd4e2
--- /dev/null
+++ b/src/plugins/videos/libs/gdata/java/lib/gdata-youtube-2.0.jar
Binary files differ
diff --git a/src/plugins/videos/libs/gdata/java/lib/gdata-youtube-meta-2.0.jar b/src/plugins/videos/libs/gdata/java/lib/gdata-youtube-meta-2.0.jar
new file mode 100644
index 0000000..ccaa7e9
--- /dev/null
+++ b/src/plugins/videos/libs/gdata/java/lib/gdata-youtube-meta-2.0.jar
Binary files differ
diff --git a/src/plugins/videos/libs/mail.jar b/src/plugins/videos/libs/mail.jar
new file mode 100644
index 0000000..9d60d13
--- /dev/null
+++ b/src/plugins/videos/libs/mail.jar
Binary files differ
diff --git a/src/plugins/videos/plugin.properties b/src/plugins/videos/plugin.properties
new file mode 100644
index 0000000..e00dee3
--- /dev/null
+++ b/src/plugins/videos/plugin.properties
@@ -0,0 +1,12 @@
+#################################################################################
+#
+# Android Emulator Core Plugin properties
+#
+#################################################################################
+
+pluginName=MOTODEV Studio for Android Video Tutorials Plug-in
+providerName=Motorola Mobility, Inc.
+copyright=Copyright (C) 2012 The Android Open Source Project
+
+# View related labels
+motodevVideosViewName=MOTODEV Video Tutorials
diff --git a/src/plugins/videos/plugin.xml b/src/plugins/videos/plugin.xml
new file mode 100644
index 0000000..760d05e
--- /dev/null
+++ b/src/plugins/videos/plugin.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?eclipse version="3.4"?>
+<plugin>
+
+ <extension
+ point="org.eclipse.ui.views">
+ <view
+ name="%motodevVideosViewName"
+ icon="icons/video_view16.png"
+ category="studioAndroidViewCategory"
+ class="com.motorola.studio.android.videos.ui.views.MOTODEVVideosView"
+ id="com.motorola.studio.android.videos.views.MOTODEVVideosView">
+ </view>
+ </extension>
+ <extension
+ point="org.eclipse.help.contexts">
+ <contexts
+ file="contexts.xml">
+ </contexts>
+ </extension>
+
+</plugin>
diff --git a/src/plugins/videos/resources/motodev_videos.xml b/src/plugins/videos/resources/motodev_videos.xml
new file mode 100644
index 0000000..4688315
--- /dev/null
+++ b/src/plugins/videos/resources/motodev_videos.xml
@@ -0,0 +1,14 @@
+<videos>
+
+ <user name="motorolamotodev" password=""/>
+
+ <channels>
+ <!--
+ <channel display_name="" name="" order="{fixed, variable}" default="{true, false}"/>
+ -->
+ <channel display_name="Getting Started" name="Getting Started" order="fixed" default="true"/>
+ <channel display_name="MOTODEV Studio Features" name="MOTODEV Studio Features"/>
+ <channel display_name="Programming Android" name="Programming Android"/>
+ </channels>
+
+</videos> \ No newline at end of file
diff --git a/src/plugins/videos/resources/swfobject.js b/src/plugins/videos/resources/swfobject.js
new file mode 100644
index 0000000..8eafe9d
--- /dev/null
+++ b/src/plugins/videos/resources/swfobject.js
@@ -0,0 +1,4 @@
+/* SWFObject v2.2 <http://code.google.com/p/swfobject/>
+ is released under the MIT License <http://www.opensource.org/licenses/mit-license.php>
+*/
+var swfobject=function(){var D="undefined",r="object",S="Shockwave Flash",W="ShockwaveFlash.ShockwaveFlash",q="application/x-shockwave-flash",R="SWFObjectExprInst",x="onreadystatechange",O=window,j=document,t=navigator,T=false,U=[h],o=[],N=[],I=[],l,Q,E,B,J=false,a=false,n,G,m=true,M=function(){var aa=typeof j.getElementById!=D&&typeof j.getElementsByTagName!=D&&typeof j.createElement!=D,ah=t.userAgent.toLowerCase(),Y=t.platform.toLowerCase(),ae=Y?/win/.test(Y):/win/.test(ah),ac=Y?/mac/.test(Y):/mac/.test(ah),af=/webkit/.test(ah)?parseFloat(ah.replace(/^.*webkit\/(\d+(\.\d+)?).*$/,"$1")):false,X=!+"\v1",ag=[0,0,0],ab=null;if(typeof t.plugins!=D&&typeof t.plugins[S]==r){ab=t.plugins[S].description;if(ab&&!(typeof t.mimeTypes!=D&&t.mimeTypes[q]&&!t.mimeTypes[q].enabledPlugin)){T=true;X=false;ab=ab.replace(/^.*\s+(\S+\s+\S+$)/,"$1");ag[0]=parseInt(ab.replace(/^(.*)\..*$/,"$1"),10);ag[1]=parseInt(ab.replace(/^.*\.(.*)\s.*$/,"$1"),10);ag[2]=/[a-zA-Z]/.test(ab)?parseInt(ab.replace(/^.*[a-zA-Z]+(.*)$/,"$1"),10):0}}else{if(typeof O.ActiveXObject!=D){try{var ad=new ActiveXObject(W);if(ad){ab=ad.GetVariable("$version");if(ab){X=true;ab=ab.split(" ")[1].split(",");ag=[parseInt(ab[0],10),parseInt(ab[1],10),parseInt(ab[2],10)]}}}catch(Z){}}}return{w3:aa,pv:ag,wk:af,ie:X,win:ae,mac:ac}}(),k=function(){if(!M.w3){return}if((typeof j.readyState!=D&&j.readyState=="complete")||(typeof j.readyState==D&&(j.getElementsByTagName("body")[0]||j.body))){f()}if(!J){if(typeof j.addEventListener!=D){j.addEventListener("DOMContentLoaded",f,false)}if(M.ie&&M.win){j.attachEvent(x,function(){if(j.readyState=="complete"){j.detachEvent(x,arguments.callee);f()}});if(O==top){(function(){if(J){return}try{j.documentElement.doScroll("left")}catch(X){setTimeout(arguments.callee,0);return}f()})()}}if(M.wk){(function(){if(J){return}if(!/loaded|complete/.test(j.readyState)){setTimeout(arguments.callee,0);return}f()})()}s(f)}}();function f(){if(J){return}try{var Z=j.getElementsByTagName("body")[0].appendChild(C("span"));Z.parentNode.removeChild(Z)}catch(aa){return}J=true;var X=U.length;for(var Y=0;Y<X;Y++){U[Y]()}}function K(X){if(J){X()}else{U[U.length]=X}}function s(Y){if(typeof O.addEventListener!=D){O.addEventListener("load",Y,false)}else{if(typeof j.addEventListener!=D){j.addEventListener("load",Y,false)}else{if(typeof O.attachEvent!=D){i(O,"onload",Y)}else{if(typeof O.onload=="function"){var X=O.onload;O.onload=function(){X();Y()}}else{O.onload=Y}}}}}function h(){if(T){V()}else{H()}}function V(){var X=j.getElementsByTagName("body")[0];var aa=C(r);aa.setAttribute("type",q);var Z=X.appendChild(aa);if(Z){var Y=0;(function(){if(typeof Z.GetVariable!=D){var ab=Z.GetVariable("$version");if(ab){ab=ab.split(" ")[1].split(",");M.pv=[parseInt(ab[0],10),parseInt(ab[1],10),parseInt(ab[2],10)]}}else{if(Y<10){Y++;setTimeout(arguments.callee,10);return}}X.removeChild(aa);Z=null;H()})()}else{H()}}function H(){var ag=o.length;if(ag>0){for(var af=0;af<ag;af++){var Y=o[af].id;var ab=o[af].callbackFn;var aa={success:false,id:Y};if(M.pv[0]>0){var ae=c(Y);if(ae){if(F(o[af].swfVersion)&&!(M.wk&&M.wk<312)){w(Y,true);if(ab){aa.success=true;aa.ref=z(Y);ab(aa)}}else{if(o[af].expressInstall&&A()){var ai={};ai.data=o[af].expressInstall;ai.width=ae.getAttribute("width")||"0";ai.height=ae.getAttribute("height")||"0";if(ae.getAttribute("class")){ai.styleclass=ae.getAttribute("class")}if(ae.getAttribute("align")){ai.align=ae.getAttribute("align")}var ah={};var X=ae.getElementsByTagName("param");var ac=X.length;for(var ad=0;ad<ac;ad++){if(X[ad].getAttribute("name").toLowerCase()!="movie"){ah[X[ad].getAttribute("name")]=X[ad].getAttribute("value")}}P(ai,ah,Y,ab)}else{p(ae);if(ab){ab(aa)}}}}}else{w(Y,true);if(ab){var Z=z(Y);if(Z&&typeof Z.SetVariable!=D){aa.success=true;aa.ref=Z}ab(aa)}}}}}function z(aa){var X=null;var Y=c(aa);if(Y&&Y.nodeName=="OBJECT"){if(typeof Y.SetVariable!=D){X=Y}else{var Z=Y.getElementsByTagName(r)[0];if(Z){X=Z}}}return X}function A(){return !a&&F("6.0.65")&&(M.win||M.mac)&&!(M.wk&&M.wk<312)}function P(aa,ab,X,Z){a=true;E=Z||null;B={success:false,id:X};var ae=c(X);if(ae){if(ae.nodeName=="OBJECT"){l=g(ae);Q=null}else{l=ae;Q=X}aa.id=R;if(typeof aa.width==D||(!/%$/.test(aa.width)&&parseInt(aa.width,10)<310)){aa.width="310"}if(typeof aa.height==D||(!/%$/.test(aa.height)&&parseInt(aa.height,10)<137)){aa.height="137"}j.title=j.title.slice(0,47)+" - Flash Player Installation";var ad=M.ie&&M.win?"ActiveX":"PlugIn",ac="MMredirectURL="+O.location.toString().replace(/&/g,"%26")+"&MMplayerType="+ad+"&MMdoctitle="+j.title;if(typeof ab.flashvars!=D){ab.flashvars+="&"+ac}else{ab.flashvars=ac}if(M.ie&&M.win&&ae.readyState!=4){var Y=C("div");X+="SWFObjectNew";Y.setAttribute("id",X);ae.parentNode.insertBefore(Y,ae);ae.style.display="none";(function(){if(ae.readyState==4){ae.parentNode.removeChild(ae)}else{setTimeout(arguments.callee,10)}})()}u(aa,ab,X)}}function p(Y){if(M.ie&&M.win&&Y.readyState!=4){var X=C("div");Y.parentNode.insertBefore(X,Y);X.parentNode.replaceChild(g(Y),X);Y.style.display="none";(function(){if(Y.readyState==4){Y.parentNode.removeChild(Y)}else{setTimeout(arguments.callee,10)}})()}else{Y.parentNode.replaceChild(g(Y),Y)}}function g(ab){var aa=C("div");if(M.win&&M.ie){aa.innerHTML=ab.innerHTML}else{var Y=ab.getElementsByTagName(r)[0];if(Y){var ad=Y.childNodes;if(ad){var X=ad.length;for(var Z=0;Z<X;Z++){if(!(ad[Z].nodeType==1&&ad[Z].nodeName=="PARAM")&&!(ad[Z].nodeType==8)){aa.appendChild(ad[Z].cloneNode(true))}}}}}return aa}function u(ai,ag,Y){var X,aa=c(Y);if(M.wk&&M.wk<312){return X}if(aa){if(typeof ai.id==D){ai.id=Y}if(M.ie&&M.win){var ah="";for(var ae in ai){if(ai[ae]!=Object.prototype[ae]){if(ae.toLowerCase()=="data"){ag.movie=ai[ae]}else{if(ae.toLowerCase()=="styleclass"){ah+=' class="'+ai[ae]+'"'}else{if(ae.toLowerCase()!="classid"){ah+=" "+ae+'="'+ai[ae]+'"'}}}}}var af="";for(var ad in ag){if(ag[ad]!=Object.prototype[ad]){af+='<param name="'+ad+'" value="'+ag[ad]+'" />'}}aa.outerHTML='<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"'+ah+">"+af+"</object>";N[N.length]=ai.id;X=c(ai.id)}else{var Z=C(r);Z.setAttribute("type",q);for(var ac in ai){if(ai[ac]!=Object.prototype[ac]){if(ac.toLowerCase()=="styleclass"){Z.setAttribute("class",ai[ac])}else{if(ac.toLowerCase()!="classid"){Z.setAttribute(ac,ai[ac])}}}}for(var ab in ag){if(ag[ab]!=Object.prototype[ab]&&ab.toLowerCase()!="movie"){e(Z,ab,ag[ab])}}aa.parentNode.replaceChild(Z,aa);X=Z}}return X}function e(Z,X,Y){var aa=C("param");aa.setAttribute("name",X);aa.setAttribute("value",Y);Z.appendChild(aa)}function y(Y){var X=c(Y);if(X&&X.nodeName=="OBJECT"){if(M.ie&&M.win){X.style.display="none";(function(){if(X.readyState==4){b(Y)}else{setTimeout(arguments.callee,10)}})()}else{X.parentNode.removeChild(X)}}}function b(Z){var Y=c(Z);if(Y){for(var X in Y){if(typeof Y[X]=="function"){Y[X]=null}}Y.parentNode.removeChild(Y)}}function c(Z){var X=null;try{X=j.getElementById(Z)}catch(Y){}return X}function C(X){return j.createElement(X)}function i(Z,X,Y){Z.attachEvent(X,Y);I[I.length]=[Z,X,Y]}function F(Z){var Y=M.pv,X=Z.split(".");X[0]=parseInt(X[0],10);X[1]=parseInt(X[1],10)||0;X[2]=parseInt(X[2],10)||0;return(Y[0]>X[0]||(Y[0]==X[0]&&Y[1]>X[1])||(Y[0]==X[0]&&Y[1]==X[1]&&Y[2]>=X[2]))?true:false}function v(ac,Y,ad,ab){if(M.ie&&M.mac){return}var aa=j.getElementsByTagName("head")[0];if(!aa){return}var X=(ad&&typeof ad=="string")?ad:"screen";if(ab){n=null;G=null}if(!n||G!=X){var Z=C("style");Z.setAttribute("type","text/css");Z.setAttribute("media",X);n=aa.appendChild(Z);if(M.ie&&M.win&&typeof j.styleSheets!=D&&j.styleSheets.length>0){n=j.styleSheets[j.styleSheets.length-1]}G=X}if(M.ie&&M.win){if(n&&typeof n.addRule==r){n.addRule(ac,Y)}}else{if(n&&typeof j.createTextNode!=D){n.appendChild(j.createTextNode(ac+" {"+Y+"}"))}}}function w(Z,X){if(!m){return}var Y=X?"visible":"hidden";if(J&&c(Z)){c(Z).style.visibility=Y}else{v("#"+Z,"visibility:"+Y)}}function L(Y){var Z=/[\\\"<>\.;]/;var X=Z.exec(Y)!=null;return X&&typeof encodeURIComponent!=D?encodeURIComponent(Y):Y}var d=function(){if(M.ie&&M.win){window.attachEvent("onunload",function(){var ac=I.length;for(var ab=0;ab<ac;ab++){I[ab][0].detachEvent(I[ab][1],I[ab][2])}var Z=N.length;for(var aa=0;aa<Z;aa++){y(N[aa])}for(var Y in M){M[Y]=null}M=null;for(var X in swfobject){swfobject[X]=null}swfobject=null})}}();return{registerObject:function(ab,X,aa,Z){if(M.w3&&ab&&X){var Y={};Y.id=ab;Y.swfVersion=X;Y.expressInstall=aa;Y.callbackFn=Z;o[o.length]=Y;w(ab,false)}else{if(Z){Z({success:false,id:ab})}}},getObjectById:function(X){if(M.w3){return z(X)}},embedSWF:function(ab,ah,ae,ag,Y,aa,Z,ad,af,ac){var X={success:false,id:ah};if(M.w3&&!(M.wk&&M.wk<312)&&ab&&ah&&ae&&ag&&Y){w(ah,false);K(function(){ae+="";ag+="";var aj={};if(af&&typeof af===r){for(var al in af){aj[al]=af[al]}}aj.data=ab;aj.width=ae;aj.height=ag;var am={};if(ad&&typeof ad===r){for(var ak in ad){am[ak]=ad[ak]}}if(Z&&typeof Z===r){for(var ai in Z){if(typeof am.flashvars!=D){am.flashvars+="&"+ai+"="+Z[ai]}else{am.flashvars=ai+"="+Z[ai]}}}if(F(Y)){var an=u(aj,am,ah);if(aj.id==ah){w(ah,true)}X.success=true;X.ref=an}else{if(aa&&A()){aj.data=aa;P(aj,am,ah,ac);return}else{w(ah,true)}}if(ac){ac(X)}})}else{if(ac){ac(X)}}},switchOffAutoHideShow:function(){m=false},ua:M,getFlashPlayerVersion:function(){return{major:M.pv[0],minor:M.pv[1],release:M.pv[2]}},hasFlashPlayerVersion:F,createSWF:function(Z,Y,X){if(M.w3){return u(Z,Y,X)}else{return undefined}},showExpressInstall:function(Z,aa,X,Y){if(M.w3&&A()){P(Z,aa,X,Y)}},removeSWF:function(X){if(M.w3){y(X)}},createCSS:function(aa,Z,Y,X){if(M.w3){v(aa,Z,Y,X)}},addDomLoadEvent:K,addLoadEvent:s,getQueryParamValue:function(aa){var Z=j.location.search||j.location.hash;if(Z){if(/\?/.test(Z)){Z=Z.split("?")[1]}if(aa==null){return L(Z)}var Y=Z.split("&");for(var X=0;X<Y.length;X++){if(Y[X].substring(0,Y[X].indexOf("="))==aa){return L(Y[X].substring((Y[X].indexOf("=")+1)))}}}return""},expressInstallCallback:function(){if(a){var X=c(R);if(X&&l){X.parentNode.replaceChild(l,X);if(Q){w(Q,true);if(M.ie&&M.win){l.style.display="block"}}if(E){E(B)}}a=false}}}}(); \ No newline at end of file
diff --git a/src/plugins/videos/resources/watch_video.html b/src/plugins/videos/resources/watch_video.html
new file mode 100644
index 0000000..a0dc565
--- /dev/null
+++ b/src/plugins/videos/resources/watch_video.html
@@ -0,0 +1,31 @@
+<html>
+<head>
+
+ <script type="text/javascript" src="@@JAVASCRIPT_SRC@@"></script>
+
+ <script type="text/javascript">
+
+ function setVideo(url){
+ var videoframe = document.getElementById('video-frame');
+ videoframe.src = url;
+ }
+
+ function adjustSize(x, y){
+ var videoframe = document.getElementById('video-frame');
+ videoframe.width = x;
+ videoframe.height = y;
+ }
+
+ hasFlashPlayer(swfobject.hasFlashPlayerVersion('1'));
+
+ </script>
+
+</head>
+
+<body style="margin:0; overflow:hidden; background-color:#000000;">
+
+ <iframe style="filter:alpha(Opacity=0);-moz-opacity(0);" id="video-frame" src="" frameborder="0" allowfullscreen></iframe>
+
+</body>
+
+</html> \ No newline at end of file
diff --git a/src/plugins/videos/src/com/motorola/studio/android/videos/Activator.java b/src/plugins/videos/src/com/motorola/studio/android/videos/Activator.java
new file mode 100644
index 0000000..1b4da19
--- /dev/null
+++ b/src/plugins/videos/src/com/motorola/studio/android/videos/Activator.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.videos;
+
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.ui.plugin.AbstractUIPlugin;
+import org.osgi.framework.BundleContext;
+
+import com.motorola.studio.android.common.log.StudioLogger;
+
+/**
+ * The activator class controls the plug-in life cycle
+ */
+public class Activator extends AbstractUIPlugin
+{
+
+ // The plug-in ID
+ public static final String PLUGIN_ID = "com.motorola.studio.android.videos"; //$NON-NLS-1$
+
+ // The shared instance
+ private static Activator plugin;
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.ui.plugin.AbstractUIPlugin#start(org.osgi.framework.BundleContext)
+ */
+ @Override
+ public void start(BundleContext context) throws Exception
+ {
+ StudioLogger.debug(Activator.class, "Starting MOTODEV Studio for Android Videos Plugin...");
+
+ super.start(context);
+ plugin = this;
+
+ StudioLogger.debug(Activator.class, "MOTODEV Studio for Android Videos Plugin started.");
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.ui.plugin.AbstractUIPlugin#stop(org.osgi.framework.BundleContext)
+ */
+ @Override
+ public void stop(BundleContext context) throws Exception
+ {
+ plugin = null;
+ super.stop(context);
+ }
+
+ /**
+ * Returns the shared instance
+ *
+ * @return the shared instance
+ */
+ public static Activator getDefault()
+ {
+ return plugin;
+ }
+
+ /**
+ * Returns an image descriptor for the image file at the given
+ * plug-in relative path
+ *
+ * @param path the path
+ * @return the image descriptor
+ */
+ public static ImageDescriptor getImageDescriptor(String path)
+ {
+ return imageDescriptorFromPlugin(PLUGIN_ID, path);
+ }
+}
diff --git a/src/plugins/videos/src/com/motorola/studio/android/videos/i18n/VideosNLS.java b/src/plugins/videos/src/com/motorola/studio/android/videos/i18n/VideosNLS.java
new file mode 100644
index 0000000..610ecf8
--- /dev/null
+++ b/src/plugins/videos/src/com/motorola/studio/android/videos/i18n/VideosNLS.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.motorola.studio.android.videos.i18n;
+
+import org.eclipse.osgi.util.NLS;
+
+/**
+ * Class that contains the localized messages
+ */
+public class VideosNLS extends NLS
+{
+ static
+ {
+ NLS.initializeMessages("com.motorola.studio.android.videos.i18n.videosNLS", VideosNLS.class);
+ }
+
+ public static String UI_Order_Most_Recent;
+
+ public static String UI_Order_Most_Viewed;
+
+ public static String UI_Order_Top_Rated;
+
+ public static String UI_MOTODEV_Video;
+
+ public static String UI_Play_Video;
+
+ public static String UI_Open_External_Browser;
+
+ public static String UI_Search;
+
+ public static String UI_Job_Refresh_View;
+
+ public static String UI_Loading;
+
+ public static String UI_ErrorMsg;
+
+ public static String UI_No_Flash_Player;
+
+ public static String UI_No_Flash_Player_32bits_Extension;
+
+ public static String UI_No_Flash_Player_64bits_Extension;
+
+ public static String UI_Flash_Player_Link_32bits;
+
+ public static String UI_Flash_Player_Link_64bits;
+
+ public static String UI_Reload;
+
+ public static String UI_No_Videos_Search;
+
+ public static String UI_Copy_URL;
+
+ public static String UI_Copy_Embedded_URL;
+
+ public static String UI_More;
+
+ public static String UI_Less;
+
+}
diff --git a/src/plugins/videos/src/com/motorola/studio/android/videos/i18n/videosNLS.properties b/src/plugins/videos/src/com/motorola/studio/android/videos/i18n/videosNLS.properties
new file mode 100644
index 0000000..6db8001
--- /dev/null
+++ b/src/plugins/videos/src/com/motorola/studio/android/videos/i18n/videosNLS.properties
@@ -0,0 +1,21 @@
+UI_Order_Most_Recent=Most Recent
+UI_Order_Most_Viewed=Most Viewed
+UI_Order_Top_Rated=Top Rated
+UI_MOTODEV_Video=MOTODEV Video:
+UI_Play_Video=Play video
+UI_Open_External_Browser=Open in external browser
+UI_Search=Search...
+UI_Job_Refresh_View=Refreshing Videos view
+UI_Loading=Loading...
+UI_ErrorMsg=No Internet connection \nCheck your <a>Network Settings</a> \nReason:
+UI_No_Flash_Player=You do not have Adobe Flash Player installed, or the installed version does not match your operating system's architecture
+UI_No_Flash_Player_32bits_Extension=(32 bits).\nDownload Adobe Flash Player from {0}.
+UI_No_Flash_Player_64bits_Extension=(64 bits).\nDownload Adobe Flash Player 11 Beta for Desktops from {0}.
+UI_Flash_Player_Link_32bits=http://get.adobe.com/flashplayer/
+UI_Flash_Player_Link_64bits=http://labs.adobe.com/downloads/flashplayer11.html
+UI_Reload=Reload
+UI_No_Videos_Search=No videos found in this channel
+UI_Copy_URL=Copy video URL
+UI_Copy_Embedded_URL=Copy embedded video URL
+UI_More=More
+UI_Less=Less
diff --git a/src/plugins/videos/src/com/motorola/studio/android/videos/implementation/youtube/YoutubeVideoServiceProvider.java b/src/plugins/videos/src/com/motorola/studio/android/videos/implementation/youtube/YoutubeVideoServiceProvider.java
new file mode 100644
index 0000000..5bd4889
--- /dev/null
+++ b/src/plugins/videos/src/com/motorola/studio/android/videos/implementation/youtube/YoutubeVideoServiceProvider.java
@@ -0,0 +1,181 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.videos.implementation.youtube;
+
+import java.net.Authenticator;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.core.internal.net.ProxyManager;
+import org.eclipse.core.net.proxy.IProxyService;
+
+import com.google.gdata.client.youtube.YouTubeService;
+import com.google.gdata.data.extensions.Rating;
+import com.google.gdata.data.youtube.PlaylistEntry;
+import com.google.gdata.data.youtube.PlaylistFeed;
+import com.google.gdata.data.youtube.PlaylistLinkEntry;
+import com.google.gdata.data.youtube.PlaylistLinkFeed;
+import com.google.gdata.data.youtube.VideoEntry;
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.common.proxy.ProxyAuthenticator;
+import com.motorola.studio.android.videos.model.Video;
+import com.motorola.studio.android.videos.model.VideoChannel;
+import com.motorola.studio.android.videos.model.extension.VideoServiceProvider;
+
+/**
+ * Implements the methods to retrieve videos from YouTube
+ */
+public class YoutubeVideoServiceProvider implements VideoServiceProvider
+{
+
+ private String youtubeUser = null;
+
+ /*
+ * YouTube Service
+ */
+ private YouTubeService youtubeService = null;
+
+ private final String YOUTUBE_SERVICE_APP_ID = "motodev-studio-for-android";
+
+ /*
+ * YouTube Service URIs
+ */
+ public static final String YOUTUBE_GDATA_SERVER = "http://gdata.youtube.com";
+
+ // change <user> by the real YouTube username
+ private final String ALL_PLAYLISTS_FEED = YOUTUBE_GDATA_SERVER
+ + "/feeds/api/users/<user>/playlists";
+
+ /*
+ * Map "playlist name" -> object from Youtube API that represent the playlist
+ */
+ private final Map<String, PlaylistLinkEntry> allPlaylistsMap =
+ new HashMap<String, PlaylistLinkEntry>();
+
+ /**
+ * Initialize the YouTube Service
+ */
+ public YoutubeVideoServiceProvider(String user) throws Exception
+ {
+ // Try to retrieve proxy configuration to use if necessary
+ IProxyService proxyService = ProxyManager.getProxyManager();
+ if (proxyService.isProxiesEnabled() || proxyService.isSystemProxiesEnabled())
+ {
+ Authenticator.setDefault(new ProxyAuthenticator());
+ }
+
+ youtubeUser = user;
+ youtubeService = new YouTubeService(YOUTUBE_SERVICE_APP_ID);
+ PlaylistLinkFeed feeds =
+ youtubeService.getFeed(new URL(ALL_PLAYLISTS_FEED.replace("<user>", youtubeUser)),
+ PlaylistLinkFeed.class);
+ for (PlaylistLinkEntry feedEntry : feeds.getEntries())
+ {
+ allPlaylistsMap.put(feedEntry.getTitle().getPlainText(), feedEntry);
+ }
+
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.videos.model.extension.VideoServiceProvider#loadVideos(java.util.List)
+ */
+ public List<Video> loadVideos(VideoChannel channel) throws Exception
+ {
+
+ List<Video> videos = null;
+
+ PlaylistLinkEntry entry = allPlaylistsMap.get(channel.getName());
+
+ if (entry != null)
+ {
+ // Get videos in the playlist
+ String playlistUrl = entry.getFeedUrl();
+ PlaylistFeed playlistFeed =
+ youtubeService.getFeed(new URL(playlistUrl), PlaylistFeed.class);
+
+ videos = new ArrayList<Video>();
+ for (PlaylistEntry playlistEntry : playlistFeed.getEntries())
+ {
+ videos.add(getVideoInstance(playlistEntry));
+ }
+ }
+
+ return videos;
+
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.videos.model.VideoManager#rate(com.motorola.studio.android.videos.model.Video, int)
+ */
+ public void rate(Video video, int rate) throws Exception
+ {
+
+ VideoEntry youtubeVideo = (VideoEntry) video.getData();
+ String ratingUrl = youtubeVideo.getRatingLink().getHref();
+ Rating myRating = new Rating();
+ myRating.setValue(rate);
+ myRating.setMax(5);
+ myRating.setMin(1);
+ youtubeVideo.setRating(myRating);
+
+ youtubeService.insert(new URL(ratingUrl), youtubeVideo);
+
+ }
+
+ /**
+ * Create and populate an object of Video type, with
+ * the information from the VideoEntry passed as argument
+ *
+ * @param videoEntry the VideoEntry object to be converted
+ * @return the corresponding Video instance
+ */
+ private Video getVideoInstance(VideoEntry videoEntry)
+ {
+ Video video = new Video();
+
+ video.setId(videoEntry.getId());
+ video.setTitle(videoEntry.getTitle().getPlainText());
+ video.setDescription(videoEntry.getMediaGroup().getDescription() != null ? videoEntry
+ .getMediaGroup().getDescription().getPlainTextContent() : "");
+ video.setEmbeddedLink(videoEntry.getMediaGroup().getPlayer().getUrl()
+ .replace("watch?v=", "embed/").replace("&feature=youtube_gdata_player", ""));
+ video.setExternalLink(videoEntry.getMediaGroup().getPlayer().getUrl());
+ video.setRating(videoEntry.getRating() != null ? videoEntry.getRating().getAverage() : 0);
+ video.setDate(new Date(videoEntry.getUpdated().getValue()));
+ video.setViews(videoEntry.getStatistics() != null ? videoEntry.getStatistics()
+ .getViewCount() : 0);
+ video.setData(videoEntry);
+ video.setKeywords(videoEntry.getMediaGroup().getKeywords() != null ? videoEntry
+ .getMediaGroup().getKeywords().getKeywords() : null);
+
+ try
+ {
+ video.setSnapshot(new URL(videoEntry.getMediaGroup().getThumbnails().get(0).getUrl()));
+ }
+ catch (MalformedURLException e)
+ {
+ StudioLogger.error(this.getClass(), "Error while retrieving video snapshot", e);
+ }
+
+ return video;
+
+ }
+}
diff --git a/src/plugins/videos/src/com/motorola/studio/android/videos/model/Video.java b/src/plugins/videos/src/com/motorola/studio/android/videos/model/Video.java
new file mode 100644
index 0000000..d64ec96
--- /dev/null
+++ b/src/plugins/videos/src/com/motorola/studio/android/videos/model/Video.java
@@ -0,0 +1,190 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.videos.model;
+
+import java.net.URL;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * Class that represents a single Video
+ */
+public class Video
+{
+
+ /*
+ * Attributes
+ */
+ private String id = null;
+
+ private String title = null;
+
+ private String description = null;
+
+ private Date date = null;
+
+ private float rating = 0;
+
+ private long views = 0;
+
+ private URL snapshot = null;
+
+ private String embeddedLink = null;
+
+ private String externalLink = null;
+
+ private boolean visible = true;
+
+ private VideoChannel channel = null;
+
+ private Object data = null;
+
+ private List<String> keywords = null;
+
+ /*
+ * Methods
+ */
+ public String getId()
+ {
+ return id;
+ }
+
+ public void setId(String id)
+ {
+ this.id = id;
+ }
+
+ public String getTitle()
+ {
+ return title;
+ }
+
+ public void setTitle(String title)
+ {
+ this.title = title;
+ }
+
+ public String getDescription()
+ {
+ return description;
+ }
+
+ public void setDescription(String description)
+ {
+ this.description = description;
+ }
+
+ public Date getDate()
+ {
+ return date;
+ }
+
+ public void setDate(Date date)
+ {
+ this.date = date;
+ }
+
+ public float getRating()
+ {
+ return rating;
+ }
+
+ public void setRating(float rating)
+ {
+ this.rating = rating;
+ }
+
+ public long getViews()
+ {
+ return views;
+ }
+
+ public void setViews(long views)
+ {
+ this.views = views;
+ }
+
+ public URL getSnapshot()
+ {
+ return snapshot;
+ }
+
+ public void setSnapshot(URL snapshot)
+ {
+ this.snapshot = snapshot;
+ }
+
+ public String getEmbeddedLink()
+ {
+ return embeddedLink;
+ }
+
+ public void setEmbeddedLink(String embeddedLink)
+ {
+ this.embeddedLink = embeddedLink;
+ }
+
+ public String getExternalLink()
+ {
+ return externalLink;
+ }
+
+ public void setExternalLink(String externalLink)
+ {
+ this.externalLink = externalLink;
+ }
+
+ public boolean isVisible()
+ {
+ return visible;
+ }
+
+ public void setVisible(boolean visible)
+ {
+ this.visible = visible;
+ }
+
+ public VideoChannel getChannel()
+ {
+ return channel;
+ }
+
+ public void setChannel(VideoChannel channel)
+ {
+ this.channel = channel;
+ }
+
+ public Object getData()
+ {
+ return data;
+ }
+
+ public void setData(Object data)
+ {
+ this.data = data;
+ }
+
+ public List<String> getKeywords()
+ {
+ return keywords;
+ }
+
+ public void setKeywords(List<String> keywords)
+ {
+ this.keywords = keywords;
+ }
+
+}
diff --git a/src/plugins/videos/src/com/motorola/studio/android/videos/model/VideoChannel.java b/src/plugins/videos/src/com/motorola/studio/android/videos/model/VideoChannel.java
new file mode 100644
index 0000000..82ef7dd
--- /dev/null
+++ b/src/plugins/videos/src/com/motorola/studio/android/videos/model/VideoChannel.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.videos.model;
+
+import java.util.List;
+
+/**
+ * Class that represents a single channel / playlist
+ */
+public class VideoChannel
+{
+
+ /*
+ * Attributes
+ */
+ private String name = null;
+
+ private String displayName = null;
+
+ private boolean ordered = false;
+
+ private boolean defaultChannel = false;
+
+ private List<Video> videos = null;
+
+ private int visibleVideos = 0;
+
+ private boolean active = true;
+
+ /*
+ * Methods
+ */
+ public int getVisibleVideos()
+ {
+ return visibleVideos;
+ }
+
+ public void setVisibleVideos(int visibleVideos)
+ {
+ this.visibleVideos = visibleVideos;
+ }
+
+ public VideoChannel(String name)
+ {
+ this.name = name;
+ }
+
+ public String getName()
+ {
+ return name;
+ }
+
+ public void setName(String name)
+ {
+ this.name = name;
+ }
+
+ public List<Video> getVideos()
+ {
+ return videos;
+ }
+
+ public void setVideos(List<Video> videos)
+ {
+ this.videos = videos;
+ }
+
+ public boolean isOrdered()
+ {
+ return ordered;
+ }
+
+ public void setOrdered(boolean ordered)
+ {
+ this.ordered = ordered;
+ }
+
+ public String getDisplayName()
+ {
+ return displayName;
+ }
+
+ public void setDisplayName(String displayName)
+ {
+ this.displayName = displayName;
+ }
+
+ public boolean isDefaultChannel()
+ {
+ return defaultChannel;
+ }
+
+ public void setDefaultChannel(boolean defaultChannel)
+ {
+ this.defaultChannel = defaultChannel;
+ }
+
+ public boolean isActive()
+ {
+ return active;
+ }
+
+ public void setActive(boolean active)
+ {
+ this.active = active;
+ }
+
+}
diff --git a/src/plugins/videos/src/com/motorola/studio/android/videos/model/VideoManager.java b/src/plugins/videos/src/com/motorola/studio/android/videos/model/VideoManager.java
new file mode 100644
index 0000000..4e87470
--- /dev/null
+++ b/src/plugins/videos/src/com/motorola/studio/android/videos/model/VideoManager.java
@@ -0,0 +1,431 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.videos.model;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.InputStream;
+import java.net.URL;
+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 javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+
+import org.eclipse.core.runtime.FileLocator;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.common.utilities.HttpUtils;
+import com.motorola.studio.android.videos.Activator;
+import com.motorola.studio.android.videos.implementation.youtube.YoutubeVideoServiceProvider;
+import com.motorola.studio.android.videos.model.extension.VideoServiceProvider;
+
+/**
+ * Manage the actions related to videos, including loading them and
+ * providing any related information to other classes.
+ */
+public class VideoManager
+{
+
+ /*
+ * Sort options
+ */
+ public static final int SORT_MOST_RECENT = 0;
+
+ public static final int SORT_MOST_VIEWED = 1;
+
+ public static final int SORT_TOP_RATED = 2;
+
+ /*
+ * Video channels and source definitions (XML)
+ */
+ public static final String VIDEOS_DEFINITIONS = "resources/motodev_videos.xml";
+
+ public static final String TAG_USER = "user";
+
+ public static final String TAG_USER_ATT_NAME = "name";
+
+ public static final String TAG_CHANNEL = "channel";
+
+ public static final String TAG_CHANNEL_ATT_NAME = "name";
+
+ public static final String TAG_CHANNEL_ATT_DISPLAY_NAME = "display_name";
+
+ // "fixed" or "variable"
+ public static final String TAG_CHANNEL_ATT_ORDER = "order";
+
+ public static final String TAG_CHANNEL_ATT_ORDER_FIXED = "fixed";
+
+ // "true" or "false"
+ public static final String TAG_CHANNEL_ATT_DEFAULT = "default";
+
+ /*
+ * Singleton
+ */
+ private static VideoManager instance;
+
+ /*
+ * The selected video service provider
+ */
+ private VideoServiceProvider videoServiceProvider = null;
+
+ /*
+ * Credentials information to be passed to the video service provider
+ */
+ private String userName = null;
+
+ /*
+ * The video channel objects (the videos themselves are attributes of these objects)
+ */
+ private String defaultVideoChannel = null;
+
+ // Map "channel name" -> object that represent the video channel
+ private final Map<String, VideoChannel> channelsMap = new HashMap<String, VideoChannel>();
+
+ private final List<VideoChannel> channelsList = new ArrayList<VideoChannel>();
+
+ /**
+ * Singleton
+ *
+ * @return a unique VideoManager instance
+ * @throws Exception
+ */
+ public static synchronized VideoManager getInstance() throws Exception
+ {
+ if (instance == null)
+ {
+ instance = new VideoManager();
+ }
+ return instance;
+ }
+
+ /**
+ * Load all channels and videos
+ */
+ public void load() throws Exception
+ {
+ channelsMap.clear();
+ channelsList.clear();
+
+ File file =
+ new File(FileLocator.toFileURL(
+ Activator.getDefault().getBundle().getEntry(VIDEOS_DEFINITIONS)).getPath());
+
+ DocumentBuilder documentBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
+ Document doc = documentBuilder.parse(file);
+
+ /*
+ * Get service username
+ */
+ NodeList user = doc.getElementsByTagName(TAG_USER);
+ Node userNameNode = user.item(0).getAttributes().getNamedItem(TAG_USER_ATT_NAME);
+ userName = userNameNode.getNodeValue();
+
+ /*
+ * Initialize video service provider
+ */
+ videoServiceProvider = new YoutubeVideoServiceProvider(userName);
+
+ /*
+ * Get all channels
+ */
+ NodeList channels = doc.getElementsByTagName(TAG_CHANNEL);
+
+ for (int i = 0; i < channels.getLength(); i++)
+ {
+
+ Node channel = channels.item(i);
+ String displayName =
+ channel.getAttributes().getNamedItem(TAG_CHANNEL_ATT_DISPLAY_NAME)
+ .getTextContent();
+ String name =
+ channel.getAttributes().getNamedItem(TAG_CHANNEL_ATT_NAME).getTextContent();
+ boolean order = false;
+ boolean defaultChannel = false;
+ if (channel.getAttributes().getNamedItem(TAG_CHANNEL_ATT_ORDER) != null)
+ {
+ order =
+ channel.getAttributes().getNamedItem(TAG_CHANNEL_ATT_ORDER)
+ .getTextContent().equals(TAG_CHANNEL_ATT_ORDER_FIXED) ? true
+ : false;
+ }
+ if (channel.getAttributes().getNamedItem(TAG_CHANNEL_ATT_DEFAULT) != null)
+ {
+ defaultChannel =
+ channel.getAttributes().getNamedItem(TAG_CHANNEL_ATT_DEFAULT)
+ .getTextContent().equals(new Boolean(true).toString()) ? true
+ : false;
+ }
+
+ /*
+ * Create and populate the channels
+ */
+ VideoChannel videoChannel = new VideoChannel(name);
+ videoChannel.setDisplayName(displayName);
+ videoChannel.setOrdered(order);
+ videoChannel.setDefaultChannel(defaultChannel);
+ if (defaultChannel)
+ {
+ defaultVideoChannel = name;
+ }
+ List<Video> videos = videoServiceProvider.loadVideos(videoChannel);
+ if (videos != null)
+ {
+ videoChannel.setVideos(videos);
+ }
+ else
+ {
+ videoChannel.setActive(false);
+ }
+ channelsMap.put(name, videoChannel);
+ channelsList.add(videoChannel);
+
+ }
+
+ }
+
+ /**
+ * Sort all videos from all channels
+ *
+ * @param criteria
+ */
+ public void sort(final int criteria)
+ {
+ for (VideoChannel channel : channelsList)
+ {
+ if (!channel.isOrdered())
+ {
+ List<Video> videos = channel.getVideos();
+ Collections.sort(videos, new Comparator<Video>()
+ {
+
+ public int compare(Video arg0, Video arg1)
+ {
+ int result = 0;
+
+ switch (criteria)
+ {
+ case VideoManager.SORT_MOST_RECENT:
+ result = arg0.getDate().compareTo(arg1.getDate());
+ break;
+ case VideoManager.SORT_MOST_VIEWED:
+ result = arg0.getViews() >= arg1.getViews() ? 1 : -1;
+ break;
+ case VideoManager.SORT_TOP_RATED:
+ result = arg0.getRating() >= arg1.getRating() ? 1 : -1;
+ break;
+
+ }
+ return result;
+ }
+ });
+ }
+ }
+ }
+
+ /**
+ * Get videos from a channel
+ *
+ * @param channel channel to retrieve the videos
+ */
+ public List<Video> getVideos(VideoChannel channel)
+ {
+ List<Video> videos = null;
+ if (channelsMap.get(channel.getName()) != null)
+ {
+ videos = channelsMap.get(channel.getName()).getVideos();
+ }
+ return videos;
+ }
+
+ /**
+ * Get all channels
+ *
+ * @return all channels
+ */
+ public List<VideoChannel> getChannels()
+ {
+ return channelsList;
+ }
+
+ /**
+ * Get only the active channels
+ *
+ * @return the active channels, which were found by the video service provider
+ */
+ public List<VideoChannel> getActiveChannels()
+ {
+ List<VideoChannel> activeVideoChannels = new ArrayList<VideoChannel>();
+ for (VideoChannel channel : channelsList)
+ {
+ if (channel.isActive())
+ {
+ activeVideoChannels.add(channel);
+ }
+ }
+ return activeVideoChannels;
+ }
+
+ /**
+ * Get the default channel, to be selected in the UI by default
+ *
+ * @return the default channel
+ */
+ public VideoChannel getDefaultChannel()
+ {
+ return channelsMap.get(defaultVideoChannel);
+ }
+
+ /**
+ * Rate a video
+ *
+ * @param video video to be rated
+ * @param rate video rate
+ */
+ public void rate(Video video, int rate) throws Exception
+ {
+ // delegate to the video service provider
+ videoServiceProvider.rate(video, rate);
+ }
+
+ /**
+ * Search all videos from all channels
+ *
+ * @param keyword search keyword
+ * @return only the videos that match the keyword
+ */
+ public List<Video> search(String keyword)
+ {
+
+ List<Video> selectedVideos = new ArrayList<Video>();
+
+ // split the keyword into tokens
+ String[] tokens = keyword.split("\\s");
+ int numberOfTokens = tokens.length;
+
+ /*
+ * For each channel, search the videos and try to find all
+ * tokens on each video
+ */
+ int visibleVideosInChannel;
+ for (VideoChannel channel : channelsList)
+ {
+ visibleVideosInChannel = 0;
+ for (Video video : channel.getVideos())
+ {
+
+ int tokensFound = 0;
+
+ if (numberOfTokens > 0)
+ {
+ StringBuffer sb = new StringBuffer();
+ sb.append(video.getTitle());
+ sb.append(" ");
+ sb.append(video.getDescription());
+ sb.append(" ");
+ sb.append(video.getKeywords());
+
+ for (int i = 0; i < tokens.length; i++)
+ {
+ String token = tokens[i];
+ if (sb.toString().toUpperCase().contains(token.toUpperCase()))
+ {
+ tokensFound++;
+ }
+ }
+ }
+
+ // if all tokens were found, mark the video as visible ...
+ if (tokensFound == numberOfTokens)
+ {
+ video.setVisible(true);
+ visibleVideosInChannel++;
+ selectedVideos.add(video);
+ }
+ // ... otherwise, mark the video as not visible
+ else
+ {
+ video.setVisible(false);
+ }
+ }
+ channel.setVisibleVideos(visibleVideosInChannel);
+ }
+
+ return selectedVideos;
+
+ }
+
+ /**
+ * Get the video thumbnail from the internet, given its URL
+ *
+ * @param url video URL
+ * @return the file representing the downloaded thumbnail
+ */
+ public File getThumbnail(URL url)
+ {
+
+ File cacheIconFile = null;
+ HttpUtils httpUtils = new HttpUtils();
+ InputStream inStream = null;
+ FileOutputStream outStream = null;
+ try
+ {
+ inStream = httpUtils.getInputStreamForUrl(url.toString(), new NullProgressMonitor());
+
+ File remoteFile = new File(url.toString());
+ String[] remoteFileName = remoteFile.getName().split("\\.");
+ cacheIconFile = File.createTempFile(remoteFileName[0], "." + remoteFileName[1]);
+ cacheIconFile.deleteOnExit();
+
+ outStream = new FileOutputStream(cacheIconFile);
+
+ byte[] buf = new byte[1024];
+ int len;
+ while ((len = inStream.read(buf)) > 0)
+ {
+ outStream.write(buf, 0, len);
+ }
+
+ }
+ catch (Exception e)
+ {
+ StudioLogger.error(this.getClass(), "Error while retrieving video thumbnail", e);
+ }
+ finally
+ {
+ try
+ {
+ inStream.close();
+ outStream.close();
+ }
+ catch (Exception e)
+ {
+ StudioLogger.error(e.getMessage());
+ }
+ }
+
+ return cacheIconFile;
+ }
+
+}
diff --git a/src/plugins/videos/src/com/motorola/studio/android/videos/model/extension/VideoServiceProvider.java b/src/plugins/videos/src/com/motorola/studio/android/videos/model/extension/VideoServiceProvider.java
new file mode 100644
index 0000000..d91f83f
--- /dev/null
+++ b/src/plugins/videos/src/com/motorola/studio/android/videos/model/extension/VideoServiceProvider.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.videos.model.extension;
+
+import java.util.List;
+
+import com.motorola.studio.android.videos.model.Video;
+import com.motorola.studio.android.videos.model.VideoChannel;
+
+/**
+ * Define an interface that can be used to connect video
+ * service providers. This way, the videos displayed in the view
+ * can come from different service providers
+ */
+public interface VideoServiceProvider
+{
+
+ /**
+ * Load the videos of the given channel / playlist
+ *
+ * @param channel the channel / playlist of the videos that must be retrieved
+ * @return all videos in the channel / playlist
+ * @throws Exception
+ */
+ public List<Video> loadVideos(VideoChannel channel) throws Exception;
+
+ /**
+ * Rate a video
+ *
+ * @param video the video to be rated
+ * @param rate rate from 1 to 5
+ */
+ public void rate(Video video, int rate) throws Exception;
+
+}
diff --git a/src/plugins/videos/src/com/motorola/studio/android/videos/ui/utils/UiUtilities.java b/src/plugins/videos/src/com/motorola/studio/android/videos/ui/utils/UiUtilities.java
new file mode 100644
index 0000000..674b15e
--- /dev/null
+++ b/src/plugins/videos/src/com/motorola/studio/android/videos/ui/utils/UiUtilities.java
@@ -0,0 +1,289 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.videos.ui.utils;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.swt.custom.StyleRange;
+import org.eclipse.swt.custom.StyledText;
+import org.eclipse.swt.events.DisposeEvent;
+import org.eclipse.swt.events.DisposeListener;
+import org.eclipse.swt.events.PaintEvent;
+import org.eclipse.swt.events.PaintListener;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Device;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.ImageData;
+import org.eclipse.swt.graphics.ImageLoader;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Label;
+
+import com.motorola.studio.android.common.log.StudioLogger;
+
+/**
+ * General UI Utilities used by the MOTODEV Studio Videos View
+ */
+public class UiUtilities
+{
+
+ /**
+ * Highlight the search keyword in the styled text provided
+ *
+ * @param styledText the styled text to be decorated
+ * @param keyword the keyword to be highlighted in the styled text
+ * @param highlightTextColor the background color to be used to highlight the keyword
+ */
+ public static void highlightKeywords(StyledText styledText, String keyword,
+ Color highlightTextColor)
+ {
+
+ // split into multiple keywords
+ String[] keywordPieces = keyword.split(" ");
+
+ /*
+ * Define which characters must be highlighted by creating a boolean array with the text
+ * size plus 1 (the last position will remain with the default value - false, and that's
+ * important to define the last segment to be highlighted)
+ */
+ List<Integer> startOffsets = null;
+ boolean[] highlightedChars = null;
+ highlightedChars = new boolean[styledText.getText().length() + 1];
+
+ // for each keyword, define which characters must be highlighted in the string / StyledText
+ for (String keywordPiece : keywordPieces)
+ {
+ // find all occurrences of that keyword
+ startOffsets =
+ findAllIndexOf(styledText.getText().toUpperCase(), keywordPiece.toUpperCase());
+
+ if (startOffsets.size() > 0)
+ {
+ // for each occurrence, mark the characters that must be highlighted
+ for (int startOffset : startOffsets)
+ {
+ for (int i = startOffset; i < startOffset + keywordPiece.length(); i++)
+ {
+ highlightedChars[i] = true;
+ }
+ }
+
+ }
+ }
+
+ // finally, create and set the style ranges based on the boolean array with the
+ // information about what characters that must be highlighted
+ styledText.setStyleRanges(createStyleRanges(highlightedChars, highlightTextColor));
+ }
+
+ /**
+ * Create and return an array of StyleRanges based on a boolean array defining which characters
+ * must be highlighted in the StyledText
+ *
+ * @param highlightedChars a boolean array defining which characters must be highlighted in the StyledText
+ * @param highlightTextColor the background color to be used to highlight the keyword
+ * @return an array of the StyleRanges that must be applied to the StyledText
+ */
+ private static StyleRange[] createStyleRanges(boolean[] highlightedChars,
+ Color highlightTextColor)
+ {
+
+ List<StyleRange> styleRanges = new ArrayList<StyleRange>();
+
+ // the start variable marks the start of a new segment
+ Integer start = null;
+ for (int j = 0; j < highlightedChars.length; j++)
+ {
+ if (highlightedChars[j] == true)
+ {
+ // this is the beginning of a new segment. If start is not
+ // null, then this is just an adjacent character in the current segment
+ if (start == null)
+ {
+ start = j;
+ }
+ }
+ else
+ {
+ // end of the current segment, register it and continue searching for the next segment
+ if (start != null)
+ {
+ styleRanges.add(getHighlightStyle(start, j - start, highlightTextColor));
+ start = null;
+ }
+ }
+ }
+
+ return styleRanges.toArray(new StyleRange[styleRanges.size()]);
+
+ }
+
+ /**
+ * Find all "indexOf" of a keyword in a string, and not only the
+ * first one, as the method available in the String type implementation
+ *
+ * @param text the base string
+ * @param keyword the keyword to be find in the base string
+ * @return a list with all indexes
+ */
+ private static List<Integer> findAllIndexOf(String text, String keyword)
+ {
+ List<Integer> allIndexes = new ArrayList<Integer>();
+
+ if (!keyword.equals(""))
+ {
+ int index = text.indexOf(keyword);
+ while (index >= 0)
+ {
+ allIndexes.add(index);
+ index = text.indexOf(keyword, index + keyword.length());
+ }
+ }
+
+ return allIndexes;
+ }
+
+ /**
+ * Create the StyleRange that will be used to highlight the keyword in a given location
+ *
+ * @param startOffset where the keyword starts in a string
+ * @param length the size of the keyword
+ * @param highlightTextColor the background color to be used to highlight the keyword
+ * @return the corresponding StyleRange
+ */
+ private static StyleRange getHighlightStyle(int startOffset, int length,
+ Color highlightTextColor)
+ {
+ StyleRange styleRange = new StyleRange();
+ styleRange.start = startOffset;
+ styleRange.length = length;
+ styleRange.background = highlightTextColor;
+ return styleRange;
+ }
+
+ /**
+ * Display an animated GIF in a Label
+ *
+ * @param device the device object to be used to create the UI components
+ * @param path the GIF path
+ * @param container the Label where the GIF will be displayed
+ */
+ public static void displayAnimatedGIF(final Device device, final String path,
+ final Label container)
+ {
+
+ // create the image loader
+ final ImageLoader imageLoader = new ImageLoader();
+ imageLoader.load(path);
+
+ // create GC and set the first image
+ final Image firstImage = new Image(device, imageLoader.data[0]);
+ final GC gc = new GC(firstImage);
+ container.setImage(firstImage);
+
+ // the paint listener is important because just calling redraw on the
+ // container doesn't update the content (actually, that works on Windows,
+ // but on Linux and MacOS it doesn't)
+ container.addPaintListener(new PaintListener()
+ {
+ public void paintControl(PaintEvent e)
+ {
+ e.gc.drawImage(firstImage, 0, 0);
+ }
+ });
+
+ /*
+ * Create a thread that flip among the other images in the GIF
+ */
+ final Thread thread = new Thread()
+ {
+ @Override
+ public void run()
+ {
+
+ try
+ {
+ final Integer[] imageNumber = new Integer[1];
+ imageNumber[0] = 0;
+
+ /*
+ * Do it until the thread is interrupted
+ */
+ while (true)
+ {
+
+ // wait the appropriate time
+ int delayTime = imageLoader.data[imageNumber[0]].delayTime;
+ Thread.sleep(delayTime * 10);
+
+ // draw the next image
+ // it's a sync call so that the executions are not delayed and grouped in the
+ // feature. Ex: after some time, 10 calls are executed one right after another,
+ // what will flip among 10 images in the GIF almost at the same time, which
+ // has no value to the user. Using syncExec we prevent that.
+ Display.getDefault().syncExec(new Runnable()
+ {
+ public void run()
+ {
+ // cyclic counter
+ imageNumber[0] =
+ imageNumber[0].intValue() == imageLoader.data.length - 1
+ ? 0 : imageNumber[0] + 1;
+
+ ImageData nextImageData = imageLoader.data[imageNumber[0]];
+ Image nextImage = new Image(device, nextImageData);
+
+ gc.drawImage(nextImage, nextImageData.x, nextImageData.y);
+ nextImage.dispose();
+
+ // redraw
+ if (!container.isDisposed())
+ {
+ container.redraw();
+ }
+ }
+ });
+
+ }
+ }
+ catch (InterruptedException e)
+ {
+ StudioLogger.info(this.getClass(),
+ "Thread displaying animated GIF was interrupted: " + path);
+ }
+ }
+ };
+
+ /*
+ * When the container is disposed, the thread that flips
+ * among the images in the GIF is also stopped
+ */
+ container.addDisposeListener(new DisposeListener()
+ {
+
+ public void widgetDisposed(DisposeEvent e)
+ {
+ thread.interrupt();
+ }
+ });
+
+ // start the thread
+ thread.start();
+
+ }
+
+}
diff --git a/src/plugins/videos/src/com/motorola/studio/android/videos/ui/views/MOTODEVVideosView.java b/src/plugins/videos/src/com/motorola/studio/android/videos/ui/views/MOTODEVVideosView.java
new file mode 100644
index 0000000..f5fc912
--- /dev/null
+++ b/src/plugins/videos/src/com/motorola/studio/android/videos/ui/views/MOTODEVVideosView.java
@@ -0,0 +1,1077 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.videos.ui.views;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.net.URL;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.eclipse.core.runtime.FileLocator;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.jface.action.ControlContribution;
+import org.eclipse.jface.action.ToolBarManager;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.browser.Browser;
+import org.eclipse.swt.browser.BrowserFunction;
+import org.eclipse.swt.browser.ProgressAdapter;
+import org.eclipse.swt.browser.ProgressEvent;
+import org.eclipse.swt.custom.SashForm;
+import org.eclipse.swt.events.ControlEvent;
+import org.eclipse.swt.events.ControlListener;
+import org.eclipse.swt.events.ModifyEvent;
+import org.eclipse.swt.events.ModifyListener;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Device;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.layout.RowData;
+import org.eclipse.swt.layout.RowLayout;
+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.Event;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Link;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.ui.IMemento;
+import org.eclipse.ui.ISharedImages;
+import org.eclipse.ui.IViewSite;
+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.part.ViewPart;
+import org.osgi.framework.Bundle;
+
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.common.log.UsageDataConstants;
+import com.motorola.studio.android.common.utilities.EclipseUtils;
+import com.motorola.studio.android.videos.Activator;
+import com.motorola.studio.android.videos.i18n.VideosNLS;
+import com.motorola.studio.android.videos.model.Video;
+import com.motorola.studio.android.videos.model.VideoManager;
+import com.motorola.studio.android.videos.ui.utils.UiUtilities;
+
+/**
+ * The view that contains all MOTODEV Video Tutorials
+ */
+public class MOTODEVVideosView extends ViewPart
+{
+
+ /*
+ * The ID of the view as specified by the extension.
+ */
+ public static final String ID = "com.motorola.studio.android.videos.views.MOTODEVVideosView";
+
+ /*
+ * View state
+ */
+ private final String OPEN_EXTERNAL_BROWSER_PREFERENCE = "open.external.browser";
+
+ /*
+ * Video Manager
+ */
+ private VideoManager videoManager;
+
+ /*
+ * Resource constants
+ */
+ private final String VIDEO_WATCH_EMBED_HTML = "resources/watch_video.html";
+
+ private final String VIDEO_WATCH_EMBED_HTML_JS_LIBRARY = "resources/swfobject.js";
+
+ private final String VIEW_LOADING_ICON = "icons/loading_icon.gif";
+
+ private final String VIEW_ERROR_ICON = "icons/error_icon.png";
+
+ /*
+ * Containers
+ */
+ private Composite parentComposite = null;
+
+ private Composite container = null;
+
+ private VideosListComposite videosListComposite;
+
+ private SashForm sash;
+
+ private VideoPlayerComposite videoPlayerArea;
+
+ private String videoWatchURL;
+
+ private String videoWatchJSLibraryURL;
+
+ private Boolean flashPlayerSupport = null;
+
+ private Boolean isBrowserLoaded = null;
+
+ private static Boolean openExternalBrowser = null;
+
+ public static int PLAY_LISTENER = 43214321;
+
+ private Color backgroundColor = null;
+
+ private Image viewErrorImg = null;
+
+ private String loadingImgFullPath = null;
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.part.ViewPart#init(org.eclipse.ui.IViewSite, org.eclipse.ui.IMemento)
+ */
+ @Override
+ public void init(IViewSite site, IMemento memento) throws PartInitException
+ {
+
+ super.init(site, memento);
+
+ try
+ {
+
+ Device device = getDefaultImage().getDevice();
+ Bundle bundle = Activator.getDefault().getBundle();
+ Display display = getSite().getShell().getDisplay();
+
+ /*
+ * Retrieve view state
+ */
+ if (openExternalBrowser == null && memento != null)
+ {
+ openExternalBrowser = memento.getBoolean(OPEN_EXTERNAL_BROWSER_PREFERENCE);
+ }
+
+ /*
+ * Colors
+ */
+ backgroundColor = display.getSystemColor(SWT.COLOR_LIST_BACKGROUND);
+
+ /*
+ * HTML to be embedded in the browser
+ */
+ videoWatchURL =
+ FileLocator.toFileURL(bundle.getEntry(VIDEO_WATCH_EMBED_HTML)).getFile();
+
+ // using the method getAbsolutePath is necessary for this to work on all systems
+ videoWatchJSLibraryURL =
+ new Path(FileLocator.toFileURL(
+ bundle.getEntry(VIDEO_WATCH_EMBED_HTML_JS_LIBRARY)).getFile())
+ .toPortableString();
+
+ /*
+ * Images to be used
+ */
+ viewErrorImg =
+ new Image(device, FileLocator.toFileURL(bundle.getEntry(VIEW_ERROR_ICON))
+ .getPath());
+
+ loadingImgFullPath =
+ FileLocator.toFileURL(
+ Activator.getDefault().getBundle().getEntry(VIEW_LOADING_ICON))
+ .getPath();
+
+ }
+ catch (IOException e)
+ {
+ StudioLogger.error(this.getClass(), "Error while initializing Videos View", e);
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.part.WorkbenchPart#createPartControl(org.eclipse.swt.widgets.Composite)
+ */
+ @Override
+ public void createPartControl(Composite parent)
+ {
+
+ // just point to the parent so that it can be accessed by the Job that will be created
+ parentComposite = parent;
+
+ clearContent();
+
+ /*
+ * Display a loading screen while the content is not loaded
+ */
+ displayLoadingScreen(parent);
+
+ /*
+ * Create a Job to load the view content
+ */
+ Job refreshViews = new Job(VideosNLS.UI_Job_Refresh_View)
+ {
+ @Override
+ protected IStatus run(IProgressMonitor monitor)
+ {
+
+ final Boolean[] showErrorComposite = new Boolean[1];
+ showErrorComposite[0] = false;
+
+ final String[] errorMessage = new String[1];
+ errorMessage[0] = null;
+
+ /*
+ * Try to load the videos information.
+ * If an exception is thrown, log the error and prepare to show
+ * the error screen
+ */
+ try
+ {
+ videoManager = VideoManager.getInstance();
+ videoManager.load();
+ videoManager.sort(VideoManager.SORT_MOST_RECENT);
+ }
+ catch (Exception e)
+ {
+ showErrorComposite[0] = true;
+ errorMessage[0] = VideosNLS.UI_ErrorMsg + " " + e.getLocalizedMessage();
+ StudioLogger.error(this.getClass(), "Error while initializing Videos View", e);
+ }
+
+ if (!monitor.isCanceled())
+ {
+
+ /*
+ * Run the following code in the UI thread, whether it's the
+ * error screen or the videos screen itself. Also, disable the
+ * loading composite that is being displayed
+ */
+ Display.getDefault().asyncExec(new Runnable()
+ {
+ public void run()
+ {
+
+ // dispose the loading composite
+ clearContent();
+
+ if (showErrorComposite[0])
+ {
+ // if an error occurred, display the error screen
+ displayErrorScreen(parentComposite, errorMessage[0]);
+ }
+ else
+ {
+ // ... otherwise, continue with the normal workflow
+ displayVideosScreen(parentComposite);
+ }
+
+ }
+ });
+
+ }
+
+ return Status.OK_STATUS;
+ }
+
+ @Override
+ public boolean belongsTo(Object family)
+ {
+ // relate the Job to this view
+ return getTitle().equals(family);
+ }
+
+ };
+
+ // Schedule the job to run
+ refreshViews.schedule();
+
+ }
+
+ /**
+ * Display the videos screen
+ *
+ * @param parent the parent composite
+ */
+ private void displayVideosScreen(Composite parent)
+ {
+
+ // Create the main composite container
+ container = new Composite(parent, SWT.NONE);
+ container.setLayoutData(new GridData(GridData.FILL_BOTH));
+ container.setLayout(new GridLayout(1, false));
+
+ sash = new SashForm(container, SWT.VERTICAL | SWT.SMOOTH | SWT.BORDER);
+
+ sash.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+
+ // Create a global media player area, with the media player and info related to the video
+ createMediaPlayerAndToolbarArea(sash);
+
+ // Create videos list area
+ createVideosListArea(sash);
+
+ // Adjust the media player size then the view changes its size
+ sash.getChildren()[0].addControlListener(new ControlListener()
+ {
+ public void controlResized(ControlEvent e)
+ {
+ videoPlayerArea.adjustMediaPlayerSize(sash);
+ }
+
+ public void controlMoved(ControlEvent e)
+ {
+ //do nothing
+ }
+ });
+
+ sash.setWeights(new int[]
+ {
+ 1, 2
+ });
+ parentComposite.layout();
+
+ }
+
+ /**
+ * Display the loading screen
+ *
+ * @param parent the parent composite
+ * @return the loading composite
+ */
+ private void displayLoadingScreen(Composite parent)
+ {
+ container =
+ displayInfoScreen(parent, loadingImgFullPath, null, VideosNLS.UI_Loading, null,
+ false);
+
+ parentComposite.layout();
+ }
+
+ /**
+ * Display the error screen
+ *
+ * @param parent the parent composite
+ */
+ private void displayErrorScreen(Composite parent, String error)
+ {
+
+ container = displayInfoScreen(parent, null, viewErrorImg, error, new SelectionAdapter()
+ {
+ @Override
+ public void widgetSelected(SelectionEvent e)
+ {
+ EclipseUtils.openNetworkPreferences(getSite().getShell());
+ }
+ }, true);
+
+ parentComposite.layout();
+
+ }
+
+ /**
+ * Generic method that display a information message (loading, error, warning, etc) in
+ * the screen, being composed of an image, a text and possibly a reload button
+ *
+ * @param parent the parent composite
+ * @param imagePath path to the image that must be displayed
+ * @param image alternatively, if no path is provided (null), an Image object can be used
+ * @param message the message to be displayed (a Link will be used to display the message)
+ * @param selectionListener handler to be called when the user clicks on the message
+ * @param reloadButton whether a "reload" button should be displayed or not
+ * @return the composite created to construct the view
+ */
+ private Composite displayInfoScreen(Composite parent, String imagePath, Image image,
+ String message, SelectionListener selectionListener, boolean reloadButton)
+ {
+
+ Composite mainComposite = new Composite(parent, SWT.NONE);
+ mainComposite.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, true, true));
+ mainComposite.setLayout(new GridLayout(1, false));
+ mainComposite.setBackground(backgroundColor);
+
+ Composite centeredComposite = new Composite(mainComposite, SWT.NONE);
+ centeredComposite.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, true, true));
+ centeredComposite.setLayout(new GridLayout(1, false));
+ centeredComposite.setBackground(backgroundColor);
+
+ /*
+ * Status image
+ */
+ final Label infoImg = new Label(centeredComposite, SWT.NONE);
+ infoImg.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, true, false));
+ infoImg.setBackground(backgroundColor);
+
+ // Checking the mime type doesn't work on Linux
+ // MimetypesFileTypeMap.getDefaultFileTypeMap().getContentType(new File(imagePath)).equals("image/gif"))
+ // Instead of "image/gif", it returns "application/octet-stream"
+ if (imagePath != null && imagePath.endsWith(".gif"))
+ {
+ UiUtilities.displayAnimatedGIF(getDefaultImage().getDevice(), imagePath, infoImg);
+ }
+ else if (image != null)
+ {
+ infoImg.setImage(image);
+ }
+
+ /*
+ * Status text
+ */
+ Link infoText = new Link(centeredComposite, SWT.NONE);
+ GridData loadingTextGridData = new GridData(SWT.CENTER, SWT.NONE, true, false);
+ loadingTextGridData.verticalIndent = 10;
+ infoText.setLayoutData(loadingTextGridData);
+ infoText.setBackground(backgroundColor);
+ infoText.setText(message);
+ if (selectionListener != null)
+ {
+ infoText.addSelectionListener(selectionListener);
+ }
+
+ /*
+ * Reload button, if requested
+ */
+ if (reloadButton)
+ {
+ Button button = new Button(centeredComposite, SWT.NONE);
+ GridData buttonGridData = new GridData(SWT.CENTER, SWT.NONE, false, false);
+ buttonGridData.verticalIndent = 10;
+ button.setLayoutData(buttonGridData);
+ button.setText(VideosNLS.UI_Reload);
+ button.addSelectionListener(new SelectionAdapter()
+ {
+ @Override
+ public void widgetSelected(SelectionEvent e)
+ {
+ reloadView();
+ }
+ });
+ }
+
+ return mainComposite;
+ }
+
+ /**
+ * Select the initial video to be displayed
+ * It's the first video of the first channel
+ */
+ private void selectInitialVideo()
+ {
+
+ if (openExternalBrowser != null && !openExternalBrowser)
+ {
+ Video firstVideo = null;
+ try
+ {
+ firstVideo = videoManager.getActiveChannels().get(0).getVideos().get(0);
+ }
+ catch (Exception e)
+ {
+ StudioLogger.error(this.getClass(), "No initial video to set", e);
+ }
+
+ if (firstVideo != null)
+ {
+ playVideo(firstVideo);
+ }
+
+ videoPlayerArea.adjustVideoPlayerInitialSize();
+ }
+ }
+
+ /**
+ * Clear view content, i.e. remove everything being displayed
+ */
+ private void clearContent()
+ {
+ if (container != null && !container.isDisposed())
+ {
+ container.dispose();
+ }
+
+ }
+
+ /**
+ * Try to reload the view
+ */
+ private void reloadView()
+ {
+ createPartControl(parentComposite);
+ }
+
+ /**
+ * Create the view toolbar, with search field and a checkbox to
+ * define if the video must be displayed inside the view or in
+ * the external browser
+ */
+ private void createToolBar()
+ {
+ ToolBarManager tbManager =
+ (ToolBarManager) getViewSite().getActionBars().getToolBarManager();
+ tbManager.add(new ToolbarContribution("MOTODEV Video Tutorials Toolbar"));
+ tbManager.update(true);
+ getViewSite().getActionBars().updateActionBars();
+ }
+
+ /**
+ * Create the area that contains the media player (browser embedded) as well as
+ * the information about the video being watched.
+ *
+ * Additionally, the toolbar are is also created after the browser is loaded, since
+ * it depends on information acquired during browser loading (such as browser compatibility
+ * and Flash Player availability)
+ *
+ * @param parent the parent SashForm composite
+ */
+ private void createMediaPlayerAndToolbarArea(SashForm parent)
+ {
+ Composite newPage = new Composite(parent, SWT.BORDER);
+ GridLayout layout = new GridLayout(1, false);
+ layout.marginWidth = 1;
+ layout.marginHeight = 1;
+ newPage.setLayout(layout);
+ newPage.setVisible(false);
+
+ videoPlayerArea = new VideoPlayerComposite(newPage);
+ videoPlayerArea.createVideoPlayerArea(new Font(getDefaultImage().getDevice(), getSite()
+ .getShell().getDisplay().getSystemFont().getFontData()[0].getName(), 12, SWT.BOLD));
+ Browser browser = videoPlayerArea.getBrowser();
+ new FlashPlayerDetection(browser, "hasFlashPlayer");
+ // identify when the page is loaded
+ browser.addProgressListener(new ProgressAdapter()
+ {
+
+ @Override
+ public void completed(ProgressEvent event)
+ {
+ // only the first loading matters, so that we remove the listener,
+ // otherwise it would be called each time a new video is selected to
+ // be played
+ isBrowserLoaded = true;
+ videoPlayerArea.getBrowser().removeProgressListener(this);
+
+ /*
+ * The toolbar is only created when the embedded page is loaded
+ */
+ createToolBar();
+ selectInitialVideo();
+ }
+ });
+
+ // Set the JavaScript library source (with the real path) and open the URL
+ File file = getVideoWatchHTML(videoWatchURL, videoWatchJSLibraryURL);
+ browser.setUrl(file.toString());
+ }
+
+ /**
+ * Get the HTML to be used in runtime, with the JavasScript library real path
+ * set. This is necessary because relative paths do not work, and loading
+ * the lib programmatically using JavaScript doesn't work on Linux and MacOS
+ * since this is a async process and the objects defined in the lib might not
+ * be loaded when you try to use them.
+ *
+ * @param fileURL the original HTML file
+ * @param javascriptSrc the JavaScript full path
+ * @return a file with the original HTML file, but with the JavasScript full path set
+ */
+ private File getVideoWatchHTML(String fileURL, String javascriptSrc)
+ {
+
+ File tempFile = null;
+
+ BufferedReader bReader = null;
+ BufferedWriter bWriter = null;
+ FileReader fReader = null;
+ FileWriter fWriter = null;
+ try
+ {
+ // the original file
+ File originalFile = new File(fileURL);
+
+ // the temp file, with the same name as the original
+ String[] tempFileName = originalFile.getName().split("\\.");
+ tempFile = File.createTempFile(tempFileName[0], "." + tempFileName[1]);
+ tempFile.deleteOnExit();
+
+ /*
+ * Read the original file and copy it's content to the temp file,
+ * at the same time that the placeholders for the javascript library
+ * path is replaced by the real path(s)
+ */
+ fReader = new FileReader(originalFile);
+ fWriter = new FileWriter(tempFile);
+ bReader = new BufferedReader(fReader);
+ bWriter = new BufferedWriter(fWriter);
+ String line;
+ StringBuffer stringBuffer = new StringBuffer();
+ while ((line = bReader.readLine()) != null)
+ {
+ stringBuffer.append(line);
+ }
+ bWriter.write(stringBuffer.toString().replace("@@JAVASCRIPT_SRC@@", javascriptSrc));
+
+ }
+ catch (IOException e)
+ {
+ StudioLogger.error(this.getClass(),
+ "Could not create the HTML to be diplayed in the Browser widget", e);
+ }
+ finally
+ {
+ // close both reader/writer
+ try
+ {
+ bWriter.close();
+ bReader.close();
+ fWriter.close();
+ fReader.close();
+ }
+ catch (IOException e)
+ {
+ StudioLogger.error(e.getMessage());
+ }
+
+ }
+
+ return tempFile;
+ }
+
+ /**
+ * Crate the tabs, which represent the playlists
+ *
+ * @param parent the parent composite
+ */
+ private void createVideosListArea(Composite parent)
+ {
+ Composite newPage = new Composite(parent, SWT.NONE);
+ newPage.setLayout(new GridLayout(1, false));
+
+ videosListComposite =
+ new VideosListComposite(newPage, SWT.NONE, videoManager.getActiveChannels(),
+ videoManager.getDefaultChannel().getName());
+ videosListComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+ videosListComposite.addListener(MOTODEVVideosView.PLAY_LISTENER, new Listener()
+ {
+ public void handleEvent(Event event)
+ {
+ playVideo((Video) event.data);
+ }
+ });
+ }
+
+ /**
+ * Play the video, whether using the internal or external browser
+ *
+ * @param video the video to be played
+ */
+ private void playVideo(Video video)
+ {
+
+ if (openExternalBrowser == null || openExternalBrowser)
+ {
+ openVideoInExternalBrowser(video);
+ }
+ else
+ {
+ openVideoInEmbeddedBrowser(video);
+ }
+
+ // Log video playback
+ try
+ {
+
+ StudioLogger.collectUsageData(UsageDataConstants.WHAT_VIDEOS_PLAY,
+ UsageDataConstants.KIND_VIDEOS,
+ "Video Played: "
+ + video.getId()
+ + "|"
+ + video.getTitle()
+ + "|"
+ + (openExternalBrowser != null ? openExternalBrowser.toString()
+ : "null"), Activator.PLUGIN_ID, Activator.getDefault()
+ .getBundle().getVersion().toString());
+ }
+ catch (Exception e)
+ {
+ // do nothing, just do that to be safe and to not break the functionality
+ // due to errors when logging usage information
+ }
+
+ }
+
+ /**
+ * Play the video using the embedded browser
+ *
+ * @param video the video to be played
+ */
+ private void openVideoInEmbeddedBrowser(Video video)
+ {
+ videoPlayerArea.openVideo(video);
+ /*
+ * Select video in the list
+ */
+ videosListComposite.setSelectedVideo(video);
+
+ parentComposite.layout();
+
+ /*
+ * Adjust size
+ */
+ videoPlayerArea.adjustMediaPlayerSize(sash);
+ }
+
+ /**
+ * Open the external browser
+ *
+ * @param video the video to be opened
+ */
+ private void openVideoInExternalBrowser(Video video)
+ {
+
+ IWorkbenchBrowserSupport browserSupport = PlatformUI.getWorkbench().getBrowserSupport();
+ IWebBrowser browser;
+ try
+ {
+
+ browser =
+ browserSupport.createBrowser(IWorkbenchBrowserSupport.LOCATION_BAR
+ | IWorkbenchBrowserSupport.NAVIGATION_BAR
+ | IWorkbenchBrowserSupport.AS_EXTERNAL, VideosNLS.UI_MOTODEV_Video
+ + video.getTitle(), null, null);
+ browser.openURL(new URL(video.getExternalLink()));
+
+ }
+ catch (Exception e)
+ {
+ StudioLogger.error(this.getClass(), "Error opening video on external browser", e);
+ }
+ }
+
+ /**
+ * Filter videos in the UI based on the search results
+ *
+ * @param keyword search keyword
+ */
+ private void searchAndFilter(String keyword)
+ {
+ // execute trim and remove duplicated blank spaces
+ keyword = removeDuplicatedBlankSpaces(keyword.trim());
+
+ // search and filter
+ videosListComposite.filter(videoManager.search(keyword), keyword.equals(""));
+ // highlight keywords
+ videosListComposite.highlightKeywords(keyword);
+
+ }
+
+ /**
+ * Remove all duplicated spaces in a given string
+ *
+ * @param text the string to have duplicated spaces removed
+ * @return the string passed as parameter with no duplicated spaces
+ */
+ private String removeDuplicatedBlankSpaces(String text)
+ {
+ Pattern pattern = Pattern.compile("\\s+");
+ Matcher matcher = pattern.matcher(text);
+ matcher.find();
+ return matcher.replaceAll(" ");
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.part.WorkbenchPart#dispose()
+ */
+ @Override
+ public void dispose()
+ {
+ super.dispose();
+ if (container != null && !container.isDisposed())
+ {
+ container.dispose();
+ }
+ // cancel all jobs related to this view
+ Job.getJobManager().cancel(getTitle());
+ }
+
+ /*
+ * Save the view state when the workbench is closed
+ *
+ * @see org.eclipse.ui.part.ViewPart#saveState(org.eclipse.ui.IMemento)
+ */
+ @Override
+ public void saveState(IMemento memento)
+ {
+ if (canPlayEmbeddedVideo() && openExternalBrowser != null)
+ {
+ memento.putBoolean(OPEN_EXTERNAL_BROWSER_PREFERENCE, openExternalBrowser);
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.part.WorkbenchPart#setFocus()
+ */
+ @Override
+ public void setFocus()
+ {
+ // nothing
+ }
+
+ /**
+ * Check if the browser can load web pages. Due to several problems of the
+ * Browser widget on Linux, we are disabling this feature on this OS for now.
+ *
+ * Some of the problems on Linux are:
+ * - Browser doesn't work with some proxies
+ * - Intermitent crashes of SWT
+ * - The Browser widget blinks on the screen
+ *
+ * For MacOS X, it's enabled only from Snow Leopard on (> 10.6), since the Browser
+ * widget might not work correctly in Leopard with Flash Player 64 bits, since the
+ * kernel modules themselves run in 32 bits, what causes a crash (Studio closes).
+ *
+ * @return "true" if the browser can load pages, "false" otherwise
+ */
+ private boolean canPlayEmbeddedVideo()
+ {
+
+ Boolean result = false;
+
+ // for now, the correct version will only be analyzed on MacOSX
+ boolean correctVersion = true;
+
+ if (Platform.getOS().equals(Platform.OS_MACOSX))
+ {
+ String versionText = System.getProperty("os.version");
+
+ // Get the version as a number
+ float versionNumber = 0;
+ try
+ {
+
+ if (!versionText.contains("."))
+ {
+ versionNumber = Integer.parseInt(versionText);
+ }
+ else
+ {
+ String[] versionPieces = versionText.split("\\.");
+ versionNumber =
+ Float.parseFloat(versionPieces[0] + "."
+ + (!versionPieces[1].equals("") ? versionPieces[1] : "0"));
+
+ }
+ }
+ catch (NumberFormatException e)
+ {
+ StudioLogger.error(this.getClass(), "Cannot get the OS version", e);
+ }
+
+ // Leopard = 10.5 = not supported
+ if (versionNumber <= 10.5)
+ {
+ correctVersion = false;
+ }
+
+ }
+
+ // If we are not on Linux and the MacOS X has the correct version,
+ // check if the browser could load the web page and, consequently,
+ // if the flash player is supported or not
+ if (!Platform.getOS().equals(Platform.OS_LINUX) && correctVersion)
+ {
+ if (isBrowserLoaded != null && isBrowserLoaded && flashPlayerSupport != null)
+ {
+ result = true;
+ }
+ }
+
+ // Log player support
+ try
+ {
+
+ StudioLogger.collectUsageData(UsageDataConstants.WHAT_VIDEOS_PLAYER_SUPPORT,
+ UsageDataConstants.KIND_VIDEOS, result.toString() + "|" + Platform.getOS()
+ + "|" + correctVersion, Activator.PLUGIN_ID, Activator.getDefault()
+ .getBundle().getVersion().toString());
+ }
+ catch (Exception e)
+ {
+ // do nothing, just do that to be safe and to not break the functionality
+ // due to errors when logging usage information
+ }
+
+ return result;
+ }
+
+ /**
+ * Check if an appropriate version of Flash Player was found, which
+ * means that the videos can be displayed in the embedded browser
+ *
+ * @return "true" if an appropriate version of Flash Player was found, "false" otherwise
+ */
+ private boolean hasFlashPlayer()
+ {
+ Boolean result = false;
+
+ if (isBrowserLoaded != null && isBrowserLoaded && flashPlayerSupport != null)
+ {
+ result = flashPlayerSupport;
+ }
+
+ // Log flash player support
+ try
+ {
+
+ StudioLogger.collectUsageData(UsageDataConstants.WHAT_VIDEOS_PLAYER_SUPPORT,
+ UsageDataConstants.KIND_VIDEOS, result.toString(), Activator.PLUGIN_ID,
+ Activator.getDefault().getBundle().getVersion().toString());
+ }
+ catch (Exception e)
+ {
+ // do nothing, just do that to be safe and to not break the functionality
+ // due to errors when logging usage information
+ }
+
+ return result;
+ }
+
+ /**
+ * Get the message that will be displayed to the user to inform that
+ * the appropriate version of Flash Player was not found
+ *
+ * @return the "no Flash Player" message
+ */
+ private String getNoFlashMessage()
+ {
+ String message = VideosNLS.UI_No_Flash_Player + " ";
+
+ if (Platform.getOSArch().equals(Platform.ARCH_X86))
+ {
+ // 32 bits
+ message +=
+ NLS.bind(VideosNLS.UI_No_Flash_Player_32bits_Extension,
+ VideosNLS.UI_Flash_Player_Link_32bits);
+ }
+ else
+ {
+ // 64 bits
+ message +=
+ NLS.bind(VideosNLS.UI_No_Flash_Player_64bits_Extension,
+ VideosNLS.UI_Flash_Player_Link_64bits);
+ }
+
+ return message;
+ }
+
+ /**
+ * Create the toolbar, with a search field and a checkbox to
+ * define if the video must be displayed inside the view or in
+ * the external browser
+ */
+ private class ToolbarContribution extends ControlContribution
+ {
+
+ protected ToolbarContribution(String id)
+ {
+ super(id);
+ }
+
+ @Override
+ protected Control createControl(Composite parent)
+ {
+
+ Composite toolbarComposite = new Composite(parent, SWT.NONE);
+ toolbarComposite.setLayout(new RowLayout(SWT.HORIZONTAL));
+
+ final Text searchText = new Text(toolbarComposite, SWT.SEARCH);
+ searchText.setLayoutData(new RowData(150, SWT.DEFAULT));
+ searchText.setMessage(VideosNLS.UI_Search);
+
+ searchText.addModifyListener(new ModifyListener()
+ {
+ public void modifyText(ModifyEvent e)
+ {
+ searchAndFilter(searchText.getText());
+ }
+ });
+
+ if (canPlayEmbeddedVideo())
+ {
+
+ // recover the state, if there is flash player
+ // if there is NO flash player, the checkbox is always selected
+ boolean enablement = hasFlashPlayer();
+ boolean selection =
+ !enablement ? true : openExternalBrowser != null ? openExternalBrowser
+ : false;
+ final Button openExternal = new Button(toolbarComposite, SWT.CHECK);
+ openExternal.setText(VideosNLS.UI_Open_External_Browser);
+ openExternal.setToolTipText(VideosNLS.UI_Open_External_Browser);
+ openExternal.setEnabled(enablement);
+ openExternal.setSelection(selection);
+ openExternalBrowser = selection;
+
+ if (!enablement)
+ {
+ Label warningImg = new Label(toolbarComposite, SWT.NONE);
+ warningImg.setImage(PlatformUI.getWorkbench().getSharedImages()
+ .getImage(ISharedImages.IMG_OBJS_WARN_TSK));
+ warningImg.setToolTipText(getNoFlashMessage());
+ }
+
+ openExternal.addSelectionListener(new SelectionAdapter()
+ {
+ @Override
+ public void widgetSelected(SelectionEvent e)
+ {
+ openExternalBrowser = openExternal.getSelection();
+ }
+ });
+
+ }
+
+ return toolbarComposite;
+
+ }
+ }
+
+ /**
+ * Function called from JavaScript to inform if the browser
+ * could find Adobe Flash Player or not
+ */
+ private class FlashPlayerDetection extends BrowserFunction
+ {
+
+ @Override
+ public Object function(Object[] arguments)
+ {
+ flashPlayerSupport = new Boolean(arguments[0].toString());
+ return null;
+ }
+
+ public FlashPlayerDetection(Browser browser, String name)
+ {
+ super(browser, name);
+ }
+
+ }
+
+} \ No newline at end of file
diff --git a/src/plugins/videos/src/com/motorola/studio/android/videos/ui/views/VideoComposite.java b/src/plugins/videos/src/com/motorola/studio/android/videos/ui/views/VideoComposite.java
new file mode 100644
index 0000000..54e9076
--- /dev/null
+++ b/src/plugins/videos/src/com/motorola/studio/android/videos/ui/views/VideoComposite.java
@@ -0,0 +1,520 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.videos.ui.views;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.core.runtime.FileLocator;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.ScrolledComposite;
+import org.eclipse.swt.custom.StyledText;
+import org.eclipse.swt.dnd.Clipboard;
+import org.eclipse.swt.dnd.TextTransfer;
+import org.eclipse.swt.dnd.Transfer;
+import org.eclipse.swt.events.ControlEvent;
+import org.eclipse.swt.events.ControlListener;
+import org.eclipse.swt.events.MouseAdapter;
+import org.eclipse.swt.events.MouseEvent;
+import org.eclipse.swt.events.MouseListener;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Cursor;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.ImageData;
+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.Control;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Menu;
+import org.eclipse.swt.widgets.MenuItem;
+
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.videos.Activator;
+import com.motorola.studio.android.videos.i18n.VideosNLS;
+import com.motorola.studio.android.videos.model.Video;
+import com.motorola.studio.android.videos.model.VideoManager;
+import com.motorola.studio.android.videos.ui.utils.UiUtilities;
+
+/**
+ * A specialized Composite to be used to represent a single Video
+ *
+ */
+public class VideoComposite extends Composite
+{
+
+ /*
+ * Video Manager
+ */
+ private VideoManager videoManager;
+
+ /*
+ * The video that is being represented
+ */
+ private Video video = null;
+
+ /*
+ * Resource constants
+ */
+ private final String PLAY_ICON = "icons/play_icon.png";
+
+ private final String THUMBNAIL_LOADING_ICON = "icons/thumbnail_loading.png";
+
+ private final String THUMBNAIL_NOT_AVAILABLE_ICON = "icons/preview_not_available.png";
+
+ /*
+ * Other global elements and resources
+ */
+ private Image playImg = null;
+
+ private Image thumbLoadingImg = null;
+
+ private Font videoTitleFont = null;
+
+ private Font videoTitleSelectedFont = null;
+
+ private Color highlightTextColor = null;
+
+ private Color backgroundColor = null;
+
+ private boolean showAllDescription = false;
+
+ /*
+ * Some widgets in the composite
+ */
+ private StyledText videoTitle = null;
+
+ private final List<StyledText> styledTexts = new ArrayList<StyledText>();
+
+ /*
+ * Parent class
+ */
+ private VideosListComposite parentClass = null;
+
+ private final ScrolledComposite scrollParent;
+
+ /*
+ * Layout details
+ */
+ private final int MINIMUM_DESCRIPTION_COLUMN_WIDTH = 200;
+
+ /**
+ * Constructor responsible for creating the entire component
+ *
+ * @param parent the parent composite
+ * @param style SWT style
+ * @param video the video to be represented
+ */
+ public VideoComposite(VideosListComposite parentClass, Composite parent,
+ ScrolledComposite scrollParent, int style, Video video)
+ {
+ super(parent, style);
+ this.video = video;
+ this.parentClass = parentClass;
+ this.scrollParent = scrollParent;
+ init();
+ createControls();
+ }
+
+ /**
+ * Initialize variables and resources
+ */
+ private void init()
+ {
+
+ try
+ {
+
+ // Video Manager instance
+ videoManager = VideoManager.getInstance();
+
+ /*
+ * Images to be used
+ */
+ playImg =
+ new Image(getShell().getDisplay(), FileLocator.toFileURL(
+ Activator.getDefault().getBundle().getEntry(PLAY_ICON)).getPath());
+
+ thumbLoadingImg =
+ new Image(getShell().getDisplay(), FileLocator.toFileURL(
+ Activator.getDefault().getBundle().getEntry(THUMBNAIL_LOADING_ICON))
+ .getPath());
+
+ /*
+ * Fonts
+ */
+ videoTitleFont =
+ new Font(getShell().getDisplay(), getShell().getDisplay().getSystemFont()
+ .getFontData()[0].getName(), 10, SWT.BOLD);
+
+ videoTitleSelectedFont =
+ new Font(getShell().getDisplay(), getShell().getDisplay().getSystemFont()
+ .getFontData()[0].getName(), 10, SWT.BOLD | SWT.ITALIC);
+
+ /*
+ * Colors
+ */
+ backgroundColor = getShell().getDisplay().getSystemColor(SWT.COLOR_LIST_BACKGROUND);
+
+ highlightTextColor = getShell().getDisplay().getSystemColor(SWT.COLOR_LIST_SELECTION);
+
+ }
+ catch (Exception e)
+ {
+ StudioLogger.error(this.getClass(), "Error while initializing Video Composite", e);
+ }
+
+ }
+
+ /**
+ * Create all widgets of this composite
+ */
+ private void createControls()
+ {
+
+ // set the composite layout
+ setLayout(new GridLayout(3, false));
+ setBackground(backgroundColor);
+
+ // Set loading thumbnail image
+ final Label videoImage = new Label(this, SWT.NONE);
+ videoImage.setImage(thumbLoadingImg);
+
+ // Get the real thumbnail
+ Thread updateVideoImage = new Thread(new Runnable()
+ {
+
+ public void run()
+ {
+ // this operation is time consuming (require internet access)
+ // that's why we are using a new thread and just after we have the
+ // file download we use the UI thread
+ final File thumbnail = videoManager.getThumbnail(video.getSnapshot());
+
+ Display.getDefault().syncExec(new Runnable()
+ {
+ public void run()
+ {
+ Image videoImg =
+ thumbnail != null ? new Image(getShell().getDisplay(), thumbnail
+ .toString()) : null;
+
+ // make sure the element still exists, the view can be closed
+ if (!videoImage.isDisposed())
+ {
+ if (videoImg != null)
+ {
+ videoImage.setImage(videoImg);
+ }
+ else
+ {
+ ImageData notAvailable =
+ Activator.imageDescriptorFromPlugin(Activator.PLUGIN_ID,
+ THUMBNAIL_NOT_AVAILABLE_ICON).getImageData();
+ Point size = videoImage.getSize();
+ if (videoImage.getImage() != null
+ && !videoImage.getImage().isDisposed())
+ {
+ size =
+ new Point(videoImage.getImage().getBounds().width,
+ videoImage.getImage().getBounds().height);
+ videoImage.getImage().dispose();
+ }
+ videoImage.setImage(new Image(getShell().getDisplay(), notAvailable
+ .scaledTo(size.x, size.y)));
+ }
+ }
+
+ }
+ });
+
+ }
+ });
+ updateVideoImage.start();
+
+ videoImage.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, false, false));
+ videoImage.setBackground(backgroundColor);
+
+ final Composite titleAndDescriptionComposite =
+ new Composite(this, SWT.WRAP | SWT.BACKGROUND);
+ GridData data = new GridData(SWT.FILL, SWT.FILL, true, true);
+ data.widthHint = MINIMUM_DESCRIPTION_COLUMN_WIDTH;
+ data.minimumWidth = MINIMUM_DESCRIPTION_COLUMN_WIDTH;
+ data.heightHint = videoImage.getSize().y;
+ titleAndDescriptionComposite.setLayoutData(data);
+ titleAndDescriptionComposite.setLayout(new GridLayout(1, true));
+ titleAndDescriptionComposite.setBackground(backgroundColor);
+
+ videoTitle = new StyledText(titleAndDescriptionComposite, SWT.WRAP | SWT.BACKGROUND);
+ videoTitle.setEnabled(false);
+ videoTitle.setBackground(backgroundColor);
+ videoTitle.setFont(videoTitleFont);
+ data = new GridData(SWT.FILL, SWT.FILL, true, false);
+
+ videoTitle.setLayoutData(data);
+ styledTexts.add(videoTitle);
+
+ final StyledText videoDescription =
+ new StyledText(titleAndDescriptionComposite, SWT.WRAP | SWT.BACKGROUND);
+ videoDescription.setEnabled(false);
+ videoDescription.setBackground(backgroundColor);
+ data = new GridData(SWT.FILL, SWT.FILL, true, true);
+ videoDescription.setLayoutData(data);
+ styledTexts.add(videoDescription);
+
+ Composite playButtonArea = new Composite(this, SWT.NONE);
+ GridLayout layout = new GridLayout(1, true);
+ playButtonArea.setLayoutData(new GridData(SWT.NONE, SWT.NONE, false, false));
+ playButtonArea.setLayout(layout);
+ playButtonArea.setBackground(backgroundColor);
+
+ Label playImage = new Label(playButtonArea, SWT.NONE);
+ playImage.setImage(playImg);
+ playImage.setBackground(backgroundColor);
+ playImage.setToolTipText(VideosNLS.UI_Play_Video);
+ playImage.setCursor(new Cursor(getShell().getDisplay(), SWT.CURSOR_HAND));
+ playImage.setLayoutData(new GridData(SWT.NONE, SWT.NONE, false, false));
+
+ videoTitle.setText(video.getTitle());
+ videoDescription.setText(video.getDescription());
+
+ final Label moreText = new Label(playButtonArea, SWT.WRAP | SWT.BACKGROUND);
+ moreText.addMouseListener(new MouseListener()
+ {
+
+ public void mouseUp(MouseEvent e)
+ {
+ if (!showAllDescription)
+ {
+ showAllDescription = true;
+ GridData data = new GridData(SWT.FILL, SWT.FILL, true, true);
+ data.widthHint = MINIMUM_DESCRIPTION_COLUMN_WIDTH;
+ data.minimumWidth = MINIMUM_DESCRIPTION_COLUMN_WIDTH;
+ titleAndDescriptionComposite.setLayoutData(data);
+ }
+ else
+ {
+ showAllDescription = false;
+ GridData data = new GridData(SWT.FILL, SWT.FILL, true, true);
+ data.widthHint = MINIMUM_DESCRIPTION_COLUMN_WIDTH;
+ data.minimumWidth = MINIMUM_DESCRIPTION_COLUMN_WIDTH;
+ data.heightHint = videoImage.getSize().y;
+ titleAndDescriptionComposite.setLayoutData(data);
+ }
+
+ // redraw everything
+ parentClass.layout();
+ ((Composite) scrollParent.getContent()).layout(true);
+ scrollParent.setMinSize(scrollParent.getContent().computeSize(SWT.DEFAULT,
+ SWT.DEFAULT));
+ }
+
+ public void mouseDown(MouseEvent e)
+ {
+ //do nothing
+ }
+
+ public void mouseDoubleClick(MouseEvent e)
+ {
+ //do nothing
+ }
+ });
+
+ titleAndDescriptionComposite.addControlListener(new ControlListener()
+ {
+
+ public void controlResized(ControlEvent e)
+ {
+ Point descriptionPreferedSize =
+ videoDescription.computeSize(titleAndDescriptionComposite.getSize().x,
+ SWT.DEFAULT);
+ Point titlePreferedSize =
+ videoTitle.computeSize(titleAndDescriptionComposite.getSize().x,
+ SWT.DEFAULT);
+
+ int preferedHeight = descriptionPreferedSize.y + titlePreferedSize.y;
+
+ if (preferedHeight + 20 > videoImage.getSize().y)
+ {
+ moreText.setCursor(new Cursor(getShell().getDisplay(), SWT.CURSOR_HAND));
+ moreText.setBackground(backgroundColor);
+ GridData data = new GridData(SWT.CENTER, SWT.TOP, false, false);
+ moreText.setLayoutData(data);
+
+ moreText.setVisible(true);
+ if (!showAllDescription)
+ {
+ moreText.setText("(+)");
+ moreText.setToolTipText(VideosNLS.UI_More);
+ }
+ else
+ {
+ moreText.setText("(-)");
+ moreText.setToolTipText(VideosNLS.UI_Less);
+ }
+ }
+ else
+ {
+ moreText.setVisible(false);
+ moreText.setCursor(new Cursor(getShell().getDisplay(), SWT.CURSOR_ARROW));
+ }
+ }
+
+ public void controlMoved(ControlEvent e)
+ {
+ // do nothing
+ }
+ });
+
+ playImage.addMouseListener(new MouseAdapter()
+ {
+ @Override
+ public void mouseDown(MouseEvent e)
+ {
+ Event event = new Event();
+ event.data = video;
+ parentClass.notifyListeners(MOTODEVVideosView.PLAY_LISTENER, event);
+ }
+ });
+
+ // create and set the pop-up menu for the composite and all widgets in the composite
+ setMenuForAllWidgets(this, createVideoPopupMenu());
+ }
+
+ /**
+ * Add a menu item in the video composite right-click, with options
+ * to copy the video URL
+ *
+ * @param videoComposite the video composite
+ * @param video the video being represented by the composite
+ * @return the menu
+ */
+ private Menu createVideoPopupMenu()
+ {
+
+ Menu popupMenu = new Menu(getShell(), SWT.POP_UP);
+
+ MenuItem videoUrl = new MenuItem(popupMenu, SWT.NONE);
+ videoUrl.setText(VideosNLS.UI_Copy_URL);
+ MenuItem videoEmbeddedUrl = new MenuItem(popupMenu, SWT.NONE);
+ videoEmbeddedUrl.setText(VideosNLS.UI_Copy_Embedded_URL);
+
+ videoUrl.addSelectionListener(new SelectionAdapter()
+ {
+ @Override
+ public void widgetSelected(SelectionEvent e)
+ {
+ copyTextToClipboard(video.getExternalLink());
+ }
+ });
+
+ videoEmbeddedUrl.addSelectionListener(new SelectionAdapter()
+ {
+ @Override
+ public void widgetSelected(SelectionEvent e)
+ {
+ copyTextToClipboard(video.getEmbeddedLink());
+ }
+ });
+
+ return popupMenu;
+ }
+
+ /**
+ * Recursively add the menu for all widgets
+ *
+ * @param mainComposite the main composite
+ * @param popupMenu the menu to be added to the main composite all all children widgets
+ */
+ private void setMenuForAllWidgets(Composite mainComposite, Menu popupMenu)
+ {
+
+ mainComposite.setMenu(popupMenu);
+
+ for (Control control : mainComposite.getChildren())
+ {
+ control.setMenu(popupMenu);
+ if (control instanceof Composite && ((Composite) control).getChildren().length > 0)
+ {
+ setMenuForAllWidgets((Composite) control, popupMenu);
+ }
+ }
+ }
+
+ /**
+ * Copy the selected text to clipboard
+ *
+ * @param text the text to be copied to the clipboard
+ */
+ private void copyTextToClipboard(String text)
+ {
+ Clipboard clipboard = new Clipboard(getShell().getDisplay());
+ clipboard.setContents(new Object[]
+ {
+ text
+ }, new Transfer[]
+ {
+ TextTransfer.getInstance()
+ });
+ }
+
+ /**
+ * Change the element appearance to mark it as selected
+ */
+ public void select()
+ {
+ videoTitle.setFont(videoTitleSelectedFont);
+ }
+
+ /**
+ * Change the element appearance to the default one
+ */
+ public void deselect()
+ {
+ videoTitle.setFont(videoTitleFont);
+ }
+
+ /**
+ * Highlight the given keyword in the composite
+ *
+ * @param keyword the keyword to be highlighted
+ */
+ public void highlightKeyword(String keyword)
+ {
+ for (StyledText styledText : styledTexts)
+ {
+ UiUtilities.highlightKeywords(styledText, keyword, highlightTextColor);
+ }
+ }
+
+ /**
+ * Retrieve the video being represented
+ *
+ * @return the video that is being represented
+ */
+ public Video getVideo()
+ {
+ return video;
+ }
+
+}
diff --git a/src/plugins/videos/src/com/motorola/studio/android/videos/ui/views/VideoPlayerComposite.java b/src/plugins/videos/src/com/motorola/studio/android/videos/ui/views/VideoPlayerComposite.java
new file mode 100644
index 0000000..85e16ba
--- /dev/null
+++ b/src/plugins/videos/src/com/motorola/studio/android/videos/ui/views/VideoPlayerComposite.java
@@ -0,0 +1,260 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.motorola.studio.android.videos.ui.views;
+
+import org.eclipse.core.internal.net.ProxyManager;
+import org.eclipse.core.net.proxy.IProxyData;
+import org.eclipse.core.net.proxy.IProxyService;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.browser.AuthenticationEvent;
+import org.eclipse.swt.browser.AuthenticationListener;
+import org.eclipse.swt.browser.Browser;
+import org.eclipse.swt.custom.SashForm;
+import org.eclipse.swt.custom.ScrolledComposite;
+import org.eclipse.swt.events.ControlAdapter;
+import org.eclipse.swt.events.ControlEvent;
+import org.eclipse.swt.graphics.Font;
+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.Label;
+
+import com.motorola.studio.android.videos.model.Video;
+
+/**
+ * This composite represents the player area on the MOTODEV videos view
+ *
+ */
+@SuppressWarnings("restriction")
+public class VideoPlayerComposite extends Composite
+{
+ private final int PLAYER_MAXIMUM_WIDTH = 560;
+
+ private final double PLAYER_HEIGHT_RATE = 0.7;
+
+ private static final int BROWSER_MIN_WIDTH = 250;
+
+ private static final int BROWSER_COMPOSITE_MIN_WIDTH = BROWSER_MIN_WIDTH + 50;
+
+ private boolean sashResized = false;
+
+ private ScrolledComposite scrolledComposite;
+
+ private Composite mainComposite;
+
+ private Label globalVideoTitle;
+
+ public VideoPlayerComposite(Composite parent)
+ {
+ super(parent, SWT.NONE);
+ }
+
+ /*
+ * Media Player
+ */
+ private Browser browser;
+
+ public Browser getBrowser()
+ {
+ return browser;
+ }
+
+ public Label getGlobalVideoTitle()
+ {
+ return globalVideoTitle;
+ }
+
+ public void setVideoTitleFont(Font font)
+ {
+ globalVideoTitle.setFont(font);
+ }
+
+ public void createVideoPlayerArea(Font fontForLabel)
+ {
+ GridLayout layout = new GridLayout(1, false);
+ layout.marginWidth = 1;
+ layout.marginHeight = 1;
+ this.setLayout(layout);
+ this.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+ this.setVisible(false);
+
+ /*
+ * Setup the scrolled composite
+ */
+ scrolledComposite = new ScrolledComposite(this, SWT.H_SCROLL | SWT.V_SCROLL);
+ scrolledComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+ scrolledComposite.setLayout(new GridLayout(1, false));
+
+ mainComposite = new Composite(scrolledComposite, SWT.NONE);
+ mainComposite.setLayout(new GridLayout(1, false));
+ mainComposite.setVisible(false);
+ GridData data = new GridData(SWT.FILL, SWT.FILL, true, true);
+ data.widthHint = BROWSER_COMPOSITE_MIN_WIDTH;
+ data.minimumWidth = BROWSER_COMPOSITE_MIN_WIDTH;
+ data.exclude = true;
+ mainComposite.setLayoutData(data);
+
+ globalVideoTitle = new Label(mainComposite, SWT.WRAP);
+ globalVideoTitle.setFont(fontForLabel);
+ globalVideoTitle.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
+
+ browser = new Browser(mainComposite, SWT.NO_SCROLL);
+
+ browser.setVisible(false);
+ browser.setSize(new Point(0, 0));
+ browser.setJavascriptEnabled(true);
+
+ data = new GridData(SWT.NONE, SWT.NONE, false, false);
+ data.widthHint = BROWSER_MIN_WIDTH;
+ data.minimumWidth = BROWSER_MIN_WIDTH;
+ data.exclude = true;
+ browser.setLayoutData(data);
+
+ // set proxy user and password, if needed
+ browser.addAuthenticationListener(new AuthenticationListener()
+ {
+ public void authenticate(AuthenticationEvent event)
+ {
+ IProxyService proxyService = ProxyManager.getProxyManager();
+ if (proxyService.isProxiesEnabled() || proxyService.isSystemProxiesEnabled())
+ {
+ IProxyData proxyData = proxyService.getProxyData(IProxyData.HTTP_PROXY_TYPE);
+ event.user = proxyData.getUserId();
+ event.password = proxyData.getPassword();
+ }
+ }
+ });
+
+ scrolledComposite.setContent(mainComposite);
+ scrolledComposite.setExpandVertical(true);
+ scrolledComposite.setExpandHorizontal(true);
+ scrolledComposite.setAlwaysShowScrollBars(false);
+ scrolledComposite.getVerticalBar().setIncrement(20);
+
+ scrolledComposite.addControlListener(new ControlAdapter()
+ {
+ @Override
+ public void controlResized(ControlEvent e)
+ {
+ scrolledComposite.setMinSize(mainComposite.computeSize(SWT.DEFAULT, SWT.DEFAULT));
+ }
+ });
+ }
+
+ /**
+ * Adjust the media player size, according to the view's size
+ */
+ public void adjustMediaPlayerSize(SashForm sash)
+ {
+
+ Point browserSize = new Point(0, 0);
+
+ Point viewSize = sash.getSize();
+ int sashWidth = viewSize.x - 40;
+
+ ((GridData) globalVideoTitle.getLayoutData()).widthHint = sashWidth;
+
+ globalVideoTitle.getParent().layout();
+
+ int width = sashWidth > PLAYER_MAXIMUM_WIDTH ? PLAYER_MAXIMUM_WIDTH : sashWidth;
+ if (width < BROWSER_MIN_WIDTH)
+ {
+ width = BROWSER_MIN_WIDTH;
+ }
+ int height = (int) (width * PLAYER_HEIGHT_RATE);
+
+ browserSize.x = width;
+ browserSize.y = height;
+
+ int titleLineSize = globalVideoTitle.getSize().y;
+
+ if (sashResized)
+ {
+ // enter here due to a vertical resize (from top to bottom or vice-versa)
+ Point browserOriginalSize = new Point(0, 0);
+ browserOriginalSize.y = browserSize.y;
+ browserOriginalSize.x = browserSize.x;
+
+ // calculate necessary height to get the width
+ height =
+ sash.getChildren()[0].getSize().y - titleLineSize - 20 < (int) (browserSize.x * PLAYER_HEIGHT_RATE)
+ ? sash.getChildren()[0].getSize().y - titleLineSize - 20
+ : (int) (browserSize.x * PLAYER_HEIGHT_RATE);
+ width = (int) (browserSize.y / PLAYER_HEIGHT_RATE);
+
+ // check if the width is smaller than the minimum, if so, set it to the minimum
+ if (width < BROWSER_MIN_WIDTH)
+ {
+ width = BROWSER_MIN_WIDTH;
+ }
+
+ // adjust height according to width (it may have changed)
+ height = (int) (width * PLAYER_HEIGHT_RATE);
+
+ browserSize.x = width;
+ browserSize.y = height;
+
+ // sashResized indicates that the sash division was moved
+ if (browserOriginalSize.x != browserSize.x && browserOriginalSize.y != browserSize.y)
+ {
+ sashResized = true;
+ }
+ if (browserOriginalSize.x != browserSize.x && browserOriginalSize.y == browserSize.y)
+ {
+ sashResized = false;
+ }
+
+ }
+
+ ((GridData) browser.getLayoutData()).widthHint = browserSize.x;
+ ((GridData) browser.getLayoutData()).heightHint = browserSize.y;
+ browser.setSize(browserSize);
+ browser.execute("adjustSize(" + browserSize.x + ", " + browserSize.y + ");");
+
+ globalVideoTitle.getParent().layout();
+
+ sash.setWeights(sash.getWeights());
+ }
+
+ public void adjustVideoPlayerInitialSize()
+ {
+ scrolledComposite.setMinSize(mainComposite.computeSize(SWT.DEFAULT, SWT.DEFAULT));
+ }
+
+ public void openVideo(Video video)
+ {
+ // adjust parent composites to visible
+ this.setVisible(true);
+ this.getParent().setVisible(true);
+ mainComposite.setVisible(true);
+
+ /*
+ * Set video title in the player area
+ */
+ globalVideoTitle.getParent().setVisible(true);
+ globalVideoTitle.setText(video.getTitle());
+ ((GridData) globalVideoTitle.getLayoutData()).exclude = false;
+
+ /*
+ * Adjust and update the browser to play the video
+ */
+ browser.setVisible(true);
+ ((GridData) browser.getLayoutData()).exclude = false;
+ browser.execute("setVideo('" + video.getEmbeddedLink() + "');");
+ }
+}
diff --git a/src/plugins/videos/src/com/motorola/studio/android/videos/ui/views/VideosListComposite.java b/src/plugins/videos/src/com/motorola/studio/android/videos/ui/views/VideosListComposite.java
new file mode 100644
index 0000000..7a6bc26
--- /dev/null
+++ b/src/plugins/videos/src/com/motorola/studio/android/videos/ui/views/VideosListComposite.java
@@ -0,0 +1,393 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.motorola.studio.android.videos.ui.views;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.CTabFolder;
+import org.eclipse.swt.custom.CTabItem;
+import org.eclipse.swt.custom.ScrolledComposite;
+import org.eclipse.swt.events.ControlAdapter;
+import org.eclipse.swt.events.ControlEvent;
+import org.eclipse.swt.graphics.Font;
+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.Label;
+
+import com.motorola.studio.android.videos.i18n.VideosNLS;
+import com.motorola.studio.android.videos.model.Video;
+import com.motorola.studio.android.videos.model.VideoChannel;
+
+/**
+ * A specialized Composite that shows all videos in a list.
+ * The videos are organized in tabs, given that each tab represent
+ * a different channel / playlist
+ */
+public class VideosListComposite extends Composite
+{
+
+ /*
+ * The channels that are being represented
+ */
+ private List<VideoChannel> videoChannels = null;
+
+ /*
+ * The selected video in the list
+ */
+ private String selectedVideo = null;
+
+ /*
+ * The default channel
+ */
+ private String defaultVideoChannelName = null;
+
+ /*
+ * Widgets in the composite
+ */
+ private final List<Composite> tabCompositeList = new ArrayList<Composite>(); // List of the parent composites for each tab
+
+ private final Map<String, VideoComposite> videoCompositeMap =
+ new HashMap<String, VideoComposite>(); // Map "video id" -> composite that represents that video
+
+ private final Map<String, Composite> noVideosCompositeMap = new HashMap<String, Composite>(); // Map "channel name" -> "no videos" composite
+
+ /**
+ * Constructor responsible for creating the entire component
+ *
+ * @param parent the parent composite
+ * @param style SWT style
+ * @param videoChannels all video channels with their respective videos already populated
+ * @param defaultVideoChannelName the name of the default video channel (to be selected in the UI)
+ */
+ public VideosListComposite(Composite parent, int style, List<VideoChannel> videoChannels,
+ String defaultVideoChannelName)
+ {
+ super(parent, style);
+ this.videoChannels = videoChannels;
+ this.defaultVideoChannelName = defaultVideoChannelName;
+ createControls();
+ }
+
+ /**
+ * Change the style for the video being played
+ *
+ * @param video the video to be selected
+ */
+ public void setSelectedVideo(Video video)
+ {
+
+ VideoComposite composite;
+
+ if (video != null)
+ {
+
+ // deselect the current selected video
+ if (selectedVideo != null)
+ {
+ composite = videoCompositeMap.get(selectedVideo);
+ if (composite != null)
+ {
+ composite.deselect();
+ }
+ }
+
+ // update the selection
+ selectedVideo = video.getId();
+ composite = videoCompositeMap.get(video.getId());
+ if (composite != null)
+ {
+ composite.select();
+ }
+
+ }
+ else
+ {
+ selectedVideo = null;
+ }
+
+ }
+
+ /**
+ * Highlight the given keyword in the video composites being
+ * displayed at this moment
+ *
+ * @param keyword the keyword to be highlighted
+ */
+ public void highlightKeywords(String keyword)
+ {
+ for (Map.Entry<String, VideoComposite> entry : videoCompositeMap.entrySet())
+ {
+ VideoComposite videoComposite = entry.getValue();
+ if (videoComposite.getVideo().isVisible())
+ {
+ videoComposite.highlightKeyword(keyword);
+ }
+ }
+ }
+
+ /**
+ * Create all widgets of this composite
+ */
+ private void createControls()
+ {
+
+ // set the composite layout
+ setLayout(new GridLayout(1, false));
+
+ CTabFolder tabFolder = new CTabFolder(this, SWT.NONE);
+ tabFolder.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+ tabFolder.setBorderVisible(true);
+ tabFolder.setFont(new Font(getShell().getDisplay(), getShell().getDisplay().getSystemFont()
+ .getFontData()[0].getName(), 10, SWT.BOLD));
+
+ tabCompositeList.clear();
+ videoCompositeMap.clear();
+
+ for (VideoChannel channel : videoChannels)
+ {
+ CTabItem item = new CTabItem(tabFolder, SWT.NONE);
+ item.setText(channel.getDisplayName());
+ item.setData(channel);
+ Composite itemComposite = new Composite(tabFolder, SWT.NONE);
+ itemComposite.setLayout(new GridLayout(1, false));
+ itemComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+ Composite videosScrolledComposite = populateVideos(itemComposite, channel);
+ // store tab item
+ videosScrolledComposite.setData(item);
+ tabCompositeList.add(videosScrolledComposite);
+ item.setControl(itemComposite);
+ if (channel.getName().equals(defaultVideoChannelName))
+ {
+ tabFolder.setSelection(item);
+ }
+ }
+ }
+
+ /**
+ * Populate one tab (given the parent composite) with its videos
+ *
+ * @param parent
+ * @param videos
+ */
+ private Composite populateVideos(Composite parent, VideoChannel channel)
+ {
+
+ /*
+ * Create the scrolled composite
+ */
+ final ScrolledComposite videosScrolledComposite =
+ new ScrolledComposite(parent, SWT.H_SCROLL | SWT.V_SCROLL);
+ videosScrolledComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+ videosScrolledComposite.setLayout(new GridLayout(1, false));
+
+ /*
+ * Create the composite which will be the parent of each video representation composite
+ */
+ final Composite videosComposite = new Composite(videosScrolledComposite, SWT.NONE);
+ videosComposite.setLayout(new GridLayout(1, false));
+ videosComposite.setLayoutData(new GridData(GridData.FILL, GridData.FILL, true, true));
+
+ createNoVideosComposite(videosComposite, channel.getName());
+
+ /*
+ * For each video, create its composite
+ */
+ for (final Video video : channel.getVideos())
+ {
+ VideoComposite videoComposite =
+ new VideoComposite(this, videosComposite, videosScrolledComposite, SWT.NONE,
+ video);
+ videoComposite.setLayoutData(new GridData(GridData.FILL, GridData.FILL, true, false));
+ videoCompositeMap.put(video.getId(), videoComposite);
+ }
+
+ videosScrolledComposite.setContent(videosComposite);
+ videosScrolledComposite.setExpandVertical(true);
+ videosScrolledComposite.setExpandHorizontal(true);
+ videosScrolledComposite.setAlwaysShowScrollBars(true);
+ videosScrolledComposite.getVerticalBar().setIncrement(20);
+ videosScrolledComposite.getVerticalBar().setPageIncrement(100);
+ videosScrolledComposite.getHorizontalBar().setIncrement(20);
+ videosScrolledComposite.getHorizontalBar().setPageIncrement(100);
+
+ videosScrolledComposite.addControlListener(new ControlAdapter()
+ {
+ @Override
+ public void controlResized(ControlEvent e)
+ {
+ videosScrolledComposite.setMinSize(videosComposite.computeSize(SWT.DEFAULT,
+ SWT.DEFAULT));
+ }
+ });
+
+ return videosScrolledComposite;
+
+ }
+
+ /**
+ * Implement this
+ */
+ public void sort()
+ {
+ /*
+ // change all parents
+ setParent(getSite().getShell());
+ // add in the order
+ setParent(mainComposite);
+ */
+ }
+
+ /**
+ * Filter the videos being displayed and show only the ones passed as parameter.
+ * Also identify if all videos are in the list, so that the appropriate actions
+ * are executed.
+ *
+ * @param videos the list of videos to remain in the screen
+ * @param all whether all videos videos are in the list or not
+ */
+ public void filter(List<Video> videos, boolean all)
+ {
+ /*
+ * Hide all items
+ */
+ for (Map.Entry<String, VideoComposite> composite : videoCompositeMap.entrySet())
+ {
+ GridData layoutData = (GridData) composite.getValue().getLayoutData();
+ if (layoutData != null)
+ {
+ layoutData.exclude = true;
+ }
+ composite.getValue().setVisible(false);
+ }
+
+ /*
+ * Show only items that match
+ */
+ for (Video video : videos)
+ {
+ ((GridData) videoCompositeMap.get(video.getId()).getLayoutData()).exclude = false;
+ Composite composite = videoCompositeMap.get(video.getId());
+ composite.setVisible(true);
+ }
+
+ /*
+ * Update tab name accordingly, and also display or hide the
+ * "no videos" message
+ */
+ for (Composite composite : tabCompositeList)
+ {
+
+ CTabItem tabItem = (CTabItem) composite.getData();
+ VideoChannel videoChannel = (VideoChannel) tabItem.getData();
+
+ if (!all)
+ {
+ tabItem.setText(videoChannel.getDisplayName() + " ("
+ + videoChannel.getVisibleVideos() + ")");
+ }
+ else
+ {
+ tabItem.setText(videoChannel.getDisplayName());
+ }
+
+ /*
+ * Display or hide the "No Videos" composite
+ */
+ if (videoChannel.getVisibleVideos() == 0)
+ {
+ displayNoVideosComposite(videoChannel.getName());
+ }
+ else
+ {
+ hideNoVideosComposite(videoChannel.getName());
+ }
+
+ // update scroll bar size
+ ((ScrolledComposite) composite).setMinSize(composite.getChildren()[0].computeSize(
+ SWT.DEFAULT, SWT.DEFAULT));
+
+ }
+
+ // force the parent layout, as well as the layout of all children
+ getParent().layout(true, true);
+
+ }
+
+ /**
+ * Create a composite that will be displayed when no videos are
+ * found due to a search
+ *
+ * @param parent the parent composite
+ * @param channelName the channel name
+ */
+ private void createNoVideosComposite(Composite parent, String channelName)
+ {
+
+ Composite noVideosComposite = new Composite(parent, SWT.NONE);
+ GridData layoutData = new GridData(SWT.FILL, SWT.FILL, true, false);
+ layoutData.exclude = true;
+ noVideosComposite.setLayoutData(layoutData);
+ noVideosComposite.setLayout(new FillLayout());
+ noVideosComposite.setVisible(false);
+
+ Label noVideosLabel = new Label(noVideosComposite, SWT.NONE);
+ noVideosLabel.setText(VideosNLS.UI_No_Videos_Search);
+
+ noVideosCompositeMap.put(channelName, noVideosComposite);
+
+ }
+
+ /**
+ * Display the composite that states that no videos are
+ * found due to a search
+ *
+ * @param channelName the channel name
+ */
+ private void displayNoVideosComposite(String channelName)
+ {
+ Composite noVideosComposite = noVideosCompositeMap.get(channelName);
+ if (noVideosComposite != null)
+ {
+ ((GridData) noVideosComposite.getLayoutData()).exclude = false;
+ noVideosComposite.setVisible(true);
+ }
+
+ }
+
+ /**
+ * Hide the composite that states that no videos are
+ * found due to a search
+ *
+ * @param channelName the channel name
+ */
+ private void hideNoVideosComposite(String channelName)
+ {
+
+ Composite noVideosComposite = noVideosCompositeMap.get(channelName);
+ if (noVideosComposite != null)
+ {
+ ((GridData) noVideosComposite.getLayoutData()).exclude = true;
+ noVideosComposite.setVisible(false);
+ }
+
+ }
+}