├── .github ├── dependabot.yml └── workflows │ ├── build.yml │ ├── release.yml │ └── run-ui-tests.yml ├── .gitignore ├── .idea └── gradle.xml ├── .run ├── Run Plugin.run.xml ├── Run Tests.run.xml └── Run Verifications.run.xml ├── CHANGELOG.md ├── LICENSE ├── README.md ├── build.gradle.kts ├── files └── gradle-wrapper.jar ├── gradle.properties ├── gradle ├── libs.versions.toml └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── images ├── cmp_template.png ├── compose_template.png ├── desktopApp_configuration.png ├── details.png ├── details_cmp.png ├── edit_configurations.png ├── final.png ├── final_cmp.png ├── iosApp_configuration.png ├── kmp_template.png ├── new_project.png ├── new_project_cmp.png └── xml_template.png ├── qodana.yml ├── settings.gradle.kts └── src └── main ├── kotlin └── com │ └── github │ └── cnrture │ └── quickprojectwizard │ ├── action │ └── CreateFeatureAction.kt │ ├── common │ ├── Constants.kt │ ├── Extensions.kt │ ├── NoRippleTheme.kt │ ├── Utils.kt │ └── file │ │ ├── File.kt │ │ ├── FileTree.kt │ │ ├── FileWriter.kt │ │ ├── ImportAnalyzer.kt │ │ └── LibraryDependencyFinder.kt │ ├── components │ ├── QPWActionCard.kt │ ├── QPWButton.kt │ ├── QPWCheckbox.kt │ ├── QPWDialogActions.kt │ ├── QPWDialogWrapper.kt │ ├── QPWFileTree.kt │ ├── QPWRadioButton.kt │ ├── QPWTabRow.kt │ ├── QPWText.kt │ └── QPWTextField.kt │ ├── data │ ├── SettingsService.kt │ └── SettingsState.kt │ ├── dialog │ ├── FeatureMakerDialogWrapper.kt │ ├── MessageDialogWrapper.kt │ └── SettingsDialogWrapper.kt │ ├── projectwizard │ ├── AndroidStudioTemplateProvider.kt │ ├── CMPTemplate.kt │ ├── ComposeTemplate.kt │ ├── Extensions.kt │ ├── XMLTemplate.kt │ ├── cmparch │ │ ├── AndroidFileGenerator.kt │ │ ├── CMPConfigModel.kt │ │ ├── CMPImageLibrary.kt │ │ ├── CommonFileGenerator.kt │ │ ├── DesktopFileGenerator.kt │ │ ├── FileGenerator.kt │ │ ├── IOSFileGenerator.kt │ │ └── Template.kt │ ├── composearch │ │ ├── common │ │ │ └── EmptyCollectExtension.kt │ │ └── ui │ │ │ ├── EmptyActivity.kt │ │ │ ├── components │ │ │ ├── EmptyEmptyScreen.kt │ │ │ └── EmptyLoadingBar.kt │ │ │ ├── main │ │ │ ├── EmptyMainContract.kt │ │ │ ├── EmptyMainScreen.kt │ │ │ ├── EmptyMainScreenPreviewProvider.kt │ │ │ └── EmptyMainViewModel.kt │ │ │ ├── navigation │ │ │ ├── EmptyNavigationGraph.kt │ │ │ └── Screen.kt │ │ │ └── theme │ │ │ ├── EmptyColor.kt │ │ │ ├── EmptyTheme.kt │ │ │ └── EmptyType.kt │ ├── general │ │ ├── EmptyConstants.kt │ │ ├── EmptyMainApplication.kt │ │ ├── EmptyManifestXml.kt │ │ ├── ImageLibrary.kt │ │ ├── NetworkLibrary.kt │ │ ├── data │ │ │ ├── model │ │ │ │ └── EmptyMainEntityModel.kt │ │ │ ├── repository │ │ │ │ └── EmptyMainRepositoryImpl.kt │ │ │ └── source │ │ │ │ ├── local │ │ │ │ ├── EmptyMainDao.kt │ │ │ │ └── EmptyMainRoomDB.kt │ │ │ │ └── remote │ │ │ │ ├── EmptyKtorApi.kt │ │ │ │ └── EmptyMainService.kt │ │ ├── detekt │ │ │ └── EmptyDetektConfig.kt │ │ ├── di │ │ │ ├── EmptyLocalModule.kt │ │ │ ├── EmptyNetworkModule.kt │ │ │ └── EmptyRepositoryModule.kt │ │ └── domain │ │ │ └── MainRepository.kt │ ├── gradle │ │ ├── Dependencies.kt │ │ ├── GradleKts.kt │ │ ├── Library.kt │ │ ├── Plugin.kt │ │ ├── ProjectGradleKts.kt │ │ ├── Version.kt │ │ └── network │ │ │ ├── GetVersions.kt │ │ │ └── VersionModel.kt │ ├── recipes │ │ ├── ComposeMultiplatformProjectRecipe.kt │ │ ├── ComposeProjectRecipe.kt │ │ └── XMLProjectRecipe.kt │ ├── util │ │ ├── FileUtils.kt │ │ └── NotificationUtil.kt │ └── xmlarch │ │ ├── common │ │ └── EmptyCollectExtension.kt │ │ └── ui │ │ ├── EmptyActivityLayout.kt │ │ ├── EmptyActivityXML.kt │ │ ├── EmptyFragmentLayout.kt │ │ ├── EmptyMainFragment.kt │ │ ├── EmptyMainNavGraphXML.kt │ │ ├── EmptyMainUIState.kt │ │ └── EmptyMainViewModelXML.kt │ ├── theme │ ├── QPWColor.kt │ └── QPWTheme.kt │ └── toolwindow │ ├── QuickProjectWizardToolWindowFactory.kt │ ├── manager │ ├── colorpicker │ │ └── ColorPickerComponent.kt │ ├── featuremaker │ │ ├── FeatureMakerComponent.kt │ │ └── components │ │ │ ├── ConfigurationPanel.kt │ │ │ ├── FileTreePanel.kt │ │ │ └── RootSelectionContent.kt │ └── modulemaker │ │ ├── CreateNewModuleConfigurationPanel.kt │ │ ├── ModuleMakerComponent.kt │ │ ├── MoveExistingFilesToModuleContent.kt │ │ └── components │ │ ├── DetectModulesContent.kt │ │ ├── ExistingModulesContent.kt │ │ ├── FileTreePanel.kt │ │ ├── LibrarySelectionContent.kt │ │ ├── ModuleTypeNameContent.kt │ │ ├── PluginSelectionContent.kt │ │ └── RootSelectionContent.kt │ └── template │ ├── FeatureTemplate.kt │ ├── GitIgnoreTemplate.kt │ ├── GradleTemplate.kt │ ├── ManifestTemplate.kt │ ├── ModuleReadMeTemplate.kt │ └── TemplateWriter.kt └── resources ├── META-INF ├── plugin.xml ├── pluginIcon.svg └── pluginIcon_dark.svg ├── fileTemplates ├── code │ ├── android_main_activity.kt.ft │ ├── android_manifest.xml.ft │ ├── app_module.kt.ft │ ├── application.kt.ft │ ├── assets_contents.json.ft │ ├── collect_extension.kt.ft │ ├── colors_contents.json.ft │ ├── common_app.kt.ft │ ├── compose.gradle.kts.ft │ ├── compose_ios_main.kt.ft │ ├── compose_multiplatform.xml.ft │ ├── compose_screen.kt.ft │ ├── compose_view_model.kt.ft │ ├── compose_wasm_js_main.kt.ft │ ├── config.xcconfig.ft │ ├── constants.kt.ft │ ├── contentview.swift.ft │ ├── contract.kt.ft │ ├── desktop_main.kt.ft │ ├── empty_screen.kt.ft │ ├── gradle.properties.ft │ ├── gradle_wrapper.properties.ft │ ├── icons_contents.json.ft │ ├── idea_workspace.xml.ft │ ├── info.plist.ft │ ├── iosapp.swift.ft │ ├── libs.versions.toml.ft │ ├── loading_bar.kt.ft │ ├── mvi.kt.ft │ ├── mvi_delegate.kt.ft │ ├── navigation_graph.kt.ft │ ├── navigation_screens.kt.ft │ ├── podfile.ft │ ├── preview_contents.json.ft │ ├── project.pbxproj.ft │ ├── project_build.gradle.kts.ft │ ├── repository.kt.ft │ ├── repository_impl.kt.ft │ ├── service.kt.ft │ ├── settings.gradle.kts.ft │ └── values.xml.ft └── velocity_implicit.vm ├── icons └── pluginIcon.svg └── images ├── qpw-cmp.png ├── qpw-compose.png └── qpw-xml.png /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # Dependabot configuration: 2 | # https://docs.github.com/en/free-pro-team@latest/github/administering-a-repository/configuration-options-for-dependency-updates 3 | 4 | version: 2 5 | updates: 6 | # Maintain dependencies for Gradle dependencies 7 | - package-ecosystem: "gradle" 8 | directory: "/" 9 | schedule: 10 | interval: "daily" 11 | # Maintain dependencies for GitHub Actions 12 | - package-ecosystem: "github-actions" 13 | directory: "/" 14 | schedule: 15 | interval: "daily" 16 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | # GitHub Actions Workflow created for handling the release process based on the draft release prepared with the Build workflow. 2 | # Running the publishPlugin task requires all following secrets to be provided: PUBLISH_TOKEN, PRIVATE_KEY, PRIVATE_KEY_PASSWORD, CERTIFICATE_CHAIN. 3 | # See https://plugins.jetbrains.com/docs/intellij/plugin-signing.html for more information. 4 | 5 | name: Release 6 | on: 7 | release: 8 | types: [prereleased, released] 9 | 10 | jobs: 11 | 12 | # Prepare and publish the plugin to JetBrains Marketplace repository 13 | release: 14 | name: Publish Plugin 15 | runs-on: ubuntu-latest 16 | permissions: 17 | contents: write 18 | pull-requests: write 19 | steps: 20 | 21 | # Check out the current repository 22 | - name: Fetch Sources 23 | uses: actions/checkout@v4 24 | with: 25 | ref: ${{ github.event.release.tag_name }} 26 | 27 | # Set up Java environment for the next steps 28 | - name: Setup Java 29 | uses: actions/setup-java@v4 30 | with: 31 | distribution: zulu 32 | java-version: 17 33 | 34 | # Setup Gradle 35 | - name: Setup Gradle 36 | uses: gradle/actions/setup-gradle@v4 37 | with: 38 | gradle-home-cache-cleanup: true 39 | 40 | # Set environment variables 41 | - name: Export Properties 42 | id: properties 43 | shell: bash 44 | run: | 45 | CHANGELOG="$(cat << 'EOM' | sed -e 's/^[[:space:]]*$//g' -e '/./,$!d' 46 | ${{ github.event.release.body }} 47 | EOM 48 | )" 49 | 50 | echo "changelog<> $GITHUB_OUTPUT 51 | echo "$CHANGELOG" >> $GITHUB_OUTPUT 52 | echo "EOF" >> $GITHUB_OUTPUT 53 | 54 | # Update the Unreleased section with the current release note 55 | - name: Patch Changelog 56 | if: ${{ steps.properties.outputs.changelog != '' }} 57 | env: 58 | CHANGELOG: ${{ steps.properties.outputs.changelog }} 59 | run: | 60 | ./gradlew patchChangelog --release-note="$CHANGELOG" 61 | 62 | # Publish the plugin to JetBrains Marketplace 63 | - name: Publish Plugin 64 | env: 65 | PUBLISH_TOKEN: ${{ secrets.PUBLISH_TOKEN }} 66 | CERTIFICATE_CHAIN: ${{ secrets.CERTIFICATE_CHAIN }} 67 | PRIVATE_KEY: ${{ secrets.PRIVATE_KEY }} 68 | PRIVATE_KEY_PASSWORD: ${{ secrets.PRIVATE_KEY_PASSWORD }} 69 | run: ./gradlew publishPlugin 70 | 71 | # Upload artifact as a release asset 72 | - name: Upload Release Asset 73 | env: 74 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 75 | run: gh release upload ${{ github.event.release.tag_name }} ./build/distributions/* 76 | 77 | # Create a pull request 78 | - name: Create Pull Request 79 | if: ${{ steps.properties.outputs.changelog != '' }} 80 | env: 81 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 82 | run: | 83 | VERSION="${{ github.event.release.tag_name }}" 84 | BRANCH="changelog-update-$VERSION" 85 | LABEL="release changelog" 86 | 87 | git config user.email "action@github.com" 88 | git config user.name "GitHub Action" 89 | 90 | git checkout -b $BRANCH 91 | git commit -am "Changelog update - $VERSION" 92 | git push --set-upstream origin $BRANCH 93 | 94 | gh label create "$LABEL" \ 95 | --description "Pull requests with release changelog update" \ 96 | --force \ 97 | || true 98 | 99 | gh pr create \ 100 | --title "Changelog update - \`$VERSION\`" \ 101 | --body "Current pull request contains patched \`CHANGELOG.md\` file for the \`$VERSION\` version." \ 102 | --label "$LABEL" \ 103 | --head $BRANCH 104 | -------------------------------------------------------------------------------- /.github/workflows/run-ui-tests.yml: -------------------------------------------------------------------------------- 1 | # GitHub Actions Workflow for launching UI tests on Linux, Windows, and Mac in the following steps: 2 | # - Prepare and launch IDE with your plugin and robot-server plugin, which is needed to interact with the UI. 3 | # - Wait for IDE to start. 4 | # - Run UI tests with a separate Gradle task. 5 | # 6 | # Please check https://github.com/JetBrains/intellij-ui-test-robot for information about UI tests with IntelliJ Platform. 7 | # 8 | # Workflow is triggered manually. 9 | 10 | name: Run UI Tests 11 | on: 12 | workflow_dispatch 13 | 14 | jobs: 15 | 16 | testUI: 17 | runs-on: ${{ matrix.os }} 18 | strategy: 19 | fail-fast: false 20 | matrix: 21 | include: 22 | - os: ubuntu-latest 23 | runIde: | 24 | export DISPLAY=:99.0 25 | Xvfb -ac :99 -screen 0 1920x1080x16 & 26 | gradle runIdeForUiTests & 27 | - os: windows-latest 28 | runIde: start gradlew.bat runIdeForUiTests 29 | - os: macos-latest 30 | runIde: ./gradlew runIdeForUiTests & 31 | 32 | steps: 33 | 34 | # Check out the current repository 35 | - name: Fetch Sources 36 | uses: actions/checkout@v4 37 | 38 | # Set up Java environment for the next steps 39 | - name: Setup Java 40 | uses: actions/setup-java@v4 41 | with: 42 | distribution: zulu 43 | java-version: 17 44 | 45 | # Setup Gradle 46 | - name: Setup Gradle 47 | uses: gradle/actions/setup-gradle@v4 48 | with: 49 | gradle-home-cache-cleanup: true 50 | 51 | # Run IDEA prepared for UI testing 52 | - name: Run IDE 53 | run: ${{ matrix.runIde }} 54 | 55 | # Wait for IDEA to be started 56 | - name: Health Check 57 | uses: jtalk/url-health-check-action@v4 58 | with: 59 | url: http://127.0.0.1:8082 60 | max-attempts: 15 61 | retry-delay: 30s 62 | 63 | # Run tests 64 | - name: Tests 65 | run: ./gradlew test 66 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | .idea 3 | .intellijPlatform 4 | .qodana 5 | build 6 | .kotlin 7 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 17 | 18 | -------------------------------------------------------------------------------- /.run/Run Plugin.run.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 12 | 17 | 19 | true 20 | true 21 | false 22 | 23 | 24 | -------------------------------------------------------------------------------- /.run/Run Tests.run.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 12 | 17 | 19 | true 20 | true 21 | false 22 | true 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /.run/Run Verifications.run.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 12 | 17 | 19 | true 20 | true 21 | false 22 | false 23 | 24 | 25 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Quick Project Wizard Changelog 4 | 5 | ## [Unreleased] 6 | 7 | ## [0.0.6] 8 | 9 | ### Added 10 | 11 | - Bug fixes 12 | 13 | [Unreleased]: https://github.com/cnrture/QuickProjectWizard/compare/v0.0.6...HEAD 14 | [0.0.6]: https://github.com/cnrture/QuickProjectWizard/commits/v0.0.6 15 | [0.0.5]: https://github.com/cnrture/QuickProjectWizard/commits/v0.0.5 16 | [0.0.4]: https://github.com/cnrture/QuickProjectWizard/commits/v0.0.4 17 | [0.0.3]: https://github.com/cnrture/QuickProjectWizard/commits/v0.0.3 18 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # QuickProjectWizard 2 | 3 | ![Build](https://github.com/cnrture/QuickProjectWizard/workflows/Build/badge.svg) 4 | [![Version](https://img.shields.io/jetbrains/plugin/v/25221.svg)](https://plugins.jetbrains.com/plugin/25221) 5 | [![Downloads](https://img.shields.io/jetbrains/plugin/d/25221.svg)](https://plugins.jetbrains.com/plugin/25221) 6 | 7 | New Project 8 | 9 | 10 | #### This plugin simplifies the process of setting up a new Android project with all the necessary dependencies and configurations. 11 | - Popular libraries and frameworks 12 | - Compose, XML and Compose Multiplatform templates 13 | - data, di, domain, ui, and common packages 14 | 15 | 16 | ## Installation 17 | 18 | - Using the IDE built-in plugin system: 19 | 20 | Settings/Preferences > Plugins > Marketplace > Search for "QuickProjectWizard" > 21 | Install 22 | 23 | - Using JetBrains Marketplace: 24 | 25 | Go to [JetBrains Marketplace](https://plugins.jetbrains.com/plugin/MARKETPLACE_ID) and install it by clicking the Install to ... button in case your IDE is running. 26 | 27 | You can also download the [latest release](https://plugins.jetbrains.com/plugin/MARKETPLACE_ID/versions) from JetBrains Marketplace and install it manually using 28 | Settings/Preferences > Plugins > ⚙️ > Install plugin from disk... 29 | 30 | - Manually: 31 | 32 | Download the [latest release](https://github.com/cnrture/QuickProjectWizard/releases/latest) and install it manually using 33 | Settings/Preferences > Plugins > ⚙️ > Install plugin from disk... 34 | 35 | # Usage 36 | 37 | ## 1. Create a new project 38 | 39 | ### Compose and XML 40 | New Project 41 | 42 | ### Compose Multiplatform 43 | New Project 44 | 45 | - Open your Android Studio. 46 | - Click New Project. 47 | - Select Quick Project Wizard and click Next. 48 |

