android/.gitignore                 |    9 
 android/Bootstrap/Makefile.shared  |   56 ++---
 android/CustomTarget_lo_android.mk |    1 
 android/Makefile                   |    3 
 android/README.md                  |   14 +
 android/build.gradle.kts           |    9 
 android/gradle/libs.versions.toml  |   14 +
 android/settings.gradle.kts        |   29 +++
 android/source/Makefile            |    8 
 android/source/build.gradle        |  321 ----------------------------------
 android/source/build.gradle.kts    |  347 +++++++++++++++++++++++++++++++++++++
 11 files changed, 446 insertions(+), 365 deletions(-)

New commits:
commit 26173fcdad80c614f9aa854ecfed83568f463799
Author:     Ivan Vladimirovich Kuznetsov <[email protected]>
AuthorDate: Mon Feb 16 22:37:28 2026 +0300
Commit:     Michael Weghorn <[email protected]>
CommitDate: Tue Feb 17 10:10:45 2026 +0100

    Android. Turn to standard android project structure.
    
    There is no reason to use a custom project structure. The usual Android
    project structure makes the development of new features easier using a
    modular approach. Therefore, the logic of gradle files was divided into
    different levels, gradle scripts was migrated to kotlin,
    dependency definitions was moved into toml file.
    No one java or kotlin file has been changed.
    
    These changes based on:
    1) Android build structure:
    https://developer.android.com/build/android-build-structure
    2) Migrate your build to version catalogs:
    https://developer.android.com/build/migrate-to-catalogs
    3) Migrate your build configuration from Groovy to Kotlin
    https://developer.android.com/build/migrate-to-kotlin-dsl
    
    Note: The source module has not been renamed to app (this is the usual
    name for a top-level application subproject), because I currently do not
    know what other changes will need to be made besides gradle scripts
    (maybe in the Makefiles or in other) for a correct build. Additional
    analysis required.
    
    Change-Id: Ic11efdb51524e795b8c6e2605b6501d9699c525c
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/195939
    Tested-by: Jenkins
    Reviewed-by: Michael Weghorn <[email protected]>

diff --git a/android/.gitignore b/android/.gitignore
index b8e2e1d1cccd..a06252d55ab3 100644
--- a/android/.gitignore
+++ b/android/.gitignore
@@ -1,5 +1,7 @@
-/source/.gradle/
-/source/.idea/
+/build
+/.gradle
+/.idea
+/local.properties
 /source/assets/
 /source/assets_strippedUI/
 /source/build/
@@ -7,8 +9,7 @@
 /source/jni/Application.mk
 /source/res_generated
 /jniLibs/
-/source/liboSettings.gradle
-/source/local.properties
+/source/liboSettings.gradle.kts
 /source/native-code.cxx
 /obj/
 /source/source.iml
diff --git a/android/Bootstrap/Makefile.shared 
b/android/Bootstrap/Makefile.shared
index b83705978a03..81a7ff50e7f1 100644
--- a/android/Bootstrap/Makefile.shared
+++ b/android/Bootstrap/Makefile.shared
@@ -18,13 +18,6 @@ JNILIBSDIR=$(BUILDDIR)/android/jniLibs
 SODEST=$(JNILIBSDIR)/$(ANDROID_APP_ABI)
 OBJLOCAL=$(BUILDDIR)/android/obj/local/$(ANDROID_APP_ABI)
 
-#
-# Helpful rules ...
-#
-
-local.properties: $(BUILDDIR)/config_host.mk
-       echo sdk.dir=$(ANDROID_SDK_DIR) >local.properties
-
 #
 # Build / link the single .so for this app
 #
@@ -86,37 +79,32 @@ stop-start-cycle:
        $(ANDROID_SDK_DIR)/platform-tools/adb shell stop && 
$(ANDROID_SDK_DIR)/platform-tools/adb shell start && sleep 10
 
 # build-host specific stuff (build paths and the like) to keep build.gradle 
