Skip to content

Commit

Permalink
✨ feat: Add android node
Browse files Browse the repository at this point in the history
  • Loading branch information
caoccao committed Mar 17, 2024
1 parent 2231c10 commit 76a788f
Show file tree
Hide file tree
Showing 46 changed files with 1,567 additions and 8 deletions.
14 changes: 10 additions & 4 deletions .github/workflows/android_build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,13 @@ on:

jobs:
build_apk:
name: Build APK
strategy:
matrix:
include:
- mode: node
- mode: v8

name: Build APK for ${{ matrix.mode }}
runs-on: ubuntu-latest

steps:
Expand All @@ -41,11 +47,11 @@ jobs:

- name: Build the Artifact
run: |
cd android_v8
cd android_${{ matrix.mode }}
gradle build --debug
- name: Upload the Artifact
uses: actions/upload-artifact@v4
with:
name: javet-shell-android-v8
path: android_v8/app/build/outputs/apk/release/*.apk
name: javet-shell-android-${{ matrix.mode }}
path: android_${{ matrix.mode }}/app/build/outputs/apk/release/*.apk
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ Javet Shell is a console or Android application that provides Node.js flavored c

## Features

- [Android](android)
- [Android (Node)](android_node) or [Android (V8)](android_v8)
- [Linux + MacOS + Windows](console)
- Complete access to JVM
- Debug with Chrome Dev Tools
Expand All @@ -18,7 +18,7 @@ Javet Shell is a console or Android application that provides Node.js flavored c
- Download the apk file from the latest [action](https://github.com/caoccao/JavetShell/actions/workflows/android_build.yml).
- Install the apk file.

Please refer to [Android (V8)](android_v8) for details.
Please refer to [Android (Node)](android_node) or [Android (V8)](android_v8) for details.

### Console

Expand Down
10 changes: 10 additions & 0 deletions android_node/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
*.iml
/.gradle
/local.properties
/.idea
/build
/captures
/.externalNativeBuild
/.cxx

.DS_Store
6 changes: 6 additions & 0 deletions android_node/.idea/copyright/Apache_License_2_0.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions android_node/.idea/copyright/profiles_settings.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions android_node/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Javet Shell Android (Node)

Javet Shell Android (Node) is an Android application that provides a Node.js flavored console interactions.
2 changes: 2 additions & 0 deletions android_node/app/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
/build
/libs
118 changes: 118 additions & 0 deletions android_node/app/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
/*
* Copyright (c) 2023-2024. caoccao.com Sam Cao
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

plugins {
id("com.android.application")
id("org.jetbrains.kotlin.android")
}

object Config {
object Projects {
// https://mvnrepository.com/artifact/net.bytebuddy/byte-buddy
const val BYTE_BUDDY = "net.bytebuddy:byte-buddy:${Versions.BYTE_BUDDY}"

const val JAVENODE = "com.caoccao.javet:javenode:${Versions.JAVENODE}"
const val JAVET__GROUP = "com.caoccao.javet"
const val JAVET__MODULE = "javet"
const val JAVET_ANDROID = "com.caoccao.javet:javet-android-node:${Versions.JAVET}"

// https://mvnrepository.com/artifact/io.vertx/vertx-core
const val VERTX = "io.vertx:vertx-core:${Versions.VERTX}"
}

object Versions {
const val BYTE_BUDDY = "1.14.10"
const val JAVENODE = "0.6.0"
const val JAVET = "3.1.0"
const val JETTY_WEBSOCKET = "9.4.53.v20231009"
const val VERTX = "4.5.0"
}
}

android {
namespace = "com.caoccao.javet.shell"
compileSdk = 34

defaultConfig {
applicationId = "com.caoccao.javet.shell"
minSdk = 24
targetSdk = 34
versionCode = 1
versionName = "0.1.0"
setProperty("archivesBaseName", "javet-shell-node-${versionName}")

testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables {
useSupportLibrary = true
}
}

buildTypes {
release {
isMinifyEnabled = false
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = "1.8"
}
buildFeatures {
compose = true
}
composeOptions {
kotlinCompilerExtensionVersion = "1.5.1"
}
packaging {
resources {
excludes += "/META-INF/{AL2.0,LGPL2.1}"
excludes += "META-INF/INDEX.LIST"
excludes += "META-INF/io.netty.versions.properties"
}
}
}

dependencies {

implementation("androidx.core:core-ktx:1.12.0")
implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.7.0")
implementation("androidx.activity:activity-compose:1.8.2")
implementation(platform("androidx.compose:compose-bom:2024.02.02"))
implementation("androidx.compose.ui:ui")
implementation("androidx.compose.ui:ui-graphics")
implementation("androidx.compose.ui:ui-tooling-preview")
implementation("androidx.compose.material3:material3")
implementation(Config.Projects.BYTE_BUDDY)
implementation(Config.Projects.JAVENODE) {
exclude(group = Config.Projects.JAVET__GROUP, module = Config.Projects.JAVET__MODULE)
}
implementation(Config.Projects.JAVET_ANDROID)
implementation(Config.Projects.VERTX)
// implementation(fileTree(mapOf("dir" to "libs", "include" to listOf("*.aar"))))
testImplementation("junit:junit:4.13.2")
androidTestImplementation("androidx.test.ext:junit:1.1.5")
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
androidTestImplementation(platform("androidx.compose:compose-bom:2024.02.02"))
androidTestImplementation("androidx.compose.ui:ui-test-junit4")
debugImplementation("androidx.compose.ui:ui-tooling")
debugImplementation("androidx.compose.ui:ui-test-manifest")
}
21 changes: 21 additions & 0 deletions android_node/app/proguard-rules.pro
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html

# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}

# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable

# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
/*
* Copyright (c) 2023-2024. caoccao.com Sam Cao
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.caoccao.javet.shell

import androidx.compose.ui.test.assertTextEquals
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onNodeWithTag
import androidx.compose.ui.test.performClick
import androidx.compose.ui.test.performTextInput
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry
import com.caoccao.javet.interop.V8Host
import com.caoccao.javet.interop.V8Runtime
import com.caoccao.javet.shell.ui.theme.JavetShellTheme
import org.junit.Assert.*
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith

/**
* Instrumented test, which will execute on an Android device.
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
@RunWith(AndroidJUnit4::class)
class TestMainActivity {
@get:Rule
val composeRule = createComposeRule()

@Test
fun testAppContext() {
// Context of the app under test.
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
assertEquals("com.caoccao.javet.shell", appContext.packageName)
}

@Test
fun testButtonExecute() {
V8Host.getV8Instance().createV8Runtime<V8Runtime>().use { v8Runtime ->
v8Runtime.logger = ConsoleLogger()
composeRule.setContent {
JavetShellTheme {
HomeScreen(v8Runtime = v8Runtime, stringBuilder = StringBuilder())
}
}
val nodeBasicTextField = composeRule.onNodeWithTag("basicTextFieldCodeString")
val nodeElevatedButtonExecute = composeRule.onNodeWithTag("elevatedButtonExecute")
val nodeTextResult = composeRule.onNodeWithTag("textResult")
nodeBasicTextField.performTextInput("1 + 1")
nodeElevatedButtonExecute.performClick()
nodeTextResult.assertTextEquals("\n> 1 + 1\n2")
}
}

@Test
fun testBasicTextFieldExecute() {
V8Host.getV8Instance().createV8Runtime<V8Runtime>().use { v8Runtime ->
v8Runtime.logger = ConsoleLogger()
composeRule.setContent {
JavetShellTheme {
HomeScreen(v8Runtime = v8Runtime, stringBuilder = StringBuilder())
}
}
val nodeBasicTextField = composeRule.onNodeWithTag("basicTextFieldCodeString")
val nodeTextResult = composeRule.onNodeWithTag("textResult")
nodeBasicTextField.performTextInput("1 + 1\n")
nodeBasicTextField.performTextInput("\n")
nodeTextResult.assertTextEquals("\n> 1 + 1\n2")
}
}

@Test
fun testIconButtonRefresh() {
V8Host.getV8Instance().createV8Runtime<V8Runtime>().use { v8Runtime ->
v8Runtime.logger = ConsoleLogger()
composeRule.setContent {
JavetShellTheme {
HomeScreen(v8Runtime = v8Runtime, stringBuilder = StringBuilder())
}
}
val nodeIconButtonRefresh = composeRule.onNodeWithTag("iconButtonRefresh")
val nodeElevatedButtonExecute = composeRule.onNodeWithTag("elevatedButtonExecute")
val nodeBasicTextField = composeRule.onNodeWithTag("basicTextFieldCodeString")
val nodeTextResult = composeRule.onNodeWithTag("textResult")
nodeBasicTextField.performTextInput("const a = 1\n")
nodeElevatedButtonExecute.performClick()
nodeTextResult.assertTextEquals("\n> const a = 1\nundefined")
nodeBasicTextField.performTextInput("const a = 1\n")
nodeElevatedButtonExecute.performClick()
nodeTextResult.assertTextEquals(
"\n> const a = 1\nundefined" +
"\n> const a = 1\nSyntaxError: Identifier 'a' has already been declared"
)
nodeIconButtonRefresh.performClick()
nodeBasicTextField.performTextInput("const a = 1\n")
nodeElevatedButtonExecute.performClick()
nodeTextResult.assertTextEquals("V8 context is refreshed.\n> const a = 1\nundefined")
}
}
}
28 changes: 28 additions & 0 deletions android_node/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">

<application
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.JavetShell"
tools:targetApi="31">
<activity
android:name=".MainActivity"
android:exported="true"
android:label="@string/app_name"
android:theme="@style/Theme.JavetShell">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>

</manifest>
Loading

0 comments on commit 76a788f

Please sign in to comment.