fix mobile export save actions
All checks were successful
Forgejo Android APK / Build signed APK (push) Successful in 1m39s
All checks were successful
Forgejo Android APK / Build signed APK (push) Successful in 1m39s
This commit is contained in:
parent
25464a90bf
commit
747ec7a89e
9 changed files with 85 additions and 14 deletions
|
|
@ -9,8 +9,8 @@ android {
|
|||
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||
// Version values below are overwritten by scripts/release.sh from
|
||||
// the root package.json. versionCode auto-increments per release.
|
||||
versionCode 714001
|
||||
versionName "7.14.1"
|
||||
versionCode 714002
|
||||
versionName "7.14.2"
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
aaptOptions {
|
||||
// Files and dirs to omit from the packaged assets dir, modified to accommodate modern web apps.
|
||||
|
|
|
|||
|
|
@ -1,11 +1,17 @@
|
|||
package com.pedshub.scribe;
|
||||
|
||||
import android.Manifest;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Bundle;
|
||||
import android.print.PrintAttributes;
|
||||
import android.print.PrintDocumentAdapter;
|
||||
import android.print.PrintManager;
|
||||
import android.util.Base64;
|
||||
import android.webkit.PermissionRequest;
|
||||
import android.webkit.WebChromeClient;
|
||||
import android.webkit.WebViewClient;
|
||||
import android.webkit.WebView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
|
@ -18,6 +24,7 @@ public class MainActivity extends BridgeActivity {
|
|||
|
||||
private static final int MIC_PERMISSION_CODE = 1001;
|
||||
private PermissionRequest pendingPermissionRequest;
|
||||
private WebView printWebView;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
|
|
@ -35,6 +42,9 @@ public class MainActivity extends BridgeActivity {
|
|||
|
||||
// Register JS interface for foreground service control
|
||||
setupRecordingBridge();
|
||||
|
||||
// Register JS interface for Android's print / Save as PDF flow.
|
||||
setupPrintBridge();
|
||||
}
|
||||
|
||||
// ── WebView Microphone Permission ──────────────────────────
|
||||
|
|
@ -80,6 +90,11 @@ public class MainActivity extends BridgeActivity {
|
|||
webView.addJavascriptInterface(new RecordingBridge(this), "NativeRecording");
|
||||
}
|
||||
|
||||
private void setupPrintBridge() {
|
||||
WebView webView = this.bridge.getWebView();
|
||||
webView.addJavascriptInterface(new PrintBridge(this), "NativePrint");
|
||||
}
|
||||
|
||||
public static class RecordingBridge {
|
||||
private final MainActivity activity;
|
||||
|
||||
|
|
@ -100,4 +115,36 @@ public class MainActivity extends BridgeActivity {
|
|||
activity.startService(intent);
|
||||
}
|
||||
}
|
||||
|
||||
public static class PrintBridge {
|
||||
private final MainActivity activity;
|
||||
|
||||
PrintBridge(MainActivity activity) {
|
||||
this.activity = activity;
|
||||
}
|
||||
|
||||
@android.webkit.JavascriptInterface
|
||||
public void printHtml(String title, String base64Html) {
|
||||
activity.runOnUiThread(() -> activity.printHtmlFromBase64(title, base64Html));
|
||||
}
|
||||
}
|
||||
|
||||
private void printHtmlFromBase64(String title, String base64Html) {
|
||||
try {
|
||||
byte[] decoded = Base64.decode(base64Html, Base64.DEFAULT);
|
||||
String html = new String(decoded, java.nio.charset.StandardCharsets.UTF_8);
|
||||
printWebView = new WebView(this);
|
||||
printWebView.setWebViewClient(new WebViewClient() {
|
||||
@Override
|
||||
public void onPageFinished(WebView view, String url) {
|
||||
PrintManager printManager = (PrintManager) getSystemService(Context.PRINT_SERVICE);
|
||||
PrintDocumentAdapter adapter = view.createPrintDocumentAdapter(title != null && !title.isEmpty() ? title : "Clinical Assistant Export");
|
||||
printManager.print(title != null && !title.isEmpty() ? title : "Clinical Assistant Export", adapter, new PrintAttributes.Builder().build());
|
||||
}
|
||||
});
|
||||
printWebView.loadDataWithBaseURL(null, html, "text/html", "UTF-8", null);
|
||||
} catch (Exception e) {
|
||||
android.util.Log.e("PedScribe", "Native print failed", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,4 +2,6 @@
|
|||
<paths xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<external-path name="my_images" path="." />
|
||||
<cache-path name="my_cache_images" path="." />
|
||||
</paths>
|
||||
<files-path name="my_files" path="." />
|
||||
<external-files-path name="my_external_files" path="." />
|
||||
</paths>
|
||||
|
|
|
|||
4
mobile/package-lock.json
generated
4
mobile/package-lock.json
generated
|
|
@ -1,12 +1,12 @@
|
|||
{
|
||||
"name": "pedscribe-mobile",
|
||||
"version": "7.14.1",
|
||||
"version": "7.14.2",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "pedscribe-mobile",
|
||||
"version": "7.14.1",
|
||||
"version": "7.14.2",
|
||||
"dependencies": {
|
||||
"@aparajita/capacitor-biometric-auth": "^8.0.0",
|
||||
"@capacitor/android": "^6.0.0",
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "pedscribe-mobile",
|
||||
"version": "7.14.1",
|
||||
"version": "7.14.2",
|
||||
"description": "PedScribe native mobile app — Capacitor wrapper for Pediatric AI Scribe",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
|
|
|
|||
4
package-lock.json
generated
4
package-lock.json
generated
|
|
@ -1,12 +1,12 @@
|
|||
{
|
||||
"name": "pediatric-ai-scribe",
|
||||
"version": "7.14.1",
|
||||
"version": "7.14.2",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "pediatric-ai-scribe",
|
||||
"version": "7.14.1",
|
||||
"version": "7.14.2",
|
||||
"dependencies": {
|
||||
"@marp-team/marp-cli": "^4.3.1",
|
||||
"@marp-team/marp-core": "^4.3.0",
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "pediatric-ai-scribe",
|
||||
"version": "7.14.1",
|
||||
"version": "7.14.2",
|
||||
"description": "AI-powered pediatric clinical documentation platform",
|
||||
"main": "server.js",
|
||||
"scripts": {
|
||||
|
|
|
|||
|
|
@ -78,7 +78,9 @@ export function createAssistantExporter(options) {
|
|||
var originalText = printBtn.textContent;
|
||||
printBtn.textContent = 'Preparing export...';
|
||||
try {
|
||||
if (!(await saveInlineExport(buildPrintableChatHtml(items, imageSrc, false)))) window.print();
|
||||
var html = buildPrintableChatHtml(items, imageSrc, false);
|
||||
if (await printWithNativeBridge(html)) return;
|
||||
if (!(await saveInlineExport(html))) window.print();
|
||||
} finally {
|
||||
printBtn.disabled = false;
|
||||
printBtn.textContent = originalText;
|
||||
|
|
@ -150,7 +152,7 @@ async function saveInlineExport(html) {
|
|||
if (!plugins || !plugins.Filesystem) return false;
|
||||
try {
|
||||
var name = 'clinical-assistant-export-' + new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19) + '.html';
|
||||
var saved = await plugins.Filesystem.writeFile({ path: name, data: utf8ToBase64(html), directory: 'DOCUMENTS' });
|
||||
var saved = await plugins.Filesystem.writeFile({ path: name, data: utf8ToBase64(html), directory: 'CACHE' });
|
||||
if (plugins.Share && saved && saved.uri) {
|
||||
try {
|
||||
await plugins.Share.share({ title: 'Clinical Assistant Export', text: 'Open this export and use Print to save as PDF.', url: saved.uri, dialogTitle: 'Save or share export' });
|
||||
|
|
@ -158,9 +160,21 @@ async function saveInlineExport(html) {
|
|||
if (!isShareCancel(e)) throw e;
|
||||
}
|
||||
}
|
||||
if (typeof window.showToast === 'function') window.showToast('Export saved to Documents', 'success');
|
||||
if (typeof window.showToast === 'function') window.showToast('Export prepared', 'success');
|
||||
return true;
|
||||
} catch (e) {
|
||||
if (typeof window.showToast === 'function') window.showToast(e.message || 'Export failed', 'error');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async function printWithNativeBridge(html) {
|
||||
if (!window.NativePrint || typeof window.NativePrint.printHtml !== 'function') return false;
|
||||
try {
|
||||
window.NativePrint.printHtml('Clinical Assistant Export', utf8ToBase64(html));
|
||||
return true;
|
||||
} catch (e) {
|
||||
if (typeof window.showToast === 'function') window.showToast(e.message || 'Native print failed', 'error');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -76,8 +76,16 @@ export function createAssistantImageStore() {
|
|||
}
|
||||
|
||||
async function saveWithNativeShare(src) {
|
||||
if (isNativeApp()) {
|
||||
return await saveWithCapacitorShare(src);
|
||||
}
|
||||
var webShare = await shareWithWebFile(src);
|
||||
if (webShare !== 'unavailable') return true;
|
||||
|
||||
return await saveWithCapacitorShare(src);
|
||||
}
|
||||
|
||||
async function saveWithCapacitorShare(src) {
|
||||
var plugins = window.Capacitor && window.Capacitor.Plugins ? window.Capacitor.Plugins : null;
|
||||
if (!plugins) return false;
|
||||
if (!plugins.Filesystem && plugins.Share && !/^data:image\//i.test(src)) {
|
||||
|
|
@ -90,7 +98,7 @@ async function saveWithNativeShare(src) {
|
|||
try {
|
||||
var base64 = await imageSourceToBase64(src);
|
||||
var name = 'clinical-visual-' + new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19) + '.png';
|
||||
var saved = await plugins.Filesystem.writeFile({ path: name, data: base64, directory: 'DOCUMENTS' });
|
||||
var saved = await plugins.Filesystem.writeFile({ path: name, data: base64, directory: 'CACHE' });
|
||||
if (plugins.Share && saved && saved.uri) {
|
||||
try {
|
||||
await plugins.Share.share({ title: 'Clinical visual', text: 'Clinical Assistant generated visual', url: saved.uri, dialogTitle: 'Save or share clinical visual' });
|
||||
|
|
@ -98,7 +106,7 @@ async function saveWithNativeShare(src) {
|
|||
if (!isShareCancel(e)) throw e;
|
||||
}
|
||||
}
|
||||
if (typeof window.showToast === 'function') window.showToast('Image saved to Documents', 'success');
|
||||
if (typeof window.showToast === 'function') window.showToast('Image prepared', 'success');
|
||||
return true;
|
||||
} catch (e) {
|
||||
if (typeof window.showToast === 'function') window.showToast('Could not save with the app. Opening browser download instead.', 'error');
|
||||
|
|
|
|||
Loading…
Reference in a new issue