Skip to content

Commit 53595d6

Browse files
authored
Merge pull request #253 from chrisws/12_29
12 29
2 parents 2e58092 + 3803de3 commit 53595d6

File tree

15 files changed

+143
-37
lines changed

15 files changed

+143
-37
lines changed

ChangeLog

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
1+
2025-09-11 (12.31)
2+
ANDROID: Implemented support for editing with the system keypad
3+
4+
2025-08-31 (12.30)
5+
ANDROID: Implemented a replacement editor keypad
6+
ANDROID: Implemented editor find command
7+
ANDROID: Updated editor help
8+
19
2025-04-26 (12.29)
210
ANDROID: minor fix for PEN(3)
311

configure.ac

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ dnl This program is distributed under the terms of the GPL v2.0
77
dnl Download the GNU Public License (GPL) from www.gnu.org
88
dnl
99

10-
AC_INIT([smallbasic], [12.30])
10+
AC_INIT([smallbasic], [12.31])
1111
AC_CONFIG_SRCDIR([configure.ac])
1212

1313
AC_CANONICAL_TARGET

src/platform/android/Makefile.am

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,11 @@
55
# Download the GNU Public License (GPL) from www.gnu.org
66
#
77

8+
#
9+
# For investigating crash reports in play-studio, for example:
10+
# aarch64-linux-android-addr2line -e symbols/v12.31-87/arm64-v8a/libsmallbasic.so.dbg -f -C 0x00000000000d2dd0
11+
#
12+
813
all-am: Makefile
914

1015
build-test: ndk-build-test
@@ -32,6 +37,18 @@ ndk-build-release:
3237

3338
release:
3439
(./gradlew clean :app:bundle)
40+
@echo "Saving debug symbols..."
41+
@VERSION_CODE=$$(sed -n 's/^[[:space:]]*versionCode[[:space:]]*\([0-9]*\).*/\1/p' app/build.gradle); \
42+
VERSION_NAME=$$(sed -n "s/^[[:space:]]*versionName[[:space:]]*['\"]\\([^'\"]*\\)['\"].*/\\1/p" app/build.gradle); \
43+
SYMBOL_DIR="symbols/v$${VERSION_NAME}-$${VERSION_CODE}"; \
44+
mkdir -p "$${SYMBOL_DIR}"; \
45+
if [ -d app/build/intermediates/native_debug_metadata/release/extractReleaseNativeDebugMetadata/out ]; then \
46+
cp -r app/build/intermediates/native_debug_metadata/release/extractReleaseNativeDebugMetadata/out/* "$${SYMBOL_DIR}/"; \
47+
echo "Symbols saved to $${SYMBOL_DIR}/"; \
48+
ls -lh "$${SYMBOL_DIR}"/*/*.dbg; \
49+
else \
50+
echo "Warning: debug symbols not found"; \
51+
fi
3552

3653
library:
3754
(./gradlew clean assemble)

src/platform/android/app/build.gradle

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ android {
99
applicationId 'net.sourceforge.smallbasic'
1010
minSdkVersion 21
1111
targetSdkVersion 36
12-
versionCode 83
13-
versionName '12.30'
12+
versionCode 88
13+
versionName '12.31'
1414
resourceConfigurations += ['en']
1515
}
1616

src/platform/android/app/src/main/java/net/sourceforge/smallbasic/MainActivity.java

Lines changed: 48 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,15 @@
3939
import android.window.OnBackInvokedCallback;
4040
import android.window.OnBackInvokedDispatcher;
4141

42+
import androidx.annotation.NonNull;
4243
import androidx.annotation.RequiresPermission;
4344
import androidx.core.app.ActivityCompat;
4445
import androidx.core.content.ContextCompat;
4546
import androidx.core.content.FileProvider;
47+
import androidx.core.graphics.Insets;
48+
import androidx.core.view.OnApplyWindowInsetsListener;
49+
import androidx.core.view.ViewCompat;
50+
import androidx.core.view.WindowInsetsCompat;
4651