49 | 50 | ## 2. Configure your project 51 | 52 | ### Compose and XML 53 | Quick Project Wizard Page 54 | 55 | ### Compose Multiplatform 56 | New Project 57 | 58 | - Select the libraries, dependencies and screens you want to include in your project (for example, Retrofit, Room, Hilt, etc.). 59 | - Customize versions and additional configurations. 60 | - After all selections have been made, click Finish. 61 |

62 | 63 | ## 3. Finalize and start coding 64 | 65 | ### Compose and XML 66 | Quick Project Wizard Page 67 | 68 | ### Compose Multiplatform 69 | New Project 70 | 71 | - Review the generated project structure to ensure all necessary dependencies and configurations are correctly applied. 72 | - Start coding your new project with all the essential tools and libraries already integrated. 73 | - Congratulations! You have successfully installed and configured the Quick Project Wizard plugin. Your new Android project is now set up with the latest and most essential dependencies, making your development process more efficient and streamlined. 74 | 75 | ## 4. Configure edit configurations (Just Compose Multiplatform) 76 | 77 | Quick Project Wizard Page 78 | 79 | - Open edit configurations. 80 | 81 | Quick Project Wizard Page 82 | 83 | - Write your composeApp path instead of "\${PROJECT_DIR}\$" (like : ~/Documents/GitHub/CMPApp/composeApp) 84 | 85 | Quick Project Wizard Page 86 | 87 | - Select Apple device from Execution Target. 88 | --- 89 | Plugin based on the [IntelliJ Platform Plugin Template][template]. 90 | 91 | [template]: https://github.com/JetBrains/intellij-platform-plugin-template 92 | [docs:plugin-description]: https://plugins.jetbrains.com/docs/intellij/plugin-user-experience.html#plugin-description-and-presentation 93 | -------------------------------------------------------------------------------- /files/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cnrture/QuickProjectWizard/342d343bb2b105f96bd8de15545fa5cac59e18dd/files/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # IntelliJ Platform Artifacts Repositories -> https://plugins.jetbrains.com/docs/intellij/intellij-artifacts.html 2 | 3 | pluginGroup = com.github.cnrture.quickprojectwizard 4 | pluginName = QuickProjectWizard 5 | pluginRepositoryUrl = https://github.com/cnrture/QuickProjectWizard 6 | # SemVer format -> https://semver.org 7 | pluginVersion = 0.0.6 8 | 9 | # Supported build number ranges and IntelliJ Platform versions -> https://plugins.jetbrains.com/docs/intellij/build-number-ranges.html 10 | pluginSinceBuild = 241 11 | pluginUntilBuild = 252.* 12 | 13 | # IntelliJ Platform Properties -> https://plugins.jetbrains.com/docs/intellij/tools-gradle-intellij-plugin.html#configuration-intellij-extension 14 | platformType = AI 15 | platformVersion = 2024.3.1.13 16 | 17 | # Plugin Dependencies -> https://plugins.jetbrains.com/docs/intellij/plugin-dependencies.html 18 | # Example: platformPlugins = com.jetbrains.php:203.4449.22, org.intellij.scala:2023.3.27@EAP 19 | platformPlugins = 20 | # Example: platformBundledPlugins = com.intellij.java 21 | platformBundledPlugins = org.jetbrains.android, org.jetbrains.kotlin, com.intellij.java 22 | 23 | # Gradle Releases -> https://github.com/gradle/gradle/releases 24 | gradleVersion = 8.9 25 | 26 | # Opt-out flag for bundling Kotlin standard library -> https://jb.gg/intellij-platform-kotlin-stdlib 27 | kotlin.stdlib.default.dependency = false 28 | 29 | # Enable Gradle Configuration Cache -> https://docs.gradle.org/current/userguide/configuration_cache.html 30 | org.gradle.configuration-cache = true 31 | 32 | # Enable Gradle Build Cache -> https://docs.gradle.org/current/userguide/build_cache.html 33 | org.gradle.caching = true 34 | 35 | # Kotlin compiler memory settings 36 | kotlin.daemon.jvmargs=-Xmx4g 37 | 38 | compose.version = 1.7.3 39 | -------------------------------------------------------------------------------- /gradle/libs.versions.toml: -------------------------------------------------------------------------------- 1 | [versions] 2 | # libraries 3 | junit = "4.13.2" 4 | 5 | # plugins 6 | changelog = "2.2.1" 7 | kotlin = "2.1.21" 8 | intelliJPlatform = "2.6.0" 9 | kover = "0.9.1" 10 | qodana = "2025.1.1" 11 | ktorVersion = "3.1.3" 12 | kotlinxSerialization = "1.8.1" 13 | freeMarker = "2.3.34" 14 | 15 | [libraries] 16 | junit = { group = "junit", name = "junit", version.ref = "junit" } 17 | ktor-core = { group = "io.ktor", name = "ktor-client-core", version.ref = "ktorVersion" } 18 | ktor-cio = { group = "io.ktor", name = "ktor-client-cio", version.ref = "ktorVersion" } 19 | kotlinx-serialization = { group = "org.jetbrains.kotlinx", name = "kotlinx-serialization-json", version.ref = "kotlinxSerialization" } 20 | freemarker = { group = "org.freemarker", name = "freemarker", version.ref = "freeMarker" } 21 | 22 | [plugins] 23 | changelog = { id = "org.jetbrains.changelog", version.ref = "changelog" } 24 | intelliJPlatform = { id = "org.jetbrains.intellij.platform", version.ref = "intelliJPlatform" } 25 | kotlin = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" } 26 | kover = { id = "org.jetbrains.kotlinx.kover", version.ref = "kover" } 27 | qodana = { id = "org.jetbrains.qodana", version.ref = "qodana" } 28 | kotlinxSerialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" } 29 | compose = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" } 30 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cnrture/QuickProjectWizard/342d343bb2b105f96bd8de15545fa5cac59e18dd/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | @rem SPDX-License-Identifier: Apache-2.0 17 | @rem 18 | 19 | @if "%DEBUG%"=="" @echo off 20 | @rem ########################################################################## 21 | @rem 22 | @rem Gradle startup script for Windows 23 | @rem 24 | @rem ########################################################################## 25 | 26 | @rem Set local scope for the variables with windows NT shell 27 | if "%OS%"=="Windows_NT" setlocal 28 | 29 | set DIRNAME=%~dp0 30 | if "%DIRNAME%"=="" set DIRNAME=. 31 | @rem This is normally unused 32 | set APP_BASE_NAME=%~n0 33 | set APP_HOME=%DIRNAME% 34 | 35 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 36 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 37 | 38 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 39 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 40 | 41 | @rem Find java.exe 42 | if defined JAVA_HOME goto findJavaFromJavaHome 43 | 44 | set JAVA_EXE=java.exe 45 | %JAVA_EXE% -version >NUL 2>&1 46 | if %ERRORLEVEL% equ 0 goto execute 47 | 48 | echo. 1>&2 49 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 50 | echo. 1>&2 51 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 52 | echo location of your Java installation. 1>&2 53 | 54 | goto fail 55 | 56 | :findJavaFromJavaHome 57 | set JAVA_HOME=%JAVA_HOME:"=% 58 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 59 | 60 | if exist "%JAVA_EXE%" goto execute 61 | 62 | echo. 1>&2 63 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 64 | echo. 1>&2 65 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 66 | echo location of your Java installation. 1>&2 67 | 68 | goto fail 69 | 70 | :execute 71 | @rem Setup the command line 72 | 73 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 74 | 75 | 76 | @rem Execute Gradle 77 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 78 | 79 | :end 80 | @rem End local scope for the variables with windows NT shell 81 | if %ERRORLEVEL% equ 0 goto mainEnd 82 | 83 | :fail 84 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 85 | rem the _cmd.exe /c_ return code! 86 | set EXIT_CODE=%ERRORLEVEL% 87 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 88 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 89 | exit /b %EXIT_CODE% 90 | 91 | :mainEnd 92 | if "%OS%"=="Windows_NT" endlocal 93 | 94 | :omega 95 | -------------------------------------------------------------------------------- /images/cmp_template.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cnrture/QuickProjectWizard/342d343bb2b105f96bd8de15545fa5cac59e18dd/images/cmp_template.png -------------------------------------------------------------------------------- /images/compose_template.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cnrture/QuickProjectWizard/342d343bb2b105f96bd8de15545fa5cac59e18dd/images/compose_template.png -------------------------------------------------------------------------------- /images/desktopApp_configuration.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cnrture/QuickProjectWizard/342d343bb2b105f96bd8de15545fa5cac59e18dd/images/desktopApp_configuration.png -------------------------------------------------------------------------------- /images/details.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cnrture/QuickProjectWizard/342d343bb2b105f96bd8de15545fa5cac59e18dd/images/details.png -------------------------------------------------------------------------------- /images/details_cmp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cnrture/QuickProjectWizard/342d343bb2b105f96bd8de15545fa5cac59e18dd/images/details_cmp.png -------------------------------------------------------------------------------- /images/edit_configurations.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cnrture/QuickProjectWizard/342d343bb2b105f96bd8de15545fa5cac59e18dd/images/edit_configurations.png -------------------------------------------------------------------------------- /images/final.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cnrture/QuickProjectWizard/342d343bb2b105f96bd8de15545fa5cac59e18dd/images/final.png -------------------------------------------------------------------------------- /images/final_cmp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cnrture/QuickProjectWizard/342d343bb2b105f96bd8de15545fa5cac59e18dd/images/final_cmp.png -------------------------------------------------------------------------------- /images/iosApp_configuration.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cnrture/QuickProjectWizard/342d343bb2b105f96bd8de15545fa5cac59e18dd/images/iosApp_configuration.png -------------------------------------------------------------------------------- /images/kmp_template.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cnrture/QuickProjectWizard/342d343bb2b105f96bd8de15545fa5cac59e18dd/images/kmp_template.png -------------------------------------------------------------------------------- /images/new_project.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cnrture/QuickProjectWizard/342d343bb2b105f96bd8de15545fa5cac59e18dd/images/new_project.png -------------------------------------------------------------------------------- /images/new_project_cmp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cnrture/QuickProjectWizard/342d343bb2b105f96bd8de15545fa5cac59e18dd/images/new_project_cmp.png -------------------------------------------------------------------------------- /images/xml_template.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cnrture/QuickProjectWizard/342d343bb2b105f96bd8de15545fa5cac59e18dd/images/xml_template.png -------------------------------------------------------------------------------- /qodana.yml: -------------------------------------------------------------------------------- 1 | # Qodana configuration: 2 | # https://www.jetbrains.com/help/qodana/qodana-yaml.html 3 | 4 | version: 1.0 5 | linter: jetbrains/qodana-jvm-community:latest 6 | projectJDK: "17" 7 | profile: 8 | name: qodana.recommended 9 | exclude: 10 | - name: All 11 | paths: 12 | - .qodana 13 | -------------------------------------------------------------------------------- /settings.gradle.kts: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | plugins { 3 | id("org.jetbrains.compose").version(extra["compose.version"] as String) 4 | id("org.gradle.toolchains.foojay-resolver-convention") version "1.0.0" 5 | } 6 | } 7 | 8 | rootProject.name = "QuickProjectWizard" 9 | -------------------------------------------------------------------------------- /src/main/kotlin/com/github/cnrture/quickprojectwizard/action/CreateFeatureAction.kt: -------------------------------------------------------------------------------- 1 | package com.github.cnrture.quickprojectwizard.action 2 | 3 | import com.github.cnrture.quickprojectwizard.dialog.FeatureMakerDialogWrapper 4 | import com.intellij.icons.AllIcons 5 | import com.intellij.ide.actions.CreateElementActionBase 6 | import com.intellij.openapi.actionSystem.AnActionEvent 7 | import com.intellij.openapi.actionSystem.CommonDataKeys 8 | import com.intellij.openapi.project.Project 9 | import com.intellij.psi.PsiDirectory 10 | import com.intellij.psi.PsiElement 11 | import java.util.function.Consumer 12 | 13 | class CreateFeatureAction : CreateElementActionBase( 14 | "Add Feature", 15 | "Create a new feature", 16 | AllIcons.Nodes.Folder, 17 | ) { 18 | override fun create(name: String, directory: PsiDirectory): Array = emptyArray() 19 | 20 | override fun invokeDialog( 21 | project: Project, 22 | directory: PsiDirectory, 23 | elementsConsumer: Consumer>, 24 | ) { 25 | val project = directory.project 26 | val virtualFile = directory.virtualFile 27 | FeatureMakerDialogWrapper(project, virtualFile).show() 28 | } 29 | 30 | override fun update(e: AnActionEvent) { 31 | val project = e.project 32 | val virtualFile = e.getData(CommonDataKeys.VIRTUAL_FILE) 33 | e.presentation.isEnabledAndVisible = project != null && virtualFile?.isDirectory == true 34 | } 35 | 36 | override fun getActionName(directory: PsiDirectory, newName: String) = "Add Feature" 37 | override fun getErrorTitle(): String = "Error Creating Feature" 38 | override fun startInWriteAction(): Boolean = false 39 | } -------------------------------------------------------------------------------- /src/main/kotlin/com/github/cnrture/quickprojectwizard/common/Constants.kt: -------------------------------------------------------------------------------- 1 | package com.github.cnrture.quickprojectwizard.common 2 | 3 | import freemarker.template.Configuration 4 | import freemarker.template.Version 5 | 6 | object Constants { 7 | const val EMPTY = "" 8 | 9 | const val MODULE_MAKER_WINDOW_WIDTH = 1200 10 | const val FEATURE_MAKER_WINDOW_WIDTH = 700 11 | const val MODULE_MAKER_WINDOW_HEIGHT = 800 12 | const val FEATURE_MAKER_WINDOW_HEIGHT = 500 13 | const val SETTINGS_WINDOW_WIDTH = 800 14 | const val SETTINGS_WINDOW_HEIGHT = 700 15 | 16 | const val ANDROID = "Android" 17 | const val KOTLIN = "Kotlin / JVM" 18 | 19 | const val DEFAULT_MODULE_NAME = ":feature:home (as an example)" 20 | const val DEFAULT_SRC_VALUE = "EMPTY" 21 | 22 | const val DEFAULT_BASE_PACKAGE_NAME = "app.example.quickprojectwizard" 23 | 24 | const val DEFAULT_WEB_VIEW_URL = "https://candroid.dev/" 25 | 26 | const val DEFAULT_EXIT_CODE = 2 27 | 28 | val FREEMARKER_VERSION: Version = Configuration.VERSION_2_3_30 29 | } -------------------------------------------------------------------------------- /src/main/kotlin/com/github/cnrture/quickprojectwizard/common/Extensions.kt: -------------------------------------------------------------------------------- 1 | package com.github.cnrture.quickprojectwizard.common 2 | 3 | import com.intellij.openapi.project.Project 4 | import com.intellij.openapi.vfs.VfsUtil 5 | import java.io.File 6 | import com.github.cnrture.quickprojectwizard.common.file.File as ProjectFile 7 | 8 | fun Project.getCurrentlySelectedFile(selectedSrc: String): File = 9 | File(rootDirectoryStringDropLast() + File.separator + selectedSrc) 10 | 11 | fun Project.rootDirectoryStringDropLast(): String = 12 | basePath!!.split(File.separator).dropLast(1).joinToString(File.separator) 13 | 14 | fun Project.rootDirectoryString(): String = basePath!! 15 | 16 | fun List.refreshFileSystem() { 17 | VfsUtil.markDirtyAndRefresh(false, true, true, *this.toTypedArray()) 18 | } 19 | 20 | fun File.toProjectFile(): ProjectFile = object : ProjectFile { 21 | private val numberOfFiles = listFiles()?.size ?: 0 22 | override val name: String = this@toProjectFile.name 23 | override val absolutePath: String = this@toProjectFile.absolutePath 24 | override val isDirectory: Boolean = this@toProjectFile.isDirectory 25 | override val hasChildren: Boolean = isDirectory && numberOfFiles > 0 26 | override val children: List = this@toProjectFile 27 | .listFiles { _, name -> !name.startsWith(".") } 28 | .orEmpty() 29 | .map { it.toProjectFile() } 30 | } -------------------------------------------------------------------------------- /src/main/kotlin/com/github/cnrture/quickprojectwizard/common/NoRippleTheme.kt: -------------------------------------------------------------------------------- 1 | package com.github.cnrture.quickprojectwizard.common 2 | 3 | import androidx.compose.foundation.interaction.Interaction 4 | import androidx.compose.foundation.interaction.MutableInteractionSource 5 | import kotlinx.coroutines.flow.Flow 6 | import kotlinx.coroutines.flow.emptyFlow 7 | 8 | class NoRippleInteractionSource : MutableInteractionSource { 9 | override val interactions: Flow = emptyFlow() 10 | override suspend fun emit(interaction: Interaction) {} 11 | override fun tryEmit(interaction: Interaction) = true 12 | } -------------------------------------------------------------------------------- /src/main/kotlin/com/github/cnrture/quickprojectwizard/common/file/File.kt: -------------------------------------------------------------------------------- 1 | package com.github.cnrture.quickprojectwizard.common.file 2 | 3 | interface File { 4 | val name: String 5 | val absolutePath: String 6 | val isDirectory: Boolean 7 | val children: List 8 | val hasChildren: Boolean 9 | } -------------------------------------------------------------------------------- /src/main/kotlin/com/github/cnrture/quickprojectwizard/common/file/FileTree.kt: -------------------------------------------------------------------------------- 1 | package com.github.cnrture.quickprojectwizard.common.file 2 | 3 | import androidx.compose.runtime.getValue 4 | import androidx.compose.runtime.mutableStateOf 5 | import androidx.compose.runtime.setValue 6 | 7 | class ExpandableFile(val file: File, val level: Int) { 8 | 9 | var children: List by mutableStateOf(emptyList()) 10 | val canExpand: Boolean get() = file.hasChildren 11 | 12 | fun toggleExpanded() { 13 | children = if (children.isEmpty()) { 14 | file.children 15 | .map { ExpandableFile(it, level + 1) } 16 | .sortedWith(compareBy({ it.file.isDirectory }, { it.file.name })) 17 | .sortedBy { !it.file.isDirectory } 18 | } else { 19 | emptyList() 20 | } 21 | } 22 | } 23 | 24 | class FileTree(root: File) { 25 | 26 | private val expandableRoot = ExpandableFile(root, 0).apply { toggleExpanded() } 27 | 28 | val items: List get() = expandableRoot.toItems() 29 | 30 | inner class Item(internal val file: ExpandableFile) { 31 | val name: String get() = file.file.name 32 | val level: Int get() = file.level 33 | val type: ItemType 34 | get() = if (file.file.isDirectory) { 35 | ItemType.Folder(isExpanded = file.children.isNotEmpty(), canExpand = file.canExpand) 36 | } else { 37 | ItemType.File(ext = file.file.name.substringAfterLast(".").lowercase()) 38 | } 39 | 40 | fun open() = when (type) { 41 | is ItemType.Folder -> file.toggleExpanded() 42 | is ItemType.File -> Unit 43 | } 44 | } 45 | 46 | sealed class ItemType { 47 | class Folder(val isExpanded: Boolean, val canExpand: Boolean) : ItemType() 48 | class File(val ext: String) : ItemType() 49 | } 50 | 51 | private fun ExpandableFile.toItems(): List { 52 | fun ExpandableFile.addTo(list: MutableList) { 53 | list.add(Item(this)) 54 | children.forEach { it.addTo(list) } 55 | } 56 | 57 | val list = mutableListOf() 58 | addTo(list) 59 | return list 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/main/kotlin/com/github/cnrture/quickprojectwizard/common/file/ImportAnalyzer.kt: -------------------------------------------------------------------------------- 1 | package com.github.cnrture.quickprojectwizard.common.file 2 | 3 | import com.github.cnrture.quickprojectwizard.common.Constants 4 | import java.io.File 5 | 6 | class ImportAnalyzer { 7 | 8 | private val modulePackageMapping = mutableMapOf>() 9 | 10 | fun analyzeSourceDirectory(directory: File): List { 11 | val requiredModules = mutableListOf() 12 | val sourceFiles = findAllSourceFiles(directory) 13 | 14 | sourceFiles.forEach { file -> 15 | val imports = extractImports(file) 16 | val modules = mapImportsToModules(imports) 17 | requiredModules.addAll(modules) 18 | } 19 | 20 | return requiredModules.distinct() 21 | } 22 | 23 | private fun findAllSourceFiles(directory: File): List { 24 | val sourceFiles = mutableListOf() 25 | 26 | directory.walkTopDown().forEach { file -> 27 | if (file.isFile && (file.extension == "kt" || file.extension == "java")) { 28 | sourceFiles.add(file) 29 | } 30 | } 31 | 32 | return sourceFiles 33 | } 34 | 35 | private fun extractImports(file: File): List { 36 | val imports = mutableListOf() 37 | 38 | try { 39 | file.readLines().forEach { line -> 40 | val trimmedLine = line.trim() 41 | if (trimmedLine.startsWith("import ")) { 42 | val importPath = trimmedLine.removePrefix("import ").removeSuffix(";").trim() 43 | imports.add(importPath) 44 | } 45 | } 46 | } catch (e: Exception) { 47 | e.printStackTrace() 48 | } 49 | 50 | return imports 51 | } 52 | 53 | private fun mapImportsToModules(imports: List): List { 54 | val modules = mutableListOf() 55 | 56 | imports.forEach { importPath -> 57 | modulePackageMapping.forEach { (module, packages) -> 58 | packages.forEach { packagePrefix -> 59 | if (importPath.startsWith(packagePrefix)) { 60 | modules.add(module) 61 | } 62 | } 63 | } 64 | } 65 | 66 | return modules.distinct() 67 | } 68 | 69 | fun discoverProjectModules(projectRoot: File) { 70 | val moduleMap = mutableMapOf>() 71 | 72 | val gradleFiles = findGradleFiles(projectRoot) 73 | 74 | gradleFiles.forEach { gradleFile -> 75 | val modulePath = getModulePath(projectRoot, gradleFile.parentFile) 76 | val packageNames = findPackageNames(gradleFile.parentFile) 77 | 78 | if (modulePath.isNotEmpty() && packageNames.isNotEmpty()) { 79 | moduleMap[modulePath] = packageNames 80 | } 81 | } 82 | 83 | modulePackageMapping.clear() 84 | modulePackageMapping.putAll(moduleMap) 85 | } 86 | 87 | private fun findGradleFiles(root: File): List { 88 | val gradleFiles = mutableListOf() 89 | 90 | root.walkTopDown() 91 | .filter { it.isFile && (it.name == "build.gradle" || it.name == "build.gradle.kts") } 92 | .forEach { gradleFiles.add(it) } 93 | 94 | return gradleFiles 95 | } 96 | 97 | private fun getModulePath(projectRoot: File, moduleDir: File): String { 98 | val relativePath = moduleDir.relativeTo(projectRoot).path 99 | if (relativePath.isEmpty()) return Constants.EMPTY 100 | 101 | return ":${relativePath.replace(File.separator, ":")}" 102 | } 103 | 104 | private fun findPackageNames(moduleDir: File): MutableList { 105 | val packageNames = mutableListOf() 106 | val sourceRoots = listOf( 107 | File(moduleDir, "src/main/java"), 108 | File(moduleDir, "src/main/kotlin") 109 | ) 110 | 111 | sourceRoots.filter { it.exists() }.forEach { srcRoot -> 112 | srcRoot.walkTopDown() 113 | .filter { it.isDirectory } 114 | .forEach { dir -> 115 | val hasSourceFiles = dir.listFiles()?.any { 116 | it.isFile && (it.extension == "kt" || it.extension == "java") 117 | } ?: false 118 | 119 | if (hasSourceFiles) { 120 | val packagePath = dir.relativeTo(srcRoot).path.replace(File.separator, ".") 121 | if (packagePath.isNotEmpty()) { 122 | packageNames.add(packagePath) 123 | } 124 | } 125 | } 126 | } 127 | 128 | return packageNames 129 | } 130 | } -------------------------------------------------------------------------------- /src/main/kotlin/com/github/cnrture/quickprojectwizard/components/QPWActionCard.kt: -------------------------------------------------------------------------------- 1 | package com.github.cnrture.quickprojectwizard.components 2 | 3 | import androidx.compose.foundation.background 4 | import androidx.compose.foundation.border 5 | import androidx.compose.foundation.clickable 6 | import androidx.compose.foundation.layout.* 7 | import androidx.compose.foundation.shape.RoundedCornerShape 8 | import androidx.compose.material.Icon 9 | import androidx.compose.runtime.Composable 10 | import androidx.compose.ui.Alignment 11 | import androidx.compose.ui.Modifier 12 | import androidx.compose.ui.draw.clip 13 | import androidx.compose.ui.graphics.Color 14 | import androidx.compose.ui.graphics.vector.ImageVector 15 | import androidx.compose.ui.text.TextStyle 16 | import androidx.compose.ui.text.font.FontWeight 17 | import androidx.compose.ui.unit.dp 18 | import androidx.compose.ui.unit.sp 19 | import com.github.cnrture.quickprojectwizard.theme.QPWTheme 20 | 21 | enum class QPWActionCardType { SMALL, MEDIUM, LARGE } 22 | 23 | @Composable 24 | fun QPWActionCard( 25 | modifier: Modifier = Modifier, 26 | title: String? = null, 27 | icon: ImageVector, 28 | actionColor: Color, 29 | type: QPWActionCardType = QPWActionCardType.LARGE, 30 | onClick: () -> Unit, 31 | ) { 32 | val fontSize = when (type) { 33 | QPWActionCardType.SMALL -> 14.sp 34 | QPWActionCardType.MEDIUM -> 16.sp 35 | QPWActionCardType.LARGE -> 20.sp 36 | } 37 | val iconBoxSize = when (type) { 38 | QPWActionCardType.SMALL -> 24.dp 39 | QPWActionCardType.MEDIUM -> 28.dp 40 | QPWActionCardType.LARGE -> 32.dp 41 | } 42 | val iconSize = when (type) { 43 | QPWActionCardType.SMALL -> 16.dp 44 | QPWActionCardType.MEDIUM -> 20.dp 45 | QPWActionCardType.LARGE -> 24.dp 46 | } 47 | val borderSize = when (type) { 48 | QPWActionCardType.SMALL -> 1.dp 49 | QPWActionCardType.MEDIUM -> 2.dp 50 | QPWActionCardType.LARGE -> 3.dp 51 | } 52 | val padding = when (type) { 53 | QPWActionCardType.SMALL -> 8.dp 54 | QPWActionCardType.MEDIUM -> 12.dp 55 | QPWActionCardType.LARGE -> 16.dp 56 | } 57 | Row( 58 | modifier = modifier 59 | .background( 60 | color = QPWTheme.colors.gray, 61 | shape = RoundedCornerShape(12.dp), 62 | ) 63 | .border( 64 | width = borderSize, 65 | color = actionColor, 66 | shape = RoundedCornerShape(12.dp) 67 | ) 68 | .clickable { onClick() } 69 | .padding(padding), 70 | verticalAlignment = Alignment.CenterVertically, 71 | horizontalArrangement = Arrangement.Center, 72 | ) { 73 | if (type == QPWActionCardType.LARGE) { 74 | Box( 75 | modifier = Modifier 76 | .size(iconBoxSize) 77 | .clip(RoundedCornerShape(8.dp)) 78 | .background(actionColor), 79 | contentAlignment = Alignment.Center 80 | ) { 81 | Icon( 82 | imageVector = icon, 83 | contentDescription = null, 84 | tint = QPWTheme.colors.white, 85 | modifier = Modifier.size(iconSize) 86 | ) 87 | } 88 | } else { 89 | Icon( 90 | imageVector = icon, 91 | contentDescription = null, 92 | tint = actionColor, 93 | modifier = Modifier.size(iconSize) 94 | ) 95 | } 96 | 97 | title?.let { 98 | Spacer(modifier = Modifier.width(8.dp)) 99 | 100 | QPWText( 101 | text = it, 102 | style = TextStyle( 103 | fontWeight = FontWeight.Medium, 104 | fontSize = fontSize, 105 | color = QPWTheme.colors.white, 106 | ), 107 | ) 108 | } 109 | } 110 | } -------------------------------------------------------------------------------- /src/main/kotlin/com/github/cnrture/quickprojectwizard/components/QPWButton.kt: -------------------------------------------------------------------------------- 1 | package com.github.cnrture.quickprojectwizard.components 2 | 3 | import androidx.compose.foundation.BorderStroke 4 | import androidx.compose.material.Button 5 | import androidx.compose.material.ButtonDefaults 6 | import androidx.compose.material.OutlinedButton 7 | import androidx.compose.runtime.Composable 8 | import androidx.compose.ui.Modifier 9 | import androidx.compose.ui.graphics.Color 10 | import androidx.compose.ui.text.TextStyle 11 | import androidx.compose.ui.unit.dp 12 | import androidx.compose.ui.unit.sp 13 | import com.github.cnrture.quickprojectwizard.common.NoRippleInteractionSource 14 | import com.github.cnrture.quickprojectwizard.theme.QPWTheme 15 | 16 | @Composable 17 | fun QPWButton( 18 | modifier: Modifier = Modifier, 19 | text: String, 20 | backgroundColor: Color, 21 | onClick: () -> Unit, 22 | ) { 23 | Button( 24 | modifier = modifier, 25 | colors = ButtonDefaults.buttonColors( 26 | backgroundColor = backgroundColor, 27 | contentColor = QPWTheme.colors.white, 28 | ), 29 | interactionSource = NoRippleInteractionSource(), 30 | onClick = onClick, 31 | content = { 32 | QPWText( 33 | text = text, 34 | color = QPWTheme.colors.white, 35 | style = TextStyle( 36 | fontSize = 16.sp, 37 | ), 38 | ) 39 | }, 40 | ) 41 | } 42 | 43 | @Composable 44 | fun QPWOutlinedButton( 45 | modifier: Modifier = Modifier, 46 | text: String, 47 | backgroundColor: Color, 48 | onClick: () -> Unit, 49 | ) { 50 | OutlinedButton( 51 | modifier = modifier, 52 | colors = ButtonDefaults.outlinedButtonColors( 53 | backgroundColor = backgroundColor.copy(alpha = 0.1f), 54 | contentColor = QPWTheme.colors.white, 55 | ), 56 | border = BorderStroke( 57 | width = 2.dp, 58 | color = backgroundColor, 59 | ), 60 | onClick = onClick, 61 | content = { 62 | QPWText( 63 | text = text, 64 | color = QPWTheme.colors.white, 65 | style = TextStyle( 66 | fontSize = 16.sp, 67 | ), 68 | ) 69 | }, 70 | ) 71 | } -------------------------------------------------------------------------------- /src/main/kotlin/com/github/cnrture/quickprojectwizard/components/QPWCheckbox.kt: -------------------------------------------------------------------------------- 1 | package com.github.cnrture.quickprojectwizard.components 2 | 3 | import androidx.compose.foundation.background 4 | import androidx.compose.foundation.layout.Row 5 | import androidx.compose.foundation.layout.Spacer 6 | import androidx.compose.foundation.layout.padding 7 | import androidx.compose.foundation.layout.size 8 | import androidx.compose.foundation.selection.selectable 9 | import androidx.compose.foundation.shape.RoundedCornerShape 10 | import androidx.compose.material.Checkbox 11 | import androidx.compose.material.CheckboxDefaults 12 | import androidx.compose.material.ExperimentalMaterialApi 13 | import androidx.compose.material.LocalMinimumInteractiveComponentEnforcement 14 | import androidx.compose.runtime.Composable 15 | import androidx.compose.runtime.CompositionLocalProvider 16 | import androidx.compose.ui.Alignment 17 | import androidx.compose.ui.Modifier 18 | import androidx.compose.ui.draw.clip 19 | import androidx.compose.ui.graphics.Color 20 | import androidx.compose.ui.semantics.Role 21 | import androidx.compose.ui.text.TextStyle 22 | import androidx.compose.ui.text.font.FontWeight 23 | import androidx.compose.ui.unit.dp 24 | import com.github.cnrture.quickprojectwizard.common.NoRippleInteractionSource 25 | import com.github.cnrture.quickprojectwizard.theme.QPWTheme 26 | 27 | @OptIn(ExperimentalMaterialApi::class) 28 | @Composable 29 | fun QPWCheckbox( 30 | modifier: Modifier = Modifier, 31 | label: String, 32 | checked: Boolean, 33 | isBackgroundEnable: Boolean = false, 34 | color: Color = QPWTheme.colors.red, 35 | onCheckedChange: (Boolean) -> Unit = {}, 36 | ) { 37 | Row( 38 | modifier = modifier 39 | .selectable( 40 | selected = checked, 41 | role = Role.Checkbox, 42 | onClick = { onCheckedChange(checked.not()) } 43 | ) 44 | .then( 45 | if (isBackgroundEnable && checked) { 46 | Modifier.background( 47 | color = color, 48 | shape = RoundedCornerShape(12.dp) 49 | ) 50 | } else { 51 | Modifier 52 | } 53 | ) 54 | .clip(RoundedCornerShape(12.dp)) 55 | .padding(8.dp), 56 | verticalAlignment = Alignment.CenterVertically, 57 | ) { 58 | CompositionLocalProvider( 59 | LocalMinimumInteractiveComponentEnforcement provides false, 60 | ) { 61 | Checkbox( 62 | checked = checked, 63 | onCheckedChange = onCheckedChange, 64 | interactionSource = NoRippleInteractionSource(), 65 | colors = CheckboxDefaults.colors( 66 | checkedColor = if (isBackgroundEnable && checked) { 67 | QPWTheme.colors.white 68 | } else { 69 | color 70 | }, 71 | uncheckedColor = QPWTheme.colors.white, 72 | checkmarkColor = if (isBackgroundEnable && checked) { 73 | color 74 | } else { 75 | QPWTheme.colors.white 76 | }, 77 | ) 78 | ) 79 | } 80 | Spacer(modifier = Modifier.size(8.dp)) 81 | QPWText( 82 | text = label, 83 | color = QPWTheme.colors.white, 84 | style = TextStyle( 85 | fontWeight = FontWeight.SemiBold, 86 | ), 87 | ) 88 | } 89 | } -------------------------------------------------------------------------------- /src/main/kotlin/com/github/cnrture/quickprojectwizard/components/QPWDialogActions.kt: -------------------------------------------------------------------------------- 1 | package com.github.cnrture.quickprojectwizard.components 2 | 3 | import androidx.compose.foundation.layout.Arrangement 4 | import androidx.compose.foundation.layout.Row 5 | import androidx.compose.runtime.Composable 6 | import androidx.compose.ui.Alignment 7 | import androidx.compose.ui.Modifier 8 | import androidx.compose.ui.graphics.Color 9 | import androidx.compose.ui.unit.dp 10 | 11 | @Composable 12 | fun QPWDialogActions( 13 | modifier: Modifier = Modifier, 14 | positiveText: String = "Create", 15 | negativeText: String = "Cancel", 16 | color: Color, 17 | onCancelClick: (() -> Unit)? = null, 18 | onCreateClick: () -> Unit, 19 | ) { 20 | Row( 21 | modifier = modifier, 22 | horizontalArrangement = Arrangement.spacedBy(16.dp, Alignment.End) 23 | ) { 24 | if (onCancelClick != null) { 25 | QPWOutlinedButton( 26 | text = negativeText, 27 | backgroundColor = color, 28 | onClick = onCancelClick, 29 | ) 30 | } 31 | QPWButton( 32 | text = positiveText, 33 | backgroundColor = color, 34 | onClick = onCreateClick, 35 | ) 36 | } 37 | } -------------------------------------------------------------------------------- /src/main/kotlin/com/github/cnrture/quickprojectwizard/components/QPWDialogWrapper.kt: -------------------------------------------------------------------------------- 1 | package com.github.cnrture.quickprojectwizard.components 2 | 3 | import androidx.compose.runtime.Composable 4 | import androidx.compose.ui.awt.ComposePanel 5 | import com.github.cnrture.quickprojectwizard.theme.QPWTheme 6 | import com.intellij.openapi.ui.DialogWrapper 7 | import com.intellij.ui.JBColor 8 | import com.intellij.util.ui.JBUI 9 | import java.awt.Color 10 | import javax.swing.Action 11 | import javax.swing.JComponent 12 | import javax.swing.JRootPane 13 | import javax.swing.UIManager 14 | import javax.swing.border.Border 15 | 16 | abstract class QPWDialogWrapper( 17 | width: Int = 0, 18 | height: Int = 0, 19 | ) : DialogWrapper(true) { 20 | 21 | private val color = JBColor(Color(0xFF18181B.toInt()), Color(0xFF18181B.toInt())) 22 | 23 | init { 24 | init() 25 | UIManager.put("Panel.background", color) 26 | if (width > 0 && height > 0) setSize(width, height) 27 | window?.setLocationRelativeTo(null) 28 | } 29 | 30 | @Composable 31 | abstract fun createDesign() 32 | 33 | override fun createCenterPanel(): JComponent { 34 | return ComposePanel().apply { 35 | setContent { 36 | QPWTheme { 37 | createDesign() 38 | } 39 | } 40 | } 41 | } 42 | 43 | override fun createActions(): Array = emptyArray() 44 | 45 | override fun createSouthPanel(): JComponent { 46 | val southPanel = super.createSouthPanel() 47 | southPanel.background = color 48 | for (component in southPanel.components) { 49 | component.background = color 50 | if (component is JComponent) component.isOpaque = true 51 | } 52 | return southPanel 53 | } 54 | 55 | override fun getRootPane(): JRootPane? { 56 | val rootPane = super.getRootPane() 57 | rootPane.background = color 58 | return rootPane 59 | } 60 | 61 | override fun createContentPaneBorder(): Border = JBUI.Borders.empty() 62 | } -------------------------------------------------------------------------------- /src/main/kotlin/com/github/cnrture/quickprojectwizard/components/QPWRadioButton.kt: -------------------------------------------------------------------------------- 1 | package com.github.cnrture.quickprojectwizard.components 2 | 3 | import androidx.compose.foundation.background 4 | import androidx.compose.foundation.layout.Row 5 | import androidx.compose.foundation.layout.Spacer 6 | import androidx.compose.foundation.layout.padding 7 | import androidx.compose.foundation.layout.size 8 | import androidx.compose.foundation.selection.selectable 9 | import androidx.compose.foundation.shape.RoundedCornerShape 10 | import androidx.compose.material.ExperimentalMaterialApi 11 | import androidx.compose.material.LocalMinimumInteractiveComponentEnforcement 12 | import androidx.compose.material.RadioButton 13 | import androidx.compose.material.RadioButtonDefaults 14 | import androidx.compose.runtime.Composable 15 | import androidx.compose.runtime.CompositionLocalProvider 16 | import androidx.compose.ui.Alignment 17 | import androidx.compose.ui.Modifier 18 | import androidx.compose.ui.draw.clip 19 | import androidx.compose.ui.graphics.Color 20 | import androidx.compose.ui.semantics.Role 21 | import androidx.compose.ui.text.TextStyle 22 | import androidx.compose.ui.text.font.FontWeight 23 | import androidx.compose.ui.unit.dp 24 | import com.github.cnrture.quickprojectwizard.common.NoRippleInteractionSource 25 | import com.github.cnrture.quickprojectwizard.theme.QPWTheme 26 | 27 | @OptIn(ExperimentalMaterialApi::class) 28 | @Composable 29 | fun QPWRadioButton( 30 | text: String, 31 | selected: Boolean, 32 | color: Color = QPWTheme.colors.red, 33 | onClick: () -> Unit, 34 | ) { 35 | Row( 36 | modifier = Modifier 37 | .selectable( 38 | selected = selected, 39 | role = Role.RadioButton, 40 | onClick = onClick, 41 | ) 42 | .then( 43 | if (selected) { 44 | Modifier.background( 45 | color = color, 46 | shape = RoundedCornerShape(12.dp) 47 | ) 48 | } else { 49 | Modifier 50 | } 51 | ) 52 | .clip(RoundedCornerShape(12.dp)) 53 | .padding(horizontal = 12.dp, vertical = 8.dp), 54 | verticalAlignment = Alignment.CenterVertically, 55 | ) { 56 | CompositionLocalProvider( 57 | LocalMinimumInteractiveComponentEnforcement provides false, 58 | ) { 59 | RadioButton( 60 | colors = RadioButtonDefaults.colors( 61 | selectedColor = QPWTheme.colors.white, 62 | unselectedColor = QPWTheme.colors.white, 63 | ), 64 | interactionSource = NoRippleInteractionSource(), 65 | selected = selected, 66 | onClick = onClick, 67 | ) 68 | } 69 | Spacer(modifier = Modifier.size(8.dp)) 70 | QPWText( 71 | text = text, 72 | color = QPWTheme.colors.white, 73 | style = TextStyle( 74 | fontWeight = FontWeight.SemiBold, 75 | ), 76 | ) 77 | } 78 | } -------------------------------------------------------------------------------- /src/main/kotlin/com/github/cnrture/quickprojectwizard/components/QPWTabRow.kt: -------------------------------------------------------------------------------- 1 | package com.github.cnrture.quickprojectwizard.components 2 | 3 | import androidx.compose.foundation.background 4 | import androidx.compose.foundation.layout.Box 5 | import androidx.compose.foundation.layout.padding 6 | import androidx.compose.material.Tab 7 | import androidx.compose.runtime.Composable 8 | import androidx.compose.ui.Modifier 9 | import androidx.compose.ui.graphics.Brush 10 | import androidx.compose.ui.graphics.Color 11 | import androidx.compose.ui.text.TextStyle 12 | import androidx.compose.ui.text.font.FontWeight 13 | import androidx.compose.ui.unit.dp 14 | import androidx.compose.ui.unit.sp 15 | import com.github.cnrture.quickprojectwizard.common.NoRippleInteractionSource 16 | import com.github.cnrture.quickprojectwizard.theme.QPWTheme 17 | 18 | @Composable 19 | fun QPWTabRow(text: String, color: Color, isSelected: Boolean, onTabSelected: () -> Unit) { 20 | Box( 21 | modifier = Modifier 22 | .then( 23 | if (isSelected) { 24 | Modifier.background( 25 | brush = Brush.verticalGradient(listOf(QPWTheme.colors.black, color.copy(alpha = 0.3f))) 26 | ) 27 | } else { 28 | Modifier.background(QPWTheme.colors.black) 29 | } 30 | ) 31 | ) { 32 | Tab( 33 | modifier = Modifier.padding(8.dp), 34 | selected = isSelected, 35 | onClick = onTabSelected, 36 | interactionSource = NoRippleInteractionSource(), 37 | text = { 38 | QPWText( 39 | text = text, 40 | style = TextStyle( 41 | fontSize = 18.sp, 42 | fontWeight = FontWeight.Bold, 43 | ), 44 | color = QPWTheme.colors.white, 45 | ) 46 | } 47 | ) 48 | } 49 | } -------------------------------------------------------------------------------- /src/main/kotlin/com/github/cnrture/quickprojectwizard/components/QPWText.kt: -------------------------------------------------------------------------------- 1 | package com.github.cnrture.quickprojectwizard.components 2 | 3 | import androidx.compose.material.MaterialTheme 4 | import androidx.compose.material.Text 5 | import androidx.compose.runtime.Composable 6 | import androidx.compose.ui.Modifier 7 | import androidx.compose.ui.graphics.Color 8 | import androidx.compose.ui.text.TextStyle 9 | import androidx.compose.ui.text.style.TextOverflow 10 | 11 | @Composable 12 | fun QPWText( 13 | modifier: Modifier = Modifier, 14 | text: String, 15 | style: TextStyle = MaterialTheme.typography.body1, 16 | color: Color = style.color, 17 | overflow: TextOverflow = TextOverflow.Clip, 18 | maxLines: Int = Int.MAX_VALUE, 19 | ) { 20 | Text( 21 | modifier = modifier, 22 | text = text, 23 | color = color, 24 | overflow = overflow, 25 | maxLines = maxLines, 26 | style = style, 27 | ) 28 | } -------------------------------------------------------------------------------- /src/main/kotlin/com/github/cnrture/quickprojectwizard/components/QPWTextField.kt: -------------------------------------------------------------------------------- 1 | package com.github.cnrture.quickprojectwizard.components 2 | 3 | import androidx.compose.material.OutlinedTextField 4 | import androidx.compose.material.TextFieldDefaults 5 | import androidx.compose.runtime.Composable 6 | import androidx.compose.ui.Modifier 7 | import androidx.compose.ui.graphics.Color 8 | import androidx.compose.ui.text.TextStyle 9 | import com.github.cnrture.quickprojectwizard.theme.QPWTheme 10 | 11 | @Composable 12 | fun QPWTextField( 13 | modifier: Modifier = Modifier, 14 | value: String, 15 | onValueChange: (String) -> Unit, 16 | label: String? = null, 17 | placeholder: String? = null, 18 | color: Color = QPWTheme.colors.white, 19 | textStyle: TextStyle = TextStyle.Default, 20 | isSingleLine: Boolean = true, 21 | ) { 22 | OutlinedTextField( 23 | modifier = modifier, 24 | label = { label?.let { QPWText(text = it) } }, 25 | placeholder = { placeholder?.let { QPWText(text = it) } }, 26 | value = value, 27 | onValueChange = { onValueChange(it) }, 28 | textStyle = textStyle, 29 | singleLine = isSingleLine, 30 | maxLines = if (isSingleLine) 1 else Int.MAX_VALUE, 31 | colors = TextFieldDefaults.outlinedTextFieldColors( 32 | focusedLabelColor = QPWTheme.colors.white, 33 | unfocusedLabelColor = QPWTheme.colors.white, 34 | cursorColor = QPWTheme.colors.white, 35 | textColor = QPWTheme.colors.white, 36 | unfocusedBorderColor = QPWTheme.colors.white, 37 | focusedBorderColor = color, 38 | placeholderColor = QPWTheme.colors.white, 39 | ) 40 | ) 41 | } -------------------------------------------------------------------------------- /src/main/kotlin/com/github/cnrture/quickprojectwizard/data/SettingsService.kt: -------------------------------------------------------------------------------- 1 | package com.github.cnrture.quickprojectwizard.data 2 | 3 | import com.intellij.openapi.components.PersistentStateComponent 4 | import com.intellij.openapi.components.Service 5 | import com.intellij.openapi.components.State 6 | import com.intellij.openapi.components.Storage 7 | 8 | @State(name = "QuickProjectWizardSettings", storages = [Storage("quickProjectWizard.xml")]) 9 | @Service(Service.Level.APP) 10 | class SettingsService : PersistentStateComponent { 11 | private var myState = SettingsState() 12 | override fun getState(): SettingsState = myState 13 | override fun loadState(state: SettingsState) { 14 | myState = state 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/kotlin/com/github/cnrture/quickprojectwizard/data/SettingsState.kt: -------------------------------------------------------------------------------- 1 | package com.github.cnrture.quickprojectwizard.data 2 | 3 | import com.github.cnrture.quickprojectwizard.common.Constants 4 | 5 | data class SettingsState( 6 | var defaultPackageName: String = Constants.DEFAULT_BASE_PACKAGE_NAME, 7 | var preferredModuleType: String = Constants.ANDROID, 8 | var webViewUrl: String = Constants.DEFAULT_WEB_VIEW_URL, 9 | 10 | var featureScreenTemplate: String = Constants.EMPTY, 11 | var featureViewModelTemplate: String = Constants.EMPTY, 12 | var featureContractTemplate: String = Constants.EMPTY, 13 | var featureComponentKeyTemplate: String = Constants.EMPTY, 14 | var featurePreviewProviderTemplate: String = Constants.EMPTY, 15 | var moduleReadmeTemplate: String = Constants.EMPTY, 16 | var manifestTemplate: String = Constants.EMPTY, 17 | var gradleAndroidTemplate: String = Constants.EMPTY, 18 | var gradleKotlinTemplate: String = Constants.EMPTY, 19 | 20 | var isCompose: Boolean = true, 21 | var isHiltEnable: Boolean = true, 22 | ) -------------------------------------------------------------------------------- /src/main/kotlin/com/github/cnrture/quickprojectwizard/dialog/MessageDialogWrapper.kt: -------------------------------------------------------------------------------- 1 | package com.github.cnrture.quickprojectwizard.dialog 2 | 3 | import androidx.compose.foundation.layout.* 4 | import androidx.compose.runtime.Composable 5 | import androidx.compose.ui.Alignment 6 | import androidx.compose.ui.Modifier 7 | import androidx.compose.ui.text.TextStyle 8 | import androidx.compose.ui.text.font.FontWeight 9 | import androidx.compose.ui.text.style.TextAlign 10 | import androidx.compose.ui.unit.dp 11 | import androidx.compose.ui.unit.sp 12 | import com.github.cnrture.quickprojectwizard.common.Constants 13 | import com.github.cnrture.quickprojectwizard.components.QPWButton 14 | import com.github.cnrture.quickprojectwizard.components.QPWDialogWrapper 15 | import com.github.cnrture.quickprojectwizard.components.QPWText 16 | import com.github.cnrture.quickprojectwizard.theme.QPWTheme 17 | 18 | class MessageDialogWrapper(private val message: String) : QPWDialogWrapper() { 19 | 20 | @Composable 21 | override fun createDesign() { 22 | Column( 23 | modifier = Modifier.padding(vertical = 24.dp, horizontal = 64.dp), 24 | horizontalAlignment = Alignment.CenterHorizontally, 25 | verticalArrangement = Arrangement.Center, 26 | ) { 27 | QPWText( 28 | text = message, 29 | color = QPWTheme.colors.white, 30 | style = TextStyle( 31 | fontSize = 16.sp, 32 | fontWeight = FontWeight.SemiBold, 33 | textAlign = TextAlign.Center, 34 | ), 35 | ) 36 | Spacer(modifier = Modifier.size(24.dp)) 37 | QPWButton( 38 | text = "Okay", 39 | onClick = { close(Constants.DEFAULT_EXIT_CODE) }, 40 | backgroundColor = QPWTheme.colors.red, 41 | ) 42 | } 43 | } 44 | } -------------------------------------------------------------------------------- /src/main/kotlin/com/github/cnrture/quickprojectwizard/projectwizard/AndroidStudioTemplateProvider.kt: -------------------------------------------------------------------------------- 1 | package com.github.cnrture.quickprojectwizard.projectwizard 2 | 3 | import com.android.tools.idea.wizard.template.Template 4 | import com.android.tools.idea.wizard.template.WizardTemplateProvider 5 | 6 | class AndroidStudioTemplateProvider : WizardTemplateProvider() { 7 | override fun getTemplates(): List