From 3c1a625f858c78d60f62b4837754871643fb4cae Mon Sep 17 00:00:00 2001 From: Fabio Martino Date: Wed, 25 Dec 2024 19:34:35 +0100 Subject: [PATCH 1/2] (feat) Add Kotlin/Java selectable choice --- README.md | 1 + assets/plugin-template/README.md.mustache | 13 +++++ .../android/build.gradle.mustache | 15 ++++++ .../src/main/kotlin/__JAVA_PATH__.kt.mustache | 12 +++++ .../kotlin/__JAVA_PATH__Plugin.kt.mustache | 23 +++++++++ src/help.ts | 1 + src/options.ts | 3 ++ src/prompt.ts | 9 ++++ src/template.ts | 51 ++++++++++++++++--- 9 files changed, 120 insertions(+), 8 deletions(-) create mode 100644 assets/plugin-template/android/src/main/kotlin/__JAVA_PATH__.kt.mustache create mode 100644 assets/plugin-template/android/src/main/kotlin/__JAVA_PATH__Plugin.kt.mustache diff --git a/README.md b/README.md index f055a05..1c966c8 100644 --- a/README.md +++ b/README.md @@ -30,4 +30,5 @@ As of the `0.8.0` release, example apps for testing are included when initializi --author ......... Author name and email (e.g. "Name ") --license ............ SPDX License ID (e.g. "MIT") --description ...... Short description of plugin features +--android-lang ..... Language for Android plugin development (e.g. "kotlin" or "java") ``` diff --git a/assets/plugin-template/README.md.mustache b/assets/plugin-template/README.md.mustache index 837a83f..eab36d9 100644 --- a/assets/plugin-template/README.md.mustache +++ b/assets/plugin-template/README.md.mustache @@ -4,8 +4,21 @@ ## Install +To use npm + ```bash npm install {{{ PACKAGE_NAME }}} +```` + +To use yarn + +```bash +yarn add {{{ PACKAGE_NAME }}} +``` + +Sync native files + +```bash npx cap sync ``` diff --git a/assets/plugin-template/android/build.gradle.mustache b/assets/plugin-template/android/build.gradle.mustache index 350a9bb..f84c20f 100644 --- a/assets/plugin-template/android/build.gradle.mustache +++ b/assets/plugin-template/android/build.gradle.mustache @@ -3,19 +3,31 @@ ext { androidxAppCompatVersion = project.hasProperty('androidxAppCompatVersion') ? rootProject.ext.androidxAppCompatVersion : '1.7.0' androidxJunitVersion = project.hasProperty('androidxJunitVersion') ? rootProject.ext.androidxJunitVersion : '1.2.1' androidxEspressoCoreVersion = project.hasProperty('androidxEspressoCoreVersion') ? rootProject.ext.androidxEspressoCoreVersion : '3.6.1' +{{ #KOTLIN }} + androidxCoreKTXVersion = project.hasProperty('androidxCoreKTXVersion') ? rootProject.ext.androidxCoreKTXVersion : '1.15.0' +{{ /KOTLIN }} } buildscript { +{{ #KOTLIN }} + ext.kotlin_version = project.hasProperty("kotlin_version") ? rootProject.ext.kotlin_version : '1.9.25' +{{ /KOTLIN }} repositories { google() mavenCentral() } dependencies { classpath 'com.android.tools.build:gradle:8.7.2' +{{ #KOTLIN }} + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" +{{ /KOTLIN }} } } apply plugin: 'com.android.library' +{{ #KOTLIN }} +apply plugin: 'kotlin-android' +{{ /KOTLIN }} android { namespace "{{ PACKAGE_ID }}" @@ -52,6 +64,9 @@ dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) implementation project(':capacitor-android') implementation "androidx.appcompat:appcompat:$androidxAppCompatVersion" +{{ #KOTLIN }} + implementation "androidx.core:core-ktx:$androidxCoreKTXVersion" +{{ /KOTLIN }} testImplementation "junit:junit:$junitVersion" androidTestImplementation "androidx.test.ext:junit:$androidxJunitVersion" androidTestImplementation "androidx.test.espresso:espresso-core:$androidxEspressoCoreVersion" diff --git a/assets/plugin-template/android/src/main/kotlin/__JAVA_PATH__.kt.mustache b/assets/plugin-template/android/src/main/kotlin/__JAVA_PATH__.kt.mustache new file mode 100644 index 0000000..341ff69 --- /dev/null +++ b/assets/plugin-template/android/src/main/kotlin/__JAVA_PATH__.kt.mustache @@ -0,0 +1,12 @@ +package {{ PACKAGE_ID }} + +import android.util.Log + +class {{ CLASS }} { + + fun echo(value: String?): String? { + Log.i("Echo", value ?: "null") + + return value + } +} diff --git a/assets/plugin-template/android/src/main/kotlin/__JAVA_PATH__Plugin.kt.mustache b/assets/plugin-template/android/src/main/kotlin/__JAVA_PATH__Plugin.kt.mustache new file mode 100644 index 0000000..52ee14c --- /dev/null +++ b/assets/plugin-template/android/src/main/kotlin/__JAVA_PATH__Plugin.kt.mustache @@ -0,0 +1,23 @@ +package {{ PACKAGE_ID }} + +import com.getcapacitor.JSObject +import com.getcapacitor.Plugin +import com.getcapacitor.PluginCall +import com.getcapacitor.annotation.CapacitorPlugin +import com.getcapacitor.PluginMethod + +@CapacitorPlugin(name = "{{ CLASS }}") +class {{ CLASS }}Plugin : Plugin() { + + private val implementation = {{ CLASS }}() + + @PluginMethod + fun echo(call: PluginCall) { + val value = call.getString("value") + + val ret = JSObject().apply { + put("value", implementation.echo(value)) + } + call.resolve(ret) + } +} diff --git a/src/help.ts b/src/help.ts index db53697..dc0c53a 100644 --- a/src/help.ts +++ b/src/help.ts @@ -10,6 +10,7 @@ const help = ` --author ......... Author name and email (e.g. "Name ") --license ............ SPDX License ID (e.g. "MIT") --description ...... Short description of plugin features + --android-lang ............ Language for Android plugin development (e.g. "kotlin" or "java") -h, --help ................ Print help, then quit --verbose ................. Print verbose output to stderr diff --git a/src/options.ts b/src/options.ts index cc852bc..c56baec 100644 --- a/src/options.ts +++ b/src/options.ts @@ -17,6 +17,7 @@ export interface OptionValues { author: string; license: string; description: string; + 'android-lang': string; } export type Validators = { @@ -57,6 +58,8 @@ export const VALIDATORS: Validators = { typeof value !== 'string' || value.trim().length === 0 ? `Must provide a valid license, e.g. "MIT"` : true, description: (value) => typeof value !== 'string' || value.trim().length === 0 ? `Must provide a description` : true, + 'android-lang': (value) => + typeof value !== 'string' || value.trim().length === 0 ? `Must provide a language, e.g. "kotlin"` : true, dir: (value) => typeof value !== 'string' || value.trim().length === 0 ? `Must provide a directory, e.g. "my-plugin"` diff --git a/src/prompt.ts b/src/prompt.ts index 8b49e86..57d205e 100644 --- a/src/prompt.ts +++ b/src/prompt.ts @@ -81,6 +81,15 @@ export const gatherDetails = (initialOptions: Options): Promise => message: `Enter a SPDX license identifier for your plugin.\n`, validate: VALIDATORS.license, }, + { + type: 'select', + name: 'android-lang', + message: `What language would you like to use for your Android plugin?\n`, + choices: [ + { title: 'Kotlin', value: 'kotlin' }, + { title: 'Java', value: 'java' } + ] + }, { type: 'text', name: 'description', diff --git a/src/template.ts b/src/template.ts index e675cb7..bfb6930 100644 --- a/src/template.ts +++ b/src/template.ts @@ -1,4 +1,4 @@ -import { readFile, rmdir, mkdir, writeFile, unlink } from 'fs/promises'; +import { readFile, rmdir, mkdir, writeFile, unlink, readdir, rm } from 'fs/promises'; import Mustache from 'mustache'; import { dirname, join, resolve, sep } from 'path'; import tar from 'tar'; @@ -25,6 +25,7 @@ export const extractTemplate = async ( ): Promise => { const templateFiles: string[] = []; const templateFolders: string[] = []; + const androidLang = details['android-lang'].toLowerCase(); await mkdir(dir, { recursive: true }); await tar.extract({ file: type === 'PLUGIN_TEMPLATE' ? TEMPLATE_PATH : WWW_TEMPLATE_PATH, @@ -41,14 +42,44 @@ export const extractTemplate = async ( }); await Promise.all(templateFiles.map((p) => resolve(dir, p)).map((p) => applyTemplate(p, details))); - await Promise.all(templateFolders.map((p) => resolve(dir, p)).map((p) => rmdir(p))); + await deleteUnnecessaryFolders(dir, androidLang); + await Promise.all(templateFolders.map((p) => resolve(dir, p)).map((p) => rm(p, { recursive: true }))); +}; + +const deleteUnnecessaryFolders = async (dir: string, androidLang: string): Promise => { + const androidFolder = join(dir, 'android', 'src', 'main'); + const javaFolder = join(androidFolder, 'java'); + const kotlinFolder = join(androidFolder, 'kotlin'); + + if (androidLang === 'kotlin' && await folderExists(javaFolder)) { + await rm(javaFolder, { recursive: true }); + } + + if (androidLang === 'java' && await folderExists(kotlinFolder)) { + await rm(kotlinFolder, { recursive: true }); + } +}; + +const folderExists = async (folderPath: string): Promise => { + try { + const files = await readdir(folderPath); + return files != null; + } catch (err) { + return false; + } }; export const applyTemplate = async ( p: string, - { name, 'package-id': packageId, 'class-name': className, repo, author, license, description }: OptionValues, + { name, 'package-id': packageId, 'class-name': className, repo, author, license, description, 'android-lang': androidLang }: OptionValues, ): Promise => { const template = await readFile(p, { encoding: 'utf8' }); + + const conditionalView = { + KOTLIN: androidLang.toLowerCase() === 'kotlin', // Set KOTLIN flag + // Add more flags... + } + const view = { CAPACITOR_VERSION: CAPACITOR_VERSION, PACKAGE_NAME: name, @@ -60,17 +91,21 @@ export const applyTemplate = async ( AUTHOR: author, LICENSE: license, DESCRIPTION: description, + ANDROID_LANG: androidLang, }; - const contents = Mustache.render(template, view); - const filePath = Object.entries(view).reduce( - (acc, [key, value]) => (value ? acc.replaceAll(`__${key}__`, value) : acc), - p.substring(0, p.length - MUSTACHE_EXTENSION.length), + const combinedView = { ...view, ...conditionalView }; + const intermediateContents = Mustache.render(template, combinedView); + const finalContents = Mustache.render(intermediateContents, view); + let filePath = p.substring(0, p.length - MUSTACHE_EXTENSION.length); + filePath = Object.entries(view).reduce( + (acc, [key, value]) => (value ? acc.replaceAll(`__${key}__`, value.toString()) : acc), + filePath, ); await mkdir(dirname(filePath), { recursive: true }); // take off the .mustache extension and write the file, then remove the template - await writeFile(filePath, contents, { encoding: 'utf8' }); + await writeFile(filePath, finalContents, { encoding: 'utf8' }); await unlink(p); }; From 86995f4c119ba047d21c943d886feac8326fcb0f Mon Sep 17 00:00:00 2001 From: Fabio Martino Date: Wed, 14 May 2025 23:43:46 +0200 Subject: [PATCH 2/2] Updated dependencies --- assets/plugin-template/package.json.mustache | 10 +++++----- src/template.ts | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/assets/plugin-template/package.json.mustache b/assets/plugin-template/package.json.mustache index 36ac2c5..dc7d5df 100644 --- a/assets/plugin-template/package.json.mustache +++ b/assets/plugin-template/package.json.mustache @@ -55,13 +55,13 @@ "@ionic/eslint-config": "^0.4.0", "@ionic/prettier-config": "^4.0.0", "@ionic/swiftlint-config": "^2.0.0", - "eslint": "^8.57.0", - "prettier": "^3.4.2", - "prettier-plugin-java": "^2.6.6", + "eslint": "^9.26.0", + "prettier": "^3.5.3", + "prettier-plugin-java": "^2.6.7", "rimraf": "^6.0.1", - "rollup": "^4.30.1", + "rollup": "^4.40.2", "swiftlint": "^2.0.0", - "typescript": "~4.1.5" + "typescript": "~5.4.5" }, "peerDependencies": { "@capacitor/core": ">={{{ CAPACITOR_VERSION }}}" diff --git a/src/template.ts b/src/template.ts index 981357d..1e98490 100644 --- a/src/template.ts +++ b/src/template.ts @@ -7,7 +7,7 @@ import type { OptionValues } from './options'; const MUSTACHE_EXTENSION = '.mustache'; -export const CAPACITOR_VERSION = '7.0.0'; +export const CAPACITOR_VERSION = '7.2.0'; const TEMPLATE_PATH = resolve(__dirname, '..', 'assets', 'plugin-template.tar.gz');