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") +}
