Own Android platform services and narrow legacy fallback

This commit is contained in:
2026-06-17 14:53:49 +02:00
parent 74abddd81e
commit 1e9235cb6a
11 changed files with 417 additions and 109 deletions

View File

@@ -286,6 +286,18 @@ target_link_libraries(pp_platform_linux
PRIVATE PRIVATE
pp_project_warnings) pp_project_warnings)
add_library(pp_platform_android STATIC
${PP_PLATFORM_ANDROID_SOURCES})
target_include_directories(pp_platform_android
PUBLIC
"${CMAKE_CURRENT_SOURCE_DIR}/src")
target_link_libraries(pp_platform_android
PUBLIC
pp_platform_api
pp_project_options
PRIVATE
pp_project_warnings)
add_library(pp_app_core STATIC add_library(pp_app_core STATIC
src/app_core/about_menu.h src/app_core/about_menu.h
src/app_core/app_dialog.h src/app_core/app_dialog.h

View File

@@ -31,8 +31,7 @@
#include "pch.h" #include "pch.h"
#include "app.h" #include "app.h"
#include "platform_legacy/legacy_platform_services.h" #include "platform_android/android_platform_services.h"
#include "platform_legacy/legacy_platform_state.h"
#include "asset.h" #include "asset.h"
#include "keymap.h" #include "keymap.h"
#include "main.h" #include "main.h"
@@ -1092,8 +1091,8 @@ void android_main(struct android_app* state) {
// Make sure glue isn't stripped. // Make sure glue isn't stripped.
// DON'T REMOVE, even if the compiler say it's deprecated // DON'T REMOVE, even if the compiler say it's deprecated
app_dummy(); app_dummy();
auto platform_services = pp::platform::legacy::create_platform_services({ auto platform_services = pp::platform::android::create_platform_services({
.android_bridge = { .bridge = {
.clipboard_text = [] { return android_get_clipboard(); }, .clipboard_text = [] { return android_get_clipboard(); },
.set_clipboard_text = [](std::string_view text) { .set_clipboard_text = [](std::string_view text) {
return android_set_clipboard(std::string(text)); return android_set_clipboard(std::string(text));
@@ -1111,7 +1110,7 @@ void android_main(struct android_app* state) {
android_pick_file_save(std::move(callback)); android_pick_file_save(std::move(callback));
}, },
}, },
.android_storage_paths = g_android_storage_paths, .storage_paths = g_android_storage_paths,
}); });
App::I = new App; App::I = new App;

View File

@@ -117,6 +117,11 @@ set(PP_PLATFORM_LINUX_SOURCES
src/platform_linux/linux_platform_services.h src/platform_linux/linux_platform_services.h
) )
set(PP_PLATFORM_ANDROID_SOURCES
src/platform_android/android_platform_services.cpp
src/platform_android/android_platform_services.h
)
set(PP_PLATFORM_APPLE_SOURCES set(PP_PLATFORM_APPLE_SOURCES
src/platform_apple/apple_platform_state.cpp src/platform_apple/apple_platform_state.cpp
src/platform_apple/apple_platform_services.cpp src/platform_apple/apple_platform_services.cpp

View File

@@ -18,6 +18,13 @@ agent or engineer to remove them without reconstructing context from chat.
## Reductions ## Reductions
- 2026-06-17: `DEBT-0016`/`DEBT-0017` were narrowed again.
`src/platform_android/android_platform_services.*` now owns the concrete
Android `PlatformServices` implementation and
`android/src/cpp/main.cpp` now binds that owned service directly, so
`src/platform_legacy/legacy_platform_services.*` no longer carries the
Android bridge/configuration surface or the touched Android execution
branches.
- 2026-06-17: `DEBT-0016`/`DEBT-0017`/`DEBT-0050`/`DEBT-0051`/`DEBT-0052`/ - 2026-06-17: `DEBT-0016`/`DEBT-0017`/`DEBT-0050`/`DEBT-0051`/`DEBT-0052`/
`DEBT-0053` were narrowed again. `src/platform_apple/apple_platform_services.*` `DEBT-0053` were narrowed again. `src/platform_apple/apple_platform_services.*`
now owns the concrete Apple `PlatformServices` implementation and now owns the concrete Apple `PlatformServices` implementation and
@@ -2397,8 +2404,8 @@ agent or engineer to remove them without reconstructing context from chat.
| DEBT-0013 | Open | Modernization | `pp_assets`, `pp_document`, `pano_cli inspect-project`, `pano_cli load-project`, and `pano_cli save-project` validate the fixed PPI header, thumbnail/body byte layout, generated multi-layer/multi-frame PPI writing with explicit layer opacity/blend/alpha-lock/visibility metadata, per-layer frame durations, metadata-only and targeted dirty-face-payload save/load round-trips, layer/frame index, dirty-face descriptors, dirty-face PNG payload metadata, asset-level RGBA PNG payload decoding, pure document-to-PPI export, CLI document export automation, file-writing document export automation, stroke-script-generated document payload export, decoded pixel attachment to `pp_document`, live save-path snapshot-readiness reporting, and app-core canvas-snapshot-to-PPI export automation, but full legacy PPI round-trip parity and pure live save writer replacement are not yet extracted | Full PPI save parity requires staged extraction of legacy `Canvas` serialization and image/layer payload handling | `ctest --preset desktop-fast --build-config Debug`; `pp_assets_image_pixels_tests`; `pp_assets_ppi_header_tests`; `pp_document_ppi_import_tests`; `pp_document_ppi_export_tests`; `pano_cli_inspect_project_layout_smoke`; `pano_cli_load_project_metadata_smoke`; `pano_cli_save_project_roundtrip_smoke`; `pano_cli_save_project_payload_roundtrip_smoke`; `pano_cli_simulate_document_export_smoke`; `pano_cli_save_document_project_roundtrip_smoke`; `pano_cli_apply_stroke_script_roundtrip_smoke`; `pano_cli_apply_stroke_script_rejects_tiny_canvas` | Full PPI load/save fixtures cover thumbnails, decoded layer face payloads attached to documents, frames, corrupt payloads, dirty-face payload saving, arbitrary legacy canvas payload/layout combinations, and legacy app round-trip compatibility | | DEBT-0013 | Open | Modernization | `pp_assets`, `pp_document`, `pano_cli inspect-project`, `pano_cli load-project`, and `pano_cli save-project` validate the fixed PPI header, thumbnail/body byte layout, generated multi-layer/multi-frame PPI writing with explicit layer opacity/blend/alpha-lock/visibility metadata, per-layer frame durations, metadata-only and targeted dirty-face-payload save/load round-trips, layer/frame index, dirty-face descriptors, dirty-face PNG payload metadata, asset-level RGBA PNG payload decoding, pure document-to-PPI export, CLI document export automation, file-writing document export automation, stroke-script-generated document payload export, decoded pixel attachment to `pp_document`, live save-path snapshot-readiness reporting, and app-core canvas-snapshot-to-PPI export automation, but full legacy PPI round-trip parity and pure live save writer replacement are not yet extracted | Full PPI save parity requires staged extraction of legacy `Canvas` serialization and image/layer payload handling | `ctest --preset desktop-fast --build-config Debug`; `pp_assets_image_pixels_tests`; `pp_assets_ppi_header_tests`; `pp_document_ppi_import_tests`; `pp_document_ppi_export_tests`; `pano_cli_inspect_project_layout_smoke`; `pano_cli_load_project_metadata_smoke`; `pano_cli_save_project_roundtrip_smoke`; `pano_cli_save_project_payload_roundtrip_smoke`; `pano_cli_simulate_document_export_smoke`; `pano_cli_save_document_project_roundtrip_smoke`; `pano_cli_apply_stroke_script_roundtrip_smoke`; `pano_cli_apply_stroke_script_rejects_tiny_canvas` | Full PPI load/save fixtures cover thumbnails, decoded layer face payloads attached to documents, frames, corrupt payloads, dirty-face payload saving, arbitrary legacy canvas payload/layout combinations, and legacy app round-trip compatibility |
| DEBT-0014 | Open | Modernization | `windows-clangcl-asan` now configures as a headless Ninja/clang-cl preset and uses the release MSVC runtime required by ASan, but local builds still fail because installed clang-cl 18.1.8 is paired with VS 2026-preview STL headers that require Clang 20 or newer | Sanitizer validation should be local and repeatable, but this machine's compiler/header pairing is incompatible | `cmake --fresh --preset windows-clangcl-asan`; `cmake --build --preset windows-clangcl-asan --target pp_foundation` | Install/use Clang 20+ with the VS 2026 STL, or point the preset at a compatible VS 2022 toolchain, then make `platform-build.ps1 -Presets windows-clangcl-asan` pass for the headless matrix | | DEBT-0014 | Open | Modernization | `windows-clangcl-asan` now configures as a headless Ninja/clang-cl preset and uses the release MSVC runtime required by ASan, but local builds still fail because installed clang-cl 18.1.8 is paired with VS 2026-preview STL headers that require Clang 20 or newer | Sanitizer validation should be local and repeatable, but this machine's compiler/header pairing is incompatible | `cmake --fresh --preset windows-clangcl-asan`; `cmake --build --preset windows-clangcl-asan --target pp_foundation` | Install/use Clang 20+ with the VS 2026 STL, or point the preset at a compatible VS 2022 toolchain, then make `platform-build.ps1 -Presets windows-clangcl-asan` pass for the headless matrix |
| DEBT-0015 | Open | Modernization | Cursor visibility requests now consume pure `pp_app_core` planning through `pano_cli plan-cursor-visibility`, `App::show_cursor`/`App::hide_cursor` dispatch through `PlatformServices` without platform guards, and Windows live execution uses injected `WindowsPlatformServices`, but macOS cursor execution still reaches the retained fallback adapter | Keep canvas cursor behavior stable while platform shells are extracted incrementally | `pp_app_core_document_platform_io_tests`; `pano_cli plan-cursor-visibility --visible`; `ctest --preset desktop-fast --build-config Debug` | Cursor visibility execution is owned by injected `pp_platform_*` services for every supported platform | | DEBT-0015 | Open | Modernization | Cursor visibility requests now consume pure `pp_app_core` planning through `pano_cli plan-cursor-visibility`, `App::show_cursor`/`App::hide_cursor` dispatch through `PlatformServices` without platform guards, and Windows live execution uses injected `WindowsPlatformServices`, but macOS cursor execution still reaches the retained fallback adapter | Keep canvas cursor behavior stable while platform shells are extracted incrementally | `pp_app_core_document_platform_io_tests`; `pano_cli plan-cursor-visibility --visible`; `ctest --preset desktop-fast --build-config Debug` | Cursor visibility execution is owned by injected `pp_platform_*` services for every supported platform |
| DEBT-0016 | Open | Modernization | Clipboard get/set requests now consume pure `pp_app_core` planning through `pano_cli plan-clipboard-read` and `pano_cli plan-clipboard-write`; Windows live execution uses injected `WindowsPlatformServices`, Apple clipboard execution now uses `src/platform_apple/apple_platform_services.*`, and Android clipboard execution still reaches the retained non-Windows fallback adapter | Keep picker/color text clipboard behavior stable while platform shells are extracted incrementally | `pp_app_core_document_platform_io_tests`; `pano_cli plan-clipboard-write --text #ff00aa`; `ctest --preset desktop-fast --build-config Debug` | Clipboard execution is owned by injected `pp_platform_*` services for every supported platform | | DEBT-0016 | Open | Modernization | Clipboard get/set requests now consume pure `pp_app_core` planning through `pano_cli plan-clipboard-read` and `pano_cli plan-clipboard-write`; Windows live execution uses injected `WindowsPlatformServices`, Apple clipboard execution now uses `src/platform_apple/apple_platform_services.*`, Android clipboard execution now uses `src/platform_android/android_platform_services.*`, and only Linux/Web fallback behavior still routes through the retained non-Windows adapter | Keep picker/color text clipboard behavior stable while platform shells are extracted incrementally | `pp_app_core_document_platform_io_tests`; `pano_cli plan-clipboard-write --text #ff00aa`; `ctest --preset desktop-fast --build-config Debug` | Clipboard execution is owned by injected `pp_platform_*` services for every supported platform |
| DEBT-0017 | Open | Modernization | Startup storage path preparation, `App::clipboard_get_text`, `App::clipboard_set_text`, `App::show_cursor`, `App::hide_cursor`, `App::showKeyboard`, `App::hideKeyboard`, `App::display_file`, `App::share_file`, native app/window close, UI-thread lifecycle hooks, render-context acquire/release/present hooks, render-target binding hooks, render platform hint hooks, render debug callback hooks, render-capture frame hooks, recording cleanup, live asset/layout reload policy, diagnostic stacktrace/crash hooks, per-frame platform hooks, `App::pick_image`, `App::pick_file`, the non-writer `App::pick_file_save`, `App::pick_dir`, working-directory picker/display-path policy, canvas input tip/pressure policy, prepared-file save/download handoff, work-directory document export collection policy, app network TLS verification policy, PPBR export data-directory policy, SonarPen availability/startup, and VR mode start/stop now call the SDK-free `pp::platform::PlatformServices` interface. Windows injects `WindowsPlatformServices` from `src/platform_windows/windows_platform_services.*`, Apple now injects `src/platform_apple/apple_platform_services.*`, and the retained non-Windows fallback adapter in `src/platform_legacy/legacy_platform_services.*` is narrowed to Android/Linux/Web bridge functions plus retained no-op branches, including retained iOS canvas tip behavior, retained non-Windows VR unsupported/no-op behavior, and retained macOS PPBR export directory override; `pp_platform_api` also owns the default network TLS policy helper consumed by retained curl sites that cannot yet depend on injected services | Preserve behavior while moving platform execution behind a testable service boundary before platform shell implementations are injected | `pp_platform_api_tests`; `pp_app_core_document_export_tests`; `pp_app_core_document_platform_io_tests`; `ctest --preset desktop-fast --build-config Debug`; `powershell -ExecutionPolicy Bypass -File scripts\automation\package-smoke.ps1 -Preset windows-msvc-default -Configuration Debug` | Replace `src/platform_legacy/legacy_platform_services.*` with injected `pp_platform_*` service implementations owned by each non-Windows platform shell | | DEBT-0017 | Open | Modernization | Startup storage path preparation, `App::clipboard_get_text`, `App::clipboard_set_text`, `App::show_cursor`, `App::hide_cursor`, `App::showKeyboard`, `App::hideKeyboard`, `App::display_file`, `App::share_file`, native app/window close, UI-thread lifecycle hooks, render-context acquire/release/present hooks, render-target binding hooks, render platform hint hooks, render debug callback hooks, render-capture frame hooks, recording cleanup, live asset/layout reload policy, diagnostic stacktrace/crash hooks, per-frame platform hooks, `App::pick_image`, `App::pick_file`, the non-writer `App::pick_file_save`, `App::pick_dir`, working-directory picker/display-path policy, canvas input tip/pressure policy, prepared-file save/download handoff, work-directory document export collection policy, app network TLS verification policy, PPBR export data-directory policy, SonarPen availability/startup, and VR mode start/stop now call the SDK-free `pp::platform::PlatformServices` interface. Windows injects `WindowsPlatformServices` from `src/platform_windows/windows_platform_services.*`, Apple now injects `src/platform_apple/apple_platform_services.*`, Android now injects `src/platform_android/android_platform_services.*`, and the retained non-Windows fallback adapter in `src/platform_legacy/legacy_platform_services.*` is narrowed to Linux/Web behavior plus retained no-op branches, including retained iOS canvas tip behavior, retained non-Windows VR unsupported/no-op behavior, and retained macOS PPBR export directory override; `pp_platform_api` also owns the default network TLS policy helper consumed by retained curl sites that cannot yet depend on injected services | Preserve behavior while moving platform execution behind a testable service boundary before platform shell implementations are injected | `pp_platform_api_tests`; `pp_app_core_document_export_tests`; `pp_app_core_document_platform_io_tests`; `ctest --preset desktop-fast --build-config Debug`; `powershell -ExecutionPolicy Bypass -File scripts\automation\package-smoke.ps1 -Preset windows-msvc-default -Configuration Debug` | Replace `src/platform_legacy/legacy_platform_services.*` with injected `pp_platform_*` service implementations owned by each non-Windows platform shell |
| DEBT-0019 | Open | Modernization | Unreferenced-parameter warnings are muted globally through `pp_project_warnings` with MSVC `/wd4100` and Clang/GCC `-Wno-unused-parameter` | Legacy callbacks, virtual hooks, serializer methods, and platform/API compatibility functions carry many intentionally unused parameters during the component split; muting this keeps stricter warning builds focused on higher-signal migration issues | `cmake --build --preset windows-msvc-default --config Debug --target PanoPainter`; `ctest --preset desktop-fast --build-config Debug`; `cmake --build --preset linux-clang --target pp_foundation` | Remove `/wd4100` and `-Wno-unused-parameter`, mark intentionally unused parameters with names/comments or `[[maybe_unused]]`, and make the Windows app plus headless Clang/GCC tests pass without unreferenced-parameter warnings | | DEBT-0019 | Open | Modernization | Unreferenced-parameter warnings are muted globally through `pp_project_warnings` with MSVC `/wd4100` and Clang/GCC `-Wno-unused-parameter` | Legacy callbacks, virtual hooks, serializer methods, and platform/API compatibility functions carry many intentionally unused parameters during the component split; muting this keeps stricter warning builds focused on higher-signal migration issues | `cmake --build --preset windows-msvc-default --config Debug --target PanoPainter`; `ctest --preset desktop-fast --build-config Debug`; `cmake --build --preset linux-clang --target pp_foundation` | Remove `/wd4100` and `-Wno-unused-parameter`, mark intentionally unused parameters with names/comments or `[[maybe_unused]]`, and make the Windows app plus headless Clang/GCC tests pass without unreferenced-parameter warnings |
| DEBT-0020 | Open | Modernization | Document resize dialog state, selected-resolution planning, and execution dispatch now consume pure `pp_app_core` through `NodeDialogResize`, `App::dialog_resize`, `pano_cli plan-document-resize`, and the `DocumentResizeServices` boundary, and live resize shares `src/legacy_document_canvas_services.*` with canvas clear commands; resize history clearing is an explicit app-core execution output implemented directly by the retained `ActionManager`, but the shared live bridge still calls legacy `Canvas::resize`, updates the legacy app title, and owns the retained `ActionManager` call site | Preserve existing layer/frame GPU resize behavior while the document model and canvas execution boundary are extracted incrementally | `pp_app_core_document_resize_tests`; `pano_cli plan-document-resize --current-resolution 2048 --selected-resolution-index 4`; `ctest --preset desktop-fast --build-config Debug` | Document resize execution is owned by injected document/app services with no legacy resize adapter, title shim, or retained `ActionManager` history clearing | | DEBT-0020 | Open | Modernization | Document resize dialog state, selected-resolution planning, and execution dispatch now consume pure `pp_app_core` through `NodeDialogResize`, `App::dialog_resize`, `pano_cli plan-document-resize`, and the `DocumentResizeServices` boundary, and live resize shares `src/legacy_document_canvas_services.*` with canvas clear commands; resize history clearing is an explicit app-core execution output implemented directly by the retained `ActionManager`, but the shared live bridge still calls legacy `Canvas::resize`, updates the legacy app title, and owns the retained `ActionManager` call site | Preserve existing layer/frame GPU resize behavior while the document model and canvas execution boundary are extracted incrementally | `pp_app_core_document_resize_tests`; `pano_cli plan-document-resize --current-resolution 2048 --selected-resolution-index 4`; `ctest --preset desktop-fast --build-config Debug` | Document resize execution is owned by injected document/app services with no legacy resize adapter, title shim, or retained `ActionManager` history clearing |
| DEBT-0021 | Open | Modernization | Layer rename planning/execution dispatch, layer panel operation planning/execution dispatch, layer panel selected-control/visibility view projection, and explicit layer history-intent helpers now consume pure `pp_app_core` through `App::dialog_layer_rename`, `App::init_sidebar` layer callbacks, `NodePanelLayer::update_attributes()`, `pano_cli plan-layer-rename`, `pano_cli plan-layer-operation`, `pano_cli plan-layer-panel-view`, `DocumentLayerRenameServices`, and `DocumentLayerOperationServices`, and the live execution adapters are centralized in `src/legacy_document_layer_services.*`; rename now records undo before applying the new name through separate app-core service calls, but the shared bridge and panel adapter still mutate legacy `Canvas` layer state, `NodeLayer`/`NodePanelLayer`, and retained `ActionManager` undo entries for add/remove/property-change/clear paths | Preserve existing UI/canvas behavior while document layer commands, panel projection, and undo history are extracted incrementally | `pp_app_core_document_layer_tests`; `pano_cli plan-layer-rename --old-name Base --new-name Paint`; `pano_cli plan-layer-operation --kind add --layer-count 2 --index 1 --name Paint`; `pano_cli plan-layer-panel-view --layer-count 3 --current-index 1 --hidden-index 2 --locked-index 1 --current-opacity 0.25 --current-blend-mode 4`; `ctest --preset desktop-fast --build-config Debug` | Layer command execution and panel state projection are owned by the document/app command boundary with legacy `Canvas`/UI nodes acting only as adapters or removed entirely | | DEBT-0021 | Open | Modernization | Layer rename planning/execution dispatch, layer panel operation planning/execution dispatch, layer panel selected-control/visibility view projection, and explicit layer history-intent helpers now consume pure `pp_app_core` through `App::dialog_layer_rename`, `App::init_sidebar` layer callbacks, `NodePanelLayer::update_attributes()`, `pano_cli plan-layer-rename`, `pano_cli plan-layer-operation`, `pano_cli plan-layer-panel-view`, `DocumentLayerRenameServices`, and `DocumentLayerOperationServices`, and the live execution adapters are centralized in `src/legacy_document_layer_services.*`; rename now records undo before applying the new name through separate app-core service calls, but the shared bridge and panel adapter still mutate legacy `Canvas` layer state, `NodeLayer`/`NodePanelLayer`, and retained `ActionManager` undo entries for add/remove/property-change/clear paths | Preserve existing UI/canvas behavior while document layer commands, panel projection, and undo history are extracted incrementally | `pp_app_core_document_layer_tests`; `pano_cli plan-layer-rename --old-name Base --new-name Paint`; `pano_cli plan-layer-operation --kind add --layer-count 2 --index 1 --name Paint`; `pano_cli plan-layer-panel-view --layer-count 3 --current-index 1 --hidden-index 2 --locked-index 1 --current-opacity 0.25 --current-blend-mode 4`; `ctest --preset desktop-fast --build-config Debug` | Layer command execution and panel state projection are owned by the document/app command boundary with legacy `Canvas`/UI nodes acting only as adapters or removed entirely |

View File

@@ -70,6 +70,20 @@ What is already real:
- `pp_app_core` - `pp_app_core`
Latest slice: Latest slice:
- `src/platform_android/android_platform_services.*` now owns the concrete
Android `PlatformServices` implementation and `create_platform_services()`
instead of leaving the live Android execution surface inside
`platform_legacy`.
- `android/src/cpp/main.cpp` now binds that owned Android `PlatformServices`
instance into `App` directly at the Android entrypoint.
- The touched Android clipboard, keyboard visibility, JNI thread
attach/detach, async render-context, and file-picker execution now route
through `src/platform_android/android_platform_services.*` instead of the
cross-platform fallback adapter.
- `src/platform_legacy/legacy_platform_services.*` no longer carries the
Android bridge/configuration surface or the touched Android method branches,
so the retained fallback adapter is narrower again and now focused on the
Linux/Web path plus generic fallback policy.
- `src/platform_apple/apple_platform_services.*` now owns the concrete Apple - `src/platform_apple/apple_platform_services.*` now owns the concrete Apple
`PlatformServices` implementation plus the `create_apple_platform_services()` `PlatformServices` implementation plus the `create_apple_platform_services()`
factory instead of leaving the live Apple execution surface inside factory instead of leaving the live Apple execution surface inside
@@ -86,15 +100,6 @@ Latest slice:
document-service provider/configuration surface or the touched Apple method document-service provider/configuration surface or the touched Apple method
branches, so the retained fallback adapter is narrower and now focused on the branches, so the retained fallback adapter is narrower and now focused on the
Android/Linux/Web path. Android/Linux/Web path.
- `src/platform_legacy/legacy_platform_services.*` now takes an explicit
Android bridge through `create_platform_services(...)` instead of declaring
Android JNI/EGL/clipboard/file-picker hooks directly inside the legacy
adapter.
- `android/src/cpp/main.cpp` now seeds that Android-owned bridge into its
owned legacy `PlatformServices` instance.
- The touched Android clipboard, keyboard visibility, JNI thread attach/detach,
async render-context, and file-picker ownership now sit at the Android
entrypoint instead of the cross-platform fallback adapter body.
- `src/platform_legacy/legacy_platform_services.*` now takes an explicit - `src/platform_legacy/legacy_platform_services.*` now takes an explicit
`WebPlatformServices*` dependency through `create_platform_services(...)` `WebPlatformServices*` dependency through `create_platform_services(...)`
instead of routing WebGL publish/flush/default-canvas/save-prepared-file instead of routing WebGL publish/flush/default-canvas/save-prepared-file

View File

@@ -51,8 +51,9 @@ Completed, blocked, and superseded task history moved to
`src/platform_apple/apple_platform_state.cpp` and `src/platform_apple/apple_platform_state.cpp` and
`src/platform_apple/apple_platform_services.*`, and the live Apple `src/platform_apple/apple_platform_services.*`, and the live Apple
`PlatformServices` surface now binds directly from those Apple-owned files, `PlatformServices` surface now binds directly from those Apple-owned files,
but the broader non-Windows fallback adapter still exists for and Android now also binds directly from
Android/Linux/Web `src/platform_android/android_platform_services.*`, but the broader
non-Windows fallback adapter still exists for Linux/Web
- `platform_legacy` is still part of the live app shell - `platform_legacy` is still part of the live app shell
- The app runtime boundary is not finished: - The app runtime boundary is not finished:
- render/UI queues are static `App` state - render/UI queues are static `App` state
@@ -80,6 +81,20 @@ Completed, blocked, and superseded task history moved to
the queue is now ordered by code movement instead. the queue is now ordered by code movement instead.
Current slice: Current slice:
- `src/platform_android/android_platform_services.*` now owns the concrete
Android `PlatformServices` implementation and `create_platform_services()`
instead of leaving the live Android execution surface in
`platform_legacy`.
- `android/src/cpp/main.cpp` now binds that owned Android `PlatformServices`
instance into `App` directly at the Android entrypoint.
- The touched Android clipboard, keyboard visibility, JNI thread
attach/detach, async render-context, and file-picker execution now route
through `src/platform_android/android_platform_services.*` instead of the
cross-platform fallback adapter.
- `src/platform_legacy/legacy_platform_services.*` no longer carries the
Android bridge/configuration surface or the touched Android method branches,
so the retained fallback adapter is narrower again and now focused on the
Linux/Web path plus generic fallback policy.
- `src/platform_apple/apple_platform_services.*` now owns the concrete Apple - `src/platform_apple/apple_platform_services.*` now owns the concrete Apple
`PlatformServices` implementation plus the `create_apple_platform_services()` `PlatformServices` implementation plus the `create_apple_platform_services()`
factory instead of leaving the live Apple execution surface in factory instead of leaving the live Apple execution surface in
@@ -96,15 +111,6 @@ Current slice:
document-service provider/configuration surface or the touched Apple method document-service provider/configuration surface or the touched Apple method
branches, so the retained fallback adapter is narrower and now focused on the branches, so the retained fallback adapter is narrower and now focused on the
Android/Linux/Web path. Android/Linux/Web path.
- `src/platform_legacy/legacy_platform_services.*` now takes an explicit
Android bridge through `create_platform_services(...)` instead of declaring
Android JNI/EGL/clipboard/file-picker hooks directly inside the legacy
adapter.
- `android/src/cpp/main.cpp` now seeds that Android-owned bridge into its
owned legacy `PlatformServices` instance.
- The touched Android clipboard, keyboard visibility, JNI thread attach/detach,
async render-context, and file-picker ownership now sit at the Android
entrypoint instead of the cross-platform fallback adapter body.
- `src/platform_legacy/legacy_platform_services.*` now takes an explicit - `src/platform_legacy/legacy_platform_services.*` now takes an explicit
`WebPlatformServices*` dependency through `create_platform_services(...)` `WebPlatformServices*` dependency through `create_platform_services(...)`
instead of routing WebGL publish/flush/default-canvas/save-prepared-file instead of routing WebGL publish/flush/default-canvas/save-prepared-file

View File

@@ -0,0 +1,316 @@
#include "platform_android/android_platform_services.h"
#include "platform_api/network_tls_policy.h"
#include "platform_api/platform_policy.h"
#include <memory>
#include <utility>
#if defined(__ANDROID__)
#include <GLES3/gl3.h>
#endif
namespace pp::platform::android {
namespace {
constexpr auto kPlatformFamily = pp::platform::PlatformFamily::android;
class AndroidPlatformServices final : public pp::platform::PlatformServices {
public:
explicit AndroidPlatformServices(AndroidPlatformServicesConfig config)
: bridge_(std::move(config.bridge))
, storage_paths_(std::move(config.storage_paths))
{
}
[[nodiscard]] PlatformStoragePaths prepare_storage_paths() override
{
return storage_paths_ ? *storage_paths_ : PlatformStoragePaths {};
}
void log_stacktrace() override
{
}
void trigger_crash_test() override
{
#if defined(__ANDROID__)
int* x = nullptr;
*x = 42;
#endif
}
[[nodiscard]] std::string clipboard_text() override
{
if (bridge_.clipboard_text)
return bridge_.clipboard_text();
return {};
}
[[nodiscard]] bool set_clipboard_text(std::string_view text) override
{
if (bridge_.set_clipboard_text)
return bridge_.set_clipboard_text(text);
return false;
}
void set_cursor_visible(bool visible) override
{
(void)visible;
}
void set_virtual_keyboard_visible(bool visible) override
{
if (bridge_.set_virtual_keyboard_visible)
bridge_.set_virtual_keyboard_visible(visible);
}
void attach_ui_thread() override
{
if (bridge_.attach_ui_thread)
bridge_.attach_ui_thread();
}
void detach_ui_thread() override
{
if (bridge_.detach_ui_thread)
bridge_.detach_ui_thread();
}
void acquire_render_context() override
{
if (bridge_.acquire_render_context)
bridge_.acquire_render_context();
}
void release_render_context() override
{
if (bridge_.release_render_context)
bridge_.release_render_context();
}
void present_render_context() override
{
if (bridge_.present_render_context)
bridge_.present_render_context();
}
void bind_default_render_target() override
{
#if defined(__ANDROID__)
glBindFramebuffer(GL_FRAMEBUFFER, 0);
#endif
}
void bind_main_render_target() override
{
bind_default_render_target();
}
void apply_render_platform_hints() override
{
}
void install_render_debug_callback() override
{
}
void begin_render_capture_frame() override
{
}
void end_render_capture_frame() override
{
}
[[nodiscard]] bool deletes_recorded_files_on_clear() override
{
return platform_deletes_recorded_files_on_clear(kPlatformFamily);
}
void clear_recorded_files(std::string_view recording_path) override
{
(void)recording_path;
}
void publish_exported_image(std::string_view path) override
{
(void)path;
}
void flush_persistent_storage() override
{
}
[[nodiscard]] std::vector<std::string> document_browse_roots(
std::string_view work_path,
std::string_view data_path) override
{
return platform_document_browse_roots(kPlatformFamily, work_path, data_path);
}
void save_ui_state() override
{
}
[[nodiscard]] bool enables_live_asset_reloading() override
{
return platform_enables_live_asset_reloading(kPlatformFamily);
}
void update_platform_frame(float delta_time_seconds) override
{
(void)delta_time_seconds;
}
void report_rendered_frames(int frames) override
{
(void)frames;
}
void display_file(std::string_view path) override
{
(void)path;
}
void share_file(std::string_view path) override
{
(void)path;
}
void request_app_close() override
{
}
[[nodiscard]] bool start_vr_mode() override
{
return false;
}
void stop_vr_mode() override
{
}
void pick_image(PickedPathCallback callback) override
{
if (bridge_.pick_file)
bridge_.pick_file(std::move(callback));
}
void pick_file(std::vector<std::string> file_types, PickedPathCallback callback) override
{
(void)file_types;
if (bridge_.pick_file)
bridge_.pick_file(std::move(callback));
}
void pick_save_file(std::vector<std::string> file_types, PickedPathCallback callback) override
{
(void)file_types;
if (bridge_.pick_save_file)
bridge_.pick_save_file(std::move(callback));
}
void pick_directory(PickedPathCallback callback) override
{
(void)callback;
}
[[nodiscard]] bool supports_working_directory_picker() override
{
return platform_supports_working_directory_picker(kPlatformFamily);
}
[[nodiscard]] std::string format_working_directory_path(std::string_view path) override
{
return std::string(path);
}
[[nodiscard]] bool uses_prepared_file_writes() override
{
return platform_uses_prepared_file_writes(kPlatformFamily);
}
[[nodiscard]] bool uses_work_directory_document_export_collections() override
{
return platform_uses_work_directory_document_export_collections(kPlatformFamily);
}
[[nodiscard]] bool disables_network_tls_verification() override
{
return default_disables_network_tls_verification();
}
[[nodiscard]] bool uses_ppbr_export_data_directory_override() override
{
return platform_uses_ppbr_export_data_directory_override(kPlatformFamily);
}
[[nodiscard]] bool supports_sonarpen() override
{
return platform_supports_sonarpen(kPlatformFamily);
}
void start_sonarpen() override
{
}
[[nodiscard]] int default_canvas_resolution() override
{
return platform_default_canvas_resolution(kPlatformFamily);
}
[[nodiscard]] bool draws_canvas_tip_for_pointer(
bool is_mouse,
bool is_stylus,
bool is_left_button_release) override
{
return platform_draws_canvas_tip_for_pointer(
kPlatformFamily,
is_mouse,
is_stylus,
is_left_button_release);
}
[[nodiscard]] float adjust_canvas_input_pressure(float pressure) override
{
return pressure;
}
[[nodiscard]] PreparedFileTarget prepare_writable_file(
std::string_view type,
std::string_view default_name,
std::string_view data_path,
std::string_view temporary_path) override
{
return plan_platform_writable_file(
kPlatformFamily,
type,
default_name,
data_path,
temporary_path);
}
void save_prepared_file(
std::string_view path,
std::string_view suggested_name,
PreparedFileCallback callback) override
{
(void)suggested_name;
callback(std::string(path), false);
}
private:
AndroidPlatformBridge bridge_;
std::shared_ptr<pp::platform::PlatformStoragePaths> storage_paths_;
};
}
std::unique_ptr<pp::platform::PlatformServices> create_platform_services(
AndroidPlatformServicesConfig config)
{
return std::make_unique<AndroidPlatformServices>(std::move(config));
}
}

View File

@@ -0,0 +1,32 @@
#pragma once
#include "platform_api/platform_services.h"
#include <functional>
#include <memory>
#include <string_view>
namespace pp::platform::android {
struct AndroidPlatformBridge {
std::function<std::string()> clipboard_text;
std::function<bool(std::string_view text)> set_clipboard_text;
std::function<void(bool visible)> set_virtual_keyboard_visible;
std::function<void()> attach_ui_thread;
std::function<void()> detach_ui_thread;
std::function<void()> acquire_render_context;
std::function<void()> release_render_context;
std::function<void()> present_render_context;
std::function<void(PickedPathCallback callback)> pick_file;
std::function<void(PickedPathCallback callback)> pick_save_file;
};
struct AndroidPlatformServicesConfig {
AndroidPlatformBridge bridge;
std::shared_ptr<pp::platform::PlatformStoragePaths> storage_paths;
};
[[nodiscard]] std::unique_ptr<pp::platform::PlatformServices> create_platform_services(
AndroidPlatformServicesConfig config = {});
}

View File

@@ -9,9 +9,7 @@
#include "platform_api/platform_policy.h" #include "platform_api/platform_policy.h"
#include "renderer_gl/opengl_capabilities.h" #include "renderer_gl/opengl_capabilities.h"
#ifdef __ANDROID__ #ifdef __LINUX__
#include "main.h"
#elif __LINUX__
#include <tinyfiledialogs.h> #include <tinyfiledialogs.h>
#include "platform_linux/linux_platform_services.h" #include "platform_linux/linux_platform_services.h"
std::string linux_home_path(); std::string linux_home_path();
@@ -33,10 +31,6 @@ class LegacyPlatformServices final : public pp::platform::PlatformServices {
public: public:
explicit LegacyPlatformServices(pp::platform::legacy::LegacyPlatformServicesConfig config = {}) explicit LegacyPlatformServices(pp::platform::legacy::LegacyPlatformServicesConfig config = {})
: glfw_shell_(std::move(config.glfw_shell)) : glfw_shell_(std::move(config.glfw_shell))
, android_bridge_(std::move(config.android_bridge))
#ifdef __ANDROID__
, android_storage_paths_(std::move(config.android_storage_paths))
#endif
, web_platform_services_(config.web_platform_services) , web_platform_services_(config.web_platform_services)
{ {
} }
@@ -72,8 +66,6 @@ public:
data_path + "/frames", data_path + "/frames",
{}, {},
}; };
#elif defined(__ANDROID__)
return android_storage_paths_ ? *android_storage_paths_ : pp::platform::PlatformStoragePaths {};
#else #else
return {}; return {};
#endif #endif
@@ -85,34 +77,18 @@ public:
void trigger_crash_test() override void trigger_crash_test() override
{ {
#if defined(__ANDROID__)
int *x = nullptr; *x = 42;
LOG("%d", *x);
#endif
} }
[[nodiscard]] std::string clipboard_text() override [[nodiscard]] std::string clipboard_text() override
{ {
#ifdef __ANDROID__
if (android_bridge_.clipboard_text)
return android_bridge_.clipboard_text();
return {}; return {};
#else
return {};
#endif
} }
[[nodiscard]] bool set_clipboard_text(std::string_view text) override [[nodiscard]] bool set_clipboard_text(std::string_view text) override
{ {
const std::string value(text); const std::string value(text);
#ifdef __ANDROID__
if (android_bridge_.set_clipboard_text)
return android_bridge_.set_clipboard_text(value);
return false;
#else
(void)value; (void)value;
return false; return false;
#endif
} }
void set_cursor_visible(bool visible) override void set_cursor_visible(bool visible) override
@@ -122,54 +98,31 @@ public:
void set_virtual_keyboard_visible(bool visible) override void set_virtual_keyboard_visible(bool visible) override
{ {
#ifdef __ANDROID__
if (android_bridge_.set_virtual_keyboard_visible)
android_bridge_.set_virtual_keyboard_visible(visible);
#else
(void)visible; (void)visible;
#endif
} }
void attach_ui_thread() override void attach_ui_thread() override
{ {
#ifdef __ANDROID__
if (android_bridge_.attach_ui_thread)
android_bridge_.attach_ui_thread();
#endif
} }
void detach_ui_thread() override void detach_ui_thread() override
{ {
#ifdef __ANDROID__
if (android_bridge_.detach_ui_thread)
android_bridge_.detach_ui_thread();
#endif
} }
void acquire_render_context() override void acquire_render_context() override
{ {
#if __ANDROID__ #if __LINUX__ || __WEB__
if (android_bridge_.acquire_render_context)
android_bridge_.acquire_render_context();
#elif __LINUX__ || __WEB__
invoke_legacy_glfw_shell_callback(glfw_shell_.acquire_render_context); invoke_legacy_glfw_shell_callback(glfw_shell_.acquire_render_context);
#endif #endif
} }
void release_render_context() override void release_render_context() override
{ {
#if __ANDROID__
if (android_bridge_.release_render_context)
android_bridge_.release_render_context();
#endif
} }
void present_render_context() override void present_render_context() override
{ {
#if __ANDROID__ #if __LINUX__ || __WEB__
if (android_bridge_.present_render_context)
android_bridge_.present_render_context();
#elif __LINUX__ || __WEB__
invoke_legacy_glfw_shell_callback(glfw_shell_.present_render_context); invoke_legacy_glfw_shell_callback(glfw_shell_.present_render_context);
#endif #endif
} }
@@ -279,10 +232,7 @@ public:
void pick_image(pp::platform::PickedPathCallback callback) override void pick_image(pp::platform::PickedPathCallback callback) override
{ {
#ifdef __ANDROID__ #ifdef __LINUX__
if (android_bridge_.pick_file)
android_bridge_.pick_file(std::move(callback));
#elif __LINUX__
if (auto p = tinyfd_openFileDialog("Open File", "", 0, nullptr, nullptr, false)) if (auto p = tinyfd_openFileDialog("Open File", "", 0, nullptr, nullptr, false))
invoke_picked_path_if_selected(p, callback); invoke_picked_path_if_selected(p, callback);
#elif __WEB__ #elif __WEB__
@@ -294,10 +244,7 @@ public:
void pick_file(std::vector<std::string> file_types, pp::platform::PickedPathCallback callback) override void pick_file(std::vector<std::string> file_types, pp::platform::PickedPathCallback callback) override
{ {
#ifdef __ANDROID__ #ifdef __LINUX__
if (android_bridge_.pick_file)
android_bridge_.pick_file(std::move(callback));
#elif __LINUX__
if (auto p = tinyfd_openFileDialog("Open File", "", 0, nullptr, nullptr, false)) if (auto p = tinyfd_openFileDialog("Open File", "", 0, nullptr, nullptr, false))
invoke_picked_path_if_selected(p, callback); invoke_picked_path_if_selected(p, callback);
#elif __WEB__ #elif __WEB__
@@ -310,13 +257,8 @@ public:
void pick_save_file(std::vector<std::string> file_types, pp::platform::PickedPathCallback callback) override void pick_save_file(std::vector<std::string> file_types, pp::platform::PickedPathCallback callback) override
{ {
#if __ANDROID__
if (android_bridge_.pick_save_file)
android_bridge_.pick_save_file(std::move(callback));
#else
(void)file_types; (void)file_types;
(void)callback; (void)callback;
#endif
} }
void pick_directory(pp::platform::PickedPathCallback callback) override void pick_directory(pp::platform::PickedPathCallback callback) override
@@ -472,10 +414,6 @@ private:
} }
pp::platform::legacy::LegacyGlfwPlatformShell glfw_shell_; pp::platform::legacy::LegacyGlfwPlatformShell glfw_shell_;
pp::platform::legacy::LegacyAndroidPlatformBridge android_bridge_;
#ifdef __ANDROID__
std::shared_ptr<pp::platform::PlatformStoragePaths> android_storage_paths_;
#endif
pp::platform::WebPlatformServices* web_platform_services_ = nullptr; pp::platform::WebPlatformServices* web_platform_services_ = nullptr;
}; };

View File

@@ -13,23 +13,8 @@ struct LegacyGlfwPlatformShell {
std::function<void()> request_app_close; std::function<void()> request_app_close;
}; };
struct LegacyAndroidPlatformBridge {
std::function<std::string()> clipboard_text;
std::function<bool(std::string_view text)> set_clipboard_text;
std::function<void(bool visible)> set_virtual_keyboard_visible;
std::function<void()> attach_ui_thread;
std::function<void()> detach_ui_thread;
std::function<void()> acquire_render_context;
std::function<void()> release_render_context;
std::function<void()> present_render_context;
std::function<void(PickedPathCallback callback)> pick_file;
std::function<void(PickedPathCallback callback)> pick_save_file;
};
struct LegacyPlatformServicesConfig { struct LegacyPlatformServicesConfig {
LegacyGlfwPlatformShell glfw_shell; LegacyGlfwPlatformShell glfw_shell;
LegacyAndroidPlatformBridge android_bridge;
std::shared_ptr<pp::platform::PlatformStoragePaths> android_storage_paths;
pp::platform::WebPlatformServices* web_platform_services = nullptr; pp::platform::WebPlatformServices* web_platform_services = nullptr;
}; };

View File

@@ -358,6 +358,9 @@ endif()
if(TARGET pp_platform_linux) if(TARGET pp_platform_linux)
target_link_libraries(pp_platform_api_tests PRIVATE pp_platform_linux) target_link_libraries(pp_platform_api_tests PRIVATE pp_platform_linux)
endif() endif()
if(TARGET pp_platform_android)
target_link_libraries(pp_platform_api_tests PRIVATE pp_platform_android)
endif()
add_test(NAME pp_platform_api_tests COMMAND pp_platform_api_tests) add_test(NAME pp_platform_api_tests COMMAND pp_platform_api_tests)
set_tests_properties(pp_platform_api_tests PROPERTIES set_tests_properties(pp_platform_api_tests PROPERTIES