stuff
fixed sponsorblock not restoring properly hid navigation bar when opening search view and reshowing it when closing updated download item data after calling ytdlp so that the user doesnt have to wait. Its parallel added Persian lang in the app added socks5 proxy support
This commit is contained in:
parent
c07ffcfad3
commit
59ca614ffd
12 changed files with 133 additions and 80 deletions
|
|
@ -19,6 +19,7 @@ import kotlinx.coroutines.launch
|
|||
import java.util.*
|
||||
import java.util.concurrent.Executors
|
||||
|
||||
|
||||
class App : Application() {
|
||||
|
||||
override fun onCreate() {
|
||||
|
|
@ -26,7 +27,7 @@ class App : Application() {
|
|||
instance = this
|
||||
createNotificationChannels()
|
||||
val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this)
|
||||
|
||||
setDefaultValues()
|
||||
applicationScope = CoroutineScope(SupervisorJob())
|
||||
applicationScope.launch((Dispatchers.IO)) {
|
||||
try {
|
||||
|
|
@ -59,6 +60,21 @@ class App : Application() {
|
|||
Aria2c.getInstance().init(this)
|
||||
}
|
||||
|
||||
private fun setDefaultValues(){
|
||||
val SPL = 1
|
||||
val sp = PreferenceManager.getDefaultSharedPreferences(this)
|
||||
if (sp.getInt("spl", 0) != SPL) {
|
||||
PreferenceManager.setDefaultValues(this, R.xml.root_preferences, true)
|
||||
PreferenceManager.setDefaultValues(this, R.xml.downloading_preferences, true)
|
||||
PreferenceManager.setDefaultValues(this, R.xml.appearance_preferences, true)
|
||||
PreferenceManager.setDefaultValues(this, R.xml.processing_preferences, true)
|
||||
PreferenceManager.setDefaultValues(this, R.xml.folders_preference, true)
|
||||
PreferenceManager.setDefaultValues(this, R.xml.updating_preferences, true)
|
||||
sp.edit().putInt("spl", SPL).apply()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun createNotificationChannels() {
|
||||
val notificationUtil = NotificationUtil(this)
|
||||
notificationUtil.createNotificationChannel()
|
||||
|
|
|
|||
|
|
@ -207,6 +207,14 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, OnClickListene
|
|||
}
|
||||
}
|
||||
|
||||
searchView?.addTransitionListener { _, _, newState ->
|
||||
if (newState == SearchView.TransitionState.SHOWING){
|
||||
mainActivity?.hideBottomNavigation()
|
||||
}else if (newState == SearchView.TransitionState.HIDING){
|
||||
mainActivity?.showBottomNavigation()
|
||||
}
|
||||
}
|
||||
|
||||
mainActivity?.onBackPressedDispatcher?.addCallback(this) {
|
||||
if (searchView?.isShowing == true) {
|
||||
searchView?.hide()
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ import androidx.core.content.ContextCompat
|
|||
import androidx.core.view.forEach
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import androidx.recyclerview.widget.ItemTouchHelper
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
|
|
@ -215,8 +216,7 @@ class HistoryFragment : Fragment(), HistoryAdapter.OnItemClickListener{
|
|||
}
|
||||
}
|
||||
R.id.download_queue -> {
|
||||
val intent = Intent(context, DownloadQueueMainFragment::class.java)
|
||||
startActivity(intent)
|
||||
findNavController().navigate(R.id.downloadQueueMainFragment)
|
||||
}
|
||||
R.id.remove_deleted_history -> {
|
||||
if(allhistoryList!!.isEmpty()){
|
||||
|
|
|
|||
|
|
@ -4,10 +4,12 @@ import android.app.Activity
|
|||
import android.content.DialogInterface
|
||||
import android.content.Intent
|
||||
import android.content.SharedPreferences
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.os.Environment
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.appcompat.app.AppCompatDelegate
|
||||
import androidx.core.content.edit
|
||||
import androidx.core.os.LocaleListCompat
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
|
|
@ -18,7 +20,6 @@ import androidx.preference.PreferenceManager
|
|||
import androidx.work.WorkInfo
|
||||
import androidx.work.WorkManager
|
||||
import com.deniscerri.ytdlnis.BuildConfig
|
||||
import com.deniscerri.ytdlnis.MainActivity
|
||||
import com.deniscerri.ytdlnis.R
|
||||
import com.deniscerri.ytdlnis.database.models.CommandTemplate
|
||||
import com.deniscerri.ytdlnis.database.models.CookieItem
|
||||
|
|
@ -65,7 +66,7 @@ class MainSettingsFragment : PreferenceFragmentCompat() {
|
|||
setPreferencesFromResource(R.xml.root_preferences, rootKey)
|
||||
val navController = findNavController()
|
||||
val appearance = findPreference<Preference>("appearance")
|
||||
appearance?.summary = "${getString(R.string.language)}, ${getString(R.string.Theme)}, ${getString(R.string.accents)}"
|
||||
appearance?.summary = "${if (Build.VERSION.SDK_INT < 33) getString(R.string.language) + ", " else ""}${getString(R.string.Theme)}, ${getString(R.string.accents)}, ${getString(R.string.preferred_search_engine)}"
|
||||
appearance?.setOnPreferenceClickListener {
|
||||
navController.navigate(R.id.action_mainSettingsFragment_to_appearanceSettingsFragment)
|
||||
true
|
||||
|
|
@ -338,34 +339,36 @@ class MainSettingsFragment : PreferenceFragmentCompat() {
|
|||
|
||||
//preference restore
|
||||
if(json.has("settings")){
|
||||
val prefs = json.getAsJsonArray("settings")
|
||||
val preferencesKeys = preferences.all.map { it.key }
|
||||
prefs.forEach {
|
||||
val key : String = it.asJsonObject.get("key").toString().replace("\"", "")
|
||||
if (preferencesKeys.contains(key)){
|
||||
when(it.asJsonObject.get("type").toString().replace("\"", "")){
|
||||
"String" -> {
|
||||
val value = it.asJsonObject.get("value").toString().replace("\"", "")
|
||||
editor.putString(key, value)
|
||||
}
|
||||
"Boolean" -> {
|
||||
val value = it.asJsonObject.get("value").toString().replace("\"", "").toBoolean()
|
||||
editor.putBoolean(key, value)
|
||||
}
|
||||
"Int" -> {
|
||||
val value = it.asJsonObject.get("value").toString().replace("\"", "").toInt()
|
||||
editor.putInt(key, value)
|
||||
}
|
||||
"HashSet" -> {
|
||||
val value = hashSetOf(it.asJsonObject.get("value").toString().replace("(\")|(\\[)|(])".toRegex(), ""))
|
||||
editor.putStringSet(key, value)
|
||||
PreferenceManager.getDefaultSharedPreferences(requireContext()).edit(commit = true){
|
||||
clear()
|
||||
val prefs = json.getAsJsonArray("settings")
|
||||
val preferencesKeys = preferences.all.map { it.key }
|
||||
prefs.forEach {
|
||||
val key : String = it.asJsonObject.get("key").toString().replace("\"", "")
|
||||
if (preferencesKeys.contains(key)){
|
||||
when(it.asJsonObject.get("type").toString().replace("\"", "")){
|
||||
"String" -> {
|
||||
val value = it.asJsonObject.get("value").toString().replace("\"", "")
|
||||
putString(key, value)
|
||||
}
|
||||
"Boolean" -> {
|
||||
val value = it.asJsonObject.get("value").toString().replace("\"", "").toBoolean()
|
||||
putBoolean(key, value)
|
||||
}
|
||||
"Int" -> {
|
||||
val value = it.asJsonObject.get("value").toString().replace("\"", "").toInt()
|
||||
putInt(key, value)
|
||||
}
|
||||
"HashSet" -> {
|
||||
val value = it.asJsonObject.get("value").toString().replace("(\")|(\\[)|(])|([ \\t])".toRegex(), "").split(",")
|
||||
putStringSet(key, value.toHashSet())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
finalMessage.append("${getString(R.string.settings)}: ${prefs.count()}\n")
|
||||
}
|
||||
finalMessage.append("${getString(R.string.settings)}: ${prefs.count()}\n")
|
||||
}
|
||||
editor.commit()
|
||||
|
||||
//history restore
|
||||
if(json.has("downloads")){
|
||||
|
|
@ -480,14 +483,14 @@ class MainSettingsFragment : PreferenceFragmentCompat() {
|
|||
builder.setPositiveButton(
|
||||
getString(R.string.restart)
|
||||
) { _: DialogInterface?, _: Int ->
|
||||
val intent = Intent(context, MainActivity::class.java)
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
requireContext().startActivity(intent)
|
||||
val intent: Intent? = requireContext().packageManager
|
||||
.getLaunchIntentForPackage(requireContext().packageName)
|
||||
intent?.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
|
||||
startActivity(intent)
|
||||
if(json.has("settings")){
|
||||
AppCompatDelegate.setApplicationLocales(LocaleListCompat.forLanguageTags(preferences.getString("app_language", "en")))
|
||||
}
|
||||
activity?.finishAffinity()
|
||||
Runtime.getRuntime().exit(0)
|
||||
}
|
||||
|
||||
// handle the negative button of the alert dialog
|
||||
|
|
|
|||
|
|
@ -540,6 +540,11 @@ class InfoUtil(private val context: Context) {
|
|||
request.addOption("--cookies", cookiesFile.absolutePath)
|
||||
}
|
||||
|
||||
val proxy = sharedPreferences.getString("proxy", "")
|
||||
if (proxy!!.isNotBlank()){
|
||||
request.addOption("--proxy", proxy)
|
||||
}
|
||||
|
||||
YoutubeDL.getInstance().execute(request){ progress, _, line ->
|
||||
try{
|
||||
val formats = mutableListOf<Format>()
|
||||
|
|
@ -601,6 +606,11 @@ class InfoUtil(private val context: Context) {
|
|||
request.addOption("--cookies", cookiesFile.absolutePath)
|
||||
}
|
||||
|
||||
val proxy = sharedPreferences.getString("proxy", "")
|
||||
if (proxy!!.isNotBlank()){
|
||||
request.addOption("--proxy", proxy)
|
||||
}
|
||||
|
||||
val youtubeDLResponse = YoutubeDL.getInstance().execute(request)
|
||||
val results: Array<String?> = try {
|
||||
val lineSeparator = System.getProperty("line.separator")
|
||||
|
|
@ -697,6 +707,11 @@ class InfoUtil(private val context: Context) {
|
|||
request.addOption("--cookies", cookiesFile.absolutePath)
|
||||
}
|
||||
|
||||
val proxy = sharedPreferences.getString("proxy", "")
|
||||
if (proxy!!.isNotBlank()){
|
||||
request.addOption("--proxy", proxy)
|
||||
}
|
||||
|
||||
val youtubeDLResponse = YoutubeDL.getInstance().execute(request)
|
||||
val jsonObject = JSONObject(youtubeDLResponse.out)
|
||||
|
||||
|
|
@ -836,6 +851,11 @@ class InfoUtil(private val context: Context) {
|
|||
request.addOption("--cookies", cookiesFile.absolutePath)
|
||||
}
|
||||
|
||||
val proxy = sharedPreferences.getString("proxy", "")
|
||||
if (proxy!!.isNotBlank()){
|
||||
request.addOption("--proxy", proxy)
|
||||
}
|
||||
|
||||
val youtubeDLResponse = YoutubeDL.getInstance().execute(request)
|
||||
val results: Array<String?> = try {
|
||||
val lineSeparator = System.getProperty("line.separator")
|
||||
|
|
|
|||
|
|
@ -147,24 +147,6 @@ class NotificationUtil(var context: Context) {
|
|||
notificationManager.notify(DOWNLOAD_RESUME_NOTIFICATION_ID, notificationBuilder.build())
|
||||
}
|
||||
|
||||
fun createUpdatingItemNotification(channel: String){
|
||||
val notificationBuilder = getBuilder(channel)
|
||||
|
||||
notificationBuilder
|
||||
.setContentTitle(context.getString(R.string.updating_download_data))
|
||||
.setSmallIcon(android.R.drawable.stat_sys_download)
|
||||
.setLargeIcon(
|
||||
BitmapFactory.decodeResource(
|
||||
context.resources,
|
||||
android.R.drawable.stat_sys_download
|
||||
)
|
||||
)
|
||||
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
|
||||
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
|
||||
.clearActions()
|
||||
notificationManager.notify(DOWNLOAD_UPDATING_NOTIFICATION_ID, notificationBuilder.build())
|
||||
}
|
||||
|
||||
fun createDownloadFinished(title: String?,
|
||||
filepath: String?,
|
||||
channel: String
|
||||
|
|
@ -289,7 +271,6 @@ class NotificationUtil(var context: Context) {
|
|||
const val DOWNLOAD_FINISHED_CHANNEL_ID = "3"
|
||||
const val DOWNLOAD_FINISHED_NOTIFICATION_ID = 3
|
||||
const val DOWNLOAD_RESUME_NOTIFICATION_ID = 4
|
||||
const val DOWNLOAD_UPDATING_NOTIFICATION_ID = 5
|
||||
private const val PROGRESS_MAX = 100
|
||||
private const val PROGRESS_CURR = 0
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,8 @@ import androidx.work.workDataOf
|
|||
import com.deniscerri.ytdlnis.MainActivity
|
||||
import com.deniscerri.ytdlnis.R
|
||||
import com.deniscerri.ytdlnis.database.DBManager
|
||||
import com.deniscerri.ytdlnis.database.dao.DownloadDao
|
||||
import com.deniscerri.ytdlnis.database.dao.ResultDao
|
||||
import com.deniscerri.ytdlnis.database.models.DownloadItem
|
||||
import com.deniscerri.ytdlnis.database.models.HistoryItem
|
||||
import com.deniscerri.ytdlnis.database.repository.DownloadRepository
|
||||
|
|
@ -25,6 +27,9 @@ import com.deniscerri.ytdlnis.util.InfoUtil
|
|||
import com.deniscerri.ytdlnis.util.NotificationUtil
|
||||
import com.yausername.youtubedl_android.YoutubeDL
|
||||
import com.yausername.youtubedl_android.YoutubeDLRequest
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import java.io.File
|
||||
|
||||
|
|
@ -62,25 +67,9 @@ class DownloadWorker(
|
|||
repository.setDownloadStatus(downloadItem, DownloadRepository.Status.Active)
|
||||
}
|
||||
|
||||
var wasQuickDownloaded = false
|
||||
//update item if its incomplete
|
||||
if (downloadItem.title.isEmpty() || downloadItem.author.isEmpty() || downloadItem.thumb.isEmpty()){
|
||||
notificationUtil.createUpdatingItemNotification(NotificationUtil.DOWNLOAD_SERVICE_CHANNEL_ID);
|
||||
|
||||
runCatching {
|
||||
setProgressAsync(workDataOf("progress" to 0, "output" to context.getString(R.string.updating_download_data), "id" to downloadItem.id, "log" to false))
|
||||
val info = infoUtil.getMissingInfo(downloadItem.url)
|
||||
if (downloadItem.title.isEmpty()) downloadItem.title = info?.title.toString()
|
||||
if (downloadItem.author.isEmpty()) downloadItem.author = info?.author.toString()
|
||||
downloadItem.duration = info?.duration.toString()
|
||||
downloadItem.website = info?.website.toString()
|
||||
if (downloadItem.thumb.isEmpty()) downloadItem.thumb = info?.thumb.toString()
|
||||
runBlocking {
|
||||
wasQuickDownloaded = resultDao.getCountInt() == 0
|
||||
dao.update(downloadItem)
|
||||
}
|
||||
}
|
||||
notificationUtil.cancelDownloadNotification(NotificationUtil.DOWNLOAD_UPDATING_NOTIFICATION_ID)
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
//update item if its incomplete
|
||||
updateDownloadItem(downloadItem, infoUtil, dao, resultDao)
|
||||
}
|
||||
|
||||
val intent = Intent(context, MainActivity::class.java)
|
||||
|
|
@ -344,7 +333,7 @@ class DownloadWorker(
|
|||
NotificationUtil.DOWNLOAD_FINISHED_CHANNEL_ID
|
||||
)
|
||||
|
||||
if (wasQuickDownloaded){
|
||||
if (updateDownloadItem(downloadItem, infoUtil, dao, resultDao)){
|
||||
runCatching {
|
||||
setProgressAsync(workDataOf("progress" to 100, "output" to "Creating Result Items", "id" to downloadItem.id, "log" to false))
|
||||
runBlocking {
|
||||
|
|
@ -398,10 +387,30 @@ class DownloadWorker(
|
|||
)
|
||||
}
|
||||
}
|
||||
|
||||
return Result.success()
|
||||
}
|
||||
|
||||
private fun updateDownloadItem(downloadItem: DownloadItem, infoUtil: InfoUtil, dao: DownloadDao, resultDao: ResultDao) : Boolean {
|
||||
var wasQuickDownloaded = false
|
||||
if (downloadItem.title.isEmpty() || downloadItem.author.isEmpty() || downloadItem.thumb.isEmpty()){
|
||||
|
||||
runCatching {
|
||||
setProgressAsync(workDataOf("progress" to 0, "output" to context.getString(R.string.updating_download_data), "id" to downloadItem.id, "log" to false))
|
||||
val info = infoUtil.getMissingInfo(downloadItem.url)
|
||||
if (downloadItem.title.isEmpty()) downloadItem.title = info?.title.toString()
|
||||
if (downloadItem.author.isEmpty()) downloadItem.author = info?.author.toString()
|
||||
downloadItem.duration = info?.duration.toString()
|
||||
downloadItem.website = info?.website.toString()
|
||||
if (downloadItem.thumb.isEmpty()) downloadItem.thumb = info?.thumb.toString()
|
||||
runBlocking {
|
||||
wasQuickDownloaded = resultDao.getCountInt() == 0
|
||||
dao.update(downloadItem)
|
||||
}
|
||||
}
|
||||
}
|
||||
return wasQuickDownloaded
|
||||
}
|
||||
|
||||
override fun onStopped() {
|
||||
YoutubeDL.getInstance().destroyProcessById(itemId.toInt().toString())
|
||||
super.onStopped()
|
||||
|
|
|
|||
5
app/src/main/res/drawable/baseline_network_locked_24.xml
Normal file
5
app/src/main/res/drawable/baseline_network_locked_24.xml
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
<vector android:height="24dp"
|
||||
android:viewportHeight="24" android:viewportWidth="24"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="?android:colorAccent" android:pathData="M19.5,10c0.17,0 0.33,0.03 0.5,0.05L20,1L1,20h13v-3c0,-0.89 0.39,-1.68 1,-2.23v-0.27c0,-2.48 2.02,-4.5 4.5,-4.5zM22,16v-1.5c0,-1.38 -1.12,-2.5 -2.5,-2.5S17,13.12 17,14.5L17,16c-0.55,0 -1,0.45 -1,1v4c0,0.55 0.45,1 1,1h5c0.55,0 1,-0.45 1,-1v-4c0,-0.55 -0.45,-1 -1,-1zM21,16h-3v-1.5c0,-0.83 0.67,-1.5 1.5,-1.5s1.5,0.67 1.5,1.5L21,16z"/>
|
||||
</vector>
|
||||
|
|
@ -95,6 +95,7 @@
|
|||
<item>bn</item>
|
||||
<item>de</item>
|
||||
<item>es</item>
|
||||
<item>fa</item>
|
||||
<item>fr</item>
|
||||
<item>in</item>
|
||||
<item>it</item>
|
||||
|
|
|
|||
|
|
@ -50,11 +50,11 @@
|
|||
<string name="limit_rate_summary">Maximum download rate (in bytes per second), e.g. 50K or 4.2M</string>
|
||||
<string name="aria2_summary">Use aria2c as downloader instead of the default</string>
|
||||
<string name="processing">Processing</string>
|
||||
<string name="embed_subs_summary">Adds subtitles in the video</string>
|
||||
<string name="embed_subs_summary">Add subtitles in the video</string>
|
||||
<string name="embed_thumb">Thumbnail Covers</string>
|
||||
<string name="embed_thumb_summary">Uses the thumbnail as cover art</string>
|
||||
<string name="embed_thumb_summary">Use the thumbnail as cover art</string>
|
||||
<string name="add_chapters">Chapters in Videos</string>
|
||||
<string name="add_chapters_summary">Marks YouTube / SponsorBlock segments as chapters for the video</string>
|
||||
<string name="add_chapters_summary">Mark YouTube / SponsorBlock segments as chapters for the video</string>
|
||||
<string name="save_thumb">Save Thumbnail</string>
|
||||
<string name="save_thumb_summary">Save the thumbnail in the download folder</string>
|
||||
<string name="audio_format">Audio Format</string>
|
||||
|
|
@ -66,13 +66,13 @@
|
|||
<string name="command_download_notification_channel_name">Command Downloads</string>
|
||||
<string name="command_download_notification_channel_description">Notification showing the progress of downloading using a yt-dlp command</string>
|
||||
<string name="items_left">item(s) left</string>
|
||||
<string name="history_is_empty">History empty.</string>
|
||||
<string name="history_is_empty">History is empty.</string>
|
||||
<string name="you_are_going_to_delete">Deleting</string>
|
||||
<string name="url">URL</string>
|
||||
<string name="link_copied_to_clipboard">Link copied to clipboard</string>
|
||||
<string name="api_key">Use API Key</string>
|
||||
<string name="api_key_summary">Faster results from YouTube</string>
|
||||
<string name="update_app_summary">Announces new versions of the app when it is opened</string>
|
||||
<string name="update_app_summary">Announce new versions of the app when it is opened</string>
|
||||
<string name="downloads">Downloads</string>
|
||||
<string name="remove_deleted">Remove Deleted</string>
|
||||
<string name="remove_duplicates">Remove Duplicates</string>
|
||||
|
|
@ -260,4 +260,6 @@
|
|||
<string name="backup_created_successfully">Backup file saved in the downloads folder</string>
|
||||
<string name="appearance">Appearance</string>
|
||||
<string name="backup_restore">Backup And Restore</string>
|
||||
<string name="socks5_proxy_summary">Use the specified HTTP/HTTPS/SOCKS proxy</string>
|
||||
<string name="socks5_proxy">Socks5 Proxy URL</string>
|
||||
</resources>
|
||||
|
|
@ -35,6 +35,13 @@
|
|||
app:summary="@string/download_over_metered_networks_summary"
|
||||
app:title="@string/download_over_metered_networks" />
|
||||
|
||||
<EditTextPreference
|
||||
android:icon="@drawable/baseline_network_locked_24"
|
||||
app:key="proxy"
|
||||
app:defaultValue=""
|
||||
app:summary="@string/socks5_proxy_summary"
|
||||
app:title="@string/socks5_proxy" />
|
||||
|
||||
<ListPreference
|
||||
android:dependency="download_card"
|
||||
android:defaultValue="video"
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
<locale android:name="bn"/>
|
||||
<locale android:name="de"/>
|
||||
<locale android:name="es"/>
|
||||
<locale android:name="fa-IR"/>
|
||||
<locale android:name="fr"/>
|
||||
<locale android:name="in"/>
|
||||
<locale android:name="it"/>
|
||||
|
|
@ -13,12 +14,12 @@
|
|||
<locale android:name="lv"/>
|
||||
<locale android:name="nb-NO"/>
|
||||
<locale android:name="pl"/>
|
||||
<locale android:name="pt_BR" />
|
||||
<locale android:name="pt-BR" />
|
||||
<locale android:name="ru"/>
|
||||
<locale android:name="ro_RO"/>
|
||||
<locale android:name="ro-RO"/>
|
||||
<locale android:name="sq-AL"/>
|
||||
<locale android:name="tr-TR"/>
|
||||
<locale android:name="ta_IN"/>
|
||||
<locale android:name="ta-IN"/>
|
||||
<locale android:name="uk"/>
|
||||
<locale android:name="vi"/>
|
||||
<locale android:name="zh-CN"/>
|
||||
|
|
|
|||
Loading…
Reference in a new issue