Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ See the official [plugin documentation](https://www.appdevforall.org/codeonthego
| [`markdown-preview/`](markdown-preview/) | Renders Markdown files with a live preview pane in the editor. |
| [`keystore-generator/`](keystore-generator/) | Generates signing keystores from inside the IDE. |
| [`snippets/`](snippets/) | Adds user-managed code snippets with prefix-triggered expansions. |
| [`random-xkcd/`](random-xkcd/) | Random xkcd comic in the editor bottom sheet; canonical small-plugin walkthrough with in-IDE help. |

## Building a plugin

Expand Down
Binary file modified libs/plugin-api.jar
Binary file not shown.
29 changes: 29 additions & 0 deletions random-xkcd/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Gradle
.gradle/
build/
gradle-app.setting
!gradle-wrapper.jar
.gradletasknamecache

# IDE
.idea/
*.iml
*.ipr
*.iws
.project
.classpath
.settings/
.kotlin/

# Local configuration
local.properties

# OS
.DS_Store
Thumbs.db

# Logs
*.log

# Test outputs
test-results/
90 changes: 90 additions & 0 deletions random-xkcd/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
# random-xkcd

A small Code on the Go plugin that shows a random xkcd comic in the
editor bottom sheet. Tap for a new comic, double-tap to copy the URL,
triple-tap to copy the image. Long-press the tab for in-IDE help.

Designed as a canonical "this is what a small CoGo plugin looks like"
example. Under 300 lines of Kotlin, every plugin-specific concept
called out where it shows up in the code.

## The tutorial

The full walkthrough lives in `src/main/assets/docs/index.html` —
the **Tier 3 docs page** served by the host IDE at
`http://localhost:6174/plugin/com.codeonthego.xkcdrandom/index.html`
once the plugin is installed.

To read it:

- **Inside CoGo** (the canonical path) — long-press the **XKCD** tab in
the editor bottom sheet → tap **"See More"** → tap **"Code
walkthrough"**. The IDE opens the page in an in-IDE WebView.
- **Outside CoGo** — open `src/main/assets/docs/index.html` directly
in any browser. Renders identically.

The tutorial covers the plugin in 7 steps:

1. Plugin entry point (`IPlugin` lifecycle)
2. Manifest + permissions
3. Bottom-sheet tab UI (`UIExtension`)
4. Tap interactions (single / double / triple)
5. Network fetch over HTTPS
6. Clipboard support (text + image via host `FileProvider`)
7. Three-tier tooltip help (`DocumentationExtension`)

## Build

```bash
./gradlew assemblePlugin
```

Produces `build/plugin/random-xkcd.cgp` — the bundle you sideload
into Code on the Go via **Preferences → Plugin Manager → +**.

## Source layout

```
random-xkcd/
├── build.gradle.kts
└── src/main/
├── AndroidManifest.xml
├── assets/
│ ├── docs/ ← Tier 3 walkthrough (the tutorial)
│ ├── icon_day.png ← Plugin Manager icon, light theme
│ └── icon_night.png ← Plugin Manager icon, dark theme
├── kotlin/com/codeonthego/xkcdrandom/
│ ├── XkcdRandomPlugin.kt ← lifecycle + tab + tooltip registration
│ ├── fragments/XkcdPanelFragment.kt
│ ├── net/XkcdApiClient.kt ← HTTP, two endpoints, no auth
│ ├── net/XkcdComic.kt
│ └── ui/TapCountClassifier.kt ← 1/2/3 tap state machine
└── res/
├── layout/fragment_xkcd_panel.xml
└── values/, values-night/
```

Plus unit tests under `src/test/` for the tap classifier
(JUnit 4 + Truth, no Robolectric).

## Run tests

```bash
./gradlew testDebugUnitTest
```

## xkcd attribution + license

xkcd comics are © Randall Munroe and licensed **CC BY-NC 2.5**
(https://xkcd.com/license.html). This plugin:

- Fetches comics over HTTPS from xkcd.com (no caching, no redistribution
beyond what the user explicitly copies to their own clipboard).
- Displays an attribution line — *"Comics © Randall Munroe · xkcd.com ·
CC BY-NC 2.5"* — beneath every comic in the bottom-sheet panel.
- Is itself non-commercial (open-source demo plugin for an
open-source IDE), consistent with the NC term.

The plugin's own source code is licensed per the surrounding
`plugin-examples` repository (see `LICENSE` at the repo root). xkcd's
license applies only to the comic content the plugin displays.
93 changes: 93 additions & 0 deletions random-xkcd/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import org.jetbrains.kotlin.gradle.dsl.JvmTarget

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

pluginBuilder {
pluginName = "random-xkcd"
}

android {
namespace = "com.codeonthego.xkcdrandom"
compileSdk = 34

defaultConfig {
applicationId = "com.codeonthego.xkcdrandom"
minSdk = 26
targetSdk = 34
versionCode = 1
versionName = "1.0.0"
}

buildTypes {
release {
isMinifyEnabled = false
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
}
}

compileOptions {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}

packaging {
resources {
excludes += setOf(
"META-INF/versions/9/OSGI-INF/MANIFEST.MF",
"META-INF/DEPENDENCIES",
"META-INF/LICENSE",
"META-INF/LICENSE.txt",
"META-INF/NOTICE",
"META-INF/NOTICE.txt"
)
}
}

testOptions {
unitTests.isReturnDefaultValues = true
}
}

kotlin {
compilerOptions {
jvmTarget.set(JvmTarget.JVM_17)
}
}

dependencies {
// The plugin-api jar is the canonical contract for plugins. Available
// at compile time; the IDE provides it at runtime.
compileOnly(files("../libs/plugin-api.jar"))

implementation("androidx.appcompat:appcompat:1.6.1")
implementation("com.google.android.material:material:1.10.0")
implementation("androidx.fragment:fragment-ktx:1.8.8")
implementation("androidx.core:core-ktx:1.13.1")
implementation("org.jetbrains.kotlin:kotlin-stdlib:2.3.0")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.8.1")

// OkHttp for the xkcd JSON endpoint + image fetch.
// Kept tiny and dependency-free — no Glide/Retrofit, since this plugin is a
// teaching example and we want the network layer to read top-to-bottom.
implementation("com.squareup.okhttp3:okhttp:4.12.0")

testImplementation("junit:junit:4.13.2")
}

tasks.wrapper {
gradleVersion = "8.14.3"
distributionType = Wrapper.DistributionType.BIN
}

// Disable AAR metadata checks that fail under the plugin-builder
// pipeline (Beepy + Forms use the same workaround).
tasks.matching {
it.name.contains("checkDebugAarMetadata") ||
it.name.contains("checkReleaseAarMetadata")
}.configureEach {
enabled = false
}
3 changes: 3 additions & 0 deletions random-xkcd/gradle.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
android.useAndroidX=true
android.nonTransitiveRClass=true
kotlin.code.style=official
Binary file added random-xkcd/gradle/wrapper/gradle-wrapper.jar
Binary file not shown.
7 changes: 7 additions & 0 deletions random-xkcd/gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
Loading