diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 50eb577eb..267a84ee9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -12,7 +12,7 @@ jobs: name: Automated tests strategy: matrix: - api-level: [ 25, 30, 33, 34, 35 ] + api-level: [ 25, 30, 33, 34, 35, 36 ] fail-fast: true runs-on: ubuntu-22.04 steps: @@ -57,7 +57,7 @@ jobs: uses: reactivecircus/android-emulator-runner@v2 with: api-level: ${{ matrix.api-level }} - target: ${{ (matrix.api-level == 35) && 'google_apis' || 'default' }} + target: ${{ (matrix.api-level >= 35) && 'google_apis' || 'default' }} arch: x86_64 profile: pixel_2 ram-size: 3072M @@ -76,7 +76,7 @@ jobs: GRADLE_OPTS: "-Dorg.gradle.internal.http.connectionTimeout=60000 -Dorg.gradle.internal.http.socketTimeout=60000 -Dorg.gradle.internal.network.retry.max.attempts=6 -Dorg.gradle.internal.network.retry.initial.backOff=2000" with: api-level: ${{ matrix.api-level }} - target: ${{ (matrix.api-level == 35) && 'google_apis' || 'default' }} + target: ${{ (matrix.api-level >= 35) && 'google_apis' || 'default' }} arch: x86_64 profile: pixel_2 ram-size: 3072M @@ -118,7 +118,7 @@ jobs: name: Automated tests for PlayStore variant strategy: matrix: - api-level: [ 25, 30, 33, 34, 35 ] + api-level: [ 25, 30, 33, 34, 35, 36 ] fail-fast: true runs-on: ubuntu-22.04 steps: @@ -163,7 +163,7 @@ jobs: uses: reactivecircus/android-emulator-runner@v2 with: api-level: ${{ matrix.api-level }} - target: ${{ (matrix.api-level == 35) && 'google_apis' || 'default' }} + target: ${{ (matrix.api-level >= 35) && 'google_apis' || 'default' }} arch: x86_64 profile: pixel_2 ram-size: 2048M @@ -182,7 +182,7 @@ jobs: GRADLE_OPTS: "-Dorg.gradle.internal.http.connectionTimeout=60000 -Dorg.gradle.internal.http.socketTimeout=60000 -Dorg.gradle.internal.network.retry.max.attempts=6 -Dorg.gradle.internal.network.retry.initial.backOff=2000" with: api-level: ${{ matrix.api-level }} - target: ${{ (matrix.api-level == 35) && 'google_apis' || 'default' }} + target: ${{ (matrix.api-level >= 35) && 'google_apis' || 'default' }} arch: x86_64 profile: pixel_2 ram-size: 2048M @@ -199,7 +199,7 @@ jobs: name: Automated tests for Custom app strategy: matrix: - api-level: [ 25, 30, 33, 34, 35 ] + api-level: [ 25, 30, 33, 34, 35, 36 ] fail-fast: true runs-on: ubuntu-22.04 steps: @@ -244,7 +244,7 @@ jobs: uses: reactivecircus/android-emulator-runner@v2 with: api-level: ${{ matrix.api-level }} - target: ${{ (matrix.api-level == 35) && 'google_apis' || 'default' }} + target: ${{ (matrix.api-level >= 35) && 'google_apis' || 'default' }} arch: x86_64 profile: pixel_2 ram-size: 2048M @@ -263,7 +263,7 @@ jobs: GRADLE_OPTS: "-Dorg.gradle.internal.http.connectionTimeout=60000 -Dorg.gradle.internal.http.socketTimeout=60000 -Dorg.gradle.internal.network.retry.max.attempts=6 -Dorg.gradle.internal.network.retry.initial.backOff=2000" with: api-level: ${{ matrix.api-level }} - target: ${{ (matrix.api-level == 35) && 'google_apis' || 'default' }} + target: ${{ (matrix.api-level >= 35) && 'google_apis' || 'default' }} arch: x86_64 profile: pixel_2 ram-size: 2048M @@ -280,7 +280,7 @@ jobs: name: Automated tests on Tablet strategy: matrix: - api-level: [ 25, 30, 33, 34, 35 ] + api-level: [ 25, 30, 33, 34, 35, 36 ] fail-fast: true runs-on: ubuntu-22.04 steps: @@ -325,7 +325,7 @@ jobs: uses: reactivecircus/android-emulator-runner@v2 with: api-level: ${{ matrix.api-level }} - target: ${{ (matrix.api-level == 35) && 'google_apis' || 'default' }} + target: ${{ (matrix.api-level >= 35) && 'google_apis' || 'default' }} arch: x86_64 profile: pixel_2 ram-size: 2048M @@ -344,7 +344,7 @@ jobs: GRADLE_OPTS: "-Dorg.gradle.internal.http.connectionTimeout=60000 -Dorg.gradle.internal.http.socketTimeout=60000 -Dorg.gradle.internal.network.retry.max.attempts=6 -Dorg.gradle.internal.network.retry.initial.backOff=2000" with: api-level: ${{ matrix.api-level }} - target: ${{ (matrix.api-level == 35) && 'google_apis' || 'default' }} + target: ${{ (matrix.api-level >= 35) && 'google_apis' || 'default' }} arch: x86_64 profile: pixel_c ram-size: 2048M diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 805350b16..2226a86b1 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -1,3 +1,4 @@ +import com.android.build.gradle.internal.tasks.factory.dependsOn import com.slack.keeper.optInToKeeper import org.w3c.dom.Element import plugin.KiwixConfigurationPlugin @@ -29,12 +30,12 @@ android { // it directly in the AndroidManifest file. namespace = "org.kiwix.kiwixmobile" defaultConfig { - base.archivesName.set(apkPrefix) resValue("string", "app_name", "Kiwix") resValue("string", "app_search_string", "Search Kiwix") versionCode = "".getVersionCode() versionName = generateVersionName() manifestPlaceholders["permission"] = "android.permission.MANAGE_EXTERNAL_STORAGE" + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" } lint { checkDependencies = true @@ -130,7 +131,7 @@ dependencies { androidTestImplementation(Libs.leakcanary_android_instrumentation) testImplementation(Libs.kotlinx_coroutines_test) } -task("generateVersionCodeAndName") { +tasks.register("generateVersionCodeAndName") { val file = File("VERSION_INFO") if (!file.exists()) file.createNewFile() file.printWriter().use { @@ -138,7 +139,7 @@ task("generateVersionCodeAndName") { } } -task("renameTarakFile") { +tasks.register("renameTarakFile") { val taraskFile = File("$rootDir/core/src/main/res/values-b+be+tarask/strings.xml") val mainStringsFile = File("$rootDir/core/src/main/res/values/strings.xml") @@ -214,6 +215,10 @@ fun elementToString(element: Element): String { return result.writer.toString() } -tasks.build { - dependsOn("renameTarakFile") +gradle.projectsEvaluated { + tasks.forEach { task -> + if (task.name != "renameTarakFile") { + task.dependsOn("renameTarakFile") + } + } } diff --git a/app/src/androidTest/java/org/kiwix/kiwixmobile/ObjectBoxToRoomMigratorTest.kt b/app/src/androidTest/java/org/kiwix/kiwixmobile/ObjectBoxToRoomMigratorTest.kt index b667ed8ef..0ea1de241 100644 --- a/app/src/androidTest/java/org/kiwix/kiwixmobile/ObjectBoxToRoomMigratorTest.kt +++ b/app/src/androidTest/java/org/kiwix/kiwixmobile/ObjectBoxToRoomMigratorTest.kt @@ -119,8 +119,11 @@ class ObjectBoxToRoomMigratorTest { val expectedSearchTerm = "test search" val expectedZimId = "8812214350305159407L" val expectedUrl = "http://kiwix.app/mainPage" - val recentSearchEntity = - RecentSearchEntity(searchTerm = expectedSearchTerm, zimId = expectedZimId, url = expectedUrl) + val recentSearchEntity = RecentSearchEntity( + searchTerm = expectedSearchTerm, + zimId = expectedZimId, + url = expectedUrl + ) // insert into object box box.put(recentSearchEntity) // migrate data into room database diff --git a/app/src/androidTest/java/org/kiwix/kiwixmobile/download/DownloadRobot.kt b/app/src/androidTest/java/org/kiwix/kiwixmobile/download/DownloadRobot.kt index 21e4a7955..d1b728d75 100644 --- a/app/src/androidTest/java/org/kiwix/kiwixmobile/download/DownloadRobot.kt +++ b/app/src/androidTest/java/org/kiwix/kiwixmobile/download/DownloadRobot.kt @@ -68,7 +68,9 @@ class DownloadRobot : BaseRobot() { clickOn(ViewId(R.id.downloadsFragment)) } - fun waitForDataToLoad(retryCountForDataToLoad: Int = 10) { + // Increasing the default timeout for data loading because, on the Android 16 Emulator, + // the internet connection is slow, and the library download takes longer. + fun waitForDataToLoad(retryCountForDataToLoad: Int = 20) { try { isVisible(TextId(string.your_languages)) } catch (e: RuntimeException) { diff --git a/app/src/androidTest/java/org/kiwix/kiwixmobile/initial/download/InitialDownloadRobot.kt b/app/src/androidTest/java/org/kiwix/kiwixmobile/initial/download/InitialDownloadRobot.kt index a059eac8d..41706d817 100644 --- a/app/src/androidTest/java/org/kiwix/kiwixmobile/initial/download/InitialDownloadRobot.kt +++ b/app/src/androidTest/java/org/kiwix/kiwixmobile/initial/download/InitialDownloadRobot.kt @@ -56,7 +56,9 @@ class InitialDownloadRobot : BaseRobot() { refresh(R.id.librarySwipeRefresh) } - fun waitForDataToLoad(retryCountForDataToLoad: Int = 10) { + // Increasing the default timeout for data loading because, on the Android 16 Emulator, + // the internet connection is slow, and the library download takes longer. + fun waitForDataToLoad(retryCountForDataToLoad: Int = 20) { try { isVisible(TextId(string.your_languages)) } catch (e: RuntimeException) { diff --git a/app/src/main/java/org/kiwix/kiwixmobile/intro/CustomPageIndicator.kt b/app/src/main/java/org/kiwix/kiwixmobile/intro/CustomPageIndicator.kt index e2a9c0a62..6867b4c49 100644 --- a/app/src/main/java/org/kiwix/kiwixmobile/intro/CustomPageIndicator.kt +++ b/app/src/main/java/org/kiwix/kiwixmobile/intro/CustomPageIndicator.kt @@ -21,6 +21,7 @@ import android.animation.Animator import android.animation.AnimatorListenerAdapter import android.animation.AnimatorSet import android.animation.ValueAnimator +import android.annotation.SuppressLint import android.content.Context import android.database.DataSetObserver import android.graphics.Canvas @@ -776,13 +777,17 @@ class CustomPageIndicator @JvmOverloads constructor( val density = context.resources.displayMetrics.density.toInt() // Load attributes - val a = - getContext().obtainStyledAttributes(attrs, R.styleable.CustomPageIndicator, defStyle, 0) - dotDiameter = - a.getDimensionPixelSize( - R.styleable.CustomPageIndicator_ipi_dotDiameter, - DEFAULT_DOT_SIZE * density - ) + @SuppressLint("UseKtx") + val a = getContext().obtainStyledAttributes( + attrs, + R.styleable.CustomPageIndicator, + defStyle, + 0 + ) + dotDiameter = a.getDimensionPixelSize( + R.styleable.CustomPageIndicator_ipi_dotDiameter, + DEFAULT_DOT_SIZE * density + ) dotRadius = (dotDiameter / 2).toFloat() halfDotRadius = dotRadius / 2 gap = diff --git a/app/src/main/java/org/kiwix/kiwixmobile/nav/destination/library/local/LocalLibraryFragment.kt b/app/src/main/java/org/kiwix/kiwixmobile/nav/destination/library/local/LocalLibraryFragment.kt index 58fc61197..d2813c15d 100644 --- a/app/src/main/java/org/kiwix/kiwixmobile/nav/destination/library/local/LocalLibraryFragment.kt +++ b/app/src/main/java/org/kiwix/kiwixmobile/nav/destination/library/local/LocalLibraryFragment.kt @@ -366,18 +366,17 @@ class LocalLibraryFragment : BaseFragment(), CopyMoveFileHandler.FileCopyMoveCal } private fun showFileChooser() { - val intent = - Intent().apply { - action = Intent.ACTION_OPEN_DOCUMENT - type = "*/*" - addCategory(Intent.CATEGORY_OPENABLE) - if (sharedPreferenceUtil.prefIsTest) { - putExtra( - "android.provider.extra.INITIAL_URI", - "content://com.android.externalstorage.documents/document/primary:Download".toUri() - ) - } + val intent = Intent().apply { + action = Intent.ACTION_OPEN_DOCUMENT + type = "*/*" + addCategory(Intent.CATEGORY_OPENABLE) + if (sharedPreferenceUtil.prefIsTest) { + putExtra( + "android.provider.extra.INITIAL_URI", + "content://com.android.externalstorage.documents/document/primary:Download".toUri() + ) } + } try { fileSelectLauncher.launch(Intent.createChooser(intent, "Select a zim file")) } catch (_: ActivityNotFoundException) { diff --git a/app/src/main/java/org/kiwix/kiwixmobile/nav/destination/reader/KiwixReaderFragment.kt b/app/src/main/java/org/kiwix/kiwixmobile/nav/destination/reader/KiwixReaderFragment.kt index f60a0273a..5f5f0f94b 100644 --- a/app/src/main/java/org/kiwix/kiwixmobile/nav/destination/reader/KiwixReaderFragment.kt +++ b/app/src/main/java/org/kiwix/kiwixmobile/nav/destination/reader/KiwixReaderFragment.kt @@ -135,11 +135,10 @@ class KiwixReaderFragment : CoreReaderFragment() { // Update the reader screen title to prevent showing the previously set title // when creating the new archive object. updateTitle() - val filePath = - FileUtils.getLocalFilePathByUri( - requireActivity().applicationContext, - zimFileUri.toUri() - ) + val filePath = FileUtils.getLocalFilePathByUri( + requireActivity().applicationContext, + zimFileUri.toUri() + ) if (filePath == null || !File(filePath).isFileExist()) { // Close the previously opened book in the reader. Since this file is not found, // it will not be set in the zimFileReader. The previously opened ZIM file diff --git a/app/src/main/java/org/kiwix/kiwixmobile/zimManager/ZimManageViewModel.kt b/app/src/main/java/org/kiwix/kiwixmobile/zimManager/ZimManageViewModel.kt index 1a80ceaea..37ad61b7a 100644 --- a/app/src/main/java/org/kiwix/kiwixmobile/zimManager/ZimManageViewModel.kt +++ b/app/src/main/java/org/kiwix/kiwixmobile/zimManager/ZimManageViewModel.kt @@ -42,6 +42,7 @@ import org.kiwix.kiwixmobile.BuildConfig.DEBUG import org.kiwix.kiwixmobile.core.R import org.kiwix.kiwixmobile.core.StorageObserver import org.kiwix.kiwixmobile.core.base.SideEffect +import org.kiwix.kiwixmobile.core.compat.CompatHelper.Companion.convertToLocal import org.kiwix.kiwixmobile.core.compat.CompatHelper.Companion.isWifi import org.kiwix.kiwixmobile.core.dao.DownloadRoomDao import org.kiwix.kiwixmobile.core.dao.NewBookDao @@ -475,7 +476,7 @@ class ZimManageViewModel @Inject constructor( networkLanguageCounts: MutableMap, listToActivateBy: List ) = Locale.getISOLanguages() - .map(::Locale) + .map { it.convertToLocal() } .filter { networkLanguageCounts.containsKey(it.isO3Language) } .map { locale -> Language( diff --git a/app/src/main/res/anim/nav_default_enter_anim.xml b/app/src/main/res/anim/nav_default_enter_anim.xml deleted file mode 100644 index f35ce95a0..000000000 --- a/app/src/main/res/anim/nav_default_enter_anim.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - diff --git a/app/src/main/res/anim/nav_default_exit_anim.xml b/app/src/main/res/anim/nav_default_exit_anim.xml deleted file mode 100644 index 6aef5554f..000000000 --- a/app/src/main/res/anim/nav_default_exit_anim.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - diff --git a/app/src/main/res/anim/nav_default_pop_enter_anim.xml b/app/src/main/res/anim/nav_default_pop_enter_anim.xml deleted file mode 100644 index f35ce95a0..000000000 --- a/app/src/main/res/anim/nav_default_pop_enter_anim.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - diff --git a/app/src/main/res/anim/nav_default_pop_exit_anim.xml b/app/src/main/res/anim/nav_default_pop_exit_anim.xml deleted file mode 100644 index 6aef5554f..000000000 --- a/app/src/main/res/anim/nav_default_pop_exit_anim.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - diff --git a/build.gradle.kts b/build.gradle.kts index 95259c517..326f75613 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,3 +1,5 @@ +import org.gradle.kotlin.dsl.register + buildscript { repositories { google() @@ -28,6 +30,6 @@ allprojects { } } -tasks.create("clean") { +tasks.register("clean") { delete(rootProject.layout.buildDirectory) } diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts index cb174da13..7765fae0c 100644 --- a/buildSrc/build.gradle.kts +++ b/buildSrc/build.gradle.kts @@ -11,7 +11,7 @@ repositories { } dependencies { - implementation("com.android.tools.build:gradle:8.7.2") + implementation("com.android.tools.build:gradle:8.11.0-alpha03") implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:2.0.0") implementation("com.google.devtools.ksp:com.google.devtools.ksp.gradle.plugin:2.0.0-1.0.24") implementation("org.jacoco:org.jacoco.core:0.8.12") @@ -23,8 +23,8 @@ dependencies { exclude(group = "com.google.guava", module = "guava") } implementation("io.gitlab.arturbosch.detekt:detekt-gradle-plugin:1.23.8") - implementation("com.googlecode.json-simple:json-simple:1.1") - implementation("com.squareup.okhttp3:okhttp:4.10.0") + implementation("com.googlecode.json-simple:json-simple:1.1.1") + implementation("com.squareup.okhttp3:okhttp:4.12.0") implementation(gradleApi()) implementation(localGroovy()) diff --git a/buildSrc/src/main/kotlin/Config.kt b/buildSrc/src/main/kotlin/Config.kt index dff15bd95..f661f3e92 100644 --- a/buildSrc/src/main/kotlin/Config.kt +++ b/buildSrc/src/main/kotlin/Config.kt @@ -22,9 +22,9 @@ object Config { // Here is a list of all Android versions with their corresponding API // levels: https://apilevels.com/ - const val compileSdk = 35 // SDK version used by Gradle to compile our app. + const val compileSdk = 36 // SDK version used by Gradle to compile our app. const val minSdk = 25 // Minimum SDK (Minimum Support Device) is 25 (Android 7.1 Nougat). - const val targetSdk = 35 // Target SDK (Maximum Support Device) is 34 (Android 14). + const val targetSdk = 36 // Target SDK (Maximum Support Device) is 36 (Android 16). val javaVersion = JavaVersion.VERSION_17 diff --git a/buildSrc/src/main/kotlin/Versions.kt b/buildSrc/src/main/kotlin/Versions.kt index 562cf6cd1..57a9d6e18 100644 --- a/buildSrc/src/main/kotlin/Versions.kt +++ b/buildSrc/src/main/kotlin/Versions.kt @@ -16,7 +16,7 @@ object Versions { const val org_jetbrains_kotlinx_kotlinx_coroutines: String = "1.10.1" - const val kotlinx_coroutines_rx3: String = "1.3.9" + const val kotlinx_coroutines_rx3: String = "1.10.1" const val androidx_test_espresso: String = "3.6.1" @@ -40,7 +40,7 @@ object Versions { const val androidx_test_core: String = "1.6.1" - const val androidx_test_orchestrator: String = "1.5.0" + const val androidx_test_orchestrator: String = "1.5.1" const val io_objectbox: String = "4.0.3" @@ -48,7 +48,7 @@ object Versions { const val android_arch_lifecycle_extensions: String = "1.1.1" - const val com_android_tools_build_gradle: String = "8.7.2" + const val com_android_tools_build_gradle: String = "8.11.0-alpha03" const val de_fayard_buildsrcversions_gradle_plugin: String = "0.7.0" @@ -58,7 +58,7 @@ object Versions { const val leakcanary_android: String = "2.14" - const val constraintlayout: String = "2.1.4" + const val constraintlayout: String = "2.2.0" const val swipe_refresh_layout: String = "1.1.0" diff --git a/buildSrc/src/main/kotlin/custom/CustomApps.kt b/buildSrc/src/main/kotlin/custom/CustomApps.kt index 7f4faa4d1..b1dade9ed 100644 --- a/buildSrc/src/main/kotlin/custom/CustomApps.kt +++ b/buildSrc/src/main/kotlin/custom/CustomApps.kt @@ -24,7 +24,7 @@ import org.json.simple.JSONObject import org.json.simple.parser.JSONParser import java.io.File -typealias ProductFlavors = NamedDomainObjectContainer +typealias ProductFlavors = NamedDomainObjectContainer object CustomApps { diff --git a/buildSrc/src/main/kotlin/plugin/AllProjectConfigurer.kt b/buildSrc/src/main/kotlin/plugin/AllProjectConfigurer.kt index 6b8a1dbd3..d5796ecb7 100644 --- a/buildSrc/src/main/kotlin/plugin/AllProjectConfigurer.kt +++ b/buildSrc/src/main/kotlin/plugin/AllProjectConfigurer.kt @@ -58,6 +58,7 @@ class AllProjectConfigurer { if (isLibrary) { namespace = "org.kiwix.kiwixmobile.core" } + setCompileSdkVersion(Config.compileSdk) defaultConfig { minSdk = Config.minSdk @@ -161,6 +162,9 @@ class AllProjectConfigurer { add("UnusedResources") add("NonConstantResourceId") add("NotifyDataSetChanged") + add("Aligned16KB") // TODO Remove when properly migrated to Android 16. + add("AndroidGradlePluginVersion") + add("MemberExtensionConflict") } lintConfig = target.rootProject.file("lintConfig.xml") } diff --git a/contrib/instrumentation-customapps.sh b/contrib/instrumentation-customapps.sh index 51e82804b..762a0caf8 100644 --- a/contrib/instrumentation-customapps.sh +++ b/contrib/instrumentation-customapps.sh @@ -21,6 +21,10 @@ # Enable Wi-Fi on the emulator adb shell svc wifi enable adb logcat -c +# Check if the stylus_handwriting_enabled setting exists before disabling +if adb shell settings list secure | grep -q "stylus_handwriting_enabled"; then + adb shell settings put secure stylus_handwriting_enabled 0 +fi # shellcheck disable=SC2035 adb logcat *:E -v color & @@ -61,6 +65,10 @@ while [ $retry -le 3 ]; do # Enable Wi-Fi on the emulator adb shell svc wifi enable adb logcat -c + # Check if the stylus_handwriting_enabled setting exists before disabling + if adb shell settings list secure | grep -q "stylus_handwriting_enabled"; then + adb shell settings put secure stylus_handwriting_enabled 0 + fi # shellcheck disable=SC2035 adb logcat *:E -v color & diff --git a/contrib/instrumentation-file-opening-on-tablet.sh b/contrib/instrumentation-file-opening-on-tablet.sh index 08535b0fc..db8b7e919 100644 --- a/contrib/instrumentation-file-opening-on-tablet.sh +++ b/contrib/instrumentation-file-opening-on-tablet.sh @@ -3,6 +3,10 @@ # Enable Wi-Fi on the emulator adb shell svc wifi enable adb logcat -c +# Check if the stylus_handwriting_enabled setting exists before disabling +if adb shell settings list secure | grep -q "stylus_handwriting_enabled"; then + adb shell settings put secure stylus_handwriting_enabled 0 +fi # shellcheck disable=SC2035 adb logcat *:E -v color & @@ -42,6 +46,10 @@ while [ $retry -le 3 ]; do # Enable Wi-Fi on the emulator adb shell svc wifi enable adb logcat -c + # Check if the stylus_handwriting_enabled setting exists before disabling + if adb shell settings list secure | grep -q "stylus_handwriting_enabled"; then + adb shell settings put secure stylus_handwriting_enabled 0 + fi # shellcheck disable=SC2035 adb logcat *:E -v color & diff --git a/contrib/instrumentation-release.sh b/contrib/instrumentation-release.sh index ec309b7ff..e44bce4af 100644 --- a/contrib/instrumentation-release.sh +++ b/contrib/instrumentation-release.sh @@ -3,6 +3,10 @@ # Enable Wi-Fi on the emulator adb shell svc wifi enable adb logcat -c +# Check if the stylus_handwriting_enabled setting exists before disabling +if adb shell settings list secure | grep -q "stylus_handwriting_enabled"; then + adb shell settings put secure stylus_handwriting_enabled 0 +fi # shellcheck disable=SC2035 adb logcat *:E -v color & @@ -42,6 +46,10 @@ while [ $retry -le 3 ]; do # Enable Wi-Fi on the emulator adb shell svc wifi enable adb logcat -c + # Check if the stylus_handwriting_enabled setting exists before disabling + if adb shell settings list secure | grep -q "stylus_handwriting_enabled"; then + adb shell settings put secure stylus_handwriting_enabled 0 + fi # shellcheck disable=SC2035 adb logcat *:E -v color & diff --git a/contrib/instrumentation.sh b/contrib/instrumentation.sh index 55628ce13..955b7fe63 100644 --- a/contrib/instrumentation.sh +++ b/contrib/instrumentation.sh @@ -3,6 +3,10 @@ # Enable Wi-Fi on the emulator adb shell svc wifi enable adb logcat -c +# Check if the stylus_handwriting_enabled setting exists before disabling +if adb shell settings list secure | grep -q "stylus_handwriting_enabled"; then + adb shell settings put secure stylus_handwriting_enabled 0 +fi # shellcheck disable=SC2035 adb logcat *:E -v color & @@ -43,6 +47,10 @@ while [ $retry -le 3 ]; do # Enable Wi-Fi on the emulator adb shell svc wifi enable adb logcat -c + # Check if the stylus_handwriting_enabled setting exists before disabling + if adb shell settings list secure | grep -q "stylus_handwriting_enabled"; then + adb shell settings put secure stylus_handwriting_enabled 0 + fi # shellcheck disable=SC2035 adb logcat *:E -v color & diff --git a/core/build.gradle.kts b/core/build.gradle.kts index 20216805f..b2b73d07a 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -20,6 +20,7 @@ apply(plugin = "io.objectbox") android { defaultConfig { buildConfigField("long", "VERSION_CODE", "".getVersionCode().toString()) + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" } buildTypes { getByName("release") { diff --git a/core/src/main/AndroidManifest.xml b/core/src/main/AndroidManifest.xml index d8ddfacdf..adf34d1cb 100644 --- a/core/src/main/AndroidManifest.xml +++ b/core/src/main/AndroidManifest.xml @@ -52,12 +52,13 @@ @@ -91,6 +92,7 @@ + = Build.VERSION_CODES.TIRAMISU -> CompatV33() + Build.VERSION.SDK_INT >= Build.VERSION_CODES.BAKLAVA -> CompatV36() + Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU -> CompatV33() else -> CompatV25() } @@ -36,10 +37,6 @@ class CompatHelper private constructor() { /** Singleton instance of [CompatHelper] */ private val instance by lazy(::CompatHelper) - /** Get the current Android API level. */ - val sdkVersion: Int - get() = Build.VERSION.SDK_INT - val compat get() = instance.compatValue /** @@ -79,5 +76,7 @@ class CompatHelper private constructor() { fun ConnectivityManager.isWifi(): Boolean = compat.isWifi(this) + + fun String.convertToLocal() = compat.convertToLocal(this) } } diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/compat/CompatV25.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/compat/CompatV25.kt index f2afc8db3..191ed6380 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/compat/CompatV25.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/compat/CompatV25.kt @@ -26,6 +26,7 @@ import android.content.pm.ResolveInfo import android.net.ConnectivityManager import android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET import android.net.NetworkCapabilities.TRANSPORT_WIFI +import java.util.Locale open class CompatV25 : Compat { override fun queryIntentActivities( @@ -61,4 +62,6 @@ open class CompatV25 : Compat { return connectivity.getNetworkCapabilities(connectivity.activeNetwork) ?.hasTransport(TRANSPORT_WIFI) == true } + + override fun convertToLocal(language: String): Locale = Locale(language) } diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/compat/CompatV33.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/compat/CompatV33.kt index 004475aab..c1854b469 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/compat/CompatV33.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/compat/CompatV33.kt @@ -18,17 +18,17 @@ package org.kiwix.kiwixmobile.core.compat -import android.annotation.TargetApi import android.content.Intent import android.content.pm.PackageInfo import android.content.pm.PackageManager import android.content.pm.PackageManager.PackageInfoFlags import android.content.pm.ResolveInfo import android.net.ConnectivityManager +import android.os.Build.VERSION_CODES.TIRAMISU +import androidx.annotation.RequiresApi +import java.util.Locale -const val API_33 = 33 - -@TargetApi(API_33) +@RequiresApi(TIRAMISU) open class CompatV33 : Compat { private val compatV25 = CompatV25() override fun queryIntentActivities( @@ -52,4 +52,6 @@ open class CompatV33 : Compat { override fun isWifi(connectivity: ConnectivityManager): Boolean = compatV25.isWifi(connectivity) + + override fun convertToLocal(language: String): Locale = compatV25.convertToLocal(language) } diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/compat/CompatV36.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/compat/CompatV36.kt new file mode 100644 index 000000000..6a5524e97 --- /dev/null +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/compat/CompatV36.kt @@ -0,0 +1,53 @@ +/* + * Kiwix Android + * Copyright (c) 2025 Kiwix + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package org.kiwix.kiwixmobile.core.compat + +import android.content.Intent +import android.content.pm.PackageInfo +import android.content.pm.PackageManager +import android.content.pm.ResolveInfo +import android.net.ConnectivityManager +import android.os.Build.VERSION_CODES.BAKLAVA +import androidx.annotation.RequiresApi +import java.util.Locale + +@RequiresApi(BAKLAVA) +open class CompatV36 : Compat { + private val compatV33 = CompatV33() + override fun queryIntentActivities( + packageManager: PackageManager, + intent: Intent, + flags: ResolveInfoFlagsCompat + ): List = compatV33.queryIntentActivities(packageManager, intent, flags) + + override fun getPackageInformation( + packageName: String, + packageManager: PackageManager, + flag: Int + ): PackageInfo = + compatV33.getPackageInformation(packageName, packageManager, flag) + + override fun isNetworkAvailable(connectivity: ConnectivityManager): Boolean = + compatV33.isNetworkAvailable(connectivity) + + override fun isWifi(connectivity: ConnectivityManager): Boolean = + compatV33.isWifi(connectivity) + + override fun convertToLocal(language: String): Locale = Locale.of(language) +} diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/dao/entities/LanguageEntity.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/dao/entities/LanguageEntity.kt index 0d5f6ff96..26690ebf9 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/dao/entities/LanguageEntity.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/dao/entities/LanguageEntity.kt @@ -22,6 +22,7 @@ import io.objectbox.annotation.Convert import io.objectbox.annotation.Entity import io.objectbox.annotation.Id import io.objectbox.converter.PropertyConverter +import org.kiwix.kiwixmobile.core.compat.CompatHelper.Companion.convertToLocal import org.kiwix.kiwixmobile.core.zim_manager.Language import java.util.Locale @@ -35,7 +36,7 @@ data class LanguageEntity( ) { constructor(language: Language) : this( 0, - Locale(language.languageCode), + language.languageCode.convertToLocal(), language.active, language.occurencesOfLanguage ) @@ -49,5 +50,5 @@ class StringToLocaleConverter : PropertyConverter { entityProperty?.isO3Language ?: Locale.ENGLISH.isO3Language override fun convertToEntityProperty(databaseValue: String?): Locale = - databaseValue?.let(::Locale) ?: Locale.ENGLISH + databaseValue?.convertToLocal() ?: Locale.ENGLISH } diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/extensions/ContextExtensions.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/extensions/ContextExtensions.kt index ee4aa6a26..f2ae7a722 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/extensions/ContextExtensions.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/extensions/ContextExtensions.kt @@ -33,6 +33,9 @@ import android.util.TypedValue import android.widget.Toast import androidx.annotation.AttrRes import androidx.core.content.ContextCompat +import androidx.core.graphics.createBitmap +import androidx.core.graphics.drawable.toDrawable +import androidx.core.graphics.scale import org.kiwix.kiwixmobile.core.base.BaseBroadcastReceiver import java.util.Locale @@ -87,14 +90,9 @@ fun Context.getResizedDrawable(resourceId: Int, width: Int, height: Int): Drawab val drawable = ContextCompat.getDrawable(this, resourceId) return if (drawable != null) { - val bitmap = Bitmap.createScaledBitmap( - getBitmapFromDrawable(drawable), - width, - height, - false - ) + val bitmap = getBitmapFromDrawable(drawable).scale(width, height, false) - BitmapDrawable(resources, bitmap).apply { + bitmap.toDrawable(resources).apply { bounds = drawable.bounds } } else { @@ -107,11 +105,7 @@ fun Context.getBitmapFromDrawable(drawable: Drawable): Bitmap { return drawable.bitmap } - val bitmap = Bitmap.createBitmap( - drawable.intrinsicWidth, - drawable.intrinsicHeight, - Bitmap.Config.ARGB_8888 - ) + val bitmap = createBitmap(drawable.intrinsicWidth, drawable.intrinsicHeight) val canvas = Canvas(bitmap) drawable.setBounds(0, 0, canvas.width, canvas.height) drawable.draw(canvas) diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/extensions/SearchViewExtensions.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/extensions/SearchViewExtensions.kt index b9a162a40..29890d1a8 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/extensions/SearchViewExtensions.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/extensions/SearchViewExtensions.kt @@ -18,6 +18,7 @@ package org.kiwix.kiwixmobile.core.extensions +import android.annotation.SuppressLint import android.content.Context import android.view.ViewGroup.LayoutParams import android.widget.EditText @@ -29,6 +30,7 @@ import org.kiwix.kiwixmobile.core.R const val CLOSE_ICON_PADDING = 30 +@SuppressLint("PrivateResource") fun SearchView.setUpSearchView(context: Context) { val heightAndWidth = context.resources.getDimensionPixelSize( R.dimen.material_minimum_height_and_width diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/main/CoreReaderFragment.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/main/CoreReaderFragment.kt index 884264883..ae5c1af46 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/main/CoreReaderFragment.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/main/CoreReaderFragment.kt @@ -72,6 +72,7 @@ import androidx.constraintlayout.widget.Group import androidx.coordinatorlayout.widget.CoordinatorLayout import androidx.core.app.ActivityCompat import androidx.core.content.ContextCompat +import androidx.core.content.edit import androidx.core.net.toUri import androidx.core.view.GravityCompat import androidx.core.view.MenuHost @@ -2516,7 +2517,6 @@ abstract class CoreReaderFragment : SharedPreferenceUtil.PREF_KIWIX_MOBILE, 0 ) - val editor = settings.edit() val webViewHistoryEntityList = arrayListOf() webViewList.forEachIndexed { index, view -> if (view.url == null) return@forEachIndexed @@ -2525,9 +2525,10 @@ abstract class CoreReaderFragment : withContext(Dispatchers.IO) { repositoryActions?.saveWebViewPageHistory(webViewHistoryEntityList) } - editor.putString(TAG_CURRENT_FILE, zimReaderContainer?.zimReaderSource?.toDatabase()) - editor.putInt(TAG_CURRENT_TAB, currentWebViewIndex) - editor.apply() + settings.edit { + putString(TAG_CURRENT_FILE, zimReaderContainer?.zimReaderSource?.toDatabase()) + putInt(TAG_CURRENT_TAB, currentWebViewIndex) + } Log.d( TAG_KIWIX, "Save current zim file to preferences: " + diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/qr/GenerateQR.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/qr/GenerateQR.kt index a64d7493f..dd332e6b1 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/qr/GenerateQR.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/qr/GenerateQR.kt @@ -18,6 +18,7 @@ package org.kiwix.kiwixmobile.core.qr +import android.annotation.SuppressLint import android.graphics.Bitmap import android.graphics.Color import com.google.zxing.BarcodeFormat @@ -39,6 +40,7 @@ class GenerateQR @Inject constructor() { * @param foregroundColor The color of the QR code. * @param backgroundColor The background color of the QR code. */ + @SuppressLint("UseKtx") fun createQR( code: String, size: Int = 512, diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/settings/CorePrefsFragment.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/settings/CorePrefsFragment.kt index ed4b04f65..d14aa3624 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/settings/CorePrefsFragment.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/settings/CorePrefsFragment.kt @@ -46,6 +46,7 @@ import org.kiwix.kiwixmobile.core.CoreApp.Companion.coreComponent import org.kiwix.kiwixmobile.core.CoreApp.Companion.instance import org.kiwix.kiwixmobile.core.DarkModeConfig import org.kiwix.kiwixmobile.core.R +import org.kiwix.kiwixmobile.core.compat.CompatHelper.Companion.convertToLocal import org.kiwix.kiwixmobile.core.compat.CompatHelper.Companion.getPackageInformation import org.kiwix.kiwixmobile.core.compat.CompatHelper.Companion.getVersionCode import org.kiwix.kiwixmobile.core.dao.LibkiwixBookmarks @@ -169,9 +170,7 @@ abstract class CorePrefsFragment : if (selectedLang == Locale.ROOT.toString()) { getString(R.string.device_default) } else { - Locale( - selectedLang - ).displayLanguage + selectedLang.convertToLocal().displayLanguage } languagePref.onPreferenceChangeListener = Preference.OnPreferenceChangeListener { _, newValue -> @@ -206,7 +205,7 @@ abstract class CorePrefsFragment : val entries = arrayOfNulls(languageCodeList.size) entries[0] = getString(R.string.device_default) for (i in 1 until languageCodeList.size) { - val locale = Locale(languageCodeList[i]) + val locale = languageCodeList[i].convertToLocal() entries[i] = locale.displayLanguage + " (" + locale.getDisplayLanguage(locale) + ") " } return entries @@ -389,7 +388,7 @@ abstract class CorePrefsFragment : } try { fileSelectLauncher.launch(Intent.createChooser(intent, "Select a bookmark file")) - } catch (ex: ActivityNotFoundException) { + } catch (_: ActivityNotFoundException) { activity.toast( resources.getString(R.string.no_app_found_to_select_bookmark_file), Toast.LENGTH_SHORT diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/utils/BookUtils.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/utils/BookUtils.kt index 794e1017d..0813b99c6 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/utils/BookUtils.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/utils/BookUtils.kt @@ -17,13 +17,15 @@ */ package org.kiwix.kiwixmobile.core.utils +import org.kiwix.kiwixmobile.core.compat.CompatHelper.Companion.convertToLocal import java.util.Locale /** * Created by mhutti1 on 19/04/17. */ class BookUtils { - val localeMap = Locale.getISOLanguages().map(::Locale).associateBy { it.isO3Language } + val localeMap = + Locale.getISOLanguages().map { it.convertToLocal() }.associateBy { it.isO3Language } // Get the language from the language codes of the parsed xml stream @Suppress("MagicNumber") diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/utils/LanguageContainer.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/utils/LanguageContainer.kt index 9e0503a45..b4d172ad3 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/utils/LanguageContainer.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/utils/LanguageContainer.kt @@ -18,6 +18,7 @@ package org.kiwix.kiwixmobile.core.utils +import org.kiwix.kiwixmobile.core.compat.CompatHelper.Companion.convertToLocal import java.util.Locale class LanguageContainer private constructor(val languageCode: String, val languageName: String) { @@ -25,7 +26,7 @@ class LanguageContainer private constructor(val languageCode: String, val langua companion object { private fun chooseLanguageName(languageCode: String): String { - val displayLanguage = Locale(languageCode).displayLanguage + val displayLanguage = languageCode.convertToLocal().displayLanguage return if (displayLanguage.length == 2 || displayLanguage.isEmpty()) { Locale.ENGLISH.displayLanguage } else { diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/utils/SharedPreferenceUtil.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/utils/SharedPreferenceUtil.kt index 830ac5574..3d952208b 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/utils/SharedPreferenceUtil.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/utils/SharedPreferenceUtil.kt @@ -21,6 +21,7 @@ import android.content.Context import android.content.ContextWrapper import android.content.SharedPreferences import android.os.Build +import androidx.annotation.ChecksSdkIntAtLeast import androidx.appcompat.app.AppCompatDelegate import androidx.core.content.edit import androidx.preference.PreferenceManager @@ -136,9 +137,6 @@ class SharedPreferenceUtil @Inject constructor(val context: Context) { ContextWrapper(context).externalMediaDirs[0]?.path ?: context.filesDir.path // a workaround for emulators - fun getPrefStorageTitle(defaultTitle: String): String = - sharedPreferences.getString(PREF_STORAGE_TITLE, defaultTitle) ?: defaultTitle - fun putPrefBookMarkMigrated(isMigrated: Boolean) = sharedPreferences.edit { putBoolean(PREF_BOOKMARKS_MIGRATED, isMigrated) } @@ -168,9 +166,6 @@ class SharedPreferenceUtil @Inject constructor(val context: Context) { _prefWifiOnlys.onNext(wifiOnly) } - fun putPrefStorageTitle(storageTitle: String) = - sharedPreferences.edit { putString(PREF_STORAGE_TITLE, storageTitle) } - fun putPrefStorage(storage: String) { sharedPreferences.edit { putString(PREF_STORAGE, storage) } _prefStorages.onNext(storage) @@ -294,9 +289,11 @@ class SharedPreferenceUtil @Inject constructor(val context: Context) { path.substringBefore(context.getString(R.string.android_directory_seperator)) } + @ChecksSdkIntAtLeast(api = Build.VERSION_CODES.R) fun isPlayStoreBuildWithAndroid11OrAbove(): Boolean = isPlayStoreBuild && Build.VERSION.SDK_INT >= Build.VERSION_CODES.R + @ChecksSdkIntAtLeast(api = Build.VERSION_CODES.R) fun isNotPlayStoreBuildWithAndroid11OrAbove(): Boolean = !isPlayStoreBuild && Build.VERSION.SDK_INT >= Build.VERSION_CODES.R @@ -316,7 +313,6 @@ class SharedPreferenceUtil @Inject constructor(val context: Context) { private const val PREF_BACK_TO_TOP = "pref_backtotop" private const val PREF_FULLSCREEN = "pref_fullscreen" private const val PREF_NEW_TAB_BACKGROUND = "pref_newtab_background" - private const val PREF_STORAGE_TITLE = "pref_selected_title" const val PREF_EXTERNAL_LINK_POPUP = "pref_external_link_popup" const val PREF_SHOW_STORAGE_OPTION = "show_storgae_option" private const val PREF_IS_FIRST_RUN = "isFirstRun" diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/utils/SimpleTextWatcher.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/utils/SimpleTextWatcher.kt deleted file mode 100644 index c401569d7..000000000 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/utils/SimpleTextWatcher.kt +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Kiwix Android - * Copyright (c) 2020 Kiwix - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - */ - -package org.kiwix.kiwixmobile.core.utils - -import android.text.Editable -import android.text.TextWatcher - -class SimpleTextWatcher( - private val onTextWatcherChangeAction: (CharSequence?, Int, Int, Int) -> Unit -) : TextWatcher { - @SuppressWarnings("EmptyFunctionBlock") - override fun afterTextChanged(p0: Editable?) { - } - - @SuppressWarnings("EmptyFunctionBlock") - override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) { - } - - override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) { - onTextWatcherChangeAction.invoke(s, start, before, count) - } -} diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/zim_manager/Language.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/zim_manager/Language.kt index e341d53ae..b979fa463 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/zim_manager/Language.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/zim_manager/Language.kt @@ -20,6 +20,7 @@ package org.kiwix.kiwixmobile.core.zim_manager import android.os.Parcelable import kotlinx.parcelize.Parcelize +import org.kiwix.kiwixmobile.core.compat.CompatHelper.Companion.convertToLocal import java.util.Locale @Parcelize @@ -51,7 +52,7 @@ data class Language constructor( languageCode: String, active: Boolean, occurrencesOfLanguage: Int - ) : this(Locale(languageCode), active, occurrencesOfLanguage) + ) : this(languageCode.convertToLocal(), active, occurrencesOfLanguage) override fun equals(other: Any?): Boolean = (other as Language).language == language && other.active == active diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/zim_manager/fileselect_view/BooksOnDiskListItem.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/zim_manager/fileselect_view/BooksOnDiskListItem.kt index d831cf843..0ba5e8757 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/zim_manager/fileselect_view/BooksOnDiskListItem.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/zim_manager/fileselect_view/BooksOnDiskListItem.kt @@ -18,6 +18,7 @@ package org.kiwix.kiwixmobile.core.zim_manager.fileselect_view +import org.kiwix.kiwixmobile.core.compat.CompatHelper.Companion.convertToLocal import org.kiwix.kiwixmobile.core.dao.entities.BookOnDiskEntity import org.kiwix.kiwixmobile.core.dao.entities.DownloadRoomEntity import org.kiwix.kiwixmobile.core.entity.LibraryNetworkEntity @@ -50,7 +51,7 @@ sealed class BooksOnDiskListItem { override val id: Long = databaseId ) : BooksOnDiskListItem() { val locale: Locale by lazy { - Locale(book.language) + book.language.convertToLocal() } constructor(bookOnDiskEntity: BookOnDiskEntity) : this( diff --git a/core/src/main/res/drawable/ic_qr_code.xml b/core/src/main/res/drawable/ic_qr_code.xml deleted file mode 100644 index b3de5671c..000000000 --- a/core/src/main/res/drawable/ic_qr_code.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/core/src/main/res/layout/header_language.xml b/core/src/main/res/layout/header_language.xml deleted file mode 100644 index 0b003c81e..000000000 --- a/core/src/main/res/layout/header_language.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - diff --git a/core/src/main/res/layout/item_book.xml b/core/src/main/res/layout/item_book.xml deleted file mode 100644 index c64cbfcf0..000000000 --- a/core/src/main/res/layout/item_book.xml +++ /dev/null @@ -1,130 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/core/src/main/res/menu/menu_add_note_dialog.xml b/core/src/main/res/menu/menu_add_note_dialog.xml deleted file mode 100644 index 911016178..000000000 --- a/core/src/main/res/menu/menu_add_note_dialog.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - - - diff --git a/core/src/test/java/org/kiwix/kiwixmobile/core/dao/NewBookDaoTest.kt b/core/src/test/java/org/kiwix/kiwixmobile/core/dao/NewBookDaoTest.kt index 1010622c1..338de94cb 100644 --- a/core/src/test/java/org/kiwix/kiwixmobile/core/dao/NewBookDaoTest.kt +++ b/core/src/test/java/org/kiwix/kiwixmobile/core/dao/NewBookDaoTest.kt @@ -91,7 +91,9 @@ internal class NewBookDaoTest { } } - private fun expectEmissionOfExistingAndNotExistingBook(isInTrashFolder: Boolean = false): Pair { + private fun expectEmissionOfExistingAndNotExistingBook( + isInTrashFolder: Boolean = false + ): Pair { val query: Query = mockk() every { box.query().build() } returns query val zimReaderSourceThatExists = mockk() diff --git a/core/src/test/java/org/kiwix/kiwixmobile/core/dao/NewBookmarksDaoTest.kt b/core/src/test/java/org/kiwix/kiwixmobile/core/dao/NewBookmarksDaoTest.kt index 4b6955b0c..a01d40020 100644 --- a/core/src/test/java/org/kiwix/kiwixmobile/core/dao/NewBookmarksDaoTest.kt +++ b/core/src/test/java/org/kiwix/kiwixmobile/core/dao/NewBookmarksDaoTest.kt @@ -18,17 +18,17 @@ package org.kiwix.kiwixmobile.core.dao -import org.junit.jupiter.api.Test import io.mockk.every import io.mockk.mockk import io.mockk.verify import io.objectbox.Box import io.objectbox.query.Query import io.objectbox.query.QueryBuilder -import org.kiwix.kiwixmobile.core.dao.entities.BookmarkEntity_ +import org.junit.jupiter.api.Test import org.kiwix.kiwixmobile.core.dao.entities.BookmarkEntity -import org.kiwix.kiwixmobile.core.page.bookmark +import org.kiwix.kiwixmobile.core.dao.entities.BookmarkEntity_ import org.kiwix.kiwixmobile.core.page.adapter.Page +import org.kiwix.kiwixmobile.core.page.bookmark import org.kiwix.kiwixmobile.core.page.bookmark.adapter.BookmarkItem import org.kiwix.kiwixmobile.core.reader.ZimFileReader @@ -69,7 +69,6 @@ internal class NewBookmarksDaoTest { every { bookmarkItem.zimName } returns "" every { bookmarkItem.databaseId } returns 0L newBookmarksDao.getCurrentZimBookmarksUrl(zimFileReader) - val bookmarkEntity: BookmarkEntity = mockk() every { query.property(BookmarkEntity_.bookmarkUrl).findStrings().toList().distinct() } returns listOf("") diff --git a/core/src/test/java/org/kiwix/kiwixmobile/core/entity/MetaLinkNetworkEntityTest.kt b/core/src/test/java/org/kiwix/kiwixmobile/core/entity/MetaLinkNetworkEntityTest.kt index 2bf805b39..81dee0a95 100644 --- a/core/src/test/java/org/kiwix/kiwixmobile/core/entity/MetaLinkNetworkEntityTest.kt +++ b/core/src/test/java/org/kiwix/kiwixmobile/core/entity/MetaLinkNetworkEntityTest.kt @@ -27,13 +27,12 @@ class MetaLinkNetworkEntityTest { @Throws(Exception::class) fun testDeserialize() { val serializer = Persister() - val result = - serializer.read( - MetaLinkNetworkEntity::class.java, - MetaLinkNetworkEntityTest::class.java.classLoader!!.getResourceAsStream( - "wikipedia_af_all_nopic_2016-05.zim.meta4" - ) + val result = serializer.read( + MetaLinkNetworkEntity::class.java, + MetaLinkNetworkEntityTest::class.java.classLoader!!.getResourceAsStream( + "wikipedia_af_all_nopic_2016-05.zim.meta4" ) + ) result?.urls?.let { MetaLinkNetworkEntityUrlAssert(it).hasItems( listOf( @@ -68,9 +67,7 @@ class MetaLinkNetworkEntityTest { } // Basic file attributes assertThat(result.file?.name).isEqualTo("wikipedia_af_all_nopic_2016-05.zim") - assertThat(result.file?.size).isEqualTo(63973123L) - // File hashes assertThat(result.file?.getHash("md5")).isEqualTo("6f06866b61c4a921b57f28cfd4307220") assertThat( @@ -103,21 +100,17 @@ class MetaLinkNetworkEntityTest { */ class MetaLinkNetworkEntityUrlAssert( actual: List - ) : - AbstractAssert>( - actual, - MetaLinkNetworkEntityUrlAssert::class.java - ) { + ) : AbstractAssert>( + actual, + MetaLinkNetworkEntityUrlAssert::class.java + ) { private fun intersectionWith( first: List, second: List, function: (S, T) -> Boolean ): Boolean { val filtered = first.filter { a -> second.any { b -> function(a, b) } } - if (filtered.isNotEmpty()) { - return true - } - return false + return filtered.isNotEmpty() } fun hasItems(items: List): Boolean { diff --git a/core/src/test/java/org/kiwix/kiwixmobile/core/search/viewmodel/SearchStateTest.kt b/core/src/test/java/org/kiwix/kiwixmobile/core/search/viewmodel/SearchStateTest.kt index 116177b7c..9aca9dabe 100644 --- a/core/src/test/java/org/kiwix/kiwixmobile/core/search/viewmodel/SearchStateTest.kt +++ b/core/src/test/java/org/kiwix/kiwixmobile/core/search/viewmodel/SearchStateTest.kt @@ -43,8 +43,9 @@ internal class SearchStateTest { val estimatedMatches = 100 every { suggestionSearchWrapper.estimatedMatches } returns estimatedMatches.toLong() // Settings list to hasNext() to ensure it returns true only for the first call. - // Otherwise, if we do not set this, the method will always return true when checking if the iterator has a next value, - // causing our test case to get stuck in an infinite loop due to this explicit setting. + // Otherwise, if we do not set this, the method will always return true when + // checking if the iterator has a next value, causing our test case to get stuck + // in an infinite loop due to this explicit setting. every { searchIteratorWrapper.hasNext() } returnsMany listOf(true, false) every { searchIteratorWrapper.next() } returns entryWrapper every { entryWrapper.title } returns searchTerm @@ -119,7 +120,8 @@ internal class SearchStateTest { every { entryWrapper.path } returns "path" every { suggestionSearchWrapper.getResults(any(), any()) } returns searchIteratorWrapper - val searchResultsWithTerm = SearchResultsWithTerm(searchTerm, suggestionSearchWrapper, mockk()) + val searchResultsWithTerm = + SearchResultsWithTerm(searchTerm, suggestionSearchWrapper, mockk()) val searchState = SearchState(searchTerm, searchResultsWithTerm, emptyList(), FromWebView) var list: List? = emptyList() var list1: List? = emptyList() diff --git a/core/src/test/java/org/kiwix/kiwixmobile/core/search/viewmodel/SearchViewModelTest.kt b/core/src/test/java/org/kiwix/kiwixmobile/core/search/viewmodel/SearchViewModelTest.kt index 3b4103db6..1aadfb56b 100644 --- a/core/src/test/java/org/kiwix/kiwixmobile/core/search/viewmodel/SearchViewModelTest.kt +++ b/core/src/test/java/org/kiwix/kiwixmobile/core/search/viewmodel/SearchViewModelTest.kt @@ -50,7 +50,6 @@ import org.kiwix.kiwixmobile.core.dao.RecentSearchRoomDao import org.kiwix.kiwixmobile.core.reader.ZimFileReader import org.kiwix.kiwixmobile.core.reader.ZimReaderContainer import org.kiwix.kiwixmobile.core.search.adapter.SearchListItem.RecentSearchListItem -import org.kiwix.kiwixmobile.core.search.adapter.SearchListItem.ZimSearchResultListItem import org.kiwix.kiwixmobile.core.search.viewmodel.Action.ActivityResultReceived import org.kiwix.kiwixmobile.core.search.viewmodel.Action.ClickedSearchInText import org.kiwix.kiwixmobile.core.search.viewmodel.Action.ConfirmedDelete @@ -123,7 +122,6 @@ internal class SearchViewModelTest { @Test fun `SearchState combines sources from inputs`() = runTest { - val item = ZimSearchResultListItem("", "") val searchTerm = "searchTerm" val searchOrigin = FromWebView val suggestionSearch: SuggestionSearch = mockk() diff --git a/core/src/test/java/org/kiwix/kiwixmobile/core/search/viewmodel/SuggestionIteratorWrapper.kt b/core/src/test/java/org/kiwix/kiwixmobile/core/search/viewmodel/SuggestionIteratorWrapper.kt index 85340cd69..e2299e646 100644 --- a/core/src/test/java/org/kiwix/kiwixmobile/core/search/viewmodel/SuggestionIteratorWrapper.kt +++ b/core/src/test/java/org/kiwix/kiwixmobile/core/search/viewmodel/SuggestionIteratorWrapper.kt @@ -21,7 +21,9 @@ package org.kiwix.kiwixmobile.core.search.viewmodel import org.kiwix.libzim.SuggestionIterator class SuggestionIteratorWrapper : SuggestionIterator() { - override fun remove() {} + override fun remove() { + // Do nothing just to ignore the EmptyFunctionBlock detekt error. + } override fun hasNext(): Boolean = super.hasNext() override fun next(): SuggestionItemWrapper = super.next() as SuggestionItemWrapper diff --git a/custom/build.gradle.kts b/custom/build.gradle.kts index 665522333..3284ccdaf 100644 --- a/custom/build.gradle.kts +++ b/custom/build.gradle.kts @@ -4,16 +4,16 @@ import com.android.build.gradle.internal.dsl.ProductFlavor import custom.CustomApps import custom.createPublisher import custom.transactionWithCommit -import plugin.KiwixConfigurationPlugin -import java.net.URI -import java.net.URLDecoder -import java.util.Locale -import java.util.Base64 -import java.io.FileOutputStream import okhttp3.OkHttpClient import okhttp3.Request import okhttp3.ResponseBody +import plugin.KiwixConfigurationPlugin import java.io.FileNotFoundException +import java.io.FileOutputStream +import java.net.URI +import java.net.URLDecoder +import java.util.Base64 +import java.util.Locale plugins { android @@ -24,6 +24,7 @@ plugins.apply(KiwixConfigurationPlugin::class) android { defaultConfig { applicationId = "org.kiwix" + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" } flavorDimensions += "default" @@ -59,8 +60,8 @@ android { } } -fun ProductFlavor.createDownloadTask(file: File): Task { - return tasks.create( +fun ProductFlavor.createDownloadTask(file: File): TaskProvider { + return tasks.register( "download${ name.replaceFirstChar { if (it.isLowerCase()) it.titlecase(Locale.getDefault()) else "$it" @@ -156,8 +157,8 @@ fun writeZimFileDataInChunk( outputStream?.close() } -fun ProductFlavor.createDownloadTaskForPlayAssetDelivery(file: File): Task { - return tasks.create( +fun ProductFlavor.createDownloadTaskForPlayAssetDelivery(file: File): TaskProvider { + return tasks.register( "download${ name.replaceFirstChar { if (it.isLowerCase()) it.titlecase(Locale.getDefault()) else "$it" @@ -205,10 +206,10 @@ val String.removeAuthenticationFromUrl: String fun ProductFlavor.createPublishApkWithExpansionTask( file: File, applicationVariants: DomainObjectSet -): Task { +): TaskProvider { val capitalizedName = name.replaceFirstChar { if (it.isLowerCase()) it.titlecase(Locale.getDefault()) else "$it" } - return tasks.create("publish${capitalizedName}ReleaseApkWithExpansionFile") { + return tasks.register("publish${capitalizedName}ReleaseApkWithExpansionFile") { group = "publishing" description = "Uploads $capitalizedName to the Play Console with an Expansion file" doLast { @@ -236,10 +237,10 @@ fun DomainObjectSet.releaseVariantsFor(productFlavor: Produc .filter { !it.outputFileName.contains("universal") } .sortedBy { it.versionCodeOverride } -fun ProductFlavor.createPublishBundleWithAssetPlayDelivery(): Task { +fun ProductFlavor.createPublishBundleWithAssetPlayDelivery(): TaskProvider { val capitalizedName = name.replaceFirstChar { if (it.isLowerCase()) it.titlecase(Locale.getDefault()) else "$it" } - return tasks.create("publish${capitalizedName}ReleaseBundleWithPlayAssetDelivery") { + return tasks.register("publish${capitalizedName}ReleaseBundleWithPlayAssetDelivery") { group = "publishing" description = "Uploads $capitalizedName to the Play Console with an Play Asset delivery mode" doLast { @@ -249,7 +250,7 @@ fun ProductFlavor.createPublishBundleWithAssetPlayDelivery(): Task { .transactionWithCommit(packageName) { val generatedBundleFile = File( - "$buildDir/outputs/bundle/${capitalizedName.lowercase(Locale.getDefault())}" + + "${layout.buildDirectory}/outputs/bundle/${capitalizedName.lowercase(Locale.getDefault())}" + "Release/custom-${capitalizedName.lowercase(Locale.getDefault())}-release.aab" ) if (generatedBundleFile.exists()) { diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index d190455a7..5fdc5bb53 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ #Mon Dec 19 16:13:45 IST 2022 distributionBase=GRADLE_USER_HOME -distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip distributionPath=wrapper/dists zipStorePath=wrapper/dists zipStoreBase=GRADLE_USER_HOME