# SumDoc view-SDK for Android

Open and display `.docx` / `.docm` documents inside your own Android app,
rendered by the same engine and the same page that ship in SumDoc on Google
Play (version 2026.3.33, build 11). The document is opened by a native
Rust core; layout and drawing happen in a WebView page bundled with the SDK.
No server, no network: everything runs on the device.

Android, `arm64-v8a` only (the core is not built for other ABIs). The page
opens in view mode; the pencil switches to editing, which is the same editor
SumDoc ships. The SDK carries the same release number as the store build and
is released together with it.

## What is in the box

| Path | What | Size |
|---|---|---|
| `jniLibs/arm64-v8a/libfastdoc_mobile_core.so` | the document core (JNI) | 145 MB |
| `assets/a4/` | the viewer page: `index.html`, JS, CSS, fonts, icons | 21 MB |
| `assets/sample.docx` | a small document to prove the integration | 3 KB |
| `bridge/FastDocBridge.kt` | Kotlin wrapper over the core's JNI surface | |
| `bridge/FastDocWebBridge.kt` | routes page requests to the core (WebView bridge) | |
| `example/SumDocViewerActivity.kt` | reference host activity (steps 2 and 3 as code) | |
| `example/AndroidManifest.xml`, `example/build.gradle.kts` | what the reference host needs | |
| `VERSION` | version, build, date, checksums | |

The binary and the page are byte-for-byte the ones inside the Play AAB
(`SumDoc-2026.3.31-code8`); see `VERSION` for SHA-256.

## Requirements

- Android Gradle Plugin 8.x, Kotlin 2.x, `minSdk` 27 or higher, `compileSdk` 36.
- Dependencies: `androidx.webkit:webkit:1.12.1` (document-start script) and
  `androidx.appcompat:appcompat:1.7.0` (used by the reference activity).
- A device or emulator with an `arm64-v8a` system image and Android System
  WebView (any recent version; verified with Chromium 113).
- Roughly 160 MB of APK per architecture — the core is large. Ship it in an
  App Bundle so Play delivers only the arm64 split.

## Steps 1-2-3

### 1. Add to your app

Copy into your application module (`app/src/main/…`):

```
jniLibs/                       -> app/src/main/jniLibs/
assets/                        -> app/src/main/assets/
bridge/FastDocBridge.kt        -> app/src/main/java/com/fastdoc/mobile/FastDocBridge.kt
bridge/FastDocWebBridge.kt     -> app/src/main/java/com/fastdoc/mobile/shellhost/FastDocWebBridge.kt
```

Keep the bridge files' packages exactly as they are
(`com.fastdoc.mobile` and `com.fastdoc.mobile.shellhost`): the JNI symbol
names in the core are derived from `com.fastdoc.mobile.FastDocBridge`.

In `app/build.gradle.kts` add:

```kotlin
android {
  defaultConfig { ndk { abiFilters += listOf("arm64-v8a") } }
}
dependencies {
  implementation("androidx.webkit:webkit:1.12.1")
  implementation("androidx.appcompat:appcompat:1.7.0")
}
```

### 2. Create the WebView

Copy `example/SumDocViewerActivity.kt` into your package (change the
`package` line) and register the activity in your manifest
(`example/AndroidManifest.xml` shows the element). What the activity does,
and what any host must do:

```kotlin
val wv = WebView(this)
wv.settings.allowFileAccessFromFileURLs = true       // page is ES modules on file://
wv.settings.allowUniversalAccessFromFileURLs = true
wv.settings.domStorageEnabled = true                 // page keeps its UI language here

// before the page runs (needs androidx.webkit): UI language, product name,
// and a function for the header "back" button — see the reference activity
WebViewCompat.addDocumentStartJavaScript(wv,
  "try{if(!localStorage.getItem('a4.ui-locale'))localStorage.setItem('a4.ui-locale','en');}catch(e){} " +
  "window.__a4ProductBrand = 'SumDoc'; " +
  "window.__fastdocMobileBack = function(){ SumDocHost.back(); };",
  setOf("*"))

val bridge = FastDocWebBridge(wv, docxBytes, path, displayName) { savedBytes ->
  /* write savedBytes wherever the document should be saved; return that path */
}
bridge.attach()                                      // JS on, file access on, window.FastDocNative
wv.addJavascriptInterface(object {
  @JavascriptInterface fun back() { /* close the viewer */ }
  @JavascriptInterface fun спрятатьКлавиатуру() { /* hide the soft keyboard */ }
}, "SumDocHost")
wv.loadUrl("file:///android_asset/a4/index.html")
setContentView(wv)
```