static
-liboSettings.gradle: $(BUILDDIR)/config_build.mk $(BUILDDIR)/config_host.mk \
+liboSettings.gradle.kts: $(BUILDDIR)/config_build.mk 
$(BUILDDIR)/config_host.mk \
                $(wildcard $(INSTDIR)/program/version.ini 
$(INSTDIR)/program/versionrc) \
                $(SRCDIR)/android/Bootstrap/Makefile.shared
        @echo "creating $@"
        ( \
                echo "// created by Makefile.shared - your changes will be 
overridden" \
-               && echo "ext {" \
-               && echo "    liboSrcRoot         = '$(SRC_ROOT)'" \
-               && echo "    liboJniLibsdir      = '$(JNILIBSDIR)'" \
-               && echo "    liboWorkdir         = '$(WORKDIR)'" \
-               && echo "    liboInstdir         = '$(INSTDIR)'" \
-               && echo "    liboEtcFolder       = '$(LIBO_ETC_FOLDER)'" \
-               && echo "    liboUreMiscFolder   = '$(LIBO_URE_MISC_FOLDER)'" \
-               && echo "    liboSharedResFolder = 
'$(LIBO_SHARE_RESOURCE_FOLDER)'" \
-               && echo "    liboUREJavaFolder   = 
'$(LIBO_URE_SHARE_JAVA_FOLDER)'" \
-               && echo "    liboShareJavaFolder = '$(LIBO_SHARE_JAVA_FOLDER)'" 
\
-               && echo "    liboVersionMajor    = '$(LIBO_VERSION_MAJOR)'" \
-               && echo "    liboVersionMinor    = '$(LIBO_VERSION_MINOR)'" \
-               && echo "    liboGitFullCommit   = '$(shell cd $(SRCDIR) && git 
log -1 --format=%H)'" \
-               && echo "}" \
-               && echo "android {" \
-               && echo "    ndkPath = '$(ANDROID_NDK_DIR)'" \
-               && echo "    ndkVersion = '$(ANDROID_NDK_VERSION)'" \
-               && echo "}" \
-               && echo "android.defaultConfig {" \
-               && echo "    applicationId '$(ANDROID_PACKAGE_NAME)'" \
-               && echo "    minSdkVersion $(ANDROID_API_LEVEL)" \
-               && echo "    versionCode project.hasProperty('cmdVersionCode') 
? cmdVersionCode.toInteger() : $(if $(versionCode),$(versionCode),1)" \
-               && echo "    versionName 
'$(LIBO_VERSION_MAJOR).$(LIBO_VERSION_MINOR).$(LIBO_VERSION_MICRO).$(LIBO_VERSION_PATCH)$(LIBO_VERSION_SUFFIX)$(LIBO_VERSION_SUFFIX_SUFFIX)'"
 \
-               && echo "    buildConfigField('String', 'BUILD_ID_SHORT', 
'\"$(shell cd $(SRCDIR) && git log -1 --format=%h)\"')" \
-               && echo "    buildConfigField('String', 'VENDOR', 
'\"$(OOO_VENDOR)\"')" \
-               && echo "    buildConfigField('String', 'PRIVACY_POLICY_URL', 
'\"$(PRIVACY_POLICY_URL)\"')" \
+               && echo "extra.apply {" \
+               && echo "    set(\"liboSrcRoot\", \"$(SRC_ROOT)\")" \
+               && echo "    set(\"liboJniLibsdir\", \"$(JNILIBSDIR)\")" \
+               && echo "    set(\"liboWorkdir\", \"$(WORKDIR)\")" \
+               && echo "    set(\"liboInstdir\", \"$(INSTDIR)\")" \
+               && echo "    set(\"liboEtcFolder\", \"$(LIBO_ETC_FOLDER)\")" \
+               && echo "    set(\"liboUreMiscFolder\", 
\"$(LIBO_URE_MISC_FOLDER)\")" \
+               && echo "    set(\"liboSharedResFolder\", 
\"$(LIBO_SHARE_RESOURCE_FOLDER)\")" \
+               && echo "    set(\"liboUREJavaFolder\", 
\"$(LIBO_URE_SHARE_JAVA_FOLDER)\")" \
+               && echo "    set(\"liboShareJavaFolder\", 
\"$(LIBO_SHARE_JAVA_FOLDER)\")" \
+               && echo "    set(\"liboVersionMajor\", 
\"$(LIBO_VERSION_MAJOR)\")" \
+               && echo "    set(\"liboVersionMinor\", 
\"$(LIBO_VERSION_MINOR)\")" \
+               && echo "    set(\"liboGitFullCommit\", \"$(shell cd $(SRCDIR) 
&& git log -1 --format=%H)\")" \
+               && echo "    set(\"androidNdkPath\", \"$(ANDROID_NDK_DIR)\")" \
+               && echo "    set(\"androidNdkVersion\", 
\"$(ANDROID_NDK_VERSION)\")" \
+               && echo "    set(\"androidApplicationId\", 
\"$(ANDROID_PACKAGE_NAME)\")" \
+               && echo "    set(\"androidMinSdkVersion\", 
$(ANDROID_API_LEVEL))" \
+               && echo "    set(\"androidVersionName\", 
\"$(LIBO_VERSION_MAJOR).$(LIBO_VERSION_MINOR).$(LIBO_VERSION_MICRO).$(LIBO_VERSION_PATCH)$(LIBO_VERSION_SUFFIX)$(LIBO_VERSION_SUFFIX_SUFFIX)\")"
 \
+               && echo "    set(\"buildIdShort\", \"\\"$(shell cd $(SRCDIR) && 
git log -1 --format=%h)\\"\")" \
+               && echo "    set(\"vendor\", \"\\"$(OOO_VENDOR)\\"\")" \
+               && echo "    set(\"privacyPolicyUrl\", 
\"\\"$(PRIVACY_POLICY_URL)\\"\")" \
                && echo "}" \
        ) > $@
diff --git a/android/CustomTarget_lo_android.mk 
b/android/CustomTarget_lo_android.mk
index 815548cdf3a8..920945b0df7e 100644
--- a/android/CustomTarget_lo_android.mk
+++ b/android/CustomTarget_lo_android.mk
@@ -16,6 +16,7 @@ $(call gb_CustomTarget_get_target,android/loandroid3) : \
 $(loandroid3_DIR)/done : $(call 
gb_Postprocess_get_target,AllModulesButInstsetNative)
        $(call gb_Output_announce,$(subst $(WORKDIR)/,,$@),$(true),MAK,2)
        $(call gb_Trace_StartRange,$(subst $(WORKDIR)/,,$@),MAK)
+       cd $(SRCDIR)/android && $(MAKE) local.properties
        cd $(SRCDIR)/android/source && $(MAKE) all
 ifeq ($(ENABLE_JAVA),TRUE)
 # Copy to $(BUILDDIR)/instsetoo_native as that is where the tinderbox build 
script
diff --git a/android/Makefile b/android/Makefile
index a0a7231f7b07..fb64e5bfac3b 100644
--- a/android/Makefile
+++ b/android/Makefile
@@ -15,6 +15,9 @@ include $(module_directory)/../solenv/gbuild/partial_build.mk
 SIGNED_APK := $(BUILDDIR)/android/source/bin/LibreOfficeViewer.apk
 RELEASE_APK_USAGE := echo; echo "Usage: make versionCode=<version_num+1> 
key=<key_name> release-apk"
 
