Selaa lähdekoodia

Merge remote-tracking branch 'vcmi/beta' into bugfixing

Ivan Savenko 1 vuosi sitten
vanhempi
sitoutus
55efa3c719
100 muutettua tiedostoa jossa 375 lisäystä ja 5717 poistoa
  1. 27 24
      .github/workflows/github.yml
  2. 4 3
      CI/android/before_install.sh
  3. 4 0
      CI/conan/android-32-ndk
  4. 4 0
      CI/conan/android-64-ndk
  5. 26 32
      CMakeLists.txt
  6. 15 1
      CMakePresets.json
  7. 3 1
      android/.gitignore
  8. 98 0
      android/AndroidManifest.xml
  9. 0 9
      android/GeneratedVersion.java.in
  10. 17 0
      android/androiddeployqt.json.in
  11. 0 1
      android/defs.gradle
  12. 6 1
      android/gradle.properties
  13. 0 8
      android/vcmi-app/.gitignore
  14. 48 62
      android/vcmi-app/build.gradle
  15. 0 54
      android/vcmi-app/src/main/AndroidManifest.xml
  16. 0 94
      android/vcmi-app/src/main/java/eu/vcmi/vcmi/ActivityAbout.java
  17. 0 58
      android/vcmi-app/src/main/java/eu/vcmi/vcmi/ActivityBase.java
  18. 0 48
      android/vcmi-app/src/main/java/eu/vcmi/vcmi/ActivityError.java
  19. 25 270
      android/vcmi-app/src/main/java/eu/vcmi/vcmi/ActivityLauncher.java
  20. 0 351
      android/vcmi-app/src/main/java/eu/vcmi/vcmi/ActivityMods.java
  21. 0 53
      android/vcmi-app/src/main/java/eu/vcmi/vcmi/ActivityWithToolbar.java
  22. 0 217
      android/vcmi-app/src/main/java/eu/vcmi/vcmi/Config.java
  23. 0 11
      android/vcmi-app/src/main/java/eu/vcmi/vcmi/Const.java
  24. 1 10
      android/vcmi-app/src/main/java/eu/vcmi/vcmi/NativeMethods.java
  25. 1 20
      android/vcmi-app/src/main/java/eu/vcmi/vcmi/Storage.java
  26. 2 7
      android/vcmi-app/src/main/java/eu/vcmi/vcmi/VcmiSDLActivity.java
  27. 0 171
      android/vcmi-app/src/main/java/eu/vcmi/vcmi/content/AsyncLauncherInitialization.java
  28. 0 52
      android/vcmi-app/src/main/java/eu/vcmi/vcmi/content/DialogAuthors.java
  29. 0 36
      android/vcmi-app/src/main/java/eu/vcmi/vcmi/content/ModBaseViewHolder.java
  30. 0 254
      android/vcmi-app/src/main/java/eu/vcmi/vcmi/content/ModsAdapter.java
  31. 0 35
      android/vcmi-app/src/main/java/eu/vcmi/vcmi/content/ModsViewHolder.java
  32. 0 258
      android/vcmi-app/src/main/java/eu/vcmi/vcmi/mods/VCMIMod.java
  33. 0 106
      android/vcmi-app/src/main/java/eu/vcmi/vcmi/mods/VCMIModContainer.java
  34. 0 108
      android/vcmi-app/src/main/java/eu/vcmi/vcmi/mods/VCMIModsRepo.java
  35. 0 46
      android/vcmi-app/src/main/java/eu/vcmi/vcmi/settings/AdventureAiController.java
  36. 0 37
      android/vcmi-app/src/main/java/eu/vcmi/vcmi/settings/AdventureAiSelectionDialog.java
  37. 0 193
      android/vcmi-app/src/main/java/eu/vcmi/vcmi/settings/CopyDataController.java
  38. 0 19
      android/vcmi-app/src/main/java/eu/vcmi/vcmi/settings/DoubleConfig.java
  39. 0 174
      android/vcmi-app/src/main/java/eu/vcmi/vcmi/settings/ExportDataController.java
  40. 0 48
      android/vcmi-app/src/main/java/eu/vcmi/vcmi/settings/LanguageSettingController.java
  41. 0 55
      android/vcmi-app/src/main/java/eu/vcmi/vcmi/settings/LanguageSettingDialog.java
  42. 0 75
      android/vcmi-app/src/main/java/eu/vcmi/vcmi/settings/LauncherSettingController.java
  43. 0 73
      android/vcmi-app/src/main/java/eu/vcmi/vcmi/settings/LauncherSettingDialog.java
  44. 0 31
      android/vcmi-app/src/main/java/eu/vcmi/vcmi/settings/LauncherSettingWithDialogController.java
  45. 0 83
      android/vcmi-app/src/main/java/eu/vcmi/vcmi/settings/LauncherSettingWithSliderController.java
  46. 0 38
      android/vcmi-app/src/main/java/eu/vcmi/vcmi/settings/ModsBtnController.java
  47. 0 40
      android/vcmi-app/src/main/java/eu/vcmi/vcmi/settings/MusicSettingController.java
  48. 0 63
      android/vcmi-app/src/main/java/eu/vcmi/vcmi/settings/PointerModeSettingController.java
  49. 0 55
      android/vcmi-app/src/main/java/eu/vcmi/vcmi/settings/PointerModeSettingDialog.java
  50. 0 51
      android/vcmi-app/src/main/java/eu/vcmi/vcmi/settings/PointerMultiplierSettingController.java
  51. 0 48
      android/vcmi-app/src/main/java/eu/vcmi/vcmi/settings/PointerMultiplierSettingDialog.java
  52. 0 64
      android/vcmi-app/src/main/java/eu/vcmi/vcmi/settings/ScreenScaleSettingController.java
  53. 0 98
      android/vcmi-app/src/main/java/eu/vcmi/vcmi/settings/ScreenScaleSettingDialog.java
  54. 0 40
      android/vcmi-app/src/main/java/eu/vcmi/vcmi/settings/SoundSettingController.java
  55. 0 39
      android/vcmi-app/src/main/java/eu/vcmi/vcmi/settings/StartGameController.java
  56. 0 49
      android/vcmi-app/src/main/java/eu/vcmi/vcmi/util/AsyncRequest.java
  57. 54 297
      android/vcmi-app/src/main/java/eu/vcmi/vcmi/util/FileUtil.java
  58. 0 8
      android/vcmi-app/src/main/java/eu/vcmi/vcmi/util/IZipProgressReporter.java
  59. 0 198
      android/vcmi-app/src/main/java/eu/vcmi/vcmi/util/InstallModAsync.java
  60. 4 1
      android/vcmi-app/src/main/java/eu/vcmi/vcmi/util/LibsLoader.java
  61. 0 41
      android/vcmi-app/src/main/java/eu/vcmi/vcmi/util/Log.java
  62. 0 30
      android/vcmi-app/src/main/java/eu/vcmi/vcmi/util/ServerResponse.java
  63. 0 92
      android/vcmi-app/src/main/java/eu/vcmi/vcmi/util/SharedPrefs.java
  64. 0 58
      android/vcmi-app/src/main/java/eu/vcmi/vcmi/util/Utils.java
  65. 0 52
      android/vcmi-app/src/main/java/eu/vcmi/vcmi/viewmodels/ObservableViewModel.java
  66. 9 2
      android/vcmi-app/src/main/java/org/libsdl/app/SDLActivity.java
  67. 26 0
      android/vcmi-app/src/main/java/org/libsdl/app/SDLControllerManager.java
  68. BIN
      android/vcmi-app/src/main/res/drawable-nodpi/divider_compat.png
  69. 0 30
      android/vcmi-app/src/main/res/drawable-v24/ic_launcher_foreground.xml
  70. 0 4
      android/vcmi-app/src/main/res/drawable/compat_toolbar_shadow.xml
  71. 0 9
      android/vcmi-app/src/main/res/drawable/ic_error.xml
  72. 0 9
      android/vcmi-app/src/main/res/drawable/ic_info.xml
  73. 0 170
      android/vcmi-app/src/main/res/drawable/ic_launcher_background.xml
  74. 0 9
      android/vcmi-app/src/main/res/drawable/ic_star_empty.xml
  75. 0 9
      android/vcmi-app/src/main/res/drawable/ic_star_full.xml
  76. 0 9
      android/vcmi-app/src/main/res/drawable/ic_star_half.xml
  77. 0 6
      android/vcmi-app/src/main/res/drawable/overlay_edittext_background.xml
  78. 0 7
      android/vcmi-app/src/main/res/drawable/recycler_divider_drawable.xml
  79. 0 19
      android/vcmi-app/src/main/res/layout-v21/inc_toolbar.xml
  80. 0 71
      android/vcmi-app/src/main/res/layout/activity_about.xml
  81. 0 22
      android/vcmi-app/src/main/res/layout/activity_error.xml
  82. 1 1
      android/vcmi-app/src/main/res/layout/activity_game.xml
  83. 0 106
      android/vcmi-app/src/main/res/layout/activity_launcher.xml
  84. 0 29
      android/vcmi-app/src/main/res/layout/activity_mods.xml
  85. 0 18
      android/vcmi-app/src/main/res/layout/activity_toolbar_wrapper.xml
  86. 0 34
      android/vcmi-app/src/main/res/layout/dialog_authors.xml
  87. 0 33
      android/vcmi-app/src/main/res/layout/inc_launcher_btn.xml
  88. 0 27
      android/vcmi-app/src/main/res/layout/inc_launcher_slider.xml
  89. 0 7
      android/vcmi-app/src/main/res/layout/inc_separator.xml
  90. 0 28
      android/vcmi-app/src/main/res/layout/inc_toolbar.xml
  91. 0 25
      android/vcmi-app/src/main/res/layout/mod_base_adapter_item.xml
  92. 0 101
      android/vcmi-app/src/main/res/layout/mods_adapter_item.xml
  93. 0 10
      android/vcmi-app/src/main/res/menu/menu_launcher.xml
  94. 0 9
      android/vcmi-app/src/main/res/menu/menu_mods.xml
  95. 0 67
      android/vcmi-app/src/main/res/values-cs/strings.xml
  96. 0 58
      android/vcmi-app/src/main/res/values-de/strings.xml
  97. 0 67
      android/vcmi-app/src/main/res/values-es/strings.xml
  98. 0 60
      android/vcmi-app/src/main/res/values-pl/strings.xml
  99. 0 55
      android/vcmi-app/src/main/res/values-ru/strings.xml
  100. 0 55
      android/vcmi-app/src/main/res/values-uk/strings.xml

+ 27 - 24
.github/workflows/github.yml

@@ -88,16 +88,16 @@ jobs:
             preset: windows-mingw-conan-linux
             conan_profile: mingw32-linux.jinja
           - platform: android-32
-            os: ubuntu-22.04
+            os: macos-14
             extension: apk
-            preset: android-conan-ninja-release
+            preset: android-daily-release
             conan_profile: android-32
             conan_options: --conf tools.android:ndk_path=$ANDROID_NDK_ROOT
             artifact_platform: armeabi-v7a
           - platform: android-64
-            os: ubuntu-22.04
+            os: macos-14
             extension: apk
-            preset: android-conan-ninja-release
+            preset: android-daily-release
             conan_profile: android-64
             conan_options: --conf tools.android:ndk_path=$ANDROID_NDK_ROOT
             artifact_platform: arm64-v8a
@@ -187,6 +187,12 @@ jobs:
       env:
         GENERATE_ONLY_BUILT_CONFIG: 1
 
+    - uses: actions/setup-java@v4
+      if: ${{ startsWith(matrix.platform, 'android') }}
+      with:
+        distribution: 'temurin'
+        java-version: '11'
+
     - name: Build Number
       run: |
         source '${{github.workspace}}/CI/get_package_name.sh'
@@ -215,7 +221,7 @@ jobs:
       run: |
         ctest --preset ${{matrix.preset}}
 
-    - name: Kill XProtect to work around CPack issue on macOS 
+    - name: Kill XProtect to work around CPack issue on macOS
       if: ${{ startsWith(matrix.platform, 'mac') }}
       run: |
         # Cf. https://github.com/actions/runner-images/issues/7522#issuecomment-1556766641
@@ -234,13 +240,6 @@ jobs:
           && '${{github.workspace}}/CI/${{matrix.platform}}/post_pack.sh' '${{github.workspace}}' "$(ls '${{ env.VCMI_PACKAGE_FILE_NAME }}'.*)"
         rm -rf _CPack_Packages
 
-    - name: Create Android package
-      if: ${{ startsWith(matrix.platform, 'android') }}
-      run: |
-        cd android
-        ./gradlew assembleDaily --info
-        echo ANDROID_APK_PATH="$(ls ${{ github.workspace }}/android/vcmi-app/build/outputs/apk/daily/*.${{ matrix.extension }})" >> $GITHUB_ENV
-
     - name: Additional logs
       if: ${{ failure() && steps.cpack.outcome == 'failure' && matrix.platform == 'msvc' }}
       run: |
@@ -254,7 +253,14 @@ jobs:
         name: ${{ env.VCMI_PACKAGE_FILE_NAME }} - ${{ matrix.platform }}
         path: |
           ${{github.workspace}}/out/build/${{matrix.preset}}/${{ env.VCMI_PACKAGE_FILE_NAME }}.${{ matrix.extension }}
-          
+
+    - name: Find Android package
+      if: ${{ startsWith(matrix.platform, 'android') }}
+      run: |
+        builtApkPath="$(ls ${{ github.workspace }}/out/build/${{ matrix.preset }}/android-build/vcmi-app/build/outputs/apk/release/*.${{ matrix.extension }})"
+        ANDROID_APK_PATH="${{ github.workspace }}/$VCMI_PACKAGE_FILE_NAME.${{ matrix.extension }}"
+        mv "$builtApkPath" "$ANDROID_APK_PATH"
+        echo "ANDROID_APK_PATH=$ANDROID_APK_PATH" >> $GITHUB_ENV
     - name: Android artifacts
       if: ${{ startsWith(matrix.platform, 'android') }}
       uses: actions/upload-artifact@v4
@@ -262,7 +268,7 @@ jobs:
         name: ${{ env.VCMI_PACKAGE_FILE_NAME }} - ${{ matrix.platform }}
         path: |
           ${{ env.ANDROID_APK_PATH }}
-          
+
     - name: Symbols
       if: ${{ matrix.platform == 'msvc' }}
       uses: actions/upload-artifact@v4
