Skip to content

Commit f84d2ee

Browse files
Merge pull request #24 from appdevforall/ADFA-4247-bookshelf-plugin
ADFA-4247 add bookshelf plugin
2 parents a027015 + 742989f commit f84d2ee

21 files changed

Lines changed: 944 additions & 0 deletions

.github/workflows/update-libs.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ jobs:
6060
declare -A MAP=(
6161
["apk-viewer"]="apk-analyzer.cgp"
6262
["Beepy"]="beepy.cgp"
63+
["bookshelf"]="bookshelf.cgp"
6364
["keystore-generator"]="keystore-generator.cgp"
6465
["markdown-preview"]="markdown-previewer.cgp"
6566
["ndk-installer-plugin"]="ndk-installer.cgp"

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ See the official [plugin documentation](https://www.appdevforall.org/codeonthego
1010
| -------------------------------------------------- | ----------------------------------------------------------------- |
1111
| [`Beepy/`](Beepy/) | Plays a sound when a build starts, succeeds, or fails. |
1212
| [`apk-viewer/`](apk-viewer/) | Inspects an APK's contents and surfaces a structural breakdown. |
13+
| [`bookshelf/`](bookshelf/) | Adds reference textbooks (C, C++, Java, Kotlin, Android) accessible from CoGo's in-IDE Bookshelf help page. |
1314
| [`markdown-preview/`](markdown-preview/) | Renders Markdown files with a live preview pane in the editor. |
1415
| [`keystore-generator/`](keystore-generator/) | Generates signing keystores from inside the IDE. |
1516
| [`snippets/`](snippets/) | Adds user-managed code snippets with prefix-triggered expansions. |

bookshelf/.gitignore

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# Gradle
2+
.gradle/
3+
build/
4+
gradle-app.setting
5+
!gradle-wrapper.jar
6+
.gradletasknamecache
7+
8+
# IDE
9+
.idea/
10+
*.iml
11+
*.ipr
12+
*.iws
13+
.project
14+
.classpath
15+
.settings/
16+
.kotlin/
17+
18+
# Local configuration
19+
local.properties
20+
21+
# OS
22+
.DS_Store
23+
Thumbs.db
24+
25+
# Logs
26+
*.log
27+
28+
# Test outputs
29+
test-results/

bookshelf/bookshelf.html

Lines changed: 223 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,223 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8">
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
6+
<title>Bookshelf Plugin Documentation</title>
7+
<style>
8+
:root {
9+
--bg: #ffffff;
10+
--fg: #000000;
11+
--accent: #8b4513;
12+
--accent-light: #f6ece1;
13+
--border: #d0d7de;
14+
--code-bg: #f4f6f8;
15+
--table-stripe: #f8fafb;
16+
}
17+
* { margin: 0; padding: 0; box-sizing: border-box; }
18+
body {
19+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
20+
color: var(--fg);
21+
background: var(--bg);
22+
line-height: 1.6;
23+
max-width: 52rem;
24+
margin: 0 auto;
25+
padding: 2rem 1.5rem 4rem;
26+
}
27+
header { border-bottom: 3px solid var(--accent); padding-bottom: 1.2rem; margin-bottom: 2rem; }
28+
header h1 { font-size: 2rem; color: var(--accent); }
29+
header p.subtitle { color: #555; margin-top: 0.3rem; }
30+
header .meta { font-size: 0.85rem; color: #777; margin-top: 0.5rem; }
31+
nav { background: var(--accent-light); border: 1px solid var(--border); border-radius: 6px; padding: 1rem 1.5rem; margin-bottom: 2.5rem; }
32+
nav h2 { font-size: 0.95rem; text-transform: uppercase; letter-spacing: 0.05em; color: var(--accent); margin-bottom: 0.5rem; }
33+
nav ol { padding-left: 1.3rem; }
34+
nav li { margin: 0.25rem 0; }
35+
nav a { color: var(--accent); text-decoration: none; }
36+
nav a:hover { text-decoration: underline; }
37+
section { margin-bottom: 2.5rem; }
38+
h2 { font-size: 1.4rem; color: var(--accent); border-bottom: 1px solid var(--border); padding-bottom: 0.3rem; margin-bottom: 1rem; }
39+
h3 { font-size: 1.1rem; margin: 1.2rem 0 0.5rem; }
40+
p, li { margin-bottom: 0.5rem; }
41+
ul, ol { padding-left: 1.4rem; }
42+
code {
43+
font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, monospace;
44+
background: var(--code-bg);
45+
padding: 0.15em 0.35em;
46+
border-radius: 3px;
47+
font-size: 0.9em;
48+
}
49+
table { width: 100%; border-collapse: collapse; margin: 0.8rem 0 1rem; font-size: 0.95rem; }
50+
th, td { text-align: left; padding: 0.55rem 0.8rem; border: 1px solid var(--border); }
51+
th { background: var(--accent-light); font-weight: 600; }
52+
tr:nth-child(even) td { background: var(--table-stripe); }
53+
footer { margin-top: 3rem; padding-top: 1rem; border-top: 1px solid var(--border); font-size: 0.85rem; color: #777; }
54+
</style>
55+
</head>
56+
<body>
57+
58+
<header>
59+
<h1>Bookshelf Plugin</h1>
60+
<p class="subtitle">Offline reference textbooks inside Code on the Go's in-IDE help</p>
61+
<div class="meta">Author: App Dev for All &middot; Package: <code>org.appdevforall.bookshelfplugin</code></div>
62+
</header>
63+
64+
<nav>
65+
<h2>Contents</h2>
66+
<ol>
67+
<li><a href="#overview">Executive Overview</a></li>
68+
<li><a href="#functionality">Core Functionality</a></li>
69+
<li><a href="#architecture">Technical Architecture</a></li>
70+
<li><a href="#usage">Usage</a></li>
71+
<li><a href="#benefits">Key Benefits</a></li>
72+
<li><a href="#attribution">Attribution &amp; License</a></li>
73+
</ol>
74+
</nav>
75+
76+
<section id="overview">
77+
<h2>1. Executive Overview</h2>
78+
<p>
79+
Bookshelf bundles a small curated library of programming reference
80+
textbooks &mdash; covering C, C++, Java, Kotlin, Android, and Pebble
81+
templates &mdash; and installs them into Code on the Go's existing in-IDE
82+
Bookshelf help page. Before the plugin is installed, the Bookshelf entries
83+
display a placeholder PDF; after installation, the same entries open the
84+
real book. Uninstalling restores the placeholders.
85+
</p>
86+
<p>
87+
The plugin is read-only on the network and ships every book bundled inside
88+
the <code>.cgp</code>, so it works offline once installed. No accounts, no
89+
downloads, no telemetry.
90+
</p>
91+
</section>
92+
93+
<section id="functionality">
94+
<h2>2. Core Functionality</h2>
95+
<p>
96+
On install, Bookshelf swaps eight placeholder PDF rows in CoGo's
97+
documentation database for the corresponding compressed textbook. On
98+
uninstall, the rows revert to the placeholder. The books shipped today
99+
are:
100+
</p>
101+
<table>
102+
<thead>
103+
<tr><th>Topic</th><th>Title</th></tr>
104+
</thead>
105+
<tbody>
106+
<tr><td>Android</td><td>Android Notes for Professionals</td></tr>
107+
<tr><td>C</td><td>Beej's Guide to C Programming</td></tr>
108+
<tr><td>C++</td><td>Modern C++ Tutorial (Ou Changkun)</td></tr>
109+
<tr><td>Java (intro)</td><td>Java, Java, Java</td></tr>
110+
<tr><td>Java (OO)</td><td>Java, Java, Java &mdash; Object-Oriented Problem Solving</td></tr>
111+
<tr><td>Java (reference)</td><td>Java Notes for Professionals</td></tr>
112+
<tr><td>Kotlin</td><td>Kotlin Notes for Professionals</td></tr>
113+
<tr><td>Templating</td><td>Pebble Template Guide</td></tr>
114+
</tbody>
115+
</table>
116+
<p>
117+
Each book is delivered as a brotli-compressed PDF (<code>.pdf.br</code>) to
118+
keep the bundled <code>.cgp</code> small; CoGo's documentation viewer
119+
decompresses on read.
120+
</p>
121+
</section>
122+
123+
<section id="architecture">
124+
<h2>3. Technical Architecture</h2>
125+
<p>
126+
Bookshelf implements two interfaces from <code>plugin-api</code>:
127+
<code>IPlugin</code> for lifecycle and
128+
<code>DocumentationExtension</code> for the install/uninstall hooks.
129+
</p>
130+
<p>
131+
Installation logic lives in <code>onDocumentationInstall()</code>. It opens
132+
the IDE's <code>documentation.db</code> SQLite database via
133+
<code>SQLiteDatabase.openDatabase(&hellip;, OPEN_READWRITE)</code>,
134+
enumerates <code>assets/books/*.pdf.br</code> from the plugin's own
135+
asset bundle, and for each book:
136+
</p>
137+
<ol>
138+
<li>Reads the pre-compressed bytes through
139+
<code>ResourceManager.openPluginAsset(&hellip;)</code>.</li>
140+
<li>Deletes any rows at the target path (and its
141+
<code>&lt;path&gt;-N</code> fragments) from the
142+
<code>Content</code> table.</li>
143+
<li>Inserts the brotli-compressed payload, chunked into 1&nbsp;MiB
144+
rows when larger than the chunk size, with content type ID
145+
<code>14</code> (which the host schema reserves for
146+
<code>application/pdf</code> with brotli compression).</li>
147+
</ol>
148+
<p>
149+
The entire batch runs inside a single transaction; if anything fails the
150+
transaction is rolled back so the documentation database is never left
151+
half-mutated.
152+
</p>
153+
<p>
154+
Uninstall (<code>onDocumentationUninstall()</code>) queries the same table
155+
for rows under <code>bookshelf/org.appdevforall.bookshelfplugin/%.pdf</code>,
156+
deletes them, and recreates each one by copying the host's
157+
<code>t/textbookPlaceholder.pdf</code> row (and its fragments) back into the
158+
original path. This relies on the placeholder row already existing in the
159+
schema, which is the case in CoGo's stock <code>documentation.db</code>.
160+
</p>
161+
<p>
162+
<code>getTier3DocsAssetPath()</code> returns <code>null</code> on purpose:
163+
the host's default Tier-3 asset insert would race with Bookshelf's manual
164+
database writes, so the plugin opts out of the automated path.
165+
</p>
166+
</section>
167+
168+
<section id="usage">
169+
<h2>4. Usage</h2>
170+
<ol>
171+
<li>Open Code on the Go's <strong>Plugin Manager</strong>.</li>
172+
<li>Install the <code>bookshelf.cgp</code> artifact (either from a local
173+
file or from the plugin catalog if it has been published there).</li>
174+
<li>Open <strong>Help &rarr; Bookshelf</strong> in the IDE.</li>
175+
<li>Select any of the eight titles &mdash; each opens the real PDF
176+
directly inside the help viewer.</li>
177+
</ol>
178+
<p>
179+
To revert, uninstall the plugin from the same Plugin Manager view; the
180+
Bookshelf entries return to their placeholder PDF.
181+
</p>
182+
</section>
183+
184+
<section id="benefits">
185+
<h2>5. Key Benefits</h2>
186+
<ul>
187+
<li><strong>Offline-first.</strong> No network access is required at
188+
install or read time &mdash; everything ships inside the
189+
<code>.cgp</code>.</li>
190+
<li><strong>Curated.</strong> Eight widely-cited free programming
191+
textbooks, hand-picked for relevance to the languages most CoGo
192+
users write.</li>
193+
<li><strong>Non-destructive.</strong> Uninstall restores the host's
194+
placeholder PDFs, leaving no orphan database state behind.</li>
195+
<li><strong>Compact.</strong> All eight books fit into roughly
196+
24&nbsp;MiB of brotli-compressed payload, decompressed on demand by
197+
the host viewer.</li>
198+
<li><strong>Permissioned narrowly.</strong> Requests only
199+
<code>filesystem.read</code>, <code>filesystem.write</code>, and
200+
<code>ide.environment.write</code>; no network or shell access.</li>
201+
</ul>
202+
</section>
203+
204+
<section id="attribution">
205+
<h2>6. Attribution &amp; License</h2>
206+
<p>
207+
Each bundled textbook retains its original author and license. The
208+
Bookshelf plugin redistributes the PDFs under the terms each author
209+
granted (Creative Commons or equivalent permissive licenses); see the
210+
front matter of each book for the specific terms.
211+
</p>
212+
<p>
213+
The plugin code itself is part of the Code on the Go plugin examples
214+
repository.
215+
</p>
216+
</section>
217+
218+
<footer>
219+
Bookshelf for Code on the Go &middot; App Dev for All
220+
</footer>
221+
222+
</body>
223+
</html>

bookshelf/build.gradle.kts

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
2+
3+
plugins {
4+
id("com.android.application")
5+
id("org.jetbrains.kotlin.android")
6+
id("com.itsaky.androidide.plugins.build")
7+
}
8+
9+
pluginBuilder {
10+
pluginName = "bookshelf"
11+
}
12+
13+
android {
14+
namespace = "org.appdevforall.bookshelfplugin"
15+
compileSdk = 34
16+
17+
defaultConfig {
18+
applicationId = "org.appdevforall.bookshelfplugin"
19+
minSdk = 26
20+
targetSdk = 34
21+
versionCode = 1
22+
versionName = "1.0.0"
23+
}
24+
25+
buildTypes {
26+
release {
27+
isMinifyEnabled = false
28+
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
29+
}
30+
}
31+
32+
compileOptions {
33+
sourceCompatibility = JavaVersion.VERSION_17
34+
targetCompatibility = JavaVersion.VERSION_17
35+
}
36+
37+
}
38+
39+
kotlin {
40+
compilerOptions {
41+
jvmTarget.set(JvmTarget.JVM_17)
42+
}
43+
}
44+
45+
dependencies {
46+
compileOnly(files("../libs/gradle-plugin.jar"))
47+
compileOnly(files("../libs/plugin-api.jar"))
48+
49+
implementation("org.jetbrains.kotlin:kotlin-stdlib:2.1.21")
50+
}
51+
52+
tasks.matching {
53+
it.name.contains("checkDebugAarMetadata") ||
54+
it.name.contains("checkReleaseAarMetadata")
55+
}.configureEach {
56+
enabled = false
57+
}
58+
59+
60+
61+
62+

bookshelf/gradle.properties

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
2+
android.useAndroidX=true
3+
android.nonTransitiveRClass=true
44.4 KB
Binary file not shown.
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
distributionBase=GRADLE_USER_HOME
2+
distributionPath=wrapper/dists
3+
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip
4+
networkTimeout=10000
5+
validateDistributionUrl=true
6+
zipStoreBase=GRADLE_USER_HOME
7+
zipStorePath=wrapper/dists

0 commit comments

Comments
 (0)