+local.properties: $(BUILDDIR)/config_host.mk
+       echo sdk.dir=$(ANDROID_SDK_DIR) >local.properties
+
 release-apk: build
        # versionCode and key are mandatory
        @if test -z "$(versionCode)" ; then $(RELEASE_APK_USAGE) ; exit 1 ; fi
diff --git a/android/README.md b/android/README.md
index 6d4da9a96395..12039a89c16e 100644
--- a/android/README.md
+++ b/android/README.md
@@ -13,6 +13,16 @@ It uses OpenGL ES 2 for rendering of the document tiles 
which are gathered from
 LibreOffice using LOK. The application contains the LibreOffice core in one 
shared
 library: `liblo-native-code.so`, which is bundled together with the 
application.
 
+## Adding Dependencies
+
+To add a new dependency, make sure it hasn't already been declared in
+`android/gradle/libs.versions.toml`. If it has, just use the existing 
dependency declaration.
+Otherwise, declare it in the version catalog, then reference it in your Gradle 
files.
+
+**Note:** You cannot declare *different versions* of *one* dependency. If you 
really need to do this,
+create another version catalog or declare the dependency explicitly in the 
`.gradle` file.
+
+
 ## Architecture and Threading
 
 The application implements editing support using 4 threads:
@@ -223,7 +233,7 @@ Note that lldb might not yield the same results as 
`ndk-gdb`. If you suspect a
 problem with `lldb`, you can try to manually use `ndk-gdb` as described above.
 Using `lldb` from within Android Studio is more comfortable though and works 
like this:
 
-- open `android/source/build.gradle` in Android Studio via File|New → Import 
Project
+- open `android` directory in Android Studio via File|Open...
 - make sure you select the right build variant (`strippedUIDebug` is what you 
want)
 - use Run|Edit Configurations to create a new configuration of type "Android 
Native"
        - on tab "General" pick module "source"
@@ -242,7 +252,7 @@ then toggling the breakpoint by clicking on the margin is 
more comfortable.
 
 ### Debugging the Java Part
 
-Open `android/source/build.gradle` in Android studio via File|New → Import
+Open `android` directory in Android studio via File|Open...
 Project and you can use Android Studio's debugging interface.
 Just make sure you pick the correct build variant (strippedUIDebug)
 
diff --git a/android/build.gradle.kts b/android/build.gradle.kts
new file mode 100644
index 000000000000..d0827442e449
--- /dev/null
+++ b/android/build.gradle.kts
@@ -0,0 +1,9 @@
+plugins {
+    alias(libs.plugins.android.application) apply false
+}
+
+buildscript {
+    dependencies {
+        classpath(libs.gradle)
+    }
+}
diff --git a/android/source/gradle.properties b/android/gradle.properties
similarity index 100%
rename from android/source/gradle.properties
rename to android/gradle.properties
diff --git a/android/gradle/libs.versions.toml 
b/android/gradle/libs.versions.toml
new file mode 100644
index 000000000000..2777ce25257d
--- /dev/null
+++ b/android/gradle/libs.versions.toml
@@ -0,0 +1,14 @@
+[versions]
+constraintlayout_version = "2.2.1"
+gradle_version = "8.13.1"
+material_version = "1.13.0"
+preference_version = "1.2.1"
+
+[libraries]
+constraintlayout = { module = "androidx.constraintlayout:constraintlayout", 
version.ref = "constraintlayout_version" }
+gradle = { module = "com.android.tools.build:gradle", version.ref = 
"gradle_version" }
+material = { module = "com.google.android.material:material", version.ref = 
"material_version" }
+preference = { module = "androidx.preference:preference", version.ref = 
"preference_version" }
+
+[plugins]
+android-application = { id = "com.android.application", version.ref = 
"gradle_version" }
diff --git a/android/source/gradle/wrapper/gradle-wrapper.jar 
b/android/gradle/wrapper/gradle-wrapper.jar
similarity index 100%
rename from android/source/gradle/wrapper/gradle-wrapper.jar
rename to android/gradle/wrapper/gradle-wrapper.jar
diff --git a/android/source/gradle/wrapper/gradle-wrapper.properties 
b/android/gradle/wrapper/gradle-wrapper.properties
similarity index 100%
rename from android/source/gradle/wrapper/gradle-wrapper.properties
rename to android/gradle/wrapper/gradle-wrapper.properties
diff --git a/android/source/gradlew b/android/gradlew
similarity index 100%
rename from android/source/gradlew
rename to android/gradlew
diff --git a/android/source/gradlew.bat b/android/gradlew.bat
similarity index 100%
rename from android/source/gradlew.bat
rename to android/gradlew.bat
diff --git a/android/settings.gradle.kts b/android/settings.gradle.kts
new file mode 100644
index 000000000000..0e746d3ac596
--- /dev/null
+++ b/android/settings.gradle.kts
@@ -0,0 +1,29 @@
+pluginManagement {
+    repositories {
+        google {
+            content {
+                includeGroupByRegex("com\.android.*")
+                includeGroupByRegex("com\.google.*")
+                includeGroupByRegex("androidx.*")
+            }
+        }
+        mavenCentral()
+        gradlePluginPortal()
+        maven {
+            url = java.net.URI("https://ipv6.repo1.maven.org/maven2";)
+        }
+    }
+}
+
+dependencyResolutionManagement {
+    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
+    repositories {
+        google()
+        mavenCentral()
+        maven {
+            url = java.net.URI("https://ipv6.repo1.maven.org/maven2";)
+        }
+    }
+}
+
+include(":source")
diff --git a/android/source/Makefile b/android/source/Makefile
index 284563c4466c..f5ef9fd3e15b 100644
--- a/android/source/Makefile
+++ b/android/source/Makefile
@@ -17,7 +17,7 @@ native-code.cxx: $(SRCDIR)/solenv/bin/native-code.py
        $< -j -g core -g writer -g calc -g draw -g edit > $@
 
 install:
