Login to OpenSubtitles

This commit is contained in:
Nicolas Pomepuy 2024-10-30 16:02:44 +01:00 committed by Duncan McNamara
parent e317f1f8f0
commit c263543970
14 changed files with 334 additions and 56 deletions

View File

@ -1,6 +1,6 @@
package org.videolan.resources.opensubtitles
import retrofit2.Response
import retrofit2.Call
import retrofit2.http.Body
import retrofit2.http.GET
import retrofit2.http.POST
@ -19,7 +19,10 @@ interface IOpenSubtitleService {
): OpenSubV1
@POST("download")
suspend fun queryDownloadUrl( @Body downloadLinkBody: DownloadLinkBody): Response<DownloadLink>
suspend fun queryDownloadUrl( @Body downloadLinkBody: DownloadLinkBody): DownloadLink
@POST("login")
fun login( @Body loginBody: LoginBody): Call<OpenSubtitleAccount>
}

View File

@ -3,13 +3,6 @@ package org.videolan.resources.opensubtitles
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
data class QueryParameters(
@field:Json(name = "query") val query: String,
@field:Json(name = "episode") val episode: String,
@field:Json(name = "season") val season: String
)
data class OpenSubV1(
@field:Json(name = "data")
val `data`: List<Data>,
@ -166,31 +159,29 @@ data class DownloadLink(
val resetTimeUtc: String
)
@JsonClass(generateAdapter = true)
data class LoginResult(
@Json(name = "base_url")
val baseUrl: String,
@Json(name = "status")
val status: Int,
@Json(name = "token")
val token: String,
@Json(name = "user")
val user: User
data class OpenSubtitleAccount(
@field:Json(name = "base_url")
val baseUrl: String?,
@field:Json(name = "status")
val status: Int?,
@field:Json(name = "token")
val token: String?,
@field:Json(name = "user")
val user: User?
)
@JsonClass(generateAdapter = true)
data class User(
@Json(name = "allowed_downloads")
val allowedDownloads: Int,
@Json(name = "allowed_translations")
val allowedTranslations: Int,
@Json(name = "ext_installed")
val extInstalled: Boolean,
@Json(name = "level")
val level: String,
@Json(name = "user_id")
val userId: Int,
@Json(name = "vip")
@field:Json(name = "allowed_downloads")
val allowedDownloads: Int?,
@field:Json(name = "allowed_translations")
val allowedTranslations: Int?,
@field:Json(name = "ext_installed")
val extInstalled: Boolean?,
@field:Json(name = "level")
val level: String?,
@field:Json(name = "user_id")
val userId: Int?,
@field:Json(name = "vip")
val vip: Boolean?
)

View File

@ -1,5 +1,7 @@
package org.videolan.resources.opensubtitles
import retrofit2.Response
class OpenSubtitleRepository(private val openSubtitleService: IOpenSubtitleService) {
/*
@ -41,8 +43,11 @@ class OpenSubtitleRepository(private val openSubtitleService: IOpenSubtitleServi
}
suspend fun getDownloadLink(fileId: Long): DownloadLink {
val query = openSubtitleService.queryDownloadUrl(DownloadLinkBody(fileId))
return query.body() ?: throw Exception("No body")
return openSubtitleService.queryDownloadUrl(DownloadLinkBody(fileId))
}
fun login(username: String, password: String): Response<OpenSubtitleAccount> {
return openSubtitleService.login(LoginBody(username, password)).execute()
}
companion object {

View File

@ -0,0 +1,64 @@
/*
* ************************************************************************
* OpensubtitleUser.kt
* *************************************************************************
* Copyright © 2024 VLC authors and VideoLAN
* Author: Nicolas POMEPUY
* 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 2 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
* **************************************************************************
*
*
*/
package main.java.org.videolan.resources.opensubtitles
import android.content.SharedPreferences
import com.squareup.moshi.JsonAdapter
import com.squareup.moshi.Moshi
import com.squareup.moshi.Types
import org.videolan.resources.opensubtitles.OpenSubtitleAccount
import org.videolan.tools.KEY_OPEN_SUBTITLES_USER
import org.videolan.tools.putSingle
data class OpenSubtitlesUser(
val logged: Boolean = false,
val account: OpenSubtitleAccount? = null,
val username: String = "",
val errorMessage: String? = null
) {
fun isVip() = account?.user?.vip ?: false
}
object OpenSubtitlesUserUtil {
fun get(settings: SharedPreferences): OpenSubtitlesUser {
settings.getString(KEY_OPEN_SUBTITLES_USER, "")?.let { userString ->
val moshi = Moshi.Builder().build()
val type = Types.newParameterizedType(OpenSubtitlesUser::class.java)
val jsonAdapter: JsonAdapter<OpenSubtitlesUser> = moshi.adapter(type)
return try {
jsonAdapter.fromJson(userString) ?: OpenSubtitlesUser()
} catch (e: Exception) {
OpenSubtitlesUser()
}
}
return OpenSubtitlesUser()
}
fun save(settings: SharedPreferences, user: OpenSubtitlesUser) {
val moshi = Moshi.Builder().build()
val jsonAdapter = moshi.adapter(OpenSubtitlesUser::class.java)
settings.putSingle(KEY_OPEN_SUBTITLES_USER, jsonAdapter.toJson(user))
}
}

View File

@ -23,8 +23,8 @@
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_selected="true" android:color="@color/orange300" />
<item android:state_pressed="true" android:color="@color/orange300" />
<item android:state_focused="true" android:color="@color/orange300" />
<item android:state_selected="true" android:color="?attr/colorPrimary" />
<item android:state_pressed="true" android:color="?attr/colorPrimary" />
<item android:state_focused="true" android:color="?attr/colorPrimary" />
<item android:color="?attr/colorControlNormal" />
</selector>

View File

@ -0,0 +1,11 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960">
<path
android:fillColor="@color/standard_selection_control_normal"
android:pathData="M234,684Q285,645 348,622.5Q411,600 480,600Q549,600 612,622.5Q675,645 726,684Q761,643 780.5,591Q800,539 800,480Q800,347 706.5,253.5Q613,160 480,160Q347,160 253.5,253.5Q160,347 160,480Q160,539 179.5,591Q199,643 234,684ZM480,520Q421,520 380.5,479.5Q340,439 340,380Q340,321 380.5,280.5Q421,240 480,240Q539,240 579.5,280.5Q620,321 620,380Q620,439 579.5,479.5Q539,520 480,520ZM480,880Q397,880 324,848.5Q251,817 197,763Q143,709 111.5,636Q80,563 80,480Q80,397 111.5,324Q143,251 197,197Q251,143 324,111.5Q397,80 480,80Q563,80 636,111.5Q709,143 763,197Q817,251 848.5,324Q880,397 880,480Q880,563 848.5,636Q817,709 763,763Q709,817 636,848.5Q563,880 480,880ZM480,800Q533,800 580,784.5Q627,769 666,740Q627,711 580,695.5Q533,680 480,680Q427,680 380,695.5Q333,711 294,740Q333,769 380,784.5Q427,800 480,800ZM480,440Q506,440 523,423Q540,406 540,380Q540,354 523,337Q506,320 480,320Q454,320 437,337Q420,354 420,380Q420,406 437,423Q454,440 480,440ZM480,380Q480,380 480,380Q480,380 480,380Q480,380 480,380Q480,380 480,380Q480,380 480,380Q480,380 480,380Q480,380 480,380Q480,380 480,380ZM480,740Q480,740 480,740Q480,740 480,740Q480,740 480,740Q480,740 480,740Q480,740 480,740Q480,740 480,740Q480,740 480,740Q480,740 480,740Z" />
</vector>

View File

@ -1,9 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="?attr/colorControlNormal"
android:pathData="M9.31,6.71c-0.39,0.39 -0.39,1.02 0,1.41L13.19,12l-3.88,3.88c-0.39,0.39 -0.39,1.02 0,1.41 0.39,0.39 1.02,0.39 1.41,0l4.59,-4.59c0.39,-0.39 0.39,-1.02 0,-1.41L10.72,6.7c-0.38,-0.38 -1.02,-0.38 -1.41,0.01z"/>
</vector>

View File

@ -0,0 +1,9 @@
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="@color/red500transparent25">
</solid>
<corners android:radius="8dp">
</corners>
</shape>

View File

@ -20,6 +20,7 @@
<color name="orange800">#ff610a</color>
<color name="orange900">#ff5014</color>
<color name="red500transparent25">#40ff0000</color>
<color name="red500transparent">#b4ff0000</color>
<color name="orange500transparent">#b4ff8800</color>
<color name="orange500focus">#86ff8800</color>

View File

@ -716,6 +716,17 @@
<string name="popup_playback">Playing in a Pop-Up window</string>
<string name="popup_expand">Expand video</string>
<string name="download_subtitles">Download subtitles</string>
<string name="open_subtitles_login_title">Open subtitles login</string>
<string name="open_subtitles_not_logged_in">You are not logged in</string>
<string name="open_subtitles_logged_in">Welcome %1$s</string>
<string name="open_subtitles_vip">You are a VIP OpenSubtitles user</string>
<string name="open_subtitles_log_out">Log out</string>
<string name="open_subtitles_username">Username</string>
<string name="open_subtitles_password">Password</string>
<string name="open_subtitles_is_vip">VIP account</string>
<string name="open_subtitles_is_not_vip">Standard account</string>
<string name="login_error">Invalid credentials</string>
<string name="unknown_error">Unexpected error</string>
<string name="browse_folder">Browse folder</string>
<string name="listen">Listen</string>
<string name="apply_to_bt">Apply to bluetooth device</string>

View File

@ -246,6 +246,10 @@ const val ALBUMS_SHOW_TRACK_NUMBER = "albums_show_track_number"
//widgets
const val WIDGETS_PREVIEW_PLAYING = "widgets_preview_playing"
//OpenSubtitles
const val KEY_OPEN_SUBTITLES_USER = "open_subtitles_user"
const val KEY_SAFE_MODE_PIN = "safe_mode_pin"
const val KEY_RESTRICT_SETTINGS = "restrict_settings"
const val KEY_SAFE_MODE = "safe_mode"

View File

@ -90,14 +90,14 @@
android:contentDescription="@string/talkback_subtitle_history"
android:selected="@{state == state.History}"
app:layout_constraintBottom_toBottomOf="@+id/movieName"
app:layout_constraintEnd_toStartOf="@+id/sub_download_next"
app:layout_constraintEnd_toStartOf="@+id/sub_login"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="@+id/sub_download_search"
app:layout_constraintTop_toTopOf="@+id/movieName"
app:srcCompat="@drawable/ic_history" />
<ImageView
android:id="@+id/sub_download_next"
android:id="@+id/sub_login"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
@ -106,10 +106,11 @@
android:padding="4dp"
app:layout_constraintBottom_toBottomOf="@+id/movieName"
app:layout_constraintEnd_toEndOf="parent"
android:selected="@{state == state.Login}"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="@+id/sub_download_history"
app:layout_constraintTop_toTopOf="@+id/movieName"
app:srcCompat="@drawable/ic_sub_next" />
app:srcCompat="@drawable/ic_account" />
<TextView
@ -200,7 +201,7 @@
android:visibility="@{state == state.Search ? View.VISIBLE : View.GONE}"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/movieName">
app:layout_constraintTop_toBottomOf="@+id/subs_download_list">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/name"
@ -293,6 +294,124 @@
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@+id/seasonContainer" />
<TextView
android:id="@+id/textView37"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="24dp"
android:layout_marginEnd="16dp"
android:text="@string/open_subtitles_login_title"
android:textColor="?attr/font_default"
android:textSize="16sp"
android:visibility="@{state == state.Login ? View.VISIBLE : View.GONE}"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/cancel_button" />
<TextView
android:id="@+id/loginDescription"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:text="@{viewmodel.observableUser.get().logged ? @{@string/open_subtitles_logged_in(viewmodel.observableUser.get().username)} : @string/open_subtitles_not_logged_in}"
android:visibility="@{state == state.Login ? View.VISIBLE : View.GONE}"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textView37" />
<TextView
android:id="@+id/vipDescription"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:text="@{viewmodel.observableUser.get().isVip() ? @string/open_subtitles_is_vip : @string/open_subtitles_is_not_vip}"
android:visibility="@{state == state.Login &amp;&amp; viewmodel.observableUser.get().logged ? View.VISIBLE : View.GONE}"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/loginDescription" />
<TextView
android:id="@+id/error"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="16dp"
android:background="@drawable/rounded_corners_permissions_warning"
android:padding="12dp"
android:text="@{viewmodel.observableUser.get().errorMessage}"
android:textSize="16sp"
android:visibility="@{state == state.Login &amp;&amp; !TextUtils.isEmpty(viewmodel.observableUser.get().errorMessage) ? View.VISIBLE : View.GONE}"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/passwordContainer" />
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/usernameContainer"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="16dp"
android:visibility="@{state == state.Login &amp;&amp; !viewmodel.observableUser.get().logged ? View.VISIBLE : View.GONE}"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/vipDescription">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/username"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10"
android:focusable="true"
android:hint="@string/open_subtitles_username"
android:imeOptions="actionDone"
android:nextFocusRight="@+id/search_button"
android:nextFocusDown="@+id/search_button"
android:nextFocusForward="@+id/search_button"
android:visibility="@{state == state.Login ? View.VISIBLE : View.GONE}" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/passwordContainer"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:visibility="@{state == state.Login &amp;&amp; !viewmodel.observableUser.get().logged ? View.VISIBLE : View.GONE}"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/usernameContainer">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/password"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10"
android:focusable="true"
android:hint="@string/open_subtitles_password"
android:imeOptions="actionDone"
android:nextFocusRight="@+id/search_button"
android:nextFocusDown="@+id/search_button"
android:nextFocusForward="@+id/search_button"
android:password="true"
android:visibility="@{state == state.Login ? View.VISIBLE : View.GONE}" />
</com.google.android.material.textfield.TextInputLayout>
<Button
android:id="@+id/loginButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:layout_marginEnd="16dp"
android:text="@{viewmodel.observableUser.get().logged ? @string/open_subtitles_log_out : @string/login}"
android:visibility="@{state == state.Login ? View.VISIBLE : View.GONE}"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@+id/error" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.core.widget.NestedScrollView>

View File

@ -1,6 +1,7 @@
package org.videolan.vlc.gui.dialogs
import android.content.DialogInterface
import android.content.SharedPreferences
import android.content.res.Configuration
import android.net.Uri
import android.os.Bundle
@ -23,15 +24,16 @@ import kotlinx.coroutines.ObsoleteCoroutinesApi
import kotlinx.coroutines.channels.actor
import kotlinx.coroutines.isActive
import kotlinx.coroutines.withContext
import main.java.org.videolan.resources.opensubtitles.OpenSubtitlesUser
import main.java.org.videolan.resources.opensubtitles.OpenSubtitlesUserUtil
import org.videolan.resources.opensubtitles.OpenSubtitleRepository
import org.videolan.resources.util.parcelable
import org.videolan.resources.util.parcelableList
import org.videolan.tools.Settings
import org.videolan.vlc.R
import org.videolan.vlc.databinding.SubtitleDownloaderDialogBinding
import org.videolan.vlc.gui.helpers.UiTools
import org.videolan.vlc.gui.helpers.UiTools.deleteSubtitleDialog
import org.videolan.vlc.gui.view.OnItemSelectListener
import org.videolan.vlc.media.MediaUtils
import org.videolan.vlc.util.VLCDownloadManager
import org.videolan.vlc.viewmodels.SubtitlesModel
@ -46,6 +48,7 @@ class SubtitleDownloaderDialogFragment : VLCBottomSheetDialogFragment() {
override fun initialFocusedView(): View = binding.movieName
private lateinit var settings: SharedPreferences
private lateinit var downloadAdapter: SubtitlesAdapter
private lateinit var historyAdapter: SubtitlesAdapter
private lateinit var binding: SubtitleDownloaderDialogBinding
@ -112,10 +115,27 @@ class SubtitleDownloaderDialogFragment : VLCBottomSheetDialogFragment() {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
super.onCreateView(inflater, container, savedInstanceState)
settings = Settings.getInstance(requireContext())
binding = SubtitleDownloaderDialogBinding.inflate(inflater, container, false)
binding.viewmodel = viewModel
binding.subLogin.setOnClickListener {
state = SubDownloadDialogState.Login
}
binding.loginButton.setOnClickListener {
if (viewModel.observableUser.get()?.logged == true) {
val user = OpenSubtitlesUser()
OpenSubtitlesUserUtil.save(settings, user)
viewModel.observableUser.set(user)
}else {
viewModel.login(
settings,
binding.username.text.toString(),
binding.password.text.toString()
)
}
}
binding.movieName.text = names ?: uris.lastPathSegment
state = SubDownloadDialogState.Download
@ -161,6 +181,7 @@ class SubtitleDownloaderDialogFragment : VLCBottomSheetDialogFragment() {
})
//todo
viewModel.observableSearchHearingImpaired.set(false)
viewModel.observableUser.set(OpenSubtitlesUserUtil.get(settings))
binding.retryButton.setOnClickListener {
viewModel.onRefresh()
@ -220,5 +241,6 @@ class SubtitleDownloaderDialogFragment : VLCBottomSheetDialogFragment() {
enum class SubDownloadDialogState {
Download,
History,
Search
Search,
Login
}

View File

@ -1,6 +1,7 @@
package org.videolan.vlc.viewmodels
import android.content.Context
import android.content.SharedPreferences
import android.net.Uri
import android.text.Html
import android.text.Spanned
@ -9,19 +10,25 @@ import androidx.core.text.toSpanned
import androidx.databinding.Observable
import androidx.databinding.ObservableBoolean
import androidx.databinding.ObservableField
import androidx.lifecycle.*
import androidx.lifecycle.MediatorLiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.map
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import main.java.org.videolan.resources.opensubtitles.OpenSubtitlesUser
import main.java.org.videolan.resources.opensubtitles.OpenSubtitlesUserUtil
import org.videolan.resources.opensubtitles.Data
import org.videolan.resources.opensubtitles.OpenSubV1
import org.videolan.resources.opensubtitles.OpenSubtitleClient
import org.videolan.resources.opensubtitles.OpenSubtitleRepository
import org.videolan.resources.util.NoConnectivityException
import org.videolan.tools.CoroutineContextProvider
import org.videolan.tools.FileUtils
import org.videolan.tools.LocaleUtils
import org.videolan.tools.Settings
import org.videolan.tools.putSingle
import org.videolan.vlc.BuildConfig
@ -31,7 +38,28 @@ import org.videolan.vlc.gui.dialogs.SubtitleItem
import org.videolan.vlc.repository.ExternalSubRepository
import org.videolan.vlc.util.TextUtils
import java.io.File
import java.util.*
import java.util.Locale
import java.util.MissingResourceException
import kotlin.collections.HashMap
import kotlin.collections.List
import kotlin.collections.MutableList
import kotlin.collections.emptyList
import kotlin.collections.filter
import kotlin.collections.find
import kotlin.collections.first
import kotlin.collections.forEach
import kotlin.collections.get
import kotlin.collections.indices
import kotlin.collections.isNotEmpty
import kotlin.collections.joinToString
import kotlin.collections.listOf
import kotlin.collections.map
import kotlin.collections.mutableListOf
import kotlin.collections.orEmpty
import kotlin.collections.plus
import kotlin.collections.set
import kotlin.collections.setOf
import kotlin.collections.toList
private const val LAST_USED_LANGUAGES = "last_used_subtitles"
@ -41,6 +69,7 @@ class SubtitlesModel(private val context: Context, private val mediaUri: Uri, pr
val observableSearchSeason = ObservableField<String>()
val observableSearchLanguage = ObservableField<List<String>>()
val observableSearchHearingImpaired = ObservableField<Boolean>()
val observableUser = ObservableField<OpenSubtitlesUser>()
private var previousSearchLanguage: List<String>? = null
val manualSearchEnabled = ObservableBoolean(false)
@ -235,6 +264,24 @@ class SubtitlesModel(private val context: Context, private val mediaUri: Uri, pr
return Settings.getInstance(context).getStringSet(LAST_USED_LANGUAGES, setOf(language))?.map { if (it.length > 2) migrateFromOld(it) ?: it else it } ?: emptyList()
}
fun login(settings: SharedPreferences, username: String, password: String) {
viewModelScope.launch {
withContext(Dispatchers.IO) {
val call = OpenSubtitleRepository.getInstance().login(username, password)
if (call.isSuccessful) {
val userResult = call.body()
if (userResult != null) {
val openSubtitlesUser = OpenSubtitlesUser(true, userResult, username = username)
OpenSubtitlesUserUtil.save(settings, openSubtitlesUser)
observableUser.set(openSubtitlesUser)
return@withContext
}
}
observableUser.set(OpenSubtitlesUser(false, null, errorMessage = if (call.code() == 401) context.getString(R.string.login_error) else context.getString(R.string.unknown_error)))
}
}
}
private fun migrateFromOld(it: String?): String? {
return oldLanguagesMigration[it]
}