4752
import java.io.BufferedReader;
4853
import java.io.BufferedWriter;
@@ -121,7 +126,7 @@ public class MainActivity extends NativeActivity {
121126
public static native boolean libraryMode();
122127
public static native void onActivityPaused(boolean paused);
123128
public static native void onBack();
124-
public static native void onResize(int width, int height);
129+
public static native void onResize(int width, int height, int imeState);
125130
public static native void onUnicodeChar(int ch);
126131
public static native boolean optionSelected(int index);
127132
public static native void runFile(String fileName);
@@ -429,6 +434,10 @@ public int getWindowHeight() {
429434
return rect.height();
430435
}
431436

437+
public boolean isInsetBasedOnResize() {
438+
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.BAKLAVA;
439+
}
440+
432441
public boolean isPredictiveBack() {
433442
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.BAKLAVA;
434443
}
@@ -452,10 +461,11 @@ public boolean loadModules() {
452461
@Override
453462
public void onGlobalLayout() {
454463
super.onGlobalLayout();
455-
// find the visible coordinates of our view
456-
Rect rect = new Rect();
457-
findViewById(android.R.id.content).getWindowVisibleDisplayFrame(rect);
458-
onResize(rect.width(), rect.height());
464+
if (!isInsetBasedOnResize()) {
465+
Rect rect = new Rect();
466+
findViewById(android.R.id.content).getWindowVisibleDisplayFrame(rect);
467+
onResize(rect.width(), rect.height(), 0);
468+
}
459469
}
460470

461471
@Override
@@ -774,6 +784,7 @@ protected void onCreate(Bundle savedInstanceState) {
774784
setImmersiveMode();
775785
setupStorageEnvironment();
776786
setupPredictiveBack();
787+
setupInsetBaseOnResize();
777788
if (!libraryMode()) {
778789
processIntent();
779790
processSettings();
@@ -1063,14 +1074,42 @@ private void setImmersiveMode() {
10631074
}
10641075
}
10651076

1077+
/**
1078+
* onResize() handler for android 16+
1079+
*/
1080+
private void setupInsetBaseOnResize() {
1081+
if (isInsetBasedOnResize()) {
1082+
View view = getWindow().getDecorView();
1083+
ViewCompat.setOnApplyWindowInsetsListener(view, new OnApplyWindowInsetsListener() {
1084+
@NonNull
1085+
@Override
1086+
public WindowInsetsCompat onApplyWindowInsets(@NonNull View view, @NonNull WindowInsetsCompat insets) {
1087+
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
1088+
Insets navInsets = insets.getInsets(WindowInsetsCompat.Type.navigationBars());
1089+
Insets ime = insets.getInsets(WindowInsetsCompat.Type.ime());
1090+
boolean imeVisible = insets.isVisible(WindowInsetsCompat.Type.ime());
1091+
int bottomInset = Math.max(systemBars.bottom, ime.bottom);
1092+
int width = view.getWidth() - navInsets.right;
1093+
int height = view.getHeight() - (systemBars.top + bottomInset);
1094+
view.setPadding(0, 0, navInsets.right, navInsets.bottom);
1095+
if (width > 0 && height > 0) {
1096+
// ignore spurious transitional values
1097+
onResize(width, height, imeVisible ? 1 : -1);
1098+
}
1099+
return insets;
1100+
}
1101+
});
1102+
view.post(() -> ViewCompat.requestApplyInsets(view));
1103+
}
1104+
}
1105+
10661106
//
10671107
// Hook into Predictive Back (Android 13+)
10681108
//
10691109
private void setupPredictiveBack() {
10701110
if (isPredictiveBack()) {
10711111
getOnBackInvokedDispatcher().registerOnBackInvokedCallback(
1072-
OnBackInvokedDispatcher.PRIORITY_DEFAULT,
1073-
new OnBackInvokedCallback() {
1112+
OnBackInvokedDispatcher.PRIORITY_OVERLAY, new OnBackInvokedCallback() {
10741113
@Override
10751114
public void onBackInvoked() {
10761115
Log.d(TAG, "onBackInvoked");
@@ -1226,7 +1265,8 @@ protected Response getFile(String remoteHost, String path, boolean asset) throws
12261265
String name = "webui/" + path;
12271266
long length = getFileLength(name);
12281267
log("Opened " + name + " " + length + " bytes");
1229-
String contentType = path.endsWith("js") ? "text/javascript" : "text/html";
1268+
String contentType = path.endsWith("js") ? "text/javascript" :
1269+
path.endsWith("css") ? "text/css": "text/html";
12301270
result = new Response(getAssets().open(name), length, contentType);
12311271
if ("index.html".equals(path) && isHostNotPermitted(remoteHost)) {
12321272
requestHostPermission(remoteHost);

src/platform/android/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ buildscript {
55
mavenCentral()
66
}
77
dependencies {
8-
classpath 'com.android.tools.build:gradle:8.12.1'
8+
classpath 'com.android.tools.build:gradle:8.13.0'
99
}
1010
}
1111

src/platform/android/jni/display.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ bool Canvas::create(int w, int h) {
5353
return result;
5454
}
5555

56-
void Canvas::drawRegion(Canvas *src, const MARect *srcRect, int destX, int destY) {
56+
void Canvas::drawRegion(Canvas *src, const MARect *srcRect, int destX, int destY) const {
5757
int srcH = srcRect->height;
5858
if (srcRect->top + srcRect->height > src->_h) {
5959
srcH = src->_h - srcRect->top;
@@ -65,7 +65,7 @@ void Canvas::drawRegion(Canvas *src, const MARect *srcRect, int destX, int destY
6565
}
6666
}
6767

68-
void Canvas::fillRect(int left, int top, int width, int height, pixel_t drawColor) {
68+
void Canvas::fillRect(int left, int top, int width, int height, pixel_t drawColor) const {
6969
int dtX = x();
7070
int dtY = y();
7171
uint8_t dR, dG, dB;

src/platform/android/jni/editor.cpp

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ struct StatusMessage {
102102
out->setStatus(message);
103103
}
104104

105-
private:
105+
private:
106106
bool _dirty;
107107
bool _find;
108108
int _scroll;
@@ -126,7 +126,6 @@ void showHelp(TextEditHelpWidget *helpWidget) {
126126
void Runtime::editSource(strlib::String loadPath, bool restoreOnExit) {
127127
logEntered();
128128

129-
showKeypad(false);
130129
int w = _output->getWidth();
131130
int h = _output->getHeight();
132131
int charWidth = _output->getCharWidth();
@@ -153,11 +152,16 @@ void Runtime::editSource(strlib::String loadPath, bool restoreOnExit) {
153152
_output->addInput(editWidget);
154153
_output->addInput(helpWidget);
155154

156-
if (_keypad != nullptr) {
157-
_output->addInput(_keypad);
155+
if (opt_ide == IDE_EXTERNAL) {
156+
showKeypad(true);
158157
} else {
159-
_keypad = new KeypadInput(w, false, false, charWidth, charHeight);
160-
_output->addInput(_keypad);
158+
showKeypad(false);
159+
if (_keypad != nullptr) {
160+
_output->addInput(_keypad);
161+
} else {
162+
_keypad = new KeypadInput(w, false, false, charWidth, charHeight);
163+
_output->addInput(_keypad);
164+
}
161165
}
162166

163167
statusMessage.update(editWidget, _output);
@@ -207,6 +211,7 @@ void Runtime::editSource(strlib::String loadPath, bool restoreOnExit) {
207211
} else {
208212
widget = helpWidget;
209213
showHelp(helpWidget);
214+
showKeypad(false);
210215
}
211216
break;
212217
case SB_KEY_F(9):
@@ -258,6 +263,9 @@ void Runtime::editSource(strlib::String loadPath, bool restoreOnExit) {
258263
}
259264

260265
if ((exitHelp || isBack()) && widget == helpWidget) {
266+
if (opt_ide == IDE_EXTERNAL) {
267+
showKeypad(true);
268+
}
261269
widget = editWidget;
262270
helpWidget->hide();
263271
editWidget->setFocus(true);
@@ -293,6 +301,7 @@ void Runtime::editSource(strlib::String loadPath, bool restoreOnExit) {
293301
if (!_output->removeInput(_keypad)) {
294302
trace("Failed to remove keypad input");
295303
}
304+
showKeypad(false);
296305
_editor = editWidget;
297306
_editor->setFocus(false);
298307
} else {

src/platform/android/jni/runtime.cpp

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -219,9 +219,9 @@ extern "C" JNIEXPORT void JNICALL Java_net_sourceforge_smallbasic_MainActivity_s
219219
}
220220

221221
extern "C" JNIEXPORT void JNICALL Java_net_sourceforge_smallbasic_MainActivity_onResize
222-
(JNIEnv *env, jclass jclazz, jint width, jint height) {
222+
(JNIEnv *env, jclass jclazz, jint width, jint height, jint imeState) {
223223
if (runtime != nullptr && !runtime->isClosing() && runtime->isActive() && os_graphics) {
224-
runtime->onResize(width, height);
224+
runtime->onResize(width, height, imeState);
225225
}
226226
}
227227

@@ -258,7 +258,7 @@ extern "C" JNIEXPORT void JNICALL Java_net_sourceforge_smallbasic_MainActivity_c
258258

259259
void onContentRectChanged(ANativeActivity *activity, const ARect *rect) {
260260
logEntered();
261-
runtime->onResize(rect->right, rect->bottom);
261+
runtime->onResize(rect->right, rect->bottom, 0);
262262
}
263263

264264
jbyteArray newByteArray(JNIEnv *env, const char *str) {
@@ -619,7 +619,7 @@ void Runtime::loadConfig() {
619619
int height = getInteger("getWindowHeight");
620620
if (height != _graphics->getHeight()) {
621621
// height adjustment for bottom virtual navigation bar
622-
onResize(_graphics->getWidth(), height);
622+
onResize(_graphics->getWidth(), height, 0);
623623
}
624624

625625
_output->setTextColor(DEFAULT_FOREGROUND, DEFAULT_BACKGROUND);
@@ -990,14 +990,18 @@ void Runtime::showKeypad(bool show) {
990990
_app->activity->vm->DetachCurrentThread();
991991
}
992992

993-
void Runtime::onResize(int width, int height) {
993+
void Runtime::onResize(int width, int height, int imeState) {
994994
logEntered();
995995
if (_graphics != nullptr) {
996996
int w = _graphics->getWidth();
997997
int h = _graphics->getHeight();
998998
if (w != width || h != height) {
999-
trace("Resized from %d %d to %d %d", w, h, width, height);
999+
trace("Resized from %d %d to %d %d [ime=%d]", w, h, width, height, imeState);
10001000
ALooper_acquire(_app->looper);
1001+
if (imeState != 0) {
1002+
// in android 16+ when resize also knows whether the ime (keypad) is active
1003+
_keypadActive = (imeState > 0);
1004+
}
10011005
_graphics->setSize(width, height);
10021006
auto *maEvent = new MAEvent();
10031007
maEvent->type = EVENT_TYPE_SCREEN_CHANGED;

src/platform/android/jni/runtime.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ struct Runtime : public System {
7070
void showCursor(CursorType cursorType) override {}
7171
void showKeypad(bool show);
7272
void onPaused(bool paused) { if (_graphics != nullptr) _graphics->onPaused(paused); }
73-
void onResize(int w, int h);
73+
void onResize(int w, int h, int imeState);
7474
void onRunCompleted() override;
7575
void onUnicodeChar(int ch);
7676
void loadConfig();

0 commit comments

Comments
 (0)