Commit 29d7906f authored by Habib Kazemi's avatar Habib Kazemi Committed by Geoffrey Métais

Add multiLanguage subtitle download

Signed-off-by: default avatarGeoffrey Métais <geoffrey.metais@gmail.com>
parent dc29abf4
......@@ -155,12 +155,12 @@
app:layout_constraintStart_toStartOf="@+id/left_vertical_guideline"
app:layout_goneMarginTop="40dp" />
<Spinner
<org.videolan.vlc.gui.MultiSelectionSpinner
android:id="@+id/language_list_spinner"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:entries="@array/language_entries"
android:focusable="true"
app:layout_constraintEnd_toStartOf="@+id/right_vertical_guideline"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintTop_toTopOf="@+id/top_horizontal_guideline" />
......
......@@ -11,7 +11,6 @@
</string-array>
<string-array name="language_entries">
<item>All</item>
<item>Albanian</item>
<item>Arabic</item>
<item>Armenian</item>
......@@ -80,7 +79,6 @@
</string-array>
<string-array name="language_values">
<item></item>
<item>alb</item>
<item>ara</item>
<item>arm</item>
......
package org.videolan.vlc.gui
import android.app.AlertDialog
import android.content.Context
import android.content.DialogInterface
import android.content.DialogInterface.OnMultiChoiceClickListener
import android.util.AttributeSet
import android.widget.ArrayAdapter
import android.widget.SpinnerAdapter
import androidx.appcompat.widget.AppCompatSpinner
class MultiSelectionSpinner : AppCompatSpinner, OnMultiChoiceClickListener, DialogInterface.OnDismissListener{
private var items = mutableListOf<String>()
private var selection = mutableListOf<Boolean>()
private var adapter: ArrayAdapter<String>
private var listener: org.videolan.vlc.gui.OnItemSelectListener? = null
val selectedIndices: List<Int>
get() {
return selection.mapIndexed { index, b -> Pair(index, b)}.filter { it.second }.map { it.first }
}
constructor(context: Context) : super(context) {
adapter = ArrayAdapter(context, android.R.layout.simple_spinner_item)
super.setAdapter(adapter)
}
constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {
adapter = ArrayAdapter(context, android.R.layout.simple_spinner_item)
super.setAdapter(adapter)
}
override fun onClick(dialog: DialogInterface, index: Int, isChecked: Boolean) {
if (index < selection.size) {
selection[index] = isChecked
adapter.clear()
adapter.add(buildSelectedItemString())
} else {
throw IllegalArgumentException(
"Argument 'index' is out of bounds.")
}
}
override fun performClick(): Boolean {
val builder = AlertDialog.Builder(context)
builder.setOnDismissListener(this)
builder.setMultiChoiceItems(items.toTypedArray(), selection.toBooleanArray(), this).show()
return true
}
override fun onDismiss(dialog: DialogInterface?) {
listener?.onItemSelect(selectedIndices)
}
override fun setAdapter(adapter: SpinnerAdapter) {
throw RuntimeException(
"setAdapter is not supported by MultiSelectSpinner. Use setItems instead")
}
fun setItems(items: List<String>) {
this.items = items.toMutableList()
adapter.clear()
adapter.add(items[0])
selection.addAll(items.map{false})
}
override fun setSelection(index: Int) {
selection = selection.map { false }.toMutableList()
if (index >= 0 && index < selection.size) selection[index] = true
else throw IllegalArgumentException("Index $index is out of bounds.")
adapter.clear()
adapter.add(buildSelectedItemString())
listener?.onItemSelect(selectedIndices)
}
fun setSelection(selectedIndices: List<Int>) {
selection = selection.map { false }.toMutableList()
selectedIndices.forEach {
if (it >= 0 && it < selection.size) selection[it] = true
else throw IllegalArgumentException("Index $it is out of bounds.")
}
adapter.clear()
adapter.add(buildSelectedItemString())
listener?.onItemSelect(this.selectedIndices)
}
private fun buildSelectedItemString(): String {
val sb = StringBuilder()
val selectedItems = items.filterIndexed { index, _ -> selection[index] }
if (selectedItems.isEmpty()) sb.append("All")
else selectedItems.forEachIndexed { index, s ->
if (index > 0) sb.append(',')
sb.append(s)
}
return sb.toString()
}
fun setOnItemsSelectListener(l: org.videolan.vlc.gui.OnItemSelectListener) {
listener = l
}
}
interface OnItemSelectListener {
fun onItemSelect(selectedItems: List<Int>)
}
......@@ -6,9 +6,9 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.AdapterView
import org.videolan.vlc.R
import org.videolan.vlc.databinding.SubtitleDownloadFragmentBinding
import org.videolan.vlc.gui.OnItemSelectListener
import org.videolan.vlc.gui.helpers.UiTools
import org.videolan.vlc.util.AndroidDevices
import org.videolan.vlc.viewmodels.SubtitlesModel
......@@ -49,16 +49,16 @@ class SubtitleDownloadFragment : androidx.fragment.app.Fragment() {
}
val allValuesOfLanguages = resources.getStringArray(R.array.language_values)
binding.languageListSpinner.setSelection(allValuesOfLanguages.indexOf(viewModel.getLastUsedLanguage()))
binding.languageListSpinner.onItemSelectedListener = object :AdapterView.OnItemSelectedListener {
override fun onNothingSelected(parent: AdapterView<*>?) { }
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
val selectedLanguage = allValuesOfLanguages[position]
viewModel.observableSearchLanguage.set(selectedLanguage)
val allEntriesOfLanguages = resources.getStringArray(R.array.language_entries)
binding.languageListSpinner.setOnItemsSelectListener(object: OnItemSelectListener {
override fun onItemSelect(selectedItems: List<Int>) {
val selectedLanguages = selectedItems.map { allValuesOfLanguages[it] }
viewModel.observableSearchLanguage.set(selectedLanguages)
}
})
binding.languageListSpinner.setItems(allEntriesOfLanguages.toList())
binding.languageListSpinner.setSelection(viewModel.getLastUsedLanguage().map { allValuesOfLanguages.indexOf(it) })
}
return binding.root
}
......
......@@ -49,10 +49,10 @@ class OpenSubtitleRepository(private val openSubtitleService: IOpenSubtitleServi
languageId = actualLanguageId) }
}
suspend fun queryWithImdbid(imdbId: Int, tag: String?, episode: Int? , season: Int?, languageIds: List<String> ): List<OpenSubtitle> {
suspend fun queryWithImdbid(imdbId: Int, tag: String?, episode: Int? , season: Int?, languageIds: List<String>? ): List<OpenSubtitle> {
val actualEpisode = episode ?: 0
val actualSeason = season ?: 0
val actualLanguageIds = languageIds.toSet().run { if (contains("")) setOf("") else this }
val actualLanguageIds = languageIds?.toSet()?.run { if (contains("") || isEmpty()) setOf("") else this } ?: setOf("")
val actualTag = tag ?: ""
return actualLanguageIds.flatMap {
retrofitResponseCall { openSubtitleService.query(
......@@ -64,8 +64,8 @@ class OpenSubtitleRepository(private val openSubtitleService: IOpenSubtitleServi
}
}
suspend fun queryWithHash(movieByteSize: Long, movieHash: String, languageIds: List<String>): List<OpenSubtitle> {
val actualLanguageIds = languageIds.toSet().run { if (contains("")) setOf("") else this }
suspend fun queryWithHash(movieByteSize: Long, movieHash: String, languageIds: List<String>?): List<OpenSubtitle> {
val actualLanguageIds = languageIds?.toSet()?.run { if (contains("") || isEmpty()) setOf("") else this } ?: setOf("")
return actualLanguageIds.flatMap {
retrofitResponseCall {
openSubtitleService.query(
......@@ -76,10 +76,10 @@ class OpenSubtitleRepository(private val openSubtitleService: IOpenSubtitleServi
}
}
suspend fun queryWithName(name: String, episode: Int?, season: Int?, languageIds: List<String>): List<OpenSubtitle> {
suspend fun queryWithName(name: String, episode: Int?, season: Int?, languageIds: List<String>?): List<OpenSubtitle> {
val actualEpisode = episode ?: 0
val actualSeason = season ?: 0
val actualLanguageIds = languageIds.toSet().run { if (contains("")) setOf("") else this }
val actualLanguageIds = languageIds?.toSet()?.run { if (contains("") || isEmpty()) setOf("") else this } ?: setOf("")
return actualLanguageIds.flatMap {
retrofitResponseCall {
openSubtitleService.query(
......
......@@ -24,8 +24,8 @@ class SubtitlesModel(private val context: Context, private val mediaPath: String
val observableSearchName = ObservableField<String>()
val observableSearchEpisode = ObservableField<String>()
val observableSearchSeason = ObservableField<String>()
val observableSearchLanguage = ObservableField<String>()
private var previousSearchLanguage: String? = null
val observableSearchLanguage = ObservableField<List<String>>()
private var previousSearchLanguage: List<String>? = null
val manualSearchEnabled = ObservableBoolean(false)
val isApiLoading = ObservableBoolean(false)
......@@ -47,7 +47,7 @@ class SubtitlesModel(private val context: Context, private val mediaPath: String
override fun onPropertyChanged(sender: Observable?, propertyId: Int) {
if (observableSearchLanguage.get() != previousSearchLanguage) {
previousSearchLanguage = observableSearchLanguage.get()
saveLastUsedLanguage(observableSearchLanguage.get() ?: "")
saveLastUsedLanguage(observableSearchLanguage.get() ?: listOf())
search(!manualSearchEnabled.get())
}
}
......@@ -106,12 +106,12 @@ class SubtitlesModel(private val context: Context, private val mediaPath: String
if (!isChecked) search(true)
}
private suspend fun getSubtitleByName(name: String, episode: Int?, season: Int?, languageId: String?): List<OpenSubtitle> {
return OpenSubtitleRepository.getInstance().queryWithName(name ,episode, season, languageId)
private suspend fun getSubtitleByName(name: String, episode: Int?, season: Int?, languageIds: List<String>?): List<OpenSubtitle> {
return OpenSubtitleRepository.getInstance().queryWithName(name ,episode, season, languageIds)
}
private suspend fun getSubtitleByHash(movieByteSize: Long, movieHash: String, languageId: String?): List<OpenSubtitle> {
return OpenSubtitleRepository.getInstance().queryWithHash(movieByteSize, movieHash, languageId)
private suspend fun getSubtitleByHash(movieByteSize: Long, movieHash: String, languageIds: List<String>?): List<OpenSubtitle> {
return OpenSubtitleRepository.getInstance().queryWithHash(movieByteSize, movieHash, languageIds)
}
fun onRefresh() {
......@@ -156,9 +156,9 @@ class SubtitlesModel(private val context: Context, private val mediaPath: String
ExternalSubRepository.getInstance(context).deleteSubtitle(mediaPath, idSubtitle)
}
fun getLastUsedLanguage() = Settings.getInstance(context).getString(LAST_USED_LANGUAGE, Locale.getDefault().isO3Language).getCompliantLanguageID()
fun getLastUsedLanguage() = Settings.getInstance(context).getStringSet(LAST_USED_LANGUAGE, setOf(Locale.getDefault().isO3Language)).map { it.getCompliantLanguageID() }
fun saveLastUsedLanguage(lastUsedLang: String) = Settings.getInstance(context).edit().putString(LAST_USED_LANGUAGE, lastUsedLang).apply()
fun saveLastUsedLanguage(lastUsedLanguages: List<String>) = Settings.getInstance(context).edit().putStringSet(LAST_USED_LANGUAGE, lastUsedLanguages.toSet()).apply()
class Factory(private val context: Context, private val mediaPath: String): ViewModelProvider.NewInstanceFactory() {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment