diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 060cdf5961..d770d82476 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,13 +1,8 @@ name: Publish App on: - push: - branches-ignore: - - '*' - - '*/**' - tags: - - '*' - - '*/**' + release: + types: [published] jobs: publish: @@ -32,19 +27,17 @@ jobs: echo "$SSH_KEY" | base64 -d > ssh_key chmod 600 ssh_key - - name: Set tag variable - run: echo "TAG=$(echo ${GITHUB_REF:10})" >> $GITHUB_ENV - - name: Publish APK to download.kiwix.org env: KEY_ALIAS: ${{ secrets.KEY_ALIAS }} KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }} KEY_STORE_PASSWORD: ${{ secrets.KEY_STORE_PASSWORD }} UNIVERSAL_RELEASE_APK: app/build/outputs/apk/release/*universal*.apk + ARCHIVE_NAME: kiwix-${{ github.event.release.tag_name }}.apk run: | ./gradlew assembleRelease - cp $UNIVERSAL_RELEASE_APK kiwix-${TAG}.apk - scp -P 30022 -vrp -i ssh_key -o StrictHostKeyChecking=no kiwix-${TAG}.apk ci@master.download.kiwix.org:/data/download/release/kiwix-android/ + cp "$UNIVERSAL_RELEASE_APK" "$ARCHIVE_NAME" + scp -P 30022 -vrp -i ssh_key -o StrictHostKeyChecking=no "$ARCHIVE_NAME" ci@master.download.kiwix.org:/data/download/release/kiwix-android/ # This is temporary, once we will publish 3.7.0 then we will uncommented this code. # # This is necessary for F-Droid diff --git a/CHANGELOG b/CHANGELOG index ce2e67b1ea..dc344bea9f 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,8 @@ +3.8.1 +Bug Fixes: +* Application crash occurring when users rapidly typed during a search for an entry. (https://github.com/kiwix/kiwix-android/pull/3558) +* Play Store deep linking issue on Android 12 and above. (https://github.com/kiwix/kiwix-android/pull/3550) + 3.8.0 New Features: * Download pause/resume feature. (https://github.com/kiwix/kiwix-android/pull/3459) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index d85b34bdab..174f160bf3 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -39,6 +39,7 @@ + @@ -52,10 +53,10 @@ - - - - + + + + @@ -66,13 +67,13 @@ - - - - + + + + - + @@ -80,12 +81,12 @@ - - - - + + + + - + @@ -93,10 +94,10 @@ - - - - + + + + @@ -106,10 +107,10 @@ - - - - + + + + diff --git a/app/src/main/res/values-in/strings.xml b/app/src/main/res/values-in/strings.xml index a71be3cbaf..3512589a08 100644 --- a/app/src/main/res/values-in/strings.xml +++ b/app/src/main/res/values-in/strings.xml @@ -1,8 +1,10 @@ Sistem file Anda tidak mendukung file berukuran lebih dari 4GB Mendeteksi apakah sistem file dapat membuat file berukuran 4GB + Terima Berkas diff --git a/buildSrc/src/main/kotlin/Config.kt b/buildSrc/src/main/kotlin/Config.kt index 7caea9131c..208906c98b 100644 --- a/buildSrc/src/main/kotlin/Config.kt +++ b/buildSrc/src/main/kotlin/Config.kt @@ -31,5 +31,5 @@ object Config { // Version Information const val versionMajor = 3 // Major version component of the app's version name and version code. const val versionMinor = 8 // Minor version component of the app's version name and version code. - const val versionPatch = 0 // Patch version component of the app's version name and version code. + const val versionPatch = 1 // Patch version component of the app's version name and version code. } diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/search/SearchFragment.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/search/SearchFragment.kt index 55dbecc41f..d0fa06af50 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/search/SearchFragment.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/search/SearchFragment.kt @@ -61,7 +61,6 @@ import org.kiwix.kiwixmobile.core.search.viewmodel.Action 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.ExitedSearch -import org.kiwix.kiwixmobile.core.search.viewmodel.Action.Filter import org.kiwix.kiwixmobile.core.search.viewmodel.Action.OnItemClick import org.kiwix.kiwixmobile.core.search.viewmodel.Action.OnItemLongClick import org.kiwix.kiwixmobile.core.search.viewmodel.Action.OnOpenInNewTabClick @@ -219,7 +218,7 @@ class SearchFragment : BaseFragment() { searchView?.setOnQueryTextListener( SimpleTextListener { if (it.isNotEmpty()) { - searchViewModel.actions.trySend(Filter(it)).isSuccess + searchViewModel.searchResults(it) } } ) diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/search/viewmodel/SearchViewModel.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/search/viewmodel/SearchViewModel.kt index f284a0422f..e835ee27fa 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/search/viewmodel/SearchViewModel.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/search/viewmodel/SearchViewModel.kt @@ -31,6 +31,8 @@ import kotlinx.coroutines.channels.trySendBlocking import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asFlow import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.debounce +import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.mapLatest import kotlinx.coroutines.flow.receiveAsFlow import kotlinx.coroutines.launch @@ -65,6 +67,8 @@ import org.kiwix.kiwixmobile.core.search.viewmodel.effects.StartSpeechInput import org.kiwix.libzim.SuggestionSearch import javax.inject.Inject +const val DEBOUNCE_DELAY = 500L + @OptIn(ExperimentalCoroutinesApi::class) class SearchViewModel @Inject constructor( private val recentSearchDao: NewRecentSearchDao, @@ -88,10 +92,24 @@ class SearchViewModel @Inject constructor( val actions = Channel(Channel.UNLIMITED) private val filter = ConflatedBroadcastChannel("") private val searchOrigin = ConflatedBroadcastChannel(FromWebView) + private val debouncedSearchQuery = MutableStateFlow("") init { viewModelScope.launch { reducer() } viewModelScope.launch { actionMapper() } + viewModelScope.launch { debouncedSearchQuery() } + } + + private suspend fun debouncedSearchQuery() { + // Observe and collect the debounced search query + debouncedSearchQuery + // Applying debouncing to delay the emission of consecutive search queries + .debounce(DEBOUNCE_DELAY) + // Ensuring that only distinct search queries are processed + .distinctUntilChanged() + .collect { query -> + actions.trySend(Filter(query)).isSuccess + } } @Suppress("DEPRECATION") @@ -201,6 +219,10 @@ class SearchViewModel @Inject constructor( }, LATEST) .subscribeOn(Schedulers.io()) } + + fun searchResults(query: String) { + debouncedSearchQuery.value = query + } } data class SearchResultsWithTerm(val searchTerm: String, val suggestionSearch: SuggestionSearch?) diff --git a/core/src/main/res/values-de/strings.xml b/core/src/main/res/values-de/strings.xml index 79dd948122..d51482380e 100644 --- a/core/src/main/res/values-de/strings.xml +++ b/core/src/main/res/values-de/strings.xml @@ -8,6 +8,7 @@ * FF-11 * FF11 * Falwye +* Fw * IMayBeABitShy * Justman10000 * Killarnee @@ -164,7 +165,7 @@ Sind Sie sicher, dass Sie diesen Download abbrechen möchten? Speichergeräteauswahl Die Vorlesefunktion ist für diese ZIM-Datei nicht aktiviert - Die Initialisierung er Vorlesefunktion ist fehlgeschlagen. Bitte versuchen Sie es noch einmal + Die Initialisierung der Vorlesefunktion ist fehlgeschlagen. Bitte versuchen Sie es noch einmal Unerwarteter Fehler in der Vorlesefunktion. Bitte versuchen Sie es noch einmal Nächste Vorherige diff --git a/core/src/main/res/values-in/strings.xml b/core/src/main/res/values-in/strings.xml index 6d700568ac..b0a4e7d714 100644 --- a/core/src/main/res/values-in/strings.xml +++ b/core/src/main/res/values-in/strings.xml @@ -46,6 +46,7 @@ LANJUT Nyalakan server Matikan server + Tak dapat menemukan alamat IP. Masukkan alamat IP ini ke browser Anda untuk mengakses server %s Galat: Berkas ZIM yang dipilih tidak ditemukan. Zim file tidak dapat dibuka @@ -70,7 +71,9 @@ Hapus item ini? Hapus riwayat Hapus riwayat penelusuran dan tab terakhir + Izin Semua Markah Telah Dibersihkan + Hapus markah buku Bersihkan Semua Riwayat? Bagikan Bagikan ZIM file dengan: @@ -82,6 +85,7 @@ Tahukah Anda? Urungkan Tab ditutup + Tab ditutup Markah ditambahkan Silakan Nilai Kami Nilai! @@ -154,7 +158,11 @@ Jangan tanya lagi Bahasa terpilih: Bahasa lain: + Yah… Ini memalukan Daftar file Zim Anda + Log Aplikasi + Rincian Perangkat + KIRIM RINCIAN Tab baru Ambil konten Favicon @@ -175,6 +183,7 @@ Lihat riwayat semua buku Riwayat pencarian %1$d dipilih + Beralih tab Tutup semua tab Dalam proses Selesai @@ -197,6 +206,8 @@ Beberapa file tidak terhapus %d buku Koneksi gagal + Perangkat Anda: + PERANGKAT SEKITAR FILE UNTUK DIPINDAHKAN Mempersiapkan file untuk dipindahkan.... Melakukan jabat tangan.... @@ -214,7 +225,19 @@ Tidak ada Hasil Tidak Ada Markah Tidak ada Riwayat + Hapus Semua Riwayat? + Hapus Riwayat Terpilih? + Hapus Semua Markah Buku? + Hapus Markah Buku Terpilih? + Nyala + Mati + Otomatis Kirimkan semua detil berikut agar kami dapat menelaah masalahnya %d%% Buka di tab baru + Tab dipulihkan + Tab-tab dipulihkan + Izinkan + Jangan izinkan + Pergi ke Pengaturan diff --git a/core/src/main/res/values-sc/strings.xml b/core/src/main/res/values-sc/strings.xml index b56b84c9e0..aaf7e3062e 100644 --- a/core/src/main/res/values-sc/strings.xml +++ b/core/src/main/res/values-sc/strings.xml @@ -46,6 +46,8 @@ Hotspot Kiwix Avia su servidore Firma su servidore + Custu serbidore est giai in esecutzione. Pro praghere istuda·lu e torra·lu a allùghere. + Non potzo agatare s\'indiritzu IP. Inserta custu indiritzu ip in su navigadore tuo pro intrare in su servidore %s Cumpartzi s\'URL cun àteras aplicatziones Errore: su documentu ZIM ischertadu no est istadu agatadu. @@ -206,7 +208,7 @@ Acabadu In pàusa Fallidu: %s - Su documentu zim non s\'est pòdidu iscarrigare, proa a torrare a installare s\'aplicatzione dae sa butega de aplicatziones + Su documentu zim non s\'est pòdidu iscarrigare. Proa a torrare a installare s\'aplicatzione dae sa butega de aplicatziones Sarva Nota Tìtulu de s\'artìculu de sa Wiki @@ -319,4 +321,8 @@ Cronologia de navigatzione isboidada Bae a sas impostatziones Pro fàghere custa atzione fruni s\'atzessu a sas notìficas + Incarca inoghe pro chircare dispositivos a curtzu. + Su nùmene de su dispositivu tuo at a apàrrere inoghe. + Inoghe, as a atzapare una lista de dispositivos a curtzu. Toca subra de su nùmene de unu dispositivu pro incumintzare cun sa tràmuda de archìvios. + Inoghe as a atzapare sa lista de archìvios ZIM a disponimentu pro sa tràmuda. diff --git a/core/src/main/res/values-xal/strings.xml b/core/src/main/res/values-xal/strings.xml index b0dfc65189..44210e16a0 100644 --- a/core/src/main/res/values-xal/strings.xml +++ b/core/src/main/res/values-xal/strings.xml @@ -23,7 +23,7 @@ Холвасыг оңгдан хавчурһд неетн Цацх цегин үүлчллһнә цувг Цацх цегиг асаҗ чадсн уга. - Цацх цегтн асаһата кевтә. Үрглҗлүлхин төләд цацх цегән унтрахнтн. + Цацх цегтн асалһата кевтә. Үрглҗлүлхин төләд цацх цегән унтрахнтн. Сервериг аҗллулх болмҗ уга Сервериг аҗллулх болмҗ уга. Сервериг амҗлтта аҗллулв. @@ -59,7 +59,7 @@ Уга, байрлҗанав. Хөөннь Неех - Токармҗ + Төкәрмҗ Саң Файлнь амҗлтта уга кегдв Эңгин 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 7fbf391906..6d19e066d2 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 @@ -29,10 +29,12 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.Job import kotlinx.coroutines.channels.Channel +import kotlinx.coroutines.delay import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.consumeAsFlow import kotlinx.coroutines.launch import kotlinx.coroutines.test.TestCoroutineDispatcher +import kotlinx.coroutines.test.TestCoroutineScheduler import kotlinx.coroutines.test.TestCoroutineScope import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.resetMain @@ -57,7 +59,6 @@ import org.kiwix.kiwixmobile.core.search.viewmodel.Action.ClickedSearchInText import org.kiwix.kiwixmobile.core.search.viewmodel.Action.ConfirmedDelete import org.kiwix.kiwixmobile.core.search.viewmodel.Action.CreatedWithArguments import org.kiwix.kiwixmobile.core.search.viewmodel.Action.ExitedSearch -import org.kiwix.kiwixmobile.core.search.viewmodel.Action.Filter import org.kiwix.kiwixmobile.core.search.viewmodel.Action.OnItemClick import org.kiwix.kiwixmobile.core.search.viewmodel.Action.OnItemLongClick import org.kiwix.kiwixmobile.core.search.viewmodel.Action.OnOpenInNewTabClick @@ -109,6 +110,94 @@ internal class SearchViewModelTest { viewModel = SearchViewModel(recentSearchDao, zimReaderContainer, searchResultGenerator) } + @Nested + inner class DebouncedTest { + @Test + fun `Search action is debounced`() = runTest { + val searchTerm1 = "query1" + val searchTerm2 = "query2" + val searchTerm3 = "query3" + val searchOrigin = FromWebView + val suggestionSearch: SuggestionSearch = mockk() + + viewModel.state + .test(this) + .also { + searchResult(searchTerm1, suggestionSearch, testScheduler) + delay(100) + searchResult(searchTerm2, suggestionSearch, testScheduler) + delay(100) + searchResult(searchTerm3, suggestionSearch, testScheduler) + delay(DEBOUNCE_DELAY) + it.assertValue( + SearchState( + searchTerm3, + SearchResultsWithTerm(searchTerm3, suggestionSearch), + emptyList(), + searchOrigin + ) + ) + } + .finish() + } + + @Test + fun `Search action is not debounced if time hasn't passed`() = runTest { + val searchTerm1 = "query1" + val searchTerm2 = "query2" + val searchTerm3 = "query3" + val searchOrigin = FromWebView + val suggestionSearch: SuggestionSearch = mockk() + + viewModel.state + .test(this) + .also { + searchResult(searchTerm1, suggestionSearch, testScheduler) + delay(50) // assume user rapidly typing + searchResult(searchTerm2, suggestionSearch, testScheduler) + delay(50) + // test value is not passed to searchResult as time has not passed and user still typing + // Match if it is initial `SearchState` + it.assertValue( + SearchState( + "", + SearchResultsWithTerm("", null), + emptyList(), + searchOrigin + ) + ) + searchResult(searchTerm3, suggestionSearch, testScheduler) + delay(DEBOUNCE_DELAY) + it.assertValue( + SearchState( + searchTerm3, + SearchResultsWithTerm(searchTerm3, suggestionSearch), + emptyList(), + searchOrigin + ) + ) + } + .finish() + } + + private fun searchResult( + searchTerm: String, + suggestionSearch: SuggestionSearch, + testScheduler: TestCoroutineScheduler + ) { + coEvery { + searchResultGenerator.generateSearchResults(searchTerm, zimFileReader) + } returns suggestionSearch + viewModel.searchResults(searchTerm) + recentsFromDb.trySend(emptyList()).isSuccess + viewModel.actions.trySend(ScreenWasStartedFrom(FromWebView)).isSuccess + testScheduler.apply { + advanceTimeBy(400) + runCurrent() + } + } + } + @Nested inner class StateTests { @Test @@ -252,7 +341,7 @@ internal class SearchViewModelTest { coEvery { searchResultGenerator.generateSearchResults(searchTerm, zimFileReader) } returns suggestionSearch - viewModel.actions.trySend(Filter(searchTerm)).isSuccess + viewModel.searchResults(searchTerm) recentsFromDb.trySend(databaseResults).isSuccess viewModel.actions.trySend(ScreenWasStartedFrom(searchOrigin)).isSuccess testScheduler.apply { diff --git a/custom/src/main/res/values-fr/strings.xml b/custom/src/main/res/values-fr/strings.xml index fc32942e0e..48c0230daa 100644 --- a/custom/src/main/res/values-fr/strings.xml +++ b/custom/src/main/res/values-fr/strings.xml @@ -1,9 +1,10 @@ Réessayer - Installation non valide. Veuillez télécharger Zim.\n Assurez-vous que le Wi-Fi est actif et que vous disposez d’assez d\'espace de stockage. + Installation non valide. Veuillez télécharger Zim.\\n Assurez-vous que le Wi-Fi est actif et que vous disposez d’assez d\\’espace de stockage. diff --git a/lintConfig.xml b/lintConfig.xml index 3266191e3e..62aaad8d0b 100644 --- a/lintConfig.xml +++ b/lintConfig.xml @@ -47,4 +47,5 @@ +