-       if test "$$ENABLE_ANDROID_LOK" != "TRUE" ; then ./gradlew $(if 
$(verbose),--info) $(if $(versionCode),-PcmdVersionCode=$(versionCode)) 
install$(if $(DISABLE_UI),StrippedUI)$(if 
$(ENABLE_ANDROID_EDITING),Editing)Debug ; fi
+       if test "$$ENABLE_ANDROID_LOK" != "TRUE" ; then ../gradlew $(if 
$(verbose),--info) $(if $(versionCode),-PcmdVersionCode=$(versionCode)) 
install$(if $(DISABLE_UI),StrippedUI)$(if 
$(ENABLE_ANDROID_EDITING),Editing)Debug ; fi
        @if test "$$ENABLE_ANDROID_LOK" != "TRUE" ; then echo ; echo 'Run it 
with "make run"' ; echo ; fi
 
 uninstall:
@@ -26,11 +26,11 @@ uninstall:
 clean:
        rm -rf assets assets_strippedUI build jniLibs jniLibs_debug 
res_generated $(OBJLOCAL)
        rm -f native-code.cxx
-       rm -f liboSettings.gradle
+       rm -f liboSettings.gradle.kts
 
-build-gradle: liboSettings.gradle local.properties link-so
+build-gradle: liboSettings.gradle.kts link-so
 ifeq ($(ENABLE_JAVA),TRUE)
-       if test "$$ENABLE_ANDROID_LOK" != "TRUE" ; then ./gradlew $(if 
$(verbose),--info) $(if $(versionCode),-PcmdVersionCode=$(versionCode)) 
assemble$(BUILD_VARIANT) lint$(BUILD_VARIANT)  ; fi
+       if test "$$ENABLE_ANDROID_LOK" != "TRUE" ; then ../gradlew $(if 
$(verbose),--info) $(if $(versionCode),-PcmdVersionCode=$(versionCode)) 
assemble$(BUILD_VARIANT) lint$(BUILD_VARIANT)  ; fi
 endif
 
 run:
