From 8a557a7da48461249752dfe9bc7545e9a9706bdc Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 30 Jul 2025 04:41:59 +0000 Subject: [PATCH 1/3] Initial plan From 778af79eccc8e7ea2a7a7769722aae325d3c7348 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 30 Jul 2025 04:50:09 +0000 Subject: [PATCH 2/3] Complete Android project structure with JavaScript execution engine Co-authored-by: rushhiii <142855385+rushhiii@users.noreply.github.com> --- .gitignore | 38 ++++++ README.md | 112 ++++++++++++++++++ app/build.gradle | 58 +++++++++ app/proguard-rules.pro | 28 +++++ app/src/main/AndroidManifest.xml | 36 ++++++ .../com/scriptabledroid/app/MainActivity.kt | 105 ++++++++++++++++ .../java/com/scriptabledroid/app/Script.kt | 9 ++ .../com/scriptabledroid/app/ScriptAdapter.kt | 48 ++++++++ .../app/ScriptEditorActivity.kt | 111 +++++++++++++++++ .../com/scriptabledroid/app/ScriptEngine.kt | 73 ++++++++++++ .../com/scriptabledroid/app/ScriptStorage.kt | 88 ++++++++++++++ app/src/main/res/layout/activity_main.xml | 49 ++++++++ .../res/layout/activity_script_editor.xml | 82 +++++++++++++ app/src/main/res/layout/item_script.xml | 69 +++++++++++ app/src/main/res/mipmap-hdpi/ic_launcher.png | 0 .../res/mipmap-hdpi/ic_launcher_round.png | 0 app/src/main/res/mipmap-mdpi/ic_launcher.png | 0 .../res/mipmap-mdpi/ic_launcher_round.png | 0 app/src/main/res/mipmap-xhdpi/ic_launcher.png | 0 .../res/mipmap-xhdpi/ic_launcher_round.png | 0 .../main/res/mipmap-xxhdpi/ic_launcher.png | 0 .../res/mipmap-xxhdpi/ic_launcher_round.png | 0 .../main/res/mipmap-xxxhdpi/ic_launcher.png | 0 .../res/mipmap-xxxhdpi/ic_launcher_round.png | 0 app/src/main/res/values/colors.xml | 10 ++ app/src/main/res/values/strings.xml | 18 +++ app/src/main/res/values/themes.xml | 17 +++ app/src/main/res/xml/backup_rules.xml | 4 + .../main/res/xml/data_extraction_rules.xml | 9 ++ build.gradle | 5 + gradle/wrapper/gradle-wrapper.properties | 5 + settings.gradle | 17 +++ 32 files changed, 991 insertions(+) create mode 100644 .gitignore create mode 100644 app/build.gradle create mode 100644 app/proguard-rules.pro create mode 100644 app/src/main/AndroidManifest.xml create mode 100644 app/src/main/java/com/scriptabledroid/app/MainActivity.kt create mode 100644 app/src/main/java/com/scriptabledroid/app/Script.kt create mode 100644 app/src/main/java/com/scriptabledroid/app/ScriptAdapter.kt create mode 100644 app/src/main/java/com/scriptabledroid/app/ScriptEditorActivity.kt create mode 100644 app/src/main/java/com/scriptabledroid/app/ScriptEngine.kt create mode 100644 app/src/main/java/com/scriptabledroid/app/ScriptStorage.kt create mode 100644 app/src/main/res/layout/activity_main.xml create mode 100644 app/src/main/res/layout/activity_script_editor.xml create mode 100644 app/src/main/res/layout/item_script.xml create mode 100644 app/src/main/res/mipmap-hdpi/ic_launcher.png create mode 100644 app/src/main/res/mipmap-hdpi/ic_launcher_round.png create mode 100644 app/src/main/res/mipmap-mdpi/ic_launcher.png create mode 100644 app/src/main/res/mipmap-mdpi/ic_launcher_round.png create mode 100644 app/src/main/res/mipmap-xhdpi/ic_launcher.png create mode 100644 app/src/main/res/mipmap-xhdpi/ic_launcher_round.png create mode 100644 app/src/main/res/mipmap-xxhdpi/ic_launcher.png create mode 100644 app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png create mode 100644 app/src/main/res/mipmap-xxxhdpi/ic_launcher.png create mode 100644 app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png create mode 100644 app/src/main/res/values/colors.xml create mode 100644 app/src/main/res/values/strings.xml create mode 100644 app/src/main/res/values/themes.xml create mode 100644 app/src/main/res/xml/backup_rules.xml create mode 100644 app/src/main/res/xml/data_extraction_rules.xml create mode 100644 build.gradle create mode 100644 gradle/wrapper/gradle-wrapper.properties create mode 100644 settings.gradle diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a8a763c --- /dev/null +++ b/.gitignore @@ -0,0 +1,38 @@ +*.iml +.gradle +/local.properties +/.idea/caches +/.idea/libraries +/.idea/modules.xml +/.idea/workspace.xml +/.idea/navEditor.xml +/.idea/assetWizardSettings.xml +.DS_Store +/build +/captures +.externalNativeBuild +.cxx +local.properties + +# Android Studio +*.iml +.idea/ + +# Build +build/ +*/build/ + +# Local configuration file (sdk path, etc) +local.properties + +# Proguard folder generated by Eclipse +proguard/ + +# Log Files +*.log + +# Android Studio Navigation editor temp files +.navigation/ + +# Android Studio captures folder +captures/ \ No newline at end of file diff --git a/README.md b/README.md index 934f52f..cf97a2f 100644 --- a/README.md +++ b/README.md @@ -15,3 +15,115 @@ > * Power users looking for customizable widget logic > * Anyone who wishes Scriptable existed on Android +## Features + +- **JavaScript Script Editor**: Write and edit JavaScript code with syntax highlighting +- **Script Execution**: Run JavaScript scripts with access to Android APIs +- **Battery Information**: Access battery level and charging state through JavaScript +- **Script Management**: Save, edit, and organize your scripts +- **System Integration**: Built-in APIs for device information and notifications + +## Getting Started + +### Prerequisites + +- Android Studio Arctic Fox (2020.3.1) or later +- Android SDK API level 21 or higher +- Java 8 or higher + +### Building the Project + +1. Clone the repository: +```bash +git clone https://github.com/rushhiii/ScriptableDroid.git +cd ScriptableDroid +``` + +2. Open the project in Android Studio + +3. Sync the project with Gradle files + +4. Build and run the app on your device or emulator + +### Project Structure + +``` +ScriptableDroid/ +├── app/ +│ ├── src/main/ +│ │ ├── java/com/scriptabledroid/app/ +│ │ │ ├── MainActivity.kt # Main activity with script list +│ │ │ ├── ScriptEditorActivity.kt # Script editor with code highlighting +│ │ │ ├── ScriptEngine.kt # JavaScript execution engine +│ │ │ ├── ScriptStorage.kt # Script persistence +│ │ │ ├── ScriptAdapter.kt # RecyclerView adapter for scripts +│ │ │ └── Script.kt # Script data model +│ │ ├── res/ # Android resources +│ │ └── AndroidManifest.xml +│ └── build.gradle # App-level Gradle config +├── build.gradle # Project-level Gradle config +└── settings.gradle # Gradle settings +``` + +## JavaScript API + +ScriptableDroid provides several built-in JavaScript APIs: + +### Device API +```javascript +// Get battery level (0-100) +const batteryLevel = Device.batteryLevel(); + +// Get battery state ("charging", "discharging", "full", "not_charging", "unknown") +const batteryState = Device.batteryState(); +``` + +### Console API +```javascript +// Log messages +console.log("Hello from ScriptableDroid!"); +``` + +### Notification API (Coming Soon) +```javascript +// Create notifications +Notification.create("Title", "Message body"); +``` + +## Example Scripts + +### Battery Monitor +```javascript +// Get battery information +const batteryLevel = Device.batteryLevel(); +const batteryState = Device.batteryState(); + +console.log("Battery Level: " + batteryLevel + "%"); +console.log("Battery State: " + batteryState); + +"Battery: " + batteryLevel + "% (" + batteryState + ")"; +``` + +## Contributing + +1. Fork the repository +2. Create your feature branch (`git checkout -b feature/AmazingFeature`) +3. Commit your changes (`git commit -m 'Add some AmazingFeature'`) +4. Push to the branch (`git push origin feature/AmazingFeature`) +5. Open a Pull Request + +## License + +This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details. + +## Roadmap + +- [x] Basic JavaScript execution environment +- [x] Script editor with syntax highlighting +- [x] Device battery information API +- [ ] File system access +- [ ] Network requests support +- [ ] Home screen widgets +- [ ] More system APIs (contacts, calendar, etc.) +- [ ] Script sharing and import/export + diff --git a/app/build.gradle b/app/build.gradle new file mode 100644 index 0000000..7326232 --- /dev/null +++ b/app/build.gradle @@ -0,0 +1,58 @@ +plugins { + id 'com.android.application' + id 'org.jetbrains.kotlin.android' +} + +android { + namespace 'com.scriptabledroid.app' + compileSdk 34 + + defaultConfig { + applicationId "com.scriptabledroid.app" + minSdk 21 + targetSdk 34 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + + buildTypes { + release { + minifyEnabled 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 { + viewBinding true + } +} + +dependencies { + + implementation 'androidx.core:core-ktx:1.12.0' + implementation 'androidx.appcompat:appcompat:1.6.1' + implementation 'com.google.android.material:material:1.10.0' + implementation 'androidx.constraintlayout:constraintlayout:2.1.4' + + // Duktape for JavaScript execution + implementation 'com.squareup.duktape:duktape-android:1.4.0' + + // For code editor + implementation 'io.github.rosemoe.sora-editor:editor:0.23.4' + implementation 'io.github.rosemoe.sora-editor:language-javascript:0.23.4' + + // JSON serialization + implementation 'com.google.code.gson:gson:2.10.1' + + testImplementation 'junit:junit:4.13.2' + androidTestImplementation 'androidx.test.ext:junit:1.1.5' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1' +} \ No newline at end of file diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro new file mode 100644 index 0000000..6eb51ff --- /dev/null +++ b/app/proguard-rules.pro @@ -0,0 +1,28 @@ +# 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 + +# Keep Duktape classes +-keep class com.squareup.duktape.** { *; } + +# Keep Gson classes +-keep class com.google.gson.** { *; } +-keep class com.scriptabledroid.app.Script { *; } \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..bce4f33 --- /dev/null +++ b/app/src/main/AndroidManifest.xml @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/java/com/scriptabledroid/app/MainActivity.kt b/app/src/main/java/com/scriptabledroid/app/MainActivity.kt new file mode 100644 index 0000000..c4bbb7b --- /dev/null +++ b/app/src/main/java/com/scriptabledroid/app/MainActivity.kt @@ -0,0 +1,105 @@ +package com.scriptabledroid.app + +import android.content.Intent +import android.os.Bundle +import android.view.View +import androidx.appcompat.app.AppCompatActivity +import androidx.recyclerview.widget.LinearLayoutManager +import com.google.android.material.dialog.MaterialAlertDialogBuilder +import com.scriptabledroid.app.databinding.ActivityMainBinding + +class MainActivity : AppCompatActivity() { + + private lateinit var binding: ActivityMainBinding + private lateinit var scriptStorage: ScriptStorage + private lateinit var scriptEngine: ScriptEngine + private lateinit var scriptAdapter: ScriptAdapter + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + binding = ActivityMainBinding.inflate(layoutInflater) + setContentView(binding.root) + + scriptStorage = ScriptStorage(this) + scriptEngine = ScriptEngine(this) + + setupRecyclerView() + setupFab() + + // Create default scripts on first run + scriptStorage.createDefaultScripts() + loadScripts() + } + + private fun setupRecyclerView() { + scriptAdapter = ScriptAdapter( + scripts = emptyList(), + onRunClick = { script -> runScript(script) }, + onEditClick = { script -> editScript(script) }, + onDeleteClick = { script -> confirmDeleteScript(script) } + ) + + binding.recyclerViewScripts.apply { + layoutManager = LinearLayoutManager(this@MainActivity) + adapter = scriptAdapter + } + } + + private fun setupFab() { + binding.fabNewScript.setOnClickListener { + createNewScript() + } + } + + private fun loadScripts() { + val scripts = scriptStorage.getAllScripts() + scriptAdapter.updateScripts(scripts) + + binding.textViewEmpty.visibility = if (scripts.isEmpty()) View.VISIBLE else View.GONE + binding.recyclerViewScripts.visibility = if (scripts.isEmpty()) View.GONE else View.VISIBLE + } + + private fun runScript(script: Script) { + val result = scriptEngine.executeScript(script.content) + + MaterialAlertDialogBuilder(this) + .setTitle("Script Output: ${script.name}") + .setMessage(result) + .setPositiveButton("OK", null) + .show() + } + + private fun editScript(script: Script) { + val intent = Intent(this, ScriptEditorActivity::class.java).apply { + putExtra("script_id", script.id) + } + startActivity(intent) + } + + private fun createNewScript() { + val intent = Intent(this, ScriptEditorActivity::class.java) + startActivity(intent) + } + + private fun confirmDeleteScript(script: Script) { + MaterialAlertDialogBuilder(this) + .setTitle("Delete Script") + .setMessage("Are you sure you want to delete '${script.name}'?") + .setPositiveButton("Delete") { _, _ -> + scriptStorage.deleteScript(script.id) + loadScripts() + } + .setNegativeButton("Cancel", null) + .show() + } + + override fun onResume() { + super.onResume() + loadScripts() + } + + override fun onDestroy() { + super.onDestroy() + scriptEngine.close() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/scriptabledroid/app/Script.kt b/app/src/main/java/com/scriptabledroid/app/Script.kt new file mode 100644 index 0000000..b6f51ba --- /dev/null +++ b/app/src/main/java/com/scriptabledroid/app/Script.kt @@ -0,0 +1,9 @@ +package com.scriptabledroid.app + +data class Script( + val id: String, + val name: String, + val content: String, + val description: String = "", + val lastModified: Long = System.currentTimeMillis() +) \ No newline at end of file diff --git a/app/src/main/java/com/scriptabledroid/app/ScriptAdapter.kt b/app/src/main/java/com/scriptabledroid/app/ScriptAdapter.kt new file mode 100644 index 0000000..4fb18c1 --- /dev/null +++ b/app/src/main/java/com/scriptabledroid/app/ScriptAdapter.kt @@ -0,0 +1,48 @@ +package com.scriptabledroid.app + +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView +import com.scriptabledroid.app.databinding.ItemScriptBinding + +class ScriptAdapter( + private var scripts: List