@@ -283,19 +289,17 @@ jobs:
       if: ${{ (matrix.pack == 1 || startsWith(matrix.platform, 'android')) && (github.ref == 'refs/heads/develop' || github.ref == 'refs/heads/beta' || github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/heads/features/')) && matrix.platform != 'msvc' && matrix.platform != 'mingw-32' }}
       continue-on-error: true
       run: |
-        if cd '${{github.workspace}}/android/vcmi-app/build/outputs/apk/daily' ; then
-          mv '${{ env.ANDROID_APK_PATH }}' "$VCMI_PACKAGE_FILE_NAME.${{ matrix.extension }}"
-        else
+        if [ -z '${{ env.ANDROID_APK_PATH }}' ] ; then
           cd '${{github.workspace}}/out/build/${{matrix.preset}}'
         fi
         source '${{github.workspace}}/CI/upload_package.sh'
       env:
         DEPLOY_RSA: ${{ secrets.DEPLOY_RSA }}
         PACKAGE_EXTENSION: ${{ matrix.extension }}
-  
+
   # copy-pasted mostly
   bundle_release:
-    
+
     needs: build
     if: always() && github.ref == 'refs/heads/master'
     strategy:
@@ -303,7 +307,6 @@ jobs:
         include:
           - platform: android-32
             os: ubuntu-22.04
-            extension: aab
             preset: android-conan-ninja-release
             conan_profile: android-32
             conan_options: --conf tools.android:ndk_path=$ANDROID_NDK_ROOT
@@ -367,13 +370,13 @@ jobs:
       uses: actions/download-artifact@v4
       with:
         name: Android JNI android-64
-        path: ${{ github.workspace }}/android/vcmi-app/src/main/jniLibs/
- 
+        path: ${{ github.workspace }}/out/build/${{ matrix.preset }}/android-build/libs
+
     - name: Create Android package
       run: |
-        cd android
+        cd out/build/${{ matrix.preset }}/android-build
         ./gradlew bundleRelease --info
-        echo ANDROID_APK_PATH="$(ls ${{ github.workspace }}/android/vcmi-app/build/outputs/bundle/release/*.aab)" >> $GITHUB_ENV
+        echo ANDROID_APK_PATH="$(ls ${{ github.workspace }}/out/build/${{ matrix.preset }}/android-build/vcmi-app/build/outputs/bundle/release/*.aab)" >> $GITHUB_ENV
       env:
         ANDROID_STORE_PASSWORD: ${{ secrets.ANDROID_STORE_PASSWORD }}
         ANDROID_KEY_PASSWORD: ${{ secrets.ANDROID_KEY_PASSWORD }}

+ 4 - 3
CI/android/before_install.sh

@@ -1,8 +1,9 @@
 #!/usr/bin/env bash
 
-sudo apt-get update
-sudo apt-get install ninja-build
+echo "ANDROID_NDK_ROOT=$ANDROID_HOME/ndk/25.2.9519653" >> $GITHUB_ENV
+
+brew install ninja
 
 mkdir ~/.conan ; cd ~/.conan
-curl -L "https://github.com/vcmi/vcmi-dependencies/releases/download/android-1.0/$DEPS_FILENAME.txz" \
+curl -L "https://github.com/vcmi/vcmi-dependencies/releases/download/android-1.1/$DEPS_FILENAME.txz" \
 	| tar -xf - --xz

+ 4 - 0
CI/conan/android-32-ndk

@@ -0,0 +1,4 @@
+include(android-32)
+
+[tool_requires]
+android-ndk/r25c

+ 4 - 0
CI/conan/android-64-ndk

@@ -0,0 +1,4 @@
+include(android-64)
+
+[tool_requires]
+android-ndk/r25c

+ 26 - 32
CMakeLists.txt

@@ -58,13 +58,22 @@ option(ENABLE_CCACHE "Speed up recompilation by caching previous compilations" O
 # Platform-specific options
 
 if(ANDROID)
+	set(ANDROID_TARGET_SDK_VERSION "33" CACHE STRING "Android target SDK version")
+	set(ANDROIDDEPLOYQT_OPTIONS "" CACHE STRING "Additional androiddeployqt options separated by semi-colon")
+	set(ANDROID_GRADLE_PROPERTIES "" CACHE STRING "Additional Gradle properties separated by semi-colon")
+
 	set(ENABLE_STATIC_LIBS ON)
-	set(ENABLE_LAUNCHER OFF)
+	set(ENABLE_LAUNCHER ON)
 else()
 	option(ENABLE_STATIC_LIBS "Build library and all components such as AI statically" OFF)
 	option(ENABLE_LAUNCHER "Enable compilation of launcher" ON)
 endif()
 
+if(APPLE_IOS)
+	set(BUNDLE_IDENTIFIER_PREFIX "" CACHE STRING "Bundle identifier prefix")
+	set(APP_DISPLAY_NAME "VCMI" CACHE STRING "App name on the home screen")
+endif()
+
 if(APPLE_IOS OR ANDROID)
 	set(ENABLE_MONOLITHIC_INSTALL OFF)
 	set(ENABLE_SINGLE_APP_BUILD ON)
@@ -100,11 +109,6 @@ if (ENABLE_STATIC_LIBS AND CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
 	set(CMAKE_POSITION_INDEPENDENT_CODE ON)
 endif()
 
-if(APPLE_IOS)
-	set(BUNDLE_IDENTIFIER_PREFIX "" CACHE STRING "Bundle identifier prefix")
-	set(APP_DISPLAY_NAME "VCMI" CACHE STRING "App name on the home screen")
-endif()
-
 if(ENABLE_COLORIZED_COMPILER_OUTPUT)
 	if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
 		add_compile_options(-fcolor-diagnostics)
@@ -147,10 +151,6 @@ set(CMAKE_MODULE_PATH ${CMAKE_HOME_DIRECTORY}/cmake_modules ${PROJECT_SOURCE_DIR
 
 include(VCMIUtils)
 include(VersionDefinition)
-if(ANDROID)
-	set(VCMI_VERSION "${APP_SHORT_VERSION}")
-	configure_file("android/GeneratedVersion.java.in" "${CMAKE_SOURCE_DIR}/android/vcmi-app/src/main/java/eu/vcmi/vcmi/util/GeneratedVersion.java" @ONLY)
-endif()
 
 vcmi_print_important_variables()
 
@@ -575,8 +575,12 @@ elseif(APPLE)
 	endif()
 elseif(ANDROID)
 	include(GNUInstallDirs)
-	set(LIB_DIR "jniLibs/${ANDROID_ABI}")
-	set(DATA_DIR "assets")
+	set(LIB_DIR "libs/${ANDROID_ABI}")
+
+	# required by Qt
+	set(androidPackageSourceDir "${CMAKE_SOURCE_DIR}/android")
+	set(androidQtBuildDir "${CMAKE_BINARY_DIR}/android-build")
+	set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${androidQtBuildDir}/${LIB_DIR}")
 else()
 	# includes lib path which determines where to install shared libraries (either /lib or /lib64)
 	include(GNUInstallDirs)
@@ -621,6 +625,13 @@ else()
 	set(SCRIPTING_LIB_DIR "${LIB_DIR}/scripting")
 endif()
 
+# common Qt paths
+if(ENABLE_LAUNCHER OR ENABLE_EDITOR)
+	get_target_property(qmakePath Qt${QT_VERSION_MAJOR}::qmake IMPORTED_LOCATION)
+	get_filename_component(qtDir "${qmakePath}/../../" ABSOLUTE)
+	set(qtBinDir "${qtDir}/bin")
+endif()
+
 #######################################
 #        Add subdirectories           #
 #######################################
@@ -682,32 +693,15 @@ endif()
 #######################################
 
 if(ANDROID)
+	string(REPLACE ";" "\n" ANDROID_GRADLE_PROPERTIES_MULTILINE "${ANDROID_GRADLE_PROPERTIES}")
+	file(WRITE "${androidPackageSourceDir}/vcmi-app/gradle.properties" "signingRoot=${CMAKE_SOURCE_DIR}/CI/android\n${ANDROID_GRADLE_PROPERTIES_MULTILINE}")
+
 	if(ANDROID_STL MATCHES "_shared$")
 		set(stlLibName "${CMAKE_SHARED_LIBRARY_PREFIX}${ANDROID_STL}${CMAKE_SHARED_LIBRARY_SUFFIX}")
 		install(FILES "${CMAKE_SYSROOT}/usr/lib/${ANDROID_SYSROOT_LIB_SUBDIR}/${stlLibName}"
 			DESTINATION ${LIB_DIR}
 		)
 	endif()
-
-	# zip internal assets - 'config' and 'Mods' dirs, save md5 of the zip
-	install(CODE "
-		cmake_path(ABSOLUTE_PATH CMAKE_INSTALL_PREFIX
-			OUTPUT_VARIABLE absolute_install_prefix
-		)
-		set(absolute_data_dir \"\${absolute_install_prefix}/${DATA_DIR}\")
-		file(MAKE_DIRECTORY \"\${absolute_data_dir}\")
-
-		set(internal_data_zip \"\${absolute_data_dir}/internalData.zip\")
-		execute_process(COMMAND
-			\"${CMAKE_COMMAND}\" -E tar c \"\${internal_data_zip}\" --format=zip -- config Mods
-			WORKING_DIRECTORY \"${CMAKE_SOURCE_DIR}\"
-		)
-
-		file(MD5 \"\${internal_data_zip}\" internal_data_zip_md5)
-		file(WRITE \"\${absolute_data_dir}/internalDataHash.txt\"
-			\${internal_data_zip_md5}
-		)
-	")
 else()
 	install(DIRECTORY config DESTINATION ${DATA_DIR})
 	if (ENABLE_CLIENT OR ENABLE_SERVER)

+ 15 - 1
CMakePresets.json

@@ -52,7 +52,7 @@
             "hidden": true,
             "cacheVariables": {
                 "ENABLE_LOBBY": "ON",
-				"ENABLE_TEST": "ON",
+                "ENABLE_TEST": "ON",
                 "ENABLE_LUA": "ON"
             }
         },
@@ -294,6 +294,15 @@
             "cacheVariables": {
                 "CMAKE_BUILD_TYPE": "RelWithDebInfo"
             }
+        },
+        {
+            "name": "android-daily-release",
+            "displayName": "Android daily release",
+            "description": "VCMI Android daily build",
+            "inherits": "android-conan-ninja-release",
+            "cacheVariables": {
+                "ANDROID_GRADLE_PROPERTIES": "applicationIdSuffix=.daily;signingConfig=dailySigning;applicationLabel=VCMI daily"
+            }
         }
     ],
     "buildPresets": [
@@ -412,6 +421,11 @@
             "name": "android-conan-ninja-release",
             "configurePreset": "android-conan-ninja-release",
             "inherits": "default-release"
+        },
+        {
+            "name": "android-daily-release",
+            "configurePreset": "android-daily-release",
+            "inherits": "android-conan-ninja-release"
         }
     ],
     "testPresets": [

+ 3 - 1
android/.gitignore

@@ -1,9 +1,11 @@
 *.iml
 .gradle
-/local.properties
 .DS_Store
 /build
 /captures
 .externalNativeBuild
 .cxx
 local.properties
+
+# generated by CMake build
+/vcmi-app/gradle.properties

+ 98 - 0
android/AndroidManifest.xml

@@ -0,0 +1,98 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="eu.vcmi.vcmi">
+
+    <!-- %%INSERT_PERMISSIONS -->
+    <uses-permission android:name="android.permission.VIBRATE" />
+
+    <!-- The following comment will be replaced upon deployment with default permissions based on the dependencies of the application.
+         Remove the comment if you do not require these default permissions. -->
+    <!-- %%INSERT_PERMISSIONS_DISABLED -->
+
+    <!-- The following comment will be replaced upon deployment with default features based on the dependencies of the application.
+         Remove the comment if you do not require these default features. -->
+    <!-- %%INSERT_FEATURES -->
+
+	<supports-screens
+        android:largeScreens="true"
+        android:xlargeScreens="true" />
+    <application
+        android:name="org.qtproject.qt5.android.bindings.QtApplication"
+        android:hardwareAccelerated="true"
+        android:hasFragileUserData="true"
+        android:allowBackup="false"
+        android:installLocation="auto"
+        android:icon="@mipmap/ic_launcher"
+        android:label="${applicationLabel}"
+        android:testOnly="false"
+        android:supportsRtl="true"
+        android:usesCleartextTraffic="false">
+		<activity
+			android:name=".ActivityLauncher"
+			android:configChanges="orientation|uiMode|screenLayout|screenSize|smallestScreenSize|layoutDirection|locale|fontScale|keyboard|keyboardHidden|navigation|mcc|mnc|density"
+			android:exported="true"
+            android:screenOrientation="sensorLandscape">
+			<intent-filter>
+				<action android:name="android.intent.action.MAIN"/>
+				<category android:name="android.intent.category.LAUNCHER"/>
+			</intent-filter>
+
+			<meta-data android:name="android.app.lib_name" android:value="-- %%INSERT_APP_LIB_NAME%% --"/>
+			<meta-data android:name="android.app.qt_sources_resource_id" android:resource="@array/qt_sources"/>
+			<meta-data android:name="android.app.repository" android:value="default"/>
+			<meta-data android:name="android.app.qt_libs_resource_id" android:resource="@array/qt_libs"/>
+			<meta-data android:name="android.app.bundled_libs_resource_id" android:resource="@array/bundled_libs"/>
+			<!-- Deploy Qt libs as part of package -->
+			<meta-data android:name="android.app.bundle_local_qt_libs" android:value="-- %%BUNDLE_LOCAL_QT_LIBS%% --"/>
+			<!-- Run with local libs -->
+			<meta-data android:name="android.app.use_local_qt_libs" android:value="-- %%USE_LOCAL_QT_LIBS%% --"/>
+			<meta-data android:name="android.app.libs_prefix" android:value="/data/local/tmp/qt/"/>
+			<meta-data android:name="android.app.load_local_libs_resource_id" android:resource="@array/load_local_libs"/>
+			<meta-data android:name="android.app.load_local_jars" android:value="-- %%INSERT_LOCAL_JARS%% --"/>
+			<meta-data android:name="android.app.static_init_classes" android:value="-- %%INSERT_INIT_CLASSES%% --"/>
+
+			<!--  Messages maps -->
+			<meta-data android:value="@string/ministro_not_found_msg" android:name="android.app.ministro_not_found_msg"/>
+			<meta-data android:value="@string/ministro_needed_msg" android:name="android.app.ministro_needed_msg"/>
+			<meta-data android:value="@string/fatal_error_msg" android:name="android.app.fatal_error_msg"/>
+			<meta-data android:value="@string/unsupported_android_version" android:name="android.app.unsupported_android_version"/>
+			<!--  Messages maps -->
+
+			<!-- Background running -->
+			<!-- Warning: changing this value to true may cause unexpected crashes if the
+						  application still try to draw after
+						  "applicationStateChanged(Qt::ApplicationSuspended)"
+						  signal is sent! -->
+			<meta-data android:name="android.app.background_running" android:value="false"/>
+			<!-- Background running -->
+
+			<!-- auto screen scale factor -->
+			<meta-data android:name="android.app.auto_screen_scale_factor" android:value="false"/>
+			<!-- auto screen scale factor -->
+
+			<!-- extract android style -->
+			<!-- available android:values :
+				* default - In most cases this will be the same as "full", but it can also be something else if needed, e.g., for compatibility reasons
+				* full - useful QWidget & Quick Controls 1 apps
+				* minimal - useful for Quick Controls 2 apps, it is much faster than "full"
+				* none - useful for apps that don't use any of the above Qt modules
+				-->
+			<meta-data android:name="android.app.extract_android_style" android:value="none"/>
+			<!-- extract android style -->
+		</activity>
+
+        <activity
+            android:name=".VcmiSDLActivity"
+            android:configChanges="keyboardHidden|orientation|screenSize"
+            android:label="@string/app_name"
+            android:launchMode="singleTop"
+            android:screenOrientation="sensorLandscape" />
+
+        <service
+            android:name=".ServerService"
+            android:process="eu.vcmi.vcmi.srv"
+            android:description="@string/server_name"
+            android:exported="false"/>
+    </application>
+
+</manifest>

+ 0 - 9
android/GeneratedVersion.java.in

@@ -1,9 +0,0 @@
-package eu.vcmi.vcmi.util;
-
-/**
- * Generated via cmake
- */
-public class GeneratedVersion
-{
-    public static final String VCMI_VERSION = "@VCMI_VERSION@";
-}

+ 17 - 0
android/androiddeployqt.json.in

@@ -0,0 +1,17 @@
+{
+	"android-min-sdk-version": "@CMAKE_ANDROID_API@",
+	"android-package-source-directory": "@androidPackageSourceDir@",
+	"android-target-sdk-version": "@ANDROID_TARGET_SDK_VERSION@",
+	"application-binary": "vcmiclient",
+	"architectures": {
+		"@ANDROID_ABI@": "@ANDROID_SYSROOT_LIB_SUBDIR@"
+	},
+	"ndk": "@CMAKE_ANDROID_NDK@",
+	"ndk-host": "@ANDROID_HOST_TAG@",
+	"qt": "@qtDir@",
+	"sdk": "@androidSdkDir@",
+	"sdkBuildToolsRevision": "31.0.0",
+	"stdcpp-path": "@ANDROID_TOOLCHAIN_ROOT@/sysroot/usr/lib/",
+	"tool-prefix": "llvm",
+	"toolchain-prefix": "llvm"
+}

+ 0 - 1
android/defs.gradle

@@ -1,4 +1,3 @@
 ext {
 	// these values will be retrieved during gradle build
-	gitInfoVcmi = "none"
 }

+ 6 - 1
android/gradle.properties

@@ -7,13 +7,18 @@
 # Specifies the JVM arguments used for the daemon process.
 # The setting is particularly useful for tweaking memory settings.
 org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
+
 # When configured, Gradle will run in incubating parallel mode.
 # This option should only be used with decoupled projects. More details, visit
 # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
-# org.gradle.parallel=true
+org.gradle.parallel=true
+
 # AndroidX package structure to make it clearer which packages are bundled with the
 # Android operating system, and which are packaged with your app"s APK
 # https://developer.android.com/topic/libraries/support-library/androidx-rn
 android.useAndroidX=true
+
 # Automatically convert third-party libraries to use AndroidX
 android.enableJetifier=true
+
+# Qt-generated properties

+ 0 - 8
android/vcmi-app/.gitignore

@@ -1,8 +0,0 @@
-/build
-
-# generated by CMake build
-/src/main/assets/internalData.zip
-/src/main/assets/internalDataHash.txt
-/src/main/java/eu/vcmi/vcmi/util/GeneratedVersion.java
-/src/main/jniLibs
-/src/main/res/raw/authors.txt

+ 48 - 62
android/vcmi-app/build.gradle

@@ -1,20 +1,49 @@
-plugins {
-	id 'com.android.application'
-}
+apply plugin: 'com.android.application'
 
 android {
-	compileSdk 33
+	/*******************************************************
+	 * The following variables:
+	 * - androidBuildToolsVersion,
+	 * - androidCompileSdkVersion
+	 * - qt5AndroidDir - holds the path to qt android files
+	 *                   needed to build any Qt application
+	 *                   on Android.
+	 *
+	 * are defined in gradle.properties file. This file is
+	 * updated by QtCreator and androiddeployqt tools.
+	 * Changing them manually might break the compilation!
+	 *******************************************************/
+
 	ndkVersion '25.2.9519653'
 
+	// Extract native libraries from the APK
+	packagingOptions.jniLibs.useLegacyPackaging true
+
 	defaultConfig {
 		applicationId "is.xyz.vcmi"
-		minSdk 19
-		targetSdk 33
-		versionCode 1522
-		versionName "1.5.2"
+
+		compileSdk = androidCompileSdkVersion.takeAfter("-") as Integer // has "android-" prepended
+		minSdk = qtMinSdkVersion as Integer
+		targetSdk = qtTargetSdkVersion as Integer // ANDROID_TARGET_SDK_VERSION in the CMake project
+
+		versionCode 1530
+		versionName "1.5.3"
+
 		setProperty("archivesBaseName", "vcmi")
 	}
 
+	sourceSets {
+		main {
+			// Qt requires these to be in the android project root
+			manifest.srcFile '../AndroidManifest.xml'
+			jniLibs.srcDirs = ['../libs']
+
+			java.srcDirs = [qt5AndroidDir + '/src', 'src', 'java']
+			aidl.srcDirs = [qt5AndroidDir + '/src', 'src', 'aidl']
+			res.srcDirs = [qt5AndroidDir + '/res', 'src/main/res', '../res']
+		}
+	}
+
 	signingConfigs {
 		releaseSigning
 		dailySigning
@@ -36,27 +65,18 @@ android {
 		release {
 			minifyEnabled false
 			zipAlignEnabled true
-			signingConfig signingConfigs.releaseSigning
+			applicationIdSuffix = project.findProperty('applicationIdSuffix')
+			signingConfig = signingConfigs[project.findProperty('signingConfig') ?: 'releaseSigning']
 			proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
 			manifestPlaceholders = [
-				applicationLabel: '@string/app_name',
+				applicationLabel: project.findProperty('applicationLabel') ?: 'VCMI',
 			]
 			ndk {
 				debugSymbolLevel 'full'
 			}
 		}
-		daily {
-			initWith release
-			applicationIdSuffix '.daily'
-			signingConfig signingConfigs.dailySigning
-			manifestPlaceholders = [
-				applicationLabel: 'VCMI daily',
-			]
-		}
 	}
 
-	applicationVariants.all { variant -> RenameOutput(project.archivesBaseName, variant) }
-
 	tasks.withType(JavaCompile) {
 		options.compilerArgs += ["-Xlint:deprecation"]
 	}
@@ -66,30 +86,9 @@ android {
 		targetCompatibility JavaVersion.VERSION_1_8
 	}
 
-	buildFeatures {
-		viewBinding true
-		dataBinding true
-	}
-}
-
-def RenameOutput(final baseName, final variant) {
-	final def buildTaskId = System.getenv("GITHUB_RUN_ID")
-
-	ResolveGitInfo()
-
-	def name = baseName + "-" + ext.gitInfoVcmi
-
-	if (buildTaskId != null && !buildTaskId.isEmpty()) {
-		name = buildTaskId + "-" + name
-	}
-
-	if (!variant.buildType.name != "release") {
-		name += "-" + variant.buildType.name
-	}
-
-	variant.outputs.each { output ->
-		def oldPath = output.outputFile.getAbsolutePath()
-		output.outputFileName = name + oldPath.substring(oldPath.lastIndexOf("."))
+	// Do not compress Qt binary resources file
+	aaptOptions {
+		noCompress 'rcc'
 	}
 }
 
@@ -111,16 +110,6 @@ def CommandOutput(final cmd, final arguments, final cwd) {
 	}
 }
 
-def ResolveGitInfo() {
-	if (ext.gitInfoVcmi != "none") {
-		return
-	}
-	ext.gitInfoVcmi =
-		CommandOutput("git", ["log", "-1", "--pretty=%D", "--decorate-refs=refs/remotes/origin/*"], ".").replace("origin/", "").replace(", HEAD", "").replaceAll("[^a-zA-Z0-9\\-_]", "_") +
-		"-" +
-		CommandOutput("git", ["describe", "--match=", "--always", "--abbrev=7"], ".")
-}
-
 def SigningPropertiesPath(final basePath, final signingConfigKey) {
 	return file("${basePath}/${signingConfigKey}.properties")
 }
@@ -130,9 +119,8 @@ def SigningKeystorePath(final basePath, final keystoreFileName) {
 }
 
 def LoadSigningConfig(final signingConfigKey) {
-	final def projectRoot = "${project.projectDir}/../../CI/android"
 	final def props = new Properties()
-	final def propFile = SigningPropertiesPath(projectRoot, signingConfigKey)
+	final def propFile = SigningPropertiesPath(signingRoot, signingConfigKey)
 	
 	def signingConfig = android.signingConfigs.getAt(signingConfigKey)
 	
@@ -143,7 +131,7 @@ def LoadSigningConfig(final signingConfigKey) {
 			&& props.containsKey('STORE_FILE')
 			&& props.containsKey('KEY_ALIAS')) {
 
-			signingConfig.storeFile = SigningKeystorePath(projectRoot, props['STORE_FILE'])
+			signingConfig.storeFile = SigningKeystorePath(signingRoot, props['STORE_FILE'])
 			signingConfig.storePassword = props['STORE_PASSWORD']
 			signingConfig.keyAlias = props['KEY_ALIAS']
 			
@@ -167,9 +155,7 @@ def LoadSigningConfig(final signingConfigKey) {
 }
 
 dependencies {
-	implementation 'androidx.appcompat:appcompat:1.2.0'
-	implementation 'com.google.android.material:material:1.3.0'
-	implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
-	implementation 'com.google.android.gms:play-services-base:18.2.0'
-	implementation 'com.google.android.gms:play-services-basement:18.1.0'
+	implementation fileTree(dir: '../libs', include: ['*.jar', '*.aar'])
+	implementation 'androidx.annotation:annotation:1.7.1'
+	implementation 'androidx.documentfile:documentfile:1.0.1'
 }

+ 0 - 54
android/vcmi-app/src/main/AndroidManifest.xml

@@ -1,54 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="eu.vcmi.vcmi">
-
-    <uses-permission android:name="android.permission.INTERNET" />
-    <uses-permission android:name="android.permission.VIBRATE" />
-
-    <application
-        android:extractNativeLibs="true"
-        android:hardwareAccelerated="true"
-        android:hasFragileUserData="true"
-        android:allowBackup="false"
-        android:installLocation="auto"
-        android:icon="@mipmap/ic_launcher"
-        android:label="${applicationLabel}"
-        android:testOnly="false"
-        android:supportsRtl="true"
-        android:theme="@style/Theme.VCMI">
-        <activity
-            android:exported="true"
-            android:name=".ActivityLauncher"
-            android:screenOrientation="sensorLandscape">
-            <intent-filter>
-                <action android:name="android.intent.action.MAIN" />
-
-                <category android:name="android.intent.category.LAUNCHER" />
-            </intent-filter>
-        </activity>
-        <activity
-            android:name=".ActivityError"
-            android:screenOrientation="sensorLandscape" />
-        <activity
-            android:name=".ActivityMods"
-            android:screenOrientation="sensorLandscape" />
-        <activity
-            android:name=".ActivityAbout"
-            android:screenOrientation="sensorLandscape" />
-
-        <activity
-            android:name=".VcmiSDLActivity"
-            android:configChanges="keyboardHidden|orientation|screenSize"
-            android:label="@string/app_name"
-            android:launchMode="singleTop"
-            android:screenOrientation="sensorLandscape"
-            android:theme="@style/Theme.VCMI.Full" />
-
-        <service
-            android:name=".ServerService"
-            android:process="eu.vcmi.vcmi.srv"
-            android:description="@string/server_name"
-            android:exported="false"/>
-    </application>
-
-</manifest>

+ 0 - 94
android/vcmi-app/src/main/java/eu/vcmi/vcmi/ActivityAbout.java

@@ -1,94 +0,0 @@
-package eu.vcmi.vcmi;
-
-import android.content.ActivityNotFoundException;
-import android.content.Intent;
-import android.net.Uri;
-import android.os.Bundle;
-import androidx.annotation.Nullable;
-import androidx.core.content.ContextCompat;
-import android.text.SpannableString;
-import android.text.Spanned;
-import android.text.style.ForegroundColorSpan;
-import android.text.style.UnderlineSpan;
-import android.view.View;
-import android.widget.TextView;
-import android.widget.Toast;
-
-import eu.vcmi.vcmi.content.DialogAuthors;
-import eu.vcmi.vcmi.util.GeneratedVersion;
-import eu.vcmi.vcmi.util.Utils;
-
-/**
- * @author F
- */
-public class ActivityAbout extends ActivityWithToolbar
-{
-    private static final String DIALOG_AUTHORS_TAG = "DIALOG_AUTHORS_TAG";
-
-    @Override
-    protected void onCreate(@Nullable final Bundle savedInstanceState)
-    {
-        super.onCreate(savedInstanceState);
-        setContentView(R.layout.activity_about);
-        initToolbar(R.string.about_title);
-
-        initControl(R.id.about_version_app, getString(R.string.about_version_app, GeneratedVersion.VCMI_VERSION));
-        initControl(R.id.about_version_launcher, getString(R.string.about_version_launcher, Utils.appVersionName(this)));
-        initControlUrl(R.id.about_link_portal, R.string.about_links_main, R.string.url_project_page, this::onUrlPressed);
-        initControlUrl(R.id.about_link_repo_main, R.string.about_links_repo, R.string.url_project_repo, this::onUrlPressed);
-        initControlUrl(R.id.about_link_repo_launcher, R.string.about_links_repo_launcher, R.string.url_launcher_repo, this::onUrlPressed);
-        initControlBtn(R.id.about_btn_authors, this::onBtnAuthorsPressed);
-        initControlUrl(R.id.about_btn_privacy, R.string.about_btn_privacy, R.string.url_launcher_privacy, this::onUrlPressed);
-    }
-
-    private void initControlBtn(final int viewId, final View.OnClickListener callback)
-    {
-        findViewById(viewId).setOnClickListener(callback);
-    }
-
-    private void initControlUrl(final int textViewResId, final int baseTextRes, final int urlTextRes, final IInternalUrlCallback callback)
-    {
-        final TextView ctrl = (TextView) findViewById(textViewResId);
-        final String urlText = getString(urlTextRes);
-        final String fullText = getString(baseTextRes, urlText);
-
-        ctrl.setText(decoratedLinkText(fullText, fullText.indexOf(urlText), fullText.length()));
-        ctrl.setOnClickListener(v -> callback.onPressed(urlText));
-    }
-
-    private Spanned decoratedLinkText(final String rawText, final int start, final int end)
-    {
-        final SpannableString spannableString = new SpannableString(rawText);
-        spannableString.setSpan(new UnderlineSpan(), start, end, 0);
-        spannableString.setSpan(new ForegroundColorSpan(ContextCompat.getColor(this, R.color.accent)), start, end, 0);
-        return spannableString;
-    }
-
-    private void initControl(final int textViewResId, final String text)
-    {
-        ((TextView) findViewById(textViewResId)).setText(text);
-    }
-
-    private void onBtnAuthorsPressed(final View v)
-    {
-        final DialogAuthors dialogAuthors = new DialogAuthors();
-        dialogAuthors.show(getSupportFragmentManager(), DIALOG_AUTHORS_TAG);
-    }
-
-    private void onUrlPressed(final String url)
-    {
-        try
-        {
-            startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(url)));
-        }
-        catch (final ActivityNotFoundException ignored)
-        {
-            Toast.makeText(this, R.string.about_error_opening_url, Toast.LENGTH_LONG).show();
-        }
-    }
-
-    private interface IInternalUrlCallback
-    {
-        void onPressed(final String link);
-    }
-}

+ 0 - 58
android/vcmi-app/src/main/java/eu/vcmi/vcmi/ActivityBase.java

@@ -1,58 +0,0 @@
-package eu.vcmi.vcmi;
-
-import android.os.Bundle;
-import androidx.annotation.Nullable;
-import androidx.appcompat.app.AppCompatActivity;
-
-import eu.vcmi.vcmi.util.Log;
-import eu.vcmi.vcmi.util.SharedPrefs;
-
-/**
- * @author F
- */
-public abstract class ActivityBase extends AppCompatActivity
-{
-    protected SharedPrefs mPrefs;
-
-    @Override
-    protected void onCreate(@Nullable final Bundle savedInstanceState)
-    {
-        super.onCreate(savedInstanceState);
-        setupExceptionHandler();
-        mPrefs = new SharedPrefs(this);
-    }
-
-    private void setupExceptionHandler()
-    {
-        final Thread.UncaughtExceptionHandler prevHandler = Thread.getDefaultUncaughtExceptionHandler();
-        if (prevHandler != null && !(prevHandler instanceof VCMIExceptionHandler)) // no need to recreate it if it's already setup
-        {
-            Thread.setDefaultUncaughtExceptionHandler(new VCMIExceptionHandler(prevHandler));
-        }
-    }
-
-    private static class VCMIExceptionHandler implements Thread.UncaughtExceptionHandler
-    {
-        private Thread.UncaughtExceptionHandler mPrevHandler;
-
-        private VCMIExceptionHandler(final Thread.UncaughtExceptionHandler prevHandler)
-        {
-            mPrevHandler = prevHandler;
-        }
-
-        @Override
-        public void uncaughtException(final Thread thread, final Throwable throwable)
-        {
-            Log.e(this, "Unhandled exception", throwable); // to save the exception to file before crashing
-
-            if (mPrevHandler != null && !(mPrevHandler instanceof VCMIExceptionHandler))
-            {
-                mPrevHandler.uncaughtException(thread, throwable);
-            }
-            else
-            {
-                System.exit(1);
-            }
-        }
-    }
-}

+ 0 - 48
android/vcmi-app/src/main/java/eu/vcmi/vcmi/ActivityError.java

@@ -1,48 +0,0 @@
-package eu.vcmi.vcmi;
-
-import android.content.Intent;
-import android.os.Bundle;
-import androidx.annotation.Nullable;
-import android.view.View;
-import android.widget.TextView;
-
-/**
- * @author F
- */
-public class ActivityError extends ActivityWithToolbar
-{
-    public static final String ARG_ERROR_MSG = "ActivityError.msg";
-
-    @Override
-    protected void onCreate(@Nullable final Bundle savedInstanceState)
-    {
-        super.onCreate(savedInstanceState);
-        setContentView(R.layout.activity_error);
-        initToolbar(R.string.launcher_title);
-
-        final View btnTryAgain = findViewById(R.id.error_btn_try_again);
-        btnTryAgain.setOnClickListener(new OnErrorRetryPressed());
-
-        final Bundle extras = getIntent().getExtras();
-        if (extras != null)
-        {
-            final String errorMessage = extras.getString(ARG_ERROR_MSG);
-            final TextView errorMessageView = (TextView) findViewById(R.id.error_message);
-            if (errorMessage != null)
-            {
-                errorMessageView.setText(errorMessage);
-            }
-        }
-    }
-
-    private class OnErrorRetryPressed implements View.OnClickListener
-    {
-        @Override
-        public void onClick(final View v)
-        {
-            // basically restarts main activity
-            startActivity(new Intent(ActivityError.this, ActivityLauncher.class));
-            finish();
-        }
-    }
-}

+ 25 - 270
android/vcmi-app/src/main/java/eu/vcmi/vcmi/ActivityLauncher.java

@@ -1,307 +1,62 @@
 package eu.vcmi.vcmi;
 
-import android.Manifest;
 import android.app.Activity;
 import android.content.Intent;
 import android.net.Uri;
-import android.os.Build;
 import android.os.Bundle;
-import android.view.Menu;
-import android.view.MenuItem;
-import android.view.View;
-import android.widget.TextView;
-import android.widget.Toast;
-import org.json.JSONObject;
+import android.os.Environment;
+import android.provider.DocumentsContract;
+
+import androidx.annotation.Nullable;
+
 import java.io.File;
-import java.util.ArrayList;
-import java.util.List;
 
-import eu.vcmi.vcmi.content.AsyncLauncherInitialization;
-import eu.vcmi.vcmi.settings.AdventureAiController;
-import eu.vcmi.vcmi.settings.LanguageSettingController;
-import eu.vcmi.vcmi.settings.CopyDataController;
-import eu.vcmi.vcmi.settings.ExportDataController;
-import eu.vcmi.vcmi.settings.LauncherSettingController;
-import eu.vcmi.vcmi.settings.ModsBtnController;
-import eu.vcmi.vcmi.settings.MusicSettingController;
-import eu.vcmi.vcmi.settings.PointerModeSettingController;
-import eu.vcmi.vcmi.settings.PointerMultiplierSettingController;
-import eu.vcmi.vcmi.settings.ScreenScaleSettingController;
-import eu.vcmi.vcmi.settings.ScreenScaleSettingDialog;
-import eu.vcmi.vcmi.settings.SoundSettingController;
-import eu.vcmi.vcmi.settings.StartGameController;
+import eu.vcmi.vcmi.VcmiSDLActivity;
 import eu.vcmi.vcmi.util.FileUtil;
-import eu.vcmi.vcmi.util.Log;
-import eu.vcmi.vcmi.util.SharedPrefs;
+
+import org.libsdl.app.SDL;
 
 /**
  * @author F
  */
-public class ActivityLauncher extends ActivityWithToolbar
+public class ActivityLauncher extends org.qtproject.qt5.android.bindings.QtActivity
 {
-    public static final int PERMISSIONS_REQ_CODE = 123;
-
-    private final List<LauncherSettingController<?, ?>> mActualSettings = new ArrayList<>();
-    private View mProgress;
-    private TextView mErrorMessage;
-    private Config mConfig;
-    private LauncherSettingController<String, Config> mCtrlLanguage;
-    private LauncherSettingController<PointerModeSettingController.PointerMode, Config> mCtrlPointerMode;
-    private LauncherSettingController<Void, Void> mCtrlStart;
-    private LauncherSettingController<Float, Config> mCtrlPointerMulti;
-    private LauncherSettingController<ScreenScaleSettingController.ScreenScale, Config> mCtrlScreenScale;
-    private LauncherSettingController<Integer, Config> mCtrlSoundVol;
-    private LauncherSettingController<Integer, Config> mCtrlMusicVol;
-    private LauncherSettingController<String, Config> mAiController;
-    private CopyDataController mCtrlCopy;
-    private ExportDataController mCtrlExport;
-
-    private final AsyncLauncherInitialization.ILauncherCallbacks mInitCallbacks = new AsyncLauncherInitialization.ILauncherCallbacks()
-    {
-        @Override
-        public Activity ctx()
-        {
-            return ActivityLauncher.this;
-        }
-
-        @Override
-        public SharedPrefs prefs()
-        {
-            return mPrefs;
-        }
-
-        @Override
-        public void onInitSuccess()
-        {
-            loadConfigFile();
-            mCtrlStart.show();
-            mCtrlCopy.show();
-            mCtrlExport.show();
-            for (LauncherSettingController<?, ?> setting: mActualSettings) {
-                setting.show();
-            }
-            mErrorMessage.setVisibility(View.GONE);
-            mProgress.setVisibility(View.GONE);
-        }
+    private static final int PICK_EXTERNAL_VCMI_DATA_TO_COPY = 1;
 
-        @Override
-        public void onInitFailure(final AsyncLauncherInitialization.InitResult result)
-        {
-            mCtrlCopy.show();
-            if (result.mFailSilently)
-            {
-                return;
-            }
-            ActivityLauncher.this.onInitFailure(result);
-        }
-    };
+    public boolean justLaunched = true;
 
     @Override
-    public void onCreate(final Bundle savedInstanceState)
+    public void onCreate(@Nullable final Bundle savedInstanceState)
     {
         super.onCreate(savedInstanceState);
-
-        if (savedInstanceState == null) // only clear the log if this is initial onCreate and not config change
-        {
-            Log.init();
-        }
-
-        Log.i(this, "Starting launcher");
-
-        setContentView(R.layout.activity_launcher);
-        initToolbar(R.string.launcher_title, true);
-
-        mProgress = findViewById(R.id.launcher_progress);
-        mErrorMessage = (TextView) findViewById(R.id.launcher_error);
-        mErrorMessage.setVisibility(View.GONE);
-
-        ((TextView) findViewById(R.id.launcher_version_info)).setText(getString(R.string.launcher_version, BuildConfig.VERSION_NAME));
-
-        initSettingsGui();
+        justLaunched = savedInstanceState == null;
+        SDL.setContext(this);
     }
 
     @Override
-    public void onStart()
+    protected void onActivityResult(int requestCode, int resultCode, Intent resultData)
     {
-        super.onStart();
-        new AsyncLauncherInitialization(mInitCallbacks).execute((Void) null);
-    }
-
-    @Override
-    public void onBackPressed()
-    {
-        saveConfig();
-        super.onBackPressed();
-    }
-
-    @Override
-    public boolean onCreateOptionsMenu(final Menu menu)
-    {
-        getMenuInflater().inflate(R.menu.menu_launcher, menu);
-        return true;
-    }
-
-    @Override
-    public boolean onOptionsItemSelected(final MenuItem item)
-    {
-        if (item.getItemId() == R.id.menu_launcher_about)
-        {
-            startActivity(new Intent(this, ActivityAbout.class));
-            return true;
-        }
-        return super.onOptionsItemSelected(item);
-    }
-
-    @Override
-    public void onActivityResult(int requestCode, int resultCode, Intent resultData)
-    {
-        if(requestCode == CopyDataController.PICK_EXTERNAL_VCMI_DATA_TO_COPY
-            && resultCode == Activity.RESULT_OK)
-        {
-            Uri uri;
-
-            if (resultData != null)
-            {
-                uri = resultData.getData();
-
-                mCtrlCopy.copyData(uri);
-            }
-
-            return;
-        }
-
-        if(requestCode == ExportDataController.PICK_DIRECTORY_TO_EXPORT
-                && resultCode == Activity.RESULT_OK)
+        if (requestCode == PICK_EXTERNAL_VCMI_DATA_TO_COPY && resultCode == Activity.RESULT_OK)
         {
-            Uri uri = null;
-            if (resultData != null)
-            {
-                uri = resultData.getData();
-
-                mCtrlExport.copyData(uri);
-            }
-
+            if (resultData != null && FileUtil.copyData(resultData.getData(), this))
+                NativeMethods.heroesDataUpdate();
             return;
         }
 
         super.onActivityResult(requestCode, resultCode, resultData);
     }
 
-    public void requestStoragePermissions()
-    {
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
-        {
-            requestPermissions(
-                    new String[] {Manifest.permission.WRITE_EXTERNAL_STORAGE},
-                    PERMISSIONS_REQ_CODE);
-        }
-    }
-
-    private void initSettingsGui()
+    public void copyHeroesData()
     {
-        mCtrlStart = new StartGameController(this, v -> onLaunchGameBtnPressed()).init(R.id.launcher_btn_start);
-        (mCtrlCopy = new CopyDataController(this)).init(R.id.launcher_btn_copy);
-        (mCtrlExport = new ExportDataController(this)).init(R.id.launcher_btn_export);
-        new ModsBtnController(this, v -> startActivity(new Intent(ActivityLauncher.this, ActivityMods.class))).init(R.id.launcher_btn_mods);
-        mCtrlLanguage = new LanguageSettingController(this).init(R.id.launcher_btn_cp, mConfig);
-        mCtrlPointerMode = new PointerModeSettingController(this).init(R.id.launcher_btn_pointer_mode, mConfig);
-        mCtrlPointerMulti = new PointerMultiplierSettingController(this).init(R.id.launcher_btn_pointer_multi, mConfig);
-        mCtrlScreenScale = new ScreenScaleSettingController(this).init(R.id.launcher_btn_scale, mConfig);
-        mCtrlSoundVol = new SoundSettingController(this).init(R.id.launcher_btn_volume_sound, mConfig);
-        mCtrlMusicVol = new MusicSettingController(this).init(R.id.launcher_btn_volume_music, mConfig);
-        mAiController = new AdventureAiController(this).init(R.id.launcher_btn_adventure_ai, mConfig);
-
-        mActualSettings.clear();
-        mActualSettings.add(mCtrlLanguage);
-        mActualSettings.add(mCtrlPointerMode);
-        mActualSettings.add(mCtrlPointerMulti);
-        mActualSettings.add(mCtrlScreenScale);
-        mActualSettings.add(mCtrlSoundVol);
-        mActualSettings.add(mCtrlMusicVol);
-        mActualSettings.add(mAiController);
-
-        mCtrlStart.hide(); // start is initially hidden, until we confirm that everything is okay via AsyncLauncherInitialization
-        mCtrlCopy.hide();
-        mCtrlExport.hide();
+        Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
+        intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI,
+            Uri.fromFile(new File(Environment.getExternalStorageDirectory(), "vcmi-data"))
+        );
+        startActivityForResult(intent, PICK_EXTERNAL_VCMI_DATA_TO_COPY);
     }
 
-    private void onLaunchGameBtnPressed()
+    public void onLaunchGameBtnPressed()
     {
-        saveConfig();
         startActivity(new Intent(ActivityLauncher.this, VcmiSDLActivity.class));
     }
-
-    private void saveConfig()
-    {
-        if (mConfig == null)
-        {
-            return;
-        }
-
-        try
-        {
-            mConfig.save(new File(FileUtil.configFileLocation(Storage.getVcmiDataDir(this))));
-        }
-        catch (final Exception e)
-        {
-            Toast.makeText(this, getString(R.string.launcher_error_config_saving_failed, e.getMessage()), Toast.LENGTH_LONG).show();
-        }
-    }
-
-    private void loadConfigFile()
-    {
-        try
-        {
-            final String settingsFileContent = FileUtil.read(
-                    new File(FileUtil.configFileLocation(Storage.getVcmiDataDir(this))));
-
-            mConfig = Config.load(new JSONObject(settingsFileContent));
-        }
-        catch (final Exception e)
-        {
-            Log.e(this, "Could not load config file", e);
-            mConfig = new Config();
-        }
-        onConfigUpdated();
-    }
-
-    private void onConfigUpdated()
-    {
-        if(mConfig.mScreenScale == -1)
-            mConfig.updateScreenScale(ScreenScaleSettingDialog.getSupportedScalingRange(ActivityLauncher.this)[1]);
-
-        updateCtrlConfig(mCtrlLanguage, mConfig);
-        updateCtrlConfig(mCtrlPointerMode, mConfig);
-        updateCtrlConfig(mCtrlPointerMulti, mConfig);
-        updateCtrlConfig(mCtrlScreenScale, mConfig);
-        updateCtrlConfig(mCtrlSoundVol, mConfig);
-        updateCtrlConfig(mCtrlMusicVol, mConfig);
-        updateCtrlConfig(mAiController, mConfig);
-    }
-
-    private <TSetting, TConf> void updateCtrlConfig(
-            final LauncherSettingController<TSetting, TConf> ctrl,
-            final TConf config)
-    {
-        if (ctrl != null)
-        {
-            ctrl.updateConfig(config);
-        }
-    }
-
-    private void onInitFailure(final AsyncLauncherInitialization.InitResult initResult)
-    {
-        Log.d(this, "Init failed with " + initResult);
-
-        mProgress.setVisibility(View.GONE);
-        mCtrlStart.hide();
-
-        for (LauncherSettingController<?, ?> setting: mActualSettings)
-        {
-            setting.hide();
-        }
-
-        mErrorMessage.setVisibility(View.VISIBLE);
-        mErrorMessage.setText(initResult.mMessage);
-    }
 }

+ 0 - 351
android/vcmi-app/src/main/java/eu/vcmi/vcmi/ActivityMods.java

@@ -1,351 +0,0 @@
-package eu.vcmi.vcmi;
-
-import android.content.DialogInterface;
-import android.os.AsyncTask;
-import android.os.Bundle;
-
-import androidx.annotation.Nullable;
-import com.google.android.material.snackbar.Snackbar;
-
-import androidx.appcompat.app.AlertDialog;
-import androidx.recyclerview.widget.DefaultItemAnimator;
-import androidx.recyclerview.widget.DividerItemDecoration;
-import androidx.recyclerview.widget.LinearLayoutManager;
-import androidx.recyclerview.widget.RecyclerView;
-
-import android.view.Menu;
-import android.view.MenuInflater;
-import android.view.MenuItem;
-import android.view.View;
-import android.widget.TextView;
-
-import com.google.android.gms.common.GooglePlayServicesNotAvailableException;
-import com.google.android.gms.common.GooglePlayServicesRepairableException;
-import com.google.android.gms.common.GooglePlayServicesUtil;
-import com.google.android.gms.security.ProviderInstaller;
-
-import org.json.JSONException;
-import org.json.JSONObject;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.Locale;
-
-import eu.vcmi.vcmi.content.ModBaseViewHolder;
-import eu.vcmi.vcmi.content.ModsAdapter;
-import eu.vcmi.vcmi.mods.VCMIMod;
-import eu.vcmi.vcmi.mods.VCMIModContainer;
-import eu.vcmi.vcmi.mods.VCMIModsRepo;
-import eu.vcmi.vcmi.util.InstallModAsync;
-import eu.vcmi.vcmi.util.FileUtil;
-import eu.vcmi.vcmi.util.Log;
-import eu.vcmi.vcmi.util.ServerResponse;
-
-/**
- * @author F
- */
-public class ActivityMods extends ActivityWithToolbar
-{
-    private static final boolean ENABLE_REPO_DOWNLOADING = true;
-    private static final String REPO_URL = "https://raw.githubusercontent.com/vcmi/vcmi-mods-repository/develop/vcmi-1.5.json";
-    private VCMIModsRepo mRepo;
-    private RecyclerView mRecycler;
-
-    private VCMIModContainer mModContainer;
-    private TextView mErrorMessage;
-    private View mProgress;
-    private ModsAdapter mModsAdapter;
-
-    @Override
-    protected void onCreate(@Nullable final Bundle savedInstanceState)
-    {
-        super.onCreate(savedInstanceState);
-        setContentView(R.layout.activity_mods);
-        initToolbar(R.string.mods_title);
-
-        mRepo = new VCMIModsRepo();
-
-        mProgress = findViewById(R.id.mods_progress);
-
-        mErrorMessage = (TextView) findViewById(R.id.mods_error_text);
-        mErrorMessage.setVisibility(View.GONE);
-
-        mRecycler = (RecyclerView) findViewById(R.id.mods_recycler);
-        mRecycler.setItemAnimator(new DefaultItemAnimator());
-        mRecycler.setLayoutManager(new LinearLayoutManager(this));
-        mRecycler.addItemDecoration(new DividerItemDecoration(this, DividerItemDecoration.VERTICAL));
-        mRecycler.setVisibility(View.GONE);
-
-        mModsAdapter = new ModsAdapter(new OnAdapterItemAction());
-        mRecycler.setAdapter(mModsAdapter);
-
-        new AsyncLoadLocalMods().execute((Void) null);
-
-        try {
-            ProviderInstaller.installIfNeeded(this);
-        } catch (GooglePlayServicesRepairableException e) {
-            GooglePlayServicesUtil.getErrorDialog(e.getConnectionStatusCode(), this, 0);
-        } catch (GooglePlayServicesNotAvailableException e) {
-            Log.e("SecurityException", "Google Play Services not available.");
-        }
-    }
-
-    private void loadLocalModData() throws IOException, JSONException
-    {
-        final File dataRoot = Storage.getVcmiDataDir(this);
-        final String internalDataRoot = getFilesDir() + "/" + Const.VCMI_DATA_ROOT_FOLDER_NAME;
-
-        final File modsRoot = new File(dataRoot,"/Mods");
-        final File internalModsRoot = new File(internalDataRoot + "/Mods");
-        if (!modsRoot.exists() && !internalModsRoot.exists())
-        {
-            Log.w(this, "We don't have mods folders");
-            return;
-        }
-        final File[] modsFiles = modsRoot.listFiles();
-        final File[] internalModsFiles = internalModsRoot.listFiles();
-        final List<File> topLevelModsFolders = new ArrayList<>();
-        if (modsFiles != null && modsFiles.length > 0)
-        {
-            Collections.addAll(topLevelModsFolders, modsFiles);
-        }
-        if (internalModsFiles != null && internalModsFiles.length > 0)
-        {
-            Collections.addAll(topLevelModsFolders, internalModsFiles);
-        }
-        mModContainer = VCMIModContainer.createContainer(topLevelModsFolders);
-
-        final File modConfigFile = new File(dataRoot, "config/modSettings.json");
-        if (!modConfigFile.exists())
-        {
-            Log.w(this, "We don't have mods config");
-            return;
-        }
-
-        JSONObject rootConfigObj = new JSONObject(FileUtil.read(modConfigFile));
-        JSONObject activeMods = rootConfigObj.getJSONObject("activeMods");
-        mModContainer.updateContainerFromConfigJson(activeMods, rootConfigObj.optJSONObject("core"));
-
-        Log.i(this, "Loaded mods: " + mModContainer);
-    }
-
-    @Override
-    public boolean onCreateOptionsMenu(final Menu menu)
-    {
-        final MenuInflater menuInflater = getMenuInflater();
-        menuInflater.inflate(R.menu.menu_mods, menu);
-        return super.onCreateOptionsMenu(menu);
-    }
-
-    @Override
-    public boolean onOptionsItemSelected(final MenuItem item)
-    {
-        if (item.getItemId() == R.id.menu_mods_download_repo)
-        {
-            Log.i(this, "Should download repo now...");
-            if (ENABLE_REPO_DOWNLOADING)
-            {
-                mProgress.setVisibility(View.VISIBLE);
-                mRepo.init(REPO_URL, new OnModsRepoInitialized()); // disabled because the json is broken anyway
-            }
-            else
-            {
-                Snackbar.make(findViewById(R.id.mods_data_root), "Loading repo is disabled for now, because .json can't be parsed anyway",
-                    Snackbar.LENGTH_LONG).show();
-            }
-        }
-        return super.onOptionsItemSelected(item);
-    }
-
-    private void handleNoData()
-    {
-        mProgress.setVisibility(View.GONE);
-        mRecycler.setVisibility(View.GONE);
-        mErrorMessage.setVisibility(View.VISIBLE);
-        mErrorMessage.setText("Could not load local mods list");
-    }
-
-    private void saveModSettingsToFile()
-    {
-        mModContainer.saveToFile(
-                new File(
-                        Storage.getVcmiDataDir(this),
-                        "config/modSettings.json"));
-    }
-
-    private class OnModsRepoInitialized implements VCMIModsRepo.IOnModsRepoDownloaded
-    {
-        @Override
-        public void onSuccess(ServerResponse<List<VCMIMod>> response)
-        {
-            Log.i(this, "Initialized mods repo");
-			if (mModContainer == null)
-			{
-				handleNoData();
-			}
-			else
-			{
-				mModContainer.updateFromRepo(response.mContent);
-				mModsAdapter.updateModsList(mModContainer.submods());
-				mProgress.setVisibility(View.GONE);
-			}
-        }
-
-        @Override
-        public void onError(final int code)
-        {
-            Log.i(this, "Mods repo error: " + code);
-        }
-    }
-
-    private class AsyncLoadLocalMods extends AsyncTask<Void, Void, Void>
-    {
-        @Override
-        protected void onPreExecute()
-        {
-            mProgress.setVisibility(View.VISIBLE);
-        }
-
-        @Override
-        protected Void doInBackground(final Void... params)
-        {
-            try
-            {
-                loadLocalModData();
-            }
-            catch (IOException e)
-            {
-                Log.e(this, "Loading local mod data failed", e);
-            }
-            catch (JSONException e)
-            {
-                Log.e(this, "Parsing local mod data failed", e);
-            }
-            return null;
-        }
-
-        @Override
-        protected void onPostExecute(final Void aVoid)
-        {
-            if (mModContainer == null || !mModContainer.hasSubmods())
-            {
-                handleNoData();
-            }
-            else
-            {
-                mProgress.setVisibility(View.GONE);
-                mRecycler.setVisibility(View.VISIBLE);
-                mModsAdapter.updateModsList(mModContainer.submods());
-            }
-        }
-    }
-
-    private class OnAdapterItemAction implements ModsAdapter.IOnItemAction
-    {
-        @Override
-        public void onItemPressed(final ModsAdapter.ModItem mod, final RecyclerView.ViewHolder vh)
-        {
-            Log.i(this, "Mod pressed: " + mod);
-            if (mod.mMod.hasSubmods())
-            {
-                if (mod.mExpanded)
-                {
-                    mModsAdapter.detachSubmods(mod, vh);
-                }
-                else
-                {
-                    mModsAdapter.attachSubmods(mod, vh);
-                    mRecycler.scrollToPosition(vh.getAdapterPosition() + 1);
-                }
-                mod.mExpanded = !mod.mExpanded;
-            }
-        }
-
-        @Override
-        public void onDownloadPressed(final ModsAdapter.ModItem mod, final RecyclerView.ViewHolder vh)
-        {
-            Log.i(this, "Mod download pressed: " + mod);
-            mModsAdapter.downloadProgress(mod, "0%");
-            installModAsync(mod);
-        }
-
-        @Override
-        public void onTogglePressed(final ModsAdapter.ModItem item, final ModBaseViewHolder holder)
-        {
-            if(!item.mMod.mSystem && item.mMod.mInstalled)
-            {
-                item.mMod.mActive = !item.mMod.mActive;
-                mModsAdapter.notifyItemChanged(holder.getAdapterPosition());
-                saveModSettingsToFile();
-            }
-        }
-
-        @Override
-        public void onUninstall(ModsAdapter.ModItem item, ModBaseViewHolder holder)
-        {
-            File installationFolder = item.mMod.installationFolder;
-            ActivityMods activity = ActivityMods.this;
-
-            if(installationFolder != null){
-                new AlertDialog.Builder(activity)
-                    .setTitle(activity.getString(R.string.mods_removal_title, item.mMod.mName))
-                    .setMessage(activity.getString(R.string.mods_removal_confirmation, item.mMod.mName))
-                    .setIcon(android.R.drawable.ic_dialog_alert)
-                    .setNegativeButton(android.R.string.no, null)
-                    .setPositiveButton(android.R.string.yes, (dialog, whichButton) ->
-                    {
-                        FileUtil.clearDirectory(installationFolder);
-                        installationFolder.delete();
-
-                        mModsAdapter.modRemoved(item);
-                    })
-                    .show();
-            }
-        }
-    }
-
-    private void installModAsync(ModsAdapter.ModItem mod){
-        File dataDir = Storage.getVcmiDataDir(this);
-        File modFolder = new File(
-                new File(dataDir, "Mods"),
-                mod.mMod.mId.toLowerCase(Locale.US));
-
-        InstallModAsync modInstaller = new InstallModAsync(
-            modFolder,
-            this,
-            new InstallModCallback(mod)
-        );
-
-        modInstaller.execute(mod.mMod.mArchiveUrl);
-    }
-
-    public class InstallModCallback implements InstallModAsync.PostDownload
-    {
-        private ModsAdapter.ModItem mod;
-
-        public InstallModCallback(ModsAdapter.ModItem mod)
-        {
-            this.mod = mod;
-        }
-
-        @Override
-        public void downloadDone(Boolean succeed, File modFolder)
-        {
-            if(succeed){
-                mModsAdapter.modInstalled(mod, modFolder);
-            }
-        }
-
-        @Override
-        public void downloadProgress(String... progress)
-        {
-            if(progress.length > 0)
-            {
-                mModsAdapter.downloadProgress(mod, progress[0]);
-            }
-        }
-    }
-}

+ 0 - 53
android/vcmi-app/src/main/java/eu/vcmi/vcmi/ActivityWithToolbar.java

@@ -1,53 +0,0 @@
-package eu.vcmi.vcmi;
-
-import androidx.appcompat.app.ActionBar;
-import androidx.appcompat.widget.Toolbar;
-import android.view.MenuItem;
-import android.view.ViewStub;
-
-/**
- * @author F
- */
-public abstract class ActivityWithToolbar extends ActivityBase
-{
-    @Override
-    public void setContentView(final int layoutResId)
-    {
-        super.setContentView(R.layout.activity_toolbar_wrapper);
-        final ViewStub contentStub = (ViewStub) findViewById(R.id.toolbar_wrapper_content_stub);
-        contentStub.setLayoutResource(layoutResId);
-        contentStub.inflate();
-    }
-
-    @Override
-    public boolean onOptionsItemSelected(final MenuItem item)
-    {
-        if (item.getItemId() == android.R.id.home)
-        {
-            finish();
-            return true;
-        }
-        return super.onOptionsItemSelected(item);
-    }
-
-    protected void initToolbar(final int textResId)
-    {
-        initToolbar(textResId, false);
-    }
-
-    protected void initToolbar(final int textResId, final boolean isTopLevelActivity)
-    {
-        final Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
-        setSupportActionBar(toolbar);
-        toolbar.setTitle(textResId);
-
-        if (!isTopLevelActivity)
-        {
-            final ActionBar bar = getSupportActionBar();
-            if (bar != null)
-            {
-                bar.setDisplayHomeAsUpEnabled(true);
-            }
-        }
-    }
-}

+ 0 - 217
android/vcmi-app/src/main/java/eu/vcmi/vcmi/Config.java

@@ -1,217 +0,0 @@
-package eu.vcmi.vcmi;
-
-import org.json.JSONException;
-import org.json.JSONObject;
-
-import java.io.File;
-import java.io.IOException;
-
-import eu.vcmi.vcmi.util.FileUtil;
-import eu.vcmi.vcmi.util.Log;
-
-/**
- * @author F
- */
-public class Config
-{
-    public static final String DEFAULT_LANGUAGE = "english";
-    public static final int DEFAULT_MUSIC_VALUE = 5;
-    public static final int DEFAULT_SOUND_VALUE = 5;
-
-    public String mLanguage;
-    public int mScreenScale;
-    public int mVolumeSound;
-    public int mVolumeMusic;
-    private String adventureAi;
-    private double mPointerSpeedMultiplier;
-    private boolean mUseRelativePointer;
-    private JSONObject mRawObject;
-
-    private boolean mIsModified;
-
-    private static JSONObject accessNode(final JSONObject baseObj, String type)
-    {
-        if (baseObj == null)
-        {
-            return null;
-        }
-
-        return baseObj.optJSONObject(type);
-    }
-
-    private static JSONObject accessResolutionNode(final JSONObject baseObj)
-    {
-        if (baseObj == null)
-        {
-            return null;
-        }
-
-        final JSONObject video = baseObj.optJSONObject("video");
-        if (video != null)
-        {
-            return video.optJSONObject("resolution");
-        }
-        return null;
-    }
-
-    private static double loadDouble(final JSONObject node, final String key, final double fallback)
-    {
-        if (node == null)
-        {
-            return fallback;
-        }
-
-        return node.optDouble(key, fallback);
-    }
-
-    @SuppressWarnings("unchecked")
-    private static <T> T loadEntry(final JSONObject node, final String key, final T fallback)
-    {
-        if (node == null)
-        {
-            return fallback;
-        }
-        final Object value = node.opt(key);
-        return value == null ? fallback : (T) value;
-    }
-
-    public static Config load(final JSONObject obj)
-    {
-        Log.v("loading config from json: " + obj.toString());
-        final Config config = new Config();
-        final JSONObject general = accessNode(obj, "general");
-        final JSONObject server = accessNode(obj, "server");
-        final JSONObject resolution = accessResolutionNode(obj);
-        config.mLanguage = loadEntry(general, "language", DEFAULT_LANGUAGE);
-        config.mScreenScale = loadEntry(resolution, "scaling", -1);
-        config.mVolumeSound = loadEntry(general, "sound", DEFAULT_SOUND_VALUE);
-        config.mVolumeMusic = loadEntry(general, "music", DEFAULT_MUSIC_VALUE);
-        config.adventureAi = loadEntry(server, "playerAI", "Nullkiller");
-        config.mUseRelativePointer = loadEntry(general, "userRelativePointer", false);
-        config.mPointerSpeedMultiplier = loadDouble(general, "relativePointerSpeedMultiplier", 1.0);
-
-        config.mRawObject = obj;
-        return config;
-    }
-
-    public void updateLanguage(final String s)
-    {
-        mLanguage = s;
-        mIsModified = true;
-    }
-
-    public void updateScreenScale(final int scale)
-    {
-        mScreenScale = scale;
-        mIsModified = true;
-    }
-
-    public void updateSound(final int i)
-    {
-        mVolumeSound = i;
-        mIsModified = true;
-    }
-
-    public void updateMusic(final int i)
-    {
-        mVolumeMusic = i;
-        mIsModified = true;
-    }
-
-    public void setAdventureAi(String ai)
-    {
-        adventureAi = ai;
-        mIsModified = true;
-    }
-
-    public String getAdventureAi()
-    {
-        return this.adventureAi == null ? "Nullkiller" : this.adventureAi;
-    }
-
-    public void setPointerSpeedMultiplier(float speedMultiplier)
-    {
-        mPointerSpeedMultiplier = speedMultiplier;
-        mIsModified = true;
-    }
-
-    public float getPointerSpeedMultiplier()
-    {
-        return (float)mPointerSpeedMultiplier;
-    }
-
-    public void setPointerMode(boolean isRelative)
-    {
-        mUseRelativePointer = isRelative;
-        mIsModified = true;
-    }
-
-    public boolean getPointerModeIsRelative()
-    {
-        return mUseRelativePointer;
-    }
-
-    public void save(final File location) throws IOException, JSONException
-    {
-        if (!needsSaving(location))
-        {
-            Log.d(this, "Config doesn't need saving");
-            return;
-        }
-        try
-        {
-            final String configString = toJson();
-            FileUtil.write(location, configString);
-            Log.v(this, "Saved config: " + configString);
-        }
-        catch (final Exception e)
-        {
-            Log.e(this, "Could not save config", e);
-            throw e;
-        }
-    }
-
-    private boolean needsSaving(final File location)
-    {
-        return mIsModified || !location.exists();
-    }
-
-    private String toJson() throws JSONException
-    {
-        final JSONObject generalNode = accessNode(mRawObject, "general");
-        final JSONObject serverNode = accessNode(mRawObject, "server");
-        final JSONObject resolutionNode = accessResolutionNode(mRawObject);
-
-        final JSONObject root = mRawObject == null ? new JSONObject() : mRawObject;
-        final JSONObject general = generalNode == null ? new JSONObject() : generalNode;
-        final JSONObject video = new JSONObject();
-        final JSONObject resolution = resolutionNode == null ? new JSONObject() : resolutionNode;
-        final JSONObject server = serverNode == null ? new JSONObject() : serverNode;
-
-        if (mLanguage != null)
-        {
-            general.put("language", mLanguage);
-        }
-
-        general.put("music", mVolumeMusic);
-        general.put("sound", mVolumeSound);
-        general.put("userRelativePointer", mUseRelativePointer);
-        general.put("relativePointerSpeedMultiplier", mPointerSpeedMultiplier);
-        root.put("general", general);
-
-        if(this.adventureAi != null)
-        {
-            server.put("playerAI", this.adventureAi);
-            root.put("server", server);
-        }
-
-        if (mScreenScale > 0)
-        {
-            resolution.put("scaling", mScreenScale);
-            video.put("resolution", resolution);
-            root.put("video", video);
-        }
-
-        return root.toString();
-    }
-}

+ 0 - 11
android/vcmi-app/src/main/java/eu/vcmi/vcmi/Const.java

@@ -1,15 +1,6 @@
 package eu.vcmi.vcmi;
 
-import android.content.Context;
 import android.os.Build;
-import android.os.Environment;
-
-import java.io.BufferedWriter;
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStreamWriter;
 
 /**
  * @author F
@@ -17,8 +8,6 @@ import java.io.OutputStreamWriter;
 public class Const
 {
     public static final String JNI_METHOD_SUPPRESS = "unused"; // jni methods are marked as unused, because IDE doesn't understand jni calls
-    // used to disable lint errors about try-with-resources being unsupported on api <19 (it is supported, because retrolambda backports it)
-    public static final int SUPPRESS_TRY_WITH_RESOURCES_WARNING = Build.VERSION_CODES.KITKAT;
 
     public static final String VCMI_DATA_ROOT_FOLDER_NAME = "vcmi-data";
 }

+ 1 - 10
android/vcmi-app/src/main/java/eu/vcmi/vcmi/NativeMethods.java

@@ -1,14 +1,8 @@
 package eu.vcmi.vcmi;
 
-import android.app.Activity;
 import android.content.Context;
-import android.content.Intent;
 import android.os.Build;
-import android.os.Environment;
-import android.os.Looper;
-import android.os.Message;
 import android.os.Messenger;
-import android.os.RemoteException;
 import android.os.VibrationEffect;
 import android.os.Vibrator;
 
@@ -17,9 +11,6 @@ import org.libsdl.app.SDLActivity;
 
 import java.io.File;
 import java.lang.ref.WeakReference;
-import java.util.Date;
-import java.util.Locale;
-import java.text.SimpleDateFormat;
 
 import eu.vcmi.vcmi.util.Log;
 
@@ -35,7 +26,7 @@ public class NativeMethods
     }
 
     public static native void initClassloader();
-
+    public static native void heroesDataUpdate();
     public static native boolean tryToSaveTheGame();
 
     public static void setupMsg(final Messenger msg)

+ 1 - 20
android/vcmi-app/src/main/java/eu/vcmi/vcmi/Storage.java

@@ -1,33 +1,14 @@
 package eu.vcmi.vcmi;
 
 import android.content.Context;
+
 import java.io.File;
-import java.io.IOException;
-import eu.vcmi.vcmi.util.FileUtil;
 
 public class Storage
 {
     public static File getVcmiDataDir(Context context)
     {
         File root = context.getExternalFilesDir(null);
-
         return new File(root, Const.VCMI_DATA_ROOT_FOLDER_NAME);
     }
-
-    public static boolean testH3DataFolder(Context context)
-    {
-        return testH3DataFolder(getVcmiDataDir(context));
-    }
-
-    public static boolean testH3DataFolder(final File baseDir)
-    {
-        final File testH3Data = new File(baseDir, "Data");
-        final File testH3data = new File(baseDir, "data");
-        final File testH3DATA = new File(baseDir, "DATA");
-        return testH3Data.exists() || testH3data.exists() || testH3DATA.exists();
-    }
-
-    public static String getH3DataFolder(Context context){
-        return getVcmiDataDir(context).getAbsolutePath();
-    }
 }

+ 2 - 7
android/vcmi-app/src/main/java/eu/vcmi/vcmi/VcmiSDLActivity.java

@@ -84,9 +84,7 @@ public class VcmiSDLActivity extends SDLActivity
 
     @Override
     protected String getMainSharedObject() {
-        String library = "libvcmiclient.so";
-
-        return getContext().getApplicationInfo().nativeLibraryDir + "/" + library;
+        return String.format("%s/lib%s.so", getContext().getApplicationInfo().nativeLibraryDir, LibsLoader.CLIENT_LIB);
     }
 
     @Override
@@ -100,9 +98,6 @@ public class VcmiSDLActivity extends SDLActivity
     {
         super.onCreate(savedInstanceState);
 
-        if(mBrokenLibraries)
-            return;
-
         final View outerLayout = getLayoutInflater().inflate(R.layout.activity_game, null, false);
         final ViewGroup layout = (ViewGroup) outerLayout.findViewById(R.id.game_outer_frame);
         mProgressBar = outerLayout.findViewById(R.id.game_progress);
@@ -182,4 +177,4 @@ public class VcmiSDLActivity extends SDLActivity
             mCallback = callback;
         }
     }
-}
+}

+ 0 - 171
android/vcmi-app/src/main/java/eu/vcmi/vcmi/content/AsyncLauncherInitialization.java

@@ -1,171 +0,0 @@
-package eu.vcmi.vcmi.content;
-
-import android.Manifest;
-import android.app.Activity;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.net.Uri;
-import android.os.AsyncTask;
-import android.os.Build;
-import android.os.Environment;
-import androidx.core.app.ActivityCompat;
-
-import java.io.File;
-import java.lang.ref.WeakReference;
-import java.util.ArrayList;
-import java.util.List;
-
-import eu.vcmi.vcmi.Const;
-import eu.vcmi.vcmi.R;
-import eu.vcmi.vcmi.Storage;
-import eu.vcmi.vcmi.util.FileUtil;
-import eu.vcmi.vcmi.util.Log;
-import eu.vcmi.vcmi.util.SharedPrefs;
-
-/**
- * @author F
- */
-public class AsyncLauncherInitialization extends AsyncTask<Void, Void, AsyncLauncherInitialization.InitResult>
-{
-    private final WeakReference<ILauncherCallbacks> mCallbackRef;
-
-    public AsyncLauncherInitialization(final ILauncherCallbacks callback)
-    {
-        mCallbackRef = new WeakReference<>(callback);
-    }
-
-    private InitResult init()
-    {
-        InitResult initResult = handleDataFoldersInitialization();
-
-        if (!initResult.mSuccess)
-        {
-            return initResult;
-        }
-        Log.d(this, "Folders check passed");
-
-        return initResult;
-    }
-
-    private InitResult handleDataFoldersInitialization()
-    {
-        final ILauncherCallbacks callbacks = mCallbackRef.get();
-
-        if (callbacks == null)
-        {
-            return InitResult.failure("Internal error");
-        }
-
-        final Context ctx = callbacks.ctx();
-        final File vcmiDir = Storage.getVcmiDataDir(ctx);
-
-        final File internalDir = ctx.getFilesDir();
-        final File vcmiInternalDir = new File(internalDir, Const.VCMI_DATA_ROOT_FOLDER_NAME);
-        Log.i(this, "Using " + vcmiDir.getAbsolutePath() + " as root vcmi dir");
-
-        if(!vcmiInternalDir.exists()) vcmiInternalDir.mkdir();
-        if(!vcmiDir.exists()) vcmiDir.mkdir();
-
-        if (!Storage.testH3DataFolder(ctx))
-        {
-            // no h3 data present -> instruct user where to put it
-            return InitResult.failure(
-                ctx.getString(
-                    R.string.launcher_error_h3_data_missing,
-                        Storage.getVcmiDataDir(ctx)));
-        }
-
-        final File testVcmiData = new File(vcmiInternalDir, "Mods/vcmi/mod.json");
-        final boolean internalVcmiDataExisted = testVcmiData.exists();
-        if (!internalVcmiDataExisted && !FileUtil.unpackVcmiDataToInternalDir(vcmiInternalDir, ctx.getAssets()))
-        {
-            // no h3 data present -> instruct user where to put it
-            return InitResult.failure(ctx.getString(R.string.launcher_error_vcmi_data_internal_missing));
-        }
-
-        final String previousInternalDataHash = callbacks.prefs().load(SharedPrefs.KEY_CURRENT_INTERNAL_ASSET_HASH, null);
-        final String currentInternalDataHash = FileUtil.readAssetsStream(ctx.getAssets(), "internalDataHash.txt");
-        if (currentInternalDataHash == null || previousInternalDataHash == null || !currentInternalDataHash.equals(previousInternalDataHash))
-        {
-            // we should update the data only if it existed previously (hash is bound to be empty if we have just created the data)
-            if (internalVcmiDataExisted)
-            {
-                Log.i(this, "Internal data needs to be created/updated; old hash=" + previousInternalDataHash
-                            + ", new hash=" + currentInternalDataHash);
-                if (!FileUtil.reloadVcmiDataToInternalDir(vcmiInternalDir, ctx.getAssets()))
-                {
-                    return InitResult.failure(ctx.getString(R.string.launcher_error_vcmi_data_internal_update));
-                }
-            }
-            callbacks.prefs().save(SharedPrefs.KEY_CURRENT_INTERNAL_ASSET_HASH, currentInternalDataHash);
-        }
-
-        return InitResult.success();
-    }
-
-    @Override
-    protected InitResult doInBackground(final Void... params)
-    {
-        return init();
-    }
-
-    @Override
-    protected void onPostExecute(final InitResult initResult)
-    {
-        final ILauncherCallbacks callbacks = mCallbackRef.get();
-        if (callbacks == null)
-        {
-            return;
-        }
-
-        if (initResult.mSuccess)
-        {
-            callbacks.onInitSuccess();
-        }
-        else
-        {
-            callbacks.onInitFailure(initResult);
-        }
-    }
-
-    public interface ILauncherCallbacks
-    {
-        Activity ctx();
-
-        SharedPrefs prefs();
-
-        void onInitSuccess();
-
-        void onInitFailure(InitResult result);
-    }
-
-    public static final class InitResult
-    {
-        public final boolean mSuccess;
-        public final String mMessage;
-        public boolean mFailSilently;
-
-        public InitResult(final boolean success, final String message)
-        {
-            mSuccess = success;
-            mMessage = message;
-        }
-
-        @Override
-        public String toString()
-        {
-            return String.format("success: %s (%s)", mSuccess, mMessage);
-        }
-
-        public static InitResult failure(String message)
-        {
-            return new InitResult(false, message);
-        }
-
-        public static InitResult success()
-        {
-            return new InitResult(true, "");
-        }
-    }
-}

+ 0 - 52
android/vcmi-app/src/main/java/eu/vcmi/vcmi/content/DialogAuthors.java

@@ -1,52 +0,0 @@
-package eu.vcmi.vcmi.content;
-
-import android.annotation.SuppressLint;
-import android.app.Dialog;
-import android.os.Bundle;
-import androidx.annotation.NonNull;
-import androidx.fragment.app.DialogFragment;
-import androidx.appcompat.app.AlertDialog;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.widget.TextView;
-
-import java.io.IOException;
-
-import eu.vcmi.vcmi.R;
-import eu.vcmi.vcmi.util.FileUtil;
-import eu.vcmi.vcmi.util.Log;
-
-/**
- * @author F
- */
-public class DialogAuthors extends DialogFragment
-{
-    @NonNull
-    @Override
-    public Dialog onCreateDialog(final Bundle savedInstanceState)
-    {
-        final LayoutInflater inflater = LayoutInflater.from(getActivity());
-        @SuppressLint("InflateParams") final View inflated = inflater.inflate(R.layout.dialog_authors, null, false);
-        final TextView vcmiAuthorsView = (TextView) inflated.findViewById(R.id.dialog_authors_vcmi);
-        final TextView launcherAuthorsView = (TextView) inflated.findViewById(R.id.dialog_authors_launcher);
-        loadAuthorsContent(vcmiAuthorsView, launcherAuthorsView);
-        return new AlertDialog.Builder(getActivity())
-            .setView(inflated)
-            .create();
-    }
-
-    private void loadAuthorsContent(final TextView vcmiAuthorsView, final TextView launcherAuthorsView)
-    {
-        try
-        {
-            // to be checked if this should be converted to async load (not really a file operation so it should be okay)
-            final String authorsContent = "See ingame credits";
-            vcmiAuthorsView.setText(authorsContent);
-            launcherAuthorsView.setText("Fay"); // TODO hardcoded for now
-        }
-        catch (final Exception e)
-        {
-            Log.e(this, "Could not load authors content", e);
-        }
-    }
-}

+ 0 - 36
android/vcmi-app/src/main/java/eu/vcmi/vcmi/content/ModBaseViewHolder.java

@@ -1,36 +0,0 @@
-package eu.vcmi.vcmi.content;
-
-import androidx.recyclerview.widget.RecyclerView;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ImageView;
-import android.widget.TextView;
-
-import eu.vcmi.vcmi.R;
-
-/**
- * @author F
- */
-public class ModBaseViewHolder extends RecyclerView.ViewHolder
-{
-    final View mModNesting;
-    final TextView mModName;
-
-    ModBaseViewHolder(final View parentView)
-    {
-        this(
-            LayoutInflater.from(parentView.getContext()).inflate(
-                R.layout.mod_base_adapter_item,
-                (ViewGroup) parentView,
-                false),
-            true);
-    }
-
-    protected ModBaseViewHolder(final View v, final boolean internal)
-    {
-        super(v);
-        mModNesting = itemView.findViewById(R.id.mods_adapter_item_nesting);
-        mModName = (TextView) itemView.findViewById(R.id.mods_adapter_item_name);
-    }
-}

+ 0 - 254
android/vcmi-app/src/main/java/eu/vcmi/vcmi/content/ModsAdapter.java

@@ -1,254 +0,0 @@
-package eu.vcmi.vcmi.content;
-
-import android.content.Context;
-import androidx.recyclerview.widget.RecyclerView;
-import android.view.View;
-import android.view.ViewGroup;
-import java.io.File;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Locale;
-import eu.vcmi.vcmi.R;
-import eu.vcmi.vcmi.mods.VCMIMod;
-import eu.vcmi.vcmi.util.Log;
-
-/**
- * @author F
- */
-public class ModsAdapter extends RecyclerView.Adapter<ModBaseViewHolder>
-{
-    private static final int NESTING_WIDTH_PER_LEVEL = 16;
-    private static final int VIEWTYPE_MOD = 0;
-    private static final int VIEWTYPE_FAILED_MOD = 1;
-    private final List<ModItem> mDataset = new ArrayList<>();
-    private final IOnItemAction mItemListener;
-
-    public ModsAdapter(final IOnItemAction itemListener)
-    {
-        mItemListener = itemListener;
-    }
-
-    @Override
-    public ModBaseViewHolder onCreateViewHolder(final ViewGroup parent, final int viewType)
-    {
-        switch (viewType)
-        {
-            case VIEWTYPE_MOD:
-                return new ModsViewHolder(parent);
-            case VIEWTYPE_FAILED_MOD:
-                return new ModBaseViewHolder(parent);
-            default:
-                Log.e(this, "Unhandled view type: " + viewType);
-                return null;
-        }
-    }
-
-    @Override
-    public void onBindViewHolder(final ModBaseViewHolder holder, final int position)
-    {
-        final ModItem item = mDataset.get(position);
-        final int viewType = getItemViewType(position);
-
-        final Context ctx = holder.itemView.getContext();
-        holder.mModNesting.getLayoutParams().width = item.mNestingLevel * NESTING_WIDTH_PER_LEVEL;
-        switch (viewType)
-        {
-            case VIEWTYPE_MOD:
-                final ModsViewHolder modHolder = (ModsViewHolder) holder;
-                modHolder.mModName.setText(item.mMod.mName + ", " + item.mMod.mVersion);
-                modHolder.mModType.setText(item.mMod.mModType);
-                if (item.mMod.mSize > 0)
-                {
-                    modHolder.mModSize.setVisibility(View.VISIBLE);
-                    // TODO unit conversion
-                    modHolder.mModSize.setText(String.format(Locale.getDefault(), "%.1f kB", item.mMod.mSize / 1024.0f));
-                }
-                else
-                {
-                    modHolder.mModSize.setVisibility(View.GONE);
-                }
-                modHolder.mModAuthor.setText(ctx.getString(R.string.mods_item_author_template, item.mMod.mAuthor));
-                modHolder.mStatusIcon.setImageResource(selectModStatusIcon(item.mMod.mActive));
-
-                modHolder.mDownloadBtn.setVisibility(View.GONE);
-                modHolder.mDownloadProgress.setVisibility(View.GONE);
-                modHolder.mUninstall.setVisibility(View.GONE);
-
-                if(!item.mMod.mSystem)
-                {
-                    if (item.mDownloadProgress != null)
-                    {
-                        modHolder.mDownloadProgress.setText(item.mDownloadProgress);
-                        modHolder.mDownloadProgress.setVisibility(View.VISIBLE);
-                    }
-                    else if (!item.mMod.mInstalled)
-                    {
-                        modHolder.mDownloadBtn.setVisibility(View.VISIBLE);
-                    }
-                    else if (item.mMod.installationFolder != null)
-                    {
-                        modHolder.mUninstall.setVisibility(View.VISIBLE);
-                    }
-
-                    modHolder.itemView.setOnClickListener(v -> mItemListener.onItemPressed(item, holder));
-                    modHolder.mStatusIcon.setOnClickListener(v -> mItemListener.onTogglePressed(item, holder));
-                    modHolder.mDownloadBtn.setOnClickListener(v -> mItemListener.onDownloadPressed(item, holder));
-                    modHolder.mUninstall.setOnClickListener(v -> mItemListener.onUninstall(item, holder));
-                }
-
-                break;
-            case VIEWTYPE_FAILED_MOD:
-                holder.mModName.setText(ctx.getString(R.string.mods_failed_mod_loading, item.mMod.mName));
-                break;
-            default:
-                Log.e(this, "Unhandled view type: " + viewType);
-                break;
-        }
-    }
-
-    private int selectModStatusIcon(final boolean active)
-    {
-        // TODO distinguishing mods that aren't downloaded or have an update available
-        if (active)
-        {
-            return R.drawable.ic_star_full;
-        }
-        return R.drawable.ic_star_empty;
-    }
-
-    @Override
-    public int getItemViewType(final int position)
-    {
-        return mDataset.get(position).mMod.mLoadedCorrectly ? VIEWTYPE_MOD : VIEWTYPE_FAILED_MOD;
-    }
-
-    @Override
-    public int getItemCount()
-    {
-        return mDataset.size();
-    }
-
-    public void attachSubmods(final ModItem mod, final RecyclerView.ViewHolder vh)
-    {
-        int adapterPosition = vh.getAdapterPosition();
-        final List<ModItem> submods = new ArrayList<>();
-
-        for (VCMIMod v : mod.mMod.submods())
-        {
-            ModItem modItem = new ModItem(v, mod.mNestingLevel + 1);
-            submods.add(modItem);
-        }
-
-        mDataset.addAll(adapterPosition + 1, submods);
-        notifyItemRangeInserted(adapterPosition + 1, submods.size());
-    }
-
-    public void detachSubmods(final ModItem mod, final RecyclerView.ViewHolder vh)
-    {
-        final int adapterPosition = vh.getAdapterPosition();
-        final int checkedPosition = adapterPosition + 1;
-        int detachedElements = 0;
-        while (checkedPosition < mDataset.size() && mDataset.get(checkedPosition).mNestingLevel > mod.mNestingLevel)
-        {
-            ++detachedElements;
-            mDataset.remove(checkedPosition);
-        }
-        notifyItemRangeRemoved(checkedPosition, detachedElements);
-    }
-
-    public void updateModsList(List<VCMIMod> mods)
-    {
-        mDataset.clear();
-
-        List<ModItem> list = new ArrayList<>();
-
-        for (VCMIMod mod : mods)
-        {
-            ModItem modItem = new ModItem(mod);
-            list.add(modItem);
-        }
-
-        mDataset.addAll(list);
-
-        notifyDataSetChanged();
-    }
-
-    public void modInstalled(ModItem mod, File modFolder)
-    {
-        try
-        {
-            mod.mMod.updateFromModInfo(modFolder);
-            mod.mMod.mLoadedCorrectly = true;
-            mod.mMod.mActive = true; // active by default
-            mod.mMod.mInstalled = true;
-            mod.mMod.installationFolder = modFolder;
-            mod.mDownloadProgress = null;
-            notifyItemChanged(mDataset.indexOf(mod));
-        }
-        catch (Exception ex)
-        {
-            Log.e("Failed to install mod", ex);
-        }
-    }
-
-    public void downloadProgress(ModItem mod, String progress)
-    {
-        mod.mDownloadProgress = progress;
-        notifyItemChanged(mDataset.indexOf(mod));
-    }
-
-    public void modRemoved(ModItem item)
-    {
-        int itemIndex = mDataset.indexOf(item);
-
-        if(item.mMod.mArchiveUrl != null && item.mMod.mArchiveUrl != "")
-        {
-            item.mMod.mInstalled = false;
-            item.mMod.installationFolder = null;
-
-            notifyItemChanged(itemIndex);
-        }
-        else
-        {
-            mDataset.remove(item);
-            notifyItemRemoved(itemIndex);
-        }
-    }
-
-    public interface IOnItemAction
-    {
-        void onItemPressed(final ModItem mod, final RecyclerView.ViewHolder vh);
-
-        void onDownloadPressed(final ModItem mod, final RecyclerView.ViewHolder vh);
-
-        void onTogglePressed(ModItem item, ModBaseViewHolder holder);
-
-        void onUninstall(ModItem item, ModBaseViewHolder holder);
-    }
-
-    public static class ModItem
-    {
-        public final VCMIMod mMod;
-        public int mNestingLevel;
-        public boolean mExpanded;
-        public String mDownloadProgress;
-
-        public ModItem(final VCMIMod mod)
-        {
-            this(mod, 0);
-        }
-
-        public ModItem(final VCMIMod mod, final int nestingLevel)
-        {
-            mMod = mod;
-            mNestingLevel = nestingLevel;
-            mExpanded = false;
-        }
-
-        @Override
-        public String toString()
-        {
-            return mMod.toString();
-        }
-    }
-}

+ 0 - 35
android/vcmi-app/src/main/java/eu/vcmi/vcmi/content/ModsViewHolder.java

@@ -1,35 +0,0 @@
-package eu.vcmi.vcmi.content;
-
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ImageView;
-import android.widget.TextView;
-
-import eu.vcmi.vcmi.R;
-
-/**
- * @author F
- */
-public class ModsViewHolder extends ModBaseViewHolder
-{
-    final TextView mModAuthor;
-    final TextView mModType;
-    final TextView mModSize;
-    final ImageView mStatusIcon;
-    final View mDownloadBtn;
-    final TextView mDownloadProgress;
-    final View mUninstall;
-
-    ModsViewHolder(final View parentView)
-    {
-        super(LayoutInflater.from(parentView.getContext()).inflate(R.layout.mods_adapter_item, (ViewGroup) parentView, false), true);
-        mModAuthor = (TextView) itemView.findViewById(R.id.mods_adapter_item_author);
-        mModType = (TextView) itemView.findViewById(R.id.mods_adapter_item_modtype);
-        mModSize = (TextView) itemView.findViewById(R.id.mods_adapter_item_size);
-        mDownloadBtn = itemView.findViewById(R.id.mods_adapter_item_btn_download);
-        mStatusIcon = (ImageView) itemView.findViewById(R.id.mods_adapter_item_status);
-        mDownloadProgress = (TextView) itemView.findViewById(R.id.mods_adapter_item_install_progress);
-        mUninstall = itemView.findViewById(R.id.mods_adapter_item_btn_uninstall);
-    }
-}

+ 0 - 258
android/vcmi-app/src/main/java/eu/vcmi/vcmi/mods/VCMIMod.java

@@ -1,258 +0,0 @@
-package eu.vcmi.vcmi.mods;
-
-import android.text.TextUtils;
-
-import org.json.JSONArray;
-import org.json.JSONException;
-import org.json.JSONObject;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-
-import eu.vcmi.vcmi.BuildConfig;
-import eu.vcmi.vcmi.util.FileUtil;
-import eu.vcmi.vcmi.util.Log;
-
-/**
- * @author F
- */
-public class VCMIMod
-{
-    protected final Map<String, VCMIMod> mSubmods;
-    public String mId;
-    public String mName;
-    public String mDesc;
-    public String mVersion;
-    public String mAuthor;
-    public String mContact;
-    public String mModType;
-    public String mArchiveUrl;
-    public long mSize;
-    public File installationFolder;
-
-    // config values
-    public boolean mActive;
-    public boolean mInstalled;
-    public boolean mValidated;
-    public String mChecksum;
-
-    // internal
-    public boolean mLoadedCorrectly;
-    public boolean mSystem;
-
-    protected VCMIMod()
-    {
-        mSubmods = new HashMap<>();
-    }
-
-    public static VCMIMod buildFromRepoJson(final String id,
-                                            final JSONObject obj,
-                                            JSONObject modDownloadData)
-    {
-        final VCMIMod mod = new VCMIMod();
-        mod.mId = id.toLowerCase(Locale.US);
-        mod.mName = obj.optString("name");
-        mod.mDesc = obj.optString("description");
-        mod.mVersion = obj.optString("version");
-        mod.mAuthor = obj.optString("author");
-        mod.mContact = obj.optString("contact");
-        mod.mModType = obj.optString("modType");
-        mod.mArchiveUrl = modDownloadData.optString("download");
-        mod.mSize = obj.optLong("size");
-        mod.mLoadedCorrectly = true;
-        return mod;
-    }
-
-    public static VCMIMod buildFromConfigJson(final String id, final JSONObject obj) throws JSONException
-    {
-        final VCMIMod mod = new VCMIMod();
-        mod.updateFromConfigJson(id, obj);
-        mod.mInstalled = true;
-        return mod;
-    }
-
-    public static VCMIMod buildFromModInfo(final File modPath) throws IOException, JSONException
-    {
-        final VCMIMod mod = new VCMIMod();
-        if (!mod.updateFromModInfo(modPath))
-        {
-            return mod;
-        }
-        mod.mLoadedCorrectly = true;
-        mod.mActive = true; // active by default
-        mod.mInstalled = true;
-        mod.installationFolder = modPath;
-
-        return mod;
-    }
-
-    protected static Map<String, VCMIMod> loadSubmods(final List<File> modsList) throws IOException, JSONException
-    {
-        final Map<String, VCMIMod> submods = new HashMap<>();
-        for (final File f : modsList)
-        {
-            if (!f.isDirectory())
-            {
-                Log.w("VCMI", "Non-directory encountered in mods dir: " + f.getName());
-                continue;
-            }
-
-            final VCMIMod submod = buildFromModInfo(f);
-            if (submod == null)
-            {
-                Log.w(null, "Could not build mod in folder " + f + "; ignoring");
-                continue;
-            }
-
-            submods.put(submod.mId, submod);
-        }
-        return submods;
-    }
-
-    public void updateFromConfigJson(final String id, final JSONObject obj) throws JSONException
-    {
-        if(mSystem)
-        {
-            return;
-        }
-
-        mId = id.toLowerCase(Locale.US);
-        mActive = obj.optBoolean("active");
-        mValidated = obj.optBoolean("validated");
-        mChecksum = obj.optString("checksum");
-
-        final JSONObject submods = obj.optJSONObject("mods");
-        if (submods != null)
-        {
-            updateChildrenFromConfigJson(submods);
-        }
-    }
-
-    protected void updateChildrenFromConfigJson(final JSONObject submods) throws JSONException
-    {
-        final JSONArray names = submods.names();
-        for (int i = 0; i < names.length(); ++i)
-        {
-            final String modId = names.getString(i);
-            final String normalizedModId = modId.toLowerCase(Locale.US);
-            if (!mSubmods.containsKey(normalizedModId))
-            {
-                Log.w(this, "Mod present in config but not found in /Mods; ignoring: " + modId);
-                continue;
-            }
-
-            mSubmods.get(normalizedModId).updateFromConfigJson(modId, submods.getJSONObject(modId));
-        }
-    }
-
-    public boolean updateFromModInfo(final File modPath) throws IOException, JSONException
-    {
-        final File modInfoFile = new File(modPath, "mod.json");
-        if (!modInfoFile.exists())
-        {
-            Log.w(this, "Mod info doesn't exist");
-            mName = modPath.getAbsolutePath();
-            return false;
-        }
-        try
-        {
-            final JSONObject modInfoContent = new JSONObject(FileUtil.read(modInfoFile));
-            mId = modPath.getName().toLowerCase(Locale.US);
-            mName = modInfoContent.optString("name");
-            mDesc = modInfoContent.optString("description");
-            mVersion = modInfoContent.optString("version");
-            mAuthor = modInfoContent.optString("author");
-            mContact = modInfoContent.optString("contact");
-            mModType = modInfoContent.optString("modType");
-            mSystem = mId.equals("vcmi");
-
-            final File submodsDir = new File(modPath, "Mods");
-            if (submodsDir.exists())
-            {
-                final List<File> submodsFiles = new ArrayList<>();
-                Collections.addAll(submodsFiles, submodsDir.listFiles());
-                mSubmods.putAll(loadSubmods(submodsFiles));
-            }
-            return true;
-        }
-        catch (final JSONException ex)
-        {
-            mName = modPath.getAbsolutePath();
-            return false;
-        }
-    }
-
-    @Override
-    public String toString()
-    {
-        if (!BuildConfig.DEBUG)
-        {
-            return "";
-        }
-        return String.format("mod:[id:%s,active:%s,submods:[%s]]", mId, mActive, TextUtils.join(",", mSubmods.values()));
-    }
-
-    protected void submodsToJson(final JSONObject modsRoot) throws JSONException
-    {
-        for (final VCMIMod submod : mSubmods.values())
-        {
-            final JSONObject submodEntry = new JSONObject();
-            submod.toJsonInternal(submodEntry);
-            modsRoot.put(submod.mId, submodEntry);
-        }
-    }
-
-    protected void toJsonInternal(final JSONObject root) throws JSONException
-    {
-        root.put("active", mActive);
-        root.put("validated", mValidated);
-        if (!TextUtils.isEmpty(mChecksum))
-        {
-            root.put("checksum", mChecksum);
-        }
-        if (!mSubmods.isEmpty())
-        {
-            JSONObject submods = new JSONObject();
-            submodsToJson(submods);
-            root.put("mods", submods);
-        }
-    }
-
-    public boolean hasSubmods()
-    {
-        return !mSubmods.isEmpty();
-    }
-
-    public List<VCMIMod> submods()
-    {
-        final ArrayList<VCMIMod> ret = new ArrayList<>();
-
-        ret.addAll(mSubmods.values());
-
-        Collections.sort(ret, new Comparator<VCMIMod>()
-        {
-            @Override
-            public int compare(VCMIMod left, VCMIMod right)
-            {
-                return left.mName.compareTo(right.mName);
-            }
-        });
-
-        return ret;
-    }
-
-    protected void updateFrom(VCMIMod other)
-    {
-        this.mModType = other.mModType;
-        this.mAuthor = other.mAuthor;
-        this.mDesc = other.mDesc;
-        this.mArchiveUrl = other.mArchiveUrl;
-    }
-}

+ 0 - 106
android/vcmi-app/src/main/java/eu/vcmi/vcmi/mods/VCMIModContainer.java

@@ -1,106 +0,0 @@
-package eu.vcmi.vcmi.mods;
-
-import android.text.TextUtils;
-
-import org.json.JSONException;
-import org.json.JSONObject;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.List;
-import java.util.Locale;
-
-import eu.vcmi.vcmi.BuildConfig;
-import eu.vcmi.vcmi.util.FileUtil;
-import eu.vcmi.vcmi.util.Log;
-
-/**
- * @author F
- */
-public class VCMIModContainer extends VCMIMod
-{
-    private VCMIMod mCoreStatus; // kept here to correctly save core object to modSettings
-
-    private VCMIModContainer()
-    {
-    }
-
-    public static VCMIModContainer createContainer(final List<File> modsList) throws IOException, JSONException
-    {
-        final VCMIModContainer mod = new VCMIModContainer();
-        mod.mSubmods.putAll(loadSubmods(modsList));
-        return mod;
-    }
-
-    public void updateContainerFromConfigJson(final JSONObject modsList, final JSONObject coreStatus) throws JSONException
-    {
-        updateChildrenFromConfigJson(modsList);
-        if (coreStatus != null)
-        {
-            mCoreStatus = VCMIMod.buildFromConfigJson("core", coreStatus);
-        }
-    }
-
-    public void updateFromRepo(List<VCMIMod> repoMods){
-        for (VCMIMod mod : repoMods)
-        {
-            final String normalizedModId = mod.mId.toLowerCase(Locale.US);
-
-            if(mSubmods.containsKey(normalizedModId)){
-                VCMIMod existing = mSubmods.get(normalizedModId);
-
-                existing.updateFrom(mod);
-            }
-            else{
-                mSubmods.put(normalizedModId, mod);
-            }
-        }
-    }
-
-    @Override
-    public String toString()
-    {
-        if (!BuildConfig.DEBUG)
-        {
-            return "";
-        }
-        return String.format("mods:[%s]", TextUtils.join(",", mSubmods.values()));
-    }
-
-    public void saveToFile(final File location)
-    {
-        try
-        {
-            FileUtil.write(location, toJson());
-        }
-        catch (Exception e)
-        {
-            Log.e(this, "Could not save mod settings", e);
-        }
-    }
-
-    protected String toJson() throws JSONException
-    {
-        final JSONObject root = new JSONObject();
-        final JSONObject activeMods = new JSONObject();
-        final JSONObject coreStatus = new JSONObject();
-        root.put("activeMods", activeMods);
-        submodsToJson(activeMods);
-
-        coreStatusToJson(coreStatus);
-        root.put("core", coreStatus);
-
-        return root.toString();
-    }
-
-    private void coreStatusToJson(final JSONObject coreStatus) throws JSONException
-    {
-        if (mCoreStatus == null)
-        {
-            mCoreStatus = new VCMIMod();
-            mCoreStatus.mId = "core";
-            mCoreStatus.mActive = true;
-        }
-        mCoreStatus.toJsonInternal(coreStatus);
-    }
-}

+ 0 - 108
android/vcmi-app/src/main/java/eu/vcmi/vcmi/mods/VCMIModsRepo.java

@@ -1,108 +0,0 @@
-package eu.vcmi.vcmi.mods;
-
-import org.json.JSONArray;
-import org.json.JSONException;
-import org.json.JSONObject;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import eu.vcmi.vcmi.util.AsyncRequest;
-import eu.vcmi.vcmi.util.Log;
-import eu.vcmi.vcmi.util.ServerResponse;
-
-/**
- * @author F
- */
-public class VCMIModsRepo
-{
-    private final List<VCMIMod> mModsList;
-    private IOnModsRepoDownloaded mCallback;
-
-    public VCMIModsRepo()
-    {
-        mModsList = new ArrayList<>();
-    }
-
-    public void init(final String url, final IOnModsRepoDownloaded callback)
-    {
-        mCallback = callback;
-        new AsyncLoadRepo().execute(url);
-    }
-
-    public interface IOnModsRepoDownloaded
-    {
-        void onSuccess(ServerResponse<List<VCMIMod>> response);
-        void onError(final int code);
-    }
-
-    private class AsyncLoadRepo extends AsyncRequest<List<VCMIMod>>
-    {
-        @Override
-        protected ServerResponse<List<VCMIMod>> doInBackground(final String... params)
-        {
-            ServerResponse<List<VCMIMod>> serverResponse = sendRequest(params[0]);
-            if (serverResponse.isValid())
-            {
-                final List<VCMIMod> mods = new ArrayList<>();
-                try
-                {
-                    JSONObject jsonContent = new JSONObject(serverResponse.mRawContent);
-                    final JSONArray names = jsonContent.names();
-                    for (int i = 0; i < names.length(); ++i)
-                    {
-                        try
-                        {
-                            String name = names.getString(i);
-                            JSONObject modDownloadData = jsonContent.getJSONObject(name);
-
-                            if(modDownloadData.has("mod"))
-                            {
-                                String modFileAddress = modDownloadData.getString("mod");
-                                ServerResponse<List<VCMIMod>> modFile = sendRequest(modFileAddress);
-
-                                if (!modFile.isValid())
-                                {
-                                    continue;
-                                }
-
-                                JSONObject modJson = new JSONObject(modFile.mRawContent);
-                                mods.add(VCMIMod.buildFromRepoJson(name, modJson, modDownloadData));
-                            }
-                            else
-                            {
-                                mods.add(VCMIMod.buildFromRepoJson(name, modDownloadData, modDownloadData));
-                            }
-                        }
-                        catch (JSONException e)
-                        {
-                            Log.e(this, "Could not parse the response as json", e);
-                        }
-                    }
-                    serverResponse.mContent = mods;
-                }
-                catch (JSONException e)
-                {
-                    Log.e(this, "Could not parse the response as json", e);
-                    serverResponse.mCode = ServerResponse.LOCAL_ERROR_PARSING;
-                }
-            }
-            return serverResponse;
-        }
-
-        @Override
-        protected void onPostExecute(final ServerResponse<List<VCMIMod>> response)
-        {
-            if (response.isValid())
-            {
-                mModsList.clear();
-                mModsList.addAll(response.mContent);
-                mCallback.onSuccess(response);
-            }
-            else
-            {
-                mCallback.onError(response.mCode);
-            }
-        }
-    }
-}

+ 0 - 46
android/vcmi-app/src/main/java/eu/vcmi/vcmi/settings/AdventureAiController.java

@@ -1,46 +0,0 @@
-package eu.vcmi.vcmi.settings;
-
-import androidx.appcompat.app.AppCompatActivity;
-import eu.vcmi.vcmi.Config;
-import eu.vcmi.vcmi.R;
-
-/**
- * @author F
- */
-public class AdventureAiController extends LauncherSettingWithDialogController<String, Config>
-{
-    public AdventureAiController(final AppCompatActivity activity)
-    {
-        super(activity);
-    }
-
-    @Override
-    protected LauncherSettingDialog<String> dialog()
-    {
-        return new AdventureAiSelectionDialog();
-    }
-
-    @Override
-    public void onItemChosen(final String item)
-    {
-        mConfig.setAdventureAi(item);
-        updateContent();
-    }
-
-    @Override
-    protected String mainText()
-    {
-        return mActivity.getString(R.string.launcher_btn_adventure_ai_title);
-    }
-
-    @Override
-    protected String subText()
-    {
-        if (mConfig == null)
-        {
-            return "";
-        }
-
-        return mConfig.getAdventureAi();
-    }
-}

+ 0 - 37
android/vcmi-app/src/main/java/eu/vcmi/vcmi/settings/AdventureAiSelectionDialog.java

@@ -1,37 +0,0 @@
-package eu.vcmi.vcmi.settings;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import eu.vcmi.vcmi.R;
-
-/**
- * @author F
- */
-public class AdventureAiSelectionDialog extends LauncherSettingDialog<String>
-{
-    private static final List<String> AVAILABLE_AI = new ArrayList<>();
-
-    static
-    {
-        AVAILABLE_AI.add("VCAI");
-        AVAILABLE_AI.add("Nullkiller");
-    }
-
-    public AdventureAiSelectionDialog()
-    {
-        super(AVAILABLE_AI);
-    }
-
-    @Override
-    protected int dialogTitleResId()
-    {
-        return R.string.launcher_btn_adventure_ai_title;
-    }
-
-    @Override
-    protected CharSequence itemName(final String item)
-    {
-        return item;
-    }
-}

+ 0 - 193
android/vcmi-app/src/main/java/eu/vcmi/vcmi/settings/CopyDataController.java

@@ -1,193 +0,0 @@
-package eu.vcmi.vcmi.settings;
-
-import android.app.Activity;
-import android.content.Intent;
-import android.net.Uri;
-import android.os.AsyncTask;
-import android.os.Environment;
-import android.provider.DocumentsContract;
-import android.view.View;
-
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.util.ArrayList;
-import java.util.List;
-
-import androidx.appcompat.app.AppCompatActivity;
-import androidx.documentfile.provider.DocumentFile;
-import androidx.loader.content.AsyncTaskLoader;
-import eu.vcmi.vcmi.R;
-import eu.vcmi.vcmi.Storage;
-import eu.vcmi.vcmi.util.FileUtil;
-
-public class CopyDataController extends LauncherSettingController<Void, Void>
-{
-    public static final int PICK_EXTERNAL_VCMI_DATA_TO_COPY = 3;
-
-    private String progress;
-
-    public CopyDataController(final AppCompatActivity act)
-    {
-        super(act);
-    }
-
-    @Override
-    protected String mainText()
-    {
-        return mActivity.getString(R.string.launcher_btn_import_title);
-    }
-
-    @Override
-    protected String subText()
-    {
-        if (progress != null)
-        {
-            return progress;
-        }
-
-        return mActivity.getString(R.string.launcher_btn_import_description);
-    }
-
-    @Override
-    public void onClick(final View v)
-    {
-        Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
-
-        intent.putExtra(
-                DocumentsContract.EXTRA_INITIAL_URI,
-                Uri.fromFile(new File(Environment.getExternalStorageDirectory(), "vcmi-data")));
-
-        mActivity.startActivityForResult(intent, PICK_EXTERNAL_VCMI_DATA_TO_COPY);
-    }
-
-    public void copyData(Uri folderToCopy)
-    {
-        AsyncCopyData copyTask = new AsyncCopyData(mActivity, folderToCopy);
-
-        copyTask.execute();
-    }
-
-    private class AsyncCopyData extends AsyncTask<String, String, Boolean>
-    {
-        private Activity owner;
-        private Uri folderToCopy;
-
-        public AsyncCopyData(Activity owner, Uri folderToCopy)
-        {
-            this.owner = owner;
-            this.folderToCopy = folderToCopy;
-        }
-
-        @Override
-        protected Boolean doInBackground(final String... params)
-        {
-            File targetDir = Storage.getVcmiDataDir(owner);
-            DocumentFile sourceDir = DocumentFile.fromTreeUri(owner, folderToCopy);
-
-            ArrayList<String> allowedFolders = new ArrayList<String>();
-
-            allowedFolders.add("Data");
-            allowedFolders.add("Mp3");
-            allowedFolders.add("Maps");
-            allowedFolders.add("Saves");
-            allowedFolders.add("Mods");
-            allowedFolders.add("config");
-
-            return copyDirectory(targetDir, sourceDir, allowedFolders);
-        }
-
-        @Override
-        protected void onPostExecute(Boolean result)
-        {
-            super.onPostExecute(result);
-
-            if (result)
-            {
-                CopyDataController.this.progress = null;
-                CopyDataController.this.updateContent();
-            }
-        }
-
-        @Override
-        protected void onProgressUpdate(String... values)
-        {
-            CopyDataController.this.progress = values[0];
-            CopyDataController.this.updateContent();
-        }
-
-        private boolean copyDirectory(File targetDir, DocumentFile sourceDir, List<String> allowed)
-        {
-            if (!targetDir.exists())
-            {
-                targetDir.mkdir();
-            }
-
-            for (DocumentFile child : sourceDir.listFiles())
-            {
-                if (allowed != null)
-                {
-                    boolean fileAllowed = false;
-                    
-                    for (String str : allowed)
-                    {
-                        if (str.equalsIgnoreCase(child.getName()))
-                        {
-                            fileAllowed = true;
-                            break;
-                        }
-                    }
-                    
-                    if (!fileAllowed)
-                        continue;
-                }
-
-                File exported = new File(targetDir, child.getName());
-
-                if (child.isFile())
-                {
-                    publishProgress(owner.getString(R.string.launcher_progress_copy,
-                            child.getName()));
-
-                    if (!exported.exists())
-                    {
-                        try
-                        {
-                            exported.createNewFile();
-                        }
-                        catch (IOException e)
-                        {
-                            publishProgress("Failed to copy file " + child.getName());
-
-                            return false;
-                        }
-                    }
-
-                    try (
-                        final OutputStream targetStream = new FileOutputStream(exported, false);
-                        final InputStream sourceStream = owner.getContentResolver()
-                                .openInputStream(child.getUri()))
-                    {
-                        FileUtil.copyStream(sourceStream, targetStream);
-                    }
-                    catch (IOException e)
-                    {
-                        publishProgress("Failed to copy file " + child.getName());
-
-                        return false;
-                    }
-                }
-
-                if (child.isDirectory() && !copyDirectory(exported, child, null))
-                {
-                    return false;
-                }
-            }
-
-            return true;
-        }
-    }
-}
-

+ 0 - 19
android/vcmi-app/src/main/java/eu/vcmi/vcmi/settings/DoubleConfig.java

@@ -1,19 +0,0 @@
-package eu.vcmi.vcmi.settings;
-
-import eu.vcmi.vcmi.Config;
-import eu.vcmi.vcmi.util.SharedPrefs;
-
-/**
- * @author F
- */
-public class DoubleConfig
-{
-    public final Config mConfig;
-    public final SharedPrefs mPrefs;
-
-    public DoubleConfig(final Config config, final SharedPrefs prefs)
-    {
-        mConfig = config;
-        mPrefs = prefs;
-    }
-}

+ 0 - 174
android/vcmi-app/src/main/java/eu/vcmi/vcmi/settings/ExportDataController.java

@@ -1,174 +0,0 @@
-package eu.vcmi.vcmi.settings;
-
-import android.app.Activity;
-import android.content.Intent;
-import android.net.Uri;
-import android.os.AsyncTask;
-import android.os.Environment;
-import android.provider.DocumentsContract;
-import android.view.View;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-
-import androidx.appcompat.app.AppCompatActivity;
-import androidx.documentfile.provider.DocumentFile;
-import eu.vcmi.vcmi.R;
-import eu.vcmi.vcmi.Storage;
-import eu.vcmi.vcmi.util.FileUtil;
-
-public class ExportDataController extends LauncherSettingController<Void, Void>
-{
-    public static final int PICK_DIRECTORY_TO_EXPORT = 4;
-
-    private String progress;
-
-    public ExportDataController(final AppCompatActivity act)
-    {
-        super(act);
-    }
-
-    @Override
-    protected String mainText()
-    {
-        return mActivity.getString(R.string.launcher_btn_export_title);
-    }
-
-    @Override
-    protected String subText()
-    {
-        if (progress != null)
-        {
-            return progress;
-        }
-
-        return mActivity.getString(R.string.launcher_btn_export_description);
-    }
-
-    @Override
-    public void onClick(final View v)
-    {
-        Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
-
-        intent.putExtra(
-            DocumentsContract.EXTRA_INITIAL_URI,
-            Uri.fromFile(new File(Environment.getExternalStorageDirectory(), "vcmi-data")));
-
-        mActivity.startActivityForResult(intent, PICK_DIRECTORY_TO_EXPORT);
-    }
-
-    public void copyData(Uri targetFolder)
-    {
-        AsyncCopyData copyTask = new AsyncCopyData(mActivity, targetFolder);
-
-        copyTask.execute();
-    }
-
-    private class AsyncCopyData extends AsyncTask<String, String, Boolean>
-    {
-        private Activity owner;
-        private Uri targetFolder;
-
-        public AsyncCopyData(Activity owner, Uri targetFolder)
-        {
-            this.owner = owner;
-            this.targetFolder = targetFolder;
-        }
-
-        @Override
-        protected Boolean doInBackground(final String... params)
-        {
-            File targetDir = Storage.getVcmiDataDir(owner);
-            DocumentFile sourceDir = DocumentFile.fromTreeUri(owner, targetFolder);
-
-            return copyDirectory(targetDir, sourceDir);
-        }
-
-        @Override
-        protected void onPostExecute(Boolean result)
-        {
-            super.onPostExecute(result);
-
-            if (result)
-            {
-                ExportDataController.this.progress = null;
-                ExportDataController.this.updateContent();
-            }
-        }
-
-        @Override
-        protected void onProgressUpdate(String... values)
-        {
-            ExportDataController.this.progress = values[0];
-            ExportDataController.this.updateContent();
-        }
-
-        private boolean copyDirectory(File sourceDir, DocumentFile targetDir)
-        {
-            for (File child : sourceDir.listFiles())
-            {
-                DocumentFile exported = targetDir.findFile(child.getName());
-
-                if (child.isFile())
-                {
-                    publishProgress(owner.getString(R.string.launcher_progress_copy,
-                            child.getName()));
-
-                    if (exported == null)
-                    {
-                        try
-                        {
-                            exported = targetDir.createFile(
-                                "application/octet-stream",
-                                child.getName());
-                        }
-                        catch (UnsupportedOperationException e)
-                        {
-                            publishProgress("Failed to copy file " + child.getName());
-
-                            return false;
-                        }
-                    }
-
-                    if (exported == null)
-                    {
-                        publishProgress("Failed to copy file " + child.getName());
-                        return false;
-                    }
-
-                    try(
-                            final OutputStream targetStream = owner.getContentResolver()
-                                    .openOutputStream(exported.getUri());
-                            final InputStream sourceStream = new FileInputStream(child))
-                    {
-                        FileUtil.copyStream(sourceStream, targetStream);
-                    }
-                    catch (IOException e)
-                    {
-                        publishProgress("Failed to copy file " + child.getName());
-
-                        return false;
-                    }
-                }
-
-                if (child.isDirectory())
-                {
-                    if (exported == null)
-                    {
-                        exported = targetDir.createDirectory(child.getName());
-                    }
-
-                    if(!copyDirectory(child, exported))
-                    {
-                        return false;
-                    }
-                }
-            }
-
-            return true;
-        }
-    }
-}

+ 0 - 48
android/vcmi-app/src/main/java/eu/vcmi/vcmi/settings/LanguageSettingController.java

@@ -1,48 +0,0 @@
-package eu.vcmi.vcmi.settings;
-
-import androidx.appcompat.app.AppCompatActivity;
-
-import eu.vcmi.vcmi.Config;
-import eu.vcmi.vcmi.R;
-
-/**
- * @author F
- */
-public class LanguageSettingController extends LauncherSettingWithDialogController<String, Config>
-{
-    public LanguageSettingController(final AppCompatActivity activity)
-    {
-        super(activity);
-    }
-
-    @Override
-    protected LauncherSettingDialog<String> dialog()
-    {
-        return new LanguageSettingDialog();
-    }
-
-    @Override
-    public void onItemChosen(final String item)
-    {
-        mConfig.updateLanguage(item);
-        updateContent();
-    }
-
-    @Override
-    protected String mainText()
-    {
-        return mActivity.getString(R.string.launcher_btn_language_title);
-    }
-
-    @Override
-    protected String subText()
-    {
-        if (mConfig == null)
-        {
-            return "";
-        }
-        return mConfig.mLanguage == null || mConfig.mLanguage.isEmpty()
-               ? mActivity.getString(R.string.launcher_btn_language_subtitle_unknown)
-               : mActivity.getString(R.string.launcher_btn_language_subtitle, mConfig.mLanguage);
-    }
-}

+ 0 - 55
android/vcmi-app/src/main/java/eu/vcmi/vcmi/settings/LanguageSettingDialog.java

@@ -1,55 +0,0 @@
-package eu.vcmi.vcmi.settings;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import eu.vcmi.vcmi.R;
-
-/**
- * @author F
- */
-public class LanguageSettingDialog extends LauncherSettingDialog<String>
-{
-    private static final List<String> AVAILABLE_LANGUAGES = new ArrayList<>();
-
-    static
-    {
-        AVAILABLE_LANGUAGES.add("english");
-        AVAILABLE_LANGUAGES.add("czech");
-        AVAILABLE_LANGUAGES.add("chinese");
-        AVAILABLE_LANGUAGES.add("finnish");
-        AVAILABLE_LANGUAGES.add("french");
-        AVAILABLE_LANGUAGES.add("german");
-        AVAILABLE_LANGUAGES.add("hungarian");
-        AVAILABLE_LANGUAGES.add("italian");
-        AVAILABLE_LANGUAGES.add("korean");
-        AVAILABLE_LANGUAGES.add("polish");
-        AVAILABLE_LANGUAGES.add("portuguese");
-        AVAILABLE_LANGUAGES.add("russian");
-        AVAILABLE_LANGUAGES.add("spanish");
-        AVAILABLE_LANGUAGES.add("swedish");
-        AVAILABLE_LANGUAGES.add("turkish");
-        AVAILABLE_LANGUAGES.add("ukrainian");
-        AVAILABLE_LANGUAGES.add("vietnamese");
-        AVAILABLE_LANGUAGES.add("other_cp1250");
-        AVAILABLE_LANGUAGES.add("other_cp1251");
-        AVAILABLE_LANGUAGES.add("other_cp1252");
-    }
-
-    public LanguageSettingDialog()
-    {
-        super(AVAILABLE_LANGUAGES);
-    }
-
-    @Override
-    protected int dialogTitleResId()
-    {
-        return R.string.launcher_btn_language_title;
-    }
-
-    @Override
-    protected CharSequence itemName(final String item)
-    {
-        return item;
-    }
-}

+ 0 - 75
android/vcmi-app/src/main/java/eu/vcmi/vcmi/settings/LauncherSettingController.java

@@ -1,75 +0,0 @@
-package eu.vcmi.vcmi.settings;
-
-import androidx.appcompat.app.AppCompatActivity;
-import android.view.View;
-import android.widget.TextView;
-
-import eu.vcmi.vcmi.R;
-
-/**
- * @author F
- */
-public abstract class LauncherSettingController<TSetting, TConf> implements View.OnClickListener
-{
-    protected AppCompatActivity mActivity;
-    protected TConf mConfig;
-    private View mSettingViewRoot;
-    private TextView mSettingsTextMain;
-    private TextView mSettingsTextSub;
-
-    LauncherSettingController(final AppCompatActivity act)
-    {
-        mActivity = act;
-    }
-
-    public final LauncherSettingController<TSetting, TConf> init(final int rootViewResId)
-    {
-        return init(rootViewResId, null);
-    }
-
-    public final LauncherSettingController<TSetting, TConf> init(final int rootViewResId, final TConf config)
-    {
-        mSettingViewRoot = mActivity.findViewById(rootViewResId);
-        mSettingViewRoot.setOnClickListener(this);
-        mSettingsTextMain = (TextView) mSettingViewRoot.findViewById(R.id.inc_launcher_btn_main);
-        mSettingsTextSub = (TextView) mSettingViewRoot.findViewById(R.id.inc_launcher_btn_sub);
-        childrenInit(mSettingViewRoot);
-        updateConfig(config);
-        updateContent();
-        return this;
-    }
-
-    protected void childrenInit(final View root)
-    {
-
-    }
-
-    public void updateConfig(final TConf conf)
-    {
-        mConfig = conf;
-        updateContent();
-    }
-
-    public void updateContent()
-    {
-        mSettingsTextMain.setText(mainText());
-        if (mSettingsTextSub != null)
-        {
-            mSettingsTextSub.setText(subText());
-        }
-    }
-
-    protected abstract String mainText();
-
-    protected abstract String subText();
-
-    public void show()
-    {
-        mSettingViewRoot.setVisibility(View.VISIBLE);
-    }
-
-    public void hide()
-    {
-        mSettingViewRoot.setVisibility(View.GONE);
-    }
-}

+ 0 - 73
android/vcmi-app/src/main/java/eu/vcmi/vcmi/settings/LauncherSettingDialog.java

@@ -1,73 +0,0 @@
-package eu.vcmi.vcmi.settings;
-
-import android.app.Dialog;
-import android.content.DialogInterface;
-import android.os.Bundle;
-import androidx.annotation.NonNull;
-import androidx.fragment.app.DialogFragment;
-import androidx.appcompat.app.AlertDialog;
-
-import java.util.ArrayList;
-
-import java.util.List;
-
-import eu.vcmi.vcmi.util.Log;
-
-/**
- * @author F
- */
-public abstract class LauncherSettingDialog<T> extends DialogFragment
-{
-    protected final List<T> mDataset;
-    private IOnItemChosen<T> mObserver;
-
-    protected LauncherSettingDialog(final List<T> dataset)
-    {
-        mDataset = dataset;
-    }
-
-    public void observe(final IOnItemChosen<T> observer)
-    {
-        mObserver = observer;
-    }
-
-    protected abstract CharSequence itemName(T item);
-
-    protected abstract int dialogTitleResId();
-
-    @NonNull
-    @Override
-    public Dialog onCreateDialog(final Bundle savedInstanceState)
-    {
-        List<CharSequence> list = new ArrayList<>();
-
-        for (T t : mDataset)
-        {
-            CharSequence charSequence = itemName(t);
-            list.add(charSequence);
-        }
-
-        return new AlertDialog.Builder(getActivity())
-            .setTitle(dialogTitleResId())
-            .setItems(
-                    list.toArray(new CharSequence[0]),
-                    this::onItemChosenInternal)
-            .create();
-    }
-
-    private void onItemChosenInternal(final DialogInterface dialog, final int index)
-    {
-        final T chosenItem = mDataset.get(index);
-        Log.d(this, "Chosen item: " + chosenItem);
-        dialog.dismiss();
-        if (mObserver != null)
-        {
-            mObserver.onItemChosen(chosenItem);
-        }
-    }
-
-    public interface IOnItemChosen<V>
-    {
-        void onItemChosen(V item);
-    }
-}

+ 0 - 31
android/vcmi-app/src/main/java/eu/vcmi/vcmi/settings/LauncherSettingWithDialogController.java

@@ -1,31 +0,0 @@
-package eu.vcmi.vcmi.settings;
-
-import androidx.appcompat.app.AppCompatActivity;
-import android.view.View;
-
-import eu.vcmi.vcmi.util.Log;
-
-/**
- * @author F
- */
-public abstract class LauncherSettingWithDialogController<T, Conf> extends LauncherSettingController<T, Conf>
-    implements LauncherSettingDialog.IOnItemChosen<T>
-{
-    public static final String SETTING_DIALOG_ID = "settings.dialog";
-
-    protected LauncherSettingWithDialogController(final AppCompatActivity act)
-    {
-        super(act);
-    }
-
-    @Override
-    public void onClick(final View v)
-    {
-        Log.i(this, "Showing dialog");
-        final LauncherSettingDialog<T> dialog = dialog();
-        dialog.observe(this); // TODO rebinding dialogs on activity config changes
-        dialog.show(mActivity.getSupportFragmentManager(), SETTING_DIALOG_ID);
-    }
-
-    protected abstract LauncherSettingDialog<T> dialog();
-}

+ 0 - 83
android/vcmi-app/src/main/java/eu/vcmi/vcmi/settings/LauncherSettingWithSliderController.java

@@ -1,83 +0,0 @@
-package eu.vcmi.vcmi.settings;
-
-import androidx.appcompat.app.AppCompatActivity;
-import androidx.appcompat.widget.AppCompatSeekBar;
-import android.view.View;
-import android.widget.SeekBar;
-
-import eu.vcmi.vcmi.R;
-
-/**
- * @author F
- */
-public abstract class LauncherSettingWithSliderController<T, Conf> extends LauncherSettingController<T, Conf>
-{
-    private AppCompatSeekBar mSlider;
-    private final int mSliderMin;
-    private final int mSliderMax;
-
-    protected LauncherSettingWithSliderController(final AppCompatActivity act, final int min, final int max)
-    {
-        super(act);
-        mSliderMin = min;
-        mSliderMax = max;
-    }
-
-    @Override
-    protected void childrenInit(final View root)
-    {
-        mSlider = (AppCompatSeekBar) root.findViewById(R.id.inc_launcher_btn_slider);
-        if (mSliderMax <= mSliderMin)
-        {
-            throw new IllegalArgumentException("slider min>=max");
-        }
-        mSlider.setMax(mSliderMax - mSliderMin);
-        mSlider.setOnSeekBarChangeListener(new OnValueChangedListener());
-    }
-
-    protected abstract void onValueChanged(final int v);
-    protected abstract int currentValue();
-
-    @Override
-    public void updateContent()
-    {
-        super.updateContent();
-        mSlider.setProgress(currentValue() + mSliderMin);
-    }
-
-    @Override
-    protected String subText()
-    {
-        return null; // not used with slider settings
-    }
-
-    @Override
-    public void onClick(final View v)
-    {
-        // not used with slider settings
-    }
-
-    private class OnValueChangedListener implements SeekBar.OnSeekBarChangeListener
-    {
-        @Override
-        public void onProgressChanged(final SeekBar seekBar, final int progress, final boolean fromUser)
-        {
-            if (fromUser)
-            {
-                onValueChanged(progress);
-            }
-        }
-
-        @Override
-        public void onStartTrackingTouch(final SeekBar seekBar)
-        {
-
-        }
-
-        @Override
-        public void onStopTrackingTouch(final SeekBar seekBar)
-        {
-
-        }
-    }
-}

+ 0 - 38
android/vcmi-app/src/main/java/eu/vcmi/vcmi/settings/ModsBtnController.java

@@ -1,38 +0,0 @@
-package eu.vcmi.vcmi.settings;
-
-import androidx.appcompat.app.AppCompatActivity;
-import android.view.View;
-
-import eu.vcmi.vcmi.R;
-
-/**
- * @author F
- */
-public class ModsBtnController extends LauncherSettingController<Void, Void>
-{
-    private View.OnClickListener mOnSelectedAction;
-
-    public ModsBtnController(final AppCompatActivity act, final View.OnClickListener onSelectedAction)
-    {
-        super(act);
-        mOnSelectedAction = onSelectedAction;
-    }
-
-    @Override
-    protected String mainText()
-    {
-        return mActivity.getString(R.string.launcher_btn_mods_title);
-    }
-
-    @Override
-    protected String subText()
-    {
-        return mActivity.getString(R.string.launcher_btn_mods_subtitle);
-    }
-
-    @Override
-    public void onClick(final View v)
-    {
-        mOnSelectedAction.onClick(v);
-    }
-}

+ 0 - 40
android/vcmi-app/src/main/java/eu/vcmi/vcmi/settings/MusicSettingController.java

@@ -1,40 +0,0 @@
-package eu.vcmi.vcmi.settings;
-
-import androidx.appcompat.app.AppCompatActivity;
-
-import eu.vcmi.vcmi.Config;
-import eu.vcmi.vcmi.R;
-
-/**
- * @author F
- */
-public class MusicSettingController extends LauncherSettingWithSliderController<Integer, Config>
-{
-    public MusicSettingController(final AppCompatActivity act)
-    {
-        super(act, 0, 100);
-    }
-
-    @Override
-    protected void onValueChanged(final int v)
-    {
-        mConfig.updateMusic(v);
-        updateContent();
-    }
-
-    @Override
-    protected int currentValue()
-    {
-        if (mConfig == null)
-        {
-            return Config.DEFAULT_MUSIC_VALUE;
-        }
-        return mConfig.mVolumeMusic;
-    }
-
-    @Override
-    protected String mainText()
-    {
-        return mActivity.getString(R.string.launcher_btn_music_title);
-    }
-}

+ 0 - 63
android/vcmi-app/src/main/java/eu/vcmi/vcmi/settings/PointerModeSettingController.java

@@ -1,63 +0,0 @@
-package eu.vcmi.vcmi.settings;
-
-import androidx.appcompat.app.AppCompatActivity;
-import eu.vcmi.vcmi.Config;
-import eu.vcmi.vcmi.R;
-
-/**
- * @author F
- */
-public class PointerModeSettingController
-    extends LauncherSettingWithDialogController<PointerModeSettingController.PointerMode, Config>
-{
-    public PointerModeSettingController(final AppCompatActivity activity)
-    {
-        super(activity);
-    }
-
-    @Override
-    protected LauncherSettingDialog<PointerMode> dialog()
-    {
-        return new PointerModeSettingDialog();
-    }
-
-    @Override
-    public void onItemChosen(final PointerMode item)
-    {
-        mConfig.setPointerMode(item == PointerMode.RELATIVE);
-        updateContent();
-    }
-
-    @Override
-    protected String mainText()
-    {
-        return mActivity.getString(R.string.launcher_btn_pointermode_title);
-    }
-
-    @Override
-    protected String subText()
-    {
-        if (mConfig == null)
-        {
-            return "";
-        }
-        return mActivity.getString(R.string.launcher_btn_pointermode_subtitle,
-            PointerModeSettingDialog.pointerModeToUserString(mActivity, getPointerMode()));
-    }
-
-    private PointerMode getPointerMode()
-    {
-        if(mConfig.getPointerModeIsRelative())
-        {
-            return PointerMode.RELATIVE;
-        }
-
-        return PointerMode.NORMAL;
-    }
-
-    public enum PointerMode
-    {
-        NORMAL,
-        RELATIVE;
-    }
-}

+ 0 - 55
android/vcmi-app/src/main/java/eu/vcmi/vcmi/settings/PointerModeSettingDialog.java

@@ -1,55 +0,0 @@
-package eu.vcmi.vcmi.settings;
-
-import android.content.Context;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import eu.vcmi.vcmi.R;
-
-/**
- * @author F
- */
-public class PointerModeSettingDialog extends LauncherSettingDialog<PointerModeSettingController.PointerMode>
-{
-    private static final List<PointerModeSettingController.PointerMode> POINTER_MODES = new ArrayList<>();
-
-    static
-    {
-        POINTER_MODES.add(PointerModeSettingController.PointerMode.NORMAL);
-        POINTER_MODES.add(PointerModeSettingController.PointerMode.RELATIVE);
-    }
-
-    public PointerModeSettingDialog()
-    {
-        super(POINTER_MODES);
-    }
-
-    public static String pointerModeToUserString(
-            final Context ctx,
-            final PointerModeSettingController.PointerMode pointerMode)
-    {
-        switch (pointerMode)
-        {
-            default:
-                return "";
-            case NORMAL:
-                return ctx.getString(R.string.misc_pointermode_normal);
-            case RELATIVE:
-                return ctx.getString(R.string.misc_pointermode_relative);
-        }
-    }
-
-    @Override
-    protected int dialogTitleResId()
-    {
-        return R.string.launcher_btn_pointermode_title;
-    }
-
-    @Override
-    protected CharSequence itemName(final PointerModeSettingController.PointerMode item)
-    {
-        return pointerModeToUserString(getContext(), item);
-    }
-
-}

+ 0 - 51
android/vcmi-app/src/main/java/eu/vcmi/vcmi/settings/PointerMultiplierSettingController.java

@@ -1,51 +0,0 @@
-package eu.vcmi.vcmi.settings;
-
-import androidx.appcompat.app.AppCompatActivity;
-
-import eu.vcmi.vcmi.Config;
-import eu.vcmi.vcmi.R;
-
-/**
- * @author F
- */
-public class PointerMultiplierSettingController
-        extends LauncherSettingWithDialogController<Float, Config>
-{
-    public PointerMultiplierSettingController(final AppCompatActivity activity)
-    {
-        super(activity);
-    }
-
-    @Override
-    protected LauncherSettingDialog<Float> dialog()
-    {
-        return new PointerMultiplierSettingDialog();
-    }
-
-    @Override
-    public void onItemChosen(final Float item)
-    {
-        mConfig.setPointerSpeedMultiplier(item);
-        updateContent();
-    }
-
-    @Override
-    protected String mainText()
-    {
-        return mActivity.getString(R.string.launcher_btn_pointermulti_title);
-    }
-
-    @Override
-    protected String subText()
-    {
-        if (mConfig == null)
-        {
-            return "";
-        }
-
-        String pointerModeString = PointerMultiplierSettingDialog.pointerMultiplierToUserString(
-                mConfig.getPointerSpeedMultiplier());
-
-        return mActivity.getString(R.string.launcher_btn_pointermulti_subtitle, pointerModeString);
-    }
-}

+ 0 - 48
android/vcmi-app/src/main/java/eu/vcmi/vcmi/settings/PointerMultiplierSettingDialog.java

@@ -1,48 +0,0 @@
-package eu.vcmi.vcmi.settings;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Locale;
-
-import eu.vcmi.vcmi.R;
-
-/**
- * @author F
- */
-public class PointerMultiplierSettingDialog extends LauncherSettingDialog<Float>
-{
-    private static final List<Float> AVAILABLE_MULTIPLIERS = new ArrayList<>();
-
-    static
-    {
-        AVAILABLE_MULTIPLIERS.add(1.0f);
-        AVAILABLE_MULTIPLIERS.add(1.25f);
-        AVAILABLE_MULTIPLIERS.add(1.5f);
-        AVAILABLE_MULTIPLIERS.add(1.75f);
-        AVAILABLE_MULTIPLIERS.add(2.0f);
-        AVAILABLE_MULTIPLIERS.add(2.5f);
-        AVAILABLE_MULTIPLIERS.add(3.0f);
-    }
-
-    public PointerMultiplierSettingDialog()
-    {
-        super(AVAILABLE_MULTIPLIERS);
-    }
-
-    @Override
-    protected int dialogTitleResId()
-    {
-        return R.string.launcher_btn_pointermode_title;
-    }
-
-    @Override
-    protected CharSequence itemName(final Float item)
-    {
-        return pointerMultiplierToUserString(item);
-    }
-
-    public static String pointerMultiplierToUserString(final float multiplier)
-    {
-        return String.format(Locale.US, "%.2fx", multiplier);
-    }
-}

+ 0 - 64
android/vcmi-app/src/main/java/eu/vcmi/vcmi/settings/ScreenScaleSettingController.java

@@ -1,64 +0,0 @@
-package eu.vcmi.vcmi.settings;
-
-import androidx.appcompat.app.AppCompatActivity;
-
-import eu.vcmi.vcmi.Config;
-import eu.vcmi.vcmi.R;
-
-/**
- * @author F
- */
-public class ScreenScaleSettingController extends LauncherSettingWithDialogController<ScreenScaleSettingController.ScreenScale, Config>
-{
-    public ScreenScaleSettingController(final AppCompatActivity activity)
-    {
-        super(activity);
-    }
-
-    @Override
-    protected LauncherSettingDialog<ScreenScale> dialog()
-    {
-        return new ScreenScaleSettingDialog(mActivity);
-    }
-
-    @Override
-    public void onItemChosen(final ScreenScale item)
-    {
-        mConfig.updateScreenScale(item.mScreenScale);
-        updateContent();
-    }
-
-    @Override
-    protected String mainText()
-    {
-        return mActivity.getString(R.string.launcher_btn_scale_title);
-    }
-
-    @Override
-    protected String subText()
-    {
-        if (mConfig == null)
-        {
-            return "";
-        }
-        return mConfig.mScreenScale <= 0
-               ? mActivity.getString(R.string.launcher_btn_scale_subtitle_unknown)
-               : mActivity.getString(R.string.launcher_btn_scale_subtitle, mConfig.mScreenScale);
-    }
-
-    public static class ScreenScale
-    {
-        public int mScreenScale;
-
-        public ScreenScale(final int scale)
-        {
-            mScreenScale = scale;
-        }
-
-        @Override
-        public String toString()
-        {
-            return mScreenScale + "%";
-        }
-    }
-}

+ 0 - 98
android/vcmi-app/src/main/java/eu/vcmi/vcmi/settings/ScreenScaleSettingDialog.java

@@ -1,98 +0,0 @@
-package eu.vcmi.vcmi.settings;
-
-import android.app.Activity;
-import android.graphics.Point;
-import android.view.WindowMetrics;
-
-import org.json.JSONArray;
-import org.json.JSONObject;
-import java.io.File;
-import java.util.ArrayDeque;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Queue;
-import eu.vcmi.vcmi.R;
-import eu.vcmi.vcmi.Storage;
-import eu.vcmi.vcmi.util.FileUtil;
-
-/**
- * @author F
- */
-public class ScreenScaleSettingDialog extends LauncherSettingDialog<ScreenScaleSettingController.ScreenScale>
-{
-    public ScreenScaleSettingDialog(Activity mActivity)
-    {
-        super(loadScales(mActivity));
-    }
-
-    @Override
-    protected int dialogTitleResId()
-    {
-        return R.string.launcher_btn_scale_title;
-    }
-
-    @Override
-    protected CharSequence itemName(final ScreenScaleSettingController.ScreenScale item)
-    {
-        return item.toString();
-    }
-
-    public static int[] getSupportedScalingRange(Activity activity) {
-        Point screenRealSize = new Point();
-        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.R) {
-            WindowMetrics windowMetrics = activity.getWindowManager().getCurrentWindowMetrics();
-            screenRealSize.x = windowMetrics.getBounds().width();
-            screenRealSize.y = windowMetrics.getBounds().height();
-        } else {
-            activity.getWindowManager().getDefaultDisplay().getRealSize(screenRealSize);
-        }
-
-        if (screenRealSize.x < screenRealSize.y) {
-            int tmp = screenRealSize.x;
-            screenRealSize.x = screenRealSize.y;
-            screenRealSize.y = tmp;
-        }
-
-        // H3 resolution, any resolution smaller than that is not correctly supported
-        Point minResolution = new Point(800, 600);
-        // arbitrary limit on *downscaling*. Allow some downscaling, if requested by user. Should be generally limited to 100+ for all but few devices
-        double minimalScaling = 50;
-
-        Point renderResolution = screenRealSize;
-        double maximalScalingWidth = 100.0 * renderResolution.x / minResolution.x;
-        double maximalScalingHeight = 100.0 * renderResolution.y / minResolution.y;
-        double maximalScaling = Math.min(maximalScalingWidth, maximalScalingHeight);
-
-        return new int[] { (int)minimalScaling, (int)maximalScaling };
-    }
-
-    private static List<ScreenScaleSettingController.ScreenScale> loadScales(Activity activity)
-    {
-        List<ScreenScaleSettingController.ScreenScale> availableScales = new ArrayList<>();
-
-        try
-        {
-            int[] supportedScalingRange = getSupportedScalingRange(activity);
-            for (int i = 0; i <= supportedScalingRange[1] + 10 - 1; i += 10)
-            {
-                if (i >= supportedScalingRange[0])
-                    availableScales.add(new ScreenScaleSettingController.ScreenScale(i));
-            }
-
-            if(availableScales.isEmpty())
-            {
-                availableScales.add(new ScreenScaleSettingController.ScreenScale(100));
-            }
-        }
-        catch(Exception ex)
-        {
-            ex.printStackTrace();
-
-            availableScales.clear();
-
-            availableScales.add(new ScreenScaleSettingController.ScreenScale(100));
-        }
-
-        return availableScales;
-    }
-}

+ 0 - 40
android/vcmi-app/src/main/java/eu/vcmi/vcmi/settings/SoundSettingController.java

@@ -1,40 +0,0 @@
-package eu.vcmi.vcmi.settings;
-
-import androidx.appcompat.app.AppCompatActivity;
-
-import eu.vcmi.vcmi.Config;
-import eu.vcmi.vcmi.R;
-
-/**
- * @author F
- */
-public class SoundSettingController extends LauncherSettingWithSliderController<Integer, Config>
-{
-    public SoundSettingController(final AppCompatActivity act)
-    {
-        super(act, 0, 100);
-    }
-
-    @Override
-    protected void onValueChanged(final int v)
-    {
-        mConfig.updateSound(v);
-        updateContent();
-    }
-
-    @Override
-    protected int currentValue()
-    {
-        if (mConfig == null)
-        {
-            return Config.DEFAULT_SOUND_VALUE;
-        }
-        return mConfig.mVolumeSound;
-    }
-
-    @Override
-    protected String mainText()
-    {
-        return mActivity.getString(R.string.launcher_btn_sound_title);
-    }
-}

+ 0 - 39
android/vcmi-app/src/main/java/eu/vcmi/vcmi/settings/StartGameController.java

@@ -1,39 +0,0 @@
-package eu.vcmi.vcmi.settings;
-
-import androidx.appcompat.app.AppCompatActivity;
-import android.view.View;
-
-import eu.vcmi.vcmi.R;
-import eu.vcmi.vcmi.util.GeneratedVersion;
-
-/**
- * @author F
- */
-public class StartGameController extends LauncherSettingController<Void, Void>
-{
-    private View.OnClickListener mOnSelectedAction;
-
-    public StartGameController(final AppCompatActivity act, final View.OnClickListener onSelectedAction)
-    {
-        super(act);
-        mOnSelectedAction = onSelectedAction;
-    }
-
-    @Override
-    protected String mainText()
-    {
-        return mActivity.getString(R.string.launcher_btn_start_title);
-    }
-
-    @Override
-    protected String subText()
-    {
-        return mActivity.getString(R.string.launcher_btn_start_subtitle, GeneratedVersion.VCMI_VERSION);
-    }
-
-    @Override
-    public void onClick(final View v)
-    {
-        mOnSelectedAction.onClick(v);
-    }
-}

+ 0 - 49
android/vcmi-app/src/main/java/eu/vcmi/vcmi/util/AsyncRequest.java

@@ -1,49 +0,0 @@
-package eu.vcmi.vcmi.util;
-
-import android.annotation.TargetApi;
-import android.os.AsyncTask;
-import android.os.Build;
-import androidx.annotation.RequiresApi;
-
-import java.net.HttpURLConnection;
-import java.net.URL;
-import java.util.Scanner;
-
-import eu.vcmi.vcmi.Const;
-
-/**
- * @author F
- */
-public abstract class AsyncRequest<T> extends AsyncTask<String, Void, ServerResponse<T>>
-{
-    @TargetApi(Const.SUPPRESS_TRY_WITH_RESOURCES_WARNING)
-    protected ServerResponse<T> sendRequest(final String url)
-    {
-
-        try
-        {
-            final HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection();
-            final int responseCode = conn.getResponseCode();
-            if (!ServerResponse.isResponseCodeValid(responseCode))
-            {
-                return new ServerResponse<>(responseCode, null);
-            }
-
-            try (Scanner s = new java.util.Scanner(conn.getInputStream()).useDelimiter("\\A"))
-            {
-                final String response = s.hasNext() ? s.next() : "";
-                return new ServerResponse<>(responseCode, response);
-            }
-            catch (final Exception e)
-            {
-                Log.e(this, "Request failed: ", e);
-            }
-        }
-        catch (final Exception e)
-        {
-            Log.e(this, "Request failed: ", e);
-        }
-        return new ServerResponse<>(ServerResponse.LOCAL_ERROR_IO, null);
-    }
-
-}

+ 54 - 297
android/vcmi-app/src/main/java/eu/vcmi/vcmi/util/FileUtil.java

@@ -1,350 +1,107 @@
 package eu.vcmi.vcmi.util;
 
-import android.annotation.TargetApi;
-import android.content.res.AssetManager;
-import android.os.Environment;
-import android.text.TextUtils;
+import android.app.Activity;
+import android.net.Uri;
+
+import androidx.annotation.Nullable;
+import androidx.documentfile.provider.DocumentFile;
 
 import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
-import java.io.FileReader;
-import java.io.FileWriter;
 import java.io.IOException;
 import java.io.InputStream;
-import java.io.InputStreamReader;
 import java.io.OutputStream;
-import java.nio.charset.Charset;
-import java.util.zip.ZipEntry;
-import java.util.zip.ZipFile;
-import java.util.zip.ZipInputStream;
+import java.util.List;
 
-import eu.vcmi.vcmi.Const;
+import eu.vcmi.vcmi.Storage;
 
 /**
  * @author F
  */
-@TargetApi(Const.SUPPRESS_TRY_WITH_RESOURCES_WARNING)
 public class FileUtil
 {
     private static final int BUFFER_SIZE = 4096;
 
-    public static String read(final InputStream stream) throws IOException
+    public static boolean copyData(Uri folderToCopy, Activity activity)
     {
-        try (InputStreamReader reader = new InputStreamReader(stream))
-        {
-            return readInternal(reader);
-        }
+        File targetDir = Storage.getVcmiDataDir(activity);
+        DocumentFile sourceDir = DocumentFile.fromTreeUri(activity, folderToCopy);
+        return copyDirectory(targetDir, sourceDir, List.of("Data", "Maps", "Mp3"), activity);
     }
 
-    public static String read(final File file) throws IOException
+    private static boolean copyDirectory(File targetDir, DocumentFile sourceDir, @Nullable List<String> allowed, Activity activity)
     {
-        try (FileReader reader = new FileReader(file))
-        {
-            return readInternal(reader);
-        }
-        catch (final FileNotFoundException ignored)
+        if (!targetDir.exists())
         {
-            Log.w("Could not load file: " + file);
-            return null;
+            targetDir.mkdir();
         }
-    }
 
-    private static String readInternal(final InputStreamReader reader) throws IOException
-    {
-        final char[] buffer = new char[BUFFER_SIZE];
-        int currentRead;
-        final StringBuilder content = new StringBuilder();
-        while ((currentRead = reader.read(buffer, 0, BUFFER_SIZE)) >= 0)
-        {
-            content.append(buffer, 0, currentRead);
-        }
-        return content.toString();
-    }
-
-    public static void write(final File file, final String data) throws IOException
-    {
-        if (!ensureWriteable(file))
-        {
-            Log.e("Couldn't write " + data + " to " + file);
-            return;
-        }
-        try (final FileWriter fw = new FileWriter(file, false))
-        {
-            Log.v(null, "Saving data: " + data + " to " + file.getAbsolutePath());
-            fw.write(data);
-        }
-    }
-
-    private static boolean ensureWriteable(final File file)
-    {
-        if (file == null)
+        for (DocumentFile child : sourceDir.listFiles())
         {
-            Log.e("Broken path given to fileutil::ensureWriteable");
-            return false;
-        }
-
-        final File dir = file.getParentFile();
-
-        if (dir.exists() || dir.mkdirs())
-        {
-            return true;
-        }
-
-        Log.e("Couldn't create dir " + dir);
-
-        return false;
-    }
-
-    public static boolean clearDirectory(final File dir)
-    {
-        if (dir == null || dir.listFiles() == null)
-        {
-            Log.e("Broken path given to fileutil::clearDirectory");
-            return false;
-        }
-
-        for (final File f : dir.listFiles())
-        {
-            if (f.isDirectory() && !clearDirectory(f))
-            {
-                return false;
-            }
-
-            if (!f.delete())
+            if (allowed != null)
             {
-                return false;
-            }
-        }
-        return true;
-    }
-
-    public static void copyDir(final File srcFile, final File dstFile)
-    {
-        File[] files = srcFile.listFiles();
-
-        if(!dstFile.exists()) dstFile.mkdir();
-
-        if(files == null)
-            return;
-
-        for (File child : files){
-            File childTarget = new File(dstFile, child.getName());
-
-            if(child.isDirectory()){
-                copyDir(child, childTarget);
-            }
-            else{
-                copyFile(child, childTarget);
-            }
-        }
-    }
-
-    public static boolean copyFile(final File srcFile, final File dstFile)
-    {
-        if (!srcFile.exists())
-        {
-            return false;
-        }
-
-        final File dstDir = dstFile.getParentFile();
-        if (!dstDir.exists())
-        {
-            if (!dstDir.mkdirs())
-            {
-                Log.w("Couldn't create dir to copy file: " + dstFile);
-                return false;
-            }
-        }
-
-        try (final FileInputStream input = new FileInputStream(srcFile);
-             final FileOutputStream output = new FileOutputStream(dstFile))
-        {
-            copyStream(input, output);
-            return true;
-        }
-        catch (final Exception ex)
-        {
-            Log.e("Couldn't copy " + srcFile + " to " + dstFile, ex);
-            return false;
-        }
-    }
-
-    public static void copyStream(InputStream source, OutputStream target) throws IOException
-    {
-        final byte[] buffer = new byte[BUFFER_SIZE];
-        int read;
-        while ((read = source.read(buffer)) != -1)
-        {
-            target.write(buffer, 0, read);
-        }
-    }
-
-    // (when internal data have changed)
-    public static boolean reloadVcmiDataToInternalDir(final File vcmiInternalDir, final AssetManager assets)
-    {
-        return clearDirectory(vcmiInternalDir) && unpackVcmiDataToInternalDir(vcmiInternalDir, assets);
-    }
+                boolean fileAllowed = false;
 
-    public static boolean unpackVcmiDataToInternalDir(final File vcmiInternalDir, final AssetManager assets)
-    {
-        try
-        {
-            final InputStream inputStream = assets.open("internalData.zip");
-            final boolean success = unpackZipFile(inputStream, vcmiInternalDir, null);
-            inputStream.close();
-            return success;
-        }
-        catch (final Exception e)
-        {
-            Log.e("Couldn't extract vcmi data to internal dir", e);
-            return false;
-        }
-    }
-
-    public static boolean unpackZipFile(
-            final File inputFile,
-            final File destDir,
-            IZipProgressReporter progressReporter)
-    {
-        try
-        {
-            final InputStream inputStream = new FileInputStream(inputFile);
-            final boolean success = unpackZipFile(
-                    inputStream,
-                    destDir,
-                    progressReporter);
-
-            inputStream.close();
-
-            return success;
-        }
-        catch (final Exception e)
-        {
-            Log.e("Couldn't extract file to " + destDir, e);
-            return false;
-        }
-    }
-
-    public static int countFilesInZip(final File zipFile)
-    {
-        int totalEntries = 0;
-
-        try
-        {
-            final InputStream inputStream = new FileInputStream(zipFile);
-            ZipInputStream is = new ZipInputStream(inputStream);
-            ZipEntry zipEntry;
+                for (String str : allowed)
+                {
+                    if (str.equalsIgnoreCase(child.getName()))
+                    {
+                        fileAllowed = true;
+                        break;
+                    }
+                }
 
-            while ((zipEntry = is.getNextEntry()) != null)
-            {
-                totalEntries++;
+                if (!fileAllowed)
+                    continue;
             }
 
-            is.closeEntry();
-            is.close();
-            inputStream.close();
-        }
-        catch (final Exception e)
-        {
-            Log.e("Couldn't count items in zip", e);
-        }
-
-        return totalEntries;
-    }
+            File exported = new File(targetDir, child.getName());
 
-    public static boolean unpackZipFile(
-        final InputStream inputStream,
-        final File destDir,
-        final IZipProgressReporter progressReporter)
-    {
-        try
-        {
-            int unpackedEntries = 0;
-            final byte[] buffer = new byte[BUFFER_SIZE];
-
-            ZipInputStream is = new ZipInputStream(inputStream);
-            ZipEntry zipEntry;
-
-            while ((zipEntry = is.getNextEntry()) != null)
+            if (child.isFile())
             {
-                final String fileName = zipEntry.getName();
-                final File newFile = new File(destDir, fileName);
-
-                if (newFile.exists())
+                if (!exported.exists())
                 {
-                    Log.d("Already exists: " + newFile.getName());
-                    continue;
-                }
-                else if (zipEntry.isDirectory())
-                {
-                    Log.v("Creating new dir: " + zipEntry);
-                    if (!newFile.mkdirs())
+                    try
+                    {
+                        exported.createNewFile();
+                    }
+                    catch (IOException e)
                     {
-                        Log.e("Couldn't create directory " + newFile.getAbsolutePath());
+                        Log.e(activity, "createNewFile failed: " + e);
                         return false;
                     }
-                    continue;
                 }
 
-                final File parentFile = new File(newFile.getParent());
-                if (!parentFile.exists() && !parentFile.mkdirs())
+                try (
+                    final OutputStream targetStream = new FileOutputStream(exported, false);
+                    final InputStream sourceStream = activity.getContentResolver()
+                            .openInputStream(child.getUri()))
                 {
-                    Log.e("Couldn't create directory " + parentFile.getAbsolutePath());
-                    return false;
+                    copyStream(sourceStream, targetStream);
                 }
-
-                final FileOutputStream fos = new FileOutputStream(newFile, false);
-
-                int currentRead;
-                while ((currentRead = is.read(buffer)) > 0)
-                {
-                    fos.write(buffer, 0, currentRead);
-                }
-
-                fos.flush();
-                fos.close();
-                ++unpackedEntries;
-
-                if(progressReporter != null)
+                catch (IOException e)
                 {
-                    progressReporter.onUnpacked(newFile);
+                    Log.e(activity, "copyStream failed: " + e);
+                    return false;
                 }
             }
-            Log.d("Unpacked data (" + unpackedEntries + " entries)");
 
-            is.closeEntry();
-            is.close();
-            return true;
-        }
-        catch (final Exception e)
-        {
-            Log.e("Couldn't extract vcmi data to " + destDir, e);
-            return false;
+            if (child.isDirectory() && !copyDirectory(exported, child, null, activity))
+            {
+                return false;
+            }
         }
-    }
 
-    public static String configFileLocation(File filesDir)
-    {
-        return filesDir + "/config/settings.json";
+        return true;
     }
 
-    public static String readAssetsStream(final AssetManager assets, final String assetPath)
+    private static void copyStream(InputStream source, OutputStream target) throws IOException
     {
-        if (assets == null || TextUtils.isEmpty(assetPath))
-        {
-            return null;
-        }
-
-        try (java.util.Scanner s = new java.util.Scanner(assets.open(assetPath), "UTF-8").useDelimiter("\\A"))
-        {
-            return s.hasNext() ? s.next() : null;
-        }
-        catch (final IOException e)
+        final byte[] buffer = new byte[BUFFER_SIZE];
+        int read;
+        while ((read = source.read(buffer)) != -1)
         {
-            Log.e("Couldn't read stream data", e);
-            return null;
+            target.write(buffer, 0, read);
         }
     }
 }

+ 0 - 8
android/vcmi-app/src/main/java/eu/vcmi/vcmi/util/IZipProgressReporter.java

@@ -1,8 +0,0 @@
-package eu.vcmi.vcmi.util;
-
-import java.io.File;
-
-public interface IZipProgressReporter
-{
-    void onUnpacked(File newFile);
-}

+ 0 - 198
android/vcmi-app/src/main/java/eu/vcmi/vcmi/util/InstallModAsync.java

@@ -1,198 +0,0 @@
-package eu.vcmi.vcmi.util;
-
-import android.content.Context;
-import android.nfc.FormatException;
-import android.os.AsyncTask;
-import android.os.Build;
-import android.util.Log;
-
-import java.io.BufferedInputStream;
-import java.io.File;
-import java.io.FileFilter;
-import java.io.FileOutputStream;
-import java.io.InputStream;
-import java.net.URL;
-import java.net.URLConnection;
-
-public class InstallModAsync
-        extends AsyncTask<String, String, Boolean>
-        implements IZipProgressReporter
-{
-    private static final String TAG = "DOWNLOADFILE";
-    private static final int DOWNLOAD_PERCENT = 70;
-
-    private PostDownload callback;
-    private File downloadLocation;
-    private File extractLocation;
-    private Context context;
-    private int totalFiles;
-    private int unpackedFiles;
-
-    public InstallModAsync(File extractLocation, Context context, PostDownload callback)
-    {
-        this.context = context;
-        this.callback = callback;
-        this.extractLocation = extractLocation;
-    }
-
-    @Override
-    protected Boolean doInBackground(String... args)
-    {
-        int count;
-
-        try
-        {
-            File modsFolder = extractLocation.getParentFile();
-
-            if (!modsFolder.exists()) modsFolder.mkdir();
-
-            this.downloadLocation = File.createTempFile("tmp", ".zip", modsFolder);
-
-            URL url = new URL(args[0]);
-            URLConnection connection = url.openConnection();
-            connection.connect();
-
-            long lengthOfFile = -1;
-
-            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
-            {
-                lengthOfFile = connection.getContentLengthLong();
-            }
-
-            if(lengthOfFile == -1)
-            {
-                try
-                {
-                    lengthOfFile = Long.parseLong(connection.getHeaderField("Content-Length"));
-                    Log.d(TAG, "Length of the file: " + lengthOfFile);
-                } catch (NumberFormatException ex)
-                {
-                    Log.d(TAG, "Failed to parse content length", ex);
-                }
-            }
-
-            if(lengthOfFile == -1)
-            {
-                lengthOfFile = 100000000;
-                Log.d(TAG, "Using dummy length of file");
-            }
-
-            InputStream input = new BufferedInputStream(url.openStream());
-            FileOutputStream output = new FileOutputStream(downloadLocation); //context.openFileOutput("content.zip", Context.MODE_PRIVATE);
-            Log.d(TAG, "file saved at " + downloadLocation.getAbsolutePath());
-
-            byte data[] = new byte[1024];
-            long total = 0;
-            while ((count = input.read(data)) != -1)
-            {
-                total += count;
-                output.write(data, 0, count);
-                this.publishProgress((int) ((total * DOWNLOAD_PERCENT) / lengthOfFile) + "%");
-            }
-
-            output.flush();
-            output.close();
-            input.close();
-
-            File tempDir = File.createTempFile("tmp", "", modsFolder);
-
-            tempDir.delete();
-            tempDir.mkdir();
-
-            if (!extractLocation.exists()) extractLocation.mkdir();
-
-            try
-            {
-                totalFiles = FileUtil.countFilesInZip(downloadLocation);
-                unpackedFiles = 0;
-
-                FileUtil.unpackZipFile(downloadLocation, tempDir, this);
-
-                return moveModToExtractLocation(tempDir);
-            }
-            finally
-            {
-                downloadLocation.delete();
-                FileUtil.clearDirectory(tempDir);
-                tempDir.delete();
-            }
-        } catch (Exception e)
-        {
-            Log.e(TAG, "Unhandled exception while installing mod", e);
-        }
-
-        return false;
-    }
-
-    @Override
-    protected void onProgressUpdate(String... values)
-    {
-        callback.downloadProgress(values);
-    }
-
-    @Override
-    protected void onPostExecute(Boolean result)
-    {
-        if (callback != null) callback.downloadDone(result, extractLocation);
-    }
-
-    private boolean moveModToExtractLocation(File tempDir)
-    {
-        return moveModToExtractLocation(tempDir, 0);
-    }
-
-    private boolean moveModToExtractLocation(File tempDir, int level)
-    {
-        File[] modJson = tempDir.listFiles(new FileFilter()
-        {
-            @Override
-            public boolean accept(File file)
-            {
-                return file.getName().equalsIgnoreCase("Mod.json");
-            }
-        });
-
-        if (modJson != null && modJson.length > 0)
-        {
-            File modFolder = modJson[0].getParentFile();
-
-            if (!modFolder.renameTo(extractLocation))
-            {
-                FileUtil.copyDir(modFolder, extractLocation);
-            }
-
-            return true;
-        }
-
-        if (level <= 1)
-        {
-            for (File child : tempDir.listFiles())
-            {
-                if (child.isDirectory() && moveModToExtractLocation(child, level + 1))
-                {
-                    return true;
-                }
-            }
-        }
-
-        return false;
-    }
-
-    @Override
-    public void onUnpacked(File newFile)
-    {
-        unpackedFiles++;
-
-        int progress = DOWNLOAD_PERCENT
-                + (unpackedFiles * (100 - DOWNLOAD_PERCENT) / totalFiles);
-
-        publishProgress(progress + "%");
-    }
-
-    public interface PostDownload
-    {
-        void downloadDone(Boolean succeed, File modFolder);
-
-        void downloadProgress(String... progress);
-    }
-}

+ 4 - 1
android/vcmi-app/src/main/java/eu/vcmi/vcmi/util/LibsLoader.java

@@ -12,9 +12,12 @@ import eu.vcmi.vcmi.NativeMethods;
  */
 public final class LibsLoader
 {
+    public static final String CLIENT_LIB = "vcmiclient_"
+        + (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP ? Build.SUPPORTED_ABIS[0] : Build.CPU_ABI);
+
     public static void loadClientLibs(Context ctx)
     {
-        SDL.loadLibrary("vcmiclient");
+        SDL.loadLibrary(CLIENT_LIB);
         SDL.setContext(ctx);
     }
 

+ 0 - 41
android/vcmi-app/src/main/java/eu/vcmi/vcmi/util/Log.java

@@ -1,15 +1,6 @@
 package eu.vcmi.vcmi.util;
 
-import android.os.Environment;
-
-import java.io.BufferedWriter;
-import java.io.FileWriter;
-import java.io.IOException;
-import java.text.DateFormat;
-import java.util.Date;
-
 import eu.vcmi.vcmi.BuildConfig;
-import eu.vcmi.vcmi.Const;
 
 /**
  * @author F
@@ -18,8 +9,6 @@ import eu.vcmi.vcmi.Const;
 public class Log
 {
     private static final boolean LOGGING_ENABLED_CONSOLE = BuildConfig.DEBUG;
-    private static final boolean LOGGING_ENABLED_FILE = true;
-    private static final String FILELOG_PATH = "/" + Const.VCMI_DATA_ROOT_FOLDER_NAME + "/cache/VCMI_launcher.log";
     private static final String TAG_PREFIX = "VCMI/";
     private static final String STATIC_TAG = "static";
 
@@ -34,19 +23,6 @@ public class Log
         {
             android.util.Log.println(priority, TAG_PREFIX + tagString, msg);
         }
-        if (LOGGING_ENABLED_FILE) // this is probably very inefficient, but should be enough for now...
-        {
-            try
-            {
-                final BufferedWriter fileWriter = new BufferedWriter(new FileWriter(Environment.getExternalStorageDirectory() + FILELOG_PATH, true));
-                fileWriter.write(String.format("[%s] %s: %s\n", formatPriority(priority), tagString, msg));
-                fileWriter.flush();
-                fileWriter.close();
-            }
-            catch (IOException ignored)
-            {
-            }
-        }
     }
 
     private static String formatPriority(final int priority)
@@ -77,23 +53,6 @@ public class Log
         return obj.getClass().getSimpleName();
     }
 
-    public static void init()
-    {
-        if (LOGGING_ENABLED_FILE) // clear previous log
-        {
-            try
-            {
-                final BufferedWriter fileWriter = new BufferedWriter(new FileWriter(Environment.getExternalStorageDirectory() + FILELOG_PATH, false));
-                fileWriter.write("Starting VCMI launcher log, " + DateFormat.getDateTimeInstance().format(new Date()) + "\n");
-                fileWriter.flush();
-                fileWriter.close();
-            }
-            catch (IOException ignored)
-            {
-            }
-        }
-    }
-
     public static void v(final String msg)
     {
         logInternal(android.util.Log.VERBOSE, STATIC_TAG, msg);

+ 0 - 30
android/vcmi-app/src/main/java/eu/vcmi/vcmi/util/ServerResponse.java

@@ -1,30 +0,0 @@
-package eu.vcmi.vcmi.util;
-
-/**
- * @author F
- */
-public class ServerResponse<T>
-{
-    public static final int LOCAL_ERROR_IO = -1;
-    public static final int LOCAL_ERROR_PARSING = -2;
-
-    public int mCode;
-    public String mRawContent;
-    public T mContent;
-
-    public ServerResponse(final int code, final String content)
-    {
-        mCode = code;
-        mRawContent = content;
-    }
-
-    public static boolean isResponseCodeValid(final int responseCode)
-    {
-        return responseCode >= 200 && responseCode < 300;
-    }
-
-    public boolean isValid()
-    {
-        return isResponseCodeValid(mCode);
-    }
-}

+ 0 - 92
android/vcmi-app/src/main/java/eu/vcmi/vcmi/util/SharedPrefs.java

@@ -1,92 +0,0 @@
-package eu.vcmi.vcmi.util;
-
-import android.content.Context;
-import android.content.SharedPreferences;
-import androidx.annotation.NonNull;
-
-/**
- * simple shared preferences wrapper
- *
- * @author F
- */
-public class SharedPrefs
-{
-    public static final String KEY_CURRENT_INTERNAL_ASSET_HASH = "KEY_CURRENT_INTERNAL_ASSET_HASH"; // [string]
-    private static final String VCMI_PREFS_NAME = "VCMIPrefs";
-    private final SharedPreferences mPrefs;
-
-    public SharedPrefs(final Context ctx)
-    {
-        mPrefs = ctx.getSharedPreferences(VCMI_PREFS_NAME, Context.MODE_PRIVATE);
-    }
-
-    public void save(final String name, final String value)
-    {
-        mPrefs.edit().putString(name, value).apply();
-        log(name, value, true);
-    }
-
-    public String load(final String name, final String defaultValue)
-    {
-        return log(name, mPrefs.getString(name, defaultValue), false);
-    }
-
-    public void save(final String name, final int value)
-    {
-        mPrefs.edit().putInt(name, value).apply();
-        log(name, value, true);
-    }
-
-    public int load(final String name, final int defaultValue)
-    {
-        return log(name, mPrefs.getInt(name, defaultValue), false);
-    }
-
-    public void save(final String name, final float value)
-    {
-        mPrefs.edit().putFloat(name, value).apply();
-        log(name, value, true);
-    }
-
-    public float load(final String name, final float defaultValue)
-    {
-        return log(name, mPrefs.getFloat(name, defaultValue), false);
-    }
-
-    public void save(final String name, final boolean value)
-    {
-        mPrefs.edit().putBoolean(name, value).apply();
-        log(name, value, true);
-    }
-
-    public boolean load(final String name, final boolean defaultValue)
-    {
-        return log(name, mPrefs.getBoolean(name, defaultValue), false);
-    }
-
-    public <T extends Enum<T>> void saveEnum(final String name, final T value)
-    {
-        mPrefs.edit().putInt(name, value.ordinal()).apply();
-        log(name, value, true);
-    }
-
-    @SuppressWarnings("unchecked")
-    public <T extends Enum<T>> T loadEnum(final String name, @NonNull final T defaultValue)
-    {
-        final int rawValue = mPrefs.getInt(name, defaultValue.ordinal());
-        return (T) log(name, defaultValue.getClass().getEnumConstants()[rawValue], false);
-    }
-
-    private <T> T log(final String key, final T value, final boolean saving)
-    {
-        if (saving)
-        {
-            Log.v(this, "[prefs saving] " + key + " => " + value);
-        }
-        else
-        {
-            Log.v(this, "[prefs loading] " + key + " => " + value);
-        }
-        return value;
-    }
-}

+ 0 - 58
android/vcmi-app/src/main/java/eu/vcmi/vcmi/util/Utils.java

@@ -1,58 +0,0 @@
-package eu.vcmi.vcmi.util;
-
-import android.content.Context;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager;
-import android.content.res.Resources;
-import android.util.DisplayMetrics;
-
-/**
- * @author F
- */
-
-public final class Utils
-{
-    private static String sAppVersionCache;
-
-    private Utils()
-    {
-    }
-
-    public static String appVersionName(final Context ctx)
-    {
-        if (sAppVersionCache == null)
-        {
-            final PackageManager pm = ctx.getPackageManager();
-            try
-            {
-                final PackageInfo info = pm.getPackageInfo(ctx.getPackageName(), PackageManager.GET_META_DATA);
-                return sAppVersionCache = info.versionName;
-            }
-            catch (final PackageManager.NameNotFoundException e)
-            {
-                Log.e(ctx, "Couldn't resolve app version", e);
-            }
-        }
-        return sAppVersionCache;
-    }
-
-    public static float convertDpToPx(final Context ctx, final float dp)
-    {
-        return convertDpToPx(ctx.getResources(), dp);
-    }
-
-    public static float convertDpToPx(final Resources res, final float dp)
-    {
-        return dp * res.getDisplayMetrics().density;
-    }
-
-    public static float convertPxToDp(final Context ctx, final float px)
-    {
-        return convertPxToDp(ctx.getResources(), px);
-    }
-
-    public static float convertPxToDp(final Resources res, final float px)
-    {
-        return px / res.getDisplayMetrics().density;
-    }
-}

+ 0 - 52
android/vcmi-app/src/main/java/eu/vcmi/vcmi/viewmodels/ObservableViewModel.java

@@ -1,52 +0,0 @@
-package eu.vcmi.vcmi.viewmodels;
-
-import android.view.View;
-
-import androidx.lifecycle.ViewModel;
-import androidx.databinding.PropertyChangeRegistry;
-import androidx.databinding.Observable;
-
-/**
- * @author F
- */
-public class ObservableViewModel extends ViewModel implements Observable
-{
-    private PropertyChangeRegistry callbacks = new PropertyChangeRegistry();
-
-    @Override
-    public void addOnPropertyChangedCallback(
-            Observable.OnPropertyChangedCallback callback)
-    {
-        callbacks.add(callback);
-    }
-
-    @Override
-    public void removeOnPropertyChangedCallback(
-            Observable.OnPropertyChangedCallback callback)
-    {
-        callbacks.remove(callback);
-    }
-
-    public int visible(boolean isVisible)
-    {
-        return isVisible ? View.VISIBLE : View.GONE;
-    }
-
-    /**
-     * Notifies observers that all properties of this instance have changed.
-     */
-    void notifyChange() {
-        callbacks.notifyCallbacks(this, 0, null);
-    }
-
-    /**
-     * Notifies observers that a specific property has changed. The getter for the
-     * property that changes should be marked with the @Bindable annotation to
-     * generate a field in the BR class to be used as the fieldId parameter.
-     *
-     * @param fieldId The generated BR id for the Bindable field.
-     */
-    void notifyPropertyChanged(int fieldId) {
-        callbacks.notifyCallbacks(this, fieldId, null);
-    }
-}

+ 9 - 2
android/vcmi-app/src/main/java/org/libsdl/app/SDLActivity.java

@@ -61,7 +61,7 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh
     private static final String TAG = "SDL";
     private static final int SDL_MAJOR_VERSION = 2;
     private static final int SDL_MINOR_VERSION = 26;
-    private static final int SDL_MICRO_VERSION = 1;
+    private static final int SDL_MICRO_VERSION = 5;
 /*
     // Display InputType.SOURCE/CLASS of events and devices
     //
@@ -241,7 +241,14 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh
      * It can be overridden by derived classes.
      */
     protected String getMainSharedObject() {
-        return null;
+        String library;
+        String[] libraries = SDLActivity.mSingleton.getLibraries();
+        if (libraries.length > 0) {
+            library = "lib" + libraries[libraries.length - 1] + ".so";
+        } else {
+            library = "libmain.so";
+        }
+        return getContext().getApplicationInfo().nativeLibraryDir + "/" + library;
     }
 
     /**

+ 26 - 0
android/vcmi-app/src/main/java/org/libsdl/app/SDLControllerManager.java

@@ -168,6 +168,32 @@ class SDLJoystickHandler_API16 extends SDLJoystickHandler {
                 arg1Axis = MotionEvent.AXIS_GAS;
             }
 
+            // Make sure the AXIS_Z is sorted between AXIS_RY and AXIS_RZ.
+            // This is because the usual pairing are:
+            // - AXIS_X + AXIS_Y (left stick).
+            // - AXIS_RX, AXIS_RY (sometimes the right stick, sometimes triggers).
+            // - AXIS_Z, AXIS_RZ (sometimes the right stick, sometimes triggers).
+            // This sorts the axes in the above order, which tends to be correct
+            // for Xbox-ish game pads that have the right stick on RX/RY and the
+            // triggers on Z/RZ.
+            //
+            // Gamepads that don't have AXIS_Z/AXIS_RZ but use
+            // AXIS_LTRIGGER/AXIS_RTRIGGER are unaffected by this.
+            //
+            // References:
+            // - https://developer.android.com/develop/ui/views/touch-and-input/game-controllers/controller-input
+            // - https://www.kernel.org/doc/html/latest/input/gamepad.html
+            if (arg0Axis == MotionEvent.AXIS_Z) {
+                arg0Axis = MotionEvent.AXIS_RZ - 1;
+            } else if (arg0Axis > MotionEvent.AXIS_Z && arg0Axis < MotionEvent.AXIS_RZ) {
+                --arg0Axis;
+            }
+            if (arg1Axis == MotionEvent.AXIS_Z) {
+                arg1Axis = MotionEvent.AXIS_RZ - 1;
+            } else if (arg1Axis > MotionEvent.AXIS_Z && arg1Axis < MotionEvent.AXIS_RZ) {
+                --arg1Axis;
+            }
+
             return arg0Axis - arg1Axis;
         }
     }

BIN
android/vcmi-app/src/main/res/drawable-nodpi/divider_compat.png


+ 0 - 30
android/vcmi-app/src/main/res/drawable-v24/ic_launcher_foreground.xml

@@ -1,30 +0,0 @@
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:aapt="http://schemas.android.com/aapt"
-    android:width="108dp"
-    android:height="108dp"
-    android:viewportHeight="108"
-    android:viewportWidth="108">
-    <path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z">
-        <aapt:attr name="android:fillColor">
-            <gradient
-                android:endX="85.84757"
-                android:endY="92.4963"
-                android:startX="42.9492"
-                android:startY="49.59793"
-                android:type="linear">
-                <item
-                    android:color="#44000000"
-                    android:offset="0.0" />
-                <item
-                    android:color="#00000000"
-                    android:offset="1.0" />
-            </gradient>
-        </aapt:attr>
-    </path>
-    <path
-        android:fillColor="#FFFFFF"
-        android:fillType="nonZero"
-        android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z"
-        android:strokeColor="#00000000"
-        android:strokeWidth="1" />
-</vector>

+ 0 - 4
android/vcmi-app/src/main/res/drawable/compat_toolbar_shadow.xml

@@ -1,4 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<shape xmlns:android="http://schemas.android.com/apk/res/android">
-    <gradient android:startColor="#000000" android:endColor="#00000000" android:angle="270"/>
-</shape>

+ 0 - 9
android/vcmi-app/src/main/res/drawable/ic_error.xml

@@ -1,9 +0,0 @@
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="24dp"
-    android:height="24dp"
-    android:viewportHeight="24.0"
-    android:viewportWidth="24.0">
-    <path
-        android:fillColor="#FFFFFFFF"
-        android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM13,17h-2v-2h2v2zM13,13h-2L11,7h2v6z" />
-</vector>

+ 0 - 9
android/vcmi-app/src/main/res/drawable/ic_info.xml

@@ -1,9 +0,0 @@
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="24dp"
-    android:height="24dp"
-    android:viewportHeight="24.0"
-    android:viewportWidth="24.0">
-    <path
-        android:fillColor="#FFFFFFFF"
-        android:pathData="M11,17h2v-6h-2v6zM12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,20c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8zM11,9h2L13,7h-2v2z" />
-</vector>

+ 0 - 170
android/vcmi-app/src/main/res/drawable/ic_launcher_background.xml

@@ -1,170 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="108dp"
-    android:height="108dp"
-    android:viewportHeight="108"
-    android:viewportWidth="108">
-    <path
-        android:fillColor="#3DDC84"
-        android:pathData="M0,0h108v108h-108z" />
-    <path
-        android:fillColor="#00000000"
-        android:pathData="M9,0L9,108"
-        android:strokeColor="#33FFFFFF"
-        android:strokeWidth="0.8" />
-    <path
-        android:fillColor="#00000000"
-        android:pathData="M19,0L19,108"
-        android:strokeColor="#33FFFFFF"
-        android:strokeWidth="0.8" />
-    <path
-        android:fillColor="#00000000"
-        android:pathData="M29,0L29,108"
-        android:strokeColor="#33FFFFFF"
-        android:strokeWidth="0.8" />
-    <path
-        android:fillColor="#00000000"
-        android:pathData="M39,0L39,108"
-        android:strokeColor="#33FFFFFF"
-        android:strokeWidth="0.8" />
-    <path
-        android:fillColor="#00000000"
-        android:pathData="M49,0L49,108"
-        android:strokeColor="#33FFFFFF"
-        android:strokeWidth="0.8" />
-    <path
-        android:fillColor="#00000000"
-        android:pathData="M59,0L59,108"
-        android:strokeColor="#33FFFFFF"
-        android:strokeWidth="0.8" />
-    <path
-        android:fillColor="#00000000"
-        android:pathData="M69,0L69,108"
-        android:strokeColor="#33FFFFFF"
-        android:strokeWidth="0.8" />
-    <path
-        android:fillColor="#00000000"
-        android:pathData="M79,0L79,108"
-        android:strokeColor="#33FFFFFF"
-        android:strokeWidth="0.8" />
-    <path
-        android:fillColor="#00000000"
-        android:pathData="M89,0L89,108"
-        android:strokeColor="#33FFFFFF"
-        android:strokeWidth="0.8" />
-    <path
-        android:fillColor="#00000000"
-        android:pathData="M99,0L99,108"
-        android:strokeColor="#33FFFFFF"
-        android:strokeWidth="0.8" />
-    <path
-        android:fillColor="#00000000"
-        android:pathData="M0,9L108,9"
-        android:strokeColor="#33FFFFFF"
-        android:strokeWidth="0.8" />
-    <path
-        android:fillColor="#00000000"
-        android:pathData="M0,19L108,19"
-        android:strokeColor="#33FFFFFF"
-        android:strokeWidth="0.8" />
-    <path
-        android:fillColor="#00000000"
-        android:pathData="M0,29L108,29"
-        android:strokeColor="#33FFFFFF"
-        android:strokeWidth="0.8" />
-    <path
-        android:fillColor="#00000000"
-        android:pathData="M0,39L108,39"
-        android:strokeColor="#33FFFFFF"
-        android:strokeWidth="0.8" />
-    <path
-        android:fillColor="#00000000"
-        android:pathData="M0,49L108,49"
-        android:strokeColor="#33FFFFFF"
-        android:strokeWidth="0.8" />
-    <path
-        android:fillColor="#00000000"
-        android:pathData="M0,59L108,59"
-        android:strokeColor="#33FFFFFF"
-        android:strokeWidth="0.8" />
-    <path
-        android:fillColor="#00000000"
-        android:pathData="M0,69L108,69"
-        android:strokeColor="#33FFFFFF"
-        android:strokeWidth="0.8" />
-    <path
-        android:fillColor="#00000000"
-        android:pathData="M0,79L108,79"
-        android:strokeColor="#33FFFFFF"
-        android:strokeWidth="0.8" />
-    <path
-        android:fillColor="#00000000"
-        android:pathData="M0,89L108,89"
-        android:strokeColor="#33FFFFFF"
-        android:strokeWidth="0.8" />
-    <path
-        android:fillColor="#00000000"
-        android:pathData="M0,99L108,99"
-        android:strokeColor="#33FFFFFF"
-        android:strokeWidth="0.8" />
-    <path
-        android:fillColor="#00000000"
-        android:pathData="M19,29L89,29"
-        android:strokeColor="#33FFFFFF"
-        android:strokeWidth="0.8" />
-    <path
-        android:fillColor="#00000000"
-        android:pathData="M19,39L89,39"
-        android:strokeColor="#33FFFFFF"
-        android:strokeWidth="0.8" />
-    <path
-        android:fillColor="#00000000"
-        android:pathData="M19,49L89,49"
-        android:strokeColor="#33FFFFFF"
-        android:strokeWidth="0.8" />
-    <path
-        android:fillColor="#00000000"
-        android:pathData="M19,59L89,59"
-        android:strokeColor="#33FFFFFF"
-        android:strokeWidth="0.8" />
-    <path
-        android:fillColor="#00000000"
-        android:pathData="M19,69L89,69"
-        android:strokeColor="#33FFFFFF"
-        android:strokeWidth="0.8" />
-    <path
-        android:fillColor="#00000000"
-        android:pathData="M19,79L89,79"
-        android:strokeColor="#33FFFFFF"
-        android:strokeWidth="0.8" />
-    <path
-        android:fillColor="#00000000"
-        android:pathData="M29,19L29,89"
-        android:strokeColor="#33FFFFFF"
-        android:strokeWidth="0.8" />
-    <path
-        android:fillColor="#00000000"
-        android:pathData="M39,19L39,89"
-        android:strokeColor="#33FFFFFF"
-        android:strokeWidth="0.8" />
-    <path
-        android:fillColor="#00000000"
-        android:pathData="M49,19L49,89"
-        android:strokeColor="#33FFFFFF"
-        android:strokeWidth="0.8" />
-    <path
-        android:fillColor="#00000000"
-        android:pathData="M59,19L59,89"
-        android:strokeColor="#33FFFFFF"
-        android:strokeWidth="0.8" />
-    <path
-        android:fillColor="#00000000"
-        android:pathData="M69,19L69,89"
-        android:strokeColor="#33FFFFFF"
-        android:strokeWidth="0.8" />
-    <path
-        android:fillColor="#00000000"
-        android:pathData="M79,19L79,89"
-        android:strokeColor="#33FFFFFF"
-        android:strokeWidth="0.8" />
-</vector>

+ 0 - 9
android/vcmi-app/src/main/res/drawable/ic_star_empty.xml

@@ -1,9 +0,0 @@
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="40dp"
-    android:height="40dp"
-    android:viewportHeight="24.0"
-    android:viewportWidth="24.0">
-    <path
-        android:fillColor="#FFFFFFFF"
-        android:pathData="M22,9.24l-7.19,-0.62L12,2 9.19,8.63 2,9.24l5.46,4.73L5.82,21 12,17.27 18.18,21l-1.63,-7.03L22,9.24zM12,15.4l-3.76,2.27 1,-4.28 -3.32,-2.88 4.38,-0.38L12,6.1l1.71,4.04 4.38,0.38 -3.32,2.88 1,4.28L12,15.4z" />
-</vector>

+ 0 - 9
android/vcmi-app/src/main/res/drawable/ic_star_full.xml

@@ -1,9 +0,0 @@
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="40dp"
-    android:height="40dp"
-    android:viewportHeight="24.0"
-    android:viewportWidth="24.0">
-    <path
-        android:fillColor="#FFFFFFFF"
-        android:pathData="M12,17.27L18.18,21l-1.64,-7.03L22,9.24l-7.19,-0.61L12,2 9.19,8.63 2,9.24l5.46,4.73L5.82,21z" />
-</vector>

+ 0 - 9
android/vcmi-app/src/main/res/drawable/ic_star_half.xml

@@ -1,9 +0,0 @@
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="40dp"
-    android:height="40dp"
-    android:viewportHeight="24.0"
-    android:viewportWidth="24.0">
-    <path
-        android:fillColor="#FFFFFFFF"
-        android:pathData="M22,9.24l-7.19,-0.62L12,2 9.19,8.63 2,9.24l5.46,4.73L5.82,21 12,17.27 18.18,21l-1.63,-7.03L22,9.24zM12,15.4V6.1l1.71,4.04 4.38,0.38 -3.32,2.88 1,4.28L12,15.4z" />
-</vector>

+ 0 - 6
android/vcmi-app/src/main/res/drawable/overlay_edittext_background.xml

@@ -1,6 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<shape xmlns:android="http://schemas.android.com/apk/res/android">
-    <corners android:radius="4dp" />
-    <solid android:color="#A0000000" />
-    <stroke android:color="@color/accent" android:width="1dp" />
-</shape>

+ 0 - 7
android/vcmi-app/src/main/res/drawable/recycler_divider_drawable.xml

@@ -1,7 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<shape xmlns:android="http://schemas.android.com/apk/res/android">
-    <size
-        android:width="1px"
-        android:height="1px" />
-    <solid android:color="@color/accent" />
-</shape>

+ 0 - 19
android/vcmi-app/src/main/res/layout-v21/inc_toolbar.xml

@@ -1,19 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<layout>
-    <com.google.android.material.appbar.AppBarLayout
-        xmlns:android="http://schemas.android.com/apk/res/android"
-        xmlns:app="http://schemas.android.com/apk/res-auto"
-        android:layout_width="match_parent"
-        android:layout_height="?attr/actionBarSize"
-        android:background="@color/bgMain">
-
-        <androidx.appcompat.widget.Toolbar
-            android:id="@+id/toolbar"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:background="@color/bgMain"
-            android:elevation="6dp"
-            app:elevation="6dp"
-            app:title="@string/launcher_title" />
-    </com.google.android.material.appbar.AppBarLayout>
-</layout>

+ 0 - 71
android/vcmi-app/src/main/res/layout/activity_about.xml

@@ -1,71 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<ScrollView
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    android:layout_below="@+id/toolbar_include">
-
-    <LinearLayout
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:orientation="vertical">
-
-        <LinearLayout
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:orientation="vertical"
-            android:padding="@dimen/side_margin">
-
-            <androidx.appcompat.widget.AppCompatTextView
-                style="@style/VCMI.Text.Header"
-                android:text="@string/app_name" />
-
-            <androidx.appcompat.widget.AppCompatTextView
-                android:id="@+id/about_version_app"
-                style="@style/VCMI.Text"
-                android:text="@string/about_version_app" />
-
-            <androidx.appcompat.widget.AppCompatTextView
-                android:id="@+id/about_version_launcher"
-                style="@style/VCMI.Text"
-                android:text="@string/about_version_launcher" />
-        </LinearLayout>
-
-        <include layout="@layout/inc_separator" />
-
-        <androidx.appcompat.widget.AppCompatTextView
-            style="@style/VCMI.Text.LauncherSection"
-            android:text="@string/about_section_project" />
-
-        <androidx.appcompat.widget.AppCompatTextView
-            android:id="@+id/about_link_portal"
-            style="@style/VCMI.Entry.Clickable.AboutSimpleEntry"
-            android:text="@string/about_links_main" />
-
-        <androidx.appcompat.widget.AppCompatTextView
-            android:id="@+id/about_link_repo_main"
-            style="@style/VCMI.Entry.Clickable.AboutSimpleEntry"
-            android:text="@string/about_links_repo" />
-
-        <androidx.appcompat.widget.AppCompatTextView
-            android:id="@+id/about_link_repo_launcher"
-            style="@style/VCMI.Entry.Clickable.AboutSimpleEntry"
-            android:text="@string/about_links_repo_launcher" />
-
-        <include layout="@layout/inc_separator" />
-
-        <androidx.appcompat.widget.AppCompatTextView
-            style="@style/VCMI.Text.LauncherSection"
-            android:text="@string/about_section_legal" />
-
-        <androidx.appcompat.widget.AppCompatTextView
-            android:id="@+id/about_btn_authors"
-            style="@style/VCMI.Entry.Clickable.AboutSimpleEntry"
-            android:text="@string/about_btn_authors" />
-
-        <androidx.appcompat.widget.AppCompatTextView
-            android:id="@+id/about_btn_privacy"
-            style="@style/VCMI.Entry.Clickable.AboutSimpleEntry"
-            android:text="@string/about_btn_privacy" />
-    </LinearLayout>
-</ScrollView>

+ 0 - 22
android/vcmi-app/src/main/res/layout/activity_error.xml

@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<LinearLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    android:orientation="vertical">
-
-    <androidx.appcompat.widget.AppCompatTextView
-        android:id="@+id/error_message"
-        style="@style/VCMI.Text"
-        android:layout_margin="@dimen/side_margin"
-        android:text="@string/app_name" />
-
-    <androidx.appcompat.widget.AppCompatButton
-        android:id="@+id/error_btn_try_again"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_gravity="right"
-        android:layout_margin="@dimen/side_margin"
-        android:layout_marginTop="20dp"
-        android:text="@string/misc_try_again" />
-</LinearLayout>

+ 1 - 1
android/vcmi-app/src/main/res/layout/activity_game.xml

@@ -16,4 +16,4 @@
         android:layout_height="wrap_content"
         android:layout_gravity="center"
         android:visibility="gone" />
-</FrameLayout>
+</FrameLayout>

+ 0 - 106
android/vcmi-app/src/main/res/layout/activity_launcher.xml

@@ -1,106 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<ScrollView
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:app="http://schemas.android.com/apk/res-auto"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    android:layout_below="@+id/toolbar_include"
-    android:clipChildren="false"
-    android:clipToPadding="false">
-
-    <LinearLayout
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:layout_marginTop="10dp"
-        android:orientation="vertical">
-
-        <androidx.appcompat.widget.AppCompatTextView
-            android:id="@+id/launcher_version_info"
-            style="@style/VCMI.Text"
-            android:padding="@dimen/side_margin"
-            android:text="@string/app_name" />
-
-        <include layout="@layout/inc_separator" />
-
-        <androidx.appcompat.widget.AppCompatTextView
-            style="@style/VCMI.Text.LauncherSection"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:elevation="2dp"
-            android:text="@string/launcher_section_init"
-            app:elevation="2dp" />
-
-        <FrameLayout
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content">
-
-            <include
-                android:id="@+id/launcher_btn_start"
-                layout="@layout/inc_launcher_btn" />
-
-            <androidx.appcompat.widget.AppCompatTextView
-                android:id="@+id/launcher_error"
-                style="@style/VCMI.Text"
-                android:drawableLeft="@drawable/ic_error"
-                android:drawablePadding="10dp"
-                android:gravity="center_vertical"
-                android:minHeight="80dp"
-                android:padding="@dimen/side_margin"
-                android:text="@string/app_name" />
-
-            <ProgressBar
-                android:id="@+id/launcher_progress"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:layout_gravity="center" />
-        </FrameLayout>
-
-        <include
-            android:id="@+id/launcher_btn_copy"
-            layout="@layout/inc_launcher_btn" />
-
-        <include
-            android:id="@+id/launcher_btn_export"
-            layout="@layout/inc_launcher_btn" />
-
-        <include layout="@layout/inc_separator" />
-
-        <androidx.appcompat.widget.AppCompatTextView
-            style="@style/VCMI.Text.LauncherSection"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:text="@string/launcher_section_settings" />
-
-        <include
-            android:id="@+id/launcher_btn_mods"
-            layout="@layout/inc_launcher_btn" />
-
-        <include
-            android:id="@+id/launcher_btn_scale"
-            layout="@layout/inc_launcher_btn" />
-
-        <include
-            android:id="@+id/launcher_btn_adventure_ai"
-            layout="@layout/inc_launcher_btn" />
-
-        <include
-            android:id="@+id/launcher_btn_cp"
-            layout="@layout/inc_launcher_btn" />
-
-        <include
-            android:id="@+id/launcher_btn_pointer_mode"
-            layout="@layout/inc_launcher_btn" />
-
-        <include
-            android:id="@+id/launcher_btn_pointer_multi"
-            layout="@layout/inc_launcher_btn" />
-
-        <include
-            android:id="@+id/launcher_btn_volume_sound"
-            layout="@layout/inc_launcher_slider" />
-
-        <include
-            android:id="@+id/launcher_btn_volume_music"
-            layout="@layout/inc_launcher_slider" />
-    </LinearLayout>
-</ScrollView>

+ 0 - 29
android/vcmi-app/src/main/res/layout/activity_mods.xml

@@ -1,29 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<FrameLayout
-    android:id="@+id/mods_data_root"
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    android:layout_below="@+id/toolbar_include"
-    android:clipChildren="false"
-    android:clipToPadding="false">
-
-    <androidx.recyclerview.widget.RecyclerView
-        android:id="@+id/mods_recycler"
-        xmlns:tools="http://schemas.android.com/tools"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        tools:listitem="@layout/mods_adapter_item" />
-
-    <TextView
-        android:id="@+id/mods_error_text"
-        style="@style/VCMI.Text"
-        android:layout_marginTop="30dp"
-        android:gravity="center" />
-
-    <ProgressBar
-        android:id="@+id/mods_progress"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_gravity="center" />
-</FrameLayout>

+ 0 - 18
android/vcmi-app/src/main/res/layout/activity_toolbar_wrapper.xml

@@ -1,18 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<layout xmlns:android="http://schemas.android.com/apk/res/android">
-    <RelativeLayout
-        android:layout_width="match_parent"
-        android:layout_height="match_parent">
-
-        <include
-            android:id="@+id/toolbar_include"
-            layout="@layout/inc_toolbar" />
-
-        <ViewStub
-            android:id="@+id/toolbar_wrapper_content_stub"
-            android:layout_width="match_parent"
-            android:layout_height="match_parent"
-            android:layout_below="@id/toolbar_include" />
-
-    </RelativeLayout>
-</layout>

+ 0 - 34
android/vcmi-app/src/main/res/layout/dialog_authors.xml

@@ -1,34 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<ScrollView
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent">
-
-    <LinearLayout
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:orientation="vertical">
-
-        <androidx.appcompat.widget.AppCompatTextView
-            style="@style/VCMI.Text.LauncherSection"
-            android:text="@string/dialog_authors_vcmi" />
-
-
-        <androidx.appcompat.widget.AppCompatTextView
-            android:id="@+id/dialog_authors_vcmi"
-            style="@style/VCMI.Text"
-            android:padding="@dimen/side_margin" />
-
-        <include layout="@layout/inc_separator" />
-
-        <!-- TODO should this be separate or just merged with vcmi authors? -->
-        <androidx.appcompat.widget.AppCompatTextView
-            style="@style/VCMI.Text.LauncherSection"
-            android:text="@string/dialog_authors_launcher" />
-
-        <androidx.appcompat.widget.AppCompatTextView
-            android:id="@+id/dialog_authors_launcher"
-            style="@style/VCMI.Text"
-            android:padding="@dimen/side_margin" />
-    </LinearLayout>
-</ScrollView>

+ 0 - 33
android/vcmi-app/src/main/res/layout/inc_launcher_btn.xml

@@ -1,33 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<layout xmlns:android="http://schemas.android.com/apk/res/android">
-    <data>
-        <variable
-            name="title"
-            type="java.lang.String" />
-        <variable
-            name="description"
-            type="java.lang.String" />
-    </data>
-    <RelativeLayout
-        style="@style/VCMI.Entry.Clickable"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content">
-
-        <LinearLayout
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:layout_centerVertical="true"
-            android:orientation="vertical">
-
-            <TextView
-                android:id="@+id/inc_launcher_btn_main"
-                style="@style/VCMI.Text.LauncherEntry"
-                android:text="@{title}" />
-
-            <TextView
-                android:id="@+id/inc_launcher_btn_sub"
-                style="@style/VCMI.Text.LauncherEntry.Sub"
-                android:text="@{description}" />
-        </LinearLayout>
-    </RelativeLayout>
-</layout>

+ 0 - 27
android/vcmi-app/src/main/res/layout/inc_launcher_slider.xml

@@ -1,27 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<layout xmlns:android="http://schemas.android.com/apk/res/android">
-    <RelativeLayout
-        style="@style/VCMI.Entry"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content">
-
-        <LinearLayout
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:layout_centerVertical="true"
-            android:orientation="vertical">
-
-            <TextView
-                android:id="@+id/inc_launcher_btn_main"
-                style="@style/VCMI.Text.LauncherEntry"
-                android:text="@string/app_name" />
-
-            <androidx.appcompat.widget.AppCompatSeekBar
-                android:id="@+id/inc_launcher_btn_slider"
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:paddingTop="8dp"
-                android:paddingBottom="8dp" />
-        </LinearLayout>
-    </RelativeLayout>
-</layout>

+ 0 - 7
android/vcmi-app/src/main/res/layout/inc_separator.xml

@@ -1,7 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<layout xmlns:android="http://schemas.android.com/apk/res/android">
-    <View
-        android:layout_width="match_parent"
-        android:layout_height="1px"
-        android:background="@color/separator" />
-</layout>

+ 0 - 28
android/vcmi-app/src/main/res/layout/inc_toolbar.xml

@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<layout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:app="http://schemas.android.com/apk/res-auto">
-
-    <RelativeLayout
-        android:layout_width="match_parent"
-        android:layout_height="?attr/actionBarSize"
-        android:clipChildren="false"
-        android:clipToPadding="false">
-
-        <androidx.appcompat.widget.Toolbar
-            android:id="@+id/toolbar"
-            android:layout_width="match_parent"
-            android:layout_height="?attr/actionBarSize"
-            android:background="@color/bgMain"
-            android:elevation="6dp"
-            app:elevation="6dp"
-            app:title="@string/launcher_title" />
-
-        <ImageView
-            android:layout_width="match_parent"
-            android:layout_height="4dp"
-            android:layout_alignParentBottom="true"
-            android:layout_marginBottom="-4dp"
-            android:background="@drawable/compat_toolbar_shadow" />
-    </RelativeLayout>
-</layout>

+ 0 - 25
android/vcmi-app/src/main/res/layout/mod_base_adapter_item.xml

@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<LinearLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    android:layout_gravity="center"
-    android:orientation="horizontal">
-
-    <View
-        android:id="@+id/mods_adapter_item_nesting"
-        android:layout_width="0dp"
-        android:layout_height="match_parent"
-        android:background="@color/accent" />
-
-    <androidx.appcompat.widget.AppCompatTextView
-        android:id="@+id/mods_adapter_item_name"
-        style="@style/VCMI.Text"
-        xmlns:android="http://schemas.android.com/apk/res/android"
-        android:layout_marginLeft="8dp"
-        android:drawableLeft="@drawable/ic_error"
-        android:drawablePadding="18dp"
-        android:gravity="center_vertical"
-        android:padding="@dimen/side_margin"
-        android:text="@string/mods_failed_mod_loading" />
-</LinearLayout>

+ 0 - 101
android/vcmi-app/src/main/res/layout/mods_adapter_item.xml

@@ -1,101 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<LinearLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    android:layout_gravity="center"
-    android:background="?attr/selectableItemBackground"
-    android:minHeight="@dimen/entry_min_height"
-    android:orientation="horizontal">
-
-    <View
-        android:id="@+id/mods_adapter_item_nesting"
-        android:layout_width="0dp"
-        android:layout_height="match_parent"
-        android:background="@color/accent" />
-
-    <LinearLayout
-        style="@style/VCMI.Entry"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:layout_gravity="center_vertical"
-        android:elevation="4dp"
-        android:gravity="center_vertical"
-        android:orientation="horizontal"
-        android:paddingBottom="5dp"
-        android:paddingLeft="@dimen/side_margin"
-        android:paddingTop="5dp">
-
-        <androidx.appcompat.widget.AppCompatImageView
-            android:id="@+id/mods_adapter_item_status"
-            android:layout_width="40dp"
-            android:layout_height="40dp"
-            android:layout_marginRight="10dp"
-            android:src="@drawable/ic_star_full" />
-
-        <LinearLayout
-            android:layout_width="0dp"
-            android:layout_height="wrap_content"
-            android:layout_marginRight="10dp"
-            android:layout_weight="1"
-            android:orientation="vertical">
-
-            <androidx.appcompat.widget.AppCompatTextView
-                android:id="@+id/mods_adapter_item_name"
-                style="@style/VCMI.Text.ModName"
-                android:ellipsize="end"
-                android:lines="1"
-                android:text="mod name, v1.0" />
-
-            <androidx.appcompat.widget.AppCompatTextView
-                android:id="@+id/mods_adapter_item_author"
-                style="@style/VCMI.Text.ModAuthor"
-                android:ellipsize="end"
-                android:lines="1"
-                android:text="by mod author" />
-        </LinearLayout>
-
-        <LinearLayout
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:orientation="vertical">
-
-            <androidx.appcompat.widget.AppCompatTextView
-                android:id="@+id/mods_adapter_item_modtype"
-                style="@style/VCMI.Text.ModType"
-                android:layout_width="wrap_content"
-                android:layout_gravity="right"
-                android:text="tools" />
-
-            <androidx.appcompat.widget.AppCompatTextView
-                android:id="@+id/mods_adapter_item_size"
-                style="@style/VCMI.Text.ModSize"
-                android:layout_width="wrap_content"
-                android:layout_gravity="right"
-                android:text="1000 MB" />
-        </LinearLayout>
-
-        <androidx.appcompat.widget.AppCompatImageButton
-            android:id="@+id/mods_adapter_item_btn_download"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:background="?attr/selectableItemBackgroundBorderless"
-            android:padding="@dimen/side_margin"
-            android:src="@android:drawable/stat_sys_download" />
-
-        <androidx.appcompat.widget.AppCompatImageButton
-            android:id="@+id/mods_adapter_item_btn_uninstall"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:background="?attr/selectableItemBackgroundBorderless"
-            android:padding="@dimen/side_margin"
-            android:src="@android:drawable/ic_menu_delete" />
-
-        <TextView
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:id="@+id/mods_adapter_item_install_progress"
-            android:padding="@dimen/side_margin" />
-
-    </LinearLayout>
-</LinearLayout>

+ 0 - 10
android/vcmi-app/src/main/res/menu/menu_launcher.xml

@@ -1,10 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<menu xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:app="http://schemas.android.com/apk/res-auto">
-
-    <item
-        android:id="@+id/menu_launcher_about"
-        android:icon="@drawable/ic_info"
-        android:title="@string/menu_launcher_about"
-        app:showAsAction="always" />
-</menu>

+ 0 - 9
android/vcmi-app/src/main/res/menu/menu_mods.xml

@@ -1,9 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<menu xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:app="http://schemas.android.com/apk/res-auto">
-    <item
-        android:id="@+id/menu_mods_download_repo"
-        android:icon="@android:drawable/stat_sys_download"
-        android:title="@string/menu_mods_download_repo"
-        app:showAsAction="ifRoom" />
-</menu>

+ 0 - 67
android/vcmi-app/src/main/res/values-cs/strings.xml

@@ -1,71 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
 <resources>
-    <string name="url_project_page" translatable="false">https://vcmi.eu</string>
-    <string name="url_project_repo" translatable="false">https://github.com/vcmi/vcmi</string>
-    <string name="url_launcher_repo" translatable="false">https://github.com/vcmi/vcmi-android</string>
-    <string name="url_launcher_privacy" translatable="false">https://github.com/vcmi/vcmi/blob/master/docs/players/Privacy_Policy.md</string>
-
-    <string name="app_name">VCMI</string>
     <string name="server_name">Server VCMI</string>
-    <string name="launcher_title">Spouštěč VCMI</string>
-    <string name="launcher_btn_scale_title">Škálování herního rozlišení</string>
-    <string name="launcher_btn_scale_subtitle_unknown">Současné: neznámé</string>
-    <string name="launcher_btn_scale_subtitle">Současné: %1$d%%</string>
-    <string name="launcher_btn_start_title">Spustit VCMI</string>
-    <string name="launcher_btn_start_subtitle">Současná verze VCMI: %1$s</string>
-    <string name="launcher_btn_mods_title">Modifikace</string>
-    <string name="launcher_btn_mods_subtitle">Nainstalovat nové frakce, přeměty a bonusy</string>
-    <string name="launcher_btn_language_title">Jazyk</string>
-    <string name="launcher_btn_language_subtitle_unknown">Současný: neznámý</string>
-    <string name="launcher_btn_language_subtitle">Současný: %1$s</string>
-    <string name="launcher_btn_pointermode_title">Změnit režim ukazatele</string>
-    <string name="launcher_btn_pointermode_subtitle">Současný: %1$s</string>
-    <string name="launcher_btn_pointermulti_title">Násobitel rychlosti relativního ukazatele</string>
-    <string name="launcher_btn_pointermulti_subtitle">Současný: %1$s</string>
-    <string name="launcher_btn_sound_title">Hlasitost zvuků</string>
-    <string name="launcher_btn_music_title">Hlasitost hudby</string>
-    <string name="launcher_btn_adventure_ai">AI světa</string>
-    <string name="launcher_btn_adventure_ai_title">Změnit AI světa</string>
-    <string name="launcher_btn_import_title">Importovat data VCMI</string>
-    <string name="launcher_btn_import_description">Zkopírovat soubury VCMI do vestavěného úložiště. Můžete importovat starou složku dat vcmi z vydání 0.99 nebo soubory HOMM3</string>
-    <string name="launcher_btn_export_title">Exportovat data VCMI</string>
-    <string name="launcher_btn_export_description">Udělat kopii dat VCMI před odinstalací nebo pro synchronizaci s desktopovou verzí. Též můžete přímo přistoupit k interním datům.</string>
-    <string name="launcher_progress_copy">Kopírování %1$s</string>
-    <string name="launcher_version">Současná verze spouštěče: %1$s</string>
-    <string name="launcher_error_vcmi_data_root_failed">Nelze vytvořit datovou složku VCMI v %1$s.</string>
-    <string name="launcher_error_h3_data_missing">Nelze najít datovou složku v \'%1$s\'. Vložte do ní své datové soubory HoMM3 nebo použijte tlačítko níže. Možná budete muset také restartovat aplikaci.</string>
-    <string name="launcher_error_vcmi_data_internal_missing">Nelze najít nebo rozbalit data vcmi ze zdrojů aplikace. Zkuste přeinstalovat aplikaci.</string>
-    <string name="launcher_error_vcmi_data_internal_update">Nelze aktualizovat data vcmi ze zdrojů aplikace. Zkuste přeinstalovat aplikaci.</string>
-    <string name="launcher_error_permissions">Tato aplikace potřebuje oprávnění k zápisu pro použití obsahu na externím úložišti</string>
-    <string name="launcher_error_permission_broken">Nelze správně vyřešit oprávnění</string>
-    <string name="mods_item_author_template">od %1$s</string>
-    <string name="misc_try_again">Zkusit znovu</string>
-    <string name="launcher_section_init">Inicializae hry</string>
-    <string name="launcher_section_settings">Nastavení</string>
-    <string name="menu_mods_download_repo">Stáhnout data repozitáře</string>
-
-    <string name="misc_pointermode_normal">Normální</string>
-    <string name="misc_pointermode_relative">Relativní</string>
-    <string name="menu_launcher_about">O spouštěči</string>
-
-    <string name="mods_title">Nalezené modifikace</string>
-    <string name="mods_failed_mod_loading">Nelze načíst modifikaci ve složce \'%1$s\'</string>
-    <string name="mods_removal_title">Odebírání %1$s</string>
-    <string name="mods_removal_confirmation">Jste si jisti odebráním %1$s?</string>
-
-    <string name="about_title">O aplikaci</string>
-    <string name="about_version_app">Verze aplikace: %1$s</string>
-    <string name="about_version_launcher">Verze spouštěče: %1$s</string>
-    <string name="about_section_project">Projekt</string>
-    <string name="about_section_legal">Právní záležitosti</string>
-    <string name="about_links_main">Hlavní stránka: %1$s</string>
-    <string name="about_links_repo">Repozitář projektu: %1$s</string>
-    <string name="about_links_repo_launcher">Repozitář spouštěče: %1$s</string>
-    <string name="about_btn_authors">Autoři</string>
-    <string name="about_btn_privacy">Zásady ochrany osobních údajů: %1$s</string>
-    <string name="about_error_opening_url">Nebylo možné otevřít webovou stránku (nenalezena patřičná aplikace)</string>
-
-    <string name="dialog_authors_vcmi">Autoři VCMI</string>
-    <string name="dialog_authors_launcher">Autoři spouštěče</string>
-    <string name="launcher_error_config_saving_failed">Nelze uložit konfigurační soubor VCMI; důvod: %1$s</string>
 </resources>

+ 0 - 58
android/vcmi-app/src/main/res/values-de/strings.xml

@@ -1,62 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
 <resources>
-    <string name="app_name">VCMI</string>
-    <string name="launcher_btn_start_title">VCMI starten</string>
-    <string name="launcher_title">VCMI-Starter</string>
-    <string name="launcher_btn_scale_title">Skalierung der Spielauflösung</string>
-    <string name="launcher_btn_scale_subtitle_unknown">Aktuell: unbekannt</string>
-    <string name="launcher_btn_scale_subtitle">Aktuell: %1$d%%</string>
     <string name="server_name">VCMI-Server</string>
-    <string name="launcher_btn_start_subtitle">Aktuelle VCMI-Version: %1$s</string>
-    <string name="launcher_btn_mods_title">Mods</string>
-    <string name="launcher_btn_mods_subtitle">Neue Burgen, Kreaturen, Objekte und Erweiterungen hinzufügen</string>
-    <string name="launcher_btn_language_title">Sprache</string>
-    <string name="launcher_btn_language_subtitle_unknown">Aktuell: unbekannt</string>
-    <string name="launcher_btn_language_subtitle">Aktuell: %1$s</string>
-    <string name="launcher_btn_pointermode_title">Zeigermodus ändern</string>
-    <string name="launcher_btn_pointermode_subtitle">Aktuell: %1$s</string>
-    <string name="launcher_btn_pointermulti_subtitle">Aktuell: %1$s</string>
-    <string name="launcher_version">Aktuelle Version des Starters: %1$s</string>
-    <string name="launcher_error_h3_data_missing">Der Datenordner in \'%1$s\' konnte nicht gefunden werden. Legen Sie Ihre HoMM3-Datendateien dort ab oder verwenden Sie die Schaltfläche unten, um diese zu importieren. Möglicherweise müssen Sie die Anwendung neu starten.</string>
-    <string name="launcher_btn_pointermulti_title">Multiplikator der relativen Zeigergeschwindigkeit</string>
-    <string name="launcher_btn_sound_title">Lautstärke der Geräusche</string>
-    <string name="launcher_btn_music_title">Lautstärke der Musik</string>
-    <string name="launcher_btn_adventure_ai">Abenteuer KI</string>
-    <string name="launcher_btn_adventure_ai_title">Abenteuer KI ändern</string>
-    <string name="launcher_error_vcmi_data_root_failed">VCMI-Datenordner in %1$s konnte nicht erstellt werden.</string>
-    <string name="launcher_error_vcmi_data_internal_missing">Ressourcendateien konnten nicht extrahiert werden. Versuchen Sie, die Anwendung neu zu installieren.</string>
-    <string name="launcher_error_vcmi_data_internal_update">Die Ressourcendateien konnten nicht aktualisiert werden. Versuchen Sie, die Anwendung neu zu installieren.</string>
-    <string name="launcher_error_permissions">Die Anwendung benötigt Rechte, um Inhalte auf externen Speicher zu schreiben.</string>
-    <string name="launcher_error_permission_broken">Keine Berechtigung erhalten.</string>
-    <string name="launcher_error_config_saving_failed">Einstellungen konnten nicht gespeichert werden; Grund: %1$s</string>
-    <string name="mods_item_author_template">Autor %1$s</string>
-    <string name="misc_try_again">Erneut versuchen</string>
-    <string name="launcher_section_init">Initialisierung</string>
-    <string name="launcher_section_settings">Einstellungen</string>
-    <string name="menu_mods_download_repo">Liste der Mods herunterladen</string>
-    <string name="misc_pointermode_normal">Normal</string>
-    <string name="misc_pointermode_relative">Relativ</string>
-    <string name="menu_launcher_about">Über</string>
-    <string name="mods_title">Installierte Mods</string>
-    <string name="mods_failed_mod_loading">Die Mod in dem Ordner \'%1$s\' kann nicht geladen werden.</string>
-    <string name="mods_removal_title">%1$s löschen</string>
-    <string name="mods_removal_confirmation">Sind Sie sicher, dass Sie %1$s löschen wollen?</string>
-    <string name="about_title">Über die Anwendung</string>
-    <string name="about_version_app">Version der Anwendung: %1$s</string>
-    <string name="about_version_launcher">Version des Startprogramms: %1$s</string>
-    <string name="about_section_project">Projekt</string>
-    <string name="about_section_legal">Rechtliches</string>
-    <string name="about_links_main">Website: %1$s</string>
-    <string name="about_links_repo">Projekt-Repository: %1$s</string>
-    <string name="about_links_repo_launcher">Starter-Repository: %1$s</string>
-    <string name="about_btn_authors">Autoren</string>
-    <string name="about_error_opening_url">Die Seite kann nicht geöffnet werden (wahrscheinlich konnte kein Browser gefunden werden)</string>
-    <string name="dialog_authors_vcmi">VCMI-Autoren</string>
-    <string name="dialog_authors_launcher">Autoren des Starters</string>
-    <string name="about_btn_privacy">Datenschutzbestimmungen: %1$s</string>
-    <string name="launcher_btn_export_title">VCMI-Daten exportieren</string>
-    <string name="launcher_btn_export_description">Erstellen Sie eine Kopie der internen VCMI-Daten vor der Deinstallation oder zur Synchronisierung der Speicherstände mit der Desktop-Version. Sie können auch direkt auf den internen Speicher zugreifen.</string>
-    <string name="launcher_btn_import_title">VCMI-Daten importieren</string>
-    <string name="launcher_btn_import_description">Kopieren Sie VCMI-Dateien in den internen Speicher. Sie können den alten vcmi-data-Ordner von Version 0.99 oder HoMM3-Dateien importieren</string>
-    <string name="launcher_progress_copy">Kopiere %1$s</string>
 </resources>

+ 0 - 67
android/vcmi-app/src/main/res/values-es/strings.xml

@@ -1,71 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
 <resources>
-    <string name="url_project_page" translatable="false">https://vcmi.eu</string>
-    <string name="url_project_repo" translatable="false">https://github.com/vcmi/vcmi</string>
-    <string name="url_launcher_repo" translatable="false">https://github.com/vcmi/vcmi-android</string>
-    <string name="url_launcher_privacy" translatable="false">https://github.com/vcmi/vcmi/blob/master/docs/players/Privacy_Policy.md</string>
-
-    <string name="app_name">VCMI</string>
     <string name="server_name">VCMI Servidor</string>
-    <string name="launcher_title">VCMI Lanzador</string>
-    <string name="launcher_btn_scale_title">Escalado de resolución</string>
-    <string name="launcher_btn_scale_subtitle_unknown">Seleccionado: desconocido</string>
-    <string name="launcher_btn_scale_subtitle">Seleccionado: %1$d%%</string>
-    <string name="launcher_btn_start_title">Iniciar VCMI</string>
-    <string name="launcher_btn_start_subtitle">version actual VCMI: %1$s</string>
-    <string name="launcher_btn_mods_title">Mods</string>
-    <string name="launcher_btn_mods_subtitle">Instalar nuevas facciones, Objectos, extras</string>
-    <string name="launcher_btn_language_title">Idioma</string>
-    <string name="launcher_btn_language_subtitle_unknown">Seleccionado: desconocido</string>
-    <string name="launcher_btn_language_subtitle">Seleccionado: %1$s</string>
-    <string name="launcher_btn_pointermode_title">Cambiar modo del puntero</string>
-    <string name="launcher_btn_pointermode_subtitle">Seleccionado: %1$s</string>
-    <string name="launcher_btn_pointermulti_title">Múltiplo de velocidad relativa del puntero</string>
-    <string name="launcher_btn_pointermulti_subtitle">Seleccionado: %1$s</string>
-    <string name="launcher_btn_sound_title">Sonido</string>
-    <string name="launcher_btn_music_title">Música</string>
-    <string name="launcher_btn_adventure_ai">Adventure AI</string>
-    <string name="launcher_btn_adventure_ai_title">Change adventure AI</string>
-    <string name="launcher_btn_import_title">Importar datos VCMI</string>
-    <string name="launcher_btn_import_description">Copiar ficheros VCMI al almacenamiento interno. Puedes importar datos antiguos a partir de la version 0.99 de VCMI o los ficheros de HOMM3.</string>
-    <string name="launcher_btn_export_title">Exportar datos VCMI</string>
-    <string name="launcher_btn_export_description">Hacer una copia interna de los datos VCMI antes de una desinstalación o sincronizar las partidas de la versión de escritorio. Tambien puedes acceder al almacenamiento interno directamente.</string>
-    <string name="launcher_progress_copy">Copiando %1$s</string>
-    <string name="launcher_version">Version del lanzador: %1$s</string>
-    <string name="launcher_error_vcmi_data_root_failed">No se puede crear la carpeta de datos VCMI en %1$s.</string>
-    <string name="launcher_error_h3_data_missing">No se encuentra la carpeta de datos en \'%1$s\'. Coloca tus ficheros de datos de HoMM3 en la carpeta o utiliza el boton de abajo para que se haga automáticamente. Quizás sea necesario reiniciar la aplicación.</string>
-    <string name="launcher_error_vcmi_data_internal_missing">No se pudieron encontrar ni extraer datos vcmi de los recursos de la aplicación. Intente reinstalar la aplicación.</string>
-    <string name="launcher_error_vcmi_data_internal_update">No se pudieron actualizar los datos de vcmi desde los recursos de la aplicación. Intente reinstalar la aplicación.</string>
-    <string name="launcher_error_permissions">Esta aplicación necesita permisos de escritura para utilizar el contenido almacenado en un almacenamiento externo.</string>
-    <string name="launcher_error_permission_broken">Los permisos no se pueden resolver correctamente</string>
-    <string name="mods_item_author_template">por %1$s</string>
-    <string name="misc_try_again">Volver a intentar</string>
-    <string name="launcher_section_init">Iniciar juego</string>
-    <string name="launcher_section_settings">Configuración</string>
-    <string name="menu_mods_download_repo">Descargar datos del repositorio</string>
-
-    <string name="misc_pointermode_normal">Normal</string>
-    <string name="misc_pointermode_relative">Relativo</string>
-    <string name="menu_launcher_about">Acerca</string>
-
-    <string name="mods_title">Mods detectados</string>
-    <string name="mods_failed_mod_loading">No se puede cargar el Mod en la carpeta \'%1$s\' </string>
-    <string name="mods_removal_title">Eliminando %1$s</string>
-    <string name="mods_removal_confirmation">Quieres eliminar %1$s</string>
-
-    <string name="about_title">Acerca de la applicación</string>
-    <string name="about_version_app">App versión: %1$s</string>
-    <string name="about_version_launcher">Versión del lanzador: %1$s</string>
-    <string name="about_section_project">Proyecto</string>
-    <string name="about_section_legal">Legal</string>
-    <string name="about_links_main">Página principal: %1$s</string>
-    <string name="about_links_repo">Repositorio del Proyecto: %1$s</string>
-    <string name="about_links_repo_launcher">Repositorio del lanzador: %1$s</string>
-    <string name="about_btn_authors">Autores</string>
-    <string name="about_btn_privacy">Política de privacidad: %1$s</string>
-    <string name="about_error_opening_url">No se pudo abrir la página web (no se encontró una aplicacion adecuada)</string>
-
-    <string name="dialog_authors_vcmi">VCMI autores</string>
-    <string name="dialog_authors_launcher">Lanzador autores</string>
-    <string name="launcher_error_config_saving_failed">No se puede guardar la configuración de VCMI; motivo: %1$s</string>
 </resources>

+ 0 - 60
android/vcmi-app/src/main/res/values-pl/strings.xml

@@ -1,64 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
 <resources>
-    <string name="app_name">VCMI</string>
     <string name="server_name">VCMI Serwer</string>
-    <string name="launcher_title">VCMI Launcher</string>
-    <string name="launcher_btn_start_title">Włącz VCMI</string>
-    <string name="launcher_btn_start_subtitle">Obecna wersja VCMI: %1$s</string>
-    <string name="launcher_btn_mods_title">Mody</string>
-    <string name="launcher_btn_mods_subtitle">Zainstaluj nowe frakcje, obiekty, dodatki</string>
-    <string name="launcher_btn_language_title">Język</string>
-    <string name="launcher_btn_language_subtitle_unknown">Obecnie: nieznane</string>
-    <string name="launcher_btn_language_subtitle">Obecnie: %1$s</string>
-    <string name="launcher_btn_pointermode_title">Zmień tryb kursora</string>
-    <string name="launcher_btn_pointermode_subtitle">Obecnie: %1$s</string>
-    <string name="launcher_btn_pointermulti_title">Mnożnik prędkości kursora</string>
-    <string name="launcher_btn_pointermulti_subtitle">Obecnie: %1$s</string>
-    <string name="launcher_btn_sound_title">Głośność efektów dźwiękowych</string>
-    <string name="launcher_btn_music_title">Głośność muzyki</string>
-    <string name="launcher_btn_adventure_ai">Algorytm AI mapy przygody</string>
-    <string name="launcher_btn_adventure_ai_title">Zmień AI mapy przygody</string>
-    <string name="launcher_version">Obecna wersja launchera: %1$s</string>
-    <string name="launcher_error_vcmi_data_root_failed">Nie udało się stworzyć folderu na dane w %1$s.</string>
-    <string name="launcher_error_h3_data_missing">Nie znaleziono danych w \'%1$s\'. Przenieś tam pliki Heroes 3 (by mieć tam foldery %1$s/Data i %1$s/Mp3 itp) i uruchom jeszcze raz aplikację.</string>
-    <string name="launcher_error_vcmi_data_internal_missing">Nie udało się znaleźć lub wyodrębnić danych VCMI. Spróbuj zainstalować od nowa aplikację.</string>
-    <string name="launcher_error_vcmi_data_internal_update">Nie udało się zaktualizować danych VCMI. Spróbuj zainstalować od nowa aplikację.</string>
-    <string name="launcher_error_permissions">Ta aplikacja potrzebuje uprawnień do zapisu by mieć dostęp do magazynu zewnętrznego</string>
-    <string name="launcher_error_permission_broken">Nie udało się poprawnie obsłużyć uprawnień</string>
-    <string name="mods_item_author_template">przez %1$s</string>
-    <string name="misc_try_again">Spróbuj ponownie</string>
-    <string name="launcher_section_init">Inicjalizacja gry</string>
-    <string name="launcher_section_settings">Ustawienia</string>
-    <string name="menu_mods_download_repo">Pobierz dane modów z repozytorium</string>
-
-    <string name="misc_pointermode_normal">Normalny</string>
-    <string name="misc_pointermode_relative">Relatywny</string>
-    <string name="menu_launcher_about">O programie</string>
-
-    <string name="mods_title">Wykryte mody</string>
-    <string name="mods_failed_mod_loading">Nie udało się załadować moda w folderze \'%1$s\'</string>
-    <string name="mods_removal_title">Usuwanie %1$s</string>
-    <string name="mods_removal_confirmation">Czy na pewno chcesz usunąć %1$s</string>
-
-    <string name="about_title">O aplikacji</string>
-    <string name="about_version_app">Wersja aplikacji: %1$s</string>
-    <string name="about_version_launcher">Wersja launchera: %1$s</string>
-    <string name="about_section_project">Projekt</string>
-    <string name="about_section_legal">Kwestie prawne</string>
-    <string name="about_links_main">Strona główna: %1$s</string>
-    <string name="about_links_repo">Repozytorium projektu: %1$s</string>
-    <string name="about_links_repo_launcher">Repozytorium launchera: %1$s</string>
-    <string name="about_btn_authors">Autorzy</string>
-    <string name="about_btn_privacy" translatable="false">Polityka prywatności: %1$s</string>
-    <string name="about_error_opening_url">Nie udało się otworzyć witryny (nie znaleziono odpowiedniej aplikacji)</string>
-
-    <string name="dialog_authors_vcmi">Autorzy VCMI</string>
-    <string name="dialog_authors_launcher">Autorzy launchera</string>
-    <string name="launcher_error_config_saving_failed">Nie udało się zapisać konfiguracji VCMI; powód: %1$s</string>
-    
-    <string name="launcher_btn_export_title">Eksportuj dane VCMI</string>
-    <string name="launcher_btn_export_description">Stwórz kopię wewnętrznych danych VCMI przed odinstalowaniem lub by zachować zapisane gry do uruchomienia na innym urządzeniu. Możesz też mieć bezpośrednio operować na elementach wewnętrznego magazynu.</string>
-    <string name="launcher_btn_import_title">Importuj dane VCMI</string>
-    <string name="launcher_btn_import_description">Skopiuj dane VCMI do wewnętrznego magazynu. Możesz zaimportować stary folder vcmi-data z wersji 0.99 lub pliki HOMM3.</string>
-    <string name="launcher_progress_copy">Kopiowanie %1$s</string>
 </resources>

+ 0 - 55
android/vcmi-app/src/main/res/values-ru/strings.xml

@@ -1,59 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
 <resources>
-    <string name="app_name">VCMI</string>
-    <string name="launcher_btn_start_title">Запустить VCMI</string>
-    <string name="launcher_title">VCMI лаунчер</string>
     <string name="server_name">VCMI сервер</string>
-    <string name="launcher_btn_start_subtitle">Текущая версия VCMI: %1$s</string>
-    <string name="launcher_btn_mods_title">Моды</string>
-    <string name="launcher_btn_mods_subtitle">Добавить новые замки, существа, объекты, расширения</string>
-    <string name="launcher_btn_language_title">Язык</string>
-    <string name="launcher_btn_language_subtitle_unknown">Текущая: неизвестно</string>
-    <string name="launcher_btn_language_subtitle">Текущая: %1$s</string>
-    <string name="launcher_btn_pointermode_title">Изменить режим управления указателем</string>
-    <string name="launcher_btn_pointermode_subtitle">Currently: %1$s</string>
-    <string name="launcher_btn_pointermulti_subtitle">Текущая: %1$s</string>
-    <string name="launcher_version">Текущая версия лаунчера: %1$s</string>
-    <string name="launcher_error_h3_data_missing">Не удалось найти файлы Героев в \'%1$s\'. Скопируйте ваши файлы Героев 3 туда вручную или воспользуйтесь кнопкой копирования и перезапустите игру.</string>
-    <string name="launcher_btn_pointermulti_title">Относительная скорость управления указателем</string>
-    <string name="launcher_btn_sound_title">Громкость звуков</string>
-    <string name="launcher_btn_music_title">Громкость музыки</string>
-    <string name="launcher_btn_adventure_ai">ИИ карты</string>
-    <string name="launcher_btn_adventure_ai_title">Изменить ИИ карты</string>
-    <string name="launcher_error_vcmi_data_root_failed">Не удалось создать папку VCMI с данными в %1$s.</string>
-    <string name="launcher_error_vcmi_data_internal_missing">Не удалось извлечь файлы ресурсов. Попробуйте переустановить приложение.</string>
-    <string name="launcher_error_vcmi_data_internal_update">Не удалось обновить файлы ресурсов. Попробуйте переустановить приложение.</string>
-    <string name="launcher_error_permissions">Прилодению необходимы права для записи контента во внешнее хранилище.</string>
-    <string name="launcher_error_permission_broken">Не удалось получить права.</string>
-    <string name="launcher_error_config_saving_failed">Не удалось сохранить настройки; причина: %1$s</string>
-    <string name="mods_item_author_template">автор %1$s</string>
-    <string name="misc_try_again">Попробуйте еще раз</string>
-    <string name="launcher_section_init">Инициализация</string>
-    <string name="launcher_section_settings">Настройки</string>
-    <string name="menu_mods_download_repo">Скачать список модов</string>
-    <string name="misc_pointermode_normal">Обычное</string>
-    <string name="misc_pointermode_relative">Относительное</string>
-    <string name="menu_launcher_about">О приложении</string>
-    <string name="mods_title">Установленные моды</string>
-    <string name="mods_failed_mod_loading">Не удалось загрузить мод в папку \'%1$s\'</string>
-    <string name="mods_removal_title">Удаление %1$s</string>
-    <string name="mods_removal_confirmation">Вы уверены что хотите удалить %1$s</string>
-    <string name="about_title">О приложении</string>
-    <string name="about_version_app">Версия приложения: %1$s</string>
-    <string name="about_version_launcher">Версия лаунчера: %1$s</string>
-    <string name="about_section_project">Проект</string>
-    <string name="about_section_legal">Legal</string>
-    <string name="about_links_main">Сайт: %1$s</string>
-    <string name="about_links_repo">Репозиторий проекта: %1$s</string>
-    <string name="about_links_repo_launcher">Репозиторий лаунчера: %1$s</string>
-    <string name="about_btn_authors">Авторы</string>
-    <string name="about_error_opening_url">Не удалось открыть страничку (вероятно не удалось найти браузер)</string>
-    <string name="dialog_authors_vcmi">Авторы VCMI</string>
-    <string name="dialog_authors_launcher">Авторы лаунчера</string>
-    <string name="about_btn_privacy" translatable="false">Политика конфиденциальности: %1$s</string>
-    <string name="launcher_btn_export_title">Экспортировать данные VCMI</string>
-    <string name="launcher_btn_export_description">Сделать копию данных VCMI перед удалением игры или чтобы перенести файлы сохранений на версию для других платформ</string>
-    <string name="launcher_btn_import_title">Загрузить данные VCMI во внутреннее хранилище</string>
-    <string name="launcher_btn_import_description">Скопировать данные VCMI во внутреннее хранилище. Вы можете загрузить старую папку vcmi-data от версии 0.99 или файлы Героев</string>
-    <string name="launcher_progress_copy">Копируем %1$s</string>
 </resources>

+ 0 - 55
android/vcmi-app/src/main/res/values-uk/strings.xml

@@ -1,59 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
 <resources>
-    <string name="app_name">VCMI</string>
-    <string name="launcher_btn_start_title">Запустити VCMI</string>
-    <string name="launcher_title">VCMI лаунчер</string>
     <string name="server_name">VCMI сервер</string>
-    <string name="launcher_btn_start_subtitle">Поточна версія VCMI: %1$s</string>
-    <string name="launcher_btn_mods_title">Моди</string>
-    <string name="launcher_btn_mods_subtitle">Додати нові замки, істоти, об’єкти, розширення</string>
-    <string name="launcher_btn_language_title">Мова</string>
-    <string name="launcher_btn_language_subtitle_unknown">Поточна: невідомо</string>
-    <string name="launcher_btn_language_subtitle">Поточна: %1$s</string>
-    <string name="launcher_btn_pointermode_title">Змінити режим керування курсором</string>
-    <string name="launcher_btn_pointermode_subtitle">Поточна: %1$s</string>
-    <string name="launcher_btn_pointermulti_subtitle">Поточна: %1$s</string>
-    <string name="launcher_version">Поточна версія лаунчера: %1$s</string>
-    <string name="launcher_error_h3_data_missing">Не вдалося знайти файли Героїв у \'%1$s\'. Скопіюйте файли Героїв 3 туди вручну або скористайтеся кнопкою копіювання і перезапустіть гру.</string>
-    <string name="launcher_btn_pointermulti_title">Відносна швидкість керування курсором</string>
-    <string name="launcher_btn_sound_title">Гучність звуків</string>
-    <string name="launcher_btn_music_title">Гучність музики</string>
-    <string name="launcher_btn_adventure_ai">Комп’ютерний гравець</string>
-    <string name="launcher_btn_adventure_ai_title">Змінити програму комп’ютерного гравця</string>
-    <string name="launcher_error_vcmi_data_root_failed">Не вдалося створити теку VCMI з даними в %1$s.</string>
-    <string name="launcher_error_vcmi_data_internal_missing">Не вдалося розпакувати файли ресурсів. Спробуйте перевстановити програму.</string>
-    <string name="launcher_error_vcmi_data_internal_update">Не вдалося оновити файли ресурсів. Спробуйте перевстановити програму.</string>
-    <string name="launcher_error_permissions">VCMI необхідні права для запису контенту до зовнішнього сховища.</string>
-    <string name="launcher_error_permission_broken">Не вдалося отримати права.</string>
-    <string name="launcher_error_config_saving_failed">Не вдалося зберегти налаштування; причина: %1$s</string>
-    <string name="mods_item_author_template">автор %1$s</string>
-    <string name="misc_try_again">Спробуйте ще раз</string>
-    <string name="launcher_section_init">Ініціалізація</string>
-    <string name="launcher_section_settings">Налаштування</string>
-    <string name="menu_mods_download_repo">Завантажити список модів</string>
-    <string name="misc_pointermode_normal">Звичайне</string>
-    <string name="misc_pointermode_relative">Відносне</string>
-    <string name="menu_launcher_about">Про VCMI</string>
-    <string name="mods_title">Встановлені моди</string>
-    <string name="mods_failed_mod_loading">Не вдалося завантажити мод в теку \'%1$s\'</string>
-    <string name="mods_removal_title">Видалення %1$s</string>
-        <string name="mods_removal_confirmation">Ви впевнені, що хочете видалити %1$s</string>
-    <string name="about_title">Про VCMI</string>
-    <string name="about_version_app">Версія VCMI: %1$s</string>
-    <string name="about_version_launcher">Версія лаунчера: %1$s</string>
-    <string name="about_section_project">Проєкт</string>
-    <string name="about_section_legal">Legal</string>
-    <string name="about_links_main">Сайт: %1$s</string>
-    <string name="about_links_repo">Репозиторій проєкту: %1$s</string>
-    <string name="about_links_repo_launcher">Репозиторій лаунчера: %1$s</string>
-    <string name="about_btn_authors">Автори</string>
-    <string name="about_error_opening_url">Не вдалося відкрити сторінку (ймовірно, не вдалося знайти браузер)</string>
-    <string name="dialog_authors_vcmi">Автори VCMI</string>
-    <string name="dialog_authors_launcher">Автори лаунчера</string>
-    <string name="about_btn_privacy">Політика конфіденційності: %1$s</string>
-    <string name="launcher_btn_export_title">Експортувати дані VCMI</string>
-    <string name="launcher_btn_export_description">Зробити копію даних VCMI перед видаленням гри або перенести сейв-файли на версію для інших платформ</string>
-    <string name="launcher_btn_import_title">Завантажити дані VCMI у внутрішнє сховище</string>
-    <string name="launcher_btn_import_description">Копіювати дані VCMI у внутрішнє сховище. Ви можете завантажити стару теку vcmi-data від версії 0.99 чи файли героїв</string>
-    <string name="launcher_progress_copy">Копіюємо %1$s</string>
 </resources>

Kaikkia tiedostoja ei voida näyttää, sillä liian monta tiedostoa muuttui tässä diffissä