From 3fb8b6634fab2a4c88032ad4a0e81d7442fa2849 Mon Sep 17 00:00:00 2001 From: Chris Warren-Smith Date: Thu, 11 Sep 2025 20:27:10 +0930 Subject: [PATCH 1/5] ANDROID: implemented support for editing with the system keypad --- ChangeLog | 8 ++++++ configure.ac | 2 +- src/platform/android/app/build.gradle | 4 +-- .../sourceforge/smallbasic/MainActivity.java | 3 +- src/platform/android/build.gradle | 2 +- src/platform/android/jni/editor.cpp | 21 ++++++++++---- src/platform/web/main.cpp | 8 ++++++ src/ui/system.cpp | 28 +++++++++++++++---- src/ui/system.h | 2 +- 9 files changed, 61 insertions(+), 17 deletions(-) diff --git a/ChangeLog b/ChangeLog index 921d048f..f1bdb160 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,11 @@ +2025-09-11 (12.31) + ANDROID: Implemented support for editing with the system keypad + +2025-08-31 (12.30) + ANDROID: Implemented a replacement editor keypad + ANDROID: Implemented editor find command + ANDROID: Updated editor help + 2025-04-26 (12.29) ANDROID: minor fix for PEN(3) diff --git a/configure.ac b/configure.ac index 902c8064..70d88eee 100644 --- a/configure.ac +++ b/configure.ac @@ -7,7 +7,7 @@ dnl This program is distributed under the terms of the GPL v2.0 dnl Download the GNU Public License (GPL) from www.gnu.org dnl -AC_INIT([smallbasic], [12.30]) +AC_INIT([smallbasic], [12.31]) AC_CONFIG_SRCDIR([configure.ac]) AC_CANONICAL_TARGET diff --git a/src/platform/android/app/build.gradle b/src/platform/android/app/build.gradle index b8cef8dc..376caf3a 100644 --- a/src/platform/android/app/build.gradle +++ b/src/platform/android/app/build.gradle @@ -9,8 +9,8 @@ android { applicationId 'net.sourceforge.smallbasic' minSdkVersion 21 targetSdkVersion 36 - versionCode 83 - versionName '12.30' + versionCode 84 + versionName '12.31' resourceConfigurations += ['en'] } diff --git a/src/platform/android/app/src/main/java/net/sourceforge/smallbasic/MainActivity.java b/src/platform/android/app/src/main/java/net/sourceforge/smallbasic/MainActivity.java index 7c33b652..94b8ffad 100644 --- a/src/platform/android/app/src/main/java/net/sourceforge/smallbasic/MainActivity.java +++ b/src/platform/android/app/src/main/java/net/sourceforge/smallbasic/MainActivity.java @@ -1226,7 +1226,8 @@ protected Response getFile(String remoteHost, String path, boolean asset) throws String name = "webui/" + path; long length = getFileLength(name); log("Opened " + name + " " + length + " bytes"); - String contentType = path.endsWith("js") ? "text/javascript" : "text/html"; + String contentType = path.endsWith("js") ? "text/javascript" : + path.endsWith("css") ? "text/css": "text/html"; result = new Response(getAssets().open(name), length, contentType); if ("index.html".equals(path) && isHostNotPermitted(remoteHost)) { requestHostPermission(remoteHost); diff --git a/src/platform/android/build.gradle b/src/platform/android/build.gradle index ec0cacf4..b7d50dd3 100644 --- a/src/platform/android/build.gradle +++ b/src/platform/android/build.gradle @@ -5,7 +5,7 @@ buildscript { mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:8.12.1' + classpath 'com.android.tools.build:gradle:8.13.0' } } diff --git a/src/platform/android/jni/editor.cpp b/src/platform/android/jni/editor.cpp index 951ec8dc..3e2ffbbc 100644 --- a/src/platform/android/jni/editor.cpp +++ b/src/platform/android/jni/editor.cpp @@ -102,7 +102,7 @@ struct StatusMessage { out->setStatus(message); } -private: + private: bool _dirty; bool _find; int _scroll; @@ -126,7 +126,6 @@ void showHelp(TextEditHelpWidget *helpWidget) { void Runtime::editSource(strlib::String loadPath, bool restoreOnExit) { logEntered(); - showKeypad(false); int w = _output->getWidth(); int h = _output->getHeight(); int charWidth = _output->getCharWidth(); @@ -153,11 +152,16 @@ void Runtime::editSource(strlib::String loadPath, bool restoreOnExit) { _output->addInput(editWidget); _output->addInput(helpWidget); - if (_keypad != nullptr) { - _output->addInput(_keypad); + if (opt_ide == IDE_EXTERNAL) { + showKeypad(true); } else { - _keypad = new KeypadInput(w, false, false, charWidth, charHeight); - _output->addInput(_keypad); + showKeypad(false); + if (_keypad != nullptr) { + _output->addInput(_keypad); + } else { + _keypad = new KeypadInput(w, false, false, charWidth, charHeight); + _output->addInput(_keypad); + } } statusMessage.update(editWidget, _output); @@ -207,6 +211,7 @@ void Runtime::editSource(strlib::String loadPath, bool restoreOnExit) { } else { widget = helpWidget; showHelp(helpWidget); + showKeypad(false); } break; case SB_KEY_F(9): @@ -258,6 +263,9 @@ void Runtime::editSource(strlib::String loadPath, bool restoreOnExit) { } if ((exitHelp || isBack()) && widget == helpWidget) { + if (opt_ide == IDE_EXTERNAL) { + showKeypad(true); + } widget = editWidget; helpWidget->hide(); editWidget->setFocus(true); @@ -293,6 +301,7 @@ void Runtime::editSource(strlib::String loadPath, bool restoreOnExit) { if (!_output->removeInput(_keypad)) { trace("Failed to remove keypad input"); } + showKeypad(false); _editor = editWidget; _editor->setFocus(false); } else { diff --git a/src/platform/web/main.cpp b/src/platform/web/main.cpp index 8a7562f3..2f17a6fb 100644 --- a/src/platform/web/main.cpp +++ b/src/platform/web/main.cpp @@ -156,6 +156,14 @@ MHD_Response *serve_file(const char *path) { int fd = open(path, O_RDONLY | O_BINARY); if (!fstat(fd, &stbuf)) { response = MHD_create_response_from_fd(stbuf.st_size, fd); + + // add the content-type headers for browsing + unsigned len = strlen(path); + if (len > 4 && strcmp(path + len - 4, ".css") == 0) { + MHD_add_response_header(response, MHD_HTTP_HEADER_CONTENT_TYPE, "text/css"); + } else if (len > 3 && strcmp(path + len - 3, ".js") == 0) { + MHD_add_response_header(response, MHD_HTTP_HEADER_CONTENT_TYPE, "application/javascript"); + } } else { response = nullptr; } diff --git a/src/ui/system.cpp b/src/ui/system.cpp index 98f7b1c6..63eb8531 100644 --- a/src/ui/system.cpp +++ b/src/ui/system.cpp @@ -83,6 +83,7 @@ #define MENU_STR_SELECT MK_MENU("Select All", "^a") #define MENU_STR_OFF "OFF" #define MENU_STR_ON "ON" +#define MENU_STR_EXT "SYSTEM" #define MENU_STR_AUDIO " Audio [%s] " #define MENU_STR_EDITOR " Editor [%s] " #define MENU_STR_THEME " Theme [%s] " @@ -414,7 +415,8 @@ void System::handleMenu(MAEvent &event) { } break; case MENU_EDITMODE: - opt_ide = (opt_ide == IDE_NONE ? IDE_INTERNAL : IDE_NONE); + opt_ide = (opt_ide == IDE_NONE ? IDE_INTERNAL : + opt_ide == IDE_INTERNAL ? IDE_EXTERNAL : IDE_NONE); break; case MENU_THEME: g_themeId = (g_themeId + 1) % NUM_THEMES; @@ -1072,7 +1074,9 @@ void System::showMenu() { items->add(new String(MENU_STR_DEBUG)); items->add(new String(MENU_STR_OUTPUT)); #elif defined(_ANDROID) - items->add(new String(MENU_STR_FIND)); + if (opt_ide == IDE_INTERNAL) { + items->add(new String(MENU_STR_FIND)); + } #endif items->add(new String(MENU_STR_HELP)); for (int i = 0; i < completions; i++) { @@ -1090,7 +1094,9 @@ void System::showMenu() { _systemMenu[index++] = MENU_DEBUG; _systemMenu[index++] = MENU_OUTPUT; #elif defined(_ANDROID) - _systemMenu[index++] = MENU_FIND; + if (opt_ide == IDE_INTERNAL) { + _systemMenu[index++] = MENU_FIND; + } #endif _systemMenu[index++] = MENU_HELP; } else if (isRunning()) { @@ -1107,9 +1113,11 @@ void System::showMenu() { items->add(new String(MENU_STR_BACK)); _systemMenu[index++] = MENU_BACK; #else - if (!isEditing()) { + if (!isEditing() || opt_ide == IDE_EXTERNAL) { items->add(new String(MENU_STR_KEYPAD)); _systemMenu[index++] = MENU_KEYPAD; + } + if (!isEditing()) { bool controlMode = get_focus_edit()->getControlMode(); sprintf(buffer, MENU_STR_CONTROL, (controlMode ? MENU_STR_ON : MENU_STR_OFF)); items->add(new String(buffer)); @@ -1141,7 +1149,17 @@ void System::showMenu() { _systemMenu[index++] = MENU_ZOOM_DN; #endif #if !defined(_ANDROID_LIBRARY) - sprintf(buffer, MENU_STR_EDITOR, opt_ide == IDE_NONE ? MENU_STR_OFF : MENU_STR_ON); + switch (opt_ide) { + case IDE_NONE: + sprintf(buffer, MENU_STR_EDITOR, MENU_STR_OFF); + break; + case IDE_INTERNAL: + sprintf(buffer, MENU_STR_EDITOR, MENU_STR_ON); + break; + case IDE_EXTERNAL: + sprintf(buffer, MENU_STR_EDITOR, MENU_STR_EXT); + break; + } items->add(new String(buffer)); _systemMenu[index++] = MENU_EDITMODE; #endif diff --git a/src/ui/system.h b/src/ui/system.h index 4db9556a..93b58929 100755 --- a/src/ui/system.h +++ b/src/ui/system.h @@ -81,7 +81,7 @@ struct System { uint32_t getModifiedTime() const; void handleEvent(MAEvent &event); void handleMenu(MAEvent &event); - bool isEditEnabled() const {return opt_ide == IDE_INTERNAL || isScratchLoad();} + bool isEditEnabled() const {return opt_ide == IDE_INTERNAL || opt_ide == IDE_EXTERNAL || isScratchLoad();} bool isEditReady() const {return !isRestart() && isEditEnabled() && !isNetworkLoad();} bool isNetworkLoad() const {return _loadPath.indexOf("://", 1) != -1;} bool isScratchLoad() const {return _loadPath.indexOf("scratch", 0) != -1;} From d566fde17ebf90cc6aa06a311312b3eeea2281b1 Mon Sep 17 00:00:00 2001 From: Chris Warren-Smith Date: Mon, 15 Sep 2025 17:31:58 +0930 Subject: [PATCH 2/5] ANDROID: implemented support for editing with the system keypad --- src/platform/android/app/build.gradle | 2 +- src/platform/android/jni/editor.cpp | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/platform/android/app/build.gradle b/src/platform/android/app/build.gradle index 376caf3a..44366ffe 100644 --- a/src/platform/android/app/build.gradle +++ b/src/platform/android/app/build.gradle @@ -9,7 +9,7 @@ android { applicationId 'net.sourceforge.smallbasic' minSdkVersion 21 targetSdkVersion 36 - versionCode 84 + versionCode 85 versionName '12.31' resourceConfigurations += ['en'] } diff --git a/src/platform/android/jni/editor.cpp b/src/platform/android/jni/editor.cpp index 3e2ffbbc..09038d05 100644 --- a/src/platform/android/jni/editor.cpp +++ b/src/platform/android/jni/editor.cpp @@ -130,7 +130,8 @@ void Runtime::editSource(strlib::String loadPath, bool restoreOnExit) { int h = _output->getHeight(); int charWidth = _output->getCharWidth(); int charHeight = _output->getCharHeight(); - int prevScreenId = _output->selectScreen(FORM_SCREEN); + int screenId = opt_ide == IDE_EXTERNAL ? SOURCE_SCREEN : FORM_SCREEN; + int prevScreenId = _output->selectScreen(screenId); TextEditInput *editWidget; if (_editor != nullptr) { editWidget = _editor; From e536831c0d5bec6e6dbdf783d8cb391514ef2376 Mon Sep 17 00:00:00 2001 From: Chris Warren-Smith Date: Sat, 20 Sep 2025 13:37:11 +0930 Subject: [PATCH 3/5] ANDROID: implemented support for editing with the system keypad - Fixes for Pixel 9/Android 16 --- .../sourceforge/smallbasic/MainActivity.java | 51 ++++++++++++++++--- src/platform/android/jni/editor.cpp | 3 +- src/platform/android/jni/runtime.cpp | 16 +++--- src/platform/android/jni/runtime.h | 2 +- src/ui/textedit.cpp | 6 ++- 5 files changed, 60 insertions(+), 18 deletions(-) diff --git a/src/platform/android/app/src/main/java/net/sourceforge/smallbasic/MainActivity.java b/src/platform/android/app/src/main/java/net/sourceforge/smallbasic/MainActivity.java index 94b8ffad..23f4dfdb 100644 --- a/src/platform/android/app/src/main/java/net/sourceforge/smallbasic/MainActivity.java +++ b/src/platform/android/app/src/main/java/net/sourceforge/smallbasic/MainActivity.java @@ -39,10 +39,15 @@ import android.window.OnBackInvokedCallback; import android.window.OnBackInvokedDispatcher; +import androidx.annotation.NonNull; import androidx.annotation.RequiresPermission; import androidx.core.app.ActivityCompat; import androidx.core.content.ContextCompat; import androidx.core.content.FileProvider; +import androidx.core.graphics.Insets; +import androidx.core.view.OnApplyWindowInsetsListener; +import androidx.core.view.ViewCompat; +import androidx.core.view.WindowInsetsCompat; import java.io.BufferedReader; import java.io.BufferedWriter; @@ -121,7 +126,7 @@ public class MainActivity extends NativeActivity { public static native boolean libraryMode(); public static native void onActivityPaused(boolean paused); public static native void onBack(); - public static native void onResize(int width, int height); + public static native void onResize(int width, int height, int imeState); public static native void onUnicodeChar(int ch); public static native boolean optionSelected(int index); public static native void runFile(String fileName); @@ -429,6 +434,10 @@ public int getWindowHeight() { return rect.height(); } + public boolean isInsetBasedOnResize() { + return Build.VERSION.SDK_INT >= Build.VERSION_CODES.BAKLAVA; + } + public boolean isPredictiveBack() { return Build.VERSION.SDK_INT >= Build.VERSION_CODES.BAKLAVA; } @@ -452,10 +461,11 @@ public boolean loadModules() { @Override public void onGlobalLayout() { super.onGlobalLayout(); - // find the visible coordinates of our view - Rect rect = new Rect(); - findViewById(android.R.id.content).getWindowVisibleDisplayFrame(rect); - onResize(rect.width(), rect.height()); + if (!isInsetBasedOnResize()) { + Rect rect = new Rect(); + findViewById(android.R.id.content).getWindowVisibleDisplayFrame(rect); + onResize(rect.width(), rect.height(), 0); + } } @Override @@ -774,6 +784,7 @@ protected void onCreate(Bundle savedInstanceState) { setImmersiveMode(); setupStorageEnvironment(); setupPredictiveBack(); + setupInsetBaseOnResize(); if (!libraryMode()) { processIntent(); processSettings(); @@ -1063,14 +1074,40 @@ private void setImmersiveMode() { } } + /** + * onResize() handler for android 16+ + */ + private void setupInsetBaseOnResize() { + if (isInsetBasedOnResize()) { + View view = getWindow().getDecorView(); + ViewCompat.setOnApplyWindowInsetsListener(view, new OnApplyWindowInsetsListener() { + @NonNull + @Override + public WindowInsetsCompat onApplyWindowInsets(@NonNull View view, @NonNull WindowInsetsCompat insets) { + Log.d(TAG, "onApplyWindowInsets"); + Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars()); + Insets navInsets = insets.getInsets(WindowInsetsCompat.Type.navigationBars()); + Insets ime = insets.getInsets(WindowInsetsCompat.Type.ime()); + boolean imeVisible = insets.isVisible(WindowInsetsCompat.Type.ime()); + view.setPadding(0, 0, navInsets.right, navInsets.bottom); + int bottomInset = Math.max(systemBars.bottom, ime.bottom); + int width = view.getWidth(); + int height = view.getHeight() - systemBars.top - bottomInset; + onResize(width, height, imeVisible ? 1 : -1); + return insets; + } + }); + view.post(() -> ViewCompat.requestApplyInsets(view)); + } + } + // // Hook into Predictive Back (Android 13+) // private void setupPredictiveBack() { if (isPredictiveBack()) { getOnBackInvokedDispatcher().registerOnBackInvokedCallback( - OnBackInvokedDispatcher.PRIORITY_DEFAULT, - new OnBackInvokedCallback() { + OnBackInvokedDispatcher.PRIORITY_OVERLAY, new OnBackInvokedCallback() { @Override public void onBackInvoked() { Log.d(TAG, "onBackInvoked"); diff --git a/src/platform/android/jni/editor.cpp b/src/platform/android/jni/editor.cpp index 09038d05..3e2ffbbc 100644 --- a/src/platform/android/jni/editor.cpp +++ b/src/platform/android/jni/editor.cpp @@ -130,8 +130,7 @@ void Runtime::editSource(strlib::String loadPath, bool restoreOnExit) { int h = _output->getHeight(); int charWidth = _output->getCharWidth(); int charHeight = _output->getCharHeight(); - int screenId = opt_ide == IDE_EXTERNAL ? SOURCE_SCREEN : FORM_SCREEN; - int prevScreenId = _output->selectScreen(screenId); + int prevScreenId = _output->selectScreen(FORM_SCREEN); TextEditInput *editWidget; if (_editor != nullptr) { editWidget = _editor; diff --git a/src/platform/android/jni/runtime.cpp b/src/platform/android/jni/runtime.cpp index 556dd2b1..b9ea8132 100644 --- a/src/platform/android/jni/runtime.cpp +++ b/src/platform/android/jni/runtime.cpp @@ -219,9 +219,9 @@ extern "C" JNIEXPORT void JNICALL Java_net_sourceforge_smallbasic_MainActivity_s } extern "C" JNIEXPORT void JNICALL Java_net_sourceforge_smallbasic_MainActivity_onResize - (JNIEnv *env, jclass jclazz, jint width, jint height) { + (JNIEnv *env, jclass jclazz, jint width, jint height, jint imeState) { if (runtime != nullptr && !runtime->isClosing() && runtime->isActive() && os_graphics) { - runtime->onResize(width, height); + runtime->onResize(width, height, imeState); } } @@ -258,7 +258,7 @@ extern "C" JNIEXPORT void JNICALL Java_net_sourceforge_smallbasic_MainActivity_c void onContentRectChanged(ANativeActivity *activity, const ARect *rect) { logEntered(); - runtime->onResize(rect->right, rect->bottom); + runtime->onResize(rect->right, rect->bottom, 0); } jbyteArray newByteArray(JNIEnv *env, const char *str) { @@ -619,7 +619,7 @@ void Runtime::loadConfig() { int height = getInteger("getWindowHeight"); if (height != _graphics->getHeight()) { // height adjustment for bottom virtual navigation bar - onResize(_graphics->getWidth(), height); + onResize(_graphics->getWidth(), height, 0); } _output->setTextColor(DEFAULT_FOREGROUND, DEFAULT_BACKGROUND); @@ -990,14 +990,18 @@ void Runtime::showKeypad(bool show) { _app->activity->vm->DetachCurrentThread(); } -void Runtime::onResize(int width, int height) { +void Runtime::onResize(int width, int height, int imeState) { logEntered(); if (_graphics != nullptr) { int w = _graphics->getWidth(); int h = _graphics->getHeight(); if (w != width || h != height) { - trace("Resized from %d %d to %d %d", w, h, width, height); + trace("Resized from %d %d to %d %d [ime=%d]", w, h, width, height, imeState); ALooper_acquire(_app->looper); + if (imeState != 0) { + // in android 16+ when resize also knows whether the ime (keypad) is active + _keypadActive = (imeState > 0); + } _graphics->setSize(width, height); auto *maEvent = new MAEvent(); maEvent->type = EVENT_TYPE_SCREEN_CHANGED; diff --git a/src/platform/android/jni/runtime.h b/src/platform/android/jni/runtime.h index 5c1c3cb8..fc99c09f 100644 --- a/src/platform/android/jni/runtime.h +++ b/src/platform/android/jni/runtime.h @@ -70,7 +70,7 @@ struct Runtime : public System { void showCursor(CursorType cursorType) override {} void showKeypad(bool show); void onPaused(bool paused) { if (_graphics != nullptr) _graphics->onPaused(paused); } - void onResize(int w, int h); + void onResize(int w, int h, int imeState); void onRunCompleted() override; void onUnicodeChar(int ch); void loadConfig(); diff --git a/src/ui/textedit.cpp b/src/ui/textedit.cpp index 08ac0cea..c28ae47e 100644 --- a/src/ui/textedit.cpp +++ b/src/ui/textedit.cpp @@ -2538,8 +2538,10 @@ void TextEditHelpWidget::layout(int x, int y, int w, int h) { case kPopup: _width = w - (_x + _xmargin); _height = h - (_y + _ymargin); - _x = _xBase; - _y = _yBase; +#if !defined(_ANDROID) + _x = _xBase; + _y = _yBase; +#endif } } } From 45ebb9ed632cc30708dcfb9805a41c7c9903be82 Mon Sep 17 00:00:00 2001 From: Chris Warren-Smith Date: Mon, 22 Sep 2025 19:28:11 +0930 Subject: [PATCH 4/5] ANDROID: implemented support for editing with the system keypad - Fixes for Pixel 9/Android 16 --- src/platform/android/app/build.gradle | 2 +- .../net/sourceforge/smallbasic/MainActivity.java | 12 +++++++----- src/platform/android/jni/display.cpp | 4 ++-- src/ui/canvas.h | 4 ++-- 4 files changed, 12 insertions(+), 10 deletions(-) diff --git a/src/platform/android/app/build.gradle b/src/platform/android/app/build.gradle index 44366ffe..0548f8ac 100644 --- a/src/platform/android/app/build.gradle +++ b/src/platform/android/app/build.gradle @@ -9,7 +9,7 @@ android { applicationId 'net.sourceforge.smallbasic' minSdkVersion 21 targetSdkVersion 36 - versionCode 85 + versionCode 87 versionName '12.31' resourceConfigurations += ['en'] } diff --git a/src/platform/android/app/src/main/java/net/sourceforge/smallbasic/MainActivity.java b/src/platform/android/app/src/main/java/net/sourceforge/smallbasic/MainActivity.java index 23f4dfdb..3dc2f5d4 100644 --- a/src/platform/android/app/src/main/java/net/sourceforge/smallbasic/MainActivity.java +++ b/src/platform/android/app/src/main/java/net/sourceforge/smallbasic/MainActivity.java @@ -1084,16 +1084,18 @@ private void setupInsetBaseOnResize() { @NonNull @Override public WindowInsetsCompat onApplyWindowInsets(@NonNull View view, @NonNull WindowInsetsCompat insets) { - Log.d(TAG, "onApplyWindowInsets"); Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars()); Insets navInsets = insets.getInsets(WindowInsetsCompat.Type.navigationBars()); Insets ime = insets.getInsets(WindowInsetsCompat.Type.ime()); boolean imeVisible = insets.isVisible(WindowInsetsCompat.Type.ime()); - view.setPadding(0, 0, navInsets.right, navInsets.bottom); int bottomInset = Math.max(systemBars.bottom, ime.bottom); - int width = view.getWidth(); - int height = view.getHeight() - systemBars.top - bottomInset; - onResize(width, height, imeVisible ? 1 : -1); + int width = view.getWidth() - navInsets.right; + int height = view.getHeight() - (systemBars.top + bottomInset); + view.setPadding(0, 0, navInsets.right, navInsets.bottom); + if (width > 0 && height > 0) { + // ignore spurious transitional values + onResize(width, height, imeVisible ? 1 : -1); + } return insets; } }); diff --git a/src/platform/android/jni/display.cpp b/src/platform/android/jni/display.cpp index b4833d34..0bb55d1e 100644 --- a/src/platform/android/jni/display.cpp +++ b/src/platform/android/jni/display.cpp @@ -53,7 +53,7 @@ bool Canvas::create(int w, int h) { return result; } -void Canvas::drawRegion(Canvas *src, const MARect *srcRect, int destX, int destY) { +void Canvas::drawRegion(Canvas *src, const MARect *srcRect, int destX, int destY) const { int srcH = srcRect->height; if (srcRect->top + srcRect->height > src->_h) { srcH = src->_h - srcRect->top; @@ -65,7 +65,7 @@ void Canvas::drawRegion(Canvas *src, const MARect *srcRect, int destX, int destY } } -void Canvas::fillRect(int left, int top, int width, int height, pixel_t drawColor) { +void Canvas::fillRect(int left, int top, int width, int height, pixel_t drawColor) const { int dtX = x(); int dtY = y(); uint8_t dR, dG, dB; diff --git a/src/ui/canvas.h b/src/ui/canvas.h index 7bdbea6c..842b5ccf 100644 --- a/src/ui/canvas.h +++ b/src/ui/canvas.h @@ -46,8 +46,8 @@ struct Canvas { virtual ~Canvas(); bool create(int w, int h); - void drawRegion(Canvas *src, const MARect *srcRect, int dstx, int dsty); - void fillRect(int x, int y, int w, int h, pixel_t color); + void drawRegion(Canvas *src, const MARect *srcRect, int dstx, int dsty) const; + void fillRect(int x, int y, int w, int h, pixel_t color) const; void setClip(int x, int y, int w, int h); pixel_t *getLine(int y) const { return _pixels + (y * _w); } int x() const { return _clip ? _clip->left : 0; } From 3803de334913707f32dc2ef8f8aab5aadca7dcfd Mon Sep 17 00:00:00 2001 From: Chris Warren-Smith Date: Sat, 4 Oct 2025 09:11:08 +0930 Subject: [PATCH 5/5] ANDROID: setup to save debug files for crash investigations. --- src/platform/android/Makefile.am | 17 +++++++++++++++++ src/platform/android/app/build.gradle | 2 +- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/platform/android/Makefile.am b/src/platform/android/Makefile.am index d37204d3..5b8e45b2 100644 --- a/src/platform/android/Makefile.am +++ b/src/platform/android/Makefile.am @@ -5,6 +5,11 @@ # Download the GNU Public License (GPL) from www.gnu.org # +# +# For investigating crash reports in play-studio, for example: +# aarch64-linux-android-addr2line -e symbols/v12.31-87/arm64-v8a/libsmallbasic.so.dbg -f -C 0x00000000000d2dd0 +# + all-am: Makefile build-test: ndk-build-test @@ -32,6 +37,18 @@ ndk-build-release: release: (./gradlew clean :app:bundle) + @echo "Saving debug symbols..." + @VERSION_CODE=$$(sed -n 's/^[[:space:]]*versionCode[[:space:]]*\([0-9]*\).*/\1/p' app/build.gradle); \ + VERSION_NAME=$$(sed -n "s/^[[:space:]]*versionName[[:space:]]*['\"]\\([^'\"]*\\)['\"].*/\\1/p" app/build.gradle); \ + SYMBOL_DIR="symbols/v$${VERSION_NAME}-$${VERSION_CODE}"; \ + mkdir -p "$${SYMBOL_DIR}"; \ + if [ -d app/build/intermediates/native_debug_metadata/release/extractReleaseNativeDebugMetadata/out ]; then \ + cp -r app/build/intermediates/native_debug_metadata/release/extractReleaseNativeDebugMetadata/out/* "$${SYMBOL_DIR}/"; \ + echo "Symbols saved to $${SYMBOL_DIR}/"; \ + ls -lh "$${SYMBOL_DIR}"/*/*.dbg; \ + else \ + echo "Warning: debug symbols not found"; \ + fi library: (./gradlew clean assemble) diff --git a/src/platform/android/app/build.gradle b/src/platform/android/app/build.gradle index 0548f8ac..0f6581d5 100644 --- a/src/platform/android/app/build.gradle +++ b/src/platform/android/app/build.gradle @@ -9,7 +9,7 @@ android { applicationId 'net.sourceforge.smallbasic' minSdkVersion 21 targetSdkVersion 36 - versionCode 87 + versionCode 88 versionName '12.31' resourceConfigurations += ['en'] }