Order matters: settings and `bridge.attach()` before `loadUrl`. Call
`bridge.dispose()` when the WebView goes away (it frees the native document).

### 3. Open a document

`FastDocWebBridge` takes the document as bytes, so any source works:

```kotlin
// the bundled sample
startActivity(Intent(this, SumDocViewerActivity::class.java))

// a file the user picked, shared, or tapped ("Open with"):
startActivity(Intent(Intent.ACTION_VIEW).apply {
  setDataAndType(uri, "application/vnd.openxmlformats-officedocument.wordprocessingml.document")
  addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
  setClass(this@MyActivity, SumDocViewerActivity::class.java)
})
```

The reference activity reads `intent.data` with `contentResolver`, falls
back to `assets/sample.docx` when there is no URI, and writes "save" back
to the source URI when it has one. To appear in the system "Open with" list
for `.docx`/`.docm`, add the second intent-filter from
`example/AndroidManifest.xml`.

## Host contract (what the page expects from you)

- `window.FastDocNative.postEnvelope(json)` — provided by `bridge.attach()`.
  The page sends `{id, op, args}`; the bridge answers through
  `window.__fastdocNativeDeliver(json)`. You do not touch this.
- `localStorage['a4.ui-locale']` — UI language: `en`, `ru` or `ar`. Without
  it the page shows its authored (Russian) strings. The reference activity
  sets it once from the system language, like the store app; the user's own
  choice made in the page survives (it lives in DOM storage).
- `window.__a4ProductBrand` — product name in the page's own texts (about,
  protected-view banner). Defaults to "A4" when unset.
- `window.__fastdocMobileBack()` — optional; the header "back" button. If
  absent the button does nothing.
- `window.SumDocHost.спрятатьКлавиатуру()` — optional; called when leaving
  edit mode so the host can hide the keyboard. The name is fixed by the page.
- `window.__A4_ДОКУМЕНТ__` — `{путь, base64}` of the document. This page
  version does not read it (format is decided by the file extension); the
  next page version reads `.base64` to detect `.docm` by package contents,
  so the reference activity already sets it.
- The page's `save` goes to the lambda you pass to `FastDocWebBridge`; it
  receives the new `.docx` bytes and returns the path it wrote them to.

## Verified

Followed literally on an empty single-activity Kotlin project (AGP 8.11.1,
Kotlin 2.1.20, Gradle 8.14): debug APK 161 MB. The bundled sample and a
37 KB business letter (via `ACTION_VIEW`) rendered 1.5-2.2 s after a cold
launch on an arm64 API 34 emulator (pixel-identical to the Play build
2026.3.31 installed next to it) and 3.3-3.4 s on a Samsung Galaxy A22 5G
(Android 13). Roughly 1 s of that is activity start; the rest is WebView page
load plus the core opening the document.

## Known limits

- `arm64-v8a` only. No x86_64 emulator images, no 32-bit devices.
- The core is 145 MB uncompressed and loads on first use.
- The page is a full editor in view mode, not a lightweight viewer: RAM and
  first-open time are those of the SumDoc app.
- iOS: the same core and page exist in SumDoc for iOS, but the core is linked
  statically into that app; a separate `.xcframework` for third-party apps is
  next.

## Terms

Free to evaluate and to build integration prototypes. Licensing for
production use and support: contact us via sumoffice.com.