diff --git a/android/source/build.gradle b/android/source/build.gradle
deleted file mode 100644
index 2ee45b74d3b4..000000000000
--- a/android/source/build.gradle
+++ /dev/null
@@ -1,321 +0,0 @@
-apply plugin: 'com.android.application'
-// buildhost settings - paths and the like
-apply from: 'liboSettings.gradle'
-
-allprojects {
-    repositories {
-        mavenCentral()
-        maven {
-            url = "https://ipv6.repo1.maven.org/maven2";
-        }
-        google()
-    }
-}
-//build-time dependencies - android plugin for gradle
-buildscript {
-    repositories {
-        mavenCentral()
-        maven {
-            url = "https://ipv6.repo1.maven.org/maven2";
-        }
-        google()
-    }
-    dependencies {
-        classpath 'com.android.tools.build:gradle:8.13.1'
-    }
-}
-
-// compile-time dependencies
-dependencies {
-    implementation fileTree(dir: "${liboInstdir}/${liboUREJavaFolder}", 
include: [
-            "java_uno.jar",
-            "libreoffice.jar",
-            "unoloader.jar"
-    ])
-    implementation 'com.google.android.material:material:1.13.0'
-    implementation 'androidx.constraintlayout:constraintlayout:2.2.1'
-    implementation 'androidx.preference:preference:1.2.1'
-}
-
-base {
-    archivesName = "LibreOfficeViewer"
-}
-
-android {
-    namespace = 'org.libreoffice'
-    compileSdk 35
-    buildFeatures {
-        buildConfig = true
-    }
-    // uses non-conventional source layout, so need to reconfigure accordingly
-    // ToDo move to conventional layout, so stuff can be stripped down.
-    sourceSets {
-        main.manifest.srcFile 'AndroidManifest.xml'
-        main.assets.srcDirs = ['assets']
-        main.res.srcDirs = ['res', 'res_generated']
-        main.java.srcDirs = ['../Bootstrap/src', 'src/java']
-        main.jniLibs.srcDirs = ["${liboJniLibsdir}"]
-        main.assets.srcDirs 'assets_strippedUI'
-        quest.manifest.srcFile 'AndroidManifest_quest.xml'
-    }
-    defaultConfig {
-        // minSdkVersion is set in liboSettings.gradle
-        targetSdkVersion 35
-        vectorDrawables.useSupportLibrary = true
-    }
-    buildTypes {
-        debug {
-            // make android studio happy...
-            jniDebuggable true
-            // would work just fine with external, but setting emulator up is 
a little more work
-            manifestPlaceholders = [installLocation: "auto"]
-        }
-        release {
-            manifestPlaceholders = [installLocation: "preferExternal"]
-        }
-    }
-
-    flavorDimensions "default"
-    productFlavors {
-        strippedUI {
-            dimension "default"
-            buildConfigField 'boolean', 'ALLOW_EDITING', 'false'
-        }
-        strippedUIEditing {
-            dimension "default"
-            buildConfigField 'boolean', 'ALLOW_EDITING', 'true'
-        }
-
-        // product flavor for Meta Quest devices (Horizon OS)
-        quest {
-            dimension "default"
-            buildConfigField 'boolean', 'ALLOW_EDITING', 'false'
-        }
-
-    }
-    lint {
-        warningsAsErrors = true
-        // ignore missing or extra translations, since these are 
tracked/managed via Weblate
-        disable 'ExtraTranslation', 'MissingTranslation'
-        // don't error-out on external updates (new gradle plugin, library 
versions
-        // or target API become available)
-        // don't error-out on TypographyEllipsis, since this can be introduced 
with
-        // new translations, and those are handled in Weblate
-        informational 'AndroidGradlePluginVersion', 'GradleDependency', 
'NewerVersionAvailable', 'OldTargetApi', 'TypographyEllipsis'
-        // don't fail on pre-existing issues
-        // These should be dealt with at some point, though.
-        // To update lint-baseline.xml, just remove the file and run the build 
again.
-        baseline = file("lint-baseline.xml")
-        // s.a. lint.xml that contains further config
-    }
-}
-
-// show warnings about use of deprecated API
-tasks.withType(JavaCompile).configureEach {
-    options.deprecation = true
-}
-
-/* remark inherited from makefile:
-Then "assets". Let the directory structure under assets mimic
-that under solver for now.
-
-Please note that I have no idea what all of this is really necessary and for
-much of this stuff being copied, no idea whether it makes any sense at all.
-Much of this is copy-pasted from android/qa/sc/Makefile (where a couple of
-unit tests for sc are built, and those do seem to mostly work) and
-android/qa/desktop/Makefile (mmeeks's desktop demo, also works to some
-extent)
- */
-
-// Assets that are unpacked at run-time into the app's data directory. These
-// are files read by non-LO code, fontconfig and freetype for now, that doesn't
-// understand "/assets" paths.
-task copyUnpackAssets(type: Copy) {
-    description = "copies assets that need to be extracted on the device"
-    into 'assets/unpack'
-    into('program') {
-        from("${liboInstdir}/${liboEtcFolder}/types") {
-            includes = [
-                    "offapi.rdb",
-                    "oovbaapi.rdb"
-            ]
-        }
-        from("${liboInstdir}/${liboUreMiscFolder}") {
-            includes = ["types.rdb"]
-            rename 'types.rdb', 'udkapi.rdb'
-        }
-    }
-    into('user/fonts') {
-        from "${liboInstdir}/share/fonts/truetype"
-        // Note: restrict list of fonts due to size considerations - no 
technical reason anymore
-        // ToDo: fonts would be good candidate for using Expansion Files 
instead
-        includes = [
-                "Liberation*.ttf",
-                "Caladea-*.ttf",
-                "Carlito-*.ttf",
-                "Gen*.ttf",
-                "opens___.ttf"
-        ]
-    }
-    into('etc/fonts') {
-        from "./"
-        includes = ['fonts.conf']
-        filter {
-            String line ->
-                line.replaceAll(
-                        '@@APPLICATION_ID@@', new 
String("${android.defaultConfig.applicationId}")
-                )
-        }
-    }
-}
-
-task copyAssets(type: Copy) {
-    description = "copies assets that can be accessed within the installed apk"
-    into 'assets'
-
-    // include icons, Impress styles and required .ui files
-    into ('share') {
-        into ('config') {
-            from ("${liboInstdir}/share/config")
-            includes = ['images_**.zip',
-                        '**/simpress/**.xml',
-                        '**/annotation.ui',
-                        '**/hfmenubutton.ui',
-                        '**/inforeadonlydialog.ui',
-                        '**/pbmenubutton.ui',
-                        '**/scrollbars.ui',
-                        '**/tabbuttons.ui',
-                        '**/tabbuttonsmirrored.ui',
-                        '**/tabviewbar.ui'
-                        ]
-        }
-    }
-
-    into('program') {
-        from "${liboInstdir}/program"
-        includes = ['services.rdb', 'services/services.rdb']
-
-        into('resource') {
-            from "${liboInstdir}/${liboSharedResFolder}"
-            includes = ['*en-US.res']
-        }
-    }
-    into('share') {
-        from("${liboInstdir}/share") {
-            // Filter data is needed by e.g. the drawingML preset shape import.
-            includes = ['registry/**', 'filter/**']
-            // those two get processed by mobile-config.py
-            excludes = ['registry/main.xcd', 'registry/res/registry_en-US.xcd']
-        }
-        // separate data files for Chinese and Japanese
-        from("${liboWorkdir}/CustomTarget/i18npool/breakiterator/") {
-            include '*.data'
-        }
-    }
-}
-
-task copyAppResources(type: Copy) {
-    description = "copies documents to make them available as app resources"
-    into 'res_generated/raw'
-    from("${liboInstdir}") {
-        includes = ["LICENSE", "NOTICE"]
-        rename "LICENSE", "license.txt"
-        rename "NOTICE", "notice.txt"
-    }
-}
-
-task createStrippedConfig {
-    def preserveDir = file("assets_strippedUI/share/config/soffice.cfg/empty")
-    outputs.dir "assets_strippedUI"
-    outputs.dir "assets_strippedUI/share/registry/res"
-    outputs.file preserveDir
-
-    doLast {
-        file('assets_strippedUI/share/registry/res').mkdirs()
-        file("assets_strippedUI/share/config/soffice.cfg").mkdirs()
-        // just empty file
-        preserveDir.text = ""
-    }
-}
-
-
-task createStrippedConfigMain(type: Exec) {
-    dependsOn 'createStrippedConfig'
-    inputs.files "${liboInstdir}/share/registry/main.xcd", 
"${liboSrcRoot}/android/mobile-config.py"
-    outputs.file "assets_strippedUI/share/registry/main.xcd"
-    executable "${liboSrcRoot}/android/mobile-config.py"
-    args = ["${liboInstdir}/share/registry/main.xcd", 
"assets_strippedUI/share/registry/main.xcd"]
-}
-
-task createStrippedConfigRegistry(type: Exec) {
-    dependsOn 'createStrippedConfig'
-    inputs.files "${liboInstdir}/share/registry/res/registry_en-US.xcd", 
"${liboSrcRoot}/android/mobile-config.py"
-    outputs.file "assets_strippedUI/share/registry/res/registry_en-US.xcd"
-    executable "${liboSrcRoot}/android/mobile-config.py"
-    args = ["${liboInstdir}/share/registry/res/registry_en-US.xcd", 
"assets_strippedUI/share/registry/res/registry_en-US.xcd"]
-    doFirst {
-        file('assets_strippedUI/share/registry/res').mkdirs()
-    }
-}
-
-task createRCfiles {
-    inputs.file "liboSettings.gradle"
-    dependsOn copyUnpackAssets, copyAssets
-    def sofficerc     = file('assets/unpack/program/sofficerc')
-    def fundamentalrc = file('assets/program/fundamentalrc')
-    def bootstraprc   = file('assets/program/bootstraprc')
-    def unorc         = file('assets/program/unorc')
-    def versionrc     = file('assets/program/versionrc')
-
-    outputs.files sofficerc, fundamentalrc, unorc, bootstraprc, versionrc
-
-    doLast {
-        sofficerc.text = '''\
-            [Bootstrap]
-            Logo=1
-            NativeProgress=1
-            URE_BOOTSTRAP=file:///assets/program/fundamentalrc
-            HOME=$APP_DATA_DIR/cache
-            OSL_SOCKET_PATH=$APP_DATA_DIR/cache
-            '''.stripIndent()
-
-        fundamentalrc.text =  '''\
-            [Bootstrap]
-            LO_LIB_DIR=file://$APP_DATA_DIR/lib/
-            BRAND_BASE_DIR=file:///assets
-            BRAND_SHARE_SUBDIR=share
-            CONFIGURATION_LAYERS=xcsxcu:${BRAND_BASE_DIR}/share/registry 
res:${BRAND_BASE_DIR}/share/registry
-            
URE_BIN_DIR=file:///assets/ure/bin/dir/nothing-here/we-can/exec-anyway
-            '''.stripIndent()
-
-        bootstraprc.text =  '''\
-            [Bootstrap]
-            InstallMode=<installmode>
-            ProductKey=LibreOffice '''+ 
"${liboVersionMajor}.${liboVersionMinor}" + '''
-            UserInstallation=file://$APP_DATA_DIR
-            '''.stripIndent()
-
-        unorc.text = '''\
-            [Bootstrap]
-            URE_INTERNAL_LIB_DIR=file://$APP_DATA_DIR/lib/
-            UNO_TYPES=file://$APP_DATA_DIR/program/udkapi.rdb 
file://$APP_DATA_DIR/program/offapi.rdb 
file://$APP_DATA_DIR/program/oovbaapi.rdb
-            UNO_SERVICES=file:///assets/program/services.rdb 
file:///assets/program/services/services.rdb
-            '''.stripIndent()
-
-        versionrc.text = '''\
-            [Version]
-            AllLanguages=en-US
-            buildid=''' + "${liboGitFullCommit}" + '''
-            ReferenceOOoMajorMinor=4.1
-            '''.stripIndent()
-    }
-}
-
-// creating the UI stuff is cheap, don't bother only applying it for the 
flavor...
-preBuild.dependsOn 'createRCfiles',
-        'createStrippedConfigMain',
-        'createStrippedConfigRegistry',
-        'copyAppResources'
-
-clean.dependsOn 'cleanCopyAssets',
-        'cleanCreateStrippedConfig'
diff --git a/android/source/build.gradle.kts b/android/source/build.gradle.kts
new file mode 100644
index 000000000000..ff88911bb482
--- /dev/null
+++ b/android/source/build.gradle.kts
@@ -0,0 +1,347 @@
+plugins {
+    alias(libs.plugins.android.application)
+}
+
+// buildhost settings - paths and the like
+apply(from = "liboSettings.gradle.kts")
+
+val liboInstdir: String by extra
+val liboUREJavaFolder: String by extra
+val liboJniLibsdir: String by extra
+val liboEtcFolder: String by extra
+val liboUreMiscFolder: String by extra
+val liboSharedResFolder: String by extra
+val liboSrcRoot: String by extra
+val liboWorkdir: String by extra
+val liboVersionMajor: String by extra
+val liboVersionMinor: String by extra
+val liboGitFullCommit: String by extra
+val androidNdkPath: String by extra
+val androidNdkVersion: String by extra
+val androidVersionName: String by extra
+val buildIdShort: String by extra
+val vendor: String by extra
+val privacyPolicyUrl: String by extra
+val androidApplicationId: String by extra
+val androidMinSdkVersion: Int by extra
+
+// compile-time dependencies
+dependencies {
+    implementation(fileTree(mapOf(
+            "dir" to "$liboInstdir/$liboUREJavaFolder",
+            "include" to listOf(
+                    "java_uno.jar",
+                    "libreoffice.jar",
+                    "unoloader.jar"
+            )
+    )))
+    implementation(libs.material)
+    implementation(libs.constraintlayout)
+    implementation(libs.preference)
+}
+
+base {
+    archivesName.set("LibreOfficeViewer")
+}
+
+android {
+    namespace = "org.libreoffice"
+    compileSdk = 35
+
+    ndkPath = androidNdkPath
+    ndkVersion = androidNdkVersion
+
+    defaultConfig {
+        applicationId = androidApplicationId
+        minSdk = androidMinSdkVersion
+        versionCode = if (project.hasProperty("cmdVersionCode")) 
project.property("cmdVersionCode").toString().toInt() else 1
+        versionName = androidVersionName
+        buildConfigField("String", "BUILD_ID_SHORT", buildIdShort)
+        buildConfigField("String", "VENDOR", vendor)
+        buildConfigField("String", "PRIVACY_POLICY_URL", privacyPolicyUrl)
+    }
+
+    buildFeatures {
+        buildConfig = true
+    }
+
+    // uses non-conventional source layout, so need to reconfigure accordingly
+    // ToDo move to conventional layout, so stuff can be stripped down.
+    sourceSets {
+        getByName("main") {
+            manifest.srcFile("AndroidManifest.xml")
+            assets.srcDirs("assets")
+            res.srcDirs("res", "res_generated")
+            java.srcDirs("../Bootstrap/src", "src/java")
+            jniLibs.srcDirs("$liboJniLibsdir")
+            assets.srcDirs("assets_strippedUI")
+        }
+
+        create("quest") {
+            manifest.srcFile("AndroidManifest_quest.xml")
+        }
+    }
+
+    defaultConfig {
+        // minSdkVersion is set in liboSettings.gradle.kts
+        targetSdk = 35
+        vectorDrawables.useSupportLibrary = true
+    }
+
+    buildTypes {
+        debug {
+            // make android studio happy...
+            isJniDebuggable = true
+            // would work just fine with external, but setting emulator up is 
a little more work
+            manifestPlaceholders["installLocation"] = "auto"
+        }
+        release {
+            manifestPlaceholders["installLocation"] = "preferExternal"
+        }
+    }
+
+    flavorDimensions.add("default")
+    productFlavors {
+        create("strippedUI") {
+            dimension = "default"
+            buildConfigField("boolean", "ALLOW_EDITING", "false")
+        }
+        create("strippedUIEditing") {
+            dimension = "default"
+            buildConfigField("boolean", "ALLOW_EDITING", "true")
+        }
+
+        // product flavor for Meta Quest devices (Horizon OS)
+        create("quest") {
+            dimension = "default"
+            buildConfigField("boolean", "ALLOW_EDITING", "false")
+        }
+    }
+
+    lint {
+        warningsAsErrors = true
+        // ignore missing or extra translations, since these are 
tracked/managed via Weblate
+        disable.addAll(listOf("ExtraTranslation", "MissingTranslation"))
+        // don't error-out on external updates (new gradle plugin, library 
versions
+        // or target API become available)
+        // don't error-out on TypographyEllipsis, since this can be introduced 
with
+        // new translations, and those are handled in Weblate
+        informational.addAll(listOf("AndroidGradlePluginVersion", 
"GradleDependency", "NewerVersionAvailable", "OldTargetApi", 
"TypographyEllipsis"))
+        // don't fail on pre-existing issues
+        // These should be dealt with at some point, though.
+        // To update lint-baseline.xml, just remove the file and run the build 
again.
+        baseline = file("lint-baseline.xml")
+        // s.a. lint.xml that contains further config
+    }
+}
+
+// show warnings about use of deprecated API
+tasks.withType<JavaCompile> {
+    options.isDeprecation = true
+}
+
+/* remark inherited from makefile:
+Then "assets". Let the directory structure under assets mimic
+that under solver for now.
+
+Please note that I have no idea what all of this is really necessary and for
+much of this stuff being copied, no idea whether it makes any sense at all.
+Much of this is copy-pasted from android/qa/sc/Makefile (where a couple of
+unit tests for sc are built, and those do seem to mostly work) and
+android/qa/desktop/Makefile (mmeeks's desktop demo, also works to some extent)
+ */
+
+// Assets that are unpacked at run-time into the app's data directory. These
+// are files read by non-LO code, fontconfig and freetype for now, that doesn't
+// understand "/assets" paths.
+tasks.register<Copy>("copyUnpackAssets") {
+    description = "copies assets that need to be extracted on the device"
+    into("assets/unpack")
+
+    into("program") {
+        from("$liboInstdir/$liboEtcFolder/types") {
+            include(
+                    "offapi.rdb",
+                    "oovbaapi.rdb"
+            )
+        }
+        from("$liboInstdir/$liboUreMiscFolder") {
+            include("types.rdb")
+            rename("types.rdb", "udkapi.rdb")
+        }
+    }
+
+    into("user/fonts") {
+        from("$liboInstdir/share/fonts/truetype")
+        // Note: restrict list of fonts due to size considerations - no 
technical reason anymore
+        // ToDo: fonts would be good candidate for using Expansion Files 
instead
+        include(
+                "Liberation*.ttf",
+                "Caladea-*.ttf",
+                "Carlito-*.ttf",
+                "Gen*.ttf",
+                "opens___.ttf"
+        )
+    }
+
+    into("etc/fonts") {
+        from(".")
+        include("fonts.conf")
+        filter { line ->
+            line.replace(
+                    "@@APPLICATION_ID@@",
+                    android.defaultConfig.applicationId.toString()
+            )
+        }
+    }
+}
+
+tasks.register<Copy>("copyAssets") {
+    description = "copies assets that can be accessed within the installed apk"
+    into("assets")
+
+    // include icons, Impress styles and required .ui files
+    into("share") {
+        into("config") {
+            from("$liboInstdir/share/config")
+            include(
+                    "images_**.zip",
+                    "**/simpress/**.xml",
+                    "**/annotation.ui",
+                    "**/hfmenubutton.ui",
+                    "**/inforeadonlydialog.ui",
+                    "**/pbmenubutton.ui",
+                    "**/scrollbars.ui",
+                    "**/tabbuttons.ui",
+                    "**/tabbuttonsmirrored.ui",
+                    "**/tabviewbar.ui"
+            )
+        }
+    }
+
+    into("program") {
+        from("$liboInstdir/program")
+        include("services.rdb", "services/services.rdb")
+
+        into("resource") {
+            from("$liboInstdir/$liboSharedResFolder")
+            include("*en-US.res")
+        }
+    }
+    into("share") {
+        from("$liboInstdir/share") {
+            // Filter data is needed by e.g. the drawingML preset shape import.
+            include("registry/**", "filter/**")
+            // those two get processed by mobile-config.py
+            exclude("registry/main.xcd", "registry/res/registry_en-US.xcd")
+        }
+        // separate data files for Chinese and Japanese
+        from("$liboWorkdir/CustomTarget/i18npool/breakiterator/") {
+            include("*.data")
+        }
+    }
+}
+
+tasks.register<Copy>("copyAppResources") {
+    description = "copies documents to make them available as app resources"
+    into("res_generated/raw")
+    from("$liboInstdir") {
+        include("LICENSE", "NOTICE")
+        rename("LICENSE", "license.txt")
+        rename("NOTICE", "notice.txt")
+    }
+}
+
+tasks.register("createStrippedConfig") {
+    val preserveDir = file("assets_strippedUI/share/config/soffice.cfg/empty")
+    outputs.dir("assets_strippedUI")
+    outputs.dir("assets_strippedUI/share/registry/res")
+    outputs.file(preserveDir)
+
+    doLast {
+        file("assets_strippedUI/share/registry/res").mkdirs()
+        file("assets_strippedUI/share/config/soffice.cfg").mkdirs()
+        // just empty file
+        preserveDir.writeText("")
+    }
+}
+
+tasks.register<Exec>("createStrippedConfigMain") {
+    dependsOn("createStrippedConfig")
+    inputs.files("$liboInstdir/share/registry/main.xcd", 
"$liboSrcRoot/android/mobile-config.py")
+    outputs.file("assets_strippedUI/share/registry/main.xcd")
+    executable = "$liboSrcRoot/android/mobile-config.py"
+    args("$liboInstdir/share/registry/main.xcd", 
"assets_strippedUI/share/registry/main.xcd")
+}
+
+tasks.register<Exec>("createStrippedConfigRegistry") {
+    dependsOn("createStrippedConfig")
+    inputs.files("$liboInstdir/share/registry/res/registry_en-US.xcd", 
"$liboSrcRoot/android/mobile-config.py")
+    outputs.file("assets_strippedUI/share/registry/res/registry_en-US.xcd")
+    executable = "$liboSrcRoot/android/mobile-config.py"
+    args("$liboInstdir/share/registry/res/registry_en-US.xcd", 
"assets_strippedUI/share/registry/res/registry_en-US.xcd")
+    doFirst {
+        file("assets_strippedUI/share/registry/res").mkdirs()
+    }
+}
+
+tasks.register("createRCfiles") {
+    dependsOn("copyUnpackAssets", "copyAssets")
+    val sofficerc = file("assets/unpack/program/sofficerc")
+    val fundamentalrc = file("assets/program/fundamentalrc")
+    val bootstraprc = file("assets/program/bootstraprc")
+    val unorc = file("assets/program/unorc")
+    val versionrc = file("assets/program/versionrc")
+
+    outputs.files(sofficerc, fundamentalrc, unorc, bootstraprc, versionrc)
+
+    doLast {
+        sofficerc.writeText("""
+            [Bootstrap]
+            Logo=1
+            NativeProgress=1
+            URE_BOOTSTRAP=file:///assets/program/fundamentalrc
+            HOME=${"$"}APP_DATA_DIR/cache
+            OSL_SOCKET_PATH=${"$"}APP_DATA_DIR/cache
+            """.trimIndent())
+
+        fundamentalrc.writeText("""
+            [Bootstrap]
+            LO_LIB_DIR=file://${"$"}APP_DATA_DIR/lib/
+            BRAND_BASE_DIR=file:///assets
+            BRAND_SHARE_SUBDIR=share
+            CONFIGURATION_LAYERS=xcsxcu:${"$"}{BRAND_BASE_DIR}/share/registry 
res:${"$"}{BRAND_BASE_DIR}/share/registry
+            
URE_BIN_DIR=file:///assets/ure/bin/dir/nothing-here/we-can/exec-anyway
+            """.trimIndent())
+
+        bootstraprc.writeText("""
+            [Bootstrap]
+            InstallMode=<installmode>
+            ProductKey=LibreOffice ${liboVersionMajor}.${liboVersionMinor}
+            UserInstallation=file://${"$"}APP_DATA_DIR
+            """.trimIndent())
+
+        unorc.writeText("""
+            [Bootstrap]
+            URE_INTERNAL_LIB_DIR=file://${"$"}APP_DATA_DIR/lib/
+            UNO_TYPES=file://${"$"}APP_DATA_DIR/program/udkapi.rdb 
file://${"$"}APP_DATA_DIR/program/offapi.rdb 
file://${"$"}APP_DATA_DIR/program/oovbaapi.rdb
+            UNO_SERVICES=file:///assets/program/services.rdb 
file:///assets/program/services/services.rdb
+            """.trimIndent())
+
+        versionrc.writeText("""
+            [Version]
+            AllLanguages=en-US
+            buildid=${liboGitFullCommit}
+            ReferenceOOoMajorMinor=4.1
+            """.trimIndent())
+    }
+}
+
+// creating the UI stuff is cheap, don't bother only applying it for the 
flavor...
+tasks.preBuild {
+    dependsOn("createRCfiles", "createStrippedConfigMain", 
"createStrippedConfigRegistry", "copyAppResources")
+}
+
+tasks.clean {
+    dependsOn("cleanCopyAssets", "cleanCreateStrippedConfig")
+}

Reply via email to