From 62ae7adab2bebde04864c12543caefbffab24963 Mon Sep 17 00:00:00 2001 From: Maksim Date: Wed, 15 Apr 2020 16:27:40 +0200 Subject: [PATCH] Android: add Android Studio support, completely redone java part (#9066) --- .gitignore | 20 +- build/android/.gitignore | 11 + build/android/Makefile | 763 ------------------ build/android/app/build.gradle | 108 +++ .../{ => app}/src/main/AndroidManifest.xml | 38 +- .../net/minetest/minetest/CopyZipTask.java | 82 ++ .../net/minetest/minetest/GameActivity.java} | 104 +-- .../minetest/InputDialogActivity.java | 98 +++ .../net/minetest/minetest/MainActivity.java | 139 ++++ .../net/minetest/minetest/UnzipService.java | 153 ++++ .../src/main/res/drawable/background.png | Bin .../{ => app}/src/main/res/drawable/bg.xml | 2 +- .../app/src/main/res/layout/activity_main.xml | 30 + .../src/main/res/mipmap/ic_launcher.png | Bin .../app/src/main/res/values/strings.xml | 10 + .../app/src/main/res/values/styles.xml | 22 + build/android/build.gradle | 175 +--- build/android/gradle.properties | 11 +- .../android/gradle/wrapper/gradle-wrapper.jar | Bin 54731 -> 55616 bytes .../gradle/wrapper/gradle-wrapper.properties | 3 +- build/android/gradlew | 22 +- build/android/gradlew.bat | 18 +- build/android/jni/Android.mk | 448 ---------- build/android/jni/Application.mk | 9 - build/android/jni/Deps.mk | 7 - build/android/jni/Irrlicht.mk | 8 - build/android/native/build.gradle | 59 ++ build/android/native/jni/Android.mk | 218 +++++ build/android/native/jni/Application.mk | 32 + .../native/src/main/AndroidManifest.xml | 1 + .../patches/irrlicht-back_button.patch | 20 - .../patches/irrlicht-native_activity.patch | 13 - .../patches/irrlicht-texturehack.patch | 240 ------ .../android/patches/irrlicht-touchcount.patch | 30 - .../patches/libvorbis-libogg-fpu.patch | 37 - build/android/patches/openssl_arch.patch | 13 - build/android/settings.gradle | 1 + .../net.minetest.minetest/MainActivity.java | 77 -- .../MinetestAssetCopy.java | 371 --------- .../MinetestTextEntry.java | 87 -- .../android/src/main/res/layout/assetcopy.xml | 24 - .../src/main/res/values-v21/styles.xml | 17 - build/android/src/main/res/values/strings.xml | 5 - build/android/src/main/res/values/styles.xml | 14 - doc/README.android | 75 +- src/config.h | 6 +- src/main.cpp | 1 - src/porting_android.cpp | 17 +- src/porting_android.h | 5 - src/version.cpp | 5 +- 50 files changed, 1129 insertions(+), 2520 deletions(-) create mode 100644 build/android/.gitignore delete mode 100644 build/android/Makefile create mode 100644 build/android/app/build.gradle rename build/android/{ => app}/src/main/AndroidManifest.xml (68%) create mode 100644 build/android/app/src/main/java/net/minetest/minetest/CopyZipTask.java rename build/android/{src/main/java/net.minetest.minetest/MtNativeActivity.java => app/src/main/java/net/minetest/minetest/GameActivity.java} (51%) create mode 100644 build/android/app/src/main/java/net/minetest/minetest/InputDialogActivity.java create mode 100644 build/android/app/src/main/java/net/minetest/minetest/MainActivity.java create mode 100644 build/android/app/src/main/java/net/minetest/minetest/UnzipService.java rename build/android/{ => app}/src/main/res/drawable/background.png (100%) rename build/android/{ => app}/src/main/res/drawable/bg.xml (82%) create mode 100644 build/android/app/src/main/res/layout/activity_main.xml rename build/android/{ => app}/src/main/res/mipmap/ic_launcher.png (100%) create mode 100644 build/android/app/src/main/res/values/strings.xml create mode 100644 build/android/app/src/main/res/values/styles.xml delete mode 100644 build/android/jni/Android.mk delete mode 100644 build/android/jni/Application.mk delete mode 100644 build/android/jni/Deps.mk delete mode 100644 build/android/jni/Irrlicht.mk create mode 100644 build/android/native/build.gradle create mode 100644 build/android/native/jni/Android.mk create mode 100644 build/android/native/jni/Application.mk create mode 100644 build/android/native/src/main/AndroidManifest.xml delete mode 100644 build/android/patches/irrlicht-back_button.patch delete mode 100644 build/android/patches/irrlicht-native_activity.patch delete mode 100644 build/android/patches/irrlicht-texturehack.patch delete mode 100644 build/android/patches/irrlicht-touchcount.patch delete mode 100644 build/android/patches/libvorbis-libogg-fpu.patch delete mode 100644 build/android/patches/openssl_arch.patch delete mode 100644 build/android/src/main/java/net.minetest.minetest/MainActivity.java delete mode 100644 build/android/src/main/java/net.minetest.minetest/MinetestAssetCopy.java delete mode 100644 build/android/src/main/java/net.minetest.minetest/MinetestTextEntry.java delete mode 100644 build/android/src/main/res/layout/assetcopy.xml delete mode 100644 build/android/src/main/res/values-v21/styles.xml delete mode 100644 build/android/src/main/res/values/strings.xml delete mode 100644 build/android/src/main/res/values/styles.xml diff --git a/.gitignore b/.gitignore index aae9bf437..9ab69b30e 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ *.swp *.bak* *.orig +.DS_Store # Vim *.vim # Kate @@ -77,17 +78,11 @@ doc/mkdocs/mkdocs.yml ## Build files CMakeFiles Makefile -!build/android/Makefile -build/android/path.cfg -build/android/*.apk -build/android/.externalNativeBuild cmake_install.cmake CMakeCache.txt CPackConfig.cmake CPackSourceConfig.cmake src/test_config.h -src/android_version.h -src/android_version_githash.h src/cmake_config.h src/cmake_config_githash.h src/unittest/test_world/world.mt @@ -109,16 +104,3 @@ cmake_config.h cmake_config_githash.h CMakeDoxy* compile_commands.json - -## Android build files -build/android/src/main/assets -build/android/build -build/android/deps -build/android/libs -build/android/jni/lib -build/android/jni/src -build/android/src/main/jniLibs -build/android/obj -build/android/local.properties -build/android/.gradle -timestamp diff --git a/build/android/.gitignore b/build/android/.gitignore new file mode 100644 index 000000000..e0613f8b3 --- /dev/null +++ b/build/android/.gitignore @@ -0,0 +1,11 @@ +*.iml +.externalNativeBuild +.gradle +app/build +app/release +app/src/main/assets +build +local.properties +native/.* +native/build +native/deps diff --git a/build/android/Makefile b/build/android/Makefile deleted file mode 100644 index 9ec237a75..000000000 --- a/build/android/Makefile +++ /dev/null @@ -1,763 +0,0 @@ -# build options - -OS := $(shell uname) - -# compile with GPROF -# GPROF = 1 - -# build for build platform -API = 14 -APP_PLATFORM = android-$(API) - -ANDR_ROOT = $(shell pwd) -PROJ_ROOT = $(shell realpath $(ANDR_ROOT)/../..) -APP_ROOT = $(ANDR_ROOT)/src/main - -VERSION_MAJOR := $(shell cat $(PROJ_ROOT)/CMakeLists.txt | \ - grep ^set\(VERSION_MAJOR\ | sed 's/)/ /' | cut -f2 -d' ') -VERSION_MINOR := $(shell cat $(PROJ_ROOT)/CMakeLists.txt | \ - grep ^set\(VERSION_MINOR\ | sed 's/)/ /' | cut -f2 -d' ') -VERSION_PATCH := $(shell cat $(PROJ_ROOT)/CMakeLists.txt | \ - grep ^set\(VERSION_PATCH\ | sed 's/)/ /' | cut -f2 -d' ') - -################################################################################ -# toolchain config for arm new processors -################################################################################ -TARGET_HOST = arm-linux -TARGET_ABI = armeabi-v7a -TARGET_LIBDIR = armeabi-v7a -TARGET_TOOLCHAIN = arm-linux-androideabi- -TARGET_CFLAGS_ADDON = -mfloat-abi=softfp -mfpu=vfpv3 -O3 -TARGET_CXXFLAGS_ADDON = $(TARGET_CFLAGS_ADDON) -TARGET_ARCH = armv7 -CROSS_CC = clang -CROSS_CXX = clang++ -COMPILER_VERSION = clang -HAVE_LEVELDB = 0 - -################################################################################ -# toolchain config for little endian mips -################################################################################ -#TARGET_HOST = mipsel-linux -#TARGET_ABI = mips -#TARGET_LIBDIR = mips -#TARGET_TOOLCHAIN = mipsel-linux-android- -#TARGET_ARCH = mips32 -#CROSS_CC = mipsel-linux-android-gcc -#CROSS_CXX = mipsel-linux-android-g++ -#COMPILER_VERSION = 4.9 -#HAVE_LEVELDB = 0 - -################################################################################ -# toolchain config for x86 -################################################################################ -#TARGET_HOST = x86-linux -#TARGET_ABI = x86 -#TARGET_LIBDIR = x86 -#TARGET_TOOLCHAIN = x86- -#TARGET_ARCH = x86 -#CROSS_CC = clang -#CROSS_CXX = clang++ -#COMPILER_VERSION = clang -#HAVE_LEVELDB = 0 - -################################################################################ -ASSETS_TIMESTAMP = deps/assets_timestamp - -LEVELDB_DIR = $(ANDR_ROOT)/deps/leveldb/ -LEVELDB_LIB = $(LEVELDB_DIR)libleveldb.a -LEVELDB_TIMESTAMP = $(LEVELDB_DIR)/timestamp -LEVELDB_TIMESTAMP_INT = $(ANDR_ROOT)/deps/leveldb_timestamp -LEVELDB_URL_GIT = https://github.com/google/leveldb -LEVELDB_COMMIT = 2d0320a458d0e6a20fff46d5f80b18bfdcce7018 - -OPENAL_DIR = $(ANDR_ROOT)/deps/openal-soft/ -OPENAL_LIB = $(OPENAL_DIR)libs/$(TARGET_ABI)/libopenal.so -OPENAL_TIMESTAMP = $(OPENAL_DIR)/timestamp -OPENAL_TIMESTAMP_INT = $(ANDR_ROOT)/deps/openal_timestamp -OPENAL_URL_GIT = https://github.com/apportable/openal-soft - -OGG_DIR = $(ANDR_ROOT)/deps/libvorbis-libogg-android/ -OGG_LIB = $(OGG_DIR)libs/$(TARGET_ABI)/libogg.so -VORBIS_LIB = $(OGG_DIR)libs/$(TARGET_ABI)/libogg.so -OGG_TIMESTAMP = $(OGG_DIR)timestamp -OGG_TIMESTAMP_INT = $(ANDR_ROOT)/deps/ogg_timestamp -OGG_URL_GIT = https://gitlab.com/minetest/libvorbis-libogg-android - -IRRLICHT_REVISION = 5150 -IRRLICHT_DIR = $(ANDR_ROOT)/deps/irrlicht/ -IRRLICHT_LIB = $(IRRLICHT_DIR)lib/Android/libIrrlicht.a -IRRLICHT_TIMESTAMP = $(IRRLICHT_DIR)timestamp -IRRLICHT_TIMESTAMP_INT = $(ANDR_ROOT)/deps/irrlicht_timestamp -IRRLICHT_URL_SVN = https://svn.code.sf.net/p/irrlicht/code/branches/ogl-es@$(IRRLICHT_REVISION) - -OPENSSL_VERSION = 1.0.2n -OPENSSL_BASEDIR = openssl-$(OPENSSL_VERSION) -OPENSSL_DIR = $(ANDR_ROOT)/deps/$(OPENSSL_BASEDIR)/ -OPENSSL_LIB = $(OPENSSL_DIR)/libssl.a -OPENSSL_TIMESTAMP = $(OPENSSL_DIR)timestamp -OPENSSL_TIMESTAMP_INT = $(ANDR_ROOT)/deps/openssl_timestamp -OPENSSL_URL = https://www.openssl.org/source/openssl-$(OPENSSL_VERSION).tar.gz - -CURL_VERSION = 7.60.0 -CURL_DIR = $(ANDR_ROOT)/deps/curl-$(CURL_VERSION) -CURL_LIB = $(CURL_DIR)/lib/.libs/libcurl.a -CURL_TIMESTAMP = $(CURL_DIR)/timestamp -CURL_TIMESTAMP_INT = $(ANDR_ROOT)/deps/curl_timestamp -CURL_URL_HTTP = https://curl.haxx.se/download/curl-${CURL_VERSION}.tar.bz2 - -FREETYPE_DIR = $(ANDR_ROOT)/deps/freetype2-android/ -FREETYPE_LIB = $(FREETYPE_DIR)/Android/obj/local/$(TARGET_ABI)/libfreetype2-static.a -FREETYPE_TIMESTAMP = $(FREETYPE_DIR)timestamp -FREETYPE_TIMESTAMP_INT = $(ANDR_ROOT)/deps/freetype_timestamp -FREETYPE_URL_GIT = https://github.com/cdave1/freetype2-android - -ICONV_VERSION = 1.16 -ICONV_DIR = $(ANDR_ROOT)/deps/libiconv/ -ICONV_LIB = $(ICONV_DIR)/lib/.libs/libiconv.so -ICONV_TIMESTAMP = $(ICONV_DIR)timestamp -ICONV_TIMESTAMP_INT = $(ANDR_ROOT)/deps/iconv_timestamp -ICONV_URL_HTTP = https://ftp.gnu.org/pub/gnu/libiconv/libiconv-$(ICONV_VERSION).tar.gz - -SQLITE3_FOLDER = sqlite-amalgamation-3240000 -SQLITE3_URL = https://www.sqlite.org/2018/$(SQLITE3_FOLDER).zip - -ANDROID_SDK = $(shell grep '^sdk\.dir' local.properties | sed 's/^.*=[[:space:]]*//') -ANDROID_NDK = $(shell grep '^ndk\.dir' local.properties | sed 's/^.*=[[:space:]]*//') - -#use interim target variable to switch leveldb on or off -ifeq ($(HAVE_LEVELDB),1) - LEVELDB_TARGET = $(LEVELDB_LIB) -endif - -.PHONY : debug release reconfig delconfig \ - leveldb_download clean_leveldb leveldb\ - irrlicht_download clean_irrlicht irrlicht \ - clean_assets assets sqlite3_download \ - freetype_download clean_freetype freetype \ - apk clean_apk \ - clean_all clean prep_srcdir \ - install_debug install_release envpaths all \ - $(ASSETS_TIMESTAMP) $(LEVELDB_TIMESTAMP) \ - $(OPENAL_TIMESTAMP) $(OGG_TIMESTAMP) \ - $(IRRLICHT_TIMESTAMP) $(CURL_TIMESTAMP) \ - $(OPENSSL_TIMESTAMP) \ - $(ANDR_ROOT)/jni/src/android_version.h \ - $(ANDR_ROOT)/jni/src/android_version_githash.h - -debug : local.properties - export NDEBUG=; \ - export BUILD_TYPE=debug; \ - $(MAKE) apk - -all : debug release - -release : local.properties - @export NDEBUG=1; \ - export BUILD_TYPE=release; \ - $(MAKE) apk - -reconfig: delconfig - @$(MAKE) local.properties - -delconfig: - $(RM) local.properties - -local.properties: - @echo "Please specify path of ANDROID NDK"; \ - echo "e.g. $$HOME/Android/Sdk/ndk-bundle/"; \ - read ANDROID_NDK ; \ - if [ ! -d $$ANDROID_NDK ] ; then \ - echo "$$ANDROID_NDK is not a valid folder"; \ - exit 1; \ - fi; \ - echo "ndk.dir = $$ANDROID_NDK" > local.properties; \ - echo "Please specify path of ANDROID SDK"; \ - echo "e.g. $$HOME/Android/Sdk/"; \ - read SDKFLDR ; \ - if [ ! -d $$SDKFLDR ] ; then \ - echo "$$SDKFLDR is not a valid folder"; \ - exit 1; \ - fi; \ - echo "sdk.dir = $$SDKFLDR" >> local.properties; - - -$(OPENAL_TIMESTAMP) : openal_download - @LAST_MODIF=$$(find ${OPENAL_DIR} -type f -printf '%T@ %p\n' | sort -n | tail -1 | cut -f2- -d" "); \ - if [ $$(basename $$LAST_MODIF) != "timestamp" ] ; then \ - touch ${OPENAL_TIMESTAMP}; \ - fi - -openal_download : - @if [ ! -d ${OPENAL_DIR} ] ; then \ - echo "openal sources missing, downloading..."; \ - mkdir -p ${ANDR_ROOT}/deps; \ - cd ${ANDR_ROOT}/deps ; \ - git clone ${OPENAL_URL_GIT} || exit 1; \ - fi - -openal : $(OPENAL_LIB) - -$(OPENAL_LIB): $(OPENAL_TIMESTAMP) - + @REFRESH=0; \ - if [ ! -e ${OPENAL_TIMESTAMP_INT} ] ; then \ - REFRESH=1; \ - fi; \ - if [ ${OPENAL_TIMESTAMP} -nt ${OPENAL_TIMESTAMP_INT} ] ; then \ - REFRESH=1; \ - fi; \ - if [ $$REFRESH -ne 0 ] ; then \ - echo "changed timestamp for openal detected building..."; \ - cd ${OPENAL_DIR}; \ - export APP_PLATFORM=${APP_PLATFORM}; \ - export TARGET_ABI=${TARGET_ABI}; \ - export TARGET_CFLAGS_ADDON="${TARGET_CFLAGS_ADDON}"; \ - export TARGET_CXXFLAGS_ADDON="${TARGET_CXXFLAGS_ADDON}"; \ - export COMPILER_VERSION=${COMPILER_VERSION}; \ - ${ANDROID_NDK}/ndk-build \ - NDK_APPLICATION_MK=${ANDR_ROOT}/jni/Deps.mk || exit 1; \ - touch ${OPENAL_TIMESTAMP}; \ - touch ${OPENAL_TIMESTAMP_INT}; \ - else \ - echo "nothing to be done for openal"; \ - fi - -clean_openal : - $(RM) -rf ${OPENAL_DIR} - -$(OGG_TIMESTAMP) : ogg_download - @LAST_MODIF=$$(find ${OGG_DIR} -type f -printf '%T@ %p\n' | sort -n | tail -1 | cut -f2- -d" "); \ - if [ $$(basename $$LAST_MODIF) != "timestamp" ] ; then \ - touch ${OGG_TIMESTAMP}; \ - fi - -ogg_download : - @if [ ! -d ${OGG_DIR} ] ; then \ - echo "ogg sources missing, downloading..."; \ - mkdir -p ${ANDR_ROOT}/deps; \ - cd ${ANDR_ROOT}/deps ; \ - git clone ${OGG_URL_GIT}|| exit 1; \ - cd libvorbis-libogg-android ; \ - patch -p1 < ${ANDR_ROOT}/patches/libvorbis-libogg-fpu.patch || exit 1; \ - fi - -ogg : $(OGG_LIB) - -$(OGG_LIB): $(OGG_TIMESTAMP) - + @REFRESH=0; \ - if [ ! -e ${OGG_TIMESTAMP_INT} ] ; then \ - echo "${OGG_TIMESTAMP_INT} doesn't exist"; \ - REFRESH=1; \ - fi; \ - if [ ${OGG_TIMESTAMP} -nt ${OGG_TIMESTAMP_INT} ] ; then \ - REFRESH=1; \ - fi; \ - if [ $$REFRESH -ne 0 ] ; then \ - echo "changed timestamp for ogg detected building..."; \ - cd ${OGG_DIR}; \ - export APP_PLATFORM=${APP_PLATFORM}; \ - export TARGET_ABI=${TARGET_ABI}; \ - ${ANDROID_NDK}/build/tools/make-standalone-toolchain.sh \ - --toolchain=${TARGET_TOOLCHAIN}${COMPILER_VERSION} \ - --platform=${APP_PLATFORM} \ - --install-dir=$${TOOLCHAIN}; \ - touch ${OGG_TIMESTAMP}; \ - touch ${OGG_TIMESTAMP_INT}; \ - else \ - echo "nothing to be done for libogg/libvorbis"; \ - fi - -clean_ogg : - $(RM) -rf ${OGG_DIR} - -$(OPENSSL_TIMESTAMP) : openssl_download - @LAST_MODIF=$$(find ${OPENSSL_DIR} -type f -printf '%T@ %p\n' | sort -n | tail -1 | cut -f2- -d" "); \ - if [ $$(basename $$LAST_MODIF) != "timestamp" ] ; then \ - touch ${OPENSSL_TIMESTAMP}; \ - fi - -openssl_download : - @if [ ! -d ${OPENSSL_DIR} ] ; then \ - echo "openssl sources missing, downloading..."; \ - mkdir -p ${ANDR_ROOT}/deps; \ - cd ${ANDR_ROOT}/deps ; \ - wget ${OPENSSL_URL} || exit 1; \ - tar -xzf ${OPENSSL_BASEDIR}.tar.gz; \ - cd ${OPENSSL_BASEDIR}; \ - patch -p1 < ${ANDR_ROOT}/patches/openssl_arch.patch; \ - sed -i 's/-mandroid //g' Configure; \ - fi - -openssl : $(OPENSSL_LIB) - -$(OPENSSL_LIB): $(OPENSSL_TIMESTAMP) - @REFRESH=0; \ - if [ ! -e ${OPENSSL_TIMESTAMP_INT} ] ; then \ - echo "${OPENSSL_TIMESTAMP_INT} doesn't exist"; \ - REFRESH=1; \ - fi; \ - if [ ${OPENSSL_TIMESTAMP} -nt ${OPENSSL_TIMESTAMP_INT} ] ; then \ - REFRESH=1; \ - fi; \ - if [ $$REFRESH -ne 0 ] ; then \ - echo "changed timestamp for openssl detected building..."; \ - cd ${OPENSSL_DIR}; \ - ln -s ${OPENSSL_DIR} ../openssl; \ - export TOOLCHAIN=/tmp/ndk-${TARGET_HOST}-openssl; \ - ${ANDROID_NDK}/build/tools/make-standalone-toolchain.sh \ - --toolchain=${TARGET_TOOLCHAIN}${COMPILER_VERSION} \ - --platform=${APP_PLATFORM} \ - --stl=libc++ \ - --install-dir=$${TOOLCHAIN}; \ - export PATH="$${TOOLCHAIN}/bin:$${PATH}"; \ - export CFLAGS="$${CFLAGS} ${TARGET_CFLAGS_ADDON}"; \ - export LDFLAGS="$${LDFLAGS} ${TARGET_LDFLAGS_ADDON}"; \ - CC=${CROSS_CC} ./Configure -DL_ENDIAN no-asm android-${TARGET_ARCH} \ - -D__ANDROID_API__=$(API); \ - CC=${CROSS_CC} ANDROID_DEV=/tmp/ndk-${TARGET_HOST} make depend; \ - CC=${CROSS_CC} ANDROID_DEV=/tmp/ndk-${TARGET_HOST} make build_libs; \ - touch ${OPENSSL_TIMESTAMP}; \ - touch ${OPENSSL_TIMESTAMP_INT}; \ - $(RM) -rf $${TOOLCHAIN}; \ - else \ - echo "nothing to be done for openssl"; \ - fi - -clean_openssl : - $(RM) -rf ${OPENSSL_DIR}; \ - $(RM) -rf $(ANDR_ROOT)/deps/${OPENSSL_BASEDIR}.tar.gz; \ - $(RM) -rf $(ANDR_ROOT)/deps/openssl - -$(LEVELDB_TIMESTAMP) : leveldb_download - @LAST_MODIF=$$(find ${LEVELDB_DIR} -type f -printf '%T@ %p\n' | sort -n | tail -1 | cut -f2- -d" "); \ - if [ $$(basename $$LAST_MODIF) != "timestamp" ] ; then \ - touch ${LEVELDB_TIMESTAMP}; \ - fi - -leveldb_download : - @if [ ! -d ${LEVELDB_DIR} ] ; then \ - echo "leveldb sources missing, downloading..."; \ - mkdir -p ${ANDR_ROOT}/deps; \ - cd ${ANDR_ROOT}/deps ; \ - git clone ${LEVELDB_URL_GIT} || exit 1; \ - cd ${LEVELDB_DIR} || exit 1; \ - git checkout ${LEVELDB_COMMIT} || exit 1; \ - fi - -leveldb : $(LEVELDB_LIB) -ifeq ($(HAVE_LEVELDB),1) -$(LEVELDB_LIB): $(LEVELDB_TIMESTAMP) - @REFRESH=0; \ - if [ ! -e ${LEVELDB_TIMESTAMP_INT} ] ; then \ - REFRESH=1; \ - fi; \ - if [ ${LEVELDB_TIMESTAMP} -nt ${LEVELDB_TIMESTAMP_INT} ] ; then \ - REFRESH=1; \ - fi; \ - if [ $$REFRESH -ne 0 ] ; then \ - echo "changed timestamp for leveldb detected building..."; \ - cd deps/leveldb; \ - export CROSS_PREFIX=${TARGET_TOOLCHAIN}; \ - export TOOLCHAIN=/tmp/ndk-${TARGET_HOST}-leveldb; \ - ${ANDROID_NDK}/build/tools/make-standalone-toolchain.sh \ - --toolchain=${TARGET_TOOLCHAIN}${COMPILER_VERSION} \ - --platform=${APP_PLATFORM} \ - --stl=libc++ \ - --install-dir=$${TOOLCHAIN}; \ - export PATH="$${TOOLCHAIN}/bin:$${PATH}"; \ - export CC=${CROSS_CC}; \ - export CXX=${CROSS_CXX}; \ - export CFLAGS="$${CFLAGS} ${TARGET_CFLAGS_ADDON}"; \ - export CPPFLAGS="$${CPPFLAGS} ${TARGET_CXXFLAGS_ADDON}"; \ - export LDFLAGS="$${LDFLAGS} ${TARGET_LDFLAGS_ADDON}"; \ - export TARGET_OS=OS_ANDROID_CROSSCOMPILE; \ - $(MAKE) || exit 1; \ - touch ${LEVELDB_TIMESTAMP}; \ - touch ${LEVELDB_TIMESTAMP_INT}; \ - $(RM) -rf $${TOOLCHAIN}; \ - else \ - echo "nothing to be done for leveldb"; \ - fi -endif - -clean_leveldb : - ./gradlew cleanLevelDB - -$(FREETYPE_TIMESTAMP) : freetype_download - @LAST_MODIF=$$(find ${FREETYPE_DIR} -type f -printf '%T@ %p\n' | sort -n | tail -1 | cut -f2- -d" "); \ - if [ $$(basename $$LAST_MODIF) != "timestamp" ] ; then \ - touch ${FREETYPE_TIMESTAMP}; \ - fi - -freetype_download : - @if [ ! -d ${FREETYPE_DIR} ] ; then \ - echo "freetype sources missing, downloading..."; \ - mkdir -p ${ANDR_ROOT}/deps; \ - cd deps; \ - git clone ${FREETYPE_URL_GIT} || exit 1; \ - fi - -freetype : $(FREETYPE_LIB) - -$(FREETYPE_LIB) : $(FREETYPE_TIMESTAMP) - + @REFRESH=0; \ - if [ ! -e ${FREETYPE_TIMESTAMP_INT} ] ; then \ - REFRESH=1; \ - fi; \ - if [ ! -e ${FREETYPE_LIB} ] ; then \ - REFRESH=1; \ - fi; \ - if [ ${FREETYPE_TIMESTAMP} -nt ${FREETYPE_TIMESTAMP_INT} ] ; then \ - REFRESH=1; \ - fi; \ - if [ $$REFRESH -ne 0 ] ; then \ - mkdir -p ${FREETYPE_DIR}; \ - echo "changed timestamp for freetype detected building..."; \ - cd ${FREETYPE_DIR}/Android/jni; \ - export APP_PLATFORM=${APP_PLATFORM}; \ - export TARGET_ABI=${TARGET_ABI}; \ - export TARGET_CFLAGS_ADDON="${TARGET_CFLAGS_ADDON}"; \ - export TARGET_CXXFLAGS_ADDON="${TARGET_CXXFLAGS_ADDON}"; \ - export COMPILER_VERSION=${COMPILER_VERSION}; \ - ${ANDROID_NDK}/ndk-build \ - NDK_APPLICATION_MK=${ANDR_ROOT}/jni/Deps.mk || exit 1; \ - touch ${FREETYPE_TIMESTAMP}; \ - touch ${FREETYPE_TIMESTAMP_INT}; \ - else \ - echo "nothing to be done for freetype"; \ - fi - -clean_freetype : - ./gradlew cleanFreetype - -$(ICONV_TIMESTAMP) : iconv_download - @LAST_MODIF=$$(find ${ICONV_DIR} -type f -printf '%T@ %p\n' | sort -n | tail -1 | cut -f2- -d" "); \ - if [ $$(basename $$LAST_MODIF) != "timestamp" ] ; then \ - touch ${ICONV_TIMESTAMP}; \ - fi - -iconv_download : - @if [ ! -d ${ICONV_DIR} ] ; then \ - echo "iconv sources missing, downloading..."; \ - mkdir -p ${ANDR_ROOT}/deps; \ - cd ${ANDR_ROOT}/deps; \ - wget ${ICONV_URL_HTTP} || exit 1; \ - tar -xzf libiconv-${ICONV_VERSION}.tar.gz || exit 1; \ - rm libiconv-${ICONV_VERSION}.tar.gz; \ - ln -s libiconv-${ICONV_VERSION} libiconv; \ - fi - -iconv : $(ICONV_LIB) - -$(ICONV_LIB) : $(ICONV_TIMESTAMP) - @REFRESH=0; \ - if [ ! -e ${ICONV_TIMESTAMP_INT} ] ; then \ - REFRESH=1; \ - fi; \ - if [ ! -e ${ICONV_LIB} ] ; then \ - REFRESH=1; \ - fi; \ - if [ ${ICONV_TIMESTAMP} -nt ${ICONV_TIMESTAMP_INT} ] ; then \ - REFRESH=1; \ - fi; \ - if [ $$REFRESH -ne 0 ] ; then \ - mkdir -p ${ICONV_DIR}; \ - echo "changed timestamp for iconv detected building..."; \ - cd ${ICONV_DIR}; \ - export TOOLCHAIN=/tmp/ndk-${TARGET_HOST}-iconv; \ - ${ANDROID_NDK}/build/tools/make-standalone-toolchain.sh \ - --toolchain=${TARGET_TOOLCHAIN}${COMPILER_VERSION} \ - --platform=${APP_PLATFORM} \ - --stl=libc++ \ - --install-dir=$${TOOLCHAIN}; \ - export PATH="$${TOOLCHAIN}/bin:$${PATH}"; \ - export CFLAGS="$${CFLAGS} ${TARGET_CFLAGS_ADDON}"; \ - export LDFLAGS="$${LDFLAGS} ${TARGET_LDFLAGS_ADDON} -lstdc++"; \ - export CC=${CROSS_CC}; \ - export CXX=${CROSS_CXX}; \ - export TARGET_OS=OS_ANDROID_CROSSCOMPILE; \ - ./configure --host=${TARGET_HOST} || exit 1; \ - sed -i 's/LIBICONV_VERSION_INFO) /LIBICONV_VERSION_INFO) -avoid-version /g' lib/Makefile; \ - grep "iconv_LDFLAGS" src/Makefile; \ - $(MAKE) -s || exit 1; \ - touch ${ICONV_TIMESTAMP}; \ - touch ${ICONV_TIMESTAMP_INT}; \ - rm -rf ${TOOLCHAIN}; \ - else \ - echo "nothing to be done for iconv"; \ - fi - -clean_iconv : - ./gradlew cleanIconv - -#Note: Texturehack patch is required for gpu's not supporting color format -# correctly. Known bad GPU: -# -geforce on emulator -# -Vivante Corporation GC1000 core (e.g. Galaxy Tab 3) - -irrlicht_download : - @if [ ! -d "deps/irrlicht" ] ; then \ - echo "irrlicht sources missing, downloading..."; \ - mkdir -p ${ANDR_ROOT}/deps; \ - cd deps; \ - svn co ${IRRLICHT_URL_SVN} irrlicht || exit 1; \ - cd irrlicht; \ - patch -p1 < ${ANDR_ROOT}/patches/irrlicht-touchcount.patch || exit 1; \ - patch -p1 < ${ANDR_ROOT}/patches/irrlicht-back_button.patch || exit 1; \ - patch -p1 < ${ANDR_ROOT}/patches/irrlicht-texturehack.patch || exit 1; \ - patch -p1 < ${ANDR_ROOT}/patches/irrlicht-native_activity.patch || exit 1; \ - fi - -$(IRRLICHT_TIMESTAMP) : irrlicht_download - @LAST_MODIF=$$(find ${IRRLICHT_DIR} -type f -printf '%T@ %p\n' | sort -n | tail -1 | cut -f2- -d" "); \ - if [ $$(basename $$LAST_MODIF) != "timestamp" ] ; then \ - touch ${IRRLICHT_TIMESTAMP}; \ - fi - -irrlicht : $(IRRLICHT_LIB) - -$(IRRLICHT_LIB): $(IRRLICHT_TIMESTAMP) $(FREETYPE_LIB) - + @REFRESH=0; \ - if [ ! -e ${IRRLICHT_TIMESTAMP_INT} ] ; then \ - REFRESH=1; \ - fi; \ - if [ ! -e ${IRRLICHT_LIB} ] ; then \ - REFRESH=1; \ - fi; \ - if [ ${IRRLICHT_TIMESTAMP} -nt ${IRRLICHT_TIMESTAMP_INT} ] ; then \ - REFRESH=1; \ - fi; \ - if [ $$REFRESH -ne 0 ] ; then \ - mkdir -p ${IRRLICHT_DIR}; \ - echo "changed timestamp for irrlicht detected building..."; \ - cd deps/irrlicht/source/Irrlicht/Android; \ - export APP_PLATFORM=${APP_PLATFORM}; \ - export TARGET_ABI=${TARGET_ABI}; \ - export TARGET_CFLAGS_ADDON="${TARGET_CFLAGS_ADDON}"; \ - export TARGET_CXXFLAGS_ADDON="${TARGET_CXXFLAGS_ADDON}"; \ - export COMPILER_VERSION=${COMPILER_VERSION}; \ - ${ANDROID_NDK}/ndk-build \ - NDK_APPLICATION_MK=${ANDR_ROOT}/jni/Deps.mk || exit 1; \ - touch ${IRRLICHT_TIMESTAMP}; \ - touch ${IRRLICHT_TIMESTAMP_INT}; \ - else \ - echo "nothing to be done for irrlicht"; \ - fi - -clean_irrlicht : - ./gradlew cleanIrrlicht - -$(CURL_TIMESTAMP) : curl_download - @LAST_MODIF=$$(find ${CURL_DIR} -type f -printf '%T@ %p\n' | sort -n | tail -1 | cut -f2- -d" "); \ - if [ $$(basename $$LAST_MODIF) != "timestamp" ] ; then \ - touch ${CURL_TIMESTAMP}; \ - fi - -curl_download : - @if [ ! -d "deps/curl-${CURL_VERSION}" ] ; then \ - echo "curl sources missing, downloading..."; \ - mkdir -p ${ANDR_ROOT}/deps; \ - cd deps; \ - wget ${CURL_URL_HTTP} || exit 1; \ - tar -xjf curl-${CURL_VERSION}.tar.bz2 || exit 1; \ - rm curl-${CURL_VERSION}.tar.bz2; \ - ln -s curl-${CURL_VERSION} curl; \ - fi - -curl : $(CURL_LIB) - -$(CURL_LIB): $(CURL_TIMESTAMP) $(OPENSSL_LIB) - @REFRESH=0; \ - if [ ! -e ${CURL_TIMESTAMP_INT} ] ; then \ - REFRESH=1; \ - fi; \ - if [ ! -e ${CURL_LIB} ] ; then \ - REFRESH=1; \ - fi; \ - if [ ${CURL_TIMESTAMP} -nt ${CURL_TIMESTAMP_INT} ] ; then \ - REFRESH=1; \ - fi; \ - if [ $$REFRESH -ne 0 ] ; then \ - mkdir -p ${CURL_DIR}; \ - echo "changed timestamp for curl detected building..."; \ - cd deps/curl-${CURL_VERSION}; \ - export CROSS_PREFIX=${TARGET_TOOLCHAIN}; \ - export TOOLCHAIN=/tmp/ndk-${TARGET_HOST}-curl; \ - ${ANDROID_NDK}/build/tools/make-standalone-toolchain.sh \ - --toolchain=${TARGET_TOOLCHAIN}${COMPILER_VERSION} \ - --platform=${APP_PLATFORM} \ - --stl=libc++ \ - --install-dir=$${TOOLCHAIN}; \ - export PATH="$${TOOLCHAIN}/bin:$${PATH}"; \ - export CC=${CROSS_CC}; \ - export CXX=${CROSS_CXX}; \ - export TARGET_OS=OS_ANDROID_CROSSCOMPILE; \ - export CPPFLAGS="$${CPPFLAGS} -I${OPENSSL_DIR}/include ${TARGET_CFLAGS_ADDON}"; \ - export CFLAGS="$${CFLAGS} ${TARGET_CFLAGS_ADDON}"; \ - export LDFLAGS="$${LDFLAGS} -L${OPENSSL_DIR} ${TARGET_LDFLAGS_ADDON}"; \ - ./configure --host=${TARGET_HOST} --disable-shared --enable-static --with-ssl; \ - $(MAKE) -s || exit 1; \ - touch ${CURL_TIMESTAMP}; \ - touch ${CURL_TIMESTAMP_INT}; \ - $(RM) -rf $${TOOLCHAIN}; \ - else \ - echo "nothing to be done for curl"; \ - fi - -clean_curl : - ./gradlew cleanCURL - -sqlite3_download: deps/${SQLITE3_FOLDER}/sqlite3.c - -deps/${SQLITE3_FOLDER}/sqlite3.c : - cd deps; \ - wget ${SQLITE3_URL}; \ - unzip ${SQLITE3_FOLDER}.zip; \ - ln -s ${SQLITE3_FOLDER} sqlite; \ - cd ${SQLITE3_FOLDER}; - -clean_sqlite3: - ./gradlew cleanSQLite3 - -$(ASSETS_TIMESTAMP) : $(IRRLICHT_LIB) - @mkdir -p ${ANDR_ROOT}/deps; \ - for DIRNAME in {builtin,client,doc,fonts,games,mods,po,textures}; do \ - LAST_MODIF=$$(find ${PROJ_ROOT}/${DIRNAME} -type f -printf '%T@ %p\n' | sort -n | tail -1 | cut -f2- -d" "); \ - if [ $$(basename $$LAST_MODIF) != "timestamp" ]; then \ - touch ${PROJ_ROOT}/${DIRNAME}/timestamp; \ - touch ${ASSETS_TIMESTAMP}; \ - echo ${DIRNAME} changed $$LAST_MODIF; \ - fi; \ - done; \ - LAST_MODIF=$$(find ${IRRLICHT_DIR}/media -type f -printf '%T@ %p\n' | sort -n | tail -1 | cut -f2- -d" "); \ - if [ $$(basename $$LAST_MODIF) != "timestamp" ] ; then \ - touch ${IRRLICHT_DIR}/media/timestamp; \ - touch ${ASSETS_TIMESTAMP}; \ - fi; \ - if [ ${PROJ_ROOT}/minetest.conf.example -nt ${ASSETS_TIMESTAMP} ] ; then \ - echo "conf changed"; \ - touch ${ASSETS_TIMESTAMP}; \ - fi; \ - if [ ${PROJ_ROOT}/README.txt -nt ${ASSETS_TIMESTAMP} ] ; then \ - touch ${ASSETS_TIMESTAMP}; \ - fi; \ - if [ ! -e $(ASSETS_TIMESTAMP) ] ; then \ - touch $(ASSETS_TIMESTAMP); \ - fi - -assets : $(ASSETS_TIMESTAMP) - @REFRESH=0; \ - if [ ! -e ${ASSETS_TIMESTAMP}.old ] ; then \ - REFRESH=1; \ - fi; \ - if [ ${ASSETS_TIMESTAMP} -nt ${ASSETS_TIMESTAMP}.old ] ; then \ - REFRESH=1; \ - fi; \ - if [ ! -d ${APP_ROOT}/assets ] ; then \ - REFRESH=1; \ - fi; \ - if [ $$REFRESH -ne 0 ] ; then \ - echo "assets changed, refreshing..."; \ - $(MAKE) clean_assets; \ - ./gradlew copyAssets; \ - cp -r ${IRRLICHT_DIR}/media/Shaders ${APP_ROOT}/assets/Minetest/media; \ - cd ${APP_ROOT}/assets || exit 1; \ - find . -name "timestamp" -exec rm {} \; ; \ - find . -name "*.blend" -exec rm {} \; ; \ - find . -name "*~" -exec rm {} \; ; \ - find . -type d -path "*.git" -exec rm -rf {} \; ; \ - find . -type d -path "*.svn" -exec rm -rf {} \; ; \ - find . -type f -path "*.gitignore" -exec rm -rf {} \; ; \ - ls -R | grep ":$$" | sed -e 's/:$$//' -e 's/\.//' -e 's/^\///' > "index.txt"; \ - find -L Minetest > filelist.txt; \ - cp ${ANDR_ROOT}/${ASSETS_TIMESTAMP} ${ANDR_ROOT}/${ASSETS_TIMESTAMP}.old; \ - else \ - echo "nothing to be done for assets"; \ - fi - -clean_assets : - ./gradlew cleanAssets - -apk: local.properties assets $(ICONV_LIB) $(IRRLICHT_LIB) $(CURL_LIB) $(LEVELDB_TARGET) \ - $(OPENAL_LIB) $(OGG_LIB) prep_srcdir $(ANDR_ROOT)/jni/src/android_version.h \ - $(ANDR_ROOT)/jni/src/android_version_githash.h sqlite3_download - + @export TARGET_LIBDIR=${TARGET_LIBDIR}; \ - export HAVE_LEVELDB=${HAVE_LEVELDB}; \ - export APP_PLATFORM=${APP_PLATFORM}; \ - export TARGET_ABI=${TARGET_ABI}; \ - export TARGET_CFLAGS_ADDON="${TARGET_CFLAGS_ADDON}"; \ - export TARGET_CXXFLAGS_ADDON="${TARGET_CXXFLAGS_ADDON}"; \ - export COMPILER_VERSION=${COMPILER_VERSION}; \ - export GPROF=${GPROF}; \ - ${ANDROID_NDK}/ndk-build || exit 1; \ - if [ ! -e ${APP_ROOT}/jniLibs ]; then \ - ln -s ${ANDR_ROOT}/libs ${APP_ROOT}/jniLibs || exit 1; \ - fi; \ - export VERSION_STR="${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}" && \ - export BUILD_TYPE_C=$$(echo "$${BUILD_TYPE}" | sed 's/./\U&/') && \ - ./gradlew assemble$$BUILD_TYPE_C && \ - echo "APK stored at: build/outputs/apk/$$BUILD_TYPE/Minetest-$$BUILD_TYPE.apk" && \ - echo "You can install it with \`make install_$$BUILD_TYPE\`" - -# These Intentionally doesn't depend on their respective build steps, -# because it takes a while to verify that everything's up-to-date. -install_debug: - ${ANDROID_SDK}/platform-tools/adb install -r build/outputs/apk/debug/Minetest-debug.apk - -install_release: - ${ANDROID_SDK}/platform-tools/adb install -r build/outputs/apk/release/Minetest-release.apk - -prep_srcdir : - @if [ ! -e ${ANDR_ROOT}/jni/src ]; then \ - ln -s ${PROJ_ROOT}/src ${ANDR_ROOT}/jni/src; \ - fi; \ - if [ ! -e ${ANDR_ROOT}/jni/lib ]; then \ - ln -s ${PROJ_ROOT}/lib ${ANDR_ROOT}/jni/lib; \ - fi - -clean_apk : - ./gradlew clean - -clean_all : - ./gradlew cleanAll - -$(ANDR_ROOT)/jni/src/android_version_githash.h : prep_srcdir - @export VERSION_FILE=${ANDR_ROOT}/jni/src/android_version_githash.h; \ - export VERSION_FILE_NEW=$${VERSION_FILE}.new; \ - { \ - echo "#ifndef ANDROID_MT_VERSION_GITHASH_H"; \ - echo "#define ANDROID_MT_VERSION_GITHASH_H"; \ - export GITHASH=$$(git rev-parse --short=8 HEAD); \ - export VERSION_STR="${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}"; \ - echo "#define VERSION_GITHASH \"$$VERSION_STR-$$GITHASH-Android\""; \ - echo "#endif"; \ - } > "$${VERSION_FILE_NEW}"; \ - if ! cmp -s $${VERSION_FILE} $${VERSION_FILE_NEW}; then \ - echo "android_version_githash.h changed, updating..."; \ - mv "$${VERSION_FILE_NEW}" "$${VERSION_FILE}"; \ - else \ - rm "$${VERSION_FILE_NEW}"; \ - fi - - -$(ANDR_ROOT)/jni/src/android_version.h : prep_srcdir - @export VERSION_FILE=${ANDR_ROOT}/jni/src/android_version.h; \ - export VERSION_FILE_NEW=$${VERSION_FILE}.new; \ - { \ - echo "#ifndef ANDROID_MT_VERSION_H"; \ - echo "#define ANDROID_MT_VERSION_H"; \ - echo "#define VERSION_MAJOR ${VERSION_MAJOR}"; \ - echo "#define VERSION_MINOR ${VERSION_MINOR}"; \ - echo "#define VERSION_PATCH ${VERSION_PATCH}"; \ - echo "#define VERSION_STRING STR(VERSION_MAJOR) \".\" STR(VERSION_MINOR) \ - \".\" STR(VERSION_PATCH)"; \ - echo "#endif"; \ - } > $${VERSION_FILE_NEW}; \ - if ! cmp -s $${VERSION_FILE} $${VERSION_FILE_NEW}; then \ - echo "android_version.h changed, updating..."; \ - mv "$${VERSION_FILE_NEW}" "$${VERSION_FILE}"; \ - else \ - rm "$${VERSION_FILE_NEW}"; \ - fi - -clean : clean_apk clean_assets diff --git a/build/android/app/build.gradle b/build/android/app/build.gradle new file mode 100644 index 000000000..6a34a6d70 --- /dev/null +++ b/build/android/app/build.gradle @@ -0,0 +1,108 @@ +apply plugin: 'com.android.application' +android { + compileSdkVersion 29 + buildToolsVersion '29.0.3' + ndkVersion '21.0.6113669' + defaultConfig { + applicationId 'net.minetest.minetest' + minSdkVersion 16 + //noinspection OldTargetApi + targetSdkVersion 28 // Workaround for using `/sdcard` instead of the `data` patch for assets + versionName "${versionMajor}.${versionMinor}.${versionPatch}" + versionCode project.versionCode + } + + Properties props = new Properties() + props.load(new FileInputStream(file('../local.properties'))) + + if (props.getProperty('keystore') != null) { + signingConfigs { + release { + storeFile file(props['keystore']) + storePassword props['keystore.password'] + keyAlias props['key'] + keyPassword props['key.password'] + } + } + + buildTypes { + release { + minifyEnabled true + signingConfig signingConfigs.release + } + } + } + + // for multiple APKs + splits { + abi { + enable true + reset() + include 'armeabi-v7a', 'arm64-v8a' + } + } + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } +} + +task prepareAssets() { + def assetsFolder = "build/assets" + def projRoot = "../../.." + def gameToCopy = "minetest_game" + + copy { + from "${projRoot}/minetest.conf.example", "${projRoot}/README.md" into assetsFolder + } + copy { + from "${projRoot}/doc/lgpl-2.1.txt" into "${assetsFolder}" + rename("lgpl-2.1.txt", "LICENSE.txt") + } + copy { + from "${projRoot}/builtin" into "${assetsFolder}/builtin" + } + copy { + from "${projRoot}/client/shaders" into "${assetsFolder}/client/shaders" + } + copy { + from "${projRoot}/fonts" include "*.ttf" into "${assetsFolder}/fonts" + } + copy { + from "${projRoot}/games/${gameToCopy}" into "${assetsFolder}/games/${gameToCopy}" + } + /*copy { + // locales broken right now + // ToDo: fix it! + from "${projRoot}/po" into "${assetsFolder}/po" + }*/ + copy { + from "${projRoot}/textures" into "${assetsFolder}/textures" + } + + task zipAssets(type: Zip) { + archiveName "Minetest.zip" + from "${assetsFolder}" + destinationDir file("src/main/assets") + } +} + +preBuild.dependsOn zipAssets + +// Map for the version code that gives each ABI a value. +import com.android.build.OutputFile + +def abiCodes = ['armeabi-v7a': 0, 'arm64-v8a': 1] +android.applicationVariants.all { variant -> + variant.outputs.each { + output -> + def abiName = output.getFilter(OutputFile.ABI) + output.versionCodeOverride = abiCodes.get(abiName, 0) + variant.versionCode + } +} + +dependencies { + implementation project(':native') + implementation 'androidx.appcompat:appcompat:1.1.0' +} diff --git a/build/android/src/main/AndroidManifest.xml b/build/android/app/src/main/AndroidManifest.xml similarity index 68% rename from build/android/src/main/AndroidManifest.xml rename to build/android/app/src/main/AndroidManifest.xml index fb1de9cf0..3a5342751 100644 --- a/build/android/src/main/AndroidManifest.xml +++ b/build/android/app/src/main/AndroidManifest.xml @@ -4,28 +4,24 @@ package="net.minetest.minetest" android:installLocation="auto"> - - + tools:ignore="UnusedAttribute"> + android:value="3" /> @@ -33,11 +29,13 @@ + @@ -45,16 +43,18 @@ + android:value="Minetest" /> + - + android:name=".InputDialogActivity" + android:maxAspectRatio="3" + android:theme="@style/InputTheme" /> + + + diff --git a/build/android/app/src/main/java/net/minetest/minetest/CopyZipTask.java b/build/android/app/src/main/java/net/minetest/minetest/CopyZipTask.java new file mode 100644 index 000000000..770995502 --- /dev/null +++ b/build/android/app/src/main/java/net/minetest/minetest/CopyZipTask.java @@ -0,0 +1,82 @@ +/* +Minetest +Copyright (C) 2014-2020 MoNTE48, Maksim Gamarnik +Copyright (C) 2014-2020 ubulem, Bektur Mambetov + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +package net.minetest.minetest; + +import android.content.Context; +import android.content.Intent; +import android.os.AsyncTask; +import android.util.Log; + +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.lang.ref.WeakReference; + +public class CopyZipTask extends AsyncTask { + + private final WeakReference contextRef; + + CopyZipTask(Context context) { + contextRef = new WeakReference<>(context); + } + + protected String doInBackground(String... params) { + copyAssets(params); + return params[0]; + } + + @Override + protected void onPostExecute(String result) { + startUnzipService(result); + } + + private void copyAsset(String zipName) throws IOException { + String filename = zipName.substring(zipName.lastIndexOf("/") + 1); + try (InputStream in = contextRef.get().getAssets().open(filename); + OutputStream out = new FileOutputStream(zipName)) { + copyFile(in, out); + } + } + + private void copyAssets(String[] zips) { + try { + for (String zipName : zips) + copyAsset(zipName); + } catch (IOException e) { + Log.e("CopyZipTask", e.getLocalizedMessage()); + cancel(true); + } + } + + private void copyFile(InputStream in, OutputStream out) throws IOException { + byte[] buffer = new byte[1024]; + int read; + while ((read = in.read(buffer)) != -1) + out.write(buffer, 0, read); + } + + private void startUnzipService(String file) { + Intent intent = new Intent(contextRef.get(), UnzipService.class); + intent.putExtra(UnzipService.EXTRA_KEY_IN_FILE, file); + contextRef.get().startService(intent); + } +} diff --git a/build/android/src/main/java/net.minetest.minetest/MtNativeActivity.java b/build/android/app/src/main/java/net/minetest/minetest/GameActivity.java similarity index 51% rename from build/android/src/main/java/net.minetest.minetest/MtNativeActivity.java rename to build/android/app/src/main/java/net/minetest/minetest/GameActivity.java index f49d078fe..02b61b598 100644 --- a/build/android/src/main/java/net.minetest.minetest/MtNativeActivity.java +++ b/build/android/app/src/main/java/net/minetest/minetest/GameActivity.java @@ -1,3 +1,23 @@ +/* +Minetest +Copyright (C) 2014-2020 MoNTE48, Maksim Gamarnik +Copyright (C) 2014-2020 ubulem, Bektur Mambetov + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + package net.minetest.minetest; import android.app.NativeActivity; @@ -7,19 +27,14 @@ import android.os.Bundle; import android.view.View; import android.view.WindowManager; -public class MtNativeActivity extends NativeActivity { - +public class GameActivity extends NativeActivity { static { System.loadLibrary("c++_shared"); - System.loadLibrary("openal"); - System.loadLibrary("ogg"); - System.loadLibrary("vorbis"); - System.loadLibrary("iconv"); - System.loadLibrary("minetest"); + System.loadLibrary("Minetest"); } - private int m_MessagReturnCode; - private String m_MessageReturnValue; + private int messageReturnCode; + private String messageReturnValue; public static native void putMessageBoxResult(String text); @@ -27,20 +42,16 @@ public class MtNativeActivity extends NativeActivity { public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); - m_MessagReturnCode = -1; - m_MessageReturnValue = ""; - } - - @Override - protected void onResume() { - super.onResume(); - makeFullScreen(); + messageReturnCode = -1; + messageReturnValue = ""; } private void makeFullScreen() { if (Build.VERSION.SDK_INT >= 19) this.getWindow().getDecorView().setSystemUiVisibility( - View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY); + View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | + View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | + View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY); } @Override @@ -50,15 +61,31 @@ public class MtNativeActivity extends NativeActivity { makeFullScreen(); } - public void copyAssets() { - Intent intent = new Intent(this, MinetestAssetCopy.class); - startActivity(intent); + @Override + protected void onResume() { + super.onResume(); + makeFullScreen(); } - public void showDialog(String acceptButton, String hint, String current, - int editType) { + @Override + public void onBackPressed() { + // Ignore the back press so Minetest can handle it + } - Intent intent = new Intent(this, MinetestTextEntry.class); + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + if (requestCode == 101) { + if (resultCode == RESULT_OK) { + String text = data.getStringExtra("text"); + messageReturnCode = 0; + messageReturnValue = text; + } else + messageReturnCode = 1; + } + } + + public void showDialog(String acceptButton, String hint, String current, int editType) { + Intent intent = new Intent(this, InputDialogActivity.class); Bundle params = new Bundle(); params.putString("acceptButton", acceptButton); params.putString("hint", hint); @@ -66,43 +93,28 @@ public class MtNativeActivity extends NativeActivity { params.putInt("editType", editType); intent.putExtras(params); startActivityForResult(intent, 101); - m_MessageReturnValue = ""; - m_MessagReturnCode = -1; + messageReturnValue = ""; + messageReturnCode = -1; } - /* ugly code to workaround putMessageBoxResult not beeing found */ public int getDialogState() { - return m_MessagReturnCode; + return messageReturnCode; } public String getDialogValue() { - m_MessagReturnCode = -1; - return m_MessageReturnValue; + messageReturnCode = -1; + return messageReturnValue; } public float getDensity() { return getResources().getDisplayMetrics().density; } - public int getDisplayWidth() { - return getResources().getDisplayMetrics().widthPixels; - } - public int getDisplayHeight() { return getResources().getDisplayMetrics().heightPixels; } - @Override - protected void onActivityResult(int requestCode, int resultCode, - Intent data) { - if (requestCode == 101) { - if (resultCode == RESULT_OK) { - String text = data.getStringExtra("text"); - m_MessagReturnCode = 0; - m_MessageReturnValue = text; - } else { - m_MessagReturnCode = 1; - } - } + public int getDisplayWidth() { + return getResources().getDisplayMetrics().widthPixels; } } diff --git a/build/android/app/src/main/java/net/minetest/minetest/InputDialogActivity.java b/build/android/app/src/main/java/net/minetest/minetest/InputDialogActivity.java new file mode 100644 index 000000000..7c6aa111d --- /dev/null +++ b/build/android/app/src/main/java/net/minetest/minetest/InputDialogActivity.java @@ -0,0 +1,98 @@ +/* +Minetest +Copyright (C) 2014-2020 MoNTE48, Maksim Gamarnik +Copyright (C) 2014-2020 ubulem, Bektur Mambetov + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +package net.minetest.minetest; + +import android.app.Activity; +import android.content.Intent; +import android.os.Build; +import android.os.Bundle; +import android.text.InputType; +import android.view.KeyEvent; +import android.view.View; +import android.view.inputmethod.InputMethodManager; +import android.widget.EditText; + +import androidx.appcompat.app.AlertDialog; +import androidx.appcompat.app.AppCompatActivity; + +import java.util.Objects; + +public class InputDialogActivity extends AppCompatActivity { + private AlertDialog alertDialog; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + Bundle b = getIntent().getExtras(); + int editType = Objects.requireNonNull(b).getInt("editType"); + String hint = b.getString("hint"); + String current = b.getString("current"); + final AlertDialog.Builder builder = new AlertDialog.Builder(this); + EditText editText = new EditText(this); + builder.setView(editText); + editText.requestFocus(); + editText.setHint(hint); + editText.setText(current); + final InputMethodManager imm = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE); + Objects.requireNonNull(imm).toggleSoftInput(InputMethodManager.SHOW_FORCED, + InputMethodManager.HIDE_IMPLICIT_ONLY); + if (editType == 3) + editText.setInputType(InputType.TYPE_CLASS_TEXT | + InputType.TYPE_TEXT_VARIATION_PASSWORD); + else + editText.setInputType(InputType.TYPE_CLASS_TEXT); + editText.setOnKeyListener((view, KeyCode, event) -> { + if (KeyCode == KeyEvent.KEYCODE_ENTER) { + imm.hideSoftInputFromWindow(editText.getWindowToken(), 0); + pushResult(editText.getText().toString()); + return true; + } + return false; + }); + alertDialog = builder.create(); + if (!this.isFinishing()) + alertDialog.show(); + alertDialog.setOnCancelListener(dialog -> { + pushResult(editText.getText().toString()); + setResult(Activity.RESULT_CANCELED); + alertDialog.dismiss(); + makeFullScreen(); + finish(); + }); + } + + private void pushResult(String text) { + Intent resultData = new Intent(); + resultData.putExtra("text", text); + setResult(AppCompatActivity.RESULT_OK, resultData); + alertDialog.dismiss(); + makeFullScreen(); + finish(); + } + + private void makeFullScreen() { + if (Build.VERSION.SDK_INT >= 19) + this.getWindow().getDecorView().setSystemUiVisibility( + View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | + View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | + View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY); + } +} diff --git a/build/android/app/src/main/java/net/minetest/minetest/MainActivity.java b/build/android/app/src/main/java/net/minetest/minetest/MainActivity.java new file mode 100644 index 000000000..f37ae6d4b --- /dev/null +++ b/build/android/app/src/main/java/net/minetest/minetest/MainActivity.java @@ -0,0 +1,139 @@ +/* +Minetest +Copyright (C) 2014-2020 MoNTE48, Maksim Gamarnik +Copyright (C) 2014-2020 ubulem, Bektur Mambetov + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +package net.minetest.minetest; + +import android.Manifest; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.SharedPreferences; +import android.content.pm.PackageManager; +import android.os.Build; +import android.os.Bundle; +import android.view.View; +import android.widget.ProgressBar; +import android.widget.TextView; +import android.widget.Toast; + +import androidx.annotation.NonNull; +import androidx.appcompat.app.AppCompatActivity; +import androidx.core.app.ActivityCompat; +import androidx.core.content.ContextCompat; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class MainActivity extends AppCompatActivity { + private final static int versionCode = BuildConfig.VERSION_CODE; + private final static int PERMISSIONS = 1; + private static final String[] REQUIRED_SDK_PERMISSIONS = + new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}; + private static final String SETTINGS = "MinetestSettings"; + private static final String TAG_VERSION_CODE = "versionCode"; + private ProgressBar mProgressBar; + private TextView mTextView; + private SharedPreferences sharedPreferences; + private final BroadcastReceiver myReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + int progress = 0; + if (intent != null) + progress = intent.getIntExtra(UnzipService.ACTION_PROGRESS, 0); + if (progress >= 0) { + if (mProgressBar != null) { + mProgressBar.setVisibility(View.VISIBLE); + mProgressBar.setProgress(progress); + } + mTextView.setVisibility(View.VISIBLE); + } else + startNative(); + } + }; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + IntentFilter filter = new IntentFilter(UnzipService.ACTION_UPDATE); + registerReceiver(myReceiver, filter); + mProgressBar = findViewById(R.id.progressBar); + mTextView = findViewById(R.id.textView); + sharedPreferences = getSharedPreferences(SETTINGS, Context.MODE_PRIVATE); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) + checkPermission(); + else + checkAppVersion(); + } + + private void checkPermission() { + final List missingPermissions = new ArrayList<>(); + for (final String permission : REQUIRED_SDK_PERMISSIONS) { + final int result = ContextCompat.checkSelfPermission(this, permission); + if (result != PackageManager.PERMISSION_GRANTED) + missingPermissions.add(permission); + } + if (!missingPermissions.isEmpty()) { + final String[] permissions = missingPermissions + .toArray(new String[0]); + ActivityCompat.requestPermissions(this, permissions, PERMISSIONS); + } else { + final int[] grantResults = new int[REQUIRED_SDK_PERMISSIONS.length]; + Arrays.fill(grantResults, PackageManager.PERMISSION_GRANTED); + onRequestPermissionsResult(PERMISSIONS, REQUIRED_SDK_PERMISSIONS, grantResults); + } + } + + @Override + public void onRequestPermissionsResult(int requestCode, + @NonNull String[] permissions, @NonNull int[] grantResults) { + if (requestCode == PERMISSIONS) { + for (int grantResult : grantResults) { + if (grantResult != PackageManager.PERMISSION_GRANTED) { + Toast.makeText(this, R.string.not_granted, Toast.LENGTH_LONG).show(); + finish(); + } + } + checkAppVersion(); + } + } + + private void checkAppVersion() { + if (sharedPreferences.getInt(TAG_VERSION_CODE, 0) == versionCode) + startNative(); + else + new CopyZipTask(this).execute(getCacheDir() + "/Minetest.zip"); + } + + private void startNative() { + sharedPreferences.edit().putInt(TAG_VERSION_CODE, versionCode).apply(); + Intent intent = new Intent(this, GameActivity.class); + intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_CLEAR_TASK); + startActivity(intent); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + unregisterReceiver(myReceiver); + } +} diff --git a/build/android/app/src/main/java/net/minetest/minetest/UnzipService.java b/build/android/app/src/main/java/net/minetest/minetest/UnzipService.java new file mode 100644 index 000000000..ac9116994 --- /dev/null +++ b/build/android/app/src/main/java/net/minetest/minetest/UnzipService.java @@ -0,0 +1,153 @@ +/* +Minetest +Copyright (C) 2014-2020 MoNTE48, Maksim Gamarnik +Copyright (C) 2014-2020 ubulem, Bektur Mambetov + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +package net.minetest.minetest; + +import android.app.IntentService; +import android.app.Notification; +import android.app.NotificationChannel; +import android.app.NotificationManager; +import android.content.Context; +import android.content.Intent; +import android.os.Build; +import android.os.Environment; +import android.util.Log; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; +import java.util.zip.ZipInputStream; + +public class UnzipService extends IntentService { + public static final String ACTION_UPDATE = "net.minetest.minetest.UPDATE"; + public static final String ACTION_PROGRESS = "net.minetest.minetest.PROGRESS"; + public static final String EXTRA_KEY_IN_FILE = "file"; + private static final String TAG = "UnzipService"; + private final int id = 1; + private NotificationManager mNotifyManager; + + public UnzipService() { + super("net.minetest.minetest.UnzipService"); + } + + private void isDir(String dir, String location) { + File f = new File(location + dir); + if (!f.isDirectory()) + f.mkdirs(); + } + + @Override + protected void onHandleIntent(Intent intent) { + createNotification(); + unzip(intent); + } + + private void createNotification() { + String name = "net.minetest.minetest"; + String channelId = "Minetest channel"; + String description = "notifications from Minetest"; + Notification.Builder builder; + if (mNotifyManager == null) + mNotifyManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + int importance = NotificationManager.IMPORTANCE_LOW; + NotificationChannel mChannel = null; + if (mNotifyManager != null) + mChannel = mNotifyManager.getNotificationChannel(channelId); + if (mChannel == null) { + mChannel = new NotificationChannel(channelId, name, importance); + mChannel.setDescription(description); + // Configure the notification channel, NO SOUND + mChannel.setSound(null, null); + mChannel.enableLights(false); + mChannel.enableVibration(false); + mNotifyManager.createNotificationChannel(mChannel); + } + builder = new Notification.Builder(this, channelId); + } else { + builder = new Notification.Builder(this); + } + builder.setContentTitle(getString(R.string.notification_title)) + .setSmallIcon(R.mipmap.ic_launcher) + .setContentText(getString(R.string.notification_description)); + mNotifyManager.notify(id, builder.build()); + } + + private void unzip(Intent intent) { + String zip = intent.getStringExtra(EXTRA_KEY_IN_FILE); + String location = Environment.getExternalStorageDirectory() + "/Minetest/"; + int per = 0; + int size = getSummarySize(zip); + File zipFile = new File(zip); + int readLen; + byte[] readBuffer = new byte[8192]; + try (FileInputStream fileInputStream = new FileInputStream(zipFile); + ZipInputStream zipInputStream = new ZipInputStream(fileInputStream)) { + ZipEntry ze; + while ((ze = zipInputStream.getNextEntry()) != null) { + if (ze.isDirectory()) { + ++per; + isDir(ze.getName(), location); + } else { + publishProgress(100 * ++per / size); + try (OutputStream outputStream = new FileOutputStream(location + ze.getName())) { + while ((readLen = zipInputStream.read(readBuffer)) != -1) { + outputStream.write(readBuffer, 0, readLen); + } + } + } + zipFile.delete(); + } + } catch (FileNotFoundException e) { + Log.e(TAG, e.getLocalizedMessage()); + } catch (IOException e) { + Log.e(TAG, e.getLocalizedMessage()); + } + } + + private void publishProgress(int progress) { + Intent intentUpdate = new Intent(ACTION_UPDATE); + intentUpdate.putExtra(ACTION_PROGRESS, progress); + sendBroadcast(intentUpdate); + } + + private int getSummarySize(String zip) { + int size = 0; + try { + ZipFile zipSize = new ZipFile(zip); + size += zipSize.size(); + } catch (IOException e) { + Log.e(TAG, e.getLocalizedMessage()); + } + return size; + } + + @Override + public void onDestroy() { + super.onDestroy(); + mNotifyManager.cancel(id); + publishProgress(-1); + } +} diff --git a/build/android/src/main/res/drawable/background.png b/build/android/app/src/main/res/drawable/background.png similarity index 100% rename from build/android/src/main/res/drawable/background.png rename to build/android/app/src/main/res/drawable/background.png diff --git a/build/android/src/main/res/drawable/bg.xml b/build/android/app/src/main/res/drawable/bg.xml similarity index 82% rename from build/android/src/main/res/drawable/bg.xml rename to build/android/app/src/main/res/drawable/bg.xml index c76ec372d..903335ed9 100644 --- a/build/android/src/main/res/drawable/bg.xml +++ b/build/android/app/src/main/res/drawable/bg.xml @@ -1,4 +1,4 @@ \ No newline at end of file + android:tileMode="repeat" /> diff --git a/build/android/app/src/main/res/layout/activity_main.xml b/build/android/app/src/main/res/layout/activity_main.xml new file mode 100644 index 000000000..e6f461f14 --- /dev/null +++ b/build/android/app/src/main/res/layout/activity_main.xml @@ -0,0 +1,30 @@ + + + + + + + diff --git a/build/android/src/main/res/mipmap/ic_launcher.png b/build/android/app/src/main/res/mipmap/ic_launcher.png similarity index 100% rename from build/android/src/main/res/mipmap/ic_launcher.png rename to build/android/app/src/main/res/mipmap/ic_launcher.png diff --git a/build/android/app/src/main/res/values/strings.xml b/build/android/app/src/main/res/values/strings.xml new file mode 100644 index 000000000..a6fba70d5 --- /dev/null +++ b/build/android/app/src/main/res/values/strings.xml @@ -0,0 +1,10 @@ + + + + Minetest + Loading… + Required permission wasn\'t granted, Minetest can\'t run without it + Loading Minetest + Less than 1 minute… + + diff --git a/build/android/app/src/main/res/values/styles.xml b/build/android/app/src/main/res/values/styles.xml new file mode 100644 index 000000000..618507e63 --- /dev/null +++ b/build/android/app/src/main/res/values/styles.xml @@ -0,0 +1,22 @@ + + + + + + + + + + diff --git a/build/android/build.gradle b/build/android/build.gradle index fa74cd497..b02e8c6df 100644 --- a/build/android/build.gradle +++ b/build/android/build.gradle @@ -1,10 +1,24 @@ +// Top-level build file where you can add configuration options common to all sub-projects/modules. + +project.ext.set("versionMajor", 5) // Version Major +project.ext.set("versionMinor", 3) // Version Minor +project.ext.set("versionPatch", 0) // Version Patch +project.ext.set("versionExtra", "-dev") // Version Extra +project.ext.set("versionCode", 26) // Android Version Code +// NOTE: +2 after each release! +// +1 for ARM and +1 for ARM64 APK's, because +// each APK must have a larger `versionCode` than the previous + buildscript { repositories { google() jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:3.5.1' + classpath 'com.android.tools.build:gradle:3.6.2' + classpath 'org.ajoberstar.grgit:grgit-gradle:4.0.2' + // NOTE: Do not place your application dependencies here; they belong + // in the individual module build.gradle files } } @@ -15,161 +29,6 @@ allprojects { } } -def curl_version = "7.60.0" -def irrlicht_revision = "5150" -def openal_version = "1.18.2" -def openssl_version = "1.0.2n" -def sqlite3_version = "3240000" - -apply plugin: "com.android.application" - -android { - compileSdkVersion 29 - buildToolsVersion '29.0.2' - - defaultConfig { - versionCode 26 - versionName "${System.env.VERSION_STR}.${versionCode}" - minSdkVersion 14 - targetSdkVersion 29 - applicationId "net.minetest.minetest" - manifestPlaceholders = [package: "net.minetest.minetest", project: project.name] - ndk { - // Specifies the ABI configurations of your native - // libraries Gradle should build and package with your APK. - // abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64' - abiFilters 'armeabi-v7a', 'x86', 'arm64-v8a' - } - } - - compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 - } - - lintOptions { - disable "OldTargetApi", "GoogleAppIndexingWarning" - } - - Properties props = new Properties() - props.load(new FileInputStream(file("local.properties"))) - - if (props.getProperty("keystore") != null) { - signingConfigs { - release { - storeFile file(props["keystore"]) - storePassword props["keystore.password"] - keyAlias props["key"] - keyPassword props["key.password"] - } - } - - buildTypes { - release { - signingConfig signingConfigs.release - } - } - } -} - -task cleanAssets(type: Delete) { - delete 'src/main/assets' -} - -task copyAssets { - dependsOn 'cleanAssets' - mkdir "src/main/assets" - - def mtAssetsFolder = "src/main/assets/Minetest" - def projRoot = "../.." - def gameToCopy = "minetest_game" - - doLast { - mkdir "${mtAssetsFolder}" - mkdir "${mtAssetsFolder}/client" - mkdir "${mtAssetsFolder}/fonts" - mkdir "${mtAssetsFolder}/games" - mkdir "${mtAssetsFolder}/media" - - copy { - from "${projRoot}/minetest.conf.example", "${projRoot}/README.md" into mtAssetsFolder - } - copy { - from "${projRoot}/doc/lgpl-2.1.txt" into "${mtAssetsFolder}/LICENSE.txt" - } - copy { - from "${projRoot}/builtin" into "${mtAssetsFolder}/builtin" - } - copy { - from "${projRoot}/client/shaders" into "${mtAssetsFolder}/client/shaders" - } - copy { - from "${projRoot}/fonts" include "*.ttf" into "${mtAssetsFolder}/fonts" - } - copy { - from "${projRoot}/games/${gameToCopy}" into "${mtAssetsFolder}/games/${gameToCopy}" - } - copy { - from "${projRoot}/po" into "${mtAssetsFolder}/po" - } - copy { - from "${projRoot}/textures" into "${mtAssetsFolder}/textures" - } - } -} - -task cleanIconv(type: Delete) { - delete 'deps/libiconv' -} - -task cleanIrrlicht(type: Delete) { - delete 'deps/irrlicht' -} - -task cleanLevelDB(type: Delete) { - delete 'deps/leveldb' -} - -task cleanCURL(type: Delete) { - delete 'deps/curl' - delete 'deps/curl-' + curl_version -} - -task cleanOpenSSL(type: Delete) { - delete 'deps/openssl' - delete 'deps/openssl-' + openssl_version - delete 'deps/openssl-' + openssl_version + '.tar.gz' -} - -task cleanOpenAL(type: Delete) { - delete 'deps/openal-soft' -} - -task cleanFreetype(type: Delete) { - delete 'deps/freetype2-android' -} - -task cleanOgg(type: Delete) { - delete 'deps/libvorbis-libogg-android' -} - -task cleanSQLite3(type: Delete) { - delete 'deps/sqlite-amalgamation-' + sqlite3_version - delete 'deps/sqlite-amalgamation-' + sqlite3_version + '.zip' -} - -task cleanAll(type: Delete, dependsOn: [clean, cleanAssets, cleanIconv, - cleanFreetype, cleanIrrlicht, cleanLevelDB, cleanSQLite3, cleanCURL, - cleanOpenSSL, cleanOpenAL, cleanOgg]) { - delete 'deps' - delete 'gen' - delete 'libs' - delete 'obj' - delete 'bin' - delete 'Debug' - delete 'and_env' -} - -dependencies { - implementation 'androidx.core:core:1.1.0' +task clean(type: Delete) { + delete rootProject.buildDir } diff --git a/build/android/gradle.properties b/build/android/gradle.properties index 5465fec0e..53b475cf9 100644 --- a/build/android/gradle.properties +++ b/build/android/gradle.properties @@ -1,2 +1,11 @@ +<#if isLowMemory> +org.gradle.jvmargs=-Xmx4G -XX:MaxPermSize=2G -XX:+HeapDumpOnOutOfMemoryError +<#else> +org.gradle.jvmargs=-Xmx16G -XX:MaxPermSize=8G -XX:+HeapDumpOnOutOfMemoryError + +org.gradle.daemon=true +org.gradle.parallel=true +org.gradle.parallel.threads=8 +org.gradle.configureondemand=true android.enableJetifier=true -android.useAndroidX=true \ No newline at end of file +android.useAndroidX=true diff --git a/build/android/gradle/wrapper/gradle-wrapper.jar b/build/android/gradle/wrapper/gradle-wrapper.jar index 6b6ea3ab4ff4f69d55c5fd9c0a6ac70f47d41008..5c2d1cf016b3885f6930543d57b744ea8c220a1a 100644 GIT binary patch literal 55616 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�A``0fKpc0WAU$bg;D1GyIQa;7hdW#MKpuc+fPkN63DB8-QCon}w55{+ zK*~kT%+g5EzyQD?t7onEf4S{n5`Y%HU(o_IxB{TT|7ske_`f6&x7P#Y(^Lk?K1$eF z8Ohk#SUB1Kx@itd&0`AyGX5-pngUK={i!e#{R8yBlYsrOyWkeC%j+RF5iuzNd@qNfB>rpz#I8f@s{`{nv9Lfe|_68v(EoSLz4a_*l*>a ze`!-MGn)To!IAynEWd7smziIGVoE6e67%Kc<1cw&U)K0#-o>Bj6zac3|F@C;A`9b7 z=$C2lenNw4{S)+GliV}2G}0N|fG z)sG;wmk+am&9E z{i*){Mdkk`{7ZrQpYTT3{{;VHviuVEr7rtV*j}4|g8j3f;U(rv3E-cYWBU$VtD_%M1M5Tztu!v61)^^{7FFK`5OfPAl>+q z>7}IGPbO2}-(dPJfwz~OFNL~(a)Jl^2IoJ?cDeQ`pGlj`8S@I)6PGc(f|02zx2HPq$%$HzbV~+ v^TYh7&j0bc{Ml*p!|U?1+ylV=n-AuVG#FqV^dowa3FsZ*6oN|6kH7vOrq diff --git a/build/android/patches/irrlicht-back_button.patch b/build/android/patches/irrlicht-back_button.patch deleted file mode 100644 index e17b81347..000000000 --- a/build/android/patches/irrlicht-back_button.patch +++ /dev/null @@ -1,20 +0,0 @@ ---- irrlicht/source/Irrlicht/Android/CIrrDeviceAndroid.cpp.orig 2015-08-29 15:43:09.000000000 +0300 -+++ irrlicht/source/Irrlicht/Android/CIrrDeviceAndroid.cpp 2016-05-13 21:36:22.880388505 +0300 -@@ -486,7 +486,7 @@ - event.KeyInput.Char = 0; - } - -- device->postEventFromUser(event); -+ status = device->postEventFromUser(event); - } - break; - default: -@@ -543,7 +543,7 @@ - KeyMap[1] = KEY_LBUTTON; // AKEYCODE_SOFT_LEFT - KeyMap[2] = KEY_RBUTTON; // AKEYCODE_SOFT_RIGHT - KeyMap[3] = KEY_HOME; // AKEYCODE_HOME -- KeyMap[4] = KEY_BACK; // AKEYCODE_BACK -+ KeyMap[4] = KEY_CANCEL; // AKEYCODE_BACK - KeyMap[5] = KEY_UNKNOWN; // AKEYCODE_CALL - KeyMap[6] = KEY_UNKNOWN; // AKEYCODE_ENDCALL - KeyMap[7] = KEY_KEY_0; // AKEYCODE_0 diff --git a/build/android/patches/irrlicht-native_activity.patch b/build/android/patches/irrlicht-native_activity.patch deleted file mode 100644 index a9d2610c3..000000000 --- a/build/android/patches/irrlicht-native_activity.patch +++ /dev/null @@ -1,13 +0,0 @@ ---- irrlicht/source/Irrlicht/CEGLManager.cpp.orig 2018-09-11 18:19:51.453403631 +0300 -+++ irrlicht/source/Irrlicht/CEGLManager.cpp 2018-09-11 18:36:24.603471869 +0300 -@@ -9,6 +9,10 @@ - #include "irrString.h" - #include "os.h" - -+#if defined(_IRR_COMPILE_WITH_ANDROID_DEVICE_) -+#include -+#endif -+ - namespace irr - { - namespace video diff --git a/build/android/patches/irrlicht-texturehack.patch b/build/android/patches/irrlicht-texturehack.patch deleted file mode 100644 index a458ede72..000000000 --- a/build/android/patches/irrlicht-texturehack.patch +++ /dev/null @@ -1,240 +0,0 @@ ---- irrlicht/source/Irrlicht/COGLESTexture.cpp.orig 2014-06-22 17:01:13.266568869 +0200 -+++ irrlicht/source/Irrlicht/COGLESTexture.cpp 2014-06-22 17:03:59.298572810 +0200 -@@ -366,112 +366,140 @@ - void(*convert)(const void*, s32, void*) = 0; - getFormatParameters(ColorFormat, InternalFormat, filtering, PixelFormat, PixelType, convert); - -- // make sure we don't change the internal format of existing images -- if (!newTexture) -- InternalFormat = oldInternalFormat; -- -- Driver->setActiveTexture(0, this); -- -- if (Driver->testGLError()) -- os::Printer::log("Could not bind Texture", ELL_ERROR); -- -- // mipmap handling for main texture -- if (!level && newTexture) -- { -- // auto generate if possible and no mipmap data is given -- if (!IsCompressed && HasMipMaps && !mipmapData && Driver->queryFeature(EVDF_MIP_MAP_AUTO_UPDATE)) -- { -- if (Driver->getTextureCreationFlag(ETCF_OPTIMIZED_FOR_SPEED)) -- glHint(GL_GENERATE_MIPMAP_HINT, GL_FASTEST); -- else if (Driver->getTextureCreationFlag(ETCF_OPTIMIZED_FOR_QUALITY)) -- glHint(GL_GENERATE_MIPMAP_HINT, GL_NICEST); -- else -- glHint(GL_GENERATE_MIPMAP_HINT, GL_DONT_CARE); -+ bool retry = false; -+ -+ do { -+ if (retry) { -+ InternalFormat = GL_RGBA; -+ PixelFormat = GL_RGBA; -+ convert = CColorConverter::convert_A8R8G8B8toA8B8G8R8; -+ } -+ // make sure we don't change the internal format of existing images -+ if (!newTexture) -+ InternalFormat = oldInternalFormat; - -- glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE); -- AutomaticMipmapUpdate=true; -- } -+ Driver->setActiveTexture(0, this); - -- // enable bilinear filter without mipmaps -- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filtering); -- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filtering); -- } -+ if (Driver->testGLError()) -+ os::Printer::log("Could not bind Texture", ELL_ERROR); - -- // now get image data and upload to GPU -+ // mipmap handling for main texture -+ if (!level && newTexture) -+ { -+ // auto generate if possible and no mipmap data is given -+ if (!IsCompressed && HasMipMaps && !mipmapData && Driver->queryFeature(EVDF_MIP_MAP_AUTO_UPDATE)) -+ { -+ if (Driver->getTextureCreationFlag(ETCF_OPTIMIZED_FOR_SPEED)) -+ glHint(GL_GENERATE_MIPMAP_HINT, GL_FASTEST); -+ else if (Driver->getTextureCreationFlag(ETCF_OPTIMIZED_FOR_QUALITY)) -+ glHint(GL_GENERATE_MIPMAP_HINT, GL_NICEST); -+ else -+ glHint(GL_GENERATE_MIPMAP_HINT, GL_DONT_CARE); -+ -+ glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE); -+ AutomaticMipmapUpdate=true; -+ } -+ -+ // enable bilinear filter without mipmaps -+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filtering); -+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filtering); -+ } - -- u32 compressedImageSize = IImage::getCompressedImageSize(ColorFormat, image->getDimension().Width, image->getDimension().Height); -+ // now get image data and upload to GPU - -- void* source = image->lock(); -+ u32 compressedImageSize = IImage::getCompressedImageSize(ColorFormat, image->getDimension().Width, image->getDimension().Height); - -- IImage* tmpImage = 0; -+ void* source = image->lock(); - -- if (convert) -- { -- tmpImage = new CImage(image->getColorFormat(), image->getDimension()); -- void* dest = tmpImage->lock(); -- convert(source, image->getDimension().getArea(), dest); -- image->unlock(); -- source = dest; -- } -+ IImage* tmpImage = 0; - -- if (newTexture) -- { -- if (IsCompressed) -+ if (convert) - { -- glCompressedTexImage2D(GL_TEXTURE_2D, 0, InternalFormat, image->getDimension().Width, -- image->getDimension().Height, 0, compressedImageSize, source); -+ tmpImage = new CImage(image->getColorFormat(), image->getDimension()); -+ void* dest = tmpImage->lock(); -+ convert(source, image->getDimension().getArea(), dest); -+ image->unlock(); -+ source = dest; - } -- else -- glTexImage2D(GL_TEXTURE_2D, level, InternalFormat, image->getDimension().Width, -- image->getDimension().Height, 0, PixelFormat, PixelType, source); -- } -- else -- { -- if (IsCompressed) -+ -+ if (newTexture) - { -- glCompressedTexSubImage2D(GL_TEXTURE_2D, level, 0, 0, image->getDimension().Width, -- image->getDimension().Height, PixelFormat, compressedImageSize, source); -+ if (IsCompressed) -+ { -+ glCompressedTexImage2D(GL_TEXTURE_2D, 0, InternalFormat, image->getDimension().Width, -+ image->getDimension().Height, 0, compressedImageSize, source); -+ } -+ else -+ glTexImage2D(GL_TEXTURE_2D, level, InternalFormat, image->getDimension().Width, -+ image->getDimension().Height, 0, PixelFormat, PixelType, source); - } - else -- glTexSubImage2D(GL_TEXTURE_2D, level, 0, 0, image->getDimension().Width, -- image->getDimension().Height, PixelFormat, PixelType, source); -- } -- -- if (convert) -- { -- tmpImage->unlock(); -- tmpImage->drop(); -- } -- else -- image->unlock(); -- -- if (!level && newTexture) -- { -- if (IsCompressed && !mipmapData) - { -- if (image->hasMipMaps()) -- mipmapData = static_cast(image->lock())+compressedImageSize; -+ if (IsCompressed) -+ { -+ glCompressedTexSubImage2D(GL_TEXTURE_2D, level, 0, 0, image->getDimension().Width, -+ image->getDimension().Height, PixelFormat, compressedImageSize, source); -+ } - else -- HasMipMaps = false; -+ glTexSubImage2D(GL_TEXTURE_2D, level, 0, 0, image->getDimension().Width, -+ image->getDimension().Height, PixelFormat, PixelType, source); - } - -- regenerateMipMapLevels(mipmapData); -- -- if (HasMipMaps) // might have changed in regenerateMipMapLevels -+ if (convert) - { -- // enable bilinear mipmap filter -- GLint filteringMipMaps = GL_LINEAR_MIPMAP_NEAREST; -- -- if (filtering != GL_LINEAR) -- filteringMipMaps = GL_NEAREST_MIPMAP_NEAREST; -+ tmpImage->unlock(); -+ tmpImage->drop(); -+ } -+ else -+ image->unlock(); -+ -+ if (glGetError() != GL_NO_ERROR) { -+ static bool warned = false; -+ if ((!retry) && (ColorFormat == ECF_A8R8G8B8)) { -+ -+ if (!warned) { -+ os::Printer::log("Your driver claims to support GL_BGRA but fails on trying to upload a texture, converting to GL_RGBA and trying again", ELL_ERROR); -+ warned = true; -+ } -+ } -+ else if (retry) { -+ os::Printer::log("Neither uploading texture as GL_BGRA nor, converted one using GL_RGBA succeeded", ELL_ERROR); -+ } -+ retry = !retry; -+ continue; -+ } else { -+ retry = false; -+ } - -- glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filteringMipMaps); -- glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filtering); -+ if (!level && newTexture) -+ { -+ if (IsCompressed && !mipmapData) -+ { -+ if (image->hasMipMaps()) -+ mipmapData = static_cast(image->lock())+compressedImageSize; -+ else -+ HasMipMaps = false; -+ } -+ -+ regenerateMipMapLevels(mipmapData); -+ -+ if (HasMipMaps) // might have changed in regenerateMipMapLevels -+ { -+ // enable bilinear mipmap filter -+ GLint filteringMipMaps = GL_LINEAR_MIPMAP_NEAREST; -+ -+ if (filtering != GL_LINEAR) -+ filteringMipMaps = GL_NEAREST_MIPMAP_NEAREST; -+ -+ glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filteringMipMaps); -+ glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filtering); -+ } - } -- } - -- if (Driver->testGLError()) -- os::Printer::log("Could not glTexImage2D", ELL_ERROR); -+ if (Driver->testGLError()) -+ os::Printer::log("Could not glTexImage2D", ELL_ERROR); -+ } -+ while(retry); - } - - ---- irrlicht/source/Irrlicht/COGLESTexture.cpp.orig 2014-06-25 00:28:50.820501856 +0200 -+++ irrlicht/source/Irrlicht/COGLESTexture.cpp 2014-06-25 00:08:37.712544692 +0200 -@@ -422,6 +422,9 @@ - source = dest; - } - -+ //clear old error -+ glGetError(); -+ - if (newTexture) - { - if (IsCompressed) diff --git a/build/android/patches/irrlicht-touchcount.patch b/build/android/patches/irrlicht-touchcount.patch deleted file mode 100644 index d4e4b9c3e..000000000 --- a/build/android/patches/irrlicht-touchcount.patch +++ /dev/null @@ -1,30 +0,0 @@ ---- irrlicht.orig/include/IEventReceiver.h 2014-06-03 19:43:50.433713133 +0200 -+++ irrlicht/include/IEventReceiver.h 2014-06-03 19:44:36.993711489 +0200 -@@ -375,6 +375,9 @@ - // Y position of simple touch. - s32 Y; - -+ // number of current touches -+ s32 touchedCount; -+ - //! Type of touch event. - ETOUCH_INPUT_EVENT Event; - }; ---- irrlicht.orig/source/Irrlicht/Android/CIrrDeviceAndroid.cpp 2014-06-03 19:43:50.505713130 +0200 -+++ irrlicht/source/Irrlicht/Android/CIrrDeviceAndroid.cpp 2014-06-03 19:45:37.265709359 +0200 -@@ -315,6 +315,7 @@ - event.TouchInput.ID = AMotionEvent_getPointerId(androidEvent, i); - event.TouchInput.X = AMotionEvent_getX(androidEvent, i); - event.TouchInput.Y = AMotionEvent_getY(androidEvent, i); -+ event.TouchInput.touchedCount = AMotionEvent_getPointerCount(androidEvent); - - device->postEventFromUser(event); - } -@@ -326,6 +327,7 @@ - event.TouchInput.ID = AMotionEvent_getPointerId(androidEvent, pointerIndex); - event.TouchInput.X = AMotionEvent_getX(androidEvent, pointerIndex); - event.TouchInput.Y = AMotionEvent_getY(androidEvent, pointerIndex); -+ event.TouchInput.touchedCount = AMotionEvent_getPointerCount(androidEvent); - - device->postEventFromUser(event); - } diff --git a/build/android/patches/libvorbis-libogg-fpu.patch b/build/android/patches/libvorbis-libogg-fpu.patch deleted file mode 100644 index 52ab397ac..000000000 --- a/build/android/patches/libvorbis-libogg-fpu.patch +++ /dev/null @@ -1,37 +0,0 @@ ---- libvorbis-libogg-android/jni/libvorbis-jni/Android.mk.orig 2014-06-17 19:22:50.621559073 +0200 -+++ libvorbis-libogg-android/jni/libvorbis-jni/Android.mk 2014-06-17 19:38:20.641581140 +0200 -@@ -4,9 +4,6 @@ - - LOCAL_MODULE := vorbis-jni - LOCAL_CFLAGS += -I$(LOCAL_PATH)/../include -fsigned-char --ifeq ($(TARGET_ARCH),arm) -- LOCAL_CFLAGS += -march=armv6 -marm -mfloat-abi=softfp -mfpu=vfp --endif - - LOCAL_SHARED_LIBRARIES := libogg libvorbis - ---- libvorbis-libogg-android/jni/libvorbis/Android.mk.orig 2014-06-17 19:22:39.077558797 +0200 -+++ libvorbis-libogg-android/jni/libvorbis/Android.mk 2014-06-17 19:38:52.121581887 +0200 -@@ -4,9 +4,6 @@ - - LOCAL_MODULE := libvorbis - LOCAL_CFLAGS += -I$(LOCAL_PATH)/../include -ffast-math -fsigned-char --ifeq ($(TARGET_ARCH),arm) -- LOCAL_CFLAGS += -march=armv6 -marm -mfloat-abi=softfp -mfpu=vfp --endif - LOCAL_SHARED_LIBRARIES := libogg - - LOCAL_SRC_FILES := \ ---- libvorbis-libogg-android/jni/libogg/Android.mk.orig 2014-06-17 19:22:33.965558675 +0200 -+++ libvorbis-libogg-android/jni/libogg/Android.mk 2014-06-17 19:38:25.337581252 +0200 -@@ -4,10 +4,6 @@ - - LOCAL_MODULE := libogg - LOCAL_CFLAGS += -I$(LOCAL_PATH)/../include -ffast-math -fsigned-char --ifeq ($(TARGET_ARCH),arm) -- LOCAL_CFLAGS += -march=armv6 -marm -mfloat-abi=softfp -mfpu=vfp --endif -- - - LOCAL_SRC_FILES := \ - bitwise.c \ diff --git a/build/android/patches/openssl_arch.patch b/build/android/patches/openssl_arch.patch deleted file mode 100644 index d15e2b137..000000000 --- a/build/android/patches/openssl_arch.patch +++ /dev/null @@ -1,13 +0,0 @@ ---- openssl-1.0.2e.orig/Configure 2015-12-03 15:04:23.000000000 +0100 -+++ openssl-1.0.2e/Configure 2015-12-14 21:01:40.351265968 +0100 -@@ -464,8 +464,10 @@ - # Android: linux-* but without pointers to headers and libs. - "android","gcc:-mandroid -I\$(ANDROID_DEV)/include -B\$(ANDROID_DEV)/lib -O3 -fomit-frame-pointer -Wall::-D_REENTRANT::-ldl:BN_LLONG RC4_CHAR RC4_CHUNK DES_INT DES_UNROLL BF_PTR:${no_asm}:dlfcn:linux-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)", - "android-x86","gcc:-mandroid -I\$(ANDROID_DEV)/include -B\$(ANDROID_DEV)/lib -O3 -fomit-frame-pointer -Wall::-D_REENTRANT::-ldl:BN_LLONG ${x86_gcc_des} ${x86_gcc_opts}:".eval{my $asm=${x86_elf_asm};$asm=~s/:elf/:android/;$asm}.":dlfcn:linux-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)", -+"android-arm","gcc:-march=armv4 -mandroid -I\$(ANDROID_DEV)/include -B\$(ANDROID_DEV)/lib -O3 -fomit-frame-pointer -Wall::-D_REENTRANT::-ldl:BN_LLONG RC4_CHAR RC4_CHUNK DES_INT DES_UNROLL BF_PTR:${armv4_asm}:dlfcn:linux-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)", - "android-armv7","gcc:-march=armv7-a -mandroid -I\$(ANDROID_DEV)/include -B\$(ANDROID_DEV)/lib -O3 -fomit-frame-pointer -Wall::-D_REENTRANT::-ldl:BN_LLONG RC4_CHAR RC4_CHUNK DES_INT DES_UNROLL BF_PTR:${armv4_asm}:dlfcn:linux-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)", - "android-mips","gcc:-mandroid -I\$(ANDROID_DEV)/include -B\$(ANDROID_DEV)/lib -O3 -Wall::-D_REENTRANT::-ldl:BN_LLONG RC4_CHAR RC4_CHUNK DES_INT DES_UNROLL BF_PTR:${mips32_asm}:o32:dlfcn:linux-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)", -+"android-mips32","gcc:-mandroid -I\$(ANDROID_DEV)/include -B\$(ANDROID_DEV)/lib -O3 -Wall::-D_REENTRANT::-ldl:BN_LLONG RC4_CHAR RC4_CHUNK DES_INT DES_UNROLL BF_PTR:${mips32_asm}:o32:dlfcn:linux-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)", - - #### *BSD [do see comment about ${BSDthreads} above!] - "BSD-generic32","gcc:-O3 -fomit-frame-pointer -Wall::${BSDthreads}:::BN_LLONG RC2_CHAR RC4_INDEX DES_INT DES_UNROLL:${no_asm}:dlfcn:bsd-gcc-shared:-fPIC::.so.\$(SHLIB_MAJOR).\$(SHLIB_MINOR)", diff --git a/build/android/settings.gradle b/build/android/settings.gradle index b0cee5dad..b048fca7c 100644 --- a/build/android/settings.gradle +++ b/build/android/settings.gradle @@ -1 +1,2 @@ rootProject.name = "Minetest" +include ':app', ':native' diff --git a/build/android/src/main/java/net.minetest.minetest/MainActivity.java b/build/android/src/main/java/net.minetest.minetest/MainActivity.java deleted file mode 100644 index 71b0ce144..000000000 --- a/build/android/src/main/java/net.minetest.minetest/MainActivity.java +++ /dev/null @@ -1,77 +0,0 @@ -package net.minetest.minetest; - -import android.Manifest; -import android.app.Activity; -import android.content.Intent; -import android.content.pm.PackageManager; -import android.os.Build; -import android.os.Bundle; -import android.widget.Toast; - -import androidx.annotation.NonNull; -import androidx.core.app.ActivityCompat; -import androidx.core.content.ContextCompat; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -public class MainActivity extends Activity { - private final static int PERMISSIONS = 1; - private static final String[] REQUIRED_SDK_PERMISSIONS = new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}; - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - checkPermission(); - } else { - next(); - } - } - - private void checkPermission() { - final List missingPermissions = new ArrayList<>(); - // check required permission - for (final String permission : REQUIRED_SDK_PERMISSIONS) { - final int result = ContextCompat.checkSelfPermission(this, permission); - if (result != PackageManager.PERMISSION_GRANTED) { - missingPermissions.add(permission); - } - } - if (!missingPermissions.isEmpty()) { - // request permission - final String[] permissions = missingPermissions - .toArray(new String[0]); - ActivityCompat.requestPermissions(this, permissions, PERMISSIONS); - } else { - final int[] grantResults = new int[REQUIRED_SDK_PERMISSIONS.length]; - Arrays.fill(grantResults, PackageManager.PERMISSION_GRANTED); - onRequestPermissionsResult(PERMISSIONS, REQUIRED_SDK_PERMISSIONS, - grantResults); - } - } - - @Override - public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, - @NonNull int[] grantResults) { - if (requestCode == PERMISSIONS) { - for (int index = 0; index < permissions.length; index++) { - if (grantResults[index] != PackageManager.PERMISSION_GRANTED) { - // permission not granted - toast and exit - Toast.makeText(this, R.string.not_granted, Toast.LENGTH_LONG).show(); - finish(); - return; - } - } - // permission were granted - run - next(); - } - } - - private void next() { - Intent intent = new Intent(this, MtNativeActivity.class); - intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_CLEAR_TASK); - startActivity(intent); - } -} diff --git a/build/android/src/main/java/net.minetest.minetest/MinetestAssetCopy.java b/build/android/src/main/java/net.minetest.minetest/MinetestAssetCopy.java deleted file mode 100644 index 84cfca796..000000000 --- a/build/android/src/main/java/net.minetest.minetest/MinetestAssetCopy.java +++ /dev/null @@ -1,371 +0,0 @@ -package net.minetest.minetest; - -import android.annotation.SuppressLint; -import android.app.Activity; -import android.content.res.AssetFileDescriptor; -import android.os.AsyncTask; -import android.os.Build; -import android.os.Bundle; -import android.os.Environment; -import android.util.Log; -import android.view.Display; -import android.view.View; -import android.widget.ProgressBar; -import android.widget.TextView; - -import java.io.BufferedReader; -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.OutputStream; -import java.util.Vector; - -public class MinetestAssetCopy extends Activity { - private ProgressBar m_ProgressBar; - private TextView m_Filename; - private copyAssetTask m_AssetCopy; - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.assetcopy); - m_ProgressBar = findViewById(R.id.progressBar1); - m_Filename = findViewById(R.id.textView1); - Display display = getWindowManager().getDefaultDisplay(); - m_ProgressBar.getLayoutParams().width = (int) (display.getWidth() * 0.8); - m_ProgressBar.invalidate(); - - /* check if there's already a copy in progress and reuse in case it is*/ - MinetestAssetCopy prevActivity = - (MinetestAssetCopy) getLastNonConfigurationInstance(); - if (prevActivity != null) { - m_AssetCopy = prevActivity.m_AssetCopy; - } else { - m_AssetCopy = new copyAssetTask(); - m_AssetCopy.execute(); - } - } - - @Override - protected void onResume() { - super.onResume(); - makeFullScreen(); - } - - @Override - protected void onDestroy() { - super.onDestroy(); - if (m_AssetCopy != null) { - m_AssetCopy.cancel(true); - } - } - - private void makeFullScreen() { - if (Build.VERSION.SDK_INT >= 19) - this.getWindow().getDecorView().setSystemUiVisibility( - View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY); - } - - @Override - public void onWindowFocusChanged(boolean hasFocus) { - super.onWindowFocusChanged(hasFocus); - if (hasFocus) - makeFullScreen(); - } - - /* preserve asset copy background task to prevent restart of copying */ - /* this way of doing it is not recommended for latest android version */ - /* but the recommended way isn't available on android 2.x */ - public Object onRetainNonConfigurationInstance() { - return this; - } - - @SuppressLint("StaticFieldLeak") - private class copyAssetTask extends AsyncTask { - boolean m_copy_started = false; - String m_Foldername = "media"; - Vector m_foldernames; - Vector m_filenames; - Vector m_tocopy; - Vector m_asset_size_unknown; - - private long getFullSize(String filename) { - long size = 0; - try { - InputStream src = getAssets().open(filename); - byte[] buf = new byte[4096]; - - int len; - while ((len = src.read(buf)) > 0) { - size += len; - } - } catch (IOException e) { - e.printStackTrace(); - } - return size; - } - - @Override - protected String doInBackground(String... files) { - m_foldernames = new Vector<>(); - m_filenames = new Vector<>(); - m_tocopy = new Vector<>(); - m_asset_size_unknown = new Vector<>(); - String baseDir = - Environment.getExternalStorageDirectory().getAbsolutePath() - + "/"; - - - // prepare temp folder - File TempFolder = new File(baseDir + "Minetest/tmp/"); - - if (!TempFolder.exists()) { - TempFolder.mkdir(); - } else { - File[] todel = TempFolder.listFiles(); - - for (File file : todel) { - Log.v("MinetestAssetCopy", "deleting: " + file.getAbsolutePath()); - file.delete(); - } - } - - // add a .nomedia file - try { - OutputStream dst = new FileOutputStream(baseDir + "Minetest/.nomedia"); - dst.close(); - } catch (IOException e) { - Log.e("MinetestAssetCopy", "Failed to create .nomedia file"); - e.printStackTrace(); - } - - - // build lists from prepared data - BuildFolderList(); - BuildFileList(); - - // scan filelist - ProcessFileList(); - - // doing work - m_copy_started = true; - m_ProgressBar.setMax(m_tocopy.size()); - - for (int i = 0; i < m_tocopy.size(); i++) { - try { - String filename = m_tocopy.get(i); - publishProgress(i); - - boolean asset_size_unknown = false; - long filesize = -1; - - if (m_asset_size_unknown.contains(filename)) { - File testme = new File(baseDir + "/" + filename); - - if (testme.exists()) - filesize = testme.length(); - - asset_size_unknown = true; - } - - InputStream src; - try { - src = getAssets().open(filename); - } catch (IOException e) { - Log.e("MinetestAssetCopy", "Copying file: " + filename + " FAILED (not in assets)"); - e.printStackTrace(); - continue; - } - - // Transfer bytes from in to out - byte[] buf = new byte[1024]; - int len = src.read(buf, 0, 1024); - - /* following handling is crazy but we need to deal with */ - /* compressed assets.Flash chips limited livetime due to */ - /* write operations, we can't allow large files to destroy */ - /* users flash. */ - if (asset_size_unknown) { - if ((len > 0) && (len < buf.length) && (len == filesize)) { - src.close(); - continue; - } - - if (len == buf.length) { - src.close(); - long size = getFullSize(filename); - if (size == filesize) { - continue; - } - src = getAssets().open(filename); - len = src.read(buf, 0, 1024); - } - } - if (len > 0) { - int total_filesize = 0; - OutputStream dst; - try { - dst = new FileOutputStream(baseDir + "/" + filename); - } catch (IOException e) { - Log.e("MinetestAssetCopy", "Copying file: " + baseDir + - "/" + filename + " FAILED (couldn't open output file)"); - e.printStackTrace(); - src.close(); - continue; - } - dst.write(buf, 0, len); - total_filesize += len; - - while ((len = src.read(buf)) > 0) { - dst.write(buf, 0, len); - total_filesize += len; - } - - dst.close(); - Log.v("MinetestAssetCopy", "Copied file: " + - m_tocopy.get(i) + " (" + total_filesize + - " bytes)"); - } else if (len < 0) { - Log.e("MinetestAssetCopy", "Copying file: " + - m_tocopy.get(i) + " failed, size < 0"); - } - src.close(); - } catch (IOException e) { - Log.e("MinetestAssetCopy", "Copying file: " + - m_tocopy.get(i) + " failed"); - e.printStackTrace(); - } - } - return ""; - } - - /** - * update progress bar - */ - protected void onProgressUpdate(Integer... progress) { - - if (m_copy_started) { - String todisplay = m_tocopy.get(progress[0]); - m_ProgressBar.setProgress(progress[0]); - m_Filename.setText(todisplay); - } else { - String todisplay = m_Foldername; - String full_text = "scanning " + todisplay + " ..."; - m_Filename.setText(full_text); - } - } - - /** - * check all files and folders in filelist - */ - void ProcessFileList() { - String FlashBaseDir = - Environment.getExternalStorageDirectory().getAbsolutePath(); - - for (String current_path : m_filenames) { - String FlashPath = FlashBaseDir + "/" + current_path; - - if (isAssetFolder(current_path)) { - /* store information and update gui */ - m_Foldername = current_path; - publishProgress(0); - - /* open file in order to check if it's a folder */ - File current_folder = new File(FlashPath); - if (!current_folder.exists()) { - if (!current_folder.mkdirs()) { - Log.e("MinetestAssetCopy", "\t failed create folder: " + - FlashPath); - } else { - Log.v("MinetestAssetCopy", "\t created folder: " + - FlashPath); - } - } - - continue; - } - - /* if it's not a folder it's most likely a file */ - boolean refresh = true; - - File testme = new File(FlashPath); - - long asset_filesize = -1; - long stored_filesize; - - if (testme.exists()) { - try { - AssetFileDescriptor fd = getAssets().openFd(current_path); - asset_filesize = fd.getLength(); - fd.close(); - } catch (IOException e) { - m_asset_size_unknown.add(current_path); - Log.e("MinetestAssetCopy", "Failed to open asset file \"" + - FlashPath + "\" for size check"); - } - - stored_filesize = testme.length(); - - if (asset_filesize == stored_filesize) - refresh = false; - - } - - if (refresh) - m_tocopy.add(current_path); - } - } - - /** - * read list of folders prepared on package build - */ - void BuildFolderList() { - try { - InputStream is = getAssets().open("index.txt"); - BufferedReader reader = new BufferedReader(new InputStreamReader(is)); - - String line = reader.readLine(); - while (line != null) { - m_foldernames.add(line); - line = reader.readLine(); - } - is.close(); - } catch (IOException e1) { - Log.e("MinetestAssetCopy", "Error on processing index.txt"); - e1.printStackTrace(); - } - } - - /** - * read list of asset files prepared on package build - */ - void BuildFileList() { - long entrycount = 0; - try { - InputStream is = getAssets().open("filelist.txt"); - BufferedReader reader = new BufferedReader(new InputStreamReader(is)); - - String line = reader.readLine(); - while (line != null) { - m_filenames.add(line); - line = reader.readLine(); - entrycount++; - } - is.close(); - } catch (IOException e1) { - Log.e("MinetestAssetCopy", "Error on processing filelist.txt"); - e1.printStackTrace(); - } - } - - protected void onPostExecute(String result) { - finish(); - } - - boolean isAssetFolder(String path) { - return m_foldernames.contains(path); - } - } -} diff --git a/build/android/src/main/java/net.minetest.minetest/MinetestTextEntry.java b/build/android/src/main/java/net.minetest.minetest/MinetestTextEntry.java deleted file mode 100644 index cb7ba8d3b..000000000 --- a/build/android/src/main/java/net.minetest.minetest/MinetestTextEntry.java +++ /dev/null @@ -1,87 +0,0 @@ -package net.minetest.minetest; - -import android.app.Activity; -import android.app.AlertDialog; -import android.content.DialogInterface; -import android.content.Intent; -import android.os.Bundle; -import android.text.InputType; -import android.view.KeyEvent; -import android.view.View; -import android.view.View.OnKeyListener; -import android.widget.EditText; - -public class MinetestTextEntry extends Activity { - private final int MultiLineTextInput = 1; - private final int SingleLineTextInput = 2; - private final int SingleLinePasswordInput = 3; - private AlertDialog mTextInputDialog; - private EditText mTextInputWidget; - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - Bundle b = getIntent().getExtras(); - String acceptButton = b.getString("EnterButton"); - String hint = b.getString("hint"); - String current = b.getString("current"); - int editType = b.getInt("editType"); - - AlertDialog.Builder builder = new AlertDialog.Builder(this); - mTextInputWidget = new EditText(this); - mTextInputWidget.setHint(hint); - mTextInputWidget.setText(current); - mTextInputWidget.setMinWidth(300); - if (editType == SingleLinePasswordInput) { - mTextInputWidget.setInputType(InputType.TYPE_CLASS_TEXT | - InputType.TYPE_TEXT_VARIATION_PASSWORD); - } else { - mTextInputWidget.setInputType(InputType.TYPE_CLASS_TEXT); - } - - builder.setView(mTextInputWidget); - - if (editType == MultiLineTextInput) { - builder.setPositiveButton(acceptButton, new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int whichButton) { - pushResult(mTextInputWidget.getText().toString()); - } - }); - } - - builder.setOnCancelListener(new DialogInterface.OnCancelListener() { - public void onCancel(DialogInterface dialog) { - cancelDialog(); - } - }); - - mTextInputWidget.setOnKeyListener(new OnKeyListener() { - @Override - public boolean onKey(View view, int KeyCode, KeyEvent event) { - if (KeyCode == KeyEvent.KEYCODE_ENTER) { - - pushResult(mTextInputWidget.getText().toString()); - return true; - } - return false; - } - }); - - mTextInputDialog = builder.create(); - mTextInputDialog.show(); - } - - private void pushResult(String text) { - Intent resultData = new Intent(); - resultData.putExtra("text", text); - setResult(Activity.RESULT_OK, resultData); - mTextInputDialog.dismiss(); - finish(); - } - - private void cancelDialog() { - setResult(Activity.RESULT_CANCELED); - mTextInputDialog.dismiss(); - finish(); - } -} diff --git a/build/android/src/main/res/layout/assetcopy.xml b/build/android/src/main/res/layout/assetcopy.xml deleted file mode 100644 index b3da2f027..000000000 --- a/build/android/src/main/res/layout/assetcopy.xml +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - - diff --git a/build/android/src/main/res/values-v21/styles.xml b/build/android/src/main/res/values-v21/styles.xml deleted file mode 100644 index 8b0777467..000000000 --- a/build/android/src/main/res/values-v21/styles.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - diff --git a/build/android/src/main/res/values/strings.xml b/build/android/src/main/res/values/strings.xml deleted file mode 100644 index a5eaef5d1..000000000 --- a/build/android/src/main/res/values/strings.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - Preparing media… - Required permission wasn\'t granted, Minetest can\'t run without it - \ No newline at end of file diff --git a/build/android/src/main/res/values/styles.xml b/build/android/src/main/res/values/styles.xml deleted file mode 100644 index 1bd41ae76..000000000 --- a/build/android/src/main/res/values/styles.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - diff --git a/doc/README.android b/doc/README.android index c21279583..f6b67978f 100644 --- a/doc/README.android +++ b/doc/README.android @@ -1,6 +1,5 @@ -Minetest Android port -===================== -Date: 2014 06 28 +Minetest: Android version +========================= Controls -------- @@ -40,25 +39,6 @@ file can usually be found at /mnt/sdcard/Minetest. main menu is too big or small on your device, try changing this value. -Known issues ------------- -Not all issues are fixed by now: - -* Unable to exit from volume menu -- don't use the volume menu, use Android's - volume controls instead. -* 512 MB RAM seems to be inadequate -- this depends on the server you join. - Try to play on more lightweight servers. - -Versioning ----------- -Android version numbers are 4 digits instead of Minetest's 3 digits. The last -number of Android's version represents the Android internal version code. This -version code is strictly incremental. It's incremented for each official -Minetest Android build. - -E.g. prerelease Minetest Android builds have been 0.4.9.3, while the first -official version most likely will be 0.4.10.4 - Requirements ------------ @@ -69,9 +49,9 @@ following software packages. The version number in parenthesis denotes the version that was tested at the time this README was drafted; newer/older versions may or may not work. -* android SDK (api-26) -* android NDK (r17c) -* wget (1.13.4) +* Android SDK 29 +* Android NDK r21 +* Android Studio 3 [optional] Additionally, you'll need to have an Internet connection available on the build system, as the Android build will download some source packages. @@ -79,16 +59,15 @@ build system, as the Android build will download some source packages. Build ----- -Debug build: -* Enter "build/android" subdirectory -* Execute "make" -* Answer the questions about where SDK and NDK are located on your filesystem -* Wait for build to finish +The new build system Minetest Android is fully functional and is designed to +speed up and simplify the work, as well as adding the possibility of +cross-platform build. +You can use `./gradlew assemblerelease` or `./gradlew assembledebug` from the +command line or use Android Studio and click the build button. -After the build is finished, the resulting apk can be fond in -build/android/bin/. It will be called Minetest-debug.apk - -Release build: +When using gradlew, the newest NDK will be downloaded and installed +automatically. Or you can create a `local.properties` file and specify +`sdk.dir` and `ndk.dir` yourself. * In order to make a release build you'll have to have a keystore setup to sign the resulting apk package. How this is done is not part of this README. There @@ -97,32 +76,6 @@ Release build: * Once your keystore is setup, enter build/android subdirectory and create a new file "ant.properties" there. Add following lines to that file: - + > key.store= > key.alias=Minetest - -* Execute "make release" -* Enter your keystore as well as your Mintest key password once asked. Be - careful it's shown on console in clear text! -* The result can be found at "bin/Minetest-release.apk" - -Other things that may be nice to know ------------- -* The environment for Android development tools is saved within Android build - build folder. If you want direct access to it do: - - > make envpaths - > . and_env - - After you've done this you'll have your path and path variables set correct - to use adb and all other Android development tools - -* You can build a single dependency by calling make and the dependency's name, - e.g.: - - > make irrlicht - -* You can completely cleanup a dependency by calling make and the "clean" target, - e.g.: - - > make clean_irrlicht diff --git a/src/config.h b/src/config.h index 039100914..9d58a8cc1 100644 --- a/src/config.h +++ b/src/config.h @@ -11,12 +11,12 @@ #if defined USE_CMAKE_CONFIG_H #include "cmake_config.h" -#elif defined (__ANDROID__) || defined (ANDROID) +#elif defined (__ANDROID__) #define PROJECT_NAME "minetest" #define PROJECT_NAME_C "Minetest" #define STATIC_SHAREDIR "" - #include "android_version.h" - #ifdef NDEBUG + #define VERSION_STRING STR(VERSION_MAJOR) "." STR(VERSION_MINOR) "." STR(VERSION_PATCH) STR(VERSION_EXTRA) +#ifdef NDEBUG #define BUILD_TYPE "Release" #else #define BUILD_TYPE "Debug" diff --git a/src/main.cpp b/src/main.cpp index 8df2fe7d3..db020661a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -457,7 +457,6 @@ static bool create_userdata_path() } else { success = true; } - porting::copyAssets(); #else // Create user data directory success = fs::CreateDir(porting::path_user); diff --git a/src/porting_android.cpp b/src/porting_android.cpp index 7c74f7b5b..4130bdb6d 100644 --- a/src/porting_android.cpp +++ b/src/porting_android.cpp @@ -69,10 +69,10 @@ void android_main(android_app *app) /* TODO this doesn't work as expected, no idea why but there's a workaround */ /* for it right now */ extern "C" { - JNIEXPORT void JNICALL Java_net_minetest_MtNativeActivity_putMessageBoxResult( + JNIEXPORT void JNICALL Java_net_minetest_minetest_GameActivity_putMessageBoxResult( JNIEnv * env, jclass thiz, jstring text) { - errorstream << "Java_net_minetest_MtNativeActivity_putMessageBoxResult got: " + errorstream << "Java_net_minetest_minetest_GameActivity_putMessageBoxResult got: " << std::string((const char*)env->GetStringChars(text,0)) << std::endl; } @@ -107,17 +107,6 @@ jclass findClass(std::string classname) return (jclass) jnienv->CallObjectMethod(cls, findClass, strClassName); } -void copyAssets() -{ - jmethodID assetcopy = jnienv->GetMethodID(nativeActivity,"copyAssets","()V"); - - if (assetcopy == 0) { - assert("porting::copyAssets unable to find copy assets method" == 0); - } - - jnienv->CallVoidMethod(app_global->activity->clazz, assetcopy); -} - void initAndroid() { porting::jnienv = NULL; @@ -136,7 +125,7 @@ void initAndroid() exit(-1); } - nativeActivity = findClass("net/minetest/minetest/MtNativeActivity"); + nativeActivity = findClass("net/minetest/minetest/GameActivity"); if (nativeActivity == 0) { errorstream << "porting::initAndroid unable to find java native activity class" << diff --git a/src/porting_android.h b/src/porting_android.h index 0b3f2575e..60eb2a9c0 100644 --- a/src/porting_android.h +++ b/src/porting_android.h @@ -48,11 +48,6 @@ void cleanupAndroid(); */ void initializePathsAndroid(); -/** - * use java function to copy media from assets to external storage - */ -void copyAssets(); - /** * show text input dialog in java * @param acceptButton text to display on accept button diff --git a/src/version.cpp b/src/version.cpp index c0c59ee99..ae5ca3412 100644 --- a/src/version.cpp +++ b/src/version.cpp @@ -20,10 +20,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "version.h" #include "config.h" -#if defined(__ANDROID__) - #include "android_version.h" - #include "android_version_githash.h" -#elif defined(USE_CMAKE_CONFIG_H) +#if USE_CMAKE_CONFIG_H #include "cmake_config_githash.h" #endif