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 @@
+