diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 7984cdab3..0adffebbe 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -1,6 +1,9 @@ name: build -on: [push, pull_request] +on: + push: + pull_request: + workflow_dispatch: jobs: build-android: @@ -18,50 +21,121 @@ jobs: - name: Checkout uses: actions/checkout@v4 with: - submodules: true + submodules: false # We'll handle this manually for better control + token: ${{ secrets.GITHUB_TOKEN }} + - name: Initialize Submodules (Robust) + run: | + echo "=== Initial Git Status ===" + git status + echo "=== Checking .gitmodules ===" + cat .gitmodules || echo "No .gitmodules found" + echo "=== Checking git config for submodules ===" + git config --list | grep submodule || echo "No submodule config found" + echo "=== Checking .git/modules ===" + ls -la .git/modules/ || echo "No .git/modules found" + echo "=== Force clean submodule state and re-init ===" + git submodule deinit --all --force || true + rm -rf .git/modules/library || true + rm -rf .git/modules/app || true + echo "=== Re-sync and init submodules ===" + git submodule sync --recursive + git submodule update --init --recursive --force + echo "=== Submodule status after clean init ===" + git submodule status --recursive + echo "=== Manual clone fallback if needed ===" + if [ ! -d "library/src/main/cpp/love" ]; then + echo "Love submodule still missing, manual clone..." + mkdir -p library/src/main/cpp + git clone https://github.com/love2d/love.git library/src/main/cpp/love --depth 1 + fi + if [ ! -d "library/src/main/cpp/megasource" ]; then + echo "Megasource submodule still missing, manual clone..." + mkdir -p library/src/main/cpp + git clone https://github.com/love2d/megasource.git library/src/main/cpp/megasource --depth 1 + fi + echo "=== Final verification ===" + ls -la library/src/main/cpp/ + ls -la library/src/main/cpp/love/ | head -5 || echo "love directory empty/missing" + ls -la library/src/main/cpp/megasource/ | head -5 || echo "megasource directory empty/missing" - name: Setup Java 17 uses: actions/setup-java@v4 with: - distribution: adopt-hotspot - java-version: 17 + distribution: 'temurin' # adopt-hotspot is an alias for temurin + java-version: '17' # This will pick the latest 17.x + # Or be more specific like '17.0.10' if '17.0.15' is problematic cache: gradle - - name: Build Normal Flavor - run: bash ./gradlew assembleNormalRecord${{ matrix.build_type }} - - name: Build Release-specific Binaries + + # Build Library AAR variants + - name: Build Library AAR (Normal Record ${{ matrix.build_type }}) + run: bash ./gradlew :library:assembleNormalRecord${{ matrix.build_type }} + - name: Build Library AAR Release variants if: ${{ matrix.build_type == 'Release' }} - run: bash ./gradlew bundleNormalNoRecordRelease bundleEmbedRecordRelease bundleEmbedNoRecordRelease - - name: Artifact (Normal debug APK) + run: bash ./gradlew :library:assembleNormalNoRecordRelease :library:assembleEmbedRecordRelease :library:assembleEmbedNoRecordRelease + + # Build Sample App Debug APK (only for Debug builds) + - name: Build Sample App Debug APK if: ${{ matrix.build_type == 'Debug' }} + run: bash ./gradlew :sample:assembleDebug + + # Upload Library AAR artifacts + - name: Artifact (Library AAR Normal Record Debug) + if: ${{ matrix.build_type == 'Debug' }} + uses: actions/upload-artifact@v4 + with: + name: love-library-normal-record-debug.aar + path: library/build/outputs/aar/library-normal-record-debug.aar + + - name: Artifact (Library AAR Normal Record Release) + if: ${{ matrix.build_type == 'Release' }} uses: actions/upload-artifact@v4 with: - name: love-android-debug.apk - path: app/build/outputs/apk/normalRecord/debug/app-normal-record-debug.apk - - name: Artifact (Normal unsigned APK) + name: love-library-normal-record-release.aar + path: library/build/outputs/aar/library-normal-record-release.aar + + - name: Artifact (Library AAR Normal No Record Release) if: ${{ matrix.build_type == 'Release' }} uses: actions/upload-artifact@v4 with: - name: love-android.apk - path: app/build/outputs/apk/normalRecord/release/app-normal-record-release-unsigned.apk - - name: Artifact (Normal AAB w/o recording) + name: love-library-normal-norecord-release.aar + path: library/build/outputs/aar/library-normal-noRecord-release.aar + + - name: Artifact (Library AAR Embed Record Release) if: ${{ matrix.build_type == 'Release' }} uses: actions/upload-artifact@v4 with: - name: love-android-ps.aab - path: app/build/outputs/bundle/normalNoRecordRelease/app-normal-noRecord-release.aab - - name: Artifact (Embed AAB) + name: love-library-embed-record-release.aar + path: library/build/outputs/aar/library-embed-record-release.aar + + - name: Artifact (Library AAR Embed No Record Release) if: ${{ matrix.build_type == 'Release' }} uses: actions/upload-artifact@v4 with: - name: love-android-embed-record.aab - path: app/build/outputs/bundle/embedRecordRelease/app-embed-record-release.aab - - name: Artifact (Embed AAB w/o recording) + name: love-library-embed-norecord-release.aar + path: library/build/outputs/aar/library-embed-noRecord-release.aar + + # Upload Sample App APK + - name: Artifact (Sample App Debug APK) + if: ${{ matrix.build_type == 'Debug' }} + uses: actions/upload-artifact@v4 + with: + name: love-sample-app-debug.apk + path: sample/build/outputs/apk/debug/sample-debug.apk + + # Test JitPack build compatibility + - name: Test JitPack Build if: ${{ matrix.build_type == 'Release' }} + run: bash ./gradlew :library:publishToMavenLocal + + # Upload Debug symbols (for both library and sample app) + - name: Artifact (Library Debug symbols) uses: actions/upload-artifact@v4 with: - name: love-android-embed.aab - path: app/build/outputs/bundle/embedNoRecordRelease/app-embed-noRecord-release.aab - - name: Artifact (Debug symbols) + name: love-library-unstripped-debugsyms-${{ matrix.build_type }} + path: library/build/intermediates/cxx + + - name: Artifact (Sample App Debug symbols) + if: ${{ matrix.build_type == 'Debug' }} uses: actions/upload-artifact@v4 with: - name: love-android-unstripped-debugsyms-${{ matrix.build_type }} - path: app/build/intermediates/cxx + name: love-sample-app-unstripped-debugsyms-debug + path: sample/build/intermediates/cxx diff --git a/.gitmodules b/.gitmodules index d6ed0644f..895b1be15 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,8 +1,8 @@ -[submodule "app/src/main/cpp/love"] - path = app/src/main/cpp/love - url = https://github.com/love2d/love +[submodule "library/src/main/cpp/love"] + path = library/src/main/cpp/love + url = https://github.com/love2d/love.git branch = main -[submodule "app/src/main/cpp/megasource"] - path = app/src/main/cpp/megasource - url = https://github.com/love2d/megasource +[submodule "library/src/main/cpp/megasource"] + path = library/src/main/cpp/megasource + url = https://github.com/love2d/megasource.git branch = main diff --git a/README.md b/README.md index fd93e62dc..8c565f293 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,11 @@ +About fork: +--------- +This is a fork of the original project, it moves the app into a library and publishes it into an aar for easy app integration. + +https://jitpack.io/#eltonkola/love-android/Tag + +if there is interest we can clean it and try to merge into the main project + Android Port of LÖVE, an awesome 2D game engine for Lua (http://love2d.org) Copyright (c) 2006-2024 LOVE Development Team diff --git a/app/src/main/cpp/love b/app/src/main/cpp/love deleted file mode 160000 index 0f39eeb73..000000000 --- a/app/src/main/cpp/love +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 0f39eeb73c619eb4e4a80e4774d64c802b7d04be diff --git a/app/src/main/cpp/megasource b/app/src/main/cpp/megasource deleted file mode 160000 index 3582488fb..000000000 --- a/app/src/main/cpp/megasource +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 3582488fbc61d81ec3e99a216086c1a9964298aa diff --git a/build.gradle b/build.gradle index a7254e286..d9f4be71c 100644 --- a/build.gradle +++ b/build.gradle @@ -6,6 +6,8 @@ buildscript { } dependencies { classpath 'com.android.tools.build:gradle:8.7.0' + classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:2.0.21' + classpath 'org.jetbrains.kotlin:compose-compiler-gradle-plugin:2.0.21' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files diff --git a/app/build.gradle b/library/build.gradle similarity index 61% rename from app/build.gradle rename to library/build.gradle index deaedf3fc..ff8731460 100644 --- a/app/build.gradle +++ b/library/build.gradle @@ -1,7 +1,8 @@ -import java.nio.charset.StandardCharsets +import java.nio.charset.StandardCharsets // Keep if getAppName or other parts need it plugins { - id 'com.android.application' + id 'com.android.library' + id 'maven-publish' // Keep: essential for maven publishing } android { @@ -9,7 +10,8 @@ android { ndkVersion '27.1.12297006' defaultConfig { - applicationId project.properties["app.application_id"] + // Use distinct properties for library versioning if they differ from app versions + // e.g., project.properties["lib.version_code"] versionCode project.properties["app.version_code"].toInteger() versionName project.properties["app.version_name"] minSdk 23 @@ -19,21 +21,19 @@ android { externalNativeBuild { cmake { arguments "-DANDROID_STL=c++_shared", "-DANDROID_SUPPORT_FLEXIBLE_PAGE_SIZES=1" - // https://issuetracker.google.com/issues/274493986 - // Transitive shared library that's added through `add_dependencies` is not taken into - // account. This result in liboboe.so and libluajit.so not get included into the final - // APK. "love" target depends on LuaJIT, and OpenAL that depends on oboe::oboe. So, - // add "OpenAL" and "love" target. targets "love_android", "OpenAL", "love" } } ndk { - //noinspection ChromeOsAbiSupport abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86_64' debugSymbolLevel 'full' } + // getAppName function and manifestPlaceholders: + // These seem more app-specific. If your library's manifest doesn't use these, + // they could be removed or adjusted for library context. + // For now, keeping them as they were in your file. def getAppName = { def nameArray = project.properties["app.name_byte_array"] def name = project.properties["app.name"] @@ -54,55 +54,54 @@ android { } manifestPlaceholders = [ - NAME:getAppName(), - ORIENTATION:project.properties["app.orientation"], + NAME:getAppName(), + ORIENTATION:project.properties["app.orientation"], ] } + // retrieveAll3pModules function: Keep as is if used by your sourceSets def retrieveAll3pModules = { -> def modules = [] - fileTree("src/main/cpp/lua-modules/").visit { FileVisitDetails details -> if (details.isDirectory()) { if (file(details.file.path + "/Android.mk").exists() || file(details.file.path + "/CMakeLists.mk").exists()) { def logger = project.getLogger() logger.lifecycle("3rd-party module: " + details.file.path) - def javainfo = file(details.file.path + "/java.txt") if (javainfo.exists()) { def fstream = new FileInputStream(javainfo) def infile = new BufferedReader(new InputStreamReader(fstream)) def javapath = infile.readLine().replace("\\", "/") def mpath = null - if (javapath[0] != '/') { mpath = details.file.path + "/" + javapath } else { mpath = details.file.path + javapath } - modules << mpath - logger.lifecycle("Registered path " + mpath) infile.close() } } } } - return modules } buildTypes { release { minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + // proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' // Usually not for libraries } + // debug {} // Can add if needed } + // buildFeatures - prefab was already there, keep it. + // viewBinding was duplicated, keep one. buildFeatures { prefab true + viewBinding true // If your library uses ViewBinding. } flavorDimensions = ['mode', 'recording'] @@ -130,7 +129,7 @@ android { srcDirs += retrieveAll3pModules() } } - normal { + normal { // This was for 'normal' flavor, keep if it has specific sources java { srcDir 'src/normal/java' } @@ -141,31 +140,57 @@ android { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } - buildFeatures { - viewBinding true - } + externalNativeBuild { cmake { path file('src/main/cpp/CMakeLists.txt') - // '+' notation apparently has been supported long time ago - // https://issuetracker.google.com/issues/110693527#comment22 - // We require CMake 3.21 because r23 has important fixes - // that's only fixed if CMake 3.21 is used. - // https://github.com/android/ndk/issues/463 version '3.21.0+' } } packagingOptions { jniLibs { excludes += [ - 'lib/armeabi-v7a/libOpenSLES.so', - 'lib/arm64-v8a/libOpenSLES.so', - 'lib/x86/libOpenSLES.so', - 'lib/x86_64/libOpenSLES.so' + 'lib/armeabi-v7a/libOpenSLES.so', + 'lib/arm64-v8a/libOpenSLES.so', + 'lib/x86/libOpenSLES.so', + 'lib/x86_64/libOpenSLES.so' ] } } -} + + // RECOMMENDED: Use AGP's built-in publishing DSL (for AGP 7.0+) + // This should be placed INSIDE the android {} block. + publishing { + singleVariant("normalRecordRelease") { + // This tells AGP to prepare the 'normalRecordRelease' variant for publishing. + // The maven-publish plugin will then pick this up. + + // To include a sources JAR in the publication (highly recommended for libraries): + // withSourcesJar() + + // To include a Javadoc JAR (optional): + // withJavadocJar() + } + } + +} // End of android {} block + + +// REMOVE the old 'android.libraryVariants.all { ... }' publishing block. +// It conflicts with the android.publishing DSL and afterEvaluate. + +// REMOVE the 'afterEvaluate { ... }' publishing block. +// The debug code was helpful but the publishing part conflicts. +// The android.publishing DSL is preferred. + + +// Define project.group and project.version for Maven publishing. +// These will be used by the publication created by android.publishing and maven-publish. +// Ensure these properties are defined (e.g., in gradle.properties or set sensible defaults). +// Use properties specific to the library's Maven coordinates if they differ from the app. +project.group = project.findProperty("LIB_GROUP_ID")?.toString() ?: "org.love2d.android.sdk" // Example: use a library-specific group +project.version = project.findProperty("LIB_VERSION_NAME")?.toString() ?: android.defaultConfig.versionName // Use library version + dependencies { implementation 'androidx.appcompat:appcompat:1.7.0' @@ -178,7 +203,6 @@ dependencies { implementation 'com.google.oboe:oboe:1.9.3' } -// We don't even use Kotlin. Why we have to care about it? configurations.implementation { exclude group: 'org.jetbrains.kotlin', module: 'kotlin-stdlib-jdk8' -} +} \ No newline at end of file diff --git a/app/proguard-rules.pro b/library/proguard-rules.pro similarity index 100% rename from app/proguard-rules.pro rename to library/proguard-rules.pro diff --git a/app/src/embed/assets/.gitkeep b/library/src/embed/assets/.gitkeep similarity index 100% rename from app/src/embed/assets/.gitkeep rename to library/src/embed/assets/.gitkeep diff --git a/app/src/main/AndroidManifest.xml b/library/src/main/AndroidManifest.xml similarity index 83% rename from app/src/main/AndroidManifest.xml rename to library/src/main/AndroidManifest.xml index 2aa3feca4..a1aa463f6 100644 --- a/app/src/main/AndroidManifest.xml +++ b/library/src/main/AndroidManifest.xml @@ -1,6 +1,8 @@ + xmlns:android="http://schemas.android.com/apk/res/android" + package="org.love2d.android" + > @@ -24,25 +26,16 @@ - + + android:launchMode="singleInstance"> - - - - - diff --git a/app/src/main/cpp/CMakeLists.txt b/library/src/main/cpp/CMakeLists.txt similarity index 97% rename from app/src/main/cpp/CMakeLists.txt rename to library/src/main/cpp/CMakeLists.txt index e193b3027..e317db556 100644 --- a/app/src/main/cpp/CMakeLists.txt +++ b/library/src/main/cpp/CMakeLists.txt @@ -1,79 +1,79 @@ -# -# Copyright (c) 2006-2024 LOVE Development Team -# -# This software is provided 'as-is', without any express or implied -# warranty. In no event will the authors be held liable for any damages -# arising from the use of this software. -# -# Permission is granted to anyone to use this software for any purpose, -# including commercial applications, and to alter it and redistribute it -# freely, subject to the following restrictions: -# -# 1. The origin of this software must not be misrepresented; you must not -# claim that you wrote the original software. If you use this software -# in a product, an acknowledgment in the product documentation would be -# appreciated but is not required. -# 2. Altered source versions must be plainly marked as such, and must not be -# misrepresented as being the original software. -# 3. This notice may not be removed or altered from any source distribution. -# - -cmake_minimum_required(VERSION 3.21) - -project(love-android LANGUAGES C CXX) - -add_custom_target(love_android) -set(LOVE_ANDROID 1) - -# Include Megasource and LOVE -set(MEGA_LOVE "${CMAKE_CURRENT_SOURCE_DIR}/love") -add_subdirectory(megasource) - -add_dependencies(love_android love OpenAL) - -find_package(Python 3.9 COMPONENTS Interpreter) -if(NOT Python_Interpreter_FOUND) - message(WARNING "No Python detected, Android.mk won't be converted to CMakeLists.txt!") -endif() - -# List lua-modules - -file(GLOB MODULE_LIST - LIST_DIRECTORIES TRUE - RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" - CONFIGURE_DEPENDS - "lua-modules/*" -) -foreach(dir ${MODULE_LIST}) - if(IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/${dir}") - set(has_run_cmake 0) - # Load either CMakeLists.txt or convert Android.mk to CMakeLists.txt - if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${dir}/CMakeLists.txt") - add_subdirectory(${dir}) - set(has_run_cmake 1) - elseif(Python_Interpreter_FOUND AND EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${dir}/Android.mk") - get_filename_component(item_sanitized0 "${dir}" NAME) - string(REGEX REPLACE "[^\\w-]" "-" item_sanitized1 "${item_sanitized0}") - string(REGEX REPLACE "-+" "-" item "${item_sanitized0}") - message(STATUS "Converting ${dir} Android.mk to CMakeLists.txt") - execute_process(RESULT_VARIABLE status - COMMAND "${Python_EXECUTABLE}" amkparse.py convert "${item}" "${dir}/Android.mk" - WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" - OUTPUT_FILE "${dir}/CMakeLists.txt" - ) - if("${status}" STREQUAL "0") - add_subdirectory(${dir}) - set(has_run_cmake 1) - else() - message(FATAL_ERROR "amkparse.py returned ${status}") - endif() - endif() - - # If CMake has been run, retrieve targets and add it as dependency - if(has_run_cmake) - get_property(defined_build_targets DIRECTORY "${dir}" PROPERTY BUILDSYSTEM_TARGETS) - get_property(defined_import_targets DIRECTORY "${dir}" PROPERTY IMPORTED_TARGETS) - add_dependencies(love_android ${defined_build_targets} ${defined_import_targets}) - endif() - endif() -endforeach() +# +# Copyright (c) 2006-2024 LOVE Development Team +# +# This software is provided 'as-is', without any express or implied +# warranty. In no event will the authors be held liable for any damages +# arising from the use of this software. +# +# Permission is granted to anyone to use this software for any purpose, +# including commercial applications, and to alter it and redistribute it +# freely, subject to the following restrictions: +# +# 1. The origin of this software must not be misrepresented; you must not +# claim that you wrote the original software. If you use this software +# in a product, an acknowledgment in the product documentation would be +# appreciated but is not required. +# 2. Altered source versions must be plainly marked as such, and must not be +# misrepresented as being the original software. +# 3. This notice may not be removed or altered from any source distribution. +# + +cmake_minimum_required(VERSION 3.21) + +project(love-android LANGUAGES C CXX) + +add_custom_target(love_android) +set(LOVE_ANDROID 1) + +# Include Megasource and LOVE +set(MEGA_LOVE "${CMAKE_CURRENT_SOURCE_DIR}/love") +add_subdirectory(megasource) + +add_dependencies(love_android love OpenAL) + +find_package(Python 3.9 COMPONENTS Interpreter) +if(NOT Python_Interpreter_FOUND) + message(WARNING "No Python detected, Android.mk won't be converted to CMakeLists.txt!") +endif() + +# List lua-modules + +file(GLOB MODULE_LIST + LIST_DIRECTORIES TRUE + RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" + CONFIGURE_DEPENDS + "lua-modules/*" +) +foreach(dir ${MODULE_LIST}) + if(IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/${dir}") + set(has_run_cmake 0) + # Load either CMakeLists.txt or convert Android.mk to CMakeLists.txt + if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${dir}/CMakeLists.txt") + add_subdirectory(${dir}) + set(has_run_cmake 1) + elseif(Python_Interpreter_FOUND AND EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${dir}/Android.mk") + get_filename_component(item_sanitized0 "${dir}" NAME) + string(REGEX REPLACE "[^\\w-]" "-" item_sanitized1 "${item_sanitized0}") + string(REGEX REPLACE "-+" "-" item "${item_sanitized0}") + message(STATUS "Converting ${dir} Android.mk to CMakeLists.txt") + execute_process(RESULT_VARIABLE status + COMMAND "${Python_EXECUTABLE}" amkparse.py convert "${item}" "${dir}/Android.mk" + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" + OUTPUT_FILE "${dir}/CMakeLists.txt" + ) + if("${status}" STREQUAL "0") + add_subdirectory(${dir}) + set(has_run_cmake 1) + else() + message(FATAL_ERROR "amkparse.py returned ${status}") + endif() + endif() + + # If CMake has been run, retrieve targets and add it as dependency + if(has_run_cmake) + get_property(defined_build_targets DIRECTORY "${dir}" PROPERTY BUILDSYSTEM_TARGETS) + get_property(defined_import_targets DIRECTORY "${dir}" PROPERTY IMPORTED_TARGETS) + add_dependencies(love_android ${defined_build_targets} ${defined_import_targets}) + endif() + endif() +endforeach() diff --git a/app/src/main/cpp/amkparse.py b/library/src/main/cpp/amkparse.py similarity index 100% rename from app/src/main/cpp/amkparse.py rename to library/src/main/cpp/amkparse.py diff --git a/app/src/main/cpp/lua-modules/lua-modules-readme.txt b/library/src/main/cpp/lua-modules/lua-modules-readme.txt similarity index 98% rename from app/src/main/cpp/lua-modules/lua-modules-readme.txt rename to library/src/main/cpp/lua-modules/lua-modules-readme.txt index e8e24cd90..65a88e322 100644 --- a/app/src/main/cpp/lua-modules/lua-modules-readme.txt +++ b/library/src/main/cpp/lua-modules/lua-modules-readme.txt @@ -1,30 +1,30 @@ -This folder should contain external Lua C modules that will be shipped along with LOVE. One for each folder - -Each folder must contains: -* Either CMakeLists.txt or Android.mk. - * If Android.mk is present: - The Android.mk will be converted to CMakeLists.txt. This conversion may not 100% perfect! - The LOCAL_MODULE_FILENAME must be set accordingly as ndk-build appends "lib" prefix from LOCAL_MODULE by default. - For Lua includes and libraries, simply link the native library with "liblove". - Note: This will convert Android.mk to CMakeLists.txt. The conversion may not 100% perfect. Conditionals are not - parsed and treated as if all codepaths are taken. Modify the generated CMakeLists.txt if necessary! - Caveat: If the library is meant to be loaded as require("my.library") then you MUST set LOCAL_MODULE_FILENAME to - my.library (with dots). - * If CMakeLists.txt is present: - The CMake script is loaded directly. - For Lua includes and libraries, link with "luajit" target. - Caveat: If the library is meant to be loaded as require("my.library") then you MUST set OUTPUT_NAME property to - "my.library" (with dots) and PREFIX property to "" (empty string) on your CMake target using - "set_target_properties". -* If your module interacts with Java-side, a file java.txt must contain where it should look Java source files. - Example, if you have src/java/your/package/name/MyFile.java, then you need to write "src/java" in java.txt. - If this file is absent, then your Java-side code will not be compiled along. - -Note: There's bug in Android CMake support which prevents inclusion of imported *.so. - More information about it here: https://issuetracker.google.com/issues/274493986 - In short: - * If your library is simply an imported target (or prebuilt in ndk-build terms), convert it to standard target. - Otherwise the associated *.so won't be bundled into the APK! - * If your library depends on imported/prebuilt target, you must add your CMake target to the `app/build.gradle` - as part of the targets that need to be compiled. Add your CMake target at - `android.defaultConfig.externalNativeBuild.cmake.targets` property. +This folder should contain external Lua C modules that will be shipped along with LOVE. One for each folder + +Each folder must contains: +* Either CMakeLists.txt or Android.mk. + * If Android.mk is present: + The Android.mk will be converted to CMakeLists.txt. This conversion may not 100% perfect! + The LOCAL_MODULE_FILENAME must be set accordingly as ndk-build appends "lib" prefix from LOCAL_MODULE by default. + For Lua includes and libraries, simply link the native library with "liblove". + Note: This will convert Android.mk to CMakeLists.txt. The conversion may not 100% perfect. Conditionals are not + parsed and treated as if all codepaths are taken. Modify the generated CMakeLists.txt if necessary! + Caveat: If the library is meant to be loaded as require("my.library") then you MUST set LOCAL_MODULE_FILENAME to + my.library (with dots). + * If CMakeLists.txt is present: + The CMake script is loaded directly. + For Lua includes and libraries, link with "luajit" target. + Caveat: If the library is meant to be loaded as require("my.library") then you MUST set OUTPUT_NAME property to + "my.library" (with dots) and PREFIX property to "" (empty string) on your CMake target using + "set_target_properties". +* If your module interacts with Java-side, a file java.txt must contain where it should look Java source files. + Example, if you have src/java/your/package/name/MyFile.java, then you need to write "src/java" in java.txt. + If this file is absent, then your Java-side code will not be compiled along. + +Note: There's bug in Android CMake support which prevents inclusion of imported *.so. + More information about it here: https://issuetracker.google.com/issues/274493986 + In short: + * If your library is simply an imported target (or prebuilt in ndk-build terms), convert it to standard target. + Otherwise the associated *.so won't be bundled into the APK! + * If your library depends on imported/prebuilt target, you must add your CMake target to the `app/build.gradle` + as part of the targets that need to be compiled. Add your CMake target at + `android.defaultConfig.externalNativeBuild.cmake.targets` property. diff --git a/app/src/main/java/org/love2d/android/GameActivity.java b/library/src/main/java/org/love2d/android/GameActivity.java similarity index 100% rename from app/src/main/java/org/love2d/android/GameActivity.java rename to library/src/main/java/org/love2d/android/GameActivity.java diff --git a/app/src/main/java/org/love2d/android/IntentReceiverActivity.java b/library/src/main/java/org/love2d/android/IntentReceiverActivity.java similarity index 100% rename from app/src/main/java/org/love2d/android/IntentReceiverActivity.java rename to library/src/main/java/org/love2d/android/IntentReceiverActivity.java diff --git a/app/src/main/res/drawable-hdpi/love.png b/library/src/main/res/drawable-hdpi/love.png similarity index 100% rename from app/src/main/res/drawable-hdpi/love.png rename to library/src/main/res/drawable-hdpi/love.png diff --git a/app/src/main/res/drawable-mdpi/love.png b/library/src/main/res/drawable-mdpi/love.png similarity index 100% rename from app/src/main/res/drawable-mdpi/love.png rename to library/src/main/res/drawable-mdpi/love.png diff --git a/app/src/main/res/drawable-xhdpi/love.png b/library/src/main/res/drawable-xhdpi/love.png similarity index 100% rename from app/src/main/res/drawable-xhdpi/love.png rename to library/src/main/res/drawable-xhdpi/love.png diff --git a/app/src/main/res/drawable-xxhdpi/love.png b/library/src/main/res/drawable-xxhdpi/love.png similarity index 100% rename from app/src/main/res/drawable-xxhdpi/love.png rename to library/src/main/res/drawable-xxhdpi/love.png diff --git a/app/src/main/res/drawable-xxxhdpi/love.png b/library/src/main/res/drawable-xxxhdpi/love.png similarity index 100% rename from app/src/main/res/drawable-xxxhdpi/love.png rename to library/src/main/res/drawable-xxxhdpi/love.png diff --git a/app/src/normal/AndroidManifest.xml b/library/src/normal/AndroidManifest.xml similarity index 87% rename from app/src/normal/AndroidManifest.xml rename to library/src/normal/AndroidManifest.xml index fe81de954..f866dab90 100644 --- a/app/src/normal/AndroidManifest.xml +++ b/library/src/normal/AndroidManifest.xml @@ -26,11 +26,11 @@ - + + + + + { - - private Data[] data = null; - - @NonNull - @Override - public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { - View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.row_game, parent, false); - ViewHolder holder = new ViewHolder(view); - view.setOnClickListener(holder); - - return holder; - } - - @Override - public void onBindViewHolder(ViewHolder holder, int position) { - holder.setData(this.data[position]); - } - - @Override - public int getItemCount() { - return data != null ? data.length : 0; - } - - public void setData(Data[] data) { - this.data = data; - } - - static class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener { - - private final TextView name; - private final ImageView image; - private File file; - - public ViewHolder(View itemView) { - super(itemView); - - name = itemView.findViewById(R.id.textView); - image = itemView.findViewById(R.id.imageView); - } - - public void setData(Data data) { - name.setText(data.path.getName()); - image.setImageResource(data.directory ? R.drawable.ic_baseline_folder_32 : R.drawable.ic_baseline_insert_drive_file_32); - file = data.path; - } - - @Override - public void onClick(View v) { - if (file == null) { - return; - } - - Context context = v.getContext(); - Intent intent = new Intent(context, GameActivity.class); - intent.setData(Uri.fromFile(file)); - context.startActivity(intent); - } - } - - static class Data { - // Absolute path of the file. - public File path; - // Denote if this game is a directory. - public boolean directory; - } -} +/* + * Copyright (c) 2006-2024 LOVE Development Team + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +package org.love2d.android; + +import android.content.Context; +import android.content.Intent; +import android.net.Uri; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.recyclerview.widget.RecyclerView; + +import java.io.File; + +public class GameListAdapter extends RecyclerView.Adapter { + + private Data[] data = null; + + @NonNull + @Override + public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.row_game, parent, false); + ViewHolder holder = new ViewHolder(view); + view.setOnClickListener(holder); + + return holder; + } + + @Override + public void onBindViewHolder(ViewHolder holder, int position) { + holder.setData(this.data[position]); + } + + @Override + public int getItemCount() { + return data != null ? data.length : 0; + } + + public void setData(Data[] data) { + this.data = data; + } + + static class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener { + + private final TextView name; + private final ImageView image; + private File file; + + public ViewHolder(View itemView) { + super(itemView); + + name = itemView.findViewById(R.id.textView); + image = itemView.findViewById(R.id.imageView); + } + + public void setData(Data data) { + name.setText(data.path.getName()); + image.setImageResource(data.directory ? R.drawable.ic_baseline_folder_32 : R.drawable.ic_baseline_insert_drive_file_32); + file = data.path; + } + + @Override + public void onClick(View v) { + if (file == null) { + return; + } + + Context context = v.getContext(); + Intent intent = new Intent(context, GameActivity.class); + intent.setData(Uri.fromFile(file)); + context.startActivity(intent); + } + } + + static class Data { + // Absolute path of the file. + public File path; + // Denote if this game is a directory. + public boolean directory; + } +} diff --git a/app/src/normal/java/org/love2d/android/LoveDocumentsProvider.java b/library/src/normal/java/org/love2d/android/LoveDocumentsProvider.java similarity index 100% rename from app/src/normal/java/org/love2d/android/LoveDocumentsProvider.java rename to library/src/normal/java/org/love2d/android/LoveDocumentsProvider.java diff --git a/app/src/normal/java/org/love2d/android/MainActivity.java b/library/src/normal/java/org/love2d/android/MainActivity.java similarity index 100% rename from app/src/normal/java/org/love2d/android/MainActivity.java rename to library/src/normal/java/org/love2d/android/MainActivity.java diff --git a/app/src/normal/res/drawable/ic_baseline_folder_32.xml b/library/src/normal/res/drawable/ic_baseline_folder_32.xml similarity index 100% rename from app/src/normal/res/drawable/ic_baseline_folder_32.xml rename to library/src/normal/res/drawable/ic_baseline_folder_32.xml diff --git a/app/src/normal/res/drawable/ic_baseline_info_32.xml b/library/src/normal/res/drawable/ic_baseline_info_32.xml similarity index 100% rename from app/src/normal/res/drawable/ic_baseline_info_32.xml rename to library/src/normal/res/drawable/ic_baseline_info_32.xml diff --git a/app/src/normal/res/drawable/ic_baseline_insert_drive_file_32.xml b/library/src/normal/res/drawable/ic_baseline_insert_drive_file_32.xml similarity index 100% rename from app/src/normal/res/drawable/ic_baseline_insert_drive_file_32.xml rename to library/src/normal/res/drawable/ic_baseline_insert_drive_file_32.xml diff --git a/app/src/normal/res/layout/activity_about.xml b/library/src/normal/res/layout/activity_about.xml similarity index 100% rename from app/src/normal/res/layout/activity_about.xml rename to library/src/normal/res/layout/activity_about.xml diff --git a/app/src/normal/res/layout/activity_main.xml b/library/src/normal/res/layout/activity_main.xml similarity index 100% rename from app/src/normal/res/layout/activity_main.xml rename to library/src/normal/res/layout/activity_main.xml diff --git a/app/src/normal/res/layout/row_game.xml b/library/src/normal/res/layout/row_game.xml similarity index 97% rename from app/src/normal/res/layout/row_game.xml rename to library/src/normal/res/layout/row_game.xml index 69d7b196a..0b02e0381 100644 --- a/app/src/normal/res/layout/row_game.xml +++ b/library/src/normal/res/layout/row_game.xml @@ -1,32 +1,32 @@ - - - - - - - + + + + + + + diff --git a/app/src/normal/res/menu/options_menu.xml b/library/src/normal/res/menu/options_menu.xml similarity index 96% rename from app/src/normal/res/menu/options_menu.xml rename to library/src/normal/res/menu/options_menu.xml index 4ec267a5f..e33d89c25 100644 --- a/app/src/normal/res/menu/options_menu.xml +++ b/library/src/normal/res/menu/options_menu.xml @@ -1,16 +1,16 @@ - - - - - - - - + + + + + + + + diff --git a/app/src/normal/res/values/strings.xml b/library/src/normal/res/values/strings.xml similarity index 100% rename from app/src/normal/res/values/strings.xml rename to library/src/normal/res/values/strings.xml diff --git a/sample/.gitignore b/sample/.gitignore new file mode 100644 index 000000000..42afabfd2 --- /dev/null +++ b/sample/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/sample/build.gradle b/sample/build.gradle new file mode 100644 index 000000000..ec7e7703f --- /dev/null +++ b/sample/build.gradle @@ -0,0 +1,68 @@ +plugins { + id 'com.android.application' + id 'org.jetbrains.kotlin.android' + id 'org.jetbrains.kotlin.plugin.compose' +} + +android { + namespace 'org.love2d.android' + compileSdk 35 + + defaultConfig { + applicationId "org.love2d.android" + minSdk 24 + targetSdk 35 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_11 + targetCompatibility JavaVersion.VERSION_11 + } + kotlinOptions { + jvmTarget = '11' + } + buildFeatures { + compose true + } +} + +dependencies { + + +// implementation project(path: ':library') + implementation(project(':library')) { + attributes { + attribute( + Attribute.of("com.android.build.gradle.internal.attributes.VariantAttr", String), + "normalRecordDebug" + ) + } + } +// implementation project(path: ':library', configuration: 'embedNoRecordDebug') + + implementation 'androidx.core:core-ktx:1.16.0' + implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.9.1' + implementation 'androidx.activity:activity-compose:1.10.1' + implementation platform('androidx.compose:compose-bom:2025.06.00') + implementation 'androidx.compose.ui:ui' + implementation 'androidx.compose.ui:ui-graphics' + implementation 'androidx.compose.ui:ui-tooling-preview' + implementation 'androidx.compose.material3:material3' + testImplementation 'junit:junit:4.13.2' + androidTestImplementation 'androidx.test.ext:junit:1.2.1' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.6.1' + androidTestImplementation platform('androidx.compose:compose-bom:2025.06.00') + androidTestImplementation 'androidx.compose.ui:ui-test-junit4' + debugImplementation 'androidx.compose.ui:ui-tooling' + debugImplementation 'androidx.compose.ui:ui-test-manifest' +} \ No newline at end of file diff --git a/sample/proguard-rules.pro b/sample/proguard-rules.pro new file mode 100644 index 000000000..481bb4348 --- /dev/null +++ b/sample/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/sample/src/androidTest/java/org/love2d/android/ExampleInstrumentedTest.kt b/sample/src/androidTest/java/org/love2d/android/ExampleInstrumentedTest.kt new file mode 100644 index 000000000..43dd4a290 --- /dev/null +++ b/sample/src/androidTest/java/org/love2d/android/ExampleInstrumentedTest.kt @@ -0,0 +1,24 @@ +package org.love2d.android + +import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.ext.junit.runners.AndroidJUnit4 + +import org.junit.Test +import org.junit.runner.RunWith + +import org.junit.Assert.* + +/** + * Instrumented test, which will execute on an Android device. + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +@RunWith(AndroidJUnit4::class) +class ExampleInstrumentedTest { + @Test + fun useAppContext() { + // Context of the app under test. + val appContext = InstrumentationRegistry.getInstrumentation().targetContext + assertEquals("org.love2d.android", appContext.packageName) + } +} \ No newline at end of file diff --git a/sample/src/main/AndroidManifest.xml b/sample/src/main/AndroidManifest.xml new file mode 100644 index 000000000..15d1aa3eb --- /dev/null +++ b/sample/src/main/AndroidManifest.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + diff --git a/sample/src/main/assets/hello.love b/sample/src/main/assets/hello.love new file mode 100644 index 000000000..3c9cb11f7 Binary files /dev/null and b/sample/src/main/assets/hello.love differ diff --git a/sample/src/main/assets/snake.love b/sample/src/main/assets/snake.love new file mode 100644 index 000000000..ca1531391 Binary files /dev/null and b/sample/src/main/assets/snake.love differ diff --git a/sample/src/main/java/org/love2d/android/SampleActivity.kt b/sample/src/main/java/org/love2d/android/SampleActivity.kt new file mode 100644 index 000000000..3fbb40af3 --- /dev/null +++ b/sample/src/main/java/org/love2d/android/SampleActivity.kt @@ -0,0 +1,220 @@ +package org.love2d.android + +import android.content.Context +import android.content.Intent +import android.net.Uri +import android.os.Bundle +import android.widget.Toast +import androidx.activity.ComponentActivity +import androidx.activity.compose.setContent +import androidx.activity.enableEdgeToEdge +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.Button +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Text +import androidx.compose.material3.TopAppBar +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.core.content.ContextCompat.startActivity +import androidx.core.content.FileProvider +import org.love2d.android.ui.theme.LÖVEForAndroidTheme +import java.io.File +import java.io.FileOutputStream + +class SampleActivity : ComponentActivity() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + enableEdgeToEdge() + setContent { + LÖVEForAndroidTheme { + GameList( + modifier = Modifier.fillMaxSize() + ) + } + } + } +} + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun GameList(modifier: Modifier = Modifier) { + + Scaffold( + modifier = modifier, + topBar = { + TopAppBar( + title = { Text(text = "Games") } + ) + } + ){ + + val context = LocalContext.current + + Column ( + modifier = Modifier.padding(it).padding(32.dp) + ) { + Button( + onClick = { + context.launchLoveWithLuaFile() + }, + modifier = Modifier.fillMaxWidth() + ){ + Text(text = "Empty Game") + } + + Spacer(modifier = Modifier.height(16.dp)) + + Button( + onClick = { + context.launchLoveDirectly("hello.love") + }, + modifier = Modifier.fillMaxWidth() + ){ + Text(text = "Hello") + } + Spacer(modifier = Modifier.height(16.dp)) + + Button( + onClick = { + context.launchLoveDirectly("snake.love") + }, + modifier = Modifier.fillMaxWidth() + ){ + Text(text = "Snake Demo Game") + } + + + } + } + + +} + +fun Context.launchLoveDirectly(luaFileName: String) { + val inputStream = assets.open(luaFileName) + val file = File(filesDir, luaFileName) + + inputStream.use { input -> + file.outputStream().use { output -> + input.copyTo(output) + } + } + + // Go directly to GameActivity + val intent = Intent(this, GameActivity::class.java) + intent.setData(Uri.fromFile(file)) + + startActivity(intent) +} + + +fun Context.launchLoveWithLuaFile(luaFileName: String? = null) { + if(luaFileName == null){ + val intent = Intent(this, GameActivity::class.java) + startActivity(intent) + }else { + // Copy your Lua file from assets to internal storage + val inputStream = assets.open(luaFileName) + val file = File(filesDir, luaFileName) + + inputStream.use { input -> + file.outputStream().use { output -> + input.copyTo(output) + } + } + + // Create file URI + val fileUri = FileProvider.getUriForFile( + this, + "${packageName}.fileprovider", // You'll need to set up FileProvider + file + ) + + // Send intent to IntentReceiverActivity + val intent = Intent(this, IntentReceiverActivity::class.java) + intent.action = Intent.ACTION_SEND + intent.putExtra(Intent.EXTRA_STREAM, fileUri) + intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) + + startActivity(intent) + } +} + + +private fun Context.openGame(path: String?=null){ + if(path == null){ + val intent = Intent(this, GameActivity::class.java) + startActivity(intent) + }else { + + + val lojaFile = copyAssetToInternalStorage(this, path) + println(">>>>> game path :" + lojaFile.absolutePath) + println(">>>>> game content :" + lojaFile.readText()) + if (lojaFile.exists()) { + val intent = Intent(this, GameActivity::class.java) + intent.data = Uri.fromFile(lojaFile) + println(">>>>> game url:" + intent.data) + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_NEW_DOCUMENT) + startActivity(intent) + +// val intent = Intent(this, IntentReceiverActivity::class.java).apply { +// action = Intent.ACTION_SEND +// +// val uri = Uri.fromFile(lojaFile) +// +// putExtra(Intent.EXTRA_STREAM, uri) +// type = "application/octet-stream" // Or "text/plain" +// addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) +// } +// this.startActivity(intent) + + + } else { + Toast.makeText(this, "UPS, file does not exist!", Toast.LENGTH_SHORT).show() + } + } +} + +fun copyAssetToCache(context: Context, filename: String): File { + val file = File(context.cacheDir, filename) + file.parentFile?.mkdirs() + context.assets.open(filename).use { input -> + FileOutputStream(file).use { output -> + input.copyTo(output) + } + } + return file // Now you can use file.absolutePath +} + +fun copyAssetToInternalStorage(context: Context, assetName: String): File { + val inputStream = context.assets.open(assetName) + val file = File(context.filesDir, assetName) + + inputStream.use { input -> + file.outputStream().use { output -> + input.copyTo(output) + } + } + + return file +} + + + +@Preview(showBackground = true) +@Composable +fun GameListPreview() { + LÖVEForAndroidTheme { + GameList() + } +} \ No newline at end of file diff --git a/sample/src/main/java/org/love2d/android/ui/theme/Color.kt b/sample/src/main/java/org/love2d/android/ui/theme/Color.kt new file mode 100644 index 000000000..982c60181 --- /dev/null +++ b/sample/src/main/java/org/love2d/android/ui/theme/Color.kt @@ -0,0 +1,11 @@ +package org.love2d.android.ui.theme + +import androidx.compose.ui.graphics.Color + +val Purple80 = Color(0xFFD0BCFF) +val PurpleGrey80 = Color(0xFFCCC2DC) +val Pink80 = Color(0xFFEFB8C8) + +val Purple40 = Color(0xFF6650a4) +val PurpleGrey40 = Color(0xFF625b71) +val Pink40 = Color(0xFF7D5260) \ No newline at end of file diff --git a/sample/src/main/java/org/love2d/android/ui/theme/Theme.kt b/sample/src/main/java/org/love2d/android/ui/theme/Theme.kt new file mode 100644 index 000000000..110e7b32f --- /dev/null +++ b/sample/src/main/java/org/love2d/android/ui/theme/Theme.kt @@ -0,0 +1,58 @@ +package org.love2d.android.ui.theme + +import android.app.Activity +import android.os.Build +import androidx.compose.foundation.isSystemInDarkTheme +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.darkColorScheme +import androidx.compose.material3.dynamicDarkColorScheme +import androidx.compose.material3.dynamicLightColorScheme +import androidx.compose.material3.lightColorScheme +import androidx.compose.runtime.Composable +import androidx.compose.ui.platform.LocalContext + +private val DarkColorScheme = darkColorScheme( + primary = Purple80, + secondary = PurpleGrey80, + tertiary = Pink80 +) + +private val LightColorScheme = lightColorScheme( + primary = Purple40, + secondary = PurpleGrey40, + tertiary = Pink40 + + /* Other default colors to override + background = Color(0xFFFFFBFE), + surface = Color(0xFFFFFBFE), + onPrimary = Color.White, + onSecondary = Color.White, + onTertiary = Color.White, + onBackground = Color(0xFF1C1B1F), + onSurface = Color(0xFF1C1B1F), + */ +) + +@Composable +fun LÖVEForAndroidTheme( + darkTheme: Boolean = isSystemInDarkTheme(), + // Dynamic color is available on Android 12+ + dynamicColor: Boolean = true, + content: @Composable () -> Unit +) { + val colorScheme = when { + dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> { + val context = LocalContext.current + if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context) + } + + darkTheme -> DarkColorScheme + else -> LightColorScheme + } + + MaterialTheme( + colorScheme = colorScheme, + typography = Typography, + content = content + ) +} \ No newline at end of file diff --git a/sample/src/main/java/org/love2d/android/ui/theme/Type.kt b/sample/src/main/java/org/love2d/android/ui/theme/Type.kt new file mode 100644 index 000000000..cdedc0d0b --- /dev/null +++ b/sample/src/main/java/org/love2d/android/ui/theme/Type.kt @@ -0,0 +1,34 @@ +package org.love2d.android.ui.theme + +import androidx.compose.material3.Typography +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.font.FontFamily +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.sp + +// Set of Material typography styles to start with +val Typography = Typography( + bodyLarge = TextStyle( + fontFamily = FontFamily.Default, + fontWeight = FontWeight.Normal, + fontSize = 16.sp, + lineHeight = 24.sp, + letterSpacing = 0.5.sp + ) + /* Other default text styles to override + titleLarge = TextStyle( + fontFamily = FontFamily.Default, + fontWeight = FontWeight.Normal, + fontSize = 22.sp, + lineHeight = 28.sp, + letterSpacing = 0.sp + ), + labelSmall = TextStyle( + fontFamily = FontFamily.Default, + fontWeight = FontWeight.Medium, + fontSize = 11.sp, + lineHeight = 16.sp, + letterSpacing = 0.5.sp + ) + */ +) \ No newline at end of file diff --git a/sample/src/main/res/drawable/ic_launcher_background.xml b/sample/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 000000000..07d5da9cb --- /dev/null +++ b/sample/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/sample/src/main/res/drawable/ic_launcher_foreground.xml b/sample/src/main/res/drawable/ic_launcher_foreground.xml new file mode 100644 index 000000000..2b068d114 --- /dev/null +++ b/sample/src/main/res/drawable/ic_launcher_foreground.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/sample/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/sample/src/main/res/mipmap-anydpi-v26/ic_launcher.xml new file mode 100644 index 000000000..6f3b755bf --- /dev/null +++ b/sample/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/sample/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/sample/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml new file mode 100644 index 000000000..6f3b755bf --- /dev/null +++ b/sample/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/sample/src/main/res/mipmap-hdpi/ic_launcher.webp b/sample/src/main/res/mipmap-hdpi/ic_launcher.webp new file mode 100644 index 000000000..c209e78ec Binary files /dev/null and b/sample/src/main/res/mipmap-hdpi/ic_launcher.webp differ diff --git a/sample/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/sample/src/main/res/mipmap-hdpi/ic_launcher_round.webp new file mode 100644 index 000000000..b2dfe3d1b Binary files /dev/null and b/sample/src/main/res/mipmap-hdpi/ic_launcher_round.webp differ diff --git a/sample/src/main/res/mipmap-mdpi/ic_launcher.webp b/sample/src/main/res/mipmap-mdpi/ic_launcher.webp new file mode 100644 index 000000000..4f0f1d64e Binary files /dev/null and b/sample/src/main/res/mipmap-mdpi/ic_launcher.webp differ diff --git a/sample/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/sample/src/main/res/mipmap-mdpi/ic_launcher_round.webp new file mode 100644 index 000000000..62b611da0 Binary files /dev/null and b/sample/src/main/res/mipmap-mdpi/ic_launcher_round.webp differ diff --git a/sample/src/main/res/mipmap-xhdpi/ic_launcher.webp b/sample/src/main/res/mipmap-xhdpi/ic_launcher.webp new file mode 100644 index 000000000..948a3070f Binary files /dev/null and b/sample/src/main/res/mipmap-xhdpi/ic_launcher.webp differ diff --git a/sample/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/sample/src/main/res/mipmap-xhdpi/ic_launcher_round.webp new file mode 100644 index 000000000..1b9a6956b Binary files /dev/null and b/sample/src/main/res/mipmap-xhdpi/ic_launcher_round.webp differ diff --git a/sample/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/sample/src/main/res/mipmap-xxhdpi/ic_launcher.webp new file mode 100644 index 000000000..28d4b77f9 Binary files /dev/null and b/sample/src/main/res/mipmap-xxhdpi/ic_launcher.webp differ diff --git a/sample/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/sample/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp new file mode 100644 index 000000000..9287f5083 Binary files /dev/null and b/sample/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp differ diff --git a/sample/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/sample/src/main/res/mipmap-xxxhdpi/ic_launcher.webp new file mode 100644 index 000000000..aa7d6427e Binary files /dev/null and b/sample/src/main/res/mipmap-xxxhdpi/ic_launcher.webp differ diff --git a/sample/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/sample/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp new file mode 100644 index 000000000..9126ae37c Binary files /dev/null and b/sample/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp differ diff --git a/sample/src/main/res/raw/hello.lua b/sample/src/main/res/raw/hello.lua new file mode 100644 index 000000000..bc46983d6 --- /dev/null +++ b/sample/src/main/res/raw/hello.lua @@ -0,0 +1,3 @@ +function love.draw() + love.graphics.print("Hello World", 400, 300) +end \ No newline at end of file diff --git a/sample/src/main/res/values/colors.xml b/sample/src/main/res/values/colors.xml new file mode 100644 index 000000000..f8c6127d3 --- /dev/null +++ b/sample/src/main/res/values/colors.xml @@ -0,0 +1,10 @@ + + + #FFBB86FC + #FF6200EE + #FF3700B3 + #FF03DAC5 + #FF018786 + #FF000000 + #FFFFFFFF + \ No newline at end of file diff --git a/sample/src/main/res/values/strings.xml b/sample/src/main/res/values/strings.xml new file mode 100644 index 000000000..cb706a844 --- /dev/null +++ b/sample/src/main/res/values/strings.xml @@ -0,0 +1,3 @@ + + Sample + \ No newline at end of file diff --git a/sample/src/main/res/values/themes.xml b/sample/src/main/res/values/themes.xml new file mode 100644 index 000000000..e21951389 --- /dev/null +++ b/sample/src/main/res/values/themes.xml @@ -0,0 +1,5 @@ + + + +